#ifdef PRECOMPILEDHEADERS #include "Tactical All.h" #else #include "builddefines.h" #include #include #include #include "wcheck.h" #include "Render Fun.h" #include "stdlib.h" #include "debug.h" #include "MemMan.h" #include "Overhead Types.h" #include "Soldier Control.h" #include "Animation Cache.h" #include "Animation Data.h" #include "Animation Control.h" #include "container.h" #include #include "pathai.h" #include "Random.h" #include "worldman.h" #include "Isometric Utils.h" #include "Render Dirty.h" #include "renderworld.h" #include "sys globals.h" #include "video.h" #include "points.h" #include "Win util.h" #include "Sound Control.h" #include "weapons.h" #include "vobject_blitters.h" #include "Handle UI.h" #include "soldier ani.h" #include "Event pump.h" #include "opplist.h" #include "ai.h" #include "interface.h" #include "lighting.h" #include "faces.h" #include "Soldier Profile.h" #include "gap.h" #include "interface panels.h" #include "campaign.h" #include "soldier macros.h" #include "english.h" #include "Squads.h" #ifdef NETWORKED #include "Networking.h" #include "NetworkEvent.h" #endif #include "structure wrap.h" #include "items.h" #include "Soundman.h" #include "utilities.h" #include "Strategic.h" #include "soldier tile.h" #include "Smell.h" #include "Keys.h" #include "dialogue control.h" #include "soldier functions.h" #include "rt time defines.h" #include "Exit Grids.h" #include "gamescreen.h" #include "Quests.h" #include "message.h" #include "NPC.h" #include "SkillCheck.h" #include "handle doors.h" #include "interface dialogue.h" #include "smokeeffects.h" #include "GameSettings.h" #include "tile animation.h" #include "ShopKeeper Interface.h" #include "Arms Dealer Init.h" #include "vehicles.h" #include "rotting corpses.h" #include "Interface Control.h" #include "strategicmap.h" #include "morale.h" #include "meanwhile.h" #include "drugs and alcohol.h" #include "SkillCheck.h" #include "boxing.h" #include "overhead map.h" #include "Map Information.h" #include "environment.h" #include "Game Clock.h" #include "Explosion Control.h" #include "Buildings.h" #include "Text.h" #include "Strategic Merc Handler.h" #include "Campaign Types.h" #include "Strategic Status.h" #include "civ quotes.h" #endif //turnspeed //UINT8 gubPlayerTurnSpeedUpFactor = 1; //UINT8 gubEnemyTurnSpeedUpFactor = 1; //UINT8 gubCreatureTurnSpeedUpFactor = 1; //UINT8 gubMilitiaTurnSpeedUpFactor = 1; //UINT8 gubCivilianTurnSpeedUpFactor = 1; //turnspeed //extern BOOLEAN fAllowTacticalMilitiaCommand; //lal extern INT16 DirIncrementer[8]; #define PALETTEFILENAME "BINARYDATA\\ja2pal.dat" #define LOW_MORALE_BATTLE_SND_THREASHOLD 35 #define TURNING_FROM_PRONE_OFF 0 #define TURNING_FROM_PRONE_ON 1 #define TURNING_FROM_PRONE_START_UP_FROM_MOVE 2 #define TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE 3 #define MIN_SUBSEQUENT_SNDS_DELAY 2000 // Enumerate extended directions enum { EX_NORTH = 0, EX_NORTHEAST = 4, EX_EAST = 8, EX_SOUTHEAST = 12, EX_SOUTH = 16, EX_SOUTHWEST = 20, EX_WEST = 24, EX_NORTHWEST = 28, EX_NUM_WORLD_DIRECTIONS = 32, EX_DIRECTION_IRRELEVANT } ExtendedWorldDirections; // LUT for conversion from 8-direction to extended direction UINT8 ubExtDirection[] = { EX_NORTH, EX_NORTHEAST, EX_EAST, EX_SOUTHEAST, EX_SOUTH, EX_SOUTHWEST, EX_WEST, EX_NORTHWEST }; UINT8 gExtOneCDirection[ EX_NUM_WORLD_DIRECTIONS ] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, }; typedef struct { CHAR8 zName[20]; UINT8 ubRandomVal; BOOLEAN fPreload; BOOLEAN fBadGuy; BOOLEAN fDontAllowTwoInRow; BOOLEAN fStopDialogue; } BATTLESNDS_STRUCT; BATTLESNDS_STRUCT gBattleSndsData[] = { "ok1", 2, 1, 1, 1, 2, "ok2", 0, 1, 1, 1, 2, "cool", 0, 1, 0, 1, 0, "curse", 0, 1, 1, 1, 0, "hit1", 2, 1, 1, 1, 1, "hit2", 0, 1, 1, 1, 1, "laugh", 0, 1, 1, 1, 0, "attn", 0, 1, 0, 1, 0, "dying", 0, 1, 1, 1, 1, "humm", 0, 0, 0, 1, 1, "noth", 0, 0, 0, 1, 1, "gotit", 0, 0, 0, 1, 1, "lmok1", 2, 1, 0, 1, 2, "lmok2", 0, 1, 0, 1, 2, "lmattn", 0, 1, 0, 1, 0, "locked", 0, 0, 0, 1, 0, "enem", 0, 1, 1, 1, 0, }; // WDS - Clean up inventory handling // ---------------------------------------- // New inventory handling code. // ---------------------------------------- Inventory::Inventory() { slotCnt = NUM_INV_SLOTS; inv.reserve(slotCnt); for (int idx=0; idx < slotCnt; ++idx) { OBJECTTYPE *filler = new OBJECTTYPE; // Use MEMALLOC? inv.push_back(*filler); } clear(); Assert (inv.size() == slotCnt); }; Inventory::Inventory(int slotCount) { slotCnt = slotCount; inv.reserve(slotCnt); for (int idx=0; idx < slotCnt; ++idx) { OBJECTTYPE *filler = new OBJECTTYPE; // Use MEMALLOC? inv.push_back(*filler); } clear(); Assert (inv.size() == slotCnt); }; Inventory::Inventory(const Inventory& src) { inv.reserve(slotCnt); Assert (src.inv.size() == slotCnt); inv = src.inv; Assert (inv.size() == slotCnt); } int Inventory::size() const { Assert (inv.size() == slotCnt); return slotCnt; } // Assignment operator Inventory& Inventory::operator=(const Inventory& src) { // This IF is just for setting breakpoints when trying to figure out inventory item problems. Remove it later if ((src.inv.size() != slotCnt) || (inv.size() != slotCnt)) { int i = 0; // Set BP here if following asserts throw } Assert (src.inv.size() == slotCnt); Assert (inv.size() == slotCnt); if (this != &src) { if (inv.size() == 0) { inv.reserve(slotCnt); for (int idx=0; idx < slotCnt; ++idx) { inv.push_back(src.inv[idx]); } } else { clear(); for (int idx=0; idx < slotCnt; ++idx) { inv[idx] = src.inv[idx]; } } } return *this; } Inventory::~Inventory() { }; OBJECTTYPE& Inventory::operator [] (int idx) { // This IF is just from setting breakpoints when trying to figure out inventory item problems. Remove it later if ((idx < 0) || (idx >= slotCnt) || (inv.size() != slotCnt)) { int i = inv.size(); // Set BP here if following asserts throw } Assert(idx >= 0); Assert(idx < slotCnt); Assert(inv.size() == slotCnt); return inv[idx]; }; void Inventory::clear() { Assert (inv.size() == slotCnt); for (int idx=0; idx < slotCnt; ++idx) { memset(&inv[idx], 0, sizeof(OBJECTTYPE)); } }; // ---------------------------------------- SOLDIERTYPE::SOLDIERTYPE() { bNewItemCount.reserve(inv.size()); bNewItemCycleCount.reserve(inv.size()); for (int idx=0; idx < (int)inv.size(); ++idx) { bNewItemCount.push_back(0); bNewItemCycleCount.push_back(0); } initialize(); Assert(bNewItemCount.size() == inv.size()); Assert(bNewItemCycleCount.size() == inv.size()); // The following are based on the "old" SOLDIERTYPE struct // Remove these later Assert(SIZEOF_SOLDIERTYPE_POD == 2344); Assert(offsetof( SOLDIERTYPE, bFiller ) == 2305); } // Copy Constructor SOLDIERTYPE::SOLDIERTYPE(const SOLDIERTYPE& src) { memcpy(this, &src, SIZEOF_SOLDIERTYPE_POD); inv = src.inv; bNewItemCount = src.bNewItemCount; bNewItemCycleCount = src.bNewItemCycleCount; Assert(bNewItemCount.size() == inv.size()); Assert(bNewItemCycleCount.size() == inv.size()); } // Assignment operator SOLDIERTYPE& SOLDIERTYPE::operator=(const SOLDIERTYPE& src) { if (this != &src) { memcpy(this, &src, SIZEOF_SOLDIERTYPE_POD); inv = src.inv; bNewItemCount = src.bNewItemCount; bNewItemCycleCount = src.bNewItemCycleCount; } return *this; } // Destructor SOLDIERTYPE::~SOLDIERTYPE() { } // Initialize the soldier. // Use this instead of the old method of calling memset! // Note that the constructor does this automatically. void SOLDIERTYPE::initialize() { memset( this, 0, SIZEOF_SOLDIERTYPE_POD); inv.clear(); for (int idx=0; idx < (int)inv.size(); ++idx) { bNewItemCount[idx] = 0; bNewItemCycleCount[idx] = 0; } Assert(bNewItemCount.size() == inv.size()); Assert(bNewItemCycleCount.size() == inv.size()); } // Ugly temporary solution // // These two functions map the "old" style inventory (fixed array of ) to the new (a flexibly sized vector). // If you change names or eliminate some positions or such you need to change these. // Eventually the need for these functions will disappear. void SOLDIERTYPE::CopyOldInventoryToNew() { // Do not use a loop in case the new inventory slots are arranged differently than the old inv[HELMETPOS] = DO_NOT_USE_Inv[OldInventory::HELMETPOS]; inv[VESTPOS] = DO_NOT_USE_Inv[OldInventory::VESTPOS]; inv[LEGPOS] = DO_NOT_USE_Inv[OldInventory::LEGPOS]; inv[HEAD1POS] = DO_NOT_USE_Inv[OldInventory::HEAD1POS]; inv[HEAD2POS] = DO_NOT_USE_Inv[OldInventory::HEAD2POS]; inv[HANDPOS] = DO_NOT_USE_Inv[OldInventory::HANDPOS]; inv[SECONDHANDPOS] = DO_NOT_USE_Inv[OldInventory::SECONDHANDPOS]; inv[BIGPOCK1POS] = DO_NOT_USE_Inv[OldInventory::BIGPOCK1POS]; inv[BIGPOCK2POS] = DO_NOT_USE_Inv[OldInventory::BIGPOCK2POS]; inv[BIGPOCK3POS] = DO_NOT_USE_Inv[OldInventory::BIGPOCK3POS]; inv[BIGPOCK4POS] = DO_NOT_USE_Inv[OldInventory::BIGPOCK4POS]; inv[SMALLPOCK1POS] = DO_NOT_USE_Inv[OldInventory::SMALLPOCK1POS]; inv[SMALLPOCK2POS] = DO_NOT_USE_Inv[OldInventory::SMALLPOCK2POS]; inv[SMALLPOCK3POS] = DO_NOT_USE_Inv[OldInventory::SMALLPOCK3POS]; inv[SMALLPOCK4POS] = DO_NOT_USE_Inv[OldInventory::SMALLPOCK4POS]; inv[SMALLPOCK5POS] = DO_NOT_USE_Inv[OldInventory::SMALLPOCK5POS]; inv[SMALLPOCK6POS] = DO_NOT_USE_Inv[OldInventory::SMALLPOCK6POS]; inv[SMALLPOCK7POS] = DO_NOT_USE_Inv[OldInventory::SMALLPOCK7POS]; inv[SMALLPOCK8POS] = DO_NOT_USE_Inv[OldInventory::SMALLPOCK8POS]; bNewItemCount[OldInventory::HELMETPOS] = DO_NOT_USE_bNewItemCount[HELMETPOS]; bNewItemCount[OldInventory::VESTPOS] = DO_NOT_USE_bNewItemCount[VESTPOS]; bNewItemCount[OldInventory::LEGPOS] = DO_NOT_USE_bNewItemCount[LEGPOS]; bNewItemCount[OldInventory::HEAD1POS] = DO_NOT_USE_bNewItemCount[HEAD1POS]; bNewItemCount[OldInventory::HEAD2POS] = DO_NOT_USE_bNewItemCount[HEAD2POS]; bNewItemCount[OldInventory::HANDPOS] = DO_NOT_USE_bNewItemCount[HANDPOS]; bNewItemCount[OldInventory::SECONDHANDPOS] = DO_NOT_USE_bNewItemCount[SECONDHANDPOS]; bNewItemCount[OldInventory::BIGPOCK1POS] = DO_NOT_USE_bNewItemCount[BIGPOCK1POS]; bNewItemCount[OldInventory::BIGPOCK2POS] = DO_NOT_USE_bNewItemCount[BIGPOCK2POS]; bNewItemCount[OldInventory::BIGPOCK3POS] = DO_NOT_USE_bNewItemCount[BIGPOCK3POS]; bNewItemCount[OldInventory::BIGPOCK4POS] = DO_NOT_USE_bNewItemCount[BIGPOCK4POS]; bNewItemCount[OldInventory::SMALLPOCK1POS] = DO_NOT_USE_bNewItemCount[SMALLPOCK1POS]; bNewItemCount[OldInventory::SMALLPOCK2POS] = DO_NOT_USE_bNewItemCount[SMALLPOCK2POS]; bNewItemCount[OldInventory::SMALLPOCK3POS] = DO_NOT_USE_bNewItemCount[SMALLPOCK3POS]; bNewItemCount[OldInventory::SMALLPOCK4POS] = DO_NOT_USE_bNewItemCount[SMALLPOCK4POS]; bNewItemCount[OldInventory::SMALLPOCK5POS] = DO_NOT_USE_bNewItemCount[SMALLPOCK5POS]; bNewItemCount[OldInventory::SMALLPOCK6POS] = DO_NOT_USE_bNewItemCount[SMALLPOCK6POS]; bNewItemCount[OldInventory::SMALLPOCK7POS] = DO_NOT_USE_bNewItemCount[SMALLPOCK7POS]; bNewItemCount[OldInventory::SMALLPOCK8POS] = DO_NOT_USE_bNewItemCount[SMALLPOCK8POS]; bNewItemCycleCount[OldInventory::HELMETPOS] = DO_NOT_USE_bNewItemCycleCount[HELMETPOS]; bNewItemCycleCount[OldInventory::VESTPOS] = DO_NOT_USE_bNewItemCycleCount[VESTPOS]; bNewItemCycleCount[OldInventory::LEGPOS] = DO_NOT_USE_bNewItemCycleCount[LEGPOS]; bNewItemCycleCount[OldInventory::HEAD1POS] = DO_NOT_USE_bNewItemCycleCount[HEAD1POS]; bNewItemCycleCount[OldInventory::HEAD2POS] = DO_NOT_USE_bNewItemCycleCount[HEAD2POS]; bNewItemCycleCount[OldInventory::HANDPOS] = DO_NOT_USE_bNewItemCycleCount[HANDPOS]; bNewItemCycleCount[OldInventory::SECONDHANDPOS] = DO_NOT_USE_bNewItemCycleCount[SECONDHANDPOS]; bNewItemCycleCount[OldInventory::BIGPOCK1POS] = DO_NOT_USE_bNewItemCycleCount[BIGPOCK1POS]; bNewItemCycleCount[OldInventory::BIGPOCK2POS] = DO_NOT_USE_bNewItemCycleCount[BIGPOCK2POS]; bNewItemCycleCount[OldInventory::BIGPOCK3POS] = DO_NOT_USE_bNewItemCycleCount[BIGPOCK3POS]; bNewItemCycleCount[OldInventory::BIGPOCK4POS] = DO_NOT_USE_bNewItemCycleCount[BIGPOCK4POS]; bNewItemCycleCount[OldInventory::SMALLPOCK1POS] = DO_NOT_USE_bNewItemCycleCount[SMALLPOCK1POS]; bNewItemCycleCount[OldInventory::SMALLPOCK2POS] = DO_NOT_USE_bNewItemCycleCount[SMALLPOCK2POS]; bNewItemCycleCount[OldInventory::SMALLPOCK3POS] = DO_NOT_USE_bNewItemCycleCount[SMALLPOCK3POS]; bNewItemCycleCount[OldInventory::SMALLPOCK4POS] = DO_NOT_USE_bNewItemCycleCount[SMALLPOCK4POS]; bNewItemCycleCount[OldInventory::SMALLPOCK5POS] = DO_NOT_USE_bNewItemCycleCount[SMALLPOCK5POS]; bNewItemCycleCount[OldInventory::SMALLPOCK6POS] = DO_NOT_USE_bNewItemCycleCount[SMALLPOCK6POS]; bNewItemCycleCount[OldInventory::SMALLPOCK7POS] = DO_NOT_USE_bNewItemCycleCount[SMALLPOCK7POS]; bNewItemCycleCount[OldInventory::SMALLPOCK8POS] = DO_NOT_USE_bNewItemCycleCount[SMALLPOCK8POS]; } void SOLDIERTYPE::CopyNewInventoryToOld() { // Do not use a loop in case the new inventory slots are arranged differently than the old DO_NOT_USE_Inv[OldInventory::HELMETPOS] = inv[HELMETPOS]; DO_NOT_USE_Inv[OldInventory::VESTPOS] = inv[VESTPOS]; DO_NOT_USE_Inv[OldInventory::LEGPOS] = inv[LEGPOS]; DO_NOT_USE_Inv[OldInventory::HEAD1POS] = inv[HEAD1POS]; DO_NOT_USE_Inv[OldInventory::HEAD2POS] = inv[HEAD2POS]; DO_NOT_USE_Inv[OldInventory::HANDPOS] = inv[HANDPOS]; DO_NOT_USE_Inv[OldInventory::SECONDHANDPOS] = inv[SECONDHANDPOS]; DO_NOT_USE_Inv[OldInventory::BIGPOCK1POS] = inv[BIGPOCK1POS]; DO_NOT_USE_Inv[OldInventory::BIGPOCK2POS] = inv[BIGPOCK2POS]; DO_NOT_USE_Inv[OldInventory::BIGPOCK3POS] = inv[BIGPOCK3POS]; DO_NOT_USE_Inv[OldInventory::BIGPOCK4POS] = inv[BIGPOCK4POS]; DO_NOT_USE_Inv[OldInventory::SMALLPOCK1POS] = inv[SMALLPOCK1POS]; DO_NOT_USE_Inv[OldInventory::SMALLPOCK2POS] = inv[SMALLPOCK2POS]; DO_NOT_USE_Inv[OldInventory::SMALLPOCK3POS] = inv[SMALLPOCK3POS]; DO_NOT_USE_Inv[OldInventory::SMALLPOCK4POS] = inv[SMALLPOCK4POS]; DO_NOT_USE_Inv[OldInventory::SMALLPOCK5POS] = inv[SMALLPOCK5POS]; DO_NOT_USE_Inv[OldInventory::SMALLPOCK6POS] = inv[SMALLPOCK6POS]; DO_NOT_USE_Inv[OldInventory::SMALLPOCK7POS] = inv[SMALLPOCK7POS]; DO_NOT_USE_Inv[OldInventory::SMALLPOCK8POS] = inv[SMALLPOCK8POS]; DO_NOT_USE_bNewItemCount[OldInventory::HELMETPOS] = bNewItemCount[HELMETPOS]; DO_NOT_USE_bNewItemCount[OldInventory::VESTPOS] = bNewItemCount[VESTPOS]; DO_NOT_USE_bNewItemCount[OldInventory::LEGPOS] = bNewItemCount[LEGPOS]; DO_NOT_USE_bNewItemCount[OldInventory::HEAD1POS] = bNewItemCount[HEAD1POS]; DO_NOT_USE_bNewItemCount[OldInventory::HEAD2POS] = bNewItemCount[HEAD2POS]; DO_NOT_USE_bNewItemCount[OldInventory::HANDPOS] = bNewItemCount[HANDPOS]; DO_NOT_USE_bNewItemCount[OldInventory::SECONDHANDPOS] = bNewItemCount[SECONDHANDPOS]; DO_NOT_USE_bNewItemCount[OldInventory::BIGPOCK1POS] = bNewItemCount[BIGPOCK1POS]; DO_NOT_USE_bNewItemCount[OldInventory::BIGPOCK2POS] = bNewItemCount[BIGPOCK2POS]; DO_NOT_USE_bNewItemCount[OldInventory::BIGPOCK3POS] = bNewItemCount[BIGPOCK3POS]; DO_NOT_USE_bNewItemCount[OldInventory::BIGPOCK4POS] = bNewItemCount[BIGPOCK4POS]; DO_NOT_USE_bNewItemCount[OldInventory::SMALLPOCK1POS] = bNewItemCount[SMALLPOCK1POS]; DO_NOT_USE_bNewItemCount[OldInventory::SMALLPOCK2POS] = bNewItemCount[SMALLPOCK2POS]; DO_NOT_USE_bNewItemCount[OldInventory::SMALLPOCK3POS] = bNewItemCount[SMALLPOCK3POS]; DO_NOT_USE_bNewItemCount[OldInventory::SMALLPOCK4POS] = bNewItemCount[SMALLPOCK4POS]; DO_NOT_USE_bNewItemCount[OldInventory::SMALLPOCK5POS] = bNewItemCount[SMALLPOCK5POS]; DO_NOT_USE_bNewItemCount[OldInventory::SMALLPOCK6POS] = bNewItemCount[SMALLPOCK6POS]; DO_NOT_USE_bNewItemCount[OldInventory::SMALLPOCK7POS] = bNewItemCount[SMALLPOCK7POS]; DO_NOT_USE_bNewItemCount[OldInventory::SMALLPOCK8POS] = bNewItemCount[SMALLPOCK8POS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::HELMETPOS] = bNewItemCycleCount[HELMETPOS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::VESTPOS] = bNewItemCycleCount[VESTPOS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::LEGPOS] = bNewItemCycleCount[LEGPOS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::HEAD1POS] = bNewItemCycleCount[HEAD1POS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::HEAD2POS] = bNewItemCycleCount[HEAD2POS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::HANDPOS] = bNewItemCycleCount[HANDPOS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::SECONDHANDPOS] = bNewItemCycleCount[SECONDHANDPOS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::BIGPOCK1POS] = bNewItemCycleCount[BIGPOCK1POS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::BIGPOCK2POS] = bNewItemCycleCount[BIGPOCK2POS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::BIGPOCK3POS] = bNewItemCycleCount[BIGPOCK3POS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::BIGPOCK4POS] = bNewItemCycleCount[BIGPOCK4POS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::SMALLPOCK1POS] = bNewItemCycleCount[SMALLPOCK1POS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::SMALLPOCK2POS] = bNewItemCycleCount[SMALLPOCK2POS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::SMALLPOCK3POS] = bNewItemCycleCount[SMALLPOCK3POS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::SMALLPOCK4POS] = bNewItemCycleCount[SMALLPOCK4POS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::SMALLPOCK5POS] = bNewItemCycleCount[SMALLPOCK5POS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::SMALLPOCK6POS] = bNewItemCycleCount[SMALLPOCK6POS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::SMALLPOCK7POS] = bNewItemCycleCount[SMALLPOCK7POS]; DO_NOT_USE_bNewItemCycleCount[OldInventory::SMALLPOCK8POS] = bNewItemCycleCount[SMALLPOCK8POS]; } // ---------------------------------------- MERCPROFILESTRUCT::MERCPROFILESTRUCT() { inv.reserve(NUM_INV_SLOTS); bInvStatus.reserve(NUM_INV_SLOTS); bInvNumber.reserve(NUM_INV_SLOTS); for (int idx=0; idx < NUM_INV_SLOTS; ++idx) { inv.push_back(0); bInvStatus.push_back(0); bInvNumber.push_back(0); } initialize(); Assert(inv.size() == NUM_INV_SLOTS); Assert(bInvStatus.size() == NUM_INV_SLOTS); Assert(bInvStatus.size() == NUM_INV_SLOTS); // The following are based on the "old" MERCPROFILESTRUCT struct // Remove these later Assert(SIZEOF_MERCPROFILESTRUCT_POD == 0x2cc); //sizeof(OLD_MERCPROFILESTRUCT)); } // Copy Constructor MERCPROFILESTRUCT::MERCPROFILESTRUCT(const MERCPROFILESTRUCT& src) { memcpy(this, &src, SIZEOF_MERCPROFILESTRUCT_POD); inv = src.inv; bInvStatus = src.bInvStatus; bInvNumber = src.bInvNumber; Assert(inv.size() == NUM_INV_SLOTS); Assert(bInvStatus.size() == NUM_INV_SLOTS); Assert(bInvStatus.size() == NUM_INV_SLOTS); } // Assignment operator MERCPROFILESTRUCT& MERCPROFILESTRUCT::operator=(const MERCPROFILESTRUCT& src) { if (this != &src) { memcpy(this, &src, SIZEOF_MERCPROFILESTRUCT_POD); inv = src.inv; bInvStatus = src.bInvStatus; bInvNumber = src.bInvNumber; } Assert(inv.size() == NUM_INV_SLOTS); Assert(bInvStatus.size() == NUM_INV_SLOTS); Assert(bInvStatus.size() == NUM_INV_SLOTS); return *this; } // Destructor MERCPROFILESTRUCT::~MERCPROFILESTRUCT() { } // Initialize the soldier. // Use this instead of the old method of calling memset! // Note that the constructor does this automatically. void MERCPROFILESTRUCT::initialize() { memset( this, 0, SIZEOF_MERCPROFILESTRUCT_POD); clearInventory(); Assert(inv.size() == NUM_INV_SLOTS); Assert(bInvStatus.size() == NUM_INV_SLOTS); Assert(bInvStatus.size() == NUM_INV_SLOTS); } // Initialize the soldier. // Use this instead of the old method of calling memset! // Note that the constructor does this automatically. void MERCPROFILESTRUCT::clearInventory() { for (int idx=0; idx < (int)inv.size(); ++idx) { inv[idx] = 0; bInvStatus[idx] = 0; bInvNumber[idx] = 0; } Assert(inv.size() == NUM_INV_SLOTS); Assert(bInvStatus.size() == NUM_INV_SLOTS); Assert(bInvStatus.size() == NUM_INV_SLOTS); } // Ugly temporary solution // // These two functions map the "old" style inventory (fixed array of ) to the new (a flexibly sized vector). // If you change names or eliminate some positions or such you need to change these. // Eventually the need for these functions will disappear. void MERCPROFILESTRUCT::CopyOldInventoryToNew() { // Do not use a loop in case the new inventory slots are arranged differently than the old inv[HELMETPOS] = DO_NOT_USE_inv[OldInventory::HELMETPOS]; inv[VESTPOS] = DO_NOT_USE_inv[OldInventory::VESTPOS]; inv[LEGPOS] = DO_NOT_USE_inv[OldInventory::LEGPOS]; inv[HEAD1POS] = DO_NOT_USE_inv[OldInventory::HEAD1POS]; inv[HEAD2POS] = DO_NOT_USE_inv[OldInventory::HEAD2POS]; inv[HANDPOS] = DO_NOT_USE_inv[OldInventory::HANDPOS]; inv[SECONDHANDPOS] = DO_NOT_USE_inv[OldInventory::SECONDHANDPOS]; inv[BIGPOCK1POS] = DO_NOT_USE_inv[OldInventory::BIGPOCK1POS]; inv[BIGPOCK2POS] = DO_NOT_USE_inv[OldInventory::BIGPOCK2POS]; inv[BIGPOCK3POS] = DO_NOT_USE_inv[OldInventory::BIGPOCK3POS]; inv[BIGPOCK4POS] = DO_NOT_USE_inv[OldInventory::BIGPOCK4POS]; inv[SMALLPOCK1POS] = DO_NOT_USE_inv[OldInventory::SMALLPOCK1POS]; inv[SMALLPOCK2POS] = DO_NOT_USE_inv[OldInventory::SMALLPOCK2POS]; inv[SMALLPOCK3POS] = DO_NOT_USE_inv[OldInventory::SMALLPOCK3POS]; inv[SMALLPOCK4POS] = DO_NOT_USE_inv[OldInventory::SMALLPOCK4POS]; inv[SMALLPOCK5POS] = DO_NOT_USE_inv[OldInventory::SMALLPOCK5POS]; inv[SMALLPOCK6POS] = DO_NOT_USE_inv[OldInventory::SMALLPOCK6POS]; inv[SMALLPOCK7POS] = DO_NOT_USE_inv[OldInventory::SMALLPOCK7POS]; inv[SMALLPOCK8POS] = DO_NOT_USE_inv[OldInventory::SMALLPOCK8POS]; bInvStatus[OldInventory::HELMETPOS] = DO_NOT_USE_bInvStatus[HELMETPOS]; bInvStatus[OldInventory::VESTPOS] = DO_NOT_USE_bInvStatus[VESTPOS]; bInvStatus[OldInventory::LEGPOS] = DO_NOT_USE_bInvStatus[LEGPOS]; bInvStatus[OldInventory::HEAD1POS] = DO_NOT_USE_bInvStatus[HEAD1POS]; bInvStatus[OldInventory::HEAD2POS] = DO_NOT_USE_bInvStatus[HEAD2POS]; bInvStatus[OldInventory::HANDPOS] = DO_NOT_USE_bInvStatus[HANDPOS]; bInvStatus[OldInventory::SECONDHANDPOS] = DO_NOT_USE_bInvStatus[SECONDHANDPOS]; bInvStatus[OldInventory::BIGPOCK1POS] = DO_NOT_USE_bInvStatus[BIGPOCK1POS]; bInvStatus[OldInventory::BIGPOCK2POS] = DO_NOT_USE_bInvStatus[BIGPOCK2POS]; bInvStatus[OldInventory::BIGPOCK3POS] = DO_NOT_USE_bInvStatus[BIGPOCK3POS]; bInvStatus[OldInventory::BIGPOCK4POS] = DO_NOT_USE_bInvStatus[BIGPOCK4POS]; bInvStatus[OldInventory::SMALLPOCK1POS] = DO_NOT_USE_bInvStatus[SMALLPOCK1POS]; bInvStatus[OldInventory::SMALLPOCK2POS] = DO_NOT_USE_bInvStatus[SMALLPOCK2POS]; bInvStatus[OldInventory::SMALLPOCK3POS] = DO_NOT_USE_bInvStatus[SMALLPOCK3POS]; bInvStatus[OldInventory::SMALLPOCK4POS] = DO_NOT_USE_bInvStatus[SMALLPOCK4POS]; bInvStatus[OldInventory::SMALLPOCK5POS] = DO_NOT_USE_bInvStatus[SMALLPOCK5POS]; bInvStatus[OldInventory::SMALLPOCK6POS] = DO_NOT_USE_bInvStatus[SMALLPOCK6POS]; bInvStatus[OldInventory::SMALLPOCK7POS] = DO_NOT_USE_bInvStatus[SMALLPOCK7POS]; bInvStatus[OldInventory::SMALLPOCK8POS] = DO_NOT_USE_bInvStatus[SMALLPOCK8POS]; bInvNumber[OldInventory::HELMETPOS] = DO_NOT_USE_bInvNumber[HELMETPOS]; bInvNumber[OldInventory::VESTPOS] = DO_NOT_USE_bInvNumber[VESTPOS]; bInvNumber[OldInventory::LEGPOS] = DO_NOT_USE_bInvNumber[LEGPOS]; bInvNumber[OldInventory::HEAD1POS] = DO_NOT_USE_bInvNumber[HEAD1POS]; bInvNumber[OldInventory::HEAD2POS] = DO_NOT_USE_bInvNumber[HEAD2POS]; bInvNumber[OldInventory::HANDPOS] = DO_NOT_USE_bInvNumber[HANDPOS]; bInvNumber[OldInventory::SECONDHANDPOS] = DO_NOT_USE_bInvNumber[SECONDHANDPOS]; bInvNumber[OldInventory::BIGPOCK1POS] = DO_NOT_USE_bInvNumber[BIGPOCK1POS]; bInvNumber[OldInventory::BIGPOCK2POS] = DO_NOT_USE_bInvNumber[BIGPOCK2POS]; bInvNumber[OldInventory::BIGPOCK3POS] = DO_NOT_USE_bInvNumber[BIGPOCK3POS]; bInvNumber[OldInventory::BIGPOCK4POS] = DO_NOT_USE_bInvNumber[BIGPOCK4POS]; bInvNumber[OldInventory::SMALLPOCK1POS] = DO_NOT_USE_bInvNumber[SMALLPOCK1POS]; bInvNumber[OldInventory::SMALLPOCK2POS] = DO_NOT_USE_bInvNumber[SMALLPOCK2POS]; bInvNumber[OldInventory::SMALLPOCK3POS] = DO_NOT_USE_bInvNumber[SMALLPOCK3POS]; bInvNumber[OldInventory::SMALLPOCK4POS] = DO_NOT_USE_bInvNumber[SMALLPOCK4POS]; bInvNumber[OldInventory::SMALLPOCK5POS] = DO_NOT_USE_bInvNumber[SMALLPOCK5POS]; bInvNumber[OldInventory::SMALLPOCK6POS] = DO_NOT_USE_bInvNumber[SMALLPOCK6POS]; bInvNumber[OldInventory::SMALLPOCK7POS] = DO_NOT_USE_bInvNumber[SMALLPOCK7POS]; bInvNumber[OldInventory::SMALLPOCK8POS] = DO_NOT_USE_bInvNumber[SMALLPOCK8POS]; } void MERCPROFILESTRUCT::CopyNewInventoryToOld() { // Do not use a loop in case the new inventory slots are arranged differently than the old DO_NOT_USE_inv[OldInventory::HELMETPOS] = inv[HELMETPOS]; DO_NOT_USE_inv[OldInventory::VESTPOS] = inv[VESTPOS]; DO_NOT_USE_inv[OldInventory::LEGPOS] = inv[LEGPOS]; DO_NOT_USE_inv[OldInventory::HEAD1POS] = inv[HEAD1POS]; DO_NOT_USE_inv[OldInventory::HEAD2POS] = inv[HEAD2POS]; DO_NOT_USE_inv[OldInventory::HANDPOS] = inv[HANDPOS]; DO_NOT_USE_inv[OldInventory::SECONDHANDPOS] = inv[SECONDHANDPOS]; DO_NOT_USE_inv[OldInventory::BIGPOCK1POS] = inv[BIGPOCK1POS]; DO_NOT_USE_inv[OldInventory::BIGPOCK2POS] = inv[BIGPOCK2POS]; DO_NOT_USE_inv[OldInventory::BIGPOCK3POS] = inv[BIGPOCK3POS]; DO_NOT_USE_inv[OldInventory::BIGPOCK4POS] = inv[BIGPOCK4POS]; DO_NOT_USE_inv[OldInventory::SMALLPOCK1POS] = inv[SMALLPOCK1POS]; DO_NOT_USE_inv[OldInventory::SMALLPOCK2POS] = inv[SMALLPOCK2POS]; DO_NOT_USE_inv[OldInventory::SMALLPOCK3POS] = inv[SMALLPOCK3POS]; DO_NOT_USE_inv[OldInventory::SMALLPOCK4POS] = inv[SMALLPOCK4POS]; DO_NOT_USE_inv[OldInventory::SMALLPOCK5POS] = inv[SMALLPOCK5POS]; DO_NOT_USE_inv[OldInventory::SMALLPOCK6POS] = inv[SMALLPOCK6POS]; DO_NOT_USE_inv[OldInventory::SMALLPOCK7POS] = inv[SMALLPOCK7POS]; DO_NOT_USE_inv[OldInventory::SMALLPOCK8POS] = inv[SMALLPOCK8POS]; DO_NOT_USE_bInvStatus[OldInventory::HELMETPOS] = bInvStatus[HELMETPOS]; DO_NOT_USE_bInvStatus[OldInventory::VESTPOS] = bInvStatus[VESTPOS]; DO_NOT_USE_bInvStatus[OldInventory::LEGPOS] = bInvStatus[LEGPOS]; DO_NOT_USE_bInvStatus[OldInventory::HEAD1POS] = bInvStatus[HEAD1POS]; DO_NOT_USE_bInvStatus[OldInventory::HEAD2POS] = bInvStatus[HEAD2POS]; DO_NOT_USE_bInvStatus[OldInventory::HANDPOS] = bInvStatus[HANDPOS]; DO_NOT_USE_bInvStatus[OldInventory::SECONDHANDPOS] = bInvStatus[SECONDHANDPOS]; DO_NOT_USE_bInvStatus[OldInventory::BIGPOCK1POS] = bInvStatus[BIGPOCK1POS]; DO_NOT_USE_bInvStatus[OldInventory::BIGPOCK2POS] = bInvStatus[BIGPOCK2POS]; DO_NOT_USE_bInvStatus[OldInventory::BIGPOCK3POS] = bInvStatus[BIGPOCK3POS]; DO_NOT_USE_bInvStatus[OldInventory::BIGPOCK4POS] = bInvStatus[BIGPOCK4POS]; DO_NOT_USE_bInvStatus[OldInventory::SMALLPOCK1POS] = bInvStatus[SMALLPOCK1POS]; DO_NOT_USE_bInvStatus[OldInventory::SMALLPOCK2POS] = bInvStatus[SMALLPOCK2POS]; DO_NOT_USE_bInvStatus[OldInventory::SMALLPOCK3POS] = bInvStatus[SMALLPOCK3POS]; DO_NOT_USE_bInvStatus[OldInventory::SMALLPOCK4POS] = bInvStatus[SMALLPOCK4POS]; DO_NOT_USE_bInvStatus[OldInventory::SMALLPOCK5POS] = bInvStatus[SMALLPOCK5POS]; DO_NOT_USE_bInvStatus[OldInventory::SMALLPOCK6POS] = bInvStatus[SMALLPOCK6POS]; DO_NOT_USE_bInvStatus[OldInventory::SMALLPOCK7POS] = bInvStatus[SMALLPOCK7POS]; DO_NOT_USE_bInvStatus[OldInventory::SMALLPOCK8POS] = bInvStatus[SMALLPOCK8POS]; DO_NOT_USE_bInvNumber[OldInventory::HELMETPOS] = bInvNumber[HELMETPOS]; DO_NOT_USE_bInvNumber[OldInventory::VESTPOS] = bInvNumber[VESTPOS]; DO_NOT_USE_bInvNumber[OldInventory::LEGPOS] = bInvNumber[LEGPOS]; DO_NOT_USE_bInvNumber[OldInventory::HEAD1POS] = bInvNumber[HEAD1POS]; DO_NOT_USE_bInvNumber[OldInventory::HEAD2POS] = bInvNumber[HEAD2POS]; DO_NOT_USE_bInvNumber[OldInventory::HANDPOS] = bInvNumber[HANDPOS]; DO_NOT_USE_bInvNumber[OldInventory::SECONDHANDPOS] = bInvNumber[SECONDHANDPOS]; DO_NOT_USE_bInvNumber[OldInventory::BIGPOCK1POS] = bInvNumber[BIGPOCK1POS]; DO_NOT_USE_bInvNumber[OldInventory::BIGPOCK2POS] = bInvNumber[BIGPOCK2POS]; DO_NOT_USE_bInvNumber[OldInventory::BIGPOCK3POS] = bInvNumber[BIGPOCK3POS]; DO_NOT_USE_bInvNumber[OldInventory::BIGPOCK4POS] = bInvNumber[BIGPOCK4POS]; DO_NOT_USE_bInvNumber[OldInventory::SMALLPOCK1POS] = bInvNumber[SMALLPOCK1POS]; DO_NOT_USE_bInvNumber[OldInventory::SMALLPOCK2POS] = bInvNumber[SMALLPOCK2POS]; DO_NOT_USE_bInvNumber[OldInventory::SMALLPOCK3POS] = bInvNumber[SMALLPOCK3POS]; DO_NOT_USE_bInvNumber[OldInventory::SMALLPOCK4POS] = bInvNumber[SMALLPOCK4POS]; DO_NOT_USE_bInvNumber[OldInventory::SMALLPOCK5POS] = bInvNumber[SMALLPOCK5POS]; DO_NOT_USE_bInvNumber[OldInventory::SMALLPOCK6POS] = bInvNumber[SMALLPOCK6POS]; DO_NOT_USE_bInvNumber[OldInventory::SMALLPOCK7POS] = bInvNumber[SMALLPOCK7POS]; DO_NOT_USE_bInvNumber[OldInventory::SMALLPOCK8POS] = bInvNumber[SMALLPOCK8POS]; } BOOLEAN IsValidSecondHandShot( SOLDIERTYPE *pSoldier ); UINT8 *gubpNumReplacementsPerRange; PaletteSubRangeType *gpPaletteSubRanges; UINT8 bHealthStrRanges[] = { 15, 30, 45, 60, 75, 90, 101 }; INT16 gsTerrainTypeSpeedModifiers[] = { 5, // Flat ground 5, // Floor 5, // Paved road 5, // Dirt road 10, // LOW GRASS 15, // HIGH GRASS 20, // TRAIN TRACKS 20, // LOW WATER 25, // MID WATER 30 // DEEP WATER }; //Kris: //Temporary for testing the speed of the translucency. Pressing Ctrl+L in turn based //input will toggle this flag. When clear, the translucency checking is turned off to //increase the speed of the game. BOOLEAN gfCalcTranslucency = FALSE; INT16 gsFullTileDirections[ MAX_FULLTILE_DIRECTIONS ] = { -1, -WORLD_COLS - 1, -WORLD_COLS }; PaletteReplacementType *gpPalRep; // Palette ranges UINT32 guiNumPaletteSubRanges; PaletteSubRangeType *guipPaletteSubRanges = NULL; // Palette replacements UINT32 guiNumReplacements; PaletteReplacementType *guipPaletteReplacements = NULL; extern BOOLEAN fReDrawFace; extern UINT8 gubWaitingForAllMercsToExitCode; BOOLEAN gfGetNewPathThroughPeople = FALSE; // LOCAL FUNCTIONS // DO NOT CALL UNLESS THROUGH EVENT_SetSoldierPosition UINT16 PickSoldierReadyAnimation( SOLDIERTYPE *pSoldier, BOOLEAN fEndReady ); BOOLEAN CheckForFullStruct( INT16 sGridNo, UINT16 *pusIndex ); void SetSoldierLocatorOffsets( SOLDIERTYPE *pSoldier ); void CheckForFullStructures( SOLDIERTYPE *pSoldier ); BOOLEAN InitNewSoldierState( SOLDIERTYPE *pSoldier, UINT8 ubNewState, UINT16 usStartingAniCode ); UINT16 GetNewSoldierStateFromNewStance( SOLDIERTYPE *pSoldier, UINT8 ubDesiredStance ); void SetSoldierAniSpeed( SOLDIERTYPE *pSoldier ); void AdjustForFastTurnAnimation( SOLDIERTYPE *pSoldier ); UINT16 SelectFireAnimation( SOLDIERTYPE *pSoldier, UINT8 ubHeight ); void SelectFallAnimation( SOLDIERTYPE *pSoldier ); BOOLEAN FullStructAlone( INT16 sGridNo, UINT8 ubRadius ); void SoldierGotHitGunFire( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial , UINT8 ubHitLocation); void SoldierGotHitBlade( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation ); void SoldierGotHitPunch( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation ); void SoldierGotHitExplosion( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation ); UINT8 CalcScreamVolume( SOLDIERTYPE * pSoldier, UINT8 ubCombinedLoss ); void PlaySoldierFootstepSound( SOLDIERTYPE *pSoldier ); void HandleSystemNewAISituation( SOLDIERTYPE *pSoldier, BOOLEAN fResetABC ); UINT16 *CreateEnemyGlow16BPPPalette( SGPPaletteEntry *pPalette, UINT32 rscale, UINT32 gscale, BOOLEAN fAdjustGreen ); UINT16 *CreateEnemyGreyGlow16BPPPalette( SGPPaletteEntry *pPalette, UINT32 rscale, UINT32 gscale, BOOLEAN fAdjustGreen ); void SoldierBleed( SOLDIERTYPE *pSoldier, BOOLEAN fBandagedBleed ); INT32 CheckBleeding( SOLDIERTYPE *pSoldier ); void EVENT_InternalSetSoldierDesiredDirection( SOLDIERTYPE *pSoldier, UINT16 usNewDirection, BOOLEAN fInitalMove, UINT16 usAnimState ); #ifdef JA2BETAVERSION extern void ValidatePlayersAreInOneGroupOnly(); extern void MapScreenDefaultOkBoxCallback( UINT8 bExitValue ); void SAIReportError( STR16 wErrorString ); #endif UINT32 SleepDartSuccumbChance( SOLDIERTYPE * pSoldier ); void EnableDisableSoldierLightEffects( BOOLEAN fEnableLights ); void SetSoldierPersonalLightLevel( SOLDIERTYPE *pSoldier ); void HandleVehicleMovementSound( SOLDIERTYPE *pSoldier, BOOLEAN fOn ) { VEHICLETYPE *pVehicle = &( pVehicleList[ pSoldier->bVehicleID ] ); if ( fOn ) { if ( pVehicle->iMovementSoundID == NO_SAMPLE ) { pVehicle->iMovementSoundID = PlayJA2Sample( pVehicle->iMoveSound, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); } } else { if ( pVehicle->iMovementSoundID != NO_SAMPLE ) { SoundStop( pVehicle->iMovementSoundID ); pVehicle->iMovementSoundID = NO_SAMPLE; } } } void AdjustNoAPToFinishMove( SOLDIERTYPE *pSoldier, BOOLEAN fSet ) { if ( pSoldier->ubBodyType == CROW ) { return; } // Check if we are a vehicle first.... if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) { // Turn off sound effects.... if ( fSet ) { HandleVehicleMovementSound( pSoldier, FALSE ); } } // Turn off sound effects.... if ( fSet ) { // Position light.... // SetCheckSoldierLightFlag( pSoldier ); } else { // DeleteSoldierLight( pSoldier ); } pSoldier->fNoAPToFinishMove = fSet; if ( !fSet ) { // return reason to default value pSoldier->ubReasonCantFinishMove = REASON_STOPPED_NO_APS; } } void HandleCrowShadowVisibility( SOLDIERTYPE *pSoldier ) { if ( pSoldier->ubBodyType == CROW ) { if ( pSoldier->usAnimState == CROW_FLY ) { if ( pSoldier->pAniTile != NULL ) { if ( pSoldier->bLastRenderVisibleValue != -1 ) { HideAniTile( pSoldier->pAniTile, FALSE ); } else { HideAniTile( pSoldier->pAniTile, TRUE ); } } } } } void HandleCrowShadowNewGridNo( SOLDIERTYPE *pSoldier ) { ANITILE_PARAMS AniParams; memset( &AniParams, 0, sizeof( ANITILE_PARAMS ) ); if ( pSoldier->ubBodyType == CROW ) { if ( pSoldier->pAniTile != NULL ) { DeleteAniTile( pSoldier->pAniTile ); pSoldier->pAniTile = NULL; } if ( pSoldier->sGridNo != NOWHERE ) { if ( pSoldier->usAnimState == CROW_FLY ) { AniParams.sGridNo = (INT16)pSoldier->sGridNo; AniParams.ubLevelID = ANI_SHADOW_LEVEL; AniParams.sDelay = pSoldier->sAniDelay; AniParams.sStartFrame = 0; AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_LOOPING | ANITILE_USE_DIRECTION_FOR_START_FRAME; AniParams.sX = pSoldier->sX; AniParams.sY = pSoldier->sY; AniParams.sZ = 0; strcpy( AniParams.zCachedFile, "TILECACHE\\FLY_SHDW.STI" ); AniParams.uiUserData3 = pSoldier->bDirection; pSoldier->pAniTile = CreateAnimationTile( &AniParams ); HandleCrowShadowVisibility( pSoldier ); } } } } void HandleCrowShadowRemoveGridNo( SOLDIERTYPE *pSoldier ) { if ( pSoldier->ubBodyType == CROW ) { if ( pSoldier->usAnimState == CROW_FLY ) { if ( pSoldier->pAniTile != NULL ) { DeleteAniTile( pSoldier->pAniTile ); pSoldier->pAniTile = NULL; } } } } void HandleCrowShadowNewDirection( SOLDIERTYPE *pSoldier ) { if ( pSoldier->ubBodyType == CROW ) { if ( pSoldier->usAnimState == CROW_FLY ) { if ( pSoldier->pAniTile != NULL ) { pSoldier->pAniTile->uiUserData3 = pSoldier->bDirection; } } } } void HandleCrowShadowNewPosition( SOLDIERTYPE *pSoldier ) { if ( pSoldier->ubBodyType == CROW ) { if ( pSoldier->usAnimState == CROW_FLY ) { if ( pSoldier->pAniTile != NULL ) { pSoldier->pAniTile->sRelativeX = pSoldier->sX; pSoldier->pAniTile->sRelativeY = pSoldier->sY; } } } } INT8 CalcActionPoints(SOLDIERTYPE *pSold) { UINT8 ubPoints,ubMaxAPs; INT8 bBandage; // dead guys don't get any APs (they shouldn't be here asking for them!) if (!pSold->bLife) return(0); // people with sleep dart drug who have collapsed get no APs if ( (pSold->bSleepDrugCounter > 0) && pSold->bCollapsed ) return( 0 ); // Calculate merc's action points at 100% capability (range is 10 - 25) // round fractions of .5 up (that's why the +20 before the division! ubPoints = 5 + (((10 * EffectiveExpLevel( pSold ) + 3 * EffectiveAgility( pSold ) + 2 * pSold->bLifeMax + 2 * EffectiveDexterity( pSold ) ) + 20) / 40); //if (GameOption[INCREASEDAP] % 2 == 1) //points += AP_INCREASE; //Madd: Add in AP Bonuses (Penalties) from worn gear ubPoints += GetGearAPBonus ( pSold ); // Calculate bandage bBandage = pSold->bLifeMax - pSold->bLife - pSold->bBleeding; // If injured, reduce action points accordingly (by up to 2/3rds) if (pSold->bLife < pSold->bLifeMax) { ubPoints -= (2 * ubPoints * (pSold->bLifeMax - pSold->bLife + (bBandage / 2))) / (3 * pSold->bLifeMax); } // If tired, reduce action points accordingly (by up to 1/2) if (pSold->bBreath < 100) ubPoints -= (ubPoints * (100 - pSold->bBreath)) / 200; if (pSold->sWeightCarriedAtTurnStart > 100) { ubPoints = (UINT8) ( ((UINT32)ubPoints) * 100 / pSold->sWeightCarriedAtTurnStart ); } // If resulting APs are below our permitted minimum, raise them to it! if (ubPoints < AP_MINIMUM) ubPoints = AP_MINIMUM; // make sure action points doesn't exceed the permitted maximum ubMaxAPs = gubMaxActionPoints[ pSold->ubBodyType ]; //if (GameOption[INCREASEDAP] % 2 == 1) // maxAPs += AP_INCREASE; // If resulting APs are below our permitted minimum, raise them to it! if (ubPoints > ubMaxAPs) ubPoints = ubMaxAPs; if ( pSold->ubBodyType == BLOODCAT ) { // use same as young monsters ubPoints = (ubPoints * AP_YOUNG_MONST_FACTOR) / 10; } else if (pSold->uiStatusFlags & SOLDIER_MONSTER) { // young monsters get extra APs if ( pSold->ubBodyType == YAF_MONSTER || pSold->ubBodyType == YAM_MONSTER || pSold->ubBodyType == INFANT_MONSTER ) { ubPoints = (ubPoints * AP_YOUNG_MONST_FACTOR) / 10; } // if frenzied, female monsters get more APs! (for young females, cumulative!) if (pSold->bFrenzied) { ubPoints = (ubPoints * AP_MONST_FRENZY_FACTOR) / 10; } } // adjust APs for phobia situations if ( pSold->ubProfile != NO_PROFILE ) { if ( (gMercProfiles[ pSold->ubProfile ].bPersonalityTrait == CLAUSTROPHOBIC) && (gbWorldSectorZ > 0) ) { ubPoints = (ubPoints * AP_CLAUSTROPHOBE) / 10; } else if ( (gMercProfiles[ pSold->ubProfile ].bPersonalityTrait == FEAR_OF_INSECTS) && (MercSeesCreature( pSold ) ) ) { ubPoints = (ubPoints * AP_AFRAID_OF_INSECTS) / 10; } } // Adjusat APs due to drugs... HandleAPEffectDueToDrugs( pSold, &ubPoints ); // If we are a vehicle, adjust APS... if ( pSold->uiStatusFlags & SOLDIER_VEHICLE ) { AdjustVehicleAPs( pSold, &ubPoints ); } //Madd // if ( pSold->bTeam != CIV_TEAM && pSold->bTeam != gbPlayerNum && gGameOptions.ubDifficultyLevel == DIF_LEVEL_INSANE ) // ubPoints += 5; // everything and everyone moves a little faster against you on insane... //Kaiden: Took your idea a step further adding the bonus for each difficulty level // and then externalized it. AND added it to the Dont max out points section below. if ( pSold->bTeam != CIV_TEAM && pSold->bTeam != gbPlayerNum) { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubPoints += gGameExternalOptions.iEasyAPBonus; break; case DIF_LEVEL_MEDIUM: ubPoints += gGameExternalOptions.iExperiencedAPBonus; break; case DIF_LEVEL_HARD: ubPoints += gGameExternalOptions.iExpertAPBonus; break; case DIF_LEVEL_INSANE: ubPoints += gGameExternalOptions.iInsaneAPBonus; break; default: ubPoints +=0; } } else ubPoints += gGameExternalOptions.iPlayerAPBonus; // if we are in boxing mode, adjust APs... THIS MUST BE LAST! if ( gTacticalStatus.bBoxingState == BOXING || gTacticalStatus.bBoxingState == PRE_BOXING ) { ubPoints /= 2; } return (ubPoints); } void CalcNewActionPoints( SOLDIERTYPE *pSoldier ) { if ( gTacticalStatus.bBoxingState == BOXING || gTacticalStatus.bBoxingState == PRE_BOXING ) { // if we are in boxing mode, carry 1/2 as many points if (pSoldier->bActionPoints > MAX_AP_CARRIED / 2) { pSoldier->bActionPoints = MAX_AP_CARRIED / 2; } } else { if (pSoldier->bActionPoints > MAX_AP_CARRIED) { pSoldier->bActionPoints = MAX_AP_CARRIED; } } pSoldier->bActionPoints += CalcActionPoints( pSoldier); // Don't max out if we are drugged.... if ( !GetDrugEffect( pSoldier, DRUG_TYPE_ADRENALINE ) ) { //Kaiden: If Enemy, they can max out, but their Max is NOW = MAX + diffAPBonus // No sense in giving them a bonus if some of the points are wasted because we // Didn't raise their cap by the same amount. if ( pSoldier->bTeam != CIV_TEAM && pSoldier->bTeam != gbPlayerNum) { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: pSoldier->bActionPoints = __min( pSoldier->bActionPoints, (gubMaxActionPoints[ pSoldier->ubBodyType ] + gGameExternalOptions.iEasyAPBonus)); break; case DIF_LEVEL_MEDIUM: pSoldier->bActionPoints = __min( pSoldier->bActionPoints, (gubMaxActionPoints[ pSoldier->ubBodyType ] + gGameExternalOptions.iExperiencedAPBonus)); break; case DIF_LEVEL_HARD: pSoldier->bActionPoints = __min( pSoldier->bActionPoints, (gubMaxActionPoints[ pSoldier->ubBodyType ] + gGameExternalOptions.iExpertAPBonus)); break; case DIF_LEVEL_INSANE: pSoldier->bActionPoints = __min( pSoldier->bActionPoints, (gubMaxActionPoints[ pSoldier->ubBodyType ] + gGameExternalOptions.iInsaneAPBonus)); break; default: pSoldier->bActionPoints = __min( pSoldier->bActionPoints, gubMaxActionPoints[ pSoldier->ubBodyType ] ); } } else { //Kaiden: Players just max out normally unless drugged pSoldier->bActionPoints = __min( pSoldier->bActionPoints, (gubMaxActionPoints[ pSoldier->ubBodyType ] + gGameExternalOptions.iPlayerAPBonus) ); } } pSoldier->bInitialActionPoints = pSoldier->bActionPoints; } void DoNinjaAttack( SOLDIERTYPE *pSoldier ) { //UINT32 uiMercFlags; UINT16 usSoldierIndex; SOLDIERTYPE *pTSoldier; UINT8 ubTDirection; UINT8 ubTargetStance; usSoldierIndex = WhoIsThere2( pSoldier->sTargetGridNo, pSoldier->bLevel ); if ( usSoldierIndex != NOBODY ) { GetSoldier( &pTSoldier, usSoldierIndex ); // Look at stance of target ubTargetStance = gAnimControl[ pTSoldier->usAnimState ].ubEndHeight; // Get his life...if < certain value, do finish! if ( (pTSoldier->bLife <= 30 || pTSoldier->bBreath <= 30) && ubTargetStance != ANIM_PRONE ) { // Do finish! ChangeSoldierState( pSoldier, NINJA_SPINKICK, 0 , FALSE ); } else { if ( ubTargetStance != ANIM_PRONE ) { if ( Random( 2 ) == 0 ) { ChangeSoldierState( pSoldier, NINJA_LOWKICK, 0 , FALSE ); } else { ChangeSoldierState( pSoldier, NINJA_PUNCH, 0 , FALSE ); } // CHECK IF HE CAN SEE US, IF SO CHANGE DIRECTION if ( pTSoldier->bOppList[ pSoldier->ubID ] == 0 && pTSoldier->bTeam != pSoldier->bTeam ) { if ( !( pTSoldier->uiStatusFlags & ( SOLDIER_MONSTER | SOLDIER_ANIMAL | SOLDIER_VEHICLE ) ) ) { ubTDirection = (UINT8)GetDirectionFromGridNo( pSoldier->sGridNo, pTSoldier ); SendSoldierSetDesiredDirectionEvent( pTSoldier, ubTDirection ); } } } else { // CHECK OUR STANCE if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_CROUCH ) { // SET DESIRED STANCE AND SET PENDING ANIMATION SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH ); pSoldier->usPendingAnimation = PUNCH_LOW; } else { // USE crouched one // NEED TO CHANGE STANCE IF NOT CROUCHD! EVENT_InitNewSoldierAnim( pSoldier, PUNCH_LOW, 0 , FALSE ); } } } } if ( pSoldier->ubProfile == 33 ) { UINT32 uiSoundID; SOUNDPARMS spParms; INT32 iFaceIndex; // Play sound! memset(&spParms, 0xff, sizeof(SOUNDPARMS)); spParms.uiSpeed = RATE_11025; spParms.uiVolume = (INT8)CalculateSpeechVolume( HIGHVOLUME ); // If we are an enemy.....reduce due to volume if ( pSoldier->bTeam != gbPlayerNum ) { spParms.uiVolume = SoundVolume( (UINT8)spParms.uiVolume, pSoldier->sGridNo ); } spParms.uiLoop = 1; spParms.uiPan = SoundDir( pSoldier->sGridNo ); spParms.uiPriority=GROUP_PLAYER; if ( pSoldier->usAnimState == NINJA_SPINKICK ) { uiSoundID = SoundPlay( "BATTLESNDS\\033_CHOP2.WAV", &spParms ); } else { if ( Random( 2 ) == 0 ) { uiSoundID = SoundPlay( "BATTLESNDS\\033_CHOP3.WAV", &spParms ); } else { uiSoundID = SoundPlay( "BATTLESNDS\\033_CHOP1.WAV", &spParms ); } } if ( uiSoundID != SOUND_ERROR ) { pSoldier->uiBattleSoundID = uiSoundID; if ( pSoldier->ubProfile != NO_PROFILE ) { // Get soldier's face ID iFaceIndex = pSoldier->iFaceIndex; // Check face index if ( iFaceIndex != -1 ) { ExternSetFaceTalking( iFaceIndex, uiSoundID ); } } } } } BOOLEAN CreateSoldierCommon( UINT8 ubBodyType, SOLDIERTYPE *pSoldier, UINT16 usSoldierID, UINT16 usState ) { BOOLEAN fSuccess = FALSE; INT32 iCounter = 0; //if we are loading a saved game, we DO NOT want to reset the opplist, look for enemies, or say a dying commnet if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) ) { // Set initial values for opplist! InitSoldierOppList( pSoldier ); HandleSight( pSoldier, SIGHT_LOOK ); // Set some quote flags if ( pSoldier->bLife >= OKLIFE ) { pSoldier->fDyingComment = FALSE; } else { pSoldier->fDyingComment = TRUE; } } // ATE: Reset some timer flags... pSoldier->uiTimeSameBattleSndDone = 0; // ATE: Reset every time..... pSoldier->fSoldierWasMoving = TRUE; pSoldier->iTuringSoundID = NO_SAMPLE; pSoldier->uiTimeSinceLastBleedGrunt = 0; if ( pSoldier->ubBodyType == QUEENMONSTER ) { pSoldier->iPositionSndID = NewPositionSnd( NOWHERE, POSITION_SOUND_FROM_SOLDIER, (UINT32)pSoldier, QUEEN_AMBIENT_NOISE ); } // ANYTHING AFTER HERE CAN FAIL do { if (usSoldierID <= gTacticalStatus.Team[ OUR_TEAM ].bLastID) { pSoldier->pKeyRing = (KEY_ON_RING *) MemAlloc( NUM_KEYS * sizeof( KEY_ON_RING ) ); memset( pSoldier->pKeyRing , 0, NUM_KEYS * sizeof( KEY_ON_RING ) ); for( iCounter = 0; iCounter < NUM_KEYS; iCounter++ ) { pSoldier->pKeyRing[ iCounter ].ubKeyID = INVALID_KEY_NUMBER; } } else { pSoldier->pKeyRing = NULL; } // Create frame cache if( InitAnimationCache( usSoldierID, &( pSoldier->AnimCache) ) == FALSE ) { DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed animation cache creation" ) ); break; } if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) ) { // Init new soldier state // OFFSET FIRST ANIMATION FRAME FOR NEW MERCS if ( usState != STANDING ) { EVENT_InitNewSoldierAnim( pSoldier, usState, (UINT8)0, TRUE ); } else { EVENT_InitNewSoldierAnim( pSoldier, usState, (UINT8)Random( 10 ), TRUE ); } } else { /// if we don't have a world loaded, and are in a bad anim, goto standing. // bad anims are: HOPFENCE, // CLIMBDOWNROOF, FALLFORWARD_ROOF,FALLOFF, CLIMBUPROOF if( !gfWorldLoaded && ( usState == HOPFENCE || usState == CLIMBDOWNROOF || usState == FALLFORWARD_ROOF || usState == FALLOFF || usState == CLIMBUPROOF ) ) { EVENT_InitNewSoldierAnim( pSoldier, STANDING, 0, TRUE ); } else { EVENT_InitNewSoldierAnim( pSoldier, usState, pSoldier->usAniCode, TRUE ); } } //if ( pSoldier->pBackGround != NULL ) // MemFree( pSoldier->pBackGround ); // INIT ANIMATION DATA //if((pSoldier->pBackGround=MemAlloc(SOLDIER_UNBLIT_SIZE))==NULL) //{ // DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed unblit memory allocation" ) ); // break; //} //memset(pSoldier->pBackGround, 0, SOLDIER_UNBLIT_SIZE); //if((pSoldier->pZBackground=MemAlloc(SOLDIER_UNBLIT_SIZE))==NULL) //{ // DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed unblit memory allocation" ) ); // break; //} //memset(pSoldier->pZBackground, 0, SOLDIER_UNBLIT_SIZE); // Init palettes if( CreateSoldierPalettes( pSoldier ) == FALSE ) { DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed in creating soldier palettes" ) ); break; } fSuccess = TRUE; } while( FALSE ); if ( !fSuccess ) { DeleteSoldier( (pSoldier ) ); } return( fSuccess ); } BOOLEAN DeleteSoldier( SOLDIERTYPE *pSoldier ) { UINT32 cnt; INT32 iGridNo; INT8 bDir; BOOLEAN fRet; if ( pSoldier != NULL ) { //if(pSoldier->pBackGround!=NULL) //MemFree(pSoldier->pBackGround); //if(pSoldier->pZBackground!=NULL) //MemFree(pSoldier->pZBackground); if( pSoldier->sGridNo != NOWHERE ) { // Remove adjacency records for (bDir = 0; bDir < NUM_WORLD_DIRECTIONS; bDir++) { iGridNo = pSoldier->sGridNo + DirIncrementer[ bDir ]; if( iGridNo >= 0 && iGridNo < WORLD_MAX ) { gpWorldLevelData[ iGridNo ].ubAdjacentSoldierCnt--; } } } // Delete key ring if (pSoldier->pKeyRing) { MemFree( pSoldier->pKeyRing ); pSoldier->pKeyRing = NULL; } // Delete faces DeleteSoldierFace( pSoldier ); // FREE PALETTES if ( pSoldier->p8BPPPalette != NULL ) { MemFree( pSoldier->p8BPPPalette ); pSoldier->p8BPPPalette = NULL; } if ( pSoldier->p16BPPPalette != NULL ) { MemFree( pSoldier->p16BPPPalette ); pSoldier->p16BPPPalette = NULL; } for ( cnt = 0; cnt < NUM_SOLDIER_SHADES; cnt++ ) { if ( pSoldier->pShades[ cnt ] != NULL ) { MemFree( pSoldier->pShades[ cnt ] ); pSoldier->pShades[ cnt ] = NULL; } } for ( cnt = 0; cnt < NUM_SOLDIER_EFFECTSHADES; cnt++ ) { if ( pSoldier->pEffectShades[ cnt ] != NULL ) { MemFree( pSoldier->pEffectShades[ cnt ] ); pSoldier->pEffectShades[ cnt ] = NULL; } } // Delete glows for ( cnt = 0; cnt < 20; cnt++ ) { if ( pSoldier->pGlowShades[ cnt ] != NULL ) { MemFree( pSoldier->pGlowShades[ cnt ] ); pSoldier->pGlowShades[ cnt ] = NULL; } } if ( pSoldier->ubBodyType == QUEENMONSTER ) { DeletePositionSnd( pSoldier->iPositionSndID ); } // Free any animations we may have locked... UnLoadCachedAnimationSurfaces( pSoldier->ubID, &( pSoldier->AnimCache) ); // Free Animation cache DeleteAnimationCache( pSoldier->ubID, &( pSoldier->AnimCache) ); // Soldier is not active pSoldier->bActive = FALSE; // Remove light DeleteSoldierLight( pSoldier ); // Remove reseved movement value UnMarkMovementReserved( pSoldier ); } // REMOVE SOLDIER FROM SLOT! fRet = RemoveMercSlot( pSoldier ); if (!fRet) { RemoveAwaySlot( pSoldier ); } return( TRUE ); } BOOLEAN CreateSoldierLight( SOLDIERTYPE *pSoldier ) { if ( pSoldier->bTeam != gbPlayerNum ) { return( FALSE ); } // DO ONLY IF WE'RE AT A GOOD LEVEL if ( pSoldier->iLight == -1 ) { // ATE: Check for goggles in headpos.... if ( GetTotalVisionRangeBonus( pSoldier, NORMAL_LIGHTLEVEL_NIGHT ) >= UVGOGGLES_BONUS ) { if( ( pSoldier->iLight=LightSpriteCreate("Light4", 0 ) )==(-1)) { DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed loading light" ) ); return( FALSE ); } else { LightSprites[ pSoldier->iLight ].uiFlags |= MERC_LIGHT; } } else if ( GetTotalVisionRangeBonus( pSoldier, NORMAL_LIGHTLEVEL_NIGHT ) >= NIGHTSIGHTGOGGLES_BONUS ) { if( ( pSoldier->iLight=LightSpriteCreate("Light3", 0 ) )==(-1)) { DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed loading light" ) ); return( FALSE ); } else { LightSprites[ pSoldier->iLight ].uiFlags |= MERC_LIGHT; } } else { if( ( pSoldier->iLight=LightSpriteCreate("Light2", 0 ) )==(-1)) { DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed loading light" ) ); return( FALSE ); } else { LightSprites[ pSoldier->iLight ].uiFlags |= MERC_LIGHT; } } if ( pSoldier->bLevel != 0 ) { LightSpriteRoofStatus(pSoldier->iLight, TRUE ); } } return( TRUE ); } BOOLEAN ReCreateSoldierLight( SOLDIERTYPE *pSoldier ) { if ( pSoldier->bTeam != gbPlayerNum ) { return( FALSE ); } if ( !pSoldier->bActive ) { return( FALSE ); } if ( !pSoldier->bInSector ) { return( FALSE ); } // Delete Light! DeleteSoldierLight( pSoldier ); if ( pSoldier->iLight == -1 ) { CreateSoldierLight( pSoldier ); } return( TRUE ); } BOOLEAN ReCreateSelectedSoldierLight( ) { SOLDIERTYPE *pSoldier; if ( gusSelectedSoldier == NO_SOLDIER ) { return( FALSE ); } pSoldier = MercPtrs[ gusSelectedSoldier ]; return( ReCreateSoldierLight( pSoldier ) ); } BOOLEAN DeleteSoldierLight( SOLDIERTYPE *pSoldier ) { if( pSoldier->iLight!=(-1) ) { LightSpriteDestroy( pSoldier->iLight ); pSoldier->iLight = -1; } return( TRUE ); } // FUNCTIONS CALLED BY EVENT PUMP ///////////////////////////////// BOOLEAN ChangeSoldierState( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT16 usStartingAniCode, BOOLEAN fForce ) { EV_S_CHANGESTATE SChangeState; // Send message that we have changed states SChangeState.usNewState = usNewState; SChangeState.usSoldierID = pSoldier->ubID; SChangeState.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue; SChangeState.usStartingAniCode = usStartingAniCode; SChangeState.sXPos = pSoldier->sX; SChangeState.sYPos = pSoldier->sY; SChangeState.fForce = fForce; SChangeState.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue; //AddGameEvent( S_CHANGESTATE, 0, &SChangeState ); EVENT_InitNewSoldierAnim( pSoldier, SChangeState.usNewState, SChangeState.usStartingAniCode, SChangeState.fForce ); return( TRUE ); } // This function reevaluates the stance if the guy sees us! BOOLEAN ReevaluateEnemyStance( SOLDIERTYPE *pSoldier, UINT16 usAnimState ) { INT32 cnt, iClosestEnemy = NOBODY; INT16 sTargetXPos, sTargetYPos; BOOLEAN fReturnVal = FALSE; INT16 sDist, sClosestDist = 10000; // make the chosen one not turn to face us if ( OK_ENEMY_MERC( pSoldier ) && pSoldier->ubID != gTacticalStatus.ubTheChosenOne && gAnimControl[ usAnimState ].ubEndHeight == ANIM_STAND && !( pSoldier->uiStatusFlags & SOLDIER_UNDERAICONTROL) ) { if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_OFF ) { // If we are a queen and see enemies, goto ready if ( pSoldier->ubBodyType == QUEENMONSTER ) { if ( gAnimControl[ usAnimState ].uiFlags & ( ANIM_BREATH ) ) { if ( pSoldier->bOppCnt > 0 ) { EVENT_InitNewSoldierAnim( pSoldier, QUEEN_INTO_READY, 0 , TRUE ); return( TRUE ); } } } // ATE: Don't do this if we're not a merc..... if ( !IS_MERC_BODY_TYPE( pSoldier ) ) { return( FALSE ); } if ( gAnimControl[ usAnimState ].uiFlags & ( ANIM_MERCIDLE | ANIM_BREATH ) ) { if ( pSoldier->bOppCnt > 0 ) { // Pick a guy this buddy sees and turn towards them! for ( cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++ ) { if ( pSoldier->bOppList[ cnt ] == SEEN_CURRENTLY ) { sDist = PythSpacesAway( pSoldier->sGridNo, MercPtrs[ cnt ]->sGridNo ); if (sDist < sClosestDist) { sClosestDist = sDist; iClosestEnemy = cnt; } } } if (iClosestEnemy != NOBODY) { // Change to fire ready animation ConvertGridNoToXY( MercPtrs[ iClosestEnemy ]->sGridNo, &sTargetXPos, &sTargetYPos ); pSoldier->fDontChargeReadyAPs = TRUE; // Ready weapon fReturnVal = SoldierReadyWeapon( pSoldier, sTargetXPos, sTargetYPos, FALSE ); return( fReturnVal ); } } } } } return( FALSE ); } void CheckForFreeupFromHit( SOLDIERTYPE *pSoldier, UINT32 uiOldAnimFlags, UINT32 uiNewAnimFlags, UINT16 usOldAniState, UINT16 usNewState ) { // THIS COULD POTENTIALLY CALL EVENT_INITNEWAnim() if the GUY was SUPPRESSED // CHECK IF THE OLD ANIMATION WAS A HIT START THAT WAS NOT FOLLOWED BY A HIT FINISH // IF SO, RELEASE ATTACKER FROM ATTACKING // If old and new animations are the same, do nothing! if ( usOldAniState == QUEEN_HIT && usNewState == QUEEN_HIT ) { return; } if ( usOldAniState != usNewState && ( uiOldAnimFlags & ANIM_HITSTART ) && !( uiNewAnimFlags & ANIM_HITFINISH ) && !( uiNewAnimFlags & ANIM_IGNOREHITFINISH ) && !(pSoldier->uiStatusFlags & SOLDIER_TURNINGFROMHIT ) ) { // 0verhaul: Yet again, this is handled by the state transition code. // Release attacker // DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Releasesoldierattacker, normal hit animation ended NEW: %s ( %d ) OLD: %s ( %d )", gAnimControl[ usNewState ].zAnimStr, usNewState, gAnimControl[ usOldAniState ].zAnimStr, pSoldier->usOldAniState ) ); // ReleaseSoldiersAttacker( pSoldier ); //FREEUP GETTING HIT FLAG // pSoldier->fGettingHit = FALSE; // ATE: if our guy, have 10% change of say damn, if still conscious... if ( pSoldier->bTeam == gbPlayerNum && pSoldier->bLife >= OKLIFE ) { if ( Random( 10 ) == 0 ) { DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_CURSE1 ) ); } } } // CHECK IF WE HAVE FINSIHED A HIT WHILE DOWN // OBLY DO THIS IF 1 ) We are dead already or 2 ) We are alive still if ( ( uiOldAnimFlags & ANIM_HITWHENDOWN ) && ( ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) || pSoldier->bLife != 0 ) ) { // 0verhaul: Ditto // Release attacker // DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Releasesoldierattacker, animation of kill on the ground ended") ); // ReleaseSoldiersAttacker( pSoldier ); //FREEUP GETTING HIT FLAG // pSoldier->fGettingHit = FALSE; if ( pSoldier->bLife == 0 ) { //ATE: Set previous attacker's value! // This is so that the killer can say their killed quote.... pSoldier->ubAttackerID = pSoldier->ubPreviousAttackerID; } } } // THIS IS CALLED FROM AN EVENT ( S_CHANGESTATE )! BOOLEAN EVENT_InitNewSoldierAnim( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT16 usStartingAniCode, BOOLEAN fForce ) { DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"EVENT_InitNewSoldierAnim"); UINT16 usNewGridNo = 0; INT16 sAPCost = 0; INT16 sBPCost = 0; UINT32 uiOldAnimFlags; UINT32 uiNewAnimFlags; UINT16 usSubState; UINT16 usItem; BOOLEAN fTryingToRestart = FALSE; CHECKF( usNewState < NUMANIMATIONSTATES ); /////////////////////////////////////////////////////////////////////// // DO SOME CHECKS ON OUR NEW ANIMATION! ///////////////////////////////////////////////////////////////////// // If we are NOT loading a game, continue normally if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) ) { // CHECK IF WE ARE TRYING TO INTURRUPT A SCRIPT WHICH WE DO NOT WANT INTERRUPTED! if ( pSoldier->fInNonintAnim ) { return( FALSE ); } if ( pSoldier->fRTInNonintAnim ) { if ( !(gTacticalStatus.uiFlags & INCOMBAT) ) { return( FALSE ); } else { pSoldier->fRTInNonintAnim = FALSE; } } // Check if we can restart this animation if it's the same as our current! if ( usNewState == pSoldier->usAnimState ) { if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_NORESTART ) && !fForce ) { fTryingToRestart = TRUE; } } // Check state, if we are not at the same height, set this ani as the pending one and // change stance accordingly // ATE: ONLY IF WE ARE STARTING AT START OF ANIMATION! if ( usStartingAniCode == 0 ) { if ( gAnimControl[ usNewState ].ubHeight != gAnimControl[ pSoldier->usAnimState ].ubEndHeight && !( gAnimControl[ usNewState ].uiFlags & ( ANIM_STANCECHANGEANIM | ANIM_IGNORE_AUTOSTANCE ) ) ) { // Check if we are going from crouched height to prone height, and adjust fast turning accordingly // Make guy turn while crouched THEN go into prone if ( ( gAnimControl[ usNewState ].ubEndHeight == ANIM_PRONE && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH ) && !( gTacticalStatus.uiFlags & INCOMBAT ) ) { pSoldier->fTurningUntilDone = TRUE; pSoldier->ubPendingStanceChange = gAnimControl[ usNewState ].ubEndHeight; pSoldier->usPendingAnimation = usNewState; return( TRUE ); } // Check if we are in realtime and we are going from stand to crouch else if ( gAnimControl[ usNewState ].ubEndHeight == ANIM_CROUCH && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND && ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) && ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) ) { pSoldier->ubDesiredHeight = gAnimControl[ usNewState ].ubEndHeight; // Continue with this course of action IE: Do animation and skip from stand to crouch } // Check if we are in realtime and we are going from crouch to stand else if ( gAnimControl[ usNewState ].ubEndHeight == ANIM_STAND && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH && ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) && ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) && pSoldier->usAnimState != HELIDROP ) { pSoldier->ubDesiredHeight = gAnimControl[ usNewState ].ubEndHeight; // Continue with this course of action IE: Do animation and skip from stand to crouch } else { // ONLY DO FOR EVERYONE BUT PLANNING GUYS if ( pSoldier->ubID < MAX_NUM_SOLDIERS ) { // Set our next moving animation to be pending, after pSoldier->usPendingAnimation = usNewState; // Set new state to be animation to move to new stance SendChangeSoldierStanceEvent( pSoldier, gAnimControl[ usNewState ].ubHeight ); return( TRUE ); } } } } if ( usNewState == ADJACENT_GET_ITEM ) { if ( pSoldier->ubPendingDirection != NO_PENDING_DIRECTION ) { EVENT_InternalSetSoldierDesiredDirection( pSoldier, pSoldier->ubPendingDirection, FALSE, pSoldier->usAnimState ); pSoldier->ubPendingDirection = NO_PENDING_DIRECTION; pSoldier->usPendingAnimation = ADJACENT_GET_ITEM; pSoldier->fTurningUntilDone = TRUE; SoldierGotoStationaryStance( pSoldier ); return( TRUE ); } } if ( usNewState == CLIMBUPROOF ) { if ( pSoldier->ubPendingDirection != NO_PENDING_DIRECTION ) { EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->ubPendingDirection ); pSoldier->ubPendingDirection = NO_PENDING_DIRECTION; pSoldier->usPendingAnimation = CLIMBUPROOF; pSoldier->fTurningUntilDone = TRUE; SoldierGotoStationaryStance( pSoldier ); return( TRUE ); } } if ( usNewState == CLIMBDOWNROOF ) { if ( pSoldier->ubPendingDirection != NO_PENDING_DIRECTION ) { EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->ubPendingDirection ); pSoldier->ubPendingDirection = NO_PENDING_DIRECTION; pSoldier->usPendingAnimation = CLIMBDOWNROOF; pSoldier->fTurningFromPronePosition = FALSE; pSoldier->fTurningUntilDone = TRUE; SoldierGotoStationaryStance( pSoldier ); return( TRUE ); } } // ATE: Don't raise/lower automatically if we are low on health, // as our gun looks lowered anyway.... //if ( pSoldier->bLife > INJURED_CHANGE_THREASHOLD ) { // Don't do some of this if we are a monster! // ATE: LOWER AIMATION IS GOOD, RAISE ONE HOWEVER MAY CAUSE PROBLEMS FOR AI.... if ( !(pSoldier->uiStatusFlags & SOLDIER_MONSTER ) && pSoldier->ubBodyType != ROBOTNOWEAPON && pSoldier->bTeam == gbPlayerNum ) { // If this animation is a raise_weapon animation if ( ( gAnimControl[ usNewState ].uiFlags & ANIM_RAISE_WEAPON ) && !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_RAISE_WEAPON | ANIM_NOCHANGE_WEAPON ) ) ) { // We are told that we need to rasie weapon // Do so only if // 1) We have a rifle in hand... usItem = pSoldier->inv[ HANDPOS ].usItem; // if ( usItem != NOTHING && (Item[ usItem ].fFlags & ITEM_TWO_HANDED) && usItem != ROCKET_LAUNCHER && usItem != RPG7 ) if ( usItem != NOTHING && (Item[ usItem ].twohanded ) && !Item[usItem].rocketlauncher ) { // Switch on height! switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: // 2) OK, all's fine... lower weapon first.... pSoldier->usPendingAnimation = usNewState; // Set new state to be animation to move to new stance usNewState = RAISE_RIFLE; } } } // If this animation is a lower_weapon animation if ( ( gAnimControl[ usNewState ].uiFlags & ANIM_LOWER_WEAPON ) && !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_LOWER_WEAPON | ANIM_NOCHANGE_WEAPON ) ) ) { // We are told that we need to rasie weapon // Do so only if // 1) We have a rifle in hand... usItem = pSoldier->inv[ HANDPOS ].usItem; // if ( usItem != NOTHING && (Item[ usItem ].fFlags & ITEM_TWO_HANDED) && usItem != ROCKET_LAUNCHER && usItem != RPG7 ) if ( usItem != NOTHING && (Item[ usItem ].twohanded ) && !Item[usItem].rocketlauncher ) { // Switch on height! switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: // 2) OK, all's fine... lower weapon first.... pSoldier->usPendingAnimation = usNewState; // Set new state to be animation to move to new stance usNewState = LOWER_RIFLE; } } } } } // Are we cowering and are tyring to move, getup first... if ( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING && pSoldier->usAnimState == COWERING && gAnimControl[ usNewState ].ubEndHeight == ANIM_STAND ) { pSoldier->usPendingAnimation = usNewState; // Set new state to be animation to move to new stance usNewState = END_COWER; } // If we want to start swatting, put a pending animation if( pSoldier->usAnimState != START_SWAT && usNewState == SWATTING ) { // Set new state to be animation to move to new stance usNewState = START_SWAT; } if( pSoldier->usAnimState == SWATTING && usNewState == CROUCHING ) { // Set new state to be animation to move to new stance usNewState = END_SWAT; } if( pSoldier->usAnimState == WALKING && usNewState == STANDING && pSoldier->bLife < INJURED_CHANGE_THREASHOLD && pSoldier->ubBodyType <= REGFEMALE && !MercInWater( pSoldier ) ) { // Set new state to be animation to move to new stance usNewState = END_HURT_WALKING; } // Check if we are an enemy, and we are in an animation what should be overriden // by if he sees us or not. if ( ReevaluateEnemyStance( pSoldier, usNewState ) ) { return( TRUE ); } // OK....... if ( pSoldier->ubBodyType > REGFEMALE ) { if ( pSoldier->bLife < INJURED_CHANGE_THREASHOLD ) { if ( usNewState == READY_RIFLE_STAND ) { // pSoldier->usPendingAnimation2 = usNewState; // usNewState = FROM_INJURED_TRANSITION; } } } // Alrighty, check if we should free buddy up! if ( usNewState == GIVING_AID ) { UnSetUIBusy( pSoldier->ubID ); } // SUBSTITUDE VARIOUS REG ANIMATIONS WITH ODD BODY TYPES if ( SubstituteBodyTypeAnimation( pSoldier, usNewState, &usSubState ) ) { usNewState = usSubState; } // CHECK IF WE CAN DO THIS ANIMATION! if ( IsAnimationValidForBodyType( pSoldier, usNewState ) == FALSE ) { return( FALSE ); } // OK, make guy transition if a big merc... if ( pSoldier->uiAnimSubFlags & SUB_ANIM_BIGGUYTHREATENSTANCE ) { if ( usNewState == KNEEL_DOWN && pSoldier->usAnimState != BIGMERC_CROUCH_TRANS_INTO ) { UINT16 usItem; // Do we have a rifle? usItem = pSoldier->inv[ HANDPOS ].usItem; if ( usItem != NOTHING ) { if ( Item[ usItem ].usItemClass == IC_GUN && !Item[usItem].rocketlauncher ) { // if ( (Item[ usItem ].fFlags & ITEM_TWO_HANDED) ) if ( (Item[ usItem ].twohanded ) ) { usNewState = BIGMERC_CROUCH_TRANS_INTO; } } } } if ( usNewState == KNEEL_UP && pSoldier->usAnimState != BIGMERC_CROUCH_TRANS_OUTOF ) { UINT16 usItem; // Do we have a rifle? usItem = pSoldier->inv[ HANDPOS ].usItem; if ( usItem != NOTHING ) { if ( Item[ usItem ].usItemClass == IC_GUN && !Item[usItem].rocketlauncher ) { // if ( (Item[ usItem ].fFlags & ITEM_TWO_HANDED) ) if ( (Item[ usItem ].twohanded ) ) { usNewState = BIGMERC_CROUCH_TRANS_OUTOF; } } } } } // OK, if we have reverse set, do the side step! if ( pSoldier->bReverse ) { if ( usNewState == WALKING || usNewState == RUNNING || usNewState == SWATTING ) { // CHECK FOR SIDEWAYS! if ( pSoldier->bDirection == gPurpendicularDirection[ pSoldier->bDirection ][ pSoldier->usPathingData[ pSoldier->usPathIndex ] ] ) { // We are perpendicular! usNewState = SIDE_STEP; } else { if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH ) { usNewState = SWAT_BACKWARDS; } else { // Here, change to opposite direction usNewState = WALK_BACKWARDS; } } } } // ATE: Patch hole for breath collapse for roofs, fences if ( usNewState == CLIMBUPROOF || usNewState == CLIMBDOWNROOF || usNewState == HOPFENCE ) { // Check for breath collapse if a given animation like if ( CheckForBreathCollapse( pSoldier ) || pSoldier->bCollapsed ) { // UNset UI UnSetUIBusy( pSoldier->ubID ); SoldierCollapse( pSoldier ); pSoldier->bBreathCollapsed = FALSE; return( FALSE ); } } // If we are in water.....and trying to run, change to run if ( pSoldier->bOverTerrainType == LOW_WATER || pSoldier->bOverTerrainType == MED_WATER ) { // Check animation // Change to walking if ( usNewState == RUNNING ) { usNewState = WALKING; } } // Turn off anipause flag for any anim! pSoldier->uiStatusFlags &= (~SOLDIER_PAUSEANIMOVE); // Unset paused for no APs..... AdjustNoAPToFinishMove( pSoldier, FALSE ); if ( usNewState == CRAWLING && pSoldier->usDontUpdateNewGridNoOnMoveAnimChange == 1 ) { if ( pSoldier->fTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE ) { pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_START_UP_FROM_MOVE; } // ATE: IF we are starting to crawl, but have to getup to turn first...... if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_START_UP_FROM_MOVE ) { usNewState = PRONE_UP; pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE; } } // We are about to start moving // Handle buddy beginning to move... // check new gridno, etc // ATE: Added: Make check that old anim is not a moving one as well if ( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING && !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) || ( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING && fForce ) ) { BOOLEAN fKeepMoving; if ( usNewState == CRAWLING && pSoldier->usDontUpdateNewGridNoOnMoveAnimChange == LOCKED_NO_NEWGRIDNO ) { // Turn off lock once we are crawling once... pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = 1; } // ATE: Additional check here if we have just been told to update animation ONLY, not goto gridno stuff... if ( !pSoldier->usDontUpdateNewGridNoOnMoveAnimChange ) { if ( usNewState != SWATTING ) { DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Handling New gridNo for %d: Old %s, New %s", pSoldier->ubID, gAnimControl[ pSoldier->usAnimState ].zAnimStr , gAnimControl[ usNewState ].zAnimStr) ); if ( !( gAnimControl[ usNewState ].uiFlags & ANIM_SPECIALMOVE ) ) { // Handle goto new tile... if ( HandleGotoNewGridNo( pSoldier, &fKeepMoving, TRUE, usNewState ) ) { if ( !fKeepMoving ) { return( FALSE ); } // Make sure desy = zeroed out... // pSoldier->fPastXDest = pSoldier->fPastYDest = FALSE; } else { if ( pSoldier->bBreathCollapsed ) { // UNset UI UnSetUIBusy( pSoldier->ubID ); SoldierCollapse( pSoldier ); pSoldier->bBreathCollapsed = FALSE; } return( FALSE ); } } else { // Change desired direction // Just change direction EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, pSoldier->usAnimState ); } //check for services ReceivingSoldierCancelServices( pSoldier ); GivingSoldierCancelServices( pSoldier ); // Check if we are a vehicle, and start playing noise sound.... if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) { HandleVehicleMovementSound( pSoldier, TRUE ); } } } } else { // Check for stopping movement noise... if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) { HandleVehicleMovementSound( pSoldier, FALSE ); // If a vehicle, set hewight to 0 SetSoldierHeight( pSoldier, (FLOAT)( 0 ) ); } } // Reset to false always..... // ( Unless locked ) if ( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING ) { if ( pSoldier->usDontUpdateNewGridNoOnMoveAnimChange != LOCKED_NO_NEWGRIDNO ) { pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = FALSE; } } if ( fTryingToRestart ) { return( FALSE ); } } // ATE: If this is an AI guy.. unlock him! if ( gTacticalStatus.fEnemySightingOnTheirTurn ) { if ( gTacticalStatus.ubEnemySightingOnTheirTurnEnemyID == pSoldier->ubID ) { pSoldier->fPauseAllAnimation = FALSE; gTacticalStatus.fEnemySightingOnTheirTurn = FALSE; } } /////////////////////////////////////////////////////////////////////// // HERE DOWN - WE HAVE MADE A DESCISION! ///////////////////////////////////////////////////////////////////// uiOldAnimFlags = gAnimControl[ pSoldier->usAnimState ].uiFlags; uiNewAnimFlags = gAnimControl[ usNewState ].uiFlags; usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc( pSoldier->usPathingData[ pSoldier->usPathIndex ] ) ); // CHECKING IF WE HAVE A HIT FINISH BUT NO DEATH IS DONE WITH A SPECIAL ANI CODE // IN THE HIT FINSIH ANI SCRIPTS // CHECKING IF WE HAVE FINISHED A DEATH ANIMATION IS DONE WITH A SPECIAL ANI CODE // IN THE DEATH SCRIPTS // CHECK IF THIS NEW STATE IS NON-INTERRUPTABLE // IF SO - SET NON-INT FLAG if ( uiNewAnimFlags & ANIM_NONINTERRUPT ) { pSoldier->fInNonintAnim = TRUE; } if ( uiNewAnimFlags & ANIM_RT_NONINTERRUPT ) { pSoldier->fRTInNonintAnim = TRUE; } // CHECK IF WE ARE NOT AIMING, IF NOT, RESET LAST TAGRET! if ( !(gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY ) && !(gAnimControl[ usNewState ].uiFlags & ANIM_FIREREADY ) ) { // ATE: Also check for the transition anims to not reset this // this should have used a flag but we're out of them.... if ( usNewState != READY_RIFLE_STAND && usNewState != READY_RIFLE_PRONE && usNewState != READY_RIFLE_CROUCH && usNewState != ROBOT_SHOOT ) { pSoldier->sLastTarget = NOWHERE; } } // If a special move state, release np aps if ( ( gAnimControl[ usNewState ].uiFlags & ANIM_SPECIALMOVE ) ) { AdjustNoAPToFinishMove( pSoldier, FALSE ); } if ( gAnimControl[ usNewState ].uiFlags & ANIM_UPDATEMOVEMENTMODE ) { if ( pSoldier->bTeam == gbPlayerNum ) { // pSoldier->usUIMovementMode = GetMoveStateBasedOnStance( pSoldier, gAnimControl[ usNewState ].ubEndHeight ); } } // ATE: If not a moving animation - turn off reverse.... if ( !( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING ) ) { pSoldier->bReverse = FALSE; } // ONLY DO FOR EVERYONE BUT PLANNING GUYS if ( pSoldier->ubID < MAX_NUM_SOLDIERS ) { // Do special things based on new state switch( usNewState ) { case STANDING: // Update desired height pSoldier->ubDesiredHeight = ANIM_STAND; break; case CROUCHING: // Update desired height pSoldier->ubDesiredHeight = ANIM_CROUCH; break; case PRONE: // Update desired height pSoldier->ubDesiredHeight = ANIM_PRONE; break; case READY_RIFLE_STAND: case READY_RIFLE_PRONE: case READY_RIFLE_CROUCH: case READY_DUAL_STAND: case READY_DUAL_CROUCH: case READY_DUAL_PRONE: // OK, get points to ready weapon.... if ( !pSoldier->fDontChargeReadyAPs ) { sAPCost = GetAPsToReadyWeapon( pSoldier, usNewState ); DeductPoints( pSoldier, sAPCost, sBPCost ); } else { pSoldier->fDontChargeReadyAPs = FALSE; } break; case WALKING: pSoldier->usPendingAnimation = NO_PENDING_ANIMATION; pSoldier->ubPendingActionAnimCount = 0; break; case SWATTING: pSoldier->usPendingAnimation = NO_PENDING_ANIMATION; pSoldier->ubPendingActionAnimCount = 0; break; case CRAWLING: // Turn off flag... pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF; pSoldier->ubPendingActionAnimCount = 0; pSoldier->usPendingAnimation = NO_PENDING_ANIMATION; break; case RUNNING: // Only if our previous is not running if ( pSoldier->usAnimState != RUNNING ) { sAPCost = AP_START_RUN_COST; DeductPoints( pSoldier, sAPCost, sBPCost ); } // Set pending action count to 0 pSoldier->ubPendingActionAnimCount = 0; pSoldier->usPendingAnimation = NO_PENDING_ANIMATION; break; case ADULTMONSTER_WALKING: pSoldier->ubPendingActionAnimCount = 0; break; case ROBOT_WALK: pSoldier->ubPendingActionAnimCount = 0; break; case KNEEL_UP: case KNEEL_DOWN: case BIGMERC_CROUCH_TRANS_INTO: case BIGMERC_CROUCH_TRANS_OUTOF: if ( !pSoldier->fDontChargeAPsForStanceChange ) { DeductPoints( pSoldier, AP_CROUCH, BP_CROUCH ); } pSoldier->fDontChargeAPsForStanceChange = FALSE; break; case PRONE_UP: case PRONE_DOWN: // ATE: If we are NOT waiting for prone down... if ( pSoldier->fTurningFromPronePosition < TURNING_FROM_PRONE_START_UP_FROM_MOVE && !pSoldier->fDontChargeAPsForStanceChange ) { // ATE: Don't do this if we are still 'moving'.... if ( pSoldier->sGridNo == pSoldier->sFinalDestination || pSoldier->usPathIndex == 0 ) { DeductPoints( pSoldier, AP_PRONE, BP_PRONE ); } } pSoldier->fDontChargeAPsForStanceChange = FALSE; break; //Deduct points for stance change //sAPCost = GetAPsToChangeStance( pSoldier, gAnimControl[ usNewState ].ubEndHeight ); //DeductPoints( pSoldier, sAPCost, 0 ); //break; case START_AID: DeductPoints( pSoldier, AP_START_FIRST_AID, BP_START_FIRST_AID ); break; case CUTTING_FENCE: DeductPoints( pSoldier, AP_USEWIRECUTTERS, BP_USEWIRECUTTERS ); break; case PLANT_BOMB: DeductPoints( pSoldier, AP_DROP_BOMB, 0 ); break; case STEAL_ITEM: DeductPoints( pSoldier, AP_STEAL_ITEM, 0 ); break; case CROW_DIE: // Delete shadow of crow.... if ( pSoldier->pAniTile != NULL ) { DeleteAniTile( pSoldier->pAniTile ); pSoldier->pAniTile = NULL; } break; case CROW_FLY: // Ate: startup a shadow ( if gridno is set ) HandleCrowShadowNewGridNo( pSoldier ); break; case CROW_EAT: // ATE: Make sure height level is 0.... SetSoldierHeight( pSoldier, (FLOAT)(0) ); HandleCrowShadowRemoveGridNo( pSoldier ); break; case USE_REMOTE: DeductPoints( pSoldier, AP_USE_REMOTE, 0 ); break; //case PUNCH: //Deduct points for punching //sAPCost = MinAPsToAttack( pSoldier, pSoldier->sGridNo, FALSE ); //DeductPoints( pSoldier, sAPCost, 0 ); //break; case HOPFENCE: DeductPoints( pSoldier, AP_JUMPFENCE, BP_JUMPFENCE ); break; // Deduct aps for falling down.... case FALLBACK_HIT_STAND: case FALLFORWARD_FROMHIT_STAND: DeductPoints( pSoldier, AP_FALL_DOWN, BP_FALL_DOWN ); break; case FALLFORWARD_FROMHIT_CROUCH: DeductPoints( pSoldier, (AP_FALL_DOWN/2), (BP_FALL_DOWN/2) ); break; case QUEEN_SWIPE: // ATE: set damage counter... pSoldier->uiPendingActionData1 = 0; break; case CLIMBDOWNROOF: // disable sight gTacticalStatus.uiFlags |= DISALLOW_SIGHT; DeductPoints( pSoldier, AP_CLIMBOFFROOF, BP_CLIMBOFFROOF ); break; case CLIMBUPROOF: // disable sight gTacticalStatus.uiFlags |= DISALLOW_SIGHT; DeductPoints( pSoldier, AP_CLIMBROOF, BP_CLIMBROOF ); break; case JUMP_OVER_BLOCKING_PERSON: // Set path.... { UINT16 usNewGridNo; DeductPoints( pSoldier, AP_JUMP_OVER, BP_JUMP_OVER ); usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( pSoldier->bDirection ) ); usNewGridNo = NewGridNo( (UINT16)usNewGridNo, DirectionInc( pSoldier->bDirection ) ); pSoldier->usPathDataSize = 0; pSoldier->usPathIndex = 0; pSoldier->usPathingData[ pSoldier->usPathDataSize ] = pSoldier->bDirection; pSoldier->usPathDataSize++; pSoldier->usPathingData[ pSoldier->usPathDataSize ] = pSoldier->bDirection; pSoldier->usPathDataSize++; pSoldier->sFinalDestination = usNewGridNo; // Set direction EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, JUMP_OVER_BLOCKING_PERSON ); } break; case GENERIC_HIT_STAND: case GENERIC_HIT_CROUCH: case STANDING_BURST_HIT: case ADULTMONSTER_HIT: case ADULTMONSTER_DYING: case COW_HIT: case COW_DYING: case BLOODCAT_HIT: case BLOODCAT_DYING: case WATER_HIT: case WATER_DIE: case DEEP_WATER_HIT: case DEEP_WATER_DIE: case RIFLE_STAND_HIT: case LARVAE_HIT: case LARVAE_DIE: case QUEEN_HIT: case QUEEN_DIE: case INFANT_HIT: case INFANT_DIE: case CRIPPLE_HIT: case CRIPPLE_DIE: case CRIPPLE_DIE_FLYBACK: case ROBOTNW_HIT: case ROBOTNW_DIE: // Set getting hit flag to TRUE pSoldier->fGettingHit = TRUE; break; case CHARIOTS_OF_FIRE: case BODYEXPLODING: // Merc on fire! pSoldier->uiPendingActionData1 = PlaySoldierJA2Sample( pSoldier->ubID, ( FIRE_ON_MERC ), RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 5, SoundDir( pSoldier->sGridNo ), TRUE ); break; } } // Remove old animation profile HandleAnimationProfile( pSoldier, pSoldier->usAnimState, TRUE ); // From animation control, set surface if ( SetSoldierAnimationSurface( pSoldier, usNewState ) == FALSE ) { return( FALSE ); } // Set state pSoldier->usOldAniState = pSoldier->usAnimState; pSoldier->sOldAniCode = pSoldier->usAniCode; // Change state value! pSoldier->usAnimState = usNewState; // Set current frame pSoldier->usAniCode = usStartingAniCode; // Handle cleanup stuff for getting hit. Shouldn't this be part of the animation script? CheckForFreeupFromHit( pSoldier, uiOldAnimFlags, uiNewAnimFlags, pSoldier->usOldAniState, usNewState ); // Perform attack busy stuff if (pSoldier->usOldAniState != pSoldier->usAnimState) { if ( uiNewAnimFlags & ANIM_ATTACK ) { gTacticalStatus.ubAttackBusyCount++; DebugAttackBusy( String( "**** Attack animation transfer to %s for %d.\nABC now %d\n", gAnimControl[ usNewState ].zAnimStr, pSoldier->ubID, gTacticalStatus.ubAttackBusyCount ) ); } else if (uiOldAnimFlags & ANIM_ATTACK || pSoldier->fChangingStanceDueToSuppression ) { DebugAttackBusy( String( "**** Transfer to %s for %d.\n", gAnimControl[ usNewState ].zAnimStr, pSoldier->ubID ) ); } if (uiOldAnimFlags & ANIM_ATTACK ) { DebugAttackBusy( String( "**** Attack animation transfer from %s for %d. Reducing ABC.\n", gAnimControl[ pSoldier->usOldAniState ].zAnimStr, pSoldier->ubID ) ); ReduceAttackBusyCount( ); } else if (uiNewAnimFlags & ANIM_ATTACK || pSoldier->fChangingStanceDueToSuppression ) { DebugAttackBusy( String( "**** Transfer from %s for %d\n", gAnimControl[ pSoldier->usOldAniState ].zAnimStr, pSoldier->ubID ) ); } } if (pSoldier->fChangingStanceDueToSuppression) { pSoldier->fChangingStanceDueToSuppression = FALSE; DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker - end of suppression stance change") ); DebugAttackBusy( String( "@@@@@@@ Freeing up attacker - end of suppression stance change for %d\n", pSoldier->ubID ) ); ReduceAttackBusyCount( ); } pSoldier->sZLevelOverride = -1; if ( !( pSoldier->uiStatusFlags & SOLDIER_LOCKPENDINGACTIONCOUNTER ) ) { //ATE Cancel ANY pending action... if ( pSoldier->ubPendingActionAnimCount > 0 && ( gAnimControl[ pSoldier->usOldAniState ].uiFlags & ANIM_MOVING ) ) { // Do some special things for some actions switch( pSoldier->ubPendingAction ) { case MERC_GIVEITEM: // Unset target as enaged MercPtrs[ pSoldier->uiPendingActionData4 ]->uiStatusFlags &= (~SOLDIER_ENGAGEDINACTION); break; } pSoldier->ubPendingAction = NO_PENDING_ACTION; } else { // Increment this for almost all animations except some movement ones... // That's because this represents ANY animation other than the one we began when the pending action was started // ATE: Added to ignore this count if we are waiting for someone to move out of our way... if ( usNewState != START_SWAT && usNewState != END_SWAT && !( gAnimControl[ usNewState ].uiFlags & ANIM_NOCHANGE_PENDINGCOUNT ) && !pSoldier->fDelayedMovement && !( pSoldier->uiStatusFlags & SOLDIER_ENGAGEDINACTION ) ) { pSoldier->ubPendingActionAnimCount++; } } } // Set new animation profile HandleAnimationProfile( pSoldier, usNewState, FALSE ); // Reset some animation values pSoldier->fForceShade = FALSE; // CHECK IF WE ARE AT AN IDLE ACTION #if 0 if ( gAnimControl[ usNewState ].uiFlags & ANIM_IDLE ) { pSoldier->bAction = ACTION_DONE; } else { pSoldier->bAction = ACTION_BUSY; } #endif // ATE; For some animations that could use some variations, do so.... if (usNewState == CHARIOTS_OF_FIRE || usNewState == BODYEXPLODING ) { pSoldier->usAniCode = (UINT16)( Random( 10 ) ); } // ATE: Default to first frame.... // Will get changed ( probably ) by AdjustToNextAnimationFrame() ConvertAniCodeToAniFrame( pSoldier, (INT16)( 0 ) ); // Set delay speed SetSoldierAniSpeed( pSoldier ); // Reset counters RESETTIMECOUNTER( pSoldier->UpdateCounter, pSoldier->sAniDelay ); // Adjust to new animation frame ( the first one ) AdjustToNextAnimationFrame( pSoldier ); // Setup offset information for UI above guy SetSoldierLocatorOffsets( pSoldier ); // Lesh: test fix visibility after raising gun if ( ( gAnimControl[ pSoldier->usOldAniState ].uiFlags & ANIM_RAISE_WEAPON) && (gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY) ) //equivalent if ( (pSoldier->usAnimState == AIM_RIFLE_PRONE) || (pSoldier->usAnimState == AIM_RIFLE_CROUCH) || (pSoldier->usAnimState == AIM_RIFLE_STAND) ) { if ( (pSoldier->usOldAniState == READY_RIFLE_STAND) || (pSoldier->usOldAniState == READY_RIFLE_CROUCH) || (pSoldier->usOldAniState == READY_RIFLE_PRONE) ) { HandleSight(pSoldier,SIGHT_LOOK); } } // If our own guy... if ( pSoldier->bTeam == gbPlayerNum ) { // Are we stationary? if ( gAnimControl[ usNewState ].uiFlags & ANIM_STATIONARY ) { // Position light.... // SetCheckSoldierLightFlag( pSoldier ); } else { // Hide light..... // DeleteSoldierLight( pSoldier ); } } // If we are certain animations, reload palette if ( usNewState == VEHICLE_DIE || usNewState == CHARIOTS_OF_FIRE || usNewState == BODYEXPLODING ) { CreateSoldierPalettes( pSoldier ); } // ATE: if the old animation was a movement, and new is not, play sound... // OK, play final footstep sound... if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) ) { if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_STATIONARY ) && ( gAnimControl[ pSoldier->usOldAniState ].uiFlags & ANIM_MOVING ) ) { PlaySoldierFootstepSound( pSoldier ); } } // Free up from stance change FreeUpNPCFromStanceChange( pSoldier ); return( TRUE ); } void InternalRemoveSoldierFromGridNo( SOLDIERTYPE *pSoldier, BOOLEAN fForce ) { INT8 bDir; INT32 iGridNo; if((pSoldier->sGridNo!=NO_MAP_POS) ) { if ( pSoldier->bInSector || fForce ) { // Remove from world ( old pos ) RemoveMerc( pSoldier->sGridNo, pSoldier, FALSE ); HandleAnimationProfile( pSoldier, pSoldier->usAnimState, TRUE ); // Remove records of this guy being adjacent for (bDir = 0; bDir < NUM_WORLD_DIRECTIONS; bDir++) { iGridNo = pSoldier->sGridNo + DirIncrementer[ bDir ]; if( iGridNo >= 0 && iGridNo < WORLD_MAX ) { gpWorldLevelData[ iGridNo ].ubAdjacentSoldierCnt--; } } HandlePlacingRoofMarker( pSoldier, pSoldier->sGridNo, FALSE , FALSE ); // Remove reseved movement value UnMarkMovementReserved( pSoldier ); HandleCrowShadowRemoveGridNo( pSoldier ); // Reset gridno... pSoldier->sGridNo = NO_MAP_POS; } } } void RemoveSoldierFromGridNo( SOLDIERTYPE *pSoldier ) { InternalRemoveSoldierFromGridNo( pSoldier, FALSE ); } void EVENT_InternalSetSoldierPosition( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos ,BOOLEAN fUpdateDest, BOOLEAN fUpdateFinalDest, BOOLEAN fForceRemove ) { INT16 sNewGridNo; // Not if we're dead! if ( ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) ) { return; } // Set new map index sNewGridNo = GETWORLDINDEXFROMWORLDCOORDS(dNewYPos, dNewXPos ); if ( fUpdateDest ) { pSoldier->sDestination = sNewGridNo; } if ( fUpdateFinalDest ) { pSoldier->sFinalDestination = sNewGridNo; } // Copy old values pSoldier->dOldXPos = pSoldier->dXPos; pSoldier->dOldYPos = pSoldier->dYPos; // Set New pos pSoldier->dXPos = dNewXPos; pSoldier->dYPos = dNewYPos; pSoldier->sX = (INT16)dNewXPos; pSoldier->sY = (INT16)dNewYPos; HandleCrowShadowNewPosition( pSoldier ); SetSoldierGridNo( pSoldier, sNewGridNo, fForceRemove ); if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) ) { if ( gGameSettings.fOptions[ TOPTION_MERC_ALWAYS_LIGHT_UP ] ) { SetCheckSoldierLightFlag( pSoldier ); } } // ATE: Mirror calls if we are a vehicle ( for all our passengers ) UpdateAllVehiclePassengersGridNo( pSoldier ); } void EVENT_SetSoldierPosition( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos ) { EVENT_InternalSetSoldierPosition( pSoldier, dNewXPos, dNewYPos ,TRUE, TRUE, FALSE ); } void EVENT_SetSoldierPositionForceDelete( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos ) { EVENT_InternalSetSoldierPosition( pSoldier, dNewXPos, dNewYPos ,TRUE, TRUE, TRUE ); } void EVENT_SetSoldierPositionAndMaybeFinalDest( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos, BOOLEAN fUpdateFinalDest ) { EVENT_InternalSetSoldierPosition( pSoldier, dNewXPos, dNewYPos ,TRUE, fUpdateFinalDest, FALSE ); } void EVENT_SetSoldierPositionAndMaybeFinalDestAndMaybeNotDestination( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos, BOOLEAN fUpdateDest, BOOLEAN fUpdateFinalDest ) { EVENT_InternalSetSoldierPosition( pSoldier, dNewXPos, dNewYPos ,fUpdateDest, fUpdateFinalDest, FALSE ); } void InternalSetSoldierHeight( SOLDIERTYPE *pSoldier, FLOAT dNewHeight, BOOLEAN fUpdateLevel ) { INT8 bOldLevel = pSoldier->bLevel; pSoldier->dHeightAdjustment = dNewHeight; pSoldier->sHeightAdjustment = (INT16)pSoldier->dHeightAdjustment; if ( !fUpdateLevel ) { return; } if ( pSoldier->sHeightAdjustment > 0 ) { pSoldier->bLevel = SECOND_LEVEL; ApplyTranslucencyToWalls((INT16)(pSoldier->dXPos/CELL_X_SIZE), (INT16)(pSoldier->dYPos/CELL_Y_SIZE)); //LightHideTrees((INT16)(pSoldier->dXPos/CELL_X_SIZE), (INT16)(pSoldier->dYPos/CELL_Y_SIZE)); //ConcealAllWalls(); //pSoldier->pLevelNode->ubShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubShadeLevel; //pSoldier->pLevelNode->ubSumLights=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubSumLights; //pSoldier->pLevelNode->ubMaxLights=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubMaxLights; //pSoldier->pLevelNode->ubNaturalShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubNaturalShadeLevel; } else { pSoldier->bLevel = FIRST_LEVEL; //pSoldier->pLevelNode->ubShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubShadeLevel; //pSoldier->pLevelNode->ubSumLights=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubSumLights; //pSoldier->pLevelNode->ubMaxLights=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubMaxLights; //pSoldier->pLevelNode->ubNaturalShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubNaturalShadeLevel; } if ( bOldLevel == 0 && pSoldier->bLevel == 0 ) { } else { // Show room at new level //HideRoom( pSoldier->sGridNo, pSoldier ); } } void SetSoldierHeight( SOLDIERTYPE *pSoldier, FLOAT dNewHeight ) { InternalSetSoldierHeight( pSoldier, dNewHeight, TRUE ); } void SetSoldierGridNo( SOLDIERTYPE *pSoldier, INT16 sNewGridNo, BOOLEAN fForceRemove ) { BOOLEAN fInWaterValue; INT8 bDir; INT32 cnt; SOLDIERTYPE * pEnemy; //INT16 sX, sY, sWorldX, sZLevel; // Not if we're dead! if ( ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) ) { return; } if ( sNewGridNo != pSoldier->sGridNo || pSoldier->pLevelNode == NULL ) { // Check if we are moving AND this is our next dest gridno.... if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_MOVING | ANIM_SPECIALMOVE ) ) { if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) ) { if ( sNewGridNo != pSoldier->sDestination ) { // THIS MUST be our new one......MAKE IT SO sNewGridNo = pSoldier->sDestination; } // Now check this baby.... if ( sNewGridNo == pSoldier->sGridNo ) { return; } } } pSoldier->sOldGridNo = pSoldier->sGridNo; if ( pSoldier->ubBodyType == QUEENMONSTER ) { SetPositionSndGridNo( pSoldier->iPositionSndID, sNewGridNo ); } if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) ) { InternalRemoveSoldierFromGridNo( pSoldier, fForceRemove ); } // CHECK IF OUR NEW GIRDNO IS VALID,IF NOT DONOT SET! if ( !GridNoOnVisibleWorldTile( sNewGridNo ) ) { pSoldier->sGridNo = sNewGridNo; return; } // Alrighty, update UI for this guy, if he's the selected guy... if ( gusSelectedSoldier == pSoldier->ubID ) { if ( guiCurrentEvent == C_WAIT_FOR_CONFIRM ) { // Update path! gfPlotNewMovement = TRUE; } } // Reset some flags for optimizations.. pSoldier->sWalkToAttackGridNo = NOWHERE; // ATE: Make sure! // RemoveMerc( pSoldier->sGridNo, pSoldier, FALSE ); pSoldier->sGridNo = sNewGridNo; // OK, check for special code to close door... if ( pSoldier->bEndDoorOpenCode == 2 ) { pSoldier->bEndDoorOpenCode = 0; HandleDoorChangeFromGridNo( pSoldier, pSoldier->sEndDoorOpenCodeData, FALSE ); } // OK, Update buddy's strategic insertion code.... pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; pSoldier->usStrategicInsertionData = sNewGridNo; // Remove this gridno as a reserved place! if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) ) { UnMarkMovementReserved( pSoldier ); } if ( pSoldier->sInitialGridNo == 0 ) { pSoldier->sInitialGridNo = sNewGridNo; pSoldier->usPatrolGrid[0] = sNewGridNo; } // Add records of this guy being adjacent for (bDir = 0; bDir < NUM_WORLD_DIRECTIONS; bDir++) { gpWorldLevelData[ pSoldier->sGridNo + DirIncrementer[ bDir ] ].ubAdjacentSoldierCnt++; } if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) ) { DropSmell( pSoldier ); } // HANDLE ANY SPECIAL RENDERING SITUATIONS pSoldier->sZLevelOverride = -1; // If we are over a fence ( hopping ), make us higher! if ( IsJumpableFencePresentAtGridno( sNewGridNo ) ) { //sX = MapX( sNewGridNo ); //sY = MapY( sNewGridNo ); //GetWorldXYAbsoluteScreenXY( sX, sY, &sWorldX, &sZLevel); //pSoldier->sZLevelOverride = (sZLevel*Z_SUBLAYERS)+ROOF_Z_LEVEL; pSoldier->sZLevelOverride = TOPMOST_Z_LEVEL; } // Add/ remove tree if we are near it // CheckForFullStructures( pSoldier ); // Add merc at new pos if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) ) { AddMercToHead( pSoldier->sGridNo, pSoldier, TRUE ); // If we are in the middle of climbing the roof! if ( pSoldier->usAnimState == CLIMBUPROOF ) { if(pSoldier->iLight!=(-1)) LightSpriteRoofStatus(pSoldier->iLight, TRUE ); } else if ( pSoldier->usAnimState == CLIMBDOWNROOF ) { if(pSoldier->iLight!=(-1)) LightSpriteRoofStatus(pSoldier->iLight, FALSE ); } //JA2Gold: //if the player wants the merc to cast the fake light AND it is night if( pSoldier->bTeam != OUR_TEAM || gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] && NightTime() ) { if ( pSoldier->bLevel > 0 && gpWorldLevelData[pSoldier->sGridNo].pRoofHead != NULL ) { gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubShadeLevel; gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubSumLights=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubSumLights; gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubMaxLights=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubMaxLights; gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubNaturalShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubNaturalShadeLevel; } else { gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubShadeLevel; gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubSumLights=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubSumLights; gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubMaxLights=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubMaxLights; gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubNaturalShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubNaturalShadeLevel; } } ///HandlePlacingRoofMarker( pSoldier, pSoldier->sGridNo, TRUE, FALSE ); HandleAnimationProfile( pSoldier, pSoldier->usAnimState, FALSE ); HandleCrowShadowNewGridNo( pSoldier ); } pSoldier->bOldOverTerrainType = pSoldier->bOverTerrainType; pSoldier->bOverTerrainType = GetTerrainType( pSoldier->sGridNo ); // OK, check that our animation is up to date! // Check our water value if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) ) { fInWaterValue = MercInWater( pSoldier ); // ATE: If ever in water MAKE SURE WE WALK AFTERWOODS! if ( fInWaterValue ) { pSoldier->usUIMovementMode = WALKING; } if ( fInWaterValue != pSoldier->fPrevInWater ) { //Update Animation data SetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState ); // Update flag pSoldier->fPrevInWater = fInWaterValue; // Update sound... if ( fInWaterValue ) { PlaySoldierJA2Sample( pSoldier->ubID, ENTER_WATER_1, RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ), TRUE ); } else { // ATE: Check if we are going from water to land - if so, resume // with regular movement mode... EVENT_InitNewSoldierAnim( pSoldier, pSoldier->usUIMovementMode, 0 , FALSE ); } } // OK, If we were not in deep water but we are now, handle deep animations! if ( pSoldier->bOverTerrainType == DEEP_WATER && pSoldier->bOldOverTerrainType != DEEP_WATER ) { // Based on our current animation, change! switch( pSoldier->usAnimState ) { case WALKING: case RUNNING: // IN deep water, swim! // Make transition from low to deep EVENT_InitNewSoldierAnim( pSoldier, LOW_TO_DEEP_WATER, 0 , FALSE ); pSoldier->usPendingAnimation = DEEP_WATER_SWIM; PlayJA2Sample( ENTER_DEEP_WATER_1, RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); } } // Damage water if in deep water.... if ( pSoldier->bOverTerrainType == MED_WATER || pSoldier->bOverTerrainType == DEEP_WATER ) { WaterDamage( pSoldier ); } // OK, If we were in deep water but we are NOT now, handle mid animations! if ( pSoldier->bOverTerrainType != DEEP_WATER && pSoldier->bOldOverTerrainType == DEEP_WATER ) { // Make transition from low to deep EVENT_InitNewSoldierAnim( pSoldier, DEEP_TO_LOW_WATER, 0 , FALSE ); pSoldier->usPendingAnimation = pSoldier->usUIMovementMode; } } // are we now standing in tear gas without a decently working gas mask? if ( GetSmokeEffectOnTile( sNewGridNo, pSoldier->bLevel ) > 1 ) //lal: removed normal smoke { BOOLEAN fSetGassed = TRUE; // If we have a functioning gas mask... if ( FindGasMask ( pSoldier ) != NO_SLOT && pSoldier->inv[ HEAD1POS ].bStatus[ 0 ] >= GASMASK_MIN_STATUS ) { fSetGassed = FALSE; } // if ( pSoldier->inv[ HEAD1POS ].usItem == GASMASK && pSoldier->inv[ HEAD1POS ].bStatus[ 0 ] >= GASMASK_MIN_STATUS ) //{ // fSetGassed = FALSE; //} // if ( pSoldier->inv[ HEAD2POS ].usItem == GASMASK && pSoldier->inv[ HEAD2POS ].bStatus[ 0 ] >= GASMASK_MIN_STATUS ) //{ // fSetGassed = FALSE; //} if ( fSetGassed ) { pSoldier->uiStatusFlags |= SOLDIER_GASSED; } } if ( pSoldier->bTeam == gbPlayerNum && pSoldier->bStealthMode ) { // Merc got to a new tile by "sneaking". Did we theoretically sneak // past an enemy? if ( pSoldier->bOppCnt > 0 ) // opponents in sight { // check each possible enemy for ( cnt = 0; cnt < MAX_NUM_SOLDIERS; cnt++ ) { pEnemy = MercPtrs[ cnt ]; // if this guy is here and alive enough to be looking for us if ( pEnemy->bActive && pEnemy->bInSector && ( pEnemy->bLife >= OKLIFE ) ) { // no points for sneaking by the neutrals & friendlies!!! if ( !pEnemy->bNeutral && ( pSoldier->bSide != pEnemy->bSide ) && (pEnemy->ubBodyType != COW && pEnemy->ubBodyType != CROW) ) { // if we SEE this particular oppponent, and he DOESN'T see us... and he COULD see us... if ( (pSoldier->bOppList[ cnt ] == SEEN_CURRENTLY) && pEnemy->bOppList[ pSoldier->ubID ] != SEEN_CURRENTLY && PythSpacesAway( pSoldier->sGridNo, pEnemy->sGridNo ) < DistanceVisible( pEnemy, DIRECTION_IRRELEVANT, DIRECTION_IRRELEVANT, pSoldier->sGridNo, pSoldier->bLevel, pSoldier ) ) { // AGILITY (5): Soldier snuck 1 square past unaware enemy StatChange( pSoldier, AGILAMT, 5, FALSE ); // Keep looping, we'll give'em 1 point for EACH such enemy! } } } } } } // Adjust speed based on terrain, etc SetSoldierAniSpeed( pSoldier ); } else { int i = 0; } } void EVENT_FireSoldierWeapon( SOLDIERTYPE *pSoldier, INT16 sTargetGridNo ) { INT16 sTargetXPos, sTargetYPos; BOOLEAN fDoFireRightAway = FALSE; DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("EVENT_FireSoldierWeapon")); // CANNOT BE SAME GRIDNO! if ( pSoldier->sGridNo == sTargetGridNo ) { return; } if ( pSoldier->ubID == 33 ) { int i = 0; } //switch ( pSoldier->inv[ pSoldier->ubAttackingHand ].ubGunAmmoType ) //{ // case AMMO_SLEEP_DART: // pSoldier->fMuzzleFlash = FALSE; // break; // default: // pSoldier->fMuzzleFlash = TRUE; // break; //} #if 0 // 0verhaul: This does not go here! In spite of this function's name, it is not the actual "fire" function. // In fact this sets the muzzle flash even while the soldier may be turning to shoot, which can cause // problems for real-time shooting. // The correct place for this is UseGun, which already has code to set or reset the flash. if ( IsFlashSuppressor (&pSoldier->inv[ pSoldier->ubAttackingHand ], pSoldier ) ) pSoldier->fMuzzleFlash = FALSE; else pSoldier->fMuzzleFlash = TRUE; #endif DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("EVENT_FireSoldierWeapon: Muzzle flash = %d",pSoldier->fMuzzleFlash)); // Increment the number of people busy doing stuff because of an attack //if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) ) //{ // Nah, just let the animations speak for themselves // gTacticalStatus.ubAttackBusyCount++; DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting attack, attack count now %d", gTacticalStatus.ubAttackBusyCount) ); DebugAttackBusy( String("!!!!!!! Starting fire weapon attack, attack count now %d\n", gTacticalStatus.ubAttackBusyCount) ); //} // Set soldier's target gridno // This assignment was redundent because it's already set in // the actual event call pSoldier->sTargetGridNo = sTargetGridNo; //pSoldier->sLastTarget = sTargetGridNo; pSoldier->ubTargetID = WhoIsThere2( sTargetGridNo, pSoldier->bTargetLevel ); #if 0 // if (Item[pSoldier->inv[HANDPOS].usItem].usItemClass & IC_GUN) { if (pSoldier->bDoBurst) { // This is NOT the bullets to fire. That is done as a check of bDoBurst against the weapon burst count or // bDoAutofire, or single-fire. So let the bullet count be managed by the firing code. // Set the TOTAL number of bullets to be fired // Can't shoot more bullets than we have in our magazine! if(pSoldier->bDoAutofire) pSoldier->bBulletsLeft = __min( pSoldier->bDoAutofire, pSoldier->inv[ pSoldier->ubAttackingHand ].ubGunShotsLeft ); else { DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"EVENT_FireSoldierWeapon: do burst"); if ( pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST ) pSoldier->bBulletsLeft = __min( Weapon[GetAttachedGrenadeLauncher(&pSoldier->inv[pSoldier->ubAttackingHand])].ubShotsPerBurst, pSoldier->inv[ pSoldier->ubAttackingHand ].ubGunShotsLeft ); else pSoldier->bBulletsLeft = __min( GetShotsPerBurst(&pSoldier->inv[ pSoldier->ubAttackingHand ]), pSoldier->inv[ pSoldier->ubAttackingHand ].ubGunShotsLeft ); } } else if ( IsValidSecondHandShot( pSoldier ) ) { // two-pistol attack - two bullets! pSoldier->bBulletsLeft = 2; } else { pSoldier->bBulletsLeft = 1; } if ( AmmoTypes[pSoldier->inv[ pSoldier->ubAttackingHand ].ubGunAmmoType].numberOfBullets > 1 ) { pSoldier->bBulletsLeft *= AmmoTypes[pSoldier->inv[ pSoldier->ubAttackingHand ].ubGunAmmoType].numberOfBullets; } } #endif DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting attack, bullets left %d", pSoldier->bBulletsLeft) ); // Convert our grid-not into an XY ConvertGridNoToXY( sTargetGridNo, &sTargetXPos, &sTargetYPos ); // Change to fire animation // Ready weapon SoldierReadyWeapon( pSoldier, sTargetXPos, sTargetYPos, FALSE ); // IF WE ARE AN NPC, SLIDE VIEW TO SHOW WHO IS SHOOTING { //if ( pSoldier->fDoSpread ) //{ // If we are spreading burst, goto right away! //EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ), 0, FALSE ); //} // else { if (pSoldier->uiStatusFlags & SOLDIER_MONSTER ) { // Force our direction! EVENT_SetSoldierDirection( pSoldier, pSoldier->bDesiredDirection ); EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ), 0, FALSE ); } else { // IF WE ARE IN REAl-TIME, FIRE IMMEDIATELY! if ( ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) ) { //fDoFireRightAway = TRUE; } // Check if our weapon has no intermediate anim... if (Item[pSoldier->inv[ HANDPOS ].usItem].rocketlauncher || Item[pSoldier->inv[ HANDPOS ].usItem].grenadelauncher || Item[pSoldier->inv[ HANDPOS ].usItem].mortar ) ///* switch( pSoldier->inv[ HANDPOS ].usItem ) // { //case RPG7: //case ROCKET_LAUNCHER: //case MORTAR: //case GLAUNCHER:*/ fDoFireRightAway = TRUE; // break; //} if ( fDoFireRightAway ) { // Set to true so we don't get toasted twice for APs.. pSoldier->fDontUnsetLastTargetFromTurn = TRUE; // Make sure we don't try and do fancy prone turning..... pSoldier->fTurningFromPronePosition = FALSE; // Force our direction! EVENT_SetSoldierDirection( pSoldier, pSoldier->bDesiredDirection ); EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ), 0, FALSE ); } else { // Set flag indicating we are about to shoot once destination direction is hit pSoldier->fTurningToShoot = TRUE; if ( pSoldier->bTeam != gbPlayerNum && pSoldier->bVisible != -1) { LocateSoldier( pSoldier->ubID, DONTSETLOCATOR ); } } } } } } //gAnimControl[ pSoldier->usAnimState ].ubEndHeight // ChangeSoldierState( pSoldier, SHOOT_RIFLE_STAND, 0 , FALSE ); UINT16 SelectFireAnimation( SOLDIERTYPE *pSoldier, UINT8 ubHeight ) { INT16 sDist; UINT16 usItem; FLOAT dTargetX; FLOAT dTargetY; FLOAT dTargetZ; BOOLEAN fDoLowShot = FALSE; DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("SelectFireAnimation")); //Do different things if we are a monster if (pSoldier->uiStatusFlags & SOLDIER_MONSTER) { switch( pSoldier->ubBodyType ) { case ADULTFEMALEMONSTER: case AM_MONSTER: case YAF_MONSTER: case YAM_MONSTER: return( MONSTER_SPIT_ATTACK ); break; case LARVAE_MONSTER: break; case INFANT_MONSTER: return( INFANT_ATTACK ); break; case QUEENMONSTER: return( QUEEN_SPIT ); break; } return( TRUE ); } if ( pSoldier->ubBodyType == ROBOTNOWEAPON ) { if ( pSoldier->bDoBurst > 0 ) { return( ROBOT_BURST_SHOOT ); } else { return( ROBOT_SHOOT ); } } // Check for rocket laucncher.... if ( Item[pSoldier->inv[ HANDPOS ].usItem].rocketlauncher ) { return( SHOOT_ROCKET ); } // Check for mortar.... if ( Item[pSoldier->inv[ HANDPOS ].usItem].mortar ) { DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"SelectFireAnimation: shoot_mortar"); return( SHOOT_MORTAR ); } // Check for tank cannon if ( Item[pSoldier->inv[ HANDPOS ].usItem].cannon ) { return( TANK_SHOOT ); } if ( pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE ) { return( TANK_BURST ); } // Determine which animation to do...depending on stance and gun in hand... switch ( ubHeight ) { case ANIM_STAND: usItem = pSoldier->inv[ HANDPOS ].usItem; // CHECK 2ND HAND! if ( IsValidSecondHandShot( pSoldier ) ) { // Increment the number of people busy doing stuff because of an attack //gTacticalStatus.ubAttackBusyCount++; //DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting attack with 2 guns, attack count now %d", gTacticalStatus.ubAttackBusyCount) ); return( SHOOT_DUAL_STAND ); } else { // OK, while standing check distance away from target, and shoot low if we should! sDist = PythSpacesAway( pSoldier->sGridNo, pSoldier->sTargetGridNo ); //ATE: OK, SEE WERE WE ARE TARGETING.... GetTargetWorldPositions( pSoldier, pSoldier->sTargetGridNo, &dTargetX, &dTargetY, &dTargetZ ); //CalculateSoldierZPos( pSoldier, FIRING_POS, &dFirerZ ); if ( sDist <= 2 && dTargetZ <= 100 ) { fDoLowShot = TRUE; } // ATE: Made distence away long for psitols such that they never use this.... //if ( !(Item[ usItem ].fFlags & ITEM_TWO_HANDED) ) //{ // fDoLowShot = FALSE; //} // Don't do any low shots if in water if ( MercInWater( pSoldier ) ) { fDoLowShot = FALSE; } if ( pSoldier->bDoBurst > 0 ) { if ( fDoLowShot ) { return( FIRE_BURST_LOW_STAND ); } else { return( STANDING_BURST ); } } else { if ( fDoLowShot ) { return( FIRE_LOW_STAND ); } else { return( SHOOT_RIFLE_STAND ); } } } break; case ANIM_PRONE: if ( pSoldier->bDoBurst > 0 ) { // pSoldier->fBurstCompleted = FALSE; return( PRONE_BURST ); } else { if ( IsValidSecondHandShot( pSoldier ) ) { return( SHOOT_DUAL_PRONE ); } else { return( SHOOT_RIFLE_PRONE ); } } break; case ANIM_CROUCH: if ( IsValidSecondHandShot( pSoldier ) ) { // Increment the number of people busy doing stuff because of an attack //gTacticalStatus.ubAttackBusyCount++; //DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting attack with 2 guns, attack count now %d", gTacticalStatus.ubAttackBusyCount) ); return( SHOOT_DUAL_CROUCH ); } else { if ( pSoldier->bDoBurst > 0 ) { // pSoldier->fBurstCompleted = FALSE; return( CROUCHED_BURST ); } else { return( SHOOT_RIFLE_CROUCH ); } } break; default: AssertMsg( FALSE, String( "SelectFireAnimation: ERROR - Invalid height %d", ubHeight ) ); break; } // If here, an internal error has occured! Assert( FALSE ); return ( 0 ); } UINT16 GetMoveStateBasedOnStance( SOLDIERTYPE *pSoldier, UINT8 ubStanceHeight ) { // Determine which animation to do...depending on stance and gun in hand... switch ( ubStanceHeight ) { case ANIM_STAND: if ( pSoldier->fUIMovementFast && !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) ) { return( RUNNING ); } else { return( WALKING ); } break; case ANIM_PRONE: if ( pSoldier->fUIMovementFast ) { return( CRAWLING ); } else { return( CRAWLING ); } break; case ANIM_CROUCH: if ( pSoldier->fUIMovementFast ) { return( SWATTING ); } else { return( SWATTING ); } break; default: AssertMsg( FALSE, String( "GetMoveStateBasedOnStance: ERROR - Invalid height %d", ubStanceHeight ) ); break; } // If here, an internal error has occured! Assert( FALSE ); return ( 0 ); } void SelectFallAnimation( SOLDIERTYPE *pSoldier ) { // Determine which animation to do...depending on stance and gun in hand... switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: EVENT_InitNewSoldierAnim( pSoldier, FLYBACK_HIT, 0 , FALSE ); break; case ANIM_PRONE: EVENT_InitNewSoldierAnim( pSoldier, FLYBACK_HIT, 0 , FALSE ); break; } } BOOLEAN SoldierReadyWeapon( SOLDIERTYPE *pSoldier ) { return( InternalSoldierReadyWeapon( pSoldier, (INT8)pSoldier->bDirection, FALSE ) ); } BOOLEAN SoldierReadyWeapon( SOLDIERTYPE *pSoldier, INT16 sTargetXPos, INT16 sTargetYPos, BOOLEAN fEndReady ) { INT16 sFacingDir; sFacingDir = GetDirectionFromXY( sTargetXPos , sTargetYPos, pSoldier ); return( InternalSoldierReadyWeapon( pSoldier, (INT8)sFacingDir, fEndReady ) ); } BOOLEAN InternalSoldierReadyWeapon( SOLDIERTYPE *pSoldier, UINT8 sFacingDir, BOOLEAN fEndReady ) { UINT16 usAnimState; BOOLEAN fReturnVal = FALSE; // Handle monsters differently if (pSoldier->uiStatusFlags & SOLDIER_MONSTER) { if ( !fEndReady ) { EVENT_SetSoldierDesiredDirection( pSoldier, sFacingDir ); } return( FALSE ); } DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("InternalSoldierReadyWeapon: PickingAnimation")); usAnimState = PickSoldierReadyAnimation( pSoldier, fEndReady ); if ( usAnimState != INVALID_ANIMATION ) { EVENT_InitNewSoldierAnim( pSoldier, usAnimState, 0 , FALSE ); fReturnVal = TRUE; } if ( !fEndReady ) { // Ready direction for new facing direction if ( usAnimState == INVALID_ANIMATION ) { usAnimState = pSoldier->usAnimState; } EVENT_InternalSetSoldierDesiredDirection( pSoldier, sFacingDir, FALSE, usAnimState ); // Check if facing dir is different from ours and change direction if so! //if ( sFacingDir != pSoldier->bDirection ) //{ // DeductPoints( pSoldier, AP_CHANGE_FACING, 0 ); //}// } return( fReturnVal ); } UINT16 PickSoldierReadyAnimation( SOLDIERTYPE *pSoldier, BOOLEAN fEndReady ) { DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("PickSoldierReadyAnimation")); // Invalid animation if nothing in our hands if ( pSoldier->inv[ HANDPOS ].usItem == NOTHING ) { return( INVALID_ANIMATION ); } if ( pSoldier->bOverTerrainType == DEEP_WATER ) { return( INVALID_ANIMATION ); } if ( pSoldier->ubBodyType == ROBOTNOWEAPON ) { return( INVALID_ANIMATION ); } // Check if we have a gun..... if ( Item[ pSoldier->inv[ HANDPOS ].usItem ].usItemClass != IC_GUN && !Item[pSoldier->inv[ HANDPOS ].usItem].grenadelauncher ) { return( INVALID_ANIMATION ); } if ( Item[pSoldier->inv[ HANDPOS ].usItem].rocketlauncher ) { return( INVALID_ANIMATION ); } if ( pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE ) { return( INVALID_ANIMATION ); } if ( fEndReady ) { // IF our gun is already drawn, do not change animation, just direction if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_FIREREADY | ANIM_FIRE )) { switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: // CHECK 2ND HAND! if ( IsValidSecondHandShot( pSoldier ) ) { return( END_DUAL_STAND ); } else { return( END_RIFLE_STAND ); } break; case ANIM_PRONE: if ( IsValidSecondHandShot( pSoldier ) ) { return( END_DUAL_PRONE ); } else { return( END_RIFLE_PRONE ); } break; case ANIM_CROUCH: // CHECK 2ND HAND! if ( IsValidSecondHandShot( pSoldier ) ) { return( END_DUAL_CROUCH ); } else { return( END_RIFLE_CROUCH ); } break; } } } else { // IF our gun is already drawn, do not change animation, just direction if ( !(gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_FIREREADY | ANIM_FIRE ) ) ) { { switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: // CHECK 2ND HAND! if ( IsValidSecondHandShot( pSoldier ) ) { return( READY_DUAL_STAND ); } else { return( READY_RIFLE_STAND ); } break; case ANIM_PRONE: // Go into crouch, turn, then go into prone again //ChangeSoldierStance( pSoldier, ANIM_CROUCH ); //pSoldier->ubDesiredHeight = ANIM_PRONE; //ChangeSoldierState( pSoldier, PRONE_UP ); if ( IsValidSecondHandShot( pSoldier ) ) { return( READY_DUAL_PRONE ); } else { return( READY_RIFLE_PRONE ); } break; case ANIM_CROUCH: // CHECK 2ND HAND! if ( IsValidSecondHandShot( pSoldier ) ) { return( READY_DUAL_CROUCH ); } else { return( READY_RIFLE_CROUCH ); } break; } } } } return( INVALID_ANIMATION ); } // 0verhaul: These routines are obsolete. Just call ReduceAttackBusyCount to reduce the ABC or // FreeUpAttacker to abort the current action. // extern SOLDIERTYPE * FreeUpAttackerGivenTarget( UINT8 ubID, UINT8 ubTargetID ); // extern SOLDIERTYPE * ReduceAttackBusyGivenTarget( UINT8 ubID, UINT8 ubTargetID ); // ATE: THIS FUNCTION IS USED FOR ALL SOLDIER TAKE DAMAGE FUNCTIONS! void EVENT_SoldierGotHit( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, INT16 sBreathLoss, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation, INT16 sSubsequent, INT16 sLocationGrid ) { UINT8 ubCombinedLoss, ubVolume, ubReason; // SOLDIERTYPE * pNewSoldier; ubReason = 0; DebugMsg(TOPIC_JA2,DBG_LEVEL_3 , "EVENT_SoldierGotHit"); #if 0 // 0verhaul: Under the new ABC system this is no longer necessary. // ATE: If we have gotten hit, but are still in our attack animation, reduce count! switch ( pSoldier->usAnimState ) { case SHOOT_ROCKET: case SHOOT_MORTAR: case THROW_ITEM: // crouch throwing case THROW_ITEM_CROUCHED: // crouch throwing case LOB_ITEM: DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker - ATTACK ANIMATION %s ENDED BY HIT ANIMATION, Now %d", gAnimControl[ pSoldier->usAnimState ].zAnimStr, gTacticalStatus.ubAttackBusyCount ) ); ReduceAttackBusyCount( pSoldier->ubID, FALSE ); break; } #endif // DO STUFF COMMON FOR ALL TYPES if ( ubAttackerID != NOBODY) { MercPtrs[ubAttackerID]->bLastAttackHit = TRUE; } // Set attacker's ID pSoldier->ubAttackerID = ubAttackerID; #if 0 // 0verhaul: Slashing out more unnecessary and reworked code if ( !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) ) { // Increment being attacked count pSoldier->bBeingAttackedCount++; } // if defender is a vehicle, there will be no hit animation played! if ( !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) ) { // Increment the number of people busy doing stuff because of an attack (busy doing hit anim!) gTacticalStatus.ubAttackBusyCount++; DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Person got hit, attack count now %d", gTacticalStatus.ubAttackBusyCount) ); } #endif // ATE; Save hit location info...( for later anim determination stuff ) pSoldier->ubHitLocation = ubHitLocation; // handle morale for heavy damage attacks if ( sDamage > 25 ) { if ( pSoldier->ubAttackerID != NOBODY && MercPtrs[ pSoldier->ubAttackerID ]->bTeam == gbPlayerNum ) { HandleMoraleEvent( MercPtrs[ pSoldier->ubAttackerID ], MORALE_DID_LOTS_OF_DAMAGE, MercPtrs[ pSoldier->ubAttackerID ]->sSectorX, MercPtrs[ pSoldier->ubAttackerID ]->sSectorY, MercPtrs[ pSoldier->ubAttackerID ]->bSectorZ ); } if (pSoldier->bTeam == gbPlayerNum) { HandleMoraleEvent( pSoldier, MORALE_TOOK_LOTS_OF_DAMAGE, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); } } // SWITCH IN TYPE OF WEAPON if ( ubSpecial == FIRE_WEAPON_TOSSED_OBJECT_SPECIAL ) { ubReason = TAKE_DAMAGE_OBJECT; } else if ( Item[ usWeaponIndex ].usItemClass & IC_TENTACLES ) { ubReason = TAKE_DAMAGE_TENTACLES; } // marke take out gunfire if ammotype is explosive // callahan update start // setting new func to intercept testhit else if ( Item[ usWeaponIndex ].usItemClass & ( IC_GUN | IC_THROWING_KNIFE ) && ubAttackerID == NOBODY) { sBreathLoss += BP_GET_HIT; ubReason = TAKE_DAMAGE_GUNFIRE; } // callahan update end else if ( Item[ usWeaponIndex ].usItemClass & ( IC_GUN | IC_THROWING_KNIFE ) && AmmoTypes[MercPtrs[ubAttackerID]->inv[MercPtrs[ubAttackerID]->ubAttackingHand ].ubGunAmmoType].explosionSize <= 1) { if ( ubSpecial == FIRE_WEAPON_SLEEP_DART_SPECIAL ) { UINT32 uiChance; // put the drug in! pSoldier->bSleepDrugCounter = 10; uiChance = SleepDartSuccumbChance( pSoldier ); if ( PreRandom( 100 ) < uiChance ) { // succumb to the drug! sBreathLoss = (INT16)( pSoldier->bBreathMax * 100 ); } } else if ( ubSpecial == FIRE_WEAPON_BLINDED_BY_SPIT_SPECIAL ) { // blinded!! if ( (pSoldier->bBlindedCounter == 0) ) { // say quote if (pSoldier->uiStatusFlags & SOLDIER_PC) { TacticalCharacterDialogue( pSoldier, QUOTE_BLINDED ); } DecayIndividualOpplist( pSoldier ); } // will always increase counter by at least 1 pSoldier->bBlindedCounter += (sDamage / 8) + 1; // Dirty panel fInterfacePanelDirty = DIRTYLEVEL2; } sBreathLoss += BP_GET_HIT; ubReason = TAKE_DAMAGE_GUNFIRE; } else if ( Item[ usWeaponIndex ].usItemClass & IC_BLADE ) { sBreathLoss = BP_GET_HIT; ubReason = TAKE_DAMAGE_BLADE; } else if ( Item[ usWeaponIndex ].usItemClass & IC_PUNCH ) { // damage from hand-to-hand is 1/4 normal, 3/4 breath.. the sDamage value // is actually how much breath we'll take away sBreathLoss = sDamage * 100; sDamage = sDamage / PUNCH_REAL_DAMAGE_PORTION; if ( AreInMeanwhile() && gCurrentMeanwhileDef.ubMeanwhileID == INTERROGATION ) { sBreathLoss = 0; sDamage /= 2; } ubReason = TAKE_DAMAGE_HANDTOHAND; } // marke added one 'or' for explosive ammo. variation of: AmmoTypes[pSoldier->inv[pSoldier->ubAttackingHand ].ubGunAmmoType].explosionSize > 1 // extracting attacker´s ammo type else if ( Item[ usWeaponIndex ].usItemClass & IC_EXPLOSV || AmmoTypes[MercPtrs[ubAttackerID]->inv[MercPtrs[ubAttackerID]->ubAttackingHand ].ubGunAmmoType].explosionSize > 1) { INT8 bDeafValue; bDeafValue = Explosive[ Item[ usWeaponIndex ].ubClassIndex ].ubVolume / 10; if ( bDeafValue == 0 ) bDeafValue = 1; // Lesh: flashbang does damage switch ( ubSpecial ) { case FIRE_WEAPON_BLINDED_AND_DEAFENED: pSoldier->bDeafenedCounter = bDeafValue; //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Soldier is blinded and deafened" ); // if soldier in building OR underground if ( InBuilding(sLocationGrid) || (gbWorldSectorZ) ) { // deal max special damage pSoldier->bBlindedCounter = (INT8)Explosive[ Item[ usWeaponIndex ].ubClassIndex ].ubDuration; // say quote if (pSoldier->uiStatusFlags & SOLDIER_PC) { TacticalCharacterDialogue( pSoldier, QUOTE_BLINDED ); } } else if ( NightTime() ) // if soldier outside at night { // halve effect pSoldier->bBlindedCounter = (INT8)Explosive[ Item[ usWeaponIndex ].ubClassIndex ].ubDuration / 2; if ( pSoldier->bBlindedCounter == 0 ) pSoldier->bBlindedCounter = 1; pSoldier->bDeafenedCounter /= 2; // say quote if (pSoldier->uiStatusFlags & SOLDIER_PC) { TacticalCharacterDialogue( pSoldier, QUOTE_BLINDED ); } } DecayIndividualOpplist( pSoldier ); break; case FIRE_WEAPON_BLINDED: break; case FIRE_WEAPON_DEAFENED: //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Soldier is deafened" ); pSoldier->bDeafenedCounter = bDeafValue; break; }; if ( usWeaponIndex == STRUCTURE_EXPLOSION ) { ubReason = TAKE_DAMAGE_STRUCTURE_EXPLOSION; } else { ubReason = TAKE_DAMAGE_EXPLOSION; } } else { DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Soldier Control: Weapon class not handled in SoldierGotHit( ) %d", usWeaponIndex ) ); } #if 0 // 0verhaul: None of this hairyness is necessary anymore! Hazaa! // CJC: moved to after SoldierTakeDamage so that any quotes from the defender // will not be said if they are knocked out or killed if ( ubReason != TAKE_DAMAGE_TENTACLES && ubReason != TAKE_DAMAGE_OBJECT ) { // OK, OK: THis is hairy, however, it's ness. because the normal freeup call uses the // attckers intended target, and here we want to use thier actual target.... // ATE: If it's from GUNFIRE damage, keep in mind bullets... if ( Item[ usWeaponIndex ].usItemClass & IC_GUN ) { pNewSoldier = FreeUpAttackerGivenTarget( pSoldier->ubAttackerID, pSoldier->ubID ); } else { pNewSoldier = ReduceAttackBusyGivenTarget( pSoldier->ubAttackerID, pSoldier->ubID ); } if (pNewSoldier != NULL) { pSoldier = pNewSoldier; } DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Tried to free up attacker, attack count now %d", gTacticalStatus.ubAttackBusyCount) ); } #endif // OK, If we are a vehicle.... damage vehicle...( people inside... ) if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) { SoldierTakeDamage( pSoldier, ANIM_CROUCH, sDamage, sBreathLoss, ubReason, pSoldier->ubAttackerID, NOWHERE, FALSE, TRUE ); return; } // DEDUCT LIFE ubCombinedLoss = SoldierTakeDamage( pSoldier, ANIM_CROUCH, sDamage, sBreathLoss, ubReason, pSoldier->ubAttackerID, NOWHERE, FALSE, TRUE ); // ATE: OK, Let's check our ASSIGNMENT state, // If anything other than on a squad or guard, make them guard.... if ( pSoldier->bTeam == gbPlayerNum ) { if ( pSoldier->bAssignment >= ON_DUTY && pSoldier->bAssignment != ASSIGNMENT_POW ) { if( pSoldier->fMercAsleep ) { pSoldier->fMercAsleep = FALSE; pSoldier -> fForcedToStayAwake = FALSE; // refresh map screen fCharacterInfoPanelDirty = TRUE; fTeamPanelDirty = TRUE; } AddCharacterToAnySquad( pSoldier ); } } // SCREAM!!!! ubVolume = CalcScreamVolume( pSoldier, ubCombinedLoss ); // IF WE ARE AT A HIT_STOP ANIMATION // DO APPROPRIATE HITWHILE DOWN ANIMATION if ( !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_HITSTOP ) || pSoldier->usAnimState != JFK_HITDEATH_STOP ) { MakeNoise( pSoldier->ubID, pSoldier->sGridNo, pSoldier->bLevel, pSoldier->bOverTerrainType, ubVolume, NOISE_SCREAM); } // IAN ADDED THIS SAT JUNE 14th : HAVE TO SHOW VICTIM! if (gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT) && pSoldier->bVisible != -1 && pSoldier->bTeam == gbPlayerNum ) LocateSoldier(pSoldier->ubID,DONTSETLOCATOR); if ( Item[ usWeaponIndex ].usItemClass & IC_BLADE ) { PlayJA2Sample( (UINT32)( KNIFE_IMPACT ), RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); } else { PlayJA2Sample( (UINT32)( BULLET_IMPACT_1 + Random(3) ), RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); } // PLAY RANDOM GETTING HIT SOUND // ONLY IF WE ARE CONSCIOUS! if ( pSoldier->bLife >= CONSCIOUSNESS ) { if ( pSoldier->ubBodyType == CROW ) { // Exploding crow... PlayJA2Sample( CROW_EXPLODE_1, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); } else { // ATE: This is to disallow large amounts of smaples being played which is load! if ( pSoldier->fGettingHit && pSoldier->usAniCode != STANDING_BURST_HIT ) { } else { DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_HIT1 + Random( 2 ) ) ); } } } // CHECK FOR DOING HIT WHILE DOWN if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_HITSTOP ) ) { switch( pSoldier->usAnimState ) { case FLYBACKHIT_STOP: ChangeSoldierState( pSoldier, FALLBACK_DEATHTWICH, 0, FALSE ); break; case STAND_FALLFORWARD_STOP: ChangeSoldierState( pSoldier, GENERIC_HIT_DEATHTWITCHNB, 0, FALSE ); break; case JFK_HITDEATH_STOP: ChangeSoldierState( pSoldier, JFK_HITDEATH_TWITCHB, 0, FALSE ); break; case FALLBACKHIT_STOP: ChangeSoldierState( pSoldier, FALLBACK_HIT_DEATHTWITCHNB, 0, FALSE ); break; case PRONE_LAYFROMHIT_STOP: ChangeSoldierState( pSoldier, PRONE_HIT_DEATHTWITCHNB, 0, FALSE ); break; case PRONE_HITDEATH_STOP: ChangeSoldierState( pSoldier, PRONE_HIT_DEATHTWITCHB, 0 , FALSE ); break; case FALLFORWARD_HITDEATH_STOP: ChangeSoldierState( pSoldier, GENERIC_HIT_DEATHTWITCHB, 0 , FALSE ); break; case FALLBACK_HITDEATH_STOP: ChangeSoldierState( pSoldier, FALLBACK_HIT_DEATHTWITCHB, 0 , FALSE ); break; case FALLOFF_DEATH_STOP: ChangeSoldierState( pSoldier, FALLOFF_TWITCHB, 0 , FALSE ); break; case FALLOFF_STOP: ChangeSoldierState( pSoldier, FALLOFF_TWITCHNB, 0 , FALSE ); break; case FALLOFF_FORWARD_DEATH_STOP: ChangeSoldierState( pSoldier, FALLOFF_FORWARD_TWITCHB, 0 , FALSE ); break; case FALLOFF_FORWARD_STOP: ChangeSoldierState( pSoldier, FALLOFF_FORWARD_TWITCHNB, 0 , FALSE ); break; default: DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Soldier Control: Death state %d has no death hit", pSoldier->usAnimState ) ); } return; } // Set goback to aim after hit flag! // Only if we were aiming! if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY ) { pSoldier->fGoBackToAimAfterHit = TRUE; } // IF COWERING, PLAY SPECIFIC GENERIC HIT STAND... if ( pSoldier->uiStatusFlags & SOLDIER_COWERING ) { if ( pSoldier->bLife == 0 || IS_MERC_BODY_TYPE( pSoldier ) ) { EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE ); } else { EVENT_InitNewSoldierAnim( pSoldier, CIV_COWER_HIT, 0 , FALSE ); } return; } // Change based on body type switch( pSoldier->ubBodyType ) { case COW: EVENT_InitNewSoldierAnim( pSoldier, COW_HIT, 0 , FALSE ); return; break; case BLOODCAT: EVENT_InitNewSoldierAnim( pSoldier, BLOODCAT_HIT, 0 , FALSE ); return; break; case ADULTFEMALEMONSTER: case AM_MONSTER: case YAF_MONSTER: case YAM_MONSTER: EVENT_InitNewSoldierAnim( pSoldier, ADULTMONSTER_HIT, 0 , FALSE ); return; break; case LARVAE_MONSTER: EVENT_InitNewSoldierAnim( pSoldier, LARVAE_HIT, 0 , FALSE ); return; break; case QUEENMONSTER: EVENT_InitNewSoldierAnim( pSoldier, QUEEN_HIT, 0 , FALSE ); return; break; case CRIPPLECIV: { // OK, do some code here to allow the fact that poor buddy can be thrown back if it's a big enough hit... EVENT_InitNewSoldierAnim( pSoldier, CRIPPLE_HIT, 0 , FALSE ); //pSoldier->bLife = 0; //EVENT_InitNewSoldierAnim( pSoldier, CRIPPLE_DIE_FLYBACK, 0 , FALSE ); } return; break; case ROBOTNOWEAPON: EVENT_InitNewSoldierAnim( pSoldier, ROBOTNW_HIT, 0 , FALSE ); return; break; case INFANT_MONSTER: EVENT_InitNewSoldierAnim( pSoldier, INFANT_HIT, 0 , FALSE ); return; case CROW: EVENT_InitNewSoldierAnim( pSoldier, CROW_DIE, 0 , FALSE ); return; //case FATCIV: case MANCIV: case MINICIV: case DRESSCIV: case HATKIDCIV: case KIDCIV: // OK, if life is 0 and not set as dead ( this is a death hit... ) if ( !( pSoldier->uiStatusFlags & SOLDIER_DEAD ) && pSoldier->bLife == 0 ) { // Randomize death! if ( Random( 2 ) ) { EVENT_InitNewSoldierAnim( pSoldier, CIV_DIE2, 0 , FALSE ); return; } } // IF here, go generic hit ALWAYS..... EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE ); return; break; } // If here, we are a merc, check if we are in water if ( pSoldier->bOverTerrainType == LOW_WATER ) { EVENT_InitNewSoldierAnim( pSoldier, WATER_HIT, 0 , FALSE ); return; } if ( pSoldier->bOverTerrainType == DEEP_WATER ) { EVENT_InitNewSoldierAnim( pSoldier, DEEP_WATER_HIT, 0 , FALSE ); return; } // SWITCH IN TYPE OF WEAPON if ( Item[ usWeaponIndex ].usItemClass & ( IC_GUN | IC_THROWING_KNIFE ) ) { SoldierGotHitGunFire( pSoldier, usWeaponIndex, sDamage, bDirection, sRange, ubAttackerID, ubSpecial, ubHitLocation ); } if ( Item[ usWeaponIndex ].usItemClass & IC_BLADE ) { SoldierGotHitBlade( pSoldier, usWeaponIndex, sDamage, bDirection, sRange, ubAttackerID, ubSpecial, ubHitLocation ); } // marke setting ammo explosions included here with 3rd 'or' including ubReason if ( Item[ usWeaponIndex ].usItemClass & IC_EXPLOSV || Item[ usWeaponIndex ].usItemClass & IC_TENTACLES || ubReason == TAKE_DAMAGE_EXPLOSION ) { SoldierGotHitExplosion( pSoldier, usWeaponIndex, sDamage, bDirection, sRange, ubAttackerID, ubSpecial, ubHitLocation ); } if ( Item[ usWeaponIndex ].usItemClass & IC_PUNCH ) { SoldierGotHitPunch( pSoldier, usWeaponIndex, sDamage, bDirection, sRange, ubAttackerID, ubSpecial, ubHitLocation ); } } UINT8 CalcScreamVolume( SOLDIERTYPE * pSoldier, UINT8 ubCombinedLoss ) { // NB explosions are so loud they should drown out screams UINT8 ubVolume; if (ubCombinedLoss < 1) { ubVolume = 1; } else { ubVolume = ubCombinedLoss; } // Victim yells out in pain, making noise. Yelps are louder from greater // wounds, but softer for more experienced soldiers. if (ubVolume > (10 - EffectiveExpLevel( pSoldier ) )) { ubVolume = 10 - EffectiveExpLevel( pSoldier ); } /* // the "Speck factor"... He's a whiner, and extra-sensitive to pain! if (ptr->trait == NERVOUS) ubVolume += 2; */ if (ubVolume < 0) { ubVolume = 0; } return( ubVolume ); } void DoGenericHit( SOLDIERTYPE *pSoldier, UINT8 ubSpecial, INT16 bDirection ) { // Based on stance, select generic hit animation switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: // For now, check if we are affected by a burst // For now, if the weapon was a gun, special 1 == burst // ATE: Only do this for mercs! if ( ubSpecial == FIRE_WEAPON_BURST_SPECIAL && pSoldier->ubBodyType <= REGFEMALE ) { //SetSoldierDesiredDirection( pSoldier, bDirection ); EVENT_SetSoldierDirection( pSoldier, (INT8)bDirection ); EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->bDirection ); EVENT_InitNewSoldierAnim( pSoldier, STANDING_BURST_HIT, 0 , FALSE ); } else { // Check in hand for rifle if ( SoldierCarriesTwoHandedWeapon( pSoldier ) ) { EVENT_InitNewSoldierAnim( pSoldier, RIFLE_STAND_HIT, 0 , FALSE ); } else { EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE ); } } break; case ANIM_PRONE: EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_PRONE, 0 , FALSE ); break; case ANIM_CROUCH: EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_CROUCH, 0 , FALSE ); break; } } void SoldierGotHitGunFire( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation ) { UINT16 usNewGridNo; BOOLEAN fBlownAway = FALSE; BOOLEAN fHeadHit = FALSE; BOOLEAN fFallenOver = FALSE; // MAYBE CHANGE TO SPECIAL ANIMATION BASED ON VALUE SET BY DAMAGE CALCULATION CODE // ALL THESE ONLY WORK ON STANDING PEOPLE if (!(pSoldier->uiStatusFlags & SOLDIER_MONSTER) && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND) { if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND ) { if (ubSpecial == FIRE_WEAPON_HEAD_EXPLODE_SPECIAL) { if ( gGameSettings.fOptions[ TOPTION_BLOOD_N_GORE ] ) { if (SpacesAway( pSoldier->sGridNo, Menptr[ubAttackerID].sGridNo ) <= MAX_DISTANCE_FOR_MESSY_DEATH || (SpacesAway( pSoldier->sGridNo, Menptr[ubAttackerID].sGridNo ) <= MAX_BARRETT_DISTANCE_FOR_MESSY_DEATH && usWeaponIndex == BARRETT )) { usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (INT8)( DirectionInc( pSoldier->bDirection ) ) ); // CHECK OK DESTINATION! if ( OKFallDirection( pSoldier, usNewGridNo, pSoldier->bLevel, pSoldier->bDirection, JFK_HITDEATH ) ) { usNewGridNo = NewGridNo( (UINT16)usNewGridNo, (INT8)( DirectionInc( pSoldier->bDirection ) ) ); if ( OKFallDirection( pSoldier, usNewGridNo, pSoldier->bLevel, pSoldier->bDirection, pSoldier->usAnimState ) ) { fHeadHit = TRUE; } } } } } else if (ubSpecial == FIRE_WEAPON_CHEST_EXPLODE_SPECIAL) { if ( gGameSettings.fOptions[ TOPTION_BLOOD_N_GORE ] ) { if (SpacesAway( pSoldier->sGridNo, Menptr[ubAttackerID].sGridNo ) <= MAX_DISTANCE_FOR_MESSY_DEATH || (SpacesAway( pSoldier->sGridNo, Menptr[ubAttackerID].sGridNo ) <= MAX_BARRETT_DISTANCE_FOR_MESSY_DEATH && usWeaponIndex == BARRETT )) { // possibly play torso explosion anim! if (pSoldier->bDirection == bDirection) { usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ pSoldier->bDirection ] ) ); if ( OKFallDirection( pSoldier, usNewGridNo, pSoldier->bLevel, gOppositeDirection[ bDirection ], FLYBACK_HIT ) ) { usNewGridNo = NewGridNo( (UINT16)usNewGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) ); if ( OKFallDirection( pSoldier, usNewGridNo, pSoldier->bLevel, gOppositeDirection[ bDirection ], pSoldier->usAnimState ) ) { fBlownAway = TRUE; } } } } } } else if (ubSpecial == FIRE_WEAPON_LEG_FALLDOWN_SPECIAL) { // possibly play fall over anim! // this one is NOT restricted by distance if (IsValidStance( pSoldier, ANIM_PRONE ) ) { // Can't be in water, or not standing if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND && !MercInWater( pSoldier ) ) { fFallenOver = TRUE; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzLateLocalizedString[ 20 ], pSoldier->name ); } } } } } // IF HERE AND GUY IS DEAD, RETURN! if ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) { // 0verhaul: Handled in the soldier state change code // DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Releasesoldierattacker,Dead soldier hit" ) ); // ReleaseSoldiersAttacker( pSoldier ); return; } if ( fFallenOver ) { SoldierCollapse( pSoldier ); return; } if ( fBlownAway ) { // Only for mercs... if ( pSoldier->ubBodyType < 4 ) { ChangeToFlybackAnimation( pSoldier, (INT8)bDirection ); return; } } if ( fHeadHit ) { // Only for mercs ( or KIDS! ) if ( pSoldier->ubBodyType < 4 || pSoldier->ubBodyType == HATKIDCIV || pSoldier->ubBodyType == KIDCIV ) { EVENT_InitNewSoldierAnim( pSoldier, JFK_HITDEATH, 0 , FALSE ); return; } } DoGenericHit( pSoldier, ubSpecial, bDirection ); } void SoldierGotHitExplosion( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation ) { INT16 sNewGridNo; // IF HERE AND GUY IS DEAD, RETURN! if ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) { return; } //check for services ReceivingSoldierCancelServices( pSoldier ); GivingSoldierCancelServices( pSoldier ); if ( gGameSettings.fOptions[ TOPTION_BLOOD_N_GORE ] ) { if ( Explosive[ Item[ usWeaponIndex ].ubClassIndex ].ubRadius >= 3 && pSoldier->bLife == 0 && gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_PRONE ) { if ( sRange >= 2 && sRange <= 4 ) { DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_HIT1 + Random( 2 ) ) ); EVENT_InitNewSoldierAnim( pSoldier, CHARIOTS_OF_FIRE, 0 , FALSE ); return; } else if ( sRange <= 1 ) { DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_HIT1 + Random( 2 ) ) ); EVENT_InitNewSoldierAnim( pSoldier, BODYEXPLODING, 0 , FALSE ); return; } } } // If we can't fal back or such, so generic hit... if ( pSoldier->ubBodyType >= 4 ) { DoGenericHit( pSoldier, 0, bDirection ); return; } // Lesh: possible soldier behavior when affected by flashbang // Soldier can: // 1. stand as if there was no explosion at all // 2. crouch. represent that soldier didn't expect such blow and instinctively // made defensive movement to protect his body // 3. fall forward. again, he didn't expect that something will explode behind // him and deafens him // 4. fall backward. unexpected blast, fear, clumsy moves and soldier flies backward. // Based on stance, select generic hit animation switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: if ( ubSpecial == FIRE_WEAPON_DEAFENED ) { switch( Random(10) ) { case 0: case 1: case 2: case 3: case 4: case 5: // 6 of 10 - crouch ChangeSoldierStance( pSoldier, ANIM_CROUCH ); break; case 6: case 7: case 8: // 3 of 10 - fall forward BeginTyingToFall( pSoldier ); EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0, FALSE ); break; case 9: // 1 of 10 - still standing DoGenericHit( pSoldier, 0, bDirection ); break; }; break; } else if ( ubSpecial == FIRE_WEAPON_BLINDED_AND_DEAFENED ) { switch( Random(10) ) { case 0: case 1: case 2: case 3: case 4: // 5 of 10 - crouch ChangeSoldierStance( pSoldier, ANIM_CROUCH ); break; case 5: case 6: case 7: case 8: // 4 of 10 - fall backward (if possible) either forward // Check behind us! sNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) ); if ( OKFallDirection( pSoldier, sNewGridNo, pSoldier->bLevel, gOppositeDirection[ bDirection ], FLYBACK_HIT ) ) { EVENT_SetSoldierDirection( pSoldier, (INT8)bDirection ); EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->bDirection ); ChangeToFallbackAnimation( pSoldier, (INT8)bDirection ); } else { BeginTyingToFall( pSoldier ); EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0, FALSE ); } break; case 9: // 1 of 10 - still standing DoGenericHit( pSoldier, 0, bDirection ); break; }; break; } else if ( ubSpecial == FIRE_WEAPON_BLINDED ) { } case ANIM_CROUCH: if ( ubSpecial == FIRE_WEAPON_BLINDED || ubSpecial == FIRE_WEAPON_BLINDED_AND_DEAFENED || ubSpecial == FIRE_WEAPON_DEAFENED ) { DoGenericHit( pSoldier, 0, bDirection ); break; } EVENT_SetSoldierDirection( pSoldier, (INT8)bDirection ); EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->bDirection ); // Check behind us! sNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) ); if ( OKFallDirection( pSoldier, sNewGridNo, pSoldier->bLevel, gOppositeDirection[ bDirection ], FLYBACK_HIT ) ) { ChangeToFallbackAnimation( pSoldier, (INT8)bDirection ); } else { if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND ) { BeginTyingToFall( pSoldier ); EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0, FALSE ); } else { SoldierCollapse( pSoldier ); } } break; case ANIM_PRONE: SoldierCollapse( pSoldier ); break; } } void SoldierGotHitBlade( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation ) { // IF HERE AND GUY IS DEAD, RETURN! if ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) { return; } // Based on stance, select generic hit animation switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: // Check in hand for rifle if ( SoldierCarriesTwoHandedWeapon( pSoldier ) ) { EVENT_InitNewSoldierAnim( pSoldier, RIFLE_STAND_HIT, 0 , FALSE ); } else { EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE ); } break; case ANIM_CROUCH: EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_CROUCH, 0 , FALSE ); break; case ANIM_PRONE: EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_PRONE, 0 , FALSE ); break; } } void SoldierGotHitPunch( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation ) { // IF HERE AND GUY IS DEAD, RETURN! if ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) { return; } // Based on stance, select generic hit animation switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: // Check in hand for rifle if ( SoldierCarriesTwoHandedWeapon( pSoldier ) ) { EVENT_InitNewSoldierAnim( pSoldier, RIFLE_STAND_HIT, 0 , FALSE ); } else { EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE ); } break; case ANIM_CROUCH: EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_CROUCH, 0 , FALSE ); break; case ANIM_PRONE: EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_PRONE, 0 , FALSE ); break; } } BOOLEAN EVENT_InternalGetNewSoldierPath( SOLDIERTYPE *pSoldier, UINT16 sDestGridNo, UINT16 usMovementAnim, BOOLEAN fFromUI, BOOLEAN fForceRestartAnim ) { INT32 iDest; INT16 sNewGridNo; BOOLEAN fContinue; UINT32 uiDist; UINT16 usAnimState; UINT16 usMoveAnimState = usMovementAnim; INT16 sMercGridNo; UINT16 usPathingData[ MAX_PATH_LIST_SIZE ]; UINT8 ubPathingMaxDirection; BOOLEAN fAdvancePath = TRUE; UINT8 fFlags = 0; // Ifd this code, make true if a player if ( fFromUI == 3 ) { if ( pSoldier->bTeam == gbPlayerNum ) { fFromUI = 1; } else { fFromUI = 0; } } // ATE: if a civ, and from UI, and were cowering, remove from cowering if ( AM_AN_EPC( pSoldier ) && fFromUI ) { if ( pSoldier->uiStatusFlags & SOLDIER_COWERING ) { SetSoldierCowerState( pSoldier, FALSE ); usMoveAnimState = WALKING; } } pSoldier->bGoodContPath = FALSE; if ( pSoldier->fDelayedMovement ) { if ( pSoldier->ubDelayedMovementFlags & DELAYED_MOVEMENT_FLAG_PATH_THROUGH_PEOPLE ) { fFlags = PATH_THROUGH_PEOPLE; } else { fFlags = PATH_IGNORE_PERSON_AT_DEST; } pSoldier->fDelayedMovement = FALSE; } if ( gfGetNewPathThroughPeople ) { fFlags = PATH_THROUGH_PEOPLE; } // ATE: Some stuff here for realtime, going through interface.... if ( ( !( gTacticalStatus.uiFlags & INCOMBAT ) && ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) && fFromUI == 1 ) || fFromUI == 2 ) { if ( pSoldier->bCollapsed ) { return( FALSE ); } sMercGridNo = pSoldier->sGridNo; pSoldier->sGridNo = pSoldier->sDestination; // Check if path is good before copying it into guy's path... if ( FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, NO_COPYROUTE, fFlags ) == 0 ) { // Set to old.... pSoldier->sGridNo = sMercGridNo; return( FALSE ); } uiDist = FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, COPYROUTE, fFlags ); pSoldier->sGridNo = sMercGridNo; pSoldier->sFinalDestination = sDestGridNo; if ( uiDist > 0 ) { // Add one to path data size.... if ( fAdvancePath ) { memcpy( usPathingData, pSoldier->usPathingData, sizeof( usPathingData ) ); ubPathingMaxDirection = (UINT8)usPathingData[ MAX_PATH_LIST_SIZE -1 ]; memcpy( &(pSoldier->usPathingData[1]), usPathingData, sizeof( usPathingData ) - sizeof( UINT16 ) ); // If we have reach the max, go back one sFinalDest.... if ( pSoldier->usPathDataSize == MAX_PATH_LIST_SIZE ) { //pSoldier->sFinalDestination = NewGridNo( (UINT16)pSoldier->sFinalDestination, DirectionInc( gOppositeDirection[ ubPathingMaxDirection ] ) ); } else { pSoldier->usPathDataSize++; } } usMoveAnimState = pSoldier->usUIMovementMode; if ( pSoldier->bOverTerrainType == DEEP_WATER ) { usMoveAnimState = DEEP_WATER_SWIM; } // Change animation only.... set value to NOT call any goto new gridno stuff..... if ( usMoveAnimState != pSoldier->usAnimState ) { // pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = TRUE; EVENT_InitNewSoldierAnim( pSoldier, usMoveAnimState, 0, FALSE ); } return( TRUE ); } return( FALSE ); } // we can use the soldier's level here because we don't have pathing across levels right now... if (pSoldier->bPathStored) { fContinue = TRUE; } else { iDest = FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, usMovementAnim, COPYROUTE, fFlags ); fContinue = (iDest != 0); } // Only if we can get a path here if ( fContinue ) { // Debug messages DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier %d: Get new path", pSoldier->ubID ) ); // Set final destination pSoldier->sFinalDestination = sDestGridNo; pSoldier->fPastXDest = 0; pSoldier->fPastYDest = 0; // CHECK IF FIRST TILE IS FREE sNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( (UINT8)pSoldier->usPathingData[ pSoldier->usPathIndex ] ) ); // If true, we're OK, if not, WAIT for a guy to pass! // If we are in deep water, we can only swim! if ( pSoldier->bOverTerrainType == DEEP_WATER ) { usMoveAnimState = DEEP_WATER_SWIM; } // If we were aiming, end aim! usAnimState = PickSoldierReadyAnimation( pSoldier, TRUE ); // Add a pending animation first! // Only if we were standing! if ( usAnimState != INVALID_ANIMATION && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND ) { EVENT_InitNewSoldierAnim( pSoldier, usAnimState, 0, FALSE ); pSoldier->usPendingAnimation = usMoveAnimState; } else { // Call local copy for change soldier state! EVENT_InitNewSoldierAnim( pSoldier, usMoveAnimState, 0, fForceRestartAnim ); } // Change desired direction // ATE: Here we have a situation where in RT, we may have // gotten a new path, but we are alreayd moving.. so // at leasty change new dest. This will be redundent if the ANI is a totaly new one return( TRUE ); } return( FALSE ); } void EVENT_GetNewSoldierPath( SOLDIERTYPE *pSoldier, UINT16 sDestGridNo, UINT16 usMovementAnim ) { // ATE: Default restart of animation to TRUE EVENT_InternalGetNewSoldierPath( pSoldier, sDestGridNo, usMovementAnim, FALSE, TRUE ); } // Change our state based on stance, to stop! void StopSoldier( SOLDIERTYPE *pSoldier ) { ReceivingSoldierCancelServices( pSoldier ); GivingSoldierCancelServices( pSoldier ); if ( !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_STATIONARY ) ) { //SoldierGotoStationaryStance( pSoldier ); EVENT_StopMerc( pSoldier, pSoldier->sGridNo, pSoldier->bDirection ); } // Set desination pSoldier->sFinalDestination = pSoldier->sGridNo; } void SoldierGotoStationaryStance( SOLDIERTYPE *pSoldier ) { // ATE: This is to turn off fast movement, that us used to change movement mode // for ui display on stance changes.... if ( pSoldier->bTeam == gbPlayerNum ) { //pSoldier->fUIMovementFast = FALSE; } // The queen, if she sees anybody, goes to ready, not normal breath.... if ( pSoldier->ubBodyType == QUEENMONSTER ) { if ( pSoldier->bOppCnt > 0 || pSoldier->bTeam == gbPlayerNum ) { EVENT_InitNewSoldierAnim( pSoldier, QUEEN_READY, 0 , TRUE ); return; } } // Check if we are in deep water! if ( pSoldier->bOverTerrainType == DEEP_WATER ) { // IN deep water, tred! EVENT_InitNewSoldierAnim( pSoldier, DEEP_WATER_TRED, 0 , FALSE ); } else if ( pSoldier->ubServicePartner != NOBODY && pSoldier->bLife >= OKLIFE && pSoldier->bBreath > 0 ) { EVENT_InitNewSoldierAnim( pSoldier, GIVING_AID, 0 , FALSE ); } else { // Change state back to stationary state for given height switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: // If we are cowering....goto cower state if ( pSoldier->uiStatusFlags & SOLDIER_COWERING ) { EVENT_InitNewSoldierAnim( pSoldier, START_COWER, 0 , FALSE ); } else { EVENT_InitNewSoldierAnim( pSoldier, STANDING, 0 , FALSE ); } break; case ANIM_CROUCH: // If we are cowering....goto cower state if ( pSoldier->uiStatusFlags & SOLDIER_COWERING ) { EVENT_InitNewSoldierAnim( pSoldier, COWERING, 0 , FALSE ); } else { EVENT_InitNewSoldierAnim( pSoldier, CROUCHING, 0 , FALSE ); } break; case ANIM_PRONE: EVENT_InitNewSoldierAnim( pSoldier, PRONE, 0 , FALSE ); break; } } } void ChangeSoldierStance( SOLDIERTYPE *pSoldier, UINT8 ubDesiredStance ) { UINT16 usNewState; // Check if they are the same! if ( ubDesiredStance == gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) { // Free up from stance change FreeUpNPCFromStanceChange( pSoldier ); return; } // Set UI Busy SetUIBusy( pSoldier->ubID ); // ATE: If we are an NPC, cower.... if ( pSoldier->ubBodyType >= FATCIV && pSoldier->ubBodyType <= KIDCIV ) { if ( ubDesiredStance == ANIM_STAND ) { SetSoldierCowerState( pSoldier, FALSE ); } else { SetSoldierCowerState( pSoldier, TRUE ); } } else { usNewState = GetNewSoldierStateFromNewStance( pSoldier, ubDesiredStance ); // Set desired stance pSoldier->ubDesiredHeight = ubDesiredStance; // Now change to appropriate animation EVENT_InitNewSoldierAnim( pSoldier, usNewState, 0 , FALSE ); } } void EVENT_InternalSetSoldierDestination( SOLDIERTYPE *pSoldier, UINT16 usNewDirection, BOOLEAN fFromMove, UINT16 usAnimState ) { UINT16 usNewGridNo; INT16 sXPos, sYPos; // Get dest gridno, convert to center coords usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( usNewDirection ) ); ConvertMapPosToWorldTileCenter( usNewGridNo, &sXPos, &sYPos ); // Save new dest gridno, x, y pSoldier->sDestination = usNewGridNo; pSoldier->sDestXPos = sXPos; pSoldier->sDestYPos = sYPos; pSoldier->bMovementDirection = (INT8)usNewDirection; // OK, ATE: If we are side_stepping, calculate a NEW desired direction.... if ( pSoldier->bReverse && usAnimState == SIDE_STEP ) { UINT8 ubPerpDirection; // Get a new desired direction, ubPerpDirection = gPurpendicularDirection[ pSoldier->bDirection ][ usNewDirection ]; // CHange actual and desired direction.... EVENT_SetSoldierDirection( pSoldier, ubPerpDirection ); pSoldier->bDesiredDirection = pSoldier->bDirection; } else { if ( !( gAnimControl[ usAnimState ].uiFlags & ANIM_SPECIALMOVE ) ) { EVENT_InternalSetSoldierDesiredDirection( pSoldier, usNewDirection, fFromMove, usAnimState ); } } } void EVENT_SetSoldierDestination( SOLDIERTYPE *pSoldier, UINT16 usNewDirection ) { EVENT_InternalSetSoldierDestination( pSoldier, usNewDirection, FALSE, pSoldier->usAnimState ); } // function to determine which direction a creature can turn in INT8 MultiTiledTurnDirection( SOLDIERTYPE * pSoldier, INT8 bStartDirection, INT8 bDesiredDirection ) { INT8 bTurningIncrement; INT8 bCurrentDirection; INT8 bLoop; UINT16 usStructureID, usAnimSurface; STRUCTURE_FILE_REF * pStructureFileRef; BOOLEAN fOk = FALSE; // start by trying to turn in quickest direction bTurningIncrement = (INT8) QuickestDirection( bStartDirection, bDesiredDirection ); usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, pSoldier->usUIMovementMode ); pStructureFileRef = GetAnimationStructureRef( pSoldier->ubID, usAnimSurface, pSoldier->usUIMovementMode ); if ( !pStructureFileRef ) { // without structure data, well, assume quickest direction return( bTurningIncrement ); } // ATE: Only if we have a levelnode... if ( pSoldier->pLevelNode != NULL && pSoldier->pLevelNode->pStructureData != NULL ) { usStructureID = pSoldier->pLevelNode->pStructureData->usStructureID; } else { usStructureID = INVALID_STRUCTURE_ID; } bLoop = 0; bCurrentDirection = bStartDirection; while( bLoop < 2 ) { while( bCurrentDirection != bDesiredDirection ) { bCurrentDirection += bTurningIncrement; // did we wrap directions? if ( bCurrentDirection < 0 ) { bCurrentDirection = (MAXDIR - 1); } else if ( bCurrentDirection >= MAXDIR ) { bCurrentDirection = 0; } // check to see if we can add creature in that direction fOk = OkayToAddStructureToWorld( pSoldier->sGridNo, pSoldier->bLevel, &(pStructureFileRef->pDBStructureRef[ gOneCDirection[ bCurrentDirection ] ]), usStructureID ); if (!fOk) { break; } } if ( (bCurrentDirection == bDesiredDirection) && fOk ) { // success!! return( bTurningIncrement ); } bLoop++; if ( bLoop < 2 ) { // change direction of loop etc bCurrentDirection = bStartDirection; bTurningIncrement *= -1; } } // nothing found... doesn't matter much what we return return( bTurningIncrement ); } void EVENT_InternalSetSoldierDesiredDirection( SOLDIERTYPE *pSoldier, UINT16 usNewDirection, BOOLEAN fInitalMove, UINT16 usAnimState ) { //if ( usAnimState == WALK_BACKWARDS ) if ( pSoldier->bReverse && usAnimState != SIDE_STEP ) { // OK, check if we are going to go in the exact opposite than our facing.... usNewDirection = gOppositeDirection[ usNewDirection ]; } pSoldier->bDesiredDirection = (INT8)usNewDirection; // If we are prone, goto crouched first! // ONly if we are stationary, and only if directions are differnet! // ATE: If we are fNoAPsToFinnishMove, stop what we were doing and // reset flag..... if ( pSoldier->fNoAPToFinishMove && ( gAnimControl[ usAnimState ].uiFlags & ANIM_MOVING ) ) { // ATE; Commented this out: NEVER, EVER, start a new anim from this function, as an eternal loop will result.... //SoldierGotoStationaryStance( pSoldier ); // Reset flag! AdjustNoAPToFinishMove( pSoldier, FALSE ); } if ( pSoldier->bDesiredDirection != pSoldier->bDirection ) { if ( gAnimControl[ usAnimState ].uiFlags & ( ANIM_BREATH | ANIM_OK_CHARGE_AP_FOR_TURN | ANIM_FIREREADY ) && !fInitalMove && !pSoldier->fDontChargeTurningAPs ) { // Deduct points for initial turn! switch( gAnimControl[ usAnimState ].ubEndHeight ) { // Now change to appropriate animation case ANIM_STAND: DeductPoints( pSoldier, AP_LOOK_STANDING, 0 ); break; case ANIM_CROUCH: DeductPoints( pSoldier, AP_LOOK_CROUCHED, 0 ); break; case ANIM_PRONE: DeductPoints( pSoldier, AP_LOOK_PRONE, 0 ); break; } } pSoldier->fDontChargeTurningAPs = FALSE; if ( fInitalMove ) { if ( gAnimControl[ usAnimState ].ubHeight == ANIM_PRONE ) { if ( pSoldier->fTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE ) { pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_START_UP_FROM_MOVE; } } } if ( gAnimControl[ usAnimState ].uiFlags & ANIM_STATIONARY || pSoldier->fNoAPToFinishMove || fInitalMove ) { if ( gAnimControl[ usAnimState ].ubHeight == ANIM_PRONE ) { // Set this beasty of a flag to allow us to go back down to prone if we choose! // ATE: Alrighty, set flag to go back down only if we are not moving anywhere //if ( pSoldier->sDestination == pSoldier->sGridNo ) if ( !fInitalMove ) { pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_ON; // Set a pending animation to change stance first... SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH ); } } } } // Set desired direction for the extended directions... pSoldier->ubHiResDesiredDirection = ubExtDirection[ pSoldier->bDesiredDirection ]; if ( pSoldier->bDesiredDirection != pSoldier->bDirection ) { if ( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE ) || CREATURE_OR_BLOODCAT( pSoldier ) ) { pSoldier->uiStatusFlags |= SOLDIER_PAUSEANIMOVE; } } if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) { pSoldier->bTurningIncrement = (INT8) ExtQuickestDirection( pSoldier->ubHiResDirection, pSoldier->ubHiResDesiredDirection ); } else { if ( pSoldier->uiStatusFlags & SOLDIER_MULTITILE ) { pSoldier->bTurningIncrement = (INT8) MultiTiledTurnDirection( pSoldier, pSoldier->bDirection, pSoldier->bDesiredDirection ); } else { pSoldier->bTurningIncrement = (INT8) QuickestDirection( pSoldier->bDirection, pSoldier->bDesiredDirection ); } } } void EVENT_SetSoldierDesiredDirection( SOLDIERTYPE *pSoldier, UINT16 usNewDirection ) { EVENT_InternalSetSoldierDesiredDirection( pSoldier, usNewDirection, FALSE, pSoldier->usAnimState ); } void EVENT_SetSoldierDirection( SOLDIERTYPE *pSoldier, UINT16 usNewDirection ) { // Remove old location data HandleAnimationProfile( pSoldier, pSoldier->usAnimState, TRUE ); pSoldier->bDirection = (INT8)usNewDirection; // Updated extended direction..... pSoldier->ubHiResDirection = ubExtDirection[ pSoldier->bDirection ]; // Add new stuff HandleAnimationProfile( pSoldier, pSoldier->usAnimState, FALSE ); // If we are turning, we have chaanged our aim! if ( !pSoldier->fDontUnsetLastTargetFromTurn ) { pSoldier->sLastTarget = NOWHERE; } AdjustForFastTurnAnimation( pSoldier ); // Update structure info! // if ( pSoldier->uiStatusFlags & SOLDIER_MULTITILE ) { UpdateMercStructureInfo( pSoldier ); } // Handle Profile data for hit locations HandleAnimationProfile( pSoldier, pSoldier->usAnimState, TRUE ); HandleCrowShadowNewDirection( pSoldier ); // Change values! SetSoldierLocatorOffsets( pSoldier ); } void EVENT_BeginMercTurn( SOLDIERTYPE *pSoldier, BOOLEAN fFromRealTime, INT32 iRealTimeCounter ) { // NB realtimecounter is not used, always passed in as 0 now! INT32 iBlood; if (pSoldier->bUnderFire) { // UnderFire now starts at 2 for "under fire this turn", // down to 1 for "under fire last turn", to 0. pSoldier->bUnderFire--; } // ATE: Add decay effect sfor drugs... if ( fFromRealTime ) //&& iRealTimeCounter % 300 ) { HandleEndTurnDrugAdjustments( pSoldier ); } else { HandleEndTurnDrugAdjustments( pSoldier ); } // ATE: Don't bleed if in AUTO BANDAGE! if ( !gTacticalStatus.fAutoBandageMode ) { // Blood is not for the weak of heart, or mechanical if ( !( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE | SOLDIER_ROBOT ) ) ) { if ( pSoldier->bBleeding || pSoldier->bLife < OKLIFE ) // is he bleeding or dying? { iBlood = CheckBleeding( pSoldier ); // check if he might lose another life point // ATE: Only if in sector! if ( pSoldier->bInSector ) { if ( iBlood != NOBLOOD ) { DropBlood( pSoldier, (INT8)iBlood, pSoldier->bVisible ); } } } } } // survived bleeding, but is he out of breath? if ( pSoldier->bLife && !pSoldier->bBreath && MercInWater( pSoldier ) ) { // Drowning... } // if he is still alive (didn't bleed to death) if ( pSoldier->bLife ) { // reduce the effects of any residual shock from past injuries by half pSoldier->bShock /= 2; // if this person has heard a noise that hasn't been investigated if (pSoldier->sNoiseGridno != NOWHERE) { if (pSoldier->ubNoiseVolume) // and the noise volume is still positive { pSoldier->ubNoiseVolume--; // the volume of the noise "decays" by 1 point if (!pSoldier->ubNoiseVolume) // if the volume has reached zero { pSoldier->sNoiseGridno = NOWHERE; // forget about the noise! } } } // save unused action points up to a maximum /* if ((savedPts = pSoldier->bActionPts) > MAX_AP_CARRIED) savedPts = MAX_AP_CARRIED; */ if ( pSoldier->uiStatusFlags & SOLDIER_GASSED ) { // then must get a gas mask or leave the gassed area to get over it // if ( ( pSoldier->inv[ HEAD1POS ].usItem == GASMASK || pSoldier->inv[ HEAD2POS ].usItem == GASMASK ) || !( GetSmokeEffectOnTile( pSoldier->sGridNo, pSoldier->bLevel ) ) ) if ( FindGasMask( pSoldier ) != NO_SLOT || !( GetSmokeEffectOnTile( pSoldier->sGridNo, pSoldier->bLevel ) ) ) { // Turn off gassed flag.... pSoldier->uiStatusFlags &= (~SOLDIER_GASSED ); } } if ( pSoldier->bBlindedCounter > 0 ) { pSoldier->bBlindedCounter--; if (pSoldier->bBlindedCounter == 0) { // we can SEE!!!!! HandleSight( pSoldier, SIGHT_LOOK ); // Dirty panel fInterfacePanelDirty = DIRTYLEVEL2; } } if ( pSoldier->bDeafenedCounter > 0 ) { pSoldier->bDeafenedCounter--; } // ATE: To get around a problem... // If an AI guy, and we have 0 life, and are still at higher hieght, // Kill them..... pSoldier->sWeightCarriedAtTurnStart = (INT16) CalculateCarriedWeight( pSoldier ); UnusedAPsToBreath( pSoldier ); // Set flag back to normal, after reaching a certain statge if ( pSoldier->bBreath > 80 ) { pSoldier->usQuoteSaidFlags &= ( ~SOLDIER_QUOTE_SAID_LOW_BREATH ); } if ( pSoldier->bBreath > 50 ) { pSoldier->usQuoteSaidFlags &= ( ~SOLDIER_QUOTE_SAID_DROWNING ); } if ( pSoldier->ubTurnsUntilCanSayHeardNoise > 0) { pSoldier->ubTurnsUntilCanSayHeardNoise--; } if ( pSoldier->bInSector ) { CheckForBreathCollapse( pSoldier ); } CalcNewActionPoints( pSoldier ); pSoldier->bTilesMoved = 0; if ( pSoldier->bInSector ) { BeginSoldierGetup( pSoldier ); // CJC Nov 30: handle RT opplist decaying in another function which operates less often if ( gTacticalStatus.uiFlags & INCOMBAT ) { VerifyAndDecayOpplist( pSoldier ); // turn off xray if ( pSoldier->uiXRayActivatedTime ) { TurnOffXRayEffects( pSoldier ); } } if ( (pSoldier->bTeam == gbPlayerNum) && (pSoldier->ubProfile != NO_PROFILE) ) { switch( gMercProfiles[ pSoldier->ubProfile ].bPersonalityTrait ) { case FEAR_OF_INSECTS: if ( MercSeesCreature( pSoldier ) ) { HandleMoraleEvent( pSoldier, MORALE_INSECT_PHOBIC_SEES_CREATURE, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); if ( !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_PERSONALITY) ) { TacticalCharacterDialogue( pSoldier, QUOTE_PERSONALITY_TRAIT ); pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_PERSONALITY; } } break; case CLAUSTROPHOBIC: if ( gbWorldSectorZ > 0 && Random( 6 - gbWorldSectorZ ) == 0 ) { // underground! HandleMoraleEvent( pSoldier, MORALE_CLAUSTROPHOBE_UNDERGROUND, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); if ( !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_PERSONALITY) ) { TacticalCharacterDialogue( pSoldier, QUOTE_PERSONALITY_TRAIT ); pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_PERSONALITY; } } break; case NERVOUS: if ( DistanceToClosestFriend( pSoldier ) > NERVOUS_RADIUS ) { // augh!! if ( pSoldier->bMorale < 50 ) { HandleMoraleEvent( pSoldier, MORALE_NERVOUS_ALONE, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); if ( !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_PERSONALITY) ) { TacticalCharacterDialogue( pSoldier, QUOTE_PERSONALITY_TRAIT ); pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_PERSONALITY; } } } else { if ( pSoldier->bMorale > 45 ) { // turn flag off, so that we say it every two turns pSoldier->usQuoteSaidFlags &= ~SOLDIER_QUOTE_SAID_PERSONALITY; } } break; } } } // Reset quote flags for under heavy fire and close call! pSoldier->usQuoteSaidFlags &= ( ~SOLDIER_QUOTE_SAID_BEING_PUMMELED ); pSoldier->usQuoteSaidExtFlags &= ( ~SOLDIER_QUOTE_SAID_EXT_CLOSE_CALL ); pSoldier->bNumHitsThisTurn = 0; pSoldier->ubSuppressionPoints = 0; pSoldier->fCloseCall = FALSE; pSoldier->ubMovementNoiseHeard = 0; // If soldier has new APs, reset flags! if ( pSoldier->bActionPoints > 0 ) { pSoldier->fUIFirstTimeNOAP = FALSE; pSoldier->bMoved = FALSE; pSoldier->bPassedLastInterrupt = FALSE; } } } // UTILITY FUNCTIONS CALLED BY OVERHEAD.H UINT8 gDirectionFrom8to2[ ] = { 0, 0, 1, 1, 0, 1, 1, 0 }; BOOLEAN ConvertAniCodeToAniFrame( SOLDIERTYPE *pSoldier, UINT16 usAniFrame ) { UINT16 usAnimSurface; UINT8 ubTempDir; // Given ani code, adjust for facing direction // get anim surface and determine # of frames usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState ); CHECKF( usAnimSurface != INVALID_ANIMATION_SURFACE ); // COnvert world direction into sprite direction ubTempDir = gOneCDirection[ pSoldier->bDirection ]; //If we are only one frame, ignore what the script is telling us! if ( gAnimSurfaceDatabase[ usAnimSurface ].ubFlags & ANIM_DATA_FLAG_NOFRAMES ) { usAniFrame = 0; } if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 32 ) { ubTempDir = gExtOneCDirection[ pSoldier->ubHiResDirection ]; } // Check # of directions /surface, adjust if ness. else if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 4 ) { ubTempDir = ubTempDir / 2; } // Check # of directions /surface, adjust if ness. else if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 1 ) { ubTempDir = 0; } // Check # of directions /surface, adjust if ness. else if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 3 ) { if ( pSoldier->bDirection == NORTHWEST ) { ubTempDir = 1; } if ( pSoldier->bDirection == WEST ) { ubTempDir = 0; } if ( pSoldier->bDirection == EAST ) { ubTempDir = 2; } } else if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 2 ) { ubTempDir = gDirectionFrom8to2[ pSoldier->bDirection ]; } pSoldier->usAniFrame = usAniFrame + (UINT16) ( ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumFramesPerDir * ubTempDir ) ); if ( gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject == NULL ) { pSoldier->usAniFrame = 0; return( TRUE ); } if ( pSoldier->usAniFrame >= gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->usNumberOfObjects ) { // Debug msg here.... // ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"Soldier Animation: Wrong Number of frames per number of objects: %d vs %d, %S", gAnimSurfaceDatabase[ usAnimSurface ].uiNumFramesPerDir, gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->usNumberOfObjects, gAnimControl[ pSoldier->usAnimState ].zAnimStr ); pSoldier->usAniFrame = 0; } return( TRUE ); } void TurnSoldier( SOLDIERTYPE *pSoldier) { INT16 sDirection; BOOLEAN fDoDirectionChange = TRUE; INT32 cnt; // If we are a vehicle... DON'T TURN! if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) { if ( pSoldier->ubBodyType != TANK_NW && pSoldier->ubBodyType != TANK_NE ) { return; } } else // Lesh: patch for "Bug: Enemy turns around in turn based mode!" { // in case of errors in turning tasks if ( pSoldier->bDesiredDirection > 7 || pSoldier->bDesiredDirection < 0) { DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("TurnSoldier() Warinig: Invalid desired direction for non-vehicle unit") ); pSoldier->bDesiredDirection = pSoldier->bDirection; } } // Lesh: patch ended // We handle sight now.... if ( pSoldier->uiStatusFlags & SOLDIER_LOOK_NEXT_TURNSOLDIER ) { if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_STATIONARY && pSoldier->usAnimState != CLIMBUPROOF && pSoldier->usAnimState != CLIMBDOWNROOF ) ) { // HANDLE SIGHT! HandleSight( pSoldier,SIGHT_LOOK | SIGHT_RADIO ); } // Turn off! pSoldier->uiStatusFlags &= (~SOLDIER_LOOK_NEXT_TURNSOLDIER ); HandleSystemNewAISituation( pSoldier, FALSE ); } if ( pSoldier->fTurningToShoot ) { if ( pSoldier->bDirection == pSoldier->bDesiredDirection ) { if ( ( (gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY ) && !pSoldier->fTurningFromPronePosition ) || pSoldier->ubBodyType == ROBOTNOWEAPON || pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE ) { DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("TurnSoldier: EVENT_InitNewSoldierAnim") ); EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ), 0, FALSE ); pSoldier->fTurningToShoot = FALSE; // Save last target gridno! //pSoldier->sLastTarget = pSoldier->sTargetGridNo; } // Else check if we are trying to shoot and once was prone, but am now crouched because we needed to turn... else if ( pSoldier->fTurningFromPronePosition ) { if ( IsValidStance( pSoldier, ANIM_PRONE ) ) { SendChangeSoldierStanceEvent( pSoldier, ANIM_PRONE ); pSoldier->usPendingAnimation = SelectFireAnimation( pSoldier, ANIM_PRONE ); } else { EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, ANIM_CROUCH ), 0, FALSE ); } pSoldier->fTurningToShoot = FALSE; pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF; } } } if ( pSoldier->fTurningToFall ) { if ( pSoldier->bDirection == pSoldier->bDesiredDirection ) { SelectFallAnimation( pSoldier ); pSoldier->fTurningToFall = FALSE; } } if ( pSoldier->fTurningUntilDone && ( pSoldier->ubPendingStanceChange != NO_PENDING_STANCE ) ) { if ( pSoldier->bDirection == pSoldier->bDesiredDirection ) { SendChangeSoldierStanceEvent( pSoldier, pSoldier->ubPendingStanceChange ); pSoldier->ubPendingStanceChange = NO_PENDING_STANCE; pSoldier->fTurningUntilDone = FALSE; } } if ( pSoldier->fTurningUntilDone && ( pSoldier->usPendingAnimation != NO_PENDING_ANIMATION ) ) { if ( pSoldier->bDirection == pSoldier->bDesiredDirection ) { UINT16 usPendingAnimation; usPendingAnimation = pSoldier->usPendingAnimation; pSoldier->usPendingAnimation = NO_PENDING_ANIMATION; EVENT_InitNewSoldierAnim( pSoldier, usPendingAnimation, 0 , FALSE ); pSoldier->fTurningUntilDone = FALSE; } } // Don't do anything if we are at dest direction! if ( pSoldier->bDirection == pSoldier->bDesiredDirection ) { if ( pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE ) { if ( pSoldier->iTuringSoundID != NO_SAMPLE ) { SoundStop( pSoldier->iTuringSoundID ); pSoldier->iTuringSoundID = NO_SAMPLE; PlaySoldierJA2Sample( pSoldier->ubID, TURRET_STOP, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ), TRUE ); } } // Turn off! pSoldier->uiStatusFlags &= (~SOLDIER_LOOK_NEXT_TURNSOLDIER ); pSoldier->fDontUnsetLastTargetFromTurn = FALSE; // Unset ui busy if from ui if ( pSoldier->bTurningFromUI && ( pSoldier->fTurningFromPronePosition != 3 ) && ( pSoldier->fTurningFromPronePosition != 1 ) ) { UnSetUIBusy( pSoldier->ubID ); pSoldier->bTurningFromUI = FALSE; } if ( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE ) || CREATURE_OR_BLOODCAT( pSoldier ) ) { pSoldier->uiStatusFlags &= (~SOLDIER_PAUSEANIMOVE); } FreeUpNPCFromTurning( pSoldier, LOOK); // Undo our flag for prone turning... // Else check if we are trying to shoot and once was prone, but am now crouched because we needed to turn... if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_ON ) { // ATE: Don't do this if we have something in our hands we are going to throw! if ( IsValidStance( pSoldier, ANIM_PRONE ) && pSoldier->pTempObject == NULL ) { SendChangeSoldierStanceEvent( pSoldier, ANIM_PRONE ); } pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF; } // If a special code, make guy crawl after stance change! if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE && pSoldier->usAnimState != PRONE_UP && pSoldier->usAnimState != PRONE_DOWN ) { if ( IsValidStance( pSoldier, ANIM_PRONE ) ) { EVENT_InitNewSoldierAnim( pSoldier, CRAWLING, 0, FALSE ); } } if ( pSoldier->uiStatusFlags & SOLDIER_TURNINGFROMHIT ) { // This section seems problem-prone. It relies on all animations happening without interruption. There must be a more // foolproof method. if ( pSoldier->fGettingHit == 1 ) { if ( pSoldier->usPendingAnimation != FALLFORWARD_ROOF && pSoldier->usPendingAnimation != FALLOFF && pSoldier->usAnimState != FALLFORWARD_ROOF && pSoldier->usAnimState != FALLOFF ) { // Go back to original direction EVENT_SetSoldierDesiredDirection( pSoldier, (INT8)pSoldier->uiPendingActionData1 ); //SETUP GETTING HIT FLAG TO 2 pSoldier->fGettingHit = 2; } else { pSoldier->uiStatusFlags &= (~SOLDIER_TURNINGFROMHIT ); } } else if ( pSoldier->fGettingHit == 2 ) { // Turn off pSoldier->uiStatusFlags &= (~SOLDIER_TURNINGFROMHIT ); // Release attacker // DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Releasesoldierattacker, turning from hit animation ended") ); // ReleaseSoldiersAttacker( pSoldier ); DebugAttackBusy( "Finished turning from hit. Reducing attack busy.\n"); ReduceAttackBusyCount( ); //FREEUP GETTING HIT FLAG // pSoldier->fGettingHit = FALSE; } } return; } // IF WE ARE HERE, WE ARE IN THE PROCESS OF TURNING // DOUBLE CHECK TO UNSET fNOAPs... if ( pSoldier->fNoAPToFinishMove ) { AdjustNoAPToFinishMove( pSoldier, FALSE ); } // Do something different for vehicles.... if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) { fDoDirectionChange = FALSE; // Get new direction /* sDirection = pSoldier->ubHiResDirection + ExtQuickestDirection( pSoldier->ubHiResDirection, pSoldier->ubHiResDesiredDirection ); */ sDirection = pSoldier->ubHiResDirection + pSoldier->bTurningIncrement; if (sDirection > 31) { sDirection = 0; } else { if ( sDirection < 0 ) { sDirection = 31; } } pSoldier->ubHiResDirection = (UINT8)sDirection; // Are we at a multiple of a 'cardnal' direction? for ( cnt = 0; cnt < 8; cnt++ ) { if ( sDirection == ubExtDirection[ cnt ] ) { fDoDirectionChange = TRUE; sDirection = (INT16)cnt; break; } } if ( pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE ) { if ( pSoldier->iTuringSoundID == NO_SAMPLE ) { pSoldier->iTuringSoundID = PlaySoldierJA2Sample( pSoldier->ubID, TURRET_MOVE, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 100, SoundDir( pSoldier->sGridNo ), TRUE ); } } } else { // Get new direction //sDirection = pSoldier->bDirection + QuickestDirection( pSoldier->bDirection, pSoldier->bDesiredDirection ); sDirection = pSoldier->bDirection + pSoldier->bTurningIncrement; if (sDirection > 7) { sDirection = 0; } else { if ( sDirection < 0 ) { sDirection = 7; } } } // CHECK FOR A VALID TURN DIRECTION // This is needed for prone animations as well as any multi-tiled structs if ( fDoDirectionChange ) { if ( OKToAddMercToWorld( pSoldier, (INT8)sDirection ) ) { // Don't do this if we are walkoing off screen... if ( gubWaitingForAllMercsToExitCode == WAIT_FOR_MERCS_TO_WALKOFF_SCREEN || gubWaitingForAllMercsToExitCode == WAIT_FOR_MERCS_TO_WALK_TO_GRIDNO ) { } else { // ATE: We should only do this if we are STATIONARY! if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_STATIONARY ) ) { pSoldier->uiStatusFlags |= SOLDIER_LOOK_NEXT_TURNSOLDIER; } // otherwise, it's handled next tile... } EVENT_SetSoldierDirection( pSoldier, sDirection ); if ( pSoldier->ubBodyType != LARVAE_MONSTER && !MercInWater( pSoldier ) && pSoldier->bOverTerrainType != DIRT_ROAD && pSoldier->bOverTerrainType != PAVED_ROAD ) { PlaySoldierFootstepSound( pSoldier ); } } else { // Are we prone crawling? if ( pSoldier->usAnimState == CRAWLING ) { // OK, we want to getup, turn and go prone again.... SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH ); pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE; } // If we are a creature, or multi-tiled, cancel AI action.....? else if ( pSoldier->uiStatusFlags & SOLDIER_MULTITILE ) { pSoldier->bDesiredDirection = pSoldier->bDirection; } } } } UINT8 gRedGlowR[]= { 0, // Normal shades 25, 50, 75, 100, 125, 150, 175, 200, 225, 0, // For gray palettes 25, 50, 75, 100, 125, 150, 175, 200, 225, }; #if 0 UINT8 gOrangeGlowR[]= { 0, // Normal shades 20, 40, 60, 80, 100, 120, 140, 160, 180, 0, // For gray palettes 20, 40, 60, 80, 100, 120, 140, 160, 180, }; #endif UINT8 gOrangeGlowR[]= { 0, // Normal shades 25, 50, 75, 100, 125, 150, 175, 200, 225, 0, // For gray palettes 25, 50, 75, 100, 125, 150, 175, 200, 225, }; #if 0 UINT8 gOrangeGlowG[]= { 0, // Normal shades 5, 10, 25, 30, 35, 40, 45, 50, 55, 0, // For gray palettes 5, 10, 25, 30, 35, 40, 45, 50, 55, }; #endif UINT8 gOrangeGlowG[]= { 0, // Normal shades 20, 40, 60, 80, 100, 120, 140, 160, 180, 0, // For gray palettes 20, 40, 60, 80, 100, 120, 140, 160, 180, }; BOOLEAN CreateSoldierPalettes( SOLDIERTYPE *pSoldier ) { UINT16 usAnimSurface, usPaletteAnimSurface; CHAR8 zColFilename[ 100 ]; INT32 iWhich; INT32 cnt; INT8 bBodyTypePalette; SGPPaletteEntry Temp8BPPPalette[ 256 ]; //NT32 uiCount; //PPaletteEntry Pal[256]; if ( pSoldier->p8BPPPalette != NULL ) { MemFree( pSoldier->p8BPPPalette ); pSoldier->p8BPPPalette = NULL; } // Allocate mem for new palette pSoldier->p8BPPPalette = (SGPPaletteEntry *) MemAlloc( sizeof( SGPPaletteEntry ) * 256 ); memset( pSoldier->p8BPPPalette, 0, sizeof( SGPPaletteEntry ) * 256 ); CHECKF( pSoldier->p8BPPPalette != NULL ); // --- TAKE FROM CURRENT ANIMATION HVOBJECT! usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState ); CHECKF( usAnimSurface != INVALID_ANIMATION_SURFACE ); if ( ( bBodyTypePalette = GetBodyTypePaletteSubstitutionCode( pSoldier, pSoldier->ubBodyType, zColFilename ) ) == -1 ) { // ATE: here we want to use the breath cycle for the palette..... usPaletteAnimSurface = LoadSoldierAnimationSurface( pSoldier, STANDING ); if ( usPaletteAnimSurface != INVALID_ANIMATION_SURFACE ) { // Use palette from HVOBJECT, then use substitution for pants, etc memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[ usPaletteAnimSurface ].hVideoObject->pPaletteEntry, sizeof( pSoldier->p8BPPPalette ) * 256 ); // Substitute based on head, etc SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->HeadPal ); SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->VestPal ); SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->PantsPal ); SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->SkinPal ); } } else if ( bBodyTypePalette == 0 ) { // Use palette from hvobject memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->pPaletteEntry, sizeof( pSoldier->p8BPPPalette ) * 256 ); } else { // Use col file if ( CreateSGPPaletteFromCOLFile( Temp8BPPPalette, zColFilename ) ) { // Copy into palette memcpy( pSoldier->p8BPPPalette, Temp8BPPPalette, sizeof( pSoldier->p8BPPPalette ) * 256 ); } else { // Use palette from hvobject memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->pPaletteEntry, sizeof( pSoldier->p8BPPPalette ) * 256 ); } } if ( pSoldier->p16BPPPalette != NULL ) { MemFree( pSoldier->p16BPPPalette ); pSoldier->p16BPPPalette = NULL; } // -- BUILD 16BPP Palette from this pSoldier->p16BPPPalette = Create16BPPPalette( pSoldier->p8BPPPalette ); for ( iWhich = 0; iWhich < NUM_SOLDIER_SHADES; iWhich++ ) { if ( pSoldier->pShades[ iWhich ] != NULL ) { MemFree( pSoldier->pShades[ iWhich ] ); pSoldier->pShades[ iWhich ] = NULL; } } for ( iWhich = 0; iWhich < NUM_SOLDIER_EFFECTSHADES; iWhich++ ) { if ( pSoldier->pEffectShades[ iWhich ] != NULL ) { MemFree( pSoldier->pEffectShades[ iWhich ] ); pSoldier->pEffectShades[ iWhich ] = NULL; } } for ( iWhich = 0; iWhich < 20; iWhich++ ) { if ( pSoldier->pGlowShades[ iWhich ] != NULL ) { MemFree( pSoldier->pGlowShades[ iWhich ] ); pSoldier->pGlowShades[ iWhich ] = NULL; } } CreateSoldierPaletteTables( pSoldier, HVOBJECT_GLOW_GREEN ); // Build a grayscale palette for testing grayout of mercs //for(uiCount=0; uiCount < 256; uiCount++) //{ // Pal[uiCount].peRed=(UINT8)(uiCount%128)+128; // Pal[uiCount].peGreen=(UINT8)(uiCount%128)+128; // Pal[uiCount].peBlue=(UINT8)(uiCount%128)+128; //} pSoldier->pEffectShades[ 0 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 100, 100, 100, TRUE ); pSoldier->pEffectShades[ 1 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 100, 150, 100, TRUE ); // Build shades for glowing visible bad guy // First do visible guy pSoldier->pGlowShades[ 0 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 255, 255, 255, FALSE ); for ( cnt = 1; cnt < 10; cnt++ ) { pSoldier->pGlowShades[ cnt ] = CreateEnemyGlow16BPPPalette( pSoldier->p8BPPPalette, gRedGlowR[cnt], 255, FALSE ); } // Now for gray guy... pSoldier->pGlowShades[ 10 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 100, 100, 100, TRUE ); for ( cnt = 11; cnt < 19; cnt++ ) { pSoldier->pGlowShades[ cnt ] = CreateEnemyGreyGlow16BPPPalette( pSoldier->p8BPPPalette, gRedGlowR[cnt], 0, FALSE ); } pSoldier->pGlowShades[ 19 ] = CreateEnemyGreyGlow16BPPPalette( pSoldier->p8BPPPalette, gRedGlowR[18], 0, FALSE ); // ATE: OK, piggyback on the shades we are not using for 2 colored lighting.... // ORANGE, VISIBLE GUY pSoldier->pShades[ 20 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 255, 255, 255, FALSE ); for ( cnt = 21; cnt < 30; cnt++ ) { pSoldier->pShades[ cnt ] = CreateEnemyGlow16BPPPalette( pSoldier->p8BPPPalette, gOrangeGlowR[ ( cnt - 20 )], gOrangeGlowG[ ( cnt - 20 ) ], TRUE ); } // ORANGE, GREY GUY pSoldier->pShades[ 30 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 100, 100, 100, TRUE ); for ( cnt = 31; cnt < 39; cnt++ ) { pSoldier->pShades[ cnt ] = CreateEnemyGreyGlow16BPPPalette( pSoldier->p8BPPPalette, gOrangeGlowR[ ( cnt - 20 ) ], gOrangeGlowG[ ( cnt - 20 ) ], TRUE ); } pSoldier->pShades[ 39 ] = CreateEnemyGreyGlow16BPPPalette( pSoldier->p8BPPPalette, gOrangeGlowR[18], gOrangeGlowG[18], TRUE ); return( TRUE ); } void AdjustAniSpeed( SOLDIERTYPE *pSoldier ) { if ( ( gTacticalStatus.uiFlags & SLOW_ANIMATION ) ) { if ( gTacticalStatus.bRealtimeSpeed == -1 ) { pSoldier->sAniDelay = 10000; } else { pSoldier->sAniDelay = pSoldier->sAniDelay * ( 1 * gTacticalStatus.bRealtimeSpeed / 2 ); } } RESETTIMECOUNTER( pSoldier->UpdateCounter, pSoldier->sAniDelay ); } void CalculateSoldierAniSpeed( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pStatsSoldier ) { DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"CalculateSoldierAniSpeed"); UINT32 uiTerrainDelay; UINT32 uiSpeed = 0; INT8 bBreathDef, bLifeDef, bAgilDef; INT8 bAdditional = 0; // for those animations which have a speed of zero, we have to calculate it // here. Some animation, such as water-movement, have an ADDITIONAL speed switch( pSoldier->usAnimState ) { // Lesh: bursting animation delay control begins // Add your animation ID to control it case STANDING_BURST: case FIRE_STAND_BURST_SPREAD: case FIRE_BURST_LOW_STAND: case TANK_BURST: case CROUCHED_BURST: case PRONE_BURST: pSoldier->sAniDelay = Weapon[Item[pSoldier->inv[HANDPOS].usItem].ubClassIndex].sAniDelay; AdjustAniSpeed( pSoldier ); return; // Lesh: end case PRONE: case STANDING: pSoldier->sAniDelay = ( pStatsSoldier->bBreath * 2 ) + (100 - pStatsSoldier->bLife ); // Limit it! if ( pSoldier->sAniDelay < 40 ) { pSoldier->sAniDelay = 40; } AdjustAniSpeed( pSoldier ); return; case CROUCHING: pSoldier->sAniDelay = ( pStatsSoldier->bBreath * 2 ) + ( (100 - pStatsSoldier->bLife ) ); // Limit it! if ( pSoldier->sAniDelay < 40 ) { pSoldier->sAniDelay = 40; } AdjustAniSpeed( pSoldier ); return; case WALKING: // Adjust based on body type bAdditional = (UINT8)( gubAnimWalkSpeeds[ pStatsSoldier->ubBodyType ].sSpeed ); if ( bAdditional < 0 ) bAdditional = 0; break; case RUNNING: // Adjust based on body type bAdditional = (UINT8)gubAnimRunSpeeds[ pStatsSoldier->ubBodyType ].sSpeed; if ( bAdditional < 0 ) bAdditional = 0; break; case SWATTING: // Adjust based on body type if ( pStatsSoldier->ubBodyType <= REGFEMALE ) { bAdditional = (UINT8)gubAnimSwatSpeeds[ pStatsSoldier->ubBodyType ].sSpeed; if ( bAdditional < 0 ) bAdditional = 0; } break; case CRAWLING: // Adjust based on body type if ( pStatsSoldier->ubBodyType <= REGFEMALE ) { bAdditional = (UINT8)gubAnimCrawlSpeeds[ pStatsSoldier->ubBodyType ].sSpeed; if ( bAdditional < 0 ) bAdditional = 0; } break; case READY_RIFLE_STAND: // Raise rifle based on aim vs non-aim. if ( pSoldier->bAimTime == 0 ) { // Quick shot pSoldier->sAniDelay = 70; } else { pSoldier->sAniDelay = 150; } AdjustAniSpeed( pSoldier ); return; } // figure out movement speed (terrspeed) if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) { uiSpeed = gsTerrainTypeSpeedModifiers[ pStatsSoldier->bOverTerrainType ]; uiTerrainDelay = uiSpeed; } else { uiTerrainDelay = 40; // standing still } bBreathDef = 50 - ( pStatsSoldier->bBreath / 2 ); if ( bBreathDef > 30 ) bBreathDef = 30; bAgilDef = 50 - ( EffectiveAgility( pStatsSoldier ) / 4 ); bLifeDef = 50 - ( pStatsSoldier->bLife / 2 ); uiTerrainDelay += ( bLifeDef + bBreathDef + bAgilDef + bAdditional ); pSoldier->sAniDelay = (INT16)uiTerrainDelay; // If a moving animation and w/re on drugs, increase speed.... if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) { if ( GetDrugEffect( pSoldier, DRUG_TYPE_ADRENALINE ) ) { pSoldier->sAniDelay = pSoldier->sAniDelay / 2; } } // MODIFTY NOW BASED ON REAL-TIME, ETC // Adjust speed, make twice as fast if in turn-based! if ( gTacticalStatus.uiFlags & TURNBASED && ( gTacticalStatus.uiFlags & INCOMBAT ) ) { pSoldier->sAniDelay = pSoldier->sAniDelay / 2; } // MODIFY IF REALTIME COMBAT if ( !( gTacticalStatus.uiFlags & INCOMBAT ) ) { // ATE: If realtime, and stealth mode... if ( pStatsSoldier->bStealthMode ) { pSoldier->sAniDelay = (INT16)( pSoldier->sAniDelay * 2 ); } //pSoldier->sAniDelay = pSoldier->sAniDelay * ( 1 * gTacticalStatus.bRealtimeSpeed / 2 ); } } UINT8 GetSpeedUpFactor( ) { switch( gTacticalStatus.ubCurrentTeam ) { case OUR_TEAM: return gGameExternalOptions.gubPlayerTurnSpeedUpFactor; case ENEMY_TEAM: return gGameExternalOptions.gubEnemyTurnSpeedUpFactor; case CREATURE_TEAM: return gGameExternalOptions.gubCreatureTurnSpeedUpFactor; case MILITIA_TEAM: return gGameExternalOptions.gubMilitiaTurnSpeedUpFactor; case CIV_TEAM: return gGameExternalOptions.gubCivilianTurnSpeedUpFactor; } return 1; } void SetSoldierAniSpeed( SOLDIERTYPE *pSoldier ) { SOLDIERTYPE *pStatsSoldier; DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"SetSoldierAniSpeed"); // ATE: If we are an enemy and are not visible...... // Set speed to 0 if ( ( gTacticalStatus.uiFlags & TURNBASED && ( gTacticalStatus.uiFlags & INCOMBAT ) ) || gTacticalStatus.fAutoBandageMode ) { if ( ( ( pSoldier->bVisible == -1 && pSoldier->bVisible == pSoldier->bLastRenderVisibleValue ) || gTacticalStatus.fAutoBandageMode ) && pSoldier->usAnimState != MONSTER_UP ) { if ( pSoldier->bDoBurst && !PTR_OURTEAM ) { pSoldier->sAniDelay = 50; } else { pSoldier->sAniDelay = 0; } RESETTIMECOUNTER( pSoldier->UpdateCounter, pSoldier->sAniDelay ); return; } } // Default stats soldier to same as normal soldier..... pStatsSoldier = pSoldier; if ( pSoldier->fUseMoverrideMoveSpeed ) { pStatsSoldier = MercPtrs[ pSoldier->bOverrideMoveSpeed ]; } // Only calculate if set to zero if ( ( pSoldier->sAniDelay = gAnimControl[ pSoldier->usAnimState ].sSpeed ) == 0 ) { CalculateSoldierAniSpeed( pSoldier, pStatsSoldier ); } AdjustAniSpeed( pSoldier ); if ( _KeyDown( SPACE ) ) { //pSoldier->sAniDelay = 1000; } if ( gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT ) if( GetSpeedUpFactor() ) pSoldier->sAniDelay /= GetSpeedUpFactor(); else pSoldier->sAniDelay = 0; } /////////////////////////////////////////////////////// //PALETTE REPLACEMENT FUNCTIONS /////////////////////////////////////////////////////// BOOLEAN LoadPaletteData( ) { HWFILE hFile; UINT32 cnt, cnt2; hFile = FileOpen( PALETTEFILENAME, FILE_ACCESS_READ, FALSE ); // Read # of types if ( !FileRead( hFile, &guiNumPaletteSubRanges, sizeof( guiNumPaletteSubRanges ), (UINT32 *)NULL ) ) { return( FALSE ); } // Malloc! gpPaletteSubRanges = (PaletteSubRangeType *) MemAlloc( sizeof( PaletteSubRangeType ) * guiNumPaletteSubRanges ); gubpNumReplacementsPerRange = (UINT8 *) MemAlloc( sizeof( UINT8 ) * guiNumPaletteSubRanges ); // Read # of types for each! for ( cnt = 0; cnt < guiNumPaletteSubRanges; cnt++ ) { if ( !FileRead( hFile, &gubpNumReplacementsPerRange[ cnt ], sizeof( UINT8 ), (UINT32 *)NULL ) ) { return( FALSE ); } } // Loop for each one, read in data for ( cnt = 0; cnt < guiNumPaletteSubRanges; cnt++ ) { if ( !FileRead( hFile, &gpPaletteSubRanges[ cnt ].ubStart, sizeof( UINT8 ), (UINT32 *)NULL ) ) { return( FALSE ); } if ( !FileRead( hFile, &gpPaletteSubRanges[ cnt ].ubEnd, sizeof( UINT8 ), (UINT32 *)NULL ) ) { return( FALSE ); } } // Read # of palettes if ( !FileRead( hFile, &guiNumReplacements, sizeof( guiNumReplacements ), (UINT32 *)NULL ) ) { return( FALSE ); } // Malloc! gpPalRep = (PaletteReplacementType *) MemAlloc( sizeof( PaletteReplacementType ) * guiNumReplacements ); // Read! for ( cnt = 0; cnt < guiNumReplacements; cnt++ ) { // type if ( !FileRead( hFile, &gpPalRep[ cnt ].ubType, sizeof( gpPalRep[ cnt ].ubType ), (UINT32 *)NULL ) ) { return( FALSE ); } if ( !FileRead( hFile, &gpPalRep[ cnt ].ID, sizeof( gpPalRep[ cnt ].ID ), (UINT32 *)NULL ) ) { return( FALSE ); } // # entries if ( !FileRead( hFile, &gpPalRep[ cnt ].ubPaletteSize, sizeof( gpPalRep[ cnt ].ubPaletteSize ), (UINT32 *)NULL ) ) { return( FALSE ); } // Malloc gpPalRep[ cnt ].r = (UINT8 *) MemAlloc( gpPalRep[ cnt ].ubPaletteSize ); CHECKF( gpPalRep[ cnt ].r != NULL ); gpPalRep[ cnt ].g = (UINT8 *) MemAlloc( gpPalRep[ cnt ].ubPaletteSize ); CHECKF( gpPalRep[ cnt ].g != NULL ); gpPalRep[ cnt ].b = (UINT8 *) MemAlloc( gpPalRep[ cnt ].ubPaletteSize ); CHECKF( gpPalRep[ cnt ].b != NULL ); for( cnt2 = 0; cnt2 < gpPalRep[ cnt ].ubPaletteSize; cnt2++ ) { if ( !FileRead( hFile, &gpPalRep[ cnt ].r[ cnt2 ], sizeof( UINT8 ), (UINT32 *)NULL ) ) { return( FALSE ); } if ( !FileRead( hFile, &gpPalRep[ cnt ].g[ cnt2 ], sizeof( UINT8 ), (UINT32 *)NULL ) ) { return( FALSE ); } if ( !FileRead( hFile, &gpPalRep[ cnt ].b[ cnt2 ], sizeof( UINT8 ), (UINT32 *)NULL ) ) { return( FALSE ); } } } FileClose( hFile ); return( TRUE ); } BOOLEAN SetPaletteReplacement( SGPPaletteEntry *p8BPPPalette, PaletteRepID aPalRep ) { UINT32 cnt2; UINT8 ubType; UINT8 ubPalIndex; CHECKF( GetPaletteRepIndexFromID( aPalRep, &ubPalIndex ) ); // Get range type ubType = gpPalRep[ ubPalIndex ].ubType; for ( cnt2 = gpPaletteSubRanges[ ubType ].ubStart; cnt2 <= gpPaletteSubRanges[ ubType ].ubEnd; cnt2++ ) { p8BPPPalette[ cnt2 ].peRed = gpPalRep[ ubPalIndex ].r[ cnt2 - gpPaletteSubRanges[ ubType ].ubStart ]; p8BPPPalette[ cnt2 ].peGreen = gpPalRep[ ubPalIndex ].g[ cnt2 - gpPaletteSubRanges[ ubType ].ubStart ]; p8BPPPalette[ cnt2 ].peBlue = gpPalRep[ ubPalIndex ].b[ cnt2 - gpPaletteSubRanges[ ubType ].ubStart ]; } return( TRUE ); } BOOLEAN DeletePaletteData( ) { UINT32 cnt; // Free! if ( gpPaletteSubRanges != NULL ) { MemFree( gpPaletteSubRanges ); gpPaletteSubRanges = NULL; } if ( gubpNumReplacementsPerRange != NULL ) { MemFree( gubpNumReplacementsPerRange ); gubpNumReplacementsPerRange = NULL; } for ( cnt = 0; cnt < guiNumReplacements; cnt++ ) { // Free if ( gpPalRep[ cnt ].r != NULL ) { MemFree( gpPalRep[ cnt ].r ); gpPalRep[ cnt ].r = NULL; } if ( gpPalRep[ cnt ].g != NULL ) { MemFree( gpPalRep[ cnt ].g ); gpPalRep[ cnt ].g = NULL; } if ( gpPalRep[ cnt ].b != NULL ) { MemFree( gpPalRep[ cnt ].b ); gpPalRep[ cnt ].b = NULL; } } // Free if ( gpPalRep != NULL ) { MemFree( gpPalRep ); gpPalRep = NULL; } return( TRUE ); } BOOLEAN GetPaletteRepIndexFromID( PaletteRepID aPalRep, UINT8 *pubPalIndex ) { UINT32 cnt; // Check if type exists for ( cnt = 0; cnt < guiNumReplacements; cnt++ ) { if ( COMPARE_PALETTEREP_ID( aPalRep, gpPalRep[ cnt ].ID ) ) { *pubPalIndex = ( UINT8 )cnt; return( TRUE ); } } DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Invalid Palette Replacement ID given"); return( FALSE ); } UINT16 GetNewSoldierStateFromNewStance( SOLDIERTYPE *pSoldier, UINT8 ubDesiredStance ) { UINT16 usNewState; INT8 bCurrentHeight; bCurrentHeight = ( ubDesiredStance - gAnimControl[ pSoldier->usAnimState ].ubEndHeight ); // Now change to appropriate animation switch( bCurrentHeight ) { case ANIM_STAND - ANIM_CROUCH: usNewState = KNEEL_UP; break; case ANIM_CROUCH - ANIM_STAND: usNewState = KNEEL_DOWN; break; case ANIM_STAND - ANIM_PRONE: usNewState = PRONE_UP; break; case ANIM_PRONE - ANIM_STAND: usNewState = KNEEL_DOWN; break; case ANIM_CROUCH - ANIM_PRONE: usNewState = PRONE_UP; break; case ANIM_PRONE - ANIM_CROUCH: usNewState = PRONE_DOWN; break; default: // Cannot get here unless ub desired stance is bogus DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "GetNewSoldierStateFromNewStance bogus ubDesiredStance value %d", ubDesiredStance ) ); usNewState = pSoldier->usAnimState; } return( usNewState ); } void MoveMercFacingDirection( SOLDIERTYPE *pSoldier, BOOLEAN fReverse, FLOAT dMovementDist ) { FLOAT dAngle = (FLOAT)0; // Determine which direction we are in switch( pSoldier->bDirection ) { case NORTH: dAngle = (FLOAT)( -1 * PI ); break; case NORTHEAST: dAngle = (FLOAT)( PI * .75 ); break; case EAST: dAngle = (FLOAT)( PI / 2 ); break; case SOUTHEAST: dAngle = (FLOAT)( PI / 4 ); break; case SOUTH: dAngle = (FLOAT)0; break; case SOUTHWEST: //dAngle = (FLOAT)( PI * -.25 ); dAngle = (FLOAT)-0.786; break; case WEST: dAngle = (FLOAT) ( PI *-.5 ); break; case NORTHWEST: dAngle = (FLOAT) ( PI * -.75 ); break; } if ( fReverse ) { dMovementDist = dMovementDist * -1; } MoveMerc( pSoldier, dMovementDist, dAngle, FALSE ); } void BeginSoldierClimbUpRoof( SOLDIERTYPE *pSoldier ) { INT8 bNewDirection; UINT8 ubWhoIsThere; if ( FindHeigherLevel( pSoldier, pSoldier->sGridNo, pSoldier->bDirection, &bNewDirection ) && ( pSoldier->bLevel == 0 ) ) { if ( EnoughPoints( pSoldier, GetAPsToClimbRoof( pSoldier, FALSE ), 0, TRUE ) ) { //Kaiden: Helps if we look where we are going before we try to climb on top of someone ubWhoIsThere = WhoIsThere2( NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bNewDirection ) ), 1 ); if ( ubWhoIsThere != NOBODY && ubWhoIsThere != pSoldier->ubID ) { return; } else { if (pSoldier->bTeam == gbPlayerNum) { // OK, SET INTERFACE FIRST SetUIBusy( pSoldier->ubID ); } pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bNewDirection ) ); pSoldier->ubPendingDirection = bNewDirection; //pSoldier->usPendingAnimation = CLIMBUPROOF; EVENT_InitNewSoldierAnim( pSoldier, CLIMBUPROOF, 0 , FALSE ); InternalReceivingSoldierCancelServices( pSoldier, FALSE ); InternalGivingSoldierCancelServices( pSoldier, FALSE ); } } } else { DebugAttackBusy( String( "Soldier %d tried to climb where no roof is.\n", pSoldier->ubID ) ); pSoldier->bAction = AI_ACTION_NONE; } } void BeginSoldierClimbFence( SOLDIERTYPE *pSoldier ) { INT8 bDirection; if ( FindFenceJumpDirection( pSoldier, pSoldier->sGridNo, pSoldier->bDirection, &bDirection ) ) { pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bDirection ) ); pSoldier->fDontChargeTurningAPs = TRUE; EVENT_InternalSetSoldierDesiredDirection( pSoldier, bDirection, FALSE, pSoldier->usAnimState ); pSoldier->fTurningUntilDone = TRUE; // ATE: Reset flag to go back to prone... pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF; pSoldier->usPendingAnimation = HOPFENCE; } } UINT32 SleepDartSuccumbChance( SOLDIERTYPE * pSoldier ) { UINT32 uiChance; INT8 bEffectiveStrength; // figure out base chance of succumbing, bEffectiveStrength = EffectiveStrength( pSoldier ); if (bEffectiveStrength > 90) { uiChance = 110 - bEffectiveStrength; } else if (bEffectiveStrength > 80) { uiChance = 120 - bEffectiveStrength; } else if (bEffectiveStrength > 70) { uiChance = 130 - bEffectiveStrength; } else { uiChance = 140 - bEffectiveStrength; } // add in a bonus based on how long it's been since shot... highest chance at the beginning uiChance += (10 - pSoldier->bSleepDrugCounter); return( uiChance ); } void BeginSoldierGetup( SOLDIERTYPE *pSoldier ) { // RETURN IF WE ARE BEING SERVICED if ( pSoldier->ubServiceCount > 0 ) { return; } // ATE: Don't getup if we are in a meanwhile if ( AreInMeanwhile( ) ) { return; } if ( pSoldier->bCollapsed ) { if ( pSoldier->bLife >= OKLIFE && pSoldier->bBreath >= OKBREATH && (pSoldier->bSleepDrugCounter == 0) ) { // get up you hoser! pSoldier->bCollapsed = FALSE; pSoldier->bTurnsCollapsed = 0; if ( IS_MERC_BODY_TYPE( pSoldier ) ) { switch( pSoldier->usAnimState ) { case FALLOFF_FORWARD_STOP: case PRONE_LAYFROMHIT_STOP: case STAND_FALLFORWARD_STOP: ChangeSoldierStance( pSoldier, ANIM_CROUCH ); break; case FALLBACKHIT_STOP: case FALLOFF_STOP: case FLYBACKHIT_STOP: case FALLBACK_HIT_STAND: case FALLOFF: case FLYBACK_HIT: // ROLL OVER EVENT_InitNewSoldierAnim( pSoldier, ROLLOVER, 0 , FALSE ); break; default: ChangeSoldierStance( pSoldier, ANIM_CROUCH ); break; } } else { EVENT_InitNewSoldierAnim( pSoldier, END_COWER, 0 , FALSE ); } } else { pSoldier->bTurnsCollapsed++; if ( (gTacticalStatus.bBoxingState == BOXING) && (pSoldier->uiStatusFlags & SOLDIER_BOXER) ) { if (pSoldier->bTurnsCollapsed > 1) { // We have a winnah! But it isn't this boxer! EndBoxingMatch( pSoldier ); } } } } else if ( pSoldier->bSleepDrugCounter > 0 ) { UINT32 uiChance; uiChance = SleepDartSuccumbChance( pSoldier ); if ( PreRandom( 100 ) < uiChance ) { // succumb to the drug! DeductPoints( pSoldier, 0, (INT16)( pSoldier->bBreathMax * 100 ) ); SoldierCollapse( pSoldier ); } } if ( pSoldier->bSleepDrugCounter > 0 ) { pSoldier->bSleepDrugCounter--; } } void HandleTakeDamageDeath( SOLDIERTYPE *pSoldier, UINT8 bOldLife, UINT8 ubReason ) { switch( ubReason ) { case TAKE_DAMAGE_BLOODLOSS: case TAKE_DAMAGE_ELECTRICITY: case TAKE_DAMAGE_GAS: if ( pSoldier->bInSector ) { if ( pSoldier->bVisible != -1 ) { if ( ubReason != TAKE_DAMAGE_BLOODLOSS ) { DoMercBattleSound( pSoldier, BATTLE_SOUND_DIE1 ); pSoldier->fDeadSoundPlayed = TRUE; } } if ( ( ubReason == TAKE_DAMAGE_ELECTRICITY ) && pSoldier->bLife < OKLIFE ) { pSoldier->fInNonintAnim = FALSE; } // Check for < OKLIFE if ( pSoldier->bLife < OKLIFE && pSoldier->bLife != 0 && !pSoldier->bCollapsed) { SoldierCollapse( pSoldier ); } // THis is for the die animation that will be happening.... if ( pSoldier->bLife == 0 ) { pSoldier->fDoingExternalDeath = TRUE; } // Check if he is dead.... CheckForAndHandleSoldierDyingNotFromHit( pSoldier ); } //if( !( guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) { HandleSoldierTakeDamageFeedback( pSoldier ); } if(( guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) || !pSoldier->bInSector ) { if ( pSoldier->bLife == 0 && !( pSoldier->uiStatusFlags & SOLDIER_DEAD ) ) { StrategicHandlePlayerTeamMercDeath( pSoldier ); // ATE: Here, force always to use die sound... pSoldier->fDieSoundUsed = FALSE; DoMercBattleSound( pSoldier, BATTLE_SOUND_DIE1 ); pSoldier->fDeadSoundPlayed = TRUE; // ATE: DO death sound PlayJA2Sample( (UINT8)DOORCR_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN ); PlayJA2Sample( (UINT8)HEADCR_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN ); } } break; } // 0verhaul: This is also already handled by the animation transitions // if ( ubReason == TAKE_DAMAGE_ELECTRICITY ) // { // if ( pSoldier->bLife >= OKLIFE ) // { // DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Freeing up attacker from electricity damage") ); // ReleaseSoldiersAttacker( pSoldier ); // } // } } UINT8 SoldierTakeDamage( SOLDIERTYPE *pSoldier, INT8 bHeight, INT16 sLifeDeduct, INT16 sBreathLoss, UINT8 ubReason, UINT8 ubAttacker, INT16 sSourceGrid, INT16 sSubsequent, BOOLEAN fShowDamage ) { INT8 bOldLife; UINT8 ubCombinedLoss; INT8 bBandage; INT16 sAPCost; UINT8 ubBlood; UINT16 usItemFlags=0; // Kaiden: Needed for the reveal all items after combat code from UB. pSoldier->ubLastDamageReason = ubReason; // CJC Jan 21 99: add check to see if we are hurting an enemy in an enemy-controlled // sector; if so, this is a sign of player activity switch ( pSoldier->bTeam ) { case ENEMY_TEAM: // if we're in the wilderness this always counts if ( StrategicMap[ CALCULATE_STRATEGIC_INDEX( gWorldSectorX, gWorldSectorY ) ].fEnemyControlled || SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ].ubTraversability[ THROUGH_STRATEGIC_MOVE ] != TOWN ) { // update current day of activity! UpdateLastDayOfPlayerActivity( (UINT16) GetWorldDay() ); } break; case CREATURE_TEAM: // always a sign of activity? UpdateLastDayOfPlayerActivity( (UINT16) GetWorldDay() ); break; case CIV_TEAM: if ( pSoldier->ubCivilianGroup == KINGPIN_CIV_GROUP && gubQuest[ QUEST_RESCUE_MARIA ] == QUESTINPROGRESS && gTacticalStatus.bBoxingState == NOT_BOXING ) { SOLDIERTYPE * pMaria = FindSoldierByProfileID( MARIA, FALSE ); if ( pMaria && pMaria->bActive && pMaria->bInSector ) { SetFactTrue( FACT_MARIA_ESCAPE_NOTICED ); } } break; default: break; } // Deduct life!, Show damage if we want! bOldLife = pSoldier->bLife; // OK, If we are a vehicle.... damage vehicle...( people inside... ) if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) { if ( TANK( pSoldier ) ) { //sLifeDeduct = (sLifeDeduct * 2) / 3; } else { if ( ubReason == TAKE_DAMAGE_GUNFIRE ) { sLifeDeduct /= 3; } else if ( ubReason == TAKE_DAMAGE_EXPLOSION && sLifeDeduct > 50 ) { // boom! sLifeDeduct *= 2; } } VehicleTakeDamage( pSoldier->bVehicleID, ubReason, sLifeDeduct, pSoldier->sGridNo, ubAttacker ); HandleTakeDamageDeath( pSoldier, bOldLife, ubReason ); return( 0 ); } // ATE: If we are elloit being attacked in a meanwhile... if ( pSoldier->uiStatusFlags & SOLDIER_NPC_SHOOTING ) { // Almost kill but not quite..... sLifeDeduct = ( pSoldier->bLife - 1 ); // Turn off pSoldier->uiStatusFlags &= ( ~SOLDIER_NPC_SHOOTING ); } // CJC: make sure Elliot doesn't bleed to death! if ( ubReason == TAKE_DAMAGE_BLOODLOSS && AreInMeanwhile() ) { return( 0 ); } // Calculate bandage bBandage = pSoldier->bLifeMax - pSoldier->bLife - pSoldier->bBleeding; if( guiCurrentScreen == MAP_SCREEN ) { fReDrawFace = TRUE; } if ( CREATURE_OR_BLOODCAT( pSoldier ) ) { INT16 sReductionFactor = 0; if ( pSoldier->ubBodyType == BLOODCAT ) { sReductionFactor = 2; } else if (pSoldier->uiStatusFlags & SOLDIER_MONSTER) { switch( pSoldier->ubBodyType ) { case LARVAE_MONSTER: case INFANT_MONSTER: sReductionFactor = 1; break; case YAF_MONSTER: case YAM_MONSTER: sReductionFactor = 4; break; case ADULTFEMALEMONSTER: case AM_MONSTER: sReductionFactor = 6; break; case QUEENMONSTER: // increase with range! if ( ubAttacker == NOBODY ) { sReductionFactor = 8; } else { sReductionFactor = 4 + PythSpacesAway( MercPtrs[ ubAttacker ]->sGridNo, pSoldier->sGridNo ) / 2; } break; } } if ( ubReason == TAKE_DAMAGE_EXPLOSION ) { sReductionFactor /= 4; } if ( sReductionFactor > 1 ) { sLifeDeduct = (sLifeDeduct + (sReductionFactor / 2 ) ) / sReductionFactor; } else if ( ubReason == TAKE_DAMAGE_EXPLOSION ) { // take at most 2/3rds sLifeDeduct = (sLifeDeduct * 2) / 3; } // reduce breath loss to a smaller degree, except for the queen... if ( pSoldier->ubBodyType == QUEENMONSTER ) { // in fact, reduce breath loss by MORE! sReductionFactor = __min( sReductionFactor, 8 ); sReductionFactor *= 2; } else { sReductionFactor /= 2; } if ( sReductionFactor > 1 ) { sBreathLoss = (sBreathLoss + (sReductionFactor / 2 ) ) / sReductionFactor; } } if (sLifeDeduct > pSoldier->bLife) { pSoldier->bLife = 0; } else { // Decrease Health pSoldier->bLife -= sLifeDeduct; } // ATE: Put some logic in here to allow enemies to die quicker..... // Are we an enemy? if ( pSoldier->bSide != gbPlayerNum && !pSoldier->bNeutral && pSoldier->ubProfile == NO_PROFILE ) { // ATE: Give them a chance to fall down... if ( pSoldier->bLife > 0 && pSoldier->bLife < ( OKLIFE - 1 ) ) { // Are we taking damage from bleeding? if ( ubReason == TAKE_DAMAGE_BLOODLOSS ) { // Fifty-fifty chance to die now! if ( Random( 2 ) == 0 || gTacticalStatus.Team[ pSoldier->bTeam ].bMenInSector == 1 ) { // Kill! pSoldier->bLife = 0; } } else { // OK, see how far we are.. if ( pSoldier->bLife < ( OKLIFE - 3 ) ) { // Kill! pSoldier->bLife = 0; } } } } if ( fShowDamage ) { pSoldier->sDamage += sLifeDeduct; } // Truncate life if ( pSoldier->bLife < 0 ) { pSoldier->bLife = 0; } // Calculate damage to our items if from an explosion! if ( ubReason == TAKE_DAMAGE_EXPLOSION || ubReason == TAKE_DAMAGE_STRUCTURE_EXPLOSION) { CheckEquipmentForDamage( pSoldier, sLifeDeduct ); } // Calculate bleeding if ( ubReason != TAKE_DAMAGE_GAS && !AM_A_ROBOT( pSoldier ) ) { if ( ubReason == TAKE_DAMAGE_HANDTOHAND ) { if ( sLifeDeduct > 0 ) { // HTH does 1 pt bleeding per hit pSoldier->bBleeding = pSoldier->bBleeding + 1; } } else { pSoldier->bBleeding = pSoldier->bLifeMax - ( pSoldier->bLife + bBandage ); } } // Deduct breath AND APs! sAPCost = (sLifeDeduct / AP_GET_WOUNDED_DIVISOR); // + fallCost; // ATE: if the robot, do not deduct if ( !AM_A_ROBOT( pSoldier ) ) { DeductPoints( pSoldier, sAPCost, sBreathLoss , FALSE); } ubCombinedLoss = (UINT8) sLifeDeduct / 10 + sBreathLoss / 2000; // Add shock if ( !AM_A_ROBOT( pSoldier ) ) { pSoldier->bShock += ubCombinedLoss; } // start the stopwatch - the blood is gushing! pSoldier->dNextBleed = CalcSoldierNextBleed( pSoldier ); if ( pSoldier->bInSector && pSoldier->bVisible != -1 ) { // If we are already dead, don't show damage! if ( bOldLife != 0 && fShowDamage && sLifeDeduct != 0 && sLifeDeduct < 1000 ) { // Display damage INT16 sOffsetX, sOffsetY; // Set Damage display counter pSoldier->fDisplayDamage = TRUE; pSoldier->bDisplayDamageCount = 0; if ( pSoldier->ubBodyType == QUEENMONSTER ) { pSoldier->sDamageX = 0; pSoldier->sDamageY = 0; } else { GetSoldierAnimOffsets( pSoldier, &sOffsetX, &sOffsetY ); pSoldier->sDamageX = sOffsetX; pSoldier->sDamageY = sOffsetY; } } } // OK, if here, let's see if we should drop our weapon.... if ( ubReason != TAKE_DAMAGE_BLOODLOSS && !(AM_A_ROBOT( pSoldier )) ) { INT16 sTestOne, sTestTwo, sChanceToDrop; INT8 bVisible = -1; sTestOne = EffectiveStrength( pSoldier ); sTestTwo = ( 2 * ( __max( sLifeDeduct, ( sBreathLoss / 100 ) ) ) ); if (pSoldier->ubAttackerID != NOBODY && MercPtrs[ pSoldier->ubAttackerID ]->ubBodyType == BLOODCAT ) { // bloodcat boost, let them make people drop items more sTestTwo += 20; } // If damage > effective strength.... sChanceToDrop = ( __max( 0, ( sTestTwo - sTestOne ) ) ); // ATE: Increase odds of NOT dropping an UNDROPPABLE OBJECT if ( ( pSoldier->inv[ HANDPOS ].fFlags & OBJECT_UNDROPPABLE ) ) { sChanceToDrop -= 30; } #ifdef JA2TESTVERSION //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Chance To Drop Weapon: str: %d Dam: %d Chance: %d", sTestOne, sTestTwo, sChanceToDrop ); #endif if ( Random( 100 ) < (UINT16) sChanceToDrop ) { // OK, drop item in main hand... if ( pSoldier->inv[ HANDPOS ].usItem != NOTHING ) { if ( !( pSoldier->inv[ HANDPOS ].fFlags & OBJECT_UNDROPPABLE ) ) { // ATE: if our guy, make visible.... if ( pSoldier->bTeam == gbPlayerNum ) { bVisible = 1; } //if this soldier was an enemy // Kaiden Added for UB reveal All items after combat feature! else if( pSoldier->bTeam == ENEMY_TEAM ) { //add a flag to the item so when all enemies are killed, we can run through and reveal all the enemies items usItemFlags |= WORLD_ITEM_DROPPED_FROM_ENEMY; } AddItemToPool( pSoldier->sGridNo, &(pSoldier->inv[ HANDPOS ]), bVisible, pSoldier->bLevel, usItemFlags, -1 ); //Madd: added usItemFlags to function arguments DeleteObj( &(pSoldier->inv[HANDPOS]) ); } } } } // Drop some blood! // decide blood amt, if any ubBlood = ( sLifeDeduct / BLOODDIVISOR); if ( ubBlood > MAXBLOODQUANTITY ) { ubBlood = MAXBLOODQUANTITY; } if ( !( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE | SOLDIER_ROBOT ) ) ) { if ( ubBlood != 0 ) { if ( pSoldier->bInSector ) { DropBlood( pSoldier, ubBlood, pSoldier->bVisible ); } } } //Set UI Flag for unconscious, if it's our own guy! if ( pSoldier->bTeam == gbPlayerNum ) { if ( pSoldier->bLife < OKLIFE && pSoldier->bLife > 0 && bOldLife >= OKLIFE ) { pSoldier->fUIFirstTimeUNCON = TRUE; fInterfacePanelDirty = DIRTYLEVEL2; } } if ( pSoldier->bInSector ) { CheckForBreathCollapse( pSoldier ); } // EXPERIENCE CLASS GAIN (combLoss): Getting wounded in battle DirtyMercPanelInterface( pSoldier, DIRTYLEVEL1 ); if ( ubAttacker != NOBODY ) { // don't give exp for hitting friends! if ( (MercPtrs[ ubAttacker ]->bTeam == gbPlayerNum) && (pSoldier->bTeam != gbPlayerNum) ) { if ( ubReason == TAKE_DAMAGE_EXPLOSION ) { // EXPLOSIVES GAIN (combLoss): Causing wounds in battle StatChange( MercPtrs[ ubAttacker ], EXPLODEAMT, (UINT16)( 10 * ubCombinedLoss ), FROM_FAILURE ); } /* else if ( ubReason == TAKE_DAMAGE_GUNFIRE ) { // MARKSMANSHIP GAIN (combLoss): Causing wounds in battle StatChange( MercPtrs[ ubAttacker ], MARKAMT, (UINT16)( 5 * ubCombinedLoss ), FALSE ); } */ } } if (PTR_OURTEAM) { // EXPERIENCE GAIN: Took some damage StatChange( pSoldier, EXPERAMT, ( UINT16 )( 5 * ubCombinedLoss ), FROM_FAILURE ); // Check for quote if ( !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_BEING_PUMMELED ) ) { // Check attacker! if ( ubAttacker != NOBODY && ubAttacker != pSoldier->ubID ) { pSoldier->bNumHitsThisTurn++; if ( (pSoldier->bNumHitsThisTurn >= 3) && ( pSoldier->bLife - pSoldier->bOldLife > 20 ) ) { if ( Random(100) < (UINT16)((40 * ( pSoldier->bNumHitsThisTurn - 2)))) { DelayedTacticalCharacterDialogue( pSoldier, QUOTE_TAKEN_A_BREATING ); pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_BEING_PUMMELED; pSoldier->bNumHitsThisTurn = 0; } } } } } if ((ubAttacker != NOBODY) && (Menptr[ubAttacker].bTeam == OUR_TEAM) && (pSoldier->ubProfile != NO_PROFILE) && (pSoldier->ubProfile >= FIRST_RPC && pSoldier->ubProfile < GASTON )) { gMercProfiles[pSoldier->ubProfile].ubMiscFlags |= PROFILE_MISC_FLAG_WOUNDEDBYPLAYER; if (pSoldier->ubProfile == 114) { SetFactTrue( FACT_PACOS_KILLED ); } } HandleTakeDamageDeath( pSoldier, bOldLife, ubReason ); // Check if we are < unconscious, and shutup if so! also wipe sight if ( pSoldier->bLife < CONSCIOUSNESS ) { ShutupaYoFace( pSoldier->iFaceIndex ); } if ( pSoldier->bLife < OKLIFE ) { DecayIndividualOpplist( pSoldier ); } return( ubCombinedLoss ); } extern BOOLEAN IsMercSayingDialogue( UINT8 ubProfileID ); BOOLEAN InternalDoMercBattleSound( SOLDIERTYPE *pSoldier, UINT8 ubBattleSoundID, INT8 bSpecialCode ) { SGPFILENAME zFilename; SOUNDPARMS spParms; UINT8 ubSoundID; UINT32 uiSoundID; UINT32 iFaceIndex; BOOLEAN fDoSub = FALSE; INT32 uiSubSoundID = 0; BOOLEAN fSpeechSound = FALSE; // DOUBLECHECK RANGE CHECKF ( ubBattleSoundID < NUM_MERC_BATTLE_SOUNDS ); if ( ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) ) { // Pick a passenger from vehicle.... pSoldier = PickRandomPassengerFromVehicle( pSoldier ); if ( pSoldier == NULL ) { return( FALSE ); } } // If a death sound, and we have already done ours... if ( ubBattleSoundID == BATTLE_SOUND_DIE1 ) { if ( pSoldier->fDieSoundUsed ) { return( TRUE ); } } // Are we mute? if ( pSoldier->uiStatusFlags & SOLDIER_MUTE ) { return( FALSE ); } // uiTimeSameBattleSndDone // If we are a creature, etc, pick a better sound... if ( ubBattleSoundID == BATTLE_SOUND_HIT1 || ubBattleSoundID == BATTLE_SOUND_HIT2 ) { switch ( pSoldier->ubBodyType ) { case COW: fDoSub = TRUE; uiSubSoundID = COW_HIT_SND; break; case YAF_MONSTER: case YAM_MONSTER: case ADULTFEMALEMONSTER: case AM_MONSTER: fDoSub = TRUE; if ( Random( 2 ) == 0 ) { uiSubSoundID = ACR_DIE_PART1; } else { uiSubSoundID = ACR_LUNGE; } break; case INFANT_MONSTER: fDoSub = TRUE; uiSubSoundID = BCR_SHRIEK; break; case QUEENMONSTER: fDoSub = TRUE; uiSubSoundID = LQ_SHRIEK; break; case LARVAE_MONSTER: fDoSub = TRUE; uiSubSoundID = BCR_SHRIEK; break; case BLOODCAT: fDoSub = TRUE; uiSubSoundID = BLOODCAT_HIT_1; break; case ROBOTNOWEAPON: fDoSub = TRUE; uiSubSoundID = (UINT32)( S_METAL_IMPACT1 + Random( 2 ) ); break; } } if ( ubBattleSoundID == BATTLE_SOUND_DIE1 ) { switch ( pSoldier->ubBodyType ) { case COW: fDoSub = TRUE; uiSubSoundID = COW_DIE_SND; break; case YAF_MONSTER: case YAM_MONSTER: case ADULTFEMALEMONSTER: case AM_MONSTER: fDoSub = TRUE; uiSubSoundID = CREATURE_FALL_PART_2; break; case INFANT_MONSTER: fDoSub = TRUE; uiSubSoundID = BCR_DYING; break; case LARVAE_MONSTER: fDoSub = TRUE; uiSubSoundID = LCR_RUPTURE; break; case QUEENMONSTER: fDoSub = TRUE; uiSubSoundID = LQ_DYING; break; case BLOODCAT: fDoSub = TRUE; uiSubSoundID = BLOODCAT_DIE_1; break; case ROBOTNOWEAPON: fDoSub = TRUE; uiSubSoundID = (UINT32)( EXPLOSION_1 ); PlayJA2Sample( ROBOT_DEATH, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN ); break; } } // OK. any other sound, not hits, robot makes a beep if ( pSoldier->ubBodyType == ROBOTNOWEAPON && !fDoSub ) { fDoSub = TRUE; if ( ubBattleSoundID == BATTLE_SOUND_ATTN1 ) { uiSubSoundID = ROBOT_GREETING; } else { uiSubSoundID = ROBOT_BEEP; } } if ( fDoSub ) { if( guiCurrentScreen != GAME_SCREEN ) { PlayJA2Sample( uiSubSoundID, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN ); } else { PlayJA2Sample( uiSubSoundID, RATE_11025, SoundVolume( (UINT8)CalculateSpeechVolume( HIGHVOLUME ), pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); } return( TRUE ); } // Check if this is the same one we just played... if ( pSoldier->bOldBattleSnd == ubBattleSoundID && gBattleSndsData[ ubBattleSoundID ].fDontAllowTwoInRow ) { // Are we below the min delay? if ( ( GetJA2Clock( ) - pSoldier->uiTimeSameBattleSndDone ) < MIN_SUBSEQUENT_SNDS_DELAY ) { return( TRUE ); } } // If a battle snd is STILL playing.... if ( SoundIsPlaying( pSoldier->uiBattleSoundID ) ) { // We can do a few things here.... // Is this a crutial one...? if ( gBattleSndsData[ ubBattleSoundID ].fStopDialogue == 1 ) { // Stop playing origonal SoundStop( pSoldier->uiBattleSoundID ); } else { // Skip this one... return( TRUE ); } } // If we are talking now.... if ( IsMercSayingDialogue( pSoldier->ubProfile ) ) { // We can do a couple of things now... if ( gBattleSndsData[ ubBattleSoundID ].fStopDialogue == 1 ) { // Stop dialigue... DialogueAdvanceSpeech( ); } else if ( gBattleSndsData[ ubBattleSoundID ].fStopDialogue == 2 ) { // Skip battle snd... return( TRUE ); } } // Save this one we're doing... pSoldier->bOldBattleSnd = ubBattleSoundID; pSoldier->uiTimeSameBattleSndDone = GetJA2Clock( ); // Adjust based on morale... if ( ubBattleSoundID == BATTLE_SOUND_OK1 && pSoldier->bMorale < LOW_MORALE_BATTLE_SND_THREASHOLD ) { ubBattleSoundID = BATTLE_SOUND_LOWMARALE_OK1; } if ( ubBattleSoundID == BATTLE_SOUND_ATTN1 && pSoldier->bMorale < LOW_MORALE_BATTLE_SND_THREASHOLD ) { ubBattleSoundID = BATTLE_SOUND_LOWMARALE_ATTN1; } ubSoundID = ubBattleSoundID; //if the sound to be played is a confirmation, check to see if we are to play it if( ubSoundID == BATTLE_SOUND_OK1 ) { if( gGameSettings.fOptions[ TOPTION_MUTE_CONFIRMATIONS ] ) return( TRUE ); //else a speech sound is to be played else fSpeechSound = TRUE; } // Randomize between sounds, if appropriate if ( gBattleSndsData[ ubSoundID ].ubRandomVal != 0 ) { ubSoundID = ubSoundID + (UINT8)Random( gBattleSndsData[ ubSoundID ].ubRandomVal ); } // OK, build file and play! if ( pSoldier->ubProfile != NO_PROFILE ) { sprintf( zFilename, "BATTLESNDS\\%03d_%s.ogg", pSoldier->ubProfile, gBattleSndsData[ ubSoundID ].zName ); if ( !FileExists( zFilename ) ) { sprintf( zFilename, "BATTLESNDS\\%03d_%s.wav", pSoldier->ubProfile, gBattleSndsData[ ubSoundID ].zName ); } if ( !FileExists( zFilename ) ) { // OK, temp build file... if ( pSoldier->ubBodyType == REGFEMALE ) { sprintf( zFilename, "BATTLESNDS\\f_%s.ogg", gBattleSndsData[ ubSoundID ].zName ); if ( !FileExists( zFilename ) ) { sprintf( zFilename, "BATTLESNDS\\f_%s.wav", gBattleSndsData[ ubSoundID ].zName ); } } else { sprintf( zFilename, "BATTLESNDS\\m_%s.ogg", gBattleSndsData[ ubSoundID ].zName ); if ( !FileExists( zFilename ) ) { sprintf( zFilename, "BATTLESNDS\\m_%s.wav", gBattleSndsData[ ubSoundID ].zName ); } } } } else { // Check if we can play this! if ( !gBattleSndsData[ ubSoundID ].fBadGuy ) { return( FALSE ); } if ( pSoldier->ubBodyType == HATKIDCIV || pSoldier->ubBodyType == KIDCIV ) { if ( ubSoundID == BATTLE_SOUND_DIE1 ) { sprintf( zFilename, "BATTLESNDS\\kid%d_dying.ogg", pSoldier->ubBattleSoundID ); if ( !FileExists( zFilename ) ) { sprintf( zFilename, "BATTLESNDS\\kid%d_dying.wav", pSoldier->ubBattleSoundID ); } } else { sprintf( zFilename, "BATTLESNDS\\kid%d_%s.ogg", pSoldier->ubBattleSoundID, gBattleSndsData[ ubSoundID ].zName ); if ( !FileExists( zFilename ) ) { sprintf( zFilename, "BATTLESNDS\\kid%d_%s.wav", pSoldier->ubBattleSoundID, gBattleSndsData[ ubSoundID ].zName ); } } } else { if ( ubSoundID == BATTLE_SOUND_DIE1 ) { sprintf( zFilename, "BATTLESNDS\\bad%d_die.ogg", pSoldier->ubBattleSoundID ); if ( !FileExists( zFilename ) ) { sprintf( zFilename, "BATTLESNDS\\bad%d_die.wav", pSoldier->ubBattleSoundID ); } } else { sprintf( zFilename, "BATTLESNDS\\bad%d_%s.ogg", pSoldier->ubBattleSoundID, gBattleSndsData[ ubSoundID ].zName ); if ( !FileExists( zFilename ) ) { sprintf( zFilename, "BATTLESNDS\\bad%d_%s.wav", pSoldier->ubBattleSoundID, gBattleSndsData[ ubSoundID ].zName ); } } } } // Play sound! memset(&spParms, 0xff, sizeof(SOUNDPARMS)); spParms.uiSpeed = RATE_11025; //spParms.uiVolume = CalculateSpeechVolume( pSoldier->bVocalVolume ); spParms.uiVolume = (INT8)CalculateSpeechVolume( HIGHVOLUME ); // ATE: Reduce volume for OK sounds... // ( Only for all-moves or multi-selection cases... ) if ( bSpecialCode == BATTLE_SND_LOWER_VOLUME ) { spParms.uiVolume = (INT8)CalculateSpeechVolume( MIDVOLUME ); } // If we are an enemy.....reduce due to volume if ( pSoldier->bTeam != gbPlayerNum ) { spParms.uiVolume = SoundVolume( (UINT8)spParms.uiVolume, pSoldier->sGridNo ); } spParms.uiLoop = 1; spParms.uiPan = SoundDir( pSoldier->sGridNo ); spParms.uiPriority=GROUP_PLAYER; if ( ( uiSoundID = SoundPlay( zFilename, &spParms ) ) == SOUND_ERROR ) { return( FALSE ); } else { pSoldier->uiBattleSoundID = uiSoundID; if ( pSoldier->ubProfile != NO_PROFILE ) { // Get soldier's face ID iFaceIndex = pSoldier->iFaceIndex; // Check face index if ( iFaceIndex != -1 ) { ExternSetFaceTalking( iFaceIndex, uiSoundID ); } } return( TRUE ); } } BOOLEAN DoMercBattleSound( SOLDIERTYPE *pSoldier, UINT8 ubBattleSoundID ) { // We WANT to play some RIGHT AWAY..... if ( gBattleSndsData[ ubBattleSoundID ].fStopDialogue == 1 || ( pSoldier->ubProfile == NO_PROFILE ) || InOverheadMap( ) ) { return( InternalDoMercBattleSound( pSoldier, ubBattleSoundID, 0 ) ); } // So here, only if we were currently saying dialogue..... if ( !IsMercSayingDialogue( pSoldier->ubProfile ) ) { return( InternalDoMercBattleSound( pSoldier, ubBattleSoundID, 0 ) ); } // OK, queue it up otherwise! TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_DO_BATTLE_SND, ubBattleSoundID,0 ); return( TRUE ); } BOOLEAN PreloadSoldierBattleSounds( SOLDIERTYPE *pSoldier, BOOLEAN fRemove ) { UINT32 cnt; CHECKF( pSoldier->bActive != FALSE ); for ( cnt = 0; cnt < NUM_MERC_BATTLE_SOUNDS; cnt++ ) { // OK, build file and play! if ( pSoldier->ubProfile != NO_PROFILE ) { if ( gBattleSndsData[ cnt ].fPreload ) { if ( fRemove ) { SoundUnlockSample( gBattleSndsData[ cnt ].zName ); } else { SoundLockSample( gBattleSndsData[ cnt ].zName ); } } } else { if ( gBattleSndsData[ cnt ].fPreload && gBattleSndsData[ cnt ].fBadGuy ) { if ( fRemove ) { SoundUnlockSample( gBattleSndsData[ cnt ].zName ); } else { SoundLockSample( gBattleSndsData[ cnt ].zName ); } } } } return( TRUE ); } BOOLEAN CheckSoldierHitRoof( SOLDIERTYPE *pSoldier ) { // Check if we are near a lower level INT8 bNewDirection; BOOLEAN fReturnVal = FALSE; INT16 sNewGridNo; // Default to true BOOLEAN fDoForwards = TRUE; if ( pSoldier->bLife >= OKLIFE ) { return( FALSE ); } if ( FindLowerLevel( pSoldier, pSoldier->sGridNo, pSoldier->bDirection, &bNewDirection ) && ( pSoldier->bLevel > 0 ) ) { // ONly if standing! if ( gAnimControl[ pSoldier->usAnimState ].ubHeight == ANIM_STAND ) { // We are near a lower level. // Use opposite direction bNewDirection = gOppositeDirection[ bNewDirection ]; // Alrighty, let's not blindly change here, look at whether the dest gridno is good! sNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ bNewDirection ] ) ); if ( !NewOKDestination( pSoldier, sNewGridNo, TRUE, 0 ) ) { return( FALSE ); } sNewGridNo = NewGridNo( (UINT16)sNewGridNo, DirectionInc( gOppositeDirection[ bNewDirection ] ) ); if ( !NewOKDestination( pSoldier, sNewGridNo, TRUE, 0 ) ) { return( FALSE ); } // Are wee near enough to fall forwards.... if ( pSoldier->bDirection == gOneCDirection[ bNewDirection ] || pSoldier->bDirection == gTwoCDirection[ bNewDirection ] || pSoldier->bDirection == bNewDirection || pSoldier->bDirection == gOneCCDirection[ bNewDirection ] || pSoldier->bDirection == gTwoCCDirection[ bNewDirection ] ) { // Do backwards... fDoForwards = FALSE; } // If we are facing the opposite direction, fall backwards // ATE: Make this more usefull... if ( fDoForwards ) { pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (INT16)( -1 * DirectionInc(bNewDirection ) ) ); pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sTempNewGridNo, (INT16)( -1 * DirectionInc( bNewDirection ) ) ); EVENT_SetSoldierDesiredDirection( pSoldier, gOppositeDirection[ bNewDirection ] ); pSoldier->fTurningUntilDone = TRUE; pSoldier->usPendingAnimation = FALLFORWARD_ROOF; //EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_ROOF, 0 , FALSE ); // Deduct hitpoints/breath for falling! SoldierTakeDamage( pSoldier, ANIM_CROUCH, 100, 5000, TAKE_DAMAGE_FALLROOF, NOBODY, NOWHERE, 0, TRUE ); fReturnVal = TRUE; } else { pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (INT16)( -1 * DirectionInc( bNewDirection ) ) ); pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sTempNewGridNo, (INT16)( -1 * DirectionInc( bNewDirection ) ) ); EVENT_SetSoldierDesiredDirection( pSoldier, bNewDirection ); pSoldier->fTurningUntilDone = TRUE; pSoldier->usPendingAnimation = FALLOFF; // Deduct hitpoints/breath for falling! SoldierTakeDamage( pSoldier, ANIM_CROUCH, 100, 5000, TAKE_DAMAGE_FALLROOF, NOBODY, NOWHERE, 0, TRUE ); fReturnVal = TRUE; } } } return( fReturnVal ); } void BeginSoldierClimbDownRoof( SOLDIERTYPE *pSoldier ) { INT8 bNewDirection; UINT8 ubWhoIsThere; if ( FindLowerLevel( pSoldier, pSoldier->sGridNo, pSoldier->bDirection, &bNewDirection ) && ( pSoldier->bLevel > 0 ) ) { if ( EnoughPoints( pSoldier, GetAPsToClimbRoof( pSoldier, TRUE ), 0, TRUE ) ) { //Kaiden: Helps if we look where we are going before we try to climb on top of someone ubWhoIsThere = WhoIsThere2( NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bNewDirection ) ), 0 ); if ( ubWhoIsThere != NOBODY && ubWhoIsThere != pSoldier->ubID ) { return; } else { if (pSoldier->bTeam == gbPlayerNum) { // OK, SET INTERFACE FIRST SetUIBusy( pSoldier->ubID ); } pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bNewDirection ) ); bNewDirection = gTwoCDirection[ bNewDirection ]; pSoldier->ubPendingDirection = bNewDirection; EVENT_InitNewSoldierAnim( pSoldier, CLIMBDOWNROOF, 0 , FALSE ); InternalReceivingSoldierCancelServices( pSoldier, FALSE ); InternalGivingSoldierCancelServices( pSoldier, FALSE ); } } } } /* void BeginSoldierClimbDownRoof( SOLDIERTYPE *pSoldier ) { INT8 bNewDirection; UINT8 ubWhoIsThere; if ( FindLowerLevel( pSoldier, pSoldier->sGridNo, pSoldier->bDirection, &bNewDirection ) && ( pSoldier->bLevel > 0 ) ) { if ( EnoughPoints( pSoldier, GetAPsToClimbRoof( pSoldier, TRUE ), 0, TRUE ) ) { if (pSoldier->bTeam == gbPlayerNum) { // OK, SET INTERFACE FIRST SetUIBusy( pSoldier->ubID ); } pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bNewDirection ) ); bNewDirection = gTwoCDirection[ bNewDirection ]; pSoldier->ubPendingDirection = bNewDirection; EVENT_InitNewSoldierAnim( pSoldier, CLIMBDOWNROOF, 0 , FALSE ); InternalReceivingSoldierCancelServices( pSoldier, FALSE ); InternalGivingSoldierCancelServices( pSoldier, FALSE ); } } } */ void MoveMerc( SOLDIERTYPE *pSoldier, FLOAT dMovementChange, FLOAT dAngle, BOOLEAN fCheckRange ) { INT16 dDegAngle; FLOAT dDeltaPos; FLOAT dXPos , dYPos; BOOLEAN fStop = FALSE; dDegAngle = (INT16)( dAngle * 180 / PI ); //sprintf( gDebugStr, "Move Angle: %d", (int)dDegAngle ); // Find delta Movement for X pos dDeltaPos = (FLOAT) (dMovementChange * sin( dAngle )); // Find new position dXPos = pSoldier->dXPos + dDeltaPos; if ( fCheckRange ) { fStop = FALSE; switch( pSoldier->bMovementDirection ) { case NORTHEAST: case EAST: case SOUTHEAST: if ( dXPos >= pSoldier->sDestXPos ) { fStop = TRUE; } break; case NORTHWEST: case WEST: case SOUTHWEST: if ( dXPos <= pSoldier->sDestXPos ) { fStop = TRUE; } break; case NORTH: case SOUTH: fStop = TRUE; break; } if ( fStop ) { //dXPos = pSoldier->sDestXPos; pSoldier->fPastXDest = TRUE; if ( pSoldier->sGridNo == pSoldier->sFinalDestination ) { dXPos = pSoldier->sDestXPos; } } } // Find delta Movement for Y pos dDeltaPos = (FLOAT) (dMovementChange * cos( dAngle )); // Find new pos dYPos = pSoldier->dYPos + dDeltaPos; if ( fCheckRange ) { fStop = FALSE; switch( pSoldier->bMovementDirection ) { case NORTH: case NORTHEAST: case NORTHWEST: if ( dYPos <= pSoldier->sDestYPos ) { fStop = TRUE; } break; case SOUTH: case SOUTHWEST: case SOUTHEAST: if ( dYPos >= pSoldier->sDestYPos ) { fStop = TRUE; } break; case EAST: case WEST: fStop = TRUE; break; } if ( fStop ) { //dYPos = pSoldier->sDestYPos; pSoldier->fPastYDest = TRUE; if ( pSoldier->sGridNo == pSoldier->sFinalDestination ) { dYPos = pSoldier->sDestYPos; } } } // OK, set new position EVENT_InternalSetSoldierPosition( pSoldier, dXPos, dYPos, FALSE, FALSE, FALSE ); // DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("X: %f Y: %f", dXPos, dYPos ) ); } INT16 GetDirectionFromGridNo( INT16 sGridNo, SOLDIERTYPE *pSoldier ) { INT16 sXPos, sYPos; ConvertGridNoToXY( sGridNo, &sXPos, &sYPos ); return( GetDirectionFromXY( sXPos, sYPos, pSoldier ) ); } INT16 GetDirectionToGridNoFromGridNo( INT16 sGridNoDest, INT16 sGridNoSrc ) { INT16 sXPos2, sYPos2; INT16 sXPos, sYPos; ConvertGridNoToXY( sGridNoSrc, &sXPos, &sYPos ); ConvertGridNoToXY( sGridNoDest, &sXPos2, &sYPos2 ); return( atan8( sXPos2, sYPos2, sXPos, sYPos ) ); } INT16 GetDirectionFromXY( INT16 sXPos, INT16 sYPos, SOLDIERTYPE *pSoldier ) { INT16 sXPos2, sYPos2; ConvertGridNoToXY( pSoldier->sGridNo, &sXPos2, &sYPos2 ); return( atan8( sXPos2, sYPos2, sXPos, sYPos ) ); } #if 0 UINT8 atan8( INT16 x1, INT16 y1, INT16 x2, INT16 y2 ) { static int trig[8] = { 2, 3, 4, 5, 6, 7, 8, 1 }; // returned values are N=1, NE=2, E=3, SE=4, S=5, SW=6, W=7, NW=8 double dx=(x2-x1); double dy=(y2-y1); double a; int i,k; if (dx==0) dx=0.00390625; // 1/256th #define PISLICES (8) a=(atan2(dy,dx) + PI/PISLICES)/(PI/(PISLICES/2)); i=(int)a; if (a>0) k=i; else if (a<0) k=i+(PISLICES-1); else k=0; return(trig[k]); } #endif //#if 0 UINT8 atan8( INT16 sXPos, INT16 sYPos, INT16 sXPos2, INT16 sYPos2 ) { DOUBLE test_x = sXPos2 - sXPos; DOUBLE test_y = sYPos2 - sYPos; UINT8 mFacing = WEST; INT16 dDegAngle; DOUBLE angle; if ( test_x == 0 ) { test_x = 0.04; } angle = atan2( test_x, test_y ); dDegAngle = (INT16)( angle * 180 / PI ); //sprintf( gDebugStr, "Move Angle: %d", (int)dDegAngle ); do { if ( angle >=-PI*.375 && angle <= -PI*.125 ) { mFacing = SOUTHWEST; break; } if ( angle <= PI*.375 && angle >= PI*.125 ) { mFacing = SOUTHEAST; break; } if ( angle >=PI*.623 && angle <= PI*.875 ) { mFacing = NORTHEAST; break; } if ( angle <=-PI*.623 && angle >= -PI*.875 ) { mFacing = NORTHWEST; break; } if ( angle >-PI*0.125 && angle < PI*0.125 ) { mFacing = SOUTH; } if ( angle > PI*0.375 && angle < PI*0.623 ) { mFacing = EAST; } if ( ( angle > PI*0.875 && angle <= PI ) || ( angle > -PI && angle < -PI*0.875 ) ) { mFacing = NORTH; } if ( angle > -PI*0.623 && angle < -PI*0.375 ) { mFacing = WEST; } } while( FALSE ); return( mFacing ); } UINT8 atan8FromAngle( DOUBLE angle ) { UINT8 mFacing = WEST; if ( angle > PI ) { angle = ( angle - PI ) - PI; } if ( angle < -PI ) { angle = ( PI - ( fabs( angle ) - PI ) ); } do { if ( angle >=-PI*.375 && angle <= -PI*.125 ) { mFacing = SOUTHWEST; break; } if ( angle <= PI*.375 && angle >= PI*.125 ) { mFacing = SOUTHEAST; break; } if ( angle >=PI*.623 && angle <= PI*.875 ) { mFacing = NORTHEAST; break; } if ( angle <=-PI*.623 && angle >= -PI*.875 ) { mFacing = NORTHWEST; break; } if ( angle >-PI*0.125 && angle < PI*0.125 ) { mFacing = SOUTH; } if ( angle > PI*0.375 && angle < PI*0.623 ) { mFacing = EAST; } if ( ( angle > PI*0.875 && angle <= PI ) || ( angle > -PI && angle < -PI*0.875 ) ) { mFacing = NORTH; } if ( angle > -PI*0.623 && angle < -PI*0.375 ) { mFacing = WEST; } } while( FALSE ); return( mFacing ); } void CheckForFullStructures( SOLDIERTYPE *pSoldier ) { // This function checks to see if we are near a specific structure type which requires us to blit a // small obscuring peice INT16 sGridNo; UINT16 usFullTileIndex; INT32 cnt; // Check in all 'Above' directions for ( cnt = 0; cnt < MAX_FULLTILE_DIRECTIONS; cnt++ ) { sGridNo = pSoldier->sGridNo + gsFullTileDirections[ cnt ]; if ( CheckForFullStruct( sGridNo, &usFullTileIndex ) ) { // Add one for the item's obsuring part pSoldier->usFrontArcFullTileList[ cnt ] = usFullTileIndex + 1; pSoldier->usFrontArcFullTileGridNos[ cnt ] = sGridNo; AddTopmostToHead( sGridNo, pSoldier->usFrontArcFullTileList[ cnt ] ); } else { if ( pSoldier->usFrontArcFullTileList[ cnt ] != 0 ) { RemoveTopmost( pSoldier->usFrontArcFullTileGridNos[ cnt ], pSoldier->usFrontArcFullTileList[ cnt ] ); } pSoldier->usFrontArcFullTileList[ cnt ] = 0; pSoldier->usFrontArcFullTileGridNos[ cnt ] = 0; } } } BOOLEAN CheckForFullStruct( INT16 sGridNo, UINT16 *pusIndex ) { LEVELNODE *pStruct = NULL; LEVELNODE *pOldStruct = NULL; UINT32 fTileFlags; pStruct = gpWorldLevelData[ sGridNo ].pStructHead; // Look through all structs and Search for type while( pStruct != NULL ) { if ( pStruct->usIndex != NO_TILE && pStruct->usIndex < NUMBEROFTILES ) { GetTileFlags( pStruct->usIndex, &fTileFlags ); // Advance to next pOldStruct = pStruct; pStruct = pStruct->pNext; //if( (pOldStruct->pStructureData!=NULL) && ( pOldStruct->pStructureData->fFlags&STRUCTURE_TREE ) ) if ( fTileFlags & FULL3D_TILE ) { // CHECK IF THIS TREE IS FAIRLY ALONE! if ( FullStructAlone( sGridNo, 2 ) ) { // Return true and return index *pusIndex = pOldStruct->usIndex; return( TRUE ); } else { return( FALSE ); } } } else { // Advance to next pOldStruct = pStruct; pStruct = pStruct->pNext; } } // Could not find it, return FALSE return( FALSE ); } BOOLEAN FullStructAlone( INT16 sGridNo, UINT8 ubRadius ) { INT16 sTop, sBottom; INT16 sLeft, sRight; INT16 cnt1, cnt2; INT16 iNewIndex; INT32 leftmost; // Determine start end end indicies and num rows sTop = ubRadius; sBottom = -ubRadius; sLeft = - ubRadius; sRight = ubRadius; for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) { leftmost = ( ( sGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS; for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ ) { iNewIndex = sGridNo + ( WORLD_COLS * cnt1 ) + cnt2; if ( iNewIndex >=0 && iNewIndex < WORLD_MAX && iNewIndex >= leftmost && iNewIndex < ( leftmost + WORLD_COLS ) ) { if ( iNewIndex != sGridNo ) { if ( FindStructure( iNewIndex, STRUCTURE_TREE ) != NULL ) { return( FALSE ); } } } } } return( TRUE ); } void AdjustForFastTurnAnimation( SOLDIERTYPE *pSoldier ) { // CHECK FOR FASTTURN ANIMATIONS // ATE: Mod: Only fastturn for OUR guys! if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FASTTURN && pSoldier->bTeam == gbPlayerNum && !( pSoldier->uiStatusFlags & SOLDIER_TURNINGFROMHIT ) ) { if ( pSoldier->bDirection != pSoldier->bDesiredDirection ) { pSoldier->sAniDelay = FAST_TURN_ANIM_SPEED; } else { SetSoldierAniSpeed( pSoldier ); // FreeUpNPCFromTurning( pSoldier, LOOK); } } } BOOLEAN IsActionInterruptable( SOLDIERTYPE *pSoldier ) { if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_NONINTERRUPT ) { return( FALSE ); } return( TRUE ); } // WRAPPER FUNCTIONS FOR SOLDIER EVENTS void SendSoldierPositionEvent( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos ) { // Sent event for position update EV_S_SETPOSITION SSetPosition; SSetPosition.usSoldierID = pSoldier->ubID; SSetPosition.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue; SSetPosition.dNewXPos = dNewXPos; SSetPosition.dNewYPos = dNewYPos; AddGameEvent( S_SETPOSITION, 0, &SSetPosition ); } void SendSoldierDestinationEvent( SOLDIERTYPE *pSoldier, UINT16 usNewDestination ) { // Sent event for position update EV_S_CHANGEDEST SChangeDest; SChangeDest.usSoldierID = pSoldier->ubID; SChangeDest.usNewDestination = usNewDestination; SChangeDest.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue; AddGameEvent( S_CHANGEDEST, 0, &SChangeDest ); } void SendSoldierSetDirectionEvent( SOLDIERTYPE *pSoldier, UINT16 usNewDirection ) { // Sent event for position update EV_S_SETDIRECTION SSetDirection; SSetDirection.usSoldierID = pSoldier->ubID; SSetDirection.usNewDirection = usNewDirection; SSetDirection.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue; AddGameEvent( S_SETDIRECTION, 0, &SSetDirection ); } void SendSoldierSetDesiredDirectionEvent( SOLDIERTYPE *pSoldier, UINT16 usDesiredDirection ) { // Sent event for position update EV_S_SETDESIREDDIRECTION SSetDesiredDirection; SSetDesiredDirection.usSoldierID = pSoldier->ubID; SSetDesiredDirection.usDesiredDirection = usDesiredDirection; SSetDesiredDirection.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue; AddGameEvent( S_SETDESIREDDIRECTION, 0, &SSetDesiredDirection ); } void SendGetNewSoldierPathEvent( SOLDIERTYPE *pSoldier, UINT16 sDestGridNo, UINT16 usMovementAnim ) { EV_S_GETNEWPATH SGetNewPath; SGetNewPath.usSoldierID = pSoldier->ubID; SGetNewPath.sDestGridNo = sDestGridNo; SGetNewPath.usMovementAnim = usMovementAnim; SGetNewPath.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue; AddGameEvent( S_GETNEWPATH, 0, &SGetNewPath ); } void SendChangeSoldierStanceEvent( SOLDIERTYPE *pSoldier, UINT8 ubNewStance ) { #if 0 EV_S_CHANGESTANCE SChangeStance; #ifdef NETWORKED if( !IsTheSolderUnderMyControl( pSoldier->ubID) ) return; #endif SChangeStance.ubNewStance = ubNewStance; SChangeStance.usSoldierID = pSoldier->ubID; SChangeStance.sXPos = pSoldier->sX; SChangeStance.sYPos = pSoldier->sY; SChangeStance.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue; AddGameEvent( S_CHANGESTANCE, 0, &SChangeStance ); #endif ChangeSoldierStance( pSoldier, ubNewStance ); } void SendBeginFireWeaponEvent( SOLDIERTYPE *pSoldier, INT16 sTargetGridNo ) { DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("SendBeginFireWeaponEvent")); EV_S_BEGINFIREWEAPON SBeginFireWeapon; SBeginFireWeapon.usSoldierID = pSoldier->ubID; SBeginFireWeapon.sTargetGridNo = sTargetGridNo; SBeginFireWeapon.bTargetLevel = pSoldier->bTargetLevel; SBeginFireWeapon.bTargetCubeLevel = pSoldier->bTargetCubeLevel; SBeginFireWeapon.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue; AddGameEvent( S_BEGINFIREWEAPON, 0, &SBeginFireWeapon ); } #if 0 // This function is now obsolete. Just call ReduceAttackBusyCount. // This function just encapolates the check for turnbased and having an attacker in the first place void ReleaseSoldiersAttacker( SOLDIERTYPE *pSoldier ) { INT32 cnt; UINT8 ubNumToFree; //if ( gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT) ) { // ATE: Removed... //if ( pSoldier->ubAttackerID != NOBODY ) { // JA2 Gold // set next-to-previous attacker, so long as this isn't a repeat attack if (pSoldier->ubPreviousAttackerID != pSoldier->ubAttackerID) { pSoldier->ubNextToPreviousAttackerID = pSoldier->ubPreviousAttackerID; } // get previous attacker id pSoldier->ubPreviousAttackerID = pSoldier->ubAttackerID; // Copy BeingAttackedCount here.... ubNumToFree = pSoldier->bBeingAttackedCount; // Zero it out BEFORE, as supression may increase it again... pSoldier->bBeingAttackedCount = 0; for ( cnt = 0; cnt < ubNumToFree; cnt++ ) { DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker of %d (attacker is %d) - releasesoldierattacker num to free is %d", pSoldier->ubID, pSoldier->ubAttackerID, ubNumToFree ) ); ReduceAttackBusyCount( pSoldier->ubAttackerID, FALSE ); } // ATE: Set to NOBODY if this person is NOT dead // otherise, we keep it so the kill can be awarded! if ( pSoldier->bLife != 0 && pSoldier->ubBodyType != QUEENMONSTER ) { pSoldier->ubAttackerID = NOBODY; } } } } #endif BOOLEAN MercInWater( SOLDIERTYPE *pSoldier ) { // Our water texture , for now is of a given type if ( pSoldier->bOverTerrainType == LOW_WATER || pSoldier->bOverTerrainType == MED_WATER || pSoldier->bOverTerrainType == DEEP_WATER ) { return( TRUE ); } else { return( FALSE ); } } void RevivePlayerTeam( ) { INT32 cnt; SOLDIERTYPE *pSoldier; // End the turn of player charactors cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; // look for all mercs on the same team, for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pSoldier++) { ReviveSoldier( pSoldier ); } } // What? A zombie function? void ReviveSoldier( SOLDIERTYPE *pSoldier ) { INT16 sX, sY; if ( pSoldier->bLife < OKLIFE && pSoldier->bActive ) { // If dead or unconscious, revive! pSoldier->uiStatusFlags &= ( ~SOLDIER_DEAD ); pSoldier->bLife = pSoldier->bLifeMax; pSoldier->bBleeding = 0; pSoldier->ubDesiredHeight = ANIM_STAND; AddManToTeam( pSoldier->bTeam ); // Set to standing pSoldier->fInNonintAnim = FALSE; pSoldier->fRTInNonintAnim = FALSE; // Change to standing,unless we can getup with an animation EVENT_InitNewSoldierAnim( pSoldier, STANDING, 0, TRUE ); BeginSoldierGetup( pSoldier ); // Makesure center of tile sX = CenterX( pSoldier->sGridNo ); sY = CenterY( pSoldier->sGridNo ); EVENT_SetSoldierPosition( pSoldier, (FLOAT) sX, (FLOAT) sY ); // Dirty INterface fInterfacePanelDirty = DIRTYLEVEL2; } } void HandleAnimationProfile( SOLDIERTYPE *pSoldier, UINT16 usAnimState, BOOLEAN fRemove ) { //#if 0 ANIM_PROF *pProfile; ANIM_PROF_DIR *pProfileDir; ANIM_PROF_TILE *pProfileTile; INT8 bProfileID; UINT32 iTileCount; INT16 sGridNo; UINT16 usAnimSurface; // ATE // Get Surface Index usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, usAnimState ); CHECKV( usAnimSurface != INVALID_ANIMATION_SURFACE ); bProfileID = gAnimSurfaceDatabase[ usAnimSurface ].bProfile; // Determine if this animation has a profile if ( bProfileID != -1 ) { // Getprofile pProfile = &(gpAnimProfiles[ bProfileID ] ); // Get direction pProfileDir = &( pProfile->Dirs[ pSoldier->bDirection ] ); // Loop tiles and set accordingly into world for( iTileCount = 0; iTileCount < pProfileDir->ubNumTiles; iTileCount++ ) { pProfileTile = &( pProfileDir->pTiles[ iTileCount ] ); sGridNo = pSoldier->sGridNo + ( ( WORLD_COLS * pProfileTile->bTileY ) + pProfileTile->bTileX ); // Check if in bounds if ( !OutOfBounds( pSoldier->sGridNo, sGridNo ) ) { if ( fRemove ) { // Remove from world RemoveMerc( sGridNo, pSoldier, TRUE ); } else { // PLace into world AddMercToHead( sGridNo, pSoldier, FALSE ); //if ( pProfileTile->bTileY != 0 || pProfileTile->bTileX != 0 ) { gpWorldLevelData[sGridNo].pMercHead->uiFlags |= LEVELNODE_MERCPLACEHOLDER; gpWorldLevelData[sGridNo].pMercHead->uiAnimHitLocationFlags = pProfileTile->usTileFlags; } } } } } //#endif } LEVELNODE *GetAnimProfileFlags( UINT16 sGridNo, UINT16 *usFlags, SOLDIERTYPE **ppTargSoldier, LEVELNODE *pGivenNode ) { LEVELNODE *pNode; (*ppTargSoldier) = NULL; (*usFlags ) = 0; if ( pGivenNode == NULL ) { pNode = gpWorldLevelData[sGridNo].pMercHead; } else { pNode = pGivenNode->pNext; } //#if 0 if ( pNode != NULL ) { if ( pNode->uiFlags & LEVELNODE_MERCPLACEHOLDER ) { (*usFlags) = (UINT16)pNode->uiAnimHitLocationFlags; (*ppTargSoldier) = pNode->pSoldier; } } //#endif return( pNode ); } BOOLEAN GetProfileFlagsFromGridno( SOLDIERTYPE *pSoldier, UINT16 usAnimState, UINT16 sTestGridNo, UINT16 *usFlags ) { ANIM_PROF *pProfile; ANIM_PROF_DIR *pProfileDir; ANIM_PROF_TILE *pProfileTile; INT8 bProfileID; UINT32 iTileCount; INT16 sGridNo; UINT16 usAnimSurface; // Get Surface Index usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, usAnimState ); CHECKF( usAnimSurface != INVALID_ANIMATION_SURFACE ); bProfileID = gAnimSurfaceDatabase[ usAnimSurface ].bProfile; *usFlags = 0; // Determine if this animation has a profile if ( bProfileID != -1 ) { // Getprofile pProfile = &(gpAnimProfiles[ bProfileID ] ); // Get direction pProfileDir = &( pProfile->Dirs[ pSoldier->bDirection ] ); // Loop tiles and set accordingly into world for( iTileCount = 0; iTileCount < pProfileDir->ubNumTiles; iTileCount++ ) { pProfileTile = &( pProfileDir->pTiles[ iTileCount ] ); sGridNo = pSoldier->sGridNo + ( ( WORLD_COLS * pProfileTile->bTileY ) + pProfileTile->bTileX ); // Check if in bounds if ( !OutOfBounds( pSoldier->sGridNo, sGridNo ) ) { if ( sGridNo == sTestGridNo ) { *usFlags = pProfileTile->usTileFlags; return( TRUE ); } } } } return( FALSE ); } void EVENT_SoldierBeginGiveItem( SOLDIERTYPE *pSoldier ) { SOLDIERTYPE *pTSoldier; if ( VerifyGiveItem( pSoldier, &pTSoldier ) ) { // CHANGE DIRECTION AND GOTO ANIMATION NOW pSoldier->bDesiredDirection = pSoldier->bPendingActionData3; pSoldier->bDirection = pSoldier->bPendingActionData3; // begin animation EVENT_InitNewSoldierAnim( pSoldier, GIVE_ITEM, 0 , FALSE ); } else { UnSetEngagedInConvFromPCAction( pSoldier ); MemFree( pSoldier->pTempObject ); } } void EVENT_SoldierBeginBladeAttack( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection ) { SOLDIERTYPE *pTSoldier; //UINT32 uiMercFlags; UINT16 usSoldierIndex; UINT8 ubTDirection; BOOLEAN fChangeDirection = FALSE; ROTTING_CORPSE *pCorpse; // Increment the number of people busy doing stuff because of an attack //if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) ) //{ // gTacticalStatus.ubAttackBusyCount++; DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Begin blade attack: ATB %d", gTacticalStatus.ubAttackBusyCount) ); DebugAttackBusy( String( "Begin blade attack: ATB %d\n", gTacticalStatus.ubAttackBusyCount) ); //} // CHANGE DIRECTION AND GOTO ANIMATION NOW EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection ); EVENT_SetSoldierDirection( pSoldier, ubDirection ); // CHANGE TO ANIMATION // DETERMINE ANIMATION TO PLAY // LATER BASED ON IF TAREGT KNOWS OF US, STANCE, ETC // GET POINTER TO TAREGT if (pSoldier->uiStatusFlags & SOLDIER_MONSTER) { UINT8 ubTargetID; // Is there an unconscious guy at gridno...... ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel ); if ( ubTargetID != NOBODY && ( ( MercPtrs[ ubTargetID ]->bLife < OKLIFE && MercPtrs[ ubTargetID ]->bLife > 0 ) || ( MercPtrs[ ubTargetID ]->bBreath < OKBREATH && MercPtrs[ ubTargetID ]->bCollapsed ) ) ) { pSoldier->uiPendingActionData4 = ubTargetID; // add regen bonus pSoldier->bRegenerationCounter++; EVENT_InitNewSoldierAnim( pSoldier, MONSTER_BEGIN_EATTING_FLESH, 0, FALSE ); } else { if ( PythSpacesAway( pSoldier->sGridNo, sGridNo ) <= 1 ) { EVENT_InitNewSoldierAnim( pSoldier, MONSTER_CLOSE_ATTACK, 0, FALSE ); } else { EVENT_InitNewSoldierAnim( pSoldier, ADULTMONSTER_ATTACKING, 0, FALSE ); } } } else if (pSoldier->ubBodyType == BLOODCAT) { // Check if it's a claws or teeth... if ( pSoldier->inv[ HANDPOS ].usItem == BLOODCAT_CLAW_ATTACK ) { EVENT_InitNewSoldierAnim( pSoldier, BLOODCAT_SWIPE, 0, FALSE ); } else { EVENT_InitNewSoldierAnim( pSoldier, BLOODCAT_BITE_ANIM, 0, FALSE ); } } else { usSoldierIndex = WhoIsThere2( sGridNo, pSoldier->bTargetLevel ); if ( usSoldierIndex != NOBODY ) { GetSoldier( &pTSoldier, usSoldierIndex ); // Look at stance of target switch( gAnimControl[ pTSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: case ANIM_CROUCH: // CHECK IF HE CAN SEE US, IF SO RANDOMIZE if ( pTSoldier->bOppList[ pSoldier->ubID ] == 0 && pTSoldier->bTeam != pSoldier->bTeam ) { // WE ARE NOT SEEN EVENT_InitNewSoldierAnim( pSoldier, STAB, 0 , FALSE ); } else { // WE ARE SEEN if ( Random( 50 ) > 25 ) { EVENT_InitNewSoldierAnim( pSoldier, STAB, 0 , FALSE ); } else { EVENT_InitNewSoldierAnim( pSoldier, SLICE, 0 , FALSE ); } // IF WE ARE SEEN, MAKE SURE GUY TURNS! // Get direction to target // IF WE ARE AN ANIMAL, CAR, MONSTER, DONT'T TURN if ( !( pTSoldier->uiStatusFlags & ( SOLDIER_MONSTER | SOLDIER_ANIMAL | SOLDIER_VEHICLE ) ) ) { // OK, stop merc.... EVENT_StopMerc( pTSoldier, pTSoldier->sGridNo, pTSoldier->bDirection ); if ( pTSoldier->bTeam != gbPlayerNum ) { CancelAIAction( pTSoldier, TRUE ); } ubTDirection = (UINT8)GetDirectionFromGridNo( pSoldier->sGridNo, pTSoldier ); SendSoldierSetDesiredDirectionEvent( pTSoldier, ubTDirection ); } } break; case ANIM_PRONE: // CHECK OUR STANCE if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_CROUCH ) { // SET DESIRED STANCE AND SET PENDING ANIMATION SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH ); pSoldier->usPendingAnimation = CROUCH_STAB; } else { // USE crouched one // NEED TO CHANGE STANCE IF NOT CROUCHD! EVENT_InitNewSoldierAnim( pSoldier, CROUCH_STAB, 0 , FALSE ); } break; } } else { // OK, SEE IF THERE IS AN OBSTACLE HERE... if ( !NewOKDestination( pSoldier, sGridNo, FALSE, pSoldier->bLevel ) ) { EVENT_InitNewSoldierAnim( pSoldier, STAB, 0 , FALSE ); } else { // Check for corpse! pCorpse = GetCorpseAtGridNo( sGridNo, pSoldier->bLevel ); if ( pCorpse == NULL ) { EVENT_InitNewSoldierAnim( pSoldier, CROUCH_STAB, 0 , FALSE ); } else { if ( IsValidDecapitationCorpse( pCorpse ) ) { EVENT_InitNewSoldierAnim( pSoldier, DECAPITATE, 0 , FALSE ); } else { EVENT_InitNewSoldierAnim( pSoldier, CROUCH_STAB, 0 , FALSE ); } } } } } // SET TARGET GRIDNO pSoldier->sTargetGridNo = sGridNo; pSoldier->bTargetLevel = pSoldier->bLevel; pSoldier->ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel ); } void EVENT_SoldierBeginPunchAttack( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection ) { BOOLEAN fMartialArtist = FALSE; SOLDIERTYPE *pTSoldier; //UINT32 uiMercFlags; UINT16 usSoldierIndex; UINT8 ubTDirection; BOOLEAN fChangeDirection = FALSE; UINT16 usItem; // Get item in hand... usItem = pSoldier->inv[ HANDPOS ].usItem; // Increment the number of people busy doing stuff because of an attack //if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) ) //{ // gTacticalStatus.ubAttackBusyCount++; DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Begin HTH attack: ATB %d", gTacticalStatus.ubAttackBusyCount) ); DebugAttackBusy( String( "Begin HTH attack: ATB %d\n", gTacticalStatus.ubAttackBusyCount) ); //} // get target..... usSoldierIndex = WhoIsThere2( pSoldier->sTargetGridNo, pSoldier->bLevel ); if ( usSoldierIndex != NOBODY ) { GetSoldier( &pTSoldier, usSoldierIndex ); fChangeDirection = TRUE; } else { return; } if ( fChangeDirection ) { // CHANGE DIRECTION AND GOTO ANIMATION NOW EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection ); EVENT_SetSoldierDirection( pSoldier, ubDirection ); } // Are we a martial artist? if ( HAS_SKILL_TRAIT( pSoldier, MARTIALARTS ) ) { fMartialArtist = TRUE; } if ( fMartialArtist && !AreInMeanwhile( ) && !Item[usItem].crowbar ) { // Are we in attack mode yet? if ( pSoldier->usAnimState != NINJA_BREATH && gAnimControl[ pSoldier->usAnimState ].ubHeight == ANIM_STAND && gAnimControl[ pTSoldier->usAnimState ].ubHeight != ANIM_PRONE ) { EVENT_InitNewSoldierAnim( pSoldier, NINJA_GOTOBREATH, 0 , FALSE ); } else { DoNinjaAttack( pSoldier ); } } else { // Look at stance of target switch( gAnimControl[ pTSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: case ANIM_CROUCH: if ( !Item[usItem].crowbar ) { EVENT_InitNewSoldierAnim( pSoldier, PUNCH, 0 , FALSE ); } else { EVENT_InitNewSoldierAnim( pSoldier, CROWBAR_ATTACK, 0 , FALSE ); } // CHECK IF HE CAN SEE US, IF SO CHANGE DIR if ( pTSoldier->bOppList[ pSoldier->ubID ] == 0 && pTSoldier->bTeam != pSoldier->bTeam ) { // Get direction to target // IF WE ARE AN ANIMAL, CAR, MONSTER, DONT'T TURN if ( !( pTSoldier->uiStatusFlags & ( SOLDIER_MONSTER | SOLDIER_ANIMAL | SOLDIER_VEHICLE ) ) ) { // OK, stop merc.... EVENT_StopMerc( pTSoldier, pTSoldier->sGridNo, pTSoldier->bDirection ); if ( pTSoldier->bTeam != gbPlayerNum ) { CancelAIAction( pTSoldier, TRUE ); } ubTDirection = (UINT8)GetDirectionFromGridNo( pSoldier->sGridNo, pTSoldier ); SendSoldierSetDesiredDirectionEvent( pTSoldier, ubTDirection ); } } break; case ANIM_PRONE: // CHECK OUR STANCE // ATE: Added this for CIV body types 'cause of elliot if ( !IS_MERC_BODY_TYPE( pSoldier ) ) { EVENT_InitNewSoldierAnim( pSoldier, PUNCH, 0 , FALSE ); } else { if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_CROUCH ) { // SET DESIRED STANCE AND SET PENDING ANIMATION SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH ); pSoldier->usPendingAnimation = PUNCH_LOW; } else { // USE crouched one // NEED TO CHANGE STANCE IF NOT CROUCHD! EVENT_InitNewSoldierAnim( pSoldier, PUNCH_LOW, 0 , FALSE ); } } break; } } // SET TARGET GRIDNO pSoldier->sTargetGridNo = sGridNo; pSoldier->bTargetLevel = pSoldier->bLevel; pSoldier->sLastTarget = sGridNo; pSoldier->ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel ); } void EVENT_SoldierBeginKnifeThrowAttack( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection ) { // Increment the number of people busy doing stuff because of an attack //if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) ) //{ // gTacticalStatus.ubAttackBusyCount++; //} // pSoldier->bBulletsLeft = 1; DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting knifethrow attack, bullets left %d", pSoldier->bBulletsLeft) ); DebugAttackBusy( String( "Begin knife throwing attack: ATB %d\n", gTacticalStatus.ubAttackBusyCount) ); EVENT_InitNewSoldierAnim( pSoldier, THROW_KNIFE, 0 , FALSE ); // CHANGE DIRECTION AND GOTO ANIMATION NOW EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection ); EVENT_SetSoldierDirection( pSoldier, ubDirection ); // SET TARGET GRIDNO pSoldier->sTargetGridNo = sGridNo; pSoldier->sLastTarget = sGridNo; pSoldier->fTurningFromPronePosition = 0; // NB target level must be set by functions outside of here... but I think it // is already set in HandleItem or in the AI code - CJC pSoldier->ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel ); } void EVENT_SoldierBeginDropBomb( SOLDIERTYPE *pSoldier ) { // Increment the number of people busy doing stuff because of an attack switch( gAnimControl[ pSoldier->usAnimState ].ubHeight ) { case ANIM_STAND: EVENT_InitNewSoldierAnim( pSoldier, PLANT_BOMB, 0 , FALSE ); break; default: // Call hander for planting bomb... HandleSoldierDropBomb( pSoldier, pSoldier->sPendingActionData2 ); SoldierGotoStationaryStance( pSoldier ); break; } } void EVENT_SoldierBeginUseDetonator( SOLDIERTYPE *pSoldier ) { // Increment the number of people busy doing stuff because of an attack switch( gAnimControl[ pSoldier->usAnimState ].ubHeight ) { case ANIM_STAND: EVENT_InitNewSoldierAnim( pSoldier, USE_REMOTE, 0 , FALSE ); break; default: // Call hander for planting bomb... HandleSoldierUseRemote( pSoldier, pSoldier->sPendingActionData2 ); break; } } void EVENT_SoldierBeginFirstAid( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection ) { SOLDIERTYPE *pTSoldier; //UINT32 uiMercFlags; UINT16 usSoldierIndex; BOOLEAN fRefused = FALSE; usSoldierIndex = WhoIsThere2( sGridNo, pSoldier->bLevel ); if ( usSoldierIndex != NOBODY ) { pTSoldier = MercPtrs[ usSoldierIndex ]; // OK, check if we should play quote... if ( pTSoldier->bTeam != gbPlayerNum ) { if ( pTSoldier->ubProfile != NO_PROFILE && pTSoldier->ubProfile >= FIRST_RPC && pTSoldier->ubProfile < GASTON && !RPC_RECRUITED( pTSoldier ) ) { fRefused = PCDoesFirstAidOnNPC( pTSoldier->ubProfile ); } if ( !fRefused ) { if ( CREATURE_OR_BLOODCAT( pTSoldier ) ) { // nope!! fRefused = TRUE; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, Message[ STR_REFUSE_FIRSTAID_FOR_CREATURE ] ); } else if ( !pTSoldier->bNeutral && pTSoldier->bLife >= OKLIFE && pTSoldier->bSide != pSoldier->bSide ) { fRefused = TRUE; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, Message[ STR_REFUSE_FIRSTAID ] ); } } } if ( fRefused ) { UnSetUIBusy( pSoldier->ubID ); return; } // ATE: We can only give firsty aid to one perosn at a time... cancel // any now... InternalGivingSoldierCancelServices( pSoldier, FALSE ); // CHANGE DIRECTION AND GOTO ANIMATION NOW EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection ); EVENT_SetSoldierDirection( pSoldier, ubDirection ); // CHECK OUR STANCE AND GOTO CROUCH IF NEEDED //if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_CROUCH ) //{ // SET DESIRED STANCE AND SET PENDING ANIMATION // SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH ); // pSoldier->usPendingAnimation = START_AID; //} //else { // CHANGE TO ANIMATION EVENT_InitNewSoldierAnim( pSoldier, START_AID, 0 , FALSE ); } // SET TARGET GRIDNO pSoldier->sTargetGridNo = sGridNo; // SET PARTNER ID pSoldier->ubServicePartner = (UINT8)usSoldierIndex; // SET PARTNER'S COUNT REFERENCE pTSoldier->ubServiceCount++; // If target and doer are no the same guy... if ( pTSoldier->ubID != pSoldier->ubID && !pTSoldier->bCollapsed ) { SoldierGotoStationaryStance( pTSoldier ); } } } void EVENT_SoldierEnterVehicle( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection ) { SOLDIERTYPE *pTSoldier; UINT32 uiMercFlags; UINT16 usSoldierIndex; if ( FindSoldier( sGridNo, &usSoldierIndex, &uiMercFlags, FIND_SOLDIER_GRIDNO ) ) { pTSoldier = MercPtrs[ usSoldierIndex ]; // Enter vehicle... EnterVehicle( pTSoldier, pSoldier ); } UnSetUIBusy( pSoldier->ubID ); } UINT32 SoldierDressWound( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pVictim, INT16 sKitPts, INT16 sStatus ) { UINT32 uiDressSkill, uiPossible, uiActual, uiMedcost, uiDeficiency, uiAvailAPs, uiUsedAPs; UINT8 ubBelowOKlife, ubPtsLeft; BOOLEAN fRanOut = FALSE; if (pVictim->bBleeding < 1 && pVictim->bLife >= OKLIFE ) { return(0); // nothing to do, shouldn't have even been called! } if ( pVictim->bLife == 0 ) { return(0); } // in case he has multiple kits in hand, limit influence of kit status to 100%! if (sStatus >= 100) { sStatus = 100; } // calculate wound-dressing skill (3x medical, 2x equip, 1x level, 1x dex) uiDressSkill = ( ( 3 * EffectiveMedical( pSoldier ) ) + // medical knowledge ( 2 * sStatus) + // state of medical kit (10 * EffectiveExpLevel( pSoldier ) ) + // battle injury experience EffectiveDexterity( pSoldier ) ) / 7; // general "handiness" // try to use every AP that the merc has left uiAvailAPs = pSoldier->bActionPoints; // OK, If we are in real-time, use another value... if (!(gTacticalStatus.uiFlags & TURNBASED) || !(gTacticalStatus.uiFlags & INCOMBAT ) ) { // Set to a value which looks good based on our tactical turns duration uiAvailAPs = RT_FIRST_AID_GAIN_MODIFIER; } // calculate how much bandaging CAN be done this turn uiPossible = ( uiAvailAPs * uiDressSkill ) / 50; // max rate is 2 * fullAPs // if no healing is possible (insufficient APs or insufficient dressSkill) if (!uiPossible) return(0); if (Item[pSoldier->inv[ HANDPOS ].usItem].medicalkit ) // using the GOOD medic stuff { uiPossible += ( uiPossible / 2); // add extra 50 % } uiActual = uiPossible; // start by assuming maximum possible // figure out how far below OKLIFE the victim is if (pVictim->bLife >= OKLIFE) { ubBelowOKlife = 0; } else { ubBelowOKlife = OKLIFE - pVictim->bLife; } // figure out how many healing pts we need to stop dying (2x cost) uiDeficiency = (2 * ubBelowOKlife ); // if, after that, the patient will still be bleeding if ( (pVictim->bBleeding - ubBelowOKlife ) > 0) { // then add how many healing pts we need to stop bleeding (1x cost) uiDeficiency += ( pVictim->bBleeding - ubBelowOKlife ); } // now, make sure we weren't going to give too much if ( uiActual > uiDeficiency) // if we were about to apply too much uiActual = uiDeficiency; // reduce actual not to waste anything // now make sure we HAVE that much if (Item[pSoldier->inv[ HANDPOS ].usItem].medicalkit ) { uiMedcost = (uiActual + 1) / 2; // cost is only half, rounded up if ( uiMedcost > (UINT32)sKitPts ) // if we can't afford this { fRanOut = TRUE; uiMedcost = sKitPts; // what CAN we afford? uiActual = uiMedcost * 2; // give double this as aid } } else { uiMedcost = uiActual; if ( uiMedcost > (UINT32)sKitPts) // can't afford it { fRanOut = TRUE; uiMedcost = uiActual = sKitPts; // recalc cost AND aid } } ubPtsLeft = (UINT8)uiActual; // heal real life points first (if below OKLIFE) because we don't want the // patient still DYING if bandages run out, or medic is disabled/distracted! // NOTE: Dressing wounds for life below OKLIFE now costs 2 pts/life point! if ( ubPtsLeft && pVictim->bLife < OKLIFE) { // if we have enough points to bring him all the way to OKLIFE this turn if ( ubPtsLeft >= (2 * ubBelowOKlife ) ) { // raise life to OKLIFE pVictim->bLife = OKLIFE; // reduce bleeding by the same number of life points healed up pVictim->bBleeding -= ubBelowOKlife; // use up appropriate # of actual healing points ubPtsLeft -= (2 * ubBelowOKlife); } else { pVictim->bLife += ( ubPtsLeft / 2); pVictim->bBleeding -= ( ubPtsLeft / 2); ubPtsLeft = ubPtsLeft % 2; // if ptsLeft was odd, ptsLeft = 1 } // this should never happen any more, but make sure bleeding not negative if (pVictim->bBleeding < 0) { pVictim->bBleeding = 0; } // if this healing brought the patient out of the worst of it, cancel dying if (pVictim->bLife >= OKLIFE ) { //pVictim->dying = pVictim->dyingComment = FALSE; //pVictim->shootOn = TRUE; // turn off merc QUOTE flags pVictim->fDyingComment = FALSE; } // update patient's entire panel (could have regained consciousness, etc.) } // if any healing points remain, apply that to any remaining bleeding (1/1) // DON'T spend any APs/kit pts to cure bleeding until merc is no longer dying //if ( ubPtsLeft && pVictim->bBleeding && !pVictim->dying) if ( ubPtsLeft && pVictim->bBleeding ) { // if we have enough points to bandage all remaining bleeding this turn if (ubPtsLeft >= pVictim->bBleeding ) { ubPtsLeft -= pVictim->bBleeding; pVictim->bBleeding = 0; } else // bandage what we can { pVictim->bBleeding -= ubPtsLeft; ubPtsLeft = 0; } // update patient's life bar only } // if wound has been dressed enough so that bleeding won't occur, turn off // the "warned about bleeding" flag so merc tells us about the next bleeding if ( pVictim->bBleeding <= MIN_BLEEDING_THRESHOLD ) { pVictim->fWarnedAboutBleeding = FALSE; } // if there are any ptsLeft now, then we didn't actually get to use them uiActual -= ubPtsLeft; // usedAPs equals (actionPts) * (%of possible points actually used) uiUsedAPs = ( uiActual * uiAvailAPs ) / uiPossible; if (Item[pSoldier->inv[ HANDPOS ].usItem].medicalkit ) // using the GOOD medic stuff { uiUsedAPs = ( uiUsedAPs * 2) / 3; // reverse 50% bonus by taking 2/3rds } DeductPoints( pSoldier, (INT16)uiUsedAPs, (INT16)( ( uiUsedAPs * BP_PER_AP_LT_EFFORT) ) ); if ( PTR_OURTEAM ) { // MEDICAL GAIN (actual / 2): Helped someone by giving first aid StatChange(pSoldier, MEDICALAMT, (UINT16)(uiActual / 2), FALSE); // DEXTERITY GAIN (actual / 6): Helped someone by giving first aid StatChange(pSoldier, DEXTAMT, (UINT16)(uiActual / 6), FALSE); } return( uiMedcost ); } void InternalReceivingSoldierCancelServices( SOLDIERTYPE *pSoldier, BOOLEAN fPlayEndAnim ) { SOLDIERTYPE *pTSoldier; INT32 cnt; if ( pSoldier->ubServiceCount > 0 ) { // Loop through guys who have us as servicing for ( pTSoldier = Menptr, cnt = 0; cnt < MAX_NUM_SOLDIERS; pTSoldier++, cnt++ ) { if ( pTSoldier->bActive ) { if ( pTSoldier->ubServicePartner == pSoldier->ubID ) { // END SERVICE! pSoldier->ubServiceCount--; pTSoldier->ubServicePartner = NOBODY; if ( gTacticalStatus.fAutoBandageMode ) { pSoldier->ubAutoBandagingMedic = NOBODY; ActionDone( pTSoldier ); } else { // don't use end aid animation in autobandage if ( pTSoldier->bLife >= OKLIFE && pTSoldier->bBreath > 0 && fPlayEndAnim ) { EVENT_InitNewSoldierAnim( pTSoldier, END_AID, 0 , FALSE ); } } } } } } } void ReceivingSoldierCancelServices( SOLDIERTYPE *pSoldier ) { InternalReceivingSoldierCancelServices( pSoldier, TRUE ); } void InternalGivingSoldierCancelServices( SOLDIERTYPE *pSoldier, BOOLEAN fPlayEndAnim ) { SOLDIERTYPE *pTSoldier; // GET TARGET SOLDIER if ( pSoldier->ubServicePartner != NOBODY ) { pTSoldier = MercPtrs[ pSoldier->ubServicePartner ]; // END SERVICE! pTSoldier->ubServiceCount--; pSoldier->ubServicePartner = NOBODY; if ( gTacticalStatus.fAutoBandageMode ) { pTSoldier->ubAutoBandagingMedic = NOBODY; ActionDone( pSoldier ); } else { if ( pSoldier->bLife >= OKLIFE && pSoldier->bBreath > 0 && fPlayEndAnim ) { // don't use end aid animation in autobandage EVENT_InitNewSoldierAnim( pSoldier, END_AID, 0 , FALSE ); } } } } void GivingSoldierCancelServices( SOLDIERTYPE *pSoldier ) { InternalGivingSoldierCancelServices( pSoldier, TRUE ); } void HaultSoldierFromSighting( SOLDIERTYPE *pSoldier, BOOLEAN fFromSightingEnemy ) { DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("HaultSoldierFromSighting") ); // SEND HUALT EVENT! //EV_S_STOP_MERC SStopMerc; //SStopMerc.sGridNo = pSoldier->sGridNo; //SStopMerc.bDirection = pSoldier->bDirection; //SStopMerc.usSoldierID = pSoldier->ubID; //AddGameEvent( S_STOP_MERC, 0, &SStopMerc ); // If we are a 'specialmove... ignore... if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_SPECIALMOVE ) ) { return; } // OK, check if we were going to throw something, and give it back if so! if ( pSoldier->pTempObject != NULL && fFromSightingEnemy ) { // Place it back into inv.... AutoPlaceObject( pSoldier, pSoldier->pTempObject, FALSE ); //AXP 25.03.2007: Not needed anymore, grenade costs are only deducted on throwing the object //AXP 24.03.2007: Give APs back if we wanted to throw grenade, but interrupt/spotting occured //if ( pSoldier->pThrowParams->ubActionCode == THROW_ARM_ITEM ) //{ // DeductPoints( pSoldier, -MinAPsToAttack( pSoldier, pSoldier->sTargetGridNo, FALSE ), 0 ); //} MemFree( pSoldier->pTempObject ); pSoldier->pTempObject = NULL; pSoldier->usPendingAnimation = NO_PENDING_ANIMATION; pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION; // Decrement attack counter... DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending throw because saw something") ); DebugAttackBusy("@@@@@@@ Reducing attacker busy count..., ending throw because saw something\n"); FreeUpAttacker( ); // ATE: Goto stationary stance...... SoldierGotoStationaryStance( pSoldier ); DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); } // Kaiden: Added fix from UB for seeing new enemies when throwing Knives. // ATE: Dave, don't kill me // Here, we need to handle the situation when we're throweing a knife and we see somebody // cause for some reason throwing a knie does not use the pTempObject stuff that all other stuff does... if ( pSoldier->usPendingAnimation == THROW_KNIFE ) { // Decrement attack counter... DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending throw knife because saw something") ); DebugAttackBusy("@@@@@@@ Reducing attacker busy count..., ending throw knife because saw something"); FreeUpAttacker( ); // ATE: Goto stationary stance...... SoldierGotoStationaryStance( pSoldier ); DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); } if ( !( gTacticalStatus.uiFlags & INCOMBAT ) ) { EVENT_StopMerc( pSoldier, pSoldier->sGridNo, pSoldier->bDirection ); } else { // Pause this guy from no APS AdjustNoAPToFinishMove( pSoldier, TRUE ); pSoldier->ubReasonCantFinishMove = REASON_STOPPED_SIGHT; // ATE; IF turning to shoot, stop! // ATE: We want to do this only for enemies, not items.... if ( pSoldier->fTurningToShoot && fFromSightingEnemy ) { pSoldier->fTurningToShoot = FALSE; // Release attacker // OK - this is hightly annoying , but due to the huge combinations of // things that can happen - 1 of them is that sLastTarget will get unset // after turn is done - so set flag here to tell it not to... pSoldier->fDontUnsetLastTargetFromTurn = TRUE; DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending fire because saw something") ); DebugAttackBusy("@@@@@@@ Reducing attacker busy count..., ending fire because saw something\n"); FreeUpAttacker( ); } // OK, if we are stopped at our destination, cancel pending action... if ( fFromSightingEnemy ) { if ( pSoldier->ubPendingAction != NO_PENDING_ACTION && pSoldier->sGridNo == pSoldier->sFinalDestination ) { pSoldier->ubPendingAction = NO_PENDING_ACTION; } // Stop pending animation.... pSoldier->usPendingAnimation = NO_PENDING_ANIMATION; pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION; // Kaiden: Another UB Bug Fix. // ATE: Nasty bug - clear out any fence jumping that may be in progress pSoldier->fContinueMoveAfterStanceChange = FALSE; } if ( !pSoldier->fTurningToShoot ) { pSoldier->fTurningFromPronePosition = FALSE; } } // Unset UI! if ( fFromSightingEnemy || ( pSoldier->pTempObject == NULL && !pSoldier->fTurningToShoot ) ) { UnSetUIBusy( pSoldier->ubID ); } pSoldier->bTurningFromUI = FALSE; UnSetEngagedInConvFromPCAction( pSoldier ); } // HUALT EVENT IS USED TO STOP A MERC - NETWORKING SHOULD CHECK / ADJUST TO GRIDNO? void EVENT_StopMerc( SOLDIERTYPE *pSoldier, INT16 sGridNo, INT8 bDirection ) { INT16 sX, sY; // MOVE GUY TO GRIDNO--- SHOULD BE THE SAME UNLESS IN MULTIPLAYER // Makesure center of tile sX = CenterX( sGridNo ); sY = CenterY( sGridNo ); //Cancel pending events if ( !pSoldier->fDelayedMovement ) { pSoldier->usPendingAnimation = NO_PENDING_ANIMATION; pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION; pSoldier->ubPendingDirection = NO_PENDING_DIRECTION; pSoldier->ubPendingAction = NO_PENDING_ACTION; } pSoldier->bEndDoorOpenCode = 0; pSoldier->fTurningFromPronePosition = 0; // Cancel path data! pSoldier->usPathIndex = pSoldier->usPathDataSize = 0; // Set ext tile waiting flag off! pSoldier->fDelayedMovement = FALSE; // Turn off reverse... pSoldier->bReverse = FALSE; EVENT_SetSoldierPosition( pSoldier, (FLOAT) sX, (FLOAT) sY ); pSoldier->sDestXPos = (INT16)pSoldier->dXPos; pSoldier->sDestYPos = (INT16)pSoldier->dYPos; EVENT_SetSoldierDirection(pSoldier, bDirection); if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) { SoldierGotoStationaryStance( pSoldier ); } // ATE; IF turning to shoot, stop! if ( pSoldier->fTurningToShoot ) { pSoldier->fTurningToShoot = FALSE; // Release attacker DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending fire because saw something") ); DebugAttackBusy("@@@@@@@ Reducing attacker busy count..., ending fire because saw something\n"); FreeUpAttacker( ); } // Turn off multi-move speed override.... if ( pSoldier->sGridNo == pSoldier->sFinalDestination ) { pSoldier->fUseMoverrideMoveSpeed = FALSE; } // Unset UI! UnSetUIBusy( pSoldier->ubID ); UnMarkMovementReserved( pSoldier ); } void ReLoadSoldierAnimationDueToHandItemChange( SOLDIERTYPE *pSoldier, UINT16 usOldItem, UINT16 usNewItem ) { // DON'T continue aiming! // GOTO STANCE // CHECK FOR AIMING ANIMATIONS BOOLEAN fOldRifle = FALSE; BOOLEAN fNewRifle = FALSE; // Shutoff burst.... // ( we could be on, then change gun that does not have burst ) if ( Weapon[ usNewItem ].ubShotsPerBurst == 0 && !Weapon[pSoldier->inv[HANDPOS].usItem ].NoSemiAuto ) { pSoldier->bDoBurst = FALSE; pSoldier->bWeaponMode = WM_NORMAL; pSoldier->bDoAutofire = 0; } else if ( Weapon[usNewItem].NoSemiAuto ) { pSoldier->bDoBurst = TRUE; pSoldier->bWeaponMode = WM_AUTOFIRE; pSoldier->bDoAutofire = 1; } if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY ) { // Stop aiming! SoldierGotoStationaryStance( pSoldier ); } // Cancel services... GivingSoldierCancelServices( pSoldier ); // Did we have a rifle and do we now not have one? if ( usOldItem != NOTHING ) { if ( Item[ usOldItem ].usItemClass == IC_GUN ) { // if ( (Item[ usOldItem ].fFlags & ITEM_TWO_HANDED) && usOldItem != ROCKET_LAUNCHER ) if ( (Item[ usOldItem ].twohanded ) && !Item[usOldItem].rocketlauncher ) { fOldRifle = TRUE; } } } if ( usNewItem != NOTHING ) { if ( Item[ usNewItem ].usItemClass == IC_GUN ) { // if ( (Item[ usNewItem ].fFlags & ITEM_TWO_HANDED) && usNewItem != ROCKET_LAUNCHER ) if ( (Item[ usNewItem ].twohanded ) && !Item[usNewItem].rocketlauncher ) { fNewRifle = TRUE; } } } // Switch on stance! switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: if ( fOldRifle && !fNewRifle ) { // Put it away! EVENT_InitNewSoldierAnim( pSoldier, LOWER_RIFLE, 0 , FALSE ); } else if ( !fOldRifle && fNewRifle ) { // Bring it up! EVENT_InitNewSoldierAnim( pSoldier, RAISE_RIFLE, 0 , FALSE ); } else { SetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState ); } break; case ANIM_CROUCH: case ANIM_PRONE: SetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState ); break; } } UINT16 *CreateEnemyGlow16BPPPalette( SGPPaletteEntry *pPalette, UINT32 rscale, UINT32 gscale, BOOLEAN fAdjustGreen ) { UINT16 *p16BPPPalette, r16, g16, b16, usColor; UINT32 cnt; UINT32 rmod, gmod, bmod; UINT8 r,g,b; Assert( pPalette != NULL ); p16BPPPalette = (UINT16 *) MemAlloc( sizeof( UINT16 ) * 256 ); for ( cnt = 0; cnt < 256; cnt++ ) { gmod = (pPalette[ cnt ].peGreen); bmod = (pPalette[ cnt ].peBlue); rmod = __max( rscale, (pPalette[ cnt ].peRed) ); if ( fAdjustGreen ) { gmod = __max( gscale, (pPalette[ cnt ].peGreen) ); } r = (UINT8)__min(rmod, 255); g = (UINT8)__min(gmod, 255); b = (UINT8)__min(bmod, 255); if(gusRedShift < 0) r16=((UINT16)r>>(-gusRedShift)); else r16=((UINT16)r<>(-gusGreenShift)); else g16=((UINT16)g<>(-gusBlueShift)); else b16=((UINT16)b<>(-gusRedShift)); else r16=((UINT16)r<>(-gusGreenShift)); else g16=((UINT16)g<>(-gusBlueShift)); else b16=((UINT16)b<sFinalDestination; // Can we afford this? if ( pSoldier->bGoodContPath ) { sGridNo = pSoldier->sContPathLocation; } else { // ATE: OK, don't cancel count, so pending actions are still valid... pSoldier->ubPendingActionAnimCount = 0; } // get a path to dest... if ( FindBestPath( pSoldier, sGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, NO_COPYROUTE, 0 ) ) { sAPCost = PtsToMoveDirection( pSoldier, (UINT8)guiPathingData[ 0 ] ); if ( EnoughPoints( pSoldier, sAPCost, 0 , (BOOLEAN)( pSoldier->bTeam == gbPlayerNum ) ) ) { // Acknowledge if ( pSoldier->bTeam == gbPlayerNum ) { DoMercBattleSound( pSoldier, BATTLE_SOUND_OK1 ); // If we have a face, tell text in it to go away! if ( pSoldier->iFaceIndex != -1 ) { gFacesData[ pSoldier->iFaceIndex ].fDisplayTextOver = FACE_ERASE_TEXT_OVER; } } AdjustNoAPToFinishMove( pSoldier, FALSE ); SetUIBusy( pSoldier->ubID ); // OK, try and get a path to out dest! EVENT_InternalGetNewSoldierPath( pSoldier, sGridNo, pSoldier->usUIMovementMode, FALSE, TRUE ); } } } BOOLEAN CheckForBreathCollapse( SOLDIERTYPE *pSoldier ) { // Check if we are out of breath! // Only check if > 70 if ( pSoldier->bBreathMax > 70 ) { if ( pSoldier->bBreath < 20 && !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_LOW_BREATH ) && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND ) { // WARN! TacticalCharacterDialogue( pSoldier, QUOTE_OUT_OF_BREATH ); // Set flag indicating we were warned! pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_LOW_BREATH; } } // Check for drowing..... //if ( pSoldier->bBreath < 10 && !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_DROWNING ) && pSoldier->bOverTerrainType == DEEP_WATER ) //{ // WARN! // TacticalCharacterDialogue( pSoldier, QUOTE_DROWNING ); // Set flag indicating we were warned! // pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_DROWNING; // WISDOM GAIN (25): Starting to drown // StatChange( pSoldier, WISDOMAMT, 25, FALSE ); //} if ( pSoldier->bBreath == 0 && !pSoldier->bCollapsed && !( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE | SOLDIER_ANIMAL | SOLDIER_MONSTER ) ) ) { // Collapse! // OK, Set a flag, because we may still be in the middle of an animation what is not interruptable... pSoldier->bBreathCollapsed = TRUE; return( TRUE ); } return( FALSE ); } BOOLEAN InternalIsValidStance( SOLDIERTYPE *pSoldier, INT8 bDirection, INT8 bNewStance ) { UINT16 usOKToAddStructID=0; STRUCTURE_FILE_REF *pStructureFileRef; UINT16 usAnimSurface=0; UINT16 usAnimState; // Check, if dest is prone, we can actually do this! // If we are a vehicle, we can only 'stand' if ( ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) && bNewStance != ANIM_STAND ) { return( FALSE ); } // Check if we are in water? if ( MercInWater( pSoldier ) ) { if ( bNewStance == ANIM_PRONE || bNewStance == ANIM_CROUCH ) { return( FALSE ); } } if ( pSoldier->ubBodyType == ROBOTNOWEAPON && bNewStance != ANIM_STAND ) { return( FALSE ); } // Check if we are in water? if ( AM_AN_EPC( pSoldier ) ) { if ( bNewStance == ANIM_PRONE ) { return( FALSE ); } else { return( TRUE ); } } if ( pSoldier->bCollapsed ) { if ( bNewStance == ANIM_STAND || bNewStance == ANIM_CROUCH ) { return( FALSE ); } } // Check if we can do this.... if ( pSoldier->pLevelNode && pSoldier->pLevelNode->pStructureData != NULL ) { usOKToAddStructID = pSoldier->pLevelNode->pStructureData->usStructureID; } else { usOKToAddStructID = INVALID_STRUCTURE_ID; } switch( bNewStance ) { case ANIM_STAND: usAnimState = STANDING; break; case ANIM_CROUCH: usAnimState = CROUCHING; break; case ANIM_PRONE: usAnimState = PRONE; break; default: // Something gone funny here.... usAnimState = pSoldier->usAnimState; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"Wrong desired stance given: %d, %d.", bNewStance, pSoldier->usAnimState ); } usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, usAnimState ); // Get structure ref........ pStructureFileRef = GetAnimationStructureRef( pSoldier->ubID, usAnimSurface, usAnimState ); if ( pStructureFileRef != NULL ) { // Can we add structure data for this stance...? if ( !OkayToAddStructureToWorld( pSoldier->sGridNo, pSoldier->bLevel, &(pStructureFileRef->pDBStructureRef[gOneCDirection[ bDirection ]]), usOKToAddStructID ) ) { return( FALSE ); } } return( TRUE ); } BOOLEAN IsValidStance( SOLDIERTYPE *pSoldier, INT8 bNewStance ) { return( InternalIsValidStance( pSoldier, pSoldier->bDirection, bNewStance ) ); } BOOLEAN IsValidMovementMode( SOLDIERTYPE *pSoldier, INT16 usMovementMode ) { // Check, if dest is prone, we can actually do this! // Check if we are in water? if ( MercInWater( pSoldier ) ) { if ( usMovementMode == RUNNING || usMovementMode == SWATTING || usMovementMode == CRAWLING ) { return( FALSE ); } } return( TRUE ); } void SelectMoveAnimationFromStance( SOLDIERTYPE *pSoldier ) { // Determine which animation to do...depending on stance and gun in hand... switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: EVENT_InitNewSoldierAnim( pSoldier, WALKING, 0 , FALSE ); break; case ANIM_PRONE: EVENT_InitNewSoldierAnim( pSoldier, CRAWLING, 0 , FALSE ); break; case ANIM_CROUCH: EVENT_InitNewSoldierAnim( pSoldier, SWATTING, 0 , FALSE ); break; } } void GetActualSoldierAnimDims( SOLDIERTYPE *pSoldier, INT16 *psHeight, INT16 *psWidth ) { UINT16 usAnimSurface; ETRLEObject *pTrav; usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState ); if ( usAnimSurface == INVALID_ANIMATION_SURFACE ) { *psHeight = (INT16)5; *psWidth = (INT16)5; return; } if ( gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject == NULL ) { *psHeight = (INT16)5; *psWidth = (INT16)5; return; } // OK, noodle here on what we should do... If we take each frame, it will be different slightly // depending on the frame and the value returned here will vary thusly. However, for the // uses of this function, we should be able to use just the first frame... if ( pSoldier->usAniFrame >= gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->usNumberOfObjects ) { //int i = 0; return; } pTrav = &(gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->pETRLEObject[ pSoldier->usAniFrame ] ); *psHeight = (INT16)pTrav->usHeight; *psWidth = (INT16)pTrav->usWidth; } void GetActualSoldierAnimOffsets( SOLDIERTYPE *pSoldier, INT16 *sOffsetX, INT16 *sOffsetY ) { UINT16 usAnimSurface; ETRLEObject *pTrav; usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState ); if ( usAnimSurface == INVALID_ANIMATION_SURFACE ) { *sOffsetX = (INT16)0; *sOffsetY = (INT16)0; return; } if ( gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject == NULL ) { *sOffsetX = (INT16)0; *sOffsetY = (INT16)0; return; } pTrav = &(gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->pETRLEObject[ pSoldier->usAniFrame ] ); *sOffsetX = (INT16)pTrav->sOffsetX; *sOffsetY = (INT16)pTrav->sOffsetY; } void SetSoldierLocatorOffsets( SOLDIERTYPE *pSoldier ) { INT16 sHeight, sWidth; INT16 sOffsetX, sOffsetY; // OK, from our animation, get height, width GetActualSoldierAnimDims( pSoldier, &sHeight, &sWidth ); GetActualSoldierAnimOffsets( pSoldier, &sOffsetX, &sOffsetY ); // OK, here, use the difference between center of animation ( sWidth/2 ) and our offset! //pSoldier->sLocatorOffX = ( abs( sOffsetX ) ) - ( sWidth / 2 ); pSoldier->sBoundingBoxWidth = sWidth; pSoldier->sBoundingBoxHeight = sHeight; pSoldier->sBoundingBoxOffsetX = sOffsetX; pSoldier->sBoundingBoxOffsetY = sOffsetY; } BOOLEAN SoldierCarriesTwoHandedWeapon( SOLDIERTYPE *pSoldier ) { UINT16 usItem; usItem = pSoldier->inv[ HANDPOS ].usItem; // if ( usItem != NOTHING && (Item[ usItem ].fFlags & ITEM_TWO_HANDED) ) if ( usItem != NOTHING && (Item[ usItem ].twohanded ) ) { return( TRUE ); } return( FALSE ); } INT32 CheckBleeding( SOLDIERTYPE *pSoldier ) { INT8 bBandaged; //,savedOurTurn; INT32 iBlood = NOBLOOD; if ( pSoldier->bLife != 0 ) { // if merc is hurt beyond the minimum required to bleed, or he's dying if ( ( pSoldier->bBleeding > MIN_BLEEDING_THRESHOLD) || pSoldier->bLife < OKLIFE ) { // if he's NOT in the process of being bandaged or DOCTORed if ( ( pSoldier->ubServiceCount == 0 ) && ( AnyDoctorWhoCanHealThisPatient( pSoldier, HEALABLE_EVER ) == NULL ) ) { // may drop blood whether or not any bleeding takes place this turn if ( pSoldier->bTilesMoved < 1 ) { iBlood = ( ( pSoldier->bBleeding - MIN_BLEEDING_THRESHOLD ) / BLOODDIVISOR ); // + pSoldier->dying; if ( iBlood > MAXBLOODQUANTITY ) { iBlood = MAXBLOODQUANTITY; } } else { iBlood = NOBLOOD; } // Are we in a different mode? if ( !(gTacticalStatus.uiFlags & TURNBASED ) || !(gTacticalStatus.uiFlags & INCOMBAT ) ) { pSoldier->dNextBleed -= (FLOAT)RT_NEXT_BLEED_MODIFIER; } else { // Do a single step descrease pSoldier->dNextBleed--; } // if it's time to lose some blood if ( pSoldier->dNextBleed <= 0) { // first, calculate if soldier is bandaged bBandaged = pSoldier->bLifeMax - pSoldier->bBleeding - pSoldier->bLife; // as long as he's bandaged and not "dying" if ( bBandaged && pSoldier->bLife >= OKLIFE ) { // just bleeding through existing bandages pSoldier->bBleeding++; SoldierBleed( pSoldier, TRUE ); } else // soldier is either not bandaged at all or is dying { if ( pSoldier->bLife < OKLIFE ) // if he's dying { // if he's conscious, and he hasn't already, say his "dying quote" if ( ( pSoldier->bLife >= CONSCIOUSNESS ) && !pSoldier->fDyingComment ) { TacticalCharacterDialogue( pSoldier, QUOTE_SERIOUSLY_WOUNDED ); pSoldier->fDyingComment = TRUE; } // can't permit lifemax to ever bleed beneath OKLIFE, or that // soldier might as well be dead! if (pSoldier->bLifeMax >= OKLIFE) { // bleeding while "dying" costs a PERMANENT point of life each time! pSoldier->bLifeMax--; pSoldier->bBleeding--; } } } // either way, a point of life (health) is lost because of bleeding // This will also update the life bar SoldierBleed( pSoldier, FALSE ); // if he's not dying (which includes him saying the dying quote just // now), and he hasn't warned us that he's bleeding yet, he does so // Also, not if they are being bandaged.... if ( ( pSoldier->bLife >= OKLIFE ) && !pSoldier->fDyingComment && !pSoldier->fWarnedAboutBleeding && !gTacticalStatus.fAutoBandageMode && pSoldier->ubServiceCount == 0 ) { TacticalCharacterDialogue( pSoldier, QUOTE_STARTING_TO_BLEED ); // "starting to bleed" quote pSoldier->fWarnedAboutBleeding = TRUE; } pSoldier->dNextBleed = CalcSoldierNextBleed( pSoldier ); } } } } return( iBlood ); } void SoldierBleed( SOLDIERTYPE *pSoldier, BOOLEAN fBandagedBleed ) { INT8 bOldLife; // OK, here make some stuff happen for bleeding // A banaged bleed does not show damage taken , just through existing bandages // ATE: Do this ONLY if buddy is in sector..... if ( ( pSoldier->bInSector && guiCurrentScreen == GAME_SCREEN ) || guiCurrentScreen != GAME_SCREEN ) { pSoldier->fFlashPortrait = TRUE; pSoldier->bFlashPortraitFrame = FLASH_PORTRAIT_STARTSHADE; RESETTIMECOUNTER( pSoldier->PortraitFlashCounter, FLASH_PORTRAIT_DELAY ); // If we are in mapscreen, set this person as selected if ( guiCurrentScreen == MAP_SCREEN ) { SetInfoChar( pSoldier->ubID ); } } bOldLife = pSoldier->bLife; // If we are already dead, don't show damage! if ( !fBandagedBleed ) { SoldierTakeDamage( pSoldier, ANIM_CROUCH, 1, 100, TAKE_DAMAGE_BLOODLOSS, NOBODY, NOWHERE, 0, TRUE ); } } void SoldierCollapse( SOLDIERTYPE *pSoldier ) { BOOLEAN fMerc = FALSE; if ( pSoldier->ubBodyType <= REGFEMALE ) { fMerc = TRUE; } // If we are an animal, etc, don't do anything.... switch( pSoldier->ubBodyType ) { case ADULTFEMALEMONSTER: case AM_MONSTER: case YAF_MONSTER: case YAM_MONSTER: case LARVAE_MONSTER: case INFANT_MONSTER: case QUEENMONSTER: // Give breath back.... DeductPoints( pSoldier,0, (INT16)-5000 ); return; break; } pSoldier->bCollapsed = TRUE; ReceivingSoldierCancelServices( pSoldier ); // CC has requested - handle sight here... HandleSight( pSoldier, SIGHT_LOOK ); // Check height switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) { case ANIM_STAND: if ( pSoldier->bOverTerrainType == DEEP_WATER ) { EVENT_InitNewSoldierAnim( pSoldier, DEEP_WATER_DIE, 0, FALSE ); } else if ( pSoldier->bOverTerrainType == LOW_WATER ) { EVENT_InitNewSoldierAnim( pSoldier, WATER_DIE, 0, FALSE ); } else { BeginTyingToFall( pSoldier ); EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0, FALSE ); } break; case ANIM_CROUCH: // Crouched or prone, only for mercs! BeginTyingToFall( pSoldier ); if ( fMerc ) { EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_CROUCH, 0 , FALSE); } else { // For civs... use fall from stand... EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0 , FALSE); } break; case ANIM_PRONE: switch( pSoldier->usAnimState ) { case FALLFORWARD_FROMHIT_STAND: case ENDFALLFORWARD_FROMHIT_CROUCH: ChangeSoldierState( pSoldier, STAND_FALLFORWARD_STOP, 0, FALSE ); break; case FALLBACK_HIT_STAND: ChangeSoldierState( pSoldier, FALLBACKHIT_STOP, 0, FALSE ); break; default: EVENT_InitNewSoldierAnim( pSoldier, PRONE_LAY_FROMHIT, 0 , FALSE ); break; } break; } if (pSoldier->uiStatusFlags & SOLDIER_ENEMY) { if ( !(gTacticalStatus.bPanicTriggerIsAlarm) && (gTacticalStatus.ubTheChosenOne == pSoldier->ubID) ) { // replace this guy as the chosen one! gTacticalStatus.ubTheChosenOne = NOBODY; MakeClosestEnemyChosenOne(); } if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) && (pSoldier->uiStatusFlags & SOLDIER_UNDERAICONTROL)) { #ifdef TESTAICONTROL DebugAI( String("Ending turn for %d because of error from HandleItem", pSoldier->ubID ) ); #endif EndAIGuysTurn( pSoldier ); } } // DON'T DE-SELECT GUY..... //else //{ // Check if this is our selected guy... // if ( pSoldier->ubID == gusSelectedSoldier ) // { // SelectNextAvailSoldier( pSoldier ); // } //} } FLOAT CalcSoldierNextBleed( SOLDIERTYPE *pSoldier ) { INT8 bBandaged; // calculate how many turns before he bleeds again // bleeding faster the lower life gets, and if merc is running around //pSoldier->nextbleed = 2 + (pSoldier->life / (10 + pSoldier->tilesMoved)); // min = 2 // if bandaged, give 1/2 of the bandaged life points back into equation bBandaged = pSoldier->bLifeMax - pSoldier->bLife - pSoldier->bBleeding; return( (FLOAT)1 + (FLOAT)( (pSoldier->bLife + bBandaged / 2) / (10 + pSoldier->bTilesMoved) ) ); // min = 1 } FLOAT CalcSoldierNextUnmovingBleed( SOLDIERTYPE *pSoldier ) { INT8 bBandaged; // calculate bleeding rate without the penalty for tiles moved // if bandaged, give 1/2 of the bandaged life points back into equation bBandaged = pSoldier->bLifeMax - pSoldier->bLife - pSoldier->bBleeding; return( (FLOAT)1 + (FLOAT)( (pSoldier->bLife + bBandaged / 2) / 10 ) ); // min = 1 } void HandlePlacingRoofMarker( SOLDIERTYPE *pSoldier, INT16 sGridNo, BOOLEAN fSet, BOOLEAN fForce ) { LEVELNODE *pRoofNode; LEVELNODE *pNode; if ( pSoldier->bVisible == -1 && fSet ) { return; } if ( pSoldier->bTeam != gbPlayerNum ) { //return; } // If we are on the roof, add roof UI peice! if ( pSoldier->bLevel == SECOND_LEVEL ) { // Get roof node pRoofNode = gpWorldLevelData[ sGridNo ].pRoofHead; // Return if we are still climbing roof.... if ( pSoldier->usAnimState == CLIMBUPROOF && !fForce ) { return; } if ( pRoofNode != NULL ) { if ( fSet ) { if ( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REVEALED ) { // Set some flags on this poor thing //pRoofNode->uiFlags |= ( LEVELNODE_USEBESTTRANSTYPE | LEVELNODE_REVEAL | LEVELNODE_DYNAMIC ); //pRoofNode->uiFlags |= ( LEVELNODE_DYNAMIC ); //pRoofNode->uiFlags &= ( ~LEVELNODE_HIDDEN ); //ResetSpecificLayerOptimizing( TILES_DYNAMIC_ROOF ); } } else { if ( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REVEALED ) { // Remove some flags on this poor thing //pRoofNode->uiFlags &= ~( LEVELNODE_USEBESTTRANSTYPE | LEVELNODE_REVEAL | LEVELNODE_DYNAMIC ); //pRoofNode->uiFlags |= LEVELNODE_HIDDEN; } } if ( fSet ) { // If it does not exist already.... if ( !IndexExistsInRoofLayer( sGridNo, FIRSTPOINTERS11 ) ) { pNode = AddRoofToTail( sGridNo, FIRSTPOINTERS11 ); pNode->ubShadeLevel =DEFAULT_SHADE_LEVEL; pNode->ubNaturalShadeLevel=DEFAULT_SHADE_LEVEL; } } else { RemoveRoof( sGridNo, FIRSTPOINTERS11 ); } } } } void PositionSoldierLight( SOLDIERTYPE *pSoldier ) { // DO ONLY IF WE'RE AT A GOOD LEVEL if ( ubAmbientLightLevel < MIN_AMB_LEVEL_FOR_MERC_LIGHTS ) { return; } if ( !pSoldier->bInSector ) { return; } if ( pSoldier->bTeam != gbPlayerNum ) { return; } if ( pSoldier->bLife < OKLIFE ) { return; } //if the player DOESNT want the merc to cast light if( !gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] ) { return; } if ( pSoldier->iLight == -1 ) { CreateSoldierLight( pSoldier ); } //if ( pSoldier->ubID == gusSelectedSoldier ) { LightSpritePower(pSoldier->iLight, TRUE); LightSpriteFake(pSoldier->iLight); LightSpritePosition( pSoldier->iLight, (INT16)(pSoldier->sX/CELL_X_SIZE), (INT16)(pSoldier->sY/CELL_Y_SIZE)); } } void SetCheckSoldierLightFlag( SOLDIERTYPE *pSoldier ) { PositionSoldierLight( pSoldier ); //pSoldier->uiStatusFlags |= SOLDIER_RECHECKLIGHT; } void PickPickupAnimation( SOLDIERTYPE *pSoldier, INT32 iItemIndex, INT16 sGridNo, INT8 bZLevel ) { INT8 bDirection; STRUCTURE *pStructure; BOOLEAN fDoNormalPickup = TRUE; // OK, Given the gridno, determine if it's the same one or different.... if ( sGridNo != pSoldier->sGridNo ) { // Get direction to face.... bDirection = (INT8)GetDirectionFromGridNo( sGridNo, pSoldier ); pSoldier->ubPendingDirection = bDirection; // Change to pickup animation EVENT_InitNewSoldierAnim( pSoldier, ADJACENT_GET_ITEM, 0 , FALSE ); if (!(pSoldier->uiStatusFlags & SOLDIER_PC)) { // set "pending action" value for AI so it will wait pSoldier->bAction = AI_ACTION_PENDING_ACTION; } } else { // If in water.... if ( MercInWater( pSoldier ) ) { UnSetUIBusy( pSoldier->ubID ); HandleSoldierPickupItem( pSoldier, iItemIndex, sGridNo, bZLevel ); SoldierGotoStationaryStance( pSoldier ); if (!(pSoldier->uiStatusFlags & SOLDIER_PC)) { // reset action value for AI because we're done! ActionDone( pSoldier ); } } else { // Don't show animation of getting item, if we are not standing switch ( gAnimControl[ pSoldier->usAnimState ].ubHeight ) { case ANIM_STAND: // OK, if we are looking at z-level >0, AND // we have a strucxture with items in it // look for orientation and use angle accordingly.... if ( bZLevel > 0 ) { //#if 0 // Get direction to face.... if ( ( pStructure = FindStructure( (INT16)sGridNo, ( STRUCTURE_HASITEMONTOP | STRUCTURE_OPENABLE ) ) ) != NULL ) { fDoNormalPickup = FALSE; // OK, look at orientation switch( pStructure->ubWallOrientation ) { case OUTSIDE_TOP_LEFT: case INSIDE_TOP_LEFT: bDirection = (INT8)NORTH; break; case OUTSIDE_TOP_RIGHT: case INSIDE_TOP_RIGHT: bDirection = (INT8)WEST; break; default: bDirection = pSoldier->bDirection; break; } //pSoldier->ubPendingDirection = bDirection; EVENT_SetSoldierDesiredDirection( pSoldier, bDirection ); EVENT_SetSoldierDirection( pSoldier, bDirection ); // Change to pickup animation EVENT_InitNewSoldierAnim( pSoldier, ADJACENT_GET_ITEM, 0 , FALSE ); } //#endif } if ( fDoNormalPickup ) { EVENT_InitNewSoldierAnim( pSoldier, PICKUP_ITEM, 0 , FALSE ); } if (!(pSoldier->uiStatusFlags & SOLDIER_PC)) { // set "pending action" value for AI so it will wait pSoldier->bAction = AI_ACTION_PENDING_ACTION; } break; case ANIM_CROUCH: case ANIM_PRONE: UnSetUIBusy( pSoldier->ubID ); HandleSoldierPickupItem( pSoldier, iItemIndex, sGridNo, bZLevel ); SoldierGotoStationaryStance( pSoldier ); if (!(pSoldier->uiStatusFlags & SOLDIER_PC)) { // reset action value for AI because we're done! ActionDone( pSoldier ); } break; } } } } void PickDropItemAnimation( SOLDIERTYPE *pSoldier ) { // Don't show animation of getting item, if we are not standing switch ( gAnimControl[ pSoldier->usAnimState ].ubHeight ) { case ANIM_STAND: pSoldier->bAction = AI_ACTION_PENDING_ACTION; EVENT_InitNewSoldierAnim( pSoldier, DROP_ITEM, 0 , FALSE ); break; case ANIM_CROUCH: case ANIM_PRONE: SoldierHandleDropItem( pSoldier ); SoldierGotoStationaryStance( pSoldier ); ActionDone( pSoldier); break; } } void EVENT_SoldierBeginCutFence( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection ) { // Make sure we have a structure here.... if ( IsCuttableWireFenceAtGridNo( sGridNo ) ) { // CHANGE DIRECTION AND GOTO ANIMATION NOW EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection ); EVENT_SetSoldierDirection( pSoldier, ubDirection ); //BOOLEAN CutWireFence( INT16 sGridNo ) // SET TARGET GRIDNO pSoldier->sTargetGridNo = sGridNo; // CHANGE TO ANIMATION EVENT_InitNewSoldierAnim( pSoldier, CUTTING_FENCE, 0 , FALSE ); } } void EVENT_SoldierBeginRepair( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection ) { INT8 bRepairItem; UINT8 ubID; // Make sure we have a structure here.... bRepairItem = IsRepairableStructAtGridNo( sGridNo, &ubID ); if ( bRepairItem ) { // CHANGE DIRECTION AND GOTO ANIMATION NOW EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection ); EVENT_SetSoldierDirection( pSoldier, ubDirection ); //BOOLEAN CutWireFence( INT16 sGridNo ) // SET TARGET GRIDNO //pSoldier->sTargetGridNo = sGridNo; // CHANGE TO ANIMATION EVENT_InitNewSoldierAnim( pSoldier, GOTO_REPAIRMAN, 0 , FALSE ); // SET BUDDY'S ASSIGNMENT TO REPAIR... // Are we a SAM site? ( 3 == SAM ) if ( bRepairItem == 3 ) { SetSoldierAssignment( pSoldier, REPAIR, TRUE, FALSE, -1 ); } else if ( bRepairItem == 2 ) // ( 2 == VEHICLE ) { SetSoldierAssignment( pSoldier, REPAIR, FALSE, FALSE, ubID ); } } } void EVENT_SoldierBeginRefuel( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection ) { INT8 bRefuelItem; UINT8 ubID; // Make sure we have a structure here.... bRefuelItem = IsRefuelableStructAtGridNo( sGridNo, &ubID ); if ( bRefuelItem ) { // CHANGE DIRECTION AND GOTO ANIMATION NOW EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection ); EVENT_SetSoldierDirection( pSoldier, ubDirection ); //BOOLEAN CutWireFence( INT16 sGridNo ) // SET TARGET GRIDNO //pSoldier->sTargetGridNo = sGridNo; // CHANGE TO ANIMATION EVENT_InitNewSoldierAnim( pSoldier, REFUEL_VEHICLE, 0 , FALSE ); // SET BUDDY'S ASSIGNMENT TO REPAIR... } } void EVENT_SoldierBeginTakeBlood( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection ) { ROTTING_CORPSE *pCorpse; // See if these is a corpse here.... pCorpse = GetCorpseAtGridNo( sGridNo , pSoldier->bLevel ); if ( pCorpse != NULL ) { pSoldier->uiPendingActionData4 = pCorpse->iID; // CHANGE DIRECTION AND GOTO ANIMATION NOW EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection ); EVENT_SetSoldierDirection( pSoldier, ubDirection ); EVENT_InitNewSoldierAnim( pSoldier, TAKE_BLOOD_FROM_CORPSE, 0 , FALSE ); } else { // Say NOTHING quote... DoMercBattleSound( pSoldier, BATTLE_SOUND_NOTHING ); } } void EVENT_SoldierBeginAttachCan( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection ) { STRUCTURE * pStructure; DOOR_STATUS * pDoorStatus; // OK, find door, attach to door, do animation...., remove item.... // First make sure we still have item in hand.... if ( !Item[pSoldier->inv[ HANDPOS ].usItem].canandstring ) { return; } pStructure = FindStructure( sGridNo, STRUCTURE_ANYDOOR ); if ( pStructure == NULL ) { return; } // Modify door status to make sure one is created for this door // Use the current door state for this if ( !(pStructure->fFlags & STRUCTURE_OPEN) ) { ModifyDoorStatus( sGridNo, FALSE, FALSE ); } else { ModifyDoorStatus( sGridNo, TRUE, TRUE ); } // Now get door status... pDoorStatus = GetDoorStatus( sGridNo ); if ( pDoorStatus == NULL ) { // SOmething wrong here... return; } // OK set flag! pDoorStatus->ubFlags |= DOOR_HAS_TIN_CAN; // Do animation EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection ); EVENT_SetSoldierDirection( pSoldier, ubDirection ); EVENT_InitNewSoldierAnim( pSoldier, ATTACH_CAN_TO_STRING, 0 , FALSE ); // Remove item... DeleteObj( &(pSoldier->inv[ HANDPOS ] ) ); fInterfacePanelDirty = DIRTYLEVEL2; } void EVENT_SoldierBeginReloadRobot( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection, UINT8 ubMercSlot ) { UINT8 ubPerson; // Make sure we have a robot here.... ubPerson = WhoIsThere2( sGridNo, pSoldier->bLevel ); if ( ubPerson != NOBODY && MercPtrs[ ubPerson ]->uiStatusFlags & SOLDIER_ROBOT ) { // CHANGE DIRECTION AND GOTO ANIMATION NOW EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection ); EVENT_SetSoldierDirection( pSoldier, ubDirection ); // CHANGE TO ANIMATION EVENT_InitNewSoldierAnim( pSoldier, RELOAD_ROBOT, 0 , FALSE ); } } void ResetSoldierChangeStatTimer( SOLDIERTYPE *pSoldier ) { pSoldier->uiChangeLevelTime = 0; pSoldier->uiChangeHealthTime = 0; pSoldier->uiChangeStrengthTime= 0; pSoldier->uiChangeDexterityTime= 0; pSoldier->uiChangeAgilityTime= 0; pSoldier->uiChangeWisdomTime= 0; pSoldier->uiChangeLeadershipTime= 0; pSoldier->uiChangeMarksmanshipTime= 0; pSoldier->uiChangeExplosivesTime= 0; pSoldier->uiChangeMedicalTime= 0; pSoldier->uiChangeMechanicalTime= 0; return; } void ChangeToFlybackAnimation( SOLDIERTYPE *pSoldier, INT8 bDirection ) { UINT16 usNewGridNo; // Get dest gridno, convert to center coords usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) ); usNewGridNo = NewGridNo( (UINT16)usNewGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) ); // Remove any previous actions pSoldier->ubPendingAction = NO_PENDING_ACTION; // Set path.... pSoldier->usPathDataSize = 0; pSoldier->usPathIndex = 0; pSoldier->usPathingData[ pSoldier->usPathDataSize ] = gOppositeDirection[ pSoldier->bDirection ]; pSoldier->usPathDataSize++; pSoldier->usPathingData[ pSoldier->usPathDataSize ] = gOppositeDirection[ pSoldier->bDirection ]; pSoldier->usPathDataSize++; pSoldier->sFinalDestination = usNewGridNo; EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, FLYBACK_HIT ); // Get a new direction based on direction EVENT_InitNewSoldierAnim( pSoldier, FLYBACK_HIT, 0 , FALSE ); } void ChangeToFallbackAnimation( SOLDIERTYPE *pSoldier, INT8 bDirection ) { UINT16 usNewGridNo; // Get dest gridno, convert to center coords usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) ); //usNewGridNo = NewGridNo( (UINT16)usNewGridNo, (UINT16)(-1 * DirectionInc( bDirection ) ) ); // Remove any previous actions pSoldier->ubPendingAction = NO_PENDING_ACTION; // Set path.... pSoldier->usPathDataSize = 0; pSoldier->usPathIndex = 0; pSoldier->usPathingData[ pSoldier->usPathDataSize ] = gOppositeDirection[ pSoldier->bDirection ]; pSoldier->usPathDataSize++; pSoldier->sFinalDestination = usNewGridNo; EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, FALLBACK_HIT_STAND ); // Get a new direction based on direction EVENT_InitNewSoldierAnim( pSoldier, FALLBACK_HIT_STAND, 0 , FALSE ); } void SetSoldierCowerState( SOLDIERTYPE *pSoldier, BOOLEAN fOn ) { // Robot's don't cower! if ( pSoldier->ubBodyType == ROBOTNOWEAPON ) { DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("ERROR: Robot was told to cower!" ) ); return; } // OK< set flag and do anim... if ( fOn ) { if ( !( pSoldier->uiStatusFlags & SOLDIER_COWERING ) ) { EVENT_InitNewSoldierAnim( pSoldier, START_COWER, 0, FALSE ); pSoldier->uiStatusFlags |= SOLDIER_COWERING; pSoldier->ubDesiredHeight = ANIM_CROUCH; } } else { if ( (pSoldier->uiStatusFlags & SOLDIER_COWERING) ) { EVENT_InitNewSoldierAnim( pSoldier, END_COWER, 0, FALSE ); pSoldier->uiStatusFlags &= (~SOLDIER_COWERING ); pSoldier->ubDesiredHeight = ANIM_STAND; } } } void MercStealFromMerc( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTarget ) { INT16 sActionGridNo, sGridNo, sAdjustedGridNo; UINT8 ubDirection; // OK, find an adjacent gridno.... sGridNo = pTarget->sGridNo; // See if we can get there to punch sActionGridNo = FindAdjacentGridEx( pSoldier, sGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE ); if ( sActionGridNo != -1 ) { // SEND PENDING ACTION pSoldier->ubPendingAction = MERC_STEAL; pSoldier->bTargetLevel = pTarget->bLevel; // 0verhaul: Update the level too! pSoldier->sPendingActionData2 = pTarget->sGridNo; pSoldier->bPendingActionData3 = ubDirection; pSoldier->ubPendingActionAnimCount = 0; // CHECK IF WE ARE AT THIS GRIDNO NOW if ( pSoldier->sGridNo != sActionGridNo ) { // WALK UP TO DEST FIRST SendGetNewSoldierPathEvent( pSoldier, sActionGridNo, pSoldier->usUIMovementMode ); } else { EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection ); EVENT_InitNewSoldierAnim( pSoldier, STEAL_ITEM, 0 , FALSE ); } // OK, set UI // gTacticalStatus.ubAttackBusyCount++; // reset attacking item (hand) pSoldier->usAttackingWeapon = 0; DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting STEAL attack, attack count now %d", gTacticalStatus.ubAttackBusyCount) ); DebugAttackBusy( String( "!!!!!!! Starting STEAL attack, attack count now %d\n", gTacticalStatus.ubAttackBusyCount ) ); SetUIBusy( pSoldier->ubID ); } } SOLDIERTYPE *pTMilitiaSoldier; BOOLEAN PlayerSoldierStartTalking( SOLDIERTYPE *pSoldier, UINT8 ubTargetID, BOOLEAN fValidate ) { INT16 sFacingDir, sXPos, sYPos, sAPCost; SOLDIERTYPE *pTSoldier; UINT32 uiRange; if ( ubTargetID == NOBODY ) { return( FALSE ); } pTSoldier = MercPtrs[ ubTargetID ]; // Check distance again, to be sure if ( fValidate ) { // OK, since we locked this guy from moving // we should be close enough, so talk ( unless he is now dead ) if ( !IsValidTalkableNPC( (UINT8)ubTargetID, FALSE, FALSE, FALSE ) ) { return( FALSE ); } uiRange = GetRangeFromGridNoDiff( pSoldier->sGridNo, pTSoldier->sGridNo ); if ( uiRange > ( NPC_TALK_RADIUS * 2 ) ) { // Todo here - should we follow dude? return( FALSE ); } } // Get APs... sAPCost = AP_TALK; if ( !(gTacticalStatus.uiFlags & INCOMBAT) || (gTacticalStatus.uiFlags & REALTIME) ) //lal { ConvertGridNoToXY( pTSoldier->sGridNo, &sXPos, &sYPos ); // Get direction from mouse pos sFacingDir = GetDirectionFromXY( sXPos, sYPos, pSoldier ); // Set our guy facing SendSoldierSetDesiredDirectionEvent( pSoldier, sFacingDir ); // Set NPC facing SendSoldierSetDesiredDirectionEvent( pTSoldier, gOppositeDirection[ sFacingDir ] ); // Stop our guys... EVENT_StopMerc( pSoldier, pSoldier->sGridNo, pSoldier->bDirection ); } pTMilitiaSoldier = pTSoldier; //lal //lal // ATE; Check for normal civs... if ( GetCivType( pTSoldier ) != CIV_TYPE_NA ) { //lal if ( ( pTSoldier->bTeam == MILITIA_TEAM ) && ( gGameExternalOptions.fAllowTacticalMilitiaCommand == TRUE ) && (pSoldier->bSide == pTSoldier->bSide) ) { PopupMilitiaControlMenu( pTSoldier ); return( FALSE ); } else { // Deduct points from our guy.... DeductPoints( pSoldier, sAPCost, 0 ); StartCivQuote( pTSoldier ); return( FALSE ); } } // Are we an EPC that is being escorted? if ( pTSoldier->ubProfile != NO_PROFILE && pTSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { return( InitiateConversation( pTSoldier, pSoldier, APPROACH_EPC_WHO_IS_RECRUITED, 0 ) ); //Converse( pTSoldier->ubProfile, pSoldier->ubProfile, APPROACH_EPC_WHO_IS_RECRUITED, 0 ); } else if (pTSoldier->bNeutral) { switch( pTSoldier->ubProfile ) { case JIM: case JACK: case OLAF: case RAY: case OLGA: case TYRONE: // Start combat etc DeleteTalkingMenu(); CancelAIAction( pTSoldier, TRUE ); AddToShouldBecomeHostileOrSayQuoteList( pTSoldier->ubID ); break; default: // Start talking! return( InitiateConversation( pTSoldier, pSoldier, NPC_INITIAL_QUOTE, 0 ) ); break; } } else { // Start talking with hostile NPC return( InitiateConversation( pTSoldier, pSoldier, APPROACH_ENEMY_NPC_QUOTE, 0 ) ); } return( TRUE ); } BOOLEAN IsValidSecondHandShot( SOLDIERTYPE *pSoldier ) { if ( Item[ pSoldier->inv[ SECONDHANDPOS ].usItem ].usItemClass == IC_GUN && !(Item[ pSoldier->inv[SECONDHANDPOS ].usItem ].twohanded ) && !pSoldier->bDoBurst && !Item[pSoldier->inv[ HANDPOS ].usItem].grenadelauncher && Item[ pSoldier->inv[HANDPOS].usItem ].usItemClass == IC_GUN && pSoldier->inv[SECONDHANDPOS].bGunStatus >= USABLE && pSoldier->inv[SECONDHANDPOS].ubGunShotsLeft > 0 ) { return( TRUE ); } return( FALSE ); } BOOLEAN IsValidSecondHandShotForReloadingPurposes( SOLDIERTYPE *pSoldier ) { // should be maintained as same as function above with line // about ammo taken out! if ( Item[ pSoldier->inv[ SECONDHANDPOS ].usItem ].usItemClass == IC_GUN && !pSoldier->bDoBurst && !Item[pSoldier->inv[ HANDPOS ].usItem].grenadelauncher && Item[ pSoldier->inv[HANDPOS].usItem ].usItemClass == IC_GUN && pSoldier->inv[SECONDHANDPOS].bGunStatus >= USABLE //&& // pSoldier->inv[SECONDHANDPOS].ubGunShotsLeft > 0 && // gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_PRONE ) ) { return( TRUE ); } return( FALSE ); } BOOLEAN CanRobotBeControlled( SOLDIERTYPE *pSoldier ) { SOLDIERTYPE *pController; if ( !( pSoldier->uiStatusFlags & SOLDIER_ROBOT ) ) { return( FALSE ); } if ( pSoldier->ubRobotRemoteHolderID == NOBODY ) { return( FALSE ); } pController = MercPtrs[ pSoldier->ubRobotRemoteHolderID ]; if ( pController->bActive ) { if ( ControllingRobot( pController ) ) { // ALL'S OK! return( TRUE ); } } return( FALSE ); } BOOLEAN ControllingRobot( SOLDIERTYPE *pSoldier ) { SOLDIERTYPE *pRobot; INT8 bPos; if ( !pSoldier->bActive ) { return( FALSE ); } // EPCs can't control the robot (no inventory to hold remote, for one) if ( AM_AN_EPC( pSoldier ) ) { return( FALSE ); } // Don't require pSoldier->bInSector here, it must work from mapscreen! // are we in ok shape? if ( pSoldier->bLife < OKLIFE || ( pSoldier->bTeam != gbPlayerNum ) ) { return( FALSE ); } // allow control from within vehicles - allows strategic travel in a vehicle with robot! if ( ( pSoldier->bAssignment >= ON_DUTY ) && ( pSoldier->bAssignment != VEHICLE ) ) { return( FALSE ); } // is the soldier wearing a robot remote control? bPos = FindRemoteControl( pSoldier ); if ( bPos == NO_SLOT ) { return( FALSE ); } // Find the robot pRobot = FindSoldierByProfileID( ROBOT, TRUE ); if ( !pRobot ) { return( FALSE ); } if ( pRobot->bActive ) { // Are we in the same sector....? // ARM: CHANGED TO WORK IN MAPSCREEN, DON'T USE WorldSector HERE if ( pRobot->sSectorX == pSoldier->sSectorX && pRobot->sSectorY == pSoldier->sSectorY && pRobot->bSectorZ == pSoldier->bSectorZ ) { // they have to be either both in sector, or both on the road if ( pRobot->fBetweenSectors == pSoldier->fBetweenSectors ) { // if they're on the road... if ( pRobot->fBetweenSectors ) { // they have to be in the same squad or vehicle if ( pRobot->bAssignment != pSoldier->bAssignment ) { return( FALSE ); } // if in a vehicle, must be the same vehicle if ( pRobot->bAssignment == VEHICLE && ( pRobot->iVehicleId != pSoldier->iVehicleId ) ) { return( FALSE ); } } // all OK! return( TRUE ); } } } return( FALSE ); } SOLDIERTYPE *GetRobotController( SOLDIERTYPE *pSoldier ) { if ( pSoldier->ubRobotRemoteHolderID == NOBODY ) { return( NULL ); } else { return( MercPtrs[ pSoldier->ubRobotRemoteHolderID ] ); } } void UpdateRobotControllerGivenRobot( SOLDIERTYPE *pRobot ) { SOLDIERTYPE *pTeamSoldier; INT32 cnt = 0; // Loop through guys and look for a controller! // set up soldier ptr as first element in mercptrs list cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; // run through list for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++, pTeamSoldier++) { if ( pTeamSoldier->bActive ) { if ( ControllingRobot( pTeamSoldier ) ) { pRobot->ubRobotRemoteHolderID = pTeamSoldier->ubID; return; } } } pRobot->ubRobotRemoteHolderID = NOBODY; } void UpdateRobotControllerGivenController( SOLDIERTYPE *pSoldier ) { SOLDIERTYPE *pTeamSoldier; INT32 cnt = 0; // First see if are still controlling the robot if ( !ControllingRobot( pSoldier ) ) { return; } // set up soldier ptr as first element in mercptrs list cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; // Loop through guys to find the robot.... for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++, pTeamSoldier++) { if ( pTeamSoldier->bActive && ( pTeamSoldier->uiStatusFlags & SOLDIER_ROBOT ) ) { pTeamSoldier->ubRobotRemoteHolderID = pSoldier->ubID; } } } void HandleSoldierTakeDamageFeedback( SOLDIERTYPE *pSoldier ) { // Do sound..... // if ( pSoldier->bLife >= CONSCIOUSNESS ) { // ATE: Limit how often we grunt... if ( ( GetJA2Clock( ) - pSoldier->uiTimeSinceLastBleedGrunt ) > 1000 ) { pSoldier->uiTimeSinceLastBleedGrunt = GetJA2Clock( ); DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_HIT1 + Random( 2 ) ) ); } } // Flash portrait.... pSoldier->fFlashPortrait = TRUE; pSoldier->bFlashPortraitFrame = FLASH_PORTRAIT_STARTSHADE; RESETTIMECOUNTER( pSoldier->PortraitFlashCounter, FLASH_PORTRAIT_DELAY ); } void HandleSystemNewAISituation( SOLDIERTYPE *pSoldier, BOOLEAN fResetABC ) { // Are we an AI guy? if ( gTacticalStatus.ubCurrentTeam != gbPlayerNum && pSoldier->bTeam != gbPlayerNum ) { if ( pSoldier->bNewSituation == IS_NEW_SITUATION ) { // Cancel what they were doing.... pSoldier->usPendingAnimation = NO_PENDING_ANIMATION; pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION; pSoldier->fTurningFromPronePosition = FALSE; pSoldier->ubPendingDirection = NO_PENDING_DIRECTION; pSoldier->ubPendingAction = NO_PENDING_ACTION; pSoldier->bEndDoorOpenCode = 0; // if this guy isn't under direct AI control, WHO GIVES A FLYING FLICK? if ( pSoldier->uiStatusFlags & SOLDIER_UNDERAICONTROL ) { if ( pSoldier->fTurningToShoot ) { pSoldier->fTurningToShoot = FALSE; // Release attacker // OK - this is hightly annoying , but due to the huge combinations of // things that can happen - 1 of them is that sLastTarget will get unset // after turn is done - so set flag here to tell it not to... pSoldier->fDontUnsetLastTargetFromTurn = TRUE; DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending fire because saw something: DONE IN SYSTEM NEW SITUATION") ); DebugAttackBusy( "@@@@@@@ Reducing attacker busy count..., ending fire because saw something: DONE IN SYSTEM NEW SITUATION\n" ); FreeUpAttacker( ); } if ( pSoldier->pTempObject != NULL ) { // Place it back into inv.... AutoPlaceObject( pSoldier, pSoldier->pTempObject, FALSE ); MemFree( pSoldier->pTempObject ); pSoldier->pTempObject = NULL; pSoldier->usPendingAnimation = NO_PENDING_ANIMATION; pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION; // Decrement attack counter... DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending throw because saw something: DONE IN SYSTEM NEW SITUATION") ); DebugAttackBusy("@@@@@@@ Reducing attacker busy count..., ending throw because saw something: DONE IN SYSTEM NEW SITUATION\n"); FreeUpAttacker( ); } } } } } void InternalPlaySoldierFootstepSound( SOLDIERTYPE * pSoldier ) { UINT8 ubRandomSnd; INT8 bVolume = MIDVOLUME; // Assume outside UINT32 ubSoundBase = WALK_LEFT_OUT; UINT8 ubRandomMax = 4; // Determine if we are on the floor if ( !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) ) { if ( pSoldier->usAnimState == HOPFENCE ) { bVolume = HIGHVOLUME; } if ( pSoldier->uiStatusFlags & SOLDIER_ROBOT ) { PlaySoldierJA2Sample( pSoldier->ubID, ROBOT_BEEP, RATE_11025, SoundVolume( bVolume, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ), TRUE ); return; } //if ( SoldierOnScreen( pSoldier->ubID ) ) { if ( pSoldier->usAnimState == CRAWLING ) { ubSoundBase = CRAWL_1; } else { // Pick base based on terrain over.... if ( pSoldier->bOverTerrainType == FLAT_FLOOR ) { ubSoundBase = WALK_LEFT_IN; } else if ( pSoldier->bOverTerrainType == DIRT_ROAD || pSoldier->bOverTerrainType == PAVED_ROAD ) { ubSoundBase = WALK_LEFT_ROAD; } else if ( pSoldier->bOverTerrainType == LOW_WATER || pSoldier->bOverTerrainType == MED_WATER ) { ubSoundBase = WATER_WALK1_IN; ubRandomMax = 2; } else if ( pSoldier->bOverTerrainType == DEEP_WATER ) { ubSoundBase = SWIM_1; ubRandomMax = 2; } } // Pick a random sound... do { ubRandomSnd = (UINT8)Random( ubRandomMax ); } while ( ubRandomSnd == pSoldier->ubLastFootPrintSound ); pSoldier->ubLastFootPrintSound = ubRandomSnd; // OK, if in realtime, don't play at full volume, because too many people walking around // sounds don't sound good - ( unless we are the selected guy, then always play at reg volume ) if ( ! ( gTacticalStatus.uiFlags & INCOMBAT ) && ( pSoldier->ubID != gusSelectedSoldier ) ) { bVolume = LOWVOLUME; } PlaySoldierJA2Sample( pSoldier->ubID, ubSoundBase + pSoldier->ubLastFootPrintSound, RATE_11025, SoundVolume( bVolume, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ), TRUE ); } } } void PlaySoldierFootstepSound( SOLDIERTYPE *pSoldier ) { // normally, not in stealth mode if ( !pSoldier->bStealthMode ) { InternalPlaySoldierFootstepSound( pSoldier ); } } void PlayStealthySoldierFootstepSound( SOLDIERTYPE *pSoldier ) { // even if in stealth mode InternalPlaySoldierFootstepSound( pSoldier ); } void CrowsFlyAway( UINT8 ubTeam ) { UINT32 cnt; SOLDIERTYPE *pTeamSoldier; for ( cnt = gTacticalStatus.Team[ ubTeam ].bFirstID, pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ ubTeam ].bLastID; cnt++, pTeamSoldier++ ) { if ( pTeamSoldier->bActive && pTeamSoldier->bInSector ) { if ( pTeamSoldier->ubBodyType == CROW && pTeamSoldier->usAnimState != CROW_FLY ) { // fly away even if not seen! HandleCrowFlyAway( pTeamSoldier ); } } } } #ifdef JA2BETAVERSION void DebugValidateSoldierData( ) { UINT32 cnt; SOLDIERTYPE *pSoldier; CHAR16 sString[ 1024 ]; BOOLEAN fProblemDetected = FALSE; static UINT32 uiFrameCount = 0; // this function is too slow to run every frame, so do the check only every 50 frames if ( uiFrameCount++ < 50 ) { return; } // reset frame counter uiFrameCount = 0; // Loop through our team... cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pSoldier++) { if ( pSoldier->bActive ) { // OK, first check for alive people // Don't do this check if we are a vehicle... if ( pSoldier->bLife > 0 && !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) ) { // Alive -- now check for proper group IDs if ( pSoldier->ubGroupID == 0 && pSoldier->bAssignment != IN_TRANSIT && pSoldier->bAssignment != ASSIGNMENT_POW && !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) ) { // This is bad! swprintf( sString, L"Soldier Data Error: Soldier %d is alive but has a zero group ID.", cnt ); fProblemDetected = TRUE; } else if ( ( pSoldier->ubGroupID != 0 ) && ( GetGroup( pSoldier->ubGroupID ) == NULL ) ) { // This is bad! swprintf( sString, L"Soldier Data Error: Soldier %d has an invalid group ID of %d.", cnt, pSoldier->ubGroupID ); fProblemDetected = TRUE; } } else { if ( pSoldier->ubGroupID != 0 && ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) ) { // Dead guys should have 0 group IDs //swprintf( sString, L"GroupID Error: Soldier %d is dead but has a non-zero group ID.", cnt ); //fProblemDetected = TRUE; } } // check for invalid sector data if ( ( pSoldier->bAssignment != IN_TRANSIT ) && ( ( pSoldier->sSectorX <= 0 ) || ( pSoldier->sSectorX >= 17 ) || ( pSoldier->sSectorY <= 0 ) || ( pSoldier->sSectorY >= 17 ) || ( pSoldier->bSectorZ < 0 ) || ( pSoldier->bSectorZ > 3 ) ) ) { swprintf( sString, L"Soldier Data Error: Soldier %d is located at %d/%d/%d.", cnt, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); fProblemDetected = TRUE; } } if ( fProblemDetected ) { SAIReportError( sString ); /* if ( guiCurrentScreen == MAP_SCREEN ) DoMapMessageBox( MSG_BOX_BASIC_STYLE, sString, MAP_SCREEN, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback ); else DoMessageBox( MSG_BOX_BASIC_STYLE, sString, GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_OK, NULL, NULL ); */ break; } } // also do this ValidatePlayersAreInOneGroupOnly(); } #endif void BeginTyingToFall( SOLDIERTYPE *pSoldier ) { pSoldier->bStartFallDir = pSoldier->bDirection; pSoldier->fTryingToFall = TRUE; // Randomize direction if ( Random( 50 ) < 25 ) { pSoldier->fFallClockwise = TRUE; } else { pSoldier->fFallClockwise = FALSE; } } void SetSoldierAsUnderAiControl( SOLDIERTYPE *pSoldierToSet ) { SOLDIERTYPE *pSoldier=NULL; INT32 cnt; if ( pSoldierToSet == NULL ) { return; } // Loop through ALL teams... cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ LAST_TEAM ].bLastID; cnt++,pSoldier++) { if( pSoldier->bActive ) { pSoldier->uiStatusFlags &= ~SOLDIER_UNDERAICONTROL; } } pSoldierToSet->uiStatusFlags |= SOLDIER_UNDERAICONTROL; } void HandlePlayerTogglingLightEffects( BOOLEAN fToggleValue ) { if( fToggleValue ) { //Toggle light status if ( gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] ) gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] = FALSE; else gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] = TRUE; } //Update all the mercs in the sector EnableDisableSoldierLightEffects( gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] ); SetRenderFlags(RENDER_FLAG_FULL); } void EnableDisableSoldierLightEffects( BOOLEAN fEnableLights ) { SOLDIERTYPE *pSoldier=NULL; INT32 cnt; // Loop through player teams... cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++,pSoldier++) { //if the soldier is in the sector if( pSoldier->bActive && pSoldier->bInSector && pSoldier->bLife >= OKLIFE ) { //if we are to enable the lights if( fEnableLights ) { //Add the light around the merc PositionSoldierLight( pSoldier ); } else { //Delete the fake light the merc casts DeleteSoldierLight( pSoldier ); //Light up the merc though SetSoldierPersonalLightLevel( pSoldier ); } } } } void SetSoldierPersonalLightLevel( SOLDIERTYPE *pSoldier ) { if( pSoldier == NULL ) { return; } if( pSoldier->sGridNo == NOWHERE ) { return; } //THe light level for the soldier gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubShadeLevel = 3; gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubSumLights = 5; gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubMaxLights = 5; gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubNaturalShadeLevel = 5; }