diff options
-rw-r--r-- | Changelog-Trunk.txt | 5 | ||||
-rw-r--r-- | doc/script_commands.txt | 47 | ||||
-rw-r--r-- | src/map/map.h | 4 | ||||
-rw-r--r-- | src/map/npc.c | 156 |
4 files changed, 124 insertions, 88 deletions
diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt index af3d4af01..92b8e95d0 100644 --- a/Changelog-Trunk.txt +++ b/Changelog-Trunk.txt @@ -4,6 +4,11 @@ AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK. 2007/10/18 + * Clarified how npc names work in script_commands.txt. + * Fixed a forgotten "return 0;" that stopped the parsing of the file + after the first "script" of the file is parsed sucessfully (caused by r11502). + * Applied the same parsing and restrictions of npc names to warps, duplicates + and shops (other code expects unique names for them too). [FlavioJS] * Applied the new checking function to clif code, this fixes various length mismatches caused by incomplete code in r11386 * Added clif_process_message(), an unified way to validate all four diff --git a/doc/script_commands.txt b/doc/script_commands.txt index c19ad9716..bf9343cfe 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -9,7 +9,7 @@ //= Maeki Rika - A section on general concepts and lots of //= other updates and additions. //===== Version =========================================== -//= 3.06.20070910 +//= 3.08.20071018 //========================================================= //= 1.0 - First release, filled will as much info as I could //= remember or figure out, most likely there are errors, @@ -86,6 +86,8 @@ //= Added info about the new behavior of 'getexp' [ultramage] //= 3.07.20070915 //= Fixed 'duplicate' missing the target 'name' parameter! [ultramage] +//= 3.08.20071018 +//= Clarified how npc names work. [FlavioJS] //===== Description ======================================= //= A reference manual for the eAthena scripting language, //= sorted out depending on their functionality. @@ -261,6 +263,25 @@ special ai mobs (which by default attack other monsters instead of players). You can add these, so using 10 will spawn small monsters that attack other mobs (if you specify both 2 and 4, the small version takes priority). +** Npc names + +/!\ WARNING: this applies to warps, npcs, duplicates and shops /!\ + +Npc names are kinda special and are formatted this way: + +<Display name>{::<Unique name>} + +All npcs need to have a unique name that is used for identification purposes. +When you have to identify a npc by it's name, you should use <Unique name>. +If <Unique name> is not provided, use <Display name> instead. + +The client has a special feature when displaying names: +if the display name contains a '#' character, it hides that part of the name. +ex: if your npc is named 'Hunter#hunter1', it will be displayed as 'Hunter' + +<Display name> must be at most 24 characters in length. +<Unique name> must be at most 24 characters in length. + ** Define a warp point <from map name>,<fromX>,<fromY>,<facing>%TAB%warp%TAB%<warp name>%TAB%<spanx>,<spany>,<to map name>,<toX>,<toY> @@ -300,8 +321,9 @@ Sprite id is the sprite number used to display this particular NPC. For a full list of sprite id numbers see http://kalen.s79.xrea.com/npc/npce.shtml You may also use a monster's ID number instead to display a monster sprite for this NPC. It is possible to use a job sprite as well, but you must first define it as a -monster sprite in 'mob_avail.txt', a full description on how to do this is for -another manual. A '-1' sprite id will make the NPC invisible (and unclickable). +monster sprite in 'mob_avail.txt', a full description on how to do this is not +in the scope of this manual. +A '-1' sprite id will make the NPC invisible (and unclickable). A '111' sprite id will make an NPC which does not have a sprite, but is still clickable, which is useful if you want to make a clickable object of the 3D terrain. @@ -313,25 +335,6 @@ direction across Y. Walking into that area will trigger the NPC. If no from the beginning of the script, otherwise, it will start from the 'OnTouch:' label. -NPC name is kinda special, because it's not only the name of NPC you will see on -screen. It's formatted this way: - -<Screen name>{#<Extra name identifier>}{::<Label name>} - -The extra identifier is there that you can make an npc with an invisible name -(just omit the screen name, but keep the identifier name) and so that you can -refer to several NPCs which have the same name on screen, which is useful to -make an NPC that relocates depending on special conditions, for example - you -define several NPC objects and hide all except one. -('Hunter#hunter1','Hunter#hunter2'...) The extra name identifiers will let your -code tell them apart. - -Label name is used to duplicate NPC objects (more on that below). - -The complete NPC name (Screen name + extra identifier) may not exceed 24 -characters. The label name is counted separately but also limited to 24 -characters. - The code part is the script code that will execute whenever the NPC is 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 diff --git a/src/map/map.h b/src/map/map.h index 4a0223610..5cef74b07 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -830,8 +830,8 @@ struct npc_data { short n; short class_; short speed; - char name[NAME_LENGTH]; - char exname[NAME_LENGTH]; + char name[NAME_LENGTH+1];// display name + char exname[NAME_LENGTH+1];// unique npc name int chat_id; unsigned int next_walktime; diff --git a/src/map/npc.c b/src/map/npc.c index 7e442e615..9b86ff367 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -94,7 +94,7 @@ int npc_enable_sub(struct block_list *bl, va_list ap) nullpo_retr(0, nd=va_arg(ap,struct npc_data *)); if(bl->type == BL_PC && (sd=(struct map_session_data *)bl)) { - char name[50]; // need 24 + 9 for the "::OnTouch" + char name[NAME_LENGTH*2+3]; if (nd->sc.option&OPTION_INVISIBLE) // 無効化されている return 1; @@ -103,7 +103,7 @@ int npc_enable_sub(struct block_list *bl, va_list ap) return 1; sd->areanpc_id=nd->bl.id; - snprintf(name, 50, "%s::OnTouch", nd->exname); // exname to be specific. exname is the unique identifier for script events. [Lance] + snprintf(name, ARRAYLENGTH(name), "%s::OnTouch", nd->exname); // exname to be specific. exname is the unique identifier for script events. [Lance] npc_event(sd,name,0); } //aFree(name); @@ -194,7 +194,7 @@ int npc_event_export(char* lname, void* data, va_list ap) if ((lname[0]=='O' || lname[0]=='o')&&(lname[1]=='N' || lname[1]=='n')) { struct event_data *ev; - char buf[51]; + char buf[NAME_LENGTH*2+3]; char* p = strchr(lname, ':'); // エクスポートされる ev = (struct event_data *) aMalloc(sizeof(struct event_data)); @@ -208,7 +208,7 @@ int npc_event_export(char* lname, void* data, va_list ap) ev->nd = nd; ev->pos = pos; *p = '\0'; - sprintf(buf, "%s::%s", nd->exname, lname); + snprintf(buf, ARRAYLENGTH(buf), "%s::%s", nd->exname, lname); *p = ':'; strdb_put(ev_db, buf, ev); } @@ -561,9 +561,9 @@ void npc_timerevent_quit(struct map_session_data* sd) sd->npc_timer_id = -1; if (nd && nd->bl.type == BL_NPC) { //Execute OnTimerQuit - char buf[sizeof(nd->exname)+sizeof("::OnTimerQuit")+1]; + char buf[NAME_LENGTH*2+3]; struct event_data *ev; - sprintf(buf,"%s::OnTimerQuit",nd->exname); + snprintf(buf, ARRAYLENGTH(buf), "%s::OnTimerQuit", nd->exname); ev = strdb_get(ev_db, buf); if(ev && ev->nd != nd) { ShowWarning("npc_timerevent_quit: Unable to execute \"OnTimerQuit\", two NPCs have the same event name [%s]!\n",buf); @@ -765,14 +765,13 @@ int npc_touch_areanpc(struct map_session_data* sd, int m, int x, int y) break; case SCRIPT: { - //char *name=(char *)aCallocA(50,sizeof(char)); // fixed [Shinomori] - char name[50]; // need 24 max + 9 for "::OnTouch" + char name[NAME_LENGTH*2+3]; if(sd->areanpc_id == map[m].npc[i]->bl.id) return 1; sd->areanpc_id = map[m].npc[i]->bl.id; - sprintf(name,"%s::OnTouch", map[m].npc[i]->exname); // It goes here too. exname being the unique identifier. [Lance] + snprintf(name, ARRAYLENGTH(name), "%s::OnTouch", map[m].npc[i]->exname); // It goes here too. exname being the unique identifier. [Lance] if( npc_event(sd,name,0)>0 ) { pc_stop_walking(sd,1); //Make it stop walking! @@ -1032,11 +1031,11 @@ int npc_buysellsel(struct map_session_data* sd, int id, int type) //npc_buylist for script-controlled shops. static int npc_buylist_sub(struct map_session_data* sd, int n, unsigned short* item_list, struct npc_data* nd) { - char npc_ev[51]; + char npc_ev[NAME_LENGTH*2+3]; int i; int regkey = add_str("@bought_nameid"); int regkey2 = add_str("@bought_quantity"); - sprintf(npc_ev, "%s::OnBuyItem", nd->exname); + snprintf(npc_ev, ARRAYLENGTH(npc_ev), "%s::OnBuyItem", nd->exname); for(i=0;i<n;i++){ pc_setreg(sd,regkey+(i<<24),(int)item_list[i*2+1]); pc_setreg(sd,regkey2+(i<<24),(int)item_list[i*2]); @@ -1214,8 +1213,8 @@ int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) } if(nd) { - char npc_ev[51]; - sprintf(npc_ev, "%s::OnSellItem", nd->exname); + char npc_ev[NAME_LENGTH*2+3]; + snprintf(npc_ev, ARRAYLENGTH(npc_ev), "%s::OnSellItem", nd->exname); npc_event(sd, npc_ev, 0); } @@ -1302,7 +1301,7 @@ int npc_unload(struct npc_data* nd) npc_remove_map(nd); map_deliddb(&nd->bl); - strdb_remove(npcname_db, (nd->bl.subtype < SCRIPT) ? nd->name : nd->exname); + strdb_remove(npcname_db, nd->exname); if (nd->chat_id) // remove npc chatroom object and kick users chat_deletenpcchat(nd); @@ -1418,6 +1417,72 @@ void npc_delsrcfile(const char* name) } } +/// Parses and sets the name and exname of a npc. +/// Assumes that m, x and y are already set in nd. +static void npc_parsename(struct npc_data* nd, const char* name, const char* start, const char* buffer, const char* filepath) +{ + const char* p; + struct npc_data* dnd; + char newname[NAME_LENGTH]; + + // parse name + p = strstr(name,"::"); + if( p ) + {// <Display name>::<Unique name> + size_t len = p-name; + if( len >= NAME_LENGTH ) + { + ShowWarning("npc_parsename: Display name of '%s' is too long (len=%u) in file '%s', line'%d'. Truncating to %u characters.\n", name, (unsigned int)len, filepath, strline(buffer,start-buffer), NAME_LENGTH); + safestrncpy(nd->name, name, sizeof(nd->name)); + } + else + { + memcpy(nd->name, name, len); + memset(nd->name+len, 0, sizeof(nd->name)-len); + } + len = strlen(p+2); + if( len >= NAME_LENGTH ) + ShowWarning("npc_parsename: Unique name of '%s' is too long (len=%u) in file '%s', line'%d'. Truncating to %u characters.\n", name, (unsigned int)len, filepath, strline(buffer,start-buffer), NAME_LENGTH); + safestrncpy(nd->exname, p+2, sizeof(nd->exname)); + } + else + {// <Display name> + size_t len = strlen(name); + if( len >= NAME_LENGTH ) + ShowWarning("npc_parsename: Name '%s' is too long (len=%u) in file '%s', line'%d'. Truncating to %u characters.\n", name, (unsigned int)len, filepath, strline(buffer,start-buffer), NAME_LENGTH); + safestrncpy(nd->name, name, sizeof(nd->name)); + safestrncpy(nd->exname, name, sizeof(nd->exname)); + } + + if( *nd->exname == '\0' || strstr(nd->exname,"::") != NULL ) + {// invalid + snprintf(newname, ARRAYLENGTH(newname), "0_%d_%d_%d", nd->bl.m, nd->bl.x, nd->bl.y); + ShowWarning("npc_parsename: Invalid unique name in file '%s', line'%d'. Renaming '%s' to '%s'.\n", filepath, strline(buffer,start-buffer), nd->exname, newname); + safestrncpy(nd->exname, newname, sizeof(nd->exname)); + } + + if( *nd->exname == '\0' || (dnd=npc_name2id(nd->exname)) != NULL ) + {// duplicate unique name, generate new one + char this_mapname[32]; + char other_mapname[32]; + int i = 0; + do + { + ++i; + snprintf(newname, ARRAYLENGTH(newname), "%d_%d_%d_%d", i, nd->bl.m, nd->bl.x, nd->bl.y); + } + while( npc_name2id(newname) != NULL ); + + strcpy(this_mapname, (nd->bl.m==-1?"(not on a map)":mapindex_id2name(nd->bl.m))); + strcpy(other_mapname, (dnd->bl.m==-1?"(not on a map)":mapindex_id2name(dnd->bl.m))); + + ShowWarning("npc_parsename: Duplicate unique name in file '%s', line'%d'. Renaming '%s' to '%s'.\n", filepath, strline(buffer,start-buffer), nd->exname, newname); + ShowDebug("this npc:\n display name '%s'\n unique name '%s'\n map=%s, x=%d, y=%d\n", nd->name, nd->exname, this_mapname, nd->bl.x, nd->bl.y); + ShowDebug("other npc:\n display name '%s'\n unique name '%s'\n map=%s, x=%d, y=%d\n", dnd->name, dnd->exname, other_mapname, dnd->bl.x, dnd->bl.y); + safestrncpy(nd->exname, newname, sizeof(nd->exname)); + } +} + /// Parses a warp npc. const char* npc_parse_warp(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath) { @@ -1451,8 +1516,7 @@ const char* npc_parse_warp(char* w1, char* w2, char* w3, char* w4, const char* s nd->bl.m = m; nd->bl.x = x; nd->bl.y = y; - safestrncpy(nd->name, w3, sizeof(nd->name)); - safestrncpy(nd->exname, w3, sizeof(nd->exname)); + npc_parsename(nd, w3, start, buffer, filepath); if (!battle_config.warp_point_debug) nd->class_ = WARP_CLASS; @@ -1476,7 +1540,7 @@ const char* npc_parse_warp(char* w1, char* w2, char* w3, char* w4, const char* s status_change_init(&nd->bl); unit_dataset(&nd->bl); clif_spawn(&nd->bl); - strdb_put(npcname_db, nd->name, nd); + strdb_put(npcname_db, nd->exname, nd); return strchr(start,'\n');// continue } @@ -1546,7 +1610,7 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const nd->bl.x = x; nd->bl.y = y; nd->bl.id = npc_get_new_npc_id(); - safestrncpy(nd->name, w3, sizeof(nd->name)); + npc_parsename(nd, w3, start, buffer, filepath); nd->class_ = m==-1?-1:atoi(w4); nd->speed = 200; @@ -1566,7 +1630,7 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const {// 'floating' shop? map_addiddb(&nd->bl); } - strdb_put(npcname_db, nd->name, nd); + strdb_put(npcname_db, nd->exname, nd); return strchr(start,'\n');// continue } @@ -1680,7 +1744,7 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons char mapname[32]; struct script_code *script; int i; - char *p; + const char* end; struct npc_label_list* label_list; int label_list_num; @@ -1697,9 +1761,9 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons else {// npc in a map if( sscanf(w1, "%31[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 - || (strcmp(w2, "script") == 0 && strchr(w4,',') == NULL) ) + || (strcasecmp(w2, "script") == 0 && strchr(w4,',') == NULL) ) { - ShowError("npc_parse_script: unkown format for a script in file '%s', line '%d'. Skipping the rest of file...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4); + ShowError("npc_parse_script: Unkown format for a script in file '%s', line '%d'. Skipping the rest of 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;// unknown format, don't continue } m = map_mapname2mapid(mapname); @@ -1708,7 +1772,6 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons if( strcmp(w2, "script") == 0 ) {// parsing script with curly const char* real_start; - const char* end; end = npc_skip_script(start, buffer, filepath); if( end == NULL ) @@ -1729,6 +1792,8 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons else {// duplicate npc char srcname[128]; + + 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); @@ -1761,39 +1826,11 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons nd->u.scr.ys = 0; } - // extended name - p = strstr(w3,"::"); - if( p ) - { - *p = '\0'; - safestrncpy(nd->name, w3, sizeof(nd->name)); - safestrncpy(nd->exname, p+2, sizeof(nd->exname)); - } - else - { - safestrncpy(nd->name, w3, sizeof(nd->name)); - safestrncpy(nd->exname, w3, sizeof(nd->exname)); - } - - if( npc_name2id(nd->exname) ) - {// duplicate name, generate new name - char newexname[32]; - int i = 0; - do - { - ++i; - sprintf(newexname, "%31d", i); - dnd = npc_name2id(newexname); - } - while( dnd ); - ShowWarning("npc_parse_script: Overriding NPC '%s::%s' to '%s::%d'.. in file '%s', line '%d' (Duplicated System Name - Lazy scripters >_>) \n", nd->name, nd->exname, nd->name, i, filepath, strline(buffer,start-buffer)); - safestrncpy(nd->exname, newexname, sizeof(nd->exname)); - } - 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; @@ -1814,15 +1851,6 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons nd->ud.dir = dir; npc_setcells(nd); map_addblock(&nd->bl); - // Unused. You can always use xxx::OnXXXX events. Have this removed to improve perfomance. - /*if (evflag) { // イベント型 - struct event_data *ev = (struct event_data *)aCalloc(1, sizeof(struct event_data)); - ev->nd = nd; - ev->pos = 0; - strdb_put(ev_db, nd->exname, ev); - } else { - clif_spawn(&nd->bl); - }*/ if( class_ >= 0 ) { status_set_viewdata(&nd->bl, nd->class_); @@ -1846,8 +1874,8 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons if ((lname[0] == 'O' || lname[0] == 'o') && (lname[1] == 'N' || lname[1] == 'n')) { struct event_data* ev; - char buf[50+1]; // 24 for npc name + 24 for label + 2 for a "::" and 1 for EOS - snprintf(buf, sizeof(buf), "%s::%s", nd->exname, lname); + 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); @@ -1886,7 +1914,7 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons } nd->u.scr.timerid = -1; - return 0; + return end; } void npc_setcells(struct npc_data* nd) |