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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use std::error::Error;

use spring_ai_sys::COMMAND_TO_ID_ENGINE;

use crate::{
    ai_interface::{
        callback::{
            command::{
                command_data::{
                    cheats::{
                        GiveMeNewUnitCheatCommandData, GiveMeResourceCheatCommandData,
                        SetMyIncomeMultiplierCheatCommandData,
                    },
                    CommandData,
                },
                command_topic::CommandTopic,
            },
            engine::handle_command,
            resource::Resource,
            unit::Unit,
            unit_def::UnitDef,
        },
        AIInterface,
    },
    get_callback,
};

#[derive(Debug, Copy, Clone)]
pub struct Cheat {
    ai_id: i32,
}

#[derive(Debug, Copy, Clone)]
pub struct CheatAll {
    status: bool,
    will_desync: bool,
}

impl AIInterface {
    pub fn cheat(&self) -> Cheat {
        Cheat { ai_id: self.ai_id }
    }
}

impl Cheat {
    pub fn enable(&self) -> Result<(), Box<dyn Error>> {
        let set_cheat = get_callback!(self.ai_id, Cheats_setEnabled)?;
        unsafe { set_cheat(self.ai_id, true) };
        Ok(())
    }

    pub fn enable_event(&self) -> Result<(), Box<dyn Error>> {
        let set_cheat_event = get_callback!(self.ai_id, Cheats_setEventsEnabled)?;
        unsafe { set_cheat_event(self.ai_id, true) };
        Ok(())
    }

    pub fn disable(&self) -> Result<(), Box<dyn Error>> {
        let set_cheat = get_callback!(self.ai_id, Cheats_setEnabled)?;
        unsafe { set_cheat(self.ai_id, false) };
        Ok(())
    }

    pub fn disable_event(&self) -> Result<(), Box<dyn Error>> {
        let set_cheat_event = get_callback!(self.ai_id, Cheats_setEventsEnabled)?;
        unsafe { set_cheat_event(self.ai_id, false) };
        Ok(())
    }

    pub fn status(&self) -> Result<bool, Box<dyn Error>> {
        let get_cheat = get_callback!(self.ai_id, Cheats_isEnabled)?;
        Ok(unsafe { get_cheat(self.ai_id) })
    }

    pub fn will_desync(&self) -> Result<bool, Box<dyn Error>> {
        let is_passive = get_callback!(self.ai_id, Cheats_isOnlyPassive)?;
        Ok(unsafe { is_passive(self.ai_id) })
    }

    pub fn give_unit(&self, unit_def: UnitDef, position: [f32; 3]) -> Result<Unit, String> {
        let mut command_c_data = GiveMeNewUnitCheatCommandData {
            unit_def_id: unit_def.def_id,
            position,
        }
        .c_data();

        handle_command(
            self.ai_id,
            COMMAND_TO_ID_ENGINE,
            -1,
            CommandTopic::CheatsGiveMeNewUnit.into(),
            &mut command_c_data,
        )?;

        Ok(Unit {
            ai_id: self.ai_id,
            unit_id: command_c_data.ret_newUnitId,
        })
    }

    pub fn give_resource(&self, resource: Resource, amount: f32) -> Result<(), String> {
        let mut command_data = GiveMeResourceCheatCommandData {
            resource_id: resource.resource_id,
            amount,
        };

        handle_command(
            self.ai_id,
            COMMAND_TO_ID_ENGINE,
            -1,
            CommandTopic::CallLuaRules.into(),
            &mut command_data.c_data(),
        )?;

        Ok(())
    }

    pub fn set_income_multiplier(&self, factor: f32) -> Result<(), String> {
        let mut command_data = SetMyIncomeMultiplierCheatCommandData { factor };

        handle_command(
            self.ai_id,
            COMMAND_TO_ID_ENGINE,
            -1,
            CommandTopic::CallLuaRules.into(),
            &mut command_data.c_data(),
        )?;

        Ok(())
    }

    pub fn all(&self) -> Result<CheatAll, Box<dyn Error>> {
        Ok(CheatAll {
            status: self.status()?,
            will_desync: self.will_desync()?,
        })
    }
}