mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-08 19:00:57 +00:00
1003 lines
30 KiB
C++
1003 lines
30 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.
|
|
*
|
|
*/
|
|
|
|
#include "twine/twine.h"
|
|
#include "common/config-manager.h"
|
|
#include "common/debug.h"
|
|
#include "common/error.h"
|
|
#include "common/events.h"
|
|
#include "common/keyboard.h"
|
|
#include "common/str.h"
|
|
#include "common/system.h"
|
|
#include "common/textconsole.h"
|
|
#include "engines/util.h"
|
|
#include "graphics/managed_surface.h"
|
|
#include "graphics/palette.h"
|
|
#include "graphics/pixelformat.h"
|
|
#include "graphics/surface.h"
|
|
#include "gui/debugger.h"
|
|
#include "twine/actor.h"
|
|
#include "twine/animations.h"
|
|
#include "twine/collision.h"
|
|
#include "twine/debug.h"
|
|
#include "twine/debug_grid.h"
|
|
#include "twine/debug_scene.h"
|
|
#include "twine/extra.h"
|
|
#include "twine/flamovies.h"
|
|
#include "twine/gamestate.h"
|
|
#include "twine/grid.h"
|
|
#include "twine/holomap.h"
|
|
#include "twine/hqrdepack.h"
|
|
#include "twine/interface.h"
|
|
#include "twine/keyboard.h"
|
|
#include "twine/menu.h"
|
|
#include "twine/menuoptions.h"
|
|
#include "twine/movements.h"
|
|
#include "twine/music.h"
|
|
#include "twine/redraw.h"
|
|
#include "twine/renderer.h"
|
|
#include "twine/resources.h"
|
|
#include "twine/scene.h"
|
|
#include "twine/screens.h"
|
|
#include "twine/script_life.h"
|
|
#include "twine/script_move.h"
|
|
#include "twine/sound.h"
|
|
#include "twine/text.h"
|
|
|
|
namespace TwinE {
|
|
|
|
TwinEEngine::TwinEEngine(OSystem *system, Common::Language language, uint32 flags)
|
|
: Engine(system), _gameLang(language), _gameFlags(flags), _rnd("twine") {
|
|
setDebugger(new GUI::Debugger());
|
|
_actor = new Actor(this);
|
|
_animations = new Animations(this);
|
|
_collision = new Collision(this);
|
|
_extra = new Extra(this);
|
|
_gameState = new GameState(this);
|
|
_grid = new Grid(this);
|
|
_movements = new Movements(this);
|
|
_hqrdepack = new HQRDepack(this);
|
|
_interface = new Interface(this);
|
|
_menu = new Menu(this);
|
|
_flaMovies = new FlaMovies(this);
|
|
_menuOptions = new MenuOptions(this);
|
|
_music = new Music(this);
|
|
_redraw = new Redraw(this);
|
|
_renderer = new Renderer(this);
|
|
_resources = new Resources(this);
|
|
_scene = new Scene(this);
|
|
_screens = new Screens(this);
|
|
_scriptLife = new ScriptLife(this);
|
|
_scriptMove = new ScriptMove(this);
|
|
_holomap = new Holomap(this);
|
|
_sound = new Sound(this);
|
|
_text = new Text(this);
|
|
_debugGrid = new DebugGrid(this);
|
|
_debug = new Debug(this);
|
|
_debugScene = new DebugScene(this);
|
|
}
|
|
|
|
TwinEEngine::~TwinEEngine() {
|
|
delete _actor;
|
|
delete _animations;
|
|
delete _collision;
|
|
delete _extra;
|
|
delete _gameState;
|
|
delete _grid;
|
|
delete _movements;
|
|
delete _hqrdepack;
|
|
delete _interface;
|
|
delete _menu;
|
|
delete _flaMovies;
|
|
delete _music;
|
|
delete _redraw;
|
|
delete _renderer;
|
|
delete _resources;
|
|
delete _scene;
|
|
delete _screens;
|
|
delete _scriptLife;
|
|
delete _scriptMove;
|
|
delete _holomap;
|
|
delete _sound;
|
|
delete _text;
|
|
delete _debugGrid;
|
|
delete _debug;
|
|
delete _debugScene;
|
|
}
|
|
|
|
Common::Error TwinEEngine::run() {
|
|
debug("Starting twine");
|
|
syncSoundSettings();
|
|
initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT);
|
|
allocVideoMemory();
|
|
initAll();
|
|
initEngine();
|
|
_music->stopTrackMusic();
|
|
_music->stopMidiMusic();
|
|
return Common::kNoError;
|
|
}
|
|
|
|
bool TwinEEngine::hasFeature(EngineFeature f) const {
|
|
return false;
|
|
}
|
|
|
|
void TwinEEngine::allocVideoMemory() {
|
|
const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
|
|
workVideoBuffer.create(SCREEN_WIDTH, SCREEN_HEIGHT, format);
|
|
frontVideoBuffer.create(SCREEN_WIDTH, SCREEN_HEIGHT, format);
|
|
|
|
int32 j = 0;
|
|
int32 k = 0;
|
|
for (int32 i = SCREEN_HEIGHT; i > 0; i--) {
|
|
screenLookupTable[j] = k;
|
|
j++;
|
|
k += SCREEN_WIDTH;
|
|
}
|
|
|
|
// initVideoVar1 = -1;
|
|
}
|
|
|
|
static int getLanguageTypeIndex(const char *languageName) {
|
|
char buffer[256];
|
|
Common::strlcpy(buffer, languageName, sizeof(buffer));
|
|
|
|
char *ptr = strchr(buffer, ' ');
|
|
if (ptr != nullptr) {
|
|
*ptr = '\0';
|
|
}
|
|
|
|
const int32 length = ARRAYSIZE(LanguageTypes);
|
|
for (int32 i = 0; i < length; i++) {
|
|
if (!strcmp(LanguageTypes[i].name, buffer)) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return 0; // English
|
|
}
|
|
|
|
#define ConfGetOrDefault(key, defaultVal) (ConfMan.hasKey(key) ? ConfMan.get(key) : Common::String(defaultVal))
|
|
#define ConfGetIntOrDefault(key, defaultVal) (ConfMan.hasKey(key) ? atoi(ConfMan.get(key).c_str()) : (defaultVal))
|
|
|
|
void TwinEEngine::initConfigurations() {
|
|
// TODO: use existing entries for some of the settings - like volume and so on.
|
|
|
|
Common::String language = ConfGetOrDefault("Language", Common::getLanguageDescription(_gameLang));
|
|
cfgfile.LanguageId = getLanguageTypeIndex(language.c_str()) + 1;
|
|
|
|
Common::String languageCD = ConfGetOrDefault("LanguageCD", "None");
|
|
cfgfile.LanguageCDId = getLanguageTypeIndex(languageCD.c_str()) + 1;
|
|
|
|
cfgfile.FlagDisplayText = ConfGetOrDefault("FlagDisplayText", "ON") == "ON";
|
|
cfgfile.FlagKeepVoice = ConfGetOrDefault("FlagKeepVoice", "OFF") == "ON";
|
|
const Common::String midiType = ConfGetOrDefault("MidiType", "auto");
|
|
if (midiType == "auto") {
|
|
Common::File midiHqr;
|
|
if (midiHqr.exists(Resources::HQR_MIDI_MI_WIN_FILE)) {
|
|
cfgfile.MidiType = 1;
|
|
} else {
|
|
cfgfile.MidiType = 0;
|
|
}
|
|
} else if (midiType == "midi") {
|
|
cfgfile.MidiType = 1;
|
|
} else {
|
|
cfgfile.MidiType = 0;
|
|
}
|
|
cfgfile.Version = ConfGetIntOrDefault("Version", EUROPE_VERSION);
|
|
cfgfile.FullScreen = ConfGetIntOrDefault("FullScreen", 1) == 1;
|
|
cfgfile.UseCD = ConfGetIntOrDefault("UseCD", 0);
|
|
cfgfile.Sound = ConfGetIntOrDefault("Sound", 0);
|
|
cfgfile.Movie = ConfGetIntOrDefault("Movie", 0);
|
|
cfgfile.CrossFade = ConfGetIntOrDefault("CrossFade", 0);
|
|
cfgfile.Fps = ConfGetIntOrDefault("Fps", DEFAULT_FRAMES_PER_SECOND);
|
|
cfgfile.Debug = ConfGetIntOrDefault("Debug", 0);
|
|
cfgfile.UseAutoSaving = ConfGetIntOrDefault("UseAutoSaving", 0);
|
|
cfgfile.AutoAgressive = ConfGetIntOrDefault("CombatAuto", 0);
|
|
cfgfile.ShadowMode = ConfGetIntOrDefault("Shadow", 0);
|
|
cfgfile.SceZoom = ConfGetIntOrDefault("SceZoom", 0);
|
|
cfgfile.WallCollision = ConfGetIntOrDefault("WallCollision", 0);
|
|
}
|
|
|
|
void TwinEEngine::initEngine() {
|
|
// getting configuration file
|
|
initConfigurations();
|
|
|
|
/** Engine current version */
|
|
const char *ENGINE_VERSION = "0.2.2";
|
|
|
|
// Show engine information
|
|
debug("TwinEngine v%s", ENGINE_VERSION);
|
|
debug("(c)2002 The TwinEngine team.");
|
|
debug("(c)2020 The ScummVM team.");
|
|
debug("Refer to the credits for further details.");
|
|
debug("Released under the terms of the GNU GPL license version 2 (or, at your opinion, any later). See COPYING file.");
|
|
debug("The original Little Big Adventure game is:");
|
|
debug("(c)1994 by Adeline Software International, All Rights Reserved.");
|
|
|
|
_screens->clearScreen();
|
|
|
|
// Check if LBA CD-Rom is on drive
|
|
_music->initCdrom();
|
|
|
|
// Display company logo
|
|
_screens->adelineLogo();
|
|
|
|
// verify game version screens
|
|
if (cfgfile.Version == EUROPE_VERSION) {
|
|
// Little Big Adventure screen
|
|
_screens->loadImageDelay(RESSHQR_LBAIMG, 3);
|
|
// Electronic Arts Logo
|
|
_screens->loadImageDelay(RESSHQR_EAIMG, 2);
|
|
} else if (cfgfile.Version == USA_VERSION) {
|
|
// Relentless screen
|
|
_screens->loadImageDelay(RESSHQR_RELLENTIMG, 3);
|
|
// Electronic Arts Logo
|
|
_screens->loadImageDelay(RESSHQR_EAIMG, 2);
|
|
} else if (cfgfile.Version == MODIFICATION_VERSION) {
|
|
// Modification screen
|
|
_screens->loadImageDelay(RESSHQR_RELLENTIMG, 2);
|
|
}
|
|
|
|
_flaMovies->playFlaMovie(FLA_DRAGON3);
|
|
|
|
_screens->loadMenuImage();
|
|
|
|
_menu->mainMenu();
|
|
}
|
|
|
|
void TwinEEngine::initMCGA() {
|
|
_redraw->drawInGameTransBox = 1;
|
|
}
|
|
|
|
void TwinEEngine::initSVGA() {
|
|
_redraw->drawInGameTransBox = 0;
|
|
}
|
|
|
|
void TwinEEngine::initAll() {
|
|
_grid->blockBuffer = (uint8 *)malloc(64 * 64 * 25 * 2 * sizeof(uint8));
|
|
_animations->animBuffer1 = _animations->animBuffer2 = (uint8 *)malloc(5000 * sizeof(uint8));
|
|
memset(_menu->itemAngle, 256, sizeof(_menu->itemAngle)); // reset inventory items angles
|
|
|
|
_redraw->bubbleSpriteIndex = SPRITEHQR_DIAG_BUBBLE_LEFT;
|
|
|
|
_scene->sceneHero = &_scene->sceneActors[0];
|
|
|
|
_redraw->renderRight = SCREEN_TEXTLIMIT_RIGHT;
|
|
_redraw->renderBottom = SCREEN_TEXTLIMIT_BOTTOM;
|
|
// Set clip to fullscreen by default, allows main menu to render properly after load
|
|
_interface->resetClip();
|
|
|
|
_resources->initResources();
|
|
|
|
initSVGA();
|
|
}
|
|
|
|
int TwinEEngine::getRandomNumber(uint max) {
|
|
return _rnd.getRandomNumber(max);
|
|
}
|
|
|
|
void TwinEEngine::freezeTime() {
|
|
if (!isTimeFreezed) {
|
|
saveFreezedTime = lbaTime;
|
|
}
|
|
isTimeFreezed++;
|
|
}
|
|
|
|
void TwinEEngine::unfreezeTime() {
|
|
--isTimeFreezed;
|
|
if (isTimeFreezed == 0) {
|
|
lbaTime = saveFreezedTime;
|
|
}
|
|
}
|
|
|
|
void TwinEEngine::processActorSamplePosition(int32 actorIdx) {
|
|
const ActorStruct *actor = &_scene->sceneActors[actorIdx];
|
|
const int32 channelIdx = _sound->getActorChannel(actorIdx);
|
|
_sound->setSamplePosition(channelIdx, actor->x, actor->y, actor->z);
|
|
}
|
|
|
|
int32 TwinEEngine::runGameEngine() { // mainLoopInteration
|
|
readKeys();
|
|
|
|
if (_scene->needChangeScene > -1) {
|
|
_scene->changeScene();
|
|
}
|
|
|
|
previousLoopPressedKey = loopPressedKey;
|
|
_keyboard.key = _keyboard.pressedKey;
|
|
loopPressedKey = _keyboard.skippedKey;
|
|
loopCurrentKey = _keyboard.internalKeyCode;
|
|
|
|
_debug->processDebug(loopCurrentKey);
|
|
|
|
if (_menuOptions->canShowCredits != 0) {
|
|
// TODO: if current music playing != 8, than play_track(8);
|
|
if (_keyboard.internalKeyCode != 0) {
|
|
return 0;
|
|
}
|
|
if (_keyboard.pressedKey != 0) {
|
|
return 0;
|
|
}
|
|
if (_keyboard.skippedKey != 0) {
|
|
return 0;
|
|
}
|
|
} else {
|
|
// Process give up menu - Press ESC
|
|
if (_keyboard.internalKeyCode == 1 && _scene->sceneHero->life > 0 && _scene->sceneHero->entity != -1 && !_scene->sceneHero->staticFlags.bIsHidden) {
|
|
freezeTime();
|
|
if (_menu->giveupMenu()) {
|
|
unfreezeTime();
|
|
_redraw->redrawEngineActions(1);
|
|
freezeTime();
|
|
_gameState->saveGame(); // auto save game
|
|
quitGame = 0;
|
|
unfreezeTime();
|
|
return 0;
|
|
}
|
|
unfreezeTime();
|
|
_redraw->redrawEngineActions(1);
|
|
}
|
|
|
|
if (loopCurrentKey == twineactions[TwinEActionType::OptionsMenu].localKey) {
|
|
int tmpLangCD = cfgfile.LanguageCDId;
|
|
freezeTime();
|
|
_sound->pauseSamples();
|
|
_menu->OptionsMenuSettings[5] = 15;
|
|
cfgfile.LanguageCDId = 0;
|
|
_text->initTextBank(0);
|
|
_menu->optionsMenu();
|
|
cfgfile.LanguageCDId = tmpLangCD;
|
|
_text->initTextBank(_text->currentTextBank + 3);
|
|
//TODO: play music
|
|
_sound->resumeSamples();
|
|
unfreezeTime();
|
|
_redraw->redrawEngineActions(1);
|
|
}
|
|
|
|
// inventory menu
|
|
loopInventoryItem = -1;
|
|
if (loopCurrentKey == twineactions[TwinEActionType::InventoryMenu].localKey && _scene->sceneHero->entity != -1 && _scene->sceneHero->controlMode == kManual) {
|
|
freezeTime();
|
|
_menu->processInventoryMenu();
|
|
|
|
switch (loopInventoryItem) {
|
|
case kiHolomap:
|
|
_holomap->processHolomap();
|
|
_screens->lockPalette = 1;
|
|
warning("Use inventory [kiHolomap] not implemented!\n");
|
|
break;
|
|
case kiMagicBall:
|
|
if (_gameState->usingSabre == 1) {
|
|
_actor->initModelActor(0, 0);
|
|
}
|
|
_gameState->usingSabre = 0;
|
|
break;
|
|
case kiUseSabre:
|
|
if (_scene->sceneHero->body != InventoryItems::kiUseSabre) {
|
|
if (_actor->heroBehaviour == kProtoPack) {
|
|
_actor->setBehaviour(kNormal);
|
|
}
|
|
_actor->initModelActor(InventoryItems::kiUseSabre, 0);
|
|
_animations->initAnim(kSabreUnknown, 1, 0, 0);
|
|
|
|
_gameState->usingSabre = 1;
|
|
}
|
|
break;
|
|
case kiBookOfBu: {
|
|
_screens->fadeToBlack(_screens->paletteRGBA);
|
|
_screens->loadImage(RESSHQR_INTROSCREEN1IMG);
|
|
_text->initTextBank(2);
|
|
_text->newGameVar4 = 0;
|
|
_text->textClipFull();
|
|
_text->setFontCrossColor(15);
|
|
int32 tmpFlagDisplayText = cfgfile.FlagDisplayText;
|
|
cfgfile.FlagDisplayText = 1;
|
|
_text->drawTextFullscreen(161);
|
|
cfgfile.FlagDisplayText = tmpFlagDisplayText;
|
|
_text->textClipSmall();
|
|
_text->newGameVar4 = 1;
|
|
_text->initTextBank(_text->currentTextBank + 3);
|
|
_screens->fadeToBlack(_screens->paletteRGBACustom);
|
|
_screens->clearScreen();
|
|
flip();
|
|
setPalette(_screens->paletteRGBA);
|
|
_screens->lockPalette = 1;
|
|
} break;
|
|
case kiProtoPack:
|
|
if (_gameState->gameFlags[InventoryItems::kiBookOfBu]) {
|
|
_scene->sceneHero->body = 0;
|
|
} else {
|
|
_scene->sceneHero->body = 1;
|
|
}
|
|
|
|
if (_actor->heroBehaviour == kProtoPack) {
|
|
_actor->setBehaviour(kNormal);
|
|
} else {
|
|
_actor->setBehaviour(kProtoPack);
|
|
}
|
|
break;
|
|
case kiPinguin: {
|
|
ActorStruct *pinguin = &_scene->sceneActors[_scene->mecaPinguinIdx];
|
|
|
|
pinguin->x = _renderer->destX + _scene->sceneHero->x;
|
|
pinguin->y = _scene->sceneHero->y;
|
|
pinguin->z = _renderer->destZ + _scene->sceneHero->z;
|
|
pinguin->angle = _scene->sceneHero->angle;
|
|
|
|
_movements->rotateActor(0, 800, pinguin->angle);
|
|
|
|
if (!_collision->checkCollisionWithActors(_scene->mecaPinguinIdx)) {
|
|
pinguin->life = 50;
|
|
pinguin->body = -1;
|
|
_actor->initModelActor(0, _scene->mecaPinguinIdx);
|
|
pinguin->dynamicFlags.bIsDead = 0; // &= 0xDF
|
|
pinguin->brickShape = 0;
|
|
_movements->moveActor(pinguin->angle, pinguin->angle, pinguin->speed, &pinguin->move);
|
|
_gameState->gameFlags[InventoryItems::kiPinguin] = 0; // byte_50D89 = 0;
|
|
pinguin->info0 = lbaTime + 1500;
|
|
}
|
|
} break;
|
|
case kiBonusList: {
|
|
int32 tmpLanguageCDIdx;
|
|
tmpLanguageCDIdx = cfgfile.LanguageCDId;
|
|
unfreezeTime();
|
|
_redraw->redrawEngineActions(1);
|
|
freezeTime();
|
|
cfgfile.LanguageCDId = 0;
|
|
_text->initTextBank(2);
|
|
_text->textClipFull();
|
|
_text->setFontCrossColor(15);
|
|
_text->drawTextFullscreen(162);
|
|
_text->textClipSmall();
|
|
cfgfile.LanguageCDId = tmpLanguageCDIdx;
|
|
_text->initTextBank(_text->currentTextBank + 3);
|
|
} break;
|
|
case kiCloverLeaf:
|
|
if (_scene->sceneHero->life < 50) {
|
|
if (_gameState->inventoryNumLeafs > 0) {
|
|
_scene->sceneHero->life = 50;
|
|
_gameState->inventoryMagicPoints = _gameState->magicLevelIdx * 20;
|
|
_gameState->inventoryNumLeafs--;
|
|
_redraw->addOverlay(koInventoryItem, 27, 0, 0, 0, koNormal, 3);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
unfreezeTime();
|
|
_redraw->redrawEngineActions(1);
|
|
}
|
|
|
|
// Process behaviour menu - Press CTRL and F1..F4 Keys
|
|
if ((loopCurrentKey == twineactions[TwinEActionType::BehaviourMenu].localKey ||
|
|
loopCurrentKey == twineactions[TwinEActionType::QuickBehaviourNormal].localKey ||
|
|
loopCurrentKey == twineactions[TwinEActionType::QuickBehaviourAthletic].localKey ||
|
|
loopCurrentKey == twineactions[TwinEActionType::QuickBehaviourAggressive].localKey ||
|
|
loopCurrentKey == twineactions[TwinEActionType::QuickBehaviourDiscreet].localKey) &&
|
|
_scene->sceneHero->entity != -1 && _scene->sceneHero->controlMode == kManual) {
|
|
if (loopCurrentKey == twineactions[TwinEActionType::QuickBehaviourNormal].localKey) {
|
|
_actor->heroBehaviour = HeroBehaviourType::kNormal;
|
|
} else if (loopCurrentKey == twineactions[TwinEActionType::QuickBehaviourAthletic].localKey) {
|
|
_actor->heroBehaviour = HeroBehaviourType::kAthletic;
|
|
} else if (loopCurrentKey == twineactions[TwinEActionType::QuickBehaviourAggressive].localKey) {
|
|
_actor->heroBehaviour = HeroBehaviourType::kAggressive;
|
|
} else if (loopCurrentKey == twineactions[TwinEActionType::QuickBehaviourDiscreet].localKey) {
|
|
_actor->heroBehaviour = HeroBehaviourType::kDiscrete;
|
|
}
|
|
freezeTime();
|
|
_menu->processBehaviourMenu();
|
|
unfreezeTime();
|
|
_redraw->redrawEngineActions(1);
|
|
}
|
|
|
|
// use Proto-Pack
|
|
if (loopCurrentKey == twineactions[TwinEActionType::UseProtoPack].localKey && _gameState->gameFlags[InventoryItems::kiProtoPack] == 1) {
|
|
if (_gameState->gameFlags[InventoryItems::kiBookOfBu]) {
|
|
_scene->sceneHero->body = 0;
|
|
} else {
|
|
_scene->sceneHero->body = 1;
|
|
}
|
|
|
|
if (_actor->heroBehaviour == kProtoPack) {
|
|
_actor->setBehaviour(kNormal);
|
|
} else {
|
|
_actor->setBehaviour(kProtoPack);
|
|
}
|
|
}
|
|
|
|
// Press Enter to Recenter Screen
|
|
if ((loopPressedKey & 2) && !disableScreenRecenter) {
|
|
_grid->newCameraX = _scene->sceneActors[_scene->currentlyFollowedActor].x >> 9;
|
|
_grid->newCameraY = _scene->sceneActors[_scene->currentlyFollowedActor].y >> 8;
|
|
_grid->newCameraZ = _scene->sceneActors[_scene->currentlyFollowedActor].z >> 9;
|
|
_redraw->reqBgRedraw = 1;
|
|
}
|
|
|
|
// Draw holomap
|
|
if (loopCurrentKey == 35 && _gameState->gameFlags[InventoryItems::kiHolomap] == 1 && !_gameState->gameFlags[GAMEFLAG_INVENTORY_DISABLED]) {
|
|
freezeTime();
|
|
//TestRestoreModeSVGA(1);
|
|
_holomap->processHolomap();
|
|
_screens->lockPalette = 1;
|
|
unfreezeTime();
|
|
_redraw->redrawEngineActions(1);
|
|
}
|
|
|
|
// Process Pause - Press P
|
|
if (loopCurrentKey == twineactions[TwinEActionType::Pause].localKey) {
|
|
freezeTime();
|
|
_text->setFontColor(15);
|
|
_text->drawText(5, 446, "Pause"); // no key for pause in Text Bank
|
|
copyBlockPhys(5, 446, 100, 479);
|
|
do {
|
|
readKeys();
|
|
if (shouldQuit()) {
|
|
break;
|
|
}
|
|
g_system->delayMillis(10);
|
|
} while (_keyboard.internalKeyCode != 0x19 && !_keyboard.pressedKey);
|
|
unfreezeTime();
|
|
_redraw->redrawEngineActions(1);
|
|
}
|
|
}
|
|
|
|
loopActorStep = _movements->getRealValue(&loopMovePtr);
|
|
if (!loopActorStep) {
|
|
loopActorStep = 1;
|
|
}
|
|
|
|
_movements->setActorAngle(0, -256, 5, &loopMovePtr);
|
|
disableScreenRecenter = 0;
|
|
|
|
_scene->processEnvironmentSound();
|
|
|
|
// Reset HitBy state
|
|
for (int32 a = 0; a < _scene->sceneNumActors; a++) {
|
|
_scene->sceneActors[a].hitBy = -1;
|
|
}
|
|
|
|
_extra->processExtras();
|
|
|
|
for (int32 a = 0; a < _scene->sceneNumActors; a++) {
|
|
ActorStruct *actor = &_scene->sceneActors[a];
|
|
|
|
if (!actor->dynamicFlags.bIsDead) {
|
|
if (actor->life == 0) {
|
|
if (a == 0) { // if its hero who died
|
|
_animations->initAnim(kLandDeath, 4, 0, 0);
|
|
actor->controlMode = 0;
|
|
} else {
|
|
_sound->playSample(37, getRandomNumber(2000) + 3096, 1, actor->x, actor->y, actor->z, a);
|
|
|
|
if (a == _scene->mecaPinguinIdx) {
|
|
_extra->addExtraExplode(actor->x, actor->y, actor->z);
|
|
}
|
|
}
|
|
|
|
if (actor->bonusParameter & 0x1F0 && !(actor->bonusParameter & 1)) {
|
|
_actor->processActorExtraBonus(a);
|
|
}
|
|
}
|
|
|
|
_movements->processActorMovements(a);
|
|
|
|
actor->collisionX = actor->x;
|
|
actor->collisionY = actor->y;
|
|
actor->collisionZ = actor->z;
|
|
|
|
if (actor->positionInMoveScript != -1) {
|
|
_scriptMove->processMoveScript(a);
|
|
}
|
|
|
|
_animations->processActorAnimations(a);
|
|
|
|
if (actor->staticFlags.bIsZonable) {
|
|
_scene->processActorZones(a);
|
|
}
|
|
|
|
if (actor->positionInLifeScript != -1) {
|
|
_scriptLife->processLifeScript(a);
|
|
}
|
|
|
|
processActorSamplePosition(a);
|
|
|
|
if (quitGame != -1) {
|
|
return quitGame;
|
|
}
|
|
|
|
if (actor->staticFlags.bCanDrown) {
|
|
int32 brickSound;
|
|
brickSound = _grid->getBrickSoundType(actor->x, actor->y - 1, actor->z);
|
|
actor->brickSound = brickSound;
|
|
|
|
if ((brickSound & 0xF0) == 0xF0) {
|
|
if ((brickSound & 0xF) == 1) {
|
|
if (a) { // all other actors
|
|
int32 rnd = getRandomNumber(2000) + 3096;
|
|
_sound->playSample(0x25, rnd, 1, actor->x, actor->y, actor->z, a);
|
|
if (actor->bonusParameter & 0x1F0) {
|
|
if (!(actor->bonusParameter & 1)) {
|
|
_actor->processActorExtraBonus(a);
|
|
}
|
|
actor->life = 0;
|
|
}
|
|
} else { // if Hero
|
|
if (_actor->heroBehaviour != 4 || (brickSound & 0x0F) != actor->anim) {
|
|
if (!_actor->cropBottomScreen) {
|
|
_animations->initAnim(kDrawn, 4, 0, 0);
|
|
_renderer->projectPositionOnScreen(actor->x - _grid->cameraX, actor->y - _grid->cameraY, actor->z - _grid->cameraZ);
|
|
_actor->cropBottomScreen = _renderer->projPosY;
|
|
}
|
|
_renderer->projectPositionOnScreen(actor->x - _grid->cameraX, actor->y - _grid->cameraY, actor->z - _grid->cameraZ);
|
|
actor->controlMode = 0;
|
|
actor->life = -1;
|
|
_actor->cropBottomScreen = _renderer->projPosY;
|
|
actor->staticFlags.bCanDrown |= 0x10;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (actor->life <= 0) {
|
|
if (!a) { // if its Hero
|
|
if (actor->dynamicFlags.bAnimEnded) {
|
|
if (_gameState->inventoryNumLeafs > 0) { // use clover leaf automaticaly
|
|
_scene->sceneHero->x = _scene->newHeroX;
|
|
_scene->sceneHero->y = _scene->newHeroY;
|
|
_scene->sceneHero->z = _scene->newHeroZ;
|
|
|
|
_scene->needChangeScene = _scene->currentSceneIdx;
|
|
_gameState->inventoryMagicPoints = _gameState->magicLevelIdx * 20;
|
|
|
|
_grid->newCameraX = (_scene->sceneHero->x >> 9);
|
|
_grid->newCameraY = (_scene->sceneHero->y >> 8);
|
|
_grid->newCameraZ = (_scene->sceneHero->z >> 9);
|
|
|
|
_scene->heroPositionType = kReborn;
|
|
|
|
_scene->sceneHero->life = 50;
|
|
_redraw->reqBgRedraw = 1;
|
|
_screens->lockPalette = 1;
|
|
_gameState->inventoryNumLeafs--;
|
|
_actor->cropBottomScreen = 0;
|
|
} else { // game over
|
|
_gameState->inventoryNumLeafsBox = 2;
|
|
_gameState->inventoryNumLeafs = 1;
|
|
_gameState->inventoryMagicPoints = _gameState->magicLevelIdx * 20;
|
|
_actor->heroBehaviour = _actor->previousHeroBehaviour;
|
|
actor->angle = _actor->previousHeroAngle;
|
|
actor->life = 50;
|
|
|
|
if (_scene->previousSceneIdx != _scene->currentSceneIdx) {
|
|
_scene->newHeroX = -1;
|
|
_scene->newHeroY = -1;
|
|
_scene->newHeroZ = -1;
|
|
_scene->currentSceneIdx = _scene->previousSceneIdx;
|
|
}
|
|
|
|
_gameState->saveGame();
|
|
_gameState->processGameoverAnimation();
|
|
quitGame = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
} else {
|
|
_actor->processActorCarrier(a);
|
|
actor->dynamicFlags.bIsDead = 1;
|
|
actor->entity = -1;
|
|
actor->zone = -1;
|
|
}
|
|
}
|
|
|
|
if (_scene->needChangeScene != -1) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// recenter screen automatically
|
|
if (!disableScreenRecenter && !_debugGrid->useFreeCamera) {
|
|
ActorStruct *actor = &_scene->sceneActors[_scene->currentlyFollowedActor];
|
|
_renderer->projectPositionOnScreen(actor->x - (_grid->newCameraX << 9),
|
|
actor->y - (_grid->newCameraY << 8),
|
|
actor->z - (_grid->newCameraZ << 9));
|
|
if (_renderer->projPosX < 80 || _renderer->projPosX > 539 || _renderer->projPosY < 80 || _renderer->projPosY > 429) {
|
|
_grid->newCameraX = ((actor->x + 0x100) >> 9) + (((actor->x + 0x100) >> 9) - _grid->newCameraX) / 2;
|
|
_grid->newCameraY = actor->y >> 8;
|
|
_grid->newCameraZ = ((actor->z + 0x100) >> 9) + (((actor->z + 0x100) >> 9) - _grid->newCameraZ) / 2;
|
|
|
|
if (_grid->newCameraX >= 64) {
|
|
_grid->newCameraX = 63;
|
|
}
|
|
|
|
if (_grid->newCameraZ >= 64) {
|
|
_grid->newCameraZ = 63;
|
|
}
|
|
|
|
_redraw->reqBgRedraw = 1;
|
|
}
|
|
}
|
|
|
|
_redraw->redrawEngineActions(_redraw->reqBgRedraw);
|
|
|
|
// workaround to fix hero redraw after drowning
|
|
if (_actor->cropBottomScreen && _redraw->reqBgRedraw == 1) {
|
|
_scene->sceneHero->staticFlags.bIsHidden = 1;
|
|
_redraw->redrawEngineActions(1);
|
|
_scene->sceneHero->staticFlags.bIsHidden = 0;
|
|
}
|
|
|
|
_scene->needChangeScene = -1;
|
|
_redraw->reqBgRedraw = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool TwinEEngine::gameEngineLoop() { // mainLoop
|
|
uint32 start;
|
|
|
|
_redraw->reqBgRedraw = 1;
|
|
_screens->lockPalette = 1;
|
|
_movements->setActorAngle(0, -256, 5, &loopMovePtr);
|
|
|
|
while (quitGame == -1) {
|
|
start = g_system->getMillis();
|
|
|
|
while (g_system->getMillis() < start + cfgfile.Fps) {
|
|
if (runGameEngine()) {
|
|
return true;
|
|
}
|
|
g_system->delayMillis(10);
|
|
}
|
|
lbaTime++;
|
|
if (shouldQuit()) {
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void TwinEEngine::delaySkip(uint32 time) {
|
|
uint32 startTicks = _system->getMillis();
|
|
uint32 stopTicks = 0;
|
|
_keyboard.internalKeyCode = 0;
|
|
do {
|
|
readKeys();
|
|
if (_keyboard.internalKeyCode == 1) {
|
|
break;
|
|
}
|
|
if (shouldQuit()) {
|
|
break;
|
|
}
|
|
stopTicks = _system->getMillis() - startTicks;
|
|
_system->delayMillis(1);
|
|
//lbaTime++;
|
|
} while (stopTicks <= time);
|
|
}
|
|
|
|
void TwinEEngine::setPalette(const uint32 *palette) {
|
|
#if 1
|
|
uint8 pal[NUMOFCOLORS * 3];
|
|
uint8 *out = pal;
|
|
const uint8 *in = (const uint8 *)palette;
|
|
for (int i = 0; i < NUMOFCOLORS; i++) {
|
|
out[0] = in[0];
|
|
out[1] = in[1];
|
|
out[2] = in[2];
|
|
out += 3;
|
|
in += 4;
|
|
}
|
|
g_system->getPaletteManager()->setPalette(pal, 0, 256);
|
|
#else
|
|
frontVideoBuffer.setPalette(palette, 0, 256);
|
|
workVideoBuffer.setPalette(palette, 0, 256);
|
|
#endif
|
|
flip();
|
|
}
|
|
|
|
void TwinEEngine::flip() {
|
|
g_system->copyRectToScreen(frontVideoBuffer.getPixels(), frontVideoBuffer.pitch, 0, 0, frontVideoBuffer.w, frontVideoBuffer.h);
|
|
g_system->updateScreen();
|
|
}
|
|
|
|
void TwinEEngine::copyBlockPhys(int32 left, int32 top, int32 right, int32 bottom) {
|
|
assert(left < right);
|
|
assert(top < bottom);
|
|
// TODO: fix this - looks like the palette includes a color key at pos 0
|
|
g_system->copyRectToScreen(frontVideoBuffer.getPixels(), frontVideoBuffer.pitch, left, top, right - left + 1, bottom - top + 1);
|
|
g_system->updateScreen();
|
|
}
|
|
|
|
void TwinEEngine::crossFade(const Graphics::ManagedSurface &buffer, const uint32 *palette) {
|
|
Graphics::ManagedSurface backupSurface;
|
|
Graphics::ManagedSurface newSurface;
|
|
Graphics::ManagedSurface tempSurface;
|
|
Graphics::ManagedSurface surfaceTable;
|
|
|
|
Graphics::PixelFormat fmt(4, 8, 8, 8, 8, 24, 16, 8, 0);
|
|
backupSurface.create(frontVideoBuffer.w, frontVideoBuffer.h, fmt);
|
|
newSurface.create(frontVideoBuffer.w, frontVideoBuffer.h, fmt);
|
|
tempSurface.create(frontVideoBuffer.w, frontVideoBuffer.h, Graphics::PixelFormat::createFormatCLUT8());
|
|
tempSurface.setPalette(palette, 0, 256);
|
|
|
|
surfaceTable.create(frontVideoBuffer.w, frontVideoBuffer.h, fmt);
|
|
|
|
backupSurface.transBlitFrom(frontVideoBuffer);
|
|
newSurface.transBlitFrom(tempSurface);
|
|
|
|
for (int32 i = 0; i < 8; i++) {
|
|
surfaceTable.blitFrom(backupSurface);
|
|
surfaceTable.transBlitFrom(newSurface, 0, false, 0, i * 32);
|
|
frontVideoBuffer.blitFrom(surfaceTable);
|
|
flip();
|
|
delaySkip(50);
|
|
}
|
|
|
|
frontVideoBuffer.blitFrom(newSurface);
|
|
flip();
|
|
|
|
backupSurface.free();
|
|
newSurface.free();
|
|
tempSurface.free();
|
|
surfaceTable.free();
|
|
}
|
|
|
|
/** Pressed key char map - scanCodeTab2 */
|
|
static const struct KeyProperties {
|
|
uint8 high;
|
|
bool pressed;
|
|
uint8 key;
|
|
} pressedKeyCharMap[] = {
|
|
{0x01, false, 0x48}, // up
|
|
{0x02, false, 0x50}, // down
|
|
{0x04, false, 0x4B}, // left
|
|
{0x08, false, 0x4D}, // right
|
|
{0x05, false, 0x47}, // home
|
|
{0x09, false, 0x49}, // pageup
|
|
{0x0A, false, 0x51}, // pagedown
|
|
{0x06, false, 0x4F}, // end
|
|
{0x01, true, 0x39}, // space bar
|
|
{0x02, true, 0x1C}, // enter
|
|
{0x04, true, 0x1D}, // ctrl
|
|
{0x08, true, 0x38}, // alt
|
|
{0x10, true, 0x53}, // del
|
|
{0x20, true, 0x2A}, // left shift
|
|
{0x20, true, 0x36}, // right shift
|
|
{0x01, true, 0x3B}, // F1
|
|
{0x02, true, 0x3C}, // F2
|
|
{0x04, true, 0x3D}, // F3
|
|
{0x08, true, 0x3E}, // F4
|
|
{0x10, true, 0x3F}, // F5
|
|
{0x20, true, 0x40}, // F6
|
|
{0x40, true, 0x41}, // F7
|
|
{0x80, true, 0x42}, // F8
|
|
{0x01, true, 0x43}, // F9
|
|
{0x02, true, 0x44}, // F10
|
|
{0x04, true, 0x57}, // ?
|
|
{0x08, true, 0x58}, // ?
|
|
{0x00, true, 0x2A}, // left shift
|
|
{0x00, true, 0x00},
|
|
{0x00, false, 0x00},
|
|
{0x00, false, 0x00}};
|
|
static_assert(ARRAYSIZE(pressedKeyCharMap) == 31, "Expected size of key char map");
|
|
|
|
void TwinEEngine::readKeys() {
|
|
if (shouldQuit()) {
|
|
_keyboard.internalKeyCode = 1;
|
|
_keyboard.skippedKey = 1;
|
|
return;
|
|
}
|
|
_keyboard.skippedKey = 0;
|
|
_keyboard.internalKeyCode = 0;
|
|
|
|
Common::Event event;
|
|
while (g_system->getEventManager()->pollEvent(event)) {
|
|
uint8 localKey = 0;
|
|
switch (event.type) {
|
|
case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
|
|
_keyboard.actionStates[event.customType] = false;
|
|
localKey = twineactions[event.customType].localKey;
|
|
break;
|
|
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
|
|
if (!cfgfile.Debug) {
|
|
switch (event.customType) {
|
|
case TwinEActionType::NextRoom:
|
|
case TwinEActionType::PreviousRoom:
|
|
case TwinEActionType::ApplyCellingGrid:
|
|
case TwinEActionType::IncreaseCellingGridIndex:
|
|
case TwinEActionType::DecreaseCellingGridIndex:
|
|
break;
|
|
default:
|
|
localKey = twineactions[event.customType].localKey;
|
|
_keyboard.actionStates[event.customType] = true;
|
|
break;
|
|
}
|
|
} else {
|
|
localKey = twineactions[event.customType].localKey;
|
|
_keyboard.actionStates[event.customType] = true;
|
|
}
|
|
break;
|
|
case Common::EVENT_KEYUP:
|
|
_keyboard.pressedKey = 0;
|
|
break;
|
|
case Common::EVENT_LBUTTONDOWN:
|
|
leftMouse = 1;
|
|
break;
|
|
case Common::EVENT_RBUTTONDOWN:
|
|
rightMouse = 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (localKey == 0) {
|
|
continue;
|
|
}
|
|
|
|
for (int i = 0; i < ARRAYSIZE(pressedKeyCharMap); i++) {
|
|
if (pressedKeyCharMap[i].key == localKey) {
|
|
if (pressedKeyCharMap[i].pressed) {
|
|
_keyboard.pressedKey |= pressedKeyCharMap[i].high;
|
|
} else {
|
|
_keyboard.skippedKey |= pressedKeyCharMap[i].high;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
_keyboard.internalKeyCode = localKey;
|
|
}
|
|
}
|
|
|
|
void TwinEEngine::drawText(int32 x, int32 y, const char *string, int32 center) {
|
|
#if 0 // TODO
|
|
SDL_Color white = {0xFF, 0xFF, 0xFF, 0};
|
|
SDL_Color *forecol = &white;
|
|
SDL_Rect rectangle;
|
|
Graphics::ManagedSurface *text = TTF_RenderText_Solid(font, string, *forecol);
|
|
|
|
if (center) {
|
|
rectangle.x = x - (text->w / 2);
|
|
} else {
|
|
rectangle.x = x;
|
|
}
|
|
rectangle.y = y - 2;
|
|
rectangle.w = text->w;
|
|
rectangle.h = text->h;
|
|
|
|
SDL_BlitSurface(text, NULL, screenBuffer, &rectangle);
|
|
SDL_FreeSurface(text);
|
|
#endif
|
|
}
|
|
|
|
void TwinEEngine::getMousePositions(MouseStatusStruct *mouseData) {
|
|
Common::Point point = g_system->getEventManager()->getMousePos();
|
|
mouseData->x = point.x;
|
|
mouseData->y = point.y;
|
|
mouseData->left = leftMouse;
|
|
mouseData->right = rightMouse;
|
|
leftMouse = 0;
|
|
rightMouse = 0;
|
|
}
|
|
|
|
} // namespace TwinE
|