halflife-photomode/dlls/scripted.cpp

1268 lines
32 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.
*
* This source code contains proprietary and confidential information of
* Valve LLC and its suppliers. Access to this code is restricted to
* persons who have executed a written SDK license with Valve. Any access,
* use or distribution of this code by or to any unlicensed person is illegal.
*
****/
/*
===== scripted.cpp ========================================================
*/
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "animation.h"
#include "saverestore.h"
#include "schedule.h"
#include "scripted.h"
#include "defaultai.h"
/*
classname "scripted_sequence"
targetname "me" - there can be more than one with the same name, and they act in concert
target "the_entity_I_want_to_start_playing" or "class entity_classname" will pick the closest inactive scientist
play "name_of_sequence"
idle "name of idle sequence to play before starting"
donetrigger "whatever" - can be any other triggerable entity such as another sequence, train, door, or a special case like "die" or "remove"
moveto - if set the monster first moves to this nodes position
range # - only search this far to find the target
spawnflags - (stop if blocked, stop if player seen)
*/
//
// Cache user-entity-field values until spawn is called.
//
bool CCineMonster::KeyValue(KeyValueData* pkvd)
2013-08-30 13:34:05 -07:00
{
if (FStrEq(pkvd->szKeyName, "m_iszIdle"))
{
m_iszIdle = ALLOC_STRING(pkvd->szValue);
return true;
2013-08-30 13:34:05 -07:00
}
else if (FStrEq(pkvd->szKeyName, "m_iszPlay"))
{
m_iszPlay = ALLOC_STRING(pkvd->szValue);
return true;
2013-08-30 13:34:05 -07:00
}
else if (FStrEq(pkvd->szKeyName, "m_iszEntity"))
{
m_iszEntity = ALLOC_STRING(pkvd->szValue);
return true;
2013-08-30 13:34:05 -07:00
}
else if (FStrEq(pkvd->szKeyName, "m_fMoveTo"))
{
m_fMoveTo = atoi(pkvd->szValue);
return true;
2013-08-30 13:34:05 -07:00
}
else if (FStrEq(pkvd->szKeyName, "m_flRepeat"))
{
m_flRepeat = atof(pkvd->szValue);
return true;
2013-08-30 13:34:05 -07:00
}
else if (FStrEq(pkvd->szKeyName, "m_flRadius"))
{
m_flRadius = atof(pkvd->szValue);
return true;
2013-08-30 13:34:05 -07:00
}
else if (FStrEq(pkvd->szKeyName, "m_iFinishSchedule"))
{
m_iFinishSchedule = atoi(pkvd->szValue);
return true;
2013-08-30 13:34:05 -07:00
}
return CBaseMonster::KeyValue(pkvd);
2013-08-30 13:34:05 -07:00
}
TYPEDESCRIPTION CCineMonster::m_SaveData[] =
{
DEFINE_FIELD(CCineMonster, m_iszIdle, FIELD_STRING),
DEFINE_FIELD(CCineMonster, m_iszPlay, FIELD_STRING),
DEFINE_FIELD(CCineMonster, m_iszEntity, FIELD_STRING),
DEFINE_FIELD(CCineMonster, m_fMoveTo, FIELD_INTEGER),
DEFINE_FIELD(CCineMonster, m_flRepeat, FIELD_FLOAT),
DEFINE_FIELD(CCineMonster, m_flRadius, FIELD_FLOAT),
DEFINE_FIELD(CCineMonster, m_iDelay, FIELD_INTEGER),
DEFINE_FIELD(CCineMonster, m_startTime, FIELD_TIME),
DEFINE_FIELD(CCineMonster, m_saved_movetype, FIELD_INTEGER),
DEFINE_FIELD(CCineMonster, m_saved_solid, FIELD_INTEGER),
DEFINE_FIELD(CCineMonster, m_saved_effects, FIELD_INTEGER),
DEFINE_FIELD(CCineMonster, m_iFinishSchedule, FIELD_INTEGER),
DEFINE_FIELD(CCineMonster, m_interruptable, FIELD_BOOLEAN),
2013-08-30 13:34:05 -07:00
};
IMPLEMENT_SAVERESTORE(CCineMonster, CBaseMonster);
2013-08-30 13:34:05 -07:00
LINK_ENTITY_TO_CLASS(scripted_sequence, CCineMonster);
2013-08-30 13:34:05 -07:00
#define CLASSNAME "scripted_sequence"
LINK_ENTITY_TO_CLASS(aiscripted_sequence, CCineAI);
2013-08-30 13:34:05 -07:00
void CCineMonster::Spawn()
2013-08-30 13:34:05 -07:00
{
// pev->solid = SOLID_TRIGGER;
// UTIL_SetSize(pev, Vector(-8, -8, -8), Vector(8, 8, 8));
pev->solid = SOLID_NOT;
// REMOVE: The old side-effect
#if 0
if ( m_iszIdle )
m_fMoveTo = 4;
#endif
// if no targetname, start now
if (FStringNull(pev->targetname) || !FStringNull(m_iszIdle))
2013-08-30 13:34:05 -07:00
{
SetThink(&CCineMonster::CineThink);
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time + 1.0;
// Wait to be used?
if (!FStringNull(pev->targetname))
2013-08-30 13:34:05 -07:00
m_startTime = gpGlobals->time + 1E6;
}
if ((pev->spawnflags & SF_SCRIPT_NOINTERRUPT) != 0)
2021-11-19 13:43:33 +01:00
m_interruptable = false;
2013-08-30 13:34:05 -07:00
else
2021-11-19 13:45:16 +01:00
m_interruptable = true;
2013-08-30 13:34:05 -07:00
}
//=========================================================
// FCanOverrideState - returns false, scripted sequences
2013-08-30 13:34:05 -07:00
// cannot possess entities regardless of state.
//=========================================================
bool CCineMonster::FCanOverrideState()
2013-08-30 13:34:05 -07:00
{
if ((pev->spawnflags & SF_SCRIPT_OVERRIDESTATE) != 0)
2021-11-19 13:45:16 +01:00
return true;
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
}
//=========================================================
// FCanOverrideState - returns true because scripted AI can
// possess entities regardless of their state.
//=========================================================
bool CCineAI::FCanOverrideState()
2013-08-30 13:34:05 -07:00
{
2021-11-19 13:45:16 +01:00
return true;
2013-08-30 13:34:05 -07:00
}
//
// CineStart
//
void CCineMonster::Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
2013-08-30 13:34:05 -07:00
{
// do I already know who I should use
CBaseEntity* pEntity = m_hTargetEnt;
CBaseMonster* pTarget = NULL;
2013-08-30 13:34:05 -07:00
if (pEntity)
2013-08-30 13:34:05 -07:00
pTarget = pEntity->MyMonsterPointer();
if (pTarget)
2013-08-30 13:34:05 -07:00
{
// am I already playing the script?
if (pTarget->m_scriptState == SCRIPT_PLAYING)
2013-08-30 13:34:05 -07:00
return;
m_startTime = gpGlobals->time + 0.05;
}
else
{
// if not, try finding them
SetThink(&CCineMonster::CineThink);
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time;
}
}
// This doesn't really make sense since only MOVETYPE_PUSH get 'Blocked' events
void CCineMonster::Blocked(CBaseEntity* pOther)
2013-08-30 13:34:05 -07:00
{
}
void CCineMonster::Touch(CBaseEntity* pOther)
2013-08-30 13:34:05 -07:00
{
/*
2013-08-30 13:34:05 -07:00
ALERT( at_aiconsole, "Cine Touch\n" );
if (m_pentTarget && OFFSET(pOther->pev) == OFFSET(m_pentTarget))
{
CBaseMonster *pTarget = GetClassPtr((CBaseMonster *)VARS(m_pentTarget));
pTarget->m_monsterState == MONSTERSTATE_SCRIPT;
}
*/
}
/*
entvars_t *pevOther = VARS( gpGlobals->other );
if ( !FBitSet ( pevOther->flags , FL_MONSTER ) )
{// touched by a non-monster.
return;
}
pevOther->origin.z += 1;
if ( FBitSet ( pevOther->flags, FL_ONGROUND ) )
{// clear the onground so physics don't bitch
pevOther->flags -= FL_ONGROUND;
}
// toss the monster!
pevOther->velocity = pev->movedir * pev->speed;
pevOther->velocity.z += m_flHeight;
pev->solid = SOLID_NOT;// kill the trigger for now !!!UNDONE
}
*/
//
// ********** Cinematic DIE **********
//
void CCineMonster::Die()
2013-08-30 13:34:05 -07:00
{
SetThink(&CCineMonster::SUB_Remove);
2013-08-30 13:34:05 -07:00
}
//
// ********** Cinematic PAIN **********
//
void CCineMonster::Pain()
2013-08-30 13:34:05 -07:00
{
}
//
// ********** Cinematic Think **********
//
// find a viable entity
bool CCineMonster::FindEntity()
2013-08-30 13:34:05 -07:00
{
edict_t* pentTarget;
2013-08-30 13:34:05 -07:00
pentTarget = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_iszEntity));
m_hTargetEnt = NULL;
CBaseMonster* pTarget = NULL;
2013-08-30 13:34:05 -07:00
while (!FNullEnt(pentTarget))
{
if (FBitSet(VARS(pentTarget)->flags, FL_MONSTER))
2013-08-30 13:34:05 -07:00
{
pTarget = GetMonsterPointer(pentTarget);
if (pTarget && pTarget->CanPlaySequence(FCanOverrideState(), SS_INTERRUPT_BY_NAME))
2013-08-30 13:34:05 -07:00
{
m_hTargetEnt = pTarget;
2021-11-19 13:45:16 +01:00
return true;
2013-08-30 13:34:05 -07:00
}
ALERT(at_console, "Found %s, but can't play!\n", STRING(m_iszEntity));
2013-08-30 13:34:05 -07:00
}
pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(m_iszEntity));
pTarget = NULL;
}
if (!pTarget)
2013-08-30 13:34:05 -07:00
{
CBaseEntity* pEntity = NULL;
while ((pEntity = UTIL_FindEntityInSphere(pEntity, pev->origin, m_flRadius)) != NULL)
2013-08-30 13:34:05 -07:00
{
if (FClassnameIs(pEntity->pev, STRING(m_iszEntity)))
2013-08-30 13:34:05 -07:00
{
if (FBitSet(pEntity->pev->flags, FL_MONSTER))
2013-08-30 13:34:05 -07:00
{
pTarget = pEntity->MyMonsterPointer();
if (pTarget && pTarget->CanPlaySequence(FCanOverrideState(), SS_INTERRUPT_IDLE))
2013-08-30 13:34:05 -07:00
{
m_hTargetEnt = pTarget;
2021-11-19 13:45:16 +01:00
return true;
2013-08-30 13:34:05 -07:00
}
}
}
}
}
pTarget = NULL;
m_hTargetEnt = NULL;
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
}
// make the entity enter a scripted sequence
void CCineMonster::PossessEntity()
2013-08-30 13:34:05 -07:00
{
CBaseEntity* pEntity = m_hTargetEnt;
CBaseMonster* pTarget = NULL;
if (pEntity)
2013-08-30 13:34:05 -07:00
pTarget = pEntity->MyMonsterPointer();
if (pTarget)
2013-08-30 13:34:05 -07:00
{
// FindEntity() just checked this!
2013-08-30 13:34:05 -07:00
#if 0
if ( !pTarget->CanPlaySequence( FCanOverrideState() ) )
{
ALERT( at_aiconsole, "Can't possess entity %s\n", STRING(pTarget->pev->classname) );
return;
}
#endif
pTarget->m_pGoalEnt = this;
pTarget->m_pCine = this;
pTarget->m_hTargetEnt = this;
m_saved_movetype = pTarget->pev->movetype;
m_saved_solid = pTarget->pev->solid;
m_saved_effects = pTarget->pev->effects;
pTarget->pev->effects |= pev->effects;
switch (m_fMoveTo)
{
case 0:
pTarget->m_scriptState = SCRIPT_WAIT;
2013-08-30 13:34:05 -07:00
break;
case 1:
pTarget->m_scriptState = SCRIPT_WALK_TO_MARK;
DelayStart(true);
2013-08-30 13:34:05 -07:00
break;
case 2:
pTarget->m_scriptState = SCRIPT_RUN_TO_MARK;
DelayStart(true);
2013-08-30 13:34:05 -07:00
break;
case 4:
UTIL_SetOrigin(pTarget->pev, pev->origin);
2013-08-30 13:34:05 -07:00
pTarget->pev->ideal_yaw = pev->angles.y;
pTarget->pev->avelocity = Vector(0, 0, 0);
pTarget->pev->velocity = Vector(0, 0, 0);
2013-08-30 13:34:05 -07:00
pTarget->pev->effects |= EF_NOINTERP;
pTarget->pev->angles.y = pev->angles.y;
pTarget->m_scriptState = SCRIPT_WAIT;
m_startTime = gpGlobals->time + 1E6;
// UNDONE: Add a flag to do this so people can fixup physics after teleporting monsters
// pTarget->pev->flags &= ~FL_ONGROUND;
break;
}
// ALERT( at_aiconsole, "\"%s\" found and used (INT: %s)\n", STRING( pTarget->pev->targetname ), FBitSet(pev->spawnflags, SF_SCRIPT_NOINTERRUPT)?"No":"Yes" );
2013-08-30 13:34:05 -07:00
pTarget->m_IdealMonsterState = MONSTERSTATE_SCRIPT;
if (!FStringNull(m_iszIdle))
2013-08-30 13:34:05 -07:00
{
StartSequence(pTarget, m_iszIdle, false);
if (FStrEq(STRING(m_iszIdle), STRING(m_iszPlay)))
2013-08-30 13:34:05 -07:00
{
pTarget->pev->framerate = 0;
}
}
}
}
// make the entity carry out the scripted sequence instructions, but without
2013-08-30 13:34:05 -07:00
// destroying the monster's state.
void CCineAI::PossessEntity()
2013-08-30 13:34:05 -07:00
{
Schedule_t* pNewSchedule;
2013-08-30 13:34:05 -07:00
CBaseEntity* pEntity = m_hTargetEnt;
CBaseMonster* pTarget = NULL;
if (pEntity)
2013-08-30 13:34:05 -07:00
pTarget = pEntity->MyMonsterPointer();
if (pTarget)
2013-08-30 13:34:05 -07:00
{
if (!pTarget->CanPlaySequence(FCanOverrideState(), SS_INTERRUPT_AI))
2013-08-30 13:34:05 -07:00
{
ALERT(at_aiconsole, "(AI)Can't possess entity %s\n", STRING(pTarget->pev->classname));
2013-08-30 13:34:05 -07:00
return;
}
pTarget->m_pGoalEnt = this;
pTarget->m_pCine = this;
pTarget->m_hTargetEnt = this;
m_saved_movetype = pTarget->pev->movetype;
m_saved_solid = pTarget->pev->solid;
m_saved_effects = pTarget->pev->effects;
pTarget->pev->effects |= pev->effects;
switch (m_fMoveTo)
{
case 0:
2013-08-30 13:34:05 -07:00
case 5:
pTarget->m_scriptState = SCRIPT_WAIT;
2013-08-30 13:34:05 -07:00
break;
case 1:
pTarget->m_scriptState = SCRIPT_WALK_TO_MARK;
2013-08-30 13:34:05 -07:00
break;
case 2:
pTarget->m_scriptState = SCRIPT_RUN_TO_MARK;
2013-08-30 13:34:05 -07:00
break;
case 4:
2013-08-30 13:34:05 -07:00
// zap the monster instantly to the site of the script entity.
UTIL_SetOrigin(pTarget->pev, pev->origin);
2013-08-30 13:34:05 -07:00
pTarget->pev->ideal_yaw = pev->angles.y;
pTarget->pev->avelocity = Vector(0, 0, 0);
pTarget->pev->velocity = Vector(0, 0, 0);
2013-08-30 13:34:05 -07:00
pTarget->pev->effects |= EF_NOINTERP;
pTarget->pev->angles.y = pev->angles.y;
pTarget->m_scriptState = SCRIPT_WAIT;
m_startTime = gpGlobals->time + 1E6;
// UNDONE: Add a flag to do this so people can fixup physics after teleporting monsters
pTarget->pev->flags &= ~FL_ONGROUND;
break;
default:
ALERT(at_aiconsole, "aiscript: invalid Move To Position value!");
2013-08-30 13:34:05 -07:00
break;
}
ALERT(at_aiconsole, "\"%s\" found and used\n", STRING(pTarget->pev->targetname));
2013-08-30 13:34:05 -07:00
pTarget->m_IdealMonsterState = MONSTERSTATE_SCRIPT;
/*
2013-08-30 13:34:05 -07:00
if (m_iszIdle)
{
2021-11-19 13:43:33 +01:00
StartSequence( pTarget, m_iszIdle, false );
2013-08-30 13:34:05 -07:00
if (FStrEq( STRING(m_iszIdle), STRING(m_iszPlay)))
{
pTarget->pev->framerate = 0;
}
}
*/
// Already in a scripted state?
if (pTarget->m_MonsterState == MONSTERSTATE_SCRIPT)
2013-08-30 13:34:05 -07:00
{
pNewSchedule = pTarget->GetScheduleOfType(SCHED_AISCRIPT);
pTarget->ChangeSchedule(pNewSchedule);
2013-08-30 13:34:05 -07:00
}
}
}
void CCineMonster::CineThink()
2013-08-30 13:34:05 -07:00
{
if (FindEntity())
{
PossessEntity();
ALERT(at_aiconsole, "script \"%s\" using monster \"%s\"\n", STRING(pev->targetname), STRING(m_iszEntity));
2013-08-30 13:34:05 -07:00
}
else
{
CancelScript();
ALERT(at_aiconsole, "script \"%s\" can't find monster \"%s\"\n", STRING(pev->targetname), STRING(m_iszEntity));
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time + 1.0;
}
}
// lookup a sequence name and setup the target monster to play it
bool CCineMonster::StartSequence(CBaseMonster* pTarget, int iszSeq, bool completeOnEmpty)
2013-08-30 13:34:05 -07:00
{
if (FStringNull(iszSeq) && completeOnEmpty)
2013-08-30 13:34:05 -07:00
{
SequenceDone(pTarget);
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
}
pTarget->pev->sequence = pTarget->LookupSequence(STRING(iszSeq));
2013-08-30 13:34:05 -07:00
if (pTarget->pev->sequence == -1)
{
ALERT(at_error, "%s: unknown scripted sequence \"%s\"\n", STRING(pTarget->pev->targetname), STRING(iszSeq));
2013-08-30 13:34:05 -07:00
pTarget->pev->sequence = 0;
2021-11-19 13:43:33 +01:00
// return false;
2013-08-30 13:34:05 -07:00
}
#if 0
char *s;
if ( pev->spawnflags & SF_SCRIPT_NOINTERRUPT )
s = "No";
else
s = "Yes";
ALERT( at_console, "%s (%s): started \"%s\":INT:%s\n", STRING( pTarget->pev->targetname ), STRING( pTarget->pev->classname ), STRING( iszSeq), s );
#endif
pTarget->pev->frame = 0;
pTarget->ResetSequenceInfo();
2021-11-19 13:45:16 +01:00
return true;
2013-08-30 13:34:05 -07:00
}
// lookup a sequence name and setup the target monster to play it
// overridden for CCineAI because it's ok for them to not have an animation sequence
// for the monster to play. For a regular Scripted Sequence, that situation is an error.
bool CCineAI::StartSequence(CBaseMonster* pTarget, int iszSeq, bool completeOnEmpty)
2013-08-30 13:34:05 -07:00
{
if (iszSeq == 0 && completeOnEmpty)
2013-08-30 13:34:05 -07:00
{
// no sequence was provided. Just let the monster proceed, however, we still have to fire any Sequence target
// and remove any non-repeatable CineAI entities here ( because there is code elsewhere that handles those tasks, but
// not until the animation sequence is finished. We have to manually take care of these things where there is no sequence.
SequenceDone(pTarget);
2013-08-30 13:34:05 -07:00
2021-11-19 13:45:16 +01:00
return true;
2013-08-30 13:34:05 -07:00
}
pTarget->pev->sequence = pTarget->LookupSequence(STRING(iszSeq));
2013-08-30 13:34:05 -07:00
if (pTarget->pev->sequence == -1)
{
ALERT(at_error, "%s: unknown aiscripted sequence \"%s\"\n", STRING(pTarget->pev->targetname), STRING(iszSeq));
2013-08-30 13:34:05 -07:00
pTarget->pev->sequence = 0;
2021-11-19 13:43:33 +01:00
// return false;
2013-08-30 13:34:05 -07:00
}
pTarget->pev->frame = 0;
pTarget->ResetSequenceInfo();
2021-11-19 13:45:16 +01:00
return true;
2013-08-30 13:34:05 -07:00
}
//=========================================================
// SequenceDone - called when a scripted sequence animation
// sequence is done playing ( or when an AI Scripted Sequence
// doesn't supply an animation sequence to play ). Expects
// the CBaseMonster pointer to the monster that the sequence
// possesses.
2013-08-30 13:34:05 -07:00
//=========================================================
void CCineMonster::SequenceDone(CBaseMonster* pMonster)
2013-08-30 13:34:05 -07:00
{
//ALERT( at_aiconsole, "Sequence %s finished\n", STRING( m_pCine->m_iszPlay ) );
if ((pev->spawnflags & SF_SCRIPT_REPEATABLE) == 0)
2013-08-30 13:34:05 -07:00
{
SetThink(&CCineMonster::SUB_Remove);
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time + 0.1;
}
2013-08-30 13:34:05 -07:00
// This is done so that another sequence can take over the monster when triggered by the first
2013-08-30 13:34:05 -07:00
pMonster->CineCleanup();
FixScriptMonsterSchedule(pMonster);
2013-08-30 13:34:05 -07:00
// This may cause a sequence to attempt to grab this guy NOW, so we have to clear him out
// of the existing sequence
SUB_UseTargets(NULL, USE_TOGGLE, 0);
2013-08-30 13:34:05 -07:00
}
//=========================================================
// When a monster finishes a scripted sequence, we have to
// fix up its state and schedule for it to return to a
// normal AI monster.
2013-08-30 13:34:05 -07:00
//
// Scripted sequences just dirty the Schedule and drop the
// monster in Idle State.
//=========================================================
void CCineMonster::FixScriptMonsterSchedule(CBaseMonster* pMonster)
2013-08-30 13:34:05 -07:00
{
if (pMonster->m_IdealMonsterState != MONSTERSTATE_DEAD)
2013-08-30 13:34:05 -07:00
pMonster->m_IdealMonsterState = MONSTERSTATE_IDLE;
pMonster->ClearSchedule();
}
//=========================================================
// When a monster finishes a scripted sequence, we have to
// fix up its state and schedule for it to return to a
// normal AI monster.
2013-08-30 13:34:05 -07:00
//
// AI Scripted sequences will, depending on what the level
// designer selects:
//
// -Dirty the monster's schedule and drop out of the
2013-08-30 13:34:05 -07:00
// sequence in their current state.
//
// -Select a specific AMBUSH schedule, regardless of state.
//=========================================================
void CCineAI::FixScriptMonsterSchedule(CBaseMonster* pMonster)
2013-08-30 13:34:05 -07:00
{
switch (m_iFinishSchedule)
2013-08-30 13:34:05 -07:00
{
case SCRIPT_FINISHSCHED_DEFAULT:
pMonster->ClearSchedule();
break;
case SCRIPT_FINISHSCHED_AMBUSH:
pMonster->ChangeSchedule(pMonster->GetScheduleOfType(SCHED_AMBUSH));
break;
default:
ALERT(at_aiconsole, "FixScriptMonsterSchedule - no case!\n");
pMonster->ClearSchedule();
break;
2013-08-30 13:34:05 -07:00
}
}
bool CBaseMonster::ExitScriptedSequence()
2013-08-30 13:34:05 -07:00
{
if (pev->deadflag == DEAD_DYING)
2013-08-30 13:34:05 -07:00
{
// is this legal?
// BUGBUG -- This doesn't call Killed()
m_IdealMonsterState = MONSTERSTATE_DEAD;
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
}
if (m_pCine)
{
m_pCine->CancelScript();
2013-08-30 13:34:05 -07:00
}
2021-11-19 13:45:16 +01:00
return true;
2013-08-30 13:34:05 -07:00
}
void CCineMonster::AllowInterrupt(bool fAllow)
2013-08-30 13:34:05 -07:00
{
if ((pev->spawnflags & SF_SCRIPT_NOINTERRUPT) != 0)
2013-08-30 13:34:05 -07:00
return;
m_interruptable = fAllow;
}
bool CCineMonster::CanInterrupt()
2013-08-30 13:34:05 -07:00
{
if (!m_interruptable)
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
CBaseEntity* pTarget = m_hTargetEnt;
2013-08-30 13:34:05 -07:00
if (pTarget != NULL && pTarget->pev->deadflag == DEAD_NO)
2021-11-19 13:45:16 +01:00
return true;
2013-08-30 13:34:05 -07:00
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
}
int CCineMonster::IgnoreConditions()
2013-08-30 13:34:05 -07:00
{
if (CanInterrupt())
2013-08-30 13:34:05 -07:00
return 0;
return SCRIPT_BREAK_CONDITIONS;
}
void ScriptEntityCancel(edict_t* pentCine)
2013-08-30 13:34:05 -07:00
{
// make sure they are a scripted_sequence
if (FClassnameIs(pentCine, CLASSNAME))
2013-08-30 13:34:05 -07:00
{
CCineMonster* pCineTarget = GetClassPtr((CCineMonster*)VARS(pentCine));
2013-08-30 13:34:05 -07:00
// make sure they have a monster in mind for the script
CBaseEntity* pEntity = pCineTarget->m_hTargetEnt;
CBaseMonster* pTarget = NULL;
if (pEntity)
2013-08-30 13:34:05 -07:00
pTarget = pEntity->MyMonsterPointer();
2013-08-30 13:34:05 -07:00
if (pTarget)
{
// make sure their monster is actually playing a script
if (pTarget->m_MonsterState == MONSTERSTATE_SCRIPT)
2013-08-30 13:34:05 -07:00
{
// tell them do die
pTarget->m_scriptState = CCineMonster::SCRIPT_CLEANUP;
// do it now
pTarget->CineCleanup();
2013-08-30 13:34:05 -07:00
}
}
}
}
// find all the cinematic entities with my targetname and stop them from playing
void CCineMonster::CancelScript()
2013-08-30 13:34:05 -07:00
{
ALERT(at_aiconsole, "Cancelling script: %s\n", STRING(m_iszPlay));
if (FStringNull(pev->targetname))
2013-08-30 13:34:05 -07:00
{
ScriptEntityCancel(edict());
2013-08-30 13:34:05 -07:00
return;
}
edict_t* pentCineTarget = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(pev->targetname));
2013-08-30 13:34:05 -07:00
while (!FNullEnt(pentCineTarget))
{
ScriptEntityCancel(pentCineTarget);
2013-08-30 13:34:05 -07:00
pentCineTarget = FIND_ENTITY_BY_TARGETNAME(pentCineTarget, STRING(pev->targetname));
}
}
// find all the cinematic entities with my targetname and tell them to wait before starting
void CCineMonster::DelayStart(bool state)
2013-08-30 13:34:05 -07:00
{
edict_t* pentCine = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(pev->targetname));
2013-08-30 13:34:05 -07:00
while (!FNullEnt(pentCine))
{
if (FClassnameIs(pentCine, "scripted_sequence"))
2013-08-30 13:34:05 -07:00
{
CCineMonster* pTarget = GetClassPtr((CCineMonster*)VARS(pentCine));
2013-08-30 13:34:05 -07:00
if (state)
{
pTarget->m_iDelay++;
}
else
{
pTarget->m_iDelay--;
if (pTarget->m_iDelay <= 0)
pTarget->m_startTime = gpGlobals->time + 0.05;
}
}
pentCine = FIND_ENTITY_BY_TARGETNAME(pentCine, STRING(pev->targetname));
}
}
// Find an entity that I'm interested in and precache the sounds he'll need in the sequence.
void CCineMonster::Activate()
2013-08-30 13:34:05 -07:00
{
edict_t* pentTarget;
CBaseMonster* pTarget;
2013-08-30 13:34:05 -07:00
// The entity name could be a target name or a classname
// Check the targetname
pentTarget = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_iszEntity));
pTarget = NULL;
while (!pTarget && !FNullEnt(pentTarget))
{
if (FBitSet(VARS(pentTarget)->flags, FL_MONSTER))
2013-08-30 13:34:05 -07:00
{
pTarget = GetMonsterPointer(pentTarget);
2013-08-30 13:34:05 -07:00
}
pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(m_iszEntity));
}
2013-08-30 13:34:05 -07:00
// If no entity with that targetname, check the classname
if (!pTarget)
2013-08-30 13:34:05 -07:00
{
pentTarget = FIND_ENTITY_BY_CLASSNAME(NULL, STRING(m_iszEntity));
while (!pTarget && !FNullEnt(pentTarget))
{
pTarget = GetMonsterPointer(pentTarget);
2013-08-30 13:34:05 -07:00
pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(m_iszEntity));
}
}
// Found a compatible entity
if (pTarget)
2013-08-30 13:34:05 -07:00
{
void* pmodel;
pmodel = GET_MODEL_PTR(pTarget->edict());
if (pmodel)
2013-08-30 13:34:05 -07:00
{
// Look through the event list for stuff to precache
SequencePrecache(pmodel, STRING(m_iszIdle));
SequencePrecache(pmodel, STRING(m_iszPlay));
2013-08-30 13:34:05 -07:00
}
}
}
bool CBaseMonster::CineCleanup()
2013-08-30 13:34:05 -07:00
{
CCineMonster* pOldCine = m_pCine;
2013-08-30 13:34:05 -07:00
// am I linked to a cinematic?
if (m_pCine)
{
// okay, reset me to what it thought I was before
m_pCine->m_hTargetEnt = NULL;
pev->movetype = m_pCine->m_saved_movetype;
pev->solid = m_pCine->m_saved_solid;
pev->effects = m_pCine->m_saved_effects;
}
else
{
// arg, punt
pev->movetype = MOVETYPE_STEP; // this is evil
2013-08-30 13:34:05 -07:00
pev->solid = SOLID_SLIDEBOX;
}
m_pCine = NULL;
m_hTargetEnt = NULL;
m_pGoalEnt = NULL;
if (pev->deadflag == DEAD_DYING)
{
// last frame of death animation?
pev->health = 0;
pev->framerate = 0.0;
pev->solid = SOLID_NOT;
SetState(MONSTERSTATE_DEAD);
2013-08-30 13:34:05 -07:00
pev->deadflag = DEAD_DEAD;
UTIL_SetSize(pev, pev->mins, Vector(pev->maxs.x, pev->maxs.y, pev->mins.z + 2));
2013-08-30 13:34:05 -07:00
if (pOldCine && FBitSet(pOldCine->pev->spawnflags, SF_SCRIPT_LEAVECORPSE))
2013-08-30 13:34:05 -07:00
{
SetUse(NULL); // BUGBUG -- This doesn't call Killed()
SetThink(NULL); // This will probably break some stuff
SetTouch(NULL);
2013-08-30 13:34:05 -07:00
}
else
SUB_StartFadeOut(); // SetThink( SUB_DoNothing );
// This turns off animation & physics in case their origin ends up stuck in the world or something
StopAnimation();
pev->movetype = MOVETYPE_NONE;
pev->effects |= EF_NOINTERP; // Don't interpolate either, assume the corpse is positioned in its final resting place
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
}
// If we actually played a sequence
if (pOldCine && !FStringNull(pOldCine->m_iszPlay))
2013-08-30 13:34:05 -07:00
{
if ((pOldCine->pev->spawnflags & SF_SCRIPT_NOSCRIPTMOVEMENT) == 0)
2013-08-30 13:34:05 -07:00
{
// reset position
Vector new_origin, new_angle;
GetBonePosition(0, new_origin, new_angle);
2013-08-30 13:34:05 -07:00
// Figure out how far they have moved
// We can't really solve this problem because we can't query the movement of the origin relative
// to the sequence. We can get the root bone's position as we do here, but there are
// cases where the root bone is in a different relative position to the entity's origin
// before/after the sequence plays. So we are stuck doing this:
// !!!HACKHACK: Float the origin up and drop to floor because some sequences have
// irregular motion that can't be properly accounted for.
// UNDONE: THIS SHOULD ONLY HAPPEN IF WE ACTUALLY PLAYED THE SEQUENCE.
Vector oldOrigin = pev->origin;
// UNDONE: ugly hack. Don't move monster if they don't "seem" to move
// this really needs to be done with the AX,AY,etc. flags, but that aren't consistantly
// being set, so animations that really do move won't be caught.
if ((oldOrigin - new_origin).Length2D() < 8.0)
new_origin = oldOrigin;
pev->origin.x = new_origin.x;
pev->origin.y = new_origin.y;
pev->origin.z += 1;
pev->flags |= FL_ONGROUND;
int drop = DROP_TO_FLOOR(ENT(pev));
2013-08-30 13:34:05 -07:00
// Origin in solid? Set to org at the end of the sequence
if (drop < 0)
2013-08-30 13:34:05 -07:00
pev->origin = oldOrigin;
else if (drop == 0) // Hanging in air?
2013-08-30 13:34:05 -07:00
{
pev->origin.z = new_origin.z;
pev->flags &= ~FL_ONGROUND;
}
// else entity hit floor, leave there
// pEntity->pev->origin.z = new_origin.z + 5.0; // damn, got to fix this
UTIL_SetOrigin(pev, pev->origin);
2013-08-30 13:34:05 -07:00
pev->effects |= EF_NOINTERP;
}
// We should have some animation to put these guys in, but for now it's idle.
// Due to NOINTERP above, there won't be any blending between this anim & the sequence
m_Activity = ACT_RESET;
}
// set them back into a normal state
pev->enemy = NULL;
if (pev->health > 0)
2013-08-30 13:34:05 -07:00
m_IdealMonsterState = MONSTERSTATE_IDLE; // m_previousState;
else
{
// Dropping out because he got killed
// Can't call killed() no attacker and weirdness (late gibbing) may result
m_IdealMonsterState = MONSTERSTATE_DEAD;
SetConditions(bits_COND_LIGHT_DAMAGE);
2013-08-30 13:34:05 -07:00
pev->deadflag = DEAD_DYING;
FCheckAITrigger();
pev->deadflag = DEAD_NO;
}
// SetAnimation( m_MonsterState );
ClearBits(pev->spawnflags, SF_MONSTER_WAIT_FOR_SCRIPT);
2013-08-30 13:34:05 -07:00
2021-11-19 13:45:16 +01:00
return true;
2013-08-30 13:34:05 -07:00
}
class CScriptedSentence : public CBaseToggle
{
public:
void Spawn() override;
bool KeyValue(KeyValueData* pkvd) override;
void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value) override;
void EXPORT FindThink();
void EXPORT DelayThink();
int ObjectCaps() override { return (CBaseToggle::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
CBaseToggle* FindEntity();
bool AcceptableSpeaker(CBaseToggle* pTarget);
bool StartSentence(CBaseToggle* pTarget);
2013-08-30 13:34:05 -07:00
private:
int m_iszSentence; // string index for idle animation
int m_iszEntity; // entity that is wanted for this sentence
float m_flRadius; // range to search
float m_flDuration; // How long the sentence lasts
float m_flRepeat; // repeat rate
float m_flAttenuation;
float m_flVolume;
bool m_active;
int m_iszListener; // name of entity to look at while talking
2013-08-30 13:34:05 -07:00
};
#define SF_SENTENCE_ONCE 0x0001
#define SF_SENTENCE_FOLLOWERS 0x0002 // only say if following player
#define SF_SENTENCE_INTERRUPT 0x0004 // force talking except when dead
#define SF_SENTENCE_CONCURRENT 0x0008 // allow other people to keep talking
2013-08-30 13:34:05 -07:00
TYPEDESCRIPTION CScriptedSentence::m_SaveData[] =
{
DEFINE_FIELD(CScriptedSentence, m_iszSentence, FIELD_STRING),
DEFINE_FIELD(CScriptedSentence, m_iszEntity, FIELD_STRING),
DEFINE_FIELD(CScriptedSentence, m_flRadius, FIELD_FLOAT),
DEFINE_FIELD(CScriptedSentence, m_flDuration, FIELD_FLOAT),
DEFINE_FIELD(CScriptedSentence, m_flRepeat, FIELD_FLOAT),
DEFINE_FIELD(CScriptedSentence, m_flAttenuation, FIELD_FLOAT),
DEFINE_FIELD(CScriptedSentence, m_flVolume, FIELD_FLOAT),
DEFINE_FIELD(CScriptedSentence, m_active, FIELD_BOOLEAN),
DEFINE_FIELD(CScriptedSentence, m_iszListener, FIELD_STRING),
2013-08-30 13:34:05 -07:00
};
IMPLEMENT_SAVERESTORE(CScriptedSentence, CBaseToggle);
2013-08-30 13:34:05 -07:00
LINK_ENTITY_TO_CLASS(scripted_sentence, CScriptedSentence);
2013-08-30 13:34:05 -07:00
bool CScriptedSentence::KeyValue(KeyValueData* pkvd)
2013-08-30 13:34:05 -07:00
{
if (FStrEq(pkvd->szKeyName, "sentence"))
{
m_iszSentence = ALLOC_STRING(pkvd->szValue);
return true;
2013-08-30 13:34:05 -07:00
}
else if (FStrEq(pkvd->szKeyName, "entity"))
{
m_iszEntity = ALLOC_STRING(pkvd->szValue);
return true;
2013-08-30 13:34:05 -07:00
}
else if (FStrEq(pkvd->szKeyName, "duration"))
{
m_flDuration = atof(pkvd->szValue);
return true;
2013-08-30 13:34:05 -07:00
}
else if (FStrEq(pkvd->szKeyName, "radius"))
{
m_flRadius = atof(pkvd->szValue);
return true;
2013-08-30 13:34:05 -07:00
}
else if (FStrEq(pkvd->szKeyName, "refire"))
{
m_flRepeat = atof(pkvd->szValue);
return true;
2013-08-30 13:34:05 -07:00
}
else if (FStrEq(pkvd->szKeyName, "attenuation"))
2013-08-30 13:34:05 -07:00
{
pev->impulse = atoi(pkvd->szValue);
return true;
2013-08-30 13:34:05 -07:00
}
else if (FStrEq(pkvd->szKeyName, "volume"))
2013-08-30 13:34:05 -07:00
{
m_flVolume = atof(pkvd->szValue) * 0.1;
return true;
2013-08-30 13:34:05 -07:00
}
else if (FStrEq(pkvd->szKeyName, "listener"))
{
m_iszListener = ALLOC_STRING(pkvd->szValue);
return true;
2013-08-30 13:34:05 -07:00
}
return CBaseToggle::KeyValue(pkvd);
2013-08-30 13:34:05 -07:00
}
void CScriptedSentence::Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
2013-08-30 13:34:05 -07:00
{
if (!m_active)
2013-08-30 13:34:05 -07:00
return;
// ALERT( at_console, "Firing sentence: %s\n", STRING(m_iszSentence) );
SetThink(&CScriptedSentence::FindThink);
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time;
}
void CScriptedSentence::Spawn()
2013-08-30 13:34:05 -07:00
{
pev->solid = SOLID_NOT;
2021-11-19 13:45:16 +01:00
m_active = true;
2013-08-30 13:34:05 -07:00
// if no targetname, start now
if (FStringNull(pev->targetname))
2013-08-30 13:34:05 -07:00
{
SetThink(&CScriptedSentence::FindThink);
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time + 1.0;
}
switch (pev->impulse)
2013-08-30 13:34:05 -07:00
{
case 1: // Medium radius
m_flAttenuation = ATTN_STATIC;
break;
case 2: // Large radius
2013-08-30 13:34:05 -07:00
m_flAttenuation = ATTN_NORM;
break;
case 3: //EVERYWHERE
2013-08-30 13:34:05 -07:00
m_flAttenuation = ATTN_NONE;
break;
2013-08-30 13:34:05 -07:00
default:
case 0: // Small radius
m_flAttenuation = ATTN_IDLE;
break;
}
pev->impulse = 0;
// No volume, use normal
if (m_flVolume <= 0)
2013-08-30 13:34:05 -07:00
m_flVolume = 1.0;
}
void CScriptedSentence::FindThink()
2013-08-30 13:34:05 -07:00
{
CBaseToggle* pEnt = FindEntity();
if (pEnt)
2013-08-30 13:34:05 -07:00
{
StartSentence(pEnt);
if ((pev->spawnflags & SF_SENTENCE_ONCE) != 0)
UTIL_Remove(this);
SetThink(&CScriptedSentence::DelayThink);
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time + m_flDuration + m_flRepeat;
2021-11-19 13:43:33 +01:00
m_active = false;
// ALERT( at_console, "%s: found monster %s\n", STRING(m_iszSentence), STRING(m_iszEntity) );
2013-08-30 13:34:05 -07:00
}
else
{
// ALERT( at_console, "%s: can't find monster %s\n", STRING(m_iszSentence), STRING(m_iszEntity) );
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time + m_flRepeat + 0.5;
}
}
void CScriptedSentence::DelayThink()
2013-08-30 13:34:05 -07:00
{
2021-11-19 13:45:16 +01:00
m_active = true;
if (FStringNull(pev->targetname))
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time + 0.1;
SetThink(&CScriptedSentence::FindThink);
2013-08-30 13:34:05 -07:00
}
bool CScriptedSentence::AcceptableSpeaker(CBaseToggle* pTarget)
2013-08-30 13:34:05 -07:00
{
CBaseMonster* pMonster;
pMonster = NULL;
if (pTarget)
{
pMonster = pTarget->MyMonsterPointer();
}
if (pMonster)
2013-08-30 13:34:05 -07:00
{
if ((pev->spawnflags & SF_SENTENCE_FOLLOWERS) != 0)
2013-08-30 13:34:05 -07:00
{
if (pMonster->m_hTargetEnt == NULL || !FClassnameIs(pMonster->m_hTargetEnt->pev, "player"))
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
}
bool override;
if ((pev->spawnflags & SF_SENTENCE_INTERRUPT) != 0)
2021-11-19 13:45:16 +01:00
override = true;
2013-08-30 13:34:05 -07:00
else
2021-11-19 13:43:33 +01:00
override = false;
if (pMonster->CanPlaySentence(override))
2021-11-19 13:45:16 +01:00
return true;
2013-08-30 13:34:05 -07:00
}
else
{
// targeting something other than a monster, sure it can speak
if (pTarget && pTarget->IsAllowedToSpeak())
return true;
}
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
}
CBaseToggle* CScriptedSentence::FindEntity()
2013-08-30 13:34:05 -07:00
{
edict_t* pentTarget;
CBaseToggle* pSpeakingEnt;
2013-08-30 13:34:05 -07:00
pentTarget = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(m_iszEntity));
pSpeakingEnt = NULL;
2013-08-30 13:34:05 -07:00
while (!FNullEnt(pentTarget))
{
CBaseEntity* pEnt = Instance(pentTarget);
pSpeakingEnt = pEnt ? pEnt->MyTogglePointer() : NULL;
if (pSpeakingEnt != NULL)
2013-08-30 13:34:05 -07:00
{
if (AcceptableSpeaker(pSpeakingEnt))
{
// ALERT(at_console, "acceptable speaker\n");
return pSpeakingEnt;
}
// ALERT(at_console, "found unacceptable speaker\n");
2013-08-30 13:34:05 -07:00
}
pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(m_iszEntity));
}
CBaseEntity* pEntity = NULL;
while ((pEntity = UTIL_FindEntityInSphere(pEntity, pev->origin, m_flRadius)) != NULL)
2013-08-30 13:34:05 -07:00
{
if (FClassnameIs(pEntity->pev, STRING(m_iszEntity)))
2013-08-30 13:34:05 -07:00
{
if (FBitSet(pEntity->pev->flags, FL_MONSTER))
2013-08-30 13:34:05 -07:00
{
pSpeakingEnt = pEntity->MyTogglePointer();
if (AcceptableSpeaker(pSpeakingEnt))
return pSpeakingEnt;
2013-08-30 13:34:05 -07:00
}
}
}
2013-08-30 13:34:05 -07:00
return NULL;
}
bool CScriptedSentence::StartSentence(CBaseToggle* pTarget)
2013-08-30 13:34:05 -07:00
{
if (!pTarget)
2013-08-30 13:34:05 -07:00
{
ALERT(at_aiconsole, "Not Playing sentence %s\n", STRING(m_iszSentence));
2021-11-19 13:43:33 +01:00
return false;
2013-08-30 13:34:05 -07:00
}
bool bConcurrent = false;
if ((pev->spawnflags & SF_SENTENCE_CONCURRENT) == 0)
2021-11-19 13:45:16 +01:00
bConcurrent = true;
2013-08-30 13:34:05 -07:00
CBaseEntity* pListener = NULL;
2013-08-30 13:34:05 -07:00
if (!FStringNull(m_iszListener))
{
float radius = m_flRadius;
if (FStrEq(STRING(m_iszListener), "player"))
radius = 4096; // Always find the player
2013-08-30 13:34:05 -07:00
pListener = UTIL_FindEntityGeneric(STRING(m_iszListener), pTarget->pev->origin, radius);
2013-08-30 13:34:05 -07:00
}
pTarget->PlayScriptedSentence(STRING(m_iszSentence), m_flDuration, m_flVolume, m_flAttenuation, bConcurrent, pListener);
ALERT(at_aiconsole, "Playing sentence %s (%.1f)\n", STRING(m_iszSentence), m_flDuration);
SUB_UseTargets(NULL, USE_TOGGLE, 0);
2021-11-19 13:45:16 +01:00
return true;
2013-08-30 13:34:05 -07:00
}
/*
*/
//=========================================================
// Furniture - this is the cool comment I cut-and-pasted
//=========================================================
class CFurniture : public CBaseMonster
{
public:
void Spawn() override;
void Die();
int Classify() override;
int ObjectCaps() override { return (CBaseMonster::ObjectCaps() & ~FCAP_ACROSS_TRANSITION); }
2013-08-30 13:34:05 -07:00
};
LINK_ENTITY_TO_CLASS(monster_furniture, CFurniture);
2013-08-30 13:34:05 -07:00
//=========================================================
// Furniture is killed
//=========================================================
void CFurniture::Die()
2013-08-30 13:34:05 -07:00
{
SetThink(&CFurniture::SUB_Remove);
2013-08-30 13:34:05 -07:00
pev->nextthink = gpGlobals->time;
}
//=========================================================
// This used to have something to do with bees flying, but
2013-08-30 13:34:05 -07:00
// now it only initializes moving furniture in scripted sequences
//=========================================================
void CFurniture::Spawn()
2013-08-30 13:34:05 -07:00
{
PRECACHE_MODEL((char*)STRING(pev->model));
SET_MODEL(ENT(pev), STRING(pev->model));
2013-08-30 13:34:05 -07:00
pev->movetype = MOVETYPE_NONE;
pev->solid = SOLID_BBOX;
pev->health = 80000;
2013-08-30 13:34:05 -07:00
pev->takedamage = DAMAGE_AIM;
pev->effects = 0;
pev->yaw_speed = 0;
pev->sequence = 0;
pev->frame = 0;
2013-08-30 13:34:05 -07:00
// pev->nextthink += 1.0;
// SetThink (WalkMonsterDelay);
2013-08-30 13:34:05 -07:00
ResetSequenceInfo();
2013-08-30 13:34:05 -07:00
pev->frame = 0;
MonsterInit();
}
//=========================================================
// ID's Furniture as neutral (noone will attack it)
//=========================================================
int CFurniture::Classify()
2013-08-30 13:34:05 -07:00
{
return CLASS_NONE;
2013-08-30 13:34:05 -07:00
}