This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Combat/elevator.cpp

1006 lines
28 KiB
C++

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando *
* *
* $Archive:: /Commando/Code/Combat/elevator.cpp $*
* *
* $Author:: Byon_g $*
* *
* $Modtime:: 9/13/01 12:03p $*
* *
* $Revision:: 25 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "elevator.h"
#include "simpledefinitionfactory.h"
#include "persistfactory.h"
#include "wwphysids.h"
#include "bitstream.h"
#include "debug.h"
#include "hanim.h"
#include "wwhack.h"
#include "combat.h"
#include "smartgameobj.h"
#include "gameobjmanager.h"
#include "soldier.h"
#include "wwprofile.h"
#include "audiblesound.h"
#include "wwaudio.h"
#include "bitpackids.h"
DECLARE_FORCE_LINK( elevatorphys );
/*
** Local constants
*/
enum
{
EFFECT_DOOR_OPENING,
EFFECT_DOOR_CLOSING,
EFFECT_DOOR_UNLOCKED,
EFFECT_ACCESS_DENIED,
EFFECT_ELEVATOR_MOVING,
EFFECT_ELEVATOR_STOPPED_MOVING,
};
/*
** ElevatorPhysDefClass
*/
SimplePersistFactoryClass<ElevatorPhysDefClass, PHYSICS_CHUNKID_ELEVATORPHYSDEF> _ElevatorPhysDefPersistFactory;
DECLARE_DEFINITION_FACTORY(ElevatorPhysDefClass, CLASSID_ELEVATORPHYSDEF, "ElevatorPhys") _ElevatorPhysDefDefFactory;
ElevatorPhysDefClass::ElevatorPhysDefClass( void ) :
AccessiblePhysDefClass(),
CloseDelay( 2 ),
/*LowerCallZone( Vector3( 0,0,0), Vector3( 1,1,1 ) ),
LowerInsideZone( Vector3( 0,0,0), Vector3( 1,1,1 ) ),
UpperCallZone( Vector3( 0,0,0), Vector3( 1,1,1 ) ),
UpperInsideZone( Vector3( 0,0,0), Vector3( 1,1,1 ) ),*/
DoorClosedTop_FrameNum( -1 ),
DoorOpeningBottom_FrameNum( -1 ),
ElevatorStartTop_FrameNum( -1 ),
ElevatorStoppedBottom_FrameNum( -1 ),
DoorOpenSoundDefID( 0 ),
DoorCloseSoundDefID( 0 ),
DoorUnlockSoundDefID( 0 ),
DoorAccessDeniedSoundDefID( 0 ),
ElevatorMovingSoundDefID( 0 )
{
for (int index = 0; index < ZONE_MAX; index ++) {
CallZones[index].Center = Vector3( 0, 0, 0 );
CallZones[index].Extent = Vector3( 1, 1, 1 );
}
ZONE_PARAM(ElevatorPhysDefClass, CallZones[ZONE_LOWER_CALL], "LowerCallZone" );
ZONE_PARAM(ElevatorPhysDefClass, CallZones[ZONE_LOWER_INSIDE], "LowerInsideZone" );
ZONE_PARAM(ElevatorPhysDefClass, CallZones[ZONE_UPPER_CALL], "UpperCallZone" );
ZONE_PARAM(ElevatorPhysDefClass, CallZones[ZONE_UPPER_INSIDE], "UpperInsideZone" );
EDITABLE_PARAM(ElevatorPhysDefClass, ParameterClass::TYPE_FLOAT, CloseDelay );
PARAM_SEPARATOR(ElevatorPhysDefClass, "Frame Numbers");
EDITABLE_PARAM(ElevatorPhysDefClass, ParameterClass::TYPE_FLOAT, DoorClosedTop_FrameNum );
EDITABLE_PARAM(ElevatorPhysDefClass, ParameterClass::TYPE_FLOAT, DoorOpeningBottom_FrameNum );
EDITABLE_PARAM(ElevatorPhysDefClass, ParameterClass::TYPE_FLOAT, ElevatorStartTop_FrameNum );
EDITABLE_PARAM(ElevatorPhysDefClass, ParameterClass::TYPE_FLOAT, ElevatorStoppedBottom_FrameNum );
PARAM_SEPARATOR(ElevatorPhysDefClass, "Sounds");
EDITABLE_PARAM(ElevatorPhysDefClass, ParameterClass::TYPE_SOUNDDEFINITIONID, DoorOpenSoundDefID );
EDITABLE_PARAM(ElevatorPhysDefClass, ParameterClass::TYPE_SOUNDDEFINITIONID, DoorCloseSoundDefID );
EDITABLE_PARAM(ElevatorPhysDefClass, ParameterClass::TYPE_SOUNDDEFINITIONID, DoorUnlockSoundDefID );
EDITABLE_PARAM(ElevatorPhysDefClass, ParameterClass::TYPE_SOUNDDEFINITIONID, DoorAccessDeniedSoundDefID );
EDITABLE_PARAM(ElevatorPhysDefClass, ParameterClass::TYPE_SOUNDDEFINITIONID, ElevatorMovingSoundDefID );
}
uint32 ElevatorPhysDefClass::Get_Class_ID (void) const
{
return CLASSID_ELEVATORPHYSDEF;
}
bool ElevatorPhysDefClass::Is_Type(const char * type_name)
{
if (stricmp(type_name,ElevatorPhysDefClass::Get_Type_Name()) == 0) {
return true;
} else {
return AccessiblePhysDefClass::Is_Type(type_name);
}
}
PersistClass * ElevatorPhysDefClass::Create( void ) const
{
ElevatorPhysClass * elevator = new ElevatorPhysClass;
elevator->Init( *this );
return elevator;
}
enum {
CHUNKID_DEF_OLD_PARENT = 714001418,
CHUNKID_DEF_VARIABLES,
CHUNKID_DEF_PARENT,
MICROCHUNKID_DEF_CLOSE_DELAY = 1,
MICROCHUNKID_DEF_LOCK_CODE,
MICROCHUNKID_DEF_UPPER_CALL_ZONE,
MICROCHUNKID_DEF_UPPER_INSIDE_ZONE,
MICROCHUNKID_DEF_LOWER_CALL_ZONE,
MICROCHUNKID_DEF_LOWER_INSIDE_ZONE,
MICROCHUNKID_DEF_DOORCLOSED_FRAMENUM,
MICROCHUNKID_DEF_DOOROPENING_FRAMENUM,
MICROCHUNKID_DEF_ELEVATOR_START_FRAMENUM,
MICROCHUNKID_DEF_ELEVATOR_STOPPED_FRAMENUM,
MICROCHUNKID_DEF_DOOR_OPEN_SOUNDID,
MICROCHUNKID_DEF_DOOR_CLOSE_SOUNDID,
MICROCHUNKID_DEF_DOOR_UNLOCK_SOUNDID,
MICROCHUNKID_DEF_DOOR_ACCESS_DENIED_SOUNDID,
MICROCHUNKID_DEF_ELEVATOR_MOVING_SOUNDID
};
bool ElevatorPhysDefClass::Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_DEF_PARENT );
AccessiblePhysDefClass::Save( csave );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_DEF_VARIABLES );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_CLOSE_DELAY, CloseDelay );
//WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_LOCK_CODE, LockCode );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_LOWER_CALL_ZONE, CallZones[ZONE_LOWER_CALL] );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_LOWER_INSIDE_ZONE, CallZones[ZONE_LOWER_INSIDE] );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_UPPER_CALL_ZONE, CallZones[ZONE_UPPER_CALL] );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_UPPER_INSIDE_ZONE, CallZones[ZONE_UPPER_INSIDE] );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_DOORCLOSED_FRAMENUM, DoorClosedTop_FrameNum );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_DOOROPENING_FRAMENUM, DoorOpeningBottom_FrameNum );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_ELEVATOR_START_FRAMENUM, ElevatorStartTop_FrameNum );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_ELEVATOR_STOPPED_FRAMENUM, ElevatorStoppedBottom_FrameNum );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_DOOR_OPEN_SOUNDID, DoorOpenSoundDefID );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_DOOR_CLOSE_SOUNDID, DoorCloseSoundDefID );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_DOOR_UNLOCK_SOUNDID, DoorUnlockSoundDefID );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_DOOR_ACCESS_DENIED_SOUNDID, DoorAccessDeniedSoundDefID );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_ELEVATOR_MOVING_SOUNDID, ElevatorMovingSoundDefID );
csave.End_Chunk();
return true;
}
bool ElevatorPhysDefClass::Load( ChunkLoadClass &cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_DEF_OLD_PARENT:
StaticAnimPhysDefClass::Load( cload );
break;
case CHUNKID_DEF_PARENT:
AccessiblePhysDefClass::Load( cload );
break;
case CHUNKID_DEF_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_CLOSE_DELAY, CloseDelay );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_LOCK_CODE, LockCode );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_LOWER_CALL_ZONE, CallZones[ZONE_LOWER_CALL] );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_LOWER_INSIDE_ZONE, CallZones[ZONE_LOWER_INSIDE] );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_UPPER_CALL_ZONE, CallZones[ZONE_UPPER_CALL] );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_UPPER_INSIDE_ZONE, CallZones[ZONE_UPPER_INSIDE] );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_DOORCLOSED_FRAMENUM, DoorClosedTop_FrameNum );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_DOOROPENING_FRAMENUM, DoorOpeningBottom_FrameNum );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_ELEVATOR_START_FRAMENUM, ElevatorStartTop_FrameNum );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_ELEVATOR_STOPPED_FRAMENUM, ElevatorStoppedBottom_FrameNum );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_DOOR_OPEN_SOUNDID, DoorOpenSoundDefID );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_DOOR_CLOSE_SOUNDID, DoorCloseSoundDefID );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_DOOR_UNLOCK_SOUNDID, DoorUnlockSoundDefID );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_DOOR_ACCESS_DENIED_SOUNDID, DoorAccessDeniedSoundDefID );
READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_ELEVATOR_MOVING_SOUNDID, ElevatorMovingSoundDefID );
default:
Debug_Say(( "Unrecognized ElevatorPhys Variable chunkID\n" ));
break;
}
cload.Close_Micro_Chunk();
}
break;
default:
Debug_Say(( "Unrecognized ElevatorPhys chunkID\n" ));
break;
}
cload.Close_Chunk();
}
return true;
}
const PersistFactoryClass & ElevatorPhysDefClass::Get_Factory (void) const
{
return _ElevatorPhysDefPersistFactory;
}
/*
**
*/
SimplePersistFactoryClass<ElevatorPhysClass, PHYSICS_CHUNKID_ELEVATORPHYS> _ElevatorPhysPersistFactory;
const PersistFactoryClass & ElevatorPhysClass::Get_Factory (void) const
{
return _ElevatorPhysPersistFactory;
}
/*
**
*/
ElevatorPhysClass::ElevatorPhysClass( void ) :
CheckTimer( 0 ),
State( STATE_UP ),
MovingSoundObj( NULL ),
PrevFrame( 0 ),
IsCallTimerSet( false ),
CallTimer( 0 ),
TriggerRequest( 0 )
{
DoorStates[TOP] = DOOR_STATE_NORMAL;
DoorStates[BOTTOM] = DOOR_STATE_NORMAL;
}
ElevatorPhysClass::~ElevatorPhysClass( void )
{
if ( MovingSoundObj != NULL ) {
MovingSoundObj->Stop();
MovingSoundObj->Remove_From_Scene();
REF_PTR_RELEASE( MovingSoundObj );
}
}
void ElevatorPhysClass::Init( const ElevatorPhysDefClass & def )
{
AccessiblePhysClass::Init(def);
AnimManager.Set_Animation_Mode( AnimCollisionManagerClass::ANIMATE_TARGET );
AnimManager.Set_Target_Frame( 0 );
}
/*
** Save and Load
*/
enum {
CHUNKID_PARENT = 714001438,
CHUNKID_VARIABLES,
CHUNKID_RIDER_OBJECT,
MICROCHUNKID_ = 1,
MICROCHUNKID_STATE,
MICROCHUNKID_TOP_DOOR_STATE,
MICROCHUNKID_BOTTOM_DOOR_STATE
};
bool ElevatorPhysClass::Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
AccessiblePhysClass::Save( csave );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_VARIABLES );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATE, State );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TOP_DOOR_STATE, DoorStates[TOP] );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_BOTTOM_DOOR_STATE, DoorStates[BOTTOM] );
csave.End_Chunk();
if ( CurrentAIRider != NULL ) {
csave.Begin_Chunk( CHUNKID_RIDER_OBJECT );
CurrentAIRider.Save( csave );
csave.End_Chunk();
}
return true;
}
bool ElevatorPhysClass::Load( ChunkLoadClass &cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
AccessiblePhysClass::Load( cload );
break;
case CHUNKID_RIDER_OBJECT:
CurrentAIRider.Load( cload );
break;
case CHUNKID_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK( cload, MICROCHUNKID_STATE, State );
READ_MICRO_CHUNK( cload, MICROCHUNKID_TOP_DOOR_STATE, DoorStates[TOP] );
READ_MICRO_CHUNK( cload, MICROCHUNKID_BOTTOM_DOOR_STATE, DoorStates[BOTTOM] );
default:
Debug_Say(( "Unrecognized ElevatorPhys Variable chunkID\n" ));
break;
}
cload.Close_Micro_Chunk();
}
break;
default:
Debug_Say(( "Unrecognized ElevatorPhys chunkID\n" ));
break;
}
cload.Close_Chunk();
}
AnimManager.Set_Animation_Mode( AnimCollisionManagerClass::ANIMATE_TARGET );
AnimManager.Set_Target_Frame( 0 );
return true;
}
void ElevatorPhysClass::Save_State( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_PARENT );
AccessiblePhysClass::Save_State( csave );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_VARIABLES );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATE, State );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TOP_DOOR_STATE, DoorStates[TOP] );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_BOTTOM_DOOR_STATE, DoorStates[BOTTOM] );
csave.End_Chunk();
if ( CurrentAIRider != NULL ) {
csave.Begin_Chunk( CHUNKID_RIDER_OBJECT );
CurrentAIRider.Save( csave );
csave.End_Chunk();
}
}
#define LEGACY_STATICANIMPHYS_CHUNK_ANIMMANAGER 0x01170012
void ElevatorPhysClass::Load_State( ChunkLoadClass &cload )
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PARENT:
AccessiblePhysClass::Load_State( cload );
break;
case CHUNKID_RIDER_OBJECT:
CurrentAIRider.Load( cload );
break;
case CHUNKID_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK( cload, MICROCHUNKID_STATE, State );
READ_MICRO_CHUNK( cload, MICROCHUNKID_TOP_DOOR_STATE, DoorStates[TOP] );
READ_MICRO_CHUNK( cload, MICROCHUNKID_BOTTOM_DOOR_STATE, DoorStates[BOTTOM] );
default:
Debug_Say(( "Unrecognized ElevatorPhys Variable chunkID\n" ));
break;
}
cload.Close_Micro_Chunk();
}
break;
case LEGACY_STATICANIMPHYS_CHUNK_ANIMMANAGER:
AnimManager.Load(cload);
break;
default:
Debug_Say(( "Unrecognized ElevatorPhys chunkID\n" ));
break;
}
cload.Close_Chunk();
}
}
void ElevatorPhysClass::Timestep( float dt )
{
AccessiblePhysClass::Timestep( dt );
/*const ElevatorPhysDefClass *def = Get_ElevatorPhysDef();
for (int index = 0; index < ZONE_MAX; index ++) {
OBBoxClass world_box;
OBBoxClass::Transform( Get_Transform(), def->CallZones[index], &world_box );
PhysicsSceneClass::Get_Instance ()->Add_Debug_OBBox (world_box, Vector3 (1, 0, 0));
}*/
{
WWPROFILE("ElevatorPhys::Timestep");
// Delayed Open/close
if ( AnimManager.Peek_Animation() != NULL ) {
//
// Make sure the door sounds play at the appropriate times
//
Update_Sound_Effects();
if ( IsCallTimerSet ) {
CallTimer -= dt;
}
//
// Should we evaluate the state of the elevator?
//
CheckTimer -= dt;
if ( CheckTimer <= 0 ) {
CheckTimer = 0.5F;
Update_State();
}
}
}
return ;
}
void ElevatorPhysClass::Set_Door_State( int new_state, int door_id )
{
if ( new_state != DoorStates[door_id] ) {
switch ( new_state )
{
case DOOR_STATE_NORMAL:
break;
case DOOR_STATE_UNLOCKED:
Play_Effect( EFFECT_DOOR_UNLOCKED, door_id == TOP );
break;
case DOOR_STATE_ACCESS_DENIED:
Play_Effect( EFFECT_ACCESS_DENIED, door_id == TOP );
break;
}
DoorStates[door_id] = new_state;
}
return ;
}
void ElevatorPhysClass::Set_State( int new_state )
{
if ( new_state != State ) {
switch ( new_state )
{
case STATE_DOWN:
case STATE_UP:
//
// Leave the elevator in this state for at least 4 seconds (before it checks
// to see if it should move to the next level).
//
//Timer = 4;
break;
case STATE_MOVING_UP:
AnimManager.Set_Animation_Mode( AnimCollisionManagerClass::ANIMATE_TARGET );
AnimManager.Set_Target_Frame( 0 );
break;
case STATE_MOVING_DOWN:
AnimManager.Set_Animation_Mode( AnimCollisionManagerClass::ANIMATE_TARGET );
AnimManager.Set_Target_Frame( AnimManager.Peek_Animation()->Get_Num_Frames() - 1 );
break;
}
State = new_state;
}
return ;
}
void ElevatorPhysClass::Update_State(void)
{
switch ( State ) {
case STATE_MOVING_UP:
if ( AnimManager.Get_Current_Frame() == 0 ) {
Set_State( STATE_UP );
}
break;
case STATE_MOVING_DOWN:
if ( AnimManager.Get_Current_Frame() == AnimManager.Peek_Animation()->Get_Num_Frames() - 1 ) {
Set_State( STATE_DOWN );
}
break;
case STATE_UP:
if ( CombatManager::I_Am_Server() ) {
if ( IsCallTimerSet && CallTimer <= 0 ) {
IsCallTimerSet = false;
if ( Triggered( ZONE_LOWER_CALL ) || Triggered( ZONE_UPPER_INSIDE ) ) {
Set_State( STATE_MOVING_DOWN );
TriggerRequest = 0;
}
} else if ( IsCallTimerSet == false ) {
bool in_call = Triggered( ZONE_LOWER_CALL );
bool in_inside = Triggered( ZONE_UPPER_INSIDE );
if ( in_call && !in_inside ) {
IsCallTimerSet = true;
CallTimer = 1.0F;
} else if ( in_call || in_inside ) {
IsCallTimerSet = true;
CallTimer = 1.0F;
}
}
}
break;
case STATE_DOWN:
if ( CombatManager::I_Am_Server() ) {
if ( IsCallTimerSet && CallTimer <= 0 ) {
IsCallTimerSet = false;
if ( Triggered( ZONE_LOWER_INSIDE ) || Triggered( ZONE_UPPER_CALL ) ) {
Set_State( STATE_MOVING_UP );
TriggerRequest = 0;
}
} else if ( IsCallTimerSet == false ) {
bool in_call = Triggered( ZONE_UPPER_CALL );
bool in_inside = Triggered( ZONE_LOWER_INSIDE );
if ( in_call && !in_inside ) {
IsCallTimerSet = true;
CallTimer = 1.0F;
} else if ( in_call || in_inside ) {
IsCallTimerSet = true;
CallTimer = 1.0F;
}
}
}
break;
}
return ;
}
inline bool Is_Top_Zone ( int zone_id )
{
return ((zone_id > ZONE_LOWER_INSIDE) && (zone_id <= ZONE_UPPER_INSIDE));
}
inline bool Is_Call_Zone ( int zone_id )
{
return (zone_id == ZONE_LOWER_CALL) || (zone_id == ZONE_UPPER_CALL);
}
bool ElevatorPhysClass::Triggered( int zone_id )
{
const ElevatorPhysDefClass *def = Get_ElevatorPhysDef();
OBBoxClass world_box;
OBBoxClass::Transform( Get_Transform(), def->CallZones[zone_id], &world_box );
//
// Get information about the zone we are checking
//
bool is_top = ::Is_Top_Zone( zone_id );
bool is_call = ::Is_Call_Zone( zone_id );
//
// Quick return if an AI is requesting the elevator
//
if (TriggerRequest & (1 << zone_id)) {
return true;
}
//
// Get the list of all the players in the world
//
SList<SoldierGameObj> *player_list = GameObjManager::Get_Star_Game_Obj_List();
//
// Loop over each player
//
SLNode<SoldierGameObj> *objnode;
for ( objnode = player_list->Head(); objnode; objnode = objnode->Next()) {
SoldierGameObj *soldier = objnode->Data();
if ( soldier != NULL ) {
//
// Is this player inside the trigger zone?
//
Vector3 pos;
soldier->Get_Position( &pos );
if ( CollisionMath::Overlap_Test(world_box, pos) == CollisionMath::INSIDE ) {
//
// Is the door locked?
//
if ( Get_ElevatorPhysDef()->LockCode != 0 ) {
bool open_door = true;
if ( is_call ) {
if ( soldier->Has_Key( Get_ElevatorPhysDef()->LockCode ) ) {
//
// The player just unlocked this door...
//
Set_Door_State( DOOR_STATE_UNLOCKED, is_top ? TOP : BOTTOM );
} else {
//
// Someone tried to enter this door without unlocking it...
//
Set_Door_State( DOOR_STATE_ACCESS_DENIED, is_top ? TOP : BOTTOM );
open_door = false;
}
}
if ( open_door ) {
return true;
}
} else {
//
// Reset the state of the door to 'normal'
//
if ( is_call ) {
Set_Door_State( DOOR_STATE_NORMAL, is_top ? TOP : BOTTOM );
}
return true;
}
}
}
}
//
// Reset the state of the door to 'normal'
//
if ( is_call ) {
Set_Door_State( DOOR_STATE_NORMAL, is_top ? TOP : BOTTOM );
}
return false;
}
inline bool Has_Frame_Occured (float prev_frame, float current_frame, float target_frame)
{
bool retval = false;
//
// Check to see if the animation passed the target frame in either direction
//
if ( target_frame >= 0 ) {
retval = ((prev_frame <= target_frame) && (current_frame >= target_frame));
retval |= ((prev_frame >= target_frame) && (current_frame <= target_frame));
}
return retval;
}
void ElevatorPhysClass::Update_Sound_Effects( void )
{
const ElevatorPhysDefClass *def = Get_ElevatorPhysDef();
float curr_frame = AnimManager.Get_Current_Frame();
float prev_frame = PrevFrame;
if ( State == STATE_MOVING_DOWN ) {
//
// Check to see if we should play the opening or closing sounds on the door
//
if ( Has_Frame_Occured( prev_frame, curr_frame, 0 ) ) {
Play_Effect( EFFECT_DOOR_CLOSING, true );
} else if ( Has_Frame_Occured( prev_frame, curr_frame, def->DoorOpeningBottom_FrameNum ) ) {
Play_Effect( EFFECT_DOOR_OPENING, false );
}
//
// Check to see if we should start or stop the elevator moving sound
//
if ( Has_Frame_Occured( prev_frame, curr_frame, def->ElevatorStartTop_FrameNum ) ) {
Play_Effect( EFFECT_ELEVATOR_MOVING, true );
} else if ( Has_Frame_Occured( prev_frame, curr_frame, def->ElevatorStoppedBottom_FrameNum ) ) {
Play_Effect( EFFECT_ELEVATOR_STOPPED_MOVING, false );
}
} else if ( State == STATE_MOVING_UP ) {
//
// Check to see if we should play the opening or closing sounds on the door
//
if ( Has_Frame_Occured( prev_frame, curr_frame, AnimManager.Peek_Animation()->Get_Num_Frames()-1 ) ) {
Play_Effect( EFFECT_DOOR_CLOSING, false );
} else if ( Has_Frame_Occured( prev_frame, curr_frame, def->DoorClosedTop_FrameNum ) ) {
Play_Effect( EFFECT_DOOR_OPENING, true );
}
//
// Check to see if we should start or stop the elevator moving sound
//
if ( Has_Frame_Occured( prev_frame, curr_frame, def->ElevatorStoppedBottom_FrameNum ) ) {
Play_Effect( EFFECT_ELEVATOR_MOVING, false );
} else if ( Has_Frame_Occured( prev_frame, curr_frame, def->ElevatorStartTop_FrameNum ) ) {
Play_Effect( EFFECT_ELEVATOR_STOPPED_MOVING, true );
}
}
PrevFrame = curr_frame;
return ;
}
void ElevatorPhysClass::Play_Effect( int effect_id, bool is_top )
{
const ElevatorPhysDefClass *def = Get_ElevatorPhysDef();
switch ( effect_id )
{
case EFFECT_DOOR_OPENING:
{
Matrix3D tm(1);
Get_Door_Transform( is_top, tm );
WWAudioClass::Get_Instance()->Create_Instant_Sound( def->DoorOpenSoundDefID, tm );
}
break;
case EFFECT_DOOR_CLOSING:
{
Matrix3D tm(1);
Get_Door_Transform( is_top, tm );
WWAudioClass::Get_Instance()->Create_Instant_Sound( def->DoorCloseSoundDefID, tm );
}
break;
case EFFECT_DOOR_UNLOCKED:
{
Matrix3D tm(1);
Get_Door_Transform( is_top, tm );
WWAudioClass::Get_Instance()->Create_Instant_Sound( def->DoorUnlockSoundDefID, tm );
}
break;
case EFFECT_ACCESS_DENIED:
{
Matrix3D tm(1);
Get_Door_Transform( is_top, tm );
WWAudioClass::Get_Instance()->Create_Instant_Sound( def->DoorAccessDeniedSoundDefID, tm );
}
break;
case EFFECT_ELEVATOR_MOVING:
{
if ( def->ElevatorMovingSoundDefID != 0 ) {
RenderObjClass *model = Peek_Model ();
if (model != NULL) {
//
// Lookup the bone we need to attach this sound to
//
int bone_index = model->Get_Bone_Index( "ELESOUND" );
if ( bone_index > 0) {
//
// Create the sound object (if necessary)
//
if ( MovingSoundObj == NULL ) {
MovingSoundObj = WWAudioClass::Get_Instance ()->Create_Continuous_Sound( def->ElevatorMovingSoundDefID );
}
//
// Attach the sound to the elevator car and insert it into the scene
//
if ( MovingSoundObj != NULL ) {
MovingSoundObj->Attach_To_Object( model, bone_index );
MovingSoundObj->Add_To_Scene( true );
}
}
}
}
}
break;
case EFFECT_ELEVATOR_STOPPED_MOVING:
{
if ( MovingSoundObj != NULL ) {
MovingSoundObj->Stop();
MovingSoundObj->Remove_From_Scene();
}
}
break;
}
return ;
}
void ElevatorPhysClass::Get_Door_Transform( bool is_top, Matrix3D &tm )
{
const ElevatorPhysDefClass *def = Get_ElevatorPhysDef();
tm.Make_Identity();
Matrix3D curr_tm = Get_Transform();
//
// We assume the door is 'about' halfway between the call and inside zones.
//
if ( is_top ) {
Vector3 zone_vector = def->CallZones[ZONE_UPPER_CALL].Center - def->CallZones[ZONE_UPPER_INSIDE].Center;
Vector3 center_point = def->CallZones[ZONE_UPPER_INSIDE].Center + (zone_vector * 0.5F);
tm.Set_Translation( center_point + curr_tm.Get_Translation () );
} else {
Vector3 zone_vector = def->CallZones[ZONE_LOWER_CALL].Center - def->CallZones[ZONE_LOWER_INSIDE].Center;
Vector3 center_point = def->CallZones[ZONE_LOWER_INSIDE].Center + (zone_vector * 0.5F);
tm.Set_Translation( center_point + curr_tm.Get_Translation () );
}
return ;
}
void ElevatorPhysClass::Set_Precision(void)
{
cEncoderList::Set_Precision( BITPACK_ELEVATOR_STATE, 0, (int)ElevatorPhysClass::STATE_MAX );
cEncoderList::Set_Precision( BITPACK_ELEVATOR_TOP_DOOR_STATE, 0, (int)ElevatorPhysClass::DOOR_STATE_MAX );
cEncoderList::Set_Precision( BITPACK_ELEVATOR_BOTTOM_DOOR_STATE, 0, (int)ElevatorPhysClass::DOOR_STATE_MAX );
return ;
}
bool ElevatorPhysClass::Can_Object_Enter (SmartGameObj *game_obj)
{
bool retval = false;
WWASSERT( game_obj != NULL && game_obj->Peek_Physical_Object () != NULL );
if( game_obj == NULL || game_obj->Peek_Physical_Object () == NULL ) {
return false;
}
//
// Don't let other objects on if we've already got an AI rider
//
if (CurrentAIRider != NULL) {
return false;
}
//
// Get the object's bounding box
//
RenderObjClass *model = game_obj->Peek_Physical_Object()->Peek_Model ();
const AABoxClass &obj_box = model->Get_Bounding_Box ();
//
// Get the bottom and top call zones
//
const ElevatorPhysDefClass *def = Get_ElevatorPhysDef();
OBBoxClass bottom_box;
OBBoxClass top_box;
OBBoxClass::Transform( Get_Transform(), def->CallZones[STATE_DOWN], &bottom_box );
OBBoxClass::Transform( Get_Transform(), def->CallZones[STATE_UP], &top_box );
//
// Which floor is the object waiting on?
//
if( CollisionMath::Intersection_Test( bottom_box, obj_box ) ) {
retval = (State == STATE_DOWN);
} else if( CollisionMath::Intersection_Test( top_box, obj_box ) ) {
retval = (State == STATE_UP);
}
return retval;
}
bool ElevatorPhysClass::Can_Object_Exit (SmartGameObj * /*game_obj*/)
{
bool retval = false;
//
// If the elevator is at rest, then the object can exit
//
if (State == STATE_DOWN || State == STATE_UP) {
retval = true;
}
return retval;
}
void ElevatorPhysClass::Request_Elevator (SmartGameObj *game_obj)
{
WWASSERT( game_obj != NULL && game_obj->Peek_Physical_Object () != NULL );
if( game_obj == NULL || game_obj->Peek_Physical_Object () == NULL ) {
return ;
}
//
// Get the object's bounding box
//
RenderObjClass *model = game_obj->Peek_Physical_Object()->Peek_Model ();
const AABoxClass &obj_box = model->Get_Bounding_Box ();
//
// Get the bottom and top call zones
//
const ElevatorPhysDefClass *def = Get_ElevatorPhysDef();
//
// See what trigger zone the object is in...
//
for ( int index = 0; index < ZONE_MAX; index ++) {
//
// Transform the trigger zone into world space
//
OBBoxClass world_box;
OBBoxClass::Transform( Get_Transform(), def->CallZones[index], &world_box );
//
// Is this the trigger zone the object is standing in?
//
if( CollisionMath::Intersection_Test( world_box, obj_box ) ) {
TriggerRequest |= (1 << index);
}
}
return ;
}
int ElevatorPhysClass::Get_Floor (void)
{
int floor = 13;
//
// Return 0 if the elevator is currently sitting on the bottom floor
// or return 1 if the elevator is sitting at the top floor
//
if (State == STATE_DOWN) {
floor = 0;
} else if (State == STATE_UP) {
floor = 1;
}
return floor;
}