def hit_wall(x, y): return ( state.board.outside(x, y) or is_wall(state.board.get(x, y)) or dist(state.player.pos, (x, y)) > state.max_range or is_door(state.board.get(x, y)) )
def update(self, state, end_fn): enemies = sorted( [ e for e in state.enemies if e.square in state.visible and dist(state.player.pos, e.pos) < 5 ], key=lambda e: dist(e.pos, state.player.pos), ) if not enemies: state.text_box = misc.TextBox( "thunder", "There is no one in range" ) target = None else: target = random.choice(enemies) state.active_tool = None if target: state.player.thunder(state, state.player, target, end_fn)
def rwalk(a, b): d = int(dist(a, b) / CELL_SIZE) + 2 path = list(tween.tween(a, b, d)) def _distort(p): x, y = p delta = random.gauss(0, CELL_SIZE / 3) nx, ny = normalize((y, -x)) dx = nx * delta dy = ny * delta return x + dx, y + dy return [_distort(p) for p in path[:-1]] + [path[-1]]
def _apply_damage(source, delay): damage = 1 e2.hurt(damage, DType.THUNDER) ppos = state.to_pixel(e2.pos, CELL_SIZE) state.particles.append(DamageText(f"-{damage}", ppos, 12)) pyxel.play(2, 50) near = [ e for e in state.enemies if dist(e.pos, e2.pos) < 3 and e not in touched ] nt = touched | set(e for e in near) for n in near: self.thunder(state, e2, n, end_fn, nt) if not near: self.wait(delay, end_fn)
def take_action(self, state: State, end_turn_fn) -> ActionReport: self.state_ref = state if not self.square in state.visible: return self.wait(1, end_turn_fn) elif not self.met_already: pyxel.playm(1, loop=True) self.met_already = True skels = [e for e in state.enemies if e.parent == self] can_invoke = not skels and self.cooldown_spawn < 1 if not skels: self.sprite.stop() self.cooldown_spawn -= 1 elif not self.should_tp: return self.wait(1, end_turn_fn) self.cooldown_shoot -= 1 if self.should_tp: pos = state.player.pos while dist(pos, state.player.pos) < 4: pos = self.pick_free_spot(state) for _ in range(50): state.particles.append( BossMolecule(_center(self.pos), _center(pos), TPV)) self.sprite = self._teleport_sprite self.should_tp = False return self.move(*pos, end_turn_fn, TPV) elif can_invoke: self.cooldown_spawn = 4 self.sprite = self._invoke_sprite self.sprite.play() pyxel.play(3, 3) return self.wait(16, partial(self._do_spawn, state, end=end_turn_fn)) elif self.cooldown_shoot < 1: self.cooldown_shoot = 2 return self.shoot(state, state.player, end_turn_fn) return random_move(state, self, end_turn_fn)
def straight_line(state: State, e: AIActor, end_turn) -> ActionReport: possible = [ n for n in state.board.neighbours(*e.pos) if can_walk(state.board, *n) and n not in state.occupied ] if e.square in state.visible and possible: possible = sorted(possible, key=lambda x: dist(x, state.player.square)) if possible[0] == state.player.square: return e.attack(state.player, end_turn) else: x, y = possible[0] e.move(x, y, end_turn) return x, y else: if possible: x, y = random.choice(possible) e.move(x, y, end_turn, 1) return x, y else: e.wait(10, end_turn) return None
def __init__(self, start, end, callback=None): speed = 1 / 15 d = dist(start, end) self._path = list(tween.tween(start, end, int(speed * d * FPS))) self._callback = callback
def update(state: State) -> State: x, y = state.player.pos deads_enemies = [] for e in state.enemies: e.update(state) if e.pv < 1: deads_enemies.append(e) for d in deads_enemies: state.enemies.remove(d) if state.text_box is not None: state.text_box.update(state) return elif state.player_turn: player_action(state) else: game_turn(state) state.player.update(state) # if state.player.pv < 1: # state.text_box = misc.TextBox("skull", "You are dead...") # Move camera if needed px, py = state.player.pos cx, cy = state.camera lthreshold = 6 rthreshold = 9 cx = px - lthreshold if px - cx < lthreshold else cx cx = px - rthreshold if px - cx > rthreshold else cx cy = py - lthreshold if py - cy < lthreshold else cy cy = py - rthreshold if py - cy > rthreshold else cy state.camera = cx, cy deads_particles = [] for p in state.particles: p.update(state) if not p.living(): deads_particles.append(p) for dp in deads_particles: state.particles.remove(dp) # store in-range block indices max_range = state.max_range state.in_range = set() for i in range(len(state.board)): x, y = index_to_pos(i, state.board.side) center = x + 0.5, y + 0.5 if dist(center, state.player.pos) < max_range * 2: state.in_range.add(i) # for i in range(3): # state.particles.append(Glitter(state.player.pos)) def ray_dirs(i): px, py = state.player.pos c, r = index_to_pos(i, state.board.side) return [ (x - px, y - py) for x, y in [ (c + 0.5, r), (c + 1, r + 0.5), (c + 0.5, r + 1), (c, r + 0.5), ] if x - px and y - py ] def hit_wall(x, y): return ( state.board.outside(x, y) or is_wall(state.board.get(x, y)) or dist(state.player.pos, (x, y)) > state.max_range or is_door(state.board.get(x, y)) ) rays: List[VecF] = sum([ray_dirs(i) for i in state.in_range], []) state.visible = set() for r in rays: trav, hit, _ = cast_ray((px + 0.5, py + 0.5), r, hit_wall) state.visible.update(trav) if not state.board.outside(*hit): state.visible.add(hit) state.visited |= state.visible return state