Files
PuGa/puga/planet.py
T
dodoxandClaude Sonnet 5 bf15c709ea Add planet environment-extra materials to capex calcs; buildcost.py tool
Every building needs a planet-hazard material bundle on top of its normal
BuildingCosts (SEA for low pressure, HSE for high pressure, MGC for low
gravity, BL for high gravity, INS for cold, TSH for heat - area-scaled
where applicable), confirmed against live in-game build panels on Nike.
FIO's own BuildRequirements field is an unreliable superset across hazard
types, not a direct per-building list - don't use it directly.

- puga/planet.py::environment_extras(): the real threshold table.
- tools/buildcost.py: full construction material list for one building,
  optionally on a given planet.
- Wired into whatif.py/buy.py (via simulate(planet=...)) and scan.py's
  own bcost(), so their capex numbers stop silently omitting this.
- docs/mechanics.md: the rule, plus a separate forum-sourced note on the
  CX order-cancellation penalty.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-17 19:03:51 +02:00

53 lines
2.9 KiB
Python

"""Rank planets by extraction factor for a resource. Also holds environment_extras(): FIO's own
BuildRequirements field is an unreliable superset across hazard types (confirmed wrong in-game,
see docs/mechanics.md) - the real per-building material is computed from the planet's own
pressure/gravity/temperature/surface against the Prosperous Universe Handbook's threshold table,
which PRUNplanner's own calculations don't model either."""
from . import fio
# (condition check, ticker, qty per unit area, flat qty) - handbook building-costs table
ENV_RULES = [
(lambda p: (p.get("pressure") or 0) < 0.25, "SEA", 1.0, 0.0),
(lambda p: (p.get("pressure") or 0) > 2.0, "HSE", 0.0, 1.0),
(lambda p: 0 < (p.get("gravity") or 1) < 0.25, "MGC", 0.0, 1.0),
(lambda p: (p.get("gravity") or 0) > 2.5, "BL", 0.0, 1.0),
(lambda p: (p.get("temperature") if p.get("temperature") is not None else 20) < -25, "INS", 10.0, 0.0),
(lambda p: (p.get("temperature") or 0) > 75, "TSH", 0.0, 1.0),
]
def environment_extras(planet: dict, area: float) -> dict[str, float]:
"""Extra construction/repair materials a building needs beyond its normal BuildingCosts and
the universal MCG area cost (already handled elsewhere, e.g. simulate()'s mcg_per_area) - one
row per hazard the planet actually has (pressure/gravity/temperature; rocky-vs-gaseous MCG/AEF
swap not covered here). planet needs lowercase pressure/gravity/temperature keys, e.g. from
prunplanner._g('/data/planet/{id}/') or fio.planets_full() remapped to lowercase."""
out: dict[str, float] = {}
for cond, tk, per_area, flat in ENV_RULES:
if cond(planet):
out[tk] = out.get(tk, 0.0) + per_area * area + flat
return out
def candidates(resource_ticker: str, min_factor: float = 0.0):
"""All planets with this resource, sorted by factor descending. Each: PlanetNaturalId,
PlanetName, factor, resource_type, plus the raw planet record (temperature/pressure/
gravity/fertility/faction/BuildRequirements/SystemId etc)."""
mats = {m["Ticker"]: m["MaterialId"] for m in fio.materials()}
mid = mats.get(resource_ticker.upper())
if not mid:
raise ValueError(f"unknown material ticker {resource_ticker!r}")
out = []
for p in fio.planets_full():
for r in p.get("Resources", []):
if r["MaterialId"] == mid and r["Factor"] >= min_factor:
out.append({
"natural_id": p["PlanetNaturalId"], "name": p["PlanetName"],
"factor": r["Factor"], "resource_type": r["ResourceType"],
"temperature": p["Temperature"], "pressure": p["Pressure"], "gravity": p["Gravity"],
"fertility": p["Fertility"], "faction": p.get("FactionCode"),
"system_id": p["SystemId"], "build_requirements": p.get("BuildRequirements", []),
})
out.sort(key=lambda x: -x["factor"])
return out