Duck Animation Suggestion for 2D Platforms

maxman

Well-known member
I was playing Naruto awhile ago and I've known that playable characters from most mods play idle animation while releasing and after holding a duck button. We know that some characters duck with one sprite frame in an animation with pressing, holding, and releasing it, and it's no problem. The problem is that when using more than 1 frames in a duck anim like 2-4 or over, you can see that how many frame played in that anim for holding, but when you release it, it reverts back to anim idle instead of playing something like an anim unduck (for standing up for ducking; no such word for "unduck" in OpenBOR). Example, when you hold down, then frame 1 ("I'm about to get down") plays, then frame 2 ("I'm half way to get down"), and lastly frame 3 ("OK. Here I'm down here!"). But when releasing it, it's back to idle anim ("Hey that's funny. I was down here and then all of a sudden I'm back to standing up without moving up from being down here. That's probably because the engine isn't interactive enough for the duck").

anim duck

offset 23 50
delay 5
frame data/chars/mikey/duck1.png
frame data/chars/mikey/duck2.png
frame data/chars/mikey/duck3.png
frame data/chars/mikey/duck4.png #frame 4 should refer to be played all the way to frame 1 after releasing a down button from ducking and should NOT revert to anim idle after releasing it
frame data/chars/mikey/duck5.png
 
I use this to go back to duck anim

Code:
void aniframe(void Ani, int Frame){	
	// Changes anim and select frame
    void self = getlocalvar("self"); // Get self

    changeentityproperty(self, "animation", openborconstant(Ani));
	changeentityproperty(self, "animpos", Frame);
}

@cmd aniframe "ANI_DUCK" 0

Use it on duckattack last frame
 
EDIT: Fixed the problem. Deleted my other posts. They didn't really bring anything helpful to the conversation.

HEY!!! I finally managed to fix it! YAAAAR!
I had a totally unnecessary one of these  } things at the end. I used Goliath's script, but made some very small changes to suit what I needed. For anyone using this to fix their own issue, the below is a KEYSCRIPT. You should be able to cut/copy/paste, directly into your own existing KEYSCRIPT without too much fuss. Secondly:
- FOLLOW2 is the ANIM I'm using to make the character get up from crouching/ducking.
- FOLLOW3 is the ANIM I'm using to make the character crawl along the floor/duck-walk.

//Duckrise
    if(iDownR){ //Down Released?
      if(vAniID == openborconstant("ANI_DUCK") ){ //Ducking?
        performattack(vSelf, openborconstant("ANI_FOLLOW2") );
      }
    }

//Crawl
  if(vAniID == openborconstant("ANI_DUCK")){  // Duck?
      if (iRight){ // New key status of right pressed
        performattack(vSelf, openborconstant("ANI_FOLLOW3")); // Change anim to duck walk
        changeentityproperty(vSelf, "direction", 1); // Change to right facing
        changeentityproperty(vSelf, "velocity", 0.5, 0, 0); //Move
      }
      if (iLeft){  // New key status of left pressed
        performattack(vSelf, openborconstant("ANI_FOLLOW3")); // Change anim to duck walk
        changeentityproperty(vSelf, "direction", 0); // Change to left facing
        changeentityproperty(vSelf, "velocity", -0.5, 0, 0); //Move
      }
    }

//Crawlstop
    if(vAniID == openborconstant("ANI_FOLLOW3")){ // Duckwalk anim?
if (iDownR){ // Down key released?
changeentityproperty(vSelf, "animation", openborconstant("ANI_FOLLOW2"));
// Change to duck anim
changeentityproperty(vSelf, "velocity", 0, 0, 0); //Stop
}
else if (iRightR || iLeftR){ // Right ot Left key releassed?
changeentityproperty(vSelf, "animation", openborconstant("ANI_FOLLOW2"));
// Change to duck anim
changeentityproperty(vSelf, "velocity", 0, 0, 0); //Stop
}
else if (iAttack){ // Attack key press?
changeentityproperty(vSelf, "animation", openborconstant("ANI_DUCKATTACK"));
// Change to duckattack anim
changeentityproperty(vSelf, "velocity", 0, 0, 0); //Stop
}
    }

Thank you so much Maxman + Goliath! Now all that's left to do is get duck-attacking down... about that:
I understand that this here below is an ANIMATIONSCRIPT:
Code:
void aniframe(void Ani, int Frame){	
	// Changes anim and select frame
    void self = getlocalvar("self"); // Get self

    changeentityproperty(self, "animation", openborconstant(Ani));
	changeentityproperty(self, "animpos", Frame);

...And I also understand that this goes in the character's DUCKATTACK anim:
Code:
@cmd aniframe "ANI_DUCK" 0

I've pasted exactly as is and how you said to, but still can't get it working. Have i done this incorrectly?

 
You can choose what frame you want to switch to.

Lets say that your duck anim has 10 frames in total.
First 5 frames for crawling, and last 5 frames for duck loop.
Then all you need to do is use the script i posted aniframe, on duckattack last frame, AND SELECT the frame where the duck loop starts.

In this case frame 6.

So: @cmd aniframe "ANI_DUCK" 5

This + keys will do the job.

Command = @cmd
 
Ok, so here's where I am. Below is my Duck anim:

        anim duck
loop         1 3 4
delay 6
offset 55 78
bbox         48 32 20 46
frame data/chars/pc-dredd/duck01.PNG
bbox         48 37 20 41
frame data/chars/pc-dredd/duck02.PNG
bbox         48 42 20 37
frame data/chars/pc-dredd/duck03.PNG
bbox         48 49 20 29
delay 2
frame data/chars/pc-dredd/duck04.PNG
frame data/chars/pc-dredd/duck04.PNG
delay 6
bbox         48 42 20 37
frame data/chars/pc-dredd/duck03.PNG
bbox         48 37 20 41
frame data/chars/pc-dredd/duck02.PNG
bbox         48 32 20 46
frame data/chars/pc-dredd/duck01.PNG

This is the duck attack:
 
        anim        duckattack
delay 6
offset 55 78
bbox         46 43 18 35
frame data/chars/pc-dredd/atkd01.PNG
frame data/chars/pc-dredd/atkd02.PNG
delay 4
attack 52 50 31 9 5 0 0 1 0 0
frame data/chars/pc-dredd/atkd04.PNG
delay 12
frame data/chars/pc-dredd/atkd03.PNG
delay 6
attack 0 0 0 0 0 0 0 0 0 0
frame data/chars/pc-dredd/atkd02.PNG
@cmd aniframe "ANI_DUCK" 3
frame data/chars/pc-dredd/atkd01.PNG


I figured the "0" at the end of the @cmd aniframe, should be the frame where the character is actually kneeling. Unfortunately, the result is not what i'd hoped. He still plays the entire ducking anim after attacking, but now he can only attack once without having to refresh the animation. IOW, any futher presses of the attack button result in nothing happening at all. I have to go back to IDLE or any other animation before i can duck and punch again. :/
I've gotta be missing something here.
 
Look at this video:

http://youtu.be/xHgfuOXFMk4

Player Anims:

Code:
anim	duck
	loop	1 4
	delay	6
	offset	49 83
	bbox	41 49 18 35
	cancel	5 11 0 A2 freespecial4				#THROW GRANADE DUCK // GO BACK TO IDLE¿?
	@cmd	keyint2 "ANI_FOLLOW15" 0 "D" 0 0	#DUCK2
		frame	data/chars/marco/dck00.png
		frame	data/chars/marco/dck01.png
		frame	data/chars/marco/dck02.png
	bbox	40 61 18 23
		frame	data/chars/marco/dck03.png
	delay	10
		frame	data/chars/marco/dck04.png		#ANIMPOS 4
		frame	data/chars/marco/dck05.png
		frame	data/chars/marco/dck06.png
		frame	data/chars/marco/dck07.png
		frame	data/chars/marco/dck06.png
		frame	data/chars/marco/dck05.png
		frame	data/chars/marco/dck04.png
		
#-------DUCK2
anim	follow15
	loop	1 4
	delay	6
	offset	49 83
	bbox	41 49 18 35
	cancel	5 11 0 A2 freespecial4
		frame	data/chars/marco/dck00.png
		frame	data/chars/marco/dck01.png
		frame	data/chars/marco/dck02.png
	bbox	40 61 18 23
		frame	data/chars/marco/dck03.png
	delay	10
		frame	data/chars/marco/dck04.png
		frame	data/chars/marco/dck05.png
		frame	data/chars/marco/dck06.png
		frame	data/chars/marco/dck07.png
		frame	data/chars/marco/dck06.png
		frame	data/chars/marco/dck05.png
		frame	data/chars/marco/dck04.png
		
#------DUCK SHOOT
anim	duckattack
	delay	1
	offset	49 83
	bbox	40 60 19 24
		frame	data/chars/marco/dckat00.png
	delay	6
	@cmd rangeAnim 0 35 34 "ANI_ATTACK4"			#RANGE ATTACK
		frame	data/chars/marco/dckat00.png
	@cmd	shooter "Bullet" 32 16 3 0
		frame	data/chars/marco/dckat01.png
		frame	data/chars/marco/dckat02.png
		frame	data/chars/marco/dckat03.png
		frame	data/chars/marco/dckat04.png
		frame	data/chars/marco/dckat05.png
		frame	data/chars/marco/dckat06.png
		frame	data/chars/marco/dckat07.png
		frame	data/chars/marco/dckat08.png
	@cmd	aniframe "ANI_DUCK"	4					#BACk TO DUCK
		frame	data/chars/marco/dckat09.png

Player keys:

Code:
if(vAniID == openborconstant("ANI_DUCKATTACK"))
	{ // Duckattack anim?
		if(iRight)
		{ // Right key press
			changeentityproperty(vSelf, "animation", openborconstant("ANI_FOLLOW10"));
			// Change to Duck Walk
			changeentityproperty(vSelf, "direction", 1);
			// Change to right facing
			changeentityproperty(vSelf, "velocity", 0.5, 0, 0);
			// Set velocity
		}
		else if(iLeft)
		{ // Left key press
			changeentityproperty(vSelf, "animation", openborconstant("ANI_FOLLOW10"));
			// Change to Duck Walk
			changeentityproperty(vSelf, "direction", 0);
			// Change to left facing
			changeentityproperty(vSelf, "velocity", -0.5, 0, 0);
			// Set velocity
		}
		else if(iDownR)
		{ // Down key released?
			changeentityproperty(vSelf, "animation", openborconstant("ANI_IDLE"));
			// Change to idle anim
			changeentityproperty(vSelf, "aiflag", "idling", 1);
			// Turns on idle status
		}
		else if(iJump)
		{ // Jump key pressed?
			changeentityproperty(vSelf, "animation", openborconstant("ANI_FORWARDJUMP"));
			// Change to jump anim
			changeentityproperty(vSelf, "aiflag", "idling", 0);
			// Set idling status off
			changeentityproperty(vSelf, "aiflag", "jumping", 1);
			// Set jump status on
			changeentityproperty(vSelf, "takeaction", "common_jump");
			// Set jump status on
			tossentity(vSelf, 3.5, 0, 0); // Jump
		}
		else if (vAniPos == 4 || vAniPos == 5 || vAniPos == 6 || vAniPos == 7 || vAniPos == 8 || vAniPos == 9 || vAniPos == 10)
		{ // Select frames
			if(iAttack)
			{ // Attack key press
				changeentityproperty(vSelf, "animation", openborconstant("ANI_DUCKATTACK"));
				// Change to duckttack anim
			}
		}
	}
if(vAniID == openborconstant("ANI_FOLLOW15"))
	{ // Duck2 anim?
		if(iDownR)
		{ // Down key relesed?
			changeentityproperty(vSelf, "animation", openborconstant("ANI_IDLE"));
			// Change to IDLE anim
			changeentityproperty(vSelf, "velocity", 0, 0, 0);
			// Set velocity
			changeentityproperty(vSelf, "aiflag", "idling", 1);
			// Change to idle status
		}
		else if (vAniPos == 3 || vAniPos == 4 || vAniPos == 5 || vAniPos == 6 || vAniPos == 7 || vAniPos == 8 || vAniPos == 9 || vAniPos == 10)
		{ // Select frames
			if(iAttack)
			{ // Attack key press
				changeentityproperty(vSelf, "animation", openborconstant("ANI_DUCKATTACK"));
				// Change to Duckattack anim
			}
			else if(iRight)
			{ // Right key press
				changeentityproperty(vSelf, "animation", openborconstant("ANI_FOLLOW10"));
				// Change to Duck Walk
				changeentityproperty(vSelf, "direction", 1);
				// Change to right facing
				changeentityproperty(vSelf, "velocity", 0.5, 0, 0);
				// Set velocity
			}
			else if(iLeft)
			{ // Left key press
				changeentityproperty(vSelf, "animation", openborconstant("ANI_FOLLOW10"));
				// Change to Duck Walk
				changeentityproperty(vSelf, "direction", 0);
				// Change to left facing
				changeentityproperty(vSelf, "velocity", -0.5, 0, 0);
				// Set velocity
			}
			else if(iJump)
			{ // Jump key pressed?
				changeentityproperty(vSelf, "animation", openborconstant("ANI_JUMP"));
				// Change to jump anim
				changeentityproperty(vSelf, "aiflag", "idling", 0);
				// Set idling status off
				changeentityproperty(vSelf, "aiflag", "jumping", 1);
				// Set jump status on
				changeentityproperty(vSelf, "takeaction", "common_jump");
				// Set jump status on
				tossentity(vSelf, 3.5, 0, 0); // Jump
			}
			else if(iAttack)
			{ // Attack key press
				changeentityproperty(vSelf, "animation", openborconstant("ANI_DUCKATTACK"));
				// Change to Duckattack anim
			}
		}
	}

I use and old bor version on this demo, don´t know if it works on more recent.
 
In old build, this function:

Code:
void aniframe(void Ani, int Frame){	
	// Changes anim and select frame
    void self = getlocalvar("self"); // Get self

    changeentityproperty(self, "animation", openborconstant(Ani));
	changeentityproperty(self, "animpos", Frame);
}

works well but in newer build, you must add a little fix shown with red color:

void aniframe(void Ani, int Frame){
// Changes anim and select frame
    void self = getlocalvar("self"); // Get self

    changeentityproperty(self, "animation", openborconstant(Ani), 2);
changeentityproperty(self, "animpos", Frame);
}
 
HEY HEY! IT WORKS!
Well, sort of. It still has the problem of only letting you attack only once before having to somehow refresh the animation.

EDIT: Huzzah! it's fixed!
Had to add this:
Code:
//Really hacky Duck Attack fix
    if(iAttack){ //Attack button pressed?
      if(vAniID == openborconstant("ANI_DUCK") ){ //Blocking?
        performattack(vSelf, openborconstant("ANI_DUCKATTACK") );
      }
    }
to the KEYSCRIPT, but now it fires without a hitch. :D
Does this count as FIXED/Answered now?
 
Bloodbane said:
void aniframe(void Ani, int Frame){
// Changes anim and select frame
    void self = getlocalvar("self"); // Get self

    changeentityproperty(self, "animation", openborconstant(Ani), 2);
changeentityproperty(self, "animpos", Frame);
}

Added new parameter to change anim function?

I check the manual and says:

changeentityproperty(ent, property, value1, value2, ...)

ent
    Target entity.
property
    String name of the property you wish to change. See the individual properties for their specific behaviors.
value
    New value. Data type will vary depending on the property. Most properties only accept a single value, but some may allow and/or require several. See the individual properties for details.

Does not especify any new parameter¿?

SaintJudas said:
HEY HEY! IT WORKS!

I´m glad you finally achive it.
 
I´m glad you finally achive it.
Couldn't have done it without your help brother. Thank you!

Though I will test to be sure, but using cancel to perform a Ducking-Attack doesn't seem to have the same problem a standard DUCKATTACK does. (Read above. Can attack once only etc.) Once I've got everything figured out perfectly, I want to post a Tutorial for anyone having the same issue or just new people wanting to do new stuff.

Next: I would like to do the same with DuckWalk/Crawl. The way I have it set up, Releasing either the Down or Forward key makes the character go directly to FOLLOW2 (my getting up from ducking anim).
It works perfectly if i release Down while still holding Forward (Get ups, then starts walking.).
However, If I should release Forward While still holding Down, FOLLOW2 will play (Gets up, then gets back down.).The above is happening specifically because I have it set that way right now in the KEYSCRIPT. It's posted on the previous page.

I changed it to "ANI_DUCK", then tried to use aniframe the same way as in the DUCKATTACK. The Problem I think, is that FOLLOW3(My Crawl/DuckWalk anim) has 12 frames and is meant to be looped. It's fine if i stop Crawling/DuckWalking before it is supposed to loop.
The problem occurs after the 12th frame. The loop cycle breaks once Aniframe is used, and the character continues to slide forward in the called frame(Duck04.png in my case) like a crouching Gumby. Sorry if it's a little wordy, But I like to give as much information as possible when discussing an issue. It helps to avoid confusion if I can provide any pertinent details. :D 
 
I realize now that DC was right about using loopstart and loopend for looping the duck animation with script after I watched Goliath's duck animation video and tested the duck out myself. Also, I realize that using cancel works thanks to Goliath and Beastie so I'm no longer using the if playerkeys movements under @script command from several posts ago. (e.g. playerkeys(getentityproperty(self, "playerindex"), 0, "movedown" with attacks.) It was funny that I could press/hold any attack button to make him stand attack and then press down to make him crouch attack which makes a 2-combo hit. :P (ridiculous and horrendous of what I did before.) I think I did jump to conclusions and got lost learning to script the duck after duckattack until Goliath posted a vid with script provided for ducking. That helped.

This is what I had done so far.
Code:
anim	duck
	bbox	4 0 46 95
	offset	24 91
	loop	1 2 5
	delay	4
	cancel	2 5 0 a2 freespecial6
	cancel	2 5 0 a3 freespecial7
	cancel	2 5 0 j freespecial8
	cancel	2 5 0 s freespecial9
	cancel	2 5 0 a4 freespecial10
	frame	data/chars/ryu/0001000000.gif
	bbox	3 1 49 75
	offset	22 72
	frame	data/chars/ryu/0001000001.gif
	bbox	3 0 49 73
	offset	23 66
	delay	20
	frame	data/chars/ryu/0001000002.gif
	frame	data/chars/ryu/0001000002.gif
	frame	data/chars/ryu/0001000002.gif
	frame	data/chars/ryu/0001000002.gif

Code:
anim	duckattack
	delay	4
	bbox	7 1 46 68
	offset	22 65
	frame	data/chars/ryu/278.gif
	attack	43 5 46 21 3 0 1 0 0 0
	frame	data/chars/ryu/279.gif
	frame	data/chars/ryu/280.gif
	offset	23 66
	frame	data/chars/ryu/281.gif
	delay	1
	frame	data/chars/ryu/281.gif
	@cmd	aniframe "ANI_DUCK" 2
	frame	data/chars/ryu/281.gif

Even the key script too, I did some edits. :)
Code:
void main()
{
    int iPlIndex = getlocalvar("player"); //Get calling player
    void vSelf = getplayerproperty(iPlIndex , "entity"); //Get calling entity
	void vTarget = getentityproperty(vSelf, "opponent"); //Get calling opponent
	//void vTarget = getlocalvar(self, "opponent);
	//void vTarget = findtarget("opponent"); //Get nearest opponent to caller.
    void vAniID = getentityproperty(vSelf,"animationID"); //Get current animation ID
    void vAniPos = getentityproperty(vSelf, "animpos"); //Get current animation frame
	int iFrame = getentityproperty(vSelf, "animpos"); //Caller's frame
    int iDir = getentityproperty(vSelf, "direction");  //Get current facing direction

    void iUp = playerkeys(iPlIndex, 1, "moveup"); // New key status of "Up"
    void iDown = playerkeys(iPlIndex, 1, "movedown"); // New key status of "Down"
    void iLeft = playerkeys(iPlIndex, 1, "moveleft"); // New key status of "Left"
    void iRight = playerkeys(iPlIndex, 1, "moveright"); // New key status of "Right"
    void iJump = playerkeys(iPlIndex, 1, "jump"); //New key status of "Jump"
    void iSpecial = playerkeys(iPlIndex, 1, "Special"); //New key status of "Special"
    void iAttack = playerkeys(iPlIndex, 1, "attack"); //New key status of "Attack"
    void iAttack2 = playerkeys(iPlIndex, 1, "attack2"); // New key of "Attack 2"
    void iAttack3 = playerkeys(iPlIndex, 1, "attack3"); // New key of "Attack 3"
    void iAttack4 = playerkeys(iPlIndex, 1, "attack4"); // New key of "Attack 4"

    void iLeftR = playerkeys(iPlIndex, 2, "moveleft"); // Release status of "Left"
    void iRightR = playerkeys(iPlIndex, 2, "moveright"); // Release status of "Right"
    void iDownR = playerkeys(iPlIndex, 2, "movedown"); //Release status of "Down"
    void iAttackR = playerkeys(iPlIndex, 2, "attack"); //Release status of "Attack"

    void iUpH = playerkeys(iPlIndex, 0, "moveup"); //Hold status of "Up"
    void iDownH = playerkeys(iPlIndex, 0, "movedown");//Hold status of "Down"
    void iLeftH = playerkeys(iPlIndex, 0, "moveleft");//Hold status of "Left"
    void iRightH = playerkeys(iPlIndex, 0, "moveright");//Hold status of "Right"

	void iJumpR = playerkeys(iPlIndex, 2, "jump"); // Release status of "Jump"
	void iSpecialR = playerkeys(iPlIndex, 2, "special"); //Release status of "Special"
	void iAttack2R = playerkeys(iPlIndex, 2, "attack2"); //Release status of "Attack2"
	void iAttack3R = playerkeys(iPlIndex, 2, "attack3"); //Release status of "Attack3"
	void iAttack4R = playerkeys(iPlIndex, 2, "attack4"); //Release status of "Attack4"
	
	void iAttackH = playerkeys(iPlIndex, 0, "attack"); //Hold status of "Attack"
	void iJumpH = playerkeys(iPlIndex, 0, "jump"); //Hold status of "Jump"
	void iSpecialH = playerkeys(iPlIndex, 0, "special"); //Hold status of "Special"
	void iAttack2H = playerkeys(iPlIndex, 0, "attack2"); //Hold status of "Attack2"
	void iAttack3H = playerkeys(iPlIndex, 0, "attack3"); //Hold status of "Attack3"
	void iAttack4H = playerkeys(iPlIndex, 0, "attack4"); //Hold status of "Attack4"
	
	void iStart = playerkeys(iPlIndex, 1, "start"); //New key status of "Start"
	
	//void iButton(Normal/Hold/Release) = playerkeys(iPlIndex, number, "Button");
	//0 = Hold; 1 = Press; 2 = Release
	//PlIndex = Player Index
	
//Jump
    if(vAniID == openborconstant("ANI_IDLE") || vAniID == openborconstant("ANI_WALK") || vAniID == openborconstant("ANI_BACKWALK")){
      if(iUpH){ //Jump pressed?
        changeentityproperty(vSelf, "aiflag", "jumping", 1); // Enable jumping status
        changeentityproperty(vSelf, "aiflag", "idling", 0); // Disable idling status
        if(iLeftH){ //Left held?
          changeentityproperty(vSelf, "direction", 1); // Change direction to face right
          changeentityproperty(vSelf, "animation", openborconstant("ANI_FOLLOW3"),1); //Change animation for going left
          tossentity(vSelf, 4, -1.5, 0); //Move entity to the left in air. tossentity(vSelf, y, x, z)
        } else if(iRightH){ //Right held?
          changeentityproperty(vSelf, "direction", 1); //Change direction to face right
          changeentityproperty(vSelf, "animation", openborconstant("ANI_FOLLOW2"),1); //Change animation for going right
          tossentity(vSelf, 4, 1.5, 0); //Move entity to the right in air.
        } else { //Nothing
        changeentityproperty(vSelf, "animation", openborconstant("ANI_FOLLOW1"),1); //Change to animation for moving straight up
          tossentity(vSelf, 4, 0, 0); //Jumping straight up
		  
        }
      }
    }
	

//Still holding crouch button after pressing any attack button
//Unduck

if(vAniID == openborconstant("ANI_DUCK")){ //Ducking?
  if(iDownR){ //Release key of Down
	 changeentityproperty(vSelf, "aiflag", "idling", 0); //Disable idling status
	 performattack(vSelf, openborconstant("ANI_FOLLOW6")); //Perform unduck animation
	}
	/*
	else if(iStart){ //Press start?
	 changeentityproperty(vSelf, "aiflag", "idling", 0);
	 performattack(vSelf, openborconstant("ANI_FOLLOW19")); //Perform unduck animation
	}
	*/
}


    if(iAttack){ //Attack button pressed?
      if(vAniID == openborconstant("ANI_DUCK") ){ //Ducking?
        performattack(vSelf, openborconstant("ANI_DUCKATTACK") );
      }
    }   


if(vAniID == openborconstant("ANI_DUCKATTACK"))
	{ // Duckattack anim?
		if(vAniPos == 2 || vAniPos == 3 || vAniPos == 4 || vAniPos == 5)
		{ // Select frames
			if(iAttack)
			{ // Attack key press
				changeentityproperty(vSelf, "animation", openborconstant("ANI_DUCKATTACK"));
				// Change to duckttack anim
			}
		}
		else if(iDownR)
		{ // Down key released?
			//changeentityproperty(vSelf, "animation", openborconstant("ANI_IDLE"));
			// Change to idle anim
			performattack(vSelf, openborconstant("ANI_FOLLOW6"));//Perform unduck
			changeentityproperty(vSelf, "aiflag", "idling", 0);//Idling status off
			// Turns on idle status
		}
	}
if(vAniID == openborconstant("ANI_FOLLOW7"))
	{ // Duck2 anim?
		if(iDownR)
		{ // Down key relesed?
			changeentityproperty(vSelf, "animation", openborconstant("ANI_IDLE"));
			// Change to IDLE anim
			changeentityproperty(vSelf, "velocity", 0, 0, 0);
			// Set velocity
			changeentityproperty(vSelf, "aiflag", "idling", 1);
			// Change to idle status
		}
		else if (vAniPos == 2 || vAniPos == 3 || vAniPos == 4 || vAniPos == 5)
		{ // Select frames
			if(iAttack)
			{ // Attack key press
				changeentityproperty(vSelf, "animation", openborconstant("ANI_DUCKATTACK"));
				// Change to Duckattack anim
			}
			else if(iAttack)
			{ // Attack key press
				changeentityproperty(vSelf, "animation", openborconstant("ANI_DUCKATTACK"));
				// Change to Duckattack anim
			}
		}
	}


}

Thank you so much DC, Bloodbane, BeasTie, SaintJudas, and Goliath. I got the duck working as I wanted. :) However, I have a problem with the down release key and start/pause key while playing the duckattack transition before going to anim duck. You see that there's anim duck without any button unless you press a down key for the char to unduck/stand up. OpenBOR is not detecting the down release key while pausing and unpausing on duckattack, and it thinks the player is holding the down key while I release a down key before pausing and after unpausing. I tried using iStart with a different follow animation for unducking after duckattack and duckattack-and-then-duck. But it seems pressing start key makes the character move up every time so I skipped it. There needs to be a solution for duckattacks between the use of down release key and start key. (Imagine if you press a duckattack button and pause the game before moving back to duck anim in OpenBOR, you will see that duck anim is already performed without pressing any button unless you press down to get up.)

EDIT: I'll figure out how to do this key script.
 
O Ilusionista said:
Just remember that "cancel" counts towards "maxfreespecials", ok?

True, O, and I know. :) I have maxfreespecials set with a number I want in models.txt for more use of freespecials I need. ;) That was before I started to use cancel. I use 5 of them for special "duck attacks" under duck anim. :)
 
Finally, I figured out how to fix the problem today. I found out that using keyint2 from animationscript works for ducking and unducking while duckattack anim is performed. How I got it? I copied the keyint2 script from Bloodbane's Contra script.c and pasted it in the animation script file I have for it. It works very well as I expected and I didn't know how to do it before.

Here's the duckattack anim I fixed using keyint2 for key hold and release. (anim, frame, key, hold/release flag)
anim duckattack
delay 5
bbox 7 1 46 68
offset 22 65
frame data/chars/ryu/278.gif
attack 43 5 46 21 3 0 1 0 0 0
frame data/chars/ryu/279.gif
frame data/chars/ryu/280.gif
offset 23 66
delay 2
frame data/chars/ryu/281.gif
@cmd keyint2 "ANI_DUCK" 2 "D" 0
@cmd keyint2 "ANI_FOLLOW6" 0 "D" 1
frame data/chars/ryu/281.gif

I added keyint2 in a script file, and the bolded red below is what I changed.
void keyint2(void Ani, int Frame, void Key, int Hflag)
{// Change current animation if proper key is pressed or released
// Animation is changed without attacking

    void self = getlocalvar("self");
    int Dir = getentityproperty(self, "direction");
    int iPIndex = getentityproperty(self,"playerindex"); //Get player index
    void iRKey;

      if (Key=="U"){ //Up Required?
        iRKey = playerkeys(iPIndex, 0, "moveup"); // "Up"
      } else if (Key=="D"){ //Down Required?
        iRKey = playerkeys(iPIndex, 0, "movedown"); // "Down"
      } else if (Key=="L"){ //Left Required?
        iRKey = playerkeys(iPIndex, 0, "moveleft"); // "Left"
      } else if (Key=="R"){ //Right Required?
        iRKey = playerkeys(iPIndex, 0, "moveright"); // "Right"
      } else if (Key=="F"){ //Forward Required?
        if (Dir == 1){ // Facing Left?
          iRKey = playerkeys(iPIndex, 0, "moveleft"); // "Left"
        } else { // Facing Right
          iRKey = playerkeys(iPIndex, 0, "moveright"); // "Right"
        }
      } else if (Key=="B"){ //Backward Required?
        if (Dir == 1){ // Facing Right?
          iRKey = playerkeys(iPIndex, 0, "moveleft"); // "Left"
        } else { // Facing Left
          iRKey = playerkeys(iPIndex, 0, "moveright"); // "Right"
        }
      } else if (Key=="A"){ //Attack Required?
        iRKey = playerkeys(iPIndex, 0, "attack"); // "Attack"
      }

      if (Hflag==1){ //Not holding the button case?
        iRKey = !iRKey; //Take the opposite condition
      }

      if (iRKey){
        if (Ani=="ANI_IDLE"){ // Going idle?
          setidle(self, openborconstant("ANI_IDLE")); //Be idle!
        } else {
          changeentityproperty(self, "animation", openborconstant(Ani),2);
        }
        changeentityproperty(self, "animpos", Frame); //Change frame
      }
}

O Ilusionista, here's a good reason why I used freespecials 6-10. It's simply because I used freespecials as special attacks like a2 for mid punch using anim freespecial for example. It goes like this (my previous header change):

Code:
name Ryu
type player
health  10
gfxshadow	1
speed   14
turndelay  2000
load       hadoken
load       hadoken2
load       hadoken3
nolife 1
ondeathscript data/scripts/death.c
didhitscript data/scripts/hit1.c
script data/scripts/ryu.c
atchain    1 1 1   #stand low punch
com	a2 freespecial  #stand medium punch
com	a3 freespecial2 #stand high punch
com	j  freespecial3 #stand low kick
com	s  freespecial4 #stand medium kick
com	a4 freespecial5 #stand high kick

Now that I got to be a bit better in scripts, I did create scripts for attacks and replaced the animation names of freespecials with attacks: Replaced name Anim freespecial with Anim attack2.

For example:
Code:
	//Stand middle punch
	if(iAttack2){ //Attack2 button pressed?
	  if(vAniID == openborconstant("ANI_IDLE") || vAniID == openborconstant("ANI_WALK") || vAniID == openborconstant("ANI_BACKWALK")){
	  //Perform an attack move in one of the three animations
		performattack(vSelf, openborconstant("ANI_ATTACK2")); //Perform middle punch
		changeentityproperty(vSelf, "velocity", 0, 0, 0); // Move? No, because all of the velocity is 0.
		//That's why Ryu is not moving while he's attacking!
	  }
	}

I advanced to a new level for making the attack types (e.g. anim attack2) for creating the script (I guess). If anyone especially a beginner likes a simple coding for attack commands (jump, special, a2, etc. as attacks), I can go back to using freespecials.

EDIT: BTW I modified my player script. I think I made some changes here.

Code:
void main()
{
    int iPlIndex = getlocalvar("player"); //Get calling player
    void vSelf = getplayerproperty(iPlIndex , "entity"); //Get calling entity
	void vTarget = getentityproperty(vSelf, "opponent"); //Get calling opponent
	//void vTarget = getlocalvar(self, "opponent);
	//void vTarget = findtarget("opponent"); //Get nearest opponent to caller.
    void vAniID = getentityproperty(vSelf,"animationID"); //Get current animation ID
    void vAniPos = getentityproperty(vSelf, "animpos"); //Get current animation frame
	int iFrame = getentityproperty(vSelf, "animpos"); //Caller's frame
    int iDir = getentityproperty(vSelf, "direction");  //Get current facing direction

    void iUp = playerkeys(iPlIndex, 1, "moveup"); // New key status of "Up"
    void iDown = playerkeys(iPlIndex, 1, "movedown"); // New key status of "Down"
    void iLeft = playerkeys(iPlIndex, 1, "moveleft"); // New key status of "Left"
    void iRight = playerkeys(iPlIndex, 1, "moveright"); // New key status of "Right"
    void iJump = playerkeys(iPlIndex, 1, "jump"); //New key status of "Jump"
    void iSpecial = playerkeys(iPlIndex, 1, "Special"); //New key status of "Special"
    void iAttack = playerkeys(iPlIndex, 1, "attack"); //New key status of "Attack"
    void iAttack2 = playerkeys(iPlIndex, 1, "attack2"); // New key of "Attack 2"
    void iAttack3 = playerkeys(iPlIndex, 1, "attack3"); // New key of "Attack 3"
    void iAttack4 = playerkeys(iPlIndex, 1, "attack4"); // New key of "Attack 4"

    void iLeftR = playerkeys(iPlIndex, 2, "moveleft"); // Release status of "Left"
    void iRightR = playerkeys(iPlIndex, 2, "moveright"); // Release status of "Right"
    void iDownR = playerkeys(iPlIndex, 2, "movedown"); //Release status of "Down"
    void iAttackR = playerkeys(iPlIndex, 2, "attack"); //Release status of "Attack"

    void iUpH = playerkeys(iPlIndex, 0, "moveup"); //Hold status of "Up"
    void iDownH = playerkeys(iPlIndex, 0, "movedown");//Hold status of "Down"
    void iLeftH = playerkeys(iPlIndex, 0, "moveleft");//Hold status of "Left"
    void iRightH = playerkeys(iPlIndex, 0, "moveright");//Hold status of "Right"

	void iJumpR = playerkeys(iPlIndex, 2, "jump"); // Release status of "Jump"
	void iSpecialR = playerkeys(iPlIndex, 2, "special"); //Release status of "Special"
	void iAttack2R = playerkeys(iPlIndex, 2, "attack2"); //Release status of "Attack2"
	void iAttack3R = playerkeys(iPlIndex, 2, "attack3"); //Release status of "Attack3"
	void iAttack4R = playerkeys(iPlIndex, 2, "attack4"); //Release status of "Attack4"
	
	void iAttackH = playerkeys(iPlIndex, 0, "attack"); //Hold status of "Attack"
	void iJumpH = playerkeys(iPlIndex, 0, "jump"); //Hold status of "Jump"
	void iSpecialH = playerkeys(iPlIndex, 0, "special"); //Hold status of "Special"
	void iAttack2H = playerkeys(iPlIndex, 0, "attack2"); //Hold status of "Attack2"
	void iAttack3H = playerkeys(iPlIndex, 0, "attack3"); //Hold status of "Attack3"
	void iAttack4H = playerkeys(iPlIndex, 0, "attack4"); //Hold status of "Attack4"
	
	//void iStart = playerkeys(iPlIndex, 1, "start"); //New key status of "Start"
	//void iStartR = playerkeys(iPlIndex, 2, "start"); //Release key status of "Start"
	
	//void iButton(Normal/Hold/Release) = playerkeys(iPlIndex, number, "Button");
	//0 = Hold; 1 = Press; 2 = Release
	//PlIndex = Player Index
	//changeentityproperty(entity, propname, value)
	//Property name = propname
	
//Jump
    if(vAniID == openborconstant("ANI_IDLE") || vAniID == openborconstant("ANI_WALK") || vAniID == openborconstant("ANI_BACKWALK")){
	//Which animation are you going to press up for jumping while standing or moving?
      if(iUpH){ //Jump pressed?
        changeentityproperty(vSelf, "aiflag", "jumping", 1); // Enable jumping status
        changeentityproperty(vSelf, "aiflag", "idling", 0); // Disable idling status
        if(iLeftH){ //Left held?
          changeentityproperty(vSelf, "direction", 1); // Change direction to face right
          changeentityproperty(vSelf, "animation", openborconstant("ANI_FOLLOW3"),1); //Change animation for going left
          tossentity(vSelf, 4, -1.5, 0); //Move entity to the left in air. tossentity(vSelf, y, x, z)
        } else if(iRightH){ //Right held?
          changeentityproperty(vSelf, "direction", 1); //Change direction to face right
          changeentityproperty(vSelf, "animation", openborconstant("ANI_FOLLOW2"),1); //Change animation for going right
          tossentity(vSelf, 4, 1.5, 0); //Move entity to the right in air.
        } else { //Nothing
        changeentityproperty(vSelf, "animation", openborconstant("ANI_FOLLOW1"),1); //Change to animation for moving straight up
          tossentity(vSelf, 4, 0, 0); //Jumping straight up
		  
        }
      }
    }



//Unduck

if(vAniID == openborconstant("ANI_DUCK")){ //Ducking?
  if(iDownR){ //Release key of Down
	 changeentityproperty(vSelf, "aiflag", "idling", 0); //Disable idling status
	 performattack(vSelf, openborconstant("ANI_FOLLOW6")); //Perform unduck animation
	}
}

    if(iAttack){ //Attack button pressed?
      if(vAniID == openborconstant("ANI_DUCK") ){ //Ducking?
        performattack(vSelf, openborconstant("ANI_DUCKATTACK") ); //Duckattack anim performed? / Crouch low punch performed
      }
    }
	
	if(iAttack2){ //Attack2 button pressed?
		if(vAniID == openborconstant("ANI_DUCK")){ //Ducking?
			performattack(vSelf, openborconstant("ANI_FREESPECIAL6")); //Crouch middle punch performed
		}
	}

	//Stand middle punch
	if(iAttack2){ //Attack2 button pressed?
	  if(vAniID == openborconstant("ANI_IDLE") || vAniID == openborconstant("ANI_WALK") || vAniID == openborconstant("ANI_BACKWALK")){
	  //Perform an attack move in one of the three animations
		performattack(vSelf, openborconstant("ANI_ATTACK2")); //Perform middle punch
		changeentityproperty(vSelf, "velocity", 0, 0, 0); // Move? No, because all of the velocity is 0.
		//That's why Ryu is not moving while he's attacking!
	  }
	}

	//Stand high punch
	if(iAttack3){ //Attack3 button pressed?
	  if(vAniID == openborconstant("ANI_IDLE") || vAniID == openborconstant("ANI_WALK") || vAniID == openborconstant("ANI_BACKWALK")){
	  //Perform an attack move in one of the three animations
		performattack(vSelf, openborconstant("ANI_ATTACK3")); //Perform high punch
		changeentityproperty(vSelf, "velocity", 0, 0, 0); // Move?
	  }
	}
	
	//Stand low kick
	if(iJump){ //Jump button pressed?
	  if(vAniID == openborconstant("ANI_IDLE") || vAniID == openborconstant("ANI_WALK") || vAniID == openborconstant("ANI_BACKWALK")){
	  //Perform an attack move in one of the three animations
		performattack(vSelf, openborconstant("ANI_ATTACK4")); //Perform low kick
		changeentityproperty(vSelf, "velocity", 0, 0, 0); // Move?
	  }
	}

	//Stand middle kick
	if(iSpecial){
	  if(vAniID == openborconstant("ANI_IDLE") || vAniID == openborconstant("ANI_WALK") || vAniID == openborconstant("ANI_BACKWALK")){
	  //Perform an attack move in one of the three animations
		performattack(vSelf, openborconstant("ANI_ATTACK5")); //Perform middle kick
		changeentityproperty(vSelf, "velocity", 0, 0, 0); // Move?
	  }
	}

	//Stand high kick
	if(iAttack4){
	  if(vAniID == openborconstant("ANI_IDLE") || vAniID == openborconstant("ANI_WALK") || vAniID == openborconstant("ANI_BACKWALK")){
	  //Perform an attack move in one of the three animations
		performattack(vSelf, openborconstant("ANI_ATTACK6")); //Perform high kick
		changeentityproperty(vSelf, "velocity", 0, 0, 0); // Move?
	  }
	}

	
}
 
changeentityproperty(self, "animation", openborconstant(Ani),2);
What the last value means?

Because according to the manual:

changeentityproperty(entity, propname, values)

~Change an entity's property by name.
~'entity' is the handle of that entity.
~'propname' is the property's name.
~'value' is new value you want to set.

So you use that value to go to ANI_ATTACK2?
or its something undocumented?
 
O Ilusionista said:
changeentityproperty(self, "animation", openborconstant(Ani),2);

What the last value means?

According to Bloodbane, this is what he said a page ago:

Bloodbane said:
In old build, this function:

Code:
void aniframe(void Ani, int Frame){	
	// Changes anim and select frame
    void self = getlocalvar("self"); // Get self

    changeentityproperty(self, "animation", openborconstant(Ani));
	changeentityproperty(self, "animpos", Frame);
}

works well but in newer build, you must add a little fix shown with red color:

void aniframe(void Ani, int Frame){
// Changes anim and select frame
    void self = getlocalvar("self"); // Get self

    changeentityproperty(self, "animation", openborconstant(Ani), 2);
changeentityproperty(self, "animpos", Frame);
}

O Ilusionista said:
So you use that value to go to ANI_ATTACK2?

Nope, not on anim attack2, but updating script for any animation you're using. Using the value for any animation (not just anim attack2) works well for newer versions.

EDIT: Using 1 works, but it will play like the old version without value. For example, if your script has a value of 1 in keyint2 for animation while you're holding a down button when you have duck anim in there, the first frame of duck anim will play instead of going to the second or third frame (whichever you declared). So using 2 is better.
 
Back
Top Bottom