mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-09 03:40:23 +00:00
Added proper documentation to moves.rs, support_condition.rs and support_effect.rs
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
//! A digimon card
|
||||
//!
|
||||
//! This module stores the [Digimon] struct, which describes a digimon card.
|
||||
//! This module stores the [`Digimon`] struct, which describes a digimon card.
|
||||
//!
|
||||
//! # Layout
|
||||
//! The digimon card has a size of 0x138 bytes, and it's layout is the following:
|
||||
//! The digimon card has a size of `0x138` bytes, and it's layout is the following:
|
||||
//!
|
||||
//! | Offset | Size | Type | Name | Location | Details |
|
||||
//! |--------|------|----------------------|---------------------------|------------------------|-------------------------------------------------------------------------------------|
|
||||
@@ -12,7 +12,7 @@
|
||||
//! | 0x17 | 0x1 | `u8` | Speciality & Level | `speciality level` | The bottom nibble of this byte is the level, while the top nibble is the speciality |
|
||||
//! | 0x18 | 0x1 | `u8` | DP | `dp_cost` | |
|
||||
//! | 0x19 | 0x1 | `u8` | +P | `dp_give` | |
|
||||
//! | 0x1a | 0x1 | `u8` | Unknown | `unknown_1a` | Is` 0` for all digimon |
|
||||
//! | 0x1a | 0x1 | `u8` | Unknown | `unknown_1a` | Is `0` for all digimon |
|
||||
//! | 0x1b | 0x2 | `u16` | Health | `hp` | |
|
||||
//! | 0x1d | 0x1c | [`Move`] | Circle Move | `move_circle` | |
|
||||
//! | 0x39 | 0x1c | [`Move`] | Triangle move | `move_triangle` | |
|
||||
@@ -48,7 +48,7 @@ use crate::game::{
|
||||
|
||||
/// A digimon card
|
||||
///
|
||||
/// Contains all information about each digimon stored in the [`Card Table`](table::Table)
|
||||
/// Contains all information about each digimon card stored in the [`Card Table`](crate::game::card::table::Table)
|
||||
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Digimon
|
||||
@@ -192,34 +192,19 @@ pub enum FromBytesError
|
||||
EffectThird( #[error(source)] property::support_effect::FromBytesError ),
|
||||
}
|
||||
|
||||
/// The error type thrown by [`Bytes::to_bytes`]
|
||||
#[derive(Debug)]
|
||||
#[derive(derive_more::Display, err_impl::Error)]
|
||||
pub enum ToBytesError
|
||||
{
|
||||
/// Unable to write a move
|
||||
#[display(fmt = "Unable to write the {} move", name)]
|
||||
Move {
|
||||
name: &'static str,
|
||||
#[error(source)]
|
||||
err: property::moves::ToBytesError,
|
||||
},
|
||||
}
|
||||
|
||||
impl Bytes for Digimon
|
||||
{
|
||||
type ByteArray = [u8; 0x138];
|
||||
|
||||
type FromError = FromBytesError;
|
||||
|
||||
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
|
||||
{
|
||||
// Get all byte arrays we need
|
||||
util::array_split!(bytes,
|
||||
0x00..0x1d => _,
|
||||
0x1d..0x39 => move_circle,
|
||||
0x1d..0x39 => move_circle,
|
||||
0x39..0x55 => move_triangle,
|
||||
0x55..0x71 => move_cross,
|
||||
0x55..0x71 => move_cross,
|
||||
0x71..0x91 => condition_first,
|
||||
0x91..0xb1 => condition_second,
|
||||
0xb1..0xc1 => effect_first,
|
||||
@@ -316,7 +301,7 @@ impl Bytes for Digimon
|
||||
})
|
||||
}
|
||||
|
||||
type ToError = ToBytesError;
|
||||
type ToError = !;
|
||||
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
|
||||
{
|
||||
// Get all byte arrays we need
|
||||
@@ -328,9 +313,9 @@ impl Bytes for Digimon
|
||||
0x19..0x1a => dp_give,
|
||||
0x1a..0x1b => unknown_1a,
|
||||
0x1b..0x1d => hp,
|
||||
0x1d..0x39 => move_circle,
|
||||
0x1d..0x39 => move_circle,
|
||||
0x39..0x55 => move_triangle,
|
||||
0x55..0x71 => move_cross,
|
||||
0x55..0x71 => move_cross,
|
||||
0x71..0x91 => condition_first,
|
||||
0x91..0xb1 => condition_second,
|
||||
0xb1..0xc1 => effect_first,
|
||||
@@ -375,9 +360,9 @@ impl Bytes for Digimon
|
||||
LittleEndian::write_u16(hp, self.hp);
|
||||
|
||||
// Moves
|
||||
self. move_circle.to_bytes( move_circle ).map_err(|err| ToBytesError::Move{ name: "circle" , err })?;
|
||||
self.move_triangle.to_bytes( move_triangle ).map_err(|err| ToBytesError::Move{ name: "triangle", err })?;
|
||||
self. move_cross.to_bytes( move_cross ).map_err(|err| ToBytesError::Move{ name: "cross" , err })?;
|
||||
self. move_circle.to_bytes( move_circle )?;
|
||||
self.move_triangle.to_bytes( move_triangle )?;
|
||||
self. move_cross.to_bytes( move_cross )?;
|
||||
|
||||
// Support conditions
|
||||
// Note: Although support conditions and effects aren't written if they're None,
|
||||
@@ -391,17 +376,13 @@ impl Bytes for Digimon
|
||||
if let Some(support_effect) = &self.effects[2] { support_effect.to_bytes( effect_third )?; }
|
||||
|
||||
// Cross move
|
||||
if let Some(move_cross) = self.cross_move_effect { move_cross.to_bytes( &mut cross_move_effect[0] )
|
||||
.expect("Unable to convert cross move effect to bytes")
|
||||
};
|
||||
if let Some(move_cross) = self.cross_move_effect { move_cross.to_bytes( &mut cross_move_effect[0] )? };
|
||||
|
||||
// Unknown
|
||||
unknown_e2[0] = self.unknown_e2;
|
||||
|
||||
// Support arrow color
|
||||
if let Some(arrow_color) = self.effect_arrow_color { arrow_color.to_bytes( &mut effect_arrow_color[1] )
|
||||
.expect("Unable to convert arrow color to bytes");
|
||||
}
|
||||
if let Some(arrow_color) = self.effect_arrow_color { arrow_color.to_bytes( &mut effect_arrow_color[0] )? }
|
||||
|
||||
// effect_description
|
||||
// Note: Each string is at most [char; 20], this cannot fail
|
||||
|
||||
@@ -68,7 +68,7 @@ macro_rules! generate_enum_property_mod
|
||||
)*
|
||||
}
|
||||
|
||||
/// Error type for [`Bytes::from_bytes`]
|
||||
/// Error type for [`$crate::game::Bytes::from_bytes`]
|
||||
#[derive(Debug)]
|
||||
#[derive(::derive_more::Display, ::err_impl::Error)]
|
||||
$mod_vis enum FromBytesError {
|
||||
|
||||
@@ -1,88 +1,95 @@
|
||||
// Crate
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Game
|
||||
use crate::game::util;
|
||||
use crate::game::Bytes;
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
//! A digimon's move
|
||||
//!
|
||||
//! This module contains the [`Move`] struct, which describes a generic move.
|
||||
//!
|
||||
//! # Layout
|
||||
//! Each move has a size of `0x1c` bytes, and it's layout is the following:
|
||||
//!
|
||||
//! | Offset | Size | Type | Name | Location | Details |
|
||||
//! |--------|------|----------------------|---------------------------|------------------------|-----------------------------------|
|
||||
//! | 0x0 | 0x2 | `u16` | Power | `power` | |
|
||||
//! | 0x2 | 0x4 | `u32` | Unknown | `unknown` | Most likely stores animation data |
|
||||
//! | 0x4 | 0x16 | `[char; 0x16]` | Name | `name` | Null-terminated |
|
||||
|
||||
// byteorder
|
||||
use byteorder::ByteOrder;
|
||||
use byteorder::LittleEndian;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
|
||||
// Types
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// A digimon's move
|
||||
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Move
|
||||
// Crate
|
||||
use crate::game::{util, Bytes};
|
||||
|
||||
/// A digimon's move
|
||||
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Move
|
||||
{
|
||||
/// The move's name
|
||||
name: arrayvec::ArrayVec<[ascii::AsciiChar; 21]>,
|
||||
|
||||
/// The move's power
|
||||
power: u16,
|
||||
|
||||
/// The unknown data
|
||||
unknown: u32,
|
||||
}
|
||||
|
||||
/// Error type for [`Bytes::FromBytes`]
|
||||
#[derive(Debug, derive_more::Display, err_impl::Error)]
|
||||
pub enum FromBytesError
|
||||
{
|
||||
/// Unable to read the move name
|
||||
#[display(fmt = "Unable to read the move name")]
|
||||
Name( #[error(source)] util::ReadNullAsciiStringError ),
|
||||
}
|
||||
|
||||
/// Error type for [`Bytes::ToBytes`]
|
||||
#[derive(Debug, derive_more::Display, err_impl::Error)]
|
||||
pub enum ToBytesError
|
||||
{
|
||||
/// The name was too big to be written to file
|
||||
#[display(fmt = "The name \"{}\" is too long to be written to file (max is 21)", _0)]
|
||||
NameTooLong( String ),
|
||||
}
|
||||
|
||||
// Bytes
|
||||
impl Bytes for Move
|
||||
{
|
||||
type ByteArray = [u8; 0x1c];
|
||||
|
||||
type FromError = FromBytesError;
|
||||
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
|
||||
{
|
||||
/// The move's name
|
||||
name: String,
|
||||
|
||||
/// The move's power
|
||||
power: u16,
|
||||
|
||||
/// The unknown data
|
||||
unknown: u32,
|
||||
// And return the move
|
||||
Ok( Self {
|
||||
name : util::read_null_ascii_string( &bytes[0x0..0x15] )
|
||||
.map_err(FromBytesError::Name)?
|
||||
.chars().collect(),
|
||||
power : LittleEndian::read_u16( &bytes[0x0..0x2] ),
|
||||
unknown: LittleEndian::read_u32( &bytes[0x2..0x6] ),
|
||||
})
|
||||
}
|
||||
|
||||
/// The error type thrown by `FromBytes`
|
||||
#[derive(Debug, derive_more::Display, err_impl::Error)]
|
||||
pub enum FromBytesError
|
||||
type ToError = !;
|
||||
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
|
||||
{
|
||||
/// Unable to convert name to a string
|
||||
#[display(fmt = "Unable to convert name to a string")]
|
||||
NameToString( #[error(source)] util::ReadNullTerminatedStringError ),
|
||||
}
|
||||
|
||||
/// The error type thrown by `ToBytes`
|
||||
#[derive(Debug, derive_more::Display, err_impl::Error)]
|
||||
pub enum ToBytesError
|
||||
{
|
||||
/// The name was too big to be written to file
|
||||
#[display(fmt = "The name \"{}\" is too long to be written to file (max is 21)", _0)]
|
||||
NameTooLong( String ),
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
// Impl
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Bytes
|
||||
impl Bytes for Move
|
||||
{
|
||||
type ByteArray = [u8; 0x1c];
|
||||
// Get all byte arrays we need
|
||||
util::array_split_mut!(bytes,
|
||||
0x0..0x02 => power,
|
||||
0x2..0x04 => unknown,
|
||||
0x4..0x1c => name,
|
||||
);
|
||||
|
||||
type FromError = FromBytesError;
|
||||
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
|
||||
{
|
||||
// And return the move
|
||||
Ok( Self {
|
||||
name : util::read_null_terminated_string( &bytes[0x6..0x1c] ).map_err(FromBytesError::NameToString)?.to_string(),
|
||||
power : LittleEndian::read_u16( &bytes[0x0..0x2] ),
|
||||
unknown: LittleEndian::read_u32( &bytes[0x2..0x6] ),
|
||||
})
|
||||
}
|
||||
// Write the name
|
||||
name.copy_from_slice(
|
||||
// Note: `self.name` is at most [char; 21], this cannot fail
|
||||
util::write_null_ascii_string(self.name.as_ref().as_ref(), &mut [0u8; 22])
|
||||
.expect("Name was too large for output buffer")
|
||||
);
|
||||
|
||||
type ToError = ToBytesError;
|
||||
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
|
||||
{
|
||||
// Write the name
|
||||
bytes[0x6..0x1c].copy_from_slice( &{
|
||||
// Check if our name is too big
|
||||
if self.name.len() >= 0x16 { return Err( ToBytesError::NameTooLong( self.name.clone() ) ); }
|
||||
|
||||
// Else make the buffer and copy everything over
|
||||
let mut buf = [0u8; 0x16];
|
||||
buf[ 0..self.name.len() ].copy_from_slice( self.name.as_bytes() );
|
||||
buf
|
||||
});
|
||||
|
||||
// Then write the power and the unknown
|
||||
LittleEndian::write_u16(&mut bytes[0x0..0x2], self.power);
|
||||
LittleEndian::write_u32(&mut bytes[0x2..0x6], self.unknown);
|
||||
|
||||
// And return Ok
|
||||
Ok(())
|
||||
}
|
||||
// Then write the power and the unknown
|
||||
LittleEndian::write_u16(power , self.power );
|
||||
LittleEndian::write_u32(unknown, self.unknown);
|
||||
|
||||
// And return Ok
|
||||
Ok(())
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
@@ -1,146 +1,134 @@
|
||||
// Crate
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Game
|
||||
use crate::game::{Bytes};
|
||||
use crate::game::card::property::{DigimonProperty, SupportConditionOperation};
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
//! A digimon's support condition
|
||||
//!
|
||||
//! This module contains the [`SupportCondition`] struct, which describes a condition for a support effect.
|
||||
//!
|
||||
//! # Layout
|
||||
//! Each support condition has a size of `0x20` bytes, and it's layout is the following:
|
||||
//!
|
||||
//! TODO: Layout
|
||||
//! | Offset | Size | Type | Name | Location | Details |
|
||||
//! |--------|------|----------------------|---------------------------|------------------------|-----------------------------------|
|
||||
|
||||
// byteorder
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
|
||||
// Types
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// A digimon's support effect condition
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct SupportCondition
|
||||
{
|
||||
/// If the effect should throw a misfire if the condition isn't met
|
||||
misfire: bool,
|
||||
|
||||
/// The condition type
|
||||
cond: DigimonProperty,
|
||||
|
||||
/// The type argument
|
||||
type_arg: Option<DigimonProperty>,
|
||||
|
||||
/// The number argument
|
||||
num_arg: u16,
|
||||
|
||||
/// The operation
|
||||
operation: SupportConditionOperation,
|
||||
|
||||
/// Unknown
|
||||
unknown: [u8; 16],
|
||||
}
|
||||
|
||||
/// The error type thrown by `FromBytes`
|
||||
#[derive(Debug, derive_more::Display)]
|
||||
pub enum FromBytesError
|
||||
{
|
||||
/// Unable to read the condition
|
||||
#[display(fmt = "Unable to read the effect condition")]
|
||||
Condition( crate::game::card::property::digimon_property::FromBytesError ),
|
||||
|
||||
/// Unable to read a property argument
|
||||
#[display(fmt = "Unable to read the property argument")]
|
||||
PropertyArgument( crate::game::card::property::digimon_property::FromBytesError ),
|
||||
|
||||
/// Unable to read the effect operation
|
||||
#[display(fmt = "Unable to read the effect operation")]
|
||||
Operation( crate::game::card::property::support_condition_operation::FromBytesError ),
|
||||
}
|
||||
|
||||
impl std::error::Error for FromBytesError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::Condition(err) |
|
||||
Self::PropertyArgument(err) => Some(err),
|
||||
Self::Operation(err) => Some(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Crate
|
||||
use crate::game::{
|
||||
Bytes,
|
||||
card::property::{
|
||||
self, DigimonProperty, SupportConditionOperation
|
||||
},
|
||||
};
|
||||
|
||||
// Impl
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
impl SupportCondition
|
||||
/// A digimon's support effect condition
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct SupportCondition
|
||||
{
|
||||
/// If the effect should throw a misfire if the condition isn't met
|
||||
misfire: bool,
|
||||
|
||||
/// The condition type
|
||||
cond: DigimonProperty,
|
||||
|
||||
/// The type argument
|
||||
type_arg: Option<DigimonProperty>,
|
||||
|
||||
/// The number argument
|
||||
num_arg: u16,
|
||||
|
||||
/// The operation
|
||||
operation: SupportConditionOperation,
|
||||
|
||||
/// Unknown
|
||||
unknown: [u8; 16],
|
||||
}
|
||||
|
||||
/// The error type thrown by `FromBytes`
|
||||
#[derive(Debug)]
|
||||
#[derive(derive_more::Display, err_impl::Error)]
|
||||
pub enum FromBytesError
|
||||
{
|
||||
/// Unable to read the condition
|
||||
#[display(fmt = "Unable to read the effect condition")]
|
||||
Condition( #[error(source)] property::digimon_property::FromBytesError ),
|
||||
|
||||
/// Unable to read a property argument
|
||||
#[display(fmt = "Unable to read the property argument")]
|
||||
PropertyArgument( #[error(source)] property::digimon_property::FromBytesError ),
|
||||
|
||||
/// Unable to read the effect operation
|
||||
#[display(fmt = "Unable to read the effect operation")]
|
||||
Operation( #[error(source)] property::support_condition_operation::FromBytesError ),
|
||||
}
|
||||
|
||||
// Bytes
|
||||
impl Bytes for SupportCondition
|
||||
{
|
||||
type ByteArray = [u8; 0x20];
|
||||
|
||||
type FromError = FromBytesError;
|
||||
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
|
||||
{
|
||||
// Get the condition
|
||||
let cond = DigimonProperty::from_bytes( &bytes[0x2] )
|
||||
.map_err(FromBytesError::Condition)?;
|
||||
|
||||
// And return the move
|
||||
Ok( Self {
|
||||
misfire: (bytes[0x0] != 0),
|
||||
cond,
|
||||
|
||||
type_arg: (bytes[0x8] != 0)
|
||||
.then(|| DigimonProperty::from_bytes( &bytes[0x8] ))
|
||||
.transpose()
|
||||
.map_err(FromBytesError::PropertyArgument)?,
|
||||
|
||||
num_arg: LittleEndian::read_u16( &bytes[0x14..0x16] ),
|
||||
|
||||
operation: SupportConditionOperation::from_bytes( &bytes[0x1a] )
|
||||
.map_err(FromBytesError::Operation)?,
|
||||
|
||||
unknown: [
|
||||
bytes[0x3], bytes[0x4], bytes[0x5], bytes[0x6], bytes[0x7],
|
||||
|
||||
bytes[0x9], bytes[0xa ], bytes[0xb ], bytes[0xc ], bytes[0xd ], bytes[0xe],
|
||||
bytes[0xf], bytes[0x10], bytes[0x11], bytes[0x12], bytes[0x13],
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
// Bytes
|
||||
impl Bytes for SupportCondition
|
||||
type ToError = !;
|
||||
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
|
||||
{
|
||||
type ByteArray = [u8; 0x20];
|
||||
// 0x0 - Misfire
|
||||
bytes[0x0] = if self.misfire { 1 } else { 0 };
|
||||
|
||||
type FromError = FromBytesError;
|
||||
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
|
||||
{
|
||||
// Get the condition
|
||||
let cond = DigimonProperty::from_bytes( &bytes[0x2] ).map_err(FromBytesError::Condition)?;
|
||||
|
||||
// And return the move
|
||||
Ok( Self {
|
||||
misfire: { bytes[0x0] != 0 },
|
||||
cond,
|
||||
|
||||
type_arg: if bytes[0x8] != 0 { Some(
|
||||
DigimonProperty::from_bytes( &bytes[0x8] ).map_err(FromBytesError::PropertyArgument)?
|
||||
)} else { None },
|
||||
|
||||
num_arg: LittleEndian::read_u16( &bytes[0x14..0x16] ),
|
||||
|
||||
operation: SupportConditionOperation::from_bytes( &bytes[0x1a] ).map_err(FromBytesError::Operation)?,
|
||||
|
||||
unknown: [
|
||||
bytes[0x3], bytes[0x4], bytes[0x5], bytes[0x6], bytes[0x7],
|
||||
|
||||
bytes[0x9], bytes[0xa ], bytes[0xb ], bytes[0xc ], bytes[0xd ], bytes[0xe],
|
||||
bytes[0xf], bytes[0x10], bytes[0x11], bytes[0x12], bytes[0x13],
|
||||
]
|
||||
})
|
||||
}
|
||||
// 0x1 - Always zero
|
||||
bytes[0x1] = 0;
|
||||
|
||||
type ToError = !;
|
||||
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
|
||||
{
|
||||
// 0x0 - Misfire
|
||||
bytes[0x0] = if self.misfire { 1 } else { 0 };
|
||||
|
||||
// 0x1 - Always zero
|
||||
bytes[0x1] = 0;
|
||||
|
||||
// 0x2 - Condition
|
||||
#[allow(clippy::diverging_sub_expression)] { // False positive
|
||||
self.cond.to_bytes(&mut bytes[0x2])
|
||||
.expect("Unable to convert condition to bytes");
|
||||
}
|
||||
|
||||
// 0x3..0x8 - Unknown[0..5]
|
||||
bytes[0x3..0x8].copy_from_slice( &self.unknown[0..5] );
|
||||
|
||||
// 0x8 - Type arg / 0 if None
|
||||
if let Some(type_arg) = self.type_arg {
|
||||
type_arg.to_bytes(&mut bytes[0x8])
|
||||
.expect("Unable to convert type argument to bytes")
|
||||
}
|
||||
else { bytes[0x8] = 0; }
|
||||
|
||||
// 0x9..0x14 - Unknown[0x5..0x10]
|
||||
bytes[0x9..0x14].copy_from_slice( &self.unknown[0x5..0x10] );
|
||||
|
||||
// 0x14..0x16 - Number arg
|
||||
LittleEndian::write_u16(&mut bytes[0x14..0x16], self.num_arg);
|
||||
|
||||
// 0x1a - Operation arg
|
||||
#[allow(clippy::diverging_sub_expression)] { // False positive
|
||||
self.operation.to_bytes(&mut bytes[0x1a])
|
||||
.expect("Unable to convert operation to bytes");
|
||||
}
|
||||
|
||||
// And return OK
|
||||
Ok(())
|
||||
// 0x2 - Condition
|
||||
self.cond.to_bytes(&mut bytes[0x2])?;
|
||||
|
||||
// 0x3..0x8 - Unknown[0..5]
|
||||
bytes[0x3..0x8].copy_from_slice( &self.unknown[0..5] );
|
||||
|
||||
// 0x8 - Type arg / 0 if None
|
||||
if let Some(type_arg) = self.type_arg {
|
||||
type_arg.to_bytes(&mut bytes[0x8])?;
|
||||
}
|
||||
else { bytes[0x8] = 0; }
|
||||
|
||||
// 0x9..0x14 - Unknown[0x5..0x10]
|
||||
bytes[0x9..0x14].copy_from_slice( &self.unknown[0x5..0x10] );
|
||||
|
||||
// 0x14..0x16 - Number arg
|
||||
LittleEndian::write_u16(&mut bytes[0x14..0x16], self.num_arg);
|
||||
|
||||
// 0x1a - Operation arg
|
||||
self.operation.to_bytes(&mut bytes[0x1a])?;
|
||||
|
||||
// And return OK
|
||||
Ok(())
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
@@ -1,298 +1,354 @@
|
||||
//! Support effects
|
||||
|
||||
// Lints
|
||||
#![allow(
|
||||
// We have a lot of `a, b, c, x, y` from the formulas,
|
||||
// but we can't change those names since they're the actual
|
||||
// names of the variables in the formulas
|
||||
clippy::many_single_char_names
|
||||
)]
|
||||
//! A digimon's support effect
|
||||
//!
|
||||
//! This module contains the [`SupportEffect`] struct, which describes a support effect.
|
||||
//!
|
||||
//! # Layout
|
||||
//! Each support effect has a size of `0x10` bytes, and it's general layout is the following:
|
||||
//!
|
||||
//! | Offset | Size | Type | Name | Location | Details |
|
||||
//! |--------|------|----------------------|---------------------------|------------------------|---------------------------------------------------------------|
|
||||
//! | 0x0 | 0x1 | `bool` | Exists | N/A | If `0`, the effect does not exist |
|
||||
//! | 0x1 | 0x1 | N/A | Effect Type | N/A | Determines which [`SupportEffect`] variant is used. |
|
||||
//! | 0x2 | 0xe | N/A | Arguments | N/A | The arguments used for the current [`SupportEffect`] variant. |
|
||||
|
||||
// byteorder
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
|
||||
// Crate
|
||||
use crate::{
|
||||
game::{
|
||||
Bytes,
|
||||
card::property::{DigimonProperty, SupportEffectOperation, AttackType, PlayerType, Slot},
|
||||
use crate::game::{
|
||||
Bytes,
|
||||
util,
|
||||
card::property::{
|
||||
self, DigimonProperty, SupportEffectOperation, AttackType, PlayerType, Slot
|
||||
},
|
||||
};
|
||||
|
||||
// Types
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// A digimon's support effects
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
// TODO: Move this `allow` to the variant once clippy allows
|
||||
#[allow(clippy::pub_enum_variant_names)] // `Effect` on `VoidOpponentSupportEffect` isn't refering to the enum
|
||||
pub enum SupportEffect
|
||||
{
|
||||
/// Changes a property of either digimon
|
||||
///
|
||||
/// # Valid properties
|
||||
/// Only the following properties are valid for this effect:
|
||||
/// - `OwnSpeciality` / `OpnSpeciality` ,
|
||||
/// - `OwnHP` / `OpnHP` ,
|
||||
/// - `OwnCircleAttack` / `OpnCircleAttack` ,
|
||||
/// - `OwnTriangleAttack` / `OpnTriangleAttack`,
|
||||
/// - `OwnCrossAttack` / `OpnCrossAttack` ,
|
||||
/// - `OwnAttack` / `OpnAttack` ,
|
||||
/// - `OwnLevel` / `OpnLevel` ,
|
||||
///
|
||||
/// # Equation
|
||||
/// This variant uses the following equation
|
||||
/// to calculate the property:
|
||||
///
|
||||
/// `<property> = ( <A> + <Y> ) + ( <C> <op> ( <B> + <X> ) )`
|
||||
#[serde(rename = "Change property")]
|
||||
ChangeProperty {
|
||||
property: DigimonProperty,
|
||||
|
||||
a: Option<DigimonProperty>,
|
||||
b: Option<DigimonProperty>,
|
||||
c: Option<DigimonProperty>,
|
||||
|
||||
x: u16,
|
||||
y: u16,
|
||||
|
||||
op: SupportEffectOperation,
|
||||
},
|
||||
/// A digimon's support effects
|
||||
///
|
||||
/// As this type is wildly volatile in which arguments it uses and from where,
|
||||
/// it is an `enum` with struct variants instead of a struct. This simplifices argument
|
||||
/// verification and, from a language perspective, makes more sense as an implementation.
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
// TODO: Move this `allow` to the variant once clippy allows
|
||||
#[allow(clippy::pub_enum_variant_names)] // `Effect` on `VoidOpponentSupportEffect` isn't refering to the enum
|
||||
pub enum SupportEffect
|
||||
{
|
||||
/// Changes a property of either digimon
|
||||
///
|
||||
/// # Valid properties
|
||||
/// Only the following properties are valid for this effect:
|
||||
/// - `OwnSpeciality` / `OpnSpeciality` ,
|
||||
/// - `OwnHP` / `OpnHP` ,
|
||||
/// - `OwnCircleAttack` / `OpnCircleAttack` ,
|
||||
/// - `OwnTriangleAttack` / `OpnTriangleAttack`,
|
||||
/// - `OwnCrossAttack` / `OpnCrossAttack` ,
|
||||
/// - `OwnAttack` / `OpnAttack` ,
|
||||
/// - `OwnLevel` / `OpnLevel` ,
|
||||
///
|
||||
/// # Equation
|
||||
/// This variant uses the following equation
|
||||
/// to calculate the property:
|
||||
///
|
||||
/// `<property> = ( <A> + <Y> ) + ( <C> <op> ( <B> + <X> ) )`
|
||||
#[serde(rename = "Change property")]
|
||||
ChangeProperty {
|
||||
property: DigimonProperty,
|
||||
|
||||
/// A player uses an attack type
|
||||
#[serde(rename = "Use attack")]
|
||||
UseAttack {
|
||||
player: PlayerType,
|
||||
attack: AttackType,
|
||||
},
|
||||
a: Option<DigimonProperty>,
|
||||
b: Option<DigimonProperty>,
|
||||
c: Option<DigimonProperty>,
|
||||
|
||||
/// Set the temp slot
|
||||
///
|
||||
/// # Equation
|
||||
/// This variant uses the following equation
|
||||
/// to calculate the property:
|
||||
///
|
||||
/// `<temp slot> = <A> + (<B> <op> <C>)`
|
||||
#[serde(rename = "Set temp slot")]
|
||||
SetTempSlot {
|
||||
a: Option<DigimonProperty>,
|
||||
b: Option<DigimonProperty>,
|
||||
c: Option<DigimonProperty>,
|
||||
|
||||
op: SupportEffectOperation,
|
||||
},
|
||||
x: u16,
|
||||
y: u16,
|
||||
|
||||
/// Moves cards from a slot to another
|
||||
///
|
||||
/// # Valid moves
|
||||
/// Only the following moves are valid for this effect, for both the player and opponent:
|
||||
/// - `Hand` -> `Offline`
|
||||
/// - `Hand` -> `Online`
|
||||
/// - `Online` -> `Offline`
|
||||
/// - `Offline` -> `Online`
|
||||
/// - `Dp` -> `Offline`
|
||||
#[serde(rename = "Move cards")]
|
||||
MoveCards {
|
||||
player : PlayerType,
|
||||
source : Slot,
|
||||
destination: Slot,
|
||||
|
||||
count: u16,
|
||||
},
|
||||
|
||||
/// Shuffles a player's online deck
|
||||
#[serde(rename = "Shuffle online deck")]
|
||||
ShuffleOnlineDeck {
|
||||
player: PlayerType,
|
||||
},
|
||||
|
||||
/// Voids the opponent's support effect
|
||||
#[serde(rename = "Void opponent support effect")]
|
||||
VoidOpponentSupportEffect,
|
||||
|
||||
/// Voids the opponent's support option effect
|
||||
#[serde(rename = "Void opponent support option effect")]
|
||||
VoidOpponentSupportOptionEffect,
|
||||
|
||||
/// Picks the partner from the online deck and puts it onto the hand
|
||||
#[serde(rename = "Pick partner card")]
|
||||
PickPartnerCard,
|
||||
|
||||
/// Cycles the opponent's attack types
|
||||
///
|
||||
/// # Order
|
||||
/// The order is the following:
|
||||
/// - `Circle` -> `Triangle`
|
||||
/// - `Triangle` -> `Cross`
|
||||
/// - `Cross` -> `Circle`
|
||||
#[serde(rename = "Cycle opponent attack type")]
|
||||
CycleOpponentAttackType,
|
||||
|
||||
/// If the digimon is Ko'd it revives with health
|
||||
#[serde(rename = "Ko'd digimon revives")]
|
||||
KoDigimonRevives {
|
||||
health: u16,
|
||||
},
|
||||
|
||||
/// A player draws cards
|
||||
#[serde(rename = "Draw cards")]
|
||||
DrawCards {
|
||||
player: PlayerType,
|
||||
|
||||
count: u16,
|
||||
},
|
||||
|
||||
/// Own attack becomes Eat Up HP
|
||||
#[serde(rename = "Own attack becomes Eat Up HP")]
|
||||
OwnAttackBecomesEatUpHP,
|
||||
|
||||
/// A player attacks first
|
||||
#[serde(rename = "Attack first")]
|
||||
AttackFirst {
|
||||
player: PlayerType
|
||||
},
|
||||
}
|
||||
op: SupportEffectOperation,
|
||||
},
|
||||
|
||||
/// A player uses an attack type
|
||||
#[serde(rename = "Use attack")]
|
||||
UseAttack {
|
||||
player: PlayerType,
|
||||
attack: AttackType,
|
||||
},
|
||||
|
||||
/// The error type thrown by `FromBytes`
|
||||
#[derive(Debug, derive_more::Display)]
|
||||
pub enum FromBytesError
|
||||
{
|
||||
/// An unknown effect type was found
|
||||
#[display(fmt = "Unknown byte for an effect type: {}", byte)]
|
||||
UnknownEffectType { byte: u8 },
|
||||
/// Set the temp slot
|
||||
///
|
||||
/// # Equation
|
||||
/// This variant uses the following equation
|
||||
/// to calculate the property:
|
||||
///
|
||||
/// `<temp slot> = <A> + (<B> <op> <C>)`
|
||||
#[serde(rename = "Set temp slot")]
|
||||
SetTempSlot {
|
||||
a: Option<DigimonProperty>,
|
||||
b: Option<DigimonProperty>,
|
||||
c: Option<DigimonProperty>,
|
||||
|
||||
/// An unknown property argument was found
|
||||
#[display(fmt = "An unknown property was found for the {} property", rank)]
|
||||
PropertyArgument {
|
||||
rank: &'static str,
|
||||
|
||||
|
||||
err: crate::game::card::property::digimon_property::FromBytesError,
|
||||
},
|
||||
|
||||
/// An unknown operation was found
|
||||
#[display(fmt = "An unknown operation was found")]
|
||||
Operation( crate::game::card::property::support_effect_operation::FromBytesError ),
|
||||
|
||||
/// An unknown attack type was found
|
||||
#[display(fmt = "An unknown attack type was found")]
|
||||
AttackType( crate::game::card::property::attack_type::FromBytesError ),
|
||||
}
|
||||
op: SupportEffectOperation,
|
||||
},
|
||||
|
||||
impl std::error::Error for FromBytesError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::UnknownEffectType{ .. } => None,
|
||||
Self::PropertyArgument{ err, ..} => Some(err),
|
||||
Self::Operation(err) => Some(err),
|
||||
Self::AttackType(err) => Some(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Moves cards from a slot to another
|
||||
///
|
||||
/// # Valid moves
|
||||
/// Only the following moves are valid for this effect, for both the player and opponent:
|
||||
/// - `Hand` -> `Offline`
|
||||
/// - `Hand` -> `Online`
|
||||
/// - `Online` -> `Offline`
|
||||
/// - `Offline` -> `Online`
|
||||
/// - `Dp` -> `Offline`
|
||||
#[serde(rename = "Move cards")]
|
||||
MoveCards {
|
||||
player : PlayerType,
|
||||
source : Slot,
|
||||
destination: Slot,
|
||||
|
||||
count: u16,
|
||||
},
|
||||
|
||||
/// Shuffles a player's online deck
|
||||
#[serde(rename = "Shuffle online deck")]
|
||||
ShuffleOnlineDeck {
|
||||
player: PlayerType,
|
||||
},
|
||||
|
||||
/// Voids the opponent's support effect
|
||||
#[serde(rename = "Void opponent support effect")]
|
||||
VoidOpponentSupportEffect,
|
||||
|
||||
/// Voids the opponent's support option effect
|
||||
#[serde(rename = "Void opponent support option effect")]
|
||||
VoidOpponentSupportOptionEffect,
|
||||
|
||||
/// Picks the partner from the online deck and puts it onto the hand
|
||||
#[serde(rename = "Pick partner card")]
|
||||
PickPartnerCard,
|
||||
|
||||
/// Cycles the opponent's attack types
|
||||
///
|
||||
/// # Order
|
||||
/// The order is the following:
|
||||
/// - `Circle` -> `Triangle`
|
||||
/// - `Triangle` -> `Cross`
|
||||
/// - `Cross` -> `Circle`
|
||||
#[serde(rename = "Cycle opponent attack type")]
|
||||
CycleOpponentAttackType,
|
||||
|
||||
/// If the digimon is Ko'd it revives with health
|
||||
#[serde(rename = "Ko'd digimon revives")]
|
||||
KoDigimonRevives {
|
||||
health: u16,
|
||||
},
|
||||
|
||||
/// A player draws cards
|
||||
#[serde(rename = "Draw cards")]
|
||||
DrawCards {
|
||||
player: PlayerType,
|
||||
count: u16,
|
||||
},
|
||||
|
||||
/// Own attack becomes Eat Up HP
|
||||
#[serde(rename = "Own attack becomes Eat Up HP")]
|
||||
OwnAttackBecomesEatUpHP,
|
||||
|
||||
/// A player attacks first
|
||||
#[serde(rename = "Attack first")]
|
||||
AttackFirst {
|
||||
player: PlayerType
|
||||
},
|
||||
}
|
||||
|
||||
// Impl
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
impl Bytes for SupportEffect
|
||||
/// Error type for [`Bytes::from_bytes`]
|
||||
#[derive(Debug)]
|
||||
#[derive(derive_more::Display, err_impl::Error)]
|
||||
pub enum FromBytesError
|
||||
{
|
||||
/// Unknown property for first property argument
|
||||
#[display(fmt = "Unknown property for first property argument")]
|
||||
FirstProperty( #[error(source)] property::digimon_property::FromBytesError ),
|
||||
|
||||
/// Unknown property for second property argument
|
||||
#[display(fmt = "Unknown property for second property argument")]
|
||||
SecondProperty( #[error(source)] property::digimon_property::FromBytesError ),
|
||||
|
||||
/// Unknown property for third property argument
|
||||
#[display(fmt = "Unknown property for third property argument")]
|
||||
ThirdProperty( #[error(source)] property::digimon_property::FromBytesError ),
|
||||
|
||||
/// Unknown operation argument
|
||||
#[display(fmt = "Unknown operation argument")]
|
||||
Operation( #[error(source)] property::support_effect_operation::FromBytesError ),
|
||||
|
||||
/// Unknown attack type for [`SupportEffect::UseAttack`]
|
||||
#[display(fmt = "Unknown attack type")]
|
||||
UseAttackAttackType( #[error(source)] property::attack_type::FromBytesError ),
|
||||
|
||||
/// Unknown effect type
|
||||
#[display(fmt = "Unknown byte for an effect type: {}", "byte")]
|
||||
EffectType { byte: u8 },
|
||||
}
|
||||
|
||||
impl Bytes for SupportEffect
|
||||
{
|
||||
type ByteArray = [u8; 0x10];
|
||||
|
||||
type FromError = FromBytesError;
|
||||
|
||||
/// `bytes` should include the `exists` byte
|
||||
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
|
||||
{
|
||||
type ByteArray = [u8; 0x10];
|
||||
// Utility uses
|
||||
use PlayerType::{Player, Opponent};
|
||||
use Slot::{Hand, Online as OnlineDeck, Offline as OfflineDeck, Dp as DpSlot};
|
||||
|
||||
type FromError = FromBytesError;
|
||||
/// `bytes` should include the `exists` byte
|
||||
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError>
|
||||
{
|
||||
// Assert that we do exist
|
||||
assert_ne!(bytes[0x0], 0);
|
||||
|
||||
// The effect type byte
|
||||
let effect_type_byte = bytes[0x1];
|
||||
|
||||
// The properties
|
||||
let a = if bytes[0x2] != 0 {
|
||||
Some( DigimonProperty::from_bytes( &bytes[0x2] ) .map_err(|err| FromBytesError::PropertyArgument{ rank: "1st", err })? )
|
||||
} else { None };
|
||||
|
||||
let b = if bytes[0x4] != 0 {
|
||||
Some( DigimonProperty::from_bytes( &bytes[0x4] ) .map_err(|err| FromBytesError::PropertyArgument{ rank: "2nd", err })? )
|
||||
} else { None };
|
||||
|
||||
let c = if bytes[0x6] != 0 {
|
||||
Some( DigimonProperty::from_bytes( &bytes[0x6] ) .map_err(|err| FromBytesError::PropertyArgument{ rank: "3rd", err })? )
|
||||
} else { None };
|
||||
|
||||
// The numbers
|
||||
let x = LittleEndian::read_u16( &bytes[0xa..0xc] );
|
||||
let y = LittleEndian::read_u16( &bytes[0xc..0xe] );
|
||||
|
||||
// The operation
|
||||
let op = SupportEffectOperation::from_bytes( &bytes[0xf] ) .map_err(FromBytesError::Operation)?;
|
||||
|
||||
// Check what the effect type is
|
||||
match effect_type_byte
|
||||
{
|
||||
0..=13 => {
|
||||
Ok( Self::ChangeProperty {
|
||||
// Note: unwrapping is fine here because we know that `effect_type_byte+1` is between 1 and 14 inclusive
|
||||
property: DigimonProperty::from_bytes( &(effect_type_byte+1) )
|
||||
.expect("Unable to get digimon property from bytes"),
|
||||
a, b, c, x, y, op,
|
||||
})
|
||||
},
|
||||
|
||||
// Take lower byte from `x` for these
|
||||
16 => { Ok( Self::UseAttack{ player: PlayerType::Player , attack: AttackType::from_bytes( &x.to_le_bytes()[0] ) .map_err(FromBytesError::AttackType)? } ) },
|
||||
17 => { Ok( Self::UseAttack{ player: PlayerType::Opponent, attack: AttackType::from_bytes( &x.to_le_bytes()[0] ) .map_err(FromBytesError::AttackType)? } ) },
|
||||
|
||||
|
||||
25 => { Ok( Self::SetTempSlot{ a, b, c, op } ) },
|
||||
|
||||
26 => { Ok( Self::MoveCards{ player: PlayerType::Player , source: Slot::Hand , destination: Slot::Offline, count: y } ) },
|
||||
27 => { Ok( Self::MoveCards{ player: PlayerType::Opponent, source: Slot::Hand , destination: Slot::Offline, count: y } ) },
|
||||
|
||||
30 => { Ok( Self::MoveCards{ player: PlayerType::Player , source: Slot::Hand , destination: Slot::Online , count: y } ) },
|
||||
31 => { Ok( Self::MoveCards{ player: PlayerType::Opponent, source: Slot::Hand , destination: Slot::Online , count: y } ) },
|
||||
|
||||
32 => { Ok( Self::MoveCards{ player: PlayerType::Player , source: Slot::Online , destination: Slot::Offline, count: y } ) },
|
||||
33 => { Ok( Self::MoveCards{ player: PlayerType::Opponent, source: Slot::Online , destination: Slot::Offline, count: y } ) },
|
||||
|
||||
34 => { Ok( Self::MoveCards{ player: PlayerType::Player , source: Slot::Offline, destination: Slot::Online , count: y } ) },
|
||||
35 => { Ok( Self::MoveCards{ player: PlayerType::Opponent, source: Slot::Offline, destination: Slot::Online , count: y } ) },
|
||||
|
||||
36 => { Ok( Self::MoveCards{ player: PlayerType::Player , source: Slot::Dp , destination: Slot::Offline, count: y } ) },
|
||||
37 => { Ok( Self::MoveCards{ player: PlayerType::Opponent, source: Slot::Dp , destination: Slot::Offline, count: y } ) },
|
||||
|
||||
|
||||
42 => { Ok( Self::ShuffleOnlineDeck{ player: PlayerType::Player } ) },
|
||||
43 => { Ok( Self::ShuffleOnlineDeck{ player: PlayerType::Opponent } ) },
|
||||
|
||||
44 => { Ok( Self::VoidOpponentSupportEffect ) },
|
||||
45 => { Ok( Self::VoidOpponentSupportOptionEffect ) },
|
||||
|
||||
46 => { Ok( Self::PickPartnerCard ) },
|
||||
|
||||
47 => { Ok( Self::CycleOpponentAttackType ) },
|
||||
|
||||
48 => { Ok( Self::KoDigimonRevives{ health: y } ) },
|
||||
|
||||
49 => { Ok( Self::DrawCards{ player: PlayerType::Player , count: y } ) },
|
||||
50 => { Ok( Self::DrawCards{ player: PlayerType::Opponent, count: y } ) },
|
||||
|
||||
51 => { Ok( Self::OwnAttackBecomesEatUpHP ) },
|
||||
|
||||
52 => { Ok( Self::AttackFirst{ player: PlayerType::Player } ) },
|
||||
53 => { Ok( Self::AttackFirst{ player: PlayerType::Opponent } ) },
|
||||
|
||||
_ => Err( FromBytesError::UnknownEffectType{ byte: effect_type_byte } ),
|
||||
}
|
||||
}
|
||||
// The effect type byte
|
||||
let effect_type_byte = bytes[0x1];
|
||||
|
||||
type ToError = !;
|
||||
fn to_bytes(&self, _bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
|
||||
// The property argument getters
|
||||
let get_a = || (bytes[0x2] != 0)
|
||||
.then(|| DigimonProperty::from_bytes( &bytes[0x2] ))
|
||||
.transpose()
|
||||
.map_err(FromBytesError::FirstProperty);
|
||||
let get_b = || (bytes[0x4] != 0)
|
||||
.then(|| DigimonProperty::from_bytes( &bytes[0x4] ))
|
||||
.transpose()
|
||||
.map_err(FromBytesError::SecondProperty);
|
||||
let get_c = || (bytes[0x6] != 0)
|
||||
.then(|| DigimonProperty::from_bytes( &bytes[0x6] ))
|
||||
.transpose()
|
||||
.map_err(FromBytesError::ThirdProperty);
|
||||
let get_attack_type = || AttackType::from_bytes( &bytes[0xa] ) // Lower byte of `x`
|
||||
.map_err(FromBytesError::UseAttackAttackType);
|
||||
|
||||
// The number arguments
|
||||
let x = LittleEndian::read_u16( &bytes[0xa..0xc] );
|
||||
let y = LittleEndian::read_u16( &bytes[0xc..0xe] );
|
||||
|
||||
// The operation argument
|
||||
let op = SupportEffectOperation::from_bytes( &bytes[0xf] )
|
||||
.map_err(FromBytesError::Operation)?;
|
||||
|
||||
// Check what the effect type is
|
||||
match effect_type_byte
|
||||
{
|
||||
// Match which effect we are
|
||||
todo!()
|
||||
0..=13 => Ok( Self::ChangeProperty {
|
||||
// Note: unwrapping is fine here because we know that `effect_type_byte+1` is between 1 and 14 inclusive
|
||||
property: DigimonProperty::from_bytes( &(effect_type_byte+1) )
|
||||
.expect("Unable to get digimon property from bytes"),
|
||||
a: get_a()?, b: get_b()?, c: get_c()?, x, y, op,
|
||||
}),
|
||||
|
||||
|
||||
// Lower byte of `x` contains the attack type
|
||||
16 => Ok( Self::UseAttack{ player: Player , attack: get_attack_type()? }),
|
||||
17 => Ok( Self::UseAttack{ player: Opponent, attack: get_attack_type()? }),
|
||||
|
||||
|
||||
25 => Ok( Self::SetTempSlot{ a: get_a()?, b: get_b()?, c: get_c()?, op } ),
|
||||
|
||||
26 => Ok( Self::MoveCards{ player: Player , source: Hand, destination: OfflineDeck, count: y } ),
|
||||
27 => Ok( Self::MoveCards{ player: Opponent, source: Hand, destination: OfflineDeck, count: y } ),
|
||||
|
||||
30 => Ok( Self::MoveCards{ player: Player , source: Hand, destination: OnlineDeck, count: y } ),
|
||||
31 => Ok( Self::MoveCards{ player: Opponent, source: Hand, destination: OnlineDeck, count: y } ),
|
||||
|
||||
32 => Ok( Self::MoveCards{ player: Player , source: OnlineDeck, destination: OfflineDeck, count: y } ),
|
||||
33 => Ok( Self::MoveCards{ player: Opponent, source: OnlineDeck, destination: OfflineDeck, count: y } ),
|
||||
|
||||
34 => Ok( Self::MoveCards{ player: Player , source: OfflineDeck, destination: OnlineDeck, count: y } ),
|
||||
35 => Ok( Self::MoveCards{ player: Opponent, source: OfflineDeck, destination: OnlineDeck, count: y } ),
|
||||
|
||||
36 => Ok( Self::MoveCards{ player: Player , source: DpSlot, destination: OfflineDeck, count: y } ),
|
||||
37 => Ok( Self::MoveCards{ player: Opponent, source: DpSlot, destination: OfflineDeck, count: y } ),
|
||||
|
||||
|
||||
42 => Ok( Self::ShuffleOnlineDeck{ player: Player } ),
|
||||
43 => Ok( Self::ShuffleOnlineDeck{ player: Opponent } ),
|
||||
|
||||
44 => Ok( Self::VoidOpponentSupportEffect ),
|
||||
45 => Ok( Self::VoidOpponentSupportOptionEffect ),
|
||||
|
||||
46 => Ok( Self::PickPartnerCard ),
|
||||
|
||||
47 => Ok( Self::CycleOpponentAttackType ),
|
||||
|
||||
48 => Ok( Self::KoDigimonRevives{ health: y } ),
|
||||
|
||||
49 => Ok( Self::DrawCards{ player: Player , count: y } ),
|
||||
50 => Ok( Self::DrawCards{ player: Opponent, count: y } ),
|
||||
|
||||
51 => Ok( Self::OwnAttackBecomesEatUpHP ),
|
||||
|
||||
52 => Ok( Self::AttackFirst{ player: Player } ),
|
||||
53 => Ok( Self::AttackFirst{ player: Opponent } ),
|
||||
|
||||
_ => Err( FromBytesError::EffectType{ byte: effect_type_byte } ),
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
type ToError = !;
|
||||
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
|
||||
{
|
||||
// Get all byte arrays we need
|
||||
util::array_split_mut!(bytes,
|
||||
0x0..0x01 => exists,
|
||||
0x1..0x02 => effect_type,
|
||||
0x2..0x03 => bytes_a,
|
||||
0x3..0x04 => _,
|
||||
0x4..0x05 => bytes_b,
|
||||
0x5..0x06 => _,
|
||||
0x6..0x07 => bytes_c,
|
||||
0x7..0x0a => _,
|
||||
0xa..0x0c => bytes_x,
|
||||
0xc..0x0e => bytes_y,
|
||||
0xe..0x0f => _,
|
||||
0xf..0x10 => bytes_op,
|
||||
);
|
||||
|
||||
// Set that the effect exists
|
||||
exists[0] = 1;
|
||||
|
||||
// Check our variant and fill `bytes` with info
|
||||
#[allow(clippy::unneeded_field_pattern)] // Placeholder
|
||||
match self {
|
||||
Self::ChangeProperty { property, a, b, c, x, y, op } => {
|
||||
property.to_bytes(&mut effect_type[0])?;
|
||||
effect_type[0] -= 1;
|
||||
if let Some(a) = a { a.to_bytes(&mut bytes_a[0])?; }
|
||||
if let Some(b) = b { b.to_bytes(&mut bytes_b[0])?; }
|
||||
if let Some(c) = c { c.to_bytes(&mut bytes_c[0])?; }
|
||||
LittleEndian::write_u16(bytes_x, *x);
|
||||
LittleEndian::write_u16(bytes_y, *y);
|
||||
op.to_bytes(&mut bytes_op[0])?;
|
||||
},
|
||||
|
||||
Self::UseAttack { player: _, attack: _ } => todo!(),
|
||||
|
||||
Self::SetTempSlot { a: _, b: _, c: _, op: _ } => todo!(),
|
||||
|
||||
Self::MoveCards { player: _, source: _, destination: _, count: _ } => todo!(),
|
||||
|
||||
Self::ShuffleOnlineDeck { player: _ } => todo!(),
|
||||
|
||||
Self::VoidOpponentSupportEffect => todo!(),
|
||||
|
||||
Self::VoidOpponentSupportOptionEffect => todo!(),
|
||||
|
||||
Self::PickPartnerCard => todo!(),
|
||||
|
||||
Self::CycleOpponentAttackType => todo!(),
|
||||
|
||||
Self::KoDigimonRevives { health: _ } => todo!(),
|
||||
|
||||
Self::DrawCards { player: _, count: _ } => todo!(),
|
||||
|
||||
Self::OwnAttackBecomesEatUpHP => todo!(),
|
||||
|
||||
Self::AttackFirst { player: _ } => todo!(),
|
||||
}
|
||||
|
||||
// And return Ok
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@
|
||||
clippy::identity_op, // Makes sense sometimes for symmetry
|
||||
clippy::items_after_statements, // Sometimes we only introduce items when we first use them.
|
||||
clippy::unseparated_literal_suffix, // We only separate them when they are long
|
||||
clippy::diverging_sub_expression, // We use `?` on `Result<T, !>` for extracting the result currently, once a method is done for it, we'll use it.
|
||||
clippy::match_same_arms, // Sometimes we separate them for clarify and order
|
||||
|
||||
// TODO: Deal with casts eventually
|
||||
clippy::cast_possible_wrap,
|
||||
|
||||
Reference in New Issue
Block a user