mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-27 20:28:27 +00:00
a81b093e65
fixed load actors entrance removed some orphans svn-id: r17964
599 lines
14 KiB
C++
599 lines
14 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2004-2005 The ScummVM project
|
|
*
|
|
* The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
|
|
*
|
|
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* $Header$
|
|
*
|
|
*/
|
|
|
|
// Event management module
|
|
|
|
|
|
#include "saga/saga.h"
|
|
#include "saga/gfx.h"
|
|
|
|
|
|
#include "saga/animation.h"
|
|
#include "saga/console.h"
|
|
#include "saga/scene.h"
|
|
#include "saga/interface.h"
|
|
#include "saga/text.h"
|
|
#include "saga/palanim.h"
|
|
#include "saga/render.h"
|
|
#include "saga/sndres.h"
|
|
#include "saga/music.h"
|
|
#include "saga/actor.h"
|
|
|
|
#include "saga/events.h"
|
|
|
|
namespace Saga {
|
|
|
|
Events::Events(SagaEngine *vm) : _vm(vm), _initialized(false) {
|
|
debug(0, "Initializing event subsystem...");
|
|
_initialized = true;
|
|
}
|
|
|
|
Events::~Events(void) {
|
|
debug(0, "Shutting down event subsystem...");
|
|
freeList();
|
|
}
|
|
|
|
// Function to process event list once per frame.
|
|
// First advances event times, then processes each event with the appropriate
|
|
// handler depending on the type of event.
|
|
int Events::handleEvents(long msec) {
|
|
EVENT *event_p;
|
|
|
|
long delta_time;
|
|
int result;
|
|
|
|
// Advance event times
|
|
processEventTime(msec);
|
|
|
|
// Process each event in list
|
|
for (EventList::iterator eventi = _eventList.begin(); eventi != _eventList.end(); ++eventi) {
|
|
event_p = (EVENT *)eventi.operator->();
|
|
|
|
// Call the appropriate event handler for the specific event type
|
|
switch (event_p->type) {
|
|
|
|
case ONESHOT_EVENT:
|
|
result = handleOneShot(event_p);
|
|
break;
|
|
|
|
case CONTINUOUS_EVENT:
|
|
result = handleContinuous(event_p);
|
|
break;
|
|
|
|
case INTERVAL_EVENT:
|
|
result = handleInterval(event_p);
|
|
break;
|
|
|
|
case IMMEDIATE_EVENT:
|
|
result = handleImmediate(event_p);
|
|
break;
|
|
|
|
default:
|
|
result = EVENT_INVALIDCODE;
|
|
warning("Invalid event code encountered");
|
|
break;
|
|
}
|
|
|
|
// Process the event appropriately based on result code from
|
|
// handler
|
|
if ((result == EVENT_DELETE) || (result == EVENT_INVALIDCODE)) {
|
|
// If there is no event chain, delete the base event.
|
|
if (event_p->chain == NULL) {
|
|
eventi=_eventList.eraseAndPrev(eventi);
|
|
} else {
|
|
// If there is an event chain present, move the next event
|
|
// in the chain up, adjust it by the previous delta time,
|
|
// and reprocess the event */
|
|
delta_time = event_p->time;
|
|
EVENT *from_chain=event_p->chain;
|
|
memcpy(event_p, from_chain,sizeof(*event_p));
|
|
free(from_chain);
|
|
|
|
event_p->time += delta_time;
|
|
--eventi;
|
|
}
|
|
} else if (result == EVENT_BREAK) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
int Events::handleContinuous(EVENT *event) {
|
|
double event_pc = 0.0; // Event completion percentage
|
|
int event_done = 0;
|
|
|
|
BUFFER_INFO buf_info;
|
|
SCENE_BGINFO bg_info;
|
|
SURFACE *back_buf;
|
|
|
|
event_pc = ((double)event->duration - event->time) / event->duration;
|
|
|
|
if (event_pc >= 1.0) {
|
|
// Cap percentage to 100
|
|
event_pc = 1.0;
|
|
event_done = 1;
|
|
}
|
|
|
|
if (event_pc < 0.0) {
|
|
// Event not signaled, skip it
|
|
return EVENT_CONTINUE;
|
|
} else if (!(event->code & SIGNALED)) {
|
|
// Signal event
|
|
event->code |= SIGNALED;
|
|
event_pc = 0.0;
|
|
}
|
|
|
|
switch (event->code & EVENT_MASK) {
|
|
case PAL_EVENT:
|
|
switch (event->op) {
|
|
case EVENT_BLACKTOPAL:
|
|
back_buf = _vm->_gfx->getBackBuffer();
|
|
_vm->_gfx->blackToPal(back_buf, (PALENTRY *)event->data, event_pc);
|
|
break;
|
|
|
|
case EVENT_PALTOBLACK:
|
|
back_buf = _vm->_gfx->getBackBuffer();
|
|
_vm->_gfx->palToBlack(back_buf, (PALENTRY *)event->data, event_pc);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case TRANSITION_EVENT:
|
|
switch (event->op) {
|
|
case EVENT_DISSOLVE:
|
|
_vm->_render->getBufferInfo(&buf_info);
|
|
_vm->_scene->getBGInfo(&bg_info);
|
|
_vm->transitionDissolve(buf_info.bg_buf, buf_info.bg_buf_w,
|
|
buf_info.bg_buf_h, buf_info.bg_buf_w, bg_info.bg_buf, bg_info.bg_w,
|
|
bg_info.bg_h, bg_info.bg_p, 0, 0, 0, event_pc);
|
|
break;
|
|
case EVENT_DISSOLVE_BGMASK:
|
|
// we dissolve it centered.
|
|
// set flag of Dissolve to 1. It is a hack to simulate zero masking.
|
|
int w, h;
|
|
byte *mask_buf;
|
|
size_t len;
|
|
|
|
_vm->_render->getBufferInfo(&buf_info);
|
|
_vm->_scene->getBGMaskInfo(w, h, mask_buf, len);
|
|
_vm->transitionDissolve(buf_info.bg_buf, buf_info.bg_buf_w,
|
|
buf_info.bg_buf_h, buf_info.bg_buf_w, mask_buf, w, h, 0, 1,
|
|
(320 - w) / 2, (200 - h) / 2, event_pc);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
if (event_done) {
|
|
return EVENT_DELETE;
|
|
}
|
|
|
|
return EVENT_CONTINUE;
|
|
}
|
|
|
|
int Events::handleImmediate(EVENT *event) {
|
|
double event_pc = 0.0; // Event completion percentage
|
|
bool event_done = false;
|
|
|
|
SURFACE *back_buf;
|
|
|
|
event_pc = ((double)event->duration - event->time) / event->duration;
|
|
|
|
if (event_pc >= 1.0) {
|
|
// Cap percentage to 100
|
|
event_pc = 1.0;
|
|
event_done = true;
|
|
}
|
|
|
|
if (event_pc < 0.0) {
|
|
// Event not signaled, skip it
|
|
return EVENT_BREAK;
|
|
} else if (!(event->code & SIGNALED)) {
|
|
// Signal event
|
|
event->code |= SIGNALED;
|
|
event_pc = 0.0;
|
|
}
|
|
|
|
switch (event->code & EVENT_MASK) {
|
|
case PAL_EVENT:
|
|
switch (event->op) {
|
|
case EVENT_BLACKTOPAL:
|
|
back_buf = _vm->_gfx->getBackBuffer();
|
|
_vm->_gfx->blackToPal(back_buf, (PALENTRY *)event->data, event_pc);
|
|
break;
|
|
|
|
case EVENT_PALTOBLACK:
|
|
back_buf = _vm->_gfx->getBackBuffer();
|
|
_vm->_gfx->palToBlack(back_buf, (PALENTRY *)event->data, event_pc);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case SCRIPT_EVENT:
|
|
case BG_EVENT:
|
|
case INTERFACE_EVENT:
|
|
handleOneShot(event);
|
|
event_done = true;
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
if (event_done) {
|
|
return EVENT_DELETE;
|
|
}
|
|
|
|
return EVENT_BREAK;
|
|
}
|
|
|
|
int Events::handleOneShot(EVENT *event) {
|
|
SURFACE *back_buf;
|
|
ScriptThread *sthread;
|
|
Rect rect;
|
|
|
|
static SCENE_BGINFO bginfo;
|
|
|
|
if (event->time > 0) {
|
|
return EVENT_CONTINUE;
|
|
}
|
|
|
|
// Event has been signaled
|
|
|
|
switch (event->code & EVENT_MASK) {
|
|
case TEXT_EVENT:
|
|
switch (event->op) {
|
|
case EVENT_DISPLAY:
|
|
_vm->textSetDisplay((TEXTLIST_ENTRY *)event->data, 1);
|
|
break;
|
|
case EVENT_REMOVE:
|
|
{
|
|
_vm->textDeleteEntry(_vm->_scene->_textList, (TEXTLIST_ENTRY *)event->data);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
case SOUND_EVENT:
|
|
_vm->_sound->stopSound();
|
|
if (event->op == EVENT_PLAY)
|
|
_vm->_sndRes->playSound(event->param, event->param2, event->param3 != 0);
|
|
break;
|
|
case VOICE_EVENT:
|
|
_vm->_sndRes->playVoice(event->param);
|
|
break;
|
|
case MUSIC_EVENT:
|
|
_vm->_music->stop();
|
|
if (event->op == EVENT_PLAY)
|
|
_vm->_music->play(event->param, event->param2);
|
|
break;
|
|
case BG_EVENT:
|
|
{
|
|
BUFFER_INFO rbuf_info;
|
|
Point bg_pt;
|
|
|
|
if (!(_vm->_scene->getFlags() & kSceneFlagISO)) {
|
|
|
|
back_buf = _vm->_gfx->getBackBuffer();
|
|
|
|
_vm->_render->getBufferInfo(&rbuf_info);
|
|
_vm->_scene->getBGInfo(&bginfo);
|
|
|
|
bg_pt.x = bginfo.bg_x;
|
|
bg_pt.y = bginfo.bg_y;
|
|
|
|
bufToBuffer(rbuf_info.bg_buf, rbuf_info.bg_buf_w, rbuf_info.bg_buf_h,
|
|
bginfo.bg_buf, bginfo.bg_w, bginfo.bg_h, NULL, &bg_pt);
|
|
|
|
// If it is inset scene then draw black border
|
|
if (bginfo.bg_w < _vm->getDisplayWidth() || bginfo.bg_h < _vm->getSceneHeight()) {
|
|
SURFACE s;
|
|
s.pixels = rbuf_info.bg_buf;
|
|
s.w = s.pitch = rbuf_info.bg_buf_w;
|
|
s.h = rbuf_info.bg_buf_h;
|
|
s.bytesPerPixel = 1;
|
|
Common::Rect rect1(2, bginfo.bg_h + 4);
|
|
Common::Rect rect2(bginfo.bg_w + 4, 2);
|
|
Common::Rect rect3(2, bginfo.bg_h + 4);
|
|
Common::Rect rect4(bginfo.bg_w + 4, 2);
|
|
rect1.moveTo(bginfo.bg_x - 2, bginfo.bg_y - 2);
|
|
rect2.moveTo(bginfo.bg_x - 2, bginfo.bg_y - 2);
|
|
rect3.moveTo(bginfo.bg_x + bginfo.bg_w, bginfo.bg_y - 2);
|
|
rect4.moveTo(bginfo.bg_x - 2, bginfo.bg_y + bginfo.bg_h);
|
|
|
|
drawRect(&s, rect1, kITEColorBlack);
|
|
drawRect(&s, rect2, kITEColorBlack);
|
|
drawRect(&s, rect3, kITEColorBlack);
|
|
drawRect(&s, rect4, kITEColorBlack);
|
|
}
|
|
|
|
if (event->param == SET_PALETTE) {
|
|
PALENTRY *pal_p;
|
|
_vm->_scene->getBGPal(&pal_p);
|
|
_vm->_gfx->setPalette(back_buf, pal_p);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ANIM_EVENT:
|
|
switch (event->op) {
|
|
case EVENT_PLAY:
|
|
_vm->_anim->play(event->param, event->time, true);
|
|
break;
|
|
case EVENT_STOP:
|
|
_vm->_anim->stop(event->param);
|
|
break;
|
|
case EVENT_FRAME:
|
|
_vm->_anim->play(event->param, event->time, false);
|
|
break;
|
|
case EVENT_SETFLAG:
|
|
_vm->_anim->setFlag(event->param, event->param2);
|
|
break;
|
|
case EVENT_CLEARFLAG:
|
|
_vm->_anim->clearFlag(event->param, event->param2);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case SCENE_EVENT:
|
|
switch (event->op) {
|
|
case EVENT_END:
|
|
_vm->_scene->nextScene();
|
|
return EVENT_BREAK;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case PALANIM_EVENT:
|
|
switch (event->op) {
|
|
case EVENT_CYCLESTART:
|
|
_vm->_palanim->cycleStart();
|
|
break;
|
|
case EVENT_CYCLESTEP:
|
|
_vm->_palanim->cycleStep(event->time);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case INTERFACE_EVENT:
|
|
switch (event->op) {
|
|
case EVENT_ACTIVATE:
|
|
_vm->_interface->activate();
|
|
break;
|
|
case EVENT_DEACTIVATE:
|
|
_vm->_interface->deactivate();
|
|
break;
|
|
case EVENT_SET_STATUS:
|
|
_vm->_interface->setStatusText((const char*)event->data);
|
|
_vm->_interface->drawStatusBar();
|
|
break;
|
|
case EVENT_CLEAR_STATUS:
|
|
_vm->_interface->setStatusText("");
|
|
_vm->_interface->drawStatusBar();
|
|
break;
|
|
case EVENT_RESTORE_MODE:
|
|
_vm->_interface->restoreMode();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case SCRIPT_EVENT:
|
|
switch (event->op) {
|
|
case EVENT_EXEC_BLOCKING:
|
|
case EVENT_EXEC_NONBLOCKING:
|
|
debug(0, "Exec module number %d script entry number %d", event->param, event->param2);
|
|
|
|
sthread = _vm->_script->createThread(event->param, event->param2);
|
|
if (sthread == NULL) {
|
|
_vm->_console->DebugPrintf("Thread creation failed.\n");
|
|
break;
|
|
}
|
|
|
|
sthread->_threadVars[kThreadVarAction] = TO_LE_16(event->param3);
|
|
sthread->_threadVars[kThreadVarObject] = TO_LE_16(event->param4);
|
|
sthread->_threadVars[kThreadVarWithObject] = TO_LE_16(event->param5);
|
|
sthread->_threadVars[kThreadVarActor] = TO_LE_16(event->param6);
|
|
|
|
if (event->op == EVENT_EXEC_BLOCKING)
|
|
_vm->_script->completeThread();
|
|
|
|
break;
|
|
case EVENT_THREAD_WAKE:
|
|
_vm->_script->wakeUpThreads(event->param);
|
|
break;
|
|
}
|
|
break;
|
|
case CURSOR_EVENT:
|
|
switch (event->op) {
|
|
case EVENT_SHOW:
|
|
_vm->_gfx->showCursor(true);
|
|
break;
|
|
case EVENT_HIDE:
|
|
_vm->_gfx->showCursor(false);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GRAPHICS_EVENT:
|
|
switch (event->op) {
|
|
case EVENT_FILL_RECT:
|
|
rect.top = event->param2;
|
|
rect.bottom = event->param3;
|
|
rect.left = event->param4;
|
|
rect.right = event->param5;
|
|
drawRect((SURFACE *)event->data, rect, event->param);
|
|
break;
|
|
case EVENT_SETFLAG:
|
|
_vm->_render->setFlag(event->param);
|
|
break;
|
|
case EVENT_CLEARFLAG:
|
|
_vm->_render->clearFlag(event->param);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return EVENT_DELETE;
|
|
}
|
|
|
|
int Events::handleInterval(EVENT *event) {
|
|
return EVENT_DELETE;
|
|
}
|
|
|
|
// Schedules an event in the event list; returns a pointer to the scheduled
|
|
// event suitable for chaining if desired.
|
|
EVENT *Events::queue(EVENT *event) {
|
|
EVENT *queuedEvent;
|
|
|
|
queuedEvent = _eventList.pushBack(*event).operator->();
|
|
initializeEvent(queuedEvent);
|
|
|
|
return queuedEvent;
|
|
}
|
|
|
|
// Places a 'add_event' on the end of an event chain given by 'head_event'
|
|
// (head_event may be in any position in the event chain)
|
|
EVENT *Events::chain(EVENT *headEvent, EVENT *addEvent) {
|
|
if (headEvent == NULL) {
|
|
return queue(addEvent);
|
|
}
|
|
|
|
EVENT *walkEvent;
|
|
for (walkEvent = headEvent; walkEvent->chain != NULL; walkEvent = walkEvent->chain) {
|
|
continue;
|
|
}
|
|
|
|
walkEvent->chain = (EVENT *)malloc(sizeof(*walkEvent->chain));
|
|
*walkEvent->chain = *addEvent;
|
|
initializeEvent(walkEvent->chain);
|
|
|
|
return walkEvent->chain;
|
|
}
|
|
|
|
int Events::initializeEvent(EVENT *event) {
|
|
event->chain = NULL;
|
|
switch (event->type) {
|
|
case ONESHOT_EVENT:
|
|
break;
|
|
case CONTINUOUS_EVENT:
|
|
case IMMEDIATE_EVENT:
|
|
event->time += event->duration;
|
|
break;
|
|
case INTERVAL_EVENT:
|
|
break;
|
|
default:
|
|
return FAILURE;
|
|
break;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
int Events::clearList() {
|
|
EVENT *chain_walk;
|
|
EVENT *next_chain;
|
|
EVENT *event_p;
|
|
|
|
// Walk down event list
|
|
for (EventList::iterator eventi = _eventList.begin(); eventi != _eventList.end(); ++eventi) {
|
|
event_p = (EVENT *)eventi.operator->();
|
|
|
|
// Only remove events not marked NODESTROY (engine events)
|
|
if (!(event_p->code & NODESTROY)) {
|
|
// Remove any events chained off this one */
|
|
for (chain_walk = event_p->chain; chain_walk != NULL; chain_walk = next_chain) {
|
|
next_chain = chain_walk->chain;
|
|
free(chain_walk);
|
|
}
|
|
eventi=_eventList.eraseAndPrev(eventi);
|
|
}
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
// Removes all events from the list (even NODESTROY)
|
|
int Events::freeList() {
|
|
EVENT *chain_walk;
|
|
EVENT *next_chain;
|
|
EVENT *event_p;
|
|
|
|
// Walk down event list
|
|
EventList::iterator eventi = _eventList.begin();
|
|
while (eventi != _eventList.end()) {
|
|
event_p = (EVENT *)eventi.operator->();
|
|
|
|
// Remove any events chained off this one */
|
|
for (chain_walk = event_p->chain; chain_walk != NULL; chain_walk = next_chain) {
|
|
next_chain = chain_walk->chain;
|
|
free(chain_walk);
|
|
}
|
|
eventi=_eventList.erase(eventi);
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
// Walks down the event list, updating event times by 'msec'.
|
|
int Events::processEventTime(long msec) {
|
|
EVENT *event_p;
|
|
uint16 event_count = 0;
|
|
|
|
for (EventList::iterator eventi = _eventList.begin(); eventi != _eventList.end(); ++eventi) {
|
|
event_p = (EVENT *)eventi.operator->();
|
|
|
|
event_p->time -= msec;
|
|
event_count++;
|
|
|
|
if (event_p->type == IMMEDIATE_EVENT)
|
|
break;
|
|
|
|
if (event_count > EVENT_WARNINGCOUNT) {
|
|
warning("Event list exceeds %u", EVENT_WARNINGCOUNT);
|
|
}
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
} // End of namespace Saga
|