diff --git a/h2/src/entities/obj_tree2.qc b/h2/src/entities/obj_tree2.qc index 2bde187..37805c7 100644 --- a/h2/src/entities/obj_tree2.qc +++ b/h2/src/entities/obj_tree2.qc @@ -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 ) { diff --git a/src/constants.qh b/src/constants.qh index 9358ac4..b97c761 100644 --- a/src/constants.qh +++ b/src/constants.qh @@ -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 \ No newline at end of file +#endif + +const vector g_vec_null = [0.0, 0.0, 0.0]; \ No newline at end of file diff --git a/src/entities/info_player_start.qc b/src/entities/info_player_start.qc index 146dfe7..d7a14c0 100644 --- a/src/entities/info_player_start.qc +++ b/src/entities/info_player_start.qc @@ -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." ); diff --git a/src/entities/trigger_teleport.qc b/src/entities/trigger_teleport.qc index b046bc9..b994a4b 100644 --- a/src/entities/trigger_teleport.qc +++ b/src/entities/trigger_teleport.qc @@ -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 ); } } } diff --git a/src/system/headers.src b/src/system/headers.src index edae3e2..da0a0d2 100644 --- a/src/system/headers.src +++ b/src/system/headers.src @@ -5,5 +5,7 @@ idActor.qh idPlayer.qh idRules.qh idItem.qh +idMover.qh +idFX.qh classes.qh #endlist diff --git a/src/system/idActor.qc b/src/system/idActor.qc index e8077bb..6d01aa3 100644 --- a/src/system/idActor.qc +++ b/src/system/idActor.qc @@ -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 ); } \ No newline at end of file diff --git a/src/system/idActor.qh b/src/system/idActor.qh index 9b86818..25967d0 100644 --- a/src/system/idActor.qh +++ b/src/system/idActor.qh @@ -1,3 +1,5 @@ class idActor : idEntity { void idActor( void ); + + virtual void _InternalPostSpawn( void ); }; \ No newline at end of file diff --git a/src/system/idEngine.qc b/src/system/idEngine.qc index a26fc89..28eaa31 100644 --- a/src/system/idEngine.qc +++ b/src/system/idEngine.qc @@ -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__; +} diff --git a/src/system/idEngine.qh b/src/system/idEngine.qh index 3b306bf..7d43a97 100644 --- a/src/system/idEngine.qh +++ b/src/system/idEngine.qh @@ -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; diff --git a/src/system/idEntity.qc b/src/system/idEntity.qc index 0639d9e..f2a36fe 100644 --- a/src/system/idEntity.qc +++ b/src/system/idEntity.qc @@ -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 } \ No newline at end of file diff --git a/src/system/idEntity.qh b/src/system/idEntity.qh index d1e0d07..6c2d3bb 100644 --- a/src/system/idEntity.qh +++ b/src/system/idEntity.qh @@ -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; \ No newline at end of file diff --git a/src/system/idFX.qc b/src/system/idFX.qc new file mode 100644 index 0000000..9eb74a7 --- /dev/null +++ b/src/system/idFX.qc @@ -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 +} \ No newline at end of file diff --git a/src/system/idFX.qh b/src/system/idFX.qh new file mode 100644 index 0000000..9da4d10 --- /dev/null +++ b/src/system/idFX.qh @@ -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; \ No newline at end of file diff --git a/src/system/idItem.qc b/src/system/idItem.qc index 97ae4ed..4479e4d 100644 --- a/src/system/idItem.qc +++ b/src/system/idItem.qc @@ -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) { diff --git a/src/system/idMover.qc b/src/system/idMover.qc new file mode 100644 index 0000000..4b0182d --- /dev/null +++ b/src/system/idMover.qc @@ -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) +{ +} \ No newline at end of file diff --git a/src/system/idMover.qh b/src/system/idMover.qh new file mode 100644 index 0000000..1d3ec65 --- /dev/null +++ b/src/system/idMover.qh @@ -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; \ No newline at end of file diff --git a/src/system/sources.src b/src/system/sources.src index 4ec1a9d..44a9a75 100644 --- a/src/system/sources.src +++ b/src/system/sources.src @@ -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