2022-12-17 13:32:43 +01:00
|
|
|
/***
|
2013-08-30 13:34:05 -07:00
|
|
|
*
|
|
|
|
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
|
|
|
|
*
|
|
|
|
* This product contains software technology licensed from Id
|
|
|
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
|
|
|
* All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use, distribution, and modification of this source code and/or resulting
|
|
|
|
* object code is restricted to non-commercial enhancements to products from
|
|
|
|
* Valve LLC. All other use, distribution, or modification is prohibited
|
|
|
|
* without written permission from Valve LLC.
|
|
|
|
*
|
|
|
|
****/
|
|
|
|
/*
|
|
|
|
|
|
|
|
===== bmodels.cpp ========================================================
|
|
|
|
|
|
|
|
spawn, think, and use functions for entities that use brush models
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "extdll.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "cbase.h"
|
|
|
|
#include "doors.h"
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
#define SF_BRUSH_ACCDCC 16 // brush should accelerate and decelerate when toggled
|
|
|
|
#define SF_BRUSH_HURT 32 // rotating brush that inflicts pain based on rotation speed
|
|
|
|
#define SF_ROTATING_NOT_SOLID 64 // some special rotating objects are not solid.
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// covering cheesy noise1, noise2, & noise3 fields so they make more sense (for rotating fans)
|
2021-11-28 16:54:48 +01:00
|
|
|
#define noiseStart noise1
|
|
|
|
#define noiseStop noise2
|
|
|
|
#define noiseRunning noise3
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
#define SF_PENDULUM_SWING 2 // spawnflag that makes a pendulum a rope swing.
|
2013-08-30 13:34:05 -07:00
|
|
|
//
|
|
|
|
// BModelOrigin - calculates origin of a bmodel from absmin/size because all bmodel origins are 0 0 0
|
|
|
|
//
|
2021-11-28 16:54:48 +01:00
|
|
|
Vector VecBModelOrigin(entvars_t* pevBModel)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
return pevBModel->absmin + (pevBModel->size * 0.5);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// =================== FUNC_WALL ==============================================
|
|
|
|
|
|
|
|
/*QUAKED func_wall (0 .5 .8) ?
|
|
|
|
This is just a solid wall if not inhibited
|
|
|
|
*/
|
|
|
|
class CFuncWall : public CBaseEntity
|
|
|
|
{
|
|
|
|
public:
|
2021-11-28 16:54:48 +01:00
|
|
|
void Spawn() override;
|
|
|
|
void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value) override;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// Bmodels don't go across transitions
|
2021-11-29 20:31:17 +01:00
|
|
|
int ObjectCaps() override { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(func_wall, CFuncWall);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncWall::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->angles = g_vecZero;
|
|
|
|
pev->movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything
|
|
|
|
pev->solid = SOLID_BSP;
|
|
|
|
SET_MODEL(ENT(pev), STRING(pev->model));
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// If it can't move/go away, it's really part of the world
|
|
|
|
pev->flags |= FL_WORLDBRUSH;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncWall::Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (ShouldToggle(useType, pev->frame != 0))
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->frame = 1 - pev->frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
#define SF_WALL_START_OFF 0x0001
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
class CFuncWallToggle : public CFuncWall
|
|
|
|
{
|
|
|
|
public:
|
2021-11-28 16:54:48 +01:00
|
|
|
void Spawn() override;
|
|
|
|
void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value) override;
|
|
|
|
void TurnOff();
|
|
|
|
void TurnOn();
|
|
|
|
bool IsOn();
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(func_wall_toggle, CFuncWallToggle);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncWallToggle::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
CFuncWall::Spawn();
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((pev->spawnflags & SF_WALL_START_OFF) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
TurnOff();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncWallToggle::TurnOff()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->solid = SOLID_NOT;
|
|
|
|
pev->effects |= EF_NODRAW;
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_SetOrigin(pev, pev->origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncWallToggle::TurnOn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->solid = SOLID_BSP;
|
|
|
|
pev->effects &= ~EF_NODRAW;
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_SetOrigin(pev, pev->origin);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
bool CFuncWallToggle::IsOn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->solid == SOLID_NOT)
|
2021-11-19 13:43:33 +01:00
|
|
|
return false;
|
2021-11-19 13:45:16 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncWallToggle::Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 15:32:26 +01:00
|
|
|
bool status = IsOn();
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (ShouldToggle(useType, status))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (status)
|
2013-08-30 13:34:05 -07:00
|
|
|
TurnOff();
|
|
|
|
else
|
|
|
|
TurnOn();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
#define SF_CONVEYOR_VISUAL 0x0001
|
|
|
|
#define SF_CONVEYOR_NOTSOLID 0x0002
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
class CFuncConveyor : public CFuncWall
|
|
|
|
{
|
|
|
|
public:
|
2021-11-28 16:54:48 +01:00
|
|
|
void Spawn() override;
|
|
|
|
void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value) override;
|
|
|
|
void UpdateSpeed(float speed);
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(func_conveyor, CFuncConveyor);
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncConveyor::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
SetMovedir(pev);
|
2013-08-30 13:34:05 -07:00
|
|
|
CFuncWall::Spawn();
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((pev->spawnflags & SF_CONVEYOR_VISUAL) == 0)
|
|
|
|
SetBits(pev->flags, FL_CONVEYOR);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// HACKHACK - This is to allow for some special effects
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((pev->spawnflags & SF_CONVEYOR_NOTSOLID) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->solid = SOLID_NOT;
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->skin = 0; // Don't want the engine thinking we've got special contents on this brush
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->speed == 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->speed = 100;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
UpdateSpeed(pev->speed);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// HACKHACK -- This is ugly, but encode the speed in the rendercolor to avoid adding more data to the network stream
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncConveyor::UpdateSpeed(float speed)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// Encode it as an integer with 4 fractional bits
|
|
|
|
int speedCode = (int)(fabs(speed) * 16.0);
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (speed < 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->rendercolor.x = 1;
|
|
|
|
else
|
|
|
|
pev->rendercolor.x = 0;
|
|
|
|
|
|
|
|
pev->rendercolor.y = (speedCode >> 8);
|
|
|
|
pev->rendercolor.z = (speedCode & 0xFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncConveyor::Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->speed = -pev->speed;
|
|
|
|
UpdateSpeed(pev->speed);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// =================== FUNC_ILLUSIONARY ==============================================
|
|
|
|
|
|
|
|
|
|
|
|
/*QUAKED func_illusionary (0 .5 .8) ?
|
|
|
|
A simple entity that looks solid but lets you walk through it.
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
class CFuncIllusionary : public CBaseToggle
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
public:
|
2021-03-05 23:07:22 +01:00
|
|
|
void Spawn() override;
|
2021-11-28 16:54:48 +01:00
|
|
|
void EXPORT SloshTouch(CBaseEntity* pOther);
|
|
|
|
bool KeyValue(KeyValueData* pkvd) override;
|
2021-11-29 20:31:17 +01:00
|
|
|
int ObjectCaps() override { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(func_illusionary, CFuncIllusionary);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
bool CFuncIllusionary::KeyValue(KeyValueData* pkvd)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FStrEq(pkvd->szKeyName, "skin")) //skin is used for content type
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->skin = atof(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 15:32:26 +01:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
return CBaseToggle::KeyValue(pkvd);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncIllusionary::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->angles = g_vecZero;
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->movetype = MOVETYPE_NONE;
|
|
|
|
pev->solid = SOLID_NOT; // always solid_not
|
|
|
|
SET_MODEL(ENT(pev), STRING(pev->model));
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// I'd rather eat the network bandwidth of this than figure out how to save/restore
|
|
|
|
// these entities after they have been moved to the client, or respawn them ala Quake
|
|
|
|
// Perhaps we can do this in deathmatch only.
|
|
|
|
// MAKE_STATIC(ENT(pev));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Monster only clip brush
|
2021-11-28 16:54:48 +01:00
|
|
|
//
|
2013-08-30 13:34:05 -07:00
|
|
|
// This brush will be solid for any entity who has the FL_MONSTERCLIP flag set
|
|
|
|
// in pev->flags
|
|
|
|
//
|
2021-11-28 16:54:48 +01:00
|
|
|
// otherwise it will be invisible and not solid. This can be used to keep
|
2013-08-30 13:34:05 -07:00
|
|
|
// specific monsters out of certain areas
|
|
|
|
//
|
|
|
|
// -------------------------------------------------------------------------------
|
|
|
|
class CFuncMonsterClip : public CFuncWall
|
|
|
|
{
|
|
|
|
public:
|
2021-11-28 16:54:48 +01:00
|
|
|
void Spawn() override;
|
|
|
|
void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value) override {} // Clear out func_wall's use function
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(func_monsterclip, CFuncMonsterClip);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-03-05 20:54:33 +01:00
|
|
|
void CFuncMonsterClip::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
CFuncWall::Spawn();
|
2021-11-28 16:54:48 +01:00
|
|
|
if (CVAR_GET_FLOAT("showtriggers") == 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->effects = EF_NODRAW;
|
|
|
|
pev->flags |= FL_MONSTERCLIP;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// =================== FUNC_ROTATING ==============================================
|
|
|
|
class CFuncRotating : public CBaseEntity
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
// basic functions
|
2021-03-05 23:07:22 +01:00
|
|
|
void Spawn() override;
|
|
|
|
void Precache() override;
|
2021-11-28 16:54:48 +01:00
|
|
|
void EXPORT SpinUp();
|
|
|
|
void EXPORT SpinDown();
|
|
|
|
bool KeyValue(KeyValueData* pkvd) override;
|
|
|
|
void EXPORT HurtTouch(CBaseEntity* pOther);
|
|
|
|
void EXPORT RotatingUse(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value);
|
2021-03-05 20:54:33 +01:00
|
|
|
void EXPORT Rotate();
|
2021-11-28 16:54:48 +01:00
|
|
|
void RampPitchVol(bool fUp);
|
|
|
|
void Blocked(CBaseEntity* pOther) override;
|
2021-11-29 20:31:17 +01:00
|
|
|
int ObjectCaps() override { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
2021-11-28 16:54:48 +01:00
|
|
|
bool Save(CSave& save) override;
|
|
|
|
bool Restore(CRestore& restore) override;
|
|
|
|
|
|
|
|
static TYPEDESCRIPTION m_SaveData[];
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
float m_flFanFriction;
|
|
|
|
float m_flAttenuation;
|
|
|
|
float m_flVolume;
|
|
|
|
float m_pitch;
|
2021-11-28 16:54:48 +01:00
|
|
|
int m_sounds;
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
TYPEDESCRIPTION CFuncRotating::m_SaveData[] =
|
|
|
|
{
|
|
|
|
DEFINE_FIELD(CFuncRotating, m_flFanFriction, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CFuncRotating, m_flAttenuation, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CFuncRotating, m_flVolume, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CFuncRotating, m_pitch, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CFuncRotating, m_sounds, FIELD_INTEGER)};
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
IMPLEMENT_SAVERESTORE(CFuncRotating, CBaseEntity);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(func_rotating, CFuncRotating);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
bool CFuncRotating::KeyValue(KeyValueData* pkvd)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
if (FStrEq(pkvd->szKeyName, "fanfriction"))
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
m_flFanFriction = atof(pkvd->szValue) / 100;
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "Volume"))
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
m_flVolume = atof(pkvd->szValue) / 10.0;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
if (m_flVolume > 1.0)
|
|
|
|
m_flVolume = 1.0;
|
|
|
|
if (m_flVolume < 0.0)
|
|
|
|
m_flVolume = 0.0;
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "spawnorigin"))
|
|
|
|
{
|
|
|
|
Vector tmp;
|
2021-11-28 16:54:48 +01:00
|
|
|
UTIL_StringToVector((float*)tmp, pkvd->szValue);
|
|
|
|
if (tmp != g_vecZero)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->origin = tmp;
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "sounds"))
|
|
|
|
{
|
|
|
|
m_sounds = atoi(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 15:32:26 +01:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
return CBaseEntity::KeyValue(pkvd);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*QUAKED func_rotating (0 .5 .8) ? START_ON REVERSE X_AXIS Y_AXIS
|
|
|
|
You need to have an origin brush as part of this entity. The
|
|
|
|
center of that brush will be
|
|
|
|
the point around which it is rotated. It will rotate around the Z
|
|
|
|
axis by default. You can
|
|
|
|
check either the X_AXIS or Y_AXIS box to change that.
|
|
|
|
|
|
|
|
"speed" determines how fast it moves; default value is 100.
|
|
|
|
"dmg" damage to inflict when blocked (2 default)
|
|
|
|
|
|
|
|
REVERSE will cause the it to rotate in the opposite direction.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncRotating::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// set final pitch. Must not be PITCH_NORM, since we
|
|
|
|
// plan on pitch shifting later.
|
|
|
|
|
|
|
|
m_pitch = PITCH_NORM - 1;
|
|
|
|
|
|
|
|
// maintain compatibility with previous maps
|
|
|
|
if (m_flVolume == 0.0)
|
|
|
|
m_flVolume = 1.0;
|
|
|
|
|
|
|
|
// if the designer didn't set a sound attenuation, default to one.
|
|
|
|
m_flAttenuation = ATTN_NORM;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
if (FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_SMALLRADIUS))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
m_flAttenuation = ATTN_IDLE;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_MEDIUMRADIUS))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
m_flAttenuation = ATTN_STATIC;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_LARGERADIUS))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
m_flAttenuation = ATTN_NORM;
|
|
|
|
}
|
|
|
|
|
|
|
|
// prevent divide by zero if level designer forgets friction!
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_flFanFriction == 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
m_flFanFriction = 1;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
if (FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_Z_AXIS))
|
|
|
|
pev->movedir = Vector(0, 0, 1);
|
|
|
|
else if (FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_X_AXIS))
|
|
|
|
pev->movedir = Vector(1, 0, 0);
|
|
|
|
else
|
|
|
|
pev->movedir = Vector(0, 1, 0); // y-axis
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// check for reverse rotation
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_BACKWARDS))
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->movedir = pev->movedir * -1;
|
|
|
|
|
|
|
|
// some rotating objects like fake volumetric lights will not be solid.
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pev->spawnflags, SF_ROTATING_NOT_SOLID))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->solid = SOLID_NOT;
|
|
|
|
pev->skin = CONTENTS_EMPTY;
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->movetype = MOVETYPE_PUSH;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->solid = SOLID_BSP;
|
|
|
|
pev->movetype = MOVETYPE_PUSH;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
UTIL_SetOrigin(pev, pev->origin);
|
2021-11-28 16:54:48 +01:00
|
|
|
SET_MODEL(ENT(pev), STRING(pev->model));
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
SetUse(&CFuncRotating::RotatingUse);
|
2013-08-30 13:34:05 -07:00
|
|
|
// did level designer forget to assign speed?
|
|
|
|
if (pev->speed <= 0)
|
|
|
|
pev->speed = 0;
|
|
|
|
|
|
|
|
// Removed this per level designers request. -- JAY
|
|
|
|
// if (pev->dmg == 0)
|
|
|
|
// pev->dmg = 2;
|
|
|
|
|
|
|
|
// instant-use brush?
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_INSTANT))
|
|
|
|
{
|
|
|
|
SetThink(&CFuncRotating::SUB_CallUseToggle);
|
|
|
|
pev->nextthink = pev->ltime + 1.5; // leave a magic delay for client to start up
|
|
|
|
}
|
2013-08-30 13:34:05 -07:00
|
|
|
// can this brush inflict pain?
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pev->spawnflags, SF_BRUSH_HURT))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
SetTouch(&CFuncRotating::HurtTouch);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
Precache();
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncRotating::Precache()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
char* szSoundFile = (char*)STRING(pev->message);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// set up fan sounds
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->message) && strlen(szSoundFile) > 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// if a path is set for a wave, use it
|
|
|
|
|
|
|
|
PRECACHE_SOUND(szSoundFile);
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseRunning = ALLOC_STRING(szSoundFile);
|
2021-11-28 16:54:48 +01:00
|
|
|
}
|
|
|
|
else
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// otherwise use preset sound
|
|
|
|
switch (m_sounds)
|
|
|
|
{
|
|
|
|
case 1:
|
2021-11-28 16:54:48 +01:00
|
|
|
PRECACHE_SOUND("fans/fan1.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseRunning = ALLOC_STRING("fans/fan1.wav");
|
|
|
|
break;
|
|
|
|
case 2:
|
2021-11-28 16:54:48 +01:00
|
|
|
PRECACHE_SOUND("fans/fan2.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseRunning = ALLOC_STRING("fans/fan2.wav");
|
|
|
|
break;
|
|
|
|
case 3:
|
2021-11-28 16:54:48 +01:00
|
|
|
PRECACHE_SOUND("fans/fan3.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseRunning = ALLOC_STRING("fans/fan3.wav");
|
|
|
|
break;
|
|
|
|
case 4:
|
2021-11-28 16:54:48 +01:00
|
|
|
PRECACHE_SOUND("fans/fan4.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseRunning = ALLOC_STRING("fans/fan4.wav");
|
|
|
|
break;
|
|
|
|
case 5:
|
2021-11-28 16:54:48 +01:00
|
|
|
PRECACHE_SOUND("fans/fan5.wav");
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseRunning = ALLOC_STRING("fans/fan5.wav");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
default:
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FStringNull(pev->message) && strlen(szSoundFile) > 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
PRECACHE_SOUND(szSoundFile);
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->noiseRunning = ALLOC_STRING(szSoundFile);
|
|
|
|
break;
|
2021-11-28 16:54:48 +01:00
|
|
|
}
|
|
|
|
else
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->noiseRunning = ALLOC_STRING("common/null.wav");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
if (pev->avelocity != g_vecZero)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// if fan was spinning, and we went through transition or save/restore,
|
|
|
|
// make sure we restart the sound. 1.5 sec delay is magic number. KDB
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CFuncRotating::SpinUp);
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->nextthink = pev->ltime + 1.5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Touch - will hurt others based on how fast the brush is spinning
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncRotating::HurtTouch(CBaseEntity* pOther)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
entvars_t* pevOther = pOther->pev;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// we can't hurt this thing, so we're not concerned with it
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 == pevOther->takedamage)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
// calculate damage based on rotation speed
|
|
|
|
pev->dmg = pev->avelocity.Length() / 10;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
pOther->TakeDamage(pev, pev, pev->dmg, DMG_CRUSH);
|
|
|
|
|
|
|
|
pevOther->velocity = (pevOther->origin - VecBModelOrigin(pev)).Normalize() * pev->dmg;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// RampPitchVol - ramp pitch and volume up to final values, based on difference
|
|
|
|
// between how fast we're going vs how fast we plan to go
|
|
|
|
//
|
2021-11-28 16:54:48 +01:00
|
|
|
#define FANPITCHMIN 30
|
|
|
|
#define FANPITCHMAX 100
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncRotating::RampPitchVol(bool fUp)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
|
|
|
|
Vector vecAVel = pev->avelocity;
|
|
|
|
vec_t vecCur;
|
|
|
|
vec_t vecFinal;
|
|
|
|
float fpct;
|
|
|
|
float fvol;
|
|
|
|
float fpitch;
|
|
|
|
int pitch;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// get current angular velocity
|
|
|
|
|
2018-09-02 22:24:42 +02:00
|
|
|
vecCur = fabs(vecAVel.x != 0 ? vecAVel.x : (vecAVel.y != 0 ? vecAVel.y : vecAVel.z));
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// get target angular velocity
|
|
|
|
|
|
|
|
vecFinal = (pev->movedir.x != 0 ? pev->movedir.x : (pev->movedir.y != 0 ? pev->movedir.y : pev->movedir.z));
|
|
|
|
vecFinal *= pev->speed;
|
2018-09-02 22:24:42 +02:00
|
|
|
vecFinal = fabs(vecFinal);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// calc volume and pitch as % of final vol and pitch
|
|
|
|
|
|
|
|
fpct = vecCur / vecFinal;
|
2021-11-28 16:54:48 +01:00
|
|
|
// if (fUp)
|
|
|
|
// fvol = m_flVolume * (0.5 + fpct/2.0); // spinup volume ramps up from 50% max vol
|
|
|
|
// else
|
|
|
|
fvol = m_flVolume * fpct; // slowdown volume ramps down to 0
|
|
|
|
|
|
|
|
fpitch = FANPITCHMIN + (FANPITCHMAX - FANPITCHMIN) * fpct;
|
|
|
|
|
|
|
|
pitch = (int)fpitch;
|
2013-08-30 13:34:05 -07:00
|
|
|
if (pitch == PITCH_NORM)
|
2021-11-28 16:54:48 +01:00
|
|
|
pitch = PITCH_NORM - 1;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// change the fan's vol and pitch
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseRunning),
|
2013-08-30 13:34:05 -07:00
|
|
|
fvol, m_flAttenuation, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// SpinUp - accelerates a non-moving func_rotating up to it's speed
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncRotating::SpinUp()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
Vector vecAVel; //rotational velocity
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
pev->nextthink = pev->ltime + 0.1;
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->avelocity = pev->avelocity + (pev->movedir * (pev->speed * m_flFanFriction));
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
vecAVel = pev->avelocity; // cache entity's rotational velocity
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// if we've met or exceeded target speed, set target speed and stop thinking
|
2021-11-28 16:54:48 +01:00
|
|
|
if (fabs(vecAVel.x) >= fabs(pev->movedir.x * pev->speed) &&
|
|
|
|
fabs(vecAVel.y) >= fabs(pev->movedir.y * pev->speed) &&
|
|
|
|
fabs(vecAVel.z) >= fabs(pev->movedir.z * pev->speed))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->avelocity = pev->movedir * pev->speed; // set speed in case we overshot
|
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseRunning),
|
2013-08-30 13:34:05 -07:00
|
|
|
m_flVolume, m_flAttenuation, SND_CHANGE_PITCH | SND_CHANGE_VOL, FANPITCHMAX);
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
SetThink(&CFuncRotating::Rotate);
|
2013-08-30 13:34:05 -07:00
|
|
|
Rotate();
|
2021-11-28 16:54:48 +01:00
|
|
|
}
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
|
|
|
{
|
2021-11-19 13:45:16 +01:00
|
|
|
RampPitchVol(true);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// SpinDown - decelerates a moving func_rotating to a standstill.
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncRotating::SpinDown()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
Vector vecAVel; //rotational velocity
|
2013-08-30 13:34:05 -07:00
|
|
|
vec_t vecdir;
|
|
|
|
|
|
|
|
pev->nextthink = pev->ltime + 0.1;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->avelocity = pev->avelocity - (pev->movedir * (pev->speed * m_flFanFriction)); //spin down slower than spinup
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
vecAVel = pev->avelocity; // cache entity's rotational velocity
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
if (pev->movedir.x != 0)
|
|
|
|
vecdir = pev->movedir.x;
|
|
|
|
else if (pev->movedir.y != 0)
|
|
|
|
vecdir = pev->movedir.y;
|
|
|
|
else
|
|
|
|
vecdir = pev->movedir.z;
|
|
|
|
|
|
|
|
// if we've met or exceeded target speed, set target speed and stop thinking
|
|
|
|
// (note: must check for movedir > 0 or < 0)
|
2021-11-28 16:54:48 +01:00
|
|
|
if (((vecdir > 0) && (vecAVel.x <= 0 && vecAVel.y <= 0 && vecAVel.z <= 0)) ||
|
2013-08-30 13:34:05 -07:00
|
|
|
((vecdir < 0) && (vecAVel.x >= 0 && vecAVel.y >= 0 && vecAVel.z >= 0)))
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->avelocity = g_vecZero; // set speed in case we overshot
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// stop sound, we're done
|
2021-11-28 16:54:48 +01:00
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseRunning /* Stop */),
|
|
|
|
0, 0, SND_STOP, m_pitch);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CFuncRotating::Rotate);
|
2013-08-30 13:34:05 -07:00
|
|
|
Rotate();
|
2021-11-28 16:54:48 +01:00
|
|
|
}
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
|
|
|
{
|
2021-11-19 13:43:33 +01:00
|
|
|
RampPitchVol(false);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncRotating::Rotate()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->nextthink = pev->ltime + 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// Rotating Use - when a rotating brush is triggered
|
|
|
|
//=========================================================
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncRotating::RotatingUse(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// is this a brush that should accelerate and decelerate when turned on/off (fan)?
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pev->spawnflags, SF_BRUSH_ACCDCC))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// fan is spinning, so stop it.
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->avelocity != g_vecZero)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CFuncRotating::SpinDown);
|
|
|
|
//EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, (char *)STRING(pev->noiseStop),
|
2013-08-30 13:34:05 -07:00
|
|
|
// m_flVolume, m_flAttenuation, 0, m_pitch);
|
|
|
|
|
|
|
|
pev->nextthink = pev->ltime + 0.1;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else // fan is not moving, so start it
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CFuncRotating::SpinUp);
|
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseRunning),
|
2013-08-30 13:34:05 -07:00
|
|
|
0.01, m_flAttenuation, 0, FANPITCHMIN);
|
|
|
|
|
|
|
|
pev->nextthink = pev->ltime + 0.1;
|
|
|
|
}
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (!FBitSet(pev->spawnflags, SF_BRUSH_ACCDCC)) //this is a normal start/stop brush.
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->avelocity != g_vecZero)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// play stopping sound here
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CFuncRotating::SpinDown);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
// EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, (char *)STRING(pev->noiseStop),
|
2013-08-30 13:34:05 -07:00
|
|
|
// m_flVolume, m_flAttenuation, 0, m_pitch);
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->nextthink = pev->ltime + 0.1;
|
|
|
|
// pev->avelocity = g_vecZero;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char*)STRING(pev->noiseRunning),
|
2013-08-30 13:34:05 -07:00
|
|
|
m_flVolume, m_flAttenuation, 0, FANPITCHMAX);
|
|
|
|
pev->avelocity = pev->movedir * pev->speed;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CFuncRotating::Rotate);
|
2013-08-30 13:34:05 -07:00
|
|
|
Rotate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// RotatingBlocked - An entity has blocked the brush
|
|
|
|
//
|
2021-11-29 20:31:17 +01:00
|
|
|
void CFuncRotating::Blocked(CBaseEntity* pOther)
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pOther->TakeDamage(pev, pev, pev->dmg, DMG_CRUSH);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//#endif
|
|
|
|
|
|
|
|
|
|
|
|
class CPendulum : public CBaseEntity
|
|
|
|
{
|
|
|
|
public:
|
2021-11-28 16:54:48 +01:00
|
|
|
void Spawn() override;
|
|
|
|
bool KeyValue(KeyValueData* pkvd) override;
|
|
|
|
void EXPORT Swing();
|
|
|
|
void EXPORT PendulumUse(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value);
|
|
|
|
void EXPORT Stop();
|
|
|
|
void Touch(CBaseEntity* pOther) override;
|
|
|
|
void EXPORT RopeTouch(CBaseEntity* pOther); // this touch func makes the pendulum a rope
|
2021-11-29 20:31:17 +01:00
|
|
|
int ObjectCaps() override { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
2021-11-28 16:54:48 +01:00
|
|
|
bool Save(CSave& save) override;
|
|
|
|
bool Restore(CRestore& restore) override;
|
|
|
|
void Blocked(CBaseEntity* pOther) override;
|
|
|
|
|
|
|
|
static TYPEDESCRIPTION m_SaveData[];
|
|
|
|
|
|
|
|
float m_accel; // Acceleration
|
|
|
|
float m_distance; //
|
|
|
|
float m_time;
|
|
|
|
float m_damp;
|
|
|
|
float m_maxSpeed;
|
|
|
|
float m_dampSpeed;
|
|
|
|
Vector m_center;
|
|
|
|
Vector m_start;
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
LINK_ENTITY_TO_CLASS(func_pendulum, CPendulum);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
TYPEDESCRIPTION CPendulum::m_SaveData[] =
|
|
|
|
{
|
|
|
|
DEFINE_FIELD(CPendulum, m_accel, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CPendulum, m_distance, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CPendulum, m_time, FIELD_TIME),
|
|
|
|
DEFINE_FIELD(CPendulum, m_damp, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CPendulum, m_maxSpeed, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CPendulum, m_dampSpeed, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CPendulum, m_center, FIELD_VECTOR),
|
|
|
|
DEFINE_FIELD(CPendulum, m_start, FIELD_VECTOR),
|
2013-08-30 13:34:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
IMPLEMENT_SAVERESTORE(CPendulum, CBaseEntity);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
bool CPendulum::KeyValue(KeyValueData* pkvd)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
if (FStrEq(pkvd->szKeyName, "distance"))
|
|
|
|
{
|
|
|
|
m_distance = atof(pkvd->szValue);
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else if (FStrEq(pkvd->szKeyName, "damp"))
|
|
|
|
{
|
|
|
|
m_damp = atof(pkvd->szValue) * 0.001;
|
2021-11-28 15:32:26 +01:00
|
|
|
return true;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
2021-11-28 15:32:26 +01:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
return CBaseEntity::KeyValue(pkvd);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CPendulum::Spawn()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// set the axis of rotation
|
2021-11-29 20:31:17 +01:00
|
|
|
CBaseToggle::AxisDir(pev);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pev->spawnflags, SF_DOOR_PASSABLE))
|
|
|
|
pev->solid = SOLID_NOT;
|
2013-08-30 13:34:05 -07:00
|
|
|
else
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->solid = SOLID_BSP;
|
|
|
|
pev->movetype = MOVETYPE_PUSH;
|
2013-08-30 13:34:05 -07:00
|
|
|
UTIL_SetOrigin(pev, pev->origin);
|
2021-11-28 16:54:48 +01:00
|
|
|
SET_MODEL(ENT(pev), STRING(pev->model));
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_distance == 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (pev->speed == 0)
|
|
|
|
pev->speed = 100;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
m_accel = (pev->speed * pev->speed) / (2 * fabs(m_distance)); // Calculate constant acceleration from speed and distance
|
2013-08-30 13:34:05 -07:00
|
|
|
m_maxSpeed = pev->speed;
|
|
|
|
m_start = pev->angles;
|
|
|
|
m_center = pev->angles + (m_distance * 0.5) * pev->movedir;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_INSTANT))
|
|
|
|
{
|
|
|
|
SetThink(&CPendulum::SUB_CallUseToggle);
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->nextthink = gpGlobals->time + 0.1;
|
|
|
|
}
|
|
|
|
pev->speed = 0;
|
2021-11-28 16:54:48 +01:00
|
|
|
SetUse(&CPendulum::PendulumUse);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pev->spawnflags, SF_PENDULUM_SWING))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
SetTouch(&CPendulum::RopeTouch);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CPendulum::PendulumUse(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != pev->speed) // Pendulum is moving, stop it and auto-return if necessary
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (FBitSet(pev->spawnflags, SF_PENDULUM_AUTO_RETURN))
|
|
|
|
{
|
|
|
|
float delta;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
delta = CBaseToggle::AxisDelta(pev->spawnflags, pev->angles, m_start);
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
pev->avelocity = m_maxSpeed * pev->movedir;
|
|
|
|
pev->nextthink = pev->ltime + (delta / m_maxSpeed);
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(&CPendulum::Stop);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->speed = 0; // Dead stop
|
|
|
|
SetThink(NULL);
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->avelocity = g_vecZero;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
pev->nextthink = pev->ltime + 0.1; // Start the pendulum moving
|
|
|
|
m_time = gpGlobals->time; // Save time to calculate dt
|
|
|
|
SetThink(&CPendulum::Swing);
|
2013-08-30 13:34:05 -07:00
|
|
|
m_dampSpeed = m_maxSpeed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CPendulum::Stop()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->angles = m_start;
|
|
|
|
pev->speed = 0;
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(NULL);
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->avelocity = g_vecZero;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
void CPendulum::Blocked(CBaseEntity* pOther)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
m_time = gpGlobals->time;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CPendulum::Swing()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
float delta, dt;
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
delta = CBaseToggle::AxisDelta(pev->spawnflags, pev->angles, m_center);
|
2021-11-28 16:54:48 +01:00
|
|
|
dt = gpGlobals->time - m_time; // How much time has passed?
|
|
|
|
m_time = gpGlobals->time; // Remember the last time called
|
|
|
|
|
|
|
|
if (delta > 0 && m_accel > 0)
|
|
|
|
pev->speed -= m_accel * dt; // Integrate velocity
|
|
|
|
else
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->speed += m_accel * dt;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->speed > m_maxSpeed)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->speed = m_maxSpeed;
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (pev->speed < -m_maxSpeed)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->speed = -m_maxSpeed;
|
|
|
|
// scale the destdelta vector by the time spent traveling to get velocity
|
|
|
|
pev->avelocity = pev->speed * pev->movedir;
|
|
|
|
|
|
|
|
// Call this again
|
|
|
|
pev->nextthink = pev->ltime + 0.1;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 != m_damp)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
m_dampSpeed -= m_damp * m_dampSpeed * dt;
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_dampSpeed < 30.0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
pev->angles = m_center;
|
|
|
|
pev->speed = 0;
|
2021-11-28 16:54:48 +01:00
|
|
|
SetThink(NULL);
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->avelocity = g_vecZero;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (pev->speed > m_dampSpeed)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->speed = m_dampSpeed;
|
2021-11-28 16:54:48 +01:00
|
|
|
else if (pev->speed < -m_dampSpeed)
|
2013-08-30 13:34:05 -07:00
|
|
|
pev->speed = -m_dampSpeed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CPendulum::Touch(CBaseEntity* pOther)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
entvars_t* pevOther = pOther->pev;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (pev->dmg <= 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
// we can't hurt this thing, so we're not concerned with it
|
2021-11-28 16:54:48 +01:00
|
|
|
if (0 == pevOther->takedamage)
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
// calculate damage based on rotation speed
|
|
|
|
float damage = pev->dmg * pev->speed * 0.01;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (damage < 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
damage = -damage;
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
pOther->TakeDamage(pev, pev, damage, DMG_CRUSH);
|
|
|
|
|
|
|
|
pevOther->velocity = (pevOther->origin - VecBModelOrigin(pev)).Normalize() * damage;
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
2021-11-29 20:31:17 +01:00
|
|
|
void CPendulum::RopeTouch(CBaseEntity* pOther)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
entvars_t* pevOther = pOther->pev;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!pOther->IsPlayer())
|
|
|
|
{ // not a player!
|
|
|
|
ALERT(at_console, "Not a client\n");
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (ENT(pevOther) == pev->enemy)
|
|
|
|
{ // this player already on the rope.
|
2013-08-30 13:34:05 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pev->enemy = pOther->edict();
|
|
|
|
pevOther->velocity = g_vecZero;
|
|
|
|
pevOther->movetype = MOVETYPE_NONE;
|
|
|
|
}
|