/* 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 FILE: Element.cpp AUTHOR: John DiCamillo OVERVIEW ======== Package Element (e.g. Flight) class implementation */ #include "MemDebug.h" #include "Element.h" #include "Instruction.h" #include "RadioMessage.h" #include "RadioHandler.h" #include "Sim.h" #include "Ship.h" #include "NetUtil.h" #include "Game.h" // +----------------------------------------------------------------------+ static int id_key = 1000; Element::Element(const char* call_sign, int a_iff, int a_type) : id(id_key++), name(call_sign), type(a_type), iff(a_iff), player(0), command_ai(1), commander(0), assignment(0), carrier(0), combat_group(0), combat_unit(0), launch_time(0), hold_time(0), zone_lock(0), respawns(0), count(0), rogue(false), playable(true), intel(0) { if (!call_sign) { char buf[32]; sprintf_s(buf, "Pkg %d", id); name = buf; } SetLoadout(0); } Element::~Element() { flight_plan.destroy(); objectives.destroy(); instructions.destroy(); for (int i = 0; i < ships.size(); i++) ships[i]->SetElement(0); respawns = 0; } // +----------------------------------------------------------------------+ int Element::AddShip(Ship* ship, int index) { if (ship && !ships.contains(ship)) { Observe(ship); if (index < 0) { ships.append(ship); index = ships.size(); } else { ships.insert(ship, index-1); } ship->SetElement(this); if (respawns < ship->RespawnCount()) respawns = ship->RespawnCount(); } return index; } void Element::DelShip(Ship* ship) { if (ship && ships.contains(ship)) { ships.remove(ship); ship->SetElement(0); if (ships.isEmpty()) respawns = ship->RespawnCount(); } } Ship* Element::GetShip(int index) { if (index >= 1 && index <= ships.size()) return ships[index-1]; return 0; } int Element::GetShipClass() { if (ships.size()) return ships[0]->Class(); return 0; } int Element::FindIndex(const Ship* s) { return ships.index(s) + 1; } bool Element::Contains(const Ship* s) { return ships.contains(s); } bool Element::IsActive() const { bool active = false; for (int i = 0; i < ships.size() && !active; i++) { Ship* s = ships[i]; if (s->Life() && s->MissionClock()) active = true; } return active; } bool Element::IsFinished() const { bool finished = false; if (launch_time > 0 && respawns < 1) { finished = true; if (ships.size() > 0) { for (int i = 0; i < ships.size() && finished; i++) { Ship* s = ships[i]; if (s->RespawnCount() > 0 || s->MissionClock() == 0 || s->Life() && !s->GetInbound()) finished = false; } } } return finished; } bool Element::IsNetObserver() const { bool observer = !IsSquadron(); for (int i = 0; i < ships.size() && observer; i++) { Ship* s = ships[i]; if (!s->IsNetObserver()) observer = false; } return observer; } bool Element::IsSquadron() const { return count > 0; } bool Element::IsStatic() const { if (IsSquadron() || IsFinished()) return false; const Ship* s = ships.at(0); if (s && s->IsStatic()) return true; return false; } // +----------------------------------------------------------------------+ bool Element::IsHostileTo(const Ship* s) const { if (iff <= 0 || iff >= 100 || !s || launch_time == 0 || IsFinished()) return false; if (IsSquadron()) return false; if (s->IsRogue()) return true; int s_iff = s->GetIFF(); if (s_iff <= 0 || s_iff >= 100 || s_iff == iff) return false; if (ships.size() > 0 && ships[0]->GetRegion() != s->GetRegion()) return false; return true; } bool Element::IsHostileTo(int iff_code) const { if (iff <= 0 || iff >= 100 || launch_time == 0 || IsFinished()) return false; if (IsSquadron()) return false; if (iff_code <= 0 || iff_code >= 100 || iff_code == iff) return false; return true; } bool Element::IsObjectiveTargetOf(const Ship* s) const { if (!s || launch_time == 0 || IsFinished()) return false; const char* e_name = Name().data(); int e_len = Name().length(); Instruction* orders = s->GetRadioOrders(); if (orders && orders->Action() > Instruction::SWEEP) { const char* o_name = orders->TargetName(); int o_len = 0; if (o_name && *o_name) o_len = strlen(o_name); if (e_len < o_len) o_len = e_len; if (!strncmp(e_name, o_name, o_len)) return true; } Element* elem = s->GetElement(); if (elem) { for (int i = 0; i < elem->NumObjectives(); i++) { Instruction* obj = elem->GetObjective(i); if (obj) { const char* o_name = obj->TargetName(); int o_len = 0; if (o_name && *o_name) o_len = strlen(o_name); if (e_len < o_len) o_len = e_len; if (!strncmp(e_name, o_name, o_len)) return true; } } } return false; } // +----------------------------------------------------------------------+ void Element::SetLaunchTime(DWORD t) { if (launch_time == 0 || t == 0) launch_time = t; } double Element::GetHoldTime() { return hold_time; } void Element::SetHoldTime(double t) { if (t >= 0) hold_time = t; } bool Element::GetZoneLock() { return zone_lock; } void Element::SetZoneLock(bool z) { zone_lock = z; } void Element::SetLoadout(int* l) { if (l) { CopyMemory(load, l, sizeof(load)); } else { for (int i = 0; i < 16; i++) load[i] = -1; } } // +----------------------------------------------------------------------+ bool Element::Update(SimObject* obj) { // false alarm, keep watching: if (obj->Life() != 0) { ::Print("Element (%s) false update on (%s) life = %f\n", Name().data(), obj->Name(), obj->Life()); return false; } Ship* s = (Ship*) obj; ships.remove(s); if (ships.isEmpty()) respawns = s->RespawnCount(); return SimObserver::Update(obj); } const char* Element::GetObserverName() const { return (const char*) (Text("Element ") + Name()); } // +----------------------------------------------------------------------+ void Element::AddNavPoint(Instruction* pt, Instruction* afterPoint, bool send) { if (pt && !flight_plan.contains(pt)) { int index = -1; if (afterPoint) { index = flight_plan.index(afterPoint); if (index > -1) flight_plan.insert(pt, index+1); else flight_plan.append(pt); } else { flight_plan.append(pt); } if (send) { NetUtil::SendNavData(true, this, index, pt); } } } void Element::DelNavPoint(Instruction* pt, bool send) { // XXX MEMORY LEAK // This is a small memory leak, but I'm not sure if it is // safe to delete the navpoint when removing it from the // flight plan. Other ships in the element might have // pointers to the object...? if (pt) { int index = flight_plan.index(pt); flight_plan.remove(pt); if (send) { NetUtil::SendNavDelete(this, index); } } } // +----------------------------------------------------------------------+ void Element::ClearFlightPlan(bool send) { hold_time = 0; flight_plan.destroy(); objectives.destroy(); instructions.destroy(); if (send) { NetUtil::SendNavDelete(this, -1); } } // +----------------------------------------------------------------------+ Instruction* Element::GetNextNavPoint() { if (hold_time <= 0 && flight_plan.size() > 0) { ListIter iter = flight_plan; while (++iter) { Instruction* navpt = iter.value(); if (navpt->Status() == Instruction::COMPLETE && navpt->HoldTime() > 0) return navpt; if (navpt->Status() <= Instruction::ACTIVE) return navpt; } } return 0; } // +----------------------------------------------------------------------+ int Element::GetNavIndex(const Instruction* n) { int index = 0; if (flight_plan.size() > 0) { ListIter navpt = flight_plan; while (++navpt) { index++; if (navpt.value() == n) return index; } } return 0; } // +----------------------------------------------------------------------+ List& Element::GetFlightPlan() { return flight_plan; } int Element::FlightPlanLength() { return flight_plan.size(); } // +----------------------------------------------------------------------+ void Element::ClearObjectives() { objectives.destroy(); } void Element::AddObjective(Instruction* obj) { objectives.append(obj); } Instruction* Element::GetObjective(int index) { if (objectives.isEmpty()) return 0; if (index < 0) index = 0; else if (index >= objectives.size()) index = index % objectives.size(); return objectives.at(index); } Instruction* Element::GetTargetObjective() { for (int i = 0; i < objectives.size(); i++) { Instruction* obj = objectives[i]; if (obj->Status() <= Instruction::ACTIVE) { switch (obj->Action()) { case Instruction::INTERCEPT: case Instruction::STRIKE: case Instruction::ASSAULT: case Instruction::SWEEP: case Instruction::PATROL: case Instruction::RECON: case Instruction::ESCORT: case Instruction::DEFEND: return obj; default: break; } } } return 0; } // +----------------------------------------------------------------------+ void Element::ClearInstructions() { instructions.clear(); } void Element::AddInstruction(const char* instr) { instructions.append(new(__FILE__,__LINE__) Text(instr)); } Text Element::GetInstruction(int index) { if (instructions.isEmpty()) return Text(); if (index < 0) index = 0; if (index >= instructions.size()) index = index % instructions.size(); return *instructions.at(index); } // +----------------------------------------------------------------------+ void Element::ResumeAssignment() { SetAssignment(0); if (objectives.isEmpty()) return; Instruction* objective = 0; for (int i = 0; i < objectives.size() && !objective; i++) { Instruction* instr = objectives[i]; if (instr->Status() <= Instruction::ACTIVE) { switch (instr->Action()) { case Instruction::INTERCEPT: case Instruction::STRIKE: case Instruction::ASSAULT: objective = instr; break; } } } if (objective) { Sim* sim = Sim::GetSim(); ListIter iter = sim->GetElements(); while (++iter) { Element* elem = iter.value(); SimObject* tgt = objective->GetTarget(); if (tgt && tgt->Type() == SimObject::SIM_SHIP && elem->Contains((const Ship*) tgt)) { SetAssignment(elem); return; } } } } // +----------------------------------------------------------------------+ void Element::HandleRadioMessage(RadioMessage* msg) { if (!msg) return; static RadioHandler rh; // if this is a message from within the element, // then all ships should report in. Otherwise, // just the leader will acknowledge the message. int full_report = ships.contains(msg->Sender()); int reported = false; ListIter s = ships; while (++s) { if (rh.ProcessMessage(msg, s.value())) { if (full_report) { if (s.value() != msg->Sender()) rh.AcknowledgeMessage(msg, s.value()); } else if (!reported) { rh.AcknowledgeMessage(msg, s.value()); reported = true; } } } } // +----------------------------------------------------------------------+ bool Element::CanCommand(Element* e) { while (e) { if (e->commander == this) return true; e = e->commander; } return false; } // +----------------------------------------------------------------------+ void Element::ExecFrame(double seconds) { if (hold_time > 0) { hold_time -= seconds; return; } ListIter iter = flight_plan; while (++iter) { Instruction* instr = iter.value(); if (instr->Status() == Instruction::COMPLETE && instr->HoldTime() > 0) instr->SetHoldTime(instr->HoldTime() - seconds); } } // +----------------------------------------------------------------------+ void Element::SetIFF(int iff) { for (int i = 0; i < ships.size(); i++) ships[i]->SetIFF(iff); }