Port over NSMoverEntity as idMover.

Add idFX class.
Add idEngine::NumForEdict() and idEngine::EdictForNum()
Port over various methods.
This commit is contained in:
Marco Cawthorne 2023-08-25 14:37:13 -07:00
parent 1739999520
commit a7a4333ec9
Signed by: eukara
GPG key ID: CE2032F0A2882A22
17 changed files with 765 additions and 28 deletions

View file

@ -10,7 +10,6 @@ void obj_tree2::obj_tree2( void ) {
void obj_tree2::Spawn( void ) {
SetModel( "models/tree2.mdl" );
AddEffect( EF_DARKFIELD );
}
void obj_tree2::Precache( void ) {

View file

@ -206,13 +206,14 @@ typedef enumflags
EF_BRIGHTFIELD = 1,
EF_MUZZLEFLASH = 2,
EF_BRIGHTLIGHT = 4,
EF_TORCHLIGHT = 6,
EF_DIMLIGHT = 8,
EF_DARKLIGHT = 16,
EF_DARKFIELD = 32,
EF_LIGHT = 64,
EF_NODRAW = 128
} effects_t;
/* make the compiler shut up */
#define EF_TORCHLIGHT 6
#else
typedef enumflags
{
@ -285,4 +286,6 @@ typedef enum
#define SCALE_ORIGIN_BOTTOM 32 // Scaling origin at object bottom
#define SCALE_ORIGIN_TOP 64 // Scaling origin at object top
#define DRF_TRANSLUCENT 128
#endif
#endif
const vector g_vec_null = [0.0, 0.0, 0.0];

View file

@ -14,7 +14,7 @@ idEntity idPlayerStart::MovePlayerToStart( idPlayer player ) {
idEntity point = gameEngine.Find( ( idEntity ) world, ::classname, "idPlayerStart" );
if ( point ) {
player.Transport( point.GetOrigin(), point.GetAngles() );
player.Transport( point.GetOrigin(), point.GetAngles(), false );
return point;
} else {
gameEngine.Error( "Cannot find idPlayerStart on level." );

View file

@ -14,7 +14,7 @@ void idTriggerTeleport::TeleporterTouched( idEntity toucher ) {
idEntity destination = FindFirstTarget();
if ( destination ) {
toucher.Transport( destination.GetOrigin(), destination.GetAngles() );
toucher.Transport( destination.GetOrigin(), destination.GetAngles(), true );
}
}
}

View file

@ -5,5 +5,7 @@ idActor.qh
idPlayer.qh
idRules.qh
idItem.qh
idMover.qh
idFX.qh
classes.qh
#endlist

View file

@ -1,3 +1,10 @@
void idActor::idActor ( void ) {
}
void idActor::_InternalPostSpawn ( void ) {
/* monsters are auto-aim by default */
SetTakedamage( DAMAGE_AIM );
SetSolid( SOLID_SLIDEBOX );
SetMovetype( MOVETYPE_STEP );
}

View file

@ -1,3 +1,5 @@
class idActor : idEntity {
void idActor( void );
virtual void _InternalPostSpawn( void );
};

View file

@ -296,3 +296,20 @@ void idEngine::MultiCast( vector where, multicast_t set ) {
multicast(where, set);
#endif
}
float idEngine::NumForEdict( idEntity targetEnt ) {
/* this requires denormals, may not work on custom engines */
return (float)(__variant)targetEnt / (float)(__variant)nextent(world);
}
idEntity idEngine::EdictForNum( float targetNum ) {
idEntity e = ( idEntity ) world;
while ( ( e = NextEnt( e ) ) ) {
if ( NumForEdict( e ) == targetNum )
return (e);
}
Error( "idEngine::EdictForNum: edict not found\n" );
return __NULL__;
}

View file

@ -64,10 +64,14 @@ class idEngine {
nonvirtual void SetSpawnParms( idEntity e );
/* natively implemented by QuakeWorld */
nonvirtual void LogFrag(idEntity killer, idEntity victim);
nonvirtual string InfoKey(idEntity e, string key);
nonvirtual float StringToFloat(string s);
nonvirtual void MultiCast(vector where, float set);
nonvirtual void LogFrag( idEntity killer, idEntity victim );
nonvirtual string InfoKey( idEntity e, string key );
nonvirtual float StringToFloat( string s );
nonvirtual void MultiCast( vector where, float set );
/* custom builtins that we can do within QC */
virtual float NumForEdict( idEntity targetEnt );
virtual idEntity EdictForNum( float targetNum );
};
idEngine gameEngine;

View file

@ -6,15 +6,29 @@ void idEntity::idEntity( void ) {
if (g_world_initialized == false) {
Precache();
}
_InternalPreSpawn();
Spawn();
_InternalPostSpawn();
}
void idEntity::Precache( void ) {
}
void idEntity::_InternalPreSpawn( void ) {
}
void idEntity::_InternalPostSpawn( void ) {
}
void idEntity::Spawn( void ) {
}
void idEntity::Death( void ) {
}
void idEntity::Pain( void ) {
}
/* set functions */
void idEntity::SetModelindex( float value ) {
modelindex = value;
@ -78,9 +92,6 @@ void idEntity::SetSize( vector min, vector max ) {
gameEngine.SetSize( this, min, max );
}
void idEntity::ScheduleThink( void ( void ) func, float timer ) {
}
void idEntity::SetHealth( float value ) {
health = value;
@ -461,7 +472,7 @@ bool idEntity::IsPlayer( void ) {
return HasFlag(FL_CLIENT);
}
bool idEntity::IsMonster( void ) {
bool idEntity::IsActor( void ) {
return HasFlag(FL_MONSTER);
}
@ -481,13 +492,18 @@ void idEntity::InitTrigger( void ) {
SetModelindex(0);
}
void idEntity::Transport( vector new_pos, vector new_ang ) {
float flSpeed = vlen(this.GetVelocity());
gameEngine.MakeVectors(new_ang);
SetVelocity(v_forward * flSpeed);
void idEntity::Transport( vector newPosition, vector newAngle, bool retainVelocity ) {
SetOrigin(new_pos);
SetAngles(new_ang);
if (retainVelocity == true) {
float flSpeed = gameEngine.VLen(this.GetVelocity());
gameEngine.MakeVectors(newAngle);
SetVelocity(v_forward * flSpeed);
} else {
SetVelocity([0,0,0]);
}
SetOrigin(newPosition);
SetAngles(newAngle);
ForceUpdateClientAngle();
}
@ -495,10 +511,83 @@ idEntity idEntity::FindFirstTarget( void ) {
return gameEngine.Find( (idEntity) world, ::targetname, target);
}
void idEntity::Death( void ) {
void idEntity::ClearVelocity( void ) {
velocity = g_vec_null;
avelocity = g_vec_null;
}
void idEntity::Pain( void ) {
float idEntity::GetTime(void)
{
return (movetype == MOVETYPE_PUSH) ? ltime : time;
}
void idEntity::ReleaseThink( void ) {
think = __NULL__;
nextthink = 0.0f;
}
void idEntity::SetThink( void ( void ) func ) {
think = func;
}
void idEntity::SetNextThink( float fl ) {
float flTime = GetTime() + fl;
/* HACK: to make sure things happen post-spawn */
if ( flTime == 0.0f )
flTime = 0.1f;
if ( flTime >= 0 )
nextthink = flTime;
}
void idEntity::ScheduleThink( void ( void ) func, float fl ) {
SetThink( func );
SetNextThink( fl );
}
/* this is where Hexen 2 specific methods begin. They will be mostly stubbed */
void idEntity::SetScale( float modelScale ) {
#ifdef TARGET_HEXEN2
scale = modelScale;
#endif
}
float idEntity::GetScale( void ) {
#ifdef TARGET_HEXEN2
return scale;
#else
return 1.0f;
#endif
}
void idEntity::SetScaleOrigin( scaleOrigin_t scaleOrg ) {
#ifdef TARGET_HEXEN2
drawflags &= ~SCALE_ORIGIN_MASKOUT;
switch ( scaleOrg ) {
case SCALEORIGIN_CENTER:
drawflags |= SCALE_ORIGIN_CENTER;
break;
case SCALEORIGIN_BOTTOM:
drawflags |= SCALE_ORIGIN_BOTTOM;
break;
case SCALEORIGIN_TOP:
drawflags |= SCALE_ORIGIN_TOP;
break;
}
#endif
}
scaleOrigin_t idEntity::GetScaleOrigin( void ) {
#ifdef TARGET_HEXEN2
if (drawflags & SCALE_ORIGIN_BOTTOM)
return SCALEORIGIN_BOTTOM;
else if (drawflags & SCALE_ORIGIN_TOP)
return SCALEORIGIN_TOP;
else
return SCALEORIGIN_CENTER;
#else
return SCALEORIGIN_CENTER;
#endif
}

View file

@ -1,4 +1,12 @@
typedef enum
{
SCALEORIGIN_CENTER,
SCALEORIGIN_BOTTOM,
SCALEORIGIN_TOP
} scaleOrigin_t;
class idEntity {
public:
void idEntity( void );
/* core engine tracked fields */
@ -33,8 +41,21 @@ class idEntity {
nonvirtual void ClearEffects( void );
/** Sets the size of the entity, relative to its pivot point/origin. */
nonvirtual void SetSize( vector, vector );
/** Schedules a think, first parameter is function to call, second parm is the seconds to wait. */
/** Schedules a think timer. You can only have one going at any given time.
This is the preferred way of setting think timers.
Note that when an entity of movement type `MOVETYPE_PUSH` is not moving,
it will never get to think. */
nonvirtual void ScheduleThink( void (), float );
/** Call to prevent the entity from executing its think timer. */
nonvirtual void ReleaseThink( void );
/** Overrides the Think function of the entity.
Only use it when you want to retain a think timer that's already been set for the entity. */
nonvirtual void SetThink( void ( void ) );
/** Sets the next think timer of the entity.
It has to be a positive value. For example `::SetNextThink(1.5f); will trigger the think
1.5 seconds from then on.*/
nonvirtual void SetNextThink( float );
/** Sets the health of the entity. Will clamp to its maximum health. */
nonvirtual void SetHealth( float );
/** Adds a frag to the entity its info. */
@ -193,20 +214,44 @@ class idEntity {
nonvirtual string GetNoiseValue4( void );
nonvirtual string GetInfoKey( string );
/** Sets the model scale of the entity, normalized value. 1.0 being normal size. */
nonvirtual void SetScale( float );
/** Returns the model scale of the entity. In Quake will always return 1.0. */
nonvirtual float GetScale( void );
/** Sets the origin from which the model scales. See scaleOrigin_t for types. */
nonvirtual void SetScaleOrigin( scaleOrigin_t );
/** Returns the origin at which the model scales. See scaleOrigin_t for types. */
nonvirtual scaleOrigin_t GetScaleOrigin( void );
/** Returns true/false whether this idEntity is a player. */
nonvirtual bool IsPlayer( void );
nonvirtual bool IsMonster( void );
/** Returns true/false whether this idEntity is an actor. */
nonvirtual bool IsActor( void );
/** Returns true/false whether this idEntity is part of the world. */
nonvirtual bool IsWorld( void );
/** Will turn the entity into an invisible trigger. */
nonvirtual void InitTrigger( void );
nonvirtual void Transport( vector, vector );
/** Will transport the entity to a given position, facing a specific angle while retaining the original velocity if desired. */
nonvirtual void Transport( vector, vector, bool );
/** Will return an entity handle of the first entity it finds that was named after SetTarget() 'ing it. */
nonvirtual idEntity FindFirstTarget( void );
nonvirtual float GetTime( void );
nonvirtual void ClearVelocity( void );
/** Overridable: When the entity has initialized, it will execute this method. */
virtual void Spawn( void );
/** Overridable: When the entity spawns on level-load, execute this method. */
virtual void Precache( void );
/** Overridable: Called when the entity dies. Only valid when the entity has health and can take damage. See SetTakedamage() and SetHealth(), as well as SetMaxHealth(). */
virtual void Death( void );
/** Overridable: Called when the entity felt pain. Only valid when the entity has health and can take damage. See SetTakedamage() and SetHealth(), as well as SetMaxHealth(). */
virtual void Pain( void );
private:
virtual void _InternalPreSpawn( void );
virtual void _InternalPostSpawn( void );
};
var bool g_world_initialized;

177
src/system/idFX.qc Normal file
View file

@ -0,0 +1,177 @@
#ifdef TARGET_QUAKEWORLD
#define FX_MSGTYPE MSG_MULTICAST
#else
#define FX_MSGTYPE MSG_BROADCAST
#endif
void idFX::CastMuzzleFlash( idEntity targetEntity ) {
/* the entity is already emitting a light of sorts. */
if (targetEntity.effects > 1)
return;
targetEntity.effects = EF_MUZZLEFLASH;
}
void idFX::CastSpike( vector targetPos ) {
gameEngine.WriteByte( FX_MSGTYPE, SVC_TEMP_ENTITY );
gameEngine.WriteByte( FX_MSGTYPE, TE_SPIKE );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.MultiCast(targetPos, MULTICAST_PHS);
}
void idFX::CastSuperSpike( vector targetPos ) {
gameEngine.WriteByte( FX_MSGTYPE, SVC_TEMP_ENTITY );
gameEngine.WriteByte( FX_MSGTYPE, TE_SUPERSPIKE );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[1] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[2] );
gameEngine.MultiCast( targetPos, MULTICAST_PHS );
}
void idFX::CastGunShot( vector targetPos, float shotCount ) {
gameEngine.WriteByte( FX_MSGTYPE, SVC_TEMP_ENTITY );
gameEngine.WriteByte( FX_MSGTYPE, TE_GUNSHOT );
#ifdef TARGET_QUAKEWORLD
gameEngine.WriteByte( FX_MSGTYPE, shotCount );
#endif
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[1] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[2] );
gameEngine.MultiCast( targetPos, MULTICAST_PVS );
}
void idFX::CastExplosion( vector targetPos ) {
gameEngine.WriteByte( FX_MSGTYPE, SVC_TEMP_ENTITY );
gameEngine.WriteByte( FX_MSGTYPE, TE_EXPLOSION );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[1] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[2] );
gameEngine.MultiCast( targetPos, MULTICAST_PHS );
}
void idFX::CastTarExplosion( vector targetPos ) {
gameEngine.WriteByte( FX_MSGTYPE, SVC_TEMP_ENTITY );
gameEngine.WriteByte( FX_MSGTYPE, TE_TAREXPLOSION );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[1] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[2] );
gameEngine.MultiCast( targetPos, MULTICAST_PHS );
}
void idFX::CastLightning( idEntity boltOwner, vector startPos, vector endPos, lightningStyle_t lightningStyle ) {
gameEngine.WriteByte (FX_MSGTYPE, SVC_TEMP_ENTITY);
switch ( lightningStyle ) {
case FXLIGHTNING_TYPE1:
gameEngine.WriteByte(FX_MSGTYPE, TE_LIGHTNING1);
break;
case FXLIGHTNING_TYPE2:
gameEngine.WriteByte(FX_MSGTYPE, TE_LIGHTNING2);
break;
case FXLIGHTNING_TYPE3:
gameEngine.WriteByte(FX_MSGTYPE, TE_LIGHTNING3);
break;
default:
float r = gameEngine.RInt(gameEngine.Random() * 2.0);
gameEngine.WriteByte(FX_MSGTYPE, TE_LIGHTNING1);
}
gameEngine.WriteEntity(FX_MSGTYPE, boltOwner);
gameEngine.WriteCoord(FX_MSGTYPE, startPos[0] );
gameEngine.WriteCoord(FX_MSGTYPE, startPos[1] );
gameEngine.WriteCoord(FX_MSGTYPE, startPos[2] );
gameEngine.WriteCoord(FX_MSGTYPE, endPos[0] );
gameEngine.WriteCoord(FX_MSGTYPE, endPos[1] );
gameEngine.WriteCoord(FX_MSGTYPE, endPos[2] );
gameEngine.MultiCast( startPos, MULTICAST_PHS );
}
void idFX::CastWizardSpike( vector targetPos ) {
gameEngine.WriteByte( FX_MSGTYPE, SVC_TEMP_ENTITY );
gameEngine.WriteByte( FX_MSGTYPE, TE_WIZSPIKE );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.MultiCast(targetPos, MULTICAST_PHS);
}
void idFX::CastKnightSpike( vector targetPos ) {
gameEngine.WriteByte( FX_MSGTYPE, SVC_TEMP_ENTITY );
gameEngine.WriteByte( FX_MSGTYPE, TE_KNIGHTSPIKE );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.MultiCast(targetPos, MULTICAST_PHS);
}
void idFX::CastLavaSplash( vector targetPos ) {
gameEngine.WriteByte( FX_MSGTYPE, SVC_TEMP_ENTITY );
gameEngine.WriteByte( FX_MSGTYPE, TE_LAVASPLASH );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[1] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[2] );
gameEngine.MultiCast( targetPos, MULTICAST_PVS );
}
void idFX::CastTeleport( vector targetPos ) {
gameEngine.WriteByte( FX_MSGTYPE, SVC_TEMP_ENTITY );
gameEngine.WriteByte( FX_MSGTYPE, TE_TELEPORT );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[1] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[2] );
gameEngine.MultiCast( targetPos, MULTICAST_PVS );
}
void idFX::CastBlood( vector targetPos ) {
#ifdef TARGET_QUAKEWORLD
gameEngine.WriteByte( FX_MSGTYPE, SVC_TEMP_ENTITY );
gameEngine.WriteByte( FX_MSGTYPE, TE_BLOOD );
gameEngine.WriteByte( FX_MSGTYPE, 1 );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[0] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[1] );
gameEngine.WriteCoord( FX_MSGTYPE, targetPos[2] );
gameEngine.MultiCast( targetPos, MULTICAST_PVS );
#else
gameEngine.Particle( targetPos, [0, 0, 0], 73, 1 );
#endif
}
void idFX::CastLightningBlood( idEntity boltOwner, vector startPos, vector endPos, lightningStyle_t lightningStyle ) {
gameEngine.WriteByte (FX_MSGTYPE, SVC_TEMP_ENTITY);
/* QW exclusive effect, substitute the other games with a separate blood impact */
#ifdef TARGET_QUAKEWORLD
gameEngine.WriteByte(FX_MSGTYPE, TE_LIGHTNINGBLOOD);
#else
switch ( lightningStyle ) {
case FXLIGHTNING_TYPE1:
gameEngine.WriteByte(FX_MSGTYPE, TE_LIGHTNING1);
break;
case FXLIGHTNING_TYPE2:
gameEngine.WriteByte(FX_MSGTYPE, TE_LIGHTNING2);
break;
case FXLIGHTNING_TYPE3:
gameEngine.WriteByte(FX_MSGTYPE, TE_LIGHTNING3);
break;
default:
float r = gameEngine.RInt(gameEngine.Random() * 2.0);
gameEngine.WriteByte(FX_MSGTYPE, TE_LIGHTNING1);
}
#endif
gameEngine.WriteEntity(FX_MSGTYPE, boltOwner);
gameEngine.WriteCoord(FX_MSGTYPE, startPos[0] );
gameEngine.WriteCoord(FX_MSGTYPE, startPos[1] );
gameEngine.WriteCoord(FX_MSGTYPE, startPos[2] );
gameEngine.WriteCoord(FX_MSGTYPE, endPos[0] );
gameEngine.WriteCoord(FX_MSGTYPE, endPos[1] );
gameEngine.WriteCoord(FX_MSGTYPE, endPos[2] );
gameEngine.MultiCast( startPos, MULTICAST_PHS );
/* substitute for NQ/H2 */
#ifndef TARGET_QUAKEWORLD
CastBlood(endPos);
#endif
}

40
src/system/idFX.qh Normal file
View file

@ -0,0 +1,40 @@
typedef enum
{
FXLIGHTNING_RANDOM,
FXLIGHTNING_TYPE1,
FXLIGHTNING_TYPE2,
FXLIGHTNING_TYPE3
} lightningStyle_t;
/** The idFX class contains helper functions for effects included in NQ/QW/H2. Keep in mind that a lot of these assume that your palette is organized similarily to how they are in their respective games. So if you use a custom palette, you may want to avoid using this namespace. */
class idFX
{
/** Causes the target entity to cast a muzzleflash. */
nonvirtual void CastMuzzleFlash( idEntity );
/** Create a spike impact at the specified position. */
nonvirtual void CastSpike( vector );
/** Create a super-spike impact at the specified position. */
nonvirtual void CastSuperSpike( vector );
/** Create a gun shot impact at the specified position. */
nonvirtual void CastGunShot( vector, float );
/** Create an explosion at the specified position. */
nonvirtual void CastExplosion( vector );
/** Create a tar explosion at the specified position. */
nonvirtual void CastTarExplosion( vector );
/** Create a lightning strike between to positions. */
nonvirtual void CastLightning( idEntity, vector, vector, lightningStyle_t );
/** Create a wizard spike impact at the specified position. */
nonvirtual void CastWizardSpike( vector );
/** Create a knight spike impact at the specified position. */
nonvirtual void CastKnightSpike( vector );
/** Create a lava splash at the specified position. */
nonvirtual void CastLavaSplash( vector );
/** Create a teleport effect at the specified position. */
nonvirtual void CastTeleport( vector );
/** Create a blood splash specified position. */
nonvirtual void CastBlood( vector );
/** Create a lightning bolt between two points + blood splash at the specified position. */
nonvirtual void CastLightningBlood( idEntity, vector, vector, lightningStyle_t );
};
noref idFX gameFX;

View file

@ -1,13 +1,15 @@
.float item;
void idItem::idItem(void) {
}
void idItem::SetID(float a) {
// item = a;
item = a;
}
float idItem::GetID(void) {
//return item;
return item;
}
void idItem::PrimaryAttack(void) {

261
src/system/idMover.qc Normal file
View file

@ -0,0 +1,261 @@
void idMover::idMover( void ) {
}
vector
idMover::GetDirectionalPosition(vector vecAngle, float flLip)
{
vector vecMoveDir = g_vec_null;
vector vecPos = g_vec_null;
/* editor angle */
if (vecAngle == [0,-1,0]) {
vecMoveDir = [0,0,1];
} else if (vecAngle == [0,-2,0]) {
vecMoveDir = [0,0,-1];
} else {
/* manually specified angle */
gameEngine.MakeVectors(vecAngle);
vecMoveDir = v_forward;
}
vecPos = (GetOrigin() + vecMoveDir * (gameEngine.AbsoluteFloat(vecMoveDir * size) - flLip));
return vecPos;
}
vector
idMover::GetDirectionalRotation(vector normalizedAngle, float travelDistance)
{
vector vecMoveDir = normalizedAngle;
return (GetAngles() + vecMoveDir * travelDistance);
}
void
idMover::SetMoverState(moverState_t val)
{
m_moverState = val;
}
moverState_t
idMover::GetMoverState(void)
{
return m_moverState;
}
void
idMover::SetMoverType(moverType_t val)
{
m_moverType = val;
}
moverType_t
idMover::GetMoverType(void)
{
return m_moverType;
}
void
idMover::SetMoverPosition1(vector val)
{
m_vecPos1 = val;
}
vector
idMover::GetMoverPosition1(void)
{
return m_vecPos1;
}
void
idMover::SetMoverPosition2(vector val)
{
m_vecPos2 = val;
}
vector
idMover::GetMoverPosition2(void)
{
return m_vecPos2;
}
void
idMover::SetMoverRotation1(vector val)
{
m_vecPos3 = val;
}
vector
idMover::GetMoverRotation1(void)
{
return m_vecPos3;
}
void
idMover::SetMoverRotation2(vector val)
{
m_vecPos4 = val;
}
vector
idMover::GetMoverRotation2(void)
{
return m_vecPos4;
}
void
idMover::MoveToPosition(vector vecDest, float flSpeed)
{
MoveAndRotateToPosition(vecDest, GetAngles(), flSpeed);
}
void
idMover::MoveAndRotateToPosition(vector vecDest, vector vecAngle, float flSpeed)
{
vector vecDifference;
vector vecAngleDifference;
float flTravel;
float fTravelTime;
/* selects which end method to trigger based on state. */
static void MoveToRotPosition_Done(float travelTime) {
if (m_moverState == MOVER_1TO2) {
ScheduleThink(_ArrivedAtRotPosition2, travelTime);
} else if (m_moverState == MOVER_2TO1) {
ScheduleThink(_ArrivedAtRotPosition1, travelTime);
}
MoverStartsMoving();
}
/* selects which end positition to set based on state */
static void MoveToRotPosition_SetDest(vector vecDest, vector vecAngle) {
if (m_moverState == MOVER_POS2) {
m_vecPos1 = vecDest;
m_vecPos3 = vecAngle;
m_moverState = MOVER_2TO1;
} else {
m_moverState = MOVER_1TO2;
m_vecPos2 = vecDest;
m_vecPos4 = vecAngle;
}
}
/* abort if no speed is defined whatsoever */
if (!flSpeed) {
gameEngine.ObjError("idMover::MoveToPosition: No speed defined!");
return;
}
/* set the appropriate attribute */
MoveToRotPosition_SetDest(vecDest, vecAngle);
/* calculate travel distance and time */
vecDifference = (vecDest - GetOrigin());
vecAngleDifference = (vecAngle - GetAngles());
flTravel = gameEngine.VLen(vecDifference);
fTravelTime = (flTravel / flSpeed);
/* if we won't move far enough, we may rotate? */
if (!flTravel) {
flTravel = gameEngine.VLen(vecAngleDifference);
fTravelTime = (flTravel / flSpeed);
}
/* if we're already there, don't bother and trigger it right now. */
if (fTravelTime <= 0.0) {
if (m_moverState == MOVER_1TO2) {
MoverStartsMoving();
_ArrivedAtRotPosition2();
MoverFinishesMoving();
} else if (m_moverState == MOVER_2TO1) {
MoverStartsMoving();
_ArrivedAtRotPosition1();
MoverFinishesMoving();
}
return;
}
/* schedule the movement and proceed to trigger the end after a certain time */
MoveToRotPosition_Done(fTravelTime);
SetVelocity(vecDifference * (1.0f / fTravelTime));
SetAngularVelocity((vecAngleDifference * (1.0 / fTravelTime)));
}
void
idMover::MoveToReverse(float flSpeed)
{
if ((GetMoverState() == MOVER_POS2) || (GetMoverState() == MOVER_1TO2)){
MoveToPosition(GetMoverPosition1(), flSpeed);
} else {
MoveToPosition(GetMoverPosition2(), flSpeed);
}
}
void
idMover::RotateToPosition(vector vecAngle, float flSpeed)
{
MoveAndRotateToPosition(GetOrigin(), vecAngle, flSpeed);
}
void
idMover::RotateToReverse(float flSpeed)
{
if ((GetMoverState() == MOVER_POS2) || (GetMoverState() == MOVER_1TO2)){
RotateToPosition(GetMoverRotation1(), flSpeed);
} else {
RotateToPosition(GetMoverRotation2(), flSpeed);
}
}
bool
idMover::IsMoving(void)
{
switch (GetMoverState()) {
case MOVER_POS1:
case MOVER_POS2:
return false;
break;
default:
return true;
}
}
void
idMover::_ArrivedAtRotPosition1(void)
{
SetOrigin(m_vecPos1);
SetAngles(m_vecPos3);
ClearVelocity();
ReleaseThink();
m_moverState = MOVER_POS1;
MoverFinishesMoving();
}
void
idMover::_ArrivedAtRotPosition2(void)
{
SetOrigin(m_vecPos2);
SetAngles(m_vecPos4);
ClearVelocity();
ReleaseThink();
m_moverState = MOVER_POS2;
MoverFinishesMoving();
}
void
idMover::_BeginMoving(void)
{
}
void
idMover::MoverStartsMoving(void)
{
}
void
idMover::MoverFinishesMoving(void)
{
}

87
src/system/idMover.qh Normal file
View file

@ -0,0 +1,87 @@
/** The state the NSMoverEntity is in. */
typedef enum
{
MOVER_POS1, /**< At the initial starting position. */
MOVER_POS2, /**< At the final destination. */
MOVER_1TO2, /**< On its way to the final destination. */
MOVER_2TO1 /**< on its way back to the starting position. */
} moverState_t;
/** The movement type of the NSMoverEntity. */
typedef enum
{
MOVERTYPE_LINEAR, /**< Moves in a linear fashion. */
MOVERTYPE_ACCELERATED, /**< Moved in an accelerated fashion. */
} moverType_t;
class idMover:idEntity
{
public:
void idMover(void);
/** Returns a directional position from the current one. */
nonvirtual vector GetDirectionalPosition(vector, float);
/** Returns a directional angle from the current one. */
nonvirtual vector GetDirectionalRotation(vector, float);
/** Set the movement state. */
nonvirtual void SetMoverState(moverState_t);
/** Returns the movement state. */
nonvirtual moverState_t GetMoverState(void);
/** Set the movement type. */
nonvirtual void SetMoverType(moverType_t);
/** Returns the movement type. */
nonvirtual moverType_t GetMoverType(void);
/** Sets the initial starting position. */
nonvirtual void SetMoverPosition1(vector);
/** Returns the starting position. */
nonvirtual vector GetMoverPosition1(void);
/** Sets the final destination. */
nonvirtual void SetMoverPosition2(vector);
/** Returns the final destination. */
nonvirtual vector GetMoverPosition2(void);
/** Sets the initial starting angle. */
nonvirtual void SetMoverRotation1(vector);
/** Returns the starting angle. */
nonvirtual vector GetMoverRotation1(void);
/** Sets the final destination angle. */
nonvirtual void SetMoverRotation2(vector);
/** Returns the final destination angle. */
nonvirtual vector GetMoverRotation2(void);
/** Moves this entity to the specified position. */
nonvirtual void MoveToPosition(vector, float);
/** Rotates this entity to the desired angle. */
nonvirtual void RotateToPosition(vector, float);
/** Moves and rotates this entity to a desired location. */
nonvirtual void MoveAndRotateToPosition(vector, vector, float);
/** Moves to the reverse state. If a mover is at pos1, it'll go to pos2, etc. */
nonvirtual void MoveToReverse(float);
/** Rotates to the reversed state. */
nonvirtual void RotateToReverse(float);
/** Returns if the NSMoverEntity is currently moving. */
nonvirtual bool IsMoving(void);
/** Overridable: Called when the mover starts moving from its position to another. */
virtual void MoverStartsMoving(void);
/** Overridable: Called when the mover completes its movement to a destination. */
virtual void MoverFinishesMoving(void);
private:
nonvirtual void _ArrivedAtRotPosition1(void);
nonvirtual void _ArrivedAtRotPosition2(void);
nonvirtual void _BeginMoving(void);
};
.vector m_vecPos1;
.vector m_vecPos2;
.vector m_vecPos3;
.vector m_vecPos4;
.moverState_t m_moverState;
.moverType_t m_moverType;

View file

@ -1,9 +1,11 @@
#includelist
idEngine.qc
idEntity.qc
idActor.qc
idPlayer.qc
idRules.qc
idItem.qc
idMover.qc
idFX.qc
idEngine.qc
classes.qc
#endlist