class Game(): def __init__(self): pyxel.init(240, 240, caption='Bourne Again', fps=30) pyxel.mouse(True) pyxel.load('stuff.pyxres') self.seemsg = '' self.seemsg_iter = '' self.seemsg_out = '' self.player = Player(30, 120) self.beam_angle = None self.inputed_angle = None self.beam_start_time = 0 #self.wll_index = 0 self.targets = { 'A': Target(200, 120, 'Target A feels lonely'), 'B': Target(1, 20, 'Fortune awaits Target B'), 'C': Target(120, 199, 'Target C is in the mood for shawarma') } self.walls = [Solid(150, 120), Solid(100, 45), Solid(24, 200)] self.flag = Flag(60, 24) self.powerup = Powerup(80, 120) self.upgraded = False self.blocked_functions = ['up', 'down', 'left'] self.locked = False self.has_won = False self.raycast_distance = 0 self.raycast_has_collided = False pyxel.playm(1, loop=True) pyxel.run(self.update, self.draw) def update(self): if pyxel.btnp(pyxel.KEY_ESCAPE): pyxel.quit() if pyxel.btnp(pyxel.KEY_W): self.has_won = True if not self.locked and pyxel.btnr(pyxel.KEY_SPACE): script = INPUT_PREAMBLE + self.read_input() self.reset_vars() try: self.locked = True if (self.upgraded or (not self.upgraded and not any(bf in script for bf in self.blocked_functions))): exec(script) except signals.Help: self.set_msg( signals.HELP if self.upgraded else signals.START_HELP) except signals.Credits: self.set_msg(signals.CREDITS) except signals.See as see: """Start typing out info on the named target.""" if see.msg in self.targets: tgt: Target = self.targets[see.msg] self.set_msg( f'{see.msg} ({tgt.x}, {tgt.y}) - Relative to you: ({tgt.x-self.player.x}, {tgt.y-self.player.y})\n{tgt.comment}' ) else: self.set_msg(f'{see.msg}: no such target') except signals.Right as right: self.player.deltax = right.dist except signals.Left as left: self.player.deltax = -left.dist except signals.Up as up: self.player.deltay = -up.dist except signals.Down as down: self.player.deltay = down.dist except signals.Shoot as shot: self.inputed_angle = shot.angle % 360 self.beam_angle = radians(-shot.angle) self.beam_start_time = pyxel.frame_count pyxel.play(3, 0) except: pass self.move_player() if len(self.seemsg) > 0: self.typeout() def draw(self): pyxel.cls(12 if self.upgraded else 13) # targets [self.draw_target(k, t) for k, t in self.targets.items()] # walls [self.draw_wall(w) for w in self.walls] # powerup if not self.upgraded: pyxel.blt(self.powerup.x, self.powerup.y, 0, 48, 0, 16, 16, 0) # player pyxel.blt(self.player.x, self.player.y, 0, *self.player.sprite(), 16, 16, 0) # shot beam if self.beam_angle is not None: self.draw_beam(self.beam_angle, self.beam_start_time) # flag pyxel.blt(self.flag.x, self.flag.y, 0, *self.flag.step_anim(pyxel.frame_count), 16, 16, 0) # see() text background for l, txt in enumerate(self.seemsg_out.splitlines()): pyxel.rect(0, 6 * l, 4 * len(txt) + 2, 7, 1) # see() text pyxel.text(1, 1, self.seemsg_out, 7) # exec prompt if not self.locked: pyxel.rect(0, 232, 240, 8, 0) pyxel.text(0, 234, '[SPACE] run INPUT', 7) if self.has_won: pyxel.cls(0) pyxel.blt(78, 64, 0, 0, 32, 84, 20, 0) pyxel.text(20, 202, 'Bourne Again\nGGJ 2020\nrun credits()', 9) def read_input(self): """Get all the text from the INPUT file.""" f = open('INPUT', 'r') script = f.read() f.close() return script def reset_vars(self): """Clean up script execution state for the next run.""" self.seemsg = '' self.seemsg_iter = '' self.seemsg_out = '' def move_player(self): """Move the player step by step to the new position.""" # block move if player would collide if ((self.player.deltax > 0 and self.player_will_collide(1, 0)) or (self.player.deltax < 0 and self.player_will_collide(-1, 0)) or (self.player.deltay > 0 and self.player_will_collide(0, 1)) or (self.player.deltay < 0 and self.player_will_collide(0, -1))): self.player.stop() else: self.player.update_pos() if self.player.stopped(): self.locked = False self.player.reset_anim() else: if pyxel.frame_count % 6 == 0: self.player.step_anim() if self.player_touching_powerup(): if not self.upgraded: msg = """Learned: up(distance) down(distance) left(distance) """ self.set_msg(msg) self.upgraded = True self.has_won = self.player_touching_flag() if self.has_won: self.player.stop() def set_msg(self, msg: str): """Change the see() message.""" self.seemsg = msg self.seemsg_iter = iter(self.seemsg) def typeout(self): """Type out see() text one letter at a time.""" if len(self.seemsg_out) < len(self.seemsg): self.locked = True pyxel.play(3, 1) self.seemsg_out += next(self.seemsg_iter) else: self.locked = False def draw_target(self, name: str, tgt: Target): """Draw the given Target. Put its name in the corner.""" pyxel.blt(tgt.x, tgt.y, 0, 32, 0, 16, 16, 0) pyxel.text(tgt.x, tgt.y + 11, name, 0) def draw_beam(self, angle: float, starttime: int): """Draw a beam at the angle for both eyes.""" eye1 = (self.player.x + 5, self.player.y + 4) eye2 = (self.player.x + 10, self.player.y + 4) self.raycast_has_collided = self.detect_raycast_colision(eye1, eye2) elapsed_frames = pyxel.frame_count - starttime color = 10 if 3 <= elapsed_frames < 6: color = 11 elif 6 <= elapsed_frames < 9: color = 3 elif 9 <= elapsed_frames < 12: color = 1 elif elapsed_frames >= 12: self.beam_angle = None self.locked = False return if self.raycast_has_collided: pyxel.line(eye1[0], eye1[1], eye1[0] + self.raycast_distance * cos(self.beam_angle), eye1[1] + self.raycast_distance * sin(self.beam_angle), color) pyxel.line(eye2[0], eye2[1], eye2[0] + self.raycast_distance * cos(self.beam_angle), eye2[1] + self.raycast_distance * sin(self.beam_angle), color) self.raycast_has_collided = False else: pyxel.line(eye1[0], eye1[1], eye1[0] + 1000 * cos(self.beam_angle), eye1[1] + 1000 * sin(self.beam_angle), color) pyxel.line(eye2[0], eye2[1], eye2[0] + 1000 * cos(self.beam_angle), eye2[1] + 1000 * sin(self.beam_angle), color) def draw_wall(self, sld: Solid): pyxel.blt(sld.x, sld.y, 0, 0, 16, 16, 16, 0) def player_will_collide(self, xdir, ydir): """Will the player enter a solid thing if they move in the given direction?""" out: bool = False # boundaries if (self.player.x + xdir + 14 >= 240 or self.player.x + xdir <= 0 or self.player.y + ydir + 15 >= 240 or self.player.y + ydir <= 0): return True # walls for w in self.walls: if (self.player.x + xdir + 14 >= w.x and self.player.x + xdir <= w.x + 14 and self.player.y + ydir + 15 >= w.y and self.player.y + ydir <= w.y + 15): out = True return out def player_touching_flag(self): """Is the player touching the flag?""" if (self.player.x + 9 >= self.flag.x and self.player.x <= self.flag.x + 9 and self.player.y + 16 >= self.flag.y and self.player.y <= self.flag.y + 16): return True return False def player_touching_powerup(self): """Is the player touching a powerup?""" if (self.player.x + 9 >= self.powerup.x and self.player.x <= self.powerup.x + 9 and self.player.y + 11 >= self.powerup.y and self.player.y <= self.powerup.y + 11): return True def detect_raycast_colision(self, eye1, eye2): wll_index = 0 for wll in self.walls: for e in [eye1, eye2]: x = wll.x - e[0] y = e[1] - wll.y for xt in range(x, x + 16): for yt in range(y - 16, y): if (self.inputed_angle == 90 or self.inputed_angle == 270): if (self.inputed_angle == 90): if (xt == 0 and yt > e[1]): self.raycast_distance = wll.y - e[1] #print("hit down") #self.walls.pop(wll_index) return True if (self.inputed_angle == 270): if (xt == 0 and yt < e[1]): self.raycast_distance = wll.y - e[1] #print("hit up") #self.walls.pop(wll_index) return True if (self.inputed_angle == 180 or self.inputed_angle == 0): if (self.inputed_angle == 0): if (yt == 0 and yt < e[1]): #print(f'is horizontal {xt}') if (yt == 0): self.raycast_distance = wll.x - e[0] #print("hit right") #self.walls.pop(wll_index) return True if (self.inputed_angle == 180): if (yt == 0 and yt > e[1]): #print(f'is horizontal {xt}') if (yt == 0): self.raycast_distance = wll.x - e[0] #print("hit left") self.walls.pop(wll_index) return True else: angle_value = tan(abs(self.beam_angle)) #print(angle_value) if (yt - 1 <= (angle_value * xt)) and (yt + 1 >= (angle_value * xt)): #print("distance standard") self.raycast_distance = sqrt(( (wll.x - e[0])**2) + ((e[1] - wll.y)**2)) #print("hit") #self.walls.pop(wll_index) return True #wll_index+=1 return False