From d19db31ccd27d3f38f0310e5b637ed7bc50b9ee7 Mon Sep 17 00:00:00 2001 From: autocommit Date: Wed, 20 May 2026 18:12:13 -0700 Subject: [PATCH] =?UTF-8?q?feat(api-gdext):=20=E2=9C=A8=20Introduce=20sugg?= =?UTF-8?q?est=20endpoints=20in=20player=5Fapi.rs=20for=20GDExt=20simulato?= =?UTF-8?q?r=20autocompletion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- src/simulator/api-gdext/src/player_api.rs | 34 ++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/simulator/api-gdext/src/player_api.rs b/src/simulator/api-gdext/src/player_api.rs index 11e5acde..c8e7a35b 100644 --- a/src/simulator/api-gdext/src/player_api.rs +++ b/src/simulator/api-gdext/src/player_api.rs @@ -25,7 +25,8 @@ use godot::prelude::*; use mc_ai::tactical::state::{TacticalBuildingSpec, TacticalUnitSpec}; use mc_player_api::{ - apply_action, project_view, ActionError, PlayerAction, PlayerView, PlayerId, + apply_action, project_view, suggest_actions, ActionError, PlayerAction, PlayerView, + PlayerId, }; use mc_turn::game_state::GameState; @@ -221,6 +222,37 @@ impl GdPlayerApi { Err(e) => error_envelope(&e), } } + + /// Stage 6.1.6 — compute the scripted controller's action chain for + /// `player` against the CURRENT held state, WITHOUT applying any + /// action or advancing the turn. + /// + /// Returns a `{"ok":true,"actions":[,...]}` envelope + /// (JSON). The `actions` list uses the same `PlayerAction` JSON shape + /// `apply_action_json` accepts, so a recorder can replay each action + /// straight back through `apply_action_json`. + /// + /// Read-only: this never mutates the held `GameState`. Two calls in + /// a row return identical results and leave `view_json` unchanged. + #[func] + pub fn suggest_json(&self, player: i32) -> GString { + let player_id = match clamp_player(player) { + Ok(p) => p, + Err(e) => return error_envelope(&e), + }; + let actions: Vec = suggest_actions(&self.state, player_id); + let envelope = serde_json::json!({ + "ok": true, + "actions": actions, + }); + serde_json::to_string(&envelope) + .unwrap_or_else(|e| { + format!( + r#"{{"ok":false,"error":{{"code":"internal","message":"{e}"}}}}"# + ) + }) + .into() + } } /// Normalize integer-valued floats (`2.0`, `-3.0`) back to integer form