added agent movement animations; redefined game loop; added turn counting; adjusted classes; fixed a bug; code refactoring
This commit is contained in:
parent
0fc5cedae6
commit
af238a6ed6
69
agent.py
69
agent.py
@ -1,5 +1,6 @@
|
||||
import project_constants as const
|
||||
import json
|
||||
from pygame import transform, Surface, SRCALPHA
|
||||
|
||||
|
||||
# Class of our agent, initialization of it
|
||||
@ -7,13 +8,78 @@ import json
|
||||
# values that are later used by another function called 'is_valid_move' (which is defined in Minefield));
|
||||
|
||||
class Agent:
|
||||
last_action = None
|
||||
new_action = None
|
||||
|
||||
def __init__(self, json_path):
|
||||
with open(json_path) as json_data:
|
||||
data = json.load(json_data)
|
||||
self.row, self.column = data["agents_initial_state"]["position"].split(",")
|
||||
self.position = [int(self.row), int(self.column)]
|
||||
self.on_screen_coordinates = const.get_tile_coordinates(tuple(self.position))
|
||||
# self.direction = const.Direction()
|
||||
self.direction = const.Direction(data["agents_initial_state"]["direction"])
|
||||
self.rotation_angle = -const.Direction(self.direction).value * 90
|
||||
self.going_forward = False
|
||||
self.rotating_left = False
|
||||
self.rotating_right = False
|
||||
|
||||
def update_and_draw(self, window, delta_time):
|
||||
self.update(delta_time)
|
||||
self.draw(window)
|
||||
|
||||
def draw(self, window):
|
||||
coord_x, coord_y = self.on_screen_coordinates[0], self.on_screen_coordinates[1]
|
||||
tl_dimension = const.V_TILE_SIZE / 2
|
||||
|
||||
rotating_rect = transform.rotate(const.ASSET_SAPPER, self.rotation_angle).get_rect()
|
||||
rotating_rect.center = (coord_x + tl_dimension, coord_y + tl_dimension)
|
||||
|
||||
window.blit(transform.rotate(const.ASSET_SAPPER, self.rotation_angle), rotating_rect)
|
||||
|
||||
def update(self, delta_time):
|
||||
self.new_action = self.going_forward + self.rotating_left * 2 + self.rotating_right * 4
|
||||
|
||||
if self.going_forward:
|
||||
direction = const.Direction(self.direction).value
|
||||
x, y = self.on_screen_coordinates
|
||||
|
||||
# heading either up or down
|
||||
if direction % 2 == 0:
|
||||
self.on_screen_coordinates = (x, y + (direction - 1) * const.V_TILE_SIZE * delta_time)
|
||||
|
||||
# heading either left or right
|
||||
else:
|
||||
self.on_screen_coordinates = (x - (direction - 2) * const.V_TILE_SIZE * delta_time, y)
|
||||
|
||||
elif self.rotating_right:
|
||||
self.rotation_angle -= delta_time * 90 % 360
|
||||
|
||||
elif self.rotating_left:
|
||||
self.rotation_angle += delta_time * 90 % 360
|
||||
|
||||
if self.last_action != self.new_action or \
|
||||
not any((self.going_forward, self.rotating_right, self.rotating_left)):
|
||||
self.last_action = self.new_action
|
||||
self.rotation_angle = -const.Direction(self.direction).value * 90
|
||||
self.on_screen_coordinates = const.get_tile_coordinates(tuple(self.position))
|
||||
|
||||
def animate(self, action):
|
||||
self.reset_actions()
|
||||
if action == const.Action.ROTATE_LEFT:
|
||||
self.rotating_left = True
|
||||
elif action == const.Action.ROTATE_RIGHT:
|
||||
self.rotating_right = True
|
||||
elif action == const.Action.GO:
|
||||
self.going_forward = True
|
||||
|
||||
def take_action(self, action):
|
||||
if action == const.Action.ROTATE_LEFT:
|
||||
self.rotate_left()
|
||||
elif action == const.Action.ROTATE_RIGHT:
|
||||
self.rotate_right()
|
||||
elif action == const.Action.GO:
|
||||
self.go()
|
||||
|
||||
def rotate_left(self):
|
||||
self.direction = self.direction.previous()
|
||||
@ -50,3 +116,6 @@ class Agent:
|
||||
def go_down(self):
|
||||
|
||||
return self.position[0] + 1, self.position[1]
|
||||
|
||||
def reset_actions(self):
|
||||
self.going_forward = self.rotating_right = self.rotating_left = False
|
||||
|
@ -69,14 +69,15 @@ def blit_graphics(minefield):
|
||||
elif isinstance(tile.mine, TimeMine):
|
||||
display_time_mine(tile.position, "0" + str(tile.mine.timer))
|
||||
|
||||
# sapper
|
||||
display_sapper(
|
||||
minefield.agent.position,
|
||||
minefield.agent.direction
|
||||
)
|
||||
# changed sapper's movement from jumping to continuous movement (moved everything to Agent's class)
|
||||
# # sapper
|
||||
# display_sapper(
|
||||
# minefield.agent.position,
|
||||
# minefield.agent.direction
|
||||
# )
|
||||
|
||||
# update graphics after blitting
|
||||
pygame.display.update()
|
||||
# pygame.display.update()
|
||||
|
||||
|
||||
# moved here from minefield
|
||||
@ -216,12 +217,12 @@ def display_chained_mine(coords, parent_coords=None):
|
||||
|
||||
display_mine(coords)
|
||||
|
||||
if parent_coords is not None:
|
||||
const.SCREEN.blit(
|
||||
const.ASSET_CHAINS,
|
||||
calculate_screen_position(coords)
|
||||
)
|
||||
const.SCREEN.blit(
|
||||
const.ASSET_CHAINS,
|
||||
calculate_screen_position(coords)
|
||||
)
|
||||
|
||||
if parent_coords is not None:
|
||||
display_number(const.Coords.X, parent_coords[0])
|
||||
display_number(const.Coords.Y, parent_coords[1])
|
||||
|
||||
|
54
main.py
54
main.py
@ -27,10 +27,14 @@ def main():
|
||||
# create an instance of Minefield, pass necessary data
|
||||
minefield = mf.Minefield(const.MAP_RANDOM_10x10)
|
||||
|
||||
# getting agent's instance
|
||||
agent = minefield.agent
|
||||
|
||||
# setting flags for program
|
||||
running = True
|
||||
in_menu = True
|
||||
|
||||
# creating list storing all ui components
|
||||
ui_components_list = UiComponentsList([INPUT_ROW, INPUT_COLUMN, RANDOM_BUTTON, OK_BUTTON])
|
||||
|
||||
# initializing goal position
|
||||
@ -41,6 +45,10 @@ def main():
|
||||
|
||||
while running:
|
||||
|
||||
# ============== #
|
||||
# ==== MENU ==== #
|
||||
# ============== #
|
||||
|
||||
while running and in_menu:
|
||||
events = pygame.event.get()
|
||||
|
||||
@ -51,11 +59,10 @@ def main():
|
||||
# graphics (from display_assets)
|
||||
blit_graphics(minefield)
|
||||
|
||||
agent.update_and_draw(const.SCREEN, 0)
|
||||
|
||||
# drawing gui
|
||||
INPUT_ROW.run(const.SCREEN, pygame.mouse.get_pos(), events)
|
||||
INPUT_COLUMN.run(const.SCREEN, pygame.mouse.get_pos(), events)
|
||||
OK_BUTTON.draw(const.SCREEN, pygame.mouse.get_pos())
|
||||
RANDOM_BUTTON.draw(const.SCREEN, pygame.mouse.get_pos())
|
||||
ui_components_list.run_all(const.SCREEN, pygame.mouse.get_pos(), events)
|
||||
|
||||
# highlighting chosen tile destination (if exists)
|
||||
if not(INPUT_ROW.empty() or INPUT_COLUMN.empty()):
|
||||
@ -79,6 +86,10 @@ def main():
|
||||
|
||||
clock.tick(const.V_FPS)
|
||||
|
||||
# ========================== #
|
||||
# ==== BEFORE GAME LOOP ==== #
|
||||
# ========================== #
|
||||
|
||||
if running:
|
||||
for component in ui_components_list.selectable_ui_components:
|
||||
component.set_flags(is_active=False)
|
||||
@ -90,6 +101,13 @@ def main():
|
||||
direction=minefield.agent.direction),
|
||||
minefield=minefield, tox=row, toy=column)
|
||||
|
||||
action = None
|
||||
in_game_timer = 0
|
||||
|
||||
# =================== #
|
||||
# ==== GAME LOOP ==== #
|
||||
# =================== #
|
||||
|
||||
while running and not in_menu:
|
||||
|
||||
for event in pygame.event.get():
|
||||
@ -98,11 +116,15 @@ def main():
|
||||
|
||||
# FPS control
|
||||
clock.tick(const.V_FPS)
|
||||
delta_time = clock.get_time() / 1000
|
||||
action_delta_time = delta_time / const.ACTION_INTERVAL
|
||||
|
||||
# graphics (from display_assets)
|
||||
blit_graphics(minefield)
|
||||
const.SCREEN.blit(HIGHLIGHT, const.get_tile_coordinates((row, column)))
|
||||
|
||||
agent.update_and_draw(const.SCREEN, action_delta_time)
|
||||
|
||||
# drawing ui components so they don't "disappear"
|
||||
ui_components_list.draw_all(const.SCREEN, pygame.mouse.get_pos())
|
||||
|
||||
@ -110,27 +132,31 @@ def main():
|
||||
pygame.display.flip()
|
||||
|
||||
# make the next move from sequence of actions
|
||||
if any(action_sequence):
|
||||
if in_game_timer >= const.ACTION_INTERVAL and any(action_sequence):
|
||||
in_game_timer -= const.ACTION_INTERVAL
|
||||
minefield.next_turn()
|
||||
|
||||
agent.take_action(action)
|
||||
|
||||
action = action_sequence.pop(0)
|
||||
|
||||
if action == const.Action.ROTATE_LEFT:
|
||||
minefield.agent.rotate_left()
|
||||
elif action == const.Action.ROTATE_RIGHT:
|
||||
minefield.agent.rotate_right()
|
||||
elif action == const.Action.GO:
|
||||
minefield.agent.go()
|
||||
agent.animate(action)
|
||||
|
||||
time.sleep(const.ACTION_INTERVAL)
|
||||
elif in_game_timer >= const.ACTION_INTERVAL:
|
||||
agent.take_action(action)
|
||||
|
||||
agent.update_and_draw(const.SCREEN, delta_time)
|
||||
agent.reset_actions()
|
||||
|
||||
else:
|
||||
in_menu = True
|
||||
if not any([x.is_selected for x in ui_components_list.ui_components]):
|
||||
INPUT_ROW.set_is_selected(True)
|
||||
time.sleep(const.ACTION_INTERVAL)
|
||||
|
||||
for component in ui_components_list.selectable_ui_components:
|
||||
component.set_flags(is_active=True)
|
||||
|
||||
in_game_timer += delta_time
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -5,6 +5,7 @@ class TimeMine(Mine):
|
||||
def __init__(self, position, timer, active=True):
|
||||
self.type = "time"
|
||||
self.timer = timer
|
||||
self.starting_time = timer
|
||||
super().__init__(position, active)
|
||||
|
||||
def disarm(self):
|
||||
|
42
minefield.py
42
minefield.py
@ -1,9 +1,7 @@
|
||||
import agent as ag
|
||||
import project_constants as const
|
||||
import tile as tl
|
||||
from mine_models import standard_mine as sm
|
||||
from mine_models import time_mine as tm
|
||||
from mine_models import chained_mine as cm
|
||||
from mine_models.time_mine import TimeMine
|
||||
import json_generator as jg
|
||||
|
||||
|
||||
@ -37,6 +35,16 @@ class Minefield:
|
||||
predecessor = self.matrix[predecessor_row][predecessor_column]
|
||||
self.matrix[successor_row][successor_column].mine.predecessor = predecessor
|
||||
|
||||
def next_turn(self):
|
||||
self.turn += 1
|
||||
|
||||
for row in range(const.V_GRID_VER_TILES):
|
||||
for column in range(const.V_GRID_VER_TILES):
|
||||
mine = self.matrix[row][column].mine
|
||||
|
||||
if mine is not None and isinstance(mine, TimeMine):
|
||||
mine.timer = max(0, mine.starting_time - self.turn)
|
||||
|
||||
# ================ #
|
||||
# === MOVEMENT === #
|
||||
# ================ #
|
||||
@ -50,31 +58,3 @@ class Minefield:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
# distinguishes new mine's type and creates appropriate object
|
||||
def _create_mine(self, mine_data, row, column):
|
||||
mine_type = mine_data["mine_type"]
|
||||
|
||||
# TIME MINE
|
||||
if mine_type == "time":
|
||||
timer = mine_data["timer"]
|
||||
mine = tm.TimeMine((row, column), int(timer))
|
||||
|
||||
# CHAINED MINE
|
||||
elif mine_type == "chained":
|
||||
if mine_data["predecessor"] is not None:
|
||||
# locate predecessor
|
||||
row, column = map(int, mine_data["predecessor"].split(','))
|
||||
|
||||
# get predecessor object and assign it to the new mine
|
||||
predecessor = self.matrix[row][column].mine
|
||||
mine = cm.ChainedMine((row, column), predecessor)
|
||||
|
||||
else:
|
||||
mine = cm.ChainedMine((row, column))
|
||||
|
||||
# STANDARD MINE
|
||||
else:
|
||||
mine = sm.StandardMine((row, column))
|
||||
|
||||
return mine
|
||||
|
@ -22,7 +22,7 @@ V_NAME_OF_WINDOW = "MineFusion TM"
|
||||
DIR_ASSETS = os.path.join("resources", "assets")
|
||||
V_FPS = 60
|
||||
|
||||
ACTION_INTERVAL = 0.75 # interval between two actions in seconds
|
||||
ACTION_INTERVAL = 0.6 # interval between two actions in seconds
|
||||
|
||||
V_TILE_SIZE = 60
|
||||
V_GRID_VER_TILES = 10 # vertical (number of rows)
|
||||
@ -164,7 +164,7 @@ INPUT_ROW = InputBox(
|
||||
position=(_gui_x, _gui_y),
|
||||
dimensions=(_gui_width, _ib_height),
|
||||
text="row",
|
||||
valid_input_characters="123456890",
|
||||
valid_input_characters="1234567890",
|
||||
input_centered=True,
|
||||
clear_input_on_click=True
|
||||
)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import pygame
|
||||
from ui.button import Button
|
||||
from ui.input_box import InputBox
|
||||
|
||||
|
||||
class UiComponentsList:
|
||||
@ -18,6 +19,16 @@ class UiComponentsList:
|
||||
|
||||
self.selectable_ui_components[0].set_is_selected(select_first_item)
|
||||
|
||||
def run_all(self, window, mouse_position, events):
|
||||
for component in filter(lambda x: x not in self.selectable_ui_components, self.ui_components):
|
||||
component.draw(window)
|
||||
|
||||
for component in filter(lambda x: isinstance(x, Button), self.selectable_ui_components):
|
||||
component.draw(window, mouse_position)
|
||||
|
||||
for component in filter(lambda x: isinstance(x, InputBox), self.selectable_ui_components):
|
||||
component.run(window, mouse_position, events)
|
||||
|
||||
def draw_all(self, window, mouse_position):
|
||||
for component in filter(lambda x: x not in self.selectable_ui_components, self.ui_components):
|
||||
component.draw(window)
|
||||
|
Loading…
Reference in New Issue
Block a user