diff options
Diffstat (limited to 'src/map/quest.c')
-rw-r--r-- | src/map/quest.c | 400 |
1 files changed, 370 insertions, 30 deletions
diff --git a/src/map/quest.c b/src/map/quest.c index ed8e5bd33..7a216095e 100644 --- a/src/map/quest.c +++ b/src/map/quest.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -25,10 +25,12 @@ #include "map/battle.h" #include "map/chrif.h" #include "map/clif.h" +#include "map/homunculus.h" #include "map/intif.h" #include "map/itemdb.h" #include "map/log.h" #include "map/map.h" +#include "map/mercenary.h" #include "map/mob.h" #include "map/npc.h" #include "map/party.h" @@ -52,8 +54,8 @@ #include <string.h> #include <time.h> -struct quest_interface quest_s; -struct quest_db *db_data[MAX_QUEST_DB]; ///< Quest database +static struct quest_interface quest_s; +static struct quest_db *db_data[MAX_QUEST_DB]; ///< Quest database struct quest_interface *quest; @@ -63,7 +65,8 @@ struct quest_interface *quest; * @param quest_id ID to lookup * @return Quest entry (equals to &quest->dummy if the ID is invalid) */ -struct quest_db *quest_db(int quest_id) { +static struct quest_db *quest_db(int quest_id) +{ if (quest_id < 0 || quest_id >= MAX_QUEST_DB || quest->db_data[quest_id] == NULL) return &quest->dummy; return quest->db_data[quest_id]; @@ -75,7 +78,7 @@ struct quest_db *quest_db(int quest_id) { * @param sd Player's data * @return 0 in case of success, nonzero otherwise (i.e. the player has no quests) */ -int quest_pc_login(struct map_session_data *sd) +static int quest_pc_login(struct map_session_data *sd) { #if PACKETVER < 20141022 int i; @@ -103,22 +106,23 @@ int quest_pc_login(struct map_session_data *sd) * * New quest will be added as Q_ACTIVE. * - * @param sd Player's data - * @param quest_id ID of the quest to add. + * @param sd Player's data + * @param quest_id ID of the quest to add. + * @param time_limit Custom time, in UNIX epoch, for this quest * @return 0 in case of success, nonzero otherwise */ -int quest_add(struct map_session_data *sd, int quest_id) +static int quest_add(struct map_session_data *sd, int quest_id, unsigned int time_limit) { int n; struct quest_db *qi = quest->db(quest_id); nullpo_retr(-1, sd); - if( qi == &quest->dummy ) { + if (qi == &quest->dummy) { ShowError("quest_add: quest %d not found in DB.\n", quest_id); return -1; } - if( quest->check(sd, quest_id, HAVEQUEST) >= 0 ) { + if (quest->check(sd, quest_id, HAVEQUEST) >= 0) { ShowError("quest_add: Character %d already has quest %d.\n", sd->status.char_id, quest_id); return -1; } @@ -130,7 +134,7 @@ int quest_add(struct map_session_data *sd, int quest_id) sd->avail_quests++; RECREATE(sd->quest_log, struct quest, sd->num_quests); - if( sd->avail_quests != sd->num_quests ) { + if (sd->avail_quests != sd->num_quests) { // The character has some completed quests, make room before them so that they will stay at the end of the array memmove(&sd->quest_log[n+1], &sd->quest_log[n], sizeof(struct quest)*(sd->num_quests-sd->avail_quests)); } @@ -138,17 +142,24 @@ int quest_add(struct map_session_data *sd, int quest_id) memset(&sd->quest_log[n], 0, sizeof(struct quest)); sd->quest_log[n].quest_id = qi->id; - if( qi->time ) + if (time_limit != 0) + sd->quest_log[n].time = time_limit; + else if (qi->time != 0) sd->quest_log[n].time = (unsigned int)(time(NULL) + qi->time); sd->quest_log[n].state = Q_ACTIVE; sd->save_quest = true; clif->quest_add(sd, &sd->quest_log[n]); +#if PACKETVER >= 20150513 + clif->quest_notify_objective(sd, &sd->quest_log[n]); +#else clif->quest_update_objective(sd, &sd->quest_log[n]); +#endif + quest->questinfo_refresh(sd); - if( map->save_settings&64 ) - chrif->save(sd,0); + if ((map->save_settings & 64) != 0) + chrif->save(sd, 0); return 0; } @@ -161,7 +172,7 @@ int quest_add(struct map_session_data *sd, int quest_id) * @param qid2 New quest to add * @return 0 in case of success, nonzero otherwise */ -int quest_change(struct map_session_data *sd, int qid1, int qid2) +static int quest_change(struct map_session_data *sd, int qid1, int qid2) { int i; struct quest_db *qi = quest->db(qid2); @@ -198,11 +209,15 @@ int quest_change(struct map_session_data *sd, int qid1, int qid2) clif->quest_delete(sd, qid1); clif->quest_add(sd, &sd->quest_log[i]); +#if PACKETVER >= 20150513 + clif->quest_notify_objective(sd, &sd->quest_log[i]); +#else clif->quest_update_objective(sd, &sd->quest_log[i]); +#endif + quest->questinfo_refresh(sd); if( map->save_settings&64 ) chrif->save(sd,0); - return 0; } @@ -213,7 +228,7 @@ int quest_change(struct map_session_data *sd, int qid1, int qid2) * @param quest_id ID of the quest to remove * @return 0 in case of success, nonzero otherwise */ -int quest_delete(struct map_session_data *sd, int quest_id) +static int quest_delete(struct map_session_data *sd, int quest_id) { int i; @@ -242,6 +257,7 @@ int quest_delete(struct map_session_data *sd, int quest_id) sd->save_quest = true; clif->quest_delete(sd, quest_id); + quest->questinfo_refresh(sd); if( map->save_settings&64 ) chrif->save(sd,0); @@ -257,7 +273,7 @@ int quest_delete(struct map_session_data *sd, int quest_id) * int Party ID * int Mob ID */ -int quest_update_objective_sub(struct block_list *bl, va_list ap) +static int quest_update_objective_sub(struct block_list *bl, va_list ap) { struct map_session_data *sd = NULL; int party_id = va_arg(ap, int); @@ -284,7 +300,7 @@ int quest_update_objective_sub(struct block_list *bl, va_list ap) * @param sd Character's data * @param mob_id Monster ID */ -void quest_update_objective(struct map_session_data *sd, int mob_id) +static void quest_update_objective(struct map_session_data *sd, int mob_id) { int i,j; @@ -340,7 +356,7 @@ void quest_update_objective(struct map_session_data *sd, int mob_id) * @param qs New quest state * @return 0 in case of success, nonzero otherwise */ -int quest_update_status(struct map_session_data *sd, int quest_id, enum quest_state qs) +static int quest_update_status(struct map_session_data *sd, int quest_id, enum quest_state qs) { int i; @@ -369,6 +385,7 @@ int quest_update_status(struct map_session_data *sd, int quest_id, enum quest_st } clif->quest_delete(sd, quest_id); + quest->questinfo_refresh(sd); if( map->save_settings&64 ) chrif->save(sd,0); @@ -391,7 +408,7 @@ int quest_update_status(struct map_session_data *sd, int quest_id, enum quest_st * 1 if the quest's timeout has expired * 0 otherwise */ -int quest_check(struct map_session_data *sd, int quest_id, enum quest_check_type type) +static int quest_check(struct map_session_data *sd, int quest_id, enum quest_check_type type) { int i; @@ -433,7 +450,7 @@ int quest_check(struct map_session_data *sd, int quest_id, enum quest_check_type * @return The parsed quest entry. * @retval NULL in case of errors. */ -struct quest_db *quest_read_db_sub(struct config_setting_t *cs, int n, const char *source) +static struct quest_db *quest_read_db_sub(struct config_setting_t *cs, int n, const char *source) { struct quest_db *entry = NULL; struct config_setting_t *t = NULL; @@ -533,7 +550,7 @@ struct quest_db *quest_read_db_sub(struct config_setting_t *cs, int n, const cha * * @return Number of loaded quests, or -1 if the file couldn't be read. */ -int quest_read_db(void) +static int quest_read_db(void) { char filepath[256]; struct config_t quest_db_conf; @@ -541,7 +558,7 @@ int quest_read_db(void) int i = 0, count = 0; const char *filename = "quest_db.conf"; - snprintf(filepath, 256, "%s/%s", map->db_path, filename); + safesnprintf(filepath, 256, "%s/%s", map->db_path, filename); if (!libconfig->load_file(&quest_db_conf, filepath)) return -1; @@ -580,7 +597,8 @@ int quest_read_db(void) * @see map->foreachpc * @param ap Ignored */ -int quest_reload_check_sub(struct map_session_data *sd, va_list ap) { +static int quest_reload_check_sub(struct map_session_data *sd, va_list ap) +{ int i, j; nullpo_ret(sd); @@ -609,7 +627,8 @@ int quest_reload_check_sub(struct map_session_data *sd, va_list ap) { /** * Clears the quest database for shutdown or reload. */ -void quest_clear_db(void) { +static void quest_clear_db(void) +{ int i; for (i = 0; i < MAX_QUEST_DB; i++) { @@ -624,12 +643,316 @@ void quest_clear_db(void) { } } +/* +* Limit the questinfo icon id to avoid client problems +*/ +static int quest_questinfo_validate_icon(int icon) +{ +#if PACKETVER >= 20170315 + if (icon < 0 || (icon > 10 && icon != 9999)) + icon = 9999; +#elif PACKETVER >= 20120410 + if (icon < 0 || (icon > 8 && icon != 9999) || icon == 7) + icon = 9999; // Default to nothing if icon id is invalid. +#else + if (icon < 0 || icon > 7) + icon = 0; + else + icon = icon + 1; +#endif + return icon; +} + +/** + * Refresh the questinfo bubbles on the player map. + * + * @param sd session data. + * @param qi questinfo data. + * + */ +static void quest_questinfo_refresh(struct map_session_data *sd) +{ + int i; + + nullpo_retv(sd); + + for (i = 0; i < VECTOR_LENGTH(map->list[sd->bl.m].qi_data); i++) { + struct questinfo *qi = &VECTOR_INDEX(map->list[sd->bl.m].qi_data, i); + // Remove the bubbles if one of the conditions is no longer valid. + if (quest->questinfo_validate(sd, qi) == false) { +#if PACKETVER >= 20120410 + clif->quest_show_event(sd, &qi->nd->bl, 9999, 0); +#else + clif->quest_show_event(sd, &qi->nd->bl, 0, 0); +#endif + } else { + clif->quest_show_event(sd, &qi->nd->bl, qi->icon, qi->color); + } + } +} + +/** + * Validate all possible conditions required for the questinfo + * + * @param sd session data. + * @param qi questinfo data. + * + * @retval true if conditions are correct. + * @retval false if one condition or more are in-correct. + */ +static bool quest_questinfo_validate(struct map_session_data *sd, struct questinfo *qi) +{ + nullpo_retr(false, sd); + nullpo_retr(false, qi); + if (qi->hasJob && quest->questinfo_validate_job(sd, qi) == false) + return false; + if (qi->sex_enabled && quest->questinfo_validate_sex(sd, qi) == false) + return false; + if ((qi->base_level.min != 0 || qi->base_level.max != 0) && quest->questinfo_validate_baselevel(sd, qi) == false) + return false; + if ((qi->job_level.min != 0 || qi->job_level.max != 0) && quest->questinfo_validate_joblevel(sd, qi) == false) + return false; + if (VECTOR_LENGTH(qi->items) > 0 && quest->questinfo_validate_items(sd, qi) == false) + return false; + if (qi->homunculus.level != 0 && quest->questinfo_validate_homunculus_level(sd, qi) == false) + return false; + if (qi->homunculus.class_ != 0 && quest->questinfo_validate_homunculus_type(sd, qi) == false) + return false; + if (VECTOR_LENGTH(qi->quest_requirement) > 0 && quest->questinfo_validate_quests(sd, qi) == false) + return false; + if (qi->mercenary_class != 0 && quest->questinfo_validate_mercenary_class(sd, qi) == false) + return false; + return true; +} + +/** + * Validate job required for the questinfo + * + * @param sd session data. + * @param qi questinfo data. + * + * @retval true if player job is matching the required. + * @retval false if player job is NOT matching the required. + */ +static bool quest_questinfo_validate_job(struct map_session_data *sd, struct questinfo *qi) +{ + nullpo_retr(false, sd); + nullpo_retr(false, qi); + if (sd->status.class == qi->job) + return true; + return false; +} + +/** + * Validate sex required for the questinfo + * + * @param sd session data. + * @param qi questinfo data. + * + * @retval true if player sex is matching the required. + * @retval false if player sex is NOT matching the required. + */ +static bool quest_questinfo_validate_sex(struct map_session_data *sd, struct questinfo *qi) +{ + nullpo_retr(false, sd); + nullpo_retr(false, qi); + if (sd->status.sex == qi->sex) + return true; + return false; +} + +/** + * Validate base level required for the questinfo + * + * @param sd session data. + * @param qi questinfo data. + * + * @retval true if player base level is included in the required level range. + * @retval false if player base level is NOT included in the required level range. + */ +static bool quest_questinfo_validate_baselevel(struct map_session_data *sd, struct questinfo *qi) +{ + nullpo_retr(false, sd); + nullpo_retr(false, qi); + if (sd->status.base_level >= qi->base_level.min && sd->status.base_level <= qi->base_level.max) + return true; + return false; +} + +/** + * Validate job level required for the questinfo + * + * @param sd session data. + * @param qi questinfo data. + * + * @retval true if player job level is included in the required level range. + * @retval false if player job level is NOT included in the required level range. + */ +static bool quest_questinfo_validate_joblevel(struct map_session_data *sd, struct questinfo *qi) +{ + nullpo_retr(false, sd); + nullpo_retr(false, qi); + if (sd->status.job_level >= qi->job_level.min && sd->status.job_level <= qi->job_level.max) + return true; + return false; +} + +/** + * Validate items list required for the questinfo + * + * @param sd session data. + * @param qi questinfo data. + * + * @retval true if player have all the items required. + * @retval false if player is missing one or more of the items required. + */ +static bool quest_questinfo_validate_items(struct map_session_data *sd, struct questinfo *qi) +{ + nullpo_retr(false, sd); + nullpo_retr(false, qi); + + for (int i = 0; i < VECTOR_LENGTH(qi->items); i++) { + struct questinfo_itemreq *item = &VECTOR_INDEX(qi->items, i); + int count = 0; + for (int j = 0; j < sd->status.inventorySize; j++) { + if (sd->status.inventory[j].nameid == item->nameid) + count += sd->status.inventory[j].amount; + } + if (count < item->min || count > item->max) + return false; + } + + return true; +} + +/** + * Validate minimal homunculus level required for the questinfo + * + * @param sd session data. + * @param qi questinfo data. + * + * @retval true if homunculus level >= the required value. + * @retval false if homunculus level smaller than the required value. + */ +static bool quest_questinfo_validate_homunculus_level(struct map_session_data *sd, struct questinfo *qi) +{ + nullpo_retr(false, sd); + nullpo_retr(false, qi); + + if (sd->hd == NULL) + return false; + if (!homun_alive(sd->hd)) + return false; + if (sd->hd->homunculus.level < qi->homunculus.level) + return false; + return true; +} + +/** + * Validate homunculus type required for the questinfo + * + * @param sd session data. + * @param qi questinfo data. + * + * @retval true if player's homunculus is matching the required. + * @retval false if player's homunculus is NOT matching the required. + */ +static bool quest_questinfo_validate_homunculus_type(struct map_session_data *sd, struct questinfo *qi) +{ + nullpo_retr(false, sd); + nullpo_retr(false, qi); + + if (sd->hd == NULL) + return false; + if (!homun_alive(sd->hd)) + return false; + if (homun->class2type(sd->hd->homunculus.class_) != qi->homunculus_type) + return false; + return true; +} + +/** + * Validate quest list required for the questinfo + * + * @param sd session data. + * @param qi questinfo data. + * + * @retval true if player have all the quests required. + * @retval false if player is missing one or more of the quests required. + */ +static bool quest_questinfo_validate_quests(struct map_session_data *sd, struct questinfo *qi) +{ + int i; + + nullpo_retr(false, sd); + nullpo_retr(false, qi); + + for (i = 0; i < VECTOR_LENGTH(qi->quest_requirement); i++) { + struct questinfo_qreq *quest_requirement = &VECTOR_INDEX(qi->quest_requirement, i); + int quest_progress = quest->check(sd, quest_requirement->id, HAVEQUEST); + if (quest_progress == -1) + quest_progress = 0; + else if (quest_progress == 0 || quest_progress == 1) + quest_progress = 1; + else + quest_progress = 2; + if (quest_progress != quest_requirement->state) + return false; + } + + return true; +} + +/** + * Validate mercenary class required for the questinfo + * + * @param sd session data. + * @param qi questinfo data. + * + * @retval true if player have a mercenary with the given class. + * @retval false if player does NOT have a mercenary with the given class. + */ +static bool quest_questinfo_validate_mercenary_class(struct map_session_data *sd, struct questinfo *qi) +{ + nullpo_retr(false, sd); + nullpo_retr(false, qi); + + if (sd->md == NULL) + return false; + + if (sd->md->mercenary.class_ != qi->mercenary_class) + return false; + + return true; +} + +/** + * Clears the questinfo data vector + * + * @param m mapindex. + * + */ +static void quest_questinfo_vector_clear(int m) +{ + int i; + + Assert_retv(m >= 0 && m < map->count); + + for (i = 0; i < VECTOR_LENGTH(map->list[m].qi_data); i++) { + struct questinfo *qi_data = &VECTOR_INDEX(map->list[m].qi_data, i); + VECTOR_CLEAR(qi_data->items); + VECTOR_CLEAR(qi_data->quest_requirement); + } + VECTOR_CLEAR(map->list[m].qi_data); +} + /** * Initializes the quest interface. * * @param minimal Run in minimal mode (skips most of the loading) */ -void do_init_quest(bool minimal) { +static void do_init_quest(bool minimal) +{ if (minimal) return; @@ -639,14 +962,16 @@ void do_init_quest(bool minimal) { /** * Finalizes the quest interface before shutdown. */ -void do_final_quest(void) { +static void do_final_quest(void) +{ quest->clear(); } /** * Reloads the quest database. */ -void do_reload_quest(void) { +static void do_reload_quest(void) +{ quest->clear(); quest->read_db(); @@ -658,7 +983,8 @@ void do_reload_quest(void) { /** * Initializes default values for the quest interface. */ -void quest_defaults(void) { +void quest_defaults(void) +{ quest = &quest_s; quest->db_data = db_data; @@ -681,4 +1007,18 @@ void quest_defaults(void) { quest->clear = quest_clear_db; quest->read_db = quest_read_db; quest->read_db_sub = quest_read_db_sub; + + quest->questinfo_validate_icon = quest_questinfo_validate_icon; + quest->questinfo_refresh = quest_questinfo_refresh; + quest->questinfo_validate = quest_questinfo_validate; + quest->questinfo_validate_job = quest_questinfo_validate_job; + quest->questinfo_validate_sex = quest_questinfo_validate_sex; + quest->questinfo_validate_baselevel = quest_questinfo_validate_baselevel; + quest->questinfo_validate_joblevel = quest_questinfo_validate_joblevel; + quest->questinfo_validate_items = quest_questinfo_validate_items; + quest->questinfo_validate_homunculus_level = quest_questinfo_validate_homunculus_level; + quest->questinfo_validate_homunculus_type = quest_questinfo_validate_homunculus_type; + quest->questinfo_validate_quests = quest_questinfo_validate_quests; + quest->questinfo_validate_mercenary_class = quest_questinfo_validate_mercenary_class; + quest->questinfo_vector_clear = quest_questinfo_vector_clear; } |