Skip to content

Effects Handling

Cromha edited this page Feb 27, 2024 · 1 revision

Introduction

In this wiki page, you'll learn more about the Bane Of Wargs game engine, and more specifically about how the game engine handle effects. I thought it'd be interesting to do and it might help understanding better how effect types works and how they really affect the player. We'll go over every different effects types and how they're handling, because, even if they're handled the same way, they all have different parameters, options, systems and actions in the game engine.

Note that this wiki page complements the Effects Types wiki page, which also complements this one. So make sure you've check the Effect Types wiki page before checking this one.


Here's an extract from the source code of the game engine where the effects are handled:

Source Code Extract

main.py class, l.1021-1143

        # All the checks for the player active effects
        # are here
        #
        # If the player has any active effects, load
        # them one by one and update them depending
        # on their dictionary content and type
        global player_damage_coefficient, time_elapsing_coefficient
        player_damage_coefficient = 1
        time_elapsing_coefficient = 1
        if player["held item"] != " ":
            player["critical hit chance"] = item[player["held item"]]["critical hit chance"]
        else:
            player["critical hit chance"] = 0
        if player["active effects"] != {}:
            for i in list(player["active effects"]):
                current_effect = player["active effects"][i]
                effect_over = False
                # Run the actions for every effect type
                if current_effect["type"] == 'healing':
                    # Check if the effect duration's over
                    if (
                        (
                            current_effect["effect duration"] + current_effect["effect starting time"]
                        ) < player["elapsed time game days"]
                    ):
                        # Remove that effect from the player
                        # active effects and set the player
                        # modified stats to before the effect
                        # happened
                        player["active effects"].pop(i)
                        player["health"] = current_effect["before stats"]["health"]
                        player["max health"] = current_effect["before stats"]["max health"]
                        effect_over = True
                    # Check if the effect has already been
                    # applied or not
                    if not current_effect["already applied"] and not effect_over:
                        # Apply that effect changes now
                        if current_effect["effects"]["health changes"] >= 999:
                            player["health"] = player["max health"]
                        else:
                            player["health"] += current_effect["effects"]["health changes"]
                        player["max health"] += current_effect["effects"]["max health changes"]
                        player["active effects"][i]["already applied"] = True
                elif current_effect["type"] == 'protection':
                    # Check if the effect duration's over
                    if (
                        (
                            current_effect["effect duration"] + current_effect["effect starting time"]
                        ) < player["elapsed time game days"] and current_effect["effect duration"] != 999
                    ):
                        # Remove that effect from the player
                        # active effects
                        player["active effects"].pop(i)
                        effect_over = True
                    # Apply the effect effects if the
                    # effect isn't over
                    if not effect_over:
                        player["armor protection"] = player["armor protection"] * current_effect[
                            "effects"
                        ]["protection coefficient"]
                elif current_effect["type"] == 'strength':
                    # Check if the effect duration's over
                    if (
                        (
                            current_effect["effect duration"] + current_effect["effect starting time"]
                        ) < player["elapsed time game days"] and current_effect["effect duration"] != 999
                    ):
                        # Remove that effect from the player
                        # active effects
                        player["active effects"].pop(i)
                        effect_over = True
                    # Apply the effect effects if the
                    # effect isn't over
                    if not effect_over:
                        player["critical hit chance"] = player["critical hit chance"] * current_effect["effects"][
                            "critical hit chance coefficient"
                        ]
                        # If the player already has an effect that changes
                        # the damage coefficient and that's greater, don't
                        # apply the current effect coefficient
                        # = keep the greater one
                        if not player_damage_coefficient > current_effect["effects"]["damage coefficient"]:
                            player_damage_coefficient = current_effect["effects"]["damage coefficient"]
                elif current_effect["type"] == 'agility':
                    # Check if the effect duration's over
                    if (
                        (
                            current_effect["effect duration"] + current_effect["effect starting time"]
                        ) < player["elapsed time game days"] and current_effect["effect duration"] != 999
                    ):
                        # Remove that effect from the player
                        # active effects
                        player["active effects"].pop(i)
                        effect_over = True
                    # Apply the effect effects if the
                    # effect isn't over
                    if not effect_over:
                        player["agility"] = player["agility"] * current_effect[
                            "effects"
                        ]["agility coefficient"]
                elif current_effect["type"] == 'time elapsing':
                    # Check if the effect duration's over
                    if (
                        (
                            current_effect["effect duration"] + current_effect["effect starting time"]
                        ) < player["elapsed time game days"] and current_effect["effect duration"] != 999
                    ):
                        # Remove that effect from the player
                        # active effects
                        player["active effects"].pop(i)
                        effect_over = True
                    # Apply the effect effects if the
                    # effect isn't over
                    if not effect_over:
                        # If the player already has an effect that changes
                        # the damage coefficient, make so that the global
                        # coefficient gets added that effect coefficient
                        if time_elapsing_coefficient != 1:
                            time_elapsing_coefficient = (
                                time_elapsing_coefficient * current_effect["effects"]["time elapsing coefficient"]
                            )
                        else:
                            time_elapsing_coefficient = current_effect["effects"]["time elapsing coefficient"]

consumable_handling class, l.16-359

def get_healing_effect_changes(effect_data):
    health_changes = 0
    max_health_changes = 0

    if "augmentation" in list(effect_data["health change"]):
        augmentation = effect_data["health change"]["augmentation"]
        health_changes += augmentation
    if "diminution" in list(effect_data["health change"]):
        diminution = effect_data["health change"]["diminution"]
        health_changes -= diminution
    if "max health" in list(effect_data["health change"]):
        if "augmentation" in list(effect_data["health change"]["max health"]):
            augmentation = effect_data["health change"]["max health"]["augmentation"]
            max_health_changes += augmentation
        if "diminution" in list(effect_data["health change"]["max health"]):
            diminution = effect_data["health change"]["max health"]["diminution"]
            max_health_changes -= diminution

    return health_changes, max_health_changes


def get_exp_changes_effect_changes(effect_data):
    exp_changes = 0

    if "augmentation" in list(effect_data["exp change"]):
        augmentation = effect_data["exp change"]["augmentation"]
        exp_changes += augmentation
    if "diminution" in list(effect_data["exp change"]):
        diminution = effect_data["exp change"]["diminution"]
        exp_changes -= diminution

    return exp_changes


def healing_effect(effect_data, player):
    # Generate a UUID for that new
    # effect
    effect_uuid = str(uuid_handling.generate_random_uuid())

    # If this effect has a timer, create
    # the effect dictionary that will be
    # added to the player save data and then
    # handled by the main.py function
    #
    # If not, just apply the effects of that
    # effect one by one
    if "effect time" in effect_data:
        effects = {}

        # Create the applied effects dictionary
        health_changes, max_health_changes = get_healing_effect_changes(effect_data)

        effects = {
            "health changes": health_changes,
            "max health changes": max_health_changes
        }

        effect_dictionary = {
            "effect duration": effect_data["effect time"],
            "effect starting time": player["elapsed time game days"],
            "type": effect_data["type"],
            "already applied": False,
            "before stats": {
                "health": player["health"],
                "max health": player["max health"]
            },
            "effects": effects
        }

        # Add the effect dictionary to the player
        # active effects dictionary
        player["active effects"][effect_uuid] = effect_dictionary
    else:
        if effect_data["health change"] is not None:
            health_changes, max_health_changes = get_healing_effect_changes(effect_data)

            if health_changes >= 999:
                player["health"] = player["max health"]
            else:
                player["health"] += health_changes
                player["max health"] += max_health_changes


def protection_effect(effect_data, player):
    # Generate a UUID for that new
    # effect
    effect_uuid = str(uuid_handling.generate_random_uuid())

    # Create the effect dictionary that will
    # be added to the player save data and then
    # handled by the main.py function

    effects = {}

    # Create the applied effects dictionary
    if "coefficient" in list(effect_data["protection change"]):
        protection_coefficient = effect_data["protection change"]["coefficient"]
    else:
        protection_coefficient = 1

    effects = {
        "protection coefficient": protection_coefficient
    }

    effect_dictionary = {
        "effect duration": effect_data["effect time"],
        "effect starting time": player["elapsed time game days"],
        "type": effect_data["type"],
        "effects": effects
    }

    # Add the effect dictionary to the player
    # active effects dictionary
    player["active effects"][effect_uuid] = effect_dictionary


def strength_effect(effect_data, player):
    # Generate a UUID for that new
    # effect
    effect_uuid = str(uuid_handling.generate_random_uuid())

    # Create the effect dictionary that will
    # be added to the player save data and then
    # handled by the main.py function

    effects = {}

    # Create the applied effects dictionary
    damage_coefficient = 1
    critical_hit_chance_coefficient = 1
    if effect_data["strength change"] is not None:
        if "damage coefficient" in list(effect_data["strength change"]):
            damage_coefficient = effect_data["strength change"]["damage coefficient"]
        if "critical hit chance coefficient" in list(effect_data["strength change"]):
            critical_hit_chance_coefficient = effect_data["strength change"]["critical hit chance coefficient"]

    effects = {
        "damage coefficient": damage_coefficient,
        "critical hit chance coefficient": critical_hit_chance_coefficient
    }

    effect_dictionary = {
        "effect duration": effect_data["effect time"],
        "effect starting time": player["elapsed time game days"],
        "type": effect_data["type"],
        "effects": effects
    }

    # Add the effect dictionary to the player
    # active effects dictionary
    player["active effects"][effect_uuid] = effect_dictionary


def agility_effect(effect_data, player):
    # Generate a UUID for that new
    # effect
    effect_uuid = str(uuid_handling.generate_random_uuid())

    # Create the effect dictionary that will
    # be added to the player save data and then
    # handled by the main.py function

    effects = {}

    # Create the applied effects dictionary
    if "coefficient" in list(effect_data["agility change"]):
        agility_coefficient = effect_data["agility change"]["coefficient"]
    else:
        agility_coefficient = 1

    effects = {
        "agility coefficient": agility_coefficient
    }

    effect_dictionary = {
        "effect duration": effect_data["effect time"],
        "effect starting time": player["elapsed time game days"],
        "type": effect_data["type"],
        "effects": effects
    }

    # Add the effect dictionary to the player
    # active effects dictionary
    player["active effects"][effect_uuid] = effect_dictionary


def time_elapsing_effect(effect_data, player):
    # Generate a UUID for that new
    # effect
    effect_uuid = str(uuid_handling.generate_random_uuid())

    # Create the effect dictionary that will
    # be added to the player save data and then
    # handled by the main.py function

    effects = {}

    # Create the applied effects dictionary
    if "coefficient" in list(effect_data["time change"]):
        time_elapsing_coefficient = effect_data["time change"]["coefficient"]
    else:
        time_elapsing_coefficient = 1

    effects = {
        "time elapsing coefficient": time_elapsing_coefficient
    }

    effect_dictionary = {
        "effect duration": effect_data["effect time"],
        "effect starting time": player["elapsed time game days"],
        "type": effect_data["type"],
        "effects": effects
    }

    # Add the effect dictionary to the player
    # active effects dictionary
    player["active effects"][effect_uuid] = effect_dictionary


def attributes_addition_effect(current_effect_data, player):
    # Check if there're effects to add, and
    # if yes, add them one by one

    if "attributes addition" in list(current_effect_data):
        for i in current_effect_data["attributes addition"]:
            player["attributes"] += [i]


def dialog_displaying_effect(current_effect_data, player, dialog, preferences, text_replacements_generic, drinks):
    print("")
    text_handling.print_separator("=")
    dialog_handling.print_dialog(current_effect_data["dialog"], dialog, preferences, text_replacements_generic, player, drinks)
    text_handling.print_separator("=")
    print("")


def enemy_spawning_effect(
    current_effect_data, player, lists, map_location, enemy, item,
    start_player, preferences, drinks, npcs, zone, mounts, mission,
    dialog, player_damage_coefficient, text_replacements_generic
):
    enemy_list = lists[current_effect_data["enemy list"]]
    enemies_number = current_effect_data["enemies number"]
    enemy_handling.spawn_enemy(
        map_location, enemy_list, enemies_number, enemy, item, lists, start_player, map, player,
        preferences, drinks, npcs, zone, mounts, mission, dialog, player_damage_coefficient,
        text_replacements_generic
    )


def exp_change_effect(current_effect_data, player):
    exp_changes = get_exp_changes_effect_changes(current_effect_data)
    player["xp"] += exp_changes


def coordinates_change_effect(current_effect_data, player):
    if "x" in list(current_effect_data["coordinates change"]):
        player["x"] = current_effect_data["coordinates change"]["x"]
    if "y" in list(current_effect_data["coordinates change"]):
        player["y"] = current_effect_data["coordinates change"]["y"]


def inventory_change_effect(current_effect_data, player):
    if "removals" in list(current_effect_data["inventory change"]):
        for i in current_effect_data["inventory change"]["removals"]:
            player["inventory"].remove(i)
    if "additions" in list(current_effect_data["inventory change"]):
        for i in current_effect_data["inventory change"]["additions"]:
            player["inventory"].append(i)


def consume_consumable(
    item_data, consumable_name, player,
    dialog, preferences, text_replacements_generic,
    lists, map_location, enemy, item, drinks,
    start_player, npcs, zone,
    mounts, mission, player_damage_coefficient
):
    # First, load the consumable data and stores
    # it in a variable, then remove the item
    # from the player's inventory
    logger_sys.log_message(f"INFO: Loading consumable '{consumable_name}' data")
    consumable_data = item_data[consumable_name]
    logger_sys.log_message(f"INFO: Loaded consumable '{consumable_name}' data:\n{consumable_data}")
    player["inventory"].remove(consumable_name)
    logger_sys.log_message(f"INFO: Removing item '{consumable_name}' form the player inventory")

    # If the consumable is a food, load the
    # health regeneration and apply them
    if consumable_data["type"] == "Food":
        if consumable_data["healing level"] == 999:
            player["health"] = player["max health"]
        else:
            player["health"] += consumable_data["healing level"]
        player["max health"] += consumable_data["max bonus"]
    else:

        # Then, load the consumable effects 1 by 1
        # and apply the effects 1 by 1
        logger_sys.log_message(f"INFO: Getting consumable '{consumable_name}' effects")

        if consumable_data["effects"] is not None:
            effects = consumable_data["effects"]
            logger_sys.log_message(f"INFO: Loaded consumable '{consumable_name}' effects:\n{effects}")
            count = 0
            for effect in consumable_data["effects"]:
                current_effect_data = consumable_data["effects"][count]
                current_effect_type = current_effect_data["type"]

                logger_sys.log_message(
                    f"INFO: Running consumable effect with '{current_effect_type}' parameter;\n{current_effect_data}"
                )

                if current_effect_type == "healing":
                    healing_effect(current_effect_data, player)
                elif current_effect_type == "protection":
                    protection_effect(current_effect_data, player)
                elif current_effect_type == "strength":
                    strength_effect(current_effect_data, player)
                elif current_effect_type == "agility":
                    agility_effect(current_effect_data, player)
                elif current_effect_type == "time elapsing":
                    time_elapsing_effect(current_effect_data, player)
                elif current_effect_type == "attributes addition":
                    attributes_addition_effect(current_effect_data, player)
                elif current_effect_type == "dialog displaying":
                    dialog_displaying_effect(current_effect_data, player, dialog, preferences, text_replacements_generic, drinks)
                elif current_effect_type == "enemy spawning":
                    enemy_spawning_effect(
                        current_effect_data, player, lists, map_location, enemy, item,
                        start_player, preferences, drinks, npcs, zone, mounts, mission,
                        dialog, player_damage_coefficient, text_replacements_generic
                    )
                elif current_effect_type == "exp change":
                    exp_change_effect(current_effect_data, player)
                elif current_effect_type == "coordinates change":
                    coordinates_change_effect(current_effect_data, player)
                elif current_effect_type == "inventory change":
                    inventory_change_effect(current_effect_data, player)

                count += 1

        else:
            logger_sys.log_message(f"INFO: Found no effects for consumable '{consumable_name}'")

Index

Different Effects Types

There are currently 11 different types of effects in the current game engine:

- healing # change player health and max health (for a defined amount of time if defined)
- protection # change player global armor protection (for a defined amount of time)
- strength # increase the player global damage and critical hit chance (for a defined amount of time)
- agility # change player global agility (for a defined amount of time)
- time elapsing # increase/decrease time elapsing (for a defined amount of time)
- *attributes addition # add custom attributes to the player
- *dialog displaying # display a dialog
- *enemy spawning # spawn an enemy/enemies
- exp change # change player experience stat
- *coordinates change # change the player's location
- *inventory change # give/remove items for the player's inventory

Note that all the effect starting with a * do not display on the consumable effect info in the inventory management UI (they're invisible).

Every active effects are stored in the player save data active effects dictionary. Here's how it can look:

[...]
active effects: {} # here, no effects are active
[...]
[...]
active effects:
  457f110d-9bea-4ecf-96f0-b59a9768b501:
    effect duration: 0.5
    effect starting time: 554.5899999999987
    effects:
      protection coefficient: 1.4
    type: protection
[...]

As you notice, each effect has a specific UUID, generated from the uuid_handling.py class' function generate_random_uuid, which uses the built-in python uuid module uuid4 generation method. This makes so that each effects, even if they're from the same type, are unique. Once the effect is over, the effect dictionary is removed from the active effects dictionary. This means that the active effects key can be empty, it'll have as a value only {}.

Hint: you can check the player save data using the secret $player$data$ command, while being in game.

Clone this wiki locally