Initial commit
This commit is contained in:
2
engines/wage/POTFILES
Normal file
2
engines/wage/POTFILES
Normal file
@@ -0,0 +1,2 @@
|
||||
engines/wage/metaengine.cpp
|
||||
engines/wage/saveload.cpp
|
||||
1054
engines/wage/combat.cpp
Normal file
1054
engines/wage/combat.cpp
Normal file
File diff suppressed because it is too large
Load Diff
3
engines/wage/configure.engine
Normal file
3
engines/wage/configure.engine
Normal file
@@ -0,0 +1,3 @@
|
||||
# This file is included from the main "configure" script
|
||||
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
|
||||
add_engine wage "WAGE" yes "" "" "highres" "imgui"
|
||||
6
engines/wage/credits.pl
Normal file
6
engines/wage/credits.pl
Normal file
@@ -0,0 +1,6 @@
|
||||
begin_section("WAGE");
|
||||
add_person("Alikhan Balpykov", "Alikhan", "");
|
||||
add_person("Eugene Sandulenko", "sev", "");
|
||||
|
||||
add_person("Alexei Svitkine", "asvitkine", "(Java implementation author)");
|
||||
end_section();
|
||||
96
engines/wage/debugger.cpp
Normal file
96
engines/wage/debugger.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
/* 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 "common/file.h"
|
||||
#include "wage/wage.h"
|
||||
#include "wage/debugger.h"
|
||||
#include "wage/entities.h"
|
||||
#include "wage/script.h"
|
||||
#include "wage/world.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
Debugger::Debugger(WageEngine *engine) : GUI::Debugger(), _engine(engine) {
|
||||
registerCmd("continue", WRAP_METHOD(Debugger, cmdExit));
|
||||
registerCmd("scenes", WRAP_METHOD(Debugger, Cmd_ListScenes));
|
||||
registerCmd("script", WRAP_METHOD(Debugger, Cmd_Script));
|
||||
}
|
||||
|
||||
Debugger::~Debugger() {
|
||||
}
|
||||
|
||||
static int strToInt(const char *s) {
|
||||
if (!*s)
|
||||
// No string at all
|
||||
return 0;
|
||||
else if (toupper(s[strlen(s) - 1]) != 'H')
|
||||
// Standard decimal string
|
||||
return atoi(s);
|
||||
|
||||
// Hexadecimal string
|
||||
uint tmp = 0;
|
||||
int read = sscanf(s, "%xh", &tmp);
|
||||
|
||||
if (read < 1)
|
||||
error("strToInt failed on string \"%s\"", s);
|
||||
return (int)tmp;
|
||||
}
|
||||
|
||||
bool Debugger::Cmd_ListScenes(int argc, const char **argv) {
|
||||
int currentScene = 0;
|
||||
|
||||
for (uint i = 1; i < _engine->_world->_orderedScenes.size(); i++) { // #0 is STORAGE@
|
||||
if (_engine->_world->_player->_currentScene == _engine->_world->_orderedScenes[i])
|
||||
currentScene = i;
|
||||
|
||||
debugPrintf("%d: %s\n", i, _engine->_world->_orderedScenes[i]->_name.c_str());
|
||||
}
|
||||
|
||||
debugPrintf("\nCurrent scene is #%d: %s\n", currentScene, _engine->_world->_orderedScenes[currentScene]->_name.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::Cmd_Script(int argc, const char **argv) {
|
||||
Script *script = _engine->_world->_player->_currentScene->_script;
|
||||
|
||||
if (argc >= 2) {
|
||||
int scriptNum = strToInt(argv[1]);
|
||||
|
||||
if (scriptNum)
|
||||
script = _engine->_world->_orderedScenes[scriptNum]->_script;
|
||||
else
|
||||
script = _engine->_world->_globalScript;
|
||||
}
|
||||
|
||||
if (script == NULL) {
|
||||
debugPrintf("There is no script for current scene\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < script->_scriptText.size(); i++) {
|
||||
debugPrintf("%d [%04x]: %s\n", i, script->_scriptText[i]->offset, script->_scriptText[i]->line.c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Wage
|
||||
46
engines/wage/debugger.h
Normal file
46
engines/wage/debugger.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WAGE_DEBUGGER_H
|
||||
#define WAGE_DEBUGGER_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "gui/debugger.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
class WageEngine;
|
||||
|
||||
class Debugger : public GUI::Debugger {
|
||||
protected:
|
||||
WageEngine *_engine;
|
||||
|
||||
bool Cmd_ListScenes(int argc, const char **argv);
|
||||
bool Cmd_Script(int argc, const char **argv);
|
||||
|
||||
public:
|
||||
Debugger(WageEngine *engine);
|
||||
~Debugger() override;
|
||||
};
|
||||
|
||||
} // End of namespace Wage
|
||||
|
||||
#endif
|
||||
541
engines/wage/debugtools.cpp
Normal file
541
engines/wage/debugtools.cpp
Normal file
@@ -0,0 +1,541 @@
|
||||
/* 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 "backends/imgui/IconsMaterialSymbols.h"
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
#include "backends/imgui/imgui.h"
|
||||
#include "backends/imgui/imgui_fonts.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
#include "wage/wage.h"
|
||||
#include "wage/dt-internal.h"
|
||||
#include "wage/design.h"
|
||||
#include "wage/gui.h"
|
||||
#include "wage/script.h"
|
||||
#include "wage/sound.h"
|
||||
#include "wage/world.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
ImGuiState *_state = nullptr;
|
||||
|
||||
ImGuiImage getPatternImage(int fillType) {
|
||||
int index = fillType - 1;
|
||||
if (index >= 0 && index < (int)_state->_patternTextures.size()) {
|
||||
return _state->_patternTextures[index];
|
||||
}
|
||||
return {0, 0, 0};
|
||||
}
|
||||
|
||||
void drawThicknessIcon(int thickness) {
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
ImDrawList *dl = ImGui::GetWindowDrawList();
|
||||
|
||||
// draw Border
|
||||
dl->AddRect(pos, pos + ImVec2(16, 16), IM_COL32(255, 255, 255, 255));
|
||||
|
||||
float half = thickness * 0.5f;
|
||||
// draw Bar
|
||||
dl->AddRectFilled(
|
||||
ImVec2(pos.x, pos.y + 8 - half),
|
||||
ImVec2(pos.x + 16, pos.y + 8 + half),
|
||||
IM_COL32(255, 255, 255, 255));
|
||||
|
||||
ImGui::Dummy(ImVec2(16, 16));
|
||||
ImGui::SetItemTooltip("Line thickness: %d", thickness);
|
||||
}
|
||||
|
||||
void drawMissingPatternIcon() {
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
ImDrawList *dl = ImGui::GetWindowDrawList();
|
||||
float size = 16.0f;
|
||||
|
||||
dl->AddLine(pos, pos + ImVec2(size, size), IM_COL32(255, 0, 0, 255));
|
||||
dl->AddRect(pos, pos + ImVec2(size, size), IM_COL32(255, 255, 255, 255));
|
||||
ImGui::Dummy(ImVec2(size, size));
|
||||
}
|
||||
|
||||
ImGuiImage getImageID(Designed *d, const char *type, int steps = -1) {
|
||||
Common::String key = Common::String::format("%s:%s:%d", d->_name.c_str(), type, steps);
|
||||
|
||||
if (_state->_images.contains(key))
|
||||
return _state->_images[key];
|
||||
|
||||
if (!d->_design)
|
||||
return { 0, 0, 0 };
|
||||
|
||||
// We need to precompute dimensions
|
||||
Graphics::ManagedSurface tmpsurf(1024, 768, Graphics::PixelFormat::createFormatCLUT8());
|
||||
d->_design->paint(&tmpsurf, *g_wage->_world->_patterns, 0, 0);
|
||||
|
||||
int sx = d->_design->getBounds()->width(), sy = d->_design->getBounds()->height();
|
||||
|
||||
if (!sx || !sy)
|
||||
return { 0, 0, 0 };
|
||||
|
||||
Graphics::ManagedSurface *surface = new Graphics::ManagedSurface();
|
||||
surface->create(sx, sy, Graphics::PixelFormat::createFormatCLUT8());
|
||||
surface->fillRect(Common::Rect(0, 0, sx, sy), 4); // white background
|
||||
|
||||
d->_design->paint(surface, *g_wage->_world->_patterns, 0, 0, steps);
|
||||
|
||||
if (surface->surfacePtr()) {
|
||||
_state->_images[key] = { (ImTextureID)g_system->getImGuiTexture(*surface->surfacePtr(), g_wage->_gui->_wm->getPalette(), g_wage->_gui->_wm->getPaletteSize()), sx, sy };
|
||||
}
|
||||
|
||||
delete surface;
|
||||
|
||||
return _state->_images[key];
|
||||
}
|
||||
|
||||
void showImage(const ImGuiImage &image, float scale) {
|
||||
ImVec2 size = { (float)image.width * scale, (float)image.height * scale };
|
||||
|
||||
ImGui::BeginGroup();
|
||||
ImVec2 screenPos = ImGui::GetCursorScreenPos();
|
||||
ImGui::Image(image.id, size);
|
||||
|
||||
ImGui::GetWindowDrawList()->AddRect(screenPos, screenPos + ImVec2(size.x, size.y), 0xFFFFFFFF);
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
void showDesignScriptWindow(Design *d) {
|
||||
if (!_state->_showScriptWindow)
|
||||
return;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
|
||||
|
||||
if (ImGui::Begin("Design Script", &_state->_showScriptWindow)) {
|
||||
|
||||
for (uint i = 0; i < d->_drawOps.size(); i++) {
|
||||
const DrawOp &op = d->_drawOps[i];
|
||||
// check if this is the current step
|
||||
bool isCurrent = (_state->_currentStep == i + 1);
|
||||
|
||||
if (isCurrent) {
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
float width = ImGui::GetContentRegionAvail().x;
|
||||
ImDrawList *dl = ImGui::GetWindowDrawList();
|
||||
|
||||
ImU32 green = IM_COL32(0, 255, 0, 255);
|
||||
ImU32 greenTransparent = IM_COL32(0, 255, 0, 50);
|
||||
|
||||
// draw Arrow
|
||||
dl->AddText(pos, green, ICON_MS_ARROW_RIGHT_ALT);
|
||||
|
||||
// draw highlight
|
||||
dl->AddRectFilled(
|
||||
ImVec2(pos.x + 16.f, pos.y),
|
||||
ImVec2(pos.x + width, pos.y + 16.f),
|
||||
greenTransparent,
|
||||
0.4f);
|
||||
}
|
||||
|
||||
// offset text to the right so it doesn't overlap the arrow
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 16.0f);
|
||||
|
||||
// opcode
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "[%5d]", op.offset);
|
||||
ImGui::SameLine();
|
||||
|
||||
// draw current foreground fill pattern
|
||||
if (op.fillType > 0) {
|
||||
ImGuiImage patImg = getPatternImage(op.fillType);
|
||||
if (patImg.id) showImage(patImg, 1.0f);
|
||||
else drawMissingPatternIcon();
|
||||
|
||||
ImGui::SetItemTooltip("Foreground pattern: %d", op.fillType);
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
// draw current border fill pattern
|
||||
if (op.borderFillType > 0) {
|
||||
ImGuiImage patImg = getPatternImage(op.borderFillType);
|
||||
if (patImg.id) showImage(patImg, 1.0f);
|
||||
else drawMissingPatternIcon();
|
||||
|
||||
ImGui::SetItemTooltip("Border pattern: %d", op.borderFillType);
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
// draw line thickness icon
|
||||
if (op.borderThickness > 0) {
|
||||
drawThicknessIcon(op.borderThickness);
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
Common::String label = Common::String::format("%s##%d", op.opcode.c_str(), i);
|
||||
if (ImGui::Selectable(label.c_str(), isCurrent)) {
|
||||
_state->_currentStep = i + 1;
|
||||
}
|
||||
if (isCurrent) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void showDesignViewer(Designed *item, const char *typeStr) {
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
Design *d = item->_design;
|
||||
if (d) {
|
||||
if (d->_drawOps.empty()) {
|
||||
// force render to populate ops
|
||||
Graphics::ManagedSurface tmp;
|
||||
tmp.create(1, 1, Graphics::PixelFormat::createFormatCLUT8());
|
||||
d->paint(&tmp, *g_wage->_world->_patterns, 0, 0, -1);
|
||||
tmp.free();
|
||||
}
|
||||
|
||||
uint totalSteps = d->_drawOps.size();
|
||||
|
||||
if (ImGui::Button(ICON_MS_SKIP_PREVIOUS)) {
|
||||
if (_state->_currentStep > 1)
|
||||
_state->_currentStep--;
|
||||
else
|
||||
_state->_currentStep = totalSteps;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(ICON_MS_SKIP_NEXT)) {
|
||||
if (_state->_currentStep < totalSteps)
|
||||
_state->_currentStep++;
|
||||
else
|
||||
_state->_currentStep = 1;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Script")) {
|
||||
_state->_showScriptWindow = !_state->_showScriptWindow;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Step: %d / %d", (_state->_currentStep), totalSteps);
|
||||
|
||||
if (d->getBounds()) {
|
||||
ImGuiImage imgID = getImageID(item, typeStr, _state->_currentStep);
|
||||
showImage(imgID, 1.0);
|
||||
}
|
||||
showDesignScriptWindow(d);
|
||||
} else {
|
||||
ImGui::Text("No Design Data");
|
||||
}
|
||||
}
|
||||
|
||||
void resetStepToLast(Designed *d) {
|
||||
if (d && d->_design) {
|
||||
// if drawOps is empty, force a paint to calculate steps
|
||||
if (d->_design->_drawOps.empty()) {
|
||||
Graphics::ManagedSurface tmp(1, 1, Graphics::PixelFormat::createFormatCLUT8());
|
||||
d->_design->paint(&tmp, *g_wage->_world->_patterns, 0, 0, -1);
|
||||
}
|
||||
_state->_currentStep = d->_design->_drawOps.size();
|
||||
} else {
|
||||
_state->_currentStep = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void showWorld() {
|
||||
if (!_state->_showWorld)
|
||||
return;
|
||||
|
||||
// Calculate the viewport size
|
||||
ImVec2 viewportSize = ImGui::GetMainViewport()->Size;
|
||||
|
||||
// Calculate the window size
|
||||
ImVec2 windowSize = ImVec2(
|
||||
viewportSize.x * 0.9f,
|
||||
viewportSize.y * 0.9f
|
||||
);
|
||||
|
||||
// Calculate the centered position
|
||||
ImVec2 centeredPosition = ImVec2(
|
||||
(viewportSize.x - windowSize.x) * 0.5f,
|
||||
(viewportSize.y - windowSize.y) * 0.5f
|
||||
);
|
||||
|
||||
// Set the next window position and size
|
||||
ImGui::SetNextWindowPos(centeredPosition, ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSize(windowSize, ImGuiCond_FirstUseEver);
|
||||
|
||||
if (ImGui::Begin("World", &_state->_showWorld)) {
|
||||
ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None;
|
||||
|
||||
if (ImGui::BeginTabBar("MainTabBar", tab_bar_flags)) {
|
||||
if (ImGui::BeginTabItem("Scenes")) {
|
||||
|
||||
{ // Left pane
|
||||
ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.4f, ImGui::GetContentRegionAvail().y), ImGuiChildFlags_None);
|
||||
|
||||
if (ImGui::BeginListBox("##listbox scenes", ImVec2(-FLT_MIN, ImGui::GetContentRegionAvail().y))) {
|
||||
for (int n = 0; n < (int)g_wage->_world->_orderedScenes.size(); n++) {
|
||||
const bool is_selected = (_state->_selectedScene == n);
|
||||
Common::String label = Common::String::format("%s##%d", g_wage->_world->_orderedScenes[n]->_name.c_str(), n);
|
||||
if (ImGui::Selectable(label.c_str(), is_selected)) {
|
||||
_state->_selectedScene = n;
|
||||
resetStepToLast(g_wage->_world->_orderedScenes[n]);
|
||||
}
|
||||
|
||||
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
|
||||
if (is_selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
{ // Right pane
|
||||
ImGui::BeginChild("ChildR", ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y), ImGuiChildFlags_Borders);
|
||||
|
||||
if (ImGui::BeginTabBar("SceneTabBar", tab_bar_flags)) {
|
||||
if (ImGui::BeginTabItem("Script")) {
|
||||
if (g_wage->_world->_orderedScenes[_state->_selectedScene]->_script && g_wage->_world->_orderedScenes[_state->_selectedScene]->_script->_scriptText.size()) {
|
||||
for (auto &t : g_wage->_world->_orderedScenes[_state->_selectedScene]->_script->_scriptText) {
|
||||
ImGui::Text("[%4d]", t->offset);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", t->line.c_str());
|
||||
}
|
||||
} else {
|
||||
ImGui::Text("No script");
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Text")) {
|
||||
ImGui::TextWrapped("%s", g_wage->_world->_orderedScenes[_state->_selectedScene]->_text.c_str());
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Design")) {
|
||||
Designed *d = g_wage->_world->_orderedScenes[_state->_selectedScene];
|
||||
showDesignViewer(d, "scene");
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Objects")) {
|
||||
{ // Left pane
|
||||
ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.4f, ImGui::GetContentRegionAvail().y), ImGuiChildFlags_None);
|
||||
|
||||
if (ImGui::BeginListBox("##listbox objects", ImVec2(-FLT_MIN, ImGui::GetContentRegionAvail().y))) {
|
||||
for (int n = 0; n < (int)g_wage->_world->_orderedObjs.size(); n++) {
|
||||
const bool is_selected = (_state->_selectedObj == n);
|
||||
Common::String label = Common::String::format("%s##%d", g_wage->_world->_orderedObjs[n]->_name.c_str(), n);
|
||||
if (ImGui::Selectable(label.c_str(), is_selected)) {
|
||||
_state->_selectedObj = n;
|
||||
resetStepToLast(g_wage->_world->_orderedObjs[n]);
|
||||
}
|
||||
|
||||
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
|
||||
if (is_selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
{ // Right pane
|
||||
ImGui::BeginChild("ChildR", ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y), ImGuiChildFlags_Borders);
|
||||
|
||||
showDesignViewer(g_wage->_world->_orderedObjs[_state->_selectedObj], "obj");
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Characters")) {
|
||||
{ // Left pane
|
||||
ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.4f, ImGui::GetContentRegionAvail().y), ImGuiChildFlags_None);
|
||||
|
||||
if (ImGui::BeginListBox("##listbox characters", ImVec2(-FLT_MIN, ImGui::GetContentRegionAvail().y))) {
|
||||
for (int n = 0; n < (int)g_wage->_world->_orderedChrs.size(); n++) {
|
||||
const bool is_selected = (_state->_selectedChr == n);
|
||||
Common::String label = Common::String::format("%s##%d", g_wage->_world->_orderedChrs[n]->_name.c_str(), n);
|
||||
if (ImGui::Selectable(label.c_str(), is_selected)) {
|
||||
_state->_selectedChr = n;
|
||||
resetStepToLast(g_wage->_world->_orderedChrs[n]);
|
||||
}
|
||||
|
||||
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
|
||||
if (is_selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
{ // Right pane
|
||||
ImGui::BeginChild("ChildR", ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y), ImGuiChildFlags_Borders);
|
||||
|
||||
showDesignViewer(g_wage->_world->_orderedChrs[_state->_selectedChr], "chr");
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Sounds")) {
|
||||
{ // Left pane
|
||||
ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.4f, ImGui::GetContentRegionAvail().y), ImGuiChildFlags_None);
|
||||
|
||||
if (ImGui::BeginListBox("##listbox sounds", ImVec2(-FLT_MIN, ImGui::GetContentRegionAvail().y))) {
|
||||
for (int n = 0; n < (int)g_wage->_world->_orderedSounds.size(); n++) {
|
||||
const bool is_selected = (_state->_selectedSound == n);
|
||||
Common::String label = Common::String::format("%s##%d", g_wage->_world->_orderedSounds[n]->_name.c_str(), n);
|
||||
if (ImGui::Selectable(label.c_str(), is_selected))
|
||||
_state->_selectedSound = n;
|
||||
|
||||
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
|
||||
if (is_selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
{ // Right pane
|
||||
ImGui::BeginChild("ChildR", ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y), ImGuiChildFlags_Borders);
|
||||
|
||||
ImGui::Text("Sound playback");
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Global Script")) {
|
||||
for (auto &t : g_wage->_world->_globalScript->_scriptText) {
|
||||
ImGui::Text("[%4d]", t->offset);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", t->line.c_str());
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("World")) {
|
||||
ImGui::Text("This is the Global Script tab!\nblah blah blah blah blah");
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void onImGuiInit() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
io.Fonts->AddFontDefault();
|
||||
|
||||
ImFontConfig icons_config;
|
||||
icons_config.MergeMode = true;
|
||||
icons_config.PixelSnapH = false;
|
||||
icons_config.OversampleH = 3;
|
||||
icons_config.OversampleV = 3;
|
||||
icons_config.GlyphOffset = {0, 4};
|
||||
|
||||
static const ImWchar icons_ranges[] = {ICON_MIN_MS, ICON_MAX_MS, 0};
|
||||
ImGui::addTTFFontFromArchive("MaterialSymbolsSharp.ttf", 16.f, &icons_config, icons_ranges);
|
||||
|
||||
_state = new ImGuiState();
|
||||
|
||||
// pre-load patterns into array
|
||||
Graphics::MacPatterns &patterns = *g_wage->_world->_patterns;
|
||||
for (uint i = 0; i < patterns.size(); ++i) {
|
||||
Graphics::ManagedSurface surface(16, 16, Graphics::PixelFormat::createFormatCLUT8());
|
||||
Common::Rect rect(16, 16);
|
||||
Design::drawFilledRect(&surface, rect, kColorBlack, patterns, i + 1);
|
||||
|
||||
if (surface.surfacePtr()) {
|
||||
ImTextureID tex = (ImTextureID)g_system->getImGuiTexture(
|
||||
*surface.surfacePtr(),
|
||||
g_wage->_gui->_wm->getPalette(),
|
||||
g_wage->_gui->_wm->getPaletteSize());
|
||||
|
||||
_state->_patternTextures.push_back({tex, 16, 16});
|
||||
} else {
|
||||
_state->_patternTextures.push_back({0, 0, 0});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onImGuiRender() {
|
||||
if (!debugChannelSet(-1, kDebugImGui)) {
|
||||
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange | ImGuiConfigFlags_NoMouse;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_state)
|
||||
return;
|
||||
|
||||
ImGui::GetIO().ConfigFlags &= ~(ImGuiConfigFlags_NoMouseCursorChange | ImGuiConfigFlags_NoMouse);
|
||||
|
||||
if (ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("World", NULL, &_state->_showWorld);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
showWorld();
|
||||
}
|
||||
|
||||
void onImGuiCleanup() {
|
||||
delete _state;
|
||||
_state = nullptr;
|
||||
}
|
||||
|
||||
} // namespace Wage
|
||||
31
engines/wage/debugtools.h
Normal file
31
engines/wage/debugtools.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WAGE_DEBUGTOOLS_H
|
||||
#define WAGE_DEBUGTOOLS_H
|
||||
|
||||
namespace Wage {
|
||||
void onImGuiInit();
|
||||
void onImGuiRender();
|
||||
void onImGuiCleanup();
|
||||
} // namespace Wage
|
||||
|
||||
#endif // WAGE_DEBUGTOOLS_H
|
||||
759
engines/wage/design.cpp
Normal file
759
engines/wage/design.cpp
Normal file
@@ -0,0 +1,759 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "graphics/managed_surface.h"
|
||||
#include "graphics/primitives.h"
|
||||
#include "graphics/macgui/macwindowmanager.h"
|
||||
|
||||
#include "wage/design.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
struct PlotData {
|
||||
Graphics::ManagedSurface *surface;
|
||||
Graphics::MacPatterns *patterns;
|
||||
uint fillType;
|
||||
int thickness;
|
||||
Design *design;
|
||||
|
||||
PlotData(Graphics::ManagedSurface *s, Graphics::MacPatterns *p, int f, int t, Design *d) :
|
||||
surface(s), patterns(p), fillType(f), thickness(t), design(d) {}
|
||||
};
|
||||
|
||||
void drawPixelPlain(int x, int y, int color, void *data);
|
||||
|
||||
Design::Design(Common::SeekableReadStream *data) {
|
||||
_len = data->readUint16BE() - 2;
|
||||
_data = (byte *)malloc(_len);
|
||||
data->read(_data, _len);
|
||||
|
||||
_surface = NULL;
|
||||
_bounds = new Common::Rect();
|
||||
_maskImage = nullptr;
|
||||
|
||||
_boundsCalculationMode = false;
|
||||
_renderedSteps = 0;
|
||||
_lastOpString = "";
|
||||
}
|
||||
|
||||
Design::~Design() {
|
||||
free(_data);
|
||||
if (_surface)
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
delete _bounds;
|
||||
delete _maskImage;
|
||||
}
|
||||
|
||||
void Design::paint(Graphics::ManagedSurface *surface, Graphics::MacPatterns &patterns, int x, int y, int steps) {
|
||||
bool needRender = false;
|
||||
|
||||
if (_surface == NULL) {
|
||||
_boundsCalculationMode = true;
|
||||
_bounds->debugPrint(4, "Internal bounds:");
|
||||
render(patterns, -1);
|
||||
_boundsCalculationMode = false;
|
||||
if (_bounds->right == -10000) {
|
||||
_bounds->left = _bounds->top = _bounds->right = _bounds->bottom = 0;
|
||||
}
|
||||
_bounds->debugPrint(4, "Calculated bounds:");
|
||||
|
||||
_surface = new Graphics::ManagedSurface;
|
||||
_surface->create(_bounds->width() + 1, _bounds->height(), Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
_surface->clear(kColorGreen);
|
||||
|
||||
needRender = true;
|
||||
}
|
||||
|
||||
_bounds->debugPrint(4, "Using bounds:");
|
||||
#if 0
|
||||
PlotData pd(_surface, &patterns, 8, 1, this);
|
||||
int x1 = 50, y1 = 50, x2 = 200, y2 = 200, borderThickness = 30;
|
||||
Common::Rect inn(x1-5, y1-5, x2+5, y2+5);
|
||||
drawRoundRect(inn, 6, kColorGray80, false, drawPixelPlain, &pd);
|
||||
|
||||
drawThickLine(x1, y1, x2-borderThickness, y1, borderThickness, kColorBlack, drawPixel, &pd);
|
||||
drawThickLine(x2-borderThickness, y1, x2-borderThickness, y2, borderThickness, kColorBlack, drawPixel, &pd);
|
||||
drawThickLine(x2-borderThickness, y2-borderThickness, x1, y2-borderThickness, borderThickness, kColorBlack, drawPixel, &pd);
|
||||
drawThickLine(x1, y2-borderThickness, x1, y1, borderThickness, kColorBlack, drawPixel, &pd);
|
||||
drawThickLine(x2+10, y2+10, x2+100, y2+100, borderThickness, kColorBlack, drawPixel, &pd);
|
||||
|
||||
g_system->copyRectToScreen(_surface->getPixels(), _surface->pitch, 0, 0, _surface->w, _surface->h);
|
||||
|
||||
while (true) {
|
||||
((WageEngine *)g_engine)->processEvents();
|
||||
g_system->updateScreen();
|
||||
g_system->delayMillis(50);
|
||||
}
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (_drawOps.empty())
|
||||
render(patterns);
|
||||
|
||||
// only re-render if the requested step count changed
|
||||
if (needRender || _renderedSteps != steps) {
|
||||
_surface->clear(kColorGreen);
|
||||
render(patterns, steps);
|
||||
_renderedSteps = steps;
|
||||
}
|
||||
|
||||
if (_bounds->width() && _bounds->height()) {
|
||||
const int padding = 14;
|
||||
// Common::Rect from(padding, padding, _bounds->width() - 2 * padding, _bounds->height() - 2 * padding);
|
||||
// in order to restore the design, we just cut the left and top part of that
|
||||
Common::Rect from(padding, padding, _bounds->width(), _bounds->height());
|
||||
Common::Rect to(from);
|
||||
to.moveTo(x, y);
|
||||
surface->transBlitFrom(*_surface, from, to, kColorGreen);
|
||||
}
|
||||
}
|
||||
|
||||
void Design::render(Graphics::MacPatterns &patterns, int steps) {
|
||||
Common::MemoryReadStream in(_data, _len);
|
||||
bool needRender = true;
|
||||
int currentStep = 0;
|
||||
|
||||
while (needRender) {
|
||||
uint32 opOffset = in.pos();
|
||||
byte fillType = in.readByte();
|
||||
byte borderThickness = in.readByte();
|
||||
byte borderFillType = in.readByte();
|
||||
int type = in.readByte();
|
||||
|
||||
if (in.eos())
|
||||
break;
|
||||
|
||||
debug(8, "fill: %d borderFill: %d border: %d type: %d", fillType, borderFillType, borderThickness, type);
|
||||
|
||||
switch (type) {
|
||||
case 4:
|
||||
drawRect(_surface, in, patterns, fillType, borderThickness, borderFillType);
|
||||
break;
|
||||
case 8:
|
||||
drawRoundRect(_surface, in, patterns, fillType, borderThickness, borderFillType);
|
||||
break;
|
||||
case 12:
|
||||
drawOval(_surface, in, patterns, fillType, borderThickness, borderFillType);
|
||||
break;
|
||||
case 16:
|
||||
case 20:
|
||||
drawPolygon(_surface, in, patterns, fillType, borderThickness, borderFillType);
|
||||
break;
|
||||
case 24:
|
||||
drawBitmap(_surface, in);
|
||||
break;
|
||||
default:
|
||||
warning("Unknown type => %d", type);
|
||||
break;
|
||||
}
|
||||
|
||||
if (_boundsCalculationMode && !_lastOpString.empty()) {
|
||||
DrawOp op;
|
||||
op.offset = opOffset;
|
||||
op.fillType = fillType;
|
||||
op.borderThickness = borderThickness;
|
||||
op.borderFillType = borderFillType;
|
||||
op.opcode = _lastOpString;
|
||||
_drawOps.push_back(op);
|
||||
_lastOpString = "";
|
||||
}
|
||||
currentStep++;
|
||||
|
||||
if (steps > 0 && currentStep >= steps) {
|
||||
needRender = false;
|
||||
break;
|
||||
}
|
||||
//g_system->copyRectToScreen(_surface->getPixels(), _surface->pitch, 0, 0, _surface->w, _surface->h);
|
||||
//((WageEngine *)g_engine)->processEvents();
|
||||
//g_system->updateScreen();
|
||||
//g_system->delayMillis(500);
|
||||
}
|
||||
}
|
||||
|
||||
bool Design::isInBounds(int x, int y) {
|
||||
if (_surface == NULL)
|
||||
error("Design::isInBounds(): Surface is null");
|
||||
if (_maskImage == nullptr)
|
||||
return false;
|
||||
if (x >= _maskImage->w || y >= _maskImage->h)
|
||||
return false;
|
||||
|
||||
byte pixel = ((byte *)_maskImage->getBasePtr(x, y))[0];
|
||||
return pixel != kColorGreen;
|
||||
}
|
||||
|
||||
void Design::adjustBounds(int16 x, int16 y) {
|
||||
_bounds->right = MAX(x, _bounds->right);
|
||||
_bounds->bottom = MAX(y, _bounds->bottom);
|
||||
}
|
||||
|
||||
class PlotDataPrimitives : public Graphics::Primitives {
|
||||
void drawPoint(int x, int y, uint32 color, void *data) override {
|
||||
PlotData *p = (PlotData *)data;
|
||||
|
||||
if (p->fillType > p->patterns->size())
|
||||
return;
|
||||
|
||||
if (p->design && p->design->isBoundsCalculation()) {
|
||||
if (x < 0 || y < 0)
|
||||
return;
|
||||
if (p->thickness == 1) {
|
||||
p->design->adjustBounds(x, y);
|
||||
} else {
|
||||
int x1 = x;
|
||||
int x2 = x1 + p->thickness;
|
||||
int y1 = y;
|
||||
int y2 = y1 + p->thickness;
|
||||
|
||||
for (y = y1; y < y2; y++)
|
||||
for (x = x1; x < x2; x++)
|
||||
p->design->adjustBounds(x, y);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const byte *pat = p->patterns->operator[](p->fillType - 1);
|
||||
|
||||
if (p->thickness == 1) {
|
||||
if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) {
|
||||
uint xu = (uint)x; // for letting compiler optimize it
|
||||
uint yu = (uint)y;
|
||||
|
||||
*((byte *)p->surface->getBasePtr(xu, yu)) =
|
||||
(pat[yu % 8] & (1 << (7 - xu % 8))) ?
|
||||
color : (byte)kColorWhite;
|
||||
}
|
||||
} else {
|
||||
int x1 = x - p->thickness / 2;
|
||||
int x2 = x1 + p->thickness;
|
||||
int y1 = y - p->thickness / 2;
|
||||
int y2 = y1 + p->thickness;
|
||||
|
||||
for (y = y1; y < y2; y++)
|
||||
for (x = x1; x < x2; x++)
|
||||
if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) {
|
||||
uint xu = (uint)x; // for letting compiler optimize it
|
||||
uint yu = (uint)y;
|
||||
*((byte *)p->surface->getBasePtr(xu, yu)) =
|
||||
(pat[yu % 8] & (1 << (7 - xu % 8))) ?
|
||||
color : (byte)kColorWhite;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PlotDataCirclePrimitives : public Graphics::Primitives {
|
||||
void drawPoint(int x, int y, uint32 color, void *data) override {
|
||||
PlotData *p = (PlotData *)data;
|
||||
|
||||
if (p->fillType > p->patterns->size())
|
||||
return;
|
||||
|
||||
if (p->design && p->design->isBoundsCalculation()) {
|
||||
if (x < 0 || y < 0)
|
||||
return;
|
||||
if (p->thickness == 1) {
|
||||
p->design->adjustBounds(x, y);
|
||||
} else {
|
||||
int x1 = x;
|
||||
int x2 = x1 + p->thickness;
|
||||
int y1 = y;
|
||||
int y2 = y1 + p->thickness;
|
||||
|
||||
for (y = y1; y < y2; y++)
|
||||
for (x = x1; x < x2; x++)
|
||||
p->design->adjustBounds(x, y);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const byte *pat = p->patterns->operator[](p->fillType - 1);
|
||||
|
||||
// Draw circle when thickness is > 1, put a pixel otherwise
|
||||
if (p->thickness == 1) {
|
||||
if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) {
|
||||
uint xu = (uint)x; // for letting compiler optimize it
|
||||
uint yu = (uint)y;
|
||||
|
||||
*((byte *)p->surface->getBasePtr(xu, yu)) =
|
||||
(pat[yu % 8] & (1 << (7 - xu % 8))) ? color : (byte)kColorWhite;
|
||||
}
|
||||
} else {
|
||||
int x1 = x - p->thickness / 2;
|
||||
int x2 = x1 + p->thickness;
|
||||
int y1 = y - p->thickness / 2;
|
||||
int y2 = y1 + p->thickness;
|
||||
|
||||
PlotData pd(p->surface, p->patterns, p->fillType, 1, p->design);
|
||||
|
||||
subprimitives.drawEllipse(x1, y1, x2 - 1, y2 - 1, kColorBlack, true, &pd);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
PlotDataPrimitives subprimitives;
|
||||
};
|
||||
|
||||
void drawPixelPlain(int x, int y, int color, void *data) {
|
||||
PlotData *p = (PlotData *)data;
|
||||
|
||||
if (p->design && p->design->isBoundsCalculation()) {
|
||||
p->design->adjustBounds(x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h)
|
||||
*((byte *)p->surface->getBasePtr(x, y)) = (byte)color;
|
||||
}
|
||||
|
||||
void Design::drawRect(Graphics::ManagedSurface *surface, Common::ReadStream &in,
|
||||
Graphics::MacPatterns &patterns, byte fillType, byte borderThickness, byte borderFillType) {
|
||||
int16 y1 = in.readSint16BE();
|
||||
int16 x1 = in.readSint16BE();
|
||||
int16 y2 = in.readSint16BE();
|
||||
int16 x2 = in.readSint16BE();
|
||||
|
||||
if (x1 > x2)
|
||||
SWAP(x1, x2);
|
||||
if (y1 > y2)
|
||||
SWAP(y1, y2);
|
||||
|
||||
if (_boundsCalculationMode)
|
||||
_lastOpString = Common::String::format("rect %d, %d, %d, %d", x1, y1, x2, y2);
|
||||
|
||||
if (_boundsCalculationMode) {
|
||||
_bounds->top = MIN(y1, _bounds->top);
|
||||
_bounds->left = MIN(x1, _bounds->left);
|
||||
_bounds->right = MAX(x2, _bounds->right);
|
||||
_bounds->bottom = MAX(y2, _bounds->bottom);
|
||||
}
|
||||
|
||||
if (_surface) {
|
||||
if (!_maskImage) {
|
||||
_maskImage = new Graphics::ManagedSurface(_surface->w, _surface->h);
|
||||
_maskImage->clear(kColorGreen);
|
||||
}
|
||||
_maskImage->fillRect(Common::Rect(x1, y1, x2, y2), kColorBlack);
|
||||
}
|
||||
|
||||
Common::Rect r(x1, y1, x2, y2);
|
||||
PlotData pd(surface, &patterns, fillType, 1, this);
|
||||
PlotDataPrimitives primitives;
|
||||
|
||||
if (fillType <= patterns.size())
|
||||
primitives.drawFilledRect1(r, kColorBlack, &pd);
|
||||
|
||||
pd.fillType = borderFillType;
|
||||
pd.thickness = borderThickness;
|
||||
|
||||
if (borderThickness > 1) {
|
||||
x1 += borderThickness / 2;
|
||||
y1 += borderThickness / 2;
|
||||
x2 -= (borderThickness - 1) / 2;
|
||||
y2 -= (borderThickness - 1) / 2;
|
||||
}
|
||||
|
||||
if (borderThickness > 0 && borderFillType <= patterns.size()) {
|
||||
primitives.drawLine(x1, y1, x2 - 1, y1, kColorBlack, &pd);
|
||||
primitives.drawLine(x2 - 1, y1, x2 - 1, y2 - 1, kColorBlack, &pd);
|
||||
primitives.drawLine(x2 - 1, y2 - 1, x1, y2 - 1, kColorBlack, &pd);
|
||||
primitives.drawLine(x1, y2 - 1, x1, y1, kColorBlack, &pd);
|
||||
}
|
||||
}
|
||||
|
||||
void Design::drawRoundRect(Graphics::ManagedSurface *surface, Common::ReadStream &in,
|
||||
Graphics::MacPatterns &patterns, byte fillType, byte borderThickness, byte borderFillType) {
|
||||
int16 y1 = in.readSint16BE();
|
||||
int16 x1 = in.readSint16BE();
|
||||
int16 y2 = in.readSint16BE() - 1;
|
||||
int16 x2 = in.readSint16BE() - 1;
|
||||
int16 arc = in.readSint16BE();
|
||||
|
||||
if (x1 > x2)
|
||||
SWAP(x1, x2);
|
||||
if (y1 > y2)
|
||||
SWAP(y1, y2);
|
||||
|
||||
if (_boundsCalculationMode)
|
||||
_lastOpString = Common::String::format("roundRect %d, %d, %d, %d", x1, y1, x2, y2);
|
||||
|
||||
if (_surface) {
|
||||
if (!_maskImage) {
|
||||
_maskImage = new Graphics::ManagedSurface(_surface->w, _surface->h);
|
||||
_maskImage->clear(kColorGreen);
|
||||
}
|
||||
_maskImage->fillRect(Common::Rect(x1, y1, x2, y2), kColorBlack);
|
||||
}
|
||||
|
||||
if (borderThickness > 1) {
|
||||
x1 += borderThickness / 2;
|
||||
y1 += borderThickness / 2;
|
||||
x2 -= (borderThickness - 1) / 2;
|
||||
y2 -= (borderThickness - 1) / 2;
|
||||
}
|
||||
|
||||
Common::Rect r(x1, y1, x2, y2);
|
||||
PlotData pd(surface, &patterns, fillType, 1, this);
|
||||
PlotDataPrimitives primitives;
|
||||
|
||||
if (fillType <= patterns.size())
|
||||
primitives.drawRoundRect1(r, arc / 2, kColorBlack, true, &pd);
|
||||
|
||||
pd.fillType = borderFillType;
|
||||
pd.thickness = borderThickness;
|
||||
|
||||
if (borderThickness > 0 && borderFillType <= patterns.size())
|
||||
primitives.drawRoundRect1(r, arc / 2 - 1, kColorBlack, false, &pd);
|
||||
}
|
||||
|
||||
void Design::drawPolygon(Graphics::ManagedSurface *surface, Common::ReadStream &in,
|
||||
Graphics::MacPatterns &patterns, byte fillType, byte borderThickness, byte borderFillType) {
|
||||
|
||||
byte ignored = in.readSint16BE(); // ignored
|
||||
|
||||
if (ignored)
|
||||
warning("Ignored: %d", ignored);
|
||||
|
||||
int numBytes = in.readSint16BE(); // #bytes used by polygon data, including the numBytes
|
||||
int16 by1 = in.readSint16BE();
|
||||
int16 bx1 = in.readSint16BE();
|
||||
int16 by2 = in.readSint16BE();
|
||||
int16 bx2 = in.readSint16BE();
|
||||
Common::Rect bbox(bx1, by1, bx2, by2);
|
||||
|
||||
if (_surface) {
|
||||
if (!_maskImage) {
|
||||
_maskImage = new Graphics::ManagedSurface(_surface->w, _surface->h);
|
||||
_maskImage->clear(kColorGreen);
|
||||
}
|
||||
_maskImage->fillRect(Common::Rect(bx1, by1, bx2, by2), kColorBlack);
|
||||
}
|
||||
|
||||
numBytes -= 8;
|
||||
|
||||
int y1 = in.readSint16BE();
|
||||
int x1 = in.readSint16BE();
|
||||
|
||||
Common::Array<int> xcoords;
|
||||
Common::Array<int> ycoords;
|
||||
|
||||
numBytes -= 6;
|
||||
|
||||
while (numBytes > 0) {
|
||||
int y2 = y1;
|
||||
int x2 = x1;
|
||||
int b = in.readSByte();
|
||||
if (b == -128) {
|
||||
y2 = in.readSint16BE();
|
||||
numBytes -= 3;
|
||||
} else {
|
||||
y2 += b;
|
||||
numBytes -= 1;
|
||||
}
|
||||
b = in.readSByte();
|
||||
if (b == -128) {
|
||||
x2 = in.readSint16BE();
|
||||
numBytes -= 3;
|
||||
} else {
|
||||
x2 += b;
|
||||
numBytes -= 1;
|
||||
}
|
||||
xcoords.push_back(x1);
|
||||
ycoords.push_back(y1);
|
||||
x1 = x2;
|
||||
y1 = y2;
|
||||
}
|
||||
xcoords.push_back(x1);
|
||||
ycoords.push_back(y1);
|
||||
|
||||
if (_boundsCalculationMode) {
|
||||
Common::String vertices;
|
||||
for (uint i = 0; i < xcoords.size(); ++i) {
|
||||
vertices += Common::String::format("(%d,%d)", xcoords[i], ycoords[i]);
|
||||
if (i < xcoords.size() - 1)
|
||||
vertices += ", ";
|
||||
}
|
||||
_lastOpString = Common::String::format("polygon %d, %d, %d, %d, [%s]", bx1, by1, bx2, by2, vertices.c_str());
|
||||
}
|
||||
|
||||
if (borderThickness > 1) {
|
||||
for (uint i = 0; i < xcoords.size(); ++i) {
|
||||
xcoords[i] += borderThickness / 2;
|
||||
ycoords[i] += borderThickness / 2;
|
||||
}
|
||||
}
|
||||
|
||||
int npoints = xcoords.size();
|
||||
int *xpoints = (int *)calloc(npoints, sizeof(int));
|
||||
int *ypoints = (int *)calloc(npoints, sizeof(int));
|
||||
for (int i = 0; i < npoints; i++) {
|
||||
xpoints[i] = xcoords[i];
|
||||
ypoints[i] = ycoords[i];
|
||||
}
|
||||
|
||||
PlotData pd(surface, &patterns, fillType, 1, this);
|
||||
PlotDataPrimitives primitives;
|
||||
|
||||
if (fillType <= patterns.size()) {
|
||||
primitives.drawPolygonScan(xpoints, ypoints, npoints, bbox, kColorBlack, &pd);
|
||||
}
|
||||
|
||||
pd.fillType = borderFillType;
|
||||
pd.thickness = borderThickness;
|
||||
if (borderThickness > 0 && borderFillType <= patterns.size()) {
|
||||
for (int i = 1; i < npoints; i++)
|
||||
primitives.drawLine(xpoints[i-1], ypoints[i-1], xpoints[i], ypoints[i], kColorBlack, &pd);
|
||||
}
|
||||
|
||||
free(xpoints);
|
||||
free(ypoints);
|
||||
}
|
||||
|
||||
void Design::drawOval(Graphics::ManagedSurface *surface, Common::ReadStream &in,
|
||||
Graphics::MacPatterns &patterns, byte fillType, byte borderThickness, byte borderFillType) {
|
||||
int16 y1 = in.readSint16BE();
|
||||
int16 x1 = in.readSint16BE();
|
||||
int16 y2 = in.readSint16BE();
|
||||
int16 x2 = in.readSint16BE();
|
||||
PlotData pd(surface, &patterns, fillType, 1, this);
|
||||
PlotDataPrimitives primitives;
|
||||
|
||||
if (_boundsCalculationMode)
|
||||
_lastOpString = Common::String::format("oval %d, %d, %d, %d", x1, y1, x2, y2);
|
||||
|
||||
if (_surface) {
|
||||
if (!_maskImage) {
|
||||
_maskImage = new Graphics::ManagedSurface(_surface->w, _surface->h);
|
||||
_maskImage->clear(kColorGreen);
|
||||
}
|
||||
_maskImage->fillRect(Common::Rect(x1, y1, x2, y2), kColorBlack);
|
||||
}
|
||||
|
||||
if (fillType <= patterns.size())
|
||||
PlotDataPrimitives().drawEllipse(x1, y1, x2, y2, kColorBlack, true, &pd);
|
||||
|
||||
pd.fillType = borderFillType;
|
||||
pd.thickness = borderThickness;
|
||||
|
||||
if (borderThickness > 1) {
|
||||
x1 += borderThickness / 2;
|
||||
y1 += borderThickness / 2;
|
||||
x2 -= (borderThickness - 1) / 2;
|
||||
y2 -= (borderThickness - 1) / 2;
|
||||
}
|
||||
|
||||
if (borderThickness > 0 && borderFillType <= patterns.size())
|
||||
PlotDataCirclePrimitives().drawEllipse(x1, y1, x2 - 1, y2 - 1, kColorBlack, false, &pd);
|
||||
}
|
||||
|
||||
void Design::drawBitmap(Graphics::ManagedSurface *surface, Common::SeekableReadStream &in) {
|
||||
int numBytes = in.readSint16BE();
|
||||
|
||||
// Check for broken bitmap data
|
||||
if (numBytes < 10) {
|
||||
if (numBytes > 2) {
|
||||
warning("Design::drawBitmap(): Broken bitmap data");
|
||||
in.seek(numBytes - 2, SEEK_CUR);
|
||||
} else {
|
||||
warning("Design::drawBitmap(): Completely broken bitmap data");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int y1 = in.readSint16BE();
|
||||
int x1 = in.readSint16BE();
|
||||
int y2 = in.readSint16BE();
|
||||
int x2 = in.readSint16BE();
|
||||
int w = x2 - x1;
|
||||
int h = y2 - y1;
|
||||
|
||||
if (_boundsCalculationMode)
|
||||
_lastOpString = Common::String::format("bitmap %d, %d, %d, %d [%d bytes]", x1, y1, x2, y2, numBytes);
|
||||
|
||||
if (_surface) {
|
||||
if (!_maskImage) {
|
||||
_maskImage = new Graphics::ManagedSurface(_surface->w, _surface->h);
|
||||
_maskImage->clear(kColorGreen);
|
||||
}
|
||||
}
|
||||
|
||||
Graphics::Surface tmp;
|
||||
|
||||
tmp.create(w, h, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
numBytes -= 10;
|
||||
|
||||
int x = 0, y = 0;
|
||||
while (numBytes > 0 && y < h) {
|
||||
int n = in.readSByte();
|
||||
int count;
|
||||
int b = 0;
|
||||
int state = 0;
|
||||
|
||||
numBytes--;
|
||||
|
||||
if ((n >= 0) && (n <= 127)) { // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
|
||||
count = n + 1;
|
||||
state = 1;
|
||||
} else if ((n >= -127) && (n <= -1)) { // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
|
||||
b = in.readByte();
|
||||
numBytes--;
|
||||
count = -n + 1;
|
||||
state = 2;
|
||||
} else { // Else if n is -128, noop.
|
||||
count = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count && y < h; i++) {
|
||||
byte color = 0;
|
||||
if (state == 1) {
|
||||
color = in.readByte();
|
||||
numBytes--;
|
||||
} else if (state == 2)
|
||||
color = b;
|
||||
|
||||
for (int c = 0; c < 8; c++) {
|
||||
if (_boundsCalculationMode) {
|
||||
adjustBounds(x1 + x, y1 + y);
|
||||
} else if (x1 + x >= 0 && x1 + x < surface->w && y1 + y >= 0 && y1 + y < surface->h)
|
||||
*((byte *)tmp.getBasePtr(x, y)) = (color & (1 << (7 - c % 8))) ? kColorBlack : kColorWhite;
|
||||
x++;
|
||||
if (x == w) {
|
||||
y++;
|
||||
x = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (numBytes < 0 && (i + 1 < count) && (y < h)) {
|
||||
warning("Design::drawBitmap(): Bitmap data ended prematurely");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numBytes > 0)
|
||||
in.seek(numBytes, SEEK_CUR);
|
||||
else if (numBytes < 0)
|
||||
warning("Design::drawBitmap(): Read past the end of bitmap data (%d bytes)", numBytes);
|
||||
|
||||
if (!_boundsCalculationMode) {
|
||||
Graphics::FloodFill ff(&tmp, kColorWhite, kColorGreen);
|
||||
for (int yy = 0; yy < h; yy++) {
|
||||
ff.addSeed(0, yy);
|
||||
ff.addSeed(w - 1, yy);
|
||||
}
|
||||
for (int xx = 0; xx < w; xx++) {
|
||||
ff.addSeed(xx, 0);
|
||||
ff.addSeed(xx, h - 1);
|
||||
}
|
||||
ff.fill();
|
||||
|
||||
y = 0;
|
||||
|
||||
if (y1 < 0)
|
||||
y = -y1;
|
||||
|
||||
for (; y < h && y1 + y < surface->h; y++) {
|
||||
x = 0;
|
||||
if (x1 < 0)
|
||||
x = -x1;
|
||||
|
||||
byte *src = (byte *)tmp.getBasePtr(x, y);
|
||||
byte *dst = (byte *)surface->getBasePtr(x1 + x, y1 + y);
|
||||
byte *mask = (byte *)_maskImage->getBasePtr(x1 + x, y1 + y);
|
||||
|
||||
for (; x < w && x1 + x < surface->w; x++) {
|
||||
if (*src != kColorGreen) {
|
||||
*dst = *src;
|
||||
*mask = kColorBlack;
|
||||
}
|
||||
src++;
|
||||
dst++;
|
||||
mask++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tmp.free();
|
||||
}
|
||||
|
||||
void Design::drawRect(Graphics::ManagedSurface *surface, const Common::Rect &rect, int thickness, int color, Graphics::MacPatterns &patterns, byte fillType) {
|
||||
PlotData pd(surface, &patterns, fillType, thickness, nullptr);
|
||||
PlotDataPrimitives().drawRect1(rect, kColorBlack, &pd);
|
||||
}
|
||||
|
||||
void Design::drawRect(Graphics::ManagedSurface *surface, int x1, int y1, int x2, int y2, int thickness, int color, Graphics::MacPatterns &patterns, byte fillType) {
|
||||
drawRect(surface, Common::Rect(MIN(x1, x2), MIN(y1, y2), MAX(x1, x2), MAX(y1, y2)),
|
||||
thickness, color, patterns, fillType);
|
||||
}
|
||||
|
||||
|
||||
void Design::drawFilledRect(Graphics::ManagedSurface *surface, Common::Rect &rect, int color, Graphics::MacPatterns &patterns, byte fillType) {
|
||||
PlotData pd(surface, &patterns, fillType, 1, nullptr);
|
||||
PlotDataPrimitives().drawFilledRect1(rect, color, &pd);
|
||||
}
|
||||
|
||||
void Design::drawFilledRoundRect(Graphics::ManagedSurface *surface, Common::Rect &rect, int arc, int color, Graphics::MacPatterns &patterns, byte fillType) {
|
||||
PlotData pd(surface, &patterns, fillType, 1, nullptr);
|
||||
PlotDataPrimitives().drawRoundRect1(rect, arc, color, true, &pd);
|
||||
}
|
||||
|
||||
void Design::drawHLine(Graphics::ManagedSurface *surface, int x1, int x2, int y, int thickness, int color, Graphics::MacPatterns &patterns, byte fillType) {
|
||||
PlotData pd(surface, &patterns, fillType, thickness, nullptr);
|
||||
PlotDataPrimitives().drawHLine(x1, x2, y, color, &pd);
|
||||
}
|
||||
|
||||
void Design::drawVLine(Graphics::ManagedSurface *surface, int x, int y1, int y2, int thickness, int color, Graphics::MacPatterns &patterns, byte fillType) {
|
||||
PlotData pd(surface, &patterns, fillType, thickness, nullptr);
|
||||
PlotDataPrimitives().drawVLine(x, y1, y2, color, &pd);
|
||||
}
|
||||
|
||||
} // End of namespace Wage
|
||||
122
engines/wage/design.h
Normal file
122
engines/wage/design.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WAGE_DESIGN_H
|
||||
#define WAGE_DESIGN_H
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
#include "graphics/nine_patch.h"
|
||||
#include "graphics/macgui/macwindowmanager.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
using namespace Graphics::MacGUIConstants;
|
||||
|
||||
// struct to hold design operation data
|
||||
struct DrawOp {
|
||||
uint32 offset;
|
||||
Common::String opcode;
|
||||
int fillType;
|
||||
int borderThickness;
|
||||
int borderFillType;
|
||||
int lineSize;
|
||||
};
|
||||
|
||||
class Design {
|
||||
public:
|
||||
Design(Common::SeekableReadStream *data);
|
||||
~Design();
|
||||
|
||||
Common::Array<DrawOp> _drawOps;
|
||||
|
||||
void setBounds(Common::Rect *bounds) {
|
||||
_bounds = bounds;
|
||||
}
|
||||
|
||||
Common::Rect *getBounds() {
|
||||
return _bounds;
|
||||
}
|
||||
|
||||
void paint(Graphics::ManagedSurface *canvas, Graphics::MacPatterns &patterns, int x, int y, int steps = -1);
|
||||
bool isInBounds(int x, int y);
|
||||
static void drawRect(Graphics::ManagedSurface *surface, const Common::Rect &rect, int thickness, int color, Graphics::MacPatterns &patterns, byte fillType);
|
||||
static void drawRect(Graphics::ManagedSurface *surface, int x1, int y1, int x2, int y2, int thickness, int color, Graphics::MacPatterns &patterns, byte fillType);
|
||||
static void drawFilledRect(Graphics::ManagedSurface *surface, Common::Rect &rect, int color, Graphics::MacPatterns &patterns, byte fillType);
|
||||
static void drawFilledRoundRect(Graphics::ManagedSurface *surface, Common::Rect &rect, int arc, int color, Graphics::MacPatterns &patterns, byte fillType);
|
||||
static void drawHLine(Graphics::ManagedSurface *surface, int x1, int x2, int y, int thickness, int color, Graphics::MacPatterns &patterns, byte fillType);
|
||||
static void drawVLine(Graphics::ManagedSurface *surface, int x, int y1, int y2, int thickness, int color, Graphics::MacPatterns &patterns, byte fillType);
|
||||
|
||||
bool isBoundsCalculation() { return _boundsCalculationMode; }
|
||||
void adjustBounds(int16 x, int16 y);
|
||||
|
||||
private:
|
||||
byte *_data;
|
||||
int _len;
|
||||
int _renderedSteps;
|
||||
Common::String _lastOpString;
|
||||
Common::Rect *_bounds;
|
||||
Graphics::ManagedSurface *_surface;
|
||||
bool _boundsCalculationMode;
|
||||
Graphics::ManagedSurface *_maskImage;
|
||||
|
||||
private:
|
||||
void render(Graphics::MacPatterns &patterns, int steps = -1);
|
||||
void drawRect(Graphics::ManagedSurface *surface, Common::ReadStream &in,
|
||||
Graphics::MacPatterns &patterns, byte fillType, byte borderThickness, byte borderFillType);
|
||||
void drawRoundRect(Graphics::ManagedSurface *surface, Common::ReadStream &in,
|
||||
Graphics::MacPatterns &patterns, byte fillType, byte borderThickness, byte borderFillType);
|
||||
void drawPolygon(Graphics::ManagedSurface *surface, Common::ReadStream &in,
|
||||
Graphics::MacPatterns &patterns, byte fillType, byte borderThickness, byte borderFillType);
|
||||
void drawOval(Graphics::ManagedSurface *surface, Common::ReadStream &in,
|
||||
Graphics::MacPatterns &patterns, byte fillType, byte borderThickness, byte borderFillType);
|
||||
void drawBitmap(Graphics::ManagedSurface *surface, Common::SeekableReadStream &in);
|
||||
};
|
||||
|
||||
} // End of namespace Wage
|
||||
|
||||
#endif
|
||||
137
engines/wage/detection.cpp
Normal file
137
engines/wage/detection.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
/* 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 "base/plugins.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/macresman.h"
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
static const PlainGameDescriptor wageGames[] = {
|
||||
{"afm", "Another Fine Mess"},
|
||||
{"amot", "A Mess O' Trouble"},
|
||||
{"cantitoe", "Camp Cantitoe"},
|
||||
{"drakmythcastle", "Drakmyth Castle"},
|
||||
{"grailquest", "GrailQuest: Adventure in the Age of King Arthur"},
|
||||
{"raysmaze", "Ray's Maze"},
|
||||
{"scepters", "Enchanted Scepters"},
|
||||
{"twisted", "Twisted!"},
|
||||
{"worldbuilder", "World Builder"},
|
||||
{"wage", "WAGE"},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
#include "wage/detection_tables.h"
|
||||
#include "wage/detection.h"
|
||||
|
||||
static const DebugChannelDef debugFlagList[] = {
|
||||
{ Wage::kDebugImGui, "imgui", "Show ImGui debug window (if available)"},
|
||||
{ Wage::kDebugSound, "sound", "Show sound debug information"},
|
||||
{ Wage::kDebugLoading, "loading", "Show loading debug information" },
|
||||
DEBUG_CHANNEL_END
|
||||
};
|
||||
|
||||
static ADGameDescription s_fallbackDesc = {
|
||||
"wage",
|
||||
"",
|
||||
AD_ENTRY1(0, 0),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
};
|
||||
|
||||
class WageMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
|
||||
mutable Common::String _filenameStr;
|
||||
|
||||
public:
|
||||
WageMetaEngineDetection() : AdvancedMetaEngineDetection(Wage::gameDescriptions, wageGames) {
|
||||
_md5Bytes = 2 * 1024 * 1024;
|
||||
_guiOptions = GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GAMEOPTION_TTS, GUIO_NOMUSIC);
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "wage";
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "World Adventure Game Engine";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "World Builder (C) Silicon Beach Software";
|
||||
}
|
||||
|
||||
const DebugChannelDef *getDebugChannels() const override {
|
||||
return debugFlagList;
|
||||
}
|
||||
|
||||
ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extra) const override;
|
||||
};
|
||||
|
||||
ADDetectedGame WageMetaEngineDetection::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extra) const {
|
||||
SearchMan.addDirectory("WageMetaEngineDetection::fallbackDetect", fslist.begin()->getParent());
|
||||
|
||||
for (Common::FSList::const_iterator fs = fslist.begin(); fs != fslist.end(); ++fs) {
|
||||
if (fs->isDirectory())
|
||||
continue;
|
||||
|
||||
Common::Path filePath = Common::Path(fs->getPathInArchive());
|
||||
Common::MacResManager resManager;
|
||||
if (!resManager.open(filePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Common::MacFinderInfo finderInfo;
|
||||
if (resManager.getFileFinderInfo(filePath, finderInfo)) {
|
||||
if (READ_BE_UINT32(finderInfo.type) != MKTAG('A', 'P', 'P', 'L')) {
|
||||
continue;
|
||||
}
|
||||
if (READ_BE_UINT32(finderInfo.creator) != MKTAG('W', 'E', 'D', 'T')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Common::Path outPath(fs->getFileName());
|
||||
_filenameStr = outPath.toString();
|
||||
|
||||
s_fallbackDesc.filesDescriptions[0].fileName = _filenameStr.c_str();
|
||||
|
||||
ADDetectedGame game;
|
||||
game.desc = &s_fallbackDesc;
|
||||
|
||||
FileProperties tmp;
|
||||
if (getFileProperties(allFiles, kMD5MacResFork, filePath, tmp)) {
|
||||
game.hasUnknownFiles = true;
|
||||
game.matchedFiles[filePath] = tmp;
|
||||
}
|
||||
|
||||
SearchMan.remove("WageMetaEngineDetection::fallbackDetect");
|
||||
return game;
|
||||
}
|
||||
}
|
||||
|
||||
SearchMan.remove("WageMetaEngineDetection::fallbackDetect");
|
||||
return ADDetectedGame();
|
||||
}
|
||||
|
||||
REGISTER_PLUGIN_STATIC(WAGE_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, WageMetaEngineDetection);
|
||||
27
engines/wage/detection.h
Normal file
27
engines/wage/detection.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WAGE_DETECTION_H
|
||||
#define WAGE_DETECTION_H
|
||||
|
||||
#define GAMEOPTION_TTS GUIO_GAMEOPTIONS1
|
||||
|
||||
#endif // WAGE_DETECTION_H
|
||||
391
engines/wage/detection_tables.h
Normal file
391
engines/wage/detection_tables.h
Normal file
@@ -0,0 +1,391 @@
|
||||
/* 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 "engines/wage/wage.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
#define ADGF_DEFAULT (ADGF_DROPLANGUAGE|ADGF_DROPPLATFORM|ADGF_MACRESFORK)
|
||||
#define ADGF_GENERIC (ADGF_DEFAULT|ADGF_USEEXTRAASTITLE|ADGF_AUTOGENTARGET)
|
||||
|
||||
#define FANGAME(n,m,s) { "wage",n,AD_ENTRY1s(n,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC,GUIO0() }
|
||||
#define FANGAMEr(n,m,s,r) { "wage",n,AD_ENTRY1s(n,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC | r,GUIO0() }
|
||||
#define FANGAMEN(n,f,m,s) { "wage",n,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC,GUIO0() }
|
||||
#define FANGAMENr(n,f,m,s,r) { "wage",n,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC | r,GUIO0() }
|
||||
#define FANGAMEND(n,f,m,s) { "wage",n,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC | ADGF_DEMO,GUIO0() }
|
||||
#define FANGAMENDr(n,f,m,s,r) { "wage",n,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC | ADGF_DEMO | r,GUIO0() }
|
||||
#define BIGGAME(t,v,f,m,s) { t,v,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_DEFAULT,GUIO0() }
|
||||
#define BIGGAMEr(t,v,f,m,s,r) { t,v,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_DEFAULT | r,GUIO0() }
|
||||
#define BIGGAMED(t,v,f,m,s) { t,v,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_DEFAULT| ADGF_DEMO,GUIO0() }
|
||||
|
||||
static const ADGameDescription gameDescriptions[] = {
|
||||
FANGAMEND("World Builder Demo World", "Demo World", "e221e6f9631f110b484f239e58137a3f", 45467),
|
||||
FANGAME("Ray's World Template", "r:28be556dd85b5c0b28f5ac20793ba542", 38264),
|
||||
|
||||
// Use two files, so we override other included templates
|
||||
{ "worldbuilder",
|
||||
"World Builder",
|
||||
AD_ENTRY2s("World Template","cb30c899eec946803793fca4e916adb8", 34444,
|
||||
"Example Sound Lib", "ca2d25ee13020d5949da06b6a38a1101", 49505),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_GENERIC,
|
||||
GUIO0()
|
||||
},
|
||||
|
||||
// Use two files, so we override other included templates
|
||||
{ "worldbuilder",
|
||||
"World Builder v1.2",
|
||||
AD_ENTRY2s("World Template","242d9d343cfc09d61096beb91419c493", 34772,
|
||||
"Sound Library #1", "02f62fdb302c5f1e0a9a11edb972f4a3", 471067),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_GENERIC,
|
||||
GUIO0()
|
||||
},
|
||||
|
||||
|
||||
// Render Tests
|
||||
//FANGAME("Shape Test", "7466edab34a853707343c26eb362b8c1", 38194),
|
||||
//FANGAME("Rects", "03d2f93b1cafe963a0e29232f4867cc4", 38177),
|
||||
//FANGAME("Filled Rects", "835ae43bf16a8d83b84a99724eaec79d", 38194),
|
||||
//FANGAME("Round Rects", "9602f7baafb13fd1c7fd00f92743227c", 38194),
|
||||
//FANGAME("Filled Round Rects", "4d880677a97fd07e6a4f04a46f147084", 38194),
|
||||
//FANGAME("Polygon", "c84d8065c6a4eb7dd232003954ba7cab", 38352),
|
||||
//FANGAME("Filled Polygon", "2d5a3c952c6c43c0456eae9bbc52867d", 38310),
|
||||
//FANGAME("Ovals", "9b5e3d5afe3b4c46e506216a5ae16e14", 38194),
|
||||
//FANGAME("Filled Ovals", "6990174e0b39d85f3069f98511bb0074", 38194),
|
||||
|
||||
// Game series:
|
||||
//
|
||||
// The Axe-orcist
|
||||
// Jamie the Demon Slayer
|
||||
//
|
||||
// Brownie’s Time Travels
|
||||
// SparGate
|
||||
// Brownie’s Dream
|
||||
// Who Shot Brownie Dog?
|
||||
// The Time Squisher
|
||||
// Brownie Saves the Day
|
||||
|
||||
FANGAME("3rd Floor", "931aa0b6ada3aced5117ee6e6daf2fb8", 281153),
|
||||
FANGAME("3rd Floor", "140883954b7cd89b0ffabde6ee0073d4", 281167), // alt version
|
||||
FANGAME("3rd Floor", "afe1a9eb0d4e5857f9f748570862c88e", 281213), // v1.3
|
||||
BIGGAME("afm", "v1.3", "Another Fine Mess 1.3", "25934edf84e814f41f3ddf87185b554b", 1409792),
|
||||
BIGGAME("afm", "v1.4", "Another Fine Mess 1.4", "28d2b78c7abcbfd526f9c252444ef0b3", 1420563),
|
||||
BIGGAME("afm", "v1.4", "Another Fine Mess 1.4", "r:49de322edff7118ec14b240047cef6d2", 1410160),
|
||||
BIGGAME("afm", "v1.5", "Another Fine Mess 1.5", "r:9a3351c5e216d56529779a0685dba182", 1420717),
|
||||
BIGGAME("afm", "v1.6", "Another Fine Mess 1.6", "r:b87fbc493f93a2b1da340b3448b18b26", 1420717),
|
||||
BIGGAME("afm", "v1.8", "Another Fine Mess 1.8", "8bbec64ffe9deee4ff48d27f01176814", 1420467),
|
||||
FANGAMEN("Alien Clutch", "Alien Clutch 2.0", "5c783f6b7f4a7d8d49e138be94e00f5a", 514976),
|
||||
BIGGAME("amot", "v1.8", "A Mess O' Trouble 1.8", "57de8f1f79a24fa1a296aa10242c3648", 1842848),
|
||||
FANGAMEN("The Adventures of Steve Reeve", "TAoSR", "e8b8ab3a838382125594aae17d53a4e7", 843323),
|
||||
FANGAMEND("The Ashland Revolution", "Ashland 1.0", "f6ff38b24953ae818a000f88998e938d", 150220),
|
||||
FANGAMEND("The Ashland Revolution", "xn--The Ashland Revolution Demo-uc9p", "18f1f5d1081b9a5676ccfb0c3b857059", 144767), // Original file name "The Ashland Revolution Demo†"
|
||||
FANGAMEND("The Ashland Revolution", "Ashland Demo", "7a340e5ea03f7468717de408fb5b13fb", 149828), // alt version
|
||||
FANGAMEND("The Ashland Revolution", "The Ashland Revolution Demo", "de6fdfe2b9d9efc0ea842e2bde68673a", 144753), // alt version
|
||||
FANGAME("The Axe-orcist", "9718b72e86a9727bbd68e12fa992b268", 308508),
|
||||
FANGAME("Ben's Castle 1.5", "r:6e8d74405be3875fbb85cfe8c358e641", 187691),
|
||||
FANGAMEN("Beyond Twilight Vale", "Beyond Twilight Vale 0.8", "ba67d80dc22d32f42446c891daf56fc8", 248089),
|
||||
FANGAME("Brownie Saves the Day", "964d9cf87422c09c50fe294adf9a7571", 680145),
|
||||
FANGAME("Brownie's Dream", "379f2c810efc18bad932049d331df1b6", 440448),
|
||||
FANGAME("Brownie's Dream", "78f79114a7e85e452a7b8c7346a21456", 440448), // alt version?
|
||||
FANGAMEN("Brownie's Time Travels", "Brownie's Time Travels v1.2", "d95999aff4e283bd21db686cfb2cb9b9", 471333),
|
||||
FANGAMEN("Brownie's Time Travels", "Brownie's Time Travels v1.2", "9dc2c6ba42788dbe534eea3ce6da91d8", 471333), // alt version
|
||||
FANGAME("Bug Hunt", "b55b23a5f38d28407bd6c178a64ab629", 195443),
|
||||
FANGAME("Bug Hunt", "71ffb17554e6331dd8fa63698a196bd1", 195443), // alt version
|
||||
FANGAME("Bug Hunt", "cd7e1064813b9b0e3cd946e569109b34", 195523), // alt version
|
||||
FANGAME("Bug Hunt", "b0fbc1b0dff61185f7bd94bf6ae7c1c8", 195509), // alt version
|
||||
BIGGAME("cantitoe", "", "Camp Cantitoe", "4a44860fdfe5b9e98bcf8433801cd925", 616729),
|
||||
BIGGAME("cantitoe", "", "Camp Cantitoe", "6bf7e2c13eb444a861cc07f352b6ce12", 616715), // alt version
|
||||
FANGAMEN("Can James Be Found?", "xn--Can James Be Found 1.1.1-iba62n", "27822449e7e725d898181adf6e3b1689", 767445), // original file name "Can James Be Found? 1.1.1"
|
||||
FANGAME("Canal District", "061a09e9258145f6a585e869a0319004", 641214),
|
||||
FANGAMEN("Carbon Copy The Game", "Carbon Copy", "322410318c60045928a10f6b4c0b0a87", 519189),
|
||||
FANGAMEN("Carbon Copy The Game", "Carbon Copy", "53417dce5931dfb96a21583dbafe1f8c", 331759), // alt version
|
||||
FANGAMEN("Carbon Copy The Game", "Carbon Copy", "dd1f8eb1fe741cfb0df8827ecb04e584", 518580), // alt version
|
||||
FANGAME("Castle of Ert", "31a832c2be51e622fb2c586803dadf9e", 198699), // alt version
|
||||
FANGAME("Castle of Ert", "289d1f944d7c30b257587a7a49579d0f", 198713), // alt version
|
||||
FANGAMEN("Castle of Ert", "Castle of Ert V1.2", "bc1c119b4a95ea6891a6e22c35e6c2d8", 201984),
|
||||
FANGAMEN("City Adventure", "City Adventure 1.1", "0c9415ea525ffeedb346f7a62ebe6cd3", 285789),
|
||||
FANGAME("Crystal Adventure", "d9e82fc3a58217a0ea4d6fddcb7fbc2a", 477918),
|
||||
FANGAMEN("Crystal Search", "Crystal Search 1.5", "a9dda0422c6424e915363745ad2ecc41", 517019),
|
||||
FANGAMEN("Dash Hansen and the Search for the Black Orchid", "Dash Hansen", "555e07344506720c8e4a29e1b5ba3578", 257129),
|
||||
FANGAMEND("Death Mall", "Death Mall Demo", "8284351df1c7b678279139ed486ab127", 254618),
|
||||
FANGAMEND("Death Mall", "Death Mall Demo", "f741a9b6ab34402fa74e42f5a4e479dd", 254018), // alt version
|
||||
FANGAMEND("Death Mall", "Death Mall Demo", "39f32a73fecb19db12ba55d09c674cc9", 254604), // alt version
|
||||
FANGAMEN("Deep Angst", "xn--Deep Angst-398e", "ddc4c8b3d317e2c79a12c53275253ac3", 329294), // Original file name "Deep Angst™", v.90b
|
||||
FANGAMEN("Deep Angst", "xn--Deep Angst-398e", "9e4465b5547e3a5bf343e6185e600cff", 329266), // Original file name "Deep Angst™", v.90B
|
||||
FANGAMEN("Deep Angst", "xn--Deep Angst-398e", "d8f782f4297b78e72e21f39b5432b0ae", 329266), // Original file name "Deep Angst™", v.90B
|
||||
FANGAMEN("Deep Angst", "xn--DEEP ANGST 1987Ronald Wartow-k8a2050w", "38cc0c9a301e63b0a942677a0d19b0ac", 337122), // original file name "DEEP ANGST™ ©1987Ronald Wartow", v.90B
|
||||
FANGAMEN("Deep Angst II: The Nightmare Ends", "Deep Angst II", "2e468021416bc2b82e283030c7f6bc5d", 408761),
|
||||
FANGAME("Deep Ennui", "9879bf659f11c9177d578a347da0c658", 85819),
|
||||
FANGAME("Deep Ennui", "4d98fcefc3e456be95023b0f06cae320", 85233), // alt version
|
||||
FANGAME("Doomsday Machine", "bdb8845dfe0b4a5eb5c88da5c1b87cd6", 126371),
|
||||
FANGAME("Double Trouble", "1cd05d66163b44f9f0d8c3e7232bc2db", 542115),
|
||||
BIGGAME("drakmythcastle", "Disk I", "Drakmyth Castle disk I of II", "54dd0a817b667fc05c4f2dee6abe126a", 793528),
|
||||
BIGGAME("drakmythcastle", "Disk I", "Drakmyth Castle", "b796545fc4b7c2b89683db873e005e92", 792355), // alt version
|
||||
BIGGAME("drakmythcastle", "Disk II", "Drakmyth Castle II", "b57af17c805775d7d68b62133164c4e4", 1685403),
|
||||
FANGAMENr("Dune Eternity", "xn--DUNE ETERNITY -paaanaa75fbbobb", "4946bc99cc42bf83b628352aa9b81a7b", 289945, GF_RES800), // Original file name is "***DUNE ETERNITY*** "
|
||||
FANGAMENr("Dune Eternity", "xn--DUNE ETERNITY1-paaaoaa65fbbpbb", "r:4946bc99cc42bf83b628352aa9b81a7b", 289945, GF_RES800), // Same as above with different name
|
||||
FANGAMEN("Dungeon World II", "DungeonWorld2", "74a7153f9ae61a59a216078a37f68f2c", 229943),
|
||||
FANGAMEr("Dynasty of Dar", "e118a261d33831c224f3b776ec5dd2a8", 275437, GF_RES800),
|
||||
FANGAMEr("Dynasty of Dar", "e15eae8d9a4c40512be3bb81ecedf32c", 275423, GF_RES800), // alt version
|
||||
FANGAME("Edg's World", "480bcf68be49ee3765902e922ccdc833", 106513),
|
||||
FANGAME("Edg's World", "bcbfbf4d2baeadc345f62246279ce3d6", 105927), // alt version
|
||||
FANGAME("Eidisi I", "ed8fec61ad94ddec06feaf4eb720084b", 172296),
|
||||
FANGAME("Eidisi I", "a20f080d6109d8e253d74b0dfb4b2ae5", 172296), // alt version
|
||||
FANGAME("Eidisi I", "06ae31c4361f9bd5b91593858b6d0d79", 172310), // alt version
|
||||
FANGAME("Elite Commando", "3fc74f1403c1529b52f3cd4d60771dac", 293704), // v1.0
|
||||
FANGAMEN("Enchanted Pencils", "Enchanted Pencils 0.99 (PG)", "49a0708da81dbeb28c6e607429c92209", 408657),
|
||||
FANGAMEN("Escape!", "xn--Escape!-z46c", "28a9658ee846a34f133df29b54cf255a", 64819), // Original file name "Escape!†"
|
||||
FANGAMEN("Escape!", "xn--Escape!-lha", "63378d6b032d4337b7e74759337eceaa", 64805), // Original file name " Escape!", alt version
|
||||
FANGAMEN("Escape from Inaka", "Escape from Inaka - 1.0b1", "a1a91e9b7b6ad01d779993dab24fd080", 1112974),
|
||||
FANGAME("Escape from School!", "2055747bb874052333190eb993246a7f", 49849),
|
||||
FANGAMEN("Escape from School!", "xn--Escape from School!-3g6k", "fcc581e52d1fc8ea4603d7c953fa935a", 49863), // Original file name "Escape from School!†", alt version
|
||||
FANGAME("Escape From Saecvrom", "ab401f96ca0755bf5fb849f71cc47d83", 588982),
|
||||
FANGAME("Everyman 1", "97d78e22493245636f84ad8db732305c", 335449),
|
||||
FANGAME("EveryMan 1", "9104b23c8978c7db118bb60fb5f8bf0f", 335435), // alt version
|
||||
FANGAMEN("EveryMan 1", "EveryMan I", "9104b23c8978c7db118bb60fb5f8bf0f", 335435), // Same as above with different name
|
||||
FANGAME("EveryMan 1", "829aabea15e143f20fe692f0bc38e56a", 335444), // alt version
|
||||
FANGAMEN("EveryMan 2", "EveryMan 2 2.02", "401c1b0eae618fa5f39b6577bc108c2f", 715273),
|
||||
FANGAME("Exploration Zeta!", "9006eff549afadc956e5de4ae6a24fbd", 366343),
|
||||
FANGAMEND("Explorer", "Explorer DEMO", "0ae79f48754466c4cd65137c7f186384", 460972),
|
||||
FANGAMEND("Explorer", "Explorer DEMO", "6b22972808630201ece7cc96a0ef17f7", 460959), // alt version
|
||||
FANGAME("Fantasy Quest", "b52d3e2680a76c23b2791e2c87f6b6bd", 762498),
|
||||
FANGAMEND("Finding D", "Finding D - concept demo", "3657a0483c70a1b44641e62120290574", 55723),
|
||||
FANGAME("Find the Heart", "0c0c282649597ea1ac82d97c8d4029a2", 105979), // From Joshua's Worlds 1.0
|
||||
FANGAME("Find the Heart", "6fb8c6b8bc2287ba225b0ac6580dc073", 105675), // From Joshua's Worlds 1.0, alt version
|
||||
FANGAME("Find the Heart", "08de3248b8c691d9a08af984bdcfa872", 105629), // From Joshua's Worlds, alt version
|
||||
FANGAME("Find the Heart", "73935d313b666763e50d2cdc6b3b7908", 105615), // Standalone
|
||||
FANGAMEN("Fortune Teller", "Fortune Teller 1.1", "3c09329fc3c92a70e5c8874cc763f5cb", 73675),
|
||||
FANGAMEN("Fred Rogers - Terrorist", "xn--Terrorist-od0e", "4398f711bc2a456e6d730641308593f0", 524213), // Original file name "Terrorist†"
|
||||
FANGAMEN("Fred Rogers - Terrorist", "Terrorist", "a37f39fcf9a1c88e1b30385754b2a1f7", 524199), // alt version
|
||||
FANGAME("Fred Rogers - Terrorist", "8597a77619847efbce3f1f8b2ceb3258", 524199), // alt version
|
||||
FANGAME("Galactic Warrior", "bf61d2344a63b597ab288ddaacfa402a", 89117),
|
||||
FANGAMEN("get-a-mac", "get-a-mac 1.1", "e12a38d9b56ca62414b99cdb58aa3e1c", 144936),
|
||||
FANGAMEN("Get out of MAZE", "Get out of MAZE 1.0", "cd9eb31e1178b0eec35ee84ff781647b", 53583),
|
||||
FANGAME("Gold Bug!", "34a379231ae97150e336b91773e6dd47", 782645),
|
||||
BIGGAME("grailquest", "", "GrailQuest", "6441d134c0aedd8cb3b6909107a22ee3", 739452),
|
||||
BIGGAMED("grailquest", "Demo", "GrailQuest.demo", "c0ef30ef7e4119fe76c5fbe2f930e7b1", 190810),
|
||||
FANGAME("Haikook", "405d383b273a0252645962e0e664be20", 419636),
|
||||
FANGAME("Grendel-Dazz", "968addd863d8e5c90f73d029c9240ded", 267156),
|
||||
FANGAMEN("Haunted House", "The haunted house 1.4", "655ff082160909beb83b99979cdfc2c6", 107959),
|
||||
FANGAMEN("Haunted House", "Haunted House 1.5", "5e34e9fa13f4c90876f10a40ea1d1c79", 177244),
|
||||
FANGAMEN("Haunted University", "Haunted U. v2.04", "d9511905bb908a0c3ea13c996e053aec", 1674435),
|
||||
FANGAME("Hollywood Starbound", "34499b3935f4c019c05e77ade28aa527", 633717),
|
||||
FANGAMEN("The Hotel Caper", "The Hotel Caper V1.0", "c9b3c75814fc6b14feae044157bef252", 231713),
|
||||
FANGAMEN("The Hotel Caper", "The Hotel Caper V1.0", "4658ece81a6f211a828e747125482f48", 231713), // alt version
|
||||
FANGAMEN("The Hotel Caper", "The Hotel Caper V1.0", "baaaae4569fbb1947d74667ac484bea9", 231773), // alt version
|
||||
FANGAMEN("The Hotel Caper", "Hotel Caper V1.0", "138e89e5d5bcac6aba222cf33677bcec", 231127),
|
||||
FANGAMEN("The Hotel Caper", "The Hotel Caper V2.0", "eed79653ca95f4e953aa0ff41e8c8034", 260418),
|
||||
FANGAMEN(".i.f.", "xn--.i.f.-", "575fc0c25e9823dd742de1acdcaac325", 183221),
|
||||
FANGAMEND("Introduction to Gothic", "Intro to Gothic", "606eec666f0b2d767e4423747e740434", 207811),
|
||||
FANGAMEN("Intruder", "INTRUDER 1.1", "7d819891784c79fef66ff3bd9d946d6d", 203105),
|
||||
FANGAMEN("Intruder", "INTRUDER v1.2", "a7df3f4196e922153cd4345def774c29", 241007),
|
||||
FANGAME("Intruder ][", "r:a35a5e110bb5e4a3a3b91db26deaa881", 697570),
|
||||
FANGAME("Jack & Beanstalk Starring You", "2cd38be71d32892a83913c061a03aa19", 312646),
|
||||
FANGAMEN("James Bond 007", "007", "2449924f2cb43454489a4ef91c0ee702", 50407),
|
||||
FANGAME("Jamie the Demon Slayer", "ed054aa760569059c7ea554e822089a6", 232533),
|
||||
FANGAME("Joe Wu Tse's Dungeon", "r:ea800af93f3886550d7c6c1687cfc9e1", 189764),
|
||||
FANGAMEN("Journey", "The Journey 1.5", "4bbc047d21740f79f3f198628ba8f8bc", 665011),
|
||||
FANGAMEN("Journey", "2 The Journey v1.5 (1992)", "4bbc047d21740f79f3f198628ba8f8bc", 665011), // alt version
|
||||
FANGAMEN("Journey", "1 The Journey v1.6 (1992)", "cff7929abf284060c2070dfc1a2fa9cf", 765189),
|
||||
FANGAMEN("Journey", "The Journey 1.6.2 US", "588a516caa187005fdfcbc72823c8eff", 820316),
|
||||
FANGAMEN("Jumble", "xn--LSJUMBLE -nd0e", "555ead186ec1683157e53b96fc4a99d5", 647083), // Original file name is "LSJUMBLE† "
|
||||
FANGAME("Karth of the Jungle", "6dae6eef6f46101ba8c9e312bb82e824", 96455),
|
||||
FANGAME("Karth of the Jungle", "52414de10b5d0f75a582740d8d3face6", 96455), // alt version
|
||||
FANGAME("Karth of the Jungle", "c869cacc59f2402d06bc8197be28b5df", 96704), // alt version
|
||||
FANGAME("Karth of the Jungle", "b1f4fbf94d685f22204e4c7d81edd0b4", 95869), // alt version
|
||||
FANGAME("Karth of the Jungle II", "6f26e0bace5c41d91c135e5e102de5bc", 200797),
|
||||
FANGAMEN("Karth of the Jungle II", "Karth II", "dd4d719aabc78f82123721d3bd6c28fb", 200419),
|
||||
FANGAMEN("Labyrinth of Destiny", "Labyrinth of Destiny 1.0.5", "74f65f537358137721920e16d684f00b", 461053),
|
||||
FANGAMEN("Little Pythagoras", "Little Pythagoras 1.1.1", "66a5d2ac1a0df2ee84cbee583c1f1dfe", 628565),
|
||||
FANGAME("Lost Crystal", "945a1cf1ead6dd298305935b5ccb21d2", 770816),
|
||||
FANGAMEN("Lost In Kookyville", "Lost In Kookyville 1.2.4", "89ecb4cef5cc4036e54f8bc45ce7520e", 721313),
|
||||
FANGAMEN("Lost In Kookyville", "Lost In Kookyville 1.2.4", "dfa0abbadf3848f415c6696d6d914455", 722917), // alt version?
|
||||
FANGAME("Lost Princess", "29d9889b566286506ded7d8bec7b75ce", 166502),
|
||||
FANGAME("Lost Weekend", "5952ce2396c162e478f49b93f64dbefc", 178443),
|
||||
FANGAME("Lost Weekend", "c1f3de4fedf59b0a7637bbb2a0ed16d6", 178443), // 24-bit version
|
||||
FANGAME("The Lost Skater", "5d1fa9ac715ce780ace9aa018caadb51", 334718),
|
||||
{ "wage",
|
||||
"Mac Spudd!",
|
||||
AD_ENTRY2s("Mac Spudd!","eaba9195dd27c2a26b809a730417122b",781859,
|
||||
"Mac Spudd! Sounds", "4c57693915fbe9ce6c72a40d84c4e929", 663598),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_GENERIC,
|
||||
GUIO0()
|
||||
},
|
||||
{ "wage",
|
||||
"Mac Spudd!",
|
||||
AD_ENTRY2s("Mac Spudd!","eaba9195dd27c2a26b809a730417122b",781859,
|
||||
"Mac Spudd! Sounds", "75fb4df3390de44be771bf6cb491b085", 663716),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_GENERIC,
|
||||
GUIO0()
|
||||
},
|
||||
FANGAME("MacWanker 1.0", "2fd407020adf527d1193f3351d7244b5", 178005),
|
||||
FANGAME("Magic Rings", "263e2c90af61f0798bf41f6a1e3f6345", 108788),
|
||||
FANGAMEN("Mansion!", "Mansion! 1.0", "c993619e5bd99ccca6e3ce28d93be33a", 389857),
|
||||
FANGAMEN("Maze of the Questing Beast", "MQB", "03775e1894809f24234aeaab18f39451", 134444),
|
||||
FANGAMEN("Maze of the Questing Beast", "MQB", "e2b89bd1ae34114445a4bb4795f0f514", 134444), // alt version
|
||||
FANGAMEN("Maze of the Questing Beast Solutions", "MQB Solutions", "8a27e515f12162cc8b85e4f2bd16a1e5", 37606),
|
||||
FANGAME("Messy House", "32ca71f2ff37997407cead590c2dd306", 176864),
|
||||
FANGAME("Midnight Snack", "70ba8a5a1f0304669c9987360bba236f", 67696),
|
||||
FANGAME("Midnight Snack", "24973af10822979e23866d88a7d2e15c", 67710), // alt version
|
||||
FANGAME("Midnight Snack", "0179e0fbc7370f31e4b64f40d2b44f71", 67110), // alt version
|
||||
FANGAME("Mike's House", "591b5a4d52ecf9a7d87256e83b78b0fd", 87101),
|
||||
FANGAME("Mike's House", "e4c0b836a21799db3995a921a447c28e", 87087), // alt version
|
||||
FANGAME("Mike's House", "79c5e9ffaacfd8aadddc7c2923655a41", 86775), // alt version
|
||||
FANGAME("Mindy - The Quest for Home", "a60f17662adf23146e5247d4d1e46d07", 117183),
|
||||
FANGAME("Minitorian", "c728dfccdca12571e275a4113b3db343", 586208),
|
||||
FANGAMEN("M'Lord's Warrior", "xn--M'Lord's Warrior -f82j", "e0a0622ce2023984e3118141a9688ec5", 465383), // Original file name is "M'Lord's Warrior †"
|
||||
FANGAMEN("Mormonoids from the Deep", "Mormonoids 1.0", "15c99d26ac2abb9eb4802a7dd5326086", 642975),
|
||||
FANGAMEN("Mormonoids from the Deep", "Mormonoids 1.25", "4730d0c47d13401d73353e980f91a304", 645062),
|
||||
FANGAMEN("Mormonoids from the Deep", "Mormonoids 1.25", "1a7ee052b375f0c0a4c18836c978ce5b", 645077), // alt version
|
||||
FANGAMEN("Mountain of Mayhem", "xn--Mountain of Mayhem -3g6k", "634211b004371635d191ae0687035501", 749747), // Original file name "Mountain of Mayhem †"
|
||||
FANGAME("Mountain of Mayhem", "c83a5703b3ea95f465839f4f54ef0805", 749733), // alt version
|
||||
FANGAME("Mystery of the Moors", "593dc2fcb92c0f5f4d94b5fc390a2ba2", 849909),
|
||||
FANGAME("Necropolis", "70fbba8a0b1626216f674df2ca9c58ba", 397396),
|
||||
FANGAME("Nightcrawler Ned", "8423fc015c395bd6be54a7ea69170d99", 366286),
|
||||
FANGAMEND("Odyssey", "Odyssey Demo", "c5b23c29bada41b587a86c7090570578", 256842),
|
||||
FANGAMEN("Parrot Talk", "PARROT TALK V1", "c38c090be8c078d931905c93bc0689f5", 118680),
|
||||
FANGAMEN("Parrot Talk", "PARROT TALKV2", "5ec1df9e2d6d0dcf1a040a95500d9551", 118628),
|
||||
FANGAME("Pavilion", "a980e60a291c0a7b949474177affa134", 231431),
|
||||
FANGAMEN("Pencils", "Pencils.99", "09dbcdbefe20536c2db1b1a4fb4e5ed3", 408295),
|
||||
FANGAMEN("Pencils", "Pencils.99", "9757cc28d82cea636ee260b9aa33c167", 407695), // alt version
|
||||
FANGAMEN("Pencils", "Pencils.99", "3ae42a43cd51aaf624ecfeea7d00d401", 408281), // alt version
|
||||
FANGAME("Periapt", "7e26a7827c694232624321a5a6844511", 405750),
|
||||
FANGAME("Periapt", "bc36e40de279d5f0844577fe702d9f64", 405750), // alt version
|
||||
FANGAME("Periapt", "661642865321fa81ce84ae2eedbc1aff", 405736), // alt version
|
||||
FANGAMEN("Periapt", "Periapt.1", "661642865321fa81ce84ae2eedbc1aff", 405736), // alt version
|
||||
FANGAME("The Phoenix", "bd6dabf7a19d2ab7902498a8513f8c71", 431387),
|
||||
FANGAMEN("The Phoenix v1.2", "The Phoenix", "fee9f1de7ad9096d084461d6066192b1", 431384),
|
||||
FANGAME("Pirate Attack!", "d4d3f59b57d5bf3dd690fd657ecdd9c6", 323722),
|
||||
FANGAME("Porno Writer", "9d521bf40f6824228266923774aadfea", 467342),
|
||||
FANGAME("Porno Writer 2.0", "b5228c22158ff40ccac36b6a61cc317a", 791379),
|
||||
FANGAME("Pretty Good Pornography 3.0", "c86c92ec69b464a197659b1bd80880b9", 1122264),
|
||||
FANGAME("Psychotic!", "d6229615b71b189f6ef71399a0856cd2", 367053),
|
||||
FANGAME("Psychotic!", "c7608f67592563b44f2f48fe5fec63ce", 367067), // alt version
|
||||
FANGAME("Psychotic!", "51aa5f2744ceb5666c9556bccee797d6", 367173), // another alt version
|
||||
FANGAME("Puzzle Piece Search", "6c21c1e0c6afef9300941abd7782dd16", 247437), // From Joshua's Worlds 1.0
|
||||
FANGAME("The Puzzle Piece Search", "8fa1d80dd3f1ed69f45d15d774968995", 247082), // From Joshua's Worlds
|
||||
FANGAME("The Puzzle Piece Search", "bb4d42fd5936ac7fec92a8efc1987653", 247128), // From Joshua's Worlds¸ alt version
|
||||
FANGAME("The Puzzle Piece Search", "fb839ac4f22427f44e99bcc5afd57a0b", 247068), // Standalone
|
||||
FANGAME("Pyramid of No Return", "4bf4c39b140f5aadb5f8c9a50153d18e", 384889),
|
||||
FANGAME("P-W Adventure", "a8e9f97ee02f01de588a4dbabe55ca3f", 218960),
|
||||
FANGAMEN("Pyramid of Ert", "Pyramid of Ert V1.2", "358b03ea9c978fbfd2ce2833daea00f8", 315527),
|
||||
FANGAME("Queen Quest", "7ca009dad76827ce008c3c7fa01cab0a", 56770),
|
||||
FANGAME("Queen Quest", "cde1255992101e0763687f45e0f47169", 56783), // alt version
|
||||
FANGAME("Quest for T-Rex", "51aa29d24ca702c8980e959706ef0b76", 592328),
|
||||
FANGAME("Quest for T-Rex", "f1edd78817e5a72fb7d85879f168fc8c", 592328), // alt version
|
||||
FANGAME("Quest for T-Rex", "31ef93ae4ee7b48e3720b977dcf480c3", 592314), // alt version
|
||||
FANGAME("Quest for T-Rex", "90d5ac734a3944480bdeb4a89524db41", 591728), // alt version
|
||||
FANGAMEN("Quest for T-Rex", "3 Quest for T-Rex (19901125)", "1ff85bc2ca98e016bc4809ba4499b58b", 595989), // alt version
|
||||
FANGAMEN("Quest for T-Rex", "Quest for T-Rex 1.2", "edd1164eccad3eaa23b3636000b5a047", 593924),
|
||||
FANGAMEN("Quest for the Dark Sword", "xn--Quest for the Dark Sword-3r4o", "d98c3879ff20ca7e835e3a630c2c74ef", 572320), // original file name "Quest for the Dark Sword™"
|
||||
FANGAMEN("Quester: The Forbidden Lands", "Quester (Demo)", "r:5f0b06526d3a951121333953f559da3f", 254868),
|
||||
FANGAMEN("Quester: The Forbidden Lands", "Quester-The Forbidden Lands", "5f0b06526d3a951121333953f559da3f", 254868),
|
||||
FANGAME("Radical Castle", "e424dea5542817435b4b6da41fd532cb", 355345),
|
||||
FANGAME("Radical Castle", "c8393e30ecf376b9635e30894eacecd0", 347022), // alt version
|
||||
FANGAME("Radical Castle", "be56ff729b11793dc3da76336ec1abe0", 358533), // alt version
|
||||
FANGAMEN("Radical Castle", "Radical Castle 1.0", "7c7701dcab013c65510f97a8712fc419", 347022),
|
||||
FANGAMEN("Radical Castle", "Radical Castle 2.0", "ff1e2292dc49622b53dc04f6fa2e3577", 357418),
|
||||
BIGGAME("raysmaze", "v1.5", "Ray's Maze1.5", "328096d3c7ccb838956c42b273f17b8e", 1408260),
|
||||
BIGGAME("raysmaze", "v1.5/alt", "Ray's Maze1.5", "401cd46df5e49fa4171ed398b3e0227b", 1408260),
|
||||
FANGAMEN("Realms & Reality", "Realms & Reality 1.2", "13ee1c40855bdf666edafcf554ec35d6", 492358),
|
||||
FANGAME("Ray's World Builder Demo", "eafea10db54530ac6e6128be31741542", 115800),
|
||||
FANGAME("Robot Planet", "1066f6b2892cda16c2f365d1ec97537c", 107089),
|
||||
FANGAME("Royal Malaise, Part 1", "a12d6990b2a0d1ce3cca5f19c8ef70cc", 306481),
|
||||
FANGAMEN("Sands of Time", "xn--Sands of Time-1s6g", "d065662865d0cb9065812479ed7d2795", 122416), // Original file name "Sands of Time†"
|
||||
FANGAMEN("Sands of Time", "Sands of Time", "466add24c5092937afca005041c7150b", 122380), // alt version
|
||||
BIGGAME("scepters", "", "Scepters", "ecb776fb660205fad5819a26f7e180b5", 347103), // original 1986 version
|
||||
BIGGAME("scepters", "", "Scepters", "e6c58e96b02f8eb46e0ccfe4f547045b", 346339), // alt version
|
||||
BIGGAME("scepters", "", "Scepters", "2c824f1bd7b22c575c7be86ac88ebd23", 347284), // alt version
|
||||
FANGAME("Schmoozer", "7bbb3d317d2074870c72b28d07984ef8", 221244),
|
||||
FANGAME("Schmoozer", "db31e320f286a5f4ab2c70022d24dcb2", 221244), // alt version
|
||||
FANGAME("sMythWorld", "8224b15a910c9e7515923107d138de77", 472101),
|
||||
FANGAME("Sorceror's Quest (Demo)", "r:ee8efbccea0807674af2bb53e6edf338", 531622),
|
||||
FANGAMEND("Space Adventure", "SpaceAdventure", "3908c75d639989a28993c59931fbe1ec", 155100),
|
||||
FANGAMEND("Space Adventure", "SpaceAdventure", "e38d524cb778ed0beb77ee9299f0ed45", 155100), // alt version
|
||||
FANGAMEND("Space Adventure", "SpaceAdventure", "6cc726d460c76fbe8a4d16ce1831d4e7", 155086), // alt version
|
||||
FANGAME("Space Adventure", "6cc726d460c76fbe8a4d16ce1831d4e7", 155086), // identical to the above, different file name
|
||||
FANGAME("SpaceAdventure (Demo)", "r:6cc726d460c76fbe8a4d16ce1831d4e7", 155086), // identical to the above, different file name
|
||||
FANGAMEN("SparGate", "SparGate- vNC.1", "a7a7bfc1825011c2df8b7a03b57fcac9", 611991),
|
||||
FANGAMEN("Spear of Destiny", "xn--SpearOfDestiny-ef3h", "ac00a26f04f83b47c278cc1d226f48df", 333409), // Original file name "SpearOfDestiny†"
|
||||
FANGAMEN("Spear of Destiny", "SpearOfDestiny", "64faf0d0324e25dbe5519a8bbdb43081", 332809), // alt version
|
||||
FANGAMEN("Spear of Destiny", "Spear Of Destiny", "93fa152f3de31c0202e978fe2e9d78f7", 333395), // alt version, different file name
|
||||
FANGAME("Spear of Destiny", "ea90bddd0925742351340cf88dd1c7a6", 620350), // alt version, different file name
|
||||
FANGAME("Star Trek", "3067332e6f0bb0314579f9bf102e1b56", 53064),
|
||||
FANGAME("Star Trek", "ad91a24fa79a64fd11fff84740073659", 47280), // alt version
|
||||
FANGAME("Star Trip", "3067332e6f0bb0314579f9bf102e1b56", 53064),
|
||||
FANGAME("Starport", "d9e25edc9ea5d2a8b29a3693de19ae2c", 253970),
|
||||
FANGAMEN("Strange Disappearance", "xn--Strange Disappearance -", "9d6e41b61c0fc90400e5da2fcb653a4a", 772026), // Original file name "Strange Disappearance "
|
||||
FANGAME("The Sultan's Palace", "fde31cbcc77b66969b4cfcd43075341e", 456599), // Releases titled "Palace of Sand" were made with FutureBasic
|
||||
FANGAMEN("Success", "Success 1.0", "bacb07694a4559ae617923484aa40505", 93935),
|
||||
FANGAMEN("Swamp Witch", "xn--Swamp Witch-dl3f", "bd8c8394be31f7845d55785b7ccfbbde", 739525), // Original file name "Swamp Witch†"
|
||||
FANGAME("Swamp Witch", "07463c8b3b908b0c493a41b949ac1ff5", 739875), // alt version
|
||||
FANGAME("Swamp Witch", "865a07a0356926d6cab8f14208e11f9c", 739511), // alt version
|
||||
FANGAME("Swamp Witch", "dcda6bb0f27ae66884cab75ffc04e0d9", 739525), // alt version
|
||||
FANGAME("Sweetspace Now!", "574dc7dd25543f7a516d6524f0c5ab33", 123557), // Comes with Jumble
|
||||
FANGAMEN("Sword of Ages", "Sword of Ages 2.1", "d6dfea87c29dfe32dd958bbba25c70f3", 218673),
|
||||
FANGAMEN("Sword of Siegfried", "Sword of Siegfried 1.0", "2ae8f21cfb228ce58ee47b767bdd8820", 234507),
|
||||
FANGAMEN("Sword of Siegfried", "Sword of Siegfried 1.0", "7a68b00019592f3aa671cae024f7e843", 234493), // alt version
|
||||
FANGAME("Sword of Siegfried", "7a68b00019592f3aa671cae024f7e843", 234493), // identical to the above, different file name
|
||||
FANGAME("Time Bomb", "e96f3e2efd1e3db6ad0be22180f5473c", 64308),
|
||||
FANGAME("Time Bomb", "976180f9be0d1029aaba7774fec9767c", 64322), // alt version
|
||||
FANGAMEN("The Time Squisher", "Time Squisher v 1.1", "3e296141e7a7c9b05e0dde779d9bb50d", 463526),
|
||||
FANGAMEND("Time Star", "xn--Time Star-eh2e", "761e91f3da7217dc9516879a8ba08fd9", 210562), // Original file name "Time Star™"
|
||||
FANGAMEND("Time Streak!!", "xn--Time Streak!!-8q9g", "e90f123a113e38c83c205b5fe50521fb", 483488), // Original file name "Time Streak!!™"
|
||||
FANGAME("The Tower", "4cd8755ccb5bbeaf2e5f7848a8daa033", 556283),
|
||||
FANGAME("The Tower", "r:a79282051dd190bb8d41c7600bdc157e", 733539), // alt version
|
||||
FANGAMEND("Tombworld", "xn--Demo TombWorld-8ra", "695734292024290d5d0aa6a66ff628f6", 663996), // Original file name "Demo TombWorld©"
|
||||
FANGAMEND("Tombworld", "DemoTombWorld", "4b3f56cfa595eab3919a918767e11870", 387403), // alt version
|
||||
BIGGAME("twisted", "", "Twisted! 1.1", "7226874582d7e5fa13004340e9c5ba2b", 957878),
|
||||
BIGGAME("twisted", "", "Twisted! 1.5", "786909ab45da5af2b96f3a96a63727f0", 960186),
|
||||
BIGGAME("twisted", "", "Twisted! 1.6", "6e0be4c7d83231e56a431dc4ef7bf978", 960698),
|
||||
FANGAME("The Village", "fd35cad8c61064d6c8aaadab7070ccad", 314572),
|
||||
FANGAME("The Village", "b84ac84be610498c4de5d9bd4fd36175", 314620), // alt version
|
||||
FANGAMEN("Volcano I", "Volcano", "r:d55531ddaa800b77e8f3da6385283183", 101635),
|
||||
FANGAMEN("Volcano", "Volcano 2.4", "d04eba23fe7d566fba0c9b34242d8f10", 227431),
|
||||
FANGAMEN("Volcano II", "xn--Volcano II-0y6e", "7941c08b34bc2408b98c0004c7154aeb", 82735), // Original file name "Volcano II†"
|
||||
FANGAMEN("Who Shot Brownie Dog?", "xn--Who Shot Brownie Dogv1.01-lba88n", "4e7a950567c73e98e4478e5683e943ca", 195923),
|
||||
FANGAME("Widow's Sons", "r:1bfae1966acb268c947a4ba69e0f0693", 65796),
|
||||
FANGAMEN("Wishing Well", "xn--Wishing Well -", "607a66461d65a73c1222d63dbde0175d", 103432), // original file name "Wishing Well "
|
||||
FANGAMEN("Wishing Well", "xn--Wishing Well -", "5fcd9ac410bffd6369fb8723e86e6420", 103421), // original file name "Wishing Well ", alt version
|
||||
FANGAME("The Wizard's Apprentice", "477c2d2d4672d968dfac1df2acef1dc9", 782568),
|
||||
FANGAME("Wizard's Warehouse", "18b4678c76961c1f3ae3013e13f691a3", 159492),
|
||||
FANGAME("Wizard's Warehouse", "d0ab2d4b0009eb61cbff925f8c382379", 158846), // alt version
|
||||
FANGAMEN("Wizard's Warehouse 2", "WizWarehouse 2.0", "e67ac67548236a73897a85cd12b30492", 230614),
|
||||
FANGAMEN("Wizard's Warehouse 2", "WizWarehouse 2.0", "418cc7a44477a4408ea365c8de86f577", 230614), // alt version
|
||||
FANGAME("Zhore's Xers 2.3", "r:a4d6748fc48988bd6d216468a56b14d6", 484867),
|
||||
FANGAME("ZikTuria", "a91559e6f04a67dcb9023a98a0faed77", 52716),
|
||||
FANGAMEN("Zoony", "xn--Zoony-j64b", "3f7418ea8672ea558521c178188cfce5", 154734), // original file name "Zoony™"
|
||||
FANGAMEN("Zoony", "xn--Zoony-j64b", "55d3d42b5dca9630eb28ad464f343c67", 154734), // original file name "Zoony™", alt version
|
||||
FANGAMEN("Zoony", "xn--Zoony-j64b", "ecbe19c53f9d5b7ef62e66db9ea42348", 154970), // original file name "Zoony™", alt version
|
||||
FANGAMEN("Zoony", "xn--Zoony-j64b", "d75c2fb1a6ddd15f7f6e25b721fb6bfb", 154853), // original file name "Zoony™", alt version
|
||||
FANGAMEN("Zoony", "xn--Zoony-j64b", "f0741c2378f03bc17969078658a5a6ca", 154148), // original file name "Zoony™", alt version
|
||||
|
||||
AD_TABLE_END_MARKER
|
||||
};
|
||||
|
||||
} // End of namespace Wage
|
||||
52
engines/wage/dt-internal.h
Normal file
52
engines/wage/dt-internal.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WAGE_DT_INTERNAL_H
|
||||
#define WAGE_DT_INTERNAL_H
|
||||
|
||||
namespace Wage {
|
||||
|
||||
typedef struct ImGuiImage {
|
||||
ImTextureID id;
|
||||
int width;
|
||||
int height;
|
||||
} ImGuiImage;
|
||||
|
||||
typedef struct ImGuiState {
|
||||
bool _showWorld = false;
|
||||
|
||||
Common::HashMap<Common::String, ImGuiImage> _images;
|
||||
Common::Array<ImGuiImage> _patternTextures;
|
||||
|
||||
ImGuiTextFilter _nameFilter;
|
||||
int _currentStep = 1;
|
||||
bool _showScriptWindow = false;
|
||||
int _selectedScene = 0;
|
||||
int _selectedObj = 0;
|
||||
int _selectedChr = 0;
|
||||
int _selectedSound = 0;
|
||||
} ImGuiState;
|
||||
|
||||
extern ImGuiState *_state;
|
||||
|
||||
}
|
||||
|
||||
#endif // WAGE_DT_INTERNAL_H
|
||||
495
engines/wage/entities.cpp
Normal file
495
engines/wage/entities.cpp
Normal file
@@ -0,0 +1,495 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "wage/wage.h"
|
||||
#include "wage/entities.h"
|
||||
#include "wage/design.h"
|
||||
#include "wage/gui.h"
|
||||
#include "wage/script.h"
|
||||
#include "wage/world.h"
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
#include "graphics/macgui/macfontmanager.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
void Designed::setDesignBounds(Common::Rect *bounds) {
|
||||
_designBounds = bounds;
|
||||
}
|
||||
|
||||
Designed::~Designed() {
|
||||
delete _design;
|
||||
delete _designBounds;
|
||||
}
|
||||
|
||||
Context::Context() {
|
||||
_visits = 0;
|
||||
_kills = 0;
|
||||
_experience = 0;
|
||||
_frozen = false;
|
||||
_freezeTimer = 0;
|
||||
|
||||
for (int i = 0; i < 26 * 9; i++)
|
||||
_userVariables[i] = 0;
|
||||
|
||||
for (int i = 0; i < 18; i++)
|
||||
_statVariables[i] = 0;
|
||||
}
|
||||
|
||||
Scene::Scene() {
|
||||
_resourceId = 0;
|
||||
|
||||
_script = NULL;
|
||||
_design = NULL;
|
||||
_textBounds = NULL;
|
||||
_font = NULL;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
_blocked[i] = false;
|
||||
|
||||
_soundFrequency = 0;
|
||||
_soundType = 0;
|
||||
_worldX = 0;
|
||||
_worldY = 0;
|
||||
|
||||
_visited = false;
|
||||
}
|
||||
|
||||
Scene::Scene(Common::String name, Common::SeekableReadStream *data) {
|
||||
debug(9, "Creating scene: %s", name.c_str());
|
||||
|
||||
_name = name;
|
||||
_classType = SCENE;
|
||||
_design = new Design(data);
|
||||
|
||||
_resourceId = 0;
|
||||
|
||||
_script = NULL;
|
||||
_textBounds = NULL;
|
||||
_font = NULL;
|
||||
|
||||
setDesignBounds(readRect(data));
|
||||
_worldY = data->readSint16BE();
|
||||
_worldX = data->readSint16BE();
|
||||
_blocked[NORTH] = (data->readByte() != 0);
|
||||
_blocked[SOUTH] = (data->readByte() != 0);
|
||||
_blocked[EAST] = (data->readByte() != 0);
|
||||
_blocked[WEST] = (data->readByte() != 0);
|
||||
_soundFrequency = data->readSint16BE();
|
||||
_soundType = data->readByte();
|
||||
data->readByte(); // unknown
|
||||
_messages[NORTH] = data->readPascalString();
|
||||
_messages[SOUTH] = data->readPascalString();
|
||||
_messages[EAST] = data->readPascalString();
|
||||
_messages[WEST] = data->readPascalString();
|
||||
_soundName = data->readPascalString();
|
||||
|
||||
_visited = false;
|
||||
|
||||
delete data;
|
||||
}
|
||||
|
||||
Scene::~Scene() {
|
||||
delete _script;
|
||||
delete _textBounds;
|
||||
delete _font;
|
||||
}
|
||||
|
||||
void Scene::paint(Graphics::ManagedSurface *surface, int x, int y) {
|
||||
Common::Rect r(x, y, surface->w + x, surface->h + y);
|
||||
surface->fillRect(r, kColorWhite);
|
||||
|
||||
_design->paint(surface, *((WageEngine *)g_engine)->_world->_patterns, x, y);
|
||||
|
||||
for (ObjList::const_iterator it = _objs.begin(); it != _objs.end(); ++it) {
|
||||
debug(2, "painting Obj: %s, index: %d, type: %d", (*it)->_name.c_str(), (*it)->_index, (*it)->_type);
|
||||
(*it)->_design->paint(surface, *((WageEngine *)g_engine)->_world->_patterns, x, y);
|
||||
}
|
||||
|
||||
for (ChrList::const_iterator it = _chrs.begin(); it != _chrs.end(); ++it) {
|
||||
debug(2, "painting Chr: %s", (*it)->_name.c_str());
|
||||
(*it)->_design->paint(surface, *((WageEngine *)g_engine)->_world->_patterns, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
Designed *Scene::lookUpEntity(int x, int y) {
|
||||
for (ObjList::const_iterator it = _objs.end(); it != _objs.begin(); ) {
|
||||
it--;
|
||||
if ((*it)->_design->isInBounds(x, y))
|
||||
return *it;
|
||||
}
|
||||
|
||||
for (ChrList::const_iterator it = _chrs.end(); it != _chrs.begin(); ) {
|
||||
it--;
|
||||
if ((*it)->_design->isInBounds(x, y))
|
||||
return *it;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Obj::Obj() : _currentOwner(NULL), _currentScene(NULL) {
|
||||
_index = 0;
|
||||
_resourceId = 0;
|
||||
_namePlural = false;
|
||||
_value = 0;
|
||||
_attackType = 0;
|
||||
_numberOfUses = 0;
|
||||
_returnToRandomScene = false;
|
||||
_type = 0;
|
||||
_accuracy = 0;
|
||||
_damage = 0;
|
||||
}
|
||||
|
||||
Obj::Obj(Common::String name, Common::SeekableReadStream *data, int resourceId) {
|
||||
_resourceId = resourceId;
|
||||
|
||||
_name = name;
|
||||
_classType = OBJ;
|
||||
_currentOwner = NULL;
|
||||
_currentScene = NULL;
|
||||
|
||||
_index = 0;
|
||||
|
||||
_design = new Design(data);
|
||||
|
||||
setDesignBounds(readRect(data));
|
||||
|
||||
int16 namePlural = data->readSint16BE();
|
||||
|
||||
if (namePlural == 256)
|
||||
_namePlural = true; // TODO: other flags?
|
||||
else if (namePlural == 0)
|
||||
_namePlural = false;
|
||||
else
|
||||
error("Obj <%s> had weird namePlural set (%d)", name.c_str(), namePlural);
|
||||
|
||||
if (data->readSint16BE() != 0)
|
||||
error("Obj <%s> had short set", name.c_str());
|
||||
|
||||
if (data->readByte() != 0)
|
||||
error("Obj <%s> had byte set", name.c_str());
|
||||
|
||||
_accuracy = data->readByte();
|
||||
_value = data->readByte();
|
||||
_type = data->readSByte();
|
||||
_damage = data->readByte();
|
||||
_attackType = data->readSByte();
|
||||
_numberOfUses = data->readSint16BE();
|
||||
int16 returnTo = data->readSint16BE();
|
||||
if (returnTo == 256) // TODO any other possibilities?
|
||||
_returnToRandomScene = true;
|
||||
else if (returnTo == 0)
|
||||
_returnToRandomScene = false;
|
||||
else
|
||||
error("Obj <%s> had weird returnTo set", name.c_str());
|
||||
|
||||
_sceneOrOwner = data->readPascalString();
|
||||
_clickMessage = data->readPascalString();
|
||||
_operativeVerb = data->readPascalString();
|
||||
_failureMessage = data->readPascalString();
|
||||
_useMessage = data->readPascalString();
|
||||
_sound = data->readPascalString();
|
||||
|
||||
delete data;
|
||||
}
|
||||
|
||||
Obj::~Obj() {
|
||||
}
|
||||
|
||||
Chr *Obj::removeFromChr() {
|
||||
if (_currentOwner != NULL) {
|
||||
for (int i = (int)_currentOwner->_inventory.size() - 1; i >= 0; i--)
|
||||
if (_currentOwner->_inventory[i] == this)
|
||||
_currentOwner->_inventory.remove_at(i);
|
||||
|
||||
for (int i = 0; i < Chr::NUMBER_OF_ARMOR_TYPES; i++) {
|
||||
if (_currentOwner->_armor[i] == this) {
|
||||
_currentOwner->_armor[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _currentOwner;
|
||||
}
|
||||
|
||||
Designed *Obj::removeFromCharOrScene() {
|
||||
Designed *from = removeFromChr();
|
||||
|
||||
if (_currentScene != NULL) {
|
||||
_currentScene->_objs.remove(this);
|
||||
from = _currentScene;
|
||||
}
|
||||
|
||||
return from;
|
||||
}
|
||||
|
||||
void Obj::resetState(Chr *owner, Scene *scene) {
|
||||
removeFromCharOrScene();
|
||||
|
||||
warning("STUB: Obj::resetState()");
|
||||
}
|
||||
|
||||
Chr::Chr(Common::String name, Common::SeekableReadStream *data) {
|
||||
_name = name;
|
||||
_classType = CHR;
|
||||
_design = new Design(data);
|
||||
|
||||
_index = 0;
|
||||
_resourceId = 0;
|
||||
_currentScene = NULL;
|
||||
|
||||
setDesignBounds(readRect(data));
|
||||
|
||||
_physicalStrength = data->readByte();
|
||||
_physicalHp = data->readByte();
|
||||
_naturalArmor = data->readByte();
|
||||
_physicalAccuracy = data->readByte();
|
||||
|
||||
_spiritualStength = data->readByte();
|
||||
_spiritialHp = data->readByte();
|
||||
_resistanceToMagic = data->readByte();
|
||||
_spiritualAccuracy = data->readByte();
|
||||
|
||||
_runningSpeed = data->readByte();
|
||||
_rejectsOffers = data->readByte();
|
||||
_followsOpponent = data->readByte();
|
||||
|
||||
data->readSByte(); // TODO: ???
|
||||
data->readSint32BE(); // TODO: ???
|
||||
|
||||
_weaponDamage1 = data->readByte();
|
||||
_weaponDamage2 = data->readByte();
|
||||
|
||||
data->readSByte(); // TODO: ???
|
||||
|
||||
if (data->readSByte() == 1)
|
||||
_playerCharacter = true;
|
||||
else
|
||||
_playerCharacter = false;
|
||||
|
||||
_maximumCarriedObjects = data->readByte();
|
||||
_returnTo = data->readSByte();
|
||||
|
||||
_winningWeapons = data->readByte();
|
||||
_winningMagic = data->readByte();
|
||||
_winningRun = data->readByte();
|
||||
_winningOffer = data->readByte();
|
||||
_losingWeapons = data->readByte();
|
||||
_losingMagic = data->readByte();
|
||||
_losingRun = data->readByte();
|
||||
_losingOffer = data->readByte();
|
||||
|
||||
_gender = data->readSByte();
|
||||
if (data->readSByte() == 1)
|
||||
_nameProperNoun = true;
|
||||
else
|
||||
_nameProperNoun = false;
|
||||
|
||||
_initialScene = data->readPascalString();
|
||||
_nativeWeapon1 = data->readPascalString();
|
||||
_operativeVerb1 = data->readPascalString();
|
||||
_nativeWeapon2 = data->readPascalString();
|
||||
_operativeVerb2 = data->readPascalString();
|
||||
|
||||
_initialComment = data->readPascalString();
|
||||
_scoresHitComment = data->readPascalString();
|
||||
_receivesHitComment = data->readPascalString();
|
||||
_makesOfferComment = data->readPascalString();
|
||||
_rejectsOfferComment = data->readPascalString();
|
||||
_acceptsOfferComment = data->readPascalString();
|
||||
_dyingWords = data->readPascalString();
|
||||
|
||||
_initialSound = data->readPascalString();
|
||||
_scoresHitSound = data->readPascalString();
|
||||
_receivesHitSound = data->readPascalString();
|
||||
_dyingSound = data->readPascalString();
|
||||
|
||||
_weaponSound1 = data->readPascalString();
|
||||
_weaponSound2 = data->readPascalString();
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_ARMOR_TYPES; i++)
|
||||
_armor[i] = NULL;
|
||||
|
||||
_weapon1 = NULL;
|
||||
_weapon2 = NULL;
|
||||
|
||||
// Create native weapons
|
||||
if (!_nativeWeapon1.empty() && !_operativeVerb1.empty()) {
|
||||
_weapon1 = new Obj;
|
||||
|
||||
_weapon1->_name = _nativeWeapon1;
|
||||
_weapon1->_operativeVerb = _operativeVerb1;
|
||||
_weapon1->_type = Obj::REGULAR_WEAPON;
|
||||
_weapon1->_accuracy = 0;
|
||||
_weapon1->_damage = _weaponDamage1;
|
||||
_weapon1->_sound = _weaponSound1;
|
||||
}
|
||||
|
||||
if (!_nativeWeapon2.empty() && !_operativeVerb2.empty()) {
|
||||
_weapon2 = new Obj;
|
||||
|
||||
_weapon2->_name = _nativeWeapon2;
|
||||
_weapon2->_operativeVerb = _operativeVerb2;
|
||||
_weapon2->_type = Obj::REGULAR_WEAPON;
|
||||
_weapon2->_accuracy = 0;
|
||||
_weapon2->_damage = _weaponDamage2;
|
||||
_weapon2->_sound = _weaponSound2;
|
||||
}
|
||||
|
||||
delete data;
|
||||
}
|
||||
|
||||
Chr::~Chr() {
|
||||
delete _weapon1;
|
||||
delete _weapon2;
|
||||
}
|
||||
|
||||
void Chr::resetState() {
|
||||
_context._statVariables[PHYS_STR_BAS] = _context._statVariables[PHYS_STR_CUR] = _physicalStrength;
|
||||
_context._statVariables[PHYS_HIT_BAS] = _context._statVariables[PHYS_HIT_CUR] = _physicalHp;
|
||||
_context._statVariables[PHYS_ARM_BAS] = _context._statVariables[PHYS_ARM_CUR] = _naturalArmor;
|
||||
_context._statVariables[PHYS_ACC_BAS] = _context._statVariables[PHYS_ACC_CUR] = _physicalAccuracy;
|
||||
|
||||
_context._statVariables[SPIR_STR_BAS] = _context._statVariables[SPIR_STR_CUR] = _spiritualStength;
|
||||
_context._statVariables[SPIR_HIT_BAS] = _context._statVariables[SPIR_HIT_CUR] = _spiritialHp;
|
||||
_context._statVariables[SPIR_ARM_BAS] = _context._statVariables[SPIR_ARM_CUR] = _naturalArmor;
|
||||
_context._statVariables[SPIR_ACC_BAS] = _context._statVariables[SPIR_ACC_CUR] = _physicalAccuracy;
|
||||
|
||||
_context._statVariables[PHYS_SPE_BAS] = _context._statVariables[PHYS_SPE_CUR] = _runningSpeed;
|
||||
}
|
||||
|
||||
ObjArray *Chr::getWeapons(bool includeMagic) {
|
||||
ObjArray *list = new ObjArray;
|
||||
|
||||
if (_weapon1)
|
||||
list->push_back(_weapon1);
|
||||
|
||||
if (_weapon2)
|
||||
list->push_back(_weapon2);
|
||||
|
||||
for (uint i = 0; i < _inventory.size(); i++)
|
||||
switch (_inventory[i]->_type) {
|
||||
case Obj::REGULAR_WEAPON:
|
||||
case Obj::THROW_WEAPON:
|
||||
list->push_back(_inventory[i]);
|
||||
break;
|
||||
case Obj::MAGICAL_OBJECT:
|
||||
if (includeMagic)
|
||||
list->push_back(_inventory[i]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
ObjArray *Chr::getMagicalObjects() {
|
||||
ObjArray *list = new ObjArray;
|
||||
|
||||
for (uint i = 0; i < _inventory.size(); i++)
|
||||
if (_inventory[i]->_type == Obj::MAGICAL_OBJECT)
|
||||
list->push_back(_inventory[i]);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void Chr::wearObjs() {
|
||||
for (uint i = 0; i < _inventory.size(); i++)
|
||||
wearObjIfPossible(_inventory[i]);
|
||||
}
|
||||
|
||||
int Chr::wearObjIfPossible(Obj *obj) {
|
||||
switch (obj->_type) {
|
||||
case Obj::HELMET:
|
||||
if (_armor[HEAD_ARMOR] == NULL) {
|
||||
_armor[HEAD_ARMOR] = obj;
|
||||
return Chr::HEAD_ARMOR;
|
||||
}
|
||||
break;
|
||||
case Obj::CHEST_ARMOR:
|
||||
if (_armor[BODY_ARMOR] == NULL) {
|
||||
_armor[BODY_ARMOR] = obj;
|
||||
return Chr::BODY_ARMOR;
|
||||
}
|
||||
break;
|
||||
case Obj::SHIELD:
|
||||
if (_armor[SHIELD_ARMOR] == NULL) {
|
||||
_armor[SHIELD_ARMOR] = obj;
|
||||
return Chr::SHIELD_ARMOR;
|
||||
}
|
||||
break;
|
||||
case Obj::SPIRITUAL_ARMOR:
|
||||
if (_armor[MAGIC_ARMOR] == NULL) {
|
||||
_armor[MAGIC_ARMOR] = obj;
|
||||
return Chr::MAGIC_ARMOR;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *Chr::getDefiniteArticle(bool capitalize) {
|
||||
if (!_nameProperNoun)
|
||||
return capitalize ? "The " : "the ";
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool Chr::isWearing(Obj *obj) {
|
||||
for (int i = 0; i < NUMBER_OF_ARMOR_TYPES; i++)
|
||||
if (_armor[i] == obj)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Wage
|
||||
343
engines/wage/entities.h
Normal file
343
engines/wage/entities.h
Normal file
@@ -0,0 +1,343 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WAGE_ENTITIES_H
|
||||
#define WAGE_ENTITIES_H
|
||||
|
||||
namespace Graphics {
|
||||
class ManagedSurface;
|
||||
class MacFont;
|
||||
}
|
||||
|
||||
namespace Wage {
|
||||
|
||||
class Design;
|
||||
class Script;
|
||||
|
||||
enum StatVariable {
|
||||
/** The base physical accuracy of the player. */
|
||||
PHYS_ACC_BAS = 0,
|
||||
/** The current physical accuracy of the player. */
|
||||
PHYS_ACC_CUR = 1,
|
||||
/** The base physical armor of the player. */
|
||||
PHYS_ARM_BAS = 2,
|
||||
/** The current physical armor of the player. */
|
||||
PHYS_ARM_CUR = 3,
|
||||
/** The base physical hit points of the player. */
|
||||
PHYS_HIT_BAS = 4,
|
||||
/** The current physical hit points of the player. */
|
||||
PHYS_HIT_CUR = 5,
|
||||
/** The base physical speed of the player. */
|
||||
PHYS_SPE_BAS = 6,
|
||||
/** The current physical speed of the player. */
|
||||
PHYS_SPE_CUR = 7,
|
||||
/** The base physical strength of the player. */
|
||||
PHYS_STR_BAS = 8,
|
||||
/** The current physical strength of the player. */
|
||||
PHYS_STR_CUR = 9,
|
||||
/** The base spiritual accuracy of the player. */
|
||||
SPIR_ACC_BAS = 10,
|
||||
/** The current spiritual accuracy of the player. */
|
||||
SPIR_ACC_CUR = 11,
|
||||
/** The base spiritual armor of the player. */
|
||||
SPIR_ARM_BAS = 12,
|
||||
/** The current spiritual armor of the player. */
|
||||
SPIR_ARM_CUR = 13,
|
||||
/** The base spiritual hit points of the player. */
|
||||
SPIR_HIT_BAS = 14,
|
||||
/** The current spiritual hit points of the player. */
|
||||
SPIR_HIT_CUR = 15,
|
||||
/** The base spiritual strength of the player. */
|
||||
SPIR_STR_BAS = 16,
|
||||
/** The current spiritual strength of the player. */
|
||||
SPIR_STR_CUR = 17
|
||||
};
|
||||
|
||||
class Context {
|
||||
public:
|
||||
Context();
|
||||
|
||||
int16 _visits; // Number of scenes visited, including repeated visits
|
||||
int16 _kills; // Number of characters killed
|
||||
int16 _experience;
|
||||
bool _frozen;
|
||||
int16 _freezeTimer;
|
||||
int16 _userVariables[26 * 9];
|
||||
int16 _statVariables[18];
|
||||
};
|
||||
|
||||
class Designed {
|
||||
public:
|
||||
Designed() : _design(NULL), _designBounds(NULL), _classType(UNKNOWN) {}
|
||||
~Designed();
|
||||
|
||||
Common::String _name;
|
||||
Design *_design;
|
||||
Common::Rect *_designBounds;
|
||||
OperandType _classType;
|
||||
|
||||
Common::Rect *getDesignBounds() {
|
||||
return _designBounds == NULL ? NULL : new Common::Rect(*_designBounds);
|
||||
}
|
||||
|
||||
void setDesignBounds(Common::Rect *bounds);
|
||||
|
||||
Common::String toString() const { return _name; }
|
||||
};
|
||||
|
||||
class Chr : public Designed {
|
||||
public:
|
||||
enum ChrDestination {
|
||||
RETURN_TO_STORAGE = 0,
|
||||
RETURN_TO_RANDOM_SCENE = 1,
|
||||
RETURN_TO_INITIAL_SCENE = 2
|
||||
};
|
||||
|
||||
enum ChrPart {
|
||||
HEAD = 0,
|
||||
CHEST = 1,
|
||||
SIDE = 2
|
||||
};
|
||||
|
||||
enum ChrArmorType {
|
||||
HEAD_ARMOR = 0,
|
||||
BODY_ARMOR = 1,
|
||||
SHIELD_ARMOR = 2,
|
||||
MAGIC_ARMOR = 3,
|
||||
NUMBER_OF_ARMOR_TYPES = 4
|
||||
};
|
||||
|
||||
Chr(Common::String name, Common::SeekableReadStream *data);
|
||||
~Chr();
|
||||
|
||||
int _index;
|
||||
int _resourceId;
|
||||
Common::String _initialScene;
|
||||
int _gender;
|
||||
bool _nameProperNoun;
|
||||
bool _playerCharacter;
|
||||
uint _maximumCarriedObjects;
|
||||
int _returnTo;
|
||||
|
||||
int _physicalStrength;
|
||||
int _physicalHp;
|
||||
int _naturalArmor;
|
||||
int _physicalAccuracy;
|
||||
int _spiritualStength;
|
||||
int _spiritialHp;
|
||||
int _resistanceToMagic;
|
||||
int _spiritualAccuracy;
|
||||
int _runningSpeed;
|
||||
uint _rejectsOffers;
|
||||
int _followsOpponent;
|
||||
|
||||
Common::String _initialSound;
|
||||
Common::String _scoresHitSound;
|
||||
Common::String _receivesHitSound;
|
||||
Common::String _dyingSound;
|
||||
|
||||
Common::String _nativeWeapon1;
|
||||
Common::String _operativeVerb1;
|
||||
int _weaponDamage1;
|
||||
Common::String _weaponSound1;
|
||||
|
||||
Common::String _nativeWeapon2;
|
||||
Common::String _operativeVerb2;
|
||||
int _weaponDamage2;
|
||||
Common::String _weaponSound2;
|
||||
|
||||
int _winningWeapons;
|
||||
int _winningMagic;
|
||||
int _winningRun;
|
||||
int _winningOffer;
|
||||
int _losingWeapons;
|
||||
int _losingMagic;
|
||||
int _losingRun;
|
||||
int _losingOffer;
|
||||
|
||||
Common::String _initialComment;
|
||||
Common::String _scoresHitComment;
|
||||
Common::String _receivesHitComment;
|
||||
Common::String _makesOfferComment;
|
||||
Common::String _rejectsOfferComment;
|
||||
Common::String _acceptsOfferComment;
|
||||
Common::String _dyingWords;
|
||||
|
||||
Scene *_currentScene;
|
||||
ObjArray _inventory;
|
||||
|
||||
Obj *_armor[NUMBER_OF_ARMOR_TYPES];
|
||||
|
||||
Context _context;
|
||||
|
||||
ObjArray *getWeapons(bool includeMagic);
|
||||
ObjArray *getMagicalObjects();
|
||||
const char *getDefiniteArticle(bool capitalize);
|
||||
|
||||
Obj *_weapon1;
|
||||
Obj *_weapon2;
|
||||
|
||||
public:
|
||||
int wearObjIfPossible(Obj *obj);
|
||||
void wearObjs();
|
||||
|
||||
void resetState();
|
||||
|
||||
bool isWearing(Obj *obj);
|
||||
};
|
||||
|
||||
class Obj : public Designed {
|
||||
public:
|
||||
Obj();
|
||||
Obj(Common::String name, Common::SeekableReadStream *data, int resourceId);
|
||||
~Obj();
|
||||
|
||||
enum ObjectType {
|
||||
REGULAR_WEAPON = 1,
|
||||
THROW_WEAPON = 2,
|
||||
MAGICAL_OBJECT = 3,
|
||||
HELMET = 4,
|
||||
SHIELD = 5,
|
||||
CHEST_ARMOR = 6,
|
||||
SPIRITUAL_ARMOR = 7,
|
||||
MOBILE_OBJECT = 8,
|
||||
IMMOBILE_OBJECT = 9
|
||||
};
|
||||
|
||||
enum AttackType {
|
||||
CAUSES_PHYSICAL_DAMAGE = 0,
|
||||
CAUSES_SPIRITUAL_DAMAGE = 1,
|
||||
CAUSES_PHYSICAL_AND_SPIRITUAL_DAMAGE = 2,
|
||||
HEALS_PHYSICAL_DAMAGE = 3,
|
||||
HEALS_SPIRITUAL_DAMAGE = 4,
|
||||
HEALS_PHYSICAL_AND_SPIRITUAL_DAMAGE = 5,
|
||||
FREEZES_OPPONENT = 6
|
||||
};
|
||||
|
||||
public:
|
||||
int _index;
|
||||
int _resourceId;
|
||||
bool _namePlural;
|
||||
uint _value;
|
||||
int _attackType;
|
||||
int _numberOfUses;
|
||||
bool _returnToRandomScene;
|
||||
Common::String _sceneOrOwner;
|
||||
Common::String _clickMessage;
|
||||
Common::String _failureMessage;
|
||||
Common::String _useMessage;
|
||||
|
||||
Scene *_currentScene;
|
||||
Chr *_currentOwner;
|
||||
|
||||
int _type;
|
||||
uint _accuracy;
|
||||
Common::String _operativeVerb;
|
||||
int _damage;
|
||||
Common::String _sound;
|
||||
|
||||
public:
|
||||
void setCurrentOwner(Chr *currentOwner) {
|
||||
_currentOwner = currentOwner;
|
||||
if (currentOwner != NULL)
|
||||
_currentScene = NULL;
|
||||
}
|
||||
|
||||
void setCurrentScene(Scene *currentScene) {
|
||||
_currentScene = currentScene;
|
||||
if (currentScene != NULL)
|
||||
_currentOwner = NULL;
|
||||
}
|
||||
|
||||
Chr *removeFromChr();
|
||||
Designed *removeFromCharOrScene();
|
||||
|
||||
void resetState(Chr *owner, Scene *scene);
|
||||
};
|
||||
|
||||
class Scene : public Designed {
|
||||
public:
|
||||
enum SceneTypes {
|
||||
PERIODIC = 0,
|
||||
RANDOM = 1
|
||||
};
|
||||
|
||||
int _resourceId;
|
||||
|
||||
Script *_script;
|
||||
Common::String _text;
|
||||
Common::Rect *_textBounds;
|
||||
Graphics::MacFont *_font;
|
||||
bool _blocked[4];
|
||||
Common::String _messages[4];
|
||||
int _soundFrequency; // times a minute, max 3600
|
||||
int _soundType;
|
||||
Common::String _soundName;
|
||||
int _worldX;
|
||||
int _worldY;
|
||||
bool _visited;
|
||||
|
||||
ObjList _objs;
|
||||
ChrList _chrs;
|
||||
|
||||
Scene();
|
||||
Scene(Common::String name, Common::SeekableReadStream *data);
|
||||
~Scene();
|
||||
|
||||
Designed *lookUpEntity(int x, int y);
|
||||
|
||||
Common::Rect *getTextBounds() {
|
||||
return _textBounds == NULL ? NULL : new Common::Rect(*_textBounds);
|
||||
}
|
||||
|
||||
void paint(Graphics::ManagedSurface *screen, int x, int y);
|
||||
|
||||
const Graphics::MacFont *getFont() { return _font; }
|
||||
};
|
||||
|
||||
} // End of namespace Wage
|
||||
|
||||
#endif
|
||||
674
engines/wage/gui.cpp
Normal file
674
engines/wage/gui.cpp
Normal file
@@ -0,0 +1,674 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/timer.h"
|
||||
#include "common/system.h"
|
||||
#include "common/config-manager.h"
|
||||
|
||||
#include "audio/softsynth/pcspk.h"
|
||||
|
||||
#include "graphics/primitives.h"
|
||||
#include "graphics/macgui/macfontmanager.h"
|
||||
#include "graphics/macgui/macdialog.h"
|
||||
#include "graphics/macgui/macwindowmanager.h"
|
||||
#include "graphics/macgui/macwindow.h"
|
||||
#include "graphics/macgui/macmenu.h"
|
||||
|
||||
#include "wage/wage.h"
|
||||
#include "wage/design.h"
|
||||
#include "wage/entities.h"
|
||||
#include "wage/gui.h"
|
||||
#include "wage/world.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
static const Graphics::MacMenuData menuSubItems[] = {
|
||||
{ kMenuHighLevel, "File", 0, 0, false },
|
||||
{ kMenuHighLevel, "Edit", 0, 0, false },
|
||||
{ kMenuFile, "New", kMenuActionNew, 0, true },
|
||||
{ kMenuFile, "Open...", kMenuActionOpen, 0, true },
|
||||
{ kMenuFile, "Close", kMenuActionClose, 0, false },
|
||||
{ kMenuFile, "Save", kMenuActionSave, 0, false },
|
||||
{ kMenuFile, "Save as...", kMenuActionSaveAs, 0, true },
|
||||
{ kMenuFile, "Revert", kMenuActionRevert, 0, false },
|
||||
{ kMenuFile, "Quit", kMenuActionQuit, 0, true },
|
||||
|
||||
{ kMenuEdit, "Undo", kMenuActionUndo, 'Z', false },
|
||||
{ kMenuEdit, NULL, 0, 0, false },
|
||||
{ kMenuEdit, "Cut", kMenuActionCut, 'K', false },
|
||||
{ kMenuEdit, "Copy", kMenuActionCopy, 'C', false },
|
||||
{ kMenuEdit, "Paste", kMenuActionPaste, 'V', false },
|
||||
{ kMenuEdit, "Clear", kMenuActionClear, 'B', false },
|
||||
|
||||
{ 0, NULL, 0, 0, false }
|
||||
};
|
||||
|
||||
static bool consoleWindowCallback(WindowClick click, Common::Event &event, void *gui);
|
||||
static bool sceneWindowCallback(WindowClick click, Common::Event &event, void *gui);
|
||||
static void menuCommandsCallback(int action, Common::String &text, void *data);
|
||||
|
||||
|
||||
Gui::Gui(WageEngine *engine) {
|
||||
_engine = engine;
|
||||
_scene = NULL;
|
||||
_sceneDirty = true;
|
||||
_screen.create(g_system->getWidth(), g_system->getHeight(), Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
_wm = new Graphics::MacWindowManager(Graphics::kWMNoScummVMWallpaper);
|
||||
_wm->_fontMan->loadFonts(Common::Path(engine->getGameFile()));
|
||||
_wm->setScreen(&_screen);
|
||||
|
||||
_menu = _wm->addMenu();
|
||||
|
||||
_menu->setCommandsCallback(menuCommandsCallback, this);
|
||||
|
||||
_menu->addStaticMenus(menuSubItems);
|
||||
_menu->addSubMenu(nullptr, kMenuAbout);
|
||||
_menu->addMenuItem(_menu->getSubmenu(nullptr, kMenuAbout), _engine->_world->getAboutMenuItemName(), kMenuActionAbout);
|
||||
|
||||
_commandsMenuId = _menu->addMenuItem(nullptr, _engine->_world->_commandsMenuName);
|
||||
regenCommandsMenu();
|
||||
|
||||
if (!_engine->_world->_weaponMenuDisabled) {
|
||||
_weaponsMenuId = _menu->addMenuItem(nullptr, _engine->_world->_weaponsMenuName);
|
||||
|
||||
regenWeaponsMenu();
|
||||
} else {
|
||||
_weaponsMenuId = -1;
|
||||
}
|
||||
|
||||
_menu->calcDimensions();
|
||||
|
||||
if (g_system->hasTextInClipboard()) {
|
||||
_menu->enableCommand(kMenuEdit, kMenuActionPaste, true);
|
||||
}
|
||||
|
||||
_sceneWindow = _wm->addWindow(false, false, false);
|
||||
_sceneWindow->setCallback(sceneWindowCallback, this);
|
||||
|
||||
//TODO: Make the font we use here work
|
||||
// (currently MacFontRun::getFont gets called with the fonts being uninitialized,
|
||||
// so it initializes them by itself with default params, and not those here)
|
||||
const Graphics::MacFont *font = new Graphics::MacFont(Graphics::kMacFontSystem, 8);
|
||||
|
||||
uint maxWidth = _screen.w;
|
||||
|
||||
_consoleWindow = _wm->addTextWindow(font, kColorBlack, kColorWhite, maxWidth, Graphics::kTextAlignLeft, _menu, 4);
|
||||
_consoleWindow->setCallback(consoleWindowCallback, this);
|
||||
_consoleWindow->setBorderColor(kColorWhite);
|
||||
_consoleWindow->setEditable(true);
|
||||
|
||||
_selectedMenuItem = -1;
|
||||
|
||||
loadBorders();
|
||||
}
|
||||
|
||||
Gui::~Gui() {
|
||||
_screen.free();
|
||||
_console.free();
|
||||
delete _wm;
|
||||
}
|
||||
|
||||
void Gui::draw() {
|
||||
if (_engine->_isGameOver) {
|
||||
_wm->draw();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_engine->_world->_player->_currentScene)
|
||||
return;
|
||||
|
||||
if (_scene != _engine->_world->_player->_currentScene) {
|
||||
_sceneDirty = true;
|
||||
|
||||
_scene = _engine->_world->_player->_currentScene;
|
||||
|
||||
Common::Rect sceneBounds = *_scene->_designBounds;
|
||||
const Graphics::BorderOffsets &offsets = _sceneWindow->getBorderOffsets();
|
||||
|
||||
int maxTitleWidth = sceneBounds.width() - (kWindowMinWidth - offsets.right);
|
||||
Common::String displayTitle = _scene->_name;
|
||||
|
||||
if (maxTitleWidth > 0) {
|
||||
const Graphics::Font *titleFont = getTitleFont();
|
||||
if (titleFont) {
|
||||
// keep deleting the last character untill the title fits
|
||||
while (displayTitle.size() > 0 && titleFont->getStringWidth(displayTitle) > maxTitleWidth) {
|
||||
displayTitle.deleteLastChar();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
displayTitle.clear();
|
||||
}
|
||||
|
||||
_sceneWindow->setTitle(displayTitle);
|
||||
_sceneWindow->setDimensions(sceneBounds);
|
||||
_consoleWindow->setDimensions(*_scene->_textBounds);
|
||||
|
||||
_wm->setActiveWindow(_consoleWindow->getId());
|
||||
_wm->setFullRefresh(true);
|
||||
}
|
||||
|
||||
drawScene();
|
||||
|
||||
_wm->draw();
|
||||
|
||||
_sceneDirty = false;
|
||||
}
|
||||
|
||||
void Gui::drawScene() {
|
||||
if (!_sceneDirty)
|
||||
return;
|
||||
|
||||
_scene->paint(_sceneWindow->getWindowSurface(), 0, 0);
|
||||
_sceneWindow->setDirty(true);
|
||||
|
||||
_sceneDirty = true;
|
||||
_menu->setDirty(true);
|
||||
}
|
||||
|
||||
static bool consoleWindowCallback(WindowClick click, Common::Event &event, void *g) {
|
||||
Gui *gui = (Gui *)g;
|
||||
|
||||
return gui->processConsoleEvents(click, event);
|
||||
}
|
||||
|
||||
bool Gui::processConsoleEvents(WindowClick click, Common::Event &event) {
|
||||
if (click == kBorderCloseButton && event.type == Common::EVENT_LBUTTONUP) {
|
||||
_engine->quitGame();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool sceneWindowCallback(WindowClick click, Common::Event &event, void *g) {
|
||||
Gui *gui = (Gui *)g;
|
||||
|
||||
return gui->processSceneEvents(click, event);
|
||||
}
|
||||
|
||||
bool Gui::processSceneEvents(WindowClick click, Common::Event &event) {
|
||||
if (click == kBorderInner && event.type == Common::EVENT_LBUTTONUP) {
|
||||
Designed *obj = _scene->lookUpEntity(event.mouse.x, event.mouse.y);
|
||||
|
||||
if (obj != nullptr)
|
||||
_engine->processTurn(NULL, obj);
|
||||
|
||||
return true;
|
||||
}
|
||||
if (click == kBorderCloseButton && event.type == Common::EVENT_LBUTTONUP) {
|
||||
_engine->quitGame();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.type == Common::EVENT_KEYDOWN) {
|
||||
return _consoleWindow->processEvent(event);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Menu stuff
|
||||
////////////////
|
||||
void Gui::regenCommandsMenu() {
|
||||
_menu->createSubMenuFromString(_commandsMenuId, _engine->_world->_commandsMenu.c_str(), kMenuActionCommand);
|
||||
}
|
||||
|
||||
void Gui::regenWeaponsMenu() {
|
||||
if (_engine->_world->_weaponMenuDisabled)
|
||||
return;
|
||||
|
||||
_menu->clearSubMenu(_weaponsMenuId);
|
||||
|
||||
Chr *player = _engine->_world->_player;
|
||||
if (!player) {
|
||||
warning("regenWeaponsMenu: player is not defined");
|
||||
return;
|
||||
}
|
||||
ObjArray *weapons = player->getWeapons(true);
|
||||
|
||||
bool empty = true;
|
||||
|
||||
Graphics::MacMenuSubMenu *submenu = _menu->getSubmenu(nullptr, _weaponsMenuId);
|
||||
if (submenu == nullptr)
|
||||
submenu = _menu->addSubMenu(nullptr, _weaponsMenuId);
|
||||
|
||||
for (uint i = 0; i < weapons->size(); i++) {
|
||||
Obj *obj = (*weapons)[i];
|
||||
if (obj->_type == Obj::REGULAR_WEAPON ||
|
||||
obj->_type == Obj::THROW_WEAPON ||
|
||||
obj->_type == Obj::MAGICAL_OBJECT) {
|
||||
Common::String command(obj->_operativeVerb);
|
||||
command += " ";
|
||||
command += obj->_name;
|
||||
|
||||
_menu->addMenuItem(submenu, command, kMenuActionCommand, 0, 0, true);
|
||||
|
||||
empty = false;
|
||||
}
|
||||
}
|
||||
delete weapons;
|
||||
|
||||
if (empty)
|
||||
_menu->addMenuItem(submenu, "You have no weapons", 0, 0, 0, false);
|
||||
}
|
||||
|
||||
bool Gui::processEvent(Common::Event &event) {
|
||||
if (event.type == Common::EVENT_CLIPBOARD_UPDATE) {
|
||||
_menu->enableCommand(kMenuEdit, kMenuActionPaste, true);
|
||||
}
|
||||
|
||||
if (event.type == Common::EVENT_MOUSEMOVE) {
|
||||
bool mouseOnItem = false;
|
||||
for (int i = 0; i < _menu->numberOfMenus(); i++) {
|
||||
Graphics::MacMenuItem *menuItem = _menu->getMenuItem(i);
|
||||
|
||||
if (menuItem->enabled && menuItem->bbox.contains(event.mouse.x, event.mouse.y)) {
|
||||
if (_selectedMenuItem != i) {
|
||||
_engine->sayText(menuItem->text, Common::TextToSpeechManager::INTERRUPT);
|
||||
_selectedMenuItem = i;
|
||||
}
|
||||
|
||||
mouseOnItem = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mouseOnItem) {
|
||||
_selectedMenuItem = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type == Common::EVENT_KEYDOWN) {
|
||||
_wm->setActiveWindow(_consoleWindow->getId());
|
||||
}
|
||||
|
||||
return _wm->processEvent(event);
|
||||
}
|
||||
|
||||
void menuCommandsCallback(int action, Common::String &text, void *data) {
|
||||
Gui *g = (Gui *)data;
|
||||
|
||||
g->executeMenuCommand(action, text);
|
||||
}
|
||||
|
||||
void Gui::executeMenuCommand(int action, Common::String &text) {
|
||||
switch(action) {
|
||||
case kMenuActionAbout:
|
||||
aboutDialog();
|
||||
break;
|
||||
|
||||
case kMenuActionNew:
|
||||
_engine->_restartRequested = true;
|
||||
break;
|
||||
|
||||
case kMenuActionClose:
|
||||
// This is a no-op as we do not let new game to be opened
|
||||
break;
|
||||
|
||||
case kMenuActionRevert:
|
||||
if (_engine->_defaultSaveSlot != -1) {
|
||||
_engine->_isGameOver = false;
|
||||
|
||||
_engine->_world->_weaponMenuDisabled = false;
|
||||
_engine->loadGameState(_engine->_defaultSaveSlot);
|
||||
|
||||
_scene = nullptr; // To force current scene to be redrawn
|
||||
_engine->redrawScene();
|
||||
g_system->updateScreen();
|
||||
}
|
||||
break;
|
||||
|
||||
case kMenuActionOpen:
|
||||
_engine->scummVMSaveLoadDialog(false);
|
||||
break;
|
||||
|
||||
case kMenuActionQuit:
|
||||
_engine->quitGame();
|
||||
break;
|
||||
|
||||
case kMenuActionSave:
|
||||
_engine->saveGame();
|
||||
break;
|
||||
|
||||
case kMenuActionSaveAs:
|
||||
_engine->scummVMSaveLoadDialog(true);
|
||||
break;
|
||||
|
||||
case kMenuActionUndo:
|
||||
actionUndo();
|
||||
break;
|
||||
case kMenuActionCut:
|
||||
actionCut();
|
||||
break;
|
||||
case kMenuActionCopy:
|
||||
actionCopy();
|
||||
break;
|
||||
case kMenuActionPaste:
|
||||
actionPaste();
|
||||
break;
|
||||
case kMenuActionClear:
|
||||
actionClear();
|
||||
break;
|
||||
|
||||
case kMenuActionCommand: {
|
||||
_engine->_inputText = text;
|
||||
Common::String inp = text + '\n';
|
||||
|
||||
_engine->sayText(text, Common::TextToSpeechManager::QUEUE);
|
||||
|
||||
appendText(inp.c_str());
|
||||
|
||||
_consoleWindow->clearInput();
|
||||
|
||||
_engine->processTurn(&text, NULL);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
warning("Unknown action: %d", action);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////
|
||||
// Console stuff
|
||||
//////////////////
|
||||
const Graphics::MacFont *Gui::getConsoleMacFont() {
|
||||
Scene *scene = _engine->_world->_player->_currentScene;
|
||||
|
||||
return scene->getFont();
|
||||
}
|
||||
|
||||
const Graphics::Font *Gui::getConsoleFont() {
|
||||
return _wm->_fontMan->getFont(*getConsoleMacFont());
|
||||
}
|
||||
|
||||
const Graphics::Font *Gui::getTitleFont() {
|
||||
return _wm->_fontMan->getFont(Graphics::MacFont(Graphics::kMacFontSystem, 12));
|
||||
}
|
||||
|
||||
void Gui::appendText(const char *s) {
|
||||
_consoleWindow->appendText(s, getConsoleMacFont());
|
||||
}
|
||||
|
||||
void Gui::clearOutput() {
|
||||
_consoleWindow->clearText();
|
||||
}
|
||||
|
||||
void Gui::actionCopy() {
|
||||
g_system->setTextInClipboard(Common::convertUtf32ToUtf8(_consoleWindow->getSelection()));
|
||||
|
||||
_menu->enableCommand(kMenuEdit, kMenuActionPaste, true);
|
||||
}
|
||||
|
||||
void Gui::actionPaste() {
|
||||
if (g_system->hasTextInClipboard()) {
|
||||
_undobuffer = _engine->_inputText;
|
||||
|
||||
_consoleWindow->appendInput(g_system->getTextFromClipboard());
|
||||
|
||||
_menu->enableCommand(kMenuEdit, kMenuActionUndo, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Gui::actionUndo() {
|
||||
_consoleWindow->clearInput();
|
||||
_consoleWindow->appendInput(_undobuffer);
|
||||
|
||||
_menu->enableCommand(kMenuEdit, kMenuActionUndo, false);
|
||||
}
|
||||
|
||||
void Gui::actionClear() {
|
||||
if (_consoleWindow->getSelectedText()->endY == -1)
|
||||
return;
|
||||
|
||||
Common::String input = Common::convertFromU32String(_consoleWindow->getInput());
|
||||
|
||||
_consoleWindow->cutSelection();
|
||||
|
||||
_undobuffer = input;
|
||||
|
||||
_menu->enableCommand(kMenuEdit, kMenuActionUndo, true);
|
||||
}
|
||||
|
||||
void Gui::actionCut() {
|
||||
if (_consoleWindow->getSelectedText()->endY == -1)
|
||||
return;
|
||||
|
||||
Common::String input = Common::convertFromU32String(_consoleWindow->getInput());
|
||||
|
||||
g_system->setTextInClipboard(_consoleWindow->cutSelection());
|
||||
|
||||
_undobuffer = input;
|
||||
|
||||
_menu->enableCommand(kMenuEdit, kMenuActionUndo, true);
|
||||
_menu->enableCommand(kMenuEdit, kMenuActionPaste, true);
|
||||
}
|
||||
|
||||
void Gui::disableUndo() {
|
||||
_menu->enableCommand(kMenuEdit, kMenuActionUndo, false);
|
||||
}
|
||||
|
||||
void Gui::disableAllMenus() {
|
||||
_menu->disableAllMenus();
|
||||
}
|
||||
|
||||
void Gui::enableNewGameMenus() {
|
||||
_menu->enableCommand(kMenuFile, kMenuActionNew, true);
|
||||
_menu->enableCommand(kMenuFile, kMenuActionOpen, true);
|
||||
_menu->enableCommand(kMenuFile, kMenuActionQuit, true);
|
||||
}
|
||||
|
||||
void Gui::enableSave() {
|
||||
_menu->enableCommand(kMenuFile, kMenuActionSave, true);
|
||||
_menu->enableCommand(kMenuFile, kMenuActionSaveAs, true);
|
||||
}
|
||||
|
||||
void Gui::enableRevert() {
|
||||
_menu->enableCommand(kMenuFile, kMenuActionRevert, true);
|
||||
}
|
||||
|
||||
class AboutDialog : public Graphics::MacDialog {
|
||||
public:
|
||||
AboutDialog(Graphics::ManagedSurface *screen, Graphics::MacWindowManager *wm, int width, Graphics::MacText *mactext, int maxTextWidth, Graphics::MacDialogButtonArray *buttons, uint defaultButton);
|
||||
virtual ~AboutDialog() {
|
||||
if (_volumeChanged)
|
||||
ConfMan.flushToDisk();
|
||||
}
|
||||
|
||||
virtual void paint() override;
|
||||
virtual bool processEvent(const Common::Event &event) override;
|
||||
|
||||
private:
|
||||
Common::Rect _volBbox;
|
||||
|
||||
const int kVolWidth = 160;
|
||||
bool _volumeChanged = false;
|
||||
};
|
||||
|
||||
AboutDialog::AboutDialog(Graphics::ManagedSurface *screen, Graphics::MacWindowManager *wm, int width, Graphics::MacText *mactext, int maxTextWidth, Graphics::MacDialogButtonArray *buttons, uint defaultButton)
|
||||
: Graphics::MacDialog(screen, wm, width, mactext, maxTextWidth, buttons, defaultButton) {
|
||||
_volBbox = Common::Rect(0, 0, kVolWidth, 12);
|
||||
_volBbox.moveTo(_bbox.left + (_bbox.width() - kVolWidth) / 2, _bbox.bottom - 32);
|
||||
}
|
||||
|
||||
|
||||
void AboutDialog::paint() {
|
||||
Graphics::MacDialog::paint();
|
||||
|
||||
const char *volumeText = "- Volume +";
|
||||
int w = _font->getStringWidth(volumeText);
|
||||
int x = _bbox.left + (_bbox.width() - w) / 2;
|
||||
int y = _bbox.bottom - 52;
|
||||
|
||||
_font->drawString(_screen, volumeText, x, y, _bbox.width(), kColorBlack);
|
||||
|
||||
uint32 volume = ConfMan.getInt("sfx_volume");
|
||||
|
||||
Graphics::Primitives &primitives = _wm->getDrawPrimitives();
|
||||
|
||||
Common::Rect volBox(0, 0, volume * kVolWidth / 256, 12);
|
||||
volBox.moveTo(_bbox.left + (_bbox.width() - kVolWidth) / 2, _bbox.bottom - 32);
|
||||
|
||||
Graphics::MacPlotData pd(_screen, nullptr, &_wm->getPatterns(), 1, 0, 0, 1, _wm->_colorBlack, false);
|
||||
primitives.drawFilledRect1(volBox, kColorBlack, &pd);
|
||||
primitives.drawRect1(_volBbox, kColorBlack, &pd);
|
||||
}
|
||||
|
||||
bool AboutDialog::processEvent(const Common::Event &event) {
|
||||
if (event.type == Common::EVENT_LBUTTONUP) {
|
||||
if (_volBbox.contains(event.mouse.x, event.mouse.y)) {
|
||||
int delta = event.mouse.x - _volBbox.left;
|
||||
|
||||
int volume = delta * 256 / kVolWidth;
|
||||
ConfMan.setInt("sfx_volume", volume);
|
||||
_volumeChanged = true;
|
||||
|
||||
_needsRedraw = true;
|
||||
|
||||
g_wage->syncSoundSettings();
|
||||
|
||||
g_wage->_speaker->play(Audio::PCSpeaker::kWaveFormSquare, 500, 150);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Gui::aboutDialog() {
|
||||
Common::U32String messageText(_engine->_world->_aboutMessage, Common::kMacRoman);
|
||||
Common::U32String disclaimer("\n\n\n\nThis adventure was produced with World Builder\xAA\nthe adventure game creation system.\n\xA9 Copyright 1986 by William C. Appleton, All Right Reserved\nPublished by Silicon Beach Software, Inc.", Common::kMacRoman);
|
||||
|
||||
_engine->sayText(_engine->_world->_aboutMessage);
|
||||
_engine->sayText(disclaimer, Common::TextToSpeechManager::QUEUE);
|
||||
messageText += disclaimer;
|
||||
|
||||
Graphics::MacFont font(Graphics::kMacFontGeneva, 9, 0);
|
||||
Graphics::MacText aboutMessage(messageText, _wm, &font, Graphics::kColorBlack,
|
||||
Graphics::kColorWhite, 400, Graphics::kTextAlignCenter);
|
||||
|
||||
Graphics::MacDialogButtonArray buttons;
|
||||
|
||||
buttons.push_back(new Graphics::MacDialogButton("OK", 191, aboutMessage.getTextHeight() + 30, 68, 28));
|
||||
// add a dummy button to push volume slider position down
|
||||
// to avoid the overlapping of volume slider with OK button in the about section
|
||||
buttons.push_back(new Graphics::MacDialogButton("", 0, aboutMessage.getTextHeight() + 100, 0, 0));
|
||||
|
||||
AboutDialog about(&_screen, _wm, 450, &aboutMessage, 400, &buttons, 0);
|
||||
|
||||
delete buttons.back();
|
||||
buttons.pop_back();
|
||||
// close the menu before calling run because it blocks execution
|
||||
if (_menu)
|
||||
_menu->closeMenu();
|
||||
|
||||
int button = about.run();
|
||||
|
||||
if (button == Graphics::kMacDialogQuitRequested)
|
||||
_engine->_shouldQuit = true;
|
||||
}
|
||||
|
||||
void Gui::gameOver() {
|
||||
Graphics::MacDialogButtonArray buttons;
|
||||
|
||||
buttons.push_back(new Graphics::MacDialogButton("OK", 66, 67, 68, 28));
|
||||
|
||||
Graphics::MacFont font;
|
||||
|
||||
Graphics::MacText gameOverMessage(*_engine->_world->_gameOverMessage, _wm, &font, Graphics::kColorBlack,
|
||||
Graphics::kColorWhite, 199, Graphics::kTextAlignCenter);
|
||||
|
||||
_engine->sayText(*_engine->_world->_gameOverMessage, Common::TextToSpeechManager::QUEUE);
|
||||
|
||||
Graphics::MacDialog gameOverDialog(&_screen, _wm, 199, &gameOverMessage, 199, &buttons, 0);
|
||||
|
||||
int button = gameOverDialog.run();
|
||||
|
||||
if (button == Graphics::kMacDialogQuitRequested)
|
||||
_engine->_shouldQuit = true;
|
||||
|
||||
_engine->doClose();
|
||||
|
||||
disableAllMenus();
|
||||
enableNewGameMenus();
|
||||
}
|
||||
|
||||
bool Gui::saveDialog() {
|
||||
Graphics::MacDialogButtonArray buttons;
|
||||
|
||||
buttons.push_back(new Graphics::MacDialogButton("No", 19, 67, 68, 28));
|
||||
buttons.push_back(new Graphics::MacDialogButton("Yes", 112, 67, 68, 28));
|
||||
buttons.push_back(new Graphics::MacDialogButton("Cancel", 205, 67, 68, 28));
|
||||
|
||||
Graphics::MacFont font;
|
||||
|
||||
Graphics::MacText saveBeforeCloseMessage(*_engine->_world->_saveBeforeCloseMessage, _wm, &font, Graphics::kColorBlack,
|
||||
Graphics::kColorWhite, 250, Graphics::kTextAlignCenter);
|
||||
|
||||
_engine->sayText(*_engine->_world->_saveBeforeCloseMessage);
|
||||
|
||||
Graphics::MacDialog save(&_screen, _wm, 291, &saveBeforeCloseMessage, 250, &buttons, 1);
|
||||
|
||||
int button = save.run();
|
||||
|
||||
if (button == Graphics::kMacDialogQuitRequested)
|
||||
_engine->_shouldQuit = true;
|
||||
else if (button == 2) // Cancel
|
||||
return false;
|
||||
else if (button == 1)
|
||||
_engine->saveGame();
|
||||
|
||||
_engine->doClose();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Wage
|
||||
185
engines/wage/gui.h
Normal file
185
engines/wage/gui.h
Normal file
@@ -0,0 +1,185 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WAGE_GUI_H
|
||||
#define WAGE_GUI_H
|
||||
|
||||
#include "common/str-array.h"
|
||||
#include "graphics/font.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
#include "graphics/macgui/macwindowmanager.h"
|
||||
#include "graphics/macgui/mactextwindow.h"
|
||||
#include "graphics/macgui/macwindow.h"
|
||||
#include "graphics/macgui/mactext.h"
|
||||
#include "graphics/macgui/macmenu.h"
|
||||
#include "graphics/macgui/macwindowborder.h"
|
||||
|
||||
#include "common/events.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "image/bmp.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
using namespace Graphics::MacWindowConstants;
|
||||
|
||||
class Scene;
|
||||
class WageEngine;
|
||||
|
||||
enum {
|
||||
kCursorHeight = 12
|
||||
};
|
||||
|
||||
enum {
|
||||
kFontStyleBold = 1,
|
||||
kFontStyleItalic = 2,
|
||||
kFontStyleUnderline = 4,
|
||||
kFontStyleOutline = 8,
|
||||
kFontStyleShadow = 16,
|
||||
kFontStyleCondensed = 32,
|
||||
kFontStyleExtended = 64
|
||||
};
|
||||
|
||||
enum {
|
||||
kMenuHighLevel = -1,
|
||||
kMenuAbout = 0,
|
||||
kMenuFile = 1,
|
||||
kMenuEdit = 2,
|
||||
kMenuCommands = 3,
|
||||
kMenuWeapons = 4
|
||||
};
|
||||
|
||||
enum {
|
||||
kMenuActionAbout,
|
||||
kMenuActionNew,
|
||||
kMenuActionOpen,
|
||||
kMenuActionClose,
|
||||
kMenuActionSave,
|
||||
kMenuActionSaveAs,
|
||||
kMenuActionRevert,
|
||||
kMenuActionQuit,
|
||||
|
||||
kMenuActionUndo,
|
||||
kMenuActionCut,
|
||||
kMenuActionCopy,
|
||||
kMenuActionPaste,
|
||||
kMenuActionClear,
|
||||
|
||||
kMenuActionCommand
|
||||
};
|
||||
|
||||
class Gui {
|
||||
public:
|
||||
Gui(WageEngine *engine);
|
||||
~Gui();
|
||||
|
||||
void draw();
|
||||
void appendText(const char *str);
|
||||
bool processEvent(Common::Event &event);
|
||||
|
||||
void setSceneDirty() { _sceneDirty = true; }
|
||||
void regenCommandsMenu();
|
||||
void regenWeaponsMenu();
|
||||
|
||||
void actionCopy();
|
||||
void actionPaste();
|
||||
void actionUndo();
|
||||
void actionClear();
|
||||
void actionCut();
|
||||
void disableUndo();
|
||||
void disableAllMenus();
|
||||
void enableNewGameMenus();
|
||||
void enableSave();
|
||||
void enableRevert();
|
||||
|
||||
bool processSceneEvents(WindowClick click, Common::Event &event);
|
||||
bool processConsoleEvents(WindowClick click, Common::Event &event);
|
||||
void executeMenuCommand(int action, Common::String &text);
|
||||
|
||||
void clearOutput();
|
||||
|
||||
void gameOver();
|
||||
bool saveDialog();
|
||||
void aboutDialog();
|
||||
|
||||
private:
|
||||
void drawScene();
|
||||
const Graphics::MacFont *getConsoleMacFont();
|
||||
const Graphics::Font *getConsoleFont();
|
||||
const Graphics::Font *getTitleFont();
|
||||
|
||||
void loadBorders();
|
||||
void loadBorder(Graphics::MacWindow *target, const char *border[], uint height, uint32 flags, int titlePos = 0);
|
||||
|
||||
public:
|
||||
Graphics::ManagedSurface _screen;
|
||||
|
||||
WageEngine *_engine;
|
||||
|
||||
Scene *_scene;
|
||||
|
||||
Graphics::MacWindowManager *_wm;
|
||||
Graphics::MacWindow *_sceneWindow;
|
||||
Graphics::MacTextWindow *_consoleWindow;
|
||||
|
||||
private:
|
||||
Graphics::ManagedSurface _console;
|
||||
Graphics::MacMenu *_menu;
|
||||
bool _sceneDirty;
|
||||
|
||||
Common::String _undobuffer;
|
||||
|
||||
int _commandsMenuId;
|
||||
int _weaponsMenuId;
|
||||
|
||||
int _selectedMenuItem;
|
||||
};
|
||||
|
||||
} // End of namespace Wage
|
||||
|
||||
#endif
|
||||
304
engines/wage/guiborders.cpp
Normal file
304
engines/wage/guiborders.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License..
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions..
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "wage/wage.h"
|
||||
#include "wage/gui.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
// Original files sit in engines/wage/guiborders
|
||||
// to print out similar pictures, use
|
||||
// bmpDecoder.getSurface()->debugPrint(0, 0, 0, 0, 0, 1);
|
||||
// in MacWindowBorder::loadBorder()
|
||||
//
|
||||
// TODO: Store these as XPM files?
|
||||
|
||||
static const char *wage_border_inact_title[] = {
|
||||
"...................................... .. .. ......................................",
|
||||
".. .. .. ..",
|
||||
".. ################################ .. ## .. ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ .. ## .. ################################ ",
|
||||
".. .. .. ",
|
||||
" .... ######################## .................. ######################## .... ",
|
||||
".. .......... ",
|
||||
".. ################################ .......... ################################ ",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ .......... ################################ ..",
|
||||
".. .......... ..",
|
||||
".................................. .................................."};
|
||||
|
||||
static const char *wage_border_act_noscrollbar_title[] = {
|
||||
"...................................... .. .. ......................................",
|
||||
".. .. .. ..",
|
||||
".. ################################ .. ## .. ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ######## ######## ## ################################ ..",
|
||||
".. ######## ############ ######## ## ################################ ..",
|
||||
".. ######## ############ ######## ## ################################ ..",
|
||||
".. ######## ############ ######## ## ################################ ..",
|
||||
".. ######## ############ ######## ## ################################ ..",
|
||||
".. ######## ############ ######## ## ################################ ..",
|
||||
".. ######## ############ ######## ## ################################ ..",
|
||||
".. ######## ######## ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ## ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ .. ## .. ################################ ",
|
||||
".. .. .. ",
|
||||
" .... #### #### .................. #### #### .... ",
|
||||
".. .......... ",
|
||||
".. ################################ .......... ################################ ",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ########## ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ .......... ################################ ..",
|
||||
".. .......... ..",
|
||||
".................................. .................................."};
|
||||
|
||||
static const char *wage_border_inact[] = {
|
||||
"...................................... ......................................",
|
||||
".. .. ..",
|
||||
".. ################################ .. ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ .. ################################ ",
|
||||
".. .. ",
|
||||
" .... ######################## .......... ######################## .... ",
|
||||
".. .. ",
|
||||
".. ################################ .. ################################ ",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ .. ################################ ..",
|
||||
".. .. ..",
|
||||
".................................. .................................."};
|
||||
|
||||
static const char *wage_border_act[] = {
|
||||
"...................................... ......................................",
|
||||
".. .. ..",
|
||||
".. ################################ .. ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ######## ######## ################################ ..",
|
||||
".. ######## ############ ######## ################################ ..",
|
||||
".. ######## ############ ######## ################################ ..",
|
||||
".. ######## ############ ######## ################################ ..",
|
||||
".. ######## ############ ######## ################################ ..",
|
||||
".. ######## ############ ######## ################################ ..",
|
||||
".. ######## ############ ######## ################################ ..",
|
||||
".. ######## ######## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ .. ################################ ",
|
||||
".. .. ",
|
||||
"...... #### #### .......... ########## ########## .... ",
|
||||
"...... #### #### .......... ######## ######## .... ",
|
||||
"...... #### #### .......... ###### ###### .... ",
|
||||
"...... #### #### .......... #### #### .... ",
|
||||
"...... #### #### .......... ## ## .... ",
|
||||
"...... #### #### .......... .... ",
|
||||
" .... #### #### .......... #### #### .... ",
|
||||
"...... #### #### .......... .... ",
|
||||
"...... #### #### .......... ## ## .... ",
|
||||
"...... #### #### .......... #### #### .... ",
|
||||
"...... #### #### .......... ###### ###### .... ",
|
||||
"...... #### #### .......... ######## ######## .... ",
|
||||
"...... #### #### .......... ########## ########## .... ",
|
||||
".. .. ",
|
||||
".. ################################ .. ################################ ",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ## ################################ ..",
|
||||
".. ################################ ################################ ..",
|
||||
".. ################################ .. ################################ ..",
|
||||
".. .. ..",
|
||||
".................................. .................................."};
|
||||
|
||||
static const byte wage_border_palette[] = {
|
||||
0, 0, 0,
|
||||
255, 255, 255,
|
||||
255, 0, 255
|
||||
};
|
||||
|
||||
void Gui::loadBorders() {
|
||||
_consoleWindow->enableScrollbar(true);
|
||||
loadBorder(_sceneWindow, wage_border_inact_title, ARRAYSIZE(wage_border_inact_title), Graphics::kWindowBorderTitle, 22);
|
||||
loadBorder(_sceneWindow, wage_border_act_noscrollbar_title, ARRAYSIZE(wage_border_act_noscrollbar_title), Graphics::kWindowBorderActive|Graphics::kWindowBorderTitle, 22);
|
||||
loadBorder(_consoleWindow, wage_border_inact, ARRAYSIZE(wage_border_inact), Graphics::kWindowBorderScrollbar, 0);
|
||||
loadBorder(_consoleWindow, wage_border_act, ARRAYSIZE(wage_border_act), Graphics::kWindowBorderScrollbar|Graphics::kWindowBorderActive, 0);
|
||||
}
|
||||
|
||||
void Gui::loadBorder(Graphics::MacWindow *target, const char *border[], uint height, uint32 flags, int titlePos) {
|
||||
uint width = strlen(border[0]) / 2;
|
||||
|
||||
Graphics::ManagedSurface *surface = new Graphics::ManagedSurface();
|
||||
surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
surface->setPalette(wage_border_palette, 0, 3);
|
||||
surface->setTransparentColor(2);
|
||||
|
||||
for (uint y = 0; y < height; y++) {
|
||||
byte *dst = (byte *)surface->getBasePtr(0, y);
|
||||
|
||||
for (uint x = 0; x < width; x++) {
|
||||
switch(border[y][x * 2]) {
|
||||
case ' ':
|
||||
*dst = 0;
|
||||
break;
|
||||
|
||||
case '#':
|
||||
*dst = 1;
|
||||
break;
|
||||
|
||||
case '.':
|
||||
*dst = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
error("Incorrect symbol in bitmap '%c'(%02x) at %d,%d", border[y][x * 2], border[y][x * 2], x, y);
|
||||
}
|
||||
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
Graphics::BorderOffsets offsets;
|
||||
offsets.left = 16;
|
||||
offsets.right = 16;
|
||||
offsets.top = 16;
|
||||
offsets.bottom = 16;
|
||||
offsets.titleTop = 0;
|
||||
offsets.titleBottom = 0;
|
||||
offsets.dark = false;
|
||||
offsets.closeButtonTop = -1;
|
||||
offsets.closeButtonLeft = -1;
|
||||
offsets.closeButtonWidth = 0;
|
||||
offsets.resizeButtonTop = -1;
|
||||
offsets.resizeButtonHeight = 0;
|
||||
offsets.upperScrollHeight = 16;
|
||||
offsets.lowerScrollHeight = 16;
|
||||
offsets.titlePos = titlePos;
|
||||
offsets.titlePadding = 8;
|
||||
target->setBorder(surface, flags, offsets);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // End of namespace Wage
|
||||
BIN
engines/wage/guiborders/wage_border_act-noscrollbar-title.bmp
Normal file
BIN
engines/wage/guiborders/wage_border_act-noscrollbar-title.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
BIN
engines/wage/guiborders/wage_border_act-noscrollbar.bmp
Normal file
BIN
engines/wage/guiborders/wage_border_act-noscrollbar.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
BIN
engines/wage/guiborders/wage_border_act-title.bmp
Normal file
BIN
engines/wage/guiborders/wage_border_act-title.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.6 KiB |
BIN
engines/wage/guiborders/wage_border_act.bmp
Normal file
BIN
engines/wage/guiborders/wage_border_act.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
BIN
engines/wage/guiborders/wage_border_inact-title.bmp
Normal file
BIN
engines/wage/guiborders/wage_border_inact-title.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
BIN
engines/wage/guiborders/wage_border_inact.bmp
Normal file
BIN
engines/wage/guiborders/wage_border_inact.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
119
engines/wage/metaengine.cpp
Normal file
119
engines/wage/metaengine.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/* 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 "base/plugins.h"
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "wage/wage.h"
|
||||
#include "wage/detection.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
uint32 WageEngine::getFeatures() {
|
||||
return _gameDescription->flags;
|
||||
}
|
||||
|
||||
const char *WageEngine::getGameFile() const {
|
||||
return _gameDescription->filesDescriptions[0].fileName;
|
||||
}
|
||||
|
||||
} // End of namespace Wage
|
||||
|
||||
#ifdef USE_TTS
|
||||
|
||||
static const ADExtraGuiOptionsMap optionsList[] = {
|
||||
{
|
||||
GAMEOPTION_TTS,
|
||||
{
|
||||
_s("Enable Text to Speech"),
|
||||
_s("Use TTS to read the descriptions (if TTS is available)"),
|
||||
"tts_enabled",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
AD_EXTRA_GUI_OPTIONS_TERMINATOR
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class WageMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
|
||||
public:
|
||||
const char *getName() const override {
|
||||
return "wage";
|
||||
}
|
||||
|
||||
#ifdef USE_TTS
|
||||
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
|
||||
return optionsList;
|
||||
}
|
||||
#endif
|
||||
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
|
||||
|
||||
bool hasFeature(MetaEngineFeature f) const override;
|
||||
int getMaximumSaveSlot() const override;
|
||||
};
|
||||
|
||||
bool WageMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||
return checkExtendedSaves(f) ||
|
||||
(f == kSupportsLoadingDuringStartup);
|
||||
}
|
||||
|
||||
bool Wage::WageEngine::hasFeature(EngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsReturnToLauncher) ||
|
||||
(f == kSupportsLoadingDuringRuntime) ||
|
||||
(f == kSupportsSavingDuringRuntime) ||
|
||||
(f == kSupportsQuitDialogOverride);
|
||||
}
|
||||
|
||||
Common::Error WageMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
|
||||
*engine = new Wage::WageEngine(syst, desc);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
int WageMetaEngine::getMaximumSaveSlot() const { return 999; }
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(WAGE)
|
||||
REGISTER_PLUGIN_DYNAMIC(WAGE, PLUGIN_TYPE_ENGINE, WageMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(WAGE, PLUGIN_TYPE_ENGINE, WageMetaEngine);
|
||||
#endif
|
||||
|
||||
namespace Wage {
|
||||
|
||||
bool WageEngine::canLoadGameStateCurrently(Common::U32String *msg) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WageEngine::canSaveGameStateCurrently(Common::U32String *msg) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Wage
|
||||
36
engines/wage/module.mk
Normal file
36
engines/wage/module.mk
Normal file
@@ -0,0 +1,36 @@
|
||||
MODULE := engines/wage
|
||||
|
||||
MODULE_OBJS := \
|
||||
combat.o \
|
||||
debugger.o \
|
||||
design.o \
|
||||
entities.o \
|
||||
gui.o \
|
||||
guiborders.o \
|
||||
metaengine.o \
|
||||
randomhat.o \
|
||||
saveload.o \
|
||||
script.o \
|
||||
sound.o \
|
||||
util.o \
|
||||
wage.o \
|
||||
world.o
|
||||
|
||||
ifdef USE_IMGUI
|
||||
MODULE_OBJS += \
|
||||
debugtools.o
|
||||
endif
|
||||
|
||||
MODULE_DIRS += \
|
||||
engines/wage
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_WAGE), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
# Detection objects
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
82
engines/wage/randomhat.cpp
Normal file
82
engines/wage/randomhat.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/random.h"
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "wage/randomhat.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
void RandomHat::addTokens(int type, int count) {
|
||||
_tokens.setVal(type, _tokens.getValOrDefault(type, 0) + count);
|
||||
}
|
||||
|
||||
int RandomHat::countTokens() {
|
||||
int count = 0;
|
||||
for (Common::HashMap<int, int>::const_iterator it = _tokens.begin(); it != _tokens.end(); ++it)
|
||||
count += it->_value;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int RandomHat::drawToken() {
|
||||
int total = countTokens();
|
||||
if (total > 0) {
|
||||
int random = _rnd->getRandomNumber(total - 1);
|
||||
int count = 0;
|
||||
for (Common::HashMap<int, int>::iterator it = _tokens.begin(); it != _tokens.end(); ++it) {
|
||||
if (random >= count && random < count + it->_value) {
|
||||
it->_value--;
|
||||
return it->_key;
|
||||
}
|
||||
count += it->_value;
|
||||
}
|
||||
}
|
||||
return kTokNone;
|
||||
}
|
||||
|
||||
} // End of namespace Wage
|
||||
76
engines/wage/randomhat.h
Normal file
76
engines/wage/randomhat.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WAGE_RANDOMHAT_H
|
||||
#define WAGE_RANDOMHAT_H
|
||||
|
||||
namespace Wage {
|
||||
|
||||
enum {
|
||||
kTokWeapons = -400,
|
||||
kTokMagic = -300,
|
||||
kTokRun = -200,
|
||||
kTokOffer = -100,
|
||||
kTokNone = -100000
|
||||
};
|
||||
|
||||
class RandomHat {
|
||||
public:
|
||||
RandomHat(Common::RandomSource *rnd) : _rnd(rnd) {}
|
||||
|
||||
void addTokens(int type, int count);
|
||||
int drawToken();
|
||||
|
||||
private:
|
||||
Common::RandomSource *_rnd;
|
||||
Common::HashMap<int, int> _tokens;
|
||||
|
||||
int countTokens();
|
||||
};
|
||||
|
||||
} // End of namespace Wage
|
||||
|
||||
#endif
|
||||
781
engines/wage/saveload.cpp
Normal file
781
engines/wage/saveload.cpp
Normal file
@@ -0,0 +1,781 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/debug-channels.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "gui/saveload.h"
|
||||
|
||||
#include "graphics/thumbnail.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "wage/wage.h"
|
||||
#include "wage/world.h"
|
||||
#include "wage/entities.h"
|
||||
#include "wage/gui.h"
|
||||
|
||||
#define SAVEGAME_CURRENT_VERSION 1
|
||||
|
||||
//
|
||||
// Original saves format is supported.
|
||||
// ScummVM adds flags, description and thumbnail
|
||||
// in the end of the file (shouldn't make saves incompatible).
|
||||
//
|
||||
// Version 0 (original/ScummVM): first ScummVM version
|
||||
//
|
||||
|
||||
namespace Wage {
|
||||
|
||||
//TODO: make sure these are calculated right: (we add flag, description, etc)
|
||||
#define VARS_INDEX 0x005E
|
||||
#define SCENES_INDEX 0x0232
|
||||
|
||||
#define SCENE_SIZE 0x0010
|
||||
#define CHR_SIZE 0x0016
|
||||
#define OBJ_SIZE 0x0010
|
||||
|
||||
#define GET_HEX_OFFSET(ptr, baseOffset, entrySize) ((ptr) == nullptr ? -1 : ((baseOffset) + (entrySize) * (ptr)->_index))
|
||||
#define GET_HEX_CHR_OFFSET(ptr) GET_HEX_OFFSET((ptr), chrsHexOffset, CHR_SIZE)
|
||||
#define GET_HEX_OBJ_OFFSET(ptr) GET_HEX_OFFSET((ptr), objsHexOffset, OBJ_SIZE)
|
||||
#define GET_HEX_SCENE_OFFSET(ptr) ((ptr) == nullptr ? -1 : \
|
||||
((ptr) == _world->_storageScene ? 0 : (SCENES_INDEX + getSceneIndex(_world->_player->_currentScene) * SCENE_SIZE)))
|
||||
|
||||
int WageEngine::getSceneIndex(Scene *scene) const {
|
||||
assert(scene);
|
||||
Common::Array<Scene *> &orderedScenes = _world->_orderedScenes;
|
||||
for (int32 i = 0; i < (int)orderedScenes.size(); ++i) {
|
||||
if (orderedScenes[i] == scene)
|
||||
return i - 1;
|
||||
}
|
||||
|
||||
warning("Scene's index not found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Obj *WageEngine::getObjByOffset(int offset, int objBaseOffset) const {
|
||||
int objIndex = -1;
|
||||
|
||||
if (offset != 0xFFFF) {
|
||||
objIndex = (offset - objBaseOffset) / CHR_SIZE;
|
||||
}
|
||||
|
||||
if (objIndex >= 0 && (uint)objIndex < _world->_orderedObjs.size()) {
|
||||
return _world->_orderedObjs[objIndex];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Chr *WageEngine::getChrById(int resId) const {
|
||||
Common::Array<Chr *> &orderedChrs = _world->_orderedChrs;
|
||||
for (uint32 i = 0; i < orderedChrs.size(); ++i) {
|
||||
if (orderedChrs[i]->_resourceId == resId)
|
||||
return orderedChrs[i];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Chr *WageEngine::getChrByOffset(int offset, int chrBaseOffset) const {
|
||||
int chrIndex = -1;
|
||||
|
||||
if (offset != 0xFFFF) {
|
||||
chrIndex = (offset - chrBaseOffset) / CHR_SIZE;
|
||||
}
|
||||
|
||||
if (chrIndex >= 0 && (uint)chrIndex < _world->_orderedChrs.size()) {
|
||||
return _world->_orderedChrs[chrIndex];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Scene *WageEngine::getSceneById(int resId) const {
|
||||
Common::Array<Scene *> &orderedScenes = _world->_orderedScenes;
|
||||
for (uint32 i = 0; i < orderedScenes.size(); ++i) {
|
||||
if (orderedScenes[i]->_resourceId == resId)
|
||||
return orderedScenes[i];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Scene *WageEngine::getSceneByOffset(int offset) const {
|
||||
int sceneIndex = -1;
|
||||
|
||||
if (offset != 0xFFFF) {
|
||||
if (offset == 0)
|
||||
sceneIndex = 0;
|
||||
else
|
||||
sceneIndex = 1 + (offset - SCENES_INDEX) / SCENE_SIZE;
|
||||
}
|
||||
|
||||
if (sceneIndex >= 0 && (uint)sceneIndex < _world->_orderedScenes.size()) {
|
||||
if (sceneIndex == 0) return _world->_storageScene;
|
||||
return _world->_orderedScenes[sceneIndex];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int WageEngine::saveGame(const Common::String &fileName, const Common::String &descriptionString) {
|
||||
Common::OutSaveFile *out;
|
||||
int result = 0;
|
||||
|
||||
debug(9, "WageEngine::saveGame(%s, %s)", fileName.c_str(), descriptionString.c_str());
|
||||
if (!(out = _saveFileMan->openForSaving(fileName))) {
|
||||
warning("Can't create file '%s', game not saved", fileName.c_str());
|
||||
return -1;
|
||||
} else {
|
||||
debug(9, "Successfully opened %s for writing", fileName.c_str());
|
||||
}
|
||||
|
||||
// Counters
|
||||
out->writeSint16LE(_world->_scenes.size()); //numScenes
|
||||
out->writeSint16LE(_world->_chrs.size()); //numChars
|
||||
out->writeSint16LE(_world->_objs.size()); //numObjs
|
||||
|
||||
// Hex Offsets
|
||||
int chrsHexOffset = SCENES_INDEX + _world->_scenes.size() * SCENE_SIZE; //chrs start after scenes
|
||||
int objsHexOffset = chrsHexOffset + _world->_chrs.size() * CHR_SIZE; //objs start after chrs
|
||||
out->writeSint32LE(chrsHexOffset);
|
||||
out->writeSint32LE(objsHexOffset);
|
||||
|
||||
// Unique 8-byte World Signature
|
||||
out->writeSint32LE(_world->_signature); //8-byte ints? seriously? (uses 4 bytes in java code too)
|
||||
|
||||
Chr *player = _world->_player;
|
||||
Context &playerContext = player->_context;
|
||||
|
||||
// More Counters
|
||||
out->writeSint32LE(playerContext._visits); //visitNum
|
||||
out->writeSint32LE(_loopCount); //loopNum
|
||||
out->writeSint32LE(playerContext._kills); //killNum
|
||||
|
||||
// Hex offset to player character
|
||||
out->writeSint32LE(GET_HEX_CHR_OFFSET(player)); //getPlayerHexOffset() == getHexOffsetForChr(player)
|
||||
|
||||
// character in this scene?
|
||||
out->writeSint32LE(GET_HEX_CHR_OFFSET(_monster)); //getPresCharHexOffset() == getHexOffsetForChr(monster)
|
||||
|
||||
// Hex offset to current scene
|
||||
out->writeSint32LE(GET_HEX_SCENE_OFFSET(player->_currentScene)); //getCurSceneHexOffset()
|
||||
|
||||
// wearing a helmet?
|
||||
out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::HEAD_ARMOR])); //helmetIndex
|
||||
|
||||
// holding a shield?
|
||||
out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::SHIELD_ARMOR])); //shieldIndex
|
||||
|
||||
// wearing chest armor?
|
||||
out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::BODY_ARMOR])); //chestArmIndex
|
||||
|
||||
// wearing spiritual armor?
|
||||
out->writeSint32LE(GET_HEX_OBJ_OFFSET(player->_armor[Chr::MAGIC_ARMOR])); //sprtArmIndex
|
||||
|
||||
// TODO:
|
||||
out->writeUint16LE(0xffff); // ???? - always FFFF
|
||||
out->writeUint16LE(0xffff); // ???? - always FFFF
|
||||
out->writeUint16LE(0xffff); // ???? - always FFFF
|
||||
out->writeUint16LE(0xffff); // ???? - always FFFF
|
||||
|
||||
// did a character just escape?
|
||||
out->writeSint32LE(GET_HEX_CHR_OFFSET(_running)); //getRunCharHexOffset() == getHexOffsetForChr(running)
|
||||
|
||||
// players experience points
|
||||
out->writeSint32LE(playerContext._experience);
|
||||
|
||||
out->writeSint16LE(_aim); //aim
|
||||
out->writeSint16LE(_opponentAim); //opponentAim
|
||||
|
||||
// TODO:
|
||||
out->writeSint16LE(0x0000); // always 0
|
||||
out->writeSint16LE(0x0000); // always 0
|
||||
out->writeSint16LE(0x0000); // always 0
|
||||
|
||||
// Base character stats
|
||||
out->writeByte(playerContext._statVariables[PHYS_STR_BAS]); //state.getBasePhysStr()
|
||||
out->writeByte(playerContext._statVariables[PHYS_HIT_BAS]); //state.getBasePhysHp()
|
||||
out->writeByte(playerContext._statVariables[PHYS_ARM_BAS]); //state.getBasePhysArm()
|
||||
out->writeByte(playerContext._statVariables[PHYS_ACC_BAS]); //state.getBasePhysAcc()
|
||||
out->writeByte(playerContext._statVariables[SPIR_STR_BAS]); //state.getBaseSprtStr()
|
||||
out->writeByte(playerContext._statVariables[SPIR_HIT_BAS]); //state.getBaseSprtHp()
|
||||
out->writeByte(playerContext._statVariables[SPIR_ARM_BAS]); //state.getBaseSprtArm()
|
||||
out->writeByte(playerContext._statVariables[SPIR_ACC_BAS]); //state.getBaseSprtAcc()
|
||||
out->writeByte(playerContext._statVariables[PHYS_SPE_BAS]); //state.getBaseRunSpeed()
|
||||
|
||||
// TODO:
|
||||
out->writeByte(0x0A); // ???? - always seems to be 0x0A
|
||||
|
||||
// write user vars
|
||||
for (uint32 i = 0; i < 26 * 9; ++i)
|
||||
out->writeSint16LE(playerContext._userVariables[i]);
|
||||
|
||||
// write updated info for all scenes
|
||||
Common::Array<Scene *> &orderedScenes = _world->_orderedScenes;
|
||||
for (uint32 i = 0; i < orderedScenes.size(); ++i) {
|
||||
Scene *scene = orderedScenes[i];
|
||||
if (scene != _world->_storageScene) {
|
||||
out->writeSint16LE(scene->_resourceId);
|
||||
out->writeSint16LE(scene->_worldY);
|
||||
out->writeSint16LE(scene->_worldX);
|
||||
out->writeByte(scene->_blocked[NORTH] ? 0x01 : 0x00);
|
||||
out->writeByte(scene->_blocked[SOUTH] ? 0x01 : 0x00);
|
||||
out->writeByte(scene->_blocked[EAST] ? 0x01 : 0x00);
|
||||
out->writeByte(scene->_blocked[WEST] ? 0x01 : 0x00);
|
||||
out->writeSint16LE(scene->_soundFrequency);
|
||||
out->writeByte(scene->_soundType);
|
||||
// the following two bytes are currently unknown
|
||||
out->writeByte(0);
|
||||
out->writeByte(0);
|
||||
out->writeByte(scene->_visited ? 0x01 : 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
// write updated info for all characters
|
||||
Common::Array<Chr *> &orderedChrs = _world->_orderedChrs;
|
||||
for (uint32 i = 0; i < orderedChrs.size(); ++i) {
|
||||
Chr *chr = orderedChrs[i];
|
||||
out->writeSint16LE(chr->_resourceId);
|
||||
out->writeSint16LE(chr->_currentScene->_resourceId);
|
||||
Context &chrContext = chr->_context;
|
||||
out->writeByte(chrContext._statVariables[PHYS_STR_CUR]);
|
||||
out->writeByte(chrContext._statVariables[PHYS_HIT_CUR]);
|
||||
out->writeByte(chrContext._statVariables[PHYS_ARM_CUR]);
|
||||
out->writeByte(chrContext._statVariables[PHYS_ACC_CUR]);
|
||||
out->writeByte(chrContext._statVariables[SPIR_STR_CUR]);
|
||||
out->writeByte(chrContext._statVariables[SPIR_HIT_CUR]);
|
||||
out->writeByte(chrContext._statVariables[SPIR_ARM_CUR]);
|
||||
out->writeByte(chrContext._statVariables[SPIR_ACC_CUR]);
|
||||
out->writeByte(chrContext._statVariables[PHYS_SPE_CUR]);
|
||||
out->writeByte(chr->_rejectsOffers);
|
||||
out->writeByte(chr->_followsOpponent);
|
||||
// bytes 16-20 are unknown
|
||||
out->writeByte(0);
|
||||
out->writeByte(0);
|
||||
out->writeByte(0);
|
||||
out->writeByte(0);
|
||||
out->writeByte(0);
|
||||
out->writeByte(chr->_weaponDamage1);
|
||||
out->writeByte(chr->_weaponDamage2);
|
||||
}
|
||||
|
||||
// write updated info for all objects
|
||||
Common::Array<Obj *> &orderedObjs = _world->_orderedObjs;
|
||||
for (uint32 i = 0; i < orderedObjs.size(); ++i) {
|
||||
Obj *obj = orderedObjs[i];
|
||||
Scene *location = obj->_currentScene;
|
||||
Chr *owner = obj->_currentOwner;
|
||||
|
||||
out->writeSint16LE(obj->_resourceId);
|
||||
out->writeSint16LE(location == nullptr ? 0 : location->_resourceId);
|
||||
out->writeSint16LE(owner == nullptr ? 0 : owner->_resourceId);
|
||||
|
||||
// bytes 7-9 are unknown (always = 0)
|
||||
out->writeByte(0);
|
||||
out->writeByte(0);
|
||||
out->writeByte(0);
|
||||
|
||||
out->writeByte(obj->_accuracy);
|
||||
out->writeByte(obj->_value);
|
||||
out->writeByte(obj->_type);
|
||||
out->writeByte(obj->_damage);
|
||||
out->writeByte(obj->_attackType);
|
||||
out->writeSint16LE(obj->_numberOfUses);
|
||||
}
|
||||
|
||||
// the following is appended by ScummVM
|
||||
g_engine->getMetaEngine()->appendExtendedSave(out, g_engine->getTotalPlayTime(), descriptionString, fileName.contains("auto"));
|
||||
|
||||
out->finalize();
|
||||
if (out->err()) {
|
||||
warning("Can't write file '%s'. (Disk full?)", fileName.c_str());
|
||||
result = -1;
|
||||
} else
|
||||
debug(9, "Saved game %s in file %s", descriptionString.c_str(), fileName.c_str());
|
||||
|
||||
delete out;
|
||||
return result;
|
||||
}
|
||||
|
||||
int WageEngine::loadGame(int slotId) {
|
||||
Common::InSaveFile *data;
|
||||
Common::String fileName = getSaveStateName(slotId);
|
||||
|
||||
debug(9, "WageEngine::loadGame(%d)", slotId);
|
||||
if (!(data = _saveFileMan->openForLoading(fileName))) {
|
||||
warning("Can't open file '%s', game not loaded", fileName.c_str());
|
||||
return -1;
|
||||
} else {
|
||||
debug(9, "Successfully opened %s for reading", fileName.c_str());
|
||||
}
|
||||
|
||||
// Counters
|
||||
int numScenes = data->readSint16LE();
|
||||
int numChars = data->readSint16LE();
|
||||
int numObjs = data->readSint16LE();
|
||||
|
||||
// Hex Offsets
|
||||
int chrsHexOffset = data->readSint32LE();
|
||||
int objsHexOffset = data->readSint32LE();
|
||||
|
||||
// Unique 8-byte World Signature
|
||||
int signature = data->readSint32LE();
|
||||
if (_world->_signature != signature) {
|
||||
warning("This saved game is for a different world, please select another one");
|
||||
warning("World signature = %d, save signature = %d", _world->_signature, signature);
|
||||
delete data;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// More Counters
|
||||
int visitNum = data->readSint32LE(); //visitNum
|
||||
int loopNum = data->readSint32LE(); //loopNum
|
||||
int killNum = data->readSint32LE(); //killNum
|
||||
|
||||
// Hex offset to player character
|
||||
int playerOffset = data->readSint32LE();
|
||||
|
||||
// character in this scene?
|
||||
int presCharOffset = data->readSint32LE();
|
||||
|
||||
// Hex offset to current scene
|
||||
int currentSceneOffset = data->readSint32LE();
|
||||
|
||||
// find player and current scene
|
||||
Chr *player = getChrByOffset(playerOffset, chrsHexOffset);
|
||||
if (player == nullptr) {
|
||||
warning("Invalid Character! Aborting load.");
|
||||
delete data;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Scene *currentScene = getSceneByOffset(currentSceneOffset);
|
||||
if (currentScene == nullptr) {
|
||||
warning("Invalid Scene! Aborting load.");
|
||||
delete data;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// set player character
|
||||
_world->_player = player;
|
||||
|
||||
// set current scene
|
||||
player->_currentScene = currentScene;
|
||||
|
||||
// clear the players inventory list
|
||||
player->_inventory.clear();
|
||||
|
||||
// wearing a helmet?
|
||||
int helmetOffset = data->readSint32LE(); //helmetIndex
|
||||
|
||||
// holding a shield?
|
||||
int shieldOffset = data->readSint32LE(); //shieldIndex
|
||||
|
||||
// wearing chest armor?
|
||||
int armorOffset = data->readSint32LE(); //chestArmIndex
|
||||
|
||||
// wearing spiritual armor?
|
||||
int spiritualArmorOffset = data->readSint32LE(); //sprtArmIndex
|
||||
|
||||
data->readSint16LE(); // FFFF
|
||||
data->readSint16LE(); // FFFF
|
||||
data->readSint16LE(); // FFFF
|
||||
data->readSint16LE(); // FFFF
|
||||
|
||||
/* int runCharOffset = */ data->readSint32LE();
|
||||
|
||||
// players experience points
|
||||
int exp = data->readSint32LE(); // @ playerContext._experience
|
||||
|
||||
int aim = data->readSint16LE(); //aim
|
||||
int opponentAim = data->readSint16LE(); //opponentAim
|
||||
|
||||
data->readSint16LE(); // 0000
|
||||
data->readSint16LE(); // 0000
|
||||
data->readSint16LE(); // 0000
|
||||
|
||||
// Base character stats
|
||||
int basePhysStr = data->readByte();
|
||||
int basePhysHp = data->readByte();
|
||||
int basePhysArm = data->readByte();
|
||||
int basePhysAcc = data->readByte();
|
||||
int baseSprtStr = data->readByte();
|
||||
int baseSprtHp = data->readByte();
|
||||
int baseSprtArm = data->readByte();
|
||||
int baseSprtAcc = data->readByte();
|
||||
int baseRunSpeed = data->readByte();
|
||||
|
||||
// set player stats
|
||||
Context &playerContext = player->_context;
|
||||
// I'm setting player fields also, because those are used as base values in Chr::resetState()
|
||||
playerContext._statVariables[PHYS_STR_BAS] = player->_physicalStrength = basePhysStr;
|
||||
playerContext._statVariables[PHYS_HIT_BAS] = player->_physicalHp = basePhysHp;
|
||||
playerContext._statVariables[PHYS_ARM_BAS] = player->_naturalArmor = basePhysArm;
|
||||
playerContext._statVariables[PHYS_ACC_BAS] = player->_physicalAccuracy = basePhysAcc;
|
||||
playerContext._statVariables[SPIR_STR_BAS] = player->_spiritualStength = baseSprtStr;
|
||||
playerContext._statVariables[SPIR_HIT_BAS] = player->_spiritialHp = baseSprtHp;
|
||||
playerContext._statVariables[SPIR_ARM_BAS] = player->_resistanceToMagic = baseSprtArm;
|
||||
playerContext._statVariables[SPIR_ACC_BAS] = player->_spiritualAccuracy = baseSprtAcc;
|
||||
playerContext._statVariables[PHYS_SPE_BAS] = player->_runningSpeed = baseRunSpeed;
|
||||
|
||||
// set visit#
|
||||
playerContext._visits = visitNum;
|
||||
|
||||
// set monsters killed
|
||||
playerContext._kills = killNum;
|
||||
|
||||
// set experience
|
||||
playerContext._experience = exp;
|
||||
|
||||
// if a character is present, move it to this scene
|
||||
// TODO: This is done in the engine object, would it be cleaner
|
||||
// to move it here?
|
||||
// well, it's actually down there now, now sure if that's "cleaner"
|
||||
// when it's up there or down there
|
||||
|
||||
// if a character just ran away, let our engine know
|
||||
// TODO: The current engine doesn't have a case for this, we
|
||||
// should update it
|
||||
// yep, I don't see such code anywhere in java, so not added it here
|
||||
|
||||
data->readByte(); // 0x0A?
|
||||
|
||||
// set all user variables
|
||||
for (uint32 i = 0; i < 26 * 9; ++i) {
|
||||
playerContext._userVariables[i] = data->readSint16LE();
|
||||
}
|
||||
|
||||
// update all scene stats
|
||||
Common::Array<Scene *> &orderedScenes = _world->_orderedScenes;
|
||||
if ((uint)numScenes != orderedScenes.size()) {
|
||||
warning("scenes number in file (%d) differs from the one in world (%d)", numScenes, orderedScenes.size());
|
||||
}
|
||||
for (uint32 i = 0; i < orderedScenes.size(); ++i) {
|
||||
Scene *scene = orderedScenes[i];
|
||||
if (scene != _world->_storageScene) {
|
||||
int id = data->readSint16LE();
|
||||
|
||||
if (scene->_resourceId != id) {
|
||||
warning("loadGame(): updating scenes: expected %d but got %d", scene->_resourceId, id);
|
||||
data->skip(14); //2,2,1,1,1,1,2,1,1,1,1 down there
|
||||
continue;
|
||||
}
|
||||
|
||||
scene->_worldY = data->readSint16LE();
|
||||
scene->_worldX = data->readSint16LE();
|
||||
scene->_blocked[NORTH] = data->readByte() != 0;
|
||||
scene->_blocked[SOUTH] = data->readByte() != 0;
|
||||
scene->_blocked[EAST] = data->readByte() != 0;
|
||||
scene->_blocked[WEST] = data->readByte() != 0;
|
||||
scene->_soundFrequency = data->readSint16LE();
|
||||
scene->_soundType = data->readByte();
|
||||
// the following two bytes are currently unknown
|
||||
data->readByte();
|
||||
data->readByte();
|
||||
scene->_visited = data->readByte() != 0;
|
||||
}
|
||||
|
||||
scene->_chrs.clear();
|
||||
scene->_objs.clear();
|
||||
}
|
||||
|
||||
// update all char locations and stats
|
||||
Common::Array<Chr *> &orderedChrs = _world->_orderedChrs;
|
||||
if ((uint)numChars != orderedChrs.size()) {
|
||||
warning("characters number in file (%d) differs from the one in world (%d)", numChars, orderedChrs.size());
|
||||
}
|
||||
for (uint32 i = 0; i < orderedChrs.size(); ++i) {
|
||||
int resourceId = data->readSint16LE();
|
||||
int sceneResourceId = data->readSint16LE();
|
||||
|
||||
int strength = data->readByte();
|
||||
int hp = data->readByte();
|
||||
int armor = data->readByte();
|
||||
int accuracy = data->readByte();
|
||||
int spirStrength = data->readByte();
|
||||
int spirHp = data->readByte();
|
||||
int spirArmor = data->readByte();
|
||||
int spirAccuracy = data->readByte();
|
||||
int speed = data->readByte();
|
||||
int rejectsOffers = data->readByte();
|
||||
int followsOpponent = data->readByte();
|
||||
|
||||
// bytes 16-20 are unknown
|
||||
data->readByte();
|
||||
data->readByte();
|
||||
data->readByte();
|
||||
data->readByte();
|
||||
data->readByte();
|
||||
|
||||
int weaponDamage1 = data->readByte();
|
||||
int weaponDamage2 = data->readByte();
|
||||
|
||||
Chr *chr = orderedChrs[i];
|
||||
if (chr->_resourceId != resourceId) {
|
||||
warning("loadGame(): updating chrs: expected %d but got %d", chr->_resourceId, resourceId);
|
||||
continue;
|
||||
}
|
||||
|
||||
chr->_currentScene = getSceneById(sceneResourceId);
|
||||
Context &chrContext = chr->_context;
|
||||
chrContext._statVariables[PHYS_STR_CUR] = strength;
|
||||
chrContext._statVariables[PHYS_HIT_CUR] = hp;
|
||||
chrContext._statVariables[PHYS_ARM_CUR] = armor;
|
||||
chrContext._statVariables[PHYS_ACC_CUR] = accuracy;
|
||||
chrContext._statVariables[SPIR_STR_CUR] = spirStrength;
|
||||
chrContext._statVariables[SPIR_HIT_CUR] = spirHp;
|
||||
chrContext._statVariables[SPIR_ARM_CUR] = spirArmor;
|
||||
chrContext._statVariables[SPIR_ACC_CUR] = spirAccuracy;
|
||||
chrContext._statVariables[PHYS_SPE_CUR] = speed;
|
||||
chr->_rejectsOffers = rejectsOffers;
|
||||
chr->_followsOpponent = followsOpponent;
|
||||
chr->_weaponDamage1 = weaponDamage1;
|
||||
chr->_weaponDamage2 = weaponDamage2;
|
||||
}
|
||||
|
||||
// update all object locations and stats
|
||||
Common::Array<Obj *> &orderedObjs = _world->_orderedObjs;
|
||||
if ((uint)numObjs != orderedObjs.size()) {
|
||||
warning("objects number in file (%d) differs from the one in world (%d)", numObjs, orderedObjs.size());
|
||||
}
|
||||
for (uint32 i = 0; i < orderedObjs.size(); ++i) {
|
||||
int resourceId = data->readSint16LE();
|
||||
int locationResourceId = data->readSint16LE();
|
||||
int ownerResourceId = data->readSint16LE();
|
||||
|
||||
// bytes 7-9 are unknown (always = 0)
|
||||
data->readByte();
|
||||
data->readByte();
|
||||
data->readByte();
|
||||
|
||||
int accuracy = data->readByte();
|
||||
int value = data->readByte();
|
||||
int type = data->readByte();
|
||||
int damage = data->readByte();
|
||||
int attackType= data->readByte();
|
||||
int numberOfUses = data->readSint16LE();
|
||||
|
||||
Obj *obj = orderedObjs[i];
|
||||
if (obj->_resourceId != resourceId) {
|
||||
warning("loadGame(): updating objs: expected %d but got %d", obj->_resourceId, resourceId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ownerResourceId != 0) {
|
||||
obj->setCurrentOwner(getChrById(ownerResourceId));
|
||||
if (obj->_currentOwner == nullptr)
|
||||
warning("loadGame(): updating objs: owner not found - char with id %d", ownerResourceId);
|
||||
} else {
|
||||
obj->setCurrentScene(getSceneById(locationResourceId));
|
||||
if (obj->_currentScene == nullptr)
|
||||
warning("loadGame(): updating objs: scene with id %d not found", ownerResourceId);
|
||||
}
|
||||
|
||||
obj->_accuracy = accuracy;
|
||||
obj->_value = value;
|
||||
obj->_type = type;
|
||||
obj->_damage = damage;
|
||||
obj->_attackType = attackType;
|
||||
obj->_numberOfUses = numberOfUses;
|
||||
}
|
||||
|
||||
// update inventories and scene contents
|
||||
for (uint32 i = 0; i < orderedObjs.size(); ++i) {
|
||||
Obj *obj = orderedObjs[i];
|
||||
Chr *chr = obj->_currentOwner;
|
||||
if (chr != nullptr) {
|
||||
chr->_inventory.push_back(obj);
|
||||
} else {
|
||||
Scene *scene = obj->_currentScene;
|
||||
scene->_objs.push_back(obj);
|
||||
}
|
||||
}
|
||||
|
||||
// update scene chrs
|
||||
for (uint32 i = 0; i < orderedChrs.size(); ++i) {
|
||||
Chr *chr = orderedChrs[i];
|
||||
Scene *scene = chr->_currentScene;
|
||||
scene->_chrs.push_back(chr);
|
||||
if (chr != player) {
|
||||
wearObjs(chr);
|
||||
}
|
||||
}
|
||||
|
||||
// move all worn helmets, shields, chest armors and spiritual
|
||||
// armors to player
|
||||
for (int type = 0; type < Chr::NUMBER_OF_ARMOR_TYPES; ++type) {
|
||||
Obj *armor;
|
||||
|
||||
if (type == Chr::HEAD_ARMOR)
|
||||
armor = getObjByOffset(helmetOffset, objsHexOffset);
|
||||
else if (type == Chr::SHIELD_ARMOR)
|
||||
armor = getObjByOffset(shieldOffset, objsHexOffset);
|
||||
else if (type == Chr::BODY_ARMOR)
|
||||
armor = getObjByOffset(armorOffset, objsHexOffset);
|
||||
else
|
||||
armor = getObjByOffset(spiritualArmorOffset, objsHexOffset);
|
||||
|
||||
if (armor != nullptr) {
|
||||
_world->move(armor, player);
|
||||
player->_armor[type] = armor;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: make sure that armor in the inventory gets put on if we are wearing it
|
||||
|
||||
_loopCount = loopNum;
|
||||
|
||||
// let the engine know if there is a npc in the current scene
|
||||
if (presCharOffset != 0xffff) {
|
||||
_monster = getChrByOffset(presCharOffset, chrsHexOffset);
|
||||
}
|
||||
|
||||
// java engine calls clearOutput(); here
|
||||
// processTurn("look", NULL); called in Wage right after this loadGame()
|
||||
|
||||
// TODO: as you may see, aim, opponentAim or runCharOffset are not used anywhere
|
||||
// I'm fixing the first two, as those are clearly not even mentioned anywhere
|
||||
// the runCharOffset is mentioned up there as "not implemented case"
|
||||
_aim = aim;
|
||||
_opponentAim = opponentAim;
|
||||
|
||||
// Load the savaegame header
|
||||
ExtendedSavegameHeader header;
|
||||
if (!MetaEngine::readSavegameHeader(data, &header, true))
|
||||
error("Invalid savegame");
|
||||
|
||||
_defaultSaveDescritpion = header.description;
|
||||
|
||||
delete data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Common::Error WageEngine::loadGameState(int slot) {
|
||||
warning("LOADING %d", slot);
|
||||
|
||||
if (_isGameOver)
|
||||
resetState();
|
||||
|
||||
_gui->_consoleWindow->clearText();
|
||||
|
||||
if (loadGame(slot) == 0) {
|
||||
if (slot != getAutosaveSlot()) {
|
||||
_defaultSaveSlot = slot;
|
||||
// save description is set inside of loadGame()
|
||||
_gui->enableSave();
|
||||
}
|
||||
|
||||
sayText(_world->_player->_currentScene->_name, Common::TextToSpeechManager::QUEUE);
|
||||
|
||||
_gui->regenCommandsMenu();
|
||||
_gui->regenWeaponsMenu();
|
||||
|
||||
_gui->_consoleWindow->setTextWindowFont(_world->_player->_currentScene->getFont());
|
||||
|
||||
Common::String input("look");
|
||||
processTurn(&input, NULL);
|
||||
|
||||
return Common::kNoError;
|
||||
} else {
|
||||
return Common::kUnknownError;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Error WageEngine::saveGameState(int slot, const Common::String &description, bool isAutosave) {
|
||||
Common::String saveLoadSlot = getSaveStateName(slot);
|
||||
if (saveGame(saveLoadSlot, description) == 0) {
|
||||
if (slot != getAutosaveSlot()) {
|
||||
_defaultSaveSlot = slot;
|
||||
_defaultSaveDescritpion = description;
|
||||
|
||||
_gui->enableSave();
|
||||
}
|
||||
|
||||
return Common::kNoError;
|
||||
} else {
|
||||
return Common::kUnknownError;
|
||||
}
|
||||
}
|
||||
|
||||
bool WageEngine::scummVMSaveLoadDialog(bool isSave) {
|
||||
if (!isSave) {
|
||||
// do loading
|
||||
GUI::SaveLoadChooser dialog = GUI::SaveLoadChooser(_("Load game:"), _("Load"), false);
|
||||
int slot = dialog.runModalWithCurrentTarget();
|
||||
|
||||
if (slot < 0)
|
||||
return true;
|
||||
|
||||
return loadGameState(slot).getCode() == Common::kNoError;
|
||||
}
|
||||
|
||||
// do saving
|
||||
GUI::SaveLoadChooser dialog = GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
|
||||
int slot = dialog.runModalWithCurrentTarget();
|
||||
Common::String desc = dialog.getResultString();
|
||||
|
||||
if (desc.empty()) {
|
||||
// create our own description for the saved game, the user didn't enter it
|
||||
desc = dialog.createDefaultSaveDescription(slot);
|
||||
}
|
||||
|
||||
if (desc.size() > 28)
|
||||
desc = Common::String(desc.c_str(), 28);
|
||||
|
||||
if (slot < 0)
|
||||
return true;
|
||||
|
||||
return saveGameState(slot, desc).getCode() == Common::kNoError;
|
||||
}
|
||||
|
||||
} // End of namespace Agi
|
||||
1353
engines/wage/script.cpp
Normal file
1353
engines/wage/script.cpp
Normal file
File diff suppressed because it is too large
Load Diff
161
engines/wage/script.h
Normal file
161
engines/wage/script.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WAGE_SCRIPT_H
|
||||
#define WAGE_SCRIPT_H
|
||||
|
||||
namespace Wage {
|
||||
|
||||
class Script {
|
||||
public:
|
||||
Script(Common::SeekableReadStream *data, int num, WageEngine *engine);
|
||||
~Script();
|
||||
|
||||
private:
|
||||
Common::SeekableReadStream *_data;
|
||||
|
||||
WageEngine *_engine;
|
||||
World *_world;
|
||||
int _loopCount;
|
||||
Common::String *_inputText;
|
||||
Designed *_inputClick;
|
||||
bool _handled;
|
||||
|
||||
class Operand {
|
||||
public:
|
||||
union {
|
||||
Obj *obj;
|
||||
Chr *chr;
|
||||
Designed *designed;
|
||||
Scene *scene;
|
||||
int16 number;
|
||||
Common::String *string;
|
||||
Designed *inputClick;
|
||||
} _value;
|
||||
OperandType _type;
|
||||
|
||||
Operand(Obj *value, OperandType type) {
|
||||
_value.obj = value;
|
||||
assert(type == OBJ);
|
||||
_type = type;
|
||||
}
|
||||
|
||||
Operand(Chr *value, OperandType type) {
|
||||
_value.chr = value;
|
||||
assert(type == CHR);
|
||||
_type = type;
|
||||
}
|
||||
|
||||
Operand(Scene *value, OperandType type) {
|
||||
_value.scene = value;
|
||||
assert(type == SCENE);
|
||||
_type = type;
|
||||
}
|
||||
|
||||
Operand(int value, OperandType type) {
|
||||
_value.number = value;
|
||||
assert(type == NUMBER);
|
||||
_type = type;
|
||||
}
|
||||
|
||||
Operand(Common::String *value, OperandType type) {
|
||||
_value.string = value;
|
||||
assert(type == STRING || type == TEXT_INPUT);
|
||||
_type = type;
|
||||
}
|
||||
|
||||
Operand(Designed *value, OperandType type) {
|
||||
_value.inputClick = value;
|
||||
assert(type == CLICK_INPUT);
|
||||
_type = type;
|
||||
}
|
||||
|
||||
~Operand() {
|
||||
if (_type == STRING)
|
||||
delete _value.string;
|
||||
}
|
||||
|
||||
Common::String toString() const;
|
||||
};
|
||||
|
||||
struct ScriptText {
|
||||
int offset;
|
||||
Common::String line;
|
||||
};
|
||||
|
||||
public:
|
||||
void print();
|
||||
void printLine(int offset);
|
||||
bool execute(World *world, int loopCount, Common::String *inputText, Designed *inputClick);
|
||||
|
||||
private:
|
||||
Common::String preprocessInputText(Common::String inputText);
|
||||
Operand *readOperand();
|
||||
Operand *readStringOperand();
|
||||
const char *readOperator();
|
||||
void processIf();
|
||||
void skipBlock();
|
||||
void skipIf();
|
||||
bool compare(Operand *o1, Operand *o2, int comparator);
|
||||
bool eval(Operand *lhs, const char *op, Operand *rhs);
|
||||
bool evaluatePair(Operand *lhs, const char *op, Operand *rhs);
|
||||
Operand *convertOperand(Operand *operand, int type);
|
||||
bool evalClickCondition(Operand *lhs, const char *op, Operand *rhs);
|
||||
bool evalClickEquality(Operand *lhs, Operand *rhs, bool partialMatch);
|
||||
void processMove();
|
||||
void processLet();
|
||||
|
||||
void assign(byte operandType, int uservar, uint16 value);
|
||||
|
||||
void convertToText();
|
||||
|
||||
public:
|
||||
Common::Array<ScriptText *> _scriptText;
|
||||
};
|
||||
|
||||
} // End of namespace Wage
|
||||
|
||||
#endif
|
||||
212
engines/wage/sound.cpp
Normal file
212
engines/wage/sound.cpp
Normal file
@@ -0,0 +1,212 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "wage/wage.h"
|
||||
#include "wage/entities.h"
|
||||
#include "wage/gui.h"
|
||||
#include "wage/sound.h"
|
||||
#include "wage/world.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
static const int8 deltas[] = { 0,-49,-36,-25,-16,-9,-4,-1,0,1,4,9,16,25,36,49 };
|
||||
|
||||
Sound::Sound(Common::String name, Common::SeekableReadStream *data) : _name(name) {
|
||||
_size = data->size() - 20;
|
||||
_data = (byte *)calloc(2 * _size, 1);
|
||||
|
||||
debugC(1, kDebugSound, "Sound::Sound: loading sound '%s', size %d", name.c_str(), _size);
|
||||
|
||||
data->skip(20); // Skip header
|
||||
|
||||
byte value = 0x80;
|
||||
for (uint i = 0; i < _size; i++) {
|
||||
byte d = data->readByte();
|
||||
value += deltas[d & 0xf];
|
||||
_data[i * 2] = value;
|
||||
value += deltas[(d >> 4) & 0xf];
|
||||
_data[i * 2 + 1] = value;
|
||||
}
|
||||
}
|
||||
|
||||
Sound::~Sound() {
|
||||
free(_data);
|
||||
}
|
||||
|
||||
void WageEngine::playSound(Common::String soundName, bool blocking) {
|
||||
soundName.toLowercase();
|
||||
|
||||
if (!_world->_sounds.contains(soundName)) {
|
||||
if (!soundName.empty())
|
||||
warning("playSound: Sound '%s' does not exist", soundName.c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
debugC(1, kDebugSound, "WageEngine::playSound: playing sound '%s'%s", soundName.c_str(), blocking ? " blocking" : "");
|
||||
|
||||
Sound *s = _world->_sounds[soundName];
|
||||
|
||||
Audio::AudioStream *stream = Audio::makeRawStream(s->_data, 2 * s->_size, 11000, Audio::FLAG_UNSIGNED);
|
||||
|
||||
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, stream,
|
||||
-1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
|
||||
|
||||
while (_mixer->isSoundHandleActive(_soundHandle) && !_shouldQuit && blocking) {
|
||||
Common::Event event;
|
||||
|
||||
if (_eventMan->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_QUIT:
|
||||
if (ConfMan.hasKey("confirm_exit") && ConfMan.getBool("confirm_exit")) {
|
||||
if (!_shouldQuit) {
|
||||
g_system->getEventManager()->resetQuit();
|
||||
if (_gui->saveDialog()) {
|
||||
_shouldQuit = true;
|
||||
g_system->getEventManager()->pushEvent(event);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_shouldQuit = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_system->updateScreen();
|
||||
_system->delayMillis(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void soundTimer(void *refCon) {
|
||||
Scene *scene = (Scene *)refCon;
|
||||
WageEngine *engine = (WageEngine *)g_engine;
|
||||
|
||||
if (!engine)
|
||||
return;
|
||||
|
||||
g_system->getTimerManager()->removeTimerProc(&soundTimer);
|
||||
|
||||
if (engine->_world->_player->_currentScene != scene)
|
||||
return;
|
||||
|
||||
if (engine->_soundQueue.empty() && engine->_soundToPlay.empty()) {
|
||||
if (scene->_soundType == Scene::PERIODIC) {
|
||||
engine->_soundToPlay = scene->_soundName; // We cannot play sound here because that goes recursively
|
||||
|
||||
uint32 nextRun = 60000 / scene->_soundFrequency;
|
||||
g_system->getTimerManager()->installTimerProc(&soundTimer, nextRun * 1000, scene, "WageEngine::soundTimer");
|
||||
|
||||
debugC(1, kDebugSound, "soundTimer: enqueuing next periodic sound in %d ms (%s)", nextRun, scene->_soundName.c_str());
|
||||
} else if (scene->_soundType == Scene::RANDOM) {
|
||||
for (int i = 0; i < scene->_soundFrequency; i++) {
|
||||
uint32 nextRun = g_system->getMillis() + engine->_rnd->getRandomNumber(60000);
|
||||
engine->_soundQueue.push_back(nextRun);
|
||||
|
||||
debugC(1, kDebugSound, "soundTimer: enqueuing next random sound in %d ms (%s)", nextRun, scene->_soundName.c_str());
|
||||
}
|
||||
|
||||
Common::sort(engine->_soundQueue.begin(), engine->_soundQueue.end());
|
||||
|
||||
int nextRun = engine->_soundQueue.front() - g_system->getMillis();
|
||||
engine->_soundQueue.pop_front();
|
||||
|
||||
if (nextRun < 0)
|
||||
nextRun = 1;
|
||||
|
||||
g_system->getTimerManager()->installTimerProc(&soundTimer, nextRun * 1000, scene, "WageEngine::soundTimer");
|
||||
} else {
|
||||
warning("updateSoundTimerForScene: Unknown sound type %d", scene->_soundType);
|
||||
}
|
||||
} else {
|
||||
// Do not play sound if another is still playing
|
||||
if (!engine->_soundToPlay.empty()) {
|
||||
g_system->getTimerManager()->installTimerProc(&soundTimer, g_system->getMillis() + 100, scene, "WageEngine::soundTimer");
|
||||
return;
|
||||
}
|
||||
|
||||
int nextRun = engine->_soundQueue.front();
|
||||
engine->_soundQueue.pop_front();
|
||||
|
||||
g_system->getTimerManager()->installTimerProc(&soundTimer, (nextRun - g_system->getMillis()) * 1000, scene, "WageEngine::soundTimer");
|
||||
|
||||
engine->_soundToPlay = scene->_soundName; // We cannot play sound here because that goes recursively
|
||||
|
||||
debugC(1, kDebugSound, "soundTimer: preparing next sound in %d ms (%s)", nextRun - g_system->getMillis(), scene->_soundName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void WageEngine::updateSoundTimerForScene(Scene *scene, bool firstTime) {
|
||||
if (_world->_player->_currentScene != scene)
|
||||
return;
|
||||
|
||||
if (scene->_soundFrequency > 0 && !scene->_soundName.empty()) {
|
||||
Common::String soundName(scene->_soundName);
|
||||
|
||||
soundName.toLowercase();
|
||||
|
||||
if (!_world->_sounds.contains(soundName)) {
|
||||
warning("updateSoundTimerForScene: Sound '%s' does not exist", soundName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Launch the process
|
||||
soundTimer(scene);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Wage
|
||||
64
engines/wage/sound.h
Normal file
64
engines/wage/sound.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WAGE_SOUND_H
|
||||
#define WAGE_SOUND_H
|
||||
|
||||
namespace Wage {
|
||||
|
||||
class Sound {
|
||||
public:
|
||||
Sound(Common::String name, Common::SeekableReadStream *data);
|
||||
~Sound();
|
||||
|
||||
Common::String _name;
|
||||
uint _size;
|
||||
byte *_data;
|
||||
};
|
||||
|
||||
} // End of namespace Wage
|
||||
|
||||
#endif
|
||||
130
engines/wage/util.cpp
Normal file
130
engines/wage/util.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/stream.h"
|
||||
|
||||
#include "wage/wage.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
Common::Rect *readRect(Common::SeekableReadStream *in) {
|
||||
int x1, y1, x2, y2;
|
||||
|
||||
// Account for the extra two pixels because of the squares on the border
|
||||
y1 = in->readSint16BE() - 2;
|
||||
x1 = in->readSint16BE() - 2;
|
||||
y2 = in->readSint16BE() + 2;
|
||||
x2 = in->readSint16BE() + 2;
|
||||
|
||||
bool normalized = false;
|
||||
|
||||
if (x1 > x2) {
|
||||
SWAP(x1, x2);
|
||||
normalized = true;
|
||||
}
|
||||
|
||||
if (y1 > y2) {
|
||||
SWAP(y1, y2);
|
||||
normalized = true;
|
||||
}
|
||||
|
||||
debug(9, "readRect: %s%d, %d, %d, %d", normalized ? "norm " : "", x1, y1, x2, y2);
|
||||
|
||||
return new Common::Rect(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
const char *getIndefiniteArticle(const Common::String &word) {
|
||||
switch (word[0]) {
|
||||
case 'a': case 'A':
|
||||
case 'e': case 'E':
|
||||
case 'i': case 'I':
|
||||
case 'o': case 'O':
|
||||
case 'u': case 'U':
|
||||
return "an ";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "a ";
|
||||
}
|
||||
|
||||
enum {
|
||||
GENDER_MALE = 0,
|
||||
GENDER_FEMALE = 1,
|
||||
GENDER_NEUTRAL = 2
|
||||
};
|
||||
|
||||
const char *prependGenderSpecificPronoun(int gender) {
|
||||
if (gender == GENDER_MALE)
|
||||
return "his ";
|
||||
else if (gender == GENDER_FEMALE)
|
||||
return "her ";
|
||||
else
|
||||
return "its ";
|
||||
}
|
||||
|
||||
const char *getGenderSpecificPronoun(int gender, bool capitalize) {
|
||||
if (gender == GENDER_MALE)
|
||||
return capitalize ? "He" : "he";
|
||||
else if (gender == GENDER_FEMALE)
|
||||
return capitalize ? "She" : "she";
|
||||
else
|
||||
return capitalize ? "It" : "it";
|
||||
}
|
||||
|
||||
bool isStorageScene(const Common::String &name) {
|
||||
if (name.equalsIgnoreCase(STORAGESCENE))
|
||||
return true;
|
||||
|
||||
if (name.equalsIgnoreCase("STROAGE@")) // Jumble
|
||||
return true;
|
||||
|
||||
if (name.equalsIgnoreCase("STORAGE@@")) // Jumble
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Wage
|
||||
669
engines/wage/wage.cpp
Normal file
669
engines/wage/wage.cpp
Normal file
@@ -0,0 +1,669 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug-channels.h"
|
||||
#include "common/error.h"
|
||||
#include "common/events.h"
|
||||
#include "common/punycode.h"
|
||||
#include "common/system.h"
|
||||
#include "common/text-to-speech.h"
|
||||
|
||||
#include "audio/softsynth/pcspk.h"
|
||||
|
||||
#include "engines/engine.h"
|
||||
#include "engines/util.h"
|
||||
|
||||
#include "wage/wage.h"
|
||||
#include "wage/debugtools.h"
|
||||
#include "wage/entities.h"
|
||||
#include "wage/gui.h"
|
||||
#include "wage/script.h"
|
||||
#include "wage/world.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
WageEngine *g_wage = nullptr;
|
||||
|
||||
WageEngine::WageEngine(OSystem *syst, const ADGameDescription *desc) : Engine(syst), _gameDescription(desc) {
|
||||
_rnd = new Common::RandomSource("wage");
|
||||
|
||||
_aim = Chr::SIDE;
|
||||
_opponentAim = -1;
|
||||
_temporarilyHidden = false;
|
||||
_isGameOver = false;
|
||||
_monster = NULL;
|
||||
_running = NULL;
|
||||
_lastScene = NULL;
|
||||
|
||||
_loopCount = 0;
|
||||
_turn = 0;
|
||||
|
||||
_commandWasQuick = false;
|
||||
|
||||
_shouldQuit = false;
|
||||
|
||||
_gui = NULL;
|
||||
_world = NULL;
|
||||
_offer = NULL;
|
||||
|
||||
_resManager = NULL;
|
||||
|
||||
_speaker = NULL;
|
||||
|
||||
g_wage = this;
|
||||
|
||||
debug("WageEngine::WageEngine()");
|
||||
}
|
||||
|
||||
WageEngine::~WageEngine() {
|
||||
debug("WageEngine::~WageEngine()");
|
||||
|
||||
delete _world;
|
||||
delete _resManager;
|
||||
delete _gui;
|
||||
delete _rnd;
|
||||
|
||||
g_engine = nullptr;
|
||||
g_wage = nullptr;
|
||||
}
|
||||
|
||||
bool WageEngine::pollEvent(Common::Event &event) {
|
||||
return _eventMan->pollEvent(event);
|
||||
}
|
||||
|
||||
Common::Error WageEngine::run() {
|
||||
debug("WageEngine::init");
|
||||
|
||||
int width = 512;
|
||||
int height = 342;
|
||||
|
||||
if (getFeatures() & GF_RES800) {
|
||||
width = 800;
|
||||
height = 600;
|
||||
} else if (getFeatures() & GF_RES1024) {
|
||||
width = 1024;
|
||||
height = 768;
|
||||
}
|
||||
|
||||
initGraphics(width, height);
|
||||
|
||||
setDebugger(new Debugger(this));
|
||||
|
||||
// Your main event loop should be (invoked from) here.
|
||||
_resManager = new Common::MacResManager();
|
||||
if (!_resManager->open(Common::Path(getGameFile()).punycodeDecode()))
|
||||
error("Could not open %s as a resource fork", getGameFile());
|
||||
|
||||
_world = new World(this);
|
||||
|
||||
if (!_world->loadWorld(_resManager))
|
||||
return Common::kNoGameDataFoundError;
|
||||
|
||||
_shouldQuit = false;
|
||||
|
||||
_gui = new Gui(this);
|
||||
|
||||
#ifdef USE_IMGUI
|
||||
ImGuiCallbacks callbacks;
|
||||
bool drawImGui = debugChannelSet(-1, kDebugImGui);
|
||||
callbacks.init = onImGuiInit;
|
||||
callbacks.render = drawImGui ? onImGuiRender : nullptr;
|
||||
callbacks.cleanup = onImGuiCleanup;
|
||||
_system->setImGuiCallbacks(callbacks);
|
||||
#endif
|
||||
|
||||
_speaker = new Audio::PCSpeaker();
|
||||
_speaker->init();
|
||||
|
||||
_temporarilyHidden = true;
|
||||
performInitialSetup();
|
||||
if (ConfMan.hasKey("save_slot")) {
|
||||
int saveSlot = ConfMan.getInt("save_slot");
|
||||
loadGame(saveSlot);
|
||||
_gui->regenCommandsMenu();
|
||||
_gui->regenWeaponsMenu();
|
||||
}
|
||||
|
||||
_gui->_consoleWindow->setTextWindowFont(_world->_player->_currentScene->getFont());
|
||||
|
||||
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
|
||||
if (ttsMan) {
|
||||
ttsMan->setLanguage(ConfMan.get("language"));
|
||||
ttsMan->enable(ConfMan.getBool("tts_enabled"));
|
||||
_gui->_wm->setTTSEnabled(ConfMan.getBool("tts_enabled"));
|
||||
}
|
||||
|
||||
Common::String input("look");
|
||||
processTurn(&input, NULL);
|
||||
_temporarilyHidden = false;
|
||||
|
||||
while (!_shouldQuit) {
|
||||
processEvents();
|
||||
|
||||
if (_restartRequested)
|
||||
restart();
|
||||
|
||||
if (_gui)
|
||||
_gui->draw();
|
||||
|
||||
g_system->updateScreen();
|
||||
g_system->delayMillis(50);
|
||||
|
||||
if (!_soundToPlay.empty() && !_mixer->isSoundHandleActive(_soundHandle)) {
|
||||
debugC(1, kDebugSound, "** Sound from queue: %s", _soundToPlay.c_str());
|
||||
playSound(_soundToPlay, false); // Do not block input
|
||||
_soundToPlay.clear();
|
||||
}
|
||||
}
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
// Resetting required variables
|
||||
void WageEngine::resetState() {
|
||||
_aim = Chr::CHEST;
|
||||
_opponentAim = -1;
|
||||
_temporarilyHidden = false;
|
||||
_isGameOver = false;
|
||||
_monster = nullptr;
|
||||
_running = nullptr;
|
||||
_lastScene = nullptr;
|
||||
_loopCount = 0;
|
||||
_turn = 0;
|
||||
_commandWasQuick = false;
|
||||
_shouldQuit = false;
|
||||
_offer = nullptr;
|
||||
|
||||
delete _speaker;
|
||||
}
|
||||
|
||||
void WageEngine::restart() {
|
||||
if (_isGameOver)
|
||||
resetState();
|
||||
_restartRequested = false;
|
||||
delete _gui;
|
||||
delete _world;
|
||||
|
||||
_gui = nullptr;
|
||||
|
||||
_world = new World(this);
|
||||
|
||||
if (!_world->loadWorld(_resManager)) {
|
||||
_shouldQuit = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_shouldQuit = false;
|
||||
|
||||
_gui = new Gui(this);
|
||||
|
||||
_temporarilyHidden = true;
|
||||
performInitialSetup();
|
||||
|
||||
Common::String input("look");
|
||||
processTurn(&input, NULL);
|
||||
}
|
||||
|
||||
void WageEngine::processEvents() {
|
||||
Common::Event event;
|
||||
|
||||
while (_eventMan->pollEvent(event)) {
|
||||
if (_gui->processEvent(event))
|
||||
continue;
|
||||
|
||||
switch (event.type) {
|
||||
case Common::EVENT_QUIT:
|
||||
case Common::EVENT_RETURN_TO_LAUNCHER:
|
||||
if (ConfMan.hasKey("confirm_exit") && ConfMan.getBool("confirm_exit")) {
|
||||
if (!_shouldQuit) {
|
||||
g_system->getEventManager()->resetQuit();
|
||||
g_system->getEventManager()->resetReturnToLauncher();
|
||||
if (_gui->saveDialog()) {
|
||||
_shouldQuit = true;
|
||||
g_system->getEventManager()->pushEvent(event);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_shouldQuit = true;
|
||||
}
|
||||
break;
|
||||
case Common::EVENT_KEYDOWN:
|
||||
switch (event.kbd.keycode) {
|
||||
case Common::KEYCODE_RETURN: {
|
||||
_inputText = Common::convertFromU32String(_gui->_consoleWindow->getInput());
|
||||
Common::String inp = _inputText + '\n';
|
||||
|
||||
sayText(_gui->_consoleWindow->getInput(), Common::TextToSpeechManager::INTERRUPT);
|
||||
|
||||
_gui->appendText(inp.c_str());
|
||||
|
||||
_gui->_consoleWindow->clearInput();
|
||||
|
||||
if (_inputText.empty())
|
||||
break;
|
||||
|
||||
processTurn(&_inputText, NULL);
|
||||
_gui->disableUndo();
|
||||
_gui->enableRevert();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WageEngine::setMenu(Common::String menu) {
|
||||
_world->_commandsMenu = menu;
|
||||
|
||||
_gui->regenCommandsMenu();
|
||||
}
|
||||
|
||||
void WageEngine::appendText(const char *str) {
|
||||
Common::String s(str);
|
||||
|
||||
// HACK: Added here because sometimes empty strings would be passed, leading to extra newlines
|
||||
if (!s.empty()){
|
||||
s += '\n';
|
||||
|
||||
_gui->appendText(s.c_str());
|
||||
sayText(s, Common::TextToSpeechManager::QUEUE);
|
||||
}
|
||||
|
||||
_inputText.clear();
|
||||
}
|
||||
|
||||
void WageEngine::sayText(const Common::U32String &str, Common::TextToSpeechManager::Action action) const {
|
||||
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
|
||||
if (ttsMan && ConfMan.getBool("tts_enabled")) {
|
||||
ttsMan->say(str, action);
|
||||
}
|
||||
}
|
||||
|
||||
void WageEngine::sayText(const Common::String &str, Common::TextToSpeechManager::Action action) const {
|
||||
sayText(Common::U32String(str, Common::CodePage::kMacRoman), action);
|
||||
}
|
||||
|
||||
void WageEngine::saveGame() {
|
||||
if (_defaultSaveSlot != -1 && _defaultSaveSlot != getAutosaveSlot())
|
||||
saveGameState(_defaultSaveSlot, _defaultSaveDescritpion, false);
|
||||
else
|
||||
scummVMSaveLoadDialog(true);
|
||||
}
|
||||
|
||||
void WageEngine::performInitialSetup() {
|
||||
debug(5, "Resetting Objs: %d", _world->_orderedObjs.size());
|
||||
if (_world->_orderedObjs.size() > 0) {
|
||||
for (uint i = 0; i < _world->_orderedObjs.size() - 1; i++)
|
||||
_world->move(_world->_orderedObjs[i], _world->_storageScene, true);
|
||||
|
||||
_world->move(_world->_orderedObjs[_world->_orderedObjs.size() - 1], _world->_storageScene);
|
||||
}
|
||||
|
||||
debug(5, "Resetting Chrs: %d", _world->_orderedChrs.size());
|
||||
if (_world->_orderedChrs.size() > 0) {
|
||||
for (uint i = 0; i < _world->_orderedChrs.size() - 1; i++)
|
||||
_world->move(_world->_orderedChrs[i], _world->_storageScene, true);
|
||||
|
||||
_world->move(_world->_orderedChrs[_world->_orderedChrs.size() - 1], _world->_storageScene);
|
||||
}
|
||||
|
||||
debug(5, "Resetting Owners: %d", _world->_orderedObjs.size());
|
||||
for (uint i = 0; i < _world->_orderedObjs.size(); i++) {
|
||||
Obj *obj = _world->_orderedObjs[i];
|
||||
if (!isStorageScene(obj->_sceneOrOwner)) {
|
||||
Common::String location = obj->_sceneOrOwner;
|
||||
location.toLowercase();
|
||||
Scene *scene = getSceneByName(location);
|
||||
if (scene != NULL) {
|
||||
_world->move(obj, scene);
|
||||
} else {
|
||||
if (!_world->_chrs.contains(location)) {
|
||||
// Note: PLAYER@ is not a valid target here.
|
||||
warning("Couldn't move %s to \"%s\"", obj->_name.c_str(), obj->_sceneOrOwner.c_str());
|
||||
} else {
|
||||
// TODO: Add check for max items.
|
||||
_world->move(obj, _world->_chrs[location]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool playerPlaced = false;
|
||||
for (uint i = 0; i < _world->_orderedChrs.size(); i++) {
|
||||
Chr *chr = _world->_orderedChrs[i];
|
||||
if (!isStorageScene(chr->_initialScene)) {
|
||||
Common::String key = chr->_initialScene;
|
||||
key.toLowercase();
|
||||
if (_world->_scenes.contains(key) && _world->_scenes[key] != NULL) {
|
||||
_world->move(chr, _world->_scenes[key]);
|
||||
|
||||
if (chr->_playerCharacter)
|
||||
debug(0, "Initial scene: %s", key.c_str());
|
||||
} else {
|
||||
_world->move(chr, _world->getRandomScene());
|
||||
}
|
||||
if (chr->_playerCharacter) {
|
||||
playerPlaced = true;
|
||||
}
|
||||
}
|
||||
chr->wearObjs();
|
||||
}
|
||||
if (!playerPlaced) {
|
||||
_world->move(_world->_player, _world->getRandomScene());
|
||||
}
|
||||
|
||||
sayText(_world->_player->_currentScene->_name);
|
||||
|
||||
// Set the console window's dimensions early here because
|
||||
// flowText() that needs them gets called before they're set
|
||||
_gui->_consoleWindow->setDimensions(*_world->_player->_currentScene->_textBounds);
|
||||
}
|
||||
|
||||
void WageEngine::wearObjs(Chr* chr) {
|
||||
if (chr != nullptr)
|
||||
chr->wearObjs();
|
||||
}
|
||||
|
||||
void WageEngine::doClose() {
|
||||
// No op on ScummVM since we do not allow to load arbitrary games
|
||||
}
|
||||
|
||||
Scene *WageEngine::getSceneByName(Common::String &location) {
|
||||
if (location.equals("random@")) {
|
||||
return _world->getRandomScene();
|
||||
} else {
|
||||
if (_world->_scenes.contains(location))
|
||||
return _world->_scenes[location];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void WageEngine::onMove(Designed *what, Designed *from, Designed *to) {
|
||||
Chr *player = _world->_player;
|
||||
Scene *currentScene = player->_currentScene;
|
||||
if (currentScene == _world->_storageScene && !_temporarilyHidden) {
|
||||
if (!_isGameOver) {
|
||||
_isGameOver = true;
|
||||
_gui->gameOver();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (from == currentScene || to == currentScene ||
|
||||
(what->_classType == CHR && ((Chr *)what)->_currentScene == currentScene) ||
|
||||
(what->_classType == OBJ && ((Obj *)what)->_currentScene == currentScene))
|
||||
_gui->setSceneDirty();
|
||||
|
||||
if ((from == player || to == player) && !_temporarilyHidden)
|
||||
_gui->regenWeaponsMenu();
|
||||
|
||||
if (what != player && what->_classType == CHR) {
|
||||
Chr *chr = (Chr *)what;
|
||||
// the original code below forced enemies to immediately respawn if moved to storage.
|
||||
// this broke the "Escape" mechanic, comment it out so they stay in storage.
|
||||
//if (to == _world->_storageScene) {
|
||||
// int returnTo = chr->_returnTo;
|
||||
// if (returnTo != Chr::RETURN_TO_STORAGE) {
|
||||
// Common::String returnToSceneName;
|
||||
// if (returnTo == Chr::RETURN_TO_INITIAL_SCENE) {
|
||||
// returnToSceneName = chr->_initialScene;
|
||||
// returnToSceneName.toLowercase();
|
||||
// } else {
|
||||
// returnToSceneName = "random@";
|
||||
// }
|
||||
// Scene *scene = getSceneByName(returnToSceneName);
|
||||
// if (scene != NULL && scene != _world->_storageScene) {
|
||||
// _world->move(chr, scene);
|
||||
// // To avoid sleeping twice, return if the above move command would cause a sleep.
|
||||
// if (scene == currentScene)
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
if (to == player->_currentScene) {
|
||||
if (getMonster() == NULL) {
|
||||
_monster = chr;
|
||||
encounter(player, chr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!_temporarilyHidden) {
|
||||
if (to == currentScene || from == currentScene) {
|
||||
redrawScene();
|
||||
g_system->updateScreen();
|
||||
g_system->delayMillis(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WageEngine::redrawScene() {
|
||||
Scene *currentScene = _world->_player->_currentScene;
|
||||
|
||||
if (currentScene != NULL) {
|
||||
bool firstTime = (_lastScene != currentScene);
|
||||
|
||||
_gui->draw();
|
||||
updateSoundTimerForScene(currentScene, firstTime);
|
||||
}
|
||||
}
|
||||
|
||||
void WageEngine::processTurnInternal(Common::String *textInput, Designed *clickInput) {
|
||||
Scene *playerScene = _world->_player->_currentScene;
|
||||
if (playerScene == _world->_storageScene)
|
||||
return;
|
||||
|
||||
bool shouldEncounter = false;
|
||||
|
||||
if (playerScene != _lastScene) {
|
||||
_loopCount = 0;
|
||||
_lastScene = playerScene;
|
||||
_monster = NULL;
|
||||
_running = NULL;
|
||||
_offer = NULL;
|
||||
|
||||
for (ChrList::const_iterator it = playerScene->_chrs.begin(); it != playerScene->_chrs.end(); ++it) {
|
||||
if (!(*it)->_playerCharacter) {
|
||||
_monster = *it;
|
||||
shouldEncounter = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool monsterWasNull = (_monster == NULL);
|
||||
Script *script = playerScene->_script != NULL ? playerScene->_script : _world->_globalScript;
|
||||
bool handled = script->execute(_world, _loopCount++, textInput, clickInput);
|
||||
|
||||
playerScene = _world->_player->_currentScene;
|
||||
|
||||
if (playerScene == _world->_storageScene)
|
||||
return;
|
||||
|
||||
if (playerScene != _lastScene) {
|
||||
_temporarilyHidden = true;
|
||||
_gui->clearOutput();
|
||||
_gui->_consoleWindow->setTextWindowFont(_world->_player->_currentScene->getFont());
|
||||
_world->_commandsMenu = _world->_commandsMenuDefault;
|
||||
_gui->regenCommandsMenu();
|
||||
regen();
|
||||
sayText(playerScene->_name, Common::TextToSpeechManager::QUEUE);
|
||||
Common::String input("look");
|
||||
processTurnInternal(&input, NULL);
|
||||
|
||||
if (_shouldQuit)
|
||||
return;
|
||||
|
||||
// WORKAROUND: The original Java codebase did not have this check and
|
||||
// called gameOver() only in onMove() method. However, this leads to a crash in
|
||||
// Gui::redraw(), when _engine->_world->_player->_currentScene is equal to _world->_storageScene.
|
||||
// The crash happens because storage scene's _designBounds member is NULL.
|
||||
// Therefore, to fix this, we check and call gameOver() here if needed.
|
||||
if (_world->_player->_currentScene == _world->_storageScene) {
|
||||
if (!_isGameOver) {
|
||||
_isGameOver = true;
|
||||
_gui->gameOver();
|
||||
}
|
||||
}
|
||||
|
||||
redrawScene();
|
||||
_temporarilyHidden = false;
|
||||
} else if (_loopCount == 1) {
|
||||
redrawScene();
|
||||
if (shouldEncounter && getMonster() != NULL) {
|
||||
encounter(_world->_player, _monster);
|
||||
}
|
||||
} else if (textInput != NULL && !handled) {
|
||||
if (monsterWasNull && getMonster() != NULL)
|
||||
return;
|
||||
|
||||
const char *rant = _rnd->getRandomNumber(1) ? "What?" : "Huh?";
|
||||
|
||||
appendText(rant);
|
||||
_commandWasQuick = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WageEngine::processTurn(Common::String *textInput, Designed *clickInput) {
|
||||
_commandWasQuick = false;
|
||||
Scene *prevScene = _world->_player->_currentScene;
|
||||
Chr *prevMonster = _monster;
|
||||
Chr *runner = _running;
|
||||
Common::String input;
|
||||
|
||||
if (textInput)
|
||||
input = *textInput;
|
||||
|
||||
input.toLowercase();
|
||||
|
||||
// if the player is frozen, we loop automatically to process enemy turns
|
||||
// without waiting for user input
|
||||
while (_world->_player->_context._frozen) {
|
||||
// decrement Timer
|
||||
_world->_player->_context._freezeTimer--;
|
||||
|
||||
if (_world->_player->_context._freezeTimer <= 0) {
|
||||
_world->_player->_context._frozen = false;
|
||||
_world->_player->_context._freezeTimer = 0;
|
||||
// we break the loop. The player regains control for the next input
|
||||
break;
|
||||
}
|
||||
|
||||
// enemy gets a free attack
|
||||
if (getMonster() != NULL)
|
||||
performCombatAction(getMonster(), _world->_player);
|
||||
|
||||
// since we are inside a while loop, we must
|
||||
// force a screen update or the text will not appear until the end
|
||||
if (_gui) _gui->draw();
|
||||
g_system->updateScreen();
|
||||
|
||||
// if player died during freeze, return
|
||||
if (_isGameOver || _world->_player->_currentScene == _world->_storageScene)
|
||||
return;
|
||||
}
|
||||
|
||||
// only process the player's input if they are not frozen
|
||||
if (!_world->_player->_context._frozen)
|
||||
processTurnInternal(&input, clickInput);
|
||||
|
||||
Scene *playerScene = _world->_player->_currentScene;
|
||||
|
||||
if (prevScene != playerScene && playerScene != _world->_storageScene) {
|
||||
if (prevMonster != NULL) {
|
||||
bool followed = false;
|
||||
bool monsterEscaped = false;
|
||||
|
||||
// check if the previous monster followed us to the new room
|
||||
if (prevMonster->_currentScene != playerScene) {
|
||||
// monster is gone did it escape?
|
||||
// if the monster we were fighting was running, and is now in storage that means it escaped
|
||||
if (prevMonster == runner && prevMonster->_currentScene == _world->_storageScene) {
|
||||
char buf[512];
|
||||
snprintf(buf, 512, "%s%s escapes!", prevMonster->getDefiniteArticle(true), prevMonster->_name.c_str());
|
||||
appendText(buf);
|
||||
monsterEscaped = true;
|
||||
}
|
||||
// TODO: adjacent scenes doesn't contain up/down etc... verify that monsters can't follow these...
|
||||
// only check follow logic if monster did not just escape to storage
|
||||
if (!monsterEscaped) {
|
||||
if (_world->scenesAreConnected(playerScene, prevMonster->_currentScene)) {
|
||||
int chance = _rnd->getRandomNumber(255);
|
||||
followed = (chance < prevMonster->_followsOpponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char buf[512];
|
||||
|
||||
if (followed) {
|
||||
snprintf(buf, 512, "%s%s follows you.", prevMonster->getDefiniteArticle(true), prevMonster->_name.c_str());
|
||||
appendText(buf);
|
||||
|
||||
_world->move(prevMonster, playerScene);
|
||||
} else if (!monsterEscaped) {
|
||||
// only say "You escape" if the monster did not already "Escape"
|
||||
snprintf(buf, 512, "You escape %s%s.", prevMonster->getDefiniteArticle(false), prevMonster->_name.c_str());
|
||||
appendText(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!_commandWasQuick && getMonster() != NULL) {
|
||||
performCombatAction(getMonster(), _world->_player);
|
||||
}
|
||||
|
||||
_inputText.clear();
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Wage
|
||||
268
engines/wage/wage.h
Normal file
268
engines/wage/wage.h
Normal file
@@ -0,0 +1,268 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WAGE_WAGE_H
|
||||
#define WAGE_WAGE_H
|
||||
|
||||
#include "engines/engine.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/macresman.h"
|
||||
#include "common/random.h"
|
||||
#include "common/timer.h"
|
||||
#include "common/text-to-speech.h"
|
||||
|
||||
#include "wage/debugger.h"
|
||||
|
||||
struct ADGameDescription;
|
||||
|
||||
namespace Common {
|
||||
struct Event;
|
||||
}
|
||||
|
||||
namespace Graphics {
|
||||
class MacDialog;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
class PCSpeaker;
|
||||
}
|
||||
|
||||
namespace Wage {
|
||||
|
||||
class Console;
|
||||
class Chr;
|
||||
class Designed;
|
||||
class Dialog;
|
||||
class Gui;
|
||||
class Obj;
|
||||
class Scene;
|
||||
class World;
|
||||
|
||||
typedef Common::Array<Obj *> ObjArray;
|
||||
typedef Common::Array<Chr *> ChrArray;
|
||||
typedef Common::List<Obj *> ObjList;
|
||||
typedef Common::List<Chr *> ChrList;
|
||||
|
||||
#define STORAGESCENE "STORAGE@"
|
||||
|
||||
enum {
|
||||
kDebugImGui = 1,
|
||||
kDebugSound,
|
||||
kDebugLoading,
|
||||
};
|
||||
|
||||
enum OperandType {
|
||||
OBJ = 0,
|
||||
CHR = 1,
|
||||
SCENE = 2,
|
||||
NUMBER = 3,
|
||||
STRING = 4,
|
||||
CLICK_INPUT = 5,
|
||||
TEXT_INPUT = 6,
|
||||
UNKNOWN = 100
|
||||
};
|
||||
|
||||
enum Directions {
|
||||
NORTH = 0,
|
||||
SOUTH = 1,
|
||||
EAST = 2,
|
||||
WEST = 3
|
||||
};
|
||||
|
||||
enum Resolution {
|
||||
GF_RES800 = 1 << 0,
|
||||
GF_RES1024 = 1 << 1
|
||||
};
|
||||
|
||||
Common::Rect *readRect(Common::SeekableReadStream *in);
|
||||
const char *getIndefiniteArticle(const Common::String &word);
|
||||
const char *prependGenderSpecificPronoun(int gender);
|
||||
const char *getGenderSpecificPronoun(int gender, bool capitalize);
|
||||
bool isStorageScene(const Common::String &name);
|
||||
|
||||
class WageEngine : public Engine {
|
||||
friend class Dialog;
|
||||
public:
|
||||
WageEngine(OSystem *syst, const ADGameDescription *gameDesc);
|
||||
~WageEngine() override;
|
||||
|
||||
bool hasFeature(EngineFeature f) const override;
|
||||
|
||||
Common::Error run() override;
|
||||
|
||||
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
|
||||
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
|
||||
|
||||
const char *getGameFile() const;
|
||||
void processTurn(Common::String *textInput, Designed *clickInput);
|
||||
void regen();
|
||||
|
||||
const char *getTargetName() { return _targetName.c_str(); }
|
||||
bool pollEvent(Common::Event &event);
|
||||
|
||||
private:
|
||||
bool loadWorld(Common::MacResManager *resMan);
|
||||
void performInitialSetup();
|
||||
void wearObjs(Chr *chr);
|
||||
void processTurnInternal(Common::String *textInput, Designed *clickInput);
|
||||
void performCombatAction(Chr *npc, Chr *player);
|
||||
int getValidMoveDirections(Chr *npc);
|
||||
void performAttack(Chr *attacker, Chr *victim, Obj *weapon);
|
||||
void performMagic(Chr *attacker, Chr *victim, Obj *magicalObject);
|
||||
void performMove(Chr *chr, int validMoves);
|
||||
void performOffer(Chr *attacker, Chr *victim);
|
||||
void performTake(Chr *npc, Obj *obj);
|
||||
void decrementUses(Obj *obj);
|
||||
bool attackHit(Chr *attacker, Chr *victim, Obj *weapon, int targetIndex);
|
||||
void performHealingMagic(Chr *chr, Obj *magicalObject);
|
||||
|
||||
public:
|
||||
void takeObj(Obj *obj);
|
||||
|
||||
bool handleMoveCommand(Directions dir, const char *dirName);
|
||||
bool handleLookCommand();
|
||||
Common::String *getGroundItemsList(Scene *scene);
|
||||
void appendObjNames(Common::String &str, const ObjArray &objs);
|
||||
bool handleInventoryCommand();
|
||||
bool handleStatusCommand();
|
||||
bool handleRestCommand();
|
||||
bool handleAcceptCommand();
|
||||
|
||||
bool handleTakeCommand(const char *target);
|
||||
bool handleDropCommand(const char *target);
|
||||
bool handleAimCommand(const char *target);
|
||||
bool handleWearCommand(const char *target);
|
||||
bool handleOfferCommand(const char *target);
|
||||
|
||||
void wearObj(Obj *o, int pos);
|
||||
|
||||
bool tryAttack(const Obj *weapon, const Common::String &input);
|
||||
bool handleAttack(Obj *weapon);
|
||||
|
||||
void printPlayerCondition(Chr *player);
|
||||
const char *getPercentMessage(double percent);
|
||||
|
||||
void doClose();
|
||||
|
||||
public:
|
||||
Common::RandomSource *_rnd;
|
||||
|
||||
Gui *_gui;
|
||||
World *_world;
|
||||
|
||||
Scene *_lastScene;
|
||||
int _loopCount;
|
||||
int _turn;
|
||||
Chr *_monster;
|
||||
Chr *_running;
|
||||
Obj *_offer;
|
||||
int _aim;
|
||||
int _opponentAim;
|
||||
bool _temporarilyHidden;
|
||||
bool _isGameOver;
|
||||
bool _commandWasQuick;
|
||||
bool _restartRequested = false;
|
||||
|
||||
bool _shouldQuit;
|
||||
int _defaultSaveSlot = -1;
|
||||
Common::String _defaultSaveDescritpion;
|
||||
|
||||
Common::String _inputText;
|
||||
|
||||
Common::List<int> _soundQueue;
|
||||
Common::String _soundToPlay;
|
||||
Audio::PCSpeaker *_speaker;
|
||||
|
||||
void playSound(Common::String soundName, bool blocking = true);
|
||||
void updateSoundTimerForScene(Scene *scene, bool firstTime);
|
||||
void setMenu(Common::String soundName);
|
||||
void appendText(const char *str);
|
||||
void sayText(const Common::U32String &str, Common::TextToSpeechManager::Action action = Common::TextToSpeechManager::INTERRUPT_NO_REPEAT) const;
|
||||
void sayText(const Common::String &str, Common::TextToSpeechManager::Action action = Common::TextToSpeechManager::INTERRUPT_NO_REPEAT) const;
|
||||
Obj *getOffer();
|
||||
Chr *getMonster();
|
||||
void processEvents();
|
||||
Scene *getSceneByName(Common::String &location);
|
||||
void onMove(Designed *what, Designed *from, Designed *to);
|
||||
void encounter(Chr *player, Chr *chr);
|
||||
void redrawScene();
|
||||
void saveGame();
|
||||
|
||||
uint32 getFeatures();
|
||||
|
||||
Common::Error loadGameState(int slot) override;
|
||||
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
|
||||
bool scummVMSaveLoadDialog(bool isSave);
|
||||
|
||||
private:
|
||||
int getSceneIndex(Scene *scene) const;
|
||||
Obj *getObjByOffset(int offset, int objBaseOffset) const;
|
||||
Chr *getChrById(int resId) const;
|
||||
Chr *getChrByOffset(int offset, int chrBaseOffset) const;
|
||||
Scene *getSceneById(int id) const;
|
||||
Scene *getSceneByOffset(int offset) const;
|
||||
int saveGame(const Common::String &fileName, const Common::String &descriptionString);
|
||||
int loadGame(int slotId);
|
||||
|
||||
void resetState();
|
||||
void restart();
|
||||
|
||||
private:
|
||||
const ADGameDescription *_gameDescription;
|
||||
|
||||
Common::MacResManager *_resManager;
|
||||
|
||||
Audio::SoundHandle _soundHandle;
|
||||
};
|
||||
|
||||
extern WageEngine *g_wage;
|
||||
|
||||
} // End of namespace Wage
|
||||
|
||||
#endif
|
||||
571
engines/wage/world.cpp
Normal file
571
engines/wage/world.cpp
Normal file
@@ -0,0 +1,571 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/file.h"
|
||||
#include "graphics/macgui/macwindowmanager.h"
|
||||
#include "graphics/macgui/macfontmanager.h"
|
||||
#include "graphics/macgui/macmenu.h"
|
||||
#include "graphics/fontman.h"
|
||||
|
||||
#include "wage/wage.h"
|
||||
#include "wage/entities.h"
|
||||
#include "wage/script.h"
|
||||
#include "wage/sound.h"
|
||||
#include "wage/world.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
World::World(WageEngine *engine) {
|
||||
_storageScene = new Scene;
|
||||
_storageScene->_name = STORAGESCENE;
|
||||
|
||||
_orderedScenes.push_back(_storageScene);
|
||||
_scenes[STORAGESCENE] = _storageScene;
|
||||
|
||||
_gameOverMessage = nullptr;
|
||||
_saveBeforeQuitMessage = nullptr;
|
||||
_saveBeforeCloseMessage = nullptr;
|
||||
_revertMessage = nullptr;
|
||||
|
||||
_globalScript = nullptr;
|
||||
_player = nullptr;
|
||||
_signature = 0;
|
||||
|
||||
_weaponMenuDisabled = true;
|
||||
|
||||
_engine = engine;
|
||||
|
||||
_patterns = new Graphics::MacPatterns;
|
||||
}
|
||||
|
||||
World::~World() {
|
||||
for (uint i = 0; i < _orderedObjs.size(); i++)
|
||||
delete _orderedObjs[i];
|
||||
|
||||
for (uint i = 0; i < _orderedChrs.size(); i++)
|
||||
delete _orderedChrs[i];
|
||||
|
||||
for (uint i = 0; i < _orderedSounds.size(); i++)
|
||||
delete _orderedSounds[i];
|
||||
|
||||
for (uint i = 0; i < _orderedScenes.size(); i++)
|
||||
delete _orderedScenes[i];
|
||||
|
||||
for (uint i = 0; i < _patterns->size(); i++)
|
||||
free(const_cast<byte *>(_patterns->operator[](i)));
|
||||
|
||||
delete _patterns;
|
||||
|
||||
delete _globalScript;
|
||||
|
||||
delete _gameOverMessage;
|
||||
delete _saveBeforeQuitMessage;
|
||||
delete _saveBeforeCloseMessage;
|
||||
delete _revertMessage;
|
||||
|
||||
}
|
||||
|
||||
bool World::loadWorld(Common::MacResManager *resMan) {
|
||||
Common::MacResIDArray resArray;
|
||||
Common::SeekableReadStream *res;
|
||||
Common::MacResIDArray::const_iterator iter;
|
||||
|
||||
// Dumping interpreter code
|
||||
#if 0
|
||||
res = resMan->getResource(MKTAG('C','O','D','E'), 1);
|
||||
warning("Dumping interpreter code size: %d", res->size());
|
||||
byte *buf = (byte *)malloc(res->size());
|
||||
res->read(buf, res->size());
|
||||
Common::DumpFile out;
|
||||
out.open("code.bin");
|
||||
out.write(buf, res->size());
|
||||
out.close();
|
||||
free(buf);
|
||||
delete res;
|
||||
#endif
|
||||
|
||||
if ((resArray = resMan->getResIDArray(MKTAG('G','C','O','D'))).size() == 0)
|
||||
return false;
|
||||
|
||||
// Load global script
|
||||
res = resMan->getResource(MKTAG('G','C','O','D'), resArray[0]);
|
||||
_globalScript = new Script(res, -1, _engine);
|
||||
|
||||
// TODO: read creator
|
||||
|
||||
// Load main configuration
|
||||
if ((resArray = resMan->getResIDArray(MKTAG('V','E','R','S'))).size() == 0)
|
||||
return false;
|
||||
|
||||
_name = resMan->getBaseFileName().toString();
|
||||
|
||||
if (resArray.size() > 1)
|
||||
warning("Too many VERS resources");
|
||||
|
||||
if (!resArray.empty()) {
|
||||
debug(3, "Loading version info");
|
||||
|
||||
res = resMan->getResource(MKTAG('V','E','R','S'), resArray[0]);
|
||||
|
||||
_signature = res->readSint32LE();
|
||||
res->skip(6);
|
||||
byte b = res->readByte();
|
||||
_weaponMenuDisabled = (b != 0);
|
||||
if (b != 0 && b != 1)
|
||||
error("Unexpected value for weapons menu");
|
||||
|
||||
res->skip(3);
|
||||
_aboutMessage = res->readPascalString();
|
||||
|
||||
if (!scumm_stricmp(_name.c_str(), "Scepters"))
|
||||
res->skip(1); // ????
|
||||
|
||||
_soundLibrary1 = Common::Path(res->readPascalString());
|
||||
_soundLibrary2 = Common::Path(res->readPascalString());
|
||||
|
||||
delete res;
|
||||
}
|
||||
|
||||
Common::String *message;
|
||||
if ((message = loadStringFromDITL(resMan, 2910, 1)) != NULL) {
|
||||
message->trim();
|
||||
debug(2, "_gameOverMessage: %s", message->c_str());
|
||||
_gameOverMessage = message;
|
||||
} else {
|
||||
_gameOverMessage = new Common::String("Game Over!");
|
||||
}
|
||||
if ((message = loadStringFromDITL(resMan, 2480, 3)) != NULL) {
|
||||
message->trim();
|
||||
debug(2, "_saveBeforeQuitMessage: %s", message->c_str());
|
||||
_saveBeforeQuitMessage = message;
|
||||
} else {
|
||||
_saveBeforeQuitMessage = new Common::String("Save changes before quiting?");
|
||||
}
|
||||
if ((message = loadStringFromDITL(resMan, 2490, 3)) != NULL && !message->empty()) {
|
||||
message->trim();
|
||||
debug(2, "_saveBeforeCloseMessage: %s", message->c_str());
|
||||
_saveBeforeCloseMessage = message;
|
||||
} else {
|
||||
_saveBeforeCloseMessage = new Common::String("Save changes before closing?");
|
||||
}
|
||||
if ((message = loadStringFromDITL(resMan, 2940, 2)) != NULL) {
|
||||
message->trim();
|
||||
debug(2, "_revertMessage: %s", message->c_str());
|
||||
_revertMessage = message;
|
||||
} else {
|
||||
_revertMessage = new Common::String("Revert to the last saved version?");
|
||||
}
|
||||
|
||||
// Load scenes
|
||||
resArray = resMan->getResIDArray(MKTAG('A','S','C','N'));
|
||||
debug(3, "Loading %d scenes", resArray.size());
|
||||
|
||||
for (iter = resArray.begin(); iter != resArray.end(); ++iter) {
|
||||
res = resMan->getResource(MKTAG('A','S','C','N'), *iter);
|
||||
Scene *scene = new Scene(resMan->getResName(MKTAG('A','S','C','N'), *iter), res);
|
||||
|
||||
res = resMan->getResource(MKTAG('A','C','O','D'), *iter);
|
||||
if (res != NULL)
|
||||
scene->_script = new Script(res, *iter, _engine);
|
||||
|
||||
res = resMan->getResource(MKTAG('A','T','X','T'), *iter);
|
||||
if (res != NULL) {
|
||||
scene->_textBounds = readRect(res);
|
||||
int fontType = res->readUint16BE();
|
||||
// WORKAROUND: Dune Eternity has a weird fontType ID so we override it to the correct one
|
||||
if (_name == "***DUNE ETERNITY*** ")
|
||||
fontType = 3;
|
||||
|
||||
int fontSize = res->readUint16BE();
|
||||
scene->_font = new Graphics::MacFont(fontType, fontSize, Graphics::kMacFontRegular);
|
||||
const Graphics::Font *fallback = FontMan.getFontByUsage(Graphics::FontManager::kConsoleFont);
|
||||
scene->_font->setFallback(fallback);
|
||||
|
||||
Common::String text;
|
||||
while (res->pos() < res->size()) {
|
||||
char c = res->readByte();
|
||||
if (c == 0x0d)
|
||||
c = '\n';
|
||||
text += c;
|
||||
}
|
||||
scene->_text = text;
|
||||
|
||||
delete res;
|
||||
}
|
||||
|
||||
scene->_resourceId = *iter;
|
||||
addScene(scene);
|
||||
}
|
||||
|
||||
// Load Objects
|
||||
resArray = resMan->getResIDArray(MKTAG('A','O','B','J'));
|
||||
debug(3, "Loading %d objects", resArray.size());
|
||||
|
||||
for (iter = resArray.begin(); iter != resArray.end(); ++iter) {
|
||||
res = resMan->getResource(MKTAG('A','O','B','J'), *iter);
|
||||
addObj(new Obj(resMan->getResName(MKTAG('A','O','B','J'), *iter), res, *iter));
|
||||
}
|
||||
|
||||
// Load Characters
|
||||
resArray = resMan->getResIDArray(MKTAG('A','C','H','R'));
|
||||
debug(3, "Loading %d characters", resArray.size());
|
||||
|
||||
for (iter = resArray.begin(); iter != resArray.end(); ++iter) {
|
||||
res = resMan->getResource(MKTAG('A','C','H','R'), *iter);
|
||||
Chr *chr = new Chr(resMan->getResName(MKTAG('A','C','H','R'), *iter), res);
|
||||
chr->_resourceId = *iter;
|
||||
addChr(chr);
|
||||
|
||||
if (chr->_playerCharacter) {
|
||||
if (_player)
|
||||
warning("loadWorld: Player is redefined");
|
||||
|
||||
_player = chr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_player) {
|
||||
warning("loadWorld: Player is not defined");
|
||||
|
||||
if (_chrs.empty()) {
|
||||
error("loadWorld: and I have no characters");
|
||||
}
|
||||
_player = _orderedChrs[0];
|
||||
}
|
||||
|
||||
|
||||
// Load Sounds
|
||||
resArray = resMan->getResIDArray(MKTAG('A','S','N','D'));
|
||||
debug(3, "Loading %d sounds", resArray.size());
|
||||
|
||||
for (iter = resArray.begin(); iter != resArray.end(); ++iter) {
|
||||
res = resMan->getResource(MKTAG('A','S','N','D'), *iter);
|
||||
addSound(new Sound(resMan->getResName(MKTAG('A','S','N','D'), *iter), res));
|
||||
}
|
||||
|
||||
if (!_soundLibrary1.empty()) {
|
||||
loadExternalSounds(_soundLibrary1);
|
||||
}
|
||||
if (!_soundLibrary2.empty()) {
|
||||
loadExternalSounds(_soundLibrary2);
|
||||
}
|
||||
|
||||
// Load Patterns
|
||||
res = resMan->getResource(MKTAG('P','A','T','#'), 900);
|
||||
if (res != NULL) {
|
||||
int count = res->readUint16BE();
|
||||
debug(3, "Loading %d patterns", count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
byte *pattern = (byte *)malloc(8);
|
||||
|
||||
res->read(pattern, 8);
|
||||
_patterns->push_back(pattern);
|
||||
}
|
||||
|
||||
delete res;
|
||||
} else {
|
||||
/* Enchanted Scepters did not use the PAT# resource for the textures. */
|
||||
res = resMan->getResource(MKTAG('C','O','D','E'), 1);
|
||||
if (res != NULL) {
|
||||
const char *magic = "\x92\x40\x15\x81\x20\x00\x4E\x75";
|
||||
|
||||
int cnt = 0;
|
||||
bool found = false;
|
||||
|
||||
while (!res->eos()) {
|
||||
byte b = res->readByte();
|
||||
|
||||
if (b == (byte)magic[cnt]) {
|
||||
cnt++;
|
||||
|
||||
if (cnt == 8) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
cnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
error("World::loadWorld(): Could not find Enhanced Scepters' patterns");
|
||||
|
||||
res->skip(8); // Skip 8 more bytes
|
||||
|
||||
debug(3, "Loading 29 patterns for Enhanced Scepters at %" PRId64, res->pos());
|
||||
|
||||
for (int i = 0; i < 29; i++) {
|
||||
byte *pattern = (byte *)malloc(8);
|
||||
|
||||
res->read(pattern, 8);
|
||||
_patterns->push_back(pattern);
|
||||
}
|
||||
}
|
||||
delete res;
|
||||
}
|
||||
|
||||
res = resMan->getResource(MKTAG('M','E','N','U'), 2001);
|
||||
if (res != NULL) {
|
||||
Common::StringArray *menu = Graphics::MacMenu::readMenuFromResource(res);
|
||||
_aboutMenuItemName.clear();
|
||||
Common::String string = menu->operator[](1);
|
||||
|
||||
for (uint i = 0; i < string.size() && string[i] != ';'; i++) // Read token
|
||||
_aboutMenuItemName += string[i];
|
||||
|
||||
debugC(1, kDebugLoading, "MENU: About: %s", toPrintable(_aboutMenuItemName).c_str());
|
||||
|
||||
delete menu;
|
||||
delete res;
|
||||
}
|
||||
res = resMan->getResource(MKTAG('M','E','N','U'), 2004);
|
||||
if (res != NULL) {
|
||||
Common::StringArray *menu = Graphics::MacMenu::readMenuFromResource(res);
|
||||
_commandsMenuName = menu->operator[](0);
|
||||
_commandsMenuDefault = menu->operator[](1);
|
||||
_commandsMenu = _commandsMenuDefault;
|
||||
debugC(1, kDebugLoading, "MENU: Commands name: %s", toPrintable(_commandsMenuName).c_str());
|
||||
debugC(1, kDebugLoading, "MENU: Commands menu: %s", toPrintable(_commandsMenu).c_str());
|
||||
|
||||
delete menu;
|
||||
delete res;
|
||||
}
|
||||
res = resMan->getResource(MKTAG('M','E','N','U'), 2005);
|
||||
if (res != NULL) {
|
||||
Common::StringArray *menu = Graphics::MacMenu::readMenuFromResource(res);
|
||||
_weaponsMenuName = menu->operator[](0);
|
||||
delete menu;
|
||||
delete res;
|
||||
|
||||
debugC(1, kDebugLoading, "MENU: Weapons name: %s", toPrintable(_weaponsMenuName).c_str());
|
||||
}
|
||||
// TODO: Read Apple menu and get the name of that menu item..
|
||||
|
||||
// store global info in state object for use with save/load actions
|
||||
//world.setCurrentState(initialState); // pass off the state object to the world
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void World::addSound(Sound *sound) {
|
||||
Common::String s = sound->_name;
|
||||
s.toLowercase();
|
||||
_sounds[s] = sound;
|
||||
_orderedSounds.push_back(sound);
|
||||
}
|
||||
|
||||
void World::loadExternalSounds(const Common::Path &fname) {
|
||||
Common::MacResManager resMan;
|
||||
if (!resMan.open(fname)) {
|
||||
warning("Cannot load sound file <%s>", fname.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Common::MacResIDArray resArray;
|
||||
Common::SeekableReadStream *res;
|
||||
Common::MacResIDArray::const_iterator iter;
|
||||
|
||||
resArray = resMan.getResIDArray(MKTAG('A','S','N','D'));
|
||||
for (iter = resArray.begin(); iter != resArray.end(); ++iter) {
|
||||
res = resMan.getResource(MKTAG('A','S','N','D'), *iter);
|
||||
|
||||
if (!res) {
|
||||
warning("Cannot load sound resource %d from file <%s>", *iter, fname.toString().c_str());
|
||||
continue;
|
||||
}
|
||||
addSound(new Sound(resMan.getResName(MKTAG('A','S','N','D'), *iter), res));
|
||||
}
|
||||
}
|
||||
|
||||
Common::String *World::loadStringFromDITL(Common::MacResManager *resMan, int resourceId, int itemIndex) {
|
||||
Common::SeekableReadStream *res = resMan->getResource(MKTAG('D','I','T','L'), resourceId);
|
||||
if (res) {
|
||||
int itemCount = res->readSint16BE();
|
||||
for (int i = 0; i <= itemCount; i++) {
|
||||
// int placeholder; short rect[4]; byte flags; pstring str;
|
||||
res->skip(13);
|
||||
Common::String message = res->readPascalString();
|
||||
if (i == itemIndex) {
|
||||
Common::String *msg = new Common::String(message);
|
||||
delete res;
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
delete res;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool invComparator(const Obj *l, const Obj *r) {
|
||||
return l->_index < r->_index;
|
||||
}
|
||||
|
||||
void World::move(Obj *obj, Chr *chr) {
|
||||
if (obj == NULL)
|
||||
return;
|
||||
|
||||
Designed *from = obj->removeFromCharOrScene();
|
||||
obj->setCurrentOwner(chr);
|
||||
chr->_inventory.push_back(obj);
|
||||
|
||||
Common::sort(chr->_inventory.begin(), chr->_inventory.end(), invComparator);
|
||||
|
||||
_engine->onMove(obj, from, chr);
|
||||
}
|
||||
|
||||
static bool objComparator(const Obj *o1, const Obj *o2) {
|
||||
bool o1Immobile = (o1->_type == Obj::IMMOBILE_OBJECT);
|
||||
bool o2Immobile = (o2->_type == Obj::IMMOBILE_OBJECT);
|
||||
if (o1Immobile == o2Immobile) {
|
||||
return o1->_index < o2->_index;
|
||||
}
|
||||
return o1Immobile;
|
||||
}
|
||||
|
||||
void World::move(Obj *obj, Scene *scene, bool skipSort) {
|
||||
if (obj == NULL)
|
||||
return;
|
||||
|
||||
Designed *from = obj->removeFromCharOrScene();
|
||||
obj->setCurrentScene(scene);
|
||||
scene->_objs.push_back(obj);
|
||||
|
||||
if (!skipSort)
|
||||
Common::sort(scene->_objs.begin(), scene->_objs.end(), objComparator);
|
||||
|
||||
_engine->onMove(obj, from, scene);
|
||||
}
|
||||
|
||||
static bool chrComparator(const Chr *l, const Chr *r) {
|
||||
return l->_index < r->_index;
|
||||
}
|
||||
|
||||
void World::move(Chr *chr, Scene *scene, bool skipSort) {
|
||||
if (chr == NULL)
|
||||
return;
|
||||
Scene *from = chr->_currentScene;
|
||||
if (from == scene)
|
||||
return;
|
||||
if (from != NULL)
|
||||
from->_chrs.remove(chr);
|
||||
scene->_chrs.push_back(chr);
|
||||
|
||||
if (!skipSort)
|
||||
Common::sort(scene->_chrs.begin(), scene->_chrs.end(), chrComparator);
|
||||
|
||||
if (scene == _storageScene) {
|
||||
chr->resetState();
|
||||
} else if (chr->_playerCharacter) {
|
||||
scene->_visited = true;
|
||||
_player->_context._visits++;
|
||||
}
|
||||
chr->_currentScene = scene;
|
||||
|
||||
_engine->onMove(chr, from, scene);
|
||||
}
|
||||
|
||||
Scene *World::getRandomScene() {
|
||||
// Not including storage:
|
||||
return _orderedScenes[1 + _engine->_rnd->getRandomNumber(_orderedScenes.size() - 2)];
|
||||
}
|
||||
|
||||
Scene *World::getSceneAt(int x, int y) {
|
||||
for (uint i = 0; i < _orderedScenes.size(); i++) {
|
||||
Scene *scene = _orderedScenes[i];
|
||||
|
||||
if (scene != _storageScene && scene->_worldX == x && scene->_worldY == y) {
|
||||
return scene;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const int directionsX[] = { 0, 0, 1, -1 };
|
||||
static const int directionsY[] = { -1, 1, 0, 0 };
|
||||
|
||||
bool World::scenesAreConnected(Scene *scene1, Scene *scene2) {
|
||||
if (!scene1 || !scene2)
|
||||
return false;
|
||||
|
||||
int x = scene2->_worldX;
|
||||
int y = scene2->_worldY;
|
||||
|
||||
for (int dir = 0; dir < 4; dir++)
|
||||
if (!scene2->_blocked[dir])
|
||||
if (getSceneAt(x + directionsX[dir], y + directionsY[dir]) == scene1)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *World::getAboutMenuItemName() {
|
||||
static char menu[256];
|
||||
|
||||
*menu = '\0';
|
||||
|
||||
if (_aboutMenuItemName.empty()) {
|
||||
Common::sprintf_s(menu, "About %s...", _name.c_str());
|
||||
} else { // Replace '@' with name
|
||||
const char *str = _aboutMenuItemName.c_str();
|
||||
const char *pos = strchr(str, '@');
|
||||
if (pos) {
|
||||
strncat(menu, str, (pos - str));
|
||||
strncat(menu, _name.c_str(), 255);
|
||||
strncat(menu, pos + 1, 255);
|
||||
} else {
|
||||
Common::strlcpy(menu, _aboutMenuItemName.c_str(), 256);
|
||||
}
|
||||
}
|
||||
|
||||
debugC(1, kDebugLoading, "MENU: About cleansed: %s", Common::toPrintable(menu).c_str());
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
} // End of namespace Wage
|
||||
141
engines/wage/world.h
Normal file
141
engines/wage/world.h
Normal file
@@ -0,0 +1,141 @@
|
||||
/* 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/>.
|
||||
*
|
||||
* MIT License:
|
||||
*
|
||||
* Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WAGE_WORLD_H
|
||||
#define WAGE_WORLD_H
|
||||
|
||||
#include "wage/entities.h"
|
||||
#include "graphics/macgui/macwindowmanager.h"
|
||||
|
||||
namespace Wage {
|
||||
|
||||
// Import the enum definitions
|
||||
using Graphics::MacPatterns;
|
||||
|
||||
class Script;
|
||||
class Sound;
|
||||
|
||||
class World {
|
||||
public:
|
||||
World(WageEngine *engine);
|
||||
~World();
|
||||
|
||||
bool loadWorld(Common::MacResManager *resMan);
|
||||
void loadExternalSounds(const Common::Path &fname);
|
||||
Common::String *loadStringFromDITL(Common::MacResManager *resMan, int resourceId, int itemIndex);
|
||||
void move(Obj *obj, Chr *chr);
|
||||
void move(Obj *obj, Scene *scene, bool skipSort = false);
|
||||
void move(Chr *chr, Scene *scene, bool skipSort = false);
|
||||
Scene *getRandomScene();
|
||||
Scene *getSceneAt(int x, int y);
|
||||
bool scenesAreConnected(Scene *scene1, Scene *scene2);
|
||||
const char *getAboutMenuItemName();
|
||||
|
||||
WageEngine *_engine;
|
||||
|
||||
Common::String _name;
|
||||
Common::String _aboutMessage;
|
||||
Common::Path _soundLibrary1;
|
||||
Common::Path _soundLibrary2;
|
||||
|
||||
bool _weaponMenuDisabled;
|
||||
Script *_globalScript;
|
||||
Common::HashMap<Common::String, Scene *> _scenes;
|
||||
Common::HashMap<Common::String, Obj *> _objs;
|
||||
Common::HashMap<Common::String, Chr *> _chrs;
|
||||
Common::HashMap<Common::String, Sound *> _sounds;
|
||||
Common::Array<Scene *> _orderedScenes;
|
||||
ObjArray _orderedObjs;
|
||||
ChrArray _orderedChrs;
|
||||
Common::Array<Sound *> _orderedSounds;
|
||||
Graphics::MacPatterns *_patterns;
|
||||
Scene *_storageScene;
|
||||
Chr *_player;
|
||||
int _signature;
|
||||
//List<MoveListener> moveListeners;
|
||||
|
||||
Common::String *_gameOverMessage;
|
||||
Common::String *_saveBeforeQuitMessage;
|
||||
Common::String *_saveBeforeCloseMessage;
|
||||
Common::String *_revertMessage;
|
||||
|
||||
Common::String _aboutMenuItemName;
|
||||
Common::String _commandsMenuName;
|
||||
Common::String _commandsMenu;
|
||||
Common::String _commandsMenuDefault;
|
||||
Common::String _weaponsMenuName;
|
||||
|
||||
void addScene(Scene *room) {
|
||||
if (!room->_name.empty()) {
|
||||
Common::String s = room->_name;
|
||||
s.toLowercase();
|
||||
_scenes[s] = room;
|
||||
}
|
||||
_orderedScenes.push_back(room);
|
||||
}
|
||||
|
||||
void addObj(Obj *obj) {
|
||||
Common::String s = obj->_name;
|
||||
s.toLowercase();
|
||||
_objs[s] = obj;
|
||||
obj->_index = _orderedObjs.size();
|
||||
_orderedObjs.push_back(obj);
|
||||
}
|
||||
|
||||
void addChr(Chr *chr) {
|
||||
Common::String s = chr->_name;
|
||||
s.toLowercase();
|
||||
_chrs[s] = chr;
|
||||
chr->_index = _orderedChrs.size();
|
||||
_orderedChrs.push_back(chr);
|
||||
}
|
||||
|
||||
void addSound(Sound *sound);
|
||||
};
|
||||
|
||||
} // End of namespace Wage
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user