mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-23 06:57:23 +00:00
1380 lines
29 KiB
C++
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
|