summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHaruna <haru@dotalux.com>2015-01-24 00:54:00 +0100
committerHaruna <haru@dotalux.com>2015-01-24 00:54:00 +0100
commit1ae4a7184bee7897a318adca6f0941c9c697b78a (patch)
tree00b612493d44b30e20afdaa049188e33f738df18 /src
parent21e3b27ba67b74bc106eb83e26d23255a363ce59 (diff)
parent49df787e2c2faa2621b5490c6e88c86a4b7a813f (diff)
downloadhercules-1ae4a7184bee7897a318adca6f0941c9c697b78a.tar.gz
hercules-1ae4a7184bee7897a318adca6f0941c9c697b78a.tar.bz2
hercules-1ae4a7184bee7897a318adca6f0941c9c697b78a.tar.xz
hercules-1ae4a7184bee7897a318adca6f0941c9c697b78a.zip
Merge pull request #449 from HerculesWS/questdb
Converted the Quest DB to libconfig. Added support for quest-granted item drops
Diffstat (limited to 'src')
-rw-r--r--src/map/clif.c44
-rw-r--r--src/map/quest.c235
-rw-r--r--src/map/quest.h21
3 files changed, 209 insertions, 91 deletions
diff --git a/src/map/clif.c b/src/map/clif.c
index 268066d1a..291b5cb46 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -15829,7 +15829,8 @@ void clif_parse_PartyTick(int fd, struct map_session_data* sd)
/// Sends list of all quest states (ZC_ALL_QUEST_LIST).
/// 02b1 <packet len>.W <num>.L { <quest id>.L <active>.B }*num
-void clif_quest_send_list(struct map_session_data *sd) {
+void clif_quest_send_list(struct map_session_data *sd)
+{
int fd = sd->fd;
int i;
#if PACKETVER >= 20141022
@@ -15846,7 +15847,7 @@ void clif_quest_send_list(struct map_session_data *sd) {
WFIFOW(fd, 2) = len;
WFIFOL(fd, 4) = sd->avail_quests;
- for( i = 0; i < sd->avail_quests; i++ ) {
+ for (i = 0; i < sd->avail_quests; i++) {
#if PACKETVER >= 20141022
struct quest_db *qi = quest->db(sd->quest_log[i].quest_id);
#endif
@@ -15855,7 +15856,7 @@ void clif_quest_send_list(struct map_session_data *sd) {
#if PACKETVER >= 20141022
WFIFOL(fd, i*info_len+13) = sd->quest_log[i].time - qi->time;
WFIFOL(fd, i*info_len+17) = sd->quest_log[i].time;
- WFIFOW(fd, i*info_len+21) = qi->num_objectives;
+ WFIFOW(fd, i*info_len+21) = qi->objectives_count;
#endif
}
@@ -15865,7 +15866,8 @@ void clif_quest_send_list(struct map_session_data *sd) {
/// Sends list of all quest missions (ZC_ALL_QUEST_MISSION).
/// 02b2 <packet len>.W <num>.L { <quest id>.L <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3 }*num
-void clif_quest_send_mission(struct map_session_data *sd) {
+void clif_quest_send_mission(struct map_session_data *sd)
+{
int fd = sd->fd;
int i, j;
int len = sd->avail_quests*104+8;
@@ -15876,17 +15878,17 @@ void clif_quest_send_mission(struct map_session_data *sd) {
WFIFOW(fd, 2) = len;
WFIFOL(fd, 4) = sd->avail_quests;
- for( i = 0; i < sd->avail_quests; i++ ) {
+ for (i = 0; i < sd->avail_quests; i++) {
struct quest_db *qi = quest->db(sd->quest_log[i].quest_id);
WFIFOL(fd, i*104+8) = sd->quest_log[i].quest_id;
WFIFOL(fd, i*104+12) = sd->quest_log[i].time - qi->time;
WFIFOL(fd, i*104+16) = sd->quest_log[i].time;
- WFIFOW(fd, i*104+20) = qi->num_objectives;
+ WFIFOW(fd, i*104+20) = qi->objectives_count;
- for( j = 0 ; j < qi->num_objectives; j++ ) {
- WFIFOL(fd, i*104+22+j*30) = qi->mob[j];
+ for (j = 0 ; j < qi->objectives_count; j++) {
+ WFIFOL(fd, i*104+22+j*30) = qi->objectives[j].mob;
WFIFOW(fd, i*104+26+j*30) = sd->quest_log[i].count[j];
- monster = mob->db(qi->mob[j]);
+ monster = mob->db(qi->objectives[j].mob);
memcpy(WFIFOP(fd, i*104+28+j*30), monster->jname, NAME_LENGTH);
}
}
@@ -15897,7 +15899,8 @@ void clif_quest_send_mission(struct map_session_data *sd) {
/// Notification about a new quest (ZC_ADD_QUEST).
/// 02b3 <quest id>.L <active>.B <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3
-void clif_quest_add(struct map_session_data *sd, struct quest *qd) {
+void clif_quest_add(struct map_session_data *sd, struct quest *qd)
+{
int fd = sd->fd;
int i;
struct quest_db *qi = quest->db(qd->quest_id);
@@ -15908,13 +15911,13 @@ void clif_quest_add(struct map_session_data *sd, struct quest *qd) {
WFIFOB(fd, 6) = qd->state;
WFIFOB(fd, 7) = qd->time - qi->time;
WFIFOL(fd, 11) = qd->time;
- WFIFOW(fd, 15) = qi->num_objectives;
+ WFIFOW(fd, 15) = qi->objectives_count;
- for( i = 0; i < qi->num_objectives; i++ ) {
+ for (i = 0; i < qi->objectives_count; i++) {
struct mob_db *monster;
- WFIFOL(fd, i*30+17) = qi->mob[i];
+ WFIFOL(fd, i*30+17) = qi->objectives[i].mob;
WFIFOW(fd, i*30+21) = qd->count[i];
- monster = mob->db(qi->mob[i]);
+ monster = mob->db(qi->objectives[i].count);
memcpy(WFIFOP(fd, i*30+23), monster->jname, NAME_LENGTH);
}
@@ -15936,21 +15939,22 @@ void clif_quest_delete(struct map_session_data *sd, int quest_id) {
/// Notification of an update to the hunting mission counter (ZC_UPDATE_MISSION_HUNT).
/// 02b5 <packet len>.W <mobs>.W { <quest id>.L <mob id>.L <total count>.W <current count>.W }*3
-void clif_quest_update_objective(struct map_session_data *sd, struct quest *qd) {
+void clif_quest_update_objective(struct map_session_data *sd, struct quest *qd)
+{
int fd = sd->fd;
int i;
struct quest_db *qi = quest->db(qd->quest_id);
- int len = qi->num_objectives*12+6;
+ int len = qi->objectives_count*12+6;
WFIFOHEAD(fd, len);
WFIFOW(fd, 0) = 0x2b5;
WFIFOW(fd, 2) = len;
- WFIFOW(fd, 4) = qi->num_objectives;
+ WFIFOW(fd, 4) = qi->objectives_count;
- for( i = 0; i < qi->num_objectives; i++ ) {
+ for (i = 0; i < qi->objectives_count; i++) {
WFIFOL(fd, i*12+6) = qd->quest_id;
- WFIFOL(fd, i*12+10) = qi->mob[i];
- WFIFOW(fd, i*12+14) = qi->count[i];
+ WFIFOL(fd, i*12+10) = qi->objectives[i].mob;
+ WFIFOW(fd, i*12+14) = qi->objectives[i].count;
WFIFOW(fd, i*12+16) = qd->count[i];
}
diff --git a/src/map/quest.c b/src/map/quest.c
index e993ab69d..93ec1b04c 100644
--- a/src/map/quest.c
+++ b/src/map/quest.c
@@ -26,8 +26,10 @@
#include "script.h"
#include "unit.h"
#include "../common/cbasetypes.h"
+#include "../common/conf.h"
#include "../common/malloc.h"
#include "../common/nullpo.h"
+#include "../common/random.h"
#include "../common/showmsg.h"
#include "../common/socket.h"
#include "../common/strlib.h"
@@ -244,29 +246,53 @@ int quest_update_objective_sub(struct block_list *bl, va_list ap) {
/**
- * Updates the quest objectives for a character after killing a monster.
+ * Updates the quest objectives for a character after killing a monster, including the handling of quest-granted drops.
*
* @param sd Character's data
* @param mob_id Monster ID
*/
-void quest_update_objective(TBL_PC *sd, int mob_id) {
+void quest_update_objective(TBL_PC *sd, int mob_id)
+{
int i,j;
- for( i = 0; i < sd->avail_quests; i++ ) {
+ for (i = 0; i < sd->avail_quests; i++) {
struct quest_db *qi = NULL;
- if( sd->quest_log[i].state != Q_ACTIVE ) // Skip inactive quests
+ if (sd->quest_log[i].state != Q_ACTIVE) // Skip inactive quests
continue;
qi = quest->db(sd->quest_log[i].quest_id);
- for( j = 0; j < qi->num_objectives; j++ ) {
- if( qi->mob[j] == mob_id && sd->quest_log[i].count[j] < qi->count[j] ) {
+ for (j = 0; j < qi->objectives_count; j++) {
+ if (qi->objectives[j].mob == mob_id && sd->quest_log[i].count[j] < qi->objectives[j].count) {
sd->quest_log[i].count[j]++;
sd->save_quest = true;
clif->quest_update_objective(sd, &sd->quest_log[i]);
}
}
+
+ // process quest-granted extra drop bonuses
+ for (j = 0; j < qi->dropitem_count; j++) {
+ struct quest_dropitem *dropitem = &qi->dropitem[j];
+ struct item item;
+ struct item_data *data = NULL;
+ int temp;
+ if (dropitem->mob_id != 0 && dropitem->mob_id != mob_id)
+ continue;
+ // TODO: Should this be affected by server rates?
+ if (rnd()%10000 >= dropitem->rate)
+ continue;
+ if (!(data = itemdb->exists(dropitem->nameid)))
+ continue;
+ memset(&item,0,sizeof(item));
+ item.nameid = dropitem->nameid;
+ item.identify = itemdb->isidentified2(data);
+ item.amount = 1;
+ if((temp = pc->additem(sd, &item, 1, LOG_TYPE_OTHER)) != 0) { // TODO: We might want a new log type here?
+ // Failed to obtain the item
+ clif->additem(sd, 0, 0, temp);
+ }
+ }
}
}
@@ -329,14 +355,15 @@ int quest_update_status(TBL_PC *sd, int quest_id, enum quest_state qs) {
* 1 if the quest's timeout has expired
* 0 otherwise
*/
-int quest_check(TBL_PC *sd, int quest_id, enum quest_check_type type) {
+int quest_check(TBL_PC *sd, int quest_id, enum quest_check_type type)
+{
int i;
ARR_FIND(0, sd->num_quests, i, sd->quest_log[i].quest_id == quest_id);
- if( i == sd->num_quests )
+ if (i == sd->num_quests)
return -1;
- switch( type ) {
+ switch (type) {
case HAVEQUEST:
return sd->quest_log[i].state;
case PLAYTIME:
@@ -345,10 +372,10 @@ int quest_check(TBL_PC *sd, int quest_id, enum quest_check_type type) {
if( sd->quest_log[i].state == Q_INACTIVE || sd->quest_log[i].state == Q_ACTIVE ) {
int j;
struct quest_db *qi = quest->db(sd->quest_log[i].quest_id);
- ARR_FIND(0, MAX_QUEST_OBJECTIVES, j, sd->quest_log[i].count[j] < qi->count[j]);
- if( j == MAX_QUEST_OBJECTIVES )
+ ARR_FIND(0, qi->objectives_count, j, sd->quest_log[i].count[j] < qi->objectives[j].count);
+ if (j == qi->objectives_count)
return 2;
- if( sd->quest_log[i].time < (unsigned int)time(NULL) )
+ if (sd->quest_log[i].time < (unsigned int)time(NULL))
return 1;
}
return 0;
@@ -361,77 +388,146 @@ int quest_check(TBL_PC *sd, int quest_id, enum quest_check_type type) {
}
/**
- * Loads quests from the quest db.
+ * Reads and parses an entry from the quest_db.
*
- * @return Number of loaded quests, or -1 if the file couldn't be read.
+ * @param cs The config setting containing the entry.
+ * @param n The sequential index of the current config setting.
+ * @param source The source configuration file.
+ * @return The parsed quest entry.
+ * @retval NULL in case of errors.
*/
-int quest_read_db(void) {
- // TODO[Haru] This duplicates some sv_readdb functionalities, and it would be
- // nice if it could be replaced by it. The reason why it wasn't is probably
- // because we need to accept commas (which is also used as delimiter) in the
- // last field (quest name), and sv_readdb isn't capable of doing so.
- FILE *fp;
- char line[1024];
- int i, count = 0;
- char *str[20], *p, *np;
- struct quest_db entry;
-
- sprintf(line, "%s/quest_db.txt", map->db_path);
- if ((fp=fopen(line,"r"))==NULL) {
- ShowError("can't read %s\n", line);
- return -1;
+struct quest_db *quest_read_db_sub(config_setting_t *cs, int n, const char *source)
+{
+ struct quest_db *entry = NULL;
+ config_setting_t *t = NULL;
+ int i32 = 0, quest_id;
+ const char *str = NULL;
+ /*
+ * Id: Quest ID [int]
+ * Name: Quest Name [string]
+ * TimeLimit: Time Limit (seconds) [int, optional]
+ * Targets: ( [array, optional]
+ * {
+ * MobId: Mob ID [int]
+ * Count: [int]
+ * },
+ * ... (can repeated up to MAX_QUEST_OBJECTIVES times)
+ * )
+ * Drops: (
+ * {
+ * ItemId: Item ID to drop [int]
+ * Rate: Drop rate [int]
+ * MobId: Mob ID to match [int, optional]
+ * },
+ * ... (can be repeated)
+ * )
+ */
+ if (!libconfig->setting_lookup_int(cs, "Id", &quest_id)) {
+ ShowWarning("quest_read_db: Missing id in \"%s\", entry #%d, skipping.\n", source, n);
+ return NULL;
+ }
+ if (quest_id < 0 || quest_id >= MAX_QUEST_DB) {
+ ShowWarning("quest_read_db: Invalid quest ID '%d' in \"%s\", entry #%d (min: 0, max: %d), skipping.\n", quest_id, source, n, MAX_QUEST_DB);
+ return NULL;
}
- while (fgets(line, sizeof(line), fp)) {
- if (line[0]=='/' && line[1]=='/')
- continue;
- memset(str,0,sizeof(str));
-
- for (i = 0, p = line; i < 8; i++) {
- if (( np = strchr(p,',') ) != NULL) {
- str[i] = p;
- *np = 0;
- p = np + 1;
- } else if (str[0] == NULL) {
+ if (!libconfig->setting_lookup_string(cs, "Name", &str) || !*str) {
+ ShowWarning("quest_read_db_sub: Missing Name in quest %d of \"%s\", skipping.\n", quest_id, source);
+ return NULL;
+ }
+
+ CREATE(entry, struct quest_db, 1);
+ entry->id = quest_id;
+ //safestrncpy(entry->name, str, sizeof(entry->name));
+
+ if (libconfig->setting_lookup_int(cs, "TimeLimit", &i32)) // This is an unsigned value, do not check for >= 0
+ entry->time = (unsigned int)i32;
+
+ if ((t=libconfig->setting_get_member(cs, "Targets")) && config_setting_is_list(t)) {
+ int i, len = libconfig->setting_length(t);
+ for (i = 0; i < len && entry->objectives_count < MAX_QUEST_OBJECTIVES; i++) {
+ // Note: We ensure that objectives_count < MAX_QUEST_OBJECTIVES because
+ // quest_log (as well as the client) expect this maximum size.
+ config_setting_t *tt = libconfig->setting_get_elem(t, i);
+ int mob_id = 0, count = 0;
+ if (!tt)
break;
- } else {
- ShowError("quest_read_db: insufficient columns in line %s\n", line);
+ if (!config_setting_is_group(tt))
continue;
- }
+ if (!libconfig->setting_lookup_int(tt, "MobId", &mob_id) || mob_id <= 0)
+ continue;
+ if (!libconfig->setting_lookup_int(tt, "Count", &count) || count <= 0)
+ continue;
+ RECREATE(entry->objectives, struct quest_objective, ++entry->objectives_count);
+ entry->objectives[entry->objectives_count-1].mob = mob_id;
+ entry->objectives[entry->objectives_count-1].count = count;
}
- if (str[0] == NULL)
- continue;
-
- memset(&entry, 0, sizeof(entry));
-
- entry.id = atoi(str[0]);
+ }
- if (entry.id < 0 || entry.id >= MAX_QUEST_DB) {
- ShowError("quest_read_db: Invalid quest ID '%d' in line '%s' (min: 0, max: %d.)\n", entry.id, line, MAX_QUEST_DB);
- continue;
+ if ((t=libconfig->setting_get_member(cs, "Drops")) && config_setting_is_list(t)) {
+ int i, len = libconfig->setting_length(t);
+ for (i = 0; i < len; i++) {
+ config_setting_t *tt = libconfig->setting_get_elem(t, i);
+ int mob_id = 0, nameid = 0, rate = 0;
+ if (!tt)
+ break;
+ if (!config_setting_is_group(tt))
+ continue;
+ if (!libconfig->setting_lookup_int(tt, "MobId", &mob_id))
+ mob_id = 0; // Zero = any monster
+ if (mob_id < 0)
+ continue;
+ if (!libconfig->setting_lookup_int(tt, "ItemId", &nameid) || !itemdb->exists(nameid))
+ continue;
+ if (!libconfig->setting_lookup_int(tt, "Rate", &rate) || rate <= 0)
+ continue;
+ RECREATE(entry->dropitem, struct quest_dropitem, ++entry->dropitem_count);
+ entry->dropitem[entry->dropitem_count-1].mob_id = mob_id;
+ entry->dropitem[entry->dropitem_count-1].nameid = nameid;
+ entry->dropitem[entry->dropitem_count-1].rate = rate;
}
+ }
+ return entry;
+}
- entry.time = atoi(str[1]);
+/**
+ * Loads quests from the quest db.
+ *
+ * @return Number of loaded quests, or -1 if the file couldn't be read.
+ */
+int quest_read_db(void)
+{
+ char filepath[256];
+ config_t quest_db_conf;
+ config_setting_t *qdb = NULL, *q = NULL;
+ int i = 0, count = 0;
+ const char *filename = "quest_db.conf";
+
+ sprintf(filepath, "%s/%s", map->db_path, filename);
+ if (libconfig->read_file(&quest_db_conf, filepath) || !(qdb = libconfig->setting_get_member(quest_db_conf.root, filename))) {
+ ShowError("can't read %s\n", filepath);
+ return -1;
+ }
- for (i = 0; i < MAX_QUEST_OBJECTIVES; i++) {
- entry.mob[i] = atoi(str[2*i+2]);
- entry.count[i] = atoi(str[2*i+3]);
+ while ((q = libconfig->setting_get_elem(qdb, i++))) {
+ struct quest_db *entry = quest->read_db_sub(q, i-1, filepath);
+ if (!entry)
+ continue;
- if (!entry.mob[i] || !entry.count[i])
- break;
+ if (quest->db_data[entry->id] != NULL) {
+ ShowWarning("quest_read_db: Duplicate quest %d.\n", entry->id);
+ if (quest->db_data[entry->id]->dropitem)
+ aFree(quest->db_data[entry->id]->dropitem);
+ if (quest->db_data[entry->id]->objectives)
+ aFree(quest->db_data[entry->id]->objectives);
+ aFree(quest->db_data[entry->id]);
}
+ quest->db_data[entry->id] = entry;
- entry.num_objectives = i;
-
- if (quest->db_data[entry.id] == NULL)
- quest->db_data[entry.id] = aMalloc(sizeof(struct quest_db));
-
- memcpy(quest->db_data[entry.id], &entry, sizeof(struct quest_db));
count++;
}
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, "quest_db.txt");
- return 0;
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename);
+ return count;
}
/**
@@ -476,6 +572,10 @@ void quest_clear_db(void) {
for (i = 0; i < MAX_QUEST_DB; i++) {
if (quest->db_data[i]) {
+ if (quest->db_data[i]->objectives)
+ aFree(quest->db_data[i]->objectives);
+ if (quest->db_data[i]->dropitem)
+ aFree(quest->db_data[i]->dropitem);
aFree(quest->db_data[i]);
quest->db_data[i] = NULL;
}
@@ -537,4 +637,5 @@ void quest_defaults(void) {
quest->check = quest_check;
quest->clear = quest_clear_db;
quest->read_db = quest_read_db;
+ quest->read_db_sub = quest_read_db_sub;
}
diff --git a/src/map/quest.h b/src/map/quest.h
index f988d0c61..36ac69a53 100644
--- a/src/map/quest.h
+++ b/src/map/quest.h
@@ -7,16 +7,28 @@
#include "map.h" // TBL_PC
#include "../common/cbasetypes.h"
-#include "../common/mmo.h" // MAX_QUEST_OBJECTIVES
+#include "../common/conf.h"
#define MAX_QUEST_DB (60355+1) // Highest quest ID + 1
+struct quest_dropitem {
+ int mob_id;
+ int nameid;
+ int rate;
+};
+
+struct quest_objective {
+ int mob;
+ int count;
+};
+
struct quest_db {
int id;
unsigned int time;
- int mob[MAX_QUEST_OBJECTIVES];
- int count[MAX_QUEST_OBJECTIVES];
- int num_objectives;
+ int objectives_count;
+ struct quest_objective *objectives;
+ int dropitem_count;
+ struct quest_dropitem *dropitem;
//char name[NAME_LENGTH];
};
@@ -46,6 +58,7 @@ struct quest_interface {
int (*check) (TBL_PC *sd, int quest_id, enum quest_check_type type);
void (*clear) (void);
int (*read_db) (void);
+ struct quest_db *(*read_db_sub) (config_setting_t *cs, int n, const char *source);
};
struct quest_interface *quest;