Random thoughts about scripting - Page 3

Random thoughts about scripting

Discuss game development here, from a distinct game project to an accessible third-party mutator, down to the interaction and design of individual units if you like.

Moderator: Moderators

User avatar
FLOZi
MC: Legacy & Spring 1944 Developer
Posts: 6241
Joined: 29 Apr 2005, 01:14

Post by FLOZi »

zwzsg wrote:I'm sorry, I failed at finding any information on HitByWeaponID(x,z,id,damage) beside what you posted in that thread. I probably searched in the wrong places. Can you link or quote to where you said you had directionnal damage fully working, and, better, link me to a unit which has it? I would be very interested into looking at your code and testing it.
Sorry, I got a little worked up. :oops: I had detailed my efforts more at smoths forum. As for my code, most of the relevant bits are in my previous post. All I left out was use of my new get POW(base, exponent) constant to make damage fall off according to a function which has been used for AATA since about 3 years ago. :wink:
I'm not saying that my script will work with fast weapons hitting on center, I'm saying that, in order to test my script, I evacuated that issue by using a slow shot, fired far from target, and with large aoe shot. It doesn't mean the issue is solved for every weapons, just that it wasn't what caused my problem in that particular case.
Ah I see.

A huge AOE will just mean that the unit registers a (direct) hit on both sides
Err, what? I don't think the Z and X can have both values at once, so that can't be what you mean. Do you mean that HitByWeap gets called twice with large aoe weapons? My weap had quite a large aoe, yet only one side register a hit. But I guess lots of parameter could influence that. Was slow expanding explosion added to Spring for instance?
Yes, I meant that it can get called twice in some instances - Spring has had expanding explosions for a while, and, iirc, there are even tags to effect that behaviour though I haven't used them myself yet.
But bottomline is, if the engine use the value returned by HitByWeap to change the damage dealt, that's very good, and make directionnal damage, and lots other kind of damage tweak, very easy.
Yes it does. Many thanks to yeha for all his work on this. :-) S:44 already uses it for several things, though directional damage was left out for the time being.
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Post by zwzsg »

This is spanning threads over the forums of three different websites, plus PM and IRC (through two diff intermediary persons), no wonder I'm getting lost!

So let's look at what you posted in Smoth's forum:

Code: Select all

/ the following is pseudoBOS
#define   AT_WEAPON   //whatever
#define   COBSCALE      65536
#define   COBSCALE--    65535
#define   FRONT_ARMOUR   // blah
#define   REAR_ARMOUR   // blah
#define   SIDE_ARMOUR   // blah
#define   TURR_FRONT_ARMOUR   // blah
#define   TURR_REAR_ARMOUR   // blah
#define   TURR_SIDE_ARMOUR   // blah
#define E      2.71828

#define   TURR_WEIGHT    //blah
#define   HULL_WEIGHT     //blah

#define TURR_CHECK\
//some nifty code here to check which side of turret has been hit\
armour = (turrArmour * TURR_WEIGHT + hullArmour * HULL_WEIGHT) / (TURR_WEIGHT + HULL_WEIGHT)\

HitByWeaponID(z, x, id, damage)  // note that z is passed before x!
{
   var unitX, unitZ, unitPacked, turrX, turrZ, turrPacked, hullArmour, turrArmour, armour, newDamage;
   
   if (id == AT_WEAPON) {
   
   unitPacked = get UNIT_XZ;
   unitX = unitPacked / COBSCALE;
   unitZ = unitPacked & COBSCALE--;
   
   turrPacked = get PIECE_XZ(flare);
   turrX = (turrPacked / COBSCALE) - unitX;
   turrZ = (turrPacked & COBSCALE--) - unitZ;
   
   if ((z<=x && z>=0 && x>=0) || ((0-z)<=x && z<=0 && x>=0)) {
      hullArmour = FRONT_ARMOUR;
      TURR_CHECK;
   }
   else if (((0-z)<=(0-x) && z<=0 && x<=0) || (z<=(0-x) && z>=0 && x<=0)) {
      hullArmour = REAR_ARMOUR;
      TURR_CHECK;
   }
   else {
      hullArmour = SIDE_ARMOUR;
      TURR_CHECK;
   }
      newDamage = damage * get POW (E * SCALE, (-0.001 * SCALE * (armour - damage)); // /SCALE?
      return newDamage / damage * 100;
   }
}

Ok, let's focus on one bit at a time:

Code: Select all

   unitPacked = get UNIT_XZ;
   unitX = unitPacked / COBSCALE;
   unitZ = unitPacked & COBSCALE--;
I assume get UNIT_XZ is the same as get UNIT_XZ(0), and I know that the unit of ID 0 is some weird thing that isn't really a unit but a place in memory where some but not all the properties of the unit being run are recopied. I would have used get PIECE_XZ(turret) instead, but your get UNIT_XZ probably work, and to each his own way.

Code: Select all

   
   turrPacked = get PIECE_XZ(flare);
   turrX = (turrPacked / COBSCALE) - unitX;
   turrZ = (turrPacked & COBSCALE--) - unitZ;
So now turrX and turrZ are the coordinates of the vector going from unit center to flare. Well, why not? I guess you'll want to use a get ATAN(turrZ,turrX) soon. Personnaly I find it easier to simply condensate it all in single line, get XZ_ATAN(get PIECE_XZ(flare) - get PEICE_XZ(turret)).

However, in the rest of the core, I don't see turrX and turrZ used. Uh? WTF? You calculated them for nothin?

Code: Select all

   if ((z<=x && z>=0 && x>=0) || ((0-z)<=x && z<=0 && x>=0)) {
      ...
   }
   else if (((0-z)<=(0-x) && z<=0 && x<=0) || (z<=(0-x) && z>=0 && x<=0)) {
      ...
   }
   else {
      ...
   }
First, I once tried, and the "else" statment doesn't work with Scriptor. Scriptor doesn't complain and compile without error or warning message, but the "else" bit is ignored and what follow the "else" is executed no matter the condition. Unless you are really sure of you, I suggest testing that the "else" works, and do four independent "if" if "else" doesn't work.

Then, I don't really like writing the four tests for the sign and relative value of x and z, as I find those lines confusing and easy to miswrite. I prefer to use something like:

Code: Select all

quadrant=get ATAN(x,z)/(<90>);
if(quadrant==0)
{
	...
}
if(quadrant==1)
{
	...
}
if(quadrant==2)
{
	...
}
if(quadrant==3)
{
	...
}
However I never remember between Spring and TA which use 0 360° angles and which use -180° 180° angle, and so in case Spring's ATAN returns signed angle, you'll have to use (quadrant=get ATAN(x,z)+(<180>))/(<90>);

Also, since it's more natural to start counting at 1 than at 0, I sometimes add +1

And because we usually want to divide the unit in four like an X and not like a +, I'd have to shift the angle 45° with a +(<45>).

But with my angle shift and the uncertainty about angles returned by ATAN being signed or not, it starts being really necassary to use some modulo. Which I do with:
while(angle<0)
{angle=angle + <360>;}
while(angle> <360>)
{angle=angle - <360>;}
If it had to loop many times, that wouldn't be efficient and instead I would start looking into writing the modulo with a "rest of a division". However in current case it only loops once or twice, so that's okay.

Also, I'm always afraid of rounding error when using the <...>, so instead of writing <45> I write 8192, instead of writing <90> I write 16384, instead of writing <180> I write 32768, instead of writing <360> I write 65536. The effect of the rounding most probably won't cause too much issue, especially since the conversion makes angle lower not higher.

So my script actually is more like:

Code: Select all

tmp_angle=get ATAN(z,x)+8192;

while(tmp_angle<0)
	{tmp_angle=tmp_angle + 65536;}
while(tmp_angle>65535)
	{tmp_angle=tmp_angle - 65536;}

quadrant=(tmp_angle/16384)+1;


if(quadrant==3)//Front
{
	...
}
if(quadrant==1)//Back
{
	...
}
if(quadrant==2 || quadrant==4)//Right or Left
{
	...
}
Which is actually not that more simpler than:

Code: Select all

	if ((z<=x && z>=0 && x>=0) || ((0-z)<=x && z<=0 && x>=0)) {
	...
	}
	if (((0-z)<=(0-x) && z<=0 && x<=0) || (z<=(0-x) && z>=0 && x<=0)) {
	...
	}
	if (.........................) {
      ...
	}
So I guess I could be only a matter of taste to prefer testing x and z than to combine them into the cake slice number.





Ok, back to your script.

I guess REAR_ARMOUR, FRONT_ARMOUR, and SIDE_ARMOUR are simple constant, so lines such as hullArmour = SIDE_ARMOUR; are akin to hullArmour = 100;

The TURR_CHECK; seem to only do a ponderated mean of "hull armour" and "turret armour".

But, uh, why making that distinction between turret and hull, considering that after calculating the coordinate of turret vector, you used it nowhere?

But from a second reading, I guess the line:
//some nifty code here to check which side of turret has been hit\
Must be understood as "code not written yet will go here".
In which case, well, the important part is missing!


Also, in your code, the "if" to determine which part was hit use the Z and X raw from the values passed to HitByWeaponId.

As I said, Spring has a bug, and passes absolute coordinate, not unit relative coordiante, to HitByWeapon. Unless for whacky reason HitByWeaponId use another coordinate system than HitByWeapon, your nice script will only work when the unit faces south.


Crap, I just noticed the script bit you posted on Spring forum seems a bit more advanced than the one posted in Smoth forum. I swear it wasn't in your post when I replied to it.

Code: Select all

#define SOUTH_CHECK(varName) varName >= (0 - FRONT_ARC) && varName <= FRONT_ARC
#define EAST_CHECK(varName) varName > FRONT_ARC && varName < (<180> - REAR_ARC)
#define WEST_CHECK(varName) varName > (0 - (<180> - REAR_ARC)) && varName < (0 - FRONT_ARC)

HitByWeaponId(z,x,id,damage)
{
   var unitPacked, turrPacked, hullArmour, turrArmour, newDamage, unitAngle, hullAngle, turrAngle, tempDamage;
   unitPacked = get PIECE_XZ(turret);
   turrPacked = get PIECE_XZ(flare);
   
   unitAngle = get ATAN(x,z);
   hullAngle = get HEADING;
   turrAngle = get XZ_ATAN(turrPacked - unitPacked);
   
   if (SOUTH_CHECK(unitAngle)) { // South
     if (SOUTH_CHECK(hullAngle)) {
       hullArmour = FRONT_ARMOUR;
       if (SOUTH_CHECK(turrAngle))
                  turrArmour = TURR_FRONT_ARMOUR;
              }
              // etc etc etc etc lots of nasty nested ifs
              // some code to return a nice % if penetration >
              // armour value
              // where armour is some weighted average of
              // turret and hull 
Ok, what do we have here?
First, I'd like to point out two things:

unitAngle = get ATAN(x,z);
This is obviously the angle toward impact. Why label it "unitAngle"? It's "shotAngle"!!!

hullAngle = get HEADING;
Have you just added yourself a new cob get command just to read unit heading? No need for new command, get XZ_ATAN(0 - 1234) does the job fine for me, despite looking odd to the untrained eyes. I guess I'll try to re-explain shortly why:
unit_relative_angle=get XZ_ATAN(map_absolute_packed_xz);
You input a XZ in an axis system that is independant of unit rotation, and you get an angle that depends on the unit rotation, therefore, if you input a constant, what you get is only the "relative to unit" bit. Hrm, I'm unclear, but what I mean is:
angle_between_north_and_unit=get XZ_ATAN(vector_pointing_north);
You know already how x and z are packed into xz, so it is no surpise to you that x=0 z=0 - 1234 (which is pointing north) gets packed as: (0)*65536 + (0 - 1234).
Throw in a couple inversion, and you're set.



Ok, now I'll move on to the "if" part, I'll expand the macro and value given to variable to make it easier on me:

if (SOUTH_CHECK(unitAngle)) becomes
if(varName >= (0 - FRONT_ARC) && varName <= FRONT_ARC) so
if(get ATAN(x,z) >= (0 - FRONT_ARC) && get ATAN(x,z) <= FRONT_ARC)
I don't have the values of FRONT_ARC, but I assume it's something like:
if( get ATAN(x,z) is between -90° and 90°)

So basically what we have is:

Code: Select all

if( shot hit on south of unit, map-wise )
{
	if( hull is turned south, map wise )
	{
		if( turret is turned 180°, unit-wise )
		{
		...
		}
	}
}
And then the same for every four cardinal position of all three angles. If I count well, that's 84 "if"! So either I got my count of "if" wrong, either you're a madmen and wrote pages of "if", either you lied when you said you had it working already.

But there is more concerning than the number of nested "if" such an approach has. Basically, this code suppose all three angles (point of impact, unit heading, turret angle) are right into a cardinal position, and if not they are rounded to nearest 90°.

But when you round three times by 90° consecutively, then the rounding errors start being quit significant!!

Worst case, you have a 135° error, which is really not negligible!

You'd be hitting the sout-west, and it would trigger north armour!

No really.


So I contest your:
<FLOZi> I already have directional damage working as well as you can with ballistic weapons.
- All your script bits are incomplete. Sure, stuff like "#define FRONT_ARMOUR // blah" are trivial, but the "some nifty code here to check which side of turret has been hit" is not, and I still doubt you wrote those 84 "if".
- Script bits are nice, but I'm still waiting for full exemple unit/mod to plug into Spring.
- The rounding error of your method are so large your system is about as reliable as rand(1,4).

So, unless you can show me yet another more advanced version of your directionnal damage, your statment about getting working directionnal working already was unfounded.


But thanks for the help, without you and rattle I would have never know only pushing weapons can cause directionnal damage, and that the return values of HiByWeaponId can be used to change received damage!
User avatar
rattle
Damned Developer
Posts: 8278
Joined: 01 Jun 2006, 13:15

Post by rattle »

First, I once tried, and the "else" statment doesn't work with Scriptor. Scriptor doesn't complain and compile without error or warning message, but the "else" bit is ignored and what follow the "else" is executed no matter the condition. Unless you are really sure of you, I suggest testing that the "else" works, and do four independent "if" if "else" doesn't work.
It sure does, I've been using code like the following few lines before without having any troubles such as an else statement getting ignored at all...

Code: Select all

if { ... }
else if { ... }
else if { ... }
else { ... }
User avatar
FLOZi
MC: Legacy & Spring 1944 Developer
Posts: 6241
Joined: 29 Apr 2005, 01:14

Post by FLOZi »

zwzsg wrote: Crap, I just noticed the script bit you posted on Spring forum seems a bit more advanced than the one posted in Smoth forum.
yeah, everything on Smoths forum in regards to this is out of date.
Ok, what do we have here?
First, I'd like to point out two things:

unitAngle = get ATAN(x,z);
This is obviously the angle toward impact. Why label it "unitAngle"? It's "shotAngle"!!!
yes, it started out as something else, and i just recycled the var because I'm lazy, obvisouly I forgot to rename it. :lol:
hullAngle = get HEADING;
Have you just added yourself a new cob get command just to read unit heading? No need for new command, get XZ_ATAN(0 - 1234) does the job fine for me, despite looking odd to the untrained eyes. I guess I'll try to re-explain shortly why:
unit_relative_angle=get XZ_ATAN(map_absolute_packed_xz);
You input a XZ in an axis system that is independant of unit rotation, and you get an angle that depends on the unit rotation, therefore, if you input a constant, what you get is only the "relative to unit" bit. Hrm, I'm unclear, but what I mean is:
angle_between_north_and_unit=get XZ_ATAN(vector_pointing_north);
You know already how x and z are packed into xz, so it is no surpise to you that x=0 z=0 - 1234 (which is pointing north) gets packed as: (0)*65536 + (0 - 1234).
Throw in a couple inversion, and you're set.
Yes I did add a new cob constant - was it needed? Not really. Is it neater? Much. Did I commit it? Yes, I did.
Ok, now I'll move on to the "if" part, I'll expand the macro and value given to variable to make it easier on me:

if (SOUTH_CHECK(unitAngle)) becomes
if(varName >= (0 - FRONT_ARC) && varName <= FRONT_ARC) so
if(get ATAN(x,z) >= (0 - FRONT_ARC) && get ATAN(x,z) <= FRONT_ARC)
I don't have the values of FRONT_ARC, but I assume it's something like:
if( get ATAN(x,z) is between -90° and 90°)

So basically what we have is:

Code: Select all

if( shot hit on south of unit, map-wise )
{
	if( hull is turned south, map wise )
	{
		if( turret is turned 180°, unit-wise )
		{
		...
		}
	}
}
And then the same for every four cardinal position of all three angles. If I count well, that's 84 "if"! So either I got my count of "if" wrong, either you're a madmen and wrote pages of "if", either you lied when you said you had it working already.
As I said, it's ugly. I haven't written out all the turret ifs yet, but the 16 required for directional damage on the hull are there. I am not a liar.
But there is more concerning than the number of nested "if" such an approach has. Basically, this code suppose all three angles (point of impact, unit heading, turret angle) are right into a cardinal position, and if not they are rounded to nearest 90°.

But when you round three times by 90° consecutively, then the rounding errors start being quit significant!!

Worst case, you have a 135° error, which is really not negligible!

You'd be hitting the sout-west, and it would trigger north armour!

No really.
Care to explain that a little better? :?
So I contest your:
<FLOZi> I already have directional damage working as well as you can with ballistic weapons.
- All your script bits are incomplete. Sure, stuff like "#define FRONT_ARMOUR // blah" are trivial, but the "some nifty code here to check which side of turret has been hit" is not, and I still doubt you wrote those 84 "if".
I haven't coded the turret - but I do have directional damage for the hull. Expanding the system to the turret is simple, if extremely long winded.
- Script bits are nice, but I'm still waiting for full exemple unit/mod to plug into Spring.
I don't really feel like digging through an outdated S:44 dev version to rip out the bits required, but I'll post the full script if you like.
- The rounding error of your method are so large your system is about as reliable as rand(1,4).
You still need to explain why this is true to me.
So, unless you can show me yet another more advanced version of your directionnal damage, your statment about getting working directionnal working already was unfounded.
I am not a liar.
But thanks for the help, without you and rattle I would have never know only pushing weapons can cause directionnal damage, and that the return values of HiByWeaponId can be used to change received damage!
You're welcome.

And as Rattle pointed out, else works fine in Scriptor. Look at compiler.cfg.
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Post by KDR_11k »

zwzsg wrote:No. As I said twice already, I used such a big area of effect weapon that it affects the unit even when it hits 200 m away. When the point of impact is 200 m from the unit, there is zero doubt which side was hit.
No, for his implementation. If it consistently registered the wrong side you'd just adjust your functions but it's behaving randomly.
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Post by zwzsg »

Image


http://spring.unknown-files.net/file/28 ... e_working/

I noticed the "HitByWeaponId" and "the value returned is a multiplier to damage dealt in %" only applied to the nightly build of Spring. Also, if the HitByWeaponId doesn't return immediatly, it doesn't use its returned value. But otherwise, it works!

In Spring 0.75:
x10 damage when hit on back
x1 damage when hit on the side
x0.1 damage when hit on front

In TA and Spring 0.74b3, like the previous one, highlight the part being hit.


As for explanations on why I said your rounding error can go as bad as 135° which make your whole approach fails, consider the following:
Image
The hull is between south and west. Your code round it to south.
The turret is between left and front of the hull. Your code round it to left.
The impact is between south and west. Your code rounds it to west.
So, for your code, it's considered as:
Image
As you can see, initially, the shot was on the front-right of the turret, yet your code will consider it as on the back of the turret.

Because three times, you rounded to nearest 90°.
So three times, you rounded as much as 45°
So worst case total error is 45*3=135°
Which is not acceptable.
User avatar
FLOZi
MC: Legacy & Spring 1944 Developer
Posts: 6241
Joined: 29 Apr 2005, 01:14

Post by FLOZi »

edit: hmm, you have a point. Certainly the nested ifs is not how I wanted to do it, but everyone ignored my thread at TAU asking for some neater way.

Your implementation only considers turret angle does it not?

Mine is more complex in that it first determines the thickness of armour on the side that has been hit, then compares the damage dealt (i.e. the weapons penetration) with that armour and responds accordingly, your's is much simpler in that you can just return a simple % after checking turret rotation compared to hit angle.
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Post by zwzsg »

FLOZi wrote:edit: hmm, you have a point. Certainly the nested ifs is not how I wanted to do it, but everyone ignored my thread at TAU asking for some neater way.
I did not ignore it. However there was no reply after my post, you left me in total disarray, wondering if my post was helpful to you and if you managed to adapt it or what! :cry:
Mine is more complex in that it first determines the thickness of armour on the side that has been hit, then compares the damage dealt (i.e. the weapons penetration) with that armour and responds accordingly, your's is much simpler in that you can just return a simple % after checking turret rotation compared to hit angle.
Err, if you want me to do some ponderated mean between hull armor and turret armor, it's a two minutes change. You can't really cling to that to pretend your non-working script is better. And I did that because I had in mind WhiteHawk PM about mech with rotating torso, only found about your 1994 thing later. But after rereading I realise this is maybe not you meant. And after coding it I realise there's inummerable ways to combine the tree parameters hull_armor, turret_armor, and shot_power into a damage_dealt. Anyway, here's one.

I am not a liar.
I guess we lend different meaning to "I already have directional damage working" then. I consider it true only after having seen it working ingame. You consider it "already working" when you have written half of the bos and planned the rest. :P Ok better stop or it'll sound like I'm attacking you. Beside I believe you had the directionnal damage script without the turret somewhat working ingame already.


It sure does, I've been using code like the following few lines before without having any troubles such as an else statement getting ignored at all...
Ok, I just retried, and now indeed I find "else if" and "else" working. I wonder what was happening last time I tried.
User avatar
FLOZi
MC: Legacy & Spring 1944 Developer
Posts: 6241
Joined: 29 Apr 2005, 01:14

Post by FLOZi »

zwzsg wrote:
FLOZi wrote:edit: hmm, you have a point. Certainly the nested ifs is not how I wanted to do it, but everyone ignored my thread at TAU asking for some neater way.
I did not ignore it. However there was no reply after my post, you left me in total disarray, wondering if my post was helpful to you and if you managed to adapt it or what! :cry:
Doh! I should check TAU more often
I am not a liar.
I guess we lend different meaning to "I already have directional damage working" then. I consider it true only after having seen it working ingame. You consider it "already working" when you have written half of the bos and planned the rest. :P Ok better stop or it'll sound like I'm attacking you. Beside I believe you had the directionnal damage script without the turret somewhat working ingame already.
Well, I overlooked the nested if issue as m,y testing seemd to indicate all was well, but yes, i never bothered finishing the turret chcking as Spiked decided it would be too much micro for S44 players to align all their tank hulls and turrets to face enemy tanks.
It sure does, I've been using code like the following few lines before without having any troubles such as an else statement getting ignored at all...
Ok, I just retried, and now indeed I find "else if" and "else" working. I wonder what was happening last time I tried.
You might be interested in that I also updated Scriptors compiler.cfg to allow all operations supported by Spring, i keep intending to fix up decompiler.cfg and release them.
Post Reply

Return to “Game Development”