From 16dccc94f2e15cf08149a5c63c05ff7db34751c4 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 10 Jul 2026 14:19:32 +0200 Subject: [PATCH] Fix /setspeedmod: apply per-character, persist across stat recalculation Previously stomped the shared GServer->MOVE_SPEED_MOD movement-timing divisor, which character.cpp's UpdatePosition also uses for monster position interpolation -- GM testing broke movement/attack range server-wide until restart. Now stores a per-character SpeedModPercent that GetMoveSpeed() applies on every recompute, so it survives future SetStats() calls instead of being silently overwritten back to base speed. Also fixes Mspd_base (the value actually broadcast to clients) never being set while WALKING. --- Src/World Server/character.cpp | 1 + Src/World Server/chartype.h | 1 + Src/World Server/gmcmds.cpp | 21 +++++++++++++++++++-- Src/World Server/playerstats.cpp | 7 +++++++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Src/World Server/character.cpp b/Src/World Server/character.cpp index 0ba2b43..78cb97f 100644 --- a/Src/World Server/character.cpp +++ b/Src/World Server/character.cpp @@ -40,6 +40,7 @@ CCharacter::CCharacter( ) Stats->Accury = 0; Stats->Magic_Defense = 0; Stats->Move_Speed = 0; + Stats->SpeedModPercent = 100; Stats->Attack_Speed = 0; Stats->Attack_Distance = 0; Stats->MaxWeight = 0; diff --git a/Src/World Server/chartype.h b/Src/World Server/chartype.h index 16fe4e3..53f442c 100644 --- a/Src/World Server/chartype.h +++ b/Src/World Server/chartype.h @@ -63,6 +63,7 @@ struct STATS unsigned int Magic_Defense; unsigned int Move_Speed; unsigned int Mspd_base; + unsigned int SpeedModPercent; // GM /setspeedmod override, 100 = unmodified; applied in GetMoveSpeed() unsigned int Attack_Speed; float Attack_Distance; unsigned int MaxWeight; diff --git a/Src/World Server/gmcmds.cpp b/Src/World Server/gmcmds.cpp index f654460..3bf1afe 100644 --- a/Src/World Server/gmcmds.cpp +++ b/Src/World Server/gmcmds.cpp @@ -3814,10 +3814,27 @@ bool CWorldServer::pakGMChangeMonsterDmg(CPlayer* thisclient, int rate) return true; } -//change the mSpeedModif || For test only +//change this GM's own move speed by a percentage (100 = unmodified) || For test only bool CWorldServer::pakGMChangeMSpeedModif(CPlayer* thisclient, int modif) { - GServer->MOVE_SPEED_MOD = modif; + // NOTE: this used to stomp GServer->MOVE_SPEED_MOD, a server-wide + // constant that character.cpp's UpdatePosition also uses for monster + // position interpolation -- overwriting it here broke movement/attack + // range for everyone until the next restart reloaded it from + // list_config.movespeedmod. Use a per-character percent instead, which + // GetMoveSpeed() applies on every recompute so it survives future stat + // refreshes (equip changes, buffs, etc.) instead of being a one-shot + // value that the next SetStats() call overwrites back to base. + if(modif == 0) modif = 100; + thisclient->Stats->SpeedModPercent = modif; + thisclient->SetStats( ); + + BEGINPACKET( pakspd, 0x782 ); + ADDWORD( pakspd, thisclient->clientid ); + ADDBYTE( pakspd, thisclient->Status->Stance ); + ADDWORD( pakspd, thisclient->Stats->Mspd_base ); + SendToVisible( &pakspd, thisclient ); + BEGINPACKET (pak, 0x702); ADDSTRING(pak, "mSpeedModif Changed."); ADDBYTE(pak, 0); diff --git a/Src/World Server/playerstats.cpp b/Src/World Server/playerstats.cpp index b5e1811..bae8040 100644 --- a/Src/World Server/playerstats.cpp +++ b/Src/World Server/playerstats.cpp @@ -1050,6 +1050,13 @@ unsigned int CPlayer::GetMoveSpeed( ) default: break; } + if(Stats->SpeedModPercent != 100) + mspeed = mspeed * Stats->SpeedModPercent / 100; + // Mspd_base (what actually gets broadcast to clients, e.g. worldpackets.cpp's + // 0x782) was previously only touched by the RUNNING/DRIVING branches above -- + // WALKING/default left it stale, so speed changes never showed up client-side + // while walking. Mirror the final value unconditionally. + Stats->Mspd_base = mspeed; return mspeed; }