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 .
*
* 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.
//
2021-11-29 20:31:17 +01:00
bool CCineMonster : : KeyValue ( KeyValueData * pkvd )
2013-08-30 13:34:05 -07:00
{
if ( FStrEq ( pkvd - > szKeyName , " m_iszIdle " ) )
{
2021-11-28 16:54:48 +01:00
m_iszIdle = ALLOC_STRING ( 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 , " m_iszPlay " ) )
{
2021-11-28 16:54:48 +01:00
m_iszPlay = ALLOC_STRING ( 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 , " m_iszEntity " ) )
{
2021-11-28 16:54:48 +01:00
m_iszEntity = ALLOC_STRING ( 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 , " m_fMoveTo " ) )
{
2021-11-28 16:54:48 +01:00
m_fMoveTo = atoi ( 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 , " m_flRepeat " ) )
{
2021-11-28 16:54:48 +01:00
m_flRepeat = 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 , " m_flRadius " ) )
{
2021-11-28 16:54:48 +01:00
m_flRadius = 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 , " m_iFinishSchedule " ) )
{
2021-11-28 16:54:48 +01:00
m_iFinishSchedule = 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 CBaseMonster : : KeyValue ( pkvd ) ;
2013-08-30 13:34:05 -07:00
}
2021-11-28 16:54:48 +01: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
} ;
2021-11-28 16:54:48 +01:00
IMPLEMENT_SAVERESTORE ( CCineMonster , CBaseMonster ) ;
2013-08-30 13:34:05 -07:00
2021-11-28 16:54:48 +01:00
LINK_ENTITY_TO_CLASS ( scripted_sequence , CCineMonster ) ;
2013-08-30 13:34:05 -07:00
# define CLASSNAME "scripted_sequence"
2021-11-28 16:54:48 +01:00
LINK_ENTITY_TO_CLASS ( aiscripted_sequence , CCineAI ) ;
2013-08-30 13:34:05 -07:00
2021-11-29 20:31:17 +01: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
2021-11-28 16:54:48 +01:00
if ( FStringNull ( pev - > targetname ) | | ! FStringNull ( m_iszIdle ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
SetThink ( & CCineMonster : : CineThink ) ;
2013-08-30 13:34:05 -07:00
pev - > nextthink = gpGlobals - > time + 1.0 ;
// Wait to be used?
2021-11-28 16:54:48 +01:00
if ( ! FStringNull ( pev - > targetname ) )
2013-08-30 13:34:05 -07:00
m_startTime = gpGlobals - > time + 1E6 ;
}
2021-11-28 16:54:48 +01:00
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
}
//=========================================================
2021-11-28 16:54:48 +01:00
// FCanOverrideState - returns false, scripted sequences
2013-08-30 13:34:05 -07:00
// cannot possess entities regardless of state.
//=========================================================
2021-11-29 20:31:17 +01:00
bool CCineMonster : : FCanOverrideState ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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.
//=========================================================
2021-11-29 20:31:17 +01:00
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
//
2021-11-29 20:31:17 +01:00
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
2021-11-28 16:54:48 +01:00
CBaseEntity * pEntity = m_hTargetEnt ;
CBaseMonster * pTarget = NULL ;
2013-08-30 13:34:05 -07:00
2021-11-28 16:54:48 +01:00
if ( pEntity )
2013-08-30 13:34:05 -07:00
pTarget = pEntity - > MyMonsterPointer ( ) ;
2021-11-28 16:54:48 +01:00
if ( pTarget )
2013-08-30 13:34:05 -07:00
{
// am I already playing the script?
2021-11-28 16:54:48 +01:00
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
2021-11-28 16:54:48 +01:00
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
2021-11-29 20:31:17 +01:00
void CCineMonster : : Blocked ( CBaseEntity * pOther )
2013-08-30 13:34:05 -07:00
{
}
2021-11-29 20:31:17 +01:00
void CCineMonster : : Touch ( CBaseEntity * pOther )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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 **********
//
2021-11-29 20:31:17 +01:00
void CCineMonster : : Die ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
SetThink ( & CCineMonster : : SUB_Remove ) ;
2013-08-30 13:34:05 -07:00
}
//
// ********** Cinematic PAIN **********
//
2021-11-29 20:31:17 +01:00
void CCineMonster : : Pain ( )
2013-08-30 13:34:05 -07:00
{
}
//
// ********** Cinematic Think **********
//
// find a viable entity
2021-11-29 20:31:17 +01:00
bool CCineMonster : : FindEntity ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
edict_t * pentTarget ;
2013-08-30 13:34:05 -07:00
pentTarget = FIND_ENTITY_BY_TARGETNAME ( NULL , STRING ( m_iszEntity ) ) ;
m_hTargetEnt = NULL ;
2021-11-28 16:54:48 +01:00
CBaseMonster * pTarget = NULL ;
2013-08-30 13:34:05 -07:00
while ( ! FNullEnt ( pentTarget ) )
{
2021-11-28 16:54:48 +01:00
if ( FBitSet ( VARS ( pentTarget ) - > flags , FL_MONSTER ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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
}
2021-11-28 16:54:48 +01: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 ;
}
2021-11-28 16:54:48 +01:00
if ( ! pTarget )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
CBaseEntity * pEntity = NULL ;
while ( ( pEntity = UTIL_FindEntityInSphere ( pEntity , pev - > origin , m_flRadius ) ) ! = NULL )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
if ( FClassnameIs ( pEntity - > pev , STRING ( m_iszEntity ) ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
if ( FBitSet ( pEntity - > pev - > flags , FL_MONSTER ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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
2021-11-29 20:31:17 +01:00
void CCineMonster : : PossessEntity ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
CBaseEntity * pEntity = m_hTargetEnt ;
CBaseMonster * pTarget = NULL ;
if ( pEntity )
2013-08-30 13:34:05 -07:00
pTarget = pEntity - > MyMonsterPointer ( ) ;
2021-11-28 16:54:48 +01:00
if ( pTarget )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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 )
{
2021-11-28 16:54:48 +01:00
case 0 :
pTarget - > m_scriptState = SCRIPT_WAIT ;
2013-08-30 13:34:05 -07:00
break ;
2021-11-28 16:54:48 +01:00
case 1 :
pTarget - > m_scriptState = SCRIPT_WALK_TO_MARK ;
DelayStart ( true ) ;
2013-08-30 13:34:05 -07:00
break ;
2021-11-28 16:54:48 +01:00
case 2 :
pTarget - > m_scriptState = SCRIPT_RUN_TO_MARK ;
DelayStart ( true ) ;
2013-08-30 13:34:05 -07:00
break ;
2021-11-28 16:54:48 +01:00
case 4 :
UTIL_SetOrigin ( pTarget - > pev , pev - > origin ) ;
2013-08-30 13:34:05 -07:00
pTarget - > pev - > ideal_yaw = pev - > angles . y ;
2021-11-28 16:54:48 +01:00
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 ;
}
2021-11-28 16:54:48 +01:00
// 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 ;
2021-11-28 15:32:26 +01:00
if ( ! FStringNull ( m_iszIdle ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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 ;
}
}
}
}
2021-11-28 16:54:48 +01:00
// make the entity carry out the scripted sequence instructions, but without
2013-08-30 13:34:05 -07:00
// destroying the monster's state.
2021-11-29 20:31:17 +01:00
void CCineAI : : PossessEntity ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
Schedule_t * pNewSchedule ;
2013-08-30 13:34:05 -07:00
2021-11-28 16:54:48 +01:00
CBaseEntity * pEntity = m_hTargetEnt ;
CBaseMonster * pTarget = NULL ;
if ( pEntity )
2013-08-30 13:34:05 -07:00
pTarget = pEntity - > MyMonsterPointer ( ) ;
2021-11-28 16:54:48 +01:00
if ( pTarget )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
if ( ! pTarget - > CanPlaySequence ( FCanOverrideState ( ) , SS_INTERRUPT_AI ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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 )
{
2021-11-28 16:54:48 +01:00
case 0 :
2013-08-30 13:34:05 -07:00
case 5 :
2021-11-28 16:54:48 +01:00
pTarget - > m_scriptState = SCRIPT_WAIT ;
2013-08-30 13:34:05 -07:00
break ;
2021-11-28 16:54:48 +01:00
case 1 :
pTarget - > m_scriptState = SCRIPT_WALK_TO_MARK ;
2013-08-30 13:34:05 -07:00
break ;
2021-11-28 16:54:48 +01:00
case 2 :
pTarget - > m_scriptState = SCRIPT_RUN_TO_MARK ;
2013-08-30 13:34:05 -07:00
break ;
2021-11-28 16:54:48 +01:00
case 4 :
2013-08-30 13:34:05 -07:00
// zap the monster instantly to the site of the script entity.
2021-11-28 16:54:48 +01:00
UTIL_SetOrigin ( pTarget - > pev , pev - > origin ) ;
2013-08-30 13:34:05 -07:00
pTarget - > pev - > ideal_yaw = pev - > angles . y ;
2021-11-28 16:54:48 +01:00
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 :
2021-11-28 16:54:48 +01:00
ALERT ( at_aiconsole , " aiscript: invalid Move To Position value! " ) ;
2013-08-30 13:34:05 -07:00
break ;
}
2021-11-28 16:54:48 +01:00
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 ;
2021-11-28 16:54:48 +01:00
/*
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?
2021-11-28 16:54:48 +01:00
if ( pTarget - > m_MonsterState = = MONSTERSTATE_SCRIPT )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
pNewSchedule = pTarget - > GetScheduleOfType ( SCHED_AISCRIPT ) ;
pTarget - > ChangeSchedule ( pNewSchedule ) ;
2013-08-30 13:34:05 -07:00
}
}
}
2021-11-29 20:31:17 +01:00
void CCineMonster : : CineThink ( )
2013-08-30 13:34:05 -07:00
{
if ( FindEntity ( ) )
{
2021-11-28 16:54:48 +01:00
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
{
2021-11-28 16:54:48 +01:00
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
2021-11-29 20:31:17 +01:00
bool CCineMonster : : StartSequence ( CBaseMonster * pTarget , int iszSeq , bool completeOnEmpty )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
if ( FStringNull ( iszSeq ) & & completeOnEmpty )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
SequenceDone ( pTarget ) ;
2021-11-19 13:43:33 +01:00
return false ;
2013-08-30 13:34:05 -07:00
}
2021-11-28 16:54:48 +01:00
pTarget - > pev - > sequence = pTarget - > LookupSequence ( STRING ( iszSeq ) ) ;
2013-08-30 13:34:05 -07:00
if ( pTarget - > pev - > sequence = = - 1 )
{
2021-11-28 16:54:48 +01:00
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 ;
2021-11-28 16:54:48 +01:00
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.
2021-11-29 20:31:17 +01:00
bool CCineAI : : StartSequence ( CBaseMonster * pTarget , int iszSeq , bool completeOnEmpty )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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.
2021-11-28 16:54:48 +01:00
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
}
2021-11-28 16:54:48 +01:00
pTarget - > pev - > sequence = pTarget - > LookupSequence ( STRING ( iszSeq ) ) ;
2013-08-30 13:34:05 -07:00
if ( pTarget - > pev - > sequence = = - 1 )
{
2021-11-28 16:54:48 +01:00
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 ;
2021-11-28 16:54:48 +01:00
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
2021-11-28 16:54:48 +01:00
// possesses.
2013-08-30 13:34:05 -07:00
//=========================================================
2021-11-29 20:31:17 +01: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 ) );
2021-11-28 16:54:48 +01:00
if ( ( pev - > spawnflags & SF_SCRIPT_REPEATABLE ) = = 0 )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
SetThink ( & CCineMonster : : SUB_Remove ) ;
2013-08-30 13:34:05 -07:00
pev - > nextthink = gpGlobals - > time + 0.1 ;
}
2021-11-28 16:54:48 +01:00
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
2021-11-28 16:54:48 +01:00
2013-08-30 13:34:05 -07:00
pMonster - > CineCleanup ( ) ;
2021-11-28 16:54:48 +01:00
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
2021-11-28 16:54:48 +01:00
SUB_UseTargets ( NULL , USE_TOGGLE , 0 ) ;
2013-08-30 13:34:05 -07:00
}
//=========================================================
2021-11-28 16:54:48 +01: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.
//=========================================================
2021-11-29 20:31:17 +01:00
void CCineMonster : : FixScriptMonsterSchedule ( CBaseMonster * pMonster )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
if ( pMonster - > m_IdealMonsterState ! = MONSTERSTATE_DEAD )
2013-08-30 13:34:05 -07:00
pMonster - > m_IdealMonsterState = MONSTERSTATE_IDLE ;
pMonster - > ClearSchedule ( ) ;
}
//=========================================================
2021-11-28 16:54:48 +01: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
//
// AI Scripted sequences will, depending on what the level
// designer selects:
//
2021-11-28 16:54:48 +01:00
// -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.
//=========================================================
2021-11-29 20:31:17 +01:00
void CCineAI : : FixScriptMonsterSchedule ( CBaseMonster * pMonster )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
switch ( m_iFinishSchedule )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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
}
}
2021-11-29 20:31:17 +01:00
bool CBaseMonster : : ExitScriptedSequence ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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 )
{
2021-11-28 16:54:48 +01:00
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
}
2021-11-28 16:54:48 +01:00
void CCineMonster : : AllowInterrupt ( bool fAllow )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
if ( ( pev - > spawnflags & SF_SCRIPT_NOINTERRUPT ) ! = 0 )
2013-08-30 13:34:05 -07:00
return ;
m_interruptable = fAllow ;
}
2021-11-19 14:31:11 +01:00
bool CCineMonster : : CanInterrupt ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
if ( ! m_interruptable )
2021-11-19 13:43:33 +01:00
return false ;
2013-08-30 13:34:05 -07:00
2021-11-28 16:54:48 +01:00
CBaseEntity * pTarget = m_hTargetEnt ;
2013-08-30 13:34:05 -07:00
2021-11-28 16:54:48 +01: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
}
2021-11-28 16:54:48 +01:00
int CCineMonster : : IgnoreConditions ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
if ( CanInterrupt ( ) )
2013-08-30 13:34:05 -07:00
return 0 ;
return SCRIPT_BREAK_CONDITIONS ;
}
2021-11-28 16:54:48 +01:00
void ScriptEntityCancel ( edict_t * pentCine )
2013-08-30 13:34:05 -07:00
{
// make sure they are a scripted_sequence
2021-11-28 16:54:48 +01:00
if ( FClassnameIs ( pentCine , CLASSNAME ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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
2021-11-28 16:54:48 +01:00
CBaseEntity * pEntity = pCineTarget - > m_hTargetEnt ;
CBaseMonster * pTarget = NULL ;
if ( pEntity )
2013-08-30 13:34:05 -07:00
pTarget = pEntity - > MyMonsterPointer ( ) ;
2021-11-28 16:54:48 +01:00
2013-08-30 13:34:05 -07:00
if ( pTarget )
{
// make sure their monster is actually playing a script
2021-11-28 16:54:48 +01:00
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
2021-11-28 16:54:48 +01:00
pTarget - > CineCleanup ( ) ;
2013-08-30 13:34:05 -07:00
}
}
}
}
// find all the cinematic entities with my targetname and stop them from playing
2021-11-29 20:31:17 +01:00
void CCineMonster : : CancelScript ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
ALERT ( at_aiconsole , " Cancelling script: %s \n " , STRING ( m_iszPlay ) ) ;
if ( FStringNull ( pev - > targetname ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
ScriptEntityCancel ( edict ( ) ) ;
2013-08-30 13:34:05 -07:00
return ;
}
2021-11-28 16:54:48 +01:00
edict_t * pentCineTarget = FIND_ENTITY_BY_TARGETNAME ( NULL , STRING ( pev - > targetname ) ) ;
2013-08-30 13:34:05 -07:00
while ( ! FNullEnt ( pentCineTarget ) )
{
2021-11-28 16:54:48 +01:00
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
2021-11-29 20:31:17 +01:00
void CCineMonster : : DelayStart ( bool state )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
edict_t * pentCine = FIND_ENTITY_BY_TARGETNAME ( NULL , STRING ( pev - > targetname ) ) ;
2013-08-30 13:34:05 -07:00
while ( ! FNullEnt ( pentCine ) )
{
2021-11-28 16:54:48 +01:00
if ( FClassnameIs ( pentCine , " scripted_sequence " ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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.
2021-11-29 20:31:17 +01:00
void CCineMonster : : Activate ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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 ) )
{
2021-11-28 16:54:48 +01:00
if ( FBitSet ( VARS ( pentTarget ) - > flags , FL_MONSTER ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
pTarget = GetMonsterPointer ( pentTarget ) ;
2013-08-30 13:34:05 -07:00
}
pentTarget = FIND_ENTITY_BY_TARGETNAME ( pentTarget , STRING ( m_iszEntity ) ) ;
}
2021-11-28 16:54:48 +01:00
2013-08-30 13:34:05 -07:00
// If no entity with that targetname, check the classname
2021-11-28 16:54:48 +01:00
if ( ! pTarget )
2013-08-30 13:34:05 -07:00
{
pentTarget = FIND_ENTITY_BY_CLASSNAME ( NULL , STRING ( m_iszEntity ) ) ;
while ( ! pTarget & & ! FNullEnt ( pentTarget ) )
{
2021-11-28 16:54:48 +01:00
pTarget = GetMonsterPointer ( pentTarget ) ;
2013-08-30 13:34:05 -07:00
pentTarget = FIND_ENTITY_BY_TARGETNAME ( pentTarget , STRING ( m_iszEntity ) ) ;
}
}
// Found a compatible entity
2021-11-28 16:54:48 +01:00
if ( pTarget )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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
2021-11-28 16:54:48 +01:00
SequencePrecache ( pmodel , STRING ( m_iszIdle ) ) ;
SequencePrecache ( pmodel , STRING ( m_iszPlay ) ) ;
2013-08-30 13:34:05 -07:00
}
}
}
2021-11-28 16:54:48 +01:00
2021-11-29 20:31:17 +01:00
bool CBaseMonster : : CineCleanup ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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
2021-11-28 16:54:48 +01:00
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?
2021-11-28 16:54:48 +01:00
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 ;
2021-11-28 16:54:48 +01:00
UTIL_SetSize ( pev , pev - > mins , Vector ( pev - > maxs . x , pev - > maxs . y , pev - > mins . z + 2 ) ) ;
2013-08-30 13:34:05 -07:00
2021-11-28 16:54:48 +01:00
if ( pOldCine & & FBitSet ( pOldCine - > pev - > spawnflags , SF_SCRIPT_LEAVECORPSE ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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 ;
2021-11-28 16:54:48 +01:00
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
2021-11-28 16:54:48 +01:00
if ( pOldCine & & ! FStringNull ( pOldCine - > m_iszPlay ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
if ( ( pOldCine - > pev - > spawnflags & SF_SCRIPT_NOSCRIPTMOVEMENT ) = = 0 )
2013-08-30 13:34:05 -07:00
{
// reset position
Vector new_origin , new_angle ;
2021-11-28 16:54:48 +01:00
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 ;
2021-11-28 16:54:48 +01:00
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
2021-11-28 16:54:48 +01:00
if ( drop < 0 )
2013-08-30 13:34:05 -07:00
pev - > origin = oldOrigin ;
2021-11-28 16:54:48 +01:00
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
2021-11-28 16:54:48 +01:00
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 ;
2021-11-28 16:54:48 +01:00
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 ;
2021-11-28 16:54:48 +01:00
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 );
2021-11-28 16:54:48 +01:00
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 :
2021-03-05 23:07:22 +01:00
void Spawn ( ) override ;
2021-11-28 16:54:48 +01:00
bool KeyValue ( KeyValueData * pkvd ) override ;
void Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) override ;
2021-03-05 20:54:33 +01:00
void EXPORT FindThink ( ) ;
void EXPORT DelayThink ( ) ;
2021-11-29 20:31:17 +01:00
int ObjectCaps ( ) override { return ( CBaseToggle : : ObjectCaps ( ) & ~ FCAP_ACROSS_TRANSITION ) ; }
2013-08-30 13:34:05 -07:00
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
2024-08-28 16:59:46 +02:00
CBaseToggle * FindEntity ( ) ;
bool AcceptableSpeaker ( CBaseToggle * pTarget ) ;
bool StartSentence ( CBaseToggle * pTarget ) ;
2013-08-30 13:34:05 -07:00
private :
2021-11-28 16:54:48 +01:00
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
} ;
2021-11-28 16:54:48 +01: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
2021-11-28 16:54:48 +01: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
} ;
2021-11-28 16:54:48 +01:00
IMPLEMENT_SAVERESTORE ( CScriptedSentence , CBaseToggle ) ;
2013-08-30 13:34:05 -07:00
2021-11-28 16:54:48 +01:00
LINK_ENTITY_TO_CLASS ( scripted_sentence , CScriptedSentence ) ;
2013-08-30 13:34:05 -07:00
2021-11-29 20:31:17 +01:00
bool CScriptedSentence : : KeyValue ( KeyValueData * pkvd )
2013-08-30 13:34:05 -07:00
{
if ( FStrEq ( pkvd - > szKeyName , " sentence " ) )
{
2021-11-28 16:54:48 +01:00
m_iszSentence = ALLOC_STRING ( 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 , " entity " ) )
{
2021-11-28 16:54:48 +01:00
m_iszEntity = ALLOC_STRING ( 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 , " duration " ) )
{
2021-11-28 16:54:48 +01:00
m_flDuration = 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 , " radius " ) )
{
2021-11-28 16:54:48 +01:00
m_flRadius = 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 , " refire " ) )
{
2021-11-28 16:54:48 +01:00
m_flRepeat = atof ( pkvd - > szValue ) ;
2021-11-28 15:32:26 +01:00
return true ;
2013-08-30 13:34:05 -07:00
}
2021-11-28 16:54:48 +01:00
else if ( FStrEq ( pkvd - > szKeyName , " attenuation " ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
pev - > impulse = atoi ( pkvd - > szValue ) ;
2021-11-28 15:32:26 +01:00
return true ;
2013-08-30 13:34:05 -07:00
}
2021-11-28 16:54:48 +01:00
else if ( FStrEq ( pkvd - > szKeyName , " volume " ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
m_flVolume = atof ( pkvd - > szValue ) * 0.1 ;
2021-11-28 15:32:26 +01:00
return true ;
2013-08-30 13:34:05 -07:00
}
else if ( FStrEq ( pkvd - > szKeyName , " listener " ) )
{
2021-11-28 16:54:48 +01:00
m_iszListener = ALLOC_STRING ( 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 CScriptedSentence : : 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 ( ! m_active )
2013-08-30 13:34:05 -07:00
return ;
2021-11-28 16:54:48 +01:00
// 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 ;
}
2021-11-29 20:31:17 +01:00
void CScriptedSentence : : Spawn ( )
2013-08-30 13:34:05 -07:00
{
pev - > solid = SOLID_NOT ;
2021-11-28 16:54:48 +01:00
2021-11-19 13:45:16 +01:00
m_active = true ;
2013-08-30 13:34:05 -07:00
// if no targetname, start now
2021-11-28 16:54:48 +01:00
if ( FStringNull ( pev - > targetname ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
SetThink ( & CScriptedSentence : : FindThink ) ;
2013-08-30 13:34:05 -07:00
pev - > nextthink = gpGlobals - > time + 1.0 ;
}
2021-11-28 16:54:48 +01:00
switch ( pev - > impulse )
2013-08-30 13:34:05 -07:00
{
case 1 : // Medium radius
m_flAttenuation = ATTN_STATIC ;
break ;
2021-11-28 16:54:48 +01:00
case 2 : // Large radius
2013-08-30 13:34:05 -07:00
m_flAttenuation = ATTN_NORM ;
break ;
2021-11-28 16:54:48 +01:00
case 3 : //EVERYWHERE
2013-08-30 13:34:05 -07:00
m_flAttenuation = ATTN_NONE ;
break ;
2021-11-28 16:54:48 +01:00
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
2021-11-28 16:54:48 +01:00
if ( m_flVolume < = 0 )
2013-08-30 13:34:05 -07:00
m_flVolume = 1.0 ;
}
2021-11-29 20:31:17 +01:00
void CScriptedSentence : : FindThink ( )
2013-08-30 13:34:05 -07:00
{
2024-08-28 16:59:46 +02:00
CBaseToggle * pEnt = FindEntity ( ) ;
if ( pEnt )
2013-08-30 13:34:05 -07:00
{
2024-08-28 16:59:46 +02:00
StartSentence ( pEnt ) ;
2021-11-28 16:54:48 +01:00
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 ;
2021-11-28 16:54:48 +01:00
// ALERT( at_console, "%s: found monster %s\n", STRING(m_iszSentence), STRING(m_iszEntity) );
2013-08-30 13:34:05 -07:00
}
else
{
2021-11-28 16:54:48 +01:00
// 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 ;
}
}
2021-11-29 20:31:17 +01:00
void CScriptedSentence : : DelayThink ( )
2013-08-30 13:34:05 -07:00
{
2021-11-19 13:45:16 +01:00
m_active = true ;
2021-11-28 16:54:48 +01:00
if ( FStringNull ( pev - > targetname ) )
2013-08-30 13:34:05 -07:00
pev - > nextthink = gpGlobals - > time + 0.1 ;
2021-11-28 16:54:48 +01:00
SetThink ( & CScriptedSentence : : FindThink ) ;
2013-08-30 13:34:05 -07:00
}
2024-08-28 16:59:46 +02:00
bool CScriptedSentence : : AcceptableSpeaker ( CBaseToggle * pTarget )
2013-08-30 13:34:05 -07:00
{
2024-08-28 16:59:46 +02:00
CBaseMonster * pMonster ;
pMonster = NULL ;
if ( pTarget )
{
pMonster = pTarget - > MyMonsterPointer ( ) ;
}
2021-11-28 16:54:48 +01:00
if ( pMonster )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
if ( ( pev - > spawnflags & SF_SENTENCE_FOLLOWERS ) ! = 0 )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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
}
2021-11-19 14:40:35 +01:00
bool override ;
2021-11-28 16:54:48 +01:00
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 ;
2021-11-28 16:54:48 +01:00
if ( pMonster - > CanPlaySentence ( override ) )
2021-11-19 13:45:16 +01:00
return true ;
2013-08-30 13:34:05 -07:00
}
2024-08-28 16:59:46 +02: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
}
2024-08-28 16:59:46 +02:00
CBaseToggle * CScriptedSentence : : FindEntity ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
edict_t * pentTarget ;
2024-08-28 16:59:46 +02:00
CBaseToggle * pSpeakingEnt ;
2013-08-30 13:34:05 -07:00
pentTarget = FIND_ENTITY_BY_TARGETNAME ( NULL , STRING ( m_iszEntity ) ) ;
2024-08-28 16:59:46 +02:00
pSpeakingEnt = NULL ;
2013-08-30 13:34:05 -07:00
while ( ! FNullEnt ( pentTarget ) )
{
2024-08-28 16:59:46 +02:00
CBaseEntity * pEnt = Instance ( pentTarget ) ;
pSpeakingEnt = pEnt ? pEnt - > MyTogglePointer ( ) : NULL ;
if ( pSpeakingEnt ! = NULL )
2013-08-30 13:34:05 -07:00
{
2024-08-28 16:59:46 +02: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 ) ) ;
}
2021-11-28 16:54:48 +01:00
CBaseEntity * pEntity = NULL ;
while ( ( pEntity = UTIL_FindEntityInSphere ( pEntity , pev - > origin , m_flRadius ) ) ! = NULL )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
if ( FClassnameIs ( pEntity - > pev , STRING ( m_iszEntity ) ) )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
if ( FBitSet ( pEntity - > pev - > flags , FL_MONSTER ) )
2013-08-30 13:34:05 -07:00
{
2024-08-28 16:59:46 +02:00
pSpeakingEnt = pEntity - > MyTogglePointer ( ) ;
if ( AcceptableSpeaker ( pSpeakingEnt ) )
return pSpeakingEnt ;
2013-08-30 13:34:05 -07:00
}
}
}
2021-11-28 16:54:48 +01:00
2013-08-30 13:34:05 -07:00
return NULL ;
}
2024-08-28 16:59:46 +02:00
bool CScriptedSentence : : StartSentence ( CBaseToggle * pTarget )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
if ( ! pTarget )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01: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
}
2021-11-19 14:40:35 +01:00
bool bConcurrent = false ;
2021-11-28 16:54:48 +01:00
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
2021-11-28 16:54:48 +01:00
CBaseEntity * pListener = NULL ;
2013-08-30 13:34:05 -07:00
if ( ! FStringNull ( m_iszListener ) )
{
float radius = m_flRadius ;
2021-11-28 16:54:48 +01:00
if ( FStrEq ( STRING ( m_iszListener ) , " player " ) )
radius = 4096 ; // Always find the player
2013-08-30 13:34:05 -07:00
2021-11-28 16:54:48 +01:00
pListener = UTIL_FindEntityGeneric ( STRING ( m_iszListener ) , pTarget - > pev - > origin , radius ) ;
2013-08-30 13:34:05 -07:00
}
2021-11-28 16:54:48 +01: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 :
2021-11-28 16:54:48 +01:00
void Spawn ( ) override ;
2021-03-05 20:54:33 +01:00
void Die ( ) ;
2021-11-28 16:54:48 +01:00
int Classify ( ) override ;
2021-11-29 20:31:17 +01:00
int ObjectCaps ( ) override { return ( CBaseMonster : : ObjectCaps ( ) & ~ FCAP_ACROSS_TRANSITION ) ; }
2013-08-30 13:34:05 -07:00
} ;
2021-11-28 16:54:48 +01:00
LINK_ENTITY_TO_CLASS ( monster_furniture , CFurniture ) ;
2013-08-30 13:34:05 -07:00
//=========================================================
// Furniture is killed
//=========================================================
2021-11-29 20:31:17 +01:00
void CFurniture : : Die ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
SetThink ( & CFurniture : : SUB_Remove ) ;
2013-08-30 13:34:05 -07:00
pev - > nextthink = gpGlobals - > time ;
}
//=========================================================
2021-11-28 16:54:48 +01:00
// 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
//=========================================================
2021-11-29 20:31:17 +01:00
void CFurniture : : Spawn ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
PRECACHE_MODEL ( ( char * ) STRING ( pev - > model ) ) ;
SET_MODEL ( ENT ( pev ) , STRING ( pev - > model ) ) ;
2013-08-30 13:34:05 -07:00
2021-11-28 16:54:48 +01:00
pev - > movetype = MOVETYPE_NONE ;
pev - > solid = SOLID_BBOX ;
pev - > health = 80000 ;
2013-08-30 13:34:05 -07:00
pev - > takedamage = DAMAGE_AIM ;
2021-11-28 16:54:48 +01:00
pev - > effects = 0 ;
pev - > yaw_speed = 0 ;
pev - > sequence = 0 ;
pev - > frame = 0 ;
2013-08-30 13:34:05 -07:00
2021-11-28 16:54:48 +01:00
// pev->nextthink += 1.0;
// SetThink (WalkMonsterDelay);
2013-08-30 13:34:05 -07:00
2021-11-28 16:54:48 +01:00
ResetSequenceInfo ( ) ;
2013-08-30 13:34:05 -07:00
pev - > frame = 0 ;
MonsterInit ( ) ;
}
//=========================================================
// ID's Furniture as neutral (noone will attack it)
//=========================================================
2021-11-28 16:54:48 +01:00
int CFurniture : : Classify ( )
2013-08-30 13:34:05 -07:00
{
2021-11-28 16:54:48 +01:00
return CLASS_NONE ;
2013-08-30 13:34:05 -07:00
}