scummvm/engines/agos/event.cpp

401 lines
8.3 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2001 Ludvig Strigeus
* Copyright (C) 2001-2006 The ScummVM project
*
* 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$
*
*/
#include "common/stdafx.h"
#include "agos/agos.h"
#include "agos/intern.h"
namespace AGOS {
void AGOSEngine::addTimeEvent(uint timeout, uint subroutine_id) {
TimeEvent *te = (TimeEvent *)malloc(sizeof(TimeEvent)), *first, *last = NULL;
time_t cur_time;
time(&cur_time);
te->time = cur_time + timeout - _gameStoppedClock;
if (getGameType() == GType_FF && _clockStopped)
te->time -= ((uint32)time(NULL) - _clockStopped);
te->subroutine_id = subroutine_id;
first = _firstTimeStruct;
while (first) {
if (te->time <= first->time) {
if (last) {
last->next = te;
te->next = first;
return;
}
te->next = _firstTimeStruct;
_firstTimeStruct = te;
return;
}
last = first;
first = first->next;
}
if (last) {
last->next = te;
te->next = NULL;
} else {
_firstTimeStruct = te;
te->next = NULL;
}
}
void AGOSEngine::delTimeEvent(TimeEvent *te) {
TimeEvent *cur;
if (te == _pendingDeleteTimeEvent)
_pendingDeleteTimeEvent = NULL;
if (te == _firstTimeStruct) {
_firstTimeStruct = te->next;
free(te);
return;
}
cur = _firstTimeStruct;
if (cur == NULL)
error("delTimeEvent: none available");
for (;;) {
if (cur->next == NULL)
error("delTimeEvent: no such te");
if (te == cur->next) {
cur->next = te->next;
free(te);
return;
}
cur = cur->next;
}
}
void AGOSEngine::invokeTimeEvent(TimeEvent *te) {
Subroutine *sub;
_scriptVerb = 0;
if (_runScriptReturn1)
return;
sub = getSubroutineByID(te->subroutine_id);
if (sub != NULL)
startSubroutineEx(sub);
_runScriptReturn1 = false;
}
void AGOSEngine::killAllTimers() {
TimeEvent *cur, *next;
for (cur = _firstTimeStruct; cur; cur = next) {
next = cur->next;
delTimeEvent(cur);
}
}
bool AGOSEngine::kickoffTimeEvents() {
time_t cur_time;
TimeEvent *te;
bool result = false;
if (getGameType() == GType_FF && _clockStopped)
return result;
time(&cur_time);
cur_time -= _gameStoppedClock;
while ((te = _firstTimeStruct) != NULL && te->time <= (uint32)cur_time) {
result = true;
_pendingDeleteTimeEvent = te;
invokeTimeEvent(te);
if (_pendingDeleteTimeEvent) {
_pendingDeleteTimeEvent = NULL;
delTimeEvent(te);
}
}
return result;
}
bool AGOSEngine::isVgaQueueEmpty() {
VgaTimerEntry *vte = _vgaTimerList;
bool result = false;
while (vte->delay) {
if (vte->cur_vga_file == _variableArray[999] && vte->sprite_id >= 100) {
result = true;
break;
}
vte++;
}
return result;
}
void AGOSEngine::addVgaEvent(uint16 num, const byte *code_ptr, uint16 cur_sprite, uint16 curZoneNum, int32 param) {
VgaTimerEntry *vte;
// When Simon talks to the Golum about stew in French version of
// Simon the Sorcerer 1 the code_ptr is at wrong location for
// sprite 200. This was a bug in the original game, which
// caused several glitches in this scene.
// We work around the problem by correcting the code_ptr for sprite
// 200 in this scene, if it is wrong.
if (getGameType() == GType_SIMON1 && _language == Common::FR_FRA &&
(code_ptr - _vgaBufferPointers[curZoneNum].vgaFile1 == 4) && (cur_sprite == 200) && (curZoneNum == 2))
code_ptr += 0x66;
_lockWord |= 1;
for (vte = _vgaTimerList; vte->delay; vte++) {
}
vte->delay = num;
vte->script_pointer = code_ptr;
vte->sprite_id = cur_sprite;
vte->cur_vga_file = curZoneNum;
vte->param = param;
_lockWord &= ~1;
}
void AGOSEngine::deleteVgaEvent(VgaTimerEntry * vte) {
_lockWord |= 1;
if (vte + 1 <= _nextVgaTimerToProcess) {
_nextVgaTimerToProcess--;
}
do {
memcpy(vte, vte + 1, sizeof(VgaTimerEntry));
vte++;
} while (vte->delay);
_lockWord &= ~1;
}
void AGOSEngine::processVgaEvents() {
VgaTimerEntry *vte = _vgaTimerList;
_vgaTickCounter++;
while (vte->delay) {
vte->delay -= _vgaBaseDelay;
if (vte->delay <= 0) {
uint16 curZoneNum = vte->cur_vga_file;
uint16 cur_sprite = vte->sprite_id;
const byte *script_ptr = vte->script_pointer;
int32 param = vte->param;
_nextVgaTimerToProcess = vte + 1;
deleteVgaEvent(vte);
if ((getGameType() == GType_FF || getGameType() == GType_PP) &&
script_ptr == NULL) {
panEvent(curZoneNum, cur_sprite, param);
} else if (getGameType() == GType_SIMON2 && script_ptr == NULL) {
scrollEvent();
} else {
animateEvent(script_ptr, curZoneNum, cur_sprite);
}
vte = _nextVgaTimerToProcess;
} else {
vte++;
}
}
}
void AGOSEngine::animateEvent(const byte *code_ptr, uint16 curZoneNum, uint16 cur_sprite) {
VgaPointersEntry *vpe;
_vgaCurSpriteId = cur_sprite;
_vgaCurZoneNum = curZoneNum;
_zoneNumber = curZoneNum;
vpe = &_vgaBufferPointers[curZoneNum];
_curVgaFile1 = vpe->vgaFile1;
_curVgaFile2 = vpe->vgaFile2;
_curSfxFile = vpe->sfxFile;
_vcPtr = code_ptr;
runVgaScript();
}
void AGOSEngine::panEvent(uint16 curZoneNum, uint16 cur_sprite, int32 param) {
_vgaCurSpriteId = cur_sprite;
_vgaCurZoneNum = curZoneNum;
VgaSprite *vsp = findCurSprite();
param &= 0x10;
int32 pan = (vsp->x - _scrollX + param) * 8 - 2560;
if (pan < -10000)
pan = -10000;
if (pan > 10000)
pan = 10000;
//setSfxPan(param, pan);
if (pan != 0)
addVgaEvent(10, NULL, _vgaCurSpriteId, _vgaCurZoneNum); /* pan event */
debug(0, "panEvent: param %d pan %d", param, pan);
}
void AGOSEngine::scrollEvent() {
if (_scrollCount == 0)
return;
if (getGameType() == GType_FF) {
if (_scrollCount < 0) {
if (_scrollFlag != -8) {
_scrollFlag = -8;
_scrollCount += 8;
}
} else {
if (_scrollFlag != 8) {
_scrollFlag = 8;
_scrollCount -= 8;
}
}
} else {
if (_scrollCount < 0) {
if (_scrollFlag != -1) {
_scrollFlag = -1;
if (++_scrollCount == 0)
return;
}
} else {
if (_scrollFlag != 1) {
_scrollFlag = 1;
if (--_scrollCount == 0)
return;
}
}
addVgaEvent(6, NULL, 0, 0); /* scroll event */
}
}
void AGOSEngine::timer_callback() {
if (_timer5 != 0) {
_syncFlag2 = true;
_timer5--;
} else {
timer_proc1();
}
}
void AGOSEngine::timer_proc1() {
_timer4++;
if (_lockWord & 0x80E9 || _lockWord & 2)
return;
_syncCount++;
_lockWord |= 2;
if (!(_lockWord & 0x10)) {
if (getGameType() == GType_PP) {
_syncFlag2 ^= 1;
if (!_syncFlag2) {
processVgaEvents();
} else {
if (_scrollCount == 0) {
_lockWord &= ~2;
return;
}
}
} else if (getGameType() == GType_FF) {
_syncFlag2 ^= 1;
if (!_syncFlag2) {
processVgaEvents();
} else {
// Double speed on Oracle
if (getBitFlag(99)) {
processVgaEvents();
} else if (_scrollCount == 0) {
_lockWord &= ~2;
return;
}
}
} else {
processVgaEvents();
processVgaEvents();
_syncFlag2 ^= 1;
_cepeFlag ^= 1;
if (!_cepeFlag)
processVgaEvents();
if (_mouseHideCount != 0 && _syncFlag2) {
_lockWord &= ~2;
return;
}
}
}
if (getGameType() == GType_FF)
_moviePlay->nextFrame();
animateSprites();
if (_drawImagesDebug)
animateSpritesDebug();
if (_copyPartialMode == 1) {
fillBackFromFront(80, 46, 208 - 80, 94 - 46);
}
if (_copyPartialMode == 2) {
if (getGameType() == GType_FF || getGameType() == GType_PP) {
fillFrontFromBack(0, 0, _screenWidth, _screenHeight);
} else {
fillFrontFromBack(176, 61, _screenWidth - 176, 134 - 61);
}
_copyPartialMode = 0;
}
if (_updateScreen) {
if (getGameType() == GType_FF) {
if (!getBitFlag(78)) {
oracleLogo();
}
if (getBitFlag(76)) {
swapCharacterLogo();
}
}
handleMouseMoved();
dx_update_screen_and_palette();
_updateScreen = false;
}
_lockWord &= ~2;
}
} // End of namespace AGOS