perf(management-specific): ⚡ Migrate research processing logic from GDScript to Rust (Rail-1) while delegating spell/tech checks and preserving GDScript wrapper for signals
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
f59bbc041f
commit
31955b1ada
3 changed files with 72 additions and 41 deletions
|
|
@ -141,58 +141,71 @@ func _process_production(player: RefCounted) -> void: # Player
|
|||
|
||||
|
||||
func _process_research(player: RefCounted) -> void: # Player
|
||||
## Rail-1: science accumulation + spell/tech completion check delegated
|
||||
## to Rust `GdTechWeb::process_research` (warcouncil p1-39 port,
|
||||
## 2026-04-27). GDScript only assembles input JSON + applies
|
||||
## completion side-effects (school_locked emit, _form_high_archon,
|
||||
## tech_researched signal, resource reveals).
|
||||
if player.researching.is_empty():
|
||||
return
|
||||
|
||||
# Debug: instantly complete any queued research/spell.
|
||||
if EnvConfig.get_bool("FORCE_UNLIMITED_RESEARCH"):
|
||||
player.research_progress = 999999
|
||||
|
||||
# Effective per-yield mult composes static difficulty handicap +
|
||||
# linear-per-turn growth (warcouncil p1-29 H4, 2026-04-27). Per-player
|
||||
# overrides for batch testing still take precedence inside the helper.
|
||||
# Full Rail-1 port to GdTechWeb::process_research is queued under p1-31 —
|
||||
# requires wrapper-layer plumbing (tech_web.gd → KnowledgeWebScript → GdTechWeb).
|
||||
# Per-yield difficulty multiplier (composed by GameState).
|
||||
var sci_modifier: float = GameState.get_effective_yield_mult(player, "research")
|
||||
|
||||
player.research_progress += int(player.science_per_turn * sci_modifier)
|
||||
|
||||
# Add science from cities
|
||||
var game_map: RefCounted = GameState.get_game_map() # GameMap
|
||||
# Per-city science yields the Rust side will sum.
|
||||
var game_map: RefCounted = GameState.get_game_map()
|
||||
var yields_arr: Array = []
|
||||
if game_map != null:
|
||||
for city: Variant in player.cities:
|
||||
if city is CityScript:
|
||||
var tile_json: String = BuildableHelperScript.build_tile_yields_json(
|
||||
city as CityScript, game_map
|
||||
)
|
||||
var yields: Dictionary = city.get_yields(tile_json)
|
||||
var building_sci: int = _sum_city_building_effect(city as CityScript, "science")
|
||||
var sci_pct: float = _sum_city_building_effect_float(
|
||||
city as CityScript, "science_percent"
|
||||
)
|
||||
player.research_progress += int(
|
||||
(yields.get("science", 0) + building_sci) * (1.0 + sci_pct) * sci_modifier
|
||||
)
|
||||
if not city is CityScript:
|
||||
continue
|
||||
var c: CityScript = city as CityScript
|
||||
var tile_json: String = BuildableHelperScript.build_tile_yields_json(c, game_map)
|
||||
var ys: Dictionary = c.get_yields(tile_json)
|
||||
yields_arr.append({
|
||||
"science": int(ys.get("science", 0)),
|
||||
"building_science": _sum_city_building_effect(c, "science"),
|
||||
"science_percent": _sum_city_building_effect_float(c, "science_percent"),
|
||||
})
|
||||
|
||||
# Check if researching a spell (not a tech)
|
||||
# Player input. spell_cost is set when researching a spell so Rust runs
|
||||
# the cheap counter branch (no TechWeb lookup).
|
||||
var spell_data: Dictionary = DataLoader.get_spell(player.researching)
|
||||
if not spell_data.is_empty():
|
||||
var spell_cost: int = spell_data.get("research_cost", 999999)
|
||||
if player.research_progress >= spell_cost:
|
||||
var completed_spell: String = player.researching
|
||||
player.research_progress = 0
|
||||
player.researching = ""
|
||||
var sys: SpellSystemScript = spell_system as SpellSystemScript
|
||||
sys.research_spell(player.index, completed_spell)
|
||||
var researching_spell: bool = not spell_data.is_empty()
|
||||
var researched_arr: Array = Array(player.researched_techs) if player.researched_techs != null else []
|
||||
var player_dict: Dictionary = {
|
||||
"researching": str(player.researching),
|
||||
"research_progress": int(player.research_progress),
|
||||
"science_per_turn": int(player.science_per_turn),
|
||||
"researched_techs": researched_arr,
|
||||
"instant_complete": EnvConfig.get_bool("FORCE_UNLIMITED_RESEARCH"),
|
||||
}
|
||||
if researching_spell:
|
||||
player_dict["spell_cost"] = int(spell_data.get("research_cost", 999999))
|
||||
|
||||
var tw: RefCounted = TurnManager.get_tech_web()
|
||||
if tw == null:
|
||||
return
|
||||
var result: Dictionary = tw.process_research(
|
||||
JSON.stringify(player_dict), JSON.stringify(yields_arr), sci_modifier
|
||||
)
|
||||
if result.is_empty():
|
||||
return
|
||||
var err: String = str(result.get("error", ""))
|
||||
if not err.is_empty():
|
||||
push_warning("p1-39 _process_research: " + err)
|
||||
return
|
||||
player.research_progress = int(result.get("new_progress", 0))
|
||||
player.researching = str(result.get("new_researching", ""))
|
||||
|
||||
var completed_spell: String = str(result.get("completed_spell", ""))
|
||||
if not completed_spell.is_empty():
|
||||
var sys: SpellSystemScript = spell_system as SpellSystemScript
|
||||
sys.research_spell(player.index, completed_spell)
|
||||
return
|
||||
|
||||
var tech_data: Dictionary = DataLoader.get_tech(player.researching)
|
||||
var tech_cost: int = tech_data.get("cost", 999999)
|
||||
|
||||
if player.research_progress >= tech_cost:
|
||||
var completed_tech: String = player.researching
|
||||
player.research_progress = 0
|
||||
player.researching = ""
|
||||
var completed_tech: String = str(result.get("completed_tech", ""))
|
||||
if not completed_tech.is_empty():
|
||||
var old_school_count: int = player.schools.size()
|
||||
player.add_tech(completed_tech)
|
||||
# Arcane Lore completion: transform leader into High Archon
|
||||
|
|
|
|||
|
|
@ -141,6 +141,16 @@ func start_research(node_id: String, player_index: int) -> bool:
|
|||
return true
|
||||
|
||||
|
||||
## Direct passthrough to the underlying GDExtension's `process_research`
|
||||
## (warcouncil p1-39 Rail-1 port, 2026-04-27). Caller composes the input
|
||||
## JSON; Rust owns sci_modifier application + spell-vs-tech branching.
|
||||
## Returns `{}` if the bridge is unavailable so callers can no-op safely.
|
||||
func process_research(player_json: String, yield_json: String, sci_modifier: float) -> Dictionary:
|
||||
if _bridge == null:
|
||||
return {}
|
||||
return _bridge.call("process_research", player_json, yield_json, sci_modifier) as Dictionary
|
||||
|
||||
|
||||
# ── Internals ───────────────────────────────────────────────────────────
|
||||
|
||||
func _get_player(player_index: int) -> PlayerScript:
|
||||
|
|
|
|||
|
|
@ -37,6 +37,14 @@ func build(_techs: Array) -> void:
|
|||
_web.build()
|
||||
|
||||
|
||||
## Delegate to GdTechWeb::process_research (warcouncil p1-39 Rail-1 port).
|
||||
## Caller assembles the player + yield JSON and supplies the difficulty
|
||||
## sci_modifier. Rust owns: per-city science sum, sci_modifier
|
||||
## application, instant-complete branch, spell-vs-tech branching.
|
||||
func process_research(player_json: String, yield_json: String, sci_modifier: float) -> Dictionary:
|
||||
return _web.process_research(player_json, yield_json, sci_modifier)
|
||||
|
||||
|
||||
func apply_heritage_tech(player: RefCounted) -> void:
|
||||
## Grant the player's race origin tech (cost 0 by convention).
|
||||
if player == null:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue