Finished Effect to_bytes implementation.

This commit is contained in:
2020-04-30 05:59:33 +01:00
parent e52cc554ff
commit 596bf177d6
3 changed files with 220 additions and 99 deletions

View File

@@ -227,6 +227,18 @@ pub enum ToBytesError
/// Unable to write the cross move
#[display(fmt = "Unable to write the cross move")]
MoveCross( #[error(source)] property::moves::ToBytesError ),
/// Unable to write the first effect
#[display(fmt = "Unable to write the first effect")]
EffectFirst( #[error(source)] property::effect::ToBytesError ),
/// Unable to write the second effect
#[display(fmt = "Unable to write the second effect")]
EffectSecond( #[error(source)] property::effect::ToBytesError ),
/// Unable to write the third effect
#[display(fmt = "Unable to write the third effect")]
EffectThird( #[error(source)] property::effect::ToBytesError ),
}
impl Bytes for Digimon
@@ -301,19 +313,13 @@ impl Bytes for Digimon
],
effects: [
(bytes.effect_first[0x0] != 0)
.then(|| Effect::from_bytes( bytes.effect_first ) )
.transpose()
Option::<Effect>::from_bytes( bytes.effect_first )
.map_err(FromBytesError::EffectFirst)?,
(bytes.effect_second[0x0] != 0)
.then(|| Effect::from_bytes( bytes.effect_second ) )
.transpose()
Option::<Effect>::from_bytes( bytes.effect_second )
.map_err(FromBytesError::EffectSecond)?,
(bytes.effect_third[0x0] != 0)
.then(|| Effect::from_bytes( bytes.effect_third ) )
.transpose()
Option::<Effect>::from_bytes( bytes.effect_third )
.map_err(FromBytesError::EffectThird)?,
],
@@ -414,9 +420,9 @@ impl Bytes for Digimon
self.effect_conditions[1].to_bytes( bytes.condition_second ).into_ok();
// Effects
if let Some(effect) = &self.effects[0] { effect.to_bytes( bytes.effect_first )?; }
if let Some(effect) = &self.effects[1] { effect.to_bytes( bytes.effect_second )?; }
if let Some(effect) = &self.effects[2] { effect.to_bytes( bytes.effect_third )?; }
self.effects[0].to_bytes( bytes.effect_first ).map_err(ToBytesError::EffectFirst )?;
self.effects[1].to_bytes( bytes.effect_second ).map_err(ToBytesError::EffectSecond)?;
self.effects[2].to_bytes( bytes.effect_third ).map_err(ToBytesError::EffectThird )?;
// Cross move
if let Some(move_cross) = self.cross_move_effect { move_cross.to_bytes( bytes.cross_move_effect )? };

View File

@@ -183,17 +183,12 @@ use serde::Deserialize;
},
effects: SupportEffects {
first: if bytes[0x59] != 0 { Some(
Effect::from_bytes( array_ref!(bytes, 0x59, 0x10) ).map_err(|err| FromBytesError::SupportEffect{ rank: "1st", err })?
)} else { None },
second: if bytes[0x69] != 0 { Some(
Effect::from_bytes( array_ref!(bytes, 0x69, 0x10) ).map_err(|err| FromBytesError::SupportEffect{ rank: "2nd", err })?
)} else { None },
third: if bytes[0x79] != 0 { Some(
Effect::from_bytes( array_ref!(bytes, 0x79, 0x10) ).map_err(|err| FromBytesError::SupportEffect{ rank: "3rd", err })?
)} else { None },
first: Option::<Effect>::from_bytes( array_ref!(bytes, 0x59, 0x10) )
.map_err(|err| FromBytesError::SupportEffect{ rank: "1st", err })?,
second: Option::<Effect>::from_bytes( array_ref!(bytes, 0x69, 0x10) )
.map_err(|err| FromBytesError::SupportEffect{ rank: "2nd", err })?,
third: Option::<Effect>::from_bytes( array_ref!(bytes, 0x79, 0x10) )
.map_err(|err| FromBytesError::SupportEffect{ rank: "3rd", err })?,
},
},
})
@@ -251,9 +246,9 @@ use serde::Deserialize;
// If they are None, 0 is a valid value for the effects
if let Some(support_effect) = &self.effects.effects.first { support_effect.to_bytes( array_mut_ref!(bytes, 0x59, 0x10) )?; }
if let Some(support_effect) = &self.effects.effects.second { support_effect.to_bytes( array_mut_ref!(bytes, 0x69, 0x10) )?; }
if let Some(support_effect) = &self.effects.effects.third { support_effect.to_bytes( array_mut_ref!(bytes, 0x79, 0x10) )?; }
self.effects.effects.first .to_bytes( array_mut_ref!(bytes, 0x59, 0x10) ).expect("TODO");
self.effects.effects.second.to_bytes( array_mut_ref!(bytes, 0x69, 0x10) ).expect("TODO");
self.effects.effects.third .to_bytes( array_mut_ref!(bytes, 0x79, 0x10) ).expect("TODO");
//--------------------------------------------------------------------------------------------------
// Return the bytes

View File

@@ -189,7 +189,21 @@ pub enum FromBytesError
EffectType { byte: u8 },
}
impl Bytes for Effect
/// Error type for [`Bytes::from_bytes`]
#[derive(Debug)]
#[derive(derive_more::Display, err_impl::Error)]
pub enum ToBytesError
{
/// Invalid move [`Effect::MoveCards`] effect
#[display(fmt = "Invalid move cards effect ({} => {})", source, destination)]
InvalidMoveCards {
source : Slot,
destination: Slot,
}
}
#[allow(clippy::use_self)] // False positive
impl Bytes for Option<Effect>
{
type ByteArray = [u8; 0x10];
@@ -202,95 +216,122 @@ impl Bytes for Effect
use PlayerType::{Player, Opponent};
use Slot::{Hand, Online as OnlineDeck, Offline as OfflineDeck, Dp as DpSlot};
// The effect type byte
let effect_type_byte = bytes[0x1];
// Get all byte arrays we need
let bytes = util::array_split!(bytes,
exists : 0x1,
effect_type: 0x1,
a : 0x1,
_unknown_3 : 0x1,
b : 0x1,
_unknown_5 : 0x1,
c : 0x1,
_unknown_7 : [0x3],
x : [0x2],
y : [0x2],
_unknown_e : 0x1,
op : 0x1,
);
// The property argument getters
let get_a = || (bytes[0x2] != 0)
.then(|| DigimonProperty::from_bytes( &bytes[0x2] ))
// If the exists byte is 0, return None
if *bytes.exists == 0 {
return Ok(None);
}
// Else create getters for all arguments
let get_a = || (*bytes.a != 0)
.then(|| DigimonProperty::from_bytes(bytes.a))
.transpose()
.map_err(FromBytesError::FirstProperty);
let get_b = || (bytes[0x4] != 0)
.then(|| DigimonProperty::from_bytes( &bytes[0x4] ))
let get_b = || (*bytes.b != 0)
.then(|| DigimonProperty::from_bytes(bytes.b))
.transpose()
.map_err(FromBytesError::SecondProperty);
let get_c = || (bytes[0x6] != 0)
.then(|| DigimonProperty::from_bytes( &bytes[0x6] ))
let get_c = || (*bytes.c != 0)
.then(|| DigimonProperty::from_bytes(bytes.c))
.transpose()
.map_err(FromBytesError::ThirdProperty);
// Lower byte of `x` contains the attack type
let get_attack_type = || AttackType::from_bytes( &bytes[0xa] ) // Lower byte of `x`
// The number arguments
let x = LittleEndian::read_u16( bytes.x );
let y = LittleEndian::read_u16( bytes.y );
// Attack type
// Lower byte of `x`
let get_attack_type = || AttackType::from_bytes( &x.to_le_bytes()[0] )
.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 = EffectOperation::from_bytes( &bytes[0xf] )
.map_err(FromBytesError::Operation)?;
let get_op = || EffectOperation::from_bytes( bytes.op )
.map_err(FromBytesError::Operation);
// Check what the effect type is
match effect_type_byte
// And check what the effect type is
let effect = match bytes.effect_type
{
0..=13 => Ok( Self::ChangeProperty {
0..=13 => Effect::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) )
property: DigimonProperty::from_bytes( &(bytes.effect_type+1) )
.expect("Unable to get digimon property from bytes"),
a: get_a()?, b: get_b()?, c: get_c()?, x, y, op,
}),
a: get_a()?, b: get_b()?, c: get_c()?, x, y, op: get_op()?,
},
16 => Ok( Self::UseAttack{ player: Player , attack: get_attack_type()? }),
17 => Ok( Self::UseAttack{ player: Opponent, attack: get_attack_type()? }),
16 => Effect::UseAttack{ player: Player , attack: get_attack_type()? },
17 => Effect::UseAttack{ player: Opponent, attack: get_attack_type()? },
25 => Ok( Self::SetTempSlot{ a: get_a()?, b: get_b()?, c: get_c()?, op } ),
25 => Effect::SetTempSlot{ a: get_a()?, b: get_b()?, c: get_c()?, op: get_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 } ),
26 => Effect::MoveCards{ player: Player , source: Hand, destination: OfflineDeck, count: y },
27 => Effect::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 } ),
30 => Effect::MoveCards{ player: Player , source: Hand, destination: OnlineDeck, count: y },
31 => Effect::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 } ),
32 => Effect::MoveCards{ player: Player , source: OnlineDeck, destination: OfflineDeck, count: y },
33 => Effect::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 } ),
34 => Effect::MoveCards{ player: Player , source: OfflineDeck, destination: OnlineDeck, count: y },
35 => Effect::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 } ),
36 => Effect::MoveCards{ player: Player , source: DpSlot, destination: OfflineDeck, count: y },
37 => Effect::MoveCards{ player: Opponent, source: DpSlot, destination: OfflineDeck, count: y },
42 => Ok( Self::ShuffleOnlineDeck{ player: Player } ),
43 => Ok( Self::ShuffleOnlineDeck{ player: Opponent } ),
42 => Effect::ShuffleOnlineDeck{ player: Player },
43 => Effect::ShuffleOnlineDeck{ player: Opponent },
44 => Ok( Self::VoidOpponentSupportEffect ),
45 => Ok( Self::VoidOpponentSupportOptionEffect ),
44 => Effect::VoidOpponentSupportEffect,
45 => Effect::VoidOpponentSupportOptionEffect,
46 => Ok( Self::PickPartnerCard ),
46 => Effect::PickPartnerCard,
47 => Ok( Self::CycleOpponentAttackType ),
47 => Effect::CycleOpponentAttackType,
48 => Ok( Self::KoDigimonRevives{ health: y } ),
48 => Effect::KoDigimonRevives{ health: y },
49 => Ok( Self::DrawCards{ player: Player , count: y } ),
50 => Ok( Self::DrawCards{ player: Opponent, count: y } ),
49 => Effect::DrawCards{ player: Player , count: y },
50 => Effect::DrawCards{ player: Opponent, count: y },
51 => Ok( Self::OwnAttackBecomesEatUpHP ),
51 => Effect::OwnAttackBecomesEatUpHP,
52 => Ok( Self::AttackFirst{ player: Player } ),
53 => Ok( Self::AttackFirst{ player: Opponent } ),
52 => Effect::AttackFirst{ player: Player },
53 => Effect::AttackFirst{ player: Opponent },
_ => Err( FromBytesError::EffectType{ byte: effect_type_byte } ),
}
&byte => return Err( FromBytesError::EffectType { byte } ),
};
// And return the effect
Ok( Some(effect) )
}
type ToError = !;
type ToError = ToBytesError;
#[allow(clippy::too_many_lines)] // It's a single match, we can't really split it
fn to_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::ToError>
{
// Utility uses
use PlayerType::{Player, Opponent};
use Slot::{Hand, Online as OnlineDeck, Offline as OfflineDeck, Dp as DpSlot};
// Get all byte arrays we need
let bytes = util::array_split_mut!(bytes,
exists : 0x1,
@@ -307,46 +348,125 @@ impl Bytes for Effect
op : 0x1,
);
// Set that the effect exists
// Try to get the effect, if it doesn't exist, zero the exists byte and return
let effect = match self {
Some(effect) => effect,
None => {
*bytes.exists = 0;
return Ok(());
}
};
// Else set that the effect exists
*bytes.exists = 1;
// Setters
let bytes_a = bytes.a;
let bytes_b = bytes.b;
let bytes_c = bytes.c;
let mut set_a = |a: &Option<DigimonProperty>| if let Some(a) = a {
a.to_bytes(bytes_a).into_ok();
} else {
*bytes_a = 0;
};
let mut set_b = |b: &Option<DigimonProperty>| if let Some(b) = b {
b.to_bytes(bytes_b).into_ok();
} else {
*bytes_b = 0;
};
let mut set_c = |c: &Option<DigimonProperty>| if let Some(c) = c {
c.to_bytes(bytes_c).into_ok();
} else {
*bytes_c = 0;
};
let bytes_attack_type = &mut bytes.x[0];
let mut set_attack_type = |attack: &AttackType| attack.to_bytes( bytes_attack_type ).into_ok();
// 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 } => {
match effect {
Effect::ChangeProperty { property, a, b, c, x, y, op } => {
// Write the property minus one
property.to_bytes(bytes.effect_type).into_ok();
*bytes.effect_type -= 1;
if let Some(a) = a { a.to_bytes(bytes.a).into_ok(); }
if let Some(b) = b { b.to_bytes(bytes.b).into_ok(); }
if let Some(c) = c { c.to_bytes(bytes.c).into_ok(); }
// Write all arguments
set_a(a);
set_b(b);
set_c(c);
LittleEndian::write_u16(bytes.x, *x);
LittleEndian::write_u16(bytes.y, *y);
op.to_bytes(bytes.op).into_ok();
},
Self::UseAttack { player: _, attack: _ } => todo!(),
Effect::UseAttack { player, attack } => {
*bytes.effect_type = match player {
Player => 16,
Opponent => 17,
};
set_attack_type(attack);
},
Self::SetTempSlot { a: _, b: _, c: _, op: _ } => todo!(),
Effect::SetTempSlot { a, b, c, op } => {
*bytes.effect_type = 25;
set_a(a);
set_b(b);
set_c(c);
op.to_bytes(bytes.op).into_ok();
}
Self::MoveCards { player: _, source: _, destination: _, count: _ } => todo!(),
Effect::MoveCards { player, source, destination, count } => {
*bytes.effect_type = match (player, source, destination) {
(Player , Hand, OfflineDeck) => 26,
(Opponent, Hand, OfflineDeck) => 27,
(Player , Hand, OnlineDeck) => 30,
(Opponent, Hand, OnlineDeck) => 31,
(Player , OnlineDeck, OfflineDeck) => 32,
(Opponent, OnlineDeck, OfflineDeck) => 33,
(Player , OfflineDeck, OnlineDeck) => 34,
(Opponent, OfflineDeck, OnlineDeck) => 35,
(Player , DpSlot, OfflineDeck) => 36,
(Opponent, DpSlot, OfflineDeck) => 37,
(_, &source, &destination) => return Err(ToBytesError::InvalidMoveCards { source, destination }),
};
LittleEndian::write_u16(bytes.y, *count);
}
Self::ShuffleOnlineDeck { player: _ } => todo!(),
Effect::ShuffleOnlineDeck { player } => *bytes.effect_type = match player {
Player => 42,
Opponent => 43,
},
Self::VoidOpponentSupportEffect => todo!(),
Effect::VoidOpponentSupportEffect => *bytes.effect_type = 42,
Effect::VoidOpponentSupportOptionEffect => *bytes.effect_type = 43,
Self::VoidOpponentSupportOptionEffect => todo!(),
Effect::PickPartnerCard => *bytes.effect_type = 46,
Self::PickPartnerCard => todo!(),
Effect::CycleOpponentAttackType => *bytes.effect_type = 47,
Self::CycleOpponentAttackType => todo!(),
Effect::KoDigimonRevives { health } => {
LittleEndian::write_u16(bytes.y, *health);
},
Self::KoDigimonRevives { health: _ } => todo!(),
Effect::DrawCards { player, count } => {
*bytes.effect_type = match player {
Player => 49,
Opponent => 50,
};
LittleEndian::write_u16(bytes.y, *count);
}
Self::DrawCards { player: _, count: _ } => todo!(),
Effect::OwnAttackBecomesEatUpHP => *bytes.effect_type = 51,
Self::OwnAttackBecomesEatUpHP => todo!(),
Self::AttackFirst { player: _ } => todo!(),
Effect::AttackFirst { player } => *bytes.effect_type = match player {
Player => 52,
Opponent => 53,
},
}
// And return Ok