hostile-takeover/game/TriggerActions.cpp
2016-08-31 23:55:30 -04:00

1380 lines
29 KiB
C++

#include "ht.h"
namespace wi {
bool Trigger::LoadAction(IniReader *pini, FindProp *pfind)
{
char sz[1000]; // Must be big to hold large Ecom texts
sz[0] = 0;
int nAction;
int cArgs = pini->GetPropertyValue(pfind, "%d,%s", &nAction, sz);
if (cArgs == 0)
return false;
Assert(strlen(sz) + 1 < sizeof(sz));
TriggerAction *pactn;
switch (nAction) {
case knPreserveTriggerTriggerAction:
pactn = new PreserveTriggerAction();
break;
case knWaitTriggerAction:
pactn = new WaitAction();
break;
case knCenterViewTriggerAction:
pactn = new CenterViewAction();
break;
case knSetNextMissionTriggerAction:
pactn = new SetNextMissionAction();
break;
case knEndMissionTriggerAction:
pactn = new EndMissionAction();
break;
case knEcomTriggerAction:
pactn = new EcomAction();
break;
case knSetAllowedUnitsTriggerAction:
pactn = new SetAllowedUnitsAction();
break;
case knAlliesTriggerAction:
pactn = new AlliesAction();
break;
case knSetObjectiveTriggerAction:
pactn = new SetObjectiveAction();
break;
case knSetSwitchTriggerAction:
pactn = new SetSwitchAction();
break;
case knDefogAreaTriggerAction:
pactn = new DefogAreaAction();
break;
case knCreateUnitGroupTriggerAction:
pactn = new CreateUnitGroupAction();
break;
case knHuntTriggerAction:
pactn = new HuntAction();
break;
case knCreateRandomUnitGroupTriggerAction:
pactn = new CreateRandomUnitGroupAction();
break;
case knStartCountdownTimerTriggerAction:
pactn = new StartCountdownAction();
break;
case knModifyCountdownTimerTriggerAction:
pactn = new ModifyCountdownAction();
break;
case knRepairTriggerAction:
pactn = new RepairAction();
break;
case knEnableReplicatorTriggerAction:
pactn = new EnableReplicatorAction();
break;
case knModifyCreditsTriggerAction:
pactn = new ModifyCreditsAction();
break;
case knMoveUnitsInAreaTriggerAction:
pactn = new MoveUnitsInAreaAction();
break;
case knSetFormalObjectiveTextTriggerAction:
pactn = new SetFormalObjectiveTextAction();
break;
case knSetFormalObjectiveStatusTriggerAction:
pactn = new SetFormalObjectiveStatusAction();
break;
case knSetFormalObjectiveInfoTriggerAction:
pactn = new SetFormalObjectiveInfoAction();
break;
case knShowObjectivesTriggerAction:
pactn = new ShowObjectivesAction();
break;
case knCutSceneTriggerAction:
pactn = new CutSceneAction();
break;
case knJumpToMissionTriggerAction:
pactn = new JumpToMissionAction();
break;
case knModifyPvarTriggerAction:
pactn = new ModifyPvarAction();
break;
case knSetPvarTextTriggerAction:
pactn = new SetPvarTextAction();
break;
case knShowAlertTriggerAction:
pactn = new ShowAlertAction();
break;
case knSetAllowedUpgradesTriggerAction:
pactn = new SetAllowedUpgradesAction();
break;
case knSetUpgradesTriggerAction:
pactn = new SetUpgradesAction();
break;
#ifdef UNDONE
case knMoveUnitTriggerAction:
pactn = new MoveUnitAction();
break;
case knSetPlayerControlsTriggerAction:
pactn = new SetPlayerControlsAction();
break;
case knPanViewAction:
pactn = new PanViewTriggerAction();
break;
case knTargetUnitTriggerAction:
pactn = new TargetUnitAction();
break;
#endif
default:
Assert(false);
break;
}
// Init it, error if that failed
Assert(pactn != NULL, "out of memory!");
if (!pactn->Init(sz)) {
delete pactn;
return false;
}
// Link it in last
TriggerAction **ppactn = &m_pactn;
while ((*ppactn) != NULL)
ppactn = &((*ppactn)->m_pactnNext);
*ppactn = pactn;
return true;
}
// Action base class implementation
TriggerAction::TriggerAction()
{
m_pactnNext = NULL;
}
// Just here to be overridden by derived classes
TriggerAction::~TriggerAction()
{
}
bool TriggerAction::Init(char *psz)
{
return true;
}
bool TriggerAction::LoadState(Stream *pstm)
{
return true;
}
bool TriggerAction::SaveState(Stream *pstm)
{
return true;
}
// PreserveTriggerAction
bool PreserveTriggerAction::Perform(Trigger *ptgr, Side side)
{
ptgr->Arm(side);
return true;
}
// WaitAction
WaitAction::WaitAction()
{
memset(m_afWaitingSide, 0, sizeof(m_afWaitingSide));
}
#define knVerWaitActionState 2
bool WaitAction::LoadState(Stream *pstm)
{
byte nVer = pstm->ReadByte();
if (nVer != knVerWaitActionState)
return false;
pstm->ReadBytesRLE((byte *)m_atStartSide, sizeof(m_atStartSide));
pstm->ReadBytesRLE((byte *)m_afWaitingSide, sizeof(m_afWaitingSide));
return pstm->IsSuccess();
}
bool WaitAction::SaveState(Stream *pstm)
{
pstm->WriteByte(knVerWaitActionState);
pstm->WriteBytesRLE((byte *)m_atStartSide, sizeof(m_atStartSide));
pstm->WriteBytesRLE((byte *)m_afWaitingSide, sizeof(m_afWaitingSide));
return pstm->IsSuccess();
}
bool WaitAction::Init(char *psz)
{
int cSecs;
if (!ParseNumber(&psz, &cSecs))
return false;
m_ctWait = cSecs * 100;
return true;
}
bool WaitAction::Perform(Trigger *ptgr, Side side)
{
// If in the middle of waiting, check if the wait is over.
long t = gsim.GetTickCount();
if (m_afWaitingSide[side]) {
// Wait over? If so, return true
if (t - m_atStartSide[side] >= m_ctWait) {
m_afWaitingSide[side] = false;
return true;
}
// Wait not over, return false
return false;
}
// Starting the wait, assume wait not over
m_afWaitingSide[side] = true;
m_atStartSide[side] = (dword)t;
return false;
}
#ifdef DEBUG_HELPERS
char *WaitAction::ToString()
{
// UNDONE: remove hardcode kside1
sprintf(s_szDebugHelpers, "Wait %d [%d]", m_ctWait / 100,
(gsim.GetTickCount() - m_atStartSide[kside1]) / 100);
return s_szDebugHelpers;
}
#endif
// CenterViewAction
// Immediately center the View on the specified Area
bool CenterViewAction::Init(char *psz)
{
return ParseArea(&psz, &m_nArea);
}
bool CenterViewAction::Perform(Trigger *ptgr, Side side)
{
// Only bother for the local player
if (side != gpplrLocal->GetSide())
return true;
TRect trc;
ggobm.GetAreaRect(m_nArea, &trc, side);
WRect wrc;
wrc.FromTileRect(&trc);
Rect rcSimUI;
ggame.GetSimUIForm()->GetRect(&rcSimUI);
gsim.SetViewPos((wrc.left + wrc.Width() / 2) - (WcFromPc(rcSimUI.Width()) / 2),
(wrc.top + wrc.Height() / 2) - (WcFromPc(rcSimUI.Height()) / 2), true);
return true;
}
// SetNextMissionAction
bool SetNextMissionAction::Init(char *psz)
{
strncpyz(m_szLevel, psz, sizeof(m_szLevel));
return true;
}
bool SetNextMissionAction::Perform(Trigger *ptgr, Side side)
{
// Only bother for the local player
if (side != gpplrLocal->GetSide())
return true;
if (strcmp(m_szLevel, "[none]") == 0)
ggame.SetNextLevel("");
else
ggame.SetNextLevel(m_szLevel);
return true;
}
// EndMissionAction
bool EndMissionAction::Init(char *psz)
{
return ParseNumber(&psz, &m_nWinLose);
}
bool EndMissionAction::Perform(Trigger *ptgr, Side side)
{
Assert(m_pactnNext == NULL, "Action attempted after End Mission");
#ifdef DEBUG
if (m_nWinLose == knWinLoseTypeWin)
Assert(strcmp(ggame.GetNextLevel(), gsim.GetLevel()->GetFilename()) != 0, "No Next Mission specified");
#endif
// Treat local and remote players differently. In a single player game we
// don't allow non-local players to perform EndMission actions (bad
// scripting).
if (side != gpplrLocal->GetSide() && !gfMultiplayer)
return true;
// In a multiplayer game we want to notify the local player of the remote
// player's demise. We also want to mark the 'EndMission'ed player as an
// Observer and put them into observing 'mode'.
if (gfMultiplayer) {
Player *pplr = gplrm.GetPlayer(side);
if ((pplr->GetFlags() & (kfPlrUnfulfilled | kfPlrObserver)) == 0) {
// Show alert
char szT[100];
sprintf(szT, m_nWinLose == knWinLoseTypeWin ? "%s is victorious!" : "%s has been defeated!", pplr->GetName());
ShowAlert(szT);
// This player is now out, and an observer
pplr->SetFlags(pplr->GetFlags() | kfPlrObserver | kfPlrComputer);
if (pplr == gpplrLocal) {
// In MP, TriggerMgr::Update() is called synchronously in the
// simulation, so it is not possible to execute modal loops
// such the end mission forms. For this reason, this part is
// done asynchronously.
Event evt;
memset(&evt, 0, sizeof(evt));
if (m_nWinLose == knWinLoseTypeWin) {
evt.eType = mpEndMissionWinEvent;
} else {
evt.eType = mpEndMissionLoseEvent;
}
evt.dw = side;
gevm.PostEvent(&evt);
} else {
// A non-local player lost. Have the local player check to
// see if it won.
if (m_nWinLose == knWinLoseTypeLose) {
Event evt;
memset(&evt, 0, sizeof(evt));
evt.eType = checkGameOverEvent;
evt.dw = pplr->GetId();
gevm.PostEvent(&evt);
}
}
}
return true;
}
// Keep any more triggers from executing
// NOTE: doesn't keep subsequent actions in the same trigger from executing
gsim.GetLevel()->GetTriggerMgr()->Enable(false);
// Bring up the Objectives screen
Event evt;
memset(&evt, 0, sizeof(evt));
evt.eType = gameOverEvent;
#ifdef STRESS
if (gfStress) {
evt.dw = knGoAbortLevel;
} else {
#endif
gsim.Pause(true);
evt.dw = gplrm.GetPlayer(side)->ShowObjectives(m_nWinLose == knWinLoseTypeWin ? ksoWinSummary : ksoLoseSummary);
gsim.Pause(false);
#ifdef STRESS
}
#endif
// UNDONE: post this event even if the app is stopping?
gevm.PostEvent(&evt);
// if the app was exited while the mission summary was up, return to it.
return !gevm.IsAppStopping();
}
void EndMissionAction::OnMPEndMissionActionEvent(int nWinLose, Side side) {
Player *pplr = gplrm.GetPlayer(side);
if (pplr != gpplrLocal) {
return;
}
pplr->ShowObjectives(nWinLose == knWinLoseTypeWin ?
ksoWinSummary : ksoLoseSummary);
if (ggame.AskObserveGame()) {
// Cause modal forms to cancel such as build forms, help, options,
// etc.
gevm.PostEvent(cancelModeEvent);
} else {
Event evt;
memset(&evt, 0, sizeof(evt));
evt.eType = gameOverEvent;
evt.dw = knGoAbortLevel;
gevm.PostEvent(&evt);
}
}
// EcomAction
EcomAction::~EcomAction()
{
delete[] m_pszMessage;
}
bool EcomAction::Init(char *psz)
{
m_pszMessage = NULL;
int nParsed;
if (!ParseNumber(&psz, &m_nBackground))
return false;
if (!ParseNumber(&psz, &nParsed))
return false;
m_fMore = (nParsed == knMoreCloseTypeMore);
if (!ParseNumber(&psz, &m_nCharFrom)) {
Assert(false); // most likely don't have new ecoms in data file
return false;
}
if (!ParseNumber(&psz, &m_nCharTo))
return false;
m_pszMessage = new char[strlen(psz) + 1];
if (m_pszMessage != NULL)
strcpy(m_pszMessage, psz);
return true;
}
bool EcomAction::Perform(Trigger *ptgr, Side side)
{
// Does nothing in multiplayer
if (gfMultiplayer) {
return true;
}
// Let any side bring up an ecom. Mission Authors request
#if 0
// Only bother for the local player
if (side != gpplrLocal->GetSide())
return true;
#endif
// if there's a build form up, pass and try again later
if (gpmfrmm->EcomSuppressed())
return false;
// Bring up the Ecom
gsim.Pause(true);
//fixme
if (!gfMultiplayer)
Ecom(m_nCharFrom, m_nCharTo, m_pszMessage, m_nBackground, m_fMore);
gsim.Pause(false);
// if the app was exited while the ecom was up, return to this ecom.
return !gevm.IsAppStopping();
}
// SetAllowedUnitsAction
bool SetAllowedUnitsAction::Init(char *psz)
{
int nCaSideMask;
if (!ParseNumber(&psz, &nCaSideMask))
return false;
m_wfCaSideMask = nCaSideMask;
return ParseUnitMask(&psz, &m_um);
}
bool SetAllowedUnitsAction::Perform(Trigger *ptgr, Side side)
{
Player *applr[kcSides];
int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr);
for (int n = 0; n < cplrs; n++)
applr[n]->SetAllowedUnits(m_um);
return true;
}
// SetAllowedUpgradesAction
bool SetAllowedUpgradesAction::Init(char *psz)
{
int nCaSideMask;
if (!ParseNumber(&psz, &nCaSideMask))
return false;
m_wfCaSideMask = nCaSideMask;
return ParseUpgradeMask(&psz, &m_upgm);
}
bool SetAllowedUpgradesAction::Perform(Trigger *ptgr, Side side)
{
Player *applr[kcSides];
int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr);
for (int n = 0; n < cplrs; n++)
applr[n]->SetAllowedUpgrades(m_upgm);
return true;
}
// SetUpgradesAction
bool SetUpgradesAction::Init(char *psz)
{
int nCaSideMask;
if (!ParseNumber(&psz, &nCaSideMask))
return false;
m_wfCaSideMask = nCaSideMask;
return ParseUpgradeMask(&psz, &m_upgm);
}
bool SetUpgradesAction::Perform(Trigger *ptgr, Side side)
{
Player *applr[kcSides];
int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr);
word wfUpgrade = 0;
if (m_upgm & kupgmAdvancedHRC)
wfUpgrade |= kfUpgradeHrc;
if (m_upgm & kupgmAdvancedVTS)
wfUpgrade |= kfUpgradeVts;
for (int n = 0; n < cplrs; n++)
applr[n]->SetUpgrades(wfUpgrade);
return true;
}
// AlliesAction
bool AlliesAction::Init(char *psz)
{
int nCaSideMask;
if (!ParseNumber(&psz, &nCaSideMask))
return false;
m_wfCaSideMaskA = nCaSideMask;
if (!ParseNumber(&psz, &nCaSideMask))
return false;
m_wfCaSideMaskB = nCaSideMask;
return true;
}
bool AlliesAction::Perform(Trigger *ptgr, Side side)
{
SideMask sidmAllies = GetSideMaskFromCaSideMask(side, m_wfCaSideMaskB);
Player *applr[kcSides];
int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMaskA, applr);
gplrm.SetAllies(applr, cplrs, sidmAllies);
return true;
}
// SetObjectiveAction
SetObjectiveAction::SetObjectiveAction()
{
m_szObjective = NULL;
}
SetObjectiveAction::~SetObjectiveAction()
{
if (m_szObjective != NULL)
gmmgr.FreePtr(m_szObjective);
}
bool SetObjectiveAction::Init(char *psz)
{
int nCaSideMask;
if (!ParseNumber(&psz, &nCaSideMask))
return false;
m_wfCaSideMask = nCaSideMask;
m_szObjective = (char *)gmmgr.AllocPtr(strlen(psz) + 1);
gmmgr.WritePtr(m_szObjective, 0, psz, strlen(psz) + 1);
return true;
}
bool SetObjectiveAction::Perform(Trigger *ptgr, Side side)
{
Player *applr[kcSides];
int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr);
for (int n = 0; n < cplrs; n++)
applr[n]->SetObjective(m_szObjective);
return true;
}
// SetSwitchAction
bool SetSwitchAction::Init(char *psz)
{
if (!ParseNumber(&psz, &m_iSwitch))
return false;
Assert(m_iSwitch < kcSwitchMax);
int nOnOff;
if (!ParseNumber(&psz, &nOnOff))
return false;
m_fOn = nOnOff == 1;
return true;
}
bool SetSwitchAction::Perform(Trigger *ptgr, Side side)
{
gsim.GetLevel()->GetTriggerMgr()->SetSwitch(m_iSwitch, m_fOn);
return true;
}
// DefogAreaAction
bool DefogAreaAction::Init(char *psz)
{
return ParseArea(&psz, &m_nArea);
}
bool DefogAreaAction::Perform(Trigger *ptgr, Side side)
{
#if 0
// Only bother for the local player
if (side != gpplrLocal->GetSide())
return true;
#endif
TRect trc;
Level *plvl = gsim.GetLevel();
ggobm.GetAreaRect(m_nArea, &trc, side);
WCoord wxView, wyView;
gsim.GetViewPos(&wxView, &wyView);
plvl->GetFogMap()->Reveal(&trc, gpupdSim, wxView, wyView);
return true;
}
// CreateUnitGroupAction
bool CreateUnitGroupAction::Init(char *psz)
{
return ParseNumber(&psz, &m_nUnitGroup);
}
bool CreateUnitGroupAction::Perform(Trigger *ptgr, Side side)
{
gsim.GetLevel()->GetUnitGroupMgr()->ActivateUnitGroup(m_nUnitGroup);
return true;
}
// CreateRandomUnitGroupAction
bool CreateRandomUnitGroupAction::Init(char *psz)
{
return true;
}
bool CreateRandomUnitGroupAction::Perform(Trigger *ptgr, Side side)
{
gsim.GetLevel()->GetUnitGroupMgr()->ActivateRandomUnitGroup();
return true;
}
// HuntAction
bool HuntAction::Init(char *psz)
{
if (!ParseUnitMask(&psz, &m_um1))
return false;
int nCaSideMask;
if (!ParseNumber(&psz, &nCaSideMask))
return false;
m_wfCaSideMask1 = nCaSideMask;
if (!ParseUnitMask(&psz, &m_um2))
return false;
if (!ParseNumber(&psz, &nCaSideMask))
return false;
m_wfCaSideMask2 = nCaSideMask;
return true;
}
// UNDONE: since hunting units start by producing a path to the target,
// path creation is expensive, and there may be a lot of hunting units
// set things up so that the hunt actions are performed over time.
// E.g., have 5 units hunt per update.
bool HuntAction::Perform(Trigger *ptgr, Side side)
{
// UNDONE: enumerate all units that match SideMask1 & UnitMask1 and
// direct them to hunt units that match SideMask2 & UnitMask2
// Make a list of valid targets
// Loop through all
return true;
}
// StartCountdownAction
StartCountdownAction::StartCountdownAction()
{
m_szCountdown = NULL;
}
StartCountdownAction::~StartCountdownAction()
{
if (m_szCountdown != NULL)
gmmgr.FreePtr(m_szCountdown);
}
bool StartCountdownAction::Init(char *psz)
{
if (!ParseNumber(&psz, &m_nSecs))
return false;
m_szCountdown = (char *)gmmgr.AllocPtr(strlen(psz) + 1);
gmmgr.WritePtr(m_szCountdown, 0, psz, strlen(psz) + 1);
return true;
}
bool StartCountdownAction::Perform(Trigger *ptgr, Side side)
{
CountdownTimer *pcdt = gsim.GetLevel()->GetTriggerMgr()->GetCountdownTimer();
Assert(pcdt != 0);
pcdt->SetTimer(m_nSecs, m_szCountdown);
pcdt->StartTimer(true);
return true;
}
// ModifyCountdownAction
bool ModifyCountdownAction::Init(char *psz)
{
if (!ParseNumber(&psz, &m_nAction))
return false;
return true;
}
bool ModifyCountdownAction::Perform(Trigger *ptgr, Side side)
{
CountdownTimer *pct = gsim.GetLevel()->GetTriggerMgr()->GetCountdownTimer();
switch (m_nAction) {
case knModifyCountdownTypeStop:
pct->StartTimer(false);
break;
case knModifyCountdownTypeResume:
pct->StartTimer(true);
break;
case knModifyCountdownTypeHide:
pct->ShowTimer(false);
break;
case knModifyCountdownTypeShow:
pct->ShowTimer(true);
break;
default:
Assert(false);
}
return true;
}
#ifdef DEBUG_HELPERS
char *ModifyCountdownAction::ToString()
{
switch (m_nAction) {
case knModifyCountdownTypeStop:
sprintf(s_szDebugHelpers, "Stop Countdown");
break;
case knModifyCountdownTypeResume:
sprintf(s_szDebugHelpers, "Resume Countdown");
break;
case knModifyCountdownTypeHide:
sprintf(s_szDebugHelpers, "Hide Countdown");
break;
case knModifyCountdownTypeShow:
sprintf(s_szDebugHelpers, "Show Countdown");
break;
default:
Assert(false);
}
return s_szDebugHelpers;
}
#endif
// RepairAction
bool RepairAction::Init(char *psz)
{
int nCaSideMask;
if (!ParseNumber(&psz, &nCaSideMask))
return false;
m_wfCaSideMask = nCaSideMask;
int nOnOff;
if (!ParseNumber(&psz, &nOnOff))
return false;
m_fOn = nOnOff == 1;
return true;
}
bool RepairAction::Perform(Trigger *ptgr, Side side)
{
Player *applr[kcSides];
int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr);
for (int n = 0; n < cplrs; n++)
applr[n]->Repair(m_fOn);
return true;
}
// EnableReplicatorAction
bool EnableReplicatorAction::Init(char *psz)
{
int nCaSideMask;
if (!ParseNumber(&psz, &nCaSideMask))
return false;
m_wfCaSideMask = nCaSideMask;
int nOnOff;
if (!ParseNumber(&psz, &nOnOff))
return false;
m_fOn = nOnOff == 1;
return true;
}
bool EnableReplicatorAction::Perform(Trigger *ptgr, Side side)
{
SideMask sidm = GetSideMaskFromCaSideMask(side, m_wfCaSideMask);
for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) {
if (pgob->GetType() == kgtReplicator && (sidm & GetSideMask(pgob->GetSide())) != 0) {
((ReplicatorGob *)pgob)->Enable(m_fOn);
}
}
return true;
}
// ModifyCreditsAction
bool ModifyCreditsAction::Init(char *psz)
{
if (!ParseNumber(&psz, &m_nAction))
return false;
int nCaSideMask;
if (!ParseNumber(&psz, &nCaSideMask))
return false;
m_wfCaSideMask = nCaSideMask;
if (!ParseNumber(&psz, &m_nAmount))
return false;
return true;
}
bool ModifyCreditsAction::Perform(Trigger *ptgr, Side side)
{
Player *applr[kcSides];
int cplrs = GetPlayersListFromCaSideMask(side, m_wfCaSideMask, applr);
for (int n = 0; n < cplrs; n++) {
Player *pplr = applr[n];
switch (m_nAction) {
case knModifyNumberTypeSet:
pplr->SetCredits(m_nAmount, true);
break;
case knModifyNumberTypeAdd:
{
int nCredits = pplr->GetCredits() + m_nAmount;
if (nCredits < 0)
nCredits = 0;
pplr->SetCredits(nCredits, true);
}
break;
case knModifyNumberTypeSubtract:
{
int nCredits = pplr->GetCredits() - m_nAmount;
if (nCredits < 0)
nCredits = 0;
pplr->SetCredits(nCredits, true);
}
break;
default:
Assert(false);
}
}
return true;
}
#ifdef DEBUG_HELPERS
char *ModifyCreditsAction::ToString()
{
switch (m_nAction) {
case knModifyNumberTypeSet:
sprintf(s_szDebugHelpers, "Set Credits");
break;
case knModifyNumberTypeAdd:
sprintf(s_szDebugHelpers, "Add Credits");
break;
case knModifyNumberTypeSubtract:
sprintf(s_szDebugHelpers, "Subtract Credits");
break;
default:
Assert(false);
}
return s_szDebugHelpers;
}
#endif
// MoveUnitsInAreaAction
bool MoveUnitsInAreaAction::Init(char *psz)
{
int nCaSideMask;
if (!ParseNumber(&psz, &nCaSideMask))
return false;
m_wfCaSideMask = nCaSideMask;
if (!ParseUnitMask(&psz, &m_um))
return false;
if (!ParseArea(&psz, &m_nAreaSrc))
return false;
return ParseArea(&psz, &m_nAreaDst);
}
bool MoveUnitsInAreaAction::Perform(Trigger *ptgr, Side side)
{
// Store the gobs on the stack
Assert(kcpgobMax / 2 * sizeof(Gob *) <= 1536);
Gob *apgob[kcpgobMax / 2];
// Find all the relevent units in AreaSrc
Enum enm;
UnitMask umFind = m_um & kumMobileUnits;
SideMask sidmFind = GetSideMaskFromCaSideMask(side, m_wfCaSideMask);
Gob **ppgobT = apgob;
Gob *pgob;
while ((pgob = ggobm.EnumGobsInArea(&enm, m_nAreaSrc, sidmFind, umFind)) != NULL) {
*ppgobT++ = pgob;
if (ppgobT - apgob >= ARRAYSIZE(apgob))
break;
}
// Send them off to AreaDst
int cpmunt = (int)(ppgobT - apgob);
if (cpmunt > 0) {
TRect trc;
ggobm.GetAreaRect(m_nAreaDst, &trc, side);
MoveUnitsToArea((MobileUnitGob **)apgob, cpmunt, &trc);
}
return true;
}
void MoveUnitsToArea(MobileUnitGob **apmunt, int cpmunt, TRect *ptrc)
{
// Figure out min speed
MobileUnitGob **ppmunt = apmunt;
WCoord wcMoveDistPerUpdate = kwcMax;
int ipmunt;
for (ipmunt = 0; ipmunt < cpmunt; ipmunt++, ppmunt++) {
MobileUnitGob *pmunt = *ppmunt;
Assert((pmunt->GetFlags() & kfGobMobileUnit) != 0);
// Get min speed per update
MobileUnitConsts *pmuntc = (MobileUnitConsts *)pmunt->GetConsts();
if (pmuntc->GetMoveDistPerUpdate() < wcMoveDistPerUpdate)
wcMoveDistPerUpdate = pmuntc->GetMoveDistPerUpdate();
}
// Send commands
TCoord tcRadius = RadiusFromUnitCount(cpmunt) + 1;
ppmunt = apmunt;
for (ipmunt = 0; ipmunt < cpmunt; ipmunt++, ppmunt++) {
MobileUnitGob *pmunt = *ppmunt;
// Tell it where to go.
Point ptCenter;
ptrc->GetCenter(&ptCenter);
SendMoveAction(pmunt->GetId(), WcFromTc(ptCenter.x), WcFromTc(ptCenter.y), tcRadius, wcMoveDistPerUpdate);
}
}
// SetFormalObjectiveTextAction
SetFormalObjectiveTextAction::SetFormalObjectiveTextAction()
{
m_szObjective = NULL;
}
SetFormalObjectiveTextAction::~SetFormalObjectiveTextAction()
{
if (m_szObjective != NULL)
gmmgr.FreePtr(m_szObjective);
}
bool SetFormalObjectiveTextAction::Init(char *psz)
{
if (!ParseNumber(&psz, &m_iObjective))
return false;
Assert(m_iObjective < kcFormalObjectivesMax, "Objective number (%d) exceeds range", m_iObjective);
m_szObjective = (char *)gmmgr.AllocPtr(strlen(psz) + 1);
gmmgr.WritePtr(m_szObjective, 0, psz, strlen(psz) + 1);
return true;
}
bool SetFormalObjectiveTextAction::Perform(Trigger *ptgr, Side side)
{
Player *pplr = gplrm.GetPlayer(side);
pplr->SetFormalObjectiveText(m_iObjective, m_szObjective);
return true;
}
// SetFormalObjectiveStatusAction
SetFormalObjectiveStatusAction::SetFormalObjectiveStatusAction()
{
m_szStatus = NULL;
}
SetFormalObjectiveStatusAction::~SetFormalObjectiveStatusAction()
{
if (m_szStatus != NULL)
gmmgr.FreePtr(m_szStatus);
}
bool SetFormalObjectiveStatusAction::Init(char *psz)
{
if (!ParseNumber(&psz, &m_iObjective))
return false;
Assert(m_iObjective < kcFormalObjectivesMax, "Objective number (%d) exceeds range", m_iObjective);
m_szStatus = (char *)gmmgr.AllocPtr(strlen(psz) + 1);
gmmgr.WritePtr(m_szStatus, 0, psz, strlen(psz) + 1);
return true;
}
bool SetFormalObjectiveStatusAction::Perform(Trigger *ptgr, Side side)
{
Player *pplr = gplrm.GetPlayer(side);
pplr->SetFormalObjectiveStatus(m_iObjective, m_szStatus);
return true;
}
// ShowObjectivesAction
bool ShowObjectivesAction::Perform(Trigger *ptgr, Side side)
{
// Only bother for the local player
if (side != gpplrLocal->GetSide())
return true;
// Bring up the Objectives form
if (!gfMultiplayer) {
gsim.Pause(true);
gplrm.GetPlayer(side)->ShowObjectives(ksoObjectives, true);
gsim.Pause(false);
} else {
// Make sure this the action won't be executed again while
// nested inside of Player::ShowObjective's input loop.
ptgr->SetCurrentActionComplete(side);
// Show asynchronously so TriggerMgr::Update() doesn't block
Event evt;
memset(&evt, 0, sizeof(evt));
evt.eType = mpShowObjectivesEvent;
evt.dw = side;
gevm.PostEvent(&evt);
}
// if the app was exited while the objectives screen was up, return to it.
return !gevm.IsAppStopping();
}
void ShowObjectivesAction::OnMPShowObjectivesEvent(Side side) {
// Don't pause the simulation
gplrm.GetPlayer(side)->ShowObjectives(ksoObjectives, true);
}
// SetFormalObjectiveInfoAction
SetFormalObjectiveInfoAction::SetFormalObjectiveInfoAction()
{
m_szInfo = NULL;
}
SetFormalObjectiveInfoAction::~SetFormalObjectiveInfoAction()
{
if (m_szInfo != NULL)
gmmgr.FreePtr(m_szInfo);
}
bool SetFormalObjectiveInfoAction::Init(char *psz)
{
m_szInfo = (char *)gmmgr.AllocPtr(strlen(psz) + 1);
gmmgr.WritePtr(m_szInfo, 0, psz, strlen(psz) + 1);
return true;
}
bool SetFormalObjectiveInfoAction::Perform(Trigger *ptgr, Side side)
{
Player *pplr = gplrm.GetPlayer(side);
pplr->SetFormalObjectiveInfo(m_szInfo);
return true;
}
// CutSceneAction
CutSceneAction::CutSceneAction()
{
m_pszMessage = NULL;
}
CutSceneAction::~CutSceneAction()
{
if (m_pszMessage != NULL)
gmmgr.FreePtr(m_pszMessage);
}
bool CutSceneAction::Init(char *psz)
{
m_pszMessage = (char *)gmmgr.AllocPtr(strlen(psz) + 1);
gmmgr.WritePtr(m_pszMessage, 0, psz, strlen(psz) + 1);
return true;
}
bool CutSceneAction::Perform(Trigger *ptgr, Side side)
{
// Does nothing in multiplayer
if (gfMultiplayer) {
return true;
}
// UNDONE: clear screen for cleaner palette transition?
CutScene(m_pszMessage, true);
// if the app was exited while the cut scene was up, return to it
return !gevm.IsAppStopping();
}
// JumpToMissionAction
bool JumpToMissionAction::Init(char *psz)
{
strncpyz(m_szLevel, psz, sizeof(m_szLevel));
return true;
}
bool JumpToMissionAction::Perform(Trigger *ptgr, Side side)
{
// Does nothing in multiplayer
if (gfMultiplayer) {
return true;
}
Assert(m_pactnNext == NULL, "Action attempted after End Mission");
ggame.SetNextLevel(m_szLevel);
// Keep any more triggers from executing
// NOTE: doesn't keep subsequent actions in the same trigger from executing
gsim.GetLevel()->GetTriggerMgr()->Enable(false);
Event evt;
memset(&evt, 0, sizeof(evt));
evt.eType = gameOverEvent;
evt.dw = knGoSuccess;
gevm.PostEvent(&evt);
return true;
}
// ModifyPvarAction
bool ModifyPvarAction::Init(char *psz)
{
if (!ParseString(&psz, m_szName))
return false;
Assert(strlen(m_szName) < kcbPvarNameMax);
if (!ParseNumber(&psz, &m_nAction))
return false;
if (!ParseNumber(&psz, &m_nAmount))
return false;
return true;
}
bool ModifyPvarAction::Perform(Trigger *ptgr, Side side)
{
int nAmount = m_nAmount;
switch (m_nAction) {
case knModifyNumberTypeAdd:
{
char szT[kcbPvarValueMax];
if (ggame.GetVar(m_szName, szT, sizeof(szT)))
nAmount = atoi(szT) + m_nAmount;
}
break;
case knModifyNumberTypeSubtract:
{
char szT[kcbPvarValueMax];
if (ggame.GetVar(m_szName, szT, sizeof(szT)))
nAmount = atoi(szT) - m_nAmount;
}
break;
case knModifyNumberTypeSet:
// Not in v1.1 by accident
#if 0
{
char szT[kcbPvarValueMax];
if (ggame.GetVar(m_szName, szT, sizeof(szT)))
nAmount = atoi(szT);
}
#endif
break;
default:
Assert(false);
break;
}
char szT[20];
itoa(nAmount, szT, 10);
ggame.SetVar(m_szName, szT);
return true;
}
#ifdef DEBUG_HELPERS
char *ModifyPvarAction::ToString()
{
switch (m_nAction) {
case knModifyNumberTypeSet:
sprintf(s_szDebugHelpers, "Set Pvar");
break;
case knModifyNumberTypeAdd:
sprintf(s_szDebugHelpers, "Add to Pvar");
break;
case knModifyNumberTypeSubtract:
sprintf(s_szDebugHelpers, "Subtract from Pvar");
break;
default:
Assert(false);
}
return s_szDebugHelpers;
}
#endif
// SetPvarTextAction
bool SetPvarTextAction::Init(char *psz)
{
if (!ParseString(&psz, m_szName))
return false;
Assert(strlen(m_szName) < kcbPvarNameMax);
strncpyz(m_szValue, psz, sizeof(m_szValue));
return true;
}
bool SetPvarTextAction::Perform(Trigger *ptgr, Side side)
{
ggame.SetVar(m_szName, m_szValue);
return true;
}
// ShowAlertAction
bool ShowAlertAction::Init(char *psz)
{
strncpyz(m_szAlert, psz, sizeof(m_szAlert));
return true;
}
bool ShowAlertAction::Perform(Trigger *ptgr, Side side)
{
ShowAlert(m_szAlert);
return true;
}
} // namespace wi