You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
345 lines
7.8 KiB
345 lines
7.8 KiB
/*** |
|
* |
|
* Copyright (c) 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. |
|
* |
|
****/ |
|
|
|
#include "extdll.h" |
|
#include "util.h" |
|
#include "cbase.h" |
|
#include "monsters.h" |
|
#include "squadmonster.h" |
|
#include "player.h" |
|
#include "weapons.h" |
|
#include "decals.h" |
|
#include "gamerules.h" |
|
#include "effects.h" |
|
#include "saverestore.h" |
|
|
|
#define clamp( val, min, max ) ( ((val) > (max)) ? (max) : ( ((val) < (min)) ? (min) : (val) ) ) |
|
|
|
class CRopeSegment : public CBaseEntity |
|
{ |
|
public: |
|
int Save(CSave &save); |
|
int Restore(CRestore &restore); |
|
static TYPEDESCRIPTION m_SaveData[]; |
|
|
|
void Spawn(void); |
|
|
|
void EXPORT SegmentThink(void); |
|
void EXPORT SegmentTouch(CBaseEntity* pOther); |
|
|
|
CRopeSegment* m_pNext; |
|
CRopeSegment* m_pPrev; |
|
|
|
Vector m_vecJointPos; |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS(rope_segment, CRopeSegment); |
|
|
|
TYPEDESCRIPTION CRopeSegment::m_SaveData[] = |
|
{ |
|
DEFINE_FIELD(CRopeSegment, m_pNext, FIELD_CLASSPTR), |
|
DEFINE_FIELD(CRopeSegment, m_pPrev, FIELD_CLASSPTR), |
|
DEFINE_FIELD(CRopeSegment, m_vecJointPos, FIELD_POSITION_VECTOR), |
|
}; |
|
|
|
IMPLEMENT_SAVERESTORE(CRopeSegment, CBaseEntity); |
|
|
|
|
|
void CRopeSegment::Spawn(void) |
|
{ |
|
SET_MODEL(ENT(pev), "models/rope16.mdl"); |
|
|
|
pev->solid = SOLID_NOT; |
|
pev->movetype = MOVETYPE_NONE; |
|
pev->gravity = 0.0f; |
|
|
|
m_pPrev = NULL; |
|
m_pNext = NULL; |
|
m_vecJointPos = Vector(0, 0, 0); |
|
|
|
SetTouch(&CRopeSegment::Touch); |
|
SetThink(&CRopeSegment::SegmentThink); |
|
pev->nextthink = gpGlobals->time + 0.1f; |
|
} |
|
|
|
void CRopeSegment::SegmentThink(void) |
|
{ |
|
pev->nextthink = gpGlobals->time + 0.1f; |
|
} |
|
|
|
void CRopeSegment::SegmentTouch(CBaseEntity* pOther) |
|
{ |
|
ALERT(at_console, "Touched segment!\n"); |
|
|
|
SetTouch(NULL); |
|
} |
|
|
|
|
|
|
|
|
|
#define MAX_ROPE_SEGMENTS 64 |
|
|
|
class CRope : public CBaseEntity |
|
{ |
|
public: |
|
|
|
int Save(CSave &save); |
|
int Restore(CRestore &restore); |
|
static TYPEDESCRIPTION m_SaveData[]; |
|
|
|
void KeyValue(KeyValueData* pkvd); |
|
void Spawn(void); |
|
void Precache(void); |
|
|
|
void EXPORT StartupThink(void); |
|
void EXPORT RopeThink(void); |
|
|
|
void CreateSegments(); |
|
void DestroySegments(); |
|
|
|
int FindClosestSegment(Vector& vecTo, float epsilon, int iMin, int iMax); |
|
|
|
CRopeSegment* m_pSegments[MAX_ROPE_SEGMENTS]; |
|
|
|
int m_nSegments; |
|
BOOL m_fEnabled; |
|
|
|
string_t m_iszBodyModel; |
|
string_t m_iszEndingModel; |
|
|
|
void ApplyFunctor(void(*functor)(CRopeSegment* pSegment), int startIndex, int endIndex); |
|
void ApplyFunctor(void(*functor)(CRopeSegment* pSegment)); |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS(env_rope, CRope); |
|
|
|
TYPEDESCRIPTION CRope::m_SaveData[] = |
|
{ |
|
DEFINE_ARRAY(CRope, m_pSegments, FIELD_CLASSPTR, MAX_ROPE_SEGMENTS), |
|
DEFINE_FIELD(CRope, m_nSegments, FIELD_INTEGER), |
|
DEFINE_FIELD(CRope, m_fEnabled, FIELD_BOOLEAN), |
|
DEFINE_FIELD(CRope, m_iszBodyModel, FIELD_STRING), |
|
DEFINE_FIELD(CRope, m_iszEndingModel, FIELD_STRING), |
|
}; |
|
|
|
IMPLEMENT_SAVERESTORE(CRope, CBaseEntity); |
|
|
|
void CRope::KeyValue(KeyValueData* pkvd) |
|
{ |
|
if (FStrEq(pkvd->szKeyName, "segments")) |
|
{ |
|
m_nSegments = atoi(pkvd->szValue); |
|
pkvd->fHandled = TRUE; |
|
} |
|
else if (FStrEq(pkvd->szKeyName, "disable")) |
|
{ |
|
m_fEnabled = !atoi(pkvd->szValue); |
|
pkvd->fHandled = TRUE; |
|
} |
|
else if (FStrEq(pkvd->szKeyName, "endingmodel")) |
|
{ |
|
m_iszEndingModel = ALLOC_STRING(pkvd->szValue); |
|
pkvd->fHandled = TRUE; |
|
} |
|
else if (FStrEq(pkvd->szKeyName, "bodymodel")) |
|
{ |
|
m_iszBodyModel = ALLOC_STRING(pkvd->szValue); |
|
pkvd->fHandled = TRUE; |
|
} |
|
else |
|
CBaseEntity::KeyValue(pkvd); |
|
} |
|
|
|
void CRope::Spawn(void) |
|
{ |
|
Precache(); |
|
|
|
pev->solid = SOLID_NOT; |
|
pev->movetype = MOVETYPE_NONE; |
|
pev->effects = 0; |
|
|
|
for (int i = 0; i < MAX_ROPE_SEGMENTS; i++) |
|
m_pSegments[i] = NULL; |
|
|
|
SetThink(&CRope::StartupThink); |
|
pev->nextthink = gpGlobals->time + 0.1; |
|
} |
|
|
|
void CRope::Precache(void) |
|
{ |
|
|
|
PRECACHE_MODEL("models/rope16.mdl"); |
|
PRECACHE_MODEL("models/rope24.mdl"); |
|
PRECACHE_MODEL("models/rope32.mdl"); |
|
|
|
UTIL_PrecacheOther("rope_segment"); |
|
} |
|
|
|
void CRope::StartupThink(void) |
|
{ |
|
pev->nextthink = gpGlobals->time + 0.1; |
|
|
|
CreateSegments(); |
|
|
|
SetThink(&CRope::RopeThink); |
|
} |
|
|
|
//=============================================== |
|
// Use binary search for closest point. |
|
//=============================================== |
|
int CRope::FindClosestSegment(Vector& vecTo, float epsilon, int iMin, int iMax) |
|
{ |
|
if (iMax < iMin) |
|
return -1; |
|
else |
|
{ |
|
int middle = iMin + ((iMax - iMin) / 2); |
|
|
|
if (vecTo.z > (m_pSegments[middle]->pev->origin.z + epsilon)) |
|
{ |
|
return FindClosestSegment(vecTo, epsilon, iMin, middle - 1); // Higher ropes are at vector head. |
|
} |
|
else if (vecTo.z < (m_pSegments[middle]->pev->origin.z - epsilon)) |
|
{ |
|
return FindClosestSegment(vecTo, epsilon, middle + 1, iMax); // Lower ropes are at vector tail. |
|
} |
|
else |
|
{ |
|
return middle; |
|
} |
|
} |
|
} |
|
|
|
static void Functor_SetSegmentFxGlow(CRopeSegment* pSegment) |
|
{ |
|
pSegment->pev->renderfx = kRenderFxGlowShell; |
|
} |
|
|
|
static void Functor_SetSegmentFxNormal(CRopeSegment* pSegment) |
|
{ |
|
pSegment->pev->renderfx = kRenderFxNone; |
|
} |
|
|
|
void CRope::RopeThink(void) |
|
{ |
|
pev->nextthink = gpGlobals->time + 0.1; |
|
|
|
CBaseEntity* pEntity = UTIL_PlayerByIndex(1); |
|
if (pEntity) |
|
{ |
|
if ((pEntity->pev->origin - pev->origin).Length() > 384) |
|
return; |
|
|
|
ApplyFunctor(Functor_SetSegmentFxNormal); |
|
|
|
Vector v = ((CBasePlayer*)pEntity)->GetGunPosition(); |
|
int index = FindClosestSegment(v, 16, 0, m_nSegments - 1); |
|
if (index >= 0 && index < m_nSegments) |
|
{ |
|
CRopeSegment* pClosestSegment = m_pSegments[index]; |
|
|
|
if (pClosestSegment) |
|
{ |
|
pClosestSegment->SetTouch(&CRopeSegment::SegmentTouch); |
|
pClosestSegment->pev->renderfx = kRenderFxGlowShell; |
|
pClosestSegment->pev->rendercolor = Vector(255, 0, 0); |
|
ALERT(at_console, "Rope joint closest is %d at %f\n", index, pClosestSegment->pev->origin.z); |
|
} |
|
} |
|
} |
|
} |
|
|
|
//=============================================== |
|
// Create all rope segments. |
|
//=============================================== |
|
void CRope::CreateSegments() |
|
{ |
|
CRopeSegment* pRopeSegment = NULL; |
|
Vector down = Vector(0, 0, -1); |
|
Vector angles = Vector(0, 0, -90); |
|
|
|
Vector joint; |
|
|
|
joint = pev->origin + (down * 16); |
|
|
|
m_pSegments[0] = (CRopeSegment*)CBaseEntity::Create( |
|
"rope_segment", |
|
joint, |
|
angles); |
|
|
|
m_pSegments[0]->m_vecJointPos = joint; |
|
|
|
m_pSegments[0]->m_pPrev = NULL; |
|
|
|
int i; |
|
for (i = 1; i < m_nSegments; i++) |
|
{ |
|
joint = pev->origin + (down * 16 * i); |
|
|
|
m_pSegments[i] = (CRopeSegment*)CBaseEntity::Create("rope_segment", joint, angles); |
|
|
|
m_pSegments[i]->m_vecJointPos = joint; |
|
|
|
m_pSegments[i - 1]->m_pNext = m_pSegments[i]; |
|
m_pSegments[i]->m_pPrev = m_pSegments[i - 1]; |
|
m_pSegments[i]->m_pNext = NULL; |
|
} |
|
} |
|
|
|
//=============================================== |
|
// Destroy all rope segments |
|
//=============================================== |
|
void CRope::DestroySegments(void) |
|
{ |
|
int i; |
|
for (i = 0; i < m_nSegments; i++) |
|
{ |
|
if (m_pSegments[i]) |
|
{ |
|
UTIL_Remove(m_pSegments[i]); |
|
m_pSegments[i] = NULL; |
|
} |
|
} |
|
} |
|
|
|
//=============================================== |
|
// Apply a functor to a range of segments. |
|
//=============================================== |
|
void CRope::ApplyFunctor(void(*functor)(CRopeSegment* pSegment), int startIndex, int endIndex) |
|
{ |
|
// No functor? Return. |
|
if (!functor) |
|
return; |
|
|
|
// To pass -1 as value for endindex means to apply the functor to all segments. |
|
int endindex = (endIndex != -1) ? endIndex : (m_nSegments - 1); |
|
|
|
// Apply functor to all segments. |
|
int i; |
|
for (i = 0; i <= endindex; i++) |
|
{ |
|
// Apply functor. |
|
if (m_pSegments[i]) |
|
functor(m_pSegments[i]); |
|
} |
|
} |
|
|
|
//=============================================== |
|
// Apply a functor to all segments. |
|
//=============================================== |
|
void CRope::ApplyFunctor(void(*functor)(CRopeSegment* pSegment)) |
|
{ |
|
ApplyFunctor(functor, 0, -1); |
|
}
|
|
|