mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
Fixed old-style behavior when creating a soldier struct (caused memory corruption and leaks) Fixed soldiers unable to fire if an error happened while firing the off-hand weapon Fix animation system problems with soldiers interrupted while changing stance, such as twitching and not being able to navigate around obstacles Prevent splitting money while an item is in the hand, which would result in losing the original item Fix reverse of X,Y when checking visibility through a roof, which could make soldiers falsely visible/invisible Prevent militia from simply waiting on the border when enemies are known to be in the sector Fix stack overflow for soldier inventory debug display Optimized A* pathing and cleaned up VS2K5 specific code AI can now use all climb points for climbable buildings, not just a random few Fix to not allow flat roof butted against slanted roof to be identified as a climb point Prevent soldiers who cause an interrupt from a special action from continuing on their path after being interrupted Prevent soldiers who enter or leave deep water from moving a space too far and turning back Prevent soldiers from trying to crouch while in shallow water Make soldier turn in the closest direction when he must get up from prone and turn then fall back to prone Allow a soldier to swat for a tile before returning to prone if unable to return to prone immediately after a turn Fix civilians that should show up in a sector, missing because of others that aren't Fix a memset on a C++ class Place Skyrider in a sector near his helecopter once available and the helicopter is present Fix soldier corruption if too many shots burst-spread into too few tiles Include both first and last tile in spread when some locations must be skipped Fix to shoot at last location in burst Fix to remove other previous spread locations when calculating a new spread Fix to reload or rechamber off-hand when necessary, such as having a pistol shotgun in that hand git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@1532 3b4a5df2-a311-0410-b5c6-a8a6f20db521
12437 lines
343 KiB
C++
12437 lines
343 KiB
C++
#ifdef PRECOMPILEDHEADERS
|
|
#include "Tactical All.h"
|
|
#else
|
|
#include "builddefines.h"
|
|
#include <wchar.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#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 <math.h>
|
|
#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"
|
|
#include "Strategic Pathing.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 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.
|
|
// ----------------------------------------
|
|
|
|
#if 0
|
|
Inventory::Inventory() {
|
|
OBJECTTYPE filler;
|
|
memset( &filler, 0, sizeof( OBJECTTYPE ) );
|
|
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);
|
|
};
|
|
#endif
|
|
|
|
Inventory::Inventory(int slotCount ) :
|
|
inv(slotCount),
|
|
slotCnt( slotCount) {
|
|
#if 0
|
|
slotCnt = slotCount;
|
|
inv.reserve(slotCnt);
|
|
for (int idx=0; idx < slotCnt; ++idx) {
|
|
//OBJECTTYPE *filler = new OBJECTTYPE; // Use MEMALLOC?
|
|
OBJECTTYPE *filler = NULL;
|
|
filler = (OBJECTTYPE*)MemAlloc( sizeof( OBJECTTYPE ) );
|
|
memset( filler, 0, sizeof( OBJECTTYPE ) );
|
|
|
|
inv.push_back(*filler);
|
|
}
|
|
#endif
|
|
initialize();
|
|
Assert (inv.size() == slotCnt);
|
|
};
|
|
|
|
Inventory::Inventory(const Inventory& src) {
|
|
// 0verhaul: I do not think the reservation is necessary or advisable.
|
|
//inv.reserve(slotCnt);
|
|
//Assert (src.inv.size() == slotCnt);
|
|
inv = src.inv;
|
|
// 0verhaul: Must copy the new slot count or a class created with a different count assigned to this one
|
|
// will fail the assertion(s).
|
|
slotCnt = src.slotCnt;
|
|
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() == src.slotCnt);
|
|
Assert (inv.size() == slotCnt);
|
|
if (this != &src) {
|
|
slotCnt = src.slotCnt;
|
|
inv = src.inv;
|
|
#if 0
|
|
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];
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
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::initialize() {
|
|
Assert (inv.size() == slotCnt);
|
|
for (int idx=0; idx < slotCnt; ++idx) {
|
|
memset(&inv[idx], 0, sizeof(OBJECTTYPE));
|
|
}
|
|
};
|
|
|
|
// ----------------------------------------
|
|
|
|
SOLDIERTYPE::SOLDIERTYPE() :
|
|
inv(),
|
|
bNewItemCount(inv.size()),
|
|
bNewItemCycleCount(inv.size())
|
|
{
|
|
#if 0
|
|
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);
|
|
}
|
|
#endif
|
|
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.initialize();
|
|
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(NUM_INV_SLOTS),
|
|
bInvStatus(inv.size()),
|
|
bInvNumber(inv.size())
|
|
{
|
|
#if 0
|
|
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);
|
|
}
|
|
#endif
|
|
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 (vector<int>::size_type idx=0; idx < 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, UINT8 ubNewDirection, 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->ubDirection;
|
|
|
|
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->ubDirection;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 == NOBODY )
|
|
{
|
|
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->bTurningFromPronePosition == 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->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
|
|
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->ubDirection == gPurpendicularDirection[ pSoldier->ubDirection ][ 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 ( MercInWater( pSoldier) )
|
|
{
|
|
// 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 0
|
|
// 0verhaul: This is a test. The only time I have been able to make this code hit is when
|
|
// the player goes prone while moving. And that is not what this part is intended for. I
|
|
// have seen the soldier in the middle of crawling, get up, turn, and then go prone again to
|
|
// continue along his path. But this code was not hit for that part. And this code seems
|
|
// to be made for that part. So apparently they found another way to deal with it. So
|
|
// I disabled the "locked" code for usDontUpdateNewGridNoOnMoveAnimChange since it can cause
|
|
// problems of its own. Now we see if we can do without this part too.
|
|
if ( usNewState == CRAWLING && pSoldier->usDontUpdateNewGridNoOnMoveAnimChange == 1 )
|
|
{
|
|
if ( pSoldier->bTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE )
|
|
{
|
|
pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_START_UP_FROM_MOVE;
|
|
}
|
|
|
|
// ATE: IF we are starting to crawl, but have to getup to turn first......
|
|
if ( pSoldier->bTurningFromPronePosition == TURNING_FROM_PRONE_START_UP_FROM_MOVE )
|
|
{
|
|
usNewState = PRONE_UP;
|
|
pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// 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, (UINT8) 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 )
|
|
{
|
|
// 0verhaul: **** Special hack!!!!
|
|
// If a merc begins to go prone while moving, the LOCKED_NO_NEWGRIDNO is set. If the merc never finishes
|
|
// going prone, either due to interrupting the stance change with a different stance change, or other possible
|
|
// factors such as maybe getting shot (this is realtime so an enemy could see him), it stays on locked. Once
|
|
// it stays on locked, the soldier will be unable to navigate around obstacles but will simply stay put
|
|
// twitching. Since the LOCKED is only set when going prone, this unsets it.
|
|
if ( pSoldier->usDontUpdateNewGridNoOnMoveAnimChange != LOCKED_NO_NEWGRIDNO ||
|
|
(pSoldier->usDontUpdateNewGridNoOnMoveAnimChange == LOCKED_NO_NEWGRIDNO && pSoldier->usAnimState != PRONE_DOWN))
|
|
{
|
|
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( pSoldier->sGridNo, DirectionInc( (UINT8) 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->bTurningFromPronePosition = 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->bTurningFromPronePosition < 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->ubDirection ) );
|
|
usNewGridNo = NewGridNo( (UINT16)usNewGridNo, DirectionInc( pSoldier->ubDirection ) );
|
|
|
|
pSoldier->usPathDataSize = 0;
|
|
pSoldier->usPathIndex = 0;
|
|
pSoldier->usPathingData[ pSoldier->usPathDataSize ] = pSoldier->ubDirection;
|
|
pSoldier->usPathDataSize++;
|
|
pSoldier->usPathingData[ pSoldier->usPathDataSize ] = pSoldier->ubDirection;
|
|
pSoldier->usPathDataSize++;
|
|
pSoldier->sFinalDestination = usNewGridNo;
|
|
// Set direction
|
|
EVENT_InternalSetSoldierDestination( pSoldier, (UINT8) 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!=NOWHERE) )
|
|
{
|
|
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 = NOWHERE;
|
|
}
|
|
}
|
|
}
|
|
|
|
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 ( TERRAIN_IS_DEEP_WATER( pSoldier->bOverTerrainType) && !TERRAIN_IS_DEEP_WATER( pSoldier->bOldOverTerrainType) )
|
|
{
|
|
// 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;
|
|
pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = 1;
|
|
PlayJA2Sample( ENTER_DEEP_WATER_1, RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
|
|
|
|
}
|
|
}
|
|
|
|
// Damage water if in deep water....
|
|
if ( MercInHighWater( pSoldier) )
|
|
{
|
|
WaterDamage( pSoldier );
|
|
}
|
|
|
|
// OK, If we were in deep water but we are NOT now, handle mid animations!
|
|
if ( !TERRAIN_IS_DEEP_WATER( pSoldier->bOverTerrainType) && TERRAIN_IS_DEEP_WATER( pSoldier->bOldOverTerrainType) )
|
|
{
|
|
// Make transition from low to deep
|
|
EVENT_InitNewSoldierAnim( pSoldier, DEEP_TO_LOW_WATER, 0 , FALSE );
|
|
pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = 1;
|
|
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 ].ItemData.Generic.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 ) < pEnemy->GetMaxDistanceVisible(pSoldier->sGridNo, pSoldier->bLevel ) )
|
|
{
|
|
// 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->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
|
|
|
|
// 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->ubDirection, 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->ubDirection )
|
|
//{
|
|
// 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 ( MercInDeepWater( pSoldier) )
|
|
{
|
|
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:
|
|
// <SB> crouch throwing
|
|
case THROW_ITEM_CROUCHED:
|
|
// <SB> 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 ].ItemData.Gun.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 ].ItemData.Gun.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 ( MercInShallowWater( pSoldier) )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, WATER_HIT, 0 , FALSE );
|
|
return;
|
|
}
|
|
if ( MercInDeepWater( pSoldier) )
|
|
{
|
|
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->ubDirection );
|
|
|
|
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->ubDirection ) ) );
|
|
|
|
// CHECK OK DESTINATION!
|
|
if ( OKFallDirection( pSoldier, usNewGridNo, pSoldier->bLevel, pSoldier->ubDirection, JFK_HITDEATH ) )
|
|
{
|
|
usNewGridNo = NewGridNo( (UINT16)usNewGridNo, (INT8)( DirectionInc( pSoldier->ubDirection ) ) );
|
|
|
|
if ( OKFallDirection( pSoldier, usNewGridNo, pSoldier->bLevel, pSoldier->ubDirection, 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->ubDirection == bDirection)
|
|
{
|
|
usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ pSoldier->ubDirection ] ) );
|
|
|
|
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->ubDirection );
|
|
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->ubDirection );
|
|
|
|
// 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 ( !(uiDist = FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, COPYROUTE, fFlags ) ) )
|
|
{
|
|
// 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 ( MercInDeepWater( pSoldier) )
|
|
{
|
|
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 0
|
|
// Uhhmmmm, the name of this function has "NEWPath" in it.
|
|
if (pSoldier->bPathStored)
|
|
{
|
|
fContinue = TRUE;
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
iDest = FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, usMovementAnim, COPYROUTE, fFlags );
|
|
fContinue = (iDest != 0);
|
|
#if 0
|
|
}
|
|
#endif
|
|
|
|
// 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 ( MercInDeepWater( pSoldier) )
|
|
{
|
|
usMoveAnimState = DEEP_WATER_SWIM;
|
|
}
|
|
// Can't forget shallow water! AI will sometimes attempt to swat through it, which is not legal either
|
|
else if ( MercInWater( pSoldier) )
|
|
{
|
|
usMoveAnimState = WALKING;
|
|
}
|
|
|
|
// 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->ubDirection );
|
|
}
|
|
|
|
// 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 ( MercInDeepWater( pSoldier) )
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
if (!IsValidStance( pSoldier, ubDesiredStance))
|
|
{
|
|
AssertMsg( 0, "Attempted to set soldier to an invalid stance.");
|
|
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, UINT8 ubNewDirection, BOOLEAN fFromMove, UINT16 usAnimState )
|
|
{
|
|
INT16 sNewGridNo;
|
|
INT16 sXPos, sYPos;
|
|
|
|
// Get dest gridno, convert to center coords
|
|
sNewGridNo = NewGridNo( pSoldier->sGridNo, DirectionInc( ubNewDirection ) );
|
|
|
|
ConvertMapPosToWorldTileCenter( sNewGridNo, &sXPos, &sYPos );
|
|
|
|
// Save new dest gridno, x, y
|
|
pSoldier->sDestination = sNewGridNo;
|
|
pSoldier->sDestXPos = sXPos;
|
|
pSoldier->sDestYPos = sYPos;
|
|
|
|
pSoldier->bMovementDirection = (INT8)ubNewDirection;
|
|
|
|
|
|
// 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->ubDirection ][ ubNewDirection ];
|
|
|
|
// CHange actual and desired direction....
|
|
EVENT_SetSoldierDirection( pSoldier, ubPerpDirection );
|
|
pSoldier->bDesiredDirection = pSoldier->ubDirection;
|
|
}
|
|
else
|
|
{
|
|
if ( !( gAnimControl[ usAnimState ].uiFlags & ANIM_SPECIALMOVE ) )
|
|
{
|
|
EVENT_InternalSetSoldierDesiredDirection( pSoldier, ubNewDirection, fFromMove, usAnimState );
|
|
}
|
|
}
|
|
}
|
|
|
|
void EVENT_SetSoldierDestination( SOLDIERTYPE *pSoldier, UINT8 ubNewDirection )
|
|
{
|
|
EVENT_InternalSetSoldierDestination( pSoldier, ubNewDirection, 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, UINT8 ubNewDirection, 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....
|
|
ubNewDirection = gOppositeDirection[ ubNewDirection ];
|
|
}
|
|
|
|
|
|
pSoldier->bDesiredDirection = (INT8)ubNewDirection;
|
|
|
|
// 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->ubDirection )
|
|
{
|
|
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->bTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE )
|
|
{
|
|
pSoldier->bTurningFromPronePosition = 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->bTurningFromPronePosition = 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->ubDirection )
|
|
{
|
|
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->ubDirection, pSoldier->bDesiredDirection );
|
|
}
|
|
else
|
|
{
|
|
pSoldier->bTurningIncrement = (INT8) QuickestDirection( pSoldier->ubDirection, pSoldier->bDesiredDirection );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void EVENT_SetSoldierDesiredDirection( SOLDIERTYPE *pSoldier, UINT8 ubNewDirection )
|
|
{
|
|
EVENT_InternalSetSoldierDesiredDirection( pSoldier, ubNewDirection, FALSE, pSoldier->usAnimState );
|
|
}
|
|
|
|
|
|
void EVENT_SetSoldierDirection( SOLDIERTYPE *pSoldier, UINT16 usNewDirection )
|
|
{
|
|
// Remove old location data
|
|
HandleAnimationProfile( pSoldier, pSoldier->usAnimState, TRUE );
|
|
|
|
pSoldier->ubDirection = (INT8)usNewDirection;
|
|
|
|
// Updated extended direction.....
|
|
pSoldier->ubHiResDirection = ubExtDirection[ pSoldier->ubDirection ];
|
|
|
|
// 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->ubDirection ];
|
|
|
|
//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->ubDirection == NORTHWEST )
|
|
{
|
|
ubTempDir = 1;
|
|
}
|
|
if ( pSoldier->ubDirection == WEST )
|
|
{
|
|
ubTempDir = 0;
|
|
}
|
|
if ( pSoldier->ubDirection == EAST )
|
|
{
|
|
ubTempDir = 2;
|
|
}
|
|
}
|
|
else if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 2 )
|
|
{
|
|
ubTempDir = gDirectionFrom8to2[ pSoldier->ubDirection ];
|
|
}
|
|
|
|
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->ubDirection;
|
|
}
|
|
}
|
|
// 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->ubDirection == pSoldier->bDesiredDirection )
|
|
{
|
|
if ( ( (gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY ) &&
|
|
pSoldier->bTurningFromPronePosition == TURNING_FROM_PRONE_OFF) ||
|
|
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->bTurningFromPronePosition != TURNING_FROM_PRONE_OFF )
|
|
{
|
|
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->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->fTurningToFall )
|
|
{
|
|
if ( pSoldier->ubDirection == pSoldier->bDesiredDirection )
|
|
{
|
|
SelectFallAnimation( pSoldier );
|
|
pSoldier->fTurningToFall = FALSE;
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->fTurningUntilDone && ( pSoldier->ubPendingStanceChange != NO_PENDING_STANCE ) )
|
|
{
|
|
if ( pSoldier->ubDirection == pSoldier->bDesiredDirection )
|
|
{
|
|
SendChangeSoldierStanceEvent( pSoldier, pSoldier->ubPendingStanceChange );
|
|
pSoldier->ubPendingStanceChange = NO_PENDING_STANCE;
|
|
pSoldier->fTurningUntilDone = FALSE;
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->fTurningUntilDone && ( pSoldier->usPendingAnimation != NO_PENDING_ANIMATION ) )
|
|
{
|
|
if ( pSoldier->ubDirection == 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->ubDirection == 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->bTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE ) &&
|
|
( pSoldier->bTurningFromPronePosition != TURNING_FROM_PRONE_ON ) )
|
|
{
|
|
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->bTurningFromPronePosition == 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->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
|
|
}
|
|
|
|
// If a special code, make guy crawl after stance change!
|
|
if ( pSoldier->bTurningFromPronePosition == 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 );
|
|
}
|
|
// Else swat for a tile so that there's room to resume prone
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, pSoldier->usUIMovementMode, 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->ubDirection + QuickestDirection( pSoldier->ubDirection, pSoldier->bDesiredDirection );
|
|
sDirection = pSoldier->ubDirection + 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 the soldier is not crawling or multi-tiled, he should be allowed to turn in place. Even if there is some
|
|
// obstacle he shouldn't be standing on.
|
|
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 );
|
|
}
|
|
}
|
|
// Are we prone crawling?
|
|
else if ( pSoldier->usAnimState == CRAWLING )
|
|
{
|
|
// OK, we want to getup, turn and go prone again....
|
|
SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH );
|
|
pSoldier->bTurningFromPronePosition = 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->ubDirection;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
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->ubDirection )
|
|
{
|
|
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->ubDirection, &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;
|
|
|
|
// Make sure we hop the correct fence to follow our path!
|
|
if (pSoldier->usPathIndex < pSoldier->usPathDataSize)
|
|
{
|
|
bDirection = (INT8) pSoldier->usPathingData[ pSoldier->usPathIndex];
|
|
}
|
|
else
|
|
{
|
|
bDirection = pSoldier->ubDirection;
|
|
}
|
|
|
|
if ( FindFenceJumpDirection( pSoldier, pSoldier->sGridNo, 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->bTurningFromPronePosition = 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->ubDirection, &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->ubDirection == gOneCDirection[ bNewDirection ] ||
|
|
pSoldier->ubDirection == gTwoCDirection[ bNewDirection ] ||
|
|
pSoldier->ubDirection == bNewDirection ||
|
|
pSoldier->ubDirection == gOneCCDirection[ bNewDirection ] ||
|
|
pSoldier->ubDirection == 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->ubDirection, &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->ubDirection, &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 ) );
|
|
|
|
}
|
|
|
|
UINT8 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 ) );
|
|
|
|
}
|
|
|
|
UINT8 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->ubDirection != 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 ( TERRAIN_IS_WATER( pSoldier->bOverTerrainType))
|
|
{
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
BOOLEAN MercInShallowWater( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// Our water texture , for now is of a given type
|
|
if ( TERRAIN_IS_SHALLOW_WATER( pSoldier->bOverTerrainType))
|
|
{
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN MercInDeepWater( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// Our water texture , for now is of a given type
|
|
if ( TERRAIN_IS_DEEP_WATER( pSoldier->bOverTerrainType))
|
|
{
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
BOOLEAN MercInHighWater( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// Our water texture , for now is of a given type
|
|
if ( TERRAIN_IS_HIGH_WATER( pSoldier->bOverTerrainType))
|
|
{
|
|
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->ubDirection ] );
|
|
|
|
// 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->ubDirection ] );
|
|
|
|
// 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->ubDirection = 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->ubDirection );
|
|
|
|
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->ubDirection );
|
|
|
|
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->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
|
|
// 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->ubDirection;
|
|
//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->ubDirection );
|
|
}
|
|
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->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
|
|
}
|
|
}
|
|
|
|
// 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->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
|
|
|
|
// 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<<gusRedShift);
|
|
|
|
if(gusGreenShift < 0)
|
|
g16=((UINT16)g>>(-gusGreenShift));
|
|
else
|
|
g16=((UINT16)g<<gusGreenShift);
|
|
|
|
|
|
if(gusBlueShift < 0)
|
|
b16=((UINT16)b>>(-gusBlueShift));
|
|
else
|
|
b16=((UINT16)b<<gusBlueShift);
|
|
|
|
// Prevent creation of pure black color
|
|
usColor = (r16&gusRedMask)|(g16&gusGreenMask)|(b16&gusBlueMask);
|
|
|
|
if((usColor==0) && ((r+g+b)!=0))
|
|
usColor=0x0001;
|
|
|
|
p16BPPPalette[ cnt ] = usColor;
|
|
}
|
|
return( p16BPPPalette );
|
|
}
|
|
|
|
|
|
UINT16 *CreateEnemyGreyGlow16BPPPalette( SGPPaletteEntry *pPalette, UINT32 rscale, UINT32 gscale, BOOLEAN fAdjustGreen )
|
|
{
|
|
UINT16 *p16BPPPalette, r16, g16, b16, usColor;
|
|
UINT32 cnt, lumin;
|
|
UINT32 rmod, gmod, bmod;
|
|
UINT8 r,g,b;
|
|
|
|
Assert( pPalette != NULL );
|
|
|
|
p16BPPPalette = (UINT16 *) MemAlloc( sizeof( UINT16 ) * 256 );
|
|
|
|
for ( cnt = 0; cnt < 256; cnt++ )
|
|
{
|
|
lumin=(pPalette[ cnt ].peRed*299/1000)+ (pPalette[ cnt ].peGreen*587/1000)+(pPalette[ cnt ].peBlue*114/1000);
|
|
rmod=(100*lumin)/256;
|
|
gmod=(100*lumin)/256;
|
|
bmod=(100*lumin)/256;
|
|
|
|
|
|
|
|
rmod = __max( rscale, rmod );
|
|
|
|
if ( fAdjustGreen )
|
|
{
|
|
gmod = __max( gscale, gmod );
|
|
}
|
|
|
|
|
|
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<<gusRedShift);
|
|
|
|
if(gusGreenShift < 0)
|
|
g16=((UINT16)g>>(-gusGreenShift));
|
|
else
|
|
g16=((UINT16)g<<gusGreenShift);
|
|
|
|
|
|
if(gusBlueShift < 0)
|
|
b16=((UINT16)b>>(-gusBlueShift));
|
|
else
|
|
b16=((UINT16)b<<gusBlueShift);
|
|
|
|
// Prevent creation of pure black color
|
|
usColor = (r16&gusRedMask)|(g16&gusGreenMask)|(b16&gusBlueMask);
|
|
|
|
if((usColor==0) && ((r+g+b)!=0))
|
|
usColor=0x0001;
|
|
|
|
p16BPPPalette[ cnt ] = usColor;
|
|
}
|
|
return( p16BPPPalette );
|
|
}
|
|
|
|
|
|
void ContinueMercMovement( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT16 sAPCost;
|
|
INT16 sGridNo;
|
|
|
|
sGridNo = pSoldier->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->ubDirection, 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 ( MercInDeepWater( pSoldier) )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, DEEP_WATER_DIE, 0, FALSE );
|
|
}
|
|
else if ( MercInShallowWater( pSoldier) )
|
|
{
|
|
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 );
|
|
|
|
// 0verhaul: No special case here! First the FALLFORWARD_FROMHIT_CROUCH can be filled in to use the FALLFORWARD_FROMHIT_STAND anim
|
|
// then when real anims come, use them instead.
|
|
//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->ubDirection;
|
|
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, UINT8 ubDirection )
|
|
{
|
|
INT16 sNewGridNo;
|
|
UINT8 ubOppositeDir;
|
|
INT16 sDirectionInc;
|
|
|
|
ubOppositeDir = gOppositeDirection[ ubDirection ];
|
|
sDirectionInc = DirectionInc( ubOppositeDir);
|
|
|
|
// Get dest gridno, convert to center coords
|
|
sNewGridNo = NewGridNo( pSoldier->sGridNo, sDirectionInc );
|
|
if ( gubWorldMovementCosts[ sNewGridNo ][ ubOppositeDir ][ pSoldier->bLevel ] >= TRAVELCOST_BLOCKED )
|
|
{
|
|
// No room to fly back. Pretend we hit the wall and fall forward instead
|
|
BeginTyingToFall( pSoldier );
|
|
ChangeSoldierState( pSoldier, FALLFORWARD_FROMHIT_STAND, 0, FALSE );
|
|
return;
|
|
}
|
|
|
|
sNewGridNo = NewGridNo( sNewGridNo, sDirectionInc );
|
|
if ( gubWorldMovementCosts[ sNewGridNo ][ ubOppositeDir ][ pSoldier->bLevel ] >= TRAVELCOST_BLOCKED )
|
|
{
|
|
// No room to fly back. Fall back instead
|
|
BeginTyingToFall( pSoldier );
|
|
ChangeSoldierState( pSoldier, FALLBACK_HIT_STAND, 0, FALSE );
|
|
}
|
|
|
|
|
|
// Remove any previous actions
|
|
pSoldier->ubPendingAction = NO_PENDING_ACTION;
|
|
|
|
// Set path....
|
|
pSoldier->usPathDataSize = 0;
|
|
pSoldier->usPathIndex = 0;
|
|
pSoldier->usPathingData[ pSoldier->usPathDataSize ] = ubOppositeDir;
|
|
pSoldier->usPathDataSize++;
|
|
pSoldier->usPathingData[ pSoldier->usPathDataSize ] = ubOppositeDir;
|
|
pSoldier->usPathDataSize++;
|
|
pSoldier->sFinalDestination = sNewGridNo;
|
|
EVENT_InternalSetSoldierDestination( pSoldier, (UINT8) 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, UINT8 ubDirection )
|
|
{
|
|
INT16 sNewGridNo;
|
|
UINT8 ubOppositeDir;
|
|
INT16 sDirection;
|
|
|
|
ubOppositeDir = gOppositeDirection[ ubDirection ];
|
|
sDirection = DirectionInc( ubOppositeDir);
|
|
|
|
// Get dest gridno, convert to center coords
|
|
sNewGridNo = NewGridNo( pSoldier->sGridNo, sDirection );
|
|
if ( gubWorldMovementCosts[ sNewGridNo ][ ubOppositeDir ][ pSoldier->bLevel ] >= TRAVELCOST_BLOCKED )
|
|
{
|
|
// No room to fly back. Pretend we hit the wall and fall forward instead
|
|
BeginTyingToFall( pSoldier );
|
|
ChangeSoldierState( pSoldier, FALLFORWARD_FROMHIT_STAND, 0, FALSE );
|
|
return;
|
|
}
|
|
|
|
// Get dest gridno, convert to center coords
|
|
//sNewGridNo = NewGridNo( sNewGridNo, sDirection );
|
|
//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 ] = ubOppositeDir;
|
|
pSoldier->usPathDataSize++;
|
|
pSoldier->sFinalDestination = sNewGridNo;
|
|
//pSoldier->sFinalDestination = pSoldier->sGridNo;
|
|
EVENT_InternalSetSoldierDestination( pSoldier, ubOppositeDir, 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->ubDirection );
|
|
}
|
|
|
|
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].ItemData.Gun.bGunStatus >= USABLE &&
|
|
pSoldier->inv[SECONDHANDPOS].ItemData.Gun.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].ItemData.Gun.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?
|
|
// 0verhaul:
|
|
// This code will only stop a soldier if it is not the player's turn. The problem here is that the soldier's
|
|
// actions may have triggered an interrupt. This code is called in order to cancel the soldier's movement
|
|
// after the interrupt is triggered, so if the AI causes an interrupt and it's the player's turn, he will
|
|
// continue doing what he was going to do. We need this function to work even when it's the player's turn,
|
|
// at least in this case.
|
|
if ( gTacticalStatus.ubCurrentTeam != gbPlayerNum && pSoldier->bTeam != gbPlayerNum )
|
|
//if ( 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->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
|
|
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 ( MercInShallowWater( pSoldier) )
|
|
{
|
|
ubSoundBase = WATER_WALK1_IN;
|
|
ubRandomMax = 2;
|
|
}
|
|
else if ( MercInDeepWater( pSoldier) )
|
|
{
|
|
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->ubDirection;
|
|
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;
|
|
}
|