92 lines
2.8 KiB
Rust
92 lines
2.8 KiB
Rust
use serde_json::json;
|
|
|
|
use shared_contracts::runtime_story::{
|
|
RuntimeStoryActionRequest, RuntimeStoryChoiceAction, RuntimeStoryPatch,
|
|
};
|
|
|
|
use crate::{
|
|
battle::resolve_battle_action, build_status_patch, read_bool_field, read_i32_field,
|
|
read_optional_string_field,
|
|
};
|
|
|
|
fn build_battle_fixture() -> serde_json::Value {
|
|
json!({
|
|
"inBattle": true,
|
|
"npcInteractionActive": false,
|
|
"playerHp": 4,
|
|
"playerMaxHp": 40,
|
|
"playerMana": 10,
|
|
"playerMaxMana": 10,
|
|
"playerSkillCooldowns": {},
|
|
"runtimeStats": {
|
|
"hostileNpcsDefeated": 0,
|
|
"itemsUsed": 0,
|
|
"questsAccepted": 0,
|
|
"scenesTraveled": 0,
|
|
"playTimeMs": 0,
|
|
"lastPlayTickAt": null
|
|
},
|
|
"currentNpcBattleMode": "fight",
|
|
"currentNpcBattleOutcome": null,
|
|
"currentEncounter": {
|
|
"kind": "npc",
|
|
"id": "npc_bandit_01",
|
|
"npcName": "断桥匪首",
|
|
"hostile": true,
|
|
"hp": 8,
|
|
"experienceReward": 24
|
|
},
|
|
"sceneHostileNpcs": [{
|
|
"id": "npc_bandit_01",
|
|
"name": "断桥匪首",
|
|
"hp": 8,
|
|
"maxHp": 80,
|
|
"experienceReward": 24
|
|
}]
|
|
})
|
|
}
|
|
|
|
fn build_request(function_id: &str, option_text: &str) -> RuntimeStoryActionRequest {
|
|
RuntimeStoryActionRequest {
|
|
session_id: "runtime-main".to_string(),
|
|
client_version: Some(0),
|
|
action: RuntimeStoryChoiceAction {
|
|
action_type: "story_choice".to_string(),
|
|
function_id: function_id.to_string(),
|
|
target_id: None,
|
|
payload: Some(json!({
|
|
"optionText": option_text
|
|
})),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn battle_resolution_prefers_player_defeat_when_both_sides_fall_in_same_turn() {
|
|
let request = build_request("battle_all_in_crush", "全力压制");
|
|
let mut game_state = build_battle_fixture();
|
|
|
|
let resolution = resolve_battle_action(&mut game_state, &request, "battle_all_in_crush")
|
|
.expect("battle action should resolve");
|
|
|
|
assert_eq!(read_i32_field(&game_state, "playerHp"), Some(0));
|
|
assert_eq!(
|
|
read_optional_string_field(&game_state, "currentNpcBattleOutcome"),
|
|
Some("fight_defeat".to_string())
|
|
);
|
|
assert_eq!(read_bool_field(&game_state, "inBattle"), Some(false));
|
|
assert!(resolution.result_text.contains("败北"));
|
|
assert!(matches!(
|
|
resolution.patches.first(),
|
|
Some(RuntimeStoryPatch::BattleResolved { outcome, .. }) if outcome == "defeat"
|
|
));
|
|
assert_eq!(
|
|
resolution.patches.get(1),
|
|
Some(&build_status_patch(&game_state))
|
|
);
|
|
assert_eq!(
|
|
resolution.battle.and_then(|battle| battle.outcome),
|
|
Some("defeat".to_string())
|
|
);
|
|
}
|