Browse Source

magstuff

pull/112/head
jake 2 years ago
committed by andy
parent
commit
d3d8d766b8
  1. 2
      src/entity/gateway/inmemory.rs
  2. 3
      src/entity/gateway/postgres/postgres.rs
  3. 27
      src/entity/item/mag.rs
  4. 3
      src/entity/item/mod.rs
  5. 72
      src/ship/items/actions.rs
  6. 100
      src/ship/items/apply_item.rs
  7. 69
      src/ship/items/state.rs
  8. 6
      src/ship/packet/handler/message.rs
  9. 2
      src/ship/ship.rs

2
src/entity/gateway/inmemory.rs

@ -126,7 +126,7 @@ impl InMemoryGateway {
mag::MagModifier::MagCell(mag_cell_id) => {
if let Some(mag_cell) = items.get(mag_cell_id) {
if let ItemDetail::Tool(mag_cell) = mag_cell.item {
mag.apply_mag_cell(mag_cell.tool.try_into().unwrap())
mag.apply_mag_cell(mag_cell.tool.try_into().unwrap()).unwrap()
}
}
},

3
src/entity/gateway/postgres/postgres.rs

@ -65,6 +65,7 @@ impl PostgresGateway {
}
// TODO: remove unwraps, return Result
async fn apply_item_modifications(conn: &mut sqlx::PgConnection, item: ItemEntity) -> ItemEntity
{
let ItemEntity {id, item} = item;
@ -108,7 +109,7 @@ async fn apply_item_modifications(conn: &mut sqlx::PgConnection, item: ItemEntit
mag.bank()
},
mag::MagModifier::MagCell(_) => {
mag.apply_mag_cell(mag::MagCell::try_from(Into::<tool::Tool>::into(cell.unwrap().0).tool).unwrap())
mag.apply_mag_cell(mag::MagCell::try_from(Into::<tool::Tool>::into(cell.unwrap().0).tool).unwrap()).unwrap()
},
mag::MagModifier::OwnerChange(class, section_id) => {
mag.change_owner(class, section_id)

27
src/entity/item/mag.rs

@ -1,3 +1,4 @@
use thiserror::Error;
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use crate::entity::item::tool::ToolType;
@ -419,9 +420,9 @@ pub enum MagCell {
}
impl std::convert::TryFrom<ToolType> for MagCell {
type Error = ();
type Error = MagCellError;
fn try_from(tool: ToolType) -> Result<MagCell, ()> {
fn try_from(tool: ToolType) -> Result<MagCell, MagCellError> {
match tool {
ToolType::CellOfMag502 => Ok(MagCell::CellOfMag502),
ToolType::CellOfMag213 => Ok(MagCell::CellOfMag213),
@ -448,7 +449,7 @@ impl std::convert::TryFrom<ToolType> for MagCell {
ToolType::YahoosEngine => Ok(MagCell::YahoosEngine),
ToolType::DPhotonCore => Ok(MagCell::DPhotonCore),
ToolType::LibertaKit => Ok(MagCell::LibertaKit),
_ => Err(()),
_ => Err(MagCellError::IsNotMagCell),
}
}
}
@ -509,6 +510,15 @@ impl MagAttributeOrdering {
}
}
#[derive(Error, Debug)]
pub enum MagCellError {
#[error("not a mag cell")]
IsNotMagCell,
#[error("mag is rare")]
IsRareMag,
}
#[derive(Debug, Clone, PartialEq)]
pub enum MagModifier {
FeedMag{
@ -1047,7 +1057,10 @@ impl Mag {
}
// TODO: this needs more checks on validity
pub fn apply_mag_cell(&mut self, mag_cell: MagCell) {
pub fn apply_mag_cell(&mut self, mag_cell: MagCell) -> Result<(), MagCellError> {
if self.is_rare_item() {
return Err(MagCellError::IsRareMag)
}
self.mag = match mag_cell {
MagCell::CellOfMag502 => {
match self.id {
@ -1097,11 +1110,11 @@ impl Mag {
MagCell::YahoosEngine => MagType::Yahoo,
MagCell::DPhotonCore => MagType::GaelGiel,
MagCell::LibertaKit => MagType::Agastya,
}
};
Ok(())
}
// TODO: is this even needed? mags are not shop sellable...yet
pub fn is_rare_item(self) -> bool {
pub fn is_rare_item(&self) -> bool {
matches!(
self.mag,
MagType::Pitri

3
src/entity/item/mod.rs

@ -46,8 +46,9 @@ pub enum ItemNote {
y: f32,
z: f32,
},
Consumed,
Consumed, // TODO: character_id
FedToMag {
//character_id: CharacterEntityId,
mag: ItemEntityId,
},
BoughtAtShop {

72
src/ship/items/actions.rs

@ -594,7 +594,7 @@ fn use_consumed_item(character: CharacterEntity)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), CharacterEntity), ItemStateError>> + Send + 'a>>
{
move |(mut item_state, mut transaction), inventory_item| {
move |(mut item_state, transaction), inventory_item| {
let mut character = character.clone();
Box::pin(async move {
let mut transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| {
@ -623,7 +623,6 @@ where
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), new_character) = ItemStateAction::default()
//.act(consume_inventory_tool(character.id, *item_id, 1))
.act(take_item_from_inventory(character.id, *item_id, amount))
.act(use_consumed_item(character.clone()))
.commit((item_state_proxy, transaction))
@ -633,3 +632,72 @@ where
Ok((transaction, ()))
}).await
}
fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemId)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), CharacterEntity), ItemStateError>> + Send + 'a>>
{
move |(mut item_state, transaction), tool| {
let character = character.clone();
Box::pin(async move {
let mut inventory = item_state.inventory(&character.id)?;
let mag_entity = inventory.get_by_client_id_mut(&mag_item_id)
.ok_or_else(|| ItemStateError::InvalidItemId(mag_item_id))?
.item
.as_individual_mut()
.ok_or_else(|| ItemStateError::NotAMag(mag_item_id))?;
let mag_entity_id = mag_entity.entity_id;
let mut transaction = tool.with_entity_id(transaction, |mut transaction, entity_id| {
async move {
transaction.gateway().add_item_note(&entity_id, ItemNote::FedToMag {
//character_id: character.id,
mag: mag_entity_id,
}).await?;
transaction.gateway().feed_mag(&mag_entity_id, &entity_id).await?;
Ok(transaction)
}}).await?;
let food_tool = tool
.item
.stacked()
.ok_or_else(|| ItemStateError::NotMagFood(tool.item_id))?
.tool
.tool;
let mag_entity = mag_entity
.as_mag_mut()
.ok_or_else(|| ItemStateError::NotAMag(mag_item_id))?;
mag_entity.feed(food_tool);
transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
item_state.set_inventory(inventory);
Ok(((item_state, transaction), character))
})
}
}
pub async fn feed_mag<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &CharacterEntity,
mag_item_id: &ClientItemId,
tool_item_id: &ClientItemId,
) -> Result<(), ItemStateError>
where
EG: EntityGateway,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), _) = ItemStateAction::default()
.act(take_item_from_inventory(character.id, *tool_item_id, 1))
.act(feed_mag_item(character.clone(), *mag_item_id))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, ()))
}).await
}

100
src/ship/items/apply_item.rs

@ -1,10 +1,12 @@
use thiserror::Error;
use std::convert::TryFrom;
use std::convert::TryInto;
use crate::entity::gateway::{EntityGateway, GatewayError};
use crate::entity::character::CharacterEntity;
use crate::entity::item::mag::MagCell;
use crate::entity::item::mag::{MagCell, MagCellError};
use crate::entity::item::tool::ToolType;
use crate::entity::item::ItemDetail;
use crate::ship::items::state::{ItemStateProxy, InventoryState, InventoryItem, InventoryItemDetail};
use crate::entity::item::{ItemDetail, ItemEntityId};
use crate::ship::items::state::{ItemStateProxy, InventoryState, InventoryItem, InventoryItemDetail, ItemStateError};
#[derive(Error, Debug)]
@ -17,6 +19,18 @@ pub enum ApplyItemError {
InvalidItem,
#[error("gateway error {0}")]
GatewayError(#[from] GatewayError),
#[error("itemstate error {0}")]
ItemStateError(Box<ItemStateError>),
#[error("magcell error {0}")]
MagCellError(#[from] MagCellError),
}
impl From<ItemStateError> for ApplyItemError {
fn from(other: ItemStateError) -> ApplyItemError {
ApplyItemError::ItemStateError(Box::new(other))
}
}
// TODO: make all these functions not-pub
@ -81,9 +95,34 @@ async fn mag_cell<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &Consum
Ok(())
}
*/
async fn mag_cell<'a, EG>(item_state: &mut ItemStateProxy<'a>,
entity_gateway: &mut EG,
character: &CharacterEntity,
cell_entity_id: ItemEntityId,
mag_cell_type: MagCell)
-> Result<(), ApplyItemError>
where
EG: EntityGateway + ?Sized,
{
let mut inventory = item_state.inventory(&character.id)?;
let (mag_entity_id, mag) = inventory.equipped_mag_mut()
.ok_or_else(|| ApplyItemError::ItemNotEquipped)?;
mag.apply_mag_cell(mag_cell_type)?;
entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await?;
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
item_state.set_inventory(inventory);
Ok(())
}
/*
pub async fn cell_of_mag_502<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag502).await
mag_cell(entity_gateway, inventory, MagCell::CellOfMag502).await
}
pub async fn cell_of_mag_213<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
@ -179,7 +218,15 @@ pub async fn liberta_kit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell:
}
*/
async fn apply_tool<'a, EG: EntityGateway + ?Sized>(item_state: &mut ItemStateProxy<'a>, entity_gateway: &mut EG, character: &mut CharacterEntity, tool: ToolType) -> Result<(), ApplyItemError> {
async fn apply_tool<'a, EG>(item_state: &mut ItemStateProxy<'a>,
entity_gateway: &mut EG,
character: &mut CharacterEntity,
entity_id: ItemEntityId,
tool: ToolType)
-> Result<(), ApplyItemError>
where
EG: EntityGateway + ?Sized,
{
match tool {
ToolType::PowerMaterial => power_material(entity_gateway, character).await,
ToolType::MindMaterial => mind_material(entity_gateway, character).await,
@ -195,6 +242,32 @@ async fn apply_tool<'a, EG: EntityGateway + ?Sized>(item_state: &mut ItemStatePr
ToolType::Difluid => Ok(()),
ToolType::Trifluid => Ok(()),
ToolType::HuntersReport => Ok(()),
ToolType::CellOfMag502
| ToolType::CellOfMag213
| ToolType::PartsOfRobochao
| ToolType::HeartOfOpaOpa
| ToolType::HeartOfPian
| ToolType::HeartOfChao
| ToolType::HeartOfAngel
| ToolType::KitOfHamburger
| ToolType::PanthersSpirit
| ToolType::KitOfMark3
| ToolType::KitOfMasterSystem
| ToolType::KitOfGenesis
| ToolType::KitOfSegaSaturn
| ToolType::KitOfDreamcast
| ToolType::Tablet
| ToolType::DragonScale
| ToolType::HeavenStrikerCoat
| ToolType::PioneerParts
| ToolType::AmitiesMemo
| ToolType::HeartOfMorolian
| ToolType::RappysBeak
| ToolType::YahoosEngine
| ToolType::DPhotonCore
| ToolType::LibertaKit => {
mag_cell(item_state, entity_gateway, character, entity_id, tool.try_into()?).await
}
// TODO: rest of these
_ => Err(ApplyItemError::InvalidItem)
}
@ -203,17 +276,18 @@ async fn apply_tool<'a, EG: EntityGateway + ?Sized>(item_state: &mut ItemStatePr
pub async fn apply_item<'a, EG: EntityGateway + ?Sized>(item_state: &mut ItemStateProxy<'a>, entity_gateway: &mut EG, character: &mut CharacterEntity, item: InventoryItem) -> Result<(), ApplyItemError> {
let item_detail = match item.item {
match item.item {
InventoryItemDetail::Individual(individual_item) => {
individual_item.item
match individual_item.item {
ItemDetail::Tool(tool) => apply_tool(item_state, entity_gateway, character, individual_item.entity_id, tool.tool).await,
_ => Err(ApplyItemError::InvalidItem)
}
},
InventoryItemDetail::Stacked(stacked_item) => {
ItemDetail::Tool(stacked_item.tool)
for entity_id in stacked_item.entity_ids {
apply_tool(item_state, entity_gateway, character, entity_id, stacked_item.tool.tool).await?
}
Ok(())
},
};
match item_detail {
ItemDetail::Tool(tool) => apply_tool(item_state, entity_gateway, character, tool.tool).await,
_ => Err(ApplyItemError::InvalidItem)
}
}

69
src/ship/items/state.rs

@ -36,6 +36,9 @@ pub enum ItemStateError {
#[error("bank error {0}")]
BankError(#[from] BankError),
#[error("invalid item id {0}")]
InvalidItemId(ClientItemId),
#[error("invalid drop? {0:?} (this shouldn't occur)")]
BadItemDrop(ItemDrop),
@ -56,6 +59,12 @@ pub enum ItemStateError {
#[error("apply item {0}")]
ApplyItemError(#[from] crate::ship::items::apply_item::ApplyItemError),
#[error("item is not a mag {0}")]
NotAMag(ClientItemId),
#[error("item is not mag food {0}")]
NotMagFood(ClientItemId),
}
pub enum FloorType {
@ -207,6 +216,22 @@ pub struct IndividualItemDetail {
pub item: ItemDetail,
}
impl IndividualItemDetail {
pub fn as_mag(&self) -> Option<&Mag> {
match &self.item {
ItemDetail::Mag(mag) => Some(mag),
_ => None
}
}
pub fn as_mag_mut(&mut self) -> Option<&mut Mag> {
match &mut self.item {
ItemDetail::Mag(mag) => Some(mag),
_ => None
}
}
}
#[derive(Clone, Debug)]
pub struct StackedItemDetail {
pub entity_ids: Vec<ItemEntityId>,
@ -226,19 +251,35 @@ pub enum InventoryItemDetail {
}
impl InventoryItemDetail {
fn stacked(&self) -> Option<&StackedItemDetail> {
// TODO: rename as_stacked for consistency
pub fn stacked(&self) -> Option<&StackedItemDetail> {
match self {
InventoryItemDetail::Stacked(sitem) => Some(sitem),
_ => None,
}
}
fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> {
// TODO: rename as_stacked_mut for consistency
pub fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> {
match self {
InventoryItemDetail::Stacked(sitem) => Some(sitem),
_ => None,
}
}
pub fn as_individual(&self) -> Option<&IndividualItemDetail> {
match self {
InventoryItemDetail::Individual(iitem) => Some(iitem),
_ => None,
}
}
pub fn as_individual_mut(&mut self) -> Option<&mut IndividualItemDetail> {
match self {
InventoryItemDetail::Individual(iitem) => Some(iitem),
_ => None,
}
}
pub fn as_client_bytes(&self) -> [u8; 16] {
match self {
InventoryItemDetail::Individual(item) => {
@ -633,6 +674,18 @@ impl InventoryState {
}
}
pub fn get_by_client_id(&self, item_id: &ClientItemId) -> Option<&InventoryItem> {
self.inventory.0
.iter()
.find(|i| i.item_id == *item_id)
}
pub fn get_by_client_id_mut(&mut self, item_id: &ClientItemId) -> Option<&mut InventoryItem> {
self.inventory.0
.iter_mut()
.find(|i| i.item_id == *item_id)
}
pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
if self.meseta.0 == 999999 {
return Err(ItemStateError::FullOfMeseta)
@ -704,6 +757,18 @@ impl InventoryState {
}
}
pub fn equipped_mag_mut(&mut self) -> Option<(ItemEntityId, &mut Mag)> {
let mag_id = self.equipped.mag?;
self.inventory.0
.iter_mut()
.filter_map(|i| {
let individual = i.item.as_individual_mut()?;
let entity_id = individual.entity_id;
Some((entity_id, individual.as_mag_mut()?))
})
.find(|(entity_id, _)| *entity_id == mag_id)
}
pub fn sort(&mut self, item_ids: &Vec<ClientItemId>) {
self.inventory.0.sort_by(|a, b| {
let a_index = item_ids.iter().position(|item_id| *item_id == a.item_id);

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

@ -8,7 +8,7 @@ use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::items::{ItemManager, ClientItemId};
use crate::ship::packet::builder;
use crate::ship::items::state::ItemState;
use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item};
use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag};
pub async fn request_exp<EG: EntityGateway>(id: ClientId,
request_exp: &RequestExp,
@ -320,13 +320,13 @@ pub async fn player_feed_mag<EG>(id: ClientId,
entity_gateway: &mut EG,
client_location: &ClientLocation,
clients: &Clients,
item_manager: &mut ItemManager)
item_state: &mut ItemState)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
where
EG: EntityGateway
{
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
item_manager.player_feeds_mag_item(entity_gateway, &client.character, ClientItemId(mag_feed.mag_id), ClientItemId(mag_feed.item_id)).await?;
feed_mag(item_state, entity_gateway, &client.character, &ClientItemId(mag_feed.mag_id), &ClientItemId(mag_feed.item_id)).await?;
let mag_feed = mag_feed.clone();
Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter()

2
src/ship/ship.rs

@ -511,7 +511,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
},
GameMessage::PlayerFeedMag(player_feed_mag) => {
let block = self.blocks.with_client(id, &self.clients)?;
handler::message::player_feed_mag(id, player_feed_mag, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_manager).await?
handler::message::player_feed_mag(id, player_feed_mag, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_state).await?
},
GameMessage::PlayerEquipItem(player_equip_item) => {
handler::message::player_equips_item(id, player_equip_item, &mut self.entity_gateway, &self.clients, &mut self.item_state).await?

Loading…
Cancel
Save