113 lines
5.2 KiB
GDScript
113 lines
5.2 KiB
GDScript
extends RefCounted
|
|
|
|
const INITIAL_ENERGY = 1.0e27
|
|
const TRENDS = ["Antitheism", "Integration", "Xenocide", "Reconstruction", "Spirituality"]
|
|
const WEIGHTS = {
|
|
"m": [0, 2, 2, 0, 6], "l": [6, 1, 1, 2, 0],
|
|
"w": [2, 0, 6, 0, 2], "d": [0, 6, 0, 1, 3],
|
|
"c": [1, 3, 3, 3, 0], "b": [3, 0, 0, 6, 1],
|
|
}
|
|
|
|
static func defaults() -> Dictionary:
|
|
return {"Bur": 10.0, "Exp": 10.0, "Agg": 10.0,
|
|
"Federation": 1.0, "Enlightenment": 1.0, "Order": 1.0}
|
|
|
|
static func trend_defaults() -> Dictionary:
|
|
return {"Antitheism": 0.0, "Integration": 0.0, "Xenocide": 0.0,
|
|
"Reconstruction": 0.0, "Spirituality": 0.0}
|
|
|
|
static func value(source: Dictionary, key: String) -> float:
|
|
return source.get("world_parameters", {}).get(key, defaults().get(key, 1.0))
|
|
|
|
static func effective(source: Dictionary, key: String) -> float:
|
|
return pow(10.0, (clampf(value(source, key), 1.0, 19.0) - 10.0) / 9.0)
|
|
|
|
static func life_modifiers(source: Dictionary) -> Dictionary:
|
|
var aggression: float = effective(source, "Agg")
|
|
return {"progress": aggression,
|
|
"degradation": (1.0 - (1.0 - aggression) / 20.0) / value(source, "Order"),
|
|
"war": 1.0 - (1.0 - aggression) / 5.0,
|
|
"colonization": 1.0 - (1.0 - aggression) / 2.0}
|
|
|
|
static func production(source: Dictionary, entity) -> float:
|
|
if entity.event_group == "SNGRBB":
|
|
return effective(source, "Exp")
|
|
if entity.group_as == "Star":
|
|
var burn: float = effective(source, "Bur")
|
|
return sqrt(burn) if burn > 1.0 else burn
|
|
return 1.0
|
|
|
|
static func density_display(density: float) -> Dictionary:
|
|
if density <= 1.0e-5:
|
|
# Unity had log10(density) - 7 here; +7 maps 1e-7..1e-5 to 0..1.
|
|
return {"mode": "DensityRip", "position": clampf((log(maxf(density, 1e-7)) / log(10.0) + 7.0) / 2.0, 0.0, 1.0)}
|
|
if density >= 0.9:
|
|
return {"mode": "DensityCrunch", "position": clampf((density - 0.9) * 10.0, 0.0, 1.0)}
|
|
return {"mode": "Density", "position": clampf(log(density * 1.0e5) / log(9.0e4), 0.0, 1.0)}
|
|
|
|
## Unity ApocalypseCheck thresholds and WarningUI colors; visual warning only.
|
|
static func density_warning_color(density: float) -> Color:
|
|
if density >= 1.0: return Color.html("00c0ffff")
|
|
if density >= 0.97: return Color.html("20b0dfbf")
|
|
if density >= 0.9: return Color.html("40a0c07f")
|
|
if density <= 1.0e-7: return Color.html("ff0000ff")
|
|
if density <= 1.0e-6: return Color.html("df2020bf")
|
|
if density <= 1.0e-5: return Color.html("c040407f")
|
|
return Color(0.0, 0.0, 0.0, 0.0)
|
|
|
|
## Integrate the population present at the start of the simulation interval.
|
|
## Thresholds use whole event counts; no per-year loop at high speeds.
|
|
static func advance(previous: Dictionary, target: Dictionary, rules: Dictionary, years: float) -> void:
|
|
if years <= 0.0: return
|
|
if not target.has("world_parameters"): target.world_parameters = defaults()
|
|
if not target.has("trends"): target.trends = trend_defaults()
|
|
var rates: Array = [0.0, 0.0, 0.0, 0.0, 0.0]
|
|
var stages: Array = []
|
|
for id in rules:
|
|
if str(rules[id].home) != "": stages.append(id)
|
|
stages.sort()
|
|
var weight: float = 1.0
|
|
for id in stages:
|
|
var population: Dictionary = previous.get("ethics", {}).get(id, {})
|
|
var count: float = 0.0
|
|
for n in population.values(): count += n
|
|
if count > 0.0:
|
|
for ethic in population:
|
|
for i in TRENDS.size():
|
|
rates[i] += population[ethic] / count * WEIGHTS[ethic][i] * weight
|
|
weight *= 2.0
|
|
for i in TRENDS.size():
|
|
var name: String = TRENDS[i]
|
|
var accumulated: float = target.trends[name] + rates[i] * years * 1.0e-9
|
|
var events: float = floorf(snappedf(accumulated, 1.0e-12))
|
|
target.trends[name] = maxf(0.0, accumulated - events)
|
|
if events <= 0.0: continue
|
|
var key: String = "trend:" + name
|
|
target.civilization_stats[key] = target.civilization_stats.get(key, 0.0) + events
|
|
if name == "Integration": _integration(target, events)
|
|
|
|
static func _integration(target: Dictionary, events: float) -> void:
|
|
# Equal-probability outcomes in Unity. Use a saved RNG and bounded aggregate
|
|
# sampling for large steps, so thousands of triggers do not stall a frame.
|
|
var rng := RandomNumberGenerator.new()
|
|
rng.seed = 76123
|
|
if target.civilization_stats.has("trend_rng"):
|
|
rng.state = int(target.civilization_stats.trend_rng)
|
|
var counts: Array = [0.0, 0.0, 0.0]
|
|
if events <= 256.0:
|
|
for i in int(events): counts[rng.randi_range(0, 2)] += 1.0
|
|
else:
|
|
counts[0] = clampf(roundf(rng.randfn(events / 3.0, sqrt(events * 2.0 / 9.0))), 0.0, events)
|
|
var rest: float = events - counts[0]
|
|
counts[1] = clampf(roundf(rng.randfn(rest / 2.0, sqrt(rest / 4.0))), 0.0, rest)
|
|
counts[2] = rest - counts[1]
|
|
target.civilization_stats.trend_rng = rng.state
|
|
var parameters: Dictionary = target.world_parameters
|
|
parameters.Agg = maxf(1.0, parameters.Agg - counts[0] - counts[1])
|
|
parameters.Federation += counts[0] * 0.1
|
|
parameters.Enlightenment += counts[1] * 0.2
|
|
parameters.Order += counts[2] * 0.1
|
|
for i in 3:
|
|
var key: String = ["Federation", "Enlightenment", "Order"][i]
|
|
target.civilization_stats[key] = target.civilization_stats.get(key, 0.0) + counts[i]
|