How to limit enemy use of Freespecial

Aerisetta

Active member
In my game, I have a freespecial move for enemies, however the enemies are using it too often and I want them to use it less.

I thought about what if it had a mana cost, but according to the manual, enemies do not have mana. I don't want this move to drain enemy HP either.

Anyone have any advice?

(I am looking for a quick and simple solution to this, because I intend to create a new system around this freespecial, but I need a quick solution before the new system is in place)
 
You could put a throttling script to make the animation run with a lower priority. It can be done after a combo, the trick is not to assign AI range to it. It can also limit the attack with the life that the enemy has.

Code:
Example:

anim    follow5
@script
      void self = getlocalvar("self");
      int r = rand()%30;
      int life = getentityproperty(self,"health");
    if(frame == 0){

      if(r > 0){
        changeentityproperty(self, "animation", openborconstant("ANI_FOLLOW1"));
      }
    }
    @end_script   
    delay    20
    loop    1 6
    offset    119 171
    @cmd    velo001 0 0 0
    @cmd    checkally2 "Miranar" 6
    frame    data/chars/carlR/guard01.png
    delay    10
    @cmd    checkally2 "Miranar" 6
    frame    data/chars/carlR/guard01.png
    offset    123 171
    delay    8
    @cmd    checkally2 "Miranar" 6
    frame    data/chars/carlR/kick01.png
    frame    data/chars/carlR/kick01.png
    offset    119 171
    sound    data/chars/carlR/win01.wav
    frame    data/chars/carlR/win01.png
    frame    data/chars/carlR/win02.png
    delay    6
    frame    data/chars/carlR/win02.png
    frame    data/chars/carlR/win03.png
    frame    data/chars/carlR/win02.png
    frame    data/chars/carlR/win03.png

Code:
anim    follow5
@script
      void self = getlocalvar("self");
      int r = rand()%30;
      int life = getentityproperty(self,"health");
    if(frame == 0 life > 30 && life < 40){

      if(r > 0){
        changeentityproperty(self, "animation", openborconstant("ANI_FOLLOW1"));
      }
    }
    @end_script   
    delay    20
    loop    1 6
    offset    119 171
    @cmd    velo001 0 0 0
    @cmd    checkally2 "Miranar" 6
    frame    data/chars/carlR/guard01.png
    delay    10
    @cmd    checkally2 "Miranar" 6
    frame    data/chars/carlR/guard01.png
 
enemies do not have mana
@Aerisetta

I will check the manual again, it may be an outdated version, but enemies can use MP in the same way as players.
This is a good way to control the enemy's special/freespecial usage. For example, in the SOR2X I'm using this method to limit some strong attacks like Super, Counter and Rage.

In addition, the @Blade Master suggestion is a good one, you can cancel an animation into another randomly at the first frame.
 
I have done several experiments on the npcs in Openbor and yes, they can use MP (using the energycost command). But there are limitations, in whether they can spend the assigned mp with each attack that consumes mp, but they will continue to do the skills whether or not they have enough MP. In addition you can use a script to limit an NPC to do some animation if it doesn't have enough MP.

Examples:


Code:
@script//limiter for npcs, put in the start in the animation
    if(frame == 3){
      void self = getlocalvar("self");
      int mp = getentityproperty(self,"mp");
      int r = rand()%6;

      if(r == 0 && mp >= 30){
        performattack(self, openborconstant("ANI_FREESPECIAL9"));
       }

else if(r == 1 && mp >= 30){
        performattack(self, openborconstant("ANI_FREESPECIAL11"));
       }
else if(r == 2 && mp >= 20){
        performattack(self, openborconstant("ANI_FREESPECIAL6"));
       }
else if(r == 3 && mp >= 20) {
        performattack(self, openborconstant("ANI_FREESPECIAL8"));
       }
else if(r == 5) {
        performattack(self, openborconstant("ANI_FREESPECIAL20"));
       }
else {
        performattack(self, openborconstant("ANI_FOLLOW6"));
       }
    }
    @end_script
    
anim    freespecial4//Other Form
    offset    119 171
    delay    8   
    @cmd    mpcost 100
    sound    data/sfx/bigsword.wav
    @cmd    spawn01 "dust" 0 0 0
    @cmd    velo001 6 0 0
    frame    data/chars/carlR/run07.png
    @cmd    velo001 3 0 0
    frame    data/chars/carlR/run07.png
    delay    25
    @cmd    velo001 0 0 0
    frame    data/chars/carlR/run08.png   
    
    //script funtion
void mpcost(int val)
{// change the current mp
// Blade Master - 02/06/2022
    void self = getlocalvar("self"); //Get calling entity.
    int mp = getentityproperty(self,"mp");

if(mp > val){
    changeentityproperty(self, "mp", mp-val);}
else{changeentityproperty(self, "animation", openborconstant("ANI_CANT"));}
}
 
But there are limitations, in whether they can spend the assigned mp with each attack that consumes mp, but they will continue to do the skills whether or not they have enough MP
@Blade Master

Hmm it seems that something is missing in the way you are using the energycost. For example, in my case I'm using "energycost" function and there's no limitations, enemies act exactly the same as players. As an example, below you can see how the Abadede's rage move is coded.

Code:
anim freespecial #RAGE RUN
    range 90 120
    rangea 0 0
    rangez -24 24
    followanim 1
    followcond 1
    fastattack 1
    jugglecost 0
    forcedirection -1
    otg 1
    energycost 120 1 6
    loop    0
    delay    2
    offset    150 199
    @cmd hitfx "sor2_hit3.wav"
    @cmd voice "sor2_abadede_attack.wav"
    @cmd spawnRage "Rage" "Rage_B" 3 30 1
    frame    data/chars/bosses/abadede/special00.png
    frame    data/chars/bosses/abadede/special01.png
    frame    data/chars/bosses/abadede/special02.png

Note that you need to configure the energycost properly, otherwise the enemy will continue performing the special move using HP in case the MP ends, maybe it can be your case.

1671758435701.png
1671758458925.png
 
@Aerisetta

The manual needs some corrections, someone forgot to update the info about MP.

In the video below you can see the MP in action in the debug data above Abadede's head. Note that he only performs the Rage move twice and only when the MP is full with 120 points.

EDIT: This is a part of the engine source code related to the "energycost" function, where player/enemy/npc are coded together.

1672124398011.png
 

Attachments

  • 1672124210939.png
    1672124210939.png
    78.9 KB · Views: 2
Last edited:
@Aerisetta

I will check the manual again, it may be an outdated version, but enemies can use MP in the same way as players.
This is a good way to control the enemy's special/freespecial usage. For example, in the SOR2X I'm using this method to limit some strong attacks like Super, Counter and Rage.

In addition, the @Blade Master suggestion is a good one, you can cancel an animation into another randomly at the first frame.
First of all, I know this forum topic is old. I am trying to understand how the enemies behave in the OpenBor system, so I can create better AI enemies.
I have the following and it seems to work ok, but not what I wanted.
Code:
type    enemy
subtype    chase
.....
.....


anim    attack
    range  0 300
    rangez -40 40
    loop    0
  
    .....
    .....
  
anim    freespecial1
    loop    0
    .....
    .....
    ....
  
anim    freespecial2
    loop    0
    .....
    .....
    ....

When the player is within the range 0 and 300, the enemy seems 70-80% of the time performs the anim attack,
and sometimes performs freespecial1 or 2.
Can you please explain the behavior of the enemy between range, anim attack, freespecial skills, anim idle?
What I mean is in what condition, the enemies perform these animations without using range values?
I noticed sometimes the enemies being dragged to the player without any using run or idle animation.
I know I can add mp cost and random to limit the usage of certain skills.

Thank you very much
 
Last edited:
First of all, I know this forum topic is old. I am trying to understand how the enemies behave in the OpenBor system, so I can create better AI enemies.
I have the following and it seems to work ok, but not what I wanted.
Code:
type    enemy
subtype    chase
.....
.....


anim    attack
    range  0 300
    rangez -40 40
    loop    0
 
    .....
    .....
 
anim    freespecial1
    loop    0
    .....
    .....
    ....
 
anim    freespecial2
    loop    0
    .....
    .....
    ....

When the player is within the range 0 and 300, the enemy seems 70-80% of the time performs the anim attack,
and sometimes performs freespecial1 or 2.
Can you please explain the behavior of the enemy between range, anim attack, freespecial skills, anim idle?
What I mean is in what condition, the enemies perform these animations without using range values?
I noticed sometimes the enemies being dragged to the player without any using run or idle animation.
I know I can add mp cost and random to limit the usage of certain skills.

Thank you very much
Ranges are crucial for enemy A.I., you need to carefully adjust in case you want to customize enemy/npc behaviours. That said, I recommend to fill the range value in every attack animation, otherwise the engine will use default values and may not deliver the result you expect.

About the animation priority, if I'm not wrong it works from the lower to the higher numbers, meaning attack1 prioritized over attack2/3/4 etc. @DCurrent can confirm better than me.

1742929915519.png
 
About the animation priority, if I'm not wrong it works from the lower to the higher numbers, meaning attack1 prioritized over attack2/3/4 etc. @DCurrent can confirm better than me.

Correct. Attacks are coded with a slight bias toward lower numbers. Here's the engine code so you can see for yourself. This is the attack selection code for CPU entities.

C:
if(self->modeldata.chainlength > 1) // have a chain?
    {
        if(perform_atchain())
        {
            return;
        }
    }
    else if (self->ducking & DUCK_ACTIVE)
    {
        self->takeaction = common_attack_proc;
        set_attacking(self);
        ent_set_anim(self, ANI_DUCKATTACK, 0);
        return;
    }
    else // dont have a chain so just select an attack randomly
    {
        // Pick an attack
        for(i = 0; i < max_attacks; i++)
        {
            if( validanim(self, animattacks[i]) &&
                    check_range_target_all(self, target, animattacks[i], 0, 0))
            {
                // a trick to make attack 1 has a greater chance to be chosen
                // 6 5 4 3 2 1 1 1 1 1 ....
                for(j = ((5 - i) >= 0 ? (5 - i) : 0); j >= 0; j--)
                {
                    atkchoices[found++] = animattacks[i];
                }
            }
        }
        if(found > special)
        {
            self->takeaction = common_attack_proc;
            set_attacking(self);
            ent_set_anim(self, atkchoices[special + (rand32() & 0xffff) % (found - special)], 0);
            return;
        }
    }
    // if no attack was picked, just choose a random one from the valid list
    if(special && check_costmove(atkchoices[(rand32() & 0xffff) % special], 1, 0))
    {
        return;
    }
    // No attack to perform, return to A.I. root
    self->idling = IDLING_PREPARED;
    self->takeaction = NULL;
}

DC
 
Ranges are crucial for enemy A.I., you need to carefully adjust in case you want to customize enemy/npc behaviours. That said, I recommend to fill the range value in every attack animation, otherwise the engine will use default values and may not deliver the result you expect.

About the animation priority, if I'm not wrong it works from the lower to the higher numbers, meaning attack1 prioritized over attack2/3/4 etc. @DCurrent can confirm better than me.

View attachment 10392
Ahh I see, no wonder why they use anim attack most of the time. Thank so much for help!
 
Back
Top Bottom