duplicate npc command script (#5960)

Co-authored-by: Aleos <aleos89@users.noreply.github.com>
Co-authored-by: Atemo <Atemo@users.noreply.github.com>
Co-authored-by: Lemongrass3110 <lemongrass@kstp.at>
This commit is contained in:
Sader Fawall 2022-10-01 22:58:53 +02:00 committed by GitHub
parent c7ec076a6c
commit 9c2576f47a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 257 additions and 5 deletions

View File

@ -3,7 +3,7 @@
//===== By: ==================================================
//= rAthena Dev Team
//===== Last Updated: ========================================
//= 20180831
//= 20211112
//===== Description: =========================================
//= An example of how duplicate NPCs are handled:
//= NPC variables are shared between all duplicates.
@ -18,14 +18,88 @@ prontera,150,175,4 script Duplicate Test Script 909,{
close;
OnInit:
getmapxy(.map$, .x, .y, 1);
getmapxy(.map$, .x, .y, BL_NPC);
end;
OnTouch:
getmapxy(.map$, .x, .y, 1);
getmapxy(.map$, .x, .y, BL_NPC);
emotion ET_SCISSOR;
end;
}
prontera,155,175,4 duplicate(Duplicate Test Script) Duplicate Test2 909,2,2
prontera,160,175,4 duplicate(Duplicate Test Script) Duplicate Test3 909,3,3
//duplicate command script
prontera,150,168,4 script Duplicate Command Test 909,{
mes "Would you like to create a new NPC?";
.@original_npc$ = "original_npc_unique_name";
mes "Input Map Name";
mes "recommended 'prontera'";
input .@map$;
clear;
mes "Input x";
mes "recommended '155'";
input .@x,0;
clear;
mes "Input y";
mes "recommended '168'";
input .@y,0;
clear;
switch( .@s = select( "Provide no info:With name:With name and look:With name, look and dir:cancel" ) ){
case 1:
.@new_npc$ = duplicate( .@original_npc$, .@map$, .@x, .@y );
break;
case 2:
mes "Input Duplicate NPC Name";
input .@name$;
clear;
.@new_npc$ = duplicate( .@original_npc$, .@map$, .@x, .@y, .@name$ );
break;
case 3:
mes "Input Duplicate NPC Name";
input .@name$;
clear;
mes "Input look";
mes "recommended '445'";
input .@look,0;
.@new_npc$ = duplicate( .@original_npc$, .@map$, .@x, .@y, .@name$, .@look );
break;
case 4:
mes "Input Duplicate NPC Name";
input .@name$;
clear;
mes "Input look";
mes "recommended '445'";
input .@look,0;
clear;
mes "Input dir";
mes "between " + DIR_NORTH + " and " + DIR_NORTHEAST;
input .@dir,DIR_NORTH,DIR_NORTHEAST;
.@new_npc$ = duplicate( .@original_npc$, .@map$, .@x, .@y, .@name$, .@look, .@dir );
break;
default:
mes "Ok, see you next time!";
close;
}
if( getnpcid( 0, .@new_npc$ ) == 0 ){
mes "Something went wrong!";
mes "The new NPC could not be found!";
close;
}
clear;
mes "The new NPC is now at " + .@map$ + "," + .@x + "," + .@y;
end;
}
prontera,150,165,0 script test npc::original_npc_unique_name 444,{
getmapxy(.@map$, .@x, .@y, BL_NPC);
mes "Hi.";
mes "My Unique Name is: " + strnpcinfo(3);
mes "My coords are "+ .@map$ +", "+ .@x +"/" +.@y ;
close;
}

View File

@ -6840,6 +6840,18 @@ This command will fully unload a NPC object and all of it's duplicates.
---------------------------------------
*duplicate "<NPC name>","<map>",<x>,<y>{,"<Duplicate NPC name>"{,<sprite>{,<dir>{,<xs>{,<xy>}}}}};
This command will duplicate the NPC with the given <NPC name> on <map> at <x>/<y>.
If <Duplicate NPC name>, <sprite>, <dir>, <xs> or <ys> is not provided the value of the original NPC will be used.
The Unique name of the new duplicated NPC is returned on success. An empty string is returned on failure.
NOTE:
Duplicates will always have the same NPC variables as the original NPC.
Editing a NPC variable in a duplicate or the original NPC will change it for the others.
---------------------------------------
*cloakonnpc {"<NPC object name>"{,<character ID>}};
*cloakoffnpc {"<NPC object name>"{,<character ID>}};

View File

@ -5630,6 +5630,60 @@ int npc_script_event(struct map_session_data* sd, enum npce_event type){
return vector.size();
}
/**
* Duplicates a NPC.
* nd: Original NPC data
* name: Duplicate NPC name
* m: Map ID of duplicate NPC
* x: X coordinate of duplicate NPC
* y: Y coordinate of duplicate NPC
* class_: View of duplicate NPC
* dir: Facing direction of duplicate NPC
* Returns duplicate NPC data on success
*/
npc_data* npc_duplicate_npc( npc_data* nd, char name[NPC_NAME_LENGTH + 1], int16 mapid, int16 x, int16 y, int class_, uint8 dir, int16 xs, int16 ys ){
static char w1[128], w2[128], w3[128], w4[128];
const char* stat_buf = "- call from duplicate subsystem -\n";
char exname[NPC_NAME_LENGTH + 1];
snprintf(w1, sizeof(w1), "%s,%d,%d,%d", map_getmapdata(mapid)->name, x, y, dir);
snprintf(w2, sizeof(w2), "duplicate(%s)", nd->exname);
//Making sure the generated name is not used for another npc.
int i = 0;
snprintf(exname, ARRAYLENGTH(exname), "%d_%d_%d_%d", i, mapid, x, y);
while (npc_name2id(exname) != nullptr) {
++i;
snprintf(exname, ARRAYLENGTH(exname), "%d_%d_%d_%d", i, mapid, x, y);
}
snprintf(w3, sizeof(w3), "%s::%s", name, exname);
if( xs >= 0 && ys >= 0 ){
snprintf( w4, sizeof( w4 ), "%d,%d,%d", class_, xs, ys ); // Touch Area
}else{
snprintf( w4, sizeof( w4 ), "%d", class_ );
}
npc_parse_duplicate(w1, w2, w3, w4, stat_buf, stat_buf, "DUPLICATE");//DUPLICATE means nothing for now.
npc_data* dnd = npc_name2id( exname );
// No need to try and execute any events
if( dnd == nullptr ){
return nullptr;
}
//run OnInit Events
char evname[EVENT_NAME_LENGTH];
safesnprintf(evname, EVENT_NAME_LENGTH, "%s::%s", exname, script_config.init_event_name);
if ((struct event_data*)strdb_get(ev_db, evname)) {
npc_event_do(evname);
}
return dnd;
}
const char *npc_get_script_event_name(int npce_index)
{
switch (npce_index) {

View File

@ -1494,6 +1494,7 @@ void npc_parse_mob2(struct spawn_data* mob);
struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short from_y, short xs, short ys, unsigned short to_mapindex, short to_x, short to_y);
int npc_globalmessage(const char* name,const char* mes);
const char *npc_get_script_event_name(int npce_index);
npc_data* npc_duplicate_npc( npc_data* nd, char name[NPC_NAME_LENGTH + 1], int16 mapid, int16 x, int16 y, int class_, uint8 dir, int16 xs, int16 ys );
void npc_setcells(struct npc_data* nd);
void npc_unsetcells(struct npc_data* nd);

View File

@ -8815,6 +8815,7 @@ BUILDIN_FUNC(getcharid)
/*==========================================
* returns the GID of an NPC
* Returns 0 if the NPC name provided is not found.
*------------------------------------------*/
BUILDIN_FUNC(getnpcid)
{
@ -8825,9 +8826,9 @@ BUILDIN_FUNC(getnpcid)
{// unique npc name
if( ( nd = npc_name2id(script_getstr(st,3)) ) == NULL )
{
ShowError("buildin_getnpcid: No such NPC '%s'.\n", script_getstr(st,3));
//Npc not found.
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
return SCRIPT_CMD_SUCCESS;
}
}
@ -25020,6 +25021,115 @@ BUILDIN_FUNC(unloadnpc) {
return SCRIPT_CMD_SUCCESS;
}
/**
* Duplicate a NPC.
* Return the duplicate Unique name on success or empty string on failure.
* duplicate "<NPC name>","<map>",<x>,<y>{,"<Duplicate NPC name>"{,<sprite>{,<dir>{,<xs>{,<xy>}}}}};
*/
BUILDIN_FUNC(duplicate)
{
const char* old_npcname = script_getstr( st, 2 );
npc_data* nd = npc_name2id( old_npcname );
if( nd == nullptr ){
ShowError( "buildin_duplicate: No such NPC '%s'.\n", old_npcname );
script_pushstrcopy( st, "" );
return SCRIPT_CMD_FAILURE;
}
const char* mapname = script_getstr( st, 3 );
int16 mapid = map_mapname2mapid( mapname );
if( mapid < 0 ){
ShowError( "buildin_duplicate: map '%s' in not found!\n", mapname );
script_pushstrcopy( st, "" );
return SCRIPT_CMD_FAILURE;
}
struct map_data* mapdata = map_getmapdata( mapid );
if( mapdata == nullptr ){
// Should not happen, but who knows...
ShowError( "buildin_duplicate: mapdata for '%s' is unavailable!\n", mapname );
script_pushstrcopy( st, "" );
return SCRIPT_CMD_FAILURE;
}
int16 x = script_getnum( st, 4 );
if( x < 0 || x >= mapdata->xs ){
ShowError( "buildin_duplicate: x coordinate %hd is out of bounds for map %s[0-%hd]!\n", x, mapname, mapdata->xs );
script_pushstrcopy( st, "" );
return SCRIPT_CMD_FAILURE;
}
int16 y = script_getnum( st, 5 );
if( y < 0 || y >= mapdata->ys ){
ShowError( "buildin_duplicate: y coordinate %hd is out of bounds for map %s[0-%hd]!\n", y, mapname, mapdata->ys );
script_pushstrcopy( st, "" );
return SCRIPT_CMD_FAILURE;
}
char name[NPC_NAME_LENGTH + 1];
if( script_hasdata( st, 6 ) ){
const char* new_name = script_getstr( st, 6 );
if( strlen( new_name ) > NPC_NAME_LENGTH ){
ShowError( "buildin_duplicate: new NPC name \"%s\" is too long!\n", new_name );
script_pushstrcopy( st, "" );
return SCRIPT_CMD_FAILURE;
}
safestrncpy( name, new_name, sizeof( name ) );
}else{
safestrncpy( name, nd->name, sizeof( name ) );
}
int class_;
if( script_hasdata( st, 7 ) ){
class_ = script_getnum( st, 7 );
}else{
class_ = nd->class_;
}
uint8 dir;
if( script_hasdata( st, 8 ) ){
dir = script_getnum( st, 8 );
}else{
dir = nd->ud.dir;
}
int16 xs;
if( script_hasdata( st, 9 ) ){
xs = script_getnum( st, 9 );
}else{
xs = nd->u.scr.xs;
}
int16 ys;
if( script_hasdata( st, 10 ) ){
ys = script_getnum( st, 10 );
}else{
ys = nd->u.scr.ys;
}
npc_data* dnd = npc_duplicate_npc( nd, name, mapid, x, y, class_, dir, xs, ys );
if( dnd == nullptr ){
script_pushstrcopy( st, "" );
return SCRIPT_CMD_FAILURE;
}else{
script_pushstrcopy( st, dnd->exname );
return SCRIPT_CMD_SUCCESS;
}
}
/**
* Add an achievement to the player's log
* achievementadd(<achievement ID>{,<char ID>});
@ -27173,6 +27283,7 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(jobcanentermap,"s?"),
BUILDIN_DEF(openstorage2,"ii?"),
BUILDIN_DEF(unloadnpc, "s"),
BUILDIN_DEF(duplicate, "ssii?????"),
// WoE TE
BUILDIN_DEF(agitstart3,""),