summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/mmo.h14
-rw-r--r--src/map/clif.c15
-rw-r--r--src/map/homunculus.c5
-rw-r--r--src/map/instance.c12
-rw-r--r--src/map/map.c35
-rw-r--r--src/map/map.h22
-rw-r--r--src/map/pc.c5
-rw-r--r--src/map/quest.c291
-rw-r--r--src/map/quest.h14
-rw-r--r--src/map/script.c208
10 files changed, 527 insertions, 94 deletions
diff --git a/src/common/mmo.h b/src/common/mmo.h
index fa8f3048d..1b9562e9d 100644
--- a/src/common/mmo.h
+++ b/src/common/mmo.h
@@ -1271,6 +1271,20 @@ enum hz_char_ask_name_answer {
CHAR_ASK_NAME_ANS_OFFLINE = 3, // login-server offline
};
+/**
+ * Quest Info Types
+ */
+enum questinfo_type {
+ QINFO_JOB,
+ QINFO_SEX,
+ QINFO_BASE_LEVEL,
+ QINFO_JOB_LEVEL,
+ QINFO_ITEM,
+ QINFO_HOMUN_LEVEL,
+ QINFO_HOMUN_TYPE,
+ QINFO_QUEST
+};
+
/* packet size constant for itemlist */
#if MAX_INVENTORY > MAX_STORAGE && MAX_INVENTORY > MAX_CART
#define MAX_ITEMLIST MAX_INVENTORY
diff --git a/src/map/clif.c b/src/map/clif.c
index 904bfdd68..2a2d87ccc 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -10066,16 +10066,11 @@ static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd)
#if PACKETVER >= 20090218
{
int i;
- for(i = 0; i < map->list[sd->bl.m].qi_count; i++) {
- struct questinfo *qi = &map->list[sd->bl.m].qi_data[i];
- if( quest->check(sd, qi->quest_id, HAVEQUEST) == -1 ) {// Check if quest is not started
- if( qi->hasJob ) { // Check if quest is job-specific, check is user is said job class.
- if (sd->status.class == qi->job)
- clif->quest_show_event(sd, &qi->nd->bl, qi->icon, qi->color);
- } else {
- clif->quest_show_event(sd, &qi->nd->bl, qi->icon, qi->color);
- }
- }
+ 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);
+
+ if (quest->questinfo_validate(sd, qi))
+ clif->quest_show_event(sd, &qi->nd->bl, qi->icon, qi->color);
}
}
#endif
diff --git a/src/map/homunculus.c b/src/map/homunculus.c
index a6e7bb71c..6df272243 100644
--- a/src/map/homunculus.c
+++ b/src/map/homunculus.c
@@ -37,6 +37,7 @@
#include "map/party.h"
#include "map/pc.h"
#include "map/pet.h"
+#include "map/quest.h"
#include "map/script.h"
#include "map/skill.h"
#include "map/status.h"
@@ -406,6 +407,7 @@ static bool homunculus_levelup(struct homun_data *hd)
growth_int/10.0, growth_dex/10.0, growth_luk/10.0);
clif_disp_onlyself(hd->master, output);
}
+ quest->questinfo_refresh(hd->master);
return true;
}
@@ -419,6 +421,7 @@ static int homunculus_change_class(struct homun_data *hd, short class_)
hd->homunculus.class_ = class_;
status->set_viewdata(&hd->bl, class_);
homun->calc_skilltree(hd, 1);
+ quest->questinfo_refresh(hd->master);
return 1;
}
@@ -471,7 +474,7 @@ static bool homunculus_evolve(struct homun_data *hd)
if (!(battle_config.hom_setting&0x2))
skill->unit_move(&sd->hd->bl,timer->gettick(),1); // apply land skills immediately
-
+ quest->questinfo_refresh(sd);
return true;
}
diff --git a/src/map/instance.c b/src/map/instance.c
index 2c40449ad..8bd45ba50 100644
--- a/src/map/instance.c
+++ b/src/map/instance.c
@@ -30,6 +30,7 @@
#include "map/npc.h"
#include "map/party.h"
#include "map/pc.h"
+#include "map/quest.h"
#include "common/HPM.h"
#include "common/cbasetypes.h"
#include "common/db.h"
@@ -295,10 +296,10 @@ static int instance_add_map(const char *name, int instance_id, bool usebasename,
}
//Mimic questinfo
- if( map->list[m].qi_count ) {
- map->list[im].qi_count = map->list[m].qi_count;
- CREATE( map->list[im].qi_data, struct questinfo, map->list[im].qi_count );
- memcpy( map->list[im].qi_data, map->list[m].qi_data, map->list[im].qi_count * sizeof(struct questinfo) );
+ VECTOR_INIT(map->list[im].qi_data);
+ VECTOR_ENSURE(map->list[im].qi_data, VECTOR_LENGTH(map->list[m].qi_data), 1);
+ for (i = 0; i < VECTOR_LENGTH(map->list[m].qi_data); i++) {
+ VECTOR_PUSH(map->list[im].qi_data, VECTOR_INDEX(map->list[m].qi_data, i));
}
map->list[im].m = im;
@@ -517,8 +518,7 @@ static void instance_del_map(int16 m)
aFree(map->list[m].zone_mf);
}
- if( map->list[m].qi_data )
- aFree(map->list[m].qi_data);
+ quest->questinfo_vector_clear(m);
// Remove from instance
for( i = 0; i < instance->list[map->list[m].instance_id].num_map; i++ ) {
diff --git a/src/map/map.c b/src/map/map.c
index 8ea059700..0124a3035 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -3681,8 +3681,7 @@ static void do_final_maps(void)
if( map->list[i].channel )
channel->delete(map->list[i].channel);
- if( map->list[i].qi_data )
- aFree(map->list[i].qi_data);
+ quest->questinfo_vector_clear(i);
HPM->data_store_destroy(&map->list[i].hdata);
}
@@ -3752,11 +3751,7 @@ static void map_flags_init(void)
map->list[i].short_damage_rate = 100;
map->list[i].long_damage_rate = 100;
- if( map->list[i].qi_data )
- aFree(map->list[i].qi_data);
-
- map->list[i].qi_data = NULL;
- map->list[i].qi_count = 0;
+ VECTOR_INIT(map->list[i].qi_data);
}
}
@@ -5972,34 +5967,24 @@ static int map_get_new_bonus_id(void)
static void map_add_questinfo(int m, struct questinfo *qi)
{
- unsigned short i;
-
nullpo_retv(qi);
Assert_retv(m >= 0 && m < map->count);
- /* duplicate, override */
- for(i = 0; i < map->list[m].qi_count; i++) {
- if( map->list[m].qi_data[i].nd == qi->nd )
- break;
- }
-
- if( i == map->list[m].qi_count )
- RECREATE(map->list[m].qi_data, struct questinfo, ++map->list[m].qi_count);
- memcpy(&map->list[m].qi_data[i], qi, sizeof(struct questinfo));
+ VECTOR_ENSURE(map->list[m].qi_data, 1, 1);
+ VECTOR_PUSH(map->list[m].qi_data, *qi);
}
static bool map_remove_questinfo(int m, struct npc_data *nd)
{
unsigned short i;
+ nullpo_retr(false, nd);
Assert_retr(false, m >= 0 && m < map->count);
- for(i = 0; i < map->list[m].qi_count; i++) {
- struct questinfo *qi = &map->list[m].qi_data[i];
- if( qi->nd == nd ) {
- memset(&map->list[m].qi_data[i], 0, sizeof(struct questinfo));
- if( i != --map->list[m].qi_count ) {
- memmove(&map->list[m].qi_data[i],&map->list[m].qi_data[i+1],sizeof(struct questinfo)*(map->list[m].qi_count-i));
- }
+
+ for (i = 0; i < VECTOR_LENGTH(map->list[m].qi_data); i++) {
+ struct questinfo *qi_data = &VECTOR_INDEX(map->list[m].qi_data, i);
+ if (qi_data->nd == nd) {
+ VECTOR_ERASE(map->list[m].qi_data, i);
return true;
}
}
diff --git a/src/map/map.h b/src/map/map.h
index d6779ca91..04525b4d5 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -773,14 +773,31 @@ struct map_drop_list {
int drop_per;
};
+struct questinfo_qreq {
+ int id;
+ int state;
+};
struct questinfo {
struct npc_data *nd;
unsigned short icon;
unsigned char color;
- int quest_id;
bool hasJob;
unsigned short job;/* perhaps a mapid mask would be most flexible? */
+ bool sex_enabled;
+ int sex;
+ struct {
+ int min;
+ int max;
+ } base_level;
+ struct {
+ int min;
+ int max;
+ } job_level;
+ VECTOR_DECL(struct item) items;
+ struct s_homunculus homunculus;
+ int homunculus_type;
+ VECTOR_DECL(struct questinfo_qreq) quest_requirement;
};
@@ -922,8 +939,7 @@ struct map_data {
} cell_buf;
/* ShowEvent Data Cache */
- struct questinfo *qi_data;
- unsigned short qi_count;
+ VECTOR_DECL(struct questinfo) qi_data;
/* speeds up clif_updatestatus processing by causing hpmeter to run only when someone with the permission can view it */
unsigned short hpmeter_visible;
diff --git a/src/map/pc.c b/src/map/pc.c
index d9e41fde2..f2cff8ab3 100644
--- a/src/map/pc.c
+++ b/src/map/pc.c
@@ -4783,6 +4783,7 @@ static int pc_additem(struct map_session_data *sd, struct item *item_data, int a
pc->inventory_rental_add(sd, seconds);
}
}
+ quest->questinfo_refresh(sd);
return 0;
}
@@ -4820,6 +4821,7 @@ static int pc_delitem(struct map_session_data *sd, int n, int amount, int type,
clif->delitem(sd,n,amount,reason);
if(!(type&2))
clif->updatestatus(sd,SP_WEIGHT);
+ quest->questinfo_refresh(sd);
return 0;
}
@@ -6881,6 +6883,7 @@ static int pc_checkbaselevelup(struct map_session_data *sd)
party->send_levelup(sd);
pc->baselevelchanged(sd);
+ quest->questinfo_refresh(sd);
return 1;
}
@@ -6943,6 +6946,7 @@ static int pc_checkjoblevelup(struct map_session_data *sd)
clif->status_change(&sd->bl,SI_DEVIL1, 1, 0, 0, 0, 1); //Permanent blind effect from SG_DEVIL.
npc->script_event(sd, NPCE_JOBLVUP);
+ quest->questinfo_refresh(sd);
return 1;
}
@@ -9036,6 +9040,7 @@ static int pc_jobchange(struct map_session_data *sd, int class, int upper)
break;
}
}
+ quest->questinfo_refresh(sd);
return 0;
}
diff --git a/src/map/quest.c b/src/map/quest.c
index 614c79cb6..ab0b06974 100644
--- a/src/map/quest.c
+++ b/src/map/quest.c
@@ -25,6 +25,7 @@
#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"
@@ -154,6 +155,7 @@ static int quest_add(struct map_session_data *sd, int quest_id, unsigned int tim
#else
clif->quest_update_objective(sd, &sd->quest_log[n]);
#endif
+ quest->questinfo_refresh(sd);
if ((map->save_settings & 64) != 0)
chrif->save(sd, 0);
@@ -211,10 +213,10 @@ static int quest_change(struct map_session_data *sd, int qid1, int qid2)
#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;
}
@@ -254,6 +256,7 @@ static 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);
@@ -381,6 +384,7 @@ static int quest_update_status(struct map_session_data *sd, int quest_id, enum q
}
clif->quest_delete(sd, quest_id);
+ quest->questinfo_refresh(sd);
if( map->save_settings&64 )
chrif->save(sd,0);
@@ -638,6 +642,278 @@ static 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;
+ 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)
+{
+ int i, idx;
+
+ nullpo_retr(false, sd);
+ nullpo_retr(false, qi);
+
+
+ for (i = 0; i < VECTOR_LENGTH(qi->items); i++) {
+ struct item *item = &VECTOR_INDEX(qi->items, i);
+ idx = pc->search_inventory(sd, item->nameid);
+ if (idx == INDEX_NOT_FOUND)
+ return false;
+ if (sd->status.inventory[idx].amount < item->amount)
+ 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);
+ if (quest->check(sd, quest_requirement->id, HAVEQUEST) != quest_requirement->state)
+ 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.
*
@@ -699,4 +975,17 @@ 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_vector_clear = quest_questinfo_vector_clear;
}
diff --git a/src/map/quest.h b/src/map/quest.h
index d33d84885..305a48df1 100644
--- a/src/map/quest.h
+++ b/src/map/quest.h
@@ -28,6 +28,7 @@
struct block_list;
struct config_setting_t;
struct map_session_data;
+struct questinfo;
#define MAX_QUEST_DB (60355+1) // Highest quest ID + 1
@@ -79,6 +80,19 @@ struct quest_interface {
void (*clear) (void);
int (*read_db) (void);
struct quest_db *(*read_db_sub) (struct config_setting_t *cs, int n, const char *source);
+
+ int (*questinfo_validate_icon) (int icon);
+ void (*questinfo_refresh) (struct map_session_data *sd);
+ bool (*questinfo_validate) (struct map_session_data *sd, struct questinfo *qi);
+ bool (*questinfo_validate_job) (struct map_session_data *sd, struct questinfo *qi);
+ bool (*questinfo_validate_sex) (struct map_session_data *sd, struct questinfo *qi);
+ bool (*questinfo_validate_baselevel) (struct map_session_data *sd, struct questinfo *qi);
+ bool (*questinfo_validate_joblevel) (struct map_session_data *sd, struct questinfo *qi);
+ bool (*questinfo_validate_items) (struct map_session_data *sd, struct questinfo *qi);
+ bool (*questinfo_validate_homunculus_level) (struct map_session_data *sd, struct questinfo *qi);
+ bool (*questinfo_validate_homunculus_type) (struct map_session_data *sd, struct questinfo *qi);
+ bool (*questinfo_validate_quests) (struct map_session_data *sd, struct questinfo *qi);
+ void (*questinfo_vector_clear) (int m);
};
#ifdef HERCULES_CORE
diff --git a/src/map/script.c b/src/map/script.c
index 260a9848c..9a5d33604 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -20913,63 +20913,177 @@ static BUILDIN(readbook)
static BUILDIN(questinfo)
{
struct npc_data *nd = map->id2nd(st->oid);
- int quest_id, icon;
- struct questinfo qi;
+ struct questinfo qi = { 0 };
+ int icon = script_getnum(st, 2);
- if( nd == NULL || nd->bl.m == -1 )
+ if (nd == NULL)
return true;
- quest_id = script_getnum(st, 2);
- icon = script_getnum(st, 3);
-
-#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
+ if (nd->bl.m == -1) {
+ ShowWarning("buildin_questinfo: questinfo cannot be set for an npc with no valid map.\n");
+ return false;
+ }
- qi.quest_id = quest_id;
- qi.icon = (unsigned char)icon;
qi.nd = nd;
-
- if (script_hasdata(st, 4)) {
- int color = script_getnum(st, 4);
+ qi.icon = quest->questinfo_validate_icon(icon);
+ if (script_hasdata(st, 3)) {
+ int color = script_getnum(st, 3);
if (color < 0 || color > 3) {
- ShowWarning("buildin_questinfo: invalid color '%d', changing to 0\n",color);
+ ShowWarning("buildin_questinfo: invalid color '%d', defaulting to 0.\n", color);
script->reportfunc(st);
color = 0;
}
qi.color = (unsigned char)color;
}
- qi.hasJob = false;
+ map->add_questinfo(nd->bl.m, &qi);
+ return true;
+}
- if (script_hasdata(st, 5)) {
- int job = script_getnum(st, 5);
+static BUILDIN(setquestinfo)
+{
+ struct npc_data *nd = map->id2nd(st->oid);
+ struct questinfo *qi = NULL;
+ uint32 type = script_getnum(st, 2);
- if (!pc->db_checkid(job)) {
- ShowError("buildin_questinfo: Nonexistant Job Class.\n");
- } else {
- qi.hasJob = true;
- qi.job = (unsigned short)job;
+ if (nd == NULL)
+ return true;
+
+ if (nd->bl.m == -1) {
+ ShowWarning("buildin_setquestinfo: questinfo cannot be set for an npc with no valid map.\n");
+ return false;
+ }
+
+ qi = &VECTOR_LAST(map->list[nd->bl.m].qi_data);
+ if (qi == NULL) {
+ ShowWarning("buildin_setquestinfo: no valide questinfo data has been found for this npc.\n");
+ return false;
+ }
+ if (qi->nd != nd) {
+ ShowWarning("buildin_setquestinfo: invalid usage, setquestinfo must be used only after questinfo.\n");
+ return false;
+ }
+ switch (type) {
+ case QINFO_JOB:
+ {
+ int jobid = script_getnum(st, 3);
+ if (!pc->db_checkid(jobid)) {
+ ShowWarning("buildin_setquestinfo: invalid job id given (%d).\n", jobid);
+ return false;
}
+ qi->hasJob = true;
+ qi->job = jobid;
+ break;
}
+ case QINFO_SEX:
+ {
+ int sex = script_getnum(st, 3);
+ if (sex != SEX_MALE && sex != SEX_FEMALE) {
+ ShowWarning("buildin_setquestinfo: unsupported sex has been given (%d).\n", sex);
+ return false;
+ }
+ qi->sex_enabled = true;
+ qi->sex = sex;
+ break;
+ }
+ case QINFO_BASE_LEVEL:
+ {
+ int min = script_getnum(st, 3);
+ int max = script_getnum(st, 4);
+ if (min > max) {
+ ShowWarning("buildin_setquestinfo: minimal level (%d) is bigger than the maximal level (%d).\n", min, max);
+ return false;
+ }
+ qi->base_level.min = min;
+ qi->base_level.max = max;
+ break;
+ }
+ case QINFO_JOB_LEVEL:
+ {
+ int min = script_getnum(st, 3);
+ int max = script_getnum(st, 4);
+ if (min > max) {
+ ShowWarning("buildin_setquestinfo: minimal level (%d) is bigger than the maximal level (%d).\n", min, max);
+ return false;
+ }
+ qi->job_level.min = min;
+ qi->job_level.max = max;
+ break;
+ }
+ case QINFO_ITEM:
+ {
+ struct item item = { 0 };
+
+ item.nameid = script_getnum(st, 3);
+ item.amount = script_getnum(st, 4);
+
+ if (itemdb->exists(item.nameid) == NULL) {
+ ShowWarning("buildin_setquestinfo: non existing item (%d) have been given.\n", item.nameid);
+ return false;
+ }
+ if (item.amount <= 0 || item.amount > MAX_AMOUNT) {
+ ShowWarning("buildin_setquestinfo: given amount (%d) must be bigger than 0 and smaller than %d.\n", item.amount, MAX_AMOUNT + 1);
+ return false;
+ }
+ if (VECTOR_LENGTH(qi->items) == 0)
+ VECTOR_INIT(qi->items);
+ VECTOR_ENSURE(qi->items, 1, 1);
+ VECTOR_PUSH(qi->items, item);
+ break;
+ }
+ case QINFO_HOMUN_LEVEL:
+ {
+ int min = script_getnum(st, 3);
+ if (min > battle_config.hom_max_level && min > battle_config.hom_S_max_level) {
+ ShowWarning("buildin_setquestinfo: minimum homunculus level given (%d) is bigger than the max possible level.\n", min);
+ return false;
+ }
+ qi->homunculus.level = min;
+ break;
+ }
+ case QINFO_HOMUN_TYPE:
+ {
+ int hom_type = script_getnum(st, 3);
+ if (hom_type < HT_REG || hom_type > HT_S) {
+ ShowWarning("buildin_setquestinfo: invalid homunculus type (%d).\n", hom_type);
+ return false;
+ }
+ qi->homunculus_type = hom_type;
+ break;
+ }
+ case QINFO_QUEST:
+ {
+ struct questinfo_qreq quest_req = { 0 };
+ struct quest_db *quest_data = NULL;
+
+ quest_req.id = script_getnum(st, 3);
+ quest_req.state = script_getnum(st, 4);
- map->add_questinfo(nd->bl.m,&qi);
+ quest_data = quest->db(quest_req.id);
+ if (quest_data == &quest->dummy) {
+ ShowWarning("buildin_setquestinfo: invalid quest given (%d).\n", quest_req.id);
+ return false;
+ }
+ if (quest_req.state < Q_INACTIVE || quest_req.state > Q_COMPLETE) {
+ ShowWarning("buildin_setquestinfo: invalid quest state given (%d).\n", quest_req.state);
+ return false;
+ }
+ if (VECTOR_LENGTH(qi->quest_requirement) == 0)
+ VECTOR_INIT(qi->quest_requirement);
+ VECTOR_ENSURE(qi->quest_requirement, 1, 1);
+ VECTOR_PUSH(qi->quest_requirement, quest_req);
+ break;
+ }
+ default:
+ ShowWarning("buildin_setquestinfo: invalid type given (%u).\n", type);
+ return false;
+ }
return true;
}
static BUILDIN(setquest)
{
- unsigned short i;
int quest_id;
unsigned int time_limit;
struct map_session_data *sd = script->rid2sd(st);
@@ -20981,19 +21095,6 @@ static BUILDIN(setquest)
time_limit = script_hasdata(st, 3) ? script_getnum(st, 3) : 0;
quest->add(sd, quest_id, time_limit);
-
- // If questinfo is set, remove quest bubble once quest is set.
- for (i = 0; i < map->list[sd->bl.m].qi_count; i++) {
- struct questinfo *qi = &map->list[sd->bl.m].qi_data[i];
- if (qi->quest_id == quest_id) {
-#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
- }
- }
-
return true;
}
@@ -25095,7 +25196,8 @@ static void script_parse_builtin(void)
BUILDIN_DEF(checkbound, "i???????"),
//Quest Log System [Inkfish]
- BUILDIN_DEF(questinfo, "ii??"),
+ BUILDIN_DEF(questinfo, "i?"),
+ BUILDIN_DEF(setquestinfo, "i??"),
BUILDIN_DEF(setquest, "i?"),
BUILDIN_DEF(erasequest, "i?"),
BUILDIN_DEF(completequest, "i?"),
@@ -25556,6 +25658,16 @@ static void script_hardcoded_constants(void)
script->set_constant("P_AIRSHIP_ITEM_NOT_ENOUGH", P_AIRSHIP_ITEM_NOT_ENOUGH, false, false);
script->set_constant("P_AIRSHIP_ITEM_INVALID", P_AIRSHIP_ITEM_INVALID, false, false);
+ script->constdb_comment("questinfo types");
+ script->set_constant("QINFO_JOB", QINFO_JOB, false, false);
+ script->set_constant("QINFO_SEX", QINFO_SEX, false, false);
+ script->set_constant("QINFO_BASE_LEVEL", QINFO_BASE_LEVEL, false, false);
+ script->set_constant("QINFO_JOB_LEVEL", QINFO_JOB_LEVEL, false, false);
+ script->set_constant("QINFO_ITEM", QINFO_ITEM, false, false);
+ script->set_constant("QINFO_HOMUN_LEVEL", QINFO_HOMUN_LEVEL, false, false);
+ script->set_constant("QINFO_HOMUN_TYPE", QINFO_HOMUN_TYPE, false, false);
+ script->set_constant("QINFO_QUEST", QINFO_QUEST, false, false);
+
script->constdb_comment("Renewal");
#ifdef RENEWAL
script->set_constant("RENEWAL", 1, false, false);