logical: Make some projectiles animated in-flight.

git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/trunk@9656 4a71c877-e1ca-e34f-864e-861f7616d084
master
Cyp 2010-02-06 17:08:26 +00:00 committed by Git SVN Gateway
parent a99ef4ba19
commit 92225b6b18
8 changed files with 142 additions and 35 deletions

View File

@ -104,7 +104,7 @@ UDWORD getModularScaledRealTime(UDWORD timePeriod, UDWORD requiredRange)
} }
/* Call this each loop to update the game timer */ /* Call this each loop to update the game timer */
bool logicalUpdates = false; bool logicalUpdates = true;
void gameTimeUpdate() void gameTimeUpdate()
{ {
uint32_t currTime = SDL_GetTicks(); uint32_t currTime = SDL_GetTicks();
@ -134,18 +134,17 @@ void gameTimeUpdate()
} }
// Calculate the time for this frame // Calculate the time for this frame
deltaGameTime = scaledCurrTime - gameTime;
deltaGraphicsTime = scaledCurrTime - graphicsTime; deltaGraphicsTime = scaledCurrTime - graphicsTime;
// Adjust deltas. // Adjust deltas.
if (sane) if (sane)
{ {
if (deltaGameTime > GAME_UNITS_PER_TICK) if (scaledCurrTime >= gameTime)
{ {
if (deltaGameTime >= GAME_UNITS_PER_TICK*2) if (scaledCurrTime > gameTime + GAME_UNITS_PER_TICK)
{ {
// Game isn't updating fast enough... // Game isn't updating fast enough...
uint32_t slideBack = deltaGameTime - GAME_UNITS_PER_TICK*2; uint32_t slideBack = deltaGameTime - GAME_UNITS_PER_TICK;
baseTime += slideBack / modifier; // adjust the addition to base time baseTime += slideBack / modifier; // adjust the addition to base time
deltaGraphicsTime -= slideBack; deltaGraphicsTime -= slideBack;
} }
@ -159,12 +158,15 @@ void gameTimeUpdate()
} }
else else
{ {
deltaGameTime = scaledCurrTime - gameTime;
// Limit the frame time // Limit the frame time
if (deltaGraphicsTime > GTIME_MAXFRAME) if (deltaGraphicsTime > GTIME_MAXFRAME)
{ {
uint32_t slideBack = deltaGraphicsTime - GTIME_MAXFRAME; uint32_t slideBack = deltaGraphicsTime - GTIME_MAXFRAME;
baseTime += slideBack / modifier; // adjust the addition to base time baseTime += slideBack / modifier; // adjust the addition to base time
deltaGraphicsTime -= slideBack; deltaGraphicsTime -= slideBack;
deltaGameTime -= slideBack; // If !sane, then deltaGameTime == deltaGraphicsTime.
} }
} }

View File

@ -44,7 +44,7 @@
/// Changes in GAME_UNITS_PER_TICK increments. /// Changes in GAME_UNITS_PER_TICK increments.
extern UDWORD gameTime; extern UDWORD gameTime;
/// The current time in the graphical display of the game world. /// The current time in the graphical display of the game world.
/// Should be close to gameTime, up to GAME_UNITS_PER_TICK ahead. /// Should be close to gameTime, up to GAME_UNITS_PER_TICK behind.
extern UDWORD graphicsTime; extern UDWORD graphicsTime;
/** The current time in the game world - never stops. /** The current time in the game world - never stops.
* FIXME Then isn't it the real time, not the game time? Rename from gameTime2 to realTime? * FIXME Then isn't it the real time, not the game time? Rename from gameTime2 to realTime?

View File

@ -60,6 +60,7 @@ typedef struct _tilePos
For explanation of yaw/pitch/roll look for "flight dynamics" in your encyclopedia. For explanation of yaw/pitch/roll look for "flight dynamics" in your encyclopedia.
*/ */
/// Along with NEXTOBJ, the elements of the base class SIMPLE_OBJECT. FIXME If converting to C++, this can be a normal base class and a lot less ugly.
#define BASE_ELEMENTS1(pointerType) \ #define BASE_ELEMENTS1(pointerType) \
OBJECT_TYPE type; /**< The type of object */ \ OBJECT_TYPE type; /**< The type of object */ \
UDWORD id; /**< ID number of the object */ \ UDWORD id; /**< ID number of the object */ \
@ -67,7 +68,9 @@ typedef struct _tilePos
float direction; /**< Object's yaw +ve rotation around up-axis */ \ float direction; /**< Object's yaw +ve rotation around up-axis */ \
SWORD pitch; /**< Object's pitch +ve rotation around right-axis (nose up/down) */ \ SWORD pitch; /**< Object's pitch +ve rotation around right-axis (nose up/down) */ \
UBYTE player; /**< Which player the object belongs to */ \ UBYTE player; /**< Which player the object belongs to */ \
SWORD roll /**< Object's roll +ve rotation around forward-axis (left wing up/down) */ SWORD roll; /**< Object's roll +ve rotation around forward-axis (left wing up/down) */ \
SWORD prevRoll; /**< Object's roll +ve rotation around forward-axis (left wing up/down), previous tick */ \
uint32_t time; /**< Game time of given space-time position. */
#define BASE_ELEMENTS2(pointerType) \ #define BASE_ELEMENTS2(pointerType) \
SCREEN_DISP_DATA sDisplay; /**< screen coordinate details */ \ SCREEN_DISP_DATA sDisplay; /**< screen coordinate details */ \
@ -91,10 +94,12 @@ typedef struct _tilePos
TILEPOS *watchedTiles; /**< Variable size array of watched tiles, NULL for features */ \ TILEPOS *watchedTiles; /**< Variable size array of watched tiles, NULL for features */ \
UDWORD armour[NUM_HIT_SIDES][WC_NUM_WEAPON_CLASSES] UDWORD armour[NUM_HIT_SIDES][WC_NUM_WEAPON_CLASSES]
/// Along with BASE_ELEMENTS1, the elements of the base class BASE_OBJECT. FIXME If converting to C++, this can be a normal base class and a lot less ugly.
#define NEXTOBJ(pointerType) \ #define NEXTOBJ(pointerType) \
pointerType *psNext; /**< Pointer to the next object in the object list */ \ pointerType *psNext; /**< Pointer to the next object in the object list */ \
pointerType *psNextFunc /**< Pointer to the next object in the function list */ pointerType *psNextFunc /**< Pointer to the next object in the function list */
/// The elements of the base class SIMPLE_ELEMENTS.
#define SIMPLE_ELEMENTS(pointerType) \ #define SIMPLE_ELEMENTS(pointerType) \
BASE_ELEMENTS1(pointerType); \ BASE_ELEMENTS1(pointerType); \
NEXTOBJ(pointerType) NEXTOBJ(pointerType)
@ -105,7 +110,7 @@ typedef struct _tilePos
typedef struct BASE_OBJECT typedef struct BASE_OBJECT
{ {
BASE_ELEMENTS( struct BASE_OBJECT ); BASE_ELEMENTS( struct BASE_OBJECT ); ///< FIXME Should be converted to C++, and be written as ": public SIMPLE_OBJECT", avoiding a whole lot of casts.
} WZ_DECL_MAY_ALIAS BASE_OBJECT; } WZ_DECL_MAY_ALIAS BASE_OBJECT;
typedef struct SIMPLE_OBJECT typedef struct SIMPLE_OBJECT
@ -113,6 +118,26 @@ typedef struct SIMPLE_OBJECT
SIMPLE_ELEMENTS( struct SIMPLE_OBJECT ); SIMPLE_ELEMENTS( struct SIMPLE_OBJECT );
} SIMPLE_OBJECT; } SIMPLE_OBJECT;
/// Space-time coordinate.
typedef struct SPACETIME
{
uint32_t time; ///< Game time
Vector3uw pos; ///< Position of the object
int16_t pitch; ///< Object's pitch +ve rotation around right-axis (nose up/down)
float direction; ///< Object's yaw +ve rotation around up-axis
int16_t roll; ///< Object's roll +ve rotation around forward-axis (left wing up/down)
} SPACETIME;
static inline SPACETIME constructSpacetime(Vector3uw pos, float direction, int16_t pitch, int16_t roll, uint32_t time)
{
SPACETIME ret = {time, pos, pitch, direction, roll};
return ret;
}
#define GET_SPACETIME(psObj) constructSpacetime(psObj->pos, psObj->direction, psObj->pitch, psObj->roll, psObj->time)
#define SET_SPACETIME(psObj, st) do { psObj->pos = st.pos; psObj->direction = st.direction; psObj->pitch = st.pitch; psObj->roll = st.roll; psObj->time = st.time; } while(0)
static inline bool isDead(const BASE_OBJECT* psObj) static inline bool isDead(const BASE_OBJECT* psObj)
{ {
// See objmem.c for comments on the NOT_CURRENT_LIST hack // See objmem.c for comments on the NOT_CURRENT_LIST hack

View File

@ -25,6 +25,51 @@
#include "projectile.h" #include "projectile.h"
#include "structure.h" #include "structure.h"
static inline float interpolateFloat(float v1, float v2, uint32_t t1, uint32_t t2, uint32_t t)
{
int32_t numer = t - t1, denom = t2 - t1;
return v1 + (v2 - v1) * numer/denom;
}
Vector3uw interpolatePos(Vector3uw p1, Vector3uw p2, uint32_t t1, uint32_t t2, uint32_t t)
{
Vector3uw ret = { interpolateInt(p1.x, p2.x, t1, t2, t),
interpolateInt(p1.y, p2.y, t1, t2, t),
interpolateInt(p1.z, p2.z, t1, t2, t)
};
return ret;
}
float interpolateDirection(float v1, float v2, uint32_t t1, uint32_t t2, uint32_t t)
{
if (v1 > v2 + 180)
{
v2 += 360;
}
else if(v2 > v1 + 180)
{
v1 += 360;
}
return interpolateFloat(v1, v2, t1, t2, t);
}
int16_t interpolateCyclicInt16(int16_t v1, int16_t v2, uint32_t t1, uint32_t t2, uint32_t t)
{
int i1 = (uint16_t)v1; // i1: [0; 0xFFFF] v2: [-0x8000; 0x7FFF]
int i2 = v2 + ((v2 + 0x8000 - i1) & 0x10000); // i2: [i1 - 0x8000; i1 + 0x7FFF]
return interpolateInt(i1, i2, t1, t2, t);
}
SPACETIME interpolateSpacetime(SPACETIME st1, SPACETIME st2, uint32_t t)
{
return constructSpacetime(interpolatePos(st1.pos, st2.pos, st1.time, st2.time, t),
interpolateDirection(st1.direction, st2.direction, st1.time, st2.time, t),
interpolateCyclicInt16(st1.pitch, st2.pitch, st1.time, st2.time, t),
interpolateCyclicInt16(st1.roll, st2.roll, st1.time, st2.time, t),
t
);
}
void checkObject(const BASE_OBJECT* psObject, const char * const location_description, const char * function, const int recurse) void checkObject(const BASE_OBJECT* psObject, const char * const location_description, const char * function, const int recurse)
{ {
if (recurse < 0) if (recurse < 0)

View File

@ -33,6 +33,21 @@ extern "C"
static const unsigned int max_check_object_recursion = 4; static const unsigned int max_check_object_recursion = 4;
static inline unsigned interpolateInt(int32_t v1, int32_t v2, uint32_t t1, uint32_t t2, uint32_t t)
{
int32_t numer = t - t1, denom = t2 - t1;
return v1 + (v2 - v1) * numer/denom;
}
/// Get interpolated object position at time curTime.
Vector3uw interpolatePos(Vector3uw p1, Vector3uw p2, uint32_t t1, uint32_t t2, uint32_t t);
/// Get interpolated object direction at time curTime.
float interpolateDirection(float v1, float v2, uint32_t t1, uint32_t t2, uint32_t t);
/// Get interpolated object pitch at time curTime.
int16_t interpolateCyclicInt16(int16_t v1, int16_t v2, uint32_t t1, uint32_t t2, uint32_t t);
/// Get interpolated object roll at time curTime.
SPACETIME interpolateSpacetime(SPACETIME st1, SPACETIME st2, uint32_t t);
void checkObject(const BASE_OBJECT* psObject, const char * const location_description, const char * function, const int recurse); void checkObject(const BASE_OBJECT* psObject, const char * const location_description, const char * function, const int recurse);
/* assert if object is bad */ /* assert if object is bad */

View File

@ -1052,13 +1052,19 @@ static void display3DProjectiles( void )
{ {
switch(psObj->state) switch(psObj->state)
{ {
case PROJ_IMPACT:
if (graphicsTime > psObj->time)
{
break; // Projectile has impacted.
}
// Projectile not quite impacted, so don't break.
case PROJ_INFLIGHTDIRECT: case PROJ_INFLIGHTDIRECT:
case PROJ_INFLIGHTINDIRECT: case PROJ_INFLIGHTINDIRECT:
// if source or destination is visible // if source or destination is visible
if(gfxVisible(psObj)) if(gfxVisible(psObj))
{ {
/* don't display first frame of trajectory (projectile on firing object) */ // Only display once projectile is supposed to have been spawned.
if ( graphicsTime != psObj->born ) if (graphicsTime >= psObj->prevSpacetime.time)
{ {
/* Draw a bullet at psObj->pos.x for X coord /* Draw a bullet at psObj->pos.x for X coord
psObj->pos.y for Z coord psObj->pos.y for Z coord
@ -1083,9 +1089,6 @@ static void display3DProjectiles( void )
} }
break; break;
case PROJ_IMPACT:
break;
case PROJ_POSTIMPACT: case PROJ_POSTIMPACT:
break; break;
@ -1103,6 +1106,7 @@ void renderProjectile(PROJECTILE *psCurr)
Vector3i dv; Vector3i dv;
iIMDShape *pIMD; iIMDShape *pIMD;
SDWORD rx, rz; SDWORD rx, rz;
SPACETIME st;
psStats = psCurr->psWStats; psStats = psCurr->psWStats;
/* Reject flame or command since they have interim drawn fx */ /* Reject flame or command since they have interim drawn fx */
@ -1116,22 +1120,23 @@ void renderProjectile(PROJECTILE *psCurr)
return; return;
} }
st = interpolateSpacetime(psCurr->prevSpacetime, GET_SPACETIME(psCurr), graphicsTime);
//the weapon stats holds the reference to which graphic to use //the weapon stats holds the reference to which graphic to use
/*Need to draw the graphic depending on what the projectile is doing - hitting target, /*Need to draw the graphic depending on what the projectile is doing - hitting target,
missing target, in flight etc - JUST DO IN FLIGHT FOR NOW! */ missing target, in flight etc - JUST DO IN FLIGHT FOR NOW! */
pIMD = psStats->pInFlightGraphic; pIMD = psStats->pInFlightGraphic;
if (clipXY(psCurr->pos.x,psCurr->pos.y)) if (clipXY(st.pos.x, st.pos.y))
{ {
/* Get bullet's x coord */ /* Get bullet's x coord */
dv.x = (psCurr->pos.x - player.p.x) - terrainMidX*TILE_UNITS; dv.x = (st.pos.x - player.p.x) - terrainMidX*TILE_UNITS;
/* Get it's y coord (z coord in the 3d world */ /* Get it's y coord (z coord in the 3d world */
dv.z = terrainMidY*TILE_UNITS - (psCurr->pos.y - player.p.z); dv.z = terrainMidY*TILE_UNITS - (st.pos.y - player.p.z);
/* What's the present height of the bullet? */ /* What's the present height of the bullet? */
dv.y = psCurr->pos.z; dv.y = st.pos.z;
/* Set up the matrix */ /* Set up the matrix */
iV_MatrixBegin(); iV_MatrixBegin();
@ -1145,11 +1150,11 @@ void renderProjectile(PROJECTILE *psCurr)
iV_TRANSLATE(rx,0,-rz); iV_TRANSLATE(rx,0,-rz);
/* Rotate it to the direction it's facing */ /* Rotate it to the direction it's facing */
imdRot2.y = DEG( (int)psCurr->direction ); imdRot2.y = DEG(st.direction);
iV_MatrixRotateY(-imdRot2.y); iV_MatrixRotateY(-imdRot2.y);
/* pitch it */ /* pitch it */
imdRot2.x = DEG(psCurr->pitch); imdRot2.x = DEG(st.pitch);
iV_MatrixRotateX(imdRot2.x); iV_MatrixRotateX(imdRot2.x);
if (psStats->weaponSubClass == WSC_ROCKET || psStats->weaponSubClass == WSC_MISSILE if (psStats->weaponSubClass == WSC_ROCKET || psStats->weaponSubClass == WSC_MISSILE

View File

@ -582,6 +582,9 @@ BOOL proj_SendProjectile(WEAPON *psWeap, BASE_OBJECT *psAttacker, int player, Ve
counterBatteryFire(psAttacker, psTarget); counterBatteryFire(psAttacker, psTarget);
} }
psProj->time = gameTime;
psProj->prevSpacetime.time = gameTime;
CHECK_PROJECTILE(psProj); CHECK_PROJECTILE(psProj);
return true; return true;
@ -683,16 +686,22 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
// Projectile is missile: // Projectile is missile:
bool bMissile = false; bool bMissile = false;
WEAPON_STATS *psStats; WEAPON_STATS *psStats;
Vector3uw prevPos, nextPos; Vector3uw nextPos;
unsigned int targetDistance, currentDistance; unsigned int targetDistance, currentDistance;
int32_t closestCollision = 1<<30;
BASE_OBJECT *psTempObj, *closestCollisionObject = NULL; BASE_OBJECT *psTempObj, *closestCollisionObject = NULL;
Vector3uw closestCollisionPos; SPACETIME closestCollisionSpacetime;
CHECK_PROJECTILE(psProj); CHECK_PROJECTILE(psProj);
if (psProj->prevSpacetime.time == gameTime)
{
return; // Not in flight yet.
}
timeSoFar = gameTime - psProj->born; timeSoFar = gameTime - psProj->born;
psProj->time = gameTime;
psStats = psProj->psWStats; psStats = psProj->psWStats;
ASSERT(psStats != NULL, "proj_InFlightDirectFunc: Invalid weapon stats pointer"); ASSERT(psStats != NULL, "proj_InFlightDirectFunc: Invalid weapon stats pointer");
@ -814,7 +823,6 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
} }
/* Update position */ /* Update position */
prevPos = psProj->pos;
psProj->pos = nextPos; psProj->pos = nextPos;
if (bIndirect) if (bIndirect)
@ -823,6 +831,8 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
psProj->pitch = rad2degf(atan2f(psProj->vZ - (timeSoFar * ACC_GRAVITY / GAME_TICKS_PER_SEC), psProj->vXY)); psProj->pitch = rad2degf(atan2f(psProj->vZ - (timeSoFar * ACC_GRAVITY / GAME_TICKS_PER_SEC), psProj->vXY));
} }
closestCollisionSpacetime.time = 0xFFFFFFFF;
/* Check nearby objects for possible collisions */ /* Check nearby objects for possible collisions */
gridStartIterate(psProj->pos.x, psProj->pos.y, PROJ_NEIGHBOUR_RANGE); gridStartIterate(psProj->pos.x, psProj->pos.y, PROJ_NEIGHBOUR_RANGE);
for (psTempObj = gridIterate(); psTempObj != NULL; psTempObj = gridIterate()) for (psTempObj = gridIterate(); psTempObj != NULL; psTempObj = gridIterate())
@ -877,7 +887,7 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
// FIXME HACK Needed since we got those ugly Vector3uw floating around in BASE_OBJECT... // FIXME HACK Needed since we got those ugly Vector3uw floating around in BASE_OBJECT...
Vector3i Vector3i
posProj = {psProj->pos.x, psProj->pos.y, nextPosZ}, // HACK psProj->pos.z may have been set to 0, since psProj->pos.z can't be negative. So can't use Vector3uw_To3i. posProj = {psProj->pos.x, psProj->pos.y, nextPosZ}, // HACK psProj->pos.z may have been set to 0, since psProj->pos.z can't be negative. So can't use Vector3uw_To3i.
prevPosProj = Vector3uw_To3i(prevPos), prevPosProj = Vector3uw_To3i(psProj->prevSpacetime.pos),
posTemp = Vector3uw_To3i(psTempObj->pos); posTemp = Vector3uw_To3i(psTempObj->pos);
Vector3i diff = Vector3i_Sub(posProj, posTemp); Vector3i diff = Vector3i_Sub(posProj, posTemp);
@ -887,18 +897,16 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
unsigned int targetRadius = establishTargetRadius(psTempObj); unsigned int targetRadius = establishTargetRadius(psTempObj);
int32_t collision = collisionXYZ(prevDiff, diff, targetRadius, targetHeight); int32_t collision = collisionXYZ(prevDiff, diff, targetRadius, targetHeight);
uint32_t collisionTime = psProj->prevSpacetime.time + (psProj->time - psProj->prevSpacetime.time)*collision/1024;
if (collision >= 0 && collision < closestCollision) if (collision >= 0 && collisionTime < closestCollisionSpacetime.time)
{ {
// We hit! // We hit!
//exactHitPos = prevPosProj + (posProj - prevPosProj)*collision/1024; closestCollisionSpacetime = interpolateSpacetime(psProj->prevSpacetime, GET_SPACETIME(psProj), collisionTime);
Vector3i exactHitPos = Vector3i_Add(prevPosProj, Vector3i_Div(Vector3i_Mult(Vector3i_Sub(posProj, prevPosProj), collision), 1024));
exactHitPos.z = MAX(0, exactHitPos.z); // Clamp before casting to unsigned.
closestCollisionPos = Vector3i_To3uw(exactHitPos);
closestCollision = collision;
closestCollisionObject = psTempObj; closestCollisionObject = psTempObj;
closestCollisionSpacetime.pos.z = MAX(0, interpolateInt(psProj->prevSpacetime.pos.z, nextPosZ, psProj->prevSpacetime.time, psProj->time, collisionTime));
// Keep testing for more collisions, in case there was a closer target. // Keep testing for more collisions, in case there was a closer target.
} }
} }
@ -907,7 +915,11 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
if (closestCollisionObject != NULL) if (closestCollisionObject != NULL)
{ {
// We hit! // We hit!
psProj->pos = closestCollisionPos; SET_SPACETIME(psProj, closestCollisionSpacetime);
if(psProj->time == psProj->prevSpacetime.time)
{
--psProj->prevSpacetime.time;
}
setProjectileDestination(psProj, closestCollisionObject); // We hit something. setProjectileDestination(psProj, closestCollisionObject); // We hit something.
/* Buildings cannot be penetrated and we need a penetrating weapon */ /* Buildings cannot be penetrated and we need a penetrating weapon */
@ -1042,11 +1054,11 @@ static void proj_ImpactFunc( PROJECTILE *psObj )
// may want to add both a fire effect and the las sat effect // may want to add both a fire effect and the las sat effect
if (psStats->weaponSubClass == WSC_LAS_SAT) if (psStats->weaponSubClass == WSC_LAS_SAT)
{ {
position.x = psObj->tarX; position.x = psObj->pos.x;
position.z = psObj->tarY; position.z = psObj->pos.y; // z = y [sic] intentional
position.y = map_Height(position.x, position.z); position.y = map_Height(position.x, position.z);
addEffect(&position, EFFECT_SAT_LASER, SAT_LASER_STANDARD, false, NULL, 0); addEffect(&position, EFFECT_SAT_LASER, SAT_LASER_STANDARD, false, NULL, 0);
if (clipXY(psObj->tarX, psObj->tarY)) if (clipXY(psObj->pos.x, psObj->pos.y))
{ {
shakeStart(); shakeStart();
} }
@ -1407,6 +1419,8 @@ static void proj_Update(PROJECTILE *psObj)
{ {
CHECK_PROJECTILE(psObj); CHECK_PROJECTILE(psObj);
psObj->prevSpacetime = GET_SPACETIME(psObj);
/* See if any of the stored objects have died /* See if any of the stored objects have died
* since the projectile was created * since the projectile was created
*/ */

View File

@ -55,6 +55,7 @@ typedef struct PROJECTILE
SDWORD vXY, vZ; ///< axis velocities SDWORD vXY, vZ; ///< axis velocities
UDWORD srcHeight; ///< Height of origin UDWORD srcHeight; ///< Height of origin
SDWORD altChange; ///< Change in altitude SDWORD altChange; ///< Change in altitude
SPACETIME prevSpacetime; ///< Location of projectile in previous tick.
UDWORD born; UDWORD born;
UDWORD died; UDWORD died;
UDWORD expectedDamageCaused; ///< Expected damage that this projectile will cause to the target. UDWORD expectedDamageCaused; ///< Expected damage that this projectile will cause to the target.