Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
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

/***
*
* 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);
}