* 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 git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@13407 54d463be-8e91-2dee-dedb-b68131a5f0ec
This commit is contained in:
parent
99fd15915e
commit
d55642d64c
@ -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.
|
||||
|
@ -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>}
|
||||
|
262
src/map/npc.c
262
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 )
|
||||
{
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user