diff --git a/Manual_ctf.pdf b/Manual_ctf.pdf new file mode 100644 index 0000000000000000000000000000000000000000..601854daa42e1fae7ed66c5202999b5a1ff72103 Binary files /dev/null and b/Manual_ctf.pdf differ diff --git a/__pycache__/ai.cpython-37.pyc b/__pycache__/ai.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a823f57355cee0728e07418968676be6cb0ece67 Binary files /dev/null and b/__pycache__/ai.cpython-37.pyc differ diff --git a/__pycache__/ctf.cpython-37.pyc b/__pycache__/ctf.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd7f19c7622934d1bd77fc232bf63a93e64be4d7 Binary files /dev/null and b/__pycache__/ctf.cpython-37.pyc differ diff --git a/__pycache__/gameobjects.cpython-37.pyc b/__pycache__/gameobjects.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39bcf4ea4230be5a4eeaa3ea4f609743c36555ed Binary files /dev/null and b/__pycache__/gameobjects.cpython-37.pyc differ diff --git a/__pycache__/images.cpython-311.pyc b/__pycache__/images.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e08ecc31bc01976803c0092099ad68a44d101c2 Binary files /dev/null and b/__pycache__/images.cpython-311.pyc differ diff --git a/__pycache__/images.cpython-37.pyc b/__pycache__/images.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df14fe77d23d6471915b508213fae4960fe2b034 Binary files /dev/null and b/__pycache__/images.cpython-37.pyc differ diff --git a/__pycache__/init_objects.cpython-37.pyc b/__pycache__/init_objects.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1be0cdcb96130a4b12cf0cc7146abfa4ff48ab7d Binary files /dev/null and b/__pycache__/init_objects.cpython-37.pyc differ diff --git a/__pycache__/maps.cpython-37.pyc b/__pycache__/maps.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9af117ad23c19a21a9617d71234f66f8fa7956d1 Binary files /dev/null and b/__pycache__/maps.cpython-37.pyc differ diff --git a/__pycache__/menu.cpython-37.pyc b/__pycache__/menu.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4044cccd6315b27f771cbb34aafd1d788ff5d798 Binary files /dev/null and b/__pycache__/menu.cpython-37.pyc differ diff --git a/__pycache__/print_functions.cpython-37.pyc b/__pycache__/print_functions.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b131ce0efdfbde62d86ef2276d81c9390d3853ef Binary files /dev/null and b/__pycache__/print_functions.cpython-37.pyc differ diff --git a/ai.py b/ai.py new file mode 100644 index 0000000000000000000000000000000000000000..3d80ac36eb0f154d1ea76f37db1200ebe12af150 --- /dev/null +++ b/ai.py @@ -0,0 +1,236 @@ +import math +import pymunk +from pymunk import Vec2d +import gameobjects +from collections import defaultdict, deque +import images +import pygame + +# NOTE: use only 'map0' during development! + +MIN_ANGLE_DIF = math.radians(6) # input degrees + +def angle_between_vectors(vec1, vec2): + """ Since Vec2d operates in a cartesian coordinate space we have to + convert the resulting vector to get the correct angle for our space. + """ + vec = vec1 - vec2 + vec = vec.perpendicular() + return vec.angle + +def periodic_difference_of_angles(angle1, angle2): + """ Return the periodic difference between two angles. """ + + return (angle1 - angle2)%(2*math.pi) + +class Ai: + """ A simple ai that finds the shortest path to the target using + a breadth first search. Also capable of shooting other tanks and or wooden + boxes. """ + + def __init__(self, tank,game_objects_list, tanks_list,space,currentmap): + """ Ai constructor """ + self.tank = tank + self.game_objects_list = game_objects_list + self.tanks_list = tanks_list + self.space = space + self.currentmap = currentmap + self.flag = None + self.MAX_X = currentmap.width - 1 + self.MAX_Y = currentmap.height - 1 + + self.metal_path = False + self.path = deque() + self.move_cycle = self.move_cycle_gen() + self.update_grid_pos() + self.last_shot = pygame.time.get_ticks() + + def ray_cast(self): + """Function for detecting ray cast for ai shoot function""" + + angle = self.tank.body.angle + math.pi/2 # adapt to cartesian angles + x_bullet = self.tank.body.position[0]+0.5*math.cos(angle) + y_bullet = self.tank.body.position[1]+0.5*math.sin(angle) + ray_beg = (x_bullet, y_bullet) # same as bullet start + ray_max1 = x_bullet + 100 * math.cos(angle) + ray_max2 = y_bullet + 100 * math.sin(angle) + ray_max = (ray_max1, ray_max2) # projectile line for bullet + return self.space.segment_query_first(ray_beg, ray_max, 0, \ + pymunk.ShapeFilter()) + + def update_grid_pos(self): + """ This should only be called in the beginning, + or at the end of a move_cycle. """ + + self.grid_pos = self.get_tile_of_position(self.tank.body.position) + + def maybe_shoot(self): + """ Makes a raycast query in front of the tank. If another tank + or a wooden box is found, then we shoot. """ + + res = self.ray_cast() + if hasattr(res, "shape"): + # check for the collision types + if isinstance(res.shape.collision_type, int): + col_type = res.shape.collision_type + if col_type == 1 or (col_type == 3 and \ + res.shape.parent.destructable) or col_type == 2: + ai_bullet = self.tank.shoot() + if ai_bullet != None: + ##unfair Ai (times 1.3) + ai_bullet.body.velocity = ai_bullet.body.velocity*1.3 + self.game_objects_list.append(ai_bullet) + + + def decide(self): + """ Main decision function that gets called on every tick of the game. + """ + self.maybe_shoot() + next(self.move_cycle) + + + def find_shortest_path(self): + """ A simple Breadth First Search using integer coordinates as our + nodes. Edges are calculated as we go, using an external + function. """ + + source_node = self.grid_pos.int_tuple + queue = deque() # neighbors (new nodes) + visited = set() + shortest_path = [] + queue.append((source_node, shortest_path)) + + while queue: # all tiles in worst case... + queue = self.astar_heu(queue) # rearrange according to heu! + curr_tile, curr_shortest = queue.popleft() + if self.get_target_tile() == curr_tile: + curr_shortest.append(curr_tile) + shortest_path = curr_shortest + return deque(shortest_path[1:]) + neighbours = self.get_tile_neighbors(curr_tile) + for neighbor in neighbours: + neighbor = neighbor.int_tuple + if neighbor not in visited: + queue.append((neighbor,curr_shortest + [curr_tile])) + visited.add(neighbor) + + + def astar_heu(self, input_queue): + """ Heurstic function for calculating a path the + flag using the shortest distance. """ + queue = deque(input_queue) + astar_queue = deque() + curr_min_dist = 1+Vec2d(self.tank.body.position\ + ).get_distance(self.get_target_tile()) + while queue: + curr_path =queue.popleft() + temp_dist =Vec2d(curr_path[0]).get_distance(self.get_target_tile()) + if temp_dist < curr_min_dist: + astar_queue.appendleft(curr_path) + curr_min_dist = temp_dist + else: + astar_queue.append(curr_path) + return astar_queue + + def move_cycle_gen(self): + """ A generator that iteratively goes through all the required steps + to move to our goal. """ + while True: + path = self.find_shortest_path() + if path == None: + self.metal_path = True + yield + continue + if len(path) == 0: + yield + continue + next_coord = path.popleft()+Vec2d(0.5, 0.5) + yield + + # turn() + angle_diff = angle_between_vectors(self.tank.body.position,\ + next_coord) + angle_next = periodic_difference_of_angles(\ + self.tank.body.angle,angle_diff) + self.tank.stop_moving() + self.tank.body.angle = self.tank.body.angle % (2*math.pi) + # Must check when tank is tilted downwords! + if (angle_next < -math.pi) or (math.pi > angle_next > 0): + self.tank.turn_left() + else: + self.tank.turn_right() + + # correct_angle() + while abs(angle_next) > MIN_ANGLE_DIF: + angle_next = periodic_difference_of_angles(\ + self.tank.body.angle,angle_diff) + yield + self.tank.stop_turning() + self.tank.accelerate() + yield + + # correct_pos() + current_dist = self.tank.body.position.get_distance(next_coord) + temp_next_dist = current_dist + while (current_dist >= temp_next_dist): + self.update_grid_pos() + # set new next tile when we reach the target! + temp_next_dist = current_dist + 1 + yield + move_cycle = self.move_cycle_gen() + + def get_target_tile(self): + """ Returns position of the flag if we don't have it. If we do + have the flag, return the position of our home base. """ + if self.tank.flag is not None: + x, y = self.tank.start_position + else: + self.get_flag() # Ensure that we have initialized it. + x, y = self.flag.x, self.flag.y + return Vec2d(int(x), int(y)) + + def get_flag(self): + """ This has to be called to get the flag, since we don't know + where it is when the Ai object is initialized. """ + if self.flag is None: + # Find the flag in the game objects list + for obj in self.game_objects_list: + if isinstance(obj, gameobjects.Flag): + self.flag = obj + break + return self.flag + + def get_tile_of_position(self, position_vector): + """ Converts and returns the float position of our tank to + an integer position.""" + + x, y = position_vector + return Vec2d(int(x), int(y)) + + def get_tile_neighbors(self, coord_vec): + """ Returns all bordering grid squares of the input coordinate. + A bordering square is only considered accessible if it is grass + or a wooden box. """ + + curr_tile = self.get_tile_of_position(coord_vec) + tile1 = Vec2d(curr_tile[0]+1, curr_tile[1]) + tile2 = Vec2d(curr_tile[0]-1, curr_tile[1]) + tile3 = Vec2d(curr_tile[0], curr_tile[1]+1) + tile4 = Vec2d(curr_tile[0], curr_tile[1]-1) + neighbors = [tile1, tile2, tile3, tile4] + return filter(self.filter_tile_neighbors, neighbors) + + + def filter_tile_neighbors (self, coord): + """ Checks wether a tank can move to the specific tile type""" + + x = coord[0] + y = coord[1] + if (x >= 0 and x <= self.MAX_X) and (y >= 0 and y <= self.MAX_Y): + box = self.currentmap.boxAt(x,y) + if box == 0 or box == 2 or self.metal_path == True and box == 3: + return True + return False + + + diff --git a/classes.py b/classes.py new file mode 100644 index 0000000000000000000000000000000000000000..da5db561f644fe0d6216bd1fdedffbd7ca161890 --- /dev/null +++ b/classes.py @@ -0,0 +1,34 @@ +class VideoGame: + def __init__(self, title, price, developer, platform): + self.title = title + self.price = price + self.developer = developer + self.platform = platform + + def print_description(self): + print() + print(f'{self.title} - Now only ${self.price}!!') + print(f'Developed by {self.developer} for {self.platform}') + + + +class PC_Game(VideoGame): + def __init__(self, title, price, developer, platform, requirements): + super().__init__(title, price, developer, platform) + self.requirements = requirements + + def print_description(self): + super().print_description() + print(f'Written by the renowned author {self.requirements}') + + + + + +videogames = [ + PC_Game('Crysis 3', 500, 'Crytek', 'PC','Intel HD Graphics 4000 is not the right option') + ] + + +#for videogame in videogames: + # videogame.print_description() diff --git a/ctf.py b/ctf.py new file mode 100644 index 0000000000000000000000000000000000000000..9494dee6d42dee26cd7dfe47e31d2e7c30788e07 --- /dev/null +++ b/ctf.py @@ -0,0 +1,335 @@ +import pygame +import sys +import time +from pygame.locals import * +from pygame.color import * +import pymunk +pygame.init() +pygame.display.set_mode() +#-- Initialise the clock +clock = pygame.time.Clock() +#-- Initialise the physics engine +space = pymunk.Space() +space.gravity = (0.0, 0.0) +space.damping = 0.1# Adds friction to the ground for all objects +#-- Import from the ctf framework +import ai +import images +import gameobjects +import maps +import menu +import print_functions +import init_objects + + +####### Collision functions ################# + +def collision_bullet_wall(arb, space, data): + """Handle the collision between bullet and wall.""" + + bullet = arb.shapes[0].parent + game_objects_list.remove(bullet) + space.remove(arb.shapes[0], arb.shapes[0].body) + return False + +def collision_bullet_box(arb, space, data): + """Handle the collision between bullet and box.""" + + bullet = arb.shapes[0].parent + space.remove(arb.shapes[0], arb.shapes[0].body) + if bullet in game_objects_list: + game_objects_list.remove(bullet) + box = arb.shapes[1].parent + if box.destructable: + if box.hitpoints > 1: + box.hitpoints = box.hitpoints-1 + else: + if box in game_objects_list: + game_objects_list.remove(box) + space.remove(arb.shapes[1], arb.shapes[1]) + return False + +def collision_bullet_tank(arb, space, data): + """Handle the collision between bullet and tank.""" + + bullet = arb.shapes[0].parent + tank = arb.shapes[1].parent + if bullet in game_objects_list: + game_objects_list.remove(bullet) + space.remove(arb.shapes[0], arb.shapes[0].body) + flag.is_on_tank = False + tank.on_death() + return False + + +def collision_bullet_bullet(arb, space, data): + """Handle the collision between bullet and bullet.""" + + bullet1= arb.shapes[1].parent + bullet2 = arb.shapes[0].parent + if bullet1 in game_objects_list: + game_objects_list.remove(bullet1) + space.remove(arb.shapes[0], arb.shapes[0].body) + if bullet2 in game_objects_list: + game_objects_list.remove(bullet2) + space.remove(arb.shapes[1], arb.shapes[1].body) + return False + +def create_collision_handlers(): + """Creates the handling of collisions + between different objects.""" + + handler = space.add_collision_handler(1, 3) + handler.pre_solve = collision_bullet_box + handler = space.add_collision_handler(1, 10) + handler.pre_solve = collision_bullet_wall + handler = space.add_collision_handler(1, 2) + handler.pre_solve = collision_bullet_tank + handler = space.add_collision_handler(1, 1) + handler.pre_solve = collision_bullet_bullet + +def create_objects(): + """Creates objects in the game.""" + + init_objects.create_borders(space,current_map) + init_objects.create_background(current_map,background) + init_objects.create_boxes(game_objects_list, current_map, space) + init_objects.create_tanks(tanks_list,game_objects_list , \ + current_map, space, multi, ai_list) + init_objects.create_bases(game_objects_list , current_map) + + +############## Main loop ###################### +def shoot_bullet(i): + """Shoots a bullet from the tank + and adds it to the game objects list.""" + + bullet = tanks_list[i].shoot() + if bullet != None: + game_objects_list.append(bullet) + + +def keyhandler(): + """Handles key events for the game.""" + + dic_keydown = { + K_UP: tanks_list[0].accelerate, + K_DOWN: tanks_list[0].decelerate, + K_RIGHT: tanks_list[0].turn_right, + K_LEFT: tanks_list[0].turn_left, + K_ESCAPE: pygame.quit} + + dic_keyup = { + K_UP: tanks_list[0].stop_moving, + K_DOWN: tanks_list[0].stop_moving, + K_LEFT: tanks_list[0].stop_turning, + K_RIGHT: tanks_list[0].stop_turning } + if multi: + dic_keydown_multi = { + K_w: tanks_list[1].accelerate, + K_s: tanks_list[1].decelerate, + K_d: tanks_list[1].turn_right, + K_a: tanks_list[1].turn_left} + + dic_keyup_multi = { + + K_w: tanks_list[1].stop_moving, + K_s: tanks_list[1].stop_moving, + K_d: tanks_list[1].stop_turning, + K_a: tanks_list[1].stop_turning} + + dic_keydown.update(dic_keydown_multi) + dic_keyup.update(dic_keyup_multi) + return dic_keydown, dic_keyup + + +def tank_win(tank, max_round, max_time): + """Handles the scenario when a tank wins the game.""" + + player = 1 + flag.x, flag.y = flag_start + flag.is_on_tank = False + tank.flag = None + tank.points = tank.points +1 + for i in tanks_list: + i.stop_moving() # make sure no speed when respawn + i.body.position = i.start_position + print("Player",player,":",i.points) + player = player+1 + print_functions.print_score_result(screen,tanks_list) + print("============") + + if tank.points == 2 or max_round == 10 or max_time>1800*5: # 1800 == 1 min + return False + return True + + +def fog_of_war(): + """Implements the fog of war effect for the game.""" + + tank_pixle_pos = 40 + tile_pixle_size = 100 + fog_pixle_radius = 90 + color_black = (255,255,255) + color_white = (0,0,0) + + map_pixle_size = ((current_map.width+1)*tile_pixle_size,\ + (current_map.height+1)*tile_pixle_size) + x = tanks_list[0].body.position[0]*tank_pixle_pos + y = tanks_list[0].body.position[1]*tank_pixle_pos + surface1 = pygame.Surface(map_pixle_size) + pygame.draw.circle(surface1, color_black, (x,y), \ + fog_pixle_radius,pygame.SRCALPHA) + + # add the extra fog vision in case of multiplayer + if multi: + x_multi = tanks_list[1].body.position[0]*tank_pixle_pos + y_multi = tanks_list[1].body.position[1]*tank_pixle_pos + pygame.draw.circle(surface1, color_black,(x_multi,y_multi), + fog_pixle_radius,pygame.SRCALPHA) + + pygame.draw.rect(surface1, color_white,(map_pixle_size[0], \ + map_pixle_size[1],0,0)) + surface1.set_colorkey(color_black) + screen.blit(surface1, (0,0)) + +def main_loop(): + """The main loop of the game where events are handled, + game objects are updated and displayed on the screen.""" + + + # init key inputs! + dic_keydown, dic_keyup = keyhandler() + + running = True + skip_update = 0 + + # Create the tank, images.tanks contains the image representing the tank + count_rounds = 1 + count_time = 0 + + while running: + count_time = count_time +1 + + for event in pygame.event.get(): + if event.type == KEYDOWN: + if event.key in dic_keydown: + dic_keydown[event.key]() + elif event.key == K_RETURN: + shoot_bullet(0) + elif event.key == K_SPACE: + shoot_bullet(1) + + elif event.type == KEYUP: + if event.key in dic_keyup: + dic_keyup[event.key]() + +#-- Update physics + + if skip_update == 0: + + for obj in game_objects_list: + obj.update() + skip_update = 2 + else: + skip_update -= 1 + + space.step(1 / FRAMERATE) + + #<INSERT DISPLAY BACKGROUND> + +# Display the background on the screen + screen.blit(background, (0, 0)) + + #<INSERT DISPLAY OBJECTS> + + # Update the display of the game objects on the screen + for i in tanks_list: + if i.has_won(): + count_rounds = count_rounds +1 + running = tank_win(i,count_rounds, count_time) + i.post_update(flag) + + + for i in ai_list: + i.decide() + + for obj in game_objects_list: + obj.update_screen(screen) + obj.update() + + if FOG: + fog_of_war() + + if game_mode == 1: + print_functions.print_stats_score(screen,\ + tanks_list, current_map) + + elif game_mode == 2: + print_functions.print_stats_time(screen, \ + count_time, current_map) + + elif game_mode == 3: + print_functions.print_stats_round(screen,\ + count_rounds , current_map) + + # Redisplay the entire screen (see double buffer technique) + pygame.display.flip() + + # Control the game framerate + clock.tick(FRAMERATE) + + # Redisplay the entire screen (see double buffer technique) + pygame.display.flip() + + # Control the game framerate + clock.tick(FRAMERATE) + +# main +try: + if str(sys.argv[1]) == "--singleplayer": + multi = False + if str(sys.argv[1]) == "--hot-multiplayer": + multi = True + FOG = False + current_map = maps.map0 + game_mode = 1 + +except IndexError: + main_tuple = menu.main_menu() + multi = main_tuple[0] + current_map = main_tuple[1] + FOG = main_tuple[2] + game_mode = main_tuple[3] + +#----- Initialisation -----# + +#-- Initialise the display + +#-- Constants +FRAMERATE = 100 + +#-- Variables + +# Define the current level + +flag_start = current_map.flag_position[0], current_map.flag_position[1] +# List of all game objects + +game_objects_list = [] +tanks_list = [] +ai_list = [] + +#-- Resize the screen to the size of the current level +screen = pygame.display.set_mode((current_map.rect().size[0]+100,\ + current_map.rect().size[1])) + +#<INSERT GENERATE BACKGROUND> + +#-- Generate the background +background = pygame.Surface(screen.get_size()) + +flag = init_objects.create_flag(game_objects_list,flag_start) +create_objects() +create_collision_handlers() +main_loop() diff --git a/data/base.kra b/data/base.kra new file mode 100644 index 0000000000000000000000000000000000000000..805a569aa3d6b4d8f2f0b33b2887f6af4bf182c7 Binary files /dev/null and b/data/base.kra differ diff --git a/data/base_blue.png b/data/base_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..b4acc006aad20c3d3275d754b6af87e15f0ae7e7 Binary files /dev/null and b/data/base_blue.png differ diff --git a/data/base_gray.png b/data/base_gray.png new file mode 100644 index 0000000000000000000000000000000000000000..daaaae97663e9bcfbf35fdaaa98b68492028f000 Binary files /dev/null and b/data/base_gray.png differ diff --git a/data/base_orange.png b/data/base_orange.png new file mode 100644 index 0000000000000000000000000000000000000000..d6e697c1986ff3d00165431db37c60314588567b Binary files /dev/null and b/data/base_orange.png differ diff --git a/data/base_red.png b/data/base_red.png new file mode 100644 index 0000000000000000000000000000000000000000..bae530ca3ed2ca726164a20a9ae786ce380e85b1 Binary files /dev/null and b/data/base_red.png differ diff --git a/data/base_white.png b/data/base_white.png new file mode 100644 index 0000000000000000000000000000000000000000..9b196090ebd51adf85a633eaf2351f22d6f608e2 Binary files /dev/null and b/data/base_white.png differ diff --git a/data/base_yellow.png b/data/base_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..8d2fc0fb6144684083b2d2e7a3215a706cba96dc Binary files /dev/null and b/data/base_yellow.png differ diff --git a/data/bullet.png b/data/bullet.png new file mode 100644 index 0000000000000000000000000000000000000000..77e8445e48e708a51e73d9ab8625b68f5547af80 Binary files /dev/null and b/data/bullet.png differ diff --git a/data/explosion.kra b/data/explosion.kra new file mode 100644 index 0000000000000000000000000000000000000000..1be3221f6dab027f4df3b28836e09c5fdde63039 Binary files /dev/null and b/data/explosion.kra differ diff --git a/data/explosion.png b/data/explosion.png new file mode 100644 index 0000000000000000000000000000000000000000..dbfc20f8d84e577a2d0fa2e9ffa8ddd50201abe7 Binary files /dev/null and b/data/explosion.png differ diff --git a/data/flag.png b/data/flag.png new file mode 100644 index 0000000000000000000000000000000000000000..60d5708bbe5abf8564e459d397458e9bca15ff8a Binary files /dev/null and b/data/flag.png differ diff --git a/data/grass.png b/data/grass.png new file mode 100644 index 0000000000000000000000000000000000000000..d9f37de36d7cfed9d5a1bf4021ac3d839c50a5a9 Binary files /dev/null and b/data/grass.png differ diff --git a/data/metalbox.png b/data/metalbox.png new file mode 100644 index 0000000000000000000000000000000000000000..1e4edec084ca62e5ecdd34fa7051cd76a03d89ba Binary files /dev/null and b/data/metalbox.png differ diff --git a/data/rockbox.png b/data/rockbox.png new file mode 100644 index 0000000000000000000000000000000000000000..b53b509abcc65762f888a3bf0aff92032de6f4a1 Binary files /dev/null and b/data/rockbox.png differ diff --git a/data/tank.kra b/data/tank.kra new file mode 100644 index 0000000000000000000000000000000000000000..ede20213172114be2307b62e86e7b7c0ec48ff3a Binary files /dev/null and b/data/tank.kra differ diff --git a/data/tank_blue.png b/data/tank_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..9235417dee08f19e67fbc97bf0f03506ec64bf39 Binary files /dev/null and b/data/tank_blue.png differ diff --git a/data/tank_gray.png b/data/tank_gray.png new file mode 100644 index 0000000000000000000000000000000000000000..4084b15c926ae93d5d23a5d77f37025e478924be Binary files /dev/null and b/data/tank_gray.png differ diff --git a/data/tank_orange.png b/data/tank_orange.png new file mode 100644 index 0000000000000000000000000000000000000000..84f940ba2c93504982ccf24c3fcb857e7839c122 Binary files /dev/null and b/data/tank_orange.png differ diff --git a/data/tank_red.png b/data/tank_red.png new file mode 100644 index 0000000000000000000000000000000000000000..7c8a5c1691621823b768ac638971fe2664782dc6 Binary files /dev/null and b/data/tank_red.png differ diff --git a/data/tank_white.png b/data/tank_white.png new file mode 100644 index 0000000000000000000000000000000000000000..a8e5367925987cce09edaff60a30288ad9bdb0d1 Binary files /dev/null and b/data/tank_white.png differ diff --git a/data/tank_yellow.png b/data/tank_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..a5ba8c999b53d0f48dd4953731fafd07f6df8a7b Binary files /dev/null and b/data/tank_yellow.png differ diff --git a/data/woodbox.png b/data/woodbox.png new file mode 100644 index 0000000000000000000000000000000000000000..15ee620943723cb134446c937c510dd2ab316fb8 Binary files /dev/null and b/data/woodbox.png differ diff --git a/gameobjects.py b/gameobjects.py new file mode 100644 index 0000000000000000000000000000000000000000..fbc4127cef470a5ba5192cfbbba8866f9c2c84a7 --- /dev/null +++ b/gameobjects.py @@ -0,0 +1,345 @@ +import images +import pygame +import pymunk +import math + +DEBUG = False # Change this to set it in debug mode + + +def physics_to_display(x): + """ This function is used to convert coordinates + in the physic engine into the display coordinates """ + + return x * images.TILE_SIZE + + +class GameObject: + """ Mostly handles visual aspects (pygame) of an object. + Subclasses need to implement two functions: + -screen_position that will return the position of the object + -screen_orientation that will return how much the object + is rotated on the screen (in degrees). """ + + def __init__(self, sprite): + """ Game object constructor """ + self.sprite = sprite + + + def update(self): + """ Placeholder, supposed to be implemented in a subclass. + Should update the current state (after a tick) of the object.""" + return + + def post_update(self): + """ Should be implemented in a subclass. Make updates that depend on + other objects than itself.""" + return + + + def update_screen(self, screen): + """ Updates the visual part of the game. Should NOT need to be changed + by a subclass.""" + sprite = self.sprite + + # Get the position of the object (pygame coordinates) + p = self.screen_position() + + # Rotate the sprite using the rotation of the object + sprite = pygame.transform.rotate(sprite, self.screen_orientation()) + + # The position of the screen correspond to the center of the object, + # but the function screen.blit expect to receive the top left corner + # as argument, so we need to adjust the position p with an offset + # which is the vector between the center of the sprite and the top left + # corner of the sprite + offset = pymunk.Vec2d(sprite.get_size()) / 2. + p = p - offset + screen.blit(sprite, p) # Copy the sprite on the screen + + +class GamePhysicsObject(GameObject): + """ This class extends GameObject and it is used for objects which have a + physical shape(such as tanks and boxes). This class handle the physical + interaction of the objects. + """ + + def __init__(self, x, y, orientation, sprite, space, movable): + """ Takes as parameters the starting coordinate (x,y), the orientation, + the sprite (aka the image representing the object), the physic + engine object(space) and whether the object can be moved (movable). + """ + super().__init__(sprite) + # Half dimensions of the object converted + # from screen coordinates to physic coordinates. + half_width = 0.5 * self.sprite.get_width() / images.TILE_SIZE + half_height = 0.5 * self.sprite.get_height() / images.TILE_SIZE + + # Physical objects have a rectangular shape, the points + # correspond to the corners of that shape. + points = [[-half_width, -half_height], + [-half_width, half_height], + [half_width, half_height], + [half_width, -half_height]] + self.points = points + + # Create a body (which is the physical representation + # of this game object in the physic engine) + if(movable): + # Create a movable object with some mass and moments + # (considering the game is a top view game, with no gravity, + # the mass is set to the same value for all objects).""" + mass = 10 + moment = pymunk.moment_for_poly(mass, points) + self.body = pymunk.Body(mass, moment) + else: + # Create a non movable (static) object + self.body = pymunk.Body(body_type=pymunk.Body.STATIC) + + self.body.position = x, y + # orientation is provided in degress, but pymunk expects radians. + self.body.angle = math.radians(orientation) + # Create a polygon shape using the corner of the rectangle + self.shape = pymunk.Poly(self.body, points) + self.shape.parent = self + + # Add the object to the physic engine + if(movable): + space.add(self.body, self.shape) + else: + space.add(self.shape) + + + def screen_position(self): + """ Converts the body's position in the physics engine to + screen coordinates. """ + return physics_to_display(self.body.position) + + def screen_orientation(self): + """ Angles are reversed from the engine to the display. """ + return -math.degrees(self.body.angle) + + def update_screen(self, screen): + """ Function for repaint the screen in its current state.""" + super().update_screen(screen) + # debug draw + if DEBUG: + ps = [self.body.position+p for p in self.points] + + ps = [physics_to_display(p) for p in ps] + ps += [ps[0]] + pygame.draw.lines(screen,pygame.color.THECOLORS["red"],False, ps,1) + + + + +def clamp(min_max, value): + """ Convenient helper function to bound a value to a specific interval. """ + return min(max(-min_max, value), min_max) + + +class Tank(GamePhysicsObject): + """ Extends GamePhysicsObject and handles aspects which are specific + to our tanks. """ + + ACCELERATION = 0.8 + NORMAL_MAX_SPEED = 4.0 + FLAG_MAX_SPEED = NORMAL_MAX_SPEED * 0.5 + + def __init__(self, x, y, orientation, sprite, space): + """ Tank constructor """ + super().__init__(x, y, orientation, sprite, space, True) + # Define variable used to apply motion to the tanks + self.shape.collision_type = 2 + self.acceleration = 0 # 1 forward, 0 for stand still, -1 for backwards + self.rotation = 0 # 1 clockwise, 0 for no rotation, -1 counter clockwise + self.timer = 0 + self.spawn_cooldown = 0 + self.cooldown = 0 + self.points = 0 + self.hitpoints = 2 + self.space = space + self.flag = None + self.max_speed = Tank.NORMAL_MAX_SPEED + self.start_position = pymunk.Vec2d(x, y) + + + def accelerate(self): + """ Call this function to make the tank move forward. """ + self.acceleration = 1 + + def stop_moving(self): + """ Call this function to make the tank stop moving. """ + self.acceleration = 0 + self.body.velocity = pymunk.Vec2d.zero() + + def decelerate(self): + """ Call this function to make the tank move backward. """ + self.acceleration = -1 + + def turn_left(self): + """ Makes the tank turn left (counter clock-wise). """ + self.rotation = -1 + + def turn_right(self): + """ Makes the tank turn right (clock-wise). """ + self.rotation = 1 + + def stop_turning(self): + """ Call this function to make the tank stop turning. """ + self.rotation = 0 + self.body.angular_velocity = 0 + + def update(self): + """ A function to update the objects coordinates. + Gets called at every tick of the game. """ + + # Creates a vector in the direction we want accelerate / decelerate + acceleration_vector = pymunk.Vec2d\ + (0,self.ACCELERATION*\ + self.acceleration).rotated(self.body.angle) + # Applies the vector to our velocity + self.body.velocity += acceleration_vector + + # Makes sure that we dont exceed our speed limit + velocity = clamp(self.max_speed, self.body.velocity.length) + self.body.velocity = pymunk.Vec2d(velocity, \ + 0).rotated(self.body.velocity.angle) + + # Updates the rotation + self.body.angular_velocity += self.rotation * self.ACCELERATION + self.body.angular_velocity = clamp(self.max_speed, \ + self.body.angular_velocity) + + + def post_update(self,flag): + # If the tank carries the flag, then update the positon of the flag + if(self.flag != None): + self.flag.x = self.body.position[0] + self.flag.y = self.body.position[1] + self.flag.orientation = -math.degrees(self.body.angle) + # Else ensure that the tank has its normal max speed + else: + self.max_speed = Tank.NORMAL_MAX_SPEED + + self.try_grab_flag(flag) + + self.timer += 1 + self.spawn_cooldown += 1 + self.cooldown -= 1 + + if self.cooldown > 0: + self.body.position = self.start_position + self.stop_moving() + + + + def try_grab_flag(self, flag): + """ Call this function to try to grab the flag, if the + flag is not on other tank and it is close to the current tank, + then the current tank will grab the flag. + """ + + # Check that the flag is not on other tank + if(not flag.is_on_tank): + # Check if the tank is close to the flag + flag_pos = pymunk.Vec2d(flag.x, flag.y) + if((flag_pos - self.body.position).length < 0.5): + # Grab the flag ! + self.flag = flag + flag.is_on_tank = True + self.max_speed = Tank.FLAG_MAX_SPEED + + def has_won(self): + """ Check if the current tank has won (if it is has the + flag and it is close to its start position). """ + + return self.flag != None and \ + (self.start_position - self.body.position).length < 0.2 + + + + def shoot(self): + """ Call this function to shoot a missile""" + + if self.timer > 60: + self.timer = 0 + x = self.body.position[0]+ 0.3*math.cos(math.pi/2+self.body.angle) + y = self.body.position[1]+0.3*math.sin(math.pi/2+self.body.angle) + return Bullet(x, y, self.body.angle, images.bullet, self.space) + + def on_death(self): + """ Reset the tank's position and set the cooldown when a tank is + destroyed. """ + self.flag = None + + if self.spawn_cooldown > 100: + if self.hitpoints > 1: + self.hitpoints = self.hitpoints-1 + else: + self.body.position = self.start_position + self.cooldown = 120 + self.max_speed = 0 + self.spawn_cooldown = 0 + +class Box(GamePhysicsObject): + """ This class extends the GamePhysicsObject to handle box objects. """ + + def __init__(self, x, y, sprite, movable, space, destructable): + """ It takes as arguments the coordinate of the starting position + of the box (x,y) and the box model (boxmodel). """ + super().__init__(x, y, 0, sprite, space, movable) + self.shape.collision_type = 3 + self.destructable = destructable + self.hitpoints = 2 + + +def get_box_with_type(x, y, type, space): + """ Return the box onject corresponding to the input type""" + (x, y) = (x + 0.5, y + 0.5) # set offsets in center of the tile + if type == 1: # Creates a non-movable non-destructable rockbox + return Box(x, y, images.rockbox, False, space, False) + if type == 2: # Creates a movable destructable woodbox + return Box(x, y, images.woodbox, True, space, True) + if type == 3: # Creates a movable non-destructable metalbox + return Box(x, y, images.metalbox, True, space, False) + + + +class GameVisibleObject(GameObject): + """ This class extends GameObject for object that are visible on + screen but have no physical representation (bases and flag) """ + + def __init__(self, x, y, sprite): + """ It takes argument the coordinates (x,y) and the sprite. """ + self.x = x + self.y = y + self.orientation = 0 + super().__init__(sprite) + + def screen_position(self): + return physics_to_display(pymunk.Vec2d(self.x, self.y)) + + def screen_orientation(self): + return self.orientation + + +class Flag(GameVisibleObject): + """ This class extends GameVisibleObject for representing flags.""" + + def __init__(self, x, y): + self.is_on_tank = False + super().__init__(x, y, images.flag) + + +class Bullet(GamePhysicsObject): + """ Class for the bullet objects in the game.""" + def __init__(self, x, y, sprite, orientation, space): + """Bullet constructor, take information from the tank that + shoots as input""" + super().__init__(x,y, sprite, orientation, space, True) + self.orientation = math.degrees(self.body.angle) + self.shape.collision_type = 1 + self.velocity = 15 + self.body.velocity = pymunk.Vec2d(\ + (0,self.velocity)).rotated(self.orientation) + diff --git a/images.py b/images.py new file mode 100644 index 0000000000000000000000000000000000000000..6ec9605c3263cb3516d344434c8c3808627adb94 --- /dev/null +++ b/images.py @@ -0,0 +1,46 @@ +import pygame +import os + +main_dir = os.path.split(os.path.abspath(__file__))[0] + +def load_image(file): + """ Load an image from the data directory. """ + + file = os.path.join(main_dir, 'data', file) + try: + surface = pygame.image.load(file) + except pygame.error: + raise SystemExit('Could not load image "%s" %s'%\ + (file, pygame.get_error())) + return surface.convert_alpha() + + +TILE_SIZE = 40 # Define the default size of tiles + +explosion = load_image('explosion.png') # Image of an explosion + +grass = load_image('grass.png') # Image of a grass tile + +rockbox = load_image('rockbox.png') # Image of a rock box (wall) + +metalbox = load_image('metalbox.png') # Image of a metal box + +woodbox = load_image('woodbox.png') # Image of a wood box + +flag = load_image('flag.png') # Image of flag + +bullet = load_image('bullet.png') +bullet = pygame.transform.scale(bullet, (10, 10)) +bullet = pygame.transform.rotate(bullet, -90) + +# List of image of tanks of different colors +tanks = [load_image('tank_orange.png'), load_image('tank_blue.png'),\ + load_image('tank_white.png'), + load_image('tank_yellow.png'), load_image('tank_red.png'),\ + load_image('tank_gray.png')] + +# List of image of bases corresponding to the color of each tank +bases = [load_image('base_orange.png'), load_image('base_blue.png'),\ + load_image('base_white.png'), + load_image('base_yellow.png'), load_image('base_red.png'), \ + load_image('base_gray.png')] diff --git a/init_objects.py b/init_objects.py new file mode 100644 index 0000000000000000000000000000000000000000..74e7e09431ef10fad3e9a9f58eacac5fac10b645 --- /dev/null +++ b/init_objects.py @@ -0,0 +1,89 @@ +import gameobjects +from pygame.locals import * +from pygame.color import * +import pymunk +import images +import ai +def create_flag(game_objects_list,flag_start): + """Create a flag object and add it to the list of game objects.""" + + flag = gameobjects.Flag(flag_start[0], flag_start[1]) + game_objects_list.append(flag) + flag.is_on_tank = False + flag.x, flag.y = flag_start + return flag + +def create_borders(space,current_map): + """ Create borders for the game map. """ + + static_body = space.static_body + + static_lines = [ + pymunk.Segment(static_body, + (-0.5, current_map.height), (-0.5, 0), 0.5 + ), # left + pymunk.Segment(static_body, (0, current_map.height+0.5), + (current_map.width, current_map.height+0.5), + 0.5), # lower + pymunk.Segment(static_body, + (current_map.width+0.5, 0), (current_map.width+0.5, + current_map.height), + 0.5), # right + pymunk.Segment(static_body, (0, -0.5), (current_map.width, -0.5), + 0.5) # upper + ] + static_lines[0].collision_type = 10 + static_lines[1].collision_type = 10 + static_lines[2].collision_type = 10 + static_lines[3].collision_type = 10 + space.add(*static_lines) + + +def create_background(current_map,background): + """Create a background image for the game map.""" + for x in range(0, current_map.width): + for y in range(0, current_map.height): + background.blit(images.grass,(x*images.TILE_SIZE, \ + y*images.TILE_SIZE)) + + +def create_boxes(game_objects_list , current_map, space): + """Create boxes for the game map.""" + + for x in range(0, current_map.width): + for y in range(0, current_map.height): + # Get the type of boxes + box_type = current_map.boxAt(x, y) + if(box_type != 0): + box = gameobjects.get_box_with_type(x, y, box_type, space) + game_objects_list.append(box) + +def create_tanks(tanks_list,game_objects_list,current_map,space,multi,ai_list): + """ create and manage tanks in the game. """ + + for i in range(0, len(current_map.start_positions)): + # Get the starting position of the tank "i" + pos = current_map.start_positions[i] + # Create the tank, images.tanks contains the image representing the tank + tank = gameobjects.Tank(pos[0], pos[1], pos[2], images.tanks[i], space) + # Add the tank to the list of objects to display + game_objects_list.append(tank) + # Add the tank to the list of tanks + tanks_list.append(tank) + if not multi: + for i in range(len(tanks_list)-1): + ai_list.append(ai.Ai(tanks_list[i+1], game_objects_list,\ + tanks_list, space, current_map)) + else: + for i in range(1,len(tanks_list)-1): + ai_list.append(ai.Ai(tanks_list[i+1], game_objects_list,\ + tanks_list, space, current_map)) + + +def create_bases(game_objects_list,current_map): + """ create all needed bases in the game. """ + + for i in range(0, len(current_map.start_positions)): + pos = current_map.start_positions[i] + base = gameobjects.GameVisibleObject(pos[0],pos[1],images.bases[i]) + game_objects_list.append(base) diff --git a/maps.py b/maps.py new file mode 100644 index 0000000000000000000000000000000000000000..c0841d6181076af3db479feb2c01d1b3c71483ea --- /dev/null +++ b/maps.py @@ -0,0 +1,68 @@ +import images +import pygame + + +class Map: + """ An instance of Map is a blueprint for how the game map will look. """ + + def __init__(self, width, height, boxes, start_positions, flag_position): + """ Takes as argument the size of the map (width, height), + an array with the boxes type, the start position of tanks + (start_positions) and the position of the flag (flag_position). + """ + self.width = width + self.height = height + self.boxes = boxes + self.start_positions = start_positions + self.flag_position = flag_position + + def rect(self): + """ Creates a rectangle for a specifc image. """ + + return pygame.Rect(0, 0, images.TILE_SIZE*self.width, \ + images.TILE_SIZE*self.height) + + def boxAt(self, x, y): + """ Return the type of the box at coordinates (x, y). """ + + return self.boxes[y][x] + + +map0 = Map(9, 9, + [ [0, 1, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 0, 2, 0, 2, 0, 1, 0], + [0, 2, 0, 1, 0, 1, 0, 2, 0], + [0, 0, 0, 1, 0, 1, 0, 0, 0], + [1, 1, 0, 3, 0, 3, 0, 1, 1], + [0, 0, 0, 1, 0, 1, 0, 0, 0], + [0, 2, 0, 1, 0, 1, 0, 2, 0], + [0, 1, 0, 2, 0, 2, 0, 1, 0], + [0, 1, 0, 0, 0, 0, 0, 1, 0] ], + [[0.5, 0.5, 0], [8.5, 0.5, 0], + + [0.5, 8.5, 180], [8.5, 8.5, 180]], [4.5, 4.5]) + +map1 = Map(15, 11, + [ [ 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0], + [ 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0], + [ 0, 1, 0, 3, 1, 1, 0, 0, 0, 1, 1, 3, 0, 1, 0], + [ 0, 2, 0, 0, 3, 0, 0, 2, 0, 0, 3, 0, 0, 2, 0], + [ 2, 1, 0, 1, 1, 0, 1, 3, 1, 0, 1, 1, 0, 1, 2], + [ 1, 1, 3, 0, 3, 2, 3, 0, 3, 2, 3, 0, 3, 1, 1], + [ 2, 1, 0, 1, 1, 0, 1, 3, 1, 0, 1, 1, 0, 1, 2], + [ 0, 2, 0, 0, 3, 0, 0, 2, 0, 0, 3, 0, 0, 2, 0], + [ 0, 1, 0, 3, 1, 1, 0, 0, 0, 1, 1, 3, 0, 1, 0], + [ 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0], + [ 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0] ], + [[0.5, 0.5, 0], [14.5, 0.5, 0], [0.5, 10.5, 180], + + [14.5, 10.5, 180], [7.5, 0.5, 0], [7.5, 10.5, 180]], [7.5, 5.5]) + +map2 = Map(10, 5, + [ [ 0, 2, 0, 2, 0, 0, 2, 0, 2, 0 ], + [ 0, 3, 0, 1, 3, 3, 1, 0, 3, 0 ], + [ 0, 1, 0, 1, 0, 0, 1, 0, 1, 0 ], + [ 0, 3, 0, 1, 3, 3, 1, 0, 3, 0 ], + [ 0, 2, 0, 2, 0, 0, 2, 0, 2, 0 ] ], + + [[0.5, 2.5, 270], [9.5, 2.5, 90]], [5, 2.5] ) diff --git a/menu.py b/menu.py new file mode 100644 index 0000000000000000000000000000000000000000..a8c24c24016c953311c974f573815628bfea0837 --- /dev/null +++ b/menu.py @@ -0,0 +1,106 @@ +import pygame +import images +from pygame.locals import * +import maps + + +def main_menu(): + """ + Displays the main menu screen, allowing the user to select options for + game play (singleplayer or multiplayer), map, fog, and game mode + (score limit, time limit, or round limit). """ + + multiplayer = 1 + mapp = 2 + fog = 3 + game_mode = 4 + return_ = 5 + + def get_text(var): + """ Returns the text to be displayed for each option in the main menu. + """ + + white = (255,255,255) + font = pygame.font.SysFont('Corbel',35) + + if var == multiplayer : + text1 = font.render('play:' , True , white) + text2 = font.render('Capture the Flag!' , True , white) + text3 = font.render('singleplayer: press 1!' , True , white) + text4 = font.render('multiplayer: press 2!' , True , white) + elif var == mapp : + text1 = font.render('Choose a map:' , True , white) + text2 = font.render('Capture the Flag!' , True , white) + text3 = font.render('Map1, Map2 or Map3' , True , white) + text4 = font.render('press: 1 , 2 or 3 ' , True , white) + elif var == fog : + text1 = font.render('Choose fog:' , True , white) + text2 = font.render('Capture the Flag!' , True , white) + text3 = font.render('With fog: press 1' , True , white) + text4 = font.render('Without fog: press 2' , True , white) + elif var == game_mode : + text1 = font.render('Choose a game mode:' , True , white) + text2 = font.render('Capture the Flag!' , True , white) + text3 = font.render('Score limit, Time limit or Round limit' ,\ + True , white) + text4 = font.render('press: 1, 2 or 3 ' , True , white) + + return text1,text2,text3,text4 + + def print_on_screen(lst): + """ prints the text on the screen """ + + green = (80,200,80) + width = 600 + height = 600 + screen = pygame.display.set_mode((width,height)) + screen.fill(green) + screen.blit(lst[0], (width/6,height/3)) + screen.blit(lst[1], (width/6,height/6)) + screen.blit(lst[2], (width/6,height/3+30)) + screen.blit(lst[3], (width/6,height/3+70)) + pygame.display.update() + + return_list = [] + state = 1 + while True: + + print_on_screen(get_text(state)) + + for ev in pygame.event.get(): + if ev.type == KEYDOWN: + temp = None + if ev.key == K_ESCAPE: + pygame.quit() + if ev.key == K_1: + if state == multiplayer : + temp = False + elif state == mapp : + temp = maps.map0 + elif state == fog : + temp = True + elif state == game_mode : + temp = 1 + + elif ev.key == K_2: + if state == multiplayer : + temp = True + elif state == mapp : + temp = maps.map1 + elif state == fog : + temp = False + elif state == game_mode : + temp = 2 + + elif ev.key == K_3: + if state == mapp : + temp = maps.map2 + elif state == game_mode : + temp = 3 + + if temp != None: + return_list.append(temp) + state = state + 1 + + if state == return_ : + return return_list diff --git a/print_functions.py b/print_functions.py new file mode 100644 index 0000000000000000000000000000000000000000..489bb0243efe4b129adcc0d79aa47631da5c5110 --- /dev/null +++ b/print_functions.py @@ -0,0 +1,70 @@ +import pygame +import pymunk +from pygame.locals import * +from pygame.color import * +import images + +font1 = pygame.font.SysFont('Corbel',20) +font2 = pygame.font.SysFont('Corbel',35) +white = (255,255,255) + + +def print_stats_time(screen,var, current_map): + """ Print the current time left on the game screen. """ + + text1 = font1.render("Time left:"+str(((1800*5-var)//60)) , True , white) + screen.blit(text1, (current_map.width*40+18,current_map.height*18)) + + +def print_counting_score_console(tanks_list): + """ Print the score of retrived flags in the console""" + + player = 1 + for i in tanks_list: + print('Player',player,": ",i.points) + player = player+1 + +def print_score_result(screen, tanks_list): + """ Print the current score of retrived flags for all tanks over the whole + screen before a new round is started """ + + width = 300 + height = 300 + # create all labels! + while True: + for ev in pygame.event.get(): + if ev.type == KEYDOWN: + if ev.key == K_1: + return + text2 = font2.render('Press 1 to countinue ' , True , white) + player = 1 + screen.blit(images.grass,(200, 200)) + for i in tanks_list: + text1 = font2.render("Player"+str(player)+":"+str(i.points), \ + True , white) + screen.blit(text1, (width/2-120,height/6+player*50)) + player = player +1 + screen.blit(text2, (width/2-120,height/6-40)) + text3 = font2.render('Result:' , True , white) + screen.blit(text3, (width/2-120,height/6)) + pygame.display.update() + + +def print_stats_score(screen, tanks_list , current_map): + """ Print the current score of each tank on the game screen """ + + player = 1 + for i in tanks_list: + text1 = font1.render(('Player'+str(player)+": "+str(i.points)) , \ + True , white) + screen.blit(text1,(current_map.width*40+18,\ + current_map.height*15+player*15)) + player = player+1 + + + +def print_stats_round(screen, var , current_map): + """ Print the round number on the game screen. """ + + text1 = font1.render("round nr:"+str(var) , True , white) + screen.blit(text1, (current_map.width*40+18,current_map.height*18))