trainerbase 2.17.0__tar.gz → 3.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {trainerbase-2.17.0 → trainerbase-3.1.0}/PKG-INFO +1 -1
- {trainerbase-2.17.0 → trainerbase-3.1.0}/pyproject.toml +1 -1
- {trainerbase-2.17.0 → trainerbase-3.1.0}/trainerbase/codeinjection.py +16 -8
- {trainerbase-2.17.0 → trainerbase-3.1.0}/trainerbase/common.py +32 -23
- {trainerbase-2.17.0 → trainerbase-3.1.0}/trainerbase/gameobject.py +31 -17
- trainerbase-3.1.0/trainerbase/gui.py +370 -0
- trainerbase-3.1.0/trainerbase/main.py +37 -0
- {trainerbase-2.17.0 → trainerbase-3.1.0}/trainerbase/scriptengine.py +1 -37
- {trainerbase-2.17.0 → trainerbase-3.1.0}/trainerbase/speedhack.py +15 -30
- trainerbase-2.17.0/trainerbase/gui.py +0 -277
- {trainerbase-2.17.0 → trainerbase-3.1.0}/LICENSE +0 -0
- {trainerbase-2.17.0 → trainerbase-3.1.0}/README.rst +0 -0
- {trainerbase-2.17.0 → trainerbase-3.1.0}/trainerbase/__init__.py +0 -0
- {trainerbase-2.17.0 → trainerbase-3.1.0}/trainerbase/config.py +0 -0
- {trainerbase-2.17.0 → trainerbase-3.1.0}/trainerbase/memory.py +0 -0
- {trainerbase-2.17.0 → trainerbase-3.1.0}/trainerbase/tts.py +0 -0
- {trainerbase-2.17.0 → trainerbase-3.1.0}/vendor/FASM.EXE +0 -0
- {trainerbase-2.17.0 → trainerbase-3.1.0}/vendor/speedhack32.dll +0 -0
- {trainerbase-2.17.0 → trainerbase-3.1.0}/vendor/speedhack64.dll +0 -0
- {trainerbase-2.17.0 → trainerbase-3.1.0}/vendor/speedhack_config.json +0 -0
|
@@ -4,11 +4,10 @@ from subprocess import DEVNULL
|
|
|
4
4
|
from subprocess import run as run_executable
|
|
5
5
|
from tempfile import TemporaryDirectory
|
|
6
6
|
from typing import Self, override
|
|
7
|
-
from uuid import uuid4
|
|
8
7
|
|
|
9
8
|
from pymem.exception import MemoryWriteError
|
|
10
9
|
|
|
11
|
-
from trainerbase.memory import ARCH,
|
|
10
|
+
from trainerbase.memory import ARCH, ConvertibleToAddress, ensure_address, pm
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
FASM_EXECUTABLE_PATH = Path(__file__).resolve().parent.parent / "vendor" / "FASM.EXE"
|
|
@@ -18,11 +17,7 @@ type Code = bytes | str
|
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
class AbstractCodeInjection(ABC):
|
|
21
|
-
DPG_TAG_PREFIX = "injection__"
|
|
22
|
-
|
|
23
20
|
created_code_injections: list[Self] = []
|
|
24
|
-
address: AbstractAddress
|
|
25
|
-
dpg_tag: str
|
|
26
21
|
|
|
27
22
|
def __new__(cls, *_args, **_kwargs):
|
|
28
23
|
instance = super().__new__(cls)
|
|
@@ -47,7 +42,6 @@ class CodeInjection(AbstractCodeInjection):
|
|
|
47
42
|
):
|
|
48
43
|
self.address = ensure_address(address)
|
|
49
44
|
self.code_to_inject = compile_asm(code_to_inject)
|
|
50
|
-
self.dpg_tag = f"{self.DPG_TAG_PREFIX}{uuid4()}"
|
|
51
45
|
self.original_code: bytes = b""
|
|
52
46
|
self._injected = False
|
|
53
47
|
|
|
@@ -75,7 +69,6 @@ class AllocatingCodeInjection(AbstractCodeInjection): # pylint: disable=too-man
|
|
|
75
69
|
):
|
|
76
70
|
self.address = ensure_address(address)
|
|
77
71
|
self.code_to_inject = compile_asm(code_to_inject)
|
|
78
|
-
self.dpg_tag = f"{self.DPG_TAG_PREFIX}{uuid4()}"
|
|
79
72
|
self.original_code: bytes = b""
|
|
80
73
|
|
|
81
74
|
self.original_code_length = original_code_length
|
|
@@ -121,6 +114,21 @@ class AllocatingCodeInjection(AbstractCodeInjection): # pylint: disable=too-man
|
|
|
121
114
|
self.new_memory_address = 0
|
|
122
115
|
|
|
123
116
|
|
|
117
|
+
class MultipleCodeInjection(AbstractCodeInjection):
|
|
118
|
+
def __init__(self, *injections: AbstractCodeInjection):
|
|
119
|
+
self._injections = injections
|
|
120
|
+
|
|
121
|
+
@override
|
|
122
|
+
def inject(self):
|
|
123
|
+
for injection in self._injections:
|
|
124
|
+
injection.inject()
|
|
125
|
+
|
|
126
|
+
@override
|
|
127
|
+
def eject(self):
|
|
128
|
+
for injection in self._injections:
|
|
129
|
+
injection.eject()
|
|
130
|
+
|
|
131
|
+
|
|
124
132
|
def compile_asm(code: Code) -> bytes:
|
|
125
133
|
if isinstance(code, str):
|
|
126
134
|
fasm_mode = f"use{ARCH}"
|
|
@@ -8,8 +8,8 @@ from typing import Self
|
|
|
8
8
|
from keyboard import on_press_key, on_release_key
|
|
9
9
|
from pymem.exception import MemoryReadError
|
|
10
10
|
|
|
11
|
-
from trainerbase.gameobject import GameObject
|
|
12
|
-
from trainerbase.scriptengine import Script
|
|
11
|
+
from trainerbase.gameobject import AbstractReadableObject, GameObject
|
|
12
|
+
from trainerbase.scriptengine import Script, system_script_engine
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
type Coords[T: (int, float)] = tuple[T, T, T]
|
|
@@ -75,7 +75,8 @@ class Teleport:
|
|
|
75
75
|
self.current_position: Coords | None = None
|
|
76
76
|
self.movement_vector = Vector3(0, 0, 0)
|
|
77
77
|
self.minimal_movement_vector_length = minimal_movement_vector_length
|
|
78
|
-
|
|
78
|
+
|
|
79
|
+
system_script_engine.register_script(Script(self.update_movement_vector, enabled=True))
|
|
79
80
|
|
|
80
81
|
def set_coords(self, x: Number, y: Number, z: Number = 100) -> None:
|
|
81
82
|
self.player_x.value = x
|
|
@@ -126,30 +127,35 @@ class Teleport:
|
|
|
126
127
|
new_coords = Vector3(*self.get_coords()) + dash_movement_vector
|
|
127
128
|
self.set_coords(*new_coords)
|
|
128
129
|
|
|
129
|
-
def create_movement_vector_updater_script(self) -> Script:
|
|
130
|
-
if self.movement_vector_updater_script is None:
|
|
131
|
-
self.movement_vector_updater_script = Script(self.update_movement_vector, enabled=True)
|
|
132
130
|
|
|
133
|
-
|
|
131
|
+
class Switchable(ABC):
|
|
132
|
+
@abstractmethod
|
|
133
|
+
def enable(self):
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
@abstractmethod
|
|
137
|
+
def disable(self):
|
|
138
|
+
pass
|
|
134
139
|
|
|
135
140
|
|
|
136
|
-
class
|
|
137
|
-
def __init__(self, key: str, short_press_max_delta: float = 0.3
|
|
141
|
+
class ShortLongPressSwitch:
|
|
142
|
+
def __init__(self, switchable: Switchable, key: str, short_press_max_delta: float = 0.3):
|
|
138
143
|
self._key = key
|
|
139
144
|
self._press_timestamp = None
|
|
140
145
|
self._short_press_max_delta = short_press_max_delta
|
|
141
|
-
self.
|
|
146
|
+
self._switchable = switchable
|
|
147
|
+
self._enabled = False
|
|
142
148
|
|
|
143
149
|
def _on_press(self, _):
|
|
144
150
|
if self._press_timestamp is None:
|
|
145
151
|
self._press_timestamp = time()
|
|
146
|
-
self.on_press(
|
|
152
|
+
self.on_press()
|
|
147
153
|
|
|
148
154
|
def _on_release(self, _):
|
|
149
155
|
if self._is_short_delta():
|
|
150
|
-
self.on_short_press(
|
|
156
|
+
self.on_short_press()
|
|
151
157
|
else:
|
|
152
|
-
self.on_long_press(
|
|
158
|
+
self.on_long_press()
|
|
153
159
|
|
|
154
160
|
self._press_timestamp = None
|
|
155
161
|
|
|
@@ -166,22 +172,25 @@ class ShortLongSwitch(ABC):
|
|
|
166
172
|
on_release_key(self._key, self._on_release)
|
|
167
173
|
on_press_key(self._key, self._on_press)
|
|
168
174
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
pass
|
|
175
|
+
def on_press(self):
|
|
176
|
+
self._switchable.enable()
|
|
172
177
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
178
|
+
def on_short_press(self):
|
|
179
|
+
if self._enabled:
|
|
180
|
+
self._enabled = False
|
|
181
|
+
self._switchable.disable()
|
|
182
|
+
else:
|
|
183
|
+
self._enabled = True
|
|
184
|
+
self._switchable.enable()
|
|
176
185
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
186
|
+
def on_long_press(self):
|
|
187
|
+
self._enabled = False
|
|
188
|
+
self._switchable.disable()
|
|
180
189
|
|
|
181
190
|
|
|
182
191
|
def regenerate(
|
|
183
192
|
current_value: GameObject,
|
|
184
|
-
max_value:
|
|
193
|
+
max_value: AbstractReadableObject,
|
|
185
194
|
percent: Number,
|
|
186
195
|
min_value: Number = 1,
|
|
187
196
|
):
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from typing import Self, override
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
from pymem.exception import MemoryWriteError
|
|
4
5
|
|
|
5
6
|
from trainerbase.memory import ConvertibleToAddress, ensure_address, pm
|
|
6
7
|
|
|
7
8
|
|
|
8
|
-
class
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
class AbstractReadableObject[T](ABC):
|
|
10
|
+
@property
|
|
11
|
+
@abstractmethod
|
|
12
|
+
def value(self) -> T:
|
|
13
|
+
pass
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
|
|
16
|
+
class GameObject[PymemType, TrainerBaseType](AbstractReadableObject[TrainerBaseType]):
|
|
17
|
+
created_objects: list[Self] = []
|
|
15
18
|
|
|
16
19
|
@staticmethod
|
|
17
20
|
@abstractmethod
|
|
@@ -28,25 +31,17 @@ class GameObject[PymemType, TrainerBaseType](ABC):
|
|
|
28
31
|
address: ConvertibleToAddress,
|
|
29
32
|
frozen: TrainerBaseType | None = None,
|
|
30
33
|
):
|
|
31
|
-
GameObject.
|
|
34
|
+
GameObject.created_objects.append(self)
|
|
32
35
|
|
|
33
36
|
self.address = ensure_address(address)
|
|
34
37
|
self.frozen = frozen
|
|
35
38
|
|
|
36
|
-
dpg_tag = f"{GameObject.DPG_TAG_PREFIX}{uuid4()}"
|
|
37
|
-
self.dpg_tag_frozen = f"{dpg_tag}{GameObject.DPG_TAG_POSTFIX_IS_FROZEN}"
|
|
38
|
-
self.dpg_tag_getter = f"{dpg_tag}{GameObject.DPG_TAG_POSTFIX_GETTER}"
|
|
39
|
-
self.dpg_tag_setter = f"{dpg_tag}{GameObject.DPG_TAG_POSTFIX_SETTER}"
|
|
40
|
-
|
|
41
39
|
def __repr__(self):
|
|
42
40
|
return (
|
|
43
41
|
f"<{self.__class__.__name__}"
|
|
44
42
|
f" at {hex(self.address.resolve())}:"
|
|
45
43
|
f" value={self.value},"
|
|
46
|
-
f" frozen={self.frozen}
|
|
47
|
-
f" dpg_tag_frozen={self.dpg_tag_frozen},"
|
|
48
|
-
f" dpg_tag_getter={self.dpg_tag_getter}"
|
|
49
|
-
f" dpg_tag_setter={self.dpg_tag_setter}"
|
|
44
|
+
f" frozen={self.frozen}"
|
|
50
45
|
">"
|
|
51
46
|
)
|
|
52
47
|
|
|
@@ -57,6 +52,7 @@ class GameObject[PymemType, TrainerBaseType](ABC):
|
|
|
57
52
|
return value # type: ignore
|
|
58
53
|
|
|
59
54
|
@property
|
|
55
|
+
@override
|
|
60
56
|
def value(self) -> TrainerBaseType:
|
|
61
57
|
return self.after_read(self.pm_read(self.address.resolve()))
|
|
62
58
|
|
|
@@ -136,3 +132,21 @@ class GameUnsignedLongLong(GameObject[int, int]):
|
|
|
136
132
|
class GameBool(GameObject[bool, bool]):
|
|
137
133
|
pm_read = pm.read_bool # type: ignore
|
|
138
134
|
pm_write = pm.write_bool
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class ReadonlyGameObjectSumGetter(AbstractReadableObject[int | float]):
|
|
138
|
+
def __init__(self, *game_numbers: GameInt | GameFloat):
|
|
139
|
+
self.game_numbers = game_numbers
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def value(self) -> int | float:
|
|
143
|
+
return sum(number.value for number in self.game_numbers)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def update_frozen_objects():
|
|
147
|
+
for game_object in GameObject.created_objects:
|
|
148
|
+
if game_object.frozen is not None:
|
|
149
|
+
try:
|
|
150
|
+
game_object.value = game_object.frozen
|
|
151
|
+
except MemoryWriteError:
|
|
152
|
+
continue
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from functools import partial, wraps
|
|
4
|
+
from typing import Any, Self, override
|
|
5
|
+
from uuid import uuid4
|
|
6
|
+
|
|
7
|
+
from dearpygui import dearpygui as dpg
|
|
8
|
+
from keyboard import add_hotkey
|
|
9
|
+
from pymem.exception import MemoryReadError
|
|
10
|
+
|
|
11
|
+
from trainerbase.codeinjection import AbstractCodeInjection
|
|
12
|
+
from trainerbase.common import ShortLongPressSwitch, Switchable, Teleport
|
|
13
|
+
from trainerbase.gameobject import (
|
|
14
|
+
GameBool,
|
|
15
|
+
GameByte,
|
|
16
|
+
GameDouble,
|
|
17
|
+
GameFloat,
|
|
18
|
+
GameInt,
|
|
19
|
+
GameLongLong,
|
|
20
|
+
GameObject,
|
|
21
|
+
GameShort,
|
|
22
|
+
GameUnsignedInt,
|
|
23
|
+
GameUnsignedLongLong,
|
|
24
|
+
GameUnsignedShort,
|
|
25
|
+
)
|
|
26
|
+
from trainerbase.scriptengine import Script
|
|
27
|
+
from trainerbase.speedhack import SpeedHack
|
|
28
|
+
from trainerbase.tts import say
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
TAG_TELEPORT_LABELS = "__teleport_labels"
|
|
32
|
+
TAG_SPEEDHACK_FACTOR_INPUT = "tag_speedhack_factor_input"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AbstractUIComponent(ABC):
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def add_to_ui(self) -> None:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ScriptUI(AbstractUIComponent):
|
|
42
|
+
DPG_TAG_PREFIX = "script__"
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
script: Script,
|
|
47
|
+
label: str,
|
|
48
|
+
hotkey: str | None = None,
|
|
49
|
+
tts_on_hotkey: bool = True,
|
|
50
|
+
):
|
|
51
|
+
self.script = script
|
|
52
|
+
self.pure_label = label
|
|
53
|
+
self.label_with_hotkey = label if hotkey is None else f"[{hotkey}] {label}"
|
|
54
|
+
self.hotkey = hotkey
|
|
55
|
+
self.tts_on_hotkey = tts_on_hotkey
|
|
56
|
+
|
|
57
|
+
self.dpg_tag = f"{self.DPG_TAG_PREFIX}{uuid4()}"
|
|
58
|
+
|
|
59
|
+
@override
|
|
60
|
+
def add_to_ui(self) -> None:
|
|
61
|
+
if self.hotkey is not None:
|
|
62
|
+
add_hotkey(self.hotkey, self.on_hotkey_press)
|
|
63
|
+
|
|
64
|
+
dpg.add_checkbox(
|
|
65
|
+
label=self.label_with_hotkey,
|
|
66
|
+
tag=self.dpg_tag,
|
|
67
|
+
callback=self.on_script_state_change,
|
|
68
|
+
default_value=self.script.enabled,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def on_script_state_change(self):
|
|
72
|
+
self.script.enabled = dpg.get_value(self.dpg_tag)
|
|
73
|
+
|
|
74
|
+
def on_hotkey_press(self):
|
|
75
|
+
dpg.set_value(self.dpg_tag, not dpg.get_value(self.dpg_tag))
|
|
76
|
+
|
|
77
|
+
self.on_script_state_change()
|
|
78
|
+
|
|
79
|
+
if self.tts_on_hotkey:
|
|
80
|
+
status = "enabled" if self.script.enabled else "disabled"
|
|
81
|
+
say(f"Script {self.pure_label} {status}")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class CodeInjectionUI(AbstractUIComponent):
|
|
85
|
+
DPG_TAG_PREFIX = "injection__"
|
|
86
|
+
|
|
87
|
+
def __init__(
|
|
88
|
+
self,
|
|
89
|
+
injection: AbstractCodeInjection,
|
|
90
|
+
label: str,
|
|
91
|
+
hotkey: str | None = None,
|
|
92
|
+
tts_on_hotkey: bool = True,
|
|
93
|
+
):
|
|
94
|
+
self.injection = injection
|
|
95
|
+
self.pure_label = label
|
|
96
|
+
self.label_with_hotkey = label if hotkey is None else f"[{hotkey}] {label}"
|
|
97
|
+
self.hotkey = hotkey
|
|
98
|
+
self.tts_on_hotkey = tts_on_hotkey
|
|
99
|
+
|
|
100
|
+
self.dpg_tag = f"{self.DPG_TAG_PREFIX}{uuid4()}"
|
|
101
|
+
|
|
102
|
+
@override
|
|
103
|
+
def add_to_ui(self) -> None:
|
|
104
|
+
if self.hotkey is not None:
|
|
105
|
+
add_hotkey(self.hotkey, self.on_hotkey_press)
|
|
106
|
+
|
|
107
|
+
dpg.add_checkbox(label=self.label_with_hotkey, tag=self.dpg_tag, callback=self.on_codeinjection_state_change)
|
|
108
|
+
|
|
109
|
+
def on_codeinjection_state_change(self):
|
|
110
|
+
if dpg.get_value(self.dpg_tag):
|
|
111
|
+
self.injection.inject()
|
|
112
|
+
else:
|
|
113
|
+
self.injection.eject()
|
|
114
|
+
|
|
115
|
+
def on_hotkey_press(self):
|
|
116
|
+
dpg.set_value(self.dpg_tag, not dpg.get_value(self.dpg_tag))
|
|
117
|
+
self.on_codeinjection_state_change()
|
|
118
|
+
|
|
119
|
+
if self.tts_on_hotkey:
|
|
120
|
+
status = "applied" if dpg.get_value(self.dpg_tag) else "removed"
|
|
121
|
+
say(f"CodeInjection {self.pure_label} {status}")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class GameObjectUI(AbstractUIComponent):
|
|
125
|
+
DPG_TAG_PREFIX = "object__"
|
|
126
|
+
DPG_TAG_POSTFIX_IS_FROZEN = "__frozen"
|
|
127
|
+
DPG_TAG_POSTFIX_GETTER = "__getter"
|
|
128
|
+
DPG_TAG_POSTFIX_SETTER = "__setter"
|
|
129
|
+
|
|
130
|
+
displayed_objects: list[Self] = []
|
|
131
|
+
|
|
132
|
+
def __init__(
|
|
133
|
+
self,
|
|
134
|
+
gameobject: GameObject,
|
|
135
|
+
label: str,
|
|
136
|
+
hotkey: str | None = None,
|
|
137
|
+
default_setter_input_value: Any = 0,
|
|
138
|
+
before_set: Callable = int,
|
|
139
|
+
tts_on_hotkey: bool = True,
|
|
140
|
+
):
|
|
141
|
+
self.gameobject = gameobject
|
|
142
|
+
self.pure_label = label
|
|
143
|
+
self.label_with_hotkey = label if hotkey is None else f"[{hotkey}] {label}"
|
|
144
|
+
self.hotkey = hotkey
|
|
145
|
+
self.default_setter_input_value = default_setter_input_value
|
|
146
|
+
self.before_set = before_set
|
|
147
|
+
self.tts_on_hotkey = tts_on_hotkey
|
|
148
|
+
|
|
149
|
+
dpg_tag = f"{self.DPG_TAG_PREFIX}{uuid4()}"
|
|
150
|
+
self.dpg_tag_frozen = f"{dpg_tag}{self.DPG_TAG_POSTFIX_IS_FROZEN}"
|
|
151
|
+
self.dpg_tag_getter = f"{dpg_tag}{self.DPG_TAG_POSTFIX_GETTER}"
|
|
152
|
+
self.dpg_tag_setter = f"{dpg_tag}{self.DPG_TAG_POSTFIX_SETTER}"
|
|
153
|
+
|
|
154
|
+
@override
|
|
155
|
+
def add_to_ui(self) -> None:
|
|
156
|
+
if self.hotkey is not None:
|
|
157
|
+
add_hotkey(self.hotkey, self.on_hotkey_press)
|
|
158
|
+
|
|
159
|
+
match self.gameobject:
|
|
160
|
+
case GameFloat():
|
|
161
|
+
add_setter_input = dpg.add_input_float
|
|
162
|
+
case GameDouble():
|
|
163
|
+
add_setter_input = dpg.add_input_double
|
|
164
|
+
case GameByte() | GameShort() | GameInt() | GameLongLong() | GameBool():
|
|
165
|
+
add_setter_input = dpg.add_input_int
|
|
166
|
+
case GameUnsignedShort() | GameUnsignedInt() | GameUnsignedLongLong():
|
|
167
|
+
add_setter_input = partial(dpg.add_input_int, min_clamped=True, min_value=0)
|
|
168
|
+
case _:
|
|
169
|
+
add_setter_input = dpg.add_input_text
|
|
170
|
+
|
|
171
|
+
with dpg.group(horizontal=True):
|
|
172
|
+
dpg.add_checkbox(tag=self.dpg_tag_frozen, callback=self.on_frozen_state_change)
|
|
173
|
+
dpg.add_text(self.label_with_hotkey)
|
|
174
|
+
dpg.add_input_text(width=220, tag=self.dpg_tag_getter, readonly=True)
|
|
175
|
+
add_setter_input(width=220, tag=self.dpg_tag_setter, default_value=self.default_setter_input_value)
|
|
176
|
+
dpg.add_button(label="Set", callback=self.on_value_set)
|
|
177
|
+
|
|
178
|
+
GameObjectUI.displayed_objects.append(self)
|
|
179
|
+
|
|
180
|
+
def on_frozen_state_change(self):
|
|
181
|
+
self.gameobject.frozen = self.gameobject.value if dpg.get_value(self.dpg_tag_frozen) else None
|
|
182
|
+
|
|
183
|
+
def on_value_set(self):
|
|
184
|
+
new_value = self.before_set(dpg.get_value(self.dpg_tag_setter))
|
|
185
|
+
|
|
186
|
+
if self.gameobject.frozen is None:
|
|
187
|
+
self.gameobject.value = new_value
|
|
188
|
+
else:
|
|
189
|
+
self.gameobject.frozen = new_value
|
|
190
|
+
|
|
191
|
+
def on_hotkey_press(self):
|
|
192
|
+
dpg.set_value(self.dpg_tag_frozen, not dpg.get_value(self.dpg_tag_frozen))
|
|
193
|
+
|
|
194
|
+
self.on_frozen_state_change()
|
|
195
|
+
|
|
196
|
+
if self.tts_on_hotkey:
|
|
197
|
+
status = "released" if self.gameobject.frozen is None else "frozen"
|
|
198
|
+
say(f"GameObject {self.pure_label} {status}")
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class TeleportUI(AbstractUIComponent):
|
|
202
|
+
def __init__(
|
|
203
|
+
self,
|
|
204
|
+
tp: Teleport,
|
|
205
|
+
hotkey_save_position: str | None = None,
|
|
206
|
+
hotkey_set_saved_position: str | None = None,
|
|
207
|
+
hotkey_dash: str | None = None,
|
|
208
|
+
tts_on_hotkey: bool = True,
|
|
209
|
+
):
|
|
210
|
+
self.tp = tp
|
|
211
|
+
self.hotkey_save_position = hotkey_save_position
|
|
212
|
+
self.hotkey_set_saved_position = hotkey_set_saved_position
|
|
213
|
+
self.hotkey_dash = hotkey_dash
|
|
214
|
+
self.tts_on_hotkey = tts_on_hotkey
|
|
215
|
+
|
|
216
|
+
@override
|
|
217
|
+
def add_to_ui(self) -> None:
|
|
218
|
+
self._tp_add_save_set_position_hotkeys_if_needed()
|
|
219
|
+
self._tp_add_dash_hotkeys_if_needed()
|
|
220
|
+
|
|
221
|
+
add_components(
|
|
222
|
+
GameObjectUI(self.tp.player_x, "X"),
|
|
223
|
+
GameObjectUI(self.tp.player_y, "Y"),
|
|
224
|
+
GameObjectUI(self.tp.player_z, "Z"),
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
self._tp_add_labels_if_needed()
|
|
228
|
+
|
|
229
|
+
dpg.add_button(label="Clip Coords", callback=self.on_clip_coords)
|
|
230
|
+
|
|
231
|
+
def on_clip_coords(self):
|
|
232
|
+
dpg.set_clipboard_text(repr(self.tp.get_coords()))
|
|
233
|
+
|
|
234
|
+
def on_hotkey_save_position_press(self):
|
|
235
|
+
self.tp.save_position()
|
|
236
|
+
|
|
237
|
+
if self.tts_on_hotkey:
|
|
238
|
+
say("Position saved")
|
|
239
|
+
|
|
240
|
+
def on_hotkey_set_saved_position_press(self):
|
|
241
|
+
is_position_restored = self.tp.restore_saved_position()
|
|
242
|
+
|
|
243
|
+
if self.tts_on_hotkey:
|
|
244
|
+
say("Position restored" if is_position_restored else "Save position at first")
|
|
245
|
+
|
|
246
|
+
def on_hotkey_dash_press(self):
|
|
247
|
+
self.tp.dash()
|
|
248
|
+
if self.tts_on_hotkey:
|
|
249
|
+
say("Dash!")
|
|
250
|
+
|
|
251
|
+
def on_goto_label(self):
|
|
252
|
+
self.tp.goto(dpg.get_value(TAG_TELEPORT_LABELS))
|
|
253
|
+
|
|
254
|
+
def _tp_add_save_set_position_hotkeys_if_needed(self):
|
|
255
|
+
if self.hotkey_save_position is None or self.hotkey_set_saved_position is None:
|
|
256
|
+
return
|
|
257
|
+
|
|
258
|
+
add_hotkey(self.hotkey_save_position, self.on_hotkey_save_position_press)
|
|
259
|
+
add_hotkey(self.hotkey_set_saved_position, self.on_hotkey_set_saved_position_press)
|
|
260
|
+
|
|
261
|
+
dpg.add_text(f"[{self.hotkey_save_position}] Save Position")
|
|
262
|
+
dpg.add_text(f"[{self.hotkey_set_saved_position}] Set Saved Position")
|
|
263
|
+
|
|
264
|
+
def _tp_add_dash_hotkeys_if_needed(self):
|
|
265
|
+
if self.hotkey_dash is None:
|
|
266
|
+
return
|
|
267
|
+
|
|
268
|
+
add_hotkey(self.hotkey_dash, self.on_hotkey_dash_press)
|
|
269
|
+
|
|
270
|
+
dpg.add_text(f"[{self.hotkey_dash}] Dash")
|
|
271
|
+
|
|
272
|
+
def _tp_add_labels_if_needed(self):
|
|
273
|
+
if not self.tp.labels:
|
|
274
|
+
return
|
|
275
|
+
|
|
276
|
+
labels = sorted(self.tp.labels.keys())
|
|
277
|
+
|
|
278
|
+
with dpg.group(horizontal=True):
|
|
279
|
+
dpg.add_button(label="Go To", callback=self.on_goto_label)
|
|
280
|
+
dpg.add_combo(label="Labels", tag=TAG_TELEPORT_LABELS, items=labels, default_value=labels[0])
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class SpeedHackUI(AbstractUIComponent):
|
|
284
|
+
def __init__(self, speedhack: SpeedHack, key: str):
|
|
285
|
+
self.speedhack = speedhack
|
|
286
|
+
self.key = key
|
|
287
|
+
self.switch = ShortLongPressSwitch(SpeedHackUISwitch(speedhack, TAG_SPEEDHACK_FACTOR_INPUT), key)
|
|
288
|
+
|
|
289
|
+
@override
|
|
290
|
+
def add_to_ui(self) -> None:
|
|
291
|
+
dpg.add_text(f"Hold [{self.key}] Enable SpeedHack")
|
|
292
|
+
dpg.add_text(f"Press [{self.key}] Toggle SpeedHack")
|
|
293
|
+
|
|
294
|
+
dpg.add_input_double(
|
|
295
|
+
tag=TAG_SPEEDHACK_FACTOR_INPUT,
|
|
296
|
+
label="SpeedHack Factor",
|
|
297
|
+
min_value=0.0,
|
|
298
|
+
max_value=100.0,
|
|
299
|
+
default_value=3.0,
|
|
300
|
+
min_clamped=True,
|
|
301
|
+
max_clamped=True,
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
self.switch.handle()
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class SpeedHackUISwitch(Switchable):
|
|
308
|
+
def __init__(self, speedhack: SpeedHack, dpg_tag: str):
|
|
309
|
+
self.speedhack = speedhack
|
|
310
|
+
self.dpg_tag = dpg_tag
|
|
311
|
+
|
|
312
|
+
@override
|
|
313
|
+
def enable(self):
|
|
314
|
+
self.speedhack.factor = dpg.get_value(self.dpg_tag)
|
|
315
|
+
|
|
316
|
+
@override
|
|
317
|
+
def disable(self):
|
|
318
|
+
self.speedhack.factor = 1.0
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def add_components(*components: AbstractUIComponent):
|
|
322
|
+
for component in components:
|
|
323
|
+
component.add_to_ui()
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def simple_trainerbase_menu(window_title: str, width: int, height: int):
|
|
327
|
+
def menu_decorator(initializer: Callable):
|
|
328
|
+
@wraps(initializer)
|
|
329
|
+
def run_menu_wrapper(on_initialized: Callable):
|
|
330
|
+
dpg.create_context()
|
|
331
|
+
dpg.create_viewport(
|
|
332
|
+
title=window_title,
|
|
333
|
+
min_width=width,
|
|
334
|
+
min_height=height,
|
|
335
|
+
width=width,
|
|
336
|
+
height=height,
|
|
337
|
+
)
|
|
338
|
+
dpg.setup_dearpygui()
|
|
339
|
+
|
|
340
|
+
with dpg.window(
|
|
341
|
+
label=window_title,
|
|
342
|
+
tag="menu",
|
|
343
|
+
min_size=[width, height],
|
|
344
|
+
no_close=True,
|
|
345
|
+
no_move=True,
|
|
346
|
+
no_title_bar=True,
|
|
347
|
+
horizontal_scrollbar=True,
|
|
348
|
+
):
|
|
349
|
+
initializer()
|
|
350
|
+
|
|
351
|
+
dpg.show_viewport()
|
|
352
|
+
|
|
353
|
+
on_initialized()
|
|
354
|
+
|
|
355
|
+
dpg.start_dearpygui()
|
|
356
|
+
dpg.destroy_context()
|
|
357
|
+
|
|
358
|
+
return run_menu_wrapper
|
|
359
|
+
|
|
360
|
+
return menu_decorator
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def update_displayed_objects():
|
|
364
|
+
for game_object_ui in GameObjectUI.displayed_objects:
|
|
365
|
+
try:
|
|
366
|
+
new_value = game_object_ui.gameobject.value
|
|
367
|
+
except MemoryReadError:
|
|
368
|
+
new_value = "<Unresolved>"
|
|
369
|
+
|
|
370
|
+
dpg.set_value(game_object_ui.dpg_tag_getter, new_value)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from functools import partial
|
|
3
|
+
from os import _exit as force_exit
|
|
4
|
+
from typing import NoReturn
|
|
5
|
+
|
|
6
|
+
from trainerbase.codeinjection import safely_eject_all_code_injections
|
|
7
|
+
from trainerbase.gameobject import update_frozen_objects
|
|
8
|
+
from trainerbase.gui import update_displayed_objects
|
|
9
|
+
from trainerbase.scriptengine import Script, system_script_engine
|
|
10
|
+
from trainerbase.speedhack import SpeedHack
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def run(
|
|
14
|
+
run_gui: Callable[[Callable], None],
|
|
15
|
+
on_gui_initialized_hook: Callable | None = None,
|
|
16
|
+
on_shutdown_hook: Callable | None = None,
|
|
17
|
+
) -> NoReturn:
|
|
18
|
+
system_script_engine.register_script(Script(update_frozen_objects, enabled=True))
|
|
19
|
+
system_script_engine.register_script(Script(update_displayed_objects, enabled=True))
|
|
20
|
+
|
|
21
|
+
run_gui(partial(on_gui_initialized, on_gui_initialized_hook))
|
|
22
|
+
|
|
23
|
+
system_script_engine.stop()
|
|
24
|
+
safely_eject_all_code_injections()
|
|
25
|
+
SpeedHack.disable()
|
|
26
|
+
|
|
27
|
+
if on_shutdown_hook is not None:
|
|
28
|
+
on_shutdown_hook()
|
|
29
|
+
|
|
30
|
+
force_exit(0)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def on_gui_initialized(on_gui_initialized_hook: Callable | None = None):
|
|
34
|
+
system_script_engine.start()
|
|
35
|
+
|
|
36
|
+
if on_gui_initialized_hook is not None:
|
|
37
|
+
on_gui_initialized_hook()
|
|
@@ -1,29 +1,17 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
2
|
from threading import Thread
|
|
3
3
|
from time import sleep
|
|
4
|
-
from uuid import uuid4
|
|
5
4
|
|
|
6
|
-
from dearpygui import dearpygui as dpg
|
|
7
5
|
from pymem.exception import MemoryReadError, MemoryWriteError
|
|
8
6
|
|
|
9
|
-
from trainerbase.gameobject import GameObject
|
|
10
|
-
|
|
11
7
|
|
|
12
8
|
class Script:
|
|
13
|
-
DPG_TAG_PREFIX = "script__"
|
|
14
|
-
|
|
15
9
|
def __init__(self, callback: Callable, enabled: bool = False):
|
|
16
10
|
self.callback = callback
|
|
17
11
|
self.enabled = enabled
|
|
18
|
-
self.dpg_tag = f"{Script.DPG_TAG_PREFIX}{uuid4()}"
|
|
19
12
|
|
|
20
13
|
def __repr__(self):
|
|
21
|
-
return (
|
|
22
|
-
f"<Script {getattr(self.callback, '__name__', 'Anon')}:"
|
|
23
|
-
f" enabled={self.enabled},"
|
|
24
|
-
f" dpg_tag={repr(self.dpg_tag)}"
|
|
25
|
-
f">"
|
|
26
|
-
)
|
|
14
|
+
return f"<Script {getattr(self.callback, '__name__', 'Anon')}:" f" enabled={self.enabled}" f">"
|
|
27
15
|
|
|
28
16
|
def __call__(self):
|
|
29
17
|
return self.callback()
|
|
@@ -72,27 +60,3 @@ class ScriptEngine:
|
|
|
72
60
|
|
|
73
61
|
|
|
74
62
|
system_script_engine = ScriptEngine()
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
@system_script_engine.simple_script
|
|
78
|
-
def update_frozen_objects():
|
|
79
|
-
for game_object in GameObject.updated_objects:
|
|
80
|
-
if game_object.frozen is not None:
|
|
81
|
-
try:
|
|
82
|
-
game_object.value = game_object.frozen
|
|
83
|
-
except MemoryWriteError:
|
|
84
|
-
continue
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
@system_script_engine.simple_script
|
|
88
|
-
def update_displayed_objects():
|
|
89
|
-
for game_object in GameObject.updated_objects:
|
|
90
|
-
if dpg.does_alias_exist(game_object.dpg_tag_getter):
|
|
91
|
-
try:
|
|
92
|
-
dpg.set_value(game_object.dpg_tag_getter, game_object.value)
|
|
93
|
-
except MemoryReadError:
|
|
94
|
-
dpg.set_value(game_object.dpg_tag_getter, "<Unresolved>")
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
update_frozen_objects.enabled = True
|
|
98
|
-
update_displayed_objects.enabled = True
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Self
|
|
3
3
|
|
|
4
4
|
from pyinjector import inject
|
|
5
5
|
from pymem.exception import MemoryWriteError
|
|
6
6
|
from pymem.process import module_from_name
|
|
7
7
|
|
|
8
|
-
from trainerbase.common import ShortLongSwitch
|
|
9
8
|
from trainerbase.gameobject import GameDouble
|
|
10
9
|
from trainerbase.memory import ARCH, pm
|
|
11
10
|
|
|
@@ -16,6 +15,14 @@ SPEED_MODIFIER_OFFSET = 0x78048 if ARCH == 32 else 0x86040
|
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
class SpeedHack:
|
|
18
|
+
_instance: Self | None = None
|
|
19
|
+
|
|
20
|
+
def __new__(cls, *_args, **_kwargs):
|
|
21
|
+
if cls._instance is None:
|
|
22
|
+
cls._instance = super().__new__(cls)
|
|
23
|
+
|
|
24
|
+
return cls._instance
|
|
25
|
+
|
|
19
26
|
def __init__(self):
|
|
20
27
|
self._dll_injection_address = self._inject()
|
|
21
28
|
self._speed_modifier = GameDouble(self._dll_injection_address + SPEED_MODIFIER_OFFSET)
|
|
@@ -37,35 +44,13 @@ class SpeedHack:
|
|
|
37
44
|
def factor(self, value: float):
|
|
38
45
|
self._speed_modifier.value = value
|
|
39
46
|
|
|
40
|
-
|
|
47
|
+
@classmethod
|
|
48
|
+
def disable(cls):
|
|
49
|
+
if cls._instance is None:
|
|
50
|
+
return
|
|
51
|
+
|
|
41
52
|
# TODO: Should I eject dll?
|
|
42
53
|
try:
|
|
43
|
-
|
|
54
|
+
cls._instance.factor = 1.0
|
|
44
55
|
except MemoryWriteError:
|
|
45
56
|
pass
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class SpeedHackShortLongSwitch(ShortLongSwitch):
|
|
49
|
-
@override
|
|
50
|
-
def on_press(self, context: dict):
|
|
51
|
-
speedhack = context["speedhack"]
|
|
52
|
-
dpg_tag = context["dpg_tag"]
|
|
53
|
-
dpg = context["dpg"]
|
|
54
|
-
speedhack.factor = dpg.get_value(dpg_tag)
|
|
55
|
-
|
|
56
|
-
@override
|
|
57
|
-
def on_short_press(self, context: dict):
|
|
58
|
-
speedhack = context["speedhack"]
|
|
59
|
-
|
|
60
|
-
if context["enabled"]:
|
|
61
|
-
context["enabled"] = False
|
|
62
|
-
speedhack.factor = 1.0
|
|
63
|
-
else:
|
|
64
|
-
context["enabled"] = True
|
|
65
|
-
speedhack.factor = context["dpg"].get_value(context["dpg_tag"])
|
|
66
|
-
|
|
67
|
-
@override
|
|
68
|
-
def on_long_press(self, context: dict):
|
|
69
|
-
speedhack = context["speedhack"]
|
|
70
|
-
context["enabled"] = False
|
|
71
|
-
speedhack.factor = 1.0
|
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
from collections.abc import Callable
|
|
2
|
-
from functools import partial, wraps
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
from dearpygui import dearpygui as dpg
|
|
6
|
-
from keyboard import add_hotkey
|
|
7
|
-
|
|
8
|
-
from trainerbase.codeinjection import AbstractCodeInjection
|
|
9
|
-
from trainerbase.common import Teleport
|
|
10
|
-
from trainerbase.gameobject import (
|
|
11
|
-
GameBool,
|
|
12
|
-
GameByte,
|
|
13
|
-
GameDouble,
|
|
14
|
-
GameFloat,
|
|
15
|
-
GameInt,
|
|
16
|
-
GameLongLong,
|
|
17
|
-
GameObject,
|
|
18
|
-
GameShort,
|
|
19
|
-
GameUnsignedInt,
|
|
20
|
-
GameUnsignedLongLong,
|
|
21
|
-
GameUnsignedShort,
|
|
22
|
-
)
|
|
23
|
-
from trainerbase.scriptengine import Script
|
|
24
|
-
from trainerbase.speedhack import SpeedHack, SpeedHackShortLongSwitch
|
|
25
|
-
from trainerbase.tts import say
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
TAG_TELEPORT_LABELS = "__teleport_labels"
|
|
29
|
-
TAG_SPEEDHACK_FACTOR_INPUT = "tag_speedhack_factor_input"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def simple_trainerbase_menu(window_title: str, width: int, height: int):
|
|
33
|
-
def menu_decorator(initializer: Callable):
|
|
34
|
-
@wraps(initializer)
|
|
35
|
-
def run_menu_wrapper(on_initialized: Callable):
|
|
36
|
-
dpg.create_context()
|
|
37
|
-
dpg.create_viewport(
|
|
38
|
-
title=window_title,
|
|
39
|
-
min_width=width,
|
|
40
|
-
min_height=height,
|
|
41
|
-
width=width,
|
|
42
|
-
height=height,
|
|
43
|
-
)
|
|
44
|
-
dpg.setup_dearpygui()
|
|
45
|
-
|
|
46
|
-
with dpg.window(
|
|
47
|
-
label=window_title,
|
|
48
|
-
tag="menu",
|
|
49
|
-
min_size=[width, height],
|
|
50
|
-
no_close=True,
|
|
51
|
-
no_move=True,
|
|
52
|
-
no_title_bar=True,
|
|
53
|
-
horizontal_scrollbar=True,
|
|
54
|
-
):
|
|
55
|
-
initializer()
|
|
56
|
-
|
|
57
|
-
dpg.show_viewport()
|
|
58
|
-
|
|
59
|
-
on_initialized()
|
|
60
|
-
|
|
61
|
-
dpg.start_dearpygui()
|
|
62
|
-
dpg.destroy_context()
|
|
63
|
-
|
|
64
|
-
return run_menu_wrapper
|
|
65
|
-
|
|
66
|
-
return menu_decorator
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def add_script_to_gui(
|
|
70
|
-
script: Script,
|
|
71
|
-
label: str,
|
|
72
|
-
hotkey: str | None = None,
|
|
73
|
-
tts_on_hotkey: bool = True,
|
|
74
|
-
):
|
|
75
|
-
def on_script_state_change():
|
|
76
|
-
script.enabled = dpg.get_value(script.dpg_tag)
|
|
77
|
-
|
|
78
|
-
if hotkey is not None:
|
|
79
|
-
pure_label = label
|
|
80
|
-
|
|
81
|
-
def on_hotkey_press():
|
|
82
|
-
dpg.set_value(script.dpg_tag, not dpg.get_value(script.dpg_tag))
|
|
83
|
-
on_script_state_change()
|
|
84
|
-
if tts_on_hotkey:
|
|
85
|
-
status = "enabled" if script.enabled else "disabled"
|
|
86
|
-
say(f"Script {pure_label} {status}")
|
|
87
|
-
|
|
88
|
-
add_hotkey(hotkey, on_hotkey_press)
|
|
89
|
-
|
|
90
|
-
label = f"[{hotkey}] {label}"
|
|
91
|
-
|
|
92
|
-
dpg.add_checkbox(label=label, tag=script.dpg_tag, callback=on_script_state_change, default_value=script.enabled)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def add_gameobject_to_gui(
|
|
96
|
-
gameobject: GameObject,
|
|
97
|
-
label: str,
|
|
98
|
-
hotkey: str | None = None,
|
|
99
|
-
default_setter_input_value: Any = 0,
|
|
100
|
-
before_set: Callable = int,
|
|
101
|
-
tts_on_hotkey: bool = True,
|
|
102
|
-
):
|
|
103
|
-
def on_frozen_state_change():
|
|
104
|
-
gameobject.frozen = gameobject.value if dpg.get_value(gameobject.dpg_tag_frozen) else None
|
|
105
|
-
|
|
106
|
-
def on_value_set():
|
|
107
|
-
new_value = before_set(dpg.get_value(gameobject.dpg_tag_setter))
|
|
108
|
-
|
|
109
|
-
if gameobject.frozen is None:
|
|
110
|
-
gameobject.value = new_value
|
|
111
|
-
else:
|
|
112
|
-
gameobject.frozen = new_value
|
|
113
|
-
|
|
114
|
-
if hotkey is not None:
|
|
115
|
-
pure_label = label
|
|
116
|
-
|
|
117
|
-
def on_hotkey_press():
|
|
118
|
-
dpg.set_value(gameobject.dpg_tag_frozen, not dpg.get_value(gameobject.dpg_tag_frozen))
|
|
119
|
-
on_frozen_state_change()
|
|
120
|
-
if tts_on_hotkey:
|
|
121
|
-
status = "released" if gameobject.frozen is None else "frozen"
|
|
122
|
-
say(f"GameObject {pure_label} {status}")
|
|
123
|
-
|
|
124
|
-
add_hotkey(hotkey, on_hotkey_press)
|
|
125
|
-
|
|
126
|
-
label = f"[{hotkey}] {label}"
|
|
127
|
-
|
|
128
|
-
match gameobject:
|
|
129
|
-
case GameFloat():
|
|
130
|
-
add_setter_input = dpg.add_input_float
|
|
131
|
-
case GameDouble():
|
|
132
|
-
add_setter_input = dpg.add_input_double
|
|
133
|
-
case GameByte() | GameShort() | GameInt() | GameLongLong() | GameBool():
|
|
134
|
-
add_setter_input = dpg.add_input_int
|
|
135
|
-
case GameUnsignedShort() | GameUnsignedInt() | GameUnsignedLongLong():
|
|
136
|
-
add_setter_input = partial(dpg.add_input_int, min_clamped=True, min_value=0)
|
|
137
|
-
case _:
|
|
138
|
-
add_setter_input = dpg.add_input_text
|
|
139
|
-
|
|
140
|
-
with dpg.group(horizontal=True):
|
|
141
|
-
dpg.add_checkbox(tag=gameobject.dpg_tag_frozen, callback=on_frozen_state_change)
|
|
142
|
-
dpg.add_text(label)
|
|
143
|
-
dpg.add_input_text(width=220, tag=gameobject.dpg_tag_getter, readonly=True)
|
|
144
|
-
add_setter_input(width=220, tag=gameobject.dpg_tag_setter, default_value=default_setter_input_value)
|
|
145
|
-
dpg.add_button(label="Set", callback=on_value_set)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
def add_codeinjection_to_gui(
|
|
149
|
-
injection: AbstractCodeInjection,
|
|
150
|
-
label: str,
|
|
151
|
-
hotkey: str | None = None,
|
|
152
|
-
tts_on_hotkey: bool = True,
|
|
153
|
-
):
|
|
154
|
-
def on_codeinjection_state_change():
|
|
155
|
-
if dpg.get_value(injection.dpg_tag):
|
|
156
|
-
injection.inject()
|
|
157
|
-
else:
|
|
158
|
-
injection.eject()
|
|
159
|
-
|
|
160
|
-
if hotkey is not None:
|
|
161
|
-
pure_label = label
|
|
162
|
-
|
|
163
|
-
def on_hotkey_press():
|
|
164
|
-
dpg.set_value(injection.dpg_tag, not dpg.get_value(injection.dpg_tag))
|
|
165
|
-
on_codeinjection_state_change()
|
|
166
|
-
if tts_on_hotkey:
|
|
167
|
-
status = "applied" if dpg.get_value(injection.dpg_tag) else "removed"
|
|
168
|
-
say(f"CodeInjection {pure_label} {status}")
|
|
169
|
-
|
|
170
|
-
add_hotkey(hotkey, on_hotkey_press)
|
|
171
|
-
|
|
172
|
-
label = f"[{hotkey}] {label}"
|
|
173
|
-
|
|
174
|
-
dpg.add_checkbox(label=label, tag=injection.dpg_tag, callback=on_codeinjection_state_change)
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
def add_teleport_to_gui(
|
|
178
|
-
tp: Teleport,
|
|
179
|
-
hotkey_save_position: str | None = None,
|
|
180
|
-
hotkey_set_saved_position: str | None = None,
|
|
181
|
-
hotkey_dash: str | None = None,
|
|
182
|
-
tts_on_hotkey: bool = True,
|
|
183
|
-
):
|
|
184
|
-
_tp_add_save_set_position_hotkeys_if_needed(tp, hotkey_save_position, hotkey_set_saved_position, tts_on_hotkey)
|
|
185
|
-
_tp_add_dash_hotkeys_if_needed(tp, hotkey_dash, tts_on_hotkey)
|
|
186
|
-
|
|
187
|
-
add_gameobject_to_gui(tp.player_x, "X")
|
|
188
|
-
add_gameobject_to_gui(tp.player_y, "Y")
|
|
189
|
-
add_gameobject_to_gui(tp.player_z, "Z")
|
|
190
|
-
|
|
191
|
-
_tp_add_labels_if_needed(tp)
|
|
192
|
-
|
|
193
|
-
def on_clip_coords():
|
|
194
|
-
dpg.set_clipboard_text(repr(tp.get_coords()))
|
|
195
|
-
|
|
196
|
-
dpg.add_button(label="Clip Coords", callback=on_clip_coords)
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
def _tp_add_save_set_position_hotkeys_if_needed(
|
|
200
|
-
tp: Teleport,
|
|
201
|
-
hotkey_save_position: str | None,
|
|
202
|
-
hotkey_set_saved_position: str | None,
|
|
203
|
-
tts_on_hotkey: bool,
|
|
204
|
-
):
|
|
205
|
-
if hotkey_save_position is None or hotkey_set_saved_position is None:
|
|
206
|
-
return
|
|
207
|
-
|
|
208
|
-
def on_hotkey_save_position_press():
|
|
209
|
-
tp.save_position()
|
|
210
|
-
if tts_on_hotkey:
|
|
211
|
-
say("Position saved")
|
|
212
|
-
|
|
213
|
-
def on_hotkey_set_saved_position_press():
|
|
214
|
-
is_position_restored = tp.restore_saved_position()
|
|
215
|
-
|
|
216
|
-
if tts_on_hotkey:
|
|
217
|
-
say("Position restored" if is_position_restored else "Save position at first")
|
|
218
|
-
|
|
219
|
-
add_hotkey(hotkey_save_position, on_hotkey_save_position_press)
|
|
220
|
-
add_hotkey(hotkey_set_saved_position, on_hotkey_set_saved_position_press)
|
|
221
|
-
|
|
222
|
-
dpg.add_text(f"[{hotkey_save_position}] Save Position")
|
|
223
|
-
dpg.add_text(f"[{hotkey_set_saved_position}] Set Saved Position")
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
def _tp_add_dash_hotkeys_if_needed(tp, hotkey_dash: str | None, tts_on_hotkey: bool):
|
|
227
|
-
if tp.movement_vector_updater_script is None or hotkey_dash is None:
|
|
228
|
-
return
|
|
229
|
-
|
|
230
|
-
def on_hotkey_dash_press():
|
|
231
|
-
tp.dash()
|
|
232
|
-
if tts_on_hotkey:
|
|
233
|
-
say("Dash!")
|
|
234
|
-
|
|
235
|
-
add_hotkey(hotkey_dash, on_hotkey_dash_press)
|
|
236
|
-
|
|
237
|
-
dpg.add_text(f"[{hotkey_dash}] Dash")
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
def _tp_add_labels_if_needed(tp: Teleport):
|
|
241
|
-
if not tp.labels:
|
|
242
|
-
return
|
|
243
|
-
|
|
244
|
-
def on_goto_label():
|
|
245
|
-
tp.goto(dpg.get_value(TAG_TELEPORT_LABELS))
|
|
246
|
-
|
|
247
|
-
labels = sorted(tp.labels.keys())
|
|
248
|
-
|
|
249
|
-
with dpg.group(horizontal=True):
|
|
250
|
-
dpg.add_button(label="Go To", callback=on_goto_label)
|
|
251
|
-
dpg.add_combo(label="Labels", tag=TAG_TELEPORT_LABELS, items=labels, default_value=labels[0])
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
def add_speedhack_to_gui(speedhack: SpeedHack, key: str):
|
|
255
|
-
switch = SpeedHackShortLongSwitch(
|
|
256
|
-
key,
|
|
257
|
-
context={
|
|
258
|
-
"speedhack": speedhack,
|
|
259
|
-
"dpg_tag": TAG_SPEEDHACK_FACTOR_INPUT,
|
|
260
|
-
"enabled": False,
|
|
261
|
-
"dpg": dpg,
|
|
262
|
-
},
|
|
263
|
-
)
|
|
264
|
-
switch.handle()
|
|
265
|
-
|
|
266
|
-
dpg.add_text(f"Hold [{key}] Enable SpeedHack")
|
|
267
|
-
dpg.add_text(f"Press [{key}] Toggle SpeedHack")
|
|
268
|
-
|
|
269
|
-
dpg.add_input_double(
|
|
270
|
-
tag=TAG_SPEEDHACK_FACTOR_INPUT,
|
|
271
|
-
label="SpeedHack Factor",
|
|
272
|
-
min_value=0.0,
|
|
273
|
-
max_value=100.0,
|
|
274
|
-
default_value=3.0,
|
|
275
|
-
min_clamped=True,
|
|
276
|
-
max_clamped=True,
|
|
277
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|