PHost - Formulas

PHost 4.1h


Index

Introduction

This document describes all formulas and processes used in PHost. It is intended for players who do not leave anything to chance. If you believe that some aspect of PHost operation should be documented in this file but is not, send mail to the PHost group and bring this to our attention.

This document is roughly grouped by the major object of an action. For example, Pillage is a mission that affects planets, and is therefore documented in the Planets section. If possible, formulas are arranged in the order they are needed during host processing. The exact sequence of events is documented on the Host Sequence page.

Notation

Variable names (Hull_mass, Native_race, ...) should be self-explanatory. We use common arithmetic notation. In particular, / is a fractional division.

Configuration options are specified verbatim, with links to their description. Eff_ConfigOption denotes the value of the ConfigOption modified by experience modifiers EModConfigOption.

Internally, PHost often uses people counts for populations, which were also used in the older documentation. For this document, numbers have most of the time been converted to the the more familiar clans. Since VGA Planets does not support fractional clans, PHost truncates after each step anyway. Some formulas still use people counts. For those, Colonist_clans is the clan count (e.g. 32), Colonists is the people count (e.g. 3200).

When a series of "if..." sentences is given, take the first applicable one.

Abs(X) Absolute value of X
ArcTan(X,Y) Arc-tangent of X/Y (four-quadrant), angle in radiant
With X, Y being X/Y displacements, this will produce an angle which, when converted to degrees, corresponds to the one reported in scanner reports (0=North, 90=East, 180=South, 270=West). Note that the conventional mathematical definition of the ArcTan function gives it Y/X as a parameter, not X/Y, which produces different angles (0=East, counterclockwise).
Ceil(X) Next-larger integer, preserving the sign (round up if there is any fractional part). For example, Ceil(10.0) = 10, Ceil(10.1) = 11, Ceil(-5.2) = -6
Cos(X) Cosine of X, argument is in radiants
ERnd(X) Round X to integer (round towards nearest integer; round towards nearest even number if ends in .5 exactly)
Exp(X) Exponential of X (2.7182^X)
Ln(X) Natural logarithm of X
Max(X,Y,...) Maximum
Min(X,Y,...) Minimum
PI The number π = 3.14159265358979323846
Random(X) Random integer, between 0 (inclusive) up to X (not inclusive). For example, Random(3) yields one of 0, 1, 2.
Round(X) Round X to integer (round towards nearest integer; round up if ends in .5 exactly)
Sgn(X) Sign of X (+1 if positive, -1 if negative, 0 if zero)
Sin(X) Sine of X, argument is in radiant
Sqrt(X) Square-root of X
Trunc(X) Truncate X to integer (remove fractional part). For example, Trunc(10.0) = 10, Trunc(10.1) = 10, Trunc(-5.2) = -5
X^N X to the Nth power

Back to top


General

Score

PHost creates a file, score.log, which logs the standard score. This is a very simple score computed as follows:

Score =
   Num_planets * 10
   + Num_capital_ships * 10
   + Num_freighters
   + Num_starbases * 120

A capital ship is one having at least one weapon (could be an Outrider with a Laser, or a Death Star armed up to the teeth).

Many client programs can also display this score. They compute it from the Num_planets etc. values reported within the result files. If ScoringMethod is set to None, PHost does not send all these values, causing clients to report incorrect scores. The following values will still be sent if ScoringMethod=None:

  • your own unit counts
  • the Num_capital_ships and Num_freighters of your allies offering ship level
  • the Num_planets and Num_starbases of your allies offering planet level

These values take Remote Control into account, and can thus differ from the number of units you actually see. For example, when you have 20 ships and remote-control 5 others, your RST will contain 25 ships, but your Num_ships will be reported as 20 only.

Back to top


Planets

Planetary Actions Flowchart

This table shows all actions involving planets. The left column shows the PControl stages, in chronological order. The middle column contains all actions affecting happiness or population (like "taxation happens before growth"), the right-most column contains all actions affecting minerals and buildings (like "beam-up-multiple happens before lfm" (hint, hint)). The "Details" column contains a link to the relevant section of this document, if there is one.

Stage Happiness/Population Minerals/Industry Details
Turn Processing (Phase 1) Colonists transferred to/from own ships Resources transferred to/from own ships. Resources spent for planetary buildings, starbases, starship parts, ammunition (starbases are built at this time, everything else already appeared at clientside).
LargeMeteors, MeteorShowers Meteors might bring down happiness and kill people. Meteors and meteor showers add minerals to planet core. Formulas
DeluxeSuperSpy Ionic pulse triggered by Super Spy Deluxe may kill defenses
CargoDump Ground combat Formulas
Training Ships gather supplies for training
GatherMission Ships gather stuff
SpecialMissions_1 Lizards Hiss Formulas
BuildFighters, BuildTorpedoes Ships gather stuff
ShipBuilding_1 Cloning might consume minerals.
DumpOldBaseParts Starbases recycle old starship parts
BaseMissions_1 Starbases first recycle ships, then maximize defense or load torps onto ships
FreeFighters Starbases build "free" fighters
GloryDevices Glory devices kill people and toast Amorphous natives Glory devices damage planet Formulas
ColonizeMission Colonize ships bring clans Colonize ships bring minerals Formulas
BaseMissions_2 Starbases refuel ships or unload them
Combat Population might be killed in combat.
Happiness changes if planet is captured.
Defense might be destroyed Effects on planet, Combat
Terraforming Terraforming makes environment more pleasant for inhabitants.
SensorSweep Sensor Sweep might discover planets according to their industry Formulas
SpecialMissions_2 Pillage / RGA might make people unhappy Pillage / RGA will destroy stuff, Dark Sense reports amounts afterwards Formulas
PlanetaryProduction Mining, supply production, then trans-uranium decay Formulas
PlanetaryHappiness if planet is unowned or happiness is 29 or lower, taxes are set to 0. Then, happiness changes Formulas
PlanetaryTaxation Tax collection Formulas
PlanetaryGrowth Growth Formulas
PlanetaryLosses Climate deaths, then losses through riots Overpopulation eat supplies, then structure decay, then losses through riots, then Amorphs eating colonists Formulas
ShipBuilding_2 Cloning might consume minerals.
Assimilation Cyborgs assimilate natives Formulas

Building Limits

Maximum Number of Buildings: The maximum number of structures you can build on a planet depends on the population of the planet.

Maximum_mines =
   Colonist_clans    ...if Colonist_clans < 200
   Round(200 + Sqrt(Colonist_clans - 200))
                     ...if Colonist_clans >= 200

Maximum_factories =
   Colonist_clans    ...if Colonist_clans < 100
   Round(100 + Sqrt(Colonist_clans - 100))
                     ...if Colonist_clans >= 100

Maximum_defense_posts =
   Colonist_clans    ...if Colonist_clans < 50
   Round(50 + Sqrt(Colonist_clans - 50))
                     ...if Colonist_clans >= 50

Note that you need not permanently fulfill these conditions. You can build factories and beam off clans to a ship. PHost will consider this possibility when checking turn files. However, buildings over the limit as determined from the planetary population will decay at a rate of StructureDecayPerTurn.

Meteor Strikes

Large meteors kill people and make them unhappy. Meteor showers have no effect on the population.

Colonists_survived =
   Colonists * (Random(91)+10) / 100

Colonist_happiness_change =
   -(Random(31) + 50)

Natives_survived =
   Natives * (Random(100)+1) / 100

Native_happiness_change =
   -(Random(31) + 50)

In simple words, up to 90% of the colonists and up to 99% of the natives die, and they lose 50 to 80 happiness points. The mineral amounts added to the planet's core (not surface!) are configurable (LargeMeteorOreRanges, MeteorShowerOreRanges).

Ground Combat

Ground combat happens when a hostile ship drops colonists on a foreign planet, using the ship-to-planet cargo transporters.

During ground combat, attacking colonists and defenders annihilate each other, subject to their attack and defense ratios, until one party remains.

Ground_attack_result =
   "attacker wins with Trunc(Result/Attacker_rate) clans remaining"
                     ...if Result >= 0
   "defender wins with Trunc(-Result/Defender_rate) clans remaining"
                     ...if Result < 0
   ...with
      Attacker_rate =
         GroundKillFactor[Ship_owner]
      Defender_rate =
         GroundDefenseFactor[Planet_owner] + Planet_defense / 20
      Result =
         (Attacking_clans * Attacker_rate) - (Defending_clans * Defender_rate)

Note that if the result yields 0 clans remaining (maybe due to rounding), the planet gets unowned.

Imperial Assault: When a successful Imperial Assault is performed, the result is always "attacker wins with Attacking_clans clans remaining", independant of the number of Defending_clans and their attack/defense ratios. Imperial Assault is successful if the preconditions for the Imperial Assault device are fulfilled.

Hiss

Hiss increases planetary happiness, by the same amount for natives and colonists. This formula used to read

Hiss_happiness_change =
   Min(Number_of_hissing_ships, MaxShipsHissing) * HissEffectRate

However, since PHost 4.1, HissEffectRate is arrayized. Therefore, in the general case, the Hiss_happiness_change is the sum of the HissEffectRate of the first MaxShipsHissing ships hissing at the planet (which can possibly belong to different players with different rates).

Note that hissing happens before taxation; after the hiss effect increases happiness, taxation will it bring down again.

Sensor Sweep

Buildings affect your visibility to Sensor Sweep.

Industry_detection_chance =
   0%            ...if Mines < MinesForDetectable
                    and Factories < FactoriesForDetectable
   0%            ...if Defense >= DefenseForUndetectable
   100% - (Defense / DefenseForUndetectable * 100%)
                 ...in all other cases

Industry_level_reported =
   "minimal"     ...if (Mines + Factories) < 30
   "light"       ...if (Mines + Factories) < 60
   "moderate"    ...if (Mines + Factories) < 90
   "substantial" ...if (Mines + Factories) < 120
   "heavy"       ...if (Mines + Factories) >= 120

Bioscan_detection_chance =
   0%            ...if Defense >= 20
   0%            ...if ship does not have bioscanner (of course :-)
   100%          ...if ship has full bioscanner
   20%           ...otherwise

Special Attacks

Pillage: Ships that do Pillage will plunder planetary households to get cash and supplies. They will then kill people which makes the population unhappy.

Supplies_made =
   Trunc((Colonist_clans + Native_clans) / 100)

Money_made =
   Trunc((Colonist_clans + Native_clans) / 100)

Colonist_clans_survived =
   Trunc(Colonist_clans * 0.8 - 20)

Native_clans_survived =
   Trunc(Native_clans * 0.8 - 120)

Colonist_happy_change =
   -10

Native_happy_change =
   -10

RGA: Ships that do Rebel Ground Attack will destroy planetary resources. They will then kill colonists, which makes them unhappy. Sarcastic natives will laugh at unhappy colonists.

Money_remaining =
   Trunc(Money * 0.7)

Supplies_remaining =
   Trunc(Supplies * 0.6)

Defense_remaining =
   Trunc(Defense_posts * 0.8)

Mines_remaining =
   Trunc(Mines * 0.4)

Factories_remaining =
   Trunc(Factories * 0.7)

Colonist_clans_survived =
   Trunc(Colonist_clans * 0.8)

Colonist_happy_change =
   -60

Native_happy_change =
   +40

Planetary Production

Mining: The maximum amount of minerals extracted from a planet's core depends on the mineral density.

Max_minerals_mined =
   Round(Mining_rate * Mine_count / 100)
   ...where Mining_rate = Round(RaceMiningRate * Mineral_density / 100) * RF
   ...with RF = 2 for Reptilian natives, 1 otherwise

Minerals_mined =
   Min(Max_minerals_mined, Minerals_in_core)

(v4.1:) PHost before version 4.1 used Trunc instead of Round here. The new formula always yields a little more minerals.

Supplies: Supplies are produced by factories, or by Bovinoid natives.

Supplies_made =
   Trunc((Factory_contribution + Bovinoid_contribution) * ProductionRate) / 100)
   ...where
      Factory_contribution =
         Factories
      Bovinoid_contribution =
         Min(Trunc(Native_clans / 100), Colonist_clans)
            ...if natives are Bovinoid
         0  ...otherwise

Trans-Uranium Decay: New minerals appear in the planet's core. Note that this happens after mining, so you cannot extract the new minerals the same turn. Trans-uranium decay happens separately for all four minerals.

New_minerals_in_core =
   Trunc((TransuraniumDecayRate * Mineral_density + 50) / 100)

Happiness

Your demand for taxes changes happiness. Note that these happiness changes occur before actual taxation. If the happiness before this stage is below 30, taxes are set to zero (and new happiness is computed according to this rate). If the happiness after this stage is below 30, no taxes are collected either.

Colonist_happiness_change =
   Trunc(10 - Sqrt(Colonist_clans / 10000)
            - Abs(Temperature - Target_temperature) / Temp_divisor
            - (Mines + Factories) / 300
            - Colonist_tax * 0.8)
   ...where Target_temperature = 100 and Temp_divisor = 66
      if owner is Crystal and CrystalsPreferDeserts is on
   ...where Target_temperature = 50 and Temp_divisor = 33
      otherwise

Native_happiness_change =
   Trunc(5 + Native_gov_number / 2
         - Sqrt(Native_clans / 10000)
         - (Mines + Factories) / 200
         - Native_tax * 0.85)
   ...plus 10 if natives are Avian

The Native_gov_number is tabulated in the Planet Rules section (e.g. 6 for Monarchy, yielding a value of 6/2 = 3 for the first term).

Happiness is summarized as follows:

Happiness_level =
   "happy"           ...if Happiness >= 90
   "calm"            ...if Happiness >= 70
   "unhappy"         ...if Happiness >= 50
   "very angry"      ...if Happiness >= 40
   "rioting"         ...if Happiness >= 20
   "fighting"        ...if Happiness < 20

Happiness_change_level =
   "hate you"        ...if Happiness_change < -5
   "angry about you" ...if Happiness_change < 0
   "undecided"       ...if Happiness_change = 0
   "like your leadership"
                     ...if Happiness_change <= 4
   "love you"        ...if Happiness_change >= 5

Taxation

Taxes are collected separately from colonists and natives.

Taxes_collected =
   Taxes_from_colonists + Taxes_from_natives
   ...at most MaxPlanetaryIncome

Taxes_from_colonists =
   Round(Round(Colonist_clans * Colonist_tax / 1000) * ColonistTaxRate / 100)
                    ...if Colonist_happiness >= 30
   0                ...if Colonist_happiness < 30

Taxes_from_natives =
   0                ...if Native_happiness < 30
   0                ...if natives are Amorphous
   Round(Min(Colonist_clans, Natives_due) * IF * NativeTaxRate / 100)
   ...where Natives_due = Round(Native_clans * Native_gov_number * Native_tax / 5000)
   ...where IF = 2 if natives are insectoid, IF = 1 otherwise

The Native_gov_number is tabulated in the Planet Rules section (e.g. 6 for Monarchy).

Population Growth

Maximum Population: The maximum population for a planet is a fixed number which depends on the planet's climate. With ClimateLimitsPopulation enabled, the maximum population on an ideally-tempered world is 100000 clans (10 million colonists). If configured, supplies help you support more colonists on a planet.

Eff_max_colonist_clans =
   Trunc(Eff_max_colonists / 100)

Eff_max_colonists =
   Max_colonists + Supply_bonus
      ...if ClimateLimitsPopulation is enabled
      ...at least 1
   25000000
      ...otherwise

Max_colonists =
   Trunc(10000000 * Sin(Temperature * PI / 200))
                    ...if colonists are Crystalline
                       and CrystalsPreferDeserts is enabled
                       and CrystalSinTempBehavior is enabled
                       and Temperature >= 15
   300 + MaxColTempSlope * Temperature
                    ...if colonists are Crystalline
                       and CrystalsPreferDeserts is enabled
                       and CrystalSinTempBehavior is enabled
                       and Temperature < 15
   100000 * Temperature
                    ...if colonists are Crystalline
                       and CrystalsPreferDeserts is enabled
                       and CrystalSinTempBehavior is disabled
   9000000
                    ...if colonists are Rebel and Temperature < 20
   300 + MaxColTempSlope * Temperature
                    ...if Temperature < 15
   100 + (100-Temperature) * MaxColTempSlope
                    ...if Temperature > 84
                    ...at least 6000 if colonists are Klingon, Robot, Rebel or Colony
   Trunc(10000000 * Sin(Temperature * PI / 100))
                    ...in all remaining cases

Supply_bonus =
   Trunc(100 * Supplies / 40)
                    ...if AllowEatingSupplies is enabled
   0                ...otherwise

==> Note that these formulas use colonists, not clans. A future PHost version may simplify that to clans, which would get slightly different rounding behaviour.

Native populations have simpler formulas. The absolute maximum is 15.6 million natives (156000 clans) for most races, 10 million for Siliconoids when they like it hot. Supplies do not help you to increase the limit.

Max_native_clans =
   156000
                    ...if ClimateLimitsPopulation is disabled
   1000 * Temperature
                    ...if natives are Siliconoid
                       and CrystalsPreferDeserts is enabled
                       and PHost is version 3.3c or newer
   Trunc(Sin(Temperature * PI / 100) * 156000)
                    ...in all remaining cases

It might be considered a bug that Siliconoids cannot reach the 15.6 million mark with CrystalsPreferDeserts enabled, but changing the formula would again complicate matters even more.

Growth: Growth depends on taxation and climate. Populations will never grow over the limit. The maximum growth rate is 5% per turn.

Colonist_growth_in_clans =
   Trunc(Round(Colonist_growth_rate * Colonist_clans / 100) *
           RaceGrowthRate / 100)
   ...at most Eff_max_colonist_clans - Colonist_clans

Colonist_growth_rate =
   0                ...if Colonist_clans >= Eff_max_colonist_clans
   0                ...if Colonist_happiness < 70
   0                ...if colonists are Crystalline
                       and CrystalsPreferDeserts is enabled
                       and CrystalSinTempBehavior is enabled
                       and Temperature < 15
   5 * Sin(Temperature * PI / 200) / (1 + Colonist_tax / 5)
                    ...if colonists are Crystalline
                       and CrystalsPreferDeserts is enabled
                       and CrystalSinTempBehavior is enabled
                       and Temperature >= 15
   5 * (Temperature / 100) / (1 + Colonist_tax / 5)
                    ...if colonists are Crystalline
                       and CrystalsPreferDeserts is enabled
                       and CrystalSinTempBehavior is disabled
   0                ...if Temperature < 15
   0                ...if Temperature > 84
   5 * Sin(Temperature * PI / 100) / (1 + Colonist_tax / 5)
                    ...in all other cases

Native growth is similar, but here the maximum growth rate is 4%. Natives do not grow on unowned planets(!).

Native_growth_in_clans =
   Trunc(Round(Native_growth_rate * Native_clans) / 100)
   ...at most Max_native_clans - Native_clans

Native_growth_rate =
   0                ...if Native_clans >= Max_native_clans
   0                ...if Native_happiness < 70
   4 * (Temperature / 100) / (1 + Native_tax / 5)
                    ...if natives are Siliconoids
                       and CrystalsPreferDeserts is enabled
                       and PHost is version 3.3c or newer
   4 * Sin(Temperature * PI / 100) / (1 + Native_tax / 5)
                    ...in all other cases

Planetary Losses

Overpopulation eats supplies: If enabled, colonist overpopulation will eat supplies to survive.

Supply_loss =
   0                ...if AllowEatingSupplies is off
   0                ...if ClimateLimitsPopulation is off
   0                ...if Colonists <= Max_colonists
   Trunc((Colonists - Max_colonists) / 4000) + 1
                    ...otherwise

If there are fewer supplies than needed, they'll all be used up with no negative consequences.

Climate Deaths: If enabled, colonists and natives over the population limit will die.

Colonist_clans_dying =
   0                ...if ClimateLimitsPopulation is off
   0                ...if Colonist_clans <= Eff_max_colonist_clans
   Min(Trunc(Colonist_clans * ClimateDeathRate / 100),
       Eff_max_colonist_clans - Colonist_clans)
                    ...otherwise

Native_clans_dying =
   0                ...if ClimateLimitsPopulation is off
   0                ...if planet is unowned
   0                ...if Native_clans <= Max_native_clans
   Min(Trunc(Native_clans * NativeClimateDeathRate / 100),
       Max_native_clans - Native_clans)
                    ...otherwise

==> If the population exceeds the absolute maximum of 250000 clans (25 million people), the excess clans will die and be reported as climate deaths, even if ClimateLimitsPopulation is off.

==> Eff_max_colonist_clans is computed after overpopulation-eats-supplies, so it uses the new (lower) supply amount. It uses the same formula as in the Growth step, but not the same value.

Structure Losses: Planetary buildings can be lost through riots or lack of maintenance (i.e. fewer colonists than needed to support them).

Mine_loss =
   Mine_decay + Riot_loss
Factory_loss =
   Factory_decay + Riot_loss
Defense_loss =
   Defense_decay
   ...where
      Riot_loss =
          10        ...if planet has colonists, and Colonist_happiness < 40
          10        ...if planet has natives, and Native_happiness < 40
          0         ...otherwise
      Mine_decay =
          Max(Min(Mines - Maximum_mines, Eff_StructureDecay), 0)
      Factory_decay =
          Max(Min(Factories - Maximum_factories, Eff_StructureDecay), 0)
      Defense_decay =
          Max(Min(Defense_posts - Maximum_defense_posts, Eff_StructureDecay), 0)
      Eff_StructureDecay =
          StructureDecayOnUnowned
                    ...if planet is unowned
          StructureDecayPerTurn[Planet_owner]
                    ...otherwise

Population loss through riots: Fighting colonists and/or natives will lead to loss of population. Even if just one population group fights, the other one will suffer as well.

Colonist_clans_dying =
   Ceil(Colonist_clans * (40 - Colonist_happiness) / 500)
                    ...if Colonist_happiness < 20
   Ceil(Colonist_clans * (40 - Native_happiness) / 2500)
                    ...if planet has natives, and Native_happiness < 20
   0                ...otherwise

Native_clans_dying =
   Ceil(Native_clans * (40 - Native_happiness) / 500)
                    ...if Native_happiness < 20
   Ceil(Native_clans * (40 - Colonist_happiness) / 2500)
                    ...if planet has colonists, and Colonist_happiness < 20
   0                ...otherwise

Population loss from amorphous natives: Amorphous natives eat colonists after all other losses.

Colonist_clans_eaten =
   0                ...if natives are not Amorphous
   5                ...if Native_happiness >= 70
   20               ...if Native_happiness >= 50
   40               ...otherwise

If the Amorphous natives eat all your colonists or more, you'll lose the planet.

Assimilation

Cyborgs assimilate natives, and turn them into colonists. One assimilated native clan yields one new cyborg colonist clan.

Native_clans_assimilated =
   Trunc(Colonist_clans * BorgAssimilationRate / 100)
   ...at most Native_clans

==> PHost versions before 4.1 / 3.5 would have removed Ceil(...) native clans, but only added Trunc(...) clans.

Back to top


Ships

Ship Actions Flowchart

This table lists all events that affect ships, in the order in which they happen. Effects have been roughly grouped into two categories, although the distinction is not as sharp as for the planet table.

Stage Cargo, Fuel, Cloak Damage, Crew, Mission Details
Turn Processing (Phase 1) Cargo transfers to/from own units. Ships cloak if told to. Mission selected by client, validity of Tow/Intercept orders checked at this point.
TransferOwner Ships de-cloak after owner change Mission and waypoint reset after owner change
AntiCloak_1 Lokis de-cloak ships
DeluxeSuperSpy Ionic pulse may de-cloak ships
RobMission Ships rob or get robbed
GamblingShips Gambling makes money
CargoDump, CargoTransfer Ships can receive cargo
Excess cargo may be given back
Ground Combat
TrimShipCargo Excess cargo removed
CrewExchange Crew Exchange mission
Training Training mission Formulas
BeamTransfers Ships give away/receive cargo
GatherMission, BeamUpCredits Ships gather stuff
MineLaying Ships turn torpedoes into minefields Formulas
MineSweeping Ships receive torpedoes from scooping Formulas
WebDraining Ships lose fuel to web minefields
IonStormEffects Ion storms de-cloak Ships take damage and lose crew Formulas
SpecialMissions_1 Super Refit, Repair Ship, Self Repair
BuildFighters Build Fighters
BuildTorpedoes mkt etc.
Alchemy Alchemy functions
ShipBuildOrders Ships trying to clone are set to Warp 0
BaseMissions_1 Ships receive torps Ships are fixed or recycled, or forced to surrender
SupplyRepair_1 Supplies are used... ...for self-repair Formulas
BoardingParties Ships are boarded Formulas
TowResolution Tow conflicts resolved and tows canceled Formulas
Movement Fuel burned for movement and cloaking
Mine hits can de-cloak ship
Mine hits damage ship Movement Formulas
General fuel burn
GloryDevices Damage taken from exploding Glory Devices Formulas
Chunneling Fuel consumed by chunneling Chunneling resets waypoints
AntiCloak_2 Lokis de-cloak ships
ColonizeMission Ships land on planets and are recycled Formulas
SupplyRepair_2 Supplies are used... ...for self-repair Formulas
BaseMissions_2 Starbases refuel ships or unload them
Combat Fuel burned
De-cloak if damaged
Borgs gather debris
Ships take damage, lose crew, change ownership Formulas
SupplyRepair_3 Supplies are used... ...for self-repair Formulas
SensorSweep Formulas
SpecialMissions_2 Pillaging ships gather cash/supplies Formulas
Experience Experience upgrades

General Ship Formulas

Ships burn fuel or need to have fuel available to perform a variety of operations. In addition to the operations listed here, many other maneuvers require the ship to have at least one kiloton of fuel (but do not consume that). Having fuel is also a prerequisite for being able to take part in combat (except for defending against a NUK planet).

Mass

Many formulas depend on the mass of a ship. There are three different masses which are of interest: hull mass, total mass, and combat mass.

  • The Hull_mass is just the mass of the empty hull, it is used for various effects affecting the hull of a ship, or devices built into the hull.
  • The Total_ship_mass is the total weight of the ship.
  • The Combat_mass is defined in the Combat section, and describes a ship's weapon resistance.
Total_ship_mass =
   Hull_mass
   + Fuel + Supplies + Colonists                (cargo)
   + Tritanium + Molybdenum + Duranium          (cargo)
   + Torpedoes + Fighters                       (ammunition)
   + Num_torp_launchers * Torp_launcher_mass    (weight of torpedo launchers)
   + Num_beams * Beam_mass                      (weight of beam weapons)

Note that fighter bays do not have weight (they are built into the hull, and are included in its weight). Money, engines and crew don't weigh anything as well.

Masses are recomputed from the ship's data whenever they are needed. If your ship's cargo changes, its mass changes as well.

Cloaking

Ships that cloak need to burn fuel.

Fuel_burned_by_cloak =
   0                 ...if ship has Advanced Cloaking
   Trunc(CloakFuelBurn * Max(Hull_mass, 100) / 100)
                     ...otherwise

Fuel_needed_before_movement =
   1 + Fuel_burned_by_cloak

Fuel_needed_after_movement =
   1

Ships need to decloak if their fuel drops below the Fuel_needed value. After the normal cloak fuel was burnt, one kt must remain; fuelless ships cannot cloak.

Maintaining Operation

Ships burn fuel after movement. If the ship has less fuel, nothing bad will happen, but the ship will end up without fuel afterwards.

Fuel_burned_per_turn =
   Ceil(Hull_mass * FuelUsagePerTurnFor100KT / 100)

Ships burn fuel after each fight. If the ship has less fuel, nothing bad will happen, but the ship will end up without fuel afterwards (and must thus stop fighting).

Fuel_burned_per_fight =
   Ceil(Hull_mass * FuelUsagePerFightFor100KT / 100)

Training

Ships with the Training mission use supplies from the planet they orbit to gain experience.

Experience_added = Trunc(Rate * Points / (Sqrt(Hull_Crew) + 1))
    ...where
       Points = Supplies                    if Supplies <= 25
       Points = 25 + Sqrt(8*(Supplies-25))  otherwise

       Rate = Trunc(EPTrainingScale * Academy_bonus / 100)

       Academy_bonus = 100                  for normal ships
       Academy_bonus = EPAcademyScale       for Academy ships

(v4.0i:) Academy ships receive four times as much experience from training.

See Experience Training for a more detailed explanation of training.

Supply Repair

Supply repair happens several times in host processing. This process is automatic, and there is no way to stop or force it. Ships do not need fuel to repair themselves.

Damage_repaired =
   Min(Damage, Trunc(Supplies_on_ship / 5))

Supplies_used =
   5 * Damage_repaired

Boarding

Ships with the Boarding ability (generally, all Privateer and Tholian ships) can board fuel-less enemy ships by towing them, and thus obtain control over them. Uncooperating enemy crew members are jetissoned and replaced by own crew.

New_crew_on_boarded_ship =
   Traitors + Crew_beamed_over
New_crew_on_boarding_ship =
   Crew_on_boarding_ship - Crew_beamed_over
   ...where
      Traitors =
         Crew_on_boarded_ship
                     ...if boarded ship is Privateer
         Trunc(0.9 * Crew_on_boarded_ship)
                     ...if boarded ship is Fed
         Trunc(0.7 * Crew_on_boarded_ship)
                     ...if boarded ship is Colonial
         Trunc(0.4 * Crew_on_boarded_ship)
                     ...if boarded ship is Empire
         0           ...otherwise
      Crew_beamed_over =
         Min(Boarded_ship_full_crew - Traitors,
             Trunc(Boarding_ship_crew / 2))

As a special exception, if these formulas would reduce New_crew_on_boarding_ship below 2, PHost uses Traitors at least 2 and Crew_beamed_over = 0. (v4.1f:) PHost before version 4.1f would have given exactly 2 crew, even if the original Traitors were more.

Towing

For more details about towing, see Tow Conflict Resolution.

Tow_strength =
   Engine_contribution + Movement_contribution
                    ...if ship has fuel
   0                ...if ship does not have fuel

Engine_contribution =
   Engine_tech^2 * Eff_engines * Warp_factor * TowStrengthEngineScale

Movement_contribution =
   Movement_distance * TowStrengthDistanceScale
   ...where
      Movement_distance = Min(Waypoint_distance,
                              Max_dist,
                              Max_allowed_by_fuel)
  • Engine_tech is the tech-level of the engine (i.e. 10 for Transwarp Drives)
  • Eff_engines is the number of engines the ship has, times two if the ship has gravitonic engines, plus an additional 2 if the ship has a Level 2 Tractor beam.
  • Movement_distance is an estimate how far the ship will move regularily this turn.
    • Waypoint_distance is the distance to the waypoint. For ships that hyperjump, this value is 350, independant from actual waypoint.
    • Max_dist is the maximum distance allowed by the ship's speed, as explained in the movement formulas section, e.g. 81 for a regular ship at warp 9.
    • Max_allowed_by_fuel is the maximum distance the ship can travel before running out of fuel. For the tower, the computation includes the towee's mass.

Movement

General

Before a ship starts moving, its waypoint needs be known (in form of a X/Y pair, or, actually, a dX/dY displacement). Intercept resolution attempts to ensure that for intercepting ships.

If a wraparound universe is being used, ships move the shortest possible path to reach their waypoint (i.e. "100 ly to the east", not "1900 ly to the west"). After moving across a map seam, they are moved into the map again: if a ship moves 50 ly east of the right seam (outside the map), it will end up 50 ly east of the left seam (inside the map).

Speed Limit: Damage may force a ship to slow down. Note that the maximum speed is Warp 9, even if the formula yields a higher value.

Max_speed =
   9                 ...if ship has Hardened Engines
   15 - Trunc(Damage/10)
                     ...if ship owner is Lizards
   10 - Trunc(Damage/10)
                     ...otherwise

Basics: First, we compute the maximum distance permitted by our warp factor. This is the permitted movement distance for our ship. Both values may change during movement.

Max_dist =
   Warp^2            ...for normal engines
   Warp^2 * 2        ...for gravitonic engines

Permitted_dist =
   Max_dist          ...if ship has fuel
   1                 ...if ship has no fuel, and AllowNoFuelMovement is on
   0                 ...if ship has no fuel, and AllowNoFuelMovement is off

PHost uses a time-based reasoning to move ships. Normally, a ship has one time unit to move. It doesn't need to use that unit completely. A ship moving towards a 32 ly waypoint at Warp 8 will usually reach that waypoint after one-half time unit. Mine hits can cause the ship to stop or change speed after a particular time. If a towed ship breaks free after its tower hit a mine, for example, it can use the remaining time not used by its tower to reach its own waypoint.

The distance a ship is allowed to move is computed using the identity

Movement_distance =
   Max_dist * Movement_time

Fuel Usage

Fuel_usage =
   Trunc((ERnd(Total_ship_mass/10) * Distance * Engine_fuel_usage)
           / (10000 * Max_dist))
                     ...if UseAccurateFuelModel is off
   Total_ship_mass
    * (1.0 - Exp(-(Engine_fuel_usage * Distance) / (Max_dist * 100000)))
                     ...if UseAccurateFuelModel is on

Note that the UseAccurateFuelModel formula can yield fractional values. In case ship movement occurs in multiple phases (because the ship hits a mine), these fractional values are added up, and arithmetically rounded at the end. In contrast, non-accurate movement rounds after every step.

If the ship successfully finishes its journey (i.e. does not hit a mine), Distance is the same as Movement_distance. This is what client programs assume to predict your fuel usage.

The Engine_fuel_usage is part of the engine definition (engspec.dat), and differs for all engines and speeds. Most reference sheets tabulate Engine_fuel_usage / (Warp^2) because that yields nicer numbers.

Movement Distance

Ship movement is computed in exact numbers. PHost computes the angle to the ship's waypoint, and moves it there using double-precision floating point. Thus, it is possible that a ship hits a mine after 17.3 ly at coordinates (2351.56, 1792.35). For reporting, and for actually placing the ship in the game universe, PHost has to round.

New_position =
   (Waypoint_X, Waypoint_Y)
                     ...if Distance_moved = Distance_to_waypoint
                        (i.e. waypoint reached exactly)
   (Position_X + Ceil(Distance_moved * Sin(Heading)),
    Position_Y + Ceil(Distance_moved * Cos(Heading)))
   ...with
      Heading = ArcTan(Waypoint_X - Position_X, Waypoint_Y - Position_Y)

Two noteworthy properties of this formula:

  • Because of the Ceil(...), PHost guarantees you to be able to move your maximum distance. A distance of 81 ly can always be covered with Warp 9, rounding effects will never reduce it below. Regarding fuel consumption, the excess is free (and, regarding ramscoop, it doesn't generate fuel).
  • Rounding can cause mine hits to be reported slightly outside the minefield.

For example, if you set a 90 ly waypoint, your ship will most likely move, say, 81.4 ly, but will only burn fuel for 81 ly, and its ramscoop will only make fuel for 81 ly.

Ramscoop

Ships with Ramscoop gather up thin intersteller matter during movement, and convert that into fuel. This fuel is available after movement. It can not be used for movement. If the ship hits a web mine, it will first lose fuel to the webs, and then receive its ramscoop fuel (i.e. it will end the turn with fuel, even if the webs sucked it dry before).

Fuel_made_by_ramscoop =
   Trunc(RamScoopFuelPerLY
         * (Distance_moved_uncloaked + 0.5*Distance_moved_cloaked))

A ship can move a part of its journey cloaked and another part uncloaked if it hits a mine that brings it above the DamageLevelForCloakFail limit.

Only regular movement produces fuel. Hyperjumping, chunneling, and all movement not produced by the ship's engines (i.e. being towed, ion storm, etc.) do not cause fuel to be made. The ship can, however, tow another ship or intercept a ship. Reportedly, ships do not make fuel while intercepting in some HOST versions; hence in PHost up to 4.0e, they don't make fuel either. In current versions, this restriction no longer exists.

Gravity Wells

A planet's mass will pull every ship in a certain distance towards the planet (default: 3 ly). This can be used to save fuel: move just to the edge of the warp well and let the planet pull you. To move inside a warp well, you must go at Warp 1; Warp 1 ships are not pulled.

Preconditions:

  • AllowGravityWells is enabled;
  • Ship does not already orbit a planet;
  • Ship has moved by itself (towed ships move with their tower, and are affected by gravity wells if their tower is);
  • If the ship has hyperjumped, it is affected only if AllowHyperjumpGravWells is enabled;
  • If the ship has moved normally, it is affected only if its speed is not Warp 1.

A ship is pulled towards the planet for which the following holds:

If more than one planet is in range, the ship is pulled towards the one with the highest Id. If no planet is in range, the ship is not affected.

While the ship is moved by gravity wells, it does not need fuel, and is not in danger of hitting mines.

Intercept Resolution

PHost's movement method requires all waypoints to be known before movement starts. For intercept, it therefore attempts to sort ships into an order such that all intercept targets move before their respective interceptors. In case of circular intercept, this cannot be done, so it is treated specially:

Waypoint_X =
   (Sum(Position_X) + Adjust_X) / Num_ships_in_loop

Waypoint_Y =
   (Sum(Position_Y) + Adjust_Y) / Num_ships_in_loop

Adjust_X =
   0                 ...if AllowWraparoundMap is off
   i * Map_dimension_X
       ...where 0 <= i < Num_ships_in_loop
          such that Sum((Position_X - Waypoint_X)^2) is minimal

Adjust_Y =
   0                 ...if AllowWraparoundMap is off
   j * Map_dimension_Y
       ...where 0 <= j < Num_ships_in_loop
          such that Sum((Position_Y - Waypoint_Y)^2) is minimal

The Adjust_X and Adjust_Y parameters are used only in wraparound-map games, and attempt to make sure that ships move the minimum possible distance. They are found by iterating all possible i and j values and picking the best fit. In PHost below 4.0k/3.4m, they are always zero.

Hyperjumping

Hyperjump ships can use their hyperdrive instead of the normal engines to move. See the HYP friendly code for preconditions for hyperjumping.

Fuel_used =
   50

New_position =
   (Waypoint_X, Waypoint_Y)
                     ...if Distance_to_waypoint >= 340
                        and Distance_to_waypoint <= 360
   (Position_X + Sgn(DX) * Trunc(350 * Abs(DX) / Distance_to_waypoint) + 0.4999999,
    Position_Y + Sgn(DY) * Trunc(350 * Abs(DY) / Distance_to_waypoint) + 0.4999999)
   ...with
      DX = Waypoint_X - Position_X
      DY = Waypoint_Y - Position_Y

Glory Device

Glory Devices, when set off, do damage to all ships and planets at the same location. The Glory Device ship itself will explode and thus be destroyed.

Damage_done_to_ship =
   Trunc(Trunc(100 * MineHitDamageFor100KT / Hull_mass) / DF)
   ...with
      DF = 10  ...if it's a GloryDeviceLowDamage (the advanced version),
                  and ship owned by same player as glory device
                      or players are allied, and GD player offers ship and combat level
      DF = 5   ...if it's a GloryDeviceHighDamage (the simple version)
                  and ship owned by same player as glory device
                      or players are allied, and GD player offers ship and combat level
      DF = 1   ...otherwise

Supplies_added_to_planet =
   Trunc(Native_clans / 10)
                     ...if natives are Amorphous
   0                 ...otherwise

Native_clans_survived =
   0                 ...if natives are Amorphous
   Native_clans      ...if natives are Bovinoid or Reptilian
   Trunc(0.6 * Native_clans)
                     ...otherwise

Colonist_clans_survived =
   Colonist_clans    ...if colonists are Lizard or Crystal
   Trunc(0.6 * Colonist_clans)
                     ...otherwise

Mines_remaining =
   Trunc(0.75 * Mines)

Factories_remaining =
   Trunc(0.75 * Factories)

Defense_posts_remaining =
   Trunc(0.75 * Defense_posts)

Colonizing

Using the colonize mission, ships can land on planets, be unloaded and disassembled, to start a new colony. You use that only if you want to get rid of a ship, normally, it suffices to unload colonists to start a colony.

Colonist_clans_on_planet =
   Colonist_clans_on_ship + Trunc(Ship_crew / 100) + 1

Neutronium_added_to_planet =
   Neutronium_on_ship

Money_added_to_planet =
   Money_on_ship

Tritanium_added_to_planet =
   Tritanium_on_ship + Trunc(RecycleRate * Tritanium_built_into_ship / 100)
   ...with
      Tritanium_built_into_ship =
         Hull_tritanium_cost
         + Engine_tritanium_cost * Num_engines
         + Beam_tritanium_cost * Num_beams
         + Torp_launcher_tritanium_cost * Num_torp_launchers

Duranium and Molybdenum are recycled according to the same formula as Tritanium.

Note that ammunition on the ship is not recycled, and does not yield minerals for the colony.

Back to top


Combat

Starting Parameters for Ships

  • Unless another rule says the ship starts with no shields, its starting shield level is:
    Trunc(100 - 2*Damage/3)
                         ...if ship is Lizard
    Trunc(100 - Damage)  ...otherwise
    Note that in particular a ship's fuel does not affect its combat shield level; a ship without fuel fighting a planet (with the planet using the NUK friendly code) will start with full shields unless another rule says otherwise.
  • A ship's combat mass is:
    Hull_mass + Trunc(ESB_Rate * Engine_cost / 100)
    ...plus 50 if ship is Fed
    The Engine_cost is the monetary cost of one engine of the ship. The ESB_Rate is the sum of the following components:
  • A ship's weapons are limited if the ship is damaged.
    Beam_count =
       Ship_beams        ...if ship has Full Weaponry
       Min(Ship_beams, Max_beams - Trunc(Max_beams * Damage / Divisor))
                         ...otherwise
    
    Tube_count =
       Ship_tubes        ...if ship has Full Weaponry
       Min(Ship_tubes, Max_tubes - Trunc(Max_tubes * Damage / Divisor))
                         ...otherwise
    
    Bay_count =
       Basis_bays + Bonus_bays
       ...with
          Basis_bays =
             Ship_bays   ...if ship has Full Weaponry
             Max_bays - Trunc(Max_bays * Ship_damage / Divisor)
                         ...otherwise
          Bonus_bays =
             ExtraFighterBays + EModExtraFighterBays
                         ...if Basis_bays > 0
             0           ...otherwise
    
    Divisor =
       150               ...if ship is Lizard
       100               ...otherwise
    Max_beams, Max_tubes and Max_bays are the maximum amounts allowed by the ship's hull.

Starting Parameters for Planets

A planet's defense is reduced if the planet gets damaged.

Eff_Planet_Defense =
   Trunc(Planet_Defense * (100 - Damage) / 100)
Eff_Base_Defense =
   Trunc(Base_Defense * (100 - Damage) / 100)
Eff_Total_Defense =
   Trunc((Base_Defense + Planet_Defense) * (100 - Damage) / 100)

Note that Eff_Total_Defense can differ from Eff_Planet_Defense + Eff_Base_Defense due to rounding.

A base's tech level is reduced if the base gets damaged. Here's the formula for beam tech, it applies to the other areas as well.

Eff_Beam_Tech =
   Beam_Tech
   ...at most Trunc((100 - Base_damage) / 10)
   ...at least 1

Planet's equipment in combat:

Combat_mass =
   100 + Eff_Planet_Defense
   ...plus Eff_Base_Defense if base exists

Beam_type =
   Round(Sqrt(Eff_Planet_Defense / 2))
                     ...if no base
   Max(above, Eff_Beam_Tech)
                     ...if base present

Beam_count =
   Round(Sqrt(Eff_Total_Defense / 3))
   ...at most 10 if AllowAlternativeCombat is off
   ...at most 20 if AllowAlternativeCombat is on

Torp_type =
   Round(Sqrt(Eff_Planet_Defense / 2))
                     ...if no base
   Max(above, Eff_Torp_Tech)
                     ...if base present

Tube_count =
   Round(Sqrt(Eff_Total_Defense / 4))
   ...at most 20

Torp_count =
   Tube_count * PlanetaryTorpsPerTube
   ...plus Trunc(Base_torp_cost / Torp_money_cost(Torp_type))
      if base present and UseBaseTorpsInCombat enabled
   ...at most 255

   Base_torp_cost =
      Torp_money_cost(1) * Torps_in_storage(1)
      + Torp_money_cost(2) * Torps_in_storage(2)
      ...
      + Torp_money_cost(10) * Torps_in_storage(10)

Bay_count =
   Round(Sqrt(Eff_Planet_Defense))
   ...plus 5 if base present

Fighter_count =
   Round(Sqrt(Eff_Planet_Defense)) + Base_fighters

Combat Formulas

Weapon Specs

Beam_hit_odds = Eff_BeamHitOdds
    + Trunc((Beam_Kill_Power + Beam_Expl_Power) * Eff_BeamHitBonus / 100)
Beam_recharge_rate = BeamRechargeRate
    + Trunc((Beam_Kill_Power + Beam_Expl_Power) * Eff_BeamRechargeBonus / 100)
Beam_recharge_per_tick = Random(Beam_recharge_rate)

Torp_hit_odds = Eff_TorpHitOdds
    + Trunc((Torp_Kill_Power + Torp_Expl_Power) * Eff_TorpHitBonus / 100)
Torp_recharge_rate = Eff_TubeRechargeRate
    + Trunc((Torp_Kill_Power + Torp_Expl_Power) * Eff_TubeRechargeBonus / 100)
Torp_recharge_per_tick = Random(Torp_recharge_rate)

Bay_recharge_rate = Eff_BayRechargeRate + Eff_BayRechargeBonus * Num_Bays
Bay_recharge_per_tick = Random(Bay_recharge_rate)

Eff_XXX means the effective value of a configuration option, which consists of the actual configuration value plus the experience modifier of the unit using the weapon.

Weapon Effects

Every weapon hit is defined by three values:

  • The Expl_Power is the explosive power which reduces the unit's shields and damages the hull.
  • The Kill_Power is the amount of gamma radiation emitted, which kills crew.
  • Weapons may emit Death Rays instead, which have a different characteristics. Death rays also kill crew when the ship still has shields, and do not damage the hull.

Beams: The Expl_Power and Kill_Power are taken from the weapon definition. A beam emits death rays if its Expl_Power is zero. If the owner of the weapon is Privateer, the Kill_Power is tripled. The beam's charge affects its power; a beam fired at charge 600 has only 60% of its normal power.

Expl_Power =
   Trunc(Beam_expl_power * Beam_charge / 1000)
Kill_Power =
   Trunc(Beam_kill_power * Beam_charge / 1000) * PF
   ...with PF=3 if beam owner is privateer, PF=1 otherwise

Torpedoes: The Expl_Power and Kill_Power are taken from the weapon definition. If AllowAlternativeCombat is off, the values are doubled (note that some clients already display the doubled values). A torpedo emits death rays if its Expl_Power is zero.

Fighters: The Expl_Power and Kill_Power are defined by FighterBeamExplosive and FighterBeamKill, respectively. Fighters never fire death rays.

Normal Combat

The following formulas are used if AllowAlternativeCombat is off. In this case, all results are integers, weapon hits do at least one point of damage.

Shield_damage =
   Trunc(1.5 + ((Expl_Power * Eff_ShieldDamageScaling
                + Kill_Power * Eff_ShieldKillScaling) / (Mass + 1)))

Hull_damage =
   Trunc(1.5 + ((Shield_damage * Eff_HullDamageScaling / (Mass + 1)))

Crew_killed =
   Trunc(0.5 + ((Kill_Power * Eff_CrewKillScaling) / (Mass + 1)))
   ...at least 1 if the weapon emits Death Rays.

Alternative Combat

The following formulas are used if AllowAlternativeCombat is on. In this case, results can be fractional numbers, and damages are tracked in fractions. Therefore, weapon hits can do less than one point of damage.

Shield_damage =
   (Expl_Power * Eff_ShieldDamageScaling
    + Kill_Power * Eff_ShieldKillScaling) / (Mass + 1)

Hull_damage =
   (Expl_Power * Eff_HullDamageScaling) / (Mass + 1)

Crew_killed =
   (Kill_Power * Eff_CrewKillScaling) / (Mass + 1)

Eff_XXX means the effective value of a configuration option, which consists of the actual configuration value plus any experience modifier of the unit being hit (these are the defensive bonuses).

Result for Ships

Ship captured: If a ship was captured in combat (because it lost its crew), it changes ownership. It receives a small emergency crew (the capturing ship doesn't lose any crew beyond that killed in combat, though).

Crew_after_combat =
   Min(10, Hull_crew)

Cyborgs beam debris

If a Borg ship (PlayerRace = 6) destroys another ship, it will attempt to beam up debris of the destroyed ship.

Neutronium_gathered =
   Neutronium_on_ship

Tritanium_gathered =
   Tritanium_on_ship + Hull_tritanium_cost

Duranium and Molybdenum are gathered using the same formula as Tritanium. The order is Tritanium, Duranium, Molybdenum; if the cargo room is full before all minerals are gathered, the excess is not loaded up.

Result for Planets

Planet lost: If a planet was lost in combat, it changes ownership. If the planet had a starbase, that one is destroyed.

Defense_after_combat =
   0
Colonists_after_combat =
   Trunc(Colonists * Eff_SurvivalRate / 100)
      ...where Eff_SurvivalRate =
            Trunc(ColonistCombatCaptureRate[Ship_Owner] * ColonistCombatSurvivalRate[Planet_Owner] / 100)
      ...at least 1
Colonist_happiness_after_combat =
   Colonist_happiness - Trunc(Eff_SurvivalRate / 2)
      ...at least MinimumHappiness
Experience_points_after_combat =
   Trunc(Experience_points * Eff_SurvivalRate / 100)
Natives_after_combat =
   Trunc(Natives * NativeCombatSurvivalRate / 100)
Native_happiness_after_combat =
   Native_happiness - 20
      ...at least MinimumHappiness

Planet won: Ammunition that was fired in combat is used from the starbase's storage first. The base also takes damage first.

Fighters_lost =
   Min(Fighters_launched_in_combat, Base_fighters)

Torpedoes used in combat are made up of all types' storage, so after combat, the base loses torpedoes of all types. PHost uses the following algorithm:

Torpedo_costs =
   Torps_fired * Torp_money_cost(Torp_type)

Repeat
   For i:=1 To 10     (iterate through all torpedo types)
      If (Torps_in_storage(i) > 0) And (Torpedo_costs > Torp_money_cost(i))
         Remove one torpedo of type i
         Decrease Torpedo_costs by Torp_money_cost(i)
      EndIf
   EndFor
Until the above loop didn't find any more torpedoes to remove

This attempts to make sure losses are distributed equally among all types.

Ending Status: The planet can take part in several fights during a turn. After all these fights are completed, damage effects are computed.

Base_damage_after_combat =
   Base_damage + Damage_taken
   ...if that is 100 or more, the base is destroyed

Base_defense_after_combat =
   Trunc(Base_defense * Damage_taken / 100)

Max_base_tech =
   Max(1, Trunc((100 - Base_damage_after_combat) / 10))

Defense_after_combat =
   Trunc(Planet_defense * Damage_taken / 100)

The Max_base_tech is the maximum tech level the base can have after combat. Tech levels above that limit are reduced.

Back to top


Minefields

Minefield Action Flowchart

This table lists all events that affect or are related to minefields, in the order in which they happen.

Stage Action Details
MinefieldDecay Minefields decay Formulas
MineLaying Ships perform mine lay missions (e.g. Lay Mines and Lay Web Mines). Formulas
MinesDestroyMines Overlapping hostile mines destroy each other Formulas
MineSweeping Ships sweep enemy mines or scoop own mines. Formulas
MinefieldScan Ships generate mine scan reports
WebDraining Web fields drain fuel from ships inside the field
MoveIonStorms Ion storms move (means: their old position determines which minefields they hide)
BuildTorpedoes Ships build torpedoes (means: torpedoes built on ships cannot be laid the same turn).
BaseMissions_1 This includes the Load Torpedoes onto Ships mission (means: torpedoes loaded here cannot be laid the same turn).
Movement Ships move (and are thus vulnerable to mine hits). Mine Hit Formulas
SupplyRepair_2 Ships repair themselves (e.g. repair mine hit damage)
BaseMissions_2 This includes the Refuel Ships mission which loads new fuel onto ships (e.g. those that lost it in webs)
Combat Combat Combat formulas

General

Minefields consist of an integer number of minefield units. From that, the minefield's radius is computed:

Minefield_radius =
   Sqrt(Mine_units)

Internally, PHost uses that exact value. In messages, the radius is reported as Trunc(Minefield_radius), the diameter is reported as 2 * Trunc(Minefield_radius).

Some clients evaluate the Mine_units directly to plot minefields a little more exact. For example, PCC plots Round(Minefield_radius).

Mine Decay

Each turn, minefields decay and shrink a little.

Mine_units_after_decay =
   Trunc(Mine_units * (100 - MineDecayRate) / 100)
                     ...if minefield contains normal mines
   Trunc(Mine_units * (100 - WebMineDecayRate) / 100)
                     ...if minefield contains web mines

Mine Laying

Torpedo ships can lay mines. The same formulas apply to web laying.

Torps_laid =
   Min(Torps_available, Trunc((Max_units - Existing_units) / Conversion_rate))
   ...0 if Conversion_rate is zero or Existing_units >= Max_units
   ...where
      Conversion_rate =
         Trunc(Torp_type^2 * Min(UnitsPerTorpRate[Ship_owner],
                                 UnitsPerTorpRate[Minefield_owner]) / 100)
      Max_units =
         MaximumMinefieldRadius[Minefield_owner] ^ 2
         ...if it is a normal mine field
         MaximumWebMinefieldRadius[Minefield_owner] ^ 2
         ...if it is a web field

Units_laid =
   Torps_laid * Conversion_rate

Existing_units is the number of mine units in the field we're adding to, 0 if it is a new field. Torps_available is the number of Torps allowed for laying using mdX or an extended mission like Lay Minefield. Note that Ship_owner and Minefield_owner can differ if miX or an extended mission is used.

PHost refuses adding torpedoes to a minefield that is close to its maximum size if that would exceed the maximum. For example, Robots cannot add a Mark 8 torpedo (type 10) to a 99 ly (=9801 units) minefield if the maximum radius is 100 ly, because 9801+400 = 10201 which is more than the allowed 10000. In pathological configurations, PHost may even refuse to lay a new minefield if laying a single torpedo would already exceed the limit.

Overlapping Minefields Explode

Preconditions: If two minefields overlap, i.e. Radius_1 + Radius_2 > Sqrt((X_1-X_2)^2 + (Y_1-Y_2)^2), they interact unless any of the following holds:

  • minefields are of different type (web vs. normal) and AllowMinesDestroyWebs is disabled (default)
  • minefields are owned by same player
  • minefield owners are allies, and have a mutual mine-level alliance

In the 1:1 case, i.e. two hostile minefields overlapping, the following formula is used:

Mines_exploding =
   Min(Units_1, Units_2)
                     ...if Dist = 0
   Units_1           ...if A < 0
   Units_2           ...if A > D
   Units_1 - A^2     ...otherwise
   ...with
      D = Distance between minefield centers
      A = (Units_1 - Units_2 + D^2) / (2*D)
      Units_1, Units_2 =
          Number of mine units contained in both fields

This formula is used with AlternativeMinesDestroyMines enabled when a minefield is laid or enlarged to overlap an old one. In this case, PHost iterates through all other minefields in Id order, checks whether there is a hostile overlap, and applies the above formula.

During the Mines destroy Mines stage of host processing, PHost uses more complicated formulas. The basic ideas are the following: a minefield that overlaps N hostile minefields shrinks at a speed of N minefield units per time unit. For each minefield pair, we can therefore compute the time until the overlap is gone. PHost now computes the minimum such time, and removes N * Time units from all minefields. At least one overlap is gone now. All the speeds are now recomputed, and the algorithm restarts if overlaps remain.

Time_until_overlap_gone =
   Min(U1 / S1, U2 / S2)
                     ...if D=0
                        (i.e. concentric minefields)
   U1 / S1           ...if U2*S1 - U1*S2 >= D^2 * S1
                        (i.e. field 2 eliminates field 1 completely)
   U2 / S2           ...if U1*S2 - U2*S1 >= D^2 * S2
                        (i.e. field 1 eliminates field 2 completely)
   (U1 - A^2) / S1   ...if S1 = S2
   ((U1-U2-D^2)*S1 - (U1-U2+D^2)*S2 + 2*Sqrt(D^2 * (U2*S1^2 - (U1+U2-D^2)*S1*S2 + U1*S2^2)))
     / (S1 - S2)^2   ...otherwise
   ...with
      D = Distance between minefield centers
      A = (U1 - U2 + D^2) / (2*D)
      U1,U2 = Mine units in both fields (Units_1, Units_2)
      S1,S2 = Shrink speeds, i.e. overlap counts for both fields

The frightening last formula is the solution to Sqrt(U1 - S1*x) + Sqrt(U2 - S2*x) = D obtained using Mathematica.

PHost's actual implementation is heavily optimized for speed. It does not consider all minefields at once; instead it groups them into possibly interacting partitions first (i.e. start by picking an arbitrary minefield, and add all that may interact with it considering owner, type and location, add all that may interact with the fields just added, and so on). In addition, to guarantee reasonable progress, it always advances time by a minimum amount Epsilon, which can make a minefield lose a handful more units than required to remove all overlaps. Epsilon currently is 1.0 divided by the maximum overlap occuring in the current partition, but at least 1/16, empirically chosen by testing as a compromise between precision and speed.

To predict mines-destroy-mines at client-side, it probably suffices to use an Epsilon of 1/16 all the time.

After computing all losses, PHost attempts to sort them in a way that they can be reasonably incorporated into the game data, and reported to players. The above formulas may yield results such as "minefield pair #17/#233 loses 0.12 units", but since the unit count must be an integer, PHost has to round again. Let Loss(A,B) be the loss of minefield pair A/B as computed according to the above algorithm. The amount ultimately reported using util.dat record 29 and the message will be

Rounded_loss(A,B) =
   Min(Ceil(Loss(A,B)), Units_A, Units_B)

New v4.1a: In case this formula causes a minefield to lose less mines than needed to resolve all overlaps, PHost will finally remove the excess using util.dat record 53. This generally happens only when many overlaps exist with a single field.

Mine Sweeping

Mine sweeping comes in two flavours: normal sweeping and scooping. Scooping is selected using the msc friendly code or the Scoop Torpedoes extended mission. Mine sweeping scans all minefields in range, and destroys hostile minefields if possible. Mine scooping, in addition, gathers up torpedoes from a minefield owned by the same player as the ship, and turns it back into torpedoes.

Units_swept =
   Beam_contribution + Fighter_contribution

Beam_contribution =
   Beam_count * Beam_type^2 * Sweep_rate
   ...where Sweep_rate is WebMineSweepRate or MineSweepRate
                     ...if ship has beams, is within range, and field not
                        hidden by ion storm
   0                 ...otherwise

Fighter_contribution =
   Fighter_count * FighterSweepRate
                     ...if ship has bays, and is within FighterSweepRange,
                        and minefield is a normal minefield
                            or ship is owned by colonies and AllowColoniesSweepWebs is enabled
   0                 ...otherwise

Torps_scooped =
   Min(Trunc(Mine_units / Conversion_rate), Free_cargo_room, Mission_limit)
   ...where
      Conversion_rate =
         Trunc(Torp_type^2 * UnitsPerTorpRate[Ship_owner] / 100)

Units_scooped =
   Mine_units        ...if Trunc(MineUnits / Conversion_rate) = Torps_scooped
   Torps_scooped * Conversion_rate
                     ...otherwise

The Units_scooped formula put in other words: If, after scooping the requested number of torpedoes, the equivalent of a fractional torpedo remains (i.e. less than Conversion_rate mine units), the minefield is completely removed.

If the Scoop Torpedoes mission is used, the parameter specifies the maximum number of torpedoes to scoop from each applicable minefield, not the total amount. This is denoted by the Mission_limit variable above. If the regular Mine Sweep mission together with the msc friendly code is used, there is no Mission_limit.

Mine Hits

Mine Hit Odds

The probability that a ship hits a mine depends on the type of the minefield and the ship's speed.

Mine_hit_odds =
   0                 ...if Speed <= Safe_warp
   Max(0, Base - (9 - Speed) * Bonus / 100)
   ...with
      (Base, Bonus) =
          WebMineHitOdds, WebMineOddsWarpBonusX100
                     ...if web minefield
          MineHitOdds, MineOddsWarpBonusX100
                     ...if ship is not cloaked, normal minefield
          MineHitOddsWhenCloakedX10/10, CloakMineOddsWarpBonusX100
                     ...if ship is cloaked, normal minefield

If you put the normal integer option values into this formula, you will get out a (possibly fractional) percentage, 0 to 100. This is the probability that the ship hits a mine when moving through 1 ly of the minefield.

All relevant options are arrayized and can therefore be different for each player. PHost consults the ship owner's value when moving a particular ship. It does not matter who owns the minefield.

PHost uses standard probability calculus to determine whether a ship successfully passes a minefield and, if it hits a mine, where that happens. For your convenience, here is the formula that a ship successfully passes a minefield of a given size (which is not in this form used in PHost):

Percentage =
   100 * (((100 - Mine_hit_odds)/100) ^ Distance)

Mine Hit Effects

Damage_taken =
   Trunc(100 * MineHitDamageFor100KT / Hull_mass)
                     ...for normal mines
   Trunc(100 * WebHitDamageFor100KT / Hull_mass)
                     ...for web mines

Other effects of a mine hit (all of them are checked, in this order):

  • if the ship hit a web mine, it burns Max(WebHitFuelLoss, Fuel/6) fuel, and stops (warp 0).
  • if the ship is Lizard and has 150% damage or more, it explodes. Lizard ships with more than 100% damage are permitted to continue their move (and thus bring their towee home, for example), but will explode after their movement is finished.
  • if the ship is not Lizard and has 100% damage or more, it explodes.
  • if the ship exceeds the DamageLevelForCloakFail, it de-cloaks (this affects its mine hit odds for further movement, if any).
  • if the ship has hardened engines, it continues movement normally (unless it hit a web mine, in which case its speed is now 0).
  • otherwise,
    • if HullTechNotSlowedByMines = 0, its speed is recomputed according to the speed limit formula at the beginning of this section. It continues movement for the remaining time with that speed. In other words, Permitted_dist is reduced to Distance_moved + New_max_dist * (1.0 - Movement_time_used).
    • otherwise, if Hull_tech < HullTechNotSlowedByMines, the ship's Permitted_dist is reduced by 10 ly. For example, a regular ship moving at Warp 9 has a Permitted_dist of 81 ly. The first mine hit reduces that to 71 ly, the second one to 61 ly, and so on.
    • otherwise, if Hull_tech >= HullTechNotSlowedByMines, the ship continues moving normally.

Back to top


Wormholes

General Specs

The size of the wormhole entry point depends on its mass.

Wormhole_radius =
   (Wormhole_mass ^ (WrmEntryPowerX100 / 100)) / 2

Note that PHost uses the exact fractional value internally. Ufos will report Trunc(Wormhole_radius) (versions up to 3.3d always report 5).

Endpoint Movement: Wormhole endpoints move each turn. They move towards their waypoint up to WrmDisplacement lightyears in X/Y direction, and add some random jitter of WrmRandDisplacement.

Endpoint_displacement =
   Waypoint_displacement + Random_displacement
   ...where
      Random_displacement =
         Random(1 + 2*WrmRandDisplacement) - WrmRandDisplacement
      Waypoint_displacement =
         Waypoint_position - Endpoint_position
         ...at most WrmDisplacement
         ...at least -WrmDisplacement

Size changes: Wormholes change their mass and instability each turn.

Mass_change =
   WrmMassAdd + Random(1 + 2*WrmRandMass) - WrmRandMass

Instability_change =
   -WrmStabilityAddX10 / 10 + Random(1 + 2*WrmRandStability) - WrmRandStability

The mass is an integer in the range 1..32767, the instability is a possibly fractional value in range 0..100. Note that if the mass drops to zero by this process, the wormhole dies, and PHost will set the start and end coordinates to (0,0).

Scanning for Wormholes

Let Ship_dist be the distance between the scanning ship and the center of the wormhole entry point (the X,Y from wormhole.txt).

Deterministic Scanning: A ship sees all wormholes for which Ship_dist <= WrmScanRange. A ship with the ScansAllWormholes function sees all wormholes for which Ship_dist <= 2 * WrmScanRange. These rules are only applied if WrmScanRange is not 0.

Probabilistic Scanning: The chance to see a wormhole depends on its mass.

Detection_radius =
   10 * Wormhole_mass ^ (1/3)
Detection_chance =
   100%              ...if Ship_dist <= Detection_radius
   (4 - (Ship_dist/Detection_radius)^2) * 33.3%
                     ...if Detection_radius < Ship_dist <= 2*Detection_radius
   0%                ...if Ship_dist > 2*Detection_radius

For every wormhole, a dice is rolled.

  • Ships with the ScansAllWormholes function will see all wormholes found this way.
  • Other ships see only the closest wormhole found this way (which need not really be the closest wormhole; there might be one which is closer but which was not seen by chance).

Wormhole Travel

Ships must be within Wormhole_radius lightyears from the endpoint's center to enter a wormhole.

Wormhole travel imposes some stress on the wormhole. The higher the stress, the higher the chance of a travel failure.

Wormhole_stress =
   0                 ...if Ship_mass < Wormhole_mass
   ((Ship_mass / Wormhole_mass) - 1)^2
                     ...otherwise

Travel_failure_odds =
   Wormhole_stress + Wormhole_instability
     + (Wormhole_stress*Wormhole_instability / 10)

Travel_failure_figure =
   Random(100)

Travel_fuel_usage =
   Fuel_usage(Equiv_distance, WrmTravelWarpSpeed)
   ...where Equiv_distance = Wormhole_distance / WrmTravelDistDivisor

==> Note that PHost up to version 4.0j/3.4l truncates the Travel_failure_odds value to an integral number. Later versions use the exact value.

Possible outcomes of wormhole travel:

Travel_fuel_usage > Fuel
   Travel fails:
      New_ship_X   = 1000 + Random(2001)
      New_ship_Y   = 1000 + Random(2001)
      Damage_taken = 25 + Random(75)
Travel_fuel_usage <= Fuel and Travel_failure_figure >= Travel_failure_odds
   Safe travel:
      New_ship_X   = Endpoint_X - 10 + Random(21)
      New_ship_Y   = Endpoint_Y - 10 + Random(21)
      Damage_taken = 0
Travel_fuel_usage <= Fuel and Travel_failure_figure < Travel_failure_odds
   Successful travel with damage taken:
      New_ship_X   = Endpoint_X - 10 + Random(21)
      New_ship_Y   = Endpoint_Y - 10 + Random(21)
      Damage_taken = (Travel_failure_odds - Travel_failure_figure) ^ 2

The wormhole instability provides a rating for the safety of this wormhole. It is summarized and reported to players using the following levels:

Wormhole_stability_rating =
   "very stable"     ...if Wormhole_instability <= 5
   "stable"          ...if Wormhole_instability <= 15
   "mostly stable"   ...if Wormhole_instability <= 30
   "unstable"        ...if Wormhole_instability <= 50
   "very unstable"   ...if Wormhole_instability <= 80
   "completely unstable"
                     ...otherwise

Back to top


Ion Storms

Ion storm classes are described in the Rules section.

The results of all the formulas below are limited by the MaximumIonStormRadius and MaximumIonStormVoltage configuration options.

Storm Movement

Speed   = 8              ...if Voltage > 250
          6              ...if Radius < 200
          2 + Random(3)  ...otherwise, i.e. random between 2 and 4
Heading = Old_heading - 10 + Random(21)
                         ...i.e. change by +/- 10 degrees

New Storms

Initial_voltage =
    5 + Random(Trunc(Number_of_ships_in_game) / 5)
    ...that is, stronger storms later in the game
Initial_radius =
    10 + Random(190)
    ...that is, 10 to 200
   

The initial speed is chosen according to regular movement rules.

The initial location depends on the setting of AllowWraparoundMap.

Wraparound enabled

With wraparound enabled, storms appear at a random place, with random heading.

Wraparound disabled

With wraparound disabled, storms appear in the outer regions of the map, moving roughly inwards.

First, PHost picks an Angle (0..360 degrees) and a Distance (500..1200).

Initial_X =
    Round(Center_X + Sin(Angle)*Distance*Size_X/2000)
Initial_Y =
    Round(Center_Y + Sin(Angle)*Distance*Size_Y/2000)
Initial_heading =
    Angle + 140 + Random(81)
    ...that is, towards the galaxy center, but at most 40 degrees
       to the left or right

The Center_X/Y and Size_X/Y are center and size of the universe, and are derived from WraparoundRectangle. That parameter should therefore still be set, even when wraparound is not used.

With the default WraparoundRectangle of 1000,1000,3000,3000, ion storms will appear within a distance of 500..1200 from the galaxy center. This is the same as with HOST. For larger, smaller, or non-square universes, the formula will adjust the distance accordingly.

Growing Storms

Voltage_change =
   2 * Random(6)
   ...i.e. 0 to 10 in steps of two
Radius_change =
   Random(4)
   ...i.e. 0 to 3
New_voltage =
   Old_voltage + Voltage_change + Additional_gain

New_radius =
   Old_radius - Radius_change
   ...if Old_radius > Radius_change
   1 - (Old_radius - Radius_change)
   ...otherwise

The Additional_gain consists of four bonuses that are given to each storm. Note that, because growing storms always have odd voltages, adding one will turn the storm into a weakening one. All four conditions are checked, so the bonus may be anything between zero and four.

Additional_gain =
   +1   ...if second rule was used for New_radius
   +1   ...with a 1% chance
   +1   ...with a 2.5% chance if New_voltage > 320
   +1   ...with a 10% chance if New_voltage > 500

Weakening Storms

Voltage_change =
   4 + 2 * Random(6)
   ...i.e. 4 to 14 in steps of two
Radius_change =
   Random(11)
   ...i.e. 0 to 10
New_voltage =
   Old_voltage - Voltage_change
   ...Storm disappears if this is zero or less
New_radius =
   Old_radius + Radius_change

Pancaking Effect: The pancaking effect has a probability of 1:33 to occur, and happens after normal radius and voltage changes. This may change the storm into a growing one.

Voltage_after_pancaking =
   Round(Sqrt(New_voltage))
Radius_after_pancaking =
   2 * New_radius

Merging storms

Storms Id_1 and Id_2 merge if they partially overlap: Distance_of_centers^2 <= Radius_1^2 + Radius_2^2.

New_X =
    Trunc((X_1 * Voltage_1 + X_2 * Voltage_2) / (Voltage_1 + Voltage_2))
New_Y =
    Trunc((Y_1 * Voltage_1 + Y_2 * Voltage_2) / (Voltage_1 + Voltage_2))
    ...i.e. weighed average; closer to center of stronger storm
New_Radius =
    Trunc(Sqrt(Radius_1^2 + Radius_2^2))
    ...i.e. the storm is larger than each of the original ones
New_voltage =
    Trunc((Voltage_1 * Radius_1^2 + Voltage_2 * Radius_2^2) / (Radius_1^2 + Radius_2^2))
    ...i.e. weighed average

The Id of the new storm is the same as the Id of the stronger storm; if they are equally strong, it is the higher of the two. The new heading and speed are taken from that storm.

Whether the new storm is growing or weakening depends upon whether the computed New_voltage is odd or even.

Effects

Ships: All storms de-cloak ships that don't have advanced cloaking. Storms stronger than 150 MeV affect all ships that do not cloak or were thus uncloaked, and which do not have an Ion Shield.

Essentially, this means that ships with advanced cloak are safe from ion storms while they are cloaked. However, this means they cannot do anything else while inside the storm. Ships with Ion Shield, in contrast, can do any mission without being in danger of getting damaged. The Ion Shield alone will not help them stay cloaked, though.

Ship_new_location_X = Round(Ship_location_X + Sin(Heading) * Distance)
Ship_new_location_Y = Round(Ship_location_Y + Cos(Heading) * Distance)
   ...with
      Distance = 0.75 * Storm_warp^2
      Heading  = Storm_heading * PI/180   (i.e. converted to radiant)

Ship_damage_taken =
   Trunc((Random(200) + Storm_voltage - Ship_mass - 20 * Ship_engine) * Exp_modificator)
   ...with Exp_modificator =
         1        ...if NumExperienceLevels = 0
         1 - (Ship_experience_level / NumExperienceLevels)
                  ...otherwise
   ...plus 50 if ship does not have fuel
   ...at least zero, of course
Ship_damage =
   Ship_damage + Ship_damage_taken
Ship_crew =
   Trunc(Ship_crew * (100 - Ship_damage_taken) / 100)
Ship_experience_gain =
   Trunc(EPShipIonStorm100MEV * (Storm_voltage - 100) / 100)

Minefields: Ion storms can hide minefields from scanning ships. A minefield is hidden if all of the following conditions apply:

  • The storm radius is strictly greater than the minefield radius, i.e. Storm_radius > Minefield_radius.
  • The minefield center lies inside the ion storm, i.e. Distance_between_centers <= Storm_radius
  • The scanning ship belongs to a different player than the minefield.

A hidden minefield cannot be swept or seen using beams alone. If the ship can use fighters to sweep (FighterSweepRate nonzero), those will see and sweep the minefield, though.

Back to top


Last updated 31 May 2015.


Mail support@phost.de for support, ideas, bug reports, questions. Contact Details