/* 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: CmdForceDlg.cpp AUTHOR: John DiCamillo OVERVIEW ======== Operational Command Dialog (Order of Battle Tab) */ #include "MemDebug.h" #include "CmdForceDlg.h" #include "CmdMsgDlg.h" #include "CmpnScreen.h" #include "Starshatter.h" #include "Campaign.h" #include "Combatant.h" #include "CombatAssignment.h" #include "CombatGroup.h" #include "CombatUnit.h" #include "CombatZone.h" #include "Ship.h" #include "ShipDesign.h" #include "Player.h" #include "Weapon.h" #include "Game.h" #include "DataLoader.h" #include "Button.h" #include "ComboBox.h" #include "ListBox.h" #include "Slider.h" #include "Video.h" #include "Keyboard.h" #include "Mouse.h" #include "ParseUtil.h" #include "FormatUtil.h" // +--------------------------------------------------------------------+ // DECLARE MAPPING FUNCTIONS: DEF_MAP_CLIENT(CmdForceDlg, OnMode); DEF_MAP_CLIENT(CmdForceDlg, OnSave); DEF_MAP_CLIENT(CmdForceDlg, OnExit); DEF_MAP_CLIENT(CmdForceDlg, OnForces); DEF_MAP_CLIENT(CmdForceDlg, OnCombat); DEF_MAP_CLIENT(CmdForceDlg, OnTransfer); // +--------------------------------------------------------------------+ CmdForceDlg::CmdForceDlg(Screen* s, FormDef& def, CmpnScreen* mgr) : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr), CmdDlg(mgr), cmb_forces(0), lst_combat(0), lst_desc(0), stars(0), campaign(0), current_group(0), current_unit(0), btn_transfer(0) { stars = Starshatter::GetInstance(); campaign = Campaign::GetCampaign(); Init(def); } CmdForceDlg::~CmdForceDlg() { } // +--------------------------------------------------------------------+ void CmdForceDlg::RegisterControls() { cmb_forces = (ComboBox*) FindControl(400); lst_combat = (ListBox*) FindControl(401); lst_desc = (ListBox*) FindControl(402); btn_transfer = (Button*) FindControl(403); RegisterCmdControls(this); if (btn_save) REGISTER_CLIENT(EID_CLICK, btn_save, CmdForceDlg, OnSave); if (btn_exit) REGISTER_CLIENT(EID_CLICK, btn_exit, CmdForceDlg, OnExit); for (int i = 0; i < 5; i++) { if (btn_mode[i]) REGISTER_CLIENT(EID_CLICK, btn_mode[i], CmdForceDlg, OnMode); } if (cmb_forces) REGISTER_CLIENT(EID_SELECT, cmb_forces, CmdForceDlg, OnForces); if (lst_combat) { lst_combat->AddColumn("datatype", 0); REGISTER_CLIENT(EID_SELECT, lst_combat, CmdForceDlg, OnCombat); } if (btn_transfer) { btn_transfer->SetEnabled(false); REGISTER_CLIENT(EID_CLICK, btn_transfer, CmdForceDlg, OnTransfer); } } // +--------------------------------------------------------------------+ void CmdForceDlg::ExecFrame() { CmdDlg::ExecFrame(); } // +--------------------------------------------------------------------+ void CmdForceDlg::Show() { mode = MODE_FORCES; FormWindow::Show(); ShowCmdDlg(); cmb_forces->ClearItems(); campaign = Campaign::GetCampaign(); if (campaign) { List& combatants = campaign->GetCombatants(); if (combatants.size() > 0) { for (int i = 0; i < combatants.size(); i++) { if (IsVisible(combatants[i])) { cmb_forces->AddItem(combatants[i]->Name()); } } cmb_forces->SetSelection(0); Combatant* c = combatants[0]; ShowCombatant(c); } } } // +--------------------------------------------------------------------+ bool CmdForceDlg::IsVisible(Combatant* c) { int nvis = 0; if (c) { CombatGroup* force = c->GetForce(); if (force) { List& groups = force->GetComponents(); for (int i = 0; i < groups.size(); i++) { CombatGroup* g = groups[i]; if (g->Type() < CombatGroup::CIVILIAN && g->CountUnits() > 0 && g->IntelLevel() >= Intel::KNOWN) nvis++; } } } return nvis > 0; } // +--------------------------------------------------------------------+ static char pipe_stack[32]; static bool blank_line = false; void CmdForceDlg::ShowCombatant(Combatant* c) { if (!lst_combat || !c) return; lst_combat->ClearItems(); ZeroMemory(pipe_stack, 32); CombatGroup* force = c->GetForce(); if (force) { List& groups = force->GetComponents(); for (int i = 0; i < groups.size(); i++) { CombatGroup* g = groups[i]; if (g->Type() < CombatGroup::CIVILIAN && g->CountUnits() > 0) AddCombatGroup(g); } } current_group = 0; current_unit = 0; if (lst_desc) lst_desc->ClearItems(); if (btn_transfer) btn_transfer->SetEnabled(false); } void CmdForceDlg::AddCombatGroup(CombatGroup* grp, bool last_child) { if (!lst_combat || !grp || grp->IntelLevel() < Intel::KNOWN) return; char prefix[4]; if (!grp->GetParent() || grp->GetParent()->Type() == CombatGroup::FORCE) { if (!grp->IsExpanded()) { prefix[0] = Font::PIPE_PLUS; prefix[1] = 0; } else { prefix[0] = Font::PIPE_MINUS; prefix[1] = 0; } } else { prefix[0] = last_child ? Font::PIPE_LL : Font::PIPE_LT; prefix[1] = Font::PIPE_HORZ; prefix[2] = 0; prefix[3] = 0; if (grp->GetLiveComponents().size() > 0 || grp->GetUnits().size() > 0) { if (grp->IsExpanded()) prefix[1] = Font::PIPE_MINUS; else prefix[1] = Font::PIPE_PLUS; } } int index = lst_combat->AddItemWithData( Text(pipe_stack) + Text(prefix) + grp->GetDescription(), (DWORD) grp); lst_combat->SetItemData(index-1, 1, 0); blank_line = false; int stacklen = strlen(pipe_stack); if (!grp->GetParent() || grp->GetParent()->Type() == CombatGroup::FORCE) ; // no stack after first entry else if (prefix[0] == Font::PIPE_LT) pipe_stack[stacklen++] = Font::PIPE_VERT; else pipe_stack[stacklen++] = Font::PIPE_NBSP; pipe_stack[stacklen] = 0; if (grp->IsExpanded() && grp->GetUnits().size() > 0) { prefix[0] = (grp->GetLiveComponents().size() > 0) ? Font::PIPE_VERT : Font::PIPE_NBSP; prefix[1] = Font::PIPE_NBSP; prefix[2] = 0; prefix[3] = 0; ListIter unit_iter = grp->GetUnits(); while (++unit_iter) { CombatUnit* unit = unit_iter.value(); char info[512]; int damage = (int) (100 * unit->GetSustainedDamage() / unit->GetDesign()->integrity); if (damage < 1 || unit->DeadCount() >= unit->Count()) { sprintf_s(info, "%s%s%s", pipe_stack, prefix, unit->GetDescription()); } else { sprintf_s(info, "%s%s%s %d%% damage", pipe_stack, prefix, unit->GetDescription(), damage); } int index = lst_combat->AddItemWithData(info, (DWORD) unit); lst_combat->SetItemData(index-1, 1, 1); lst_combat->SetItemColor(index-1, lst_combat->GetForeColor() * 0.65); } // blank line after last unit in group: lst_combat->AddItem(Text(pipe_stack) + Text(prefix)); blank_line = true; } if (grp->IsExpanded() && grp->GetLiveComponents().size() > 0) { List& groups = grp->GetLiveComponents(); for (int i = 0; i < groups.size(); i++) { AddCombatGroup(groups[i], i == groups.size()-1); } // blank line after last group in group: if (!blank_line) { lst_combat->AddItem(pipe_stack); blank_line = true; } } if (stacklen > 1) pipe_stack[stacklen-1] = 0; else pipe_stack[0] = 0; } // +--------------------------------------------------------------------+ void CmdForceDlg::OnForces(AWEvent* event) { Text name = cmb_forces->GetSelectedItem(); campaign = Campaign::GetCampaign(); if (campaign) { ListIter iter = campaign->GetCombatants(); while (++iter) { Combatant* c = iter.value(); if (name == c->Name()) { ShowCombatant(c); break; } } } } // +--------------------------------------------------------------------+ struct WepGroup { Text name; int count; WepGroup() : count(0) { } }; WepGroup* FindWepGroup(WepGroup* weapons, const char* name) { WepGroup* group = 0; WepGroup* iter = weapons; int w = 0; int first = -1; while (!group && w < 8) { if (first < 0 && iter->name.length() == 0) first = w; if (!_stricmp(iter->name, name)) group = iter; iter++; w++; } if (!group && first >= 0) { group = weapons + first; group->name = name; } return group; } void CmdForceDlg::OnCombat(AWEvent* event) { static int old_index = -1; int top_index = 0; bool expand = false; DWORD data = 0; DWORD type = 0; current_group = 0; current_unit = 0; if (lst_combat) { top_index = lst_combat->GetTopIndex(); int index = lst_combat->GetListIndex(); data = lst_combat->GetItemData(index); type = lst_combat->GetItemData(index, 1); Text item = lst_combat->GetItemText(index); int nplus = item.indexOf(Font::PIPE_PLUS); int xplus = -1; int dx = 10000; if (nplus < 0) nplus = item.indexOf(Font::PIPE_MINUS); if (nplus >= 0 && nplus < 64) { char pipe[64]; strncpy(pipe, item.data(), nplus+1); pipe[nplus+1] = 0; Rect rect(0, 0, 1000, 20); lst_combat->DrawText(pipe, 0, rect, DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_CALCRECT); xplus = rect.w; } if (xplus > 0) { dx = Mouse::X() - (lst_combat->GetRect().x + xplus - 16); } // look for click on plus/minus in pipe if (dx >= 0 && dx < 16) { if (data && type == 0) { CombatGroup* grp = (CombatGroup*) data; grp->SetExpanded(!grp->IsExpanded()); expand = true; current_group = grp; current_unit = 0; } } old_index = index; } if (campaign && data) { if (!expand) { lst_desc->ClearItems(); // combat group if (type == 0) { CombatGroup* grp = (CombatGroup*) data; current_group = grp; current_unit = 0; // if has units, show location and assignment if (grp->GetUnits().size() > 0) { char txt[64]; int n; n = lst_desc->AddItem("Group:") - 1; lst_desc->SetItemText(n, 1, grp->GetDescription()); n = lst_desc->AddItem("Sector:") - 1; lst_desc->SetItemText(n, 1, grp->GetRegion()); lst_desc->AddItem(" "); n = lst_desc->AddItem("Sorties:") - 1; if (grp->Sorties() >= 0) sprintf_s(txt, "%d", grp->Sorties()); else strcpy_s(txt, "Unavail"); lst_desc->SetItemText(n, 1, txt); n = lst_desc->AddItem("Kills:") - 1; if (grp->Kills() >= 0) sprintf_s(txt, "%d", grp->Kills()); else strcpy_s(txt, "Unavail"); lst_desc->SetItemText(n, 1, txt); n = lst_desc->AddItem("Eff Rating:") - 1; if (grp->Points() >= 0) { if (grp->Sorties() > 0) sprintf_s(txt, "%.1f", (double) grp->Points() / grp->Sorties()); else sprintf_s(txt, "%.1f", (double) grp->Points()); } else { strcpy_s(txt, "Unavail"); } lst_desc->SetItemText(n, 1, txt); } // else (if high-level) show components else { int n; n = lst_desc->AddItem("Group:") - 1; lst_desc->SetItemText(n, 1, grp->GetDescription()); ListIter c = grp->GetLiveComponents(); while (++c) { n = lst_desc->AddItem("-") - 1; lst_desc->SetItemText(n, 1, c->GetDescription()); } } } // combat unit else { CombatUnit* unit = (CombatUnit*) data; current_group = unit->GetCombatGroup(); current_unit = unit; int n; char txt[64]; n = lst_desc->AddItem("Unit:") - 1; lst_desc->SetItemText(n, 1, unit->GetDescription()); n = lst_desc->AddItem("Sector:") - 1; lst_desc->SetItemText(n, 1, unit->GetRegion()); const ShipDesign* design = unit->GetDesign(); if (design) { lst_desc->AddItem(" "); n = lst_desc->AddItem("Type:") - 1; lst_desc->SetItemText(n, 1, Ship::ClassName(design->type)); n = lst_desc->AddItem("Class:") - 1; lst_desc->SetItemText(n, 1, design->DisplayName()); if (design->type < Ship::STATION) FormatNumber(txt, design->radius/2); else FormatNumber(txt, design->radius*2); strcat_s(txt, " m"); n = lst_desc->AddItem("Length:") - 1; lst_desc->SetItemText(n, 1, txt); FormatNumber(txt, design->mass); strcat_s(txt, " T"); n = lst_desc->AddItem("Mass:") - 1; lst_desc->SetItemText(n, 1, txt); FormatNumber(txt, design->integrity); n = lst_desc->AddItem("Hull:") - 1; lst_desc->SetItemText(n, 1, txt); if (design->weapons.size()) { lst_desc->AddItem(" "); n = lst_desc->AddItem("Weapons:") - 1; WepGroup groups[8]; for (int w = 0; w < design->weapons.size(); w++) { Weapon* gun = design->weapons[w]; WepGroup* group = FindWepGroup(groups, gun->Group()); if (group) group->count++; } for (int g = 0; g < 8; g++) { WepGroup* group = &groups[g]; if (group && group->count) { sprintf_s(txt, "%s (%d)", group->name.data(), group->count); if (g > 0) n = lst_desc->AddItem(" ") - 1; lst_desc->SetItemText(n, 1, txt); } } } } } } else { List& combatants = campaign->GetCombatants(); Combatant* c = combatants[0]; if (cmb_forces) { Text name = cmb_forces->GetSelectedItem(); for (int i = 0; i < combatants.size(); i++) { c = combatants[i]; if (name == c->Name()) { break; } } } if (c) { ShowCombatant(c); lst_combat->ScrollTo(top_index); lst_combat->SetSelected(old_index); } } } if (btn_transfer && campaign && current_group) btn_transfer->SetEnabled( campaign->IsActive() && CanTransfer(current_group) ); } // +--------------------------------------------------------------------+ bool CmdForceDlg::CanTransfer(CombatGroup* grp) { if (!grp || !campaign) return false; if (grp->Type() < CombatGroup::WING) return false; if (grp->Type() > CombatGroup::CARRIER_GROUP) return false; if (grp->Type() == CombatGroup::FLEET || grp->Type() == CombatGroup::LCA_SQUADRON) return false; CombatGroup* player_group = campaign->GetPlayerGroup(); if (player_group->GetIFF() != grp->GetIFF()) return false; return true; } // +--------------------------------------------------------------------+ void CmdForceDlg::OnSave(AWEvent* event) { CmdDlg::OnSave(event); } void CmdForceDlg::OnExit(AWEvent* event) { CmdDlg::OnExit(event); } void CmdForceDlg::OnMode(AWEvent* event) { CmdDlg::OnMode(event); } // +--------------------------------------------------------------------+ void CmdForceDlg::OnTransfer(AWEvent* event) { if (campaign && current_group) { // check player rank/responsibility: Player* player = Player::GetCurrentPlayer(); int cmd_class = Ship::FIGHTER; switch (current_group->Type()) { case CombatGroup::WING: case CombatGroup::INTERCEPT_SQUADRON: case CombatGroup::FIGHTER_SQUADRON: cmd_class = Ship::FIGHTER; break; case CombatGroup::ATTACK_SQUADRON: cmd_class = Ship::ATTACK; break; case CombatGroup::LCA_SQUADRON: cmd_class = Ship::LCA; break; case CombatGroup::DESTROYER_SQUADRON: cmd_class = Ship::DESTROYER; break; case CombatGroup::BATTLE_GROUP: cmd_class = Ship::CRUISER; break; case CombatGroup::CARRIER_GROUP: case CombatGroup::FLEET: cmd_class = Ship::CARRIER; break; } char transfer_info[512]; if (player->CanCommand(cmd_class)) { if (current_unit) { campaign->SetPlayerUnit(current_unit); sprintf_s(transfer_info, "Your transfer request has been approved, %s %s. You are now assigned to the %s. Good luck.\n\nFleet Admiral A. Evars FORCOM\nCommanding", Player::RankName(player->Rank()), player->Name().data(), current_unit->GetDescription()); } else { campaign->SetPlayerGroup(current_group); sprintf_s(transfer_info, "Your transfer request has been approved, %s %s. You are now assigned to the %s. Good luck.\n\nFleet Admiral A. Evars FORCOM\nCommanding", Player::RankName(player->Rank()), player->Name().data(), current_group->GetDescription()); } Button::PlaySound(Button::SND_ACCEPT); CmdMsgDlg* msgdlg = manager->GetCmdMsgDlg(); msgdlg->Title()->SetText("Transfer Approved"); msgdlg->Message()->SetText(transfer_info); msgdlg->Message()->SetTextAlign(DT_LEFT); manager->ShowCmdMsgDlg(); } else { Button::PlaySound(Button::SND_REJECT); sprintf_s(transfer_info, "Your transfer request has been denied, %s %s. The %s requires a command rank of %s. Please return to your unit and your duties.\n\nFleet Admiral A. Evars FORCOM\nCommanding", Player::RankName(player->Rank()), player->Name().data(), current_group->GetDescription(), Player::RankName(Player::CommandRankRequired(cmd_class))); CmdMsgDlg* msgdlg = manager->GetCmdMsgDlg(); msgdlg->Title()->SetText("Transfer Denied"); msgdlg->Message()->SetText(transfer_info); msgdlg->Message()->SetTextAlign(DT_LEFT); manager->ShowCmdMsgDlg(); } } }