264 lines
8.4 KiB
Rust
264 lines
8.4 KiB
Rust
//! A confirmed changed-capacity save retains equipment without increasing the
|
|
//! original combat shield limits or losing quantities on additive operations.
|
|
use raptor_replica::{
|
|
content::{ContentId, ContentPack, Registry},
|
|
modding::{Command, ScriptRuntime},
|
|
persistence::Pilot,
|
|
simulation::{Input, Pickup, World},
|
|
};
|
|
use serde_json::json;
|
|
|
|
fn registry() -> Registry {
|
|
let mut registry = Registry::default();
|
|
let pack: ContentPack = serde_json::from_value(json!({
|
|
"items": [
|
|
{"id":"base:forward_guns","name":"Guns","price":0},
|
|
{"id":"base:energy","name":"Energy","price":0,"max_count":100,"initial_count":25,"shield":25,"legacy":16},
|
|
{"id":"base:super_shield","name":"Super Shield","price":0,"max_count":100,"initial_count":100,"legacy":15},
|
|
{"id":"base:ammo","name":"Ammo","price":0,"max_count":3}
|
|
],
|
|
"missions":[{"id":"base:sector_1_wave_1","name":"Test","duration":10000}]
|
|
})).unwrap();
|
|
registry.apply(pack, "base").unwrap();
|
|
registry.validate().unwrap();
|
|
registry
|
|
}
|
|
fn id(value: &str) -> ContentId {
|
|
value.into()
|
|
}
|
|
fn overflowing() -> Pilot {
|
|
let mut pilot = Pilot::default();
|
|
pilot.inventory.insert(id("base:energy"), 105);
|
|
pilot.inventory.insert(id("base:super_shield"), 125);
|
|
pilot.super_shield_reserves = vec![130];
|
|
pilot
|
|
}
|
|
fn command(world: &mut World, registry: &Registry, command: Command) {
|
|
world.apply_commands(&[command], registry).unwrap();
|
|
}
|
|
fn collect(world: &mut World, registry: &Registry, item: &str) {
|
|
world.pickups.push(Pickup {
|
|
entity: 123,
|
|
item: item.into(),
|
|
x: world.player.x,
|
|
y: world.player.y,
|
|
legacy: None,
|
|
});
|
|
world
|
|
.step(Input::default(), registry, &mut ScriptRuntime::new())
|
|
.unwrap();
|
|
assert!(world.pickups.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn confirmed_load_steps_and_snapshots_keep_full_charge_with_bounded_usable_shields() {
|
|
let registry = registry();
|
|
let saved = overflowing();
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let path = dir.path().join("pilot.json");
|
|
saved.save(&path).unwrap();
|
|
let loaded = Pilot::inspect_load(&path, ®istry, &[]).unwrap();
|
|
assert!(!loaded.compatibility.is_compatible());
|
|
let pilot = loaded.confirm_load();
|
|
let mut world = World::new(&pilot.mission, &pilot, ®istry).unwrap();
|
|
assert_eq!((world.player.shield, world.player.super_shield), (100, 100));
|
|
for _ in 0..10 {
|
|
world
|
|
.step(Input::default(), ®istry, &mut ScriptRuntime::new())
|
|
.unwrap();
|
|
}
|
|
let after = world.pilot(&pilot);
|
|
assert_eq!(after.inventory, saved.inventory);
|
|
assert_eq!(after.super_shield_reserves, saved.super_shield_reserves);
|
|
// A serialized in-progress world also keeps its retained charge.
|
|
let world: World = serde_json::from_value(serde_json::to_value(world).unwrap()).unwrap();
|
|
assert_eq!(world.pilot(&pilot).inventory, saved.inventory);
|
|
}
|
|
|
|
#[test]
|
|
fn original_world_serialization_omits_overflow_and_old_worlds_default_to_zero() {
|
|
let registry = registry();
|
|
let pilot = Pilot::default();
|
|
let world = World::new(&pilot.mission, &pilot, ®istry).unwrap();
|
|
let value = serde_json::to_value(world).unwrap();
|
|
assert!(value.get("energy_overflow").is_none());
|
|
assert!(value.get("super_shield_overflow").is_none());
|
|
let decoded: World = serde_json::from_value(value).unwrap();
|
|
assert_eq!(
|
|
(decoded.energy_overflow, decoded.super_shield_overflow),
|
|
(0, 0)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn damage_consumes_usable_charge_and_destroyed_modules_do_not_resurrect_overflow() {
|
|
let registry = registry();
|
|
let pilot = overflowing();
|
|
let mut world = World::new(&pilot.mission, &pilot, ®istry).unwrap();
|
|
command(
|
|
&mut world,
|
|
®istry,
|
|
Command::Damage {
|
|
entity: 0,
|
|
amount: 20,
|
|
},
|
|
);
|
|
assert_eq!(world.player.super_shield, 80);
|
|
assert_eq!(world.pilot(&pilot).inventory[&id("base:super_shield")], 105);
|
|
command(
|
|
&mut world,
|
|
®istry,
|
|
Command::Damage {
|
|
entity: 0,
|
|
amount: 81,
|
|
},
|
|
);
|
|
assert_eq!(world.player.super_shield, 100);
|
|
assert_eq!(world.super_shield_overflow, 30);
|
|
assert!(world.player.super_shield_reserves.is_empty());
|
|
assert_eq!(world.pilot(&pilot).inventory[&id("base:super_shield")], 130);
|
|
command(
|
|
&mut world,
|
|
®istry,
|
|
Command::Damage {
|
|
entity: 0,
|
|
amount: 101,
|
|
},
|
|
);
|
|
assert_eq!(world.super_shield_overflow, 0);
|
|
assert!(
|
|
!world
|
|
.pilot(&pilot)
|
|
.inventory
|
|
.contains_key(&id("base:super_shield"))
|
|
);
|
|
command(
|
|
&mut world,
|
|
®istry,
|
|
Command::Damage {
|
|
entity: 0,
|
|
amount: 10,
|
|
},
|
|
);
|
|
assert_eq!(world.player.shield, 90);
|
|
assert_eq!(world.pilot(&pilot).inventory[&id("base:energy")], 95);
|
|
collect(&mut world, ®istry, "base:energy");
|
|
assert_eq!(world.player.shield, 100);
|
|
assert_eq!(world.pilot(&pilot).inventory[&id("base:energy")], 105);
|
|
}
|
|
|
|
#[test]
|
|
fn additive_pickup_and_script_grants_never_reduce_quantities_above_current_capacity() {
|
|
let registry = registry();
|
|
let mut pilot = overflowing();
|
|
pilot.inventory.insert(id("base:ammo"), 7);
|
|
let mut world = World::new(&pilot.mission, &pilot, ®istry).unwrap();
|
|
for item in ["base:ammo", "base:energy", "base:super_shield"] {
|
|
command(
|
|
&mut world,
|
|
®istry,
|
|
Command::GiveItem {
|
|
item: item.into(),
|
|
count: 1,
|
|
},
|
|
);
|
|
}
|
|
assert_eq!(world.pilot(&pilot).inventory, pilot.inventory);
|
|
collect(&mut world, ®istry, "base:ammo");
|
|
assert_eq!(world.pilot(&pilot).inventory[&id("base:ammo")], 7);
|
|
command(
|
|
&mut world,
|
|
®istry,
|
|
Command::ConsumeItem {
|
|
item: "base:ammo".into(),
|
|
count: 2,
|
|
},
|
|
);
|
|
command(
|
|
&mut world,
|
|
®istry,
|
|
Command::GiveItem {
|
|
item: "base:ammo".into(),
|
|
count: 1,
|
|
},
|
|
);
|
|
assert_eq!(world.pilot(&pilot).inventory[&id("base:ammo")], 5);
|
|
}
|
|
|
|
#[test]
|
|
fn scripted_consumption_reduces_retained_charge_without_restoring_spent_charge() {
|
|
let registry = registry();
|
|
let pilot = overflowing();
|
|
let mut world = World::new(&pilot.mission, &pilot, ®istry).unwrap();
|
|
command(
|
|
&mut world,
|
|
®istry,
|
|
Command::ConsumeItem {
|
|
item: "base:energy".into(),
|
|
count: 10,
|
|
},
|
|
);
|
|
command(
|
|
&mut world,
|
|
®istry,
|
|
Command::ConsumeItem {
|
|
item: "base:super_shield".into(),
|
|
count: 110,
|
|
},
|
|
);
|
|
assert_eq!(world.player.shield, 90);
|
|
assert_eq!(world.pilot(&pilot).inventory[&id("base:energy")], 95);
|
|
assert_eq!(world.player.super_shield, 0);
|
|
assert_eq!(world.pilot(&pilot).inventory[&id("base:super_shield")], 15);
|
|
command(
|
|
&mut world,
|
|
®istry,
|
|
Command::ConsumeItem {
|
|
item: "base:super_shield".into(),
|
|
count: 15,
|
|
},
|
|
);
|
|
command(
|
|
&mut world,
|
|
®istry,
|
|
Command::ConsumeItem {
|
|
item: "base:energy".into(),
|
|
count: 90,
|
|
},
|
|
);
|
|
assert_eq!(world.pilot(&pilot).inventory[&id("base:energy")], 5);
|
|
assert_eq!(world.super_shield_overflow, 0);
|
|
command(
|
|
&mut world,
|
|
®istry,
|
|
Command::Damage {
|
|
entity: 0,
|
|
amount: 1,
|
|
},
|
|
);
|
|
assert_eq!(world.pilot(&pilot).inventory[&id("base:super_shield")], 130);
|
|
}
|
|
|
|
#[test]
|
|
fn new_large_grants_are_split_once_without_duplicating_default_energy() {
|
|
let mut registry = registry();
|
|
for item in ["base:energy", "base:super_shield"] {
|
|
registry.items.get_mut(&id(item)).unwrap().max_count = 200;
|
|
}
|
|
let mut pilot = Pilot::default();
|
|
pilot.inventory.remove(&id("base:energy"));
|
|
let mut world = World::new(&pilot.mission, &pilot, ®istry).unwrap();
|
|
for item in ["base:energy", "base:super_shield"] {
|
|
command(
|
|
&mut world,
|
|
®istry,
|
|
Command::GiveItem {
|
|
item: item.into(),
|
|
count: 125,
|
|
},
|
|
);
|
|
assert_eq!(world.pilot(&pilot).inventory[&id(item)], 125);
|
|
}
|
|
assert_eq!((world.player.shield, world.player.super_shield), (100, 100));
|
|
}
|