/* Copyright (c) 1999, Cold Ice Modification. This code has been written by SlimShady ( darcuri@optonline.net ) Use, distribution, and modification of this source code and/or resulting object code is restricted to non-commercial enhancements to products from Valve LLC. All other use, distribution, or modification is prohibited without written permission from Valve LLC and from the Cold Ice team. Please if you use this code in any public form, please give us credit. */ #include "extdll.h" #include "util.h" #include "cbase.h" #include "monsters.h" #include "weapons.h" #include "nodes.h" #include "player.h" #include "gamerules.h" #define SWORD_BODYHIT_VOLUME 128 #define SWORD_WALLHIT_VOLUME 512 class CSword : public CBasePlayerWeapon { public: void Spawn( void ); void Precache( void ); int iItemSlot( void ) { return 1; } void EXPORT SwingAgain( void ); void EXPORT Smack( void ); int GetItemInfo(ItemInfo *p); int AddToPlayer( CBasePlayer *pPlayer ); int menu_on ; void PrimaryAttack( void ); int Swing( int fFirst ); BOOL Deploy( void ); void Holster( void ); int m_iSwing; TraceResult m_trHit; }; LINK_ENTITY_TO_CLASS( weapon_sword, CSword ); enum sword_e { SWORD_IDLE = 0, SWORD_DRAW, SWORD_HOLSTER, SWORD_ATTACK1HIT, SWORD_ATTACK1MISS, SWORD_ATTACK2MISS, SWORD_ATTACK2HIT, SWORD_ATTACK3MISS, SWORD_ATTACK3HIT }; void CSword::Spawn( ) { Precache( ); m_iId = WEAPON_SWORD; SET_MODEL(ENT(pev), "models/wmodels/w_sword.mdl"); m_iClip = -1; FallInit(); } void CSword::Precache( void ) { PRECACHE_MODEL("models/vmodels/v_sword.mdl"); PRECACHE_MODEL("models/wmodels/w_sword.mdl"); PRECACHE_MODEL("models/pmodels/p_sword.mdl"); } int CSword::GetItemInfo(ItemInfo *p) { p->pszName = STRING(pev->classname); p->pszAmmo1 = NULL; p->iMaxAmmo1 = -1; p->pszAmmo2 = NULL; p->iMaxAmmo2 = -1; p->iMaxClip = WEAPON_NOCLIP; p->iSlot = 0; p->iPosition = 2; p->iId = WEAPON_SWORD; p->iWeight = SWORD_WEIGHT; return 1; } int CSword::AddToPlayer( CBasePlayer *pPlayer ) { if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) ) { MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev ); WRITE_BYTE( m_iId ); MESSAGE_END(); return TRUE; } return FALSE; } BOOL CSword::Deploy( ) { return DefaultDeploy( "models/vmodels/v_sword.mdl", "models/pmodels/p_sword.mdl", SWORD_DRAW, "crowbar" ); } void CSword::Holster( ) { m_pPlayer->m_flNextAttack = gpGlobals->time + 0.5; SendWeaponAnim( SWORD_HOLSTER ); } void FindHullIntersectionSword( const Vector &vecSrc, TraceResult &tr, float *mins, float *maxs, edict_t *pEntity ) { int i, j, k; float distance; float *minmaxs[2] = {mins, maxs}; TraceResult tmpTrace; Vector vecHullEnd = tr.vecEndPos; Vector vecEnd; distance = 1e6f; vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2); UTIL_TraceLine( vecSrc, vecHullEnd, dont_ignore_monsters, pEntity, &tmpTrace ); if ( tmpTrace.flFraction < 1.0 ) { tr = tmpTrace; return; } for ( i = 0; i < 2; i++ ) { for ( j = 0; j < 2; j++ ) { for ( k = 0; k < 2; k++ ) { vecEnd.x = vecHullEnd.x + minmaxs[i][0]; vecEnd.y = vecHullEnd.y + minmaxs[j][1]; vecEnd.z = vecHullEnd.z + minmaxs[k][2]; UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, pEntity, &tmpTrace ); if ( tmpTrace.flFraction < 1.0 ) { float thisDistance = (tmpTrace.vecEndPos - vecSrc).Length(); if ( thisDistance < distance ) { tr = tmpTrace; distance = thisDistance; } } } } } } void CSword::PrimaryAttack() { if (! Swing( 1 )) { SetThink( &CSword::SwingAgain ); pev->nextthink = gpGlobals->time + 0.1; } } void CSword::Smack( ) { DecalGunshot( &m_trHit, BULLET_PLAYER_CROWBAR ); } void CSword::SwingAgain( void ) { Swing( 0 ); } int CSword::Swing( int fFirst ) { int fDidHit = FALSE; TraceResult tr; UTIL_MakeVectors (m_pPlayer->pev->v_angle); Vector vecSrc = m_pPlayer->GetGunPosition( ); Vector vecEnd = vecSrc + gpGlobals->v_forward * 32; UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, ENT( m_pPlayer->pev ), &tr ); if ( tr.flFraction >= 1.0 ) { UTIL_TraceHull( vecSrc, vecEnd, dont_ignore_monsters, head_hull, ENT( m_pPlayer->pev ), &tr ); if ( tr.flFraction < 1.0 ) { // Calculate the point of intersection of the line (or hull) and the object we hit // This is and approximation of the "best" intersection CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit ); if ( !pHit || pHit->IsBSPModel() ) FindHullIntersectionSword( vecSrc, tr, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, m_pPlayer->edict() ); vecEnd = tr.vecEndPos; // This is the point on the actual surface (the hull could have hit space) } } if ( tr.flFraction >= 1.0 ) { if (fFirst) { // miss switch( (m_iSwing++) % 3 ) { case 0: SendWeaponAnim( SWORD_ATTACK1MISS ); break; case 1: SendWeaponAnim( SWORD_ATTACK2MISS ); break; case 2: SendWeaponAnim( SWORD_ATTACK3MISS ); break; } m_flNextPrimaryAttack = gpGlobals->time + 0.7; // play wiff or swish sound EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/cbar_miss1.wav", 1, ATTN_NORM, 0, 50 + RANDOM_LONG(0,0xF)); // player "shoot" animation m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); } } else { // hit fDidHit = TRUE; CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit); switch( ((m_iSwing++) % 2) + 1 ) { case 0: SendWeaponAnim( SWORD_ATTACK1HIT ); break; case 1: SendWeaponAnim( SWORD_ATTACK2HIT ); break; case 2: SendWeaponAnim( SWORD_ATTACK3HIT ); break; } // player "shoot" animation m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); ClearMultiDamage( ); if ( (m_flNextPrimaryAttack + 1 < gpGlobals->time) || g_pGameRules->IsMultiplayer() ) { // first swing does full damage pEntity->TraceAttack(m_pPlayer->pev, gSkillData.plrDmgSword, gpGlobals->v_forward, &tr, DMG_CLUB ); } else { // subsequent swings do half pEntity->TraceAttack(m_pPlayer->pev, gSkillData.plrDmgSword / 2, gpGlobals->v_forward, &tr, DMG_CLUB ); } ApplyMultiDamage( m_pPlayer->pev, m_pPlayer->pev ); m_flNextPrimaryAttack = gpGlobals->time + 0.7; // play thwack, smack, or dong sound float flVol = 1.0; int fHitWorld = TRUE; if (pEntity) { if (pEntity->Classify() != CLASS_NONE && pEntity->Classify() != CLASS_MACHINE) { // play thwack or smack sound switch( RANDOM_LONG(0,2) ) { case 0: EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/cbar_hitbod1.wav", 1, ATTN_NORM); break; case 1: EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/cbar_hitbod2.wav", 1, ATTN_NORM); break; case 2: EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/cbar_hitbod3.wav", 1, ATTN_NORM); break; } m_pPlayer->m_iWeaponVolume = SWORD_BODYHIT_VOLUME; if (!pEntity->IsAlive() ) return TRUE; else flVol = 0.1; fHitWorld = FALSE; } } // play texture hit sound // UNDONE: Calculate the correct point of intersection when we hit with the hull instead of the line if (fHitWorld) { float fvolbar = TEXTURETYPE_PlaySound(&tr, vecSrc, vecSrc + (vecEnd-vecSrc)*2, BULLET_PLAYER_CROWBAR); if ( g_pGameRules->IsMultiplayer() ) { // override the volume here, cause we don't play texture sounds in multiplayer, // and fvolbar is going to be 0 from the above call. fvolbar = 1; } // also play crowbar strike switch( RANDOM_LONG(0,1) ) { case 0: //UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, "weapons/cbar_hit1.wav", fvolbar, ATTN_NORM, 0, 98 + RANDOM_LONG(0,3)); EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/cbar_hit1.wav", fvolbar, ATTN_NORM, 0, 98 + RANDOM_LONG(0,3)); break; case 1: //UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, "weapons/cbar_hit2.wav", fvolbar, ATTN_NORM, 0, 98 + RANDOM_LONG(0,3)); EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/cbar_hit2.wav", fvolbar, ATTN_NORM, 0, 98 + RANDOM_LONG(0,3)); break; } } // delay the decal a bit m_trHit = tr; SetThink( &CSword::Smack ); pev->nextthink = gpGlobals->time + 0.2; m_pPlayer->m_iWeaponVolume = flVol * SWORD_WALLHIT_VOLUME; } return fDidHit; }