-
Notifications
You must be signed in to change notification settings - Fork 537
Expand file tree
/
Copy pathgame_wrapper_pokemon_gen1.py
More file actions
137 lines (112 loc) · 5.22 KB
/
Copy pathgame_wrapper_pokemon_gen1.py
File metadata and controls
137 lines (112 loc) · 5.22 KB
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
#
# License: See LICENSE.md file
# GitHub: https://github.com/Baekalfen/PyBoy
#
__pdoc__ = {
"GameWrapperPokemonGen1.cartridge_title": False,
"GameWrapperPokemonGen1.post_tick": False,
}
import numpy as np
import pyboy
from .base_plugin import PyBoyGameWrapper
logger = pyboy.logging.get_logger(__name__)
# Set of addresses for information on pokemon
# Based on the following link: https://datacrystal.tcrf.net/wiki/Pok%C3%A9mon_Red_and_Blue/RAM_map
ADDR_TITLE_SCREEN = 0x1FC3
ADDR_NUMBER_OF_POKEMON = 0xD163 # In party
#Item related values
ADDR_TOTAL_ITEMS = 0xD53A
ADDR_BADGES = 0xD356 # Currently requires conversion (each bit is an independent switch and represents a badge)
# Game Time
ADDR_HOURS = 0xDA40
ADDR_MINUTES = 0xDA42
ADDR_SECONDS = 0xDA44
# Battle mode
ADDR_BATTLE = 0xD057
class GameWrapperPokemonGen1(PyBoyGameWrapper):
"""
This class wraps Pokemon Red/Blue, and provides basic access for AIs.
If you call `print` on an instance of this object, it will show an overview of everything this object provides.
"""
cartridge_title = None
def __init__(self, *args, **kwargs):
super().__init__(*args, game_area_section=(0, 0, 20, 18), game_area_follow_scxy=True, **kwargs)
self.sprite_offset = 0
if self.pyboy.memory[ADDR_TITLE_SCREEN] == 0:
pass
else:
self.number_of_pokemon = self.pyboy.memory[ADDR_NUMBER_OF_POKEMON]
self.total_items = self.pyboy.memory[ADDR_TOTAL_ITEMS]
# Extract the time in game:
_hours = self.pyboy.memory[ADDR_HOURS]
_minutes = self.pyboy.memory[ADDR_MINUTES]
_seconds = self.pyboy.memory[ADDR_SECONDS]
self.game_time = f'{_hours}:{_minutes}:{_seconds}'
# Check whether the player is in battle mode or out of battle
if self.pyboy.memory[ADDR_BATTLE] == 1:
self.battle_state = 'In Battle'
else:
self.battle_state = 'Overworld'
self.badges = self.pyboy.memory[ADDR_BADGES] #Research how this works.
def enabled(self):
return (self.pyboy.cartridge_title == "POKEMON RED") or (self.pyboy.cartridge_title == "POKEMON BLUE")
def post_tick(self):
self._tile_cache_invalid = True
self._sprite_cache_invalid = True
self.number_of_pokemon = self.pyboy.memory[ADDR_NUMBER_OF_POKEMON]
self.total_items = self.pyboy.memory[ADDR_TOTAL_ITEMS]
# Extract the time in game:
_hours = self.pyboy.memory[ADDR_HOURS]
_minutes = self.pyboy.memory[ADDR_MINUTES]
_seconds = self.pyboy.memory[ADDR_SECONDS]
self.game_time = f'{_hours}:{_minutes}:{_seconds}'
# Check whether the player is in battle mode or out of battle
if self.pyboy.memory[ADDR_BATTLE] == 1:
self.battle_state = 'In Battle'
else:
self.battle_state = 'Overworld'
self.badges = self.pyboy.memory[ADDR_BADGES] #Research how this works.
scanline_parameters = self.pyboy.screen.tilemap_position_list
# WX = scanline_parameters[0][2]
WY = scanline_parameters[0][3]
self.use_background(WY != 0)
def _get_screen_background_tilemap(self):
### SIMILAR TO CURRENT pyboy.game_wrapper.game_area(), BUT ONLY FOR BACKGROUND TILEMAP, SO NPC ARE SKIPPED
((scx, scy), (wx, wy)) = self.pyboy.screen.get_tilemap_position()
tilemap = np.array(self.pyboy.tilemap_background[:, :])
return np.roll(np.roll(tilemap, -scy // 8, axis=0), -scx // 8, axis=1)[:18, :20]
def _get_screen_walkable_matrix(self):
walkable_tiles_indexes = []
collision_ptr = self.pyboy.memory[0xD530] + (self.pyboy.memory[0xD531] << 8)
tileset_type = self.pyboy.memory[0xFFD7]
if tileset_type > 0:
grass_tile_index = self.pyboy.memory[0xD535]
if grass_tile_index != 0xFF:
walkable_tiles_indexes.append(grass_tile_index + 0x100)
for i in range(0x180):
tile_index = self.pyboy.memory[collision_ptr + i]
if tile_index == 0xFF:
break
else:
walkable_tiles_indexes.append(tile_index + 0x100)
screen_tiles = self._get_screen_background_tilemap()
bottom_left_screen_tiles = screen_tiles[1 : 1 + screen_tiles.shape[0] : 2, ::2]
walkable_matrix = np.isin(bottom_left_screen_tiles, walkable_tiles_indexes).astype(np.uint8)
return walkable_matrix
def game_area_collision(self):
width = self.game_area_section[2]
height = self.game_area_section[3]
game_area = np.ndarray(shape=(height, width), dtype=np.uint32)
_collision = self._get_screen_walkable_matrix()
for i in range(height // 2):
for j in range(width // 2):
game_area[i * 2][j * 2 : j * 2 + 2] = _collision[i][j]
game_area[i * 2 + 1][j * 2 : j * 2 + 2] = _collision[i][j]
return game_area
def __repr__(self):
return ("Pokemon Gen 1:\n"
+ f"Pokemon in Party: {self.number_of_pokemon}\n"
+ f"Battle State: {self.battle_state}\n"
+ f"Game Time: {self.game_time}\n"
+ f"Total items: {self.total_items}\n"
+ super().__repr__())