summaryrefslogtreecommitdiffhomepage
path: root/StarsEx/CampaignPlanEvent.cpp
diff options
context:
space:
mode:
authorAki <please@ignore.pl>2022-04-01 21:23:39 +0200
committerAki <please@ignore.pl>2022-04-01 21:23:39 +0200
commit3c487c5cd69c53d6fea948643c0a76df03516605 (patch)
tree72730c7b8b26a5ef8fc9a987ec4c16129efd5aac /StarsEx/CampaignPlanEvent.cpp
parent8f353abd0bfe18baddd8a8250ab7c4f2d1c83a6e (diff)
downloadstarshatter-3c487c5cd69c53d6fea948643c0a76df03516605.zip
starshatter-3c487c5cd69c53d6fea948643c0a76df03516605.tar.gz
starshatter-3c487c5cd69c53d6fea948643c0a76df03516605.tar.bz2
Moved Stars45 to StarsEx
Diffstat (limited to 'StarsEx/CampaignPlanEvent.cpp')
-rw-r--r--StarsEx/CampaignPlanEvent.cpp1274
1 files changed, 1274 insertions, 0 deletions
diff --git a/StarsEx/CampaignPlanEvent.cpp b/StarsEx/CampaignPlanEvent.cpp
new file mode 100644
index 0000000..f487984
--- /dev/null
+++ b/StarsEx/CampaignPlanEvent.cpp
@@ -0,0 +1,1274 @@
+/* Starshatter: The Open Source Project
+ Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors
+ Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors
+ Copyright (c) 1997-2006, Destroyer Studios LLC.
+
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignPlanEvent generates simulated combat
+ events based on a statistical analysis of the
+ combatants within the context of a dynamic
+ campaign.
+*/
+
+#include "CampaignPlanEvent.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatAction.h"
+#include "CombatAssignment.h"
+#include "CombatEvent.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Mission.h"
+#include "Random.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+
+CampaignPlanEvent::CampaignPlanEvent(Campaign* c)
+ : CampaignPlan(c), event_time(0)
+{
+ if (campaign) {
+ event_time = (int) campaign->GetTime();
+ }
+}
+
+CampaignPlanEvent::~CampaignPlanEvent()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanEvent::ExecFrame()
+{
+ if (campaign && campaign->IsActive()) {
+ if (!campaign->GetPlayerGroup())
+ return;
+
+ // once every twenty minutes is plenty:
+ if (Campaign::Stardate() - exec_time < 1200)
+ return;
+
+ if (!ExecScriptedEvents())
+ ExecStatisticalEvents();
+
+ exec_time = Campaign::Stardate();
+ event_time = (int) campaign->GetTime();
+ }
+}
+
+void
+CampaignPlanEvent::SetLockout(int seconds)
+{
+ exec_time = Campaign::Stardate() + seconds;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CampaignPlanEvent::ExecScriptedEvents()
+{
+ bool scripted_event = false;
+
+ if (campaign) {
+ ListIter<CombatAction> iter = campaign->GetActions();
+ while (++iter) {
+ CombatAction* action = iter.value();
+
+ if (action->IsAvailable()) {
+
+ switch (action->Type()) {
+ case CombatAction::COMBAT_EVENT:
+ {
+ CombatEvent* event = new
+ CombatEvent(campaign,
+ action->Subtype(),
+ (int) campaign->GetTime(),
+ action->GetIFF(),
+ action->Source(),
+ action->Region());
+
+ if (!event)
+ return false;
+
+ event->SetTitle(action->GetText());
+
+ if (*action->Filename() != 0)
+ event->SetFilename(action->Filename());
+
+ if (*action->ImageFile() != 0)
+ event->SetImageFile(action->ImageFile());
+
+ if (*action->SceneFile() != 0)
+ event->SetSceneFile(action->SceneFile());
+
+ event->Load();
+
+ ProsecuteKills(action);
+ campaign->GetEvents().append(event);
+
+ action->FireAction();
+ scripted_event = true;
+
+ if (action->Subtype() == CombatEvent::CAMPAIGN_END) {
+ ::Print(">>>>> CAMPAIGN %d END (Action %03d) <<<<<\n", campaign->GetCampaignId(), action->Identity());
+ campaign->SetStatus(Campaign::CAMPAIGN_SUCCESS);
+ }
+
+ else if (action->Subtype() == CombatEvent::CAMPAIGN_FAIL) {
+ ::Print(">>>>> CAMPAIGN %d FAIL (Action %03d) <<<<<\n", campaign->GetCampaignId(), action->Identity());
+ campaign->SetStatus(Campaign::CAMPAIGN_FAILED);
+ }
+ }
+ break;
+
+ case CombatAction::STRATEGIC_DIRECTIVE:
+ {
+ CombatGroup* g = campaign->FindGroup(action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ if (g) {
+ g->SetStrategicDirection(action->GetText());
+ action->FireAction();
+ }
+ else {
+ action->FailAction();
+ }
+
+ scripted_event = true;
+ }
+ break;
+
+ case CombatAction::CAMPAIGN_SITUATION:
+ {
+ campaign->SetSituation(action->GetText());
+ action->FireAction();
+ scripted_event = true;
+ }
+ break;
+
+ case CombatAction::CAMPAIGN_ORDERS:
+ {
+ campaign->SetOrders(action->GetText());
+ action->FireAction();
+ scripted_event = true;
+ }
+ break;
+
+ case CombatAction::INTEL_EVENT:
+ {
+ CombatGroup* g = campaign->FindGroup(action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ if (g) {
+ g->SetIntelLevel(action->Subtype());
+ action->FireAction();
+ }
+ else {
+ ::Print("WARNING: Action %d (intel level) Could not find group (IFF:%d, type:%d, id:%d)\n",
+ action->Identity(),
+ action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ action->FailAction();
+ }
+
+ scripted_event = true;
+ }
+ break;
+
+ case CombatAction::ZONE_ASSIGNMENT:
+ {
+ CombatGroup* g = campaign->FindGroup(action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ if (g) {
+ bool found = false;
+
+ if (*action->Region()) {
+ CombatZone* zone = campaign->GetZone(action->Region());
+
+ if (zone) {
+ g->SetAssignedZone(zone);
+ g->SetZoneLock(true);
+ found = true;
+
+ // don't announce the move unless it's for the player's team:
+ if (action->GetIFF() == campaign->GetPlayerIFF() && _stricmp(action->GetText(), "do-not-display")) {
+ CombatEvent* event = new
+ CombatEvent(campaign,
+ CombatEvent::MOVE_TO,
+ (int) campaign->GetTime(),
+ action->GetIFF(),
+ CombatEvent::FORCOM,
+ action->Region());
+
+ if (!event)
+ return false;
+
+ Text title = Text(g->Name()) + " Orders: Proceed to " + action->Region() + " Sector";
+ event->SetTitle(title);
+
+ double eta = campaign->GetTime() + 3600;
+ eta -= fmod(eta, 1800);
+
+ char text[64];
+ FormatDayTime(text, eta);
+
+ Text info = "ORDERS:\n\nEffective immediately, ";
+ info += g->GetDescription();
+ info += " and all associated units shall proceed to ";
+ info += action->Region();
+ info += " sector and commence spaceborne operations in that area. ETA rendevous point ";
+ info += text;
+ info += ".\n\nFleet Admiral A. Evars FORCOM\nCommanding";
+
+ event->SetInformation(info);
+
+ if (*action->ImageFile() != 0)
+ event->SetImageFile(action->ImageFile());
+
+ if (*action->SceneFile() != 0)
+ event->SetSceneFile(action->SceneFile());
+
+ event->Load();
+ campaign->GetEvents().append(event);
+ }
+ }
+ }
+
+ if (!found) {
+ ::Print("WARNING: Action %d Could not find assigned zone '%s' for '%s'\n",
+ action->Identity(),
+ action->Region() ? action->Region() : "NULL",
+ g->Name().data());
+
+ g->SetAssignedZone(0);
+ }
+
+ action->FireAction();
+ }
+ else {
+ ::Print("WARNING: Action %d (zone assignment) Could not find group (IFF:%d, type:%d, id:%d)\n",
+ action->Identity(),
+ action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ action->FailAction();
+ }
+
+ scripted_event = true;
+ }
+ break;
+
+ case CombatAction::SYSTEM_ASSIGNMENT:
+ {
+ CombatGroup* g = campaign->FindGroup(action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ if (g) {
+ bool found = false;
+
+ if (*action->System()) {
+ Text system = action->System();
+
+ if (campaign->GetSystem(system)) {
+ g->SetAssignedSystem(system);
+ found = true;
+
+ // don't announce the move unless it's for the player's team:
+ if (action->GetIFF() == campaign->GetPlayerIFF() && _stricmp(action->GetText(), "do-not-display")) {
+ CombatEvent* event = new
+ CombatEvent(campaign,
+ CombatEvent::MOVE_TO,
+ (int) campaign->GetTime(),
+ action->GetIFF(),
+ CombatEvent::FORCOM,
+ action->Region());
+
+ if (!event)
+ return false;
+
+ Text title = Text(g->Name()) + " Orders: Proceed to " + action->System() + " System";
+ event->SetTitle(title);
+
+ double eta = campaign->GetTime() + 3600;
+ eta -= fmod(eta, 1800);
+
+ char text[64];
+ FormatDayTime(text, eta);
+
+ Text info = "ORDERS:\n\nEffective immediately, ";
+ info += g->GetDescription();
+ info += " and all associated units shall proceed to the ";
+ info += action->System();
+ info += " star system and commence spaceborne operations in that area. ETA rendevous point ";
+ info += text;
+ info += ".\n\nFleet Admiral A. Evars FORCOM\nCommanding";
+
+ event->SetInformation(info);
+
+ if (*action->ImageFile() != 0)
+ event->SetImageFile(action->ImageFile());
+
+ if (*action->SceneFile() != 0)
+ event->SetSceneFile(action->SceneFile());
+
+ event->Load();
+ campaign->GetEvents().append(event);
+ }
+ }
+ }
+
+ if (!found) {
+ ::Print("WARNING: Action %d Could not find assigned system '%s' for '%s'\n",
+ action->Identity(),
+ action->System() ? action->System() : "NULL",
+ g->Name().data());
+
+ g->SetAssignedSystem("");
+ }
+
+ action->FireAction();
+ }
+ else {
+ ::Print("WARNING: Action %d (system assignment) Could not find group (IFF:%d, type:%d, id:%d)\n",
+ action->Identity(),
+ action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ action->FailAction();
+ }
+
+ scripted_event = true;
+ }
+ break;
+
+ case CombatAction::NO_ACTION:
+ action->FireAction();
+ scripted_event = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ return scripted_event;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanEvent::ProsecuteKills(CombatAction* action)
+{
+ if (action->AssetKills().size() > 0) {
+ CombatGroup* g = campaign->FindGroup(action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ if (g) {
+ ListIter<Text> iter = action->AssetKills();
+ while (++iter) {
+ Text* name = iter.value();
+ CombatUnit* asset = g->FindUnit(*name);
+
+ if (asset) {
+ int value_killed = asset->Kill(1);
+
+ ListIter<Combatant> iter = campaign->GetCombatants();
+ while (++iter) {
+ Combatant* c = iter.value();
+ if (c->GetIFF() > 0 && c->GetIFF() != asset->GetIFF()) {
+ // damage to neutral assets must be scored to bad guys:
+ if (asset->GetIFF() > 0 || c->GetIFF() > 1) {
+ c->AddScore(value_killed);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (action->TargetKills().size() > 0) {
+ CombatGroup* g = campaign->FindGroup(action->TargetIFF(),
+ action->TargetType(),
+ action->TargetId());
+
+ if (g) {
+ ListIter<Text> iter = action->TargetKills();
+ while (++iter) {
+ Text* name = iter.value();
+ CombatUnit* target = g->FindUnit(*name);
+
+ if (target) {
+ int value_killed = target->Kill(1);
+
+ ListIter<Combatant> iter = campaign->GetCombatants();
+ while (++iter) {
+ Combatant* c = iter.value();
+ if (c->GetIFF() > 0 && c->GetIFF() != target->GetIFF()) {
+ // damage to neutral assets must be scored to bad guys:
+ if (target->GetIFF() > 0 || c->GetIFF() > 1) {
+ c->AddScore(value_killed);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CampaignPlanEvent::ExecStatisticalEvents()
+{
+ bool result = false;
+
+ if (campaign) {
+ ListIter<Combatant> iter = campaign->GetCombatants();
+ while (++iter && !result) {
+ Combatant* c = iter.value();
+ CombatAssignment* a = ChooseAssignment(c->GetForce());
+
+ // prefer assignments not in player's zone:
+ if (a) {
+ CombatGroup* objective = a->GetObjective();
+ CombatGroup* player = campaign->GetPlayerGroup();
+
+ if (objective && player &&
+ objective->GetCurrentZone() == player->GetCurrentZone())
+ a = ChooseAssignment(c->GetForce());
+ }
+
+ if (a) {
+ result = CreateEvent(a);
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CampaignPlanEvent::CreateEvent(CombatAssignment* a)
+{
+ CombatEvent* event = 0;
+
+ if (campaign && a && a->GetResource() && RandomChance(1,2)) {
+ event_time = (int) Random(event_time, campaign->GetTime());
+
+ CombatGroup* group = a->GetResource();
+
+ if (group == campaign->GetPlayerGroup()) {
+
+ if (group->Type() == CombatGroup::DESTROYER_SQUADRON ||
+ group->Type() == CombatGroup::BATTLE_GROUP ||
+ group->Type() == CombatGroup::CARRIER_GROUP) {
+
+ return false;
+ }
+ }
+
+ CombatGroup* target = a->GetObjective();
+
+ if (target && target == campaign->GetPlayerGroup()) {
+
+ if (target->Type() == CombatGroup::DESTROYER_SQUADRON ||
+ target->Type() == CombatGroup::BATTLE_GROUP ||
+ target->Type() == CombatGroup::CARRIER_GROUP) {
+
+ return false;
+ }
+ }
+
+ switch (a->Type()) {
+ case Mission::DEFEND:
+ event = CreateEventDefend(a);
+ break;
+
+ case Mission::ASSAULT:
+ if (group->IsStarshipGroup())
+ event = CreateEventStarship(a);
+ else
+ event = CreateEventFighterAssault(a);
+ break;
+
+ case Mission::STRIKE:
+ if (group->IsStarshipGroup())
+ event = CreateEventStarship(a);
+ else
+ event = CreateEventFighterStrike(a);
+ break;
+
+ case Mission::SWEEP:
+ event = CreateEventFighterSweep(a);
+ break;
+ }
+
+ if (event) {
+ campaign->GetEvents().append(event);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+static void FindAssignments(CombatGroup* g, List<CombatAssignment>& alist)
+{
+ if (!g) return;
+
+ alist.append(g->GetAssignments());
+
+ ListIter<CombatGroup> iter = g->GetComponents();
+ while (++iter)
+ FindAssignments(iter.value(), alist);
+}
+
+CombatAssignment*
+CampaignPlanEvent::ChooseAssignment(CombatGroup* g)
+{
+ List<CombatAssignment> alist;
+ FindAssignments(g, alist);
+
+ int tries = 5;
+
+ if (alist.size() > 0) {
+ while (tries-- > 0) {
+ int index = (int) Random(0, alist.size());
+
+ if (index >= alist.size())
+ index = 0;
+
+ CombatAssignment* a = alist[index];
+
+ if (!a) continue;
+
+ CombatGroup* resource = a->GetResource();
+ CombatGroup* objective = a->GetObjective();
+
+ if (!resource || !objective)
+ continue;
+
+ if (resource->IsReserve() || objective->IsReserve())
+ continue;
+
+ if (resource->CalcValue() < 50 || objective->CalcValue() < 50)
+ continue;
+
+ if (resource == campaign->GetPlayerGroup() || objective == campaign->GetPlayerGroup())
+ continue;
+
+ return a;
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatEvent*
+CampaignPlanEvent::CreateEventDefend(CombatAssignment* a)
+{
+ bool friendly = IsFriendlyAssignment(a);
+
+ if (!friendly)
+ return 0;
+
+ CombatEvent* event = 0;
+ CombatGroup* group = a->GetResource();
+ CombatGroup* obj = a->GetObjective();
+ CombatUnit* unit = group->GetRandomUnit();
+ CombatUnit* tgt = obj->GetRandomUnit();
+
+ if (!unit || !tgt)
+ return 0;
+
+ bool success = Success(a);
+ Text rgn = group->GetRegion();
+ Text title = Text(group->Name()) + " in Defensive Engagement";
+ Text info;
+
+ event = new CombatEvent(campaign,
+ CombatEvent::DEFEND,
+ event_time,
+ group->GetIFF(),
+ CombatEvent::TACNET,
+ rgn);
+
+ if (!event)
+ return 0;
+
+ int tgt_count = 0;
+ int unit_count = 0;
+
+ if (!success) {
+ if (tgt) {
+ if (tgt->Kill(1) > 0)
+ tgt_count++;
+ Combatant* c = group->GetCombatant();
+ if (c) c->AddScore(tgt->GetSingleValue());
+ }
+
+ if (unit && RandomChance(1,5)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+
+ CombatGroup* us = group;
+ CombatGroup* them = obj;
+ int us_count = unit_count;
+ int them_count = tgt_count;
+
+ if (obj->IsStrikeTarget()) {
+ info = Text("EVENT: ") + rgn + " Sector\n\n";
+ }
+
+ else {
+ info = Text("MISSION: Escort ") + obj->Name() + ", " + rgn + " Sector\n\n";
+ }
+
+ info += GetTeamName(group);
+ info += Text(" ") + group->GetDescription();
+
+ if (success)
+ info += " successfully defended ";
+ else
+ info += " was unable to defend ";
+
+ info += GetTeamName(obj);
+ info += Text(" ") + obj->GetDescription() + ".\n\n";
+
+ // need to find an enemy group to do the attacking...
+
+ event->SetTitle(title);
+ event->SetInformation(info);
+ return event;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatEvent*
+CampaignPlanEvent::CreateEventFighterAssault(CombatAssignment* a)
+{
+ CombatEvent* event = 0;
+ CombatGroup* group = a->GetResource();
+ CombatGroup* obj = a->GetObjective();
+ CombatUnit* unit = group->GetRandomUnit();
+ CombatUnit* tgt = obj->GetRandomUnit();
+
+ if (!unit || !tgt)
+ return 0;
+
+ bool success = Success(a);
+ Text rgn = group->GetRegion();
+ Text title = Text(group->Name());
+ Text info;
+
+ event = new CombatEvent(campaign,
+ CombatEvent::ATTACK,
+ event_time,
+ group->GetIFF(),
+ CombatEvent::TACNET,
+ rgn);
+
+ if (!event)
+ return 0;
+
+ title += Text(" Assault ") + obj->Name();
+
+ int tgt_count = 0;
+ int unit_count = 0;
+
+ if (success) {
+ if (tgt) {
+ int killed = tgt->Kill(1 + tgt->Count()/2);
+ if (killed > 0)
+ tgt_count += killed / tgt->GetSingleValue();
+ Combatant* c = group->GetCombatant();
+ if (c) c->AddScore(tgt->GetSingleValue());
+ }
+
+ if (unit && RandomChance(1,5)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+ else {
+ for (int i = 0; i < 2; i++) {
+ if (unit && RandomChance(1,4)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+ }
+
+ CombatGroup* us = group;
+ CombatGroup* them = obj;
+ int us_count = unit_count;
+ int them_count = tgt_count;
+
+ bool friendly = IsFriendlyAssignment(a);
+
+ if (friendly) {
+ info = Text("MISSION: Strike, ") + rgn + " Sector\n\n";
+ }
+
+ else {
+ info = Text("EVENT: ") + rgn + " Sector\n\n";
+
+ us = obj;
+ them = group;
+ us_count = tgt_count;
+ them_count = unit_count;
+ }
+
+ info += GetTeamName(group);
+ info += Text(" ") + group->GetDescription();
+
+ if (success)
+ info += " successfully assault ";
+ else if (!friendly)
+ info += " assault averted against ";
+ else
+ info += " attempted assault on ";
+
+ info += GetTeamName(obj);
+ info += Text(" ") + obj->GetDescription() + ".\n\n";
+
+ char text[256];
+
+ if (them_count) {
+ if (friendly) {
+ if (them_count > 1)
+ sprintf_s(text, "ENEMY KILLED:\t %d %s destroyed\n", them_count, tgt->Name().data());
+ else
+ sprintf_s(text, "ENEMY KILLED:\t %s destroyed\n", tgt->Name().data());
+ } else {
+ sprintf_s(text, "ENEMY KILLED:\t %d %s destroyed\n", them_count, them->Name().data());
+ }
+
+ info += text;
+ } else {
+ info += "ENEMY KILLED:\t 0\n";
+ }
+
+ if (us_count) {
+ if (!friendly)
+ sprintf_s(text, "ALLIED LOSSES:\t %s destroyed\n", tgt->Name().data());
+ else
+ sprintf_s(text, "ALLIED LOSSES:\t %d %s destroyed", us_count, us->Name().data());
+
+ info += text;
+ }
+ else {
+ info += "ALLIED LOSSES:\t 0";
+ }
+
+
+ event->SetTitle(title);
+ event->SetInformation(info);
+ return event;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatEvent*
+CampaignPlanEvent::CreateEventFighterStrike(CombatAssignment* a)
+{
+ CombatEvent* event = 0;
+ CombatGroup* group = a->GetResource();
+ CombatGroup* obj = a->GetObjective();
+ CombatUnit* unit = group->GetRandomUnit();
+ CombatUnit* tgt = obj->GetRandomUnit();
+
+ if (!unit || !tgt)
+ return 0;
+
+ bool success = Success(a);
+ Text rgn = group->GetRegion();
+ Text title = Text(group->Name());
+ Text info;
+
+ event = new CombatEvent(campaign,
+ CombatEvent::ATTACK,
+ event_time,
+ group->GetIFF(),
+ CombatEvent::TACNET,
+ rgn);
+
+ if (!event)
+ return 0;
+
+ if (unit)
+ title += Text(" ") + unit->GetDesign()->abrv + "s";
+
+ if (success) {
+ title += " Successfully Strike " + obj->Name();
+ }
+ else {
+ title += " Attempt Strike on " + obj->Name();
+ }
+
+ int tgt_count = 0;
+ int unit_count = 0;
+
+ if (success) {
+ if (tgt) {
+ int killed = tgt->Kill(1 + tgt->Count()/2);
+ if (killed > 0)
+ tgt_count += killed / tgt->GetSingleValue();
+ Combatant* c = group->GetCombatant();
+ if (c) c->AddScore(tgt->GetSingleValue());
+ }
+
+ if (unit && RandomChance(1,5)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+ else {
+ for (int i = 0; i < 2; i++) {
+ if (unit && RandomChance(1,4)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+ }
+
+ CombatGroup* us = group;
+ CombatGroup* them = obj;
+ int us_count = unit_count;
+ int them_count = tgt_count;
+
+ bool friendly = IsFriendlyAssignment(a);
+
+ if (friendly) {
+ info = Text("MISSION: Strike, ") + rgn + " Sector\n\n";
+ }
+
+ else {
+ info = Text("EVENT: ") + rgn + " Sector\n\n";
+
+ us = obj;
+ them = group;
+ us_count = tgt_count;
+ them_count = unit_count;
+ }
+
+ info += GetTeamName(group);
+ info += Text(" ") + group->GetDescription();
+
+ if (success)
+ info += " successfully strike ";
+ else if (!friendly)
+ info += " strike against ";
+ else
+ info += " attempted strike on ";
+
+ info += GetTeamName(obj);
+ info += Text(" ") + obj->GetDescription();
+
+ if (!success && !friendly)
+ info += " averted.\n\n";
+ else
+ info += ".\n\n";
+
+ char text[256];
+
+ if (them_count) {
+ if (friendly) {
+ if (them_count > 1)
+ sprintf_s(text, "ENEMY KILLED:\t %d %s destroyed\n", them_count, tgt->Name().data());
+ else
+ sprintf_s(text, "ENEMY KILLED:\t %s destroyed\n", tgt->Name().data());
+ } else {
+ sprintf_s(text, "ENEMY KILLED:\t %d %s destroyed\n", them_count, them->Name().data());
+ }
+
+ info += text;
+ } else {
+ info += "ENEMY KILLED:\t 0\n";
+ }
+
+ if (us_count) {
+ if (!friendly)
+ sprintf_s(text, "ALLIED LOSSES:\t %s destroyed\n", tgt->Name().data());
+ else
+ sprintf_s(text, "ALLIED LOSSES:\t %d %s destroyed", us_count, us->Name().data());
+
+ info += text;
+ } else {
+ info += "ALLIED LOSSES:\t 0";
+ }
+
+ event->SetTitle(title);
+ event->SetInformation(info);
+ return event;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatEvent*
+CampaignPlanEvent::CreateEventFighterSweep(CombatAssignment* a)
+{
+ CombatEvent* event = 0;
+ CombatGroup* group = a->GetResource();
+ CombatGroup* obj = a->GetObjective();
+ CombatUnit* unit = group->GetRandomUnit();
+ CombatUnit* tgt = obj->GetRandomUnit();
+
+ if (!unit || !tgt)
+ return 0;
+
+ bool success = Success(a);
+ Text rgn = group->GetRegion();
+ Text title = Text(group->Name());
+ Text info;
+
+ event = new CombatEvent(campaign,
+ CombatEvent::ATTACK,
+ event_time,
+ group->GetIFF(),
+ CombatEvent::TACNET,
+ rgn);
+
+ if (!event)
+ return 0;
+
+ if (unit)
+ title += Text(" ") + unit->GetDesign()->abrv + "s";
+ else
+ title += " Fighters";
+
+ if (RandomChance(1, 4)) title += " Clash with ";
+ else if (RandomChance(1, 4)) title += " Engage ";
+ else if (RandomChance(1, 4)) title += " Intercept ";
+ else title += " Encounter ";
+
+ title += obj->Name();
+
+ int tgt_count = 0;
+ int unit_count = 0;
+
+ if (success) {
+ for (int i = 0; i < 2; i++) {
+ if (tgt && RandomChance(3,4)) {
+ if (tgt->Kill(1) > 0)
+ tgt_count++;
+ Combatant* c = group->GetCombatant();
+ if (c) c->AddScore(tgt->GetSingleValue());
+ }
+ }
+
+ if (tgt_count > 1) {
+ if (tgt && RandomChance(1,4)) {
+ if (tgt->Kill(1) > 0)
+ tgt_count++;
+ Combatant* c = group->GetCombatant();
+ if (c) c->AddScore(tgt->GetSingleValue());
+ }
+ }
+
+ else {
+ if (unit && RandomChance(1,5)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < 2; i++) {
+ if (unit && RandomChance(3,4)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+
+ if (tgt && RandomChance(1,4)) {
+ if (tgt->Kill(1) > 0)
+ tgt_count++;
+ Combatant* c = group->GetCombatant();
+ if (c) c->AddScore(tgt->GetSingleValue());
+ }
+ }
+
+ CombatGroup* us = group;
+ CombatGroup* them = obj;
+ int us_count = unit_count;
+ int them_count = tgt_count;
+
+ bool friendly = IsFriendlyAssignment(a);
+
+ if (!friendly) {
+ us = obj;
+ them = group;
+ us_count = tgt_count;
+ them_count = unit_count;
+ }
+
+ if (friendly) {
+ if (RandomChance())
+ info = Text("MISSION: OCA Sweep, ") + rgn + " Sector\n\n";
+ else
+ info = Text("MISSION: FORCAP, ") + rgn + " Sector\n\n";
+
+ info += GetTeamName(group);
+ info += Text(" ") + group->GetDescription();
+ info += Text(" engaged ") + GetTeamName(obj);
+ info += Text(" ") + obj->GetDescription() + ".\n\n";
+ }
+ else {
+ info = Text("MISSION: Patrol, ") + rgn + " Sector\n\n";
+
+ info += GetTeamName(obj);
+ info += Text(" ") + obj->GetDescription();
+ info += Text(" engaged ") + GetTeamName(group);
+ info += Text(" ") + group->GetDescription() + ".\n\n";
+ }
+
+ char text[256];
+
+ if (them_count) {
+ sprintf_s(text, "ENEMY KILLED:\t %d %s destroyed\n", them_count, them->Name().data());
+
+ info += text;
+ } else {
+ info += "ENEMY KILLED:\t 0\n";
+ }
+
+ if (us_count) {
+ sprintf_s(text, "ALLIED LOSSES:\t %d %s destroyed", us_count, us->Name().data());
+ info += text;
+ } else {
+ info += "ALLIED LOSSES:\t 0";
+ }
+
+ event->SetTitle(title);
+ event->SetInformation(info);
+ return event;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatEvent*
+CampaignPlanEvent::CreateEventStarship(CombatAssignment* a)
+{
+ CombatEvent* event = 0;
+ CombatGroup* group = a->GetResource();
+ CombatGroup* obj = a->GetObjective();
+ CombatUnit* unit = group->GetRandomUnit();
+ CombatUnit* tgt = obj->GetRandomUnit();
+
+ if (!unit || !tgt)
+ return 0;
+
+ bool success = Success(a);
+ Text rgn = group->GetRegion();
+ Text title = Text(group->Name());
+ Text info;
+
+ event = new CombatEvent(campaign,
+ CombatEvent::ATTACK,
+ event_time,
+ group->GetIFF(),
+ CombatEvent::TACNET,
+ group->GetRegion());
+
+ if (!event)
+ return 0;
+
+ title += Text(" Assaults ") + a->GetObjective()->Name();
+
+ int tgt_count = 0;
+ int unit_count = 0;
+
+ if (success) {
+ if (tgt) {
+ if (tgt->Kill(1) > 0)
+ tgt_count++;
+ Combatant* c = group->GetCombatant();
+ if (c) c->AddScore(tgt->GetSingleValue());
+ }
+
+ if (unit && RandomChance(1,5)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+ else {
+ for (int i = 0; i < 2; i++) {
+ if (unit && RandomChance(1,4)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+ }
+
+ CombatGroup* us = group;
+ CombatGroup* them = obj;
+ int us_count = unit_count;
+ int them_count = tgt_count;
+
+ bool friendly = IsFriendlyAssignment(a);
+
+ if (friendly) {
+ info = Text("MISSION: Fleet Action, ") + rgn + " Sector\n\n";
+ }
+
+ else {
+ info = Text("EVENT: ") + rgn + " Sector\n\n";
+
+ us = obj;
+ them = group;
+ us_count = tgt_count;
+ them_count = unit_count;
+ }
+
+ info += GetTeamName(group);
+ info += Text(" ") + group->GetDescription();
+
+ if (success)
+ info += " successfully assaulted ";
+ else if (!friendly)
+ info += " assault against ";
+ else
+ info += " attempted assault on ";
+
+ info += GetTeamName(obj);
+ info += Text(" ") + obj->GetDescription();
+
+ if (!success && !friendly)
+ info += " failed.\n\n";
+ else
+ info += ".\n\n";
+
+ char text[256];
+
+ if (them_count) {
+ if (friendly) {
+ if (tgt->Count() > 1) {
+ sprintf_s(text, "ENEMY KILLED:\t %d %s destroyed\n", them_count, tgt->Name().data());
+ } else {
+ sprintf_s(text, "ENEMY KILLED:\t %s destroyed\n", tgt->Name().data());
+ }
+ } else {
+ if (unit->Count() > 1) {
+ sprintf_s(text, "ENEMY KILLED:\t %d %s destroyed\n", them_count, unit->Name().data());
+ } else {
+ sprintf_s(text, "ENEMY KILLED:\t %s destroyed\n", unit->Name().data());
+ }
+ }
+
+ info += text;
+ } else {
+ info += "ENEMY KILLED:\t 0\n";
+ }
+
+ if (us_count) {
+ if (!friendly)
+ sprintf_s(text, "ALLIED LOSSES:\t %s destroyed\n", tgt->Name().data());
+ else
+ sprintf_s(text, "ALLIED LOSSES:\t %s destroyed", unit->Name().data());
+
+ info += text;
+ } else {
+ info += "ALLIED LOSSES:\t 0";
+ }
+
+ event->SetTitle(title);
+ event->SetInformation(info);
+ return event;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CampaignPlanEvent::IsFriendlyAssignment(CombatAssignment* a)
+{
+ if (!campaign || !a || !a->GetResource())
+ return false;
+
+ int a_team = a->GetResource()->GetIFF();
+ CombatGroup* player = campaign->GetPlayerGroup();
+
+ if (player && (player->GetIFF() == a_team))
+ return true;
+
+ return false;
+}
+
+bool
+CampaignPlanEvent::Success(CombatAssignment* a)
+{
+ if (!campaign || !a || !a->GetResource())
+ return false;
+
+ int odds = 6 - campaign->GetCampaignId();
+
+ if (odds < 1)
+ odds = 1;
+
+ bool success = RandomChance(odds, 5);
+
+ if (!IsFriendlyAssignment(a))
+ success = !success;
+
+ return success;
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+CampaignPlanEvent::GetTeamName(CombatGroup* g)
+{
+ while (g->GetParent())
+ g = g->GetParent();
+
+ return g->Name();
+}