1433 lines
36 KiB
C++
1433 lines
36 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Logfile:: /Quake 2 Engine/Sin/code/game/doors.cpp $
|
|
// $Revision:: 71 $
|
|
// $Author:: Markd $
|
|
// $Date:: 11/16/98 9:48p $
|
|
//
|
|
// Copyright (C) 1997 by Ritual Entertainment, Inc.
|
|
// All rights reserved.
|
|
//
|
|
// This source is may not be distributed and/or modified without
|
|
// expressly written permission by Ritual Entertainment, Inc.
|
|
//
|
|
// $Log:: /Quake 2 Engine/Sin/code/game/doors.cpp $
|
|
//
|
|
// 71 11/16/98 9:48p Markd
|
|
// made door sounds always stop to kill looping sounds
|
|
//
|
|
// 70 11/13/98 4:35p Jimdose
|
|
// doors weren't staying open when wait was -1
|
|
//
|
|
// 69 10/24/98 3:25a Jimdose
|
|
// open was postponing the close event further and further into the future due
|
|
// to touchfield activation. Changed it to a cancel and a post of close.
|
|
// fixes doors staying open
|
|
//
|
|
// 68 10/24/98 12:42a Markd
|
|
// changed origins to worldorigins where appropriate
|
|
//
|
|
// 67 10/23/98 11:10p Jimdose
|
|
// removed trigger_finished
|
|
// Close now cancels pending close events. Hopefully this fixes the spinning
|
|
// door bug.
|
|
//
|
|
// 66 10/23/98 10:12p Jimdose
|
|
// fixed bug where doors don't close after actors open them
|
|
// fixed code that determines direction door should open
|
|
//
|
|
// 65 10/21/98 10:58p Jimdose
|
|
// fixed (hopefully) most of the door bugs
|
|
//
|
|
// 64 10/21/98 2:35a Jimdose
|
|
// Don't spawn trigger field when DOOR_TOGGLE is set
|
|
//
|
|
// 63 10/19/98 12:07a Jimdose
|
|
// made all code use fast checks for inheritance (no text lookups when
|
|
// possible)
|
|
// isSubclassOf no longer requires ::_classinfo()
|
|
//
|
|
// 62 10/16/98 8:24p Jimdose
|
|
// Put some extra checks in to make sure master is set
|
|
//
|
|
// 61 10/07/98 4:11p Markd
|
|
// Fixed using of doors
|
|
//
|
|
// 60 10/06/98 6:07p Markd
|
|
// Fixed door message code
|
|
//
|
|
// 59 10/02/98 7:45p Markd
|
|
// put in more door debug info
|
|
//
|
|
// 58 9/30/98 3:42p Markd
|
|
// put in bulletproofing for start thread functions
|
|
//
|
|
// 57 9/24/98 1:50a Jimdose
|
|
// Fixed side of door checks when opening doors
|
|
//
|
|
// 56 9/23/98 2:19p Markd
|
|
// made all doors play sounds not just the masters
|
|
//
|
|
// 55 9/23/98 1:31p Markd
|
|
// changed default door sound documentation
|
|
//
|
|
// 54 9/22/98 4:23p Markd
|
|
// Fixed some targetname stuff for lights, doors and scriptobjects
|
|
//
|
|
// 53 9/03/98 9:07p Jimdose
|
|
// Added CanBeOpenedBy so that the AI can check if a door is useable by an
|
|
// actor
|
|
// changed owner to master
|
|
// Fixed bug with doors that don't stop
|
|
// opened doors can be used again to close them
|
|
// fixed door blocked
|
|
//
|
|
// 52 8/28/98 4:14p Markd
|
|
// Added sound_locked support
|
|
//
|
|
// 51 8/26/98 6:43p Jimdose
|
|
// doors now only show the item needed when other is a client
|
|
//
|
|
// 50 8/24/98 6:10p Markd
|
|
// Added killable doors, that stop for nothing
|
|
//
|
|
// 49 8/24/98 5:47p Markd
|
|
// Fixed triggerable doors
|
|
//
|
|
// 48 8/24/98 11:32a Markd
|
|
// Added Start method to threads, repladed all ProcessEvent(
|
|
// EV_ScriptThread_execute) with thread->Start( -1 )
|
|
//
|
|
// 47 8/21/98 3:48p Markd
|
|
// Added openthread and closethread events
|
|
//
|
|
// 46 8/21/98 1:43a Markd
|
|
// Added full ScriptDoor functionality
|
|
//
|
|
// 45 8/18/98 11:08p Markd
|
|
// Added new Alias System
|
|
//
|
|
// 44 8/15/98 10:42a Markd
|
|
// took out auto-open for actors for non-sin-demo
|
|
//
|
|
// 43 8/14/98 8:18p Markd
|
|
// Fixed silly str thing to get rid of weird messages.
|
|
//
|
|
// 42 8/13/98 4:57p Jimdose
|
|
// Made Open check if there are any event args. If not, it prints a warning so
|
|
// that the level designers know.
|
|
//
|
|
// 41 8/03/98 7:52p Jimdose
|
|
// added hack fix doors for matt
|
|
//
|
|
// 40 7/29/98 2:31p Aldie
|
|
// Changed health to a float
|
|
//
|
|
// 39 7/26/98 3:40p Jimdose
|
|
// locked doors don't autoopen for actors
|
|
//
|
|
// 38 7/26/98 9:51a Markd
|
|
// fixed "snd_locked" bug
|
|
//
|
|
// 37 7/26/98 9:22a Jimdose
|
|
// Doors auto-open for actors
|
|
//
|
|
// 36 7/26/98 1:03a Markd
|
|
// Fixed door trigger field
|
|
//
|
|
// 35 7/26/98 12:48a Jimdose
|
|
// Fixed calculating bbox in LinkDoors
|
|
//
|
|
// 34 7/23/98 6:17p Aldie
|
|
// Updated damage system and fixed some damage related bugs. Also put tracers
|
|
// back to the way they were, and added gib event to funcscriptmodels
|
|
//
|
|
// 33 7/21/98 4:22p Aldie
|
|
// Make icons work for locks
|
|
//
|
|
// 32 7/21/98 1:10p Aldie
|
|
// Added meansofdeath to obituaries
|
|
//
|
|
// 31 7/20/98 10:15a Markd
|
|
// Fixed funky door problem
|
|
//
|
|
// 30 7/18/98 4:42p Markd
|
|
// Added snd_locked support for locked doors
|
|
//
|
|
// 29 7/11/98 4:49p Markd
|
|
// Fixed minor bug in door code with not setting other
|
|
//
|
|
// 28 7/11/98 4:46p Markd
|
|
// Fixed doors not being lockable
|
|
//
|
|
// 27 7/10/98 10:34p Markd
|
|
// Fixed locked doors
|
|
//
|
|
// 26 7/10/98 2:09p Markd
|
|
// Added locking capability to doors
|
|
//
|
|
// 25 6/30/98 6:03p Jimdose
|
|
// TryOpen now checks if other is NULL
|
|
//
|
|
// 24 6/28/98 3:15p Markd
|
|
// Fixed sliding doors
|
|
//
|
|
// 23 6/27/98 2:17p Aldie
|
|
// Updated centerprint to use new method
|
|
//
|
|
// 22 6/25/98 8:48p Markd
|
|
// Merged RotatingDoors and regular Doors
|
|
//
|
|
// 21 6/24/98 8:45p Markd
|
|
// Rewrote door code
|
|
//
|
|
// 12 6/17/98 10:14p Jimdose
|
|
// Added isOpen for AI
|
|
// Made doors stay open while an entity is in its trigger field.
|
|
//
|
|
// 11 6/10/98 2:10p Aldie
|
|
// Updated damage function.
|
|
//
|
|
// 10 6/04/98 4:35p Markd
|
|
// made door sounds not use PHS
|
|
//
|
|
// 9 5/24/98 8:55p Jimdose
|
|
// Removed the char * cast from soundindex call
|
|
//
|
|
// 8 5/24/98 8:46p Jimdose
|
|
// Made a lot of functions more str-friendly.
|
|
// Got rid of a lot of char * based strings
|
|
// Cleaned up get spawn arg functions and sound functions
|
|
// sound functions now use consistant syntax
|
|
//
|
|
// 7 5/24/98 3:42p Markd
|
|
// fixed str bug
|
|
//
|
|
// 6 5/24/98 2:46p Markd
|
|
// Made char *'s into const char *'s
|
|
//
|
|
// 5 5/24/98 1:04a Jimdose
|
|
// Added sound events for ai
|
|
//
|
|
// 4 5/22/98 9:38p Jimdose
|
|
// added DoorBlocked
|
|
//
|
|
// 3 5/03/98 4:36p Jimdose
|
|
// Changed Vector class
|
|
//
|
|
// 2 4/29/98 9:55p Jimdose
|
|
// Created file
|
|
//
|
|
// DESCRIPTION:
|
|
// Doors are environment objects that rotate open when activated by triggers
|
|
// or when used by the player.
|
|
//
|
|
|
|
#include "g_local.h"
|
|
#include "entity.h"
|
|
#include "trigger.h"
|
|
#include "mover.h"
|
|
#include "doors.h"
|
|
#include "sentient.h"
|
|
#include "areaportal.h"
|
|
#include "scriptmaster.h"
|
|
#include "item.h"
|
|
#include "actor.h"
|
|
#include "player.h"
|
|
|
|
Event EV_Door_TriggerFieldTouched( "door_triggerfield" );
|
|
Event EV_Door_TryOpen( "tryToOpen" );
|
|
Event EV_Door_Close( "close" );
|
|
Event EV_Door_Open( "open" );
|
|
Event EV_Door_DoClose( "doclose" );
|
|
Event EV_Door_DoOpen( "doopen" );
|
|
Event EV_Door_CloseEnd( "doorclosed" );
|
|
Event EV_Door_OpenEnd( "dooropened" );
|
|
Event EV_Door_Fire( "toggledoor" );
|
|
Event EV_Door_Link( "linkdoor" );
|
|
Event EV_Door_SetTime( "time" );
|
|
Event EV_Door_Lock( "lock" );
|
|
Event EV_Door_Unlock( "unlock" );
|
|
|
|
CLASS_DECLARATION( ScriptSlave, Door, "NormalDoor" );
|
|
|
|
#define DOOR_START_OPEN 1
|
|
#define DOOR_OPEN_DIRECTION 2
|
|
#define DOOR_DONT_LINK 4
|
|
#define DOOR_TOGGLE 32
|
|
#define DOOR_AUTO_OPEN 64
|
|
#define DOOR_TARGETED 128
|
|
|
|
#define STATE_OPEN 1
|
|
#define STATE_OPENING 2
|
|
#define STATE_CLOSING 3
|
|
#define STATE_CLOSED 4
|
|
|
|
/*
|
|
|
|
Doors are similar to buttons, but can spawn a fat trigger field around them
|
|
to open without a touch, and they link together to form simultanious
|
|
double/quad doors.
|
|
|
|
Door.master is the master door. If there is only one door, it points to itself.
|
|
If multiple doors, all will point to a single one.
|
|
|
|
Door.enemy chains from the master door through all doors linked in the chain.
|
|
|
|
*/
|
|
|
|
ResponseDef Door::Responses[] =
|
|
{
|
|
{ &EV_Door_TriggerFieldTouched, ( Response )Door::FieldTouched },
|
|
{ &EV_Trigger_Effect, ( Response )Door::TryOpen },
|
|
{ &EV_Activate, ( Response )Door::TryOpen },
|
|
{ &EV_Door_TryOpen, ( Response )Door::TryOpen },
|
|
{ &EV_Door_Close, ( Response )Door::Close },
|
|
{ &EV_Door_Open, ( Response )Door::Open },
|
|
{ &EV_Door_CloseEnd, ( Response )Door::CloseEnd },
|
|
{ &EV_Door_OpenEnd, ( Response )Door::OpenEnd },
|
|
{ &EV_Door_Fire, ( Response )Door::DoorFire },
|
|
{ &EV_Door_Link, ( Response )Door::LinkDoors },
|
|
{ &EV_Door_SetTime, ( Response )Door::SetTime },
|
|
{ &EV_Use, ( Response )Door::DoorUse },
|
|
{ &EV_Killed, ( Response )Door::DoorFire },
|
|
{ &EV_Blocked, ( Response )Door::DoorBlocked },
|
|
{ &EV_Door_Lock, ( Response )Door::LockDoor },
|
|
{ &EV_Door_Unlock, ( Response )Door::UnlockDoor },
|
|
{ &EV_Touch, NULL },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
Door::Door()
|
|
{
|
|
float t;
|
|
|
|
nextdoor = 0;
|
|
trigger = 0;
|
|
locked = false;
|
|
master = this;
|
|
lastblocktime = 0;
|
|
diropened = 0;
|
|
|
|
showModel();
|
|
|
|
sound_stop = G_GetStringArg( "sound_stop", gi.GlobalAlias_FindRandom( "door_stop" ) );
|
|
sound_move = G_GetStringArg( "sound_move", gi.GlobalAlias_FindRandom( "door_moving" ) );
|
|
sound_message = G_GetStringArg( "sound_message" );
|
|
sound_locked = G_GetStringArg( "sound_locked" );
|
|
|
|
gi.soundindex( sound_stop.c_str() );
|
|
gi.soundindex( sound_move.c_str() );
|
|
if ( sound_message.length() > 1 )
|
|
gi.soundindex( sound_message.c_str() );
|
|
|
|
health = G_GetFloatArg( "health" );
|
|
max_health = health;
|
|
|
|
traveltime = G_GetFloatArg( "time", 0.3 );
|
|
|
|
if ( traveltime < FRAMETIME )
|
|
{
|
|
traveltime = FRAMETIME;
|
|
}
|
|
|
|
speed = 1.0f / traveltime;
|
|
wait = G_GetFloatArg( "wait", 3 );
|
|
|
|
if ( ( spawnflags & DOOR_TOGGLE ) && ( wait == 3 ) )
|
|
wait = 0;
|
|
|
|
dmg = G_GetIntArg( "dmg", 0 );
|
|
|
|
setSolidType( SOLID_BSP );
|
|
setMoveType( MOVETYPE_PUSH );
|
|
|
|
setSize( mins, maxs );
|
|
|
|
dir = G_GetMovedir();
|
|
t = dir[ 0 ];
|
|
dir[ 0 ] = -dir[ 1 ];
|
|
dir[ 1 ] = t;
|
|
|
|
setOrigin( origin );
|
|
doormin = absmin;
|
|
doormax = absmax;
|
|
|
|
|
|
// DOOR_START_OPEN is to allow an entity to be lighted in the closed position
|
|
// but spawn in the open position
|
|
if ( spawnflags & DOOR_START_OPEN )
|
|
{
|
|
state = STATE_OPEN;
|
|
PostEvent( EV_Door_Open, 0.05f );
|
|
}
|
|
else
|
|
{
|
|
state = STATE_CLOSED;
|
|
}
|
|
previous_state = state;
|
|
|
|
if ( health )
|
|
{
|
|
takedamage = DAMAGE_YES;
|
|
}
|
|
|
|
// LinkDoors can't be done until all of the doors have been spawned, so
|
|
// the sizes can be detected properly.
|
|
nextdoor = 0;
|
|
PostEvent( EV_Door_Link, 0 );
|
|
|
|
// Default to work with monsters and players
|
|
respondto = TRIGGER_PLAYERS | TRIGGER_MONSTERS;
|
|
if ( spawnflags & 8 )
|
|
{
|
|
respondto &= ~TRIGGER_PLAYERS;
|
|
}
|
|
if ( spawnflags & 16 )
|
|
{
|
|
respondto &= ~TRIGGER_MONSTERS;
|
|
}
|
|
}
|
|
|
|
qboolean Door::isOpen
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return ( state == STATE_OPEN );
|
|
}
|
|
|
|
void Door::OpenEnd
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
// if ( master == this )
|
|
{
|
|
if ( sound_stop.length() > 1 )
|
|
{
|
|
ProcessEvent( EV_DoorSound );
|
|
sound( sound_stop, 1, CHAN_VOICE + CHAN_NO_PHS_ADD, ATTN_NORM );
|
|
}
|
|
else
|
|
{
|
|
RandomGlobalSound( "null_sound", 1, CHAN_VOICE + CHAN_NO_PHS_ADD, ATTN_NORM );
|
|
}
|
|
}
|
|
previous_state = state;
|
|
state = STATE_OPEN;
|
|
if ( spawnflags & DOOR_TOGGLE )
|
|
{
|
|
// don't close automatically
|
|
return;
|
|
}
|
|
|
|
if ( ( wait > 0 ) && ( master == this ) )
|
|
{
|
|
PostEvent( EV_Door_Close, wait );
|
|
}
|
|
}
|
|
|
|
void Door::CloseEnd
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
// if ( master == this )
|
|
{
|
|
if ( sound_stop.length() > 1 )
|
|
{
|
|
ProcessEvent( EV_DoorSound );
|
|
sound( sound_stop, 1, CHAN_VOICE + CHAN_NO_PHS_ADD, ATTN_NORM );
|
|
}
|
|
else
|
|
{
|
|
RandomGlobalSound( "null_sound", 1, CHAN_VOICE + CHAN_NO_PHS_ADD, ATTN_NORM );
|
|
}
|
|
}
|
|
|
|
SetAreaPortals( Target(), false );
|
|
|
|
previous_state = state;
|
|
state = STATE_CLOSED;
|
|
}
|
|
|
|
void Door::Close
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Door *door;
|
|
|
|
CancelEventsOfType( EV_Door_Close );
|
|
|
|
previous_state = state;
|
|
state = STATE_CLOSING;
|
|
|
|
ProcessEvent( EV_Door_DoClose );
|
|
|
|
if ( sound_move.length() > 1 )
|
|
{
|
|
ProcessEvent( EV_DoorSound );
|
|
sound( sound_move, 1, CHAN_VOICE + CHAN_NO_PHS_ADD, ATTN_NORM );
|
|
}
|
|
if ( master == this )
|
|
{
|
|
if ( max_health )
|
|
{
|
|
takedamage = DAMAGE_YES;
|
|
health = max_health;
|
|
}
|
|
|
|
// trigger all paired doors
|
|
door = ( Door * )G_GetEntity( nextdoor );
|
|
assert( door->isSubclassOf( Door ) );
|
|
while( door && ( door != this ) )
|
|
{
|
|
door->ProcessEvent( EV_Door_Close );
|
|
door = ( Door * )G_GetEntity( door->nextdoor );
|
|
assert( door->isSubclassOf( Door ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void Door::Open
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Door *door;
|
|
Event *e;
|
|
Entity *other;
|
|
|
|
if ( ev->NumArgs() < 1 )
|
|
{
|
|
ev->Error( "No entity specified to open door. Door may open the wrong way." );
|
|
other = world;
|
|
}
|
|
else
|
|
{
|
|
other = ev->GetEntity( 1 );
|
|
}
|
|
|
|
if ( state == STATE_OPENING )
|
|
{
|
|
// already going up
|
|
return;
|
|
}
|
|
|
|
if ( state == STATE_OPEN )
|
|
{
|
|
// reset top wait time
|
|
if ( wait > 0 )
|
|
{
|
|
CancelEventsOfType( EV_Door_Close );
|
|
PostEvent( EV_Door_Close, wait );
|
|
}
|
|
return;
|
|
}
|
|
|
|
previous_state = state;
|
|
state = STATE_OPENING;
|
|
|
|
e = new Event( EV_Door_DoOpen );
|
|
e->AddEntity( other );
|
|
ProcessEvent( e );
|
|
|
|
if ( sound_move.length() > 1 )
|
|
{
|
|
ProcessEvent( EV_DoorSound );
|
|
sound( sound_move, 1, CHAN_VOICE + CHAN_NO_PHS_ADD, ATTN_NORM );
|
|
}
|
|
if ( master == this )
|
|
{
|
|
// trigger all paired doors
|
|
door = ( Door * )G_GetEntity( nextdoor );
|
|
assert( door->isSubclassOf( Door ) );
|
|
while( door && ( door != this ) )
|
|
{
|
|
e = new Event( EV_Door_Open );
|
|
e->AddEntity( other );
|
|
door->ProcessEvent( e );
|
|
door = ( Door * )G_GetEntity( door->nextdoor );
|
|
assert( door->isSubclassOf( Door ) );
|
|
}
|
|
|
|
SetAreaPortals( Target(), true );
|
|
}
|
|
}
|
|
|
|
void Door::DoorUse
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Entity *other;
|
|
qboolean respond;
|
|
Event *e;
|
|
|
|
other = ev->GetEntity( 1 );
|
|
|
|
respond = ( ( ( respondto & TRIGGER_PLAYERS ) && other->isClient() ) ||
|
|
( ( respondto & TRIGGER_MONSTERS ) && other->isSubclassOf( Actor ) ) );
|
|
|
|
if ( !respond )
|
|
return;
|
|
|
|
// only allow use when not triggerd by other events
|
|
if ( health || ( spawnflags & ( DOOR_AUTO_OPEN | DOOR_TARGETED ) ) )
|
|
{
|
|
if ( other->isSubclassOf( Sentient ) && ( state == STATE_CLOSED ) )
|
|
{
|
|
if ( health )
|
|
{
|
|
gi.centerprintf ( other->edict, "jcx yv 20 string \"This door is jammed.\"" );
|
|
}
|
|
else if ( spawnflags & DOOR_TARGETED )
|
|
{
|
|
RandomGlobalSound( "door_triggered", 1, CHAN_VOICE + CHAN_NO_PHS_ADD, ATTN_NORM );
|
|
//gi.centerprintf ( other->edict, "jcx yv 20 string \"This door opens elsewhere.\"" );
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
assert( master );
|
|
if ( !master )
|
|
{
|
|
// bulletproofing
|
|
master = this;
|
|
}
|
|
|
|
if ( master->state == STATE_CLOSED )
|
|
{
|
|
e = new Event( EV_Door_TryOpen );
|
|
e->AddEntity( other );
|
|
master->ProcessEvent( e );
|
|
}
|
|
else if ( master->state == STATE_OPEN )
|
|
{
|
|
e = new Event( EV_Door_Close );
|
|
e->AddEntity( other );
|
|
master->ProcessEvent( e );
|
|
}
|
|
}
|
|
|
|
void Door::DoorFire
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Event *e;
|
|
Entity *other;
|
|
|
|
other = ev->GetEntity( 1 );
|
|
|
|
assert( master == this );
|
|
if ( master != this )
|
|
{
|
|
gi.error( "DoorFire: master != self" );
|
|
}
|
|
|
|
// no more messages
|
|
SetMessage( NULL );
|
|
|
|
// reset health in case we were damage triggered
|
|
health = max_health;
|
|
|
|
// will be reset upon return
|
|
takedamage = DAMAGE_NO;
|
|
|
|
if ( ( spawnflags & ( DOOR_TOGGLE | DOOR_START_OPEN ) ) && ( state == STATE_OPENING || state == STATE_OPEN ) )
|
|
{
|
|
spawnflags &= ~DOOR_START_OPEN;
|
|
ProcessEvent( EV_Door_Close );
|
|
}
|
|
else
|
|
{
|
|
e = new Event( EV_Door_Open );
|
|
e->AddEntity( other );
|
|
ProcessEvent( e );
|
|
}
|
|
}
|
|
|
|
void Door::DoorBlocked
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Event *e;
|
|
Entity *other;
|
|
|
|
assert( master );
|
|
if ( ( master ) && ( master != this ) )
|
|
{
|
|
master->ProcessEvent( ev );
|
|
return;
|
|
}
|
|
|
|
if ( lastblocktime > level.time )
|
|
{
|
|
return;
|
|
}
|
|
|
|
lastblocktime = level.time + 0.3;
|
|
|
|
other = ev->GetEntity( 1 );
|
|
|
|
if ( dmg )
|
|
{
|
|
other->Damage( this, this, (int)dmg, worldorigin, vec_zero, vec_zero, 0, 0, MOD_CRUSH, -1, -1, 1.0f );
|
|
}
|
|
|
|
//
|
|
// if we killed him, lets keep on going
|
|
//
|
|
if ( other->health <= 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( state == STATE_OPENING || state == STATE_OPEN )
|
|
{
|
|
spawnflags &= ~DOOR_START_OPEN;
|
|
ProcessEvent( EV_Door_Close );
|
|
}
|
|
else
|
|
{
|
|
e = new Event( EV_Door_Open );
|
|
e->AddEntity( other );
|
|
ProcessEvent( e );
|
|
}
|
|
}
|
|
|
|
void Door::FieldTouched
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Entity *other;
|
|
|
|
other = ev->GetEntity( 1 );
|
|
|
|
#ifdef SIN_DEMO
|
|
if ( ( state != STATE_OPEN ) && !( spawnflags & DOOR_AUTO_OPEN ) &&
|
|
( !other || !other->isSubclassOf( Actor ) ) )
|
|
#else
|
|
if ( ( state != STATE_OPEN ) && !( spawnflags & DOOR_AUTO_OPEN ) )
|
|
#endif
|
|
{
|
|
return;
|
|
}
|
|
|
|
TryOpen( ev );
|
|
}
|
|
|
|
qboolean Door::CanBeOpenedBy
|
|
(
|
|
Entity *ent
|
|
)
|
|
|
|
{
|
|
assert( master );
|
|
if ( ( master ) && ( master != this ) )
|
|
{
|
|
return master->CanBeOpenedBy( ent );
|
|
}
|
|
|
|
if ( !locked && !key.length() )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ( ent && ent->isSubclassOf( Sentient ) && ( ( Sentient * )ent )->HasItem( key.c_str() ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Door::TryOpen
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Entity *other;
|
|
Event *event;
|
|
|
|
//FIXME
|
|
// hack so that doors aren't triggered by guys when game starts.
|
|
// have to fix delay that guys go through before setting up their threads
|
|
if ( level.time < 0.4 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
other = ev->GetEntity( 1 );
|
|
|
|
assert( master );
|
|
if ( master && ( this != master ) )
|
|
{
|
|
event = new Event( EV_Door_TryOpen );
|
|
event->AddEntity( other );
|
|
master->ProcessEvent( event );
|
|
return;
|
|
}
|
|
|
|
if ( !other || ( other->health <= 0 ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( locked )
|
|
{
|
|
if ( sound_locked.length() > 1 )
|
|
{
|
|
other->sound( sound_locked.c_str(), 1, CHAN_VOICE );
|
|
}
|
|
else if ( other->isSubclassOf( Player) )
|
|
{
|
|
other->RandomSound( "snd_locked", 1, CHAN_VOICE );
|
|
//gi.centerprintf ( other->edict, "jcx yv 20 string \"This door is locked.\"" );
|
|
}
|
|
|
|
// locked doors don't open for anyone
|
|
return;
|
|
}
|
|
|
|
if ( !CanBeOpenedBy( other ) )
|
|
{
|
|
Item *item;
|
|
ClassDef *cls;
|
|
|
|
if ( other->isClient() )
|
|
{
|
|
cls = getClass( key.c_str() );
|
|
if ( !cls )
|
|
{
|
|
gi.dprintf( "No item named '%s'\n", key.c_str() );
|
|
return;
|
|
}
|
|
item = ( Item * )cls->newInstance();
|
|
item->CancelEventsOfType( EV_Item_DropToFloor );
|
|
item->CancelEventsOfType( EV_Remove );
|
|
item->ProcessPendingEvents();
|
|
gi.centerprintf ( other->edict, "jcx yv 20 string \"You need this item:\" jcx yv -20 icon %d", item->GetIconIndex() );
|
|
delete item;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// once we're opened by an item, we no longer need that item to open the door
|
|
key = "";
|
|
|
|
if ( Message().length() )
|
|
{
|
|
gi.centerprintf( other->edict, "jcx jcy string \"%s\"", Message().c_str() );
|
|
sound( sound_message, 1, CHAN_VOICE + CHAN_NO_PHS_ADD, ATTN_NORM );
|
|
}
|
|
|
|
event = new Event( EV_Door_Fire );
|
|
event->AddEntity( other );
|
|
ProcessEvent( event );
|
|
}
|
|
|
|
void Door::SpawnTriggerField
|
|
(
|
|
Vector fmins,
|
|
Vector fmaxs
|
|
)
|
|
|
|
{
|
|
TouchField *trig;
|
|
Vector min;
|
|
Vector max;
|
|
|
|
min = fmins - "60 60 8";
|
|
max = fmaxs + "60 60 8";
|
|
|
|
trig = new TouchField;
|
|
trig->Setup( this, EV_Door_TriggerFieldTouched, min, max, respondto );
|
|
|
|
trigger = trig->entnum;
|
|
}
|
|
|
|
EXPORT_FROM_DLL qboolean Door::DoorTouches
|
|
(
|
|
Door *e1
|
|
)
|
|
|
|
{
|
|
if ( e1->doormin.x > doormax.x )
|
|
{
|
|
return false;
|
|
}
|
|
if ( e1->doormin.y > doormax.y )
|
|
{
|
|
return false;
|
|
}
|
|
if ( e1->doormin.z > doormax.z )
|
|
{
|
|
return false;
|
|
}
|
|
if ( e1->doormax.x < doormin.x )
|
|
{
|
|
return false;
|
|
}
|
|
if ( e1->doormax.y < doormin.y )
|
|
{
|
|
return false;
|
|
}
|
|
if ( e1->doormax.z < doormin.z )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Door::LinkDoors
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Door *ent;
|
|
Door *next;
|
|
Vector cmins;
|
|
Vector cmaxs;
|
|
int t;
|
|
int i;
|
|
|
|
if ( nextdoor )
|
|
{
|
|
// already linked by another door
|
|
return;
|
|
}
|
|
|
|
// master doors own themselves
|
|
master = this;
|
|
|
|
if ( spawnflags & DOOR_DONT_LINK )
|
|
{
|
|
// don't want to link this door
|
|
nextdoor = entnum;
|
|
return;
|
|
}
|
|
|
|
cmins = absmin;
|
|
cmaxs = absmax;
|
|
|
|
ent = this;
|
|
for( t = entnum; t != 0; t = G_FindClass( t, getClassID() ) )
|
|
{
|
|
next = ( Door * )G_GetEntity( t );
|
|
if ( !ent->DoorTouches( next ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( next->nextdoor )
|
|
{
|
|
error( "cross connected doors. Targetname = %s entity %d", TargetName(), entnum );
|
|
}
|
|
|
|
ent->nextdoor = next->entnum;
|
|
ent = next;
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
if ( ent->absmin[ i ] < cmins[ i ] )
|
|
{
|
|
cmins[ i ] = ent->absmin[ i ];
|
|
}
|
|
if ( ent->absmax[ i ] > cmaxs[ i ] )
|
|
{
|
|
cmaxs[ i ] = ent->absmax[ i ];
|
|
}
|
|
}
|
|
|
|
// set master door
|
|
ent->master = this;
|
|
|
|
if ( ent->health )
|
|
{
|
|
health = ent->health;
|
|
}
|
|
|
|
if ( ent->Targeted() )
|
|
{
|
|
if ( !Targeted() )
|
|
{
|
|
SetTargetName( ent->TargetName() );
|
|
}
|
|
else if ( strcmp( TargetName(), ent->TargetName() ) )
|
|
{
|
|
// not a critical error, but let them know about it.
|
|
gi.dprintf( "cross connected doors" );
|
|
|
|
ent->SetTargetName( TargetName() );
|
|
}
|
|
}
|
|
|
|
if ( ent->Message().length() )
|
|
{
|
|
if ( Message().length() && !strcmp( Message().c_str(), ent->Message().c_str() ) )
|
|
{
|
|
// not a critical error, but let them know about it.
|
|
gi.dprintf( "Different messages on linked doors. Targetname = %s", TargetName() );
|
|
}
|
|
|
|
// only master should have a message
|
|
SetMessage( ent->Message().c_str() );
|
|
ent->SetMessage( NULL );
|
|
}
|
|
}
|
|
|
|
// make the chain a loop
|
|
ent->nextdoor = entnum;
|
|
|
|
// open up any portals we control
|
|
SetAreaPortals( Target(), ( spawnflags & DOOR_START_OPEN ) ? true : false );
|
|
|
|
// shootable or targeted doors don't need a trigger
|
|
if ( health || ( spawnflags & DOOR_TARGETED ) )
|
|
{
|
|
// Don't let the player trigger the door
|
|
return;
|
|
}
|
|
|
|
// Don't spawn trigger field when set to toggle
|
|
if ( !( spawnflags & DOOR_TOGGLE ) )
|
|
{
|
|
SpawnTriggerField( cmins, cmaxs );
|
|
}
|
|
}
|
|
|
|
void Door::SetTime
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
traveltime = ev->GetFloat( 1 );
|
|
if ( traveltime < FRAMETIME )
|
|
{
|
|
traveltime = FRAMETIME;
|
|
}
|
|
|
|
speed = 1.0f / traveltime;
|
|
}
|
|
|
|
void Door::LockDoor
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
locked = true;
|
|
}
|
|
|
|
void Door::UnlockDoor
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
locked = false;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*SINED func_rotatingdoor (0 .5 .8) ? START_OPEN OPEN_DIRECTION DOOR_DONT_LINK NOT_PLAYERS NOT_MONSTERS TOGGLE AUTO_OPEN TARGETED
|
|
if two doors touch, they are assumed to be connected and operate as a unit.
|
|
|
|
TOGGLE causes the door to wait in both the start and end states for a trigger event.
|
|
DOOR_DONT_LINK is for when you have two doors that are touching but you want to operate independently.
|
|
|
|
START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors).
|
|
OPEN_DIRECTION indicates which direction to open when START_OPEN is set.
|
|
AUTO_OPEN causes the door to open when a player is near instead of waiting for the player to use the door.
|
|
TARGETED door is only operational from triggers or script
|
|
|
|
"message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
|
|
"openangle" how wide to open the door
|
|
"angle" determines the opening direction. point toward the middle of the door (away from the hinge)
|
|
"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
|
|
"health" if set, door must be shot open
|
|
"time" move time (0.3 default)
|
|
"wait" wait before returning (3 default, -1 = never return)
|
|
"dmg" damage to inflict when blocked (0 default)
|
|
"key" The item needed to open this door (default nothing)
|
|
|
|
"sound_stop" Specify the sound that plays when the door stops moving (default global door_stop)
|
|
"sound_move" Specify the sound that plays when the door opens or closes (default global door_moving)
|
|
"sound_message" Specify the sound that plays when the door displays a message
|
|
"sound_locked" Specify the sound that plays when the door is locked
|
|
|
|
/*****************************************************************************/
|
|
CLASS_DECLARATION( Door, RotatingDoor, "func_rotatingdoor" );
|
|
|
|
ResponseDef RotatingDoor::Responses[] =
|
|
{
|
|
{ &EV_Door_DoClose, ( Response )RotatingDoor::DoClose },
|
|
{ &EV_Door_DoOpen, ( Response )RotatingDoor::DoOpen },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
void RotatingDoor::DoOpen
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Vector ang;
|
|
|
|
if ( previous_state == STATE_CLOSED )
|
|
{
|
|
if ( ev->NumArgs() > 0 )
|
|
{
|
|
Entity *other;
|
|
Vector p;
|
|
|
|
other = ev->GetEntity( 1 );
|
|
p = other->worldorigin - worldorigin;
|
|
p.z = 0;
|
|
diropened = dir * p;
|
|
}
|
|
else
|
|
{
|
|
diropened = 0 - init_door_direction;
|
|
}
|
|
}
|
|
|
|
if ( diropened < 0 )
|
|
{
|
|
ang = startangle + Vector( 0, angle, 0 );
|
|
}
|
|
else
|
|
{
|
|
ang = startangle - Vector( 0, angle, 0 );
|
|
}
|
|
|
|
MoveTo( worldorigin, ang, fabs( speed*angle ), EV_Door_OpenEnd );
|
|
}
|
|
|
|
void RotatingDoor::DoClose
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
MoveTo( worldorigin, startangle, fabs( speed*angle ), EV_Door_CloseEnd );
|
|
}
|
|
|
|
RotatingDoor::RotatingDoor()
|
|
{
|
|
startangle = angles;
|
|
|
|
angle = G_GetFloatArg( "openangle", 90 );
|
|
|
|
init_door_direction = (spawnflags & DOOR_OPEN_DIRECTION);
|
|
}
|
|
|
|
/*
|
|
/*****************************************************************************/
|
|
/*SINED func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK NOT_PLAYERS NOT_MONSTERS TOGGLE AUTO_OPEN TARGETED
|
|
if two doors touch, they are assumed to be connected and operate as a unit.
|
|
|
|
TOGGLE causes the door to wait in both the start and end states for a trigger event.
|
|
DOOR_DONT_LINK is for when you have two doors that are touching but you want to operate independently.
|
|
|
|
START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors).
|
|
OPEN_DIRECTION indicates which direction to open when START_OPEN is set.
|
|
AUTO_OPEN causes the door to open when a player is near instead of waiting for the player to use the door.
|
|
TARGETED door is only operational from triggers or script
|
|
|
|
"message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
|
|
"angle" determines the opening direction. point toward the middle of the door (away from the hinge)
|
|
"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
|
|
"health" if set, door must be shot open
|
|
"speed" move speed (100 default)
|
|
"time" move time (1/speed default, overides speed)
|
|
"wait" wait before returning (3 default, -1 = never return)
|
|
"lip" lip remaining at end of move (8 default)
|
|
"dmg" damage to inflict when blocked (0 default)
|
|
"key" The item needed to open this door (default nothing)
|
|
|
|
"sound_stop" Specify the sound that plays when the door stops moving (default global door_stop)
|
|
"sound_move" Specify the sound that plays when the door opens or closes (default global door_moving)
|
|
"sound_message" Specify the sound that plays when the door displays a message
|
|
"sound_locked" Specify the sound that plays when the door is locked
|
|
|
|
/*****************************************************************************/
|
|
CLASS_DECLARATION( Door, SlidingDoor, "func_door" );
|
|
|
|
ResponseDef SlidingDoor::Responses[] =
|
|
{
|
|
{ &EV_Door_DoClose, ( Response )SlidingDoor::DoClose },
|
|
{ &EV_Door_DoOpen, ( Response )SlidingDoor::DoOpen },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
void SlidingDoor::DoOpen
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
MoveTo( pos2, angles, speed*totalmove, EV_Door_OpenEnd );
|
|
}
|
|
|
|
void SlidingDoor::DoClose
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
MoveTo( pos1, angles, speed*totalmove, EV_Door_CloseEnd );
|
|
}
|
|
|
|
SlidingDoor::SlidingDoor()
|
|
{
|
|
Vector movedir;
|
|
float sp;
|
|
|
|
lip = G_GetFloatArg( "lip", 8 );
|
|
|
|
movedir = G_GetMovedir();
|
|
totalmove = fabs( movedir * size ) - lip;
|
|
pos1 = worldorigin;
|
|
pos2 = pos1 + movedir * totalmove;
|
|
setOrigin( pos1 );
|
|
|
|
sp = G_GetFloatArg( "speed", 0 );
|
|
if (sp)
|
|
{
|
|
speed = sp / totalmove;
|
|
}
|
|
}
|
|
|
|
/*
|
|
/*****************************************************************************/
|
|
/*SINED func_scriptdoor (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK NOT_PLAYERS NOT_MONSTERS TOGGLE AUTO_OPEN TARGETED
|
|
if two doors touch, they are assumed to be connected and operate as a unit.
|
|
|
|
TOGGLE causes the door to wait in both the start and end states for a trigger event.
|
|
DOOR_DONT_LINK is for when you have two doors that are touching but you want to operate independently.
|
|
|
|
START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors).
|
|
OPEN_DIRECTION indicates which direction to open when START_OPEN is set.
|
|
AUTO_OPEN causes the door to open when a player is near instead of waiting for the player to use the door.
|
|
TARGETED door is only operational from triggers or script
|
|
|
|
"message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
|
|
"angle" determines the opening direction. point toward the middle of the door (away from the hinge)
|
|
"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
|
|
"health" if set, door must be shot open
|
|
"speed" move speed (100 default)
|
|
"time" move time (1/speed default, overides speed)
|
|
"wait" wait before returning (3 default, -1 = never return)
|
|
"dmg" damage to inflict when blocked (0 default)
|
|
"key" The item needed to open this door (default nothing)
|
|
"initthread" code to execute to setup the door (optional)
|
|
"openthread" code to execute when opening the door (required)
|
|
"closethread" code to execute when closing the door (required)
|
|
|
|
"sound_stop" Specify the sound that plays when the door stops moving (default global door_stop)
|
|
"sound_move" Specify the sound that plays when the door opens or closes (default global door_moving)
|
|
"sound_message" Specify the sound that plays when the door displays a message
|
|
"sound_locked" Specify the sound that plays when the door is locked
|
|
|
|
/*****************************************************************************/
|
|
CLASS_DECLARATION( Door, ScriptDoor, "func_scriptdoor" );
|
|
|
|
Event EV_ScriptDoor_DoInit( "doinit" );
|
|
Event EV_ScriptDoor_SetOpenThread( "openthread" );
|
|
Event EV_ScriptDoor_SetCloseThread( "closethread" );
|
|
|
|
ResponseDef ScriptDoor::Responses[] =
|
|
{
|
|
{ &EV_ScriptDoor_DoInit, ( Response )ScriptDoor::DoInit },
|
|
{ &EV_Door_DoClose, ( Response )ScriptDoor::DoClose },
|
|
{ &EV_Door_DoOpen, ( Response )ScriptDoor::DoOpen },
|
|
{ &EV_ScriptDoor_SetOpenThread, ( Response )ScriptDoor::SetOpenThread },
|
|
{ &EV_ScriptDoor_SetCloseThread, ( Response )ScriptDoor::SetCloseThread },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
void ScriptDoor::SetOpenThread
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
openthreadname = ev->GetString( 1 );
|
|
}
|
|
|
|
void ScriptDoor::SetCloseThread
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
closethreadname = ev->GetString( 1 );
|
|
}
|
|
|
|
void ScriptDoor::DoInit
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
const char * label = NULL;
|
|
GameScript * s;
|
|
const char * tname;
|
|
|
|
s = ScriptLib.GetScript( ScriptLib.GetGameScript() );
|
|
|
|
if ( !s )
|
|
{
|
|
warning( "DoInit", "Null game script" );
|
|
return;
|
|
}
|
|
|
|
if ( initthreadname.length() )
|
|
label = initthreadname.c_str();
|
|
|
|
doorthread = Director.CreateThread( s, label, MODEL_SCRIPT );
|
|
if ( !doorthread )
|
|
{
|
|
warning( "DoInit", "Could not allocate thread." );
|
|
return;
|
|
}
|
|
doorthread->Vars()->SetVariable( "self", this );
|
|
tname = TargetName();
|
|
if ( tname && tname[ 0 ] )
|
|
{
|
|
str name;
|
|
name = "$" + str( tname );
|
|
doorthread->Vars()->SetVariable( "targetname", name.c_str() );
|
|
}
|
|
doorthread->Vars()->SetVariable( "startorigin", startorigin );
|
|
doorthread->Vars()->SetVariable( "startangles", startangle );
|
|
doorthread->Vars()->SetVariable( "movedir", movedir );
|
|
doorthread->Vars()->SetVariable( "doorsize", doorsize );
|
|
if ( initthreadname.length() )
|
|
{
|
|
// start right away
|
|
doorthread->Start( -1 );
|
|
}
|
|
}
|
|
|
|
void ScriptDoor::DoOpen
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
if ( !doorthread )
|
|
{
|
|
warning( "DoOpen", "No Thread allocated." );
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if ( !doorthread->Goto( openthreadname.c_str() ) )
|
|
{
|
|
warning( "DoOpen", "Could not goto %s", openthreadname.c_str() );
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( previous_state == STATE_CLOSED )
|
|
{
|
|
diropened = 0;
|
|
if ( ev->NumArgs() > 0 )
|
|
{
|
|
Entity *other;
|
|
Vector p;
|
|
|
|
other = ev->GetEntity( 1 );
|
|
p = other->worldorigin - worldorigin;
|
|
p.z = 0;
|
|
diropened = dir * p;
|
|
}
|
|
}
|
|
doorthread->Vars()->SetVariable( "origin", worldorigin );
|
|
doorthread->Vars()->SetVariable( "opendot", diropened );
|
|
doorthread->Start( 0 );
|
|
}
|
|
|
|
void ScriptDoor::DoClose
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
if ( !doorthread )
|
|
{
|
|
warning( "DoClose", "No Thread allocated." );
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if ( !doorthread->Goto( closethreadname.c_str() ) )
|
|
{
|
|
warning( "DoOpen", "Could not goto %s", closethreadname.c_str() );
|
|
}
|
|
}
|
|
doorthread->Vars()->SetVariable( "origin", worldorigin );
|
|
doorthread->Start( 0 );
|
|
}
|
|
|
|
ScriptDoor::ScriptDoor()
|
|
{
|
|
const char * text;
|
|
|
|
startangle = angles;
|
|
//
|
|
// see if we have an initthread
|
|
//
|
|
text = G_GetSpawnArg( "initthread" );
|
|
if ( text )
|
|
initthreadname = text;
|
|
|
|
//
|
|
// see if we have an openthread
|
|
//
|
|
text = G_GetSpawnArg( "openthread" );
|
|
if ( text )
|
|
openthreadname = text;
|
|
else
|
|
warning("ScriptDoor","No openthread defined for door at %.2f %.2f %.2f", origin[0], origin[1], origin[2] );
|
|
|
|
//
|
|
// see if we have an closethread
|
|
//
|
|
text = G_GetSpawnArg( "closethread" );
|
|
if ( text )
|
|
closethreadname = text;
|
|
else
|
|
warning("ScriptDoor","No closethread defined for door at %.2f %.2f %.2f", origin[0], origin[1], origin[2] );
|
|
//
|
|
// clear out the sounds if necessary
|
|
// scripted doors typically have their own sounds
|
|
//
|
|
text = G_GetSpawnArg( "sound_stop" );
|
|
if ( !text )
|
|
sound_stop = "";
|
|
text = G_GetSpawnArg( "sound_move" );
|
|
if ( !text )
|
|
sound_move = "";
|
|
|
|
movedir = G_GetMovedir();
|
|
startorigin = worldorigin;
|
|
doorsize = fabs( movedir * size );
|
|
PostEvent( EV_ScriptDoor_DoInit, 0 );
|
|
}
|