Skip to content

Commit

Permalink
functionality for mission objectives
Browse files Browse the repository at this point in the history
allows for tile types _other than_ alien brains to count towards objectives
unifying alien base control modules, synomium devices and alien brain
handling into a common set of functions.

using "objectiveType" in the deployment section for a missionSite will
define which type of MCD to check for when tallying mission objectives.

using "objectivesRequired" allows definition of how many of said tile
types must be destroyed in order for the mission to be considered a success
note that this is a MAXIMUM value, and for practical reasons we will use
the lower between this number and the number of actual objects on the map,
assuming this is non-zero.

"objectivePopup" will define the message that will appear between turns,
after the objective requirements have been fulfilled.

"objectiveComplete" and "objectiveFailed" are both in the form [string, int]
and represent the string and score adjustment to be shown on the debriefing
screen when one or the other of the appropriate condition is met.

"despawnPenalty" represents how many points the aliens will receive when this
site despawns due to XCom negligence.

"points" defines how many points the aliens will receive each half hour that
the mission site stays active. this does not stack with the despawn penalty.

if someone wants to add this all to the wiki i'd be most grateful.
  • Loading branch information
Warboy1982 committed Aug 2, 2015
1 parent 50f32d3 commit 0d75384
Show file tree
Hide file tree
Showing 14 changed files with 309 additions and 78 deletions.
6 changes: 6 additions & 0 deletions bin/standard/xcom1/alienDeployments.rul
Expand Up @@ -838,6 +838,7 @@ alienDeployments:
music: GMENBASE
markerName: STR_TERROR_SITE
duration: [4, 10]
despawnPenalty: 1000
- type: STR_ALIEN_BASE_ASSAULT
data:
- alienRank: 6
Expand Down Expand Up @@ -994,6 +995,9 @@ alienDeployments:
textOffset: -16
background: BACK01.SCR
showTarget: false
objectiveType: 3
objectiveComplete: [STR_ALIEN_BASE_CONTROL_DESTROYED, 500]
objectivePopup: STR_CONTROL_CENTER_DESTROYED
- type: STR_BASE_DEFENSE
data:
- alienRank: 5
Expand Down Expand Up @@ -1390,3 +1394,5 @@ alienDeployments:
showCraft: false
background: BACK01.SCR
script: BOSSBATTLE
objectiveType: 14
objectivesRequired: 1
18 changes: 17 additions & 1 deletion src/Battlescape/BattlescapeGame.cpp
Expand Up @@ -38,6 +38,7 @@
#include "AlienBAIState.h"
#include "CivilianBAIState.h"
#include "Pathfinding.h"
#include "../Ruleset/AlienDeployment.h"
#include "../Engine/Game.h"
#include "../Engine/Language.h"
#include "../Engine/Sound.h"
Expand Down Expand Up @@ -452,7 +453,7 @@ void BattlescapeGame::endTurn()

tallyUnits(liveAliens, liveSoldiers);

if (_save->allObjectivesDestroyed())
if (_save->allObjectivesDestroyed() && _save->getObjectiveType() == MUST_DESTROY)
{
_parentState->finishBattle(false, liveSoldiers);
return;
Expand Down Expand Up @@ -607,6 +608,21 @@ void BattlescapeGame::showInfoBoxQueue()
_infoboxQueue.clear();
}

/**
* Sets up a mission complete notification.
*/
void BattlescapeGame::missionComplete()
{
Game *game = _parentState->getGame();
if (game->getRuleset()->getDeployment(_save->getMissionType()))
{
std::string missionComplete = game->getRuleset()->getDeployment(_save->getMissionType())->getObjectivePopup();
if (missionComplete != "")
{
_infoboxQueue.push_back(new InfoboxOKState(game->getLanguage()->getString(missionComplete)));
}
}
}
/**
* Handles the result of non target actions, like priming a grenade.
*/
Expand Down
2 changes: 2 additions & 0 deletions src/Battlescape/BattlescapeGame.h
Expand Up @@ -193,6 +193,8 @@ class BattlescapeGame
void cleanupDeleted();
/// Get the depth of the saved game.
const int getDepth() const;
/// Sets up a mission complete notification.
void missionComplete();
};

}
Expand Down
57 changes: 46 additions & 11 deletions src/Battlescape/BattlescapeGenerator.cpp
Expand Up @@ -278,8 +278,11 @@ void BattlescapeGenerator::nextStage()
{
throw Exception("Map generator encountered an error: " + _terrain->getScript() + " script not found.");
}

generateMap(script);

setupObjectives(ruleDeploy);

int highestSoldierID = 0;
bool selectedFirstSoldier = false;
for (std::vector<BattleUnit*>::iterator j = _save->getUnits()->begin(); j != _save->getUnits()->end(); ++j)
Expand Down Expand Up @@ -427,8 +430,11 @@ void BattlescapeGenerator::run()
{
throw Exception("Map generator encountered an error: " + _terrain->getScript() + " script not found.");
}

generateMap(script);

setupObjectives(ruleDeploy);

deployXCOM();

size_t unitCount = _save->getUnits()->size();
Expand Down Expand Up @@ -2010,17 +2016,6 @@ void BattlescapeGenerator::generateMap(const std::vector<MapScript*> *script)
}
}

for (int i = 0; i < _save->getMapSizeXYZ(); ++i)
{
for (int j = 0; j != 4; ++j)
{
if (_save->getTiles()[i]->getMapData(j) && _save->getTiles()[i]->getMapData(j)->getSpecialType() == MUST_DESTROY)
{
_save->addToObjectiveCount();
}
}
}

delete _dummy;

attachNodeLinks();
Expand Down Expand Up @@ -2660,4 +2655,44 @@ void BattlescapeGenerator::setTerrain(RuleTerrain *terrain)
_terrain = terrain;
}


/**
* Sets up the objectives for the map.
* @param ruleDeploy the deployment data we're gleaning data from.
*/
void BattlescapeGenerator::setupObjectives(AlienDeployment *ruleDeploy)
{
int targetType = ruleDeploy->getObjectiveType();

if (targetType > -1)
{
int objectives = ruleDeploy->getObjectivesRequired();
int actualCount = 0;

for (int i = 0; i < _save->getMapSizeXYZ(); ++i)
{
for (int j = 0; j != 4; ++j)
{
if (_save->getTiles()[i]->getMapData(j) && _save->getTiles()[i]->getMapData(j)->getSpecialType() == targetType)
{
actualCount++;
}
}
}

if (actualCount > 0)
{
_save->setObjectiveType(targetType);

if (actualCount < objectives || objectives == 0)
{
_save->setObjectiveCount(actualCount);
}
else
{
_save->setObjectiveCount(objectives);
}
}
}
}
}
2 changes: 2 additions & 0 deletions src/Battlescape/BattlescapeGenerator.h
Expand Up @@ -164,6 +164,8 @@ class BattlescapeGenerator
void nextStage();
/// Generates an inventory battlescape.
void runInventory(Craft *craft);
/// Sets up the objectives for the map.
void setupObjectives(AlienDeployment *ruleDeploy);
};

}
Expand Down
71 changes: 45 additions & 26 deletions src/Battlescape/DebriefingState.cpp
Expand Up @@ -368,11 +368,41 @@ void DebriefingState::prepareDebriefing()
}
}

SavedGame *save = _game->getSavedGame();
SavedBattleGame *battle = save->getSavedBattle();
AlienDeployment *deployment = _game->getRuleset()->getDeployment(battle->getMissionType());

bool aborted = battle->isAborted();
bool success = !aborted;
Craft* craft = 0;
std::vector<Craft*>::iterator craftIterator;
Base* base = 0;
std::string target;

int playerInExitArea = 0; // if this stays 0 the craft is lost...
int playersSurvived = 0; // if this stays 0 the craft is lost...
int playersUnconscious = 0;


_stats.push_back(new DebriefingStat("STR_ALIENS_KILLED", false));
_stats.push_back(new DebriefingStat("STR_ALIEN_CORPSES_RECOVERED", false));
_stats.push_back(new DebriefingStat("STR_LIVE_ALIENS_RECOVERED", false));
_stats.push_back(new DebriefingStat("STR_ALIEN_ARTIFACTS_RECOVERED", false));
_stats.push_back(new DebriefingStat("STR_ALIEN_BASE_CONTROL_DESTROYED", false));

std::string objectiveCompleteText, objectiveFailedText;
int objectiveCompleteScore = 0, objectiveFailedScore = 0;
if (deployment)
{
if (deployment->getObjectiveCompleteInfo(objectiveCompleteText, objectiveCompleteScore))
{
_stats.push_back(new DebriefingStat(objectiveCompleteText, false));
}
if (deployment->getObjectiveFailedInfo(objectiveFailedText, objectiveFailedScore))
{
_stats.push_back(new DebriefingStat(objectiveFailedText, false));
}
}

_stats.push_back(new DebriefingStat("STR_CIVILIANS_KILLED_BY_ALIENS", false));
_stats.push_back(new DebriefingStat("STR_CIVILIANS_KILLED_BY_XCOM_OPERATIVES", false));
_stats.push_back(new DebriefingStat("STR_CIVILIANS_SAVED", false));
Expand All @@ -389,19 +419,6 @@ void DebriefingState::prepareDebriefing()

_stats.push_back(new DebriefingStat(_game->getRuleset()->getAlienFuelName(), true));

SavedGame *save = _game->getSavedGame();
SavedBattleGame *battle = save->getSavedBattle();
bool aborted = battle->isAborted();
bool success = !aborted;
Craft* craft = 0;
std::vector<Craft*>::iterator craftIterator;
Base* base = 0;
std::string target;

int playerInExitArea = 0; // if this stays 0 the craft is lost...
int playersSurvived = 0; // if this stays 0 the craft is lost...
int playersUnconscious = 0;

for (std::vector<Base*>::iterator i = save->getBases()->begin(); i != save->getBases()->end(); ++i)
{
// in case we have a craft - check which craft it is about
Expand Down Expand Up @@ -564,28 +581,22 @@ void DebriefingState::prepareDebriefing()
{
_txtRecovery->setText(tr("STR_ALIEN_BASE_RECOVERY"));
bool destroyAlienBase = true;
AlienDeployment *deployment = _game->getRuleset()->getDeployment(battle->getMissionType());
if (!deployment->getNextStage().empty())
{
destroyAlienBase = false;
}
else if (aborted || playersSurvived == 0)
{
for (int i = 0; i < battle->getMapSizeXYZ(); ++i)
{
// get recoverable map data objects from the battlescape map
if (
battle->getTiles()[i]->getMapData(O_OBJECT) && battle->getTiles()[i]->getMapData(O_OBJECT)->getSpecialType() == UFO_NAVIGATION)
{
destroyAlienBase = false;
break;
}
}
if (!battle->allObjectivesDestroyed())
destroyAlienBase = false;
}
success = destroyAlienBase;
if (destroyAlienBase)
{
addStat("STR_ALIEN_BASE_CONTROL_DESTROYED", 1, 500);
if (objectiveCompleteText != "")
{
addStat(objectiveCompleteText, 1, objectiveCompleteScore);
}
// Take care to remove supply missions for this base.
std::for_each(save->getAlienMissions().begin(), save->getAlienMissions().end(),
ClearAlienBase(*i));
Expand Down Expand Up @@ -806,6 +817,10 @@ void DebriefingState::prepareDebriefing()
else
{
_txtTitle->setText(tr("STR_ALIENS_DEFEATED"));
if (objectiveCompleteText != "")
{
addStat(objectiveCompleteText, 1, objectiveCompleteScore);
}
}

if (!aborted)
Expand Down Expand Up @@ -859,6 +874,10 @@ void DebriefingState::prepareDebriefing()
else
{
_txtTitle->setText(tr("STR_TERROR_CONTINUES"));
if (objectiveFailedText != "")
{
addStat(objectiveFailedText, 1, objectiveFailedScore);
}
}

if (playersSurvived > 0 && !_destroyBase)
Expand Down
12 changes: 5 additions & 7 deletions src/Battlescape/TileEngine.cpp
Expand Up @@ -1047,14 +1047,12 @@ BattleUnit *TileEngine::hit(const Position &center, int power, ItemDamageType ty
{
// power 25% to 75%
const int rndPower = RNG::generate(power/4, (power*3)/4); //RNG::boxMuller(power, power/6)
if (part == V_OBJECT && _save->getMissionType() == "STR_BASE_DEFENSE")
if (part == V_OBJECT && rndPower >= tile->getMapData(O_OBJECT)->getArmor() &&
_save->getMissionType() == "STR_BASE_DEFENSE" && tile->getMapData(V_OBJECT)->isBaseModule())
{
if (rndPower >= tile->getMapData(O_OBJECT)->getArmor() && tile->getMapData(V_OBJECT)->isBaseModule())
{
_save->getModuleMap()[(center.x/16)/10][(center.y/16)/10].second--;
}
_save->getModuleMap()[(center.x/16)/10][(center.y/16)/10].second--;
}
if (tile->damage(part, rndPower))
if (tile->damage(part, rndPower, _save->getObjectiveType()))
{
_save->addDestroyedObjective();
}
Expand Down Expand Up @@ -1441,7 +1439,7 @@ bool TileEngine::detonate(Tile* tile)
currentpart2 = tiles[i]->getMapData(currentpart)->getDataset()->getObjects()->at(diemcd)->getObjectType();
else
currentpart2 = currentpart;
if (tiles[i]->destroy(currentpart))
if (tiles[i]->destroy(currentpart, _save->getObjectiveType()))
objective = true;
currentpart = currentpart2;
if (tiles[i]->getMapData(currentpart)) // take new values
Expand Down
26 changes: 18 additions & 8 deletions src/Geoscape/GeoscapeState.cpp
Expand Up @@ -108,6 +108,7 @@
#include "../Menu/ListSaveState.h"
#include "../Ruleset/RuleGlobe.h"
#include "../Engine/Exception.h"
#include "../Ruleset/AlienDeployment.h"

namespace OpenXcom
{
Expand Down Expand Up @@ -1112,30 +1113,35 @@ class callThink: public std::unary_function<AlienMission*, void>
*/
bool GeoscapeState::processMissionSite(MissionSite *site) const
{
if (site->getSecondsRemaining() >= 30 * 60)
bool removeSite = site->getSecondsRemaining() < 30 * 60;
if (!removeSite)
{
site->setSecondsRemaining(site->getSecondsRemaining() - 30 * 60);
return false;
}
if (!site->getFollowers()->empty()) // CHEEKY EXPLOIT
else
{
return false;
removeSite = site->getFollowers()->empty(); // CHEEKY EXPLOIT
}
// Score and delete it.

int score = removeSite ? site->getDeployment()->getDespawnPenalty() : site->getDeployment()->getPoints();

Region *region = _game->getSavedGame()->locateRegion(*site);
if (region)
{
region->addActivityAlien(site->getRules()->getPoints() * 100);
//kids, tell your folks... don't ignore mission sites.
region->addActivityAlien(score);
}
for (std::vector<Country*>::iterator k = _game->getSavedGame()->getCountries()->begin(); k != _game->getSavedGame()->getCountries()->end(); ++k)
{
if ((*k)->getRules()->insideCountry(site->getLongitude(), site->getLatitude()))
{
(*k)->addActivityAlien(site->getRules()->getPoints() * 100);
(*k)->addActivityAlien(score);
break;
}
}
if (!removeSite)
{
return false;
}
delete site;
return true;
}
Expand Down Expand Up @@ -2172,6 +2178,10 @@ void GeoscapeState::determineAlienMissions()
// just an FYI: if you add a 0 to your conditionals, this flag will never resolve to true, and your command will never run.
process = (found == conditions.end() || (found->second == true && *j > 0) || (found->second == false && *j < 0));
}
if (command->getLabel() > 0 && conditions.find(command->getLabel()) != conditions.end())
{
throw Exception("Mission generator encountered an error: multiple commands are sharing the same label.");
}
// level four condition check: does random chance favour this command's execution?
if (process && RNG::percent(command->getExecutionOdds()))
{
Expand Down

0 comments on commit 0d75384

Please sign in to comment.