mirror of
https://github.com/1dot13/source.git
synced 2026-09-16 14:47:17 +02:00
Spread Patterns MOD (by Zilpin)
* You also have to update GameDir folder * git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@3009 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
@@ -154,6 +154,14 @@ BOOLEAN LoadExternalGameplayData(STR directoryName)
|
|||||||
{
|
{
|
||||||
char fileName[MAX_PATH];
|
char fileName[MAX_PATH];
|
||||||
|
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
//If file not found, or error, then the old hard-coded defaults are used by LOS.cpp
|
||||||
|
//This needs to be loaded before AmmoTypes and Items because SpreadPatterns can be referenced by name or index.
|
||||||
|
strcpy(fileName, directoryName);
|
||||||
|
strcat(fileName, SPREADPATTERNSFILENAME);
|
||||||
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName));
|
||||||
|
ReadInSpreadPatterns(fileName);
|
||||||
|
|
||||||
// WANNE: Enemy drops - begin
|
// WANNE: Enemy drops - begin
|
||||||
strcpy(fileName, directoryName);
|
strcpy(fileName, directoryName);
|
||||||
strcat(fileName, ENEMYMISCDROPSFILENAME);
|
strcat(fileName, ENEMYMISCDROPSFILENAME);
|
||||||
|
|||||||
@@ -720,6 +720,10 @@ typedef struct
|
|||||||
BOOLEAN newinv; // item only available in new inventory mode
|
BOOLEAN newinv; // item only available in new inventory mode
|
||||||
|
|
||||||
UINT16 defaultattachment;
|
UINT16 defaultattachment;
|
||||||
|
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
INT32 spreadPattern;
|
||||||
|
|
||||||
} INVTYPE;
|
} INVTYPE;
|
||||||
|
|
||||||
// CHRISL: Added new structures to handle LBE gear and the two new XML files that will be needed to deal
|
// CHRISL: Added new structures to handle LBE gear and the two new XML files that will be needed to deal
|
||||||
|
|||||||
+290
-31
@@ -106,6 +106,12 @@ UINT32 FPMult32(UINT32 uiA, UINT32 uiB)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
t_SpreadPattern *gpSpreadPattern=NULL;
|
||||||
|
INT32 giSpreadPatternCount=0;
|
||||||
|
char * gSpreadPatternMethodNames[] = {"RECTANGLE","DIAMOND","ELLIPSE",NULL};
|
||||||
|
int giSpreadPatternMethod_Default = SPREADPATTERNMETHOD_ELLIPSE;
|
||||||
|
//zilpin: ddShotgunSpread is only used as a backup if the XML file does not exist.
|
||||||
static DOUBLE ddShotgunSpread[3][BUCKSHOT_SHOTS][2] =
|
static DOUBLE ddShotgunSpread[3][BUCKSHOT_SHOTS][2] =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@@ -150,6 +156,72 @@ static DOUBLE ddShotgunSpread[3][BUCKSHOT_SHOTS][2] =
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
//Decide what spread pattern to use for an object in inventory.
|
||||||
|
//Attachment trumps Weapon trumps Ammo trumps AmmoType
|
||||||
|
//If multiple attachments, then the first one encountered is used.
|
||||||
|
//This will work for non-weapons, but ammo and ammo type are only checked for guns.
|
||||||
|
INT32 GetSpreadPattern( OBJECTTYPE * pObj )
|
||||||
|
{
|
||||||
|
int i=0,n=0;
|
||||||
|
if( !pObj || !pObj->usItem || !pObj->exists() ) return 0;
|
||||||
|
|
||||||
|
//If there are attachments, check them. Stop on the first one with something defined.
|
||||||
|
//Dear God, I hate C++ iterators. What a fugly mess.
|
||||||
|
for (attachmentList::iterator iter = pObj[0][0]->attachments.begin(); iter != pObj[0][0]->attachments.end(); ++iter){
|
||||||
|
if( n=Item[ iter->usItem ].spreadPattern )
|
||||||
|
//An attachment has it, and it trumps everything, so return it's value.
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
//The object's own spread pattern, if it has one.
|
||||||
|
if( n=Item[pObj->usItem].spreadPattern )
|
||||||
|
return n;
|
||||||
|
|
||||||
|
//If a gun, and don't have a pattern yet, check it's ammo.
|
||||||
|
if( IC_GUN & Item[pObj->usItem].usItemClass ){
|
||||||
|
//Check for the ammo first.
|
||||||
|
if( n=Item[ pObj[0][0]->data.gun.usGunAmmoItem ].spreadPattern )
|
||||||
|
return n;
|
||||||
|
//If no pattern for the ammo, try the ammo type.
|
||||||
|
if( n=AmmoTypes[pObj[0][0]->data.gun.ubGunAmmoType].spreadPattern )
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is the old code to iterate through all the attachments.
|
||||||
|
No longer works, since the NewInv project object-orientified everything.
|
||||||
|
*
|
||||||
|
//If there are attachments, check them. Stop on the first one with something defined.
|
||||||
|
for (i=0; i<MAX_ATTACHMENTS; i++)
|
||||||
|
if (pObj->usAttachItem[i])
|
||||||
|
if( n=Item[ pObj->usAttachItem[i] ].spreadPattern )
|
||||||
|
//The attachment has it, and it trumps everything, so return it's value.
|
||||||
|
return n;
|
||||||
|
|
||||||
|
//The object's own spread pattern, if it has one.
|
||||||
|
if( n=Item[pObj->usItem].spreadPattern )
|
||||||
|
return n;
|
||||||
|
|
||||||
|
//If a gun, and don't have a pattern yet, check it's ammo.
|
||||||
|
if( IC_GUN & Item[pObj->usItem].usItemClass ){
|
||||||
|
//Check for the ammo first.
|
||||||
|
if( n=Item[ pObj->ItemData.Gun.usGunAmmoItem ].spreadPattern )
|
||||||
|
return n;
|
||||||
|
//If no pattern for the ammo, try the ammo type.
|
||||||
|
if( n=AmmoTypes[pObj->ItemData.Gun.ubGunAmmoType].spreadPattern )
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
//No pattern for anything found, so use the default.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static UINT8 gubTreeSightReduction[ANIM_STAND + 1] =
|
static UINT8 gubTreeSightReduction[ANIM_STAND + 1] =
|
||||||
{
|
{
|
||||||
0,
|
0,
|
||||||
@@ -3483,6 +3555,15 @@ void CalculateFiringIncrements( DOUBLE ddHorizAngle, DOUBLE ddVerticAngle, DOUBL
|
|||||||
pBullet->qIncrZ = FloatToFixed( (FLOAT) ( sin( ddVerticAngle ) / sin( (PI/2) - ddVerticAngle ) * 2.56 ) );
|
pBullet->qIncrZ = FloatToFixed( (FLOAT) ( sin( ddVerticAngle ) / sin( (PI/2) - ddVerticAngle ) * 2.56 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
//New firing increments calculation, without any adjustment.
|
||||||
|
void CalculateFiringIncrementsSimple( DOUBLE ddHorizAngle, DOUBLE ddVerticAngle, BULLET * pBullet)
|
||||||
|
{
|
||||||
|
pBullet->qIncrX = FloatToFixed( (FLOAT) cos( ddHorizAngle ) );
|
||||||
|
pBullet->qIncrY = FloatToFixed( (FLOAT) sin( ddHorizAngle ) );
|
||||||
|
pBullet->qIncrZ = FloatToFixed( (FLOAT) ( sin( ddVerticAngle ) / sin( (PI/2) - ddVerticAngle ) * 2.56 ) );
|
||||||
|
}
|
||||||
|
|
||||||
INT8 FireBullet( SOLDIERTYPE * pFirer, BULLET * pBullet, BOOLEAN fFake )
|
INT8 FireBullet( SOLDIERTYPE * pFirer, BULLET * pBullet, BOOLEAN fFake )
|
||||||
{
|
{
|
||||||
//DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("FireBullet"));
|
//DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("FireBullet"));
|
||||||
@@ -3577,36 +3658,37 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA
|
|||||||
{
|
{
|
||||||
//DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"FireBulletGivenTarget");
|
//DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"FireBulletGivenTarget");
|
||||||
// fFake indicates that we should set things up for a call to ChanceToGetThrough
|
// fFake indicates that we should set things up for a call to ChanceToGetThrough
|
||||||
FLOAT dStartZ;
|
FLOAT dStartZ=0;
|
||||||
|
|
||||||
FLOAT d2DDistance;
|
FLOAT d2DDistance=0;
|
||||||
FLOAT dDeltaX;
|
FLOAT dDeltaX=0;
|
||||||
FLOAT dDeltaY;
|
FLOAT dDeltaY=0;
|
||||||
FLOAT dDeltaZ;
|
FLOAT dDeltaZ=0;
|
||||||
|
|
||||||
FLOAT dStartX;
|
FLOAT dStartX=0;
|
||||||
FLOAT dStartY;
|
FLOAT dStartY=0;
|
||||||
|
|
||||||
DOUBLE ddOrigHorizAngle;
|
DOUBLE ddOrigHorizAngle=0;
|
||||||
DOUBLE ddOrigVerticAngle;
|
DOUBLE ddOrigVerticAngle=0;
|
||||||
DOUBLE ddHorizAngle;
|
DOUBLE ddHorizAngle=0;
|
||||||
DOUBLE ddVerticAngle;
|
DOUBLE ddVerticAngle=0;
|
||||||
DOUBLE ddAdjustedHorizAngle;
|
DOUBLE ddAdjustedHorizAngle=0;
|
||||||
DOUBLE ddAdjustedVerticAngle;
|
DOUBLE ddAdjustedVerticAngle=0;
|
||||||
DOUBLE ddDummyHorizAngle;
|
DOUBLE ddDummyHorizAngle=0;
|
||||||
DOUBLE ddDummyVerticAngle;
|
DOUBLE ddDummyVerticAngle=0;
|
||||||
|
|
||||||
BULLET * pBullet;
|
BULLET * pBullet=NULL;
|
||||||
INT32 iBullet;
|
INT32 iBullet=0;
|
||||||
|
|
||||||
INT32 iDistance;
|
INT32 iDistance=0;
|
||||||
|
|
||||||
UINT8 ubLoop;
|
UINT8 ubLoop=0;
|
||||||
UINT8 ubShots;
|
UINT8 ubShots=0;
|
||||||
UINT8 ubImpact;
|
UINT8 ubImpact=0;
|
||||||
INT8 bCTGT;
|
INT8 bCTGT;
|
||||||
UINT8 ubSpreadIndex = 0;
|
UINT8 ubSpreadIndex = 0;
|
||||||
UINT16 usBulletFlags = 0;
|
UINT16 usBulletFlags = 0;
|
||||||
|
int n=0;
|
||||||
|
|
||||||
//DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("FireBulletGivenTarget"));
|
//DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("FireBulletGivenTarget"));
|
||||||
|
|
||||||
@@ -3694,6 +3776,12 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA
|
|||||||
}
|
}
|
||||||
|
|
||||||
ubImpact =(UINT8) GetDamage(&pFirer->inv[pFirer->ubAttackingHand]);
|
ubImpact =(UINT8) GetDamage(&pFirer->inv[pFirer->ubAttackingHand]);
|
||||||
|
|
||||||
|
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
/* zilpin: The section below, including line comments, is the original adjustment made to multiple projectile stats.
|
||||||
|
Left in comments for reference, but the new handling is after it.
|
||||||
|
The fBuckshot flag is now ignored, so it can probably be taken out of the function definition.
|
||||||
// if (!fFake)
|
// if (!fFake)
|
||||||
{
|
{
|
||||||
if (fBuckshot)
|
if (fBuckshot)
|
||||||
@@ -3722,6 +3810,25 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA
|
|||||||
ubImpact = (UINT8) (ubImpact * AmmoTypes[pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunAmmoType].multipleBulletDamageMultiplier / max(1,AmmoTypes[pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunAmmoType].multipleBulletDamageDivisor) );
|
ubImpact = (UINT8) (ubImpact * AmmoTypes[pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunAmmoType].multipleBulletDamageMultiplier / max(1,AmmoTypes[pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunAmmoType].multipleBulletDamageDivisor) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
//zilpin: Begin new code block for spread patterns, number of projectiles, impact adjustment, etc.
|
||||||
|
{
|
||||||
|
ObjectData *weapon = &(pFirer->inv[pFirer->ubAttackingHand][0]->data);
|
||||||
|
ubShots = AmmoTypes[ weapon->gun.ubGunAmmoType].numberOfBullets;
|
||||||
|
ubSpreadIndex = GetSpreadPattern( &pFirer->inv[pFirer->ubAttackingHand] );
|
||||||
|
if( ubShots>1 && !fFake )
|
||||||
|
{
|
||||||
|
fBuckshot = true;
|
||||||
|
usBulletFlags |= BULLET_FLAG_BUCKSHOT;
|
||||||
|
ubImpact = (UINT8) (ubImpact * AmmoTypes[weapon->gun.ubGunAmmoType].multipleBulletDamageMultiplier / max(1,AmmoTypes[weapon->gun.ubGunAmmoType].multipleBulletDamageDivisor) );
|
||||||
|
if (pFirer->ubTargetID != NOBODY)
|
||||||
|
{
|
||||||
|
MercPtrs[ pFirer->ubTargetID ]->bNumPelletsHitBy = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
weapon=NULL;
|
||||||
|
}
|
||||||
|
//zilpin: End of new code block.
|
||||||
|
|
||||||
// GET BULLET
|
// GET BULLET
|
||||||
for (ubLoop = 0; ubLoop < ubShots; ubLoop++)
|
for (ubLoop = 0; ubLoop < ubShots; ubLoop++)
|
||||||
@@ -3759,7 +3866,11 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ubLoop == 0 )
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
//Now the first shot will use the pattern, as well.
|
||||||
|
//Single shot weapons will still be stuck with straight-ahead only, though.
|
||||||
|
//if ( ubLoop == 0 )
|
||||||
|
if( ubShots == 1 )
|
||||||
{
|
{
|
||||||
ddHorizAngle = ddOrigHorizAngle;
|
ddHorizAngle = ddOrigHorizAngle;
|
||||||
ddVerticAngle = ddOrigVerticAngle;
|
ddVerticAngle = ddOrigVerticAngle;
|
||||||
@@ -3779,16 +3890,156 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA
|
|||||||
CalculateFiringIncrements( ddHorizAngle, ddVerticAngle, d2DDistance, pBullet, &ddAdjustedHorizAngle, &ddAdjustedVerticAngle );
|
CalculateFiringIncrements( ddHorizAngle, ddVerticAngle, d2DDistance, pBullet, &ddAdjustedHorizAngle, &ddAdjustedVerticAngle );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if( ubShots > 1 )
|
||||||
{
|
{
|
||||||
|
//Calculate the deviation due to to-hit, but only for the first shot.
|
||||||
|
//The bullet is only used temporarily. It gets reset later.
|
||||||
|
//Should this use d3DDistance?
|
||||||
|
if( ubLoop<1 ){
|
||||||
|
CalculateFiringIncrements( ddOrigHorizAngle, ddOrigVerticAngle, d2DDistance, pBullet, &ddAdjustedHorizAngle, &ddAdjustedVerticAngle );
|
||||||
|
}
|
||||||
|
|
||||||
// temporarily set bullet's sHitBy value to 0 to get unadjusted angles
|
// temporarily set bullet's sHitBy value to 0 to get unadjusted angles
|
||||||
pBullet->sHitBy = 0;
|
//Note: this no longer needs to be done since the CalculateFiringIncrementsSimple is being used below.
|
||||||
|
//pBullet->sHitBy = 0;
|
||||||
|
|
||||||
ddHorizAngle = ddAdjustedHorizAngle + ddShotgunSpread[ubSpreadIndex][ubLoop][0];
|
//Also prevent out-of-bounds overflows.
|
||||||
ddVerticAngle = ddAdjustedVerticAngle + ddShotgunSpread[ubSpreadIndex][ubLoop][1];
|
//Rotates through the array if more pellets than expected, and uses pattern index 0 if requested pattern does not exist.
|
||||||
|
if( gpSpreadPattern == NULL || giSpreadPatternCount<1 ){
|
||||||
|
//XML file missing, or empty, or error while loading. Use hard-coded defaults.
|
||||||
|
//This is from the original code.
|
||||||
|
if( ubSpreadIndex > 2 || ubSpreadIndex<0 ) ubSpreadIndex=0;
|
||||||
|
n = ubLoop % BUCKSHOT_SHOTS;
|
||||||
|
ddHorizAngle = ddAdjustedHorizAngle + ddShotgunSpread[ubSpreadIndex][n][0];
|
||||||
|
ddVerticAngle = ddAdjustedVerticAngle + ddShotgunSpread[ubSpreadIndex][n][1];
|
||||||
|
}else{
|
||||||
|
double d=0,r=0,xspread=0,yspread=0;
|
||||||
|
int n=0;
|
||||||
|
|
||||||
CalculateFiringIncrements( ddHorizAngle, ddVerticAngle, d2DDistance, pBullet, &ddDummyHorizAngle, &ddDummyVerticAngle );
|
//Use spread patterns loaded from XML.
|
||||||
pBullet->sHitBy = sHitBy;
|
if( ubSpreadIndex >= giSpreadPatternCount || ubSpreadIndex < 0)
|
||||||
|
ubSpreadIndex=0;
|
||||||
|
|
||||||
|
xspread=gpSpreadPattern[ubSpreadIndex].xspread;
|
||||||
|
yspread=gpSpreadPattern[ubSpreadIndex].yspread;
|
||||||
|
|
||||||
|
//Only use randomized spread pattern if the random spread is defined AND each static angle already fired once.
|
||||||
|
if( ubLoop >= gpSpreadPattern[ubSpreadIndex].iCount && (xspread + yspread) ){
|
||||||
|
//Create random angle within range, positive and negative.
|
||||||
|
switch( gpSpreadPattern[ubSpreadIndex].method )
|
||||||
|
{
|
||||||
|
case SPREADPATTERNMETHOD_RECT: //Rectangle Method. (Simple)
|
||||||
|
//Applying a new random number to each angle results in a rectangular spread pattern, rather than an oval one.
|
||||||
|
ddHorizAngle = (double)rand() * 2 * xspread / RAND_MAX - xspread;
|
||||||
|
ddVerticAngle = (double)rand() * 2 * yspread / RAND_MAX - yspread;
|
||||||
|
break;
|
||||||
|
case SPREADPATTERNMETHOD_DIAMOND: //Diamond Method. (Kinda Simple)
|
||||||
|
//Angles are generated within a diamond shaped region.
|
||||||
|
//This is more natural than the rectangular pattern, but still not optimal.
|
||||||
|
//The first random number is in a range of 0 to (x+y).
|
||||||
|
d = (xspread+yspread) * (double)rand() / (double)RAND_MAX;
|
||||||
|
//The second random number determines the percentage of that value to use on x. The rest is spent on y.
|
||||||
|
r = (double)rand() / (double)RAND_MAX;
|
||||||
|
ddHorizAngle = r*d;
|
||||||
|
ddVerticAngle = (1-r)*d;
|
||||||
|
//Positive and negative are then randomly determined. Otherwise, everthing would always shoot to the high right (+,+).
|
||||||
|
n = rand()%4;
|
||||||
|
if(n&1) ddHorizAngle *= -1;
|
||||||
|
if(n&2) ddVerticAngle *= -1;
|
||||||
|
break;
|
||||||
|
case SPREADPATTERNMETHOD_ELLIPSE: //Ellipse Method.
|
||||||
|
//Angles are generated within an ellipse.
|
||||||
|
//This is getting close to true spread behaviour, and may be the preferred general purpose method.
|
||||||
|
//However, due to the distribution of random numbers for r, the pattern generated tends to have
|
||||||
|
//more pellets land along the axis, making the pattern look a bit like the Swiss cross sometimes.
|
||||||
|
//(It only becomes noticable in simulations I do in an external program. In-game it looks fine.)
|
||||||
|
//
|
||||||
|
//First, get our random range of -pi to pi. This could be recalculated for every use, but doesn't need to be.
|
||||||
|
r=(double)rand()*2*PI/RAND_MAX - PI;
|
||||||
|
//Which axis is our major axis?
|
||||||
|
if( xspread > yspread )
|
||||||
|
{
|
||||||
|
//The Ellipse.
|
||||||
|
// Any point on an Ellipse border line = (x,y)
|
||||||
|
// where x = m * cos(r) and y = n * sin(r)
|
||||||
|
// where m = major axis radius and n = minor axis radius and r = all values -pi <= r <= pi
|
||||||
|
//We use any random value between -pi and pi, but vary the radius to get points inside the ellipse as well.
|
||||||
|
ddHorizAngle = ( (double)rand()*xspread / RAND_MAX ) * cos(r);
|
||||||
|
ddVerticAngle = ( (double)rand()*yspread / RAND_MAX ) * sin(r);
|
||||||
|
}else{
|
||||||
|
//Reverse sine and cosine if our major axis is y.
|
||||||
|
ddHorizAngle = ( (double)rand()*xspread / RAND_MAX ) * sin(r);
|
||||||
|
ddVerticAngle = ( (double)rand()*yspread / RAND_MAX ) * cos(r);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case -1: //Optimal Method. (Most realistic)
|
||||||
|
//Not yet implemented.
|
||||||
|
//Using the random distribution above, normal distrubution causes more pellets to end up toward the middle.
|
||||||
|
//This is generally not noticable, and in fact feels more natural to some people (not zilpin),
|
||||||
|
//but is not _even_ distribution.
|
||||||
|
//In addition, since it is random, it is possible for all of the pellets to vear off center,
|
||||||
|
//resulting in rare unnatural freak shots, which would never occur in a real shotgun.
|
||||||
|
//This may add to gameplay, but it may bother some people (like zilpin).
|
||||||
|
//Should anyone devise a way to adjust the ellipse distribution to account for this, here is where to put it.
|
||||||
|
//This would probably be done by adjusting the random value of 'r' above.
|
||||||
|
//Contact zilpin for real life shotgun spread pattern data, if you happen to be one of those mathematicians
|
||||||
|
//who can extrapolate a succinct algorithm from raw data. But keep in mind that there's not much data to go on.
|
||||||
|
//
|
||||||
|
//Also note that in the real world, shot spread tends to take the shape of a sagging funnel, rather than a cone.
|
||||||
|
//That is to say, the path of each pellet follows a mild curve, not a straight line.
|
||||||
|
//I'm really not going to worry about that here. Ever.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//If an invalid method is set in the structure, no randomized pellets are shot.
|
||||||
|
//Note that there still may have been static angle pellets fired.
|
||||||
|
//This should never happen, since the XML loading function will set them to giSpreadPatternMethod_Default.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}else if( gpSpreadPattern[ubSpreadIndex].iCount>0 ){
|
||||||
|
//Use static angle, if they exist.
|
||||||
|
n = ubLoop % gpSpreadPattern[ubSpreadIndex].iCount;
|
||||||
|
ddHorizAngle = gpSpreadPattern[ubSpreadIndex].x[n];
|
||||||
|
ddVerticAngle = gpSpreadPattern[ubSpreadIndex].y[n];
|
||||||
|
}else{
|
||||||
|
//No angles defined, so just fire straight.
|
||||||
|
ddHorizAngle = ddVerticAngle = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//#ifdef JA2TESTVERSION
|
||||||
|
DOUBLE ddRawHorizAngle = ddHorizAngle;
|
||||||
|
DOUBLE ddRawVerticAngle = ddVerticAngle;
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
//Adjust based on the to-hit deviation calculated when the first pellet was fired.
|
||||||
|
ddHorizAngle += ddAdjustedHorizAngle;
|
||||||
|
ddVerticAngle += ddAdjustedVerticAngle;
|
||||||
|
|
||||||
|
//Logging for debugging
|
||||||
|
//#ifdef JA2TESTVERSION
|
||||||
|
if(!fFake)
|
||||||
|
{
|
||||||
|
FILE *OutFile;
|
||||||
|
if ((OutFile = fopen("SpreadPatternLog.txt", "a+t")) != NULL)
|
||||||
|
{
|
||||||
|
//To easily cut-and-paste these values from the log into a C/C++ source file for later analysis
|
||||||
|
//Lots of reference debug info in a comment.
|
||||||
|
fprintf(OutFile, "{ % 9.8f , % 9.8f , % 9.8f , % 9.8f }, //DEBUG: merc %4d fired pellet %4d of %4d using method %4d %12s with SpreadPattern %4d %s\n",
|
||||||
|
ddRawHorizAngle, ddRawVerticAngle,
|
||||||
|
ddHorizAngle, ddVerticAngle,
|
||||||
|
pFirer->ubID, ubLoop, ubShots,
|
||||||
|
gpSpreadPattern[ubSpreadIndex].method, gSpreadPatternMethodNames[gpSpreadPattern[ubSpreadIndex].method],
|
||||||
|
ubSpreadIndex, gpSpreadPattern[ubSpreadIndex].Name,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
fclose(OutFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//Just calculate the increments the bullet will use, not any of the to-hit adjustments, because we already did.
|
||||||
|
CalculateFiringIncrementsSimple( ddHorizAngle, ddVerticAngle, pBullet );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -3811,10 +4062,17 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA
|
|||||||
|
|
||||||
// apply increments for first move
|
// apply increments for first move
|
||||||
|
|
||||||
pBullet->qCurrX = FloatToFixed( dStartX ) + pBullet->qIncrX;
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
pBullet->qCurrY = FloatToFixed( dStartY ) + pBullet->qIncrY;
|
//This is a bugfix for strange behavior when firing, such as the projectile hitting walls behind a merc.
|
||||||
pBullet->qCurrZ = FloatToFixed( dStartZ ) + pBullet->qIncrZ;
|
//The bullet should start it's journey at the beginning, not down range.
|
||||||
|
//Commented out the qIncr adjustments.
|
||||||
|
pBullet->qCurrX = FloatToFixed( dStartX ) ; //+ pBullet->qIncrX;
|
||||||
|
pBullet->qCurrY = FloatToFixed( dStartY ) ; //+ pBullet->qIncrY;
|
||||||
|
pBullet->qCurrZ = FloatToFixed( dStartZ ) ; //+ pBullet->qIncrZ;
|
||||||
|
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
//Also bugfix. I understand why this was added, but it causes problems.
|
||||||
|
/*
|
||||||
// NB we can only apply correction for leftovers if the bullet is going to hit
|
// NB we can only apply correction for leftovers if the bullet is going to hit
|
||||||
// because otherwise the increments are not right for the calculations!
|
// because otherwise the increments are not right for the calculations!
|
||||||
if ( pBullet->sHitBy >= 0 )
|
if ( pBullet->sHitBy >= 0 )
|
||||||
@@ -3823,6 +4081,7 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA
|
|||||||
pBullet->qCurrY += ( FloatToFixed( dDeltaY ) - pBullet->qIncrY * iDistance ) / 2;
|
pBullet->qCurrY += ( FloatToFixed( dDeltaY ) - pBullet->qIncrY * iDistance ) / 2;
|
||||||
pBullet->qCurrZ += ( FloatToFixed( dDeltaZ ) - pBullet->qIncrZ * iDistance ) / 2;
|
pBullet->qCurrZ += ( FloatToFixed( dDeltaZ ) - pBullet->qIncrZ * iDistance ) / 2;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pBullet->iImpact = ubImpact;
|
pBullet->iImpact = ubImpact;
|
||||||
|
|
||||||
|
|||||||
@@ -165,4 +165,33 @@ extern LOSResults gLOSTestResults;
|
|||||||
void MoveBullet( INT32 iBullet );
|
void MoveBullet( INT32 iBullet );
|
||||||
//BOOLEAN FireBullet2( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOAT dEndZ, INT16 sHitBy );
|
//BOOLEAN FireBullet2( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOAT dEndZ, INT16 sHitBy );
|
||||||
|
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
#define SPREADPATTERN_NAME_SIZE 32
|
||||||
|
enum SpreadPatternMethod_enum
|
||||||
|
{
|
||||||
|
SPREADPATTERNMETHOD_RECT = 0,
|
||||||
|
SPREADPATTERNMETHOD_DIAMOND,
|
||||||
|
SPREADPATTERNMETHOD_ELLIPSE,
|
||||||
|
SPREADPATTERNMETHOD_COUNT,
|
||||||
|
};
|
||||||
|
extern char *gSpreadPatternMethodNames[];
|
||||||
|
extern int giSpreadPatternMethod_Default;
|
||||||
|
typedef struct SpreadPattern_struct
|
||||||
|
{
|
||||||
|
//Unique name.
|
||||||
|
char Name[SPREADPATTERN_NAME_SIZE];
|
||||||
|
//Max spread for randomized cone of fire.
|
||||||
|
DOUBLE xspread,yspread;
|
||||||
|
//Method (i.e. algorithm) used to distribute randomized cone.
|
||||||
|
int method;
|
||||||
|
//List of statically defined spread angles.
|
||||||
|
//These get used first.
|
||||||
|
INT32 iCount;
|
||||||
|
DOUBLE *x;
|
||||||
|
DOUBLE *y;
|
||||||
|
} t_SpreadPattern;
|
||||||
|
extern t_SpreadPattern *gpSpreadPattern;
|
||||||
|
extern INT32 giSpreadPatternCount;
|
||||||
|
extern INT32 GetSpreadPattern( OBJECTTYPE * pObj );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -5636,6 +5636,10 @@
|
|||||||
<File
|
<File
|
||||||
RelativePath=".\XML_Sounds.cpp">
|
RelativePath=".\XML_Sounds.cpp">
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\XML_SpreadPatterns.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\XML_TonyInventory.cpp">
|
RelativePath=".\XML_TonyInventory.cpp">
|
||||||
</File>
|
</File>
|
||||||
|
|||||||
@@ -1002,6 +1002,10 @@
|
|||||||
RelativePath=".\XML_Sounds.cpp"
|
RelativePath=".\XML_Sounds.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\XML_SpreadPatterns.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\XML_TonyInventory.cpp"
|
RelativePath=".\XML_TonyInventory.cpp"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -185,6 +185,9 @@ typedef struct
|
|||||||
INT16 lockBustingPower;
|
INT16 lockBustingPower;
|
||||||
BOOLEAN tracerEffect;
|
BOOLEAN tracerEffect;
|
||||||
|
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
INT32 spreadPattern;
|
||||||
|
|
||||||
} AMMOTYPE;
|
} AMMOTYPE;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
|||||||
@@ -108,9 +108,14 @@ typedef PARSE_STAGE;
|
|||||||
// WANNE: Sector loadscreens [2007-05-18]
|
// WANNE: Sector loadscreens [2007-05-18]
|
||||||
#define SECTORLOADSCREENSFILENAME "Map\\SectorLoadscreens.xml"
|
#define SECTORLOADSCREENSFILENAME "Map\\SectorLoadscreens.xml"
|
||||||
|
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
#define SPREADPATTERNSFILENAME "SpreadPatterns.xml"
|
||||||
|
#define DELIVERYMETHODSFILENAME "Map\\DeliveryMethods.xml"
|
||||||
|
|
||||||
// Dealtar: Shipping destinations and delivery methods
|
// Dealtar: Shipping destinations and delivery methods
|
||||||
#define SHIPPINGDESTINATIONSFILENAME "Map\\ShippingDestinations.xml"
|
#define SHIPPINGDESTINATIONSFILENAME "Map\\ShippingDestinations.xml"
|
||||||
#define DELIVERYMETHODSFILENAME "Map\\DeliveryMethods.xml"
|
#define DELIVERYMETHODSFILENAME "Map\\DeliveryMethods.xml"
|
||||||
|
|
||||||
// Gotthard: Laptop Text files [2007-10-16]
|
// Gotthard: Laptop Text files [2007-10-16]
|
||||||
#define LAPTOPFLORISTTEXTFILENAME "Laptop\\Florist.xml"
|
#define LAPTOPFLORISTTEXTFILENAME "Laptop\\Florist.xml"
|
||||||
#define LAPTOPFUNERALTEXTFILENAME "Laptop\\Funeral.xml"
|
#define LAPTOPFUNERALTEXTFILENAME "Laptop\\Funeral.xml"
|
||||||
@@ -233,6 +238,11 @@ extern BOOLEAN ReadInRoamingInfo(STR filename);
|
|||||||
// Dealtar: New shipping system XMLs
|
// Dealtar: New shipping system XMLs
|
||||||
extern BOOLEAN ReadInShippingDestinations(STR fileName, BOOLEAN localizedVersion);
|
extern BOOLEAN ReadInShippingDestinations(STR fileName, BOOLEAN localizedVersion);
|
||||||
extern BOOLEAN ReadInDeliveryMethods(STR fileName);
|
extern BOOLEAN ReadInDeliveryMethods(STR fileName);
|
||||||
|
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
extern BOOLEAN ReadInSpreadPatterns(STR fileName);
|
||||||
|
extern BOOLEAN WriteSpreadPatterns();
|
||||||
|
extern int FindSpreadPatternIndex( const STR strName );
|
||||||
//Gotthard: Laptop Florist Text
|
//Gotthard: Laptop Florist Text
|
||||||
extern BOOLEAN ReadInFloristText(STR fileName);
|
extern BOOLEAN ReadInFloristText(STR fileName);
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ ammotypeStartElementHandle(void *userData, const XML_Char *name, const XML_Char
|
|||||||
strcmp(name, "canGoThrough") == 0 ||
|
strcmp(name, "canGoThrough") == 0 ||
|
||||||
strcmp(name, "standardIssue") == 0 ||
|
strcmp(name, "standardIssue") == 0 ||
|
||||||
strcmp(name, "numberOfBullets") == 0 ||
|
strcmp(name, "numberOfBullets") == 0 ||
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
stricmp(name, "spreadPattern") == 0 ||
|
||||||
strcmp(name, "highExplosive") == 0 ||
|
strcmp(name, "highExplosive") == 0 ||
|
||||||
strcmp(name, "explosionSize") == 0 ||
|
strcmp(name, "explosionSize") == 0 ||
|
||||||
strcmp(name, "antiTank") == 0 ||
|
strcmp(name, "antiTank") == 0 ||
|
||||||
@@ -223,6 +225,12 @@ ammotypeEndElementHandle(void *userData, const XML_Char *name)
|
|||||||
pData->curElement = ELEMENT;
|
pData->curElement = ELEMENT;
|
||||||
pData->curAmmoType.numberOfBullets = (UINT8) atol(pData->szCharData);
|
pData->curAmmoType.numberOfBullets = (UINT8) atol(pData->szCharData);
|
||||||
}
|
}
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
else if(stricmp(name, "spreadPattern") == 0)
|
||||||
|
{
|
||||||
|
pData->curElement = ELEMENT;
|
||||||
|
pData->curAmmoType.spreadPattern = FindSpreadPatternIndex( pData->szCharData );
|
||||||
|
}
|
||||||
else if(strcmp(name, "highExplosive") == 0)
|
else if(strcmp(name, "highExplosive") == 0)
|
||||||
{
|
{
|
||||||
pData->curElement = ELEMENT;
|
pData->curElement = ELEMENT;
|
||||||
|
|||||||
@@ -0,0 +1,400 @@
|
|||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
#ifdef PRECOMPILEDHEADERS
|
||||||
|
#include "Tactical All.h"
|
||||||
|
#else
|
||||||
|
#include "sgp.h"
|
||||||
|
#include "overhead types.h"
|
||||||
|
#include "Sound Control.h"
|
||||||
|
#include "Soldier Control.h"
|
||||||
|
#include "overhead.h"
|
||||||
|
#include "Event Pump.h"
|
||||||
|
#include "weapons.h"
|
||||||
|
#include "Animation Control.h"
|
||||||
|
#include "sys globals.h"
|
||||||
|
#include "Handle UI.h"
|
||||||
|
#include "Isometric Utils.h"
|
||||||
|
#include "worldman.h"
|
||||||
|
#include "math.h"
|
||||||
|
#include "points.h"
|
||||||
|
#include "ai.h"
|
||||||
|
#include "los.h"
|
||||||
|
#include "renderworld.h"
|
||||||
|
#include "opplist.h"
|
||||||
|
#include "interface.h"
|
||||||
|
#include "message.h"
|
||||||
|
#include "campaign.h"
|
||||||
|
#include "items.h"
|
||||||
|
#include "weapons.h"
|
||||||
|
#include "text.h"
|
||||||
|
#include "Soldier Profile.h"
|
||||||
|
#include "tile animation.h"
|
||||||
|
#include "Dialogue Control.h"
|
||||||
|
#include "SkillCheck.h"
|
||||||
|
#include "explosion control.h"
|
||||||
|
#include "Quests.h"
|
||||||
|
#include "Physics.h"
|
||||||
|
#include "Random.h"
|
||||||
|
#include "Vehicles.h"
|
||||||
|
#include "bullets.h"
|
||||||
|
#include "morale.h"
|
||||||
|
#include "meanwhile.h"
|
||||||
|
#include "SkillCheck.h"
|
||||||
|
#include "gamesettings.h"
|
||||||
|
#include "SaveLoadMap.h"
|
||||||
|
#include "Debug Control.h"
|
||||||
|
#include "expat.h"
|
||||||
|
#include "XML.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LIST_BUFFER_LEN (32)
|
||||||
|
#define LIST_BUFFER_SIZE (LIST_BUFFER_LEN * sizeof(t_SpreadPattern))
|
||||||
|
#define CALC_NEW_LIST_SIZE(c) ( LIST_BUFFER_SIZE * (1+ ((c) / LIST_BUFFER_LEN)) )
|
||||||
|
|
||||||
|
#define ARRAY_BUFFER_LEN (32)
|
||||||
|
#define ARRAY_BUFFER_SIZE (ARRAY_BUFFER_LEN * sizeof(DOUBLE))
|
||||||
|
#define CALC_NEW_ARRAY_SIZE(c) ( ARRAY_BUFFER_SIZE * (1+ ((c) / ARRAY_BUFFER_LEN)) )
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
PARSE_STAGE curElement;
|
||||||
|
|
||||||
|
CHAR8 szCharData[MAX_CHAR_DATA_LENGTH+1];
|
||||||
|
|
||||||
|
UINT32 currentDepth;
|
||||||
|
UINT32 maxReadDepth;
|
||||||
|
}
|
||||||
|
typedef spreadpatternParseData;
|
||||||
|
|
||||||
|
|
||||||
|
static void XMLCALL
|
||||||
|
spreadpatternStartElementHandle(void *userData, const XML_Char *name, const XML_Char **atts)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
spreadpatternParseData * pData = (spreadpatternParseData *)userData;
|
||||||
|
|
||||||
|
if(pData->currentDepth <= pData->maxReadDepth) //are we reading this element?
|
||||||
|
{
|
||||||
|
if(stricmp(name, "SPREADPATTERNLIST") == 0 && pData->curElement == ELEMENT_NONE)
|
||||||
|
{
|
||||||
|
pData->curElement = ELEMENT_LIST;
|
||||||
|
|
||||||
|
giSpreadPatternCount = 0;
|
||||||
|
gpSpreadPattern = NULL;
|
||||||
|
gpSpreadPattern = (t_SpreadPattern *)MemAlloc( LIST_BUFFER_SIZE );
|
||||||
|
Assert(gpSpreadPattern);
|
||||||
|
memset( gpSpreadPattern, 0, LIST_BUFFER_SIZE );
|
||||||
|
|
||||||
|
pData->maxReadDepth++; //we are not skipping this element
|
||||||
|
}
|
||||||
|
else if(stricmp(name, "SPREADPATTERN") == 0 && pData->curElement == ELEMENT_LIST)
|
||||||
|
{
|
||||||
|
pData->curElement = ELEMENT_LIST;
|
||||||
|
|
||||||
|
giSpreadPatternCount++;
|
||||||
|
if( !(giSpreadPatternCount % LIST_BUFFER_LEN) ){
|
||||||
|
//End of buffer. Expand buffer.
|
||||||
|
gpSpreadPattern = (t_SpreadPattern *)MemRealloc(gpSpreadPattern, CALC_NEW_LIST_SIZE(giSpreadPatternCount) );
|
||||||
|
Assert(gpSpreadPattern);
|
||||||
|
}
|
||||||
|
memset( &(gpSpreadPattern[giSpreadPatternCount-1]) , 0 , sizeof(t_SpreadPattern) );
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].iCount = 0;
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].xspread = 0.0;
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].yspread = 0.0;
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].method = giSpreadPatternMethod_Default;
|
||||||
|
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].x = (DOUBLE *)MemAlloc( ARRAY_BUFFER_SIZE );
|
||||||
|
Assert( gpSpreadPattern[giSpreadPatternCount-1].x );
|
||||||
|
memset( gpSpreadPattern[giSpreadPatternCount-1].x , 0 , ARRAY_BUFFER_SIZE );
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].y = (DOUBLE *)MemAlloc( ARRAY_BUFFER_SIZE );
|
||||||
|
Assert( gpSpreadPattern[giSpreadPatternCount-1].y );
|
||||||
|
memset( gpSpreadPattern[giSpreadPatternCount-1].y , 0 , ARRAY_BUFFER_SIZE );
|
||||||
|
|
||||||
|
pData->maxReadDepth++; //we are not skipping this element
|
||||||
|
}
|
||||||
|
else if(stricmp(name, "p") == 0 )//&& pData->curElement == ELEMENT_LIST)
|
||||||
|
{
|
||||||
|
pData->curElement = ELEMENT;
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].iCount++;
|
||||||
|
if( !(gpSpreadPattern[giSpreadPatternCount-1].iCount % ARRAY_BUFFER_LEN ) ){
|
||||||
|
//End of buffer. Expand.
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].x = (DOUBLE *)MemRealloc(gpSpreadPattern[giSpreadPatternCount-1].x, CALC_NEW_ARRAY_SIZE(gpSpreadPattern[giSpreadPatternCount-1].iCount) );
|
||||||
|
Assert( gpSpreadPattern[giSpreadPatternCount-1].x );
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].y = (DOUBLE *)MemRealloc(gpSpreadPattern[giSpreadPatternCount-1].y, CALC_NEW_ARRAY_SIZE(gpSpreadPattern[giSpreadPatternCount-1].iCount) );
|
||||||
|
Assert( gpSpreadPattern[giSpreadPatternCount-1].y );
|
||||||
|
}
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].x[ gpSpreadPattern[giSpreadPatternCount-1].iCount - 1 ] = 0.0;
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].y[ gpSpreadPattern[giSpreadPatternCount-1].iCount - 1 ] = 0.0;
|
||||||
|
|
||||||
|
pData->maxReadDepth++; //we are not skipping this element
|
||||||
|
}
|
||||||
|
else if( //pData->curElement == ELEMENT &&
|
||||||
|
( stricmp(name, "x") == 0
|
||||||
|
|| stricmp(name, "y") == 0
|
||||||
|
|| stricmp(name, "xspread") == 0
|
||||||
|
|| stricmp(name, "yspread") == 0
|
||||||
|
|| stricmp(name, "Name") == 0
|
||||||
|
|| stricmp(name, "method") == 0
|
||||||
|
))
|
||||||
|
|
||||||
|
{
|
||||||
|
pData->curElement = ELEMENT_PROPERTY;
|
||||||
|
|
||||||
|
pData->maxReadDepth++; //we are not skipping this element
|
||||||
|
}
|
||||||
|
|
||||||
|
pData->szCharData[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
pData->currentDepth++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void XMLCALL
|
||||||
|
spreadpatternCharacterDataHandle(void *userData, const XML_Char *str, int len)
|
||||||
|
{
|
||||||
|
spreadpatternParseData * pData = (spreadpatternParseData *)userData;
|
||||||
|
|
||||||
|
if( (pData->currentDepth <= pData->maxReadDepth) &&
|
||||||
|
(strlen(pData->szCharData) < MAX_CHAR_DATA_LENGTH)
|
||||||
|
&& pData->curElement == ELEMENT_PROPERTY
|
||||||
|
){
|
||||||
|
strncat(pData->szCharData,str,__min((unsigned int)len,MAX_CHAR_DATA_LENGTH-strlen(pData->szCharData)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void XMLCALL
|
||||||
|
spreadpatternEndElementHandle(void *userData, const XML_Char *name)
|
||||||
|
{
|
||||||
|
DOUBLE d;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
spreadpatternParseData * pData = (spreadpatternParseData *)userData;
|
||||||
|
|
||||||
|
if(pData->currentDepth <= pData->maxReadDepth) //we're at the end of an element that we've been reading
|
||||||
|
{
|
||||||
|
if(stricmp(name, "SPREADPATTERNLIST") == 0)
|
||||||
|
{
|
||||||
|
pData->curElement = ELEMENT_NONE;
|
||||||
|
}
|
||||||
|
else if(stricmp(name, "SPREADPATTERN") == 0)
|
||||||
|
{
|
||||||
|
pData->curElement = ELEMENT_LIST;
|
||||||
|
}
|
||||||
|
else if(stricmp(name, "p") == 0)
|
||||||
|
{
|
||||||
|
//I would prefer the format to be:
|
||||||
|
// <SPREADPATTERN><p x="0.1" y="0.1"/><p x="0.2" y="0.2"/></SPREADPATTERN>
|
||||||
|
//but it's:
|
||||||
|
// <SPREADPATTERN><p/><x>0.1</x><y>0.1</y><p/><x>0.2</x><y>0.2</y></SPREADPATTERN>
|
||||||
|
//because the XML editor will probably have problems with using attributes instead of tags.
|
||||||
|
pData->curElement = ELEMENT_LIST;
|
||||||
|
}
|
||||||
|
else if(stricmp(name, "x") == 0)
|
||||||
|
{
|
||||||
|
pData->curElement = ELEMENT;
|
||||||
|
d = atof(pData->szCharData);
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].x[ gpSpreadPattern[giSpreadPatternCount-1].iCount-1 ] = d;
|
||||||
|
}
|
||||||
|
else if(stricmp(name, "y") == 0)
|
||||||
|
{
|
||||||
|
pData->curElement = ELEMENT;
|
||||||
|
d = atof(pData->szCharData);
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].y[ gpSpreadPattern[giSpreadPatternCount-1].iCount-1 ] = d;
|
||||||
|
}
|
||||||
|
else if(stricmp(name, "xspread") == 0)
|
||||||
|
{
|
||||||
|
pData->curElement = ELEMENT;
|
||||||
|
d = fabs( atof(pData->szCharData) );
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].xspread = d;
|
||||||
|
}
|
||||||
|
else if(stricmp(name, "yspread") == 0)
|
||||||
|
{
|
||||||
|
pData->curElement = ELEMENT;
|
||||||
|
d = fabs( atof(pData->szCharData) );
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].yspread = d;
|
||||||
|
}
|
||||||
|
else if(stricmp(name, "Name") == 0)
|
||||||
|
{
|
||||||
|
pData->curElement = ELEMENT;
|
||||||
|
//Trim whitespace.
|
||||||
|
while( pData->szCharData[0] == ' ' ) memmove( pData->szCharData , &pData->szCharData[1] , strlen(&pData->szCharData[1]) );
|
||||||
|
while( strlen(pData->szCharData)>0 && pData->szCharData[ strlen(pData->szCharData)-1 ] == ' ' ) pData->szCharData[ strlen(pData->szCharData)-1 ]=0;
|
||||||
|
//Clean and copy.
|
||||||
|
memset(gpSpreadPattern[giSpreadPatternCount-1].Name,0,SPREADPATTERN_NAME_SIZE);
|
||||||
|
strncpy(gpSpreadPattern[giSpreadPatternCount-1].Name,pData->szCharData,SPREADPATTERN_NAME_SIZE-1);
|
||||||
|
}
|
||||||
|
else if(stricmp(name, "method") == 0)
|
||||||
|
{
|
||||||
|
pData->curElement = ELEMENT;
|
||||||
|
//Trim whitespace.
|
||||||
|
while( pData->szCharData[0] == ' ' ) memmove( pData->szCharData , &pData->szCharData[1] , strlen(&pData->szCharData[1]) );
|
||||||
|
while( strlen(pData->szCharData)>0 && pData->szCharData[ strlen(pData->szCharData)-1 ] == ' ' ) pData->szCharData[ strlen(pData->szCharData)-1 ]=0;
|
||||||
|
//Find it.
|
||||||
|
for(i=0;i<SPREADPATTERNMETHOD_COUNT;++i){
|
||||||
|
if(stricmp(pData->szCharData, gSpreadPatternMethodNames[i] ) == 0){
|
||||||
|
gpSpreadPattern[giSpreadPatternCount-1].method = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pData->maxReadDepth--;
|
||||||
|
}
|
||||||
|
|
||||||
|
pData->currentDepth--;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BOOLEAN ReadInSpreadPatterns(STR fileName)
|
||||||
|
{
|
||||||
|
HWFILE hFile;
|
||||||
|
UINT32 uiBytesRead;
|
||||||
|
UINT32 uiFSize;
|
||||||
|
CHAR8 * lpcBuffer;
|
||||||
|
XML_Parser parser = XML_ParserCreate(NULL);
|
||||||
|
|
||||||
|
spreadpatternParseData pData;
|
||||||
|
|
||||||
|
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "Loading SpreadPatterns.xml" );
|
||||||
|
|
||||||
|
// Open spread pattern file
|
||||||
|
hFile = FileOpen( fileName, FILE_ACCESS_READ, FALSE );
|
||||||
|
if ( !hFile )
|
||||||
|
return( FALSE );
|
||||||
|
|
||||||
|
uiFSize = FileGetSize(hFile);
|
||||||
|
lpcBuffer = (CHAR8 *) MemAlloc(uiFSize+1);
|
||||||
|
|
||||||
|
//Read in block
|
||||||
|
if ( !FileRead( hFile, lpcBuffer, uiFSize, &uiBytesRead ) )
|
||||||
|
{
|
||||||
|
MemFree(lpcBuffer);
|
||||||
|
return( FALSE );
|
||||||
|
}
|
||||||
|
|
||||||
|
lpcBuffer[uiFSize] = 0; //add a null terminator
|
||||||
|
|
||||||
|
FileClose( hFile );
|
||||||
|
|
||||||
|
|
||||||
|
XML_SetElementHandler(parser, spreadpatternStartElementHandle, spreadpatternEndElementHandle);
|
||||||
|
XML_SetCharacterDataHandler(parser, spreadpatternCharacterDataHandle);
|
||||||
|
|
||||||
|
|
||||||
|
memset(&pData,0,sizeof(pData));
|
||||||
|
//pData.maxArraySize = MAXITEMS;
|
||||||
|
|
||||||
|
XML_SetUserData(parser, &pData);
|
||||||
|
|
||||||
|
|
||||||
|
if(!XML_Parse(parser, lpcBuffer, uiFSize, TRUE))
|
||||||
|
{
|
||||||
|
CHAR8 errorBuf[511];
|
||||||
|
|
||||||
|
sprintf(errorBuf, "XML Parser Error in SpreadPatterns.xml: %s at line %d", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser));
|
||||||
|
LiveMessage(errorBuf);
|
||||||
|
|
||||||
|
MemFree(lpcBuffer);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemFree(lpcBuffer);
|
||||||
|
|
||||||
|
|
||||||
|
XML_ParserFree(parser);
|
||||||
|
|
||||||
|
//Output for debugging
|
||||||
|
//WriteSpreadPatterns();
|
||||||
|
|
||||||
|
return( TRUE );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOLEAN WriteSpreadPatterns()
|
||||||
|
{
|
||||||
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"WriteSpreadPatterns");
|
||||||
|
HWFILE hFile;
|
||||||
|
|
||||||
|
//Debug code; make sure that what we got from the file is the same as what's there
|
||||||
|
// Open a new file
|
||||||
|
hFile = FileOpen( "TableData\\SpreadPatterns_out.xml", FILE_ACCESS_WRITE | FILE_CREATE_ALWAYS, FALSE );
|
||||||
|
if ( !hFile ) return( FALSE );
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
UINT32 i,j;
|
||||||
|
|
||||||
|
FilePrintf(hFile,"<SPREADPATTERNLIST>\r\n");
|
||||||
|
FilePrintf(hFile,"<!-- giSpreadPatternCount = %d -->\r\n", giSpreadPatternCount );
|
||||||
|
for(i = 0 ; i < giSpreadPatternCount; i++)
|
||||||
|
{
|
||||||
|
FilePrintf(hFile,"\t<SPREADPATTERN>\r\n");
|
||||||
|
FilePrintf(hFile,"\t<!-- index = %d -->\r\n",i);
|
||||||
|
FilePrintf(hFile,"\t<Name>%s</Name>\r\n",gpSpreadPattern[i].Name);
|
||||||
|
FilePrintf(hFile,"\t<method>%s</method> <!-- %d -->\r\n",gSpreadPatternMethodNames[gpSpreadPattern[i].method], gpSpreadPattern[i].method);
|
||||||
|
FilePrintf(hFile,"\t<xspread>%2.4f</xspread>",gpSpreadPattern[i].xspread);
|
||||||
|
FilePrintf(hFile," <yspread>%2.4f</yspread>\r\n",gpSpreadPattern[i].yspread);
|
||||||
|
FilePrintf(hFile,"\t<!-- p iCount = %d -->\r\n",gpSpreadPattern[i].iCount);
|
||||||
|
for(j=0;j<gpSpreadPattern[i].iCount;j++)
|
||||||
|
{
|
||||||
|
FilePrintf(hFile,"\t\t<p>");
|
||||||
|
FilePrintf(hFile,"<x>%2.4f</x>",gpSpreadPattern[i].x[j]);
|
||||||
|
FilePrintf(hFile,"<y>%2.4f</y>",gpSpreadPattern[i].y[j]);
|
||||||
|
FilePrintf(hFile,"</p>\r\n");
|
||||||
|
}
|
||||||
|
FilePrintf(hFile,"\t</SPREADPATTERN>\r\n");
|
||||||
|
}
|
||||||
|
FilePrintf(hFile,"</SPREADPATTERNLIST>\r\n");
|
||||||
|
FilePrintf(hFile,"<!--End Of File-->\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
FileClose( hFile );
|
||||||
|
|
||||||
|
return( TRUE );
|
||||||
|
}
|
||||||
|
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
//This function is used primarily by XML loaders for Items and AmmoTypes.
|
||||||
|
//Given a string will determine if it is a SpreadPattern name or literal index,
|
||||||
|
//and return an integer index of the spreadpattern in the gpSpreadPattern array.
|
||||||
|
//If the name is not found, or the index was out of range, then return 0.
|
||||||
|
int FindSpreadPatternIndex( const STR strName )
|
||||||
|
{
|
||||||
|
int i=0,n=0;
|
||||||
|
char str[SPREADPATTERN_NAME_SIZE]={};
|
||||||
|
|
||||||
|
//Copy search name sans leading and trailing space.
|
||||||
|
strncpy(str,strName,SPREADPATTERN_NAME_SIZE-1);
|
||||||
|
str[SPREADPATTERN_NAME_SIZE-1]=0;
|
||||||
|
while( str[0] == ' ' ) memmove( str , &str[1] , strlen(&str[1]) );
|
||||||
|
while( strlen(str)>0 && str[ strlen(str)-1 ] == ' ' ) str[ strlen(str)-1 ]=0;
|
||||||
|
|
||||||
|
//Named pattern or raw index?
|
||||||
|
for(i=0;i<strlen(str);i++) if( !isdigit(str[i]) ) break;
|
||||||
|
|
||||||
|
if(i<strlen(str))
|
||||||
|
{
|
||||||
|
//Holds named SpreadPattern. Search.
|
||||||
|
for(i=0;i<giSpreadPatternCount;i++)
|
||||||
|
{
|
||||||
|
if( !stricmp(str, gpSpreadPattern[i].Name) )
|
||||||
|
{
|
||||||
|
//Match! Assign and return.
|
||||||
|
n=i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
//Holds numeric SpreadPattern.
|
||||||
|
//Validate.
|
||||||
|
n = atol(str);
|
||||||
|
if( n<0 || n>=giSpreadPatternCount )
|
||||||
|
n=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
+8
-1
@@ -213,7 +213,8 @@ itemStartElementHandle(void *userData, const XML_Char *name, const XML_Char **at
|
|||||||
strcmp(name, "StealthBonus") == 0 ||
|
strcmp(name, "StealthBonus") == 0 ||
|
||||||
strcmp(name, "SciFi") == 0 ||
|
strcmp(name, "SciFi") == 0 ||
|
||||||
strcmp(name, "NewInv") == 0 ||
|
strcmp(name, "NewInv") == 0 ||
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
stricmp(name, "spreadPattern") == 0 ||
|
||||||
strcmp(name, "fFlags") == 0 ))
|
strcmp(name, "fFlags") == 0 ))
|
||||||
{
|
{
|
||||||
pData->curElement = ELEMENT_PROPERTY;
|
pData->curElement = ELEMENT_PROPERTY;
|
||||||
@@ -1026,6 +1027,12 @@ itemEndElementHandle(void *userData, const XML_Char *name)
|
|||||||
pData->curElement = ELEMENT;
|
pData->curElement = ELEMENT;
|
||||||
pData->curItem.bestlaserrange = (INT16) atol(pData->szCharData);
|
pData->curItem.bestlaserrange = (INT16) atol(pData->szCharData);
|
||||||
}
|
}
|
||||||
|
//zilpin: pellet spread patterns externalized in XML
|
||||||
|
else if(stricmp(name, "spreadPattern") == 0)
|
||||||
|
{
|
||||||
|
pData->curElement = ELEMENT;
|
||||||
|
pData->curItem.spreadPattern = FindSpreadPatternIndex( pData->szCharData );
|
||||||
|
}
|
||||||
|
|
||||||
pData->maxReadDepth--;
|
pData->maxReadDepth--;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user