-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathStateManager.py
186 lines (148 loc) · 7.01 KB
/
StateManager.py
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import struct
import numpy as np
from dataclasses import dataclass, field
from typing import List
from State import *
@dataclass
class PlayerMemory:
'''
Here is stored every information for a given player
'''
percent: np.int32
facing: np.float32
pos_x: np.float32
pos_y: np.float32
action_state: np.int32
action_counter: np.int32
action_frame: np.float32
character: np.int32
body_state: np.bool
hitlag: np.float32
hitstun: np.float32
jumps_used: np.int32
charging_smash: np.bool
on_ground: np.bool
self_air_vel_x: np.float32
speed_ground_x_self: np.float32
self_air_vel_y: np.float32
attack_vel_x: np.float32
attack_vel_y: np.float32
shield_size: np.float32
cursor_x: np.float32
cursor_y: np.float32
def __init__(self):
return
def player_fac():
return [PlayerMemory(), PlayerMemory()]
@dataclass
class GameMemory:
frame: np.int32
menu: np.int32
stage: np.int32
players: List[PlayerMemory] = field(default_factory=player_fac())
# stage select screen
# sss_cursor_x: np.float32
# sss_cursor_y: np.float32
def __init__(self):
self.players = player_fac()
return
def int_handler(obj, name, shift=0, mask=0xFFFFFFFF, wrapper=None, default=0):
"""Returns a handler that sets an attribute for a given object.
obj is the object that will have its attribute set. Probably a State.
name is the attribute name to be set.
shift will be applied before mask.
Finally, wrapper will be called on the value if it is not None. If wrapper
raises ValueError, sets attribute to default.
This sets the attribute to default when called. Note that the actual final
value doesn't need to be an int. The wrapper can convert int to whatever.
This is particularly useful for enums.
"""
def handle(value):
transformed = (struct.unpack('>i', value)[0] >> shift) & mask
setattr(obj, name, generic_wrapper(transformed, wrapper, default))
setattr(obj, name, default)
return handle
def float_handler(obj, name, wrapper=None, default=0.0):
"""Returns a handler that sets an attribute for a given object.
Similar to int_handler, but no mask or shift.
"""
def handle(value):
as_float = struct.unpack('>f', value)[0]
setattr(obj, name, generic_wrapper(as_float, wrapper, default))
setattr(obj, name, default)
return handle
def generic_wrapper(value, wrapper, default):
if wrapper is not None:
try:
value = wrapper(value)
except ValueError:
value = default
return value
def add_address(x, y):
"""Returns a string representation of the sum of the two parameters.
x is a hex string address that can be converted to an int.
y is an int.
"""
return "{0:08X}".format(int(x, 16) + y)
class StateManager:
"""Converts raw memory changes into attributes in a State object."""
'''
TODO : projectiles
'''
def __init__(self, state, test=False):
"""Pass in a State object. It will have its attributes zeroed."""
self.state = state
self.addresses = {}
if test:
self.addresses['804D7420'] = int_handler(self.state, 'frame')
else: # If not testing, we are running with speedhack gecko code, which requires slippi's frame counter
self.addresses['804d6cf4'] = int_handler(self.state, 'frame') # slippi
self.addresses['80479D30'] = int_handler(self.state, 'menu', 0, 0xFF, Menu, Menu.Characters)
self.addresses['804D6CAC'] = int_handler(self.state, 'stage', 8, 0xFF, Stage, Stage.Unselected)
for player_id in range(2):
player = PlayerMemory()
self.state.players[player_id] = player
self.addresses[add_address('8045310C', 0xE90 * player_id)] = int_handler(player, 'stocks', 8, 0xFF)
cursor_x_address = add_address('81118DEC', -0xB80 * player_id)
cursor_y_address = add_address('81118DF0', -0xB80 * player_id)
self.addresses[cursor_x_address] = float_handler(player, 'cursor_x')
self.addresses[cursor_y_address] = float_handler(player, 'cursor_y')
type_address = add_address('803F0E08', 0x24 * player_id)
type_handler = int_handler(player, 'type', 24, 0xFF, PlayerType, PlayerType.Unselected)
character_handler = int_handler(player, 'character', 8, 0xFF, Character, Character.Unselected)
self.addresses[type_address] = [type_handler, character_handler]
data_pointer = add_address('80453130', 0xE90 * player_id)
self.addresses[data_pointer + ' 70'] = int_handler(player, 'action_state', 0, 0xFFFF, ActionState,
ActionState.Unselected)
self.addresses[data_pointer + ' 8C'] = float_handler(player, 'facing')
self.addresses[data_pointer + ' E0'] = float_handler(player, 'self_air_vel_x')
self.addresses[data_pointer + ' E4'] = float_handler(player, 'self_air_vel_y')
self.addresses[data_pointer + ' EC'] = float_handler(player, 'attack_vel_x')
self.addresses[data_pointer + ' F0'] = float_handler(player, 'attack_vel_y')
self.addresses[data_pointer + ' 14C'] = float_handler(player, 'speed_ground_x_self')
self.addresses[data_pointer + ' 110'] = float_handler(player, 'pos_x')
self.addresses[data_pointer + ' 114'] = float_handler(player, 'pos_y')
self.addresses[data_pointer + ' 140'] = int_handler(player, 'on_ground', 0, 0xFFFF, lambda x: x == 0, True)
self.addresses[data_pointer + ' 8F4'] = float_handler(player, 'action_frame')
self.addresses[data_pointer + ' 1890'] = float_handler(player, 'percent')
self.addresses[data_pointer + ' 19BC'] = float_handler(player, 'hitlag')
self.addresses[data_pointer + ' 20CC'] = int_handler(player, 'action_counter', 16, 0xFFFF)
self.addresses[data_pointer + ' 23A0'] = float_handler(player, 'hitstun')
self.addresses[data_pointer + ' 19F8'] = float_handler(player, 'shield_size')
self.addresses[data_pointer + ' 2174'] = int_handler(player, 'charging_smash', 0, 0x2)
self.addresses[data_pointer + ' 19C8'] = int_handler(player, 'jumps_used', 24, 0xFF)
self.addresses[data_pointer + ' 19EC'] = int_handler(player, 'body_state', 0, 0xFF, BodyState,
BodyState.Normal)
def handle(self, messages):
for address, value in messages:
"""Convert the raw address and value into changes in the State."""
assert address in self.addresses
handlers = self.addresses[address]
if isinstance(handlers, list):
for handler in handlers:
handler(value)
else:
handlers(value)
def locations(self):
"""Returns a list of addresses for exporting to Locations.txt."""
return self.addresses.keys()