Browse Source

e q u i p r e f a c t o r

pbs
jake 4 years ago
parent
commit
6e2fab31f0
  1. 5
      src/bin/main.rs
  2. 24
      src/entity/gateway/postgres/migrations/V0001__initial.sql
  3. 89
      src/entity/gateway/postgres/models.rs
  4. 158
      src/entity/gateway/postgres/postgres.rs
  5. 3
      src/entity/item/unit.rs
  6. 2
      src/ship/drops/generic_unit.rs
  7. 1
      src/ship/drops/rare_drop_table.rs
  8. 116
      src/ship/items/inventory.rs
  9. 109
      src/ship/items/manager.rs
  10. 28
      src/ship/packet/handler/message.rs
  11. 1
      src/ship/shops/armor.rs
  12. 6
      tests/test_item_actions.rs

5
src/bin/main.rs

@ -301,7 +301,6 @@ 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),
armor_slot: 0, // TODO: get rid of this
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -315,7 +314,6 @@ 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),
armor_slot: 1,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -329,7 +327,6 @@ 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),
armor_slot: 2,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -343,7 +340,6 @@ 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),
armor_slot: 3,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -373,6 +369,7 @@ fn main() {
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]);
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();
} }
info!("[patch] starting server"); info!("[patch] starting server");

24
src/entity/gateway/postgres/migrations/V0001__initial.sql

@ -122,3 +122,27 @@ 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 equipped (
pchar integer references player_character (id) unique not null,
weapon integer references item (id),
armor integer references item (id),
shield integer references item (id),
unit0 integer references item (id),
unit1 integer references item (id),
unit2 integer references item (id),
unit3 integer references item (id),
mag integer references item (id)
);
create table inventory (
pchar integer references player_character (id) unique not null,
items jsonb not null
);
create table bank (
pchar integer references player_character (id) not null,
items jsonb not null,
name varchar(128) not null,
unique (pchar, name)
);

89
src/entity/gateway/postgres/models.rs

@ -383,7 +383,6 @@ impl Into<shield::Shield> for PgShield {
pub struct PgUnit { pub struct PgUnit {
unit: unit::UnitType, unit: unit::UnitType,
modifier: Option<unit::UnitModifier>, modifier: Option<unit::UnitModifier>,
//armor_slot: u8,
} }
impl From<unit::Unit> for PgUnit { impl From<unit::Unit> for PgUnit {
@ -391,7 +390,6 @@ impl From<unit::Unit> for PgUnit {
PgUnit { PgUnit {
unit: other.unit, unit: other.unit,
modifier: other.modifier, modifier: other.modifier,
//armor_slot: other.armor_slot,
} }
} }
} }
@ -401,7 +399,6 @@ impl Into<unit::Unit> for PgUnit {
unit::Unit { unit::Unit {
unit: self.unit, unit: self.unit,
modifier: self.modifier, modifier: self.modifier,
armor_slot: 0,
} }
} }
} }
@ -573,9 +570,6 @@ pub struct PgItem {
pub enum PgItemLocationDetail { pub enum PgItemLocationDetail {
Inventory { Inventory {
character_id: u32, character_id: u32,
//#[serde(skip_serializing)]
//slot: usize,
//equipped: bool,
}, },
Bank { Bank {
character_id: u32, character_id: u32,
@ -676,6 +670,12 @@ pub struct PgMagModifier {
} }
#[derive(Debug, sqlx::FromRow)]
pub struct PgItemEntity {
pub id: i32,
pub item: sqlx::types::Json<PgItemDetail>,
}
#[derive(Debug, sqlx::FromRow)] #[derive(Debug, sqlx::FromRow)]
pub struct PgItemWithLocation { pub struct PgItemWithLocation {
pub id: i32, pub id: i32,
@ -683,6 +683,16 @@ pub struct PgItemWithLocation {
pub location: sqlx::types::Json<PgItemLocationDetail>, pub location: sqlx::types::Json<PgItemLocationDetail>,
} }
impl Into<ItemEntity> for PgItemWithLocation {
fn into(self) -> ItemEntity {
ItemEntity {
id: ItemEntityId(self.id as u32),
item: self.item.0.into(),
location: self.location.0.into(),
}
}
}
#[derive(Debug, sqlx::FromRow)] #[derive(Debug, sqlx::FromRow)]
pub struct PgMagModifierWithParameters { pub struct PgMagModifierWithParameters {
@ -691,3 +701,70 @@ pub struct PgMagModifierWithParameters {
pub feed: Option<sqlx::types::Json<PgTool>>, pub feed: Option<sqlx::types::Json<PgTool>>,
pub cell: Option<sqlx::types::Json<PgTool>>, pub cell: Option<sqlx::types::Json<PgTool>>,
} }
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PgInventoryItemEntity {
Individual(i32),
Stacked(Vec<i32>),
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgInventoryEntity {
pub pchar: i32,
pub items: sqlx::types::Json<Vec<PgInventoryItemEntity>>,
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgBankEntity {
pub pchar: i32,
pub items: sqlx::types::Json<Vec<PgInventoryItemEntity>>,
pub name: String,
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgEquipped {
pchar: i32,
weapon: Option<i32>,
armor: Option<i32>,
shield: Option<i32>,
unit0: Option<i32>,
unit1: Option<i32>,
unit2: Option<i32>,
unit3: Option<i32>,
mag: Option<i32>,
}
impl Into<EquippedEntity> for PgEquipped {
fn into(self) -> EquippedEntity {
EquippedEntity {
weapon: self.weapon.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)),
self.unit1.map(|i| ItemEntityId(i as u32)),
self.unit2.map(|i| ItemEntityId(i as u32)),
self.unit3.map(|i| ItemEntityId(i as u32)),
],
mag: self.mag.map(|i| ItemEntityId(i as u32)),
}
}
}
impl From<(CharacterEntityId, EquippedEntity)> for PgEquipped {
fn from(char_equips: (CharacterEntityId, EquippedEntity)) -> PgEquipped {
PgEquipped {
pchar: char_equips.0.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),
shield: char_equips.1.shield.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),
unit2: char_equips.1.unit[2].map(|i| i.0 as i32),
unit3: char_equips.1.unit[3].map(|i| i.0 as i32),
mag: char_equips.1.mag.map(|i| i.0 as i32),
}
}
}

158
src/entity/gateway/postgres/postgres.rs

@ -272,7 +272,22 @@ impl EntityGateway for PostgresGateway {
} }
async fn create_item(&mut self, item: NewItemEntity) -> Result<ItemEntity, GatewayError> { async fn create_item(&mut self, item: NewItemEntity) -> Result<ItemEntity, GatewayError> {
unimplemented!();
let mut tx = self.pool.begin().await?;
let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;")
.bind(sqlx::types::Json(PgItemDetail::from(item.item)))
.fetch_one(&mut tx).await?;
let location = sqlx::query_as::<_, PgItemLocation>("insert into item_location (item, location) values ($1, $2) returning *")
.bind(new_item.id)
.bind(sqlx::types::Json(PgItemLocationDetail::from(item.location)))
.fetch_one(&mut tx).await?;
tx.commit().await?;
Ok(ItemEntity {
id: ItemEntityId(new_item.id as u32),
item: new_item.item.0.into(),
location: location.location.0.into(),
})
/* /*
let mut tx = self.pool.begin().await?; let mut tx = self.pool.begin().await?;
let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;") let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;")
@ -324,7 +339,12 @@ impl EntityGateway for PostgresGateway {
} }
async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) -> Result<(), GatewayError> { async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) -> Result<(), GatewayError> {
unimplemented!();
sqlx::query("insert into item_location (item, location) values ($1, $2)")
.bind(item_id.0)
.bind(sqlx::types::Json(PgItemLocationDetail::from(item_location)))
.execute(&self.pool).await?;
Ok(())
/* /*
let mut tx = self.pool.begin().await?; let mut tx = self.pool.begin().await?;
if let ItemLocation::Inventory{slot, ..} = &item_location { if let ItemLocation::Inventory{slot, ..} = &item_location {
@ -417,27 +437,137 @@ impl EntityGateway for PostgresGateway {
).await) ).await)
} }
*/ */
async fn get_character_inventory(&mut self, _char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
unimplemented!();
async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
let inventory = sqlx::query_as::<_, PgInventoryEntity>("select * from inventory where pchar = $1")
.bind(char_id.0)
.fetch_one(&self.pool).await?;
// TODO: inefficient
let mut real_inventory = Vec::new();
for inv_item in inventory.items.0.into_iter() {
match inv_item {
PgInventoryItemEntity::Individual(item) => {
let entity = sqlx::query_as::<_, PgItemWithLocation>("select item.id, item.item, item_location.location from item join item_location on item.id = item_location.item where id = $1")
.bind(item)
.fetch_one(&self.pool).await
.map(|item| item.into())?;
real_inventory.push(InventoryItemEntity::Individual(entity));
},
PgInventoryItemEntity::Stacked(items) => {
let mut stacked_item = Vec::new();
for s_item in items {
stacked_item.push(sqlx::query_as::<_, PgItemWithLocation>("select item.id, item.item, item_location.location from item join item_location on item.id = item_location.item where id = $1")
.bind(s_item)
.fetch_one(&self.pool).await
.map(|item| item.into())?)
}
real_inventory.push(InventoryItemEntity::Stacked(stacked_item));
}
}
}
Ok(InventoryEntity::new(real_inventory))
} }
async fn get_character_bank(&mut self, _char_id: &CharacterEntityId, _bank_name: BankName) -> Result<BankEntity, GatewayError> {
unimplemented!();
async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_name: BankName) -> Result<BankEntity, GatewayError> {
let bank = sqlx::query_as::<_, PgInventoryEntity>("select * from bank where pchar = $1 and name = $2")
.bind(char_id.0)
.bind(bank_name.0)
.fetch_one(&self.pool).await?;
// TODO: inefficient
let mut real_bank = Vec::new();
for bank_item in bank.items.0.into_iter() {
match bank_item {
PgInventoryItemEntity::Individual(item) => {
let entity = sqlx::query_as::<_, PgItemWithLocation>("select item.id, item.item, item_location.location from item join item_location on item.id = item_location.item where id = $1")
.bind(item)
.fetch_one(&self.pool).await
.map(|item| item.into())?;
real_bank.push(BankItemEntity::Individual(entity));
},
PgInventoryItemEntity::Stacked(items) => {
let mut stacked_item = Vec::new();
for s_item in items {
stacked_item.push(sqlx::query_as::<_, PgItemWithLocation>("select item.id, item.item, item_location.location from item join item_location on item.id = item_location.item where id = $1")
.bind(s_item)
.fetch_one(&self.pool).await
.map(|item| item.into())?)
}
real_bank.push(BankItemEntity::Stacked(stacked_item));
}
}
}
Ok(BankEntity::new(real_bank))
} }
async fn set_character_inventory(&mut self, _char_id: &CharacterEntityId, _inventory: &InventoryEntity) -> Result<(), GatewayError> {
unimplemented!();
async fn set_character_inventory(&mut self, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> {
let inventory = inventory.items.iter()
.map(|item| {
match item {
InventoryItemEntity::Individual(item) => {
PgInventoryItemEntity::Individual(item.id.0 as i32)
},
InventoryItemEntity::Stacked(items) => {
PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect())
},
}
})
.collect::<Vec<_>>();
sqlx::query("insert into inventory (pchar, items) values ($1, $2) on conflict (pchar) do update set items = $2")
.bind(char_id.0)
.bind(sqlx::types::Json(inventory))
.execute(&self.pool)
.await?;
Ok(())
} }
async fn set_character_bank(&mut self, _char_id: &CharacterEntityId, _inventory: &BankEntity, _bank_name: BankName) -> Result<(), GatewayError> {
unimplemented!();
async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: BankName) -> Result<(), GatewayError> {
let bank = bank.items.iter()
.map(|item| {
match item {
BankItemEntity::Individual(item) => {
PgInventoryItemEntity::Individual(item.id.0 as i32)
},
BankItemEntity::Stacked(items) => {
PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect())
},
}
})
.collect::<Vec<_>>();
sqlx::query("insert into bank (pchar, items, name) values ($1, $2, $3) on conflict (pchar, name) do update set items = $2")
.bind(char_id.0)
.bind(sqlx::types::Json(bank))
.bind(bank_name.0)
.execute(&self.pool)
.await?;
Ok(())
} }
async fn get_character_equips(&mut self, _char_id: &CharacterEntityId) -> Result<EquippedEntity, GatewayError> {
unimplemented!();
async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result<EquippedEntity, GatewayError> {
let equips = sqlx::query_as::<_, PgEquipped>("select * from equipped where pchar = $1")
.bind(char_id.0)
.fetch_one(&self.pool)
.await?;
Ok(equips.into())
} }
async fn set_character_equips(&mut self, _char_id: &CharacterEntityId, _equips: &EquippedEntity) -> Result<(), GatewayError> {
unimplemented!();
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)
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(equips.weapon.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[1].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.mag.map(|i| i.0 as i32))
.execute(&self.pool)
.await?;
Ok(())
} }
} }

3
src/entity/item/unit.rs

@ -335,7 +335,6 @@ pub enum UnitModifier {
pub struct Unit { pub struct Unit {
pub unit: UnitType, pub unit: UnitType,
pub modifier: Option<UnitModifier>, pub modifier: Option<UnitModifier>,
pub armor_slot: u8,
} }
@ -362,7 +361,6 @@ impl Unit {
}, },
} }
} }
result[4] = self.armor_slot;
result result
} }
@ -380,7 +378,6 @@ impl Unit {
Ok(Unit{ Ok(Unit{
unit: u.unwrap(), unit: u.unwrap(),
modifier: m, modifier: m,
armor_slot: data[4],
}) })
} }
else { else {

2
src/ship/drops/generic_unit.rs

@ -89,7 +89,6 @@ impl GenericUnitTable {
ItemDropType::Unit(Unit { ItemDropType::Unit(Unit {
unit: unit_type, unit: unit_type,
modifier: unit_modifier, modifier: unit_modifier,
armor_slot: 0,
}) })
}) })
} }
@ -117,7 +116,6 @@ 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,
armor_slot: 0,
}))); })));
} }
} }

1
src/ship/drops/rare_drop_table.rs

@ -128,7 +128,6 @@ impl RareDropTable {
ItemDropType::Unit(Unit { ItemDropType::Unit(Unit {
unit: unit, unit: unit,
modifier: None, modifier: None,
armor_slot: 0,
}) })
}, },
RareDropItem::Tool(tool) => { RareDropItem::Tool(tool) => {

116
src/ship/items/inventory.rs

@ -14,12 +14,12 @@ const INVENTORY_CAPACITY: usize = 30;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct InventorySlot(pub usize); pub struct InventorySlot(pub usize);
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct IndividualInventoryItem { pub struct IndividualInventoryItem {
pub entity_id: ItemEntityId, pub entity_id: ItemEntityId,
pub item_id: ClientItemId, pub item_id: ClientItemId,
pub item: ItemDetail, pub item: ItemDetail,
pub equipped: bool,
} }
impl IndividualInventoryItem { impl IndividualInventoryItem {
@ -154,17 +154,6 @@ impl InventoryItem {
} }
} }
pub fn equipped(&self) -> bool {
match self {
InventoryItem::Individual(individual_inventory_item) => {
individual_inventory_item.equipped
},
InventoryItem::Stacked(_) => {
false
}
}
}
pub fn as_client_bytes(&self) -> [u8; 16] { pub fn as_client_bytes(&self) -> [u8; 16] {
match self { match self {
InventoryItem::Individual(item) => { InventoryItem::Individual(item) => {
@ -353,13 +342,15 @@ impl<'a> InventoryItemHandle<'a> {
pub struct CharacterInventory { pub struct CharacterInventory {
item_id_counter: u32, item_id_counter: u32,
items: Vec<InventoryItem>, items: Vec<InventoryItem>,
equipped: EquippedEntity,
} }
impl CharacterInventory { impl CharacterInventory {
pub fn new(items: Vec<InventoryItem>) -> CharacterInventory {
pub fn new(items: Vec<InventoryItem>, equipped: &EquippedEntity) -> CharacterInventory {
CharacterInventory{ CharacterInventory{
item_id_counter: 0, item_id_counter: 0,
items: items, items: items,
equipped: equipped.clone(),
} }
} }
@ -378,10 +369,22 @@ impl CharacterInventory {
inventory[slot].data1.copy_from_slice(&bytes[0..12]); inventory[slot].data1.copy_from_slice(&bytes[0..12]);
inventory[slot].data2.copy_from_slice(&bytes[12..16]); inventory[slot].data2.copy_from_slice(&bytes[12..16]);
inventory[slot].item_id = item.item_id().0; inventory[slot].item_id = item.item_id().0;
// does this do anything?
inventory[slot].equipped = if item.equipped() { 1 } else { 0 };
// because this actually equips the item
inventory[slot].flags |= if item.equipped() { 8 } else { 0 };
inventory[slot].equipped = 0;
inventory[slot].flags = 0;
if let InventoryItem::Individual(individual_item) = item {
if self.equipped.is_equipped(&individual_item.entity_id) {
if let ItemDetail::Unit(_) = individual_item.item {
inventory[slot].data1[4] = self.equipped.unit.iter()
.enumerate()
.find(|(_, u_id)| **u_id == Some(individual_item.entity_id))
.map(|(a, b)| a)
.unwrap_or(0) as u8
}
inventory[slot].equipped = 1;
inventory[slot].flags |= 8;
}
}
inventory inventory
}) })
} }
@ -413,7 +416,7 @@ impl CharacterInventory {
.filter(|(_, item)| { .filter(|(_, item)| {
if let InventoryItem::Individual(individual_inventory_item) = item { if let InventoryItem::Individual(individual_inventory_item) = item {
if let ItemDetail::Mag(_) = &individual_inventory_item.item { if let ItemDetail::Mag(_) = &individual_inventory_item.item {
return individual_inventory_item.equipped
return self.equipped.is_equipped(&individual_inventory_item.entity_id)
} }
} }
false false
@ -431,7 +434,7 @@ impl CharacterInventory {
.filter(|(_, item)| { .filter(|(_, item)| {
if let InventoryItem::Individual(individual_inventory_item) = item { if let InventoryItem::Individual(individual_inventory_item) = item {
if let ItemDetail::Armor(_) = &individual_inventory_item.item { if let ItemDetail::Armor(_) = &individual_inventory_item.item {
return individual_inventory_item.equipped
return self.equipped.is_equipped(&individual_inventory_item.entity_id)
} }
} }
false false
@ -449,7 +452,7 @@ impl CharacterInventory {
.filter(|(_, item)| { .filter(|(_, item)| {
if let InventoryItem::Individual(individual_inventory_item) = item { if let InventoryItem::Individual(individual_inventory_item) = item {
if let ItemDetail::Shield(_) = &individual_inventory_item.item { if let ItemDetail::Shield(_) = &individual_inventory_item.item {
return individual_inventory_item.equipped
return self.equipped.is_equipped(&individual_inventory_item.entity_id)
} }
} }
false false
@ -467,7 +470,7 @@ impl CharacterInventory {
.filter(|(_, item)| { .filter(|(_, item)| {
if let InventoryItem::Individual(individual_inventory_item) = item { if let InventoryItem::Individual(individual_inventory_item) = item {
if let ItemDetail::Weapon(_) = &individual_inventory_item.item { if let ItemDetail::Weapon(_) = &individual_inventory_item.item {
return individual_inventory_item.equipped
return self.equipped.is_equipped(&individual_inventory_item.entity_id)
} }
} }
false false
@ -509,7 +512,6 @@ impl CharacterInventory {
entity_id: floor_item.entity_id, entity_id: floor_item.entity_id,
item_id: floor_item.item_id, item_id: floor_item.item_id,
item: floor_item.item.clone(), item: floor_item.item.clone(),
equipped: false,
})); }));
if let Some(InventoryItem::Individual(new_item)) = self.items.last() { if let Some(InventoryItem::Individual(new_item)) = self.items.last() {
@ -573,7 +575,6 @@ impl CharacterInventory {
entity_id: individual_bank_item.entity_id, entity_id: individual_bank_item.entity_id,
item_id: individual_bank_item.item_id, item_id: individual_bank_item.item_id,
item: individual_bank_item.item.clone(), item: individual_bank_item.item.clone(),
equipped: false,
})); }));
(true, self.count()-1) (true, self.count()-1)
}, },
@ -582,7 +583,7 @@ impl CharacterInventory {
.enumerate() .enumerate()
.find_map(|(index, item)| { .find_map(|(index, item)| {
if let InventoryItem::Stacked(stacked_inventory_item) = item { if let InventoryItem::Stacked(stacked_inventory_item) = item {
if stacked_inventory_item.tool == stacked_inventory_item.tool {
if stacked_bank_item.tool == stacked_inventory_item.tool {
return Some((index, stacked_inventory_item)) return Some((index, stacked_inventory_item))
} }
} }
@ -639,6 +640,53 @@ impl CharacterInventory {
self.items = sorted_items; self.items = sorted_items;
} }
pub fn equip(&mut self, id: &ClientItemId, equip_slot: u8) {
for item in &self.items {
if let InventoryItem::Individual(inventory_item) = item {
if inventory_item.item_id == *id {
match inventory_item.item {
ItemDetail::Weapon(_) => self.equipped.weapon = Some(inventory_item.entity_id),
ItemDetail::Armor(_) => self.equipped.armor = Some(inventory_item.entity_id),
ItemDetail::Shield(_) => self.equipped.shield = Some(inventory_item.entity_id),
ItemDetail::Unit(_) => {
if let Some(unit) = self.equipped.unit.get_mut(equip_slot as usize) {
*unit = Some(inventory_item.entity_id)
}
}
ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id),
_ => {}
}
}
}
}
}
pub fn unequip(&mut self, id: &ClientItemId) {
for item in &self.items {
if let InventoryItem::Individual(inventory_item) = item {
if inventory_item.item_id == *id {
match inventory_item.item {
ItemDetail::Weapon(_) => self.equipped.weapon = None,
ItemDetail::Armor(_) => {
self.equipped.armor = None;
self.equipped.unit = [None; 4];
}
ItemDetail::Shield(_) => self.equipped.shield = None,
ItemDetail::Unit(_) => {
for unit in self.equipped.unit.iter_mut() {
if *unit == Some(inventory_item.entity_id) {
*unit = None
}
}
}
ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id),
_ => {}
}
}
}
}
}
pub fn as_inventory_entity(&self, character_id: &CharacterEntityId) -> InventoryEntity { pub fn as_inventory_entity(&self, character_id: &CharacterEntityId) -> InventoryEntity {
InventoryEntity { InventoryEntity {
items: self.items.iter() items: self.items.iter()
@ -673,25 +721,7 @@ impl CharacterInventory {
} }
pub fn as_equipped_entity(&self) -> EquippedEntity { pub fn as_equipped_entity(&self) -> EquippedEntity {
self.items.iter()
.fold((EquippedEntity::default(), 0), |(mut equipped, mut unit_slot), item| {
if let InventoryItem::Individual(individual_item) = item {
if individual_item.equipped {
match individual_item.item {
ItemDetail::Weapon(_) => equipped.weapon = Some(individual_item.entity_id),
ItemDetail::Armor(_) => equipped.armor = Some(individual_item.entity_id),
ItemDetail::Shield(_) => equipped.shield = Some(individual_item.entity_id),
ItemDetail::Unit(_) => {
equipped.unit[unit_slot] = Some(individual_item.entity_id);
unit_slot += 1;
}
ItemDetail::Mag(_) => equipped.mag = Some(individual_item.entity_id),
_ => {},
}
}
}
(equipped, unit_slot)
}).0
self.equipped.clone()
} }
} }

109
src/ship/items/manager.rs

@ -97,7 +97,6 @@ impl ItemManager {
entity_id: item.id, entity_id: item.id,
item_id: self.next_global_item_id(), item_id: self.next_global_item_id(),
item: item.item, item: item.item,
equipped: equipped.is_equipped(&item.id),
}) })
}, },
InventoryItemEntity::Stacked(items) => { InventoryItemEntity::Stacked(items) => {
@ -115,7 +114,7 @@ impl ItemManager {
}) })
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let character_inventory = CharacterInventory::new(inventory_items);
let character_inventory = CharacterInventory::new(inventory_items, &equipped);
let bank_items = bank.items.into_iter() let bank_items = bank.items.into_iter()
.map(|item| -> Result<BankItem, ItemManagerError> { .map(|item| -> Result<BankItem, ItemManagerError> {
@ -147,102 +146,6 @@ impl ItemManager {
self.character_inventory.insert(character.id, character_inventory); self.character_inventory.insert(character.id, character_inventory);
self.character_bank.insert(character.id, character_bank); self.character_bank.insert(character.id, character_bank);
Ok(()) Ok(())
/*
let items = entity_gateway.get_items_by_character(&character.id).await?;
let inventory_items = items.clone().into_iter()
.filter_map(|item| {
match item.location {
ItemLocation::Inventory{slot, equipped, ..} => Some((item.id, item.item, slot, equipped)),
_ => None,
}
})
.fold(BTreeMap::new(), |mut acc, (id, item, slot, equipped)| {
if item.is_stackable() {
if let ItemDetail::Tool(tool) = item {
let inventory_item = acc.entry(slot).or_insert(InventoryItem::Stacked(StackedInventoryItem {
entity_ids: Vec::new(),
item_id: self.next_global_item_id(),
tool: tool,
}));
if let InventoryItem::Stacked(ref mut stacked_inventory_item) = inventory_item {
stacked_inventory_item.entity_ids.push(id);
}
}
}
else {
acc.insert(slot, InventoryItem::Individual(IndividualInventoryItem {
entity_id: id,
item_id: self.next_global_item_id(),
item: item,
equipped: equipped,
}));
}
acc
});
// TODO: not using BankName anymore, clean this up
let mut bank_items = items.into_iter()
.filter_map(|item| {
match item.location {
ItemLocation::Bank{name, ..} => Some((item.id, item.item, name)),
_ => None,
}
})
.fold(BTreeMap::new(), |mut acc, (id, item, name)| {
acc.entry(name).or_insert(Vec::new()).push((id, item));
acc
})
.into_iter()
.map(|(bank_name, bank_items)| {
let stacked_bank_items = bank_items.into_iter()
.fold(Vec::new(), |mut acc, (id, bank_item)| {
if bank_item.is_stackable() {
let existing_item = acc.iter_mut()
.find(|item| {
if let (BankItem::Stacked(stacked_bank_item), &ItemDetail::Tool(ref tool)) = (item, &bank_item) {
stacked_bank_item.tool == *tool
}
else {
false
}
});
match existing_item {
Some(item) => {
if let BankItem::Stacked(ref mut stacked_bank_item) = item {
stacked_bank_item.entity_ids.push(id);
}
}
None => {
if let ItemDetail::Tool(tool) = bank_item {
acc.push(BankItem::Stacked(StackedBankItem {
entity_ids: vec![id],
item_id: self.next_global_item_id(),
tool: tool,
}));
}
},
}
}
else {
acc.push(BankItem::Individual(IndividualBankItem {
entity_id: id,
item_id: self.next_global_item_id(),
item: bank_item,
}));
}
acc
});
(bank_name, CharacterBank::new(stacked_bank_items))
})
.collect::<BTreeMap<_, _>>();
let inventory = CharacterInventory::new(inventory_items.into_iter().map(|(_k, v)| v).take(30).collect());
self.character_inventory.insert(character.id, inventory);
self.character_bank.insert(character.id, bank_items.remove(&BankName("".to_string())).unwrap_or(CharacterBank::new(Vec::new())));
Ok(())
*/
} }
pub fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) { pub fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) {
@ -941,10 +844,7 @@ impl ItemManager {
equip_slot: u8) equip_slot: u8)
-> Result<(), ItemManagerError> { -> Result<(), ItemManagerError> {
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 mut inventory_item_handle = inventory.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
let slot = inventory_item_handle.get_slot();
let inventory_item = inventory_item_handle.item_mut().ok_or(ItemManagerError::CannotGetMutItem)?.individual().ok_or(ItemManagerError::CannotGetIndividualItem)?;
inventory_item.equipped = true;
inventory.equip(&item_id, equip_slot);
entity_gateway.set_character_equips(&character.id, &inventory.as_equipped_entity()).await?; entity_gateway.set_character_equips(&character.id, &inventory.as_equipped_entity()).await?;
Ok(()) Ok(())
} }
@ -955,10 +855,7 @@ impl ItemManager {
item_id: ClientItemId) item_id: ClientItemId)
-> Result<(), ItemManagerError> { -> Result<(), ItemManagerError> {
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 mut inventory_item_handle = inventory.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
let slot = inventory_item_handle.get_slot();
let inventory_item = inventory_item_handle.item_mut().ok_or(ItemManagerError::CannotGetMutItem)?.individual().ok_or(ItemManagerError::CannotGetIndividualItem)?;
inventory_item.equipped = false;
inventory.unequip(&item_id);
entity_gateway.set_character_equips(&character.id, &inventory.as_equipped_entity()).await?; entity_gateway.set_character_equips(&character.id, &inventory.as_equipped_entity()).await?;
Ok(()) Ok(())
} }

28
src/ship/packet/handler/message.rs

@ -316,7 +316,8 @@ where
EG: EntityGateway EG: EntityGateway
{ {
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
item_manager.player_equips_item(entity_gateway, &client.character, ClientItemId(pkt.item_id), pkt.sub_menu).await?;
let equip_slot = ((pkt.sub_menu & 0x7) - 1) % 4;
item_manager.player_equips_item(entity_gateway, &client.character, ClientItemId(pkt.item_id), equip_slot).await?;
Ok(Box::new(None.into_iter())) Ok(Box::new(None.into_iter()))
} }
@ -330,30 +331,7 @@ where
EG: EntityGateway EG: EntityGateway
{ {
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
let equipped_unit_ids: Vec<ClientItemId> = {
item_manager.player_unequips_item(entity_gateway, &client.character, ClientItemId(pkt.item_id)).await?;
let inventory = item_manager.get_character_inventory(&client.character).unwrap();
let ue_item = inventory.get_item_by_id(ClientItemId(pkt.item_id)).ok_or(ShipError::ItemError)?;
if let ItemType::Armor(_) = ue_item.item_type() {
inventory.items()
.iter()
.filter(|inv_item| {
if let ItemType::Unit(_) = inv_item.item_type() {
return inv_item.equipped()
}
false
})
.map(|u| u.item_id())
.collect()
} else {
Vec::new()
}
};
for unit_id in equipped_unit_ids {
item_manager.player_unequips_item(entity_gateway, &client.character, unit_id).await?;
}
item_manager.player_unequips_item(entity_gateway, &client.character, ClientItemId(pkt.item_id)).await?;
Ok(Box::new(None.into_iter())) Ok(Box::new(None.into_iter()))
} }

1
src/ship/shops/armor.rs

@ -85,7 +85,6 @@ impl ShopItem for ArmorShopItem {
ItemDetail::Unit(Unit { ItemDetail::Unit(Unit {
unit: *unit, unit: *unit,
modifier: None, modifier: None,
armor_slot: 0,
}) })
}, },
} }

6
tests/test_item_actions.rs

@ -38,7 +38,6 @@ 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,
armor_slot: 0,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -51,7 +50,6 @@ 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),
armor_slot: 0,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -127,7 +125,6 @@ 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,
armor_slot: 0,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -140,7 +137,6 @@ 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),
armor_slot: 1,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -207,7 +203,6 @@ 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,
armor_slot: 0,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -220,7 +215,6 @@ 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),
armor_slot: 0,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,

Loading…
Cancel
Save