File size: 3,673 Bytes
7e764b0
04b2d3e
 
 
 
 
 
 
dadc0b2
 
695bde6
dadc0b2
 
 
 
 
04b2d3e
 
 
 
 
c2a29e2
04b2d3e
 
 
 
7e764b0
04b2d3e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7e764b0
dadc0b2
04b2d3e
c2a29e2
 
 
 
 
 
 
 
 
 
 
 
 
04b2d3e
 
dadc0b2
 
 
 
 
7e764b0
dadc0b2
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// @ts-check
/* ============================================================
   Supply accounting: a soft population cap that gates production.
   Every live unit and every queued job costs supply; Command Centers
   and Habitats grant it. Both totals are recomputed on demand from
   state β€” never cached anywhere β€” so a dying building's queue frees its
   reservation with no bookkeeping, and drift is impossible by
   construction (state already holds the whole truth).

   A match can ALSO set a hard ceiling on top of that (state.popCap β€” setup.js's Population cap
   option, 150/200/250/300/Max, null/Max meaning uncapped): supplyCap clamps the building-derived total
   to it, identically for both owners (it's a shared match rule, not a per-side or AI-temperament
   dial like difficulty/aiStrategy). buildingSupplyCap exposes the pre-clamp raw total for the one
   caller that needs to tell "still-blocked" apart from "no housing would help" β€” engine/aiEconomy.js's
   own Habitat-build trigger, which would otherwise keep raising a cap a hard ceiling has already
   overtaken, forever.
   ============================================================ */

"use strict";

import { UNITS, BUILDINGS } from "./entities.js";
import { electrifyBoost } from "./industry.js";

// Live units + every queued job, per owner. Counting all queue entries
// (not just index 0) makes the reservation happen at queue time, so a
// player can't stuff a barracks past the cap.
/** @param {State} state @param {string} owner @returns {number} */
export function supplyUsed(state, owner) {
  let used = 0;
  for (const u of state.units.values())
    if (u.owner === owner) used += UNITS[u.type].supplyCost || 0;
  for (const b of state.buildings.values()) {
    if (b.owner !== owner) continue;
    for (const job of b.queue) used += UNITS[job.unitType].supplyCost || 0;
  }
  return used;
}

// Completed buildings only β€” a Habitat still going up grants nothing
// until it finishes, so you can't produce against supply you haven't
// built yet. Losing a Habitat can leave a player legally over cap
// (nothing dies; production simply blocks until they rebuild).
/** @param {State} state @param {string} owner @returns {number} */
export function buildingSupplyCap(state, owner) {
  let cap = 0;
  let boost = null;   // the electrify bonus, computed lazily and once β€” only if a grant building is wired in
  for (const b of state.buildings.values()) {
    if (b.owner !== owner || b.constructing) continue;
    const grant = BUILDINGS[b.type].supplyGrants || 0;
    if (!grant) continue;
    // Electrification (Odyssey): a Habitat wired into the power grid houses 30% more β€” scaled by the
    // grid throttle (engine/industry.js). The flag is never set on the skirmish path, so `boost` stays
    // null there and the arithmetic is byte-identical to the old `cap += grant`.
    if (b.electrified) {
      if (boost === null) boost = electrifyBoost(state, owner);
      cap += grant * (1 + boost);
    } else cap += grant;
  }
  return cap;
}

// The cap production.js/hud.js actually enforce/display: the building-derived total above, clamped
// to the match's configured population cap (state.popCap β€” null/unset means Max, i.e. today's
// always-uncapped behaviour, byte-identical to before this setting existed). Never the other way
// round β€” a LOWER building-derived total than the configured cap is unaffected (Math.min).
/** @param {State} state @param {string} owner @returns {number} */
export function supplyCap(state, owner) {
  const raw = buildingSupplyCap(state, owner);
  return (state.popCap != null && state.popCap < raw) ? state.popCap : raw;
}