diff --git a/README.md b/README.md index 6ffbc8ad..afee30b0 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,7 @@ All the weapons are implemented, so are the gamemodes: - **Assassination** (as) - **Escape** (es) -Not all equipment is implemented and no map radar/overview is recreated. -Basically, the game is playable as long as you don't care about grenades (for now). +The game is playable as long as you don't care about the radar/overview display. # Installing 1. Get the latest version of FTEQW from the [website](http://triptohell.info/moodles/). diff --git a/Source/Client/Defs.h b/Source/Client/Defs.h index 61b7491c..49a4e45f 100755 --- a/Source/Client/Defs.h +++ b/Source/Client/Defs.h @@ -40,16 +40,20 @@ var float autocvar_cl_bob = 0.01; var float autocvar_cl_bobcycle = 0.8; var float autocvar_cl_bobup = 0.5; var float autocvar_cl_bobclassic = 0; +var float autocvar_v_lefthanded = 0; // Particle stuff var float PARTICLE_SPARK; var float PARTICLE_PIECES_BLACK; var float PARTICLE_SMOKE_GREY; var float PARTICLE_SMOKE_BROWN; +var float PARTICLE_SMOKEGRENADE; var float PARTICLE_BLOOD; var float DECAL_SHOT; var float DECAL_GLASS; +var float SHADER_CULLED; + vector vHUDColor; // Defined in HUD_Draw (HUD.c) vector vVGUIColor; // Defined in HUD_Draw (VGUI.c) vector vCrossColor; // Defined in HUD_Draw (HUDCrosshair.c) @@ -101,6 +105,10 @@ float fWeaponEventPlayer; .float fWeaponLast; .float fWeaponBoneID; +// Flashbang'd +var float fFlashTime; +var float fFlashAlpha; + void Animation_ShootWeapon( entity ePlayer ); void Animation_ReloadWeapon( entity ePlayer ); diff --git a/Source/Client/Event.c b/Source/Client/Event.c index aac419cd..f6682d59 100755 --- a/Source/Client/Event.c +++ b/Source/Client/Event.c @@ -490,6 +490,17 @@ void CSQC_Parse_Event( void ) { vSparkAngle_z = readcoord(); Effect_CreateSpark( vSparkPos, vSparkAngle ); + } else if ( fHeader == EV_SMOKE ) { + vector vSmokePos; + + vSmokePos_x = readcoord(); + vSmokePos_y = readcoord(); + vSmokePos_z = readcoord(); + + Effect_CreateSmoke( vSmokePos ); + } else if ( fHeader == EV_FLASH ) { + fFlashTime = 3.0f; + fFlashAlpha = 1.0f; } else if ( fHeader == EV_CHAT ) { float fSender = readbyte(); float fTeam = readbyte(); diff --git a/Source/Client/HUD.c b/Source/Client/HUD.c index 8de86c4e..76175e15 100755 --- a/Source/Client/HUD.c +++ b/Source/Client/HUD.c @@ -386,6 +386,20 @@ void HUD_DrawRadar( void ) { drawpic( '16 16', "sprites/radar640.spr_0.tga", '128 128', '1 1 1', 0.5, DRAWFLAG_ADDITIVE ); } +void HUD_DrawFlash( void ) { + if ( fFlashTime > 0.0f ) { + fFlashTime -= frametime; + } else { + if ( fFlashAlpha > 0.0f ) { + fFlashAlpha -= ( frametime * 0.5 ); + } else { + return; + } + } + + drawfill( '0 0', vVideoResolution, '1 1 1', fFlashAlpha, fFlashTime ); +} + /* ================= HUD_Draw @@ -403,6 +417,7 @@ void HUD_Draw( void ) { HUD_DrawCrosshair(); } + HUD_DrawFlash(); HUD_DrawTimer(); HUD_DrawRadar(); HUD_DrawHealth(); diff --git a/Source/Client/HUDWeaponSelect.c b/Source/Client/HUDWeaponSelect.c index 49fbe577..b0bd3d31 100755 --- a/Source/Client/HUDWeaponSelect.c +++ b/Source/Client/HUDWeaponSelect.c @@ -328,7 +328,7 @@ void HUD_DrawWeaponSelect( void ) { return; } - vector vSelectPos = '138 12 0'; + vector vSelectPos = '160 12 0'; for ( int i = 0; i < 4; i++ ) { HUD_DrawWeaponSelect_Num( vSelectPos, i ); @@ -336,43 +336,36 @@ void HUD_DrawWeaponSelect( void ) { // Again, grenades are treated seperately if ( i == SLOT_GRENADE ) { if ( wptTable[ fHUDWeaponSelected ].iSlot == SLOT_GRENADE ) { - vSelectPos_y -= 45; if ( getstati_punf( STAT_ITEM_HEGRENADE ) ) { - vSelectPos_y += 45; drawsubpic( vSelectPos + '0 20', '170 45', wpSymbolTable[ WEAPON_HEGRENADE ].sSprite, wpSymbolTable[ WEAPON_HEGRENADE ].vOrigin, [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); + if ( iHUDGrenadesSelected == WEAPON_HEGRENADE ) { + drawsubpic( vSelectPos + '0 20', '170 45', "sprites/640hud3.spr_0.tga", '0 0.703125', [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); + } + vSelectPos_y += 45; } - if ( iHUDGrenadesSelected == WEAPON_HEGRENADE ) { - drawsubpic( vSelectPos + '0 20', '170 45', "sprites/640hud3.spr_0.tga", '0 0.703125', [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); - } - if ( getstati_punf( STAT_ITEM_FLASHBANG ) ) { - vSelectPos_y += 45; drawsubpic( vSelectPos + '0 20', '170 45', wpSymbolTable[ WEAPON_FLASHBANG ].sSprite, wpSymbolTable[ WEAPON_FLASHBANG ].vOrigin, [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); + if ( iHUDGrenadesSelected == WEAPON_FLASHBANG ) { + drawsubpic( vSelectPos + '0 20', '170 45', "sprites/640hud3.spr_0.tga", '0 0.703125', [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); + } + vSelectPos_y += 45; } - if ( iHUDGrenadesSelected == WEAPON_FLASHBANG ) { - drawsubpic( vSelectPos + '0 20', '170 45', "sprites/640hud3.spr_0.tga", '0 0.703125', [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); - } - - if ( getstati_punf( STAT_ITEM_SMOKEGRENADE ) ) { - vSelectPos_y += 45; drawsubpic( vSelectPos + '0 20', '170 45', wpSymbolTable[ WEAPON_SMOKEGRENADE ].sSprite, wpSymbolTable[ WEAPON_SMOKEGRENADE ].vOrigin, [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); - - } - if ( iHUDGrenadesSelected == WEAPON_SMOKEGRENADE ) { - drawsubpic( vSelectPos + '0 20', '170 45', "sprites/640hud3.spr_0.tga", '0 0.703125', [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); - } - - if ( getstatf( STAT_SLOT_GRENADE ) ) { + if ( iHUDGrenadesSelected == WEAPON_SMOKEGRENADE ) { + drawsubpic( vSelectPos + '0 20', '170 45', "sprites/640hud3.spr_0.tga", '0 0.703125', [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); + } vSelectPos_y += 45; - drawsubpic( vSelectPos + '0 20', '170 45', wpSymbolTable[ getstatf( STAT_SLOT_GRENADE ) ].sSprite, wpSymbolTable[ getstatf( STAT_SLOT_GRENADE ) ].vOrigin, [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); } - if ( iHUDGrenadesSelected == getstatf( STAT_SLOT_GRENADE ) ) { - drawsubpic( vSelectPos + '0 20', '170 45', "sprites/640hud3.spr_0.tga", '0 0.703125', [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); + if ( getstatf( STAT_SLOT_GRENADE ) ) { + drawsubpic( vSelectPos + '0 20', '170 45', wpSymbolTable[ getstatf( STAT_SLOT_GRENADE ) ].sSprite, wpSymbolTable[ getstatf( STAT_SLOT_GRENADE ) ].vOrigin, [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); + if ( iHUDGrenadesSelected == getstatf( STAT_SLOT_GRENADE ) ) { + drawsubpic( vSelectPos + '0 20', '170 45', "sprites/640hud3.spr_0.tga", '0 0.703125', [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); + } + vSelectPos_y += 45; } } } else { - if ( wptTable[ fHUDWeaponSelected ].iSlot == i ) { drawsubpic( vSelectPos + '0 20', '170 45', wpSymbolTable[ fHUDWeaponSelected ].sSprite, wpSymbolTable[ fHUDWeaponSelected ].vOrigin, [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); drawsubpic( vSelectPos + '0 20', '170 45', "sprites/640hud3.spr_0.tga", '0 0.703125', [ 0.6640625, 0.17578125 ], vHUDColor, 1, DRAWFLAG_ADDITIVE ); diff --git a/Source/Client/Init.c b/Source/Client/Init.c index b82fe06d..66ca2135 100755 --- a/Source/Client/Init.c +++ b/Source/Client/Init.c @@ -80,8 +80,11 @@ void CSQC_Init(float apilevel, string enginename, float engineversion) { PARTICLE_BLOOD = particleeffectnum( "part_blood" ); DECAL_SHOT = particleeffectnum( "decal_shot" ); DECAL_GLASS = particleeffectnum( "decal_glass" ); + PARTICLE_SMOKEGRENADE = particleeffectnum( "smokegren" ); FONT_16 = loadfont( "16", "gfx/conchars_16", "16", -1 ); + + SHADER_CULLED = shaderforname( "mirror_cull" ); Radio_InitSounds(); diff --git a/Source/Client/View.c b/Source/Client/View.c index 8da1ff85..aea8f74a 100755 --- a/Source/Client/View.c +++ b/Source/Client/View.c @@ -162,7 +162,19 @@ void View_DrawViewModel( void ) { makevectors( getproperty( VF_ANGLES ) ); eViewModel.origin = getproperty( VF_ORIGIN ) + '0 0 -1' + ( v_forward * ( fBob * 0.4 ) ); eViewModel.angles = getproperty( VF_ANGLES ); - + + // Left-handed weapons + if ( autocvar_v_lefthanded ) { + v_right *= -1; + eViewModel.renderflags |= RF_USEAXIS; + eViewModel.forceshader = SHADER_CULLED; + } else { + if ( eViewModel.forceshader ) { + eViewModel.forceshader = 0; + eViewModel.renderflags -= RF_USEAXIS; + } + } + // Give the gun a tilt effect like in old HL/CS versions if ( autocvar_cl_bobclassic == 1 ) { eViewModel.angles_z = -fBob; diff --git a/Source/FreeCS-CE.prj b/Source/FreeCS-CE.prj index d167604d..e2da290f 100755 --- a/Source/FreeCS-CE.prj +++ b/Source/FreeCS-CE.prj @@ -113,18 +113,20 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/Source/Globals.h b/Source/Globals.h index 0ec95ed1..bceb11ce 100755 --- a/Source/Globals.h +++ b/Source/Globals.h @@ -222,6 +222,8 @@ enum { EV_IMPACT, EV_EXPLOSION, EV_SPARK, + EV_SMOKE, + EV_FLASH, EV_MODELGIB, EV_CAMERATRIGGER, EV_RADIOMSG, diff --git a/Source/Server/Defs.h b/Source/Server/Defs.h index 3f658bd6..62bb07d1 100755 --- a/Source/Server/Defs.h +++ b/Source/Server/Defs.h @@ -123,6 +123,7 @@ void Spawn_MakeSpectator( void ); void Client_SendEvent( entity eClient, float fEVType ); void Client_TriggerCamera( entity eTarget, vector vPos, vector vEndPos, float fResetTime ); +void Weapon_Draw( float fWeapon ); void Weapon_SwitchBest( void ); void Weapon_UpdateCurrents( void ); void Weapon_DropWeapon( int iSlot ); @@ -141,6 +142,10 @@ int BaseMelee_Attack( void ); float Player_GetMaxSpeed( float fWeapon ); void Effect_Impact( int iType, vector vPos, vector vNormal ); +void Effect_CreateSmoke( vector vPos ); +void Effect_CreateExplosion( vector vPos ); +void Effect_CreateFlash( entity eTarget ); + void TraceAttack_FireBullets( int iShots, vector vPos ); void Damage_Radius( vector vOrigin, entity eAttacker, float fDamage, float fRadius ); void Damage_Apply( entity eTarget, entity eAttacker, int iDamage, vector vHitPos ); @@ -158,9 +163,6 @@ void Animation_PlayerTop( float fFrame ); void Animation_PlayerTopTemp( float fFrame, float fTime ); void Footsteps_Update( void ); -void Effect_CreateExplosion( vector vPos ); - -// WIP string __fullspawndata; hashtable hashMaterials; diff --git a/Source/Shared/Effects.c b/Source/Shared/Effects.c index 0f4b65ea..dbce1e55 100755 --- a/Source/Shared/Effects.c +++ b/Source/Shared/Effects.c @@ -56,7 +56,6 @@ void Effect_CreateExplosion( vector vPos ) { void Effect_CreateSpark( vector vPos, vector vAngle ) { #ifdef SSQC - vPos_z += 48; WriteByte( MSG_MULTICAST, SVC_CGAMEPACKET ); WriteByte( MSG_MULTICAST, EV_SPARK ); WriteCoord( MSG_MULTICAST, vPos_x ); @@ -73,6 +72,44 @@ void Effect_CreateSpark( vector vPos, vector vAngle ) { #endif } +#ifdef SSQC +void Effect_CreateFlash( entity eTarget ) { + WriteByte( MSG_MULTICAST, SVC_CGAMEPACKET ); + WriteByte( MSG_MULTICAST, EV_FLASH ); + msg_entity = eTarget; + multicast( '0 0 0', MULTICAST_ONE ); +} +#endif + +void Effect_CreateSmoke( vector vPos ) { +#ifdef SSQC + WriteByte( MSG_MULTICAST, SVC_CGAMEPACKET ); + WriteByte( MSG_MULTICAST, EV_SMOKE ); + WriteCoord( MSG_MULTICAST, vPos_x ); + WriteCoord( MSG_MULTICAST, vPos_y ); + WriteCoord( MSG_MULTICAST, vPos_z ); + msg_entity = self; + multicast( '0 0 0', MULTICAST_ALL ); +#else + static void Effect_CreateSmoke_Think( void ) { + if ( self.frame <= 0 ) { + remove( self ); + return; + } + + pointparticles( PARTICLE_SMOKEGRENADE, self.origin, '0 0 0', 1 ); + self.frame--; + self.nextthink = time + 0.2f; + } + + entity eSmoke = spawn(); + setorigin( eSmoke, vPos ); + eSmoke.think = Effect_CreateSmoke_Think; + eSmoke.nextthink = time; + eSmoke.frame = 200; +#endif +} + #ifdef CSQC .float framerate; void Effect_AnimatedSprite( vector vPos, float fIndex, float fFPS, float fScale, float fAlpha, float fEffects ) { diff --git a/Source/Shared/Equipment.c b/Source/Shared/Equipment.c index e9aa8497..79f964b1 100755 --- a/Source/Shared/Equipment.c +++ b/Source/Shared/Equipment.c @@ -38,18 +38,21 @@ void CSEv_PlayerBuyEquipment_f( float fID ) { if ( eqptTable[ fID ].iID == WEAPON_HEGRENADE ) { if ( self.iAmmo_HEGRENADE < 2 ) { self.iAmmo_HEGRENADE++; + Money_AddMoney( self, -300 ); } else { centerprint( self, "You can't carry any more!" ); } } else if ( eqptTable[ fID ].iID == WEAPON_FLASHBANG ) { if ( self.iAmmo_FLASHBANG < 2 ) { self.iAmmo_FLASHBANG++; + Money_AddMoney( self, -300 ); } else { centerprint( self, "You can't carry any more!" ); } } else if ( eqptTable[ fID ].iID == WEAPON_SMOKEGRENADE ) { if ( self.iAmmo_SMOKEGRENADE < 2 ) { self.iAmmo_SMOKEGRENADE++; + Money_AddMoney( self, -300 ); } else { centerprint( self, "You can't carry any more!" ); } @@ -95,9 +98,6 @@ void CSEv_PlayerBuyEquipment_f( float fID ) { self.fAttackFinished = time + 1.0; return; } - - Money_AddMoney( self, -eqptTable[ fID ].iPrice ); - self.iEquipment = self.iEquipment | ( eqptTable[ fID ].iID ); } else { centerprint( self, "You have insufficient funds!" ); } diff --git a/Source/Shared/WeaponFlashbang.c b/Source/Shared/WeaponFlashbang.c index 076a7a79..567827f9 100755 --- a/Source/Shared/WeaponFlashbang.c +++ b/Source/Shared/WeaponFlashbang.c @@ -95,6 +95,22 @@ void WeaponFLASHBANG_PrimaryFire( void ) { #ifdef SSQC void WeaponFLASHBANG_Throw( void ) { static void WeaponFLASHBANG_Explode( void ) { + vector vNorm; + float fDot; + + for ( entity eFind = world; ( eFind = find( eFind, classname, "player" ) ); ) { + traceline( self.origin + '0 0 32', eFind.origin, FALSE, self ); + if ( trace_fraction == 1 ) { + makevectors ( eFind.angles ); + vNorm = normalize ( self.origin - eFind.origin ); + fDot = vNorm * v_forward; + + if ( fDot > 0.3 ) { + Effect_CreateFlash( eFind ); + } + } + } + if ( random() < 0.5 ) { sound( self, CHAN_WEAPON, "weapons/flashbang-1.wav", 1, ATTN_NORM ); } else { @@ -131,6 +147,8 @@ void WeaponFLASHBANG_Throw( void ) { if ( !self.iAmmo_FLASHBANG ) { Weapon_SwitchBest(); + } else { + Weapon_Draw( WEAPON_FLASHBANG ); } } #endif diff --git a/Source/Shared/WeaponHEGrenade.c b/Source/Shared/WeaponHEGrenade.c index 10de93ea..54d93a99 100755 --- a/Source/Shared/WeaponHEGrenade.c +++ b/Source/Shared/WeaponHEGrenade.c @@ -128,6 +128,8 @@ void WeaponHEGRENADE_Throw( void ) { if ( !self.iAmmo_HEGRENADE ) { Weapon_SwitchBest(); + } else { + Weapon_Draw( WEAPON_HEGRENADE ); } } #endif diff --git a/Source/Shared/WeaponSmokeGrenade.c b/Source/Shared/WeaponSmokeGrenade.c index 0a55976b..d555d728 100755 --- a/Source/Shared/WeaponSmokeGrenade.c +++ b/Source/Shared/WeaponSmokeGrenade.c @@ -94,9 +94,18 @@ void WeaponSMOKEGRENADE_PrimaryFire( void ) { #ifdef SSQC void WeaponSMOKEGRENADE_Throw( void ) { - static void WeaponSMOKEGRENADE_Explode( void ) { + static void WeaponSMOKEGRENADE_Die( void ) { remove( self ); } + static void WeaponSMOKEGRENADE_Explode( void ) { + Effect_CreateSmoke( self.origin ); + + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_NONE; + + self.think = WeaponSMOKEGRENADE_Die; + self.nextthink = time + 5.0f; + } static void Weapon_SMOKEGRENADE_Touch( void ) { if ( other.classname == "func_breakable" ) { Damage_Apply( other, self, 10, self.origin ); @@ -118,7 +127,6 @@ void WeaponSMOKEGRENADE_Throw( void ) { eNade.avelocity = ( v_forward * 600 ); eNade.movetype = MOVETYPE_BOUNCE; eNade.touch = Weapon_SMOKEGRENADE_Touch; - eNade.think = WeaponSMOKEGRENADE_Explode; eNade.nextthink = time + 3.0f; @@ -126,6 +134,8 @@ void WeaponSMOKEGRENADE_Throw( void ) { if ( !self.iAmmo_SMOKEGRENADE ) { Weapon_SwitchBest(); + } else { + Weapon_Draw( WEAPON_SMOKEGRENADE ); } } #endif diff --git a/Source/Shared/Weapons.c b/Source/Shared/Weapons.c index e41abe96..c11da815 100755 --- a/Source/Shared/Weapons.c +++ b/Source/Shared/Weapons.c @@ -86,7 +86,7 @@ weaponfunc_t wpnFuncTable[ CS_WEAPON_COUNT ] = { { WeaponC4BOMB_Draw, WeaponC4BOMB_PrimaryFire, Temp_Nothing, Temp_Nothing }, { WeaponFLASHBANG_Draw, WeaponFLASHBANG_PrimaryFire, Temp_Nothing, Temp_Nothing }, { WeaponHEGRENADE_Draw, WeaponHEGRENADE_PrimaryFire, Temp_Nothing, Temp_Nothing }, - { WeaponFLASHBANG_Draw, WeaponFLASHBANG_PrimaryFire, Temp_Nothing, Temp_Nothing } + { WeaponSMOKEGRENADE_Draw, WeaponSMOKEGRENADE_PrimaryFire, Temp_Nothing, Temp_Nothing } }; /* diff --git a/freecs/csprogs.dat b/freecs/csprogs.dat index 612d4fe2..cc0876b9 100644 Binary files a/freecs/csprogs.dat and b/freecs/csprogs.dat differ diff --git a/freecs/menu.dat b/freecs/menu.dat index 9712cac2..25832c29 100755 Binary files a/freecs/menu.dat and b/freecs/menu.dat differ diff --git a/freecs/particles/default.cfg b/freecs/particles/default.cfg index 1c169d4c..bdb0b69d 100755 --- a/freecs/particles/default.cfg +++ b/freecs/particles/default.cfg @@ -184,3 +184,18 @@ r_part te_explosion blend add assoc expgib } + +r_part smokegren +{ + texture ball + count 1 + scale 512 + scalefactor 1 + die 3 + alpha 0.7 + rgb 128 128 128 + spawnmode ball + gravity -25 + veladd -20 + randomvel 64 -64 +} \ No newline at end of file diff --git a/freecs/progs.dat b/freecs/progs.dat index 8e7e3cff..e61a9eb6 100644 Binary files a/freecs/progs.dat and b/freecs/progs.dat differ diff --git a/freecs/scripts/view.shader b/freecs/scripts/view.shader new file mode 100755 index 00000000..4af154c5 --- /dev/null +++ b/freecs/scripts/view.shader @@ -0,0 +1,7 @@ +mirror_cull { + cull back + program defaultskin + { + map $diffuse + } +} \ No newline at end of file