Post by BeasTie on Dec 14, 2012 22:21:55 GMT -5
3D Attacks For Enemies by Bloodbane
I'm going to share another script functions. This time the ones which are related to enemy's 3D attack.
About 3D attacks
Before going to script itself, I'll start with 3D attack definition. 3D attack is attack which can hit heroes standing in different z coordinate. It could be simple ramming attack or whole screen attack. If you have played many brawler games, you should have seen many examples of those although not all enemies perform it.
I would like to ramble more about 3D attack types but I'll skip it and just go with certain type which is related to script function discussed here. The type is narrow and targetted type which means it only hit narrow area (technically just as big as the attackbox) but targetted (meaning it doesn't move aimlessly). There are 3 kinds of them which are supported here:
1. Ram/charge (ex: Big Ben's flamethrower dash)
2. Jumpattack (ex: Mona & Lisa SoR1 Jumpkick)
3. Targetted shooting (ex: RobotY's Missile)
All of them are about moving to target and are supported by one function named 'target'.
I'm not going to explain how to set offset, attackbox, range and rangez including how to use animationscript. Learn those yourselves if you haven't.
Targetting
Like said before, these attacks are targetted which means we need to target 1st before performing them. To find the target we use a function named 'findtarget()'.
'findtarget(self)' means find another entity whom this 'self' entity is hostile to. Since this is for enemies, the target is player. If there are 2 or more player, the closest one will be chosen. If there are none, 'target' will be empty.
What are we going to do with the targetted player assuming it's not empty? See next.
Moving Philosophy
After target is attained, the next thing to do is to move the enemy toward player. How does enemy move? there are 2 ways to do it, one is with 'move' command and 2nd is with 'velocity setting'. The 1st method is not preferred cause it's cumbersome. It requires multiple frames for moving and using script to replicate 'move' brings problem with walls and platforms. It's solvable though but it's best to avoid this method and use 2nd method instead.
2nd method moves entity smoothly with velocity setting and it moves just like using 'jumpframe'. It also require less frame since one frame is enough. If more frames are used, it's usually for graphical effect.
To move entity with velocity setting, we could do this:
4 is x velocity while 2 is z velocity. Just by changing the property above, entity will move with that speed. With 2 velocity set at same time, entity will move diagonally downward right.
Now, how do we make enemy move toward player? simple just set proper velocity buuuut how much velocity do we need? We have to be careful here cause setting improper speed might make enemy misses player completely while moving. In order to get proper value we need to find the distance between enemy and player. Since they are seperated in x and z, we are going to attain distance for x and z.
Note the 'if' above. We need to check if player is available or not cause we can't get any info from it if it's not or empty.
After we get Disx and Disz, we could calculate proper speed. Let's name x velocity with Vx and z velocity with Vz.
The idea here is enemy moves with Vx in x axis to move Disx pixels while at same time enemy moves with Vz in z axis to move Disz pixels. Do you get it? by moving like that, enemy will eventually reach player's position cause the former moves Disx pixels in x axis and Disz in z axis. Both distance must be covered at same time otherwise enemy will miss.
So in math formula we could write them like this:
t = required time for enemy to reach player
Since we don't really need to know t, we could merge both formulas into:
Actually with graphic and little trigonometry, getting above formula is more straightforward but I believe not all ppl here understand trigonometry so I use above logic instead.
Getting Velocity
Both x velocity and z velocity or Vx and Vz in formula above are unknown but don't worry cause it means we can set both enemy's velocity although only one which will be used. Confused? I've said it before that proper speed is required so although one velocity is defined, the other will be calculated instead. That's the trick to get proper speed.
1st assumption: Vx is dominant one and Vz will be the calculated one. Dominant means we can define its value.
Since Vz is unknown while Vx is defined say Vx = Velx, we could get its value from formula above:
2nd assumption: Vz is dominant one and Vx will be the calculated one.
Since Vx is unknown while Vz is defined say Vz = Velz, we could get its value from formula above:
With those, both values of Vx and Vz are attained.
Code Implementation
Both assumption above can easily be implemented with script but there are couple things to deal with:
1. Both Disx and Disz MIGHT be negative value. Both are attained from subtracting player's coordinate with enemy's. If player stands behind and away from enemy, both values will be negative.
To solve this, simply turn them to positive if their value is negative, like this:
Combine this with above code that is.
2. Velocity is not relative to entity meaning setting positive velocity will move entity to right for x velocity or down for z velocity. Entity would move the opposite way with negative values. If player stand behind and away from enemy, negative value would be required.
To solve this, we must combine fact above with the formula like this:
1st assumption:
2nd assumption:
3. How to implement both above assumption? When 1st one is used and when 2nd one is used?
Actually we could set any rule here but I prefer to use Disx and Disz comparison rule. The reason is when one of them is bigger, the respective velocity would be bigger than other's velocity which mean former velocity is more dominant. That rule is implemented like this:
4. What if there are no players to target at all?
The answer is simple, let's set certain value for Vx and Vz. We could just set both to 0 which means no moving at all. However, usually enemy is in moving in x axis animation so he/she would look ugly if he/she's not moving so to solve it, simply apply defined Vx to him/her. Don't forget enemy's facing direction!
5. Other enemy using same function ended having same target or even worse altering target. This usually happen if there are 2 same enemies attacking with same attack at almost same time.
I don't get why this could happen but this issue can be fixed by using local value with unique identifier like this:
6. Although velocity is adjusted, enemy won't turn back if player is standing behind while targetting.
To solve this, simply flip facing direction to where player is like this:
Well the code become complex but don't worry all of this have great effect .
Last, this target function is meant to support other function. That means the function who perform the actual move is not this function. This function only calculate proper velocity values. To transfer this values, we use local variable. And the whole function becomes like this:
The input of this function is Velx for x velocity and Velz for z velocity. Like said before, 2 possible inputs but only one is used.
The output of this function is local variable named '"x"+self' which contains x velocity and '"z"+self' which contains z velocity.
Velocity Usage
Need aspirin? OK OK just kidding
There are 3 functions which can use the outputs above i.e:
1. dash() for targetted ram/charge
2. leap(float Vely) for semi targetted jump attack
3. shoot2(void Shot, float dx, float dy, float dz) for targetted shooting
Each functions affect enemy differently.
Since they use outputs from 'target' function, they must be declared AFTER that function.
1.Targetted Charge/Ram
In Streets of Rage series, there's a fat enemy who spew flame from his mouth while running toward player. That is one example of this type of targetted attack. The function used here moves enemy with certain speed toward player. It doesn't matter what actually they are doing, they could be ramming, sliding or shoulder charging.
Here's the function:
As you can see, both velocity are attained from localvariable made by 'target' function before. There's a velocity check to ensure both velocity are available before dashing. Changeentityproperty sets the velocity which moves the enemy. Don't worry about facing direction cause 'target' has solved it.
Due to nature of script, enemy will keep moving whether he/she hit player or not. Not hitting means player moves away. That's why it's recommended to stop the movement after he/she's done.
2.Semi Targetted Jumpattack
In Streets of Rage 1, there are twin female bosses who can perform jumpkick right to player. That is one example of this type of targetted attack. The function here toss enemy with certain speed toward player. Aside from moving in x and z, it also toss enemy upwards. Like previous function, it doesn't matter what actually they are doing, they could be jumppunching, jumpkicking or elbow strike.
Here's the function:
Everything is same as previous function except that it's using tossentity. Tossentity works just like jumpframe. Since attained velocity only for x and z, this function accept other velocity for y axis. However, this function only toss meaning after certain time, tossing apex will be reached then enemy will fall down. While jumping, x and z velocity remains so don't worry about it. After enemy lands, all movements are stopped so no need to stop it with script.
BTW I did name this as semi target cause the real Targetted Jumpattack toss enemy exactly right to where player is. This function doesn't support that precision cause like dash(), enemy will continue moving after hitting player.
3.Targetted Shooting
Last boss of Streets of Rage 3, RobotY can shoot 2 missiles and both are targetted. That's an example of this type of attack. This function shoots a projectile then alter its speed with targetted speed toward player. It doesn't matter how the projectile looks like, it could be knife, missile, firebolt or boomerang.
Here's the function:
The function differs alot with previous ones cause this function shoots projectile and alters its speed. 'Projectile' function shoots entity named Shot which is inputted. This projectile function also requires starting position for shot projectile which is procurred by attaining x,z and a coordinate above. Position adjustment is inputted to allow scripter to give position shift to the shot projectile.
vShot gets the shot projectile. After that, Vx and Vz is attained like before to alter projectile's speed with changeentityproperty. However since projectile's flying speed is based on its speed instead of velocity, speed change is required. Since speed isn't relative, we need to get enemy's facing direction 1st before changing the speed with Vx. Negative speed(-Vx) for moving left or positive speed(Vx) for right.
Review
As you can see, targetting is seperated from the action in this method. This allows flexibility in which scripted can use same 'target' function for different attacks. However, there is another important reason behind this seperation.
Take a look at this sample animation:
In this attack, enemy target player with speed of 5 or 2.5 at 2nd frame then dash at 5th frame. Enemy stops at 6th frame.
From 'targetting' frame to 'dashing' frame there's 25 centisecond delay.
Now look at this one:
The animation is same except that targetting and dashing is done at same frame. In this case, there's practically no delay between targetting and dashing.
What's the point of this? in 1st attack, player can dodge or move away within 25 centiseconds delay but in 2nd one, there's practically no chance to dodge. IOW 2nd attack is more effective than the 1st one. This is assuming player is not blocking or in invicible state.
So does that mean we should avoid giving delays and always declare 'target' and action functions at same frame? No, no and no! This is what's called with intelligence control. Smart enemies have more effective attacks than stupid ones. With this setting, 3D attack effectiveness can be controlled and thus their intelligence. That's the important reason behind this seperation.
Conclusion
I hope you all can understand all above explanation. No need to hurry, reread if you need to. Take all time you need to grasp it.
Anyways, those are just some example of 3D attacks, there are more kinds of them in brawlers games. Most 3D attack in Crime Buster v2.2 are from above type. The rest are other kind of 3D attacks. One of them is shown below. Sweep shoot ala Capcom!
I could share how to make it but I'll do it later cause this thread is already long.
Finally, I'm hoping other mods would be using 3D attacks for enemies too. No need to do it like my mod though.
I've just remember something about targetted shooting above. For semi targetted leap and targetted dash, it's accurate but for targetted shooting, it's accuracy depends on distance between fired projectile and shooter. That means if projectile is fired quite far from shooter, it might miss player although it's targetted.
This could be fixed by inserting the distance in the formula above.
-----------
originally posted by Bloodbane
I'm going to share another script functions. This time the ones which are related to enemy's 3D attack.
About 3D attacks
Before going to script itself, I'll start with 3D attack definition. 3D attack is attack which can hit heroes standing in different z coordinate. It could be simple ramming attack or whole screen attack. If you have played many brawler games, you should have seen many examples of those although not all enemies perform it.
I would like to ramble more about 3D attack types but I'll skip it and just go with certain type which is related to script function discussed here. The type is narrow and targetted type which means it only hit narrow area (technically just as big as the attackbox) but targetted (meaning it doesn't move aimlessly). There are 3 kinds of them which are supported here:
1. Ram/charge (ex: Big Ben's flamethrower dash)
2. Jumpattack (ex: Mona & Lisa SoR1 Jumpkick)
3. Targetted shooting (ex: RobotY's Missile)
All of them are about moving to target and are supported by one function named 'target'.
I'm not going to explain how to set offset, attackbox, range and rangez including how to use animationscript. Learn those yourselves if you haven't.
Targetting
Like said before, these attacks are targetted which means we need to target 1st before performing them. To find the target we use a function named 'findtarget()'.
void self = getlocalvar("self");
void target = findtarget(self);
'findtarget(self)' means find another entity whom this 'self' entity is hostile to. Since this is for enemies, the target is player. If there are 2 or more player, the closest one will be chosen. If there are none, 'target' will be empty.
What are we going to do with the targetted player assuming it's not empty? See next.
Moving Philosophy
After target is attained, the next thing to do is to move the enemy toward player. How does enemy move? there are 2 ways to do it, one is with 'move' command and 2nd is with 'velocity setting'. The 1st method is not preferred cause it's cumbersome. It requires multiple frames for moving and using script to replicate 'move' brings problem with walls and platforms. It's solvable though but it's best to avoid this method and use 2nd method instead.
2nd method moves entity smoothly with velocity setting and it moves just like using 'jumpframe'. It also require less frame since one frame is enough. If more frames are used, it's usually for graphical effect.
To move entity with velocity setting, we could do this:
void self = getlocalvar("self");
changeentityproperty(self, "velocity", 4, 2);
4 is x velocity while 2 is z velocity. Just by changing the property above, entity will move with that speed. With 2 velocity set at same time, entity will move diagonally downward right.
Now, how do we make enemy move toward player? simple just set proper velocity buuuut how much velocity do we need? We have to be careful here cause setting improper speed might make enemy misses player completely while moving. In order to get proper value we need to find the distance between enemy and player. Since they are seperated in x and z, we are going to attain distance for x and z.
void self = getlocalvar("self");
float x = getentityproperty(self, "x"); // Get enemy's x coordinate
float z = getentityproperty(self, "z"); // Get enemy's z coordinate
void target = findtarget(self); // Find target
if( target != NULL()){ // Is there a target?
float Tx = getentityproperty(target, "x"); // Get target's x coordinate
float Tz = getentityproperty(target, "z"); // Get target's z coordinate
float Disx = Tx - x; // Get x distance
float Disz = Tz - z; // Get z distance
}
Note the 'if' above. We need to check if player is available or not cause we can't get any info from it if it's not or empty.
After we get Disx and Disz, we could calculate proper speed. Let's name x velocity with Vx and z velocity with Vz.
The idea here is enemy moves with Vx in x axis to move Disx pixels while at same time enemy moves with Vz in z axis to move Disz pixels. Do you get it? by moving like that, enemy will eventually reach player's position cause the former moves Disx pixels in x axis and Disz in z axis. Both distance must be covered at same time otherwise enemy will miss.
So in math formula we could write them like this:
Disx = Vx * t
Disz = Vz * t
t = required time for enemy to reach player
Since we don't really need to know t, we could merge both formulas into:
Disx/Vx = Disz/Vz or Vx/Disx = Vz/Disz
Actually with graphic and little trigonometry, getting above formula is more straightforward but I believe not all ppl here understand trigonometry so I use above logic instead.
Getting Velocity
Both x velocity and z velocity or Vx and Vz in formula above are unknown but don't worry cause it means we can set both enemy's velocity although only one which will be used. Confused? I've said it before that proper speed is required so although one velocity is defined, the other will be calculated instead. That's the trick to get proper speed.
1st assumption: Vx is dominant one and Vz will be the calculated one. Dominant means we can define its value.
Since Vz is unknown while Vx is defined say Vx = Velx, we could get its value from formula above:
Vz = Disz * Velx/Disx
2nd assumption: Vz is dominant one and Vx will be the calculated one.
Since Vx is unknown while Vz is defined say Vz = Velz, we could get its value from formula above:
Vx = Disx * Velz/Disz
With those, both values of Vx and Vz are attained.
Code Implementation
Both assumption above can easily be implemented with script but there are couple things to deal with:
1. Both Disx and Disz MIGHT be negative value. Both are attained from subtracting player's coordinate with enemy's. If player stands behind and away from enemy, both values will be negative.
To solve this, simply turn them to positive if their value is negative, like this:
if(Disx < 0){ // Negative Disx?
Disx = -Disx; // Turn it to positive
}
if(Disz < 0){ // Negative Disz?
Disz = -Disz; // Turn it to positive
}
Combine this with above code that is.
2. Velocity is not relative to entity meaning setting positive velocity will move entity to right for x velocity or down for z velocity. Entity would move the opposite way with negative values. If player stand behind and away from enemy, negative value would be required.
To solve this, we must combine fact above with the formula like this:
1st assumption:
if(Disx < 0){ // Negative Disx?
Disx = -Disx; // Turn it to positive
}
if(Disz < 0){ // Negative Disz?
Disz = -Disz; // Turn it to positive
}
Vz = (Tz - z) * Velx/Disx; // Calculate Vz
if(Tx < x){ // Player is behind enemy?
Vx = -Velx; // Turn Vx to negative
}
2nd assumption:
if(Disx < 0){ // Negative Disx?
Disx = -Disx; // Turn it to positive
}
if(Disz < 0){ // Negative Disz?
Disz = -Disz; // Turn it to positive
}
Vx = (Tx - x) * Velz/Disz; // Calculate Vx
if(Tz < z){ // Player is behind enemy?
Vz = -Velz; // Turn Vz to negative
}
3. How to implement both above assumption? When 1st one is used and when 2nd one is used?
Actually we could set any rule here but I prefer to use Disx and Disz comparison rule. The reason is when one of them is bigger, the respective velocity would be bigger than other's velocity which mean former velocity is more dominant. That rule is implemented like this:
if(Disx < 0){ // Negative Disx?
Disx = -Disx; // Turn it to positive
}
if(Disz < 0){ // Negative Disz?
Disz = -Disz; // Turn it to positive
}
if(Disz < Disx){ // Disx bigger than Disz?
Vz = (Tz - z) * Velx/Disx; // Calculate Vz
if(Tx < x){ // Player is behind enemy?
Vx = -Velx; // Turn Vx to negative
}
} else { // Disz bigger than Disx
Vx = (Tx - x) * Velz/Disz; // Calculate Vx
if(Tz < z){ // Player is behind enemy?
Vz = -Velz; // Turn Vz to negative
}
}
4. What if there are no players to target at all?
The answer is simple, let's set certain value for Vx and Vz. We could just set both to 0 which means no moving at all. However, usually enemy is in moving in x axis animation so he/she would look ugly if he/she's not moving so to solve it, simply apply defined Vx to him/her. Don't forget enemy's facing direction!
void self = getlocalvar("self");
int dir = getentityproperty(self, "direction"); // Get enemy's facing direction
void target = findtarget(self); // Find target
if( target != NULL()){ // Is there a target?
...
} else { // No target at all!
Vz = 0; // 0 velocity
if(dir==0){ // Facing left?
Vx = -Velx; // Negative velocity
} else { Vx = Velx}; // Positive velocity
}
5. Other enemy using same function ended having same target or even worse altering target. This usually happen if there are 2 same enemies attacking with same attack at almost same time.
I don't get why this could happen but this issue can be fixed by using local value with unique identifier like this:
void self = getlocalvar("self");
setlocalvar("T"+self, findtarget(self)); // Find target and put it in local variable
if( getlocalvar("T"+self) != NULL()){ // Is there a target?
6. Although velocity is adjusted, enemy won't turn back if player is standing behind while targetting.
To solve this, simply flip facing direction to where player is like this:
if(Disx < 0){ // Negative Disx?
Disx = -Disx; // Turn it to positive
changeentityproperty(self, "direction", 0); // Face left
} else {
changeentityproperty(self, "direction", 1); // Face right
}
Well the code become complex but don't worry all of this have great effect .
Last, this target function is meant to support other function. That means the function who perform the actual move is not this function. This function only calculate proper velocity values. To transfer this values, we use local variable. And the whole function becomes like this:
void target(float Velx, float Velz)
{// Targetting opponent before leaping or dashing
void self = getlocalvar("self");
int dir = getentityproperty(self, "direction"); // Get enemy's facing direction
float x = getentityproperty(self, "x"); // Get enemy's x coordinate
float z = getentityproperty(self, "z"); // Get enemy's z coordinate
setlocalvar("T"+self, findtarget(self)); // Find target and put it in local variable
if( getlocalvar("T"+self) != NULL()){ // Is there a target?
void target = getlocalvar("T"+self);
float Tx = getentityproperty(target, "x"); // Get target's x coordinate
float Tz = getentityproperty(target, "z"); // Get target's z coordinate
float Disx = Tx - x; // Get x distance
float Disz = Tz - z; // Get z distance
if(Disx < 0){ // Negative Disx?
Disx = -Disx; // Turn it to positive
changeentityproperty(self, "direction", 0); // Face left
} else {
changeentityproperty(self, "direction", 1); // Face right
}
if(Disz < 0){ // Negative Disz?
Disz = -Disz; // Turn it to positive
}
if(Disz < Disx) // Disx bigger than Disz?
{
if(Tx < x){ // Player is behind enemy?
setlocalvar("x"+self, -Velx); // Turn Vx to negative
} else { setlocalvar("x"+self, Velx); } // Use defined Vx
setlocalvar("z"+self, Velx*(Tz-z)/Disx); // Calculate Vz then store value in local variable
} else { // Disz bigger than Disx!
if(Tz < z){ // Player is behind enemy?
setlocalvar("z"+self, -Velz); // Turn Vz to negative
} else { setlocalvar("z"+self, Velz); } // Use defined Vz
setlocalvar("x"+self, Velz*(Tx-x)/Disz); // Calculate Vx then store value in local variable
}
} else { // No target at all!
setlocalvar("z"+self, 0); // 0 velocity
if(dir==0){ // Facing left?
setlocalvar("x"+self, -Velx); // Negative velocity
} else { setlocalvar("x"+self, Velx); } // Positive velocity
}
}
The input of this function is Velx for x velocity and Velz for z velocity. Like said before, 2 possible inputs but only one is used.
The output of this function is local variable named '"x"+self' which contains x velocity and '"z"+self' which contains z velocity.
Velocity Usage
Need aspirin? OK OK just kidding
There are 3 functions which can use the outputs above i.e:
1. dash() for targetted ram/charge
2. leap(float Vely) for semi targetted jump attack
3. shoot2(void Shot, float dx, float dy, float dz) for targetted shooting
Each functions affect enemy differently.
Since they use outputs from 'target' function, they must be declared AFTER that function.
1.Targetted Charge/Ram
In Streets of Rage series, there's a fat enemy who spew flame from his mouth while running toward player. That is one example of this type of targetted attack. The function used here moves enemy with certain speed toward player. It doesn't matter what actually they are doing, they could be ramming, sliding or shoulder charging.
Here's the function:
void dash()
{// Dash with previously attained speed!
void self = getlocalvar("self");
float Vx = getlocalvar("x"+self); // Attain x velocity
float Vz = getlocalvar("z"+self); // Attain z velocity
if( Vx!=NULL() && Vz!=NULL() ){ // Are both velocity available?
changeentityproperty(self, "velocity", Vx, Vz); //Move towards target!
}
}
As you can see, both velocity are attained from localvariable made by 'target' function before. There's a velocity check to ensure both velocity are available before dashing. Changeentityproperty sets the velocity which moves the enemy. Don't worry about facing direction cause 'target' has solved it.
Due to nature of script, enemy will keep moving whether he/she hit player or not. Not hitting means player moves away. That's why it's recommended to stop the movement after he/she's done.
2.Semi Targetted Jumpattack
In Streets of Rage 1, there are twin female bosses who can perform jumpkick right to player. That is one example of this type of targetted attack. The function here toss enemy with certain speed toward player. Aside from moving in x and z, it also toss enemy upwards. Like previous function, it doesn't matter what actually they are doing, they could be jumppunching, jumpkicking or elbow strike.
Here's the function:
void leap(float Vely)
{// Leap with previously attained speed!
void self = getlocalvar("self");
float Vx = getlocalvar("x"+self); // Attain x velocity
float Vz = getlocalvar("z"+self); // Attain z velocity
if( Vx!=NULL() && Vz!=NULL() ){ // Are both velocity available?
tossentity(self, Vely, Vx, Vz); //Leap towards target!
}
}
Everything is same as previous function except that it's using tossentity. Tossentity works just like jumpframe. Since attained velocity only for x and z, this function accept other velocity for y axis. However, this function only toss meaning after certain time, tossing apex will be reached then enemy will fall down. While jumping, x and z velocity remains so don't worry about it. After enemy lands, all movements are stopped so no need to stop it with script.
BTW I did name this as semi target cause the real Targetted Jumpattack toss enemy exactly right to where player is. This function doesn't support that precision cause like dash(), enemy will continue moving after hitting player.
3.Targetted Shooting
Last boss of Streets of Rage 3, RobotY can shoot 2 missiles and both are targetted. That's an example of this type of attack. This function shoots a projectile then alter its speed with targetted speed toward player. It doesn't matter how the projectile looks like, it could be knife, missile, firebolt or boomerang.
Here's the function:
void shoot2(void Shot, float dx, float dy, float dz)
{ // Shooting targetted projectile
void self = getlocalvar("self");
int Direction = getentityproperty(self, "direction"); // Get enemy's facing direction
int x = getentityproperty(self, "x"); // Get enemy's x coordinate
int y = getentityproperty(self, "a"); // Get enemy's a coordinate
int z = getentityproperty(self, "z"); // Get enemy's z coordinate
float Vx = getlocalvar("x"+self); // Attain x velocity
float Vz = getlocalvar("z"+self); // Attain z velocity
void vShot; // Projectile entity
if (Direction == 0){ //Is entity facing left?
dx = -dx; //Reverse X direction to match facing
}
vShot = projectile(Shot, x+dx, z+dz, y+dy, Direction, 0, 0, 0); // Shoot the projectile!
if( Vx!=NULL() && Vz!=NULL() ){ // Are both velocity available?
changeentityproperty(vShot, "velocity", Vx, Vz); //Move projectile towards target!
if (Direction == 0){ //Is enemy facing left?
changeentityproperty(vShot, "speed", -Vx); // Change speed so it moves left
} else {changeentityproperty(vShot, "speed", Vx);} // Change speed so it moves right
}
}
The function differs alot with previous ones cause this function shoots projectile and alters its speed. 'Projectile' function shoots entity named Shot which is inputted. This projectile function also requires starting position for shot projectile which is procurred by attaining x,z and a coordinate above. Position adjustment is inputted to allow scripter to give position shift to the shot projectile.
vShot gets the shot projectile. After that, Vx and Vz is attained like before to alter projectile's speed with changeentityproperty. However since projectile's flying speed is based on its speed instead of velocity, speed change is required. Since speed isn't relative, we need to get enemy's facing direction 1st before changing the speed with Vx. Negative speed(-Vx) for moving left or positive speed(Vx) for right.
Review
As you can see, targetting is seperated from the action in this method. This allows flexibility in which scripted can use same 'target' function for different attacks. However, there is another important reason behind this seperation.
Take a look at this sample animation:
anim attack1
range 80 250
rangez -45 45
delay 1
offset 50 105
bbox 30 20 70 80
frame data/chars/enemy/charge01.gif
delay 10
@cmd target 5 2.5
frame data/chars/enemy/charge01.gif
frame data/chars/enemy/charge02.gif
delay 5
blast 75 30 50 80 25 1
frame data/chars/enemy/charge03.gif
delay 65
@cmd dash
frame data/chars/enemy/charge03.gif
delay 10
@cmd stop
frame data/chars/enemy/charge04.gif
attack 0
frame data/chars/enemy/charge05.gif
In this attack, enemy target player with speed of 5 or 2.5 at 2nd frame then dash at 5th frame. Enemy stops at 6th frame.
From 'targetting' frame to 'dashing' frame there's 25 centisecond delay.
Now look at this one:
anim attack1
range 80 250
rangez -45 45
delay 1
offset 50 105
bbox 30 20 70 80
frame data/chars/enemy/charge01.gif
delay 10
frame data/chars/enemy/charge01.gif
frame data/chars/enemy/charge02.gif
delay 5
blast 75 30 50 80 25 1
frame data/chars/enemy/charge03.gif
delay 65
@cmd target 5 2.5
@cmd dash
frame data/chars/enemy/charge03.gif
delay 10
@cmd stop
frame data/chars/enemy/charge04.gif
attack 0
frame data/chars/enemy/charge05.gif
The animation is same except that targetting and dashing is done at same frame. In this case, there's practically no delay between targetting and dashing.
What's the point of this? in 1st attack, player can dodge or move away within 25 centiseconds delay but in 2nd one, there's practically no chance to dodge. IOW 2nd attack is more effective than the 1st one. This is assuming player is not blocking or in invicible state.
So does that mean we should avoid giving delays and always declare 'target' and action functions at same frame? No, no and no! This is what's called with intelligence control. Smart enemies have more effective attacks than stupid ones. With this setting, 3D attack effectiveness can be controlled and thus their intelligence. That's the important reason behind this seperation.
Conclusion
I hope you all can understand all above explanation. No need to hurry, reread if you need to. Take all time you need to grasp it.
Anyways, those are just some example of 3D attacks, there are more kinds of them in brawlers games. Most 3D attack in Crime Buster v2.2 are from above type. The rest are other kind of 3D attacks. One of them is shown below. Sweep shoot ala Capcom!
I could share how to make it but I'll do it later cause this thread is already long.
Finally, I'm hoping other mods would be using 3D attacks for enemies too. No need to do it like my mod though.
I've just remember something about targetted shooting above. For semi targetted leap and targetted dash, it's accurate but for targetted shooting, it's accuracy depends on distance between fired projectile and shooter. That means if projectile is fired quite far from shooter, it might miss player although it's targetted.
This could be fixed by inserting the distance in the formula above.
-----------
originally posted by Bloodbane