Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated npcspeed, npcwalkto and npcstop script commands #8354

Merged
merged 11 commits into from
Jun 16, 2024
27 changes: 16 additions & 11 deletions doc/script_commands.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7962,28 +7962,33 @@ Return values:
//
---------------------------------------

*npcspeed <speed value>;
*npcwalkto <x>,<y>;
*npcstop;
*npcspeed( <speed value> {,"<npc name>"} );
*npcwalkto( <x>,<y> {,"<npc name>"} } );
*npcstop( {"<npc name>", {"<flag>"}});

These commands will make the NPC object in question move around the map. As they
currently are, they are a bit buggy and are not useful for much more than making
an NPC move randomly around the map.
These commands will make the NPC object in question move around the map.

'npcspeed' will set the NPCs walking speed to a specified value. As in the
@speed GM command, 200 is the slowest possible speed while 0 is the fastest
possible (instant motion). 100 is the default character walking speed.
'npcspeed' will permanently set the NPCs walking speed to a specified value. As in the
@speed GM command, MAX_WALK_SPEED (1000) is the slowest possible speed while MIN_WALK_SPEED (20) is the fastest
possible (instant motion). DEFAULT_NPC_WALK_SPEED (200) is the default NPC walking speed.

'npcwalkto' will start the NPC sprite moving towards the specified coordinates
on the same map it is currently on. The script proceeds immediately after the
NPC begins moving.

'npcstop' will stop the motion.

The <flag> value in npcstop affects how the unit is stopped. The following flags are bitwise values (can be combined using the pipe operator):
USW_NONE = Unit will keep walking to their original destination.
USW_FIXPOS = Issue a fixpos packet afterwards.
USW_MOVE_ONCE = Force the unit to move one cell if it hasn't yet.
USW_MOVE_FULL_CELL = Enable moving to the next cell when unit was already half-way there (may cause on-touch/place side-effects, such as a scripted map change).
USW_FORCE_STOP = Force stop moving.
Default: USW_FIXPOS | USW_MOVE_FULL_CELL | USW_FORCE_STOP

While in transit, the NPC will be clickable, but invoking it will cause it to
stop moving, which will make its coordinates different from what the client
computed based on the speed and motion coordinates. The effect is rather
unnerving.
computed based on the speed and motion coordinates.

Only a few NPC sprites have walking animations, and those that do, do not get
the animation invoked when moving the NPC, due to the problem in the NPC walking
Expand Down
3 changes: 2 additions & 1 deletion src/common/mmo.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ typedef uint32 t_itemid;
#define MAX_FAME 1000000000 ///Max fame points
#define MAX_CART 100 ///Maximum item in cart
#define MAX_SKILL 1623 ///Maximum skill can be hold by Player, Homunculus, & Mercenary (skill list) AND skill_db limit
#define DEFAULT_WALK_SPEED 150 ///Default walk speed
#define DEFAULT_WALK_SPEED 150 ///Default walk speed (other than NPC)
#define DEFAULT_NPC_WALK_SPEED 200 ///Default NPC walk speed
#define MIN_WALK_SPEED 20 ///Min walk speed
#define MAX_WALK_SPEED 1000 ///Max walk speed
#define MAX_STORAGE 600 ///Max number of storage slots a player can have
Expand Down
2 changes: 1 addition & 1 deletion src/map/mob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ void mvptomb_create(struct mob_data *md, char *killer, time_t time)
safestrncpy(nd->name, msg_txt(nullptr,656), sizeof(nd->name));

nd->class_ = 565;
nd->speed = 200;
nd->speed = DEFAULT_NPC_WALK_SPEED;
nd->subtype = NPCTYPE_TOMB;

nd->u.tomb.md = md;
Expand Down
16 changes: 8 additions & 8 deletions src/map/npc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@ void BarterDatabase::loadingFinished(){
npc_parsename( nd, barter->name.c_str(), nullptr, nullptr, __FILE__ ":" QUOTE(__LINE__) );

nd->class_ = barter->sprite;
nd->speed = 200;
nd->speed = DEFAULT_NPC_WALK_SPEED;

nd->bl.type = BL_NPC;
nd->subtype = NPCTYPE_BARTER;
Expand Down Expand Up @@ -3820,7 +3820,7 @@ struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short
nd->class_ = JT_GUILD_FLAG;
else
nd->class_ = JT_WARPNPC;
nd->speed = 200;
nd->speed = DEFAULT_NPC_WALK_SPEED;

nd->u.warp.mapindex = to_mapindex;
nd->u.warp.x = to_x;
Expand Down Expand Up @@ -3892,7 +3892,7 @@ static const char* npc_parse_warp(char* w1, char* w2, char* w3, char* w4, const
nd->class_ = JT_WARPNPC;
else
nd->class_ = JT_GUILD_FLAG;
nd->speed = 200;
nd->speed = DEFAULT_NPC_WALK_SPEED;

nd->u.warp.mapindex = i;
nd->u.warp.x = to_x;
Expand Down Expand Up @@ -4176,7 +4176,7 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const

npc_parsename(nd, w3, start, buffer, filepath);
nd->class_ = m == -1 ? JT_FAKENPC : npc_parseview(w4, start, buffer, filepath);
nd->speed = 200;
nd->speed = DEFAULT_NPC_WALK_SPEED;

++npc_shop;
nd->bl.type = BL_NPC;
Expand Down Expand Up @@ -4414,7 +4414,7 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons

npc_parsename(nd, w3, start, buffer, filepath);
nd->class_ = m == -1 ? JT_FAKENPC : npc_parseview(w4, start, buffer, filepath);
nd->speed = 200;
nd->speed = DEFAULT_NPC_WALK_SPEED;
nd->u.scr.script = script;
nd->u.scr.label_list = label_list;
nd->u.scr.label_list_num = label_list_num;
Expand Down Expand Up @@ -4553,7 +4553,7 @@ const char* npc_parse_duplicate( char* w1, char* w2, char* w3, char* w4, const c
nd = npc_create_npc(m, x, y);
npc_parsename(nd, w3, start, buffer, filepath);
nd->class_ = m == -1 ? JT_FAKENPC : npc_parseview(w4, start, buffer, filepath);
nd->speed = 200;
nd->speed = DEFAULT_NPC_WALK_SPEED;
nd->src_id = src_id;
nd->bl.type = BL_NPC;
nd->subtype = (enum npc_subtype)type;
Expand Down Expand Up @@ -4686,7 +4686,7 @@ int npc_duplicate4instance(struct npc_data *snd, int16 m) {
safestrncpy(wnd->name, "", ARRAYLENGTH(wnd->name));
safestrncpy(wnd->exname, newname, ARRAYLENGTH(wnd->exname));
wnd->class_ = JT_WARPNPC;
wnd->speed = 200;
wnd->speed = DEFAULT_NPC_WALK_SPEED;
wnd->u.warp.mapindex = map_id2index(imap);
wnd->u.warp.x = snd->u.warp.x;
wnd->u.warp.y = snd->u.warp.y;
Expand Down Expand Up @@ -6236,7 +6236,7 @@ void do_init_npc(void){
// Init dummy NPC
fake_nd = npc_create_npc( -1, 0, 0 );
fake_nd->class_ = JT_FAKENPC;
fake_nd->speed = 200;
fake_nd->speed = DEFAULT_NPC_WALK_SPEED;
strcpy(fake_nd->name,"FAKE_NPC");
memcpy(fake_nd->exname, fake_nd->name, 9);

Expand Down
120 changes: 96 additions & 24 deletions src/map/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16071,48 +16071,120 @@ BUILDIN_FUNC(chatmes)
return SCRIPT_CMD_SUCCESS;
}

// change npc walkspeed [Valaris]
/**
* Change npc walkspeed.
* npcspeed <speed value> {,"<npc name>"};
*/
BUILDIN_FUNC(npcspeed)
{
struct npc_data* nd;
int speed;
npc_data* nd;

speed = script_getnum(st,2);
nd =(struct npc_data *)map_id2bl(st->oid);
if (script_hasdata(st, 3))
nd = npc_name2id(script_getstr(st, 3));
else
nd = map_id2nd(st->oid);

if( nd ) {
nd->speed = speed;
if (nd == nullptr) {
if (script_hasdata(st, 3))
ShowError("buildin_npcspeed: %s is a non-existing NPC.\n", script_getstr(st, 3));
else
ShowError("buildin_npcspeed: non-existing NPC.\n");
return SCRIPT_CMD_FAILURE;
}

int speed = script_getnum(st, 2);

if (speed < MIN_WALK_SPEED || speed > MAX_WALK_SPEED) {
ShowError("buildin_npcspeed: invalid speed %d (min: %d, max: %d).\n", speed, MIN_WALK_SPEED, MAX_WALK_SPEED);
return SCRIPT_CMD_FAILURE;
}

nd->speed = speed;

return SCRIPT_CMD_SUCCESS;
}
// make an npc walk to a position [Valaris]

/**
* Make an npc walk to a position.
* npcwalkto <x>,<y> {,"<npc name>"} };
*/
BUILDIN_FUNC(npcwalkto)
{
struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
int x=0,y=0;
npc_data* nd;

x=script_getnum(st,2);
y=script_getnum(st,3);
if (script_hasdata(st, 4))
nd = npc_name2id(script_getstr(st, 4));
else
nd = map_id2nd(st->oid);

if(nd) {
if (!nd->status.hp)
status_calc_npc(nd, SCO_FIRST);
if (nd == nullptr) {
if (script_hasdata(st, 4))
ShowError("buildin_npcwalkto: %s is a non-existing NPC.\n", script_getstr(st, 4));
else
status_calc_npc(nd, SCO_NONE);
unit_walktoxy(&nd->bl,x,y,0);
ShowError("buildin_npcwalkto: non-existing NPC.\n");
return SCRIPT_CMD_FAILURE;
}

if( nd->bl.m < 0 ){
ShowError( "buildin_npcwalkto: NPC is not on a map.\n" );
return SCRIPT_CMD_FAILURE;
}

struct map_data* mapdata = map_getmapdata( nd->bl.m );
int x = script_getnum(st, 2);
int y = script_getnum(st, 3);
Atemo marked this conversation as resolved.
Show resolved Hide resolved

if( x < 0 || x >= mapdata->xs || y < 0 || y >= mapdata->ys ){
ShowWarning( "buildin_npcwalkto: coordinates %d/%d are out of bounds in map %s(%dx%d).\n", x, y, mapdata->name, mapdata->xs, mapdata->ys );
return SCRIPT_CMD_FAILURE;
}

if (!nd->status.hp)
status_calc_npc(nd, SCO_FIRST);
else
status_calc_npc(nd, SCO_NONE);
unit_walktoxy(&nd->bl,x,y,0);

return SCRIPT_CMD_SUCCESS;
}

// stop an npc's movement [Valaris]
/**
* Stop an npc's movement.
* npcstop {"<npc name>", {"<flag>"}};
*/
BUILDIN_FUNC(npcstop)
{
struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
npc_data* nd;

if (script_hasdata(st, 2))
nd = npc_name2id(script_getstr(st, 2));
else
nd = map_id2nd(st->oid);

if(nd) {
unit_stop_walking(&nd->bl,1|4);
if (nd == nullptr) {
if (script_hasdata(st, 2))
ShowError("buildin_npcstop: %s is a non-existing NPC.\n", script_getstr(st, 2));
else
ShowError("buildin_npcstop: non-existing NPC.\n");
return SCRIPT_CMD_FAILURE;
}

int flag = USW_FIXPOS | USW_MOVE_FULL_CELL | USW_FORCE_STOP;

if (script_hasdata(st, 3)) {
flag = script_getnum(st, 3);

if (flag < USW_NONE || flag > USW_ALL) {
ShowError("buildin_npcstop: invalid flag %d.\n", flag);
return SCRIPT_CMD_FAILURE;
}

if (flag & USW_FORCE_STOP)
nd->ud.state.force_walk = false;
}

unit_stop_walking( &nd->bl, flag );

return SCRIPT_CMD_SUCCESS;
}

Expand Down Expand Up @@ -27607,9 +27679,9 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(mobcount,"ss"),
BUILDIN_DEF(getlook,"i?"),
BUILDIN_DEF(getsavepoint,"i?"),
BUILDIN_DEF(npcspeed,"i"), // [Valaris]
BUILDIN_DEF(npcwalkto,"ii"), // [Valaris]
BUILDIN_DEF(npcstop,""), // [Valaris]
BUILDIN_DEF(npcspeed,"i?"),
BUILDIN_DEF(npcwalkto,"ii?"),
BUILDIN_DEF(npcstop,"??"),
BUILDIN_DEF(getmapxy,"rrr??"), //by Lorky [Lupus]
BUILDIN_DEF(mapid2name,"i"),
BUILDIN_DEF(checkoption1,"i?"),
Expand Down
5 changes: 5 additions & 0 deletions src/map/script_constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10639,6 +10639,11 @@
export_constant(IWA_NONE);
export_constant(IWA_NOTDEAD);

/* npcspeed command */
export_constant(MIN_WALK_SPEED);
export_constant(MAX_WALK_SPEED);
export_constant(DEFAULT_NPC_WALK_SPEED);

/* skill hit */
export_constant(DMG_SINGLE);
export_constant(DMG_MULTI_HIT);
Expand Down