You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

395 lines
18 KiB

use std::convert::TryInto;
use futures::future::join_all;
use thiserror::Error;
use anyhow::Context;
use rand::SeedableRng;
use rand::distributions::{WeightedIndex, Distribution};
use entity::gateway::{EntityGateway, GatewayError};
use entity::character::{CharacterEntity, TechLevel};
use entity::item::mag::{MagCell, MagCellError};
use entity::item::tool::{Tool, ToolType};
use entity::item::tech::TechniqueDisk;
use entity::item::{ItemDetail, ItemEntityId};
use entity::item::weapon::WeaponModifier;
use crate::ship::items::state::ItemStateProxy;
use crate::ship::items::inventory::InventoryItemDetail;
#[derive(Error, Debug)]
pub enum ApplyItemError {
#[error("no character")]
NoCharacter,
#[error("item not equipped")]
ItemNotEquipped,
#[error("could not use item invalid item")]
InvalidItem,
#[error("invalid tool")]
InvalidTool,
#[error("gateway error {0}")]
GatewayError(#[from] GatewayError),
#[error("magcell error {0}")]
MagCellError(#[from] MagCellError),
}
#[derive(Debug, Clone)]
pub enum ApplyItemAction {
UpdateCharacter(Box<CharacterEntity>),
CreateItem(ItemDetail),
//TransformItem(ItemDetail),
//RemoveItem,
}
async fn power_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, anyhow::Error> {
character.materials.power += 1;
entity_gateway.save_character(character).await?;
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
async fn mind_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, anyhow::Error> {
character.materials.mind += 1;
entity_gateway.save_character(character).await.unwrap();
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
async fn evade_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, anyhow::Error> {
character.materials.evade += 1;
entity_gateway.save_character(character).await.unwrap();
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
async fn def_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, anyhow::Error> {
character.materials.def += 1;
entity_gateway.save_character(character).await.unwrap();
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
async fn luck_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, anyhow::Error> {
character.materials.luck += 1;
entity_gateway.save_character(character).await.unwrap();
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
async fn hp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, anyhow::Error> {
character.materials.hp += 1;
entity_gateway.save_character(character).await.unwrap();
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
async fn tp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, anyhow::Error> {
character.materials.tp += 1;
entity_gateway.save_character(character).await.unwrap();
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
/*
async fn mag_cell<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory, mag_cell_type: MagCell) -> Result<(), ApplyItemError> {
let mut mag_handle = inventory.get_equipped_mag_handle().ok_or(ApplyItemError::ItemNotEquipped)?;
let mag_item = mag_handle.item_mut()
.ok_or(ApplyItemError::InvalidItem)?;
let actual_mag = mag_item
.individual_mut()
.ok_or(ApplyItemError::InvalidItem)?
.mag_mut()
.ok_or(ApplyItemError::InvalidItem)?;
actual_mag.apply_mag_cell(mag_cell_type);
for mag_entity_id in mag_item.entity_ids() {
for cell_entity_id in used_cell.entity_ids() {
entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await.unwrap();
}
}
Ok(())
}
*/
async fn mag_cell<'a, EG>(item_state: &mut ItemStateProxy,
entity_gateway: &mut EG,
character: &CharacterEntity,
cell_entity_id: ItemEntityId,
mag_cell_type: MagCell)
-> Result<Vec<ApplyItemAction>, anyhow::Error>
where
EG: EntityGateway + ?Sized,
{
let mut inventory = item_state.inventory(&character.id).await?;
let (mag_entity_id, mag) = inventory.equipped_mag_mut()
.ok_or(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).await;
Ok(Vec::new())
}
/*
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, 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> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag213).await
}
pub async fn parts_of_robochao<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::PartsOfRobochao).await
}
pub async fn heart_of_opaopa<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfOpaOpa).await
}
pub async fn heart_of_pian<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfPian).await
}
pub async fn heart_of_chao<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfChao).await
}
pub async fn heart_of_angel<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfAngel).await
}
pub async fn kit_of_hamburger<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfHamburger).await
}
pub async fn panthers_spirit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::PanthersSpirit).await
}
pub async fn kit_of_mark3<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMark3).await
}
pub async fn kit_of_master_system<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMasterSystem).await
}
pub async fn kit_of_genesis<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfGenesis).await
}
pub async fn kit_of_sega_saturn<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfSegaSaturn).await
}
pub async fn kit_of_dreamcast<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfDreamcast).await
}
pub async fn tablet<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::Tablet).await
}
pub async fn dragon_scale<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::DragonScale).await
}
pub async fn heaven_striker_coat<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeavenStrikerCoat).await
}
pub async fn pioneer_parts<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::PioneerParts).await
}
pub async fn amities_memo<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::AmitiesMemo).await
}
pub async fn heart_of_morolian<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfMorolian).await
}
pub async fn rappys_beak<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::RappysBeak).await
}
pub async fn yahoos_engine<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::YahoosEngine).await
}
pub async fn d_photon_core<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::DPhotonCore).await
}
pub async fn liberta_kit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::LibertaKit).await
}
*/
fn jack_o_lantern() -> Result<Vec<ApplyItemAction>, anyhow::Error>
{
let mag_rate = WeightedIndex::new([13, 13, 13, 13, 12, 12, 12, 12]).unwrap();
let mag_type = match mag_rate.sample(&mut rand_chacha::ChaChaRng::from_entropy()) {
0 => ToolType::CellOfMag502,
1 => ToolType::CellOfMag213,
2 => ToolType::HeartOfChuChu,
3 => ToolType::HeartOfKapuKapu,
4 => ToolType::PartsOfRobochao,
5 => ToolType::HeartOfOpaOpa,
6 => ToolType::HeartOfPian,
7 => ToolType::HeartOfChao,
_ => unreachable!(),
};
Ok(vec![ApplyItemAction::CreateItem(ItemDetail::Tool(Tool {tool: mag_type}))])
}
async fn weapon_grind<'a, EG>(item_state: &mut ItemStateProxy,
entity_gateway: &mut EG,
character: &mut CharacterEntity,
entity_id: ItemEntityId,
grind: u32,)
-> Result<Vec<ApplyItemAction>, anyhow::Error>
where
EG: EntityGateway + ?Sized,
{
let modifier = WeaponModifier::AddGrind {
amount: grind,
grinder: entity_id,
};
let mut inventory = item_state.inventory(&character.id).await?;
let (weapon_entity_id, weapon) = inventory.equipped_weapon_mut()
.ok_or(ApplyItemError::ItemNotEquipped)?;
weapon.apply_modifier(&modifier);
entity_gateway.add_weapon_modifier(&weapon_entity_id, &modifier).await?;
item_state.set_inventory(inventory).await;
Ok(Vec::new())
}
async fn apply_tool<'a, EG>(item_state: &mut ItemStateProxy,
entity_gateway: &mut EG,
character: &mut CharacterEntity,
entity_id: ItemEntityId,
tool: ToolType)
-> Result<Vec<ApplyItemAction>, anyhow::Error>
where
EG: EntityGateway + ?Sized,
{
match tool {
ToolType::PowerMaterial => power_material(entity_gateway, character).await,
ToolType::MindMaterial => mind_material(entity_gateway, character).await,
ToolType::EvadeMaterial => evade_material(entity_gateway, character).await,
ToolType::DefMaterial => def_material(entity_gateway, character).await,
ToolType::LuckMaterial => luck_material(entity_gateway, character).await,
ToolType::HpMaterial => hp_material(entity_gateway, character).await,
ToolType::TpMaterial => tp_material(entity_gateway, character).await,
ToolType::Monomate => Ok(Vec::new()),
ToolType::Dimate => Ok(Vec::new()),
ToolType::Trimate => Ok(Vec::new()),
ToolType::Monofluid => Ok(Vec::new()),
ToolType::Difluid => Ok(Vec::new()),
ToolType::Trifluid => Ok(Vec::new()),
ToolType::SolAtomizer => Ok(Vec::new()),
ToolType::MoonAtomizer => Ok(Vec::new()),
ToolType::StarAtomizer => Ok(Vec::new()),
ToolType::Telepipe => Ok(Vec::new()),
ToolType::Antidote => Ok(Vec::new()),
ToolType::Antiparalysis => Ok(Vec::new()),
ToolType::TrapVision => Ok(Vec::new()),
ToolType::ScapeDoll => Ok(Vec::new()),
ToolType::Monogrinder => weapon_grind(item_state, entity_gateway, character, entity_id, 1).await,
ToolType::Digrinder => weapon_grind(item_state, entity_gateway, character, entity_id, 2).await,
ToolType::Trigrinder => weapon_grind(item_state, entity_gateway, character, entity_id, 3).await,
ToolType::HuntersReport => Ok(Vec::new()),
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
}
ToolType::JackOLantern => jack_o_lantern(),
// TODO: rest of these
_ => Err(anyhow::Error::from(ApplyItemError::InvalidTool))
.with_context(|| {
format!("invalid tool {tool:?}")
})
}
}
async fn apply_tech<'a, EG>(_item_state: &mut ItemStateProxy,
entity_gateway: &mut EG,
character: &mut CharacterEntity,
_entity_id: ItemEntityId,
tech: TechniqueDisk)
-> Result<Vec<ApplyItemAction>, anyhow::Error>
where
EG: EntityGateway + ?Sized,
{
// TODO: make sure the class can learn that specific tech
character.techs.set_tech(tech.tech, TechLevel(tech.level as u8));
entity_gateway.save_character(character).await.unwrap();
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
pub async fn apply_item<'a, EG>(item_state: &'a mut ItemStateProxy,
entity_gateway: &'a mut EG,
character: &'a mut CharacterEntity,
item: InventoryItemDetail
) -> Result<Vec<ApplyItemAction>, anyhow::Error>
where
EG: EntityGateway + ?Sized + Clone + 'a
{
match item {
InventoryItemDetail::Individual(individual_item) => {
match individual_item.item {
ItemDetail::Tool(tool) => apply_tool(item_state, entity_gateway, character, individual_item.entity_id, tool.tool).await,
ItemDetail::TechniqueDisk(tech) => apply_tech(item_state, entity_gateway, character, individual_item.entity_id, tech).await,
_ => Err(anyhow::Error::from(ApplyItemError::InvalidItem))
.with_context(|| {
format!("item {individual_item:?}")
})
}
},
InventoryItemDetail::Stacked(stacked_item) => {
Ok(join_all(stacked_item.entity_ids.iter()
.map(|entity_id| {
let mut entity_gateway = entity_gateway.clone();
let mut character = character.clone();
let mut item_state = item_state.clone();
async move {
apply_tool(&mut item_state, &mut entity_gateway, &mut character, *entity_id, stacked_item.tool.tool).await
}
})
.collect::<Vec<_>>())
.await
.into_iter()
.collect::<Result<Vec<Vec<_>>, _>>()?
.into_iter()
.flatten()
.collect())
},
}
}