That's not the kind of random trigger he's looking for.
I wrote an implementation based on Source's logic_case:
const int MaxRandomTargets = 16;
const char TargetKeyValuePrefix[] = "target";
class CTriggerRandom : public CBaseDelay
{
public:
void KeyValue(KeyValueData* pkvd);
void Spawn();
void EXPORT RandomUse(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value);
int ObjectCaps() { return CBaseDelay::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
virtual int Save(CSave& save);
virtual int Restore(CRestore& restore);
static TYPEDESCRIPTION m_SaveData[];
string_t m_iszTargets[MaxRandomTargets];
private:
int BuildMap(std::array<std::uint8_t, MaxRandomTargets>& map);
};
TYPEDESCRIPTION CTriggerRandom::m_SaveData[] =
{
DEFINE_ARRAY(CTriggerRandom, m_iszTargets, FIELD_STRING, MaxRandomTargets),
};
IMPLEMENT_SAVERESTORE(CTriggerRandom, CBaseDelay);
LINK_ENTITY_TO_CLASS(trigger_random, CTriggerRandom);
void CTriggerRandom::KeyValue(KeyValueData* pkvd)
{
const size_t prefixLength = ARRAYSIZE(TargetKeyValuePrefix) - 1;
if (!strncmp(TargetKeyValuePrefix, pkvd->szKeyName, prefixLength))
{
const char* indexString = pkvd->szKeyName + prefixLength;
char* end;
int index = strtol(indexString, &end, 10);
//Must be a number and end after the number, e.g. "target1"
//index is 1 based [1, MaxRandomTargets]
if (end != indexString && *end == '\0')
{
--index;
if (index >= 0 && index < MaxRandomTargets)
{
if (FStringNull(m_iszTargets[index]))
{
m_iszTargets[index] = ALLOC_STRING(pkvd->szValue);
}
else
{
ALERT(at_error, "CTriggerRandom::KeyValue: target \"%s\" already set to \"%s\"", pkvd->szKeyName, STRING(m_iszTargets[index]));
}
}
else
{
ALERT(at_error, "CTriggerRandom::KeyValue: invalid target index \"%s\", must be in range [1, %d]", pkvd->szKeyName, MaxRandomTargets);
}
}
else
{
ALERT(at_error, "CTriggerRandom::KeyValue: invalid target format \"%s\"", pkvd->szKeyName);
}
}
else
{
CBaseDelay::KeyValue(pkvd);
}
}
void CTriggerRandom::Spawn()
{
SetUse(&CTriggerRandom::RandomUse);
}
void CTriggerRandom::RandomUse(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
{
std::array<std::uint8_t, MaxRandomTargets> map;
const int count = BuildMap(map);
if (count > 0)
{
const int index = RANDOM_LONG(0, count - 1);
const char* target = STRING(m_iszTargets[index]);
FireTargets(target, pActivator, pCaller, useType, 0);
}
}
int CTriggerRandom::BuildMap(std::array<std::uint8_t, MaxRandomTargets>& map)
{
int targetCount = 0;
for (int i = 0; i < MaxRandomTargets; ++i)
{
if (!FStringNull(m_iszTargets[i]))
{
++targetCount;
map[i] = i;
}
}
return targetCount;
}
Add it to the bottom of triggers.cpp.
Also add this as the first include files:
#include <array>
#include <cstdint>
FGD entry:
@PointClass base(Targetname) = trigger_random : "Random Trigger"
[
target1(target_destination) : "Target 1"
target2(target_destination) : "Target 2"
target3(target_destination) : "Target 3"
target4(target_destination) : "Target 4"
target5(target_destination) : "Target 5"
target6(target_destination) : "Target 6"
target7(target_destination) : "Target 7"
target8(target_destination) : "Target 8"
target9(target_destination) : "Target 9"
target10(target_destination) : "Target 10"
target11(target_destination) : "Target 11"
target12(target_destination) : "Target 12"
target13(target_destination) : "Target 13"
target14(target_destination) : "Target 14"
target15(target_destination) : "Target 15"
target16(target_destination) : "Target 16"
]
I haven't tested it but it should work fine. It logs invalid keyvalue inputs and will treat the "target" keyvalue as an invalid input since it isn't meant to be used with this entity.
It works just like
logic_case does when using the
PickRandom
input. All target keyvalues that have been set will be considered and one is randomly chosen. You can turn it into a random do/do nothing setup by setting targets that don't exist, e.g. "dummytarget".