summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAsheraf <acheraf1998@gmail.com>2018-06-25 21:00:42 +0100
committerAsheraf <acheraf1998@gmail.com>2018-07-24 02:24:40 +0100
commit7c43dd908622e083b0fecf7fc8ebab61501186fa (patch)
treef742073f0dcc69ac5ac365ab24adaaac7aebc0df
parent620c55e87019a46ffffc6ffb69bea77df088b904 (diff)
downloadhercules-7c43dd908622e083b0fecf7fc8ebab61501186fa.tar.gz
hercules-7c43dd908622e083b0fecf7fc8ebab61501186fa.tar.bz2
hercules-7c43dd908622e083b0fecf7fc8ebab61501186fa.tar.xz
hercules-7c43dd908622e083b0fecf7fc8ebab61501186fa.zip
Questinfo System overhaul
-rw-r--r--doc/script_commands.txt29
-rw-r--r--src/common/mmo.h14
-rw-r--r--src/map/clif.c11
-rw-r--r--src/map/homunculus.c5
-rw-r--r--src/map/instance.c3
-rw-r--r--src/map/map.c12
-rw-r--r--src/map/map.h19
-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
11 files changed, 532 insertions, 79 deletions
diff --git a/doc/script_commands.txt b/doc/script_commands.txt
index 2653f052a..44937a34d 100644
--- a/doc/script_commands.txt
+++ b/doc/script_commands.txt
@@ -9337,12 +9337,10 @@ that fails, the command returns an empty string instead.
//=====================================
---------------------------------------
-*questinfo(<Quest ID>, <Icon> {, <Map Mark Color>{, <Job Class>}})
-
-This is esentially a combination of questprogress() and showevent(). Use this only
-in an OnInit label. For the Quest ID, specify the quest ID that you want
-checked if it has been started yet.
+*questinfo(<Icon> {, <Map Mark Color>})
+This is esentially a showevent() that supports different conditions that can be set using setquestinfo().
+Use this only in an OnInit label.
For Icon, use one of the following:
No Icon : QTYPE_NONE
@@ -9373,8 +9371,6 @@ the available color values are:
When a user shows up on a map, each NPC is checked for questinfo that has been set.
If questinfo is present, it will check if the quest has been started, if it has not, the bubble will appear.
-Optionally, you can also specify a Job Class if the quest bubble should only appear for a certain class.
-
Example
izlude,100,100,4 script Test 844,{
mes("[Test]");
@@ -9382,12 +9378,29 @@ Example
close();
OnInit:
- questinfo(1001, QTYPE_QUEST, 0, Job_Novice);
+ questinfo(QTYPE_QUEST);
end;
}
---------------------------------------
+*setquestinfo(<Type> {, <Values...>})
+
+Use this command ONLY after a questinfo()
+it allows you to set multiple required conditions for the quest bubble to show up.
+
+supported types: values
+ QINFO_JOB: job_id
+ QINFO_SEX: sex
+ QINFO_BASE_LEVEL: min, max
+ QINFO_JOB_LEVEL: min, max
+ QINFO_ITEM: item_id, amount // append to the items list on each use
+ QINFO_HOMUN_LEVEL: min
+ QINFO_HOMUN_TYPE: homunculus_type (0 - regular, 1 - evolved, 2 - S)
+ QINFO_QUEST: quest_id, state // append to the quests list on each use
+
+---------------------------------------
+
*setquest(<ID>{, <Time Limit>})
Place quest of <ID> in the users quest log, the state of which is "active".
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 061cb7073..2a2d87ccc 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -10068,14 +10068,9 @@ static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd)
int i;
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->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);
- }
- }
+
+ 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 9b0ca3ddd..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"
@@ -517,7 +518,7 @@ static void instance_del_map(int16 m)
aFree(map->list[m].zone_mf);
}
- VECTOR_CLEAR(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 a0dfdb886..0124a3035 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -3681,7 +3681,7 @@ static void do_final_maps(void)
if( map->list[i].channel )
channel->delete(map->list[i].channel);
- VECTOR_CLEAR(map->list[i].qi_data);
+ quest->questinfo_vector_clear(i);
HPM->data_store_destroy(&map->list[i].hdata);
}
@@ -5967,19 +5967,9 @@ 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 < VECTOR_LENGTH(map->list[m].qi_data); i++) {
- struct questinfo *qi_data = &VECTOR_INDEX(map->list[m].qi_data, i);
- if (qi_data->nd == qi->nd) {
- VECTOR_INDEX(map->list[m].qi_data, i) = *qi;
- return;
- }
- }
VECTOR_ENSURE(map->list[m].qi_data, 1, 1);
VECTOR_PUSH(map->list[m].qi_data, *qi);
}
diff --git a/src/map/map.h b/src/map/map.h
index b8956d72c..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;
};
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 4dc4ee333..ca7b5ca33 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -20915,63 +20915,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);
@@ -20983,19 +21097,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 < VECTOR_LENGTH(map->list[sd->bl.m].qi_data); i++) {
- struct questinfo *qi = &VECTOR_INDEX(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;
}
@@ -25097,7 +25198,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?"),
@@ -25558,6 +25660,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);