From 94b7b25456aa8a9de1e0f2a147d58dba6e5976dd Mon Sep 17 00:00:00 2001 From: Kisuka Date: Thu, 24 Oct 2013 07:03:46 -0700 Subject: Quest Bubbles (Actually Works Finally) --- doc/script_commands.txt | 70 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 18 deletions(-) (limited to 'doc/script_commands.txt') diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 9969ff1a8..751db221a 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -7886,10 +7886,48 @@ that fails, the command returns an empty string instead. ========================= --------------------------------------- +*questinfo , {, }; + +This is esentially a combination of checkquest 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. + +For Icon, use one of the following: + +No Icon : QTYPE_NONE +! Quest Icon : QTYPE_QUEST +? Quest Icon : QTYPE_QUEST2 +! Job Icon : QTYPE_JOB +? Job Icon : QTYPE_JOB2 +! Event Icon : QTYPE_EVENT +? Event Icon : QTYPE_EVENT2 +Warg : QTYPE_WARG +Warg Face : QTYPE_WARG2 (Only for packetver >= 20120410) + +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]"; + mes "Hello World."; + close; + + OnInit: + questinfo 1001, QTYPE_QUEST, Job_Novice; + end; +} + +--------------------------------------- + *setquest ; Place quest of in the users quest log, the state of which is "active". +If *questinfo is set, and the same ID is specified here, the icon will be cleared when the quest is set. + --------------------------------------- *completequest ; @@ -7937,27 +7975,23 @@ If parameter "HUNTING" is supplied: --------------------------------------- -*showevent , ; +*showevent ; Show a colored mark in the mini-map like "viewpoint" and an emotion on top of a NPC. This is used to indicate that a NPC has a quest or an event to -certain player/s. - -state can be: - 0 = disable ( Used to disable and remove the mark and the emotion from - the NPC. ) - 1 = exclamation emotion ( Used to show an important quest event to - certain player. ) - 2 = interrogation emotion ( Used to show an non-important quest event - to certain player. ) -Other value may cause client crashes. - -color can be: - 0 = yellow "Quest" - 1 = orange "Job" - 2 = green "Event" - 3 = an MVP flag -Other values show a transparent mark in the mini-map. +a certain player. + +Available Icons: + +Remove Icon : QTYPE_NONE +! Quest Icon : QTYPE_QUEST +? Quest Icon : QTYPE_QUEST2 +! Job Icon : QTYPE_JOB +? Job Icon : QTYPE_JOB2 +! Event Icon : QTYPE_EVENT +? Event Icon : QTYPE_EVENT2 +Warg : QTYPE_WARG +Warg Face : QTYPE_WARG2 (Only for packetver >= 20120410) ---------------------------------------- -- cgit v1.2.3-60-g2f50 From bf27a9e0cf1bfe9be298c4191010ba6876457d64 Mon Sep 17 00:00:00 2001 From: shennetsind Date: Fri, 25 Oct 2013 09:32:22 -0200 Subject: Quest Bubbles Update - Introduced questinfo cache, memory & processing speed improvement - Fixed mini-map marker support Special Thanks to Haruna Signed-off-by: shennetsind --- doc/script_commands.txt | 25 ++++++++++++---- src/map/clif.c | 31 +++++++------------ src/map/instance.c | 10 +++++++ src/map/map.c | 47 ++++++++++++++++++++++++++++- src/map/map.h | 17 +++++++++++ src/map/npc.c | 3 ++ src/map/npc.h | 6 ---- src/map/script.c | 80 +++++++++++++++++++++++++++++++------------------ 8 files changed, 158 insertions(+), 61 deletions(-) (limited to 'doc/script_commands.txt') diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 751db221a..bd0b032f1 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -7886,7 +7886,7 @@ that fails, the command returns an empty string instead. ========================= --------------------------------------- -*questinfo , {, }; +*questinfo , {, {, }}; This is esentially a combination of checkquest and showevent. Use this only in an OnInit label. For the Quest ID, specify the quest ID that you want @@ -7904,6 +7904,14 @@ No Icon : QTYPE_NONE Warg : QTYPE_WARG Warg Face : QTYPE_WARG2 (Only for packetver >= 20120410) +Map Mark Color, when used, creates a mark in the user's mini map on the position of the NPC, +the available color values are: + +0 - No Marker +1 - Yellow Marker +2 - Green Marker +3 - Purple Marker + 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. @@ -7916,7 +7924,7 @@ izlude,100,100,4 script Test 844,{ close; OnInit: - questinfo 1001, QTYPE_QUEST, Job_Novice; + questinfo 1001, QTYPE_QUEST, 0, Job_Novice; end; } @@ -7975,10 +7983,11 @@ If parameter "HUNTING" is supplied: --------------------------------------- -*showevent ; +*showevent {,} -Show a colored mark in the mini-map like "viewpoint" and an emotion on top -of a NPC. This is used to indicate that a NPC has a quest or an event to +Show an emotion on top of a NPC, and optionally, +a colored mark in the mini-map like "viewpoint". +This is used to indicate that a NPC has a quest or an event to a certain player. Available Icons: @@ -7993,6 +8002,12 @@ Remove Icon : QTYPE_NONE Warg : QTYPE_WARG Warg Face : QTYPE_WARG2 (Only for packetver >= 20120410) +Mark Color: +0 - No Mark +1 - Yellow Mark +2 - Green Mark +3 - Purple Mark + ---------------------------------------- ============================ diff --git a/src/map/clif.c b/src/map/clif.c index 3dba530f4..e47bd2bf4 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -9631,28 +9631,19 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) { skill->unit_move(&sd->bl,timer->gettick(),1); // NPC Quest / Event Icon Check [Kisuka] - #if PACKETVER >= 20090218 - for(i = 0; i < map->list[sd->bl.m].npc_num; i++) { - TBL_NPC *nd = map->list[sd->bl.m].npc[i]; - - // Make sure NPC exists and is not a warp - if(nd != NULL) - { - // Check if NPC has quest attached to it - if(nd->quest.quest_id > 0) - if(quest->check(sd, nd->quest.quest_id, HAVEQUEST) == -1) // Check if quest is not started - // Check if quest is job-specific, check is user is said job class. - if(nd->quest.hasJob == true) - { - if(sd->class_ == nd->quest.job) - clif->quest_show_event(sd, &nd->bl, nd->quest.icon, 0); - } - else { - clif->quest_show_event(sd, &nd->bl, nd->quest.icon, 0); - } +#if PACKETVER >= 20090218 + 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->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); } } - #endif + } +#endif } diff --git a/src/map/instance.c b/src/map/instance.c index 045be5431..83b538ffc 100644 --- a/src/map/instance.c +++ b/src/map/instance.c @@ -242,6 +242,13 @@ int instance_add_map(const char *name, int instance_id, bool usebasename, const } } + //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) ); + } + map->list[im].m = im; map->list[im].instance_id = instance_id; map->list[im].instance_src_map = m; @@ -443,6 +450,9 @@ void instance_del_map(int16 m) { aFree(map->list[m].zone_mf); } + if( map->list[m].qi_data ) + aFree(map->list[m].qi_data); + // Remove from instance for( i = 0; i < instance->list[map->list[m].instance_id].num_map; i++ ) { if( instance->list[map->list[m].instance_id].map[i] == m ) { diff --git a/src/map/map.c b/src/map/map.c index 749b9de81..412a266fc 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -3112,6 +3112,10 @@ void do_final_maps(void) { if( map->list[i].channel ) clif->chsys_delete(map->list[i].channel); + + if( map->list[i].qi_data ) + aFree(map->list[i].qi_data); + } map->zone_db_clear(); @@ -3177,6 +3181,12 @@ void map_flags_init(void) { map->list[i].misc_damage_rate = 100; 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; } } @@ -4958,6 +4968,38 @@ int map_get_new_bonus_id (void) { return map->bonus_id++; } +void map_add_questinfo(int m, struct questinfo *qi) { + unsigned short i; + + /* 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)); +} + +bool map_remove_questinfo(int m, struct npc_data *nd) { + unsigned short i; + + 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)); + } + return true; + } + } + + return false; +} + /** * @see DBApply */ @@ -5824,7 +5866,10 @@ void map_defaults(void) { map->delblcell = map_delblcell; map->get_new_bonus_id = map_get_new_bonus_id; - + + map->add_questinfo = map_add_questinfo; + map->remove_questinfo = map_remove_questinfo; + /** * mapit interface **/ diff --git a/src/map/map.h b/src/map/map.h index 83d5aa5a8..053d7ff2e 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -557,6 +557,17 @@ struct map_drop_list { int drop_per; }; + +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? */ +}; + + struct map_data { char name[MAP_NAME_LENGTH]; uint16 index; // The map index used by the mapindex* functions. @@ -690,6 +701,10 @@ struct map_data { int (*getcellp)(struct map_data* m,int16 x,int16 y,cell_chk cellchk); void (*setcell) (int16 m, int16 x, int16 y, cell_t cell, bool flag); char *cellPos; + + /* ShowEvent Data Cache */ + struct questinfo *qi_data; + unsigned short qi_count; }; /// Stores information about a remote map (for multi-mapserver setups). @@ -1029,6 +1044,8 @@ struct map_interface { void (*addblcell) (struct block_list *bl); void (*delblcell) (struct block_list *bl); int (*get_new_bonus_id) (void); + void (*add_questinfo) (int m, struct questinfo *qi); + bool (*remove_questinfo) (int m, struct npc_data *nd); }; struct map_interface *map; diff --git a/src/map/npc.c b/src/map/npc.c index c536dc856..79e976bb3 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -1790,6 +1790,9 @@ int npc_unload(struct npc_data* nd, bool single) { aFree(nd->path);/* remove now that no other instances exist */ } } + + if( single && nd->bl.m != -1 ) + map->remove_questinfo(nd->bl.m,nd); if( (nd->subtype == SHOP || nd->subtype == CASHSHOP) && nd->src_id == 0) //src check for duplicate shops [Orcao] aFree(nd->u.shop.shop_item); diff --git a/src/map/npc.h b/src/map/npc.h index c9184b22d..f809cb19c 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -48,12 +48,6 @@ struct npc_data { char* path;/* path dir */ enum npc_subtype subtype; int src_id; - struct { - int icon; - int quest_id; - bool hasJob; - int job; - } quest; union { struct { struct script_code *script; diff --git a/src/map/script.c b/src/map/script.c index cf2ddd812..c1281bf59 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -15456,9 +15456,10 @@ BUILDIN(readbook) BUILDIN(questinfo) { struct npc_data *nd = map->id2nd(st->oid); - int quest, icon, job; - - if( nd == NULL ) + int quest, icon, job, color = 0; + struct questinfo qi; + + if( nd == NULL || nd->bl.m == -1 ) return true; quest = script_getnum(st, 2); @@ -15474,24 +15475,34 @@ BUILDIN(questinfo) icon = icon + 1; #endif - nd->quest.quest_id = quest; - nd->quest.icon = icon; - nd->quest.hasJob = false; + qi.quest_id = quest; + qi.icon = (unsigned char)icon; + qi.nd = nd; + + if( script_hasdata(st, 4) ) { + color = script_getnum(st, 4); + if( color < 0 || color > 3 ) { + ShowWarning("buildin_questinfo: invalid color '%d', changing to 0\n",color); + script->reportfunc(st); + color = 0; + } + qi.color = (unsigned char)color; + } + + qi.hasJob = false; - if(script_hasdata(st, 4)) - { - job = script_getnum(st, 4); + if(script_hasdata(st, 5)) { + job = script_getnum(st, 5); if (!pcdb_checkid(job)) - { - ShowError("questinfo: Nonexistant Job Class.\n"); - } - else - { - nd->quest.hasJob = true; - nd->quest.job = job; + ShowError("buildin_questinfo: Nonexistant Job Class.\n"); + else { + qi.hasJob = true; + qi.job = (unsigned short)job; } } + + map->add_questinfo(nd->bl.m,&qi); return true; } @@ -15499,21 +15510,24 @@ BUILDIN(questinfo) BUILDIN(setquest) { struct map_session_data *sd = script->rid2sd(st); - struct npc_data *nd = map->id2nd(st->oid); - nullpo_retr(false,sd); + unsigned short i; - if (!nd) + if (!sd) return false; quest->add(sd, script_getnum(st, 2)); // If questinfo is set, remove quest bubble once quest is set. - if(nd->quest.quest_id > 0) - #if PACKETVER >= 20120410 - clif->quest_show_event(sd, &nd->bl, 9999, 0); - #else - clif->quest_show_event(sd, &nd->bl, 0, 0); - #endif + 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 == script_getnum(st, 2) ) { +#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; } @@ -15563,12 +15577,20 @@ BUILDIN(checkquest) BUILDIN(showevent) { TBL_PC *sd = script->rid2sd(st); struct npc_data *nd = map->id2nd(st->oid); - int icon; + int icon, color = 0; if( sd == NULL || nd == NULL ) return true; icon = script_getnum(st, 2); + if( script_hasdata(st, 3) ) { + color = script_getnum(st, 3); + if( color < 0 || color > 3 ) { + ShowWarning("buildin_showevent: invalid color '%d', changing to 0\n",color); + script->reportfunc(st); + color = 0; + } + } #if PACKETVER >= 20120410 if(icon < 0 || (icon > 8 && icon != 9999) || icon == 7) @@ -15580,7 +15602,7 @@ BUILDIN(showevent) { icon = icon + 1; #endif - clif->quest_show_event(sd, &nd->bl, icon, 0); + clif->quest_show_event(sd, &nd->bl, icon, color); return true; } @@ -18086,13 +18108,13 @@ void script_parse_builtin(void) { BUILDIN_DEF(useatcmd, "s"), //Quest Log System [Inkfish] - BUILDIN_DEF(questinfo, "ii?"), + BUILDIN_DEF(questinfo, "ii??"), BUILDIN_DEF(setquest, "i"), BUILDIN_DEF(erasequest, "i"), BUILDIN_DEF(completequest, "i"), BUILDIN_DEF(checkquest, "i?"), BUILDIN_DEF(changequest, "ii"), - BUILDIN_DEF(showevent, "i"), + BUILDIN_DEF(showevent, "i?"), /** * hQueue [Ind/Hercules] -- cgit v1.2.3-60-g2f50