The Hold and Release Key Triggers During Pause Menus or Text-Type Entities

maxman

Well-known member
There's something I have in mind that probably no one has asked before. It's the issue on holding and releasing key triggers during pause menus or text type entities. Recently, I created a key script for pressing a specific button to spawn an entity, something like a moveset for a certain character, so that I press it anytime and anywhere instead of just being on the ground to press for its moveset to pop out like freespecial. However, I created a new fake entity that is used for mainly holding specific key triggers after pressing a button via keyscript. I also created a new keyscript for that fake entity which you release the given button (pressed or held). It removes itself after releasing it. I use a keyscript for pressing a given to spawn an entity. You hold the same button after pressing it until the text type entity (e.g., character moveset, conversation, etc.) pops up.

Here's a problem of what I'm facing. It's a multiplayer issue regarding the button conflict during pauses. For example, player A controls player 1 while player B controls player 2. Player A presses/holds A2 button first before player B does. Both of them have held that button on their controllers they're using until player 1's moveset pops up due to player A being the first to press. The problem is not holding the A2 button during the pause entity. The problem is when you let go off of that A2 button during the pause menu or text type entity. What happens when you release A2 during that pause before cancelling it with any button? That fake entity keeps running without pressing that same button until it shows its moveset screen.

Here's a chart on what I'm trying to point out on the multiplayer issue.
Button Control EventPlayer APlayer BNote
Who presses/holds a specific button for showing movelist first?YesNoPlayer A holds/presses A2 button first before player B
Who holds the button before their own character moveset shows up?YesYesBoth are holding A2 buttons for their movesets to show
While the first one shows moveset on screen, what would you do during the pause menu or text type entity playing? (alternative 1)Cancel by pressing any buttonRelease A2 buttonThis results the fake entity to play by itself without controlling it after player B releases the button during the pause moment when player A cancels it
While the first one shows moveset on screen, what would you do during the pause menu or text type entity playing? (alternative 2)Cancel by pressing any buttonHold A2 button before unpausing the game or player A cancels his ownPlayer B can continue holding A2 button for his character's own moveset to show


Here's an example of a keyscript for one particular character.

Example:

kuraaku.c:
C:
#import "data/scripts/animation/default.c"

void main(){
    if(openborvariant("in_level")){

        int P1 = getplayerproperty(0, "ent");
        int P2 = getplayerproperty(1, "ent");
        int P3 = getplayerproperty(2, "ent");
        int attack4 = playerkeys(0, 1, "attack4");
        int attack42 = playerkeys(1, 1, "attack4");
        int attack43 = playerkeys(2, 1, "attack4");
        int movelist = getglobalvar("movelist");
        int movelist2 = getglobalvar("movelist2");
        int movelist3 = getglobalvar("movelist3");
        void vSpawn;
        clearspawnentry();
        loadmodel("movelist");
        setspawnentry("name", "movelist");

        if(attack4){
            if(movelist == NULL()){
                setglobalvar("movelist", 1);
                vSpawn = spawn004("movelist", "movelist", 0, 174, 0, 224, 0, 0, openborconstant("ANI_FOLLOW1), 0);
            }else{
                setglobalvar("movelist", NULL());
                killentity(vSpawn);
            }
        }

        if(attack42){
            if(movelist2 == NULL()){
                setglobalvar("movelist2", 1);
                vSpawn = spawn004("movelist", "movelist", 0, 174, 0, 224, 0, 0, openborconstant("ANI_FOLLOW1"), 0);
            }else{
                setglobalvar("movelist2", NULL());
                killentity(vSpawn);
            }
        }

        if(attack43){
            if(movelist3 == NULL()){
                setglobalvar("movelist3", 1);
                vSpawn = spawn004("movelist", "movelist", 0, 174, 0, 224, 0, 0, openborconstant("ANI_FOLLOW1"), 0);
            }else{
                setglobalvar("movelist3", NULL());
                killentity(vSpawn);
            }
        }

    }
}

Fake entity for holding keys:
Code:
name movelist
type none
load teriiport
load kuraakuport

animationscript data/scripts/lib001.c
script data/scripts/movelist.c

anim idle #Terry
    delay 5
    offset 1 1
    @cmd keyint "ANI_IDLE" 0 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 1 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 2 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 3 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 4 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 5 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 6 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 7 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 8 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 9 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 10 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 11 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 12 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 13 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 14 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 15 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 16 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 17 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 18 "A3" 0 0
    frame none
    @cmd spawn06noDir "teriiport" 174 0 228
    @cmd setglobalvar "movelist" NULL()
    @cmd setglobalvar "movelist2" NULL()
    @cmd setglobalvar "movelist3" NULL()
    @cmd suicide
    frame none
 
anim follow1 #Clark
    delay 5
    offset 1 1
    @cmd keyint "ANI_IDLE" 0 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 1 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 2 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 3 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 4 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 5 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 6 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 7 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 8 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 9 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 10 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 11 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 12 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 13 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 14 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 15 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 16 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 17 "A3" 0 0
    frame none
    @cmd keyint "ANI_IDLE" 18 "A3" 0 0
    frame none
    @cmd spawn06noDir "kuraakuport" 174 0 228
    @cmd setglobalvar "movelist" NULL()
    @cmd setglobalvar "movelist2" NULL()
    @cmd setglobalvar "movelist3" NULL()
    @cmd suicide
    frame none

Here's a (key)script for the movelist entity.
Code:
void main(){
    void self = getlocalvar("self");
    void parent = getentityproperty(self, "parent");
    int P1 = getplayerproperty(0, "ent");
    int P2 = getplayerproperty(1, "ent");
    int P3 = getplayerproperty(2, "ent");
    int attack4r; int attack4h;
    int frame = getentityproperty(self, "animpos");
    int movelist = getglobalvar("movelist");
    int movelist2 = getglobalvar("movelist2");
    int movelist3 = getglobalvar("movelist3");

    if(frame >= 0 && frame < 19 && movelist == 1){
        if(parent == P1){
            attack4r = playerkeys(0, 2, "attack4");
            attack4h = playerkeys(0, 0, "attack4");
            if(attack4r){
                setglobalvar("movelist", NULL());
                killentity(self);
            }
        }
    }

    if(frame >= 0 && frame < 19 && movelist2 == 1){
        if(parent == P2){
            attack4r = playerkeys(1, 2, "attack4");
            attack4h = playerkeys(1, 0, "attack4");
            if(attack4r){
                setglobalvar("movelist2", NULL());
                killentity(self);
            }
        }
    }

    if(frame >= 0 && frame < 19 && movelist3 == 1){
        if(parent == P3){
            attack4r = playerkeys(2, 2, "attack4");
            attack4h = playerkeys(2, 0, "attack4");
            if(attack4r){
                setglobalvar("movelist3", NULL());
                killentity(self);
            }
        }
    }

}

As for the movelist spawning in different animations based on the specified given player, here's an example.

Code:
name    kuraakuport
health  1
type    text
facing  1
setlayer 999999999
subject_to_hole 0
subject_to_obstacle 0
subject_to_platform 0
subject_to_gravity 0

animationscript data/scripts/animation/default.c

anim idle
     loop    0
     delay   2
     offset  174 224
     bbox    0 0 0 0
     drawmethod alpha 6
     drawmethod channel 0.10
     frame    data/sprites/portraits/axel-kof-rod.png
     drawmethod channel 0.20
     frame    data/sprites/portraits/axel-kof-rod.png
     drawmethod channel 0.30
     frame    data/sprites/portraits/axel-kof-rod.png
     drawmethod channel 0.40
     frame    data/sprites/portraits/axel-kof-rod.png
     drawmethod channel 0.50
     frame    data/sprites/portraits/axel-kof-rod.png
     drawmethod channel 0.60
     frame    data/sprites/portraits/axel-kof-rod.png
     drawmethod channel 0.70
     frame    data/sprites/portraits/axel-kof-rod.png
     drawmethod channel 0.80
     frame    data/sprites/portraits/axel-kof-rod.png
     drawmethod channel 0.90
     frame    data/sprites/portraits/axel-kof-rod.png
     delay   -1
     drawmethod alpha 0
     frame    data/sprites/portraits/axel-kof-rod.png
     @cmd suicide
     frame   data/chars/misc/empty.gif

Code:
name Clark
type player
speed 9
health 100
mp 100
gfxshadow 1
atchain 1 2 3 4 5
icon    data/chars/heroes/clark/clark.png 1
keyscript data/scripts/keys/kuraaku.c

How can I avoid the button conflict of multiple players from happening?
 
Last edited:
I'm still trying to grasp how all of this works but let me ask these:
why could all players spawn same movelist?
Because each player character needs to have a movelist (pose as fake entity) of his/her own by different animation for holding the key/trigger with animation script after pressing with keyscript initially. I didn't/don't have to make a separate blank entity for spawning each moveset per character. The movelist acts as a child to spawn from a player parent. At first, it was very hard for me to come up with that idea to create a trigger for holding a button while not having a character to perform any action within button from an animation script. The original idea was to hold a button without a character reacting to a button press/hold. It took me a long time for that, after somebody (accidentally) pressed the buttons like crazy.

What I'd like to do with it: Hold button without character reacting with different animations like you're not pressing any button to perform an action at all. This is why I came up with the idea of a keyscript to press for spawning it, while holding a button when it exists until it's released for it to remove itself.

For example, Terry has a has his own keyscript for calling the moveset but with a different animation ID. Since Terry's the base, I gave the movelist anim idle for him.

terii.c:
Code:
#import "data/scripts/animation/default.c"

void main(){
    if(openborvariant("in_level")){

        int attack4 = playerkeys(0, 1, "attack4");
        int attack42 = playerkeys(1, 1, "attack4");
        int attack43 = playerkeys(2, 1, "attack4");
        int movelist = getglobalvar("movelist");
        int movelist2 = getglobalvar("movelist2");
        int movelist3 = getglobalvar("movelist3");
        void vSpawn;
        clearspawnentry();
        loadmodel("movelist");
        setspawnentry("name", "movelist");

        if(attack4){
            if(movelist == NULL()){
                setglobalvar("movelist", 1);
                vSpawn = spawn004("movelist", "movelist", 0, 174, 0, 224, 0, 0, openborconstant("ANI_IDLE"), 0);
            }else{
                setglobalvar("movelist", NULL());
                killentity(vSpawn);
            }
        }

        if(attack42){
            if(movelist2 == NULL()){
                setglobalvar("movelist2", 1);
                vSpawn = spawn004("movelist", "movelist", 0, 174, 0, 224, 0, 0, openborconstant("ANI_IDLE"), 0);
            }else{
                setglobalvar("movelist2", NULL());
                killentity(vSpawn);
            }
        }

        if(attack43){
            if(movelist3 == NULL()){
                setglobalvar("movelist3", 1);
                vSpawn = spawn004("movelist", "movelist", 0, 174, 0, 224, 0, 0, openborconstant("ANI_IDLE"), 0);
            }else{
                setglobalvar("movelist3", NULL());
                killentity(vSpawn);
            }
        }

    }
}

To understand this issue, imagine how two players play one OpenBOR game in real life. Both players are still playing, but one of them presses buttons like crazy including pressing the moveset button (moveset as text type like MKTCO and AOFTS), which stops/pauses the action from playing. One of them has to unpause it so that they continue to play. I think the problem to me is that when you mash buttons randomly like crazy as a player, it starts an accident as a pause or stop to halt the action. For example, let's say that you're a patient player and I'm an impatient player. You are the one who stays a lot more focused on your character's surroundings while I'm the one who keeps mashing buttons like crazy. You do the action, but I accidentally press a movelist button to show without holding it all. This results a pause after pressing it.

If you have a better idea than what I have now, that would be worth using.

According to how keyscripts are used, this is the information.

Key scripts can be extremely powerful, but if not used properly will prove equally frustrating. Keep in mind the following when creating your key scripts:

First, it is important to remember that key scripts fire both on press AND release of a key. This means a single key press actually runs a given keyscript twice; once when a player presses the key and again when he/she lets go. Make sure to account for this in your scripts or you will receive unexpected results. The playerkeys() function allows easy differentiation between press or release.

Another consideration is to know the order in which key events run. If you have multiple key scripts overlapping each other, it is vital to know and account for the order in which the engine will process each. From first to last:

  1. Level keyscript#
  2. Entity keyscript
  3. Global key#.c
  4. Global keyall.c
  5. Default key action.
Another facet to keep in mind is the use of AI Flags and the takeaction() function. Without them the engine may override your scripted command milliseconds before it has a chance to occur. A common example would be trying to execute a custom attack while walking. Simply setting your desired animation won't be enough because the engine will set the walk animation right back. But by including the correct caveat functions in your script, the engine will "wait" and execute the desired actions properly.

While fairly obvious, you should keep in mind the key event itself should be canceled when you are finished with it. Otherwise the engine will still run the key's default action immediately after all key scripts are complete. While in rare cases this might be useful, in most it will at best override your intended action and at worse produce a bug or crash.

During pause mode (whether it's text type entity or game pause), you hold the button before it unpauses, which is okay. But when you release the button before it unpauses, the movelist animation plays until it reaches to the last frame (even when you try to press it again to stop but can't), and that's the problem.
 
@maxman, I'm sorry, but this entire system needs to be scrapped and taken to ground. If you want a move-list dialog while holding a key, you don't need a model at all, and you certainly don't need an entity just for handling key inputs.

You can generate the move-list directly with drawsprite(), or better yet, draw it to a subscreen. The trigger event can be tied an either key#.c or to the parent entity you're showing move-sets for.

To prevent interference from other players, you verify no other move-lists are active. If another move-list is active, then you simply ignore the request. If the game is paused, you disable the other player's key inputs fully.

C:
changeplayerproperty(player_index, "playkeys", 0);
changeplayerproperty(player_index, "newkeys", 0);

Once a move-list is on screen, other players can mash all day, and nothing happens. As an example, this is how the random select feature I helped @O Ilusionista with works in Pocket Dimensional Clash. Once a random select initiates, keys are disabled until it completes.


DC
 
Back
Top Bottom