mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-16 12:08:36 +00:00
Change strings.h to wistrings.h to prevent SDL.h from including it while trying to include the CRT strings.h
1189 lines
31 KiB
C++
1189 lines
31 KiB
C++
#include "ht.h"
|
|
#include "wistrings.h"
|
|
|
|
namespace wi {
|
|
|
|
static AnimationData *s_panidStructureExplosion = NULL;
|
|
static AnimationData *s_panidBigSmoke = NULL;
|
|
static DialogForm *s_pfrmConfirmation = NULL;
|
|
TBitmap *StructGob::s_ptbmRepairing = NULL;
|
|
TBitmap *StructGob::s_ptbmNeedsPower = NULL;
|
|
TBitmap *StructGob::s_ptbmNeedCredits = NULL;
|
|
|
|
//===========================================================================
|
|
// StructGob implementation
|
|
|
|
bool StructGob::InitClass(StructConsts *pstruc, IniReader *pini)
|
|
{
|
|
if (!UnitGob::InitClass(pstruc, pini))
|
|
return false;
|
|
|
|
char szTemplate[10];
|
|
itoa(pstruc->gt, szTemplate, 10);
|
|
|
|
// Required properties
|
|
|
|
if (pini->GetPropertyValue(szTemplate, "TileDimensions", "%d,%d",
|
|
&pstruc->ctx, &pstruc->cty) != 2)
|
|
return false;
|
|
|
|
int nArmorStrength;
|
|
if (pini->GetPropertyValue(szTemplate, "ArmorStrength", "%d", &nArmorStrength) != 1)
|
|
return false;
|
|
Assert(nArmorStrength < 1 << 10);
|
|
pstruc->fxArmorStrength = itofx(nArmorStrength);
|
|
|
|
// Don't include Headquarters in min/max since they aren't player-buildable
|
|
|
|
if (pstruc->gt != kgtHeadquarters) {
|
|
gfxStructureArmorStrengthMin = _min(gfxStructureArmorStrengthMin, pstruc->fxArmorStrength);
|
|
gfxStructureArmorStrengthMax = _max(gfxStructureArmorStrengthMax, pstruc->fxArmorStrength);
|
|
}
|
|
|
|
// Optional properties
|
|
|
|
if (pini->GetPropertyValue(szTemplate, "ArmorStrengthMP", "%d", &nArmorStrength) != 1) {
|
|
pstruc->fxArmorStrengthMP = pstruc->fxArmorStrength;
|
|
} else {
|
|
Assert(nArmorStrength < 1 << 10);
|
|
pstruc->fxArmorStrengthMP = itofx(nArmorStrength);
|
|
}
|
|
|
|
if (pini->GetPropertyValue(szTemplate, "ReserveDimensions", "%d,%d",
|
|
&pstruc->ctxReserve, &pstruc->ctyReserve) != 2) {
|
|
pstruc->ctxReserve = pstruc->ctx;
|
|
pstruc->ctyReserve = pstruc->cty;
|
|
}
|
|
|
|
if (pini->GetPropertyValue(szTemplate, "PowerSupply", "%d", &pstruc->nPowerSupply) != 1)
|
|
pstruc->nPowerSupply = 0;
|
|
gnPowerSupplyMin = _min(gnPowerSupplyMin, pstruc->nPowerSupply);
|
|
gnPowerSupplyMax = _max(gnPowerSupplyMax, pstruc->nPowerSupply);
|
|
|
|
if (pini->GetPropertyValue(szTemplate, "PowerDemand", "%d", &pstruc->nPowerDemand) != 1)
|
|
pstruc->nPowerDemand = 0;
|
|
gnPowerDemandMin = _min(gnPowerDemandMin, pstruc->nPowerDemand);
|
|
gnPowerDemandMax = _max(gnPowerDemandMax, pstruc->nPowerDemand);
|
|
|
|
if (pini->GetPropertyValue(szTemplate, "UpgradeCost", "%d", &pstruc->nUpgradeCost) != 1)
|
|
pstruc->nUpgradeCost = 0;
|
|
|
|
// Preload the structure's menu form
|
|
|
|
if (!LoadMenu(pstruc, pini, szTemplate, kidfStructMenu))
|
|
return false;
|
|
|
|
// Preload the confirmation "Are you sure?" dialog
|
|
|
|
if (!LoadConfirmation()) {
|
|
return false;
|
|
}
|
|
|
|
// Preload the smoke animation data
|
|
|
|
if (s_panidBigSmoke == NULL) {
|
|
s_panidBigSmoke = LoadAnimationData("smoke.anir");
|
|
if (s_panidBigSmoke == NULL)
|
|
return false;
|
|
}
|
|
|
|
// Preload the structure explosion animation data
|
|
|
|
if (s_panidStructureExplosion == NULL) {
|
|
s_panidStructureExplosion = LoadAnimationData("sexplosion.anir");
|
|
if (s_panidStructureExplosion == NULL)
|
|
return false;
|
|
}
|
|
|
|
// Preload the repair symbol bitmap
|
|
|
|
if (s_ptbmRepairing == NULL) {
|
|
s_ptbmRepairing = LoadTBitmap("repairing_symbol.tbm");
|
|
if (s_ptbmRepairing == NULL) {
|
|
Assert("Failed to load repairing_symbol.tbm");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Preload the needs power symbol bitmap
|
|
|
|
if (s_ptbmNeedsPower == NULL) {
|
|
s_ptbmNeedsPower = LoadTBitmap("needs_power_symbol.tbm");
|
|
if (s_ptbmNeedsPower == NULL) {
|
|
Assert("Failed to load needs_power_symbol.tbm");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Preload the need credits symbol bitmap
|
|
|
|
if (s_ptbmNeedCredits == NULL) {
|
|
s_ptbmNeedCredits = LoadTBitmap("needs_credits_symbol.tbm");
|
|
if (s_ptbmNeedCredits == NULL) {
|
|
Assert("Failed to load need_credits_symbol.tbm");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// All structures defog a larger area
|
|
|
|
pstruc->wf |= kfUntcLargeDefog;
|
|
|
|
return true;
|
|
}
|
|
|
|
void StructGob::ExitClass(StructConsts *pstruc)
|
|
{
|
|
delete s_ptbmNeedCredits;
|
|
s_ptbmNeedCredits = NULL;
|
|
delete s_ptbmNeedsPower;
|
|
s_ptbmNeedsPower = NULL;
|
|
delete s_ptbmRepairing;
|
|
s_ptbmRepairing = NULL;
|
|
delete s_panidStructureExplosion;
|
|
s_panidStructureExplosion = NULL;
|
|
delete s_panidBigSmoke;
|
|
s_panidBigSmoke = NULL;
|
|
delete s_pfrmConfirmation;
|
|
s_pfrmConfirmation = NULL;
|
|
|
|
UnitGob::ExitClass(pstruc);
|
|
}
|
|
|
|
StructGob::StructGob(StructConsts *pstruc) : UnitGob(pstruc)
|
|
{
|
|
m_ff |= kfGobStructure;
|
|
m_tLastSmoke = 0;
|
|
m_nSeqLastVisible = 0;
|
|
}
|
|
|
|
StructGob::~StructGob()
|
|
{
|
|
}
|
|
|
|
bool StructGob::Init(WCoord wx, WCoord wy, Player *pplr, fix fxHealth, dword ff, const char *pszName)
|
|
{
|
|
if (!UnitGob::Init(wx, wy, pplr, fxHealth, ff, pszName))
|
|
return false;
|
|
|
|
// Mark this structure's position as occupied on the terrain map
|
|
|
|
TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap();
|
|
TCoord tx = TcFromWc(m_wx);
|
|
TCoord ty = TcFromWc(m_wy);
|
|
Assert(tx >= 0 && ty >= 0 && tx + m_pstruc->ctx <= ptrmap->GetWidth() && ty + m_pstruc->cty <= ptrmap->GetHeight(),
|
|
"%s out of map bounds", m_pstruc->szName);
|
|
ptrmap->SetFlags(tx, ty, m_pstruc->ctx, m_pstruc->cty, kbfStructure);
|
|
Assert(tx + m_pstruc->ctxReserve <= ptrmap->GetWidth() && ty + m_pstruc->ctyReserve <= ptrmap->GetHeight(),
|
|
"%s reserve out of map bounds", m_pstruc->szName);
|
|
ptrmap->SetFlags(tx, ty, m_pstruc->ctxReserve, m_pstruc->ctyReserve, kbfReserved);
|
|
|
|
// Shadow this structure's occupation in the gid map
|
|
|
|
ggobm.ShadowGob(this, TcFromWc(m_wx), TcFromWc(m_wy), m_pstruc->ctx, m_pstruc->cty);
|
|
|
|
// Redraw this part of the minimap
|
|
|
|
TRect trc;
|
|
GetTileRect(&trc);
|
|
gpmm->RedrawTRect(&trc);
|
|
|
|
if (ff & kfGobBeingBuilt) {
|
|
SetState(kstBeingBuilt);
|
|
} else {
|
|
Activate();
|
|
SetState(kstIdle);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void StructGob::Delete()
|
|
{
|
|
// Mark this structure's position as unoccupied on the terrain map
|
|
|
|
gsim.GetLevel()->GetTerrainMap()->ClearFlags(TcFromWc(m_wx), TcFromWc(m_wy),
|
|
m_pstruc->ctx, m_pstruc->cty, kbfStructure);
|
|
gsim.GetLevel()->GetTerrainMap()->ClearFlags(TcFromWc(m_wx), TcFromWc(m_wy),
|
|
m_pstruc->ctxReserve, m_pstruc->ctyReserve, kbfReserved);
|
|
|
|
// Unshadow this structure's occupation in the gid map
|
|
|
|
ggobm.UnshadowGob(this, TcFromWc(m_wx), TcFromWc(m_wy), m_pstruc->ctx, m_pstruc->cty);
|
|
|
|
// Remove from Gid map and force minimap redraw
|
|
|
|
UnitGob::Delete();
|
|
}
|
|
|
|
#define knVerStructGobState 3
|
|
bool StructGob::LoadState(Stream *pstm)
|
|
{
|
|
byte nVer = pstm->ReadByte();
|
|
if (nVer != knVerStructGobState)
|
|
return false;
|
|
dword ctLastSmoke = pstm->ReadDword();
|
|
m_tLastSmoke = gsim.GetTickCount() - ctLastSmoke;
|
|
|
|
if (UnitGob::LoadState(pstm)) {
|
|
ggobm.ShadowGob(this, TcFromWc(m_wx), TcFromWc(m_wy), m_pstruc->ctx, m_pstruc->cty);
|
|
if (m_wfUnit & kfUnitNeedCredits)
|
|
WaitingForCredits(true, true);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool StructGob::SaveState(Stream *pstm)
|
|
{
|
|
pstm->WriteByte(knVerStructGobState);
|
|
pstm->WriteDword((dword)(gsim.GetTickCount() - m_tLastSmoke));
|
|
return UnitGob::SaveState(pstm);
|
|
}
|
|
|
|
// are you sure dialog used to confirm structure recycling
|
|
|
|
bool StructGob::LoadConfirmation()
|
|
{
|
|
if (s_pfrmConfirmation != NULL)
|
|
return true;
|
|
|
|
s_pfrmConfirmation = new DialogForm();
|
|
if (s_pfrmConfirmation == NULL)
|
|
return false;
|
|
|
|
if (!s_pfrmConfirmation->Init(gpmfrmm, gpiniForms, kidfAreYouSure))
|
|
return false;
|
|
|
|
s_pfrmConfirmation->SetFlags(s_pfrmConfirmation->GetFlags() | kfFrmAutoTakedown);
|
|
|
|
gpmfrmm->RemoveForm(s_pfrmConfirmation);
|
|
return true;
|
|
}
|
|
|
|
bool StructGob::PopupConfirmation(char *pszTitle)
|
|
{
|
|
Assert(s_pfrmConfirmation != NULL);
|
|
|
|
// set the title color here so it will be correct for the gob we're
|
|
// popping off of
|
|
|
|
s_pfrmConfirmation->SetTitleColor(GetSideColor(GetSide()));
|
|
|
|
// set the position to be near the gob like the menu is
|
|
|
|
Rect rc;
|
|
s_pfrmConfirmation->GetRect(&rc);
|
|
int cx = rc.Width();
|
|
int cy = rc.Height();
|
|
Size sizPlayfield;
|
|
ggame.GetPlayfieldSize(&sizPlayfield);
|
|
|
|
WPoint wpt;
|
|
GetCenter(&wpt);
|
|
WCoord wxViewOrigin, wyViewOrigin;
|
|
gsim.GetViewPos(&wxViewOrigin, &wyViewOrigin);
|
|
|
|
int xDialog = PcFromWc(wpt.wx - wxViewOrigin) - (cx / 2);
|
|
int yDialog = PcFromWc(wpt.wy - wyViewOrigin) - (cy / 2);
|
|
if (xDialog < 0)
|
|
xDialog = 0;
|
|
else if (xDialog >= sizPlayfield.cx - cx)
|
|
xDialog = sizPlayfield.cx - cx;
|
|
if (yDialog < 0)
|
|
yDialog = 0;
|
|
else if (yDialog >= sizPlayfield.cy - cy)
|
|
yDialog = sizPlayfield.cy - cy;
|
|
rc.Offset(xDialog - rc.left, yDialog - rc.top);
|
|
s_pfrmConfirmation->SetRect(&rc);
|
|
|
|
// set the title
|
|
|
|
LabelControl *pctl = (LabelControl *)s_pfrmConfirmation->GetControlPtr(kidcTitle);
|
|
pctl->SetText(pszTitle);
|
|
|
|
// Show the dialog and get the user's selection
|
|
|
|
s_pfrmConfirmation->SetUserDataPtr((void*) this);
|
|
gpmfrmm->AddForm(s_pfrmConfirmation);
|
|
int idc;
|
|
s_pfrmConfirmation->DoModal(&idc);
|
|
gpmfrmm->RemoveForm(s_pfrmConfirmation);
|
|
return idc == kidcOk;
|
|
}
|
|
|
|
void StructGob::TakedownConfirmation()
|
|
{
|
|
Assert(s_pfrmConfirmation != NULL);
|
|
if ((StructGob *)s_pfrmConfirmation->GetUserDataPtr() == this)
|
|
s_pfrmConfirmation->EndForm(kidcCancel);
|
|
}
|
|
|
|
void StructGob::Activate()
|
|
{
|
|
// Now this structure can influence global power
|
|
|
|
m_pplr->AddPowerSupplyAndDemand(m_pstruc->nPowerSupply, m_pstruc->nPowerDemand);
|
|
|
|
// Add to area lists
|
|
|
|
AreaMask amNew = ggobm.CalcAreaMask(TcFromWc(m_wx), TcFromWc(m_wy), m_pstruc->ctx, m_pstruc->cty);
|
|
ggobm.MoveGobBetweenAreas(m_gid, 0, amNew);
|
|
|
|
// Call base
|
|
|
|
UnitGob::Activate();
|
|
}
|
|
|
|
void StructGob::Deactivate()
|
|
{
|
|
WaitingForCredits(false);
|
|
m_wfUnit &= ~(kfUnitRepairing | kfUnitDrawRepairingSymbol | kfUnitDrawNeedsPowerSymbol);
|
|
EnableOrDisableSymbolLayer();
|
|
|
|
// Remove this structure's global power influence
|
|
|
|
m_pplr->AddPowerSupplyAndDemand(-m_pstruc->nPowerSupply, -m_pstruc->nPowerDemand);
|
|
|
|
Assert(s_pfrmConfirmation != NULL);
|
|
if ((StructGob *)s_pfrmConfirmation->GetUserDataPtr() == this)
|
|
s_pfrmConfirmation->EndForm(kidcCancel);
|
|
|
|
// Remove from area lists
|
|
|
|
AreaMask amOld = ggobm.CalcAreaMask(TcFromWc(m_wx), TcFromWc(m_wy), m_pstruc->ctx, m_pstruc->cty);
|
|
ggobm.MoveGobBetweenAreas(m_gid, amOld, 0);
|
|
|
|
// Call base
|
|
|
|
UnitGob::Deactivate();
|
|
}
|
|
|
|
void StructGob::GetTileRect(TRect *ptrc)
|
|
{
|
|
ptrc->left = TcFromWc(m_wx);
|
|
ptrc->top = TcFromWc(m_wy);
|
|
ptrc->right = ptrc->left + m_pstruc->ctx;
|
|
ptrc->bottom = ptrc->top + m_pstruc->cty;
|
|
}
|
|
|
|
void StructGob::GetTilePaddedWRect(WRect *pwrc)
|
|
{
|
|
pwrc->left = WcTrunc(m_wx);
|
|
pwrc->top = WcTrunc(m_wy);
|
|
pwrc->right = pwrc->left + WcFromTc(m_pstruc->ctx);
|
|
pwrc->bottom = pwrc->top + WcFromTc(m_pstruc->cty);
|
|
}
|
|
|
|
bool StructGob::IsAccessible(TCoord tx, TCoord ty)
|
|
{
|
|
// tx, ty is in the structure "somewhere". See if it is accessible (a tile somewhere around it that is free
|
|
// of terrain and free of structures
|
|
|
|
TerrainMap *ptrmap = gsim.GetLevel()->GetTerrainMap();
|
|
TCoord ctx, cty;
|
|
ggobm.GetMapSize(&ctx, &cty);
|
|
for (Direction dir = 0; dir < 8; dir++) {
|
|
TCoord txT = tx + g_mpDirToDx[dir];
|
|
TCoord tyT = ty + g_mpDirToDy[dir];
|
|
if (txT < 0 || txT >= ctx || tyT < 0 || tyT >= cty)
|
|
continue;
|
|
if (!ptrmap->IsBlocked(txT, tyT, kbfStructure))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void StructGob::GetAttackPoint(WPoint *pwpt)
|
|
{
|
|
// Need to get a terrain accessible point on this structure that can be attacked.
|
|
// This tile needs to be free of blocking terrain and blocking structures.
|
|
// If there is none at least get the closest to the enemy.
|
|
|
|
// Don't use GetCenter() as that uses the UI rect. We want the structure rect which gaurantees
|
|
// a coordinate whose tile is occupied by this structure
|
|
|
|
WPoint wptExtent;
|
|
wptExtent.wx = WcFromTc(TcFromWc(m_wx) + m_pstruc->ctx);
|
|
wptExtent.wy = WcFromTc(TcFromWc(m_wy) + m_pstruc->cty);
|
|
|
|
pwpt->wx = WcTrunc(m_wx + (wptExtent.wx - m_wx) / 2) + kwcTileHalf;
|
|
pwpt->wy = WcTrunc(m_wy + (wptExtent.wy - m_wy) / 2) + kwcTileHalf;
|
|
|
|
TCoord txTest = TcFromWc(pwpt->wx);
|
|
TCoord tyTest = TcFromWc(pwpt->wy);
|
|
if (IsAccessible(txTest, tyTest))
|
|
return;
|
|
|
|
// Otherwise find a spot that is accessible
|
|
|
|
TCoord tx = TcFromWc(m_wx);
|
|
TCoord ty = TcFromWc(m_wy);
|
|
for (TCoord tyT = ty; tyT < ty + m_pstruc->cty; tyT++) {
|
|
for (TCoord txT = tx; txT < tx + m_pstruc->ctx; txT++) {
|
|
if (txT != txTest && tyT != tyTest) {
|
|
if (IsAccessible(txT, tyT)) {
|
|
pwpt->wx = WcFromTc(txT) + kwcTileHalf;
|
|
pwpt->wy = WcFromTc(tyT) + kwcTileHalf;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Couldn't find anything that is "accessible" unfortunately. Try to find something close by
|
|
// that is accessible
|
|
|
|
FindNearestFreeTile(TcFromWc(pwpt->wx), TcFromWc(pwpt->wy), pwpt, kbfStructure);
|
|
}
|
|
|
|
void StructGob::DrawSymbol(TBitmap *ptbm, DibBitmap *pbm, int xViewOrigin, int yViewOrigin)
|
|
{
|
|
int x = -xViewOrigin + PcFromWc(m_wx);
|
|
int y = -yViewOrigin + PcFromWc(m_wy);
|
|
|
|
Rect rcT;
|
|
rcT.FromWorldRect(&m_puntc->wrcUIBounds);
|
|
Size sizT;
|
|
ptbm->GetSize(&sizT);
|
|
ptbm->BltTo(pbm, x + (rcT.Width() - sizT.cx) / 2, y + (rcT.Height() - sizT.cy) / 2);
|
|
}
|
|
|
|
void StructGob::Draw(DibBitmap *pbm, int xViewOrigin, int yViewOrigin, int nLayer)
|
|
{
|
|
switch (nLayer) {
|
|
case knLayerMiniMap:
|
|
{
|
|
int iclr;
|
|
if (m_ff & kfGobSelected)
|
|
iclr = kiclrWhite;
|
|
else
|
|
iclr = GetSideColor(m_pplr->GetSide());
|
|
|
|
int cxy = gsim.GetMiniMapScale();
|
|
pbm->Fill(xViewOrigin + MmcFromWc(m_wx), yViewOrigin + MmcFromWc(m_wy), m_pstruc->ctx * cxy, m_pstruc->cty * cxy,
|
|
GetColor(iclr));
|
|
}
|
|
return;
|
|
|
|
case knLayerSymbols:
|
|
|
|
// only one symbol shows at a time. Low Power can alternate with repairing
|
|
// or with need credits, but repairing and need credits are mutually exclusive
|
|
|
|
UnitGob::Draw(pbm, xViewOrigin, yViewOrigin, nLayer);
|
|
if (m_wfUnit & kfUnitDrawNeedsPowerSymbol) {
|
|
DrawSymbol(s_ptbmNeedsPower, pbm, xViewOrigin, yViewOrigin);
|
|
} else {
|
|
if (m_wfUnit & kfUnitDrawRepairingSymbol) {
|
|
DrawSymbol(s_ptbmRepairing, pbm, xViewOrigin, yViewOrigin);
|
|
} else if (m_wfUnit & kfUnitDrawNeedCreditsSymbol) {
|
|
DrawSymbol(s_ptbmNeedCredits, pbm, xViewOrigin, yViewOrigin);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case knLayerSelection:
|
|
if ((m_ff & (kfGobSelected | kfGobBeingBuilt)) == kfGobBeingBuilt) {
|
|
Rect rcT;
|
|
rcT.FromWorldRect(&m_puntc->wrcUIBounds);
|
|
rcT.Offset(-xViewOrigin + PcFromWc(m_wx), -yViewOrigin + PcFromWc(m_wy));
|
|
DrawHealthIndicator(pbm, &rcT, m_fxHealth, m_puntc->GetArmorStrength());
|
|
return;
|
|
}
|
|
|
|
// fall through
|
|
|
|
default:
|
|
UnitGob::Draw(pbm, xViewOrigin, yViewOrigin, nLayer);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void StructGob::DefUpdate()
|
|
{
|
|
// If we're repairing we'll skip no updates, even if blocked on credits. If we're blocked
|
|
// on low power then we'll skip till we get to the next symbol flash. However, most of the time
|
|
// we will be none of these, so GetUpdateCount and do the math for cupdUntilFlash inside the tests
|
|
// for symbol situations rather than on every single update.
|
|
|
|
int cUpdates;
|
|
int cupdUntilFlash;
|
|
|
|
if ((m_puntc->wf & kfUntcNotifyPowerLowHigh) && m_pplr->IsPowerLow() && (m_ff & kfGobActive)) {
|
|
cUpdates = gsim.GetUpdateCount();
|
|
cupdUntilFlash = kcupdSymbolFlashRate - (cUpdates % kcupdSymbolFlashRate);
|
|
|
|
if (cupdUntilFlash == kcupdSymbolFlashRate) {
|
|
|
|
// Low Power will flash ON for odd intervals
|
|
|
|
if ((short)(cUpdates / kcupdSymbolFlashRate) & 1)
|
|
m_wfUnit |= kfUnitDrawNeedsPowerSymbol;
|
|
else
|
|
m_wfUnit &= ~kfUnitDrawNeedsPowerSymbol;
|
|
|
|
EnableOrDisableSymbolLayer();
|
|
if (gwfPerfOptions & kfPerfSymbolFlashing)
|
|
MarkRedraw();
|
|
}
|
|
m_unvl.MinSkip(cupdUntilFlash - 1);
|
|
}
|
|
|
|
// If repairing and sufficient credits are available
|
|
|
|
if (m_wfUnit & kfUnitRepairing) {
|
|
cUpdates = gsim.GetUpdateCount();
|
|
cupdUntilFlash = kcupdSymbolFlashRate - (cUpdates % kcupdSymbolFlashRate);
|
|
|
|
int nCreditsHave = m_pplr->GetCredits();
|
|
|
|
if (cupdUntilFlash == kcupdSymbolFlashRate) {
|
|
if (nCreditsHave > 0) {
|
|
|
|
// Repair will flash OFF for odd intervals
|
|
|
|
if ((short)(cUpdates / kcupdSymbolFlashRate) & 1)
|
|
m_wfUnit &= ~kfUnitDrawRepairingSymbol;
|
|
else
|
|
m_wfUnit |= kfUnitDrawRepairingSymbol;
|
|
|
|
} else {
|
|
m_wfUnit &= ~kfUnitDrawRepairingSymbol;
|
|
}
|
|
EnableOrDisableSymbolLayer();
|
|
if (gwfPerfOptions & kfPerfSymbolFlashing)
|
|
MarkRedraw();
|
|
}
|
|
|
|
fix fxArmorStrength = m_pstruc->GetArmorStrength();
|
|
if (m_fxHealth < fxArmorStrength) {
|
|
|
|
// n health unit costs m credits to repair
|
|
|
|
int nCreditsNeeded = knCreditsPerRepairUpdate;
|
|
|
|
// Take whatever is left (repair on a discount!)
|
|
|
|
if (nCreditsHave != 0 && nCreditsHave < nCreditsNeeded)
|
|
nCreditsNeeded = nCreditsHave;
|
|
|
|
if (nCreditsHave >= nCreditsNeeded) {
|
|
|
|
fix fxHealth = addfx(m_fxHealth, kfxHealthUnitsRepairedPerUpdate);
|
|
m_pplr->SetCredits(nCreditsHave - nCreditsNeeded, true, knConsumerRepair);
|
|
|
|
// make sure we're not in need credits mode
|
|
|
|
WaitingForCredits(false);
|
|
|
|
// At full strength? If so, stop repairing
|
|
|
|
if (fxHealth >= fxArmorStrength) {
|
|
fxHealth = fxArmorStrength;
|
|
m_wfUnit &= ~(kfUnitRepairing | kfUnitDrawRepairingSymbol);
|
|
Assert(!(m_wfUnit & kfUnitNeedCredits), "finished a repair without credits?");
|
|
EnableOrDisableSymbolLayer();
|
|
MarkRedraw();
|
|
}
|
|
SetHealth(fxHealth);
|
|
} else {
|
|
WaitingForCredits(true);
|
|
}
|
|
} else {
|
|
m_wfUnit &= ~(kfUnitRepairing | kfUnitDrawRepairingSymbol);
|
|
Assert(!(m_wfUnit & kfUnitNeedCredits), "finished a repair without credits?");
|
|
EnableOrDisableSymbolLayer();
|
|
MarkRedraw();
|
|
}
|
|
|
|
// when repairing we need every update to do our repair increments or
|
|
// to check for a fresh cash infusion (there is no credit change
|
|
// notification and because building can block w/o credits at zero it's
|
|
// too complex for now)
|
|
|
|
m_unvl.MinSkip();
|
|
}
|
|
|
|
if (m_wfUnit & kfUnitNeedCredits) {
|
|
cUpdates = gsim.GetUpdateCount();
|
|
cupdUntilFlash = kcupdSymbolFlashRate - (cUpdates % kcupdSymbolFlashRate);
|
|
|
|
if (cupdUntilFlash == kcupdSymbolFlashRate) {
|
|
|
|
// NeedCredits will flash OFF for odd intervals
|
|
|
|
if ((short)(cUpdates / kcupdSymbolFlashRate) & 1)
|
|
m_wfUnit &= ~kfUnitDrawNeedCreditsSymbol;
|
|
else
|
|
m_wfUnit |= kfUnitDrawNeedCreditsSymbol;
|
|
|
|
EnableOrDisableSymbolLayer();
|
|
if (gwfPerfOptions & kfPerfSymbolFlashing)
|
|
MarkRedraw();
|
|
}
|
|
|
|
// again, we have no credit infusion update, and so far all things that
|
|
// can block on credits get every update while credit consuming
|
|
|
|
m_unvl.MinSkip();
|
|
}
|
|
|
|
UnitGob::DefUpdate();
|
|
}
|
|
|
|
void StructGob::EnableOrDisableSymbolLayer()
|
|
{
|
|
dword ffOld = m_ff;
|
|
|
|
if (!IsAlly(gpplrLocal->GetSide()) && ((gpplrLocal->GetHandicap() & kfHcapShowEnemyResourceStatus) == 0))
|
|
m_wfUnit &= ~(kfUnitDrawNeedsPowerSymbol | kfUnitDrawNeedCreditsSymbol);
|
|
|
|
if (m_wfUnit & (kfUnitDrawRepairingSymbol | kfUnitDrawNeedsPowerSymbol | kfUnitDrawNeedCreditsSymbol)) {
|
|
m_ff |= kfGobLayerSymbols;
|
|
} else {
|
|
m_ff &= ~kfGobLayerSymbols;
|
|
}
|
|
|
|
// Changed?
|
|
|
|
if ((ffOld ^ m_ff) & kfGobLayerSymbols) {
|
|
if (gwfPerfOptions & kfPerfSymbolFlashing)
|
|
MarkRedraw();
|
|
}
|
|
}
|
|
|
|
bool StructGob::NeedsRepair()
|
|
{
|
|
return m_fxHealth < m_pstruc->GetArmorStrength();
|
|
}
|
|
|
|
bool StructGob::IsTakeoverable(Player *pplr)
|
|
{
|
|
// Can only takeover if the player has limit space
|
|
|
|
if (!ggobm.IsBelowLimit(knLimitStruct, pplr)) {
|
|
if (pplr == gpplrLocal)
|
|
ShowAlert(kidsBuildingLimitReached);
|
|
return false;
|
|
}
|
|
|
|
if (m_ff & kfGobActive)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void StructGob::Takeover(Player *pplr)
|
|
{
|
|
// Removes any player-specific influence this Structure has
|
|
|
|
Deactivate();
|
|
|
|
// Change owners
|
|
|
|
Assert(ggobm.IsBelowLimit(knLimitStruct, pplr));
|
|
ggobm.TrackGobCounts(this, false);
|
|
m_pplr->IncStructuresLost();
|
|
SetOwner(pplr);
|
|
m_pplr->IncEnemyStructuresKilled();
|
|
ggobm.TrackGobCounts(this, true);
|
|
|
|
// Applies any player-specific influence this Structure has
|
|
|
|
Activate();
|
|
|
|
// Reveal fog around this newly owned structure
|
|
|
|
if (pplr == gpplrLocal) {
|
|
WCoord wxView, wyView;
|
|
gsim.GetViewPos(&wxView, &wyView);
|
|
FogMap *pfogm = gsim.GetLevel()->GetFogMap();
|
|
WPoint wpt;
|
|
GetCenter(&wpt);
|
|
RevealPattern *prvlp = (RevealPattern *)(m_pstruc->wf & kfUntcLargeDefog ? grvlpLarge : grvlp);
|
|
pfogm->Reveal(TcFromWc(wpt.wx), TcFromWc(wpt.wy), prvlp, gpupdSim, wxView, wyView);
|
|
}
|
|
|
|
// force an update so it gets in sync on lowpower/credits etc
|
|
|
|
m_unvl.MinSkip();
|
|
|
|
// Redraw this part of the minimap
|
|
|
|
TRect trc;
|
|
GetTileRect(&trc);
|
|
gpmm->RedrawTRect(&trc);
|
|
}
|
|
|
|
// pplr is used for the replicator do do a custom override
|
|
|
|
void StructGob::WaitingForCredits(bool fNeed, bool fOverride, Player *pplr)
|
|
{
|
|
// override is for LoadState
|
|
|
|
if ((fNeed == ((m_wfUnit & kfUnitNeedCredits) == kfUnitNeedCredits)) && !fOverride)
|
|
return;
|
|
|
|
int cDelta;
|
|
if (fNeed) {
|
|
m_wfUnit |= kfUnitNeedCredits;
|
|
cDelta = 1;
|
|
} else {
|
|
m_wfUnit &= ~( kfUnitDrawNeedCreditsSymbol | kfUnitNeedCredits );
|
|
EnableOrDisableSymbolLayer();
|
|
if (gwfPerfOptions & kfPerfSymbolFlashing)
|
|
MarkRedraw();
|
|
cDelta = -1;
|
|
}
|
|
|
|
// track how many buildings need credits in the player so the local player can reflect
|
|
// that need in the UI
|
|
|
|
if (pplr == NULL)
|
|
pplr = m_pplr;
|
|
pplr->ModifyNeedCreditsCount(cDelta);
|
|
|
|
return;
|
|
}
|
|
|
|
void StructGob::OnPowerLowHigh()
|
|
{
|
|
if (!GetOwner()->IsPowerLow()) {
|
|
m_wfUnit &= ~kfUnitDrawNeedsPowerSymbol;
|
|
EnableOrDisableSymbolLayer();
|
|
MarkRedraw();
|
|
} else {
|
|
|
|
// force an update to start symbol flashing
|
|
|
|
m_unvl.MinSkip();
|
|
}
|
|
}
|
|
|
|
void StructGob::Repair(bool fOn)
|
|
{
|
|
if (fOn) {
|
|
// If already at max health, don't waste any more time
|
|
|
|
if (m_fxHealth == m_pstruc->GetArmorStrength())
|
|
return;
|
|
|
|
// toggle on repair state. Icon will be shown in update
|
|
|
|
m_wfUnit |= kfUnitRepairing;
|
|
|
|
if (m_pplr == gpplrLocal) {
|
|
if (m_pplr->GetCredits() > 0)
|
|
gsndm.PlaySfx(m_pstruc->sfxRepair);
|
|
}
|
|
|
|
} else {
|
|
m_wfUnit &= ~(kfUnitRepairing | kfUnitDrawRepairingSymbol | kfUnitDrawNeedCreditsSymbol);
|
|
WaitingForCredits(false);
|
|
if (m_pplr == gpplrLocal)
|
|
gsndm.PlaySfx(m_pstruc->sfxAbortRepair);
|
|
}
|
|
|
|
m_unvl.MinSkip();
|
|
}
|
|
|
|
bool StructGob::IsValidTarget(Gob *pgobTarget)
|
|
{
|
|
// most structures cannot attack. Towers can so they override this.
|
|
|
|
return false;
|
|
}
|
|
|
|
void StructGob::InitMenu(Form *pfrm)
|
|
{
|
|
ButtonControl *pbtn = (ButtonControl *)pfrm->GetControlPtr(kidcRepair);
|
|
pbtn->Enable(NeedsRepair());
|
|
pbtn->Show((m_wfUnit & kfUnitRepairing) == 0);
|
|
|
|
pbtn = (ButtonControl *)pfrm->GetControlPtr(kidcAbortRepair);
|
|
pbtn->Show((m_wfUnit & kfUnitRepairing) != 0);
|
|
}
|
|
|
|
void StructGob::OnMenuItemSelected(int idc)
|
|
{
|
|
switch (idc) {
|
|
|
|
// RepairCommand acts as a toggle
|
|
|
|
case kidcRepair:
|
|
case kidcAbortRepair:
|
|
gcmdq.Enqueue(kmidRepairCommand, m_gid);
|
|
break;
|
|
|
|
case kidcSelfDestruct:
|
|
if (PopupConfirmation("SELL BUILDING"))
|
|
gcmdq.Enqueue(kmidSelfDestructCommand, m_gid);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void StructGob::SelfDestruct(bool fRecycleValue)
|
|
{
|
|
// Refund half the value of the structure if recycling
|
|
|
|
if (fRecycleValue) {
|
|
m_pplr->SetCredits(m_pplr->GetCredits() + (m_pstruc->GetCost() / 2), true);
|
|
|
|
// Only do this for 'sold' structures. Structures lost while being built don't count as 'lost'
|
|
|
|
m_pplr->IncStructuresLost();
|
|
}
|
|
SetState(kstDying);
|
|
}
|
|
|
|
void StructGob::SetHealth(fix fxHealth)
|
|
{
|
|
UnitGob::SetHealth(fxHealth);
|
|
|
|
if (m_ff & kfGobBeingBuilt)
|
|
return;
|
|
|
|
if (m_fxHealth * 2 < m_pstruc->GetArmorStrength()) {
|
|
if (m_ani.GetStrip() != 1)
|
|
StartAnimation(&m_ani, 1, 0, m_ani.GetFlags());
|
|
} else {
|
|
if (m_ani.GetStrip() != 0)
|
|
StartAnimation(&m_ani, 0, 0, m_ani.GetFlags());
|
|
}
|
|
}
|
|
|
|
ScorchGob *StructGob::GetScorchGob()
|
|
{
|
|
if (ggobm.IsBelowLimit(knLimitScorch))
|
|
return new ScorchGob;
|
|
|
|
// Find the oldest scorch and delete it
|
|
|
|
int nSequenceOldest = 0x7fff;
|
|
ScorchGob *pgobScorchOldest = NULL;
|
|
for (Gob *pgobT = ggobm.GetFirstGob(); pgobT != NULL; pgobT = ggobm.GetNextGob(pgobT)) {
|
|
if (pgobT->GetType() != kgtScorch)
|
|
continue;
|
|
ScorchGob *pgobScorch = (ScorchGob *)pgobT;
|
|
int nSequence = pgobScorch->GetSequence();
|
|
if (nSequence < nSequenceOldest) {
|
|
nSequenceOldest = nSequence;
|
|
pgobScorchOldest = pgobScorch;
|
|
}
|
|
}
|
|
|
|
Assert(pgobScorchOldest != NULL);
|
|
if (pgobScorchOldest != NULL) {
|
|
ggobm.RemoveGob(pgobScorchOldest);
|
|
delete pgobScorchOldest;
|
|
}
|
|
|
|
return new ScorchGob;
|
|
}
|
|
|
|
int StructGob::ProcessStateMachineMessage(State st, Message *pmsg)
|
|
{
|
|
BeginStateMachine
|
|
OnMsg(kmidPowerLowHigh)
|
|
OnPowerLowHigh();
|
|
|
|
OnMsg(kmidHit)
|
|
int fxHealthPrev = m_fxHealth;
|
|
|
|
// apply damage
|
|
|
|
fix fxDamage = itofx(pmsg->Hit.nDamage);
|
|
if (m_pplr->GetHandicap() & kfHcapIncreasedArmor)
|
|
fxDamage = (fix)mulfx(fxDamage, (itofx(knDecreasedDamagePercent) / 100));
|
|
SetHealth(subfx(m_fxHealth, fxDamage));
|
|
if (m_fxHealth <= 0) {
|
|
Player *pplr = gplrm.GetPlayer(pmsg->Hit.sideAssailant);
|
|
pplr->IncEnemyStructuresKilled();
|
|
m_pplr->IncStructuresLost();
|
|
|
|
SetState(kstDying);
|
|
|
|
} else {
|
|
ShowDamageIndicator();
|
|
|
|
// If health is dipping into the severely damaged zone, fire off
|
|
// a couple smoke animations.
|
|
|
|
if (fxHealthPrev * 2 >= m_pstruc->GetArmorStrength() && m_fxHealth * 2 < m_pstruc->GetArmorStrength()) {
|
|
|
|
// Play damaged sound
|
|
|
|
gsndm.PlaySfx(m_pstruc->sfxDamaged);
|
|
|
|
if (gsim.GetTickCount() - m_tLastSmoke > 200) {
|
|
int cSmokes = (GetRandom() % 3) + 2;
|
|
if (m_pstruc->ctx == 1 || m_pstruc->cty == 1)
|
|
cSmokes /= 2;
|
|
for (int i = 0; i < cSmokes; i++)
|
|
gsmm.SendDelayedMsg(kmidSpawnSmoke, GetRandom() & 63, m_gid, m_gid);
|
|
|
|
m_tLastSmoke = gsim.GetTickCount();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remember that a structure has been attacked
|
|
|
|
m_pplr->SetFlags(m_pplr->GetFlags() | kfPlrStructureAttacked);
|
|
|
|
// Notify nearby allies that we've been hit! CONSIDER: pass in an expanded range?
|
|
|
|
NotifyNearbyAlliesOfHit(pmsg->Hit.gidAssailant);
|
|
|
|
OnMsg(kmidSpawnSmoke)
|
|
if (gwfPerfOptions & kfPerfSmoke) {
|
|
if (ggobm.IsBelowLimit(knLimitSupport)) {
|
|
SmokeGob *pgob = new SmokeGob((GetRandom() & 3) + 4);
|
|
Assert(pgob != NULL, "out of memory!");
|
|
if (pgob != NULL) {
|
|
WCoord wcx = WcFromTc(m_pstruc->ctx);
|
|
WCoord wcy = WcFromTc(m_pstruc->cty) - kwcTileHalf;
|
|
WCoord wdx = GetRandom() % wcx;
|
|
WCoord wdy = (GetRandom() % wcy) + WcFromTile16ths(12);
|
|
WCoord wxSmoke = m_wx + wdx;
|
|
WCoord wySmoke = m_wy + wdy;
|
|
|
|
Size wsizMap;
|
|
gsim.GetLevel()->GetTileMap()->GetTCoordMapSize(&wsizMap);
|
|
wsizMap.cx = WcFromTc(wsizMap.cx);
|
|
wsizMap.cy = WcFromTc(wsizMap.cy);
|
|
|
|
if (wxSmoke < 0)
|
|
wxSmoke = 0;
|
|
if (wxSmoke >= wsizMap.cx)
|
|
wxSmoke = wsizMap.cx - 1;
|
|
if (wySmoke >= wsizMap.cy)
|
|
wySmoke = wsizMap.cy - 1;
|
|
|
|
if (!pgob->Init(wxSmoke, wySmoke, m_gid))
|
|
delete pgob;
|
|
}
|
|
}
|
|
}
|
|
|
|
OnMsg(kmidSelfDestructCommand)
|
|
|
|
// assume we only get this message when user has
|
|
// initiated a recycle. Game can call the method.
|
|
SelfDestruct();
|
|
|
|
OnMsg(kmidRepairCommand)
|
|
|
|
// Toggle repairing
|
|
|
|
Repair(!(m_wfUnit & kfUnitRepairing));
|
|
|
|
OnMsg(kmidDelete)
|
|
Assert("Shouldn't receive kmidDelete when not in kstDying state");
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
State(kstBeingBuilt)
|
|
OnEnter
|
|
m_ff |= kfGobLayerSelection;
|
|
|
|
OnExit
|
|
m_ff &= ~kfGobLayerSelection;
|
|
|
|
OnMsg(kmidBuildComplete)
|
|
Activate();
|
|
SetState(kstIdle);
|
|
|
|
OnUpdate
|
|
// Don't advance animation
|
|
|
|
DefUpdate();
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
State(kstIdle)
|
|
OnEnter
|
|
// Play idle animation, chosen based on the health of the Structure
|
|
|
|
int nStrip;
|
|
if (m_fxHealth == 0)
|
|
nStrip = 2; // destroyed
|
|
else if (m_fxHealth * 2 < m_pstruc->GetArmorStrength())
|
|
nStrip = 1; // damaged
|
|
else
|
|
nStrip = 0; // healthy
|
|
StartAnimation(&m_ani, nStrip, 0, kfAniIgnoreFirstAdvance | kfAniLoop);
|
|
|
|
OnUpdate
|
|
AdvanceAnimation(&m_ani);
|
|
DefUpdate();
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
State(kstDying)
|
|
OnEnter
|
|
// if you're destroyed during building it's possible to get here unactivated
|
|
|
|
if (m_ff & kfGobActive)
|
|
Deactivate();
|
|
m_ff ^= kfGobLayerDepthSorted | kfGobLayerSurfaceDecal;
|
|
|
|
gsndm.PlaySfx(m_pstruc->sfxDestroyed);
|
|
StartAnimation(&m_ani, 2, 0, kfAniIgnoreFirstAdvance); // UNDONE: hardcoded
|
|
|
|
// dont remove ourselves from the "occupied" map until it's all over
|
|
// so troops can't run across this freshly destroyed area while it's hot (they have cheap boots)
|
|
|
|
gsmm.SendDelayedMsg(kmidDelete, 800, m_gid, m_gid);
|
|
|
|
// Show a huge explosion!
|
|
|
|
WCoord wcx = WcFromTc(m_pstruc->ctx);
|
|
WCoord wcy = WcFromTc(m_pstruc->cty) + kwcTileHalf;
|
|
Gob *pgobExpl = CreateAnimGob(m_wx + (wcx / 2), m_wy + (wcy / 2), kfAnmDeleteWhenDone | kfAnmSmokeFireLayer, NULL,
|
|
s_panidStructureExplosion);
|
|
if (pgobExpl != NULL)
|
|
pgobExpl->SetOwner(m_pplr);
|
|
|
|
// Show some smoke (in the near future)
|
|
|
|
int cSmokes = (GetRandom() % 3) + 1;
|
|
int i;
|
|
for (i = 0; i < cSmokes; i++)
|
|
gsmm.SendDelayedMsg(kmidSpawnSmoke, GetRandom() & 63, m_gid, m_gid);
|
|
|
|
// Lay down a random smattering of scorch marks
|
|
|
|
if (gwfPerfOptions & kfPerfScorchMarks) {
|
|
int cScorches = (GetRandom() & 3) + 1;
|
|
WCoord wcxScorchable = WcFromTc(m_pstruc->ctx);
|
|
WCoord wcyScorchable = WcFromTc(m_pstruc->cty);
|
|
|
|
Size wsizMap;
|
|
gsim.GetLevel()->GetTileMap()->GetTCoordMapSize(&wsizMap);
|
|
wsizMap.cx = WcFromTc(wsizMap.cx);
|
|
wsizMap.cy = WcFromTc(wsizMap.cy);
|
|
|
|
for (i = 0; i < cScorches; i++) {
|
|
|
|
// Pick a random scorch type to lay down but make sure it is
|
|
// one that is smaller than the structure leaving it.
|
|
|
|
Size sizScorch;
|
|
WCoord wcxScorch, wcyScorch;
|
|
int nScorch;
|
|
do {
|
|
nScorch = GetRandom() % (sizeof(gaptbmScorches) / sizeof(TBitmap *));
|
|
gaptbmScorches[nScorch]->GetSize(&sizScorch);
|
|
wcxScorch = WcFromUpc(sizScorch.cx);
|
|
wcyScorch = WcFromUpc(sizScorch.cy);
|
|
} while (wcxScorch > wcxScorchable || wcyScorch > wcyScorchable);
|
|
|
|
WCoord wxoff = GetRandom() % (wcxScorchable - wcxScorch + 1);
|
|
WCoord wyoff = GetRandom() % (wcyScorchable - wcyScorch + 1);
|
|
|
|
ScorchGob *pgobScorch = GetScorchGob();
|
|
if (pgobScorch != NULL) {
|
|
// Make sure the scorch stays within the map bounds
|
|
|
|
WCoord wxScorch = m_wx + wxoff + (wcxScorch / 2);
|
|
WCoord wyScorch = m_wy + wyoff + wcyScorch;
|
|
if (wxScorch < 0)
|
|
wxScorch = 0;
|
|
if (wxScorch >= wsizMap.cx)
|
|
wxScorch = wsizMap.cx - 1;
|
|
if (wyScorch >= wsizMap.cy)
|
|
wyScorch = wsizMap.cy - 1;
|
|
|
|
pgobScorch->Init(wxScorch, wyScorch, nScorch);
|
|
}
|
|
}
|
|
}
|
|
|
|
OnUpdate
|
|
AdvanceAnimation(&m_ani);
|
|
|
|
OnMsg(kmidDelete)
|
|
Delete();
|
|
return knDeleted;
|
|
|
|
// Do this so the kmidSpawnSmoke message will bubble up to the 'global' handler above
|
|
|
|
OnMsg(kmidSpawnSmoke)
|
|
return knNotHandled;
|
|
|
|
// Eat all other messages (e.g., kmidHit, kmidNearbyAllyHit)
|
|
|
|
DiscardMsgs
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
EndStateMachine
|
|
}
|
|
|
|
//
|
|
// SmokeGob implementation
|
|
//
|
|
|
|
SmokeGob::SmokeGob(int cLoops)
|
|
{
|
|
m_cLoops = cLoops;
|
|
}
|
|
|
|
bool SmokeGob::Init(WCoord wx, WCoord wy, StateMachineId smidNotify)
|
|
{
|
|
return AnimGob::Init(wx, wy, kfAnmLoop | kfAnmSmokeFireLayer, NULL, s_panidBigSmoke, 0, smidNotify, NULL);
|
|
}
|
|
|
|
#define knVerSmokeGobState 1
|
|
bool SmokeGob::LoadState(Stream *pstm)
|
|
{
|
|
byte nVer = pstm->ReadByte();
|
|
if (nVer != knVerSmokeGobState)
|
|
return false;
|
|
m_cLoops = pstm->ReadWord();
|
|
m_ani.Init(s_panidBigSmoke);
|
|
m_ani.LoadState(pstm);
|
|
m_smidNotify = pstm->ReadWord();
|
|
m_wfAnm = kfAnmLoop | kfAnmSmokeFireLayer;
|
|
return AnimGob::LoadState(pstm);
|
|
}
|
|
|
|
bool SmokeGob::SaveState(Stream *pstm)
|
|
{
|
|
pstm->WriteByte(knVerSmokeGobState);
|
|
pstm->WriteWord(m_cLoops);
|
|
m_ani.SaveState(pstm);
|
|
pstm->WriteWord(m_smidNotify);
|
|
return AnimGob::SaveState(pstm);
|
|
}
|
|
|
|
bool SmokeGob::IsSavable()
|
|
{
|
|
// Because AnimGob is not savable, we need to override
|
|
|
|
return true;
|
|
}
|
|
|
|
GobType SmokeGob::GetType()
|
|
{
|
|
return kgtSmoke;
|
|
}
|
|
|
|
// After the initial set (smoke growing) completes, draw the
|
|
// cycle a few times then destroy self.
|
|
|
|
bool SmokeGob::OnStripDone()
|
|
{
|
|
if (m_ani.GetStrip() == 0) {
|
|
SetAnimationStrip(&m_ani, 1);
|
|
} else {
|
|
m_cLoops--;
|
|
if (m_cLoops < 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace wi
|