Skip to content

Instantly share code, notes, and snippets.

@FreeSlave
Created December 2, 2022 12:51
Show Gist options
  • Save FreeSlave/0480df7c9c1713f99523638fd79aae6b to your computer and use it in GitHub Desktop.
Save FreeSlave/0480df7c9c1713f99523638fd79aae6b to your computer and use it in GitHub Desktop.
trigger_random (Sven Co-op like)
#define TRIGGER_RANDOM_MAX_COUNT 16
#define SF_TRIGGER_RANDOM_START_ON 1
#define SF_TRIGGER_RANDOM_ONCE 2
#define SF_TRIGGER_RANDOM_REUSABLE 4
#define SF_TRIGGER_RANDOM_TIMED 8
#define SF_TRIGGER_RANDOM_UNIQUE 16
#define SF_TRIGGER_RANDOM_DONT_REPEAT 32
class CTriggerRandom : public CPointEntity
{
public:
void Spawn();
void KeyValue( KeyValueData *pkvd );
void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value);
void EXPORT TimedThink();
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
string_t ChooseTarget();
float GetRandomDelay();
int TargetCount();
int m_targetCount;
string_t m_targets[TRIGGER_RANDOM_MAX_COUNT];
int m_uniqueTargetsLeft;
int m_triggerNumberLimit;
int m_triggerCounter;
float m_minDelay;
float m_maxDelay;
string_t m_lastTarget;
string_t m_triggerOnLimit;
inline bool IsActive() { return pev->spawnflags & SF_TRIGGER_RANDOM_START_ON; }
inline void SetActive(bool active)
{
if (active)
{
SetBits(pev->spawnflags, SF_TRIGGER_RANDOM_START_ON);
}
else
{
ClearBits(pev->spawnflags, SF_TRIGGER_RANDOM_START_ON);
}
}
};
LINK_ENTITY_TO_CLASS( trigger_random, CTriggerRandom )
// Sven Co-op compatibility
LINK_ENTITY_TO_CLASS( trigger_random_time, CTriggerRandom )
LINK_ENTITY_TO_CLASS( trigger_random_unique, CTriggerRandom )
TYPEDESCRIPTION CTriggerRandom::m_SaveData[] =
{
DEFINE_FIELD( CTriggerRandom, m_targetCount, FIELD_INTEGER ),
DEFINE_ARRAY( CTriggerRandom, m_targets, FIELD_STRING, TRIGGER_RANDOM_MAX_COUNT ),
DEFINE_FIELD( CTriggerRandom, m_uniqueTargetsLeft, FIELD_INTEGER ),
DEFINE_FIELD( CTriggerRandom, m_triggerNumberLimit, FIELD_INTEGER ),
DEFINE_FIELD( CTriggerRandom, m_triggerCounter, FIELD_INTEGER ),
DEFINE_FIELD( CTriggerRandom, m_minDelay, FIELD_FLOAT ),
DEFINE_FIELD( CTriggerRandom, m_maxDelay, FIELD_FLOAT ),
DEFINE_FIELD( CTriggerRandom, m_lastTarget, FIELD_STRING ),
DEFINE_FIELD( CTriggerRandom, m_triggerOnLimit, FIELD_STRING ),
};
IMPLEMENT_SAVERESTORE( CTriggerRandom, CPointEntity )
void CTriggerRandom::KeyValue( KeyValueData *pkvd )
{
if ( FStrEq( pkvd->szKeyName, "min_delay") ) {
m_minDelay = atof( pkvd->szValue );
if (m_minDelay < 0) {
m_minDelay = 0;
}
pkvd->fHandled = TRUE;
} else if ( FStrEq( pkvd->szKeyName, "max_delay") ) {
m_maxDelay = atof( pkvd->szValue );
if (m_maxDelay < 0) {
m_maxDelay = 0;
}
pkvd->fHandled = TRUE;
} else if ( FStrEq( pkvd->szKeyName, "trigger_number") ) {
m_triggerNumberLimit = atoi( pkvd->szValue );
if (m_triggerNumberLimit < 0) {
m_triggerNumberLimit = 0;
}
pkvd->fHandled = TRUE;
} else if ( FStrEq( pkvd->szKeyName, "target_count" ) ) { // Sven Co-op compatibility
m_targetCount = atoi( pkvd->szValue );
if (m_targetCount < 0) {
m_targetCount = 0;
} else if (m_targetCount > TRIGGER_RANDOM_MAX_COUNT) {
m_targetCount = TRIGGER_RANDOM_MAX_COUNT;
}
pkvd->fHandled = TRUE;
} else if ( strncmp(pkvd->szKeyName, "target", 6) == 0 && isdigit(pkvd->szKeyName[6])) {
const int num = atoi(pkvd->szKeyName+6);
if (num <= 0 || num > TRIGGER_RANDOM_MAX_COUNT)
return;
const int index = num - 1;
m_targets[index] = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
} else if ( FStrEq( pkvd->szKeyName, "trigger_on_limit") ) {
m_triggerOnLimit = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
} else {
CBaseEntity::KeyValue( pkvd );
}
}
void CTriggerRandom::Spawn()
{
// Sven Co-op compatibility
if (FClassnameIs(pev, "trigger_random_time"))
{
SetBits(pev->spawnflags, SF_TRIGGER_RANDOM_TIMED);
}
if (FClassnameIs(pev, "trigger_random_unique"))
{
SetBits(pev->spawnflags, SF_TRIGGER_RANDOM_UNIQUE);
if (FBitSet(pev->spawnflags, 1))
{
SetBits(pev->spawnflags, SF_TRIGGER_RANDOM_REUSABLE);
ClearBits(pev->spawnflags, 1);
}
}
m_triggerCounter = 0;
if (FBitSet(pev->spawnflags, SF_TRIGGER_RANDOM_UNIQUE)) {
m_uniqueTargetsLeft = TargetCount();
}
if (FBitSet(pev->spawnflags, SF_TRIGGER_RANDOM_TIMED)) {
if (IsActive()) {
SetThink(&CTriggerRandom::TimedThink);
pev->nextthink = gpGlobals->time + GetRandomDelay() + 0.1;
}
}
}
void CTriggerRandom::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
{
if (pev->spawnflags & SF_TRIGGER_RANDOM_TIMED) {
SetActive(!IsActive());
if (IsActive()) {
SetThink(&CTriggerRandom::TimedThink);
pev->nextthink = gpGlobals->time + GetRandomDelay();
} else {
SetThink(NULL);
m_triggerCounter = 0;
}
} else {
const int chosenTarget = ChooseTarget();
if (!FStringNull(chosenTarget)) {
FireTargets(STRING(chosenTarget), pActivator, this, USE_TOGGLE, value);
}
}
}
void CTriggerRandom::TimedThink()
{
if (IsActive()) {
int chosenTarget = ChooseTarget();
if (!FStringNull(chosenTarget)) {
FireTargets(STRING(chosenTarget), this, this, USE_TOGGLE, 0);
if (m_triggerNumberLimit) {
m_triggerCounter++;
if (m_triggerCounter >= m_triggerNumberLimit) {
SetActive(false);
m_triggerCounter = 0;
if (!FStringNull(m_triggerOnLimit))
FireTargets(STRING(m_triggerOnLimit), this, this, USE_TOGGLE, 0.0f);
}
}
}
if (pev->spawnflags & SF_TRIGGER_RANDOM_ONCE)
SetActive(false);
if (IsActive()) {
pev->nextthink = gpGlobals->time + GetRandomDelay();
}
}
}
string_t CTriggerRandom::ChooseTarget()
{
int chosenTargetIndex = 0;
string_t chosenTarget = iStringNull;
if (pev->spawnflags & SF_TRIGGER_RANDOM_UNIQUE) {
if (m_uniqueTargetsLeft) {
chosenTargetIndex = RANDOM_LONG(0, m_uniqueTargetsLeft - 1);
chosenTarget = m_targets[chosenTargetIndex];
if (chosenTarget == m_lastTarget && FBitSet(pev->spawnflags, SF_TRIGGER_RANDOM_DONT_REPEAT) && m_uniqueTargetsLeft > 1)
{
chosenTargetIndex = (chosenTargetIndex+1) % m_uniqueTargetsLeft;
chosenTarget = m_targets[chosenTargetIndex];
}
m_targets[chosenTargetIndex] = m_targets[m_uniqueTargetsLeft-1];
m_targets[m_uniqueTargetsLeft-1] = chosenTarget;
m_uniqueTargetsLeft--;
if (!m_uniqueTargetsLeft && (pev->spawnflags & SF_TRIGGER_RANDOM_REUSABLE) ) {
m_uniqueTargetsLeft = TargetCount();
}
m_lastTarget = chosenTarget;
return chosenTarget;
}
} else {
const int targetCount = TargetCount();
if (targetCount) {
chosenTargetIndex = RANDOM_LONG(0, targetCount - 1);
chosenTarget = m_targets[chosenTargetIndex];
if (chosenTarget == m_lastTarget && FBitSet(pev->spawnflags, SF_TRIGGER_RANDOM_DONT_REPEAT) && targetCount > 1)
{
chosenTargetIndex = (chosenTargetIndex+1) % targetCount;
chosenTarget = m_targets[chosenTargetIndex];
}
m_lastTarget = chosenTarget;
return chosenTarget;
}
}
return iStringNull;
}
float CTriggerRandom::GetRandomDelay()
{
return RANDOM_FLOAT(m_minDelay, Q_max(m_maxDelay, m_minDelay));
}
int CTriggerRandom::TargetCount()
{
if (m_targetCount) {
return m_targetCount;
} else {
for (int i = ARRAYSIZE(m_targets) - 1; i>=0; --i ) {
if (!FStringNull(m_targets[i])) {
m_targetCount = i+1;
return m_targetCount;
}
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment