Compare commits

...

14 Commits

Author SHA1 Message Date
c8b9564009 random formatting and ordering changes
Some checks failed
continuous-integration/drone/push Build is failing
2021-01-23 23:39:04 +00:00
b6cc50360a TOOD -> TODO 2021-01-23 04:58:20 +00:00
a238ae3846 add remaining modifiers for item types and fix presents? 2021-01-23 04:56:24 +00:00
b29bb473d5 more tests for unwrapping equips 2020-12-19 21:51:19 +00:00
9e8e10d434 finally added a present test. rename item_actions to menu_actions 2020-12-18 05:02:45 +00:00
e774b9c950 oops forgot mags 2020-12-13 20:12:31 +00:00
fc84db5393 put armors in presents. apply modifiers for armors, shields, units, mags. remove prints 2020-12-13 18:46:28 +00:00
ba0b827c24 modifiers are for the boys 2020-12-12 15:47:30 +00:00
637e182224 andy stop touching things pls 2020-12-07 02:12:49 +00:00
98b2ebfdd8 wrap everything except armours 2020-12-07 02:12:24 +00:00
7e9b989078 holy shit unwrapping presents finally works for weapons 2020-12-06 04:19:26 +00:00
c9160f177d fix tests 2020-12-04 02:47:02 +00:00
a77b9874ff initial present stuff 2020-12-04 02:35:30 +00:00
fe0f7ba41e initial wrapping stuff 2020-12-04 00:53:48 +00:00
46 changed files with 1627 additions and 424 deletions

View File

@ -1,6 +1,6 @@
[Extermination] [Extermination]
list_order = 1 list_order = 1
description = "I am a description" description = "kill some shit"
[[Extermination.quests]] [[Extermination.quests]]
bin = "q058-ret-bb.bin" bin = "q058-ret-bb.bin"
@ -32,4 +32,13 @@ dat = "q233-ext-bb.dat"
[[Retrieval.quests]] [[Retrieval.quests]]
bin = "q236-ext-bb.bin" bin = "q236-ext-bb.bin"
dat = "q236-ext-bb.dat" dat = "q236-ext-bb.dat"
#drop_table = "q102-drops" #drop_table = "q102-drops"
[Shop]
list_order = 3
description = "buy some shit"
[[Shop.quests]]
bin = "q219-shp-bb.bin"
dat = "q219-shp-bb.dat"
#drop_table = "q204-drops"

View File

@ -74,7 +74,7 @@ fn main() {
character.meseta = 999999; character.meseta = 999999;
let character = entity_gateway.create_character(character).await.unwrap(); let character = entity_gateway.create_character(character).await.unwrap();
for _ in 0..3 { for _ in 0..3usize {
entity_gateway.create_item( entity_gateway.create_item(
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Weapon( item: item::ItemDetail::Weapon(
@ -84,6 +84,7 @@ fn main() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -93,12 +94,13 @@ fn main() {
}).await.unwrap(); }).await.unwrap();
} }
for _ in 0..8 { for _ in 0..8usize {
entity_gateway.create_item( entity_gateway.create_item(
NewItemEntity { NewItemEntity {
item: ItemDetail::Tool ( item: ItemDetail::Tool (
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -119,6 +121,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}),
None,], None,],
tekked: false, tekked: false,
wrapping: Some(item::WrappingPaper::BlackYellow),
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -136,6 +139,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}),
None,], None,],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -153,6 +157,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
None,], None,],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -170,6 +175,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
None,], None,],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -187,6 +193,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),], Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -208,6 +215,7 @@ fn main() {
item: ItemDetail::Tool ( item: ItemDetail::Tool (
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::FedToMag { location: item::ItemLocation::FedToMag {
@ -223,6 +231,7 @@ fn main() {
item: ItemDetail::Tool ( item: ItemDetail::Tool (
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::CellOfMag502, tool: item::tool::ToolType::CellOfMag502,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -234,6 +243,8 @@ fn main() {
item: ItemDetail::Tool ( item: ItemDetail::Tool (
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::CellOfMag502, tool: item::tool::ToolType::CellOfMag502,
// wrapping: None,
wrapping: Some(item::WrappingPaper::PinkYellowGreen),
} }
), ),
location: item::ItemLocation::Consumed, location: item::ItemLocation::Consumed,
@ -251,6 +262,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 80}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 80}),
None,], None,],
tekked: false, tekked: false,
wrapping: None,
} }
), ),
location: ItemLocation::Bank { location: ItemLocation::Bank {
@ -266,6 +278,7 @@ fn main() {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 4, slots: 4,
wrapping: Some(item::WrappingPaper::Green),
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -280,6 +293,7 @@ fn main() {
shield: item::shield::ShieldType::Barrier, shield: item::shield::ShieldType::Barrier,
dfp: 0, dfp: 0,
evp: 0, evp: 0,
wrapping: Some(item::WrappingPaper::Green),
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -293,6 +307,7 @@ fn main() {
item::unit::Unit { item::unit::Unit {
unit: item::unit::UnitType::PriestMind, unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus), modifier: Some(item::unit::UnitModifier::Minus),
wrapping: None,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -306,6 +321,8 @@ fn main() {
item::unit::Unit { item::unit::Unit {
unit: item::unit::UnitType::PriestMind, unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus), modifier: Some(item::unit::UnitModifier::Minus),
// wrapping: None,
wrapping: Some(item::WrappingPaper::YellowBlue),
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -319,6 +336,7 @@ fn main() {
item::unit::Unit { item::unit::Unit {
unit: item::unit::UnitType::PriestMind, unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus), modifier: Some(item::unit::UnitModifier::Minus),
wrapping: None,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -332,6 +350,7 @@ fn main() {
item::unit::Unit { item::unit::Unit {
unit: item::unit::UnitType::PriestMind, unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus), modifier: Some(item::unit::UnitModifier::Minus),
wrapping: None,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -341,8 +360,56 @@ fn main() {
).await.unwrap(); ).await.unwrap();
let item13 = entity_gateway.create_item( let item13 = entity_gateway.create_item(
NewItemEntity { NewItemEntity {
item: ItemDetail::Mag( item: item::ItemDetail::Mag(item::mag::Mag::wrapped_baby_mag(5)),
item::mag::Mag::baby_mag(5) location: ItemLocation::Inventory {
character_id: character.id,
}
}
).await.unwrap();
// wrapping monomates doesn't do anything
let item14 = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Tool (
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
// wrapping: None,
wrapping: Some(item::WrappingPaper::Yellow),
}
),
location: ItemLocation::Inventory {
character_id: character.id,
}
}
).await.unwrap();
/* wrapping techs is no bueno */
// let item15 = entity_gateway.create_item(
// NewItemEntity {
// item: ItemDetail::TechniqueDisk (
// item::tech::TechniqueDisk {
// tech: item::tech::Technique::Foie,
// level: 5,
// // wrapping: None,
// wrapping: Some(item::WrappingPaper::Blue),
// }
// ),
// location: ItemLocation::Inventory {
// character_id: character.id,
// }
// }
// ).await.unwrap();
let item16 = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::ESWeapon (
item::esweapon::ESWeapon {
esweapon: item::esweapon::ESWeaponType::Hammer,
special: Some(item::esweapon::ESWeaponSpecial::Hell),
name: "BAN".to_owned(),
grind: 69u8,
wrapping: Some(item::WrappingPaper::LightBlueOrange),
}
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
@ -359,7 +426,7 @@ fn main() {
}; };
entity_gateway.set_character_equips(&character.id, &equipped).await.unwrap(); entity_gateway.set_character_equips(&character.id, &equipped).await.unwrap();
let inventory = item::InventoryEntity::new(vec![item0, item1, item2_w, item3, item4, item5_m, item6, item7_a, item8_s, item9_u0, item10_u1, item11_u2, item12_u3, item13]); let inventory = item::InventoryEntity::new(vec![item0, item1, item2_w, item3, item4, item5_m, item6, item7_a, item8_s, item9_u0, item10_u1, item11_u2, item12_u3, item13, item14, /*item15,*/ item16]);
entity_gateway.set_character_inventory(&character.id, &inventory).await.unwrap(); entity_gateway.set_character_inventory(&character.id, &inventory).await.unwrap();
entity_gateway.set_character_bank(&character.id, &item::BankEntity::default(), item::BankName("".into())).await.unwrap(); entity_gateway.set_character_bank(&character.id, &item::BankEntity::default(), item::BankName("".into())).await.unwrap();
} }

View File

@ -85,6 +85,33 @@ pub trait EntityGateway: Send + Sync + Clone {
unimplemented!(); unimplemented!();
} }
async fn add_esweapon_modifier(&mut self, _item_id: &ItemEntityId, _modifier: esweapon::ESWeaponModifier) -> Result<(), GatewayError> {
unimplemented!();
}
async fn add_armor_modifier(&mut self, _item_id: &ItemEntityId, _modifier: armor::ArmorModifier) -> Result<(), GatewayError> {
unimplemented!();
}
async fn add_unit_modifier(&mut self, _item_id: &ItemEntityId, _modifier: unit::UnitModifier) -> Result<(), GatewayError> {
unimplemented!();
}
async fn add_shield_modifier(&mut self, _item_id: &ItemEntityId, _modifier: shield::ShieldModifier) -> Result<(), GatewayError> {
unimplemented!();
}
async fn add_mag_modifier(&mut self, _item_id: &ItemEntityId, _modifier: mag::MagModifier) -> Result<(), GatewayError> {
unimplemented!();
}
async fn add_tech_modifier(&mut self, _item_id: &ItemEntityId, _modifier: tech::TechModifier) -> Result<(), GatewayError> {
unimplemented!();
}
async fn add_tool_modifier(&mut self, _item_id: &ItemEntityId, _modifier: tool::ToolModifier) -> Result<(), GatewayError> {
unimplemented!();
}
/* /*
async fn get_items_by_character(&self, _char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> { async fn get_items_by_character(&self, _char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> {

View File

@ -17,8 +17,14 @@ pub struct InMemoryGateway {
inventories: Arc<Mutex<BTreeMap<CharacterEntityId, InventoryEntity>>>, inventories: Arc<Mutex<BTreeMap<CharacterEntityId, InventoryEntity>>>,
banks: Arc<Mutex<BTreeMap<CharacterEntityId, BankEntity>>>, banks: Arc<Mutex<BTreeMap<CharacterEntityId, BankEntity>>>,
equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>, equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>,
mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>,
weapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<weapon::WeaponModifier>>>>, weapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<weapon::WeaponModifier>>>>,
esweapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<esweapon::ESWeaponModifier>>>>,
armor_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<armor::ArmorModifier>>>>,
unit_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<unit::UnitModifier>>>>,
shield_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<shield::ShieldModifier>>>>,
mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>,
tech_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<tech::TechModifier>>>>,
tool_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<tool::ToolModifier>>>>,
} }
impl InMemoryGateway { impl InMemoryGateway {
@ -31,8 +37,14 @@ impl InMemoryGateway {
inventories: Arc::new(Mutex::new(BTreeMap::new())), inventories: Arc::new(Mutex::new(BTreeMap::new())),
banks: Arc::new(Mutex::new(BTreeMap::new())), banks: Arc::new(Mutex::new(BTreeMap::new())),
equips: Arc::new(Mutex::new(BTreeMap::new())), equips: Arc::new(Mutex::new(BTreeMap::new())),
mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())), weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
esweapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
armor_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
unit_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
shield_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
tech_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
tool_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
} }
} }
} }
@ -52,8 +64,46 @@ impl InMemoryGateway {
} }
ItemDetail::Weapon(weapon) ItemDetail::Weapon(weapon)
}, },
ItemDetail::ESWeapon(mut esweapon) => {
if let Some(esweapon_modifiers) = self.esweapon_modifiers.lock().unwrap().get(&item.id) {
for esweapon_modifier in esweapon_modifiers.iter() {
esweapon.apply_modifier(&esweapon_modifier);
}
}
ItemDetail::ESWeapon(esweapon)
},
ItemDetail::Armor(mut armor) => {
if let Some(armor_modifiers) = self.armor_modifiers.lock().unwrap().get(&item.id) {
for armor_modifier in armor_modifiers.iter() {
armor.apply_modifier(&armor_modifier);
}
}
ItemDetail::Armor(armor)
},
ItemDetail::Unit(mut unit) => {
if let Some(unit_modifiers) = self.unit_modifiers.lock().unwrap().get(&item.id) {
for unit_modifier in unit_modifiers.iter() {
unit.apply_modifier(&unit_modifier);
}
}
ItemDetail::Unit(unit)
},
ItemDetail::Shield(mut shield) => {
if let Some(shield_modifiers) = self.shield_modifiers.lock().unwrap().get(&item.id) {
for shield_modifier in shield_modifiers.iter() {
shield.apply_modifier(&shield_modifier);
}
}
ItemDetail::Shield(shield)
},
ItemDetail::Mag(mag) => { ItemDetail::Mag(mag) => {
let mut mag = mag::Mag::baby_mag(mag.color as u16); let mut mag = {
if mag.wrapping.is_some() {
mag::Mag::wrapped_baby_mag(mag.color as u16)
} else {
mag::Mag::baby_mag(mag.color as u16)
}
};
if let Some(mag_modifiers) = self.mag_modifiers.lock().unwrap().get(&item.id) { if let Some(mag_modifiers) = self.mag_modifiers.lock().unwrap().get(&item.id) {
for mag_modifier in mag_modifiers.iter() { for mag_modifier in mag_modifiers.iter() {
match mag_modifier { match mag_modifier {
@ -81,12 +131,24 @@ impl InMemoryGateway {
} }
} }
ItemDetail::Mag(mag) ItemDetail::Mag(mag)
} },
_ => { ItemDetail::TechniqueDisk(mut tech) => {
item.item if let Some(tech_modifiers) = self.tech_modifiers.lock().unwrap().get(&item.id) {
} for tech_modifier in tech_modifiers.iter() {
tech.apply_modifier(&tech_modifier);
}
}
ItemDetail::TechniqueDisk(tech)
},
ItemDetail::Tool(mut tool) => {
if let Some(tool_modifiers) = self.tool_modifiers.lock().unwrap().get(&item.id) {
for tool_modifier in tool_modifiers.iter() {
tool.apply_modifier(&tool_modifier);
}
}
ItemDetail::Tool(tool)
},
}; };
item item
}) })
}) })
@ -273,6 +335,62 @@ impl EntityGateway for InMemoryGateway {
Ok(()) Ok(())
} }
async fn add_esweapon_modifier(&mut self, item_id: &ItemEntityId, modifier: esweapon::ESWeaponModifier) -> Result<(), GatewayError> {
self.esweapon_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn add_armor_modifier(&mut self, item_id: &ItemEntityId, modifier: armor::ArmorModifier) -> Result<(), GatewayError> {
self.armor_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn add_unit_modifier(&mut self, item_id: &ItemEntityId, modifier: unit::UnitModifier) -> Result<(), GatewayError> {
self.unit_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn add_shield_modifier(&mut self, item_id: &ItemEntityId, modifier: shield::ShieldModifier) -> Result<(), GatewayError> {
self.shield_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn add_mag_modifier(&mut self, item_id: &ItemEntityId, modifier: mag::MagModifier) -> Result<(), GatewayError> {
self.mag_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn add_tech_modifier(&mut self, item_id: &ItemEntityId, modifier: tech::TechModifier) -> Result<(), GatewayError> {
self.tech_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn add_tool_modifier(&mut self, item_id: &ItemEntityId, modifier: tool::ToolModifier) -> Result<(), GatewayError> {
self.tool_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> { async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
println!("getting inv"); println!("getting inv");
let inventories = self.inventories.lock().unwrap(); let inventories = self.inventories.lock().unwrap();
@ -299,7 +417,7 @@ impl EntityGateway for InMemoryGateway {
Ok(()) Ok(())
} }
// TOOD: impl bank name // TODO: impl bank name
async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, _bank_name: BankName) -> Result<(), GatewayError> { async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, _bank_name: BankName) -> Result<(), GatewayError> {
let mut banks = self.banks.lock().unwrap(); let mut banks = self.banks.lock().unwrap();
banks.insert(*char_id, bank.clone()); banks.insert(*char_id, bank.clone());

View File

@ -50,7 +50,7 @@ create table player_character (
prop_y real not null, prop_y real not null,
techs bytea not null, techs bytea not null,
config bytea not null, config bytea not null,
infoboard varchar(172) not null, infoboard varchar(172) not null,
guildcard varchar(172) not null, guildcard varchar(172) not null,
@ -93,14 +93,14 @@ create table weapon_modifier (
created_at timestamptz default current_timestamp not null created_at timestamptz default current_timestamp not null
); );
create table armor_modifier ( create table esweapon_modifier (
armor integer references item (id) not null, esweapon integer references item (id) not null,
modifier jsonb not null, modifier jsonb not null,
created_at timestamptz default current_timestamp not null created_at timestamptz default current_timestamp not null
); );
create table shield_modifier ( create table armor_modifier (
shield integer references item (id) not null, armor integer references item (id) not null,
modifier jsonb not null, modifier jsonb not null,
created_at timestamptz default current_timestamp not null created_at timestamptz default current_timestamp not null
); );
@ -111,8 +111,8 @@ create table unit_modifier (
created_at timestamptz default current_timestamp not null created_at timestamptz default current_timestamp not null
); );
create table esweapon_modifier ( create table shield_modifier (
esweapon integer references item (id) not null, shield integer references item (id) not null,
modifier jsonb not null, modifier jsonb not null,
created_at timestamptz default current_timestamp not null created_at timestamptz default current_timestamp not null
); );
@ -122,3 +122,15 @@ create table mag_modifier (
modifier jsonb not null, modifier jsonb not null,
created_at timestamptz default current_timestamp not null created_at timestamptz default current_timestamp not null
); );
create table tech_modifier (
tech integer references item (id) not null,
modifier jsonb not null,
created_at timestamptz default current_timestamp not null
);
create table tool_modifier (
tool integer references item (id) not null,
modifier jsonb not null,
created_at timestamptz default current_timestamp not null
);

View File

@ -178,7 +178,6 @@ impl From<SectionID> for PgSectionId {
} }
} }
#[derive(Debug, sqlx::FromRow)] #[derive(Debug, sqlx::FromRow)]
pub struct PgCharacter { pub struct PgCharacter {
pub id: i32, pub id: i32,
@ -277,8 +276,6 @@ impl Into<CharacterEntity> for PgCharacter {
pub struct PgGuildCard { pub struct PgGuildCard {
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct PgWeapon { pub struct PgWeapon {
weapon: weapon::WeaponType, weapon: weapon::WeaponType,
@ -286,6 +283,7 @@ pub struct PgWeapon {
grind: u8, grind: u8,
attrs: HashMap<weapon::Attribute, i8>, attrs: HashMap<weapon::Attribute, i8>,
tekked: bool, tekked: bool,
wrapping: Option<WrappingPaper>,
} }
impl From<weapon::Weapon> for PgWeapon { impl From<weapon::Weapon> for PgWeapon {
@ -296,6 +294,7 @@ impl From<weapon::Weapon> for PgWeapon {
grind: other.grind, grind: other.grind,
attrs: other.attrs.iter().flatten().map(|attr| (attr.attr, attr.value)).collect(), attrs: other.attrs.iter().flatten().map(|attr| (attr.attr, attr.value)).collect(),
tekked: other.tekked, tekked: other.tekked,
wrapping: other.wrapping,
} }
} }
} }
@ -316,6 +315,7 @@ impl Into<weapon::Weapon> for PgWeapon {
grind: self.grind, grind: self.grind,
attrs: attrs, attrs: attrs,
tekked: self.tekked, tekked: self.tekked,
wrapping: self.wrapping,
} }
} }
} }
@ -326,177 +326,13 @@ pub struct PgWeaponModifier {
pub modifier: sqlx::types::Json<weapon::WeaponModifier>, pub modifier: sqlx::types::Json<weapon::WeaponModifier>,
} }
#[derive(Debug, Serialize, Deserialize)]
pub struct PgArmor {
armor: armor::ArmorType,
dfp: u8,
evp: u8,
slots: u8,
}
impl From<armor::Armor> for PgArmor {
fn from(other: armor::Armor) -> PgArmor {
PgArmor {
armor: other.armor,
dfp: other.dfp,
evp: other.evp,
slots: other.slots,
}
}
}
impl Into<armor::Armor> for PgArmor {
fn into(self) -> armor::Armor {
armor::Armor {
armor: self.armor,
dfp: self.dfp,
evp: self.evp,
slots: self.slots,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PgShield {
shield: shield::ShieldType,
dfp: u8,
evp: u8,
}
impl From<shield::Shield> for PgShield {
fn from(other: shield::Shield) -> PgShield {
PgShield {
shield: other.shield,
dfp: other.dfp,
evp: other.evp,
}
}
}
impl Into<shield::Shield> for PgShield {
fn into(self) -> shield::Shield {
shield::Shield {
shield: self.shield,
dfp: self.dfp,
evp: self.evp,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PgUnit {
unit: unit::UnitType,
modifier: Option<unit::UnitModifier>,
}
impl From<unit::Unit> for PgUnit {
fn from(other: unit::Unit) -> PgUnit {
PgUnit {
unit: other.unit,
modifier: other.modifier,
}
}
}
impl Into<unit::Unit> for PgUnit {
fn into(self) -> unit::Unit {
unit::Unit {
unit: self.unit,
modifier: self.modifier,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PgTool {
pub tool: tool::ToolType,
}
impl From<tool::Tool> for PgTool {
fn from(other: tool::Tool) -> PgTool {
PgTool {
tool: other.tool,
}
}
}
impl Into<tool::Tool> for PgTool {
fn into(self) -> tool::Tool {
tool::Tool {
tool: self.tool,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PgTechDisk {
tech: tech::Technique,
level: u32,
}
impl From<tech::TechniqueDisk> for PgTechDisk {
fn from(other: tech::TechniqueDisk) -> PgTechDisk {
PgTechDisk {
tech: other.tech,
level: other.level,
}
}
}
impl Into<tech::TechniqueDisk> for PgTechDisk {
fn into(self) -> tech::TechniqueDisk {
tech::TechniqueDisk {
tech: self.tech,
level: self.level
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PgMag {
mag: mag::MagType,
synchro: u8,
color: u8,
}
impl From<mag::Mag> for PgMag {
fn from(other: mag::Mag) -> PgMag {
PgMag {
mag: other.mag,
synchro: other.synchro,
color: other.color,
}
}
}
impl Into<mag::Mag> for PgMag {
fn into(self) -> mag::Mag {
/*mag::Mag {
mag: self.mag,
synchro: self.synchro,
color: self.color,
def: 500,
pow: 0,
dex: 0,
mnd: 0,
iq: 0,
photon_blast: [None; 3],
class: CharacterClass::HUmar,
id: SectionID::Viridia,
}*/
let mut mag = mag::Mag::baby_mag(self.color as u16);
mag.mag = self.mag;
mag.synchro = self.synchro;
mag
}
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct PgESWeapon { pub struct PgESWeapon {
esweapon: esweapon::ESWeaponType, esweapon: esweapon::ESWeaponType,
special: Option<esweapon::ESWeaponSpecial>, special: Option<esweapon::ESWeaponSpecial>,
name: String, name: String,
grind: u8, grind: u8,
wrapping: Option<WrappingPaper>,
} }
impl From<esweapon::ESWeapon> for PgESWeapon { impl From<esweapon::ESWeapon> for PgESWeapon {
@ -506,6 +342,7 @@ impl From<esweapon::ESWeapon> for PgESWeapon {
special: other.special, special: other.special,
name: other.name, name: other.name,
grind: other.grind, grind: other.grind,
wrapping: other.wrapping,
} }
} }
} }
@ -517,33 +354,240 @@ impl Into<esweapon::ESWeapon> for PgESWeapon {
special: self.special, special: self.special,
name: self.name, name: self.name,
grind: self.grind, grind: self.grind,
wrapping: self.wrapping,
} }
} }
} }
#[derive(Debug, sqlx::FromRow)]
pub struct PgESWeaponModifier {
pub esweapon: i32,
pub modifier: sqlx::types::Json<esweapon::ESWeaponModifier>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PgArmor {
armor: armor::ArmorType,
dfp: u8,
evp: u8,
slots: u8,
wrapping: Option<WrappingPaper>, // TODO: check if this clobbers slots
}
impl From<armor::Armor> for PgArmor {
fn from(other: armor::Armor) -> PgArmor {
PgArmor {
armor: other.armor,
dfp: other.dfp,
evp: other.evp,
slots: other.slots,
wrapping: other.wrapping,
}
}
}
impl Into<armor::Armor> for PgArmor {
fn into(self) -> armor::Armor {
armor::Armor {
armor: self.armor,
dfp: self.dfp,
evp: self.evp,
slots: self.slots,
wrapping: self.wrapping,
}
}
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgArmorModifier {
pub armor: i32,
pub modifier: sqlx::types::Json<armor::ArmorModifier>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PgUnit {
unit: unit::UnitType,
modifier: Option<unit::UnitModifier>,
wrapping: Option<WrappingPaper>,
}
impl From<unit::Unit> for PgUnit {
fn from(other: unit::Unit) -> PgUnit {
PgUnit {
unit: other.unit,
modifier: other.modifier,
wrapping: other.wrapping,
}
}
}
impl Into<unit::Unit> for PgUnit {
fn into(self) -> unit::Unit {
unit::Unit {
unit: self.unit,
modifier: self.modifier,
wrapping: self.wrapping,
}
}
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgUnitModifier {
pub unit: i32,
pub modifier: sqlx::types::Json<unit::UnitModifier>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PgShield {
shield: shield::ShieldType,
dfp: u8,
evp: u8,
wrapping: Option<WrappingPaper>,
}
impl From<shield::Shield> for PgShield {
fn from(other: shield::Shield) -> PgShield {
PgShield {
shield: other.shield,
dfp: other.dfp,
evp: other.evp,
wrapping: other.wrapping,
}
}
}
impl Into<shield::Shield> for PgShield {
fn into(self) -> shield::Shield {
shield::Shield {
shield: self.shield,
dfp: self.dfp,
evp: self.evp,
wrapping: self.wrapping,
}
}
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgShieldModifier {
pub shield: i32,
pub modifier: sqlx::types::Json<shield::ShieldModifier>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PgMag {
mag: mag::MagType,
synchro: u8,
color: u8,
wrapping: Option<WrappingPaper>,
}
impl From<mag::Mag> for PgMag {
fn from(other: mag::Mag) -> PgMag {
PgMag {
mag: other.mag,
synchro: other.synchro,
color: other.color,
wrapping: other.wrapping,
}
}
}
impl Into<mag::Mag> for PgMag {
fn into(self) -> mag::Mag {
let mut mag = mag::Mag::baby_mag(self.color as u16);
mag.mag = self.mag;
mag.synchro = self.synchro;
mag.wrapping = self.wrapping;
mag
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PgTechDisk {
tech: tech::Technique,
level: u32,
wrapping: Option<WrappingPaper>,
}
impl From<tech::TechniqueDisk> for PgTechDisk {
fn from(other: tech::TechniqueDisk) -> PgTechDisk {
PgTechDisk {
tech: other.tech,
level: other.level,
wrapping: other.wrapping,
}
}
}
impl Into<tech::TechniqueDisk> for PgTechDisk {
fn into(self) -> tech::TechniqueDisk {
tech::TechniqueDisk {
tech: self.tech,
level: self.level,
wrapping: self.wrapping,
}
}
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgTechModifier {
pub tool: i32,
pub modifier: sqlx::types::Json<tech::TechModifier>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PgTool {
pub tool: tool::ToolType,
wrapping: Option<WrappingPaper>,
}
impl From<tool::Tool> for PgTool {
fn from(other: tool::Tool) -> PgTool {
PgTool {
tool: other.tool,
wrapping: other.wrapping,
}
}
}
impl Into<tool::Tool> for PgTool {
fn into(self) -> tool::Tool {
tool::Tool {
tool: self.tool,
wrapping: self.wrapping,
}
}
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgToolModifier {
pub tool: i32,
pub modifier: sqlx::types::Json<tool::ToolModifier>,
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum PgItemDetail { pub enum PgItemDetail {
Weapon(PgWeapon), Weapon(PgWeapon),
Armor(PgArmor),
Shield(PgShield),
Unit(PgUnit),
Tool(PgTool),
TechDisk(PgTechDisk),
Mag(PgMag),
ESWeapon(PgESWeapon), ESWeapon(PgESWeapon),
Armor(PgArmor),
Unit(PgUnit),
Shield(PgShield),
Mag(PgMag),
TechDisk(PgTechDisk),
Tool(PgTool),
} }
impl From<ItemDetail> for PgItemDetail { impl From<ItemDetail> for PgItemDetail {
fn from(other: ItemDetail) -> PgItemDetail { fn from(other: ItemDetail) -> PgItemDetail {
match other { match other {
ItemDetail::Weapon(weapon) => PgItemDetail::Weapon(weapon.into()), ItemDetail::Weapon(weapon) => PgItemDetail::Weapon(weapon.into()),
ItemDetail::Armor(armor) => PgItemDetail::Armor(armor.into()),
ItemDetail::Shield(shield) => PgItemDetail::Shield(shield.into()),
ItemDetail::Unit(unit) => PgItemDetail::Unit(unit.into()),
ItemDetail::Tool(tool) => PgItemDetail::Tool(tool.into()),
ItemDetail::TechniqueDisk(tech_disk) => PgItemDetail::TechDisk(tech_disk.into()),
ItemDetail::Mag(mag) => PgItemDetail::Mag(mag.into()),
ItemDetail::ESWeapon(esweapon) => PgItemDetail::ESWeapon(esweapon.into()), ItemDetail::ESWeapon(esweapon) => PgItemDetail::ESWeapon(esweapon.into()),
ItemDetail::Armor(armor) => PgItemDetail::Armor(armor.into()),
ItemDetail::Unit(unit) => PgItemDetail::Unit(unit.into()),
ItemDetail::Shield(shield) => PgItemDetail::Shield(shield.into()),
ItemDetail::Mag(mag) => PgItemDetail::Mag(mag.into()),
ItemDetail::TechniqueDisk(tech_disk) => PgItemDetail::TechDisk(tech_disk.into()),
ItemDetail::Tool(tool) => PgItemDetail::Tool(tool.into()),
} }
} }
} }
@ -552,13 +596,13 @@ impl Into<ItemDetail> for PgItemDetail {
fn into(self) -> ItemDetail { fn into(self) -> ItemDetail {
match self { match self {
PgItemDetail::Weapon(weapon) => ItemDetail::Weapon(weapon.into()), PgItemDetail::Weapon(weapon) => ItemDetail::Weapon(weapon.into()),
PgItemDetail::Armor(armor) => ItemDetail::Armor(armor.into()),
PgItemDetail::Shield(shield) => ItemDetail::Shield(shield.into()),
PgItemDetail::Unit(unit) => ItemDetail::Unit(unit.into()),
PgItemDetail::Tool(tool) => ItemDetail::Tool(tool.into()),
PgItemDetail::TechDisk(tech_disk) => ItemDetail::TechniqueDisk(tech_disk.into()),
PgItemDetail::Mag(mag) => ItemDetail::Mag(mag.into()),
PgItemDetail::ESWeapon(esweapon) => ItemDetail::ESWeapon(esweapon.into()), PgItemDetail::ESWeapon(esweapon) => ItemDetail::ESWeapon(esweapon.into()),
PgItemDetail::Armor(armor) => ItemDetail::Armor(armor.into()),
PgItemDetail::Unit(unit) => ItemDetail::Unit(unit.into()),
PgItemDetail::Shield(shield) => ItemDetail::Shield(shield.into()),
PgItemDetail::Mag(mag) => ItemDetail::Mag(mag.into()),
PgItemDetail::TechDisk(tech_disk) => ItemDetail::TechniqueDisk(tech_disk.into()),
PgItemDetail::Tool(tool) => ItemDetail::Tool(tool.into()),
} }
} }
} }
@ -569,7 +613,6 @@ pub struct PgItem {
pub item: sqlx::types::Json<PgItemDetail>, pub item: sqlx::types::Json<PgItemDetail>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum PgItemLocationDetail { pub enum PgItemLocationDetail {
Inventory { Inventory {
@ -627,7 +670,6 @@ impl Into<ItemLocation> for PgItemLocationDetail {
} }
} }
#[derive(Debug, sqlx::FromRow)] #[derive(Debug, sqlx::FromRow)]
pub struct PgItemLocation { pub struct PgItemLocation {
//pub id: i32, //pub id: i32,
@ -635,13 +677,14 @@ pub struct PgItemLocation {
created_at: chrono::DateTime<chrono::Utc>, created_at: chrono::DateTime<chrono::Utc>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum PgMagModifierDetail { pub enum PgMagModifierDetail {
FeedMag(i32), FeedMag(i32),
BankMag, BankMag,
MagCell(i32), MagCell(i32),
OwnerChange(CharacterClass, SectionID) OwnerChange(CharacterClass, SectionID),
WrapPresent,
UnwrapPresent,
} }
impl From<mag::MagModifier> for PgMagModifierDetail { impl From<mag::MagModifier> for PgMagModifierDetail {
@ -651,6 +694,8 @@ impl From<mag::MagModifier> for PgMagModifierDetail {
mag::MagModifier::BankMag => PgMagModifierDetail::BankMag, mag::MagModifier::BankMag => PgMagModifierDetail::BankMag,
mag::MagModifier::MagCell(cell) => PgMagModifierDetail::MagCell(cell.0 as i32), mag::MagModifier::MagCell(cell) => PgMagModifierDetail::MagCell(cell.0 as i32),
mag::MagModifier::OwnerChange(class, section_id) => PgMagModifierDetail::OwnerChange(class, section_id), mag::MagModifier::OwnerChange(class, section_id) => PgMagModifierDetail::OwnerChange(class, section_id),
mag::MagModifier::WrapPresent => PgMagModifierDetail::WrapPresent,
mag::MagModifier::UnwrapPresent => PgMagModifierDetail::UnwrapPresent,
} }
} }
} }
@ -662,6 +707,8 @@ impl Into<mag::MagModifier> for PgMagModifierDetail {
PgMagModifierDetail::BankMag => mag::MagModifier::BankMag, PgMagModifierDetail::BankMag => mag::MagModifier::BankMag,
PgMagModifierDetail::MagCell(cell) => mag::MagModifier::MagCell(ItemEntityId(cell as u32)), PgMagModifierDetail::MagCell(cell) => mag::MagModifier::MagCell(ItemEntityId(cell as u32)),
PgMagModifierDetail::OwnerChange(class, section_id) => mag::MagModifier::OwnerChange(class, section_id), PgMagModifierDetail::OwnerChange(class, section_id) => mag::MagModifier::OwnerChange(class, section_id),
PgMagModifierDetail::WrapPresent => mag::MagModifier::WrapPresent,
PgMagModifierDetail::UnwrapPresent => mag::MagModifier::UnwrapPresent,
} }
} }
} }
@ -673,7 +720,6 @@ pub struct PgMagModifier {
created_at: chrono::DateTime<chrono::Utc>, created_at: chrono::DateTime<chrono::Utc>,
} }
#[derive(Debug, sqlx::FromRow)] #[derive(Debug, sqlx::FromRow)]
pub struct PgItemEntity { pub struct PgItemEntity {
pub id: i32, pub id: i32,
@ -697,7 +743,6 @@ impl Into<ItemEntity> for PgItemWithLocation {
} }
} }
#[derive(Debug, sqlx::FromRow)] #[derive(Debug, sqlx::FromRow)]
pub struct PgMagModifierWithParameters { pub struct PgMagModifierWithParameters {
pub mag: i32, pub mag: i32,
@ -706,7 +751,6 @@ pub struct PgMagModifierWithParameters {
pub cell: Option<sqlx::types::Json<PgTool>>, pub cell: Option<sqlx::types::Json<PgTool>>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)] #[serde(untagged)]
pub enum PgInventoryItemEntity { pub enum PgInventoryItemEntity {
@ -732,11 +776,11 @@ pub struct PgEquipped {
pchar: i32, pchar: i32,
weapon: Option<i32>, weapon: Option<i32>,
armor: Option<i32>, armor: Option<i32>,
shield: Option<i32>,
unit0: Option<i32>, unit0: Option<i32>,
unit1: Option<i32>, unit1: Option<i32>,
unit2: Option<i32>, unit2: Option<i32>,
unit3: Option<i32>, unit3: Option<i32>,
shield: Option<i32>,
mag: Option<i32>, mag: Option<i32>,
} }
@ -745,12 +789,12 @@ impl Into<EquippedEntity> for PgEquipped {
EquippedEntity { EquippedEntity {
weapon: self.weapon.map(|i| ItemEntityId(i as u32)), weapon: self.weapon.map(|i| ItemEntityId(i as u32)),
armor: self.armor.map(|i| ItemEntityId(i as u32)), armor: self.armor.map(|i| ItemEntityId(i as u32)),
shield: self.shield.map(|i| ItemEntityId(i as u32)),
unit: [self.unit0.map(|i| ItemEntityId(i as u32)), unit: [self.unit0.map(|i| ItemEntityId(i as u32)),
self.unit1.map(|i| ItemEntityId(i as u32)), self.unit1.map(|i| ItemEntityId(i as u32)),
self.unit2.map(|i| ItemEntityId(i as u32)), self.unit2.map(|i| ItemEntityId(i as u32)),
self.unit3.map(|i| ItemEntityId(i as u32)), self.unit3.map(|i| ItemEntityId(i as u32)),
], ],
shield: self.shield.map(|i| ItemEntityId(i as u32)),
mag: self.mag.map(|i| ItemEntityId(i as u32)), mag: self.mag.map(|i| ItemEntityId(i as u32)),
} }
} }
@ -762,13 +806,12 @@ impl From<(CharacterEntityId, EquippedEntity)> for PgEquipped {
pchar: char_equips.0.0 as i32, pchar: char_equips.0.0 as i32,
weapon: char_equips.1.weapon.map(|i| i.0 as i32), weapon: char_equips.1.weapon.map(|i| i.0 as i32),
armor: char_equips.1.armor.map(|i| i.0 as i32), armor: char_equips.1.armor.map(|i| i.0 as i32),
shield: char_equips.1.shield.map(|i| i.0 as i32),
unit0: char_equips.1.unit[0].map(|i| i.0 as i32), unit0: char_equips.1.unit[0].map(|i| i.0 as i32),
unit1: char_equips.1.unit[1].map(|i| i.0 as i32), unit1: char_equips.1.unit[1].map(|i| i.0 as i32),
unit2: char_equips.1.unit[2].map(|i| i.0 as i32), unit2: char_equips.1.unit[2].map(|i| i.0 as i32),
unit3: char_equips.1.unit[3].map(|i| i.0 as i32), unit3: char_equips.1.unit[3].map(|i| i.0 as i32),
shield: char_equips.1.shield.map(|i| i.0 as i32),
mag: char_equips.1.mag.map(|i| i.0 as i32), mag: char_equips.1.mag.map(|i| i.0 as i32),
} }
} }
} }

View File

@ -65,6 +65,74 @@ impl PostgresGateway {
ItemDetail::Weapon(weapon) ItemDetail::Weapon(weapon)
}, },
ItemDetail::ESWeapon(mut esweapon) => {
let q = r#"select esweapon, modifier
from esweapon_modifier
where esweapon = $1
order by created_at"#;
let esweapon_modifiers = sqlx::query_as::<_, PgESWeaponModifier>(q)
.bind(id.0 as i32)
.fetch(&self.pool);
esweapon_modifiers.for_each(|modifier| {
if let Ok(modifier) = modifier {
esweapon.apply_modifier(&modifier.modifier);
}
}).await;
ItemDetail::ESWeapon(esweapon)
},
ItemDetail::Armor(mut armor) => {
let q = r#"select armor, modifier
from armor_modifier
where armor = $1
order by created_at"#;
let armor_modifiers = sqlx::query_as::<_, PgArmorModifier>(q)
.bind(id.0 as i32)
.fetch(&self.pool);
armor_modifiers.for_each(|modifier| {
if let Ok(modifier) = modifier {
armor.apply_modifier(&modifier.modifier);
}
}).await;
ItemDetail::Armor(armor)
},
ItemDetail::Unit(mut unit) => {
let q = r#"select unit, modifier
from unit_modifier
where unit = $1
order by created_at"#;
let unit_modifiers = sqlx::query_as::<_, PgUnitModifier>(q)
.bind(id.0 as i32)
.fetch(&self.pool);
unit_modifiers.for_each(|modifier| {
if let Ok(modifier) = modifier {
unit.apply_modifier(&modifier.modifier);
}
}).await;
ItemDetail::Unit(unit)
},
ItemDetail::Shield(mut shield) => {
let q = r#"select shield, modifier
from shield_modifier
where shield = $1
order by created_at"#;
let shield_modifiers = sqlx::query_as::<_, PgShieldModifier>(q)
.bind(id.0 as i32)
.fetch(&self.pool);
shield_modifiers.for_each(|modifier| {
if let Ok(modifier) = modifier {
shield.apply_modifier(&modifier.modifier);
}
}).await;
ItemDetail::Shield(shield)
},
ItemDetail::Mag(mut mag) => { ItemDetail::Mag(mut mag) => {
let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell
from mag_modifier from mag_modifier
@ -91,6 +159,8 @@ impl PostgresGateway {
mag::MagModifier::OwnerChange(class, section_id) => { mag::MagModifier::OwnerChange(class, section_id) => {
mag.change_owner(class, section_id) mag.change_owner(class, section_id)
}, },
mag::MagModifier::WrapPresent => {mag.apply_modifier(&modifier)},
mag::MagModifier::UnwrapPresent => {mag.apply_modifier(&modifier)},
} }
}).await; }).await;
@ -416,6 +486,62 @@ impl EntityGateway for PostgresGateway {
Ok(()) Ok(())
} }
async fn add_esweapon_modifier(&mut self, item_id: &ItemEntityId, modifier: esweapon::ESWeaponModifier) -> Result<(), GatewayError> {
sqlx::query("insert into esweapon_modifier (esweapon, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
async fn add_armor_modifier(&mut self, item_id: &ItemEntityId, modifier: armor::ArmorModifier) -> Result<(), GatewayError> {
sqlx::query("insert into armor_modifier (armor, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
async fn add_unit_modifier(&mut self, item_id: &ItemEntityId, modifier: unit::UnitModifier) -> Result<(), GatewayError> {
sqlx::query("insert into unit_modifier (unit, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
async fn add_shield_modifier(&mut self, item_id: &ItemEntityId, modifier: shield::ShieldModifier) -> Result<(), GatewayError> {
sqlx::query("insert into shield_modifier (shield, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
async fn add_mag_modifier(&mut self, item_id: &ItemEntityId, modifier: mag::MagModifier) -> Result<(), GatewayError> {
sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
async fn add_tech_modifier(&mut self, item_id: &ItemEntityId, modifier: tech::TechModifier) -> Result<(), GatewayError> {
sqlx::query("insert into tech_modifier (tech, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
async fn add_tool_modifier(&mut self, item_id: &ItemEntityId, modifier: tool::ToolModifier) -> Result<(), GatewayError> {
sqlx::query("insert into tool_modifier (tool, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
/* /*
async fn get_items_by_character(&self, char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> { async fn get_items_by_character(&self, char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> {
let q = r#"select * from ( let q = r#"select * from (
@ -540,7 +666,6 @@ impl EntityGateway for PostgresGateway {
} }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
sqlx::query("insert into inventory (pchar, items) values ($1, $2) on conflict (pchar) do update set items = $2") sqlx::query("insert into inventory (pchar, items) values ($1, $2) on conflict (pchar) do update set items = $2")
.bind(char_id.0) .bind(char_id.0)
.bind(sqlx::types::Json(inventory)) .bind(sqlx::types::Json(inventory))
@ -582,16 +707,16 @@ impl EntityGateway for PostgresGateway {
} }
async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> { async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> {
sqlx::query(r#"insert into equipped (pchar, weapon, armor, shield, unit0, unit1, unit2, unit3, mag) values ($1, $2, $3, $4, $5, $6, $7, $8, $9) sqlx::query(r#"insert into equipped (pchar, weapon, armor, unit0, unit1, unit2, unit3, shield, mag) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)
on conflict (pchar) do update set weapon=$2, armor=$3, shield=$4, unit0=$5, unit1=$6, unit2=$7, unit3=$8, mag=$9"#) on conflict (pchar) do update set weapon=$2, armor=$3, shield=$4, unit0=$5, unit1=$6, unit2=$7, unit3=$8, mag=$9"#)
.bind(char_id.0) .bind(char_id.0)
.bind(equips.weapon.map(|i| i.0 as i32)) .bind(equips.weapon.map(|i| i.0 as i32))
.bind(equips.armor.map(|i| i.0 as i32)) .bind(equips.armor.map(|i| i.0 as i32))
.bind(equips.shield.map(|i| i.0 as i32))
.bind(equips.unit[0].map(|i| i.0 as i32)) .bind(equips.unit[0].map(|i| i.0 as i32))
.bind(equips.unit[1].map(|i| i.0 as i32)) .bind(equips.unit[1].map(|i| i.0 as i32))
.bind(equips.unit[2].map(|i| i.0 as i32)) .bind(equips.unit[2].map(|i| i.0 as i32))
.bind(equips.unit[3].map(|i| i.0 as i32)) .bind(equips.unit[3].map(|i| i.0 as i32))
.bind(equips.shield.map(|i| i.0 as i32))
.bind(equips.mag.map(|i| i.0 as i32)) .bind(equips.mag.map(|i| i.0 as i32))
.execute(&self.pool) .execute(&self.pool)
.await?; .await?;

View File

@ -1,5 +1,5 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::ItemEntityId; use crate::entity::item::{ItemEntityId, WrappingPaper};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ItemParseError { pub enum ItemParseError {
@ -288,27 +288,31 @@ impl ArmorType {
} }
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ArmorModifier { pub enum ArmorModifier {
AddSlot { AddSlot {
addslot: ItemEntityId, addslot: ItemEntityId,
} },
WrapPresent,
UnwrapPresent,
} }
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Armor { pub struct Armor {
pub armor: ArmorType, pub armor: ArmorType,
pub dfp: u8, pub dfp: u8,
pub evp: u8, pub evp: u8,
pub slots: u8, pub slots: u8,
pub wrapping: Option<WrappingPaper>,
} }
impl Armor { impl Armor {
pub fn as_bytes(&self) -> [u8; 16] { pub fn as_bytes(&self) -> [u8; 16] {
let mut result = [0; 16]; let mut result = [0; 16];
result[0..3].copy_from_slice(&self.armor.value()); result[0..3].copy_from_slice(&self.armor.value());
if self.wrapping.is_some() {
result[4] += 0x40;
}
result[5] = self.slots; result[5] = self.slots;
result[6] = self.dfp; result[6] = self.dfp;
result[8] = self.evp; result[8] = self.evp;
@ -317,16 +321,32 @@ impl Armor {
pub fn from_bytes(data: [u8; 16]) -> Result<Armor, ItemParseError> { pub fn from_bytes(data: [u8; 16]) -> Result<Armor, ItemParseError> {
let a = ArmorType::parse_type([data[0], data[1], data[2]]); let a = ArmorType::parse_type([data[0], data[1], data[2]]);
let w = {
if data[4] & 0x40 == 0x40 {
WrappingPaper::from(0) // always use default wrapping paper to preserve slot info
} else {
None
}
};
if a.is_ok() { if a.is_ok() {
Ok(Armor { Ok(Armor {
armor: a.unwrap(), armor: a.unwrap(),
dfp: data[6], dfp: data[6],
evp: data[8], evp: data[8],
slots: data[5], slots: data[5],
wrapping: w,
}) })
} }
else { else {
Err(ItemParseError::InvalidArmorBytes) // TODO: error handling if wrong bytes are given Err(ItemParseError::InvalidArmorBytes) // TODO: error handling if wrong bytes are given
} }
} }
pub fn apply_modifier(&mut self, modifier: &ArmorModifier) {
match modifier {
ArmorModifier::WrapPresent => {self.wrapping = Some(WrappingPaper::WhitePink)},
ArmorModifier::UnwrapPresent => {self.wrapping = None},
_ => {},
}
}
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::{WrappingPaper};
// TODO: actually use this // TODO: actually use this
#[derive(Debug)] #[derive(Debug)]
pub enum ItemParseError { pub enum ItemParseError {
@ -169,12 +170,21 @@ impl ESWeaponSpecial {
} }
} }
#[derive(Debug, Serialize, Deserialize)]
pub enum ESWeaponModifier {
WrapPresent {
paper: WrappingPaper,
},
UnwrapPresent,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ESWeapon { pub struct ESWeapon {
pub esweapon: ESWeaponType, pub esweapon: ESWeaponType,
pub special: Option<ESWeaponSpecial>, pub special: Option<ESWeaponSpecial>,
pub name: String, pub name: String,
pub grind: u8, pub grind: u8,
pub wrapping: Option<WrappingPaper>
} }
impl ESWeapon { impl ESWeapon {
@ -184,6 +194,7 @@ impl ESWeapon {
special: None, special: None,
name: "".to_owned(), name: "".to_owned(),
grind: 0, grind: 0,
wrapping: None,
} }
} }
@ -241,7 +252,10 @@ impl ESWeapon {
result[1] = 0x70 + self.esweapon.to_value(); result[1] = 0x70 + self.esweapon.to_value();
result[2] = self.special.map(|s| s.to_value()).unwrap_or(0); result[2] = self.special.map(|s| s.to_value()).unwrap_or(0);
result[3] = self.grind; result[3] = self.grind;
//result[4] = tekked/untekked flag if self.wrapping.is_some() {
result[4] += 0x40;
result[5] = self.wrapping.unwrap().value();
}
result[6..12].clone_from_slice(&self.bytes_from_name()); result[6..12].clone_from_slice(&self.bytes_from_name());
result result
} }
@ -252,12 +266,28 @@ impl ESWeapon {
let special = ESWeaponSpecial::from_value(bytes[2]); let special = ESWeaponSpecial::from_value(bytes[2]);
let grind = bytes[3]; let grind = bytes[3];
let name = ESWeapon::name_from_bytes(&bytes[6..12]); let name = ESWeapon::name_from_bytes(&bytes[6..12]);
let wrapping = {
if bytes[4] & 0x40 == 0x40 {
WrappingPaper::from(bytes[5])
} else {
None
}
};
ESWeapon { ESWeapon {
esweapon: esweapon, esweapon: esweapon,
special: special.ok(), special: special.ok(),
grind: grind, grind: grind,
name: name, name: name,
wrapping: wrapping,
}
}
pub fn apply_modifier(&mut self, modifier: &ESWeaponModifier) {
match modifier {
ESWeaponModifier::WrapPresent{paper} => {self.wrapping = Some(*paper)},
ESWeaponModifier::UnwrapPresent => {self.wrapping = None},
_ => {},
} }
} }
} }
@ -276,6 +306,7 @@ mod test {
special: Some(ESWeaponSpecial::Berserk), special: Some(ESWeaponSpecial::Berserk),
grind: 137u8, grind: 137u8,
name: "JAKESERV".to_owned(), name: "JAKESERV".to_owned(),
wrapping: None,
}); });
} }
@ -287,6 +318,7 @@ mod test {
special: Some(ESWeaponSpecial::Chaos), special: Some(ESWeaponSpecial::Chaos),
grind: 72u8, grind: 72u8,
name: "PSYCHO".to_owned(), name: "PSYCHO".to_owned(),
wrapping: None,
}; };
let bytes = testweapon.as_bytes(); let bytes = testweapon.as_bytes();
assert_eq!(bytes, [0x00, 0x7B, 0x09, 0x48, 0x00, 0x00, 0x82, 0x13, 0xE4, 0x68, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00]); assert_eq!(bytes, [0x00, 0x7B, 0x09, 0x48, 0x00, 0x00, 0x82, 0x13, 0xE4, 0x68, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00]);
@ -300,10 +332,9 @@ mod test {
special: Some(ESWeaponSpecial::Spirit), special: Some(ESWeaponSpecial::Spirit),
grind: 105u8, grind: 105u8,
name: "YUGIOH".to_owned(), name: "YUGIOH".to_owned(),
wrapping: None,
}; };
let bytes = testweapon.as_bytes(); let bytes = testweapon.as_bytes();
assert_eq!(bytes, [0x00, 0xA7, 0x0B, 0x69, 0x00, 0x00, 0x83, 0x35, 0x9D, 0x2F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00]); assert_eq!(bytes, [0x00, 0xA7, 0x0B, 0x69, 0x00, 0x00, 0x83, 0x35, 0x9D, 0x2F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00]);
} }
} }

View File

@ -2,7 +2,7 @@ use std::collections::HashMap;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::tool::ToolType; use crate::entity::item::tool::ToolType;
use crate::entity::character::{CharacterClass, SectionID}; use crate::entity::character::{CharacterClass, SectionID};
use crate::entity::item::ItemEntityId; use crate::entity::item::{ItemEntityId, WrappingPaper};
use std::io::Read; use std::io::Read;
use std::cmp::Ordering::{Less, Greater, Equal}; use std::cmp::Ordering::{Less, Greater, Equal};
@ -56,8 +56,6 @@ lazy_static::lazy_static! {
}; };
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ItemParseError { pub enum ItemParseError {
InvalidMagType, InvalidMagType,
@ -509,14 +507,16 @@ impl MagAttributeOrdering {
} }
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Serialize)]
pub enum MagModifier { pub enum MagModifier {
FeedMag{ FeedMag{
food: ItemEntityId, food: ItemEntityId,
}, },
BankMag, // when putting a mag in the bank it truncates the values which has applications when raising degenerate mags BankMag, // when putting a mag in the bank it truncates the values which has applications when raising degenerate mags
MagCell(ItemEntityId), MagCell(ItemEntityId),
OwnerChange(CharacterClass, SectionID) OwnerChange(CharacterClass, SectionID),
WrapPresent,
UnwrapPresent,
} }
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, enum_utils::FromStr)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, enum_utils::FromStr)]
@ -529,7 +529,7 @@ pub enum PhotonBlast {
MyllaYoulla, MyllaYoulla,
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Mag { pub struct Mag {
pub mag: MagType, pub mag: MagType,
def: u16, def: u16,
@ -543,9 +543,9 @@ pub struct Mag {
//modifiers: Vec<MagModifier>, //modifiers: Vec<MagModifier>,
pub class: CharacterClass, pub class: CharacterClass,
pub id: SectionID, pub id: SectionID,
pub wrapping: Option<WrappingPaper>,
} }
impl Mag { impl Mag {
pub fn baby_mag(skin: u16) -> Mag { pub fn baby_mag(skin: u16) -> Mag {
Mag { Mag {
@ -561,6 +561,25 @@ impl Mag {
//modifiers: Vec::new(), //modifiers: Vec::new(),
class: CharacterClass::HUmar, class: CharacterClass::HUmar,
id: SectionID::Viridia, id: SectionID::Viridia,
wrapping: None,
}
}
pub fn wrapped_baby_mag(skin: u16) -> Mag {
Mag {
mag: MagType::Mag,
def: 500,
pow: 0,
dex: 0,
mnd: 0,
synchro: 20,
iq: 0,
photon_blast: [None; 3],
color: (skin % 18) as u8,
//modifiers: Vec::new(),
class: CharacterClass::HUmar,
id: SectionID::Viridia,
wrapping: WrappingPaper::from((skin % 10) as u8),
} }
} }
@ -575,6 +594,9 @@ impl Mag {
result[12] = self.synchro; result[12] = self.synchro;
result[13] = self.iq; result[13] = self.iq;
result[14] = self.photon_blast_count(); result[14] = self.photon_blast_count();
if self.wrapping.is_some() {
result[14] += 0x40;
}
result[15] = self.color; result[15] = self.color;
result result
} }
@ -648,6 +670,13 @@ impl Mag {
let sync = data[12] % 121; // TODO: handle invalid values. let sync = data[12] % 121; // TODO: handle invalid values.
let iq = data[13] % 201; // TODO: handle invalid values. let iq = data[13] % 201; // TODO: handle invalid values.
let wp = {
if data[14] & 0x40 == 0x40 {
WrappingPaper::from(data[15] % 10) // % 10 to have valid wrapping paper colour.
} else {
None
}
};
Ok(Mag{ Ok(Mag{
mag: m.unwrap(), mag: m.unwrap(),
@ -660,8 +689,9 @@ impl Mag {
photon_blast: [None, None, None], // TODO: actually get PBs from bytes photon_blast: [None, None, None], // TODO: actually get PBs from bytes
color: data[15] % 18, color: data[15] % 18,
//modifiers: Vec::new(), //modifiers: Vec::new(),
class: CharacterClass::HUmar, class: CharacterClass::HUmar, // TODO: determine character class
id: SectionID::Viridia, id: SectionID::Viridia, // TODO: determine section id
wrapping: wp,
}) })
} }
else { else {
@ -1097,6 +1127,14 @@ impl Mag {
MagCell::LibertaKit => MagType::Agastya, MagCell::LibertaKit => MagType::Agastya,
} }
} }
pub fn apply_modifier(&mut self, modifier: &MagModifier) {
match modifier {
MagModifier::WrapPresent => {self.wrapping = WrappingPaper::from(self.color % 10)}, // prevents mag color from crashing wrapping papers. client always shows mags in default paper colour ?
MagModifier::UnwrapPresent => {self.wrapping = None},
_ => {}, // TODO: other modifiers are already handled elsewhere. do they need to be moved here?
}
}
} }
@ -1167,6 +1205,7 @@ mod test {
color: 0, color: 0,
class: CharacterClass::FOmarl, class: CharacterClass::FOmarl,
id: SectionID::Whitill, id: SectionID::Whitill,
wrapping: None,
}); });
} }
@ -1174,4 +1213,3 @@ mod test {
fn test_mag_does_not_level_down() { fn test_mag_does_not_level_down() {
} }
} }

View File

@ -69,7 +69,6 @@ impl Meseta {
} }
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ItemType { pub enum ItemType {
Weapon(weapon::WeaponType), Weapon(weapon::WeaponType),
@ -159,6 +158,34 @@ impl ItemDetail {
_ => None, _ => None,
} }
} }
pub fn is_wrapped(&mut self) -> bool {
match self {
ItemDetail::Weapon(w) => w.wrapping.is_some(),
ItemDetail::Armor(a) => a.wrapping.is_some(),
ItemDetail::Shield(s) => s.wrapping.is_some(),
ItemDetail::Unit(u) => u.wrapping.is_some(),
ItemDetail::Tool(t) => t.wrapping.is_some(),
ItemDetail::TechniqueDisk(d) => d.wrapping.is_some(),
ItemDetail::Mag(m) => m.wrapping.is_some(),
ItemDetail::ESWeapon(e) => e.wrapping.is_some(),
_ => unreachable!(),
}
}
pub fn unwrap_present(&mut self) {
match self {
ItemDetail::Weapon(ref mut w) => w.wrapping = None,
ItemDetail::Armor(ref mut a) => a.wrapping = None,
ItemDetail::Shield(ref mut s) => s.wrapping = None,
ItemDetail::Unit(ref mut u) => u.wrapping = None,
ItemDetail::Tool(ref mut t) => t.wrapping = None,
ItemDetail::TechniqueDisk(ref mut d) => d.wrapping = None,
ItemDetail::Mag(ref mut m) => m.wrapping = None,
ItemDetail::ESWeapon(ref mut e) => e.wrapping = None,
_ => unreachable!(),
};
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -200,7 +227,7 @@ impl InventoryItemEntity {
_ => self, _ => self,
} }
} }
//pub fn with_individual<T>(&self, func: fn(&ItemEntity) -> T) -> Option<T> {
pub fn with_individual<F: Fn(&ItemEntity) -> T, T>(&self, func: F) -> Option<T> { pub fn with_individual<F: Fn(&ItemEntity) -> T, T>(&self, func: F) -> Option<T> {
match self { match self {
InventoryItemEntity::Individual(item) => Some(func(item)), InventoryItemEntity::Individual(item) => Some(func(item)),
@ -208,7 +235,6 @@ impl InventoryItemEntity {
} }
} }
//pub fn with_stacked<T>(&self, func: fn(&Vec<ItemEntity>) -> T) -> Option<T> {
pub fn with_stacked<F: Fn(&Vec<ItemEntity>) -> T, T>(&self, func: F) -> Option<T> { pub fn with_stacked<F: Fn(&Vec<ItemEntity>) -> T, T>(&self, func: F) -> Option<T> {
match self { match self {
InventoryItemEntity::Stacked(items) => Some(func(items)), InventoryItemEntity::Stacked(items) => Some(func(items)),
@ -252,7 +278,6 @@ impl InventoryEntity {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum BankItemEntity { pub enum BankItemEntity {
Individual(ItemEntity), Individual(ItemEntity),
@ -287,10 +312,8 @@ impl BankItemEntity {
} }
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct BankEntity { pub struct BankEntity {
//pub items: [Option<CharacterBankItem>; 30],
pub items: Vec<BankItemEntity>, pub items: Vec<BankItemEntity>,
} }
@ -301,3 +324,41 @@ impl BankEntity {
} }
} }
} }
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum WrappingPaper {
WhitePink, // 0
YellowBlue, // 1
BlackYellow, // 2
LightBlueOrange, // 3
PinkYellowGreen, // 4
RedGreen, // 5
Magenta, // 6
Blue, // 7
Yellow, // 8
Vermillion, // 9
Green, // 10
}
impl WrappingPaper {
pub fn value(&self) -> u8 {
*self as u8
}
pub fn from(data: u8) -> Option<WrappingPaper> {
match data {
0 => Some(WrappingPaper::WhitePink),
1 => Some(WrappingPaper::YellowBlue),
2 => Some(WrappingPaper::BlackYellow),
3 => Some(WrappingPaper::LightBlueOrange),
4 => Some(WrappingPaper::PinkYellowGreen),
5 => Some(WrappingPaper::RedGreen),
6 => Some(WrappingPaper::Magenta),
7 => Some(WrappingPaper::Blue),
8 => Some(WrappingPaper::Yellow),
9 => Some(WrappingPaper::Vermillion),
10 => Some(WrappingPaper::Green),
_ => None,
}
}
}

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::{WrappingPaper};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ItemParseError { pub enum ItemParseError {
@ -518,18 +519,30 @@ impl ShieldType {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum ShieldModifier {
WrapPresent {
paper: WrappingPaper,
},
UnwrapPresent,
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct Shield { pub struct Shield {
pub shield: ShieldType, pub shield: ShieldType,
pub dfp: u8, pub dfp: u8,
pub evp: u8, pub evp: u8,
pub wrapping: Option<WrappingPaper>,
} }
impl Shield { impl Shield {
pub fn as_bytes(&self) -> [u8; 16] { pub fn as_bytes(&self) -> [u8; 16] {
let mut result = [0; 16]; let mut result = [0; 16];
result[0..3].copy_from_slice(&self.shield.value()); result[0..3].copy_from_slice(&self.shield.value());
if self.wrapping.is_some() {
result[4] += 0x40;
result[5] = self.wrapping.unwrap().value();
};
result[6] = self.dfp; result[6] = self.dfp;
result[8] = self.evp; result[8] = self.evp;
result result
@ -537,15 +550,30 @@ impl Shield {
pub fn from_bytes(data: [u8; 16]) -> Result<Shield, ItemParseError> { pub fn from_bytes(data: [u8; 16]) -> Result<Shield, ItemParseError> {
let s = ShieldType::parse_type([data[0], data[1], data[2]]); let s = ShieldType::parse_type([data[0], data[1], data[2]]);
let wrapping = {
if data[4] & 0x40 == 0x40 {
WrappingPaper::from(data[5])
} else {
None
}
};
if s.is_ok() { if s.is_ok() {
Ok(Shield{ Ok(Shield{
shield: s.unwrap(), shield: s.unwrap(),
dfp: data[6], dfp: data[6],
evp: data[8], evp: data[8],
wrapping: wrapping,
}) })
} }
else { else {
Err(ItemParseError::InvalidShieldBytes) // TODO: error handling if wrong bytes are given Err(ItemParseError::InvalidShieldBytes) // TODO: error handling if wrong bytes are given
} }
} }
pub fn apply_modifier(&mut self, modifier: &ShieldModifier) {
match modifier {
ShieldModifier::WrapPresent{paper} => {self.wrapping = Some(*paper)}, // TODO: client always shows shields in default paper colour ?
ShieldModifier::UnwrapPresent => {self.wrapping = None},
}
}
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::{WrappingPaper};
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, enum_utils::FromStr, derive_more::Display, strum_macros::EnumIter)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, enum_utils::FromStr, derive_more::Display, strum_macros::EnumIter)]
@ -75,10 +76,19 @@ impl Technique {
} }
} }
#[derive(Debug, Serialize)]
pub enum TechModifier {
WrapPresent {
paper: WrappingPaper,
},
UnwrapPresent,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TechniqueDisk { pub struct TechniqueDisk {
pub tech: Technique, pub tech: Technique,
pub level: u32, pub level: u32,
pub wrapping: Option<WrappingPaper>, // TODO: validate if this clobbers tech value?
} }
impl TechniqueDisk { impl TechniqueDisk {
@ -88,6 +98,17 @@ impl TechniqueDisk {
result[1] = 2; result[1] = 2;
result[2] = self.level as u8 - 1; result[2] = self.level as u8 - 1;
result[4] = self.tech.as_value(); result[4] = self.tech.as_value();
if self.wrapping.is_some() {
result[4] += 0x40;
result[5] = self.wrapping.unwrap().value();
};
result result
} }
pub fn apply_modifier(&mut self, modifier: &TechModifier) {
match modifier {
TechModifier::WrapPresent{paper} => {self.wrapping = Some(*paper)},
TechModifier::UnwrapPresent => {self.wrapping = None},
}
}
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::{WrappingPaper};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ItemParseError { pub enum ItemParseError {
@ -647,10 +648,18 @@ impl ToolType {
} }
} }
#[derive(Debug, Serialize)]
pub enum ToolModifier {
WrapPresent {
paper: WrappingPaper,
},
UnwrapPresent,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Tool { pub struct Tool {
pub tool: ToolType, pub tool: ToolType,
pub wrapping: Option<WrappingPaper>, // TODO: what happens if a present is N monomates and the inventory already has 10?
} }
impl Tool { impl Tool {
@ -668,10 +677,18 @@ impl Tool {
} }
pub fn from_bytes(data: [u8; 16]) -> Result<Tool, ItemParseError> { pub fn from_bytes(data: [u8; 16]) -> Result<Tool, ItemParseError> {
let t = ToolType::parse_type([data[0], data[1], data[2]]); let t = ToolType::parse_type([data[0], data[1], data[2]]);
let w = {
if data[4] & 0x40 == 0x40 {
WrappingPaper::from(data[5])
} else {
None
}
};
if t.is_ok() { if t.is_ok() {
Ok(Tool { Ok(Tool {
tool: t.unwrap(), tool: t.unwrap(),
wrapping: w,
}) })
} }
else { else {
@ -686,4 +703,11 @@ impl Tool {
pub fn max_stack(&self) -> usize { pub fn max_stack(&self) -> usize {
self.tool.max_stack() self.tool.max_stack()
} }
pub fn apply_modifier(&mut self, modifier: &ToolModifier) {
match modifier {
ToolModifier::WrapPresent{paper} => {self.wrapping = Some(*paper)},
ToolModifier::UnwrapPresent => {self.wrapping = None},
}
}
} }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::{WrappingPaper};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ItemParseError { pub enum ItemParseError {
@ -329,15 +330,19 @@ pub enum UnitModifier {
Plus, Plus,
Minus, Minus,
MinusMinus, MinusMinus,
WrapPresent {
paper: WrappingPaper,
},
UnwrapPresent,
} }
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct Unit { pub struct Unit {
pub unit: UnitType, pub unit: UnitType,
pub modifier: Option<UnitModifier>, pub modifier: Option<UnitModifier>,
pub wrapping: Option<WrappingPaper>,
} }
impl Unit { impl Unit {
pub fn as_bytes(&self) -> [u8; 16] { pub fn as_bytes(&self) -> [u8; 16] {
let mut result = [0; 16]; let mut result = [0; 16];
@ -359,8 +364,15 @@ impl Unit {
result[6] = 0xFE; result[6] = 0xFE;
result[7] = 0xFF; result[7] = 0xFF;
}, },
_ => {}, // handling wrapping here is weird?
} }
} }
if self.wrapping.is_some() {
result[4] += 0x40;
result[5] = self.wrapping.unwrap().value();
}
result result
} }
@ -375,13 +387,30 @@ impl Unit {
_ => None, _ => None,
}; };
let w = {
if data[4] & 0x40 == 0x40 {
WrappingPaper::from(data[5])
} else {
None
}
};
Ok(Unit{ Ok(Unit{
unit: u.unwrap(), unit: u.unwrap(),
modifier: m, modifier: m,
wrapping: w,
}) })
} }
else { else {
Err(ItemParseError::InvalidUnitBytes) // TODO: error handling if wrong bytes are given Err(ItemParseError::InvalidUnitBytes) // TODO: error handling if wrong bytes are given
} }
} }
pub fn apply_modifier(&mut self, modifier: &UnitModifier) {
match modifier {
UnitModifier::WrapPresent{paper} => {self.wrapping = Some(*paper)},
UnitModifier::UnwrapPresent => {self.wrapping = None},
_ => {},
}
}
} }

View File

@ -1,4 +1,4 @@
use crate::entity::item::ItemEntityId; use crate::entity::item::{ItemEntityId, WrappingPaper};
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -44,7 +44,6 @@ impl WeaponAttribute {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, strum_macros::EnumIter)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, strum_macros::EnumIter)]
pub enum WeaponSpecial { pub enum WeaponSpecial {
Draw = 1, Draw = 1,
@ -1420,7 +1419,6 @@ impl WeaponType {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum TekSpecialModifier { pub enum TekSpecialModifier {
Plus, Plus,
@ -1452,18 +1450,22 @@ pub enum WeaponModifier {
percent: TekPercentModifier, percent: TekPercentModifier,
grind: i32, grind: i32,
}, },
WrapPresent {
paper: WrappingPaper,
},
UnwrapPresent,
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Weapon { pub struct Weapon {
pub weapon: WeaponType, pub weapon: WeaponType,
pub special: Option<WeaponSpecial>, pub special: Option<WeaponSpecial>,
pub grind: u8, pub grind: u8,
pub attrs: [Option<WeaponAttribute>; 3], pub attrs: [Option<WeaponAttribute>; 3],
pub tekked: bool, pub tekked: bool,
pub wrapping: Option<WrappingPaper>,
} }
impl Weapon { impl Weapon {
pub fn new(wep: WeaponType) -> Weapon { pub fn new(wep: WeaponType) -> Weapon {
Weapon { Weapon {
@ -1472,6 +1474,7 @@ impl Weapon {
grind: 0, grind: 0,
attrs: [None; 3], attrs: [None; 3],
tekked: true, tekked: true,
wrapping: None,
} }
} }
@ -1516,15 +1519,30 @@ impl Weapon {
self.grind = std::cmp::max(self.grind as i32 + grind, 0) as u8; self.grind = std::cmp::max(self.grind as i32 + grind, 0) as u8;
self.tekked = true; self.tekked = true;
}, },
WeaponModifier::WrapPresent{paper} => {self.wrapping = Some(*paper)},
WeaponModifier::UnwrapPresent => {self.wrapping = None},
_ => {} _ => {}
} }
} }
pub fn wrap_present(&mut self, value: u8) {
self.wrapping = WrappingPaper::from(value);
}
pub fn unwrap_present(&mut self) {
self.wrapping = None;
}
pub fn as_bytes(&self) -> [u8; 16] { pub fn as_bytes(&self) -> [u8; 16] {
let mut result = [0u8; 16]; let mut result = [0u8; 16];
result[0..3].copy_from_slice(&self.weapon.value()); result[0..3].copy_from_slice(&self.weapon.value());
result[3] = self.grind; result[3] = self.grind;
result[4] = self.special.map(|s| s.value()).unwrap_or(0); result[4] = self.special.map(|s| s.value()).unwrap_or(0);
if self.wrapping.is_some() {
result[4] += 0x40;
result[5] = self.wrapping.unwrap().value();
};
if self.tekked == false { if self.tekked == false {
result[4] += 0x80 result[4] += 0x80
@ -1545,17 +1563,18 @@ impl Weapon {
let mut t = true; let mut t = true;
let g = data[3]; let g = data[3];
if data[4] >= 0x81 && data[4] <= 0xA8 { if data[4] & 0x80 == 0x80 {
s = WeaponSpecial::from(data[4] - 0x80); s = WeaponSpecial::from(data[4] - 0x80);
t = false; t = false;
} }
else if data[4] >= 0x01 && data[4] <= 0x28 {
s = WeaponSpecial::from(data[4]); let p = {
t = true; if data[4] & 0x40 == 0x40 {
} WrappingPaper::from(data[5])
// else { } else {
// return Err(ItemParseError::InvalidSpecial) None
// } }
};
let mut a = [ let mut a = [
None, None,
@ -1585,6 +1604,7 @@ impl Weapon {
a[2], a[2],
], ],
tekked: t, tekked: t,
wrapping: p,
}) })
} }
else { else {
@ -1592,5 +1612,3 @@ impl Weapon {
} }
} }
} }

View File

@ -220,6 +220,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
special: None, special: None,
attrs: [None; 3], attrs: [None; 3],
tekked: true, tekked: true,
wrapping: None,
}), }),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
@ -233,6 +234,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 0, slots: 0,
wrapping: None,
}), }),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
@ -260,6 +262,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
item: ItemDetail::Tool ( item: ItemDetail::Tool (
Tool { Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
}), }),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
@ -273,6 +276,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
item: ItemDetail::Tool ( item: ItemDetail::Tool (
Tool { Tool {
tool: item::tool::ToolType::Monofluid, tool: item::tool::ToolType::Monofluid,
wrapping: None,
}), }),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,

View File

@ -107,6 +107,7 @@ impl GenericArmorTable {
dfp: dfp_modifier as u8, dfp: dfp_modifier as u8,
evp: evp_modifier as u8, evp: evp_modifier as u8,
slots: slots as u8, slots: slots as u8,
wrapping: None,
})) }))
} }
} }
@ -126,24 +127,28 @@ mod test {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 1, slots: 1,
wrapping: None,
}))); })));
assert!(gat.get_drop(&MapArea::Caves3, &mut rng) == Some(ItemDropType::Armor(Armor { assert!(gat.get_drop(&MapArea::Caves3, &mut rng) == Some(ItemDropType::Armor(Armor {
armor: ArmorType::AbsorbArmor, armor: ArmorType::AbsorbArmor,
dfp: 1, dfp: 1,
evp: 1, evp: 1,
slots: 1, slots: 1,
wrapping: None,
}))); })));
assert!(gat.get_drop(&MapArea::Forest2, &mut rng) == Some(ItemDropType::Armor(Armor { assert!(gat.get_drop(&MapArea::Forest2, &mut rng) == Some(ItemDropType::Armor(Armor {
armor: ArmorType::HyperFrame, armor: ArmorType::HyperFrame,
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 0, slots: 0,
wrapping: None,
}))); })));
assert!(gat.get_drop(&MapArea::DarkFalz, &mut rng) == Some(ItemDropType::Armor(Armor { assert!(gat.get_drop(&MapArea::DarkFalz, &mut rng) == Some(ItemDropType::Armor(Armor {
armor: ArmorType::ImperialArmor, armor: ArmorType::ImperialArmor,
dfp: 2, dfp: 2,
evp: 1, evp: 1,
slots: 0, slots: 0,
wrapping: None,
}))); })));
} }
} }

View File

@ -86,6 +86,7 @@ impl GenericShieldTable {
shield: shield_type, shield: shield_type,
dfp: dfp_modifier as u8, dfp: dfp_modifier as u8,
evp: evp_modifier as u8, evp: evp_modifier as u8,
wrapping: None,
})) }))
} }
} }
@ -105,21 +106,25 @@ mod test {
shield: ShieldType::FreezeBarrier, shield: ShieldType::FreezeBarrier,
dfp: 4, dfp: 4,
evp: 1, evp: 1,
wrapping: None,
}))); })));
assert!(gst.get_drop(&MapArea::Caves3, &mut rng) == Some(ItemDropType::Shield(Shield { assert!(gst.get_drop(&MapArea::Caves3, &mut rng) == Some(ItemDropType::Shield(Shield {
shield: ShieldType::PsychicBarrier, shield: ShieldType::PsychicBarrier,
dfp: 3, dfp: 3,
evp: 2, evp: 2,
wrapping: None,
}))); })));
assert!(gst.get_drop(&MapArea::Mines2, &mut rng) == Some(ItemDropType::Shield(Shield { assert!(gst.get_drop(&MapArea::Mines2, &mut rng) == Some(ItemDropType::Shield(Shield {
shield: ShieldType::ImperialBarrier, shield: ShieldType::ImperialBarrier,
dfp: 0, dfp: 0,
evp: 4, evp: 4,
wrapping: None,
}))); })));
assert!(gst.get_drop(&MapArea::DarkFalz, &mut rng) == Some(ItemDropType::Shield(Shield { assert!(gst.get_drop(&MapArea::DarkFalz, &mut rng) == Some(ItemDropType::Shield(Shield {
shield: ShieldType::DivinityBarrier, shield: ShieldType::DivinityBarrier,
dfp: 1, dfp: 1,
evp: 0, evp: 0,
wrapping: None,
}))); })));
} }
} }

View File

@ -89,6 +89,7 @@ impl GenericUnitTable {
ItemDropType::Unit(Unit { ItemDropType::Unit(Unit {
unit: unit_type, unit: unit_type,
modifier: unit_modifier, modifier: unit_modifier,
wrapping: None,
}) })
}) })
} }
@ -116,6 +117,7 @@ mod test {
assert!(gut.get_drop(&area, &mut rng) == Some(ItemDropType::Unit(Unit { assert!(gut.get_drop(&area, &mut rng) == Some(ItemDropType::Unit(Unit {
unit: unit, unit: unit,
modifier: umod, modifier: umod,
wrapping: None,
}))); })));
} }
} }

View File

@ -503,6 +503,7 @@ impl GenericWeaponTable {
grind: weapon_grind as u8, grind: weapon_grind as u8,
attrs: weapon_attributes, attrs: weapon_attributes,
tekked: weapon_special.is_none(), tekked: weapon_special.is_none(),
wrapping: None,
})) }))
} }
} }
@ -524,6 +525,7 @@ mod test {
grind: 0, grind: 0,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
}))); })));
let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Hard, SectionID::Skyly); let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Hard, SectionID::Skyly);
@ -533,6 +535,7 @@ mod test {
grind: 2, grind: 2,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
}))); })));
let gwt = GenericWeaponTable::new(Episode::One, Difficulty::VeryHard, SectionID::Skyly); let gwt = GenericWeaponTable::new(Episode::One, Difficulty::VeryHard, SectionID::Skyly);
@ -542,6 +545,7 @@ mod test {
grind: 0, grind: 0,
attrs: [None, None, None], attrs: [None, None, None],
tekked: false, tekked: false,
wrapping: None,
}))); })));
let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly); let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly);
@ -551,6 +555,7 @@ mod test {
grind: 0, grind: 0,
attrs: [Some(WeaponAttribute {attr: Attribute::ABeast, value: 30}), Some(WeaponAttribute {attr: Attribute::Dark, value: 30}), None], attrs: [Some(WeaponAttribute {attr: Attribute::ABeast, value: 30}), Some(WeaponAttribute {attr: Attribute::Dark, value: 30}), None],
tekked: true, tekked: true,
wrapping: None,
}))); })));
} }
} }

View File

@ -104,6 +104,7 @@ impl RareDropTable {
grind: 0, grind: 0,
attrs: self.attribute_table.generate_rare_attributes(map_area, rng), attrs: self.attribute_table.generate_rare_attributes(map_area, rng),
tekked: false, tekked: false,
wrapping: None,
}) })
}, },
@ -113,6 +114,7 @@ impl RareDropTable {
dfp: self.armor_stats.dfp_modifier(&armor, rng) as u8, dfp: self.armor_stats.dfp_modifier(&armor, rng) as u8,
evp: self.armor_stats.evp_modifier(&armor, rng) as u8, evp: self.armor_stats.evp_modifier(&armor, rng) as u8,
slots: self.armor_stats.slots(map_area, rng) as u8, slots: self.armor_stats.slots(map_area, rng) as u8,
wrapping: None,
}) })
}, },
RareDropItem::Shield(shield) => { RareDropItem::Shield(shield) => {
@ -120,17 +122,20 @@ impl RareDropTable {
shield: shield, shield: shield,
dfp: self.shield_stats.dfp_modifier(&shield, rng) as u8, dfp: self.shield_stats.dfp_modifier(&shield, rng) as u8,
evp: self.shield_stats.evp_modifier(&shield, rng) as u8, evp: self.shield_stats.evp_modifier(&shield, rng) as u8,
wrapping: None,
}) })
}, },
RareDropItem::Unit(unit) => { RareDropItem::Unit(unit) => {
ItemDropType::Unit(Unit { ItemDropType::Unit(Unit {
unit: unit, unit: unit,
modifier: None, modifier: None,
wrapping: None,
}) })
}, },
RareDropItem::Tool(tool) => { RareDropItem::Tool(tool) => {
ItemDropType::Tool(Tool { ItemDropType::Tool(Tool {
tool: tool, tool: tool,
wrapping: None,
}) })
}, },
RareDropItem::Mag(_mag) => { RareDropItem::Mag(_mag) => {

View File

@ -103,7 +103,8 @@ impl TechniqueTable {
Some(ItemDropType::TechniqueDisk(TechniqueDisk { Some(ItemDropType::TechniqueDisk(TechniqueDisk {
tech: *tech, tech: *tech,
level: level as u32 level: level as u32,
wrapping: None,
})) }))
} }
} }
@ -127,7 +128,8 @@ mod test {
assert!(tt.get_drop(&area, &mut rng) == Some(ItemDropType::TechniqueDisk( assert!(tt.get_drop(&area, &mut rng) == Some(ItemDropType::TechniqueDisk(
TechniqueDisk { TechniqueDisk {
tech: tech, tech: tech,
level: level level: level,
wrapping: None,
}))); })));
} }
} }

View File

@ -158,7 +158,8 @@ impl ToolTable {
}; };
Some(ItemDropType::Tool(Tool { Some(ItemDropType::Tool(Tool {
tool: tool_type tool: tool_type,
wrapping: None,
})) }))
} }
} }

View File

@ -3,9 +3,14 @@ use thiserror::Error;
use libpso::character::character;//::InventoryItem; use libpso::character::character;//::InventoryItem;
use crate::entity::character::CharacterEntityId; use crate::entity::character::CharacterEntityId;
use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, ItemType, ItemLocation, InventoryEntity, InventoryItemEntity, EquippedEntity}; use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, ItemType, ItemLocation, InventoryEntity, InventoryItemEntity, EquippedEntity};
use crate::entity::item::tool::Tool;
use crate::entity::item::mag::Mag;
use crate::entity::item::weapon::Weapon; use crate::entity::item::weapon::Weapon;
use crate::entity::item::esweapon::ESWeapon;
use crate::entity::item::armor::Armor; // TODO: cleanup uses
use crate::entity::item::unit::Unit;
use crate::entity::item::shield::Shield;
use crate::entity::item::mag::Mag;
use crate::entity::item::tech::TechniqueDisk;
use crate::entity::item::tool::Tool;
use crate::ship::items::{ClientItemId, BankItem, BankItemHandle}; use crate::ship::items::{ClientItemId, BankItem, BankItemHandle};
use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem}; use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem};
@ -38,12 +43,69 @@ impl IndividualInventoryItem {
} }
} }
pub fn weapon_mut(&mut self) -> Option<&mut Weapon> {
match self.item {
ItemDetail::Weapon(ref mut weapon) => Some(weapon),
_ => None
}
}
pub fn esweapon_mut(&mut self) -> Option<&mut ESWeapon> {
match self.item {
ItemDetail::ESWeapon(ref mut esweapon) => Some(esweapon),
_ => None
}
}
pub fn armor_mut(&mut self) -> Option<&mut Armor> {
match self.item {
ItemDetail::Armor(ref mut armor) => Some(armor),
_ => None
}
}
pub fn unit_mut(&mut self) -> Option<&mut Unit> {
match self.item {
ItemDetail::Unit(ref mut unit) => Some(unit),
_ => None
}
}
pub fn shield_mut(&mut self) -> Option<&mut Shield> {
match self.item {
ItemDetail::Shield(ref mut shield) => Some(shield),
_ => None
}
}
pub fn mag_mut(&mut self) -> Option<&mut Mag> { pub fn mag_mut(&mut self) -> Option<&mut Mag> {
match self.item { match self.item {
ItemDetail::Mag(ref mut mag) => Some(mag), ItemDetail::Mag(ref mut mag) => Some(mag),
_ => None _ => None
} }
} }
pub fn tech_mut(&mut self) -> Option<&mut TechniqueDisk> {
match self.item {
ItemDetail::TechniqueDisk(ref mut tech) => Some(tech),
_ => None
}
}
pub fn tool_mut(&mut self) -> Option<&mut Tool> {
match self.item {
ItemDetail::Tool(ref mut tool) => Some(tool),
_ => None
}
}
pub fn is_wrapped(&mut self) -> bool {
self.item.is_wrapped()
}
pub fn unwrap_present(&mut self) {
self.item.unwrap_present();
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -66,6 +128,14 @@ impl StackedInventoryItem {
None None
} }
} }
pub fn is_wrapped(&self) -> bool {
self.tool.wrapping.is_some()
}
pub fn unwrap_present(&mut self) {
self.tool.wrapping = None;
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -127,7 +197,7 @@ impl InventoryItem {
} }
} }
// TOOD: delete? // TODO: delete?
pub fn are_same_stackable_tool(&self, other_stacked_item: &StackedFloorItem) -> bool { pub fn are_same_stackable_tool(&self, other_stacked_item: &StackedFloorItem) -> bool {
match self { match self {
InventoryItem::Stacked(self_stacked_item) => { InventoryItem::Stacked(self_stacked_item) => {
@ -138,7 +208,7 @@ impl InventoryItem {
} }
} }
// TOOD: delete? // TODO: delete?
pub fn can_combine_stacks(&self, other_stacked_item: &StackedFloorItem) -> bool { pub fn can_combine_stacks(&self, other_stacked_item: &StackedFloorItem) -> bool {
match self { match self {
InventoryItem::Stacked(self_stacked_item) => { InventoryItem::Stacked(self_stacked_item) => {
@ -151,7 +221,7 @@ impl InventoryItem {
} }
// TODO: result // TODO: result
// TOOD: delete? // TODO: delete?
pub fn combine_stacks(&mut self, other_stacked_item: &mut StackedFloorItem) { pub fn combine_stacks(&mut self, other_stacked_item: &mut StackedFloorItem) {
match self { match self {
InventoryItem::Stacked(self_stacked_item) => { InventoryItem::Stacked(self_stacked_item) => {
@ -167,13 +237,13 @@ impl InventoryItem {
InventoryItem::Individual(item) => { InventoryItem::Individual(item) => {
match &item.item { match &item.item {
ItemDetail::Weapon(w) => w.as_bytes(), ItemDetail::Weapon(w) => w.as_bytes(),
ItemDetail::Armor(a) => a.as_bytes(),
ItemDetail::Shield(s) => s.as_bytes(),
ItemDetail::Unit(u) => u.as_bytes(),
ItemDetail::Tool(t) => t.as_individual_bytes(),
ItemDetail::TechniqueDisk(d) => d.as_bytes(),
ItemDetail::Mag(m) => m.as_bytes(),
ItemDetail::ESWeapon(e) => e.as_bytes(), ItemDetail::ESWeapon(e) => e.as_bytes(),
ItemDetail::Armor(a) => a.as_bytes(),
ItemDetail::Unit(u) => u.as_bytes(),
ItemDetail::Shield(s) => s.as_bytes(),
ItemDetail::Mag(m) => m.as_bytes(),
ItemDetail::TechniqueDisk(d) => d.as_bytes(),
ItemDetail::Tool(t) => t.as_individual_bytes(),
} }
}, },
InventoryItem::Stacked(item) => { InventoryItem::Stacked(item) => {
@ -219,6 +289,41 @@ impl InventoryItem {
_ => None _ => None
} }
} }
pub fn item_detail(&self) -> ItemDetail {
match self {
InventoryItem::Individual(individual_inventory_item) => individual_inventory_item.item.clone(),
InventoryItem::Stacked(stacked_inventory_item) => ItemDetail::Tool(stacked_inventory_item.tool),
}
}
pub fn as_consumed_item(&self) -> ConsumedItem {
match self {
InventoryItem::Individual(individual_inventory_item) => {ConsumedItem::Individual(IndividualConsumedItem {
entity_id: individual_inventory_item.entity_id,
item: individual_inventory_item.item.clone(),
})},
InventoryItem::Stacked(stacked_inventory_item) => {ConsumedItem::Stacked(StackedConsumedItem {
entity_ids: stacked_inventory_item.entity_ids.clone(),
tool: stacked_inventory_item.tool,
})},
}
}
// TODO: validate if wrapping stacked items & tools are valid in the client. Gallons shop allows users to wrap tools (eg techs) but then the client may not be able to unwrap it afterwards?
pub fn is_wrapped(&mut self) -> bool {
match self {
InventoryItem::Individual(i) => i.is_wrapped(),
InventoryItem::Stacked(s) => s.is_wrapped(),
}
}
pub fn unwrap_present(&mut self) {
match self {
InventoryItem::Individual(i) => i.unwrap_present(),
InventoryItem::Stacked(s) => s.unwrap_present(),
};
}
} }

View File

@ -4,10 +4,9 @@ use thiserror::Error;
use crate::entity::gateway::EntityGateway; use crate::entity::gateway::EntityGateway;
use crate::entity::character::{CharacterEntity, CharacterEntityId, TechLevel}; use crate::entity::character::{CharacterEntity, CharacterEntityId, TechLevel};
use crate::entity::item::{ItemDetail, ItemLocation, BankName}; use crate::entity::item::{ItemDetail, ItemLocation, BankName};
use crate::entity::item::{Meseta, NewItemEntity, ItemEntity, InventoryItemEntity, EquippedEntity, InventoryEntity, BankItemEntity, BankEntity}; use crate::entity::item::{Meseta, NewItemEntity, ItemEntity, InventoryItemEntity, EquippedEntity, InventoryEntity, BankItemEntity, BankEntity, ItemType};
use crate::entity::item::tool::{Tool, ToolType}; use crate::entity::item::tool::{Tool, ToolType};
use crate::entity::item::unit; use crate::entity::item::{unit, weapon, armor, shield, mag, tech, tool, esweapon};
use crate::entity::item::weapon;
use crate::ship::map::MapArea; use crate::ship::map::MapArea;
use crate::ship::ship::ItemDropLocation; use crate::ship::ship::ItemDropLocation;
use crate::ship::drops::{ItemDrop, ItemDropType}; use crate::ship::drops::{ItemDrop, ItemDropType};
@ -503,7 +502,6 @@ impl ItemManager {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
let used_item = inventory.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?; let used_item = inventory.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
let consumed_item = used_item.consume(amount)?; let consumed_item = used_item.consume(amount)?;
if let ItemDetail::TechniqueDisk(tech_disk) = consumed_item.item() { if let ItemDetail::TechniqueDisk(tech_disk) = consumed_item.item() {
// TODO: validate tech level in packet is in bounds [1..30] // TODO: validate tech level in packet is in bounds [1..30]
character.techs.set_tech(tech_disk.tech, TechLevel(tech_disk.level as u8)); character.techs.set_tech(tech_disk.tech, TechLevel(tech_disk.level as u8));
@ -636,114 +634,149 @@ impl ItemManager {
} }
pub async fn use_item<EG: EntityGateway>(&mut self, pub async fn use_item<EG: EntityGateway>(&mut self,
used_item: ConsumedItem,
entity_gateway: &mut EG, entity_gateway: &mut EG,
character: &mut CharacterEntity) -> Result<(), anyhow::Error> { character: &mut CharacterEntity,
client_item_id: ClientItemId) -> Result<(), anyhow::Error> {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
match &used_item.item() { let mut used_item_handle = inventory.get_item_handle_by_id(client_item_id).ok_or(ItemManagerError::ItemIdNotInInventory(client_item_id))?;
ItemDetail::Weapon(_w) => { let used_item = used_item_handle.item_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
// something like when items are used to combine/transform them? if used_item.is_wrapped() {
//_ => {} used_item.unwrap_present();
}, } else {
ItemDetail::Tool(t) => { match used_item.item_type() {
match t.tool { ItemType::Weapon(_) => {
ToolType::PowerMaterial => { let _actual_used_item = used_item.individual_mut().ok_or(ItemManagerError::CannotGetMutItem)?.weapon_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
use_tool::power_material(entity_gateway, character).await; // combining / unsealing?
}, },
ToolType::MindMaterial => { ItemType::ESWeapon(_) => { // TODO: wrap srank weapons
use_tool::mind_material(entity_gateway, character).await; let _actual_used_item = used_item.individual_mut().ok_or(ItemManagerError::CannotGetMutItem)?.esweapon_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
}, // combining / unsealing?
ToolType::EvadeMaterial => { },
use_tool::evade_material(entity_gateway, character).await; ItemType::Armor(_) => {
}, let _actual_used_item = used_item.individual_mut().ok_or(ItemManagerError::CannotGetMutItem)?.armor_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
ToolType::DefMaterial => { // combining / unsealing?
use_tool::def_material(entity_gateway, character).await; },
}, ItemType::Unit(_) => {
ToolType::LuckMaterial => { let _actual_used_item = used_item.individual_mut().ok_or(ItemManagerError::CannotGetMutItem)?.unit_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
use_tool::luck_material(entity_gateway, character).await; // combining / unsealing?
}, },
ToolType::HpMaterial => { ItemType::Shield(_) => {
use_tool::hp_material(entity_gateway, character).await; let _actual_used_item = used_item.individual_mut().ok_or(ItemManagerError::CannotGetMutItem)?.shield_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
}, // combining / unsealing?
ToolType::TpMaterial => { },
use_tool::tp_material(entity_gateway, character).await; ItemType::Mag(_) => {
}, let _actual_used_item = used_item.individual_mut().ok_or(ItemManagerError::CannotGetMutItem)?.mag_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
ToolType::CellOfMag502 => { // combining / unsealing?
use_tool::cell_of_mag_502(entity_gateway, &used_item, inventory).await?; },
}, ItemType::TechniqueDisk(_) => {
ToolType::CellOfMag213 => { let _actual_used_item = used_item.individual_mut().ok_or(ItemManagerError::CannotGetMutItem)?.tech_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
use_tool::cell_of_mag_213(entity_gateway, &used_item, inventory).await?; // combining / unsealing?
}, },
ToolType::PartsOfRobochao => { ItemType::Tool(_) => {
use_tool::parts_of_robochao(entity_gateway, &used_item, inventory).await?; let consumed_item = used_item.as_consumed_item();
}, match &used_item.item_detail() {
ToolType::HeartOfOpaOpa => { ItemDetail::Tool(t) => {
use_tool::heart_of_opaopa(entity_gateway, &used_item, inventory).await?; match t.tool {
}, ToolType::PowerMaterial => {
ToolType::HeartOfPian => { use_tool::power_material(entity_gateway, character).await;
use_tool::heart_of_pian(entity_gateway, &used_item, inventory).await?; },
}, ToolType::MindMaterial => {
ToolType::HeartOfChao=> { use_tool::mind_material(entity_gateway, character).await;
use_tool::heart_of_chao(entity_gateway, &used_item, inventory).await?; },
}, ToolType::EvadeMaterial => {
ToolType::HeartOfAngel => { use_tool::evade_material(entity_gateway, character).await;
use_tool::heart_of_angel(entity_gateway, &used_item, inventory).await?; },
}, ToolType::DefMaterial => {
ToolType::KitOfHamburger => { use_tool::def_material(entity_gateway, character).await;
use_tool::kit_of_hamburger(entity_gateway, &used_item, inventory).await?; },
}, ToolType::LuckMaterial => {
ToolType::PanthersSpirit => { use_tool::luck_material(entity_gateway, character).await;
use_tool::panthers_spirit(entity_gateway, &used_item, inventory).await?; },
}, ToolType::HpMaterial => {
ToolType::KitOfMark3 => { use_tool::hp_material(entity_gateway, character).await;
use_tool::kit_of_mark3(entity_gateway, &used_item, inventory).await?; },
}, ToolType::TpMaterial => {
ToolType::KitOfMasterSystem=> { use_tool::tp_material(entity_gateway, character).await;
use_tool::kit_of_master_system(entity_gateway, &used_item, inventory).await?; },
}, ToolType::CellOfMag502 => {
ToolType::KitOfGenesis => { use_tool::cell_of_mag_502(entity_gateway, &consumed_item, inventory).await?;
use_tool::kit_of_genesis(entity_gateway, &used_item, inventory).await?; },
}, ToolType::CellOfMag213 => {
ToolType::KitOfSegaSaturn => { use_tool::cell_of_mag_213(entity_gateway, &consumed_item, inventory).await?;
use_tool::kit_of_sega_saturn(entity_gateway, &used_item, inventory).await?; },
}, ToolType::PartsOfRobochao => {
ToolType::KitOfDreamcast => { use_tool::parts_of_robochao(entity_gateway, &consumed_item, inventory).await?;
use_tool::kit_of_dreamcast(entity_gateway, &used_item, inventory).await?; },
}, ToolType::HeartOfOpaOpa => {
ToolType::Tablet => { use_tool::heart_of_opaopa(entity_gateway, &consumed_item, inventory).await?;
use_tool::tablet(entity_gateway, &used_item, inventory).await?; },
}, ToolType::HeartOfPian => {
ToolType::DragonScale => { use_tool::heart_of_pian(entity_gateway, &consumed_item, inventory).await?;
use_tool::dragon_scale(entity_gateway, &used_item, inventory).await?; },
}, ToolType::HeartOfChao=> {
ToolType::HeavenStrikerCoat => { use_tool::heart_of_chao(entity_gateway, &consumed_item, inventory).await?;
use_tool::heaven_striker_coat(entity_gateway, &used_item, inventory).await?; },
}, ToolType::HeartOfAngel => {
ToolType::PioneerParts => { use_tool::heart_of_angel(entity_gateway, &consumed_item, inventory).await?;
use_tool::pioneer_parts(entity_gateway, &used_item, inventory).await?; },
}, ToolType::KitOfHamburger => {
ToolType::AmitiesMemo => { use_tool::kit_of_hamburger(entity_gateway, &consumed_item, inventory).await?;
use_tool::amities_memo(entity_gateway, &used_item, inventory).await?; },
}, ToolType::PanthersSpirit => {
ToolType::HeartOfMorolian => { use_tool::panthers_spirit(entity_gateway, &consumed_item, inventory).await?;
use_tool::heart_of_morolian(entity_gateway, &used_item, inventory).await?; },
}, ToolType::KitOfMark3 => {
ToolType::RappysBeak => { use_tool::kit_of_mark3(entity_gateway, &consumed_item, inventory).await?;
use_tool::rappys_beak(entity_gateway, &used_item, inventory).await?; },
}, ToolType::KitOfMasterSystem=> {
ToolType::YahoosEngine => { use_tool::kit_of_master_system(entity_gateway, &consumed_item, inventory).await?;
use_tool::yahoos_engine(entity_gateway, &used_item, inventory).await?; },
}, ToolType::KitOfGenesis => {
ToolType::DPhotonCore => { use_tool::kit_of_genesis(entity_gateway, &consumed_item, inventory).await?;
use_tool::d_photon_core(entity_gateway, &used_item, inventory).await?; },
}, ToolType::KitOfSegaSaturn => {
ToolType::LibertaKit => { use_tool::kit_of_sega_saturn(entity_gateway, &consumed_item, inventory).await?;
use_tool::liberta_kit(entity_gateway, &used_item, inventory).await?; },
}, ToolType::KitOfDreamcast => {
_ => {} use_tool::kit_of_dreamcast(entity_gateway, &consumed_item, inventory).await?;
} },
ToolType::Tablet => {
use_tool::tablet(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::DragonScale => {
use_tool::dragon_scale(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::HeavenStrikerCoat => {
use_tool::heaven_striker_coat(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::PioneerParts => {
use_tool::pioneer_parts(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::AmitiesMemo => {
use_tool::amities_memo(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::HeartOfMorolian => {
use_tool::heart_of_morolian(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::RappysBeak => {
use_tool::rappys_beak(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::YahoosEngine => {
use_tool::yahoos_engine(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::DPhotonCore => {
use_tool::d_photon_core(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::LibertaKit => {
use_tool::liberta_kit(entity_gateway, &consumed_item, inventory).await?;
},
_ => {}
}
},
_ => {},
}
},
} }
_ => {}
} }
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(()) Ok(())
@ -923,4 +956,9 @@ impl ItemManager {
Ok(weapon) Ok(weapon)
} }
pub fn get_inventory_item_by_id(&mut self, character: &CharacterEntity, item_id: ClientItemId) -> Result<&InventoryItem, anyhow::Error> {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
Ok(inventory.get_item_by_id(item_id).ok_or(ItemManagerError::ItemIdNotInInventory(item_id))?)
}
} }

View File

@ -1,4 +1,4 @@
// TOOD: `pub(super) for most of these?` // TODO: `pub(super) for most of these?`
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use std::collections::HashMap; use std::collections::HashMap;
use thiserror::Error; use thiserror::Error;

View File

@ -1,12 +1,9 @@
// TOOD: `pub(super) for most of these?` // TODO: `pub(super) for most of these?`
use std::io::{Read}; use std::io::{Read};
use byteorder::{LittleEndian, ReadBytesExt}; use byteorder::{LittleEndian, ReadBytesExt};
use thiserror::Error; use thiserror::Error;
use crate::ship::monster::MonsterType; use crate::ship::monster::MonsterType;
use crate::ship::room::Episode; use crate::ship::room::Episode;
use crate::ship::map::*; use crate::ship::map::*;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -62,7 +59,6 @@ impl RawMapEnemy {
} }
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]
#[error("")] #[error("")]
pub enum MapEnemyError { pub enum MapEnemyError {
@ -70,7 +66,6 @@ pub enum MapEnemyError {
MapAreaError(#[from] MapAreaError), MapAreaError(#[from] MapAreaError),
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct MapEnemy { pub struct MapEnemy {
pub monster: MonsterType, pub monster: MonsterType,
@ -194,7 +189,6 @@ impl MapEnemy {
_ => return Err(MapEnemyError::UnknownEnemyId(enemy.id)) _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id))
} }
}, },
MapArea::Tower => { MapArea::Tower => {
match (enemy, episode) { match (enemy, episode) {
(RawMapEnemy {id: 97, ..}, _) => MonsterType::DelLily, (RawMapEnemy {id: 97, ..}, _) => MonsterType::DelLily,

View File

@ -1,4 +1,4 @@
// TOOD: `pub(super) for most of these?` // TODO: `pub(super) for most of these?`
use std::path::PathBuf; use std::path::PathBuf;
use std::io::{Read}; use std::io::{Read};
@ -96,7 +96,7 @@ fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) ->
monsters.push(Some(MapEnemy::new(MonsterType::VolOptCore, monster.map_area))); monsters.push(Some(MapEnemy::new(MonsterType::VolOptCore, monster.map_area)));
monsters.push(Some(MapEnemy::new(MonsterType::VolOptUnused, monster.map_area))); monsters.push(Some(MapEnemy::new(MonsterType::VolOptUnused, monster.map_area)));
}, },
// TOOD: this cares about difficulty (theres an ult-specific darvant?) // TODO: this cares about difficulty (theres an ult-specific darvant?)
MonsterType::DarkFalz => { MonsterType::DarkFalz => {
for _ in 0..509 { for _ in 0..509 {
monsters.push(Some(MapEnemy::new(MonsterType::Darvant, monster.map_area))); monsters.push(Some(MapEnemy::new(MonsterType::Darvant, monster.map_area)));

View File

@ -1,4 +1,4 @@
// TOOD: `pub(super) for most of these?` // TODO: `pub(super) for most of these?`
use std::io::{Read}; use std::io::{Read};

View File

@ -1,4 +1,4 @@
// TOOD: `pub(super) for most of these?` // TODO: `pub(super) for most of these?`
use rand::Rng; use rand::Rng;

View File

@ -119,7 +119,7 @@ pub fn character_leveled_up(area_client: AreaClient, level: u32, before_stats: C
} }
} }
// TOOD: checksum? // TODO: checksum?
pub fn bank_item_list(bank: &CharacterBank, char_bank_meseta: u32) -> BankItemList { pub fn bank_item_list(bank: &CharacterBank, char_bank_meseta: u32) -> BankItemList {
BankItemList { BankItemList {
aflag: 0, aflag: 0,

View File

@ -1,12 +1,12 @@
use libpso::packet::ship::*; use libpso::packet::ship::*;
use libpso::packet::messages::*; use libpso::packet::messages::*;
use crate::entity::gateway::EntityGateway; use crate::entity::gateway::EntityGateway;
use crate::entity::item::{ItemType}; use crate::entity::item::{ItemType, ItemDetail};
use crate::common::serverstate::ClientId; use crate::common::serverstate::ClientId;
use crate::common::leveltable::CharacterLevelTable; use crate::common::leveltable::CharacterLevelTable;
use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients, ItemDropLocation}; use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients, ItemDropLocation};
use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::items::{ItemManager, ClientItemId, InventoryItem}; use crate::ship::items::{ItemManager, ClientItemId, InventoryItem, ItemManagerError};
use crate::ship::packet::builder; use crate::ship::packet::builder;
pub async fn request_exp<EG: EntityGateway>(id: ClientId, pub async fn request_exp<EG: EntityGateway>(id: ClientId,
@ -272,9 +272,12 @@ where
EG: EntityGateway EG: EntityGateway
{ {
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
let item_used_type = item_manager.player_consumes_tool(entity_gateway, &mut client.character, ClientItemId(player_use_tool.item_id), 1).await?; item_manager.use_item(entity_gateway, &mut client.character, ClientItemId(player_use_tool.item_id)).await?;
item_manager.use_item(item_used_type, entity_gateway, &mut client.character).await?; if let ItemDetail::Tool(_t) = item_manager.get_inventory_item_by_id(&client.character, ClientItemId(player_use_tool.item_id))?.item_detail() {
item_manager.player_consumes_tool(entity_gateway, &mut client.character, ClientItemId(player_use_tool.item_id), 1).await?;
}
Ok(Box::new(None.into_iter())) Ok(Box::new(None.into_iter()))
} }

View File

@ -7,7 +7,7 @@ use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::packet::builder::quest; use crate::ship::packet::builder::quest;
use libpso::util::array_to_utf8; use libpso::util::array_to_utf8;
// TOOD: enum // TODO: enum
enum QuestFileType { enum QuestFileType {
Bin, Bin,
Dat Dat

View File

@ -653,7 +653,7 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
handler::room::cool_62(id, cool62, &block.client_location) handler::room::cool_62(id, cool62, &block.client_location)
}, },
RecvShipPacket::ClientCharacterData(_) => { RecvShipPacket::ClientCharacterData(_) => {
// TOOD: validate this in some way? // TODO: validate this in some way?
Box::new(None.into_iter()) Box::new(None.into_iter())
}, },
RecvShipPacket::DoneBursting(_) => { RecvShipPacket::DoneBursting(_) => {

View File

@ -71,6 +71,7 @@ impl ShopItem for ArmorShopItem {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: *slot as u8, slots: *slot as u8,
wrapping: None,
}) })
}, },
ArmorShopItem::Barrier(barrier) => { ArmorShopItem::Barrier(barrier) => {
@ -78,12 +79,14 @@ impl ShopItem for ArmorShopItem {
shield: *barrier, shield: *barrier,
dfp: 0, dfp: 0,
evp: 0, evp: 0,
wrapping: None,
}) })
}, },
ArmorShopItem::Unit(unit) => { ArmorShopItem::Unit(unit) => {
ItemDetail::Unit(Unit { ItemDetail::Unit(Unit {
unit: *unit, unit: *unit,
modifier: None, modifier: None,
wrapping: None,
}) })
}, },
} }

View File

@ -22,11 +22,11 @@ pub enum ToolShopItem {
impl Ord for ToolShopItem { impl Ord for ToolShopItem {
fn cmp(&self, other: &ToolShopItem) -> std::cmp::Ordering { fn cmp(&self, other: &ToolShopItem) -> std::cmp::Ordering {
let a = match self { let a = match self {
ToolShopItem::Tool(t) => Tool{tool : *t}.as_individual_bytes(), ToolShopItem::Tool(t) => Tool{tool : *t, wrapping: None,}.as_individual_bytes(),
ToolShopItem::Tech(t) => t.as_bytes(), ToolShopItem::Tech(t) => t.as_bytes(),
}; };
let b = match other { let b = match other {
ToolShopItem::Tool(t) => Tool{tool : *t}.as_individual_bytes(), ToolShopItem::Tool(t) => Tool{tool : *t, wrapping: None,}.as_individual_bytes(),
ToolShopItem::Tech(t) => t.as_bytes(), ToolShopItem::Tech(t) => t.as_bytes(),
}; };
@ -37,11 +37,11 @@ impl Ord for ToolShopItem {
impl PartialOrd for ToolShopItem { impl PartialOrd for ToolShopItem {
fn partial_cmp(&self, other: &ToolShopItem) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &ToolShopItem) -> Option<std::cmp::Ordering> {
let a = match self { let a = match self {
ToolShopItem::Tool(t) => Tool{tool : *t}.as_individual_bytes(), ToolShopItem::Tool(t) => Tool{tool : *t, wrapping: None,}.as_individual_bytes(),
ToolShopItem::Tech(t) => t.as_bytes(), ToolShopItem::Tech(t) => t.as_bytes(),
}; };
let b = match other { let b = match other {
ToolShopItem::Tool(t) => Tool{tool : *t}.as_individual_bytes(), ToolShopItem::Tool(t) => Tool{tool : *t, wrapping: None,}.as_individual_bytes(),
ToolShopItem::Tech(t) => t.as_bytes(), ToolShopItem::Tech(t) => t.as_bytes(),
}; };
@ -73,7 +73,8 @@ impl ShopItem for ToolShopItem {
match self { match self {
ToolShopItem::Tool(tool) => { ToolShopItem::Tool(tool) => {
Tool { Tool {
tool: *tool tool: *tool,
wrapping: None,
}.as_individual_bytes()[0..12].try_into().unwrap() }.as_individual_bytes()[0..12].try_into().unwrap()
}, },
ToolShopItem::Tech(tech) => { ToolShopItem::Tech(tech) => {
@ -86,7 +87,8 @@ impl ShopItem for ToolShopItem {
match self { match self {
ToolShopItem::Tool(tool) => { ToolShopItem::Tool(tool) => {
ItemDetail::Tool(Tool { ItemDetail::Tool(Tool {
tool: *tool tool: *tool,
wrapping: None,
}) })
}, },
ToolShopItem::Tech(tech) => { ToolShopItem::Tech(tech) => {
@ -262,6 +264,7 @@ impl<R: Rng + SeedableRng> ToolShop<R> {
ToolShopItem::Tech(TechniqueDisk { ToolShopItem::Tech(TechniqueDisk {
tech: tech, tech: tech,
level: level as u32, level: level as u32,
wrapping: None,
}) })
}) })
.collect() .collect()

View File

@ -142,6 +142,7 @@ impl ShopItem for WeaponShopItem {
grind: self.grind as u8, grind: self.grind as u8,
attrs: [self.attributes[0], self.attributes[1], None], attrs: [self.attributes[0], self.attributes[1], None],
tekked: true, tekked: true,
wrapping: None,
} }
) )
} }

View File

@ -27,6 +27,7 @@ async fn test_bank_items_sent_in_character_login() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -68,6 +69,7 @@ async fn test_request_bank_items() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -116,6 +118,7 @@ async fn test_request_stacked_bank_items() {
item: item::ItemDetail::Tool ( item: item::ItemDetail::Tool (
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -165,6 +168,7 @@ async fn test_request_bank_items_sorted() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -177,6 +181,7 @@ async fn test_request_bank_items_sorted() {
item: item::ItemDetail::Tool ( item: item::ItemDetail::Tool (
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -193,6 +198,7 @@ async fn test_request_bank_items_sorted() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -243,6 +249,7 @@ async fn test_deposit_individual_item() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -258,6 +265,7 @@ async fn test_deposit_individual_item() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -320,6 +328,7 @@ async fn test_deposit_stacked_item() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -384,6 +393,7 @@ async fn test_deposit_partial_stacked_item() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -458,6 +468,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -470,6 +481,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -535,6 +547,7 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -550,6 +563,7 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -617,6 +631,7 @@ async fn test_deposit_individual_item_in_full_bank() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -635,6 +650,7 @@ async fn test_deposit_individual_item_in_full_bank() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -695,6 +711,7 @@ async fn test_deposit_stacked_item_in_full_bank() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -714,6 +731,7 @@ async fn test_deposit_stacked_item_in_full_bank() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -775,6 +793,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -790,6 +809,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -810,6 +830,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -988,6 +1009,7 @@ async fn test_withdraw_individual_item() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1051,6 +1073,7 @@ async fn test_withdraw_stacked_item() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1115,6 +1138,7 @@ async fn test_withdraw_partial_stacked_item() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1186,6 +1210,7 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -1198,6 +1223,7 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1265,6 +1291,7 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1281,6 +1308,7 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -1347,6 +1375,7 @@ async fn test_withdraw_individual_item_in_full_inventory() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1366,6 +1395,7 @@ async fn test_withdraw_individual_item_in_full_inventory() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -1421,6 +1451,7 @@ async fn test_withdraw_stacked_item_in_full_inventory() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1441,6 +1472,7 @@ async fn test_withdraw_stacked_item_in_full_inventory() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -1502,6 +1534,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1523,6 +1556,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -1538,6 +1572,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {

View File

@ -0,0 +1,289 @@
use elseware::common::serverstate::{ClientId, ServerState};
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket};
use libpso::packet::ship::*;
use libpso::packet::messages::*;
#[path = "common.rs"]
mod common;
use common::*;
// unwrap presents
#[async_std::test]
async fn test_unwrap_weapon() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let wrapped_item = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(item::weapon::Weapon {
weapon: item::weapon::WeaponType::Saber,
special: Some(item::weapon::WeaponSpecial::Burning),
grind: 5,
attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Machine, value: 20}),
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 30}),
None],
tekked: false,
wrapping: Some(item::WrappingPaper::RedGreen),
}),
location: item::ItemLocation::Inventory{
character_id: char1.id,
}
}).await.unwrap();
assert!(wrapped_item.item.as_client_bytes() == [0x00,0x01,0x00,0x05,0xDA,0x05,0x03,0x14,0x05,0x1E,0x00,0x00,0x00,0x00,0x00,0x00]);
let mut inventory = Vec::<item::ItemEntity>::new();
inventory.push(wrapped_item.into());
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
client: 0,
target: 0,
item_id: 0x10000,
})))).await.unwrap().for_each(drop);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
inventory_items.items[0].with_individual(|item| {
match &item.item {
item::ItemDetail::Weapon(weapon) => {
assert!(weapon.as_bytes() == [0x00, 0x01, 0x00, 0x05, 0x9A, 0x00, 0x03, 0x14, 0x05, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
},
_ => panic!(),
}
}).unwrap();
}
#[async_std::test]
async fn test_unwrap_armor() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let wrapped_item = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Armor(item::armor::Armor {
armor: item::armor::ArmorType::PerfectFrame,
dfp: 3u8,
evp: 2u8,
slots: 4u8,
wrapping: Some(item::WrappingPaper::RedGreen), // 5
}),
location: item::ItemLocation::Inventory{
character_id: char1.id,
}
}).await.unwrap();
// slots should be untouched when wrapped regardless of wrapping paper colour
assert!(wrapped_item.item.as_client_bytes() == [0x01,0x01,0x10,0x00,0x40,0x04,0x03,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00]);
let mut inventory = Vec::<item::ItemEntity>::new();
inventory.push(wrapped_item.into());
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
client: 0,
target: 0,
item_id: 0x10000,
})))).await.unwrap().for_each(drop);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
inventory_items.items[0].with_individual(|item| {
match &item.item {
item::ItemDetail::Armor(armor) => {
assert!(armor.as_bytes() == [0x01,0x01,0x10,0x00,0x00,0x04,0x03,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00]);
},
_ => panic!(),
}
}).unwrap();
}
#[async_std::test]
async fn test_unwrap_shield() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let wrapped_item = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Shield(item::shield::Shield {
shield: item::shield::ShieldType::CoreShield,
dfp: 2u8,
evp: 3u8,
wrapping: Some(item::WrappingPaper::RedGreen), // 5
}),
location: item::ItemLocation::Inventory{
character_id: char1.id,
}
}).await.unwrap();
assert!(wrapped_item.item.as_client_bytes() == [0x01,0x02,0x02,0x00,0x40,0x05,0x02,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00]);
let mut inventory = Vec::<item::ItemEntity>::new();
inventory.push(wrapped_item.into());
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
client: 0,
target: 0,
item_id: 0x10000,
})))).await.unwrap().for_each(drop);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
inventory_items.items[0].with_individual(|item| {
match &item.item {
item::ItemDetail::Shield(shield) => {
assert!(shield.as_bytes() == [0x01,0x02,0x02,0x00,0x00,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00]);
},
_ => panic!(),
}
}).unwrap();
}
#[async_std::test]
async fn test_unwrap_unit() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let wrapped_item = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Unit(item::unit::Unit {
unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::MinusMinus),
wrapping: Some(item::WrappingPaper::RedGreen), // 5
}),
location: item::ItemLocation::Inventory{
character_id: char1.id,
}
}).await.unwrap();
assert!(wrapped_item.item.as_client_bytes() == [0x01,0x03,0x00,0x00,0x40,0x05,0xFE,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]);
let mut inventory = Vec::<item::ItemEntity>::new();
inventory.push(wrapped_item.into());
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
client: 0,
target: 0,
item_id: 0x10000,
})))).await.unwrap().for_each(drop);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
inventory_items.items[0].with_individual(|item| {
match &item.item {
item::ItemDetail::Unit(unit) => {
assert!(unit.as_bytes() == [0x01,0x03,0x00,0x00,0x00,0x00,0xFE,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]);
},
_ => panic!(),
}
}).unwrap();
}
// mag cells are covered in test_mags.rs
#[async_std::test]
async fn test_unwrap_mag() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let wrapped_item = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Mag(item::mag::Mag::wrapped_baby_mag(12)), //turquoise
location: item::ItemLocation::Inventory{
character_id: char1.id,
}
}).await.unwrap();
assert!(wrapped_item.item.as_client_bytes() == [0x02,0x00,0x00,0x00,0xF4,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00,0x40,0x0C]); // item maker is wrong?
let mut inventory = Vec::<item::ItemEntity>::new();
inventory.push(wrapped_item.into());
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
client: 0,
target: 0,
item_id: 0x10000,
})))).await.unwrap().for_each(drop);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
inventory_items.items[0].with_individual(|item| {
match &item.item {
item::ItemDetail::Mag(mag) => {
assert!(mag.as_bytes() == [0x02,0x00,0x00,0x00,0xF4,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x0C]);
},
_ => panic!(),
}
}).unwrap();
}
// TODO: implement wrapping packet (message 0xD6) (gallons shop quest)
// wrap presents
#[async_std::test]
async fn test_wrap_weapon() {}
#[async_std::test]
async fn test_wrap_armor() {}
#[async_std::test]
async fn test_wrap_shield() {}
#[async_std::test]
async fn test_wrap_unit() {}
// mag cells are covered in test_mags.rs
#[async_std::test]
async fn test_wrap_mag() {}
// item combinations?

View File

@ -22,7 +22,8 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -38,7 +39,8 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: tool tool: tool,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -104,7 +106,8 @@ async fn test_pick_up_item_stack_of_items_not_already_held() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -172,6 +175,7 @@ async fn test_pick_up_meseta_when_inventory_full() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -251,6 +255,7 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -264,6 +269,7 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -277,6 +283,7 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -343,6 +350,7 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -361,6 +369,7 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -476,6 +485,7 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -491,6 +501,7 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -671,6 +682,7 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {

View File

@ -26,7 +26,8 @@ async fn test_use_monomate() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: tool tool: tool,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -76,7 +77,8 @@ async fn test_use_monomate_twice() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: tool tool: tool,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -129,7 +131,8 @@ async fn test_use_last_monomate() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: tool tool: tool,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -174,6 +177,7 @@ async fn test_use_nonstackable_tool() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::MagicStoneIritista, tool: item::tool::ToolType::MagicStoneIritista,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -214,7 +218,8 @@ async fn test_use_materials() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: tool tool: tool,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {

View File

@ -35,6 +35,7 @@ async fn test_mag_feed() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -180,6 +181,7 @@ async fn test_mag_cell() {
item: item::ItemDetail::Tool ( item: item::ItemDetail::Tool (
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::FedToMag { location: item::ItemLocation::FedToMag {
@ -193,6 +195,7 @@ async fn test_mag_cell() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::CellOfMag502, tool: item::tool::ToolType::CellOfMag502,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {

View File

@ -25,6 +25,7 @@ async fn test_equip_unit_from_equip_menu() {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 4, slots: 4,
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -37,6 +38,7 @@ async fn test_equip_unit_from_equip_menu() {
item::unit::Unit{ item::unit::Unit{
unit: item::unit::UnitType::KnightPower, unit: item::unit::UnitType::KnightPower,
modifier: None, modifier: None,
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -49,6 +51,7 @@ async fn test_equip_unit_from_equip_menu() {
item::unit::Unit{ item::unit::Unit{
unit: item::unit::UnitType::KnightPower, unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus), modifier: Some(item::unit::UnitModifier::Plus),
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -111,6 +114,7 @@ async fn test_unequip_armor_with_units() {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 4, slots: 4,
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -123,6 +127,7 @@ async fn test_unequip_armor_with_units() {
item::unit::Unit{ item::unit::Unit{
unit: item::unit::UnitType::KnightPower, unit: item::unit::UnitType::KnightPower,
modifier: None, modifier: None,
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -135,6 +140,7 @@ async fn test_unequip_armor_with_units() {
item::unit::Unit{ item::unit::Unit{
unit: item::unit::UnitType::KnightPower, unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus), modifier: Some(item::unit::UnitModifier::Plus),
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -188,6 +194,7 @@ async fn test_sort_items() {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 4, slots: 4,
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -200,6 +207,7 @@ async fn test_sort_items() {
item::unit::Unit{ item::unit::Unit{
unit: item::unit::UnitType::KnightPower, unit: item::unit::UnitType::KnightPower,
modifier: None, modifier: None,
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -212,6 +220,7 @@ async fn test_sort_items() {
item::unit::Unit{ item::unit::Unit{
unit: item::unit::UnitType::KnightPower, unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus), modifier: Some(item::unit::UnitModifier::Plus),
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,

View File

@ -29,6 +29,7 @@ async fn test_item_ids_reset_when_rejoining_rooms() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -48,6 +49,7 @@ async fn test_item_ids_reset_when_rejoining_rooms() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {

View File

@ -318,7 +318,8 @@ async fn test_other_clients_see_stacked_purchase() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -522,7 +523,7 @@ async fn test_techs_disappear_from_shop_when_bought() {
}).unwrap(); }).unwrap();
} }
// TOOD: this is not deterministic and can randomly fail // TODO: this is not deterministic and can randomly fail
#[async_std::test] #[async_std::test]
async fn test_units_disappear_from_shop_when_bought() { async fn test_units_disappear_from_shop_when_bought() {
let mut entity_gateway = InMemoryGateway::new(); let mut entity_gateway = InMemoryGateway::new();