Newer
Older
# coding=utf-8
import time
import os
import sys
from movableObjects import Enemy, Arrow, PowerBar
import keyboard
import random
# CURSOR ANSI ESCAPE PATTERNS
CURSOR_UP = '\033[1A' # moves cursor up one line
CURSOR_DOWN = '\033[1B' # moves cursor up one line
CURSOR_RESET = '\033[u' # resets the cursor
CURSOR_OFF = '\033[?25l' # makes cursor invisible
CURSOR_ON = '\033[?25h' # makes cursor visible
CURSOR_SAVE_POS = '\033[s' # saves cursor position
CURSOR_RESTORE_POS = '\033[u' # returns cursor to last saved position
def check_if_digits_decreased(number):
if len(str(number)) < len(str(number+1)):
return True
return False
def clear():
# posix is os name for Linux or mac
if os.name == 'posix':
os.system('clear')
# else screen will be cleared for windows
else:
os.system('cls')
class MiniGame:
"""
DISCLAIMER - Must be run in terminal for the screen to clear.
This class is used to start a game loop which runs a minigame where the player/user must defeat a set amount
of enemies by holding the space bar to charge up the weapons power and when released an arrow/bullet is fired
and if it hits the enemy then it kills it. Once the enemies have been defeated the game loop is broken and the
minigame is ended.
The minigame clears the screen so if you need to be able to view previous print's then save them all to a file or
variable and print them out after.
This is not the final version of the minigame and more can be added if there's time.
Issues:
If the arrow's velocity is greater than the width of an enemy then it can "Go" through the enemy and not kill
it as it won't register as a hit so either limit velocity or enemy width. Alternatively when the arrow moves check
all positions it will go through and check these for the enemy.
Next steps:
- The "body" of game objects (arrows and enemies) can have a character linked to each bodypart leading to more
complex designs
- Add animations for bow, arrow, enemy
- Display number of enemies left to defeat DONE
- Add color to text using the python colorama module
"""
def __init__(self, the_game_map, game_map_info, enemies_to_kill, arrows_to_use):
self.game_map = [[x for x in row] for row in the_game_map]
self.game_map_copy = the_game_map.copy()
self.game_info = game_map_info
self.enemies = []
self.add_enemy_to_map(70, 6, random.randint(3, 6), random.randint(3, 7), "█")
self.power_bar = PowerBar(self.game_map, 8, 5, 3, 1, 1, "█")
self.arrows_left = arrows_to_use
self.arrows = []
self.enemy_move_time = time.perf_counter()
self.arrow_move_time = time.perf_counter()
self.enemy_cooldown = time.perf_counter()
self.space_down = False
self.power_level = 0
self.enemies_left = enemies_to_kill
self.update_counter("enemy counter index", self.enemies_left)
self.update_counter("arrow counter index", self.arrows_left)
def reset_game(self):
self.game_map = self.game_map_copy.copy()
def update_counter(self, the_key, counter):
map_pos = self.game_info[the_key]
x, y = map_pos[0], map_pos[1]
for index, char in enumerate(str(counter)):
self.game_map[y][x + index] = str(char)
if check_if_digits_decreased(counter):
self.game_map[y][x + len(str(counter))] = " "
def add_enemy_to_map(self, x, y, w, h, char):
enemy = Enemy(self.game_info, x, y, w, h, char)
enemy.add_to_map(self.game_map, char)
self.enemies.append(enemy)
def add_arrow_to_map(self, x, y, w, h, vel_x, vel_y, char):
arrow = Arrow(self.game_info, x, y, w, h, vel_x, vel_y, char)
arrow.add_to_map(self.game_map, char)
self.arrows.append(arrow)
def keyboard_event(self, key):
if key.name == "space":
if key.event_type == "down":
if not self.space_down:
self.power_bar.add_to_map(self.game_map, " ")
self.power_bar.reset_bar()
self.space_down = True
self.power_level += 0.1
elif key.event_type == "up":
if self.arrows_left >= 1:
self.space_down = False
self.add_arrow_to_map(8, 12, 1, 1, self.power_bar.get_power()/2, -0.5, ">")
self.arrows_left -= 1
self.update_counter("arrow counter index", self.arrows_left)
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
def print_and_reset(self):
"""
Print's out the game map and then resets the cursor to the position it was before printing, so it's in the
right place to overwrite the current print of the game map acting as a clear inbetween "frames"
"""
for row in self.game_map: # Iterate over each frame's rows
for tile in row:
sys.stdout.write(tile)
sys.stdout.write('\n') # newline after all tiles in row shown
# Move cursor back to top of display ready for the next frame
sys.stdout.write(CURSOR_UP * len(self.game_map))
def check_hit(self):
to_remove = []
for index, enemy in enumerate(self.enemies):
for arrow in self.arrows:
for body_part in arrow.body:
if [int(body_part[0]), int(body_part[1])] in enemy.body:
enemy.add_to_map(self.game_map, " ")
to_remove.append(index)
for index in to_remove:
self.enemies.pop(index)
if len(to_remove) >= 1:
return True
return False
def enemy_move(self):
to_remove = []
# Returns updated map and whether the enemy is dead and so should be removed from the list of enemies.
for index, enemy in enumerate(self.enemies):
self.enemy_move_time = time.perf_counter()
self.game_map, alive = enemy.move(self.game_map, 0, 1)
if not alive:
to_remove.append(index)
for enemy_index in to_remove:
self.enemies.pop(enemy_index)
def arrow_move(self, dt):
self.arrow_move_time = time.perf_counter()
to_remove = []
for index, arrow in enumerate(self.arrows):
self.game_map, alive = arrow.arrow_move(self.game_map, dt)
if not alive:
to_remove.append(index)
for arrow_index in to_remove:
self.arrows.pop(arrow_index)
def game_loop(self):
os.system('color') # Required to work, enables ANSI codes
sys.stdout.write(CURSOR_OFF)
clear() # Clear cli
keyboard.hook(self.keyboard_event)
while True:
if self.space_down:
self.game_map = self.power_bar.next_level(self.game_map)
if time.perf_counter() - self.enemy_move_time > 0.2:
self.enemy_move()
dt = time.perf_counter() - self.arrow_move_time
if dt > 0.1:
self.arrow_move(dt)
self.print_and_reset()
if self.check_hit():
self.enemies_left -= 1
self.update_counter("enemy counter index", self.enemies_left)
if self.enemies_left <= 0:
time.sleep(1)
clear()
print(CURSOR_ON)
return True # return true if they won
self.enemy_cooldown = time.perf_counter()
if self.arrows_left <= 0 and len(self.arrows) == 0:
time.sleep(1)
clear()
print(CURSOR_ON)
if self.enemies_left <= 0:
print("You clutched up")
return True # They could have killed last enemy on last arrow so check if this won it
return False # Didn't kill all enemies so lost therefore return false
if len(self.enemies) == 0:
if time.perf_counter() - self.enemy_cooldown > 2:
self.add_enemy_to_map(random.randint(55, 70), random.randint(3, 15)
, random.randint(3, 6), random.randint(4, 7), "█")
time.sleep(0.05)
"""
# For testing
from minigame_map import game_map, game_info
while True:
game = MiniGame(game_map, game_info, 1, 1)
game.game_loop()
clear()
time.sleep(1)
"""