Files
owniverse/world/owniverse/apocalypse_controller.gd
T

170 lines
7.0 KiB
GDScript
Raw Normal View History

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