scummvm/engines/icb/event_manager.cpp
2021-03-13 15:46:30 +01:00

299 lines
11 KiB
C++

/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* file distributed with this source distribution.
*
* Additional copyright for this file:
* Copyright (C) 1999-2000 Revolution Software Ltd.
* This code is based on source code created by Revolution Software,
* used with permission.
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "engines/icb/event_manager.h"
#include "engines/icb/global_objects.h"
#include "engines/icb/mission.h"
#include "common/stream.h"
namespace ICB {
bool8 _event_manager::HasEventPending(int32 nObjectID) {
// First check the ID is in range and the object is running.
if ((nObjectID < 0) || ((uint32)nObjectID >= m_nNumObjects))
Fatal_error("Object ID %d out of range in _event_manager::HasEventPending()", nObjectID);
// If the object is not running, simply return false.
if (!m_pbRunning[nObjectID] || m_pbSuspended[nObjectID])
return (FALSE8);
// Return the flag for the object.
return (m_pEventLists[nObjectID].HasEventPending());
}
bool8 _event_manager::CheckEventWaitingForObject(int32 nObjectID, const char *pcEventName) {
// First check the ID is in range and the object is running.
if ((nObjectID < 0) || ((uint32)nObjectID >= m_nNumObjects))
Fatal_error("Object ID %d out of range in _event_manager::CheckEventWaitingForObject()", nObjectID);
// If the object is not running, simply return false.
if (!m_pbRunning[nObjectID] || m_pbSuspended[nObjectID])
return (FALSE8);
// Check the object's event list.
return (m_pEventLists[nObjectID].CheckEventWaiting(pcEventName));
}
void _event_manager::ShutDownEventProcessingForObject(int32 nObjectID) {
// First check the ID is in range and the object is running.
if ((nObjectID < 0) || ((uint32)nObjectID >= m_nNumObjects))
Fatal_error("Object ID %d out of range in _event_manager::ShutDownEventProcessingForObject()", nObjectID);
// Mark it shut down.
m_pbRunning[nObjectID] = FALSE8;
}
void _event_manager::RegisterForEvent(int32 nObjectID, const char *pcEventName) {
// First check the ID is in range and the object is running.
if ((nObjectID < 0) || ((uint32)nObjectID >= m_nNumObjects))
Fatal_error("Object ID %d out of range in _event_manager::RegisterForEvent()", nObjectID);
// Add the event for the object.
if (m_pbRunning[nObjectID])
m_pEventLists[nObjectID].AddEventForObject(pcEventName);
}
void _event_manager::UnregisterForEvent(int32 nObjectID, const char *pcEventName) {
// First check the ID is in range and the object is running.
if ((nObjectID < 0) || ((uint32)nObjectID >= m_nNumObjects))
Fatal_error("Object ID %d out of range in _event_manager::UnregisterForEvent()", nObjectID);
// Make sure the object is running.
if (m_pbRunning[nObjectID]) {
// Remove event for the object.
m_pEventLists[nObjectID].RemoveEventForObject(pcEventName);
// This is a useful opportunity to do some cleaning up. Having worked with the implementers, it
// seems like desirable behaviour that all events get cleared when you unregister an object for one.
m_pEventLists[nObjectID].ClearAllOutstandingEvents();
}
}
void _event_manager::ClearAllEventsForObject(int32 nObjectID) {
// First check the ID is in range and the object is running.
if ((nObjectID < 0) || ((uint32)nObjectID >= m_nNumObjects))
Fatal_error("Object ID %d out of range in _event_manager::ClearAllEventsForObject()", nObjectID);
// Clear the object's events.
if (m_pbRunning[nObjectID])
m_pEventLists[nObjectID].ClearAllOutstandingEvents();
}
void _event_manager::SetSuspendFlagForObject(int32 nObjectID, bool8 bState) {
// First check the ID is in range and the object is running.
if ((nObjectID < 0) || ((uint32)nObjectID >= m_nNumObjects))
Fatal_error("Object ID %d out of range in _event_manager::SetSuspendFlagForObject()", nObjectID);
// Flag the object suspended or not.
m_pbSuspended[nObjectID] = bState;
}
bool8 _event_manager::DidObjectSendLastNamedEvent(int32 nCallerID, int32 nObjectID, const char *pcEventName) const {
if ((nCallerID < 0) || ((uint32)nCallerID >= m_nNumObjects))
Fatal_error("Caller ID %d out of range in _event_manager::DidObjectSendLastEvent()", nCallerID);
if ((nObjectID < 0) || ((uint32)nObjectID >= m_nNumObjects))
Fatal_error("Object ID %d out of range in _event_manager::DidObjectSendLastEvent()", nObjectID);
// Get the ID of last poster of named event.
if (!m_pbRunning[nCallerID] || m_pbSuspended[nCallerID])
return (FALSE8);
else
return (m_pEventLists[nCallerID].DidObjectSendLastNamedEvent(nObjectID, pcEventName));
}
int32 _event_manager::GetIDOfLastObjectToPostEvent(int32 nCallerID, const char *pcEventName) const {
if ((nCallerID < 0) || ((uint32)nCallerID >= m_nNumObjects))
Fatal_error("Caller ID %d out of range in _event_manager::GetIDOfLastObjectToPostEvent()", nCallerID);
// Get the ID of last poster of named event.
if (!m_pbRunning[nCallerID] || m_pbSuspended[nCallerID])
return (FALSE8);
else
return (m_pEventLists[nCallerID].GetIDOfLastObjectToPostEvent(pcEventName));
}
void _event_manager::PostNamedEventToObject(const char *pcEventName, int32 nTargetID, int32 nSenderID) {
if ((nSenderID < 0) || ((uint32)nSenderID >= m_nNumObjects))
Fatal_error("Sender ID %d out of range in _event_manager::PostNamedEventToObject()", nSenderID);
if ((nTargetID < 0) || ((uint32)nTargetID >= m_nNumObjects))
Fatal_error("Target ID %d out of range in _event_manager::PostNamedEventToObject()", nTargetID);
// Post the named event for one object only.
if (m_pbRunning[nTargetID] || m_pbSuspended[nTargetID])
m_pEventLists[nTargetID].PostNamedEvent(pcEventName, nSenderID);
}
bool8 _event_manager::IsObjectRegisteredForEvent(int32 nCallerID, const char *pcEventName) {
if ((nCallerID < 0) || ((uint32)nCallerID >= m_nNumObjects))
Fatal_error("Sender ID %d out of range in _event_manager::IsObjectRegisteredForEvent()", nCallerID);
if (m_pbRunning[nCallerID])
return (m_pEventLists[nCallerID].IsEventInList(pcEventName));
else
return (FALSE8);
}
void _event_manager::Initialise() {
uint32 i;
// Initialize the line-of-sight module.
g_oLineOfSight->Initialise();
// Add an entry for each of the objects in this session.
m_nNumObjects = (uint8)MS->total_objects;
memset(m_pbRunning, 0, MAX_session_objects * sizeof(bool8));
memset(m_pbSuspended, 0, MAX_session_objects * sizeof(bool8));
for (i = 0; i < m_nNumObjects; ++i) {
// Create an event list for the object.
m_pEventLists[i].SetNewObjectName(MS->logic_structs[i]->GetName());
// Flag it running or not.
if (MS->logic_structs[i]->ob_status == OB_STATUS_HELD)
m_pbRunning[i] = FALSE8;
else
m_pbRunning[i] = TRUE8;
}
// To start with, no event timers are running.
for (i = 0; i < EVENT_MANAGER_MAX_TIMERS; ++i)
m_pbActiveTimers[i] = FALSE8;
}
void _event_manager::PostNamedEvent(const char *pcEventName, int32 nSenderID) {
uint32 i;
// Loop through each object in the session.
for (i = 0; i < m_nNumObjects; ++i) {
// Post the event only if the object is marked as running.
if (m_pbRunning[i] && !m_pbSuspended[i])
m_pEventLists[i].PostNamedEvent(pcEventName, nSenderID);
}
}
void _event_manager::CycleEventManager() {
uint32 i;
uint32 nElapsed;
// Loop for each timer.
for (i = 0; i < EVENT_MANAGER_MAX_TIMERS; ++i) {
// Watch out for unused slots.
if (m_pbActiveTimers[i]) {
// Process the timer.
m_pEventTimers[i].SetEventTime(m_pEventTimers[i].GetEventTime() + 1);
// If it has gone past the end then it has expired and so needs removing.
if (m_pEventTimers[i].GetEventTime() > m_pEventTimers[i].GetEnd()) {
// Delete this expired timer.
m_pbActiveTimers[i] = FALSE8;
} else {
// Timer has not expired but might not even have started.
if (m_pEventTimers[i].GetEventTime() >= m_pEventTimers[i].GetEnd()) {
// Yes, it has started and has not expired. So check if it matches some multiple of
// the interval.
nElapsed = m_pEventTimers[i].GetEventTime() - m_pEventTimers[i].GetStart();
if ((m_pEventTimers[i].GetInterval() == 0) || ((nElapsed % m_pEventTimers[i].GetInterval()) == 0)) {
// We need to generate an event.
PostNamedEvent(m_pEventTimers[i].GetEventName(), m_pEventTimers[i].GetObjectID());
}
}
}
}
}
}
void _event_manager::PostRepeatingEvent(const char *pcEventName, uint32 nStart, uint32 nInterval, uint32 nEnd) {
uint32 i;
// Look for a vacant slot.
i = 0;
while ((i < EVENT_MANAGER_MAX_TIMERS) && m_pbActiveTimers[i])
++i;
// If we ran off the end, we failed to find a spare slot.
if (i == EVENT_MANAGER_MAX_TIMERS)
Fatal_error("Run out of event timer slots in _event_manager::PostRepeatingEvent()");
// Set up the timer.
m_pEventTimers[i].SetEventName(pcEventName);
m_pEventTimers[i].SetStart(nStart);
m_pEventTimers[i].SetEnd(nEnd);
m_pEventTimers[i].SetInterval(nInterval);
m_pEventTimers[i].SetEventTime(0);
// Flag it as running.
m_pbActiveTimers[i] = TRUE8;
}
void _event_manager::Save(Common::WriteStream *stream) const {
uint32 i;
uint32 nNumActiveTimers = 0;
// First, work out how many timers we are going to write.
for (i = 0; i < EVENT_MANAGER_MAX_TIMERS; ++i) {
if (m_pbActiveTimers[i])
++nNumActiveTimers;
}
// Write the number of active event timers in the file.
stream->writeUint32LE(nNumActiveTimers);
// Now write the actual active event timers.
for (i = 0; i < EVENT_MANAGER_MAX_TIMERS; ++i) {
if (m_pbActiveTimers[i])
m_pEventTimers[i].Save(stream);
}
}
void _event_manager::Restore(Common::SeekableReadStream *stream) {
uint32 i;
uint32 nNumActiveTimers;
_event_timer oEventTimer;
// Make sure all timers are currently deactivated. This is the same as overwriting them.
for (i = 0; i < EVENT_MANAGER_MAX_TIMERS; ++i)
m_pbActiveTimers[i] = FALSE8;
// Find out how many timers we are going to read in.
nNumActiveTimers = stream->readUint32LE();
// Read them in.
for (i = 0; i < nNumActiveTimers; ++i) {
oEventTimer.Restore(stream);
m_pEventTimers[i] = oEventTimer;
m_pbActiveTimers[i] = TRUE8;
}
if (stream->err())
Fatal_error("Error restoring event timers from save file");
}
} // End of namespace ICB