    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88

    use memflow::{types::Address, mem::MemoryView};
    use num_traits::FromPrimitive;

    use crate::{CheatCtx, Error, cs2dumper, structs::Vec3, traits::{MemoryClass, BaseEntity}, enums::{TeamID, PlayerType}};

    pub struct CPlayerController(Address);

    impl MemoryClass for CPlayerController {
        fn ptr(&self) -> Address {
            self.0
        }

        fn new(ptr: Address) -> Self {
            Self(ptr)
        }
    }

    impl BaseEntity for CPlayerController {
        fn from_index(ctx: &mut CheatCtx, entity_list: Address, index: i32) -> Result<Option<CPlayerController>, Error> {
            let list_entry = ctx.process.read_addr64(entity_list + 8 * (index >> 9) + 16)?;
            if list_entry.is_null() && !list_entry.is_valid() {
                return Ok(None);
            }

            let player_ptr = ctx.process.read_addr64(list_entry + 120 * (index & 0x1FF))?;
            if player_ptr.is_null() && !player_ptr.is_valid() {
                return Ok(None);
            }

            Ok(Some(Self::new(player_ptr)))
        }

        fn pos(&self, ctx: &mut CheatCtx) -> Result<Vec3, Error> {
            let node = ctx.process.read_addr64(self.0 + cs2dumper::client::C_BaseEntity::m_pGameSceneNode)?;
            Ok(ctx.process.read(node + cs2dumper::client::CGameSceneNode::m_vecAbsOrigin)?)
        }

        fn class_name(&self, ctx: &mut CheatCtx) -> Result<String, Error> {
            let entity_identity_ptr = ctx.process.read_addr64(self.0 + cs2dumper::client::CEntityInstance::m_pEntity)?;
            let class_name_ptr = ctx.process.read_addr64(entity_identity_ptr + cs2dumper::client::CEntityIdentity::m_designerName)?;
            Ok(ctx.process.read_char_string_n(class_name_ptr, 32)?)
        }
    }

    impl CPlayerController {
        pub fn get_pawn(&self, ctx: &mut CheatCtx, entity_list: Address) -> Result<Option<super::CPlayerPawn>, Error> {
            let uhandle = ctx.process.read(self.0 + cs2dumper::client::CCSPlayerController::m_hPlayerPawn)?;
            super::CPlayerPawn::from_uhandle(ctx, entity_list, uhandle)
        }

        // Technically something that should be in the BaseEntity trait, but we are only gonna use it on CPlayerController
        pub fn get_team(&self, ctx: &mut CheatCtx) -> Result<Option<TeamID>, Error> {
            let team_num: i32 = ctx.process.read(self.0 + cs2dumper::client::C_BaseEntity::m_iTeamNum)?;
            Ok(TeamID::from_i32(team_num))
        }

        pub fn get_player_type(&self, ctx: &mut CheatCtx, local: &CPlayerController) -> Result<Option<PlayerType>, Error> {
            if self.0 == local.0 {
                return Ok(Some(PlayerType::Local))
            }

            let team = {
                match self.get_team(ctx)? {
                    Some(t) => t,
                    None => { return Ok(None) },
                }
            };

            let local_team = {
                match local.get_team(ctx)? {
                    Some(t) => t,
                    None => { return Ok(None) },
                }
            };

            let player_type = {
                if team == TeamID::Spectator {
                    PlayerType::Spectator
                } else if team != local_team {
                    PlayerType::Enemy
                } else {
                    PlayerType::Team
                }
            };

            Ok(Some(player_type))
        }
    }