239 lines
8.2 KiB
Rust
239 lines
8.2 KiB
Rust
use serde::Serialize;
|
|
use std::collections::BTreeMap;
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct AdventureRunResponse {
|
|
pub combat_log: serde_json::Value,
|
|
pub enemy: String,
|
|
pub loot: Vec<String>,
|
|
pub zone: String,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct HealResponse {
|
|
pub gold: i32,
|
|
pub healed: bool,
|
|
pub hp: i32,
|
|
pub max_hp: i32,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct SpellEntryResponse {
|
|
pub code: String,
|
|
pub combat_damage_max: i32,
|
|
pub combat_damage_min: i32,
|
|
pub combat_effect: String,
|
|
pub description: String,
|
|
pub heal_max: i32,
|
|
pub heal_min: i32,
|
|
pub id: String,
|
|
pub known: bool,
|
|
pub name: String,
|
|
pub range: String,
|
|
pub spell_level: i32,
|
|
pub spell_points: i32,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct SpellbookResponse {
|
|
pub caster_class: String,
|
|
pub learn_costs: BTreeMap<String, i32>,
|
|
pub max_spell_points: i32,
|
|
pub spell_level: i32,
|
|
pub spell_points: i32,
|
|
pub spells: Vec<SpellEntryResponse>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct ActivateSpellResponse {
|
|
pub active_spell: Option<String>,
|
|
pub spell_points: i32,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct LearnSpellsResponse {
|
|
pub gold: i32,
|
|
pub learned_count: usize,
|
|
pub max_spell_points: i32,
|
|
pub spell_points: i32,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
#[serde(untagged)]
|
|
pub enum LearnSpellResponse {
|
|
Activate(ActivateSpellResponse),
|
|
Learn(LearnSpellsResponse),
|
|
}
|
|
|
|
impl From<sb_core::adventure::AdventureOutcome> for AdventureRunResponse {
|
|
fn from(outcome: sb_core::adventure::AdventureOutcome) -> Self {
|
|
AdventureRunResponse {
|
|
// to_value mirrors exactly what json! does to &log today: struct →
|
|
// Value::Object with BTreeMap ordering — nested keys sorted, byte-identical wire.
|
|
combat_log: serde_json::to_value(&outcome.combat_log)
|
|
.expect("CombatLog graph contains no map types; to_value is infallible"),
|
|
enemy: outcome.enemy_name,
|
|
loot: outcome.loot_item_names,
|
|
zone: outcome.zone_id,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use sb_shared::{CombatLog, CombatRound, CritEntry, CritResult};
|
|
|
|
fn representative_log() -> CombatLog {
|
|
CombatLog {
|
|
rounds: vec![
|
|
CombatRound {
|
|
attacker: "Test".into(),
|
|
defender: "Giant Rat".into(),
|
|
raw_roll: 67,
|
|
ob: 15,
|
|
db: 0,
|
|
result: 82,
|
|
damage: 12,
|
|
crit: None,
|
|
defender_hp: 0,
|
|
},
|
|
CombatRound {
|
|
attacker: "Giant Rat".into(),
|
|
defender: "Test".into(),
|
|
raw_roll: 99,
|
|
ob: 3,
|
|
db: 5,
|
|
result: 97,
|
|
damage: 18,
|
|
crit: Some(CritResult {
|
|
severity: "A".into(),
|
|
crit: CritEntry {
|
|
name: "critical".into(),
|
|
damage_multiplier: 2.0,
|
|
stun_rounds: 1,
|
|
bleed_per_round: 2,
|
|
bleed_rounds: 3,
|
|
effects: vec!["stun".into()],
|
|
},
|
|
}),
|
|
defender_hp: 62,
|
|
},
|
|
],
|
|
winner: "Test".into(),
|
|
loser: "Giant Rat".into(),
|
|
xp_gained: 5,
|
|
gold_gained: 2,
|
|
}
|
|
}
|
|
|
|
fn representative_outcome() -> sb_core::adventure::AdventureOutcome {
|
|
sb_core::adventure::AdventureOutcome {
|
|
zone_id: "skara_brae_streets".into(),
|
|
enemy_name: "Giant Rat".into(),
|
|
combat_log: representative_log(),
|
|
loot_item_names: vec!["Iron Sword".into()],
|
|
}
|
|
}
|
|
|
|
const GOLDEN_W1: &str = r#"{"combat_log":{"gold_gained":2,"loser":"Giant Rat","rounds":[{"attacker":"Test","crit":null,"damage":12,"db":0,"defender":"Giant Rat","defender_hp":0,"ob":15,"raw_roll":67,"result":82},{"attacker":"Giant Rat","crit":{"crit":{"bleed_per_round":2,"bleed_rounds":3,"damage_multiplier":2.0,"effects":["stun"],"name":"critical","stun_rounds":1},"severity":"A"},"damage":18,"db":5,"defender":"Test","defender_hp":62,"ob":3,"raw_roll":99,"result":97}],"winner":"Test","xp_gained":5},"enemy":"Giant Rat","loot":["Iron Sword"],"zone":"skara_brae_streets"}"#;
|
|
|
|
const GOLDEN_W2: &str = r#"{"gold":123,"healed":true,"hp":45,"max_hp":80}"#;
|
|
|
|
const GOLDEN_W3: &str = r#"{"code":"fire_bolt","combat_damage_max":6,"combat_damage_min":2,"combat_effect":"damage","description":"Hurls a mote of fire at the target.","heal_max":0,"heal_min":0,"id":"spell_fire_bolt","known":true,"name":"Fire Bolt","range":"short","spell_level":1,"spell_points":10}"#;
|
|
|
|
const GOLDEN_W4: &str = r#"{"caster_class":"mage","learn_costs":{"2":1000,"3":2000,"4":4000,"5":7000,"6":10000,"7":20000},"max_spell_points":40,"spell_level":4,"spell_points":12,"spells":[{"code":"fire_bolt","combat_damage_max":6,"combat_damage_min":2,"combat_effect":"damage","description":"Hurls a mote of fire at the target.","heal_max":0,"heal_min":0,"id":"spell_fire_bolt","known":true,"name":"Fire Bolt","range":"short","spell_level":1,"spell_points":10}]}"#;
|
|
|
|
const GOLDEN_W5: &str = r#"{"active_spell":"mage_armor","spell_points":12}"#;
|
|
const GOLDEN_W5_NULL: &str = r#"{"active_spell":null,"spell_points":12}"#;
|
|
|
|
const GOLDEN_W6: &str =
|
|
r#"{"gold":19700,"learned_count":4,"max_spell_points":40,"spell_points":40}"#;
|
|
|
|
#[test]
|
|
fn wire_identity_adventure_response() {
|
|
let dto: AdventureRunResponse = representative_outcome().into();
|
|
assert_eq!(serde_json::to_string(&dto).unwrap(), GOLDEN_W1);
|
|
}
|
|
|
|
#[test]
|
|
fn wire_identity_heal_response() {
|
|
let dto = HealResponse {
|
|
gold: 123,
|
|
healed: true,
|
|
hp: 45,
|
|
max_hp: 80,
|
|
};
|
|
assert_eq!(serde_json::to_string(&dto).unwrap(), GOLDEN_W2);
|
|
}
|
|
|
|
fn representative_spell_entry() -> SpellEntryResponse {
|
|
SpellEntryResponse {
|
|
code: "fire_bolt".into(),
|
|
combat_damage_max: 6,
|
|
combat_damage_min: 2,
|
|
combat_effect: "damage".into(),
|
|
description: "Hurls a mote of fire at the target.".into(),
|
|
heal_max: 0,
|
|
heal_min: 0,
|
|
id: "spell_fire_bolt".into(),
|
|
known: true,
|
|
name: "Fire Bolt".into(),
|
|
range: "short".into(),
|
|
spell_level: 1,
|
|
spell_points: 10,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn wire_identity_spell_entry_response() {
|
|
let dto = representative_spell_entry();
|
|
assert_eq!(serde_json::to_string(&dto).unwrap(), GOLDEN_W3);
|
|
}
|
|
|
|
#[test]
|
|
fn wire_identity_spellbook_response() {
|
|
let dto = SpellbookResponse {
|
|
caster_class: "mage".into(),
|
|
learn_costs: BTreeMap::from([
|
|
("2".into(), 1000),
|
|
("3".into(), 2000),
|
|
("4".into(), 4000),
|
|
("5".into(), 7000),
|
|
("6".into(), 10000),
|
|
("7".into(), 20000),
|
|
]),
|
|
max_spell_points: 40,
|
|
spell_level: 4,
|
|
spell_points: 12,
|
|
spells: vec![representative_spell_entry()],
|
|
};
|
|
assert_eq!(serde_json::to_string(&dto).unwrap(), GOLDEN_W4);
|
|
}
|
|
|
|
#[test]
|
|
fn wire_identity_activate_spell_response() {
|
|
let dto = ActivateSpellResponse {
|
|
active_spell: Some("mage_armor".into()),
|
|
spell_points: 12,
|
|
};
|
|
assert_eq!(serde_json::to_string(&dto).unwrap(), GOLDEN_W5);
|
|
|
|
let dto = ActivateSpellResponse {
|
|
active_spell: None,
|
|
spell_points: 12,
|
|
};
|
|
assert_eq!(serde_json::to_string(&dto).unwrap(), GOLDEN_W5_NULL);
|
|
}
|
|
|
|
#[test]
|
|
fn wire_identity_learn_response() {
|
|
let dto = LearnSpellResponse::Learn(LearnSpellsResponse {
|
|
gold: 19700,
|
|
learned_count: 4,
|
|
max_spell_points: 40,
|
|
spell_points: 40,
|
|
});
|
|
assert_eq!(serde_json::to_string(&dto).unwrap(), GOLDEN_W6);
|
|
}
|
|
}
|