def play_game(game_data, gfx_data): while True: for res in game_data.timesystem.tick(game_data, gfx_data): try: msg = res.get("message") if msg: game_data.log.add_message(msg) dead_entity = res.get("dead") if dead_entity: if dead_entity == game_data.player: msg, game_data = kill_player(game_data, gfx_data.assets) gfx_data.windows.activate_wnd_for_state( game_data.state, game_data, gfx_data) else: game_data.stats.monster_killed(dead_entity) msg, game_data = kill_monster(dead_entity, game_data, gfx_data.assets) game_data.log.add_message(msg) cast = res.get("cast") if cast is not None: if cast: formula = res.get("formula") game_data.stats.throw_vial(formula) if config.conf.cooldown_mode == "always": game_data.player.caster.add_cooldown( formula.formula_idx, formula.cooldown + 1) else: game_data.player.caster.add_cooldown( formula.formula_idx, formula.cooldown) do_quit = res.get("quit") if do_quit: keep_playing = res.get("keep_playing") return keep_playing == True # handle False and None xp = res.get("xp") if xp: leveled_up = game_data.player.level.add_xp(xp) # leveled_up = game_data.player.level.add_xp(game_data.player.level.xp_to_next_level) if not config.conf.keys: game_data.log.add_message( Message("You gain {} xp".format(xp))) if leveled_up: game_data.log.add_message( Message( "You grow stronger, reached level {}".format( game_data.player.level.current_level), (0, 255, 0), )) game_data.prev_state.append(game_data.state) game_data.state = GameStates.LEVEL_UP game_data.menu_data.currchoice = 0 except AttributeError: print("Failed to handle res={}".format(res)) traceback.print_exc()
def attack(self, target): results = [] dmg = self.power - target.fighter.defense if dmg > 0: text = "{} attacks {} for {}".format(self.owner.name, target.name, dmg) results.append({"message": Message(text, tcod.white)}) results.extend(target.fighter.take_damage(self.owner, dmg, self.dmg_type)) else: text = "{} attacks {} but is too weak to hurt".format(self.owner.name, target.name) results.append({"message": Message(text, tcod.white)}) return results
def apply(self, game_data, gfx_data, target): results = [] # create graphcs target_tiles = get_tiles_within(game_data.map, self.area, target) for pos in target_tiles: gfx_data.visuals.add_temporary(pos, pos, lifespan=0.2, asset=Assets.get().spark_effect, wait=0.2) # cause effect targets = [] for e in game_data.map.entities: if e.fighter and e.pos in target_tiles: targets.append(e) effect = EffectBuilder.create( EffectType.DAMAGE, amount=Freezebomb.DAMAGE, dmg_type=DamageType.COLD, rounds=Freezebomb.ROUNDS, caster=game_data.player, ) for t in targets: results.append( {"message": Message(f"Hit {t.name.lower()} with freezebomb, slowed for {Freezebomb.ROUNDS} rounds")} ) results.extend(effect.apply(t)) return results
def execute(self, game_data, gfx_data): game_data.map.entities.remove(self.ingredient) text = "You found an ingredient, some {}".format( self.ingredient.ingredient.name.lower()) result = [{"message": Message(text)}] game_data.ingredient_storage.add(self.ingredient.ingredient) return self.package(result=result)
def kill_player(game_state, assets): game_state.player.drawable = Drawable(assets.monster_corpse) game_state.player.render_order = RenderOrder.CORPSE game_state.player.name = "Your corpse" game_state.state = GameStates.PLAYER_DEAD game_state.stats.end_time = datetime.datetime.now() game_state.player.effects.clear() return Message("You died", tcod.red), game_state
def execute(self, game_data, gfx_data): if game_data.inventory.has_free_spot(): game_data.map.entities.remove(self.consumable) text = f"You found a consumable, {self.consumable.consumable.name.lower()}" game_data.inventory.add(self.consumable.consumable) else: text = "Your inventory is full" result = [{"message": Message(text)}] return self.package(result=result)
def take_damage(self, source, dmg, dmg_type): assert dmg > 0 assert source assert dmg_type results = [] if self.hp <= 0: # already dead, stacked damage return results if self.shield: shield_results, actual_dmg = self.shield.on_hit(source, dmg, dmg_type) results.extend(shield_results) dmg = actual_dmg for res in shield_results: if res.get("depleted"): self.shield = None if dmg_type in self.immunities: dmg_type_name = dmg_type.name.lower() text = f"{self.owner.name} is immune to {dmg_type_name} and resisted all damage" results.append({"message": Message(text)}) dmg = 0 if dmg_type in self.resistances: resisted = dmg // 2 dmg_type_name = dmg_type.name.lower() text = f"{self.owner.name} has resistance to {dmg_type_name} and resisted {resisted}/{dmg} damage" results.append({"message": Message(text)}) dmg -= resisted self.hp = max(0, self.hp - dmg) if dmg > 0: self.show_on_hit(dmg) if self.hp <= 0: self.killed_by = source if config.conf.keys: results.append({"dead": self.owner}) else: results.append({"dead": self.owner, "xp": self.xp}) return results
def on_hit(self, source, dmg, dmg_type): assert self.visual_effect results = [] if source and self.owner.distance_to(source) <= self.distance: for sb in self.strikebacks: sb.apply(source) if "amount" in sb.stats: text = "Shield hits {} for {} {} damage".format( source.name, sb.stats.amount, sb.stats.dmg_type.name.lower()) results.append({"message": Message(text)}) actual_dmg = max(0, dmg - self.level) self.level = max(0, self.level - dmg) if self.level <= 0: results.append({"depleted": True}) results.append( {"message": Message("Your shield has been depleted")}) VisualEffectSystem.get().remove(self.visual_effect) return results, actual_dmg
def apply(self, game_data, gfx_data, target): explored_tiles = [] for x in range(game_data.map.width): for y in range(game_data.map.height): if game_data.map.tiles[x][y].explored and not game_data.map.tiles[x][y].blocked: explored_tiles.append((x, y)) x, y = random.choice(explored_tiles) game_data.player.pos = Pos(x, y) results = [{"message": Message("Teleported to a previously explored location")}] return results
def handle_start_throwing_vial(start_throwing_vial, turn_results): player_action = None if start_throwing_vial >= len(self.caster.formulas): game_data.log.add_message( Message("You don't have that formula yet", tcod.yellow)) elif self.caster.is_on_cooldown(start_throwing_vial): game_data.log.add_message( Message("That's not ready yet", tcod.yellow)) else: formula = self.caster.formulas[start_throwing_vial] if formula.targeted: start_throwing_vial_results = { "targeting_formula": formula, "formula_idx": start_throwing_vial, } turn_results.append(start_throwing_vial_results) else: player_action = ThrowVialAction( self, formula, targetpos=game_data.player.pos) return player_action, turn_results
def execute(self, game_data, gfx_data): all_keys = game_data.map.num_keys_found == game_data.map.num_keys_total if not all_keys: num_remaining = game_data.map.num_keys_total - game_data.map.num_keys_found text = "You haven't found all the keys on this level yet, {} keys left".format( num_remaining) game_data.map.stairs_found = True result = [{"message": Message(text)}] return self.package(result=result) results = [] if game_data.run_planner.has_next: if game_data.map.tutorial: # re-set formulas after tutorial game_data.formula_builder.run_tutorial = False game_data.formula_builder.set_initial_slots() game_data.player.caster.clear_cooldowns() # reset shield if game_data.player.fighter.shield: game_data.player.fighter.shield = None elif config.conf.keys: results.append({"xp": game_data.player.level.xp_to_next_level}) game_data.prev_state.append(game_data.state) game_data.prev_state.append(GameStates.FORMULA_SCREEN) game_data.state = GameStates.STORY_SCREEN game_data.map = game_data.run_planner.activate_next_level() game_data.map.entities = game_data.map.entities game_data.fov_map = initialize_fov(game_data.map) game_data.fov_recompute = True cam_width = min(game_data.map.width, gfx_data.camera.orig_width) cam_height = min(game_data.map.height, gfx_data.camera.orig_height) gfx_data.camera = Camera(cam_width, cam_height, game_data) gfx_data.windows.activate_wnd_for_state(game_data.state, game_data, gfx_data) gfx_data.camera.initialize_map() gfx_data.camera.center_on(game_data.player.pos.x, game_data.player.pos.y) game_data.stats.next_level() results.append({"descended": True}) else: game_data.prev_state.append(game_data.state) game_data.state = GameStates.VICTORY game_data.story.next_story() gfx_data.windows.activate_wnd_for_state(game_data.state, game_data, gfx_data) game_data.stats.end_time = datetime.datetime.now() results.append({"victory": True}) return self.package(results)
def kill_monster(monster, game_state, assets): msg = Message("{} is dead".format(monster.name), tcod.orange) monster.drawable = Drawable(assets.monster_corpse) monster.color = tcod.dark_red monster.blocks = False monster.fighter = None monster.ai = None monster.render_order = RenderOrder.CORPSE monster.name = "Remains of {}".format(monster.name) monster.active = False game_state.timesystem.release(monster) return msg, game_state
class Caster: def __init__(self, num_slots, num_formulas): self.num_slots = num_slots self.num_formulas = num_formulas self.formulas = None self.clear_cooldowns() def create_cooldowns(self): retr = {} for idx in range(self.num_formulas): retr[idx] = 0 return retr def set_formulas(self, formulas): self.formulas = formulas for s in self.formulas: s.caster = self def is_on_cooldown(self, formula_idx): if formula_idx not in self.cooldowns: return False return self.cooldowns[formula_idx] > 0 def add_cooldown(self, formula_idx, cooldown): self.cooldowns[formula_idx] = cooldown def get_cooldown(self, formula_idx): return self.cooldowns[formula_idx] def tick_cooldowns(self): to_remove = [] for formula_idx in self.cooldowns: self.cooldowns[formula_idx] -= 1 if self.cooldowns[formula_idx] <= 0: to_remove.append(formula_idx) for r in to_remove: del self.cooldowns[r] def clear_cooldowns(self): self.cooldowns = self.create_cooldowns() def has_cooldown(self): if not self.cooldowns: return False for cd in self.cooldowns: if cd > 0: return True return False cooldown_message = Message("That formula is on cooldown")
def do_looting(game_data, gfx_data, prefix): val = random.randint(0, 100) can_heal = game_data.player.fighter.hp < game_data.player.fighter.max_hp if val <= 40 and can_heal: amount = 3 game_data.player.fighter.heal(amount) result = [{ "message": Message( f"{prefix} your wounds close, you've been healed {amount} points" ) }] elif val <= 70 and game_data.player.caster.has_cooldown(): cooldown_reduction = 5 for _ in range(0, cooldown_reduction): game_data.player.caster.tick_cooldowns() text = f"{prefix} you shimmer, cooldowns reduced by {cooldown_reduction}" result = [{"message": Message(text)}] else: # we can always add more shield shield = game_data.player.fighter.shield shield_strength = 3 if shield: shield.level += shield_strength if shield.level > shield.max_level: shield.max_level = shield.level text = f"{prefix} your shield strengthens" else: shield_effect = EffectBuilder.create( EffectType.DEFENSE, level=shield_strength, strikebacks=[], distance=0, ) shield_effect.apply(game_data.player) text = f"{prefix} a shield appears around you" result = [{"message": Message(text)}] return result
def execute(self, game_data, gfx_data): game_data.map.entities.remove(self.key) game_data.map.num_keys_found += 1 if game_data.map.num_keys_found == game_data.map.num_keys_total: text = "All keys found, head to the stairs" elif game_data.map.stairs_found: text = "You found a key, {}/{} keys found".format( game_data.map.num_keys_found, game_data.map.num_keys_total) else: text = "You found a key, {} keys found, wonder how many are left?".format( game_data.map.num_keys_found) result = [{"message": Message(text)}] result.extend( do_looting(game_data, gfx_data, prefix="You pick up the key and")) return self.package(result=result)
def apply_craft(self, game_data): if self.output_slot.ingredient == Ingredient.EMPTY: return # no crafting inputs = [slot.ingredient for slot in self.input_slots] game_data.ingredient_storage.remove_multiple(inputs) game_data.ingredient_storage.add(self.output_slot.ingredient) if not self.has_all_needed(inputs, game_data): for s in self.input_slots: # lacking one, reset s.ingredient = Ingredient.EMPTY inputs = [slot.ingredient for slot in self.input_slots] self.update_ingredient_counts(inputs) logname = self.output_slot.ingredient.name.capitalize() return {"message": Message(f"You crafted a {logname}")}
def apply(self, **kwargs): def get_msg(e): return self.applied_text.format(e.name_with_prep) caster = kwargs.get("caster") target_x = kwargs.get("target_x") target_y = kwargs.get("target_y") entities = kwargs.get("entities") game_map = kwargs.get("game_map") fov_map = kwargs.get("fov_map") triggered_trap = kwargs.get("triggered_trap") results = [] if caster.distance(target_x, target_y) > self.distance: results.append({ "cast": False, "message": Message("Target out of range", tcod.yellow) }) return results elif self.trap and not triggered_trap: game_map.tiles[target_x][target_y].trap = self results.append({ "cast": True, "message": Message("Formula cast as trap"), "formula": self, }) elif self.area < 1.5: # no aoe for e in entities: if not e.fighter or not tcod.map_is_in_fov( fov_map, target_x, target_y): continue if e.pos.x == target_x and e.pos.y == target_y: results.append({ "cast": True, "message": Message(get_msg(e)), "targets": [e], "formula": self, }) for effect in self.effects: results.extend(effect.apply(e)) if effect.stats.rounds > 1: e.add_effect(effect, effect.stats.rounds) break else: results.append({ "cast": False, "message": Message("No target"), "targets": [], "formula": self, }) else: # aoe formula targets = [] results.append({ "cast": True, "targets": targets, "formula": self, "message": Message("Splash vial thrown"), }) for e in entities: if not e.fighter or not tcod.map_is_in_fov( fov_map, target_x, target_y): continue if e.distance(target_x, target_y) < self.area: results.append({"message": Message(get_msg(e))}) for effect in self.effects: results.extend(effect.apply(e)) if effect.stats.rounds > 1: e.add_effect(effect, effect.stats.rounds) targets.append(e) return results
def take_turn(self, game_data, gfx_data): def handle_fullscreen(fullscreen): display = ( game_data.constants.window_size.width, game_data.constants.window_size.height, ) if pygame.display.get_driver() == "x11": pygame.display.toggle_fullscreen() else: display_copy = gfx_data.main.copy() if fullscreen: gfx_data.main = pygame.display.set_mode(display) else: gfx_data.main = pygame.display.set_mode( display, pygame.FULLSCREEN) gfx_data.fullscreen = not gfx_data.fullscreen gfx_data.main.blit(display_copy, (0, 0)) pygame.display.update() def handle_interact(turn_results): player_action = None for e in game_data.map.entities: if e.pos.x == self.pos.x and e.pos.y == self.pos.y: if e.stairs: self.last_num_explored = None game_data.story.next_story() self.caster.clear_cooldowns() player_action = DescendStairsAction(self) break elif e.key: player_action = PickupKeyAction(self, e) break elif e.ingredient: player_action = PickupIngredientAction(self, e) break elif e.consumable: player_action = PickupConsumableAction(self, e) break elif e.name.startswith("Remains of"): # monster if e.orig_name == "Arina": game_data.prev_state.append(game_data.state) game_data.state = GameStates.VICTORY game_data.story.next_story() gfx_data.windows.activate_wnd_for_state( game_data.state, game_data, gfx_data) game_data.stats.end_time = datetime.datetime.now() elif not config.conf.keys: e.name = "Looted r" + e.name[1:] game_data.stats.loot_monster(e) player_action = LootAction(self) break else: if config.conf.cooldown_mode != "always": player_action = WaitAction(self) return player_action, turn_results def handle_show_help(): game_data.prev_state.append(game_data.state) if game_data.state == GameStates.FORMULA_SCREEN: game_data.state = GameStates.FORMULA_HELP_SCEEN elif game_data.state == GameStates.STORY_SCREEN: game_data.state = GameStates.STORY_HELP_SCREEN elif game_data.state == GameStates.INVENTORY: game_data.state = GameStates.INVENTORY_HELP elif game_data.state == GameStates.CRAFTING: game_data.state = GameStates.CRAFTING_HELP else: game_data.state = GameStates.GENERAL_HELP_SCREEN gfx_data.windows.activate_wnd_for_state(game_data.state, game_data, gfx_data) def handle_move(move, turn_results): player_action = None dx, dy = move destx = self.pos.x + dx desty = self.pos.y + dy if self.godmode: self.pos = Pos(destx, desty) gfx_data.camera.center_on(destx, desty) game_data.stats.move_player(Pos(destx, desty)) player_action = MoveToPositionAction(self, targetpos=Pos( destx, desty)) elif not game_data.map.is_blocked(destx, desty): target = get_blocking_entites_at_location( game_data.map.entities, destx, desty) if target and target.fighter: player_action = AttackAction(self, target=target) else: gfx_data.camera.center_on(destx, desty) game_data.stats.move_player(Pos(destx, desty)) player_action = MoveToPositionAction(self, targetpos=Pos( destx, desty)) return player_action, turn_results def handle_moving_towards(turn_results): player_action = None if self.pos == self.moving_towards: self.moving_towards = None else: last_moving_towards_pos = Pos(self.pos.x, self.pos.y) self.move_astar( self.moving_towards, game_data.map.entities, game_data.map, length_limit=False, ) if last_moving_towards_pos == self.pos: self.moving_towards = None else: gfx_data.camera.center_on(self.pos.x, self.pos.y) game_data.stats.move_player(self.pos) time.sleep(0.15) player_action = MoveToPositionAction(self, targetpos=Pos( self.pos.x, self.pos.y)) return player_action, turn_results def handle_start_throwing_vial(start_throwing_vial, turn_results): player_action = None if start_throwing_vial >= len(self.caster.formulas): game_data.log.add_message( Message("You don't have that formula yet", tcod.yellow)) elif self.caster.is_on_cooldown(start_throwing_vial): game_data.log.add_message( Message("That's not ready yet", tcod.yellow)) else: formula = self.caster.formulas[start_throwing_vial] if formula.targeted: start_throwing_vial_results = { "targeting_formula": formula, "formula_idx": start_throwing_vial, } turn_results.append(start_throwing_vial_results) else: player_action = ThrowVialAction( self, formula, targetpos=game_data.player.pos) return player_action, turn_results def handle_use_consumable(use_consumable, turn_results): player_action = None item = game_data.inventory.get_quickslot(use_consumable) if item: if item.targeted: res = { "use_consumable": item, "use_consumable_index": use_consumable, } turn_results.append(res) else: player_action = UseConsumableAction( self, item, targetpos=game_data.player.pos) return player_action, turn_results def handle_targeting(left_click, right_click, turn_results): player_action = None if left_click: targetx, targety = left_click.cx, left_click.cy distance = (self.pos - Vec(targetx, targety)).length() if game_data.targeting_formula: alternate = left_click.alternate if alternate and config.conf.trapcast: game_data.targeting_formula.trap = True game_data.targeting_formula.set_texts() if distance > game_data.targeting_formula.distance: turn_results.append({ "target_out_of_range": True, "targeting_formula": game_data.targeting_formula, }) else: player_action = ThrowVialAction( self, game_data.targeting_formula, targetpos=(Pos(targetx, targety)), ) # gfx_data.visuals.add_temporary(self.pos, Pos(targetx, targety), lifespan=distance * 0.1, gfx_data.visuals.add_temporary( self.pos, Pos(targetx, targety), lifespan=0.2, asset=gfx_data.assets.throwing_bottle, ) game_data.state = game_data.prev_state.pop() game_data.targeting_formula_idx = None elif game_data.targeting_consumable: if distance > game_data.targeting_consumable.distance: turn_results.append({ "target_out_of_range": True, "targeting_consumable": game_data.targeting_consumable, }) else: player_action = UseConsumableAction( self, game_data.targeting_consumable, targetpos=(Pos(targetx, targety)), ) gfx_data.visuals.add_temporary( self.pos, Pos(targetx, targety), lifespan=0.2, asset=gfx_data.assets.throwing_bottle, ) game_data.state = game_data.prev_state.pop() game_data.targeting_consumable = None elif right_click: turn_results.append({"targeting_cancelled": True}) return player_action, turn_results if game_data.stats.start_time is None: game_data.stats.start_time = datetime.datetime.now() if self.action_points < 100: return None player_action = None if not self.last_num_explored: recompute_fov( game_data.fov_map, game_data.player.pos.x, game_data.player.pos.y, game_data.constants.fov_radius, game_data.constants.fov_light_walls, game_data.constants.fov_algorithm, ) self.last_num_explored = game_data.map.num_explored self.handle_tick_cooldowns(game_data) while not player_action: turn_results = [] if game_data.fov_recompute: recompute_fov( game_data.fov_map, game_data.player.pos.x, game_data.player.pos.y, game_data.constants.fov_radius, game_data.constants.fov_light_walls, game_data.constants.fov_algorithm, ) gfx_data.windows.draw(game_data, gfx_data) events = pygame.event.get() quit_events = [e for e in events if e.type == pygame.QUIT] if quit_events: player_action = ExitAction(keep_playing=False, ask=False) if config.conf.is_replaying and input_recorder.events: if not config.conf.is_testing: time.sleep(0.2) next_event = input_recorder.events.pop(0) print(f"Replaying event {next_event}") if next_event.event_type == InputType.KEY: key_events = [next_event] mouse_events = [] else: key_events = [] mouse_events = [next_event] else: if not pygame.key.get_focused( ) and not self.ignore_first_click: self.ignore_first_click = True key_events = [ KeyEvent(e) for e in events if e.type == pygame.KEYDOWN ] mouse_events = [ MouseEvent(e) for e in events if e.type == pygame.MOUSEBUTTONDOWN ] if key_events and self.ignore_first_click: self.ignore_first_click = False key_action = handle_keys(key_events, game_data.state) mouse_action = handle_mouse(mouse_events, game_data.constants, gfx_data.camera, self.ignore_first_click, game_data.logger) if mouse_action: if self.ignore_first_click: self.ignore_first_click = False else: gui_action = gfx_data.windows.handle_click( game_data, gfx_data, mouse_action) if gui_action: mouse_action = gui_action if key_action: handled, gui_action = gfx_data.windows.handle_key( game_data, gfx_data, key_action) if handled: key_action = gui_action fullscreen = key_action.get(EventType.fullscreen) move = key_action.get(EventType.move) do_exit = key_action.get(EventType.exit) left_click = mouse_action.get(EventType.left_click) right_click = mouse_action.get(EventType.right_click) show_help = key_action.get(EventType.show_help) interact = key_action.get(EventType.interact) start_crafting = key_action.get(EventType.start_crafting) inventory = key_action.get(EventType.inventory) use_consumable = None if key_action.get(EventType.use_consumable) is not None: use_consumable = key_action.get(EventType.use_consumable) elif mouse_action.get(EventType.use_consumable) is not None: use_consumable = mouse_action.get(EventType.use_consumable) start_throwing_vial = None if key_action.get(EventType.start_throwing_vial) is not None: start_throwing_vial = key_action.get( EventType.start_throwing_vial) elif mouse_action.get(EventType.start_throwing_vial) is not None: start_throwing_vial = mouse_action.get( EventType.start_throwing_vial) if interact: action, turn_results = handle_interact(turn_results) assert turn_results is not None if action: player_action = action if start_crafting: game_data.prev_state.append(game_data.state) game_data.state = GameStates.CRAFTING gfx_data.windows.activate_wnd_for_state( game_data.state, game_data, gfx_data) if inventory: game_data.prev_state.append(game_data.state) game_data.state = GameStates.INVENTORY gfx_data.windows.activate_wnd_for_state( game_data.state, game_data, gfx_data) if show_help: handle_show_help() elif move: action, turn_results = handle_move(move, turn_results) assert turn_results is not None if action: player_action = action if left_click and game_data.state == GameStates.PLAY: valid_tile = True if left_click.cx >= game_data.map.width or left_click.cy >= game_data.map.height: valid_tile = False for e in game_data.map.entities: if e.pos.x == left_click.cx and e.pos.y == left_click.cy: if e.ai: valid_tile = False if valid_tile and game_data.map.tiles[left_click.cx][ left_click.cy].explored: # click to move self.moving_towards = Pos(left_click.cx, left_click.cy) if self.moving_towards: action, turn_results = handle_moving_towards(turn_results) assert turn_results is not None if action: player_action = action if game_data.state == GameStates.LEVEL_UP: gfx_data.windows.get(LevelUpWindow).visible = True elif game_data.state == GameStates.TARGETING: action, turn_results = handle_targeting( left_click, right_click, turn_results) assert turn_results is not None if action: player_action = action if start_throwing_vial is not None: action, turn_results = handle_start_throwing_vial( start_throwing_vial, turn_results) assert turn_results is not None if action: player_action = action if use_consumable is not None: action, turn_results = handle_use_consumable( use_consumable, turn_results) assert turn_results is not None if action: player_action = action if do_exit: if game_data.state == GameStates.TARGETING: turn_results.append({"targeting_cancelled": True}) else: keep_playing = key_action.get(EventType.keep_playing) player_action = ExitAction(keep_playing) if fullscreen: handle_fullscreen(fullscreen) key_action = mouse_action = None # clear them for next round for res in turn_results: game_data.targeting_formula = res.get("targeting_formula") game_data.targeting_formula_idx = res.get("formula_idx") if game_data.targeting_formula: formula_idx = res.get("formula_idx") if self.caster.is_on_cooldown(formula_idx): game_data.log.add_message(self.caster.cooldown_message) else: game_data.prev_state.append(game_data.state) game_data.state = GameStates.TARGETING game_data.log.add_message( game_data.targeting_formula.targeting_message) targeting_cancelled = res.get("targeting_cancelled") if targeting_cancelled: game_data.state = game_data.prev_state.pop() game_data.log.add_message(Message("Targeting cancelled")) target_out_of_range = res.get("target_out_of_range") if target_out_of_range: game_data.log.add_message(Message("Target out of range")) use_consumable = res.get(EventType.use_consumable) if use_consumable: if use_consumable.targeted: game_data.targeting_consumable = use_consumable print( f"{use_consumable.name} is a targeted consumable, targeting..." ) game_data.prev_state.append(game_data.state) game_data.state = GameStates.TARGETING gfx_data.visuals.update(game_data, gfx_data) gfx_data.clock.tick(gfx_data.fps_per_second) pygame.display.flip() # end of no action assert player_action if type(player_action) == MoveToPositionAction: game_data.fov_recompute = True return player_action.execute(game_data, gfx_data)
def apply(self, game_data, gfx_data, target): game_data.player.caster.clear_cooldowns() results = [{"message": Message("Your cooldowns have been cleared, have at'em!")}] return results
def targeting_message(self): return Message(self.targeting_message_text)