Skip to content

Commit 9401b3d

Browse files
committed
Add *duplicate/remove npc script command
- `npc_duplicate()` duplicate any existing duplicated NPC. - `npc_duplicate_remove()` will remove any existing NPC other than source NPC.
1 parent 944d848 commit 9401b3d

File tree

2 files changed

+167
-0
lines changed

2 files changed

+167
-0
lines changed

doc/script_commands.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7123,6 +7123,22 @@ otherwise it's displayed to the entire npc area.
71237123

71247124
---------------------------------------
71257125

7126+
*npc_duplicate("<source_npc_name>", "<new_npc_name>", "<new_npc_hidden_name>", "<mapname>", <map_x>, <map_y>, <dir>{, <sprite_id>{, <map_xs>, <map_ys>}});
7127+
7128+
Duplicate any existing NPC based on <source_npc_name>.
7129+
Return 1 on success, 0 if failed.
7130+
7131+
---------------------------------------
7132+
7133+
*npc_duplicate_remove({"<npc_name>", {<flag>}});
7134+
7135+
Remove any duplicated NPC from source NPC or depend on <npc_name> if specified.
7136+
If <npc_name> are source NPC, it will only remove all duplicated NPC.
7137+
If <flag> are specified, it will unload monster too. (default: 1)
7138+
Return 1 on success, 0 if failed.
7139+
7140+
---------------------------------------
7141+
71267142
*doevent("<NPC object name>::<event label>")
71277143

71287144
This command will start a new execution thread in a specified NPC object

src/map/script.c

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12572,6 +12572,155 @@ static BUILDIN(cloakoffnpc)
1257212572
return true;
1257312573
}
1257412574

12575+
12576+
/*
12577+
* Create a duplicate of source NPC.
12578+
* npc_duplicate("<source_npc_name>", "<new_npc_name>", "<new_npc_hidden_name>", "<mapname>", <map_x>, <map_y>, <dir>{, <sprite_id>{, <map_xs>, <map_ys>}});
12579+
* Return 1 on success, 0 if failed.
12580+
*/
12581+
static BUILDIN(npc_duplicate)
12582+
{
12583+
int txs = -1, tys = -1;
12584+
12585+
if (script_hasdata(st, 10))
12586+
txs = script_getnum(st, 10);
12587+
if (script_hasdata(st, 11))
12588+
tys = script_getnum(st, 11);
12589+
12590+
if (txs < -1)
12591+
txs = -1;
12592+
if (tys < -1)
12593+
tys = -1;
12594+
12595+
if (txs == -1 && tys != -1)
12596+
txs = 0;
12597+
if (txs != -1 && tys == -1)
12598+
tys = 0;
12599+
12600+
const char *dup_name = script_getstr(st, 3);
12601+
const char *dup_hidden_name = script_getstr(st, 4);
12602+
12603+
char targetname[NAME_LENGTH] = "";
12604+
strcat(targetname, dup_name);
12605+
12606+
if (strlen(dup_hidden_name) != 0) {
12607+
strncat(targetname, "#", 1);
12608+
strncat(targetname, dup_hidden_name, strlen(dup_hidden_name));
12609+
}
12610+
12611+
if (strlen(targetname) > NAME_LENGTH) {
12612+
ShowError("buildin_npc_duplicate: NPC name '%s' is to long (max %d chars).\n", targetname, NAME_LENGTH);
12613+
script_pushint(st, 0);
12614+
return false;
12615+
} else if (npc->name2id(targetname) != NULL) {
12616+
ShowError("buildin_npc_duplicate: NPC named '%s' already exists.\n", targetname);
12617+
script_pushint(st, 0);
12618+
return false;
12619+
}
12620+
12621+
const char *npc_name = script_getstr(st, 2);
12622+
struct npc_data *nd_source = npc->name2id(npc_name);
12623+
int tclass_ = nd_source->class_;
12624+
12625+
if (script_hasdata(st, 9))
12626+
tclass_ = script_getnum(st, 9);
12627+
if (tclass_ < -1)
12628+
tclass_ = FAKE_NPC;
12629+
12630+
if (nd_source == NULL) {
12631+
ShowError("buildin_npc_duplicate: Source NPC '%s' not found.\n", npc_name);
12632+
script_pushint(st, 0);
12633+
return false;
12634+
}
12635+
12636+
if (nd_source->src_id != 0) {
12637+
ShowError("buildin_npc_duplicate: Source NPC '%s' is a duplicated NPC.\n", nd_source->name);
12638+
script_pushint(st, 0);
12639+
return false;
12640+
}
12641+
12642+
const char *tmap = script_getstr(st, 5);
12643+
int tmapid = map->mapname2mapid(tmap);
12644+
if (tmapid < 0) {
12645+
ShowError("buildin_npc_duplicate: Target map '%s' not found.\n", tmap);
12646+
script_pushint(st, 0);
12647+
return false;
12648+
}
12649+
12650+
if (map->list[tmapid].npc_num >= MAX_NPC_PER_MAP) {
12651+
ShowError("buildin_npc_duplicate: Exceeded MAX NPC per map (%d).\n", MAX_NPC_PER_MAP);
12652+
return false;
12653+
}
12654+
12655+
int tx = script_getnum(st, 6);
12656+
int ty = script_getnum(st, 7);
12657+
int tdir = script_getnum(st, 8);
12658+
12659+
if (tclass_ != FAKE_NPC && tclass_ != HIDDEN_WARP_CLASS) {
12660+
if (map->getcell(tmapid, NULL, tx, ty, CELL_CHKNOPASS) > 0) {
12661+
ShowError("buildin_npc_duplicate: Invalid NPC Location. %s,%d,%d\n", tmap, tx, ty);
12662+
script_pushint(st, 0);
12663+
return false;
12664+
}
12665+
}
12666+
12667+
if (tdir >= UNIT_DIR_MAX) {
12668+
ShowWarning("buildin_npc_duplicate: Invalid NPC direction %d. Default to %d.\n", tdir, (tdir % UNIT_DIR_MAX));
12669+
tdir %= UNIT_DIR_MAX; // trim spin-over
12670+
}
12671+
else if (tdir <= UNIT_DIR_UNDEFINED) {
12672+
ShowWarning("buildin_npc_duplicate: Invalid NPC direction %d. Default to %d.\n", tdir, UNIT_DIR_SOUTH);
12673+
tdir = UNIT_DIR_SOUTH;
12674+
}
12675+
12676+
struct npc_data *nd_target = npc->create_npc(nd_source->subtype, tmapid, tx, ty, tdir, tclass_);
12677+
12678+
safestrncpy(nd_target->name, targetname, sizeof(nd_target->name));
12679+
safestrncpy(nd_target->exname, targetname, sizeof(nd_target->exname));
12680+
12681+
if (npc->duplicate_sub(nd_target, nd_source, txs, tys, NPO_ONINIT) == true)
12682+
script_pushint(st, 1);
12683+
else
12684+
script_pushint(st, 0);
12685+
12686+
return true;
12687+
}
12688+
12689+
/*
12690+
* npc_duplicate_remove({"<npc_name>", {<flag>}});
12691+
* Return 1 on success, 0 if failed.
12692+
*/
12693+
static BUILDIN(npc_duplicate_remove)
12694+
{
12695+
struct npc_data *nd = map->id2nd(st->oid);
12696+
12697+
if (script_hasdata(st, 2))
12698+
nd = npc->name2id(script_getstr(st, 2));
12699+
12700+
if (nd == NULL) {
12701+
if (script_hasdata(st, 2)) {
12702+
ShowError("buildin_npc_duplicate_remove: NPC '%s' not found.\n", script_getstr(st, 2));
12703+
}
12704+
else {
12705+
ShowError("buildin_npc_duplicate_remove: NPC not found.\n");
12706+
}
12707+
script_pushint(st, 0);
12708+
return false;
12709+
}
12710+
12711+
int flag = 1;
12712+
if (script_hasdata(st, 3))
12713+
flag = script_getnum(st, 3);
12714+
12715+
if (nd->src_id == 0) // remove all dupicates for this source npc
12716+
npc->unload_duplicates(nd, (flag != 0));
12717+
else // just remove this duplicate NPC
12718+
npc->unload(nd, true, (flag != 0));
12719+
12720+
script_pushint(st, 1);
12721+
return true;
12722+
}
12723+
1257512724
/* Starts a status effect on the target unit or on the attached player.
1257612725
*
1257712726
* sc_start <effect_id>,<duration>,<val1>{,<rate>,<flag>,{<unit_id>}};
@@ -26938,6 +27087,8 @@ static void script_parse_builtin(void)
2693827087
BUILDIN_DEF(hideonnpc,"s"),
2693927088
BUILDIN_DEF(cloakonnpc,"s?"),
2694027089
BUILDIN_DEF(cloakoffnpc,"s?"),
27090+
BUILDIN_DEF(npc_duplicate, "ssssiii???"),
27091+
BUILDIN_DEF(npc_duplicate_remove, "??"),
2694127092
BUILDIN_DEF(sc_start,"iii???"),
2694227093
BUILDIN_DEF2(sc_start,"sc_start2","iiii???"),
2694327094
BUILDIN_DEF2(sc_start,"sc_start4","iiiiii???"),

0 commit comments

Comments
 (0)