Laboratory

Overview

Core production building that can send output via Routes, earn money, and optionally convert excess production into Control or Loyalty based on mode and personality. Supports maintenance costs, pause, control-gain mode, upgrade level, and runtime save/load of key fields. Registers with LabGroupRegistrar and participates in the global tick as a Lab type.

Serialized Atoms

  • FloatVariable moneyPerUnit, currentMoney
  • FloatVariable routeBaseCost, routeCostPerNode, routeRefund, routeCostMultiplier
  • FloatVariable controlGainPerStar, loyalty, labMaintenanceCost
  • VoidEvent monthlyTick — triggers maintenance.
  • BoolVariable controlToLoyaltyMode
  • IntVariable selectedPersonality

Production Settings

  • float baseProduction (default 0.5)
  • float bonusProduction (default 0)
  • float TotalProduction (read-only) = base + bonus

Runtime State

  • List<Route> routes (read-only in inspector)
  • Region region
  • bool ProductionPaused — toggles visual pause on routes
  • bool ControlGainMode — converts all production to Control when true
  • int UpgradeLevel — derived from baseProduction on load

Tick Logic

When not paused:

  1. modified = baseProduction + bonusProduction
  2. If not ControlGainMode:
    • Send modified through each Route.SendProduction(modified).
    • Earn money up to demand: currentMoney += min(modified, region.LocalDemand) * moneyPerUnit.
    • If modified > region.LocalDemand (excess):
      • If controlToLoyaltyMode == false:
        • Personality switch on selectedPersonality:
          • MOBSTER → add 50% of the excess as extra money.
          • SUBTERFUGE → add Control by excess * controlGainPerStar (clamped to 1).
        • (Other personalities: no special excess handling.)
      • Else (convert to Loyalty) → increase loyalty by excess * controlGainPerStar (clamped to 1).
  3. Else (ControlGainMode) → increase region.Control by modified * controlGainPerStar (clamped to 1).
  4. UpdateSupply() sets region.SupplyPerTick = modified and region.Presence = true.

Routes

  • AddRoute(Route route) — adds and registers with LabGroupRegistrar.
  • RemoveRoute(int index, bool refund=true) / RemoveRoute(Route r, bool refund=true) / RemoveLastRoute(bool refund) — remove and optionally refund using (routeBaseCost + routeCostPerNode * NodesTraversed) * routeCostMultiplier * routeRefund. Destroys the Route GameObject.

Maintenance

  • Subscribed in Awake()monthlyTick.Register(ApplyMaintenanceCost).
  • ApplyMaintenanceCost() subtracts labMaintenanceCost from currentMoney on each monthly tick.

Save / Load

  • OnSave:
    • Header: PrefabType="Laboratory", RegionId = region.name.
    • Body: two lines — BaseProduction and BonusProduction.
  • OnLoad:
    • Read header and re-parent under the Region by name.
    • Parse BaseProduction (also sets UpgradeLevel = floor(BaseProduction * 2)) and BonusProduction.
    • Call region.AddLaboratory(this) to register with the Region.

TickBehavior

  • IsLab() returns true so GameManager updates Labs after Regions but before other systems.

Cleanup

  • OnDestroy() unregisters from LabGroupRegistrar and unsubscribes from the monthly tick.