mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
4470 lines
102 KiB
C++
4470 lines
102 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
Copyright (C) 2016-2017 Dustin Land
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#pragma hdrstop
|
|
#include "precompiled.h"
|
|
#include "Game_local.h"
|
|
#include "PredictedValue_impl.h"
|
|
|
|
/***********************************************************************
|
|
|
|
rvmWeaponObject
|
|
|
|
***********************************************************************/
|
|
|
|
const idEventDef EV_Weapon_Grabber_SetGrabDistance("grabberGrabDistance", "f");
|
|
|
|
CLASS_DECLARATION(idClass, rvmWeaponObject)
|
|
EVENT(EV_Weapon_Grabber_SetGrabDistance, idWeapon::Event_GrabberSetGrabDistance)
|
|
END_CLASS
|
|
|
|
/*
|
|
==================
|
|
rvmWeaponObject::Init
|
|
==================
|
|
*/
|
|
void rvmWeaponObject::Init(idWeapon* weapon)
|
|
{
|
|
next_attack = 0.0f;
|
|
owner = weapon;
|
|
stateThread.SetOwner(this);
|
|
owner->Event_WeaponRising();
|
|
|
|
idClass::Init();
|
|
}
|
|
|
|
/*
|
|
==================
|
|
rvmWeaponObject::IsFiring
|
|
==================
|
|
*/
|
|
bool rvmWeaponObject::IsFiring()
|
|
{
|
|
if (IsStateRunning("Fire"))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (next_attack >= gameLocal.realClientTime)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
rvmWeaponObject::IsReloading
|
|
==================
|
|
*/
|
|
bool rvmWeaponObject::IsReloading()
|
|
{
|
|
if (IsStateRunning("Reload"))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
rvmWeaponObject::FindSound
|
|
==================
|
|
*/
|
|
const idSoundShader* rvmWeaponObject::FindSound(const char* name)
|
|
{
|
|
const char* soundName = owner->GetKey(name);
|
|
if (soundName == NULL || soundName[0] == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
return declManager->FindSound(soundName);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
idWeapon
|
|
|
|
***********************************************************************/
|
|
|
|
//
|
|
// class def
|
|
//
|
|
CLASS_DECLARATION(idAnimatedEntity, idWeapon)
|
|
END_CLASS
|
|
|
|
|
|
idCVar cg_projectile_clientAuthoritative_maxCatchup("cg_projectile_clientAuthoritative_maxCatchup", "500", CVAR_INTEGER, "");
|
|
|
|
idCVar g_useWeaponDepthHack("g_useWeaponDepthHack", "1", CVAR_BOOL, "Crunch z depth on weapons");
|
|
|
|
idCVar g_weaponShadows("g_weaponShadows", "0", CVAR_BOOL | CVAR_ARCHIVE, "Cast shadows from weapons");
|
|
|
|
extern idCVar cg_predictedSpawn_debug;
|
|
|
|
/***********************************************************************
|
|
|
|
init
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::idWeapon()
|
|
================
|
|
*/
|
|
idWeapon::idWeapon()
|
|
{
|
|
owner = NULL;
|
|
worldModel = NULL;
|
|
weaponDef = NULL;
|
|
|
|
isFiring = false;
|
|
isLinked = false;
|
|
currentWeaponObject = nullptr;
|
|
isFlashLight = false;
|
|
OutOfAmmo = false;
|
|
|
|
memset(&guiLight, 0, sizeof(guiLight));
|
|
memset(&muzzleFlash, 0, sizeof(muzzleFlash));
|
|
memset(&worldMuzzleFlash, 0, sizeof(worldMuzzleFlash));
|
|
memset(&nozzleGlow, 0, sizeof(nozzleGlow));
|
|
|
|
muzzleFlashEnd = 0;
|
|
flashColor = vec3_origin;
|
|
muzzleFlashHandle = -1;
|
|
worldMuzzleFlashHandle = -1;
|
|
guiLightHandle = -1;
|
|
nozzleGlowHandle = -1;
|
|
modelDefHandle = -1;
|
|
grabberState = -1;
|
|
|
|
berserk = 2;
|
|
brassDelay = 0;
|
|
|
|
allowDrop = true;
|
|
isPlayerFlashlight = false;
|
|
|
|
fraccos = 0.0f;
|
|
fraccos2 = 0.0f;
|
|
|
|
Clear();
|
|
|
|
fl.networkSync = true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::~idWeapon()
|
|
================
|
|
*/
|
|
idWeapon::~idWeapon()
|
|
{
|
|
Clear();
|
|
delete worldModel.GetEntity();
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idWeapon::Spawn
|
|
================
|
|
*/
|
|
void idWeapon::Spawn()
|
|
{
|
|
if (!gameLocal.isClient)
|
|
{
|
|
// setup the world model
|
|
worldModel = static_cast<idAnimatedEntity*>(gameLocal.SpawnEntityType(idAnimatedEntity::Type, NULL));
|
|
worldModel.GetEntity()->fl.networkSync = true;
|
|
}
|
|
|
|
if (1 /*!common->IsMultiplayer()*/)
|
|
{
|
|
//grabber.Initialize();
|
|
}
|
|
|
|
//thread = new idThread();
|
|
//thread->ManualDelete();
|
|
//thread->ManualControl();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::SetOwner
|
|
|
|
Only called at player spawn time, not each weapon switch
|
|
================
|
|
*/
|
|
void idWeapon::SetOwner(idPlayer* _owner)
|
|
{
|
|
assert(!owner);
|
|
owner = _owner;
|
|
SetName(va("%s_weapon", owner->name.c_str()));
|
|
|
|
if (worldModel.GetEntity())
|
|
{
|
|
worldModel.GetEntity()->SetName(va("%s_weapon_worldmodel", owner->name.c_str()));
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::SetFlashlightOwner
|
|
|
|
Only called at player spawn time, not each weapon switch
|
|
================
|
|
*/
|
|
void idWeapon::SetFlashlightOwner(idPlayer* _owner)
|
|
{
|
|
assert(!owner);
|
|
owner = _owner;
|
|
SetName(va("%s_weapon_flashlight", owner->name.c_str()));
|
|
|
|
isFlashLight = true;
|
|
|
|
if (worldModel.GetEntity())
|
|
{
|
|
worldModel.GetEntity()->SetName(va("%s_weapon_flashlight_worldmodel", owner->name.c_str()));
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ShouldConstructScriptObjectAtSpawn
|
|
|
|
Called during idEntity::Spawn to see if it should construct the script object or not.
|
|
Overridden by subclasses that need to spawn the script object themselves.
|
|
================
|
|
*/
|
|
bool idWeapon::ShouldConstructScriptObjectAtSpawn() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::CacheWeapon
|
|
================
|
|
*/
|
|
void idWeapon::CacheWeapon(const char* weaponName)
|
|
{
|
|
const idDeclEntityDef* weaponDef;
|
|
const char* brassDefName;
|
|
const char* clipModelName;
|
|
idTraceModel trm;
|
|
const char* guiName;
|
|
|
|
weaponDef = gameLocal.FindEntityDef(weaponName, false);
|
|
if (!weaponDef)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// precache the brass collision model
|
|
brassDefName = weaponDef->dict.GetString("def_ejectBrass");
|
|
if (brassDefName[0])
|
|
{
|
|
const idDeclEntityDef* brassDef = gameLocal.FindEntityDef(brassDefName, false);
|
|
if (brassDef)
|
|
{
|
|
brassDef->dict.GetString("clipmodel", "", &clipModelName);
|
|
if (!clipModelName[0])
|
|
{
|
|
clipModelName = brassDef->dict.GetString("model"); // use the visual model
|
|
}
|
|
// load the trace model
|
|
collisionModelManager->TrmFromModel(clipModelName, trm);
|
|
}
|
|
}
|
|
|
|
guiName = weaponDef->dict.GetString("gui");
|
|
if (guiName[0])
|
|
{
|
|
uiManager->FindGui(guiName, true, false, true);
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Save
|
|
================
|
|
*/
|
|
void idWeapon::Save(idSaveGame* savefile) const
|
|
{
|
|
// jmarshall
|
|
//savefile->WriteInt( status );
|
|
//savefile->WriteObject( thread );
|
|
//savefile->WriteString( state );
|
|
//savefile->WriteString( idealState );
|
|
// jmarshall end
|
|
|
|
savefile->WriteInt(animBlendFrames);
|
|
savefile->WriteInt(animDoneTime);
|
|
//savefile->WriteBool( isLinked );
|
|
|
|
savefile->WriteObject(owner);
|
|
worldModel.Save(savefile);
|
|
|
|
savefile->WriteInt(hideTime);
|
|
savefile->WriteFloat(hideDistance);
|
|
savefile->WriteInt(hideStartTime);
|
|
savefile->WriteFloat(hideStart);
|
|
savefile->WriteFloat(hideEnd);
|
|
savefile->WriteFloat(hideOffset);
|
|
savefile->WriteBool(hide);
|
|
savefile->WriteBool(disabled);
|
|
|
|
savefile->WriteInt(berserk);
|
|
|
|
savefile->WriteVec3(playerViewOrigin);
|
|
savefile->WriteMat3(playerViewAxis);
|
|
|
|
savefile->WriteVec3(viewWeaponOrigin);
|
|
savefile->WriteMat3(viewWeaponAxis);
|
|
|
|
savefile->WriteVec3(muzzleOrigin);
|
|
savefile->WriteMat3(muzzleAxis);
|
|
|
|
savefile->WriteVec3(pushVelocity);
|
|
|
|
savefile->WriteString(weaponDef->GetName());
|
|
savefile->WriteFloat(meleeDistance);
|
|
savefile->WriteString(meleeDefName);
|
|
savefile->WriteInt(brassDelay);
|
|
savefile->WriteString(icon);
|
|
savefile->WriteString(pdaIcon);
|
|
savefile->WriteString(displayName);
|
|
savefile->WriteString(itemDesc);
|
|
|
|
savefile->WriteInt(guiLightHandle);
|
|
savefile->WriteRenderLight(guiLight);
|
|
|
|
savefile->WriteInt(muzzleFlashHandle);
|
|
savefile->WriteRenderLight(muzzleFlash);
|
|
|
|
savefile->WriteInt(worldMuzzleFlashHandle);
|
|
savefile->WriteRenderLight(worldMuzzleFlash);
|
|
|
|
savefile->WriteVec3(flashColor);
|
|
savefile->WriteInt(muzzleFlashEnd);
|
|
savefile->WriteInt(flashTime);
|
|
|
|
savefile->WriteBool(lightOn);
|
|
savefile->WriteBool(silent_fire);
|
|
|
|
savefile->WriteInt(kick_endtime);
|
|
savefile->WriteInt(muzzle_kick_time);
|
|
savefile->WriteInt(muzzle_kick_maxtime);
|
|
savefile->WriteAngles(muzzle_kick_angles);
|
|
savefile->WriteVec3(muzzle_kick_offset);
|
|
|
|
savefile->WriteInt(ammoType);
|
|
savefile->WriteInt(ammoRequired);
|
|
savefile->WriteInt(clipSize);
|
|
savefile->WriteInt(ammoClip.Get());
|
|
savefile->WriteInt(lowAmmo);
|
|
savefile->WriteBool(powerAmmo);
|
|
|
|
// savegames <= 17
|
|
savefile->WriteInt(0);
|
|
|
|
savefile->WriteInt(zoomFov);
|
|
|
|
savefile->WriteJoint(barrelJointView);
|
|
savefile->WriteJoint(flashJointView);
|
|
savefile->WriteJoint(ejectJointView);
|
|
savefile->WriteJoint(guiLightJointView);
|
|
savefile->WriteJoint(ventLightJointView);
|
|
|
|
savefile->WriteJoint(flashJointWorld);
|
|
savefile->WriteJoint(barrelJointWorld);
|
|
savefile->WriteJoint(ejectJointWorld);
|
|
|
|
savefile->WriteBool(hasBloodSplat);
|
|
|
|
savefile->WriteSoundShader(sndHum);
|
|
|
|
savefile->WriteParticle(weaponSmoke);
|
|
savefile->WriteInt(weaponSmokeStartTime);
|
|
savefile->WriteBool(continuousSmoke);
|
|
savefile->WriteParticle(strikeSmoke);
|
|
savefile->WriteInt(strikeSmokeStartTime);
|
|
savefile->WriteVec3(strikePos);
|
|
savefile->WriteMat3(strikeAxis);
|
|
savefile->WriteInt(nextStrikeFx);
|
|
|
|
savefile->WriteBool(nozzleFx);
|
|
savefile->WriteInt(nozzleFxFade);
|
|
|
|
savefile->WriteInt(lastAttack);
|
|
|
|
savefile->WriteInt(nozzleGlowHandle);
|
|
savefile->WriteRenderLight(nozzleGlow);
|
|
|
|
savefile->WriteVec3(nozzleGlowColor);
|
|
savefile->WriteMaterial(nozzleGlowShader);
|
|
savefile->WriteFloat(nozzleGlowRadius);
|
|
|
|
savefile->WriteInt(weaponAngleOffsetAverages);
|
|
savefile->WriteFloat(weaponAngleOffsetScale);
|
|
savefile->WriteFloat(weaponAngleOffsetMax);
|
|
savefile->WriteFloat(weaponOffsetTime);
|
|
savefile->WriteFloat(weaponOffsetScale);
|
|
|
|
savefile->WriteBool(allowDrop);
|
|
savefile->WriteObject(projectileEnt);
|
|
|
|
//savefile->WriteStaticObject(grabber);
|
|
savefile->WriteInt(grabberState);
|
|
|
|
savefile->WriteJoint(smokeJointView);
|
|
|
|
savefile->WriteInt(weaponParticles.Num());
|
|
for (int i = 0; i < weaponParticles.Num(); i++)
|
|
{
|
|
WeaponParticle_t* part = weaponParticles.GetIndex(i);
|
|
savefile->WriteString(part->name);
|
|
savefile->WriteString(part->particlename);
|
|
savefile->WriteBool(part->active);
|
|
savefile->WriteInt(part->startTime);
|
|
savefile->WriteJoint(part->joint);
|
|
savefile->WriteBool(part->smoke);
|
|
if (!part->smoke)
|
|
{
|
|
savefile->WriteObject(part->emitter);
|
|
}
|
|
}
|
|
savefile->WriteInt(weaponLights.Num());
|
|
for (int i = 0; i < weaponLights.Num(); i++)
|
|
{
|
|
WeaponLight_t* light = weaponLights.GetIndex(i);
|
|
savefile->WriteString(light->name);
|
|
savefile->WriteBool(light->active);
|
|
savefile->WriteInt(light->startTime);
|
|
savefile->WriteJoint(light->joint);
|
|
savefile->WriteInt(light->lightHandle);
|
|
savefile->WriteRenderLight(light->light);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Restore
|
|
================
|
|
*/
|
|
void idWeapon::Restore(idRestoreGame* savefile)
|
|
{
|
|
// jmarshall
|
|
//savefile->ReadInt( (int &)status );
|
|
//savefile->ReadObject( reinterpret_cast<idClass *&>( thread ) );
|
|
//savefile->ReadString( state );
|
|
//savefile->ReadString( idealState );
|
|
// jmarshall end
|
|
|
|
savefile->ReadInt(animBlendFrames);
|
|
savefile->ReadInt(animDoneTime);
|
|
//savefile->ReadBool( isLinked );
|
|
|
|
savefile->ReadObject(reinterpret_cast<idClass*&>(owner));
|
|
worldModel.Restore(savefile);
|
|
|
|
savefile->ReadInt(hideTime);
|
|
savefile->ReadFloat(hideDistance);
|
|
savefile->ReadInt(hideStartTime);
|
|
savefile->ReadFloat(hideStart);
|
|
savefile->ReadFloat(hideEnd);
|
|
savefile->ReadFloat(hideOffset);
|
|
savefile->ReadBool(hide);
|
|
savefile->ReadBool(disabled);
|
|
|
|
savefile->ReadInt(berserk);
|
|
|
|
savefile->ReadVec3(playerViewOrigin);
|
|
savefile->ReadMat3(playerViewAxis);
|
|
|
|
savefile->ReadVec3(viewWeaponOrigin);
|
|
savefile->ReadMat3(viewWeaponAxis);
|
|
|
|
savefile->ReadVec3(muzzleOrigin);
|
|
savefile->ReadMat3(muzzleAxis);
|
|
|
|
savefile->ReadVec3(pushVelocity);
|
|
|
|
idStr objectname;
|
|
savefile->ReadString(objectname);
|
|
weaponDef = gameLocal.FindEntityDef(objectname);
|
|
meleeDef = gameLocal.FindEntityDef(weaponDef->dict.GetString("def_melee"), false);
|
|
|
|
const idDeclEntityDef* projectileDef = gameLocal.FindEntityDef(weaponDef->dict.GetString("def_projectile"), false);
|
|
if (projectileDef)
|
|
{
|
|
projectileDict = projectileDef->dict;
|
|
}
|
|
else
|
|
{
|
|
projectileDict.Clear();
|
|
}
|
|
|
|
const idDeclEntityDef* brassDef = gameLocal.FindEntityDef(weaponDef->dict.GetString("def_ejectBrass"), false);
|
|
if (brassDef)
|
|
{
|
|
brassDict = brassDef->dict;
|
|
}
|
|
else
|
|
{
|
|
brassDict.Clear();
|
|
}
|
|
|
|
savefile->ReadFloat(meleeDistance);
|
|
savefile->ReadString(meleeDefName);
|
|
savefile->ReadInt(brassDelay);
|
|
savefile->ReadString(icon);
|
|
savefile->ReadString(pdaIcon);
|
|
savefile->ReadString(displayName);
|
|
savefile->ReadString(itemDesc);
|
|
|
|
savefile->ReadInt(guiLightHandle);
|
|
savefile->ReadRenderLight(guiLight);
|
|
if (guiLightHandle >= 0)
|
|
{
|
|
guiLightHandle = gameRenderWorld->AddLightDef(&guiLight);
|
|
}
|
|
|
|
savefile->ReadInt(muzzleFlashHandle);
|
|
savefile->ReadRenderLight(muzzleFlash);
|
|
if (muzzleFlashHandle >= 0)
|
|
{
|
|
muzzleFlashHandle = gameRenderWorld->AddLightDef(&muzzleFlash);
|
|
}
|
|
|
|
savefile->ReadInt(worldMuzzleFlashHandle);
|
|
savefile->ReadRenderLight(worldMuzzleFlash);
|
|
if (worldMuzzleFlashHandle >= 0)
|
|
{
|
|
worldMuzzleFlashHandle = gameRenderWorld->AddLightDef(&worldMuzzleFlash);
|
|
}
|
|
|
|
savefile->ReadVec3(flashColor);
|
|
savefile->ReadInt(muzzleFlashEnd);
|
|
savefile->ReadInt(flashTime);
|
|
|
|
savefile->ReadBool(lightOn);
|
|
savefile->ReadBool(silent_fire);
|
|
|
|
savefile->ReadInt(kick_endtime);
|
|
savefile->ReadInt(muzzle_kick_time);
|
|
savefile->ReadInt(muzzle_kick_maxtime);
|
|
savefile->ReadAngles(muzzle_kick_angles);
|
|
savefile->ReadVec3(muzzle_kick_offset);
|
|
|
|
savefile->ReadInt((int&)ammoType);
|
|
savefile->ReadInt(ammoRequired);
|
|
savefile->ReadInt(clipSize);
|
|
|
|
int savedAmmoClip = 0;
|
|
savefile->ReadInt(savedAmmoClip);
|
|
ammoClip = savedAmmoClip;
|
|
|
|
savefile->ReadInt(lowAmmo);
|
|
savefile->ReadBool(powerAmmo);
|
|
|
|
// savegame versions <= 17
|
|
int foo;
|
|
savefile->ReadInt(foo);
|
|
|
|
savefile->ReadInt(zoomFov);
|
|
|
|
savefile->ReadJoint(barrelJointView);
|
|
savefile->ReadJoint(flashJointView);
|
|
savefile->ReadJoint(ejectJointView);
|
|
savefile->ReadJoint(guiLightJointView);
|
|
savefile->ReadJoint(ventLightJointView);
|
|
|
|
savefile->ReadJoint(flashJointWorld);
|
|
savefile->ReadJoint(barrelJointWorld);
|
|
savefile->ReadJoint(ejectJointWorld);
|
|
|
|
savefile->ReadBool(hasBloodSplat);
|
|
|
|
savefile->ReadSoundShader(sndHum);
|
|
|
|
savefile->ReadParticle(weaponSmoke);
|
|
savefile->ReadInt(weaponSmokeStartTime);
|
|
savefile->ReadBool(continuousSmoke);
|
|
savefile->ReadParticle(strikeSmoke);
|
|
savefile->ReadInt(strikeSmokeStartTime);
|
|
savefile->ReadVec3(strikePos);
|
|
savefile->ReadMat3(strikeAxis);
|
|
savefile->ReadInt(nextStrikeFx);
|
|
|
|
savefile->ReadBool(nozzleFx);
|
|
savefile->ReadInt(nozzleFxFade);
|
|
|
|
savefile->ReadInt(lastAttack);
|
|
|
|
savefile->ReadInt(nozzleGlowHandle);
|
|
savefile->ReadRenderLight(nozzleGlow);
|
|
if (nozzleGlowHandle >= 0)
|
|
{
|
|
nozzleGlowHandle = gameRenderWorld->AddLightDef(&nozzleGlow);
|
|
}
|
|
|
|
savefile->ReadVec3(nozzleGlowColor);
|
|
savefile->ReadMaterial(nozzleGlowShader);
|
|
savefile->ReadFloat(nozzleGlowRadius);
|
|
|
|
savefile->ReadInt(weaponAngleOffsetAverages);
|
|
savefile->ReadFloat(weaponAngleOffsetScale);
|
|
savefile->ReadFloat(weaponAngleOffsetMax);
|
|
savefile->ReadFloat(weaponOffsetTime);
|
|
savefile->ReadFloat(weaponOffsetScale);
|
|
|
|
savefile->ReadBool(allowDrop);
|
|
savefile->ReadObject(reinterpret_cast<idClass*&>(projectileEnt));
|
|
|
|
//savefile->ReadStaticObject(grabber);
|
|
savefile->ReadInt(grabberState);
|
|
|
|
savefile->ReadJoint(smokeJointView);
|
|
|
|
int particleCount;
|
|
savefile->ReadInt(particleCount);
|
|
for (int i = 0; i < particleCount; i++)
|
|
{
|
|
WeaponParticle_t newParticle;
|
|
memset(&newParticle, 0, sizeof(newParticle));
|
|
|
|
idStr name, particlename;
|
|
savefile->ReadString(name);
|
|
savefile->ReadString(particlename);
|
|
|
|
strcpy(newParticle.name, name.c_str());
|
|
strcpy(newParticle.particlename, particlename.c_str());
|
|
|
|
savefile->ReadBool(newParticle.active);
|
|
savefile->ReadInt(newParticle.startTime);
|
|
savefile->ReadJoint(newParticle.joint);
|
|
savefile->ReadBool(newParticle.smoke);
|
|
if (newParticle.smoke)
|
|
{
|
|
newParticle.particle = static_cast<const idDeclParticle*>(declManager->FindType(DECL_PARTICLE, particlename, false));
|
|
}
|
|
else
|
|
{
|
|
savefile->ReadObject(reinterpret_cast<idClass*&>(newParticle.emitter));
|
|
}
|
|
|
|
weaponParticles.Set(newParticle.name, newParticle);
|
|
}
|
|
|
|
int lightCount;
|
|
savefile->ReadInt(lightCount);
|
|
for (int i = 0; i < lightCount; i++)
|
|
{
|
|
WeaponLight_t newLight;
|
|
memset(&newLight, 0, sizeof(newLight));
|
|
|
|
idStr name;
|
|
savefile->ReadString(name);
|
|
strcpy(newLight.name, name.c_str());
|
|
|
|
savefile->ReadBool(newLight.active);
|
|
savefile->ReadInt(newLight.startTime);
|
|
savefile->ReadJoint(newLight.joint);
|
|
savefile->ReadInt(newLight.lightHandle);
|
|
savefile->ReadRenderLight(newLight.light);
|
|
if (newLight.lightHandle >= 0)
|
|
{
|
|
newLight.lightHandle = gameRenderWorld->AddLightDef(&newLight.light);
|
|
}
|
|
weaponLights.Set(newLight.name, newLight);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Weapon definition management
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::Clear
|
|
================
|
|
*/
|
|
void idWeapon::Clear()
|
|
{
|
|
DeconstructScriptObject();
|
|
scriptObject.Free();
|
|
|
|
if (muzzleFlashHandle != -1)
|
|
{
|
|
gameRenderWorld->FreeLightDef(muzzleFlashHandle);
|
|
muzzleFlashHandle = -1;
|
|
}
|
|
if (muzzleFlashHandle != -1)
|
|
{
|
|
gameRenderWorld->FreeLightDef(muzzleFlashHandle);
|
|
muzzleFlashHandle = -1;
|
|
}
|
|
if (worldMuzzleFlashHandle != -1)
|
|
{
|
|
gameRenderWorld->FreeLightDef(worldMuzzleFlashHandle);
|
|
worldMuzzleFlashHandle = -1;
|
|
}
|
|
if (guiLightHandle != -1)
|
|
{
|
|
gameRenderWorld->FreeLightDef(guiLightHandle);
|
|
guiLightHandle = -1;
|
|
}
|
|
if (nozzleGlowHandle != -1)
|
|
{
|
|
gameRenderWorld->FreeLightDef(nozzleGlowHandle);
|
|
nozzleGlowHandle = -1;
|
|
}
|
|
|
|
memset(&renderEntity, 0, sizeof(renderEntity));
|
|
renderEntity.entityNum = entityNumber;
|
|
|
|
renderEntity.noShadow = true;
|
|
renderEntity.noSelfShadow = true;
|
|
renderEntity.customSkin = NULL;
|
|
|
|
// set default shader parms
|
|
renderEntity.shaderParms[SHADERPARM_RED] = 1.0f;
|
|
renderEntity.shaderParms[SHADERPARM_GREEN] = 1.0f;
|
|
renderEntity.shaderParms[SHADERPARM_BLUE] = 1.0f;
|
|
renderEntity.shaderParms[3] = 1.0f;
|
|
renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = 0.0f;
|
|
renderEntity.shaderParms[5] = 0.0f;
|
|
renderEntity.shaderParms[6] = 0.0f;
|
|
renderEntity.shaderParms[7] = 0.0f;
|
|
|
|
if (refSound.referenceSound)
|
|
{
|
|
refSound.referenceSound->Free(true);
|
|
}
|
|
memset(&refSound, 0, sizeof(refSound_t));
|
|
|
|
// setting diversity to 0 results in no random sound. -1 indicates random.
|
|
refSound.diversity = -1.0f;
|
|
|
|
if (owner)
|
|
{
|
|
// don't spatialize the weapon sounds
|
|
refSound.listenerId = owner->GetListenerId();
|
|
}
|
|
|
|
// clear out the sounds from our spawnargs since we'll copy them from the weapon def
|
|
const idKeyValue* kv = spawnArgs.MatchPrefix("snd_");
|
|
while (kv)
|
|
{
|
|
spawnArgs.Delete(kv->GetKey());
|
|
kv = spawnArgs.MatchPrefix("snd_");
|
|
}
|
|
|
|
hideTime = 300;
|
|
hideDistance = -15.0f;
|
|
hideStartTime = gameLocal.time - hideTime;
|
|
hideStart = 0.0f;
|
|
hideEnd = 0.0f;
|
|
hideOffset = 0.0f;
|
|
hide = false;
|
|
disabled = false;
|
|
|
|
weaponSmoke = NULL;
|
|
weaponSmokeStartTime = 0;
|
|
continuousSmoke = false;
|
|
strikeSmoke = NULL;
|
|
strikeSmokeStartTime = 0;
|
|
strikePos.Zero();
|
|
strikeAxis = mat3_identity;
|
|
nextStrikeFx = 0;
|
|
|
|
icon = "";
|
|
pdaIcon = "";
|
|
displayName = "";
|
|
itemDesc = "";
|
|
|
|
playerViewAxis.Identity();
|
|
playerViewOrigin.Zero();
|
|
viewWeaponAxis.Identity();
|
|
viewWeaponOrigin.Zero();
|
|
muzzleAxis.Identity();
|
|
muzzleOrigin.Zero();
|
|
pushVelocity.Zero();
|
|
|
|
animBlendFrames = 0;
|
|
animDoneTime = 0;
|
|
|
|
projectileDict.Clear();
|
|
meleeDef = NULL;
|
|
meleeDefName = "";
|
|
meleeDistance = 0.0f;
|
|
brassDict.Clear();
|
|
|
|
if (currentWeaponObject != nullptr)
|
|
{
|
|
delete currentWeaponObject;
|
|
currentWeaponObject = nullptr;
|
|
}
|
|
|
|
flashTime = 250;
|
|
lightOn = false;
|
|
silent_fire = false;
|
|
|
|
grabberState = -1;
|
|
//grabber.Update(owner, true);
|
|
|
|
ammoType = 0;
|
|
ammoRequired = 0;
|
|
ammoClip = 0;
|
|
clipSize = 0;
|
|
lowAmmo = 0;
|
|
powerAmmo = false;
|
|
|
|
kick_endtime = 0;
|
|
muzzle_kick_time = 0;
|
|
muzzle_kick_maxtime = 0;
|
|
muzzle_kick_angles.Zero();
|
|
muzzle_kick_offset.Zero();
|
|
|
|
zoomFov = 90;
|
|
|
|
barrelJointView = INVALID_JOINT;
|
|
flashJointView = INVALID_JOINT;
|
|
ejectJointView = INVALID_JOINT;
|
|
guiLightJointView = INVALID_JOINT;
|
|
ventLightJointView = INVALID_JOINT;
|
|
|
|
barrelJointWorld = INVALID_JOINT;
|
|
flashJointWorld = INVALID_JOINT;
|
|
ejectJointWorld = INVALID_JOINT;
|
|
|
|
smokeJointView = INVALID_JOINT;
|
|
|
|
//Clean up the weapon particles
|
|
for (int i = 0; i < weaponParticles.Num(); i++)
|
|
{
|
|
WeaponParticle_t* part = weaponParticles.GetIndex(i);
|
|
if (!part->smoke)
|
|
{
|
|
if (part->emitter != NULL)
|
|
{
|
|
//Destroy the emitters
|
|
part->emitter->PostEventMS(&EV_Remove, 0);
|
|
}
|
|
}
|
|
}
|
|
weaponParticles.Clear();
|
|
|
|
//Clean up the weapon lights
|
|
for (int i = 0; i < weaponLights.Num(); i++)
|
|
{
|
|
WeaponLight_t* light = weaponLights.GetIndex(i);
|
|
if (light->lightHandle != -1)
|
|
{
|
|
gameRenderWorld->FreeLightDef(light->lightHandle);
|
|
}
|
|
}
|
|
weaponLights.Clear();
|
|
|
|
hasBloodSplat = false;
|
|
nozzleFx = false;
|
|
nozzleFxFade = 1500;
|
|
lastAttack = 0;
|
|
nozzleGlowHandle = -1;
|
|
nozzleGlowShader = NULL;
|
|
nozzleGlowRadius = 10;
|
|
nozzleGlowColor.Zero();
|
|
|
|
weaponAngleOffsetAverages = 0;
|
|
weaponAngleOffsetScale = 0.0f;
|
|
weaponAngleOffsetMax = 0.0f;
|
|
weaponOffsetTime = 0.0f;
|
|
weaponOffsetScale = 0.0f;
|
|
|
|
allowDrop = true;
|
|
|
|
animator.ClearAllAnims(gameLocal.time, 0);
|
|
FreeModelDef();
|
|
|
|
sndHum = NULL;
|
|
|
|
//isLinked = false;
|
|
projectileEnt = NULL;
|
|
|
|
isFiring = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::InitWorldModel
|
|
================
|
|
*/
|
|
void idWeapon::InitWorldModel(const idDeclEntityDef* def)
|
|
{
|
|
idEntity* ent;
|
|
|
|
ent = worldModel.GetEntity();
|
|
|
|
assert(ent);
|
|
assert(def);
|
|
|
|
const char* model = def->dict.GetString("model_world");
|
|
const char* attach = def->dict.GetString("joint_attach");
|
|
|
|
ent->SetSkin(NULL);
|
|
if (model[0] && attach[0])
|
|
{
|
|
ent->Show();
|
|
ent->SetModel(model);
|
|
if (ent->GetAnimator()->ModelDef())
|
|
{
|
|
ent->SetSkin(ent->GetAnimator()->ModelDef()->GetDefaultSkin());
|
|
}
|
|
ent->GetPhysics()->SetContents(0);
|
|
ent->GetPhysics()->SetClipModel(NULL, 1.0f);
|
|
ent->BindToJoint(owner, attach, true);
|
|
ent->GetPhysics()->SetOrigin(vec3_origin);
|
|
ent->GetPhysics()->SetAxis(mat3_identity);
|
|
|
|
// We don't want to interpolate the world model of weapons, let them
|
|
// just bind normally to the player's joint and be driven by the player's
|
|
// animation so that the weapon and the player don't appear out of sync.
|
|
//ent->SetUseClientInterpolation( false );
|
|
|
|
// supress model in player views, but allow it in mirrors and remote views
|
|
renderEntity_t* worldModelRenderEntity = ent->GetRenderEntity();
|
|
if (worldModelRenderEntity)
|
|
{
|
|
worldModelRenderEntity->suppressSurfaceInViewID = owner->entityNumber + 1;
|
|
worldModelRenderEntity->suppressShadowInViewID = owner->entityNumber + 1;
|
|
worldModelRenderEntity->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ent->SetModel("");
|
|
ent->Hide();
|
|
}
|
|
|
|
flashJointWorld = ent->GetAnimator()->GetJointHandle("flash");
|
|
barrelJointWorld = ent->GetAnimator()->GetJointHandle("muzzle");
|
|
ejectJointWorld = ent->GetAnimator()->GetJointHandle("eject");
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetWeaponDef
|
|
================
|
|
*/
|
|
void idWeapon::GetWeaponDef(const char* objectname, int ammoinclip)
|
|
{
|
|
const char* shader;
|
|
const char* objectType;
|
|
const char* vmodel;
|
|
const char* guiName;
|
|
const char* projectileName;
|
|
const char* brassDefName;
|
|
const char* smokeName;
|
|
int ammoAvail;
|
|
|
|
Clear();
|
|
|
|
if (!objectname || !objectname[0])
|
|
{
|
|
return;
|
|
}
|
|
|
|
assert(owner);
|
|
|
|
weaponDef = gameLocal.FindEntityDef(objectname);
|
|
|
|
ammoType = GetAmmoNumForName(weaponDef->dict.GetString("ammoType"));
|
|
ammoRequired = weaponDef->dict.GetInt("ammoRequired");
|
|
clipSize = weaponDef->dict.GetInt("clipSize");
|
|
lowAmmo = weaponDef->dict.GetInt("lowAmmo");
|
|
|
|
icon = weaponDef->dict.GetString("icon");
|
|
pdaIcon = weaponDef->dict.GetString("pdaIcon");
|
|
displayName = weaponDef->dict.GetString("display_name");
|
|
itemDesc = weaponDef->dict.GetString("inv_desc");
|
|
|
|
silent_fire = weaponDef->dict.GetBool("silent_fire");
|
|
powerAmmo = weaponDef->dict.GetBool("powerAmmo");
|
|
|
|
muzzle_kick_time = SEC2MS(weaponDef->dict.GetFloat("muzzle_kick_time"));
|
|
muzzle_kick_maxtime = SEC2MS(weaponDef->dict.GetFloat("muzzle_kick_maxtime"));
|
|
muzzle_kick_angles = weaponDef->dict.GetAngles("muzzle_kick_angles");
|
|
muzzle_kick_offset = weaponDef->dict.GetVector("muzzle_kick_offset");
|
|
|
|
hideTime = SEC2MS(weaponDef->dict.GetFloat("hide_time", "0.3"));
|
|
hideDistance = weaponDef->dict.GetFloat("hide_distance", "-15");
|
|
|
|
// muzzle smoke
|
|
smokeName = weaponDef->dict.GetString("smoke_muzzle");
|
|
if (*smokeName != '\0')
|
|
{
|
|
weaponSmoke = static_cast<const idDeclParticle*>(declManager->FindType(DECL_PARTICLE, smokeName));
|
|
}
|
|
else
|
|
{
|
|
weaponSmoke = NULL;
|
|
}
|
|
continuousSmoke = weaponDef->dict.GetBool("continuousSmoke");
|
|
weaponSmokeStartTime = (continuousSmoke) ? gameLocal.time : 0;
|
|
|
|
smokeName = weaponDef->dict.GetString("smoke_strike");
|
|
if (*smokeName != '\0')
|
|
{
|
|
strikeSmoke = static_cast<const idDeclParticle*>(declManager->FindType(DECL_PARTICLE, smokeName));
|
|
}
|
|
else
|
|
{
|
|
strikeSmoke = NULL;
|
|
}
|
|
strikeSmokeStartTime = 0;
|
|
strikePos.Zero();
|
|
strikeAxis = mat3_identity;
|
|
nextStrikeFx = 0;
|
|
|
|
// setup gui light
|
|
memset(&guiLight, 0, sizeof(guiLight));
|
|
const char* guiLightShader = weaponDef->dict.GetString("mtr_guiLightShader");
|
|
if (*guiLightShader != '\0')
|
|
{
|
|
guiLight.shader = declManager->FindMaterial(guiLightShader, false);
|
|
guiLight.lightRadius[0] = guiLight.lightRadius[1] = guiLight.lightRadius[2] = 3;
|
|
guiLight.pointLight = true;
|
|
}
|
|
|
|
// setup the view model
|
|
vmodel = weaponDef->dict.GetString("model_view");
|
|
SetModel(vmodel);
|
|
|
|
// setup the world model
|
|
InitWorldModel(weaponDef);
|
|
|
|
// copy the sounds from the weapon view model def into out spawnargs
|
|
const idKeyValue* kv = weaponDef->dict.MatchPrefix("snd_");
|
|
while (kv)
|
|
{
|
|
spawnArgs.Set(kv->GetKey(), kv->GetValue());
|
|
kv = weaponDef->dict.MatchPrefix("snd_", kv);
|
|
}
|
|
|
|
// find some joints in the model for locating effects
|
|
barrelJointView = animator.GetJointHandle("barrel");
|
|
flashJointView = animator.GetJointHandle("flash");
|
|
ejectJointView = animator.GetJointHandle("eject");
|
|
guiLightJointView = animator.GetJointHandle("guiLight");
|
|
ventLightJointView = animator.GetJointHandle("ventLight");
|
|
|
|
idStr smokeJoint = weaponDef->dict.GetString("smoke_joint");
|
|
if (smokeJoint.Length() > 0)
|
|
{
|
|
smokeJointView = animator.GetJointHandle(smokeJoint);
|
|
}
|
|
else
|
|
{
|
|
smokeJointView = INVALID_JOINT;
|
|
}
|
|
|
|
// get the projectile
|
|
projectileDict.Clear();
|
|
|
|
projectileName = weaponDef->dict.GetString("def_projectile");
|
|
if (projectileName[0] != '\0')
|
|
{
|
|
const idDeclEntityDef* projectileDef = gameLocal.FindEntityDef(projectileName, false);
|
|
if (!projectileDef)
|
|
{
|
|
gameLocal.Warning("Unknown projectile '%s' in weapon '%s'", projectileName, objectname);
|
|
}
|
|
else
|
|
{
|
|
const char* spawnclass = projectileDef->dict.GetString("spawnclass");
|
|
idTypeInfo* cls = idClass::GetClass(spawnclass);
|
|
if (!cls || !cls->IsType(idProjectile::Type))
|
|
{
|
|
gameLocal.Warning("Invalid spawnclass '%s' on projectile '%s' (used by weapon '%s')", spawnclass, projectileName, objectname);
|
|
}
|
|
else
|
|
{
|
|
projectileDict = projectileDef->dict;
|
|
}
|
|
}
|
|
}
|
|
|
|
// set up muzzleflash render light
|
|
const idMaterial* flashShader;
|
|
idVec3 flashTarget;
|
|
idVec3 flashUp;
|
|
idVec3 flashRight;
|
|
float flashRadius;
|
|
bool flashPointLight;
|
|
|
|
weaponDef->dict.GetString("mtr_flashShader", "", &shader);
|
|
flashShader = declManager->FindMaterial(shader, false);
|
|
flashPointLight = weaponDef->dict.GetBool("flashPointLight", "1");
|
|
weaponDef->dict.GetVector("flashColor", "0 0 0", flashColor);
|
|
flashRadius = (float)weaponDef->dict.GetInt("flashRadius"); // if 0, no light will spawn
|
|
flashTime = SEC2MS(weaponDef->dict.GetFloat("flashTime", "0.25"));
|
|
flashTarget = weaponDef->dict.GetVector("flashTarget");
|
|
flashUp = weaponDef->dict.GetVector("flashUp");
|
|
flashRight = weaponDef->dict.GetVector("flashRight");
|
|
|
|
memset(&muzzleFlash, 0, sizeof(muzzleFlash));
|
|
muzzleFlash.lightId = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
|
|
muzzleFlash.allowLightInViewID = owner->entityNumber + 1;
|
|
|
|
// the weapon lights will only be in first person
|
|
guiLight.allowLightInViewID = owner->entityNumber + 1;
|
|
nozzleGlow.allowLightInViewID = owner->entityNumber + 1;
|
|
|
|
muzzleFlash.pointLight = flashPointLight;
|
|
muzzleFlash.shader = flashShader;
|
|
muzzleFlash.shaderParms[SHADERPARM_RED] = flashColor[0];
|
|
muzzleFlash.shaderParms[SHADERPARM_GREEN] = flashColor[1];
|
|
muzzleFlash.shaderParms[SHADERPARM_BLUE] = flashColor[2];
|
|
muzzleFlash.shaderParms[SHADERPARM_TIMESCALE] = 1.0f;
|
|
|
|
muzzleFlash.lightRadius[0] = flashRadius;
|
|
muzzleFlash.lightRadius[1] = flashRadius;
|
|
muzzleFlash.lightRadius[2] = flashRadius;
|
|
|
|
if (!flashPointLight)
|
|
{
|
|
muzzleFlash.target = flashTarget;
|
|
muzzleFlash.up = flashUp;
|
|
muzzleFlash.right = flashRight;
|
|
muzzleFlash.end = flashTarget;
|
|
}
|
|
|
|
// the world muzzle flash is the same, just positioned differently
|
|
worldMuzzleFlash = muzzleFlash;
|
|
worldMuzzleFlash.suppressLightInViewID = owner->entityNumber + 1;
|
|
worldMuzzleFlash.allowLightInViewID = 0;
|
|
worldMuzzleFlash.lightId = LIGHTID_WORLD_MUZZLE_FLASH + owner->entityNumber;
|
|
|
|
//-----------------------------------
|
|
|
|
nozzleFx = weaponDef->dict.GetBool("nozzleFx");
|
|
nozzleFxFade = weaponDef->dict.GetInt("nozzleFxFade", "1500");
|
|
nozzleGlowColor = weaponDef->dict.GetVector("nozzleGlowColor", "1 1 1");
|
|
nozzleGlowRadius = weaponDef->dict.GetFloat("nozzleGlowRadius", "10");
|
|
weaponDef->dict.GetString("mtr_nozzleGlowShader", "", &shader);
|
|
nozzleGlowShader = declManager->FindMaterial(shader, false);
|
|
|
|
// get the melee damage def
|
|
meleeDistance = weaponDef->dict.GetFloat("melee_distance");
|
|
meleeDefName = weaponDef->dict.GetString("def_melee");
|
|
if (meleeDefName.Length())
|
|
{
|
|
meleeDef = gameLocal.FindEntityDef(meleeDefName, false);
|
|
if (!meleeDef)
|
|
{
|
|
gameLocal.Error("Unknown melee '%s'", meleeDefName.c_str());
|
|
}
|
|
}
|
|
|
|
// get the brass def
|
|
brassDict.Clear();
|
|
brassDelay = weaponDef->dict.GetInt("ejectBrassDelay", "0");
|
|
brassDefName = weaponDef->dict.GetString("def_ejectBrass");
|
|
|
|
if (brassDefName[0])
|
|
{
|
|
const idDeclEntityDef* brassDef = gameLocal.FindEntityDef(brassDefName, false);
|
|
if (!brassDef)
|
|
{
|
|
gameLocal.Warning("Unknown brass '%s'", brassDefName);
|
|
}
|
|
else
|
|
{
|
|
brassDict = brassDef->dict;
|
|
}
|
|
}
|
|
|
|
if ((ammoType < 0) || (ammoType >= AMMO_NUMTYPES))
|
|
{
|
|
gameLocal.Warning("Unknown ammotype in object '%s'", objectname);
|
|
}
|
|
|
|
ammoClip = ammoinclip;
|
|
if ((ammoClip.Get() < 0) || (ammoClip.Get() > clipSize))
|
|
{
|
|
// first time using this weapon so have it fully loaded to start
|
|
ammoClip = clipSize;
|
|
ammoAvail = owner->inventory.HasAmmo(ammoType, ammoRequired);
|
|
if (ammoClip.Get() > ammoAvail)
|
|
{
|
|
ammoClip = ammoAvail;
|
|
}
|
|
//In D3XP we use ammo as soon as it is moved into the clip. This allows for weapons that share ammo
|
|
owner->inventory.UseAmmo(ammoType, ammoClip.Get());
|
|
}
|
|
|
|
renderEntity.gui[0] = NULL;
|
|
guiName = weaponDef->dict.GetString("gui");
|
|
if (guiName[0])
|
|
{
|
|
renderEntity.gui[0] = uiManager->FindGui(guiName, true, false, true);
|
|
}
|
|
|
|
zoomFov = weaponDef->dict.GetInt("zoomFov", "70");
|
|
berserk = weaponDef->dict.GetInt("berserk", "2");
|
|
|
|
weaponAngleOffsetAverages = weaponDef->dict.GetInt("weaponAngleOffsetAverages", "10");
|
|
weaponAngleOffsetScale = weaponDef->dict.GetFloat("weaponAngleOffsetScale", "0.25");
|
|
weaponAngleOffsetMax = weaponDef->dict.GetFloat("weaponAngleOffsetMax", "10");
|
|
|
|
weaponOffsetTime = weaponDef->dict.GetFloat("weaponOffsetTime", "400");
|
|
weaponOffsetScale = weaponDef->dict.GetFloat("weaponOffsetScale", "0.005");
|
|
|
|
if (!weaponDef->dict.GetString("weapon_class", NULL, &objectType))
|
|
{
|
|
gameLocal.Error("No 'weaponclass' set on '%s'.", objectname);
|
|
}
|
|
|
|
// setup script object
|
|
idTypeInfo* typeInfo;
|
|
typeInfo = idClass::GetClass(objectType);
|
|
if (!typeInfo)
|
|
{
|
|
gameLocal.Error("Failed to get weapon class");
|
|
}
|
|
if (currentWeaponObject != nullptr)
|
|
{
|
|
delete currentWeaponObject;
|
|
currentWeaponObject = nullptr;
|
|
}
|
|
currentWeaponObject = static_cast<rvmWeaponObject*>(typeInfo->CreateInstance());
|
|
currentWeaponObject->Init(this);
|
|
currentWeaponObject->CallSpawn();
|
|
|
|
spawnArgs = weaponDef->dict;
|
|
|
|
shader = spawnArgs.GetString("snd_hum");
|
|
if (shader && *shader)
|
|
{
|
|
sndHum = declManager->FindSound(shader);
|
|
StartSoundShader(sndHum, SND_CHANNEL_BODY, 0, false, NULL);
|
|
}
|
|
|
|
isLinked = true;
|
|
|
|
//isLinked = true;
|
|
|
|
// call script object's constructor
|
|
//ConstructScriptObject();
|
|
|
|
// make sure we have the correct skin
|
|
UpdateSkin();
|
|
|
|
//Initialize the particles
|
|
if (!gameLocal.isMultiplayer)
|
|
{
|
|
|
|
const idKeyValue* pkv = weaponDef->dict.MatchPrefix("weapon_particle", NULL);
|
|
while (pkv)
|
|
{
|
|
WeaponParticle_t newParticle;
|
|
memset(&newParticle, 0, sizeof(newParticle));
|
|
|
|
idStr name = pkv->GetValue();
|
|
|
|
strcpy(newParticle.name, name.c_str());
|
|
|
|
idStr jointName = weaponDef->dict.GetString(va("%s_joint", name.c_str()));
|
|
newParticle.joint = animator.GetJointHandle(jointName.c_str());
|
|
newParticle.smoke = weaponDef->dict.GetBool(va("%s_smoke", name.c_str()));
|
|
newParticle.active = false;
|
|
newParticle.startTime = 0;
|
|
|
|
idStr particle = weaponDef->dict.GetString(va("%s_particle", name.c_str()));
|
|
strcpy(newParticle.particlename, particle.c_str());
|
|
|
|
if (newParticle.smoke)
|
|
{
|
|
newParticle.particle = static_cast<const idDeclParticle*>(declManager->FindType(DECL_PARTICLE, particle, false));
|
|
}
|
|
else
|
|
{
|
|
idDict args;
|
|
|
|
const idDeclEntityDef* emitterDef = gameLocal.FindEntityDef("func_emitter", false);
|
|
args = emitterDef->dict;
|
|
args.Set("model", particle.c_str());
|
|
args.SetBool("start_off", true);
|
|
|
|
idEntity* ent;
|
|
gameLocal.SpawnEntityDef(args, &ent, false);
|
|
newParticle.emitter = (idFuncEmitter*)ent;
|
|
|
|
if (newParticle.emitter != NULL)
|
|
{
|
|
newParticle.emitter->BecomeActive(TH_THINK);
|
|
}
|
|
}
|
|
|
|
weaponParticles.Set(name.c_str(), newParticle);
|
|
|
|
pkv = weaponDef->dict.MatchPrefix("weapon_particle", pkv);
|
|
}
|
|
|
|
const idKeyValue* lkv = weaponDef->dict.MatchPrefix("weapon_light", NULL);
|
|
while (lkv)
|
|
{
|
|
WeaponLight_t newLight;
|
|
memset(&newLight, 0, sizeof(newLight));
|
|
|
|
newLight.lightHandle = -1;
|
|
newLight.active = false;
|
|
newLight.startTime = 0;
|
|
|
|
idStr name = lkv->GetValue();
|
|
strcpy(newLight.name, name.c_str());
|
|
|
|
idStr jointName = weaponDef->dict.GetString(va("%s_joint", name.c_str()));
|
|
newLight.joint = animator.GetJointHandle(jointName.c_str());
|
|
|
|
shader = weaponDef->dict.GetString(va("%s_shader", name.c_str()));
|
|
newLight.light.shader = declManager->FindMaterial(shader, false);
|
|
|
|
float radius = weaponDef->dict.GetFloat(va("%s_radius", name.c_str()));
|
|
newLight.light.lightRadius[0] = newLight.light.lightRadius[1] = newLight.light.lightRadius[2] = radius;
|
|
newLight.light.pointLight = true;
|
|
newLight.light.noShadows = true;
|
|
|
|
newLight.light.allowLightInViewID = owner->entityNumber + 1;
|
|
|
|
weaponLights.Set(name.c_str(), newLight);
|
|
|
|
lkv = weaponDef->dict.MatchPrefix("weapon_light", lkv);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
GUIs
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::Icon
|
|
================
|
|
*/
|
|
const char* idWeapon::Icon() const
|
|
{
|
|
return icon;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::PdaIcon
|
|
================
|
|
*/
|
|
const char* idWeapon::PdaIcon() const
|
|
{
|
|
return pdaIcon;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::DisplayName
|
|
================
|
|
*/
|
|
const char* idWeapon::DisplayName() const
|
|
{
|
|
return ""; // idLocalization::GetString(displayName);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Description
|
|
================
|
|
*/
|
|
const char* idWeapon::Description() const
|
|
{
|
|
return "";// idLocalization::GetString(itemDesc);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::UpdateGUI
|
|
================
|
|
*/
|
|
void idWeapon::UpdateGUI()
|
|
{
|
|
if (!renderEntity.gui[0])
|
|
{
|
|
return;
|
|
}
|
|
|
|
//if( status == WP_HOLSTERED )
|
|
//{
|
|
// return;
|
|
//}
|
|
|
|
if (owner->weaponGone)
|
|
{
|
|
// dropping weapons was implemented wierd, so we have to not update the gui when it happens or we'll get a negative ammo count
|
|
return;
|
|
}
|
|
|
|
if (gameLocal.localClientNum != owner->entityNumber) {
|
|
// if updating the hud for a followed client
|
|
if (gameLocal.localClientNum >= 0 && gameLocal.entities[gameLocal.localClientNum] && gameLocal.entities[gameLocal.localClientNum]->IsType(idPlayer::Type)) {
|
|
idPlayer* p = static_cast<idPlayer*>(gameLocal.entities[gameLocal.localClientNum]);
|
|
if (!p->spectating || p->spectator != owner->entityNumber) {
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
int inclip = AmmoInClip();
|
|
int ammoamount = AmmoAvailable();
|
|
|
|
if (ammoamount < 0)
|
|
{
|
|
// show infinite ammo
|
|
renderEntity.gui[0]->SetStateString("player_ammo", "");
|
|
}
|
|
else
|
|
{
|
|
// show remaining ammo
|
|
renderEntity.gui[0]->SetStateString("player_totalammo", va("%i", ammoamount));
|
|
renderEntity.gui[0]->SetStateString("player_ammo", ClipSize() ? va("%i", inclip) : "--");
|
|
renderEntity.gui[0]->SetStateString("player_clips", ClipSize() ? va("%i", ammoamount / ClipSize()) : "--");
|
|
|
|
renderEntity.gui[0]->SetStateString("player_allammo", va("%i/%i", inclip, ammoamount));
|
|
}
|
|
renderEntity.gui[0]->SetStateBool("player_ammo_empty", (ammoamount == 0));
|
|
renderEntity.gui[0]->SetStateBool("player_clip_empty", (inclip == 0));
|
|
renderEntity.gui[0]->SetStateBool("player_clip_low", (inclip <= lowAmmo));
|
|
|
|
//Let the HUD know the total amount of ammo regardless of the ammo required value
|
|
renderEntity.gui[0]->SetStateString("player_ammo_count", va("%i", AmmoCount()));
|
|
|
|
//Grabber Gui Info
|
|
renderEntity.gui[0]->SetStateString("grabber_state", va("%i", grabberState));
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Model and muzzleflash
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::UpdateFlashPosition
|
|
================
|
|
*/
|
|
void idWeapon::UpdateFlashPosition()
|
|
{
|
|
// the flash has an explicit joint for locating it
|
|
GetGlobalJointTransform(true, flashJointView, muzzleFlash.origin, muzzleFlash.axis);
|
|
|
|
// if the desired point is inside or very close to a wall, back it up until it is clear
|
|
idVec3 start = muzzleFlash.origin - playerViewAxis[0] * 16;
|
|
idVec3 end = muzzleFlash.origin + playerViewAxis[0] * 8;
|
|
trace_t tr;
|
|
gameLocal.clip.TracePoint(tr, start, end, MASK_SHOT_RENDERMODEL, owner);
|
|
// be at least 8 units away from a solid
|
|
muzzleFlash.origin = tr.endpos - playerViewAxis[0] * 8;
|
|
|
|
// put the world muzzle flash on the end of the joint, no matter what
|
|
GetGlobalJointTransform(false, flashJointWorld, worldMuzzleFlash.origin, worldMuzzleFlash.axis);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::MuzzleFlashLight
|
|
================
|
|
*/
|
|
void idWeapon::MuzzleFlashLight()
|
|
{
|
|
|
|
if (!lightOn && (!g_muzzleFlash.GetBool() || !muzzleFlash.lightRadius[0]))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (flashJointView == INVALID_JOINT)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UpdateFlashPosition();
|
|
|
|
// these will be different each fire
|
|
muzzleFlash.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC(gameLocal.time);
|
|
muzzleFlash.shaderParms[SHADERPARM_DIVERSITY] = renderEntity.shaderParms[SHADERPARM_DIVERSITY];
|
|
|
|
worldMuzzleFlash.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC(gameLocal.time);
|
|
worldMuzzleFlash.shaderParms[SHADERPARM_DIVERSITY] = renderEntity.shaderParms[SHADERPARM_DIVERSITY];
|
|
|
|
// the light will be removed at this time
|
|
muzzleFlashEnd = gameLocal.time + flashTime;
|
|
|
|
if (muzzleFlashHandle != -1)
|
|
{
|
|
gameRenderWorld->UpdateLightDef(muzzleFlashHandle, &muzzleFlash);
|
|
gameRenderWorld->UpdateLightDef(worldMuzzleFlashHandle, &worldMuzzleFlash);
|
|
}
|
|
else
|
|
{
|
|
muzzleFlashHandle = gameRenderWorld->AddLightDef(&muzzleFlash);
|
|
worldMuzzleFlashHandle = gameRenderWorld->AddLightDef(&worldMuzzleFlash);
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::UpdateSkin
|
|
================
|
|
*/
|
|
bool idWeapon::UpdateSkin()
|
|
{
|
|
//const function_t *func;
|
|
//
|
|
//if ( !isLinked ) {
|
|
// return false;
|
|
//}
|
|
//
|
|
//func = scriptObject.GetFunction( "UpdateSkin" );
|
|
//if ( !func ) {
|
|
// idLib::Warning( "Can't find function 'UpdateSkin' in object '%s'", scriptObject.GetTypeName() );
|
|
// return false;
|
|
//}
|
|
//
|
|
//// use the frameCommandThread since it's safe to use outside of framecommands
|
|
//gameLocal.frameCommandThread->CallFunction( this, func, true );
|
|
//gameLocal.frameCommandThread->Execute();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idWeapon::FlashlightOn
|
|
================
|
|
*/
|
|
void idWeapon::FlashlightOn()
|
|
{
|
|
//const function_t *func;
|
|
//
|
|
//if ( !isLinked ) {
|
|
// return;
|
|
//}
|
|
//
|
|
//func = scriptObject.GetFunction( "TurnOn" );
|
|
//if ( !func ) {
|
|
// idLib::Warning( "Can't find function 'TurnOn' in object '%s'", scriptObject.GetTypeName() );
|
|
// return;
|
|
//}
|
|
//
|
|
//// use the frameCommandThread since it's safe to use outside of framecommands
|
|
//gameLocal.frameCommandThread->CallFunction( this, func, true );
|
|
//gameLocal.frameCommandThread->Execute();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idWeapon::FlashlightOff
|
|
================
|
|
*/
|
|
void idWeapon::FlashlightOff()
|
|
{
|
|
//const function_t *func;
|
|
//
|
|
//if ( !isLinked ) {
|
|
// return;
|
|
//}
|
|
//
|
|
//func = scriptObject.GetFunction( "TurnOff" );
|
|
//if ( !func ) {
|
|
// idLib::Warning( "Can't find function 'TurnOff' in object '%s'", scriptObject.GetTypeName() );
|
|
// return;
|
|
//}
|
|
//
|
|
//// use the frameCommandThread since it's safe to use outside of framecommands
|
|
//gameLocal.frameCommandThread->CallFunction( this, func, true );
|
|
//gameLocal.frameCommandThread->Execute();
|
|
//
|
|
//return;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::SetModel
|
|
================
|
|
*/
|
|
void idWeapon::SetModel(const char* modelname)
|
|
{
|
|
assert(modelname);
|
|
|
|
if (modelDefHandle >= 0)
|
|
{
|
|
gameRenderWorld->RemoveDecals(modelDefHandle);
|
|
}
|
|
|
|
renderEntity.hModel = animator.SetModel(modelname);
|
|
if (renderEntity.hModel)
|
|
{
|
|
renderEntity.customSkin = animator.ModelDef()->GetDefaultSkin();
|
|
animator.GetJoints(&renderEntity.numJoints, &renderEntity.joints);
|
|
}
|
|
else
|
|
{
|
|
renderEntity.customSkin = NULL;
|
|
renderEntity.callback = NULL;
|
|
renderEntity.numJoints = 0;
|
|
renderEntity.joints = NULL;
|
|
}
|
|
|
|
// hide the model until an animation is played
|
|
Hide();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetGlobalJointTransform
|
|
|
|
This returns the offset and axis of a weapon bone in world space, suitable for attaching models or lights
|
|
================
|
|
*/
|
|
bool idWeapon::GetGlobalJointTransform(bool viewModel, const jointHandle_t jointHandle, idVec3& offset, idMat3& axis)
|
|
{
|
|
if (viewModel)
|
|
{
|
|
// view model
|
|
if (animator.GetJointTransform(jointHandle, gameLocal.time, offset, axis))
|
|
{
|
|
offset = offset * viewWeaponAxis + viewWeaponOrigin;
|
|
axis = axis * viewWeaponAxis;
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// world model
|
|
if (worldModel.GetEntity() && worldModel.GetEntity()->GetAnimator()->GetJointTransform(jointHandle, gameLocal.time, offset, axis))
|
|
{
|
|
offset = worldModel.GetEntity()->GetPhysics()->GetOrigin() + offset * worldModel.GetEntity()->GetPhysics()->GetAxis();
|
|
axis = axis * worldModel.GetEntity()->GetPhysics()->GetAxis();
|
|
return true;
|
|
}
|
|
}
|
|
offset = viewWeaponOrigin;
|
|
axis = viewWeaponAxis;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::SetPushVelocity
|
|
================
|
|
*/
|
|
void idWeapon::SetPushVelocity(const idVec3& pushVelocity)
|
|
{
|
|
this->pushVelocity = pushVelocity;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
State control/player interface
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::Think
|
|
================
|
|
*/
|
|
void idWeapon::Think()
|
|
{
|
|
if (isFlashLight)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (currentWeaponObject)
|
|
{
|
|
currentWeaponObject->Execute();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Raise
|
|
================
|
|
*/
|
|
void idWeapon::Raise()
|
|
{
|
|
currentWeaponObject->SetState("Raise");
|
|
currentWeaponObject->AppendState("Idle");
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::PutAway
|
|
================
|
|
*/
|
|
void idWeapon::PutAway()
|
|
{
|
|
hasBloodSplat = false;
|
|
currentWeaponObject->SetState("Lower");
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Reload
|
|
NOTE: this is only for impulse-triggered reload, auto reload is scripted
|
|
================
|
|
*/
|
|
void idWeapon::Reload()
|
|
{
|
|
OutOfAmmo = false;
|
|
|
|
// Reload and go back to idle.
|
|
currentWeaponObject->SetState("Reload");
|
|
currentWeaponObject->AppendState("Idle");
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::LowerWeapon
|
|
================
|
|
*/
|
|
void idWeapon::LowerWeapon()
|
|
{
|
|
if (!hide)
|
|
{
|
|
hideStart = 0.0f;
|
|
hideEnd = hideDistance;
|
|
if (gameLocal.time - hideStartTime < hideTime)
|
|
{
|
|
hideStartTime = gameLocal.time - (hideTime - (gameLocal.time - hideStartTime));
|
|
}
|
|
else
|
|
{
|
|
hideStartTime = gameLocal.time;
|
|
}
|
|
hide = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::RaiseWeapon
|
|
================
|
|
*/
|
|
void idWeapon::RaiseWeapon()
|
|
{
|
|
Show();
|
|
|
|
if (hide)
|
|
{
|
|
hideStart = hideDistance;
|
|
hideEnd = 0.0f;
|
|
if (gameLocal.time - hideStartTime < hideTime)
|
|
{
|
|
hideStartTime = gameLocal.time - (hideTime - (gameLocal.time - hideStartTime));
|
|
}
|
|
else
|
|
{
|
|
hideStartTime = gameLocal.time;
|
|
}
|
|
hide = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::HideWeapon
|
|
================
|
|
*/
|
|
void idWeapon::HideWeapon()
|
|
{
|
|
Hide();
|
|
if (worldModel.GetEntity())
|
|
{
|
|
worldModel.GetEntity()->Hide();
|
|
}
|
|
muzzleFlashEnd = 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ShowWeapon
|
|
================
|
|
*/
|
|
void idWeapon::ShowWeapon()
|
|
{
|
|
Show();
|
|
if (worldModel.GetEntity())
|
|
{
|
|
worldModel.GetEntity()->Show();
|
|
}
|
|
if (lightOn)
|
|
{
|
|
MuzzleFlashLight();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::HideWorldModel
|
|
================
|
|
*/
|
|
void idWeapon::HideWorldModel()
|
|
{
|
|
if (worldModel.GetEntity())
|
|
{
|
|
worldModel.GetEntity()->Hide();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ShowWorldModel
|
|
================
|
|
*/
|
|
void idWeapon::ShowWorldModel()
|
|
{
|
|
if (worldModel.GetEntity())
|
|
{
|
|
worldModel.GetEntity()->Show();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::OwnerDied
|
|
================
|
|
*/
|
|
void idWeapon::OwnerDied()
|
|
{
|
|
currentWeaponObject->OwnerDied();
|
|
|
|
Hide();
|
|
if (worldModel.GetEntity())
|
|
{
|
|
worldModel.GetEntity()->Hide();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::BeginAttack
|
|
================
|
|
*/
|
|
void idWeapon::BeginAttack()
|
|
{
|
|
if (!OutOfAmmo)
|
|
{
|
|
lastAttack = gameLocal.time;
|
|
}
|
|
|
|
if (currentWeaponObject->IsFiring())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (currentWeaponObject->IsReloading())
|
|
{
|
|
return;
|
|
}
|
|
|
|
currentWeaponObject->SetState("Fire");
|
|
currentWeaponObject->AppendState("Idle");
|
|
|
|
isFiring = true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::EndAttack
|
|
================
|
|
*/
|
|
void idWeapon::EndAttack()
|
|
{
|
|
isFiring = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::isReady
|
|
================
|
|
*/
|
|
bool idWeapon::IsReady() const
|
|
{
|
|
return !hide && !IsHidden() && !currentWeaponObject->IsRunning() && !IsHolstered() && !currentWeaponObject->IsStateRunning("Lower");
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::IsReloading
|
|
================
|
|
*/
|
|
bool idWeapon::IsReloading() const
|
|
{
|
|
if (!currentWeaponObject)
|
|
{
|
|
return false;
|
|
}
|
|
return currentWeaponObject->IsStateRunning("Reload");
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::IsHolstered
|
|
================
|
|
*/
|
|
bool idWeapon::IsHolstered() const
|
|
{
|
|
if (!currentWeaponObject)
|
|
{
|
|
return false;
|
|
}
|
|
return currentWeaponObject->IsStateRunning("Holstered");
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ShowCrosshair
|
|
================
|
|
*/
|
|
bool idWeapon::ShowCrosshair() const
|
|
{
|
|
// JDC: this code would never function as written, I'm assuming they wanted the following behavior
|
|
// return !( state == idStr( WP_RISING ) || state == idStr( WP_LOWERING ) || state == idStr( WP_HOLSTERED ) );
|
|
return !currentWeaponObject->IsRunning() || currentWeaponObject->IsStateRunning("Fire");
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idWeapon::CanDrop
|
|
=====================
|
|
*/
|
|
bool idWeapon::CanDrop() const
|
|
{
|
|
if (!weaponDef || !worldModel.GetEntity())
|
|
{
|
|
return false;
|
|
}
|
|
const char* classname = weaponDef->dict.GetString("def_dropItem");
|
|
if (!classname[0])
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::WeaponStolen
|
|
================
|
|
*/
|
|
void idWeapon::WeaponStolen()
|
|
{
|
|
assert(!gameLocal.isClient);
|
|
if (projectileEnt)
|
|
{
|
|
projectileEnt = NULL;
|
|
}
|
|
|
|
// set to holstered so we can switch weapons right away
|
|
//idealState = WP_HOLSTERED;
|
|
|
|
HideWeapon();
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idWeapon::DropItem
|
|
=====================
|
|
*/
|
|
idEntity* idWeapon::DropItem(const idVec3& velocity, int activateDelay, int removeDelay, bool died)
|
|
{
|
|
if (!weaponDef || !worldModel.GetEntity())
|
|
{
|
|
return NULL;
|
|
}
|
|
if (!allowDrop)
|
|
{
|
|
return NULL;
|
|
}
|
|
const char* classname = weaponDef->dict.GetString("def_dropItem");
|
|
if (!classname[0])
|
|
{
|
|
return NULL;
|
|
}
|
|
StopSound(SND_CHANNEL_BODY, true);
|
|
StopSound(SND_CHANNEL_BODY3, true);
|
|
|
|
return idMoveableItem::DropItem(classname, worldModel.GetEntity()->GetPhysics()->GetOrigin(), worldModel.GetEntity()->GetPhysics()->GetAxis(), velocity, activateDelay, removeDelay);
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Script state management
|
|
|
|
***********************************************************************/
|
|
|
|
/***********************************************************************
|
|
|
|
Particles/Effects
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::UpdateNozzelFx
|
|
================
|
|
*/
|
|
void idWeapon::UpdateNozzleFx()
|
|
{
|
|
if (!nozzleFx)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// shader parms
|
|
//
|
|
int la = gameLocal.time - lastAttack + 1;
|
|
float s = 1.0f;
|
|
float l = 0.0f;
|
|
if (la < nozzleFxFade)
|
|
{
|
|
s = ((float)la / nozzleFxFade);
|
|
l = 1.0f - s;
|
|
}
|
|
renderEntity.shaderParms[5] = s;
|
|
renderEntity.shaderParms[6] = l;
|
|
|
|
if (ventLightJointView == INVALID_JOINT)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// vent light
|
|
//
|
|
if (nozzleGlowHandle == -1)
|
|
{
|
|
memset(&nozzleGlow, 0, sizeof(nozzleGlow));
|
|
if (owner)
|
|
{
|
|
nozzleGlow.allowLightInViewID = owner->entityNumber + 1;
|
|
}
|
|
nozzleGlow.pointLight = true;
|
|
nozzleGlow.noShadows = true;
|
|
nozzleGlow.lightRadius.x = nozzleGlowRadius;
|
|
nozzleGlow.lightRadius.y = nozzleGlowRadius;
|
|
nozzleGlow.lightRadius.z = nozzleGlowRadius;
|
|
nozzleGlow.shader = nozzleGlowShader;
|
|
nozzleGlow.shaderParms[SHADERPARM_TIMESCALE] = 1.0f;
|
|
nozzleGlow.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC(gameLocal.time);
|
|
GetGlobalJointTransform(true, ventLightJointView, nozzleGlow.origin, nozzleGlow.axis);
|
|
nozzleGlowHandle = gameRenderWorld->AddLightDef(&nozzleGlow);
|
|
}
|
|
|
|
GetGlobalJointTransform(true, ventLightJointView, nozzleGlow.origin, nozzleGlow.axis);
|
|
|
|
nozzleGlow.shaderParms[SHADERPARM_RED] = nozzleGlowColor.x * s;
|
|
nozzleGlow.shaderParms[SHADERPARM_GREEN] = nozzleGlowColor.y * s;
|
|
nozzleGlow.shaderParms[SHADERPARM_BLUE] = nozzleGlowColor.z * s;
|
|
gameRenderWorld->UpdateLightDef(nozzleGlowHandle, &nozzleGlow);
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idWeapon::BloodSplat
|
|
================
|
|
*/
|
|
bool idWeapon::BloodSplat(float size)
|
|
{
|
|
float s, c;
|
|
idMat3 localAxis, axistemp;
|
|
idVec3 localOrigin, normal;
|
|
|
|
if (hasBloodSplat)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
hasBloodSplat = true;
|
|
|
|
if (modelDefHandle < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!GetGlobalJointTransform(true, ejectJointView, localOrigin, localAxis))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
localOrigin[0] += gameLocal.random.RandomFloat() * -10.0f;
|
|
localOrigin[1] += gameLocal.random.RandomFloat() * 1.0f;
|
|
localOrigin[2] += gameLocal.random.RandomFloat() * -2.0f;
|
|
|
|
normal = idVec3(gameLocal.random.CRandomFloat(), -gameLocal.random.RandomFloat(), -1);
|
|
normal.Normalize();
|
|
|
|
idMath::SinCos16(gameLocal.random.RandomFloat() * idMath::TWO_PI, s, c);
|
|
|
|
localAxis[2] = -normal;
|
|
localAxis[2].NormalVectors(axistemp[0], axistemp[1]);
|
|
localAxis[0] = axistemp[0] * c + axistemp[1] * -s;
|
|
localAxis[1] = axistemp[0] * -s + axistemp[1] * -c;
|
|
|
|
localAxis[0] *= 1.0f / size;
|
|
localAxis[1] *= 1.0f / size;
|
|
|
|
idPlane localPlane[2];
|
|
|
|
localPlane[0] = localAxis[0];
|
|
localPlane[0][3] = -(localOrigin * localAxis[0]) + 0.5f;
|
|
|
|
localPlane[1] = localAxis[1];
|
|
localPlane[1][3] = -(localOrigin * localAxis[1]) + 0.5f;
|
|
|
|
const idMaterial* mtr = declManager->FindMaterial("textures/decals/duffysplatgun");
|
|
|
|
gameRenderWorld->ProjectOverlay(modelDefHandle, localPlane, mtr);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
Visual presentation
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::MuzzleRise
|
|
|
|
The machinegun and chaingun will incrementally back up as they are being fired
|
|
================
|
|
*/
|
|
void idWeapon::MuzzleRise(idVec3& origin, idMat3& axis)
|
|
{
|
|
int time;
|
|
float amount;
|
|
idAngles ang;
|
|
idVec3 offset;
|
|
|
|
time = kick_endtime - gameLocal.time;
|
|
if (time <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (muzzle_kick_maxtime <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (time > muzzle_kick_maxtime)
|
|
{
|
|
time = muzzle_kick_maxtime;
|
|
}
|
|
|
|
amount = (float)time / (float)muzzle_kick_maxtime;
|
|
ang = muzzle_kick_angles * amount;
|
|
offset = muzzle_kick_offset * amount;
|
|
|
|
origin = origin - axis * offset;
|
|
axis = ang.ToMat3() * axis;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::AlertMonsters
|
|
================
|
|
*/
|
|
void idWeapon::AlertMonsters()
|
|
{
|
|
trace_t tr;
|
|
idEntity* ent;
|
|
idVec3 end = muzzleFlash.origin + muzzleFlash.axis * muzzleFlash.target;
|
|
|
|
gameLocal.clip.TracePoint(tr, muzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner);
|
|
if (g_debugWeapon.GetBool())
|
|
{
|
|
gameRenderWorld->DebugLine(colorYellow, muzzleFlash.origin, end, 0);
|
|
gameRenderWorld->DebugArrow(colorGreen, muzzleFlash.origin, tr.endpos, 2, 0);
|
|
}
|
|
|
|
if (tr.fraction < 1.0f)
|
|
{
|
|
ent = gameLocal.GetTraceEntity(tr);
|
|
if (ent->IsType(idAI::Type))
|
|
{
|
|
static_cast<idAI*>(ent)->TouchedByFlashlight(owner);
|
|
}
|
|
else if (ent->IsType(idTrigger::Type))
|
|
{
|
|
ent->Signal(SIG_TOUCH);
|
|
ent->ProcessEvent(&EV_Touch, owner, &tr);
|
|
}
|
|
}
|
|
|
|
// jitter the trace to try to catch cases where a trace down the center doesn't hit the monster
|
|
end += muzzleFlash.axis * muzzleFlash.right * idMath::Sin16(MS2SEC(gameLocal.time) * 31.34f);
|
|
end += muzzleFlash.axis * muzzleFlash.up * idMath::Sin16(MS2SEC(gameLocal.time) * 12.17f);
|
|
gameLocal.clip.TracePoint(tr, muzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner);
|
|
if (g_debugWeapon.GetBool())
|
|
{
|
|
gameRenderWorld->DebugLine(colorYellow, muzzleFlash.origin, end, 0);
|
|
gameRenderWorld->DebugArrow(colorGreen, muzzleFlash.origin, tr.endpos, 2, 0);
|
|
}
|
|
|
|
if (tr.fraction < 1.0f)
|
|
{
|
|
ent = gameLocal.GetTraceEntity(tr);
|
|
if (ent->IsType(idAI::Type))
|
|
{
|
|
static_cast<idAI*>(ent)->TouchedByFlashlight(owner);
|
|
}
|
|
else if (ent->IsType(idTrigger::Type))
|
|
{
|
|
ent->Signal(SIG_TOUCH);
|
|
ent->ProcessEvent(&EV_Touch, owner, &tr);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetMuzzlePositionWithHacks
|
|
|
|
Some weapons that have a barrel joint either have it pointing in the wrong
|
|
direction (rocket launcher), or don't animate it properly (pistol).
|
|
|
|
For good 3D TV / head mounted display work, we need to display a laser sight
|
|
in the world.
|
|
|
|
Fixing the animated meshes would be ideal, but hacking it in code is
|
|
the pragmatic move right now.
|
|
|
|
Returns false for hands, grenades, and chainsaw.
|
|
================
|
|
*/
|
|
bool idWeapon::GetMuzzlePositionWithHacks(idVec3& origin, idMat3& axis)
|
|
{
|
|
// I couldn't find a simple enum to identify the weapons that need
|
|
// workaround hacks...
|
|
const idStr& weaponIconName = pdaIcon;
|
|
|
|
origin = playerViewOrigin;
|
|
axis = playerViewAxis;
|
|
|
|
if (weaponIconName == "guis/assets/hud/icons/grenade_new.tga")
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (weaponIconName == "guis/assets/hud/icons/chainsaw_new.tga")
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (weaponIconName == "guis/assets/hud/icons/soul_cube.tga")
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (barrelJointView != INVALID_JOINT)
|
|
{
|
|
GetGlobalJointTransform(true, barrelJointView, origin, axis);
|
|
}
|
|
else if (guiLightJointView != INVALID_JOINT)
|
|
{
|
|
GetGlobalJointTransform(true, guiLightJointView, origin, axis);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// get better axis joints for weapons where the barrelJointView isn't
|
|
// animated properly
|
|
idVec3 discardedOrigin;
|
|
if (weaponIconName == "guis/assets/hud/icons/pistol_new.tga")
|
|
{
|
|
// muzzle doesn't animate during firing, Bod does
|
|
const jointHandle_t bodJoint = animator.GetJointHandle("Bod");
|
|
GetGlobalJointTransform(true, bodJoint, discardedOrigin, axis);
|
|
}
|
|
if (weaponIconName == "guis/assets/hud/icons/rocketlauncher_new.tga")
|
|
{
|
|
// joint doesn't point straight, so rotate it
|
|
//std::swap( axis[0], axis[2] );
|
|
}
|
|
if (weaponIconName == "guis/assets/hud/icons/shotgun_new.tga")
|
|
{
|
|
// joint doesn't point straight, so rotate it
|
|
const jointHandle_t bodJoint = animator.GetJointHandle("trigger");
|
|
GetGlobalJointTransform(true, bodJoint, discardedOrigin, axis);
|
|
//std::swap( axis[0], axis[2] );
|
|
axis[0] = -axis[0];
|
|
}
|
|
|
|
// we probably should fix the above hacks above that are based on texture names above at some
|
|
// point
|
|
if (weaponDef != NULL)
|
|
{
|
|
if ((idStr::Icmp("weapon_shotgun_double", weaponDef->GetName()) == 0) || (idStr::Icmp("weapon_shotgun_double_mp", weaponDef->GetName()) == 0))
|
|
{
|
|
// joint doesn't point straight, so rotate it
|
|
//std::swap( axis[0], axis[2] );
|
|
}
|
|
else if (idStr::Icmp("weapon_grabber", weaponDef->GetName()) == 0)
|
|
{
|
|
idVec3 forward = axis[0];
|
|
forward.Normalize();
|
|
const float scaleOffset = 4.0f;
|
|
forward *= scaleOffset;
|
|
origin += forward;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::PresentWeapon
|
|
================
|
|
*/
|
|
void idWeapon::PresentWeapon(bool showViewModel)
|
|
{
|
|
playerViewOrigin = owner->firstPersonViewOrigin;
|
|
playerViewAxis = owner->firstPersonViewAxis;
|
|
|
|
if (isPlayerFlashlight)
|
|
{
|
|
viewWeaponOrigin = playerViewOrigin;
|
|
viewWeaponAxis = playerViewAxis;
|
|
|
|
fraccos = cos((gameLocal.framenum & 255) / 127.0f * idMath::PI);
|
|
|
|
static unsigned int divisor = 32;
|
|
unsigned int val = (gameLocal.framenum + gameLocal.framenum / divisor) & 255;
|
|
fraccos2 = cos(val / 127.0f * idMath::PI);
|
|
|
|
static idVec3 baseAdjustPos = idVec3(-8.0f, -20.0f, -10.0f); // rt, fwd, up
|
|
static float pscale = 0.5f;
|
|
static float yscale = 0.125f;
|
|
idVec3 adjustPos = baseAdjustPos;// + ( idVec3( fraccos, 0.0f, fraccos2 ) * scale );
|
|
viewWeaponOrigin += adjustPos.x * viewWeaponAxis[1] + adjustPos.y * viewWeaponAxis[0] + adjustPos.z * viewWeaponAxis[2];
|
|
// viewWeaponOrigin += owner->viewBob;
|
|
|
|
static idAngles baseAdjustAng = idAngles(88.0f, 10.0f, 0.0f); //
|
|
idAngles adjustAng = baseAdjustAng + idAngles(fraccos * pscale, fraccos2 * yscale, 0.0f);
|
|
// adjustAng += owner->GetViewBobAngles();
|
|
viewWeaponAxis = adjustAng.ToMat3() * viewWeaponAxis;
|
|
}
|
|
else
|
|
{
|
|
// calculate weapon position based on player movement bobbing
|
|
owner->CalculateViewWeaponPos(viewWeaponOrigin, viewWeaponAxis);
|
|
|
|
// hide offset is for dropping the gun when approaching a GUI or NPC
|
|
// This is simpler to manage than doing the weapon put-away animation
|
|
if (gameLocal.time - hideStartTime < hideTime)
|
|
{
|
|
float frac = (float)(gameLocal.time - hideStartTime) / (float)hideTime;
|
|
if (hideStart < hideEnd)
|
|
{
|
|
frac = 1.0f - frac;
|
|
frac = 1.0f - frac * frac;
|
|
}
|
|
else
|
|
{
|
|
frac = frac * frac;
|
|
}
|
|
hideOffset = hideStart + (hideEnd - hideStart) * frac;
|
|
}
|
|
else
|
|
{
|
|
hideOffset = hideEnd;
|
|
if (hide && disabled)
|
|
{
|
|
Hide();
|
|
}
|
|
}
|
|
viewWeaponOrigin += hideOffset * viewWeaponAxis[2];
|
|
|
|
// kick up based on repeat firing
|
|
MuzzleRise(viewWeaponOrigin, viewWeaponAxis);
|
|
}
|
|
|
|
// set the physics position and orientation
|
|
GetPhysics()->SetOrigin(viewWeaponOrigin);
|
|
GetPhysics()->SetAxis(viewWeaponAxis);
|
|
UpdateVisuals();
|
|
|
|
// update the weapon script
|
|
//UpdateScript();
|
|
|
|
UpdateGUI();
|
|
|
|
// update animation
|
|
UpdateAnimation();
|
|
|
|
// only show the surface in player view
|
|
renderEntity.allowSurfaceInViewID = owner->entityNumber + 1;
|
|
|
|
// crunch the depth range so it never pokes into walls this breaks the machine gun gui
|
|
renderEntity.weaponDepthHack = g_useWeaponDepthHack.GetBool();
|
|
|
|
// present the model
|
|
if (showViewModel)
|
|
{
|
|
Present();
|
|
}
|
|
else
|
|
{
|
|
FreeModelDef();
|
|
}
|
|
|
|
if (worldModel.GetEntity() && worldModel.GetEntity()->GetRenderEntity())
|
|
{
|
|
// deal with the third-person visible world model
|
|
// don't show shadows of the world model in first person
|
|
if (gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() || pm_thirdPerson.GetBool())
|
|
{
|
|
worldModel.GetEntity()->GetRenderEntity()->suppressShadowInViewID = 0;
|
|
}
|
|
else
|
|
{
|
|
worldModel.GetEntity()->GetRenderEntity()->suppressShadowInViewID = owner->entityNumber + 1;
|
|
worldModel.GetEntity()->GetRenderEntity()->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
|
|
}
|
|
}
|
|
|
|
if (nozzleFx)
|
|
{
|
|
UpdateNozzleFx();
|
|
}
|
|
|
|
// muzzle smoke
|
|
if (showViewModel && !disabled && weaponSmoke && (weaponSmokeStartTime != 0))
|
|
{
|
|
// use the barrel joint if available
|
|
|
|
if (smokeJointView != INVALID_JOINT)
|
|
{
|
|
GetGlobalJointTransform(true, smokeJointView, muzzleOrigin, muzzleAxis);
|
|
}
|
|
else if (barrelJointView != INVALID_JOINT)
|
|
{
|
|
GetGlobalJointTransform(true, barrelJointView, muzzleOrigin, muzzleAxis);
|
|
}
|
|
else
|
|
{
|
|
// default to going straight out the view
|
|
muzzleOrigin = playerViewOrigin;
|
|
muzzleAxis = playerViewAxis;
|
|
}
|
|
// spit out a particle
|
|
if (!gameLocal.smokeParticles->EmitSmoke(weaponSmoke, weaponSmokeStartTime, gameLocal.random.RandomFloat(), muzzleOrigin, muzzleAxis))
|
|
{
|
|
weaponSmokeStartTime = (continuousSmoke) ? gameLocal.time : 0;
|
|
}
|
|
}
|
|
|
|
if (showViewModel && strikeSmoke && strikeSmokeStartTime != 0)
|
|
{
|
|
// spit out a particle
|
|
if (!gameLocal.smokeParticles->EmitSmoke(strikeSmoke, strikeSmokeStartTime, gameLocal.random.RandomFloat(), strikePos, strikeAxis))
|
|
{
|
|
strikeSmokeStartTime = 0;
|
|
}
|
|
}
|
|
|
|
if (showViewModel && !hide)
|
|
{
|
|
|
|
for (int i = 0; i < weaponParticles.Num(); i++)
|
|
{
|
|
WeaponParticle_t* part = weaponParticles.GetIndex(i);
|
|
|
|
if (part->active)
|
|
{
|
|
if (part->smoke)
|
|
{
|
|
if (part->joint != INVALID_JOINT)
|
|
{
|
|
GetGlobalJointTransform(true, part->joint, muzzleOrigin, muzzleAxis);
|
|
}
|
|
else
|
|
{
|
|
// default to going straight out the view
|
|
muzzleOrigin = playerViewOrigin;
|
|
muzzleAxis = playerViewAxis;
|
|
}
|
|
if (!gameLocal.smokeParticles->EmitSmoke(part->particle, part->startTime, gameLocal.random.RandomFloat(), muzzleOrigin, muzzleAxis))
|
|
{
|
|
part->active = false; // all done
|
|
part->startTime = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (part->emitter != NULL)
|
|
{
|
|
//Manually update the position of the emitter so it follows the weapon
|
|
renderEntity_t* rendEnt = part->emitter->GetRenderEntity();
|
|
GetGlobalJointTransform(true, part->joint, rendEnt->origin, rendEnt->axis);
|
|
|
|
if (part->emitter->GetModelDefHandle() != -1)
|
|
{
|
|
gameRenderWorld->UpdateEntityDef(part->emitter->GetModelDefHandle(), rendEnt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < weaponLights.Num(); i++)
|
|
{
|
|
WeaponLight_t* light = weaponLights.GetIndex(i);
|
|
|
|
if (light->active)
|
|
{
|
|
|
|
GetGlobalJointTransform(true, light->joint, light->light.origin, light->light.axis);
|
|
if ((light->lightHandle != -1))
|
|
{
|
|
gameRenderWorld->UpdateLightDef(light->lightHandle, &light->light);
|
|
}
|
|
else
|
|
{
|
|
light->lightHandle = gameRenderWorld->AddLightDef(&light->light);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove the muzzle flash light when it's done
|
|
if ((!lightOn && (gameLocal.time >= muzzleFlashEnd)) || IsHidden())
|
|
{
|
|
if (muzzleFlashHandle != -1)
|
|
{
|
|
gameRenderWorld->FreeLightDef(muzzleFlashHandle);
|
|
muzzleFlashHandle = -1;
|
|
}
|
|
if (worldMuzzleFlashHandle != -1)
|
|
{
|
|
gameRenderWorld->FreeLightDef(worldMuzzleFlashHandle);
|
|
worldMuzzleFlashHandle = -1;
|
|
}
|
|
}
|
|
|
|
// update the muzzle flash light, so it moves with the gun
|
|
if (muzzleFlashHandle != -1)
|
|
{
|
|
UpdateFlashPosition();
|
|
gameRenderWorld->UpdateLightDef(muzzleFlashHandle, &muzzleFlash);
|
|
gameRenderWorld->UpdateLightDef(worldMuzzleFlashHandle, &worldMuzzleFlash);
|
|
|
|
// wake up monsters with the flashlight
|
|
if (!gameLocal.isMultiplayer && lightOn && !owner->fl.notarget)
|
|
{
|
|
AlertMonsters();
|
|
}
|
|
}
|
|
|
|
// update the gui light
|
|
if (guiLight.lightRadius[0] && guiLightJointView != INVALID_JOINT)
|
|
{
|
|
GetGlobalJointTransform(true, guiLightJointView, guiLight.origin, guiLight.axis);
|
|
|
|
if ((guiLightHandle != -1))
|
|
{
|
|
gameRenderWorld->UpdateLightDef(guiLightHandle, &guiLight);
|
|
}
|
|
else
|
|
{
|
|
guiLightHandle = gameRenderWorld->AddLightDef(&guiLight);
|
|
}
|
|
}
|
|
// jmarshall - eval
|
|
//if( status != WP_READY && sndHum )
|
|
//{
|
|
// StopSound( SND_CHANNEL_BODY, false );
|
|
//}
|
|
// jmarshall end
|
|
|
|
UpdateSound();
|
|
|
|
// constant rumble...
|
|
float highMagnitude = weaponDef->dict.GetFloat("controllerConstantShakeHighMag");
|
|
int highDuration = weaponDef->dict.GetInt("controllerConstantShakeHighTime");
|
|
float lowMagnitude = weaponDef->dict.GetFloat("controllerConstantShakeLowMag");
|
|
int lowDuration = weaponDef->dict.GetInt("controllerConstantShakeLowTime");
|
|
|
|
//if( owner->IsLocallyControlled() )
|
|
//{
|
|
// owner->SetControllerShake( highMagnitude, highDuration, lowMagnitude, lowDuration );
|
|
//}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::RemoveMuzzleFlashlight
|
|
================
|
|
*/
|
|
void idWeapon::RemoveMuzzleFlashlight()
|
|
{
|
|
if (muzzleFlashHandle != -1)
|
|
{
|
|
gameRenderWorld->FreeLightDef(muzzleFlashHandle);
|
|
muzzleFlashHandle = -1;
|
|
}
|
|
if (worldMuzzleFlashHandle != -1)
|
|
{
|
|
gameRenderWorld->FreeLightDef(worldMuzzleFlashHandle);
|
|
worldMuzzleFlashHandle = -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::EnterCinematic
|
|
================
|
|
*/
|
|
void idWeapon::EnterCinematic()
|
|
{
|
|
StopSound(SND_CHANNEL_ANY, false);
|
|
|
|
disabled = true;
|
|
|
|
LowerWeapon();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ExitCinematic
|
|
================
|
|
*/
|
|
void idWeapon::ExitCinematic()
|
|
{
|
|
disabled = false;
|
|
|
|
RaiseWeapon();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::NetCatchup
|
|
================
|
|
*/
|
|
void idWeapon::NetCatchup()
|
|
{
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetZoomFov
|
|
================
|
|
*/
|
|
int idWeapon::GetZoomFov()
|
|
{
|
|
return zoomFov;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetWeaponAngleOffsets
|
|
================
|
|
*/
|
|
void idWeapon::GetWeaponAngleOffsets(int* average, float* scale, float* max)
|
|
{
|
|
*average = weaponAngleOffsetAverages;
|
|
*scale = weaponAngleOffsetScale;
|
|
*max = weaponAngleOffsetMax;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetWeaponTimeOffsets
|
|
================
|
|
*/
|
|
void idWeapon::GetWeaponTimeOffsets(float* time, float* scale)
|
|
{
|
|
*time = weaponOffsetTime;
|
|
*scale = weaponOffsetScale;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
Ammo
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetAmmoNumForName
|
|
================
|
|
*/
|
|
ammo_t idWeapon::GetAmmoNumForName(const char* ammoname)
|
|
{
|
|
int num;
|
|
const idDict* ammoDict;
|
|
|
|
assert(ammoname);
|
|
|
|
ammoDict = gameLocal.FindEntityDefDict("ammo_types", false);
|
|
if (ammoDict == NULL)
|
|
{
|
|
gameLocal.Error("Could not find entity definition for 'ammo_types'\n");
|
|
return 0;
|
|
}
|
|
|
|
if (!ammoname[0])
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (!ammoDict->GetInt(ammoname, "-1", num))
|
|
{
|
|
|
|
}
|
|
|
|
if ((num < 0) || (num >= AMMO_NUMTYPES))
|
|
{
|
|
//gameLocal.Warning( "Ammo type '%s' value out of range. Maximum ammo types is %d.\n", ammoname, AMMO_NUMTYPES );
|
|
num = 0;
|
|
}
|
|
|
|
return (ammo_t)num;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetAmmoNameForNum
|
|
================
|
|
*/
|
|
const char* idWeapon::GetAmmoNameForNum(ammo_t ammonum)
|
|
{
|
|
int i;
|
|
int num;
|
|
const idDict* ammoDict;
|
|
const idKeyValue* kv;
|
|
char text[32];
|
|
|
|
ammoDict = gameLocal.FindEntityDefDict("ammo_types", false);
|
|
if (ammoDict == NULL)
|
|
{
|
|
gameLocal.Error("Could not find entity definition for 'ammo_types'\n");
|
|
return NULL;
|
|
}
|
|
|
|
sprintf(text, "%d", ammonum);
|
|
|
|
num = ammoDict->GetNumKeyVals();
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
kv = ammoDict->GetKeyVal(i);
|
|
if (kv->GetValue() == text)
|
|
{
|
|
return kv->GetKey();
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetAmmoPickupNameForNum
|
|
================
|
|
*/
|
|
const char* idWeapon::GetAmmoPickupNameForNum(ammo_t ammonum)
|
|
{
|
|
int i;
|
|
int num;
|
|
const idDict* ammoDict;
|
|
const idKeyValue* kv;
|
|
|
|
ammoDict = gameLocal.FindEntityDefDict("ammo_names", false);
|
|
if (!ammoDict)
|
|
{
|
|
gameLocal.Error("Could not find entity definition for 'ammo_names'\n");
|
|
}
|
|
|
|
const char* name = GetAmmoNameForNum(ammonum);
|
|
|
|
if (name != NULL && *name != NULL)
|
|
{
|
|
num = ammoDict->GetNumKeyVals();
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
kv = ammoDict->GetKeyVal(i);
|
|
if (idStr::Icmp(kv->GetKey(), name) == 0)
|
|
{
|
|
return kv->GetValue();
|
|
}
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::AmmoAvailable
|
|
================
|
|
*/
|
|
int idWeapon::AmmoAvailable() const
|
|
{
|
|
if (owner)
|
|
{
|
|
return owner->inventory.HasAmmo(ammoType, ammoRequired);
|
|
}
|
|
else
|
|
{
|
|
//if( g_infiniteAmmo.GetBool() )
|
|
//{
|
|
// return 10; // arbitrary number, just so whatever's calling thinks there's sufficient ammo...
|
|
//}
|
|
//else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::AmmoInClip
|
|
================
|
|
*/
|
|
int idWeapon::AmmoInClip() const
|
|
{
|
|
return ammoClip.Get();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ResetAmmoClip
|
|
================
|
|
*/
|
|
void idWeapon::ResetAmmoClip()
|
|
{
|
|
ammoClip = -1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetAmmoType
|
|
================
|
|
*/
|
|
ammo_t idWeapon::GetAmmoType() const
|
|
{
|
|
return ammoType;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ClipSize
|
|
================
|
|
*/
|
|
int idWeapon::ClipSize() const
|
|
{
|
|
return clipSize;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::LowAmmo
|
|
================
|
|
*/
|
|
int idWeapon::LowAmmo() const
|
|
{
|
|
return lowAmmo;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::AmmoRequired
|
|
================
|
|
*/
|
|
int idWeapon::AmmoRequired() const
|
|
{
|
|
return ammoRequired;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetGrabberState
|
|
|
|
Returns the current grabberState
|
|
================
|
|
*/
|
|
int idWeapon::GetGrabberState() const
|
|
{
|
|
|
|
return grabberState;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::AmmoCount
|
|
|
|
Returns the total number of rounds regardless of the required ammo
|
|
================
|
|
*/
|
|
int idWeapon::AmmoCount() const
|
|
{
|
|
|
|
if (owner)
|
|
{
|
|
return owner->inventory.HasAmmo(ammoType, 1);
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::WriteToSnapshot
|
|
================
|
|
*/
|
|
void idWeapon::WriteToSnapshot(idBitMsg& msg) const
|
|
{
|
|
msg.WriteBits(ammoClip.Get(), ASYNC_PLAYER_INV_CLIP_BITS);
|
|
msg.WriteBits(worldModel.GetSpawnId(), 32);
|
|
msg.WriteBits(lightOn, 1);
|
|
msg.WriteBits(isFiring ? 1 : 0, 1);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ReadFromSnapshot
|
|
================
|
|
*/
|
|
void idWeapon::ReadFromSnapshot(const idBitMsg& msg)
|
|
{
|
|
const int snapshotAmmoClip = msg.ReadBits(ASYNC_PLAYER_INV_CLIP_BITS);
|
|
worldModel.SetSpawnId(msg.ReadBits(32));
|
|
const bool snapshotLightOn = msg.ReadBits(1) != 0;
|
|
isFiring = msg.ReadBits(1) != 0;
|
|
|
|
// Local clients predict the ammo in the clip. Only use the ammo cpunt from the snapshot for local clients
|
|
// if the server has processed the same usercmd in which we predicted the ammo count.
|
|
if (owner != NULL)
|
|
{
|
|
ammoClip.UpdateFromSnapshot(snapshotAmmoClip, owner->entityNumber);
|
|
}
|
|
|
|
// WEAPON_NETFIRING is only turned on for other clients we're predicting. not for local client
|
|
//if ( owner && !owner->IsLocallyControlled() && WEAPON_NETFIRING.IsLinked() ) {
|
|
//
|
|
// // immediately go to the firing state so we don't skip fire animations
|
|
// if ( !WEAPON_NETFIRING && isFiring ) {
|
|
// idealState = "Fire";
|
|
// }
|
|
//
|
|
// // immediately switch back to idle
|
|
// if ( WEAPON_NETFIRING && !isFiring ) {
|
|
// idealState = "Idle";
|
|
// }
|
|
//
|
|
// WEAPON_NETFIRING = isFiring;
|
|
// WEAPON_ATTACK = isFiring;
|
|
//}
|
|
|
|
// Only update the flashlight state if it has changed, and if this isn't the local player.
|
|
// The local player sets their flashlight immediately for responsiveness.
|
|
//if( owner != NULL && !owner->IsLocallyControlled() && lightOn != snapshotLightOn )
|
|
//{
|
|
// if( snapshotLightOn )
|
|
// {
|
|
// FlashlightOn();
|
|
// }
|
|
// else
|
|
// {
|
|
// FlashlightOff();
|
|
// }
|
|
//}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ClientReceiveEvent
|
|
================
|
|
*/
|
|
bool idWeapon::ClientReceiveEvent(int event, int time, const idBitMsg& msg)
|
|
{
|
|
|
|
switch (event)
|
|
{
|
|
case EVENT_RELOAD:
|
|
{
|
|
// Local clients predict reloads, only process this event for remote clients.
|
|
//if ( owner != NULL && !owner->IsLocallyControlled() && ( gameLocal.time - time < 1000 ) ) {
|
|
// if ( WEAPON_NETRELOAD.IsLinked() ) {
|
|
// WEAPON_NETRELOAD = true;
|
|
// WEAPON_NETENDRELOAD = false;
|
|
// }
|
|
//}
|
|
return true;
|
|
}
|
|
case EVENT_ENDRELOAD:
|
|
{
|
|
// Local clients predict reloads, only process this event for remote clients.
|
|
//if ( owner != NULL && !owner->IsLocallyControlled() && WEAPON_NETENDRELOAD.IsLinked() ) {
|
|
// WEAPON_NETENDRELOAD = true;
|
|
//}
|
|
return true;
|
|
}
|
|
case EVENT_CHANGESKIN:
|
|
{
|
|
int index = gameLocal.ClientRemapDecl(DECL_SKIN, msg.ReadLong());
|
|
renderEntity.customSkin = (index != -1) ? static_cast<const idDeclSkin*>(declManager->DeclByIndex(DECL_SKIN, index)) : NULL;
|
|
UpdateVisuals();
|
|
if (worldModel.GetEntity())
|
|
{
|
|
worldModel.GetEntity()->SetSkin(renderEntity.customSkin);
|
|
}
|
|
return true;
|
|
}
|
|
default:
|
|
{
|
|
return idEntity::ClientReceiveEvent(event, time, msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Script events
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_Clear
|
|
===============
|
|
*/
|
|
void idWeapon::Event_Clear()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_GetOwner
|
|
===============
|
|
*/
|
|
void idWeapon::Event_GetOwner()
|
|
{
|
|
idThread::ReturnEntity(owner);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_WeaponReady
|
|
===============
|
|
*/
|
|
void idWeapon::Event_WeaponReady()
|
|
{
|
|
//status = WP_READY;
|
|
currentWeaponObject->SetState("Rising");
|
|
currentWeaponObject->AppendState("Idle");
|
|
// idLib::PrintfIf( g_debugWeapon.GetBool(), "Weapon Status WP_READY \n" );
|
|
//if ( isLinked ) {
|
|
// WEAPON_RAISEWEAPON = false;
|
|
//}
|
|
if (sndHum)
|
|
{
|
|
StartSoundShader(sndHum, SND_CHANNEL_BODY, 0, false, NULL);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_WeaponOutOfAmmo
|
|
===============
|
|
*/
|
|
void idWeapon::Event_WeaponOutOfAmmo()
|
|
{
|
|
//status = WP_OUTOFAMMO;
|
|
OutOfAmmo = true;
|
|
// idLib::PrintfIf( g_debugWeapon.GetBool(), "Weapon Status WP_OUTOFAMMO \n" );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_WeaponReloading
|
|
===============
|
|
*/
|
|
void idWeapon::Event_WeaponReloading()
|
|
{
|
|
Reload();
|
|
// idLib::PrintfIf( g_debugWeapon.GetBool(), "Weapon Status WP_RELOAD \n" );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_WeaponHolstered
|
|
===============
|
|
*/
|
|
void idWeapon::Event_WeaponHolstered()
|
|
{
|
|
//status = WP_HOLSTERED;
|
|
// idLib::PrintfIf( g_debugWeapon.GetBool(), "Weapon Status WP_HOLSTERED \n" );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_WeaponRising
|
|
===============
|
|
*/
|
|
void idWeapon::Event_WeaponRising()
|
|
{
|
|
Raise();
|
|
// idLib::PrintfIf( g_debugWeapon.GetBool(), "Weapon Status WP_RISING \n" );
|
|
owner->WeaponRisingCallback();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_WeaponLowering
|
|
===============
|
|
*/
|
|
void idWeapon::Event_WeaponLowering()
|
|
{
|
|
LowerWeapon();
|
|
// idLib::PrintfIf( g_debugWeapon.GetBool(), "Weapon Status WP_LOWERING \n" );
|
|
owner->WeaponLoweringCallback();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_UseAmmo
|
|
===============
|
|
*/
|
|
void idWeapon::Event_UseAmmo(int amount)
|
|
{
|
|
if (gameLocal.isClient) {
|
|
return;
|
|
}
|
|
|
|
|
|
owner->inventory.UseAmmo(ammoType, (powerAmmo) ? amount : (amount * ammoRequired));
|
|
if (clipSize && ammoRequired)
|
|
{
|
|
ammoClip -= powerAmmo ? amount : (amount * ammoRequired);
|
|
if (ammoClip.Get() < 0)
|
|
{
|
|
ammoClip = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_AddToClip
|
|
===============
|
|
*/
|
|
void idWeapon::Event_AddToClip(int amount)
|
|
{
|
|
int ammoAvail;
|
|
|
|
if (gameLocal.isClient) {
|
|
return;
|
|
}
|
|
|
|
int oldAmmo = ammoClip.Get();
|
|
ammoAvail = owner->inventory.HasAmmo(ammoType, ammoRequired) + AmmoInClip();
|
|
|
|
ammoClip += amount;
|
|
if (ammoClip.Get() > clipSize)
|
|
{
|
|
ammoClip = clipSize;
|
|
}
|
|
|
|
|
|
if (ammoClip.Get() > ammoAvail)
|
|
{
|
|
ammoClip = ammoAvail;
|
|
}
|
|
|
|
// for shared ammo we need to use the ammo when it is moved into the clip
|
|
int usedAmmo = ammoClip.Get() - oldAmmo;
|
|
owner->inventory.UseAmmo(ammoType, usedAmmo);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_AmmoInClip
|
|
===============
|
|
*/
|
|
void idWeapon::Event_AmmoInClip()
|
|
{
|
|
int ammo = AmmoInClip();
|
|
idThread::ReturnFloat(ammo);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::AmmoAvailable
|
|
===============
|
|
*/
|
|
int idWeapon::AmmoAvailable()
|
|
{
|
|
int ammoAvail = owner->inventory.HasAmmo(ammoType, ammoRequired);
|
|
ammoAvail += AmmoInClip();
|
|
|
|
return ammoAvail;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_AmmoAvailable
|
|
===============
|
|
*/
|
|
void idWeapon::Event_AmmoAvailable()
|
|
{
|
|
idThread::ReturnFloat(AmmoAvailable());
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_TotalAmmoCount
|
|
===============
|
|
*/
|
|
void idWeapon::Event_TotalAmmoCount()
|
|
{
|
|
int ammoAvail = owner->inventory.HasAmmo(ammoType, 1);
|
|
idThread::ReturnFloat(ammoAvail);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_ClipSize
|
|
===============
|
|
*/
|
|
void idWeapon::Event_ClipSize()
|
|
{
|
|
idThread::ReturnFloat(clipSize);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_AutoReload
|
|
===============
|
|
*/
|
|
void idWeapon::Event_AutoReload()
|
|
{
|
|
assert(owner);
|
|
if (gameLocal.isClient) {
|
|
idThread::ReturnFloat(0.0f);
|
|
return;
|
|
}
|
|
idThread::ReturnFloat(gameLocal.userInfo[owner->entityNumber].GetBool("ui_autoReload"));
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_NetReload
|
|
===============
|
|
*/
|
|
void idWeapon::Event_NetReload()
|
|
{
|
|
assert(owner);
|
|
if (gameLocal.isServer)
|
|
{
|
|
ServerSendEvent(EVENT_RELOAD, NULL, false, 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_NetEndReload
|
|
===============
|
|
*/
|
|
void idWeapon::Event_NetEndReload()
|
|
{
|
|
assert(owner);
|
|
if (gameLocal.isServer)
|
|
{
|
|
ServerSendEvent(EVENT_ENDRELOAD, NULL, false, 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_PlayAnim
|
|
===============
|
|
*/
|
|
void idWeapon::Event_PlayAnim(int channel, const char* animname, bool loop)
|
|
{
|
|
int anim;
|
|
|
|
anim = animator.GetAnim(animname);
|
|
if (!anim)
|
|
{
|
|
gameLocal.Warning("missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName());
|
|
animator.Clear(channel, gameLocal.time, FRAME2MS(animBlendFrames));
|
|
animDoneTime = 0;
|
|
}
|
|
else
|
|
{
|
|
if (!(owner && owner->GetInfluenceLevel()))
|
|
{
|
|
Show();
|
|
}
|
|
animator.PlayAnim(channel, anim, gameLocal.time, FRAME2MS(animBlendFrames));
|
|
animDoneTime = animator.CurrentAnim(channel)->GetEndTime();
|
|
if (worldModel.GetEntity())
|
|
{
|
|
anim = worldModel.GetEntity()->GetAnimator()->GetAnim(animname);
|
|
if (anim)
|
|
{
|
|
worldModel.GetEntity()->GetAnimator()->PlayAnim(channel, anim, gameLocal.time, FRAME2MS(animBlendFrames));
|
|
}
|
|
}
|
|
}
|
|
animBlendFrames = 0;
|
|
idThread::ReturnInt(0);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_PlayCycle
|
|
===============
|
|
*/
|
|
void idWeapon::Event_PlayCycle(int channel, const char* animname)
|
|
{
|
|
int anim;
|
|
|
|
anim = animator.GetAnim(animname);
|
|
if (!anim)
|
|
{
|
|
gameLocal.Warning("missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName());
|
|
animator.Clear(channel, gameLocal.time, FRAME2MS(animBlendFrames));
|
|
animDoneTime = 0;
|
|
}
|
|
else
|
|
{
|
|
if (!(owner && owner->GetInfluenceLevel()))
|
|
{
|
|
Show();
|
|
}
|
|
animator.CycleAnim(channel, anim, gameLocal.time, FRAME2MS(animBlendFrames));
|
|
animDoneTime = animator.CurrentAnim(channel)->GetEndTime();
|
|
if (worldModel.GetEntity())
|
|
{
|
|
anim = worldModel.GetEntity()->GetAnimator()->GetAnim(animname);
|
|
worldModel.GetEntity()->GetAnimator()->CycleAnim(channel, anim, gameLocal.time, FRAME2MS(animBlendFrames));
|
|
}
|
|
}
|
|
animBlendFrames = 0;
|
|
idThread::ReturnInt(0);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_AnimDone
|
|
===============
|
|
*/
|
|
bool idWeapon::Event_AnimDone(int channel, int blendFrames)
|
|
{
|
|
if (animDoneTime - FRAME2MS(blendFrames) <= gameLocal.time)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_SetBlendFrames
|
|
===============
|
|
*/
|
|
void idWeapon::Event_SetBlendFrames(int channel, int blendFrames)
|
|
{
|
|
animBlendFrames = blendFrames;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_GetBlendFrames
|
|
===============
|
|
*/
|
|
void idWeapon::Event_GetBlendFrames(int channel)
|
|
{
|
|
idThread::ReturnInt(animBlendFrames);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_Next
|
|
================
|
|
*/
|
|
void idWeapon::Event_Next()
|
|
{
|
|
// change to another weapon if possible
|
|
owner->NextBestWeapon();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_SetSkin
|
|
================
|
|
*/
|
|
void idWeapon::Event_SetSkin(const char* skinname)
|
|
{
|
|
const idDeclSkin* skinDecl = NULL;
|
|
if (!skinname || !skinname[0])
|
|
{
|
|
skinDecl = NULL;
|
|
}
|
|
else
|
|
{
|
|
skinDecl = declManager->FindSkin(skinname);
|
|
}
|
|
|
|
// Don't update if the skin hasn't changed.
|
|
if (renderEntity.customSkin == skinDecl && worldModel.GetEntity() != NULL && worldModel.GetEntity()->GetSkin() == skinDecl)
|
|
{
|
|
return;
|
|
}
|
|
|
|
renderEntity.customSkin = skinDecl;
|
|
UpdateVisuals();
|
|
|
|
if (worldModel.GetEntity())
|
|
{
|
|
worldModel.GetEntity()->SetSkin(skinDecl);
|
|
}
|
|
|
|
// Hack, don't send message if flashlight, because clients process the flashlight instantly.
|
|
//if( gameLocal.isServer && !isPlayerFlashlight )
|
|
//{
|
|
// idBitMsg msg;
|
|
// byte msgBuf[MAX_EVENT_PARAM_SIZE];
|
|
//
|
|
// msg.InitWrite( msgBuf, sizeof( msgBuf ) );
|
|
// msg.WriteLong( ( skinDecl != NULL ) ? gameLocal.ServerRemapDecl( -1, DECL_SKIN, skinDecl->Index() ) : -1 );
|
|
// ServerSendEvent( EVENT_CHANGESKIN, &msg, false );
|
|
//}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_Flashlight
|
|
================
|
|
*/
|
|
void idWeapon::Event_Flashlight(int enable)
|
|
{
|
|
if (enable)
|
|
{
|
|
lightOn = true;
|
|
MuzzleFlashLight();
|
|
}
|
|
else
|
|
{
|
|
lightOn = false;
|
|
muzzleFlashEnd = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_GetLightParm
|
|
================
|
|
*/
|
|
void idWeapon::Event_GetLightParm(int parmnum)
|
|
{
|
|
if ((parmnum < 0) || (parmnum >= MAX_ENTITY_SHADER_PARMS))
|
|
{
|
|
gameLocal.Error("shader parm index (%d) out of range", parmnum);
|
|
return;
|
|
}
|
|
|
|
idThread::ReturnFloat(muzzleFlash.shaderParms[parmnum]);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_SetLightParm
|
|
================
|
|
*/
|
|
void idWeapon::Event_SetLightParm(int parmnum, float value)
|
|
{
|
|
if ((parmnum < 0) || (parmnum >= MAX_ENTITY_SHADER_PARMS))
|
|
{
|
|
gameLocal.Error("shader parm index (%d) out of range", parmnum);
|
|
return;
|
|
}
|
|
|
|
muzzleFlash.shaderParms[parmnum] = value;
|
|
worldMuzzleFlash.shaderParms[parmnum] = value;
|
|
UpdateVisuals();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_SetLightParms
|
|
================
|
|
*/
|
|
void idWeapon::Event_SetLightParms(float parm0, float parm1, float parm2, float parm3)
|
|
{
|
|
muzzleFlash.shaderParms[SHADERPARM_RED] = parm0;
|
|
muzzleFlash.shaderParms[SHADERPARM_GREEN] = parm1;
|
|
muzzleFlash.shaderParms[SHADERPARM_BLUE] = parm2;
|
|
muzzleFlash.shaderParms[SHADERPARM_ALPHA] = parm3;
|
|
|
|
worldMuzzleFlash.shaderParms[SHADERPARM_RED] = parm0;
|
|
worldMuzzleFlash.shaderParms[SHADERPARM_GREEN] = parm1;
|
|
worldMuzzleFlash.shaderParms[SHADERPARM_BLUE] = parm2;
|
|
worldMuzzleFlash.shaderParms[SHADERPARM_ALPHA] = parm3;
|
|
|
|
UpdateVisuals();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_Grabber
|
|
================
|
|
*/
|
|
void idWeapon::Event_Grabber(int enable)
|
|
{
|
|
if (enable)
|
|
{
|
|
grabberState = 0;
|
|
}
|
|
else
|
|
{
|
|
grabberState = -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_GrabberHasTarget
|
|
================
|
|
*/
|
|
int idWeapon::Event_GrabberHasTarget()
|
|
{
|
|
return grabberState;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_GrabberSetGrabDistance
|
|
================
|
|
*/
|
|
void idWeapon::Event_GrabberSetGrabDistance(float dist)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_CreateProjectile
|
|
================
|
|
*/
|
|
idEntity* idWeapon::CreateProjectile()
|
|
{
|
|
if (!gameLocal.isClient) {
|
|
projectileEnt = NULL;
|
|
gameLocal.SpawnEntityDef(projectileDict, &projectileEnt, false);
|
|
if (projectileEnt)
|
|
{
|
|
projectileEnt->SetOrigin(GetPhysics()->GetOrigin());
|
|
projectileEnt->Bind(owner, false);
|
|
projectileEnt->Hide();
|
|
}
|
|
return projectileEnt;
|
|
}
|
|
else
|
|
{
|
|
return (NULL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_CreateProjectile
|
|
================
|
|
*/
|
|
void idWeapon::Event_CreateProjectile()
|
|
{
|
|
idThread::ReturnEntity(CreateProjectile());
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetProjectileLaunchOriginAndAxis
|
|
================
|
|
*/
|
|
void idWeapon::GetProjectileLaunchOriginAndAxis(idVec3& origin, idMat3& axis)
|
|
{
|
|
assert(owner != NULL);
|
|
|
|
// calculate the muzzle position
|
|
if (barrelJointView != INVALID_JOINT && projectileDict.GetBool("launchFromBarrel"))
|
|
{
|
|
// there is an explicit joint for the muzzle
|
|
// GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis );
|
|
GetMuzzlePositionWithHacks(origin, axis);
|
|
}
|
|
else
|
|
{
|
|
// go straight out of the view
|
|
origin = playerViewOrigin;
|
|
axis = playerViewAxis;
|
|
}
|
|
|
|
axis = playerViewAxis; // Fix for plasma rifle not firing correctly on initial shot of a burst fire
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_LaunchProjectiles
|
|
================
|
|
*/
|
|
void idWeapon::Event_LaunchProjectiles(int num_projectiles, float spread, float fuseOffset, float launchPower, float dmgPower) {
|
|
idProjectile* proj;
|
|
idEntity* ent;
|
|
int i;
|
|
idVec3 dir;
|
|
float ang;
|
|
float spin;
|
|
float distance;
|
|
trace_t tr;
|
|
idVec3 start;
|
|
idVec3 muzzle_pos;
|
|
idBounds ownerBounds, projBounds;
|
|
|
|
if (IsHidden()) {
|
|
return;
|
|
}
|
|
|
|
if (!projectileDict.GetNumKeyVals()) {
|
|
const char* classname = weaponDef->dict.GetString("classname");
|
|
gameLocal.Warning("No projectile defined on '%s'", classname);
|
|
return;
|
|
}
|
|
|
|
// avoid all ammo considerations on an MP client
|
|
if (!gameLocal.isClient) {
|
|
|
|
#if 1 // previously _D3XP
|
|
|
|
int ammoAvail = owner->inventory.HasAmmo(ammoType, ammoRequired);
|
|
if ((clipSize != 0) && (ammoClip.Get() <= 0)) {
|
|
return;
|
|
}
|
|
|
|
#else
|
|
// check if we're out of ammo or the clip is empty
|
|
int ammoAvail = owner->inventory.HasAmmo(ammoType, ammoRequired);
|
|
if (!ammoAvail || ((clipSize != 0) && (ammoClip <= 0))) {
|
|
return;
|
|
}
|
|
#endif
|
|
// if this is a power ammo weapon ( currently only the bfg ) then make sure
|
|
// we only fire as much power as available in each clip
|
|
if (powerAmmo) {
|
|
// power comes in as a float from zero to max
|
|
// if we use this on more than the bfg will need to define the max
|
|
// in the .def as opposed to just in the script so proper calcs
|
|
// can be done here.
|
|
dmgPower = (int)dmgPower + 1;
|
|
if (dmgPower > ammoClip.Get()) {
|
|
dmgPower = ammoClip.Get();
|
|
}
|
|
}
|
|
|
|
#if 1 // previously _D3XP
|
|
if (clipSize == 0) {
|
|
//Weapons with a clip size of 0 launch strait from inventory without moving to a clip
|
|
#endif
|
|
//In D3XP we used the ammo when the ammo was moved into the clip so we don't want to
|
|
//use it now.
|
|
owner->inventory.UseAmmo(ammoType, (powerAmmo) ? dmgPower : ammoRequired);
|
|
|
|
#if 1 // previously _D3XP
|
|
}
|
|
#endif
|
|
|
|
if (clipSize && ammoRequired) {
|
|
#if 1 // previously _D3XP
|
|
ammoClip -= powerAmmo ? dmgPower : ammoRequired;
|
|
#else
|
|
ammoClip -= powerAmmo ? dmgPower : 1;
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
if (!silent_fire) {
|
|
// wake up nearby monsters
|
|
gameLocal.AlertAI(owner);
|
|
}
|
|
|
|
// set the shader parm to the time of last projectile firing,
|
|
// which the gun material shaders can reference for single shot barrel glows, etc
|
|
renderEntity.shaderParms[SHADERPARM_DIVERSITY] = gameLocal.random.CRandomFloat();
|
|
renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC(gameLocal.realClientTime);
|
|
|
|
if (worldModel.GetEntity()) {
|
|
worldModel.GetEntity()->SetShaderParm(SHADERPARM_DIVERSITY, renderEntity.shaderParms[SHADERPARM_DIVERSITY]);
|
|
worldModel.GetEntity()->SetShaderParm(SHADERPARM_TIMEOFFSET, renderEntity.shaderParms[SHADERPARM_TIMEOFFSET]);
|
|
}
|
|
|
|
// calculate the muzzle position
|
|
if (barrelJointView != INVALID_JOINT && projectileDict.GetBool("launchFromBarrel")) {
|
|
// there is an explicit joint for the muzzle
|
|
GetGlobalJointTransform(true, barrelJointView, muzzleOrigin, muzzleAxis);
|
|
}
|
|
else {
|
|
// go straight out of the view
|
|
muzzleOrigin = playerViewOrigin;
|
|
muzzleAxis = playerViewAxis;
|
|
}
|
|
|
|
// add some to the kick time, incrementally moving repeat firing weapons back
|
|
if (kick_endtime < gameLocal.realClientTime) {
|
|
kick_endtime = gameLocal.realClientTime;
|
|
}
|
|
kick_endtime += muzzle_kick_time;
|
|
if (kick_endtime > gameLocal.realClientTime + muzzle_kick_maxtime) {
|
|
kick_endtime = gameLocal.realClientTime + muzzle_kick_maxtime;
|
|
}
|
|
|
|
if (gameLocal.isClient) {
|
|
|
|
// predict instant hit projectiles
|
|
if (projectileDict.GetBool("net_instanthit")) {
|
|
float spreadRad = DEG2RAD(spread);
|
|
muzzle_pos = muzzleOrigin + playerViewAxis[0] * 2.0f;
|
|
for (i = 0; i < num_projectiles; i++) {
|
|
ang = idMath::Sin(spreadRad * gameLocal.random.RandomFloat());
|
|
spin = (float)DEG2RAD(360.0f) * gameLocal.random.RandomFloat();
|
|
dir = playerViewAxis[0] + playerViewAxis[2] * (ang * idMath::Sin(spin)) - playerViewAxis[1] * (ang * idMath::Cos(spin));
|
|
dir.Normalize();
|
|
gameLocal.clip.Translation(tr, muzzle_pos, muzzle_pos + dir * 4096.0f, NULL, mat3_identity, MASK_SHOT_RENDERMODEL, owner);
|
|
if (tr.fraction < 1.0f) {
|
|
idProjectile::ClientPredictionCollide(this, projectileDict, tr, vec3_origin, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else {
|
|
|
|
ownerBounds = owner->GetPhysics()->GetAbsBounds();
|
|
|
|
owner->AddProjectilesFired(num_projectiles);
|
|
|
|
float spreadRad = DEG2RAD(spread);
|
|
for (i = 0; i < num_projectiles; i++) {
|
|
ang = idMath::Sin(spreadRad * gameLocal.random.RandomFloat());
|
|
spin = (float)DEG2RAD(360.0f) * gameLocal.random.RandomFloat();
|
|
dir = playerViewAxis[0] + playerViewAxis[2] * (ang * idMath::Sin(spin)) - playerViewAxis[1] * (ang * idMath::Cos(spin));
|
|
dir.Normalize();
|
|
|
|
if (projectileEnt) {
|
|
ent = projectileEnt;
|
|
ent->Show();
|
|
ent->Unbind();
|
|
projectileEnt = NULL;
|
|
}
|
|
else {
|
|
gameLocal.SpawnEntityDef(projectileDict, &ent, false);
|
|
}
|
|
|
|
if (!ent || !ent->IsType(idProjectile::Type)) {
|
|
const char* projectileName = weaponDef->dict.GetString("def_projectile");
|
|
gameLocal.Error("'%s' is not an idProjectile", projectileName);
|
|
}
|
|
|
|
if (projectileDict.GetBool("net_instanthit")) {
|
|
// don't synchronize this on top of the already predicted effect
|
|
ent->fl.networkSync = false;
|
|
}
|
|
|
|
proj = static_cast<idProjectile*>(ent);
|
|
proj->Create(owner, muzzleOrigin, dir);
|
|
|
|
projBounds = proj->GetPhysics()->GetBounds().Rotate(proj->GetPhysics()->GetAxis());
|
|
|
|
// make sure the projectile starts inside the bounding box of the owner
|
|
if (i == 0) {
|
|
muzzle_pos = muzzleOrigin + playerViewAxis[0] * 2.0f;
|
|
if ((ownerBounds - projBounds).RayIntersection(muzzle_pos, playerViewAxis[0], distance)) {
|
|
start = muzzle_pos + distance * playerViewAxis[0];
|
|
}
|
|
else {
|
|
start = ownerBounds.GetCenter();
|
|
}
|
|
gameLocal.clip.Translation(tr, start, muzzle_pos, proj->GetPhysics()->GetClipModel(), proj->GetPhysics()->GetClipModel()->GetAxis(), MASK_SHOT_RENDERMODEL, owner);
|
|
muzzle_pos = tr.endpos;
|
|
}
|
|
|
|
proj->Launch(muzzle_pos, dir, pushVelocity, fuseOffset, launchPower, dmgPower);
|
|
}
|
|
|
|
// toss the brass
|
|
#if 1 // previously _D3XP
|
|
// if (brassDelay >= 0)
|
|
#endif
|
|
//PostEventMS(&EV_Weapon_EjectBrass, brassDelay);
|
|
}
|
|
|
|
// add the light for the muzzleflash
|
|
if (!lightOn) {
|
|
MuzzleFlashLight();
|
|
}
|
|
|
|
owner->WeaponFireFeedback(&weaponDef->dict);
|
|
|
|
// reset muzzle smoke
|
|
weaponSmokeStartTime = gameLocal.realClientTime;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_LaunchProjectilesEllipse
|
|
================
|
|
*/
|
|
void idWeapon::Event_LaunchProjectilesEllipse(int num_projectiles, float spreada, float spreadb, float fuseOffset, float power)
|
|
{
|
|
idProjectile* proj;
|
|
idEntity* ent;
|
|
int i;
|
|
idVec3 dir;
|
|
float anga, angb;
|
|
float spin;
|
|
float distance;
|
|
trace_t tr;
|
|
idVec3 start;
|
|
idVec3 muzzle_pos;
|
|
idBounds ownerBounds, projBounds;
|
|
|
|
if (IsHidden())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!projectileDict.GetNumKeyVals())
|
|
{
|
|
const char* classname = weaponDef->dict.GetString("classname");
|
|
gameLocal.Warning("No projectile defined on '%s'", classname);
|
|
return;
|
|
}
|
|
|
|
// avoid all ammo considerations on a client
|
|
if (!gameLocal.isClient)
|
|
{
|
|
if ((clipSize != 0) && (ammoClip.Get() <= 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (clipSize == 0)
|
|
{
|
|
//Weapons with a clip size of 0 launch strait from inventory without moving to a clip
|
|
owner->inventory.UseAmmo(ammoType, ammoRequired);
|
|
}
|
|
|
|
if (clipSize && ammoRequired)
|
|
{
|
|
ammoClip -= ammoRequired;
|
|
}
|
|
|
|
if (!silent_fire)
|
|
{
|
|
// wake up nearby monsters
|
|
gameLocal.AlertAI(owner);
|
|
}
|
|
|
|
}
|
|
|
|
// set the shader parm to the time of last projectile firing,
|
|
// which the gun material shaders can reference for single shot barrel glows, etc
|
|
renderEntity.shaderParms[SHADERPARM_DIVERSITY] = gameLocal.random.CRandomFloat();
|
|
renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC(gameLocal.time);
|
|
|
|
if (worldModel.GetEntity())
|
|
{
|
|
worldModel.GetEntity()->SetShaderParm(SHADERPARM_DIVERSITY, renderEntity.shaderParms[SHADERPARM_DIVERSITY]);
|
|
worldModel.GetEntity()->SetShaderParm(SHADERPARM_TIMEOFFSET, renderEntity.shaderParms[SHADERPARM_TIMEOFFSET]);
|
|
}
|
|
|
|
// calculate the muzzle position
|
|
if (barrelJointView != INVALID_JOINT && projectileDict.GetBool("launchFromBarrel"))
|
|
{
|
|
// there is an explicit joint for the muzzle
|
|
GetGlobalJointTransform(true, barrelJointView, muzzleOrigin, muzzleAxis);
|
|
}
|
|
else
|
|
{
|
|
// go straight out of the view
|
|
muzzleOrigin = playerViewOrigin;
|
|
muzzleAxis = playerViewAxis;
|
|
}
|
|
|
|
// add some to the kick time, incrementally moving repeat firing weapons back
|
|
if (kick_endtime < gameLocal.time)
|
|
{
|
|
kick_endtime = gameLocal.time;
|
|
}
|
|
kick_endtime += muzzle_kick_time;
|
|
if (kick_endtime > gameLocal.time + muzzle_kick_maxtime)
|
|
{
|
|
kick_endtime = gameLocal.time + muzzle_kick_maxtime;
|
|
}
|
|
|
|
if (!gameLocal.isClient)
|
|
{
|
|
ownerBounds = owner->GetPhysics()->GetAbsBounds();
|
|
|
|
owner->AddProjectilesFired(num_projectiles);
|
|
|
|
float spreadRadA = DEG2RAD(spreada);
|
|
float spreadRadB = DEG2RAD(spreadb);
|
|
|
|
for (i = 0; i < num_projectiles; i++)
|
|
{
|
|
//Ellipse Form
|
|
spin = (float)DEG2RAD(360.0f) * gameLocal.random.RandomFloat();
|
|
anga = idMath::Sin(spreadRadA * gameLocal.random.RandomFloat());
|
|
angb = idMath::Sin(spreadRadB * gameLocal.random.RandomFloat());
|
|
dir = playerViewAxis[0] + playerViewAxis[2] * (angb * idMath::Sin(spin)) - playerViewAxis[1] * (anga * idMath::Cos(spin));
|
|
dir.Normalize();
|
|
|
|
gameLocal.SpawnEntityDef(projectileDict, &ent);
|
|
if (ent == NULL || !ent->IsType(idProjectile::Type))
|
|
{
|
|
const char* projectileName = weaponDef->dict.GetString("def_projectile");
|
|
gameLocal.Error("'%s' is not an idProjectile", projectileName);
|
|
return;
|
|
}
|
|
|
|
proj = static_cast<idProjectile*>(ent);
|
|
proj->Create(owner, muzzleOrigin, dir);
|
|
|
|
projBounds = proj->GetPhysics()->GetBounds().Rotate(proj->GetPhysics()->GetAxis());
|
|
|
|
// make sure the projectile starts inside the bounding box of the owner
|
|
if (i == 0)
|
|
{
|
|
muzzle_pos = muzzleOrigin + playerViewAxis[0] * 2.0f;
|
|
if ((ownerBounds - projBounds).RayIntersection(muzzle_pos, playerViewAxis[0], distance))
|
|
{
|
|
start = muzzle_pos + distance * playerViewAxis[0];
|
|
}
|
|
else
|
|
{
|
|
start = ownerBounds.GetCenter();
|
|
}
|
|
gameLocal.clip.Translation(tr, start, muzzle_pos, proj->GetPhysics()->GetClipModel(), proj->GetPhysics()->GetClipModel()->GetAxis(), MASK_SHOT_RENDERMODEL, owner);
|
|
muzzle_pos = tr.endpos;
|
|
}
|
|
|
|
proj->Launch(muzzle_pos, dir, pushVelocity, fuseOffset, power);
|
|
}
|
|
|
|
// toss the brass
|
|
//if( brassDelay >= 0 ) {
|
|
// PostEventMS( &EV_Weapon_EjectBrass, brassDelay );
|
|
//}
|
|
}
|
|
|
|
// add the light for the muzzleflash
|
|
if (!lightOn)
|
|
{
|
|
MuzzleFlashLight();
|
|
}
|
|
|
|
owner->WeaponFireFeedback(&weaponDef->dict);
|
|
|
|
// reset muzzle smoke
|
|
weaponSmokeStartTime = gameLocal.time;
|
|
|
|
}
|
|
|
|
/**
|
|
* Gives the player a powerup as if it were a weapon shot. It will use the ammo amount specified
|
|
* as ammoRequired.
|
|
*/
|
|
void idWeapon::Event_LaunchPowerup(const char* powerup, float duration, int useAmmo)
|
|
{
|
|
|
|
if (IsHidden())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// check if we're out of ammo
|
|
if (useAmmo)
|
|
{
|
|
int ammoAvail = owner->inventory.HasAmmo(ammoType, ammoRequired);
|
|
if (!ammoAvail)
|
|
{
|
|
return;
|
|
}
|
|
owner->inventory.UseAmmo(ammoType, ammoRequired);
|
|
}
|
|
|
|
// set the shader parm to the time of last projectile firing,
|
|
// which the gun material shaders can reference for single shot barrel glows, etc
|
|
renderEntity.shaderParms[SHADERPARM_DIVERSITY] = gameLocal.random.CRandomFloat();
|
|
renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC(gameLocal.time);
|
|
|
|
if (worldModel.GetEntity())
|
|
{
|
|
worldModel.GetEntity()->SetShaderParm(SHADERPARM_DIVERSITY, renderEntity.shaderParms[SHADERPARM_DIVERSITY]);
|
|
worldModel.GetEntity()->SetShaderParm(SHADERPARM_TIMEOFFSET, renderEntity.shaderParms[SHADERPARM_TIMEOFFSET]);
|
|
}
|
|
|
|
// add the light for the muzzleflash
|
|
if (!lightOn)
|
|
{
|
|
MuzzleFlashLight();
|
|
}
|
|
|
|
//owner->Give( powerup, va( "%f", duration ), ITEM_GIVE_FEEDBACK | ITEM_GIVE_UPDATE_STATE | ITEM_GIVE_FROM_WEAPON );
|
|
|
|
|
|
}
|
|
|
|
void idWeapon::Event_StartWeaponSmoke()
|
|
{
|
|
|
|
// reset muzzle smoke
|
|
weaponSmokeStartTime = gameLocal.time;
|
|
}
|
|
|
|
void idWeapon::Event_StopWeaponSmoke()
|
|
{
|
|
|
|
// reset muzzle smoke
|
|
weaponSmokeStartTime = 0;
|
|
}
|
|
|
|
void idWeapon::Event_StartWeaponParticle(const char* name)
|
|
{
|
|
WeaponParticle_t* part;
|
|
weaponParticles.Get(name, &part);
|
|
if (part)
|
|
{
|
|
part->active = true;
|
|
part->startTime = gameLocal.time;
|
|
|
|
//Toggle the emitter
|
|
if (!part->smoke && part->emitter != NULL)
|
|
{
|
|
part->emitter->Show();
|
|
part->emitter->PostEventMS(&EV_Activate, 0, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void idWeapon::Event_StopWeaponParticle(const char* name)
|
|
{
|
|
WeaponParticle_t* part;
|
|
weaponParticles.Get(name, &part);
|
|
if (part)
|
|
{
|
|
part->active = false;
|
|
part->startTime = 0;
|
|
|
|
//Toggle the emitter
|
|
if (!part->smoke)
|
|
{
|
|
if (part->emitter != NULL)
|
|
{
|
|
part->emitter->Hide();
|
|
part->emitter->PostEventMS(&EV_Activate, 0, this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void idWeapon::Event_StartWeaponLight(const char* name)
|
|
{
|
|
WeaponLight_t* light;
|
|
weaponLights.Get(name, &light);
|
|
if (light)
|
|
{
|
|
light->active = true;
|
|
light->startTime = gameLocal.time;
|
|
}
|
|
}
|
|
|
|
void idWeapon::Event_StopWeaponLight(const char* name)
|
|
{
|
|
WeaponLight_t* light;
|
|
weaponLights.Get(name, &light);
|
|
if (light)
|
|
{
|
|
light->active = false;
|
|
light->startTime = 0;
|
|
if (light->lightHandle != -1)
|
|
{
|
|
gameRenderWorld->FreeLightDef(light->lightHandle);
|
|
light->lightHandle = -1;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
=====================
|
|
idWeapon::Event_Melee
|
|
=====================
|
|
*/
|
|
void idWeapon::Event_Melee()
|
|
{
|
|
idEntity* ent;
|
|
trace_t tr;
|
|
|
|
if (weaponDef == NULL)
|
|
{
|
|
gameLocal.Error("No weaponDef on '%s'", this->GetName());
|
|
return;
|
|
}
|
|
|
|
if (meleeDef == NULL)
|
|
{
|
|
gameLocal.Error("No meleeDef on '%s'", weaponDef->dict.GetString("classname"));
|
|
return;
|
|
}
|
|
|
|
if (!gameLocal.isClient)
|
|
{
|
|
idVec3 start = playerViewOrigin;
|
|
idVec3 end = start + playerViewAxis[0] * (meleeDistance * owner->PowerUpModifier(MELEE_DISTANCE));
|
|
gameLocal.clip.TracePoint(tr, start, end, MASK_SHOT_RENDERMODEL, owner);
|
|
if (tr.fraction < 1.0f)
|
|
{
|
|
ent = gameLocal.GetTraceEntity(tr);
|
|
}
|
|
else
|
|
{
|
|
ent = NULL;
|
|
}
|
|
|
|
if (g_debugWeapon.GetBool())
|
|
{
|
|
gameRenderWorld->DebugLine(colorYellow, start, end, 100);
|
|
if (ent != NULL)
|
|
{
|
|
gameRenderWorld->DebugBounds(colorRed, ent->GetPhysics()->GetBounds(), ent->GetPhysics()->GetOrigin(), 100);
|
|
}
|
|
}
|
|
|
|
bool hit = false;
|
|
const char* hitSound = meleeDef->dict.GetString("snd_miss");
|
|
|
|
if (ent != NULL)
|
|
{
|
|
|
|
float push = meleeDef->dict.GetFloat("push");
|
|
idVec3 impulse = -push * owner->PowerUpModifier(SPEED) * tr.c.normal;
|
|
|
|
if (gameLocal.world->spawnArgs.GetBool("no_Weapons") && (ent->IsType(idActor::Type) || ent->IsType(idAFAttachment::Type)))
|
|
{
|
|
idThread::ReturnInt(0);
|
|
return;
|
|
}
|
|
|
|
ent->ApplyImpulse(this, tr.c.id, tr.c.point, impulse);
|
|
|
|
// weapon stealing - do this before damaging so weapons are not dropped twice
|
|
if (gameLocal.isMultiplayer
|
|
&& weaponDef->dict.GetBool("stealing")
|
|
&& ent->IsType(idPlayer::Type)
|
|
&& !owner->PowerUpActive(BERSERK)
|
|
&& ((gameLocal.gameType != GAME_TDM) || gameLocal.serverInfo.GetBool("si_teamDamage") || (owner->team != static_cast<idPlayer*>(ent)->team))
|
|
)
|
|
{
|
|
|
|
owner->StealWeapon(static_cast<idPlayer*>(ent));
|
|
}
|
|
|
|
if (ent->fl.takedamage)
|
|
{
|
|
idVec3 kickDir, globalKickDir;
|
|
meleeDef->dict.GetVector("kickDir", "0 0 0", kickDir);
|
|
globalKickDir = muzzleAxis * kickDir;
|
|
//Adjust the melee powerup modifier for the invulnerability boss fight
|
|
float mod = owner->PowerUpModifier(MELEE_DAMAGE);
|
|
if (!strcmp(ent->GetEntityDefName(), "monster_hunter_invul"))
|
|
{
|
|
//Only do a quater of the damage mod
|
|
mod *= 0.25f;
|
|
}
|
|
ent->Damage(owner, owner, globalKickDir, meleeDefName, mod, tr.c.id);
|
|
hit = true;
|
|
}
|
|
|
|
if (weaponDef->dict.GetBool("impact_damage_effect"))
|
|
{
|
|
|
|
if (ent->spawnArgs.GetBool("bleed"))
|
|
{
|
|
|
|
hitSound = meleeDef->dict.GetString(owner->PowerUpActive(BERSERK) ? "snd_hit_berserk" : "snd_hit");
|
|
|
|
ent->AddDamageEffect(tr, impulse, meleeDef->dict.GetString("classname"));
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
int type = tr.c.material->GetSurfaceType();
|
|
if (type == SURFTYPE_NONE)
|
|
{
|
|
type = GetDefaultSurfaceType();
|
|
}
|
|
|
|
const char* materialType = gameLocal.sufaceTypeNames[type];
|
|
|
|
// start impact sound based on material type
|
|
hitSound = meleeDef->dict.GetString(va("snd_%s", materialType));
|
|
if (*hitSound == '\0')
|
|
{
|
|
hitSound = meleeDef->dict.GetString("snd_metal");
|
|
}
|
|
|
|
if (gameLocal.time > nextStrikeFx)
|
|
{
|
|
const char* decal;
|
|
// project decal
|
|
decal = weaponDef->dict.GetString("mtr_strike");
|
|
if (decal != NULL && *decal != NULL)
|
|
{
|
|
gameLocal.ProjectDecal(tr.c.point, -tr.c.normal, 8.0f, true, 6.0, decal);
|
|
}
|
|
nextStrikeFx = gameLocal.time + 200;
|
|
}
|
|
else
|
|
{
|
|
hitSound = "";
|
|
}
|
|
|
|
strikeSmokeStartTime = gameLocal.time;
|
|
strikePos = tr.c.point;
|
|
strikeAxis = -tr.endAxis;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*hitSound != '\0')
|
|
{
|
|
const idSoundShader* snd = declManager->FindSound(hitSound);
|
|
StartSoundShader(snd, SND_CHANNEL_BODY2, 0, true, NULL);
|
|
}
|
|
|
|
idThread::ReturnInt(hit);
|
|
owner->WeaponFireFeedback(&weaponDef->dict);
|
|
return;
|
|
}
|
|
|
|
idThread::ReturnInt(0);
|
|
owner->WeaponFireFeedback(&weaponDef->dict);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idWeapon::Event_GetWorldModel
|
|
=====================
|
|
*/
|
|
void idWeapon::Event_GetWorldModel()
|
|
{
|
|
idThread::ReturnEntity(worldModel.GetEntity());
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idWeapon::Event_AllowDrop
|
|
=====================
|
|
*/
|
|
void idWeapon::Event_AllowDrop(int allow)
|
|
{
|
|
if (allow)
|
|
{
|
|
allowDrop = true;
|
|
}
|
|
else
|
|
{
|
|
allowDrop = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idWeapon::CallNativeEvent
|
|
=====================
|
|
*/
|
|
void idWeapon::CallNativeEvent(idStr& name)
|
|
{
|
|
if (name == "EjectBrass")
|
|
{
|
|
Event_EjectBrass();
|
|
}
|
|
else
|
|
{
|
|
gameLocal.Error("Unknown idWeapon NativeEvent type\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_EjectBrass
|
|
|
|
Toss a shell model out from the breach if the bone is present
|
|
================
|
|
*/
|
|
void idWeapon::Event_EjectBrass()
|
|
{
|
|
if (!g_showBrass.GetBool() || !owner->CanShowWeaponViewmodel())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ejectJointView == INVALID_JOINT || !brassDict.GetNumKeyVals())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (gameLocal.isClient)
|
|
{
|
|
return;
|
|
}
|
|
|
|
idMat3 axis;
|
|
idVec3 origin, linear_velocity, angular_velocity;
|
|
idEntity* ent;
|
|
|
|
if (!GetGlobalJointTransform(true, ejectJointView, origin, axis))
|
|
{
|
|
return;
|
|
}
|
|
|
|
gameLocal.SpawnEntityDef(brassDict, &ent, false);
|
|
if (!ent || !ent->IsType(idDebris::Type))
|
|
{
|
|
gameLocal.Error("'%s' is not an idDebris", weaponDef ? weaponDef->dict.GetString("def_ejectBrass") : "def_ejectBrass");
|
|
}
|
|
idDebris* debris = static_cast<idDebris*>(ent);
|
|
debris->Create(owner, origin, axis);
|
|
debris->Launch();
|
|
|
|
linear_velocity = 40 * (playerViewAxis[0] + playerViewAxis[1] + playerViewAxis[2]);
|
|
angular_velocity.Set(10 * gameLocal.random.CRandomFloat(), 10 * gameLocal.random.CRandomFloat(), 10 * gameLocal.random.CRandomFloat());
|
|
|
|
debris->GetPhysics()->SetLinearVelocity(linear_velocity);
|
|
debris->GetPhysics()->SetAngularVelocity(angular_velocity);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_IsInvisible
|
|
===============
|
|
*/
|
|
bool idWeapon::Event_IsInvisible(void)
|
|
{
|
|
if (!owner)
|
|
{
|
|
return false;
|
|
}
|
|
return owner->PowerUpActive(INVISIBILITY);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::ClientThink
|
|
===============
|
|
*/
|
|
void idWeapon::ClientThink(const int curTime, const float fraction, const bool predict)
|
|
{
|
|
UpdateAnimation();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::ClientPredictionThink
|
|
===============
|
|
*/
|
|
void idWeapon::ClientPredictionThink()
|
|
{
|
|
UpdateAnimation();
|
|
}
|
|
|
|
|
|
void idWeapon::ForceAmmoInClip()
|
|
{
|
|
ammoClip = clipSize;
|
|
} |