open_toontown_panda3d/direct/src/interval/cIntervalManager.cxx

498 lines
17 KiB
C++

// Filename: cIntervalManager.cxx
// Created by: drose (10Sep02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "cIntervalManager.h"
#include "cMetaInterval.h"
#include "dcast.h"
#include "eventQueue.h"
#include "mutexHolder.h"
CIntervalManager *CIntervalManager::_global_ptr;
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::Constructor
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
CIntervalManager::
CIntervalManager() {
_first_slot = 0;
_next_event_index = 0;
_event_queue = EventQueue::get_global_event_queue();
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::Destructor
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
CIntervalManager::
~CIntervalManager() {
nassertv(_name_index.empty());
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::add_c_interval
// Access: Published
// Description: Adds the interval to the manager, and returns a
// unique index for the interval. This index will be
// unique among all the currently added intervals, but
// not unique across all intervals ever added to the
// manager. The maximum index value will never exceed
// the maximum number of intervals added at any given
// time.
//
// If the external flag is true, the interval is
// understood to also be stored in the scripting
// language data structures. In this case, it will be
// available for information returned by
// get_next_event() and get_next_removal(). If external
// is false, the interval's index will never be returned
// by these two functions.
////////////////////////////////////////////////////////////////////
int CIntervalManager::
add_c_interval(CInterval *interval, bool external) {
MutexHolder holder(_lock);
// First, check the name index. If we already have an interval by
// this name, it gets finished and removed.
NameIndex::iterator ni = _name_index.find(interval->get_name());
if (ni != _name_index.end()) {
int old_index = (*ni).second;
nassertr(old_index >= 0 && old_index < (int)_intervals.size(), -1)
CInterval *old_interval = _intervals[old_index]._interval;
if (old_interval == interval) {
// No, it's the same interval that was already here. In this
// case, don't finish the interval; just return it.
return old_index;
}
finish_interval(old_interval);
remove_index(old_index);
_name_index.erase(ni);
}
int slot;
if (_first_slot >= (int)_intervals.size()) {
// All the slots are filled; make a new slot.
nassertr(_first_slot == (int)_intervals.size(), -1);
slot = (int)_intervals.size();
_intervals.push_back(IntervalDef());
_first_slot = (int)_intervals.size();
} else {
// Some slot is available; use it.
slot = _first_slot;
nassertr(slot >= 0 && slot < (int)_intervals.size(), -1);
_first_slot = _intervals[slot]._next_slot;
}
IntervalDef &def = _intervals[slot];
def._interval = interval;
def._flags = 0;
if (external) {
def._flags |= F_external;
}
if (interval->is_of_type(CMetaInterval::get_class_type())) {
def._flags |= F_meta_interval;
}
def._next_slot = -1;
_name_index[interval->get_name()] = slot;
nassertr(_first_slot >= 0, slot);
return slot;
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::find_c_interval
// Access: Published
// Description: Returns the index associated with the named interval,
// if there is such an interval, or -1 if there is not.
////////////////////////////////////////////////////////////////////
int CIntervalManager::
find_c_interval(const string &name) const {
MutexHolder holder(_lock);
NameIndex::const_iterator ni = _name_index.find(name);
if (ni != _name_index.end()) {
return (*ni).second;
}
return -1;
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::get_c_interval
// Access: Published
// Description: Returns the interval associated with the given index.
////////////////////////////////////////////////////////////////////
CInterval *CIntervalManager::
get_c_interval(int index) const {
MutexHolder holder(_lock);
nassertr(index >= 0 && index < (int)_intervals.size(), NULL);
return _intervals[index]._interval;
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::remove_c_interval
// Access: Published
// Description: Removes the indicated interval from the queue
// immediately. It will not be returned from
// get_next_removal(), and none of its pending events,
// if any, will be returned by get_next_event().
////////////////////////////////////////////////////////////////////
void CIntervalManager::
remove_c_interval(int index) {
MutexHolder holder(_lock);
nassertv(index >= 0 && index < (int)_intervals.size());
IntervalDef &def = _intervals[index];
nassertv(def._interval != (CInterval *)NULL);
NameIndex::iterator ni = _name_index.find(def._interval->get_name());
nassertv(ni != _name_index.end());
nassertv((*ni).second == index);
_name_index.erase(ni);
def._interval = (CInterval *)NULL;
def._next_slot = _first_slot;
_first_slot = index;
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::interrupt
// Access: Published
// Description: Pauses or finishes (removes from the active queue)
// all intervals tagged with auto_pause or auto_finish
// set to true. These are intervals that someone fired
// up but won't necessarily expect to clean up; they can
// be interrupted at will when necessary.
//
// Returns the number of intervals affected.
////////////////////////////////////////////////////////////////////
int CIntervalManager::
interrupt() {
MutexHolder holder(_lock);
int num_paused = 0;
NameIndex::iterator ni;
ni = _name_index.begin();
while (ni != _name_index.end()) {
int index = (*ni).second;
const IntervalDef &def = _intervals[index];
nassertr(def._interval != (CInterval *)NULL, num_paused);
if (def._interval->get_auto_pause() || def._interval->get_auto_finish()) {
// This interval may be interrupted.
if (def._interval->get_auto_pause()) {
// It may be interrupted simply by pausing it.
if (interval_cat.is_debug()) {
interval_cat.debug()
<< "Auto-pausing " << def._interval->get_name() << "\n";
}
if (def._interval->get_state() == CInterval::S_started) {
def._interval->priv_interrupt();
}
} else {
// It should be interrupted by finishing it.
if (interval_cat.is_debug()) {
interval_cat.debug()
<< "Auto-finishing " << def._interval->get_name() << "\n";
}
switch (def._interval->get_state()) {
case CInterval::S_initial:
def._interval->priv_instant();
break;
case CInterval::S_final:
break;
default:
def._interval->priv_finalize();
}
}
// Now carefully remove it from the active list.
NameIndex::iterator prev;
prev = ni;
++ni;
_name_index.erase(prev);
remove_index(index);
num_paused++;
} else {
// The interval should remain on the active list.
++ni;
}
}
return num_paused;
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::get_num_intervals
// Access: Published
// Description: Returns the number of currently active intervals.
////////////////////////////////////////////////////////////////////
int CIntervalManager::
get_num_intervals() const {
MutexHolder holder(_lock);
return _name_index.size();
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::get_max_index
// Access: Published
// Description: Returns one more than the largest interval index
// number in the manager. If you walk through all the
// values between (0, get_max_index()] and call
// get_c_interval() on each number, you will retrieve
// all of the managed intervals (and possibly a number
// of NULL pointers as well).
////////////////////////////////////////////////////////////////////
int CIntervalManager::
get_max_index() const {
MutexHolder holder(_lock);
return _intervals.size();
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::step
// Access: Published
// Description: This should be called every frame to do the
// processing for all the active intervals. It will
// call step_play() for each interval that has been
// added and that has not yet been removed.
//
// After each call to step(), the scripting language
// should call get_next_event() and get_next_removal()
// repeatedly to process all the high-level
// (e.g. Python-interval-based) events and to manage the
// high-level list of intervals.
////////////////////////////////////////////////////////////////////
void CIntervalManager::
step() {
MutexHolder holder(_lock);
NameIndex::iterator ni;
ni = _name_index.begin();
while (ni != _name_index.end()) {
int index = (*ni).second;
const IntervalDef &def = _intervals[index];
nassertv(def._interval != (CInterval *)NULL);
if (!def._interval->step_play()) {
// This interval is finished and wants to be removed from the
// active list.
NameIndex::iterator prev;
prev = ni;
++ni;
_name_index.erase(prev);
remove_index(index);
} else {
// The interval can remain on the active list.
++ni;
}
}
_next_event_index = 0;
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::get_next_event
// Access: Published
// Description: This should be called by the scripting language after
// each call to step(). It returns the index number of
// the next interval that has events requiring servicing
// by the scripting language, or -1 if no more intervals
// have any events pending.
//
// If this function returns something other than -1, it
// is the scripting language's responsibility to query
// the indicated interval for its next event via
// get_event_index(), and eventually pop_event().
//
// Then get_next_event() should be called again until it
// returns -1.
////////////////////////////////////////////////////////////////////
int CIntervalManager::
get_next_event() {
MutexHolder holder(_lock);
while (_next_event_index < (int)_intervals.size()) {
IntervalDef &def = _intervals[_next_event_index];
if (def._interval != (CInterval *)NULL) {
if ((def._flags & F_external) != 0 &&
def._interval->check_t_callback()) {
return _next_event_index;
}
if ((def._flags & F_meta_interval) != 0) {
CMetaInterval *meta_interval;
DCAST_INTO_R(meta_interval, def._interval, -1);
if (meta_interval->is_event_ready()) {
nassertr((def._flags & F_external) != 0, -1);
return _next_event_index;
}
}
}
_next_event_index++;
}
return -1;
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::get_next_removal
// Access: Published
// Description: This should be called by the scripting language after
// each call to step(). It returns the index number of
// an interval that was recently removed, or -1 if no
// intervals were removed.
//
// If this returns something other than -1, the
// scripting language should clean up its own data
// structures accordingly, and then call
// get_next_removal() again.
////////////////////////////////////////////////////////////////////
int CIntervalManager::
get_next_removal() {
MutexHolder holder(_lock);
if (!_removed.empty()) {
int index = _removed.back();
_removed.pop_back();
nassertr(index >= 0 && index < (int)_intervals.size(), -1);
IntervalDef &def = _intervals[index];
def._interval = (CInterval *)NULL;
def._next_slot = _first_slot;
_first_slot = index;
return index;
}
return -1;
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::output
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void CIntervalManager::
output(ostream &out) const {
MutexHolder holder(_lock);
out << "CIntervalManager, " << (int)_name_index.size() << " intervals.";
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::write
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void CIntervalManager::
write(ostream &out) const {
MutexHolder holder(_lock);
// We need to write this line so that it's clear what's going on
// when there are no intervals in the list.
out << (int)_name_index.size() << " intervals.\n";
NameIndex::const_iterator ni;
for (ni = _name_index.begin(); ni != _name_index.end(); ++ni) {
int index = (*ni).second;
nassertv(index >= 0 && index < (int)_intervals.size());
const IntervalDef &def = _intervals[index];
nassertv(def._interval != (CInterval *)NULL);
out << *def._interval << "\n";
}
if (!_removed.empty()) {
out << "\nRemoved:\n";
Removed::const_iterator ri;
for (ri = _removed.begin(); ri != _removed.end(); ++ri) {
int index = (*ri);
nassertv(index >= 0 && index < (int)_intervals.size());
const IntervalDef &def = _intervals[index];
nassertv(def._interval != (CInterval *)NULL);
out << "(R)" << *def._interval << "\n";
}
}
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::get_global_ptr
// Access: Published, Static
// Description: Returns the pointer to the one global
// CIntervalManager object.
////////////////////////////////////////////////////////////////////
CIntervalManager *CIntervalManager::
get_global_ptr() {
if (_global_ptr == (CIntervalManager *)NULL) {
_global_ptr = new CIntervalManager;
}
return _global_ptr;
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::finish_interval
// Access: Private
// Description: Explicitly finishes the indicated interval in
// preparation for moving it to the removed queue.
////////////////////////////////////////////////////////////////////
void CIntervalManager::
finish_interval(CInterval *interval) {
switch (interval->get_state()) {
case CInterval::S_initial:
interval->priv_instant();
break;
case CInterval::S_final:
break;
default:
interval->priv_finalize();
}
}
////////////////////////////////////////////////////////////////////
// Function: CIntervalManager::remove_index
// Access: Private
// Description: Removes the indicated index number from the active
// list, either by moving it to the removed queue if it
// is flagged external, or by simply making the slot
// available again if it is not. Assumes the lock is
// already held.
////////////////////////////////////////////////////////////////////
void CIntervalManager::
remove_index(int index) {
nassertv(_lock.debug_is_locked());
nassertv(index >= 0 && index < (int)_intervals.size());
IntervalDef &def = _intervals[index];
if ((def._flags & F_external) != 0) {
_removed.push_back(index);
} else {
def._interval = (CInterval *)NULL;
def._next_slot = _first_slot;
_first_slot = index;
}
}