[PHost Logo]©

Detailed Operation
The Portable Host
Version 3.2.3.5g

INDEX


Introduction

This file contains details regarding the inner workings of PHOST. If you believe that some aspect of PHOST operation should be documented in this file but it is not, then please send e-mail to the PHOST support team and bring this to our attention.

Back to the index


Mission Ordering

PHOST performs the following events in sequence. The phase numbers (1, 2, and 3) refer to broad stages of host processing. Phase 1 represents the processing of TRN files, phase 2 represents the bulk of host processing, phase 3 represents the generation of RST files. AUXHOST 1 programs run between phase 1 and phase 2, while AUXHOST 2 programs run betweeen phase 2 and phase 3.

Phase 1

  1. Host Data Checking
  2. Turn File Processing:
  3. Alliance Status Processing (if DelayAllianceCommands is disabled)
  4. Cheat Checking

Phase 2

  1. Ship/Planet Ownership Transfers (give command, gsN friendly code)
  2. Meteors
  3. Meteor Showers
  4. Loki Anti-Cloak Tachyon Fields
  5. Birdman Super Spy Deluxe Missions
  6. New Natives Appearing
  7. Privateer Rob Mission
  8. Gambling Ships
  9. Cargo Dump (also Imperial Assault and bdm action) from ships to foreign planets
  10. Cargo Transfer from ships to foreign ships
  11. Overloaded ships are trimmed down
  12. Beam Transfer Friendly Codes (btt, btf, and btm)
  13. Cargo Gather missions
  14. Beam Transfer Planetary Credits (bum action)
  15. Minefield Decay
  16. Mine Laying and Minefield Explosions
  17. Minefields which still overlap explode (3.4f and later)
  18. Minefield Friendly Code Assignment
  19. Minefield Sweeping
  20. Minefield Scanning
  21. Wormhole Scanning
  22. Web Draining
  23. Special Missions 1 (Fed Super Refit, Lizard Hiss, Cyborg self repair)
  24. Fighter Building for Ships (and lfm action)
  25. Torpedo Building for Ships
  26. Alchemy Functions (including Aries advanced refinery)
  27. Populate Build Queue (build orders from starbases, and cln orders)
  28. Ship Building/Cloning
  29. Dump Old Starbase Parts
  30. Starbase Missions: Fix, Recycle, Load Torps, Max Defense, Force Surrender
  31. Ship Repair Using Supplies
  32. Boarding Parties (tow capture)
  33. Free Fighters for Starbases
  34. Ship Movement: tow resolution, movement, intercept resolution, intercepts, wormhole travel, gravity wells
  35. Glory Devices
  36. Chunneling
  37. Loki Anti-Cloak Tachyon Fields (again)
  38. Ship Colonize Mission
  39. Ship Repair Using Supplies (again)
  40. Starbase Missions: Refuel, Unload Freighters, Repair Base
  41. Combat
  42. Ship Repair Using Supplies (again)
  43. Science Ships
  44. Ship Sensor Sweep Mission
  45. Special Missions 2 (Klingon Pillage, Rebel Ground Attack, Empire Dark Sense)
  46. Planetary Production: mines, supplies, TUDR
  47. Planetary Happiness Changes
  48. Planetary Taxation
  49. Planetary Colonist and Native Growth
  50. Planetary Losses: climate deaths, overpopulation supply loss, structure decay, losses due to riots/civil war, losses from Amorphous natives
  51. Clear Invalid Tow/Intercept Missions (on ships destroyed in combat)
  52. Ship Building/Cloning (again)
  53. Cyborg Native Assimilation
  54. Birdmen Super Spy Mission
  55. Ship Exploration Mission
  56. Update player activity levels and build queue priorities
  57. Build Queue Report

Phase 3

  1. External Message Processing (MESS.EXT file)
  2. Ships Scan for Enemy Ships
  3. Planets Scan for Enemy Ships
  4. SHIPSCAN.EXT file is written
  5. Ship Adjustments: ships with >100% damage deleted, waypoints truncated, tow missions that target deleted ships cleared, intercept missions on deleted/non-visible ships cleared, ship speed damage-limited
  6. Calculate Scores
  7. Alliance Status Processing (if DelayAllianceCommands is enabled)
  8. Generate RST Files
  9. Generate UTIL.DAT Files
  10. Append UTIL.EXT files to UTIL.DAT files
  11. Remove UTIL.EXT files
  12. Remove TRN Files

Back to the index


Definitions

Planetary Abbreviations

COL Colonists on a planet
CLANS Clans on a planet or ship (COL / 100)
NAT Natives on a planet
NATCLANS Native clans on a planet (NAT / 100)
F Factories
M Mines
DP Planetary defense posts
BDP Base defense posts
PDAMAGE Planetary damage during combat
SUP Supplies
MC Megacredits
FC Friendly code
CTAX Colonist tax rate (%)
NTAX Native tax rate (%)
NATGOV Native government (1=Anarchy, 9=Unity)
CHAPPY Colonist happiness (-300 to 100)
NHAPPY Native happiness (-300 to 100)
TEMP Planet temperature

Mathematical Notation

ABS(X) Absolute value of X
ARCTAN(Y,X) Four-quadrant arctangent of Y/X
COS(X) Cosine of argument (argument is in radians)
DIST(X,Y) Distance in LY from X to Y
ERND(X) Round floating point to nearest integer. Values that have a fractional part of exactly 0.5 are rounded to the nearest even integer (e.g., 8.5 becomes 8, 9.5 becomes 10)
EXP(X) Exponential function of X (i.e., 2.71828^X)
MAX(A,B) Maximum of A and B
MIN(A,B) Minimum of A and B
RAND(N) Random integer in the range 0 to N-1 inclusive
RND(X) Round floating point number to nearest integer
SGN(X) Sign of X (+1 if positive, -1 if negative, else 0)
SIN(X) Sine of argument (argument is in radians)
SQRT(X) Square root of X
TRUNC(X) Truncate floating point by discarding fractional part
X^N X to the Nth power

Other Conventions

cfg_XXXX This notation represent the configuration option XXXX

Colonist/native populations are generally expressed as number of persons, not clans. When a formula yields a number of colonists/natives which is not evenly divisible by 100, the result is rounded down. Unless specified otherwise, PHOST truncates results.

Back to the index


Planetary Formulas

     COL survived after meteor strike = COL * (RAND(91) + 10) / 100

     NAT survived after meteor strike = NAT * (RAND(100) + 1) / 100

     CHAPPY after meteor strike = CHAPPY - (RAND(31) + 50)

     NHAPPY after meteor strike = NHAPPY - (RAND(31) + 50)

     NHAPPY after combat in which planet was lost = NHAPPY - 20

     COL after Klingon pillage = TRUNC(0.8*COL - 2000)
     CHAPPY after Klingon pillage = CHAPPY - 10

     NAT after Klingon pillage = TRUNC(0.8*NAT - 12000)
     NHAPPY after Klingon pillage = NHAPPY - 10

     SUP generated by Klingon pillage = TRUNC((COL+NAT) / 10000)
     MC  generated by Klingon pillage = TRUNC((COL+NAT) / 10000)
     (COL and NAT are populations present *before* reduction)

     CHAPPY after Rebel RGA = CHAPPY - 60
     NHAPPY after Rebel RGA = NHAPPY + 40
     MC     after Rebel RGA = TRUNC(MC*7/10)
     SUP    after Rebel RGA = TRUNC(SUP*6/10)
     DP     after Rebel RGA = TRUNC(DP*8/10)
     M      after Rebel RGA = TRUNC(M*4/10)
     F      after Rebel RGA = TRUNC(F*7/10)
     COL    after Rebel RGA = TRUNC(COL*8/10)

     Minerals mined = ROUND(MINING_RATE * M / 100)
     MINING_RATE = ROUND((cfg_RaceMiningRate * MINERAL_DENSITY) / 100)
     If at least 100 Reptilian natives are present,
        MINING_RATE = MINING_RATE * 2
        PHost before version 3.5 used TRUNC instead of ROUND in this formula.

     Colonist supply contribution = F
     Bovinoid supply contribution = MIN(NATCLANS / 100, CLANS),
                                             if planet has Bovinoids
                                  = 0, otherwise
     Total supplies collected =
       = TRUNC((COLONIST_SUPPLIES+BOVINOID_SUPPLIES) * cfg_ProductionRate / 100)

     Minerals from TUDR
       = TRUNC((cfg_TransuraniumDecayRate * MINERAL_DENSITY + 50) / 100)

     Natives assimilated = MIN(COL * cfg_BorgAssimilationRate / 100, NAT)

     TargetTemp = 100 if planet is Crystal and cfg_CrystalsPreferDeserts is ON
                = 50
     TempDivisor = 66 if planet is Crystal and cfg_CrystalsPreferDeserts is ON
                 = 33
     CHAPPY change = TRUNC(10 - SQRT(CLANS/10000.0)
                              - ABS(TEMP-TargetTemp)/TempDivisor
                              - (M+F)/300
                              - CTAX*0.8)

     NHAPPY change = TRUNC(5 + NATGOV/2 - SQRT(NATCLANS/10000.0) - (M+F)/200
                             - NTAX*0.85)
     If natives are Avian,
        NHAPPY change = NHAPPY change + 10

     CHAPPY/NHAPPY change from Hissing
                 = (Number of Hissing ships)*cfg_HissEffectRate
     (Note that hissing happens before taxation; after the hiss effect 
     increases happiness, taxation will it bring down again)

     MC from colonist tax = 0, if CHAPPY < 30
                          = TRUNC((CLANS * CTAX * 5 + 2500) / 5000)
                                         if CHAPPY >= 30
          ...then  MC = TRUNC((MC * cfg_ColonistTaxRate + 50) / 100)

     MC from native tax =
                = 0, if NHAPPY < 30 or natives are Amorphous
                = MIN(TRUNC((NATCLANS * NATGOV * NTAX + 2500) / 5000), CLANS)
          ...then  MC = MC * 2 if natives are Insectoid
          ...then  MC = TRUNC((MC * cfg_NativeTaxRate + 50) / 100)

     Max COL Supply Bonus = 0, if cfg_AllowEatingSupplies is OFF
                          = TRUNC(100*SUP/40), if cfg_AllowEatingSupplies is ON
     Base Max COL on planet
                       = MAX(10 000 000*sin(TEMP * 3.14159 / 200), 100)
                            if planet is Crystalline
                            and cfg_CrystalsPreferDeserts is ON
                            and cfg_CrystalSinTempBehavior is ON
                            and TEMP >= 15
                       = 300 + cfg_MaxColTempSlope*TEMP
                            if planet is Crystalline
                            and cfg_CrystalsPreferDeserts is ON
                            and cfg_CrystalSinTempBehavior is ON
                            and TEMP < 15
                       = MAX(100 000*TEMP, 100)
                            if planet is Crystalline
                            and cfg_CrystalsPreferDeserts is ON
                            and cfg_CrystalSinTempBehavior is OFF
                       = 9000000 if TEMP < 20 and planet is Rebel
                       = 300 + cfg_MaxColTempSlope*TEMP if TEMP < 15
                       = at least 6000 if TEMP > 84 and planet is Klingon,
                                       Robotic, Rebel, or Colonial
                       = 100 + (100 - TEMP)*cfg_MaxColTempSlope if TEMP > 84
                       = SIN(TEMP * 3.14159 / 100) * 10 000 000
                                  if TEMP >= 15 and TEMP <= 84
     Max COL on planet = 25 000 000 if cfg_ClimateLimitsPopulation is OFF
                       = TRUNC(BASE_MAX_COL) + TRUNC(MAX_COL_SUPPLY_BONUS)


     Max NAT on planet = 15 600 000 if cfg_ClimateLimitsPopulation is OFF
                       = (100 000 * TEMP)
                              if natives are Siliconoid
                              and CrystalsPreferDeserts is ON
                              and PHost 3.3c or later
                       = sin(TEMP * 3.14159 / 100) * 15 600 000

     COL_GROWTH_RATE = 0 if CHAPPY < 70 or COL >= Max COL on Planet
                     = 0 if planet is Crystalline
                         and cfg_CrystalsPreferDeserts is ON
                         and cfg_CrystalSinTempBehavior is ON
                         and TEMP < 15
                     = 5*SIN(TEMP * 3.14159 / 200) / (1 + CTAX/5)
                         if planet is Crystalline
                         and cfg_CrystalsPreferDeserts is ON
                         and cfg_CrystalSinTempBehavior is ON
                         and TEMP >= 15
                     = 5 * (TEMP / 100) / (1 + CTAX/5)
                         if planet is Crystalline
                         and cfg_CrystalsPreferDeserts is ON
                         and cfg_CrystalSinTempBehavior is OFF
                     = 0, if TEMP < 15 or or TEMP > 84
                     = 5*SIN(TEMP * 3.14159 / 100) / (1 + CTAX/5)
     COL growth =
         = RND(COL_GROWTH_RATE * COL / 10000) * cfg_RaceGrowthRate
     IF (COL growth + COL > Max COL on Planet) THEN
        COL growth = Max COL on Planet - COL

     COL climate deaths = (COL - 25 000 000) if COL > 25 000 000
                        = 0, if cfg_ClimateLimitsPopulation is OFF
                        = 0, if COL <= Max COL on planet
                        = TRUNC(COL * cfg_ClimateDeathRate / 100)
     If (COL - COL climate deaths < Max COL on planet):
               COL climate deaths = COL - Max COL on planet
     If (COL_climate_deaths < 100):
               COL climate deaths = 0

     Overpopulation supply loss
                    = 0, if cfg_ClimateLimitsPopulation is OFF
                    = 0, if COL <= BASE_MAX_COL_ON_PLANET
                    = MIN(TRUNC((COL-BASE_MAX_COL_ON_PLANET)/4000)+1,SUP)

     NAT_GROWTH_RATE = 0 if NHAPPY < 70
                     = 4*(TEMP/100) / (1 + NTAX/5)
                            if natives are Siliconoids
                            and cfg_CrystalsPreferDeserts is ON
                            and PHost 3.3c or later
                     = 4*SIN(TEMP * 3.14159 / 100) / (1 + NTAX/5)
     NAT growth = 0, if planet is unowned
                = (NAT_GROWTH_RATE * NAT + 50) / 100
     if (NAT growth + NAT > Max NAT on Planet) THEN
        NAT growth = Max NAT on Planet - NAT

     NAT climate deaths = (25 000 000 - NAT) if NAT > 25 000 000
                        = 0, if cfg_ClimateLimitsPopulation is OFF
                        = 0, if planet is unowned
                        = 0, if NAT < Max NAT on planet
                        = TRUNC((NAT * cfg_NativeClimateDeathRate) / 100)
     If (NAT - NAT_climate_deaths < Max NAT on planet):
               NAT climate deaths = NAT - Max NAT on planet

     Max M on planet = CLANS, if CLANS < 200
                     = RND(200 + SQRT(CLANS - 200))

     Max F on planet = CLANS, if CLANS < 100
                     = RND(100 + SQRT(CLANS - 100))

     Max DP on planet = CLANS, if CLANS < 50
                      = RND(50 + SQRT(CLANS - 50))

     COL deaths from Amorphous natives = 500, if NHAPPY >= 70
                                       = 2000, if NHAPPY >= 50
                                       = 4000, if NHAPPY < 50
            deaths = MIN(deaths, COL)

     COL deaths from fighting = COL * (40 - CHAPPY) / 500,
                                    if CHAPPY < 20
                              = COL * (40 - NHAPPY) / 2500,
                                    if NHAPPY < 20

     NAT deaths from fighting = NAT * (40 - NHAPPY) / 500,
                                    if NHAPPY < 20
                              = NAT * (40 - CHAPPY) / 2500,
                                    if CHAPPY < 20

     % chance that planet is detected on sensor sweep =
                   = 0, if DP >= cfg_DefenseForUndetectable
                   = 0, if (M < cfg_MinesForDetectable)
                           AND (F < cfg_FactoriesForDetectable)
                   = 100% *
              (cfg_DefenseForUndetectable - DP) / cfg_DefenseForUndetectable

     Planet industrial activity = "minimal", if (M+F) < 30
                                = "light", if (M+F) < 60
                                = "moderate", if (M+F) < 90
                                = "substantial", if (M+F) < 120
                                = "heavy", if (M+F) >= 120

Back to the index


Ship Formulas

     Fuel needed to remain cloaked = 1 KT after movement phase
                                   = CLOAK_FUEL_BURN+1 KT before movement phase

     CLOAK_FUEL_BURN = 0  if ship has advanced cloaking device
                     = TRUNC(MAX(ShipHullMass, 100) * cfg_CloakFuelBurn / 100)

     UnitsPerTorp = TRUNC(TorpType^2
                          * MIN(cfg_UnitsPerTorpRate[ShipOwner],
                                cfg_UnitsPerTorpRate[MinefieldOwner])
                          / 100)
     MAX_TORPS_TO_LAY = 0, if UnitsPerTorp is 0
                      = TRUNC((cfg_MaximumMinefieldRadius^2 - MinefieldRadius^2)
                                                  / UnitsPerTorp)
     Units laid in minefield = UnitsPerTorp*MIN(Torps, MAX_TORPS_TO_LAY)

     Torps to scoop = MinefieldRadius^2, if UnitsPerTorp is 0
                    = TRUNC(MinefieldRadius^2 / UnitsPerTorp)

     NOTE: If the number of torps created by scooping (as limited by cargo
     room) actually equals TorpsToScoop, then the minefield vanishes (i.e.,
     minefield units leading to fractional torps are lost if the minefield
     is completely swept).

     Mine units swept = TRUNC(BeamType^2 * BeamNumber * cfg_MineSweepRate)
                      = above + (NumFighters * cfg_FighterSweepRate)
                            if ship is in range
                            and (minefield is not a web mine field
                                or (ship is Colonial
                                   and cfg_AllowColoniesSweepWebs is ON))

     Minerals reclaimed from colonizing ship =
          = TRUNC((HullMineral
                         + NumEngines*EngineMineral
                         + NumBeams*BeamMineral
                         + NumTubes*TubeMineral) * cfg_RecycleRate / 100)

     Colonists on planet after colonize mission =
                  = 100*(TRUNC(ShipCrew/100) + 1)

     Max ship speed (warp) = 15 - TRUNC(ShipDamage / 10) if Lizards
                           = 10 - TRUNC(ShipDamage / 10)

     Fuel consumption rate = EngineFuelConsumption / 200 000
                                             if ship hull is gravitonic
                           = EngineFuelConsumption / 100 000
     Fuel consumption = TRUNC(ERND((FuellessShipMass+ShipFuel)/10)
                         * 10*FUEL_CONSUMPTION_RATE
                         * TRUNC(DistanceToTravel))
                          if cfg_UseAccurateFuelModel is OFF
                      = RND((FuellessShipMass+ShipFuel)
                         * (1 - EXP(-FUEL_CONSUMPTION_RATE * DistanceToTravel)))
                             if cfg_UseAccurateFuelModel is ON
     EngineFuelConsumption is derived from value from ENGSPEC.DAT and is
     tabulated in Eden Tan's Infolist[Remote]. It is reproduced here for convenience
     (note that this only applies to the original ship list):
Fuel Usage Warp 1 Warp 2 Warp 3 Warp 4 Warp 5 Warp 6 Warp 7 Warp 8 Warp 9
StarDrive 1 100.00 200.00 300.00 400.00 500.00 600.00 700.00 800.00 900.00
StarDrive 2 100.00 107.50 300.00 400.00 500.00 600.00 700.00 800.00 900.00
StarDrive 3 100.00 106.25 107.78 337.50 500.00 600.00 700.00 800.00 900.00
SuperStarDrv 4 100.00 103.75 104.44 106.25 300.00 322.22 495.92 487.50 900.00
Nova Drive 5 100.00 103.75 104.44 106.25 104.00 291.67 291.84 366.41 900.00
HeavyNova Drv 6 100.00 103.75 104.44 106.25 104.00 103.69 251.02 335.16 900.00
Quantam Drive 7 100.00 103.75 104.44 106.25 104.00 103.69 108.16 303.91 529.63
Hyper Drive 8 100.00 100.00 100.00 100.00 100.00 100.00 102.04 109.38 529.63
Transwarp Drive 100.00 100.00 100.00 100.00 100.00 100.00 100.00 100.00 100.00
     Mine hit damage = TRUNC(100 * cfg_MineHitDamageFor100KT / ShipHullMass)
     Web hit damage  = TRUNC(100 * cfg_WebHitDamageFor100KT / ShipHullMass)

     Ground attack attack factor  = cfg_GroundKillFactor
     Ground attack defense factor = cfg_GroundDefenseFactor + PlanetDefense/20
     Ground attack discriminant = AttackColonists*GROUND_ATTACK_FACTOR
                                   - DefenseColonists*GROUND_DEFENSE_FACTOR
     Planet colonists remaining after ground attack =
         = GROUND_ATTACK_DISCRIMINANT / GROUND_ATTACK_FACTOR
                     if GROUND_ATTACK_DISCRIMINANT >= 0 (attackers win)
         = -GROUND_ATTACK_DISCRIMINANT / GROUND_DEFENSE_FACTOR
                     if GROUND_ATTACK_DISCRIMINANT < 0 (defenders win)

Back to the index


Minefield Formulas

     Minefield units after decay =
          = TRUNC(CurrentUnits * (100 - cfg_MineDecayRate) / 100) for mines
          = TRUNC(CurrentUnits * (100 - cfg_WebMineDecayRate) / 100) for webs

     Web drain fuel loss = MIN(cfg_WebDrainFuelLoss, ShipFuel)
     Web hit fuel loss = MIN(MAX(50, ShipFuel/6), ShipFuel)

     Minefields exploding:
         D = DIST( Mine1, Mine2 ), the distance between 2 minefield centers
         R1 = SQRT( MineUnits1 ), the radii of the minefields
         R2 = SQRT( MineUnits2 )
         IF (D > (R1+R2)) THEN
            Mines exploding = 0
         ELSE IF (D is 0) THEN
            Mines exploding = MineUnits1 if R1 < R2
                            = MineUnits2 if R2 <= R1
         ELSE
            A = (R1*R1 - R2*R2 + D*D) / (2*D)
            IF (A < 0) THEN
               Mines exploding = MineUnits1
            ELSE IF (A > D) THEN
               Mines exploding = MineUnits2
            ELSE
               Mines exploding = R1*R1 - A*A
            ENDIF
         ENDIF
     When you use AlternativeMinesDestroyMines (i.e. the only
     possible method in PHost up to 3.4e), PHost will try this formula
     for all minefields which overlap the new one, in Id order.

     The new method uses essentially the same formulas, but in a
     differential fashion to correctly handle multiple overlaps at
     once.

Back to the index


Planetary Combat Formulas

    Base tech maximum after damage (a.k.a. BaseDamageTech(TECH) ) =
         = MAX(MIN( (100-BaseDamage)/10, BaseTech(TECH) ), 1)

    PLANET_COMBAT_DEFENSE = TRUNC(DP * (100-PDAMAGE) / 100)
    BASE_COMBAT_DEFENSE   = TRUNC(BDP * (100-PDAMAGE) / 100)
    TOTAL_COMBAT_DEFENSE  = TRUNC((DP + BDP) * (100-PDAMAGE) / 100)
    (damage-scaled planetary/starbase defense post count)

    Planet combat mass = 100 + PLANET_COMBAT_DEFENSE
                       = above + BASE_COMBAT_DEFENSE
                             if starbase present

    Planet beam type = RND(PLANET_COMBAT_DEFENSE / 2) if no base
                     = MAX(above, BaseDamageTech(BEAMS)) if base present

    Planet beam number = MIN(RND(SQRT(TOTAL_COMBAT_DEFENSE / 3)), 20)
                                    if cfg_AllowAlternativeCombat is ON
                       = MIN(RND(SQRT(TOTAL_COMBAT_DEFENSE / 3)), 10)
                                    if cfg_AllowAlternativeCombat is OFF

    Planet torp type = RND(PLANET_COMBAT_DEFENSE / 2) if no base
                     = MAX(above, BaseDamageTech(TORPS)) if base present

    Planet tube number = MIN(RND(SQRT(TOTAL_COMBAT_DEFENSE / 4)), 20)

    Planet torp number = PLANET_TUBE_NUMBER * cfg_PlanetaryTorpsPerTube
                       = above + BASE_TORPS if base is present and
                         UseBaseTorpsInCombat is enabled. BASE_TORPS
                         is calculated according to the following
                         algorithm:

                     EQUIVCOST = 0
                     FOR Type=1 TO 10
                         EQUIVCOST = EQUIVCOST +
                                   TorpsInStorage(Type)*TorpMoneyCost(Type)
                     ENDFOR
                     BASE_TORPS = EQUIVCOST / TorpMoneyCost(PLANET_TORP_TYPE)
    A planet can have at most 255 torpedoes.

    Torps removed from base storage after combat are calculated as follows:

          COST_OF_TORPS = TorpsFiredInBattle * TorpMoneyCost(PLANET_TORP_TYPE)
          WHILE (COST_OF_TORPS > 0)
              DONE=1
              FOR TYPE=1 to 10
                  IF (       BaseTorps(TYPE) > 0
                         AND TorpMoneyCost(TYPE) <= COST_OF_TORPS
                     )
                      COST_OF_TORPS = COST_OF_TORPS - TorpMoneyCost(TYPE)
                      BaseTorps(TYPE) = BaseTorps(TYPE) - 1
                      DONE=0
                  ENDIF
              ENDFOR
              IF (DONE) STOP
          ENDWHILE

    Planet bay number = MIN(RND(SQRT(PLANET_COMBAT_DEFENSE)), 50) if no base
                      = MIN(above+5, 50) if base present

    Planet fighters = RND(SQRT(PLANET_COMBAT_DEFENSE)) if no base
                    = above + BaseFighters if base present

    NAT after combat in which planet is conquered =
                    = TRUNC(NAT*cfg_NativeCombatSurvivalRate/100)

Back to the index


Ship Combat Formulas

    Ship shields = TRUNC((300 - 2*ShipDamage)/3) if ship is Lizard
                 = TRUNC(100 - ShipDamage)

    Ship mass = ShipHullMass
                   + TRUNC(ENGINE_TECH_BONUS*cfg_EngineShieldBonusRate/100)
                              if cfg_AllowEngineShieldBonus is ON
                              and (ship is fighting a ship,
                                  or cfg_AllowESBonusAgainstPlanets is ON)
              = ShipHullMass  otherwise
              = above + 50
                    if ship is Federation and cfg_AllowFedCombatBonus is ON
    If cfg_PALIncludesESB is ON (hardwired on PHost 3.3d or earlier), the
    ship mass computed here is used for PAL computations. Otherwise,
    only the ShipHullMass is used for PALs.

    ENGINE_TECH_BONUS = cost (in megacredits) of building 1 engine of the
                        same type as the ship's engines

    DamageScale = 150 if ship is Lizard
                = 100 otherwise
    Ship beam number =
             = ShipBeams if ship is Federation and cfg_AllowFedCombatBonus is ON
             = MIN(ShipBeams, MaxHullBeams-TRUNC(MaxHullBeams*ShipDamage/DamageScale))

    Ship bay number =
             = ShipBays if ship is Federation and cfg_AllowFedCombatBonus is ON
             = MIN(ShipBays, MaxShipBays-TRUNC(MaxShipBays*ShipDamage/DamageScale))

    Ship tube number
             = ShipTubes if ship is Federation and cfg_AllowFedCombatBonus is ON
             = MIN(ShipTubes, MaxHullTubes-TRUNC(MaxHullTubes*ShipDamage/DamageScale))

    Beam hit odds = cfg_BeamHitOdds
                    + TRUNC((BeamKillPower+BeamSmashPower)*cfg_BeamHitBonus/100)

    Torp hit odds = cfg_TorpHitOdds
                    + TRUNC((TorpKillPower+TorpSmashPower)*cfg_TorpHitBonus/100)

    Beam recharge rate = cfg_BeamRechargeRate
                    + TRUNC((BeamKillPower + BeamSmashPower)
                                 * cfg_BeamRechargeBonus / 100)

    Bay recharge rate = cfg_BayRechargeRate + cfg_BayRechargeBonus*NumBays

    Tube recharge rate = cfg_TubeRechargeRate
                    + TRUNC((TorpKillPower + TorpSmashPower)
                                 * cfg_TubeRechargeBonus / 100)

    Damage to shields = (SmashPower * cfg_ShieldDamageScaling) / (Mass + 1)
                                    if cfg_AllowAlternativeCombat is ON
                      = TRUNC(above + 1.5)
                                    if cfg_AllowAlternativeCombat is OFF

    Damage to hull = (SmashPower * cfg_HullDamageScaling) / (Mass + 1)
                                    if cfg_AllowAlternativeCombat is ON
                   = TRUNC((DAMAGE_TO_SHIELDS * cfg_HullDamageScaling)
                               / (Mass + 1) + 1.5)
                                    if cfg_AllowAlternativeCombat is OFF

    Crew killed = (KillPower * cfg_CrewKillScaling) / (Mass + 1)
                                    if cfg_AllowAlternativeCombat is ON
                = TRUNC(above + 0.5) if cfg_AllowAlternativeCombat is OFF

Special rules for "Death Rays" (PHost 4.0)

A torpedo or beam with a SmashPower of 0 emits "death rays". Those do no damage to shields or to the hull, but immediately kill crew. The formula remains the same as above, EXCEPT that it kills at least one crew member for each hit in Tim combat.

Back to the index


Planetary Happiness Flowchart

    ; "Special Missions 1"
    CHAPPY = MIN(CHAPPY + HISS_EFFECT, 100)
    NHAPPY = MIN(NHAPPY + HISS_EFFECT, 100)

    ; ... much later, after movement:
    IF (Planet is captured in combat) THEN
        ;can occur multiple times if planet is captured more than once
        NHAPPY = NHAPPY - 20
    ENDIF

    ; after terraforming, RGA and Pillage ...

    IF CHAPPY < 30 AND CTAX > 0 THEN
        CTAX = 0
        ;Colonists refuse to pay taxes
    ENDIF
    CHAPPY = CHAPPY + COLONIST_HAPPY_CHANGE
    IF CHAPPY < 20 THEN
        ;Civil war
    ELSE IF CHAPPY < 40 THEN
        ;Riots
    ENDIF

    IF CLANS == 0 THEN
        CTAX = 0
        NTAX = 0
    ENDIF

    IF NHAPPY < 30 AND NTAX > 0 THEN
        NTAX = 0
        ;Natives refuse to pay taxes
    ENDIF
    NHAPPY = NHAPPY + NATIVE_HAPPY_CHANGE
    IF NHAPPY < 20 THEN
        ;Native civil war
    ELSE IF NHAPPY < 40 THEN
        ;Native riots
    ENDIF

Back to the index


Planetary Losses Flowchart

    COL = COL - COL_CLIMATE_DEATHS
    NAT = NAT - NAT_CLIMATE_DEATHS
    SUP = SUP - OVERPOPULATION_SUPPLY_LOSS
    M   = M   - STRUCTURE_DECAY_LOSS
    F   = F   - STRUCTURE_DECAY_LOSS
    DP  = DP  - STRUCTURE_DECAY_LOSS

    IF CHAPPY < 40 OR NHAPPY < 40 THEN
        M = M - 10
        F = F - 10
    ENDIF

    COL = COL - COL_FIGHTING_DEATHS
    NAT = NAT - NAT_FIGHTING_DEATHS

    COL = COL - AMORPHOUS_LOSSES

Back to the index


Ship Movement Flowchart

    Ship starting co-ordinates = (Xs, Ys)
    Ship waypoint co-ordinates = (Xw, Yw)
    Start-to-waypoint distance = D = DIST(startpoint,waypoint)
    Actual travel distance     = T = MIN(D, ShipSpeed*ShipSpeed*GravFactor)
                                     (where GravFactor = 2 for gravitonic ships,
                                            GravFactor = 1 for all other ships)

    Ship heading = ARCTAN(Yw-Ys, Xw-Xs)
    (HEADING)

    IF T == D THEN
       New ship location = (Xw, Yw)
    ELSE
       Ship displacement (floating point) = (T*COS(HEADING), T*SIN(HEADING))
                                          = (Xdf, Ydf)
       IF Xw == Xs THEN
          Xdf = 0
       ENDIF
       IF Yw == Ys THEN
          Ydf = 0
       ENDIF
       Ship displacement (integer) = (ROUNDUP(Xdf), ROUNDUP(Ydf))
                                   = (Xsi, Ysi)
       New ship location = (Xs+Xsi, Ys+Ysi)
    ENDIF

    ROUNDUP(Q) rounds number Q towards the next larger integer, preserving
    the sign:
       ROUNDUP(0)     = 0
       ROUNDUP(10.3)  = 11          ROUNDUP(-10.3)  = -11
       ROUNDUP(11)    = 11          ROUNDUP(-11)    = -11
       ROUNDUP(11.01) = 12          ROUNDUP(-11.01) = -12

    ***NOTE: Ship locations are limited to the square (0,0)-(10000,10000).
             If an X or Y position of a ship exceeds the value 10000, it is
             reduced to 10000. If a position becomes negative, it is mapped
             to the value 10000.

Back to the index


Hyperwarp Ship Movement Flowchart

    Ship starting co-ordinates = (Xs, Ys)
    Ship waypoint co-ordinates = (Xw, Yw)

    DX = Xw-Xs
    DY = Yw-Ys
    DIST_TO_WAYPOINT = SQRT(DX*DX+DY*DY)

    IF (DIST_TO_WAYPOINT >= 340 AND DIST_TO_WAYPOINT <= 360) THEN
       New Location X = Xw
       New Location Y = Yw
    ELSE
       New Location X = Xs + SGN(DX)*TRUNC(350*ABS(DX)/SQRT(DX^2 + DY^2) + 0.4999999)
       New Location Y = Ys + SGN(DY)*TRUNC(350*ABS(DY)/SQRT(DX^2 + DY^2) + 0.4999999)
    ENDIF

Back to the index


Wraparound Movement Flowchart

Definitions:

      ShipX    -- X co-ordinate of ship position before wraparound
      ShipY    -- Y co-ordinate of ship position before wraparound
      NewShipX -- X co-ordinate of ship position after wraparound
      NewShipY -- Y co-ordinate of ship position after wraparound

      LowerLeftX  -- X co-ordinate of lower-left corner of wrap region
      LowerLeftY  -- Y co-ordinate of lower-left corner of wrap region
      UpperRightX -- X co-ordinate of upper-right corner of wrap region
      UpperRightY -- Y co-ordinate of upper-right corner of wrap region
      (these are the four numbers specified in cfg_WraparoundRectangle)

      DimX -- Length of wrap region along the X-axis (UpperRightX-LowerLeftX)
      DimY -- Length of wrap region along the Y-axis (UpperRightY-LowerLeftY)

Movement Formulas:

      IF (ShipX < LowerLeftX) THEN
         NewShipX = ShipX + DimX
      ELSE IF (ShipX >= UpperRightX) THEN
         NewShipX = ShipX - DimX
      ENDIF

      IF (ShipY < LowerLeftY) THEN
         NewShipY = ShipY + DimY
      ELSE IF (ShipY >= UpperRightY) THEN
         NewShipY = ShipY - DimY
      ENDIF

      Note that PHost 3.3b and earlier had ">" in the UpperRightX
      and UpperRightY comparisons which caused ugly ambiguities.

Back to the index


Wormhole Formulas

  Wormhole detection radius = D = 10 * WormholeMass^(1/3)
  Wormhole detection chance = 100% if DIST(ship,wormhole) <= D
                            = (4 - (DIST(ship,wormhole)/D)^2) * 33.3%
                                   if D < DIST(ship,wormhole) <= 2D
                            = 0% if DIST(ship,wormhole) > 2D

  Wormhole stress factor = (TotalShipMass/WormholeMass - 1)^2
  (WRM_STRESS)               if TotalShipMass >= WormholeMass
                         = 0 if TotalShipMass < WormholeMass
  Wormhole passage failure chance = WRM_STRESS + WormholeInstability
  (WRM_FAILURE)                      + WRM_STRESS*WormholeInstability/10
                                     (percent)

  Wormhole passage failure figure = RAND(100)
  (WRM_FAILFIG)

  Ship damage from wormhole passage failure = (WRM_FAILFIG - WRM_FAILURE)^2
                                               if WRM_FAILFIG < WRM_FAILURE
                                            = 0 if WRM_FAILFIG >= WRM_FAILURE

  Wormhole entry radius = 0.5 * WormholeMass^(cfg_WrmEntryPowerX100/100) LY

  Ship exit point X (normal travel) = WRM_EXIT_X + RAND(21) - 10
  Ship exit point Y (normal travel) = WRM_EXIT_Y + RAND(21) - 10

  Ship exit point X (insufficient fuel) = RAND(2001) + 1000
  Ship exit point Y (insufficient fuel) = RAND(2001) + 1000

Back to the index


This document is maintained by The Portable Host Project[Remote] (support@phost.de).

Last updated 29 December, 2006