Skip to content

Adding spells and attacks

DarkTl edited this page Jun 9, 2019 · 9 revisions

Adding new spells and attacks is not as simple as adding traits or characters. Not only you need to get a good animation somewhere, it requires a good deal of knowledge about how battle engine handles those animations. I suggest you to check how our attacks were made before trying to add yours.

Json files

You can find files descriping BE skills in game\content\db\battle_skillls, each of them should start with bes_to be recognized. Animations themselves can be located everywhere in the game directory, but usually we put them to game\content\gfx, more about it below.

Json files fieds

  • class: the logic used for the spell.
  1. Most spells use BE_Action class, it simply shows spell animation near the single target.
  2. MultiAttack class repeats the same attack multiple times, it's used for basic attacks like Axe Strike 2X. MitliAttacks have a few additional fields for animations: "sd_duration" , "interval", "scale" sd_duration is duration of one instance of attack, interval is time between instances of attack, scale resizes them if you need it.
  3. ArrowsSkill class requires three different animations: when character fires a projectile, the projectile itself and impact animation.
  4. P2P_Skil is the same, but needs no firing animation. ArrowsSkill and P2P_Skil show projectile flying to the target and then doing something to it, rather than showing animation instantly like all other classes do.
  5. BasicPoisonSpell acts like BE_Action, but also applies poison effect to the target.
  6. BasicHealingSpell acts like BE_Action, but heals the target.
  7. FullScreenCenteredArealSkill should be used for full screen animations (1280x720).
  8. ReviveSpell is special class that can be used to revive an ally withing battle engine.
  9. DefenceBuffSpell applies a defensive buff to an ally.
  10. ArealSkill is for spells that target a big area rather than one character, but at the same time don't use full screen animation.
  11. ATL_ArealSkill is used for special cases, it uses animation function for the attack, ignoring all usual targeting options. Used when we need to position area spell in unusual way. They use "atl" field for animation effects, which sets the name of animation function used for them.
  • name: name of the attack/spell, must be unique.
  • mn: menu name. If spell name is way too long for menus, you can use mn field for shorter version used only in menus.
  • menu_pos: where this attack/spell should be in battle engine menu compared to other attacks/spells. The lower the number, the higher the position.
  • attributes: control the logic of the spell. Expects a list of special attributes. One of them should be attack type: melee, ranged, magic or status. It controls stats used to deal the damage and defend from it. Melee is stuff like swords, ranged is stuff like bows, magic is any kind of spell, status is special things like magical shields or poisons. It also should include one or more elements used by the attack/spell. Possible elements are: fire, water, air, earth, ice, electricity, light, darkness, physical, poison. Simple non-magical attacks could have physical element only. While high tier attacks could even use everything at once. Elements decide how effective this attack/spell will be, as the target may be weak or resistant to certain elements. Finally, attributes may include attribute inevitable. It means this attack cannot be dodged no matter what. Ideally should be used only for high tier attacks.
  • kind: should be used for unusual spells that don't just damage enemy. buff applies a buff to an ally, damage_over_time is used for poisons, healing heals the target, revival is for reviving spells.
  • effect and multiplier: control spells damage. Effect sums with the caster's stats and multiplier multiplies the final spell damage after all calculations. Work differently for special spells: for healing "effect": 0.1 means healing 10% of max health, for poisons it means damage the target by 10% of its health. For others like magical shields it's meaningless.
  • critpower: if positive, increases critical attacks damage. Otherwise reduces it. critpower 0.2 means +20% damage.
  • mp_cost, vitality_cost and health_cost: how much spell costs. If you have none of them, the spell/attack will be free. You also can even use all three if you want. If value is not integer, it acts as a percentage. For instance "health_cost": 0.5 means the spell will cost 50% of caster max health, and "mp_cost": 1.0 means 100% of mp is needed. While "mp_cost": 1 means the spell costs 1 mp.
  • range: how far spell/attack can be used. Can be from 1 to 4. Currently we have four rows in battle engine. Range 1 means it only can attack the closet row, while range 4 means no restrictions.
  • type: who can be targeted by this spell/attack. By default it's one enemy, so we use this field only when we need another target(s). Can be:
  1. all_enemies: all enemies are targeted.
  2. sa: one ally. Normally used for healing or buffs.
  3. all_allies: all allies, for example for mass healing.
  • piercing: can be true or false. If true, can attack enemies on back row without the need to kill everyone on the front row.
  • true_pierce: can be true or false. If true, and piercing is also true, spell can target enemies on back row without damage penalty (usual penalty is -50% of damage, unless you killed everyone on the front row). Ideally should be used for high tier attacks/spells.
  • desc: short description, used in interface. Can be any text.
  • tier: determines how powerful this spell/attack is. Can be from 0 to 4. It doesn't actually control the damage, it tells the game how rare this spell should be. Tier 0 spells/attacks are common, all characters can use them. While tier 4 is the most powerful, and only high level characters should have access to them.
  • attacker_effects: animation (and sound) that plays before spell is activated, ie when character casts it.
    "attacker_effects": {
        "gfx": "cast_ice_1",
        "sfx": "default",
        "zoom": 1.5,
        "duration": 0.84,
        "cast": {
            "point": "bc",
            "yo": -75
        }

gfx is the name of animation, and sfx is the sound used by it. We have default sounds for them, in which case default is used. zoom changes animation size, duration is animation duration in seconds (it's always important, the game hides animation immediately and starts next animation after waiting for duration, so if it's incorrect and the animation you use is actually longer or shorter, it will look weird). Cast part controls the position of animation. Quoting the code,

            if type == "sopos":
                xpos = char.be.default_pos[0] + char.besprite_size[0] / 2
                ypos = char.be.default_pos[1] + yo
            if type == "pos":
                xpos = char.be.current_pos[0]
                ypos = char.be.current_pos[1] + yo
            elif type == "center":
                xpos = char.be.current_pos[0] + char.besprite_size[0] / 2
                ypos = char.be.current_pos[1] + char.besprite_size[1] / 2 + yo
            elif type == "tc":
                xpos = char.be.current_pos[0] + char.besprite_size[0] / 2
                ypos = char.be.current_pos[1] + yo
            elif type == "bc":
                xpos = char.be.current_pos[0] + char.besprite_size[0] / 2
                ypos = char.be.current_pos[1] + char.besprite_size[1] + yo
            elif type == "fc":
                if char.be.row in [0, 1]:
                    xpos = char.be.current_pos[0] + char.besprite_size[0]
                    ypos = char.be.current_pos[1] + char.besprite_size[1] / 2 + yo
                else:
                    xpos = char.be.current_pos[0]
                    ypos = char.be.current_pos[1] + char.besprite_size[1] / 2 + yo

So in other words, bc targets bottom center of character, tc top center, etc. yo means yoffset, ie we add 75 pixels to y position, to make spell look better. xo would do the same for x position. You can't predict how spell will look like before trying it in game, so if it doesn't look perfectly, manupulating yo and xo often helps.

  • main effect: actual spell animation
    "main_effect": {
        "gfx": "ice_2",
        "sfx": "content/sfx/sound/be/ice1.mp3",
        "duration": 1.5,
        "aim": {
            "point": "bc",
            "anchor": [0.5, 1.0],
            "yo": 80
        },
        "start_at": 0,
        "zoom": 1.3
    },

gfx is the name of animation, and sfx is the sound used by it. duration is animation duration in seconds. aim works in the same way as cast works for attacker effect. anchor controls how we position spell. anchor[0.0, 0.0] is left top part of spell, anchor[1.0, 1.0] is right bottom, anchor[0.5, 0.5] is the center. You can use any values from 0 to 1 here, like [0,25, 0.75] if it looks good in battle engine. So in our case, we try to position center bottom of spell (anchor": [0.5, 1.0]) to bottom center of target ("point": "bc"). For non symmetrical spells you should also add "hflip": true to effects. It will mirror them when enemies use them on your party.

start_at allows to add pause before animation starts playing. Useful in some cases, for example for P2P class, when you have another animation playing before main animation.

  • target_sprite_damage_effect: how target reacts to be damaged by spell/attack. We have a number of hardcoded effects, adding new ones needs you to know how to code in renpy. initial_pause is pause before we show this effect, and duration is its duration in seconds. You can find the existing damage effects by searching for target_sprite_damage_effect in skills json files. or by checking show_target_sprite_damage_effect function in game\code\be\core.rpy
  • target_damage_effect: effect applied to damage numbers above target head. In theory it can support many effects, but we use only two so far:
    "target_damage_effect": {
        "gfx": ""
    }

which means no special effect, and

    "target_damage_effect": {
        "gfx": "battle_bounce"
    },

which means numbers will shake a bit.

  • target_death_effect: what happens to the target if it dies after spell/attack was used on it. In most cases we use dissolve, ie it slowly disappears. You already know what initial_pause and duration mean.
    "target_death_effect": {
        "gfx": "dissolve",
        "initial_pause": 0.6,
        "duration": 0.5
    }

For some ice based spells we use shatter, the target sprite will be shattered to pieces.

    "target_death_effect": {
        "gfx": "shatter",
        "initial_pause": 4.7,
        "duration": 0.2
    }
  • dodge_effect: if attack can be dodged, it could be used to set up timings to make dodging look cooler. Not a mandatory field.
    "dodge_effect": {
        "initial_pause": 0.95,
        "duration": 0.75
    }
  • arrow skills effects: Arrow skill class expects animations for firing (for example some facncy magical bow animation) and for projectile (for example some magical arrow). You add them in a usual way.
    "firing_effects": {
        "gfx": "emerald_bow_webm",
        "sfx": "content/sfx/sound/be/elf_bow.ogg",
        "duration": 0.86
    },
    "projectile_effects": {
        "gfx": "emerald_bow_arrow_webm",
        "duration": 0.76

Note that P2P_Skill class expects only projectile_effects.

Loading animations

There are different ways to load battle animations into the game, depending on animation itself.

  • If animation is a list of sprites of the same size, you can create a folder for it ingame\content\gfx\animationsand put animation inside. Sprites lists in this folder animated automatically. For example, folder named coin_top 0.13 1 will create in game animation called coin_top, with 0.13 seconds between frames, and looped. coin_top 0.13 would be non looped.
  • if your animation is a webm video (most of our animations are), you can put it into game\content\gfx\autowebm and name its folder in a special way. Folders there consist of 3 parts: animation name, the amount of loops and channel used by this animation. for example, angel_bow_webm 1 main_gfx_bow creates animation called angel_bow_webm which repeats one time and uses channel main_gfx_bow. While big_food_1 inf creates animation called big_food_1 and looped infinitely. I'll explain channels meaning now. In renpy video files use channels, and one channel can play one file. So in cases when we have to play two or more videos we have to set another channels to some of them. When you show how a bow fires an arrow, at some point you have no choice but to show bow and arrow videos at the same time, hence in such cases another channel is needed.
  • If your animation is a sprite sheet, you need to put it into game\content\gfx\be\filmstrips and then load adding a line into the code. For example, image simple_spray_attack = Transform(FilmStrip("content/gfx/be/filmstrips/spray.webp", (192, 192), (5, 8), .03, loop=False), zoom=1.2) where (192, 192) is size of one frame, (5, 8) is sheet rows and cols amount and .03 is time between frames.
  • For more precise control, if you have multiple sprites you can load them using renpy language.

image Fire Arrow cast:

"content/gfx/be/animations/flame_arrow/FlameArrow_1.webp"
pause .06
"content/gfx/be/animations/flame_arrow/FlameArrow_2.webp"
pause .06
"content/gfx/be/animations/flame_arrow/FlameArrow_3.webp"
pause .06
"content/gfx/be/animations/flame_arrow/FlameArrow_4.webp"
pause .06
"content/gfx/be/animations/flame_arrow/FlameArrow_5.webp"
pause .06
"content/gfx/be/animations/flame_arrow/FlameArrow_6.webp"
pause .06
"content/gfx/be/animations/flame_arrow/FlameArrow_7.webp"
pause .06
"content/gfx/be/animations/flame_arrow/FlameArrow_8.webp"
pause .09
"content/gfx/be/animations/flame_arrow/FlameArrow_9.webp"
pause .09
"content/gfx/be/animations/flame_arrow/FlameArrow_10.webp"
pause .12
"content/gfx/be/animations/flame_arrow/FlameArrow_11.webp"
pause .12
"content/gfx/be/animations/flame_arrow/FlameArrow_12.webp"
pause .12
"content/gfx/be/animations/flame_arrow/FlameArrow_13.webp"
pause .12

in this case we create animation called Fire Arrow cast, with different timing between frames.

Using the new spells/attacks

After you finished everything, the new attack/spell should be added to the game to be usable. Melee/ranged attacks could be added to some items, for spells you could create scrolls, and any of them can be used by mods if you allow them to.

Clone this wiki locally