mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
822 lines
23 KiB
C++
822 lines
23 KiB
C++
// Game_console.cpp
|
|
//
|
|
|
|
// Container class for all world objects that have GUIs on them. (except monsters & weapons)
|
|
// Provides some keys to these guis not provided by default GUI code like "rand"
|
|
|
|
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "prey_local.h"
|
|
|
|
#define TRANSLATION_TIME 1000 // Time over which translation takes place
|
|
|
|
const idEventDef EV_CallGuiEvent("guiEvent", "s");
|
|
|
|
//==========================================================================
|
|
//
|
|
// hhConsole
|
|
//
|
|
//==========================================================================
|
|
|
|
CLASS_DECLARATION(idStaticEntity, hhConsole)
|
|
EVENT( EV_Activate, hhConsole::Event_Activate )
|
|
EVENT( EV_TalonAction, hhConsole::Event_TalonAction )
|
|
EVENT( EV_CallGuiEvent, hhConsole::Event_CallGuiEvent )
|
|
EVENT( EV_BecomeNonSolid, hhConsole::Event_BecomeNonSolid )
|
|
EVENT( EV_PostSpawn, hhConsole::Event_PostSpawn )
|
|
END_CLASS
|
|
|
|
void hhConsole::Spawn() {
|
|
// BecomeActive(TH_MISC2); // For ai
|
|
|
|
bTimeEventsAlways = spawnArgs.GetBool("timeEventsAlways");
|
|
bUsesRand = spawnArgs.GetBool("usesRand");
|
|
if (bUsesRand || bTimeEventsAlways) {
|
|
BecomeActive(TH_THINK);
|
|
}
|
|
|
|
bool bDamageable = !spawnArgs.GetBool("noDamage");
|
|
if (bDamageable) {
|
|
fl.takedamage = true;
|
|
}
|
|
|
|
// Stuff color into gui_parms 20,21,22
|
|
idVec3 color;
|
|
GetColor(color);
|
|
SetOnAllGuis("gui_parm20", color[0]);
|
|
SetOnAllGuis("gui_parm21", color[1]);
|
|
SetOnAllGuis("gui_parm22", color[2]);
|
|
|
|
// Translation variables
|
|
translationAlpha.Init(gameLocal.time, 0, 0.0f, 0.0f); // Start at 0 (untranslated)
|
|
transState = TS_UNTRANSLATED;
|
|
|
|
// Start idle sound
|
|
StartSound("snd_idle", SND_CHANNEL_ANY, 0, true, NULL);
|
|
|
|
UpdateVisuals();
|
|
|
|
// Init AI data
|
|
aiCanUse = spawnArgs.GetBool("ai_can_use");
|
|
aiUseCount = 0;
|
|
aiReuseWaitTime = (int)(spawnArgs.GetFloat("ai_reuse_wait")*1000.0f);
|
|
aiUseTime = (int)(spawnArgs.GetFloat("ai_use_time")*1000.0f);
|
|
aiTriggerWaitTime = (int)(spawnArgs.GetFloat("ai_use_trigger_wait")*1000.0f);
|
|
aiMaxUses = spawnArgs.GetInt("ai_max_uses", "-1");
|
|
aiRetriggerWait = (int)(spawnArgs.GetFloat("ai_use_retrigger_wait")*1000.0f);
|
|
|
|
// Clamp the trigger wait to be less than the total use time
|
|
if(aiTriggerWaitTime > aiUseTime) {
|
|
aiTriggerWaitTime = aiUseTime - 32;
|
|
if(aiTriggerWaitTime < 0) {
|
|
aiTriggerWaitTime = 0;
|
|
}
|
|
}
|
|
|
|
aiCurrUsedStartTime = gameLocal.GetTime();
|
|
aiCurrUsedBy = NULL;
|
|
aiLastUsedBy = NULL;
|
|
aiLastUsedTime = -aiReuseWaitTime;
|
|
aiWaitingToTrigger = true;
|
|
aiLastTriggerTime = gameLocal.GetTime() - 1000;
|
|
|
|
perchSpot = NULL;
|
|
PostEventMS( &EV_PostSpawn, 0 );
|
|
}
|
|
|
|
void hhConsole::Event_PostSpawn() {
|
|
// Automatically cause all consoles to spawn Talon perch spots
|
|
if(!spawnArgs.GetBool("noTalonTarget")) {
|
|
idDict args;
|
|
idVec3 offset;
|
|
bool bGuiInteractive = false;
|
|
|
|
// cjr - Iterate through guis and determine if any are interactive. If all are not, then Talon should not squawk
|
|
for ( int ix = 0; ix < MAX_RENDERENTITY_GUI; ix++ ) {
|
|
if ( renderEntity.gui[ix] && renderEntity.gui[ix]->IsInteractive() ) {
|
|
bGuiInteractive = true;
|
|
}
|
|
}
|
|
|
|
const char *perchDef;
|
|
if ( spawnArgs.GetBool( "noTalonSquawk" ) || !bGuiInteractive) { // CJR: Check if talon should squawk at this spot
|
|
perchDef = spawnArgs.GetString("def_perch");
|
|
} else {
|
|
perchDef = spawnArgs.GetString("def_perchSquawk"); // Talon should squawk at this spot
|
|
}
|
|
|
|
if (!gameLocal.isClient) {
|
|
if (perchDef && perchDef[0]) {
|
|
// Set the offset for the perch location and priority
|
|
offset = GetPhysics()->GetAxis() * spawnArgs.GetVector("offset_perch", "0 0 24");
|
|
float offsetYaw = spawnArgs.GetFloat("offset_perchyaw");
|
|
args.SetVector("origin", GetPhysics()->GetOrigin() + offset);
|
|
|
|
idAngles angles = GetAxis().ToAngles();
|
|
angles.yaw += offsetYaw;
|
|
args.SetMatrix("rotation", angles.ToMat3());
|
|
args.Set("target", this->name.c_str());
|
|
perchSpot = (hhTalonTarget *)gameLocal.SpawnObject( perchDef, &args ); // Consoles are automatically high priority spots
|
|
if (perchSpot && ( spawnArgs.GetBool("bindPerchSpot") || IsBound() ) ) {
|
|
perchSpot->Bind(this, true);
|
|
}
|
|
}
|
|
else {
|
|
gameLocal.Warning("Need def_perch key on %s", name.c_str());
|
|
perchSpot = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhConsole::Save(idSaveGame *savefile) const {
|
|
savefile->WriteFloat( translationAlpha.GetStartTime() ); // idInterpolate<float>
|
|
savefile->WriteFloat( translationAlpha.GetDuration() );
|
|
savefile->WriteFloat( translationAlpha.GetStartValue() );
|
|
savefile->WriteFloat( translationAlpha.GetEndValue() );
|
|
|
|
savefile->Write( &transState, sizeof(transState) );
|
|
savefile->WriteBool( bTimeEventsAlways );
|
|
savefile->WriteBool( bUsesRand );
|
|
savefile->WriteObject( perchSpot );
|
|
savefile->WriteBool( aiCanUse );
|
|
savefile->WriteInt( aiUseCount );
|
|
savefile->WriteInt( aiMaxUses );
|
|
savefile->WriteInt( aiReuseWaitTime );
|
|
savefile->WriteInt( aiUseTime );
|
|
savefile->WriteInt( aiTriggerWaitTime );
|
|
savefile->WriteBool( aiWaitingToTrigger );
|
|
savefile->WriteInt( aiLastTriggerTime );
|
|
savefile->WriteInt( aiRetriggerWait );
|
|
aiCurrUsedBy.Save(savefile);
|
|
savefile->WriteInt( aiCurrUsedStartTime );
|
|
aiLastUsedBy.Save(savefile);
|
|
savefile->WriteInt( aiLastUsedTime );
|
|
}
|
|
|
|
void hhConsole::Restore( idRestoreGame *savefile ) {
|
|
float set;
|
|
|
|
savefile->ReadFloat( set ); // idInterpolate<float>
|
|
translationAlpha.SetStartTime( set );
|
|
savefile->ReadFloat( set );
|
|
translationAlpha.SetDuration( set );
|
|
savefile->ReadFloat( set );
|
|
translationAlpha.SetStartValue(set);
|
|
savefile->ReadFloat( set );
|
|
translationAlpha.SetEndValue( set );
|
|
|
|
savefile->Read( &transState, sizeof(transState) );
|
|
savefile->ReadBool( bTimeEventsAlways );
|
|
savefile->ReadBool( bUsesRand );
|
|
savefile->ReadObject( reinterpret_cast<idClass *&>(perchSpot) );
|
|
savefile->ReadBool( aiCanUse );
|
|
savefile->ReadInt( aiUseCount );
|
|
savefile->ReadInt( aiMaxUses );
|
|
savefile->ReadInt( aiReuseWaitTime );
|
|
savefile->ReadInt( aiUseTime );
|
|
savefile->ReadInt( aiTriggerWaitTime );
|
|
savefile->ReadBool( aiWaitingToTrigger );
|
|
savefile->ReadInt( aiLastTriggerTime );
|
|
savefile->ReadInt( aiRetriggerWait );
|
|
|
|
aiCurrUsedBy.Restore(savefile);
|
|
savefile->ReadInt( aiCurrUsedStartTime );
|
|
aiLastUsedBy.Restore(savefile);
|
|
savefile->ReadInt( aiLastUsedTime );
|
|
}
|
|
|
|
void hhConsole::Event_CallGuiEvent(const char *eventName) {
|
|
CallNamedEvent(eventName);
|
|
}
|
|
|
|
void hhConsole::Event_BecomeNonSolid( void ) {
|
|
GetPhysics()->GetClipModel()->SetContents( 0 );
|
|
GetPhysics()->SetClipMask( 0 );
|
|
}
|
|
|
|
void hhConsole::SetOnAllGuis(const char *key, float value) {
|
|
for (int ix=0; ix<MAX_RENDERENTITY_GUI; ix++) {
|
|
if (renderEntity.gui[ix]) {
|
|
renderEntity.gui[ix]->SetStateFloat(key, value);
|
|
renderEntity.gui[ix]->StateChanged(gameLocal.time);
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhConsole::SetOnAllGuis(const char *key, int value) {
|
|
for (int ix=0; ix<MAX_RENDERENTITY_GUI; ix++) {
|
|
if (renderEntity.gui[ix]) {
|
|
renderEntity.gui[ix]->SetStateInt(key, value);
|
|
renderEntity.gui[ix]->StateChanged(gameLocal.time);
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhConsole::SetOnAllGuis(const char *key, bool value) {
|
|
for (int ix=0; ix<MAX_RENDERENTITY_GUI; ix++) {
|
|
if (renderEntity.gui[ix]) {
|
|
renderEntity.gui[ix]->SetStateBool(key, value);
|
|
renderEntity.gui[ix]->StateChanged(gameLocal.time);
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhConsole::SetOnAllGuis(const char *key, const char *value) {
|
|
for (int ix=0; ix<MAX_RENDERENTITY_GUI; ix++) {
|
|
if (renderEntity.gui[ix]) {
|
|
renderEntity.gui[ix]->SetStateString(key, value);
|
|
renderEntity.gui[ix]->StateChanged(gameLocal.time);
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhConsole::Killed(idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location) {
|
|
|
|
fl.takedamage = false;
|
|
|
|
// JRM - disable msgs on kill?
|
|
if(spawnArgs.GetBool("ai_damage_disable_msgs","0")) {
|
|
fl.refreshReactions = false;
|
|
}
|
|
|
|
// Inform the gui that we are damaged (not used, since gui materials can't see entity parms)
|
|
// SetShaderParm(SHADERPARM_DIVERSITY, gameLocal.random.RandomFloat());
|
|
SetOnAllGuis("damaged", true);
|
|
|
|
// Tell the gui that we died, so the gui can be in sync
|
|
CallNamedEvent("Death");
|
|
}
|
|
|
|
bool hhConsole::HandleSingleGuiCommand(idEntity *entityGui, idLexer *src) {
|
|
idToken token;
|
|
if (!src->ReadToken(&token) || token == ";") {
|
|
return false;
|
|
}
|
|
if (token.Icmp("setfont") == 0) {
|
|
if (src->ReadToken(&token)) {
|
|
idStr fontname = "fonts\\";
|
|
fontname += token;
|
|
if (renderEntity.gui[0]) {
|
|
// renderEntity.gui[0]->Translate(fontname.c_str()); // jmarshall
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
src->UnreadToken( &token );
|
|
return false;
|
|
}
|
|
|
|
// This is overridden so we can update camera targets reguardless of PVS
|
|
void hhConsole::Present( void ) {
|
|
PROFILE_SCOPE("Present", PROFMASK_NORMAL);
|
|
|
|
if ( !gameLocal.isNewFrame ) {
|
|
return;
|
|
}
|
|
|
|
// don't present to the renderer if the entity hasn't changed
|
|
if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
|
|
return;
|
|
}
|
|
BecomeInactive( TH_UPDATEVISUALS );
|
|
|
|
// camera target for remote render views
|
|
if ( cameraTarget ) { // && gameLocal.InPlayerPVS( this ) ) { // HUMANHEAD pdm: removed PVS check
|
|
renderEntity.remoteRenderView = cameraTarget->GetRenderView();
|
|
}
|
|
|
|
// if set to invisible, skip
|
|
if ( !renderEntity.hModel || IsHidden() ) {
|
|
return;
|
|
}
|
|
|
|
// add to refresh list
|
|
if ( modelDefHandle == -1 ) {
|
|
modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
|
|
} else {
|
|
gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );
|
|
}
|
|
}
|
|
|
|
void hhConsole::Think() {
|
|
|
|
if (thinkFlags & TH_THINK) {
|
|
if (bUsesRand) {
|
|
float rand = gameLocal.random.RandomFloat();
|
|
SetOnAllGuis("rand", rand);
|
|
}
|
|
|
|
if (bTimeEventsAlways) {
|
|
// Call event handler so time events get run
|
|
const char *cmd;
|
|
sysEvent_t ev;
|
|
memset( &ev, 0, sizeof( ev ) );
|
|
ev.evType = SE_NONE;
|
|
|
|
// This should work after the merge is done, since in the new code, HandleEvent() calls RunTimeEvents()
|
|
for (int ix=0; ix<MAX_RENDERENTITY_GUI; ix++) {
|
|
if (renderEntity.gui[ix]) {
|
|
cmd = renderEntity.gui[ix]->HandleEvent(&ev, gameLocal.time);
|
|
HandleGuiCommands(this, cmd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (thinkFlags & TH_MISC1) {
|
|
// Handle any translation
|
|
if (transState == TS_TRANSLATING || transState == TS_UNTRANSLATING) {
|
|
float alpha = translationAlpha.GetCurrentValue(gameLocal.time);
|
|
if (renderEntity.gui[0] || renderEntity.gui[1] || renderEntity.gui[2]) {
|
|
|
|
SetOnAllGuis("translationAlpha", alpha);
|
|
|
|
// Determine if the transition is done
|
|
if (translationAlpha.IsDone(gameLocal.time)) {
|
|
if (transState == TS_TRANSLATING) {
|
|
transState = TS_TRANSLATED;
|
|
BecomeInactive(TH_MISC1);
|
|
}
|
|
else if (transState == TS_UNTRANSLATING) {
|
|
transState = TS_UNTRANSLATED;
|
|
BecomeInactive(TH_MISC1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// JRM - ai using update
|
|
if (thinkFlags & TH_MISC2) {
|
|
UpdateUse();
|
|
}
|
|
|
|
idStaticEntity::Think();
|
|
}
|
|
|
|
// Toggles the translation state of the gui
|
|
void hhConsole::Translate(bool bLanded) {
|
|
|
|
if (!spawnArgs.GetBool("translate")) {
|
|
return;
|
|
}
|
|
|
|
float curalpha = translationAlpha.GetCurrentValue(gameLocal.time);
|
|
switch(transState) {
|
|
case TS_UNTRANSLATED:
|
|
case TS_UNTRANSLATING:
|
|
translationAlpha.Init(gameLocal.time, TRANSLATION_TIME, curalpha, 1.0f);
|
|
transState = TS_TRANSLATING;
|
|
BecomeActive(TH_MISC1);
|
|
break;
|
|
case TS_TRANSLATED:
|
|
case TS_TRANSLATING:
|
|
translationAlpha.Init(gameLocal.time, TRANSLATION_TIME, curalpha, 0.0f);
|
|
transState = TS_UNTRANSLATING;
|
|
BecomeActive(TH_MISC1);
|
|
break;
|
|
}
|
|
|
|
if (bLanded) {
|
|
StartSound( "snd_translate", SND_CHANNEL_ANY, 0, true, NULL);
|
|
}
|
|
}
|
|
|
|
void hhConsole::ConsoleActivated() {
|
|
// Called when someone "uses" the gui (gui calls activate cmd)
|
|
// JRM - turn off msgs after the player uses this
|
|
if(!aiCurrUsedBy.IsValid() && spawnArgs.GetBool("ai_player_use_disable_msgs", "0")) {
|
|
fl.refreshReactions = false;
|
|
}
|
|
}
|
|
|
|
void hhConsole::ClearTalonTargetType() { // CJR - Called when any command is sent to a gui. This disables talon's squawking
|
|
if ( perchSpot ) {
|
|
perchSpot->SetSquawk( false ); // Set the console to stop Talon from squawking
|
|
}
|
|
}
|
|
|
|
void hhConsole::Event_Activate( idEntity *activator ) {
|
|
// Don't toggle on/off like in idStaticEntity
|
|
// any GUIs still get the onTrigger() event
|
|
}
|
|
|
|
void hhConsole::Event_TalonAction(idEntity *talon, bool landed) {
|
|
Translate(landed);
|
|
}
|
|
|
|
void hhConsole::Use(idAI *ai) {
|
|
HH_ASSERT(ai);
|
|
|
|
// Just started using console?
|
|
if(!aiCurrUsedBy.IsValid()) {
|
|
aiCurrUsedBy = ai;
|
|
aiCurrUsedStartTime = gameLocal.GetTime();
|
|
BecomeActive(TH_MISC2);
|
|
}
|
|
}
|
|
|
|
void hhConsole::UpdateUse(void) {
|
|
|
|
// Time to deactivate?
|
|
if(aiCurrUsedBy.IsValid() && gameLocal.GetTime() > aiCurrUsedStartTime + aiUseTime) {
|
|
aiLastUsedBy = aiCurrUsedBy;
|
|
aiLastUsedTime = gameLocal.GetTime();
|
|
aiCurrUsedBy = NULL;
|
|
aiWaitingToTrigger = true;
|
|
aiUseCount++;
|
|
if(aiMaxUses > 0 && aiUseCount >= aiMaxUses) {
|
|
fl.refreshReactions = false;
|
|
}
|
|
BecomeInactive(TH_MISC2);
|
|
}
|
|
|
|
// Time to fire triggers?
|
|
if (aiCurrUsedBy.IsValid()) {
|
|
|
|
// Waiting to trigger...
|
|
if(aiWaitingToTrigger && gameLocal.GetTime() > aiCurrUsedStartTime + aiTriggerWaitTime) {
|
|
aiWaitingToTrigger = false;
|
|
|
|
OnTriggeredByAI(aiCurrUsedBy.GetEntity());
|
|
}
|
|
// Time to re-trigger?
|
|
else if (!aiWaitingToTrigger && aiRetriggerWait > 0 && gameLocal.GetTime() - aiLastTriggerTime > aiRetriggerWait) {
|
|
OnTriggeredByAI(aiCurrUsedBy.GetEntity());
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhConsole::OnTriggeredByAI(idAI *ai) {
|
|
|
|
aiLastTriggerTime = gameLocal.GetTime();
|
|
|
|
// Tell the gui that we were used, so the gui can be in sync
|
|
CallNamedEvent("AIUse");
|
|
}
|
|
|
|
bool hhConsole::CanUse(idAI *ai) {
|
|
HH_ASSERT(ai);
|
|
|
|
if (!aiCanUse) {
|
|
return false;
|
|
}
|
|
|
|
if (aiMaxUses > 0 && aiUseCount >= aiMaxUses) {
|
|
return false;
|
|
}
|
|
|
|
// Can't use it if someone else is using it
|
|
if (aiCurrUsedBy.IsValid() && aiCurrUsedBy.GetEntity() != ai) {
|
|
return false;
|
|
}
|
|
|
|
// Have to wait before we can use it again?
|
|
if (gameLocal.GetTime() < aiLastUsedTime + aiReuseWaitTime) {
|
|
return false;
|
|
}
|
|
|
|
return ai->spawnArgs.GetBool("CanUseConsole", "0");
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// hhConsoleCountdown
|
|
//
|
|
//==========================================================================
|
|
|
|
const idEventDef EV_CountdownStart("startcountdown", NULL);
|
|
const idEventDef EV_CountdownStop("stopcountdown", NULL);
|
|
const idEventDef EV_CountdownSet("setcountdown", "d");
|
|
|
|
CLASS_DECLARATION(hhConsole, hhConsoleCountdown)
|
|
EVENT( EV_Activate, hhConsoleCountdown::Event_Activate )
|
|
EVENT( EV_CountdownStart, hhConsoleCountdown::Event_StartCountdown )
|
|
EVENT( EV_CountdownStop, hhConsoleCountdown::Event_StopCountdown )
|
|
EVENT( EV_CountdownSet, hhConsoleCountdown::Event_SetCountdown )
|
|
END_CLASS
|
|
|
|
void hhConsoleCountdown::Spawn() {
|
|
countingDown = false;
|
|
countStart = spawnArgs.GetFloat("countStart");
|
|
countEnd = spawnArgs.GetFloat("countEnd");
|
|
countdown.Init(gameLocal.time, 0, countStart, countStart);
|
|
SetGuiOctal((int)countStart);
|
|
|
|
if (spawnArgs.GetBool("enabled")) {
|
|
StartCountdown();
|
|
}
|
|
}
|
|
|
|
void hhConsoleCountdown::Save(idSaveGame *savefile) const {
|
|
savefile->WriteBool(countingDown);
|
|
savefile->WriteFloat(countStart);
|
|
savefile->WriteFloat(countEnd);
|
|
|
|
savefile->WriteFloat( countdown.GetStartTime() ); // idInterpolate<float>
|
|
savefile->WriteFloat( countdown.GetDuration() );
|
|
savefile->WriteFloat( countdown.GetStartValue() );
|
|
savefile->WriteFloat( countdown.GetEndValue() );
|
|
}
|
|
|
|
void hhConsoleCountdown::Restore( idRestoreGame *savefile ) {
|
|
float set;
|
|
|
|
savefile->ReadBool(countingDown);
|
|
savefile->ReadFloat(countStart);
|
|
savefile->ReadFloat(countEnd);
|
|
|
|
savefile->ReadFloat( set ); // idInterpolate<float>
|
|
countdown.SetStartTime( set );
|
|
savefile->ReadFloat( set );
|
|
countdown.SetDuration( set );
|
|
savefile->ReadFloat( set );
|
|
countdown.SetStartValue(set);
|
|
savefile->ReadFloat( set );
|
|
countdown.SetEndValue( set );
|
|
}
|
|
|
|
void hhConsoleCountdown::SetGuiOctal(int value) {
|
|
// Convert to base-8 numbers and set on gui
|
|
idStr octalValue;
|
|
sprintf(octalValue, "%o", value);
|
|
SetOnAllGuis("countdown", octalValue.c_str());
|
|
}
|
|
|
|
void hhConsoleCountdown::UpdateGUI(float curValue) {
|
|
SetGuiOctal((int)curValue);
|
|
SetOnAllGuis("fraction", curValue / countStart);
|
|
SetOnAllGuis("counting", countingDown);
|
|
}
|
|
|
|
void hhConsoleCountdown::Think() {
|
|
hhConsole::Think();
|
|
|
|
if (thinkFlags & TH_MISC3) {
|
|
if (countingDown) {
|
|
UpdateGUI(countdown.GetCurrentValue(gameLocal.time));
|
|
if (countdown.IsDone(gameLocal.time)) {
|
|
BecomeInactive(TH_MISC3);
|
|
ActivateTargets(this);
|
|
StopCountdown();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhConsoleCountdown::SetCountdown(int count) {
|
|
if (countingDown) {
|
|
gameLocal.Warning("You must stop countdown before set");
|
|
return;
|
|
}
|
|
countStart = count;
|
|
countdown.Init(gameLocal.time, 0, countStart, countStart);
|
|
UpdateGUI(count);
|
|
}
|
|
|
|
void hhConsoleCountdown::StartCountdown() {
|
|
if (!countingDown) {
|
|
// Start counting down from current value
|
|
float curValue = countdown.GetCurrentValue(gameLocal.time);
|
|
// resume from previously stopped count
|
|
countdown.Init(gameLocal.time, SEC2MS(countStart), curValue, countEnd);
|
|
BecomeActive(TH_MISC3);
|
|
countingDown = true;
|
|
}
|
|
}
|
|
|
|
void hhConsoleCountdown::StopCountdown() {
|
|
if (countingDown) {
|
|
// Stop countdown at current value
|
|
float curValue = countdown.GetCurrentValue(gameLocal.time);
|
|
countdown.Init(gameLocal.time, 0, curValue, curValue);
|
|
countingDown = false;
|
|
UpdateGUI(countdown.GetCurrentValue(gameLocal.time));
|
|
}
|
|
}
|
|
|
|
void hhConsoleCountdown::Reset() {
|
|
// Reset to initial start time (or set start time)
|
|
countdown.Init(gameLocal.time, 0, countStart, countStart);
|
|
UpdateGUI(countdown.GetCurrentValue(gameLocal.time));
|
|
}
|
|
|
|
void hhConsoleCountdown::Event_Activate(idEntity *activator) {
|
|
StartCountdown();
|
|
}
|
|
void hhConsoleCountdown::Event_SetCountdown(int count) {
|
|
SetCountdown(count);
|
|
}
|
|
void hhConsoleCountdown::Event_StartCountdown() {
|
|
StartCountdown();
|
|
}
|
|
void hhConsoleCountdown::Event_StopCountdown() {
|
|
StopCountdown();
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// hhConsoleKeypad
|
|
//
|
|
// Generic console for entering text on a keypad and displaying it
|
|
//==========================================================================
|
|
|
|
CLASS_DECLARATION(hhConsole, hhConsoleKeypad)
|
|
END_CLASS
|
|
|
|
void hhConsoleKeypad::Spawn() {
|
|
maxLength = spawnArgs.GetInt("maxlength");
|
|
|
|
SetOnAllGuis("atmax", false);
|
|
}
|
|
|
|
void hhConsoleKeypad::Save(idSaveGame *savefile) const {
|
|
savefile->WriteString(keyBuffer);
|
|
savefile->WriteInt(maxLength);
|
|
}
|
|
|
|
void hhConsoleKeypad::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadString(keyBuffer);
|
|
savefile->ReadInt(maxLength);
|
|
}
|
|
|
|
void hhConsoleKeypad::UpdateGui() {
|
|
int length = keyBuffer.Length();
|
|
|
|
// Enforce maxlength
|
|
if (length > maxLength) {
|
|
keyBuffer = keyBuffer.Left(maxLength);
|
|
}
|
|
|
|
SetOnAllGuis("atmax", (length >= maxLength));
|
|
SetOnAllGuis("keypad", keyBuffer.c_str());
|
|
}
|
|
|
|
bool hhConsoleKeypad::HandleSingleGuiCommand(idEntity *entityGui, idLexer *src) {
|
|
idToken token;
|
|
if (!src->ReadToken(&token) || token == ";") {
|
|
return false;
|
|
}
|
|
if (token.Icmp("backspace") == 0) {
|
|
if (keyBuffer.Length() > 0) {
|
|
keyBuffer = keyBuffer.Left(keyBuffer.Length()-1);
|
|
}
|
|
}
|
|
else if (token.Icmp("clear") == 0) {
|
|
keyBuffer.Empty();
|
|
}
|
|
else if (token.Icmp("enter") == 0) {
|
|
keyBuffer += '\n';
|
|
}
|
|
else if (token.IcmpPrefix("keypad_") == 0) { // KEYPAD_X
|
|
token.ToLower();
|
|
token.Strip("keypad_");
|
|
keyBuffer += token.c_str();
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
|
|
UpdateGui();
|
|
return true;
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// hhConsoleAlarm
|
|
//
|
|
// alarm console
|
|
//==========================================================================
|
|
|
|
const idEventDef EV_SpawnMonster("<spawnMonster>");
|
|
|
|
CLASS_DECLARATION(hhConsole, hhConsoleAlarm)
|
|
EVENT( EV_SpawnMonster, hhConsoleAlarm::Event_SpawnMonster )
|
|
END_CLASS
|
|
|
|
void hhConsoleAlarm::Spawn() {
|
|
bAlarmActive = false;
|
|
numMonsters = 0;
|
|
bSpawning = false;
|
|
maxMonsters = spawnArgs.GetInt( "max_monsters", "0" );
|
|
BecomeActive(TH_TICKER);
|
|
}
|
|
|
|
void hhConsoleAlarm::Save(idSaveGame *savefile) const {
|
|
savefile->WriteBool(bAlarmActive);
|
|
savefile->WriteInt( numMonsters );
|
|
savefile->WriteInt( maxMonsters );
|
|
currentMonster.Save( savefile );
|
|
savefile->WriteBool( bSpawning );
|
|
}
|
|
|
|
void hhConsoleAlarm::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadBool(bAlarmActive);
|
|
savefile->ReadInt( numMonsters );
|
|
savefile->ReadInt( maxMonsters );
|
|
currentMonster.Restore( savefile );
|
|
savefile->ReadBool( bSpawning );
|
|
}
|
|
|
|
void hhConsoleAlarm::ConsoleActivated() {
|
|
bAlarmActive = !bAlarmActive;
|
|
if ( !bAlarmActive ) {
|
|
CancelEvents( &EV_SpawnMonster );
|
|
for ( int i=0;i<targets.Num();i++ ) {
|
|
if ( targets[i].IsValid() ) {
|
|
if ( targets[i]->IsType( hhMountedGun::Type ) ) {
|
|
targets[i]->PostEventMS( &EV_Deactivate, 0 );
|
|
}
|
|
targets[i]->DeactivateTargetsType( hhMountedGun::Type );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhConsoleAlarm::Event_SpawnMonster() {
|
|
idDict args;
|
|
// Copy keys for monster
|
|
idStr tmpStr, realKeyName;
|
|
const idKeyValue *kv = spawnArgs.MatchPrefix("ent_", NULL);
|
|
while(kv) {
|
|
tmpStr = kv->GetKey();
|
|
int usIndex = tmpStr.FindChar("ent_", '_');
|
|
realKeyName = tmpStr.Mid(usIndex+1, strlen(kv->GetKey())-usIndex-1);
|
|
args.Set(realKeyName, kv->GetValue());
|
|
kv = spawnArgs.MatchPrefix("ent_", kv);
|
|
}
|
|
idEntity *ent = gameLocal.FindEntity( spawnArgs.GetString( "spawn_location" ) );
|
|
if ( ent ) {
|
|
args.SetVector( "origin", ent->GetOrigin() );
|
|
} else {
|
|
gameLocal.Warning("No spawn_location specified for %s\n", GetName());
|
|
return;
|
|
}
|
|
|
|
// entity collision checks for seeing if we are going to collide with another entity on spawn
|
|
const idDict *entDef = gameLocal.FindEntityDefDict( spawnArgs.GetString( "def_monster" ), false );
|
|
if ( entDef ) {
|
|
bool clipped = false;
|
|
idVec3 entSize;
|
|
idClipModel *cm;
|
|
idClipModel *clipModels[ MAX_GENTITIES ];
|
|
entDef->GetVector( "size", "0", entSize );
|
|
idBounds bounds = idBounds( ent->GetOrigin() ).Expand( max( entSize.x, entSize.y ) );
|
|
int num = gameLocal.clip.ClipModelsTouchingBounds( bounds, MASK_MONSTERSOLID, clipModels, MAX_GENTITIES );
|
|
if ( ai_debugBrain.GetBool() ) {
|
|
gameRenderWorld->DebugBounds( colorRed, bounds, vec3_origin, 5000 );
|
|
}
|
|
for ( int i=0;i<num;i++ ) {
|
|
cm = clipModels[ i ];
|
|
// don't check render entities
|
|
if ( cm->IsRenderModel() ) {
|
|
continue;
|
|
}
|
|
idEntity *hit = cm->GetEntity();
|
|
if ( ( hit == this ) || !hit->fl.takedamage || !hit->IsType( idActor::Type ) ) {
|
|
continue;
|
|
}
|
|
clipped = true;
|
|
break;
|
|
}
|
|
if ( !clipped ) {
|
|
currentMonster = gameLocal.SpawnObject(spawnArgs.GetString( "def_monster" ), &args);
|
|
if ( currentMonster.IsValid() ) {
|
|
currentMonster->PostEventMS( &EV_Activate, 0, this );
|
|
bSpawning = false;
|
|
numMonsters++;
|
|
} else {
|
|
gameLocal.Warning( "%s could not spawn monster\n", GetName() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhConsoleAlarm::Ticker() {
|
|
|
|
if (bAlarmActive) {
|
|
if ( currentMonster.IsValid() && currentMonster->GetHealth() <= 0 ) {
|
|
currentMonster.Clear();
|
|
}
|
|
|
|
//check on our monster
|
|
if ( !bSpawning && !currentMonster.IsValid() && spawnArgs.GetString( "def_monster" )[0] ) {
|
|
if ( !maxMonsters || numMonsters < maxMonsters ) {
|
|
float min = spawnArgs.GetFloat( "mindelay", "2.0" );
|
|
float max = spawnArgs.GetFloat( "maxdelay", "2.0" );
|
|
if ( max < min ) {
|
|
max = min;
|
|
}
|
|
bSpawning = true;
|
|
PostEventSec( &EV_SpawnMonster, min + (max-min)*gameLocal.random.RandomFloat() );
|
|
}
|
|
}
|
|
}
|
|
|
|
hhConsole::Ticker();
|
|
} |