void Projectile::CheckLock(const Ship &target) { double base = hasLock ? 1. : .5; hasLock = false; // For each tracking type, calculate the probability that a lock will be // lost in a given five-second period. Then, since this check is done every // second, test against the fifth root of that probability. if(weapon->Tracking()) hasLock |= Check(weapon->Tracking(), base); // Optical tracking is about 15% for interceptors and 75% for medium warships. if(weapon->OpticalTracking()) { double weight = target.Mass() * target.Mass(); double probability = weapon->OpticalTracking() * weight / (200000. + weight); hasLock |= Check(probability, base); } // Infrared tracking is 10% when heat is zero and 100% when heat is full. if(weapon->InfraredTracking()) { double probability = weapon->InfraredTracking() * min(1., target.Heat() + .1); hasLock |= Check(probability, base); } // Radar tracking depends on whether the target ship has jamming capabilities. // Jamming of 1 is enough to increase your chance of dodging to 50%. if(weapon->RadarTracking()) { double probability = weapon->RadarTracking() / (1. + target.Attributes().Get("radar jamming")); hasLock |= Check(probability, base); } }
void ShipInfoDisplay::UpdateAttributes(const Ship &ship) { bool isGeneric = ship.Name().empty() || ship.GetPlanet(); attributeLabels.clear(); attributeValues.clear(); attributesHeight = 20; const Outfit &attributes = ship.Attributes(); attributeLabels.push_back("cost:"); attributeValues.push_back(Format::Number(ship.Cost())); attributesHeight += 20; attributeLabels.push_back(string()); attributeValues.push_back(string()); attributesHeight += 10; if(attributes.Get("shield generation")) { attributeLabels.push_back("shields charge / max:"); attributeValues.push_back(Format::Number(60. * attributes.Get("shield generation")) + " / " + Format::Number(attributes.Get("shields"))); } else { attributeLabels.push_back("shields:"); attributeValues.push_back(Format::Number(attributes.Get("shields"))); } attributesHeight += 20; if(attributes.Get("hull repair rate")) { attributeLabels.push_back("hull repair / max:"); attributeValues.push_back(Format::Number(60. * attributes.Get("hull repair rate")) + " / " + Format::Number(attributes.Get("hull"))); } else { attributeLabels.push_back("hull:"); attributeValues.push_back(Format::Number(attributes.Get("hull"))); } attributesHeight += 20; double emptyMass = ship.Mass(); attributeLabels.push_back(isGeneric ? "mass with no cargo:" : "mass:"); attributeValues.push_back(Format::Number(emptyMass)); attributesHeight += 20; attributeLabels.push_back(isGeneric ? "cargo space:" : "cargo:"); if(isGeneric) attributeValues.push_back(Format::Number(attributes.Get("cargo space"))); else attributeValues.push_back(Format::Number(ship.Cargo().Used()) + " / " + Format::Number(attributes.Get("cargo space"))); attributesHeight += 20; attributeLabels.push_back("required crew / bunks:"); attributeValues.push_back(Format::Number(ship.RequiredCrew()) + " / " + Format::Number(attributes.Get("bunks"))); attributesHeight += 20; attributeLabels.push_back(isGeneric ? "fuel capacity:" : "fuel:"); double fuelCapacity = attributes.Get("fuel capacity"); if(isGeneric) attributeValues.push_back(Format::Number(fuelCapacity)); else attributeValues.push_back(Format::Number(ship.Fuel() * fuelCapacity) + " / " + Format::Number(fuelCapacity)); attributesHeight += 20; double fullMass = emptyMass + (isGeneric ? attributes.Get("cargo space") : ship.Cargo().Used()); isGeneric &= (fullMass != emptyMass); attributeLabels.push_back(string()); attributeValues.push_back(string()); attributesHeight += 10; attributeLabels.push_back(isGeneric ? "movement, full / no cargo:" : "movement:"); attributeValues.push_back(string()); attributesHeight += 20; attributeLabels.push_back("max speed:"); attributeValues.push_back(Format::Number(60. * attributes.Get("thrust") / attributes.Get("drag"))); attributesHeight += 20; attributeLabels.push_back("acceleration:"); if(!isGeneric) attributeValues.push_back(Format::Number(3600. * attributes.Get("thrust") / fullMass)); else attributeValues.push_back(Format::Number(3600. * attributes.Get("thrust") / fullMass) + " / " + Format::Number(3600. * attributes.Get("thrust") / emptyMass)); attributesHeight += 20; attributeLabels.push_back("turning:"); if(!isGeneric) attributeValues.push_back(Format::Number(60. * attributes.Get("turn") / fullMass)); else attributeValues.push_back(Format::Number(60. * attributes.Get("turn") / fullMass) + " / " + Format::Number(60. * attributes.Get("turn") / emptyMass)); attributesHeight += 20; // Find out how much outfit, engine, and weapon space the chassis has. map<string, double> chassis; static const string names[] = { "outfit space free:", "outfit space", " weapon capacity:", "weapon capacity", " engine capacity:", "engine capacity", "gun ports free:", "gun ports", "turret mounts free:", "turret mounts" }; static const int NAMES = sizeof(names) / sizeof(names[0]); for(int i = 1; i < NAMES; i += 2) chassis[names[i]] = attributes.Get(names[i]); for(const auto &it : ship.Outfits()) for(auto &cit : chassis) cit.second -= it.second * it.first->Get(cit.first); attributeLabels.push_back(string()); attributeValues.push_back(string()); attributesHeight += 10; for(int i = 0; i < NAMES; i += 2) { attributeLabels.push_back(names[i]); attributeValues.push_back(Format::Number(attributes.Get(names[i + 1])) + " / " + Format::Number(chassis[names[i + 1]])); attributesHeight += 20; } if(ship.BaysFree(false)) { attributeLabels.push_back("drone bays:"); attributeValues.push_back(to_string(ship.BaysFree(false))); attributesHeight += 20; } if(ship.BaysFree(true)) { attributeLabels.push_back("fighter bays:"); attributeValues.push_back(to_string(ship.BaysFree(true))); attributesHeight += 20; } tableLabels.clear(); energyTable.clear(); heatTable.clear(); // Skip a spacer and the table header. attributesHeight += 30; tableLabels.push_back("idle:"); energyTable.push_back(Format::Number( 60. * (attributes.Get("energy generation") + attributes.Get("solar collection")))); heatTable.push_back(Format::Number( 60. * (attributes.Get("heat generation") - attributes.Get("cooling")))); attributesHeight += 20; tableLabels.push_back("moving:"); energyTable.push_back(Format::Number( -60. * (attributes.Get("thrusting energy") + attributes.Get("reverse thrusting energy") + attributes.Get("turning energy")))); heatTable.push_back(Format::Number( 60. * (attributes.Get("thrusting heat") + attributes.Get("reverse thrusting heat") + attributes.Get("turning heat")))); attributesHeight += 20; double firingEnergy = 0.; double firingHeat = 0.; for(const auto &it : ship.Outfits()) if(it.first->IsWeapon() && it.first->Reload()) { firingEnergy += it.second * it.first->FiringEnergy() / it.first->Reload(); firingHeat += it.second * it.first->FiringHeat() / it.first->Reload(); } tableLabels.push_back("firing:"); energyTable.push_back(Format::Number(-60. * firingEnergy)); heatTable.push_back(Format::Number(60. * firingHeat)); attributesHeight += 20; double shieldEnergy = attributes.Get("shield energy"); double hullEnergy = attributes.Get("hull energy"); tableLabels.push_back((shieldEnergy && hullEnergy) ? "shields / hull:" : hullEnergy ? "repairing hull:" : "charging shields:"); energyTable.push_back(Format::Number(-60. * (shieldEnergy + hullEnergy))); double shieldHeat = attributes.Get("shield heat"); double hullHeat = attributes.Get("hull heat"); heatTable.push_back(Format::Number(60. * (shieldHeat + hullHeat))); attributesHeight += 20; tableLabels.push_back("max:"); energyTable.push_back(Format::Number(attributes.Get("energy capacity"))); heatTable.push_back(Format::Number(60. * emptyMass * .1 * attributes.Get("heat dissipation"))); // Pad by 10 pixels on the top and bottom. attributesHeight += 30; }