summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaru <haru@dotalux.com>2018-07-24 05:22:42 +0200
committerGitHub <noreply@github.com>2018-07-24 05:22:42 +0200
commit1f1b82f737809a44aaabb98499293aa75eee8125 (patch)
treeddbe3ee0b93db54a5febe237524bdf4823596911
parent019f60feb9272ac5a527c8cdb430c2328108417c (diff)
parent90f7ef5dfb75ba3d819a4ac93518ebeadcfe7cc5 (diff)
downloadhercules-1f1b82f737809a44aaabb98499293aa75eee8125.tar.gz
hercules-1f1b82f737809a44aaabb98499293aa75eee8125.tar.bz2
hercules-1f1b82f737809a44aaabb98499293aa75eee8125.tar.xz
hercules-1f1b82f737809a44aaabb98499293aa75eee8125.zip
Merge pull request #2107 from Asheraf/questinfo_vec
Questinfo System overhaul
-rw-r--r--doc/script_commands.txt29
-rw-r--r--npc/re/jobs/novice/academy.txt60
-rw-r--r--npc/re/warps/cities/izlude.txt4
-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
13 files changed, 596 insertions, 118 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/npc/re/jobs/novice/academy.txt b/npc/re/jobs/novice/academy.txt
index 1b6576737..fb19e59a7 100644
--- a/npc/re/jobs/novice/academy.txt
+++ b/npc/re/jobs/novice/academy.txt
@@ -98,7 +98,9 @@ iz_int,56,32,3 script Wounded Swordsman#izint 4_TOWER_01,8,8,{
end;
OnInit:
- questinfo(21001, QTYPE_QUEST, 1, Job_Novice);
+ questinfo(QTYPE_QUEST, 1);
+ setquestinfo(QINFO_JOB, Job_Novice);
+ setquestinfo(QINFO_QUEST, 21001, 1);
end;
OnTouch_:
@@ -166,7 +168,9 @@ int_land,58,69,5 script Sailor#int_land 4W_SAILOR,{
}
OnInit:
- questinfo(21002, QTYPE_QUEST, 1, Job_Novice);
+ questinfo(QTYPE_QUEST, 1);
+ setquestinfo(QINFO_JOB, Job_Novice);
+ setquestinfo(QINFO_QUEST, 21002, 1);
end;
}
@@ -264,7 +268,9 @@ int_land,78,103,5 script Captain Carocc#int_land 4_M_REINDEER,{
close();
OnInit:
- questinfo(21002, QTYPE_QUEST, 1, Job_Novice);
+ questinfo(QTYPE_QUEST, 1);
+ setquestinfo(QINFO_JOB, Job_Novice);
+ setquestinfo(QINFO_QUEST, 21002, 1);
end;
}
@@ -366,7 +372,9 @@ int_land,73,100,3 script Lumin#iz_int 4_M_NOV_RUMIN,{
end;
}
OnInit:
- questinfo(7471, QTYPE_QUEST, 1, Job_Novice);
+ questinfo(QTYPE_QUEST, 1);
+ setquestinfo(QINFO_JOB, Job_Novice);
+ setquestinfo(QINFO_QUEST, 7471, 1);
end;
}
@@ -782,7 +790,9 @@ izlude,198,213,3 script Captain Carocc#iz 4_M_REINDEER,5,5,{
end;
OnInit:
- questinfo(7472, QTYPE_QUEST, 1, Job_Novice);
+ questinfo(QTYPE_QUEST, 1);
+ setquestinfo(QINFO_JOB, Job_Novice);
+ setquestinfo(QINFO_QUEST, 7472, 1);
//setquestLevel 7472 1 14
//SetQuestQuest 7472 7473 0
end;
@@ -1103,7 +1113,9 @@ izlude,122,207,3 script Criatura Academy Staff#0 4_M_KHKYEL,3,3,{
}
OnInit:
- questinfo(7473, QTYPE_QUEST, 1, Job_Novice);
+ questinfo(QTYPE_QUEST, 1);
+ setquestinfo(QINFO_JOB, Job_Novice);
+ setquestinfo(QINFO_QUEST, 7473, 1);
//setquestLevel 7473 1 14
end;
@@ -2581,7 +2593,9 @@ izlude,140,260,3 script Instructor Argos#iz 4_M_LIEMAN,{
}
OnInit:
- questinfo(15001, QTYPE_QUEST, 0, Job_Novice);
+ questinfo(QTYPE_QUEST);
+ setquestinfo(QINFO_JOB, Job_Novice);
+ setquestinfo(QINFO_QUEST, 15001, 1);
end;
}
@@ -2792,7 +2806,9 @@ iz_ac01,100,39,5 script Academy Receptionist#1 4_F_01,{
}
OnInit:
- questinfo(4269, QTYPE_QUEST, 0, Job_Novice);
+ questinfo(QTYPE_QUEST);
+ setquestinfo(QINFO_JOB, Job_Novice);
+ setquestinfo(QINFO_QUEST, 4269, 1);
end;
}
@@ -3669,7 +3685,8 @@ izlude,115,181,5 script Shop Helper#iz 4_F_KHELLISIA,{
}
OnInit:
- questinfo(1237, QTYPE_QUEST, 0);
+ questinfo(QTYPE_QUEST);
+ setquestinfo(QINFO_QUEST, 1237, 1);
end;
}
@@ -3854,7 +3871,8 @@ iz_ac01,53,74,3 script Attribute Expert#ac 1_M_WIZARD,{
}
OnInit:
- questinfo(2299, QTYPE_NONE, 0);
+ questinfo(QTYPE_QUEST, 1);
+ setquestinfo(QINFO_QUEST, 2299, 1);
end;
}
@@ -4406,10 +4424,21 @@ iz_ac01,147,50,3 script Dacquoise#ac 4_COOK,{
}
OnInit:
- questinfo(14154, QTYPE_QUEST, 1, Job_Novice);
- questinfo(14155, QTYPE_QUEST, 1, Job_Novice);
- questinfo(14156, QTYPE_QUEST, 1, Job_Novice);
- questinfo(14157, QTYPE_QUEST, 1, Job_Novice);
+ questinfo(QTYPE_QUEST, 1);
+ setquestinfo(QINFO_JOB, Job_Novice);
+ setquestinfo(QINFO_QUEST, 14154, 1);
+
+ questinfo(QTYPE_QUEST, 1);
+ setquestinfo(QINFO_JOB, Job_Novice);
+ setquestinfo(QINFO_QUEST, 14155, 1);
+
+ questinfo(QTYPE_QUEST, 1);
+ setquestinfo(QINFO_JOB, Job_Novice);
+ setquestinfo(QINFO_QUEST, 14156, 1);
+
+ questinfo(QTYPE_QUEST, 1);
+ setquestinfo(QINFO_JOB, Job_Novice);
+ setquestinfo(QINFO_QUEST, 14157, 1);
end;
}
@@ -4904,7 +4933,8 @@ iz_ac01,45,80,5 script Adept Adventurer#ac 4_M_JOB_BLACKSMITH,{
}
OnInit:
- questinfo(2298, QTYPE_QUEST, 0);
+ questinfo(QTYPE_QUEST);
+ setquestinfo(QINFO_QUEST, 2298, 1);
end;
}
diff --git a/npc/re/warps/cities/izlude.txt b/npc/re/warps/cities/izlude.txt
index 124e3ed1f..1e691e33b 100644
--- a/npc/re/warps/cities/izlude.txt
+++ b/npc/re/warps/cities/izlude.txt
@@ -82,7 +82,9 @@ OnTouch:
end;
OnInit:
- questinfo(21001, QTYPE_QUEST, 1, Job_Novice);
+ questinfo(QTYPE_QUEST, 1);
+ setquestinfo(QINFO_JOB, Job_Novice);
+ setquestinfo(QINFO_QUEST, 21001, 1);
end;
}
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);