mirror of
https://github.com/1dot13/source.git
synced 2026-08-05 14:00:23 +02:00
New Feature: Traps made out of guns and a piece of tripwire (by Flugente)
- guns can now be attached to tripwire and then be used as tripwire-activatd traps o Attach any firearm (any gun, no RPGs, Mortars or GLs) to a piece of tripwire. You will need the newest xmls for that. o You can plant the tripwire as you usually do. o Once activated, the gun that is attached to the wire will fire once. It will fire in the direction that you planted the bomb, just like claymore mines do (the direction that the merc planting the wire was looking). o The bullet fired has the range and damage it would have if fired from the gun. o The gun correctly heats, jams, degrades and whatever else guns do when fired. If the gun is jammed, not pumped (pumpguns) or does not have ammo, it will not fire. o After firing, the gun lies on the floor, just where the trp was planted. o This can be applied to any tripwire. You can ave multiple of these traps. Just put them into your normal trap setup however you like. o At the moment, only single shots are possible, even for guns that do not have a single shot mode (MG3 etc.) o Although I did not experience any problems with attachments, it might be a good idea to take any attachments off a gun if you attach it to tripwire. o When the gun is attached, it is possible to attach gun attachments (lasers, scopes etc.) to the explosive. Do not attach any attachments to the explosive. They will be lost if the wire is activated. o see: http://www.bears-pit.com/board/ubbthreads.php/topics/305634.html#Post305634 -> To support this feature, some (already existing) xml-tags have been added to the weapons in Data-1.13\TableData\Items.xml (<nasAttachmentClass>, <AttachmentPoint>, <AttachToPointAPCost>, <Attachment>) git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5331 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
@@ -49,6 +49,7 @@
|
||||
// HEADROCK HAM 3.6: This must be included, for testing whether Bloodcats and Enemies can see one another.
|
||||
#include "Campaign Types.h"
|
||||
#include "soldier tile.h" // added by Flugente
|
||||
#include "Sound Control.h" // added by Flugente
|
||||
|
||||
//forward declarations of common classes to eliminate includes
|
||||
class OBJECTTYPE;
|
||||
@@ -5249,6 +5250,579 @@ INT8 FireFragmentGivenTarget( SOLDIERTYPE * pThrower, FLOAT dStartX, FLOAT dStar
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
// Flugente: fire a shot from a gun that has no user (used for traps with attached guns)
|
||||
INT8 FireBulletGivenTargetTrapOnly( SOLDIERTYPE* pThrower, OBJECTTYPE* pObj, INT32 gridno, FLOAT dStartZ, FLOAT dEndX, FLOAT dEndY, FLOAT dEndZ, INT16 sHitBy )
|
||||
{
|
||||
if ( !pObj )
|
||||
return 0;
|
||||
|
||||
if ( !pThrower )
|
||||
return 0;
|
||||
|
||||
FLOAT dStartX = 0;
|
||||
FLOAT dStartY = 0;
|
||||
|
||||
FLOAT d2DDistance=0;
|
||||
FLOAT dDeltaX=0;
|
||||
FLOAT dDeltaY=0;
|
||||
FLOAT dDeltaZ=0;
|
||||
|
||||
DOUBLE ddOrigHorizAngle=0;
|
||||
DOUBLE ddOrigVerticAngle=0;
|
||||
DOUBLE ddHorizAngle=0;
|
||||
DOUBLE ddVerticAngle=0;
|
||||
DOUBLE ddAdjustedHorizAngle=0;
|
||||
DOUBLE ddAdjustedVerticAngle=0;
|
||||
DOUBLE ddDummyHorizAngle=0;
|
||||
DOUBLE ddDummyVerticAngle=0;
|
||||
|
||||
BULLET* pBullet=NULL;
|
||||
INT32 iBullet=0;
|
||||
|
||||
INT32 iDistance=0;
|
||||
|
||||
UINT8 ubLoop=0;
|
||||
UINT8 ubShots=0;
|
||||
UINT8 ubImpact=0;
|
||||
INT8 bCTGT;
|
||||
UINT8 ubSpreadIndex = 0;
|
||||
UINT16 usBulletFlags = 0;
|
||||
int n=0;
|
||||
|
||||
BOOLEAN fBuckshot = FALSE;
|
||||
if ( AmmoTypes[(*pObj)[0]->data.gun.ubGunAmmoType].numberOfBullets > 1 )
|
||||
fBuckshot = TRUE;
|
||||
|
||||
dStartX = (FLOAT) CenterX( gridno );
|
||||
dStartY = (FLOAT) CenterY( gridno );
|
||||
|
||||
dDeltaX = dEndX - dStartX;
|
||||
dDeltaY = dEndY - dStartY;
|
||||
dDeltaZ = dEndZ - dStartZ;
|
||||
|
||||
//lal bugfix
|
||||
if( dDeltaZ > 0 )
|
||||
d2DDistance = Distance3D( dDeltaX, dDeltaY, dDeltaZ );
|
||||
else
|
||||
d2DDistance = Distance2D( dDeltaX, dDeltaY );
|
||||
|
||||
iDistance = (INT32) d2DDistance;
|
||||
|
||||
if ( d2DDistance != iDistance )
|
||||
{
|
||||
iDistance += 1;
|
||||
d2DDistance = (FLOAT) ( iDistance);
|
||||
}
|
||||
|
||||
ddOrigHorizAngle = atan2( dDeltaY, dDeltaX );
|
||||
ddOrigVerticAngle = atan2( dDeltaZ, (d2DDistance * 2.56f) );
|
||||
ddAdjustedHorizAngle = ddOrigHorizAngle;
|
||||
ddAdjustedVerticAngle = ddOrigVerticAngle;
|
||||
|
||||
ubShots = 1;
|
||||
fTracer = FALSE;
|
||||
|
||||
// Check if we have spit as a weapon!
|
||||
if ( Weapon[ pObj->usItem ].ubWeaponClass == MONSTERCLASS )
|
||||
{
|
||||
usBulletFlags |= BULLET_FLAG_CREATURE_SPIT;
|
||||
}
|
||||
else if ( Item[ pObj->usItem ].usItemClass == IC_THROWING_KNIFE )
|
||||
{
|
||||
usBulletFlags |= BULLET_FLAG_KNIFE;
|
||||
}
|
||||
else if ( Item[pObj->usItem].rocketlauncher )
|
||||
{
|
||||
usBulletFlags |= BULLET_FLAG_MISSILE;
|
||||
}
|
||||
else if ( Item[pObj->usItem].cannon )
|
||||
{
|
||||
usBulletFlags |= BULLET_FLAG_TANK_CANNON;
|
||||
}
|
||||
else if ( Item[pObj->usItem].rocketrifle )
|
||||
{
|
||||
usBulletFlags |= BULLET_FLAG_SMALL_MISSILE;
|
||||
}
|
||||
else if ( pObj->usItem == FLAMETHROWER )
|
||||
{
|
||||
usBulletFlags |= BULLET_FLAG_FLAME;
|
||||
ubSpreadIndex = 2;
|
||||
}
|
||||
|
||||
// no option to use fire bursts or autofire yet
|
||||
/*// HEADROCK HAM B2.5: Set tracer effect on/off for individual bullets in a Tracer Magazine, as part of the
|
||||
// New Tracer System.
|
||||
else if (gGameExternalOptions.ubRealisticTracers > 0 && gGameExternalOptions.ubNumBulletsPerTracer > 0 && (pFirer->bDoAutofire > 0 || pFirer->bDoBurst > 0)
|
||||
&& AmmoTypes[ (*pObjAttHand)[0]->data.gun.ubGunAmmoType ].tracerEffect )
|
||||
{
|
||||
UINT16 iBulletsLeft, iBulletsPerTracer;
|
||||
iBulletsPerTracer = gGameExternalOptions.ubNumBulletsPerTracer;
|
||||
iBulletsLeft = (*pObjAttHand)[0]->data.gun.ubGunShotsLeft + pFirer->bDoBurst;
|
||||
|
||||
if ((((iBulletsLeft - (pFirer->bDoBurst - 1)) / iBulletsPerTracer) - ((iBulletsLeft - pFirer->bDoBurst) / iBulletsPerTracer)) == 1)
|
||||
{
|
||||
fTracer = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
fTracer = FALSE;
|
||||
}
|
||||
}*/
|
||||
else if ( AmmoTypes[ (*pObj)[0]->data.gun.ubGunAmmoType ].tracerEffect && gGameSettings.fOptions[ TOPTION_TRACERS_FOR_SINGLE_FIRE ] )
|
||||
{
|
||||
//usBulletFlags |= BULLET_FLAG_TRACER;
|
||||
fTracer = TRUE;
|
||||
}
|
||||
|
||||
ubImpact =(UINT8) GetDamage( pObj );
|
||||
|
||||
// we have to adjust the damage...
|
||||
//ubImpact = min(255, 4 * ubImpact );
|
||||
|
||||
//zilpin: Begin new code block for spread patterns, number of projectiles, impact adjustment, etc.
|
||||
{
|
||||
ObjectData *weapon = &((*pObj)[0]->data);
|
||||
ubShots = AmmoTypes[ weapon->gun.ubGunAmmoType].numberOfBullets;
|
||||
ubSpreadIndex = GetSpreadPattern( pObj );
|
||||
if( ubShots>1 )
|
||||
{
|
||||
fBuckshot = true;
|
||||
usBulletFlags |= BULLET_FLAG_BUCKSHOT;
|
||||
ubImpact = (UINT8) (ubImpact * AmmoTypes[weapon->gun.ubGunAmmoType].multipleBulletDamageMultiplier / max(1,AmmoTypes[weapon->gun.ubGunAmmoType].multipleBulletDamageDivisor) );
|
||||
}
|
||||
weapon=NULL;
|
||||
}
|
||||
//zilpin: End of new code block.
|
||||
|
||||
// GET BULLET
|
||||
for (ubLoop = 0; ubLoop < ubShots; ubLoop++)
|
||||
{
|
||||
iBullet = CreateBullet( NOBODY, FALSE, usBulletFlags, pObj->usItem );
|
||||
if (iBullet == -1)
|
||||
{
|
||||
//DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Failed to create bullet") );
|
||||
|
||||
return( FALSE );
|
||||
}
|
||||
pBullet = GetBulletPtr( iBullet );
|
||||
|
||||
// HEADROCK HAM 4: The HitBy value now holds the ratio between the Distance Aperture and the Final Aperture.
|
||||
// Basically, this represents by how much our shooter has managed to make the shot more accurate than it would
|
||||
// be without any extra aiming or optical equipment. HitBy can now only be positive, and equals 100 when the
|
||||
// shot is perfectly on-target, or 0 when it is as inaccurate as the shooting system can possibly allow.
|
||||
// In this sense, sApertureRatio is the closest "relative" that the NCTH system has to the old system. it tells
|
||||
// us, on a scale of 0-100, how accurate we are compared to "Best" and "Worst" accuracy.
|
||||
// Note also that references to this value have been changed accordingly. Most imporantly, functions that alter
|
||||
// the path of the bullet, or determine whether collisions are more likely thanks to low HitBy, have been
|
||||
// altered to _not use this value at all_.
|
||||
// At the moment, HitBy is used only for determining extra damage (0-50% extra), and for determining
|
||||
// how much extra experience is gained by the shooter if the target is actually hit by the bullet.
|
||||
pBullet->sHitBy = sHitBy;
|
||||
|
||||
// HEADROCK HAM 4: TODO: Determine whether this value is required anymore. Why ever let bullets
|
||||
// pass through the roof at all??
|
||||
if (dStartZ < WALL_HEIGHT_UNITS)
|
||||
{
|
||||
if (dEndZ > WALL_HEIGHT_UNITS)
|
||||
{
|
||||
pBullet->fCheckForRoof = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pBullet->fCheckForRoof = FALSE;
|
||||
}
|
||||
}
|
||||
else // dStartZ >= WALL_HEIGHT_UNITS; presumably >
|
||||
{
|
||||
if (dEndZ < WALL_HEIGHT_UNITS)
|
||||
{
|
||||
pBullet->fCheckForRoof = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pBullet->fCheckForRoof = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Flugente: we have to set this, otherwise the gun that fired this shot wont be found in WeaponHit()
|
||||
pBullet->fFragment = true;
|
||||
|
||||
//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 )
|
||||
{
|
||||
// CHRISL: If we don't set the ddHorizAngle, at the very least, shooting by corners is impossible. Unfortunately, I don't know what other
|
||||
// impacts these two lines will have. Headrock didn't include them when he originally wrote NCTH but they are in a similar location in the
|
||||
// OCTH code. Hopefully no issues will result from this change.
|
||||
ddHorizAngle = ddOrigHorizAngle;
|
||||
ddVerticAngle = ddOrigVerticAngle;
|
||||
// HEADROCK HAM 4: Firing increments no longer required here (NCTH)
|
||||
// calculate by hand (well, without angles) to match LOS
|
||||
pBullet->qIncrX = FloatToFixed( dDeltaX / (FLOAT)iDistance );
|
||||
pBullet->qIncrY = FloatToFixed( dDeltaY / (FLOAT)iDistance );
|
||||
pBullet->qIncrZ = FloatToFixed( dDeltaZ / (FLOAT)iDistance );
|
||||
}
|
||||
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
|
||||
//Note: this no longer needs to be done since the CalculateFiringIncrementsSimple is being used below.
|
||||
//pBullet->sHitBy = 0;
|
||||
|
||||
//Also prevent out-of-bounds overflows.
|
||||
//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;
|
||||
|
||||
//Use spread patterns loaded from XML.
|
||||
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( TRUE )
|
||||
{
|
||||
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,
|
||||
NOBODY, 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 );
|
||||
}
|
||||
|
||||
|
||||
pBullet->ddHorizAngle = ddHorizAngle;
|
||||
|
||||
/*if (ubLoop == 0 && pFirer->bDoBurst < 2)
|
||||
{
|
||||
pBullet->fAimed = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// buckshot pellets after the first can hit friendlies even at close range
|
||||
pBullet->fAimed = FALSE;
|
||||
}*/
|
||||
|
||||
// buckshot pellets after the first can hit friendlies even at close range
|
||||
pBullet->fAimed = FALSE;
|
||||
|
||||
if ( pBullet->usFlags & BULLET_FLAG_KNIFE )
|
||||
{
|
||||
pBullet->ubItemStatus = (*pObj)[0]->data.objectStatus;
|
||||
}
|
||||
|
||||
// apply increments for first move
|
||||
|
||||
//zilpin: pellet spread patterns externalized in XML
|
||||
//This is a bugfix for strange behavior when firing, such as the projectile hitting walls behind a merc.
|
||||
//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
|
||||
// because otherwise the increments are not right for the calculations!
|
||||
if ( pBullet->sHitBy >= 0 )
|
||||
{
|
||||
pBullet->qCurrX += ( FloatToFixed( dDeltaX ) - pBullet->qIncrX * iDistance ) / 2;
|
||||
pBullet->qCurrY += ( FloatToFixed( dDeltaY ) - pBullet->qIncrY * iDistance ) / 2;
|
||||
pBullet->qCurrZ += ( FloatToFixed( dDeltaZ ) - pBullet->qIncrZ * iDistance ) / 2;
|
||||
}
|
||||
*/
|
||||
|
||||
pBullet->iImpact = ubImpact;
|
||||
|
||||
pBullet->iRange = GunRange( pObj, NULL ); // SANDRO - added argument
|
||||
// HEADROCK HAM 5.1: Define original point.
|
||||
pBullet->sOrigGridNo = ((INT32)dStartX) / CELL_X_SIZE + ((INT32)dStartY) / CELL_Y_SIZE * WORLD_COLS;
|
||||
pBullet->sTargetGridNo = ((INT32)dEndX) / CELL_X_SIZE + ((INT32)dEndY) / CELL_Y_SIZE * WORLD_COLS;
|
||||
|
||||
//pBullet->bStartCubesAboveLevelZ = (INT8) CONVERT_HEIGHTUNITS_TO_INDEX( (INT32)dStartZ - CONVERT_PIXELS_TO_HEIGHTUNITS( gpWorldLevelData[ gridno ].sHeight ) );
|
||||
//pBullet->bEndCubesAboveLevelZ = (INT8) CONVERT_HEIGHTUNITS_TO_INDEX( (INT32)dEndZ - CONVERT_PIXELS_TO_HEIGHTUNITS( gpWorldLevelData[ pBullet->sTargetGridNo ].sHeight ) );
|
||||
|
||||
pBullet->bStartCubesAboveLevelZ = (INT8) CONVERT_HEIGHTUNITS_TO_INDEX( (INT32)dStartZ );
|
||||
pBullet->bEndCubesAboveLevelZ = (INT8) CONVERT_HEIGHTUNITS_TO_INDEX( (INT32)dEndZ );
|
||||
|
||||
// HEADROCK HAM BETA2.5: New method for signifying whether a bullet is a tracer or not, using an individual
|
||||
// bullet structure flag. Hehehehe, I think this is kind of reverting to old code, isn't it?
|
||||
/*if (gGameExternalOptions.ubRealisticTracers > 0 && gGameExternalOptions.ubNumBulletsPerTracer > 0 && (pFirer->bDoAutofire > 0 || pFirer->bDoBurst > 0)
|
||||
&& AmmoTypes[ (*pObj)[0]->data.gun.ubGunAmmoType ].tracerEffect )
|
||||
{
|
||||
UINT16 iBulletsLeft, iBulletsPerTracer;
|
||||
iBulletsPerTracer = gGameExternalOptions.ubNumBulletsPerTracer;
|
||||
iBulletsLeft = (*pObj)[0]->data.gun.ubGunShotsLeft + pFirer->bDoBurst;
|
||||
|
||||
// Is this specific bullet a tracer? - based on how many tracers there are per regular bullets in
|
||||
// a tracer magazine (INI-settable).
|
||||
if ((((iBulletsLeft - (pFirer->bDoBurst - 1)) / iBulletsPerTracer) - ((iBulletsLeft - pFirer->bDoBurst) / iBulletsPerTracer)) == 1)
|
||||
{
|
||||
pBullet->fTracer = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pBullet->fTracer = FALSE;
|
||||
}
|
||||
}*/
|
||||
|
||||
///////////////////////// SOUND ////////////////////////////
|
||||
//PLAY SOUND
|
||||
// ( For throwing knife.. it's earlier in the animation
|
||||
if ( Weapon[ pObj->usItem ].sSound != 0 && Item[ pObj->usItem ].usItemClass != IC_THROWING_KNIFE )
|
||||
{
|
||||
// Switch on silencer...
|
||||
UINT16 noisefactor = GetPercentNoiseVolume( pObj );
|
||||
if( noisefactor < MAX_PERCENT_NOISE_VOLUME_FOR_SILENCED_SOUND || Weapon[ pObj->usItem ].ubAttackVolume <= 10 )
|
||||
{
|
||||
INT32 uiSound;
|
||||
|
||||
uiSound = Weapon [ pObj->usItem ].silencedSound;
|
||||
|
||||
PlayJA2Sample( uiSound, RATE_11025, SoundVolume( HIGHVOLUME, gridno ), 1, SoundDir( gridno ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
INT8 volume = HIGHVOLUME;
|
||||
if ( noisefactor < 100 ) volume = (volume * noisefactor) / 100;
|
||||
PlayJA2Sample( Weapon[ pObj->usItem ].sSound, RATE_11025, SoundVolume( volume, gridno ), 1, SoundDir( gridno ) );
|
||||
}
|
||||
}
|
||||
///////////////////////// SOUND ////////////////////////////
|
||||
|
||||
// deduct ammo
|
||||
(*pObj)[0]->data.gun.ubGunShotsLeft = max(0, (*pObj)[0]->data.gun.ubGunShotsLeft - 1);
|
||||
|
||||
//gTacticalStatus.ubAttackBusyCount++;
|
||||
|
||||
if(is_client)send_bullet( pBullet, pObj->usItem );
|
||||
FireBullet( pThrower, pBullet, FALSE );
|
||||
|
||||
///////////////////////// SOUND ////////////////////////////
|
||||
UINT8 ubVolume = Weapon[ pObj->usItem ].ubAttackVolume;
|
||||
|
||||
// Snap: get cumulative noise reduction from the weapon and its attachments
|
||||
UINT16 noisefactor = GetPercentNoiseVolume( pObj );
|
||||
if ( ubVolume * noisefactor > 25000 )
|
||||
{ // Snap: hack this to prevent overflow (damn miserly programmers!)
|
||||
ubVolume = 250;
|
||||
}
|
||||
else
|
||||
{
|
||||
ubVolume = __max( 1, ( ubVolume * GetPercentNoiseVolume( pObj ) ) / 100 );
|
||||
}
|
||||
|
||||
MakeNoise( NOBODY, gridno, 0, pThrower->bOverTerrainType, ubVolume, NOISE_GUNFIRE );
|
||||
///////////////////////// SOUND ////////////////////////////
|
||||
|
||||
///////////////////////// OVERHEATING AND STATUS REDUCTION ////////////////////////////
|
||||
INT16 iOverheatReliabilityMalus = 0;
|
||||
// Flugente: Increase Weapon Temperature
|
||||
if ( gGameOptions.fWeaponOverheating )
|
||||
{
|
||||
FLOAT overheatjampercentage = GetGunOverheatDamagePercentage( pObj ); // ... how much above the gun's usOverheatingDamageThreshold are we? ...
|
||||
|
||||
if ( overheatjampercentage > 1.0 )
|
||||
iOverheatReliabilityMalus = (INT16)floor(overheatjampercentage*overheatjampercentage);
|
||||
|
||||
GunIncreaseHeat( pObj );
|
||||
}
|
||||
|
||||
// Flugente : Added a malus to reliability for overheated guns
|
||||
// HEADROCK HAM 5: Variable NCTH base change
|
||||
UINT32 uiDepreciateTest = 0;
|
||||
if ( UsingNewCTHSystem() == true)
|
||||
{
|
||||
UINT16 usBaseChance = gGameCTHConstants.BASIC_RELIABILITY_ODDS;
|
||||
FLOAT dReliabilityRatio = 3.0f * ((FLOAT)usBaseChance / (FLOAT)BASIC_DEPRECIATE_CHANCE); // Compare original odds to new odds.
|
||||
uiDepreciateTest = usBaseChance + (INT16)( dReliabilityRatio * GetReliability( pObj ) - iOverheatReliabilityMalus);
|
||||
uiDepreciateTest = max(0, uiDepreciateTest);
|
||||
}
|
||||
else
|
||||
{
|
||||
uiDepreciateTest = max( BASIC_DEPRECIATE_CHANCE + 3 * GetReliability( pObj ) - iOverheatReliabilityMalus, 0);
|
||||
}
|
||||
if ( !PreRandom( uiDepreciateTest ) && ( (*pObj)[0]->data.objectStatus > 1) )
|
||||
{
|
||||
(*pObj)[0]->data.objectStatus--;
|
||||
}
|
||||
///////////////////////// OVERHEATING AND STATUS REDUCTION ////////////////////////////
|
||||
|
||||
///////////////////////// JAMMING ////////////////////////////
|
||||
// Algorithm for jamming
|
||||
int maxJamChance = 50; // Externalize this?
|
||||
int reliability = GetReliability( pObj );
|
||||
int condition = (*pObj)[0]->data.gun.bGunStatus;
|
||||
int invertedBaseJamChance = condition + (reliability * 2) - gGameExternalOptions.ubWeaponReliabilityReductionPerRainIntensity * 1;
|
||||
|
||||
// Flugente FTW 1: If overheating is allowed, a gun will be prone to more overheating if its temperature is high
|
||||
if ( gGameOptions.fWeaponOverheating )
|
||||
{
|
||||
FLOAT overheatjampercentage = GetGunOverheatJamPercentage( pObj ); // how much above the gun's usOverheatingJamThreshold are we? ...
|
||||
|
||||
int overheatjamfactor = (int)(100* overheatjampercentage); // We need an integer value and rough percentages
|
||||
|
||||
overheatjamfactor = max(0, overheatjamfactor - 100); // If we haven't reached the OverheatJamThreshold, no increased chance of jamming because of overheating
|
||||
|
||||
invertedBaseJamChance -= overheatjamfactor; // lower invertedBaseJamChance (thereby increasing jamChance later on)
|
||||
}
|
||||
|
||||
if (invertedBaseJamChance < 0)
|
||||
invertedBaseJamChance = 0;
|
||||
else if (invertedBaseJamChance > 100)
|
||||
invertedBaseJamChance = 100;
|
||||
int jamChance = 100 - (int)sqrt((double)invertedBaseJamChance * (75.0 + (double)invertedBaseJamChance / 2.0));
|
||||
if (jamChance < 0)
|
||||
jamChance = 0;
|
||||
else if (jamChance > maxJamChance - reliability)
|
||||
jamChance = maxJamChance - reliability;
|
||||
|
||||
|
||||
if ((INT32) PreRandom( 100 ) < jamChance )
|
||||
{
|
||||
// jam! negate the gun ammo status.
|
||||
(*pObj)[0]->data.gun.bGunAmmoStatus *= -1;
|
||||
}
|
||||
///////////////////////// JAMMING ////////////////////////////
|
||||
|
||||
//<SB> manual recharge
|
||||
if (Weapon[Item[pObj->usItem].ubClassIndex].APsToReloadManually > 0)
|
||||
(*pObj)[0]->data.gun.ubGunState &= ~GS_CARTRIDGE_IN_CHAMBER;
|
||||
//<SB>
|
||||
}
|
||||
|
||||
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
INT8 ChanceToGetThrough( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOAT dEndZ )
|
||||
{
|
||||
if ( Item[pFirer->usAttackingWeapon].usItemClass == IC_GUN || Item[ pFirer->usAttackingWeapon ].usItemClass == IC_THROWING_KNIFE || Item[pFirer->usAttackingWeapon].rocketlauncher )
|
||||
|
||||
Reference in New Issue
Block a user