-
Notifications
You must be signed in to change notification settings - Fork 0
/
Main.py
186 lines (170 loc) · 7.01 KB
/
Main.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
import pygame
from DungeonAdventure import DungeonAdventure
from GUI.Raycast3D import Raycast
from GUI.Settings import *
from GUI.PlayerControls import PlayerControls
from GUI.Drawing import Drawing
from GUI.Sprites import SpritesContainer
from GUI.Menu import Menu
from GUI.Memo import Memo
from GUI.Sound import Sound
class Main:
"""
Pygame controller for running game. This class works in tandem and gets information
from DungeonAdventure which is the non-GUI controller of Model
"""
save_file = 'dungeon_save.pkl'
def __init__(self):
self.__sound = Sound()
self.__screen = None
self.__menu = None
self.__game_data = None
self.__world_coords = {}
self.__mini_map_coords = set()
self.__player_crtl = None
self.__hero = None
self.__drawing = None
self.__sprites = None
self.__raycast = None
self.__collision_walls = []
self.__load_game()
@property
def mini_map_coords(self):
return self.__mini_map_coords
def __load_game(self):
"""
Load data needed for game, mainly:
1. Setup models for pygame, loading constructors for
GUI controls (PlayerControl), GUI objects (Sprites),
drawing modules onto GUI sufrace (Drawing & Memo),
and "3D engine" of sorts by Raycasting
2. Grab Model information from DungeonAdventure.
"""
pygame.init()
pygame.event.set_blocked(None) # start by block everything
pygame.event.set_allowed([pygame.QUIT, pygame.KEYDOWN]) # allow only those relevant
pygame.display.set_caption('Dungeon Escape')
self.__screen = pygame.display.set_mode((WIDTH, HEIGHT))
self.__memo = Memo(self.__screen)
self.__menu = Menu(self.__screen, self.__sound)
self.__obtain_game_data()
# TODO: decrease/narrow params passed
self.__player_crtl = PlayerControls(self.__sound, self.__game_data, self.__memo, self.__collision_walls)
self.__sprites = SpritesContainer(self.__sound, self.__game_data, self.__player_crtl)
self.__drawing = Drawing(self.__screen, self.__sound, self.__mini_map_coords, self.__player_crtl,
self.__hero, self.__sprites, self.__game_data.maze.egress.coords)
self.__raycast = Raycast(self.__player_crtl, self.__world_coords, self.__drawing.textures)
self.__player_crtl.get_rooms_in_sight() # initiate sprites for 1st room
@staticmethod
def __validate_intro_dat(dat):
if not isinstance(dat, dict):
print(f'invalid dat type {type(dat)}')
return False
for k in 'guild name'.split():
if k not in dat:
print(f'invalid dat, missing {k}')
return False
return True
def __obtain_game_data(self):
"""
Helper function for load game, obtains:
hero & dungeon maze (including its monsters & objects)
"""
while self.__game_data is None:
op, dat = self.__menu.intro_menu()
if op == 'new':
print('new game')
if not self.__validate_intro_dat(dat):
continue
guild = dat['guild']
name = dat['name']
dat = DungeonAdventure(guild=guild, name=name)
elif op == 'load':
print('load from saved game')
if dat is None:
print(f'...but dat is None')
else:
raise ValueError(f"unrecognized op '{op}'")
if dat is None:
continue
self.__game_data = dat
self.__hero = self.__game_data.hero
def __parse_map(maze):
"""
Parse maze layout information into usable
information to create walls for GUI
Converts 4x4 string map into 9x9, lists of 3-length strings, 9th will be 1 char
"""
map_text = maze.str().splitlines()
map_parsed = []
row = []
for line in map_text:
temp = ''
i = 0
while i < len(line):
if len(temp) == 3:
row.append(temp)
temp = ''
temp += line[i]
i += 1
if len(temp) > 0:
row.append(temp)
map_parsed.append(row)
row = []
for j, line in enumerate(map_parsed):
for i, char in enumerate(line):
if char == '---' or '+' in char or '|' in char:
self.__collision_walls.append(pygame.Rect(i * TILE, j * TILE, TILE, TILE))
self.__world_coords[(i * TILE, j * TILE)] = 'wall'
self.__mini_map_coords.add((i * MAP_TILE, j * MAP_TILE))
elif '=' in char or char[0] == 'H':
self.__world_coords[(i * TILE, j * TILE)] = 'door'
__parse_map(self.__game_data.maze)
def game_loop(self):
"""
Game work-horse that loops through all major components of game
to create real-time effect of GUI game
"""
self.__sound.in_game()
clock = pygame.time.Clock()
while self.__hero.is_alive:
clock.tick(FPS)
self.__player_crtl.movement()
if self.__player_crtl.pause_on:
self.__player_crtl.pause_on = False
op = self.__menu.pause_menu(game_data=self.__game_data)
if op == 'continue':
print('Carry on as you were.')
elif op == 'reset':
print('Back to square one!')
self.__game_data = None
break
else:
# "This is bad, Peter. This is very, very bad."
print(f'pause_menu returned unrecognized op {op}')
break
elif self.__player_crtl.win_game:
break
else:
self.__drawing.background()
self.__sprites.load_sprites()
walls = self.__raycast.view_3D()
objects = self.__sprites.obtain_sprites(walls)
self.__drawing.world(walls + objects)
self.__memo.message_box()
self.__drawing.weapon_and_ui(clock)
pygame.display.flip()
if self.__game_data:
print(f'\nReveal of dungeon:\n{self.__game_data.maze}')
if self.__player_crtl.win_game:
self.__menu.win_screen()
elif not self.__menu.reset:
self.__menu.lose_screen()
if __name__ == '__main__':
try:
while True:
m = Main()
m.game_loop()
except KeyboardInterrupt:
print('\n\n Thank you for playing!\n\n')
exit(0)