From aced358b5676d631dc578d28dbd21f79b5f2eab8 Mon Sep 17 00:00:00 2001 From: DeValdi Date: Fri, 6 Feb 2026 02:23:52 +0100 Subject: [PATCH] Add additional setWeaponProperty customizations This change unlocks the "anim_group" weapon property's setter and gives access to the natively supported fourth skill level ("special"). The two changes complement each other, since developers are more likely to use the first feature (i.e. an additional weapon animation) if it means not overriding an already in-use skill level. To specify the "special" skill level as the target of setWeaponProperty calls, pass "special" instead of "poor", "std", or "pro". The "anim_group" property expects as argument the animation group ID in the form of an integer. The wiki page on setWeaponProperty should be updated with additional information, since the ID's are only visible in code. To make a ped use the "special" skill level, assign a stat value of 5000 to the weapon stat. This reflects the same value used by GTA:SA for the cops' pistol animation (the only place where this skill level is used by the base game). The Lua function setPedStat has been updated to differentiate between weapon and non-weapon stats, notably for value checking (maximum is either 5000 or 1000, respectively). The weapon property sync structure was changed to include ALL weapon properties, not just the one's that can be set via setWeaponProperty. This is due to the fact that GTA:SA seems to clear the "special" skill level entries when joining the server, which also includes indices, slot numbers, etc. There was no problem before (with just the "poor", "std" and "pro" levels) since these entries do not get cleared and the server only had to override the relevant properties when joining. By default, the "special" skill levels for all weapons but the pistol inherit their properties from their "pro" counterparts. The pistol uses the "cop_colt" configuration provided by the game's "weapon.dat" file. --- Client/game_sa/CWeaponStatManagerSA.cpp | 306 ++++++++++++------ Client/game_sa/CWeaponStatManagerSA.h | 1 + .../mods/deathmatch/logic/CPacketHandler.cpp | 21 +- .../logic/lua/CLuaFunctionParseHelpers.cpp | 1 + .../deathmatch/logic/luadefs/CLuaPedDefs.cpp | 18 +- Client/multiplayer_sa/CMultiplayerSA.cpp | 70 ++-- .../logic/CStaticFunctionDefinitions.cpp | 83 +++-- .../deathmatch/logic/CWeaponStatManager.cpp | 188 ++++++++--- .../deathmatch/logic/CWeaponStatManager.h | 1 + .../logic/lua/CLuaFunctionParseHelpers.cpp | 1 + .../logic/luadefs/CLuaFunctionDefs.Weapon.cpp | 1 + .../logic/packets/CMapInfoPacket.cpp | 36 ++- Shared/sdk/net/SyncStructures.h | 51 ++- 13 files changed, 576 insertions(+), 202 deletions(-) diff --git a/Client/game_sa/CWeaponStatManagerSA.cpp b/Client/game_sa/CWeaponStatManagerSA.cpp index e976a61250f..999ef64331b 100644 --- a/Client/game_sa/CWeaponStatManagerSA.cpp +++ b/Client/game_sa/CWeaponStatManagerSA.cpp @@ -14,6 +14,7 @@ sWeaponInfo CWeaponStatManagerSA::OriginalPoorWeaponData[WEAPONTYPE_MAX + 1]; sWeaponInfo CWeaponStatManagerSA::OriginalNormalWeaponData[WEAPONTYPE_MAX + 1]; sWeaponInfo CWeaponStatManagerSA::OriginalHitmanWeaponData[WEAPONTYPE_MAX + 1]; +sWeaponInfo CWeaponStatManagerSA::OriginalSpecialWeaponData[WEAPONTYPE_MAX + 1]; CWeaponStatManagerSA::CWeaponStatManagerSA() { @@ -35,7 +36,7 @@ void CWeaponStatManagerSA::InitLists() LoadDefaultInternal(pWeaponStat, weaponType); } - for (int skill = 0; skill < 3; skill++) + for (int skill = 0; skill < WEAPONSKILL_MAX_NUMBER; skill++) { for (int i = 0; i < NUM_WeaponInfosOtherSkill; i++) { @@ -124,21 +125,17 @@ CWeaponStat* CWeaponStatManagerSA::GetWeaponStatsFromSkillLevel(eWeaponType type CWeaponStat* pPoor = GetWeaponStats(type, WEAPONSKILL_POOR); CWeaponStat* pStd = GetWeaponStats(type, WEAPONSKILL_STD); CWeaponStat* pPro = GetWeaponStats(type, WEAPONSKILL_PRO); - if (pStd) - { - if (pPoor && pPro) - { - if (fSkillLevel >= pPro->GetRequiredStatLevel()) - return pPro; - else if (fSkillLevel >= pStd->GetRequiredStatLevel()) - return pStd; - else - return pPoor; - } - else - return pStd; - } - return nullptr; + CWeaponStat* pSpec = GetWeaponStats(type, WEAPONSKILL_SPECIAL); + if (pSpec && fSkillLevel >= pSpec->GetRequiredStatLevel()) + return pSpec; + else if (pPro && fSkillLevel >= pPro->GetRequiredStatLevel()) + return pPro; + else if (pStd && fSkillLevel >= pStd->GetRequiredStatLevel()) + return pStd; + else if (pPoor && fSkillLevel >= pPoor->GetRequiredStatLevel()) + return pPoor; + else + return pStd; } CWeaponStat* CWeaponStatManagerSA::GetOriginalWeaponStats(eWeaponType type, eWeaponSkill skill) @@ -1510,6 +1507,71 @@ void CWeaponStatManagerSA::Init() OriginalHitmanWeaponData[32].aim_offset = 4; OriginalHitmanWeaponData[32].default_combo = 4; OriginalHitmanWeaponData[32].combos_available = 1; + + // 22 - Colt 45 Stat: 3 + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].weapon_range = 35.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].target_range = 30.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].accuracy = 1.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].damage = 25; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].life_span = 0.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].firing_speed = 0.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].spread = 0.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].maximum_clip_ammo = 17; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].move_speed = 1.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].flags = 28721; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim_group = 14; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].fire_type = (eFireType)1; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].model = 346; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].model2 = -1; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].weapon_slot = (eWeaponSlot)2; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].fire_offset = CVector(0.25f, 0.050000000745058f, 0.090000003576279f); + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].skill_level = (eWeaponSkill)3; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].required_skill_level = 5000; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim_loop_start = 0.20000001788139f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim_loop_stop = 0.49399998784065f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim_loop_bullet_fire = 0.20000001788139f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim2_loop_start = 0.20000001788139f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim2_loop_stop = 0.49399998784065f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim2_loop_bullet_fire = 0.20000001788139f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim_breakout_time = 3.3000001907349f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].radius = 0.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].aim_offset = 2; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].default_combo = 4; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].combos_available = 1; + + for (int i = WEAPONTYPE_PISTOL + 1; i <= WEAPONTYPE_EXTINGUISHER; ++i) + { + OriginalSpecialWeaponData[i].weapon_range = OriginalHitmanWeaponData[i].weapon_range; + OriginalSpecialWeaponData[i].target_range = OriginalHitmanWeaponData[i].target_range; + OriginalSpecialWeaponData[i].accuracy = OriginalHitmanWeaponData[i].accuracy; + OriginalSpecialWeaponData[i].damage = OriginalHitmanWeaponData[i].damage; + OriginalSpecialWeaponData[i].life_span = OriginalHitmanWeaponData[i].life_span; + OriginalSpecialWeaponData[i].firing_speed = OriginalHitmanWeaponData[i].firing_speed; + OriginalSpecialWeaponData[i].spread = OriginalHitmanWeaponData[i].spread; + OriginalSpecialWeaponData[i].maximum_clip_ammo = OriginalHitmanWeaponData[i].maximum_clip_ammo; + OriginalSpecialWeaponData[i].move_speed = OriginalHitmanWeaponData[i].move_speed; + OriginalSpecialWeaponData[i].flags = OriginalHitmanWeaponData[i].flags; + OriginalSpecialWeaponData[i].anim_group = OriginalHitmanWeaponData[i].anim_group; + OriginalSpecialWeaponData[i].fire_type = OriginalHitmanWeaponData[i].fire_type; + OriginalSpecialWeaponData[i].model = OriginalHitmanWeaponData[i].model; + OriginalSpecialWeaponData[i].model2 = OriginalHitmanWeaponData[i].model2; + OriginalSpecialWeaponData[i].weapon_slot = OriginalHitmanWeaponData[i].weapon_slot; + OriginalSpecialWeaponData[i].fire_offset = OriginalHitmanWeaponData[i].fire_offset; + OriginalSpecialWeaponData[i].skill_level = (eWeaponSkill)3; + OriginalSpecialWeaponData[i].required_skill_level = 5000; + OriginalSpecialWeaponData[i].anim_loop_start = OriginalHitmanWeaponData[i].anim_loop_start; + OriginalSpecialWeaponData[i].anim_loop_stop = OriginalHitmanWeaponData[i].anim_loop_stop; + OriginalSpecialWeaponData[i].anim_loop_bullet_fire = OriginalHitmanWeaponData[i].anim_loop_bullet_fire; + OriginalSpecialWeaponData[i].anim2_loop_start = OriginalHitmanWeaponData[i].anim2_loop_start; + OriginalSpecialWeaponData[i].anim2_loop_stop = OriginalHitmanWeaponData[i].anim2_loop_stop; + OriginalSpecialWeaponData[i].anim2_loop_bullet_fire = OriginalHitmanWeaponData[i].anim2_loop_bullet_fire; + OriginalSpecialWeaponData[i].anim_breakout_time = OriginalHitmanWeaponData[i].anim_breakout_time; + OriginalSpecialWeaponData[i].radius = OriginalHitmanWeaponData[i].radius; + OriginalSpecialWeaponData[i].aim_offset = OriginalHitmanWeaponData[i].aim_offset; + OriginalSpecialWeaponData[i].default_combo = OriginalHitmanWeaponData[i].default_combo; + OriginalSpecialWeaponData[i].combos_available = OriginalHitmanWeaponData[i].combos_available; + } + // End of Skill Level Weapons } @@ -1555,6 +1617,39 @@ bool CWeaponStatManagerSA::LoadDefault(CWeaponStat* pDest, eWeaponType weaponTyp pDest->SetCombosAvailable(OriginalPoorWeaponData[iVal].combos_available); break; } + case WEAPONSKILL_STD: + { + pDest->SetWeaponRange(OriginalNormalWeaponData[iVal].weapon_range); + pDest->SetTargetRange(OriginalNormalWeaponData[iVal].target_range); + pDest->SetAccuracy(OriginalNormalWeaponData[iVal].accuracy); + pDest->SetDamagePerHit(OriginalNormalWeaponData[iVal].damage); + pDest->SetLifeSpan(OriginalNormalWeaponData[iVal].life_span); + pDest->SetFiringSpeed(OriginalNormalWeaponData[iVal].firing_speed); + pDest->SetSpread(OriginalNormalWeaponData[iVal].spread); + pDest->SetMaximumClipAmmo(OriginalNormalWeaponData[iVal].maximum_clip_ammo); + pDest->SetMoveSpeed(OriginalNormalWeaponData[iVal].move_speed); + pDest->SetFlags(OriginalNormalWeaponData[iVal].flags); + pDest->SetAnimGroup(OriginalNormalWeaponData[iVal].anim_group); + pDest->SetFireType(OriginalNormalWeaponData[iVal].fire_type); + pDest->SetModel(OriginalNormalWeaponData[iVal].model); + pDest->SetModel2(OriginalNormalWeaponData[iVal].model2); + pDest->SetSlot(OriginalNormalWeaponData[iVal].weapon_slot); + pDest->SetFireOffset(&OriginalNormalWeaponData[iVal].fire_offset); + pDest->SetSkill(OriginalNormalWeaponData[iVal].skill_level); + pDest->SetRequiredStatLevel(OriginalNormalWeaponData[iVal].required_skill_level); + pDest->SetWeaponAnimLoopStart(OriginalNormalWeaponData[iVal].anim_loop_start); + pDest->SetWeaponAnimLoopStop(OriginalNormalWeaponData[iVal].anim_loop_stop); + pDest->SetWeaponAnimLoopFireTime(OriginalNormalWeaponData[iVal].anim_loop_bullet_fire); + pDest->SetWeaponAnim2LoopStart(OriginalNormalWeaponData[iVal].anim2_loop_start); + pDest->SetWeaponAnim2LoopStop(OriginalNormalWeaponData[iVal].anim2_loop_stop); + pDest->SetWeaponAnim2LoopFireTime(OriginalNormalWeaponData[iVal].anim2_loop_bullet_fire); + pDest->SetWeaponAnimBreakoutTime(OriginalNormalWeaponData[iVal].anim_breakout_time); + pDest->SetWeaponRadius(OriginalNormalWeaponData[iVal].radius); + pDest->SetAimOffsetIndex(OriginalNormalWeaponData[iVal].aim_offset); + pDest->SetDefaultCombo(OriginalNormalWeaponData[iVal].default_combo); + pDest->SetCombosAvailable(OriginalNormalWeaponData[iVal].combos_available); + break; + } case WEAPONSKILL_PRO: { pDest->SetWeaponRange(OriginalHitmanWeaponData[iVal].weapon_range); @@ -1588,37 +1683,37 @@ bool CWeaponStatManagerSA::LoadDefault(CWeaponStat* pDest, eWeaponType weaponTyp pDest->SetCombosAvailable(OriginalHitmanWeaponData[iVal].combos_available); break; } - case WEAPONSKILL_STD: + case WEAPONSKILL_SPECIAL: { - pDest->SetWeaponRange(OriginalNormalWeaponData[iVal].weapon_range); - pDest->SetTargetRange(OriginalNormalWeaponData[iVal].target_range); - pDest->SetAccuracy(OriginalNormalWeaponData[iVal].accuracy); - pDest->SetDamagePerHit(OriginalNormalWeaponData[iVal].damage); - pDest->SetLifeSpan(OriginalNormalWeaponData[iVal].life_span); - pDest->SetFiringSpeed(OriginalNormalWeaponData[iVal].firing_speed); - pDest->SetSpread(OriginalNormalWeaponData[iVal].spread); - pDest->SetMaximumClipAmmo(OriginalNormalWeaponData[iVal].maximum_clip_ammo); - pDest->SetMoveSpeed(OriginalNormalWeaponData[iVal].move_speed); - pDest->SetFlags(OriginalNormalWeaponData[iVal].flags); - pDest->SetAnimGroup(OriginalNormalWeaponData[iVal].anim_group); - pDest->SetFireType(OriginalNormalWeaponData[iVal].fire_type); - pDest->SetModel(OriginalNormalWeaponData[iVal].model); - pDest->SetModel2(OriginalNormalWeaponData[iVal].model2); - pDest->SetSlot(OriginalNormalWeaponData[iVal].weapon_slot); - pDest->SetFireOffset(&OriginalNormalWeaponData[iVal].fire_offset); - pDest->SetSkill(OriginalNormalWeaponData[iVal].skill_level); - pDest->SetRequiredStatLevel(OriginalNormalWeaponData[iVal].required_skill_level); - pDest->SetWeaponAnimLoopStart(OriginalNormalWeaponData[iVal].anim_loop_start); - pDest->SetWeaponAnimLoopStop(OriginalNormalWeaponData[iVal].anim_loop_stop); - pDest->SetWeaponAnimLoopFireTime(OriginalNormalWeaponData[iVal].anim_loop_bullet_fire); - pDest->SetWeaponAnim2LoopStart(OriginalNormalWeaponData[iVal].anim2_loop_start); - pDest->SetWeaponAnim2LoopStop(OriginalNormalWeaponData[iVal].anim2_loop_stop); - pDest->SetWeaponAnim2LoopFireTime(OriginalNormalWeaponData[iVal].anim2_loop_bullet_fire); - pDest->SetWeaponAnimBreakoutTime(OriginalNormalWeaponData[iVal].anim_breakout_time); - pDest->SetWeaponRadius(OriginalNormalWeaponData[iVal].radius); - pDest->SetAimOffsetIndex(OriginalNormalWeaponData[iVal].aim_offset); - pDest->SetDefaultCombo(OriginalNormalWeaponData[iVal].default_combo); - pDest->SetCombosAvailable(OriginalNormalWeaponData[iVal].combos_available); + pDest->SetWeaponRange(OriginalSpecialWeaponData[iVal].weapon_range); + pDest->SetTargetRange(OriginalSpecialWeaponData[iVal].target_range); + pDest->SetAccuracy(OriginalSpecialWeaponData[iVal].accuracy); + pDest->SetDamagePerHit(OriginalSpecialWeaponData[iVal].damage); + pDest->SetLifeSpan(OriginalSpecialWeaponData[iVal].life_span); + pDest->SetFiringSpeed(OriginalSpecialWeaponData[iVal].firing_speed); + pDest->SetSpread(OriginalSpecialWeaponData[iVal].spread); + pDest->SetMaximumClipAmmo(OriginalSpecialWeaponData[iVal].maximum_clip_ammo); + pDest->SetMoveSpeed(OriginalSpecialWeaponData[iVal].move_speed); + pDest->SetFlags(OriginalSpecialWeaponData[iVal].flags); + pDest->SetAnimGroup(OriginalSpecialWeaponData[iVal].anim_group); + pDest->SetFireType(OriginalSpecialWeaponData[iVal].fire_type); + pDest->SetModel(OriginalSpecialWeaponData[iVal].model); + pDest->SetModel2(OriginalSpecialWeaponData[iVal].model2); + pDest->SetSlot(OriginalSpecialWeaponData[iVal].weapon_slot); + pDest->SetFireOffset(&OriginalSpecialWeaponData[iVal].fire_offset); + pDest->SetSkill(OriginalSpecialWeaponData[iVal].skill_level); + pDest->SetRequiredStatLevel(OriginalSpecialWeaponData[iVal].required_skill_level); + pDest->SetWeaponAnimLoopStart(OriginalSpecialWeaponData[iVal].anim_loop_start); + pDest->SetWeaponAnimLoopStop(OriginalSpecialWeaponData[iVal].anim_loop_stop); + pDest->SetWeaponAnimLoopFireTime(OriginalSpecialWeaponData[iVal].anim_loop_bullet_fire); + pDest->SetWeaponAnim2LoopStart(OriginalSpecialWeaponData[iVal].anim2_loop_start); + pDest->SetWeaponAnim2LoopStop(OriginalSpecialWeaponData[iVal].anim2_loop_stop); + pDest->SetWeaponAnim2LoopFireTime(OriginalSpecialWeaponData[iVal].anim2_loop_bullet_fire); + pDest->SetWeaponAnimBreakoutTime(OriginalSpecialWeaponData[iVal].anim_breakout_time); + pDest->SetWeaponRadius(OriginalSpecialWeaponData[iVal].radius); + pDest->SetAimOffsetIndex(OriginalSpecialWeaponData[iVal].aim_offset); + pDest->SetDefaultCombo(OriginalSpecialWeaponData[iVal].default_combo); + pDest->SetCombosAvailable(OriginalSpecialWeaponData[iVal].combos_available); break; } } @@ -1669,6 +1764,39 @@ bool CWeaponStatManagerSA::LoadDefaultInternal(CWeaponStatSA* pDest, eWeaponType pDest->SetCombosAvailable(OriginalPoorWeaponData[iVal].combos_available); break; } + case WEAPONSKILL_STD: + { + pDest->SetWeaponRange(OriginalNormalWeaponData[iVal].weapon_range); + pDest->SetTargetRange(OriginalNormalWeaponData[iVal].target_range); + pDest->SetAccuracy(OriginalNormalWeaponData[iVal].accuracy); + pDest->SetDamagePerHit(OriginalNormalWeaponData[iVal].damage); + pDest->SetLifeSpan(OriginalNormalWeaponData[iVal].life_span); + pDest->SetFiringSpeed(OriginalNormalWeaponData[iVal].firing_speed); + pDest->SetSpread(OriginalNormalWeaponData[iVal].spread); + pDest->SetMaximumClipAmmo(OriginalNormalWeaponData[iVal].maximum_clip_ammo); + pDest->SetMoveSpeed(OriginalNormalWeaponData[iVal].move_speed); + pDest->SetFlags(OriginalNormalWeaponData[iVal].flags); + pDest->SetAnimGroup(OriginalNormalWeaponData[iVal].anim_group); + pDest->SetFireType(OriginalNormalWeaponData[iVal].fire_type); + pDest->SetModel(OriginalNormalWeaponData[iVal].model); + pDest->SetModel2(OriginalNormalWeaponData[iVal].model2); + pDest->SetSlot(OriginalNormalWeaponData[iVal].weapon_slot); + pDest->SetFireOffset(&OriginalNormalWeaponData[iVal].fire_offset); + pDest->SetSkill(OriginalNormalWeaponData[iVal].skill_level); + pDest->SetRequiredStatLevel(OriginalNormalWeaponData[iVal].required_skill_level); + pDest->SetWeaponAnimLoopStart(OriginalNormalWeaponData[iVal].anim_loop_start); + pDest->SetWeaponAnimLoopStop(OriginalNormalWeaponData[iVal].anim_loop_stop); + pDest->SetWeaponAnimLoopFireTime(OriginalNormalWeaponData[iVal].anim_loop_bullet_fire); + pDest->SetWeaponAnim2LoopStart(OriginalNormalWeaponData[iVal].anim2_loop_start); + pDest->SetWeaponAnim2LoopStop(OriginalNormalWeaponData[iVal].anim2_loop_stop); + pDest->SetWeaponAnim2LoopFireTime(OriginalNormalWeaponData[iVal].anim2_loop_bullet_fire); + pDest->SetWeaponAnimBreakoutTime(OriginalNormalWeaponData[iVal].anim_breakout_time); + pDest->SetWeaponRadius(OriginalNormalWeaponData[iVal].radius); + pDest->SetAimOffsetIndex(OriginalNormalWeaponData[iVal].aim_offset); + pDest->SetDefaultCombo(OriginalNormalWeaponData[iVal].default_combo); + pDest->SetCombosAvailable(OriginalNormalWeaponData[iVal].combos_available); + break; + } case WEAPONSKILL_PRO: { pDest->SetWeaponRange(OriginalHitmanWeaponData[iVal].weapon_range); @@ -1702,37 +1830,37 @@ bool CWeaponStatManagerSA::LoadDefaultInternal(CWeaponStatSA* pDest, eWeaponType pDest->SetCombosAvailable(OriginalHitmanWeaponData[iVal].combos_available); break; } - case WEAPONSKILL_STD: + case WEAPONSKILL_SPECIAL: { - pDest->SetWeaponRange(OriginalNormalWeaponData[iVal].weapon_range); - pDest->SetTargetRange(OriginalNormalWeaponData[iVal].target_range); - pDest->SetAccuracy(OriginalNormalWeaponData[iVal].accuracy); - pDest->SetDamagePerHit(OriginalNormalWeaponData[iVal].damage); - pDest->SetLifeSpan(OriginalNormalWeaponData[iVal].life_span); - pDest->SetFiringSpeed(OriginalNormalWeaponData[iVal].firing_speed); - pDest->SetSpread(OriginalNormalWeaponData[iVal].spread); - pDest->SetMaximumClipAmmo(OriginalNormalWeaponData[iVal].maximum_clip_ammo); - pDest->SetMoveSpeed(OriginalNormalWeaponData[iVal].move_speed); - pDest->SetFlags(OriginalNormalWeaponData[iVal].flags); - pDest->SetAnimGroup(OriginalNormalWeaponData[iVal].anim_group); - pDest->SetFireType(OriginalNormalWeaponData[iVal].fire_type); - pDest->SetModel(OriginalNormalWeaponData[iVal].model); - pDest->SetModel2(OriginalNormalWeaponData[iVal].model2); - pDest->SetSlot(OriginalNormalWeaponData[iVal].weapon_slot); - pDest->SetFireOffset(&OriginalNormalWeaponData[iVal].fire_offset); - pDest->SetSkill(OriginalNormalWeaponData[iVal].skill_level); - pDest->SetRequiredStatLevel(OriginalNormalWeaponData[iVal].required_skill_level); - pDest->SetWeaponAnimLoopStart(OriginalNormalWeaponData[iVal].anim_loop_start); - pDest->SetWeaponAnimLoopStop(OriginalNormalWeaponData[iVal].anim_loop_stop); - pDest->SetWeaponAnimLoopFireTime(OriginalNormalWeaponData[iVal].anim_loop_bullet_fire); - pDest->SetWeaponAnim2LoopStart(OriginalNormalWeaponData[iVal].anim2_loop_start); - pDest->SetWeaponAnim2LoopStop(OriginalNormalWeaponData[iVal].anim2_loop_stop); - pDest->SetWeaponAnim2LoopFireTime(OriginalNormalWeaponData[iVal].anim2_loop_bullet_fire); - pDest->SetWeaponAnimBreakoutTime(OriginalNormalWeaponData[iVal].anim_breakout_time); - pDest->SetWeaponRadius(OriginalNormalWeaponData[iVal].radius); - pDest->SetAimOffsetIndex(OriginalNormalWeaponData[iVal].aim_offset); - pDest->SetDefaultCombo(OriginalNormalWeaponData[iVal].default_combo); - pDest->SetCombosAvailable(OriginalNormalWeaponData[iVal].combos_available); + pDest->SetWeaponRange(OriginalSpecialWeaponData[iVal].weapon_range); + pDest->SetTargetRange(OriginalSpecialWeaponData[iVal].target_range); + pDest->SetAccuracy(OriginalSpecialWeaponData[iVal].accuracy); + pDest->SetDamagePerHit(OriginalSpecialWeaponData[iVal].damage); + pDest->SetLifeSpan(OriginalSpecialWeaponData[iVal].life_span); + pDest->SetFiringSpeed(OriginalSpecialWeaponData[iVal].firing_speed); + pDest->SetSpread(OriginalSpecialWeaponData[iVal].spread); + pDest->SetMaximumClipAmmo(OriginalSpecialWeaponData[iVal].maximum_clip_ammo); + pDest->SetMoveSpeed(OriginalSpecialWeaponData[iVal].move_speed); + pDest->SetFlags(OriginalSpecialWeaponData[iVal].flags); + pDest->SetAnimGroup(OriginalSpecialWeaponData[iVal].anim_group); + pDest->SetFireType(OriginalSpecialWeaponData[iVal].fire_type); + pDest->SetModel(OriginalSpecialWeaponData[iVal].model); + pDest->SetModel2(OriginalSpecialWeaponData[iVal].model2); + pDest->SetSlot(OriginalSpecialWeaponData[iVal].weapon_slot); + pDest->SetFireOffset(&OriginalSpecialWeaponData[iVal].fire_offset); + pDest->SetSkill(OriginalSpecialWeaponData[iVal].skill_level); + pDest->SetRequiredStatLevel(OriginalSpecialWeaponData[iVal].required_skill_level); + pDest->SetWeaponAnimLoopStart(OriginalSpecialWeaponData[iVal].anim_loop_start); + pDest->SetWeaponAnimLoopStop(OriginalSpecialWeaponData[iVal].anim_loop_stop); + pDest->SetWeaponAnimLoopFireTime(OriginalSpecialWeaponData[iVal].anim_loop_bullet_fire); + pDest->SetWeaponAnim2LoopStart(OriginalSpecialWeaponData[iVal].anim2_loop_start); + pDest->SetWeaponAnim2LoopStop(OriginalSpecialWeaponData[iVal].anim2_loop_stop); + pDest->SetWeaponAnim2LoopFireTime(OriginalSpecialWeaponData[iVal].anim2_loop_bullet_fire); + pDest->SetWeaponAnimBreakoutTime(OriginalSpecialWeaponData[iVal].anim_breakout_time); + pDest->SetWeaponRadius(OriginalSpecialWeaponData[iVal].radius); + pDest->SetAimOffsetIndex(OriginalSpecialWeaponData[iVal].aim_offset); + pDest->SetDefaultCombo(OriginalSpecialWeaponData[iVal].default_combo); + pDest->SetCombosAvailable(OriginalSpecialWeaponData[iVal].combos_available); break; } } @@ -1746,19 +1874,15 @@ eWeaponSkill CWeaponStatManagerSA::GetWeaponSkillFromSkillLevel(eWeaponType type CWeaponStat* pPoor = GetWeaponStats(type, WEAPONSKILL_POOR); CWeaponStat* pStd = GetWeaponStats(type, WEAPONSKILL_STD); CWeaponStat* pPro = GetWeaponStats(type, WEAPONSKILL_PRO); - if (pStd) - { - if (pPoor && pPro) - { - if (fSkillLevel >= pPro->GetRequiredStatLevel()) - return WEAPONSKILL_PRO; - else if (fSkillLevel >= pStd->GetRequiredStatLevel()) - return WEAPONSKILL_STD; - else - return WEAPONSKILL_POOR; - } - else - return WEAPONSKILL_STD; - } - return WEAPONSKILL_STD; + CWeaponStat* pSpec = GetWeaponStats(type, WEAPONSKILL_SPECIAL); + if (pSpec && fSkillLevel >= pSpec->GetRequiredStatLevel()) + return WEAPONSKILL_SPECIAL; + else if (pPro && fSkillLevel >= pPro->GetRequiredStatLevel()) + return WEAPONSKILL_PRO; + else if (pStd && fSkillLevel >= pStd->GetRequiredStatLevel()) + return WEAPONSKILL_STD; + else if (pPoor && fSkillLevel >= pPoor->GetRequiredStatLevel()) + return WEAPONSKILL_POOR; + else + return WEAPONSKILL_STD; } diff --git a/Client/game_sa/CWeaponStatManagerSA.h b/Client/game_sa/CWeaponStatManagerSA.h index 8446598e2b1..f79b0714a66 100644 --- a/Client/game_sa/CWeaponStatManagerSA.h +++ b/Client/game_sa/CWeaponStatManagerSA.h @@ -100,4 +100,5 @@ class CWeaponStatManagerSA : public CWeaponStatManager static sWeaponInfo OriginalPoorWeaponData[WEAPONTYPE_MAX + 1]; static sWeaponInfo OriginalNormalWeaponData[WEAPONTYPE_MAX + 1]; static sWeaponInfo OriginalHitmanWeaponData[WEAPONTYPE_MAX + 1]; + static sWeaponInfo OriginalSpecialWeaponData[WEAPONTYPE_MAX + 1]; }; diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index 7cf90498616..9166d6b527d 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -2599,6 +2599,8 @@ void CPacketHandler::Packet_MapInfo(NetBitStreamInterface& bitStream) pWeaponInfo->SetWeaponAnim2LoopFireTime(weaponProperty.data.anim2_loop_bullet_fire); pWeaponInfo->SetAnimBreakoutTime(weaponProperty.data.anim_breakout_time); + + pWeaponInfo->SetAnimGroup(weaponProperty.data.anim_group); } bool bEnabled; @@ -2613,7 +2615,7 @@ void CPacketHandler::Packet_MapInfo(NetBitStreamInterface& bitStream) bitStream.ReadBit(bReadWeaponInfo); if (bReadWeaponInfo) { - for (int j = 0; j <= 2; j++) + for (int j = 0; j <= 3; j++) { bitStream.Read(&weaponProperty); CWeaponStat* pWeaponInfo = g_pGame->GetWeaponStatManager()->GetWeaponStats((eWeaponType)weaponProperty.data.weaponType, (eWeaponSkill)j); @@ -2634,6 +2636,23 @@ void CPacketHandler::Packet_MapInfo(NetBitStreamInterface& bitStream) pWeaponInfo->SetWeaponAnim2LoopFireTime(weaponProperty.data.anim2_loop_bullet_fire); pWeaponInfo->SetAnimBreakoutTime(weaponProperty.data.anim_breakout_time); + + pWeaponInfo->SetAnimGroup(weaponProperty.data.anim_group); + pWeaponInfo->SetAnimGroup(weaponProperty.data.anim_group); + pWeaponInfo->SetFireType((eFireType)weaponProperty.data.fire_type); + pWeaponInfo->SetModel(weaponProperty.data.model); + pWeaponInfo->SetModel2( weaponProperty.data.model2); + pWeaponInfo->SetSlot((eWeaponSlot)weaponProperty.data.weapon_slot); + pWeaponInfo->SetFireOffset(&weaponProperty.data.fire_offset); + pWeaponInfo->SetSkill((eWeaponSkill)weaponProperty.data.skill_level); + pWeaponInfo->SetRequiredStatLevel(weaponProperty.data.required_skill_level); + pWeaponInfo->SetFiringSpeed(weaponProperty.data.firing_speed); + pWeaponInfo->SetRadius(weaponProperty.data.radius); + pWeaponInfo->SetLifeSpan(weaponProperty.data.life_span); + pWeaponInfo->SetSpread(weaponProperty.data.spread); + pWeaponInfo->SetAimOffsetIndex(weaponProperty.data.aim_offset); + pWeaponInfo->SetDefaultCombo(weaponProperty.data.default_combo); + pWeaponInfo->SetCombosAvailable(weaponProperty.data.combos_available); } } diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp index f056360de43..ef53e291e5a 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp @@ -314,6 +314,7 @@ IMPLEMENT_ENUM_BEGIN(eWeaponSkill) ADD_ENUM(WEAPONSKILL_POOR, "poor") ADD_ENUM(WEAPONSKILL_STD, "std") ADD_ENUM(WEAPONSKILL_PRO, "pro") +ADD_ENUM(WEAPONSKILL_SPECIAL, "special") IMPLEMENT_ENUM_END("weapon-skill") IMPLEMENT_ENUM_BEGIN(ERenderFormat) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp index c7f52ff0e4f..c390b1bd2c9 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp @@ -2119,8 +2119,22 @@ int CLuaPedDefs::SetPedStat(lua_State* luaVM) if (!argStream.HasErrors()) { // Check the stat and value - if (usStat > NUM_PLAYER_STATS - 1 || fValue < 0.0f || fValue > 1000.0f) - argStream.SetCustomError("Stat must be 0 to 342 and value must be 0 to 1000."); + if (usStat > NUM_PLAYER_STATS - 1) + { + argStream.SetCustomError("Stat must be 0 to 342 and value must be 0 to 5000."); + } + else if (fValue < 0.0f) + { + argStream.SetCustomError("Value must be greater than 0."); + } + else if ((usStat >= WEAPONTYPE_PISTOL_SKILL && usStat <= WEAPONTYPE_SNIPERRIFLE_SKILL) && fValue > 5000.0f) + { + argStream.SetCustomError("Value must be at most 5000 (for weapon stats)."); + } + else if ((usStat < WEAPONTYPE_PISTOL_SKILL || usStat > WEAPONTYPE_SNIPERRIFLE_SKILL) && fValue > 1000.0f) + { + argStream.SetCustomError("Value must be at most 1000 (for non-weapon stats)."); + } else if (CStaticFunctionDefinitions::SetPedStat(*pEntity, usStat, fValue)) { lua_pushboolean(luaVM, true); diff --git a/Client/multiplayer_sa/CMultiplayerSA.cpp b/Client/multiplayer_sa/CMultiplayerSA.cpp index d6e82139cde..cdb7e73486a 100644 --- a/Client/multiplayer_sa/CMultiplayerSA.cpp +++ b/Client/multiplayer_sa/CMultiplayerSA.cpp @@ -6105,40 +6105,54 @@ eWeaponType weaponSkillWeapon; BYTE weaponSkill; bool CPed_GetWeaponSkill() { + if (weaponSkillWeapon < WEAPONTYPE_PISTOL || weaponSkillWeapon > WEAPONTYPE_TEC9) + { + return false; + } + SClientEntity* pPedClientEntity = pGameInterface->GetPools()->GetPed((DWORD*)weaponSkillPed); CPed* pPed = pPedClientEntity ? pPedClientEntity->pEntity : nullptr; - if (pPed) + if (!pPed) + { + return false; + } + + CPlayerPed* playerPed = dynamic_cast(pPed); + if (!playerPed) + { + return false; + } + + unsigned short skillStatIdx = pGameInterface->GetStats()->GetSkillStatIndex(weaponSkillWeapon); + float stat; + CPed* pLocalPlayerPed = pGameInterface->GetPools()->GetPedFromRef((DWORD)1); + if (pPed == pLocalPlayerPed) { - CPed* pLocalPlayerPed = pGameInterface->GetPools()->GetPedFromRef((DWORD)1); - if (pPed != pLocalPlayerPed) + stat = pGameInterface->GetStats()->GetStatValue(skillStatIdx); + } + else + { + CRemoteDataStorageSA* data = CRemoteDataSA::GetRemoteDataStorage(playerPed); + if (!data) { - if (weaponSkillWeapon >= WEAPONTYPE_PISTOL && weaponSkillWeapon <= WEAPONTYPE_TEC9) - { - CPlayerPed* playerPed = dynamic_cast(pPed); - if (playerPed) - { - CRemoteDataStorageSA* data = CRemoteDataSA::GetRemoteDataStorage(playerPed); - if (data) - { - float stat = data->m_stats.StatTypesFloat[pGameInterface->GetStats()->GetSkillStatIndex(weaponSkillWeapon)]; - - CWeaponInfo* pPoor = pGameInterface->GetWeaponInfo(weaponSkillWeapon, WEAPONSKILL_POOR); - CWeaponInfo* pStd = pGameInterface->GetWeaponInfo(weaponSkillWeapon, WEAPONSKILL_STD); - CWeaponInfo* pPro = pGameInterface->GetWeaponInfo(weaponSkillWeapon, WEAPONSKILL_PRO); - - if (stat >= pPro->GetRequiredStatLevel()) - weaponSkill = WEAPONSKILL_PRO; - else if (stat >= pStd->GetRequiredStatLevel()) - weaponSkill = WEAPONSKILL_STD; - else - weaponSkill = WEAPONSKILL_POOR; - return true; - } - } - } + return false; } + stat = data->m_stats.StatTypesFloat[skillStatIdx]; } - return false; + + CWeaponInfo* pPoor = pGameInterface->GetWeaponInfo(weaponSkillWeapon, WEAPONSKILL_POOR); + CWeaponInfo* pStd = pGameInterface->GetWeaponInfo(weaponSkillWeapon, WEAPONSKILL_STD); + CWeaponInfo* pPro = pGameInterface->GetWeaponInfo(weaponSkillWeapon, WEAPONSKILL_PRO); + CWeaponInfo* pSpec = pGameInterface->GetWeaponInfo(weaponSkillWeapon, WEAPONSKILL_SPECIAL); + if (stat >= pSpec->GetRequiredStatLevel()) + weaponSkill = WEAPONSKILL_SPECIAL; + else if (stat >= pPro->GetRequiredStatLevel()) + weaponSkill = WEAPONSKILL_PRO; + else if (stat >= pStd->GetRequiredStatLevel()) + weaponSkill = WEAPONSKILL_STD; + else + weaponSkill = WEAPONSKILL_POOR; + return true; } static void __declspec(naked) HOOK_CPed_GetWeaponSkill() diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 810820a820e..5afa1630ee0 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -2497,6 +2497,11 @@ bool CStaticFunctionDefinitions::SetWeaponProperty(eWeaponProperty eProperty, eW pWeaponInfo->ToggleFlagBits(sData); break; } + case WEAPON_ANIM_GROUP: + { + pWeaponInfo->SetAnimGroup(sData); + break; + } default: return false; } @@ -3948,47 +3953,61 @@ bool CStaticFunctionDefinitions::SetPedStat(CElement* pElement, unsigned short u assert(pElement); // Check the stat - if (usStat < NUM_PLAYER_STATS && fValue >= 0.0f && fValue <= 1000.0f) + if (usStat > NUM_PLAYER_STATS - 1) + { + return false; + } + if (fValue < 0.0f) + { + return false; + } + if ((usStat >= 69 /* WEAPONTYPE_PISTOL_SKILL */ && usStat <= 79 /* WEAPONTYPE_SNIPERRIFLE_SKILL */) && fValue > 5000.0f) + { + return false; + } + if ((usStat < 69 /* WEAPONTYPE_PISTOL_SKILL */ || usStat > 79 /* WEAPONTYPE_SNIPERRIFLE_SKILL */) && fValue > 1000.0f) { - RUN_CHILDREN(SetPedStat(*iter, usStat, fValue)) + return false; + } - if (IS_PLAYER(pElement)) - { - CPlayer* pPlayer = static_cast(pElement); + RUN_CHILDREN(SetPedStat(*iter, usStat, fValue)) - // Dont let them set visual stats if they dont have the CJ model - if ((usStat != 21 /* FAT */ && usStat != 23 /* BODY_MUSCLE */) || pPlayer->GetModel() == 0) - { - // Save the stat - pPlayer->SetPlayerStat(usStat, fValue); + if (IS_PLAYER(pElement)) + { + CPlayer* pPlayer = static_cast(pElement); - // Notify everyone - CPlayerStatsPacket Packet; - Packet.SetSourceElement(pPlayer); - Packet.Add(usStat, fValue); - m_pPlayerManager->BroadcastOnlyJoined(Packet); + // Dont let them set visual stats if they dont have the CJ model + if ((usStat != 21 /* FAT */ && usStat != 23 /* BODY_MUSCLE */) || pPlayer->GetModel() == 0) + { + // Save the stat + pPlayer->SetPlayerStat(usStat, fValue); - return true; - } + // Notify everyone + CPlayerStatsPacket Packet; + Packet.SetSourceElement(pPlayer); + Packet.Add(usStat, fValue); + m_pPlayerManager->BroadcastOnlyJoined(Packet); + + return true; } - else if (IS_PED(pElement)) - { - CPed* pPed = static_cast(pElement); + } + else if (IS_PED(pElement)) + { + CPed* pPed = static_cast(pElement); - // Dont let them set visual stats if they dont have the CJ model - if ((usStat != 21 /* FAT */ && usStat != 23 /* BODY_MUSCLE */) || pPed->GetModel() == 0) - { - // Save the stat - pPed->SetPlayerStat(usStat, fValue); + // Dont let them set visual stats if they dont have the CJ model + if ((usStat != 21 /* FAT */ && usStat != 23 /* BODY_MUSCLE */) || pPed->GetModel() == 0) + { + // Save the stat + pPed->SetPlayerStat(usStat, fValue); - // Notify everyone - CPlayerStatsPacket Packet; - Packet.SetSourceElement(pPed); - Packet.Add(usStat, fValue); - m_pPlayerManager->BroadcastOnlyJoined(Packet); + // Notify everyone + CPlayerStatsPacket Packet; + Packet.SetSourceElement(pPed); + Packet.Add(usStat, fValue); + m_pPlayerManager->BroadcastOnlyJoined(Packet); - return true; - } + return true; } } diff --git a/Server/mods/deathmatch/logic/CWeaponStatManager.cpp b/Server/mods/deathmatch/logic/CWeaponStatManager.cpp index dd42aae5828..209e16497db 100644 --- a/Server/mods/deathmatch/logic/CWeaponStatManager.cpp +++ b/Server/mods/deathmatch/logic/CWeaponStatManager.cpp @@ -15,6 +15,7 @@ SFixedArray CWeaponStatManager::OriginalPoorWeaponData; SFixedArray CWeaponStatManager::OriginalNormalWeaponData; SFixedArray CWeaponStatManager::OriginalHitmanWeaponData; +SFixedArray CWeaponStatManager::OriginalSpecialWeaponData; CWeaponStatManager::CWeaponStatManager() { Init(); @@ -36,7 +37,7 @@ CWeaponStatManager::CWeaponStatManager() LoadDefault(pWeaponStat, weaponType); } - for (int skill = 0; skill < 3; skill++) + for (int skill = 0; skill < WEAPONSKILL_MAX_NUMBER; skill++) { for (int i = 0; i < NUM_WeaponInfosOtherSkill; i++) { @@ -89,21 +90,17 @@ CWeaponStat* CWeaponStatManager::GetWeaponStatsFromSkillLevel(eWeaponType type, CWeaponStat* pPoor = GetWeaponStats(type, WEAPONSKILL_POOR); CWeaponStat* pStd = GetWeaponStats(type, WEAPONSKILL_STD); CWeaponStat* pPro = GetWeaponStats(type, WEAPONSKILL_PRO); - if (pStd) - { - if (pPoor && pPro) - { - if (fSkillLevel >= pPro->GetRequiredStatLevel()) - return pPro; - else if (fSkillLevel >= pStd->GetRequiredStatLevel()) - return pStd; - else - return pPoor; - } - else - return pStd; - } - return NULL; + CWeaponStat* pSpec = GetWeaponStats(type, WEAPONSKILL_SPECIAL); + if (pSpec && fSkillLevel >= pSpec->GetRequiredStatLevel()) + return pSpec; + else if (pPro && fSkillLevel >= pPro->GetRequiredStatLevel()) + return pPro; + else if (pStd && fSkillLevel >= pStd->GetRequiredStatLevel()) + return pStd; + else if (pPoor && fSkillLevel >= pPoor->GetRequiredStatLevel()) + return pPoor; + else + return pStd; } CWeaponStat* CWeaponStatManager::GetOriginalWeaponStats(eWeaponType type, eWeaponSkill skill) @@ -1475,6 +1472,71 @@ void CWeaponStatManager::Init() OriginalHitmanWeaponData[32].aim_offset = 4; OriginalHitmanWeaponData[32].default_combo = 4; OriginalHitmanWeaponData[32].combos_available = 1; + + // 22 - Colt 45 Stat: 3 + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].weapon_range = 35.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].target_range = 30.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].accuracy = 1.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].damage = 25; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].life_span = 0.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].firing_speed = 0.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].spread = 0.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].maximum_clip_ammo = 17; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].move_speed = 1.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].flags = 28721; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim_group = 14; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].fire_type = (eFireType)1; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].model = 346; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].model2 = -1; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].weapon_slot = (eWeaponSlot)2; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].fire_offset = CVector(0.25f, 0.050000000745058f, 0.090000003576279f); + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].skill_level = (eWeaponSkill)3; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].required_skill_level = 5000; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim_loop_start = 0.20000001788139f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim_loop_stop = 0.49399998784065f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim_loop_bullet_fire = 0.20000001788139f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim2_loop_start = 0.20000001788139f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim2_loop_stop = 0.49399998784065f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim2_loop_bullet_fire = 0.20000001788139f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].anim_breakout_time = 3.3000001907349f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].radius = 0.0f; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].aim_offset = 2; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].default_combo = 4; + OriginalSpecialWeaponData[WEAPONTYPE_PISTOL].combos_available = 1; + + for (int i = WEAPONTYPE_PISTOL + 1; i <= WEAPONTYPE_EXTINGUISHER; ++i) + { + OriginalSpecialWeaponData[i].weapon_range = OriginalHitmanWeaponData[i].weapon_range; + OriginalSpecialWeaponData[i].target_range = OriginalHitmanWeaponData[i].target_range; + OriginalSpecialWeaponData[i].accuracy = OriginalHitmanWeaponData[i].accuracy; + OriginalSpecialWeaponData[i].damage = OriginalHitmanWeaponData[i].damage; + OriginalSpecialWeaponData[i].life_span = OriginalHitmanWeaponData[i].life_span; + OriginalSpecialWeaponData[i].firing_speed = OriginalHitmanWeaponData[i].firing_speed; + OriginalSpecialWeaponData[i].spread = OriginalHitmanWeaponData[i].spread; + OriginalSpecialWeaponData[i].maximum_clip_ammo = OriginalHitmanWeaponData[i].maximum_clip_ammo; + OriginalSpecialWeaponData[i].move_speed = OriginalHitmanWeaponData[i].move_speed; + OriginalSpecialWeaponData[i].flags = OriginalHitmanWeaponData[i].flags; + OriginalSpecialWeaponData[i].anim_group = OriginalHitmanWeaponData[i].anim_group; + OriginalSpecialWeaponData[i].fire_type = OriginalHitmanWeaponData[i].fire_type; + OriginalSpecialWeaponData[i].model = OriginalHitmanWeaponData[i].model; + OriginalSpecialWeaponData[i].model2 = OriginalHitmanWeaponData[i].model2; + OriginalSpecialWeaponData[i].weapon_slot = OriginalHitmanWeaponData[i].weapon_slot; + OriginalSpecialWeaponData[i].fire_offset = OriginalHitmanWeaponData[i].fire_offset; + OriginalSpecialWeaponData[i].skill_level = (eWeaponSkill)3; + OriginalSpecialWeaponData[i].required_skill_level = 5000; + OriginalSpecialWeaponData[i].anim_loop_start = OriginalHitmanWeaponData[i].anim_loop_start; + OriginalSpecialWeaponData[i].anim_loop_stop = OriginalHitmanWeaponData[i].anim_loop_stop; + OriginalSpecialWeaponData[i].anim_loop_bullet_fire = OriginalHitmanWeaponData[i].anim_loop_bullet_fire; + OriginalSpecialWeaponData[i].anim2_loop_start = OriginalHitmanWeaponData[i].anim2_loop_start; + OriginalSpecialWeaponData[i].anim2_loop_stop = OriginalHitmanWeaponData[i].anim2_loop_stop; + OriginalSpecialWeaponData[i].anim2_loop_bullet_fire = OriginalHitmanWeaponData[i].anim2_loop_bullet_fire; + OriginalSpecialWeaponData[i].anim_breakout_time = OriginalHitmanWeaponData[i].anim_breakout_time; + OriginalSpecialWeaponData[i].radius = OriginalHitmanWeaponData[i].radius; + OriginalSpecialWeaponData[i].aim_offset = OriginalHitmanWeaponData[i].aim_offset; + OriginalSpecialWeaponData[i].default_combo = OriginalHitmanWeaponData[i].default_combo; + OriginalSpecialWeaponData[i].combos_available = OriginalHitmanWeaponData[i].combos_available; + } + // End of Skill Level Weapons } @@ -1521,6 +1583,40 @@ bool CWeaponStatManager::LoadDefault(CWeaponStat* pDest, eWeaponType weaponType, pDest->SetCombosAvailable(OriginalPoorWeaponData[iVal].combos_available); break; } + case WEAPONSKILL_STD: + { + pDest->SetWeaponSkillLevel(weaponSkill); + pDest->SetWeaponRange(OriginalNormalWeaponData[iVal].weapon_range); + pDest->SetTargetRange(OriginalNormalWeaponData[iVal].target_range); + pDest->SetAccuracy(OriginalNormalWeaponData[iVal].accuracy); + pDest->SetDamagePerHit(OriginalNormalWeaponData[iVal].damage); + pDest->SetLifeSpan(OriginalNormalWeaponData[iVal].life_span); + pDest->SetFiringSpeed(OriginalNormalWeaponData[iVal].firing_speed); + pDest->SetSpread(OriginalNormalWeaponData[iVal].spread); + pDest->SetMaximumClipAmmo(OriginalNormalWeaponData[iVal].maximum_clip_ammo); + pDest->SetMoveSpeed(OriginalNormalWeaponData[iVal].move_speed); + pDest->SetFlags(OriginalNormalWeaponData[iVal].flags); + pDest->SetAnimGroup(OriginalNormalWeaponData[iVal].anim_group); + pDest->SetFireType(OriginalNormalWeaponData[iVal].fire_type); + pDest->SetModel(OriginalNormalWeaponData[iVal].model); + pDest->SetModel2(OriginalNormalWeaponData[iVal].model2); + pDest->SetSlot(OriginalNormalWeaponData[iVal].weapon_slot); + pDest->SetFireOffset(&OriginalNormalWeaponData[iVal].fire_offset); + pDest->SetSkill(OriginalNormalWeaponData[iVal].skill_level); + pDest->SetRequiredStatLevel(OriginalNormalWeaponData[iVal].required_skill_level); + pDest->SetWeaponAnimLoopStart(OriginalNormalWeaponData[iVal].anim_loop_start); + pDest->SetWeaponAnimLoopStop(OriginalNormalWeaponData[iVal].anim_loop_stop); + pDest->SetWeaponAnimLoopFireTime(OriginalNormalWeaponData[iVal].anim_loop_bullet_fire); + pDest->SetWeaponAnim2LoopStart(OriginalNormalWeaponData[iVal].anim2_loop_start); + pDest->SetWeaponAnim2LoopStop(OriginalNormalWeaponData[iVal].anim2_loop_stop); + pDest->SetWeaponAnim2LoopFireTime(OriginalNormalWeaponData[iVal].anim2_loop_bullet_fire); + pDest->SetWeaponAnimBreakoutTime(OriginalNormalWeaponData[iVal].anim_breakout_time); + pDest->SetWeaponRadius(OriginalNormalWeaponData[iVal].radius); + pDest->SetAimOffsetIndex(OriginalNormalWeaponData[iVal].aim_offset); + pDest->SetDefaultCombo(OriginalNormalWeaponData[iVal].default_combo); + pDest->SetCombosAvailable(OriginalNormalWeaponData[iVal].combos_available); + break; + } case WEAPONSKILL_PRO: { pDest->SetWeaponSkillLevel(weaponSkill); @@ -1555,38 +1651,38 @@ bool CWeaponStatManager::LoadDefault(CWeaponStat* pDest, eWeaponType weaponType, pDest->SetCombosAvailable(OriginalHitmanWeaponData[iVal].combos_available); break; } - case WEAPONSKILL_STD: + case WEAPONSKILL_SPECIAL: { pDest->SetWeaponSkillLevel(weaponSkill); - pDest->SetWeaponRange(OriginalNormalWeaponData[iVal].weapon_range); - pDest->SetTargetRange(OriginalNormalWeaponData[iVal].target_range); - pDest->SetAccuracy(OriginalNormalWeaponData[iVal].accuracy); - pDest->SetDamagePerHit(OriginalNormalWeaponData[iVal].damage); - pDest->SetLifeSpan(OriginalNormalWeaponData[iVal].life_span); - pDest->SetFiringSpeed(OriginalNormalWeaponData[iVal].firing_speed); - pDest->SetSpread(OriginalNormalWeaponData[iVal].spread); - pDest->SetMaximumClipAmmo(OriginalNormalWeaponData[iVal].maximum_clip_ammo); - pDest->SetMoveSpeed(OriginalNormalWeaponData[iVal].move_speed); - pDest->SetFlags(OriginalNormalWeaponData[iVal].flags); - pDest->SetAnimGroup(OriginalNormalWeaponData[iVal].anim_group); - pDest->SetFireType(OriginalNormalWeaponData[iVal].fire_type); - pDest->SetModel(OriginalNormalWeaponData[iVal].model); - pDest->SetModel2(OriginalNormalWeaponData[iVal].model2); - pDest->SetSlot(OriginalNormalWeaponData[iVal].weapon_slot); - pDest->SetFireOffset(&OriginalNormalWeaponData[iVal].fire_offset); - pDest->SetSkill(OriginalNormalWeaponData[iVal].skill_level); - pDest->SetRequiredStatLevel(OriginalNormalWeaponData[iVal].required_skill_level); - pDest->SetWeaponAnimLoopStart(OriginalNormalWeaponData[iVal].anim_loop_start); - pDest->SetWeaponAnimLoopStop(OriginalNormalWeaponData[iVal].anim_loop_stop); - pDest->SetWeaponAnimLoopFireTime(OriginalNormalWeaponData[iVal].anim_loop_bullet_fire); - pDest->SetWeaponAnim2LoopStart(OriginalNormalWeaponData[iVal].anim2_loop_start); - pDest->SetWeaponAnim2LoopStop(OriginalNormalWeaponData[iVal].anim2_loop_stop); - pDest->SetWeaponAnim2LoopFireTime(OriginalNormalWeaponData[iVal].anim2_loop_bullet_fire); - pDest->SetWeaponAnimBreakoutTime(OriginalNormalWeaponData[iVal].anim_breakout_time); - pDest->SetWeaponRadius(OriginalNormalWeaponData[iVal].radius); - pDest->SetAimOffsetIndex(OriginalNormalWeaponData[iVal].aim_offset); - pDest->SetDefaultCombo(OriginalNormalWeaponData[iVal].default_combo); - pDest->SetCombosAvailable(OriginalNormalWeaponData[iVal].combos_available); + pDest->SetWeaponRange(OriginalSpecialWeaponData[iVal].weapon_range); + pDest->SetTargetRange(OriginalSpecialWeaponData[iVal].target_range); + pDest->SetAccuracy(OriginalSpecialWeaponData[iVal].accuracy); + pDest->SetDamagePerHit(OriginalSpecialWeaponData[iVal].damage); + pDest->SetLifeSpan(OriginalSpecialWeaponData[iVal].life_span); + pDest->SetFiringSpeed(OriginalSpecialWeaponData[iVal].firing_speed); + pDest->SetSpread(OriginalSpecialWeaponData[iVal].spread); + pDest->SetMaximumClipAmmo(OriginalSpecialWeaponData[iVal].maximum_clip_ammo); + pDest->SetMoveSpeed(OriginalSpecialWeaponData[iVal].move_speed); + pDest->SetFlags(OriginalSpecialWeaponData[iVal].flags); + pDest->SetAnimGroup(OriginalSpecialWeaponData[iVal].anim_group); + pDest->SetFireType(OriginalSpecialWeaponData[iVal].fire_type); + pDest->SetModel(OriginalSpecialWeaponData[iVal].model); + pDest->SetModel2(OriginalSpecialWeaponData[iVal].model2); + pDest->SetSlot(OriginalSpecialWeaponData[iVal].weapon_slot); + pDest->SetFireOffset(&OriginalSpecialWeaponData[iVal].fire_offset); + pDest->SetSkill(OriginalSpecialWeaponData[iVal].skill_level); + pDest->SetRequiredStatLevel(OriginalSpecialWeaponData[iVal].required_skill_level); + pDest->SetWeaponAnimLoopStart(OriginalSpecialWeaponData[iVal].anim_loop_start); + pDest->SetWeaponAnimLoopStop(OriginalSpecialWeaponData[iVal].anim_loop_stop); + pDest->SetWeaponAnimLoopFireTime(OriginalSpecialWeaponData[iVal].anim_loop_bullet_fire); + pDest->SetWeaponAnim2LoopStart(OriginalSpecialWeaponData[iVal].anim2_loop_start); + pDest->SetWeaponAnim2LoopStop(OriginalSpecialWeaponData[iVal].anim2_loop_stop); + pDest->SetWeaponAnim2LoopFireTime(OriginalSpecialWeaponData[iVal].anim2_loop_bullet_fire); + pDest->SetWeaponAnimBreakoutTime(OriginalSpecialWeaponData[iVal].anim_breakout_time); + pDest->SetWeaponRadius(OriginalSpecialWeaponData[iVal].radius); + pDest->SetAimOffsetIndex(OriginalSpecialWeaponData[iVal].aim_offset); + pDest->SetDefaultCombo(OriginalSpecialWeaponData[iVal].default_combo); + pDest->SetCombosAvailable(OriginalSpecialWeaponData[iVal].combos_available); break; } default: diff --git a/Server/mods/deathmatch/logic/CWeaponStatManager.h b/Server/mods/deathmatch/logic/CWeaponStatManager.h index 9aa23e696c7..2349c4a34bc 100644 --- a/Server/mods/deathmatch/logic/CWeaponStatManager.h +++ b/Server/mods/deathmatch/logic/CWeaponStatManager.h @@ -99,4 +99,5 @@ class CWeaponStatManager static SFixedArray OriginalPoorWeaponData; static SFixedArray OriginalNormalWeaponData; static SFixedArray OriginalHitmanWeaponData; + static SFixedArray OriginalSpecialWeaponData; }; diff --git a/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp index a46958aa479..71d509458ac 100644 --- a/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp +++ b/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp @@ -229,6 +229,7 @@ IMPLEMENT_ENUM_BEGIN(eWeaponSkill) ADD_ENUM(WEAPONSKILL_POOR, "poor") ADD_ENUM(WEAPONSKILL_STD, "std") ADD_ENUM(WEAPONSKILL_PRO, "pro") +ADD_ENUM(WEAPONSKILL_SPECIAL, "special") IMPLEMENT_ENUM_END("weapon-skill") IMPLEMENT_ENUM_BEGIN(eWeaponState) diff --git a/Server/mods/deathmatch/logic/luadefs/CLuaFunctionDefs.Weapon.cpp b/Server/mods/deathmatch/logic/luadefs/CLuaFunctionDefs.Weapon.cpp index 19897098aa4..79e66e33456 100644 --- a/Server/mods/deathmatch/logic/luadefs/CLuaFunctionDefs.Weapon.cpp +++ b/Server/mods/deathmatch/logic/luadefs/CLuaFunctionDefs.Weapon.cpp @@ -693,6 +693,7 @@ int CLuaFunctionDefs::SetWeaponProperty(lua_State* luaVM) case WEAPON_DAMAGE: case WEAPON_MAX_CLIP_AMMO: case WEAPON_FLAGS: + case WEAPON_ANIM_GROUP: { int sWeaponInfo = 0; argStream.ReadNumber(sWeaponInfo); diff --git a/Server/mods/deathmatch/logic/packets/CMapInfoPacket.cpp b/Server/mods/deathmatch/logic/packets/CMapInfoPacket.cpp index aceac1b129f..32e29af87e7 100644 --- a/Server/mods/deathmatch/logic/packets/CMapInfoPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CMapInfoPacket.cpp @@ -297,6 +297,23 @@ bool CMapInfoPacket::Write(NetBitStreamInterface& BitStream) const WeaponProperty.data.anim2_loop_bullet_fire = pWeaponStat->GetWeaponAnim2LoopFireTime(); WeaponProperty.data.anim_breakout_time = pWeaponStat->GetWeaponAnimBreakoutTime(); + + WeaponProperty.data.anim_group = pWeaponStat->GetAnimGroup(); + WeaponProperty.data.fire_type = pWeaponStat->GetFireType(); + WeaponProperty.data.model = pWeaponStat->GetModel(); + WeaponProperty.data.model2 = pWeaponStat->GetModel2(); + WeaponProperty.data.weapon_slot = pWeaponStat->GetSlot(); + WeaponProperty.data.fire_offset = *pWeaponStat->GetFireOffset(); + WeaponProperty.data.skill_level = pWeaponStat->GetSkill(); + WeaponProperty.data.required_skill_level = pWeaponStat->GetRequiredStatLevel(); + WeaponProperty.data.firing_speed = pWeaponStat->GetFiringSpeed(); + WeaponProperty.data.radius = pWeaponStat->GetRadius(); + WeaponProperty.data.life_span = pWeaponStat->GetLifeSpan(); + WeaponProperty.data.spread = pWeaponStat->GetSpread(); + WeaponProperty.data.aim_offset = pWeaponStat->GetAimOffsetIndex(); + WeaponProperty.data.default_combo = pWeaponStat->GetDefaultCombo(); + WeaponProperty.data.combos_available = pWeaponStat->GetCombosAvailable(); + BitStream.Write(&WeaponProperty); BitStream.WriteBit(g_pGame->GetJetpackWeaponEnabled((eWeaponType)i)); @@ -306,7 +323,7 @@ bool CMapInfoPacket::Write(NetBitStreamInterface& BitStream) const { sWeaponPropertySync WeaponProperty; BitStream.WriteBit(true); - for (int j = 0; j <= 2; j++) + for (int j = 0; j <= 3; j++) { CWeaponStat* pWeaponStat = g_pGame->GetWeaponStatManager()->GetWeaponStats((eWeaponType)i, (eWeaponSkill)j); WeaponProperty.data.weaponType = (int)pWeaponStat->GetWeaponType(); @@ -327,6 +344,23 @@ bool CMapInfoPacket::Write(NetBitStreamInterface& BitStream) const WeaponProperty.data.anim2_loop_bullet_fire = pWeaponStat->GetWeaponAnim2LoopFireTime(); WeaponProperty.data.anim_breakout_time = pWeaponStat->GetWeaponAnimBreakoutTime(); + + WeaponProperty.data.anim_group = pWeaponStat->GetAnimGroup(); + WeaponProperty.data.fire_type = pWeaponStat->GetFireType(); + WeaponProperty.data.model = pWeaponStat->GetModel(); + WeaponProperty.data.model2 = pWeaponStat->GetModel2(); + WeaponProperty.data.weapon_slot = pWeaponStat->GetSlot(); + WeaponProperty.data.fire_offset = *pWeaponStat->GetFireOffset(); + WeaponProperty.data.skill_level = pWeaponStat->GetSkill(); + WeaponProperty.data.required_skill_level = pWeaponStat->GetRequiredStatLevel(); + WeaponProperty.data.firing_speed = pWeaponStat->GetFiringSpeed(); + WeaponProperty.data.radius = pWeaponStat->GetRadius(); + WeaponProperty.data.life_span = pWeaponStat->GetLifeSpan(); + WeaponProperty.data.spread = pWeaponStat->GetSpread(); + WeaponProperty.data.aim_offset = pWeaponStat->GetAimOffsetIndex(); + WeaponProperty.data.default_combo = pWeaponStat->GetDefaultCombo(); + WeaponProperty.data.combos_available = pWeaponStat->GetCombosAvailable(); + BitStream.Write(&WeaponProperty); } diff --git a/Shared/sdk/net/SyncStructures.h b/Shared/sdk/net/SyncStructures.h index c30cf4ce504..780aa763491 100644 --- a/Shared/sdk/net/SyncStructures.h +++ b/Shared/sdk/net/SyncStructures.h @@ -2378,7 +2378,23 @@ struct sWeaponPropertySync : public ISyncStructure bitStream.Read(data.nAmmo) && bitStream.Read(data.nDamage) && bitStream.Read(data.fAccuracy) && bitStream.Read(data.fMoveSpeed) && bitStream.Read(data.anim_loop_start) && bitStream.Read(data.anim_loop_stop) && bitStream.Read(data.anim_loop_bullet_fire) && bitStream.Read(data.anim2_loop_start) && bitStream.Read(data.anim2_loop_stop) && bitStream.Read(data.anim2_loop_bullet_fire) && - bitStream.Read(data.anim_breakout_time)) + bitStream.Read(data.anim_breakout_time) && + + bitStream.Read(data.anim_group) && + bitStream.Read(data.fire_type) && + bitStream.Read(data.model) && + bitStream.Read(data.model2) && + bitStream.Read(data.weapon_slot) && + bitStream.Read(data.fire_offset.fX) && bitStream.Read(data.fire_offset.fY) && bitStream.Read(data.fire_offset.fZ) && + bitStream.Read(data.skill_level) && + bitStream.Read(data.required_skill_level) && + bitStream.Read(data.firing_speed) && + bitStream.Read(data.radius) && + bitStream.Read(data.life_span) && + bitStream.Read(data.spread) && + bitStream.Read(data.aim_offset) && + bitStream.Read(data.default_combo) && + bitStream.Read(data.combos_available)) return true; return false; @@ -2402,6 +2418,22 @@ struct sWeaponPropertySync : public ISyncStructure bitStream.Write(data.anim2_loop_stop); bitStream.Write(data.anim2_loop_bullet_fire); bitStream.Write(data.anim_breakout_time); + + bitStream.Write(data.anim_group); + bitStream.Write(data.fire_type); + bitStream.Write(data.model); + bitStream.Write(data.model2); + bitStream.Write(data.weapon_slot); + bitStream.Write(data.fire_offset.fX); bitStream.Write(data.fire_offset.fY); bitStream.Write(data.fire_offset.fZ); + bitStream.Write(data.skill_level); + bitStream.Write(data.required_skill_level); + bitStream.Write(data.firing_speed); + bitStream.Write(data.radius); + bitStream.Write(data.life_span); + bitStream.Write(data.spread); + bitStream.Write(data.aim_offset); + bitStream.Write(data.default_combo); + bitStream.Write(data.combos_available); } struct @@ -2427,6 +2459,23 @@ struct sWeaponPropertySync : public ISyncStructure FLOAT anim2_loop_bullet_fire; // time in animation2 when weapon should be fired FLOAT anim_breakout_time; // time after which player can break out of attack and run off + + DWORD anim_group; + + DWORD fire_type; + int model; + int model2; + DWORD weapon_slot; + CVector fire_offset; + BYTE skill_level; + int required_skill_level; + float firing_speed; + float radius; + float life_span; + float spread; + short aim_offset; + BYTE default_combo; + BYTE combos_available; } data; };