logical: Make some projectiles animated in-flight.
git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/trunk@9656 4a71c877-e1ca-e34f-864e-861f7616d084master
parent
a99ef4ba19
commit
92225b6b18
|
@ -104,7 +104,7 @@ UDWORD getModularScaledRealTime(UDWORD timePeriod, UDWORD requiredRange)
|
|||
}
|
||||
|
||||
/* Call this each loop to update the game timer */
|
||||
bool logicalUpdates = false;
|
||||
bool logicalUpdates = true;
|
||||
void gameTimeUpdate()
|
||||
{
|
||||
uint32_t currTime = SDL_GetTicks();
|
||||
|
@ -134,18 +134,17 @@ void gameTimeUpdate()
|
|||
}
|
||||
|
||||
// Calculate the time for this frame
|
||||
deltaGameTime = scaledCurrTime - gameTime;
|
||||
deltaGraphicsTime = scaledCurrTime - graphicsTime;
|
||||
|
||||
// Adjust deltas.
|
||||
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...
|
||||
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
|
||||
deltaGraphicsTime -= slideBack;
|
||||
}
|
||||
|
@ -159,12 +158,15 @@ void gameTimeUpdate()
|
|||
}
|
||||
else
|
||||
{
|
||||
deltaGameTime = scaledCurrTime - gameTime;
|
||||
|
||||
// Limit the frame time
|
||||
if (deltaGraphicsTime > GTIME_MAXFRAME)
|
||||
{
|
||||
uint32_t slideBack = deltaGraphicsTime - GTIME_MAXFRAME;
|
||||
baseTime += slideBack / modifier; // adjust the addition to base time
|
||||
deltaGraphicsTime -= slideBack;
|
||||
deltaGameTime -= slideBack; // If !sane, then deltaGameTime == deltaGraphicsTime.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
/// Changes in GAME_UNITS_PER_TICK increments.
|
||||
extern UDWORD gameTime;
|
||||
/// 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;
|
||||
/** 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?
|
||||
|
|
|
@ -60,6 +60,7 @@ typedef struct _tilePos
|
|||
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) \
|
||||
OBJECT_TYPE type; /**< The type of 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 */ \
|
||||
SWORD pitch; /**< Object's pitch +ve rotation around right-axis (nose up/down) */ \
|
||||
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) \
|
||||
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 */ \
|
||||
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) \
|
||||
pointerType *psNext; /**< Pointer to the next object in the object list */ \
|
||||
pointerType *psNextFunc /**< Pointer to the next object in the function list */
|
||||
|
||||
/// The elements of the base class SIMPLE_ELEMENTS.
|
||||
#define SIMPLE_ELEMENTS(pointerType) \
|
||||
BASE_ELEMENTS1(pointerType); \
|
||||
NEXTOBJ(pointerType)
|
||||
|
@ -105,7 +110,7 @@ typedef struct _tilePos
|
|||
|
||||
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;
|
||||
|
||||
typedef struct SIMPLE_OBJECT
|
||||
|
@ -113,6 +118,26 @@ typedef struct SIMPLE_OBJECT
|
|||
SIMPLE_ELEMENTS( struct 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)
|
||||
{
|
||||
// See objmem.c for comments on the NOT_CURRENT_LIST hack
|
||||
|
|
|
@ -25,6 +25,51 @@
|
|||
#include "projectile.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)
|
||||
{
|
||||
if (recurse < 0)
|
||||
|
|
|
@ -33,6 +33,21 @@ extern "C"
|
|||
|
||||
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);
|
||||
|
||||
/* assert if object is bad */
|
||||
|
|
|
@ -1052,13 +1052,19 @@ static void display3DProjectiles( void )
|
|||
{
|
||||
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_INFLIGHTINDIRECT:
|
||||
// if source or destination is visible
|
||||
if(gfxVisible(psObj))
|
||||
{
|
||||
/* don't display first frame of trajectory (projectile on firing object) */
|
||||
if ( graphicsTime != psObj->born )
|
||||
// Only display once projectile is supposed to have been spawned.
|
||||
if (graphicsTime >= psObj->prevSpacetime.time)
|
||||
{
|
||||
/* Draw a bullet at psObj->pos.x for X coord
|
||||
psObj->pos.y for Z coord
|
||||
|
@ -1083,9 +1089,6 @@ static void display3DProjectiles( void )
|
|||
}
|
||||
break;
|
||||
|
||||
case PROJ_IMPACT:
|
||||
break;
|
||||
|
||||
case PROJ_POSTIMPACT:
|
||||
break;
|
||||
|
||||
|
@ -1103,6 +1106,7 @@ void renderProjectile(PROJECTILE *psCurr)
|
|||
Vector3i dv;
|
||||
iIMDShape *pIMD;
|
||||
SDWORD rx, rz;
|
||||
SPACETIME st;
|
||||
|
||||
psStats = psCurr->psWStats;
|
||||
/* Reject flame or command since they have interim drawn fx */
|
||||
|
@ -1116,22 +1120,23 @@ void renderProjectile(PROJECTILE *psCurr)
|
|||
return;
|
||||
}
|
||||
|
||||
st = interpolateSpacetime(psCurr->prevSpacetime, GET_SPACETIME(psCurr), graphicsTime);
|
||||
|
||||
//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,
|
||||
missing target, in flight etc - JUST DO IN FLIGHT FOR NOW! */
|
||||
pIMD = psStats->pInFlightGraphic;
|
||||
|
||||
if (clipXY(psCurr->pos.x,psCurr->pos.y))
|
||||
if (clipXY(st.pos.x, st.pos.y))
|
||||
{
|
||||
/* 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 */
|
||||
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? */
|
||||
dv.y = psCurr->pos.z;
|
||||
dv.y = st.pos.z;
|
||||
/* Set up the matrix */
|
||||
iV_MatrixBegin();
|
||||
|
||||
|
@ -1145,11 +1150,11 @@ void renderProjectile(PROJECTILE *psCurr)
|
|||
iV_TRANSLATE(rx,0,-rz);
|
||||
|
||||
/* Rotate it to the direction it's facing */
|
||||
imdRot2.y = DEG( (int)psCurr->direction );
|
||||
imdRot2.y = DEG(st.direction);
|
||||
iV_MatrixRotateY(-imdRot2.y);
|
||||
|
||||
/* pitch it */
|
||||
imdRot2.x = DEG(psCurr->pitch);
|
||||
imdRot2.x = DEG(st.pitch);
|
||||
iV_MatrixRotateX(imdRot2.x);
|
||||
|
||||
if (psStats->weaponSubClass == WSC_ROCKET || psStats->weaponSubClass == WSC_MISSILE
|
||||
|
|
|
@ -582,6 +582,9 @@ BOOL proj_SendProjectile(WEAPON *psWeap, BASE_OBJECT *psAttacker, int player, Ve
|
|||
counterBatteryFire(psAttacker, psTarget);
|
||||
}
|
||||
|
||||
psProj->time = gameTime;
|
||||
psProj->prevSpacetime.time = gameTime;
|
||||
|
||||
CHECK_PROJECTILE(psProj);
|
||||
|
||||
return true;
|
||||
|
@ -683,16 +686,22 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
|
|||
// Projectile is missile:
|
||||
bool bMissile = false;
|
||||
WEAPON_STATS *psStats;
|
||||
Vector3uw prevPos, nextPos;
|
||||
Vector3uw nextPos;
|
||||
unsigned int targetDistance, currentDistance;
|
||||
int32_t closestCollision = 1<<30;
|
||||
BASE_OBJECT *psTempObj, *closestCollisionObject = NULL;
|
||||
Vector3uw closestCollisionPos;
|
||||
SPACETIME closestCollisionSpacetime;
|
||||
|
||||
CHECK_PROJECTILE(psProj);
|
||||
|
||||
if (psProj->prevSpacetime.time == gameTime)
|
||||
{
|
||||
return; // Not in flight yet.
|
||||
}
|
||||
|
||||
timeSoFar = gameTime - psProj->born;
|
||||
|
||||
psProj->time = gameTime;
|
||||
|
||||
psStats = psProj->psWStats;
|
||||
ASSERT(psStats != NULL, "proj_InFlightDirectFunc: Invalid weapon stats pointer");
|
||||
|
||||
|
@ -814,7 +823,6 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
|
|||
}
|
||||
|
||||
/* Update position */
|
||||
prevPos = psProj->pos;
|
||||
psProj->pos = nextPos;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
closestCollisionSpacetime.time = 0xFFFFFFFF;
|
||||
|
||||
/* Check nearby objects for possible collisions */
|
||||
gridStartIterate(psProj->pos.x, psProj->pos.y, PROJ_NEIGHBOUR_RANGE);
|
||||
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...
|
||||
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.
|
||||
prevPosProj = Vector3uw_To3i(prevPos),
|
||||
prevPosProj = Vector3uw_To3i(psProj->prevSpacetime.pos),
|
||||
posTemp = Vector3uw_To3i(psTempObj->pos);
|
||||
|
||||
Vector3i diff = Vector3i_Sub(posProj, posTemp);
|
||||
|
@ -887,18 +897,16 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
|
|||
unsigned int targetRadius = establishTargetRadius(psTempObj);
|
||||
|
||||
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!
|
||||
//exactHitPos = prevPosProj + (posProj - prevPosProj)*collision/1024;
|
||||
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;
|
||||
closestCollisionSpacetime = interpolateSpacetime(psProj->prevSpacetime, GET_SPACETIME(psProj), collisionTime);
|
||||
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.
|
||||
}
|
||||
}
|
||||
|
@ -907,7 +915,11 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
|
|||
if (closestCollisionObject != NULL)
|
||||
{
|
||||
// We hit!
|
||||
psProj->pos = closestCollisionPos;
|
||||
SET_SPACETIME(psProj, closestCollisionSpacetime);
|
||||
if(psProj->time == psProj->prevSpacetime.time)
|
||||
{
|
||||
--psProj->prevSpacetime.time;
|
||||
}
|
||||
setProjectileDestination(psProj, closestCollisionObject); // We hit something.
|
||||
|
||||
/* 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
|
||||
if (psStats->weaponSubClass == WSC_LAS_SAT)
|
||||
{
|
||||
position.x = psObj->tarX;
|
||||
position.z = psObj->tarY;
|
||||
position.x = psObj->pos.x;
|
||||
position.z = psObj->pos.y; // z = y [sic] intentional
|
||||
position.y = map_Height(position.x, position.z);
|
||||
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();
|
||||
}
|
||||
|
@ -1407,6 +1419,8 @@ static void proj_Update(PROJECTILE *psObj)
|
|||
{
|
||||
CHECK_PROJECTILE(psObj);
|
||||
|
||||
psObj->prevSpacetime = GET_SPACETIME(psObj);
|
||||
|
||||
/* See if any of the stored objects have died
|
||||
* since the projectile was created
|
||||
*/
|
||||
|
|
|
@ -55,6 +55,7 @@ typedef struct PROJECTILE
|
|||
SDWORD vXY, vZ; ///< axis velocities
|
||||
UDWORD srcHeight; ///< Height of origin
|
||||
SDWORD altChange; ///< Change in altitude
|
||||
SPACETIME prevSpacetime; ///< Location of projectile in previous tick.
|
||||
UDWORD born;
|
||||
UDWORD died;
|
||||
UDWORD expectedDamageCaused; ///< Expected damage that this projectile will cause to the target.
|
||||
|
|
Loading…
Reference in New Issue