Help with the default "slinging towel" throw

MadGear

Active member
Hey guys,

In the past, I have attempted to fix the default "slinging towel" throw for my mod, but I was unable to make a normal looking throw. The reason is because I just don't know the commands. I've tried looking inside other mods to see how it works but it just confused me because I don't know what I'm looking for.

@DCurrent You seem to kinda hate seeing that default towel throw, so can you help me fix it for my mod? Of course, if anyone else can also help me then I appreciate that. If I know the commands and where to place them, then I just might be able to fix it after all. I am willing to upload another version of the mod with the throws fixed and looking better.
 
No way to do it without script? Unfortunately, script isn't my strongest point, and yes, I did try to learn it. It just confused me and gave me a headache trying to make any sense of it 🤯
 
No way to do it without script? Unfortunately, script isn't my strongest point, and yes, I did try to learn it. It just confused me and gave me a headache trying to make any sense of it 🤯

Sorry, but script is the only feasible way. You are overcomplicating it in your mind - if you can learn native openbor commands, you can learn to use scripts. It's not like you have to write them yourself. There's already pre-made grappling systems in use across multiple modules. I also have a library for download, although I'll admit it's intended more for advanced users.

Check out some of @Bloodbane's modules. His system is very popular and I'm sure he'd be willing to help you.

DC
 
@MadGear

I don't know if it will fit in your projects, but these are the basic scripts used for grab moves on SORX. These are modified versions of the original @Bloodbane "slamstart" system, which in my opinion is the best system for grab moves.

C:
void grabStart()
{//Grab Starter for grab moves
 //Use SLAM or THROW after using this
    void self    = getlocalvar("self");
    void target    = getentityvar(self,"grabbed");

    //THERE'S NO TARGET SAVED IN THE ENTITYVAR??
    if(target == NULL()){
        target = getentityproperty(self, "grabbing"); //GET THE CURRENT GRABBED ONE

        if(target == NULL() || getentityproperty(target, "dead")){ //USED WHEN PLAYER DIES BY TIME OVER AND THE GRABBER IS THE ENEMY
            setidle(self);
        }
        else
        {
            setentityvar(self, "grabbed", target); //SAVE THE GRABBED ENTITY AS A NEW TARGET INSIDE AN ENTITYVAR
        }
    }
    
    target = getentityvar(self,"grabbed"); //FOR SAFE, GET THE TARGET AGAIN, THE NEW ONE THAT WAS SAVED INSIDE THE ENTITYVAR
    
    //THERE'S A NEW TARGET SAVED IN THE VARIABLE??
    if(target != NULL()){
        damageentity(target, self, 0, 1, openborconstant("ATK_NORMAL7"));
        changeentityproperty(target, "aiflag", "frozen", 1); //USED TO AVOID GRAB INTERRUPTION WHEN THE NODROPEN IS OFF AND ANY PLAYER IS RESPAWNED
    }
}

void grabStart2()
{//Grab Starter for non-grab moves
 //Use SLAM or THROW after using this
    void self    = getlocalvar("self");
    void target    = getentityvar(self,"grabbed");

    //THERE'S NO TARGET SAVED IN THE ENTITYVAR??
    if(target == NULL()){
        target = getentityproperty(self, "opponent"); //GET THE CURRENT GRABBED ONE

        if(target == NULL() || getentityproperty(target, "dead")){ //USED WHEN PLAYER DIES BY TIME OVER AND THE GRABBER IS THE ENEMY
            setidle(self);
        }
        else
        {
            setentityvar(self, "grabbed", target); //SAVE THE GRABBED ENTITY AS A NEW TARGET INSIDE AN ENTITYVAR
        }
    }
    
    target = getentityvar(self,"grabbed"); //FOR SAFE, GET THE TARGET AGAIN, THE NEW ONE THAT WAS SAVED INSIDE THE ENTITYVAR
    
    //THERE'S A NEW TARGET SAVED IN THE VARIABLE??
    if(target != NULL()){
        damageentity(target, self, 0, 1, openborconstant("ATK_NORMAL7"));
        changeentityproperty(target, "aiflag", "frozen", 1); //USED TO AVOID GRAB INTERRUPTION WHEN THE NODROPEN IS OFF AND ANY PLAYER IS RESPAWNED
    }
}

void position(int frame, float dx, float dy, float dz, int face)
{//Modify grabbed entity's position relative to grabber
 //Use grabstart 1st before using this
    void self    = getlocalvar("self");
    void target    = getentityvar(self,"grabbed");
    int dead    = getentityproperty(target,"dead");

    //THERE'S A NEW TARGET SAVED IN THE VARIABLE??
    if(target != NULL()){
        if(dead){ //USED WHEN PLAYER DIES BY TIME OVER AND THE GRABBER IS THE ENEMY
            bindentity(target, NULL());
            damageentity(target, self, 0, 1, openborconstant("ATK_NORMAL"));
            damageentity(self, self, 0, 1, openborconstant("ATK_NORMAL"));
            setentityvar(self, "grabbed", NULL());
        }
        else
        {
            updateframe(target, frame);
            bindentity(target, self, dx, dz, dy, face, 0);
        }
    }
}

void slam(int damage, int type, int Vx, int Vy, int Vz, int face)
{//Damage as slam finisher
    void self    = getlocalvar("self");
    void target    = getentityvar(self,"grabbed");
    int tDir    = getentityproperty(target,"direction");
    int vDir;
    
    if(face == 0){ //SAME FACING?
        vDir = tDir;
    }

    if(face == 1){ //OPPOSITE FACING?
        if(tDir == 0){ //FACING LEFT?
            vDir = 1;
        }
        else
        {
            vDir = 0;
        }
    }

    if(target != NULL()){
        void atkType;
        void eType        = getentityproperty(target,"type");
        int dir            = getentityproperty(target,"direction");
        int dropType    = 1;
        
        if(dir == 0){Vx = -Vx;}
        if(type == 1){atkType = openborconstant("ATK_NORMAL8");}
        if(type == 2){atkType = openborconstant("ATK_NORMAL9");}
        
        if(eType == openborconstant("TYPE_PLAYER") || eType == openborconstant("TYPE_NPC")){
            changeentityproperty(target, "projectilehit", "type_player", "type_npc", "type_obstacle");
        }
        else
        if(eType == openborconstant("TYPE_ENEMY")){
            changeentityproperty(target, "projectilehit", "type_enemy", "type_obstacle");
        }
        
        damageentity(target, self, damage/2, dropType, atkType); //SPLIT DAMAGE
        changeentityproperty(target, "direction", vDir);
        changeentityproperty(target, "aiflag", "projectile", 1);
        changeentityproperty(target, "damage_on_landing", damage/2); //RESET PROJECTILE STATUS TO 0 WHEN FALL ON THE GROUND, SPLIT DAMAGE
        finishGrab(Vx, Vy, Vz); //EXECUTE ALL NECESSARY TASKS TO END THE GRAB MOVE (ANTIWALL, UNBIND AND TOSSENTITY)
        setentityvar(self, "grabbed", NULL()); //CLEAR ENTITYVAR
    }
}

void throw(int damage, int type, int Vx, int Vy, int Vz, int face)
{//Damage as throw finisher
    void self    = getlocalvar("self");
    void target    = getentityvar(self,"grabbed");
    int z        = getentityproperty(self,"z");
    int tDir    = getentityproperty(target,"direction");
    int vDir;
    
    if(face == 0){ //SAME FACING?
        vDir = tDir;
    }

    if(face == 1){ //OPPOSITE FACING?
        if(tDir == 0){ //FACING LEFT?
            vDir = 1;
        }
        else
        {
            vDir = 0;
        }
    }

    if(target != NULL()){
        void atkType;
        void eType        = getentityproperty(target,"type");
        int dir            = getentityproperty(target,"direction");
        int dropType    = 1;
        
        if(dir == 0){Vx = -Vx;}
        if(type == 1){atkType = openborconstant("ATK_NORMAL8");}
        if(type == 2){atkType = openborconstant("ATK_NORMAL9");}
        if(z > (openborconstant("PLAYER_MIN_Z")+openborconstant("PLAYER_MAX_Z"))/2){Vz = -Vz ;}
        
        if(eType == openborconstant("TYPE_PLAYER") || eType == openborconstant("TYPE_NPC")){
            changeentityproperty(target, "projectilehit", "type_player", "type_npc", "type_obstacle");
        }
        else
        if(eType == openborconstant("TYPE_ENEMY")){
            changeentityproperty(target, "projectilehit", "type_enemy", "type_obstacle");
        }
        
        damageentity(target, self, 0, dropType, atkType);
        changeentityproperty(target, "direction", vDir);
        changeentityproperty(target, "aiflag", "projectile", 1);
        changeentityproperty(target, "damage_on_landing", damage); //RESET PROJECTILE STATUS TO 0 WHEN FALL ON THE GROUND, TOTAL DAMAGE
        finishGrab(Vx, Vy, Vz); //EXECUTE ALL NECESSARY TASKS TO END THE GRAB MOVE (ANTIWALL, UNBIND AND TOSSENTITY)
        setentityvar(self, "grabbed", NULL()); //CLEAR ENTITYVAR
    }
}

void finishGrab(int Vx, int Vy, int Vz)
{//This new function was created by mixing the "antiwall", "depost" and "tossentity" into a unique function
    void self    = getlocalvar("self");
    void target    = getentityvar(self, "grabbed");
    
    if(target != NULL()){
        int x        = getentityproperty(self, "x");
        int Tx        = getentityproperty(target, "x");
        int z        = getentityproperty(self, "z");
        int Tz        = getentityproperty(target, "z");
        int wall    = checkwall(Tx, Tz);

        if(wall){ //WAS DETECTED ANY WALL IN THE TARGET'S POSITION?? RUN ALL TASKS BELOW!!
            changeentityproperty(target, "position", x, z); //FIX THE GRABBED ENTITY'S POSITION ACCORDING TO GRABBER CORRDINATES BEFORE THE OPPONENT IS RELEASED
            bindentity(target, NULL()); //RELEASE GRABBED ENTITY, NOW THE OLD FUNCTION "DEPOST" WORKS HERE
            tossentity(target, Vy); //TOSS OPPONENT WITH Y VELOCITY ONLY, THIS WAY THE RELEASED OPPONENT WILL NOT BE MOVED AGAINST A WALL AGAIN
        }
        else //NO WALLS DETECTED?? ONLY THE DEFAULT TASKS WILL RUN
        {
            bindentity(target, NULL()); //RELEASE GRABBED ENTITY, NOW THE OLD FUNCTION "DEPOST" WORKS HERE
            tossentity(target, Vy, Vx, Vz); //TOSS OPPONENT WITH NORMAL VELOCITY, X, Y AND Z
        }
    }
}


And here's an example of how I'm using on the back slam for Max:

anim grabbackward #SLAM
attackone 0
fastattack 1
jugglecost 0
forcedirection 0
otg 1
loop 0
delay 24
offset 150 187
@cmd grabStart
@cmd position 0 20 0 0 0
@cmd hitfx "MediumKick.wav"
@cmd voice "Max3.wav"
frame data/chars/heroes/max/slam00.gif
delay 8
@cmd position 5 0 20 0 0
frame data/chars/heroes/max/slam01.gif
@cmd position 2 -40 35 0 0
frame data/chars/heroes/max/slam02.gif
delay 16
@cmd position 4 -60 -1 0 0
@cmd spawn01 "QuakeHigh" 0 0 0
attack 72 151 59 39 4 1 0 0 0 12
@cmd sound "Slam.wav"
frame data/chars/heroes/max/slam03.gif
@cmd slam 12 2 2 2 0 0
attack 0 0 0 0 0 0 0 0 0 0
frame data/chars/heroes/max/slam03.gif
delay 8
@cmd clearL
frame data/chars/heroes/max/slam04.gif
 
There's also this quick PDF manual, which helped me a lot in my quest for understanding how to customize enemy reaction to throw animation.

It's attached in this post, so you can read it, supposing you didn't do it before.
 

Attachments

Back
Top Bottom