Esempio n. 1
0
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()
Esempio n. 2
0
 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
Esempio n. 3
0
    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
Esempio n. 4
0
 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)
Esempio n. 5
0
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
Esempio n. 6
0
 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)
Esempio n. 7
0
    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
Esempio n. 8
0
 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
Esempio n. 9
0
    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
Esempio n. 10
0
 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
Esempio n. 11
0
    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)
Esempio n. 12
0
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
Esempio n. 13
0
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")
Esempio n. 14
0
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
Esempio n. 15
0
 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)
Esempio n. 16
0
    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}")}
Esempio n. 17
0
    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
Esempio n. 18
0
    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)
Esempio n. 19
0
 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
Esempio n. 20
0
 def targeting_message(self):
     return Message(self.targeting_message_text)