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!