Files
2026-02-02 04:50:13 +01:00

335 lines
8.6 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "got/game/back.h"
#include "common/file.h"
#include "got/events.h"
#include "got/game/boss1.h"
#include "got/game/boss2.h"
#include "got/game/boss3.h"
#include "got/game/move.h"
#include "got/game/object.h"
#include "got/game/script.h"
#include "got/gfx/image.h"
#include "got/vars.h"
namespace Got {
const char *OBJECT_NAMES[] = {
"Shrub", "Child's Doll", "UNUSED", "FUTURE",
"FUTURE", "FUTURE", "FUTURE", "FUTURE", "FUTURE",
"FUTURE", "FUTURE", "FUTURE", "FUTURE", "FUTURE",
"FUTURE"};
const char *ITEM_NAMES[] = {
"Enchanted Apple", "Lightning Power",
"Winged Boots", "Wind Power",
"Amulet of Protection", "Thunder Power"};
static const char *odinEndMessage;
void showLevel(const int newLevel) {
if (_G(area) == 2 && newLevel == 105) { // Shovel Maze
_G(thorInfo)._armor = 2; // eyeballs mode
loadNewThor();
_G(eyeballs) = 1;
} else if (_G(eyeballs) == 1) {
_G(setup).f25 = 0;
_G(thorInfo)._armor = 1;
loadNewThor();
_G(eyeballs) = 0;
}
_G(bossActive) = false;
if (!_G(shieldOn))
_G(actor[2])._active = false;
_G(slipping) = false;
if (_G(scrn)._iconGrid[_G(thor)->_centerY][_G(thor)->_centerX] == 154)
_G(thor)->_dir = 0;
// The original copied 130 bytes from _G(scrn).static_object onwards into sd_data.
// This doesn't make sense, because that would put the ending in the middle of _staticY.
// Plus, it follows with an entire copy of scrn into sd_data anyway, so the first
// move seems entirely redundant.
_G(scrn).save(_G(currentLevel));
_G(scrn).load(_G(newLevel));
_G(levelMusic) = _G(scrn)._music;
_G(thor)->_nextFrame = 0;
showObjects();
showEnemies();
// The original was probably shortly displaying Thor in direction 0 before switching back to its prior position.
// This behavior wasn't noticed during initial playthrough by Dreammaster - Warning has been added so it can be checked eventually.
if (_G(scrn)._iconGrid[_G(thor)->_centerY][_G(thor)->_centerX] == 154)
warning("showLevel - Potential short move missing");
if (_G(warpFlag))
_G(currentLevel) = newLevel - 5; // Force phase
_G(warpFlag) = false;
if (_G(warpScroll)) {
_G(warpScroll) = false;
if (_G(thor)->_dir == 0)
_G(currentLevel) = newLevel + 10;
else if (_G(thor)->_dir == 1)
_G(currentLevel) = newLevel - 10;
else if (_G(thor)->_dir == 2)
_G(currentLevel) = newLevel + 1;
else if (_G(thor)->_dir == 3)
_G(currentLevel) = newLevel - 1;
}
if (!_G(setup)._scrollFlag)
_G(currentLevel) = newLevel; // Force no scroll
switch (_G(newLevel) - _G(currentLevel)) {
case 0:
// Nothing to do
showLevelDone();
break;
case -1:
_G(gameMode) = MODE_AREA_CHANGE;
_G(transitionDir) = DIR_LEFT;
break;
case 1:
_G(gameMode) = MODE_AREA_CHANGE;
_G(transitionDir) = DIR_RIGHT;
break;
case -10:
_G(gameMode) = MODE_AREA_CHANGE;
_G(transitionDir) = DIR_UP;
break;
case 10:
_G(gameMode) = MODE_AREA_CHANGE;
_G(transitionDir) = DIR_DOWN;
break;
default:
_G(gameMode) = MODE_AREA_CHANGE;
_G(transitionDir) = DIR_PHASED;
break;
}
}
void showLevelDone() {
_G(currentLevel) = _G(newLevel);
_G(thorInfo)._lastHealth = _G(thor)->_health;
_G(thorInfo)._lastMagic = _G(thorInfo)._magic;
_G(thorInfo)._lastJewels = _G(thorInfo)._jewels;
_G(thorInfo)._lastKeys = _G(thorInfo)._keys;
_G(thorInfo)._lastScore = _G(thorInfo)._score;
_G(thorInfo)._lastItem = _G(thorInfo)._selectedItem;
_G(thorInfo)._lastScreen = _G(currentLevel);
_G(thorInfo)._lastIcon = ((_G(thor)->_x + 8) / 16) + (((_G(thor)->_y + 14) / 16) * 20);
_G(thorInfo)._lastDir = _G(thor)->_dir;
_G(thorInfo)._lastInventory = _G(thorInfo)._inventory;
_G(thorInfo)._lastObject = _G(thorInfo)._object;
_G(thorInfo)._lastObjectName = _G(thorInfo)._objectName;
_G(lastSetup) = _G(setup);
bool f = true;
if (GAME1 && _G(newLevel) == BOSS_LEVEL1 && !_G(setup)._bossDead[0]) {
boss1SetupLevel();
f = false;
}
if (GAME2 && _G(newLevel) == BOSS_LEVEL2 && !_G(setup)._bossDead[1]) {
boss2SetupLevel();
f = false;
}
if (GAME3) {
if (_G(newLevel) == BOSS_LEVEL3 && !_G(setup)._bossDead[2]) {
boss3SetupLevel();
f = false;
}
if (_G(currentLevel) == ENDING_SCREEN) {
endingScreen();
f = false;
}
}
if (_G(startup))
f = false;
if (f)
musicPlay(_G(levelMusic), false);
}
static void odin_speaks_end() {
// In case Thor is now dead, flag as such
if (!_G(thor)->_health) {
_G(thor)->_show = 0;
_G(exitFlag) = 2;
}
// If there's an end message, pass it on to the view hierarchy.
// This is used in cases like the game end where multiple
// odinSpeaks are done in sequence
if (odinEndMessage)
g_events->send(GameMessage(odinEndMessage));
}
void odinSpeaks(const int index, int item, const char *endMessage) {
odinEndMessage = endMessage;
executeScript((long)index, _G(odin), odin_speaks_end);
}
int switchIcons() {
playSound(WOOP, false);
for (int y = 0; y < 12; y++) {
for (int x = 0; x < 20; x++) {
const int ix = x * 16;
const int iy = y * 16;
if (_G(scrn)._iconGrid[y][x] == 93) {
placeTile(x, y, 144);
} else if (_G(scrn)._iconGrid[y][x] == 144) {
placeTile(x, y, 93);
killEnemies(iy, ix);
}
if (_G(scrn)._iconGrid[y][x] == 94) {
placeTile(x, y, 146);
} else if (_G(scrn)._iconGrid[y][x] == 146) {
placeTile(x, y, 94);
killEnemies(iy, ix);
}
}
}
return 0;
}
int rotateArrows() {
playSound(WOOP, false);
for (int y = 0; y < 12; y++) {
for (int x = 0; x < 20; x++) {
if (_G(scrn)._iconGrid[y][x] == 205)
placeTile(x, y, 208);
else if (_G(scrn)._iconGrid[y][x] == 206)
placeTile(x, y, 207);
else if (_G(scrn)._iconGrid[y][x] == 207)
placeTile(x, y, 205);
else if (_G(scrn)._iconGrid[y][x] == 208)
placeTile(x, y, 206);
}
}
return 0;
}
void killEnemies(const int iy, const int ix) {
int x1, y1, x2, y2;
for (int i = 3; i < MAX_ACTORS; i++) {
if (_G(actor[i])._active) {
x1 = _G(actor[i])._x;
y1 = _G(actor[i])._y + _G(actor[i])._sizeY - 2;
x2 = (_G(actor[i])._x + _G(actor[i])._sizeX);
y2 = _G(actor[i])._y + _G(actor[i])._sizeY - 1;
if (pointWithin(x1, y1, ix, iy, ix + 15, iy + 15) || pointWithin(x2, y1, ix, iy, ix + 15, iy + 15)
|| pointWithin(x1, y2, ix, iy, ix + 15, iy + 15) || pointWithin(x2, y2, ix, iy, ix + 15, iy + 15))
actorDestroyed(&_G(actor[i]));
}
}
x1 = _G(thor)->_x;
y1 = _G(thor)->_y + 11;
x2 = x1 + 13;
y2 = y1 + 5;
if (pointWithin(x1, y1, ix, iy, ix + 15, iy + 15) || pointWithin(x2, y1, ix, iy, ix + 15, iy + 15)
|| pointWithin(x1, y2, ix, iy, ix + 15, iy + 15) || pointWithin(x2, y2, ix, iy, ix + 15, iy + 15)) {
if (!_G(cheats)._freezeHealth) {
_G(thor)->_health = 0;
g_events->send(GameMessage("THOR_DIES"));
}
}
}
void removeObjects(const int y, const int x) {
const int p = (y * 20) + x;
if (_G(objectMap[p]) > 0) {
_G(objectMap[p]) = 0;
_G(objectIndex[p]) = 0;
}
}
void placeTile(const int x, const int y, const int tile) {
_G(scrn)._iconGrid[y][x] = tile;
removeObjects(y, x);
}
int backgroundTile(int x, int y) {
if (x < 0 || x >= 319 || y < 0 || y >= 191)
return 0;
x = (x + 1) >> 4;
y = (y + 1) >> 4;
return _G(scrn)._iconGrid[y][x];
}
void selectItem() {
// Only allow opening the dialog if something isn't currently going on
if (g_engine->canSaveAutosaveCurrently()) {
g_events->addView("SelectItem");
}
}
void actorSpeaks(const Actor *actor, int index, int item) {
if (actor->_type != 4)
return;
const int v = atoi(actor->_name);
if (v < 1 || v > 20)
return;
long lind = (long)_G(currentLevel);
lind = lind * 1000;
lind += (long)actor->_actorNum;
const Common::String str = Common::String::format("FACE%d", v);
if (Common::File::exists(Common::Path(str))) {
Gfx::Pics pics(str, 262);
executeScript(lind, pics);
} else {
executeScript(lind, _G(odin));
}
if (!_G(thor)->_health) {
_G(thor)->_show = 0;
_G(exitFlag) = 2;
}
}
} // namespace Got