/* 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: Sim.cpp AUTHOR: John DiCamillo OVERVIEW ======== Simulation Universe and Region classes */ #include "MemDebug.h" #include "Sim.h" #include "SimEvent.h" #include "SimObject.h" #include "Starshatter.h" #include "StarSystem.h" #include "Contact.h" #include "Ship.h" #include "ShipDesign.h" #include "Element.h" #include "Instruction.h" #include "RadioTraffic.h" #include "Shot.h" #include "Drone.h" #include "Explosion.h" #include "Debris.h" #include "Asteroid.h" #include "Drive.h" #include "QuantumDrive.h" #include "Sensor.h" #include "NavLight.h" #include "Shield.h" #include "Weapon.h" #include "WeaponGroup.h" #include "Hangar.h" #include "FlightDeck.h" #include "Sky.h" #include "Grid.h" #include "Mfd.h" #include "AudioConfig.h" #include "Mission.h" #include "MissionEvent.h" #include "CameraDirector.h" #include "MusicDirector.h" #include "Combatant.h" #include "CombatGroup.h" #include "CombatUnit.h" #include "HUDView.h" #include "SeekerAI.h" #include "ShipAI.h" #include "Power.h" #include "Callsign.h" #include "GameScreen.h" #include "Terrain.h" #include "TerrainPatch.h" #include "NetGame.h" #include "NetClientConfig.h" #include "NetServerConfig.h" #include "NetPlayer.h" #include "NetUtil.h" #include "NetData.h" #include "Game.h" #include "Sound.h" #include "Bolt.h" #include "Solid.h" #include "Sprite.h" #include "Light.h" #include "Bitmap.h" #include "DataLoader.h" #include "ParseUtil.h" #include "MouseController.h" #include "Player.h" #include "Random.h" #include "Video.h" const char* FormatGameTime(); // +--------------------------------------------------------------------+ class SimHyper { public: SimHyper(Ship* o, SimRegion* r, const Point& l, int t, bool h, Ship* fc1, Ship* fc2) : ship(o), rgn(r), loc(l), type(t), hyperdrive(h), fc_src(fc1), fc_dst(fc2) { } Ship* ship; SimRegion* rgn; Point loc; int type; bool hyperdrive; Ship* fc_src; Ship* fc_dst; }; // +--------------------------------------------------------------------+ class SimSplash { public: SimSplash(SimRegion* r, const Point& l, double d, double n) : rgn(r), loc(l), damage(d), range(n), owner_name("Collateral Damage"), missile(false) { } Text owner_name; Point loc; double damage; double range; SimRegion* rgn; bool missile; }; // +--------------------------------------------------------------------+ static bool first_frame = true; Sim* Sim::sim = 0; Sim::Sim(MotionController* c) : ctrl(c), test_mode(false), grid_shown(false), dust(0), star_system(0), active_region(0), mission(0), netgame(0), start_time(0) { Drive::Initialize(); Explosion::Initialize(); FlightDeck::Initialize(); NavLight::Initialize(); Shot::Initialize(); MFD::Initialize(); Asteroid::Initialize(); if (!sim) sim = this; cam_dir = CameraDirector::GetInstance(); } Sim::~Sim() { UnloadMission(); Shot::Close(); FlightDeck::Close(); NavLight::Close(); Token::close(); Asteroid::Close(); if (sim == this) sim = 0; } // +--------------------------------------------------------------------+ void Sim::CommitMission() { for (int i = 0; i < regions.size(); i++) regions[i]->CommitMission(); if (ShipStats::NumStats() > 0) { Print("\n\nFINAL SCORE '%s'\n", (const char*) mission->Name()); Print("Name Kill1 Kill2 Died Colls Points Cmd Pts\n"); Print("---------------- ----- ----- ----- ----- ------ ------\n"); int tk1 = 0; int tk2 = 0; int td = 0; int tc = 0; for (int i = 0; i < ShipStats::NumStats(); i++) { ShipStats* s = ShipStats::GetStats(i); s->Summarize(); Print("%-16s %5d %5d %5d %5d %6d %6d\n", s->GetName(), s->GetGunKills(), s->GetMissileKills(), s->GetDeaths(), s->GetColls(), s->GetPoints(), s->GetCommandPoints()); tk1 += s->GetGunKills(); tk2 += s->GetMissileKills(); td += s->GetDeaths(); tc += s->GetColls(); CombatGroup* group = s->GetCombatGroup(); if (group) { Combatant* c = group->GetCombatant(); if (c) c->AddScore(s->GetPoints()); if (s->GetElementIndex() == 1) group->SetSorties(group->Sorties() + 1); group->SetKills(group->Kills() + s->GetGunKills() + s->GetMissileKills()); group->SetPoints(group->Points() + s->GetPoints()); } if (s->IsPlayer()) { Player* p = Player::GetCurrentPlayer(); p->ProcessStats(s, start_time); if (mission && mission->Type() == Mission::TRAINING && s->GetDeaths() == 0 && s->GetColls() == 0) p->SetTrained(mission->Identity()); Player::Save(); // save training state right now before we forget! } } Print("--------------------------------------------\n"); Print("TOTAL %5d %5d %5d %5d\n\n", tk1, tk2, td, tc); ShipStats::Initialize(); } } // +--------------------------------------------------------------------+ void Sim::UnloadMission() { if (netgame) { delete netgame; netgame = 0; } HUDView* hud = HUDView::GetInstance(); if (hud) hud->HideAll(); ShipStats::Initialize(); events.destroy(); mission_elements.destroy(); elements.destroy(); finished.destroy(); if (active_region) active_region->Deactivate(); if (star_system) star_system->Deactivate(); if (mission) { mission->SetActive(false); mission->SetComplete(true); } regions.destroy(); scene.Collect(); GRAPHIC_DESTROY(dust); star_system = 0; active_region = 0; mission = 0; // reclaim memory used by radio traffic: RadioTraffic::DiscardMessages(); // release texture memory for 2D screens: Starshatter* stars = Starshatter::GetInstance(); if (stars) stars->InvalidateTextureCache(); cam_dir = CameraDirector::GetInstance(); if (cam_dir) cam_dir->SetShip(0); AudioConfig::SetTraining(false); } bool Sim::IsActive() const { return mission && mission->IsActive(); } bool Sim::IsComplete() const { return mission && mission->IsComplete(); } // +--------------------------------------------------------------------+ void Sim::LoadMission(Mission* m, bool preload_textures) { cam_dir = CameraDirector::GetInstance(); if (!mission) { mission = m; mission->SetActive(true); if (preload_textures) { Video* video = Game::GetVideo(); List all_models; //List all_textures; ListIter elem_iter = mission->GetElements(); while (++elem_iter) { MissionElement* elem = elem_iter.value(); const ShipDesign* design = elem->GetDesign(); if (design) { for (int i = 0; i < 4; i++) { List& models = (List&) design->models[i]; // cast-away const ListIter model_iter = models; while (++model_iter) { Model* model = model_iter.value(); if (!all_models.contains(model)) { all_models.append(model); //model->GetAllTextures(all_textures); ListIter surf_iter = model->GetSurfaces(); while (++surf_iter) { Surface* surface = surf_iter.value(); video->PreloadSurface(surface); } } } } } } /* if (video && all_textures.size() > 0) { ::Print("Preloading %d textures into video texture cache\n", all_textures.size()); ListIter bmp_iter = all_textures; while (++bmp_iter) { Bitmap* bmp = bmp_iter.value(); video->PreloadTexture(bmp); } } */ } } } void Sim::ExecMission() { cam_dir = CameraDirector::GetInstance(); if (!mission) { Print("Sim::ExecMission() - No mission to execute.\n"); return; } if (elements.size() || finished.size()) { Print("Sim::ExecMission(%s) mission is already executing.\n", mission->Name()); return; } Print("\nExec Mission: '%s'\n", (const char*) mission->Name()); if (cam_dir) cam_dir->Reset(); if (mission->Stardate() > 0) StarSystem::SetBaseTime(mission->Stardate(), true); star_system = mission->GetStarSystem(); star_system->Activate(scene); int dust_factor = 0; if (Starshatter::GetInstance()) dust_factor = Starshatter::GetInstance()->Dust(); if (star_system->NumDust() * dust_factor) { dust = new(__FILE__,__LINE__) Dust(star_system->NumDust() * 2*(dust_factor+1), dust_factor > 1); scene.AddGraphic(dust); } CreateRegions(); BuildLinks(); CreateElements(); CopyEvents(); if (netgame) { delete netgame; netgame = 0; } first_frame = true; start_time = Game::GameTime(); AudioConfig::SetTraining(mission->Type() == Mission::TRAINING); } // +--------------------------------------------------------------------+ void Sim::CreateRegions() { const char* active_region_name = 0; if (mission) active_region_name = mission->GetRegion(); else return; ListIter iter = mission->GetSystemList(); while (++iter) { StarSystem* sys = iter.value(); // insert objects from star system: ListIter star = sys->Bodies(); while (++star) { ListIter planet = star->Satellites(); while (++planet) { ListIter moon = planet->Satellites(); while (++moon) { ListIter rgn = moon->Regions(); while (++rgn) { SimRegion* sim_region = new(__FILE__,__LINE__) SimRegion(this, rgn.value()); regions.append(sim_region); if (!strcmp(active_region_name, sim_region->Name())) { ActivateRegion(sim_region); } } } ListIter rgn = planet->Regions(); while (++rgn) { SimRegion* sim_region = new(__FILE__,__LINE__) SimRegion(this, rgn.value()); regions.append(sim_region); if (!strcmp(active_region_name, sim_region->Name())) { ActivateRegion(sim_region); } } } ListIter rgn = star->Regions(); while (++rgn) { SimRegion* sim_region = new(__FILE__,__LINE__) SimRegion(this, rgn.value()); regions.append(sim_region); if (!strcmp(active_region_name, sim_region->Name())) { ActivateRegion(sim_region); } } } } } // +--------------------------------------------------------------------+ void Sim::BuildLinks() { ListIter iter = regions; while (++iter) { SimRegion* rgn = iter.value(); OrbitalRegion* orb = rgn->GetOrbitalRegion(); if (orb) { ListIter lnk_iter = orb->Links(); while (++lnk_iter) { Text* t = lnk_iter.value(); SimRegion* tgt = FindRegion(*t); if (tgt && !rgn->Links().contains(tgt)) rgn->Links().append(tgt); } } } } void Sim::CreateElements() { ListIter e_iter = mission->GetElements(); while (++e_iter) { MissionElement* msn_elem = e_iter.value(); // add element to a carrier? if (msn_elem->IsSquadron()) { Ship* carrier = FindShip(msn_elem->Carrier()); if (carrier) { Hangar* hangar = carrier->GetHangar(); if (hangar) { int* def_load = 0; if (msn_elem->Loadouts().size()) { MissionLoad* m = msn_elem->Loadouts().at(0); if (m->GetName().length()) { ShipDesign* dsn = (ShipDesign*) msn_elem->GetDesign(); ListIter sl_iter = dsn->loadouts; while (++sl_iter) { ShipLoad* sl = sl_iter.value(); if (m->GetName() == sl->name) def_load = sl->load; } } if (!def_load) { def_load = m->GetStations(); } } hangar->CreateSquadron(msn_elem->Name(), msn_elem->GetCombatGroup(), msn_elem->GetDesign(), msn_elem->Count(), msn_elem->GetIFF(), def_load, msn_elem->MaintCount(), msn_elem->DeadCount()); Element* element = CreateElement(msn_elem->Name(), msn_elem->GetIFF(), msn_elem->MissionRole()); element->SetCarrier(carrier); element->SetCombatGroup(msn_elem->GetCombatGroup()); element->SetCombatUnit(msn_elem->GetCombatUnit()); element->SetCount(msn_elem->Count()); element->SetRogue(false); element->SetPlayable(false); element->SetLoadout(def_load); } } } // create the element in space: else { Ship* carrier = 0; Hangar* hangar = 0; int squadron = -1; int slot = 0; // first create the package element: Element* element = CreateElement(msn_elem->Name(), msn_elem->GetIFF(), msn_elem->MissionRole()); element->SetPlayer(msn_elem->Player()); element->SetCombatGroup(msn_elem->GetCombatGroup()); element->SetCombatUnit(msn_elem->GetCombatUnit()); element->SetCommandAILevel(msn_elem->CommandAI()); element->SetHoldTime(msn_elem->HoldTime()); element->SetZoneLock(msn_elem->ZoneLock() ? true : false); element->SetRogue(msn_elem->IsRogue()); element->SetPlayable(msn_elem->IsPlayable()); element->SetIntelLevel(msn_elem->IntelLevel()); // if this is the player's element, make sure to activate the region: if (msn_elem->Player()) { SimRegion* rgn = FindRegion(msn_elem->Region()); if (rgn && rgn != active_region) ActivateRegion(rgn); } // if element belongs to a squadron, // find the carrier, squadron, flight deck, etc.: if (msn_elem->Squadron().length() > 0) { MissionElement* squadron_elem = mission->FindElement(msn_elem->Squadron()); if (squadron_elem) { element->SetSquadron(msn_elem->Squadron()); Element* cmdr = FindElement(squadron_elem->Carrier()); if (cmdr) { element->SetCommander(cmdr); carrier = cmdr->GetShip(1); if (carrier) { element->SetCarrier(carrier); hangar = carrier->GetHangar(); for (int s = 0; s < hangar->NumSquadrons(); s++) { if (hangar->SquadronName(s) == msn_elem->Squadron()) { squadron = s; break; } } } } } } else if (msn_elem->Commander().length() > 0) { Element* cmdr = FindElement(msn_elem->Commander()); if (cmdr) { element->SetCommander(cmdr); } } ListIter obj = msn_elem->Objectives(); while (++obj) { Instruction* o = obj.value(); Instruction* instr = 0; instr = new(__FILE__,__LINE__) Instruction(*o); element->AddObjective(instr); } if (msn_elem->Instructions().size() > 0) { ListIter instr = msn_elem->Instructions(); while (++instr) { element->AddInstruction(*instr); } } ListIter nav = msn_elem->NavList(); while (++nav) { SimRegion* rgn = FindRegion(nav->RegionName()); if (!rgn) rgn = FindRegion(msn_elem->Region()); if (rgn) { Instruction* npt = new(__FILE__,__LINE__) Instruction(rgn, nav->Location(), nav->Action()); npt->SetStatus(nav->Status()); npt->SetEMCON(nav->EMCON()); npt->SetFormation(nav->Formation()); npt->SetSpeed(nav->Speed()); npt->SetTarget(nav->TargetName()); npt->SetHoldTime(nav->HoldTime()); npt->SetFarcast(nav->Farcast()); element->AddNavPoint(npt); } } bool alertPrep = false; int* loadout = 0; int respawns = msn_elem->RespawnCount(); // if ships are to start on alert, // spot them onto the appropriate launch deck: if (hangar && element && msn_elem->Count() > 0 && msn_elem->IsAlert()) { FlightDeck* deck = 0; int queue = 1000; const ShipDesign* dsn = msn_elem->GetDesign(); if (dsn) { for (int i = 0; i < carrier->NumFlightDecks(); i++) { FlightDeck* d = carrier->GetFlightDeck(i); int dq = hangar->PreflightQueue(d); if (d && d->IsLaunchDeck() && d->SpaceLeft(dsn->type) && dq < queue) { queue = dq; deck = d; } } } if (deck) { alertPrep = true; // choose best loadout: if (msn_elem->Loadouts().size()) { MissionLoad* l = msn_elem->Loadouts().at(0); if (l->GetName().length()) { ListIter sl = ((ShipDesign*) dsn)->loadouts; while (++sl) { if (!_stricmp(sl->name, l->GetName())) loadout = sl->load; } } else { loadout = l->GetStations(); } } element->SetLoadout(loadout); for (int i = 0; i < msn_elem->Count(); i++) { int squadron = -1; int slot = -1; if (hangar->FindAvailSlot(msn_elem->GetDesign(), squadron, slot)) { alertPrep = alertPrep && hangar->GotoAlert(squadron, slot, deck, element, loadout, true, // package for launch true); // expedite HangarSlot* s = (HangarSlot*) hangar->GetSlot(squadron, slot); Ship* alertShip = hangar->GetShip(s); if (alertShip) { alertShip->SetRespawnCount(respawns); if (msn_elem->Player() == i+1) { if (alertShip->GetRegion()) { alertShip->GetRegion()->SetPlayerShip(alertShip); } else { ::Print("WARNING: alert ship '%s' region is null\n", alertShip->Name()); } } } } } } } if (!alertPrep) { // then, create the ships: for (int i = 0; i < msn_elem->Count(); i++) { MissionShip* msn_ship = 0; Text sname = msn_elem->GetShipName(i); Text rnum = msn_elem->GetRegistry(i); Text rgn_name = msn_elem->Region(); if (msn_elem->Ships().size() > i) { msn_ship = msn_elem->Ships()[i]; sname = msn_ship->Name(); rnum = msn_ship->RegNum(); rgn_name = msn_ship->Region(); } Point l2 = msn_elem->Location(); if (msn_ship && fabs(msn_ship->Location().x) < 1e9) { l2 = msn_ship->Location(); } else if (i) { Point offset = RandomPoint(); offset.z = Random(-1e3, 1e3); if (msn_elem->Count() < 5) offset *= 0.3; l2 += offset; } // choose best loadout: ListIter l = msn_elem->Loadouts(); while (++l) { if ((l->GetShip() == i) || (l->GetShip() < 0 && loadout == 0)) { if (l->GetName().length()) { ListIter sl = ((ShipDesign*) msn_elem->GetDesign())->loadouts; while (++sl) { if (!_stricmp(sl->name, l->GetName())) loadout = sl->load; } } else { loadout = l->GetStations(); } } } element->SetLoadout(loadout); Ship* ship = CreateShip(sname, rnum, (ShipDesign*) msn_elem->GetDesign(), rgn_name, l2, msn_elem->GetIFF(), msn_elem->CommandAI(), loadout); if (ship) { double heading = msn_elem->Heading(); const Skin* skin = msn_elem->GetSkin(); if (msn_ship) { heading = msn_ship->Heading(); if (msn_ship->GetSkin()) skin = msn_ship->GetSkin(); } ship->SetRogue(msn_elem->IsRogue()); ship->SetInvulnerable(msn_elem->IsInvulnerable()); ship->SetHeading(0, 0, heading + PI); ship->SetRespawnCount(respawns); ship->UseSkin(skin); if (!netgame) ship->SetRespawnLoc(RandomPoint() * 2); if (ship->IsStarship()) ship->SetHelmHeading(heading); else if (ship->IsAirborne() && ship->AltitudeAGL() > 25) ship->SetVelocity(ship->Heading() * 250); if (element) element->AddShip(ship); if (hangar) hangar->FindSlot(ship, squadron, slot, Hangar::ACTIVE); if (ship->GetRegion() && msn_elem->Player() == i+1) ship->GetRegion()->SetPlayerShip(ship); if (ship->NumFlightDecks()) { for (int i = 0; i < ship->NumFlightDecks(); i++) { FlightDeck* deck = ship->GetFlightDeck(i); if (deck) deck->Orient(ship); } } if (msn_ship) { ship->SetVelocity(msn_ship->Velocity().OtherHand()); ship->SetIntegrity((float) msn_ship->Integrity()); ship->SetRespawnCount(msn_ship->Respawns()); if (msn_ship->Ammo()[0] > -10) { for (int i = 0; i < 64; i++) { Weapon* w = ship->GetWeaponByIndex(i+1); if (w) w->SetAmmo(msn_ship->Ammo()[i]); else break; } } if (msn_ship->Fuel()[0] > -10) { for (int i = 0; i < 4; i++) { if (ship->Reactors().size() > i) { PowerSource* p = ship->Reactors()[i]; p->SetCapacity(msn_ship->Fuel()[i]); } } } if (msn_ship->Decoys() > -10) { Weapon* w = ship->GetDecoy(); if (w) w->SetAmmo(msn_ship->Decoys()); } if (msn_ship->Probes() > -10) { Weapon* w = ship->GetProbeLauncher(); if (w) w->SetAmmo(msn_ship->Probes()); } } Shield* shield = ship->GetShield(); if (shield) { shield->SetPowerLevel(50); } if (ship->Class() > Ship::FRIGATE) { ListIter iter = ship->Weapons(); while (++iter) { WeaponGroup* weapon = iter.value(); // anti-air weapon? if (weapon->GetDesign()->target_type & Ship::DRONE) { weapon->SetFiringOrders(Weapon::POINT_DEFENSE); } else { weapon->SetFiringOrders(Weapon::MANUAL); } } } if (ship->Class() > Ship::DRONE && ship->Class() < Ship::STATION) { ShipStats* stats = ShipStats::Find(sname); if (stats) { char design[64]; sprintf_s(design, "%s %s", ship->Abbreviation(), ship->Design()->display_name); stats->SetType(design); stats->SetShipClass(ship->Class()); stats->SetRole(Mission::RoleName(msn_elem->MissionRole())); stats->SetIFF(ship->GetIFF()); stats->SetRegion(msn_elem->Region()); stats->SetCombatGroup(msn_elem->GetCombatGroup()); stats->SetCombatUnit(msn_elem->GetCombatUnit()); stats->SetPlayer(msn_elem->Player() == i+1); stats->SetElementIndex(ship->GetElementIndex()); } } } // ship } // count } } } } void Sim::CopyEvents() { events.destroy(); if (mission) { ListIter iter = mission->GetEvents(); while (++iter) { MissionEvent* orig = iter.value(); MissionEvent* event = new(__FILE__,__LINE__) MissionEvent(*orig); events.append(event); } } } // +--------------------------------------------------------------------+ const char* Sim::FindAvailCallsign(int IFF) { const char* call = "Unidentified"; for (int i = 0; i < 32; i++) { call = Callsign::GetCallsign(IFF); if (!FindElement(call)) break; } return call; } Element* Sim::CreateElement(const char* callsign, int IFF, int type) { Element* elem = new(__FILE__,__LINE__) Element(callsign, IFF, type); elements.append(elem); return elem; } void Sim::DestroyElement(Element* elem) { if (elements.contains(elem)) elements.remove(elem); delete elem; } Element* Sim::FindElement(const char* name) { ListIter iter = elements; while (++iter) { Element* elem = iter.value(); Text ename = elem->Name(); if (ename == name) return elem; } return 0; } // +--------------------------------------------------------------------+ int Sim::GetAssignedElements(Element* elem, List& assigned) { assigned.clear(); if (elem) { for (int i = 0; i < elements.size(); i++) { Element* e = elements.at(i); if (!e->IsSquadron() && e->GetAssignment() == elem) assigned.append(e); } } return assigned.size(); } // +--------------------------------------------------------------------+ Ship* Sim::CreateShip(const char* name, const char* reg_num, ShipDesign* design, const char* rgn_name, const Point& loc, int IFF, int cmd_ai, const int* loadout) { if (!design) { Print("WARNING: CreateShip(%s): invalid design\n", name); return 0; } SimRegion* rgn = FindRegion(rgn_name); if (!rgn) { return 0; } Ship* ship = new(__FILE__,__LINE__) Ship(name, reg_num, design, IFF, cmd_ai, loadout); ship->MoveTo(loc.OtherHand()); if (rgn) { Print("Inserting Ship(%s) into Region(%s) (%s)\n", ship->Name(), rgn->Name(), FormatGameTime()); rgn->InsertObject(ship); if (ship->IsAirborne() && ship->AltitudeAGL() > 25) ship->SetVelocity(ship->Heading() * 250); } return ship; } Ship* Sim::FindShip(const char* name, const char* rgn_name) { Ship* ship = 0; if (rgn_name) { SimRegion* rgn = FindRegion(rgn_name); if (rgn) ship = rgn->FindShip(name); } if (!ship) { ListIter rgn = regions; while (++rgn && !ship) ship = rgn->FindShip(name); } return ship; } void Sim::DestroyShip(Ship* ship) { SimRegion* rgn = ship->GetRegion(); if (rgn) rgn->DestroyShip(ship); } void Sim::NetDockShip(Ship* ship, Ship* carrier, FlightDeck* deck) { SimRegion* rgn = ship->GetRegion(); if (rgn) rgn->NetDockShip(ship, carrier, deck); } Ship* Sim::FindShipByObjID(DWORD objid) { Ship* ship = 0; ListIter rgn = regions; while (++rgn && !ship) ship = rgn->FindShipByObjID(objid); return ship; } Shot* Sim::FindShotByObjID(DWORD objid) { Shot* shot = 0; ListIter rgn = regions; while (++rgn && !shot) shot = rgn->FindShotByObjID(objid); return shot; } // +--------------------------------------------------------------------+ Orbital* Sim::FindOrbitalBody(const char* name) { Orbital* body = 0; if (mission) { ListIter iter = mission->GetSystemList(); while (++iter && !body) { StarSystem* sys = iter.value(); body = sys->FindOrbital(name); } } return body; } // +--------------------------------------------------------------------+ Shot* Sim::CreateShot(const Point& pos, const Camera& shot_cam, WeaponDesign* design, const Ship* ship, SimRegion* rgn) { Shot* shot = 0; if (design->drone) shot = new(__FILE__,__LINE__) Drone(pos, shot_cam, design, ship); else shot = new(__FILE__,__LINE__) Shot( pos, shot_cam, design, ship); if (rgn) rgn->InsertObject(shot); else if (active_region) active_region->InsertObject(shot); return shot; } // +--------------------------------------------------------------------+ Explosion* Sim::CreateExplosion(const Point& pos, const Point& vel, int type, float exp_scale, float part_scale, SimRegion* rgn, SimObject* source, System* sys) { // don't bother creating explosions that can't be seen: if (!rgn || !active_region || rgn != active_region) return 0; Explosion* exp = new(__FILE__,__LINE__) Explosion(type, pos, vel, exp_scale, part_scale, rgn, source); if (rgn) rgn->InsertObject(exp); else if (active_region) active_region->InsertObject(exp); return exp; } // +--------------------------------------------------------------------+ Debris* Sim::CreateDebris(const Point& pos, const Point& vel, Model* model, double mass, SimRegion* rgn) { Debris* debris = new(__FILE__,__LINE__) Debris(model, pos, vel, mass); if (rgn) rgn->InsertObject(debris); else if (active_region) active_region->InsertObject(debris); return debris; } // +--------------------------------------------------------------------+ Asteroid* Sim::CreateAsteroid(const Point& pos, int t, double mass, SimRegion* rgn) { Asteroid* asteroid = new(__FILE__,__LINE__) Asteroid(t, pos, mass); if (rgn) rgn->InsertObject(asteroid); else if (active_region) active_region->InsertObject(asteroid); return asteroid; } // +--------------------------------------------------------------------+ void Sim::CreateSplashDamage(Ship* ship) { if (ship && ship->GetRegion() && ship->Design()->splash_radius > 1) { SimSplash* splash = new(__FILE__,__LINE__) SimSplash(ship->GetRegion(), ship->Location(), ship->Design()->integrity / 4, ship->Design()->splash_radius); splash->owner_name = ship->Name(); splashlist.append(splash); } } // +--------------------------------------------------------------------+ void Sim::CreateSplashDamage(Shot* shot) { if (shot && shot->GetRegion()) { double damage = shot->Damage(); if (damage < shot->Design()->damage) damage = shot->Design()->damage; SimSplash* splash = new(__FILE__,__LINE__) SimSplash(shot->GetRegion(), shot->Location(), damage, shot->Design()->lethal_radius); if (shot->Owner()) splash->owner_name = shot->Owner()->Name(); splash->missile = shot->IsMissile(); splashlist.append(splash); CreateExplosion(shot->Location(), Point(), Explosion::SHOT_BLAST, 20.0f, 1.0f, shot->GetRegion()); } } // +--------------------------------------------------------------------+ void Sim::ShowGrid(int show) { Player* player = Player::GetCurrentPlayer(); if (player && player->GridMode() == 0) { show = 0; grid_shown = false; } ListIter rgn = regions; while (++rgn) { rgn->ShowGrid(show); } grid_shown = show?true:false; } bool Sim::GridShown() const { return grid_shown; } // +--------------------------------------------------------------------+ List& Sim::GetSystemList() { if (mission) return mission->GetSystemList(); static List dummy_system_list; return dummy_system_list; } // +--------------------------------------------------------------------+ void Sim::NextView() { if (active_region) active_region->NextView(); } Ship* Sim::GetPlayerShip() { if (active_region) return active_region->GetPlayerShip(); Starshatter* stars = Starshatter::GetInstance(); if (stars && stars->InCutscene()) { Ship* player = 0; ListIter rgn = regions; while (++rgn && !player) { player = rgn->GetPlayerShip(); } return player; } return 0; } Element* Sim::GetPlayerElement() { Element* elem = 0; for (int i = 0; i < elements.size(); i++) { Element* e = elements[i]; if (e->Player() > 0) elem = e; } return elem; } bool Sim::IsSelected(Ship* s) { if (active_region) return active_region->IsSelected(s); return false; } ListIter Sim::GetSelection() { if (active_region) return active_region->GetSelection(); static List empty; return empty; } void Sim::ClearSelection() { if (active_region) active_region->ClearSelection(); } void Sim::AddSelection(Ship* s) { if (active_region) active_region->AddSelection(s); } void Sim::SetSelection(Ship* newsel) { if (active_region) active_region->SetSelection(newsel); } // +--------------------------------------------------------------------+ void Sim::SetTestMode(bool t) { test_mode = t; Ship* pship = GetPlayerShip(); if (pship) if (IsTestMode()) pship->SetControls(0); else pship->SetControls(ctrl); } // +--------------------------------------------------------------------+ SimRegion* Sim::FindRegion(const char* name) { ListIter rgn = regions; while (++rgn) if (rgn->name == name) return rgn.value(); return 0; } SimRegion* Sim::FindRegion(OrbitalRegion* orgn) { ListIter rgn = regions; while (++rgn) if (rgn->orbital_region == orgn) return rgn.value(); return 0; } // +--------------------------------------------------------------------+ SimRegion* Sim::FindNearestSpaceRegion(SimObject* object) { return FindNearestRegion(object, REAL_SPACE); } SimRegion* Sim::FindNearestTerrainRegion(SimObject* object) { return FindNearestRegion(object, AIR_SPACE); } SimRegion* Sim::FindNearestRegion(SimObject* object, int type) { if (!object) return 0; SimRegion* result = 0; double distance = 1.0e40; Point objloc = object->Location(); objloc = objloc.OtherHand(); if (object->GetRegion()) objloc += object->GetRegion()->Location(); ListIter rgn = regions; while (++rgn) { if (rgn->Type() == type) { OrbitalRegion* orgn = rgn->GetOrbitalRegion(); if (orgn) { double test = fabs((orgn->Location() - objloc).length()); if (test < distance) { result = rgn.value(); distance = test; } } } } return result; } SimRegion* Sim::FindNearestSpaceRegion(Orbital* body) { SimRegion* result = 0; if (!body) return result; ListIter rgn = regions; while (++rgn && !result) { if (rgn->IsOrbital()) { OrbitalRegion* orgn = rgn->GetOrbitalRegion(); if (orgn) { ListIter iter = body->Regions(); while (++iter) { if (iter.value() == orgn) result = rgn.value(); } } } } return result; } // +--------------------------------------------------------------------+ bool Sim::ActivateRegion(SimRegion* rgn) { if (rgn && active_region != rgn && regions.contains(rgn)) { if (active_region) active_region->Deactivate(); if (!active_region || active_region->System() != rgn->System()) { if (active_region) active_region->System()->Deactivate(); rgn->System()->Activate(scene); } active_region = rgn; star_system = active_region->System(); if (star_system) { star_system->SetActiveRegion(active_region->orbital_region); } else { ::Print("WARNING: Sim::ActivateRegion() No star system found for rgn '%s'", rgn->Name()); } active_region->Activate(); return true; } return false; } // +--------------------------------------------------------------------+ void Sim::RequestHyperJump(Ship* obj, SimRegion* rgn, const Point& loc, int type, Ship* fc1, Ship* fc2) { bool hyperdrive = false; if (obj->GetQuantumDrive() && obj->GetQuantumDrive()->Subtype() == QuantumDrive::HYPER) hyperdrive = true; jumplist.append(new(__FILE__,__LINE__) SimHyper(obj, rgn, loc, type, hyperdrive, fc1, fc2)); } // +--------------------------------------------------------------------+ void Sim::ExecFrame(double seconds) { if (first_frame) { first_frame = false; netgame = NetGame::Create(); } if (netgame) netgame->ExecFrame(); if (regions.isEmpty()) { active_region = 0; rgn_queue.clear(); jumplist.destroy(); scene.Collect(); return; } ListIter elem = elements; while (++elem) if (!elem->IsSquadron()) elem->ExecFrame(seconds); ListIter rgn = regions; while (++rgn) if (rgn.value() != active_region && rgn->NumShips() && !rgn_queue.contains(rgn.value())) rgn_queue.append(rgn.value()); // execframe for one inactive sim region: if (rgn_queue.size()) { SimRegion* exec_rgn = rgn_queue.removeIndex(0); while (exec_rgn && (exec_rgn->NumShips() == 0 || exec_rgn == active_region)) if (rgn_queue.size()) exec_rgn = rgn_queue.removeIndex(0); else exec_rgn = 0; if (exec_rgn) exec_rgn->ExecFrame(seconds); } if (active_region) active_region->ExecFrame(seconds); ExecEvents(seconds); ResolveHyperList(); ResolveSplashList(); // GC all the dead objects: scene.Collect(); if (!IsTestMode()) { ListIter e_iter = elements; while (++e_iter) { Element* elem = e_iter.value(); if (!elem->IsSquadron() && elem->IsFinished()) { finished.append(e_iter.removeItem()); } } } // setup music if (!MusicDirector::IsNoMusic()) { Starshatter* stars = Starshatter::GetInstance(); if (stars && stars->GetGameMode() == Starshatter::PLAY_MODE) { Ship* player_ship = GetPlayerShip(); if (player_ship) { int phase = player_ship->GetFlightPhase(); if (phase < Ship::ACTIVE) { MusicDirector::SetMode(MusicDirector::LAUNCH); } else if (phase > Ship::ACTIVE) { MusicDirector::SetMode(MusicDirector::RECOVERY); } else { if (player_ship->IsInCombat()) { MusicDirector::SetMode(MusicDirector::COMBAT); } else { MusicDirector::SetMode(MusicDirector::FLIGHT); } } } } } } void Sim::ExecEvents(double seconds) { ListIter iter = events; while (++iter) { MissionEvent* event = iter.value(); event->ExecFrame(seconds); } } void Sim::ResolveHyperList() { // resolve the hyper space transitions: if (jumplist.size()) { Ship* pship = GetPlayerShip(); ListIter j_iter = jumplist; while (++j_iter) { SimHyper* jump = j_iter.value(); Ship* jumpship = jump->ship; if (jumpship) { SimRegion* dest = jump->rgn; if (!dest) dest = FindNearestSpaceRegion(jumpship); if (dest) { // bring along fighters on deck: ListIter deck = jumpship->FlightDecks(); while (++deck) { for (int i = 0; i < deck->NumSlots(); i++) { Ship* s = deck->GetShip(i); if (s) { dest->InsertObject(s); s->ClearTrack(); } } } if (jump->type == 0 && !jump->hyperdrive) { // bring along nearby ships: // have to do it in two parts, because inserting the ships // into the destination corrupts the iter over the current // region's list of ships... // part one: gather the ships that will be jumping: List riders; ListIter neighbor = jumpship->GetRegion()->Ships(); while (++neighbor) { if (neighbor->IsDropship()) { Ship* s = neighbor.value(); if (s == jumpship) continue; Point delta = s->Location() - jumpship->Location(); if (delta.length() < 5e3) { riders.append(s); } } } // part two: now transfer the list to the destination: for (int i = 0; i < riders.size(); i++) { Ship* s = riders[i]; Point delta = s->Location() - jumpship->Location(); dest->InsertObject(s); s->MoveTo(jump->loc.OtherHand() + delta); s->ClearTrack(); if (jump->fc_dst) { double r = jump->fc_dst->Roll(); double p = jump->fc_dst->Pitch(); double w = jump->fc_dst->Yaw(); s->SetAbsoluteOrientation(r, p, w); s->SetVelocity(jump->fc_dst->Heading() * 500); } ProcessEventTrigger(MissionEvent::TRIGGER_JUMP, 0, s->Name()); } } // now it is safe to move the main jump ship: dest->InsertObject(jumpship); jumpship->MoveTo(jump->loc.OtherHand()); jumpship->ClearTrack(); ProcessEventTrigger(MissionEvent::TRIGGER_JUMP, 0, jumpship->Name()); NetUtil::SendObjHyper(jumpship, dest->Name(), jump->loc, jump->fc_src, jump->fc_dst, jump->type); // if using farcaster: if (jump->fc_src) { ::Print("Ship '%s' farcast to '%s'\n", jumpship->Name(), dest->Name()); CreateExplosion(jumpship->Location(), Point(0,0,0), Explosion::QUANTUM_FLASH, 1.0f, 0, dest); if (jump->fc_dst) { double r = jump->fc_dst->Roll(); double p = jump->fc_dst->Pitch(); double w = jump->fc_dst->Yaw(); jumpship->SetAbsoluteOrientation(r, p, w); jumpship->SetVelocity(jump->fc_dst->Heading() * 500); } jumpship->SetHelmHeading(jumpship->CompassHeading()); jumpship->SetHelmPitch(0); } // break orbit: else if (jump->type == Ship::TRANSITION_DROP_ORBIT) { ::Print("Ship '%s' broke orbit to '%s'\n", jumpship->Name(), dest->Name()); jumpship->SetAbsoluteOrientation(0,PI/4,0); jumpship->SetVelocity(jumpship->Heading() * 1.0e3); } // make orbit: else if (jump->type == Ship::TRANSITION_MAKE_ORBIT) { ::Print("Ship '%s' achieved orbit '%s'\n", jumpship->Name(), dest->Name()); jumpship->LookAt(Point(0,0,0)); jumpship->SetVelocity(jumpship->Heading() * 500.0); } // hyper jump: else { ::Print("Ship '%s' quantum to '%s'\n", jumpship->Name(), dest->Name()); if (jump->hyperdrive) CreateExplosion(jumpship->Location(), Point(0,0,0), Explosion::HYPER_FLASH, 1, 1, dest); else CreateExplosion(jumpship->Location(), Point(0,0,0), Explosion::QUANTUM_FLASH, 1, 0, dest); jumpship->LookAt(Point(0,0,0)); jumpship->SetVelocity(jumpship->Heading() * 500.0); jumpship->SetHelmHeading(jumpship->CompassHeading()); jumpship->SetHelmPitch(0); } } else if (regions.size() > 1) { ::Print("Warning: Unusual jump request for ship '%s'\n", jumpship->Name()); regions[1]->InsertObject(jumpship); } Sensor* sensor = jumpship->GetSensor(); if (sensor) sensor->ClearAllContacts(); } } jumplist.destroy(); if (pship && pship->GetRegion()) { if (active_region != pship->GetRegion()) { pship->GetRegion()->SetPlayerShip(pship); } } } } void Sim::ResolveSplashList() { if (splashlist.size()) { ListIter iter = splashlist; while (++iter) { SimSplash* splash = iter.value(); if (!splash->rgn) continue; // damage ships: ListIter s_iter = splash->rgn->Ships(); while (++s_iter) { Ship* ship = s_iter.value(); double distance = (ship->Location() - splash->loc).length(); if (distance > 1 && distance < splash->range) { double damage = splash->damage * (1 - distance/splash->range); if (!NetGame::IsNetGameClient()) { ship->InflictDamage(damage); } int ship_destroyed = (!ship->InTransition() && ship->Integrity() < 1.0f); // then delete the ship: if (ship_destroyed) { NetUtil::SendObjKill(ship, 0, NetObjKill::KILL_MISC); Print(" %s Killed %s (%s)\n", (const char*) splash->owner_name, ship->Name(), FormatGameTime()); // record the kill ShipStats* killer = ShipStats::Find(splash->owner_name); if (killer) { if (splash->missile) killer->AddEvent(SimEvent::MISSILE_KILL, ship->Name()); else killer->AddEvent(SimEvent::GUNS_KILL, ship->Name()); } Ship* owner = FindShip(splash->owner_name, splash->rgn->Name()); if (owner && owner->GetIFF() != ship->GetIFF()) { if (ship->GetIFF() > 0 || owner->GetIFF() > 1) { killer->AddPoints(ship->Value()); Element* elem = owner->GetElement(); if (elem) { if (owner->GetElementIndex() > 1) { Ship* s = elem->GetShip(1); if (s) { ShipStats* cmdr_stats = ShipStats::Find(s->Name()); if (cmdr_stats) { cmdr_stats->AddCommandPoints(ship->Value()/2); } } } Element* cmdr = elem->GetCommander(); if (cmdr) { Ship* s = cmdr->GetShip(1); if (s) { ShipStats* cmdr_stats = ShipStats::Find(s->Name()); if (cmdr_stats) { cmdr_stats->AddCommandPoints(ship->Value()/2); } } } } } } ShipStats* killee = ShipStats::Find(ship->Name()); if (killee) killee->AddEvent(SimEvent::DESTROYED, splash->owner_name); ship->DeathSpiral(); } } } // damage drones: ListIter drone_iter = splash->rgn->Drones(); while (++drone_iter) { Drone* drone = drone_iter.value(); double distance = (drone->Location() - splash->loc).length(); if (distance > 1 && distance < splash->range) { double damage = splash->damage * (1 - distance/splash->range); drone->InflictDamage(damage); int destroyed = (drone->Integrity() < 1.0f); // then mark the drone for deletion: if (destroyed) { NetUtil::SendWepDestroy(drone); sim->CreateExplosion(drone->Location(), drone->Velocity(), 21 /* was LARGE_EXP */, 1.0f, 1.0f, splash->rgn); drone->SetLife(0); } } } } splashlist.destroy(); } } // +--------------------------------------------------------------------+ void Sim::ProcessEventTrigger(int type, int event_id, const char* ship, int param) { Text ship_name = ship; ListIter iter = events; while (++iter) { MissionEvent* event = iter.value(); if (event->IsPending() && event->Trigger() == type) { switch (type) { case MissionEvent::TRIGGER_DAMAGE: case MissionEvent::TRIGGER_DESTROYED: case MissionEvent::TRIGGER_JUMP: case MissionEvent::TRIGGER_LAUNCH: case MissionEvent::TRIGGER_DOCK: case MissionEvent::TRIGGER_TARGET: if (event->TriggerParam() <= param) { if (ship_name.indexOf(event->TriggerShip()) == 0) event->Activate(); } break; case MissionEvent::TRIGGER_NAVPT: if (event->TriggerParam() == param) { if (ship_name.indexOf(event->TriggerShip()) == 0) event->Activate(); } break; case MissionEvent::TRIGGER_EVENT: case MissionEvent::TRIGGER_SKIPPED: if (event->TriggerParam() == event_id) event->Activate(); break; } } } } double Sim::MissionClock() const { return (Game::GameTime() - start_time) / 1000.0; } // +--------------------------------------------------------------------+ void Sim::SkipCutscene() { Starshatter* stars = Starshatter::GetInstance(); if (stars && stars->InCutscene()) { ListIter iter = events; bool end = false; double end_time = 0; while (++iter && !end) { MissionEvent* event = iter.value(); if (event->IsPending() || event->IsActive()) { if (event->Event() == MissionEvent::END_SCENE || event->Event() == MissionEvent::END_MISSION) { end = true; end_time = event->Time(); } if (event->Event() == MissionEvent::FIRE_WEAPON) { event->Skip(); } else { event->Activate(); event->Execute(true); } } } double skip_time = end_time - MissionClock(); if (skip_time > 0) { Game::SkipGameTime(skip_time); } } } // +--------------------------------------------------------------------+ void Sim::ResolveTimeSkip(double seconds) { double skipped = 0; // allow elements to process hold time, and release as needed: ListIter elem = elements; while (++elem) elem->ExecFrame(seconds); // step through the skip, ten seconds at a time: if (active_region) { double total_skip = seconds; double frame_skip = 10; Ship* player = GetPlayerShip(); while (total_skip > frame_skip) { if (active_region->CanTimeSkip()) { active_region->ResolveTimeSkip(frame_skip); total_skip -= frame_skip; skipped += frame_skip; } // break out early if player runs into bad guys... else { total_skip = 0; } } if (total_skip > 0) active_region->ResolveTimeSkip(total_skip); skipped += total_skip; } // give player control after time skip: Ship* player_ship = GetPlayerShip(); if (player_ship) { player_ship->SetAutoNav(false); player_ship->SetThrottle(75); HUDView* hud = HUDView::GetInstance(); if (hud) hud->SetHUDMode(HUDView::HUD_MODE_TAC); if (IsTestMode()) player_ship->SetControls(0); } Game::SkipGameTime(skipped); CameraDirector::SetCameraMode(CameraDirector::MODE_COCKPIT); } // +--------------------------------------------------------------------+ ListIter Sim::GetMissionElements() { mission_elements.destroy(); ListIter iter = elements; while (++iter) { Element* elem = iter.value(); int num_live_ships = 0; for (int i = 0; i < elem->NumShips(); i++) { Ship* s = elem->GetShip(i+1); if (s && !s->IsDying() && !s->IsDead()) num_live_ships++; } if (elem->IsSquadron() || num_live_ships > 0) { MissionElement* msn_elem = CreateMissionElement(elem); if (msn_elem) mission_elements.append(msn_elem); } } return mission_elements; } MissionElement* Sim::CreateMissionElement(Element* elem) { MissionElement* msn_elem = 0; if (elem->IsSquadron()) { if (!elem->GetCarrier() || elem->GetCarrier()->Integrity() < 1) return msn_elem; } if (elem && !elem->IsNetObserver()) { msn_elem = new(__FILE__,__LINE__) MissionElement; msn_elem->SetName(elem->Name()); msn_elem->SetIFF(elem->GetIFF()); msn_elem->SetMissionRole(elem->Type()); if (elem->IsSquadron() && elem->GetCarrier()) { Ship* carrier = elem->GetCarrier(); msn_elem->SetCarrier(carrier->Name()); msn_elem->SetCount(elem->GetCount()); msn_elem->SetLocation(carrier->Location().OtherHand()); if (carrier->GetRegion()) msn_elem->SetRegion(carrier->GetRegion()->Name()); int squadron_index = 0; Hangar* hangar = FindSquadron(elem->Name(), squadron_index); if (hangar) { msn_elem->SetDeadCount(hangar->NumShipsDead(squadron_index)); msn_elem->SetMaintCount(hangar->NumShipsMaint(squadron_index)); const ShipDesign* design = hangar->SquadronDesign(squadron_index); msn_elem->SetDesign(design); Text design_path = design->path_name; design_path.setSensitive(false); if (design_path.indexOf("/Mods/Ships") == 0) { design_path = design_path.substring(11, 1000); msn_elem->SetPath(design_path); } } } else { msn_elem->SetSquadron(elem->GetSquadron()); msn_elem->SetCount(elem->NumShips()); } if (elem->GetCommander()) msn_elem->SetCommander(elem->GetCommander()->Name()); msn_elem->SetCombatGroup(elem->GetCombatGroup()); msn_elem->SetCombatUnit(elem->GetCombatUnit()); Ship* ship = elem->GetShip(1); if (ship) { if (ship->GetRegion()) msn_elem->SetRegion(ship->GetRegion()->Name()); msn_elem->SetLocation(ship->Location().OtherHand()); msn_elem->SetDesign(ship->Design()); msn_elem->SetPlayer(elem->Player()); msn_elem->SetCommandAI(elem->GetCommandAILevel()); msn_elem->SetHoldTime((int) elem->GetHoldTime()); msn_elem->SetZoneLock(elem->GetZoneLock()); msn_elem->SetHeading(ship->CompassHeading()); msn_elem->SetPlayable(elem->IsPlayable()); msn_elem->SetRogue(elem->IsRogue()); msn_elem->SetIntelLevel(elem->IntelLevel()); Text design_path = ship->Design()->path_name; design_path.setSensitive(false); if (design_path.indexOf("/Mods/Ships") == 0) { design_path = design_path.substring(11, 1000); msn_elem->SetPath(design_path); } msn_elem->SetRespawnCount(ship->RespawnCount()); } MissionLoad* loadout = new(__FILE__,__LINE__) MissionLoad; CopyMemory(loadout->GetStations(), elem->Loadout(), 16 * sizeof(int)); msn_elem->Loadouts().append(loadout); int num_obj = elem->NumObjectives(); for (int i = 0; i < num_obj; i++) { Instruction* o = elem->GetObjective(i); Instruction* instr = 0; instr = new(__FILE__,__LINE__) Instruction(*o); msn_elem->AddObjective(instr); } int num_inst = elem->NumInstructions(); for (int i = 0; i < num_inst; i++) { Text instr = elem->GetInstruction(i); msn_elem->AddInstruction(instr); } ListIter nav_iter = elem->GetFlightPlan(); while (++nav_iter) { Instruction* nav = nav_iter.value(); Instruction* npt = new(__FILE__,__LINE__) Instruction(nav->RegionName(), nav->Location(), nav->Action()); npt->SetFormation(nav->Formation()); npt->SetSpeed(nav->Speed()); npt->SetTarget(nav->TargetName()); npt->SetHoldTime(nav->HoldTime()); npt->SetFarcast(nav->Farcast()); npt->SetStatus(nav->Status()); msn_elem->AddNavPoint(npt); } for (int i = 0; i < elem->NumShips(); i++) { ship = elem->GetShip(i+1); if (ship) { MissionShip* s = new(__FILE__,__LINE__) MissionShip; s->SetName(ship->Name()); s->SetRegNum(ship->Registry()); s->SetRegion(ship->GetRegion()->Name()); s->SetLocation(ship->Location().OtherHand()); s->SetVelocity(ship->Velocity().OtherHand()); s->SetRespawns(ship->RespawnCount()); s->SetHeading(ship->CompassHeading()); s->SetIntegrity(ship->Integrity()); if (ship->GetDecoy()) s->SetDecoys(ship->GetDecoy()->Ammo()); if (ship->GetProbeLauncher()) s->SetProbes(ship->GetProbeLauncher()->Ammo()); int n; int ammo[16]; int fuel[4]; for (n = 0; n < 16; n++) { Weapon* w = ship->GetWeaponByIndex(n+1); if (w) ammo[n] = w->Ammo(); else ammo[n] = -10; } for (n = 0; n < 4; n++) { if (ship->Reactors().size() > n) fuel[n] = ship->Reactors()[n]->Charge(); else fuel[n] = -10; } s->SetAmmo(ammo); s->SetFuel(fuel); msn_elem->Ships().append(s); } } } return msn_elem; } Hangar* Sim::FindSquadron(const char* name, int& index) { Hangar* hangar = 0; ListIter iter = regions; while (++iter && !hangar) { SimRegion* rgn = iter.value(); ListIter s_iter = rgn->Carriers(); while (++s_iter && !hangar) { Ship* carrier = s_iter.value(); Hangar* h = carrier->GetHangar(); for (int i = 0; i < h->NumSquadrons() && !hangar; i++) { if (h->SquadronName(i) == name) { hangar = h; index = i; } } } } return hangar; } // +===================================================================-+ SimRegion::SimRegion(Sim* s, const char* n, int t) : sim(s), name(n), type(t), orbital_region(0), star_system(0), player_ship(0), grid(0), active(false), current_view(0), sim_time(0), ai_index(0), terrain(0) { if (sim) { star_system = sim->GetStarSystem(); } } SimRegion::SimRegion(Sim* s, OrbitalRegion* r) : sim(s), orbital_region(r), type(REAL_SPACE), star_system(0), player_ship(0), grid(0), active(false), current_view(0), sim_time(0), ai_index(0), terrain(0) { if (r) { star_system = r->System(); } if (orbital_region) { name = orbital_region->Name(); grid = new(__FILE__,__LINE__) Grid((int) orbital_region->Radius(), (int) orbital_region->GridSpace()); if (orbital_region->Type() == Orbital::TERRAIN) { TerrainRegion* trgn = (TerrainRegion*) orbital_region; terrain = new(__FILE__,__LINE__) Terrain(trgn); type = AIR_SPACE; } else if (orbital_region->Asteroids() > 0) { int asteroids = orbital_region->Asteroids(); for (int i = 0; i < asteroids; i++) { Point init_loc((rand()-16384.0f) * 30, (rand()-16384.0f) * 3, (rand()-16384.0f) * 30); sim->CreateAsteroid(init_loc, i, Random(1e7, 1e8), this); } } } else { name = Game::GetText("Unknown"); } } SimRegion::~SimRegion() { GRAPHIC_DESTROY(grid); delete terrain; explosions.destroy(); shots.destroy(); ships.destroy(); debris.destroy(); asteroids.destroy(); dead_ships.destroy(); for (int i = 0; i < 5; i++) track_database[i].destroy(); } int SimRegion::operator < (const SimRegion& r) const { return (orbital_region && r.orbital_region && *orbital_region < *r.orbital_region); } int SimRegion::operator <= (const SimRegion& r) const { return (orbital_region && r.orbital_region && *orbital_region <= *r.orbital_region); } // +--------------------------------------------------------------------+ void SimRegion::SetPlayerShip(Ship* ship) { // there can only be a player ship when playing the game locally if (Starshatter::GetInstance()) { int player_index = ships.index(ship); if (player_index >= 0) { if (sim->GetActiveRegion() != this) sim->ActivateRegion(this); AttachPlayerShip(player_index); } else { Print("SimRegion %s could not set player ship '%s' - not in region\n", name.data(), ship ? ship->Name() : "(null)"); } } // if this is a stand-alone server, set player ship to null else { if (player_ship) player_ship->SetControls(0); current_view = -1; player_ship = 0; } } void SimRegion::AttachPlayerShip(int index) { if (player_ship) player_ship->SetControls(0); current_view = index; player_ship = ships[current_view]; CameraDirector* cam_dir = CameraDirector::GetInstance(); if (cam_dir) cam_dir->SetShip(player_ship); if (sim->dust) sim->dust->Reset(player_ship->Location()); if (!sim->IsTestMode()) player_ship->SetControls(sim->ctrl); MouseController* mouse_con = MouseController::GetInstance(); if (mouse_con) mouse_con->SetActive(false); } void SimRegion::NextView() { if (ships.size()) { int original_view = current_view; do { current_view++; if (current_view >= ships.size()) { current_view = 0; } } while (ships[current_view]->Life() == 0 && current_view != original_view); if (current_view != original_view) { ClearSelection(); if (!sim->IsTestMode()) player_ship->SetControls(0); if (player_ship->Rep()) player_ship->Rep()->Show(); AttachPlayerShip(current_view); } } } bool SimRegion::IsSelected(Ship* s) { return selection.contains(s); } ListIter SimRegion::GetSelection() { return selection; } void SimRegion::SetSelection(Ship* newsel) { selection.clear(); selection.append(newsel); } void SimRegion::ClearSelection() { selection.clear(); } void SimRegion::AddSelection(Ship* newsel) { if (!newsel || newsel->GetFlightPhase() < Ship::ACTIVE || newsel->GetFlightPhase() >= Ship::RECOVERY) return; if (!selection.contains(newsel)) selection.append(newsel); } // +--------------------------------------------------------------------+ void SimRegion::Activate() { if (!sim) return; ListIter ship = ships; while (++ship) ship->Activate(sim->scene); ListIter shot = shots; while (++shot) shot->Activate(sim->scene); ListIter exp = explosions; while (++exp) exp->Activate(sim->scene); ListIter deb = debris; while (++deb) deb->Activate(sim->scene); ListIter a = asteroids; while (++a) a->Activate(sim->scene); if (grid) sim->scene.AddGraphic(grid); if (terrain) terrain->Activate(sim->scene); player_ship = 0; active = true; } // +--------------------------------------------------------------------+ void SimRegion::Deactivate() { if (!sim) return; ListIter ship = ships; while (++ship) ship->Deactivate(sim->scene); ListIter shot = shots; while (++shot) shot->Deactivate(sim->scene); ListIter exp = explosions; while (++exp) exp->Deactivate(sim->scene); ListIter deb = debris; while (++deb) deb->Deactivate(sim->scene); ListIter a = asteroids; while (++a) a->Deactivate(sim->scene); if (grid) sim->scene.DelGraphic(grid); if (terrain) terrain->Deactivate(sim->scene); player_ship = 0; active = false; for (int i = 0; i < 5; i++) track_database[i].destroy(); } // +--------------------------------------------------------------------+ void SimRegion::ExecFrame(double secs) { if (!sim) return; double seconds = secs; // DON'T REALLY KNOW WHAT PURPOSE THIS SERVES.... if (!active) { double max_frame = 3 * Game::GetMaxFrameLength(); long new_time = Game::GameTime(); double delta = new_time - sim_time; seconds = delta / 1000.0; if (seconds > max_frame) seconds = max_frame; } sim_time = Game::GameTime(); if (orbital_region) location = orbital_region->Location(); CameraDirector* cam_dir = CameraDirector::GetInstance(); Point ref; if (active && cam_dir) { ref = cam_dir->GetCamera()->Pos(); UpdateSky(seconds, ref); } if (terrain) terrain->ExecFrame(seconds); UpdateTracks(seconds); UpdateShips(seconds); UpdateShots(seconds); UpdateExplosions(seconds); if (!Game::Paused()) { DamageShips(); DockShips(); if (active) { CollideShips(); CrashShips(); } DestroyShips(); } if (active && cam_dir && player_ship) { Sound::SetListener(*(cam_dir->GetCamera()), player_ship->Velocity()); } } // +--------------------------------------------------------------------+ void SimRegion::ShowGrid(int show) { if (grid) { if (show) grid->Show(); else grid->Hide(); } } // +--------------------------------------------------------------------+ void SimRegion::UpdateSky(double seconds, const Point& ref) { Dust* dust = sim->dust; if (dust) { if (orbital_region && orbital_region->Type() == Orbital::TERRAIN) { dust->Hide(); } else { dust->Show(); dust->ExecFrame(seconds, ref); if (player_ship && dust->Hidden()) { dust->Reset(player_ship->Location()); dust->Show(); } } } ListIter a = asteroids; while (++a) { a->ExecFrame(seconds); } } // +--------------------------------------------------------------------+ void SimRegion::UpdateShips(double seconds) { int ship_index = 0; if (ai_index > ships.size()) ai_index = 0; ListIter ship_iter = ships; Ship* ship = 0; while (++ship_iter) { ship = ship_iter.value(); if (ship_index == ai_index || ship == player_ship) ship->SetAIMode(2); else ship->SetAIMode(1); ship->ExecFrame(seconds); ship_index++; } ai_index++; } // +--------------------------------------------------------------------+ void SimRegion::UpdateShots(double seconds) { ListIter shot_iter = shots; while (++shot_iter) { Shot* shot = shot_iter.value(); shot->ExecFrame(seconds); if (shot->Design()->flak) { SeekerAI* seeker = (SeekerAI*) shot->GetDirector(); if (shot->Life() < 0.02 || seeker && seeker->Overshot()) { shot->SetFuse(0.001); // set lifetime to ~zero sim->CreateSplashDamage(shot); } } if (shot->Life() < 0.01) { // died of old age NetUtil::SendWepDestroy(shot); if (shot->IsDrone()) drones.remove((Drone*) shot); shot_iter.removeItem(); delete shot; shot = 0; } } } // +--------------------------------------------------------------------+ void SimRegion::UpdateExplosions(double seconds) { ListIter exp_iter = explosions; while (++exp_iter) { Explosion* exp = exp_iter.value(); exp->ExecFrame(seconds); if (exp->Life() < 0.01) { // died of old age exp_iter.removeItem(); delete exp; } } ListIter debris_iter = debris; while (++debris_iter) { Debris* d = debris_iter.value(); d->ExecFrame(seconds); if (d->Life() < 0.01) { // died of old age debris_iter.removeItem(); delete d; } } } // +--------------------------------------------------------------------+ // Check for collisions between ships and shots, and apply damage. // Also look for damage to drones and debris. void SimRegion::DamageShips() { if (ships.size() == 0 || shots.size() == 0) return; Point impact; // FOR EACH SHOT IN THE REGION: ListIter shot_iter = shots; while (++shot_iter) { Shot* shot = shot_iter.value(); const Ship* owner = shot->Owner(); const char* owner_name; if (owner) owner_name = owner->Name(); else owner_name = "[KIA]"; // CHECK FOR COLLISION WITH A SHIP: ListIter ship_iter = ships; while (shot && ++ship_iter) { Ship* ship = ship_iter.value(); int hit = ship->HitBy(shot, impact); if (hit) { // recon imager: if (shot->Damage() < 0) { ShipStats* shooter = ShipStats::Find(owner_name); if (shooter) { shooter->AddEvent(SimEvent::SCAN_TARGET, ship->Name()); } } // live round: else if (shot->Damage() > 0) { int ship_destroyed = (!ship->InTransition() && ship->Integrity() < 1.0f); // then delete the ship: if (ship_destroyed) { NetUtil::SendObjKill(ship, owner, shot->IsMissile() ? NetObjKill::KILL_SECONDARY : NetObjKill::KILL_PRIMARY); Director* director; Print(" %s Killed %s (%s)\n", owner_name, ship->Name(), FormatGameTime()); if (owner) director = owner->GetDirector(); // alert the killer if (director && director->Type() > SteerAI::SEEKER && director->Type() < SteerAI::GROUND) { ShipAI* shipAI = (ShipAI*) director; shipAI->Splash(ship); } // record the kill ShipStats* killer = ShipStats::Find(owner_name); if (killer) { if (shot->IsMissile()) killer->AddEvent(SimEvent::MISSILE_KILL, ship->Name()); else killer->AddEvent(SimEvent::GUNS_KILL, ship->Name()); } if (owner && owner->GetIFF() != ship->GetIFF()) { if (ship->GetIFF() > 0 || owner->GetIFF() > 1) { killer->AddPoints(ship->Value()); Element* elem = owner->GetElement(); if (elem) { if (owner->GetElementIndex() > 1) { Ship* s = elem->GetShip(1); if (s) { ShipStats* cmdr_stats = ShipStats::Find(s->Name()); if (cmdr_stats) { cmdr_stats->AddCommandPoints(ship->Value()/2); } } } Element* cmdr = elem->GetCommander(); if (cmdr) { Ship* s = cmdr->GetShip(1); if (s) { ShipStats* cmdr_stats = ShipStats::Find(s->Name()); if (cmdr_stats) { cmdr_stats->AddCommandPoints(ship->Value()/2); } } } } } } ShipStats* killee = ShipStats::Find(ship->Name()); if (killee) killee->AddEvent(SimEvent::DESTROYED, owner_name); ship->DeathSpiral(); } } // finally, consume the shot: if (!shot->IsBeam()) { if (owner) { ShipStats* stats = ShipStats::Find(owner_name); if (shot->Design()->primary) stats->AddGunHit(); else if (shot->Damage() > 0) stats->AddMissileHit(); } NetUtil::SendWepDestroy(shot); if (shot->IsDrone()) drones.remove((Drone*) shot); shot_iter.removeItem(); delete shot; shot = 0; } else if (!shot->HitTarget()) { shot->SetHitTarget(true); if (owner) { ShipStats* stats = ShipStats::Find(owner_name); if (shot->Design()->primary) stats->AddGunHit(); } } } } // CHECK FOR COLLISION WITH A DRONE: if (shot && shot->Design()->target_type & Ship::DRONE) { ListIter drone_iter = drones; while (shot && ++drone_iter) { Drone* d = drone_iter.value(); if (d == shot || d->Owner() == owner) continue; int hit = d->HitBy(shot, impact); if (hit) { int destroyed = (d->Integrity() < 1.0f); // then mark the drone for deletion: if (destroyed) { NetUtil::SendWepDestroy(d); sim->CreateExplosion(d->Location(), d->Velocity(), 21, 1.0f, 1.0f, this); d->SetLife(0); } // finally, consume the shot: if (!shot->IsBeam()) { if (owner) { ShipStats* stats = ShipStats::Find(owner_name); if (shot->Design()->primary) stats->AddGunHit(); else stats->AddMissileHit(); } NetUtil::SendWepDestroy(shot); if (shot->IsDrone()) drones.remove((Drone*) shot); shot_iter.removeItem(); delete shot; shot = 0; } } } } // CHECK FOR COLLISION WITH DEBRIS: ListIter debris_iter = debris; while (shot && ++debris_iter) { Debris* d = debris_iter.value(); if (d->Radius() < 50) continue; int hit = d->HitBy(shot, impact); if (hit) { int destroyed = (d->Integrity() < 1.0f); // then delete the debris: if (destroyed) { sim->CreateExplosion(d->Location(), d->Velocity(), Explosion::LARGE_EXPLOSION, 1.0f, 1.0f, this); debris_iter.removeItem(); delete d; } // finally, consume the shot: if (!shot->IsBeam()) { NetUtil::SendWepDestroy(shot); if (shot->IsDrone()) drones.remove((Drone*) shot); shot_iter.removeItem(); delete shot; shot = 0; } } } // CHECK FOR COLLISION WITH ASTEROIDS: ListIter a_iter = asteroids; while (shot && ++a_iter) { Asteroid* a = a_iter.value(); int hit = a->HitBy(shot, impact); if (hit) { if (!shot->IsBeam()) { if (shot->IsDrone()) drones.remove((Drone*) shot); shot_iter.removeItem(); delete shot; shot = 0; } } } } } // +--------------------------------------------------------------------+ void SimRegion::CollideShips() { if (ships.size() < 2 && debris.size() < 1) return; List kill_list; int s_index = 0; ListIter ship_iter = ships; while (++ship_iter) { Ship* ship = ship_iter.value(); if (ship->InTransition() || ship->GetFlightPhase() < Ship::ACTIVE || ship->MissionClock() < 10000 || ship->IsNetObserver()) continue; int t_index = 0; ListIter targ_iter = ships; while (++targ_iter) { Ship* targ = targ_iter.value(); if (t_index++ <= s_index) continue; if (targ == ship) continue; if (targ->InTransition() || targ->GetFlightPhase() < Ship::ACTIVE || targ->MissionClock() < 10000 || targ->IsNetObserver()) continue; // ignore AI fighter collisions: if (ship->IsDropship() && ship != player_ship && targ->IsDropship() && targ != player_ship) continue; // don't collide with own runway! if (ship->IsAirborne() && ship->GetCarrier() == targ) continue; if (targ->IsAirborne() && targ->GetCarrier() == ship) continue; // impact: if (ship->CollidesWith(*targ)) { Vec3 tv1 = targ->Velocity(); Vec3 sv1 = ship->Velocity(); Physical::SemiElasticCollision(*ship, *targ); Vec3 tv2 = targ->Velocity(); Vec3 sv2 = ship->Velocity(); double dvs = (sv2-sv1).length(); double dvt = (tv2-tv1).length(); if (dvs > 20) dvs *= dvs; if (dvt > 20) dvt *= dvt; if (!NetGame::IsNetGameClient()) { double old_integrity = ship->Integrity(); ship->InflictDamage(dvs); double hull_damage = old_integrity - ship->Integrity(); NetUtil::SendObjDamage(ship, hull_damage); old_integrity = targ->Integrity(); targ->InflictDamage(dvt); hull_damage = old_integrity - targ->Integrity(); NetUtil::SendObjDamage(targ, hull_damage); } // then delete the ship: if (targ->Integrity() < 1.0f) { NetUtil::SendObjKill(targ, ship, NetObjKill::KILL_COLLISION); Print(" ship %s died in collision with %s (%s)\n", targ->Name(), ship->Name(), FormatGameTime()); if (!kill_list.contains(targ)) { ShipStats* r = ShipStats::Find(targ->Name()); if (r) r->AddEvent(SimEvent::COLLIDE, ship->Name()); if (targ->GetIFF() > 0 && ship->GetIFF() != targ->GetIFF()) { r = ShipStats::Find(ship->Name()); if (r) r->AddPoints(targ->Value()); } kill_list.insert(targ); } } if (ship->Integrity() < 1.0f) { NetUtil::SendObjKill(ship, targ, NetObjKill::KILL_COLLISION); Print(" ship %s died in collision with %s (%s)\n", ship->Name(), targ->Name(), FormatGameTime()); if (!kill_list.contains(ship)) { ShipStats* r = ShipStats::Find(ship->Name()); if (r) r->AddEvent(SimEvent::COLLIDE, targ->Name()); if (ship->GetIFF() > 0 && ship->GetIFF() != targ->GetIFF()) { r = ShipStats::Find(targ->Name()); if (r) r->AddPoints(ship->Value()); } kill_list.insert(ship); } } } } ListIter debris_iter = debris; while (++debris_iter) { Debris* d = debris_iter.value(); if (d->Radius() < 50) continue; if (ship->CollidesWith(*d)) { Vec3 tv1 = d->Velocity(); Vec3 sv1 = ship->Velocity(); Physical::SemiElasticCollision(*ship, *d); Vec3 tv2 = d->Velocity(); Vec3 sv2 = ship->Velocity(); if (!NetGame::IsNetGameClient()) { ship->InflictDamage((sv2-sv1).length()); } d->InflictDamage((tv2-tv1).length()); // then delete the debris: if (d->Integrity() < 1.0f) { sim->CreateExplosion(d->Location(), d->Velocity(), Explosion::LARGE_EXPLOSION, 1.0f, 1.0f, this); debris_iter.removeItem(); delete d; } if (ship->Integrity() < 1.0f) { if (!kill_list.contains(ship)) { ShipStats* r = ShipStats::Find(ship->Name()); if (r) r->AddEvent(SimEvent::COLLIDE, Game::GetText("DEBRIS")); kill_list.insert(ship); } } } } ListIter a_iter = asteroids; while (++a_iter) { Asteroid* a = a_iter.value(); if (ship->CollidesWith(*a)) { Vec3 sv1 = ship->Velocity(); Physical::SemiElasticCollision(*ship, *a); Vec3 sv2 = ship->Velocity(); if (!NetGame::IsNetGameClient()) { ship->InflictDamage((sv2-sv1).length() * 10); } if (ship->Integrity() < 1.0f) { if (!kill_list.contains(ship)) { ShipStats* r = ShipStats::Find(ship->Name()); if (r) r->AddEvent(SimEvent::COLLIDE, Game::GetText("ASTEROID")); kill_list.insert(ship); } } } } s_index++; } ListIter killed(kill_list); while (++killed) { Ship* kill = killed.value(); kill->DeathSpiral(); } } // +--------------------------------------------------------------------+ void SimRegion::CrashShips() { if (type != AIR_SPACE || NetGame::IsNetGameClient()) return; ListIter ship_iter = ships; while (++ship_iter) { Ship* ship = ship_iter.value(); if (!ship->IsGroundUnit() && !ship->InTransition() && ship->Class() != Ship::LCA && ship->AltitudeAGL() < ship->Radius()/2) { if (ship->GetFlightPhase() == Ship::ACTIVE || ship->GetFlightPhase() == Ship::APPROACH) { ship->InflictDamage(1e6); if (ship->Integrity() < 1.0f) { Print(" ship destroyed by crash: %s (%s)\n", ship->Name(), FormatGameTime()); ShipStats* r = ShipStats::Find(ship->Name()); if (r) r->AddEvent(SimEvent::CRASH); ship->DeathSpiral(); } } } } ListIter shot_iter = shots; while (++shot_iter) { Shot* shot = shot_iter.value(); if (shot->IsBeam() || shot->IsDecoy()) continue; if (shot->AltitudeMSL() < 5e3 && shot->AltitudeAGL() < 5) { // shot hit the ground, destroy it: NetUtil::SendWepDestroy(shot); if (shot->IsDrone()) drones.remove((Drone*) shot); shot_iter.removeItem(); delete shot; } } } // +--------------------------------------------------------------------+ void SimRegion::DestroyShips() { ListIter ship_iter = ships; while (++ship_iter) { Ship* ship = ship_iter.value(); if (ship->IsDead()) { // must use the iterator to remove the current // item from the container: ship_iter.removeItem(); DestroyShip(ship); } } } // +--------------------------------------------------------------------+ void SimRegion::DestroyShip(Ship* ship) { if (!ship) return; Ship* spawn = 0; ships.remove(ship); carriers.remove(ship); selection.remove(ship); Text rgn_name; if (ship->GetRegion()) rgn_name = ship->GetRegion()->Name(); bool player_destroyed = (player_ship == ship); char ship_name[64]; char ship_reg[64]; strcpy_s(ship_name, ship->Name()); strcpy_s(ship_reg, ship->Registry()); ShipDesign* ship_design = (ShipDesign*) ship->Design(); int ship_iff = ship->GetIFF(); int cmd_ai = ship->GetCommandAILevel(); bool respawn = sim->IsTestMode() && !ship->IsGroundUnit(); bool observe = false; if (!respawn) respawn = ship->RespawnCount() > 0; if (sim->netgame) { if (!respawn) observe = player_destroyed; } if (respawn || observe) { if (!sim->netgame || !respawn) ship->SetRespawnLoc(RandomPoint() * 2); Point spawn_loc = ship->RespawnLoc(); if (ship->IsAirborne() && spawn_loc.z < 5e3) spawn_loc.z = Random(8e3, 10e3); spawn = sim->CreateShip(ship_name, ship_reg, ship_design, rgn_name, spawn_loc, ship_iff, cmd_ai, observe ? 0 : ship->GetLoadout()); spawn->SetRespawnCount(ship->RespawnCount() - 1); spawn->SetNetObserver(observe); if (sim->netgame && respawn) sim->netgame->Respawn(ship->GetObjID(), spawn); int n = strlen(ship_name); if (n > 2) { if (ship_name[n-2] == ' ' && isdigit(ship_name[n-1])) ship_name[n-2] = 0; } Element* elem = sim->FindElement(ship_name); if (elem) elem->AddShip(spawn, ship->GetOrigElementIndex()); else Print("Warning: No Element found for '%s' on respawn.\n", ship_name); if (player_destroyed) SetPlayerShip(spawn); } else { // close mission, return to menu: if (player_destroyed) { Starshatter* stars = Starshatter::GetInstance(); if (stars) stars->SetGameMode(Starshatter::PLAN_MODE); } } sim->ProcessEventTrigger(MissionEvent::TRIGGER_DESTROYED, 0, ship->Name()); dead_ships.insert(ship); ship->Destroy(); } // +--------------------------------------------------------------------+ void SimRegion::NetDockShip(Ship* ship, Ship* carrier, FlightDeck* deck) { if (!ship || !carrier || !deck) return; deck->Dock(ship); } // +--------------------------------------------------------------------+ Ship* SimRegion::FindShip(const char* ship_name) { Ship* ship = 0; if (ship_name && *ship_name) { int name_len = strlen(ship_name); ListIter ship_iter = ships; while (++ship_iter && !ship) { Ship* test = ship_iter.value(); if (!strncmp(test->Name(), ship_name, name_len)) { int test_len = strlen(test->Name()); // The only fuzzy match is for element indices. // The desired name "Alpha" matches "Alpha 1" and "Alpha 2" // but not "Alpha-Centauri" if (test_len > name_len && test->Name()[name_len] != ' ') continue; ship = test; } } } return ship; } Ship* SimRegion::FindShipByObjID(DWORD objid) { Ship* ship = 0; ListIter ship_iter = ships; while (++ship_iter && !ship) { Ship* test = ship_iter.value(); if (test->GetObjID() == objid) ship = test; } return ship; } Shot* SimRegion::FindShotByObjID(DWORD objid) { Shot* shot = 0; ListIter shot_iter = shots; while (++shot_iter && !shot) { Shot* test = shot_iter.value(); if (test->GetObjID() == objid) shot = test; } if (!shot) { ListIter drone_iter = drones; while (++drone_iter && !shot) { Drone* test = drone_iter.value(); if (test->GetObjID() == objid) shot = test; } } return shot; } // +--------------------------------------------------------------------+ void SimRegion::DockShips() { if (ships.size() == 0) return; ListIter ship_iter = ships; while (++ship_iter) { Ship* ship = ship_iter.value(); int docked = (ship->GetFlightPhase() == Ship::DOCKED); if (docked) { sim->ProcessEventTrigger(MissionEvent::TRIGGER_DOCK, 0, ship->Name()); // who did this ship dock with? Ship* carrier = ship->GetCarrier(); if (carrier) { ShipStats* s = ShipStats::Find(ship->Name()); if (s) { if (ship->IsAirborne()) s->AddEvent(SimEvent::LAND, carrier->Name()); else s->AddEvent(SimEvent::DOCK, carrier->Name()); } ShipStats* c = ShipStats::Find(carrier->Name()); if (c) c->AddEvent(SimEvent::RECOVER_SHIP, ship->Name()); } // then delete the ship: int player_docked = (player_ship == ship); char ship_name[33]; strcpy_s(ship_name, ship->Name()); selection.remove(ship); dead_ships.insert(ship_iter.removeItem()); ship->Destroy(); if (player_docked) { // close mission, return to menu: Starshatter* stars = Starshatter::GetInstance(); if (stars) stars->SetGameMode(Starshatter::PLAN_MODE); } if (carrier) Print(" %s Docked with %s\n", ship_name, carrier->Name()); } } } // +--------------------------------------------------------------------+ void SimRegion::InsertObject(Ship* ship) { if (!ship) return; SimRegion* orig = ship->GetRegion(); if (orig != this) { if (orig != 0) { if (orig->active) ship->Deactivate(sim->scene); orig->ships.remove(ship); orig->carriers.remove(ship); orig->selection.remove(ship); } ships.append(ship); if (ship->NumFlightDecks()) carriers.append(ship); TranslateObject(ship); ship->SetRegion(this); if (active) ship->Activate(sim->scene); } } void SimRegion::InsertObject(Shot* shot) { if (!shot) return; SimRegion* orig = shot->GetRegion(); if (orig != this) { if (orig != 0) orig->shots.remove(shot); shots.append(shot); if (shot->IsDrone()) drones.append((Drone*) shot); TranslateObject(shot); shot->SetRegion(this); if (active) shot->Activate(sim->scene); } } void SimRegion::InsertObject(Explosion* exp) { if (!exp) return; SimRegion* orig = exp->GetRegion(); if (orig != this) { if (orig != 0) orig->explosions.remove(exp); explosions.append(exp); TranslateObject(exp); exp->SetRegion(this); if (active) exp->Activate(sim->scene); } } void SimRegion::InsertObject(Debris* d) { if (!d) return; SimRegion* orig = d->GetRegion(); if (orig != this) { if (orig != 0) orig->debris.remove(d); debris.append(d); TranslateObject(d); d->SetRegion(this); if (active) d->Activate(sim->scene); } } void SimRegion::InsertObject(Asteroid* a) { if (!a) return; SimRegion* orig = a->GetRegion(); if (orig != this) { if (orig != 0) orig->asteroids.remove(a); asteroids.append(a); TranslateObject(a); a->SetRegion(this); if (active) a->Activate(sim->scene); } } // +--------------------------------------------------------------------+ void SimRegion::TranslateObject(SimObject* object) { if (orbital_region) location = orbital_region->Location(); if (object) { SimRegion* orig = object->GetRegion(); if (orig) { Point delta = Location() - orig->Location(); delta = delta.OtherHand(); object->TranslateBy(delta); } } } // +--------------------------------------------------------------------+ List& SimRegion::TrackList(int iff) { if (iff >= 0 && iff < 5) return track_database[iff]; static List empty; return empty; } void SimRegion::UpdateTracks(double seconds) { for (int i = 0; i < 5; i++) { ListIter track_iter = track_database[i]; while (++track_iter) { Contact* t = track_iter.value(); Ship* c_ship = t->GetShip(); Shot* c_shot = t->GetShot(); double c_life = 0; if (c_ship) { c_life = c_ship->Life(); // look for quantum jumps and orbit transitions: if (c_ship->GetRegion() != this || c_ship->IsNetObserver()) c_life = 0; } else if (c_shot) c_life = c_shot->Life(); if (t->Age() < 0 || c_life == 0) { track_iter.removeItem(); delete t; } else { t->Reset(); } } } } // +--------------------------------------------------------------------+ bool SimRegion::CanTimeSkip() const { bool ok = false; if (player_ship) { ok = true; for (int i = 0; ok && i < ships.size(); i++) { Ship* s = ships[i]; if (s != player_ship && s->GetIFF() && s->GetIFF() != player_ship->GetIFF()) { double dist = Point(s->Location() - player_ship->Location()).length(); if (s->IsStarship()) ok = dist > 60e3; else ok = dist > 30e3; } } } return ok; } // +--------------------------------------------------------------------+ void SimRegion::ResolveTimeSkip(double seconds) { for (int i = 0; i < ships.size(); i++) { Ship* ship = ships[i]; Ship* ward = ship->GetWard(); // remember to burn fuel and fix stuff... ship->ExecSystems(seconds); ship->ExecMaintFrame(seconds); ship->ClearTrack(); ListIter contact = ship->ContactList(); while (++contact) contact->ClearTrack(); if (ship->IsStatic()) continue; // if ship is cleared inbound, land him: InboundSlot* inbound = ship->GetInbound(); if (inbound) { if (inbound->Cleared()) { FlightDeck* deck = inbound->GetDeck(); if (deck) { ship->SetCarrier((Ship*) deck->GetCarrier(), deck); ship->SetFlightPhase(Ship::DOCKED); ship->Stow(); deck->Clear(inbound->Index()); } } // cleared or not, once you're inbound, don't seek navpoints: continue; } if (ship->GetHangar()) { ship->GetHangar()->ExecFrame(seconds); List& flight_decks = ship->FlightDecks(); for (int n = 0; n < flight_decks.size(); n++) flight_decks[n]->ExecFrame(seconds); } Instruction* navpt = ship->GetNextNavPoint(); Point dest = ship->Location(); double speed = 500; double space = 2.0e3 * (ship->GetElementIndex() - 1); if (ship->IsStarship()) space *= 5; if (navpt && navpt->Action() == Instruction::LAUNCH) { ship->SetNavptStatus(navpt, Instruction::COMPLETE); navpt = ship->GetNextNavPoint(); } if (navpt) { dest = navpt->Location().OtherHand(); speed = navpt->Speed(); } else if (ward) { Point delta = ship->Location() - ward->Location(); delta.y = 0; if (delta.length() > 25e3) { delta.Normalize(); dest = ward->Location() + delta * 25e3; } } Point delta = dest - ship->Location(); Point unit = delta; double dist = unit.Normalize() - space; if (dist > 1e3) { if (speed < 50) speed = 500; double etr = dist / speed; if (etr > seconds) etr = seconds; Point trans = unit * (speed * etr); if (ship->GetFuelLevel() > 1) { ship->MoveTo(ship->Location() + trans); ship->SetVelocity(unit * speed); } ship->LookAt(dest); if (ship->IsStarship()) { ship->SetFLCSMode(Ship::FLCS_HELM); ship->SetHelmHeading(ship->CompassHeading()); ship->SetHelmPitch(ship->CompassPitch()); } } else if (navpt && navpt->Status() <= Instruction::ACTIVE) { ship->SetNavptStatus(navpt, Instruction::COMPLETE); } if (ward) { Point ward_heading = ward->Heading(); ward_heading.y = 0; ward_heading.Normalize(); if (ship->GetFuelLevel() > 1) { ship->SetVelocity(ward->Velocity()); } ship->LookAt(ship->Location() + ward_heading * 1e6); if (ship->IsStarship()) { ship->SetFLCSMode(Ship::FLCS_HELM); ship->SetHelmHeading(ship->CompassHeading()); ship->SetHelmPitch(ship->CompassPitch()); } } if (dist > 1 || ward) { for (int j = 0; j < ships.size(); j++) { Ship* test = ships[j]; if (ship != test && test->Mass() >= ship->Mass()) { Point delta = ship->Location() - test->Location(); if (delta.length() < ship->Radius() * 2 + test->Radius() * 2) { ship->MoveTo(test->Location() + RandomPoint().OtherHand()); } } } } } DockShips(); } // +--------------------------------------------------------------------+ void SimRegion::CommitMission() { for (int i = 0; i < dead_ships.size(); i++) { Ship* s = dead_ships[i]; if (s->GetCombatUnit() && s->GetFlightPhase() != Ship::DOCKED) s->GetCombatUnit()->Kill(1); } for (int i = 0; i < ships.size(); i++) { Ship* s = ships[i]; CombatUnit* u = s->GetCombatUnit(); if (u) { Point u_loc = s->Location().OtherHand(); if (u_loc.z > 20e3) u_loc.z = 20e3; else if (u_loc.z < -20e3) u_loc.z = -20e3; if (u->IsStarship()) { u->SetRegion(s->GetRegion()->Name()); u->MoveTo(u_loc); } if (!u->IsDropship()) { if (s->Integrity() < 1) u->Kill(1); else u->SetSustainedDamage(s->Design()->integrity - s->Integrity()); } CombatGroup* g = u->GetCombatGroup(); if (g && g->Type() > CombatGroup::FLEET && g->GetFirstUnit() == u) { if (!g->IsZoneLocked()) g->SetRegion(s->GetRegion()->Name()); else u->SetRegion(g->GetRegion()); g->MoveTo(u_loc); } } } } // +--------------------------------------------------------------------+ const char* FormatGameTime() { static char txt[64]; int t = Game::GameTime(); int h = ( t / 3600000); int m = ((t - h*3600000) / 60000); int s = ((t - h*3600000 - m*60000) / 1000); int e = ( t - h*3600000 - m*60000 - s*1000); if (h > 0) sprintf_s(txt, "%02d:%02d:%02d.%03d", h,m,s,e); else sprintf_s(txt, "%02d:%02d.%03d", m,s,e); return txt; }