diff options
-rw-r--r-- | Changelog-Trunk.txt | 7 | ||||
-rw-r--r-- | doc/script_commands.txt | 33 | ||||
-rw-r--r-- | src/map/npc.c | 262 | ||||
-rw-r--r-- | src/map/npc.h | 2 |
4 files changed, 236 insertions, 68 deletions
diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt index 53b3a613e..65ea86bd5 100644 --- a/Changelog-Trunk.txt +++ b/Changelog-Trunk.txt @@ -3,6 +3,13 @@ Date Added AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK. IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK. +2008/12/20 + * Extended how duplicates work: (based on Orcao's work in bugreport:2361) [FlavioJS] + - you can duplicate warps/shops/cashshops/npcs (before only npcs could be duplicated) + - warp duplicates inherit the target location + - shop/cashshop duplicates inherit the item list + - npc duplicates inherit the script code (backward compatible behaviour) + - updated script_commands.txt with the information 2008/12/17 * Removed charcommand code and allowed atcommand code to support its functionality. [SketchyPhoenix] - Charcommands still retain their '#' symbol but now looks for a character name as the first parameter instead of last. diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 6a3f6c6ac..becbc060d 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -4,7 +4,7 @@ //= A reference manual for the eAthena scripting language. //= Commands are sorted depending on their functionality. //===== Version =========================================== -//= 3.34.20081111 +//= 3.25.20081220 //========================================================= //= 1.0 - First release, filled will as much info as I could //= remember or figure out, most likely there are errors, @@ -124,8 +124,10 @@ //= Adjusted the 'getequipname' description to match src [ultramage] //= 3.23.20080909 //= Added WoE SE related commands. [L0ne_W0lf] -//= 3.34.20081111 +//= 3.24.20081111 //= Changed the error behaviour of delitem/delitem2/Zeny. [FlavioJS] +//= 3.25.20081220 +//= Extended the behaviour of duplicates (warps/shops/cashshops). [FlavioJS] //========================================================= This document is a reference manual for all the scripting commands and functions @@ -375,15 +377,6 @@ triggered. It may contain commands and function calls, descriptions of which compose most of this document. It has to be in curly brackets, unlike elsewhere where we use curly brackets, these do NOT signify an optional parameter. -** Define an NPC duplicate. - -<map name>,<x>,<y>,<facing>%TAB%duplicate(<label>)%TAB%<name>%TAB%<sprite id> -<map name>,<x>,<y>,<facing>%TAB%duplicate(<label>)%TAB%<name>%TAB%<sprite id>,<triggerX>,<triggerY> - -This will duplicate an NPC referred to by 'label'. The duplicate runs the same -code as the source NPC, but has its own name, location, facing, sprite ID and -trigger area (in other words, the duplicate does not 'inherit' any of these). - ** Define a 'floating' NPC object. -%TAB%script%TAB%<NPC Name>%TAB%-1,{<code>} @@ -393,8 +386,9 @@ normally mean it's pointless since it can't do anything, but there are exceptions, mostly related to running scripts at specified time, which is what these floating NPC objects are for. More on that below. -** Define a shop NPC. +** Define a shop/cashshop NPC. +-%TAB%shop%TAB%<NPC Name>%TAB%<sprite id>,<itemid>:<price>{,<itemid>:<price>...} <map name>,<x>,<y>,<facing>%TAB%shop%TAB%<NPC Name>%TAB%<sprite id>,<itemid>:<price>{,<itemid>:<price>...} This will define a shop NPC, which, when triggered (which can only be done by @@ -415,6 +409,21 @@ This type of shop will not allow you to sell items at it, you may only purchase items here. The layout used to define sale items still count, and "<price>" refers to how many points will be spent purchasing the them. +** Define an warp/shop/cashshop/NPC duplicate. + +warp: <map name>,<x>,<y>,<facing>%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<spanx>,<spany> +shop/cashshop/npc: -%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<sprite id> +shop/cashshop/npc: <map name>,<x>,<y>,<facing>%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<sprite id> +npc: -%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<sprite id>,<triggerX>,<triggerY> +npc: <map name>,<x>,<y>,<facing>%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<sprite id>,<triggerX>,<triggerY> + +This will duplicate an warp/shop/cashshop/NPC referred to by 'label'. +Warp duplicates inherit the target location. +Shop/cashshop duplicates inherit the item list. +NPC duplicates inherit the script code. +The rest (name, location, facing, sprite ID, span/trigger area) +is obtained from the definition of the duplicate (not inherited). + ** Define a function object function%TAB%script%TAB%<function name>%TAB%{<code>} diff --git a/src/map/npc.c b/src/map/npc.c index 639de90d7..17ba64984 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -1356,11 +1356,8 @@ static int npc_unload_dup_sub(struct npc_data* nd, va_list args) { int src_id; - if( nd->subtype != SCRIPT ) - return 0; - src_id = va_arg(args, int); - if (nd->u.scr.src_id == src_id) + if (nd->src_id == src_id) npc_unload(nd); return 0; } @@ -1386,7 +1383,7 @@ int npc_unload(struct npc_data* nd) npc_chat_finalize(nd); // deallocate npc PCRE data structures #endif - if( nd->subtype == SHOP || nd->subtype == CASHSHOP ) + if( (nd->subtype == SHOP || nd->subtype == CASHSHOP) && nd->src_id == 0) //src check for duplicate shops [Orcao] aFree(nd->u.shop.shop_item); else if( nd->subtype == SCRIPT ) @@ -1401,7 +1398,7 @@ int npc_unload(struct npc_data* nd) } if (nd->u.scr.timer_event) aFree(nd->u.scr.timer_event); - if (nd->u.scr.src_id == 0) { + if (nd->src_id == 0) { if(nd->u.scr.script) { script_free_code(nd->u.scr.script); nd->u.scr.script = NULL; @@ -1892,12 +1889,11 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons struct script_code *script; int i; const char* end; + const char* script_start; struct npc_label_list* label_list; int label_list_num; - int src_id; struct npc_data* nd; - struct npc_data* dnd; if( strcmp(w1, "-") == 0 ) {// floating npc @@ -1915,53 +1911,27 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons m = map_mapname2mapid(mapname); } - if( strcmp(w2, "script") == 0 ) - {// parsing script with curly - const char* script_start; - - script_start = strstr(start,",{"); - end = strchr(start,'\n'); - if( strstr(w4,",{") == NULL || script_start == NULL || (end != NULL && script_start > end) ) - { - ShowError("npc_parse_script: Missing left curly ',{' in file '%s', line '%d'. Skipping the rest of the file.\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4); - return NULL;// can't continue - } - ++script_start; - - end = npc_skip_script(script_start, buffer, filepath); - if( end == NULL ) - return NULL;// (simple) parse error, don't continue - - script = parse_script(script_start, filepath, strline(buffer,script_start-buffer), SCRIPT_USE_LABEL_DB); - label_list = NULL; - label_list_num = 0; - src_id = 0; - if( script ) - { - DBMap* label_db = script_get_label_db(); - label_db->foreach(label_db, npc_convertlabel_db, &label_list, &label_list_num, filepath); - label_db->clear(label_db, NULL); // not needed anymore, so clear the db - } + script_start = strstr(start,",{"); + end = strchr(start,'\n'); + if( strstr(w4,",{") == NULL || script_start == NULL || (end != NULL && script_start > end) ) + { + ShowError("npc_parse_script: Missing left curly ',{' in file '%s', line '%d'. Skipping the rest of the file.\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4); + return NULL;// can't continue } - else - {// duplicate npc - char srcname[128]; + ++script_start; - end = strchr(start,'\n'); - if( sscanf(w2,"duplicate(%127[^)])",srcname) != 1 ) - { - ShowError("npc_parse_script: bad duplicate name in file '%s', line '%d' : %s\n", filepath, strline(buffer,start-buffer), w2); - return strchr(start, '\n');// next line, try to continue - } - dnd = npc_name2id(srcname); - if( dnd == NULL) { - ShowError("npc_parse_script: original npc not found for duplicate in file '%s', line '%d' : %s\n", filepath, strline(buffer,start-buffer), srcname); - return strchr(start, '\n');// next line, continue - } - script = dnd->u.scr.script; - label_list = dnd->u.scr.label_list;// TODO duplicate this? - label_list_num = dnd->u.scr.label_list_num; - src_id = dnd->bl.id; + end = npc_skip_script(script_start, buffer, filepath); + if( end == NULL ) + return NULL;// (simple) parse error, don't continue + + script = parse_script(script_start, filepath, strline(buffer,script_start-buffer), SCRIPT_USE_LABEL_DB); + label_list = NULL; + label_list_num = 0; + if( script ) + { + DBMap* label_db = script_get_label_db(); + label_db->foreach(label_db, npc_convertlabel_db, &label_list, &label_list_num, filepath); + label_db->clear(label_db, NULL); // not needed anymore, so clear the db } CREATE(nd, struct npc_data, 1); @@ -1987,7 +1957,6 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons nd->class_ = class_; nd->speed = 200; nd->u.scr.script = script; - nd->u.scr.src_id = src_id; nd->u.scr.label_list = label_list; nd->u.scr.label_list_num = label_list_num; @@ -2069,6 +2038,189 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons return end; } +/// Duplicate a warp, shop, cashshop or script. [Orcao] +/// warp: <map name>,<x>,<y>,<facing>%TAB%duplicate(<name of target>)%TAB%<NPC Name>%TAB%<spanx>,<spany> +/// shop/cashshop/npc: -%TAB%duplicate(<name of target>)%TAB%<NPC Name>%TAB%<sprite id> +/// shop/cashshop/npc: <map name>,<x>,<y>,<facing>%TAB%duplicate(<name of target>)%TAB%<NPC Name>%TAB%<sprite id> +/// npc: -%TAB%duplicate(<name of target>)%TAB%<NPC Name>%TAB%<sprite id>,<triggerX>,<triggerY> +/// npc: <map name>,<x>,<y>,<facing>%TAB%duplicate(<name of target>)%TAB%<NPC Name>%TAB%<sprite id>,<triggerX>,<triggerY> +const char* npc_parse_duplicate(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath) +{ + int x, y, dir, m, xs = -1, ys = -1, class_ = 0; + char mapname[32]; + char srcname[128]; + int i; + const char* end; + + int src_id; + int type; + struct npc_data* nd; + struct npc_data* dnd; + + end = strchr(start,'\n'); + // get the npc being duplicated + if( sscanf(w2,"duplicate(%127[^)])",srcname) != 1 ) + { + ShowError("npc_parse_script: bad duplicate name in file '%s', line '%d' : %s\n", filepath, strline(buffer,start-buffer), w2); + return end;// next line, try to continue + } + dnd = npc_name2id(srcname); + if( dnd == NULL) { + ShowError("npc_parse_script: original npc not found for duplicate in file '%s', line '%d' : %s\n", filepath, strline(buffer,start-buffer), srcname); + return end;// next line, try to continue + } + src_id = dnd->bl.id; + type = dnd->subtype; + + // get placement + if( (type==SHOP || type==CASHSHOP || type==SCRIPT) && strcmp(w1, "-") == 0 ) + {// floating shop/chashshop/script + x = y = dir = 0; + m = -1; + } + else + { + if( sscanf(w1, "%31[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 )// <map name>,<x>,<y>,<facing> + { + ShowError("npc_parse_duplicate: Invalid placement format for duplicate in file '%s', line '%d'. Skipping line...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4); + return end;// next line, try to continue + } + m = map_mapname2mapid(mapname); + } + + if( type == WARP && sscanf(w4, "%d,%d", &xs, &ys) == 2 );// <spanx>,<spany> + else if( type == SCRIPT && sscanf(w4, "%d,%d,%d", &class_, &xs, &ys) == 3);// <sprite id>,<triggerX>,<triggerY> + else if( type != WARP ) class_ = atoi(w4);// <sprite id> + else + { + ShowError("npc_parse_duplicate: Invalid span format for duplicate warp in file '%s', line '%d'. Skipping line...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4); + return end;// next line, try to continue + } + + CREATE(nd, struct npc_data, 1); + + nd->bl.prev = nd->bl.next = NULL; + nd->bl.m = m; + nd->bl.x = x; + nd->bl.y = y; + npc_parsename(nd, w3, start, buffer, filepath); + nd->bl.id = npc_get_new_npc_id(); + nd->class_ = class_; + nd->speed = 200; + nd->src_id = src_id; + nd->bl.type = BL_NPC; + nd->subtype = type; + switch( type ) + { + case SCRIPT: + ++npc_script; + nd->u.scr.xs = xs; + nd->u.scr.ys = ys; + nd->u.scr.script = dnd->u.scr.script; + nd->u.scr.label_list = dnd->u.scr.label_list; + nd->u.scr.label_list_num = dnd->u.scr.label_list_num; + break; + + case SHOP: + case CASHSHOP: + ++npc_shop; + nd->u.shop.shop_item = dnd->u.shop.shop_item; + nd->u.shop.count = dnd->u.shop.count; + break; + + case WARP: + ++npc_warp; + if( !battle_config.warp_point_debug ) + nd->class_ = WARP_CLASS; + else + nd->class_ = WARP_DEBUG_CLASS; + nd->u.warp.xs = xs; + nd->u.warp.ys = ys; + nd->u.warp.mapindex = dnd->u.warp.mapindex; + nd->u.warp.x = dnd->u.warp.x; + nd->u.warp.y = dnd->u.warp.y; + break; + } + + //Add the npc to its location + if( m >= 0 ) + { + map_addnpc(m, nd); + status_change_init(&nd->bl); + unit_dataset(&nd->bl); + nd->ud.dir = dir; + npc_setcells(nd); + map_addblock(&nd->bl); + if( class_ >= 0 ) + { + status_set_viewdata(&nd->bl, nd->class_); + clif_spawn(&nd->bl); + } + } + else + { + // we skip map_addnpc, but still add it to the list of ID's + map_addiddb(&nd->bl); + } + strdb_put(npcname_db, nd->exname, nd); + + if( type != SCRIPT ) + return end; + + //Handle labels + //----------------------------------------- + // イベント用ラベルデータのエクスポート + for (i = 0; i < nd->u.scr.label_list_num; i++) + { + char* lname = nd->u.scr.label_list[i].name; + int pos = nd->u.scr.label_list[i].pos; + + if ((lname[0] == 'O' || lname[0] == 'o') && (lname[1] == 'N' || lname[1] == 'n')) + { + struct event_data* ev; + char buf[NAME_LENGTH*2+3]; // 24 for npc name + 24 for label + 2 for a "::" and 1 for EOS + snprintf(buf, ARRAYLENGTH(buf), "%s::%s", nd->exname, lname); + + // generate the data and insert it + CREATE(ev, struct event_data, 1); + ev->nd = nd; + ev->pos = pos; + if( strdb_put(ev_db, buf, ev) != NULL )// There was already another event of the same name? + ShowWarning("npc_parse_duplicate : duplicate event %s (%s)\n", buf, filepath); + } + } + + //----------------------------------------- + // ラベルデータからタイマーイベント取り込み + for (i = 0; i < nd->u.scr.label_list_num; i++){ + int t = 0, k = 0; + char *lname = nd->u.scr.label_list[i].name; + int pos = nd->u.scr.label_list[i].pos; + if (sscanf(lname, "OnTimer%d%n", &t, &k) == 1 && lname[k] == '\0') { + // タイマーイベント + struct npc_timerevent_list *te = nd->u.scr.timer_event; + int j, k = nd->u.scr.timeramount; + if (te == NULL) + te = (struct npc_timerevent_list *)aMallocA(sizeof(struct npc_timerevent_list)); + else + te = (struct npc_timerevent_list *)aRealloc( te, sizeof(struct npc_timerevent_list) * (k+1) ); + for (j = 0; j < k; j++){ + if (te[j].timer > t){ + memmove(te+j+1, te+j, sizeof(struct npc_timerevent_list)*(k-j)); + break; + } + } + te[j].timer = t; + te[j].pos = pos; + nd->u.scr.timer_event = te; + nd->u.scr.timeramount++; + } + } + nd->u.scr.timerid = -1; + + return end; +} + void npc_setcells(struct npc_data* nd) { int m = nd->bl.m, x = nd->bl.x, y = nd->bl.y, xs, ys; @@ -2760,7 +2912,7 @@ void npc_parsesrcfile(const char* filepath) } else if( (i=0, sscanf(w2,"duplicate%n",&i), (i > 0 && w2[i] == '(')) && count > 3 ) { - p = npc_parse_script(w1,w2,w3,w4, p, buffer, filepath); + p = npc_parse_duplicate(w1,w2,w3,w4, p, buffer, filepath); } else if( strcmpi(w2,"monster") == 0 && count > 3 ) { diff --git a/src/map/npc.h b/src/map/npc.h index b2fd2954f..411e7e026 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -40,6 +40,7 @@ struct npc_data { void* chatdb; // pointer to a npc_parse struct (see npc_chat.c) enum npc_subtype subtype; + int src_id; union { struct { struct script_code *script; @@ -50,7 +51,6 @@ struct npc_data { struct npc_timerevent_list *timer_event; int label_list_num; struct npc_label_list *label_list; - int src_id; } scr; struct { struct npc_item_list* shop_item; |