class DIRECTION(Enum): HAUT = Vector(0, -1) BAS = Vector(0, 1) DROITE = Vector(1, 0) GAUCHE = Vector(-1, 0) NONE = Vector(0, 0) @classmethod def opposite(cls, direction): #renvoie la direction opposée, ou lève une erreur try: if direction == cls.HAUT: return cls.BAS elif direction == cls.BAS: return cls.HAUT elif direction == cls.GAUCHE: return cls.DROITE elif direction == cls.DROITE: return cls.GAUCHE elif direction == cls.NONE: return cls.NONE else: raise ValueError("le vecteur n'est pas une direction") except ValueError: raise ValueError
def update(self): super().update() self.update_timers() if self.jump_cd > 0: self.jump_cd -= 1 if self.move_cd > 0: self.move_cd -= 1 if self.state == ActorSlime.State.JUMP: if self.jump_in: can_move = self.move(self.jump_vect_in.x * self.jump_velocity, self.jump_vect_in.y * self.jump_velocity) if not can_move: self.state = ActorSlime.State.IDLE self.jump_cd = self.jump_cd_max if self.jump_vect_in.x * ( self.jump_target_pos.center_real[0] - self.rect. center_real[0]) < 0 and self.jump_vect_in.y * ( self.jump_target_pos.center_real[1] - self.rect.center_real[1]) < 0: self.jump_in = False theta = random.random() * 2 * math.pi self.jump_vect_out = Vector( self.rect.x + 100 * math.cos(theta) - self.rect.center_real[0], self.rect.y + 300 * math.sin(theta) - self.rect.center_real[1]) self.jump_vect_out.normalize() self.jump_return_pos = Rect( self.rect.x + 100 * math.cos(theta), self.rect.y + 300 * math.sin(theta), 0, 0) else: self.jump_cd = self.jump_cd_max self.state = ActorSlime.State.IDLE elif self.state == ActorSlime.State.MOVE: can_move = self.move(self.move_vect.x * self.move_velocity, self.move_vect.y * self.move_velocity) self.move_count += 1 if not can_move or self.move_count >= self.move_count_max: self.state = ActorSlime.State.IDLE self.move_cd = self.move_cd_max target = self.map.get_closest_ennemi( self.rect, range=self.detection_range, ennemi_team=self.team.get_ennemi()) if self.can_attack() and target is not None and self.allowed_attack: if self.can_shoot(target): self.shoot(target) elif self.can_jump(target): self.jump(target) elif self.can_move(): self.move_it() elif self.can_move(): self.move_it()
def jump(self, target): self.state = ActorSlime.State.JUMP self.jump_initial_pos = copy.deepcopy(self.rect) self.jump_return_pos = copy.deepcopy(target.rect) self.jump_target_pos = copy.deepcopy(target.rect) self.jump_return_pos.x += 300 self.jump_count = 0 self.jump_in = True self.jump_vect_in = Vector( target.rect.center_real[0] - self.rect.center_real[0], target.rect.center_real[1] - self.rect.center_real[1]) self.jump_vect_in.normalize()
def __init__(self): super().__init__() self.state = ActorSlime.State.IDLE self.team = EnumTeam.MONSTER_TEAM self.allowed_attack = False self.shoot_range = 500 self.detection_range = 1000 # Distance à laquelle il perçoit un ennemi self.jump_range = 700 self.jump_cd = 0 self.jump_cd_max = 200 self.jump_theta = 0 self.jump_in = True self.jump_count = 0 self.jump_count_max = 30 self.jump_initial_pos = None self.jump_velocity = 12 self.ammo_max = 3 # Le nombre de balles self.ammo = self.ammo_max # Le nombre de balles max self.f = 50 # vie du slime self.collidable = True self.should_update = True self.velocity = Vector(0, 0)
def execute(self, command): """ Gère les entrées dans l'invite de commande""" commands = command.split( sep=" " ) # sépare l'entrée en une liste ( escpaces supprimés, remplacés par les ',' de la liste) if commands[0] == "debug": pass elif commands[0] == "hitbox": self.draw_hit_box = not self.draw_hit_box elif commands[0] == "print": print(self.__getattribute__(commands[1])) print( self.__getattribute__(commands[1]).__getattribute__( commands[2])) elif commands[0] == "menu" or commands[ 0] == "quit": #permet de revenir au menu via l'invite de commande if pygame.mixer.music.get_busy(): pygame.mixer.music.stop( ) # On éteint la music pour permettre à celle du menu de se lancer (car vérifie si une musique est self.state = StageState.QUIT # en trainde jouer ou non) StageManager().push(game.stages.StageMainMenu.StageMainMenu()) elif commands[0] == "tp": event = pygame.event.Event(pygame.USEREVENT, name=EVENT_TP, map_name=commands[1], spawn_pos=Vector(700, 700), actor=self.map.get_actor(ActorPlayer)) pygame.event.post(event) elif commands[0] == "invincible": self.map.get_actor(ActorPlayer).invicible = not self.map.get_actor( ActorPlayer).invicible
def detect_target_position(self, target): """Renvoie le vecteur (target.rect.center - self.rect.center) pour donner la direction où aller/tirer""" pos = Vector(target.rect.center[0] - self.rect.center[0], target.rect.center[1] - self.rect.center[1]) return pos
def interact(self, actor): if self.state != ActorBomb.State.EXPLODE: if isinstance(actor, ActorArrow): self.velocity = Vector(actor.speed * math.cos(actor.dir), actor.speed * math.sin(actor.dir)) return True return False
def handle_mouse_button_down(self, pos, button): try: self.state = StageState.QUIT if self.tile_collidable: self.tile_picked = ActorTileCollidable( self.tileset_files[self.tileset_no], Vector(self.grid.get_pos_x(pos[0]), self.grid.get_pos_y(pos[1])), self.grid.width, self.grid.height) else: self.tile_picked = ActorTile( self.tileset_files[self.tileset_no], Vector(self.grid.get_pos_x(pos[0]), self.grid.get_pos_y(pos[1])), self.grid.width, self.grid.height) return True except ValueError: self.info("Tu n'as pas cliqué sur une image!")
def __init__(self): super().__init__() self.mouse_pos = Vector(0, 0) self.object_pick = None self.is_paused = True self.mode = EDIT_MODE.PICK self.grid = Grid2() #grille pour mieux positionner acteurs self.draw_hit_box = False
def destroy(self): self.speed = 0 self.state = ActorArrowChargedPlayer.State.EXPLODE event = pygame.event.Event(pygame.USEREVENT, name=EVENT_EXPLOSION, pos=Vector(self.rect.centerx, self.rect.centery), radius=self.radius, team=self.team, damage=self.damage) pygame.event.post(event) self.team = EnumTeam.NEUTRAL_TEAM
def explode(self): rect_tmp = self.rect self.state = ActorBomb.State.EXPLODE self.rect.center = rect_tmp.center event = pygame.event.Event(pygame.USEREVENT, name=EVENT_EXPLOSION, pos=Vector(self.rect.centerx, self.rect.centery), radius=self.radius, team=self.team, damage=50) pygame.event.post(event)
def __init__(self, map_name="level_0", spawn_pos_x=700, spawn_pos_y=650, direction=DIRECTION.BAS): super().__init__(False) self.is_open = False self.map_name = map_name self.spawn_pos = Vector(spawn_pos_x, spawn_pos_y) self.sprites = {} self.animation = None self.direction = direction self.reload()
def __init__(self, dir=DIRECTION.NONE, velocity=VECTOR_NULL): super().__init__() self.damage = 20 try: self.dir = dir.get_theta() except: self.dir = dir.value.get_theta() self.speed = 8 self.velocity = Vector(0, 0) if math.cos(self.dir) * velocity.x >= 0: self.velocity.x = velocity.x / 2 # Hé oui, les vitesses ne sont pas relativistes à cet ordre de grandeur, 2 + x2 = 4 if math.sin(self.dir) * velocity.y >= 0: self.velocity.y = velocity.y / 2 self.should_update = True self.draw_shadow = True self.collidable = True self.h = 20
def reload(self): super().reload() self.handle_event = True self.should_update = True self.etre_vivant = True self.allowed_attack = False # Permet de ne pas tirer dès le début self.shoot_range = 500 self.shoot_rate = 1000 # Période des tirs : en ms self.detection_range = 1000 # Distance à laquelle il perçoit un ennemi self.jump_range = 700 self.jump_cd = 0 self.jump_cd_max = 400 self.jump_theta = 0 #angle définissant le jump self.jump_in = True self.jump_count = 0 self.jump_count_max = 30 self.jump_initial_pos = None self.jump_velocity = 12 #vitesse du saut self.theta = 0 self.ammo_max = 3 # Le nombre de balles self.ammo = self.ammo_max # Le nombre de balles max self.hp = 50 # vie su slime self.move_cd = 0 self.move_cd_max = 125 self.move_vect = None self.move_count = 0 self.move_count_max = 75 self.move_velocity = 2 self.collidable = True self.should_update = True self.velocity = Vector(0, 0) self.add_timer(Timer(2000, self.allow_attack))
def move_it(self): theta = random.random() * 2 * math.pi self.move_vect = Vector(150 * math.cos(theta), 150 * math.sin(theta)) self.move_vect.normalize() self.state = ActorSlime.State.MOVE self.move_count = 0
from game.utils.Vector import Vector PLAYER_SPRITE_WIDTH = 64 PLAYER_SPRITE_HEIGHT = 64 PLAYER_MOVE_TOP = Vector(1, 8) PLAYER_MOVE_LEFT = Vector(1, 9) PLAYER_MOVE_BOTTOM = Vector(1, 10) PLAYER_MOVE_RIGHT = Vector(1, 11) PLAYER_DYING = Vector(1, 20) PLAYER_MOVE_TILES_NUMBER = 8 PLAYER_DIE_TILES_NUMBER = 6 PLAYER_MOVE_TIME = 70 PLAYER_DYING_TIME = 300 PLAYER_STANDBY = Vector(1, 10) PLAYER_WIDTH = 48 PLAYER_HEIGHT = 50 PLAYER_DEPTH = 20 WINDOW_WIDTH = 1408 WINDOW_HEIGHT = 832 EVENT_TP = "EVENT_TELEPORT" # Evenement défini par : # map_name: le nom de la Map à charger # actor: l'Actor à téléporter # spawn_pos: Un Vector représentant la position de l'Actor à téléporter. EVENT_PLAYER_INTERACT = "EVENT_PLAYER_INTERACT" # Evenement défini par : # actor: L'Actor qui demande l'intéraction
class ActorSlime(ActorAlive, ActorAnimation): """ Un ennemi (slime en l'occurence) qui a plusieurs animations selon qu'il soit imobile, attaquant , mourant ou en déplacement""" ID = 32 NAME = "SLIME" class State(EnumAuto): IDLE = () ATTACK = () JUMP = () MOVE = () DIE = () WIDTH = 128 HEIGHT = 128 FILE = "assets/slime_blue_128.png" def __init__(self): super().__init__() self.state = ActorSlime.State.IDLE self.team = EnumTeam.MONSTER_TEAM self.allowed_attack = False self.shoot_range = 500 self.detection_range = 1000 # Distance à laquelle il perçoit un ennemi self.jump_range = 700 self.jump_cd = 0 self.jump_cd_max = 200 self.jump_theta = 0 self.jump_in = True self.jump_count = 0 self.jump_count_max = 30 self.jump_initial_pos = None self.jump_velocity = 12 self.ammo_max = 3 # Le nombre de balles self.ammo = self.ammo_max # Le nombre de balles max self.f = 50 # vie du slime self.collidable = True self.should_update = True self.velocity = Vector(0, 0) def reload(self): super().reload() self.handle_event = True self.should_update = True self.etre_vivant = True self.allowed_attack = False # Permet de ne pas tirer dès le début self.shoot_range = 500 self.shoot_rate = 1000 # Période des tirs : en ms self.detection_range = 1000 # Distance à laquelle il perçoit un ennemi self.jump_range = 700 self.jump_cd = 0 self.jump_cd_max = 400 self.jump_theta = 0 #angle définissant le jump self.jump_in = True self.jump_count = 0 self.jump_count_max = 30 self.jump_initial_pos = None self.jump_velocity = 12 #vitesse du saut self.theta = 0 self.ammo_max = 3 # Le nombre de balles self.ammo = self.ammo_max # Le nombre de balles max self.hp = 50 # vie su slime self.move_cd = 0 self.move_cd_max = 125 self.move_vect = None self.move_count = 0 self.move_count_max = 75 self.move_velocity = 2 self.collidable = True self.should_update = True self.velocity = Vector(0, 0) self.add_timer(Timer(2000, self.allow_attack)) def allow_attack(self, *arks, **kwargs): self.allowed_attack = True def update(self): super().update() self.update_timers() if self.jump_cd > 0: self.jump_cd -= 1 if self.move_cd > 0: self.move_cd -= 1 if self.state == ActorSlime.State.JUMP: if self.jump_in: can_move = self.move(self.jump_vect_in.x * self.jump_velocity, self.jump_vect_in.y * self.jump_velocity) if not can_move: self.state = ActorSlime.State.IDLE self.jump_cd = self.jump_cd_max if self.jump_vect_in.x * ( self.jump_target_pos.center_real[0] - self.rect. center_real[0]) < 0 and self.jump_vect_in.y * ( self.jump_target_pos.center_real[1] - self.rect.center_real[1]) < 0: self.jump_in = False theta = random.random() * 2 * math.pi self.jump_vect_out = Vector( self.rect.x + 100 * math.cos(theta) - self.rect.center_real[0], self.rect.y + 300 * math.sin(theta) - self.rect.center_real[1]) self.jump_vect_out.normalize() self.jump_return_pos = Rect( self.rect.x + 100 * math.cos(theta), self.rect.y + 300 * math.sin(theta), 0, 0) else: self.jump_cd = self.jump_cd_max self.state = ActorSlime.State.IDLE elif self.state == ActorSlime.State.MOVE: can_move = self.move(self.move_vect.x * self.move_velocity, self.move_vect.y * self.move_velocity) self.move_count += 1 if not can_move or self.move_count >= self.move_count_max: self.state = ActorSlime.State.IDLE self.move_cd = self.move_cd_max target = self.map.get_closest_ennemi( self.rect, range=self.detection_range, ennemi_team=self.team.get_ennemi()) if self.can_attack() and target is not None and self.allowed_attack: if self.can_shoot(target): self.shoot(target) elif self.can_jump(target): self.jump(target) elif self.can_move(): self.move_it() elif self.can_move(): self.move_it() def can_move(self): return self.state == ActorSlime.State.IDLE and self.move_cd == 0 def move_it(self): theta = random.random() * 2 * math.pi self.move_vect = Vector(150 * math.cos(theta), 150 * math.sin(theta)) self.move_vect.normalize() self.state = ActorSlime.State.MOVE self.move_count = 0 def can_attack(self): return self.state == ActorSlime.State.IDLE def get_distance(self, target): return (self.rect.x - target.rect.x)**2 + (self.rect.y - target.rect.y)**2 def can_shoot(self, target): return self.get_distance( target) <= self.shoot_range**2 and self.ammo > 0 def can_jump(self, target): return self.get_distance( target) <= self.jump_range**2 and self.jump_cd == 0 def jump(self, target): self.state = ActorSlime.State.JUMP self.jump_initial_pos = copy.deepcopy(self.rect) self.jump_return_pos = copy.deepcopy(target.rect) self.jump_target_pos = copy.deepcopy(target.rect) self.jump_return_pos.x += 300 self.jump_count = 0 self.jump_in = True self.jump_vect_in = Vector( target.rect.center_real[0] - self.rect.center_real[0], target.rect.center_real[1] - self.rect.center_real[1]) self.jump_vect_in.normalize() def shoot(self, target): self.state = ActorSlime.State.ATTACK arrow = ActorArrowSlime(self.detect_target_position(target)) arrow.team = self.team arrow.rect.x = self.rect.x + (self.rect.w - arrow.rect.w) / 2 arrow.rect.y = self.rect.y + (self.rect.h - arrow.rect.w) / 2 self.map.add_actor(arrow) self.ammo -= 1 if self.ammo == 0: self.reload_ammo() def reload_ammo(self): self.add_timer(Timer(2500, self.reload_ammo_callback)) def reload_ammo_callback(self): self.ammo = self.ammo_max def idle(self): if not self.is_dead: self.state = ActorSlime.State.IDLE def die(self): self.collidable = False self.state = ActorSlime.State.DIE def dead(self): nb_slime = 0 for actor in self.map.actors: if isinstance(actor, ActorSlime): nb_slime += 1 if nb_slime == 1: for actor in self.map.actors: if isinstance(actor, ActorDoor) or isinstance( actor, ActorDoorWin): actor.open() self.map.remove_actor(self) del self def turn_on_shoot(self): print("turn on shoor appellé") self.can_shoot = True def detect_target_position(self, target): """Renvoie le vecteur (target.rect.center - self.rect.center) pour donner la direction où aller/tirer""" pos = Vector(target.rect.center[0] - self.rect.center[0], target.rect.center[1] - self.rect.center[1]) return pos def load_sprite(self): super().load_sprite() sprite_sheet = load_image(type(self).FILE, False) width = type(self).WIDTH height = type(self).HEIGHT self.animations = {} self.animations[ActorSlime.State.IDLE] = Animation( sprite_sheet, pygame.Rect(0, 0, width, height), 9, 50, True) self.animations[ActorSlime.State.MOVE] = Animation( sprite_sheet, pygame.Rect(0, height, width, height), 9, 100, True) self.animations[ActorSlime.State.JUMP] = Animation( sprite_sheet, pygame.Rect(0, height * 2, width, height), 9, 50, True) self.animations[ActorSlime.State.ATTACK] = Animation( sprite_sheet, pygame.Rect(0, height * 3, width, height), 9, 50, True, callback_fun=self.idle) self.animations[ActorSlime.State.DIE] = Animation( sprite_sheet, pygame.Rect(0, height * 4, width, height), 9, 50, True, callback_fun=self.dead) @property def animation(self): return self.animations[self.state] @property def is_dead(self): return self.hp <= 0 def interact(self, actor): return super().interact(actor)