/* ** 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 . */ /*********************************************************************************************** *** 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 _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 _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 *player_list = GameObjManager::Get_Star_Game_Obj_List(); // // Loop over each player // SLNode *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; }