diff options
author | milo24x7@gmail.com <milo24x7@gmail.com@076cb2c4-205e-83fd-5cf3-1be9aa105544> | 2013-07-07 22:08:49 +0000 |
---|---|---|
committer | milo24x7@gmail.com <milo24x7@gmail.com@076cb2c4-205e-83fd-5cf3-1be9aa105544> | 2013-07-07 22:08:49 +0000 |
commit | d17521c8b9085a91d08fecfd0b51bbbf7b1dccac (patch) | |
tree | 4673104b47dc68a079cac9f94deefd48dfcb66fa /Stars45/Ship.cpp | |
parent | 1de4b2bdbb019be6f1b7262c3eba5568d7682edd (diff) | |
download | starshatter-d17521c8b9085a91d08fecfd0b51bbbf7b1dccac.zip starshatter-d17521c8b9085a91d08fecfd0b51bbbf7b1dccac.tar.gz starshatter-d17521c8b9085a91d08fecfd0b51bbbf7b1dccac.tar.bz2 |
Updated open source license declaration and fixed some formatting issues.
Diffstat (limited to 'Stars45/Ship.cpp')
-rw-r--r-- | Stars45/Ship.cpp | 10647 |
1 files changed, 5336 insertions, 5311 deletions
diff --git a/Stars45/Ship.cpp b/Stars45/Ship.cpp index c90038c..5fc98cd 100644 --- a/Stars45/Ship.cpp +++ b/Stars45/Ship.cpp @@ -1,5311 +1,5336 @@ -/* Project Starshatter 5.0
- Destroyer Studios LLC
- Copyright (C) 1997-2007. All Rights Reserved.
-
- SUBSYSTEM: Stars.exe
- FILE: Ship.cpp
- AUTHOR: John DiCamillo
-
-
- OVERVIEW
- ========
- Starship class
-*/
-
-#include "MemDebug.h"
-#include "Ship.h"
-#include "ShipAI.h"
-#include "ShipCtrl.h"
-#include "ShipDesign.h"
-#include "ShipKiller.h"
-#include "Shot.h"
-#include "Drone.h"
-#include "SeekerAI.h"
-#include "HardPoint.h"
-#include "Weapon.h"
-#include "WeaponGroup.h"
-#include "Shield.h"
-#include "ShieldRep.h"
-#include "Computer.h"
-#include "FlightComp.h"
-#include "Drive.h"
-#include "QuantumDrive.h"
-#include "Farcaster.h"
-#include "Thruster.h"
-#include "Power.h"
-#include "FlightDeck.h"
-#include "LandingGear.h"
-#include "Hangar.h."
-#include "Sensor.h"
-#include "Contact.h"
-#include "CombatUnit.h"
-#include "Element.h"
-#include "Instruction.h"
-#include "RadioMessage.h"
-#include "RadioHandler.h"
-#include "RadioTraffic.h"
-#include "NavLight.h"
-#include "NavSystem.h"
-#include "NavAI.h"
-#include "DropShipAI.h"
-#include "Explosion.h"
-#include "MissionEvent.h"
-#include "ShipSolid.h"
-#include "Sim.h"
-#include "SimEvent.h"
-#include "StarSystem.h"
-#include "TerrainRegion.h"
-#include "Terrain.h"
-#include "System.h"
-#include "Component.h"
-#include "KeyMap.h"
-#include "RadioView.h"
-#include "AudioConfig.h"
-#include "CameraDirector.h"
-#include "HUDView.h"
-#include "Random.h"
-#include "RadioVox.h"
-
-#include "NetGame.h"
-#include "NetUtil.h"
-
-#include "MotionController.h"
-#include "Keyboard.h"
-#include "Joystick.h"
-#include "Bolt.h"
-#include "Game.h"
-#include "Solid.h"
-#include "Shadow.h"
-#include "Skin.h"
-#include "Sprite.h"
-#include "Light.h"
-#include "Bitmap.h"
-#include "Button.h"
-#include "Sound.h"
-#include "DataLoader.h"
-
-#include "Parser.h"
-#include "Reader.h"
-
-// +----------------------------------------------------------------------+
-
-static int base_contact_id = 0;
-static double range_min = 0;
-static double range_max = 250e3;
-
-int Ship::control_model = 0; // standard
-int Ship::flight_model = 0; // standard
-int Ship::landing_model = 0; // standard
-double Ship::friendly_fire_level = 1; // 100%
-
-const int HIT_NOTHING = 0;
-const int HIT_HULL = 1;
-const int HIT_SHIELD = 2;
-const int HIT_BOTH = 3;
-const int HIT_TURRET = 4;
-
-// +----------------------------------------------------------------------+
-
-Ship::Ship(const char* ship_name, const char* reg_num, ShipDesign* ship_dsn, int IFF, int cmd_ai, const int* load)
-: IFF_code(IFF), killer(0), throttle(0), augmenter(false), throttle_request(0),
-shield(0), shieldRep(0), main_drive(0), quantum_drive(0), farcaster(0),
-check_fire(false), probe(0), sensor_drone(0), primary(0), secondary(1),
-cmd_chain_index(0), target(0), subtarget(0), radio_orders(0), launch_point(0),
-g_force(0.0f), sensor(0), navsys(0), flcs(0), hangar(0), respawns(0), invulnerable(false),
-thruster(0), decoy(0), ai_mode(2), command_ai_level(cmd_ai), flcs_mode(FLCS_AUTO), loadout(0),
-emcon(3), old_emcon(3), master_caution(false), cockpit(0), gear(0), skin(0),
-auto_repair(true), last_repair_time(0), last_eval_time(0), last_beam_time(0), last_bolt_time(0),
-warp_fov(1), flight_phase(LAUNCH), launch_time(0), carrier(0), dock(0), ff_count(0),
-inbound(0), element(0), director_info("Init"), combat_unit(0), net_control(0),
-track(0), ntrack(0), track_time(0), helm_heading(0.0f), helm_pitch(0.0f),
-altitude_agl(-1.0e6f), transition_time(0.0f), transition_type(TRANSITION_NONE),
-friendly_fire_time(0), ward(0), net_observer_mode(false), orig_elem_index(-1)
-{
- sim = Sim::GetSim();
-
- strcpy_s(name, ship_name);
- if (reg_num && *reg_num)
- strcpy_s(regnum, reg_num);
- else regnum[0] = 0;
-
- design = ship_dsn;
-
- if (!design) {
- char msg[256];
- sprintf_s(msg, "No ship design found for '%s'\n", ship_name);
- Game::Panic(msg);
- }
-
- obj_type = SimObject::SIM_SHIP;
-
- radius = design->radius;
- mass = design->mass;
- integrity = design->integrity;
- vlimit = design->vlimit;
-
- agility = design->agility;
- wep_mass = 0.0f;
- wep_resist = 0.0f;
-
- CL = design->CL;
- CD = design->CD;
- stall = design->stall;
-
- chase_vec = design->chase_vec;
- bridge_vec = design->bridge_vec;
-
- acs = design->acs;
- pcs = design->acs;
-
- auto_repair = design->repair_auto;
-
- while (!base_contact_id)
- base_contact_id = rand() % 1000;
-
- contact_id = base_contact_id++;
- int sys_id = 0;
-
- for (int i = 0; i < design->reactors.size(); i++) {
- PowerSource* reactor = new(__FILE__,__LINE__) PowerSource(*design->reactors[i]);
- reactor->SetShip(this);
- reactor->SetID(sys_id++);
- reactors.append(reactor);
- systems.append(reactor);
- }
-
- for (int i = 0; i < design->drives.size(); i++) {
- Drive* drive = new(__FILE__,__LINE__) Drive(*design->drives[i]);
- drive->SetShip(this);
- drive->SetID(sys_id++);
-
- int src_index = drive->GetSourceIndex();
- if (src_index >= 0 && src_index < reactors.size())
- reactors[src_index]->AddClient(drive);
-
- drives.append(drive);
- systems.append(drive);
- }
-
- if (design->quantum_drive) {
- quantum_drive = new(__FILE__,__LINE__) QuantumDrive(*design->quantum_drive);
- quantum_drive->SetShip(this);
- quantum_drive->SetID(sys_id++);
-
- int src_index = quantum_drive->GetSourceIndex();
- if (src_index >= 0 && src_index < reactors.size())
- reactors[src_index]->AddClient(quantum_drive);
-
- quantum_drive->SetShip(this);
- systems.append(quantum_drive);
- }
-
- if (design->farcaster) {
- farcaster = new(__FILE__,__LINE__) Farcaster(*design->farcaster);
- farcaster->SetShip(this);
- farcaster->SetID(sys_id++);
-
- int src_index = farcaster->GetSourceIndex();
- if (src_index >= 0 && src_index < reactors.size())
- reactors[src_index]->AddClient(farcaster);
-
- farcaster->SetShip(this);
- systems.append(farcaster);
- }
-
- if (design->thruster) {
- thruster = new(__FILE__,__LINE__) Thruster(*design->thruster);
- thruster->SetShip(this);
- thruster->SetID(sys_id++);
-
- int src_index = thruster->GetSourceIndex();
- if (src_index >= 0 && src_index < reactors.size())
- reactors[src_index]->AddClient(thruster);
-
- thruster->SetShip(this);
- systems.append(thruster);
- }
-
- if (design->shield) {
- shield = new(__FILE__,__LINE__) Shield(*design->shield);
- shield->SetShip(this);
- shield->SetID(sys_id++);
-
- int src_index = shield->GetSourceIndex();
- if (src_index >= 0 && src_index < reactors.size())
- reactors[src_index]->AddClient(shield);
-
- if (design->shield_model) {
- shieldRep = new(__FILE__,__LINE__) ShieldRep;
- shieldRep->UseModel(design->shield_model);
- }
-
- systems.append(shield);
- }
-
- for (int i = 0; i < design->flight_decks.size(); i++) {
- FlightDeck* deck = new(__FILE__,__LINE__) FlightDeck(*design->flight_decks[i]);
- deck->SetShip(this);
- deck->SetCarrier(this);
- deck->SetID(sys_id++);
- deck->SetIndex(i);
-
- int src_index = deck->GetSourceIndex();
- if (src_index >= 0 && src_index < reactors.size())
- reactors[src_index]->AddClient(deck);
-
- flight_decks.append(deck);
- systems.append(deck);
- }
-
- if (design->flight_decks.size() > 0) {
- if (!hangar) {
- hangar = new(__FILE__,__LINE__) Hangar;
- hangar->SetShip(this);
- }
- }
-
- if (design->squadrons.size() > 0) {
- if (!hangar) {
- hangar = new(__FILE__,__LINE__) Hangar;
- hangar->SetShip(this);
- }
-
- for (int i = 0; i < design->squadrons.size(); i++) {
- ShipSquadron* s = design->squadrons[i];
- hangar->CreateSquadron(s->name, 0, s->design, s->count, GetIFF(), 0, 0, s->avail);
- }
- }
-
- if (design->gear) {
- gear = new(__FILE__,__LINE__) LandingGear(*design->gear);
- gear->SetShip(this);
- gear->SetID(sys_id++);
-
- int src_index = gear->GetSourceIndex();
- if (src_index >= 0 && src_index < reactors.size())
- reactors[src_index]->AddClient(gear);
-
- systems.append(gear);
- }
-
- if (design->sensor) {
- sensor = new(__FILE__,__LINE__) Sensor(*design->sensor);
- sensor->SetShip(this);
- sensor->SetID(sys_id++);
-
- int src_index = sensor->GetSourceIndex();
- if (src_index >= 0 && src_index < reactors.size())
- reactors[src_index]->AddClient(sensor);
-
- if (IsStarship() || IsStatic() || !strncmp(design->name, "Camera", 6))
- sensor->SetMode(Sensor::CST);
-
- systems.append(sensor);
- }
-
- int wep_index = 1;
-
- for (int i = 0; i < design->weapons.size(); i++) {
- Weapon* gun = new(__FILE__,__LINE__) Weapon(*design->weapons[i]);
- gun->SetID(sys_id++);
- gun->SetOwner(this);
- gun->SetIndex(wep_index++);
-
- int src_index = gun->GetSourceIndex();
- if (src_index >= 0 && src_index < reactors.size())
- reactors[src_index]->AddClient(gun);
-
- WeaponGroup* group = FindWeaponGroup(gun->Group());
- group->AddWeapon(gun);
- group->SetAbbreviation(gun->Abbreviation());
-
- systems.append(gun);
-
- if (IsDropship() && gun->GetTurret())
- gun->SetFiringOrders(Weapon::POINT_DEFENSE);
- else
- gun->SetFiringOrders(Weapon::MANUAL);
- }
-
- int loadout_size = design->hard_points.size();
-
- if (load && loadout_size > 0) {
- loadout = new(__FILE__,__LINE__) int[loadout_size];
-
- for (int i = 0; i < loadout_size; i++) {
- int mounted_weapon = loadout[i] = load[i];
-
- if (mounted_weapon < 0)
- continue;
-
- Weapon* missile = design->hard_points[i]->CreateWeapon(mounted_weapon);
-
- if (missile) {
- missile->SetID(sys_id++);
- missile->SetOwner(this);
- missile->SetIndex(wep_index++);
-
- WeaponGroup* group = FindWeaponGroup(missile->Group());
- group->AddWeapon(missile);
- group->SetAbbreviation(missile->Abbreviation());
-
- systems.append(missile);
- }
- }
- }
-
- if (weapons.size() > 1) {
- primary = -1;
- secondary = -1;
-
- for (int i = 0; i < weapons.size(); i++) {
- WeaponGroup* group = weapons[i];
- if (group->IsPrimary() && primary < 0) {
- primary = i;
-
- // turrets on fighters are set to point defense by default,
- // this forces the primary turret back to manual control
- group->SetFiringOrders(Weapon::MANUAL);
- }
-
- else if (group->IsMissile() && secondary < 0) {
- secondary = i;
- }
- }
-
- if (primary < 0) primary = 0;
- if (secondary < 0) secondary = 1;
-
- if (weapons.size() > 4) {
- ::Print("WARNING: Ship '%s' type '%s' has %d wep groups (max=4)\n",
- Name(), DesignName(), weapons.size());
- }
- }
-
- if (design->decoy) {
- decoy = new(__FILE__,__LINE__) Weapon(*design->decoy);
- decoy->SetOwner(this);
- decoy->SetID(sys_id++);
- decoy->SetIndex(wep_index++);
-
- int src_index = decoy->GetSourceIndex();
- if (src_index >= 0 && src_index < reactors.size())
- reactors[src_index]->AddClient(decoy);
-
- systems.append(decoy);
- }
-
- for (int i = 0; i < design->navlights.size(); i++) {
- NavLight* navlight = new(__FILE__,__LINE__) NavLight(*design->navlights[i]);
- navlight->SetShip(this);
- navlight->SetID(sys_id++);
- navlight->SetOffset(((DWORD) this) << 2);
- navlights.append(navlight);
- systems.append(navlight);
- }
-
- if (design->navsys) {
- navsys = new(__FILE__,__LINE__) NavSystem(*design->navsys);
- navsys->SetShip(this);
- navsys->SetID(sys_id++);
-
- int src_index = navsys->GetSourceIndex();
- if (src_index >= 0 && src_index < reactors.size())
- reactors[src_index]->AddClient(navsys);
-
- systems.append(navsys);
- }
-
- if (design->probe) {
- probe = new(__FILE__,__LINE__) Weapon(*design->probe);
- probe->SetOwner(this);
- probe->SetID(sys_id++);
- probe->SetIndex(wep_index++);
-
- int src_index = probe->GetSourceIndex();
- if (src_index >= 0 && src_index < reactors.size())
- reactors[src_index]->AddClient(probe);
-
- systems.append(probe);
- }
-
- for (int i = 0; i < design->computers.size(); i++) {
- Computer* comp = 0;
-
- if (design->computers[i]->Subtype() == Computer::FLIGHT) {
- flcs = new(__FILE__,__LINE__) FlightComp(*design->computers[i]);
-
- flcs->SetShip(this);
- flcs->SetMode(flcs_mode);
- flcs->SetVelocityLimit(vlimit);
-
- if (thruster)
- flcs->SetTransLimit(thruster->TransXLimit(),
- thruster->TransYLimit(),
- thruster->TransZLimit());
- else
- flcs->SetTransLimit(design->trans_x,
- design->trans_y,
- design->trans_z);
-
- comp = flcs;
- }
- else {
- comp = new(__FILE__,__LINE__) Computer(*design->computers[i]);
- }
-
- comp->SetShip(this);
- comp->SetID(sys_id++);
- int src_index = comp->GetSourceIndex();
- if (src_index >= 0 && src_index < reactors.size())
- reactors[src_index]->AddClient(comp);
-
- computers.append(comp);
- systems.append(comp);
- }
-
- radio_orders = new(__FILE__,__LINE__) Instruction("", Point(0,0,0));
-
- // Load Detail Set:
- for (int i = 0; i < DetailSet::MAX_DETAIL; i++) {
- if (design->models[i].size() > 0) {
- Solid* solid = new(__FILE__,__LINE__) ShipSolid(this);
- solid->UseModel(design->models[i].at(0));
- solid->CreateShadows(1);
-
- Point* offset = 0;
- Point* spin = 0;
-
- if (design->offsets[i].size() > 0)
- offset = new(__FILE__,__LINE__) Point(*design->offsets[i].at(0));
-
- if (design->spin_rates.size() > 0)
- spin = new(__FILE__,__LINE__) Point(*design->spin_rates.at(0));
-
- detail_level = detail.DefineLevel(design->feature_size[i], solid, offset, spin);
- }
-
- if (design->models[i].size() > 1) {
- for (int n = 1; n < design->models[i].size(); n++) {
- Solid* solid = new(__FILE__,__LINE__) ShipSolid(this); //Solid;
- solid->UseModel(design->models[i].at(n));
- solid->CreateShadows(1);
-
- Point* offset = 0;
- Point* spin = 0;
-
- if (design->offsets[i].size() > n)
- offset = new(__FILE__,__LINE__) Point(*design->offsets[i].at(n));
-
- if (design->spin_rates.size() > n)
- spin = new(__FILE__,__LINE__) Point(*design->spin_rates.at(n));
-
- detail.AddToLevel(detail_level, solid, offset, spin);
- }
- }
- }
-
- // start with lowest available detail:
- detail_level = 0; // this is highest -> detail.NumLevels()-1);
- rep = detail.GetRep(detail_level);
-
- if (design->cockpit_model) {
- cockpit = new(__FILE__,__LINE__) Solid;
- cockpit->UseModel(design->cockpit_model);
- cockpit->SetForeground(true);
- }
-
- if (design->main_drive >= 0 && design->main_drive < drives.size())
- main_drive = drives[design->main_drive];
-
- // only use light from drives:
- light = 0;
-
- // setup starship helm stuff:
- if (IsStarship()) {
- flcs_mode = FLCS_HELM;
- }
-
- // initialize the AI:
- dir = 0;
- SetControls(0);
-
- for (int i = 0; i < 4; i++) {
- missile_id[i] = 0;
- missile_eta[i] = 0;
- trigger[i] = false;
- }
-}
-
-// +--------------------------------------------------------------------+
-
-Ship::~Ship()
-{
- // the loadout can not be cleared during Destroy, because it
- // is needed after Destroy to create the re-spawned ship
-
- delete [] loadout;
- loadout = 0;
-
- Destroy();
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::Destroy()
-{
- // destroy fighters on deck:
- ListIter<FlightDeck> deck = flight_decks;
- while (++deck) {
- for (int i = 0; i < deck->NumSlots(); i++) {
- Ship* s = deck->GetShip(i);
-
- if (s && !s->IsDying() && !s->IsDead()) {
- if (sim && sim->IsActive()) {
- s->DeathSpiral();
- }
- else {
- s->transition_type = TRANSITION_DEAD;
- s->Destroy();
- }
- }
- }
- }
-
- if (element) {
- // mission ending for this ship, evaluate objectives one last time:
- for (int i = 0; i < element->NumObjectives(); i++) {
- Instruction* obj = element->GetObjective(i);
-
- if (obj->Status() <= Instruction::ACTIVE) {
- obj->Evaluate(this);
- }
- }
-
- combat_unit = element->GetCombatUnit();
- SetElement(0);
- }
-
- delete [] track;
- track = 0;
-
- delete shield;
- shield = 0;
- delete sensor;
- sensor = 0;
- delete navsys;
- navsys = 0;
- delete thruster;
- thruster = 0;
- delete farcaster;
- farcaster = 0;
- delete quantum_drive;
- quantum_drive = 0;
- delete decoy;
- decoy = 0;
- delete probe;
- probe = 0;
- delete gear;
- gear = 0;
-
- main_drive = 0;
- flcs = 0;
-
- // repair queue does not own the systems under repair:
- repair_queue.clear();
-
- navlights.destroy();
- flight_decks.destroy();
- computers.destroy();
- weapons.destroy();
- drives.destroy();
- reactors.destroy();
-
- // this is now a list of dangling pointers:
- systems.clear();
-
- delete hangar;
- hangar = 0;
-
- // this also destroys the rep:
- detail.Destroy();
- rep = 0;
-
- GRAPHIC_DESTROY(cockpit);
- GRAPHIC_DESTROY(shieldRep);
- LIGHT_DESTROY(light);
-
- delete launch_point;
- launch_point = 0;
-
- delete radio_orders;
- radio_orders = 0;
-
- delete dir;
- dir = 0;
-
- delete killer;
- killer = 0;
-
- // inbound slot is deleted by flight deck:
- inbound = 0;
-
- life = 0;
- Notify();
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::Initialize()
-{
- ShipDesign::Initialize();
- Thruster::Initialize();
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::Close()
-{
- ShipDesign::Close();
- Thruster::Close();
-}
-
-void
-Ship::SetupAgility()
-{
- const float ROLL_SPEED = (float)(PI * 0.1500);
- const float PITCH_SPEED = (float)(PI * 0.0250);
- const float YAW_SPEED = (float)(PI * 0.0250);
-
- drag = design->drag;
- dr_drg = design->roll_drag;
- dp_drg = design->pitch_drag;
- dy_drg = design->yaw_drag;
-
- if (IsDying()) {
- drag = 0.0f;
- dr_drg *= 0.25f;
- dp_drg *= 0.25f;
- dy_drg *= 0.25f;
- }
-
- if (flight_model > 0) {
- drag = design->arcade_drag;
- thrust *= 10.0f;
- }
-
- float yaw_air_factor = 1.0f;
-
- if (IsAirborne()) {
- bool grounded = AltitudeAGL() < Radius()/2;
-
- if (flight_model > 0) {
- drag *= 2.0f;
-
- if (gear && gear->GetState() != LandingGear::GEAR_UP)
- drag *= 2.0f;
-
- if (grounded)
- drag *= 3.0f;
- }
-
- else {
- if (Class() != LCA)
- yaw_air_factor = 0.3f;
-
- double rho = GetDensity();
- double speed = Velocity().length();
-
- agility = design->air_factor * rho * speed - wep_resist;
-
- if (grounded && agility < 0)
- agility = 0;
-
- else if (!grounded && agility < 0.5 * design->agility)
- agility = 0.5 * design->agility;
-
- else if (agility > 2 * design->agility)
- agility = 2 * design->agility;
-
- // undercarriage aerodynamic drag
- if (gear && gear->GetState() != LandingGear::GEAR_UP)
- drag *= 5.0f;
-
- // wheel rolling friction
- if (grounded)
- drag *= 10.0f;
-
- // dead engine drag ;-)
- if (thrust < 10)
- drag *= 5.0f;
- }
- }
-
- else {
- agility = design->agility - wep_resist;
-
- if (agility < 0.5 * design->agility)
- agility = 0.5 * design->agility;
-
- if (flight_model == 0)
- drag = 0.0f;
- }
-
- float rr = (float) (design->roll_rate * PI / 180);
- float pr = (float) (design->pitch_rate * PI / 180);
- float yr = (float) (design->yaw_rate * PI / 180);
-
- if (rr == 0) rr = (float) agility * ROLL_SPEED;
- if (pr == 0) pr = (float) agility * PITCH_SPEED;
- if (yr == 0) yr = (float) agility * YAW_SPEED * yaw_air_factor;
-
- SetAngularRates(rr, pr, yr);
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::SetRegion(SimRegion* rgn)
-{
- SimObject::SetRegion(rgn);
-
- const double GRAV = 6.673e-11;
-
- if (IsGroundUnit()) {
- // glue buildings to the terrain:
- Point loc = Location();
- Terrain* terrain = region->GetTerrain();
-
- if (terrain) {
- loc.y = terrain->Height(loc.x, loc.z);
- MoveTo(loc);
- }
- }
-
- else if (IsAirborne()) {
- Orbital* primary = GetRegion()->GetOrbitalRegion()->Primary();
-
- double m0 = primary->Mass();
- double r = primary->Radius();
-
- SetGravity((float) (GRAV * m0 / (r*r)));
- SetBaseDensity(1.0f);
- }
-
- else {
- SetGravity(0.0f);
- SetBaseDensity(0.0f);
-
- if (IsStarship())
- flcs_mode = FLCS_HELM;
- else
- flcs_mode = FLCS_AUTO;
- }
-}
-
-// +--------------------------------------------------------------------+
-
-int
-Ship::GetTextureList(List<Bitmap>& textures)
-{
- textures.clear();
-
- for (int d = 0; d < detail.NumLevels(); d++) {
- for (int i = 0; i < detail.NumModels(d); i++) {
- Graphic* g = detail.GetRep(d, i);
-
- if (g->IsSolid()) {
- Solid* solid = (Solid*) g;
- Model* model = solid->GetModel();
-
- if (model) {
- for (int n = 0; n < model->NumMaterials(); n++) {
- //textures.append(model->textures[n]);
- }
- }
- }
- }
- }
-
- return textures.size();
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::Activate(Scene& scene)
-{
- int i = 0;
- SimObject::Activate(scene);
-
- for (i = 0; i < detail.NumModels(detail_level); i++) {
- Graphic* g = detail.GetRep(detail_level, i);
- scene.AddGraphic(g);
- }
-
- for (i = 0; i < flight_decks.size(); i++)
- scene.AddLight(flight_decks[i]->GetLight());
-
- if (shieldRep)
- scene.AddGraphic(shieldRep);
-
- if (cockpit) {
- scene.AddForeground(cockpit);
- cockpit->Hide();
- }
-
- Drive* drive = GetDrive();
- if (drive) {
- for (i = 0; i < drive->NumEngines(); i++) {
- Graphic* flare = drive->GetFlare(i);
- if (flare) {
- scene.AddGraphic(flare);
- }
-
- Graphic* trail = drive->GetTrail(i);
- if (trail) {
- scene.AddGraphic(trail);
- }
- }
- }
-
- Thruster* thruster = GetThruster();
- if (thruster) {
- for (i = 0; i < thruster->NumThrusters(); i++) {
- Graphic* flare = thruster->Flare(i);
- if (flare) {
- scene.AddGraphic(flare);
- }
-
- Graphic* trail = thruster->Trail(i);
- if (trail) {
- scene.AddGraphic(trail);
- }
- }
- }
-
- for (int n = 0; n < navlights.size(); n++) {
- NavLight* navlight = navlights[n];
- for (i = 0; i < navlight->NumBeacons(); i++) {
- Graphic* beacon = navlight->Beacon(i);
- if (beacon)
- scene.AddGraphic(beacon);
- }
- }
-
- ListIter<WeaponGroup> g = weapons;
- while (++g) {
- ListIter<Weapon> w = g->GetWeapons();
- while (++w) {
- Solid* turret = w->GetTurret();
- if (turret) {
- scene.AddGraphic(turret);
-
- Solid* turret_base = w->GetTurretBase();
- if (turret_base)
- scene.AddGraphic(turret_base);
- }
- if (w->IsMissile()) {
- for (i = 0; i < w->Ammo(); i++) {
- Solid* store = w->GetVisibleStore(i);
- if (store)
- scene.AddGraphic(store);
- }
- }
- }
- }
-
- if (gear && gear->GetState() != LandingGear::GEAR_UP) {
- for (int i = 0; i < gear->NumGear(); i++) {
- scene.AddGraphic(gear->GetGear(i));
- }
- }
-}
-
-void
-Ship::Deactivate(Scene& scene)
-{
- int i = 0;
- SimObject::Deactivate(scene);
-
- for (i = 0; i < detail.NumModels(detail_level); i++) {
- Graphic* g = detail.GetRep(detail_level, i);
- scene.DelGraphic(g);
- }
-
- for (i = 0; i < flight_decks.size(); i++)
- scene.DelLight(flight_decks[i]->GetLight());
-
- if (shieldRep)
- scene.DelGraphic(shieldRep);
-
- if (cockpit)
- scene.DelForeground(cockpit);
-
- Drive* drive = GetDrive();
- if (drive) {
- for (i = 0; i < drive->NumEngines(); i++) {
- Graphic* flare = drive->GetFlare(i);
- if (flare) {
- scene.DelGraphic(flare);
- }
-
- Graphic* trail = drive->GetTrail(i);
- if (trail) {
- scene.DelGraphic(trail);
- }
- }
- }
-
- Thruster* thruster = GetThruster();
- if (thruster) {
- for (i = 0; i < thruster->NumThrusters(); i++) {
- Graphic* flare = thruster->Flare(i);
- if (flare) {
- scene.DelGraphic(flare);
- }
-
- Graphic* trail = thruster->Trail(i);
- if (trail) {
- scene.DelGraphic(trail);
- }
- }
- }
-
- for (int n = 0; n < navlights.size(); n++) {
- NavLight* navlight = navlights[n];
- for (i = 0; i < navlight->NumBeacons(); i++) {
- Graphic* beacon = navlight->Beacon(i);
- if (beacon)
- scene.DelGraphic(beacon);
- }
- }
-
- ListIter<WeaponGroup> g = weapons;
- while (++g) {
- ListIter<Weapon> w = g->GetWeapons();
- while (++w) {
- Solid* turret = w->GetTurret();
- if (turret) {
- scene.DelGraphic(turret);
-
- Solid* turret_base = w->GetTurretBase();
- if (turret_base)
- scene.DelGraphic(turret_base);
- }
- if (w->IsMissile()) {
- for (i = 0; i < w->Ammo(); i++) {
- Solid* store = w->GetVisibleStore(i);
- if (store)
- scene.DelGraphic(store);
- }
- }
- }
- }
-
- if (gear) {
- for (int i = 0; i < gear->NumGear(); i++) {
- scene.DelGraphic(gear->GetGear(i));
- }
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::MatchOrientation(const Ship& s)
-{
- Point pos = cam.Pos();
- cam.Clone(s.cam);
- cam.MoveTo(pos);
-
- if (rep)
- rep->SetOrientation(cam.Orientation());
-
- if (cockpit)
- cockpit->SetOrientation(cam.Orientation());
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::ClearTrack()
-{
- const int DEFAULT_TRACK_LENGTH = 20; // 10 seconds
-
- if (!track) {
- track = new(__FILE__,__LINE__) Point[DEFAULT_TRACK_LENGTH];
- }
-
- track[0] = Location();
- ntrack = 1;
- track_time = Game::GameTime();
-}
-
-void
-Ship::UpdateTrack()
-{
- const int DEFAULT_TRACK_UPDATE = 500; // milliseconds
- const int DEFAULT_TRACK_LENGTH = 20; // 10 seconds
-
- DWORD time = Game::GameTime();
-
- if (!track) {
- track = new(__FILE__,__LINE__) Point[DEFAULT_TRACK_LENGTH];
- track[0] = Location();
- ntrack = 1;
- track_time = time;
- }
-
- else if (time - track_time > DEFAULT_TRACK_UPDATE) {
- if (Location() != track[0]) {
- for (int i = DEFAULT_TRACK_LENGTH-2; i >= 0; i--)
- track[i+1] = track[i];
-
- track[0] = Location();
- if (ntrack < DEFAULT_TRACK_LENGTH) ntrack++;
- }
-
- track_time = time;
- }
-}
-
-Point
-Ship::TrackPoint(int i) const
-{
- if (track && i < ntrack)
- return track[i];
-
- return Point();
-}
-
-// +--------------------------------------------------------------------+
-
-const char*
-Ship::Abbreviation() const
-{
- return design->abrv;
-}
-
-const char*
-Ship::DesignName() const
-{
- return design->DisplayName();
-}
-
-const char*
-Ship::DesignFileName() const
-{
- return design->filename;
-}
-
-const char*
-Ship::ClassName() const
-{
- return ShipDesign::ClassName(design->type);
-}
-
-const char*
-Ship::ClassName(int c)
-{
- return ShipDesign::ClassName(c);
-}
-
-int
-Ship::ClassForName(const char* name)
-{
- return ShipDesign::ClassForName(name);
-}
-
-Ship::CLASSIFICATION
-Ship::Class() const
-{
- return (CLASSIFICATION) design->type;
-}
-
-bool
-Ship::IsGroundUnit() const
-{
- return (design->type & GROUND_UNITS) ? true : false;
-}
-
-bool
-Ship::IsStarship() const
-{
- return (design->type & STARSHIPS) ? true : false;
-}
-
-bool
-Ship::IsDropship() const
-{
- return (design->type & DROPSHIPS) ? true : false;
-}
-
-bool
-Ship::IsStatic() const
-{
- return design->type >= STATION;
-}
-
-bool
-Ship::IsRogue() const
-{
- return ff_count >= 50;
-}
-
-// +--------------------------------------------------------------------+
-
-bool
-Ship::IsHostileTo(const SimObject* o) const
-{
- if (o) {
- if (IsRogue())
- return true;
-
- if (o->Type() == SIM_SHIP) {
- Ship* s = (Ship*) o;
-
- if (s->IsRogue())
- return true;
-
- if (GetIFF() == 0) {
- if (s->GetIFF() > 1)
- return true;
- }
- else {
- if (s->GetIFF() > 0 && s->GetIFF() != GetIFF())
- return true;
- }
- }
-
- else if (o->Type() == SIM_SHOT || o->Type() == SIM_DRONE) {
- Shot* s = (Shot*) o;
-
- if (GetIFF() == 0) {
- if (s->GetIFF() > 1)
- return true;
- }
- else {
- if (s->GetIFF() > 0 && s->GetIFF() != GetIFF())
- return true;
- }
- }
- }
-
- return false;
-}
-
-// +--------------------------------------------------------------------+
-
-double
-Ship::RepairSpeed() const
-{
- return design->repair_speed;
-}
-
-int
-Ship::RepairTeams() const
-{
- return design->repair_teams;
-}
-
-// +--------------------------------------------------------------------+
-
-int
-Ship::NumContacts() const
-{
- // cast-away const:
- return ((Ship*)this)->ContactList().size();
-}
-
-List<Contact>&
-Ship::ContactList()
-{
- if (region)
- return region->TrackList(GetIFF());
-
- static List<Contact> empty_contact_list;
- return empty_contact_list;
-}
-
-Contact*
-Ship::FindContact(SimObject* s) const
-{
- if (!s) return 0;
-
- ListIter<Contact> c_iter = ((Ship*) this)->ContactList();
- while (++c_iter) {
- Contact* c = c_iter.value();
-
- if (c->GetShip() == s)
- return c;
-
- if (c->GetShot() == s)
- return c;
- }
-
- return 0;
-}
-
-// +--------------------------------------------------------------------+
-
-Ship*
-Ship::GetController() const
-{
- Ship* controller = 0;
-
- if (carrier) {
- // are we in same region as carrier?
- if (carrier->GetRegion() == GetRegion()) {
- return carrier;
- }
-
- // if not, figure out who our control unit is:
- else {
- double distance = 10e6;
-
- ListIter<Ship> iter = GetRegion()->Carriers();
- while (++iter) {
- Ship* test = iter.value();
- if (test->GetIFF() == GetIFF()) {
- double d = Point(Location() - test->Location()).length();
- if (d < distance) {
- controller = test;
- distance = d;
- }
- }
- }
- }
- }
-
- if (!controller) {
- if (element && element->GetCommander())
- controller = element->GetCommander()->GetShip(1);
- }
-
- return controller;
-}
-
-int
-Ship::NumInbound() const
-{
- int result = 0;
-
- for (int i = 0; i < flight_decks.size(); i++) {
- result += flight_decks[i]->GetRecoveryQueue().size();
- }
-
- return result;
-}
-
-int
-Ship::NumFlightDecks() const
-{
- return flight_decks.size();
-}
-
-FlightDeck*
-Ship::GetFlightDeck(int i) const
-{
- if (i >= 0 && i < flight_decks.size())
- return flight_decks[i];
-
- return 0;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::SetFlightPhase(OP_MODE phase)
-{
- if (phase == ACTIVE && !launch_time) {
- launch_time = Game::GameTime() + 1;
- dock = 0;
-
- if (element)
- element->SetLaunchTime(launch_time);
- }
-
- flight_phase = phase;
-
- if (flight_phase == ACTIVE)
- dock = 0;
-}
-
-void
-Ship::SetCarrier(Ship* c, FlightDeck* d)
-{
- carrier = c;
- dock = d;
-
- if (carrier)
- Observe(carrier);
-}
-
-void
-Ship::SetInbound(InboundSlot* s)
-{
- inbound = s;
-
- if (inbound && flight_phase == ACTIVE) {
- flight_phase = APPROACH;
-
- SetCarrier((Ship*) inbound->GetDeck()->GetCarrier(), inbound->GetDeck());
-
- HUDView* hud = HUDView::GetInstance();
-
- if (hud && hud->GetShip() == this)
- hud->SetHUDMode(HUDView::HUD_MODE_ILS);
- }
-}
-
-void
-Ship::Stow()
-{
- if (carrier && carrier->GetHangar())
- carrier->GetHangar()->Stow(this);
-}
-
-bool
-Ship::IsGearDown()
-{
- if (gear && gear->GetState() == LandingGear::GEAR_DOWN)
- return true;
-
- return false;
-}
-
-void
-Ship::LowerGear()
-{
- if (gear && gear->GetState() != LandingGear::GEAR_DOWN) {
- gear->SetState(LandingGear::GEAR_LOWER);
- Scene* scene = 0;
-
- if (rep)
- scene = rep->GetScene();
-
- if (scene) {
- for (int i = 0; i < gear->NumGear(); i++) {
- Solid* g = gear->GetGear(i);
- if (g) {
- if (detail_level == 0)
- scene->DelGraphic(g);
- else
- scene->AddGraphic(g);
- }
- }
- }
- }
-}
-
-void
-Ship::RaiseGear()
-{
- if (gear && gear->GetState() != LandingGear::GEAR_UP)
- gear->SetState(LandingGear::GEAR_RAISE);
-}
-
-void
-Ship::ToggleGear()
-{
- if (gear) {
- if (gear->GetState() == LandingGear::GEAR_UP ||
- gear->GetState() == LandingGear::GEAR_RAISE) {
- LowerGear();
- }
- else {
- RaiseGear();
- }
- }
-}
-
-void
-Ship::ToggleNavlights()
-{
- bool enable = false;
-
- for (int i = 0; i < navlights.size(); i++) {
- if (i == 0)
- enable = !navlights[0]->IsEnabled();
-
- if (enable)
- navlights[i]->Enable();
- else
- navlights[i]->Disable();
- }
-}
-
-// +--------------------------------------------------------------------+
-
-int
-Ship::CollidesWith(Physical& o)
-{
- // bounding spheres test:
- Point delta_loc = Location() - o.Location();
- if (delta_loc.length() > radius + o.Radius())
- return 0;
-
- if (!o.Rep())
- return 1;
-
- for (int i = 0; i < detail.NumModels(detail_level); i++) {
- Graphic* g = detail.GetRep(detail_level, i);
-
- if (o.Type() == SimObject::SIM_SHIP) {
- Ship* o_ship = (Ship*) &o;
- int o_det = o_ship->detail_level;
-
- for (int j = 0; j < o_ship->detail.NumModels(o_det); j++) {
- Graphic* o_g = o_ship->detail.GetRep(o_det, j);
-
- if (g->CollidesWith(*o_g))
- return 1;
- }
- }
- else {
- // representation collision test (will do bounding spheres first):
- if (g->CollidesWith(*o.Rep()))
- return 1;
- }
- }
-
- return 0;
-}
-
-// +--------------------------------------------------------------------+
-
-static DWORD ff_warn_time = 0;
-
-int
-Ship::HitBy(Shot* shot, Point& impact)
-{
- if (shot->Owner() == this || IsNetObserver())
- return HIT_NOTHING;
-
- if (shot->IsFlak())
- return HIT_NOTHING;
-
- if (InTransition())
- return HIT_NOTHING;
-
- Point shot_loc = shot->Location();
- Point delta = shot_loc - Location();
- double dlen = delta.length();
-
- Point hull_impact;
- int hit_type = HIT_NOTHING;
- double dscale = 1;
- float scale = design->explosion_scale;
- Weapon* wep = 0;
-
- if (!shot->IsMissile() && !shot->IsBeam()) {
- if (dlen > Radius() * 2)
- return HIT_NOTHING;
- }
-
- if (scale <= 0)
- scale = design->scale;
-
- if (shot->Owner()) {
- const ShipDesign* owner_design = shot->Owner()->Design();
- if (owner_design && owner_design->scale < scale)
- scale = (float) owner_design->scale;
- }
-
-
- // MISSILE PROCESSING ------------------------------------------------
-
- if (shot->IsMissile() && rep) {
- if (dlen < rep->Radius()) {
- hull_impact = impact = shot_loc;
-
- hit_type = CheckShotIntersection(shot, impact, hull_impact, &wep);
-
- if (hit_type) {
- if (shot->Damage() > 0) {
- DWORD flash = Explosion::HULL_FLASH;
-
- if ((hit_type & HIT_SHIELD) != 0)
- flash = Explosion::SHIELD_FLASH;
-
- sim->CreateExplosion(impact, Velocity(), flash, 0.3f * scale, scale, region);
- sim->CreateExplosion(impact, Point(), Explosion::SHOT_BLAST, 2.0f, scale, region);
- }
- }
- }
-
- if (hit_type == HIT_NOTHING && shot->IsArmed()) {
- SeekerAI* seeker = (SeekerAI*) shot->GetDirector();
-
- // if the missile overshot us, take damage proportional to distance
- double damage_radius = shot->Design()->lethal_radius;
- if (dlen < (damage_radius + Radius())) {
- if (seeker && seeker->Overshot()) {
- dscale = 1.0 - (dlen / (damage_radius + Radius()));
-
- if (dscale > 1)
- dscale = 1;
-
- if (ShieldStrength() > 5) {
- hull_impact = impact = shot_loc;
-
- if (shot->Damage() > 0) {
- if (shieldRep)
- shieldRep->Hit(impact, shot, shot->Damage()*dscale);
- sim->CreateExplosion(impact, Velocity(), Explosion::SHIELD_FLASH, 0.20f * scale, scale, region);
- sim->CreateExplosion(impact, Point(), Explosion::SHOT_BLAST, 20.0f * scale, scale, region);
- }
-
- hit_type = HIT_BOTH;
- }
- else {
- hull_impact = impact = shot_loc;
-
- if (shot->Damage() > 0) {
- sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FLASH, 0.30f * scale, scale, region);
- sim->CreateExplosion(impact, Point(), Explosion::SHOT_BLAST, 20.0f * scale, scale, region);
- }
-
- hit_type = HIT_HULL;
- }
- }
- }
- }
- }
-
- // ENERGY WEP PROCESSING ---------------------------------------------
-
- else {
- hit_type = CheckShotIntersection(shot, impact, hull_impact, &wep);
-
- // impact:
- if (hit_type) {
-
- if (hit_type & HIT_SHIELD) {
- if (shieldRep)
- shieldRep->Hit(impact, shot, shot->Damage());
- sim->CreateExplosion(impact, Velocity(), Explosion::SHIELD_FLASH, 0.20f * scale, scale, region);
- }
-
- else {
- if (shot->IsBeam())
- sim->CreateExplosion(impact, Velocity(), Explosion::BEAM_FLASH, 0.30f * scale, scale, region);
- else
- sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FLASH, 0.30f * scale, scale, region);
-
- if (IsStarship()) {
- Point burst_vel = hull_impact - Location();
- burst_vel.Normalize();
- burst_vel *= Radius() * 0.5;
- burst_vel += Velocity();
-
- sim->CreateExplosion(hull_impact, burst_vel, Explosion::HULL_BURST, 0.50f * scale, scale, region, this);
- }
- }
- }
- }
-
- // DAMAGE RESOLUTION -------------------------------------------------
-
- if (hit_type != HIT_NOTHING && shot->IsArmed()) {
- double effective_damage = shot->Damage() * dscale;
-
- // FRIENDLY FIRE --------------------------------------------------
-
- if (shot->Owner()) {
- Ship* s = (Ship*) shot->Owner();
-
- if (!IsRogue() && s->GetIFF() == GetIFF() &&
- s->GetDirector() && s->GetDirector()->Type() < 1000) {
- bool was_rogue = s->IsRogue();
-
- // only count beam hits once
- if (shot->Damage() && !shot->HitTarget() && GetFriendlyFireLevel() > 0) {
- int penalty = 1;
-
- if (shot->IsBeam()) penalty = 5;
- else if (shot->IsDrone()) penalty = 7;
-
- if (s->GetTarget() == this) penalty *= 3;
-
- s->IncFriendlyFire(penalty);
- }
-
- effective_damage *= GetFriendlyFireLevel();
-
- if (Class() > DRONE && s->Class() > DRONE) {
- if (s->IsRogue() && !was_rogue) {
- RadioMessage* warn = new(__FILE__,__LINE__) RadioMessage(s, this, RadioMessage::DECLARE_ROGUE);
- RadioTraffic::Transmit(warn);
- }
- else if (!s->IsRogue() && (Game::GameTime() - ff_warn_time) > 5000) {
- ff_warn_time = Game::GameTime();
-
- RadioMessage* warn = 0;
- if (s->GetTarget() == this)
- warn = new(__FILE__,__LINE__) RadioMessage(s, this, RadioMessage::WARN_TARGETED);
- else
- warn = new(__FILE__,__LINE__) RadioMessage(s, this, RadioMessage::WARN_ACCIDENT);
-
- RadioTraffic::Transmit(warn);
- }
- }
- }
- }
-
- if (effective_damage > 0) {
- if (!shot->IsBeam() && shot->Design()->damage_type == WeaponDesign::DMG_NORMAL)
- ApplyTorque(shot->Velocity() * (float) effective_damage * 1e-6f);
-
- if (!NetGame::IsNetGameClient()) {
- InflictDamage(effective_damage, shot, hit_type, hull_impact);
- }
- }
- }
-
- return hit_type;
-}
-
-static bool CheckRaySphereIntersection(Point loc, double radius, Point Q, Point w, double len)
-{
- Point d0 = loc - Q;
- Point d1 = d0.cross(w);
- double dlen = d1.length(); // distance of point from line
-
- if (dlen > radius) // clean miss
- return false; // (no impact)
-
- // possible collision course...
- // find the point on the ray that is closest
- // to the sphere's location:
- Point closest = Q + w * (d0 * w);
-
- // find the leading edge, and it's distance from the location:
- Point leading_edge = Q + w*len;
- Point leading_delta = leading_edge - loc;
- double leading_dist = leading_delta.length();
-
- // if the leading edge is not within the sphere,
- if (leading_dist > radius) {
- // check to see if the closest point is between the
- // ray's endpoints:
- Point delta1 = closest - Q;
- Point delta2 = leading_edge - Q; // this is w*len
-
- // if the closest point is not between the leading edge
- // and the origin, this ray does not intersect:
- if (delta1 * delta2 < 0 || delta1.length() > len) {
- return false;
- }
- }
-
- return true;
-}
-
-int
-Ship::CheckShotIntersection(Shot* shot, Point& ipt, Point& hpt, Weapon** wep)
-{
- int hit_type = HIT_NOTHING;
- Point shot_loc = shot->Location();
- Point shot_org = shot->Origin();
- Point shot_vpn = shot_loc - shot_org;
- double shot_len = shot_vpn.Normalize();
- double blow_len = shot_len;
- bool hit_hull = false;
- bool easy = false;
-
- if (shot_len <= 0)
- return hit_type;
-
- if (shot_len < 1000)
- shot_len = 1000;
-
- Point hull_impact;
- Point shield_impact;
- Point turret_impact;
- Point closest;
- double d0 = 1e9;
- double d1 = 1e9;
- double ds = 1e9;
-
- if (dir && dir->Type() == SteerAI::FIGHTER) {
- ShipAI* shipAI = (ShipAI*) dir;
- easy = shipAI->GetAILevel() < 2;
- }
-
- if (shieldRep && ShieldStrength() > 5) {
- if (shieldRep->CheckRayIntersection(shot_org, shot_vpn, shot_len, shield_impact)) {
- hit_type = HIT_SHIELD;
- closest = shield_impact;
- d0 = Point(closest - shot_org).length();
- ds = d0;
-
- ipt = shield_impact;
- }
- }
-
- if (shieldRep && hit_type == HIT_SHIELD && !shot->IsBeam())
- blow_len = shieldRep->Radius() * 2;
-
- for (int i = 0; i < detail.NumModels(detail_level) && !hit_hull; i++) {
- Solid* s = (Solid*) detail.GetRep(detail_level, i);
- if (s) {
- if (easy) {
- hit_hull = CheckRaySphereIntersection(s->Location(), s->Radius(), shot_org, shot_vpn, shot_len);
- }
- else {
- hit_hull = s->CheckRayIntersection(shot_org, shot_vpn, blow_len, hull_impact)?true:false;
- }
- }
- }
-
- if (hit_hull) {
- if (ShieldStrength() > 5 && !shieldRep)
- hit_type = HIT_SHIELD;
-
- hit_type = hit_type | HIT_HULL;
- hpt = hull_impact;
-
- d1 = Point(hull_impact - shot_org).length();
-
- if (d1 < d0) {
- closest = hull_impact;
- d0 = d1;
- }
- }
-
- if (IsStarship() || IsStatic()) {
- ListIter<WeaponGroup> g_iter = Weapons();
- while (++g_iter) {
- WeaponGroup* g = g_iter.value();
-
- if (g->GetDesign() && g->GetDesign()->turret_model) {
- double tsize = g->GetDesign()->turret_model->Radius();
-
- ListIter<Weapon> w_iter = g->GetWeapons();
- while (++w_iter) {
- Weapon* w = w_iter.value();
-
- Point tloc = w->GetTurret()->Location();
-
- if (CheckRaySphereIntersection(tloc, tsize, shot_org, shot_vpn, shot_len)) {
- Point delta = tloc - shot_org;
- d1 = delta.length();
-
- if (d1 < d0) {
- if (wep) *wep = w;
- hit_type = hit_type | HIT_TURRET;
- turret_impact = tloc;
-
- d0 = d1;
-
- closest = turret_impact;
- hull_impact = turret_impact;
- hpt = turret_impact;
-
- if (d1 < ds)
- ipt = turret_impact;
- }
- }
- }
- }
- }
- }
-
- // trim beam shots to closest impact point:
- if (hit_type && shot->IsBeam()) {
- shot->SetBeamPoints(shot_org, closest);
- }
-
- return hit_type;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::InflictNetDamage(double damage, Shot* shot)
-{
- if (damage > 0 && !IsNetObserver()) {
- Physical::InflictDamage(damage, 0);
-
- // shake by percentage of maximum damage
- double newshake = 50 * damage/design->integrity;
- const double MAX_SHAKE = 7;
-
- if (shake < MAX_SHAKE) shake += (float) newshake;
- if (shake > MAX_SHAKE) shake = (float) MAX_SHAKE;
- }
-}
-
-void
-Ship::InflictNetSystemDamage(System* system, double damage, BYTE dmg_type)
-{
- if (system && damage > 0 && !IsNetObserver()) {
- bool dmg_normal = dmg_type == WeaponDesign::DMG_NORMAL;
- bool dmg_power = dmg_type == WeaponDesign::DMG_POWER;
- bool dmg_emp = dmg_type == WeaponDesign::DMG_EMP;
-
- double sys_damage = damage;
- double avail = system->Availability();
-
- if (dmg_normal || system->IsPowerCritical() && dmg_emp) {
- system->ApplyDamage(sys_damage);
- master_caution = true;
-
- if (system->GetExplosionType() && (avail - system->Availability()) >= 50) {
- float scale = design->explosion_scale;
- if (scale <= 0)
- scale = design->scale;
-
- sim->CreateExplosion(system->MountLocation(), Velocity() * 0.7f, system->GetExplosionType(), 0.2f * scale, scale, region, this, system);
- }
- }
- }
-}
-
-void
-Ship::SetNetSystemStatus(System* system, int status, int power, int reactor, double avail)
-{
- if (system && !IsNetObserver()) {
- if (system->GetPowerLevel() != power)
- system->SetPowerLevel(power);
-
- if (system->GetSourceIndex() != reactor) {
- System* s = GetSystem(reactor);
-
- if (s && s->Type() == System::POWER_SOURCE) {
- PowerSource* reac = (PowerSource*) s;
- reac->AddClient(system);
- }
- }
-
- if (system->Status() != status) {
- if (status == System::MAINT) {
- ListIter<Component> comp = system->GetComponents();
- while (++comp) {
- Component* c = comp.value();
-
- if (c->Status() < Component::NOMINAL && c->Availability() < 75) {
- if (c->SpareCount() &&
- c->ReplaceTime() <= 300 &&
- (c->Availability() < 50 ||
- c->ReplaceTime() < c->RepairTime())) {
-
- c->Replace();
- }
-
- else if (c->Availability() >= 50 || c->NumJerried() < 5) {
- c->Repair();
- }
- }
- }
-
- RepairSystem(system);
- }
- }
-
- if (system->Availability() < avail) {
- system->SetNetAvail(avail);
- }
- else {
- system->SetNetAvail(-1);
- }
- }
-}
-
-// +----------------------------------------------------------------------+
-
-bool IsWeaponBlockedFriendly(Weapon* w, const SimObject* test)
-{
- if (w->GetTarget()) {
- Point tgt = w->GetTarget()->Location();
- Point obj = test->Location();
- Point wep = w->MountLocation();
-
- Point dir = tgt - wep;
- double d = dir.Normalize();
- Point rho = obj - wep;
- double r = rho.Normalize();
-
- // if target is much closer than obstacle,
- // don't worry about friendly fire...
- if (d < 1.5 * r)
- return false;
-
- Point dst = dir * r + wep;
- double err = (obj - dst).length();
-
- if (err < test->Radius() * 1.5)
- return true;
- }
-
- return false;
-}
-
-void
-Ship::CheckFriendlyFire()
-{
- // if no weapons, there is no worry about friendly fire...
- if (weapons.size() < 1)
- return;
-
- // only check once each second
- if (Game::GameTime() - friendly_fire_time < 1000)
- return;
-
- List<Weapon> w_list;
- int i, j;
-
- // clear the FF blocked flag on all weapons
- for (i = 0; i < weapons.size(); i++) {
- WeaponGroup* g = weapons[i];
-
- for (j = 0; j < g->NumWeapons(); j++) {
- Weapon* w = g->GetWeapon(j);
- w_list.append(w);
- w->SetBlockedFriendly(false);
- }
- }
-
- // for each friendly ship within some kind of weapons range,
- ListIter<Contact> c_iter = ContactList();
- while (++c_iter) {
- Contact* c = c_iter.value();
- Ship* cship = c->GetShip();
- Shot* cshot = c->GetShot();
-
- if (cship && cship != this && (cship->GetIFF() == 0 || cship->GetIFF() == GetIFF())) {
- double range = (cship->Location() - Location()).length();
-
- if (range > 100e3)
- continue;
-
- // check each unblocked weapon to see if it is blocked by that ship
- ListIter<Weapon> iter = w_list;
- while (++iter) {
- Weapon* w = iter.value();
-
- if (!w->IsBlockedFriendly())
- w->SetBlockedFriendly(IsWeaponBlockedFriendly(w, cship));
- }
- }
-
- else if (cshot && cshot->GetIFF() == GetIFF()) {
- double range = (cshot->Location() - Location()).length();
-
- if (range > 30e3)
- continue;
-
- // check each unblocked weapon to see if it is blocked by that shot
- ListIter<Weapon> iter = w_list;
- while (++iter) {
- Weapon* w = iter.value();
-
- if (!w->IsBlockedFriendly())
- w->SetBlockedFriendly(IsWeaponBlockedFriendly(w, cshot));
- }
- }
- }
-
- friendly_fire_time = Game::GameTime() + (DWORD) Random(0, 500);
-}
-
-// +----------------------------------------------------------------------+
-
-Ship*
-Ship::GetLeader() const
-{
- if (element)
- return element->GetShip(1);
-
- return (Ship*) this;
-}
-
-int
-Ship::GetElementIndex() const
-{
- if (element)
- return element->FindIndex(this);
-
- return 0;
-}
-
-int
-Ship::GetOrigElementIndex() const
-{
- return orig_elem_index;
-}
-
-void
-Ship::SetElement(Element* e)
-{
- element = e;
-
- if (element) {
- combat_unit = element->GetCombatUnit();
-
- if (combat_unit) {
- integrity = (float) (design->integrity - combat_unit->GetSustainedDamage());
- }
-
- orig_elem_index = element->FindIndex(this);
- }
-}
-
-void
-Ship::SetLaunchPoint(Instruction* pt)
-{
- if (pt && !launch_point)
- launch_point = pt;
-}
-
-void
-Ship::AddNavPoint(Instruction* pt, Instruction* after)
-{
- if (GetElementIndex() == 1)
- element->AddNavPoint(pt, after);
-}
-
-void
-Ship::DelNavPoint(Instruction* pt)
-{
- if (GetElementIndex() == 1)
- element->DelNavPoint(pt);
-}
-
-void
-Ship::ClearFlightPlan()
-{
- if (GetElementIndex() == 1)
- element->ClearFlightPlan();
-}
-
-// +----------------------------------------------------------------------+
-
-bool
-Ship::IsAutoNavEngaged()
-{
- if (navsys && navsys->AutoNavEngaged())
- return true;
-
- return false;
-}
-
-void
-Ship::SetAutoNav(bool engage)
-{
- if (navsys) {
- if (navsys->AutoNavEngaged()) {
- if (!engage)
- navsys->DisengageAutoNav();
- }
- else {
- if (engage)
- navsys->EngageAutoNav();
- }
-
- if (sim)
- SetControls(sim->GetControls());
- }
-}
-
-void
-Ship::CommandMode()
-{
- if (!dir || dir->Type() != ShipCtrl::DIR_TYPE) {
- const char* msg = "Captain on the bridge";
- RadioVox* vox = new(__FILE__,__LINE__) RadioVox(0, "1", msg);
-
- if (vox) {
- vox->AddPhrase(msg);
-
- if (!vox->Start()) {
- RadioView::Message( RadioTraffic::TranslateVox(msg) );
- delete vox;
- }
- }
-
- SetControls(sim->GetControls());
- }
-
- else {
- const char* msg = "Exec, you have the conn";
- RadioVox* vox = new(__FILE__,__LINE__) RadioVox(0, "1", msg);
-
- if (vox) {
- vox->AddPhrase(msg);
-
- if (!vox->Start()) {
- RadioView::Message( RadioTraffic::TranslateVox(msg) );
- delete vox;
- }
- }
-
- SetControls(0);
- }
-}
-
-// +----------------------------------------------------------------------+
-
-Instruction*
-Ship::GetNextNavPoint()
-{
- if (launch_point && launch_point->Status() <= Instruction::ACTIVE)
- return launch_point;
-
- if (element)
- return element->GetNextNavPoint();
-
- return 0;
-}
-
-int
-Ship::GetNavIndex(const Instruction* n)
-{
- if (element)
- return element->GetNavIndex(n);
-
- return 0;
-}
-
-double
-Ship::RangeToNavPoint(const Instruction* n)
-{
- double distance = 0;
-
- if (n && n->Region()) {
- Point npt = n->Region()->Location() + n->Location();
- npt -= GetRegion()->Location();
- npt = npt.OtherHand(); // convert from map to sim coords
-
- distance = Point(npt - Location()).length();
- }
-
- return distance;
-}
-
-void
-Ship::SetNavptStatus(Instruction* navpt, int status)
-{
- if (navpt && navpt->Status() != status) {
- if (status == Instruction::COMPLETE) {
- if (navpt->Action() == Instruction::ASSAULT)
- ::Print("Completed Assault\n");
-
- else if (navpt->Action() == Instruction::STRIKE)
- ::Print("Completed Strike\n");
- }
-
- navpt->SetStatus(status);
-
- if (status == Instruction::COMPLETE)
- sim->ProcessEventTrigger(MissionEvent::TRIGGER_NAVPT, 0, Name(), GetNavIndex(navpt));
-
- if (element) {
- int index = element->GetNavIndex(navpt);
-
- if (index >= 0)
- NetUtil::SendNavData(false, element, index-1, navpt);
- }
- }
-}
-
-List<Instruction>&
-Ship::GetFlightPlan()
-{
- if (element)
- return element->GetFlightPlan();
-
- static List<Instruction> dummy_flight_plan;
- return dummy_flight_plan;
-}
-
-int
-Ship::FlightPlanLength()
-{
- if (element)
- return element->FlightPlanLength();
-
- return 0;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::SetWard(Ship* s)
-{
- if (ward == s)
- return;
-
- ward = s;
-
- if (ward)
- Observe(ward);
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::SetTarget(SimObject* targ, System* sub, bool from_net)
-{
- if (targ && targ->Type() == SimObject::SIM_SHIP) {
- Ship* targ_ship = (Ship*) targ;
-
- if (targ_ship && targ_ship->IsNetObserver())
- return;
- }
-
- if (target != targ) {
- // DON'T IGNORE TARGET, BECAUSE IT MAY BE IN THREAT LIST
- target = targ;
- if (target) Observe(target);
-
- if (sim && target)
- sim->ProcessEventTrigger(MissionEvent::TRIGGER_TARGET, 0, target->Name());
- }
-
- subtarget = sub;
-
- ListIter<WeaponGroup> weapon = weapons;
- while (++weapon) {
- if (weapon->GetFiringOrders() != Weapon::POINT_DEFENSE) {
- weapon->SetTarget(target, subtarget);
-
- if (sub || !IsStarship())
- weapon->SetSweep(Weapon::SWEEP_NONE);
- else
- weapon->SetSweep(Weapon::SWEEP_TIGHT);
- }
- }
-
- if (!from_net && NetGame::GetInstance())
- NetUtil::SendObjTarget(this);
-
- // track engagement:
- if (target && target->Type() == SimObject::SIM_SHIP) {
- Element* elem = GetElement();
- Element* tgt_elem = ((Ship*) target)->GetElement();
-
- if (elem)
- elem->SetAssignment(tgt_elem);
- }
-}
-
-void
-Ship::DropTarget()
-{
- target = 0;
- subtarget = 0;
-
- SetTarget(target, subtarget);
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::CycleSubTarget(int dir)
-{
- if (!target || target->Type() != SimObject::SIM_SHIP)
- return;
-
- Ship* tgt = (Ship*) target;
-
- if (tgt->IsDropship())
- return;
-
- System* subtgt = 0;
-
- ListIter<System> sys = tgt->Systems();
-
- if (dir > 0) {
- int latch = (subtarget == 0);
- while (++sys) {
- if (sys->Type() == System::COMPUTER || // computers are not targetable
- sys->Type() == System::SENSOR) // sensors are not targetable
- continue;
-
- if (sys.value() == subtarget) {
- latch = 1;
- }
-
- else if (latch) {
- subtgt = sys.value();
- break;
- }
- }
- }
-
- else {
- System* prev = 0;
-
- while (++sys) {
- if (sys->Type() == System::COMPUTER || // computers are not targetable
- sys->Type() == System::SENSOR) // sensors are not targetable
- continue;
-
- if (sys.value() == subtarget) {
- subtgt = prev;
- break;
- }
-
- prev = sys.value();
- }
-
- if (!subtarget)
- subtgt = prev;
- }
-
- SetTarget(tgt, subtgt);
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::ExecFrame(double seconds)
-{
- ZeroMemory(trigger, sizeof(trigger));
- altitude_agl = -1.0e6f;
-
- if (flight_phase < LAUNCH) {
- DockFrame(seconds);
- return;
- }
-
- if (flight_phase == LAUNCH ||
- (flight_phase == TAKEOFF && AltitudeAGL() > Radius())) {
- SetFlightPhase(ACTIVE);
- }
-
- if (transition_time > 0) {
- transition_time -= (float) seconds;
-
- if (transition_time <= 0) {
- CompleteTransition();
- return;
- }
-
- if (rep && IsDying() && killer) {
- killer->ExecFrame(seconds);
- }
- }
-
- // observers do not run out of power:
- if (IsNetObserver()) {
- for (int i = 0; i < reactors.size(); i++)
- reactors[i]->SetFuelRange(1e6);
- }
-
- if (IsStatic()) {
- StatFrame(seconds);
- return;
- }
-
- CheckFriendlyFire();
- ExecNavFrame(seconds);
- ExecEvalFrame(seconds);
-
- if (IsAirborne()) {
- // are we trying to make orbit?
- if (Location().y >= TERRAIN_ALTITUDE_LIMIT)
- MakeOrbit();
- }
-
- if (!InTransition()) {
- ExecSensors(seconds);
- ExecThrottle(seconds);
- }
-
- else if (IsDropping() || IsAttaining() || IsSkipping()) {
- throttle = 100;
- }
-
- if (target && target->Life() == 0) {
- DropTarget();
- }
-
- ExecPhysics(seconds);
-
- if (!InTransition()) {
- UpdateTrack();
- }
-
- // are we docking?
- if (IsDropship()) {
- ListIter<Ship> iter = GetRegion()->Carriers();
-
- while (++iter) {
- Ship* carrier_target = iter.value();
-
- double range = (Location() - carrier_target->Location()).length();
- if (range > carrier_target->Radius() * 1.5)
- continue;
-
- if (carrier_target->GetIFF() == GetIFF() || carrier_target->GetIFF() == 0) {
- for (int i = 0; i < carrier_target->NumFlightDecks(); i++) {
- if (carrier_target->GetFlightDeck(i)->Recover(this))
- break;
- }
- }
- }
- }
-
- ExecSystems(seconds);
- ExecMaintFrame(seconds);
-
- if (flight_decks.size() > 0) {
- Camera* global_cam = CameraDirector::GetInstance()->GetCamera();
- Point global_cam_loc = global_cam->Pos();
- bool disable_shadows = false;
-
- for (int i = 0; i < flight_decks.size(); i++) {
- if (flight_decks[i]->ContainsPoint(global_cam_loc))
- disable_shadows = true;
- }
-
- EnableShadows(!disable_shadows);
- }
-
- if (!_finite(Location().x)) {
- DropTarget();
- }
-
- if (!IsStatic() && !IsGroundUnit() && GetFlightModel() < 2)
- CalcFlightPath();
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::LaunchProbe()
-{
- if (net_observer_mode)
- return;
-
- if (sensor_drone) {
- sensor_drone = 0;
- }
-
- if (probe) {
- sensor_drone = (Drone*) probe->Fire();
-
- if (sensor_drone)
- Observe(sensor_drone);
-
- else if (sim->GetPlayerShip() == this)
- Button::PlaySound(Button::SND_REJECT);
- }
-}
-
-void
-Ship::SetProbe(Drone* d)
-{
- if (sensor_drone != d) {
- sensor_drone = d;
-
- if (sensor_drone)
- Observe(sensor_drone);
- }
-}
-
-void
-Ship::ExecSensors(double seconds)
-{
- // how visible are we?
- DoEMCON();
-
- // what can we see?
- if (sensor)
- sensor->ExecFrame(seconds);
-
- // can we still see our target?
- if (target) {
- int target_found = 0;
- ListIter<Contact> c_iter = ContactList();
- while (++c_iter) {
- Contact* c = c_iter.value();
-
- if (target == c->GetShip() || target == c->GetShot()) {
- target_found = 1;
-
- bool vis = c->Visible(this) || c->Threat(this);
-
- if (!vis && !c->PasLock() && !c->ActLock())
- DropTarget();
- }
- }
-
- if (!target_found)
- DropTarget();
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::ExecNavFrame(double seconds)
-{
- bool auto_pilot = false;
-
- // update director info string:
- SetFLCSMode(flcs_mode);
-
- if (navsys) {
- navsys->ExecFrame(seconds);
-
- if (navsys->AutoNavEngaged()) {
- if (dir && dir->Type() == NavAI::DIR_TYPE) {
- NavAI* navai = (NavAI*) dir;
-
- if (navai->Complete()) {
- navsys->DisengageAutoNav();
- SetControls(sim->GetControls());
- }
- else {
- auto_pilot = true;
- }
- }
- }
- }
-
- // even if we are not on auto pilot,
- // have we completed the next navpoint?
-
- Instruction* navpt = GetNextNavPoint();
- if (navpt && !auto_pilot) {
- if (navpt->Region() == GetRegion()) {
- double distance = 0;
-
- Point npt = navpt->Location();
-
- if (navpt->Region())
- npt += navpt->Region()->Location();
-
- Sim* sim = Sim::GetSim();
- if (sim->GetActiveRegion())
- npt -= sim->GetActiveRegion()->Location();
-
- npt = npt.OtherHand();
-
- // distance from self to navpt:
- distance = Point(npt - Location()).length();
-
- if (distance < 10 * Radius())
- SetNavptStatus(navpt, Instruction::COMPLETE);
- }
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::ExecEvalFrame(double seconds)
-{
- // is it already too late?
- if (life == 0 || integrity < 1) return;
-
- const DWORD EVAL_FREQUENCY = 1000; // once every second
- static DWORD last_eval_frame = 0; // one ship per game frame
-
- if (element && element->NumObjectives() > 0 &&
- Game::GameTime() - last_eval_time > EVAL_FREQUENCY &&
- last_eval_frame != Game::Frame()) {
-
- last_eval_time = Game::GameTime();
- last_eval_frame = Game::Frame();
-
- for (int i = 0; i < element->NumObjectives(); i++) {
- Instruction* obj = element->GetObjective(i);
-
- if (obj->Status() <= Instruction::ACTIVE) {
- obj->Evaluate(this);
- }
- }
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::ExecPhysics(double seconds)
-{
- if (net_control) {
- net_control->ExecFrame(seconds);
- Thrust(seconds); // drive flare
- }
- else {
- thrust = (float) Thrust(seconds);
- SetupAgility();
-
- if (seconds > 0) {
- g_force = 0.0f;
- }
-
- if (IsAirborne()) {
- Point v1 = velocity;
- AeroFrame(seconds);
- Point v2 = velocity;
- Point dv = v2 - v1 + Point(0, g_accel*seconds, 0);
-
- if (seconds > 0) {
- g_force = (float) (dv * cam.vup() / seconds) / 9.8f;
- }
- }
-
- else if (IsDying() || flight_model < 2) { // standard and relaxed modes
- Physical::ExecFrame(seconds);
- }
-
- else { // arcade mode
- Physical::ArcadeFrame(seconds);
- }
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::ExecThrottle(double seconds)
-{
- double spool = 75 * seconds;
-
- if (throttle < throttle_request)
- if (throttle_request-throttle < spool)
- throttle = throttle_request;
- else
- throttle += spool;
-
- else if (throttle > throttle_request)
- if (throttle - throttle_request < spool)
- throttle = throttle_request;
- else
- throttle -= spool;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::ExecSystems(double seconds)
-{
- if (!rep)
- return;
-
- int i;
-
- ListIter<System> iter = systems;
- while (++iter) {
- System* sys = iter.value();
-
- sys->Orient(this);
-
- // sensors have already been executed,
- // they can not be run twice in a frame!
- if (sys->Type() != System::SENSOR)
- sys->ExecFrame(seconds);
- }
-
- // hangars and weapon groups are not systems
- // they must be executed separately from above
- if (hangar)
- hangar->ExecFrame(seconds);
-
- wep_mass = 0.0f;
- wep_resist = 0.0f;
-
- bool winchester_cycle = false;
-
- for (i = 0; i < weapons.size(); i++) {
- WeaponGroup* w_group = weapons[i];
- w_group->ExecFrame(seconds);
-
- if (w_group->GetTrigger() && w_group->GetFiringOrders() == Weapon::MANUAL) {
-
- Weapon* gun = w_group->GetSelected();
-
- SimObject* gun_tgt = gun->GetTarget();
-
- // if no target has been designated for this
- // weapon, let it guide on the contact closest
- // to its boresight. this must be done before
- // firing the weapon.
-
- if (sensor && gun->Guided() && !gun->Design()->beam && !gun_tgt) {
- gun->SetTarget(sensor->AcquirePassiveTargetForMissile(), 0);
- }
-
- gun->Fire();
-
- w_group->SetTrigger(false);
- w_group->CycleWeapon();
- w_group->CheckAmmo();
-
- // was that the last shot from this missile group?
- if (w_group->IsMissile() && w_group->Ammo() < 1) {
-
- // is this the current secondary weapon group?
- if (weapons[secondary] == w_group) {
- winchester_cycle = true;
- }
- }
- }
-
- wep_mass += w_group->Mass();
- wep_resist += w_group->Resistance();
- }
-
- // if we just fired the last shot in the current secondary
- // weapon group, auto cycle to another secondary weapon:
- if (winchester_cycle) {
- int old_secondary = secondary;
-
- CycleSecondary();
-
- // do not winchester-cycle to an A2G missile type,
- // or a missile that is also out of ammo,
- // keep going!
-
- while (secondary != old_secondary) {
- Weapon* missile = GetSecondary();
- if (missile && missile->CanTarget(Ship::GROUND_UNITS))
- CycleSecondary();
-
- else if (weapons[secondary]->Ammo() < 1)
- CycleSecondary();
-
- else
- break;
- }
- }
-
- mass = (float) design->mass + wep_mass;
-
- if (IsDropship())
- agility = (float) design->agility - wep_resist;
-
- if (shieldRep) {
- Solid* solid = (Solid*) rep;
- shieldRep->MoveTo(solid->Location());
- shieldRep->SetOrientation(solid->Orientation());
-
- bool bubble = false;
- if (shield)
- bubble = shield->ShieldBubble();
-
- if (shieldRep->ActiveHits()) {
- shieldRep->Energize(seconds, bubble);
- shieldRep->Show();
- }
- else {
- shieldRep->Hide();
- }
- }
-
- if (cockpit) {
- Solid* solid = (Solid*) rep;
-
- Point cpos = cam.Pos() +
- cam.vrt() * bridge_vec.x +
- cam.vpn() * bridge_vec.y +
- cam.vup() * bridge_vec.z;
-
- cockpit->MoveTo(cpos);
- cockpit->SetOrientation(solid->Orientation());
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::AeroFrame(double seconds)
-{
- float g_save = g_accel;
-
- if (Class() == LCA) {
- lat_thrust = true;
- SetGravity(0.0f);
- }
-
- if (AltitudeAGL() < Radius()) {
- SetGravity(0.0f);
-
- // on the ground/runway?
- double bottom = 1e9;
- double tlevel = Location().y - AltitudeAGL();
-
- // taking off or landing?
- if (flight_phase < ACTIVE || flight_phase > APPROACH) {
- if (dock)
- tlevel = dock->MountLocation().y;
- }
-
- if (tlevel < 0)
- tlevel = 0;
-
- if (gear)
- bottom = gear->GetTouchDown()-1;
- else
- bottom = Location().y-6;
-
- if (bottom < tlevel)
- TranslateBy(Point(0, bottom-tlevel, 0));
- }
-
- // MODEL 2: ARCADE
- if (flight_model >= 2) {
- Physical::ArcadeFrame(seconds);
- }
-
- // MODEL 1: RELAXED
- else if (flight_model == 1) {
- Physical::ExecFrame(seconds);
- }
-
- // MODEL 0: STANDARD
- else {
- // apply drag-torque (i.e. turn ship into
- // velocity vector to minimize drag):
-
- Point vnrm = velocity;
- double v = vnrm.Normalize();
- double pitch_deflection = vnrm * cam.vup();
- double yaw_deflection = vnrm * cam.vrt();
-
- if (lat_thrust && v < 250) {
- }
-
- else {
- if (v < 250) {
- double factor = 1.2 + (250 - v) / 100;
-
- ApplyPitch(pitch_deflection * -factor);
- ApplyYaw(yaw_deflection * factor);
-
- dp += (float) (dp_acc * seconds);
- dy += (float) (dy_acc * seconds);
- }
-
- else {
- if (fabs(pitch_deflection) > stall) {
- ApplyPitch(pitch_deflection * -1.2);
- dp += (float) (dp_acc * seconds);
- }
-
- ApplyYaw(yaw_deflection * 2);
- dy += (float) (dy_acc * seconds);
- }
- }
-
- // compute rest of physics:
- Physical::AeroFrame(seconds);
- }
-
- SetGravity(g_save);
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::LinearFrame(double seconds)
-{
- Physical::LinearFrame(seconds);
-
- if (!IsAirborne() || Class() != LCA)
- return;
-
- // damp lateral movement in atmosphere:
-
- // side-to-side
- if (!trans_x) {
- Point transvec = cam.vrt();
- transvec *= (transvec * velocity) * seconds * 0.5;
- velocity -= transvec;
- }
-
- // fore-and-aft
- if (!trans_y && fabs(thrust) < 1.0f) {
- Point transvec = cam.vpn();
- transvec *= (transvec * velocity) * seconds * 0.25;
- velocity -= transvec;
- }
-
- // up-and-down
- if (!trans_z) {
- Point transvec = cam.vup();
- transvec *= (transvec * velocity) * seconds * 0.5;
- velocity -= transvec;
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::DockFrame(double seconds)
-{
- SelectDetail(seconds);
-
- if (sim->GetPlayerShip() == this) {
- // Make sure the thruster sound is diabled
- // when the player is on the runway or catapult
- if (thruster) {
- thruster->ExecTrans(0,0,0);
- }
- }
-
- if (rep) {
- // Update the graphic rep and light sources:
- // (This is usually done by the physics class,
- // but when the ship is in dock, we skip the
- // standard physics processing):
- rep->MoveTo(cam.Pos());
- rep->SetOrientation(cam.Orientation());
-
- if (light)
- light->MoveTo(cam.Pos());
-
- ListIter<System> iter = systems;
- while (++iter)
- iter->Orient(this);
-
- double spool = 75 * seconds;
-
- if (flight_phase == DOCKING) {
- throttle_request = 0;
- throttle = 0;
- }
-
- else if (throttle < throttle_request)
- if (throttle_request-throttle < spool)
- throttle = throttle_request;
- else
- throttle += spool;
-
- else if (throttle > throttle_request)
- if (throttle - throttle_request < spool)
- throttle = throttle_request;
- else
- throttle -= spool;
-
- // make sure there is power to run the drive:
- for (int i = 0; i < reactors.size(); i++)
- reactors[i]->ExecFrame(seconds);
-
- // count up weapon ammo for status mfd:
- for (int i = 0; i < weapons.size(); i++)
- weapons[i]->ExecFrame(seconds);
-
- // show drive flare while on catapult:
- if (main_drive) {
- main_drive->SetThrottle(throttle);
-
- if (throttle > 0)
- main_drive->Thrust(seconds); // show drive flare
- }
- }
-
- if (cockpit && !cockpit->Hidden()) {
- Solid* solid = (Solid*) rep;
-
- Point cpos = cam.Pos() +
- cam.vrt() * bridge_vec.x +
- cam.vpn() * bridge_vec.y +
- cam.vup() * bridge_vec.z;
-
- cockpit->MoveTo(cpos);
- cockpit->SetOrientation(solid->Orientation());
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::StatFrame(double seconds)
-{
- if (flight_phase != ACTIVE) {
- flight_phase = ACTIVE;
- launch_time = Game::GameTime() + 1;
-
- if (element)
- element->SetLaunchTime(launch_time);
- }
-
- if (IsGroundUnit()) {
- // glue buildings to the terrain:
- Point loc = Location();
- Terrain* terrain = region->GetTerrain();
-
- if (terrain) {
- loc.y = terrain->Height(loc.x, loc.z);
- MoveTo(loc);
- }
- }
-
- if (rep) {
- rep->MoveTo(cam.Pos());
- rep->SetOrientation(cam.Orientation());
- }
-
- if (light) {
- light->MoveTo(cam.Pos());
- }
-
- ExecSensors(seconds);
-
- if (target && target->Life() == 0) {
- DropTarget();
- }
-
- if (dir) dir->ExecFrame(seconds);
-
- SelectDetail(seconds);
-
- int i = 0;
-
- if (rep) {
- ListIter<System> iter = systems;
- while (++iter)
- iter->Orient(this);
-
- for (i = 0; i < reactors.size(); i++)
- reactors[i]->ExecFrame(seconds);
-
- for (i = 0; i < navlights.size(); i++)
- navlights[i]->ExecFrame(seconds);
-
- for (i = 0; i < weapons.size(); i++)
- weapons[i]->ExecFrame(seconds);
-
- if (farcaster) {
- farcaster->ExecFrame(seconds);
-
- if (navlights.size() == 2) {
- if (farcaster->Charge() > 99) {
- navlights[0]->Enable();
- navlights[1]->Disable();
- }
- else {
- navlights[0]->Disable();
- navlights[1]->Enable();
- }
- }
- }
-
- if (shield)
- shield->ExecFrame(seconds);
-
- if (hangar)
- hangar->ExecFrame(seconds);
-
- if (flight_decks.size() > 0) {
- Camera* global_cam = CameraDirector::GetInstance()->GetCamera();
- Point global_cam_loc = global_cam->Pos();
- bool disable_shadows = false;
-
- for (i = 0; i < flight_decks.size(); i++) {
- flight_decks[i]->ExecFrame(seconds);
-
- if (flight_decks[i]->ContainsPoint(global_cam_loc))
- disable_shadows = true;
- }
-
- EnableShadows(!disable_shadows);
- }
- }
-
- if (shieldRep) {
- Solid* solid = (Solid*) rep;
- shieldRep->MoveTo(solid->Location());
- shieldRep->SetOrientation(solid->Orientation());
-
- bool bubble = false;
- if (shield)
- bubble = shield->ShieldBubble();
-
- if (shieldRep->ActiveHits()) {
- shieldRep->Energize(seconds, bubble);
- shieldRep->Show();
- }
- else {
- shieldRep->Hide();
- }
- }
-
- if (!_finite(Location().x)) {
- DropTarget();
- }
-}
-
-// +--------------------------------------------------------------------+
-
-Graphic*
-Ship::Cockpit() const
-{
- return cockpit;
-}
-
-void
-Ship::ShowCockpit()
-{
- if (cockpit) {
- cockpit->Show();
-
- ListIter<WeaponGroup> g = weapons;
- while (++g) {
- ListIter<Weapon> w = g->GetWeapons();
- while (++w) {
- Solid* turret = w->GetTurret();
- if (turret) {
- turret->Show();
-
- Solid* turret_base = w->GetTurretBase();
- if (turret_base)
- turret_base->Show();
- }
-
- if (w->IsMissile()) {
- for (int i = 0; i < w->Ammo(); i++) {
- Solid* store = w->GetVisibleStore(i);
- if (store) {
- store->Show();
- }
- }
- }
- }
- }
- }
-}
-
-void
-Ship::HideCockpit()
-{
- if (cockpit)
- cockpit->Hide();
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::SelectDetail(double seconds)
-{
- detail.ExecFrame(seconds);
- detail.SetLocation(GetRegion(), Location());
-
- int new_level = detail.GetDetailLevel();
-
- if (detail_level != new_level) {
- Scene* scene = 0;
-
- // remove current rep from scene (if necessary):
- for (int i = 0; i < detail.NumModels(detail_level); i++) {
- Graphic* g = detail.GetRep(detail_level, i);
- if (g) {
- scene = g->GetScene();
- if (scene)
- scene->DelGraphic(g);
- }
- }
-
- // switch to new rep:
- detail_level = new_level;
- rep = detail.GetRep(detail_level);
-
- // add new rep to scene (if necessary):
- if (scene) {
- for (int i = 0; i < detail.NumModels(detail_level); i++) {
- Graphic* g = detail.GetRep(detail_level, i);
- Point s = detail.GetSpin(detail_level, i);
- Matrix m = cam.Orientation();
-
- m.Pitch(s.x);
- m.Yaw(s.z);
- m.Roll(s.y);
-
- scene->AddGraphic(g);
- g->MoveTo(cam.Pos() + detail.GetOffset(detail_level, i));
- g->SetOrientation(m);
- }
-
- // show/hide external stores and landing gear...
- if (detail.NumLevels() > 0) {
- if (gear && (gear->GetState() != LandingGear::GEAR_UP)) {
- for (int i = 0; i < gear->NumGear(); i++) {
- Solid* g = gear->GetGear(i);
-
- if (g) {
- if (detail_level == 0)
- scene->DelGraphic(g);
- else
- scene->AddGraphic(g);
- }
- }
- }
-
- ListIter<WeaponGroup> g = weapons;
- while (++g) {
- ListIter<Weapon> w = g->GetWeapons();
- while (++w) {
- Solid* turret = w->GetTurret();
- if (turret) {
- if (detail_level == 0)
- scene->DelGraphic(turret);
- else
- scene->AddGraphic(turret);
-
- Solid* turret_base = w->GetTurretBase();
- if (turret_base) {
- if (detail_level == 0)
- scene->DelGraphic(turret_base);
- else
- scene->AddGraphic(turret_base);
- }
- }
- if (w->IsMissile()) {
- for (int i = 0; i < w->Ammo(); i++) {
- Solid* store = w->GetVisibleStore(i);
- if (store) {
- if (detail_level == 0)
- scene->DelGraphic(store);
- else
- scene->AddGraphic(store);
- }
- }
- }
- }
- }
- }
- }
- }
-
- else {
- int nmodels = detail.NumModels(detail_level);
-
- if (nmodels > 1) {
- for (int i = 0; i < nmodels; i++) {
- Graphic* g = detail.GetRep(detail_level, i);
- Point s = detail.GetSpin(detail_level, i);
- Matrix m = cam.Orientation();
-
- m.Pitch(s.x);
- m.Yaw(s.z);
- m.Roll(s.y);
-
- g->MoveTo(cam.Pos() + detail.GetOffset(detail_level, i));
- g->SetOrientation(m);
- }
- }
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::ShowRep()
-{
- for (int i = 0; i < detail.NumModels(detail_level); i++) {
- Graphic* g = detail.GetRep(detail_level, i);
- g->Show();
- }
-
- if (gear && (gear->GetState() != LandingGear::GEAR_UP)) {
- for (int i = 0; i < gear->NumGear(); i++) {
- Solid* g = gear->GetGear(i);
- if (g) g->Show();
- }
- }
-
- ListIter<WeaponGroup> g = weapons;
- while (++g) {
- ListIter<Weapon> w = g->GetWeapons();
- while (++w) {
- Solid* turret = w->GetTurret();
- if (turret) {
- turret->Show();
-
- Solid* turret_base = w->GetTurretBase();
- if (turret_base)
- turret_base->Show();
- }
-
- if (w->IsMissile()) {
- for (int i = 0; i < w->Ammo(); i++) {
- Solid* store = w->GetVisibleStore(i);
- if (store) {
- store->Show();
- }
- }
- }
- }
- }
-}
-
-void
-Ship::HideRep()
-{
- for (int i = 0; i < detail.NumModels(detail_level); i++) {
- Graphic* g = detail.GetRep(detail_level, i);
- g->Hide();
- }
-
- if (gear && (gear->GetState() != LandingGear::GEAR_UP)) {
- for (int i = 0; i < gear->NumGear(); i++) {
- Solid* g = gear->GetGear(i);
- if (g) g->Hide();
- }
- }
-
- ListIter<WeaponGroup> g = weapons;
- while (++g) {
- ListIter<Weapon> w = g->GetWeapons();
- while (++w) {
- Solid* turret = w->GetTurret();
- if (turret) {
- turret->Hide();
-
- Solid* turret_base = w->GetTurretBase();
- if (turret_base)
- turret_base->Hide();
- }
-
- if (w->IsMissile()) {
- for (int i = 0; i < w->Ammo(); i++) {
- Solid* store = w->GetVisibleStore(i);
- if (store) {
- store->Hide();
- }
- }
- }
- }
- }
-}
-
-void
-Ship::EnableShadows(bool enable)
-{
- for (int i = 0; i < detail.NumModels(detail_level); i++) {
- Graphic* g = detail.GetRep(detail_level, i);
-
- if (g->IsSolid()) {
- Solid* s = (Solid*) g;
-
- ListIter<Shadow> iter = s->GetShadows();
- while (++iter) {
- Shadow* shadow = iter.value();
- shadow->SetEnabled(enable);
- }
- }
- }
-}
-
-// +--------------------------------------------------------------------+
-
-bool
-Ship::Update(SimObject* obj)
-{
- if (obj == ward)
- ward = 0;
-
- if (obj == target) {
- target = 0;
- subtarget = 0;
- }
-
- if (obj == carrier) {
- carrier = 0;
- dock = 0;
- inbound = 0;
- }
-
- if (obj->Type() == SimObject::SIM_SHOT ||
- obj->Type() == SimObject::SIM_DRONE) {
- Shot* s = (Shot*) obj;
-
- if (sensor_drone == s)
- sensor_drone = 0;
-
- if (decoy_list.contains(s))
- decoy_list.remove(s);
-
- if (threat_list.contains(s))
- threat_list.remove(s);
- }
-
- return SimObserver::Update(obj);
-}
-
-// +--------------------------------------------------------------------+
-
-int
-Ship::GetFuelLevel() const
-{
- if (reactors.size() > 0) {
- PowerSource* reactor = reactors[0];
- if (reactor)
- return reactor->Charge();
- }
-
- return 0;
-}
-
-void
-Ship::SetThrottle(double percent)
-{
- throttle_request = percent;
-
- if (throttle_request < 0) throttle_request = 0;
- else if (throttle_request > 100) throttle_request = 100;
-
- if (throttle_request < 50)
- augmenter = false;
-}
-
-void
-Ship::SetAugmenter(bool enable)
-{
- if (throttle <= 50)
- enable = false;
-
- if (main_drive && main_drive->MaxAugmenter() <= 0)
- enable = false;
-
- augmenter = enable;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::SetTransition(double trans_time, int trans_type, const Point& trans_loc)
-{
- transition_time = (float) trans_time;
- transition_type = trans_type;
- transition_loc = trans_loc;
-}
-
-void
-Ship::DropOrbit()
-{
- if (IsDropship() && transition_type == TRANSITION_NONE && !IsAirborne()) {
- SimRegion* dst_rgn = sim->FindNearestTerrainRegion(this);
-
- if (dst_rgn &&
- dst_rgn->GetOrbitalRegion()->Primary() ==
- GetRegion()->GetOrbitalRegion()->Primary()) {
-
- transition_time = 10.0f;
- transition_type = TRANSITION_DROP_ORBIT;
- transition_loc = Location() + Heading() * (-2*Radius());
-
- RadioTraffic::SendQuickMessage(this, RadioMessage::BREAK_ORBIT);
- SetControls(0);
- }
- }
-}
-
-void
-Ship::MakeOrbit()
-{
- if (IsDropship() && transition_type == TRANSITION_NONE && IsAirborne()) {
- transition_time = 5.0f;
- transition_type = TRANSITION_MAKE_ORBIT;
- transition_loc = Location() + Heading() * (-2*Radius());
-
- RadioTraffic::SendQuickMessage(this, RadioMessage::MAKE_ORBIT);
- SetControls(0);
- }
-}
-
-// +--------------------------------------------------------------------+
-
-bool
-Ship::IsInCombat()
-{
- if (IsRogue())
- return true;
-
- bool combat = false;
-
- ListIter<Contact> c_iter = ContactList();
- while (++c_iter) {
- Contact* c = c_iter.value();
- Ship* cship = c->GetShip();
- int ciff = c->GetIFF(this);
- Point delta = c->Location() - Location();
- double dist = delta.length();
-
- if (c->Threat(this) && !cship) {
- if (IsStarship())
- combat = dist < 120e3;
- else
- combat = dist < 60e3;
- }
-
- else if (cship && ciff > 0 && ciff != GetIFF()) {
- if (IsStarship() && cship->IsStarship())
- combat = dist < 120e3;
- else
- combat = dist < 60e3;
- }
- }
-
- return combat;
-}
-
-// +--------------------------------------------------------------------+
-
-bool
-Ship::CanTimeSkip()
-{
- bool go = false;
- Instruction* navpt = GetNextNavPoint();
-
- if (MissionClock() < 10000 || NetGame::IsNetGame())
- return go;
-
- if (navpt) {
- go = true;
-
- if (navpt->Region() != GetRegion())
- go = false;
-
- else if (Point(navpt->Location().OtherHand() - Location()).length() < 30e3)
- go = false;
- }
-
- if (go)
- go = !IsInCombat();
-
- return go;
-}
-
-void
-Ship::TimeSkip()
-{
- if (CanTimeSkip()) {
- // go back to regular time before performing the skip:
- Game::SetTimeCompression(1);
-
- transition_time = 7.5f;
- transition_type = TRANSITION_TIME_SKIP;
- transition_loc = Location() + Heading() * (Velocity().length() * 4);
- // 2500; //(8*Radius());
-
- if (rand() < 16000)
- transition_loc += BeamLine() * (2.5*Radius());
- else
- transition_loc += BeamLine() * (-2 *Radius());
-
- if (rand() < 8000)
- transition_loc += LiftLine() * (-1*Radius());
- else
- transition_loc += LiftLine() * (1.8*Radius());
-
- SetControls(0);
- }
-
- else if (sim->GetPlayerShip() == this) {
- SetAutoNav(true);
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::DropCam(double time, double range)
-{
- transition_type = TRANSITION_DROP_CAM;
-
- if (time > 0)
- transition_time = (float) time;
- else
- transition_time = 10.0f;
-
- Point offset = Heading() * (Velocity().length() * 5);
- double lateral_offset = 2 * Radius();
- double vertical_offset = Radius();
-
- if (vertical_offset > 300)
- vertical_offset = 300;
-
- if (rand() < 16000)
- lateral_offset *= -1;
-
- if (rand() < 8000)
- vertical_offset *= -1;
-
- offset += BeamLine() * lateral_offset;
- offset += LiftLine() * vertical_offset;
-
- if (range > 0)
- offset *= range;
-
- transition_loc = Location() + offset;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::DeathSpiral()
-{
- if (!killer)
- killer = new(__FILE__,__LINE__) ShipKiller(this);
-
- ListIter<System> iter = systems;
- while (++iter)
- iter->PowerOff();
-
- // transfer arcade velocity to newtonian velocity:
- if (flight_model >= 2) {
- velocity += arcade_velocity;
- }
-
- if (GetIFF() < 100 && !IsGroundUnit()) {
- RadioTraffic::SendQuickMessage(this, RadioMessage::DISTRESS);
- }
-
- transition_type = TRANSITION_DEATH_SPIRAL;
-
- killer->BeginDeathSpiral();
-
- transition_time = killer->TransitionTime();
- transition_loc = killer->TransitionLoc();
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::CompleteTransition()
-{
- int old_type = transition_type;
- transition_time = 0.0f;
- transition_type = TRANSITION_NONE;
-
- switch (old_type) {
- case TRANSITION_NONE:
- case TRANSITION_DROP_CAM:
- default:
- return;
-
- case TRANSITION_DROP_ORBIT: {
- SetControls(0);
- SimRegion* dst_rgn = sim->FindNearestTerrainRegion(this);
- Point dst_loc = Location().OtherHand() * 0.20;
- dst_loc.x += 6000 * GetElementIndex();
- dst_loc.z = TERRAIN_ALTITUDE_LIMIT * 0.95;
- dst_loc += RandomDirection() * 2e3;
-
- sim->RequestHyperJump(this, dst_rgn, dst_loc, TRANSITION_DROP_ORBIT);
-
- ShipStats* stats = ShipStats::Find(Name());
- stats->AddEvent(SimEvent::BREAK_ORBIT, dst_rgn->Name());
- }
- break;
-
- case TRANSITION_MAKE_ORBIT: {
- SetControls(0);
- SimRegion* dst_rgn = sim->FindNearestSpaceRegion(this);
- double dist = 200.0e3 + 10.0e3 * GetElementIndex();
- Point esc_vec = dst_rgn->GetOrbitalRegion()->Location() -
- dst_rgn->GetOrbitalRegion()->Primary()->Location();
-
- esc_vec.z = -100 * GetElementIndex();
- esc_vec.Normalize();
- esc_vec *= -dist;
- esc_vec += RandomDirection() * 2e3;
-
- sim->RequestHyperJump(this, dst_rgn, esc_vec, TRANSITION_MAKE_ORBIT);
-
- ShipStats* stats = ShipStats::Find(Name());
- stats->AddEvent(SimEvent::MAKE_ORBIT, dst_rgn->Name());
- }
- break;
-
- case TRANSITION_TIME_SKIP: {
- Instruction* navpt = GetNextNavPoint();
-
- if (navpt) {
- Point delta = navpt->Location().OtherHand() - Location();
- Point unit = delta; unit.Normalize();
- Point trans = delta + unit * -20e3;
- double dist = trans.length();
- double speed = navpt->Speed();
-
- if (speed < 50) speed = 500;
-
- double etr = dist / speed;
-
- sim->ResolveTimeSkip(etr);
- }
- }
- break;
-
- case TRANSITION_DEATH_SPIRAL:
- SetControls(0);
- transition_type = TRANSITION_DEAD;
- break;
- }
-
-}
-
-bool
-Ship::IsAirborne() const
-{
- if (region)
- return region->Type() == SimRegion::AIR_SPACE;
-
- return false;
-}
-
-double
-Ship::CompassHeading() const
-{
- Point heading = Heading();
- double compass_heading = atan2(fabs(heading.x), heading.z);
-
- if (heading.x < 0)
- compass_heading *= -1;
-
- double result = compass_heading + PI;
-
- if (result >= 2*PI)
- result -= 2*PI;
-
- return result;
-}
-
-double
-Ship::CompassPitch() const
-{
- Point heading = Heading();
- return asin(heading.y);
-}
-
-double
-Ship::AltitudeMSL() const
-{
- return Location().y;
-}
-
-double
-Ship::AltitudeAGL() const
-{
- if (altitude_agl < -1000) {
- Ship* pThis = (Ship*) this; // cast-away const
- Point loc = Location();
-
- Terrain* terrain = region->GetTerrain();
-
- if (terrain)
- pThis->altitude_agl = (float) (loc.y - terrain->Height(loc.x, loc.z));
-
- else
- pThis->altitude_agl = (float) loc.y;
-
- if (!_finite(altitude_agl)) {
- pThis->altitude_agl = 0.0f;
- }
- }
-
- return altitude_agl;
-}
-
-double
-Ship::GForce() const
-{
- return g_force;
-}
-
-// +--------------------------------------------------------------------+
-
-WeaponGroup*
-Ship::FindWeaponGroup(const char* name)
-{
- WeaponGroup* group = 0;
-
- ListIter<WeaponGroup> iter = weapons;
- while (!group && ++iter)
- if (!_stricmp(iter->Name(), name))
- group = iter.value();
-
- if (!group) {
- group = new(__FILE__,__LINE__) WeaponGroup(name);
- weapons.append(group);
- }
-
- return group;
-}
-
-void
-Ship::SelectWeapon(int n, int w)
-{
- if (n < weapons.size())
- weapons[n]->SelectWeapon(w);
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::CyclePrimary()
-{
- if (weapons.isEmpty())
- return;
-
- if (IsDropship() && primary < weapons.size()) {
- WeaponGroup* p = weapons[primary];
- Weapon* w = p->GetSelected();
-
- if (w && w->GetTurret()) {
- p->SetFiringOrders(Weapon::POINT_DEFENSE);
- }
- }
-
- int n = primary + 1;
- while (n != primary) {
- if (n >= weapons.size())
- n = 0;
-
- if (weapons[n]->IsPrimary()) {
- weapons[n]->SetFiringOrders(Weapon::MANUAL);
- break;
- }
-
- n++;
- }
-
- primary = n;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::CycleSecondary()
-{
- if (weapons.isEmpty())
- return;
-
- int n = secondary + 1;
- while (n != secondary) {
- if (n >= weapons.size())
- n = 0;
-
- if (weapons[n]->IsMissile())
- break;
-
- n++;
- }
-
- secondary = n;
-
- // automatically switch sensors to appropriate mode:
- if (IsAirborne()) {
- Weapon* missile = GetSecondary();
- if (missile && missile->CanTarget(Ship::GROUND_UNITS))
- SetSensorMode(Sensor::GM);
- else if (sensor && sensor->GetMode() == Sensor::GM)
- SetSensorMode(Sensor::STD);
- }
-}
-
-int
-Ship::GetMissileEta(int index) const
-{
- if (index >= 0 && index < 4)
- return missile_eta[index];
-
- return 0;
-}
-
-void
-Ship::SetMissileEta(int id, int eta)
-{
- int index = -1;
-
- // are we tracking this missile's eta?
- for (int i = 0; i < 4; i++)
- if (id == missile_id[i])
- index = i;
-
- // if not, can we find an open slot to track it in?
- if (index < 0) {
- for (int i = 0; i < 4 && index < 0; i++) {
- if (missile_eta[i] == 0) {
- index = i;
- missile_id[i] = id;
- }
- }
- }
-
- // track the eta:
- if (index >= 0 && index < 4) {
- if (eta > 3599)
- eta = 3599;
-
- missile_eta[index] = (BYTE) eta;
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::DoEMCON()
-{
- ListIter<System> iter = systems;
- while (++iter) {
- System* s = iter.value();
- s->DoEMCON(emcon);
- }
-
- old_emcon = emcon;
-}
-
-// +--------------------------------------------------------------------+
-
-double
-Ship::Thrust(double seconds) const
-{
- double total_thrust = 0;
-
- if (main_drive) {
- // velocity limiter:
- Point H = Heading();
- Point V = Velocity();
- double vmag = V.Normalize();
- double eff_throttle = throttle;
- double thrust_factor = 1;
- double vfwd = H * V;
- bool aug_on = main_drive->IsAugmenterOn();
-
- if (vmag > vlimit && vfwd > 0) {
- double vmax = vlimit;
- if (aug_on)
- vmax *= 1.5;
-
- vfwd = 0.5 * vfwd + 0.5;
-
- // reduce drive efficiency at high fwd speed:
- thrust_factor = (vfwd * pow(vmax,3) / pow(vmag,3)) + (1-vfwd);
- }
-
- if (flcs)
- eff_throttle = flcs->Throttle();
-
- // square-law throttle curve to increase sensitivity
- // at lower throttle settings:
- if (flight_model > 1) {
- eff_throttle /= 100;
- eff_throttle *= eff_throttle;
- eff_throttle *= 100;
- }
-
- main_drive->SetThrottle(eff_throttle, augmenter);
- total_thrust += thrust_factor * main_drive->Thrust(seconds);
-
- if (aug_on && shake < 1.5)
- ((Ship*) this)->shake = 1.5f;
- }
-
- return total_thrust;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::CycleFLCSMode()
-{
- switch (flcs_mode) {
- case FLCS_MANUAL: SetFLCSMode(FLCS_HELM); break;
- case FLCS_AUTO: SetFLCSMode(FLCS_MANUAL); break;
- case FLCS_HELM: SetFLCSMode(FLCS_AUTO); break;
-
- default:
- if (IsStarship())
- flcs_mode = (BYTE) FLCS_HELM;
- else
- flcs_mode = (BYTE) FLCS_AUTO;
- break;
- }
-
- // reset helm heading to compass heading when switching
- // back to helm mode from manual mode:
-
- if (flcs_mode == FLCS_HELM) {
- if (IsStarship()) {
- SetHelmHeading(CompassHeading());
- SetHelmPitch(CompassPitch());
- }
- else {
- flcs_mode = (BYTE) FLCS_AUTO;
- }
- }
-}
-
-void
-Ship::SetFLCSMode(int mode)
-{
- flcs_mode = (BYTE) mode;
-
- if (IsAirborne())
- flcs_mode = (BYTE) FLCS_MANUAL;
-
- if (dir && dir->Type() < SteerAI::SEEKER) {
- switch (flcs_mode) {
- case FLCS_MANUAL: director_info = Game::GetText("flcs.manual"); break;
- case FLCS_AUTO: director_info = Game::GetText("flcs.auto"); break;
- case FLCS_HELM: director_info = Game::GetText("flcs.helm"); break;
- default: director_info = Game::GetText("flcs.fault"); break;
- }
-
- if (!flcs || !flcs->IsPowerOn())
- director_info = Game::GetText("flcs.offline");
-
- else if (IsAirborne())
- director_info = Game::GetText("flcs.atmospheric");
- }
-
- if (flcs)
- flcs->SetMode(mode);
-}
-
-int
-Ship::GetFLCSMode() const
-{
- return (int) flcs_mode;
-}
-
-void
-Ship::SetTransX(double t)
-{
- float limit = design->trans_x;
-
- if (thruster)
- limit = (float) thruster->TransXLimit();
-
- trans_x = (float) t;
-
- if (trans_x) {
- if (trans_x > limit)
- trans_x = limit;
- else if (trans_x < -limit)
- trans_x = -limit;
-
- // reduce thruster efficiency at high fwd speed:
- double vfwd = cam.vrt() * Velocity();
- double vmag = fabs(vfwd);
- if (vmag > vlimit) {
- if (trans_x > 0 && vfwd > 0 || trans_x < 0 && vfwd < 0)
- trans_x *= (float) (pow(vlimit,4) / pow(vmag,4));
- }
- }
-}
-
-void
-Ship::SetTransY(double t)
-{
- float limit = design->trans_y;
-
- if (thruster)
- limit = (float) thruster->TransYLimit();
-
- trans_y = (float) t;
-
- if (trans_y) {
- double vmag = Velocity().length();
-
- if (trans_y > limit)
- trans_y = limit;
- else if (trans_y < -limit)
- trans_y = -limit;
-
- // reduce thruster efficiency at high fwd speed:
- if (vmag > vlimit) {
- double vfwd = cam.vpn() * Velocity();
-
- if (trans_y > 0 && vfwd > 0 || trans_y < 0 && vfwd < 0)
- trans_y *= (float) (pow(vlimit,4) / pow(vmag,4));
- }
- }
-}
-
-void
-Ship::SetTransZ(double t)
-{
- float limit = design->trans_z;
-
- if (thruster)
- limit = (float) thruster->TransZLimit();
-
- trans_z = (float) t;
-
- if (trans_z) {
-
- if (trans_z > limit)
- trans_z = limit;
- else if (trans_z < -limit)
- trans_z = -limit;
-
- // reduce thruster efficiency at high fwd speed:
- double vfwd = cam.vup() * Velocity();
- double vmag = fabs(vfwd);
- if (vmag > vlimit) {
- if (trans_z > 0 && vfwd > 0 || trans_z < 0 && vfwd < 0)
- trans_z *= (float) (pow(vlimit,4) / pow(vmag,4));
- }
- }
-}
-
-void
-Ship::ExecFLCSFrame()
-{
- if (flcs)
- flcs->ExecSubFrame();
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::SetHelmHeading(double h)
-{
- while (h < 0)
- h += 2*PI;
-
- while (h >= 2*PI)
- h -= 2*PI;
-
- helm_heading = (float) h;
-}
-
-void
-Ship::SetHelmPitch(double p)
-{
- const double PITCH_LIMIT = 80 * DEGREES;
-
- if (p < -PITCH_LIMIT)
- p = -PITCH_LIMIT;
-
- else if (p > PITCH_LIMIT)
- p = PITCH_LIMIT;
-
- helm_pitch = (float) p;
-}
-
-void
-Ship::ApplyHelmYaw(double y)
-{
- // rotate compass into helm-relative orientation:
- double compass = CompassHeading() - helm_heading;
- double turn = y * PI/4;
-
- if (compass > PI)
- compass -= 2*PI;
- else if (compass < -PI)
- compass += 2*PI;
-
- // if requested turn is more than 170, reject it:
- if (fabs(compass + turn) > 170*DEGREES)
- return;
-
- SetHelmHeading(helm_heading + turn);
-}
-
-void
-Ship::ApplyHelmPitch(double p)
-{
- SetHelmPitch(helm_pitch - p * PI/4);
-}
-
-void
-Ship::ApplyPitch(double p)
-{
- if (flight_model == 0) { // standard flight model
- if (IsAirborne())
- p *= 0.5;
-
- // command for pitch up is negative
- if (p < 0) {
- if (alpha > PI/6) {
- p *= 0.05;
- }
- else if (g_force > 12.0) {
- double limit = 0.5 - (g_force - 12.0)/10.0;
-
- if (limit < 0)
- p = 0;
- else
- p *= limit;
- }
- }
-
- // command for pitch down is positive
- else if (p > 0) {
- if (alpha < -PI/8) {
- p *= 0.05;
- }
- else if (g_force < -3) {
- p *= 0.1;
- }
- }
- }
-
- Physical::ApplyPitch(p);
-}
-
-// +--------------------------------------------------------------------+
-
-bool
-Ship::FireWeapon(int n)
-{
- bool fired = false;
-
- if (n >= 0 && !CheckFire()) {
- if (n < 4)
- trigger[n] = true;
-
- if (n < weapons.size()) {
- weapons[n]->SetTrigger(true);
- fired = weapons[n]->GetTrigger();
- }
- }
-
- if (!fired && sim->GetPlayerShip() == this)
- Button::PlaySound(Button::SND_REJECT);
-
- return fired;
-}
-
-bool
-Ship::FireDecoy()
-{
- Shot* drone = 0;
-
- if (decoy && !CheckFire()) {
- drone = decoy->Fire();
-
- if (drone) {
- Observe(drone);
- decoy_list.append(drone);
- }
- }
-
- if (sim->GetPlayerShip() == this) {
- if (NetGame::IsNetGame()) {
- if (decoy && decoy->Ammo() < 1)
- Button::PlaySound(Button::SND_REJECT);
- }
-
- else if (!drone) {
- Button::PlaySound(Button::SND_REJECT);
- }
- }
-
- return drone != 0;
-}
-
-void
-Ship::AddActiveDecoy(Drone* drone)
-{
- if (drone) {
- Observe(drone);
- decoy_list.append(drone);
- }
-}
-
-Weapon*
-Ship::GetPrimary() const
-{
- if (weapons.size() > primary)
- return weapons[primary]->GetSelected();
- return 0;
-}
-
-Weapon*
-Ship::GetSecondary() const
-{
- if (weapons.size() > secondary)
- return weapons[secondary]->GetSelected();
- return 0;
-}
-
-Weapon*
-Ship::GetWeaponByIndex(int n)
-{
- for (int i = 0; i < weapons.size(); i++) {
- WeaponGroup* g = weapons[i];
-
- List<Weapon>& wlist = g->GetWeapons();
- for (int j = 0; j < wlist.size(); j++) {
- Weapon* w = wlist[j];
-
- if (w->GetIndex() == n) {
- return w;
- }
- }
- }
-
- return 0;
-}
-
-WeaponGroup*
-Ship::GetPrimaryGroup() const
-{
- if (weapons.size() > primary)
- return weapons[primary];
- return 0;
-}
-
-WeaponGroup*
-Ship::GetSecondaryGroup() const
-{
- if (weapons.size() > secondary)
- return weapons[secondary];
- return 0;
-}
-
-WeaponDesign*
-Ship::GetPrimaryDesign() const
-{
- if (weapons.size() > primary)
- return (WeaponDesign*) weapons[primary]->GetSelected()->Design();
- return 0;
-}
-
-WeaponDesign*
-Ship::GetSecondaryDesign() const
-{
- if (weapons.size() > secondary)
- return (WeaponDesign*) weapons[secondary]->GetSelected()->Design();
- return 0;
-}
-
-Weapon*
-Ship::GetDecoy() const
-{
- return decoy;
-}
-
-List<Shot>&
-Ship::GetActiveDecoys()
-{
- return decoy_list;
-}
-
-List<Shot>&
-Ship::GetThreatList()
-{
- return threat_list;
-}
-
-void
-Ship::AddThreat(Shot* s)
-{
- if (!threat_list.contains(s)) {
- Observe(s);
- threat_list.append(s);
- }
-}
-
-void
-Ship::DropThreat(Shot* s)
-{
- if (threat_list.contains(s)) {
- threat_list.remove(s);
- }
-}
-
-bool
-Ship::GetTrigger(int i) const
-{
- if (i >= 0) {
- if (i < 4)
- return trigger[i];
-
- else if (i < weapons.size())
- return weapons[i]->GetTrigger();
- }
-
- return false;
-}
-
-void
-Ship::SetTrigger(int i)
-{
- if (i >= 0 && !CheckFire()) {
- if (i < 4)
- trigger[i] = true;
-
- if (i < weapons.size())
- weapons[i]->SetTrigger();
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::SetSensorMode(int mode)
-{
- if (sensor)
- sensor->SetMode((Sensor::Mode) mode);
-}
-
-int
-Ship::GetSensorMode() const
-{
- if (sensor)
- return (int) sensor->GetMode();
-
- return 0;
-}
-
-// +--------------------------------------------------------------------+
-
-bool
-Ship::IsTracking(SimObject* tgt)
-{
- if (tgt && sensor)
- return sensor->IsTracking(tgt);
-
- return false;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::LockTarget(int type, bool closest, bool hostile)
-{
- if (sensor)
- SetTarget(sensor->LockTarget(type, closest, hostile));
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::LockTarget(SimObject* candidate)
-{
- if (sensor)
- SetTarget(sensor->LockTarget(candidate));
- else
- SetTarget(candidate);
-}
-
-// +--------------------------------------------------------------------+
-
-double
-Ship::InflictDamage(double damage, Shot* shot, int hit_type, Point impact)
-{
- double damage_applied = 0;
-
- if (Game::Paused() || IsNetObserver() || IsInvulnerable())
- return damage_applied;
-
- if (Integrity() == 0) // already dead?
- return damage_applied;
-
- const double MAX_SHAKE = 7;
- double hull_damage = damage;
- bool hit_shield = (hit_type & HIT_SHIELD) != 0;
- bool hit_hull = (hit_type & HIT_HULL) != 0;
- bool hit_turret = (hit_type & HIT_TURRET) != 0;
-
- if (impact == Point(0,0,0))
- impact = Location();
-
- if (hit_shield && ShieldStrength() > 0) {
- hull_damage = shield->DeflectDamage(shot, damage);
-
- if (shot) {
- if (shot->IsBeam()) {
- if (design->beam_hit_sound_resource) {
- if (Game::RealTime() - last_beam_time > 400) {
- Sound* s = design->beam_hit_sound_resource->Duplicate();
- s->SetLocation(impact);
- s->SetVolume(AudioConfig::EfxVolume());
- s->Play();
-
- last_beam_time = Game::RealTime();
- }
- }
- }
-
- else {
- if (design->bolt_hit_sound_resource) {
- if (Game::RealTime() - last_bolt_time > 400) {
- Sound* s = design->bolt_hit_sound_resource->Duplicate();
- s->SetLocation(impact);
- s->SetVolume(AudioConfig::EfxVolume());
- s->Play();
-
- last_bolt_time = Game::RealTime();
- }
- }
- }
- }
- }
-
- if (hit_hull) {
- hull_damage = InflictSystemDamage(hull_damage, shot, impact);
-
- int damage_type = WeaponDesign::DMG_NORMAL;
-
- if (shot && shot->Design())
- damage_type = shot->Design()->damage_type;
-
- if (damage_type == WeaponDesign::DMG_NORMAL) {
- damage_applied = hull_damage;
- Physical::InflictDamage(damage_applied, 0);
- NetUtil::SendObjDamage(this, damage_applied, shot);
- }
- }
-
- else if (hit_turret) {
- hull_damage = InflictSystemDamage(hull_damage, shot, impact) * 0.3;
-
- int damage_type = WeaponDesign::DMG_NORMAL;
-
- if (shot && shot->Design())
- damage_type = shot->Design()->damage_type;
-
- if (damage_type == WeaponDesign::DMG_NORMAL) {
- damage_applied = hull_damage;
- Physical::InflictDamage(damage_applied, 0);
- NetUtil::SendObjDamage(this, damage_applied, shot);
- }
- }
-
- // shake by percentage of maximum damage
- double newshake = 50 * damage/design->integrity;
-
- if (shake < MAX_SHAKE) shake += (float) newshake;
- if (shake > MAX_SHAKE) shake = (float) MAX_SHAKE;
-
- // start fires as needed:
- if ((IsStarship() || IsGroundUnit() || RandomChance(1,3)) && hit_hull && damage_applied > 0) {
- int old_integrity = (int) ((integrity + damage_applied)/design->integrity * 10);
- int new_integrity = (int) ((integrity )/design->integrity * 10);
-
- if (new_integrity < 5 && new_integrity < old_integrity) {
- // need accurate hull impact for starships,
- if (rep) {
- Point detonation = impact*2 - Location();
- Point direction = Location() - detonation;
- double distance = direction.Normalize() * 3;
- rep->CheckRayIntersection(detonation, direction, distance, impact);
-
- // pull fire back into hull a bit:
- direction = Location() - impact;
- impact += direction * 0.2;
-
- float scale = (float) design->scale;
-
- if (IsDropship())
- sim->CreateExplosion(impact, Velocity(), Explosion::SMOKE_TRAIL, 0.01f * scale, 0.5f * scale, region, this);
- else
- sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FIRE, 0.10f * scale, scale, region, this);
- }
- }
- }
-
- return damage_applied;
-}
-
-double
-Ship::InflictSystemDamage(double damage, Shot* shot, Point impact)
-{
- if (IsNetObserver())
- return 0;
-
- // find the system that is closest to the impact point:
- System* system = 0;
- double distance = 1e6;
- double blast_radius = 0;
- int dmg_type = 0;
-
- if (shot)
- dmg_type = shot->Design()->damage_type;
-
- bool dmg_normal = dmg_type == WeaponDesign::DMG_NORMAL;
- bool dmg_power = dmg_type == WeaponDesign::DMG_POWER;
- bool dmg_emp = dmg_type == WeaponDesign::DMG_EMP;
- double to_level = 0;
-
- if (dmg_power) {
- to_level = 1 - damage / 1e4;
-
- if (to_level < 0)
- to_level = 0;
- }
-
- // damage caused by weapons applies to closest system:
- if (shot) {
- if (shot->IsMissile())
- blast_radius = 300;
-
- ListIter<System> iter = systems;
- while (++iter) {
- System* candidate = iter.value();
- double sysrad = candidate->Radius();
-
- if (dmg_power)
- candidate->DrainPower(to_level);
-
- if (sysrad > 0 || dmg_emp && candidate->IsPowerCritical()) {
- double test_distance = (impact - candidate->MountLocation()).length();
-
- if ((test_distance-blast_radius) < sysrad || dmg_emp && candidate->IsPowerCritical()) {
- if (test_distance < distance) {
- system = candidate;
- distance = test_distance;
- }
- }
- }
- }
-
- // if a system was in range of the blast, assess the damage:
- if (system) {
- double hull_damage = damage * system->HullProtection();
- double sys_damage = damage - hull_damage;
- double avail = system->Availability();
-
- if (dmg_normal || system->IsPowerCritical() && dmg_emp) {
- system->ApplyDamage(sys_damage);
- NetUtil::SendSysDamage(this, system, sys_damage);
-
- master_caution = true;
-
- if (dmg_normal) {
- if (sys_damage < 100)
- damage -= sys_damage;
- else
- damage -= 100;
- }
-
- if (system->GetExplosionType() && (avail - system->Availability()) >= 50) {
- float scale = design->explosion_scale;
- if (scale <= 0)
- scale = design->scale;
-
- sim->CreateExplosion(system->MountLocation(), Velocity() * 0.7f, system->GetExplosionType(), 0.2f * scale, scale, region, this, system);
- }
- }
- }
- }
-
- // damage caused by collision applies to all systems:
- else {
- // ignore incidental bumps:
- if (damage < 100)
- return damage;
-
- ListIter<System> iter = systems;
- while (++iter) {
- System* sys = iter.value();
-
- if (rand() > 24000) {
- double base_damage = 33.0 + rand()/1000.0;
- double sys_damage = base_damage * (1.0 - sys->HullProtection());
- sys->ApplyDamage(sys_damage);
- NetUtil::SendSysDamage(this, system, sys_damage);
- damage -= sys_damage;
-
- master_caution = true;
- }
- }
-
- // just in case this ship has lots of systems...
- if (damage < 0)
- damage = 0;
- }
-
- // return damage remaining
- return damage;
-}
-
-// +--------------------------------------------------------------------+
-
-int
-Ship::ShieldStrength() const
-{
- if (!shield) return 0;
-
- return (int) shield->ShieldLevel();
-}
-
-int
-Ship::HullStrength() const
-{
- if (design)
- return (int) (Integrity() / design->integrity * 100);
-
- return 10;
-}
-
-// +--------------------------------------------------------------------+
-
-System*
-Ship::GetSystem(int sys_id)
-{
- System* s = 0;
-
- if (sys_id >= 0) {
- if (sys_id < systems.size()) {
- s = systems[sys_id];
- if (s->GetID() == sys_id)
- return s;
- }
-
- ListIter<System> iter = systems;
- while (++iter) {
- s = iter.value();
-
- if (s->GetID() == sys_id)
- return s;
- }
- }
-
- return 0;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::RepairSystem(System* sys)
-{
- if (!repair_queue.contains(sys)) {
- repair_queue.append(sys);
- sys->Repair();
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::IncreaseRepairPriority(int task_index)
-{
- if (task_index > 0 && task_index < repair_queue.size()) {
- System* task1 = repair_queue.at(task_index-1);
- System* task2 = repair_queue.at(task_index);
-
- repair_queue.at(task_index-1) = task2;
- repair_queue.at(task_index) = task1;
- }
-}
-
-void
-Ship::DecreaseRepairPriority(int task_index)
-{
- if (task_index >= 0 && task_index < repair_queue.size()-1) {
- System* task1 = repair_queue.at(task_index);
- System* task2 = repair_queue.at(task_index+1);
-
- repair_queue.at(task_index) = task2;
- repair_queue.at(task_index+1) = task1;
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::ExecMaintFrame(double seconds)
-{
- // is it already too late?
- if (life == 0 || integrity < 1) return;
-
- const DWORD REPAIR_FREQUENCY = 5000; // once every five seconds
- static DWORD last_repair_frame = 0; // one ship per game frame
-
- if (auto_repair &&
- Game::GameTime() - last_repair_time > REPAIR_FREQUENCY &&
- last_repair_frame != Game::Frame()) {
-
- last_repair_time = Game::GameTime();
- last_repair_frame = Game::Frame();
-
- ListIter<System> iter = systems;
- while (++iter) {
- System* sys = iter.value();
-
- if (sys->Status() != System::NOMINAL) {
- bool started_repairs = false;
-
- // emergency power routing:
- if (sys->Type() == System::POWER_SOURCE && sys->Availability() < 33) {
- PowerSource* src = (PowerSource*) sys;
- PowerSource* dst = 0;
-
- for (int i = 0; i < reactors.size(); i++) {
- PowerSource* pwr = reactors[i];
-
- if (pwr != src && pwr->Availability() > src->Availability()) {
- if (!dst ||
- (pwr->Availability() > dst->Availability() &&
- pwr->Charge() > dst->Charge()))
- dst = pwr;
- }
- }
-
- if (dst) {
- while (src->Clients().size() > 0) {
- System* s = src->Clients().at(0);
- src->RemoveClient(s);
- dst->AddClient(s);
- }
- }
- }
-
- ListIter<Component> comp = sys->GetComponents();
- while (++comp) {
- Component* c = comp.value();
-
- if (c->Status() < Component::NOMINAL && c->Availability() < 75) {
- if (c->SpareCount() &&
- c->ReplaceTime() <= 300 &&
- (c->Availability() < 50 ||
- c->ReplaceTime() < c->RepairTime())) {
-
- c->Replace();
- started_repairs = true;
- }
-
- else if (c->Availability() >= 50 || c->NumJerried() < 5) {
- c->Repair();
- started_repairs = true;
- }
- }
- }
-
- if (started_repairs)
- RepairSystem(sys);
- }
- }
- }
-
- if (repair_queue.size() > 0 && RepairTeams() > 0) {
- int team = 0;
- ListIter<System> iter = repair_queue;
- while (++iter && team < RepairTeams()) {
- System* sys = iter.value();
-
- sys->ExecMaintFrame(seconds * RepairSpeed());
- team++;
-
- if (sys->Status() != System::MAINT) {
- iter.removeItem();
-
- // emergency power routing (restore):
- if (sys->Type() == System::POWER_SOURCE &&
- sys->Status() == System::NOMINAL) {
- PowerSource* src = (PowerSource*) sys;
- int isrc = reactors.index(src);
-
- for (int i = 0; i < reactors.size(); i++) {
- PowerSource* pwr = reactors[i];
-
- if (pwr != src) {
- List<System> xfer;
-
- for (int j = 0; j < pwr->Clients().size(); j++) {
- System* s = pwr->Clients().at(j);
-
- if (s->GetSourceIndex() == isrc) {
- xfer.append(s);
- }
- }
-
- for (int j = 0; j < xfer.size(); j++) {
- System* s = xfer.at(j);
- pwr->RemoveClient(s);
- src->AddClient(s);
- }
- }
- }
- }
- }
- }
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::SetNetworkControl(Director* net)
-{
- net_control = net;
-
- delete dir;
- dir = 0;
-
- if (!net_control && GetIFF() < 100) {
- if (IsStatic())
- dir = 0;
- else if (IsStarship())
- dir = SteerAI::Create(this, SteerAI::STARSHIP);
- else
- dir = SteerAI::Create(this, SteerAI::FIGHTER);
- }
-}
-
-void
-Ship::SetControls(MotionController* m)
-{
- if (IsDropping() || IsAttaining()) {
- if (dir && dir->Type() != DropShipAI::DIR_TYPE) {
- delete dir;
- dir = new(__FILE__,__LINE__) DropShipAI(this);
- }
-
- return;
- }
-
- else if (IsSkipping()) {
- if (navsys && sim->GetPlayerShip() == this)
- navsys->EngageAutoNav();
- }
-
- else if (IsDying() || IsDead()) {
- if (dir) {
- delete dir;
- dir = 0;
- }
-
- if (navsys && navsys->AutoNavEngaged()) {
- navsys->DisengageAutoNav();
- }
-
- return;
- }
-
- else if (life == 0) {
- if (dir || navsys) {
- ::Print("Warning: dying ship '%' still has not been destroyed!\n", name);
- delete dir;
- dir = 0;
-
- if (navsys && navsys->AutoNavEngaged())
- navsys->DisengageAutoNav();
- }
-
- return;
- }
-
- if (navsys && navsys->AutoNavEngaged()) {
- NavAI* nav = 0;
-
- if (dir) {
- if (dir->Type() != NavAI::DIR_TYPE) {
- delete dir;
- dir = 0;
- }
- else {
- nav = (NavAI*) dir;
- }
- }
-
- if (!nav) {
- nav = new(__FILE__,__LINE__) NavAI(this);
- dir = nav;
- return;
- }
-
- else if (!nav->Complete()) {
- return;
- }
- }
-
- if (dir) {
- delete dir;
- dir = 0;
- }
-
- if (m) {
- Keyboard::FlushKeys();
- m->Acquire();
- dir = new(__FILE__,__LINE__) ShipCtrl(this, m);
- director_info = Game::GetText("flcs.auto");
- }
- else if (GetIFF() < 100) {
- if (IsStatic())
- dir = SteerAI::Create(this, SteerAI::GROUND);
-
- else if (IsStarship() && !IsAirborne())
- dir = SteerAI::Create(this, SteerAI::STARSHIP);
-
- else
- dir = SteerAI::Create(this, SteerAI::FIGHTER);
- }
-}
-
-// +--------------------------------------------------------------------+
-
-Color
-Ship::IFFColor(int iff)
-{
- Color c;
-
- switch (iff) {
- case 0: // NEUTRAL, NON-COMBAT
- c = Color(192,192,192);
- break;
-
- case 1: // TERELLIAN ALLIANCE
- c = Color(70,70,220);
- break;
-
- case 2: // MARAKAN HEGEMONY
- c = Color(220,20,20);
- break;
-
- case 3: // BROTHERHOOD OF IRON
- c = Color(200,180,20);
- break;
-
- case 4: // ZOLON EMPIRE
- c = Color(20,200,20);
- break;
-
- case 5:
- c = Color(128, 0, 128);
- break;
-
- case 6:
- c = Color(40,192,192);
- break;
-
- default:
- c = Color(128,128,128);
- break;
- }
-
- return c;
-}
-
-Color
-Ship::MarkerColor() const
-{
- return IFFColor(IFF_code);
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::SetIFF(int iff)
-{
- IFF_code = iff;
-
- if (hangar)
- hangar->SetAllIFF(iff);
-
- DropTarget();
-
- if (dir && dir->Type() >= 1000) {
- SteerAI* ai = (SteerAI*) dir;
- ai->DropTarget();
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::SetRogue(bool r)
-{
- bool rogue = IsRogue();
-
- ff_count = r ? 1000 : 0;
-
- if (!rogue && IsRogue()) {
- Print("Ship '%s' has been made rogue\n", Name());
- }
- else if (rogue && !IsRogue()) {
- Print("Ship '%s' is no longer rogue\n", Name());
- }
-}
-
-void
-Ship::SetFriendlyFire(int f)
-{
- bool rogue = IsRogue();
-
- ff_count = f;
-
- if (!rogue && IsRogue()) {
- Print("Ship '%s' has been made rogue with ff_count = %d\n", Name(), ff_count);
- }
- else if (rogue && !IsRogue()) {
- Print("Ship '%s' is no longer rogue\n", Name());
- }
-}
-
-void
-Ship::IncFriendlyFire(int f)
-{
- if (f > 0) {
- bool rogue = IsRogue();
-
- ff_count += f;
-
- if (!rogue && IsRogue()) {
- Print("Ship '%s' has been made rogue with ff_count = %d\n", Name(), ff_count);
- }
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-Ship::SetEMCON(int e, bool from_net)
-{
- if (e < 1) emcon = 1;
- else if (e > 3) emcon = 3;
- else emcon = (BYTE) e;
-
- if (emcon != old_emcon && !from_net && NetGame::GetInstance())
- NetUtil::SendObjEmcon(this);
-}
-
-double
-Ship::PCS() const
-{
- double e_factor = design->e_factor[emcon-1];
-
- if (IsAirborne() && !IsGroundUnit()) {
- if (AltitudeAGL() < 40)
- return 0;
-
- if (AltitudeAGL() < 200) {
- double clutter = AltitudeAGL() / 200;
- return clutter * e_factor;
- }
- }
-
- return e_factor * pcs;
-}
-
-double
-Ship::ACS() const
-{
- if (IsAirborne() && !IsGroundUnit()) {
- if (AltitudeAGL() < 40)
- return 0;
-
- if (AltitudeAGL() < 200) {
- double clutter = AltitudeAGL() / 200;
- return clutter * acs;
- }
- }
-
- return acs;
-}
-
-DWORD
-Ship::MissionClock() const
-{
- if (launch_time > 0)
- return Game::GameTime() + 1 - launch_time;
-
- return 0;
-}
-
-// +----------------------------------------------------------------------+
-
-Instruction*
-Ship::GetRadioOrders() const
-{
- return radio_orders;
-}
-
-void
-Ship::ClearRadioOrders()
-{
- if (radio_orders) {
- radio_orders->SetAction(0);
- radio_orders->ClearTarget();
- radio_orders->SetLocation(Point());
- }
-}
-
-void
-Ship::HandleRadioMessage(RadioMessage* msg)
-{
- if (!msg) return;
-
- static RadioHandler rh;
-
- if (rh.ProcessMessage(msg, this))
- rh.AcknowledgeMessage(msg, this);
-}
-
-// +----------------------------------------------------------------------+
-
-int
-Ship::Value() const
-{
- return Value(design->type);
-}
-
-// +----------------------------------------------------------------------+
-
-int
-Ship::Value(int type)
-{
- int value = 0;
-
- switch (type) {
- case DRONE: value = 10; break;
- case FIGHTER: value = 20; break;
- case ATTACK: value = 40; break;
- case LCA: value = 50; break;
-
- case COURIER: value = 100; break;
- case CARGO: value = 100; break;
- case CORVETTE: value = 100; break;
- case FREIGHTER: value = 250; break;
- case FRIGATE: value = 200; break;
- case DESTROYER: value = 500; break;
- case CRUISER: value = 800; break;
- case BATTLESHIP: value = 1000; break;
- case CARRIER: value = 1500; break;
- case SWACS: value = 500; break;
- case DREADNAUGHT: value = 1500; break;
-
- case STATION: value = 2500; break;
- case FARCASTER: value = 5000; break;
-
- case MINE: value = 20; break;
- case COMSAT: value = 200; break;
- case DEFSAT: value = 300; break;
-
- case BUILDING: value = 100; break;
- case FACTORY: value = 250; break;
- case SAM: value = 100; break;
- case EWR: value = 200; break;
- case C3I: value = 500; break;
- case STARBASE: value = 2000; break;
-
- default: value = 100; break;
- }
-
- return value;
-}
-
-// +----------------------------------------------------------------------+
-
-double
-Ship::AIValue() const
-{
- int i = 0;
- double value = 0;
-
- for (i = 0; i < reactors.size(); i++) {
- const PowerSource* r = reactors[i];
- value += r->Value();
- }
-
- for (i = 0; i < drives.size(); i++) {
- const Drive* d = drives[i];
- value += d->Value();
- }
-
- for (i = 0; i < weapons.size(); i++) {
- const WeaponGroup* w = weapons[i];
- value += w->Value();
- }
-
- return value;
-}
+/* 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: Ship.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Starship class +*/ + +#include "MemDebug.h" +#include "Ship.h" +#include "ShipAI.h" +#include "ShipCtrl.h" +#include "ShipDesign.h" +#include "ShipKiller.h" +#include "Shot.h" +#include "Drone.h" +#include "SeekerAI.h" +#include "HardPoint.h" +#include "Weapon.h" +#include "WeaponGroup.h" +#include "Shield.h" +#include "ShieldRep.h" +#include "Computer.h" +#include "FlightComp.h" +#include "Drive.h" +#include "QuantumDrive.h" +#include "Farcaster.h" +#include "Thruster.h" +#include "Power.h" +#include "FlightDeck.h" +#include "LandingGear.h" +#include "Hangar.h." +#include "Sensor.h" +#include "Contact.h" +#include "CombatUnit.h" +#include "Element.h" +#include "Instruction.h" +#include "RadioMessage.h" +#include "RadioHandler.h" +#include "RadioTraffic.h" +#include "NavLight.h" +#include "NavSystem.h" +#include "NavAI.h" +#include "DropShipAI.h" +#include "Explosion.h" +#include "MissionEvent.h" +#include "ShipSolid.h" +#include "Sim.h" +#include "SimEvent.h" +#include "StarSystem.h" +#include "TerrainRegion.h" +#include "Terrain.h" +#include "System.h" +#include "Component.h" +#include "KeyMap.h" +#include "RadioView.h" +#include "AudioConfig.h" +#include "CameraDirector.h" +#include "HUDView.h" +#include "Random.h" +#include "RadioVox.h" + +#include "NetGame.h" +#include "NetUtil.h" + +#include "MotionController.h" +#include "Keyboard.h" +#include "Joystick.h" +#include "Bolt.h" +#include "Game.h" +#include "Solid.h" +#include "Shadow.h" +#include "Skin.h" +#include "Sprite.h" +#include "Light.h" +#include "Bitmap.h" +#include "Button.h" +#include "Sound.h" +#include "DataLoader.h" + +#include "Parser.h" +#include "Reader.h" + +// +----------------------------------------------------------------------+ + +static int base_contact_id = 0; +static double range_min = 0; +static double range_max = 250e3; + +int Ship::control_model = 0; // standard +int Ship::flight_model = 0; // standard +int Ship::landing_model = 0; // standard +double Ship::friendly_fire_level = 1; // 100% + +const int HIT_NOTHING = 0; +const int HIT_HULL = 1; +const int HIT_SHIELD = 2; +const int HIT_BOTH = 3; +const int HIT_TURRET = 4; + +// +----------------------------------------------------------------------+ + +Ship::Ship(const char* ship_name, const char* reg_num, ShipDesign* ship_dsn, int IFF, int cmd_ai, const int* load) + : IFF_code(IFF), killer(0), throttle(0), augmenter(false), throttle_request(0), + shield(0), shieldRep(0), main_drive(0), quantum_drive(0), farcaster(0), + check_fire(false), probe(0), sensor_drone(0), primary(0), secondary(1), + cmd_chain_index(0), target(0), subtarget(0), radio_orders(0), launch_point(0), + g_force(0.0f), sensor(0), navsys(0), flcs(0), hangar(0), respawns(0), invulnerable(false), + thruster(0), decoy(0), ai_mode(2), command_ai_level(cmd_ai), flcs_mode(FLCS_AUTO), loadout(0), + emcon(3), old_emcon(3), master_caution(false), cockpit(0), gear(0), skin(0), + auto_repair(true), last_repair_time(0), last_eval_time(0), last_beam_time(0), last_bolt_time(0), + warp_fov(1), flight_phase(LAUNCH), launch_time(0), carrier(0), dock(0), ff_count(0), + inbound(0), element(0), director_info("Init"), combat_unit(0), net_control(0), + track(0), ntrack(0), track_time(0), helm_heading(0.0f), helm_pitch(0.0f), + altitude_agl(-1.0e6f), transition_time(0.0f), transition_type(TRANSITION_NONE), + friendly_fire_time(0), ward(0), net_observer_mode(false), orig_elem_index(-1) +{ + sim = Sim::GetSim(); + + strcpy_s(name, ship_name); + if (reg_num && *reg_num) + strcpy_s(regnum, reg_num); + else regnum[0] = 0; + + design = ship_dsn; + + if (!design) { + char msg[256]; + sprintf_s(msg, "No ship design found for '%s'\n", ship_name); + Game::Panic(msg); + } + + obj_type = SimObject::SIM_SHIP; + + radius = design->radius; + mass = design->mass; + integrity = design->integrity; + vlimit = design->vlimit; + + agility = design->agility; + wep_mass = 0.0f; + wep_resist = 0.0f; + + CL = design->CL; + CD = design->CD; + stall = design->stall; + + chase_vec = design->chase_vec; + bridge_vec = design->bridge_vec; + + acs = design->acs; + pcs = design->acs; + + auto_repair = design->repair_auto; + + while (!base_contact_id) + base_contact_id = rand() % 1000; + + contact_id = base_contact_id++; + int sys_id = 0; + + for (int i = 0; i < design->reactors.size(); i++) { + PowerSource* reactor = new(__FILE__,__LINE__) PowerSource(*design->reactors[i]); + reactor->SetShip(this); + reactor->SetID(sys_id++); + reactors.append(reactor); + systems.append(reactor); + } + + for (int i = 0; i < design->drives.size(); i++) { + Drive* drive = new(__FILE__,__LINE__) Drive(*design->drives[i]); + drive->SetShip(this); + drive->SetID(sys_id++); + + int src_index = drive->GetSourceIndex(); + if (src_index >= 0 && src_index < reactors.size()) + reactors[src_index]->AddClient(drive); + + drives.append(drive); + systems.append(drive); + } + + if (design->quantum_drive) { + quantum_drive = new(__FILE__,__LINE__) QuantumDrive(*design->quantum_drive); + quantum_drive->SetShip(this); + quantum_drive->SetID(sys_id++); + + int src_index = quantum_drive->GetSourceIndex(); + if (src_index >= 0 && src_index < reactors.size()) + reactors[src_index]->AddClient(quantum_drive); + + quantum_drive->SetShip(this); + systems.append(quantum_drive); + } + + if (design->farcaster) { + farcaster = new(__FILE__,__LINE__) Farcaster(*design->farcaster); + farcaster->SetShip(this); + farcaster->SetID(sys_id++); + + int src_index = farcaster->GetSourceIndex(); + if (src_index >= 0 && src_index < reactors.size()) + reactors[src_index]->AddClient(farcaster); + + farcaster->SetShip(this); + systems.append(farcaster); + } + + if (design->thruster) { + thruster = new(__FILE__,__LINE__) Thruster(*design->thruster); + thruster->SetShip(this); + thruster->SetID(sys_id++); + + int src_index = thruster->GetSourceIndex(); + if (src_index >= 0 && src_index < reactors.size()) + reactors[src_index]->AddClient(thruster); + + thruster->SetShip(this); + systems.append(thruster); + } + + if (design->shield) { + shield = new(__FILE__,__LINE__) Shield(*design->shield); + shield->SetShip(this); + shield->SetID(sys_id++); + + int src_index = shield->GetSourceIndex(); + if (src_index >= 0 && src_index < reactors.size()) + reactors[src_index]->AddClient(shield); + + if (design->shield_model) { + shieldRep = new(__FILE__,__LINE__) ShieldRep; + shieldRep->UseModel(design->shield_model); + } + + systems.append(shield); + } + + for (int i = 0; i < design->flight_decks.size(); i++) { + FlightDeck* deck = new(__FILE__,__LINE__) FlightDeck(*design->flight_decks[i]); + deck->SetShip(this); + deck->SetCarrier(this); + deck->SetID(sys_id++); + deck->SetIndex(i); + + int src_index = deck->GetSourceIndex(); + if (src_index >= 0 && src_index < reactors.size()) + reactors[src_index]->AddClient(deck); + + flight_decks.append(deck); + systems.append(deck); + } + + if (design->flight_decks.size() > 0) { + if (!hangar) { + hangar = new(__FILE__,__LINE__) Hangar; + hangar->SetShip(this); + } + } + + if (design->squadrons.size() > 0) { + if (!hangar) { + hangar = new(__FILE__,__LINE__) Hangar; + hangar->SetShip(this); + } + + for (int i = 0; i < design->squadrons.size(); i++) { + ShipSquadron* s = design->squadrons[i]; + hangar->CreateSquadron(s->name, 0, s->design, s->count, GetIFF(), 0, 0, s->avail); + } + } + + if (design->gear) { + gear = new(__FILE__,__LINE__) LandingGear(*design->gear); + gear->SetShip(this); + gear->SetID(sys_id++); + + int src_index = gear->GetSourceIndex(); + if (src_index >= 0 && src_index < reactors.size()) + reactors[src_index]->AddClient(gear); + + systems.append(gear); + } + + if (design->sensor) { + sensor = new(__FILE__,__LINE__) Sensor(*design->sensor); + sensor->SetShip(this); + sensor->SetID(sys_id++); + + int src_index = sensor->GetSourceIndex(); + if (src_index >= 0 && src_index < reactors.size()) + reactors[src_index]->AddClient(sensor); + + if (IsStarship() || IsStatic() || !strncmp(design->name, "Camera", 6)) + sensor->SetMode(Sensor::CST); + + systems.append(sensor); + } + + int wep_index = 1; + + for (int i = 0; i < design->weapons.size(); i++) { + Weapon* gun = new(__FILE__,__LINE__) Weapon(*design->weapons[i]); + gun->SetID(sys_id++); + gun->SetOwner(this); + gun->SetIndex(wep_index++); + + int src_index = gun->GetSourceIndex(); + if (src_index >= 0 && src_index < reactors.size()) + reactors[src_index]->AddClient(gun); + + WeaponGroup* group = FindWeaponGroup(gun->Group()); + group->AddWeapon(gun); + group->SetAbbreviation(gun->Abbreviation()); + + systems.append(gun); + + if (IsDropship() && gun->GetTurret()) + gun->SetFiringOrders(Weapon::POINT_DEFENSE); + else + gun->SetFiringOrders(Weapon::MANUAL); + } + + int loadout_size = design->hard_points.size(); + + if (load && loadout_size > 0) { + loadout = new(__FILE__,__LINE__) int[loadout_size]; + + for (int i = 0; i < loadout_size; i++) { + int mounted_weapon = loadout[i] = load[i]; + + if (mounted_weapon < 0) + continue; + + Weapon* missile = design->hard_points[i]->CreateWeapon(mounted_weapon); + + if (missile) { + missile->SetID(sys_id++); + missile->SetOwner(this); + missile->SetIndex(wep_index++); + + WeaponGroup* group = FindWeaponGroup(missile->Group()); + group->AddWeapon(missile); + group->SetAbbreviation(missile->Abbreviation()); + + systems.append(missile); + } + } + } + + if (weapons.size() > 1) { + primary = -1; + secondary = -1; + + for (int i = 0; i < weapons.size(); i++) { + WeaponGroup* group = weapons[i]; + if (group->IsPrimary() && primary < 0) { + primary = i; + + // turrets on fighters are set to point defense by default, + // this forces the primary turret back to manual control + group->SetFiringOrders(Weapon::MANUAL); + } + + else if (group->IsMissile() && secondary < 0) { + secondary = i; + } + } + + if (primary < 0) primary = 0; + if (secondary < 0) secondary = 1; + + if (weapons.size() > 4) { + ::Print("WARNING: Ship '%s' type '%s' has %d wep groups (max=4)\n", + Name(), DesignName(), weapons.size()); + } + } + + if (design->decoy) { + decoy = new(__FILE__,__LINE__) Weapon(*design->decoy); + decoy->SetOwner(this); + decoy->SetID(sys_id++); + decoy->SetIndex(wep_index++); + + int src_index = decoy->GetSourceIndex(); + if (src_index >= 0 && src_index < reactors.size()) + reactors[src_index]->AddClient(decoy); + + systems.append(decoy); + } + + for (int i = 0; i < design->navlights.size(); i++) { + NavLight* navlight = new(__FILE__,__LINE__) NavLight(*design->navlights[i]); + navlight->SetShip(this); + navlight->SetID(sys_id++); + navlight->SetOffset(((DWORD) this) << 2); + navlights.append(navlight); + systems.append(navlight); + } + + if (design->navsys) { + navsys = new(__FILE__,__LINE__) NavSystem(*design->navsys); + navsys->SetShip(this); + navsys->SetID(sys_id++); + + int src_index = navsys->GetSourceIndex(); + if (src_index >= 0 && src_index < reactors.size()) + reactors[src_index]->AddClient(navsys); + + systems.append(navsys); + } + + if (design->probe) { + probe = new(__FILE__,__LINE__) Weapon(*design->probe); + probe->SetOwner(this); + probe->SetID(sys_id++); + probe->SetIndex(wep_index++); + + int src_index = probe->GetSourceIndex(); + if (src_index >= 0 && src_index < reactors.size()) + reactors[src_index]->AddClient(probe); + + systems.append(probe); + } + + for (int i = 0; i < design->computers.size(); i++) { + Computer* comp = 0; + + if (design->computers[i]->Subtype() == Computer::FLIGHT) { + flcs = new(__FILE__,__LINE__) FlightComp(*design->computers[i]); + + flcs->SetShip(this); + flcs->SetMode(flcs_mode); + flcs->SetVelocityLimit(vlimit); + + if (thruster) + flcs->SetTransLimit(thruster->TransXLimit(), + thruster->TransYLimit(), + thruster->TransZLimit()); + else + flcs->SetTransLimit(design->trans_x, + design->trans_y, + design->trans_z); + + comp = flcs; + } + else { + comp = new(__FILE__,__LINE__) Computer(*design->computers[i]); + } + + comp->SetShip(this); + comp->SetID(sys_id++); + int src_index = comp->GetSourceIndex(); + if (src_index >= 0 && src_index < reactors.size()) + reactors[src_index]->AddClient(comp); + + computers.append(comp); + systems.append(comp); + } + + radio_orders = new(__FILE__,__LINE__) Instruction("", Point(0,0,0)); + + // Load Detail Set: + for (int i = 0; i < DetailSet::MAX_DETAIL; i++) { + if (design->models[i].size() > 0) { + Solid* solid = new(__FILE__,__LINE__) ShipSolid(this); + solid->UseModel(design->models[i].at(0)); + solid->CreateShadows(1); + + Point* offset = 0; + Point* spin = 0; + + if (design->offsets[i].size() > 0) + offset = new(__FILE__,__LINE__) Point(*design->offsets[i].at(0)); + + if (design->spin_rates.size() > 0) + spin = new(__FILE__,__LINE__) Point(*design->spin_rates.at(0)); + + detail_level = detail.DefineLevel(design->feature_size[i], solid, offset, spin); + } + + if (design->models[i].size() > 1) { + for (int n = 1; n < design->models[i].size(); n++) { + Solid* solid = new(__FILE__,__LINE__) ShipSolid(this); //Solid; + solid->UseModel(design->models[i].at(n)); + solid->CreateShadows(1); + + Point* offset = 0; + Point* spin = 0; + + if (design->offsets[i].size() > n) + offset = new(__FILE__,__LINE__) Point(*design->offsets[i].at(n)); + + if (design->spin_rates.size() > n) + spin = new(__FILE__,__LINE__) Point(*design->spin_rates.at(n)); + + detail.AddToLevel(detail_level, solid, offset, spin); + } + } + } + + // start with lowest available detail: + detail_level = 0; // this is highest -> detail.NumLevels()-1); + rep = detail.GetRep(detail_level); + + if (design->cockpit_model) { + cockpit = new(__FILE__,__LINE__) Solid; + cockpit->UseModel(design->cockpit_model); + cockpit->SetForeground(true); + } + + if (design->main_drive >= 0 && design->main_drive < drives.size()) + main_drive = drives[design->main_drive]; + + // only use light from drives: + light = 0; + + // setup starship helm stuff: + if (IsStarship()) { + flcs_mode = FLCS_HELM; + } + + // initialize the AI: + dir = 0; + SetControls(0); + + for (int i = 0; i < 4; i++) { + missile_id[i] = 0; + missile_eta[i] = 0; + trigger[i] = false; + } +} + +// +--------------------------------------------------------------------+ + +Ship::~Ship() +{ + // the loadout can not be cleared during Destroy, because it + // is needed after Destroy to create the re-spawned ship + + delete [] loadout; + loadout = 0; + + Destroy(); +} + +// +--------------------------------------------------------------------+ + +void +Ship::Destroy() +{ + // destroy fighters on deck: + ListIter<FlightDeck> deck = flight_decks; + while (++deck) { + for (int i = 0; i < deck->NumSlots(); i++) { + Ship* s = deck->GetShip(i); + + if (s && !s->IsDying() && !s->IsDead()) { + if (sim && sim->IsActive()) { + s->DeathSpiral(); + } + else { + s->transition_type = TRANSITION_DEAD; + s->Destroy(); + } + } + } + } + + if (element) { + // mission ending for this ship, evaluate objectives one last time: + for (int i = 0; i < element->NumObjectives(); i++) { + Instruction* obj = element->GetObjective(i); + + if (obj->Status() <= Instruction::ACTIVE) { + obj->Evaluate(this); + } + } + + combat_unit = element->GetCombatUnit(); + SetElement(0); + } + + delete [] track; + track = 0; + + delete shield; + shield = 0; + delete sensor; + sensor = 0; + delete navsys; + navsys = 0; + delete thruster; + thruster = 0; + delete farcaster; + farcaster = 0; + delete quantum_drive; + quantum_drive = 0; + delete decoy; + decoy = 0; + delete probe; + probe = 0; + delete gear; + gear = 0; + + main_drive = 0; + flcs = 0; + + // repair queue does not own the systems under repair: + repair_queue.clear(); + + navlights.destroy(); + flight_decks.destroy(); + computers.destroy(); + weapons.destroy(); + drives.destroy(); + reactors.destroy(); + + // this is now a list of dangling pointers: + systems.clear(); + + delete hangar; + hangar = 0; + + // this also destroys the rep: + detail.Destroy(); + rep = 0; + + GRAPHIC_DESTROY(cockpit); + GRAPHIC_DESTROY(shieldRep); + LIGHT_DESTROY(light); + + delete launch_point; + launch_point = 0; + + delete radio_orders; + radio_orders = 0; + + delete dir; + dir = 0; + + delete killer; + killer = 0; + + // inbound slot is deleted by flight deck: + inbound = 0; + + life = 0; + Notify(); +} + +// +--------------------------------------------------------------------+ + +void +Ship::Initialize() +{ + ShipDesign::Initialize(); + Thruster::Initialize(); +} + +// +--------------------------------------------------------------------+ + +void +Ship::Close() +{ + ShipDesign::Close(); + Thruster::Close(); +} + +void +Ship::SetupAgility() +{ + const float ROLL_SPEED = (float)(PI * 0.1500); + const float PITCH_SPEED = (float)(PI * 0.0250); + const float YAW_SPEED = (float)(PI * 0.0250); + + drag = design->drag; + dr_drg = design->roll_drag; + dp_drg = design->pitch_drag; + dy_drg = design->yaw_drag; + + if (IsDying()) { + drag = 0.0f; + dr_drg *= 0.25f; + dp_drg *= 0.25f; + dy_drg *= 0.25f; + } + + if (flight_model > 0) { + drag = design->arcade_drag; + thrust *= 10.0f; + } + + float yaw_air_factor = 1.0f; + + if (IsAirborne()) { + bool grounded = AltitudeAGL() < Radius()/2; + + if (flight_model > 0) { + drag *= 2.0f; + + if (gear && gear->GetState() != LandingGear::GEAR_UP) + drag *= 2.0f; + + if (grounded) + drag *= 3.0f; + } + + else { + if (Class() != LCA) + yaw_air_factor = 0.3f; + + double rho = GetDensity(); + double speed = Velocity().length(); + + agility = design->air_factor * rho * speed - wep_resist; + + if (grounded && agility < 0) + agility = 0; + + else if (!grounded && agility < 0.5 * design->agility) + agility = 0.5 * design->agility; + + else if (agility > 2 * design->agility) + agility = 2 * design->agility; + + // undercarriage aerodynamic drag + if (gear && gear->GetState() != LandingGear::GEAR_UP) + drag *= 5.0f; + + // wheel rolling friction + if (grounded) + drag *= 10.0f; + + // dead engine drag ;-) + if (thrust < 10) + drag *= 5.0f; + } + } + + else { + agility = design->agility - wep_resist; + + if (agility < 0.5 * design->agility) + agility = 0.5 * design->agility; + + if (flight_model == 0) + drag = 0.0f; + } + + float rr = (float) (design->roll_rate * PI / 180); + float pr = (float) (design->pitch_rate * PI / 180); + float yr = (float) (design->yaw_rate * PI / 180); + + if (rr == 0) rr = (float) agility * ROLL_SPEED; + if (pr == 0) pr = (float) agility * PITCH_SPEED; + if (yr == 0) yr = (float) agility * YAW_SPEED * yaw_air_factor; + + SetAngularRates(rr, pr, yr); +} + +// +--------------------------------------------------------------------+ + +void +Ship::SetRegion(SimRegion* rgn) +{ + SimObject::SetRegion(rgn); + + const double GRAV = 6.673e-11; + + if (IsGroundUnit()) { + // glue buildings to the terrain: + Point loc = Location(); + Terrain* terrain = region->GetTerrain(); + + if (terrain) { + loc.y = terrain->Height(loc.x, loc.z); + MoveTo(loc); + } + } + + else if (IsAirborne()) { + Orbital* primary = GetRegion()->GetOrbitalRegion()->Primary(); + + double m0 = primary->Mass(); + double r = primary->Radius(); + + SetGravity((float) (GRAV * m0 / (r*r))); + SetBaseDensity(1.0f); + } + + else { + SetGravity(0.0f); + SetBaseDensity(0.0f); + + if (IsStarship()) + flcs_mode = FLCS_HELM; + else + flcs_mode = FLCS_AUTO; + } +} + +// +--------------------------------------------------------------------+ + +int +Ship::GetTextureList(List<Bitmap>& textures) +{ + textures.clear(); + + for (int d = 0; d < detail.NumLevels(); d++) { + for (int i = 0; i < detail.NumModels(d); i++) { + Graphic* g = detail.GetRep(d, i); + + if (g->IsSolid()) { + Solid* solid = (Solid*) g; + Model* model = solid->GetModel(); + + if (model) { + for (int n = 0; n < model->NumMaterials(); n++) { + //textures.append(model->textures[n]); + } + } + } + } + } + + return textures.size(); +} + +// +--------------------------------------------------------------------+ + +void +Ship::Activate(Scene& scene) +{ + int i = 0; + SimObject::Activate(scene); + + for (i = 0; i < detail.NumModels(detail_level); i++) { + Graphic* g = detail.GetRep(detail_level, i); + scene.AddGraphic(g); + } + + for (i = 0; i < flight_decks.size(); i++) + scene.AddLight(flight_decks[i]->GetLight()); + + if (shieldRep) + scene.AddGraphic(shieldRep); + + if (cockpit) { + scene.AddForeground(cockpit); + cockpit->Hide(); + } + + Drive* drive = GetDrive(); + if (drive) { + for (i = 0; i < drive->NumEngines(); i++) { + Graphic* flare = drive->GetFlare(i); + if (flare) { + scene.AddGraphic(flare); + } + + Graphic* trail = drive->GetTrail(i); + if (trail) { + scene.AddGraphic(trail); + } + } + } + + Thruster* thruster = GetThruster(); + if (thruster) { + for (i = 0; i < thruster->NumThrusters(); i++) { + Graphic* flare = thruster->Flare(i); + if (flare) { + scene.AddGraphic(flare); + } + + Graphic* trail = thruster->Trail(i); + if (trail) { + scene.AddGraphic(trail); + } + } + } + + for (int n = 0; n < navlights.size(); n++) { + NavLight* navlight = navlights[n]; + for (i = 0; i < navlight->NumBeacons(); i++) { + Graphic* beacon = navlight->Beacon(i); + if (beacon) + scene.AddGraphic(beacon); + } + } + + ListIter<WeaponGroup> g = weapons; + while (++g) { + ListIter<Weapon> w = g->GetWeapons(); + while (++w) { + Solid* turret = w->GetTurret(); + if (turret) { + scene.AddGraphic(turret); + + Solid* turret_base = w->GetTurretBase(); + if (turret_base) + scene.AddGraphic(turret_base); + } + if (w->IsMissile()) { + for (i = 0; i < w->Ammo(); i++) { + Solid* store = w->GetVisibleStore(i); + if (store) + scene.AddGraphic(store); + } + } + } + } + + if (gear && gear->GetState() != LandingGear::GEAR_UP) { + for (int i = 0; i < gear->NumGear(); i++) { + scene.AddGraphic(gear->GetGear(i)); + } + } +} + +void +Ship::Deactivate(Scene& scene) +{ + int i = 0; + SimObject::Deactivate(scene); + + for (i = 0; i < detail.NumModels(detail_level); i++) { + Graphic* g = detail.GetRep(detail_level, i); + scene.DelGraphic(g); + } + + for (i = 0; i < flight_decks.size(); i++) + scene.DelLight(flight_decks[i]->GetLight()); + + if (shieldRep) + scene.DelGraphic(shieldRep); + + if (cockpit) + scene.DelForeground(cockpit); + + Drive* drive = GetDrive(); + if (drive) { + for (i = 0; i < drive->NumEngines(); i++) { + Graphic* flare = drive->GetFlare(i); + if (flare) { + scene.DelGraphic(flare); + } + + Graphic* trail = drive->GetTrail(i); + if (trail) { + scene.DelGraphic(trail); + } + } + } + + Thruster* thruster = GetThruster(); + if (thruster) { + for (i = 0; i < thruster->NumThrusters(); i++) { + Graphic* flare = thruster->Flare(i); + if (flare) { + scene.DelGraphic(flare); + } + + Graphic* trail = thruster->Trail(i); + if (trail) { + scene.DelGraphic(trail); + } + } + } + + for (int n = 0; n < navlights.size(); n++) { + NavLight* navlight = navlights[n]; + for (i = 0; i < navlight->NumBeacons(); i++) { + Graphic* beacon = navlight->Beacon(i); + if (beacon) + scene.DelGraphic(beacon); + } + } + + ListIter<WeaponGroup> g = weapons; + while (++g) { + ListIter<Weapon> w = g->GetWeapons(); + while (++w) { + Solid* turret = w->GetTurret(); + if (turret) { + scene.DelGraphic(turret); + + Solid* turret_base = w->GetTurretBase(); + if (turret_base) + scene.DelGraphic(turret_base); + } + if (w->IsMissile()) { + for (i = 0; i < w->Ammo(); i++) { + Solid* store = w->GetVisibleStore(i); + if (store) + scene.DelGraphic(store); + } + } + } + } + + if (gear) { + for (int i = 0; i < gear->NumGear(); i++) { + scene.DelGraphic(gear->GetGear(i)); + } + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::MatchOrientation(const Ship& s) +{ + Point pos = cam.Pos(); + cam.Clone(s.cam); + cam.MoveTo(pos); + + if (rep) + rep->SetOrientation(cam.Orientation()); + + if (cockpit) + cockpit->SetOrientation(cam.Orientation()); +} + +// +--------------------------------------------------------------------+ + +void +Ship::ClearTrack() +{ + const int DEFAULT_TRACK_LENGTH = 20; // 10 seconds + + if (!track) { + track = new(__FILE__,__LINE__) Point[DEFAULT_TRACK_LENGTH]; + } + + track[0] = Location(); + ntrack = 1; + track_time = Game::GameTime(); +} + +void +Ship::UpdateTrack() +{ + const int DEFAULT_TRACK_UPDATE = 500; // milliseconds + const int DEFAULT_TRACK_LENGTH = 20; // 10 seconds + + DWORD time = Game::GameTime(); + + if (!track) { + track = new(__FILE__,__LINE__) Point[DEFAULT_TRACK_LENGTH]; + track[0] = Location(); + ntrack = 1; + track_time = time; + } + + else if (time - track_time > DEFAULT_TRACK_UPDATE) { + if (Location() != track[0]) { + for (int i = DEFAULT_TRACK_LENGTH-2; i >= 0; i--) + track[i+1] = track[i]; + + track[0] = Location(); + if (ntrack < DEFAULT_TRACK_LENGTH) ntrack++; + } + + track_time = time; + } +} + +Point +Ship::TrackPoint(int i) const +{ + if (track && i < ntrack) + return track[i]; + + return Point(); +} + +// +--------------------------------------------------------------------+ + +const char* +Ship::Abbreviation() const +{ + return design->abrv; +} + +const char* +Ship::DesignName() const +{ + return design->DisplayName(); +} + +const char* +Ship::DesignFileName() const +{ + return design->filename; +} + +const char* +Ship::ClassName() const +{ + return ShipDesign::ClassName(design->type); +} + +const char* +Ship::ClassName(int c) +{ + return ShipDesign::ClassName(c); +} + +int +Ship::ClassForName(const char* name) +{ + return ShipDesign::ClassForName(name); +} + +Ship::CLASSIFICATION +Ship::Class() const +{ + return (CLASSIFICATION) design->type; +} + +bool +Ship::IsGroundUnit() const +{ + return (design->type & GROUND_UNITS) ? true : false; +} + +bool +Ship::IsStarship() const +{ + return (design->type & STARSHIPS) ? true : false; +} + +bool +Ship::IsDropship() const +{ + return (design->type & DROPSHIPS) ? true : false; +} + +bool +Ship::IsStatic() const +{ + return design->type >= STATION; +} + +bool +Ship::IsRogue() const +{ + return ff_count >= 50; +} + +// +--------------------------------------------------------------------+ + +bool +Ship::IsHostileTo(const SimObject* o) const +{ + if (o) { + if (IsRogue()) + return true; + + if (o->Type() == SIM_SHIP) { + Ship* s = (Ship*) o; + + if (s->IsRogue()) + return true; + + if (GetIFF() == 0) { + if (s->GetIFF() > 1) + return true; + } + else { + if (s->GetIFF() > 0 && s->GetIFF() != GetIFF()) + return true; + } + } + + else if (o->Type() == SIM_SHOT || o->Type() == SIM_DRONE) { + Shot* s = (Shot*) o; + + if (GetIFF() == 0) { + if (s->GetIFF() > 1) + return true; + } + else { + if (s->GetIFF() > 0 && s->GetIFF() != GetIFF()) + return true; + } + } + } + + return false; +} + +// +--------------------------------------------------------------------+ + +double +Ship::RepairSpeed() const +{ + return design->repair_speed; +} + +int +Ship::RepairTeams() const +{ + return design->repair_teams; +} + +// +--------------------------------------------------------------------+ + +int +Ship::NumContacts() const +{ + // cast-away const: + return ((Ship*)this)->ContactList().size(); +} + +List<Contact>& +Ship::ContactList() +{ + if (region) + return region->TrackList(GetIFF()); + + static List<Contact> empty_contact_list; + return empty_contact_list; +} + +Contact* +Ship::FindContact(SimObject* s) const +{ + if (!s) return 0; + + ListIter<Contact> c_iter = ((Ship*) this)->ContactList(); + while (++c_iter) { + Contact* c = c_iter.value(); + + if (c->GetShip() == s) + return c; + + if (c->GetShot() == s) + return c; + } + + return 0; +} + +// +--------------------------------------------------------------------+ + +Ship* +Ship::GetController() const +{ + Ship* controller = 0; + + if (carrier) { + // are we in same region as carrier? + if (carrier->GetRegion() == GetRegion()) { + return carrier; + } + + // if not, figure out who our control unit is: + else { + double distance = 10e6; + + ListIter<Ship> iter = GetRegion()->Carriers(); + while (++iter) { + Ship* test = iter.value(); + if (test->GetIFF() == GetIFF()) { + double d = Point(Location() - test->Location()).length(); + if (d < distance) { + controller = test; + distance = d; + } + } + } + } + } + + if (!controller) { + if (element && element->GetCommander()) + controller = element->GetCommander()->GetShip(1); + } + + return controller; +} + +int +Ship::NumInbound() const +{ + int result = 0; + + for (int i = 0; i < flight_decks.size(); i++) { + result += flight_decks[i]->GetRecoveryQueue().size(); + } + + return result; +} + +int +Ship::NumFlightDecks() const +{ + return flight_decks.size(); +} + +FlightDeck* +Ship::GetFlightDeck(int i) const +{ + if (i >= 0 && i < flight_decks.size()) + return flight_decks[i]; + + return 0; +} + +// +--------------------------------------------------------------------+ + +void +Ship::SetFlightPhase(OP_MODE phase) +{ + if (phase == ACTIVE && !launch_time) { + launch_time = Game::GameTime() + 1; + dock = 0; + + if (element) + element->SetLaunchTime(launch_time); + } + + flight_phase = phase; + + if (flight_phase == ACTIVE) + dock = 0; +} + +void +Ship::SetCarrier(Ship* c, FlightDeck* d) +{ + carrier = c; + dock = d; + + if (carrier) + Observe(carrier); +} + +void +Ship::SetInbound(InboundSlot* s) +{ + inbound = s; + + if (inbound && flight_phase == ACTIVE) { + flight_phase = APPROACH; + + SetCarrier((Ship*) inbound->GetDeck()->GetCarrier(), inbound->GetDeck()); + + HUDView* hud = HUDView::GetInstance(); + + if (hud && hud->GetShip() == this) + hud->SetHUDMode(HUDView::HUD_MODE_ILS); + } +} + +void +Ship::Stow() +{ + if (carrier && carrier->GetHangar()) + carrier->GetHangar()->Stow(this); +} + +bool +Ship::IsGearDown() +{ + if (gear && gear->GetState() == LandingGear::GEAR_DOWN) + return true; + + return false; +} + +void +Ship::LowerGear() +{ + if (gear && gear->GetState() != LandingGear::GEAR_DOWN) { + gear->SetState(LandingGear::GEAR_LOWER); + Scene* scene = 0; + + if (rep) + scene = rep->GetScene(); + + if (scene) { + for (int i = 0; i < gear->NumGear(); i++) { + Solid* g = gear->GetGear(i); + if (g) { + if (detail_level == 0) + scene->DelGraphic(g); + else + scene->AddGraphic(g); + } + } + } + } +} + +void +Ship::RaiseGear() +{ + if (gear && gear->GetState() != LandingGear::GEAR_UP) + gear->SetState(LandingGear::GEAR_RAISE); +} + +void +Ship::ToggleGear() +{ + if (gear) { + if (gear->GetState() == LandingGear::GEAR_UP || + gear->GetState() == LandingGear::GEAR_RAISE) { + LowerGear(); + } + else { + RaiseGear(); + } + } +} + +void +Ship::ToggleNavlights() +{ + bool enable = false; + + for (int i = 0; i < navlights.size(); i++) { + if (i == 0) + enable = !navlights[0]->IsEnabled(); + + if (enable) + navlights[i]->Enable(); + else + navlights[i]->Disable(); + } +} + +// +--------------------------------------------------------------------+ + +int +Ship::CollidesWith(Physical& o) +{ + // bounding spheres test: + Point delta_loc = Location() - o.Location(); + if (delta_loc.length() > radius + o.Radius()) + return 0; + + if (!o.Rep()) + return 1; + + for (int i = 0; i < detail.NumModels(detail_level); i++) { + Graphic* g = detail.GetRep(detail_level, i); + + if (o.Type() == SimObject::SIM_SHIP) { + Ship* o_ship = (Ship*) &o; + int o_det = o_ship->detail_level; + + for (int j = 0; j < o_ship->detail.NumModels(o_det); j++) { + Graphic* o_g = o_ship->detail.GetRep(o_det, j); + + if (g->CollidesWith(*o_g)) + return 1; + } + } + else { + // representation collision test (will do bounding spheres first): + if (g->CollidesWith(*o.Rep())) + return 1; + } + } + + return 0; +} + +// +--------------------------------------------------------------------+ + +static DWORD ff_warn_time = 0; + +int +Ship::HitBy(Shot* shot, Point& impact) +{ + if (shot->Owner() == this || IsNetObserver()) + return HIT_NOTHING; + + if (shot->IsFlak()) + return HIT_NOTHING; + + if (InTransition()) + return HIT_NOTHING; + + Point shot_loc = shot->Location(); + Point delta = shot_loc - Location(); + double dlen = delta.length(); + + Point hull_impact; + int hit_type = HIT_NOTHING; + double dscale = 1; + float scale = design->explosion_scale; + Weapon* wep = 0; + + if (!shot->IsMissile() && !shot->IsBeam()) { + if (dlen > Radius() * 2) + return HIT_NOTHING; + } + + if (scale <= 0) + scale = design->scale; + + if (shot->Owner()) { + const ShipDesign* owner_design = shot->Owner()->Design(); + if (owner_design && owner_design->scale < scale) + scale = (float) owner_design->scale; + } + + + // MISSILE PROCESSING ------------------------------------------------ + + if (shot->IsMissile() && rep) { + if (dlen < rep->Radius()) { + hull_impact = impact = shot_loc; + + hit_type = CheckShotIntersection(shot, impact, hull_impact, &wep); + + if (hit_type) { + if (shot->Damage() > 0) { + DWORD flash = Explosion::HULL_FLASH; + + if ((hit_type & HIT_SHIELD) != 0) + flash = Explosion::SHIELD_FLASH; + + sim->CreateExplosion(impact, Velocity(), flash, 0.3f * scale, scale, region); + sim->CreateExplosion(impact, Point(), Explosion::SHOT_BLAST, 2.0f, scale, region); + } + } + } + + if (hit_type == HIT_NOTHING && shot->IsArmed()) { + SeekerAI* seeker = (SeekerAI*) shot->GetDirector(); + + // if the missile overshot us, take damage proportional to distance + double damage_radius = shot->Design()->lethal_radius; + if (dlen < (damage_radius + Radius())) { + if (seeker && seeker->Overshot()) { + dscale = 1.0 - (dlen / (damage_radius + Radius())); + + if (dscale > 1) + dscale = 1; + + if (ShieldStrength() > 5) { + hull_impact = impact = shot_loc; + + if (shot->Damage() > 0) { + if (shieldRep) + shieldRep->Hit(impact, shot, shot->Damage()*dscale); + sim->CreateExplosion(impact, Velocity(), Explosion::SHIELD_FLASH, 0.20f * scale, scale, region); + sim->CreateExplosion(impact, Point(), Explosion::SHOT_BLAST, 20.0f * scale, scale, region); + } + + hit_type = HIT_BOTH; + } + else { + hull_impact = impact = shot_loc; + + if (shot->Damage() > 0) { + sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FLASH, 0.30f * scale, scale, region); + sim->CreateExplosion(impact, Point(), Explosion::SHOT_BLAST, 20.0f * scale, scale, region); + } + + hit_type = HIT_HULL; + } + } + } + } + } + + // ENERGY WEP PROCESSING --------------------------------------------- + + else { + hit_type = CheckShotIntersection(shot, impact, hull_impact, &wep); + + // impact: + if (hit_type) { + + if (hit_type & HIT_SHIELD) { + if (shieldRep) + shieldRep->Hit(impact, shot, shot->Damage()); + sim->CreateExplosion(impact, Velocity(), Explosion::SHIELD_FLASH, 0.20f * scale, scale, region); + } + + else { + if (shot->IsBeam()) + sim->CreateExplosion(impact, Velocity(), Explosion::BEAM_FLASH, 0.30f * scale, scale, region); + else + sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FLASH, 0.30f * scale, scale, region); + + if (IsStarship()) { + Point burst_vel = hull_impact - Location(); + burst_vel.Normalize(); + burst_vel *= Radius() * 0.5; + burst_vel += Velocity(); + + sim->CreateExplosion(hull_impact, burst_vel, Explosion::HULL_BURST, 0.50f * scale, scale, region, this); + } + } + } + } + + // DAMAGE RESOLUTION ------------------------------------------------- + + if (hit_type != HIT_NOTHING && shot->IsArmed()) { + double effective_damage = shot->Damage() * dscale; + + // FRIENDLY FIRE -------------------------------------------------- + + if (shot->Owner()) { + Ship* s = (Ship*) shot->Owner(); + + if (!IsRogue() && s->GetIFF() == GetIFF() && + s->GetDirector() && s->GetDirector()->Type() < 1000) { + bool was_rogue = s->IsRogue(); + + // only count beam hits once + if (shot->Damage() && !shot->HitTarget() && GetFriendlyFireLevel() > 0) { + int penalty = 1; + + if (shot->IsBeam()) penalty = 5; + else if (shot->IsDrone()) penalty = 7; + + if (s->GetTarget() == this) penalty *= 3; + + s->IncFriendlyFire(penalty); + } + + effective_damage *= GetFriendlyFireLevel(); + + if (Class() > DRONE && s->Class() > DRONE) { + if (s->IsRogue() && !was_rogue) { + RadioMessage* warn = new(__FILE__,__LINE__) RadioMessage(s, this, RadioMessage::DECLARE_ROGUE); + RadioTraffic::Transmit(warn); + } + else if (!s->IsRogue() && (Game::GameTime() - ff_warn_time) > 5000) { + ff_warn_time = Game::GameTime(); + + RadioMessage* warn = 0; + if (s->GetTarget() == this) + warn = new(__FILE__,__LINE__) RadioMessage(s, this, RadioMessage::WARN_TARGETED); + else + warn = new(__FILE__,__LINE__) RadioMessage(s, this, RadioMessage::WARN_ACCIDENT); + + RadioTraffic::Transmit(warn); + } + } + } + } + + if (effective_damage > 0) { + if (!shot->IsBeam() && shot->Design()->damage_type == WeaponDesign::DMG_NORMAL) { + ApplyTorque(shot->Velocity() * (float) effective_damage * 1e-6f); + } + + if (!NetGame::IsNetGameClient()) { + InflictDamage(effective_damage, shot, hit_type, hull_impact); + } + } + } + + return hit_type; +} + +static bool CheckRaySphereIntersection(Point loc, double radius, Point Q, Point w, double len) +{ + Point d0 = loc - Q; + Point d1 = d0.cross(w); + double dlen = d1.length(); // distance of point from line + + if (dlen > radius) // clean miss + return false; // (no impact) + + // possible collision course... + // find the point on the ray that is closest + // to the sphere's location: + Point closest = Q + w * (d0 * w); + + // find the leading edge, and it's distance from the location: + Point leading_edge = Q + w*len; + Point leading_delta = leading_edge - loc; + double leading_dist = leading_delta.length(); + + // if the leading edge is not within the sphere, + if (leading_dist > radius) { + // check to see if the closest point is between the + // ray's endpoints: + Point delta1 = closest - Q; + Point delta2 = leading_edge - Q; // this is w*len + + // if the closest point is not between the leading edge + // and the origin, this ray does not intersect: + if (delta1 * delta2 < 0 || delta1.length() > len) { + return false; + } + } + + return true; +} + +int +Ship::CheckShotIntersection(Shot* shot, Point& ipt, Point& hpt, Weapon** wep) +{ + int hit_type = HIT_NOTHING; + Point shot_loc = shot->Location(); + Point shot_org = shot->Origin(); + Point shot_vpn = shot_loc - shot_org; + double shot_len = shot_vpn.Normalize(); + double blow_len = shot_len; + bool hit_hull = false; + bool easy = false; + + if (shot_len <= 0) + return hit_type; + + if (shot_len < 1000) + shot_len = 1000; + + Point hull_impact; + Point shield_impact; + Point turret_impact; + Point closest; + double d0 = 1e9; + double d1 = 1e9; + double ds = 1e9; + + if (dir && dir->Type() == SteerAI::FIGHTER) { + ShipAI* shipAI = (ShipAI*) dir; + easy = shipAI->GetAILevel() < 2; + } + + if (shieldRep && ShieldStrength() > 5) { + if (shieldRep->CheckRayIntersection(shot_org, shot_vpn, shot_len, shield_impact)) { + hit_type = HIT_SHIELD; + closest = shield_impact; + d0 = Point(closest - shot_org).length(); + ds = d0; + + ipt = shield_impact; + } + } + + if (shieldRep && hit_type == HIT_SHIELD && !shot->IsBeam()) + blow_len = shieldRep->Radius() * 2; + + for (int i = 0; i < detail.NumModels(detail_level) && !hit_hull; i++) { + Solid* s = (Solid*) detail.GetRep(detail_level, i); + if (s) { + if (easy) { + hit_hull = CheckRaySphereIntersection(s->Location(), s->Radius(), shot_org, shot_vpn, shot_len); + } + else { + hit_hull = s->CheckRayIntersection(shot_org, shot_vpn, blow_len, hull_impact)?true:false; + } + } + } + + if (hit_hull) { + if (ShieldStrength() > 5 && !shieldRep) + hit_type = HIT_SHIELD; + + hit_type = hit_type | HIT_HULL; + hpt = hull_impact; + + d1 = Point(hull_impact - shot_org).length(); + + if (d1 < d0) { + closest = hull_impact; + d0 = d1; + } + } + + if (IsStarship() || IsStatic()) { + ListIter<WeaponGroup> g_iter = Weapons(); + while (++g_iter) { + WeaponGroup* g = g_iter.value(); + + if (g->GetDesign() && g->GetDesign()->turret_model) { + double tsize = g->GetDesign()->turret_model->Radius(); + + ListIter<Weapon> w_iter = g->GetWeapons(); + while (++w_iter) { + Weapon* w = w_iter.value(); + + Point tloc = w->GetTurret()->Location(); + + if (CheckRaySphereIntersection(tloc, tsize, shot_org, shot_vpn, shot_len)) { + Point delta = tloc - shot_org; + d1 = delta.length(); + + if (d1 < d0) { + if (wep) *wep = w; + hit_type = hit_type | HIT_TURRET; + turret_impact = tloc; + + d0 = d1; + + closest = turret_impact; + hull_impact = turret_impact; + hpt = turret_impact; + + if (d1 < ds) + ipt = turret_impact; + } + } + } + } + } + } + + // trim beam shots to closest impact point: + if (hit_type && shot->IsBeam()) { + shot->SetBeamPoints(shot_org, closest); + } + + return hit_type; +} + +// +--------------------------------------------------------------------+ + +void +Ship::InflictNetDamage(double damage, Shot* shot) +{ + if (damage > 0 && !IsNetObserver()) { + Physical::InflictDamage(damage, 0); + + // shake by percentage of maximum damage + double newshake = 50 * damage/design->integrity; + const double MAX_SHAKE = 7; + + if (shake < MAX_SHAKE) shake += (float) newshake; + if (shake > MAX_SHAKE) shake = (float) MAX_SHAKE; + } +} + +void +Ship::InflictNetSystemDamage(System* system, double damage, BYTE dmg_type) +{ + if (system && damage > 0 && !IsNetObserver()) { + bool dmg_normal = dmg_type == WeaponDesign::DMG_NORMAL; + bool dmg_power = dmg_type == WeaponDesign::DMG_POWER; + bool dmg_emp = dmg_type == WeaponDesign::DMG_EMP; + + double sys_damage = damage; + double avail = system->Availability(); + + if (dmg_normal || system->IsPowerCritical() && dmg_emp) { + system->ApplyDamage(sys_damage); + master_caution = true; + + if (system->GetExplosionType() && (avail - system->Availability()) >= 50) { + float scale = design->explosion_scale; + if (scale <= 0) + scale = design->scale; + + sim->CreateExplosion(system->MountLocation(), Velocity() * 0.7f, system->GetExplosionType(), 0.2f * scale, scale, region, this, system); + } + } + } +} + +void +Ship::SetNetSystemStatus(System* system, int status, int power, int reactor, double avail) +{ + if (system && !IsNetObserver()) { + if (system->GetPowerLevel() != power) + system->SetPowerLevel(power); + + if (system->GetSourceIndex() != reactor) { + System* s = GetSystem(reactor); + + if (s && s->Type() == System::POWER_SOURCE) { + PowerSource* reac = (PowerSource*) s; + reac->AddClient(system); + } + } + + if (system->Status() != status) { + if (status == System::MAINT) { + ListIter<Component> comp = system->GetComponents(); + while (++comp) { + Component* c = comp.value(); + + if (c->Status() < Component::NOMINAL && c->Availability() < 75) { + if (c->SpareCount() && + c->ReplaceTime() <= 300 && + (c->Availability() < 50 || + c->ReplaceTime() < c->RepairTime())) { + + c->Replace(); + } + + else if (c->Availability() >= 50 || c->NumJerried() < 5) { + c->Repair(); + } + } + } + + RepairSystem(system); + } + } + + if (system->Availability() < avail) { + system->SetNetAvail(avail); + } + else { + system->SetNetAvail(-1); + } + } +} + +// +----------------------------------------------------------------------+ + +bool IsWeaponBlockedFriendly(Weapon* w, const SimObject* test) +{ + if (w->GetTarget()) { + Point tgt = w->GetTarget()->Location(); + Point obj = test->Location(); + Point wep = w->MountLocation(); + + Point dir = tgt - wep; + double d = dir.Normalize(); + Point rho = obj - wep; + double r = rho.Normalize(); + + // if target is much closer than obstacle, + // don't worry about friendly fire... + if (d < 1.5 * r) + return false; + + Point dst = dir * r + wep; + double err = (obj - dst).length(); + + if (err < test->Radius() * 1.5) + return true; + } + + return false; +} + +void +Ship::CheckFriendlyFire() +{ + // if no weapons, there is no worry about friendly fire... + if (weapons.size() < 1) + return; + + // only check once each second + if (Game::GameTime() - friendly_fire_time < 1000) + return; + + List<Weapon> w_list; + int i, j; + + // clear the FF blocked flag on all weapons + for (i = 0; i < weapons.size(); i++) { + WeaponGroup* g = weapons[i]; + + for (j = 0; j < g->NumWeapons(); j++) { + Weapon* w = g->GetWeapon(j); + w_list.append(w); + w->SetBlockedFriendly(false); + } + } + + // for each friendly ship within some kind of weapons range, + ListIter<Contact> c_iter = ContactList(); + while (++c_iter) { + Contact* c = c_iter.value(); + Ship* cship = c->GetShip(); + Shot* cshot = c->GetShot(); + + if (cship && cship != this && (cship->GetIFF() == 0 || cship->GetIFF() == GetIFF())) { + double range = (cship->Location() - Location()).length(); + + if (range > 100e3) + continue; + + // check each unblocked weapon to see if it is blocked by that ship + ListIter<Weapon> iter = w_list; + while (++iter) { + Weapon* w = iter.value(); + + if (!w->IsBlockedFriendly()) + w->SetBlockedFriendly(IsWeaponBlockedFriendly(w, cship)); + } + } + + else if (cshot && cshot->GetIFF() == GetIFF()) { + double range = (cshot->Location() - Location()).length(); + + if (range > 30e3) + continue; + + // check each unblocked weapon to see if it is blocked by that shot + ListIter<Weapon> iter = w_list; + while (++iter) { + Weapon* w = iter.value(); + + if (!w->IsBlockedFriendly()) + w->SetBlockedFriendly(IsWeaponBlockedFriendly(w, cshot)); + } + } + } + + friendly_fire_time = Game::GameTime() + (DWORD) Random(0, 500); +} + +// +----------------------------------------------------------------------+ + +Ship* +Ship::GetLeader() const +{ + if (element) + return element->GetShip(1); + + return (Ship*) this; +} + +int +Ship::GetElementIndex() const +{ + if (element) + return element->FindIndex(this); + + return 0; +} + +int +Ship::GetOrigElementIndex() const +{ + return orig_elem_index; +} + +void +Ship::SetElement(Element* e) +{ + element = e; + + if (element) { + combat_unit = element->GetCombatUnit(); + + if (combat_unit) { + integrity = (float) (design->integrity - combat_unit->GetSustainedDamage()); + } + + orig_elem_index = element->FindIndex(this); + } +} + +void +Ship::SetLaunchPoint(Instruction* pt) +{ + if (pt && !launch_point) + launch_point = pt; +} + +void +Ship::AddNavPoint(Instruction* pt, Instruction* after) +{ + if (GetElementIndex() == 1) + element->AddNavPoint(pt, after); +} + +void +Ship::DelNavPoint(Instruction* pt) +{ + if (GetElementIndex() == 1) + element->DelNavPoint(pt); +} + +void +Ship::ClearFlightPlan() +{ + if (GetElementIndex() == 1) + element->ClearFlightPlan(); +} + +// +----------------------------------------------------------------------+ + +bool +Ship::IsAutoNavEngaged() +{ + if (navsys && navsys->AutoNavEngaged()) + return true; + + return false; +} + +void +Ship::SetAutoNav(bool engage) +{ + if (navsys) { + if (navsys->AutoNavEngaged()) { + if (!engage) + navsys->DisengageAutoNav(); + } + else { + if (engage) + navsys->EngageAutoNav(); + } + + if (sim) + SetControls(sim->GetControls()); + } +} + +void +Ship::CommandMode() +{ + if (!dir || dir->Type() != ShipCtrl::DIR_TYPE) { + const char* msg = "Captain on the bridge"; + RadioVox* vox = new(__FILE__,__LINE__) RadioVox(0, "1", msg); + + if (vox) { + vox->AddPhrase(msg); + + if (!vox->Start()) { + RadioView::Message( RadioTraffic::TranslateVox(msg) ); + delete vox; + } + } + + SetControls(sim->GetControls()); + } + + else { + const char* msg = "Exec, you have the conn"; + RadioVox* vox = new(__FILE__,__LINE__) RadioVox(0, "1", msg); + + if (vox) { + vox->AddPhrase(msg); + + if (!vox->Start()) { + RadioView::Message( RadioTraffic::TranslateVox(msg) ); + delete vox; + } + } + + SetControls(0); + } +} + +// +----------------------------------------------------------------------+ + +Instruction* +Ship::GetNextNavPoint() +{ + if (launch_point && launch_point->Status() <= Instruction::ACTIVE) + return launch_point; + + if (element) + return element->GetNextNavPoint(); + + return 0; +} + +int +Ship::GetNavIndex(const Instruction* n) +{ + if (element) + return element->GetNavIndex(n); + + return 0; +} + +double +Ship::RangeToNavPoint(const Instruction* n) +{ + double distance = 0; + + if (n && n->Region()) { + Point npt = n->Region()->Location() + n->Location(); + npt -= GetRegion()->Location(); + npt = npt.OtherHand(); // convert from map to sim coords + + distance = Point(npt - Location()).length(); + } + + return distance; +} + +void +Ship::SetNavptStatus(Instruction* navpt, int status) +{ + if (navpt && navpt->Status() != status) { + if (status == Instruction::COMPLETE) { + if (navpt->Action() == Instruction::ASSAULT) + ::Print("Completed Assault\n"); + + else if (navpt->Action() == Instruction::STRIKE) + ::Print("Completed Strike\n"); + } + + navpt->SetStatus(status); + + if (status == Instruction::COMPLETE) + sim->ProcessEventTrigger(MissionEvent::TRIGGER_NAVPT, 0, Name(), GetNavIndex(navpt)); + + if (element) { + int index = element->GetNavIndex(navpt); + + if (index >= 0) + NetUtil::SendNavData(false, element, index-1, navpt); + } + } +} + +List<Instruction>& +Ship::GetFlightPlan() +{ + if (element) + return element->GetFlightPlan(); + + static List<Instruction> dummy_flight_plan; + return dummy_flight_plan; +} + +int +Ship::FlightPlanLength() +{ + if (element) + return element->FlightPlanLength(); + + return 0; +} + +// +--------------------------------------------------------------------+ + +void +Ship::SetWard(Ship* s) +{ + if (ward == s) + return; + + ward = s; + + if (ward) + Observe(ward); +} + +// +--------------------------------------------------------------------+ + +void +Ship::SetTarget(SimObject* targ, System* sub, bool from_net) +{ + if (targ && targ->Type() == SimObject::SIM_SHIP) { + Ship* targ_ship = (Ship*) targ; + + if (targ_ship && targ_ship->IsNetObserver()) + return; + } + + if (target != targ) { + // DON'T IGNORE TARGET, BECAUSE IT MAY BE IN THREAT LIST + target = targ; + if (target) Observe(target); + + if (sim && target) + sim->ProcessEventTrigger(MissionEvent::TRIGGER_TARGET, 0, target->Name()); + } + + subtarget = sub; + + ListIter<WeaponGroup> weapon = weapons; + while (++weapon) { + if (weapon->GetFiringOrders() != Weapon::POINT_DEFENSE) { + weapon->SetTarget(target, subtarget); + + if (sub || !IsStarship()) + weapon->SetSweep(Weapon::SWEEP_NONE); + else + weapon->SetSweep(Weapon::SWEEP_TIGHT); + } + } + + if (!from_net && NetGame::GetInstance()) + NetUtil::SendObjTarget(this); + + // track engagement: + if (target && target->Type() == SimObject::SIM_SHIP) { + Element* elem = GetElement(); + Element* tgt_elem = ((Ship*) target)->GetElement(); + + if (elem) + elem->SetAssignment(tgt_elem); + } +} + +void +Ship::DropTarget() +{ + target = 0; + subtarget = 0; + + SetTarget(target, subtarget); +} + +// +--------------------------------------------------------------------+ + +void +Ship::CycleSubTarget(int dir) +{ + if (!target || target->Type() != SimObject::SIM_SHIP) + return; + + Ship* tgt = (Ship*) target; + + if (tgt->IsDropship()) + return; + + System* subtgt = 0; + + ListIter<System> sys = tgt->Systems(); + + if (dir > 0) { + int latch = (subtarget == 0); + while (++sys) { + if (sys->Type() == System::COMPUTER || // computers are not targetable + sys->Type() == System::SENSOR) // sensors are not targetable + continue; + + if (sys.value() == subtarget) { + latch = 1; + } + + else if (latch) { + subtgt = sys.value(); + break; + } + } + } + + else { + System* prev = 0; + + while (++sys) { + if (sys->Type() == System::COMPUTER || // computers are not targetable + sys->Type() == System::SENSOR) // sensors are not targetable + continue; + + if (sys.value() == subtarget) { + subtgt = prev; + break; + } + + prev = sys.value(); + } + + if (!subtarget) + subtgt = prev; + } + + SetTarget(tgt, subtgt); +} + +// +--------------------------------------------------------------------+ + +void +Ship::ExecFrame(double seconds) +{ + ZeroMemory(trigger, sizeof(trigger)); + altitude_agl = -1.0e6f; + + if (flight_phase < LAUNCH) { + DockFrame(seconds); + return; + } + + if (flight_phase == LAUNCH || + (flight_phase == TAKEOFF && AltitudeAGL() > Radius())) { + SetFlightPhase(ACTIVE); + } + + if (transition_time > 0) { + transition_time -= (float) seconds; + + if (transition_time <= 0) { + CompleteTransition(); + return; + } + + if (rep && IsDying() && killer) { + killer->ExecFrame(seconds); + } + } + + // observers do not run out of power: + if (IsNetObserver()) { + for (int i = 0; i < reactors.size(); i++) + reactors[i]->SetFuelRange(1e6); + } + + if (IsStatic()) { + StatFrame(seconds); + return; + } + + CheckFriendlyFire(); + ExecNavFrame(seconds); + ExecEvalFrame(seconds); + + if (IsAirborne()) { + // are we trying to make orbit? + if (Location().y >= TERRAIN_ALTITUDE_LIMIT) + MakeOrbit(); + } + + if (!InTransition()) { + ExecSensors(seconds); + ExecThrottle(seconds); + } + + else if (IsDropping() || IsAttaining() || IsSkipping()) { + throttle = 100; + } + + if (target && target->Life() == 0) { + DropTarget(); + } + + ExecPhysics(seconds); + + if (!InTransition()) { + UpdateTrack(); + } + + // are we docking? + if (IsDropship()) { + ListIter<Ship> iter = GetRegion()->Carriers(); + + while (++iter) { + Ship* carrier_target = iter.value(); + + double range = (Location() - carrier_target->Location()).length(); + if (range > carrier_target->Radius() * 1.5) + continue; + + if (carrier_target->GetIFF() == GetIFF() || carrier_target->GetIFF() == 0) { + for (int i = 0; i < carrier_target->NumFlightDecks(); i++) { + if (carrier_target->GetFlightDeck(i)->Recover(this)) + break; + } + } + } + } + + ExecSystems(seconds); + ExecMaintFrame(seconds); + + if (flight_decks.size() > 0) { + Camera* global_cam = CameraDirector::GetInstance()->GetCamera(); + Point global_cam_loc = global_cam->Pos(); + bool disable_shadows = false; + + for (int i = 0; i < flight_decks.size(); i++) { + if (flight_decks[i]->ContainsPoint(global_cam_loc)) + disable_shadows = true; + } + + EnableShadows(!disable_shadows); + } + + if (!_finite(Location().x)) { + DropTarget(); + } + + if (!IsStatic() && !IsGroundUnit() && GetFlightModel() < 2) + CalcFlightPath(); +} + +// +--------------------------------------------------------------------+ + +void +Ship::LaunchProbe() +{ + if (net_observer_mode) + return; + + if (sensor_drone) { + sensor_drone = 0; + } + + if (probe) { + sensor_drone = (Drone*) probe->Fire(); + + if (sensor_drone) + Observe(sensor_drone); + + else if (sim->GetPlayerShip() == this) + Button::PlaySound(Button::SND_REJECT); + } +} + +void +Ship::SetProbe(Drone* d) +{ + if (sensor_drone != d) { + sensor_drone = d; + + if (sensor_drone) + Observe(sensor_drone); + } +} + +void +Ship::ExecSensors(double seconds) +{ + // how visible are we? + DoEMCON(); + + // what can we see? + if (sensor) + sensor->ExecFrame(seconds); + + // can we still see our target? + if (target) { + int target_found = 0; + ListIter<Contact> c_iter = ContactList(); + while (++c_iter) { + Contact* c = c_iter.value(); + + if (target == c->GetShip() || target == c->GetShot()) { + target_found = 1; + + bool vis = c->Visible(this) || c->Threat(this); + + if (!vis && !c->PasLock() && !c->ActLock()) + DropTarget(); + } + } + + if (!target_found) + DropTarget(); + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::ExecNavFrame(double seconds) +{ + bool auto_pilot = false; + + // update director info string: + SetFLCSMode(flcs_mode); + + if (navsys) { + navsys->ExecFrame(seconds); + + if (navsys->AutoNavEngaged()) { + if (dir && dir->Type() == NavAI::DIR_TYPE) { + NavAI* navai = (NavAI*) dir; + + if (navai->Complete()) { + navsys->DisengageAutoNav(); + SetControls(sim->GetControls()); + } + else { + auto_pilot = true; + } + } + } + } + + // even if we are not on auto pilot, + // have we completed the next navpoint? + + Instruction* navpt = GetNextNavPoint(); + if (navpt && !auto_pilot) { + if (navpt->Region() == GetRegion()) { + double distance = 0; + + Point npt = navpt->Location(); + + if (navpt->Region()) + npt += navpt->Region()->Location(); + + Sim* sim = Sim::GetSim(); + if (sim->GetActiveRegion()) + npt -= sim->GetActiveRegion()->Location(); + + npt = npt.OtherHand(); + + // distance from self to navpt: + distance = Point(npt - Location()).length(); + + if (distance < 10 * Radius()) + SetNavptStatus(navpt, Instruction::COMPLETE); + } + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::ExecEvalFrame(double seconds) +{ + // is it already too late? + if (life == 0 || integrity < 1) return; + + const DWORD EVAL_FREQUENCY = 1000; // once every second + static DWORD last_eval_frame = 0; // one ship per game frame + + if (element && element->NumObjectives() > 0 && + Game::GameTime() - last_eval_time > EVAL_FREQUENCY && + last_eval_frame != Game::Frame()) { + + last_eval_time = Game::GameTime(); + last_eval_frame = Game::Frame(); + + for (int i = 0; i < element->NumObjectives(); i++) { + Instruction* obj = element->GetObjective(i); + + if (obj->Status() <= Instruction::ACTIVE) { + obj->Evaluate(this); + } + } + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::ExecPhysics(double seconds) +{ + if (net_control) { + net_control->ExecFrame(seconds); + Thrust(seconds); // drive flare + } + else { + thrust = (float) Thrust(seconds); + SetupAgility(); + + if (seconds > 0) { + g_force = 0.0f; + } + + if (IsAirborne()) { + Point v1 = velocity; + AeroFrame(seconds); + Point v2 = velocity; + Point dv = v2 - v1 + Point(0, g_accel*seconds, 0); + + if (seconds > 0) { + g_force = (float) (dv * cam.vup() / seconds) / 9.8f; + } + } + + else if (IsDying() || flight_model < 2) { // standard and relaxed modes + Physical::ExecFrame(seconds); + } + + else { // arcade mode + Physical::ArcadeFrame(seconds); + } + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::ExecThrottle(double seconds) +{ + double spool = 75 * seconds; + + if (throttle < throttle_request) + if (throttle_request-throttle < spool) + throttle = throttle_request; + else + throttle += spool; + + else if (throttle > throttle_request) + if (throttle - throttle_request < spool) + throttle = throttle_request; + else + throttle -= spool; +} + +// +--------------------------------------------------------------------+ + +void +Ship::ExecSystems(double seconds) +{ + if (!rep) + return; + + int i; + + ListIter<System> iter = systems; + while (++iter) { + System* sys = iter.value(); + + sys->Orient(this); + + // sensors have already been executed, + // they can not be run twice in a frame! + if (sys->Type() != System::SENSOR) + sys->ExecFrame(seconds); + } + + // hangars and weapon groups are not systems + // they must be executed separately from above + if (hangar) + hangar->ExecFrame(seconds); + + wep_mass = 0.0f; + wep_resist = 0.0f; + + bool winchester_cycle = false; + + for (i = 0; i < weapons.size(); i++) { + WeaponGroup* w_group = weapons[i]; + w_group->ExecFrame(seconds); + + if (w_group->GetTrigger() && w_group->GetFiringOrders() == Weapon::MANUAL) { + + Weapon* gun = w_group->GetSelected(); + + SimObject* gun_tgt = gun->GetTarget(); + + // if no target has been designated for this + // weapon, let it guide on the contact closest + // to its boresight. this must be done before + // firing the weapon. + + if (sensor && gun->Guided() && !gun->Design()->beam && !gun_tgt) { + gun->SetTarget(sensor->AcquirePassiveTargetForMissile(), 0); + } + + gun->Fire(); + + w_group->SetTrigger(false); + w_group->CycleWeapon(); + w_group->CheckAmmo(); + + // was that the last shot from this missile group? + if (w_group->IsMissile() && w_group->Ammo() < 1) { + + // is this the current secondary weapon group? + if (weapons[secondary] == w_group) { + winchester_cycle = true; + } + } + } + + wep_mass += w_group->Mass(); + wep_resist += w_group->Resistance(); + } + + // if we just fired the last shot in the current secondary + // weapon group, auto cycle to another secondary weapon: + if (winchester_cycle) { + int old_secondary = secondary; + + CycleSecondary(); + + // do not winchester-cycle to an A2G missile type, + // or a missile that is also out of ammo, + // keep going! + + while (secondary != old_secondary) { + Weapon* missile = GetSecondary(); + if (missile && missile->CanTarget(Ship::GROUND_UNITS)) + CycleSecondary(); + + else if (weapons[secondary]->Ammo() < 1) + CycleSecondary(); + + else + break; + } + } + + mass = (float) design->mass + wep_mass; + + if (IsDropship()) + agility = (float) design->agility - wep_resist; + + if (shieldRep) { + Solid* solid = (Solid*) rep; + shieldRep->MoveTo(solid->Location()); + shieldRep->SetOrientation(solid->Orientation()); + + bool bubble = false; + if (shield) + bubble = shield->ShieldBubble(); + + if (shieldRep->ActiveHits()) { + shieldRep->Energize(seconds, bubble); + shieldRep->Show(); + } + else { + shieldRep->Hide(); + } + } + + if (cockpit) { + Solid* solid = (Solid*) rep; + + Point cpos = cam.Pos() + + cam.vrt() * bridge_vec.x + + cam.vpn() * bridge_vec.y + + cam.vup() * bridge_vec.z; + + cockpit->MoveTo(cpos); + cockpit->SetOrientation(solid->Orientation()); + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::AeroFrame(double seconds) +{ + float g_save = g_accel; + + if (Class() == LCA) { + lat_thrust = true; + SetGravity(0.0f); + } + + if (AltitudeAGL() < Radius()) { + SetGravity(0.0f); + + // on the ground/runway? + double bottom = 1e9; + double tlevel = Location().y - AltitudeAGL(); + + // taking off or landing? + if (flight_phase < ACTIVE || flight_phase > APPROACH) { + if (dock) + tlevel = dock->MountLocation().y; + } + + if (tlevel < 0) + tlevel = 0; + + if (gear) + bottom = gear->GetTouchDown()-1; + else + bottom = Location().y-6; + + if (bottom < tlevel) + TranslateBy(Point(0, bottom-tlevel, 0)); + } + + // MODEL 2: ARCADE + if (flight_model >= 2) { + Physical::ArcadeFrame(seconds); + } + + // MODEL 1: RELAXED + else if (flight_model == 1) { + Physical::ExecFrame(seconds); + } + + // MODEL 0: STANDARD + else { + // apply drag-torque (i.e. turn ship into + // velocity vector to minimize drag): + + Point vnrm = velocity; + double v = vnrm.Normalize(); + double pitch_deflection = vnrm * cam.vup(); + double yaw_deflection = vnrm * cam.vrt(); + + if (lat_thrust && v < 250) { + } + + else { + if (v < 250) { + double factor = 1.2 + (250 - v) / 100; + + ApplyPitch(pitch_deflection * -factor); + ApplyYaw(yaw_deflection * factor); + + dp += (float) (dp_acc * seconds); + dy += (float) (dy_acc * seconds); + } + + else { + if (fabs(pitch_deflection) > stall) { + ApplyPitch(pitch_deflection * -1.2); + dp += (float) (dp_acc * seconds); + } + + ApplyYaw(yaw_deflection * 2); + dy += (float) (dy_acc * seconds); + } + } + + // compute rest of physics: + Physical::AeroFrame(seconds); + } + + SetGravity(g_save); +} + +// +--------------------------------------------------------------------+ + +void +Ship::LinearFrame(double seconds) +{ + Physical::LinearFrame(seconds); + + if (!IsAirborne() || Class() != LCA) + return; + + // damp lateral movement in atmosphere: + + // side-to-side + if (!trans_x) { + Point transvec = cam.vrt(); + transvec *= (transvec * velocity) * seconds * 0.5; + velocity -= transvec; + } + + // fore-and-aft + if (!trans_y && fabs(thrust) < 1.0f) { + Point transvec = cam.vpn(); + transvec *= (transvec * velocity) * seconds * 0.25; + velocity -= transvec; + } + + // up-and-down + if (!trans_z) { + Point transvec = cam.vup(); + transvec *= (transvec * velocity) * seconds * 0.5; + velocity -= transvec; + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::DockFrame(double seconds) +{ + SelectDetail(seconds); + + if (sim->GetPlayerShip() == this) { + // Make sure the thruster sound is diabled + // when the player is on the runway or catapult + if (thruster) { + thruster->ExecTrans(0,0,0); + } + } + + if (rep) { + // Update the graphic rep and light sources: + // (This is usually done by the physics class, + // but when the ship is in dock, we skip the + // standard physics processing): + rep->MoveTo(cam.Pos()); + rep->SetOrientation(cam.Orientation()); + + if (light) + light->MoveTo(cam.Pos()); + + ListIter<System> iter = systems; + while (++iter) + iter->Orient(this); + + double spool = 75 * seconds; + + if (flight_phase == DOCKING) { + throttle_request = 0; + throttle = 0; + } + + else if (throttle < throttle_request) + if (throttle_request-throttle < spool) + throttle = throttle_request; + else + throttle += spool; + + else if (throttle > throttle_request) + if (throttle - throttle_request < spool) + throttle = throttle_request; + else + throttle -= spool; + + // make sure there is power to run the drive: + for (int i = 0; i < reactors.size(); i++) + reactors[i]->ExecFrame(seconds); + + // count up weapon ammo for status mfd: + for (int i = 0; i < weapons.size(); i++) + weapons[i]->ExecFrame(seconds); + + // show drive flare while on catapult: + if (main_drive) { + main_drive->SetThrottle(throttle); + + if (throttle > 0) + main_drive->Thrust(seconds); // show drive flare + } + } + + if (cockpit && !cockpit->Hidden()) { + Solid* solid = (Solid*) rep; + + Point cpos = cam.Pos() + + cam.vrt() * bridge_vec.x + + cam.vpn() * bridge_vec.y + + cam.vup() * bridge_vec.z; + + cockpit->MoveTo(cpos); + cockpit->SetOrientation(solid->Orientation()); + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::StatFrame(double seconds) +{ + if (flight_phase != ACTIVE) { + flight_phase = ACTIVE; + launch_time = Game::GameTime() + 1; + + if (element) + element->SetLaunchTime(launch_time); + } + + if (IsGroundUnit()) { + // glue buildings to the terrain: + Point loc = Location(); + Terrain* terrain = region->GetTerrain(); + + if (terrain) { + loc.y = terrain->Height(loc.x, loc.z); + MoveTo(loc); + } + } + + if (rep) { + rep->MoveTo(cam.Pos()); + rep->SetOrientation(cam.Orientation()); + } + + if (light) { + light->MoveTo(cam.Pos()); + } + + ExecSensors(seconds); + + if (target && target->Life() == 0) { + DropTarget(); + } + + if (dir) dir->ExecFrame(seconds); + + SelectDetail(seconds); + + int i = 0; + + if (rep) { + ListIter<System> iter = systems; + while (++iter) + iter->Orient(this); + + for (i = 0; i < reactors.size(); i++) + reactors[i]->ExecFrame(seconds); + + for (i = 0; i < navlights.size(); i++) + navlights[i]->ExecFrame(seconds); + + for (i = 0; i < weapons.size(); i++) + weapons[i]->ExecFrame(seconds); + + if (farcaster) { + farcaster->ExecFrame(seconds); + + if (navlights.size() == 2) { + if (farcaster->Charge() > 99) { + navlights[0]->Enable(); + navlights[1]->Disable(); + } + else { + navlights[0]->Disable(); + navlights[1]->Enable(); + } + } + } + + if (shield) + shield->ExecFrame(seconds); + + if (hangar) + hangar->ExecFrame(seconds); + + if (flight_decks.size() > 0) { + Camera* global_cam = CameraDirector::GetInstance()->GetCamera(); + Point global_cam_loc = global_cam->Pos(); + bool disable_shadows = false; + + for (i = 0; i < flight_decks.size(); i++) { + flight_decks[i]->ExecFrame(seconds); + + if (flight_decks[i]->ContainsPoint(global_cam_loc)) + disable_shadows = true; + } + + EnableShadows(!disable_shadows); + } + } + + if (shieldRep) { + Solid* solid = (Solid*) rep; + shieldRep->MoveTo(solid->Location()); + shieldRep->SetOrientation(solid->Orientation()); + + bool bubble = false; + if (shield) + bubble = shield->ShieldBubble(); + + if (shieldRep->ActiveHits()) { + shieldRep->Energize(seconds, bubble); + shieldRep->Show(); + } + else { + shieldRep->Hide(); + } + } + + if (!_finite(Location().x)) { + DropTarget(); + } +} + +// +--------------------------------------------------------------------+ + +Graphic* +Ship::Cockpit() const +{ + return cockpit; +} + +void +Ship::ShowCockpit() +{ + if (cockpit) { + cockpit->Show(); + + ListIter<WeaponGroup> g = weapons; + while (++g) { + ListIter<Weapon> w = g->GetWeapons(); + while (++w) { + Solid* turret = w->GetTurret(); + if (turret) { + turret->Show(); + + Solid* turret_base = w->GetTurretBase(); + if (turret_base) + turret_base->Show(); + } + + if (w->IsMissile()) { + for (int i = 0; i < w->Ammo(); i++) { + Solid* store = w->GetVisibleStore(i); + if (store) { + store->Show(); + } + } + } + } + } + } +} + +void +Ship::HideCockpit() +{ + if (cockpit) + cockpit->Hide(); +} + +// +--------------------------------------------------------------------+ + +void +Ship::SelectDetail(double seconds) +{ + detail.ExecFrame(seconds); + detail.SetLocation(GetRegion(), Location()); + + int new_level = detail.GetDetailLevel(); + + if (detail_level != new_level) { + Scene* scene = 0; + + // remove current rep from scene (if necessary): + for (int i = 0; i < detail.NumModels(detail_level); i++) { + Graphic* g = detail.GetRep(detail_level, i); + if (g) { + scene = g->GetScene(); + if (scene) + scene->DelGraphic(g); + } + } + + // switch to new rep: + detail_level = new_level; + rep = detail.GetRep(detail_level); + + // add new rep to scene (if necessary): + if (scene) { + for (int i = 0; i < detail.NumModels(detail_level); i++) { + Graphic* g = detail.GetRep(detail_level, i); + Point s = detail.GetSpin(detail_level, i); + Matrix m = cam.Orientation(); + + m.Pitch(s.x); + m.Yaw(s.z); + m.Roll(s.y); + + scene->AddGraphic(g); + g->MoveTo(cam.Pos() + detail.GetOffset(detail_level, i)); + g->SetOrientation(m); + } + + // show/hide external stores and landing gear... + if (detail.NumLevels() > 0) { + if (gear && (gear->GetState() != LandingGear::GEAR_UP)) { + for (int i = 0; i < gear->NumGear(); i++) { + Solid* g = gear->GetGear(i); + + if (g) { + if (detail_level == 0) + scene->DelGraphic(g); + else + scene->AddGraphic(g); + } + } + } + + ListIter<WeaponGroup> g = weapons; + while (++g) { + ListIter<Weapon> w = g->GetWeapons(); + while (++w) { + Solid* turret = w->GetTurret(); + if (turret) { + if (detail_level == 0) + scene->DelGraphic(turret); + else + scene->AddGraphic(turret); + + Solid* turret_base = w->GetTurretBase(); + if (turret_base) { + if (detail_level == 0) + scene->DelGraphic(turret_base); + else + scene->AddGraphic(turret_base); + } + } + if (w->IsMissile()) { + for (int i = 0; i < w->Ammo(); i++) { + Solid* store = w->GetVisibleStore(i); + if (store) { + if (detail_level == 0) + scene->DelGraphic(store); + else + scene->AddGraphic(store); + } + } + } + } + } + } + } + } + + else { + int nmodels = detail.NumModels(detail_level); + + if (nmodels > 1) { + for (int i = 0; i < nmodels; i++) { + Graphic* g = detail.GetRep(detail_level, i); + Point s = detail.GetSpin(detail_level, i); + Matrix m = cam.Orientation(); + + m.Pitch(s.x); + m.Yaw(s.z); + m.Roll(s.y); + + g->MoveTo(cam.Pos() + detail.GetOffset(detail_level, i)); + g->SetOrientation(m); + } + } + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::ShowRep() +{ + for (int i = 0; i < detail.NumModels(detail_level); i++) { + Graphic* g = detail.GetRep(detail_level, i); + g->Show(); + } + + if (gear && (gear->GetState() != LandingGear::GEAR_UP)) { + for (int i = 0; i < gear->NumGear(); i++) { + Solid* g = gear->GetGear(i); + if (g) g->Show(); + } + } + + ListIter<WeaponGroup> g = weapons; + while (++g) { + ListIter<Weapon> w = g->GetWeapons(); + while (++w) { + Solid* turret = w->GetTurret(); + if (turret) { + turret->Show(); + + Solid* turret_base = w->GetTurretBase(); + if (turret_base) + turret_base->Show(); + } + + if (w->IsMissile()) { + for (int i = 0; i < w->Ammo(); i++) { + Solid* store = w->GetVisibleStore(i); + if (store) { + store->Show(); + } + } + } + } + } +} + +void +Ship::HideRep() +{ + for (int i = 0; i < detail.NumModels(detail_level); i++) { + Graphic* g = detail.GetRep(detail_level, i); + g->Hide(); + } + + if (gear && (gear->GetState() != LandingGear::GEAR_UP)) { + for (int i = 0; i < gear->NumGear(); i++) { + Solid* g = gear->GetGear(i); + if (g) g->Hide(); + } + } + + ListIter<WeaponGroup> g = weapons; + while (++g) { + ListIter<Weapon> w = g->GetWeapons(); + while (++w) { + Solid* turret = w->GetTurret(); + if (turret) { + turret->Hide(); + + Solid* turret_base = w->GetTurretBase(); + if (turret_base) + turret_base->Hide(); + } + + if (w->IsMissile()) { + for (int i = 0; i < w->Ammo(); i++) { + Solid* store = w->GetVisibleStore(i); + if (store) { + store->Hide(); + } + } + } + } + } +} + +void +Ship::EnableShadows(bool enable) +{ + for (int i = 0; i < detail.NumModels(detail_level); i++) { + Graphic* g = detail.GetRep(detail_level, i); + + if (g->IsSolid()) { + Solid* s = (Solid*) g; + + ListIter<Shadow> iter = s->GetShadows(); + while (++iter) { + Shadow* shadow = iter.value(); + shadow->SetEnabled(enable); + } + } + } +} + +// +--------------------------------------------------------------------+ + +bool +Ship::Update(SimObject* obj) +{ + if (obj == ward) + ward = 0; + + if (obj == target) { + target = 0; + subtarget = 0; + } + + if (obj == carrier) { + carrier = 0; + dock = 0; + inbound = 0; + } + + if (obj->Type() == SimObject::SIM_SHOT || + obj->Type() == SimObject::SIM_DRONE) { + Shot* s = (Shot*) obj; + + if (sensor_drone == s) + sensor_drone = 0; + + if (decoy_list.contains(s)) + decoy_list.remove(s); + + if (threat_list.contains(s)) + threat_list.remove(s); + } + + return SimObserver::Update(obj); +} + +// +--------------------------------------------------------------------+ + +int +Ship::GetFuelLevel() const +{ + if (reactors.size() > 0) { + PowerSource* reactor = reactors[0]; + if (reactor) + return reactor->Charge(); + } + + return 0; +} + +void +Ship::SetThrottle(double percent) +{ + throttle_request = percent; + + if (throttle_request < 0) throttle_request = 0; + else if (throttle_request > 100) throttle_request = 100; + + if (throttle_request < 50) + augmenter = false; +} + +void +Ship::SetAugmenter(bool enable) +{ + if (throttle <= 50) + enable = false; + + if (main_drive && main_drive->MaxAugmenter() <= 0) + enable = false; + + augmenter = enable; +} + +// +--------------------------------------------------------------------+ + +void +Ship::SetTransition(double trans_time, int trans_type, const Point& trans_loc) +{ + transition_time = (float) trans_time; + transition_type = trans_type; + transition_loc = trans_loc; +} + +void +Ship::DropOrbit() +{ + if (IsDropship() && transition_type == TRANSITION_NONE && !IsAirborne()) { + SimRegion* dst_rgn = sim->FindNearestTerrainRegion(this); + + if (dst_rgn && + dst_rgn->GetOrbitalRegion()->Primary() == + GetRegion()->GetOrbitalRegion()->Primary()) { + + transition_time = 10.0f; + transition_type = TRANSITION_DROP_ORBIT; + transition_loc = Location() + Heading() * (-2*Radius()); + + RadioTraffic::SendQuickMessage(this, RadioMessage::BREAK_ORBIT); + SetControls(0); + } + } +} + +void +Ship::MakeOrbit() +{ + if (IsDropship() && transition_type == TRANSITION_NONE && IsAirborne()) { + transition_time = 5.0f; + transition_type = TRANSITION_MAKE_ORBIT; + transition_loc = Location() + Heading() * (-2*Radius()); + + RadioTraffic::SendQuickMessage(this, RadioMessage::MAKE_ORBIT); + SetControls(0); + } +} + +// +--------------------------------------------------------------------+ + +bool +Ship::IsInCombat() +{ + if (IsRogue()) + return true; + + bool combat = false; + + ListIter<Contact> c_iter = ContactList(); + while (++c_iter) { + Contact* c = c_iter.value(); + Ship* cship = c->GetShip(); + int ciff = c->GetIFF(this); + Point delta = c->Location() - Location(); + double dist = delta.length(); + + if (c->Threat(this) && !cship) { + if (IsStarship()) + combat = dist < 120e3; + else + combat = dist < 60e3; + } + + else if (cship && ciff > 0 && ciff != GetIFF()) { + if (IsStarship() && cship->IsStarship()) + combat = dist < 120e3; + else + combat = dist < 60e3; + } + } + + return combat; +} + +// +--------------------------------------------------------------------+ + +bool +Ship::CanTimeSkip() +{ + bool go = false; + Instruction* navpt = GetNextNavPoint(); + + if (MissionClock() < 10000 || NetGame::IsNetGame()) + return go; + + if (navpt) { + go = true; + + if (navpt->Region() != GetRegion()) + go = false; + + else if (Point(navpt->Location().OtherHand() - Location()).length() < 30e3) + go = false; + } + + if (go) + go = !IsInCombat(); + + return go; +} + +void +Ship::TimeSkip() +{ + if (CanTimeSkip()) { + // go back to regular time before performing the skip: + Game::SetTimeCompression(1); + + transition_time = 7.5f; + transition_type = TRANSITION_TIME_SKIP; + transition_loc = Location() + Heading() * (Velocity().length() * 4); + // 2500; //(8*Radius()); + + if (rand() < 16000) + transition_loc += BeamLine() * (2.5*Radius()); + else + transition_loc += BeamLine() * (-2 *Radius()); + + if (rand() < 8000) + transition_loc += LiftLine() * (-1*Radius()); + else + transition_loc += LiftLine() * (1.8*Radius()); + + SetControls(0); + } + + else if (sim->GetPlayerShip() == this) { + SetAutoNav(true); + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::DropCam(double time, double range) +{ + transition_type = TRANSITION_DROP_CAM; + + if (time > 0) + transition_time = (float) time; + else + transition_time = 10.0f; + + Point offset = Heading() * (Velocity().length() * 5); + double lateral_offset = 2 * Radius(); + double vertical_offset = Radius(); + + if (vertical_offset > 300) + vertical_offset = 300; + + if (rand() < 16000) + lateral_offset *= -1; + + if (rand() < 8000) + vertical_offset *= -1; + + offset += BeamLine() * lateral_offset; + offset += LiftLine() * vertical_offset; + + if (range > 0) + offset *= range; + + transition_loc = Location() + offset; +} + +// +--------------------------------------------------------------------+ + +void +Ship::DeathSpiral() +{ + if (!killer) + killer = new(__FILE__,__LINE__) ShipKiller(this); + + ListIter<System> iter = systems; + while (++iter) + iter->PowerOff(); + + // transfer arcade velocity to newtonian velocity: + if (flight_model >= 2) { + velocity += arcade_velocity; + } + + if (GetIFF() < 100 && !IsGroundUnit()) { + RadioTraffic::SendQuickMessage(this, RadioMessage::DISTRESS); + } + + transition_type = TRANSITION_DEATH_SPIRAL; + + killer->BeginDeathSpiral(); + + transition_time = killer->TransitionTime(); + transition_loc = killer->TransitionLoc(); +} + +// +--------------------------------------------------------------------+ + +void +Ship::CompleteTransition() +{ + int old_type = transition_type; + transition_time = 0.0f; + transition_type = TRANSITION_NONE; + + switch (old_type) { + case TRANSITION_NONE: + case TRANSITION_DROP_CAM: + default: + return; + + case TRANSITION_DROP_ORBIT: { + SetControls(0); + SimRegion* dst_rgn = sim->FindNearestTerrainRegion(this); + Point dst_loc = Location().OtherHand() * 0.20; + dst_loc.x += 6000 * GetElementIndex(); + dst_loc.z = TERRAIN_ALTITUDE_LIMIT * 0.95; + dst_loc += RandomDirection() * 2e3; + + sim->RequestHyperJump(this, dst_rgn, dst_loc, TRANSITION_DROP_ORBIT); + + ShipStats* stats = ShipStats::Find(Name()); + stats->AddEvent(SimEvent::BREAK_ORBIT, dst_rgn->Name()); + } + break; + + case TRANSITION_MAKE_ORBIT: { + SetControls(0); + SimRegion* dst_rgn = sim->FindNearestSpaceRegion(this); + double dist = 200.0e3 + 10.0e3 * GetElementIndex(); + Point esc_vec = dst_rgn->GetOrbitalRegion()->Location() - + dst_rgn->GetOrbitalRegion()->Primary()->Location(); + + esc_vec.z = -100 * GetElementIndex(); + esc_vec.Normalize(); + esc_vec *= -dist; + esc_vec += RandomDirection() * 2e3; + + sim->RequestHyperJump(this, dst_rgn, esc_vec, TRANSITION_MAKE_ORBIT); + + ShipStats* stats = ShipStats::Find(Name()); + stats->AddEvent(SimEvent::MAKE_ORBIT, dst_rgn->Name()); + } + break; + + case TRANSITION_TIME_SKIP: { + Instruction* navpt = GetNextNavPoint(); + + if (navpt) { + Point delta = navpt->Location().OtherHand() - Location(); + Point unit = delta; unit.Normalize(); + Point trans = delta + unit * -20e3; + double dist = trans.length(); + double speed = navpt->Speed(); + + if (speed < 50) speed = 500; + + double etr = dist / speed; + + sim->ResolveTimeSkip(etr); + } + } + break; + + case TRANSITION_DEATH_SPIRAL: + SetControls(0); + transition_type = TRANSITION_DEAD; + break; + } + +} + +bool +Ship::IsAirborne() const +{ + if (region) + return region->Type() == SimRegion::AIR_SPACE; + + return false; +} + +double +Ship::CompassHeading() const +{ + Point heading = Heading(); + double compass_heading = atan2(fabs(heading.x), heading.z); + + if (heading.x < 0) + compass_heading *= -1; + + double result = compass_heading + PI; + + if (result >= 2*PI) + result -= 2*PI; + + return result; +} + +double +Ship::CompassPitch() const +{ + Point heading = Heading(); + return asin(heading.y); +} + +double +Ship::AltitudeMSL() const +{ + return Location().y; +} + +double +Ship::AltitudeAGL() const +{ + if (altitude_agl < -1000) { + Ship* pThis = (Ship*) this; // cast-away const + Point loc = Location(); + + Terrain* terrain = region->GetTerrain(); + + if (terrain) + pThis->altitude_agl = (float) (loc.y - terrain->Height(loc.x, loc.z)); + + else + pThis->altitude_agl = (float) loc.y; + + if (!_finite(altitude_agl)) { + pThis->altitude_agl = 0.0f; + } + } + + return altitude_agl; +} + +double +Ship::GForce() const +{ + return g_force; +} + +// +--------------------------------------------------------------------+ + +WeaponGroup* +Ship::FindWeaponGroup(const char* name) +{ + WeaponGroup* group = 0; + + ListIter<WeaponGroup> iter = weapons; + while (!group && ++iter) + if (!_stricmp(iter->Name(), name)) + group = iter.value(); + + if (!group) { + group = new(__FILE__,__LINE__) WeaponGroup(name); + weapons.append(group); + } + + return group; +} + +void +Ship::SelectWeapon(int n, int w) +{ + if (n < weapons.size()) + weapons[n]->SelectWeapon(w); +} + +// +--------------------------------------------------------------------+ + +void +Ship::CyclePrimary() +{ + if (weapons.isEmpty()) + return; + + if (IsDropship() && primary < weapons.size()) { + WeaponGroup* p = weapons[primary]; + Weapon* w = p->GetSelected(); + + if (w && w->GetTurret()) { + p->SetFiringOrders(Weapon::POINT_DEFENSE); + } + } + + int n = primary + 1; + while (n != primary) { + if (n >= weapons.size()) + n = 0; + + if (weapons[n]->IsPrimary()) { + weapons[n]->SetFiringOrders(Weapon::MANUAL); + break; + } + + n++; + } + + primary = n; +} + +// +--------------------------------------------------------------------+ + +void +Ship::CycleSecondary() +{ + if (weapons.isEmpty()) + return; + + int n = secondary + 1; + while (n != secondary) { + if (n >= weapons.size()) + n = 0; + + if (weapons[n]->IsMissile()) + break; + + n++; + } + + secondary = n; + + // automatically switch sensors to appropriate mode: + if (IsAirborne()) { + Weapon* missile = GetSecondary(); + if (missile && missile->CanTarget(Ship::GROUND_UNITS)) + SetSensorMode(Sensor::GM); + else if (sensor && sensor->GetMode() == Sensor::GM) + SetSensorMode(Sensor::STD); + } +} + +int +Ship::GetMissileEta(int index) const +{ + if (index >= 0 && index < 4) + return missile_eta[index]; + + return 0; +} + +void +Ship::SetMissileEta(int id, int eta) +{ + int index = -1; + + // are we tracking this missile's eta? + for (int i = 0; i < 4; i++) + if (id == missile_id[i]) + index = i; + + // if not, can we find an open slot to track it in? + if (index < 0) { + for (int i = 0; i < 4 && index < 0; i++) { + if (missile_eta[i] == 0) { + index = i; + missile_id[i] = id; + } + } + } + + // track the eta: + if (index >= 0 && index < 4) { + if (eta > 3599) + eta = 3599; + + missile_eta[index] = (BYTE) eta; + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::DoEMCON() +{ + ListIter<System> iter = systems; + while (++iter) { + System* s = iter.value(); + s->DoEMCON(emcon); + } + + old_emcon = emcon; +} + +// +--------------------------------------------------------------------+ + +double +Ship::Thrust(double seconds) const +{ + double total_thrust = 0; + + if (main_drive) { + // velocity limiter: + Point H = Heading(); + Point V = Velocity(); + double vmag = V.Normalize(); + double eff_throttle = throttle; + double thrust_factor = 1; + double vfwd = H * V; + bool aug_on = main_drive->IsAugmenterOn(); + + if (vmag > vlimit && vfwd > 0) { + double vmax = vlimit; + if (aug_on) + vmax *= 1.5; + + vfwd = 0.5 * vfwd + 0.5; + + // reduce drive efficiency at high fwd speed: + thrust_factor = (vfwd * pow(vmax,3) / pow(vmag,3)) + (1-vfwd); + } + + if (flcs) + eff_throttle = flcs->Throttle(); + + // square-law throttle curve to increase sensitivity + // at lower throttle settings: + if (flight_model > 1) { + eff_throttle /= 100; + eff_throttle *= eff_throttle; + eff_throttle *= 100; + } + + main_drive->SetThrottle(eff_throttle, augmenter); + total_thrust += thrust_factor * main_drive->Thrust(seconds); + + if (aug_on && shake < 1.5) + ((Ship*) this)->shake = 1.5f; + } + + return total_thrust; +} + +// +--------------------------------------------------------------------+ + +void +Ship::CycleFLCSMode() +{ + switch (flcs_mode) { + case FLCS_MANUAL: SetFLCSMode(FLCS_HELM); break; + case FLCS_AUTO: SetFLCSMode(FLCS_MANUAL); break; + case FLCS_HELM: SetFLCSMode(FLCS_AUTO); break; + + default: + if (IsStarship()) + flcs_mode = (BYTE) FLCS_HELM; + else + flcs_mode = (BYTE) FLCS_AUTO; + break; + } + + // reset helm heading to compass heading when switching + // back to helm mode from manual mode: + + if (flcs_mode == FLCS_HELM) { + if (IsStarship()) { + SetHelmHeading(CompassHeading()); + SetHelmPitch(CompassPitch()); + } + else { + flcs_mode = (BYTE) FLCS_AUTO; + } + } +} + +void +Ship::SetFLCSMode(int mode) +{ + flcs_mode = (BYTE) mode; + + if (IsAirborne()) + flcs_mode = (BYTE) FLCS_MANUAL; + + if (dir && dir->Type() < SteerAI::SEEKER) { + switch (flcs_mode) { + case FLCS_MANUAL: director_info = Game::GetText("flcs.manual"); break; + case FLCS_AUTO: director_info = Game::GetText("flcs.auto"); break; + case FLCS_HELM: director_info = Game::GetText("flcs.helm"); break; + default: director_info = Game::GetText("flcs.fault"); break; + } + + if (!flcs || !flcs->IsPowerOn()) + director_info = Game::GetText("flcs.offline"); + + else if (IsAirborne()) + director_info = Game::GetText("flcs.atmospheric"); + } + + if (flcs) + flcs->SetMode(mode); +} + +int +Ship::GetFLCSMode() const +{ + return (int) flcs_mode; +} + +void +Ship::SetTransX(double t) +{ + float limit = design->trans_x; + + if (thruster) + limit = (float) thruster->TransXLimit(); + + trans_x = (float) t; + + if (trans_x) { + if (trans_x > limit) + trans_x = limit; + else if (trans_x < -limit) + trans_x = -limit; + + // reduce thruster efficiency at high fwd speed: + double vfwd = cam.vrt() * Velocity(); + double vmag = fabs(vfwd); + if (vmag > vlimit) { + if (trans_x > 0 && vfwd > 0 || trans_x < 0 && vfwd < 0) + trans_x *= (float) (pow(vlimit,4) / pow(vmag,4)); + } + } +} + +void +Ship::SetTransY(double t) +{ + float limit = design->trans_y; + + if (thruster) + limit = (float) thruster->TransYLimit(); + + trans_y = (float) t; + + if (trans_y) { + double vmag = Velocity().length(); + + if (trans_y > limit) + trans_y = limit; + else if (trans_y < -limit) + trans_y = -limit; + + // reduce thruster efficiency at high fwd speed: + if (vmag > vlimit) { + double vfwd = cam.vpn() * Velocity(); + + if (trans_y > 0 && vfwd > 0 || trans_y < 0 && vfwd < 0) + trans_y *= (float) (pow(vlimit,4) / pow(vmag,4)); + } + } +} + +void +Ship::SetTransZ(double t) +{ + float limit = design->trans_z; + + if (thruster) + limit = (float) thruster->TransZLimit(); + + trans_z = (float) t; + + if (trans_z) { + + if (trans_z > limit) + trans_z = limit; + else if (trans_z < -limit) + trans_z = -limit; + + // reduce thruster efficiency at high fwd speed: + double vfwd = cam.vup() * Velocity(); + double vmag = fabs(vfwd); + if (vmag > vlimit) { + if (trans_z > 0 && vfwd > 0 || trans_z < 0 && vfwd < 0) + trans_z *= (float) (pow(vlimit,4) / pow(vmag,4)); + } + } +} + +void +Ship::ExecFLCSFrame() +{ + if (flcs) + flcs->ExecSubFrame(); +} + +// +--------------------------------------------------------------------+ + +void +Ship::SetHelmHeading(double h) +{ + while (h < 0) + h += 2*PI; + + while (h >= 2*PI) + h -= 2*PI; + + helm_heading = (float) h; +} + +void +Ship::SetHelmPitch(double p) +{ + const double PITCH_LIMIT = 80 * DEGREES; + + if (p < -PITCH_LIMIT) + p = -PITCH_LIMIT; + + else if (p > PITCH_LIMIT) + p = PITCH_LIMIT; + + helm_pitch = (float) p; +} + +void +Ship::ApplyHelmYaw(double y) +{ + // rotate compass into helm-relative orientation: + double compass = CompassHeading() - helm_heading; + double turn = y * PI/4; + + if (compass > PI) + compass -= 2*PI; + else if (compass < -PI) + compass += 2*PI; + + // if requested turn is more than 170, reject it: + if (fabs(compass + turn) > 170*DEGREES) + return; + + SetHelmHeading(helm_heading + turn); +} + +void +Ship::ApplyHelmPitch(double p) +{ + SetHelmPitch(helm_pitch - p * PI/4); +} + +void +Ship::ApplyPitch(double p) +{ + if (flight_model == 0) { // standard flight model + if (IsAirborne()) + p *= 0.5; + + // command for pitch up is negative + if (p < 0) { + if (alpha > PI/6) { + p *= 0.05; + } + else if (g_force > 12.0) { + double limit = 0.5 - (g_force - 12.0)/10.0; + + if (limit < 0) + p = 0; + else + p *= limit; + } + } + + // command for pitch down is positive + else if (p > 0) { + if (alpha < -PI/8) { + p *= 0.05; + } + else if (g_force < -3) { + p *= 0.1; + } + } + } + + Physical::ApplyPitch(p); +} + +// +--------------------------------------------------------------------+ + +bool +Ship::FireWeapon(int n) +{ + bool fired = false; + + if (n >= 0 && !CheckFire()) { + if (n < 4) + trigger[n] = true; + + if (n < weapons.size()) { + weapons[n]->SetTrigger(true); + fired = weapons[n]->GetTrigger(); + } + } + + if (!fired && sim->GetPlayerShip() == this) + Button::PlaySound(Button::SND_REJECT); + + return fired; +} + +bool +Ship::FireDecoy() +{ + Shot* drone = 0; + + if (decoy && !CheckFire()) { + drone = decoy->Fire(); + + if (drone) { + Observe(drone); + decoy_list.append(drone); + } + } + + if (sim->GetPlayerShip() == this) { + if (NetGame::IsNetGame()) { + if (decoy && decoy->Ammo() < 1) + Button::PlaySound(Button::SND_REJECT); + } + + else if (!drone) { + Button::PlaySound(Button::SND_REJECT); + } + } + + return drone != 0; +} + +void +Ship::AddActiveDecoy(Drone* drone) +{ + if (drone) { + Observe(drone); + decoy_list.append(drone); + } +} + +Weapon* +Ship::GetPrimary() const +{ + if (weapons.size() > primary) + return weapons[primary]->GetSelected(); + return 0; +} + +Weapon* +Ship::GetSecondary() const +{ + if (weapons.size() > secondary) + return weapons[secondary]->GetSelected(); + return 0; +} + +Weapon* +Ship::GetWeaponByIndex(int n) +{ + for (int i = 0; i < weapons.size(); i++) { + WeaponGroup* g = weapons[i]; + + List<Weapon>& wlist = g->GetWeapons(); + for (int j = 0; j < wlist.size(); j++) { + Weapon* w = wlist[j]; + + if (w->GetIndex() == n) { + return w; + } + } + } + + return 0; +} + +WeaponGroup* +Ship::GetPrimaryGroup() const +{ + if (weapons.size() > primary) + return weapons[primary]; + return 0; +} + +WeaponGroup* +Ship::GetSecondaryGroup() const +{ + if (weapons.size() > secondary) + return weapons[secondary]; + return 0; +} + +WeaponDesign* +Ship::GetPrimaryDesign() const +{ + if (weapons.size() > primary) + return (WeaponDesign*) weapons[primary]->GetSelected()->Design(); + return 0; +} + +WeaponDesign* +Ship::GetSecondaryDesign() const +{ + if (weapons.size() > secondary) + return (WeaponDesign*) weapons[secondary]->GetSelected()->Design(); + return 0; +} + +Weapon* +Ship::GetDecoy() const +{ + return decoy; +} + +List<Shot>& +Ship::GetActiveDecoys() +{ + return decoy_list; +} + +List<Shot>& +Ship::GetThreatList() +{ + return threat_list; +} + +void +Ship::AddThreat(Shot* s) +{ + if (!threat_list.contains(s)) { + Observe(s); + threat_list.append(s); + } +} + +void +Ship::DropThreat(Shot* s) +{ + if (threat_list.contains(s)) { + threat_list.remove(s); + } +} + +bool +Ship::GetTrigger(int i) const +{ + if (i >= 0) { + if (i < 4) + return trigger[i]; + + else if (i < weapons.size()) + return weapons[i]->GetTrigger(); + } + + return false; +} + +void +Ship::SetTrigger(int i) +{ + if (i >= 0 && !CheckFire()) { + if (i < 4) + trigger[i] = true; + + if (i < weapons.size()) + weapons[i]->SetTrigger(); + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::SetSensorMode(int mode) +{ + if (sensor) + sensor->SetMode((Sensor::Mode) mode); +} + +int +Ship::GetSensorMode() const +{ + if (sensor) + return (int) sensor->GetMode(); + + return 0; +} + +// +--------------------------------------------------------------------+ + +bool +Ship::IsTracking(SimObject* tgt) +{ + if (tgt && sensor) + return sensor->IsTracking(tgt); + + return false; +} + +// +--------------------------------------------------------------------+ + +void +Ship::LockTarget(int type, bool closest, bool hostile) +{ + if (sensor) + SetTarget(sensor->LockTarget(type, closest, hostile)); +} + +// +--------------------------------------------------------------------+ + +void +Ship::LockTarget(SimObject* candidate) +{ + if (sensor) + SetTarget(sensor->LockTarget(candidate)); + else + SetTarget(candidate); +} + +// +--------------------------------------------------------------------+ + +double +Ship::InflictDamage(double damage, Shot* shot, int hit_type, Point impact) +{ + double damage_applied = 0; + + if (Game::Paused() || IsNetObserver() || IsInvulnerable()) + return damage_applied; + + if (Integrity() == 0) // already dead? + return damage_applied; + + const double MAX_SHAKE = 7; + double hull_damage = damage; + bool hit_shield = (hit_type & HIT_SHIELD) != 0; + bool hit_hull = (hit_type & HIT_HULL) != 0; + bool hit_turret = (hit_type & HIT_TURRET) != 0; + + if (impact == Point(0,0,0)) + impact = Location(); + + if (hit_shield && ShieldStrength() > 0) { + hull_damage = shield->DeflectDamage(shot, damage); + + if (shot) { + if (shot->IsBeam()) { + if (design->beam_hit_sound_resource) { + if (Game::RealTime() - last_beam_time > 400) { + Sound* s = design->beam_hit_sound_resource->Duplicate(); + s->SetLocation(impact); + s->SetVolume(AudioConfig::EfxVolume()); + s->Play(); + + last_beam_time = Game::RealTime(); + } + } + } + + else { + if (design->bolt_hit_sound_resource) { + if (Game::RealTime() - last_bolt_time > 400) { + Sound* s = design->bolt_hit_sound_resource->Duplicate(); + s->SetLocation(impact); + s->SetVolume(AudioConfig::EfxVolume()); + s->Play(); + + last_bolt_time = Game::RealTime(); + } + } + } + } + } + + if (hit_hull) { + hull_damage = InflictSystemDamage(hull_damage, shot, impact); + + int damage_type = WeaponDesign::DMG_NORMAL; + + if (shot && shot->Design()) + damage_type = shot->Design()->damage_type; + + if (damage_type == WeaponDesign::DMG_NORMAL) { + damage_applied = hull_damage; + Physical::InflictDamage(damage_applied, 0); + NetUtil::SendObjDamage(this, damage_applied, shot); + } + } + + else if (hit_turret) { + hull_damage = InflictSystemDamage(hull_damage, shot, impact) * 0.3; + + int damage_type = WeaponDesign::DMG_NORMAL; + + if (shot && shot->Design()) + damage_type = shot->Design()->damage_type; + + if (damage_type == WeaponDesign::DMG_NORMAL) { + damage_applied = hull_damage; + Physical::InflictDamage(damage_applied, 0); + NetUtil::SendObjDamage(this, damage_applied, shot); + } + } + + // shake by percentage of maximum damage + double newshake = 50 * damage/design->integrity; + + if (shake < MAX_SHAKE) shake += (float) newshake; + if (shake > MAX_SHAKE) shake = (float) MAX_SHAKE; + + // start fires as needed: + if ((IsStarship() || IsGroundUnit() || RandomChance(1,3)) && hit_hull && damage_applied > 0) { + int old_integrity = (int) ((integrity + damage_applied)/design->integrity * 10); + int new_integrity = (int) ((integrity )/design->integrity * 10); + + if (new_integrity < 5 && new_integrity < old_integrity) { + // need accurate hull impact for starships, + if (rep) { + Point detonation = impact*2 - Location(); + Point direction = Location() - detonation; + double distance = direction.Normalize() * 3; + rep->CheckRayIntersection(detonation, direction, distance, impact); + + // pull fire back into hull a bit: + direction = Location() - impact; + impact += direction * 0.2; + + float scale = (float) design->scale; + + if (IsDropship()) + sim->CreateExplosion(impact, Velocity(), Explosion::SMOKE_TRAIL, 0.01f * scale, 0.5f * scale, region, this); + else + sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FIRE, 0.10f * scale, scale, region, this); + } + } + } + + return damage_applied; +} + +double +Ship::InflictSystemDamage(double damage, Shot* shot, Point impact) +{ + if (IsNetObserver()) + return 0; + + // find the system that is closest to the impact point: + System* system = 0; + double distance = 1e6; + double blast_radius = 0; + int dmg_type = 0; + + if (shot) + dmg_type = shot->Design()->damage_type; + + bool dmg_normal = dmg_type == WeaponDesign::DMG_NORMAL; + bool dmg_power = dmg_type == WeaponDesign::DMG_POWER; + bool dmg_emp = dmg_type == WeaponDesign::DMG_EMP; + double to_level = 0; + + if (dmg_power) { + to_level = 1 - damage / 1e4; + + if (to_level < 0) + to_level = 0; + } + + // damage caused by weapons applies to closest system: + if (shot) { + if (shot->IsMissile()) + blast_radius = 300; + + ListIter<System> iter = systems; + while (++iter) { + System* candidate = iter.value(); + double sysrad = candidate->Radius(); + + if (dmg_power) + candidate->DrainPower(to_level); + + if (sysrad > 0 || dmg_emp && candidate->IsPowerCritical()) { + double test_distance = (impact - candidate->MountLocation()).length(); + + if ((test_distance-blast_radius) < sysrad || dmg_emp && candidate->IsPowerCritical()) { + if (test_distance < distance) { + system = candidate; + distance = test_distance; + } + } + } + } + + // if a system was in range of the blast, assess the damage: + if (system) { + double hull_damage = damage * system->HullProtection(); + double sys_damage = damage - hull_damage; + double avail = system->Availability(); + + if (dmg_normal || system->IsPowerCritical() && dmg_emp) { + system->ApplyDamage(sys_damage); + NetUtil::SendSysDamage(this, system, sys_damage); + + master_caution = true; + + if (dmg_normal) { + if (sys_damage < 100) + damage -= sys_damage; + else + damage -= 100; + } + + if (system->GetExplosionType() && (avail - system->Availability()) >= 50) { + float scale = design->explosion_scale; + if (scale <= 0) + scale = design->scale; + + sim->CreateExplosion(system->MountLocation(), Velocity() * 0.7f, system->GetExplosionType(), 0.2f * scale, scale, region, this, system); + } + } + } + } + + // damage caused by collision applies to all systems: + else { + // ignore incidental bumps: + if (damage < 100) + return damage; + + ListIter<System> iter = systems; + while (++iter) { + System* sys = iter.value(); + + if (rand() > 24000) { + double base_damage = 33.0 + rand()/1000.0; + double sys_damage = base_damage * (1.0 - sys->HullProtection()); + sys->ApplyDamage(sys_damage); + NetUtil::SendSysDamage(this, system, sys_damage); + damage -= sys_damage; + + master_caution = true; + } + } + + // just in case this ship has lots of systems... + if (damage < 0) + damage = 0; + } + + // return damage remaining + return damage; +} + +// +--------------------------------------------------------------------+ + +int +Ship::ShieldStrength() const +{ + if (!shield) return 0; + + return (int) shield->ShieldLevel(); +} + +int +Ship::HullStrength() const +{ + if (design) + return (int) (Integrity() / design->integrity * 100); + + return 10; +} + +// +--------------------------------------------------------------------+ + +System* +Ship::GetSystem(int sys_id) +{ + System* s = 0; + + if (sys_id >= 0) { + if (sys_id < systems.size()) { + s = systems[sys_id]; + if (s->GetID() == sys_id) + return s; + } + + ListIter<System> iter = systems; + while (++iter) { + s = iter.value(); + + if (s->GetID() == sys_id) + return s; + } + } + + return 0; +} + +// +--------------------------------------------------------------------+ + +void +Ship::RepairSystem(System* sys) +{ + if (!repair_queue.contains(sys)) { + repair_queue.append(sys); + sys->Repair(); + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::IncreaseRepairPriority(int task_index) +{ + if (task_index > 0 && task_index < repair_queue.size()) { + System* task1 = repair_queue.at(task_index-1); + System* task2 = repair_queue.at(task_index); + + repair_queue.at(task_index-1) = task2; + repair_queue.at(task_index) = task1; + } +} + +void +Ship::DecreaseRepairPriority(int task_index) +{ + if (task_index >= 0 && task_index < repair_queue.size()-1) { + System* task1 = repair_queue.at(task_index); + System* task2 = repair_queue.at(task_index+1); + + repair_queue.at(task_index) = task2; + repair_queue.at(task_index+1) = task1; + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::ExecMaintFrame(double seconds) +{ + // is it already too late? + if (life == 0 || integrity < 1) return; + + const DWORD REPAIR_FREQUENCY = 5000; // once every five seconds + static DWORD last_repair_frame = 0; // one ship per game frame + + if (auto_repair && + Game::GameTime() - last_repair_time > REPAIR_FREQUENCY && + last_repair_frame != Game::Frame()) { + + last_repair_time = Game::GameTime(); + last_repair_frame = Game::Frame(); + + ListIter<System> iter = systems; + while (++iter) { + System* sys = iter.value(); + + if (sys->Status() != System::NOMINAL) { + bool started_repairs = false; + + // emergency power routing: + if (sys->Type() == System::POWER_SOURCE && sys->Availability() < 33) { + PowerSource* src = (PowerSource*) sys; + PowerSource* dst = 0; + + for (int i = 0; i < reactors.size(); i++) { + PowerSource* pwr = reactors[i]; + + if (pwr != src && pwr->Availability() > src->Availability()) { + if (!dst || + (pwr->Availability() > dst->Availability() && + pwr->Charge() > dst->Charge())) + dst = pwr; + } + } + + if (dst) { + while (src->Clients().size() > 0) { + System* s = src->Clients().at(0); + src->RemoveClient(s); + dst->AddClient(s); + } + } + } + + ListIter<Component> comp = sys->GetComponents(); + while (++comp) { + Component* c = comp.value(); + + if (c->Status() < Component::NOMINAL && c->Availability() < 75) { + if (c->SpareCount() && + c->ReplaceTime() <= 300 && + (c->Availability() < 50 || + c->ReplaceTime() < c->RepairTime())) { + + c->Replace(); + started_repairs = true; + } + + else if (c->Availability() >= 50 || c->NumJerried() < 5) { + c->Repair(); + started_repairs = true; + } + } + } + + if (started_repairs) + RepairSystem(sys); + } + } + } + + if (repair_queue.size() > 0 && RepairTeams() > 0) { + int team = 0; + ListIter<System> iter = repair_queue; + while (++iter && team < RepairTeams()) { + System* sys = iter.value(); + + sys->ExecMaintFrame(seconds * RepairSpeed()); + team++; + + if (sys->Status() != System::MAINT) { + iter.removeItem(); + + // emergency power routing (restore): + if (sys->Type() == System::POWER_SOURCE && + sys->Status() == System::NOMINAL) { + PowerSource* src = (PowerSource*) sys; + int isrc = reactors.index(src); + + for (int i = 0; i < reactors.size(); i++) { + PowerSource* pwr = reactors[i]; + + if (pwr != src) { + List<System> xfer; + + for (int j = 0; j < pwr->Clients().size(); j++) { + System* s = pwr->Clients().at(j); + + if (s->GetSourceIndex() == isrc) { + xfer.append(s); + } + } + + for (int j = 0; j < xfer.size(); j++) { + System* s = xfer.at(j); + pwr->RemoveClient(s); + src->AddClient(s); + } + } + } + } + } + } + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::SetNetworkControl(Director* net) +{ + net_control = net; + + delete dir; + dir = 0; + + if (!net_control && GetIFF() < 100) { + if (IsStatic()) + dir = 0; + else if (IsStarship()) + dir = SteerAI::Create(this, SteerAI::STARSHIP); + else + dir = SteerAI::Create(this, SteerAI::FIGHTER); + } +} + +void +Ship::SetControls(MotionController* m) +{ + if (IsDropping() || IsAttaining()) { + if (dir && dir->Type() != DropShipAI::DIR_TYPE) { + delete dir; + dir = new(__FILE__,__LINE__) DropShipAI(this); + } + + return; + } + + else if (IsSkipping()) { + if (navsys && sim->GetPlayerShip() == this) + navsys->EngageAutoNav(); + } + + else if (IsDying() || IsDead()) { + if (dir) { + delete dir; + dir = 0; + } + + if (navsys && navsys->AutoNavEngaged()) { + navsys->DisengageAutoNav(); + } + + return; + } + + else if (life == 0) { + if (dir || navsys) { + ::Print("Warning: dying ship '%' still has not been destroyed!\n", name); + delete dir; + dir = 0; + + if (navsys && navsys->AutoNavEngaged()) + navsys->DisengageAutoNav(); + } + + return; + } + + if (navsys && navsys->AutoNavEngaged()) { + NavAI* nav = 0; + + if (dir) { + if (dir->Type() != NavAI::DIR_TYPE) { + delete dir; + dir = 0; + } + else { + nav = (NavAI*) dir; + } + } + + if (!nav) { + nav = new(__FILE__,__LINE__) NavAI(this); + dir = nav; + return; + } + + else if (!nav->Complete()) { + return; + } + } + + if (dir) { + delete dir; + dir = 0; + } + + if (m) { + Keyboard::FlushKeys(); + m->Acquire(); + dir = new(__FILE__,__LINE__) ShipCtrl(this, m); + director_info = Game::GetText("flcs.auto"); + } + else if (GetIFF() < 100) { + if (IsStatic()) + dir = SteerAI::Create(this, SteerAI::GROUND); + + else if (IsStarship() && !IsAirborne()) + dir = SteerAI::Create(this, SteerAI::STARSHIP); + + else + dir = SteerAI::Create(this, SteerAI::FIGHTER); + } +} + +// +--------------------------------------------------------------------+ + +Color +Ship::IFFColor(int iff) +{ + Color c; + + switch (iff) { + case 0: // NEUTRAL, NON-COMBAT + c = Color(192,192,192); + break; + + case 1: // TERELLIAN ALLIANCE + c = Color(70,70,220); + break; + + case 2: // MARAKAN HEGEMONY + c = Color(220,20,20); + break; + + case 3: // BROTHERHOOD OF IRON + c = Color(200,180,20); + break; + + case 4: // ZOLON EMPIRE + c = Color(20,200,20); + break; + + case 5: + c = Color(128, 0, 128); + break; + + case 6: + c = Color(40,192,192); + break; + + default: + c = Color(128,128,128); + break; + } + + return c; +} + +Color +Ship::MarkerColor() const +{ + return IFFColor(IFF_code); +} + +// +--------------------------------------------------------------------+ + +void +Ship::SetIFF(int iff) +{ + IFF_code = iff; + + if (hangar) + hangar->SetAllIFF(iff); + + DropTarget(); + + if (dir && dir->Type() >= 1000) { + SteerAI* ai = (SteerAI*) dir; + ai->DropTarget(); + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::SetRogue(bool r) +{ + bool rogue = IsRogue(); + + ff_count = r ? 1000 : 0; + + if (!rogue && IsRogue()) { + Print("Ship '%s' has been made rogue\n", Name()); + } + else if (rogue && !IsRogue()) { + Print("Ship '%s' is no longer rogue\n", Name()); + } +} + +void +Ship::SetFriendlyFire(int f) +{ + bool rogue = IsRogue(); + + ff_count = f; + + if (!rogue && IsRogue()) { + Print("Ship '%s' has been made rogue with ff_count = %d\n", Name(), ff_count); + } + else if (rogue && !IsRogue()) { + Print("Ship '%s' is no longer rogue\n", Name()); + } +} + +void +Ship::IncFriendlyFire(int f) +{ + if (f > 0) { + bool rogue = IsRogue(); + + ff_count += f; + + if (!rogue && IsRogue()) { + Print("Ship '%s' has been made rogue with ff_count = %d\n", Name(), ff_count); + } + } +} + +// +--------------------------------------------------------------------+ + +void +Ship::SetEMCON(int e, bool from_net) +{ + if (e < 1) emcon = 1; + else if (e > 3) emcon = 3; + else emcon = (BYTE) e; + + if (emcon != old_emcon && !from_net && NetGame::GetInstance()) + NetUtil::SendObjEmcon(this); +} + +double +Ship::PCS() const +{ + double e_factor = design->e_factor[emcon-1]; + + if (IsAirborne() && !IsGroundUnit()) { + if (AltitudeAGL() < 40) + return 0; + + if (AltitudeAGL() < 200) { + double clutter = AltitudeAGL() / 200; + return clutter * e_factor; + } + } + + return e_factor * pcs; +} + +double +Ship::ACS() const +{ + if (IsAirborne() && !IsGroundUnit()) { + if (AltitudeAGL() < 40) + return 0; + + if (AltitudeAGL() < 200) { + double clutter = AltitudeAGL() / 200; + return clutter * acs; + } + } + + return acs; +} + +DWORD +Ship::MissionClock() const +{ + if (launch_time > 0) + return Game::GameTime() + 1 - launch_time; + + return 0; +} + +// +----------------------------------------------------------------------+ + +Instruction* +Ship::GetRadioOrders() const +{ + return radio_orders; +} + +void +Ship::ClearRadioOrders() +{ + if (radio_orders) { + radio_orders->SetAction(0); + radio_orders->ClearTarget(); + radio_orders->SetLocation(Point()); + } +} + +void +Ship::HandleRadioMessage(RadioMessage* msg) +{ + if (!msg) return; + + static RadioHandler rh; + + if (rh.ProcessMessage(msg, this)) + rh.AcknowledgeMessage(msg, this); +} + +// +----------------------------------------------------------------------+ + +int +Ship::Value() const +{ + return Value(design->type); +} + +// +----------------------------------------------------------------------+ + +int +Ship::Value(int type) +{ + int value = 0; + + switch (type) { + case DRONE: value = 10; break; + case FIGHTER: value = 20; break; + case ATTACK: value = 40; break; + case LCA: value = 50; break; + + case COURIER: value = 100; break; + case CARGO: value = 100; break; + case CORVETTE: value = 100; break; + case FREIGHTER: value = 250; break; + case FRIGATE: value = 200; break; + case DESTROYER: value = 500; break; + case CRUISER: value = 800; break; + case BATTLESHIP: value = 1000; break; + case CARRIER: value = 1500; break; + case SWACS: value = 500; break; + case DREADNAUGHT: value = 1500; break; + + case STATION: value = 2500; break; + case FARCASTER: value = 5000; break; + + case MINE: value = 20; break; + case COMSAT: value = 200; break; + case DEFSAT: value = 300; break; + + case BUILDING: value = 100; break; + case FACTORY: value = 250; break; + case SAM: value = 100; break; + case EWR: value = 200; break; + case C3I: value = 500; break; + case STARBASE: value = 2000; break; + + default: value = 100; break; + } + + return value; +} + +// +----------------------------------------------------------------------+ + +double +Ship::AIValue() const +{ + int i = 0; + double value = 0; + + for (i = 0; i < reactors.size(); i++) { + const PowerSource* r = reactors[i]; + value += r->Value(); + } + + for (i = 0; i < drives.size(); i++) { + const Drive* d = drives[i]; + value += d->Value(); + } + + for (i = 0; i < weapons.size(); i++) { + const WeaponGroup* w = weapons[i]; + value += w->Value(); + } + + return value; +} |