170 lines
7.0 KiB
GDScript
170 lines
7.0 KiB
GDScript
|
|
extends "res://world/owniverse/simulation_module.gd"
|
||
|
|
class_name OwniverseApocalypseController
|
||
|
|
const E = preload("res://world/owniverse/math/exact.gd")
|
||
|
|
const WorldParameters = preload("res://world/owniverse/world_parameters.gd")
|
||
|
|
|
||
|
|
const TICK_SECONDS: float = 0.1
|
||
|
|
const END_WAIT_TICKS: int = 20
|
||
|
|
|
||
|
|
var _schedule: Array = []
|
||
|
|
|
||
|
|
func defaults() -> Dictionary:
|
||
|
|
return {"phase": 0, "type": 0, "warning": 0, "stage": 1, "tick": 0, "end_ticks": 0}
|
||
|
|
|
||
|
|
|
||
|
|
func check_density(target: Dictionary) -> void:
|
||
|
|
var apocalypse: Dictionary = target.get("apocalypse", defaults())
|
||
|
|
if E.gt(apocalypse.get("phase", 0), 0): return
|
||
|
|
var density: Variant = world.environment.stellar_environment(target).density
|
||
|
|
var parameters: Dictionary = WorldParameters.extra_params()
|
||
|
|
var warning: int = 0
|
||
|
|
if E.ge(density, parameters.bigcrunch3): warning = 3
|
||
|
|
elif E.ge(density, parameters.bigcrunch2): warning = 2
|
||
|
|
elif E.ge(density, parameters.bigcrunch1): warning = 1
|
||
|
|
elif E.le(density, parameters.bigrip3): warning = -3
|
||
|
|
elif E.le(density, parameters.bigrip2): warning = -2
|
||
|
|
elif E.le(density, parameters.bigrip1): warning = -1
|
||
|
|
apocalypse.warning = warning
|
||
|
|
target.apocalypse = apocalypse
|
||
|
|
if E.eq(warning, 3):
|
||
|
|
start(target, 3)
|
||
|
|
elif E.eq(warning, -3):
|
||
|
|
start(target, -3)
|
||
|
|
|
||
|
|
|
||
|
|
func start(target: Dictionary, event_type: int) -> void:
|
||
|
|
var apocalypse: Dictionary = target.get("apocalypse", defaults()).duplicate(true)
|
||
|
|
if E.gt(apocalypse.phase, 0): return
|
||
|
|
apocalypse.phase = 1
|
||
|
|
apocalypse.type = event_type
|
||
|
|
apocalypse.stage = 1
|
||
|
|
apocalypse.tick = 0
|
||
|
|
apocalypse.end_ticks = 0
|
||
|
|
target.apocalypse = apocalypse
|
||
|
|
target.speed = 1
|
||
|
|
target.speed_in_year = 1
|
||
|
|
_schedule = _make_schedule("SMBH" if event_type > 0 else "S")
|
||
|
|
|
||
|
|
|
||
|
|
func process_tick(target: Dictionary) -> Dictionary:
|
||
|
|
var result: Dictionary = {"screen": -1, "finished": false}
|
||
|
|
var apocalypse: Dictionary = target.get("apocalypse", defaults())
|
||
|
|
if E.ne(apocalypse.get("phase", 0), 1): return result
|
||
|
|
if _schedule.is_empty(): _schedule = _make_schedule("SMBH" if E.gt(apocalypse.type, 0) else "S")
|
||
|
|
apocalypse.tick = E.add(apocalypse.tick, 1)
|
||
|
|
var stage_rows: Array = []
|
||
|
|
for row: Dictionary in _schedule:
|
||
|
|
if E.eq(row.stage, apocalypse.stage): stage_rows.append(row)
|
||
|
|
if stage_rows.is_empty():
|
||
|
|
var next_stage: Variant = INF
|
||
|
|
for row: Dictionary in _schedule:
|
||
|
|
if E.gt(row.stage, apocalypse.stage): next_stage = E.minimum(next_stage, row.stage)
|
||
|
|
if E.finite(next_stage):
|
||
|
|
apocalypse.stage = E.integer(next_stage)
|
||
|
|
apocalypse.tick = 0
|
||
|
|
else:
|
||
|
|
_process_final_energy(target, apocalypse)
|
||
|
|
if E.eq(apocalypse.end_ticks, END_WAIT_TICKS):
|
||
|
|
apocalypse.phase = 2
|
||
|
|
result.finished = true
|
||
|
|
target.apocalypse = apocalypse
|
||
|
|
return result
|
||
|
|
|
||
|
|
var stage_complete: bool = true
|
||
|
|
for row: Dictionary in stage_rows:
|
||
|
|
if E.gt(row.tick, apocalypse.tick):
|
||
|
|
stage_complete = false
|
||
|
|
continue
|
||
|
|
var id: String = row.id
|
||
|
|
if not world.catalog.entities.has(id): continue
|
||
|
|
if E.gt(world.ledger.physical_amount(target, id), 0):
|
||
|
|
stage_complete = false
|
||
|
|
result.screen = int(row.screen)
|
||
|
|
_destroy_fraction(target, id)
|
||
|
|
if stage_complete:
|
||
|
|
var next_stage: Variant = INF
|
||
|
|
for row: Dictionary in _schedule:
|
||
|
|
if E.gt(row.stage, apocalypse.stage): next_stage = E.minimum(next_stage, row.stage)
|
||
|
|
if E.finite(next_stage):
|
||
|
|
apocalypse.stage = E.integer(next_stage)
|
||
|
|
apocalypse.tick = 0
|
||
|
|
else:
|
||
|
|
_process_final_energy(target, apocalypse)
|
||
|
|
if E.eq(apocalypse.end_ticks, END_WAIT_TICKS):
|
||
|
|
apocalypse.phase = 2
|
||
|
|
result.finished = true
|
||
|
|
target.apocalypse = apocalypse
|
||
|
|
world.ledger.refresh_groups(target)
|
||
|
|
world.ledger.clamp_auto_resources(target)
|
||
|
|
world.presentation.refresh(target, world.catalog)
|
||
|
|
return result
|
||
|
|
|
||
|
|
|
||
|
|
func _make_schedule(skipped_id: String) -> Array:
|
||
|
|
var rows: Array = Global.ApocalypseQueue.values()
|
||
|
|
rows.sort_custom(func(a: Dictionary, b: Dictionary) -> bool: return int(a.Num) < int(b.Num))
|
||
|
|
var result: Array = []
|
||
|
|
var stage: int = -1
|
||
|
|
var tick: int = 0
|
||
|
|
for source: Dictionary in rows:
|
||
|
|
if source.Shortcut == skipped_id: continue
|
||
|
|
var row_stage: int = int(source.Stage)
|
||
|
|
if row_stage != stage:
|
||
|
|
stage = row_stage
|
||
|
|
tick = 0
|
||
|
|
tick += int(source.Tick)
|
||
|
|
result.append({"id": str(source.Shortcut), "stage": row_stage, "screen": int(source.Screen), "tick": tick})
|
||
|
|
return result
|
||
|
|
|
||
|
|
|
||
|
|
func _destroy_fraction(target: Dictionary, id: String) -> void:
|
||
|
|
var entity: OwniverseEntity = world.catalog.entities[id]
|
||
|
|
var physical: Variant = world.ledger.physical_amount(target, id)
|
||
|
|
if E.le(physical, 0): return
|
||
|
|
var amount: Variant = E.round_value(E.divide(physical, 2)) if E.gt(physical, 5) else (1 if E.ge(physical, 1) else physical)
|
||
|
|
amount = E.minimum(physical, E.maximum(0, amount))
|
||
|
|
if E.le(amount, 0): return
|
||
|
|
var fraction: Variant = E.clamp_value(E.divide(amount, physical), 0, 1)
|
||
|
|
if E.gt(entity.destroy_level, 0): world.stellar.destroy_planets(target, id, fraction, entity.destroy_level, "apocalypse")
|
||
|
|
var products: Dictionary = world.stellar.evolution_products(target, entity, amount, {"explosiveness": WorldParameters.effective(target, "Exp")})
|
||
|
|
var normalized: Dictionary = {}
|
||
|
|
for output in products:
|
||
|
|
normalized[output] = E.divide(products[output], amount)
|
||
|
|
var incoming: Dictionary = world.ledger.transfer_evolution(target, {id: fraction}, {id: normalized})
|
||
|
|
world.batches.scale_batches(target, id, E.subtract(1, fraction))
|
||
|
|
var terminal: String = "SMBH" if E.gt(target.apocalypse.type, 0) else "S"
|
||
|
|
var terminal_yield: Variant = 0
|
||
|
|
var new_owners: Dictionary = {}
|
||
|
|
if normalized.is_empty():
|
||
|
|
terminal_yield = E.multiply(amount, E.add(1, E.maximum(0, entity.mass))) if E.contains(["Star", "BH"], entity.group_as) else amount
|
||
|
|
for output in normalized:
|
||
|
|
if E.eq(output, "blast"):
|
||
|
|
var environment: Dictionary = world.environment.stellar_environment(target)
|
||
|
|
world.stellar.apply_blast(target, E.multiply(normalized[output], amount), environment)
|
||
|
|
continue
|
||
|
|
if not world.catalog.entities.has(output): continue
|
||
|
|
var output_amount: Variant = E.maximum(0, products[output])
|
||
|
|
if E.le(output_amount, 0): continue
|
||
|
|
output_amount = world.ledger.credit(target, output, output_amount)
|
||
|
|
var output_entity: OwniverseEntity = world.catalog.entities[output]
|
||
|
|
if E.gt(output_entity.lifespan, 0) or not output_entity.life_rule.is_empty():
|
||
|
|
world.batches.append_batch(target, output, output_amount, target.time.datetime, target.time.datetime)
|
||
|
|
new_owners[output] = E.add(new_owners.get(output, 0), output_amount)
|
||
|
|
world.ledger.accept_transfers(target, incoming)
|
||
|
|
world.ledger.bind_new_owners(target, new_owners)
|
||
|
|
if E.gt(terminal_yield, 0): world.ledger.credit(target, terminal, terminal_yield)
|
||
|
|
world.ledger.round_object_allocations(target)
|
||
|
|
|
||
|
|
|
||
|
|
func _process_final_energy(target: Dictionary, apocalypse: Dictionary) -> void:
|
||
|
|
var energy: Variant = E.maximum(0, target.energy)
|
||
|
|
if E.gt(energy, 0):
|
||
|
|
var amount: Variant = E.round_value(E.divide(energy, 2)) if E.gt(energy, 5) else (1 if E.ge(energy, 1) else energy)
|
||
|
|
amount = E.minimum(energy, amount)
|
||
|
|
target.energy = E.maximum(0, E.subtract(energy, amount))
|
||
|
|
world.ledger.credit(target, "SMBH" if E.gt(apocalypse.type, 0) else "S", amount)
|
||
|
|
apocalypse.end_ticks = 0
|
||
|
|
else:
|
||
|
|
apocalypse.end_ticks = int(apocalypse.end_ticks) + 1
|
||
|
|
|