PkmnLib_rs/src/dynamic_data/choices.rs

473 lines
16 KiB
Rust
Executable File

use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use parking_lot::RwLock;
use crate::dynamic_data::Pokemon;
use crate::dynamic_data::ScriptContainer;
use crate::dynamic_data::{LearnedMove, ScriptWrapper};
use crate::dynamic_data::{ScriptSource, ScriptSourceData};
/// The data on a turn choice that should be contained in every turn choice, regardless of type.
#[derive(Debug)]
struct CommonChoiceData {
/// The user of the turn choice
user: Arc<Pokemon>,
/// The speed of the user at the beginning of the turn.
speed: u32,
/// This random value is set at the beginning of the turn. It is used for tie breaking of the
/// turn order in a predictable way, regardless of implementation and hardware.
random_value: u32,
/// Whether or not the choice has failed. A failed choice will stop running, and execute special
/// fail handling during turn execution.
has_failed: AtomicBool,
/// The data we can use to retrieve scripts that are affecting this choice. This will be written
/// to once: when we need to initialize it. After that, this is only used to read. To prevent a
/// read while we're writing to it, this is a RwLock.
script_source_data: RwLock<ScriptSourceData>,
}
/// This enum defines a single choice for a Pokemon for a battle turn.
#[derive(Debug)]
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
pub enum TurnChoice {
/// A move choice tells a Pokemon to use a move on a target for this turn.
Move(MoveChoice),
/// An item choice tells a Pokemon to use an item.
Item(ItemChoice),
/// A switch choice tells a Pokemon to switch with another Pokemon from the party.
Switch(SwitchChoice),
/// A flee choice tells a Pokemon to flee from battle.
Flee(FleeChoice),
/// A pass choice tells the user to do nothing that turn.
Pass(PassChoice),
}
impl TurnChoice {
/// The shared choice data between each of the different turn choices.
fn choice_data(&self) -> &CommonChoiceData {
match self {
TurnChoice::Move(data) => &data.choice_data,
TurnChoice::Item(data) => &data.choice_data,
TurnChoice::Switch(data) => &data.choice_data,
TurnChoice::Flee(data) => &data.choice_data,
TurnChoice::Pass(data) => &data.choice_data,
}
}
/// The shared choice data between each of the different turn choices.
fn choice_data_mut(&mut self) -> &mut Box<CommonChoiceData> {
match self {
TurnChoice::Move(data) => &mut data.choice_data,
TurnChoice::Item(data) => &mut data.choice_data,
TurnChoice::Switch(data) => &mut data.choice_data,
TurnChoice::Flee(data) => &mut data.choice_data,
TurnChoice::Pass(data) => &mut data.choice_data,
}
}
/// Get the user of the given choice.
pub fn user(&self) -> &Arc<Pokemon> {
&self.choice_data().user
}
/// Get the speed of the user for the choice. Note that this speed is the speed of the Pokemon
/// at the start of the turn!
pub fn speed(&self) -> u32 {
self.choice_data().speed
}
/// Get the mutable speed of the user for the choice. Note that this speed is the speed of the Pokemon
/// at the start of the turn!
pub fn speed_mut(&mut self) -> &mut u32 {
&mut self.choice_data_mut().speed
}
/// Gets whether or not the choice has failed. If we notice this when we execute the choice, we
/// will not execute it.
pub fn has_failed(&self) -> bool {
self.choice_data().has_failed.load(Ordering::SeqCst)
}
/// Fails the choice. This will prevent it from executing and run a specific fail handling during
/// execution. Note that this can not be undone.
pub fn fail(&self) {
self.choice_data().has_failed.store(true, Ordering::SeqCst)
}
/// The random value of a turn choice gets set during the start of a choice, and is used for tie
/// breaking of turn executions. This means that choices get executed with a predictable order,
/// regardless of implementation details.
pub(crate) fn random_value(&self) -> u32 {
self.choice_data().random_value
}
/// This sets the above random value.
pub(crate) fn set_random_value(&mut self, val: u32) {
self.choice_data_mut().random_value = val;
}
/// Helper function to get the move choice data from a turn. Note that this will panic if not
/// used on a move choice.
pub(crate) fn get_move_turn_data(&self) -> &MoveChoice {
if let TurnChoice::Move(data) = self {
return data;
}
panic!("Invalid turn choice");
}
}
impl ScriptSource for TurnChoice {
fn get_script_count(&self) -> usize {
match self {
TurnChoice::Move(data) => data.get_script_count(),
TurnChoice::Item(data) => data.get_script_count(),
TurnChoice::Switch(data) => data.get_script_count(),
TurnChoice::Flee(data) => data.get_script_count(),
TurnChoice::Pass(data) => data.get_script_count(),
}
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
match self {
TurnChoice::Move(data) => data.get_script_source_data(),
TurnChoice::Item(data) => data.get_script_source_data(),
TurnChoice::Switch(data) => data.get_script_source_data(),
TurnChoice::Flee(data) => data.get_script_source_data(),
TurnChoice::Pass(data) => data.get_script_source_data(),
}
}
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
match self {
TurnChoice::Move(data) => data.get_own_scripts(scripts),
TurnChoice::Item(data) => data.get_own_scripts(scripts),
TurnChoice::Switch(data) => data.get_own_scripts(scripts),
TurnChoice::Flee(data) => data.get_own_scripts(scripts),
TurnChoice::Pass(data) => data.get_own_scripts(scripts),
}
}
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
match self {
TurnChoice::Move(data) => data.collect_scripts(scripts),
TurnChoice::Item(data) => data.collect_scripts(scripts),
TurnChoice::Switch(data) => data.collect_scripts(scripts),
TurnChoice::Flee(data) => data.collect_scripts(scripts),
TurnChoice::Pass(data) => data.collect_scripts(scripts),
}
}
}
/// The data attached to a move choice.
#[derive(Debug)]
pub struct MoveChoice {
/// The move that is used for this choice.
used_move: Arc<LearnedMove>,
/// The side this move is aimed at.
target_side: u8,
/// The index of the Pokemon on the side we're aiming at.
target_index: u8,
/// The move script.
script: ScriptContainer,
/// The priority of the move choice at the beginning of the turn.
priority: i8,
/// The common turn choice data.
choice_data: Box<CommonChoiceData>,
}
impl MoveChoice {
/// Initializes the data for a new move choice.
pub fn new(user: Arc<Pokemon>, used_move: Arc<LearnedMove>, target_side: u8, target_index: u8) -> Self {
Self {
used_move,
target_side,
target_index,
script: Default::default(),
priority: 0,
choice_data: Box::new(CommonChoiceData {
user,
speed: 0,
random_value: 0,
has_failed: Default::default(),
script_source_data: Default::default(),
}),
}
}
/// The actual learned move on the Pokemon we use for this choice.
pub fn used_move(&self) -> &Arc<LearnedMove> {
&self.used_move
}
/// The target side the move is aimed at.
pub fn target_side(&self) -> u8 {
self.target_side
}
/// The Pokemon index on the side we're aiming at.
pub fn target_index(&self) -> u8 {
self.target_index
}
/// The priority of the move choice at the beginning of the turn.
pub fn priority(&self) -> i8 {
self.priority
}
/// The priority of the move choice at the beginning of the turn.
pub fn priority_mut(&mut self) -> &mut i8 {
&mut self.priority
}
/// The user of the choice.
pub fn user(&self) -> &Arc<Pokemon> {
&self.choice_data.user
}
/// The move script of the choice.
pub fn script(&self) -> &ScriptContainer {
&self.script
}
}
impl ScriptSource for MoveChoice {
fn get_script_count(&self) -> usize {
1 + self.choice_data.user.get_script_count()
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.choice_data.script_source_data
}
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
scripts.push((&self.script).into());
}
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
self.get_own_scripts(scripts);
self.choice_data.user.collect_scripts(scripts);
}
}
/// The data given when we select an item choice.
#[derive(Debug)]
pub struct ItemChoice {
/// The shared data of all turn choices.
choice_data: Box<CommonChoiceData>,
}
impl ItemChoice {
/// Initialised a new item choice.
pub fn new(user: Arc<Pokemon>) -> Self {
Self {
choice_data: Box::new(CommonChoiceData {
user,
speed: 0,
random_value: 0,
has_failed: Default::default(),
script_source_data: Default::default(),
}),
}
}
}
impl ScriptSource for ItemChoice {
fn get_script_count(&self) -> usize {
0
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.choice_data.script_source_data
}
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
self.choice_data.user.collect_scripts(scripts);
}
}
/// The data given when we select a switch choice.
#[derive(Debug)]
pub struct SwitchChoice {
/// The shared data of all turn choices.
choice_data: Box<CommonChoiceData>,
}
impl SwitchChoice {
/// Initialise the turn choice data.
pub fn new(user: Arc<Pokemon>) -> Self {
Self {
choice_data: Box::new(CommonChoiceData {
user,
speed: 0,
random_value: 0,
has_failed: Default::default(),
script_source_data: Default::default(),
}),
}
}
}
impl ScriptSource for SwitchChoice {
fn get_script_count(&self) -> usize {
0
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.choice_data.script_source_data
}
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
self.choice_data.user.collect_scripts(scripts);
}
}
/// The data given when we select a flee choice.
#[derive(Debug)]
pub struct FleeChoice {
/// The common data all turn choices share.
choice_data: Box<CommonChoiceData>,
}
impl FleeChoice {
/// Initialises a new flee choice.
pub fn new(user: Arc<Pokemon>) -> Self {
Self {
choice_data: Box::new(CommonChoiceData {
user,
speed: 0,
random_value: 0,
has_failed: Default::default(),
script_source_data: Default::default(),
}),
}
}
}
impl ScriptSource for FleeChoice {
fn get_script_count(&self) -> usize {
0
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.choice_data.script_source_data
}
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
self.choice_data.user.collect_scripts(scripts);
}
}
/// The data given when we select a pass choice.
#[derive(Debug)]
pub struct PassChoice {
/// The common data of all turn choices.
choice_data: Box<CommonChoiceData>,
}
impl PassChoice {
/// Initialised a new pass choice.
pub fn new(user: Arc<Pokemon>) -> Self {
Self {
choice_data: Box::new(CommonChoiceData {
user,
speed: 0,
random_value: 0,
has_failed: Default::default(),
script_source_data: Default::default(),
}),
}
}
}
impl ScriptSource for PassChoice {
fn get_script_count(&self) -> usize {
0
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.choice_data.script_source_data
}
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
self.choice_data.user.collect_scripts(scripts);
}
}
impl PartialEq<Self> for TurnChoice {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self, other)
}
}
impl Eq for TurnChoice {}
impl PartialOrd for TurnChoice {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TurnChoice {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match self {
TurnChoice::Move(data) => {
if let TurnChoice::Move(other_data) = other {
let priority_compare = data.priority.cmp(&other_data.priority);
if priority_compare != std::cmp::Ordering::Equal {
return priority_compare;
}
let speed_compare = self.speed().cmp(&other.speed());
if speed_compare != std::cmp::Ordering::Equal {
return speed_compare;
}
return self.random_value().cmp(&other.random_value());
}
std::cmp::Ordering::Greater
}
TurnChoice::Item { .. } => {
if let TurnChoice::Move { .. } = other {
return std::cmp::Ordering::Less;
}
if let TurnChoice::Item { .. } = other {
let speed_compare = self.speed().cmp(&other.speed());
if speed_compare != std::cmp::Ordering::Equal {
return speed_compare;
}
return self.random_value().cmp(&other.random_value());
}
std::cmp::Ordering::Greater
}
TurnChoice::Switch { .. } => {
if let TurnChoice::Move { .. } = other {
return std::cmp::Ordering::Less;
}
if let TurnChoice::Item { .. } = other {
return std::cmp::Ordering::Less;
}
if let TurnChoice::Switch { .. } = other {
let speed_compare = self.speed().cmp(&other.speed());
if speed_compare != std::cmp::Ordering::Equal {
return speed_compare;
}
return self.random_value().cmp(&other.random_value());
}
std::cmp::Ordering::Greater
}
TurnChoice::Flee { .. } => {
if let TurnChoice::Flee { .. } = other {
let speed_compare = self.speed().cmp(&other.speed());
if speed_compare != std::cmp::Ordering::Equal {
return speed_compare;
}
return self.random_value().cmp(&other.random_value());
}
std::cmp::Ordering::Less
}
TurnChoice::Pass(..) => std::cmp::Ordering::Less,
}
}
}