Browse Source

Merge pull request 'mag feeding!' (#207) from mag_feeding into master

pbs
jake 4 years ago
parent
commit
b0be92cb50
  1. 1
      Cargo.toml
  2. 32
      data/item_stats/mag_stats.toml
  3. 4
      src/entity/character.rs
  4. 12
      src/entity/gateway/entitygateway.rs
  5. 61
      src/entity/gateway/inmemory.rs
  6. 802
      src/entity/item/mag.rs
  7. 3
      src/entity/item/mod.rs
  8. 36
      src/entity/item/tool.rs
  9. 2
      src/lib.rs
  10. 16
      src/login/character.rs
  11. 13
      src/ship/drops/rare_drop_table.rs
  12. 53
      src/ship/items/inventory.rs
  13. 163
      src/ship/items/manager.rs
  14. 1
      src/ship/items/mod.rs
  15. 175
      src/ship/items/use_tool.rs
  16. 64
      src/ship/packet/handler/message.rs
  17. 3
      src/ship/ship.rs
  18. 210
      tests/test_mags.rs

1
Cargo.toml

@ -28,4 +28,5 @@ derive_more = { version = "0.99.3", features = ["display"]}
thiserror = "1.0.15" thiserror = "1.0.15"
ages-prs = "0.1" ages-prs = "0.1"
async-trait = "0.1.31" async-trait = "0.1.31"
lazy_static = "1.4.0"

32
data/item_stats/mag_stats.toml

@ -6,96 +6,124 @@ feed_table = 1
[Mitra] [Mitra]
feed_table = 3 feed_table = 3
photon_blast = "Pilla"
[Surya] [Surya]
feed_table = 3 feed_table = 3
photon_blast = "Leilla"
[Vayu] [Vayu]
feed_table = 4 feed_table = 4
photon_blast = "MyllaYoulla"
[Varaha] [Varaha]
feed_table = 4 feed_table = 4
photon_blast = "Leilla"
[Kama] [Kama]
feed_table = 4 feed_table = 4
photon_blast = "Pilla"
[Ushasu] [Ushasu]
feed_table = 4 feed_table = 4
photon_blast = "Leilla"
[Apsaras] [Apsaras]
feed_table = 4 feed_table = 4
photon_blast = "Estlla"
[Kumara] [Kumara]
feed_table = 4 feed_table = 4
photon_blast = "Leilla"
[Kaitabha] [Kaitabha]
feed_table = 4 feed_table = 4
photon_blast = "MyllaYoulla"
[Tapas] [Tapas]
feed_table = 3 feed_table = 3
photon_blast = "MyllaYoulla"
[Bhirava] [Bhirava]
feed_table = 4 feed_table = 4
photon_blast = "Pilla"
[Kalki] [Kalki]
feed_table = 1 feed_table = 1
photon_blast = "Estlla"
[Rudra] [Rudra]
feed_table = 2 feed_table = 2
photon_blast = "Leilla"
[Marutah] [Marutah]
feed_table = 2 feed_table = 2
photon_blast = "Pilla"
[Yaksa] [Yaksa]
feed_table = 5 feed_table = 5
photon_blast = "Leilla"
[Sita] [Sita]
feed_table = 5 feed_table = 5
photon_blast = "Pilla"
[Garuda] [Garuda]
feed_table = 5 feed_table = 5
photon_blast = "Pilla"
[Nandin] [Nandin]
feed_table = 5 feed_table = 5
photon_blast = "Estlla"
[Ashvinau] [Ashvinau]
feed_table = 2 feed_table = 2
photon_blast = "Pilla"
[Ribhava] [Ribhava]
feed_table = 5 feed_table = 5
[Soma] [Soma]
feed_table = 5 feed_table = 5
photon_blast = "Estlla"
[Ila] [Ila]
feed_table = 5 feed_table = 5
photon_blast = "MyllaYoulla"
[Durga] [Durga]
feed_table = 5 feed_table = 5
photon_blast = "Estlla"
[Vritra] [Vritra]
feed_table = 1 feed_table = 1
photon_blast = "Golla"
[Namuci] [Namuci]
feed_table = 2 feed_table = 2
photon_blast = "MyllaYoulla"
[Sumba] [Sumba]
feed_table = 2 feed_table = 2
photon_blast = "Leilla"
[Naga] [Naga]
feed_table = 6 feed_table = 6
photon_blast = "MyllaYoulla"
[Pitri] [Pitri]
feed_table = 7 feed_table = 7
[Kabanda] [Kabanda]
feed_table = 6 feed_table = 6
photon_blast = "MyllaYoulla"
[Ravana] [Ravana]
feed_table = 6 feed_table = 6
[Marica] [Marica]
feed_table = 6 feed_table = 6
photon_blast = "Pilla"
[Soniti] [Soniti]
feed_table = 7 feed_table = 7
@ -105,15 +133,19 @@ feed_table = 7
[Andhaka] [Andhaka]
feed_table = 6 feed_table = 6
photon_blast = "Estlla"
[Bana] [Bana]
feed_table = 6 feed_table = 6
photon_blast = "Estlla"
[Naraka] [Naraka]
feed_table = 6 feed_table = 6
photon_blast = "Leilla"
[Madhu] [Madhu]
feed_table = 6 feed_table = 6
photon_blast = "MyllaYoulla"
[Churel] [Churel]
feed_table = 7 feed_table = 7

4
src/entity/character.rs

@ -7,7 +7,7 @@ use libpso::character::character::{DEFAULT_PALETTE_CONFIG, DEFAULT_TECH_MENU};
use crate::entity::item::tech::Technique; use crate::entity::item::tech::Technique;
use crate::entity::account::UserAccountId; use crate::entity::account::UserAccountId;
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum CharacterClass { pub enum CharacterClass {
HUmar, HUmar,
HUnewearl, HUnewearl,
@ -64,7 +64,7 @@ impl Into<u8> for CharacterClass {
} }
#[derive(Copy, Clone, Hash, PartialEq, Eq, derive_more::Display)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, derive_more::Display)]
pub enum SectionID { pub enum SectionID {
Viridia, Viridia,
Greenill, Greenill,

12
src/entity/gateway/entitygateway.rs

@ -60,6 +60,18 @@ pub trait EntityGateway: Send + Sync + Clone {
unimplemented!(); unimplemented!();
} }
async fn feed_mag(&mut self, _mag_item_id: &ItemEntityId, _tool_item_id: &ItemEntityId) {
unimplemented!();
}
async fn change_mag_owner(&mut self, _mag_item_id: &ItemEntityId, _character: &CharacterEntity) {
unimplemented!();
}
async fn use_mag_cell(&mut self, _mag_item_id: &ItemEntityId, _mag_cell_id: &ItemEntityId) {
unimplemented!();
}
async fn get_items_by_character(&self, _char: &CharacterEntity) -> Vec<ItemEntity> { async fn get_items_by_character(&self, _char: &CharacterEntity) -> Vec<ItemEntity> {
unimplemented!(); unimplemented!();
} }

61
src/entity/gateway/inmemory.rs

@ -1,4 +1,5 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::convert::TryInto;
use crate::entity::account::*; use crate::entity::account::*;
use crate::entity::character::*; use crate::entity::character::*;
@ -13,6 +14,7 @@ pub struct InMemoryGateway {
user_settings: Arc<Mutex<BTreeMap<UserSettingsId, UserSettingsEntity>>>, user_settings: Arc<Mutex<BTreeMap<UserSettingsId, UserSettingsEntity>>>,
characters: Arc<Mutex<BTreeMap<CharacterEntityId, CharacterEntity>>>, characters: Arc<Mutex<BTreeMap<CharacterEntityId, CharacterEntity>>>,
items: Arc<Mutex<BTreeMap<ItemEntityId, ItemEntity>>>, items: Arc<Mutex<BTreeMap<ItemEntityId, ItemEntity>>>,
mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>,
} }
impl InMemoryGateway { impl InMemoryGateway {
@ -22,6 +24,7 @@ impl InMemoryGateway {
user_settings: Arc::new(Mutex::new(BTreeMap::new())), user_settings: Arc::new(Mutex::new(BTreeMap::new())),
characters: Arc::new(Mutex::new(BTreeMap::new())), characters: Arc::new(Mutex::new(BTreeMap::new())),
items: Arc::new(Mutex::new(BTreeMap::new())), items: Arc::new(Mutex::new(BTreeMap::new())),
mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
} }
} }
} }
@ -165,6 +168,29 @@ impl EntityGateway for InMemoryGateway {
}); });
} }
async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) {
self.mag_modifiers.lock().unwrap()
.entry(*mag_item_id)
.or_insert(Vec::new())
.push(mag::MagModifier::FeedMag {
food: *tool_item_id
});
}
async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) {
self.mag_modifiers.lock().unwrap()
.entry(*mag_item_id)
.or_insert(Vec::new())
.push(mag::MagModifier::OwnerChange(character.char_class, character.section_id));
}
async fn use_mag_cell(&mut self, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) {
self.mag_modifiers.lock().unwrap()
.entry(*mag_item_id)
.or_insert(Vec::new())
.push(mag::MagModifier::MagCell(mag_cell_id.clone()));
}
async fn get_items_by_character(&self, character: &CharacterEntity) -> Vec<ItemEntity> { async fn get_items_by_character(&self, character: &CharacterEntity) -> Vec<ItemEntity> {
let items = self.items.lock().unwrap(); let items = self.items.lock().unwrap();
items items
@ -179,6 +205,41 @@ impl EntityGateway for InMemoryGateway {
.map(|(_, k)| { .map(|(_, k)| {
k.clone() k.clone()
}) })
.map(|mut item| {
item.item = match item.item {
ItemDetail::Mag(mut mag) => {
self.mag_modifiers.lock().unwrap().get(&item.id).map(|mag_modifiers| {
for mag_modifier in mag_modifiers.iter() {
match mag_modifier {
mag::MagModifier::FeedMag {food} => {
items.get(&food).map(|mag_feed| {
match mag_feed.item {
ItemDetail::Tool(mag_feed) => mag.feed(mag_feed.tool),
_ => {}
}
});
},
mag::MagModifier::OwnerChange(class, section_id) => {
mag.change_owner(*class, *section_id)
},
mag::MagModifier::MagCell(mag_cell_id) => {
items.get(&mag_cell_id).map(|mag_cell| {
match mag_cell.item {
ItemDetail::Tool(mag_cell) => mag.apply_mag_cell(mag_cell.tool.try_into().unwrap()),
_ => {}
}
});
},
_ => {}
}
}
});
ItemDetail::Mag(mag)
}
_ => item.item
};
item
})
.collect() .collect()
} }
} }

802
src/entity/item/mag.rs

@ -1,5 +1,62 @@
use std::collections::HashMap;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::tool::ToolType;
use crate::entity::character::{CharacterClass, SectionID};
use crate::entity::item::ItemEntityId; use crate::entity::item::ItemEntityId;
use std::io::Read;
use std::cmp::Ordering::{Less, Greater, Equal};
#[derive(Debug, Deserialize)]
struct MagStats {
feed_table: usize,
photon_blast: Option<PhotonBlast>
}
#[derive(Debug, Deserialize)]
struct MagFeedTable {
def: i16,
pow: i16,
dex: i16,
mnd: i16,
iq: i8,
syn: i8,
}
lazy_static::lazy_static! {
static ref MAG_STATS: HashMap<MagType, MagStats> = {
let mut f = std::fs::File::open("data/item_stats/mag_stats.toml").unwrap();
let mut s = String::new();
f.read_to_string(&mut s).unwrap();
let mag_stats: HashMap<String, MagStats> = toml::from_str(&s).unwrap();
mag_stats.into_iter()
.map(|(name, stats)| {
(name.parse().unwrap(), stats)
})
.collect::<HashMap<MagType, MagStats>>()
};
static ref MAG_FEEDING_TABLES: Vec<HashMap<ToolType, MagFeedTable>> = {
let mut f = std::fs::File::open("data/item_stats/mag_feed_table.toml").unwrap();
let mut s = String::new();
f.read_to_string(&mut s).unwrap();
let mut feed: HashMap<String, Vec<HashMap<String, MagFeedTable>>> = toml::from_str(&s).unwrap();
let feed = feed.remove("feedtable".into()).unwrap();
feed.into_iter()
.map(|table| {
table.into_iter()
.map(|(tool, stats)| {
(tool.parse().unwrap(), stats)
})
.collect()
})
.collect::<Vec<HashMap<ToolType, MagFeedTable>>>()
};
}
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ItemParseError { pub enum ItemParseError {
@ -250,6 +307,206 @@ impl MagType {
_ => Err(ItemParseError::InvalidMagType), _ => Err(ItemParseError::InvalidMagType),
} }
} }
pub fn can_evolve(&self) -> bool {
match self {
MagType::Mag => true,
MagType::Varuna => true,
MagType::Mitra => true,
MagType::Surya => true,
MagType::Vayu => true,
MagType::Varaha => true,
MagType::Kama => true,
MagType::Ushasu => true,
MagType::Apsaras => true,
MagType::Kumara => true,
MagType::Kaitabha => true,
MagType::Tapas => true,
MagType::Bhirava => true,
MagType::Kalki => true,
MagType::Rudra => true,
MagType::Marutah => true,
MagType::Yaksa => true,
MagType::Sita => true,
MagType::Garuda => true,
MagType::Nandin => true,
MagType::Ashvinau => true,
MagType::Ribhava => true,
MagType::Soma => true,
MagType::Ila => true,
MagType::Durga => true,
MagType::Vritra => true,
MagType::Namuci => true,
MagType::Sumba => true,
MagType::Naga => true,
MagType::Pitri => true,
MagType::Kabanda => true,
MagType::Ravana => true,
MagType::Marica => true,
MagType::Soniti => true,
MagType::Preta => true,
MagType::Andhaka => true,
MagType::Bana => true,
MagType::Naraka => true,
MagType::Madhu => true,
MagType::Churel => false,
MagType::Robochao => false,
MagType::OpaOpa => false,
MagType::Pian => false,
MagType::Chao => false,
MagType::ChuChu => false,
MagType::KapuKapu => false,
MagType::AngelsWing => false,
MagType::DevilsWing => false,
MagType::Elenor => false,
MagType::MarkIII => false,
MagType::MasterSystem => false,
MagType::Genesis => false,
MagType::SegaSaturn => false,
MagType::Dreamcast => false,
MagType::Hamburger => false,
MagType::PanzersTail => false,
MagType::DevilsTail => false,
MagType::Deva => false,
MagType::Rati => false,
MagType::Savitri => false,
MagType::Rukmin => false,
MagType::Pushan => false,
MagType::Diwari => false,
MagType::Sato => false,
MagType::Bhima => false,
MagType::Nidra => false,
MagType::GeungSi => false,
MagType::Tellusis => false,
MagType::StrikerUnit => false,
MagType::Pioneer => false,
MagType::Puyo => false,
MagType::Moro => false,
MagType::Rappy => false,
MagType::Yahoo => false,
MagType::GaelGiel => false,
MagType::Agastya => false,
}
}
}
pub enum MagCell {
CellOfMag502,
CellOfMag213,
PartsOfRobochao,
HeartOfOpaOpa,
HeartOfPian,
HeartOfChao,
HeartOfAngel,
HeartOfDevil,
KitOfHamburger,
PanthersSpirit,
KitOfMark3,
KitOfMasterSystem,
KitOfGenesis,
KitOfSegaSaturn,
KitOfDreamcast,
Tablet,
DragonScale,
HeavenStrikerCoat,
PioneerParts,
AmitiesMemo,
HeartOfMorolian,
RappysBeak,
YahoosEngine,
DPhotonCore,
LibertaKit,
}
impl std::convert::TryFrom<ToolType> for MagCell {
type Error = ();
fn try_from(tool: ToolType) -> Result<MagCell, ()> {
match tool {
ToolType::CellOfMag502 => Ok(MagCell::CellOfMag502),
ToolType::CellOfMag213 => Ok(MagCell::CellOfMag213),
ToolType::PartsOfRobochao => Ok(MagCell::PartsOfRobochao),
ToolType::HeartOfOpaOpa => Ok(MagCell::HeartOfOpaOpa),
ToolType::HeartOfPian => Ok(MagCell::HeartOfPian),
ToolType::HeartOfChao => Ok(MagCell::HeartOfChao),
ToolType::HeartOfAngel => Ok(MagCell::HeartOfAngel),
ToolType::HeartOfDevil => Ok(MagCell::HeartOfDevil),
ToolType::KitOfHamburger => Ok(MagCell::KitOfHamburger),
ToolType::PanthersSpirit => Ok(MagCell::PanthersSpirit),
ToolType::KitOfMark3 => Ok(MagCell::KitOfMark3),
ToolType::KitOfMasterSystem => Ok(MagCell::KitOfMasterSystem),
ToolType::KitOfGenesis => Ok(MagCell::KitOfGenesis),
ToolType::KitOfSegaSaturn => Ok(MagCell::KitOfSegaSaturn),
ToolType::KitOfDreamcast => Ok(MagCell::KitOfDreamcast),
ToolType::Tablet => Ok(MagCell::Tablet),
ToolType::DragonScale => Ok(MagCell::DragonScale),
ToolType::HeavenStrikerCoat => Ok(MagCell::HeavenStrikerCoat),
ToolType::PioneerParts => Ok(MagCell::PioneerParts),
ToolType::AmitiesMemo => Ok(MagCell::AmitiesMemo),
ToolType::HeartOfMorolian => Ok(MagCell::HeartOfMorolian),
ToolType::RappysBeak => Ok(MagCell::RappysBeak),
ToolType::YahoosEngine => Ok(MagCell::YahoosEngine),
ToolType::DPhotonCore => Ok(MagCell::DPhotonCore),
ToolType::LibertaKit => Ok(MagCell::LibertaKit),
_ => Err(()),
}
}
}
enum MagAttribute {
//Def,
Pow,
Dex,
Mind,
}
// one day I hope to be cool enough to figure how to enforce that each magattribute in sequence must be unique
// (to not need the _ in the match)
enum MagAttributeOrdering {
Sequence(MagAttribute, MagAttribute, MagAttribute),
Primary(MagAttribute),
MultiPrimary
}
impl MagAttributeOrdering {
fn new(pow: u16, dex: u16, mnd: u16) -> MagAttributeOrdering {
let primary = if pow > dex && pow > mnd {
MagAttribute::Pow
}
else if dex > pow && dex > mnd{
MagAttribute::Dex
}
else if mnd > pow && mnd > dex {
MagAttribute::Mind
}
else {
return MagAttributeOrdering::MultiPrimary
};
match primary {
MagAttribute::Pow => {
match dex.cmp(&mnd) {
Greater => MagAttributeOrdering::Sequence(primary, MagAttribute::Dex, MagAttribute::Mind),
Equal => MagAttributeOrdering::Primary(primary),
Less => MagAttributeOrdering::Sequence(primary, MagAttribute::Mind, MagAttribute::Dex),
}
},
MagAttribute::Dex => {
match pow.cmp(&mnd) {
Greater => MagAttributeOrdering::Sequence(primary, MagAttribute::Pow, MagAttribute::Mind),
Equal => MagAttributeOrdering::Primary(primary),
Less => MagAttributeOrdering::Sequence(primary, MagAttribute::Mind, MagAttribute::Pow),
}
},
MagAttribute::Mind => {
match pow.cmp(&dex) {
Greater => MagAttributeOrdering::Sequence(primary, MagAttribute::Pow, MagAttribute::Dex),
Equal => MagAttributeOrdering::Primary(primary),
Less => MagAttributeOrdering::Sequence(primary, MagAttribute::Dex, MagAttribute::Pow),
}
},
}
}
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -257,11 +514,12 @@ pub enum MagModifier {
FeedMag{ FeedMag{
food: ItemEntityId, food: ItemEntityId,
}, },
BankMag,
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)
} }
#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, enum_utils::FromStr)]
pub enum PhotonBlast { pub enum PhotonBlast {
Farlla, Farlla,
Estlla, Estlla,
@ -274,19 +532,38 @@ pub enum PhotonBlast {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Mag { pub struct Mag {
pub mag: MagType, pub mag: MagType,
pub def: u16,
pub pow: u16,
pub dex: u16,
pub mnd: u16,
def: u16,
pow: u16,
dex: u16,
mnd: u16,
pub synchro: u8, pub synchro: u8,
pub iq: u8,
pub photon_blast: [Option<PhotonBlast>; 3],
iq: u8,
photon_blast: [Option<PhotonBlast>; 3],
pub color: u8, pub color: u8,
pub modifiers: Vec<MagModifier>,
//modifiers: Vec<MagModifier>,
pub class: CharacterClass,
pub id: SectionID,
} }
impl Mag { impl Mag {
pub fn 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,
}
}
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.mag.value()); result[0..3].copy_from_slice(&self.mag.value());
@ -382,11 +659,516 @@ impl Mag {
iq: iq, iq: iq,
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,
id: SectionID::Viridia,
}) })
} }
else { else {
Err(ItemParseError::InvalidMagBytes) // TODO: error handling if wrong bytes are given Err(ItemParseError::InvalidMagBytes) // TODO: error handling if wrong bytes are given
} }
} }
pub fn def(&self) -> u16 {
self.def/100
}
pub fn pow(&self) -> u16 {
self.pow/100
}
pub fn dex(&self) -> u16 {
self.dex/100
}
pub fn mind(&self) -> u16 {
self.mnd/100
}
pub fn level(&self) -> u16 {
self.def() + self.pow() + self.dex() + self.mind()
}
fn change_mag_type(&mut self, previous_level: u16) {
if !self.mag.can_evolve() {
return
}
if self.level() >= 10 && previous_level < 10 {
match self.class {
CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => {
self.mag = MagType::Varuna
},
CharacterClass::RAmar | CharacterClass::RAmarl | CharacterClass::RAcast | CharacterClass::RAcaseal => {
self.mag = MagType::Kalki
},
CharacterClass::FOmar | CharacterClass::FOmarl | CharacterClass::FOnewm | CharacterClass::FOnewearl => {
self.mag = MagType::Vritra
},
}
}
if self.level() >= 35 && previous_level < 35 {
match self.mag {
MagType::Varuna => {
if self.pow > self.dex && self.pow > self.mnd {
self.mag = MagType::Rudra
}
else if self.dex > self.pow && self.dex > self.mnd {
self.mag = MagType::Marutah
}
else if self.mnd > self.pow && self.mnd > self.dex {
self.mag = MagType::Vayu
}
else {
self.mag = MagType::Rudra
}
},
MagType::Kalki => {
if self.pow > self.dex && self.pow > self.mnd {
self.mag = MagType::Surya
}
else if self.dex > self.pow && self.dex > self.mnd {
self.mag = MagType::Mitra
}
else if self.mnd > self.pow && self.mnd > self.dex {
self.mag = MagType::Tapas
}
else {
self.mag = MagType::Mitra
}
},
MagType::Vritra => {
if self.pow > self.dex && self.pow > self.mnd {
self.mag = MagType::Sumba
}
else if self.dex > self.pow && self.dex > self.mnd {
self.mag = MagType::Ashvinau
}
else if self.mnd > self.pow && self.mnd > self.dex {
self.mag = MagType::Namuci
}
else {
self.mag = MagType::Namuci
}
},
_ => unreachable!(),
}
}
if self.level() >= 50 && self.level() % 5 == 0 {
let mag_attr_ordering = MagAttributeOrdering::new(self.pow, self.dex, self.mnd);
self.mag = match self.id {
SectionID::Viridia | SectionID::Skyly | SectionID::Purplenum | SectionID::Redria | SectionID::Yellowboze => {
match self.class {
CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => {
match mag_attr_ordering {
MagAttributeOrdering::Primary(MagAttribute::Pow) => MagType::Varaha,
MagAttributeOrdering::Primary(MagAttribute::Dex) => MagType::Nandin,
MagAttributeOrdering::Primary(MagAttribute::Mind) => MagType::Kabanda,
MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind) => MagType::Varaha,
MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex) => MagType::Bhirava,
MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind) => MagType::Ila,
MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow) => MagType::Nandin,
MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex) => MagType::Kabanda,
MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow) => MagType::Ushasu,
MagAttributeOrdering::MultiPrimary => {
if self.dex >= self.mnd {
MagType::Varaha
}
else {
MagType::Bhirava
}
},
_ => unreachable!()
}
},
CharacterClass::RAmar | CharacterClass::RAmarl | CharacterClass::RAcast | CharacterClass::RAcaseal => {
match mag_attr_ordering {
MagAttributeOrdering::Primary(MagAttribute::Pow) => MagType::Kama,
MagAttributeOrdering::Primary(MagAttribute::Dex) => MagType::Kama,
MagAttributeOrdering::Primary(MagAttribute::Mind) => MagType::Varaha,
MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind) => MagType::Kama,
MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex) => MagType::Bhirava,
MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind) => MagType::Bhirava,
MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow) => MagType::Kama,
MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex) => MagType::Varaha,
MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow) => MagType::Apsaras,
MagAttributeOrdering::MultiPrimary => {
if self.mnd >= self.pow {
MagType::Kama
}
else {
MagType::Bhirava
}
}
_ => unreachable!()
}
},
CharacterClass::FOmar | CharacterClass::FOmarl | CharacterClass::FOnewm | CharacterClass::FOnewearl => {
match (mag_attr_ordering, self.def() >= 45) {
(MagAttributeOrdering::Primary(MagAttribute::Pow), true) => MagType::Andhaka,
(MagAttributeOrdering::Primary(MagAttribute::Dex), true) => MagType::Bana,
(MagAttributeOrdering::Primary(MagAttribute::Mind), true) => MagType::Bana,
(MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind), true) => MagType::Andhaka,
(MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex), true) => MagType::Andhaka,
(MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind), true) => MagType::Bana,
(MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow), true) => MagType::Bana,
(MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex), true) => MagType::Bana,
(MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow), true) => MagType::Bana,
(MagAttributeOrdering::MultiPrimary, true) => MagType::Bana,
(MagAttributeOrdering::Primary(MagAttribute::Pow), false) => MagType::Naraka,
(MagAttributeOrdering::Primary(MagAttribute::Dex), false) => MagType::Sita,
(MagAttributeOrdering::Primary(MagAttribute::Mind), false) => MagType::Naga,
(MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind), false) => MagType::Naraka,
(MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex), false) => MagType::Ravana,
(MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind), false) => MagType::Ribhava,
(MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow), false) => MagType::Sita,
(MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex), false) => MagType::Naga,
(MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow), false) => MagType::Kabanda,
(MagAttributeOrdering::MultiPrimary, false) => {
if self.pow >= self.dex {
MagType::Naga
}
else {
MagType::Kabanda
}
}
_ => unreachable!()
}
},
}
},
SectionID::Greenill | SectionID::Bluefull | SectionID::Pinkal | SectionID::Oran | SectionID::Whitill => {
match self.class {
CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => {
match mag_attr_ordering {
MagAttributeOrdering::Primary(MagAttribute::Pow) => MagType::Kama,
MagAttributeOrdering::Primary(MagAttribute::Dex) => MagType::Yaksa,
MagAttributeOrdering::Primary(MagAttribute::Mind) => MagType::Bana,
MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind) => MagType::Kama,
MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex) => MagType::Apsaras,
MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind) => MagType::Garuda,
MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow) => MagType::Yaksa,
MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex) => MagType::Bana,
MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow) => MagType::Soma,
MagAttributeOrdering::MultiPrimary => {
if self.dex >= self.mnd {
MagType::Kama
}
else {
MagType::Apsaras
}
}
_ => unreachable!()
}
},
CharacterClass::RAmar | CharacterClass::RAmarl | CharacterClass::RAcast | CharacterClass::RAcaseal => {
match mag_attr_ordering {
MagAttributeOrdering::Primary(MagAttribute::Pow) => MagType::Madhu,
MagAttributeOrdering::Primary(MagAttribute::Dex) => MagType::Varaha,
MagAttributeOrdering::Primary(MagAttribute::Mind) => MagType::Kabanda,
MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind) => MagType::Madhu,
MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex) => MagType::Kaitabha,
MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind) => MagType::Kaitabha,
MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow) => MagType::Varaha,
MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex) => MagType::Kabanda,
MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow) => MagType::Durga,
MagAttributeOrdering::MultiPrimary => {
if self.pow > self.mnd {
MagType::Kaitabha
}
else {
MagType::Varaha
}
}
_ => unreachable!()
}
},
CharacterClass::FOmar | CharacterClass::FOmarl | CharacterClass::FOnewm | CharacterClass::FOnewearl => {
match (mag_attr_ordering, self.def() >= 45) {
(MagAttributeOrdering::Primary(MagAttribute::Pow), true) => MagType::Andhaka,
(MagAttributeOrdering::Primary(MagAttribute::Dex), true) => MagType::Bana,
(MagAttributeOrdering::Primary(MagAttribute::Mind), true) => MagType::Bana,
(MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind), true) => MagType::Andhaka,
(MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex), true) => MagType::Andhaka,
(MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind), true) => MagType::Bana,
(MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow), true) => MagType::Bana,
(MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex), true) => MagType::Bana,
(MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow), true) => MagType::Bana,
(MagAttributeOrdering::MultiPrimary, true) => MagType::Bana,
(MagAttributeOrdering::Primary(MagAttribute::Pow), false) => MagType::Marica,
(MagAttributeOrdering::Primary(MagAttribute::Dex), false) => MagType::Bhirava,
(MagAttributeOrdering::Primary(MagAttribute::Mind), false) => MagType::Kumara,
(MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind), false) => MagType::Marica,
(MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex), false) => MagType::Naga,
(MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind), false) => MagType::Garuda,
(MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow), false) => MagType::Bhirava,
(MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex), false) => MagType::Kumara,
(MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow), false) => MagType::Ila,
(MagAttributeOrdering::MultiPrimary, false) => {
if self.pow >= self.dex {
MagType::Kumara
}
else {
MagType::Ila
}
}
_ => unreachable!()
}
},
}
}
};
}
if self.level() >= 100 && self.level() % 5 == 0 {
match self.id {
SectionID::Skyly | SectionID::Pinkal | SectionID::Yellowboze => {
if self.def() + self.pow() == self.dex() + self.mind() {
self.mag = match self.class {
CharacterClass::HUmar | CharacterClass::HUcast => {
MagType::Rati
},
CharacterClass::HUnewearl | CharacterClass::HUcaseal => {
MagType::Savitri
},
CharacterClass::RAmar | CharacterClass::RAcast => {
MagType::Pushan
},
CharacterClass::RAmarl | CharacterClass::RAcaseal => {
MagType::Diwari
},
CharacterClass::FOmar | CharacterClass::FOnewm => {
MagType::Nidra
},
CharacterClass::FOmarl | CharacterClass::FOnewearl => {
MagType::Bhima
},
}
}
},
SectionID::Viridia | SectionID::Bluefull | SectionID::Redria | SectionID::Whitill => {
if self.def() + self.dex() == self.pow() + self.mind() {
self.mag = match self.class {
CharacterClass::HUmar | CharacterClass::HUcast => {
MagType::Deva
},
CharacterClass::HUnewearl | CharacterClass::HUcaseal => {
MagType::Savitri
},
CharacterClass::RAmar | CharacterClass::RAcast => {
MagType::Pushan
},
CharacterClass::RAmarl | CharacterClass::RAcaseal => {
MagType::Rukmin
},
CharacterClass::FOmar | CharacterClass::FOnewm => {
MagType::Nidra
},
CharacterClass::FOmarl | CharacterClass::FOnewearl => {
MagType::Sato // best mag
},
}
}
},
SectionID::Greenill | SectionID::Purplenum | SectionID::Oran => {
if self.def() + self.mind() == self.pow() + self.dex() {
self.mag = match self.class {
CharacterClass::HUmar | CharacterClass::HUcast => {
MagType::Rati
},
CharacterClass::HUnewearl | CharacterClass::HUcaseal => {
MagType::Savitri
},
CharacterClass::RAmar | CharacterClass::RAcast => {
MagType::Pushan
},
CharacterClass::RAmarl | CharacterClass::RAcaseal => {
MagType::Rukmin
},
CharacterClass::FOmar | CharacterClass::FOnewm => {
MagType::Nidra
},
CharacterClass::FOmarl | CharacterClass::FOnewearl => {
MagType::Bhima
},
}
}
}
}
}
}
pub fn assign_photon_blast(&mut self) {
MAG_STATS.get(&self.mag).map(|stats| {
stats.photon_blast.map(|photon_blast| {
if !self.photon_blast.contains(&Some(photon_blast)) {
self.photon_blast.iter_mut().find(|k| k.is_none()).map(|pb_slot| {
*pb_slot = Some(photon_blast)
});
}
})
});
}
pub fn feed(&mut self, tool: ToolType) {
let previous_level = self.level();
MAG_STATS.get(&self.mag).map(|stats| {
MAG_FEEDING_TABLES.get(stats.feed_table).map(|feeding_table| {
feeding_table.get(&tool).map(|feed_stats| {
self.def = std::cmp::max(std::cmp::max((self.def as i16) + feed_stats.def, 0) as u16, self.def()*100);
self.pow = std::cmp::max(std::cmp::max((self.pow as i16) + feed_stats.pow, 0) as u16, self.pow()*100);
self.dex = std::cmp::max(std::cmp::max((self.dex as i16) + feed_stats.dex, 0) as u16, self.dex()*100);
self.mnd = std::cmp::max(std::cmp::max((self.mnd as i16) + feed_stats.mnd, 0) as u16, self.mind()*100);
self.iq = std::cmp::min(((self.iq as i16) + feed_stats.iq as i16) as u8, 200);
self.synchro = std::cmp::min(((self.synchro as i8) + feed_stats.syn) as u8, 120);
})
})
});
//if previous_level != self.level() {
self.change_mag_type(previous_level);
self.assign_photon_blast();
//}
}
pub fn change_owner(&mut self, class: CharacterClass, id: SectionID) {
self.class = class;
self.id = id;
}
pub fn bank(&mut self) {
// what is the truncation logic anyway
}
// TODO: this needs more checks on validity
pub fn apply_mag_cell(&mut self, mag_cell: MagCell) {
self.mag = match mag_cell {
MagCell::CellOfMag502 => {
match self.id {
SectionID::Viridia | SectionID::Skyly | SectionID::Purplenum | SectionID::Redria | SectionID::Yellowboze => {
MagType::Soniti
},
SectionID::Greenill | SectionID::Bluefull | SectionID::Pinkal | SectionID::Oran | SectionID::Whitill => {
MagType::Pitri
}
}
}
MagCell::CellOfMag213 => {
match self.id {
SectionID::Viridia | SectionID::Skyly | SectionID::Purplenum | SectionID::Redria | SectionID::Yellowboze => {
MagType::Churel
},
SectionID::Greenill | SectionID::Bluefull | SectionID::Pinkal | SectionID::Oran | SectionID::Whitill => {
MagType::Preta
}
}
},
MagCell::PartsOfRobochao => MagType::Robochao,
MagCell::HeartOfOpaOpa => MagType::OpaOpa,
MagCell::HeartOfPian => MagType::Pian,
MagCell::HeartOfChao => MagType::Chao,
MagCell::HeartOfAngel => MagType::AngelsWing,
MagCell::HeartOfDevil => if self.mag == MagType::DevilsWing {
MagType::DevilsTail
}
else {
MagType::DevilsWing
},
MagCell::KitOfHamburger => MagType::Hamburger,
MagCell::PanthersSpirit => MagType::PanzersTail,
MagCell::KitOfMark3 => MagType::MarkIII,
MagCell::KitOfMasterSystem => MagType::MasterSystem,
MagCell::KitOfGenesis => MagType::Genesis,
MagCell::KitOfSegaSaturn => MagType::SegaSaturn,
MagCell::KitOfDreamcast => MagType::Dreamcast,
MagCell::Tablet => MagType::GeungSi,
MagCell::DragonScale => MagType::Tellusis,
MagCell::HeavenStrikerCoat => MagType::StrikerUnit,
MagCell::PioneerParts => MagType::Pioneer,
MagCell::AmitiesMemo => MagType::Puyo,
MagCell::HeartOfMorolian => MagType::Moro,
MagCell::RappysBeak => MagType::Rappy,
MagCell::YahoosEngine => MagType::Yahoo,
MagCell::DPhotonCore => MagType::GaelGiel,
MagCell::LibertaKit => MagType::Agastya,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use std::io::Read;
#[test]
fn test_load_mag_stats() {
let mut f = std::fs::File::open("data/item_stats/mag_stats.toml").unwrap();
let mut s = String::new();
f.read_to_string(&mut s).unwrap();
let mags: HashMap<String, MagStats> = toml::from_str(&s).unwrap();
let _mags = mags.into_iter()
.map(|(name, stats)| {
(name.parse().unwrap(), stats)
})
.collect::<HashMap<MagType, MagStats>>();
}
#[test]
fn test_load_mag_feed_table() {
let mut f = std::fs::File::open("data/item_stats/mag_feed_table.toml").unwrap();
let mut s = String::new();
f.read_to_string(&mut s).unwrap();
let mut feed: HashMap<String, Vec<HashMap<String, MagFeedTable>>> = toml::from_str(&s).unwrap();
let feed = feed.remove("feedtable".into()).unwrap();
let _feed = feed.into_iter()
.map(|table| {
table.into_iter()
.map(|(tool, stats)| {
(tool.parse().unwrap(), stats)
})
.collect()
})
.collect::<Vec<HashMap<ToolType, MagFeedTable>>>();
}
#[test]
fn test_raise_a_sato() {
let mut mag = Mag::baby_mag(0);
mag.change_owner(CharacterClass::RAcaseal, SectionID::Whitill);
for _ in 0..137 {
mag.feed(ToolType::Antidote);
}
for _ in 0..75 {
mag.feed(ToolType::Antiparalysis);
}
mag.change_owner(CharacterClass::FOmarl, SectionID::Whitill);
for _ in 0..51 {
mag.feed(ToolType::Antiparalysis);
}
for _ in 0..284 {
mag.feed(ToolType::Dimate);
}
assert!(mag == Mag {
mag: MagType::Sato,
def: 507,
pow: 5019,
dex: 4505,
mnd: 0,
synchro: 120,
iq: 200,
photon_blast: [Some(PhotonBlast::Estlla), Some(PhotonBlast::Pilla), Some(PhotonBlast::MyllaYoulla)],
color: 0,
class: CharacterClass::FOmarl,
id: SectionID::Whitill,
});
}
#[test]
fn test_mag_does_not_level_down() {
}
} }

3
src/entity/item/mod.rs

@ -44,6 +44,9 @@ pub enum ItemLocation {
z: f32, z: f32,
}, },
Consumed, Consumed,
FedToMag {
mag: ItemEntityId,
}
/*Destroyed { /*Destroyed {
// marks an item that has been consumed in some way // marks an item that has been consumed in some way
}, },

36
src/entity/item/tool.rs

@ -251,6 +251,42 @@ impl ToolType {
} }
} }
pub fn is_mag_cell(&self) -> bool {
match self {
ToolType::CellOfMag502 => true,
ToolType::CellOfMag213 => true,
ToolType::PartsOfRobochao => true,
ToolType::HeartOfOpaOpa => true,
ToolType::HeartOfPian => true,
ToolType::HeartOfChao => true,
ToolType::HeartOfAngel => true,
ToolType::HeartOfDevil => true,
ToolType::KitOfHamburger => true,
ToolType::PanthersSpirit => true,
ToolType::KitOfMark3 => true,
ToolType::KitOfMasterSystem => true,
ToolType::KitOfGenesis => true,
ToolType::KitOfSegaSaturn => true,
ToolType::KitOfDreamcast => true,
ToolType::Tablet => true,
ToolType::DragonScale => true,
ToolType::HeavenStrikerCoat => true,
ToolType::PioneerParts => true,
ToolType::AmitiesMemo => true,
ToolType::HeartOfMorolian => true,
ToolType::RappysBeak => true,
ToolType::YahoosEngine => true,
ToolType::DPhotonCore => true,
ToolType::LibertaKit => true,
ToolType::CellOfMag0503 => true,
ToolType::CellOfMag0504 => true,
ToolType::CellOfMag0505 => true,
ToolType::CellOfMag0506 => true,
ToolType::CellOfMag0507 => true,
_ => false,
}
}
pub fn value(&self) -> [u8; 3] { pub fn value(&self) -> [u8; 3] {
match self { match self {
ToolType::Monomate => [0x03, 0x00, 0x00], ToolType::Monomate => [0x03, 0x00, 0x00],

2
src/lib.rs

@ -3,6 +3,8 @@
#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_extra)]
#![feature(const_in_array_repeat_expressions)] #![feature(const_in_array_repeat_expressions)]
#![feature(drain_filter)] #![feature(drain_filter)]
#![feature(or_patterns)]

16
src/login/character.rs

@ -224,21 +224,11 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
equipped: true, equipped: true,
}}).await; }}).await;
let mut mag = Mag::baby_mag(character.appearance.skin);
mag.change_owner(character.char_class, character.section_id);
entity_gateway.create_item( entity_gateway.create_item(
NewItemEntity { NewItemEntity {
item: ItemDetail::Mag(
Mag {
mag: MagType::Mag,
def: 500,
pow: 0,
dex: 0,
mnd: 0,
synchro: 20,
iq: 0,
photon_blast: [None; 3],
color: (character.appearance.skin % 18) as u8,
modifiers: Vec::new(),
}),
item: ItemDetail::Mag(mag),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
slot: 2, slot: 2,

13
src/ship/drops/rare_drop_table.rs

@ -136,18 +136,7 @@ impl RareDropTable {
}) })
}, },
RareDropItem::Mag(mag) => { RareDropItem::Mag(mag) => {
ItemDropType::Mag(Mag {
mag: mag,
def: 500,
pow: 0,
dex: 0,
mnd: 0,
iq: 0,
synchro: 20,
photon_blast: [None; 3],
color: rng.gen_range(0, 18),
modifiers: Vec::new(),
})
ItemDropType::Mag(Mag::baby_mag(rng.gen_range(0, 18)))
} }
} }
} }

53
src/ship/items/inventory.rs

@ -3,6 +3,7 @@ use thiserror::Error;
use libpso::character::character;//::InventoryItem; use libpso::character::character;//::InventoryItem;
use crate::entity::item::{ItemEntityId, ItemDetail, ItemType}; use crate::entity::item::{ItemEntityId, ItemDetail, ItemType};
use crate::entity::item::tool::Tool; use crate::entity::item::tool::Tool;
use crate::entity::item::mag::Mag;
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};
@ -20,6 +21,22 @@ pub struct IndividualInventoryItem {
pub equipped: bool, pub equipped: bool,
} }
impl IndividualInventoryItem {
pub fn mag(&self) -> Option<&Mag> {
match self.item {
ItemDetail::Mag(ref mag) => Some(mag),
_ => None
}
}
pub fn mag_mut(&mut self) -> Option<&mut Mag> {
match self.item {
ItemDetail::Mag(ref mut mag) => Some(mag),
_ => None
}
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct StackedInventoryItem { pub struct StackedInventoryItem {
pub entity_ids: Vec<ItemEntityId>, pub entity_ids: Vec<ItemEntityId>,
@ -57,6 +74,17 @@ pub enum InventoryItemAddToError {
} }
impl InventoryItem { impl InventoryItem {
pub fn entity_ids(&self) -> Vec<ItemEntityId> {
match self {
InventoryItem::Individual(individual_inventory_item) => {
vec![individual_inventory_item.entity_id]
},
InventoryItem::Stacked(stacked_inventory_item) => {
stacked_inventory_item.entity_ids.clone()
}
}
}
pub fn item_id(&self) -> ClientItemId { pub fn item_id(&self) -> ClientItemId {
match self { match self {
InventoryItem::Individual(individual_inventory_item) => { InventoryItem::Individual(individual_inventory_item) => {
@ -179,6 +207,13 @@ impl InventoryItem {
} }
Ok(()) Ok(())
} }
pub fn individual(&mut self) -> Option<&mut IndividualInventoryItem> {
match self {
InventoryItem::Individual(ref mut individual_inventory_item) => Some(individual_inventory_item),
_ => None
}
}
} }
@ -365,6 +400,24 @@ impl CharacterInventory {
}) })
} }
pub fn get_equipped_mag_handle<'a>(&'a mut self) -> Option<InventoryItemHandle<'a>> {
let (slot, _) = self.items.iter()
.enumerate()
.filter(|(_, item)| {
if let InventoryItem::Individual(individual_inventory_item) = item {
if let ItemDetail::Mag(_) = &individual_inventory_item.item {
return individual_inventory_item.equipped
}
}
false
})
.nth(0)?;
Some(InventoryItemHandle {
inventory: self,
slot: slot,
})
}
pub fn get_item_by_id(&self, item_id: ClientItemId) -> Option<&InventoryItem> { pub fn get_item_by_id(&self, item_id: ClientItemId) -> Option<&InventoryItem> {
self.items.iter() self.items.iter()
.filter(|item| { .filter(|item| {

163
src/ship/items/manager.rs

@ -5,7 +5,7 @@ use crate::entity::gateway::EntityGateway;
use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::character::{CharacterEntity, CharacterEntityId};
use crate::entity::item::{ItemDetail, ItemLocation, BankName}; use crate::entity::item::{ItemDetail, ItemLocation, BankName};
use crate::entity::item::{Meseta, NewItemEntity}; use crate::entity::item::{Meseta, NewItemEntity};
use crate::entity::item::tool::Tool;
use crate::entity::item::tool::{Tool, ToolType};
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};
@ -14,6 +14,7 @@ use crate::ship::location::{AreaClient, RoomId};
use crate::ship::items::bank::*; use crate::ship::items::bank::*;
use crate::ship::items::floor::*; use crate::ship::items::floor::*;
use crate::ship::items::inventory::*; use crate::ship::items::inventory::*;
use crate::ship::items::use_tool;
pub enum TriggerCreateItem { pub enum TriggerCreateItem {
@ -36,6 +37,8 @@ pub enum ItemManagerError {
NotEnoughTools(Tool, usize, usize), // have, expected NotEnoughTools(Tool, usize, usize), // have, expected
InventoryItemConsumeError(#[from] InventoryItemConsumeError), InventoryItemConsumeError(#[from] InventoryItemConsumeError),
BankFull, BankFull,
WrongItemType(ClientItemId),
UseItemError(#[from] use_tool::UseItemError),
} }
pub struct ItemManager { pub struct ItemManager {
@ -260,6 +263,9 @@ impl ItemManager {
equipped: false, equipped: false,
} }
).await; ).await;
if let Some(_) = new_inventory_item.mag() {
entity_gateway.change_mag_owner(&new_inventory_item.entity_id, character).await;
}
}, },
None => { None => {
return Err(ItemManagerError::CouldNotAddToInventory(item_id)); return Err(ItemManagerError::CouldNotAddToInventory(item_id));
@ -508,7 +514,7 @@ impl ItemManager {
character: &CharacterEntity, character: &CharacterEntity,
item_id: ClientItemId, item_id: ClientItemId,
amount: usize) amount: usize)
-> Result<ItemDetail, ItemManagerError> {
-> Result<ConsumedItem, 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 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)?;
@ -518,7 +524,7 @@ impl ItemManager {
ItemLocation::Consumed).await; ItemLocation::Consumed).await;
} }
Ok(consumed_item.item())
Ok(consumed_item)
} }
pub async fn player_deposits_item<EG: EntityGateway>(&mut self, pub async fn player_deposits_item<EG: EntityGateway>(&mut self,
@ -595,4 +601,155 @@ impl ItemManager {
Ok(inventory_item.0) Ok(inventory_item.0)
} }
pub async fn player_feeds_mag_item<EG: EntityGateway>(&mut self,
entity_gateway: &mut EG,
character: &CharacterEntity,
mag_id: ClientItemId,
tool_id: ClientItemId)
-> Result<(), ItemManagerError> {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
let consumed_tool = {
let item_to_feed = inventory.get_item_handle_by_id(tool_id).ok_or(ItemManagerError::NoSuchItemId(tool_id))?;
item_to_feed.consume(1)?
};
let mut mag_handle = inventory.get_item_handle_by_id(mag_id).ok_or(ItemManagerError::NoSuchItemId(mag_id))?;
let individual_item = mag_handle.item_mut()
.ok_or(ItemManagerError::NoSuchItemId(mag_id))?
.individual()
.ok_or(ItemManagerError::WrongItemType(mag_id))?;
let mag = individual_item
.mag_mut()
.ok_or(ItemManagerError::WrongItemType(mag_id))?;
let consumed_tool_type = match &consumed_tool {
ConsumedItem::Stacked(stacked_consumed_item) => stacked_consumed_item.tool.tool,
_ => return Err(ItemManagerError::WrongItemType(tool_id))
};
mag.feed(consumed_tool_type);
for entity_id in consumed_tool.entity_ids() {
entity_gateway.feed_mag(&individual_item.entity_id, &entity_id).await;
entity_gateway.change_item_location(&entity_id, ItemLocation::FedToMag {
mag: individual_item.entity_id,
}).await;
}
Ok(())
}
pub async fn use_item<EG: EntityGateway>(&mut self,
used_item: ConsumedItem,
entity_gateway: &mut EG,
character: &mut CharacterEntity) -> Result<(), ItemManagerError> {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
match &used_item.item() {
ItemDetail::Weapon(_w) => {
// something like when items are used to combine/transform them?
//_ => {}
},
ItemDetail::Tool(t) => {
match t.tool {
ToolType::PowerMaterial => {
use_tool::power_material(entity_gateway, character).await;
},
ToolType::MindMaterial => {
use_tool::mind_material(entity_gateway, character).await;
},
ToolType::EvadeMaterial => {
use_tool::evade_material(entity_gateway, character).await;
},
ToolType::DefMaterial => {
use_tool::def_material(entity_gateway, character).await;
},
ToolType::LuckMaterial => {
use_tool::luck_material(entity_gateway, character).await;
},
ToolType::HpMaterial => {
use_tool::hp_material(entity_gateway, character).await;
},
ToolType::TpMaterial => {
use_tool::tp_material(entity_gateway, character).await;
},
ToolType::CellOfMag502 => {
use_tool::cell_of_mag_502(entity_gateway, &used_item, inventory).await?;
},
ToolType::CellOfMag213 => {
use_tool::cell_of_mag_213(entity_gateway, &used_item, inventory).await?;
},
ToolType::PartsOfRobochao => {
use_tool::parts_of_robochao(entity_gateway, &used_item, inventory).await?;
},
ToolType::HeartOfOpaOpa => {
use_tool::heart_of_opaopa(entity_gateway, &used_item, inventory).await?;
},
ToolType::HeartOfPian => {
use_tool::heart_of_pian(entity_gateway, &used_item, inventory).await?;
},
ToolType::HeartOfChao=> {
use_tool::heart_of_chao(entity_gateway, &used_item, inventory).await?;
},
ToolType::HeartOfAngel => {
use_tool::heart_of_angel(entity_gateway, &used_item, inventory).await?;
},
ToolType::KitOfHamburger => {
use_tool::kit_of_hamburger(entity_gateway, &used_item, inventory).await?;
},
ToolType::PanthersSpirit => {
use_tool::panthers_spirit(entity_gateway, &used_item, inventory).await?;
},
ToolType::KitOfMark3 => {
use_tool::kit_of_mark3(entity_gateway, &used_item, inventory).await?;
},
ToolType::KitOfMasterSystem=> {
use_tool::kit_of_master_system(entity_gateway, &used_item, inventory).await?;
},
ToolType::KitOfGenesis => {
use_tool::kit_of_genesis(entity_gateway, &used_item, inventory).await?;
},
ToolType::KitOfSegaSaturn => {
use_tool::kit_of_sega_saturn(entity_gateway, &used_item, inventory).await?;
},
ToolType::KitOfDreamcast => {
use_tool::kit_of_dreamcast(entity_gateway, &used_item, inventory).await?;
},
ToolType::Tablet => {
use_tool::tablet(entity_gateway, &used_item, inventory).await?;
},
ToolType::DragonScale => {
use_tool::dragon_scale(entity_gateway, &used_item, inventory).await?;
},
ToolType::HeavenStrikerCoat => {
use_tool::heaven_striker_coat(entity_gateway, &used_item, inventory).await?;
},
ToolType::PioneerParts => {
use_tool::pioneer_parts(entity_gateway, &used_item, inventory).await?;
},
ToolType::AmitiesMemo => {
use_tool::amities_memo(entity_gateway, &used_item, inventory).await?;
},
ToolType::HeartOfMorolian => {
use_tool::heart_of_morolian(entity_gateway, &used_item, inventory).await?;
},
ToolType::RappysBeak => {
use_tool::rappys_beak(entity_gateway, &used_item, inventory).await?;
},
ToolType::YahoosEngine => {
use_tool::yahoos_engine(entity_gateway, &used_item, inventory).await?;
},
ToolType::DPhotonCore => {
use_tool::d_photon_core(entity_gateway, &used_item, inventory).await?;
},
ToolType::LibertaKit => {
use_tool::liberta_kit(entity_gateway, &used_item, inventory).await?;
},
_ => {}
}
}
_ => {}
}
Ok(())
}
} }

1
src/ship/items/mod.rs

@ -2,6 +2,7 @@ mod bank;
mod floor; mod floor;
mod inventory; mod inventory;
mod manager; mod manager;
pub mod use_tool;
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct ClientItemId(pub u32); pub struct ClientItemId(pub u32);

175
src/ship/items/use_tool.rs

@ -0,0 +1,175 @@
use thiserror::Error;
use crate::entity::gateway::EntityGateway;
use crate::entity::character::CharacterEntity;
use crate::entity::item::{ItemEntityId, ItemDetail};
use crate::entity::item::tool::ToolType;
use crate::entity::item::mag::MagCell;
use crate::ship::items::{ItemManager, ClientItemId, CharacterInventory, ConsumedItem};
#[derive(Error, Debug)]
#[error("")]
pub enum UseItemError {
NoCharacter,
ItemNotEquipped,
InvalidItem,
}
//pub fn use_tool()
pub async fn power_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
character.materials.power += 1;
entity_gateway.save_character(character).await;
}
pub async fn mind_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
character.materials.mind += 1;
entity_gateway.save_character(character).await;
}
pub async fn evade_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
character.materials.evade += 1;
entity_gateway.save_character(character).await;
}
pub async fn def_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
character.materials.def += 1;
entity_gateway.save_character(character).await;
}
pub async fn luck_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
character.materials.luck += 1;
entity_gateway.save_character(character).await;
}
pub async fn hp_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
character.materials.hp += 1;
entity_gateway.save_character(character).await;
}
pub async fn tp_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
character.materials.tp += 1;
entity_gateway.save_character(character).await;
}
async fn mag_cell<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory, mag_cell_type: MagCell) -> Result<(), UseItemError> {
let mut mag_handle = inventory.get_equipped_mag_handle().ok_or(UseItemError::ItemNotEquipped)?;
let mag_item = mag_handle.item_mut()
.ok_or(UseItemError::InvalidItem)?;
let actual_mag = mag_item
.individual()
.ok_or(UseItemError::InvalidItem)?
.mag_mut()
.ok_or(UseItemError::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;
}
}
Ok(())
}
pub async fn cell_of_mag_502<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag502).await
}
pub async fn cell_of_mag_213<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
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<(), UseItemError> {
mag_cell(entity_gateway, used_cell, inventory, MagCell::LibertaKit).await
}

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

@ -263,50 +263,9 @@ 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, &client.character, ClientItemId(player_use_tool.item_id), 1).await?; let item_used_type = item_manager.player_consumes_tool(entity_gateway, &client.character, ClientItemId(player_use_tool.item_id), 1).await?;
match item_used_type {
ItemDetail::Weapon(_w) => {
// something like when items are used to combine/transform them?
//_ => {}
},
ItemDetail::Tool(t) => {
match t.tool {
ToolType::PowerMaterial => {
client.character.materials.power += 1;
entity_gateway.save_character(&client.character).await;
},
ToolType::MindMaterial => {
client.character.materials.mind += 1;
entity_gateway.save_character(&client.character).await;
},
ToolType::EvadeMaterial => {
client.character.materials.evade += 1;
entity_gateway.save_character(&client.character).await;
},
ToolType::DefMaterial => {
client.character.materials.def += 1;
entity_gateway.save_character(&client.character).await;
},
ToolType::LuckMaterial => {
client.character.materials.luck += 1;
entity_gateway.save_character(&client.character).await;
},
ToolType::HpMaterial => {
client.character.materials.hp += 1;
entity_gateway.save_character(&client.character).await;
},
ToolType::TpMaterial => {
client.character.materials.tp += 1;
entity_gateway.save_character(&client.character).await;
},
_ => {}
}
}
_ => {}
}
item_manager.use_item(item_used_type, entity_gateway, &mut client.character).await?;
Ok(Box::new(None.into_iter())) Ok(Box::new(None.into_iter()))
} }
@ -327,3 +286,24 @@ EG: EntityGateway
Err(ShipError::NotEnoughMeseta(id, client.character.meseta)) Err(ShipError::NotEnoughMeseta(id, client.character.meseta))
} }
} }
pub async fn player_feed_mag<EG>(id: ClientId,
mag_feed: &PlayerFeedMag,
entity_gateway: &mut EG,
client_location: &ClientLocation,
clients: &Clients,
item_manager: &mut ItemManager)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
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?;
let mag_feed = mag_feed.clone();
Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter()
.map(move |client| {
(client.client, SendShipPacket::Message(Message::new(GameMessage::PlayerFeedMag(mag_feed.clone()))))
})))
}

3
src/ship/ship.rs

@ -348,6 +348,9 @@ impl<EG: EntityGateway> ShipServerState<EG> {
GameMessage::PlayerUsedMedicalCenter(player_used_medical_center) => { GameMessage::PlayerUsedMedicalCenter(player_used_medical_center) => {
handler::message::player_used_medical_center(id, &player_used_medical_center, &mut self.entity_gateway, &mut self.clients).await handler::message::player_used_medical_center(id, &player_used_medical_center, &mut self.entity_gateway, &mut self.clients).await
}, },
GameMessage::PlayerFeedMag(player_feed_mag) => {
handler::message::player_feed_mag(id, &player_feed_mag, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await
},
_ => { _ => {
let cmsg = msg.clone(); let cmsg = msg.clone();
Ok(Box::new(self.client_location.get_client_neighbors(id).unwrap().into_iter() Ok(Box::new(self.client_location.get_client_neighbors(id).unwrap().into_iter()

210
tests/test_mags.rs

@ -0,0 +1,210 @@
use elseware::common::serverstate::{ClientId, ServerState};
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket};
use elseware::entity::character::{CharacterClass, SectionID};
use libpso::packet::ship::*;
use libpso::packet::messages::*;
#[path = "common.rs"]
mod common;
use common::*;
#[async_std::test]
async fn test_mag_feed() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Mag(
item::mag::Mag::baby_mag(0)
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: true,
}
}).await;
for _ in 0..7 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 1,
equipped: false,
}
}).await;
}
let mut ship = 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;
for _ in 0..7 {
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerFeedMag(PlayerFeedMag {
client: 0,
target: 0,
mag_id: 0x10000,
item_id: 0x10001,
})))).await.unwrap().for_each(drop);
}
let p1_items = entity_gateway.get_items_by_character(&char1).await;
let mag = p1_items.get(0).unwrap();
match &mag.item {
item::ItemDetail::Mag(mag) => {
assert!(mag.level() == 7);
assert!(mag.def() == 5);
assert!(mag.pow() == 2);
assert!(mag.dex() == 0);
assert!(mag.mind() == 0);
}
_ => panic!()
}
}
#[async_std::test]
async fn test_mag_change_owner() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (_user2, mut char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
char1.char_class = CharacterClass::RAmarl;
char1.section_id = SectionID::Redria;
entity_gateway.save_character(&char1).await;
char2.char_class = CharacterClass::FOmarl;
char2.section_id = SectionID::Whitill;
entity_gateway.save_character(&char2).await;
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Mag(
item::mag::Mag::baby_mag(0)
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: true,
}
}).await;
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
join_lobby(&mut ship, ClientId(2)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
join_room(&mut ship, ClientId(2), 0).await;
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
client: 0,
target: 0,
unknown1: 0,
map_area: 0,
item_id: 0x10000,
x: 0.0,
y: 0.0,
z: 0.0,
})))).await.unwrap().for_each(drop);
ship.handle(ClientId(2), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
client: 0,
target: 0,
item_id: 0x10000,
map_area: 0,
unknown: [0; 3]
})))).await.unwrap().for_each(drop);
let p2_items = entity_gateway.get_items_by_character(&char2).await;
let mag = p2_items.get(0).unwrap();
match &mag.item {
item::ItemDetail::Mag(mag) => {
assert!(mag.class == CharacterClass::FOmarl);
assert!(mag.id == SectionID::Whitill);
},
_ => panic!()
}
}
#[async_std::test]
async fn test_mag_cell() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let mag = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Mag(
item::mag::Mag::baby_mag(0)
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: true,
}
}).await.unwrap();
for _ in 0..1000 {
let fed_tool = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool (
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::FedToMag {
mag: mag.id,
}
}).await.unwrap();
entity_gateway.feed_mag(&mag.id, &fed_tool.id).await;
}
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::CellOfMag502,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 1,
equipped: false,
}
}).await;
let mut ship = 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: 0x10001,
})))).await.unwrap().for_each(drop);
let p1_items = entity_gateway.get_items_by_character(&char1).await;
let mag = p1_items.get(0).unwrap();
match &mag.item {
item::ItemDetail::Mag(mag) => {
assert!(mag.mag == item::mag::MagType::Soniti);
}
_ => panic!()
}
}
Loading…
Cancel
Save