diff options
Diffstat (limited to 'Stars45/Campaign.cpp')
-rw-r--r-- | Stars45/Campaign.cpp | 4716 |
1 files changed, 2370 insertions, 2346 deletions
diff --git a/Stars45/Campaign.cpp b/Stars45/Campaign.cpp index 7d943e0..499e36a 100644 --- a/Stars45/Campaign.cpp +++ b/Stars45/Campaign.cpp @@ -1,2346 +1,2370 @@ -/* Project Starshatter 4.5
- Destroyer Studios LLC
- Copyright (C) 1997-2006. All Rights Reserved.
-
- SUBSYSTEM: Stars.exe
- FILE: Campaign.cpp
- AUTHOR: John DiCamillo
-
-
- OVERVIEW
- ========
- Campaign defines a strategic military scenario.
-*/
-
-#include "MemDebug.h"
-#include "Campaign.h"
-#include "CampaignPlanStrategic.h"
-#include "CampaignPlanAssignment.h"
-#include "CampaignPlanEvent.h"
-#include "CampaignPlanMission.h"
-#include "CampaignPlanMovement.h"
-#include "CampaignSituationReport.h"
-#include "CampaignSaveGame.h"
-#include "Combatant.h"
-#include "CombatAction.h"
-#include "CombatEvent.h"
-#include "CombatGroup.h"
-#include "CombatRoster.h"
-#include "CombatUnit.h"
-#include "CombatZone.h"
-#include "Galaxy.h"
-#include "Mission.h"
-#include "StarSystem.h"
-#include "Starshatter.h"
-#include "Player.h"
-
-#include "Game.h"
-#include "Bitmap.h"
-#include "DataLoader.h"
-#include "ParseUtil.h"
-#include "Random.h"
-#include "FormatUtil.h"
-
-
-const int TIME_NEVER = (int) 1e9;
-const int ONE_DAY = (int) 24 * 3600;
-
-// +====================================================================+
-
-MissionInfo::MissionInfo()
-: mission(0), start(0), type(0), id(0), min_rank(0), max_rank(0),
-action_id(0), action_status(0), exec_once(0),
-start_before(TIME_NEVER), start_after(0)
-{ }
-
-MissionInfo::~MissionInfo()
-{
- delete mission;
-}
-
-bool
-MissionInfo::IsAvailable()
-{
- Campaign* campaign = Campaign::GetCampaign();
- Player* player = Player::GetCurrentPlayer();
- CombatGroup* player_group = campaign->GetPlayerGroup();
-
- if (campaign->GetTime() < start_after)
- return false;
-
- if (campaign->GetTime() > start_before)
- return false;
-
- if (region.length() && player_group->GetRegion() != region)
- return false;
-
- if (min_rank && player->Rank() < min_rank)
- return false;
-
- if (max_rank && player->Rank() > max_rank)
- return false;
-
- if (exec_once < 0)
- return false;
-
- if (exec_once > 0)
- exec_once = -1;
-
- return true;
-}
-
-// +====================================================================+
-
-TemplateList::TemplateList()
-: mission_type(0), group_type(0), index(0)
-{ }
-
-TemplateList::~TemplateList()
-{
- missions.destroy();
-}
-
-// +====================================================================+
-
-static List<Campaign> campaigns;
-static Campaign* current_campaign = 0;
-
-// +--------------------------------------------------------------------+
-
-Campaign::Campaign(int id, const char* n)
-: campaign_id(id), name(n), mission_id(-1), mission(0), net_mission(0),
-scripted(false), sequential(false), time(0), startTime(0), loadTime(0),
-player_group(0), player_unit(0), status(CAMPAIGN_INIT), lockout(0),
-loaded_from_savegame(false)
-{
- ZeroMemory(path, sizeof(path));
- Load();
-}
-
-Campaign::Campaign(int id, const char* n, const char* p)
-: campaign_id(id), name(n), mission_id(-1), mission(0), net_mission(0),
-scripted(false), sequential(false), time(0), startTime(0), loadTime(0),
-player_group(0), player_unit(0), status(CAMPAIGN_INIT), lockout(0),
-loaded_from_savegame(false)
-{
- ZeroMemory(path, sizeof(path));
- strncpy(path, p, sizeof(path));
- Load();
-}
-
-// +--------------------------------------------------------------------+
-
-Campaign::~Campaign()
-{
- for (int i = 0; i < NUM_IMAGES; i++)
- image[i].ClearImage();
-
- delete net_mission;
-
- actions.destroy();
- events.destroy();
- missions.destroy();
- templates.destroy();
- planners.destroy();
- zones.destroy();
- combatants.destroy();
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Campaign::Initialize()
-{
- Campaign* c = 0;
- DataLoader* loader = DataLoader::GetLoader();
-
- for (int i = 1; i < 100; i++) {
- char path[256];
- sprintf_s(path, "Campaigns/%02d/", i);
-
- loader->UseFileSystem(true);
- loader->SetDataPath(path);
-
- if (loader->FindFile("campaign.def")) {
- char txt[256];
- sprintf_s(txt, "Dynamic Campaign %02d", i);
- c = new(__FILE__,__LINE__) Campaign(i, txt);
-
- if (c) {
- campaigns.insertSort(c);
- }
- }
- }
-
- c = new(__FILE__,__LINE__) Campaign(SINGLE_MISSIONS, "Single Missions");
- if (c) {
- campaigns.insertSort(c);
- current_campaign = c;
- }
-
- c = new(__FILE__,__LINE__) Campaign(MULTIPLAYER_MISSIONS, "Multiplayer Missions");
- if (c) {
- campaigns.insertSort(c);
- }
-
- c = new(__FILE__,__LINE__) Campaign(CUSTOM_MISSIONS, "Custom Missions");
- if (c) {
- campaigns.insertSort(c);
- }
-}
-
-void
-Campaign::Close()
-{
- Print("Campaign::Close() - destroying all campaigns\n");
- current_campaign = 0;
- campaigns.destroy();
-}
-
-Campaign*
-Campaign::GetCampaign()
-{
- return current_campaign;
-}
-
-Campaign*
-Campaign::SelectCampaign(const char* name)
-{
- Campaign* c = 0;
- ListIter<Campaign> iter = campaigns;
-
- while (++iter && !c) {
- if (!_stricmp(iter->Name(), name))
- c = iter.value();
- }
-
- if (c) {
- Print("Campaign: Selected '%s'\n", c->Name());
- current_campaign = c;
- }
- else {
- Print("Campaign: could not find '%s'\n", name);
- }
-
- return c;
-}
-
-Campaign*
-Campaign::CreateCustomCampaign(const char* name, const char* path)
-{
- int id = 0;
-
- if (name && *name && path && *path) {
- ListIter<Campaign> iter = campaigns;
-
- while (++iter) {
- Campaign* c = iter.value();
- if (c->GetCampaignId() >= id)
- id = c->GetCampaignId() + 1;
-
- if (!strcmp(c->Name(), name)) {
- Print("Campaign: custom campaign '%s' already exists.\n", name);
- return 0;
- }
- }
- }
-
- if (id == 0)
- id = CUSTOM_MISSIONS + 1;
-
- Campaign* c = new(__FILE__,__LINE__) Campaign(id, name, path);
- Print("Campaign: created custom campaign %d '%s'\n", id, name);
- campaigns.append(c);
-
- return c;
-}
-
-List<Campaign>&
-Campaign::GetAllCampaigns()
-{
- return campaigns;
-}
-
-int
-Campaign::GetLastCampaignId()
-{
- int result = 0;
-
- for (int i = 0; i < campaigns.size(); i++) {
- Campaign* c = campaigns.at(i);
-
- if (c->IsDynamic() && c->GetCampaignId() > result) {
- result = c->GetCampaignId();
- }
- }
-
- return result;
-}
-
-// +--------------------------------------------------------------------+
-
-CombatEvent*
-Campaign::GetLastEvent()
-{
- CombatEvent* result = 0;
-
- if (!events.isEmpty())
- result = events.last();
-
- return result;
-}
-
-// +--------------------------------------------------------------------+
-
-int
-Campaign::CountNewEvents() const
-{
- int result = 0;
-
- for (int i = 0; i < events.size(); i++)
- if (/*events[i]->Source() != CombatEvent::TACNET &&*/ !events[i]->Visited())
- result++;
-
- return result;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Campaign::Clear()
-{
- missions.destroy();
- planners.destroy();
- combatants.destroy();
- events.destroy();
- actions.destroy();
-
- player_group = 0;
- player_unit = 0;
-
- updateTime = time;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Campaign::Load()
-{
- // first, unload any existing data:
- Unload();
-
- if (!path[0]) {
- // then load the campaign from files:
- switch (campaign_id) {
- case SINGLE_MISSIONS: strcpy_s(path, "Missions/"); break;
- case CUSTOM_MISSIONS: strcpy_s(path, "Mods/Missions/"); break;
- case MULTIPLAYER_MISSIONS: strcpy_s(path, "Multiplayer/"); break;
- default: sprintf_s(path, "Campaigns/%02d/", campaign_id); break;
- }
- }
-
- DataLoader* loader = DataLoader::GetLoader();
- loader->UseFileSystem(true);
- loader->SetDataPath(path);
- systems.clear();
-
- if (loader->FindFile("zones.def"))
- zones.append(CombatZone::Load("zones.def"));
-
- for (int i = 0; i < zones.size(); i++) {
- Text s = zones[i]->System();
- bool found = false;
-
- for (int n = 0; !found && n < systems.size(); n++) {
- if (s == systems[n]->Name())
- found = true;
- }
-
- if (!found)
- systems.append(Galaxy::GetInstance()->GetSystem(s));
- }
-
- loader->UseFileSystem(Starshatter::UseFileSystem());
-
- if (loader->FindFile("campaign.def"))
- LoadCampaign(loader);
-
- if (campaign_id == CUSTOM_MISSIONS) {
- loader->SetDataPath(path);
- LoadCustomMissions(loader);
- }
- else {
- bool found = false;
-
- if (loader->FindFile("missions.def")) {
- loader->SetDataPath(path);
- LoadMissionList(loader);
- found = true;
- }
-
- if (loader->FindFile("templates.def")) {
- loader->SetDataPath(path);
- LoadTemplateList(loader);
- found = true;
- }
-
- if (!found) {
- loader->SetDataPath(path);
- LoadCustomMissions(loader);
- }
- }
-
- loader->UseFileSystem(true);
- loader->SetDataPath(path);
-
- if (loader->FindFile("image.pcx")) {
- loader->LoadBitmap("image.pcx", image[ 0]);
- loader->LoadBitmap("selected.pcx", image[ 1]);
- loader->LoadBitmap("unavail.pcx", image[ 2]);
- loader->LoadBitmap("banner.pcx", image[ 3]);
- }
-
- loader->SetDataPath(0);
- loader->UseFileSystem(Starshatter::UseFileSystem());
-}
-
-void
-Campaign::Unload()
-{
- SetStatus(CAMPAIGN_INIT);
-
- Game::ResetGameTime();
- StarSystem::SetBaseTime(0);
-
- startTime = Stardate();
- loadTime = startTime;
- lockout = 0;
-
- for (int i = 0; i < NUM_IMAGES; i++)
- image[i].ClearImage();
-
- Clear();
-
- zones.destroy();
-}
-
-void
-Campaign::LoadCampaign(DataLoader* loader, bool full)
-{
- BYTE* block = 0;
- const char* filename = "campaign.def";
-
- loader->UseFileSystem(true);
- loader->LoadBuffer(filename, block, true);
- loader->UseFileSystem(Starshatter::UseFileSystem());
- Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
-
- Term* term = parser.ParseTerm();
-
- if (!term) {
- return;
- }
- else {
- TermText* file_type = term->isText();
- if (!file_type || file_type->value() != "CAMPAIGN") {
- return;
- }
- }
-
- do {
- delete term; term = 0;
- term = parser.ParseTerm();
-
- if (term) {
- TermDef* def = term->isDef();
- if (def) {
- if (def->name()->value() == "name") {
- if (!def->term() || !def->term()->isText()) {
- ::Print("WARNING: name missing in '%s/%s'\n", loader->GetDataPath(), filename);
- }
- else {
- name = def->term()->isText()->value();
- name = Game::GetText(name);
- }
- }
- else if (def->name()->value() == "desc") {
- if (!def->term() || !def->term()->isText()) {
- ::Print("WARNING: description missing in '%s/%s'\n", loader->GetDataPath(), filename);
- }
- else {
- description = def->term()->isText()->value();
- description = Game::GetText(description);
- }
- }
- else if (def->name()->value() == "situation") {
- if (!def->term() || !def->term()->isText()) {
- ::Print("WARNING: situation missing in '%s/%s'\n", loader->GetDataPath(), filename);
- }
- else {
- situation = def->term()->isText()->value();
- situation = Game::GetText(situation);
- }
- }
- else if (def->name()->value() == "orders") {
- if (!def->term() || !def->term()->isText()) {
- ::Print("WARNING: orders missing in '%s/%s'\n", loader->GetDataPath(), filename);
- }
- else {
- orders = def->term()->isText()->value();
- orders = Game::GetText(orders);
- }
- }
- else if (def->name()->value() == "scripted") {
- if (def->term() && def->term()->isBool()) {
- scripted = def->term()->isBool()->value();
- }
- }
- else if (def->name()->value() == "sequential") {
- if (def->term() && def->term()->isBool()) {
- sequential = def->term()->isBool()->value();
- }
- }
- else if (full && def->name()->value() == "combatant") {
- if (!def->term() || !def->term()->isStruct()) {
- ::Print("WARNING: combatant struct missing in '%s/%s'\n", loader->GetDataPath(), filename);
- }
- else {
- TermStruct* val = def->term()->isStruct();
-
- char name[64];
- int iff = 0;
- CombatGroup* force = 0;
- CombatGroup* clone = 0;
-
- ZeroMemory(name, sizeof(name));
-
- for (int i = 0; i < val->elements()->size(); i++) {
- TermDef* pdef = val->elements()->at(i)->isDef();
- if (pdef) {
- if (pdef->name()->value() == "name") {
- GetDefText(name, pdef, filename);
-
- force = CombatRoster::GetInstance()->GetForce(name);
-
- if (force)
- clone = force->Clone(false); // shallow copy
- }
-
- else if (pdef->name()->value() == "group") {
- ParseGroup(pdef->term()->isStruct(), force, clone, filename);
- }
- }
- }
-
- loader->SetDataPath(path);
- Combatant* c = new(__FILE__,__LINE__) Combatant(name, clone);
- if (c) {
- combatants.append(c);
- }
- else {
- Unload();
- return;
- }
- }
- }
- else if (full && def->name()->value() == "action") {
- if (!def->term() || !def->term()->isStruct()) {
- ::Print("WARNING: action struct missing in '%s/%s'\n", loader->GetDataPath(), filename);
- }
- else {
- TermStruct* val = def->term()->isStruct();
- ParseAction(val, filename);
- }
- }
- }
- }
- }
- while (term);
-
- loader->ReleaseBuffer(block);
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Campaign::ParseGroup(TermStruct* val,
-CombatGroup* force,
-CombatGroup* clone,
-const char* filename)
-{
- if (!val) {
- ::Print("invalid combat group in campaign %s\n", name.data());
- return;
- }
-
- int type = 0;
- int id = 0;
-
- for (int i = 0; i < val->elements()->size(); i++) {
- TermDef* pdef = val->elements()->at(i)->isDef();
- if (pdef) {
- if (pdef->name()->value() == "type") {
- char type_name[64];
- GetDefText(type_name, pdef, filename);
- type = CombatGroup::TypeFromName(type_name);
- }
-
- else if (pdef->name()->value() == "id") {
- GetDefNumber(id, pdef, filename);
- }
- }
- }
-
- if (type && id) {
- CombatGroup* g = force->FindGroup(type, id);
-
- // found original group, now clone it over
- if (g && g->GetParent()) {
- CombatGroup* parent = CloneOver(force, clone, g->GetParent());
- parent->AddComponent(g->Clone());
- }
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Campaign::ParseAction(TermStruct* val, const char* filename)
-{
- if (!val) {
- ::Print("invalid action in campaign %s\n", name.data());
- return;
- }
-
- int id = 0;
- int type = 0;
- int subtype = 0;
- int opp_type = -1;
- int team = 0;
- int source = 0;
- Vec3 loc(0.0f, 0.0f, 0.0f);
- Text system;
- Text region;
- Text file;
- Text image;
- Text scene;
- Text text;
- int count = 1;
- int start_before = TIME_NEVER;
- int start_after = 0;
- int min_rank = 0;
- int max_rank = 100;
- int delay = 0;
- int probability = 100;
-
- int asset_type = 0;
- int asset_id = 0;
- int target_type = 0;
- int target_id = 0;
- int target_iff = 0;
-
- CombatAction* action = 0;
-
- for (int i = 0; i < val->elements()->size(); i++) {
- TermDef* pdef = val->elements()->at(i)->isDef();
- if (pdef) {
- if (pdef->name()->value() == "id") {
- GetDefNumber(id, pdef, filename);
- }
- else if (pdef->name()->value() == "type") {
- char txt[64];
- GetDefText(txt, pdef, filename);
- type = CombatAction::TypeFromName(txt);
- }
- else if (pdef->name()->value() == "subtype") {
- if (pdef->term()->isNumber()) {
- GetDefNumber(subtype, pdef, filename);
- }
-
- else if (pdef->term()->isText()) {
- char txt[64];
- GetDefText(txt, pdef, filename);
-
- if (type == CombatAction::MISSION_TEMPLATE) {
- subtype = Mission::TypeFromName(txt);
- }
- else if (type == CombatAction::COMBAT_EVENT) {
- subtype = CombatEvent::TypeFromName(txt);
- }
- else if (type == CombatAction::INTEL_EVENT) {
- subtype = Intel::IntelFromName(txt);
- }
- }
- }
- else if (pdef->name()->value() == "opp_type") {
- if (pdef->term()->isNumber()) {
- GetDefNumber(opp_type, pdef, filename);
- }
-
- else if (pdef->term()->isText()) {
- char txt[64];
- GetDefText(txt, pdef, filename);
-
- if (type == CombatAction::MISSION_TEMPLATE) {
- opp_type = Mission::TypeFromName(txt);
- }
- }
- }
- else if (pdef->name()->value() == "source") {
- char txt[64];
- GetDefText(txt, pdef, filename);
- source = CombatEvent::SourceFromName(txt);
- }
- else if (pdef->name()->value() == "team") {
- GetDefNumber(team, pdef, filename);
- }
- else if (pdef->name()->value() == "iff") {
- GetDefNumber(team, pdef, filename);
- }
- else if (pdef->name()->value() == "count") {
- GetDefNumber(count, pdef, filename);
- }
- else if (pdef->name()->value().contains("before")) {
- if (pdef->term()->isNumber()) {
- GetDefNumber(start_before, pdef, filename);
- }
- else {
- GetDefTime(start_before, pdef, filename);
- start_before -= ONE_DAY;
- }
- }
- else if (pdef->name()->value().contains("after")) {
- if (pdef->term()->isNumber()) {
- GetDefNumber(start_after, pdef, filename);
- }
- else {
- GetDefTime(start_after, pdef, filename);
- start_after -= ONE_DAY;
- }
- }
- else if (pdef->name()->value() == "min_rank") {
- if (pdef->term()->isNumber()) {
- GetDefNumber(min_rank, pdef, filename);
- }
- else {
- char rank_name[64];
- GetDefText(rank_name, pdef, filename);
- min_rank = Player::RankFromName(rank_name);
- }
- }
- else if (pdef->name()->value() == "max_rank") {
- if (pdef->term()->isNumber()) {
- GetDefNumber(max_rank, pdef, filename);
- }
- else {
- char rank_name[64];
- GetDefText(rank_name, pdef, filename);
- max_rank = Player::RankFromName(rank_name);
- }
- }
- else if (pdef->name()->value() == "delay") {
- GetDefNumber(delay, pdef, filename);
- }
- else if (pdef->name()->value() == "probability") {
- GetDefNumber(probability, pdef, filename);
- }
- else if (pdef->name()->value() == "asset_type") {
- char type_name[64];
- GetDefText(type_name, pdef, filename);
- asset_type = CombatGroup::TypeFromName(type_name);
- }
- else if (pdef->name()->value() == "target_type") {
- char type_name[64];
- GetDefText(type_name, pdef, filename);
- target_type = CombatGroup::TypeFromName(type_name);
- }
- else if (pdef->name()->value() == "location" ||
- pdef->name()->value() == "loc") {
- GetDefVec(loc, pdef, filename);
- }
- else if (pdef->name()->value() == "system" ||
- pdef->name()->value() == "sys") {
- GetDefText(system, pdef, filename);
- }
- else if (pdef->name()->value() == "region" ||
- pdef->name()->value() == "rgn" ||
- pdef->name()->value() == "zone") {
- GetDefText(region, pdef, filename);
- }
- else if (pdef->name()->value() == "file") {
- GetDefText(file, pdef, filename);
- }
- else if (pdef->name()->value() == "image") {
- GetDefText(image, pdef, filename);
- }
- else if (pdef->name()->value() == "scene") {
- GetDefText(scene, pdef, filename);
- }
- else if (pdef->name()->value() == "text") {
- GetDefText(text, pdef, filename);
- text = Game::GetText(text);
- }
- else if (pdef->name()->value() == "asset_id") {
- GetDefNumber(asset_id, pdef, filename);
- }
- else if (pdef->name()->value() == "target_id") {
- GetDefNumber(target_id, pdef, filename);
- }
- else if (pdef->name()->value() == "target_iff") {
- GetDefNumber(target_iff, pdef, filename);
- }
-
- else if (pdef->name()->value() == "asset_kill") {
- if (!action)
- action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team);
-
- if (action) {
- char txt[64];
- GetDefText(txt, pdef, filename);
- action->AssetKills().append(new (__FILE__,__LINE__) Text(txt));
- }
- }
-
- else if (pdef->name()->value() == "target_kill") {
- if (!action)
- action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team);
-
- if (action) {
- char txt[64];
- GetDefText(txt, pdef, filename);
- action->TargetKills().append(new (__FILE__,__LINE__) Text(txt));
- }
- }
-
- else if (pdef->name()->value() == "req") {
- if (!action)
- action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team);
-
- if (!pdef->term() || !pdef->term()->isStruct()) {
- ::Print("WARNING: action req struct missing in '%s'\n", filename);
- }
- else if (action) {
- TermStruct* val2 = pdef->term()->isStruct();
-
- int act = 0;
- int stat = CombatAction::COMPLETE;
- bool not = false;
-
- Combatant* c1 = 0;
- Combatant* c2 = 0;
- int comp = 0;
- int score = 0;
- int intel = 0;
- int gtype = 0;
- int gid = 0;
-
- for (int i = 0; i < val2->elements()->size(); i++) {
- TermDef* pdef2 = val2->elements()->at(i)->isDef();
- if (pdef2) {
- if (pdef2->name()->value() == "action") {
- GetDefNumber(act, pdef2, filename);
- }
- else if (pdef2->name()->value() == "status") {
- char txt[64];
- GetDefText(txt, pdef2, filename);
- stat = CombatAction::StatusFromName(txt);
- }
- else if (pdef2->name()->value() == "not") {
- GetDefBool(not, pdef2, filename);
- }
-
- else if (pdef2->name()->value() == "c1") {
- char txt[64];
- GetDefText(txt, pdef2, filename);
- c1 = GetCombatant(txt);
- }
- else if (pdef2->name()->value() == "c2") {
- char txt[64];
- GetDefText(txt, pdef2, filename);
- c2 = GetCombatant(txt);
- }
- else if (pdef2->name()->value() == "comp") {
- char txt[64];
- GetDefText(txt, pdef2, filename);
- comp = CombatActionReq::CompFromName(txt);
- }
- else if (pdef2->name()->value() == "score") {
- GetDefNumber(score, pdef2, filename);
- }
- else if (pdef2->name()->value() == "intel") {
- if (pdef2->term()->isNumber()) {
- GetDefNumber(intel, pdef2, filename);
- }
- else if (pdef2->term()->isText()) {
- char txt[64];
- GetDefText(txt, pdef2, filename);
- intel = Intel::IntelFromName(txt);
- }
- }
- else if (pdef2->name()->value() == "group_type") {
- char type_name[64];
- GetDefText(type_name, pdef2, filename);
- gtype = CombatGroup::TypeFromName(type_name);
- }
- else if (pdef2->name()->value() == "group_id") {
- GetDefNumber(gid, pdef2, filename);
- }
- }
- }
-
- if (act)
- action->AddRequirement(act, stat, not);
-
- else if (gtype)
- action->AddRequirement(c1, gtype, gid, comp, score, intel);
-
- else
- action->AddRequirement(c1, c2, comp, score);
- }
- }
- }
- }
-
- if (!action)
- action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team);
-
- if (action) {
- action->SetSource(source);
- action->SetOpposingType(opp_type);
- action->SetLocation(loc);
- action->SetSystem(system);
- action->SetRegion(region);
- action->SetFilename(file);
- action->SetImageFile(image);
- action->SetSceneFile(scene);
- action->SetCount(count);
- action->SetStartAfter(start_after);
- action->SetStartBefore(start_before);
- action->SetMinRank(min_rank);
- action->SetMaxRank(max_rank);
- action->SetDelay(delay);
- action->SetProbability(probability);
-
- action->SetAssetId(asset_id);
- action->SetAssetType(asset_type);
- action->SetTargetId(target_id);
- action->SetTargetIFF(target_iff);
- action->SetTargetType(target_type);
- action->SetText(text);
-
- actions.append(action);
- }
-}
-
-// +--------------------------------------------------------------------+
-
-CombatGroup*
-Campaign::CloneOver(CombatGroup* force, CombatGroup* clone, CombatGroup* group)
-{
- CombatGroup* orig_parent = group->GetParent();
-
- if (orig_parent) {
- CombatGroup* clone_parent = clone->FindGroup(orig_parent->Type(), orig_parent->GetID());
-
- if (!clone_parent)
- clone_parent = CloneOver(force, clone, orig_parent);
-
- CombatGroup* new_clone = clone->FindGroup(group->Type(), group->GetID());
-
- if (!new_clone) {
- new_clone = group->Clone(false);
- clone_parent->AddComponent(new_clone);
- }
-
- return new_clone;
- }
- else {
- return clone;
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Campaign::LoadMissionList(DataLoader* loader)
-{
- bool ok = true;
- BYTE* block = 0;
- const char* filename = "Missions.def";
-
- loader->UseFileSystem(true);
- loader->LoadBuffer(filename, block, true);
- loader->UseFileSystem(Starshatter::UseFileSystem());
- Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
-
- Term* term = parser.ParseTerm();
-
- if (!term) {
- return;
- }
- else {
- TermText* file_type = term->isText();
- if (!file_type || file_type->value() != "MISSIONLIST") {
- ::Print("WARNING: invalid mission list file '%s'\n", filename);
- term->print(10);
- return;
- }
- }
-
- do {
- delete term; term = 0;
- term = parser.ParseTerm();
-
- if (term) {
- TermDef* def = term->isDef();
- if (def) {
- if (def->name()->value() == "mission") {
- if (!def->term() || !def->term()->isStruct()) {
- ::Print("WARNING: mission struct missing in '%s'\n", filename);
- }
- else {
- TermStruct* val = def->term()->isStruct();
-
- int id = 0;
- Text name;
- Text desc;
- char script[256];
- char system[256];
- char region[256];
- int start = 0;
- int type = 0;
-
- ZeroMemory(script, sizeof(script));
-
- strcpy_s(system, "Unknown");
- strcpy_s(region, "Unknown");
-
- for (int i = 0; i < val->elements()->size(); i++) {
- TermDef* pdef = val->elements()->at(i)->isDef();
- if (pdef) {
- if (pdef->name()->value() == "id")
- GetDefNumber(id, pdef, filename);
-
- else if (pdef->name()->value() == "name") {
- GetDefText(name, pdef, filename);
- name = Game::GetText(name);
- }
-
- else if (pdef->name()->value() == "desc") {
- GetDefText(desc, pdef, filename);
- if (desc.length() > 0 && desc.length() < 32)
- desc = Game::GetText(desc);
- }
-
- else if (pdef->name()->value() == "start")
- GetDefTime(start, pdef, filename);
-
- else if (pdef->name()->value() == "system")
- GetDefText(system, pdef, filename);
-
- else if (pdef->name()->value() == "region")
- GetDefText(region, pdef, filename);
-
- else if (pdef->name()->value() == "script")
- GetDefText(script, pdef, filename);
-
- else if (pdef->name()->value() == "type") {
- char typestr[64];
- GetDefText(typestr, pdef, filename);
- type = Mission::TypeFromName(typestr);
- }
- }
- }
-
- MissionInfo* info = new(__FILE__,__LINE__) MissionInfo;
- if (info) {
- info->id = id;
- info->name = name;
- info->description = desc;
- info->system = system;
- info->region = region;
- info->script = script;
- info->start = start;
- info->type = type;
- info->mission = 0;
-
- info->script.setSensitive(false);
-
- missions.append(info);
- }
- else {
- ok = false;
- }
- }
- }
- }
- }
- }
- while (term);
-
- loader->ReleaseBuffer(block);
-
- if (!ok)
- Unload();
-}
-
-void
-Campaign::LoadCustomMissions(DataLoader* loader)
-{
- bool ok = true;
- List<Text> files;
- loader->UseFileSystem(true);
- loader->ListFiles("*.*", files);
-
- for (int i = 0; i < files.size(); i++) {
- Text file = *files[i];
- file.setSensitive(false);
-
- if (file.contains(".def")) {
- BYTE* block = 0;
- const char* filename = file.data();
-
- loader->UseFileSystem(true);
- loader->LoadBuffer(filename, block, true);
- loader->UseFileSystem(Starshatter::UseFileSystem());
-
- if (strstr((const char*) block, "MISSION") == (const char*) block) {
- Text name;
- Text desc;
- Text system = "Unknown";
- Text region = "Unknown";
- int start = 0;
- int type = 0;
- int msn_id = 0;
-
- Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
- Term* term = parser.ParseTerm();
-
- if (!term) {
- Print("ERROR: could not parse '%s'\n", filename);
- continue;
- }
- else {
- TermText* file_type = term->isText();
- if (!file_type || file_type->value() != "MISSION") {
- Print("ERROR: invalid mission file '%s'\n", filename);
- term->print(10);
- continue;
- }
- }
-
- do {
- delete term; term = 0;
- term = parser.ParseTerm();
-
- if (term) {
- TermDef* def = term->isDef();
- if (def) {
- if (def->name()->value() == "name") {
- GetDefText(name, def, filename);
- name = Game::GetText(name);
- }
-
- else if (def->name()->value() == "type") {
- char typestr[64];
- GetDefText(typestr, def, filename);
- type = Mission::TypeFromName(typestr);
- }
-
- else if (def->name()->value() == "id")
- GetDefNumber(msn_id, def, filename);
-
- else if (def->name()->value() == "desc") {
- GetDefText(desc, def, filename);
- if (desc.length() > 0 && desc.length() < 32)
- desc = Game::GetText(desc);
- }
-
- else if (def->name()->value() == "system")
- GetDefText(system, def, filename);
-
- else if (def->name()->value() == "region")
- GetDefText(region, def, filename);
-
- else if (def->name()->value() == "start")
- GetDefTime(start, def, filename);
- }
- }
- }
- while (term);
-
- loader->ReleaseBuffer(block);
-
- if (strstr(filename, "custom") == filename) {
- sscanf_s(filename+6, "%d", &msn_id);
-
- if (msn_id <= i)
- msn_id = i+1;
- }
- else if (msn_id < 1) {
- msn_id = i+1;
- }
-
- MissionInfo* info = new(__FILE__,__LINE__) MissionInfo;
- if (info) {
- info->id = msn_id;
- info->name = name;
- info->type = type;
- info->description = desc;
- info->system = system;
- info->region = region;
- info->script = filename;
- info->start = start;
- info->mission = 0;
-
- info->script.setSensitive(false);
-
- missions.append(info);
- }
- else {
- ok = false;
- }
- }
- else {
- Print("Invalid Custom Mission File: '%s'\n", filename);
- }
-
- loader->ReleaseBuffer(block);
- }
- }
-
- files.destroy();
-
- if (!ok)
- Unload();
- else
- missions.sort();
-}
-
-void
-Campaign::LoadTemplateList(DataLoader* loader)
-{
- BYTE* block = 0;
- const char* filename = "Templates.def";
-
- loader->UseFileSystem(true);
- loader->LoadBuffer(filename, block, true);
- loader->UseFileSystem(Starshatter::UseFileSystem());
- Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
-
- Term* term = parser.ParseTerm();
-
- if (!term) {
- return;
- }
- else {
- TermText* file_type = term->isText();
- if (!file_type || file_type->value() != "TEMPLATELIST") {
- ::Print("WARNING: invalid template list file '%s'\n", filename);
- term->print(10);
- return;
- }
- }
-
- do {
- delete term; term = 0;
- term = parser.ParseTerm();
-
- if (term) {
- TermDef* def = term->isDef();
- if (def) {
- if (def->name()->value() == "mission") {
- if (!def->term() || !def->term()->isStruct()) {
- ::Print("WARNING: mission struct missing in '%s'\n", filename);
- }
- else {
- TermStruct* val = def->term()->isStruct();
-
- char name[256];
- char script[256];
- char region[256];
- int id = 0;
- int msn_type = 0;
- int grp_type = 0;
-
- int min_rank = 0;
- int max_rank = 0;
- int action_id = 0;
- int action_status = 0;
- int exec_once = 0;
- int start_before = TIME_NEVER;
- int start_after = 0;
-
- name[0] = 0;
- script[0] = 0;
- region[0] = 0;
-
- for (int i = 0; i < val->elements()->size(); i++) {
- TermDef* pdef = val->elements()->at(i)->isDef();
- if (pdef) {
- if (pdef->name()->value() == "id")
- GetDefNumber(id, pdef, filename);
-
- else if (pdef->name()->value() == "name")
- GetDefText(name, pdef, filename);
-
- else if (pdef->name()->value() == "script")
- GetDefText(script, pdef, filename);
-
- else if (pdef->name()->value() == "rgn" || pdef->name()->value() == "region")
- GetDefText(region, pdef, filename);
-
- else if (pdef->name()->value() == "type") {
- char typestr[64];
- GetDefText(typestr, pdef, filename);
- msn_type = Mission::TypeFromName(typestr);
- }
-
- else if (pdef->name()->value() == "group") {
- char typestr[64];
- GetDefText(typestr, pdef, filename);
- grp_type = CombatGroup::TypeFromName(typestr);
- }
-
- else if (pdef->name()->value() == "min_rank")
- GetDefNumber(min_rank, pdef, filename);
-
- else if (pdef->name()->value() == "max_rank")
- GetDefNumber(max_rank, pdef, filename);
-
- else if (pdef->name()->value() == "action_id")
- GetDefNumber(action_id, pdef, filename);
-
- else if (pdef->name()->value() == "action_status")
- GetDefNumber(action_status, pdef, filename);
-
- else if (pdef->name()->value() == "exec_once")
- GetDefNumber(exec_once, pdef, filename);
-
- else if (pdef->name()->value().contains("before")) {
- if (pdef->term()->isNumber()) {
- GetDefNumber(start_before, pdef, filename);
- }
- else {
- GetDefTime(start_before, pdef, filename);
- start_before -= ONE_DAY;
- }
- }
- else if (pdef->name()->value().contains("after")) {
- if (pdef->term()->isNumber()) {
- GetDefNumber(start_after, pdef, filename);
- }
- else {
- GetDefTime(start_after, pdef, filename);
- start_after -= ONE_DAY;
- }
- }
- }
- }
-
- MissionInfo* info = new(__FILE__,__LINE__) MissionInfo;
- if (info) {
- info->id = id;
- info->name = name;
- info->script = script;
- info->region = region;
- info->type = msn_type;
- info->min_rank = min_rank;
- info->max_rank = max_rank;
- info->action_id = action_id;
- info->action_status = action_status;
- info->exec_once = exec_once;
- info->start_before = start_before;
- info->start_after = start_after;
-
- info->script.setSensitive(false);
-
- TemplateList* templist = GetTemplateList(msn_type, grp_type);
-
- if (!templist) {
- templist = new(__FILE__,__LINE__) TemplateList;
- templist->mission_type = msn_type;
- templist->group_type = grp_type;
- templates.append(templist);
- }
-
- templist->missions.append(info);
- }
- }
- }
- }
- }
- }
- while (term);
-
- loader->ReleaseBuffer(block);
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Campaign::CreatePlanners()
-{
- if (planners.size() > 0)
- planners.destroy();
-
- CampaignPlan* p;
-
- // PLAN EVENT MUST BE FIRST PLANNER:
- p = new(__FILE__,__LINE__) CampaignPlanEvent(this);
- if (p)
- planners.append(p);
-
- p = new(__FILE__,__LINE__) CampaignPlanStrategic(this);
- if (p)
- planners.append(p);
-
- p = new(__FILE__,__LINE__) CampaignPlanAssignment(this);
- if (p)
- planners.append(p);
-
- p = new(__FILE__,__LINE__) CampaignPlanMovement(this);
- if (p)
- planners.append(p);
-
- p = new(__FILE__,__LINE__) CampaignPlanMission(this);
- if (p)
- planners.append(p);
-
- if (lockout > 0 && planners.size()) {
- ListIter<CampaignPlan> plan = planners;
- while (++plan)
- plan->SetLockout(lockout);
- }
-}
-
-// +--------------------------------------------------------------------+
-
-int
-Campaign::GetPlayerIFF()
-{
- int iff = 1;
-
- if (player_group)
- iff = player_group->GetIFF();
-
- return iff;
-}
-
-void
-Campaign::SetPlayerGroup(CombatGroup* pg)
-{
- if (player_group != pg) {
- ::Print("Campaign::SetPlayerGroup(%s)\n", pg ? pg->Name().data() : "0");
-
- // should verify that the player group is
- // actually part of this campaign, first!
-
- player_group = pg;
- player_unit = 0;
-
- // need to regenerate missions when changing
- // player combat group:
- if (IsDynamic()) {
- ::Print(" destroying mission list...\n");
- missions.destroy();
- }
- }
-}
-
-void
-Campaign::SetPlayerUnit(CombatUnit* unit)
-{
- if (player_unit != unit) {
- ::Print("Campaign::SetPlayerUnit(%s)\n", unit ? unit->Name().data() : "0");
-
- // should verify that the player unit is
- // actually part of this campaign, first!
-
- player_unit = unit;
-
- if (unit)
- player_group = unit->GetCombatGroup();
-
- // need to regenerate missions when changing
- // player combat unit:
- if (IsDynamic()) {
- ::Print(" destroying mission list...\n");
- missions.destroy();
- }
- }
-}
-
-// +--------------------------------------------------------------------+
-
-CombatZone*
-Campaign::GetZone(const char* rgn)
-{
- ListIter<CombatZone> z = zones;
- while (++z) {
- if (z->HasRegion(rgn))
- return z.value();
- }
-
- return 0;
-}
-
-StarSystem*
-Campaign::GetSystem(const char* sys)
-{
- return Galaxy::GetInstance()->GetSystem(sys);
-}
-
-Combatant*
-Campaign::GetCombatant(const char* cname)
-{
- ListIter<Combatant> iter = combatants;
- while (++iter) {
- Combatant* c = iter.value();
- if (!strcmp(c->Name(), cname))
- return c;
- }
-
- return 0;
-}
-
-// +--------------------------------------------------------------------+
-
-Mission*
-Campaign::GetMission()
-{
- return GetMission(mission_id);
-}
-
-Mission*
-Campaign::GetMission(int id)
-{
- if (id < 0) {
- ::Print("ERROR - Campaign::GetMission(%d) invalid mission id\n", id);
- return 0;
- }
-
- if (mission && mission->Identity() == id) {
- return mission;
- }
-
- MissionInfo* info = 0;
- for (int i = 0; !info && i < missions.size(); i++)
- if (missions[i]->id == id)
- info = missions[i];
-
- if (info) {
- if (!info->mission) {
- ::Print("Campaign::GetMission(%d) loading mission...\n", id);
- info->mission = new(__FILE__,__LINE__) Mission(id, info->script, path);
- if (info->mission)
- info->mission->Load();
- }
-
- if (IsDynamic()) {
- if (info->mission) {
- if (!_stricmp(info->mission->Situation(), "Unknown")) {
- ::Print("Campaign::GetMission(%d) generating sitrep...\n", id);
- CampaignSituationReport sitrep(this, info->mission);
- sitrep.GenerateSituationReport();
- }
- }
- else {
- ::Print("Campaign::GetMission(%d) could not find/load mission.\n", id);
- }
- }
-
- return info->mission;
- }
-
- return 0;
-}
-
-Mission*
-Campaign::GetMissionByFile(const char* filename)
-{
- if (!filename || !*filename) {
- ::Print("ERROR - Campaign::GetMissionByFile() invalid filename\n");
- return 0;
- }
-
- int id = 0;
- int maxid = 0;
- MissionInfo* info = 0;
-
- for (int i = 0; !info && i < missions.size(); i++) {
- MissionInfo* m = missions[i];
-
- if (m->id > maxid)
- maxid = m->id;
-
- if (m->script == filename)
- info = m;
- }
-
- if (info) {
- id = info->id;
-
- if (!info->mission) {
- ::Print("Campaign::GetMission(%d) loading mission...\n", id);
- info->mission = new(__FILE__,__LINE__) Mission(id, info->script, path);
- if (info->mission)
- info->mission->Load();
- }
-
- if (IsDynamic()) {
- if (info->mission) {
- if (!_stricmp(info->mission->Situation(), "Unknown")) {
- ::Print("Campaign::GetMission(%d) generating sitrep...\n", id);
- CampaignSituationReport sitrep(this, info->mission);
- sitrep.GenerateSituationReport();
- }
- }
- else {
- ::Print("Campaign::GetMission(%d) could not find/load mission.\n", id);
- }
- }
- }
-
- else {
- info = new(__FILE__,__LINE__) MissionInfo;
- if (info) {
- info->id = maxid+1;
- info->name = "New Custom Mission";
- info->script = filename;
- info->mission = new(__FILE__,__LINE__) Mission(info->id, info->script, "Mods/Missions/");
- info->mission->SetName(info->name);
-
- info->script.setSensitive(false);
-
- missions.append(info);
- }
- }
-
- return info->mission;
-}
-
-MissionInfo*
-Campaign::CreateNewMission()
-{
- int id = 0;
- int maxid = 0;
- MissionInfo* info = 0;
-
- if (campaign_id == MULTIPLAYER_MISSIONS)
- maxid = 10;
-
- for (int i = 0; !info && i < missions.size(); i++) {
- MissionInfo* m = missions[i];
-
- if (m->id > maxid)
- maxid = m->id;
- }
-
- char filename[64];
- sprintf_s(filename, "custom%03d.def", maxid+1);
-
- info = new(__FILE__,__LINE__) MissionInfo;
- if (info) {
- info->id = maxid+1;
- info->name = "New Custom Mission";
- info->script = filename;
- info->mission = new(__FILE__,__LINE__) Mission(info->id, filename, path);
- info->mission->SetName(info->name);
-
- info->script.setSensitive(false);
-
- missions.append(info);
- }
-
- return info;
-}
-
-void
-Campaign::DeleteMission(int id)
-{
- if (id < 0) {
- ::Print("ERROR - Campaign::DeleteMission(%d) invalid mission id\n", id);
- return;
- }
-
- MissionInfo* m = 0;
- int index = -1;
-
- for (int i = 0; !m && i < missions.size(); i++) {
- if (missions[i]->id == id) {
- m = missions[i];
- index = i;
- }
- }
-
- if (m) {
- char full_path[256];
-
- if (path[strlen(path)-1] == '/')
- sprintf_s(full_path, "%s%s", path, m->script.data());
- else
- sprintf_s(full_path, "%s/%s", path, m->script.data());
-
- DeleteFile(full_path);
- Load();
- }
-
- else {
- ::Print("ERROR - Campaign::DeleteMission(%d) could not find mission\n", id);
- }
-}
-
-MissionInfo*
-Campaign::GetMissionInfo(int id)
-{
- if (id < 0) {
- ::Print("ERROR - Campaign::GetMissionInfo(%d) invalid mission id\n", id);
- return 0;
- }
-
- MissionInfo* m = 0;
- for (int i = 0; !m && i < missions.size(); i++)
- if (missions[i]->id == id)
- m = missions[i];
-
- if (m) {
- if (!m->mission) {
- m->mission = new(__FILE__,__LINE__) Mission(id, m->script);
- if (m->mission)
- m->mission->Load();
- }
-
- return m;
- }
-
- else {
- ::Print("ERROR - Campaign::GetMissionInfo(%d) could not find mission\n", id);
- }
-
- return 0;
-}
-
-void
-Campaign::ReloadMission(int id)
-{
- if (mission && mission == net_mission) {
- delete net_mission;
- net_mission = 0;
- }
-
- mission = 0;
-
- if (id >= 0 && id < missions.size()) {
- MissionInfo* m = missions[id];
- delete m->mission;
- m->mission = 0;
- }
-}
-
-void
-Campaign::LoadNetMission(int id, const char* net_mission_script)
-{
- if (mission && mission == net_mission) {
- delete net_mission;
- net_mission = 0;
- }
-
- mission_id = id;
- mission = new(__FILE__,__LINE__) Mission(id);
-
- if (mission && mission->ParseMission(net_mission_script))
- mission->Validate();
-
- net_mission = mission;
-}
-
-// +--------------------------------------------------------------------+
-
-CombatAction*
-Campaign::FindAction(int action_id)
-{
- ListIter<CombatAction> iter = actions;
- while (++iter) {
- CombatAction* a = iter.value();
-
- if (a->Identity() == action_id)
- return a;
- }
-
- return 0;
-}
-
-// +--------------------------------------------------------------------+
-
-MissionInfo*
-Campaign::FindMissionTemplate(int mission_type, CombatGroup* player_group)
-{
- MissionInfo* info = 0;
-
- if (!player_group)
- return info;
-
- TemplateList* templates = GetTemplateList(mission_type, player_group->Type());
-
- if (!templates || !templates->missions.size())
- return info;
-
- int tries = 0;
- int msize = templates->missions.size();
-
- while (!info && tries < msize) {
- // get next template:
- int index = templates->index;
-
- if (index >= msize)
- index = 0;
-
- info = templates->missions[index];
- templates->index = index + 1;
- tries++;
-
- // validate the template:
- if (info) {
- if (info->action_id) {
- CombatAction* a = FindAction(info->action_id);
-
- if (a && a->Status() != info->action_status)
- info = 0;
- }
-
- if (info && !info->IsAvailable()) {
- info = 0;
- }
- }
- }
-
- return info;
-}
-
-// +--------------------------------------------------------------------+
-
-TemplateList*
-Campaign::GetTemplateList(int msn_type, int grp_type)
-{
- for (int i = 0; i < templates.size(); i++) {
- if (templates[i]->mission_type == msn_type &&
- templates[i]->group_type == grp_type)
- return templates[i];
- }
-
- return 0;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Campaign::SetMissionId(int id)
-{
- ::Print("Campaign::SetMissionId(%d)\n", id);
-
- if (id > 0)
- mission_id = id;
- else
- ::Print(" retaining mission id = %d\n", mission_id);
-}
-
-// +--------------------------------------------------------------------+
-
-double
-Campaign::Stardate()
-{
- return StarSystem::Stardate();
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Campaign::SelectDefaultPlayerGroup(CombatGroup* g, int type)
-{
- if (player_group || !g) return;
-
- if (g->Type() == type && !g->IsReserve() && g->Value() > 0) {
- player_group = g;
- player_unit = 0;
- return;
- }
-
- for (int i = 0; i < g->GetComponents().size(); i++)
- SelectDefaultPlayerGroup(g->GetComponents()[i], type);
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Campaign::Prep()
-{
- // if this is a new campaign,
- // create combatants from roster and template:
- if (IsDynamic() && combatants.isEmpty()) {
- DataLoader* loader = DataLoader::GetLoader();
- loader->SetDataPath(path);
- LoadCampaign(loader, true);
- }
-
- StarSystem::SetBaseTime(loadTime);
-
- // load scripted missions:
- if (IsScripted() && actions.isEmpty()) {
- DataLoader* loader = DataLoader::GetLoader();
- loader->SetDataPath(path);
- LoadCampaign(loader, true);
-
- ListIter<MissionInfo> m = missions;
- while (++m) {
- GetMission(m->id);
- }
- }
-
- CheckPlayerGroup();
-}
-
-void
-Campaign::Start()
-{
- ::Print("Campaign::Start()\n");
-
- Prep();
-
- // create planners:
- CreatePlanners();
- SetStatus(CAMPAIGN_ACTIVE);
-}
-
-void
-Campaign::ExecFrame()
-{
- if (InCutscene())
- return;
-
- time = Stardate() - startTime;
-
- if (status < CAMPAIGN_ACTIVE)
- return;
-
- if (IsDynamic()) {
- bool completed = false;
- ListIter<MissionInfo> m = missions;
- while (++m) {
- if (m->mission && m->mission->IsComplete()) {
- ::Print("Campaign::ExecFrame() completed mission %d '%s'\n", m->id, m->name.data());
- completed = true;
- }
- }
-
- if (completed) {
- ::Print("Campaign::ExecFrame() destroying mission list after completion...\n");
- missions.destroy();
-
- if (!player_group || player_group->IsFighterGroup())
- time += 10 * 3600;
- else
- time += 20 * 3600;
-
- StarSystem::SetBaseTime(startTime + time - Game::GameTime()/1000.0);
- }
- else {
- m.reset();
-
- while (++m) {
- if (m->start < time && !m->mission->IsActive()) {
- MissionInfo* info = m.removeItem();
-
- if (info)
- ::Print("Campaign::ExecFrame() deleting expired mission %d start: %d current: %d\n",
- info->id,
- info->start,
- (int) time);
-
- delete info;
- }
- }
- }
-
- // PLAN EVENT MUST BE FIRST PLANNER:
- if (loaded_from_savegame && planners.size() > 0) {
- CampaignPlanEvent* plan_event = (CampaignPlanEvent*) planners.first();
- plan_event->ExecScriptedEvents();
-
- loaded_from_savegame = false;
- }
-
- ListIter<CampaignPlan> plan = planners;
- while (++plan) {
- CheckPlayerGroup();
- plan->ExecFrame();
- }
-
- CheckPlayerGroup();
-
- // Auto save game AFTER planners have run!
- // This is done to ensure that campaign status
- // is properly recorded after winning or losing
- // the campaign.
-
- if (completed) {
- CampaignSaveGame save(this);
- save.SaveAuto();
- }
- }
- else {
- // PLAN EVENT MUST BE FIRST PLANNER:
- if (planners.size() > 0) {
- CampaignPlanEvent* plan_event = (CampaignPlanEvent*) planners.first();
- plan_event->ExecScriptedEvents();
- }
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Campaign::LockoutEvents(int seconds)
-{
- lockout = seconds;
-}
-
-void
-Campaign::CheckPlayerGroup()
-{
- if (!player_group || player_group->IsReserve() || player_group->CalcValue() < 1) {
- int player_iff = GetPlayerIFF();
- player_group = 0;
-
- CombatGroup* force = 0;
- for (int i = 0; i < combatants.size() && !force; i++) {
- if (combatants[i]->GetIFF() == player_iff) {
- force = combatants[i]->GetForce();
- }
- }
-
- if (force) {
- force->CalcValue();
- SelectDefaultPlayerGroup(force, CombatGroup::WING);
-
- if (!player_group)
- SelectDefaultPlayerGroup(force, CombatGroup::DESTROYER_SQUADRON);
- }
- }
-
- if (player_unit && player_unit->GetValue() < 1)
- SetPlayerUnit(0);
-}
-
-// +--------------------------------------------------------------------+
-
-void FPU2Extended();
-void FPURestore();
-
-void
-Campaign::StartMission()
-{
- Mission* m = GetMission();
-
- if (m) {
- ::Print("\n\nCampaign Start Mission - %d. '%s'\n", m->Identity(), m->Name());
-
- if (!scripted) {
- FPU2Extended();
-
- double gtime = (double) Game::GameTime() / 1000.0;
- double base = startTime + m->Start() - 15 - gtime;
-
- StarSystem::SetBaseTime(base);
-
- double current_time = Stardate() - startTime;
-
- char buffer[32];
- FormatDayTime(buffer, current_time);
- ::Print(" current time: %s\n", buffer);
-
- FormatDayTime(buffer, m->Start());
- ::Print(" mission start: %s\n", buffer);
- ::Print("\n");
- }
- }
-}
-
-void
-Campaign::RollbackMission()
-{
- ::Print("Campaign::RollbackMission()\n");
-
- Mission* m = GetMission();
-
- if (m) {
- if (!scripted) {
- FPU2Extended();
-
- double gtime = (double) Game::GameTime() / 1000.0;
- double base = startTime + m->Start() - 60 - gtime;
-
- StarSystem::SetBaseTime(base);
-
- double current_time = Stardate() - startTime;
- ::Print(" mission start: %d\n", m->Start());
- ::Print(" current time: %d\n", (int) current_time);
- }
-
- m->SetActive(false);
- m->SetComplete(false);
- }
-}
-
-// +--------------------------------------------------------------------+
-
-bool
-Campaign::InCutscene() const
-{
- Starshatter* stars = Starshatter::GetInstance();
- return stars ? stars->InCutscene() : false;
-}
-
-bool
-Campaign::IsDynamic() const
-{
- return campaign_id >= DYNAMIC_CAMPAIGN &&
- campaign_id < SINGLE_MISSIONS;
-}
-
-bool
-Campaign::IsTraining() const
-{
- return campaign_id == TRAINING_CAMPAIGN;
-}
-
-bool
-Campaign::IsScripted() const
-{
- return scripted;
-}
-
-bool
-Campaign::IsSequential() const
-{
- return sequential;
-}
-
-// +--------------------------------------------------------------------+
-
-static CombatGroup* FindGroup(CombatGroup* g, int type, int id)
-{
- if (g->Type() == type && g->GetID() == id)
- return g;
-
- CombatGroup* result = 0;
-
- ListIter<CombatGroup> subgroup = g->GetComponents();
- while (++subgroup && !result)
- result = FindGroup(subgroup.value(), type, id);
-
- return result;
-}
-
-CombatGroup*
-Campaign::FindGroup(int iff, int type, int id)
-{
- CombatGroup* result = 0;
-
- ListIter<Combatant> combatant = combatants;
- while (++combatant && !result) {
- if (combatant->GetIFF() == iff) {
- result = ::FindGroup(combatant->GetForce(), type, id);
- }
- }
-
- return result;
-}
-
-// +--------------------------------------------------------------------+
-
-static void FindGroups(CombatGroup* g, int type, CombatGroup* near_group,
-List<CombatGroup>& groups)
-{
- if (g->Type() == type && g->IntelLevel() > Intel::RESERVE) {
- if (!near_group || g->GetAssignedZone() == near_group->GetAssignedZone())
- groups.append(g);
- }
-
- ListIter<CombatGroup> subgroup = g->GetComponents();
- while (++subgroup)
- FindGroups(subgroup.value(), type, near_group, groups);
-}
-
-CombatGroup*
-Campaign::FindGroup(int iff, int type, CombatGroup* near_group)
-{
- CombatGroup* result = 0;
- List<CombatGroup> groups;
-
- ListIter<Combatant> combatant = combatants;
- while (++combatant) {
- if (combatant->GetIFF() == iff) {
- FindGroups(combatant->GetForce(), type, near_group, groups);
- }
- }
-
- if (groups.size() > 0) {
- int index = (int) Random(0, groups.size());
- if (index >= groups.size()) index = groups.size() - 1;
- result = groups[index];
- }
-
- return result;
-}
-
-// +--------------------------------------------------------------------+
-
-static void FindStrikeTargets(CombatGroup* g, CombatGroup* strike_group,
-List<CombatGroup>& groups)
-{
- if (!strike_group || !strike_group->GetAssignedZone()) return;
-
- if (g->IsStrikeTarget() && g->IntelLevel() > Intel::RESERVE) {
- if (strike_group->GetAssignedZone() == g->GetAssignedZone() ||
- strike_group->GetAssignedZone()->HasRegion(g->GetRegion()))
- groups.append(g);
- }
-
- ListIter<CombatGroup> subgroup = g->GetComponents();
- while (++subgroup)
- FindStrikeTargets(subgroup.value(), strike_group, groups);
-}
-
-CombatGroup*
-Campaign::FindStrikeTarget(int iff, CombatGroup* strike_group)
-{
- CombatGroup* result = 0;
-
- List<CombatGroup> groups;
-
- ListIter<Combatant> combatant = GetCombatants();
- while (++combatant) {
- if (combatant->GetIFF() != 0 && combatant->GetIFF() != iff) {
- FindStrikeTargets(combatant->GetForce(), strike_group, groups);
- }
- }
-
- if (groups.size() > 0) {
- int index = rand() % groups.size();
- result = groups[index];
- }
-
- return result;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Campaign::CommitExpiredActions()
-{
- ListIter<CombatAction> iter = actions;
- while (++iter) {
- CombatAction* a = iter.value();
-
- if (a->IsAvailable())
- a->SetStatus(CombatAction::COMPLETE);
- }
-
- updateTime = time;
-}
-
-// +--------------------------------------------------------------------+
-
-int
-Campaign::GetPlayerTeamScore()
-{
- int score_us = 0;
- int score_them = 0;
-
- if (player_group) {
- int iff = player_group->GetIFF();
-
- ListIter<Combatant> iter = combatants;
- while (++iter) {
- Combatant* c = iter.value();
-
- if (iff <= 1) {
- if (c->GetIFF() <= 1)
- score_us += c->Score();
- else
- score_them += c->Score();
- }
-
- else {
- if (c->GetIFF() <= 1)
- score_them += c->Score();
- else
- score_us += c->Score();
- }
- }
- }
-
- return score_us - score_them;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Campaign::SetStatus(int s)
-{
- status = s;
-
- // record the win in player profile:
- if (status == CAMPAIGN_SUCCESS) {
- Player* player = Player::GetCurrentPlayer();
-
- if (player)
- player->SetCampaignComplete(campaign_id);
- }
-
- if (status > CAMPAIGN_ACTIVE) {
- ::Print("Campaign::SetStatus() destroying mission list at campaign end\n");
- missions.destroy();
- }
-}
-
-// +--------------------------------------------------------------------+
-
-static void GetCombatUnits(CombatGroup* g, List<CombatUnit>& units)
-{
- if (g) {
- ListIter<CombatUnit> unit = g->GetUnits();
- while (++unit) {
- CombatUnit* u = unit.value();
-
- if (u->Count() - u->DeadCount() > 0)
- units.append(u);
- }
-
- ListIter<CombatGroup> comp = g->GetComponents();
- while (++comp) {
- CombatGroup* g2 = comp.value();
-
- if (!g2->IsReserve())
- GetCombatUnits(g2, units);
- }
- }
-}
-
-int
-Campaign::GetAllCombatUnits(int iff, List<CombatUnit>& units)
-{
- units.clear();
-
- ListIter<Combatant> iter = combatants;
- while (++iter) {
- Combatant* c = iter.value();
-
- if (iff < 0 || c->GetIFF() == iff) {
- GetCombatUnits(c->GetForce(), units);
- }
- }
-
- return units.size();
-}
+/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: Stars.exe + FILE: Campaign.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Campaign defines a strategic military scenario. +*/ + +#include "MemDebug.h" +#include "Campaign.h" +#include "CampaignPlanStrategic.h" +#include "CampaignPlanAssignment.h" +#include "CampaignPlanEvent.h" +#include "CampaignPlanMission.h" +#include "CampaignPlanMovement.h" +#include "CampaignSituationReport.h" +#include "CampaignSaveGame.h" +#include "Combatant.h" +#include "CombatAction.h" +#include "CombatEvent.h" +#include "CombatGroup.h" +#include "CombatRoster.h" +#include "CombatUnit.h" +#include "CombatZone.h" +#include "Galaxy.h" +#include "Mission.h" +#include "StarSystem.h" +#include "Starshatter.h" +#include "Player.h" + +#include "Game.h" +#include "Bitmap.h" +#include "DataLoader.h" +#include "ParseUtil.h" +#include "Random.h" +#include "FormatUtil.h" + + +const int TIME_NEVER = (int) 1e9; +const int ONE_DAY = (int) 24 * 3600; + +// +====================================================================+ + +MissionInfo::MissionInfo() + : mission(0), start(0), type(0), id(0), min_rank(0), max_rank(0), + action_id(0), action_status(0), exec_once(0), + start_before(TIME_NEVER), start_after(0) +{ } + +MissionInfo::~MissionInfo() +{ + delete mission; +} + +bool +MissionInfo::IsAvailable() +{ + Campaign* campaign = Campaign::GetCampaign(); + Player* player = Player::GetCurrentPlayer(); + CombatGroup* player_group = campaign->GetPlayerGroup(); + + if (campaign->GetTime() < start_after) + return false; + + if (campaign->GetTime() > start_before) + return false; + + if (region.length() && player_group->GetRegion() != region) + return false; + + if (min_rank && player->Rank() < min_rank) + return false; + + if (max_rank && player->Rank() > max_rank) + return false; + + if (exec_once < 0) + return false; + + if (exec_once > 0) + exec_once = -1; + + return true; +} + +// +====================================================================+ + +TemplateList::TemplateList() + : mission_type(0), group_type(0), index(0) +{ } + +TemplateList::~TemplateList() +{ + missions.destroy(); +} + +// +====================================================================+ + +static List<Campaign> campaigns; +static Campaign* current_campaign = 0; + +// +--------------------------------------------------------------------+ + +Campaign::Campaign(int id, const char* n) + : campaign_id(id), name(n), mission_id(-1), mission(0), net_mission(0), + scripted(false), sequential(false), time(0), startTime(0), loadTime(0), + player_group(0), player_unit(0), status(CAMPAIGN_INIT), lockout(0), + loaded_from_savegame(false) +{ + ZeroMemory(path, sizeof(path)); + Load(); +} + +Campaign::Campaign(int id, const char* n, const char* p) + : campaign_id(id), name(n), mission_id(-1), mission(0), net_mission(0), + scripted(false), sequential(false), time(0), startTime(0), loadTime(0), + player_group(0), player_unit(0), status(CAMPAIGN_INIT), lockout(0), + loaded_from_savegame(false) +{ + ZeroMemory(path, sizeof(path)); + strncpy(path, p, sizeof(path)); + Load(); +} + +// +--------------------------------------------------------------------+ + +Campaign::~Campaign() +{ + for (int i = 0; i < NUM_IMAGES; i++) + image[i].ClearImage(); + + delete net_mission; + + actions.destroy(); + events.destroy(); + missions.destroy(); + templates.destroy(); + planners.destroy(); + zones.destroy(); + combatants.destroy(); +} + +// +--------------------------------------------------------------------+ + +void +Campaign::Initialize() +{ + Campaign* c = 0; + DataLoader* loader = DataLoader::GetLoader(); + + for (int i = 1; i < 100; i++) { + char path[256]; + sprintf_s(path, "Campaigns/%02d/", i); + + loader->UseFileSystem(true); + loader->SetDataPath(path); + + if (loader->FindFile("campaign.def")) { + char txt[256]; + sprintf_s(txt, "Dynamic Campaign %02d", i); + c = new(__FILE__,__LINE__) Campaign(i, txt); + + if (c) { + campaigns.insertSort(c); + } + } + } + + c = new(__FILE__,__LINE__) Campaign(SINGLE_MISSIONS, "Single Missions"); + if (c) { + campaigns.insertSort(c); + current_campaign = c; + } + + c = new(__FILE__,__LINE__) Campaign(MULTIPLAYER_MISSIONS, "Multiplayer Missions"); + if (c) { + campaigns.insertSort(c); + } + + c = new(__FILE__,__LINE__) Campaign(CUSTOM_MISSIONS, "Custom Missions"); + if (c) { + campaigns.insertSort(c); + } +} + +void +Campaign::Close() +{ + Print("Campaign::Close() - destroying all campaigns\n"); + current_campaign = 0; + campaigns.destroy(); +} + +Campaign* +Campaign::GetCampaign() +{ + return current_campaign; +} + +Campaign* +Campaign::SelectCampaign(const char* name) +{ + Campaign* c = 0; + ListIter<Campaign> iter = campaigns; + + while (++iter && !c) { + if (!_stricmp(iter->Name(), name)) + c = iter.value(); + } + + if (c) { + Print("Campaign: Selected '%s'\n", c->Name()); + current_campaign = c; + } + else { + Print("Campaign: could not find '%s'\n", name); + } + + return c; +} + +Campaign* +Campaign::CreateCustomCampaign(const char* name, const char* path) +{ + int id = 0; + + if (name && *name && path && *path) { + ListIter<Campaign> iter = campaigns; + + while (++iter) { + Campaign* c = iter.value(); + if (c->GetCampaignId() >= id) + id = c->GetCampaignId() + 1; + + if (!strcmp(c->Name(), name)) { + Print("Campaign: custom campaign '%s' already exists.\n", name); + return 0; + } + } + } + + if (id == 0) + id = CUSTOM_MISSIONS + 1; + + Campaign* c = new(__FILE__,__LINE__) Campaign(id, name, path); + Print("Campaign: created custom campaign %d '%s'\n", id, name); + campaigns.append(c); + + return c; +} + +List<Campaign>& +Campaign::GetAllCampaigns() +{ + return campaigns; +} + +int +Campaign::GetLastCampaignId() +{ + int result = 0; + + for (int i = 0; i < campaigns.size(); i++) { + Campaign* c = campaigns.at(i); + + if (c->IsDynamic() && c->GetCampaignId() > result) { + result = c->GetCampaignId(); + } + } + + return result; +} + +// +--------------------------------------------------------------------+ + +CombatEvent* +Campaign::GetLastEvent() +{ + CombatEvent* result = 0; + + if (!events.isEmpty()) + result = events.last(); + + return result; +} + +// +--------------------------------------------------------------------+ + +int +Campaign::CountNewEvents() const +{ + int result = 0; + + for (int i = 0; i < events.size(); i++) + if (/*events[i]->Source() != CombatEvent::TACNET &&*/ !events[i]->Visited()) + result++; + + return result; +} + +// +--------------------------------------------------------------------+ + +void +Campaign::Clear() +{ + missions.destroy(); + planners.destroy(); + combatants.destroy(); + events.destroy(); + actions.destroy(); + + player_group = 0; + player_unit = 0; + + updateTime = time; +} + +// +--------------------------------------------------------------------+ + +void +Campaign::Load() +{ + // first, unload any existing data: + Unload(); + + if (!path[0]) { + // then load the campaign from files: + switch (campaign_id) { + case SINGLE_MISSIONS: strcpy_s(path, "Missions/"); break; + case CUSTOM_MISSIONS: strcpy_s(path, "Mods/Missions/"); break; + case MULTIPLAYER_MISSIONS: strcpy_s(path, "Multiplayer/"); break; + default: sprintf_s(path, "Campaigns/%02d/", campaign_id); break; + } + } + + DataLoader* loader = DataLoader::GetLoader(); + loader->UseFileSystem(true); + loader->SetDataPath(path); + systems.clear(); + + if (loader->FindFile("zones.def")) + zones.append(CombatZone::Load("zones.def")); + + for (int i = 0; i < zones.size(); i++) { + Text s = zones[i]->System(); + bool found = false; + + for (int n = 0; !found && n < systems.size(); n++) { + if (s == systems[n]->Name()) + found = true; + } + + if (!found) + systems.append(Galaxy::GetInstance()->GetSystem(s)); + } + + loader->UseFileSystem(Starshatter::UseFileSystem()); + + if (loader->FindFile("campaign.def")) + LoadCampaign(loader); + + if (campaign_id == CUSTOM_MISSIONS) { + loader->SetDataPath(path); + LoadCustomMissions(loader); + } + else { + bool found = false; + + if (loader->FindFile("missions.def")) { + loader->SetDataPath(path); + LoadMissionList(loader); + found = true; + } + + if (loader->FindFile("templates.def")) { + loader->SetDataPath(path); + LoadTemplateList(loader); + found = true; + } + + if (!found) { + loader->SetDataPath(path); + LoadCustomMissions(loader); + } + } + + loader->UseFileSystem(true); + loader->SetDataPath(path); + + if (loader->FindFile("image.pcx")) { + loader->LoadBitmap("image.pcx", image[ 0]); + loader->LoadBitmap("selected.pcx", image[ 1]); + loader->LoadBitmap("unavail.pcx", image[ 2]); + loader->LoadBitmap("banner.pcx", image[ 3]); + } + + loader->SetDataPath(0); + loader->UseFileSystem(Starshatter::UseFileSystem()); +} + +void +Campaign::Unload() +{ + SetStatus(CAMPAIGN_INIT); + + Game::ResetGameTime(); + StarSystem::SetBaseTime(0); + + startTime = Stardate(); + loadTime = startTime; + lockout = 0; + + for (int i = 0; i < NUM_IMAGES; i++) + image[i].ClearImage(); + + Clear(); + + zones.destroy(); +} + +void +Campaign::LoadCampaign(DataLoader* loader, bool full) +{ + BYTE* block = 0; + const char* filename = "campaign.def"; + + loader->UseFileSystem(true); + loader->LoadBuffer(filename, block, true); + loader->UseFileSystem(Starshatter::UseFileSystem()); + Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block)); + + Term* term = parser.ParseTerm(); + + if (!term) { + return; + } + else { + TermText* file_type = term->isText(); + if (!file_type || file_type->value() != "CAMPAIGN") { + return; + } + } + + do { + delete term; term = 0; + term = parser.ParseTerm(); + + if (term) { + TermDef* def = term->isDef(); + if (def) { + if (def->name()->value() == "name") { + if (!def->term() || !def->term()->isText()) { + ::Print("WARNING: name missing in '%s/%s'\n", loader->GetDataPath(), filename); + } + else { + name = def->term()->isText()->value(); + name = Game::GetText(name); + } + } + else if (def->name()->value() == "desc") { + if (!def->term() || !def->term()->isText()) { + ::Print("WARNING: description missing in '%s/%s'\n", loader->GetDataPath(), filename); + } + else { + description = def->term()->isText()->value(); + description = Game::GetText(description); + } + } + else if (def->name()->value() == "situation") { + if (!def->term() || !def->term()->isText()) { + ::Print("WARNING: situation missing in '%s/%s'\n", loader->GetDataPath(), filename); + } + else { + situation = def->term()->isText()->value(); + situation = Game::GetText(situation); + } + } + else if (def->name()->value() == "orders") { + if (!def->term() || !def->term()->isText()) { + ::Print("WARNING: orders missing in '%s/%s'\n", loader->GetDataPath(), filename); + } + else { + orders = def->term()->isText()->value(); + orders = Game::GetText(orders); + } + } + else if (def->name()->value() == "scripted") { + if (def->term() && def->term()->isBool()) { + scripted = def->term()->isBool()->value(); + } + } + else if (def->name()->value() == "sequential") { + if (def->term() && def->term()->isBool()) { + sequential = def->term()->isBool()->value(); + } + } + else if (full && def->name()->value() == "combatant") { + if (!def->term() || !def->term()->isStruct()) { + ::Print("WARNING: combatant struct missing in '%s/%s'\n", loader->GetDataPath(), filename); + } + else { + TermStruct* val = def->term()->isStruct(); + + char name[64]; + int iff = 0; + CombatGroup* force = 0; + CombatGroup* clone = 0; + + ZeroMemory(name, sizeof(name)); + + for (int i = 0; i < val->elements()->size(); i++) { + TermDef* pdef = val->elements()->at(i)->isDef(); + if (pdef) { + if (pdef->name()->value() == "name") { + GetDefText(name, pdef, filename); + + force = CombatRoster::GetInstance()->GetForce(name); + + if (force) + clone = force->Clone(false); // shallow copy + } + + else if (pdef->name()->value() == "group") { + ParseGroup(pdef->term()->isStruct(), force, clone, filename); + } + } + } + + loader->SetDataPath(path); + Combatant* c = new(__FILE__,__LINE__) Combatant(name, clone); + if (c) { + combatants.append(c); + } + else { + Unload(); + return; + } + } + } + else if (full && def->name()->value() == "action") { + if (!def->term() || !def->term()->isStruct()) { + ::Print("WARNING: action struct missing in '%s/%s'\n", loader->GetDataPath(), filename); + } + else { + TermStruct* val = def->term()->isStruct(); + ParseAction(val, filename); + } + } + } + } + } + while (term); + + loader->ReleaseBuffer(block); +} + +// +--------------------------------------------------------------------+ + +void +Campaign::ParseGroup(TermStruct* val, +CombatGroup* force, +CombatGroup* clone, +const char* filename) +{ + if (!val) { + ::Print("invalid combat group in campaign %s\n", name.data()); + return; + } + + int type = 0; + int id = 0; + + for (int i = 0; i < val->elements()->size(); i++) { + TermDef* pdef = val->elements()->at(i)->isDef(); + if (pdef) { + if (pdef->name()->value() == "type") { + char type_name[64]; + GetDefText(type_name, pdef, filename); + type = CombatGroup::TypeFromName(type_name); + } + + else if (pdef->name()->value() == "id") { + GetDefNumber(id, pdef, filename); + } + } + } + + if (type && id) { + CombatGroup* g = force->FindGroup(type, id); + + // found original group, now clone it over + if (g && g->GetParent()) { + CombatGroup* parent = CloneOver(force, clone, g->GetParent()); + parent->AddComponent(g->Clone()); + } + } +} + +// +--------------------------------------------------------------------+ + +void +Campaign::ParseAction(TermStruct* val, const char* filename) +{ + if (!val) { + ::Print("invalid action in campaign %s\n", name.data()); + return; + } + + int id = 0; + int type = 0; + int subtype = 0; + int opp_type = -1; + int team = 0; + int source = 0; + Vec3 loc(0.0f, 0.0f, 0.0f); + Text system; + Text region; + Text file; + Text image; + Text scene; + Text text; + int count = 1; + int start_before = TIME_NEVER; + int start_after = 0; + int min_rank = 0; + int max_rank = 100; + int delay = 0; + int probability = 100; + + int asset_type = 0; + int asset_id = 0; + int target_type = 0; + int target_id = 0; + int target_iff = 0; + + CombatAction* action = 0; + + for (int i = 0; i < val->elements()->size(); i++) { + TermDef* pdef = val->elements()->at(i)->isDef(); + if (pdef) { + if (pdef->name()->value() == "id") { + GetDefNumber(id, pdef, filename); + } + else if (pdef->name()->value() == "type") { + char txt[64]; + GetDefText(txt, pdef, filename); + type = CombatAction::TypeFromName(txt); + } + else if (pdef->name()->value() == "subtype") { + if (pdef->term()->isNumber()) { + GetDefNumber(subtype, pdef, filename); + } + + else if (pdef->term()->isText()) { + char txt[64]; + GetDefText(txt, pdef, filename); + + if (type == CombatAction::MISSION_TEMPLATE) { + subtype = Mission::TypeFromName(txt); + } + else if (type == CombatAction::COMBAT_EVENT) { + subtype = CombatEvent::TypeFromName(txt); + } + else if (type == CombatAction::INTEL_EVENT) { + subtype = Intel::IntelFromName(txt); + } + } + } + else if (pdef->name()->value() == "opp_type") { + if (pdef->term()->isNumber()) { + GetDefNumber(opp_type, pdef, filename); + } + + else if (pdef->term()->isText()) { + char txt[64]; + GetDefText(txt, pdef, filename); + + if (type == CombatAction::MISSION_TEMPLATE) { + opp_type = Mission::TypeFromName(txt); + } + } + } + else if (pdef->name()->value() == "source") { + char txt[64]; + GetDefText(txt, pdef, filename); + source = CombatEvent::SourceFromName(txt); + } + else if (pdef->name()->value() == "team") { + GetDefNumber(team, pdef, filename); + } + else if (pdef->name()->value() == "iff") { + GetDefNumber(team, pdef, filename); + } + else if (pdef->name()->value() == "count") { + GetDefNumber(count, pdef, filename); + } + else if (pdef->name()->value().contains("before")) { + if (pdef->term()->isNumber()) { + GetDefNumber(start_before, pdef, filename); + } + else { + GetDefTime(start_before, pdef, filename); + start_before -= ONE_DAY; + } + } + else if (pdef->name()->value().contains("after")) { + if (pdef->term()->isNumber()) { + GetDefNumber(start_after, pdef, filename); + } + else { + GetDefTime(start_after, pdef, filename); + start_after -= ONE_DAY; + } + } + else if (pdef->name()->value() == "min_rank") { + if (pdef->term()->isNumber()) { + GetDefNumber(min_rank, pdef, filename); + } + else { + char rank_name[64]; + GetDefText(rank_name, pdef, filename); + min_rank = Player::RankFromName(rank_name); + } + } + else if (pdef->name()->value() == "max_rank") { + if (pdef->term()->isNumber()) { + GetDefNumber(max_rank, pdef, filename); + } + else { + char rank_name[64]; + GetDefText(rank_name, pdef, filename); + max_rank = Player::RankFromName(rank_name); + } + } + else if (pdef->name()->value() == "delay") { + GetDefNumber(delay, pdef, filename); + } + else if (pdef->name()->value() == "probability") { + GetDefNumber(probability, pdef, filename); + } + else if (pdef->name()->value() == "asset_type") { + char type_name[64]; + GetDefText(type_name, pdef, filename); + asset_type = CombatGroup::TypeFromName(type_name); + } + else if (pdef->name()->value() == "target_type") { + char type_name[64]; + GetDefText(type_name, pdef, filename); + target_type = CombatGroup::TypeFromName(type_name); + } + else if (pdef->name()->value() == "location" || + pdef->name()->value() == "loc") { + GetDefVec(loc, pdef, filename); + } + else if (pdef->name()->value() == "system" || + pdef->name()->value() == "sys") { + GetDefText(system, pdef, filename); + } + else if (pdef->name()->value() == "region" || + pdef->name()->value() == "rgn" || + pdef->name()->value() == "zone") { + GetDefText(region, pdef, filename); + } + else if (pdef->name()->value() == "file") { + GetDefText(file, pdef, filename); + } + else if (pdef->name()->value() == "image") { + GetDefText(image, pdef, filename); + } + else if (pdef->name()->value() == "scene") { + GetDefText(scene, pdef, filename); + } + else if (pdef->name()->value() == "text") { + GetDefText(text, pdef, filename); + text = Game::GetText(text); + } + else if (pdef->name()->value() == "asset_id") { + GetDefNumber(asset_id, pdef, filename); + } + else if (pdef->name()->value() == "target_id") { + GetDefNumber(target_id, pdef, filename); + } + else if (pdef->name()->value() == "target_iff") { + GetDefNumber(target_iff, pdef, filename); + } + + else if (pdef->name()->value() == "asset_kill") { + if (!action) + action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team); + + if (action) { + char txt[64]; + GetDefText(txt, pdef, filename); + action->AssetKills().append(new (__FILE__,__LINE__) Text(txt)); + } + } + + else if (pdef->name()->value() == "target_kill") { + if (!action) + action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team); + + if (action) { + char txt[64]; + GetDefText(txt, pdef, filename); + action->TargetKills().append(new (__FILE__,__LINE__) Text(txt)); + } + } + + else if (pdef->name()->value() == "req") { + if (!action) + action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team); + + if (!pdef->term() || !pdef->term()->isStruct()) { + ::Print("WARNING: action req struct missing in '%s'\n", filename); + } + else if (action) { + TermStruct* val2 = pdef->term()->isStruct(); + + int act = 0; + int stat = CombatAction::COMPLETE; + bool not = false; + + Combatant* c1 = 0; + Combatant* c2 = 0; + int comp = 0; + int score = 0; + int intel = 0; + int gtype = 0; + int gid = 0; + + for (int i = 0; i < val2->elements()->size(); i++) { + TermDef* pdef2 = val2->elements()->at(i)->isDef(); + if (pdef2) { + if (pdef2->name()->value() == "action") { + GetDefNumber(act, pdef2, filename); + } + else if (pdef2->name()->value() == "status") { + char txt[64]; + GetDefText(txt, pdef2, filename); + stat = CombatAction::StatusFromName(txt); + } + else if (pdef2->name()->value() == "not") { + GetDefBool(not, pdef2, filename); + } + + else if (pdef2->name()->value() == "c1") { + char txt[64]; + GetDefText(txt, pdef2, filename); + c1 = GetCombatant(txt); + } + else if (pdef2->name()->value() == "c2") { + char txt[64]; + GetDefText(txt, pdef2, filename); + c2 = GetCombatant(txt); + } + else if (pdef2->name()->value() == "comp") { + char txt[64]; + GetDefText(txt, pdef2, filename); + comp = CombatActionReq::CompFromName(txt); + } + else if (pdef2->name()->value() == "score") { + GetDefNumber(score, pdef2, filename); + } + else if (pdef2->name()->value() == "intel") { + if (pdef2->term()->isNumber()) { + GetDefNumber(intel, pdef2, filename); + } + else if (pdef2->term()->isText()) { + char txt[64]; + GetDefText(txt, pdef2, filename); + intel = Intel::IntelFromName(txt); + } + } + else if (pdef2->name()->value() == "group_type") { + char type_name[64]; + GetDefText(type_name, pdef2, filename); + gtype = CombatGroup::TypeFromName(type_name); + } + else if (pdef2->name()->value() == "group_id") { + GetDefNumber(gid, pdef2, filename); + } + } + } + + if (act) + action->AddRequirement(act, stat, not); + + else if (gtype) + action->AddRequirement(c1, gtype, gid, comp, score, intel); + + else + action->AddRequirement(c1, c2, comp, score); + } + } + } + } + + if (!action) + action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team); + + if (action) { + action->SetSource(source); + action->SetOpposingType(opp_type); + action->SetLocation(loc); + action->SetSystem(system); + action->SetRegion(region); + action->SetFilename(file); + action->SetImageFile(image); + action->SetSceneFile(scene); + action->SetCount(count); + action->SetStartAfter(start_after); + action->SetStartBefore(start_before); + action->SetMinRank(min_rank); + action->SetMaxRank(max_rank); + action->SetDelay(delay); + action->SetProbability(probability); + + action->SetAssetId(asset_id); + action->SetAssetType(asset_type); + action->SetTargetId(target_id); + action->SetTargetIFF(target_iff); + action->SetTargetType(target_type); + action->SetText(text); + + actions.append(action); + } +} + +// +--------------------------------------------------------------------+ + +CombatGroup* +Campaign::CloneOver(CombatGroup* force, CombatGroup* clone, CombatGroup* group) +{ + CombatGroup* orig_parent = group->GetParent(); + + if (orig_parent) { + CombatGroup* clone_parent = clone->FindGroup(orig_parent->Type(), orig_parent->GetID()); + + if (!clone_parent) + clone_parent = CloneOver(force, clone, orig_parent); + + CombatGroup* new_clone = clone->FindGroup(group->Type(), group->GetID()); + + if (!new_clone) { + new_clone = group->Clone(false); + clone_parent->AddComponent(new_clone); + } + + return new_clone; + } + else { + return clone; + } +} + +// +--------------------------------------------------------------------+ + +void +Campaign::LoadMissionList(DataLoader* loader) +{ + bool ok = true; + BYTE* block = 0; + const char* filename = "Missions.def"; + + loader->UseFileSystem(true); + loader->LoadBuffer(filename, block, true); + loader->UseFileSystem(Starshatter::UseFileSystem()); + Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block)); + + Term* term = parser.ParseTerm(); + + if (!term) { + return; + } + else { + TermText* file_type = term->isText(); + if (!file_type || file_type->value() != "MISSIONLIST") { + ::Print("WARNING: invalid mission list file '%s'\n", filename); + term->print(10); + return; + } + } + + do { + delete term; term = 0; + term = parser.ParseTerm(); + + if (term) { + TermDef* def = term->isDef(); + if (def) { + if (def->name()->value() == "mission") { + if (!def->term() || !def->term()->isStruct()) { + ::Print("WARNING: mission struct missing in '%s'\n", filename); + } + else { + TermStruct* val = def->term()->isStruct(); + + int id = 0; + Text name; + Text desc; + char script[256]; + char system[256]; + char region[256]; + int start = 0; + int type = 0; + + ZeroMemory(script, sizeof(script)); + + strcpy_s(system, "Unknown"); + strcpy_s(region, "Unknown"); + + for (int i = 0; i < val->elements()->size(); i++) { + TermDef* pdef = val->elements()->at(i)->isDef(); + if (pdef) { + if (pdef->name()->value() == "id") + GetDefNumber(id, pdef, filename); + + else if (pdef->name()->value() == "name") { + GetDefText(name, pdef, filename); + name = Game::GetText(name); + } + + else if (pdef->name()->value() == "desc") { + GetDefText(desc, pdef, filename); + if (desc.length() > 0 && desc.length() < 32) + desc = Game::GetText(desc); + } + + else if (pdef->name()->value() == "start") + GetDefTime(start, pdef, filename); + + else if (pdef->name()->value() == "system") + GetDefText(system, pdef, filename); + + else if (pdef->name()->value() == "region") + GetDefText(region, pdef, filename); + + else if (pdef->name()->value() == "script") + GetDefText(script, pdef, filename); + + else if (pdef->name()->value() == "type") { + char typestr[64]; + GetDefText(typestr, pdef, filename); + type = Mission::TypeFromName(typestr); + } + } + } + + MissionInfo* info = new(__FILE__,__LINE__) MissionInfo; + if (info) { + info->id = id; + info->name = name; + info->description = desc; + info->system = system; + info->region = region; + info->script = script; + info->start = start; + info->type = type; + info->mission = 0; + + info->script.setSensitive(false); + + missions.append(info); + } + else { + ok = false; + } + } + } + } + } + } + while (term); + + loader->ReleaseBuffer(block); + + if (!ok) + Unload(); +} + +void +Campaign::LoadCustomMissions(DataLoader* loader) +{ + bool ok = true; + List<Text> files; + loader->UseFileSystem(true); + loader->ListFiles("*.*", files); + + for (int i = 0; i < files.size(); i++) { + Text file = *files[i]; + file.setSensitive(false); + + if (file.contains(".def")) { + BYTE* block = 0; + const char* filename = file.data(); + + loader->UseFileSystem(true); + loader->LoadBuffer(filename, block, true); + loader->UseFileSystem(Starshatter::UseFileSystem()); + + if (strstr((const char*) block, "MISSION") == (const char*) block) { + Text name; + Text desc; + Text system = "Unknown"; + Text region = "Unknown"; + int start = 0; + int type = 0; + int msn_id = 0; + + Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block)); + Term* term = parser.ParseTerm(); + + if (!term) { + Print("ERROR: could not parse '%s'\n", filename); + continue; + } + else { + TermText* file_type = term->isText(); + if (!file_type || file_type->value() != "MISSION") { + Print("ERROR: invalid mission file '%s'\n", filename); + term->print(10); + continue; + } + } + + do { + delete term; term = 0; + term = parser.ParseTerm(); + + if (term) { + TermDef* def = term->isDef(); + if (def) { + if (def->name()->value() == "name") { + GetDefText(name, def, filename); + name = Game::GetText(name); + } + + else if (def->name()->value() == "type") { + char typestr[64]; + GetDefText(typestr, def, filename); + type = Mission::TypeFromName(typestr); + } + + else if (def->name()->value() == "id") + GetDefNumber(msn_id, def, filename); + + else if (def->name()->value() == "desc") { + GetDefText(desc, def, filename); + if (desc.length() > 0 && desc.length() < 32) + desc = Game::GetText(desc); + } + + else if (def->name()->value() == "system") + GetDefText(system, def, filename); + + else if (def->name()->value() == "region") + GetDefText(region, def, filename); + + else if (def->name()->value() == "start") + GetDefTime(start, def, filename); + } + } + } + while (term); + + loader->ReleaseBuffer(block); + + if (strstr(filename, "custom") == filename) { + sscanf_s(filename+6, "%d", &msn_id); + + if (msn_id <= i) + msn_id = i+1; + } + else if (msn_id < 1) { + msn_id = i+1; + } + + MissionInfo* info = new(__FILE__,__LINE__) MissionInfo; + if (info) { + info->id = msn_id; + info->name = name; + info->type = type; + info->description = desc; + info->system = system; + info->region = region; + info->script = filename; + info->start = start; + info->mission = 0; + + info->script.setSensitive(false); + + missions.append(info); + } + else { + ok = false; + } + } + else { + Print("Invalid Custom Mission File: '%s'\n", filename); + } + + loader->ReleaseBuffer(block); + } + } + + files.destroy(); + + if (!ok) + Unload(); + else + missions.sort(); +} + +void +Campaign::LoadTemplateList(DataLoader* loader) +{ + BYTE* block = 0; + const char* filename = "Templates.def"; + + loader->UseFileSystem(true); + loader->LoadBuffer(filename, block, true); + loader->UseFileSystem(Starshatter::UseFileSystem()); + Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block)); + + Term* term = parser.ParseTerm(); + + if (!term) { + return; + } + else { + TermText* file_type = term->isText(); + if (!file_type || file_type->value() != "TEMPLATELIST") { + ::Print("WARNING: invalid template list file '%s'\n", filename); + term->print(10); + return; + } + } + + do { + delete term; term = 0; + term = parser.ParseTerm(); + + if (term) { + TermDef* def = term->isDef(); + if (def) { + if (def->name()->value() == "mission") { + if (!def->term() || !def->term()->isStruct()) { + ::Print("WARNING: mission struct missing in '%s'\n", filename); + } + else { + TermStruct* val = def->term()->isStruct(); + + char name[256]; + char script[256]; + char region[256]; + int id = 0; + int msn_type = 0; + int grp_type = 0; + + int min_rank = 0; + int max_rank = 0; + int action_id = 0; + int action_status = 0; + int exec_once = 0; + int start_before = TIME_NEVER; + int start_after = 0; + + name[0] = 0; + script[0] = 0; + region[0] = 0; + + for (int i = 0; i < val->elements()->size(); i++) { + TermDef* pdef = val->elements()->at(i)->isDef(); + if (pdef) { + if (pdef->name()->value() == "id") + GetDefNumber(id, pdef, filename); + + else if (pdef->name()->value() == "name") + GetDefText(name, pdef, filename); + + else if (pdef->name()->value() == "script") + GetDefText(script, pdef, filename); + + else if (pdef->name()->value() == "rgn" || pdef->name()->value() == "region") + GetDefText(region, pdef, filename); + + else if (pdef->name()->value() == "type") { + char typestr[64]; + GetDefText(typestr, pdef, filename); + msn_type = Mission::TypeFromName(typestr); + } + + else if (pdef->name()->value() == "group") { + char typestr[64]; + GetDefText(typestr, pdef, filename); + grp_type = CombatGroup::TypeFromName(typestr); + } + + else if (pdef->name()->value() == "min_rank") + GetDefNumber(min_rank, pdef, filename); + + else if (pdef->name()->value() == "max_rank") + GetDefNumber(max_rank, pdef, filename); + + else if (pdef->name()->value() == "action_id") + GetDefNumber(action_id, pdef, filename); + + else if (pdef->name()->value() == "action_status") + GetDefNumber(action_status, pdef, filename); + + else if (pdef->name()->value() == "exec_once") + GetDefNumber(exec_once, pdef, filename); + + else if (pdef->name()->value().contains("before")) { + if (pdef->term()->isNumber()) { + GetDefNumber(start_before, pdef, filename); + } + else { + GetDefTime(start_before, pdef, filename); + start_before -= ONE_DAY; + } + } + else if (pdef->name()->value().contains("after")) { + if (pdef->term()->isNumber()) { + GetDefNumber(start_after, pdef, filename); + } + else { + GetDefTime(start_after, pdef, filename); + start_after -= ONE_DAY; + } + } + } + } + + MissionInfo* info = new(__FILE__,__LINE__) MissionInfo; + if (info) { + info->id = id; + info->name = name; + info->script = script; + info->region = region; + info->type = msn_type; + info->min_rank = min_rank; + info->max_rank = max_rank; + info->action_id = action_id; + info->action_status = action_status; + info->exec_once = exec_once; + info->start_before = start_before; + info->start_after = start_after; + + info->script.setSensitive(false); + + TemplateList* templist = GetTemplateList(msn_type, grp_type); + + if (!templist) { + templist = new(__FILE__,__LINE__) TemplateList; + templist->mission_type = msn_type; + templist->group_type = grp_type; + templates.append(templist); + } + + templist->missions.append(info); + } + } + } + } + } + } + while (term); + + loader->ReleaseBuffer(block); +} + +// +--------------------------------------------------------------------+ + +void +Campaign::CreatePlanners() +{ + if (planners.size() > 0) + planners.destroy(); + + CampaignPlan* p; + + // PLAN EVENT MUST BE FIRST PLANNER: + p = new(__FILE__,__LINE__) CampaignPlanEvent(this); + if (p) + planners.append(p); + + p = new(__FILE__,__LINE__) CampaignPlanStrategic(this); + if (p) + planners.append(p); + + p = new(__FILE__,__LINE__) CampaignPlanAssignment(this); + if (p) + planners.append(p); + + p = new(__FILE__,__LINE__) CampaignPlanMovement(this); + if (p) + planners.append(p); + + p = new(__FILE__,__LINE__) CampaignPlanMission(this); + if (p) + planners.append(p); + + if (lockout > 0 && planners.size()) { + ListIter<CampaignPlan> plan = planners; + while (++plan) + plan->SetLockout(lockout); + } +} + +// +--------------------------------------------------------------------+ + +int +Campaign::GetPlayerIFF() +{ + int iff = 1; + + if (player_group) + iff = player_group->GetIFF(); + + return iff; +} + +void +Campaign::SetPlayerGroup(CombatGroup* pg) +{ + if (player_group != pg) { + ::Print("Campaign::SetPlayerGroup(%s)\n", pg ? pg->Name().data() : "0"); + + // should verify that the player group is + // actually part of this campaign, first! + + player_group = pg; + player_unit = 0; + + // need to regenerate missions when changing + // player combat group: + if (IsDynamic()) { + ::Print(" destroying mission list...\n"); + missions.destroy(); + } + } +} + +void +Campaign::SetPlayerUnit(CombatUnit* unit) +{ + if (player_unit != unit) { + ::Print("Campaign::SetPlayerUnit(%s)\n", unit ? unit->Name().data() : "0"); + + // should verify that the player unit is + // actually part of this campaign, first! + + player_unit = unit; + + if (unit) + player_group = unit->GetCombatGroup(); + + // need to regenerate missions when changing + // player combat unit: + if (IsDynamic()) { + ::Print(" destroying mission list...\n"); + missions.destroy(); + } + } +} + +// +--------------------------------------------------------------------+ + +CombatZone* +Campaign::GetZone(const char* rgn) +{ + ListIter<CombatZone> z = zones; + while (++z) { + if (z->HasRegion(rgn)) + return z.value(); + } + + return 0; +} + +StarSystem* +Campaign::GetSystem(const char* sys) +{ + return Galaxy::GetInstance()->GetSystem(sys); +} + +Combatant* +Campaign::GetCombatant(const char* cname) +{ + ListIter<Combatant> iter = combatants; + while (++iter) { + Combatant* c = iter.value(); + if (!strcmp(c->Name(), cname)) + return c; + } + + return 0; +} + +// +--------------------------------------------------------------------+ + +Mission* +Campaign::GetMission() +{ + return GetMission(mission_id); +} + +Mission* +Campaign::GetMission(int id) +{ + if (id < 0) { + ::Print("ERROR - Campaign::GetMission(%d) invalid mission id\n", id); + return 0; + } + + if (mission && mission->Identity() == id) { + return mission; + } + + MissionInfo* info = 0; + for (int i = 0; !info && i < missions.size(); i++) + if (missions[i]->id == id) + info = missions[i]; + + if (info) { + if (!info->mission) { + ::Print("Campaign::GetMission(%d) loading mission...\n", id); + info->mission = new(__FILE__,__LINE__) Mission(id, info->script, path); + if (info->mission) + info->mission->Load(); + } + + if (IsDynamic()) { + if (info->mission) { + if (!_stricmp(info->mission->Situation(), "Unknown")) { + ::Print("Campaign::GetMission(%d) generating sitrep...\n", id); + CampaignSituationReport sitrep(this, info->mission); + sitrep.GenerateSituationReport(); + } + } + else { + ::Print("Campaign::GetMission(%d) could not find/load mission.\n", id); + } + } + + return info->mission; + } + + return 0; +} + +Mission* +Campaign::GetMissionByFile(const char* filename) +{ + if (!filename || !*filename) { + ::Print("ERROR - Campaign::GetMissionByFile() invalid filename\n"); + return 0; + } + + int id = 0; + int maxid = 0; + MissionInfo* info = 0; + + for (int i = 0; !info && i < missions.size(); i++) { + MissionInfo* m = missions[i]; + + if (m->id > maxid) + maxid = m->id; + + if (m->script == filename) + info = m; + } + + if (info) { + id = info->id; + + if (!info->mission) { + ::Print("Campaign::GetMission(%d) loading mission...\n", id); + info->mission = new(__FILE__,__LINE__) Mission(id, info->script, path); + if (info->mission) + info->mission->Load(); + } + + if (IsDynamic()) { + if (info->mission) { + if (!_stricmp(info->mission->Situation(), "Unknown")) { + ::Print("Campaign::GetMission(%d) generating sitrep...\n", id); + CampaignSituationReport sitrep(this, info->mission); + sitrep.GenerateSituationReport(); + } + } + else { + ::Print("Campaign::GetMission(%d) could not find/load mission.\n", id); + } + } + } + + else { + info = new(__FILE__,__LINE__) MissionInfo; + if (info) { + info->id = maxid+1; + info->name = "New Custom Mission"; + info->script = filename; + info->mission = new(__FILE__,__LINE__) Mission(info->id, info->script, "Mods/Missions/"); + info->mission->SetName(info->name); + + info->script.setSensitive(false); + + missions.append(info); + } + } + + return info->mission; +} + +MissionInfo* +Campaign::CreateNewMission() +{ + int id = 0; + int maxid = 0; + MissionInfo* info = 0; + + if (campaign_id == MULTIPLAYER_MISSIONS) + maxid = 10; + + for (int i = 0; !info && i < missions.size(); i++) { + MissionInfo* m = missions[i]; + + if (m->id > maxid) + maxid = m->id; + } + + char filename[64]; + sprintf_s(filename, "custom%03d.def", maxid+1); + + info = new(__FILE__,__LINE__) MissionInfo; + if (info) { + info->id = maxid+1; + info->name = "New Custom Mission"; + info->script = filename; + info->mission = new(__FILE__,__LINE__) Mission(info->id, filename, path); + info->mission->SetName(info->name); + + info->script.setSensitive(false); + + missions.append(info); + } + + return info; +} + +void +Campaign::DeleteMission(int id) +{ + if (id < 0) { + ::Print("ERROR - Campaign::DeleteMission(%d) invalid mission id\n", id); + return; + } + + MissionInfo* m = 0; + int index = -1; + + for (int i = 0; !m && i < missions.size(); i++) { + if (missions[i]->id == id) { + m = missions[i]; + index = i; + } + } + + if (m) { + char full_path[256]; + + if (path[strlen(path)-1] == '/') + sprintf_s(full_path, "%s%s", path, m->script.data()); + else + sprintf_s(full_path, "%s/%s", path, m->script.data()); + + DeleteFile(full_path); + Load(); + } + + else { + ::Print("ERROR - Campaign::DeleteMission(%d) could not find mission\n", id); + } +} + +MissionInfo* +Campaign::GetMissionInfo(int id) +{ + if (id < 0) { + ::Print("ERROR - Campaign::GetMissionInfo(%d) invalid mission id\n", id); + return 0; + } + + MissionInfo* m = 0; + for (int i = 0; !m && i < missions.size(); i++) + if (missions[i]->id == id) + m = missions[i]; + + if (m) { + if (!m->mission) { + m->mission = new(__FILE__,__LINE__) Mission(id, m->script); + if (m->mission) + m->mission->Load(); + } + + return m; + } + + else { + ::Print("ERROR - Campaign::GetMissionInfo(%d) could not find mission\n", id); + } + + return 0; +} + +void +Campaign::ReloadMission(int id) +{ + if (mission && mission == net_mission) { + delete net_mission; + net_mission = 0; + } + + mission = 0; + + if (id >= 0 && id < missions.size()) { + MissionInfo* m = missions[id]; + delete m->mission; + m->mission = 0; + } +} + +void +Campaign::LoadNetMission(int id, const char* net_mission_script) +{ + if (mission && mission == net_mission) { + delete net_mission; + net_mission = 0; + } + + mission_id = id; + mission = new(__FILE__,__LINE__) Mission(id); + + if (mission && mission->ParseMission(net_mission_script)) + mission->Validate(); + + net_mission = mission; +} + +// +--------------------------------------------------------------------+ + +CombatAction* +Campaign::FindAction(int action_id) +{ + ListIter<CombatAction> iter = actions; + while (++iter) { + CombatAction* a = iter.value(); + + if (a->Identity() == action_id) + return a; + } + + return 0; +} + +// +--------------------------------------------------------------------+ + +MissionInfo* +Campaign::FindMissionTemplate(int mission_type, CombatGroup* player_group) +{ + MissionInfo* info = 0; + + if (!player_group) + return info; + + TemplateList* templates = GetTemplateList(mission_type, player_group->Type()); + + if (!templates || !templates->missions.size()) + return info; + + int tries = 0; + int msize = templates->missions.size(); + + while (!info && tries < msize) { + // get next template: + int index = templates->index; + + if (index >= msize) + index = 0; + + info = templates->missions[index]; + templates->index = index + 1; + tries++; + + // validate the template: + if (info) { + if (info->action_id) { + CombatAction* a = FindAction(info->action_id); + + if (a && a->Status() != info->action_status) + info = 0; + } + + if (info && !info->IsAvailable()) { + info = 0; + } + } + } + + return info; +} + +// +--------------------------------------------------------------------+ + +TemplateList* +Campaign::GetTemplateList(int msn_type, int grp_type) +{ + for (int i = 0; i < templates.size(); i++) { + if (templates[i]->mission_type == msn_type && + templates[i]->group_type == grp_type) + return templates[i]; + } + + return 0; +} + +// +--------------------------------------------------------------------+ + +void +Campaign::SetMissionId(int id) +{ + ::Print("Campaign::SetMissionId(%d)\n", id); + + if (id > 0) + mission_id = id; + else + ::Print(" retaining mission id = %d\n", mission_id); +} + +// +--------------------------------------------------------------------+ + +double +Campaign::Stardate() +{ + return StarSystem::Stardate(); +} + +// +--------------------------------------------------------------------+ + +void +Campaign::SelectDefaultPlayerGroup(CombatGroup* g, int type) +{ + if (player_group || !g) return; + + if (g->Type() == type && !g->IsReserve() && g->Value() > 0) { + player_group = g; + player_unit = 0; + return; + } + + for (int i = 0; i < g->GetComponents().size(); i++) + SelectDefaultPlayerGroup(g->GetComponents()[i], type); +} + +// +--------------------------------------------------------------------+ + +void +Campaign::Prep() +{ + // if this is a new campaign, + // create combatants from roster and template: + if (IsDynamic() && combatants.isEmpty()) { + DataLoader* loader = DataLoader::GetLoader(); + loader->SetDataPath(path); + LoadCampaign(loader, true); + } + + StarSystem::SetBaseTime(loadTime); + + // load scripted missions: + if (IsScripted() && actions.isEmpty()) { + DataLoader* loader = DataLoader::GetLoader(); + loader->SetDataPath(path); + LoadCampaign(loader, true); + + ListIter<MissionInfo> m = missions; + while (++m) { + GetMission(m->id); + } + } + + CheckPlayerGroup(); +} + +void +Campaign::Start() +{ + ::Print("Campaign::Start()\n"); + + Prep(); + + // create planners: + CreatePlanners(); + SetStatus(CAMPAIGN_ACTIVE); +} + +void +Campaign::ExecFrame() +{ + if (InCutscene()) + return; + + time = Stardate() - startTime; + + if (status < CAMPAIGN_ACTIVE) + return; + + if (IsDynamic()) { + bool completed = false; + ListIter<MissionInfo> m = missions; + while (++m) { + if (m->mission && m->mission->IsComplete()) { + ::Print("Campaign::ExecFrame() completed mission %d '%s'\n", m->id, m->name.data()); + completed = true; + } + } + + if (completed) { + ::Print("Campaign::ExecFrame() destroying mission list after completion...\n"); + missions.destroy(); + + if (!player_group || player_group->IsFighterGroup()) + time += 10 * 3600; + else + time += 20 * 3600; + + StarSystem::SetBaseTime(startTime + time - Game::GameTime()/1000.0); + } + else { + m.reset(); + + while (++m) { + if (m->start < time && !m->mission->IsActive()) { + MissionInfo* info = m.removeItem(); + + if (info) + ::Print("Campaign::ExecFrame() deleting expired mission %d start: %d current: %d\n", + info->id, + info->start, + (int) time); + + delete info; + } + } + } + + // PLAN EVENT MUST BE FIRST PLANNER: + if (loaded_from_savegame && planners.size() > 0) { + CampaignPlanEvent* plan_event = (CampaignPlanEvent*) planners.first(); + plan_event->ExecScriptedEvents(); + + loaded_from_savegame = false; + } + + ListIter<CampaignPlan> plan = planners; + while (++plan) { + CheckPlayerGroup(); + plan->ExecFrame(); + } + + CheckPlayerGroup(); + + // Auto save game AFTER planners have run! + // This is done to ensure that campaign status + // is properly recorded after winning or losing + // the campaign. + + if (completed) { + CampaignSaveGame save(this); + save.SaveAuto(); + } + } + else { + // PLAN EVENT MUST BE FIRST PLANNER: + if (planners.size() > 0) { + CampaignPlanEvent* plan_event = (CampaignPlanEvent*) planners.first(); + plan_event->ExecScriptedEvents(); + } + } +} + +// +--------------------------------------------------------------------+ + +void +Campaign::LockoutEvents(int seconds) +{ + lockout = seconds; +} + +void +Campaign::CheckPlayerGroup() +{ + if (!player_group || player_group->IsReserve() || player_group->CalcValue() < 1) { + int player_iff = GetPlayerIFF(); + player_group = 0; + + CombatGroup* force = 0; + for (int i = 0; i < combatants.size() && !force; i++) { + if (combatants[i]->GetIFF() == player_iff) { + force = combatants[i]->GetForce(); + } + } + + if (force) { + force->CalcValue(); + SelectDefaultPlayerGroup(force, CombatGroup::WING); + + if (!player_group) + SelectDefaultPlayerGroup(force, CombatGroup::DESTROYER_SQUADRON); + } + } + + if (player_unit && player_unit->GetValue() < 1) + SetPlayerUnit(0); +} + +// +--------------------------------------------------------------------+ + +void FPU2Extended(); +void FPURestore(); + +void +Campaign::StartMission() +{ + Mission* m = GetMission(); + + if (m) { + ::Print("\n\nCampaign Start Mission - %d. '%s'\n", m->Identity(), m->Name()); + + if (!scripted) { + FPU2Extended(); + + double gtime = (double) Game::GameTime() / 1000.0; + double base = startTime + m->Start() - 15 - gtime; + + StarSystem::SetBaseTime(base); + + double current_time = Stardate() - startTime; + + char buffer[32]; + FormatDayTime(buffer, current_time); + ::Print(" current time: %s\n", buffer); + + FormatDayTime(buffer, m->Start()); + ::Print(" mission start: %s\n", buffer); + ::Print("\n"); + } + } +} + +void +Campaign::RollbackMission() +{ + ::Print("Campaign::RollbackMission()\n"); + + Mission* m = GetMission(); + + if (m) { + if (!scripted) { + FPU2Extended(); + + double gtime = (double) Game::GameTime() / 1000.0; + double base = startTime + m->Start() - 60 - gtime; + + StarSystem::SetBaseTime(base); + + double current_time = Stardate() - startTime; + ::Print(" mission start: %d\n", m->Start()); + ::Print(" current time: %d\n", (int) current_time); + } + + m->SetActive(false); + m->SetComplete(false); + } +} + +// +--------------------------------------------------------------------+ + +bool +Campaign::InCutscene() const +{ + Starshatter* stars = Starshatter::GetInstance(); + return stars ? stars->InCutscene() : false; +} + +bool +Campaign::IsDynamic() const +{ + return campaign_id >= DYNAMIC_CAMPAIGN && + campaign_id < SINGLE_MISSIONS; +} + +bool +Campaign::IsTraining() const +{ + return campaign_id == TRAINING_CAMPAIGN; +} + +bool +Campaign::IsScripted() const +{ + return scripted; +} + +bool +Campaign::IsSequential() const +{ + return sequential; +} + +// +--------------------------------------------------------------------+ + +static CombatGroup* FindGroup(CombatGroup* g, int type, int id) +{ + if (g->Type() == type && g->GetID() == id) + return g; + + CombatGroup* result = 0; + + ListIter<CombatGroup> subgroup = g->GetComponents(); + while (++subgroup && !result) + result = FindGroup(subgroup.value(), type, id); + + return result; +} + +CombatGroup* +Campaign::FindGroup(int iff, int type, int id) +{ + CombatGroup* result = 0; + + ListIter<Combatant> combatant = combatants; + while (++combatant && !result) { + if (combatant->GetIFF() == iff) { + result = ::FindGroup(combatant->GetForce(), type, id); + } + } + + return result; +} + +// +--------------------------------------------------------------------+ + +static void FindGroups(CombatGroup* g, int type, CombatGroup* near_group, +List<CombatGroup>& groups) +{ + if (g->Type() == type && g->IntelLevel() > Intel::RESERVE) { + if (!near_group || g->GetAssignedZone() == near_group->GetAssignedZone()) + groups.append(g); + } + + ListIter<CombatGroup> subgroup = g->GetComponents(); + while (++subgroup) + FindGroups(subgroup.value(), type, near_group, groups); +} + +CombatGroup* +Campaign::FindGroup(int iff, int type, CombatGroup* near_group) +{ + CombatGroup* result = 0; + List<CombatGroup> groups; + + ListIter<Combatant> combatant = combatants; + while (++combatant) { + if (combatant->GetIFF() == iff) { + FindGroups(combatant->GetForce(), type, near_group, groups); + } + } + + if (groups.size() > 0) { + int index = (int) Random(0, groups.size()); + if (index >= groups.size()) index = groups.size() - 1; + result = groups[index]; + } + + return result; +} + +// +--------------------------------------------------------------------+ + +static void FindStrikeTargets(CombatGroup* g, CombatGroup* strike_group, +List<CombatGroup>& groups) +{ + if (!strike_group || !strike_group->GetAssignedZone()) return; + + if (g->IsStrikeTarget() && g->IntelLevel() > Intel::RESERVE) { + if (strike_group->GetAssignedZone() == g->GetAssignedZone() || + strike_group->GetAssignedZone()->HasRegion(g->GetRegion())) + groups.append(g); + } + + ListIter<CombatGroup> subgroup = g->GetComponents(); + while (++subgroup) + FindStrikeTargets(subgroup.value(), strike_group, groups); +} + +CombatGroup* +Campaign::FindStrikeTarget(int iff, CombatGroup* strike_group) +{ + CombatGroup* result = 0; + + List<CombatGroup> groups; + + ListIter<Combatant> combatant = GetCombatants(); + while (++combatant) { + if (combatant->GetIFF() != 0 && combatant->GetIFF() != iff) { + FindStrikeTargets(combatant->GetForce(), strike_group, groups); + } + } + + if (groups.size() > 0) { + int index = rand() % groups.size(); + result = groups[index]; + } + + return result; +} + +// +--------------------------------------------------------------------+ + +void +Campaign::CommitExpiredActions() +{ + ListIter<CombatAction> iter = actions; + while (++iter) { + CombatAction* a = iter.value(); + + if (a->IsAvailable()) + a->SetStatus(CombatAction::COMPLETE); + } + + updateTime = time; +} + +// +--------------------------------------------------------------------+ + +int +Campaign::GetPlayerTeamScore() +{ + int score_us = 0; + int score_them = 0; + + if (player_group) { + int iff = player_group->GetIFF(); + + ListIter<Combatant> iter = combatants; + while (++iter) { + Combatant* c = iter.value(); + + if (iff <= 1) { + if (c->GetIFF() <= 1) + score_us += c->Score(); + else + score_them += c->Score(); + } + + else { + if (c->GetIFF() <= 1) + score_them += c->Score(); + else + score_us += c->Score(); + } + } + } + + return score_us - score_them; +} + +// +--------------------------------------------------------------------+ + +void +Campaign::SetStatus(int s) +{ + status = s; + + // record the win in player profile: + if (status == CAMPAIGN_SUCCESS) { + Player* player = Player::GetCurrentPlayer(); + + if (player) + player->SetCampaignComplete(campaign_id); + } + + if (status > CAMPAIGN_ACTIVE) { + ::Print("Campaign::SetStatus() destroying mission list at campaign end\n"); + missions.destroy(); + } +} + +// +--------------------------------------------------------------------+ + +static void GetCombatUnits(CombatGroup* g, List<CombatUnit>& units) +{ + if (g) { + ListIter<CombatUnit> unit = g->GetUnits(); + while (++unit) { + CombatUnit* u = unit.value(); + + if (u->Count() - u->DeadCount() > 0) + units.append(u); + } + + ListIter<CombatGroup> comp = g->GetComponents(); + while (++comp) { + CombatGroup* g2 = comp.value(); + + if (!g2->IsReserve()) + GetCombatUnits(g2, units); + } + } +} + +int +Campaign::GetAllCombatUnits(int iff, List<CombatUnit>& units) +{ + units.clear(); + + ListIter<Combatant> iter = combatants; + while (++iter) { + Combatant* c = iter.value(); + + if (iff < 0 || c->GetIFF() == iff) { + GetCombatUnits(c->GetForce(), units); + } + } + + return units.size(); +} |