From 3ac982f3bddb8b1647502e0590bce7827ed02d2c Mon Sep 17 00:00:00 2001 From: Michael Oliver Date: Mon, 22 Dec 2025 22:00:54 +0000 Subject: [PATCH] feat(IW3 MP): add `SetStance` GSC player method --- README.md | 1 + src/game/iw3/mp/components/gsc_methods.cpp | 40 ++++++++ src/game/iw3/mp/structs.h | 103 ++++++++++++++++++++- src/game/iw3/mp/symbols.h | 6 ++ 4 files changed, 149 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f8a6b0..04c3197 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ GSC Methods: - `LeftButtonPressed` - Check if the player is moving left (left stick left). - `RightButtonPressed` - Check if the player is moving right (left stick right). - `SetVelocity` - Changes current player velocity. +- `SetStance` - Changes current players stance. - Usage: `self setVelocity((0, 0, 300)); // Go up.` diff --git a/src/game/iw3/mp/components/gsc_methods.cpp b/src/game/iw3/mp/components/gsc_methods.cpp index a9d1e3b..a0e5b4d 100644 --- a/src/game/iw3/mp/components/gsc_methods.cpp +++ b/src/game/iw3/mp/components/gsc_methods.cpp @@ -140,6 +140,45 @@ void PlayerCmd_SetVelocity(scr_entref_t entref) ent->client->ps.velocity[2] = velocity[2]; } +void PlayerCmd_SetStance(scr_entref_t entref) +{ + gentity_s *ent = GetPlayerEntity(entref); + + if (Scr_GetNumParam() != 1) + Scr_Error("usage: SetStance( )\n"); + + const char *stanceStr = Scr_GetString(0); + + int event = -1; + int newPmFlags = ent->client->ps.pm_flags; + + if (!I_stricmp(stanceStr, "stand")) + { + event = EV_STANCE_FORCE_STAND; + newPmFlags = (newPmFlags & ~0x3) | CL_STANCE_STAND; + } + else if (!I_stricmp(stanceStr, "crouch")) + { + event = EV_STANCE_FORCE_CROUCH; + newPmFlags = (newPmFlags & ~0x3) | CL_STANCE_CROUCH; + } + else if (!I_stricmp(stanceStr, "prone")) + { + event = EV_STANCE_FORCE_PRONE; + newPmFlags = (newPmFlags & ~0x3) | CL_STANCE_PRONE; + } + else + { + Scr_ParamError(0, "stance must be 'stand', 'crouch', or 'prone'"); + } + + // Update server-side stance flags + ent->client->ps.pm_flags = newPmFlags; + + // Send event to client to sync stance visually + G_AddEvent(ent, event, 0); +} + void GScr_CloneBrushModelToScriptModel(scr_entref_t entref) { gentity_s *scriptEnt = GetEntity(entref); @@ -188,6 +227,7 @@ gsc_methods::gsc_methods() Scr_AddMethod("leftbuttonpressed", PlayerCmd_LeftButtonPressed, 0); Scr_AddMethod("rightbuttonpressed", PlayerCmd_RightButtonPressed, 0); Scr_AddMethod("setvelocity", PlayerCmd_SetVelocity, 0); + Scr_AddMethod("setstance", PlayerCmd_SetStance, 0); // Script entity methods Scr_AddMethod("clonebrushmodeltoscriptmodel", GScr_CloneBrushModelToScriptModel, 0); diff --git a/src/game/iw3/mp/structs.h b/src/game/iw3/mp/structs.h index 94e9021..80b5c80 100644 --- a/src/game/iw3/mp/structs.h +++ b/src/game/iw3/mp/structs.h @@ -8,6 +8,99 @@ namespace iw3 { namespace mp { + +enum entity_event_t : __int32 +{ + EV_NONE = 0x0, + EV_FOLIAGE_SOUND = 0x1, + EV_STOP_WEAPON_SOUND = 0x2, + EV_SOUND_ALIAS = 0x3, + EV_SOUND_ALIAS_AS_MASTER = 0x4, + EV_STOPSOUNDS = 0x5, + EV_STANCE_FORCE_STAND = 0x6, + EV_STANCE_FORCE_CROUCH = 0x7, + EV_STANCE_FORCE_PRONE = 0x8, + EV_ITEM_PICKUP = 0x9, + EV_AMMO_PICKUP = 0xA, + EV_NOAMMO = 0xB, + EV_EMPTYCLIP = 0xC, + EV_EMPTY_OFFHAND = 0xD, + EV_RESET_ADS = 0xE, + EV_RELOAD = 0xF, + EV_RELOAD_FROM_EMPTY = 0x10, + EV_RELOAD_START = 0x11, + EV_RELOAD_END = 0x12, + EV_RELOAD_START_NOTIFY = 0x13, + EV_RELOAD_ADDAMMO = 0x14, + EV_RAISE_WEAPON = 0x15, + EV_FIRST_RAISE_WEAPON = 0x16, + EV_PUTAWAY_WEAPON = 0x17, + EV_WEAPON_ALT = 0x18, + EV_PULLBACK_WEAPON = 0x19, + EV_FIRE_WEAPON = 0x1A, + EV_FIRE_WEAPON_LASTSHOT = 0x1B, + EV_RECHAMBER_WEAPON = 0x1C, + EV_EJECT_BRASS = 0x1D, + EV_MELEE_SWIPE = 0x1E, + EV_FIRE_MELEE = 0x1F, + EV_PREP_OFFHAND = 0x20, + EV_USE_OFFHAND = 0x21, + EV_SWITCH_OFFHAND = 0x22, + EV_MELEE_HIT = 0x23, + EV_MELEE_MISS = 0x24, + EV_MELEE_BLOOD = 0x25, + EV_FIRE_WEAPON_MG42 = 0x26, + EV_FIRE_QUADBARREL_1 = 0x27, + EV_FIRE_QUADBARREL_2 = 0x28, + EV_BULLET_HIT = 0x29, + EV_BULLET_HIT_CLIENT_SMALL = 0x2A, + EV_BULLET_HIT_CLIENT_LARGE = 0x2B, + EV_GRENADE_BOUNCE = 0x2C, + EV_GRENADE_EXPLODE = 0x2D, + EV_ROCKET_EXPLODE = 0x2E, + EV_ROCKET_EXPLODE_NOMARKS = 0x2F, + EV_FLASHBANG_EXPLODE = 0x30, + EV_CUSTOM_EXPLODE = 0x31, + EV_CUSTOM_EXPLODE_NOMARKS = 0x32, + EV_CHANGE_TO_DUD = 0x33, + EV_DUD_EXPLODE = 0x34, + EV_DUD_IMPACT = 0x35, + EV_BULLET = 0x36, + EV_PLAY_FX = 0x37, + EV_PLAY_FX_ON_TAG = 0x38, + EV_PHYS_EXPLOSION_SPHERE = 0x39, + EV_PHYS_EXPLOSION_CYLINDER = 0x3A, + EV_PHYS_EXPLOSION_JOLT = 0x3B, + EV_PHYS_JITTER = 0x3C, + EV_EARTHQUAKE = 0x3D, + EV_GRENADE_SUICIDE = 0x3E, + EV_DETONATE = 0x3F, + EV_NIGHTVISION_WEAR = 0x40, + EV_NIGHTVISION_REMOVE = 0x41, + EV_PLAY_RUMBLE_ON_ENT = 0x42, + EV_PLAY_RUMBLE_ON_POS = 0x43, + EV_PLAY_RUMBLELOOP_ON_ENT = 0x44, + EV_PLAY_RUMBLELOOP_ON_POS = 0x45, + EV_STOP_RUMBLE = 0x46, + EV_STOP_ALL_RUMBLES = 0x47, + EV_OBITUARY = 0x48, + EV_NO_FRAG_GRENADE_HINT = 0x49, + EV_NO_SPECIAL_GRENADE_HINT = 0x4A, + EV_TARGET_TOO_CLOSE_HINT = 0x4B, + EV_TARGET_NOT_ENOUGH_CLEARANCE = 0x4C, + EV_LOCKON_REQUIRED_HINT = 0x4D, + EV_FOOTSTEP_SPRINT = 0x4E, + EV_FOOTSTEP_RUN = 0x4F, + EV_FOOTSTEP_WALK = 0x50, + EV_FOOTSTEP_PRONE = 0x51, + EV_JUMP = 0x52, + EV_LANDING_FIRST = 0x53, + EV_LANDING_LAST = 0x6F, + EV_LANDING_PAIN_FIRST = 0x70, + EV_LANDING_PAIN_LAST = 0x8C, + EV_MAX_EVENTS = 0x8D, +}; + // TODO: verify fields struct DxGlobals { @@ -1245,6 +1338,14 @@ struct RawFile const char *buffer; }; +struct StringTable +{ + const char *name; + int columnCount; + int rowCount; + const char **values; +}; + struct Material; struct Glyph @@ -1308,7 +1409,7 @@ union XAssetHeader // const FxEffectDef *fx; // FxImpactTable *impactFx; RawFile *rawfile; - // StringTable *stringTable; + StringTable *stringTable; void *data; }; diff --git a/src/game/iw3/mp/symbols.h b/src/game/iw3/mp/symbols.h index 88a04a9..f53a826 100644 --- a/src/game/iw3/mp/symbols.h +++ b/src/game/iw3/mp/symbols.h @@ -202,6 +202,12 @@ static auto Scr_AddSourceBuffer = reinterpret_cast( 0x822212C0); +typedef int (*I_stricmp_t)(const char *s0, const char *s1); +static I_stricmp_t I_stricmp = reinterpret_cast(0x821CDCC8); + +typedef void (*G_AddEvent_t)(gentity_s *ent, int event, int eventParm); +static G_AddEvent_t G_AddEvent = reinterpret_cast(0x8224AB48); + typedef int (*Key_StringToKeynum_t)(const char *str); static Key_StringToKeynum_t Key_StringToKeynum = reinterpret_cast(0x822D69A8);