scummvm/engines/hugo/schedule.cpp
2010-10-25 19:59:32 +00:00

260 lines
9.0 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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.
*
* $URL$
* $Id$
*
*/
/*
* This code is based on original Hugo Trilogy source code
*
* Copyright (c) 1989-1995 David P. Gray
*
*/
// This module contains all the scheduling and timing stuff
#include "common/system.h"
#include "hugo/hugo.h"
#include "hugo/schedule.h"
#include "hugo/file.h"
#include "hugo/display.h"
#include "hugo/util.h"
namespace Hugo {
Scheduler::Scheduler(HugoEngine *vm) : _vm(vm) {
}
Scheduler::~Scheduler() {
}
// Initialise the timer event queue
void Scheduler::initEventQueue() {
debugC(1, kDebugSchedule, "initEventQueue");
// Chain next_p from first to last
for (int i = kMaxEvents; --i;)
_events[i - 1].nextEvent = &_events[i];
_events[kMaxEvents - 1].nextEvent = 0;
// Chain prev_p from last to first
for (int i = 1; i < kMaxEvents; i++)
_events[i].prevEvent = &_events[i - 1];
_events[0].prevEvent = 0;
_headEvent = _tailEvent = 0; // Event list is empty
_freeEvent = _events; // Free list is full
}
// Return a ptr to an event structure from the free list
event_t *Scheduler::getQueue() {
debugC(4, kDebugSchedule, "getQueue");
if (!_freeEvent) // Error: no more events available
Utils::Error(EVNT_ERR, "%s", "getQueue");
event_t *resEvent = _freeEvent;
_freeEvent = _freeEvent->nextEvent;
resEvent->nextEvent = 0;
return resEvent;
}
void Scheduler::insertActionList(uint16 actIndex) {
// Call Insert_action for each action in the list supplied
debugC(1, kDebugSchedule, "insertActionList(%d)", actIndex);
if (_vm->_actListArr[actIndex]) {
for (int i = 0; _vm->_actListArr[actIndex][i].a0.actType != ANULL; i++)
insertAction(&_vm->_actListArr[actIndex][i]);
}
}
void Scheduler::decodeString(char *line) {
// Decode a string
debugC(1, kDebugSchedule, "decodeString(%s)", line);
static const char *cypher = getCypher();
for (uint16 i = 0; i < strlen(line); i++)
line[i] -= cypher[i % strlen(cypher)];
debugC(1, kDebugSchedule, "result : %s", line);
}
// This is the scheduler which runs every tick. It examines the event queue
// for any events whose time has come. It dequeues these events and performs
// the action associated with the event, returning it to the free queue
void Scheduler::runScheduler() {
debugC(6, kDebugSchedule, "runScheduler");
status_t &gameStatus = _vm->getGameStatus();
event_t *curEvent = _headEvent; // The earliest event
while (curEvent && curEvent->time <= gameStatus.tick) // While mature events found
curEvent = doAction(curEvent); // Perform the action (returns next_p)
gameStatus.tick++; // Accessed elsewhere via getTicks()
}
uint32 Scheduler::getTicks() {
// Return system time in ticks. A tick is 1/TICKS_PER_SEC mS
debugC(3, kDebugSchedule, "getTicks");
return _vm->getGameStatus().tick;
}
void Scheduler::processBonus(int bonusIndex) {
// Add indecated bonus to score if not added already
debugC(1, kDebugSchedule, "processBonus(%d)", bonusIndex);
if (!_vm->_points[bonusIndex].scoredFl) {
_vm->adjustScore(_vm->_points[bonusIndex].score);
_vm->_points[bonusIndex].scoredFl = true;
}
}
// Transition to a new screen as follows:
// 1. Clear out all non-global events from event list.
// 2. Set the new screen (in the hero object and any carried objects)
// 3. Read in the screen files for the new screen
// 4. Schedule action list for new screen
// 5. Initialise prompt line and status line
void Scheduler::newScreen(int screenIndex) {
debugC(1, kDebugSchedule, "newScreen(%d)", screenIndex);
// Make sure the background file exists!
if (!_vm->isPacked()) {
char line[32];
if (!_vm->_file->fileExists(strcat(strncat(strcpy(line, _vm->_picDir), _vm->_screenNames[screenIndex], NAME_LEN), BKGEXT)) &&
!_vm->_file->fileExists(strcat(strcpy(line, _vm->_screenNames[screenIndex]), ".ART"))) {
Utils::Box(BOX_ANY, "%s", _vm->_textSchedule[kSsNoBackground]);
return;
}
}
// 1. Clear out all local events
event_t *curEvent = _headEvent; // The earliest event
event_t *wrkEvent; // Event ptr
while (curEvent) { // While mature events found
wrkEvent = curEvent->nextEvent; // Save p (becomes undefined after Del)
if (curEvent->localActionFl)
delQueue(curEvent); // Return event to free list
curEvent = wrkEvent;
}
// 2. Set the new screen in the hero object and any being carried
_vm->setNewScreen(screenIndex);
// 3. Read in new screen files
_vm->readScreenFiles(screenIndex);
// 4. Schedule action list for this screen
_vm->screenActions(screenIndex);
// 5. Initialise prompt line and status line
_vm->_screen->initNewScreenDisplay();
}
// Write the event queue to the file with handle f
// Note that we convert all the event structure ptrs to indexes
// using -1 for NULL. We can't convert the action ptrs to indexes
// so we save address of first dummy action ptr to compare on restore.
void Scheduler::saveEvents(Common::WriteStream *f) {
debugC(1, kDebugSchedule, "saveEvents()");
uint32 curTime = getTicks();
event_t saveEventArr[kMaxEvents]; // Convert event ptrs to indexes
// Convert event ptrs to indexes
for (int16 i = 0; i < kMaxEvents; i++) {
event_t *wrkEvent = &_events[i];
saveEventArr[i] = *wrkEvent;
saveEventArr[i].prevEvent = (wrkEvent->prevEvent == 0) ? (event_t *) - 1 : (event_t *)(wrkEvent->prevEvent - _events);
saveEventArr[i].nextEvent = (wrkEvent->nextEvent == 0) ? (event_t *) - 1 : (event_t *)(wrkEvent->nextEvent - _events);
}
int16 freeIndex = (_freeEvent == 0) ? -1 : _freeEvent - _events;
int16 headIndex = (_headEvent == 0) ? -1 : _headEvent - _events;
int16 tailIndex = (_tailEvent == 0) ? -1 : _tailEvent - _events;
f->write(&curTime, sizeof(curTime));
f->write(&freeIndex, sizeof(freeIndex));
f->write(&headIndex, sizeof(headIndex));
f->write(&tailIndex, sizeof(tailIndex));
f->write(saveEventArr, sizeof(saveEventArr));
}
// Restore the event list from file with handle f
void Scheduler::restoreEvents(Common::SeekableReadStream *f) {
debugC(1, kDebugSchedule, "restoreEvents");
uint32 saveTime;
int16 freeIndex; // Free list index
int16 headIndex; // Head of list index
int16 tailIndex; // Tail of list index
event_t savedEvents[kMaxEvents]; // Convert event ptrs to indexes
f->read(&saveTime, sizeof(saveTime)); // time of save
f->read(&freeIndex, sizeof(freeIndex));
f->read(&headIndex, sizeof(headIndex));
f->read(&tailIndex, sizeof(tailIndex));
f->read(savedEvents, sizeof(savedEvents));
event_t *wrkEvent;
// Restore events indexes to pointers
for (int i = 0; i < kMaxEvents; i++) {
wrkEvent = &savedEvents[i];
_events[i] = *wrkEvent;
_events[i].prevEvent = (wrkEvent->prevEvent == (event_t *) - 1) ? (event_t *)0 : &_events[(size_t)wrkEvent->prevEvent ];
_events[i].nextEvent = (wrkEvent->nextEvent == (event_t *) - 1) ? (event_t *)0 : &_events[(size_t)wrkEvent->nextEvent ];
}
_freeEvent = (freeIndex == -1) ? 0 : &_events[freeIndex];
_headEvent = (headIndex == -1) ? 0 : &_events[headIndex];
_tailEvent = (tailIndex == -1) ? 0 : &_events[tailIndex];
// Adjust times to fit our time
uint32 curTime = getTicks();
wrkEvent = _headEvent; // The earliest event
while (wrkEvent) { // While mature events found
wrkEvent->time = wrkEvent->time - saveTime + curTime;
wrkEvent = wrkEvent->nextEvent;
}
}
void Scheduler::restoreScreen(int screenIndex) {
// Transition to a new screen as follows:
// 1. Set the new screen (in the hero object and any carried objects)
// 2. Read in the screen files for the new screen
// 3. Initialise prompt line and status line
debugC(1, kDebugSchedule, "restoreScreen(%d)", screenIndex);
// 1. Set the new screen in the hero object and any being carried
_vm->setNewScreen(screenIndex);
// 2. Read in new screen files
_vm->readScreenFiles(screenIndex);
// 3. Initialise prompt line and status line
_vm->_screen->initNewScreenDisplay();
}
} // End of namespace Hugo