halflife-photomode/dlls/xen.cpp

590 lines
12 KiB
C++
Raw Normal View History

/***
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.
*
****/
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "animation.h"
#include "effects.h"
#define XEN_PLANT_GLOW_SPRITE "sprites/flare3.spr"
#define XEN_PLANT_HIDE_TIME 5
2013-08-30 13:34:05 -07:00
class CActAnimating : public CBaseAnimating
{
public:
void SetActivity(Activity act);
inline Activity GetActivity() { return m_Activity; }
2013-08-30 13:34:05 -07:00
int ObjectCaps() override { return CBaseAnimating::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
2013-08-30 13:34:05 -07:00
bool Save(CSave& save) override;
bool Restore(CRestore& restore) override;
static TYPEDESCRIPTION m_SaveData[];
2013-08-30 13:34:05 -07:00
private:
Activity m_Activity;
2013-08-30 13:34:05 -07:00
};
TYPEDESCRIPTION CActAnimating::m_SaveData[] =
{
DEFINE_FIELD(CActAnimating, m_Activity, FIELD_INTEGER),
2013-08-30 13:34:05 -07:00
};
IMPLEMENT_SAVERESTORE(CActAnimating, CBaseAnimating);
2013-08-30 13:34:05 -07:00
void CActAnimating::SetActivity(Activity act)
{
int sequence = LookupActivity(act);
if (sequence != ACTIVITY_NOT_AVAILABLE)
2013-08-30 13:34:05 -07:00
{
pev->sequence = sequence;
m_Activity = act;
2013-08-30 13:34:05 -07:00
pev->frame = 0;
ResetSequenceInfo();
2013-08-30 13:34:05 -07:00
}
}
class CXenPLight : public CActAnimating
{
public:
void Spawn() override;
void Precache() override;
void Touch(CBaseEntity* pOther) override;
void Think() override;
2013-08-30 13:34:05 -07:00
void LightOn();
void LightOff();
2013-08-30 13:34:05 -07:00
bool Save(CSave& save) override;
bool Restore(CRestore& restore) override;
static TYPEDESCRIPTION m_SaveData[];
2013-08-30 13:34:05 -07:00
private:
CSprite* m_pGlow;
2013-08-30 13:34:05 -07:00
};
LINK_ENTITY_TO_CLASS(xen_plantlight, CXenPLight);
2013-08-30 13:34:05 -07:00
TYPEDESCRIPTION CXenPLight::m_SaveData[] =
{
DEFINE_FIELD(CXenPLight, m_pGlow, FIELD_CLASSPTR),
2013-08-30 13:34:05 -07:00
};
IMPLEMENT_SAVERESTORE(CXenPLight, CActAnimating);
2013-08-30 13:34:05 -07:00
void CXenPLight::Spawn()
2013-08-30 13:34:05 -07:00
{
Precache();
SET_MODEL(ENT(pev), "models/light.mdl");
pev->movetype = MOVETYPE_NONE;
pev->solid = SOLID_TRIGGER;
2013-08-30 13:34:05 -07:00
UTIL_SetSize(pev, Vector(-80, -80, 0), Vector(80, 80, 32));
SetActivity(ACT_IDLE);
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time + 0.1;
pev->frame = RANDOM_FLOAT(0, 255);
2013-08-30 13:34:05 -07:00
m_pGlow = CSprite::SpriteCreate(XEN_PLANT_GLOW_SPRITE, pev->origin + Vector(0, 0, (pev->mins.z + pev->maxs.z) * 0.5), false);
m_pGlow->SetTransparency(kRenderGlow, pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z, pev->renderamt, pev->renderfx);
m_pGlow->SetAttachment(edict(), 1);
2013-08-30 13:34:05 -07:00
}
void CXenPLight::Precache()
2013-08-30 13:34:05 -07:00
{
PRECACHE_MODEL("models/light.mdl");
PRECACHE_MODEL(XEN_PLANT_GLOW_SPRITE);
2013-08-30 13:34:05 -07:00
}
void CXenPLight::Think()
2013-08-30 13:34:05 -07:00
{
StudioFrameAdvance();
pev->nextthink = gpGlobals->time + 0.1;
switch (GetActivity())
2013-08-30 13:34:05 -07:00
{
case ACT_CROUCH:
if (m_fSequenceFinished)
2013-08-30 13:34:05 -07:00
{
SetActivity(ACT_CROUCHIDLE);
2013-08-30 13:34:05 -07:00
LightOff();
}
break;
case ACT_CROUCHIDLE:
if (gpGlobals->time > pev->dmgtime)
2013-08-30 13:34:05 -07:00
{
SetActivity(ACT_STAND);
2013-08-30 13:34:05 -07:00
LightOn();
}
break;
case ACT_STAND:
if (m_fSequenceFinished)
SetActivity(ACT_IDLE);
2013-08-30 13:34:05 -07:00
break;
case ACT_IDLE:
default:
break;
}
}
void CXenPLight::Touch(CBaseEntity* pOther)
2013-08-30 13:34:05 -07:00
{
if (pOther->IsPlayer())
2013-08-30 13:34:05 -07:00
{
pev->dmgtime = gpGlobals->time + XEN_PLANT_HIDE_TIME;
if (GetActivity() == ACT_IDLE || GetActivity() == ACT_STAND)
2013-08-30 13:34:05 -07:00
{
SetActivity(ACT_CROUCH);
2013-08-30 13:34:05 -07:00
}
}
}
void CXenPLight::LightOn()
2013-08-30 13:34:05 -07:00
{
SUB_UseTargets(this, USE_ON, 0);
if (m_pGlow)
2013-08-30 13:34:05 -07:00
m_pGlow->pev->effects &= ~EF_NODRAW;
}
void CXenPLight::LightOff()
2013-08-30 13:34:05 -07:00
{
SUB_UseTargets(this, USE_OFF, 0);
if (m_pGlow)
2013-08-30 13:34:05 -07:00
m_pGlow->pev->effects |= EF_NODRAW;
}
class CXenHair : public CActAnimating
{
public:
void Spawn() override;
void Precache() override;
void Think() override;
2013-08-30 13:34:05 -07:00
};
LINK_ENTITY_TO_CLASS(xen_hair, CXenHair);
2013-08-30 13:34:05 -07:00
#define SF_HAIR_SYNC 0x0001
2013-08-30 13:34:05 -07:00
void CXenHair::Spawn()
2013-08-30 13:34:05 -07:00
{
Precache();
SET_MODEL(edict(), "models/hair.mdl");
UTIL_SetSize(pev, Vector(-4, -4, 0), Vector(4, 4, 32));
2013-08-30 13:34:05 -07:00
pev->sequence = 0;
if ((pev->spawnflags & SF_HAIR_SYNC) == 0)
2013-08-30 13:34:05 -07:00
{
pev->frame = RANDOM_FLOAT(0, 255);
pev->framerate = RANDOM_FLOAT(0.7, 1.4);
2013-08-30 13:34:05 -07:00
}
ResetSequenceInfo();
2013-08-30 13:34:05 -07:00
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
pev->nextthink = gpGlobals->time + RANDOM_FLOAT(0.1, 0.4); // Load balance these a bit
2013-08-30 13:34:05 -07:00
}
void CXenHair::Think()
2013-08-30 13:34:05 -07:00
{
StudioFrameAdvance();
pev->nextthink = gpGlobals->time + 0.5;
}
void CXenHair::Precache()
2013-08-30 13:34:05 -07:00
{
PRECACHE_MODEL("models/hair.mdl");
2013-08-30 13:34:05 -07:00
}
class CXenTreeTrigger : public CBaseEntity
{
public:
void Touch(CBaseEntity* pOther) override;
static CXenTreeTrigger* TriggerCreate(edict_t* pOwner, const Vector& position);
2013-08-30 13:34:05 -07:00
};
LINK_ENTITY_TO_CLASS(xen_ttrigger, CXenTreeTrigger);
2013-08-30 13:34:05 -07:00
CXenTreeTrigger* CXenTreeTrigger::TriggerCreate(edict_t* pOwner, const Vector& position)
2013-08-30 13:34:05 -07:00
{
CXenTreeTrigger* pTrigger = GetClassPtr((CXenTreeTrigger*)NULL);
2013-08-30 13:34:05 -07:00
pTrigger->pev->origin = position;
pTrigger->pev->classname = MAKE_STRING("xen_ttrigger");
pTrigger->pev->solid = SOLID_TRIGGER;
pTrigger->pev->movetype = MOVETYPE_NONE;
pTrigger->pev->owner = pOwner;
return pTrigger;
}
void CXenTreeTrigger::Touch(CBaseEntity* pOther)
2013-08-30 13:34:05 -07:00
{
if (pev->owner)
2013-08-30 13:34:05 -07:00
{
CBaseEntity* pEntity = CBaseEntity::Instance(pev->owner);
pEntity->Touch(pOther);
2013-08-30 13:34:05 -07:00
}
}
#define TREE_AE_ATTACK 1
2013-08-30 13:34:05 -07:00
class CXenTree : public CActAnimating
{
public:
void Spawn() override;
void Precache() override;
void Touch(CBaseEntity* pOther) override;
void Think() override;
bool TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) override
{
Attack();
return false;
}
void HandleAnimEvent(MonsterEvent_t* pEvent) override;
void Attack();
int Classify() override { return CLASS_BARNACLE; }
bool Save(CSave& save) override;
bool Restore(CRestore& restore) override;
static TYPEDESCRIPTION m_SaveData[];
static const char* pAttackHitSounds[];
static const char* pAttackMissSounds[];
2013-08-30 13:34:05 -07:00
private:
CXenTreeTrigger* m_pTrigger;
2013-08-30 13:34:05 -07:00
};
LINK_ENTITY_TO_CLASS(xen_tree, CXenTree);
2013-08-30 13:34:05 -07:00
TYPEDESCRIPTION CXenTree::m_SaveData[] =
{
DEFINE_FIELD(CXenTree, m_pTrigger, FIELD_CLASSPTR),
2013-08-30 13:34:05 -07:00
};
IMPLEMENT_SAVERESTORE(CXenTree, CActAnimating);
2013-08-30 13:34:05 -07:00
void CXenTree::Spawn()
2013-08-30 13:34:05 -07:00
{
Precache();
SET_MODEL(ENT(pev), "models/tree.mdl");
pev->movetype = MOVETYPE_NONE;
pev->solid = SOLID_BBOX;
2013-08-30 13:34:05 -07:00
pev->takedamage = DAMAGE_YES;
UTIL_SetSize(pev, Vector(-30, -30, 0), Vector(30, 30, 188));
SetActivity(ACT_IDLE);
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time + 0.1;
pev->frame = RANDOM_FLOAT(0, 255);
pev->framerate = RANDOM_FLOAT(0.7, 1.4);
2013-08-30 13:34:05 -07:00
Vector triggerPosition;
UTIL_MakeVectorsPrivate(pev->angles, triggerPosition, NULL, NULL);
2013-08-30 13:34:05 -07:00
triggerPosition = pev->origin + (triggerPosition * 64);
// Create the trigger
m_pTrigger = CXenTreeTrigger::TriggerCreate(edict(), triggerPosition);
UTIL_SetSize(m_pTrigger->pev, Vector(-24, -24, 0), Vector(24, 24, 128));
2013-08-30 13:34:05 -07:00
}
const char* CXenTree::pAttackHitSounds[] =
{
"zombie/claw_strike1.wav",
"zombie/claw_strike2.wav",
"zombie/claw_strike3.wav",
2013-08-30 13:34:05 -07:00
};
const char* CXenTree::pAttackMissSounds[] =
{
"zombie/claw_miss1.wav",
"zombie/claw_miss2.wav",
2013-08-30 13:34:05 -07:00
};
void CXenTree::Precache()
2013-08-30 13:34:05 -07:00
{
PRECACHE_MODEL("models/tree.mdl");
PRECACHE_MODEL(XEN_PLANT_GLOW_SPRITE);
PRECACHE_SOUND_ARRAY(pAttackHitSounds);
PRECACHE_SOUND_ARRAY(pAttackMissSounds);
2013-08-30 13:34:05 -07:00
}
void CXenTree::Touch(CBaseEntity* pOther)
2013-08-30 13:34:05 -07:00
{
if (!pOther->IsPlayer() && FClassnameIs(pOther->pev, "monster_bigmomma"))
2013-08-30 13:34:05 -07:00
return;
Attack();
}
void CXenTree::Attack()
2013-08-30 13:34:05 -07:00
{
if (GetActivity() == ACT_IDLE)
2013-08-30 13:34:05 -07:00
{
SetActivity(ACT_MELEE_ATTACK1);
pev->framerate = RANDOM_FLOAT(1.0, 1.4);
EMIT_SOUND_ARRAY_DYN(CHAN_WEAPON, pAttackMissSounds);
2013-08-30 13:34:05 -07:00
}
}
void CXenTree::HandleAnimEvent(MonsterEvent_t* pEvent)
2013-08-30 13:34:05 -07:00
{
switch (pEvent->event)
2013-08-30 13:34:05 -07:00
{
2021-11-29 20:55:01 +01:00
case TREE_AE_ATTACK:
{
CBaseEntity* pList[8];
bool sound = false;
int count = UTIL_EntitiesInBox(pList, 8, m_pTrigger->pev->absmin, m_pTrigger->pev->absmax, FL_MONSTER | FL_CLIENT);
Vector forward;
2013-08-30 13:34:05 -07:00
UTIL_MakeVectorsPrivate(pev->angles, forward, NULL, NULL);
2013-08-30 13:34:05 -07:00
for (int i = 0; i < count; i++)
{
if (pList[i] != this)
2013-08-30 13:34:05 -07:00
{
if (pList[i]->pev->owner != edict())
2013-08-30 13:34:05 -07:00
{
sound = true;
pList[i]->TakeDamage(pev, pev, 25, DMG_CRUSH | DMG_SLASH);
pList[i]->pev->punchangle.x = 15;
pList[i]->pev->velocity = pList[i]->pev->velocity + forward * 100;
2013-08-30 13:34:05 -07:00
}
}
}
if (sound)
{
EMIT_SOUND_ARRAY_DYN(CHAN_WEAPON, pAttackHitSounds);
}
}
2013-08-30 13:34:05 -07:00
return;
}
CActAnimating::HandleAnimEvent(pEvent);
2013-08-30 13:34:05 -07:00
}
void CXenTree::Think()
2013-08-30 13:34:05 -07:00
{
float flInterval = StudioFrameAdvance();
pev->nextthink = gpGlobals->time + 0.1;
DispatchAnimEvents(flInterval);
2013-08-30 13:34:05 -07:00
switch (GetActivity())
2013-08-30 13:34:05 -07:00
{
case ACT_MELEE_ATTACK1:
if (m_fSequenceFinished)
2013-08-30 13:34:05 -07:00
{
SetActivity(ACT_IDLE);
pev->framerate = RANDOM_FLOAT(0.6, 1.4);
2013-08-30 13:34:05 -07:00
}
break;
default:
case ACT_IDLE:
break;
}
}
// UNDONE: These need to smoke somehow when they take damage
// Touch behavior?
// Cause damage in smoke area
//
// Spores
//
class CXenSpore : public CActAnimating
{
public:
void Spawn() override;
void Precache() override;
void Touch(CBaseEntity* pOther) override;
void Think() override;
bool TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) override
{
Attack();
return false;
}
// void HandleAnimEvent( MonsterEvent_t *pEvent );
void Attack() {}
static const char* pModelNames[];
2013-08-30 13:34:05 -07:00
};
class CXenSporeSmall : public CXenSpore
{
void Spawn() override;
2013-08-30 13:34:05 -07:00
};
class CXenSporeMed : public CXenSpore
{
void Spawn() override;
2013-08-30 13:34:05 -07:00
};
class CXenSporeLarge : public CXenSpore
{
void Spawn() override;
2013-08-30 13:34:05 -07:00
static const Vector m_hullSizes[];
};
// Fake collision box for big spores
class CXenHull : public CPointEntity
{
public:
static CXenHull* CreateHull(CBaseEntity* source, const Vector& mins, const Vector& maxs, const Vector& offset);
int Classify() override { return CLASS_BARNACLE; }
2013-08-30 13:34:05 -07:00
};
CXenHull* CXenHull::CreateHull(CBaseEntity* source, const Vector& mins, const Vector& maxs, const Vector& offset)
2013-08-30 13:34:05 -07:00
{
CXenHull* pHull = GetClassPtr((CXenHull*)NULL);
2013-08-30 13:34:05 -07:00
UTIL_SetOrigin(pHull->pev, source->pev->origin + offset);
SET_MODEL(pHull->edict(), STRING(source->pev->model));
2013-08-30 13:34:05 -07:00
pHull->pev->solid = SOLID_BBOX;
pHull->pev->classname = MAKE_STRING("xen_hull");
pHull->pev->movetype = MOVETYPE_NONE;
pHull->pev->owner = source->edict();
UTIL_SetSize(pHull->pev, mins, maxs);
2013-08-30 13:34:05 -07:00
pHull->pev->renderamt = 0;
pHull->pev->rendermode = kRenderTransTexture;
// pHull->pev->effects = EF_NODRAW;
return pHull;
}
LINK_ENTITY_TO_CLASS(xen_spore_small, CXenSporeSmall);
LINK_ENTITY_TO_CLASS(xen_spore_medium, CXenSporeMed);
LINK_ENTITY_TO_CLASS(xen_spore_large, CXenSporeLarge);
LINK_ENTITY_TO_CLASS(xen_hull, CXenHull);
2013-08-30 13:34:05 -07:00
void CXenSporeSmall::Spawn()
2013-08-30 13:34:05 -07:00
{
pev->skin = 0;
CXenSpore::Spawn();
UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 64));
2013-08-30 13:34:05 -07:00
}
void CXenSporeMed::Spawn()
2013-08-30 13:34:05 -07:00
{
pev->skin = 1;
CXenSpore::Spawn();
UTIL_SetSize(pev, Vector(-40, -40, 0), Vector(40, 40, 120));
2013-08-30 13:34:05 -07:00
}
// I just eyeballed these -- fill in hulls for the legs
const Vector CXenSporeLarge::m_hullSizes[] =
{
Vector(90, -25, 0),
Vector(25, 75, 0),
Vector(-15, -100, 0),
Vector(-90, -35, 0),
Vector(-90, 60, 0),
2013-08-30 13:34:05 -07:00
};
void CXenSporeLarge::Spawn()
2013-08-30 13:34:05 -07:00
{
pev->skin = 2;
CXenSpore::Spawn();
UTIL_SetSize(pev, Vector(-48, -48, 110), Vector(48, 48, 240));
2013-08-30 13:34:05 -07:00
Vector forward, right;
UTIL_MakeVectorsPrivate(pev->angles, forward, right, NULL);
2013-08-30 13:34:05 -07:00
// Rotate the leg hulls into position
for (int i = 0; i < ARRAYSIZE(m_hullSizes); i++)
CXenHull::CreateHull(this, Vector(-12, -12, 0), Vector(12, 12, 120), (m_hullSizes[i].x * forward) + (m_hullSizes[i].y * right));
2013-08-30 13:34:05 -07:00
}
void CXenSpore::Spawn()
2013-08-30 13:34:05 -07:00
{
Precache();
SET_MODEL(ENT(pev), pModelNames[pev->skin]);
pev->movetype = MOVETYPE_NONE;
pev->solid = SOLID_BBOX;
2013-08-30 13:34:05 -07:00
pev->takedamage = DAMAGE_YES;
// SetActivity( ACT_IDLE );
2013-08-30 13:34:05 -07:00
pev->sequence = 0;
pev->frame = RANDOM_FLOAT(0, 255);
pev->framerate = RANDOM_FLOAT(0.7, 1.4);
ResetSequenceInfo();
pev->nextthink = gpGlobals->time + RANDOM_FLOAT(0.1, 0.4); // Load balance these a bit
2013-08-30 13:34:05 -07:00
}
const char* CXenSpore::pModelNames[] =
{
"models/fungus(small).mdl",
"models/fungus.mdl",
"models/fungus(large).mdl",
2013-08-30 13:34:05 -07:00
};
void CXenSpore::Precache()
2013-08-30 13:34:05 -07:00
{
PRECACHE_MODEL((char*)pModelNames[pev->skin]);
2013-08-30 13:34:05 -07:00
}
void CXenSpore::Touch(CBaseEntity* pOther)
2013-08-30 13:34:05 -07:00
{
}
void CXenSpore::Think()
2013-08-30 13:34:05 -07:00
{
float flInterval = StudioFrameAdvance();
pev->nextthink = gpGlobals->time + 0.1;
#if 0
DispatchAnimEvents( flInterval );
switch( GetActivity() )
{
default:
case ACT_IDLE:
break;
}
#endif
}