Soldier
Module: soldier.lua
Overview
The Soldier module is the death handler for AI foot soldiers: when one dies, it decides whether to drop a pickup and which kind (ammo / grenades / health), based on the soldier’s class labels, a one-in-three throttle, and the players’ current ammo/health. This is the system that makes enemies “conveniently” drop what you’re low on.
Inheritance
- Inherits from:
none — base/utility module - Imports:
MrxUtil— imported but not referenced in this file.
Instance pattern
This is a stateless manager/utility module with no per-instance tables. It tracks the following key fields:
deathCount: a module-level counter (local, but persists across calls since it’s declared outside any function), incremented on everyOnDeathcall regardless of soldier type or whether a pickup spawns.tDrops: declared (tDrops[0] = "Ammo Pickup (Bullet)",tDrops[1] = "Ammo Pickup (Rocket)") but never read anywhere in this file —OnDeathpickssPickupvia hardcoded string literals instead. Dead/unused table, likely leftover from an earlier version of the logic.tEvents: also declared (local tEvents = tEvents or {}) and also never referenced again anywhere in the file — same story, unused leftover.
Functions
OnDeath(uGuid, iArg)
Called when a soldier instance dies. Increments deathCount unconditionally, then bails out early (no pickup) if Object.InSeat(uGuid) is true. Pickup logic only runs when Object.HasLabel(uGuid, "HeavySoldier") is true or deathCount is an exact multiple of 3 (deathCount / 3 == math.floor(deathCount / 3)):
- Default pickup is
"Ammo Pickup (Small)". - Upgraded to
"Ammo Pickup (Rocket)"if the soldier has labelRocketSoldierand notMGSoldier(theMGSoldiercheck suppresses the rocket-ammo drop for that combined label case). - Upgraded to
"Ammo Pickup (Bullet)"if the soldier has labelHeavySoldier(checked after the Rocket/MG case, soRocketSoldierwithoutMGSoldierwins if both labels apply). - Then, for every player in
Player.GetAllPlayers(): if any of that player’s weapons is labeled"Grenade"and itsWeapon.GetReserveAmmo(...)is under a random threshold (math.randf(8)), pickup becomes"Ammo Pickup (Grenades)". Independently, if a random roll (math.randf() * 80) exceeds that player’s current health, pickup becomes"Health Pickup". Both checks run per-player in the same loop and the last matching player’s outcome wins (loop keeps overwritingsPickup, nobreak). - Note:
math.floor/math.randfhere are the standard Luamathlibrary (lowercase), not the engine’sMathnamespace (capitalized) used in other modules likerandomlyteleportplayer.lua— both exist, this file just uses the lowercase one throughout. - Spawns the chosen pickup via
Pg.Spawn(sPickup, x, y + 0.1, z, 0, false, true, uGuid), applies a small downward impulse (Object.ApplyImpulse(uNewGuid, 0, -0.5, 0)), and adds it to the"pickup"disposer.
Events
This file contains no Event.* calls at all. OnDeath is invoked directly by the engine as a naming-convention callback (same mechanism as OnActivate/OnDeath elsewhere), not through an Event.Create registration in this file.
Module constants & tunables
All of these are hardcoded inside OnDeath (there is no config table — tDrops is dead code):
- Pickup templates:
"Ammo Pickup (Small)"(default),"Ammo Pickup (Rocket)","Ammo Pickup (Bullet)","Ammo Pickup (Grenades)","Health Pickup". Spawned viaPg.Spawnat the corpse position, nudged down by an impulse, and added to the"pickup"disposer. - Drop throttle: a pickup is only considered when the soldier has label
"HeavySoldier"or every 3rd soldier death (deathCount / 3 == math.floor(deathCount / 3)). Change the3to make drops more/less frequent. - Class → ammo mapping (checked via
Object.HasLabel):RocketSoldier(and notMGSoldier) → rocket ammo;HeavySoldier→ bullet ammo. - Grenade top-up: if a player holds a
"Grenade"-labelled weapon whoseWeapon.GetReserveAmmois belowmath.randf(8), drop grenades. - Health top-up: if
math.randf() * 80 > Object.GetHealth(uHero)(roughly: the lower a player’s health, the likelier), drop a health pickup — this overrides the ammo choice. - Soldiers that die while seated (
Object.InSeat) drop nothing.
Notes for modders
- Retune drops by editing the throttle
3, the label→pickup mapping, and the two random thresholds (math.randf(8),math.randf() * 80) directly inOnDeath. tDropsandtEventsare dead code — populated/declared but never consulted. EditingtDropswill not change pickup selection; the strings are inline.- This file uses the standard Lua
math.*library (lowercase), not the engineMath.*namespace — keep that in mind if you copy logic between modules.