use binrw::{BinRead, BinWrite};
use bitflags::bitflags;
use modular_bitfield::prelude::*;
use serde::{Deserialize, Serialize};
use std::io;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct BattlePocket: u8 {
const POKE_BALLS = 1 << 0;
const BATTLE_ITEMS = 1 << 1;
const HP_RESTORE = 1 << 2;
const STATUS_HEALERS = 1 << 3;
const PP_RESTORE = 1 << 4;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, BinRead, BinWrite)]
#[brw(repr = u8)]
pub enum FieldPocket {
#[default]
Items = 0,
Medicine = 1,
Balls = 2,
TmHms = 3,
Berries = 4,
Mail = 5,
BattleItems = 6,
KeyItems = 7,
}
impl FieldPocket {
pub fn try_from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(FieldPocket::Items),
1 => Some(FieldPocket::Medicine),
2 => Some(FieldPocket::Balls),
3 => Some(FieldPocket::TmHms),
4 => Some(FieldPocket::Berries),
5 => Some(FieldPocket::Mail),
6 => Some(FieldPocket::BattleItems),
7 => Some(FieldPocket::KeyItems),
_ => None,
}
}
}
#[bitfield(bits = 16)]
#[derive(Debug, Clone, Copy, Default, BinRead, BinWrite)]
#[br(map = Self::from_bytes)]
#[bw(map = |s: &Self| s.into_bytes())]
#[allow(unused_parens)]
pub struct ItemBitfield {
pub natural_gift_type: B5,
pub prevent_toss: bool,
pub is_selectable: bool,
pub field_pocket: B4,
pub battle_pocket: B5,
}
#[bitfield(bits = 56)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, BinRead, BinWrite)]
#[br(map = Self::from_bytes)]
#[bw(map = |s: &Self| s.into_bytes())]
#[allow(unused_parens)]
pub struct ItemPartyUseFlagsBits {
pub heal_sleep: bool,
pub heal_poison: bool,
pub heal_burn: bool,
pub heal_freeze: bool,
pub heal_paralysis: bool,
pub heal_confusion: bool,
pub heal_attract: bool,
pub guard_spec: bool,
pub revive: bool,
pub revive_all: bool,
pub level_up: bool,
pub evolve: bool,
pub atk_stages: B4,
pub def_stages: B4,
pub spatk_stages: B4,
pub spdef_stages: B4,
pub speed_stages: B4,
pub acc_stages: B4,
pub crit_stages: B2,
pub pp_up: bool,
pub pp_max: bool,
pub pp_restore: bool,
pub pp_restore_all: bool,
pub hp_restore: bool,
pub give_hp_evs: bool,
pub give_atk_evs: bool,
pub give_def_evs: bool,
pub give_speed_evs: bool,
pub give_spatk_evs: bool,
pub give_spdef_evs: bool,
pub give_friendship_low: bool,
pub give_friendship_med: bool,
pub give_friendship_high: bool,
#[skip]
__unused: B4,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, BinRead, BinWrite)]
#[brw(little)]
pub struct ItemPartyUseValues {
pub hp_evs: i8,
pub atk_evs: i8,
pub def_evs: i8,
pub speed_evs: i8,
pub spatk_evs: i8,
pub spdef_evs: i8,
pub hp_restored: u8,
pub pp_restored: u8,
pub friendship_low: i8,
pub friendship_med: i8,
pub friendship_high: i8,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct ItemPartyUseParam {
#[serde(with = "item_party_use_flags_serde")]
flags: ItemPartyUseFlagsBits,
values: ItemPartyUseValues,
}
impl ItemPartyUseParam {
pub fn from_parts(flags: ItemPartyUseFlagsBits, values: ItemPartyUseValues) -> Self {
Self { flags, values }
}
pub fn to_parts(&self) -> (ItemPartyUseFlagsBits, ItemPartyUseValues) {
(self.flags, self.values)
}
}
mod item_party_use_flags_serde {
use super::ItemPartyUseFlagsBits;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S>(flags: &ItemPartyUseFlagsBits, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
flags.into_bytes().serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<ItemPartyUseFlagsBits, D::Error>
where
D: Deserializer<'de>,
{
let bytes = <[u8; 7]>::deserialize(deserializer)?;
Ok(ItemPartyUseFlagsBits::from_bytes(bytes))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ItemData {
pub price: u16,
pub hold_effect: u8,
pub hold_effect_param: u8,
pub pluck_effect: u8,
pub fling_effect: u8,
pub fling_power: u8,
pub natural_gift_power: u8,
pub natural_gift_type: u8,
pub prevent_toss: bool,
pub is_selectable: bool,
pub field_pocket: FieldPocket,
pub battle_pocket: BattlePocket,
pub field_use_func: u8,
pub battle_use_func: u8,
pub party_use: u8,
pub party_use_param: ItemPartyUseParam,
}
impl ItemData {
#[allow(clippy::too_many_arguments)]
pub fn from_parts(
price: u16,
hold_effect: u8,
hold_effect_param: u8,
pluck_effect: u8,
fling_effect: u8,
fling_power: u8,
natural_gift_power: u8,
bitfield: ItemBitfield,
field_use_func: u8,
battle_use_func: u8,
party_use: u8,
party_use_param: ItemPartyUseParam,
) -> io::Result<Self> {
let field_pocket_bits = bitfield.field_pocket();
let field_pocket = FieldPocket::try_from_u8(field_pocket_bits).ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Invalid field pocket bits '{}' in item bitfield (expected 0..=7)",
field_pocket_bits
),
)
})?;
let battle_pocket_bits = bitfield.battle_pocket();
let battle_pocket = BattlePocket::from_bits(battle_pocket_bits).ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Invalid battle pocket bits '{}' in item bitfield",
battle_pocket_bits
),
)
})?;
Ok(Self {
price,
hold_effect,
hold_effect_param,
pluck_effect,
fling_effect,
fling_power,
natural_gift_power,
natural_gift_type: bitfield.natural_gift_type(),
prevent_toss: bitfield.prevent_toss(),
is_selectable: bitfield.is_selectable(),
field_pocket,
battle_pocket,
field_use_func,
battle_use_func,
party_use,
party_use_param,
})
}
pub fn to_bitfield(&self) -> ItemBitfield {
ItemBitfield::new()
.with_natural_gift_type(self.natural_gift_type)
.with_prevent_toss(self.prevent_toss)
.with_is_selectable(self.is_selectable)
.with_field_pocket(self.field_pocket as u8)
.with_battle_pocket(self.battle_pocket.bits())
}
}