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 */
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.
}
}

View File

@ -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?

View File

@ -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

View File

@ -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)

View File

@ -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 */

View File

@ -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

View File

@ -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
*/

View File

@ -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.