From 596bf177d67109ced7e2a8b07ecbe1f87bea2ca8 Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Thu, 30 Apr 2020 05:59:33 +0100 Subject: [PATCH] Finished `Effect` `to_bytes` implementation. --- src/game/card/digimon.rs | 34 ++-- src/game/card/item.rs | 23 ++- src/game/card/property/effect.rs | 262 ++++++++++++++++++++++--------- 3 files changed, 220 insertions(+), 99 deletions(-) diff --git a/src/game/card/digimon.rs b/src/game/card/digimon.rs index 8c3415f..3b631f0 100644 --- a/src/game/card/digimon.rs +++ b/src/game/card/digimon.rs @@ -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::::from_bytes( bytes.effect_first ) .map_err(FromBytesError::EffectFirst)?, - - (bytes.effect_second[0x0] != 0) - .then(|| Effect::from_bytes( bytes.effect_second ) ) - .transpose() + + Option::::from_bytes( bytes.effect_second ) .map_err(FromBytesError::EffectSecond)?, - - (bytes.effect_third[0x0] != 0) - .then(|| Effect::from_bytes( bytes.effect_third ) ) - .transpose() + + Option::::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 )? }; diff --git a/src/game/card/item.rs b/src/game/card/item.rs index b083f79..c0ff5be 100644 --- a/src/game/card/item.rs +++ b/src/game/card/item.rs @@ -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::::from_bytes( array_ref!(bytes, 0x59, 0x10) ) + .map_err(|err| FromBytesError::SupportEffect{ rank: "1st", err })?, + second: Option::::from_bytes( array_ref!(bytes, 0x69, 0x10) ) + .map_err(|err| FromBytesError::SupportEffect{ rank: "2nd", err })?, + third: Option::::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 diff --git a/src/game/card/property/effect.rs b/src/game/card/property/effect.rs index 4fa9368..d512998 100644 --- a/src/game/card/property/effect.rs +++ b/src/game/card/property/effect.rs @@ -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 { 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| if let Some(a) = a { + a.to_bytes(bytes_a).into_ok(); + } else { + *bytes_a = 0; + }; + let mut set_b = |b: &Option| if let Some(b) = b { + b.to_bytes(bytes_b).into_ok(); + } else { + *bytes_b = 0; + }; + let mut set_c = |c: &Option| 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