Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

3
engines/ags/POTFILES Normal file
View File

@@ -0,0 +1,3 @@
engines/ags/ags.cpp
engines/ags/dialogs.cpp
engines/ags/engine/ac/listbox.cpp

View File

@@ -0,0 +1,170 @@
/* 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 AGS_ACHIEVEMENTS_H
#define AGS_ACHIEVEMENTS_H
#include "engines/achievements.h"
namespace AGS {
static const Common::AchievementDescriptionList achievementDescriptionList[] = {
{"44thejail", Common::STEAM_ACHIEVEMENTS, "2702680"},
{"adateinthepark", Common::STEAM_ACHIEVEMENTS, "468530"},
{"absent", Common::STEAM_ACHIEVEMENTS, "398450"},
{"alemmo", Common::STEAM_ACHIEVEMENTS, "296850"},
{"alum", Common::STEAM_ACHIEVEMENTS, "338420"},
{"apotheosis", Common::STEAM_ACHIEVEMENTS, "364350"},
{"ashinaredwitch", Common::STEAM_ACHIEVEMENTS, "1259140"},
{"astroloco", Common::STEAM_ACHIEVEMENTS, "357490"},
{"atotkdeluxe", Common::STEAM_ACHIEVEMENTS, "603870"},
{"azazelxmas", Common::STEAM_ACHIEVEMENTS, "2163620"},
{"beer", Common::STEAM_ACHIEVEMENTS, "782280"},
{"beyondowlsgard", Common::STEAM_ACHIEVEMENTS, "1709730"},
{"beyondowlsgard", Common::GALAXY_ACHIEVEMENTS, "1086301307"},
{"bizarreearthquake", Common::STEAM_ACHIEVEMENTS, "1709730"},
{"blackwell1", Common::GALAXY_ACHIEVEMENTS, "1207662883"},
{"blackwell1", Common::STEAM_ACHIEVEMENTS, "80330"},
{"blackwell2", Common::GALAXY_ACHIEVEMENTS, "1207662893"},
{"blackwell2", Common::STEAM_ACHIEVEMENTS, "80340"},
{"blackwell3", Common::GALAXY_ACHIEVEMENTS, "1207662903"},
{"blackwell3", Common::STEAM_ACHIEVEMENTS, "80350"},
{"blackwell4", Common::GALAXY_ACHIEVEMENTS, "1207662913"},
{"blackwell4", Common::STEAM_ACHIEVEMENTS, "80360"},
{"blackwell5", Common::GALAXY_ACHIEVEMENTS, "1207664393"},
{"blackwell5", Common::STEAM_ACHIEVEMENTS, "236930"},
{"brownieadv1", Common::STEAM_ACHIEVEMENTS, "2581560"},
{"brownieadv1", Common::GALAXY_ACHIEVEMENTS, "1855964876"},
{"captaindisastermoon", Common::STEAM_ACHIEVEMENTS, "1305500"},
{"castleagony", Common::STEAM_ACHIEVEMENTS, "827830"},
{"charnelhousetrilogy", Common::STEAM_ACHIEVEMENTS, "288930"},
{"chronicleofinnsmouth", Common::STEAM_ACHIEVEMENTS, "420180"},
{"crimsondiamond", Common::STEAM_ACHIEVEMENTS, "1098770"},
{"crimsondiamond", Common::GALAXY_ACHIEVEMENTS, "1602588462"},
{"crystalshardadventurebundle", Common::STEAM_ACHIEVEMENTS, "551840"},
{"detectivebhdino", Common::STEAM_ACHIEVEMENTS, "1343390"},
{"detectivegallo", Common::GALAXY_ACHIEVEMENTS, "1745746005"},
{"detectivegallo", Common::STEAM_ACHIEVEMENTS, "556060"},
{"docapocalypse", Common::STEAM_ACHIEVEMENTS, "509920"},
{"downfall2016", Common::STEAM_ACHIEVEMENTS, "364390"},
{"dreamswitchhouse", Common::STEAM_ACHIEVEMENTS, "1902850"},
{"dreamswitchhouse", Common::GALAXY_ACHIEVEMENTS, "1123332294"},
{"dustbowl", Common::STEAM_ACHIEVEMENTS, "367110"},
{"englishhaunting", Common::STEAM_ACHIEVEMENTS, "2474030"},
{"excavationhb", Common::STEAM_ACHIEVEMENTS, "1182310"},
{"excavationhb", Common::GALAXY_ACHIEVEMENTS, "1790032718"},
{"falconcity", Common::STEAM_ACHIEVEMENTS, "1270590"},
{"feriadarles", Common::STEAM_ACHIEVEMENTS, "1181570"},
{"footballgame", Common::STEAM_ACHIEVEMENTS, "654550"},
{"geminirue", Common::GALAXY_ACHIEVEMENTS, "1207659073"},
{"geminirue", Common::STEAM_ACHIEVEMENTS, "80310"},
{"gnrblex", Common::STEAM_ACHIEVEMENTS, "1060560"},
{"goldenwake", Common::STEAM_ACHIEVEMENTS, "307570"},
{"graceward", Common::STEAM_ACHIEVEMENTS, "2217060"},
{"grandmabadass", Common::STEAM_ACHIEVEMENTS, "1606640"},
{"guardduty", Common::GALAXY_ACHIEVEMENTS, "1455980545"},
{"guardduty", Common::STEAM_ACHIEVEMENTS, "872750"},
{"herdiscoming", Common::STEAM_ACHIEVEMENTS, "1028740"},
{"heroinesquest", Common::STEAM_ACHIEVEMENTS, "283880"},
{"insectophobiaep1", Common::STEAM_ACHIEVEMENTS, "655250"},
{"ioawn4t", Common::STEAM_ACHIEVEMENTS, "1603980"},
{"justignorethem", Common::STEAM_ACHIEVEMENTS, "561770"},
{"justignorethembrea1", Common::STEAM_ACHIEVEMENTS, "1900280"},
{"kathyrain", Common::GALAXY_ACHIEVEMENTS, "1460710709"},
{"kathyrain", Common::STEAM_ACHIEVEMENTS, "370910"},
{"killyourself", Common::STEAM_ACHIEVEMENTS, "1251910"},
{"lamplightcity", Common::STEAM_ACHIEVEMENTS, "761460"},
{"lancelothangover", Common::STEAM_ACHIEVEMENTS, "917380"},
{"legendofhand", Common::STEAM_ACHIEVEMENTS, "595560"},
{"legendofskye", Common::STEAM_ACHIEVEMENTS, "2314850"},
{"legendofskye", Common::GALAXY_ACHIEVEMENTS, "1562711791"},
{"mage", Common::GALAXY_ACHIEVEMENTS, "1469845437"},
{"mage", Common::STEAM_ACHIEVEMENTS, "270610"},
{"magretfdb", Common::STEAM_ACHIEVEMENTS, "2661780"},
{"metaldead", Common::STEAM_ACHIEVEMENTS, "302690"},
{"metaphobia", Common::STEAM_ACHIEVEMENTS, "1220930"},
{"mountainsofmadness", Common::STEAM_ACHIEVEMENTS, "962700"},
{"mybigsister", Common::STEAM_ACHIEVEMENTS, "733300"},
{"mybigsisterrm", Common::STEAM_ACHIEVEMENTS, "2118540"},
{"nellycootalot-hd", Common::STEAM_ACHIEVEMENTS, "763030"},
{"neofeud", Common::STEAM_ACHIEVEMENTS, "673850"},
{"nightmareframes", Common::STEAM_ACHIEVEMENTS, "1650590"},
{"oldskies", Common::STEAM_ACHIEVEMENTS, "1346360"},
{"oldskies", Common::GALAXY_ACHIEVEMENTS, "1391436028"},
{"oott", Common::GALAXY_ACHIEVEMENTS, "1444830704"},
{"oott", Common::STEAM_ACHIEVEMENTS, "425600"},
{"perfecttides", Common::STEAM_ACHIEVEMENTS, "1172800"},
{"perfecttides", Common::GALAXY_ACHIEVEMENTS, "1321012195"},
{"personalrocket", Common::STEAM_ACHIEVEMENTS, "1497290"},
{"phantomfellows", Common::STEAM_ACHIEVEMENTS, "1581490"},
{"phantomfellows", Common::GALAXY_ACHIEVEMENTS, "2134608058"},
{"primordia", Common::GALAXY_ACHIEVEMENTS, "1207659144"},
{"primordia", Common::STEAM_ACHIEVEMENTS, "227000"},
{"projectorface", Common::STEAM_ACHIEVEMENTS, "431850"},
{"qfi", Common::STEAM_ACHIEVEMENTS, "264560"},
{"redbow", Common::STEAM_ACHIEVEMENTS, "710600"},
{"resonance", Common::GALAXY_ACHIEVEMENTS, "1207659043"},
{"resonance", Common::STEAM_ACHIEVEMENTS, "212050"},
{"richardandalice", Common::STEAM_ACHIEVEMENTS, "279260"},
{"rnrneverdies", Common::STEAM_ACHIEVEMENTS, "1644080"},
{"roguestate", Common::STEAM_ACHIEVEMENTS, "396090"},
{"rosewater", Common::STEAM_ACHIEVEMENTS, "1226670"},
{"rosewater", Common::GALAXY_ACHIEVEMENTS, "1657623040"},
{"samaritan", Common::STEAM_ACHIEVEMENTS, "283180"},
{"shardlight", Common::GALAXY_ACHIEVEMENTS, "1454499527"},
{"shardlight", Common::STEAM_ACHIEVEMENTS, "336130"},
{"shivahkosher", Common::GALAXY_ACHIEVEMENTS, "1207660263"},
{"shivahkosher", Common::STEAM_ACHIEVEMENTS, "252370"},
{"sisyphus", Common::STEAM_ACHIEVEMENTS, "517910"},
{"smallsister", Common::STEAM_ACHIEVEMENTS, "904600"},
{"snowproblem", Common::STEAM_ACHIEVEMENTS, "2097090"},
{"songanimals", Common::STEAM_ACHIEVEMENTS, "1036200"},
{"stayingalive", Common::STEAM_ACHIEVEMENTS, "1285960"},
{"stellarmessep1", Common::STEAM_ACHIEVEMENTS, "1507530"},
{"strangeland", Common::GALAXY_ACHIEVEMENTS, "1392294208"},
{"strangeland", Common::STEAM_ACHIEVEMENTS, "1369520"},
{"sumatra", Common::GALAXY_ACHIEVEMENTS, "1489938431"},
{"sumatra", Common::STEAM_ACHIEVEMENTS, "610900"},
{"tales", Common::STEAM_ACHIEVEMENTS, "416250"},
{"technobabylon", Common::GALAXY_ACHIEVEMENTS, "1430234218"},
{"technobabylon", Common::STEAM_ACHIEVEMENTS, "307580"},
{"theantidote", Common::STEAM_ACHIEVEMENTS, "1026080"},
{"thecastle", Common::STEAM_ACHIEVEMENTS, "1133950"},
{"thecatlady", Common::STEAM_ACHIEVEMENTS, "253110"},
{"thesecretofhuttongrammarschoolvga", Common::STEAM_ACHIEVEMENTS, "1196520"},
{"thesecretsofjesus", Common::STEAM_ACHIEVEMENTS, "1142230"},
{"theterribleoldman", Common::STEAM_ACHIEVEMENTS, "1147030"},
{"unavowed", Common::GALAXY_ACHIEVEMENTS, "1637485057"},
{"unavowed", Common::STEAM_ACHIEVEMENTS, "336140"},
{"unlikelyprometheus", Common::STEAM_ACHIEVEMENTS, "1834750"},
{"untilihaveyou", Common::STEAM_ACHIEVEMENTS, "439310"},
{"whispersofamachine", Common::GALAXY_ACHIEVEMENTS, "1845001352"},
{"whispersofamachine", Common::STEAM_ACHIEVEMENTS, "631570"},
{"wolfterritory", Common::STEAM_ACHIEVEMENTS, "1355910"},
{"zniwadventure", Common::GALAXY_ACHIEVEMENTS, "1664228813"},
{"zniwadventure", Common::STEAM_ACHIEVEMENTS, "904750"},
ACHIEVEMENT_DESC_TABLE_END_MARKER
};
} // End of namespace AGS
#endif /* AGS_ACHIEVEMENTS_H */

399
engines/ags/ags.cpp Normal file
View File

@@ -0,0 +1,399 @@
/* 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 "ags/ags.h"
#include "ags/detection.h"
#include "ags/events.h"
#include "ags/game_scanner.h"
#include "ags/music.h"
#include "ags/console.h"
#include "common/scummsys.h"
#include "common/config-manager.h"
#include "common/compression/clickteam.h"
#include "common/debug-channels.h"
#include "common/events.h"
#include "common/file.h"
#include "common/tokenizer.h"
#include "common/util.h"
#include "engines/util.h"
#include "ags/shared/core/platform.h"
#include "common/std/set.h"
#include "ags/shared/ac/common.h"
#include "ags/engine/ac/game.h"
#include "ags/globals.h"
#include "ags/engine/ac/game_setup.h"
#include "ags/engine/ac/game_state.h"
#include "ags/engine/ac/room.h"
#include "ags/shared/core/def_version.h"
#include "ags/engine/debugging/debugger.h"
#include "ags/engine/debugging/debug_log.h"
#include "ags/shared/debugging/out.h"
#include "ags/engine/game/savegame.h"
#include "ags/engine/game/savegame_components.h"
#include "ags/engine/main/config.h"
#include "ags/engine/main/engine.h"
#include "ags/engine/main/main.h"
#include "ags/engine/main/quit.h"
#include "ags/engine/platform/base/ags_platform_driver.h"
#include "ags/engine/script/cc_instance.h"
#include "ags/engine/script/script.h"
#include "ags/engine/ac/route_finder.h"
#include "ags/shared/core/asset_manager.h"
#include "ags/shared/util/directory.h"
#include "ags/shared/script/cc_common.h"
#ifdef ENABLE_AGS_TESTS
#include "ags/tests/test_all.h"
#endif
// Include translation.h last as some AGS classes have member such as _sc, which clash with
// macro defined in translation.h.
#include "common/translation.h"
namespace AGS {
AGSEngine *g_vm;
AGSEngine::AGSEngine(OSystem *syst, const AGSGameDescription *gameDesc) : Engine(syst),
_gameDescription(gameDesc), _randomSource("AGS"), _events(nullptr), _music(nullptr),
_gfxDriver(nullptr), _globals(nullptr), _forceTextAA(false) {
g_vm = this;
AGS3::script_commands_init();
AGS3::Engine::SavegameComponents::component_handlers_init();
_events = new EventsManager();
_globals = new ::AGS3::Globals();
Common::String forceAA;
if (ConfMan.getActiveDomain()->tryGetVal("force_text_aa", forceAA))
Common::parseBool(forceAA, _forceTextAA);
// WORKAROUND: Certain games need to force AA to render the text correctly
if (_gameDescription->features & GAMEFLAG_FORCE_AA)
_forceTextAA = true;
}
AGSEngine::~AGSEngine() {
if (_globals && _G(proper_exit) == 0) {
_G(platform)->DisplayAlert("Error: the program has exited without requesting it.\n"
"Program pointer: %+03d (write this number down), engine version %s\n"
"If you see a list of numbers above, please write them down and contact\n"
"developers. Otherwise, note down any other information displayed.",
_G(our_eip), _G(EngineVersion).LongString.GetCStr());
}
delete _events;
delete _music;
delete _globals;
AGS3::Engine::SavegameComponents::component_handlers_free();
AGS3::script_commands_free();
}
uint32 AGSEngine::getFeatures() const {
return _gameDescription->desc.flags;
}
static const PluginVersion AGSTEAM_WADJETEYE[] = { { "agsteam", kWadjetEye }, { nullptr, 0 } };
static const PluginVersion AGS_FLASHLIGHT[] = { { "agsflashlight", 0 }, { nullptr, 0 } };
static const PluginVersion AGSSPRITEFONT_CLIFFTOP[] = { { "agsspritefont", kClifftopGames }, { "agsplugin.spritefont", kClifftopGames }, { nullptr, 0 } };
static const PluginVersion *const PLUGIN_VERSIONS[] = {
nullptr,
AGSTEAM_WADJETEYE,
AGS_FLASHLIGHT,
AGSSPRITEFONT_CLIFFTOP
};
const PluginVersion *AGSEngine::getNeededPlugins() const {
uint index = (_gameDescription->features & GAMEFLAG_PLUGINS_MASK);
if (index >= ARRAYSIZE(PLUGIN_VERSIONS))
return nullptr;
else
return PLUGIN_VERSIONS[index];
}
Common::String AGSEngine::getGameId() const {
return _gameDescription->desc.gameId;
}
Common::Error AGSEngine::run() {
#ifdef DETECTION_STATIC
// The game scanner is not available when detection is dynamic
if (debugChannelSet(-1, kDebugScan)) {
// Scan the given folder and subfolders for unknown games
AGS3::GameScanner scanner;
scanner.scan(ConfMan.getPath("path"));
return Common::kNoError;
}
#endif
if (isUnsupportedPre25()) {
GUIErrorMessage(_("The selected game uses a pre-2.5 version of the AGS engine, which is not supported."));
return Common::kNoError;
}
if (isUnsupportedAGS4()) {
GUIErrorMessage(_("The selected game uses version 4 of the AGS engine, which is not supported."));
return Common::kNoError;
}
if (is64BitGame()) {
// If the game file was opened and the engine started, but the
// size is -1, then it must be a game like Strangeland where
// the data file is > 2Gb
GUIErrorMessage(_("The selected game has a data file greater than 2Gb, "
"which isn't supported by your version of ScummVM yet."));
return Common::kNoError;
}
if (debugChannelSet(-1, kDebugScript))
AGS3::ccSetOption(SCOPT_DEBUGRUN, 1);
#ifdef ENABLE_AGS_TESTS
AGS3::Test_DoAllTests();
return Common::kNoError;
#endif
setDebugger(new AGSConsole(this));
Common::String filename(_gameDescription->desc.filesDescriptions[0].fileName);
Common::StringTokenizer tok(filename, ":");
Common::String type = tok.nextToken();
if (type.equals("clk")) {
Common::File *f = new Common::File();
f->open(tok.nextToken().c_str());
SearchMan.add("installer", Common::ClickteamInstaller::open(f, DisposeAfterUse::YES));
filename = tok.nextToken();
}
const char *ARGV[] = { "scummvm.exe", filename.c_str() };
const int ARGC = 2;
AGS3::main_init(ARGC, ARGV);
_G(debug_flags) = 0;
if (ConfMan.hasKey("display_fps"))
_G(display_fps) = ConfMan.getBool("display_fps") ? AGS3::kFPS_Forced : AGS3::kFPS_Hide;
Common::String saveOverrideOption;
bool saveOverride = false;
ConfMan.getActiveDomain()->tryGetVal("save_override", saveOverrideOption);
if (!saveOverrideOption.empty())
parseBool(saveOverrideOption, saveOverride);
_G(noScummAutosave) = (Common::checkGameGUIOption(GAMEOPTION_NO_AUTOSAVE, ConfMan.get("guioptions"))) && !saveOverride;
_G(noScummSaveLoad) = (Common::checkGameGUIOption(GAMEOPTION_NO_SAVELOAD, ConfMan.get("guioptions"))) && !saveOverride;
if (_G(noScummSaveLoad))
_G(noScummAutosave) = true;
_G(saveThumbnail) = !(Common::checkGameGUIOption(GAMEOPTION_NO_SAVE_THUMBNAIL, ConfMan.get("guioptions")));
AGS3::ConfigTree startup_opts;
int res = AGS3::main_process_cmdline(startup_opts, ARGC, ARGV);
if (res != 0)
return Common::kUnknownError;
if (_G(justDisplayVersion)) {
_G(platform)->WriteStdOut(AGS3::get_engine_string().GetCStr());
return Common::kNoError;
}
if (_G(justDisplayHelp)) {
AGS3::main_print_help();
return Common::kNoError;
}
if (!_G(justTellInfo))
_G(platform)->SetGUIMode(true);
AGS3::init_debug(startup_opts, _G(justTellInfo));
AGS3::AGS::Shared::Debug::Printf("%s", AGS3::get_engine_string().GetCStr());
AGS3::main_set_gamedir(ARGC, ARGV);
// Update shell associations and exit
if (_G(debug_flags) & DBG_REGONLY)
return Common::kNoError;
_music = new Music();
_G(loadSaveGameOnStartup) = ConfMan.getInt("save_slot");
syncSoundSettings();
AGS3::initialize_engine(startup_opts);
// Do shutdown stuff
::AGS3::quit_free();
return Common::kNoError;
}
SaveStateList AGSEngine::listSaves() const {
return getMetaEngine()->listSaves(_targetName.c_str());
}
bool AGSEngine::getPixelFormat(int depth, Graphics::PixelFormat &format) const {
if (depth == 8) {
format = Graphics::PixelFormat::createFormatCLUT8();
return true;
}
Common::List<Graphics::PixelFormat> supportedFormatsList = g_system->getSupportedFormats();
// Internally the engine uses the following format (see create_bitmap_ex())
// 16 bit: RGB565 / Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)
// 32 bit: ARGB8888 / Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)
// If supported by the backend, use it so that we can do direct rendering.
Graphics::PixelFormat engine_format = depth == 16 ? Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0) : Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24);
if (Common::find(supportedFormatsList.begin(), supportedFormatsList.end(), engine_format) != supportedFormatsList.end()) {
format = engine_format;
return true;
}
// For 32 bit color depth, the next best formats for which there is some optimization in the
// engine are RGBA8888 or ABGR8888 (see ScummVMRendererGraphicsDriver::Present()).
if (depth == 32) {
Graphics::PixelFormat preferred_format1 = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
Graphics::PixelFormat preferred_format2 = Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24);
for (Common::List<Graphics::PixelFormat>::iterator it =
supportedFormatsList.begin(); it != supportedFormatsList.end(); ++it) {
if (*it == preferred_format1 || *it == preferred_format2) {
format = *it;
return true;
}
}
}
// Otherwise prefer another format with the same color depth
for (Common::List<Graphics::PixelFormat>::iterator it =
supportedFormatsList.begin(); it != supportedFormatsList.end(); ++it) {
if (it->bpp() == depth) {
format = *it;
return true;
}
}
// Allow using 16 bit <-> 32 bit conversions by using the preferred graphics mode
if (!supportedFormatsList.empty()) {
format = supportedFormatsList.front();
return true;
}
return false;
}
void AGSEngine::setGraphicsMode(size_t w, size_t h, int colorDepth) {
Common::List<Graphics::PixelFormat> supportedFormatsList = g_system->getSupportedFormats();
Graphics::PixelFormat format;
if (!getPixelFormat(colorDepth, format))
error("Unsupported color depth %d", colorDepth);
initGraphics(w, h, &format);
}
bool AGSEngine::isUnsupportedPre25() const {
return _gameDescription->desc.extra &&
Common::String(_gameDescription->desc.extra).contains("Pre 2.5");
}
bool AGSEngine::isUnsupportedAGS4() const {
return _gameDescription->desc.extra &&
Common::String(_gameDescription->desc.extra).contains("AGS 4");
}
bool AGSEngine::is64BitGame() const {
Common::File f;
// TODO: There are no more entries in the tables with -1 filesize, so this check doesn't really do anything.
// Maybe find a more reliable way to detect if the system can't handle these files?
if (_gameDescription->desc.filesDescriptions[0].fileName[0] == '\0')
return false;
else
return f.open(_gameDescription->desc.filesDescriptions[0].fileName) && f.size() == -1;
}
Common::FSNode AGSEngine::getGameFolder() {
return Common::FSNode(ConfMan.getPath("path"));
}
bool AGSEngine::canLoadGameStateCurrently(Common::U32String *msg) {
if (msg) {
if (ConfMan.get("gameid") == "strangeland") {
*msg = _("This game does not support loading from the menu. Use in-game interface");
return false;
}
if (_G(noScummSaveLoad))
*msg = _("To preserve the original experience, this game should be loaded using the in-game interface.\nYou can, however, override this setting in Game Options.");
}
return !_GP(thisroom).Options.SaveLoadDisabled &&
!_G(inside_script) && !_GP(play).fast_forward && !_G(no_blocking_functions) &&
!_G(noScummSaveLoad);
}
bool AGSEngine::canSaveGameStateCurrently(Common::U32String *msg) {
if (msg) {
if (ConfMan.get("gameid") == "strangeland") {
*msg = _("This game does not support saving from the menu. Use in-game interface");
return false;
}
if (_G(noScummSaveLoad))
*msg = _("To preserve the original experience, this game should be saved using the in-game interface.\nYou can, however, override this setting in Game Options.");
}
return !_GP(thisroom).Options.SaveLoadDisabled &&
!_G(inside_script) && !_GP(play).fast_forward && !_G(no_blocking_functions) &&
!_G(noScummSaveLoad);
}
Common::Error AGSEngine::loadGameState(int slot) {
(void)AGS3::try_restore_save(slot);
return Common::kNoError;
}
Common::Error AGSEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
(void)AGS3::save_game(slot, desc.c_str());
return Common::kNoError;
}
int AGSEngine::getAutosaveSlot() const {
if (!g_engine || !_G(noScummAutosave))
return 0;
else
return -1;
}
void AGSEngine::GUIError(const Common::String &msg) {
GUIErrorMessage(msg);
}
void AGSEngine::syncSoundSettings() {
// Digital audio
Engine::syncSoundSettings();
// MIDI
_music->syncVolume();
}
} // namespace AGS

195
engines/ags/ags.h Normal file
View File

@@ -0,0 +1,195 @@
/* 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 AGS_AGS_H
#define AGS_AGS_H
#include "common/scummsys.h"
#include "common/system.h"
#include "common/error.h"
#include "common/fs.h"
#include "common/random.h"
#include "common/hash-str.h"
#include "common/util.h"
#include "engines/engine.h"
#include "engines/savestate.h"
#include "graphics/surface.h"
#include "ags/detection.h"
#include "ags/shared/gfx/bitmap.h"
#include "ags/lib/allegro/system.h"
namespace AGS3 {
class Globals;
}
namespace AGS {
/**
* @defgroup agsengine AGS Engine
* @brief Engine to run Adventure Game Studio games.
*/
/* Synced up to upstream: ---
* ----
*/
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 200
struct AGSGameDescription;
struct PluginVersion;
class EventsManager;
class Music;
struct PluginVersion {
const char *_plugin;
int _version;
};
enum AGSSteamVersion { kAGSteam = 0, kWadjetEye = 1 };
enum AGSSpriteFontVersion { kAGSSpriteFont = 0, kClifftopGames = 1 };
class AGSEngine : public Engine {
private:
const AGSGameDescription *_gameDescription;
Common::RandomSource _randomSource;
public:
EventsManager *_events;
Music *_music;
::AGS3::GFX_DRIVER *_gfxDriver;
::AGS3::Globals *_globals;
bool _forceTextAA;
protected:
// Engine APIs
Common::Error run() override;
public:
AGSEngine(OSystem *syst, const AGSGameDescription *gameDesc);
~AGSEngine() override;
void GUIError(const Common::String &msg);
void set_window_title(const char *str) {
// No implementation
}
uint32 getFeatures() const;
const PluginVersion *getNeededPlugins() const;
/**
* Returns the game Id
*/
Common::String getGameId() const;
/**
* Returns the current list of savegames
*/
SaveStateList listSaves() const;
/**
* Gets a random number
*/
uint32 getRandomNumber(uint maxNum) {
return _randomSource.getRandomNumber(maxNum);
}
/**
* Sets the random number seed
*/
void setRandomNumberSeed(uint32 seed) {
_randomSource.setSeed(seed);
}
/**
* Returns a pixel format for the given color depth.
*/
bool getPixelFormat(int depth, Graphics::PixelFormat &format) const;
/**
* Sets up the graphics mode
*/
void setGraphicsMode(size_t w, size_t h, int depth);
bool hasFeature(EngineFeature f) const override {
return
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime) ||
(f == kSupportsReturnToLauncher);
};
/**
* Returns true if the selected game is an unsupported one
* earlier than version 2.5
*/
bool isUnsupportedPre25() const;
/**
* Returns true if the selected game is built with AGS4
*/
bool isUnsupportedAGS4() const;
/*
* Returns true if the game has data files greater than 2Gb
*/
bool is64BitGame() const;
/**
* Returns the game folder as a ScummVM filesystem node
*/
Common::FSNode getGameFolder();
/**
* Indicate whether a game state can be loaded.
*/
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
/**
* Indicate whether a game state can be saved.
*/
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
/**
* Load a savegame
*/
Common::Error loadGameState(int slot) override;
/**
* Save a savegame
*/
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
/**
* Returns autosave slot (-1 if unavailable)
*/
int getAutosaveSlot() const override;
/**
* Synchronize user volume settings
*/
void syncSoundSettings() override;
};
extern AGSEngine *g_vm;
#define gfx_driver ::AGS::g_vm->_gfxDriver
#define SHOULD_QUIT ::AGS::g_vm->shouldQuit()
} // namespace AGS
#endif

View 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 ags "Adventure Game Studio" yes "" "" "16bit mad" "theoradec midi universaltracker mpeg2"

279
engines/ags/console.cpp Normal file
View File

@@ -0,0 +1,279 @@
/* 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 "ags/console.h"
#include "ags/ags.h"
#include "ags/globals.h"
#include "ags/shared/ac/sprite_cache.h"
#include "ags/shared/gfx/allegro_bitmap.h"
#include "ags/shared/script/cc_common.h"
#include "graphics/palette.h"
#include "image/png.h"
namespace AGS {
AGSConsole::AGSConsole(AGSEngine *vm) : GUI::Debugger(), _vm(vm), _logOutputTarget(nullptr), _agsDebuggerOutput(nullptr) {
registerCmd("ags_debug_groups_list", WRAP_METHOD(AGSConsole, Cmd_listDebugGroups));
registerCmd("ags_debug_groups_set", WRAP_METHOD(AGSConsole, Cmd_setDebugGroupLevel));
registerCmd("ags_set_script_dump", WRAP_METHOD(AGSConsole, Cmd_SetScriptDump));
registerCmd("ags_sprite_info", WRAP_METHOD(AGSConsole, Cmd_getSpriteInfo));
registerCmd("ags_sprite_dump", WRAP_METHOD(AGSConsole, Cmd_dumpSprite));
_logOutputTarget = new LogOutputTarget();
_agsDebuggerOutput = _GP(DbgMgr).RegisterOutput("ScummVMLog", _logOutputTarget, AGS3::AGS::Shared::kDbgMsg_None);
}
AGSConsole::~AGSConsole() {
delete _logOutputTarget;
}
struct LevelName {
const char *name;
AGS3::AGS::Shared::MessageType level;
};
static const LevelName levelNames[] = {
{"none", AGS3::AGS::Shared::kDbgMsg_None},
{"alerts", AGS3::AGS::Shared::kDbgMsg_Alert},
{"fatal", AGS3::AGS::Shared::kDbgMsg_Fatal},
{"errors", AGS3::AGS::Shared::kDbgMsg_Error},
{"warnings", AGS3::AGS::Shared::kDbgMsg_None},
{"info", AGS3::AGS::Shared::kDbgMsg_Info},
{"debug", AGS3::AGS::Shared::kDbgMsg_Debug},
{nullptr, AGS3::AGS::Shared::kDbgMsg_None}
};
struct GroupName {
const char *name;
AGS3::uint32_t group;
};
static const GroupName groupNames[] = {
{"Main", AGS3::AGS::Shared::kDbgGroup_Main},
{"Game", AGS3::AGS::Shared::kDbgGroup_Game},
{"Script", AGS3::AGS::Shared::kDbgGroup_Script},
{"SpriteCache", AGS3::AGS::Shared::kDbgGroup_SprCache},
{"ManObj", AGS3::AGS::Shared::kDbgGroup_ManObj},
{nullptr, (AGS3::uint32_t)-1}
};
bool AGSConsole::Cmd_listDebugGroups(int argc, const char **argv) {
if (argc != 1) {
debugPrintf("Usage: %s\n", argv[0]);
return true;
}
debugPrintf("%-16s %-16s\n", "Name", "Level");
for (int i = 0 ; groupNames[i].name != nullptr ; ++i)
debugPrintf("%-16s %-16s\n", groupNames[i].name, getVerbosityLevel(groupNames[i].group));
return true;
}
bool AGSConsole::Cmd_setDebugGroupLevel(int argc, const char **argv) {
if (argc != 3) {
debugPrintf("Usage: %s group level\n", argv[0]);
debugPrintf(" valid groups: ");
printGroupList();
debugPrintf("\n");
debugPrintf(" valid levels: ");
printLevelList();
debugPrintf("\n");
return true;
}
bool found = false;
AGS3::uint32_t group = parseGroup(argv[1], found);
if (!found) {
debugPrintf("Unknown debug group '%s'\n", argv[1]);
debugPrintf("Valid groups are: ");
printGroupList();
debugPrintf("\n");
return true;
}
AGS3::AGS::Shared::MessageType level = parseLevel(argv[2], found);
if (!found) {
debugPrintf("Unknown level '%s'\n", argv[2]);
debugPrintf("Valid levels are: ");
printLevelList();
debugPrintf("\n");
return true;
}
_agsDebuggerOutput->SetGroupFilter(group, level);
return true;
}
const char *AGSConsole::getVerbosityLevel(AGS3::uint32_t groupID) const {
int i = 1;
while (levelNames[i].name != nullptr) {
if (!_agsDebuggerOutput->TestGroup(groupID, levelNames[i].level))
break;
++i;
}
return levelNames[i - 1].name;
}
AGS3::uint32_t AGSConsole::parseGroup(const char *name, bool &found) const {
int i = 0;
while (groupNames[i].name != nullptr) {
if (scumm_stricmp(name, groupNames[i].name) == 0) {
found = true;
return groupNames[i].group;
}
++i;
}
found = false;
return (AGS3::uint32_t)-1;
}
AGS3::AGS::Shared::MessageType AGSConsole::parseLevel(const char *name, bool &found) const {
int i = 0;
while (levelNames[i].name != nullptr) {
if (scumm_stricmp(name, levelNames[i].name) == 0) {
found = true;
return levelNames[i].level;
}
++i;
}
found = false;
return AGS3::AGS::Shared::kDbgMsg_None;
}
void AGSConsole::printGroupList() {
debugPrintf("%s", groupNames[0].name);
for (int i = 1 ; groupNames[i].name != nullptr ; ++i)
debugPrintf(", %s", groupNames[i].name);
}
void AGSConsole::printLevelList() {
debugPrintf("%s", levelNames[0].name);
for (int i = 1 ; levelNames[i].name != nullptr ; ++i)
debugPrintf(", %s", levelNames[i].name);
}
bool AGSConsole::Cmd_SetScriptDump(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: %s [on|off]\n", argv[0]);
return true;
}
if (strcmp(argv[1], "on") == 0 || strcmp(argv[1], "true") == 0)
AGS3::ccSetOption(SCOPT_DEBUGRUN, 1);
else
AGS3::ccSetOption(SCOPT_DEBUGRUN, 0);
return true;
}
bool AGSConsole::Cmd_getSpriteInfo(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: %s SpriteNumber\n", argv[0]);
return true;
}
int spriteId = atoi(argv[1]);
if (!_GP(spriteset).DoesSpriteExist(spriteId)) {
debugPrintf("Sprite %d does not exist\n", spriteId);
return true;
}
AGS3::Shared::Bitmap *sprite = _GP(spriteset)[spriteId];
if (!sprite) {
debugPrintf("Failed to get sprite %d\n", spriteId);
return true;
}
debugPrintf("Size: %dx%d\n", sprite->GetWidth(), sprite->GetHeight());
debugPrintf("Color depth: %d\n", sprite->GetColorDepth());
return true;
}
bool AGSConsole::Cmd_dumpSprite(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: %s SpriteNumber\n", argv[0]);
return true;
}
int spriteId = atoi(argv[1]);
if (!_GP(spriteset).DoesSpriteExist(spriteId)) {
debugPrintf("Sprite %d does not exist\n", spriteId);
return true;
}
AGS3::Shared::Bitmap *sprite = _GP(spriteset)[spriteId];
if (!sprite) {
debugPrintf("Failed to get sprite %d\n", spriteId);
return true;
}
Common::Path pngFile(Common::String::format("%s-sprite%03d.png", _vm->getGameId().c_str(), spriteId));
Common::DumpFile df;
if (df.open(pngFile)) {
byte *palette = nullptr;
if (sprite->GetColorDepth() == 8) {
palette = new byte[Graphics::PALETTE_SIZE];
for (int c = 0, i = 0 ; c < Graphics::PALETTE_COUNT ; ++c, i += 3) {
palette[i] = PALETTE_6BIT_TO_8BIT(_G(current_palette)[c].r);
palette[i + 1] = PALETTE_6BIT_TO_8BIT(_G(current_palette)[c].g);
palette[i + 2] = PALETTE_6BIT_TO_8BIT(_G(current_palette)[c].b);
}
}
Image::writePNG(df, sprite->GetAllegroBitmap()->getSurface().rawSurface(), palette);
delete[] palette;
}
return true;
}
LogOutputTarget::LogOutputTarget() {
}
LogOutputTarget::~LogOutputTarget() {
}
void LogOutputTarget::PrintMessage(const AGS3::AGS::Shared::DebugMessage &msg) {
LogMessageType::Type msgType = LogMessageType::kInfo;
switch (msg.MT) {
case AGS3::AGS::Shared::kDbgMsg_None:
return;
case AGS3::AGS::Shared::kDbgMsg_Alert:
case AGS3::AGS::Shared::kDbgMsg_Fatal:
case AGS3::AGS::Shared::kDbgMsg_Error:
msgType = LogMessageType::kError;
break;
case AGS3::AGS::Shared::kDbgMsg_Warn:
msgType = LogMessageType::kWarning;
break;
case AGS3::AGS::Shared::kDbgMsg_Info:
msgType = LogMessageType::kInfo;
break;
case AGS3::AGS::Shared::kDbgMsg_Debug:
msgType = LogMessageType::kDebug;
break;
}
Common::String text = Common::String::format("%s\n", msg.Text.GetCStr());
g_system->logMessage(msgType, text.c_str());
}
} // End of namespace AGS

71
engines/ags/console.h Normal file
View File

@@ -0,0 +1,71 @@
/* 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 AGS_CONSOLE_H
#define AGS_CONSOLE_H
#include "gui/debugger.h"
#include "ags/shared/debugging/debug_manager.h"
#include "ags/shared/debugging/output_handler.h"
#include "ags/shared/core/types.h"
namespace AGS {
class AGSEngine;
class LogOutputTarget;
class AGSConsole : public GUI::Debugger {
public:
AGSConsole(AGSEngine *vm);
~AGSConsole() override;
private:
AGSEngine *_vm;
LogOutputTarget *_logOutputTarget;
AGS3::AGS::Shared::PDebugOutput _agsDebuggerOutput;
bool Cmd_listDebugGroups(int argc, const char **argv);
bool Cmd_setDebugGroupLevel(int argc, const char **argv);
bool Cmd_SetScriptDump(int argc, const char **argv);
bool Cmd_getSpriteInfo(int argc, const char **argv);
bool Cmd_dumpSprite(int argc, const char **argv);
const char *getVerbosityLevel(AGS3::uint32_t groupID) const;
AGS3::uint32_t parseGroup(const char *, bool &) const;
AGS3::AGS::Shared::MessageType parseLevel(const char *, bool &) const;
void printGroupList();
void printLevelList();
};
class LogOutputTarget : public AGS3::AGS::Shared::IOutputHandler {
public:
LogOutputTarget();
~LogOutputTarget() override;
void PrintMessage(const AGS3::AGS::Shared::DebugMessage &msg) override;
};
} // End of namespace AGS
#endif

43
engines/ags/credits.pl Normal file
View File

@@ -0,0 +1,43 @@
begin_section("AGS");
add_person("Paul Gilbert", "dreammaster", "");
add_person("Thierry Crozat", "criezy", "");
add_person("Walter Agazzi", "tag2015", "Maintenance, Game archival");
add_person("Chris Jones", "Pumaman", "Creator");
add_person("Alan Van Drake", "", "AGS");
add_person("Benjamin Penney", "", "AGS");
add_person("Benoit Pierre", "", "AGS");
add_person("Bernhard Rosenkraenzer", "", "AGS");
add_person("Cristian Morales Vega", "", "AGS");
add_person("Edward Rudd", "", "AGS");
add_person("Erico Vieira Porto", "", "AGS");
add_person("Ferdinand Thiessen", "", "AGS");
add_person("Francesco Ariis", "", "AGS");
add_person("Gilad Shaham", "", "AGS");
add_person("Ivan Mogilko", "", "AGS");
add_person("Janet Gilbert", "", "AGS");
add_person("Jochen Schleu", "", "AGS");
add_person("Joe Lee", "", "AGS");
add_person("John Steele Scott", "", "AGS");
add_person("Martin Sedlak", "", "AGS");
add_person("Matthew Gambrell", "", "AGS");
add_person("Michael Rittenhouse", "", "AGS");
add_person("Morgan Willcock", "", "AGS");
add_person("Nick Sonneveld", "", "AGS");
add_person("Ori Avtalion", "", "AGS");
add_person("Paul Wilkinson", "", "AGS");
add_person("Per Olav Flaten", "", "AGS");
add_person("Piotr Wieczorek", "", "AGS");
add_person("Ryan O'Connor", "", "AGS");
add_person("Scott Baker", "", "AGS");
add_person("Shane Stevens", "", "AGS");
add_person("Shawn R. Walker", "", "AGS");
add_person("Stefano Collavini", "", "AGS");
add_person("Steve McCrea", "", "AGS");
add_person("Steven Poulton", "", "AGS");
add_person("Sunit Das", "", "AGS");
add_person("Tobias Hansen", "", "AGS");
add_person("Tom Vandepoele", "", "AGS");
add_person("Tzach Shabtay", "", "AGS");
add_person("", "rofl0r", "AGS");
add_person("Berian Williams", "AGA", "AgsCreditz");
end_section();

201
engines/ags/detection.cpp Normal file
View File

@@ -0,0 +1,201 @@
/* 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/config-manager.h"
#include "common/file.h"
#include "common/md5.h"
#include "common/str-array.h"
#include "common/util.h"
#include "common/punycode.h"
#include "ags/detection.h"
#include "ags/detection_tables.h"
namespace AGS3 {
static const char *const HEAD_SIG = "CLIB\x1a";
static const char *const TAIL_SIG = "CLIB\x1\x2\x3\x4SIGE";
#define HEAD_SIG_SIZE 5
#define TAIL_SIG_SIZE 12
/**
* Detect the presence of an AGS game
* TODO: This is a compact version of MFLUtil::ReadSigsAndVersion. I didn't
* use the full version due to the complexities of including it when
* plugins are enabled. In the future, though, it would be nice to figure
* out, since the full version can handle not detecting on files that are
* AGS, but only contain sounds, etc. rather than a game
*/
static bool isAGSFile(Common::File &f) {
// Check for signature at beginning of file
char buffer[16];
if (f.read(buffer, HEAD_SIG_SIZE) == HEAD_SIG_SIZE &&
!memcmp(buffer, HEAD_SIG, HEAD_SIG_SIZE))
return true;
// Check for signature at end of EXE files
f.seek(-TAIL_SIG_SIZE, SEEK_END);
if (f.read(buffer, TAIL_SIG_SIZE) == TAIL_SIG_SIZE &&
!memcmp(buffer, TAIL_SIG, TAIL_SIG_SIZE))
return true;
return false;
}
} // namespace AGS3
const DebugChannelDef AGSMetaEngineDetection::debugFlagList[] = {
{AGS::kDebugGraphics, "Graphics", "Graphics debug level"},
{AGS::kDebugPath, "Path", "Pathfinding debug level"},
{AGS::kDebugFilePath, "FilePath", "File path debug level"},
{AGS::kDebugScan, "Scan", "Scan for unrecognised games"},
{AGS::kDebugScript, "Script", "Enable debug script dump"},
DEBUG_CHANNEL_END
};
AGSMetaEngineDetection::AGSMetaEngineDetection() : AdvancedMetaEngineDetection(AGS::GAME_DESCRIPTIONS, AGS::GAME_NAMES) {
_flags = kADFlagCanPlayUnknownVariants;
}
DetectedGames AGSMetaEngineDetection::detectGames(const Common::FSList &fslist, uint32 skipADFlags, bool skipIncomplete) {
FileMap allFiles;
if (fslist.empty())
return DetectedGames();
// Compose a hashmap of all files in fslist.
composeFileHashMap(allFiles, fslist, (_maxScanDepth == 0 ? 1 : _maxScanDepth));
// Run the detector on this
ADDetectedGames matches = detectGame(fslist.begin()->getParent(), allFiles, Common::UNK_LANG, Common::kPlatformUnknown, "", skipADFlags, skipIncomplete);
cleanupPirated(matches);
bool foundKnownGames = false;
DetectedGames detectedGames;
for (uint i = 0; i < matches.size(); i++) {
DetectedGame game = toDetectedGame(matches[i]);
if (game.hasUnknownFiles) {
// Check the game is an AGS game
for (FilePropertiesMap::const_iterator it = game.matchedFiles.begin(); it != game.matchedFiles.end(); it++) {
Common::File f;
if (f.open(allFiles[it->_key]) && AGS3::isAGSFile(f)) {
detectedGames.push_back(game);
break;
}
}
} else {
detectedGames.push_back(game);
foundKnownGames = true;
}
}
// If we didn't find a known game, also add a fallback detection
if (!foundKnownGames) {
// Use fallback detector if there were no matches by other means
ADDetectedGame fallbackDetectionResult = fallbackDetect(allFiles, fslist);
if (fallbackDetectionResult.desc) {
DetectedGame fallbackDetectedGame = toDetectedGame(fallbackDetectionResult);
fallbackDetectedGame.preferredTarget += "-fallback";
detectedGames.push_back(fallbackDetectedGame);
}
}
return detectedGames;
}
ADDetectedGame AGSMetaEngineDetection::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extra) const {
// Set the default values for the fallback descriptor's ADGameDescription part.
AGS::g_fallbackDesc.desc.language = Common::UNK_LANG;
AGS::g_fallbackDesc.desc.platform = Common::kPlatformUnknown;
AGS::g_fallbackDesc.desc.flags = ADGF_NO_FLAGS;
// FIXME: Hack to return match without checking for game data,
// so that the command line game scanner will work
if (ConfMan.get("gameid") == "ags-scan") {
_gameid = "ags-scan";
AGS::g_fallbackDesc.desc.gameId = "ags-scan";
return ADDetectedGame(&AGS::g_fallbackDesc.desc);
}
// Set the defaults for gameid and extra
_gameid = "ags";
_extra.clear();
bool hasUnknownFiles = true;
// Scan for AGS games
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
if (file->isDirectory())
continue;
Common::Path filename = file->getPathInArchive();
if (!filename.baseName().hasSuffixIgnoreCase(".exe") &&
!filename.baseName().hasSuffixIgnoreCase(".ags") &&
!filename.equalsIgnoreCase("ac2game.dat"))
// Neither, so move on
continue;
filename = filename.punycodeEncode();
Common::File f;
if (!allFiles.contains(filename) || !f.open(allFiles[filename]))
continue;
if (AGS3::isAGSFile(f)) {
f.seek(0);
_md5 = Common::computeStreamMD5AsString(f, 5000);
// Check whether the game is in the detection list with a different filename
for (const ::AGS::AGSGameDescription *gameP = ::AGS::GAME_DESCRIPTIONS;
gameP->desc.gameId; ++gameP) {
if (_md5 == gameP->desc.filesDescriptions[0].md5 &&
f.size() == gameP->desc.filesDescriptions[0].fileSize &&
AD_NO_SIZE != gameP->desc.filesDescriptions[0].fileSize) {
hasUnknownFiles = false;
_gameid = gameP->desc.gameId;
break;
}
}
AGS::g_fallbackDesc.desc.gameId = _gameid.c_str();
AGS::g_fallbackDesc.desc.extra = _extra.c_str();
_filenameStr = filename.toString('/');
AGS::g_fallbackDesc.desc.filesDescriptions[0].fileName = _filenameStr.c_str();
AGS::g_fallbackDesc.desc.filesDescriptions[0].fileSize = (f.size() >= 0xffffffff) ? AD_NO_SIZE : f.size();
AGS::g_fallbackDesc.desc.filesDescriptions[0].md5 = _md5.c_str();
// If adding an unknown game, append filename for easier identification
if (_gameid.equals("ags"))
AGS::g_fallbackDesc.desc.extra = _filenameStr.c_str();
ADDetectedGame game(&AGS::g_fallbackDesc.desc);
game.matchedFiles[filename].md5 = _md5;
game.matchedFiles[filename].size = f.size();
game.hasUnknownFiles = hasUnknownFiles;
return game;
}
}
return ADDetectedGame();
}
REGISTER_PLUGIN_STATIC(AGS_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, AGSMetaEngineDetection);

99
engines/ags/detection.h Normal file
View File

@@ -0,0 +1,99 @@
/* 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 AGS_DETECTION_H
#define AGS_DETECTION_H
#include "engines/advancedDetector.h"
namespace AGS {
enum AGSDebugChannels {
kDebugGraphics = 1,
kDebugPath,
kDebugScan,
kDebugFilePath,
kDebugScript,
};
enum GameFlag {
GAMEFLAG_PLUGINS_MASK = 0xff,
GAMEFLAG_PLUGINS_NONE = 0,
GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE = 1,
GAMEFLAG_PLUGINS_AGS_FLASHLIGHT = 2,
GAMEFLAG_PLUGINS_AGSSPRITEFONT_CLIFFTOP = 3,
GAMEFLAG_FORCE_AA = 1 << 8,
};
struct AGSGameDescription {
AD_GAME_DESCRIPTION_HELPERS(desc);
ADGameDescription desc;
uint32 features;
};
extern const PlainGameDescriptor GAME_NAMES[];
extern const AGSGameDescription GAME_DESCRIPTIONS[];
#define GAMEOPTION_NO_SAVE_THUMBNAIL GUIO_GAMEOPTIONS1
#define GAMEOPTION_NO_AUTOSAVE GUIO_GAMEOPTIONS2
#define GAMEOPTION_NO_SAVELOAD GUIO_GAMEOPTIONS3
} // namespace AGS
class AGSMetaEngineDetection : public AdvancedMetaEngineDetection<AGS::AGSGameDescription> {
mutable Common::String _gameid;
mutable Common::String _extra;
mutable Common::String _filenameStr;
mutable Common::String _md5;
static const DebugChannelDef debugFlagList[];
public:
AGSMetaEngineDetection();
~AGSMetaEngineDetection() override {}
const char *getName() const override {
return "ags";
}
const char *getEngineName() const override {
return "Adventure Game Studio";
}
const char *getOriginalCopyright() const override {
return "AGS Engine (C) Chris Jones";
}
const DebugChannelDef *getDebugChannels() const override {
return debugFlagList;
}
DetectedGames detectGames(const Common::FSList &fslist, uint32 skipADFlags, bool skipIncomplete) override;
ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extra = nullptr) const override;
};
#endif

10218
engines/ags/detection_tables.h Normal file

File diff suppressed because it is too large Load Diff

165
engines/ags/dialogs.cpp Normal file
View File

@@ -0,0 +1,165 @@
/* 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/config-manager.h"
#include "common/file.h"
#include "common/md5.h"
#include "common/str-array.h"
#include "common/translation.h"
#include "common/util.h"
#include "ags/detection.h"
#include "ags/metaengine.h"
#include "gui/ThemeEval.h"
#include "gui/widget.h"
#include "gui/widgets/popup.h"
namespace AGS3 {
class AGSOptionsWidget : public GUI::OptionsContainerWidget {
public:
explicit AGSOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain);
// OptionsContainerWidget API
void load() override;
bool save() override;
private:
// OptionsContainerWidget API
void defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const override;
GUI::PopUpWidget *_langPopUp;
Common::StringArray _traFileNames;
GUI::CheckboxWidget *_overrideSavesCheckbox;
GUI::CheckboxWidget *_forceTextAACheckbox;
GUI::CheckboxWidget *_displayFPSCheckbox;
};
AGSOptionsWidget::AGSOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain) :
OptionsContainerWidget(boss, name, "AGSGameOptionsDialog", domain) {
// Language
GUI::StaticTextWidget *textWidget = new GUI::StaticTextWidget(widgetsBoss(), _dialogLayout + ".translation_desc", _("Game language:"), _("Language to use for multilingual games"));
textWidget->setAlign(Graphics::kTextAlignRight);
_langPopUp = new GUI::PopUpWidget(widgetsBoss(), _dialogLayout + ".translation");
_langPopUp->appendEntry(_("<default>"), (uint32) - 1);
_traFileNames = AGSMetaEngine::getGameTranslations(_domain);
int i = 0;
for (Common::StringArray::iterator iter = _traFileNames.begin(); iter != _traFileNames.end(); ++iter) {
_langPopUp->appendEntry(*iter, i++);
}
// Override game save management
if (Common::checkGameGUIOption(GAMEOPTION_NO_AUTOSAVE, ConfMan.get("guioptions", domain)) ||
Common::checkGameGUIOption(GAMEOPTION_NO_SAVELOAD, ConfMan.get("guioptions", domain))) {
_overrideSavesCheckbox = new GUI::CheckboxWidget(widgetsBoss(), _dialogLayout + ".savesOvr", _("Enable ScummVM save management"), _("Never disable ScummVM save management and autosaves.\nNOTE: This could cause save duplication and other oddities"));
} else
_overrideSavesCheckbox = nullptr;
// Force font antialiasing
_forceTextAACheckbox = new GUI::CheckboxWidget(widgetsBoss(), _dialogLayout + ".textAA", _("Force antialiased text"), _("Use antialiasing to draw text even if the game does not ask for it"));
// Display fps
_displayFPSCheckbox = new GUI::CheckboxWidget(widgetsBoss(), _dialogLayout + ".displayFPS", _("Show FPS"), _("Show the current FPS-rate, while you play."));
}
void AGSOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const {
layouts.addDialog(layoutName, overlayedLayout);
layouts.addLayout(GUI::ThemeLayout::kLayoutVertical).addPadding(0, 0, 0, 0);
layouts.addLayout(GUI::ThemeLayout::kLayoutHorizontal).addPadding(0, 0, 0, 0);
layouts.addWidget("translation_desc", "OptionsLabel");
layouts.addWidget("translation", "PopUp").closeLayout();
layouts.addWidget("savesOvr", "Checkbox");
layouts.addWidget("textAA", "Checkbox");
layouts.addWidget("displayFPS", "Checkbox");
layouts.closeLayout().closeDialog();
}
void AGSOptionsWidget::load() {
Common::ConfigManager::Domain *gameConfig = ConfMan.getDomain(_domain);
if (!gameConfig)
return;
uint32 curLangIndex = (uint32) - 1;
Common::String curLang;
gameConfig->tryGetVal("translation", curLang);
if (!curLang.empty()) {
for (uint i = 0; i < _traFileNames.size(); ++i) {
if (_traFileNames[i].equalsIgnoreCase(curLang)) {
curLangIndex = i;
break;
}
}
}
_langPopUp->setSelectedTag(curLangIndex);
Common::String saveOverride;
gameConfig->tryGetVal("save_override", saveOverride);
if (!saveOverride.empty()) {
bool val;
if (_overrideSavesCheckbox && parseBool(saveOverride, val))
_overrideSavesCheckbox->setState(val);
}
Common::String forceTextAA;
gameConfig->tryGetVal("force_text_aa", forceTextAA);
if (!forceTextAA.empty()) {
bool val;
if (parseBool(forceTextAA, val))
_forceTextAACheckbox->setState(val);
}
Common::String displayFPS;
gameConfig->tryGetVal("display_fps", displayFPS);
if (!displayFPS.empty()) {
bool val;
if (parseBool(displayFPS, val))
_displayFPSCheckbox->setState(val);
}
}
bool AGSOptionsWidget::save() {
uint langIndex = _langPopUp->getSelectedTag();
if (langIndex < _traFileNames.size())
ConfMan.set("translation", _traFileNames[langIndex], _domain);
else
ConfMan.removeKey("translation", _domain);
if (_overrideSavesCheckbox)
ConfMan.setBool("save_override", _overrideSavesCheckbox->getState(), _domain);
ConfMan.setBool("force_text_aa", _forceTextAACheckbox->getState(), _domain);
ConfMan.setBool("display_fps", _displayFPSCheckbox->getState(), _domain);
return true;
}
} // namespace AGS3
GUI::OptionsContainerWidget *AGSMetaEngine::buildEngineOptionsWidget(GUI::GuiObject *boss, const Common::String &name, const Common::String &target) const {
return new AGS3::AGSOptionsWidget(boss, name, target);
}

View File

@@ -0,0 +1,73 @@
/* 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/>.
*
*/
//=============================================================================
//
// Functions related to finding and opening game assets.
//
//=============================================================================
#ifndef AGS_ENGINE_AC_ASSET_HELPER_H
#define AGS_ENGINE_AC_ASSET_HELPER_H
#include "common/std/memory.h"
#include "common/std/utility.h"
#include "ags/shared/util/string.h"
#include "ags/shared/core/asset_manager.h"
namespace AGS3 {
struct PACKFILE;
namespace AGS {
namespace Shared {
class Stream;
} // namespace Shared
} // namespace AGS
using AGS::Shared::AssetPath;
using AGS::Shared::Stream;
using AGS::Shared::String;
// Looks for valid asset library everywhere and returns path, or empty string if failed
String find_assetlib(const String &filename);
// Returns the path to the audio asset, considering the given bundling type
AssetPath get_audio_clip_assetpath(int bundling_type, const String &filename);
// Returns the path to the voice-over asset
AssetPath get_voice_over_assetpath(const String &filename);
// Custom AGS PACKFILE user object
// TODO: it is preferrable to let our Stream define custom readable window instead,
// keeping this as simple as possible for now (we may require a stream classes overhaul).
struct AGS_PACKFILE_OBJ {
std::unique_ptr<Stream> stream;
size_t asset_size = 0u;
size_t remains = 0u;
};
// Creates PACKFILE stream from AGS asset.
// This function is supposed to be used only when you have to create Allegro
// object, passing PACKFILE stream to constructor.
PACKFILE *PackfileFromAsset(const AssetPath &path);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,333 @@
/* 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 "ags/engine/ac/audio_channel.h"
#include "ags/engine/ac/game_state.h"
#include "ags/engine/ac/global_audio.h"
#include "ags/engine/ac/dynobj/cc_audio_clip.h"
#include "ags/engine/debugging/debug_log.h"
#include "ags/shared/game/room_struct.h"
#include "ags/engine/script/runtime_script_value.h"
#include "ags/engine/media/audio/audio_system.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/shared/debugging/out.h"
#include "ags/engine/script/script_api.h"
#include "ags/engine/script/script_runtime.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
int AudioChannel_GetID(ScriptAudioChannel *channel) {
return channel->id;
}
int AudioChannel_GetIsPlaying(ScriptAudioChannel *channel) {
if (_GP(play).fast_forward) {
return 0;
}
return AudioChans::ChannelIsPlaying(channel->id) ? 1 : 0;
}
bool AudioChannel_GetIsPaused(ScriptAudioChannel *channel) {
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch) return ch->is_paused();
return false;
}
int AudioChannel_GetPanning(ScriptAudioChannel *channel) {
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch) {
return ch->get_panning();
}
return 0;
}
void AudioChannel_SetPanning(ScriptAudioChannel *channel, int newPanning) {
if ((newPanning < -100) || (newPanning > 100))
quitprintf("!AudioChannel.Panning: panning value must be between -100 and 100 (passed=%d)", newPanning);
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch) {
ch->set_panning(newPanning);
}
}
ScriptAudioClip *AudioChannel_GetPlayingClip(ScriptAudioChannel *channel) {
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch && ch->_sourceClipID >= 0) {
return &_GP(game).audioClips[ch->_sourceClipID];
}
return nullptr;
}
int AudioChannel_GetPosition(ScriptAudioChannel *channel) {
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch) {
if (_GP(play).fast_forward)
return 999999999;
return ch->get_pos();
}
return 0;
}
int AudioChannel_GetPositionMs(ScriptAudioChannel *channel) {
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch) {
if (_GP(play).fast_forward)
return 999999999;
return ch->get_pos_ms();
}
return 0;
}
int AudioChannel_GetLengthMs(ScriptAudioChannel *channel) {
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch) {
return ch->get_length_ms();
}
return 0;
}
int AudioChannel_GetVolume(ScriptAudioChannel *channel) {
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch) {
return ch->get_volume100();
}
return 0;
}
int AudioChannel_SetVolume(ScriptAudioChannel *channel, int newVolume) {
if ((newVolume < 0) || (newVolume > 100))
quitprintf("!AudioChannel.Volume: new value out of range (supplied: %d, range: 0..100)", newVolume);
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch) {
ch->set_volume100(newVolume);
}
return 0;
}
int AudioChannel_GetSpeed(ScriptAudioChannel *channel) {
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch) {
return ch->get_speed();
}
return 0;
}
void AudioChannel_SetSpeed(ScriptAudioChannel *channel, int new_speed) {
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch) {
ch->set_speed(new_speed);
}
}
void AudioChannel_Stop(ScriptAudioChannel *channel) {
if (channel->id == SCHAN_SPEECH && _GP(play).IsNonBlockingVoiceSpeech())
stop_voice_nonblocking();
else
stop_or_fade_out_channel(channel->id, -1, nullptr);
}
void AudioChannel_Pause(ScriptAudioChannel *channel) {
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch) ch->pause();
}
void AudioChannel_Resume(ScriptAudioChannel *channel) {
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch) ch->resume();
}
void AudioChannel_Seek(ScriptAudioChannel *channel, int newPosition) {
if (newPosition < 0)
quitprintf("!AudioChannel.Seek: invalid seek position %d", newPosition);
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch)
ch->seek(newPosition);
}
void AudioChannel_SeekMs(ScriptAudioChannel *channel, int newPosition) {
if (newPosition < 0)
quitprintf("!AudioChannel.SeekMs: invalid seek position %d", newPosition);
auto* ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch)
ch->seek_ms(newPosition);
}
void AudioChannel_SetRoomLocation(ScriptAudioChannel *channel, int xPos, int yPos) {
auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
if (ch) {
int maxDist = ((xPos > _GP(thisroom).Width / 2) ? xPos : (_GP(thisroom).Width - xPos)) - AMBIENCE_FULL_DIST;
ch->_xSource = (xPos > 0) ? xPos : -1;
ch->_ySource = yPos;
ch->_maximumPossibleDistanceAway = maxDist;
if (xPos > 0) {
update_directional_sound_vol();
} else {
ch->apply_directional_modifier(0);
}
}
}
//=============================================================================
//
// Script API Functions
//
//=============================================================================
// int | ScriptAudioChannel *channel
RuntimeScriptValue Sc_AudioChannel_GetID(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptAudioChannel, AudioChannel_GetID);
}
// int | ScriptAudioChannel *channel
RuntimeScriptValue Sc_AudioChannel_GetIsPlaying(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptAudioChannel, AudioChannel_GetIsPlaying);
}
// int | ScriptAudioChannel *channel
RuntimeScriptValue Sc_AudioChannel_GetPanning(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptAudioChannel, AudioChannel_GetPanning);
}
// void | ScriptAudioChannel *channel, int newPanning
RuntimeScriptValue Sc_AudioChannel_SetPanning(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptAudioChannel, AudioChannel_SetPanning);
}
// ScriptAudioClip* | ScriptAudioChannel *channel
RuntimeScriptValue Sc_AudioChannel_GetPlayingClip(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_OBJ(ScriptAudioChannel, ScriptAudioClip, _GP(ccDynamicAudioClip), AudioChannel_GetPlayingClip);
}
// int | ScriptAudioChannel *channel
RuntimeScriptValue Sc_AudioChannel_GetPosition(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptAudioChannel, AudioChannel_GetPosition);
}
// int | ScriptAudioChannel *channel
RuntimeScriptValue Sc_AudioChannel_GetPositionMs(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptAudioChannel, AudioChannel_GetPositionMs);
}
// int | ScriptAudioChannel *channel
RuntimeScriptValue Sc_AudioChannel_GetLengthMs(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptAudioChannel, AudioChannel_GetLengthMs);
}
// int | ScriptAudioChannel *channel
RuntimeScriptValue Sc_AudioChannel_GetVolume(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptAudioChannel, AudioChannel_GetVolume);
}
// int | ScriptAudioChannel *channel, int newVolume
RuntimeScriptValue Sc_AudioChannel_SetVolume(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT_PINT(ScriptAudioChannel, AudioChannel_SetVolume);
}
// void | ScriptAudioChannel *channel
RuntimeScriptValue Sc_AudioChannel_Stop(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID(ScriptAudioChannel, AudioChannel_Stop);
}
// void | ScriptAudioChannel *channel, int newPosition
RuntimeScriptValue Sc_AudioChannel_Seek(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptAudioChannel, AudioChannel_Seek);
}
RuntimeScriptValue Sc_AudioChannel_SeekMs(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptAudioChannel, AudioChannel_SeekMs);
}
// void | ScriptAudioChannel *channel, int xPos, int yPos
RuntimeScriptValue Sc_AudioChannel_SetRoomLocation(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT2(ScriptAudioChannel, AudioChannel_SetRoomLocation);
}
RuntimeScriptValue Sc_AudioChannel_GetSpeed(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptAudioChannel, AudioChannel_GetSpeed);
}
RuntimeScriptValue Sc_AudioChannel_SetSpeed(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptAudioChannel, AudioChannel_SetSpeed);
}
RuntimeScriptValue Sc_AudioChannel_Pause(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID(ScriptAudioChannel, AudioChannel_Pause);
}
RuntimeScriptValue Sc_AudioChannel_Resume(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID(ScriptAudioChannel, AudioChannel_Resume);
}
RuntimeScriptValue Sc_AudioChannel_GetIsPaused(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_BOOL(ScriptAudioChannel, AudioChannel_GetIsPaused);
}
void RegisterAudioChannelAPI() {
ScFnRegister audiochan_api[] = {
{"AudioChannel::Pause^0", API_FN_PAIR(AudioChannel_Pause)},
{"AudioChannel::Resume^0", API_FN_PAIR(AudioChannel_Resume)},
{"AudioChannel::Seek^1", API_FN_PAIR(AudioChannel_Seek)},
{"AudioChannel::SeekMs^1", API_FN_PAIR(AudioChannel_SeekMs)},
{"AudioChannel::SetRoomLocation^2", API_FN_PAIR(AudioChannel_SetRoomLocation)},
{"AudioChannel::Stop^0", API_FN_PAIR(AudioChannel_Stop)},
{"AudioChannel::get_ID", API_FN_PAIR(AudioChannel_GetID)},
{"AudioChannel::get_IsPaused", API_FN_PAIR(AudioChannel_GetIsPaused)},
{"AudioChannel::get_IsPlaying", API_FN_PAIR(AudioChannel_GetIsPlaying)},
{"AudioChannel::get_LengthMs", API_FN_PAIR(AudioChannel_GetLengthMs)},
{"AudioChannel::get_Panning", API_FN_PAIR(AudioChannel_GetPanning)},
{"AudioChannel::set_Panning", API_FN_PAIR(AudioChannel_SetPanning)},
{"AudioChannel::get_PlayingClip", API_FN_PAIR(AudioChannel_GetPlayingClip)},
{"AudioChannel::get_Position", API_FN_PAIR(AudioChannel_GetPosition)},
{"AudioChannel::get_PositionMs", API_FN_PAIR(AudioChannel_GetPositionMs)},
{"AudioChannel::get_Volume", API_FN_PAIR(AudioChannel_GetVolume)},
{"AudioChannel::set_Volume", API_FN_PAIR(AudioChannel_SetVolume)},
{"AudioChannel::get_Speed", API_FN_PAIR(AudioChannel_GetSpeed)},
{"AudioChannel::set_Speed", API_FN_PAIR(AudioChannel_SetSpeed)},
// For compatibility with Ahmet Kamil's (aka Gord10) custom engine
{"AudioChannel::SetSpeed^1", API_FN_PAIR(AudioChannel_SetSpeed)},
};
ccAddExternalFunctions361(audiochan_api);
}
} // namespace AGS3

View File

@@ -0,0 +1,47 @@
/* 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 AGS_ENGINE_AC_AUDIO_CHANNEL_H
#define AGS_ENGINE_AC_AUDIO_CHANNEL_H
#include "ags/shared/ac/dynobj/script_audio_clip.h"
#include "ags/engine/ac/dynobj/script_audio_channel.h"
namespace AGS3 {
int AudioChannel_GetID(ScriptAudioChannel *channel);
int AudioChannel_GetIsPlaying(ScriptAudioChannel *channel);
int AudioChannel_GetPanning(ScriptAudioChannel *channel);
void AudioChannel_SetPanning(ScriptAudioChannel *channel, int newPanning);
ScriptAudioClip *AudioChannel_GetPlayingClip(ScriptAudioChannel *channel);
int AudioChannel_GetPosition(ScriptAudioChannel *channel);
int AudioChannel_GetPositionMs(ScriptAudioChannel *channel);
int AudioChannel_GetLengthMs(ScriptAudioChannel *channel);
int AudioChannel_GetVolume(ScriptAudioChannel *channel);
int AudioChannel_SetVolume(ScriptAudioChannel *channel, int newVolume);
void AudioChannel_Stop(ScriptAudioChannel *channel);
void AudioChannel_Seek(ScriptAudioChannel *channel, int newPosition);
void AudioChannel_SeekMs(ScriptAudioChannel *channel, int newPosition);
void AudioChannel_SetRoomLocation(ScriptAudioChannel *channel, int xPos, int yPos);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,173 @@
/* 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 "ags/engine/media/audio/audio_system.h"
#include "ags/engine/ac/asset_helper.h"
#include "ags/engine/ac/audio_clip.h"
#include "ags/engine/ac/audio_channel.h"
#include "ags/shared/ac/common.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/engine/ac/string.h"
#include "ags/shared/core/asset_manager.h"
#include "ags/engine/ac/dynobj/cc_audio_channel.h"
#include "ags/engine/ac/dynobj/cc_audio_clip.h"
#include "ags/engine/ac/dynobj/script_string.h"
#include "ags/engine/script/runtime_script_value.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
int AudioClip_GetID(ScriptAudioClip *clip) {
return clip->id;
}
const char *AudioClip_GetScriptName(ScriptAudioClip *clip) {
return CreateNewScriptString(clip->scriptName);
}
int AudioClip_GetFileType(ScriptAudioClip *clip) {
return clip->fileType;
}
int AudioClip_GetType(ScriptAudioClip *clip) {
return clip->type;
}
int AudioClip_GetIsAvailable(ScriptAudioClip *clip) {
return _GP(AssetMgr)->DoesAssetExist(get_audio_clip_assetpath(clip->bundlingType, clip->fileName)) ? 1 : 0;
}
void AudioClip_Stop(ScriptAudioClip *clip) {
for (int i = NUM_SPEECH_CHANS; i < _GP(game).numGameChannels; i++) {
auto *ch = AudioChans::GetChannelIfPlaying(i);
if ((ch != nullptr) && (ch->_sourceClipID == clip->id)) {
AudioChannel_Stop(&_G(scrAudioChannel)[i]);
}
}
}
ScriptAudioChannel *AudioClip_Play(ScriptAudioClip *clip, int priority, int repeat) {
ScriptAudioChannel *sc_ch = play_audio_clip(clip, priority, repeat, 0, false);
return sc_ch;
}
ScriptAudioChannel *AudioClip_PlayFrom(ScriptAudioClip *clip, int position, int priority, int repeat) {
ScriptAudioChannel *sc_ch = play_audio_clip(clip, priority, repeat, position, false);
return sc_ch;
}
ScriptAudioChannel *AudioClip_PlayQueued(ScriptAudioClip *clip, int priority, int repeat) {
ScriptAudioChannel *sc_ch = play_audio_clip(clip, priority, repeat, 0, true);
return sc_ch;
}
ScriptAudioChannel *AudioClip_PlayOnChannel(ScriptAudioClip *clip, int chan, int priority, int repeat) {
if (chan < NUM_SPEECH_CHANS || chan >= _GP(game).numGameChannels)
quitprintf("!AudioClip.PlayOnChannel: invalid channel %d, the range is %d - %d",
chan, NUM_SPEECH_CHANS, _GP(game).numGameChannels - 1);
if (priority == SCR_NO_VALUE)
priority = clip->defaultPriority;
if (repeat == SCR_NO_VALUE)
repeat = clip->defaultRepeat;
return play_audio_clip_on_channel(chan, clip, priority, repeat, 0);
}
//=============================================================================
//
// Script API Functions
//
//=============================================================================
ScriptAudioClip *AudioClip_GetByName(const char *name) {
return static_cast<ScriptAudioClip *>(ccGetScriptObjectAddress(name, _GP(ccDynamicAudioClip).GetType()));
}
RuntimeScriptValue Sc_AudioClip_GetByName(const RuntimeScriptValue *params, int32_t param_count) {
API_SCALL_OBJ_POBJ(ScriptAudioClip, _GP(ccDynamicAudioClip), AudioClip_GetByName, const char);
}
RuntimeScriptValue Sc_AudioClip_GetID(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptAudioClip, AudioClip_GetID);
}
RuntimeScriptValue Sc_AudioClip_GetScriptName(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_OBJ(ScriptAudioClip, const char, _GP(myScriptStringImpl), AudioClip_GetScriptName);
}
// int | ScriptAudioClip *clip
RuntimeScriptValue Sc_AudioClip_GetFileType(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptAudioClip, AudioClip_GetFileType);
}
// int | ScriptAudioClip *clip
RuntimeScriptValue Sc_AudioClip_GetType(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptAudioClip, AudioClip_GetType);
}
// int | ScriptAudioClip *clip
RuntimeScriptValue Sc_AudioClip_GetIsAvailable(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptAudioClip, AudioClip_GetIsAvailable);
}
// void | ScriptAudioClip *clip
RuntimeScriptValue Sc_AudioClip_Stop(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID(ScriptAudioClip, AudioClip_Stop);
}
// ScriptAudioChannel* | ScriptAudioClip *clip, int priority, int repeat
RuntimeScriptValue Sc_AudioClip_Play(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_OBJ_PINT2(ScriptAudioClip, ScriptAudioChannel, _GP(ccDynamicAudio), AudioClip_Play);
}
// ScriptAudioChannel* | ScriptAudioClip *clip, int position, int priority, int repeat
RuntimeScriptValue Sc_AudioClip_PlayFrom(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_OBJ_PINT3(ScriptAudioClip, ScriptAudioChannel, _GP(ccDynamicAudio), AudioClip_PlayFrom);
}
// ScriptAudioChannel* | ScriptAudioClip *clip, int priority, int repeat
RuntimeScriptValue Sc_AudioClip_PlayQueued(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_OBJ_PINT2(ScriptAudioClip, ScriptAudioChannel, _GP(ccDynamicAudio), AudioClip_PlayQueued);
}
RuntimeScriptValue Sc_AudioClip_PlayOnChannel(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_OBJ_PINT3(ScriptAudioClip, ScriptAudioChannel, _GP(ccDynamicAudio), AudioClip_PlayOnChannel);
}
void RegisterAudioClipAPI() {
ScFnRegister audioclip_api[] = {
{"AudioClip::GetByName", API_FN_PAIR(AudioClip_GetByName)},
{"AudioClip::Play^2", API_FN_PAIR(AudioClip_Play)},
{"AudioClip::PlayFrom^3", API_FN_PAIR(AudioClip_PlayFrom)},
{"AudioClip::PlayQueued^2", API_FN_PAIR(AudioClip_PlayQueued)},
{"AudioClip::PlayOnChannel^3", API_FN_PAIR(AudioClip_PlayOnChannel)},
{"AudioClip::Stop^0", API_FN_PAIR(AudioClip_Stop)},
{"AudioClip::get_ID", API_FN_PAIR(AudioClip_GetID)},
{"AudioClip::get_FileType", API_FN_PAIR(AudioClip_GetFileType)},
{"AudioClip::get_IsAvailable", API_FN_PAIR(AudioClip_GetIsAvailable)},
{"AudioClip::get_ScriptName", API_FN_PAIR(AudioClip_GetScriptName)},
{"AudioClip::get_Type", API_FN_PAIR(AudioClip_GetType)},
};
ccAddExternalFunctions361(audioclip_api);
}
} // namespace AGS3

View File

@@ -0,0 +1,41 @@
/* 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 AGS_ENGINE_AC_AUDIO_CLIP_H
#define AGS_ENGINE_AC_AUDIO_CLIP_H
#include "ags/shared/ac/dynobj/script_audio_clip.h"
#include "ags/engine/ac/dynobj/script_audio_channel.h"
namespace AGS3 {
int AudioClip_GetFileType(ScriptAudioClip *clip);
int AudioClip_GetType(ScriptAudioClip *clip);
int AudioClip_GetIsAvailable(ScriptAudioClip *clip);
void AudioClip_Stop(ScriptAudioClip *clip);
ScriptAudioChannel *AudioClip_Play(ScriptAudioClip *clip, int priority, int repeat);
ScriptAudioChannel *AudioClip_PlayFrom(ScriptAudioClip *clip, int position, int priority, int repeat);
ScriptAudioChannel *AudioClip_PlayQueued(ScriptAudioClip *clip, int priority, int repeat);
ScriptAudioChannel *AudioClip_PlayOnChannel(ScriptAudioClip *clip, int chan, int priority, int repeat);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,455 @@
/* 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 "ags/engine/ac/button.h"
#include "ags/shared/ac/common.h"
#include "ags/engine/ac/gui.h"
#include "ags/shared/ac/view.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/engine/ac/global_translation.h"
#include "ags/engine/ac/object.h"
#include "ags/engine/ac/string.h"
#include "ags/engine/ac/view_frame.h"
#include "ags/engine/debugging/debug_log.h"
#include "ags/engine/gui/animating_gui_button.h"
#include "ags/shared/gui/gui_main.h"
#include "ags/shared/debugging/out.h"
#include "ags/engine/script/script_api.h"
#include "ags/engine/script/script_runtime.h"
#include "ags/engine/ac/dynobj/script_string.h"
#include "ags/engine/main/game_run.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
// *** BUTTON FUNCTIONS
// Update the actual button's image from the current animation frame
void UpdateButtonState(const AnimatingGUIButton &abtn) {
// Assign view frame as normal image and reset all the rest
_GP(guibuts)[abtn.buttonid].SetImages(_GP(views)[abtn.view].loops[abtn.loop].frames[abtn.frame].pic, 0, 0);
}
void Button_Animate(GUIButton *butt, int view, int loop, int speed, int repeat, int blocking, int direction, int sframe, int volume) {
int guin = butt->ParentId;
int objn = butt->Id;
view--; // convert to internal 0-based view ID
ValidateViewAnimVLF("Button.Animate", view, loop, sframe);
ValidateViewAnimParams("Button.Animate", repeat, blocking, direction);
volume = Math::Clamp(volume, 0, 100);
// if it's already animating, stop it
FindAndRemoveButtonAnimation(guin, objn);
int but_id = _GP(guis)[guin].GetControlID(objn);
AnimatingGUIButton abtn;
abtn.ongui = guin;
abtn.onguibut = objn;
abtn.buttonid = but_id;
abtn.view = view;
abtn.loop = loop;
abtn.speed = speed;
abtn.repeat = (repeat != 0) ? ANIM_REPEAT : ANIM_ONCE; // for now, clamp to supported modes
abtn.blocking = blocking;
abtn.direction = direction;
abtn.frame = SetFirstAnimFrame(view, loop, sframe, direction);
abtn.wait = abtn.speed + _GP(views)[abtn.view].loops[abtn.loop].frames[abtn.frame].speed;
abtn.volume = volume;
_GP(animbuts).push_back(abtn);
// launch into the first frame, and play the first frame's sound
UpdateButtonState(abtn);
CheckViewFrame(abtn.view, abtn.loop, abtn.frame);
// Blocking animate
if (blocking)
GameLoopUntilButAnimEnd(guin, objn);
}
void Button_Animate4(GUIButton *butt, int view, int loop, int speed, int repeat) {
Button_Animate(butt, view, loop, speed, repeat, IN_BACKGROUND, FORWARDS, 0, 100 /* full volume */);
}
void Button_Animate7(GUIButton *butt, int view, int loop, int speed, int repeat, int blocking, int direction, int sframe) {
Button_Animate(butt, view, loop, speed, repeat, blocking, direction, sframe, 100 /* full volume */);
}
const char *Button_GetText_New(GUIButton *butt) {
return CreateNewScriptString(butt->GetText().GetCStr());
}
void Button_GetText(GUIButton *butt, char *buffer) {
snprintf(buffer, MAX_MAXSTRLEN, "%s", butt->GetText().GetCStr());
}
void Button_SetText(GUIButton *butt, const char *newtx) {
newtx = get_translation(newtx);
if (butt->GetText() != newtx) {
butt->SetText(newtx);
}
}
void Button_SetFont(GUIButton *butt, int newFont) {
if ((newFont < 0) || (newFont >= _GP(game).numfonts))
quit("!Button.Font: invalid font number.");
if (butt->Font != newFont) {
butt->Font = newFont;
butt->MarkChanged();
}
}
int Button_GetFont(GUIButton *butt) {
return butt->Font;
}
int Button_GetClipImage(GUIButton *butt) {
return butt->IsClippingImage() ? 1 : 0;
}
void Button_SetClipImage(GUIButton *butt, int newval) {
if (butt->IsClippingImage() != (newval != 0)) {
butt->SetClipImage(newval != 0);
}
}
int Button_GetGraphic(GUIButton *butt) {
// return currently displayed pic
if (butt->GetCurrentImage() < 0)
return butt->GetNormalImage();
return butt->GetCurrentImage();
}
int Button_GetMouseOverGraphic(GUIButton *butt) {
return butt->GetMouseOverImage();
}
void Button_SetMouseOverGraphic(GUIButton *guil, int slotn) {
debug_script_log("GUI %d Button %d mouseover set to slot %d", guil->ParentId, guil->Id, slotn);
slotn = std::max(0, slotn);
guil->SetMouseOverImage(slotn);
FindAndRemoveButtonAnimation(guil->ParentId, guil->Id);
}
int Button_GetNormalGraphic(GUIButton *butt) {
return butt->GetNormalImage();
}
void Button_SetNormalGraphic(GUIButton *butt, int slotn) {
debug_script_log("GUI %d Button %d normal set to slot %d", butt->ParentId, butt->Id, slotn);
slotn = std::max(0, slotn);
// NormalGraphic = 0 will turn the Button into a standard colored button
if (slotn == 0) {
butt->SetNormalImage(slotn);
}
// Any other sprite - update the clickable area to the same size as the graphic
else {
const int width = static_cast<size_t>(slotn) < _GP(game).SpriteInfos.size() ? _GP(game).SpriteInfos[slotn].Width : 0;
const int height = static_cast<size_t>(slotn) < _GP(game).SpriteInfos.size() ? _GP(game).SpriteInfos[slotn].Height : 0;
butt->SetNormalImage(slotn);
butt->SetSize(width, height);
}
FindAndRemoveButtonAnimation(butt->ParentId, butt->Id);
}
int Button_GetPushedGraphic(GUIButton *butt) {
return butt->GetPushedImage();
}
void Button_SetPushedGraphic(GUIButton *guil, int slotn) {
debug_script_log("GUI %d Button %d pushed set to slot %d", guil->ParentId, guil->Id, slotn);
slotn = std::max(0, slotn);
guil->SetPushedImage(slotn);
FindAndRemoveButtonAnimation(guil->ParentId, guil->Id);
}
int Button_GetTextColor(GUIButton *butt) {
return butt->TextColor;
}
void Button_SetTextColor(GUIButton *butt, int newcol) {
if (butt->TextColor != newcol) {
butt->TextColor = newcol;
butt->MarkChanged();
}
}
// ** start animating buttons code
size_t GetAnimatingButtonCount() {
return _GP(animbuts).size();
}
AnimatingGUIButton *GetAnimatingButtonByIndex(int idxn) {
return idxn >= 0 && (size_t)idxn < _GP(animbuts).size() ?
&_GP(animbuts)[idxn] : nullptr;
}
void AddButtonAnimation(const AnimatingGUIButton &abtn) {
_GP(animbuts).push_back(abtn);
}
// returns 1 if animation finished
bool UpdateAnimatingButton(int bu) {
AnimatingGUIButton &abtn = _GP(animbuts)[bu];
if (abtn.wait > 0) {
abtn.wait--;
return true;
}
if (!CycleViewAnim(abtn.view, abtn.loop, abtn.frame, !abtn.direction, abtn.repeat))
return false;
CheckViewFrame(abtn.view, abtn.loop, abtn.frame, abtn.volume);
abtn.wait = abtn.speed + _GP(views)[abtn.view].loops[abtn.loop].frames[abtn.frame].speed;
UpdateButtonState(abtn);
return true;
}
void StopButtonAnimation(int idxn) {
_GP(animbuts).erase(_GP(animbuts).begin() + idxn);
}
void RemoveAllButtonAnimations() {
_GP(animbuts).clear();
}
// Returns the index of the AnimatingGUIButton object corresponding to the
// given button ID; returns -1 if no such animation exists
int FindButtonAnimation(int guin, int objn) {
for (size_t i = 0; i < _GP(animbuts).size(); ++i) {
if (_GP(animbuts)[i].ongui == guin && _GP(animbuts)[i].onguibut == objn)
return i;
}
return -1;
}
void FindAndRemoveButtonAnimation(int guin, int objn) {
int idx = FindButtonAnimation(guin, objn);
if (idx >= 0)
StopButtonAnimation(idx);
}
// ** end animating buttons code
void Button_Click(GUIButton *butt, int mbut) {
process_interface_click(butt->ParentId, butt->Id, mbut);
}
bool Button_IsAnimating(GUIButton *butt) {
return FindButtonAnimation(butt->ParentId, butt->Id) >= 0;
}
// NOTE: in correspondance to similar functions for Character & Object,
// GetView returns (view index + 1), while GetLoop and GetFrame return
// zero-based index and 0 in case of no animation.
int Button_GetAnimView(GUIButton *butt) {
int idx = FindButtonAnimation(butt->ParentId, butt->Id);
return idx >= 0 ? _GP(animbuts)[idx].view + 1 : 0;
}
int Button_GetAnimLoop(GUIButton *butt) {
int idx = FindButtonAnimation(butt->ParentId, butt->Id);
return idx >= 0 ? _GP(animbuts)[idx].loop : 0;
}
int Button_GetAnimFrame(GUIButton *butt) {
int idx = FindButtonAnimation(butt->ParentId, butt->Id);
return idx >= 0 ? _GP(animbuts)[idx].frame : 0;
}
int Button_GetTextAlignment(GUIButton *butt) {
return butt->TextAlignment;
}
void Button_SetTextAlignment(GUIButton *butt, int align) {
if (butt->TextAlignment != align) {
butt->TextAlignment = (FrameAlignment)align;
butt->MarkChanged();
}
}
//=============================================================================
//
// Script API Functions
//
//=============================================================================
// void | GUIButton *butt, int view, int loop, int speed, int repeat
RuntimeScriptValue Sc_Button_Animate4(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT4(GUIButton, Button_Animate4);
}
RuntimeScriptValue Sc_Button_Animate7(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT7(GUIButton, Button_Animate7);
}
RuntimeScriptValue Sc_Button_Animate(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT8(GUIButton, Button_Animate);
}
// const char* | GUIButton *butt
RuntimeScriptValue Sc_Button_GetText_New(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_OBJ(GUIButton, const char, _GP(myScriptStringImpl), Button_GetText_New);
}
// void | GUIButton *butt, char *buffer
RuntimeScriptValue Sc_Button_GetText(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_POBJ(GUIButton, Button_GetText, char);
}
// void | GUIButton *butt, const char *newtx
RuntimeScriptValue Sc_Button_SetText(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_POBJ(GUIButton, Button_SetText, const char);
}
// void | GUIButton *butt, int newFont
RuntimeScriptValue Sc_Button_SetFont(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(GUIButton, Button_SetFont);
}
// int | GUIButton *butt
RuntimeScriptValue Sc_Button_GetFont(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(GUIButton, Button_GetFont);
}
// int | GUIButton *butt
RuntimeScriptValue Sc_Button_GetClipImage(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(GUIButton, Button_GetClipImage);
}
// void | GUIButton *butt, int newval
RuntimeScriptValue Sc_Button_SetClipImage(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(GUIButton, Button_SetClipImage);
}
// int | GUIButton *butt
RuntimeScriptValue Sc_Button_GetGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(GUIButton, Button_GetGraphic);
}
// int | GUIButton *butt
RuntimeScriptValue Sc_Button_GetMouseOverGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(GUIButton, Button_GetMouseOverGraphic);
}
// void | GUIButton *guil, int slotn
RuntimeScriptValue Sc_Button_SetMouseOverGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(GUIButton, Button_SetMouseOverGraphic);
}
// int | GUIButton *butt
RuntimeScriptValue Sc_Button_GetNormalGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(GUIButton, Button_GetNormalGraphic);
}
// void | GUIButton *guil, int slotn
RuntimeScriptValue Sc_Button_SetNormalGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(GUIButton, Button_SetNormalGraphic);
}
// int | GUIButton *butt
RuntimeScriptValue Sc_Button_GetPushedGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(GUIButton, Button_GetPushedGraphic);
}
// void | GUIButton *guil, int slotn
RuntimeScriptValue Sc_Button_SetPushedGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(GUIButton, Button_SetPushedGraphic);
}
// int | GUIButton *butt
RuntimeScriptValue Sc_Button_GetTextColor(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(GUIButton, Button_GetTextColor);
}
// void | GUIButton *butt, int newcol
RuntimeScriptValue Sc_Button_SetTextColor(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(GUIButton, Button_SetTextColor);
}
RuntimeScriptValue Sc_Button_Click(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(GUIButton, Button_Click);
}
RuntimeScriptValue Sc_Button_IsAnimating(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_BOOL(GUIButton, Button_IsAnimating);
}
RuntimeScriptValue Sc_Button_GetTextAlignment(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(GUIButton, Button_GetTextAlignment);
}
RuntimeScriptValue Sc_Button_SetTextAlignment(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(GUIButton, Button_SetTextAlignment);
}
RuntimeScriptValue Sc_Button_GetAnimFrame(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(GUIButton, Button_GetAnimFrame);
}
RuntimeScriptValue Sc_Button_GetAnimLoop(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(GUIButton, Button_GetAnimLoop);
}
RuntimeScriptValue Sc_Button_GetAnimView(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(GUIButton, Button_GetAnimView);
}
void RegisterButtonAPI() {
ScFnRegister button_api[] = {
{"Button::Animate^4", API_FN_PAIR(Button_Animate4)},
{"Button::Animate^7", API_FN_PAIR(Button_Animate7)},
{"Button::Animate^8", API_FN_PAIR(Button_Animate)},
{"Button::Click^1", API_FN_PAIR(Button_Click)},
{"Button::GetText^1", API_FN_PAIR(Button_GetText)},
{"Button::SetText^1", API_FN_PAIR(Button_SetText)},
{"Button::get_TextAlignment", API_FN_PAIR(Button_GetTextAlignment)},
{"Button::set_TextAlignment", API_FN_PAIR(Button_SetTextAlignment)},
{"Button::get_Animating", API_FN_PAIR(Button_IsAnimating)},
{"Button::get_ClipImage", API_FN_PAIR(Button_GetClipImage)},
{"Button::set_ClipImage", API_FN_PAIR(Button_SetClipImage)},
{"Button::get_Font", API_FN_PAIR(Button_GetFont)},
{"Button::set_Font", API_FN_PAIR(Button_SetFont)},
{"Button::get_Frame", API_FN_PAIR(Button_GetAnimFrame)},
{"Button::get_Graphic", API_FN_PAIR(Button_GetGraphic)},
{"Button::get_Loop", API_FN_PAIR(Button_GetAnimLoop)},
{"Button::get_MouseOverGraphic", API_FN_PAIR(Button_GetMouseOverGraphic)},
{"Button::set_MouseOverGraphic", API_FN_PAIR(Button_SetMouseOverGraphic)},
{"Button::get_NormalGraphic", API_FN_PAIR(Button_GetNormalGraphic)},
{"Button::set_NormalGraphic", API_FN_PAIR(Button_SetNormalGraphic)},
{"Button::get_PushedGraphic", API_FN_PAIR(Button_GetPushedGraphic)},
{"Button::set_PushedGraphic", API_FN_PAIR(Button_SetPushedGraphic)},
{"Button::get_Text", API_FN_PAIR(Button_GetText_New)},
{"Button::set_Text", API_FN_PAIR(Button_SetText)},
{"Button::get_TextColor", API_FN_PAIR(Button_GetTextColor)},
{"Button::set_TextColor", API_FN_PAIR(Button_SetTextColor)},
{"Button::get_View", API_FN_PAIR(Button_GetAnimView)},
};
ccAddExternalFunctions361(button_api);
}
} // namespace AGS3

View 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/>.
*
*/
#ifndef AGS_ENGINE_AC_BUTTON_H
#define AGS_ENGINE_AC_BUTTON_H
#include "ags/globals.h"
#include "ags/shared/gui/gui_button.h"
namespace AGS3 {
using AGS::Shared::GUIButton;
struct AnimatingGUIButton;
void Button_Animate(GUIButton *butt, int view, int loop, int speed, int repeat, int blocking, int direction, int sframe = 0, int volume = 100);
void Button_Animate4(GUIButton *butt, int view, int loop, int speed, int repeat);
const char *Button_GetText_New(GUIButton *butt);
void Button_GetText(GUIButton *butt, char *buffer);
void Button_SetText(GUIButton *butt, const char *newtx);
void Button_SetFont(GUIButton *butt, int newFont);
int Button_GetFont(GUIButton *butt);
int Button_GetClipImage(GUIButton *butt);
void Button_SetClipImage(GUIButton *butt, int newval);
int Button_GetGraphic(GUIButton *butt);
int Button_GetMouseOverGraphic(GUIButton *butt);
void Button_SetMouseOverGraphic(GUIButton *guil, int slotn);
int Button_GetNormalGraphic(GUIButton *butt);
void Button_SetNormalGraphic(GUIButton *guil, int slotn);
int Button_GetPushedGraphic(GUIButton *butt);
void Button_SetPushedGraphic(GUIButton *guil, int slotn);
int Button_GetTextColor(GUIButton *butt);
void Button_SetTextColor(GUIButton *butt, int newcol);
// Update button's animation, returns whether the animation continues
bool UpdateAnimatingButton(int bu);
size_t GetAnimatingButtonCount();
AnimatingGUIButton *GetAnimatingButtonByIndex(int idxn);
void AddButtonAnimation(const AnimatingGUIButton &abtn);
void StopButtonAnimation(int idxn);
int FindButtonAnimation(int guin, int objn);
void FindAndRemoveButtonAnimation(int guin, int objn);
void RemoveAllButtonAnimations();
} // namespace AGS3
#endif

View File

@@ -0,0 +1,44 @@
/* 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 "ags/engine/ac/cd_audio.h"
#include "ags/engine/platform/base/ags_platform_driver.h"
#include "ags/globals.h"
namespace AGS3 {
int init_cd_player() {
_G(use_cdplayer) = 0;
return _G(platform)->InitializeCDPlayer();
}
int cd_manager(int cmdd, int datt) {
if (!_G(triedToUseCdAudioCommand)) {
_G(triedToUseCdAudioCommand) = true;
init_cd_player();
}
if (cmdd == 0) return _G(use_cdplayer);
if (_G(use_cdplayer) == 0) return 0; // ignore other commands
return _G(platform)->CDPlayerCommand(cmdd, datt);
}
} // namespace AGS3

View File

@@ -0,0 +1,39 @@
/* 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 AGS_ENGINE_AC_CDAUDIO_H
#define AGS_ENGINE_AC_CDAUDIO_H
namespace AGS3 {
// CD Player functions
// flags returned with cd_getstatus
#define CDS_DRIVEOPEN 0x0001 // tray is open
#define CDS_DRIVELOCKED 0x0002 // tray locked shut by software
#define CDS_AUDIOSUPPORT 0x0010 // supports audio CDs
#define CDS_DRIVEEMPTY 0x0800 // no CD in drive
int init_cd_player();
int cd_manager(int cmdd, int datt);
} // namespace AGS3
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,247 @@
/* 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 AGS_ENGINE_AC_CHARACTER_H
#define AGS_ENGINE_AC_CHARACTER_H
#include "ags/shared/ac/character_info.h"
#include "ags/engine/ac/character_extras.h"
#include "ags/engine/ac/dynobj/script_object.h"
#include "ags/engine/ac/dynobj/script_inv_item.h"
#include "ags/engine/ac/dynobj/script_overlay.h"
#include "ags/engine/game/viewport.h"
#include "ags/shared/util/geometry.h"
namespace AGS3 {
// **** CHARACTER: FUNCTIONS ****
bool is_valid_character(int char_id);
// Asserts the character ID is valid,
// if not then prints a warning to the log; returns assertion result
bool AssertCharacter(const char *apiname, int char_id);
void Character_AddInventory(CharacterInfo *chaa, ScriptInvItem *invi, int addIndex);
void Character_AddWaypoint(CharacterInfo *chaa, int x, int y);
void Character_Animate(CharacterInfo *chaa, int loop, int delay, int repeat, int blocking, int direction, int sframe = 0, int volume = 100);
void Character_Animate5(CharacterInfo *chaa, int loop, int delay, int repeat, int blocking, int direction);
void Character_ChangeRoomAutoPosition(CharacterInfo *chaa, int room, int newPos);
void Character_ChangeRoom(CharacterInfo *chaa, int room, int x, int y);
void Character_ChangeRoomSetLoop(CharacterInfo *chaa, int room, int x, int y, int direction);
void Character_ChangeView(CharacterInfo *chap, int vii);
void Character_FaceDirection(CharacterInfo *char1, int direction, int blockingStyle);
void Character_FaceCharacter(CharacterInfo *char1, CharacterInfo *char2, int blockingStyle);
void Character_FaceLocation(CharacterInfo *char1, int xx, int yy, int blockingStyle);
void Character_FaceObject(CharacterInfo *char1, ScriptObject *obj, int blockingStyle);
void Character_FollowCharacter(CharacterInfo *chaa, CharacterInfo *tofollow, int distaway, int eagerness);
int Character_IsCollidingWithChar(CharacterInfo *char1, CharacterInfo *char2);
int Character_IsCollidingWithObject(CharacterInfo *chin, ScriptObject *objid);
bool Character_IsInteractionAvailable(CharacterInfo *cchar, int mood);
void Character_LockView(CharacterInfo *chap, int vii);
void Character_LockViewEx(CharacterInfo *chap, int vii, int stopMoving);
void Character_LockViewAligned(CharacterInfo *chap, int vii, int loop, int align);
void Character_LockViewAligned_Old(CharacterInfo *chap, int vii, int loop, int align);
void Character_LockViewAlignedEx(CharacterInfo *chap, int vii, int loop, int align, int stopMoving);
void Character_LockViewAlignedEx_Old(CharacterInfo *chap, int vii, int loop, int align, int stopMoving);
void Character_LockViewFrame(CharacterInfo *chaa, int view, int loop, int frame);
void Character_LockViewFrameEx(CharacterInfo *chaa, int view, int loop, int frame, int stopMoving);
void Character_LockViewOffset(CharacterInfo *chap, int vii, int xoffs, int yoffs);
void Character_LockViewOffsetEx(CharacterInfo *chap, int vii, int xoffs, int yoffs, int stopMoving);
void Character_LoseInventory(CharacterInfo *chap, ScriptInvItem *invi);
void Character_PlaceOnWalkableArea(CharacterInfo *chap);
void Character_RemoveTint(CharacterInfo *chaa);
int Character_GetHasExplicitTint(CharacterInfo *chaa);
int Character_GetHasExplicitTint_Old(CharacterInfo *ch);
void Character_Say(CharacterInfo *chaa, const char *text);
void Character_SayAt(CharacterInfo *chaa, int x, int y, int width, const char *texx);
ScriptOverlay *Character_SayBackground(CharacterInfo *chaa, const char *texx);
void Character_SetAsPlayer(CharacterInfo *chaa);
void Character_SetIdleView(CharacterInfo *chaa, int iview, int itime);
void Character_SetOption(CharacterInfo *chaa, int flag, int yesorno);
bool Character_SetProperty(CharacterInfo *chaa, const char *property, int value);
bool Character_SetTextProperty(CharacterInfo *chaa, const char *property, const char *value);
void Character_SetSpeed(CharacterInfo *chaa, int xspeed, int yspeed);
void Character_StopMoving(CharacterInfo *charp);
void Character_Tint(CharacterInfo *chaa, int red, int green, int blue, int opacity, int luminance);
void Character_Think(CharacterInfo *chaa, const char *text);
void Character_UnlockView(CharacterInfo *chaa);
void Character_UnlockViewEx(CharacterInfo *chaa, int stopMoving);
void Character_Walk(CharacterInfo *chaa, int x, int y, int blocking, int direct);
void Character_Move(CharacterInfo *chaa, int x, int y, int blocking, int direct);
void Character_WalkStraight(CharacterInfo *chaa, int xx, int yy, int blocking);
void Character_RunInteraction(CharacterInfo *chaa, int mood);
// **** CHARACTER: PROPERTIES ****
int Character_GetProperty(CharacterInfo *chaa, const char *property);
void Character_GetPropertyText(CharacterInfo *chaa, const char *property, char *bufer);
const char *Character_GetTextProperty(CharacterInfo *chaa, const char *property);
ScriptInvItem *Character_GetActiveInventory(CharacterInfo *chaa);
void Character_SetActiveInventory(CharacterInfo *chaa, ScriptInvItem *iit);
int Character_GetAnimating(CharacterInfo *chaa);
int Character_GetAnimationSpeed(CharacterInfo *chaa);
void Character_SetAnimationSpeed(CharacterInfo *chaa, int newval);
int Character_GetBaseline(CharacterInfo *chaa);
void Character_SetBaseline(CharacterInfo *chaa, int basel);
int Character_GetBlinkInterval(CharacterInfo *chaa);
void Character_SetBlinkInterval(CharacterInfo *chaa, int interval);
int Character_GetBlinkView(CharacterInfo *chaa);
void Character_SetBlinkView(CharacterInfo *chaa, int vii);
int Character_GetBlinkWhileThinking(CharacterInfo *chaa);
void Character_SetBlinkWhileThinking(CharacterInfo *chaa, int yesOrNo);
int Character_GetBlockingHeight(CharacterInfo *chaa);
void Character_SetBlockingHeight(CharacterInfo *chaa, int hit);
int Character_GetBlockingWidth(CharacterInfo *chaa);
void Character_SetBlockingWidth(CharacterInfo *chaa, int wid);
int Character_GetDiagonalWalking(CharacterInfo *chaa);
void Character_SetDiagonalWalking(CharacterInfo *chaa, int yesorno);
int Character_GetClickable(CharacterInfo *chaa);
void Character_SetClickable(CharacterInfo *chaa, int clik);
int Character_GetDestinationX(CharacterInfo *chaa);
int Character_GetDestinationY(CharacterInfo *chaa);
int Character_GetID(CharacterInfo *chaa);
int Character_GetFrame(CharacterInfo *chaa);
void Character_SetFrame(CharacterInfo *chaa, int newval);
int Character_GetIdleView(CharacterInfo *chaa);
int Character_GetIInventoryQuantity(CharacterInfo *chaa, int index);
int Character_HasInventory(CharacterInfo *chaa, ScriptInvItem *invi);
void Character_SetIInventoryQuantity(CharacterInfo *chaa, int index, int quant);
int Character_GetIgnoreLighting(CharacterInfo *chaa);
void Character_SetIgnoreLighting(CharacterInfo *chaa, int yesorno);
int Character_GetIgnoreScaling(CharacterInfo *chaa);
void Character_SetIgnoreScaling(CharacterInfo *chaa, int yesorno);
void Character_SetManualScaling(CharacterInfo *chaa, int yesorno);
int Character_GetIgnoreWalkbehinds(CharacterInfo *chaa);
void Character_SetIgnoreWalkbehinds(CharacterInfo *chaa, int yesorno);
int Character_GetMovementLinkedToAnimation(CharacterInfo *chaa);
void Character_SetMovementLinkedToAnimation(CharacterInfo *chaa, int yesorno);
int Character_GetLoop(CharacterInfo *chaa);
void Character_SetLoop(CharacterInfo *chaa, int newval);
int Character_GetMoving(CharacterInfo *chaa);
const char *Character_GetName(CharacterInfo *chaa);
void Character_SetName(CharacterInfo *chaa, const char *newName);
int Character_GetNormalView(CharacterInfo *chaa);
int Character_GetPreviousRoom(CharacterInfo *chaa);
int Character_GetRoom(CharacterInfo *chaa);
int Character_GetScaleMoveSpeed(CharacterInfo *chaa);
void Character_SetScaleMoveSpeed(CharacterInfo *chaa, int yesorno);
int Character_GetScaleVolume(CharacterInfo *chaa);
void Character_SetScaleVolume(CharacterInfo *chaa, int yesorno);
int Character_GetScaling(CharacterInfo *chaa);
void Character_SetScaling(CharacterInfo *chaa, int zoomlevel);
int Character_GetSolid(CharacterInfo *chaa);
void Character_SetSolid(CharacterInfo *chaa, int yesorno);
int Character_GetSpeaking(CharacterInfo *chaa);
int Character_GetSpeechColor(CharacterInfo *chaa);
void Character_SetSpeechColor(CharacterInfo *chaa, int ncol);
void Character_SetSpeechAnimationDelay(CharacterInfo *chaa, int newDelay);
int Character_GetSpeechView(CharacterInfo *chaa);
void Character_SetSpeechView(CharacterInfo *chaa, int vii);
int Character_GetThinkView(CharacterInfo *chaa);
void Character_SetThinkView(CharacterInfo *chaa, int vii);
int Character_GetTransparency(CharacterInfo *chaa);
void Character_SetTransparency(CharacterInfo *chaa, int trans);
int Character_GetTurnBeforeWalking(CharacterInfo *chaa);
void Character_SetTurnBeforeWalking(CharacterInfo *chaa, int yesorno);
int Character_GetView(CharacterInfo *chaa);
int Character_GetWalkSpeedX(CharacterInfo *chaa);
int Character_GetWalkSpeedY(CharacterInfo *chaa);
int Character_GetX(CharacterInfo *chaa);
void Character_SetX(CharacterInfo *chaa, int newval);
int Character_GetY(CharacterInfo *chaa);
void Character_SetY(CharacterInfo *chaa, int newval);
int Character_GetZ(CharacterInfo *chaa);
void Character_SetZ(CharacterInfo *chaa, int newval);
int Character_GetSpeakingFrame(CharacterInfo *chaa);
//=============================================================================
struct MoveList;
namespace AGS {
namespace Shared {
class Bitmap;
}
}
using namespace AGS; // FIXME later
// Configures and starts character animation.
void animate_character(CharacterInfo *chap, int loopn, int sppd, int rept, int direction = 0, int sframe = 0, int volume = 100);
// Clears up animation parameters
void stop_character_anim(CharacterInfo *chap);
void walk_character(int chac, int tox, int toy, int ignwal, bool autoWalkAnims);
int find_looporder_index(int curloop);
// returns 0 to use diagonal, 1 to not
int useDiagonal(CharacterInfo *char1);
// returns 1 normally, or 0 if they only have horizontal animations
int hasUpDownLoops(CharacterInfo *char1);
void start_character_turning(CharacterInfo *chinf, int useloop, int no_diagonal);
void fix_player_sprite(MoveList *cmls, CharacterInfo *chinf);
// Check whether two characters have walked into each other
int has_hit_another_character(int sourceChar);
int doNextCharMoveStep(CharacterInfo *chi, int &char_index, CharacterExtras *chex);
// Tells if character is currently moving, in eWalkableAreas mode
bool is_char_walking_ndirect(CharacterInfo *chi);
int find_nearest_walkable_area_within(int *xx, int *yy, int range, int step);
void find_nearest_walkable_area(int *xx, int *yy);
void FindReasonableLoopForCharacter(CharacterInfo *chap);
void walk_or_move_character(CharacterInfo *chaa, int x, int y, int blocking, int direct, bool isWalk);
int wantMoveNow(CharacterInfo *chi, CharacterExtras *chex);
void setup_player_character(int charid);
int GetCharacterFrameVolume(CharacterInfo *chi);
Shared::Bitmap *GetCharacterImage(int charid, bool *is_original = nullptr);
CharacterInfo *GetCharacterAtScreen(int xx, int yy);
// Deduces room object's scale, accounting for both manual scaling and the room region effects;
// calculates resulting sprite size.
void update_character_scale(int charid);
CharacterInfo *GetCharacterAtRoom(int x, int y);
// Get character ID at the given room coordinates
int is_pos_on_character(int xx, int yy);
void get_char_blocking_rect(int charid, int *x1, int *y1, int *width, int *y2);
// Check whether the source char is standing inside otherChar's blocking rectangle
int is_char_in_blocking_rect(int sourceChar, int otherChar, int *fromxptr, int *cwidptr);
int my_getpixel(Shared::Bitmap *blk, int x, int y);
int check_click_on_character(int xx, int yy, int mood);
void _DisplaySpeechCore(int chid, const char *displbuf);
void _DisplayThoughtCore(int chid, const char *displbuf);
void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int isThought);
int get_character_currently_talking();
void DisplaySpeech(const char *texx, int aschar);
int update_lip_sync(int talkview, int talkloop, int *talkframeptr);
// Recalculate dynamic character properties, e.g. after restoring a game save
void restore_characters();
// Calculates character's bounding box in room coordinates (takes only in-room transform into account)
// use_frame_0 optionally tells to use frame 0 of current loop instead of current frame.
Rect GetCharacterRoomBBox(int charid, bool use_frame_0 = false);
// Find a closest viewport given character is to. Checks viewports in their order in game's array,
// and returns either first viewport character's bounding box intersects with (or rather with its camera),
// or the one that is least far away from its camera; calculated as a perpendicular distance between two AABBs.
PViewport FindNearestViewport(int charid);
// order of loops to turn character in circle from down to down
extern int turnlooporder[8];
} // namespace AGS3
#endif

View File

@@ -0,0 +1,91 @@
/* 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 "ags/engine/ac/character_extras.h"
#include "ags/engine/ac/view_frame.h"
#include "ags/shared/util/stream.h"
#include "ags/shared/util/string_utils.h"
namespace AGS3 {
using namespace AGS::Shared;
int CharacterExtras::GetEffectiveY(CharacterInfo *chi) const {
return chi->y - (chi->z * zoom_offs) / 100;
}
int CharacterExtras::GetFrameSoundVolume(CharacterInfo *chi) const {
return AGS3::CalcFrameSoundVolume(
anim_volume, cur_anim_volume,
(chi->flags & CHF_SCALEVOLUME) ? zoom : 100);
}
void CharacterExtras::CheckViewFrame(CharacterInfo *chi) {
AGS3::CheckViewFrame(chi->view, chi->loop, chi->frame, GetFrameSoundVolume(chi));
}
void CharacterExtras::ReadFromSavegame(Stream *in, CharacterSvgVersion save_ver) {
in->ReadArrayOfInt16(invorder, MAX_INVORDER);
invorder_count = in->ReadInt16();
width = in->ReadInt16();
height = in->ReadInt16();
zoom = in->ReadInt16();
xwas = in->ReadInt16();
ywas = in->ReadInt16();
tint_r = in->ReadInt16();
tint_g = in->ReadInt16();
tint_b = in->ReadInt16();
tint_level = in->ReadInt16();
tint_light = in->ReadInt16();
process_idle_this_time = in->ReadInt8();
slow_move_counter = in->ReadInt8();
animwait = in->ReadInt16();
if (save_ver >= kCharSvgVersion_36025) {
anim_volume = static_cast<uint8_t>(in->ReadInt8());
cur_anim_volume = static_cast<uint8_t>(in->ReadInt8());
in->ReadInt8(); // reserved to fill int32
in->ReadInt8();
}
}
void CharacterExtras::WriteToSavegame(Stream *out) const {
out->WriteArrayOfInt16(invorder, MAX_INVORDER);
out->WriteInt16(invorder_count);
out->WriteInt16(width);
out->WriteInt16(height);
out->WriteInt16(zoom);
out->WriteInt16(xwas);
out->WriteInt16(ywas);
out->WriteInt16(tint_r);
out->WriteInt16(tint_g);
out->WriteInt16(tint_b);
out->WriteInt16(tint_level);
out->WriteInt16(tint_light);
out->WriteInt8(process_idle_this_time);
out->WriteInt8(slow_move_counter);
out->WriteInt16(animwait);
out->WriteInt8(static_cast<uint8_t>(anim_volume));
out->WriteInt8(static_cast<uint8_t>(cur_anim_volume));
out->WriteInt8(0); // reserved to fill int32
out->WriteInt8(0);
}
} // namespace AGS3

View File

@@ -0,0 +1,95 @@
/* 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/>.
*
*/
//=============================================================================
//
// CharacterExtras is a separate runtime character data. Historically it was
// separated from the design-time CharacterInfo, because the latter is exposed
// to script API and plugin API in such way that its memory layout could not
// be changed at all. Although, today this is less of an issue (see comment
// to CharacterInfo).
//
// TODO: in the long run it will be beneficial to remake this into a more
// explicit runtime Character class, while perhaps keeping CharacterInfo only
// to load design-time data.
//
//=============================================================================
#ifndef AGS_ENGINE_AC_CHARACTER_EXTRAS_H
#define AGS_ENGINE_AC_CHARACTER_EXTRAS_H
#include "ags/shared/ac/character_info.h"
#include "ags/engine/ac/runtime_defines.h"
namespace AGS3 {
// Forward declaration
namespace AGS {
namespace Shared {
class Stream;
}
}
using namespace AGS; // FIXME later
// The CharacterInfo struct size is fixed because it's exposed to script
// and plugin API, therefore new stuff has to go here
struct CharacterExtras {
short invorder[MAX_INVORDER] = {};
short invorder_count = 0;
// TODO: implement full AABB and keep updated, so that engine could rely on these cached values all time = 0;
// TODO: consider having both fixed AABB and volatile one that changes with animation frame (unless you change how anims work)
short width = 0;
short height = 0;
short zoom = 100;
short xwas = 0;
short ywas = 0;
short tint_r = 0;
short tint_g = 0;
short tint_b = 0;
short tint_level = 0;
short tint_light = 0;
int8 process_idle_this_time = 0;
int8 slow_move_counter = 0;
short animwait = 0;
int anim_volume = 100; // default animation volume (relative factor)
int cur_anim_volume = 100; // current animation sound volume (relative factor)
// Following fields are deriatives of the above (calculated from these
// and other factors), and hence are not serialized.
//
// zoom factor of sprite offsets, fixed at 100 in backwards compatible mode
int zoom_offs = 100;
int GetEffectiveY(CharacterInfo *chi) const; // return Y - Z
// Calculate wanted frame sound volume based on multiple factors
int GetFrameSoundVolume(CharacterInfo *chi) const;
// Process the current animation frame for the character:
// play linked sounds, and so forth.
void CheckViewFrame(CharacterInfo *chi);
// Read character extra data from saves.
void ReadFromSavegame(Shared::Stream *in, CharacterSvgVersion save_ver);
void WriteToSavegame(Shared::Stream *out) const;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,468 @@
/* 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 "ags/shared/ac/character_info.h"
#include "ags/shared/ac/common.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/engine/ac/character.h"
#include "ags/engine/ac/character_extras.h"
#include "ags/engine/ac/game_state.h"
#include "ags/engine/ac/global_character.h"
#include "ags/engine/ac/global_game.h"
#include "ags/engine/ac/math.h"
#include "ags/engine/ac/object.h"
#include "ags/engine/ac/view_frame.h"
#include "ags/engine/debugging/debug_log.h"
#include "ags/shared/game/room_struct.h"
#include "ags/engine/main/update.h"
#include "ags/engine/media/audio/audio_system.h"
#include "ags/ags.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
#define Random __Rand
int CharacterInfo::get_baseline() const {
if (baseline < 1)
return y;
return baseline;
}
int CharacterInfo::get_blocking_top() const {
if (blocking_height > 0)
return y - blocking_height / 2;
return y - 2;
}
int CharacterInfo::get_blocking_bottom() const {
// the blocking_bottom should be 1 less than the top + height
// since the code does <= checks on it rather than < checks
if (blocking_height > 0)
return (y + (blocking_height + 1) / 2) - 1;
return y + 3;
}
void CharacterInfo::FixupCurrentLoopAndFrame() {
// If current loop property exceeds number of loops,
// or if selected loop has no frames, then try select any first loop that has frames.
// NOTE: although this may seem like a weird solution to a problem,
// we do so for backwards compatibility; this approximately emulates older games behavior.
if (view >= 0 &&
(loop >= _GP(views)[view].numLoops || _GP(views)[view].loops[loop].numFrames == 0)) {
for (loop = 0;
(loop < _GP(views)[view].numLoops) && (_GP(views)[view].loops[loop].numFrames == 0); ++loop) {
}
if (loop == _GP(views)[view].numLoops) {
// view has no frames?!
// amazingly enough there are old games that allow this to happen...
if (_G(loaded_game_file_version) >= kGameVersion_300)
quitprintf("!Character %s is assigned view %d that has no frames!", scrname, view);
loop = 0;
}
}
// If the last saved frame exceeds a new loop, then switch to frame 1
// (first walking frame) if walking, or frame 0 otherwise or if there's less than 2 frames.
int frames_in_loop = _GP(views)[view].loops[loop].numFrames;
if (frame >= frames_in_loop) {
frame = (walking > 0 && frames_in_loop > 1) ? 1 : 0;
}
}
void CharacterInfo::UpdateMoveAndAnim(int &char_index, CharacterExtras *chex, std::vector<int> &followingAsSheep) {
int res;
if (on != 1)
return;
// Turn around during walk
res = update_character_walkturning(chex);
// Fixup character's loop prior to any further logic updates
FixupCurrentLoopAndFrame();
// FIXME: refactor this nonsense!
// [IKM] Yes, it should return! upon getting RETURN_CONTINUE here
if (res == RETURN_CONTINUE) { // [IKM] now, this is one of those places...
return; // must be careful not to screw things up
}
int doing_nothing = 1;
update_character_moving(char_index, chex, doing_nothing);
// [IKM] 2012-06-28:
// Character index value is used to set up some variables in there, so I cannot just cease using it
res = update_character_animating(char_index, doing_nothing);
// [IKM] Yes, it should return! upon getting RETURN_CONTINUE here
if (res == RETURN_CONTINUE) { // [IKM] now, this is one of those places...
return; // must be careful not to screw things up
}
update_character_follower(char_index, followingAsSheep, doing_nothing);
update_character_idle(chex, doing_nothing);
chex->process_idle_this_time = 0;
}
void CharacterInfo::UpdateFollowingExactlyCharacter() {
x = _GP(game).chars[following].x;
y = _GP(game).chars[following].y;
z = _GP(game).chars[following].z;
room = _GP(game).chars[following].room;
prevroom = _GP(game).chars[following].prevroom;
int usebase = _GP(game).chars[following].get_baseline();
if (flags & CHF_BEHINDSHEPHERD)
baseline = usebase - 1;
else
baseline = usebase + 1;
}
int CharacterInfo::update_character_walkturning(CharacterExtras *chex) {
if (walking >= TURNING_AROUND) {
// Currently rotating to correct direction
if (walkwait > 0) walkwait--;
else {
// Work out which direction is next
int wantloop = find_looporder_index(loop) + 1;
// going anti-clockwise, take one before instead
if (walking >= TURNING_BACKWARDS)
wantloop -= 2;
while (1) {
if (wantloop >= 8)
wantloop = 0;
if (wantloop < 0)
wantloop = 7;
if ((turnlooporder[wantloop] >= _GP(views)[view].numLoops) ||
(_GP(views)[view].loops[turnlooporder[wantloop]].numFrames < 1) ||
((turnlooporder[wantloop] >= 4) && ((flags & CHF_NODIAGONAL) != 0))) {
if (walking >= TURNING_BACKWARDS)
wantloop--;
else
wantloop++;
} else break;
}
loop = turnlooporder[wantloop];
walking -= TURNING_AROUND;
// if still turning, wait for next frame
if (walking % TURNING_BACKWARDS >= TURNING_AROUND)
walkwait = animspeed;
else
walking = walking % TURNING_BACKWARDS;
chex->animwait = 0;
}
return RETURN_CONTINUE;
//continue;
}
return 0;
}
void CharacterInfo::update_character_moving(int &char_index, CharacterExtras *chex, int &doing_nothing) {
if ((walking > 0) && (room == _G(displayed_room))) {
if (walkwait > 0) walkwait--;
else {
flags &= ~CHF_AWAITINGMOVE;
// Move the character
int numSteps = wantMoveNow(this, chex);
if ((numSteps) && (chex->xwas != INVALID_X)) {
// if the zoom level changed mid-move, the walkcounter
// might not have come round properly - so sort it out
x = chex->xwas;
y = chex->ywas;
chex->xwas = INVALID_X;
}
int oldxp = x, oldyp = y;
for (int ff = 0; ff < abs(numSteps); ff++) {
if (doNextCharMoveStep(this, char_index, chex))
break;
if ((walking == 0) || (walking >= TURNING_AROUND))
break;
}
if (numSteps < 0) {
// very small scaling, intersperse the movement
// to stop it being jumpy
chex->xwas = x;
chex->ywas = y;
x = ((x) - oldxp) / 2 + oldxp;
y = ((y) - oldyp) / 2 + oldyp;
} else if (numSteps > 0)
chex->xwas = INVALID_X;
if ((flags & CHF_ANTIGLIDE) == 0)
walkwaitcounter++;
}
// Fixup character's loop, it may be changed when making a walk-move
FixupCurrentLoopAndFrame();
doing_nothing = 0; // still walking?
if (walking < 1) {
// Finished walking, stop and reset state
chex->process_idle_this_time = 1;
doing_nothing = 1;
walkwait = 0;
Character_StopMoving(this);
if ((flags & CHF_MOVENOTWALK) == 0) {
// use standing pic
chex->animwait = 0;
frame = 0;
chex->CheckViewFrame(this);
}
} else if (chex->animwait > 0) {
chex->animwait--;
} else {
if (flags & CHF_ANTIGLIDE)
walkwaitcounter++;
if ((flags & CHF_MOVENOTWALK) == 0) {
frame++;
if (frame >= _GP(views)[view].loops[loop].numFrames) {
// end of loop, so loop back round skipping the standing frame
frame = 1;
if (_GP(views)[view].loops[loop].numFrames < 2)
frame = 0;
}
chex->animwait = _GP(views)[view].loops[loop].frames[frame].speed + animspeed;
if (flags & CHF_ANTIGLIDE)
walkwait = chex->animwait;
else
walkwait = 0;
chex->CheckViewFrame(this);
}
}
}
}
int CharacterInfo::update_character_animating(int &aa, int &doing_nothing) {
CharacterExtras *chex = &_GP(charextra)[index_id];
// not moving, but animating
// idleleft is <0 while idle view is playing (.animating is 0)
if (((animating != 0) || (idleleft < 0)) &&
((walking == 0) || ((flags & CHF_MOVENOTWALK) != 0)) &&
(room == _G(displayed_room))) {
doing_nothing = 0;
// idle anim doesn't count as doing something
if (idleleft < 0)
doing_nothing = 1;
if (wait > 0) wait--;
else if ((_G(char_speaking) == aa) && (_GP(game).options[OPT_LIPSYNCTEXT] != 0)) {
// currently talking with lip-sync speech
int fraa = frame;
wait = update_lip_sync(view, loop, &fraa) - 1;
// closed mouth at end of sentence
// NOTE: standard lip-sync is synchronized with text timer, not voice file
if (_GP(play).speech_in_post_state ||
((_GP(play).messagetime >= 0) && (_GP(play).messagetime < _GP(play).close_mouth_speech_time)))
frame = 0;
if (frame != fraa) {
frame = fraa;
chex->CheckViewFrame(this);
}
//continue;
return RETURN_CONTINUE;
} else {
// Normal view animation
const int oldframe = frame;
bool done_anim = false;
if ((aa == _G(char_speaking)) &&
(_GP(play).speech_in_post_state ||
((!_GP(play).speech_has_voice) &&
(_GP(play).close_mouth_speech_time > 0) &&
(_GP(play).messagetime < _GP(play).close_mouth_speech_time)))) {
// finished talking - stop animation
done_anim = true;
frame = 0;
} else {
if (!CycleViewAnim(view, loop, frame, get_anim_forwards(), get_anim_repeat())) {
done_anim = true; // finished animating
// end of idle anim
if (idleleft < 0) {
// constant anim, reset (need this cos animating==0)
if (idletime == 0)
frame = 0;
// one-off anim, stop
else {
ReleaseCharacterView(aa);
idleleft = idletime;
}
}
}
}
wait = _GP(views)[view].loops[loop].frames[frame].speed;
// idle anim doesn't have speed stored cos animating==0 (TODO: investigate why?)
if (idleleft < 0)
wait += idle_anim_speed;
else
wait += get_anim_delay();
if (frame != oldframe)
chex->CheckViewFrame(this);
if (done_anim)
stop_character_anim(this);
}
}
return 0;
}
void CharacterInfo::update_character_follower(int &aa, std::vector<int> &followingAsSheep, int &doing_nothing) {
if ((following >= 0) && (followinfo == FOLLOW_ALWAYSONTOP)) {
// an always-on-top follow
followingAsSheep.push_back(aa);
}
// not moving, but should be following another character
else if ((following >= 0) && (doing_nothing == 1)) {
short distaway = (followinfo >> 8) & 0x00ff;
// no character in this room
if ((_GP(game).chars[following].on == 0) || (on == 0));
else if (room < 0) {
room++;
if (room == 0) {
// appear in the new room
room = _GP(game).chars[following].room;
x = _GP(play).entered_at_x;
y = _GP(play).entered_at_y;
}
}
// wait a bit, so we're not constantly walking
else if (Random(100) < (followinfo & 0x00ff));
// the followed character has changed room
else if ((room != _GP(game).chars[following].room)
&& (_GP(game).chars[following].on == 0))
; // do nothing if the player isn't visible
else if (room != _GP(game).chars[following].room) {
prevroom = room;
room = _GP(game).chars[following].room;
if (room == _G(displayed_room)) {
// only move to the room-entered position if coming into
// the current room
if (_GP(play).entered_at_x > (_GP(thisroom).Width - 8)) {
x = _GP(thisroom).Width + 8;
y = _GP(play).entered_at_y;
} else if (_GP(play).entered_at_x < 8) {
x = -8;
y = _GP(play).entered_at_y;
} else if (_GP(play).entered_at_y > (_GP(thisroom).Height - 8)) {
y = _GP(thisroom).Height + 8;
x = _GP(play).entered_at_x;
} else if (_GP(play).entered_at_y < _GP(thisroom).Edges.Top + 8) {
y = _GP(thisroom).Edges.Top + 1;
x = _GP(play).entered_at_x;
} else {
// not at one of the edges
// delay for a few seconds to let the player move
room = -_GP(play).follow_change_room_timer;
}
if (room >= 0) {
walk_character(aa, _GP(play).entered_at_x, _GP(play).entered_at_y, 1, true);
doing_nothing = 0;
}
}
} else if (room != _G(displayed_room)) {
// if the character is following another character and
// neither is in the current room, don't try to move
} else if ((abs(_GP(game).chars[following].x - x) > distaway + 30) ||
(abs(_GP(game).chars[following].y - y) > distaway + 30) ||
((followinfo & 0x00ff) == 0)) {
// in same room
int goxoffs = (Random(50) - 25);
// make sure he's not standing on top of the other man
if (goxoffs < 0) goxoffs -= distaway;
else goxoffs += distaway;
walk_character(aa, _GP(game).chars[following].x + goxoffs,
_GP(game).chars[following].y + (Random(50) - 25), 0, true);
doing_nothing = 0;
}
}
}
void CharacterInfo::update_character_idle(CharacterExtras *chex, int &doing_nothing) {
// no idle animation, so skip this bit
if (idleview < 1);
// currently playing idle anim
else if (idleleft < 0);
// not in the current room
else if (room != _G(displayed_room));
// they are moving or animating (or the view is locked), so
// reset idle timeout
else if ((doing_nothing == 0) || ((flags & CHF_FIXVIEW) != 0))
idleleft = idletime;
// count idle time
else if ((_G(loopcounter) % GetGameSpeed() == 0) || (chex->process_idle_this_time == 1)) {
idleleft--;
if (idleleft == -1) {
int useloop = loop;
debug_script_log("%s: Now idle (view %d)", scrname, idleview + 1);
Character_LockView(this, idleview + 1);
// SetCharView resets it to 0
idleleft = -2;
int maxLoops = _GP(views)[idleview].numLoops;
// if the char is set to "no diagonal loops", don't try
// to use diagonal idle loops either
if ((maxLoops > 4) && (useDiagonal(this)))
maxLoops = 4;
// If it's not a "swimming"-type idleanim, choose a random loop
// if there arent enough loops to do the current one.
if ((idletime > 0) && (useloop >= maxLoops)) {
do {
useloop = ::AGS::g_vm->getRandomNumber(maxLoops - 1);
// don't select a loop which is a continuation of a previous one
} while ((useloop > 0) && (_GP(views)[idleview].loops[useloop - 1].RunNextLoop()));
}
// Normal idle anim - just reset to loop 0 if not enough to
// use the current one
else if (useloop >= maxLoops)
useloop = 0;
animate_character(this, useloop, idle_anim_speed, (idletime == 0) ? 1 : 0 /* repeat */);
// don't set Animating while the idle anim plays (TODO: investigate why?)
animating = 0;
}
} // end do idle animation
}
} // namespace AGS3

View File

@@ -0,0 +1,136 @@
/* 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 "ags/engine/ac/date_time.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/engine/platform/base/ags_platform_driver.h"
#include "ags/shared/debugging/out.h"
#include "ags/engine/script/script_api.h"
#include "ags/engine/script/script_runtime.h"
#include "ags/globals.h"
namespace AGS3 {
ScriptDateTime *DateTime_Now_Core() {
ScriptDateTime *sdt = new ScriptDateTime();
_G(platform)->GetSystemTime(sdt);
return sdt;
}
ScriptDateTime *DateTime_Now() {
ScriptDateTime *sdt = DateTime_Now_Core();
ccRegisterManagedObject(sdt, sdt);
return sdt;
}
int DateTime_GetYear(ScriptDateTime *sdt) {
return sdt->year;
}
int DateTime_GetMonth(ScriptDateTime *sdt) {
return sdt->month;
}
int DateTime_GetDayOfMonth(ScriptDateTime *sdt) {
return sdt->day;
}
int DateTime_GetHour(ScriptDateTime *sdt) {
return sdt->hour;
}
int DateTime_GetMinute(ScriptDateTime *sdt) {
return sdt->minute;
}
int DateTime_GetSecond(ScriptDateTime *sdt) {
return sdt->second;
}
int DateTime_GetRawTime(ScriptDateTime *sdt) {
return sdt->rawUnixTime;
}
//=============================================================================
//
// Script API Functions
//
//=============================================================================
// ScriptDateTime* ()
RuntimeScriptValue Sc_DateTime_Now(const RuntimeScriptValue *params, int32_t param_count) {
API_SCALL_OBJAUTO(ScriptDateTime, DateTime_Now);
}
// int (ScriptDateTime *sdt)
RuntimeScriptValue Sc_DateTime_GetYear(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDateTime, DateTime_GetYear);
}
// int (ScriptDateTime *sdt)
RuntimeScriptValue Sc_DateTime_GetMonth(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDateTime, DateTime_GetMonth);
}
// int (ScriptDateTime *sdt)
RuntimeScriptValue Sc_DateTime_GetDayOfMonth(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDateTime, DateTime_GetDayOfMonth);
}
// int (ScriptDateTime *sdt)
RuntimeScriptValue Sc_DateTime_GetHour(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDateTime, DateTime_GetHour);
}
// int (ScriptDateTime *sdt)
RuntimeScriptValue Sc_DateTime_GetMinute(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDateTime, DateTime_GetMinute);
}
// int (ScriptDateTime *sdt)
RuntimeScriptValue Sc_DateTime_GetSecond(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDateTime, DateTime_GetSecond);
}
// int (ScriptDateTime *sdt)
RuntimeScriptValue Sc_DateTime_GetRawTime(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDateTime, DateTime_GetRawTime);
}
void RegisterDateTimeAPI() {
ScFnRegister datetime_api[] = {
{"DateTime::get_Now", API_FN_PAIR(DateTime_Now)},
{"DateTime::get_DayOfMonth", API_FN_PAIR(DateTime_GetDayOfMonth)},
{"DateTime::get_Hour", API_FN_PAIR(DateTime_GetHour)},
{"DateTime::get_Minute", API_FN_PAIR(DateTime_GetMinute)},
{"DateTime::get_Month", API_FN_PAIR(DateTime_GetMonth)},
{"DateTime::get_RawTime", API_FN_PAIR(DateTime_GetRawTime)},
{"DateTime::get_Second", API_FN_PAIR(DateTime_GetSecond)},
{"DateTime::get_Year", API_FN_PAIR(DateTime_GetYear)},
};
ccAddExternalFunctions361(datetime_api);
}
} // namespace AGS3

View File

@@ -0,0 +1,41 @@
/* 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 AGS_ENGINE_AC_DATETIME_H
#define AGS_ENGINE_AC_DATETIME_H
#include "ags/engine/ac/dynobj/script_date_time.h"
namespace AGS3 {
ScriptDateTime *DateTime_Now_Core();
ScriptDateTime *DateTime_Now();
int DateTime_GetYear(ScriptDateTime *sdt);
int DateTime_GetMonth(ScriptDateTime *sdt);
int DateTime_GetDayOfMonth(ScriptDateTime *sdt);
int DateTime_GetHour(ScriptDateTime *sdt);
int DateTime_GetMinute(ScriptDateTime *sdt);
int DateTime_GetSecond(ScriptDateTime *sdt);
int DateTime_GetRawTime(ScriptDateTime *sdt);
} // namespace AGS3
#endif

File diff suppressed because it is too large Load Diff

View 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 AGS_ENGINE_AC_DIALOG_H
#define AGS_ENGINE_AC_DIALOG_H
#include "common/std/vector.h"
#include "ags/shared/ac/dialog_topic.h"
#include "ags/engine/ac/dynobj/script_dialog.h"
namespace AGS3 {
int Dialog_GetID(ScriptDialog *sd);
int Dialog_GetOptionCount(ScriptDialog *sd);
int Dialog_GetShowTextParser(ScriptDialog *sd);
const char *Dialog_GetOptionText(ScriptDialog *sd, int option);
int Dialog_DisplayOptions(ScriptDialog *sd, int sayChosenOption);
int Dialog_GetOptionState(ScriptDialog *sd, int option);
int Dialog_HasOptionBeenChosen(ScriptDialog *sd, int option);
void Dialog_SetOptionState(ScriptDialog *sd, int option, int newState);
void Dialog_Start(ScriptDialog *sd);
void do_conversation(int dlgnum);
int show_dialog_options(int dlgnum, int sayChosenOption, bool runGameLoopsInBackground);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,277 @@
/* 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 "ags/engine/ac/dialog.h"
#include "ags/shared/ac/dialog_topic.h"
#include "ags/engine/ac/dialog_options_rendering.h"
#include "ags/shared/ac/game_struct_defines.h"
#include "ags/engine/debugging/debug_log.h"
#include "ags/engine/script/runtime_script_value.h"
#include "ags/engine/ac/dynobj/cc_dialog.h"
#include "ags/shared/debugging/out.h"
#include "ags/engine/script/script_api.h"
#include "ags/engine/script/script_runtime.h"
#include "ags/globals.h"
namespace AGS3 {
// ** SCRIPT DIALOGOPTIONSRENDERING OBJECT
void DialogOptionsRendering_Update(ScriptDialogOptionsRendering *dlgOptRender) {
dlgOptRender->needRepaint = true;
}
bool DialogOptionsRendering_RunActiveOption(ScriptDialogOptionsRendering *dlgOptRender) {
dlgOptRender->chosenOptionID = dlgOptRender->activeOptionID;
return dlgOptRender->chosenOptionID >= 0;
}
int DialogOptionsRendering_GetX(ScriptDialogOptionsRendering *dlgOptRender) {
return dlgOptRender->x;
}
void DialogOptionsRendering_SetX(ScriptDialogOptionsRendering *dlgOptRender, int newX) {
dlgOptRender->x = newX;
}
int DialogOptionsRendering_GetY(ScriptDialogOptionsRendering *dlgOptRender) {
return dlgOptRender->y;
}
void DialogOptionsRendering_SetY(ScriptDialogOptionsRendering *dlgOptRender, int newY) {
dlgOptRender->y = newY;
}
int DialogOptionsRendering_GetWidth(ScriptDialogOptionsRendering *dlgOptRender) {
return dlgOptRender->width;
}
void DialogOptionsRendering_SetWidth(ScriptDialogOptionsRendering *dlgOptRender, int newWidth) {
dlgOptRender->width = newWidth;
}
int DialogOptionsRendering_GetHeight(ScriptDialogOptionsRendering *dlgOptRender) {
return dlgOptRender->height;
}
void DialogOptionsRendering_SetHeight(ScriptDialogOptionsRendering *dlgOptRender, int newHeight) {
dlgOptRender->height = newHeight;
}
int DialogOptionsRendering_GetHasAlphaChannel(ScriptDialogOptionsRendering *dlgOptRender) {
return dlgOptRender->hasAlphaChannel;
}
void DialogOptionsRendering_SetHasAlphaChannel(ScriptDialogOptionsRendering *dlgOptRender, bool hasAlphaChannel) {
dlgOptRender->hasAlphaChannel = hasAlphaChannel;
}
int DialogOptionsRendering_GetParserTextboxX(ScriptDialogOptionsRendering *dlgOptRender) {
return dlgOptRender->parserTextboxX;
}
void DialogOptionsRendering_SetParserTextboxX(ScriptDialogOptionsRendering *dlgOptRender, int newX) {
dlgOptRender->parserTextboxX = newX;
}
int DialogOptionsRendering_GetParserTextboxY(ScriptDialogOptionsRendering *dlgOptRender) {
return dlgOptRender->parserTextboxY;
}
void DialogOptionsRendering_SetParserTextboxY(ScriptDialogOptionsRendering *dlgOptRender, int newY) {
dlgOptRender->parserTextboxY = newY;
}
int DialogOptionsRendering_GetParserTextboxWidth(ScriptDialogOptionsRendering *dlgOptRender) {
return dlgOptRender->parserTextboxWidth;
}
void DialogOptionsRendering_SetParserTextboxWidth(ScriptDialogOptionsRendering *dlgOptRender, int newWidth) {
dlgOptRender->parserTextboxWidth = newWidth;
}
ScriptDialog *DialogOptionsRendering_GetDialogToRender(ScriptDialogOptionsRendering *dlgOptRender) {
return &_GP(scrDialog)[dlgOptRender->dialogID];
}
ScriptDrawingSurface *DialogOptionsRendering_GetSurface(ScriptDialogOptionsRendering *dlgOptRender) {
dlgOptRender->surfaceAccessed = true;
return dlgOptRender->surfaceToRenderTo;
}
int DialogOptionsRendering_GetActiveOptionID(ScriptDialogOptionsRendering *dlgOptRender) {
return dlgOptRender->activeOptionID + 1;
}
void DialogOptionsRendering_SetActiveOptionID(ScriptDialogOptionsRendering *dlgOptRender, int activeOptionID) {
int optionCount = _G(dialog)[_GP(scrDialog)[dlgOptRender->dialogID].id].numoptions;
if ((activeOptionID < 0) || (activeOptionID > optionCount))
quitprintf("DialogOptionsRenderingInfo.ActiveOptionID: invalid ID specified for this dialog (specified %d, valid range: 1..%d)", activeOptionID, optionCount);
if (dlgOptRender->activeOptionID != activeOptionID - 1) {
dlgOptRender->activeOptionID = activeOptionID - 1;
dlgOptRender->needRepaint = true;
}
}
//=============================================================================
//
// Script API Functions
//
//=============================================================================
RuntimeScriptValue Sc_DialogOptionsRendering_Update(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID(ScriptDialogOptionsRendering, DialogOptionsRendering_Update);
}
RuntimeScriptValue Sc_DialogOptionsRendering_RunActiveOption(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_BOOL(ScriptDialogOptionsRendering, DialogOptionsRendering_RunActiveOption);
}
// int (ScriptDialogOptionsRendering *dlgOptRender)
RuntimeScriptValue Sc_DialogOptionsRendering_GetActiveOptionID(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDialogOptionsRendering, DialogOptionsRendering_GetActiveOptionID);
}
// void (ScriptDialogOptionsRendering *dlgOptRender, int activeOptionID)
RuntimeScriptValue Sc_DialogOptionsRendering_SetActiveOptionID(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptDialogOptionsRendering, DialogOptionsRendering_SetActiveOptionID);
}
// ScriptDialog* (ScriptDialogOptionsRendering *dlgOptRender)
RuntimeScriptValue Sc_DialogOptionsRendering_GetDialogToRender(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_OBJ(ScriptDialogOptionsRendering, ScriptDialog, _GP(ccDynamicDialog), DialogOptionsRendering_GetDialogToRender);
}
// int (ScriptDialogOptionsRendering *dlgOptRender)
RuntimeScriptValue Sc_DialogOptionsRendering_GetHeight(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDialogOptionsRendering, DialogOptionsRendering_GetHeight);
}
// void (ScriptDialogOptionsRendering *dlgOptRender, int newHeight)
RuntimeScriptValue Sc_DialogOptionsRendering_SetHeight(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptDialogOptionsRendering, DialogOptionsRendering_SetHeight);
}
// int (ScriptDialogOptionsRendering *dlgOptRender)
RuntimeScriptValue Sc_DialogOptionsRendering_GetParserTextboxX(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDialogOptionsRendering, DialogOptionsRendering_GetParserTextboxX);
}
// void (ScriptDialogOptionsRendering *dlgOptRender, int newX)
RuntimeScriptValue Sc_DialogOptionsRendering_SetParserTextboxX(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptDialogOptionsRendering, DialogOptionsRendering_SetParserTextboxX);
}
// int (ScriptDialogOptionsRendering *dlgOptRender)
RuntimeScriptValue Sc_DialogOptionsRendering_GetParserTextboxY(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDialogOptionsRendering, DialogOptionsRendering_GetParserTextboxY);
}
// void (ScriptDialogOptionsRendering *dlgOptRender, int newY)
RuntimeScriptValue Sc_DialogOptionsRendering_SetParserTextboxY(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptDialogOptionsRendering, DialogOptionsRendering_SetParserTextboxY);
}
// int (ScriptDialogOptionsRendering *dlgOptRender)
RuntimeScriptValue Sc_DialogOptionsRendering_GetParserTextboxWidth(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDialogOptionsRendering, DialogOptionsRendering_GetParserTextboxWidth);
}
// void (ScriptDialogOptionsRendering *dlgOptRender, int newWidth)
RuntimeScriptValue Sc_DialogOptionsRendering_SetParserTextboxWidth(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptDialogOptionsRendering, DialogOptionsRendering_SetParserTextboxWidth);
}
// ScriptDrawingSurface* (ScriptDialogOptionsRendering *dlgOptRender)
RuntimeScriptValue Sc_DialogOptionsRendering_GetSurface(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_OBJAUTO(ScriptDialogOptionsRendering, ScriptDrawingSurface, DialogOptionsRendering_GetSurface);
}
// int (ScriptDialogOptionsRendering *dlgOptRender)
RuntimeScriptValue Sc_DialogOptionsRendering_GetWidth(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDialogOptionsRendering, DialogOptionsRendering_GetWidth);
}
// void (ScriptDialogOptionsRendering *dlgOptRender, int newWidth)
RuntimeScriptValue Sc_DialogOptionsRendering_SetWidth(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptDialogOptionsRendering, DialogOptionsRendering_SetWidth);
}
// int (ScriptDialogOptionsRendering *dlgOptRender)
RuntimeScriptValue Sc_DialogOptionsRendering_GetX(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDialogOptionsRendering, DialogOptionsRendering_GetX);
}
// void (ScriptDialogOptionsRendering *dlgOptRender, int newX)
RuntimeScriptValue Sc_DialogOptionsRendering_SetX(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptDialogOptionsRendering, DialogOptionsRendering_SetX);
}
// int (ScriptDialogOptionsRendering *dlgOptRender)
RuntimeScriptValue Sc_DialogOptionsRendering_GetY(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDialogOptionsRendering, DialogOptionsRendering_GetY);
}
// void (ScriptDialogOptionsRendering *dlgOptRender, int newY)
RuntimeScriptValue Sc_DialogOptionsRendering_SetY(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptDialogOptionsRendering, DialogOptionsRendering_SetY);
}
RuntimeScriptValue Sc_DialogOptionsRendering_GetHasAlphaChannel(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDialogOptionsRendering, DialogOptionsRendering_GetHasAlphaChannel);
}
RuntimeScriptValue Sc_DialogOptionsRendering_SetHasAlphaChannel(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PBOOL(ScriptDialogOptionsRendering, DialogOptionsRendering_SetHasAlphaChannel);
}
void RegisterDialogOptionsRenderingAPI() {
ScFnRegister dialogopt_api[] = {
{"DialogOptionsRenderingInfo::Update^0", API_FN_PAIR(DialogOptionsRendering_Update)},
{"DialogOptionsRenderingInfo::RunActiveOption^0", API_FN_PAIR(DialogOptionsRendering_RunActiveOption)},
{"DialogOptionsRenderingInfo::get_ActiveOptionID", API_FN_PAIR(DialogOptionsRendering_GetActiveOptionID)},
{"DialogOptionsRenderingInfo::set_ActiveOptionID", API_FN_PAIR(DialogOptionsRendering_SetActiveOptionID)},
{"DialogOptionsRenderingInfo::get_DialogToRender", API_FN_PAIR(DialogOptionsRendering_GetDialogToRender)},
{"DialogOptionsRenderingInfo::get_Height", API_FN_PAIR(DialogOptionsRendering_GetHeight)},
{"DialogOptionsRenderingInfo::set_Height", API_FN_PAIR(DialogOptionsRendering_SetHeight)},
{"DialogOptionsRenderingInfo::get_ParserTextBoxX", API_FN_PAIR(DialogOptionsRendering_GetParserTextboxX)},
{"DialogOptionsRenderingInfo::set_ParserTextBoxX", API_FN_PAIR(DialogOptionsRendering_SetParserTextboxX)},
{"DialogOptionsRenderingInfo::get_ParserTextBoxY", API_FN_PAIR(DialogOptionsRendering_GetParserTextboxY)},
{"DialogOptionsRenderingInfo::set_ParserTextBoxY", API_FN_PAIR(DialogOptionsRendering_SetParserTextboxY)},
{"DialogOptionsRenderingInfo::get_ParserTextBoxWidth", API_FN_PAIR(DialogOptionsRendering_GetParserTextboxWidth)},
{"DialogOptionsRenderingInfo::set_ParserTextBoxWidth", API_FN_PAIR(DialogOptionsRendering_SetParserTextboxWidth)},
{"DialogOptionsRenderingInfo::get_Surface", API_FN_PAIR(DialogOptionsRendering_GetSurface)},
{"DialogOptionsRenderingInfo::get_Width", API_FN_PAIR(DialogOptionsRendering_GetWidth)},
{"DialogOptionsRenderingInfo::set_Width", API_FN_PAIR(DialogOptionsRendering_SetWidth)},
{"DialogOptionsRenderingInfo::get_X", API_FN_PAIR(DialogOptionsRendering_GetX)},
{"DialogOptionsRenderingInfo::set_X", API_FN_PAIR(DialogOptionsRendering_SetX)},
{"DialogOptionsRenderingInfo::get_Y", API_FN_PAIR(DialogOptionsRendering_GetY)},
{"DialogOptionsRenderingInfo::set_Y", API_FN_PAIR(DialogOptionsRendering_SetY)},
{"DialogOptionsRenderingInfo::get_HasAlphaChannel", API_FN_PAIR(DialogOptionsRendering_GetHasAlphaChannel)},
{"DialogOptionsRenderingInfo::set_HasAlphaChannel", API_FN_PAIR(DialogOptionsRendering_SetHasAlphaChannel)},
};
ccAddExternalFunctions361(dialogopt_api);
}
} // namespace AGS3

View File

@@ -0,0 +1,53 @@
/* 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 AGS_ENGINE_AC_DIALOG_OPTIONS_RENDERING_H
#define AGS_ENGINE_AC_DIALOG_OPTIONS_RENDERING_H
#include "ags/engine/ac/dynobj/script_dialog.h"
#include "ags/engine/ac/dynobj/script_dialog_options_rendering.h"
namespace AGS3 {
int DialogOptionsRendering_GetX(ScriptDialogOptionsRendering *dlgOptRender);
void DialogOptionsRendering_SetX(ScriptDialogOptionsRendering *dlgOptRender, int newX);
int DialogOptionsRendering_GetY(ScriptDialogOptionsRendering *dlgOptRender);
void DialogOptionsRendering_SetY(ScriptDialogOptionsRendering *dlgOptRender, int newY);
int DialogOptionsRendering_GetWidth(ScriptDialogOptionsRendering *dlgOptRender);
void DialogOptionsRendering_SetWidth(ScriptDialogOptionsRendering *dlgOptRender, int newWidth);
int DialogOptionsRendering_GetHeight(ScriptDialogOptionsRendering *dlgOptRender);
void DialogOptionsRendering_SetHeight(ScriptDialogOptionsRendering *dlgOptRender, int newHeight);
int DialogOptionsRendering_GetHasAlphaChannel(ScriptDialogOptionsRendering *dlgOptRender);
void DialogOptionsRendering_SetHasAlphaChannel(ScriptDialogOptionsRendering *dlgOptRender, bool hasAlphaChannel);
int DialogOptionsRendering_GetParserTextboxX(ScriptDialogOptionsRendering *dlgOptRender);
void DialogOptionsRendering_SetParserTextboxX(ScriptDialogOptionsRendering *dlgOptRender, int newX);
int DialogOptionsRendering_GetParserTextboxY(ScriptDialogOptionsRendering *dlgOptRender);
void DialogOptionsRendering_SetParserTextboxY(ScriptDialogOptionsRendering *dlgOptRender, int newY);
int DialogOptionsRendering_GetParserTextboxWidth(ScriptDialogOptionsRendering *dlgOptRender);
void DialogOptionsRendering_SetParserTextboxWidth(ScriptDialogOptionsRendering *dlgOptRender, int newWidth);
ScriptDialog *DialogOptionsRendering_GetDialogToRender(ScriptDialogOptionsRendering *dlgOptRender);
ScriptDrawingSurface *DialogOptionsRendering_GetSurface(ScriptDialogOptionsRendering *dlgOptRender);
int DialogOptionsRendering_GetActiveOptionID(ScriptDialogOptionsRendering *dlgOptRender);
void DialogOptionsRendering_SetActiveOptionID(ScriptDialogOptionsRendering *dlgOptRender, int activeOptionID);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,799 @@
/* 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/config-manager.h"
#include "common/std/algorithm.h"
#include "ags/engine/ac/display.h"
#include "ags/shared/ac/common.h"
#include "ags/shared/font/ags_font_renderer.h"
#include "ags/shared/font/fonts.h"
#include "ags/engine/ac/character.h"
#include "ags/engine/ac/draw.h"
#include "ags/engine/ac/game.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/engine/ac/game_state.h"
#include "ags/engine/ac/global_audio.h"
#include "ags/engine/ac/global_game.h"
#include "ags/engine/ac/gui.h"
#include "ags/engine/ac/mouse.h"
#include "ags/engine/ac/overlay.h"
#include "ags/engine/ac/sys_events.h"
#include "ags/engine/ac/screen_overlay.h"
#include "ags/engine/ac/speech.h"
#include "ags/engine/ac/string.h"
#include "ags/engine/ac/system.h"
#include "ags/engine/ac/top_bar_settings.h"
#include "ags/engine/debugging/debug_log.h"
#include "ags/engine/gfx/blender.h"
#include "ags/shared/gui/gui_button.h"
#include "ags/shared/gui/gui_main.h"
#include "ags/engine/main/game_run.h"
#include "ags/engine/platform/base/ags_platform_driver.h"
#include "ags/shared/ac/sprite_cache.h"
#include "ags/engine/gfx/gfx_util.h"
#include "ags/shared/util/string_utils.h"
#include "ags/engine/ac/mouse.h"
#include "ags/engine/media/audio/audio_system.h"
#include "ags/engine/ac/timer.h"
#include "ags/ags.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
using namespace AGS::Shared::BitmapHelper;
struct DisplayVars {
int linespacing = 0; // font's line spacing
int fulltxtheight = 0; // total height of all the text
} disp;
// Generates a textual image and returns a disposable bitmap
Bitmap *create_textual_image(const char *text, int asspch, int isThought,
int &xx, int &yy, int &adjustedXX, int &adjustedYY, int wii, int usingfont, int allowShrink,
bool &alphaChannel) {
//
// Configure the textual image
//
const bool use_speech_textwindow = (asspch < 0) && (_GP(game).options[OPT_SPEECHTYPE] >= 2);
const bool use_thought_gui = (isThought) && (_GP(game).options[OPT_THOUGHTGUI] > 0);
alphaChannel = false;
int usingGui = -1;
if (use_speech_textwindow)
usingGui = _GP(play).speech_textwindow_gui;
else if (use_thought_gui)
usingGui = _GP(game).options[OPT_THOUGHTGUI];
const int padding = get_textwindow_padding(usingGui);
const int paddingScaled = get_fixed_pixel_size(padding);
// Just in case screen size is not neatly divisible by 320x200
const int paddingDoubledScaled = get_fixed_pixel_size(padding * 2);
// WORKAROUND: Guard Duty specifies a wii of 100,000, which is larger
// than can be supported by ScummVM's surface classes
wii = MIN(wii, 8000);
// Make message copy, because ensure_text_valid_for_font() may modify it
char todis[STD_BUFFER_SIZE];
snprintf(todis, STD_BUFFER_SIZE - 1, "%s", text);
ensure_text_valid_for_font(todis, usingfont);
break_up_text_into_lines(todis, _GP(Lines), wii - 2 * padding, usingfont);
disp.linespacing = get_font_linespacing(usingfont);
disp.fulltxtheight = get_text_lines_surf_height(usingfont, _GP(Lines).Count());
if (_GP(topBar).wantIt) {
// ensure that the window is wide enough to display any top bar text
int topBarWid = get_text_width_outlined(_GP(topBar).text, _GP(topBar).font);
topBarWid += data_to_game_coord(_GP(play).top_bar_borderwidth + 2) * 2;
if (_G(longestline) < topBarWid)
_G(longestline) = topBarWid;
}
const Rect &ui_view = _GP(play).GetUIViewport();
if (xx == OVR_AUTOPLACE);
// centre text in middle of screen
else if (yy < 0) yy = ui_view.GetHeight() / 2 - disp.fulltxtheight / 2 - padding;
// speech, so it wants to be above the character's head
else if (asspch > 0) {
yy -= disp.fulltxtheight;
if (yy < 5) yy = 5;
yy = adjust_y_for_guis(yy);
}
if (_G(longestline) < wii - paddingDoubledScaled) {
// shrink the width of the dialog box to fit the text
int oldWid = wii;
//if ((asspch >= 0) || (allowShrink > 0))
// If it's not speech, or a shrink is allowed, then shrink it
if ((asspch == 0) || (allowShrink > 0))
wii = _G(longestline) + paddingDoubledScaled;
// shift the dialog box right to align it, if necessary
if ((allowShrink == 2) && (xx >= 0))
xx += (oldWid - wii);
}
if (xx < -1) {
xx = (-xx) - wii / 2;
if (xx < 0)
xx = 0;
xx = adjust_x_for_guis(xx, yy);
if (xx + wii >= ui_view.GetWidth())
xx = (ui_view.GetWidth() - wii) - 5;
} else if (xx < 0) xx = ui_view.GetWidth() / 2 - wii / 2;
const int extraHeight = paddingDoubledScaled;
color_t text_color = MakeColor(15);
const int bmp_width = MAX(2, wii);
const int bmp_height = MAX(2, disp.fulltxtheight + extraHeight);
Bitmap *text_window_ds = BitmapHelper::CreateTransparentBitmap(
bmp_width, bmp_height, _GP(game).GetColorDepth());
// inform draw_text_window to free the old bitmap
const bool wantFreeScreenop = true;
//
// Create the textual image (may also adjust some params in the process)
//
// may later change if usingGUI, needed to avoid changing original coordinates
adjustedXX = xx;
adjustedYY = yy;
if ((strlen(todis) < 1) || (strcmp(todis, " ") == 0) || (wii == 0));
// if it's an empty speech line, don't draw anything
else if (asspch) { //text_color = ds->GetCompatibleColor(12);
int ttxleft = 0, ttxtop = paddingScaled, oriwid = wii - padding * 2;
int drawBackground = 0;
if (use_speech_textwindow) {
drawBackground = 1;
} else if (use_thought_gui) {
// make it treat it as drawing inside a window now
if (asspch > 0)
asspch = -asspch;
drawBackground = 1;
}
if (drawBackground) {
draw_text_window_and_bar(&text_window_ds, wantFreeScreenop, &ttxleft, &ttxtop, &adjustedXX, &adjustedYY, &wii, &text_color, 0, usingGui);
if (usingGui > 0) {
alphaChannel = _GP(guis)[usingGui].HasAlphaChannel();
}
} else if ((ShouldAntiAliasText()) && (_GP(game).GetColorDepth() >= 24))
alphaChannel = true;
for (size_t ee = 0; ee < _GP(Lines).Count(); ee++) {
//int ttxp=wii/2 - get_text_width_outlined(lines[ee], usingfont)/2;
int ttyp = ttxtop + ee * disp.linespacing;
// asspch < 0 means that it's inside a text box so don't
// centre the text
if (asspch < 0) {
if ((usingGui >= 0) &&
((_GP(game).options[OPT_SPEECHTYPE] >= 2) || (isThought)))
text_color = text_window_ds->GetCompatibleColor(_GP(guis)[usingGui].FgColor);
else
text_color = text_window_ds->GetCompatibleColor(-asspch);
wouttext_aligned(text_window_ds, ttxleft, ttyp, oriwid, usingfont, text_color, _GP(Lines)[ee].GetCStr(), _GP(play).text_align);
} else {
text_color = text_window_ds->GetCompatibleColor(asspch);
wouttext_aligned(text_window_ds, ttxleft, ttyp, wii, usingfont, text_color, _GP(Lines)[ee].GetCStr(), _GP(play).speech_text_align);
}
}
} else {
int xoffs, yoffs, oriwid = wii - padding * 2;
draw_text_window_and_bar(&text_window_ds, wantFreeScreenop, &xoffs, &yoffs, &adjustedXX, &adjustedYY, &wii, &text_color);
if (_GP(game).options[OPT_TWCUSTOM] > 0) {
alphaChannel = _GP(guis)[_GP(game).options[OPT_TWCUSTOM]].HasAlphaChannel();
}
adjust_y_coordinate_for_text(&yoffs, usingfont);
for (size_t ee = 0; ee < _GP(Lines).Count(); ee++)
wouttext_aligned(text_window_ds, xoffs, yoffs + ee * disp.linespacing, oriwid, usingfont, text_color, _GP(Lines)[ee].GetCStr(), _GP(play).text_align);
}
return text_window_ds;
}
// Pass yy = -1 to find Y co-ord automatically
// allowShrink = 0 for none, 1 for leftwards, 2 for rightwards
// pass blocking=2 to create permanent overlay
ScreenOverlay *display_main(int xx, int yy, int wii, const char *text, int disp_type, int usingfont,
int asspch, int isThought, int allowShrink, bool overlayPositionFixed, bool roomlayer) {
//
// Prepare for the message display
//
// AGS 2.x: If the screen is faded out, fade in again when displaying a message box.
if (!asspch && (_G(loaded_game_file_version) <= kGameVersion_272))
_GP(play).screen_is_faded_out = 0;
// if it's a normal message box and the game was being skipped,
// ensure that the screen is up to date before the message box
// is drawn on top of it
// TODO: is this really necessary anymore?
if ((_GP(play).skip_until_char_stops >= 0) && (disp_type == DISPLAYTEXT_MESSAGEBOX))
render_graphics();
// TODO: should this really be called regardless of message type?
// _display_main may be called even for custom textual overlays
EndSkippingUntilCharStops();
if (_GP(topBar).wantIt) {
// the top bar should behave like DisplaySpeech wrt blocking
disp_type = DISPLAYTEXT_SPEECH;
}
if ((asspch > 0) && (disp_type < DISPLAYTEXT_NORMALOVERLAY)) {
// update the all_buttons_disabled variable in advance
// of the adjust_x/y_for_guis calls
_GP(play).disabled_user_interface++;
update_gui_disabled_status();
_GP(play).disabled_user_interface--;
}
// remove any previous blocking texts if necessary
if (disp_type < DISPLAYTEXT_NORMALOVERLAY)
remove_screen_overlay(_GP(play).text_overlay_on);
// If fast-forwarding, then skip any blocking message immediately
if (_GP(play).fast_forward && (disp_type < DISPLAYTEXT_NORMALOVERLAY)) {
_GP(play).SetWaitSkipResult(SKIP_AUTOTIMER);
post_display_cleanup();
return nullptr;
}
//
// Configure and create an overlay object
//
int ovrtype;
switch (disp_type) {
case DISPLAYTEXT_SPEECH: ovrtype = OVER_TEXTSPEECH; break;
case DISPLAYTEXT_MESSAGEBOX: ovrtype = OVER_TEXTMSG; break;
case DISPLAYTEXT_NORMALOVERLAY: ovrtype = OVER_CUSTOM; break;
default: ovrtype = disp_type; break; // must be precreated overlay id
}
int adjustedXX, adjustedYY;
bool alphaChannel;
Bitmap *text_window_ds = create_textual_image(text, asspch, isThought, xx, yy, adjustedXX, adjustedYY, wii, usingfont, allowShrink, alphaChannel);
size_t nse = add_screen_overlay(roomlayer, xx, yy, ovrtype, text_window_ds, adjustedXX - xx, adjustedYY - yy, alphaChannel);
auto *over = get_overlay(nse); // FIXME: optimize return value
// we should not delete text_window_ds here, because it is now owned by Overlay
// If it's a non-blocking overlay type, then we're done here
if (disp_type >= DISPLAYTEXT_NORMALOVERLAY) {
return over;
}
//
// Wait for the blocking text to timeout or until skipped by another command
//
if (disp_type == DISPLAYTEXT_MESSAGEBOX) {
int countdown = GetTextDisplayTime(text);
int skip_setting = user_to_internal_skip_speech((SkipSpeechStyle)_GP(play).skip_display);
// Loop until skipped
while (true) {
if (SHOULD_QUIT)
return 0;
sys_evt_process_pending();
update_audio_system_on_game_loop();
UpdateCursorAndDrawables();
render_graphics();
eAGSMouseButton mbut;
int mwheelz;
if (run_service_mb_controls(mbut, mwheelz) && mbut > kMouseNone) {
check_skip_cutscene_mclick(mbut);
if (_GP(play).fast_forward)
break;
if (skip_setting & SKIP_MOUSECLICK && !_GP(play).IsIgnoringInput()) {
_GP(play).SetWaitSkipResult(SKIP_MOUSECLICK, mbut);
break;
}
}
bool do_break = false;
while (!_GP(play).fast_forward && !do_break && ags_keyevent_ready()) {
KeyInput ki;
if (run_service_key_controls(ki)) {
check_skip_cutscene_keypress(ki.Key);
if ((skip_setting & SKIP_KEYPRESS) && !_GP(play).IsIgnoringInput() && !IsAGSServiceKey(ki.Key)) {
_GP(play).SetWaitKeySkip(ki);
do_break = true;
}
}
}
if (do_break)
break;
update_polled_stuff();
if (_GP(play).fast_forward == 0) {
WaitForNextFrame();
}
countdown--;
// Special behavior when coupled with a voice-over
if (_GP(play).speech_has_voice) {
// extend life of text if the voice hasn't finished yet
if (AudioChans::ChannelIsPlaying(SCHAN_SPEECH) && (_GP(play).fast_forward == 0)) {
if (countdown <= 1)
countdown = 1;
} else // if the voice has finished, remove the speech
countdown = 0;
}
// Test for the timed auto-skip
if ((countdown < 1) && (skip_setting & SKIP_AUTOTIMER)) {
_GP(play).SetWaitSkipResult(SKIP_AUTOTIMER);
_GP(play).SetIgnoreInput(_GP(play).ignore_user_input_after_text_timeout_ms);
break;
}
// if skipping cutscene, don't get stuck on No Auto Remove text boxes
if ((countdown < 1) && (_GP(play).fast_forward))
break;
}
remove_screen_overlay(OVER_TEXTMSG);
invalidate_screen();
} else {
/* DISPLAYTEXT_SPEECH */
if (!overlayPositionFixed) {
over->SetRoomRelative(true);
VpPoint vpt = _GP(play).GetRoomViewport(0)->ScreenToRoom(over->x, over->y, false);
over->x = vpt.first.X;
over->y = vpt.first.Y;
}
GameLoopUntilNoOverlay();
}
//
// Post-message cleanup
//
post_display_cleanup();
return nullptr;
}
void display_at(int xx, int yy, int wii, const char *text) {
EndSkippingUntilCharStops();
// Start voice-over, if requested by the tokens in speech text
try_auto_play_speech(text, text, _GP(play).narrator_speech);
display_main(xx, yy, wii, text, DISPLAYTEXT_MESSAGEBOX, FONT_NORMAL, 0, 0, 0, false);
// Stop any blocking voice-over, if was started by this function
if (_GP(play).IsBlockingVoiceSpeech())
stop_voice_speech();
}
void post_display_cleanup() {
ags_clear_input_buffer();
_GP(play).messagetime = -1;
_GP(play).speech_in_post_state = false;
}
bool try_auto_play_speech(const char *text, const char *&replace_text, int charid) {
int voice_num;
const char *src = parse_voiceover_token(text, &voice_num);
if (src == text)
return false; // no token
if (voice_num <= 0)
quit("DisplaySpeech: auto-voice symbol '&' not followed by valid integer");
replace_text = src; // skip voice tag
if (play_voice_speech(charid, voice_num)) {
// if Voice Only, then blank out the text
if (_GP(play).speech_mode == kSpeech_VoiceOnly)
replace_text = " ";
return true;
}
return false;
}
int GetTextDisplayLength(const char *text) {
// Skip voice-over token from the length calculation if required
if (_GP(play).unfactor_speech_from_textlength != 0)
text = parse_voiceover_token(text, nullptr);
return static_cast<int>(strlen(text));
}
// Calculates lipsync frame duration (or duration per character) in game loops.
// NOTE: historical formula was this:
// loops_per_character = (((text_len / play.lipsync_speed) + 1) * fps) / text_len;
// But because of a precision loss due integer division this resulted in "jumping" values.
// The new formula uses float division, and coefficent found experimentally to make
// results match the old formula in certain key text lengths, for backwards compatibility.
int CalcLipsyncFrameDuration(int text_len, int fps) {
return static_cast<int>((((static_cast<float>(text_len) / _GP(play).lipsync_speed) + 0.75f) * fps) / text_len);
}
int GetTextDisplayTime(const char *text, int canberel) {
int uselen = 0;
auto fpstimer = ::lround(get_game_fps());
// if it's background speech, make it stay relative to game speed
if ((canberel == 1) && (_GP(play).bgspeech_game_speed == 1))
fpstimer = 40; // NOTE: should be a fixed constant here, not game speed value
if (_G(source_text_length) >= 0) {
// sync to length of original text, to make sure any animations
// and music sync up correctly
uselen = _G(source_text_length);
_G(source_text_length) = -1;
} else {
uselen = GetTextDisplayLength(text);
}
if (uselen <= 0)
return 0;
if (_GP(play).text_speed + _GP(play).text_speed_modifier <= 0)
quit("!Text speed is zero; unable to display text. Check your _GP(game).text_speed settings.");
// Store how many game loops per character of text
_G(loops_per_character) = CalcLipsyncFrameDuration(uselen, fpstimer);
int textDisplayTimeInMS = ((uselen / (_GP(play).text_speed + _GP(play).text_speed_modifier)) + 1) * 1000;
if (textDisplayTimeInMS < _GP(play).text_min_display_time_ms)
textDisplayTimeInMS = _GP(play).text_min_display_time_ms;
return (textDisplayTimeInMS * fpstimer) / 1000;
}
bool ShouldAntiAliasText() {
return (_GP(game).GetColorDepth() >= 24) && (_GP(game).options[OPT_ANTIALIASFONTS] != 0 || ::AGS::g_vm->_forceTextAA);
}
void wouttextxy_AutoOutline(Bitmap *ds, size_t font, int32_t color, const char *texx, int &xxp, int &yyp) {
const FontInfo &finfo = get_fontinfo(font);
int const thickness = finfo.AutoOutlineThickness;
auto const style = finfo.AutoOutlineStyle;
if (thickness <= 0)
return;
// 16-bit games should use 32-bit stencils to keep anti-aliasing working
// because 16-bit blending works correctly if there's an actual color
// on the destination bitmap (and our intermediate bitmaps are transparent).
int const ds_cd = ds->GetColorDepth();
bool const antialias = ds_cd >= 16 && _GP(game).options[OPT_ANTIALIASFONTS] != 0 && !is_bitmap_font(font);
int const stencil_cd = antialias ? 32 : ds_cd;
if (antialias) // This is to make sure TTFs render proper alpha channel in 16-bit games too
color |= makeacol32(0, 0, 0, 0xff);
// WORKAROUND: Clifftop's Spritefont plugin returns a wrong font height for font 2 in Kathy Rain, which causes a partial outline
// for some letters. Unfortunately fixing the value on the plugin side breaks the line spacing, so let's just correct it here.
const int t_width = get_text_width(texx, font);
const auto t_extent = get_font_surface_extent(font);
const int t_height = t_extent.second - t_extent.first + ((strcmp(_GP(game).guid, "{d6795d1c-3cfe-49ec-90a1-85c313bfccaf}") == 0) && (font == 2) ? 1 : 0);
if (t_width == 0 || t_height == 0)
return;
// Prepare stencils
const int t_yoff = t_extent.first;
Bitmap *texx_stencil, *outline_stencil;
alloc_font_outline_buffers(font, &texx_stencil, &outline_stencil,
t_width, t_height, stencil_cd);
texx_stencil->ClearTransparent();
outline_stencil->ClearTransparent();
// Ready text stencil
// Note we are drawing with y off, in case some font's glyphs exceed font's ascender
wouttextxy(texx_stencil, 0, -t_yoff, font, color, texx);
// Anti-aliased TTFs require to be alpha-blended, not blit,
// or the alpha values will be plain copied and final image will be broken.
void(Bitmap:: * pfn_drawstencil)(Bitmap * src, int dst_x, int dst_y);
if (antialias) { // NOTE: we must set out blender AFTER wouttextxy, or it will be overidden
set_argb2any_blender();
pfn_drawstencil = &Bitmap::TransBlendBlt;
} else {
pfn_drawstencil = &Bitmap::MaskedBlit;
}
// move start of text so that the outline doesn't drop off the bitmap
xxp += thickness;
int const outline_y = yyp + t_yoff;
yyp += thickness;
// What we do here: first we paint text onto outline_stencil offsetting vertically;
// then we paint resulting outline_stencil onto final dest offsetting horizontally.
int largest_y_diff_reached_so_far = -1;
for (int x_diff = thickness; x_diff >= 0; x_diff--) {
// Integer arithmetics: In the following, we use terms k*(k + 1) to account for rounding.
// (k + 0.5)^2 == k*k + 2*k*0.5 + 0.5^2 == k*k + k + 0.25 ==approx. k*(k + 1)
int y_term_limit = thickness * (thickness + 1);
if (FontInfo::kRounded == style)
y_term_limit -= x_diff * x_diff;
// Extend the outline stencil to the top and bottom
for (int y_diff = largest_y_diff_reached_so_far + 1;
y_diff <= thickness && y_diff * y_diff <= y_term_limit;
y_diff++) {
(outline_stencil->*pfn_drawstencil)(texx_stencil, 0, thickness - y_diff);
if (y_diff > 0)
(outline_stencil->*pfn_drawstencil)(texx_stencil, 0, thickness + y_diff);
largest_y_diff_reached_so_far = y_diff;
}
// Stamp the outline stencil to the left and right of the text
(ds->*pfn_drawstencil)(outline_stencil, xxp - x_diff, outline_y);
if (x_diff > 0)
(ds->*pfn_drawstencil)(outline_stencil, xxp + x_diff, outline_y);
}
}
// Draw an outline if requested, then draw the text on top
void wouttext_outline(Shared::Bitmap *ds, int xxp, int yyp, int font, color_t text_color, const char *texx) {
size_t const text_font = static_cast<size_t>(font);
// Draw outline (a backdrop) if requested
color_t const outline_color = ds->GetCompatibleColor(_GP(play).speech_text_shadow);
int const outline_font = get_font_outline(font);
if (outline_font >= 0)
wouttextxy(ds, xxp, yyp, static_cast<size_t>(outline_font), outline_color, texx);
else if (outline_font == FONT_OUTLINE_AUTO)
wouttextxy_AutoOutline(ds, text_font, outline_color, texx, xxp, yyp);
else
; // no outline
// Draw text on top
wouttextxy(ds, xxp, yyp, text_font, text_color, texx);
}
void wouttext_aligned(Bitmap *ds, int usexp, int yy, int oriwid, int usingfont, color_t text_color, const char *text, HorAlignment align) {
if (align & kMAlignHCenter)
usexp = usexp + (oriwid / 2) - (get_text_width_outlined(text, usingfont) / 2);
else if (align & kMAlignRight)
usexp = usexp + (oriwid - get_text_width_outlined(text, usingfont));
wouttext_outline(ds, usexp, yy, usingfont, text_color, text);
}
int get_font_outline_padding(int font) {
if (get_font_outline(font) == FONT_OUTLINE_AUTO) {
// scaled up bitmap font, push outline further out
if (is_bitmap_font(font) && get_font_scaling_mul(font) > 1)
return get_fixed_pixel_size(2); // FIXME: should be 2 + get_fixed_pixel_size(2)?
// otherwise, just push outline by 1 pixel
else
return 2;
}
return 0;
}
void do_corner(Bitmap *ds, int sprn, int x, int y, int offx, int offy) {
if (sprn < 0) return;
if (!_GP(spriteset).DoesSpriteExist(sprn)) {
sprn = 0;
}
x = x + offx * _GP(game).SpriteInfos[sprn].Width;
y = y + offy * _GP(game).SpriteInfos[sprn].Height;
draw_gui_sprite_v330(ds, sprn, x, y);
}
int get_but_pic(GUIMain *guo, int indx) {
int butid = guo->GetControlID(indx);
return butid >= 0 ? _GP(guibuts)[butid].GetNormalImage() : 0;
}
void draw_button_background(Bitmap *ds, int xx1, int yy1, int xx2, int yy2, GUIMain *iep) {
color_t draw_color;
if (iep == nullptr) { // standard window
draw_color = ds->GetCompatibleColor(15);
ds->FillRect(Rect(xx1, yy1, xx2, yy2), draw_color);
draw_color = ds->GetCompatibleColor(16);
ds->DrawRect(Rect(xx1, yy1, xx2, yy2), draw_color);
} else {
if (_G(loaded_game_file_version) < kGameVersion_262) {
// In pre-2.62 color 0 should be treated as "black" instead of "transparent";
// this was an unintended effect in older versions (see 2.62 changelog fixes).
if (iep->BgColor == 0)
iep->BgColor = 16;
}
if (iep->BgColor >= 0) draw_color = ds->GetCompatibleColor(iep->BgColor);
else draw_color = ds->GetCompatibleColor(0); // black backrgnd behind picture
if (iep->BgColor > 0)
ds->FillRect(Rect(xx1, yy1, xx2, yy2), draw_color);
const int leftRightWidth = _GP(game).SpriteInfos[get_but_pic(iep, 4)].Width;
const int topBottomHeight = _GP(game).SpriteInfos[get_but_pic(iep, 6)].Height;
// GUI middle space
if (iep->BgImage > 0) {
{
// offset the background image and clip it so that it is drawn
// such that the border graphics can have a transparent outside
// edge
int bgoffsx = xx1 - leftRightWidth / 2;
int bgoffsy = yy1 - topBottomHeight / 2;
ds->SetClip(Rect(bgoffsx, bgoffsy, xx2 + leftRightWidth / 2, yy2 + topBottomHeight / 2));
int bgfinishx = xx2;
int bgfinishy = yy2;
int bgoffsyStart = bgoffsy;
while (bgoffsx <= bgfinishx) {
bgoffsy = bgoffsyStart;
while (bgoffsy <= bgfinishy) {
draw_gui_sprite_v330(ds, iep->BgImage, bgoffsx, bgoffsy);
bgoffsy += _GP(game).SpriteInfos[iep->BgImage].Height;
}
bgoffsx += _GP(game).SpriteInfos[iep->BgImage].Width;
}
// return to normal clipping rectangle
ds->ResetClip();
}
}
// Vertical borders
ds->SetClip(Rect(xx1 - leftRightWidth, yy1, xx2 + 1 + leftRightWidth, yy2));
for (int uu = yy1; uu <= yy2; uu += _GP(game).SpriteInfos[get_but_pic(iep, 4)].Height) {
do_corner(ds, get_but_pic(iep, 4), xx1, uu, -1, 0); // left side
do_corner(ds, get_but_pic(iep, 5), xx2 + 1, uu, 0, 0); // right side
}
// Horizontal borders
ds->SetClip(Rect(xx1, yy1 - topBottomHeight, xx2, yy2 + 1 + topBottomHeight));
for (int uu = xx1; uu <= xx2; uu += _GP(game).SpriteInfos[get_but_pic(iep, 6)].Width) {
do_corner(ds, get_but_pic(iep, 6), uu, yy1, 0, -1); // top side
do_corner(ds, get_but_pic(iep, 7), uu, yy2 + 1, 0, 0); // bottom side
}
ds->ResetClip();
// Four corners
do_corner(ds, get_but_pic(iep, 0), xx1, yy1, -1, -1); // top left
do_corner(ds, get_but_pic(iep, 1), xx1, yy2 + 1, -1, 0); // bottom left
do_corner(ds, get_but_pic(iep, 2), xx2 + 1, yy1, 0, -1); // top right
do_corner(ds, get_but_pic(iep, 3), xx2 + 1, yy2 + 1, 0, 0); // bottom right
}
}
// Calculate the width that the left and right border of the textwindow
// GUI take up
int get_textwindow_border_width(int twgui) {
if (twgui < 0)
return 0;
if (!_GP(guis)[twgui].IsTextWindow())
quit("!GUI set as text window but is not actually a text window GUI");
int borwid = _GP(game).SpriteInfos[get_but_pic(&_GP(guis)[twgui], 4)].Width +
_GP(game).SpriteInfos[get_but_pic(&_GP(guis)[twgui], 5)].Width;
return borwid;
}
// get the hegiht of the text window's top border
int get_textwindow_top_border_height(int twgui) {
if (twgui < 0)
return 0;
if (!_GP(guis)[twgui].IsTextWindow())
quit("!GUI set as text window but is not actually a text window GUI");
return _GP(game).SpriteInfos[get_but_pic(&_GP(guis)[twgui], 6)].Height;
}
// Get the padding for a text window
// -1 for the game's custom text window
int get_textwindow_padding(int ifnum) {
int result;
if (ifnum < 0)
ifnum = _GP(game).options[OPT_TWCUSTOM];
if (ifnum > 0 && ifnum < _GP(game).numgui)
result = _GP(guis)[ifnum].Padding;
else
result = TEXTWINDOW_PADDING_DEFAULT;
return result;
}
void draw_text_window(Bitmap **text_window_ds, bool should_free_ds,
int *xins, int *yins, int *xx, int *yy, int *wii, color_t *set_text_color, int ovrheight, int ifnum) {
assert(text_window_ds);
Bitmap *ds = *text_window_ds;
if (ifnum < 0)
ifnum = _GP(game).options[OPT_TWCUSTOM];
if (ifnum <= 0) {
if (ovrheight)
quit("!Cannot use QFG4 style options without custom text window");
draw_button_background(ds, 0, 0, ds->GetWidth() - 1, ds->GetHeight() - 1, nullptr);
if (set_text_color)
*set_text_color = ds->GetCompatibleColor(16);
xins[0] = 3;
yins[0] = 3;
} else {
if (ifnum >= _GP(game).numgui)
quitprintf("!Invalid GUI %d specified as text window (total GUIs: %d)", ifnum, _GP(game).numgui);
if (!_GP(guis)[ifnum].IsTextWindow())
quit("!GUI set as text window but is not actually a text window GUI");
int tbnum = get_but_pic(&_GP(guis)[ifnum], 0);
wii[0] += get_textwindow_border_width(ifnum);
xx[0] -= _GP(game).SpriteInfos[tbnum].Width;
yy[0] -= _GP(game).SpriteInfos[tbnum].Height;
if (ovrheight == 0)
ovrheight = disp.fulltxtheight;
if (should_free_ds)
delete *text_window_ds;
int padding = get_textwindow_padding(ifnum);
*text_window_ds = BitmapHelper::CreateTransparentBitmap(wii[0], ovrheight + (padding * 2) + _GP(game).SpriteInfos[tbnum].Height * 2, _GP(game).GetColorDepth());
ds = *text_window_ds;
int xoffs = _GP(game).SpriteInfos[tbnum].Width, yoffs = _GP(game).SpriteInfos[tbnum].Height;
draw_button_background(ds, xoffs, yoffs, (ds->GetWidth() - xoffs) - 1, (ds->GetHeight() - yoffs) - 1, &_GP(guis)[ifnum]);
if (set_text_color)
*set_text_color = ds->GetCompatibleColor(_GP(guis)[ifnum].FgColor);
xins[0] = xoffs + padding;
yins[0] = yoffs + padding;
}
}
void draw_text_window_and_bar(Bitmap **text_window_ds, bool should_free_ds,
int *xins, int *yins, int *xx, int *yy, int *wii, color_t *set_text_color, int ovrheight, int ifnum) {
assert(text_window_ds);
draw_text_window(text_window_ds, should_free_ds, xins, yins, xx, yy, wii, set_text_color, ovrheight, ifnum);
if ((_GP(topBar).wantIt) && (text_window_ds && *text_window_ds)) {
// top bar on the dialog window with character's name
// create an enlarged window, then free the old one
Bitmap *ds = *text_window_ds;
Bitmap *newScreenop = BitmapHelper::CreateBitmap(ds->GetWidth(), ds->GetHeight() + _GP(topBar).height, _GP(game).GetColorDepth());
newScreenop->Blit(ds, 0, 0, 0, _GP(topBar).height, ds->GetWidth(), ds->GetHeight());
delete *text_window_ds;
*text_window_ds = newScreenop;
ds = *text_window_ds;
// draw the top bar
color_t draw_color = ds->GetCompatibleColor(_GP(play).top_bar_backcolor);
ds->FillRect(Rect(0, 0, ds->GetWidth() - 1, _GP(topBar).height - 1), draw_color);
if (_GP(play).top_bar_backcolor != _GP(play).top_bar_bordercolor) {
// draw the border
draw_color = ds->GetCompatibleColor(_GP(play).top_bar_bordercolor);
for (int j = 0; j < data_to_game_coord(_GP(play).top_bar_borderwidth); j++)
ds->DrawRect(Rect(j, j, ds->GetWidth() - (j + 1), _GP(topBar).height - (j + 1)), draw_color);
}
// draw the text
int textx = (ds->GetWidth() / 2) - get_text_width_outlined(_GP(topBar).text, _GP(topBar).font) / 2;
color_t text_color = ds->GetCompatibleColor(_GP(play).top_bar_textcolor);
wouttext_outline(ds, textx, _GP(play).top_bar_borderwidth + get_fixed_pixel_size(1), _GP(topBar).font, text_color, _GP(topBar).text);
// don't draw it next time
_GP(topBar).wantIt = 0;
// adjust the text Y position
yins[0] += _GP(topBar).height;
} else if (_GP(topBar).wantIt)
_GP(topBar).wantIt = 0;
}
} // namespace AGS3

View File

@@ -0,0 +1,87 @@
/* 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 AGS_ENGINE_AC_DISPLAY_H
#define AGS_ENGINE_AC_DISPLAY_H
#include "ags/shared/gui/gui_main.h"
namespace AGS3 {
using AGS::Shared::GUIMain;
// options for 'disp_type' parameter
// blocking speech
#define DISPLAYTEXT_SPEECH 0
// super-blocking message box
#define DISPLAYTEXT_MESSAGEBOX 1
// regular non-blocking overlay
#define DISPLAYTEXT_NORMALOVERLAY 2
// also accepts explicit overlay ID >= OVER_CUSTOM
struct ScreenOverlay;
Shared::Bitmap *create_textual_image(const char *text, int asspch, int isThought,
int &xx, int &yy, int &adjustedXX, int &adjustedYY, int wii, int usingfont, int allowShrink,
bool &alphaChannel);
// Creates a textual overlay using the given parameters;
// Pass yy = -1 to find Y co-ord automatically
// allowShrink = 0 for none, 1 for leftwards, 2 for rightwards
// pass blocking=2 to create permanent overlay
ScreenOverlay *display_main(int xx, int yy, int wii, const char *text, int disp_type, int usingfont,
int asspch, int isThought, int allowShrink, bool overlayPositionFixed, bool roomlayer = false);
// Displays a standard blocking message box at a given position
void display_at(int xx, int yy, int wii, const char *text);
// Cleans up display message state
void post_display_cleanup();
// Tests the given string for the voice-over tags and plays cue clip for the given character;
// will assign replacement string, which will be blank string if game is in "voice-only" mode
// and clip was started, or string cleaned from voice-over tags which is safe to display on screen.
// Returns whether voice-over clip was started successfully.
bool try_auto_play_speech(const char *text, const char *&replace_text, int charid);
// Calculates meaningful length of the displayed text
int GetTextDisplayLength(const char *text);
// Calculates number of game loops for displaying a text on screen
int GetTextDisplayTime(const char *text, int canberel = 0);
// Draw an outline if requested, then draw the text on top
void wouttext_outline(Shared::Bitmap *ds, int xxp, int yyp, int usingfont, color_t text_color, const char *texx);
void wouttext_aligned(Shared::Bitmap *ds, int usexp, int yy, int oriwid, int usingfont, color_t text_color, const char *text, HorAlignment align);
void do_corner(Shared::Bitmap *ds, int sprn, int xx1, int yy1, int typx, int typy);
// Returns the image of a button control on the GUI under given child index
int get_but_pic(GUIMain *guo, int indx);
void draw_button_background(Shared::Bitmap *ds, int xx1, int yy1, int xx2, int yy2, GUIMain *iep);
// Calculate the width that the left and right border of the textwindow
// GUI take up
int get_textwindow_border_width(int twgui);
// get the hegiht of the text window's top border
int get_textwindow_top_border_height(int twgui);
// draw_text_window: draws the normal or custom text window
// create a new bitmap the size of the window before calling, and
// point text_window_ds to it
// returns text start x & y pos in parameters
// Warning!: draw_text_window() and draw_text_window_and_bar() can create new text_window_ds
void draw_text_window(Shared::Bitmap **text_window_ds, bool should_free_ds, int *xins, int *yins, int *xx, int *yy, int *wii, color_t *set_text_color, int ovrheight, int ifnum);
void draw_text_window_and_bar(Shared::Bitmap **text_window_ds, bool should_free_ds,
int *xins, int *yins, int *xx, int *yy, int *wii, color_t *set_text_color, int ovrheight = 0, int ifnum = -1);
int get_textwindow_padding(int ifnum);
} // namespace AGS3
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,322 @@
/* 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 AGS_ENGINE_AC_DRAW_H
#define AGS_ENGINE_AC_DRAW_H
#include "common/std/memory.h"
#include "ags/shared/core/types.h"
#include "ags/shared/ac/common_defines.h"
#include "ags/shared/gfx/gfx_def.h"
#include "ags/shared/gfx/allegro_bitmap.h"
#include "ags/shared/gfx/bitmap.h"
#include "ags/shared/game/room_struct.h"
#include "ags/engine/ac/runtime_defines.h"
#include "ags/engine/ac/walk_behind.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
typedef std::shared_ptr<Shared::Bitmap> PBitmap;
} // namespace Shared
namespace Engine {
class IDriverDependantBitmap;
} // namespace Engine
} // namespace AGS
using namespace AGS; // FIXME later
#define IS_ANTIALIAS_SPRITES _GP(usetup).enable_antialiasing && (_GP(play).disable_antialiasing == 0)
// Render stage flags, for filtering out certain elements
// during room transitions, capturing screenshots, etc.
// NOTE: these values are internal and purely arbitrary atm.
#define RENDER_BATCH_ENGINE_OVERLAY 0x0001
#define RENDER_BATCH_MOUSE_CURSOR 0x0002
#define RENDER_SHOT_SKIP_ON_FADE (RENDER_BATCH_ENGINE_OVERLAY | RENDER_BATCH_MOUSE_CURSOR)
/**
* Buffer and info flags for viewport/camera pairs rendering in software mode
*/
struct RoomCameraDrawData {
// Intermediate bitmap for the software drawing method.
// We use this bitmap in case room camera has scaling enabled, we draw dirty room rects on it,
// and then pass to software renderer which draws sprite on top and then either blits or stretch-blits
// to the virtual screen.
// For more details see comment in ALSoftwareGraphicsDriver::RenderToBackBuffer().
AGS::Shared::PBitmap Buffer; // this is the actual bitmap
AGS::Shared::PBitmap Frame; // this is either same bitmap reference or sub-bitmap of virtual screen
bool IsOffscreen; // whether room viewport was offscreen (cannot use sub-bitmap)
bool IsOverlap; // whether room viewport overlaps any others (marking dirty rects is complicated)
};
typedef int32_t sprkey_t;
// TODO: refactor the draw unit into a virtual interface with
// two implementations: for software and video-texture render,
// instead of checking whether the current method is "software".
struct DrawState {
// Whether we should use software rendering methods
// (aka raw draw), as opposed to video texture transform & fx
bool SoftwareRender = false;
// Whether we should redraw whole game screen each frame
bool FullFrameRedraw = false;
// Walk-behinds representation
WalkBehindMethodEnum WalkBehindMethod = DrawAsSeparateSprite;
// Whether there are currently remnants of a on-screen effect
bool ScreenIsDirty = false;
// A map of shared "control blocks" per each sprite used
// when preparing object textures. "Control block" is currently just
// an integer which lets to check whether the object texture is in sync
// with the sprite. When the dynamic sprite is updated or deleted,
// the control block is marked as invalid and removed from the map;
// but certain objects may keep the shared ptr to the old block with
// "invalid" mark, thus they know that they must reset their texture.
//
// TODO: investigate an alternative of having a equivalent of
// "shared texture" with sprite ID ref in Software renderer too,
// which would allow to use same method of testing DDB ID for both
// kinds of renderers, thus saving on 1 extra notification mechanism.
std::unordered_map<sprkey_t, std::shared_ptr<uint32_t> >
SpriteNotifyMap;
};
// ObjTexture is a helper struct that pairs a raw bitmap with
// a renderer's texture and an optional position
struct ObjTexture {
// Sprite ID
uint32_t SpriteID = UINT32_MAX;
// Raw bitmap; used for software render mode,
// or when particular object types require generated image.
std::unique_ptr<Shared::Bitmap> Bmp;
// Corresponding texture, created by renderer
Engine::IDriverDependantBitmap *Ddb = nullptr;
// Sprite notification block: becomes invalid to notify an updated
// or deleted sprtie
std::shared_ptr<uint32_t> SpriteNotify;
// Sprite's position
Point Pos;
// Texture's offset, *relative* to the logical sprite's position;
// may be used in case the texture's size is different for any reason
Point Off;
ObjTexture() = default;
ObjTexture(uint32_t sprite_id, Shared::Bitmap *bmp, Engine::IDriverDependantBitmap *ddb, int x, int y, int xoff = 0, int yoff = 0)
: SpriteID(sprite_id), Bmp(bmp), Ddb(ddb), Pos(x, y), Off(xoff, yoff) {
}
ObjTexture(ObjTexture &&o);
~ObjTexture();
ObjTexture &operator =(ObjTexture &&o);
// Tests if the sprite change was notified
inline bool IsChangeNotified() const {
return SpriteNotify && (*SpriteNotify != SpriteID);
}
};
// ObjectCache stores cached object data, used to determine
// if active sprite / texture should be reconstructed
struct ObjectCache {
std::unique_ptr<AGS::Shared::Bitmap> image;
bool in_use = false; // CHECKME: possibly may be removed
int sppic = 0;
short tintr = 0, tintg = 0, tintb = 0, tintamnt = 0, tintlight = 0;
short lightlev = 0, zoom = 0;
bool mirrored = 0;
int x = 0, y = 0;
ObjectCache() = default;
ObjectCache(int pic_, int tintr_, int tintg_, int tintb_, int tint_amnt_, int tint_light_,
int light_, int zoom_, bool mirror_, int posx_, int posy_)
: sppic(pic_), tintr(tintr_), tintg(tintg_), tintb(tintb_), tintamnt(tint_amnt_), tintlight(tint_light_)
, lightlev(light_), zoom(zoom_), mirrored(mirror_), x(posx_), y(posy_) {}
};
struct DrawFPS {
Engine::IDriverDependantBitmap *ddb = nullptr;
std::unique_ptr<Shared::Bitmap> bmp;
int font = -1; // in case normal font changes at runtime
};
// Converts AGS color index to the actual bitmap color using game's color depth
int MakeColor(int color_index);
class Viewport;
class Camera;
// Initializes drawing methods and optimisation
void init_draw_method();
// Initializes global game drawing resources
void init_game_drawdata();
// Initializes drawing resources upon entering new room
void init_room_drawdata();
// Disposes resources related to the current drawing methods
void dispose_draw_method();
// Disposes global game drawing resources
void dispose_game_drawdata();
// Disposes any temporary resources on leaving current room
void dispose_room_drawdata();
// Releases all the cached textures of game objects
void clear_drawobj_cache();
// Updates drawing settings depending on main viewport's size and position on screen
void on_mainviewport_changed();
// Notifies that a new room viewport was created
void on_roomviewport_created(int index);
// Notifies that a new room viewport was deleted
void on_roomviewport_deleted(int index);
// Updates drawing settings if room viewport's position or size has changed
void on_roomviewport_changed(Viewport *view);
// Detects overlapping viewports, starting from the given index in z-sorted array
void detect_roomviewport_overlaps(size_t z_index);
// Updates drawing settings if room camera's size has changed
void on_roomcamera_changed(Camera *cam);
// Marks particular object as need to update the texture
void mark_object_changed(int objid);
// TODO: write a generic drawable/objcache system where each object
// allocates a drawable for itself, and disposes one if being removed.
void reset_drawobj_for_overlay(int objnum);
// Marks all game objects which reference this sprite for redraw
void notify_sprite_changed(int sprnum, bool deleted);
// whether there are currently remnants of a DisplaySpeech
void mark_screen_dirty();
bool is_screen_dirty();
// marks whole screen as needing a redraw
void invalidate_screen();
// marks all the camera frame as needing a redraw
void invalidate_camera_frame(int index);
// marks certain rectangle on screen as needing a redraw
// in_room flag tells how to interpret the coordinates: as in-room coords or screen viewport coordinates.
void invalidate_rect(int x1, int y1, int x2, int y2, bool in_room);
void mark_current_background_dirty();
// Avoid freeing and reallocating the memory if possible
Shared::Bitmap *recycle_bitmap(Shared::Bitmap *bimp, int coldep, int wid, int hit, bool make_transparent = false);
void recycle_bitmap(std::unique_ptr<Shared::Bitmap> &bimp, int coldep, int wid, int hit, bool make_transparent = false);
Engine::IDriverDependantBitmap* recycle_ddb_sprite(Engine::IDriverDependantBitmap *ddb, uint32_t sprite_id, Shared::Bitmap *source, bool has_alpha = false, bool opaque = false);
inline Engine::IDriverDependantBitmap* recycle_ddb_bitmap(Engine::IDriverDependantBitmap *ddb, Shared::Bitmap *source, bool has_alpha = false, bool opaque = false) {
return recycle_ddb_sprite(ddb, UINT32_MAX, source, has_alpha, opaque);
}
// Draw everything
void render_graphics(Engine::IDriverDependantBitmap *extraBitmap = nullptr, int extraX = 0, int extraY = 0);
// Construct game scene, scheduling drawing list for the renderer
void construct_game_scene(bool full_redraw = false);
// Construct final game screen elements; updates and draws mouse cursor
void construct_game_screen_overlay(bool draw_mouse = true);
// Construct engine overlay with debugging tools (fps, console)
void construct_engine_overlay();
// Clears black game borders in legacy letterbox mode
void clear_letterbox_borders();
void debug_draw_room_mask(RoomAreaMask mask);
void debug_draw_movelist(int charnum);
void update_room_debug();
void tint_image(Shared::Bitmap *g, Shared::Bitmap *source, int red, int grn, int blu, int light_level, int luminance = 255);
void draw_sprite_support_alpha(Shared::Bitmap *ds, bool ds_has_alpha, int xpos, int ypos, Shared::Bitmap *image, bool src_has_alpha,
Shared::BlendMode blend_mode = Shared::kBlendMode_Alpha, int alpha = 0xFF);
void draw_sprite_slot_support_alpha(Shared::Bitmap *ds, bool ds_has_alpha, int xpos, int ypos, int src_slot,
Shared::BlendMode blend_mode = Shared::kBlendMode_Alpha, int alpha = 0xFF);
void draw_gui_sprite(Shared::Bitmap *ds, int pic, int x, int y, bool use_alpha = true, Shared::BlendMode blend_mode = Shared::kBlendMode_Alpha);
void draw_gui_sprite_v330(Shared::Bitmap *ds, int pic, int x, int y, bool use_alpha = true, Shared::BlendMode blend_mode = Shared::kBlendMode_Alpha);
void draw_gui_sprite(Shared::Bitmap *ds, bool use_alpha, int xpos, int ypos,
Shared::Bitmap *image, bool src_has_alpha, Shared::BlendMode blend_mode = Shared::kBlendMode_Alpha, int alpha = 0xFF);
// Puts a pixel of certain color, scales it if running in upscaled resolution (legacy feature)
void putpixel_scaled(Shared::Bitmap *ds, int x, int y, int col);
// Render game on screen
void render_to_screen();
// Callbacks for the graphics driver
void draw_game_screen_callback();
void GfxDriverOnInitCallback(void *data);
bool GfxDriverSpriteEvtCallback(int evt, int data);
// Create the actsps[objid] image with the object drawn correctly.
// Returns true if nothing at all has changed and actsps is still
// intact from last time; false otherwise.
// Hardware-accelerated do not require altering the raw bitmap itself,
// so they only detect whether the sprite ID itself has changed.
// Software renderers modify the cached bitmap whenever any visual
// effect changes (scaling, tint, etc).
// * force_software option forces HW renderers to construct the image
// in software mode as well.
bool construct_object_gfx(int objid, bool force_software);
bool construct_char_gfx(int charid, bool force_software);
// Returns a cached character image prepared for the render
Shared::Bitmap *get_cached_character_image(int charid);
// Returns a cached object image prepared for the render
Shared::Bitmap *get_cached_object_image(int objid);
// Adds a walk-behind sprite to the list for the given slot
// (reuses existing texture if possible)
void add_walkbehind_image(size_t index, Shared::Bitmap *bmp, int x, int y);
void draw_and_invalidate_text(Shared::Bitmap *ds, int x1, int y1, int font, color_t text_color, const char *text);
void setpal();
// These functions are converting coordinates between data resolution and
// game resolution units. The first are units used by game data and script,
// and second define the game's screen resolution, sprite and font sizes.
// This conversion is done before anything else (like moving from room to
// viewport on screen, or scaling game further in the window by the graphic
// renderer).
int get_fixed_pixel_size(int pixels);
// coordinate conversion data,script ---> final game resolution
extern int data_to_game_coord(int coord);
extern void data_to_game_coords(int *x, int *y);
extern void data_to_game_round_up(int *x, int *y);
// coordinate conversion final game resolution ---> data,script
extern int game_to_data_coord(int coord);
extern void game_to_data_coords(int &x, int &y);
extern int game_to_data_round_up(int coord);
// convert contextual data coordinates to final game resolution
extern void ctx_data_to_game_coord(int &x, int &y, bool hires_ctx);
extern void ctx_data_to_game_size(int &x, int &y, bool hires_ctx);
extern int ctx_data_to_game_size(int size, bool hires_ctx);
extern int game_to_ctx_data_size(int size, bool hires_ctx);
// This function converts game coordinates coming from script to the actual game resolution.
extern void defgame_to_finalgame_coords(int &x, int &y);
// Creates bitmap of a format compatible with the gfxdriver;
// if col_depth is 0, uses game's native color depth.
Shared::Bitmap *CreateCompatBitmap(int width, int height, int col_depth = 0);
// Checks if the bitmap is compatible with the gfxdriver;
// returns same bitmap or its copy of a compatible format.
Shared::Bitmap *ReplaceBitmapWithSupportedFormat(Shared::Bitmap *bitmap);
// Checks if the bitmap needs any kind of adjustments before it may be used
// in AGS sprite operations. Also handles number of certain special cases
// (old systems or uncommon gfx modes, and similar stuff).
// Original bitmap **gets deleted** if a new bitmap had to be created.
Shared::Bitmap *PrepareSpriteForUse(Shared::Bitmap *bitmap, bool has_alpha);
// Same as above, but compatible for std::shared_ptr.
Shared::PBitmap PrepareSpriteForUse(Shared::PBitmap bitmap, bool has_alpha);
// Makes a screenshot corresponding to the last screen render and returns it as a bitmap
// of the requested width and height and game's native color depth.
Shared::Bitmap *CopyScreenIntoBitmap(int width, int height, const Rect *src_rect = nullptr,
bool at_native_res = false, uint32_t batch_skip_filter = 0u);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,409 @@
/* 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/>.
*
*/
//=============================================================================
//
// Software drawing component. Optimizes drawing for software renderer using
// dirty rectangles technique.
//
// TODO: do research/profiling to find out if this dirty rectangles thing
// is still giving ANY notable performance boost at all.
//
// TODO: would that give any benefit to reorganize the code and move dirty
// rectangles into SoftwareGraphicDriver?
// Alternatively: we could pass dirty rects struct pointer and room background
// DDB when calling BeginSpriteBatch(). Driver itself could be calling
// update_invalid_region(). That will keep gfx driver's changes to minimum.
//
// NOTE: this code, including structs and functions, has underwent several
// iterations of changes. Originally it was meant to perform full transform
// of dirty rects right away, but later I realized it won't work that way
// because a) Allegro does not support scaling bitmaps over destination with
// different colour depth (which may be a case when running 16-bit game),
// and b) Allegro does not support scaling and rotating of sprites with
// blending and lighting at the same time which means that room objects have
// to be drawn upon non-scaled background first. Possibly some of the code
// below may be therefore simplified.
//
//=============================================================================
#include "common/std/vector.h"
#include "ags/engine/ac/draw_software.h"
#include "ags/shared/gfx/bitmap.h"
#include "ags/shared/util/scaling.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
using namespace AGS::Engine;
IRSpan::IRSpan()
: x1(0), x2(0) {
}
IRRow::IRRow()
: numSpans(0) {
}
int IRSpan::mergeSpan(int tx1, int tx2) {
if ((tx1 > x2) || (tx2 < x1))
return 0;
// overlapping, increase the span
if (tx1 < x1)
x1 = tx1;
if (tx2 > x2)
x2 = tx2;
return 1;
}
DirtyRects::DirtyRects()
: NumDirtyRegions(0) {
}
bool DirtyRects::IsInit() const {
return DirtyRows.size() > 0;
}
void DirtyRects::Init(const Size &surf_size, const Rect &viewport) {
int height = surf_size.Height;
if (SurfaceSize != surf_size) {
Destroy();
SurfaceSize = surf_size;
DirtyRows.resize(height);
NumDirtyRegions = WHOLESCREENDIRTY;
for (int i = 0; i < height; ++i)
DirtyRows[i].numSpans = 0;
}
Viewport = viewport;
Room2Screen.Init(surf_size, viewport);
Screen2DirtySurf.Init(viewport, RectWH(0, 0, surf_size.Width, surf_size.Height));
}
void DirtyRects::SetSurfaceOffsets(int x, int y) {
Room2Screen.SetSrcOffsets(x, y);
}
void DirtyRects::Destroy() {
DirtyRows.clear();
NumDirtyRegions = 0;
}
void DirtyRects::Reset() {
NumDirtyRegions = 0;
for (size_t i = 0; i < DirtyRows.size(); ++i)
DirtyRows[i].numSpans = 0;
}
void dispose_invalid_regions(bool /* room_only */) {
_GP(RoomCamRects).clear();
_GP(RoomCamPositions).clear();
}
void set_invalidrects_globaloffs(int x, int y) {
_GP(GlobalOffs) = Point(x, y);
}
void init_invalid_regions(int view_index, const Size &surf_size, const Rect &viewport) {
if (view_index < 0) {
_GP(BlackRects).Init(surf_size, viewport);
} else {
if (_GP(RoomCamRects).size() <= (size_t)view_index) {
_GP(RoomCamRects).resize(view_index + 1);
_GP(RoomCamPositions).resize(view_index + 1);
}
_GP(RoomCamRects)[view_index].Init(surf_size, viewport);
_GP(RoomCamPositions)[view_index] = std::make_pair(-1000, -1000);
}
}
void delete_invalid_regions(int view_index) {
if (view_index >= 0) {
_GP(RoomCamRects).erase(_GP(RoomCamRects).begin() + view_index);
_GP(RoomCamPositions).erase(_GP(RoomCamPositions).begin() + view_index);
}
}
void set_invalidrects_cameraoffs(int view_index, int x, int y) {
if (view_index < 0) {
_GP(BlackRects).SetSurfaceOffsets(x, y);
return;
} else {
_GP(RoomCamRects)[view_index].SetSurfaceOffsets(x, y);
}
int &posxwas = _GP(RoomCamPositions)[view_index].first;
int &posywas = _GP(RoomCamPositions)[view_index].second;
if ((x != posxwas) || (y != posywas)) {
invalidate_all_camera_rects(view_index);
posxwas = x;
posywas = y;
}
}
void invalidate_all_rects() {
for (auto &rects : _GP(RoomCamRects)) {
if (!IsRectInsideRect(rects.Viewport, _GP(BlackRects).Viewport))
_GP(BlackRects).NumDirtyRegions = WHOLESCREENDIRTY;
rects.NumDirtyRegions = WHOLESCREENDIRTY;
}
}
void invalidate_all_camera_rects(int view_index) {
if (view_index < 0)
return;
_GP(RoomCamRects)[view_index].NumDirtyRegions = WHOLESCREENDIRTY;
}
void invalidate_rect_on_surf(int x1, int y1, int x2, int y2, DirtyRects &rects) {
if (rects.DirtyRows.size() == 0)
return;
if (rects.NumDirtyRegions >= MAXDIRTYREGIONS) {
// too many invalid rectangles, just mark the whole thing dirty
rects.NumDirtyRegions = WHOLESCREENDIRTY;
return;
}
if (x1 > x2 || y1 > y2)
return;
int a;
const Size &surfsz = rects.SurfaceSize;
if (x1 >= surfsz.Width || y1 >= surfsz.Height || x2 < 0 || y2 < 0)
return;
if (x2 >= surfsz.Width) x2 = surfsz.Width - 1;
if (y2 >= surfsz.Height) y2 = surfsz.Height - 1;
if (x1 < 0) x1 = 0;
if (y1 < 0) y1 = 0;
rects.NumDirtyRegions++;
// ** Span code
std::vector<IRRow> &dirtyRow = rects.DirtyRows;
int s, foundOne;
// add this rect to the list for this row
for (a = y1; a <= y2; a++) {
foundOne = 0;
for (s = 0; s < dirtyRow[a].numSpans; s++) {
if (dirtyRow[a].span[s].mergeSpan(x1, x2)) {
foundOne = 1;
break;
}
}
if (foundOne) {
// we were merged into a span, so we're ok
int t;
// check whether now two of the spans overlap each other
// in which case merge them
for (s = 0; s < dirtyRow[a].numSpans; s++) {
for (t = s + 1; t < dirtyRow[a].numSpans; t++) {
if (dirtyRow[a].span[s].mergeSpan(dirtyRow[a].span[t].x1, dirtyRow[a].span[t].x2)) {
dirtyRow[a].numSpans--;
for (int u = t; u < dirtyRow[a].numSpans; u++)
dirtyRow[a].span[u] = dirtyRow[a].span[u + 1];
break;
}
}
}
} else if (dirtyRow[a].numSpans < MAX_SPANS_PER_ROW) {
dirtyRow[a].span[dirtyRow[a].numSpans].x1 = x1;
dirtyRow[a].span[dirtyRow[a].numSpans].x2 = x2;
dirtyRow[a].numSpans++;
} else {
// didn't fit in an existing span, and there are none spare
int nearestDist = 99999, nearestWas = -1, extendLeft = 0;
// find the nearest span, and enlarge that to include this rect
for (s = 0; s < dirtyRow[a].numSpans; s++) {
int tleft = dirtyRow[a].span[s].x1 - x2;
if ((tleft > 0) && (tleft < nearestDist)) {
nearestDist = tleft;
nearestWas = s;
extendLeft = 1;
}
int tright = x1 - dirtyRow[a].span[s].x2;
if ((tright > 0) && (tright < nearestDist)) {
nearestDist = tright;
nearestWas = s;
extendLeft = 0;
}
}
assert(nearestWas >= 0);
if (extendLeft)
dirtyRow[a].span[nearestWas].x1 = x1;
else
dirtyRow[a].span[nearestWas].x2 = x2;
}
}
// ** End span code
//}
}
void invalidate_rect_ds(DirtyRects &rects, int x1, int y1, int x2, int y2, bool in_room) {
if (!in_room) {
// TODO: for most opimisation (esp. with multiple viewports) should perhaps
// split/cut parts of the original rectangle which overlap room viewport(s).
Rect r(x1, y1, x2, y2);
// If overlay is NOT completely over the room, then invalidate black rect
if (!IsRectInsideRect(rects.Viewport, r))
invalidate_rect_on_surf(x1, y1, x2, y2, _GP(BlackRects));
// If overlay is NOT intersecting room viewport at all, then stop
if (!AreRectsIntersecting(rects.Viewport, r))
return;
// Transform from screen to room coordinates through the known viewport
x1 = rects.Screen2DirtySurf.X.ScalePt(x1);
x2 = rects.Screen2DirtySurf.X.ScalePt(x2);
y1 = rects.Screen2DirtySurf.Y.ScalePt(y1);
y2 = rects.Screen2DirtySurf.Y.ScalePt(y2);
} else {
// Transform only from camera pos to room background
x1 -= rects.Room2Screen.X.GetSrcOffset();
y1 -= rects.Room2Screen.Y.GetSrcOffset();
x2 -= rects.Room2Screen.X.GetSrcOffset();
y2 -= rects.Room2Screen.Y.GetSrcOffset();
}
invalidate_rect_on_surf(x1, y1, x2, y2, rects);
}
void invalidate_rect_ds(int x1, int y1, int x2, int y2, bool in_room) {
if (!in_room) { // convert from game viewport to global screen coords
x1 += _GP(GlobalOffs).X;
x2 += _GP(GlobalOffs).X;
y1 += _GP(GlobalOffs).Y;
y2 += _GP(GlobalOffs).Y;
}
for (auto &rects : _GP(RoomCamRects))
invalidate_rect_ds(rects, x1, y1, x2, y2, in_room);
}
void invalidate_rect_global(int x1, int y1, int x2, int y2) {
for (auto &rects : _GP(RoomCamRects))
invalidate_rect_ds(rects, x1, y1, x2, y2, false);
}
// Note that this function is denied to perform any kind of scaling or other transformation
// other than blitting with offset. This is mainly because destination could be a 32-bit virtual screen
// while room background was 16-bit and Allegro lib does not support stretching between colour depths.
// The no_transform flag here means essentially "no offset", and indicates that the function
// must blit src on ds at 0;0. Otherwise, actual Viewport offset is used.
void update_invalid_region(Bitmap *ds, Bitmap *src, const DirtyRects &rects, bool no_transform) {
if (rects.NumDirtyRegions == 0)
return;
if (!no_transform)
ds->SetClip(rects.Viewport);
const int src_x = rects.Room2Screen.X.GetSrcOffset();
const int src_y = rects.Room2Screen.Y.GetSrcOffset();
const int dst_x = no_transform ? 0 : rects.Viewport.Left;
const int dst_y = no_transform ? 0 : rects.Viewport.Top;
if (rects.NumDirtyRegions == WHOLESCREENDIRTY) {
ds->Blit(src, src_x, src_y, dst_x, dst_y, rects.SurfaceSize.Width, rects.SurfaceSize.Height);
} else {
const std::vector<IRRow> &dirtyRow = rects.DirtyRows;
const int surf_height = rects.SurfaceSize.Height;
// TODO: is this IsMemoryBitmap check is still relevant?
// If bitmaps properties match and no transform required other than linear offset
if (src->GetColorDepth() == ds->GetColorDepth()) {
const int bypp = src->GetBPP();
// do the fast memory copy
for (int i = 0; i < surf_height; i++) {
const uint8_t *src_scanline = src->GetScanLine(i + src_y);
uint8_t *dst_scanline = ds->GetScanLineForWriting(i + dst_y);
const IRRow &dirty_row = dirtyRow[i];
for (int k = 0; k < dirty_row.numSpans; k++) {
int tx1 = dirty_row.span[k].x1;
int tx2 = dirty_row.span[k].x2;
memcpy(&dst_scanline[(tx1 + dst_x) * bypp], &src_scanline[(tx1 + src_x) * bypp], ((tx2 - tx1) + 1) * bypp);
}
}
}
// If has to use Blit, but still must draw with no transform but offset
else {
// do fast copy without transform
for (int i = 0, rowsInOne = 1; i < surf_height; i += rowsInOne, rowsInOne = 1) {
// if there are rows with identical masks, do them all in one go
// TODO: what is this for? may this be done at the invalidate_rect merge step?
while ((i + rowsInOne < surf_height) && (memcmp(&dirtyRow[i], &dirtyRow[i + rowsInOne], sizeof(IRRow)) == 0))
rowsInOne++;
const IRRow &dirty_row = dirtyRow[i];
for (int k = 0; k < dirty_row.numSpans; k++) {
int tx1 = dirty_row.span[k].x1;
int tx2 = dirty_row.span[k].x2;
ds->Blit(src, tx1 + src_x, i + src_y, tx1 + dst_x, i + dst_y, (tx2 - tx1) + 1, rowsInOne);
}
}
}
}
}
void update_invalid_region(Bitmap *ds, color_t fill_color, const DirtyRects &rects) {
ds->SetClip(rects.Viewport);
if (rects.NumDirtyRegions == WHOLESCREENDIRTY) {
ds->FillRect(rects.Viewport, fill_color);
} else {
const std::vector<IRRow> &dirtyRow = rects.DirtyRows;
const int surf_height = rects.SurfaceSize.Height;
{
const AGS::Shared::PlaneScaling &tf = rects.Room2Screen;
for (int i = 0, rowsInOne = 1; i < surf_height; i += rowsInOne, rowsInOne = 1) {
// if there are rows with identical masks, do them all in one go
// TODO: what is this for? may this be done at the invalidate_rect merge step?
while ((i + rowsInOne < surf_height) && (memcmp(&dirtyRow[i], &dirtyRow[i + rowsInOne], sizeof(IRRow)) == 0))
rowsInOne++;
const IRRow &dirty_row = dirtyRow[i];
for (int k = 0; k < dirty_row.numSpans; k++) {
Rect src_r(dirty_row.span[k].x1, i, dirty_row.span[k].x2, i + rowsInOne - 1);
Rect dst_r = tf.ScaleRange(src_r);
ds->FillRect(dst_r, fill_color);
}
}
}
}
}
void update_black_invreg_and_reset(Bitmap *ds) {
if (!_GP(BlackRects).IsInit())
return;
update_invalid_region(ds, (color_t)0, _GP(BlackRects));
_GP(BlackRects).Reset();
}
void update_room_invreg_and_reset(int view_index, Bitmap *ds, Bitmap *src, bool no_transform) {
if (view_index < 0 || _GP(RoomCamRects).size() == 0)
return;
update_invalid_region(ds, src, _GP(RoomCamRects)[view_index], no_transform);
_GP(RoomCamRects)[view_index].Reset();
}
} // namespace AGS3

View File

@@ -0,0 +1,116 @@
/* 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/>.
*
*/
//=============================================================================
//
// Software drawing component. Optimizes drawing for software renderer using
// dirty rectangles technique.
//
//=============================================================================
#ifndef AGS_ENGINE_AC_DRAW_SOFTWARE_H
#define AGS_ENGINE_AC_DRAW_SOFTWARE_H
#include "ags/shared/gfx/bitmap.h"
#include "ags/engine/gfx/ddb.h"
#include "ags/shared/util/geometry.h"
#include "ags/shared/util/scaling.h"
namespace AGS3 {
using AGS::Shared::PlaneScaling;
// TODO: choose these values depending on game resolution?
#define MAXDIRTYREGIONS 25
#define WHOLESCREENDIRTY (MAXDIRTYREGIONS + 5)
#define MAX_SPANS_PER_ROW 4
// Dirty rects store coordinate values in the coordinate system of a camera surface,
// where coords always span from 0,0 to surface width,height.
// Converting from room to dirty rects would require subtracting room camera offsets.
struct IRSpan {
int x1, x2;
int mergeSpan(int tx1, int tx2);
IRSpan();
};
struct IRRow {
IRSpan span[MAX_SPANS_PER_ROW];
int numSpans;
IRRow();
};
struct DirtyRects {
// Size of the surface managed by this dirty rects object
Size SurfaceSize;
// Where the surface is rendered on screen
Rect Viewport;
// Room -> screen coordinate transformation
PlaneScaling Room2Screen;
// Screen -> dirty surface rect
// The dirty rects are saved in coordinates limited to (0,0)->(camera size) rather than room or screen coords
PlaneScaling Screen2DirtySurf;
std::vector<IRRow> DirtyRows;
Rect DirtyRegions[MAXDIRTYREGIONS];
size_t NumDirtyRegions;
DirtyRects();
bool IsInit() const;
// Initialize dirty rects for the given surface size
void Init(const Size &surf_size, const Rect &viewport);
void SetSurfaceOffsets(int x, int y);
// Delete dirty rects
void Destroy();
// Mark all surface as tidy
void Reset();
};
// Sets global viewport offset (used for legacy letterbox)
void set_invalidrects_globaloffs(int x, int y);
// Inits dirty rects array for the given room camera/viewport pair
// View_index indicates the room viewport (>= 0) or the main viewport (-1)
void init_invalid_regions(int view_index, const Size &surf_size, const Rect &viewport);
// Deletes dirty rects for particular index
void delete_invalid_regions(int view_index);
// Disposes dirty rects arrays
void dispose_invalid_regions(bool room_only);
// Update the coordinate transformation for the particular dirty rects object
void set_invalidrects_cameraoffs(int view_index, int x, int y);
// Mark the whole screen dirty
void invalidate_all_rects();
// Mark the whole camera surface dirty
void invalidate_all_camera_rects(int view_index);
// Mark certain rectangle dirty; in_room tells if coordinates are room viewport or screen coords
void invalidate_rect_ds(int x1, int y1, int x2, int y2, bool in_room);
// Mark rectangle dirty, treat pos as global screen coords (not offset by legacy letterbox mode)
void invalidate_rect_global(int x1, int y1, int x2, int y2);
// Paints the black screen background in the regions marked as dirty
void update_black_invreg_and_reset(AGS::Shared::Bitmap *ds);
// Copies the room regions marked as dirty from source (src) to destination (ds) with the given offset (x, y)
// no_transform flag tells the system that the regions should be plain copied to the ds.
void update_room_invreg_and_reset(int view_index, AGS::Shared::Bitmap *ds, AGS::Shared::Bitmap *src, bool no_transform);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,590 @@
/* 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 "ags/engine/ac/draw.h"
#include "ags/engine/ac/drawing_surface.h"
#include "ags/shared/ac/common.h"
#include "ags/engine/ac/display.h"
#include "ags/engine/ac/game.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/engine/ac/game_state.h"
#include "ags/engine/ac/global_translation.h"
#include "ags/engine/ac/room_object.h"
#include "ags/engine/ac/room_status.h"
#include "ags/engine/ac/string.h"
#include "ags/engine/ac/walk_behind.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/engine/debugging/debug_log.h"
#include "ags/shared/font/fonts.h"
#include "ags/shared/gui/gui_main.h"
#include "ags/shared/ac/sprite_cache.h"
#include "ags/engine/script/runtime_script_value.h"
#include "ags/shared/gfx/gfx_def.h"
#include "ags/shared/debugging/out.h"
#include "ags/engine/script/script_api.h"
#include "ags/engine/script/script_runtime.h"
#include "ags/engine/gfx/gfx_util.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
using namespace AGS::Engine;
// ** SCRIPT DRAWINGSURFACE OBJECT
void DrawingSurface_Release(ScriptDrawingSurface *sds) {
if (sds->roomBackgroundNumber >= 0) {
if (sds->modified) {
if (sds->roomBackgroundNumber == _GP(play).bg_frame) {
invalidate_screen();
mark_current_background_dirty();
}
_GP(play).raw_modified[sds->roomBackgroundNumber] = 1;
}
sds->roomBackgroundNumber = -1;
}
if (sds->roomMaskType > kRoomAreaNone) {
if (sds->roomMaskType == kRoomAreaWalkBehind) {
walkbehinds_recalc();
}
sds->roomMaskType = kRoomAreaNone;
}
if (sds->dynamicSpriteNumber >= 0) {
if (sds->modified) {
game_sprite_updated(sds->dynamicSpriteNumber);
}
sds->dynamicSpriteNumber = -1;
}
if (sds->dynamicSurfaceNumber >= 0) {
_G(dynamicallyCreatedSurfaces)[sds->dynamicSurfaceNumber].reset();
sds->dynamicSurfaceNumber = -1;
}
sds->modified = 0;
}
void ScriptDrawingSurface::PointToGameResolution(int *xcoord, int *ycoord) {
ctx_data_to_game_coord(*xcoord, *ycoord, highResCoordinates != 0);
}
void ScriptDrawingSurface::SizeToGameResolution(int *width, int *height) {
ctx_data_to_game_size(*width, *height, highResCoordinates != 0);
}
void ScriptDrawingSurface::SizeToGameResolution(int *valueToAdjust) {
*valueToAdjust = ctx_data_to_game_size(*valueToAdjust, highResCoordinates != 0);
}
// convert actual co-ordinate back to what the script is expecting
void ScriptDrawingSurface::SizeToDataResolution(int *valueToAdjust) {
*valueToAdjust = game_to_ctx_data_size(*valueToAdjust, highResCoordinates != 0);
}
ScriptDrawingSurface *DrawingSurface_CreateCopy(ScriptDrawingSurface *sds) {
Bitmap *sourceBitmap = sds->GetBitmapSurface();
for (int i = 0; i < MAX_DYNAMIC_SURFACES; i++) {
if (_G(dynamicallyCreatedSurfaces)[i] == nullptr) {
_G(dynamicallyCreatedSurfaces)[i].reset(BitmapHelper::CreateBitmapCopy(sourceBitmap));
ScriptDrawingSurface *newSurface = new ScriptDrawingSurface();
newSurface->dynamicSurfaceNumber = i;
newSurface->hasAlphaChannel = sds->hasAlphaChannel;
ccRegisterManagedObject(newSurface, newSurface);
return newSurface;
}
}
quit("!DrawingSurface.CreateCopy: too many copied surfaces created");
return nullptr;
}
void DrawingSurface_DrawImageImpl(ScriptDrawingSurface *sds, Bitmap *src,
int dst_x, int dst_y, int trans, int dst_width, int dst_height,
int src_x, int src_y, int src_width, int src_height, int sprite_id, bool src_has_alpha) {
Bitmap *ds = sds->GetBitmapSurface();
if (src == ds) {
} // ignore for now; bitmap lib supports, and may be used for effects
/* debug_script_warn("DrawingSurface.DrawImage: drawing onto itself"); */
if (src->GetColorDepth() != ds->GetColorDepth()) {
if (sprite_id >= 0)
debug_script_warn("DrawImage: Sprite %d colour depth %d-bit not same as destination depth %d-bit", sprite_id, src->GetColorDepth(), ds->GetColorDepth());
else
debug_script_warn("DrawImage: Source image colour depth %d-bit not same as destination depth %d-bit", src->GetColorDepth(), ds->GetColorDepth());
}
if ((trans < 0) || (trans > 100))
debug_script_warn("DrawingSurface.DrawImage: invalid transparency %d, range is %d - %d", trans, 0, 100);
trans = Math::Clamp(trans, 0, 100);
if (trans == 100)
return; // fully transparent
if (dst_width < 1 || dst_height < 1 || src_width < 1 || src_height < 1)
return; // invalid src or dest rectangles
// Setup uninitialized arguments; convert coordinates for legacy script mode
if (dst_width == SCR_NO_VALUE) {
dst_width = src->GetWidth();
} else {
sds->SizeToGameResolution(&dst_width);
}
if (dst_height == SCR_NO_VALUE) {
dst_height = src->GetHeight();
} else {
sds->SizeToGameResolution(&dst_height);
}
if (src_x == SCR_NO_VALUE) {
src_x = 0;
}
if (src_y == SCR_NO_VALUE) {
src_y = 0;
}
sds->PointToGameResolution(&src_x, &src_y);
if (src_width == SCR_NO_VALUE) {
src_width = src->GetWidth();
} else {
sds->SizeToGameResolution(&src_width);
}
if (src_height == SCR_NO_VALUE) {
src_height = src->GetHeight();
} else {
sds->SizeToGameResolution(&src_height);
}
sds->PointToGameResolution(&dst_x, &dst_y);
if (dst_x >= ds->GetWidth() || dst_x + dst_width <= 0 || dst_y >= ds->GetHeight() || dst_y + dst_height <= 0 ||
src_x >= src->GetWidth() || src_x + src_width <= 0 || src_y >= src->GetHeight() || src_y + src_height <= 0)
return; // source or destination rects lie completely off surface
// Clamp the source rect to the valid limits to prevent exceptions (ignore dest, bitmap drawing deals with that)
Math::ClampLength(src_x, src_width, 0, src->GetWidth());
Math::ClampLength(src_y, src_height, 0, src->GetHeight());
// TODO: possibly optimize by not making a stretched intermediate bitmap
// if simpler blit/draw_sprite could be called (no translucency with alpha channel).
std::unique_ptr<Bitmap> conv_src;
if (dst_width != src->GetWidth() || dst_height != src->GetHeight() ||
src_width != src->GetWidth() || src_height != src->GetHeight()) {
// Resize and/or partial copy specified
conv_src.reset(BitmapHelper::CreateBitmap(dst_width, dst_height, src->GetColorDepth()));
conv_src->StretchBlt(src,
RectWH(src_x, src_y, src_width, src_height),
RectWH(0, 0, dst_width, dst_height));
src = conv_src.get();
}
ds = sds->StartDrawing();
draw_sprite_support_alpha(ds, sds->hasAlphaChannel != 0, dst_x, dst_y, src, src_has_alpha,
kBlendMode_Alpha, GfxDef::Trans100ToAlpha255(trans));
sds->FinishedDrawing();
}
void DrawingSurface_DrawImage(ScriptDrawingSurface *sds,
int dst_x, int dst_y, int slot, int trans,
int dst_width, int dst_height,
int src_x, int src_y, int src_width, int src_height) {
if ((slot < 0) || (!_GP(spriteset).DoesSpriteExist(slot)))
quit("!DrawingSurface.DrawImage: invalid sprite slot number specified");
DrawingSurface_DrawImageImpl(sds, _GP(spriteset)[slot], dst_x, dst_y, trans, dst_width, dst_height,
src_x, src_y, src_width, src_height, slot, (_GP(game).SpriteInfos[slot].Flags & SPF_ALPHACHANNEL) != 0);
}
void DrawingSurface_DrawImage6(ScriptDrawingSurface *sds, int xx, int yy, int slot, int trans, int width, int height) {
DrawingSurface_DrawImage(sds, xx, yy, slot, trans, width, height, 0, 0, SCR_NO_VALUE, SCR_NO_VALUE);
}
void DrawingSurface_DrawSurface(ScriptDrawingSurface *target, ScriptDrawingSurface *source, int trans,
int dst_x, int dst_y, int dst_width, int dst_height,
int src_x, int src_y, int src_width, int src_height) {
DrawingSurface_DrawImageImpl(target, source->GetBitmapSurface(), dst_x, dst_y, trans, dst_width, dst_height,
src_x, src_y, src_width, src_height, -1, source->hasAlphaChannel != 0);
}
void DrawingSurface_DrawSurface2(ScriptDrawingSurface *target, ScriptDrawingSurface *source, int trans) {
DrawingSurface_DrawSurface(target, source, trans, 0, 0, SCR_NO_VALUE, SCR_NO_VALUE, 0, 0, SCR_NO_VALUE, SCR_NO_VALUE);
}
void DrawingSurface_SetDrawingColor(ScriptDrawingSurface *sds, int newColour) {
sds->currentColourScript = newColour;
// StartDrawing to set up ds to set the colour at the appropriate
// depth for the background
Bitmap *ds = sds->StartDrawing();
if (newColour == SCR_COLOR_TRANSPARENT) {
sds->currentColour = ds->GetMaskColor();
} else {
sds->currentColour = ds->GetCompatibleColor(newColour);
}
sds->FinishedDrawingReadOnly();
}
int DrawingSurface_GetDrawingColor(ScriptDrawingSurface *sds) {
return sds->currentColourScript;
}
void DrawingSurface_SetUseHighResCoordinates(ScriptDrawingSurface *sds, int highRes) {
if (_GP(game).AllowRelativeRes())
sds->highResCoordinates = (highRes) ? 1 : 0;
}
int DrawingSurface_GetUseHighResCoordinates(ScriptDrawingSurface *sds) {
return sds->highResCoordinates;
}
int DrawingSurface_GetHeight(ScriptDrawingSurface *sds) {
Bitmap *ds = sds->StartDrawing();
int height = ds->GetHeight();
sds->FinishedDrawingReadOnly();
sds->SizeToGameResolution(&height);
return height;
}
int DrawingSurface_GetWidth(ScriptDrawingSurface *sds) {
Bitmap *ds = sds->StartDrawing();
int width = ds->GetWidth();
sds->FinishedDrawingReadOnly();
sds->SizeToGameResolution(&width);
return width;
}
void DrawingSurface_Clear(ScriptDrawingSurface *sds, int colour) {
Bitmap *ds = sds->StartDrawing();
int allegroColor;
if ((colour == -SCR_NO_VALUE) || (colour == SCR_COLOR_TRANSPARENT)) {
allegroColor = ds->GetMaskColor();
} else {
allegroColor = ds->GetCompatibleColor(colour);
}
ds->Fill(allegroColor);
sds->FinishedDrawing();
}
void DrawingSurface_DrawCircle(ScriptDrawingSurface *sds, int x, int y, int radius) {
sds->PointToGameResolution(&x, &y);
sds->SizeToGameResolution(&radius);
Bitmap *ds = sds->StartDrawing();
ds->FillCircle(Circle(x, y, radius), sds->currentColour);
sds->FinishedDrawing();
}
void DrawingSurface_DrawRectangle(ScriptDrawingSurface *sds, int x1, int y1, int x2, int y2) {
sds->PointToGameResolution(&x1, &y1);
sds->PointToGameResolution(&x2, &y2);
Bitmap *ds = sds->StartDrawing();
ds->FillRect(Rect(x1, y1, x2, y2), sds->currentColour);
sds->FinishedDrawing();
}
void DrawingSurface_DrawTriangle(ScriptDrawingSurface *sds, int x1, int y1, int x2, int y2, int x3, int y3) {
sds->PointToGameResolution(&x1, &y1);
sds->PointToGameResolution(&x2, &y2);
sds->PointToGameResolution(&x3, &y3);
Bitmap *ds = sds->StartDrawing();
ds->DrawTriangle(Triangle(x1, y1, x2, y2, x3, y3), sds->currentColour);
sds->FinishedDrawing();
}
void DrawingSurface_DrawString(ScriptDrawingSurface *sds, int xx, int yy, int font, const char *text) {
sds->PointToGameResolution(&xx, &yy);
Bitmap *ds = sds->StartDrawing();
// don't use wtextcolor because it will do a 16->32 conversion
color_t text_color = sds->currentColour;
if ((ds->GetColorDepth() <= 8) && (_GP(play).raw_color > 255)) {
text_color = ds->GetCompatibleColor(1);
debug_script_warn("RawPrint: Attempted to use hi-color on 256-col background");
}
String res_str = GUI::ApplyTextDirection(text);
wouttext_outline(ds, xx, yy, font, text_color, res_str.GetCStr());
sds->FinishedDrawing();
}
void DrawingSurface_DrawStringWrapped_Old(ScriptDrawingSurface *sds, int xx, int yy, int wid, int font, int alignment, const char *msg) {
DrawingSurface_DrawStringWrapped(sds, xx, yy, wid, font, ConvertLegacyScriptAlignment((LegacyScriptAlignment)alignment), msg);
}
void DrawingSurface_DrawStringWrapped(ScriptDrawingSurface *sds, int xx, int yy, int wid, int font, int alignment, const char *msg) {
int linespacing = get_font_linespacing(font);
sds->PointToGameResolution(&xx, &yy);
sds->SizeToGameResolution(&wid);
if (break_up_text_into_lines(msg, _GP(Lines), wid, font) == 0)
return;
Bitmap *ds = sds->StartDrawing();
color_t text_color = sds->currentColour;
for (size_t i = 0; i < _GP(Lines).Count(); i++) {
GUI::DrawTextAlignedHor(ds, _GP(Lines)[i].GetCStr(), font, text_color,
xx, xx + wid - 1, yy + linespacing * i, (FrameAlignment)alignment);
}
sds->FinishedDrawing();
}
void DrawingSurface_DrawMessageWrapped(ScriptDrawingSurface *sds, int xx, int yy, int wid, int font, int msgm) {
char displbuf[3000];
get_message_text(msgm, displbuf);
// it's probably too late but check anyway
if (strlen(displbuf) > 2899)
quit("!RawPrintMessageWrapped: message too long");
DrawingSurface_DrawStringWrapped_Old(sds, xx, yy, wid, font, kLegacyScAlignLeft, displbuf);
}
void DrawingSurface_DrawLine(ScriptDrawingSurface *sds, int fromx, int fromy, int tox, int toy, int thickness) {
sds->PointToGameResolution(&fromx, &fromy);
sds->PointToGameResolution(&tox, &toy);
sds->SizeToGameResolution(&thickness);
int ii, jj, xx, yy;
Bitmap *ds = sds->StartDrawing();
// draw several lines to simulate the thickness
color_t draw_color = sds->currentColour;
for (ii = 0; ii < thickness; ii++) {
xx = (ii - (thickness / 2));
for (jj = 0; jj < thickness; jj++) {
yy = (jj - (thickness / 2));
ds->DrawLine(Line(fromx + xx, fromy + yy, tox + xx, toy + yy), draw_color);
}
}
sds->FinishedDrawing();
}
void DrawingSurface_DrawPixel(ScriptDrawingSurface *sds, int x, int y) {
sds->PointToGameResolution(&x, &y);
int thickness = 1;
sds->SizeToGameResolution(&thickness);
int ii, jj;
Bitmap *ds = sds->StartDrawing();
// draw several pixels to simulate the thickness
color_t draw_color = sds->currentColour;
for (ii = 0; ii < thickness; ii++) {
for (jj = 0; jj < thickness; jj++) {
ds->PutPixel(x + ii, y + jj, draw_color);
}
}
sds->FinishedDrawing();
}
int DrawingSurface_GetPixel(ScriptDrawingSurface *sds, int x, int y) {
sds->PointToGameResolution(&x, &y);
Bitmap *ds = sds->StartDrawing();
int rawPixel = ds->GetPixel(x, y);
int maskColor = ds->GetMaskColor();
int colDepth = ds->GetColorDepth();
if (rawPixel == maskColor) {
rawPixel = (unsigned int)SCR_COLOR_TRANSPARENT;
} else if (colDepth > 8) {
int r = getr_depth(colDepth, rawPixel);
int g = getg_depth(colDepth, rawPixel);
int b = getb_depth(colDepth, rawPixel);
rawPixel = Game_GetColorFromRGB(r, g, b);
}
sds->FinishedDrawingReadOnly();
return rawPixel;
}
//=============================================================================
//
// Script API Functions
//
//=============================================================================
// void (ScriptDrawingSurface *sds, int colour)
RuntimeScriptValue Sc_DrawingSurface_Clear(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptDrawingSurface, DrawingSurface_Clear);
}
// ScriptDrawingSurface* (ScriptDrawingSurface *sds)
RuntimeScriptValue Sc_DrawingSurface_CreateCopy(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_OBJAUTO(ScriptDrawingSurface, ScriptDrawingSurface, DrawingSurface_CreateCopy);
}
// void (ScriptDrawingSurface *sds, int x, int y, int radius)
RuntimeScriptValue Sc_DrawingSurface_DrawCircle(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT3(ScriptDrawingSurface, DrawingSurface_DrawCircle);
}
// void (ScriptDrawingSurface* sds, int xx, int yy, int slot, int trans, int width, int height)
RuntimeScriptValue Sc_DrawingSurface_DrawImage6(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT6(ScriptDrawingSurface, DrawingSurface_DrawImage6);
}
RuntimeScriptValue Sc_DrawingSurface_DrawImage(void *self, const RuntimeScriptValue *params, int32_t param_count) {
ASSERT_OBJ_PARAM_COUNT(METHOD, 10);
DrawingSurface_DrawImage((ScriptDrawingSurface *)self, params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue, params[5].IValue,
params[6].IValue, params[7].IValue, params[8].IValue, params[9].IValue);
return RuntimeScriptValue((int32_t)0);
}
// void (ScriptDrawingSurface *sds, int fromx, int fromy, int tox, int toy, int thickness)
RuntimeScriptValue Sc_DrawingSurface_DrawLine(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT5(ScriptDrawingSurface, DrawingSurface_DrawLine);
}
// void (ScriptDrawingSurface *sds, int xx, int yy, int wid, int font, int msgm)
RuntimeScriptValue Sc_DrawingSurface_DrawMessageWrapped(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT5(ScriptDrawingSurface, DrawingSurface_DrawMessageWrapped);
}
// void (ScriptDrawingSurface *sds, int x, int y)
RuntimeScriptValue Sc_DrawingSurface_DrawPixel(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT2(ScriptDrawingSurface, DrawingSurface_DrawPixel);
}
// void (ScriptDrawingSurface *sds, int x1, int y1, int x2, int y2)
RuntimeScriptValue Sc_DrawingSurface_DrawRectangle(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT4(ScriptDrawingSurface, DrawingSurface_DrawRectangle);
}
// void (ScriptDrawingSurface *sds, int xx, int yy, int font, const char* texx, ...)
RuntimeScriptValue Sc_DrawingSurface_DrawString(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_SCRIPT_SPRINTF(DrawingSurface_DrawString, 4);
DrawingSurface_DrawString((ScriptDrawingSurface *)self, params[0].IValue, params[1].IValue, params[2].IValue, scsf_buffer);
return RuntimeScriptValue((int32_t)0);
}
// void (ScriptDrawingSurface *sds, int xx, int yy, int wid, int font, int alignment, const char *msg)
RuntimeScriptValue Sc_DrawingSurface_DrawStringWrapped_Old(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT5_POBJ(ScriptDrawingSurface, DrawingSurface_DrawStringWrapped_Old, const char);
}
RuntimeScriptValue Sc_DrawingSurface_DrawStringWrapped(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_SCRIPT_SPRINTF(DrawingSurface_DrawString, 6);
DrawingSurface_DrawStringWrapped((ScriptDrawingSurface *)self, params[0].IValue, params[1].IValue, params[2].IValue,
params[3].IValue, params[4].IValue, scsf_buffer);
return RuntimeScriptValue((int32_t)0);
}
// void (ScriptDrawingSurface* target, ScriptDrawingSurface* source, int translev)
RuntimeScriptValue Sc_DrawingSurface_DrawSurface2(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_POBJ_PINT(ScriptDrawingSurface, DrawingSurface_DrawSurface2, ScriptDrawingSurface);
}
RuntimeScriptValue Sc_DrawingSurface_DrawSurface(void *self, const RuntimeScriptValue *params, int32_t param_count) {
ASSERT_OBJ_PARAM_COUNT(METHOD, 10);
DrawingSurface_DrawSurface((ScriptDrawingSurface *)self, (ScriptDrawingSurface *)params[0].Ptr,
params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue, params[5].IValue,
params[6].IValue, params[7].IValue, params[8].IValue, params[9].IValue);
return RuntimeScriptValue((int32_t)0);
}
// void (ScriptDrawingSurface *sds, int x1, int y1, int x2, int y2, int x3, int y3)
RuntimeScriptValue Sc_DrawingSurface_DrawTriangle(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT6(ScriptDrawingSurface, DrawingSurface_DrawTriangle);
}
// int (ScriptDrawingSurface *sds, int x, int y)
RuntimeScriptValue Sc_DrawingSurface_GetPixel(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT_PINT2(ScriptDrawingSurface, DrawingSurface_GetPixel);
}
// void (ScriptDrawingSurface* sds)
RuntimeScriptValue Sc_DrawingSurface_Release(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID(ScriptDrawingSurface, DrawingSurface_Release);
}
// int (ScriptDrawingSurface *sds)
RuntimeScriptValue Sc_DrawingSurface_GetDrawingColor(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDrawingSurface, DrawingSurface_GetDrawingColor);
}
// void (ScriptDrawingSurface *sds, int newColour)
RuntimeScriptValue Sc_DrawingSurface_SetDrawingColor(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptDrawingSurface, DrawingSurface_SetDrawingColor);
}
// int (ScriptDrawingSurface *sds)
RuntimeScriptValue Sc_DrawingSurface_GetHeight(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDrawingSurface, DrawingSurface_GetHeight);
}
// int (ScriptDrawingSurface *sds)
RuntimeScriptValue Sc_DrawingSurface_GetUseHighResCoordinates(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDrawingSurface, DrawingSurface_GetUseHighResCoordinates);
}
// void (ScriptDrawingSurface *sds, int highRes)
RuntimeScriptValue Sc_DrawingSurface_SetUseHighResCoordinates(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptDrawingSurface, DrawingSurface_SetUseHighResCoordinates);
}
// int (ScriptDrawingSurface *sds)
RuntimeScriptValue Sc_DrawingSurface_GetWidth(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDrawingSurface, DrawingSurface_GetWidth);
}
//=============================================================================
//
// Exclusive variadic API implementation for Plugins
//
//=============================================================================
void ScPl_DrawingSurface_DrawStringWrapped(ScriptDrawingSurface *sds, int xx, int yy, int wid, int font, int alignment, const char *msg, ...) {
API_PLUGIN_SCRIPT_SPRINTF(msg);
DrawingSurface_DrawStringWrapped(sds, xx, yy, wid, font, alignment, scsf_buffer);
}
void RegisterDrawingSurfaceAPI(ScriptAPIVersion base_api, ScriptAPIVersion /*compat_api */) {
ScFnRegister drawsurf_api[] = {
{"DrawingSurface::Clear^1", API_FN_PAIR(DrawingSurface_Clear)},
{"DrawingSurface::CreateCopy^0", API_FN_PAIR(DrawingSurface_CreateCopy)},
{"DrawingSurface::DrawCircle^3", API_FN_PAIR(DrawingSurface_DrawCircle)},
{"DrawingSurface::DrawImage^6", API_FN_PAIR(DrawingSurface_DrawImage6)},
{"DrawingSurface::DrawImage^10", API_FN_PAIR(DrawingSurface_DrawImage)},
{"DrawingSurface::DrawLine^5", API_FN_PAIR(DrawingSurface_DrawLine)},
{"DrawingSurface::DrawMessageWrapped^5", API_FN_PAIR(DrawingSurface_DrawMessageWrapped)},
{"DrawingSurface::DrawPixel^2", API_FN_PAIR(DrawingSurface_DrawPixel)},
{"DrawingSurface::DrawRectangle^4", API_FN_PAIR(DrawingSurface_DrawRectangle)},
{"DrawingSurface::DrawString^104", Sc_DrawingSurface_DrawString},
{"DrawingSurface::DrawSurface^2", API_FN_PAIR(DrawingSurface_DrawSurface2)},
{"DrawingSurface::DrawSurface^10", API_FN_PAIR(DrawingSurface_DrawSurface)},
{"DrawingSurface::DrawTriangle^6", API_FN_PAIR(DrawingSurface_DrawTriangle)},
{"DrawingSurface::GetPixel^2", API_FN_PAIR(DrawingSurface_GetPixel)},
{"DrawingSurface::Release^0", API_FN_PAIR(DrawingSurface_Release)},
{"DrawingSurface::get_DrawingColor", API_FN_PAIR(DrawingSurface_GetDrawingColor)},
{"DrawingSurface::set_DrawingColor", API_FN_PAIR(DrawingSurface_SetDrawingColor)},
{"DrawingSurface::get_Height", API_FN_PAIR(DrawingSurface_GetHeight)},
{"DrawingSurface::get_UseHighResCoordinates", API_FN_PAIR(DrawingSurface_GetUseHighResCoordinates)},
{"DrawingSurface::set_UseHighResCoordinates", API_FN_PAIR(DrawingSurface_SetUseHighResCoordinates)},
{"DrawingSurface::get_Width", API_FN_PAIR(DrawingSurface_GetWidth)},
};
ccAddExternalFunctions361(drawsurf_api);
// Few functions have to be selected based on API level
if (base_api < kScriptAPI_v350) {
ccAddExternalObjectFunction361("DrawingSurface::DrawStringWrapped^6", API_FN_PAIR(DrawingSurface_DrawStringWrapped_Old));
}
else { // old non-variadic and new variadic variants
ccAddExternalObjectFunction361("DrawingSurface::DrawStringWrapped^6", API_FN_PAIR(DrawingSurface_DrawStringWrapped));
ccAddExternalObjectFunction361("DrawingSurface::DrawStringWrapped^106", Sc_DrawingSurface_DrawStringWrapped, (void *)ScPl_DrawingSurface_DrawStringWrapped);
}
}
} // namespace AGS3

View File

@@ -0,0 +1,57 @@
/* 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 AGS_ENGINE_AC_DRAWING_SURFACE_H
#define AGS_ENGINE_AC_DRAWING_SURFACE_H
#include "ags/engine/ac/dynobj/script_drawing_surface.h"
namespace AGS3 {
void DrawingSurface_Release(ScriptDrawingSurface *sds);
// convert actual co-ordinate back to what the script is expecting
ScriptDrawingSurface *DrawingSurface_CreateCopy(ScriptDrawingSurface *sds);
void DrawingSurface_DrawSurface(ScriptDrawingSurface *target, ScriptDrawingSurface *source, int trans,
int dst_x, int dst_y, int dst_width, int dst_height,
int src_x, int src_y, int src_width, int src_height);
void DrawingSurface_DrawSurface2(ScriptDrawingSurface *target, ScriptDrawingSurface *source, int trans);
void DrawingSurface_DrawImage6(ScriptDrawingSurface *sds, int xx, int yy, int slot, int trans, int width, int height);
void DrawingSurface_SetDrawingColor(ScriptDrawingSurface *sds, int newColour);
int DrawingSurface_GetDrawingColor(ScriptDrawingSurface *sds);
void DrawingSurface_SetUseHighResCoordinates(ScriptDrawingSurface *sds, int highRes);
int DrawingSurface_GetUseHighResCoordinates(ScriptDrawingSurface *sds);
int DrawingSurface_GetHeight(ScriptDrawingSurface *sds);
int DrawingSurface_GetWidth(ScriptDrawingSurface *sds);
void DrawingSurface_Clear(ScriptDrawingSurface *sds, int colour);
void DrawingSurface_DrawCircle(ScriptDrawingSurface *sds, int x, int y, int radius);
void DrawingSurface_DrawRectangle(ScriptDrawingSurface *sds, int x1, int y1, int x2, int y2);
void DrawingSurface_DrawTriangle(ScriptDrawingSurface *sds, int x1, int y1, int x2, int y2, int x3, int y3);
void DrawingSurface_DrawString(ScriptDrawingSurface *sds, int xx, int yy, int font, const char *text);
void DrawingSurface_DrawStringWrapped(ScriptDrawingSurface *sds, int xx, int yy, int wid, int font, int alignment, const char *msg);
void DrawingSurface_DrawStringWrapped_Old(ScriptDrawingSurface *sds, int xx, int yy, int wid, int font, int alignment, const char *msg);
void DrawingSurface_DrawMessageWrapped(ScriptDrawingSurface *sds, int xx, int yy, int wid, int font, int msgm);
void DrawingSurface_DrawLine(ScriptDrawingSurface *sds, int fromx, int fromy, int tox, int toy, int thickness);
void DrawingSurface_DrawPixel(ScriptDrawingSurface *sds, int x, int y);
int DrawingSurface_GetPixel(ScriptDrawingSurface *sds, int x, int y);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,609 @@
/* 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 "ags/engine/ac/dynamic_sprite.h"
#include "ags/shared/ac/common.h"
#include "ags/engine/ac/draw.h"
#include "ags/engine/ac/game.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/engine/ac/game_state.h"
#include "ags/engine/ac/global_dynamic_sprite.h"
#include "ags/engine/ac/global_game.h"
#include "ags/engine/ac/math.h" // M_PI
#include "ags/engine/ac/path_helper.h"
#include "ags/engine/ac/room_object.h"
#include "ags/engine/ac/room_status.h"
#include "ags/engine/ac/system.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/engine/debugging/debug_log.h"
#include "ags/shared/game/room_struct.h"
#include "ags/shared/gui/gui_button.h"
#include "ags/shared/ac/sprite_cache.h"
#include "ags/engine/gfx/graphics_driver.h"
#include "ags/engine/script/runtime_script_value.h"
#include "ags/shared/debugging/out.h"
#include "ags/engine/script/script_api.h"
#include "ags/engine/script/script_runtime.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace Shared;
using namespace Engine;
// ** SCRIPT DYNAMIC SPRITE
void DynamicSprite_Delete(ScriptDynamicSprite *sds) {
if (sds->slot) {
free_dynamic_sprite(sds->slot);
sds->slot = 0;
}
}
ScriptDrawingSurface *DynamicSprite_GetDrawingSurface(ScriptDynamicSprite *dss) {
ScriptDrawingSurface *surface = new ScriptDrawingSurface();
surface->dynamicSpriteNumber = dss->slot;
if ((_GP(game).SpriteInfos[dss->slot].Flags & SPF_ALPHACHANNEL) != 0)
surface->hasAlphaChannel = true;
ccRegisterManagedObject(surface, surface);
return surface;
}
int DynamicSprite_GetGraphic(ScriptDynamicSprite *sds) {
if (sds->slot == 0)
quit("!DynamicSprite.Graphic: Cannot get graphic, sprite has been deleted");
return sds->slot;
}
int DynamicSprite_GetWidth(ScriptDynamicSprite *sds) {
return game_to_data_coord(_GP(game).SpriteInfos[sds->slot].Width);
}
int DynamicSprite_GetHeight(ScriptDynamicSprite *sds) {
return game_to_data_coord(_GP(game).SpriteInfos[sds->slot].Height);
}
int DynamicSprite_GetColorDepth(ScriptDynamicSprite *sds) {
// Dynamic sprite ensures the sprite exists always
int depth = _GP(spriteset)[sds->slot]->GetColorDepth();
if (depth == 15)
depth = 16;
if (depth == 24)
depth = 32;
return depth;
}
void DynamicSprite_Resize(ScriptDynamicSprite *sds, int width, int height) {
if ((width < 1) || (height < 1))
quit("!DynamicSprite.Resize: width and height must be greater than zero");
if (sds->slot == 0)
quit("!DynamicSprite.Resize: sprite has been deleted");
data_to_game_coords(&width, &height);
if (width * height >= 25000000)
quitprintf("!DynamicSprite.Resize: new size is too large: %d x %d", width, height);
// resize the sprite to the requested size
Bitmap *sprite = _GP(spriteset)[sds->slot];
std::unique_ptr<Bitmap> new_pic(BitmapHelper::CreateBitmap(width, height, sprite->GetColorDepth()));
new_pic->StretchBlt(sprite,
RectWH(0, 0, _GP(game).SpriteInfos[sds->slot].Width, _GP(game).SpriteInfos[sds->slot].Height),
RectWH(0, 0, width, height));
add_dynamic_sprite(sds->slot, std::move(new_pic), (_GP(game).SpriteInfos[sds->slot].Flags & SPF_ALPHACHANNEL) != 0);
}
void DynamicSprite_Flip(ScriptDynamicSprite *sds, int direction) {
if ((direction < 1) || (direction > 3))
quit("!DynamicSprite.Flip: invalid direction");
if (sds->slot == 0)
quit("!DynamicSprite.Flip: sprite has been deleted");
// resize the sprite to the requested size
Bitmap *sprite = _GP(spriteset)[sds->slot];
std::unique_ptr<Bitmap> new_pic(BitmapHelper::CreateTransparentBitmap(sprite->GetWidth(), sprite->GetHeight(), sprite->GetColorDepth()));
// AGS script FlipDirection corresponds to internal GraphicFlip
new_pic->FlipBlt(sprite, 0, 0, static_cast<GraphicFlip>(direction));
add_dynamic_sprite(sds->slot, std::move(new_pic), (_GP(game).SpriteInfos[sds->slot].Flags & SPF_ALPHACHANNEL) != 0);
}
void DynamicSprite_CopyTransparencyMask(ScriptDynamicSprite *sds, int sourceSprite) {
if (sds->slot == 0)
quit("!DynamicSprite.CopyTransparencyMask: sprite has been deleted");
if ((_GP(game).SpriteInfos[sds->slot].Width != _GP(game).SpriteInfos[sourceSprite].Width) ||
(_GP(game).SpriteInfos[sds->slot].Height != _GP(game).SpriteInfos[sourceSprite].Height)) {
quit("!DynamicSprite.CopyTransparencyMask: sprites are not the same size");
}
Bitmap *target = _GP(spriteset)[sds->slot];
Bitmap *source = _GP(spriteset)[sourceSprite];
if (target->GetColorDepth() != source->GetColorDepth()) {
quit("!DynamicSprite.CopyTransparencyMask: sprites are not the same colour depth");
}
// set the target's alpha channel depending on the source
bool dst_has_alpha = (_GP(game).SpriteInfos[sds->slot].Flags & SPF_ALPHACHANNEL) != 0;
bool src_has_alpha = (_GP(game).SpriteInfos[sourceSprite].Flags & SPF_ALPHACHANNEL) != 0;
_GP(game).SpriteInfos[sds->slot].Flags &= ~SPF_ALPHACHANNEL;
if (src_has_alpha) {
_GP(game).SpriteInfos[sds->slot].Flags |= SPF_ALPHACHANNEL;
}
BitmapHelper::CopyTransparency(target, source, dst_has_alpha, src_has_alpha);
}
void DynamicSprite_ChangeCanvasSize(ScriptDynamicSprite *sds, int width, int height, int x, int y) {
if (sds->slot == 0)
quit("!DynamicSprite.ChangeCanvasSize: sprite has been deleted");
if ((width < 1) || (height < 1))
quit("!DynamicSprite.ChangeCanvasSize: new size is too small");
data_to_game_coords(&x, &y);
data_to_game_coords(&width, &height);
Bitmap *sprite = _GP(spriteset)[sds->slot];
std::unique_ptr<Bitmap> new_pic(BitmapHelper::CreateTransparentBitmap(width, height, sprite->GetColorDepth()));
// blit it into the enlarged image
new_pic->Blit(sprite, 0, 0, x, y, sprite->GetWidth(), sprite->GetHeight());
// replace the bitmap in the sprite set
add_dynamic_sprite(sds->slot, std::move(new_pic), (_GP(game).SpriteInfos[sds->slot].Flags & SPF_ALPHACHANNEL) != 0);
}
void DynamicSprite_Crop(ScriptDynamicSprite *sds, int x1, int y1, int width, int height) {
if ((width < 1) || (height < 1))
quit("!DynamicSprite.Crop: co-ordinates do not make sense");
if (sds->slot == 0)
quit("!DynamicSprite.Crop: sprite has been deleted");
data_to_game_coords(&x1, &y1);
data_to_game_coords(&width, &height);
if ((width > _GP(game).SpriteInfos[sds->slot].Width) || (height > _GP(game).SpriteInfos[sds->slot].Height))
quit("!DynamicSprite.Crop: requested to crop an area larger than the source");
Bitmap *sprite = _GP(spriteset)[sds->slot];
std::unique_ptr<Bitmap> new_pic(BitmapHelper::CreateBitmap(width, height, sprite->GetColorDepth()));
// blit it cropped
new_pic->Blit(sprite, x1, y1, 0, 0, new_pic->GetWidth(), new_pic->GetHeight());
// replace the bitmap in the sprite set
add_dynamic_sprite(sds->slot, std::move(new_pic), (_GP(game).SpriteInfos[sds->slot].Flags & SPF_ALPHACHANNEL) != 0);
}
void DynamicSprite_Rotate(ScriptDynamicSprite *sds, int angle, int width, int height) {
if ((angle < 1) || (angle > 359))
quit("!DynamicSprite.Rotate: invalid angle (must be 1-359)");
if (sds->slot == 0)
quit("!DynamicSprite.Rotate: sprite has been deleted");
if ((width == SCR_NO_VALUE) || (height == SCR_NO_VALUE)) {
// calculate the new image size automatically
// 1 degree = 181 degrees in terms of x/y size, so % 180
int useAngle = angle % 180;
// and 0..90 is the same as 180..90
if (useAngle > 90)
useAngle = 180 - useAngle;
// useAngle is now between 0 and 90 (otherwise the sin/cos stuff doesn't work)
double angleInRadians = (double)useAngle * (M_PI / 180.0);
double sinVal = sin(angleInRadians);
double cosVal = cos(angleInRadians);
width = (cosVal * (double)_GP(game).SpriteInfos[sds->slot].Width + sinVal * (double)_GP(game).SpriteInfos[sds->slot].Height);
height = (sinVal * (double)_GP(game).SpriteInfos[sds->slot].Width + cosVal * (double)_GP(game).SpriteInfos[sds->slot].Height);
} else {
data_to_game_coords(&width, &height);
}
// convert to allegro angle
angle = (angle * 256) / 360;
// resize the sprite to the requested size
Bitmap *sprite = _GP(spriteset)[sds->slot];
std::unique_ptr<Bitmap> new_pic(BitmapHelper::CreateTransparentBitmap(width, height, sprite->GetColorDepth()));
// rotate the sprite about its centre
// (+ width%2 fixes one pixel offset problem)
new_pic->RotateBlt(sprite, width / 2 + width % 2, height / 2,
sprite->GetWidth() / 2, sprite->GetHeight() / 2, itofix(angle));
// replace the bitmap in the sprite set
add_dynamic_sprite(sds->slot, std::move(new_pic), (_GP(game).SpriteInfos[sds->slot].Flags & SPF_ALPHACHANNEL) != 0);
}
void DynamicSprite_Tint(ScriptDynamicSprite *sds, int red, int green, int blue, int saturation, int luminance) {
Bitmap *source = _GP(spriteset)[sds->slot];
std::unique_ptr<Bitmap> new_pic(BitmapHelper::CreateBitmap(source->GetWidth(), source->GetHeight(), source->GetColorDepth()));
tint_image(new_pic.get(), source, red, green, blue, saturation, (luminance * 25) / 10);
add_dynamic_sprite(sds->slot, std::move(new_pic), (_GP(game).SpriteInfos[sds->slot].Flags & SPF_ALPHACHANNEL) != 0);
}
int DynamicSprite_SaveToFile(ScriptDynamicSprite *sds, const char *namm) {
if (sds->slot == 0)
quit("!DynamicSprite.SaveToFile: sprite has been deleted");
auto filename = String(namm);
if (filename.FindChar('.') == String::NoIndex)
filename.Append(".bmp");
ResolvedPath rp;
if (!ResolveWritePathAndCreateDirs(filename, rp))
return 0;
return _GP(spriteset)[sds->slot]->SaveToFile(rp.FullPath, _G(palette)) ? 1 : 0;
}
ScriptDynamicSprite *DynamicSprite_CreateFromSaveGame(int sgslot, int width, int height) {
int slotnum = LoadSaveSlotScreenshot(sgslot, width, height);
if (slotnum) {
ScriptDynamicSprite *new_spr = new ScriptDynamicSprite(slotnum);
return new_spr;
}
return nullptr;
}
ScriptDynamicSprite *DynamicSprite_CreateFromFile(const char *filename) {
int slotnum = LoadImageFile(filename);
if (slotnum) {
ScriptDynamicSprite *new_spr = new ScriptDynamicSprite(slotnum);
return new_spr;
}
return nullptr;
}
ScriptDynamicSprite *DynamicSprite_CreateFromScreenShot(int width, int height) {
// TODO: refactor and merge with create_savegame_screenshot()
if (!_GP(spriteset).HasFreeSlots())
return nullptr;
// NOTE: be aware that by the historical logic AGS makes a screenshot
// of a "main viewport", that may be smaller in legacy "letterbox" mode.
const Rect &viewport = _GP(play).GetMainViewport();
if (width <= 0)
width = viewport.GetWidth();
else
width = data_to_game_coord(width);
if (height <= 0)
height = viewport.GetHeight();
else
height = data_to_game_coord(height);
std::unique_ptr<Bitmap> new_pic(CopyScreenIntoBitmap(width, height, &viewport));
// replace the bitmap in the sprite set
int new_slot = add_dynamic_sprite(std::move(new_pic));
return new ScriptDynamicSprite(new_slot);
}
ScriptDynamicSprite *DynamicSprite_CreateFromExistingSprite(int slot, int preserveAlphaChannel) {
if (!_GP(spriteset).HasFreeSlots())
return nullptr;
if (!_GP(spriteset).DoesSpriteExist(slot))
quitprintf("DynamicSprite.CreateFromExistingSprite: sprite %d does not exist", slot);
// create a new sprite as a copy of the existing one
std::unique_ptr<Bitmap> new_pic(BitmapHelper::CreateBitmapCopy(_GP(spriteset)[slot]));
if (!new_pic)
return nullptr;
bool hasAlpha = (preserveAlphaChannel) && ((_GP(game).SpriteInfos[slot].Flags & SPF_ALPHACHANNEL) != 0);
int new_slot = add_dynamic_sprite(std::move(new_pic), hasAlpha);
return new ScriptDynamicSprite(new_slot);
}
ScriptDynamicSprite *DynamicSprite_CreateFromDrawingSurface(ScriptDrawingSurface *sds, int x, int y, int width, int height) {
if (!_GP(spriteset).HasFreeSlots())
return nullptr;
if (width <= 0 || height <= 0) {
debug_script_warn("WARNING: DynamicSprite.CreateFromDrawingSurface: invalid size %d x %d, will adjust", width, height);
width = std::max(1, width);
height = std::max(1, height);
}
// use DrawingSurface resolution
sds->PointToGameResolution(&x, &y);
sds->SizeToGameResolution(&width, &height);
Bitmap *ds = sds->StartDrawing();
if ((x < 0) || (y < 0) || (x + width > ds->GetWidth()) || (y + height > ds->GetHeight()))
quit("!DynamicSprite.CreateFromDrawingSurface: requested area is outside the surface");
std::unique_ptr<Bitmap> new_pic(BitmapHelper::CreateBitmap(width, height, ds->GetColorDepth()));
if (!new_pic)
return nullptr;
new_pic->Blit(ds, x, y, 0, 0, width, height);
sds->FinishedDrawingReadOnly();
int new_slot = add_dynamic_sprite(std::move(new_pic), (sds->hasAlphaChannel != 0));
return new ScriptDynamicSprite(new_slot);
}
ScriptDynamicSprite *DynamicSprite_Create(int width, int height, int alphaChannel) {
if (width <= 0 || height <= 0) {
debug_script_warn("WARNING: DynamicSprite.Create: invalid size %d x %d, will adjust", width, height);
width = MAX(1, width);
height = MAX(1, height);
}
data_to_game_coords(&width, &height);
if (!_GP(spriteset).HasFreeSlots())
return nullptr;
std::unique_ptr<Bitmap> new_pic(CreateCompatBitmap(width, height));
if (!new_pic)
return nullptr;
new_pic->ClearTransparent();
if ((alphaChannel) && (_GP(game).GetColorDepth() < 32))
alphaChannel = false;
int new_slot = add_dynamic_sprite(std::move(new_pic), alphaChannel != 0);
return new ScriptDynamicSprite(new_slot);
}
ScriptDynamicSprite *DynamicSprite_CreateFromExistingSprite_Old(int slot) {
return DynamicSprite_CreateFromExistingSprite(slot, 0);
}
ScriptDynamicSprite *DynamicSprite_CreateFromBackground(int frame, int x1, int y1, int width, int height) {
if (frame == SCR_NO_VALUE) {
frame = _GP(play).bg_frame;
} else if ((frame < 0) || ((size_t)frame >= _GP(thisroom).BgFrameCount))
quit("!DynamicSprite.CreateFromBackground: invalid frame specified");
if (x1 == SCR_NO_VALUE)
x1 = 0;
if (y1 == SCR_NO_VALUE)
y1 = 0;
if (width == SCR_NO_VALUE)
width = _GP(play).room_width;
if (height == SCR_NO_VALUE)
height = _GP(play).room_height;
if (width <= 0 || height <= 0) {
debug_script_warn("WARNING: DynamicSprite.CreateFromBackground: invalid size %d x %d, will adjust", width, height);
width = std::max(1, width);
height = std::max(1, height);
}
if ((x1 < 0) || (y1 < 0) || (x1 + width > _GP(play).room_width) || (y1 + height > _GP(play).room_height))
quit("!DynamicSprite.CreateFromBackground: invalid co-ordinates specified");
data_to_game_coords(&x1, &y1);
data_to_game_coords(&width, &height);
if (!_GP(spriteset).HasFreeSlots())
return nullptr;
// create a new sprite as a copy of the existing one
std::unique_ptr<Bitmap> new_pic(BitmapHelper::CreateBitmap(width, height, _GP(thisroom).BgFrames[frame].Graphic->GetColorDepth()));
if (!new_pic)
return nullptr;
new_pic->Blit(_GP(thisroom).BgFrames[frame].Graphic.get(), x1, y1, 0, 0, width, height);
int new_slot = add_dynamic_sprite(std::move(new_pic));
return new ScriptDynamicSprite(new_slot);
}
//=============================================================================
int add_dynamic_sprite(std::unique_ptr<Bitmap> image, bool has_alpha, uint32_t extra_flags) {
int slot = _GP(spriteset).GetFreeIndex();
if (slot <= 0)
return 0;
return add_dynamic_sprite(slot, std::move(image), has_alpha, extra_flags);
}
int add_dynamic_sprite(int slot, std::unique_ptr<Bitmap> image, bool has_alpha, uint32_t extra_flags) {
assert(slot > 0 && !_GP(spriteset).IsAssetSprite(slot));
if (slot <= 0 || _GP(spriteset).IsAssetSprite(slot))
return 0; // invalid slot, or reserved for the static sprite
uint32_t flags = SPF_DYNAMICALLOC | (SPF_ALPHACHANNEL * has_alpha) | extra_flags;
if(!_GP(spriteset).SetSprite(slot, std::move(image), flags))
return 0; // failed to add the sprite, bad image or realloc failed
// Notify a new (normal) dynamic sprite in case some objects
// have this number assigned to their Graphic property
if ((extra_flags & SPF_OBJECTOWNED) == 0)
game_sprite_updated(slot);
return slot;
}
void free_dynamic_sprite(int slot, bool notify_all) {
assert((slot > 0) && (static_cast<size_t>(slot) < _GP(game).SpriteInfos.size()) &&
(_GP(game).SpriteInfos[slot].Flags & SPF_DYNAMICALLOC));
if ((slot <= 0) || (static_cast<size_t>(slot) >= _GP(game).SpriteInfos.size()) ||
(_GP(game).SpriteInfos[slot].Flags & SPF_DYNAMICALLOC) == 0)
return;
_GP(spriteset).DeleteSprite(slot);
if (notify_all)
game_sprite_updated(slot, true);
}
//=============================================================================
//
// Script API Functions
//
//=============================================================================
// void (ScriptDynamicSprite *sds, int width, int height, int x, int y)
RuntimeScriptValue Sc_DynamicSprite_ChangeCanvasSize(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT4(ScriptDynamicSprite, DynamicSprite_ChangeCanvasSize);
}
// void (ScriptDynamicSprite *sds, int sourceSprite)
RuntimeScriptValue Sc_DynamicSprite_CopyTransparencyMask(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptDynamicSprite, DynamicSprite_CopyTransparencyMask);
}
// void (ScriptDynamicSprite *sds, int x1, int y1, int width, int height)
RuntimeScriptValue Sc_DynamicSprite_Crop(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT4(ScriptDynamicSprite, DynamicSprite_Crop);
}
// void (ScriptDynamicSprite *sds)
RuntimeScriptValue Sc_DynamicSprite_Delete(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID(ScriptDynamicSprite, DynamicSprite_Delete);
}
// void (ScriptDynamicSprite *sds, int direction)
RuntimeScriptValue Sc_DynamicSprite_Flip(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT(ScriptDynamicSprite, DynamicSprite_Flip);
}
// ScriptDrawingSurface* (ScriptDynamicSprite *dss)
RuntimeScriptValue Sc_DynamicSprite_GetDrawingSurface(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_OBJAUTO(ScriptDynamicSprite, ScriptDrawingSurface, DynamicSprite_GetDrawingSurface);
}
// void (ScriptDynamicSprite *sds, int width, int height)
RuntimeScriptValue Sc_DynamicSprite_Resize(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT2(ScriptDynamicSprite, DynamicSprite_Resize);
}
// void (ScriptDynamicSprite *sds, int angle, int width, int height)
RuntimeScriptValue Sc_DynamicSprite_Rotate(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT3(ScriptDynamicSprite, DynamicSprite_Rotate);
}
// int (ScriptDynamicSprite *sds, const char* namm)
RuntimeScriptValue Sc_DynamicSprite_SaveToFile(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT_POBJ(ScriptDynamicSprite, DynamicSprite_SaveToFile, const char);
}
// void (ScriptDynamicSprite *sds, int red, int green, int blue, int saturation, int luminance)
RuntimeScriptValue Sc_DynamicSprite_Tint(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_VOID_PINT5(ScriptDynamicSprite, DynamicSprite_Tint);
}
// int (ScriptDynamicSprite *sds)
RuntimeScriptValue Sc_DynamicSprite_GetColorDepth(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDynamicSprite, DynamicSprite_GetColorDepth);
}
// int (ScriptDynamicSprite *sds)
RuntimeScriptValue Sc_DynamicSprite_GetGraphic(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDynamicSprite, DynamicSprite_GetGraphic);
}
// int (ScriptDynamicSprite *sds)
RuntimeScriptValue Sc_DynamicSprite_GetHeight(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDynamicSprite, DynamicSprite_GetHeight);
}
// int (ScriptDynamicSprite *sds)
RuntimeScriptValue Sc_DynamicSprite_GetWidth(void *self, const RuntimeScriptValue *params, int32_t param_count) {
API_OBJCALL_INT(ScriptDynamicSprite, DynamicSprite_GetWidth);
}
// ScriptDynamicSprite* (int width, int height, int alphaChannel)
RuntimeScriptValue Sc_DynamicSprite_Create(const RuntimeScriptValue *params, int32_t param_count) {
API_SCALL_OBJAUTO_PINT3(ScriptDynamicSprite, DynamicSprite_Create);
}
// ScriptDynamicSprite* (int frame, int x1, int y1, int width, int height)
RuntimeScriptValue Sc_DynamicSprite_CreateFromBackground(const RuntimeScriptValue *params, int32_t param_count) {
API_SCALL_OBJAUTO_PINT5(ScriptDynamicSprite, DynamicSprite_CreateFromBackground);
}
// ScriptDynamicSprite* (ScriptDrawingSurface *sds, int x, int y, int width, int height)
RuntimeScriptValue Sc_DynamicSprite_CreateFromDrawingSurface(const RuntimeScriptValue *params, int32_t param_count) {
API_SCALL_OBJAUTO_POBJ_PINT4(ScriptDynamicSprite, DynamicSprite_CreateFromDrawingSurface, ScriptDrawingSurface);
}
// ScriptDynamicSprite* (int slot)
RuntimeScriptValue Sc_DynamicSprite_CreateFromExistingSprite_Old(const RuntimeScriptValue *params, int32_t param_count) {
API_SCALL_OBJAUTO_PINT(ScriptDynamicSprite, DynamicSprite_CreateFromExistingSprite_Old);
}
// ScriptDynamicSprite* (int slot, int preserveAlphaChannel)
RuntimeScriptValue Sc_DynamicSprite_CreateFromExistingSprite(const RuntimeScriptValue *params, int32_t param_count) {
API_SCALL_OBJAUTO_PINT2(ScriptDynamicSprite, DynamicSprite_CreateFromExistingSprite);
}
// ScriptDynamicSprite* (const char *filename)
RuntimeScriptValue Sc_DynamicSprite_CreateFromFile(const RuntimeScriptValue *params, int32_t param_count) {
API_SCALL_OBJAUTO_POBJ(ScriptDynamicSprite, DynamicSprite_CreateFromFile, const char);
}
// ScriptDynamicSprite* (int sgslot, int width, int height)
RuntimeScriptValue Sc_DynamicSprite_CreateFromSaveGame(const RuntimeScriptValue *params, int32_t param_count) {
API_SCALL_OBJAUTO_PINT3(ScriptDynamicSprite, DynamicSprite_CreateFromSaveGame);
}
// ScriptDynamicSprite* (int width, int height)
RuntimeScriptValue Sc_DynamicSprite_CreateFromScreenShot(const RuntimeScriptValue *params, int32_t param_count) {
API_SCALL_OBJAUTO_PINT2(ScriptDynamicSprite, DynamicSprite_CreateFromScreenShot);
}
void RegisterDynamicSpriteAPI() {
ScFnRegister dynsprite_api[] = {
{"DynamicSprite::Create^3", API_FN_PAIR(DynamicSprite_Create)},
{"DynamicSprite::CreateFromBackground", API_FN_PAIR(DynamicSprite_CreateFromBackground)},
{"DynamicSprite::CreateFromDrawingSurface^5", API_FN_PAIR(DynamicSprite_CreateFromDrawingSurface)},
{"DynamicSprite::CreateFromExistingSprite^1", API_FN_PAIR(DynamicSprite_CreateFromExistingSprite_Old)},
{"DynamicSprite::CreateFromExistingSprite^2", API_FN_PAIR(DynamicSprite_CreateFromExistingSprite)},
{"DynamicSprite::CreateFromFile", API_FN_PAIR(DynamicSprite_CreateFromFile)},
{"DynamicSprite::CreateFromSaveGame", API_FN_PAIR(DynamicSprite_CreateFromSaveGame)},
{"DynamicSprite::CreateFromScreenShot", API_FN_PAIR(DynamicSprite_CreateFromScreenShot)},
{"DynamicSprite::ChangeCanvasSize^4", API_FN_PAIR(DynamicSprite_ChangeCanvasSize)},
{"DynamicSprite::CopyTransparencyMask^1", API_FN_PAIR(DynamicSprite_CopyTransparencyMask)},
{"DynamicSprite::Crop^4", API_FN_PAIR(DynamicSprite_Crop)},
{"DynamicSprite::Delete", API_FN_PAIR(DynamicSprite_Delete)},
{"DynamicSprite::Flip^1", API_FN_PAIR(DynamicSprite_Flip)},
{"DynamicSprite::GetDrawingSurface^0", API_FN_PAIR(DynamicSprite_GetDrawingSurface)},
{"DynamicSprite::Resize^2", API_FN_PAIR(DynamicSprite_Resize)},
{"DynamicSprite::Rotate^3", API_FN_PAIR(DynamicSprite_Rotate)},
{"DynamicSprite::SaveToFile^1", API_FN_PAIR(DynamicSprite_SaveToFile)},
{"DynamicSprite::Tint^5", API_FN_PAIR(DynamicSprite_Tint)},
{"DynamicSprite::get_ColorDepth", API_FN_PAIR(DynamicSprite_GetColorDepth)},
{"DynamicSprite::get_Graphic", API_FN_PAIR(DynamicSprite_GetGraphic)},
{"DynamicSprite::get_Height", API_FN_PAIR(DynamicSprite_GetHeight)},
{"DynamicSprite::get_Width", API_FN_PAIR(DynamicSprite_GetWidth)},
};
ccAddExternalFunctions361(dynsprite_api);
}
} // namespace AGS3

View File

@@ -0,0 +1,67 @@
/* 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 AGS_ENGINE_AC_DYNAMICSPRITE_H
#define AGS_ENGINE_AC_DYNAMICSPRITE_H
#include "ags/engine/ac/dynobj/script_dynamic_sprite.h"
#include "ags/engine/ac/dynobj/script_drawing_surface.h"
namespace AGS3 {
void DynamicSprite_Delete(ScriptDynamicSprite *sds);
ScriptDrawingSurface *DynamicSprite_GetDrawingSurface(ScriptDynamicSprite *dss);
int DynamicSprite_GetGraphic(ScriptDynamicSprite *sds);
int DynamicSprite_GetWidth(ScriptDynamicSprite *sds);
int DynamicSprite_GetHeight(ScriptDynamicSprite *sds);
int DynamicSprite_GetColorDepth(ScriptDynamicSprite *sds);
void DynamicSprite_Resize(ScriptDynamicSprite *sds, int width, int height);
void DynamicSprite_Flip(ScriptDynamicSprite *sds, int direction);
void DynamicSprite_CopyTransparencyMask(ScriptDynamicSprite *sds, int sourceSprite);
void DynamicSprite_ChangeCanvasSize(ScriptDynamicSprite *sds, int width, int height, int x, int y);
void DynamicSprite_Crop(ScriptDynamicSprite *sds, int x1, int y1, int width, int height);
void DynamicSprite_Rotate(ScriptDynamicSprite *sds, int angle, int width, int height);
void DynamicSprite_Tint(ScriptDynamicSprite *sds, int red, int green, int blue, int saturation, int luminance);
int DynamicSprite_SaveToFile(ScriptDynamicSprite *sds, const char *namm);
ScriptDynamicSprite *DynamicSprite_CreateFromSaveGame(int sgslot, int width, int height);
ScriptDynamicSprite *DynamicSprite_CreateFromFile(const char *filename);
ScriptDynamicSprite *DynamicSprite_CreateFromScreenShot(int width, int height);
ScriptDynamicSprite *DynamicSprite_CreateFromExistingSprite(int slot, int preserveAlphaChannel);
ScriptDynamicSprite *DynamicSprite_CreateFromDrawingSurface(ScriptDrawingSurface *sds, int x, int y, int width, int height);
ScriptDynamicSprite *DynamicSprite_Create(int width, int height, int alphaChannel);
ScriptDynamicSprite *DynamicSprite_CreateFromExistingSprite_Old(int slot);
ScriptDynamicSprite *DynamicSprite_CreateFromBackground(int frame, int x1, int y1, int width, int height);
// Registers a new dynamic sprite, and returns a slot number;
// returns 0 if no free slot could be found or allocated.
// Updates game.SpriteInfos[].
int add_dynamic_sprite(std::unique_ptr<AGS::Shared::Bitmap> image, bool has_alpha = false, uint32_t extra_flags = 0u);
// Registers a new dynamic sprite in the given slot number,
// previous bitmap on this slot (if any) will be deleted.
// Returns same slot number on success, or 0 if there was an error.
// Updates game.SpriteInfos[].
int add_dynamic_sprite(int slot, std::unique_ptr<AGS::Shared::Bitmap> image, bool hasAlpha = false, uint32_t extra_flags = 0u);
// Disposes a dynamic sprite, and frees the slot
void free_dynamic_sprite(int slot, bool notify_all = true);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,38 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_ALL_DYNAMIC_CLASSES_H
#define AGS_ENGINE_AC_DYNOBJ_ALL_DYNAMIC_CLASSES_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
#include "ags/engine/ac/dynobj/cc_audio_channel.h"
#include "ags/engine/ac/dynobj/cc_audio_clip.h"
#include "ags/engine/ac/dynobj/cc_character.h"
#include "ags/engine/ac/dynobj/cc_dialog.h"
#include "ags/engine/ac/dynobj/cc_gui.h"
#include "ags/engine/ac/dynobj/cc_gui_object.h"
#include "ags/engine/ac/dynobj/cc_hotspot.h"
#include "ags/engine/ac/dynobj/cc_inventory.h"
#include "ags/engine/ac/dynobj/cc_object.h"
#include "ags/engine/ac/dynobj/cc_region.h"
#include "ags/engine/ac/dynobj/cc_serializer.h"
#endif

View File

@@ -0,0 +1,41 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_ALL_SCRIPT_CLASSES_H
#define AGS_ENGINE_AC_DYNOBJ_ALL_SCRIPT_CLASSES_H
#include "ags/engine/ac/dynobj/script_date_time.h"
#include "ags/engine/ac/dynobj/script_dialog.h"
#include "ags/engine/ac/dynobj/script_dialog_options_rendering.h"
#include "ags/engine/ac/dynobj/script_drawing_surface.h"
#include "ags/engine/ac/dynobj/script_dynamic_sprite.h"
#include "ags/engine/ac/dynobj/script_gui.h"
#include "ags/engine/ac/dynobj/script_hotspot.h"
#include "ags/engine/ac/dynobj/script_inv_item.h"
#include "ags/engine/ac/dynobj/script_mouse.h"
#include "ags/engine/ac/dynobj/script_object.h"
#include "ags/engine/ac/dynobj/script_overlay.h"
#include "ags/engine/ac/dynobj/script_region.h"
#include "ags/engine/ac/dynobj/script_string.h"
#include "ags/engine/ac/dynobj/script_system.h"
#include "ags/engine/ac/dynobj/script_view_frame.h"
#endif

View File

@@ -0,0 +1,95 @@
/* 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 "ags/shared/core/types.h"
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
#include "ags/shared/util/memory_stream.h"
namespace AGS3 {
using namespace AGS::Shared;
int CCBasicObject::Dispose(void * /*address*/, bool /*force*/) {
return 0; // cannot be removed from memory
}
int CCBasicObject::Serialize(void * /*address*/, uint8_t * /*buffer*/, int /*bufsize*/) {
return 0; // does not save data
}
void *CCBasicObject::GetFieldPtr(void *address, intptr_t offset) {
return static_cast<uint8_t *>(address) + offset;
}
void CCBasicObject::Read(void *address, intptr_t offset, uint8_t *dest, size_t size) {
memcpy(dest, static_cast<uint8_t *>(address) + offset, size);
}
uint8_t CCBasicObject::ReadInt8(void *address, intptr_t offset) {
return *(uint8_t *)(static_cast<uint8_t *>(address) + offset);
}
int16_t CCBasicObject::ReadInt16(void *address, intptr_t offset) {
return *(int16_t *)(static_cast<uint8_t *>(address) + offset);
}
int32_t CCBasicObject::ReadInt32(void *address, intptr_t offset) {
return *(int32_t *)(static_cast<uint8_t *>(address) + offset);
}
float CCBasicObject::ReadFloat(void *address, intptr_t offset) {
return *(float *)(static_cast<uint8_t *>(address) + offset);
}
void CCBasicObject::Write(void *address, intptr_t offset, const uint8_t *src, size_t size) {
memcpy(static_cast<uint8_t *>(address) + offset, src, size);
}
void CCBasicObject::WriteInt8(void *address, intptr_t offset, uint8_t val) {
*(uint8_t *)(static_cast<uint8_t *>(address) + offset) = val;
}
void CCBasicObject::WriteInt16(void *address, intptr_t offset, int16_t val) {
*(int16_t *)(static_cast<uint8_t *>(address) + offset) = val;
}
void CCBasicObject::WriteInt32(void *address, intptr_t offset, int32_t val) {
*(int32_t *)(static_cast<uint8_t *>(address) + offset) = val;
}
void CCBasicObject::WriteFloat(void *address, intptr_t offset, float val) {
*(float *)(static_cast<uint8_t *>(address) + offset) = val;
}
int AGSCCDynamicObject::Serialize(void *address, uint8_t *buffer, int bufsize) {
// If the required space is larger than the provided buffer,
// then return negated required space, notifying the caller that a larger buffer is necessary
size_t req_size = CalcSerializeSize(address);
assert(req_size <= INT32_MAX); // dynamic object API does not support size > int32
if (bufsize < 0 || req_size > static_cast<size_t>(bufsize))
return -(static_cast<int32_t>(req_size));
MemoryStream mems(reinterpret_cast<uint8_t *>(buffer), bufsize, kStream_Write);
Serialize(address, &mems);
return static_cast<int32_t>(mems.GetPosition());
}
} // namespace AGS3

View File

@@ -0,0 +1,117 @@
/* 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/>.
*
*/
//=============================================================================
//
// This is a collection of common implementations of the IScriptObject
// interface. Intended to be used as parent classes for majority of the
// script object managers.
//
// CCBasicObject: parent for managers that treat object contents as raw
// byte buffer.
//
// AGSCCDynamicObject, extends CCBasicObject: parent for built-in dynamic
// object managers; provides simpler serialization methods working with
// streams instead of a raw memory buffer.
//
// AGSCCStaticObject, extends CCBasicObject: a formal stub, intended as
// a parent for built-in static object managers.
//
//=============================================================================
#ifndef AGS_ENGINE_AC_DYNOBJ_CCDYNAMIC_OBJECT_H
#define AGS_ENGINE_AC_DYNOBJ_CCDYNAMIC_OBJECT_H
#include "ags/engine/ac/dynobj/cc_script_object.h"
namespace AGS3 {
namespace AGS { namespace Shared { class Stream; } }
// CCBasicObject: basic implementation of the script object interface,
// intended to be used as a parent for object/manager classes that do not
// require specific implementation.
// * Dispose ignored, never deletes any data on its own;
// * Serialization skipped, does not save or load anything;
// * Provides default implementation for reading and writing data fields,
// treats the contents of an object as a raw byte buffer.
struct CCBasicObject : public IScriptObject {
public:
virtual ~CCBasicObject() = default;
// Dispose the object
int Dispose(void * /*address*/, bool /*force*/) override;
// Serialize the object into BUFFER (which is BUFSIZE bytes)
// return number of bytes used
int Serialize(void * /*address*/, uint8_t * /*buffer*/, int /*bufsize*/) override;
//
// Legacy support for reading and writing object fields by their relative offset
//
void *GetFieldPtr(void *address, intptr_t offset) override;
void Read(void *address, intptr_t offset, uint8_t *dest, size_t size) override;
uint8_t ReadInt8(void *address, intptr_t offset) override;
int16_t ReadInt16(void *address, intptr_t offset) override;
int32_t ReadInt32(void *address, intptr_t offset) override;
float ReadFloat(void *address, intptr_t offset) override;
void Write(void *address, intptr_t offset, const uint8_t *src, size_t size) override;
void WriteInt8(void *address, intptr_t offset, uint8_t val) override;
void WriteInt16(void *address, intptr_t offset, int16_t val) override;
void WriteInt32(void *address, intptr_t offset, int32_t val) override;
void WriteFloat(void *address, intptr_t offset, float val) override;
};
// AGSCCDynamicObject: standard parent implementation for the built-in
// script objects/manager.
// * Serialization from a raw buffer; provides a virtual function that
// accepts Stream, to be implemented in children instead.
// * Provides Unserialize interface that accepts Stream.
struct AGSCCDynamicObject : public CCBasicObject {
public:
virtual ~AGSCCDynamicObject() = default;
// TODO: pass savegame format version
int Serialize(void *address, uint8_t *buffer, int bufsize) override;
// Try unserializing the object from the given input stream
virtual void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) = 0;
protected:
// Savegame serialization
// Calculate and return required space for serialization, in bytes
virtual size_t CalcSerializeSize(const void *address) = 0;
// Write object data into the provided stream
virtual void Serialize(const void *address, AGS::Shared::Stream *out) = 0;
};
// CCStaticObject is a base class for managing static global objects in script.
// The static objects can never be disposed, and do not support serialization
// through IScriptObject interface.
struct AGSCCStaticObject : public CCBasicObject {
public:
virtual ~AGSCCStaticObject() = default;
const char *GetType() override { return "StaticObject"; }
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,51 @@
/* 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 "ags/shared/util/stream.h"
#include "ags/engine/ac/dynobj/cc_audio_channel.h"
#include "ags/engine/ac/dynobj/script_audio_channel.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/engine/media/audio/audio_system.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
const char *CCAudioChannel::GetType() {
return "AudioChannel";
}
size_t CCAudioChannel::CalcSerializeSize(const void * /*address*/) {
return sizeof(int32_t);
}
void CCAudioChannel::Serialize(const void *address, Stream *out) {
const ScriptAudioChannel *ach = static_cast<const ScriptAudioChannel *>(address);
out->WriteInt32(ach->id);
}
void CCAudioChannel::Unserialize(int index, Stream *in, size_t data_sz) {
int id = in->ReadInt32();
ccRegisterUnserializedObject(index, &_G(scrAudioChannel)[id], this);
}
} // namespace AGS3

View File

@@ -0,0 +1,41 @@
/* 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 AGS_ENGINE_DYNOBJ_CC_AUDIO_CHANNEL_H
#define AGS_ENGINE_DYNOBJ_CC_AUDIO_CHANNEL_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
struct CCAudioChannel final : AGSCCDynamicObject {
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,50 @@
/* 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 "ags/engine/ac/dynobj/cc_audio_clip.h"
#include "ags/shared/ac/dynobj/script_audio_clip.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/shared/util/stream.h"
namespace AGS3 {
using namespace AGS::Shared;
const char *CCAudioClip::GetType() {
return "AudioClip";
}
size_t CCAudioClip::CalcSerializeSize(const void * /*address*/) {
return sizeof(int32_t);
}
void CCAudioClip::Serialize(const void *address, Stream *out) {
const ScriptAudioClip *ach = static_cast<const ScriptAudioClip *>(address);
out->WriteInt32(ach->id);
}
void CCAudioClip::Unserialize(int index, Stream *in, size_t data_sz) {
int id = in->ReadInt32();
ccRegisterUnserializedObject(index, &_GP(game).audioClips[id], this);
}
} // namespace AGS3

View File

@@ -0,0 +1,41 @@
/* 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 AGS_ENGINE_DYNOBJ_CC_AUDIO_CLIP_H
#define AGS_ENGINE_DYNOBJ_CC_AUDIO_CLIP_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
struct CCAudioClip final : AGSCCDynamicObject {
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,370 @@
/* 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 "ags/engine/ac/dynobj/cc_character.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/shared/ac/character_info.h"
#include "ags/engine/ac/global_character.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/shared/script/cc_common.h" // cc_error
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
// return the type name of the object
const char *CCCharacter::GetType() {
return "Character";
}
// serialize the object into BUFFER (which is BUFSIZE bytes)
// return number of bytes used
size_t CCCharacter::CalcSerializeSize(const void * /*address*/) {
return sizeof(int32_t);
}
// serialize the object into BUFFER (which is BUFSIZE bytes)
// return number of bytes used
void CCCharacter::Serialize(const void *address, Stream *out) {
const CharacterInfo *chaa = static_cast<const CharacterInfo *>(address);
out->WriteInt32(chaa->index_id);
}
void CCCharacter::Unserialize(int index, Stream *in, size_t data_sz) {
int num = in->ReadInt32();
ccRegisterUnserializedObject(index, &_GP(game).chars[num], this);
}
uint8_t CCCharacter::ReadInt8(void *address, intptr_t offset) {
const CharacterInfo *ci = static_cast<CharacterInfo *>(address);
const int on_offset = 28 * sizeof(int32_t) /* first var group */
+ 301 * sizeof(int16_t) /* inventory */ + sizeof(int16_t) * 2 /* two shorts */ + 40 /* name */ + 20 /* scrname */;
if (offset == on_offset)
return ci->on;
cc_error("ScriptCharacter: unsupported 'char' variable offset %d", offset);
return 0;
}
void CCCharacter::WriteInt8(void *address, intptr_t offset, uint8_t val) {
CharacterInfo *ci = static_cast<CharacterInfo *>(address);
const int on_offset = 28 * sizeof(int32_t) /* first var group */
+ 301 * sizeof(int16_t) /* inventory */ + sizeof(int16_t) * 2 /* two shorts */ + 40 /* name */ + 20 /* scrname */;
if (offset == on_offset)
ci->on = val;
else
cc_error("ScriptCharacter: unsupported 'char' variable offset %d", offset);
}
int16_t CCCharacter::ReadInt16(void *address, intptr_t offset) {
const CharacterInfo *ci = static_cast<CharacterInfo *>(address);
// Handle inventory fields
const int invoffset = 112;
if (offset >= invoffset && offset < (uint)(invoffset + MAX_INV * sizeof(short))) {
return ci->inv[(offset - invoffset) / sizeof(short)];
}
switch (offset) {
// +9 int32 = 36
case 36:
return ci->following;
case 38:
return ci->followinfo;
// 40 +1 int32 = 44
case 44:
return ci->idletime;
case 46:
return ci->idleleft;
case 48:
return ci->transparency;
case 50:
return ci->baseline;
// 52 +3 int32 = 64
case 64:
return ci->blinkview;
case 66:
return ci->blinkinterval;
case 68:
return ci->blinktimer;
case 70:
return ci->blinkframe;
case 72:
return ci->walkspeed_y;
case 74:
return ci->pic_yoffs;
// 76 +2 int32 = 84
case 84:
return ci->speech_anim_speed;
case 86:
return ci->idle_anim_speed;
case 88:
return ci->blocking_width;
case 90:
return ci->blocking_height;
// 92 +1 int32 = 96
case 96:
return ci->pic_xoffs;
case 98:
return ci->walkwaitcounter;
case 100:
return ci->loop;
case 102:
return ci->frame;
case 104:
return ci->walking;
case 106:
return ci->animating;
case 108:
return ci->walkspeed;
case 110:
return ci->animspeed;
// 112 +301 int16 = 714 (skip inventory)
case 714:
return ci->actx;
case 716:
return ci->acty;
default:
cc_error("ScriptCharacter: unsupported 'short' variable offset %d", offset);
return 0;
}
}
void CCCharacter::WriteInt16(void *address, intptr_t offset, int16_t val) {
CharacterInfo *ci = static_cast<CharacterInfo *>(address);
// Detect when a game directly modifies the inventory, which causes the displayed
// and actual inventory to diverge since 2.70. Force an update of the displayed
// inventory for older games that rely on this behaviour.
const int invoffset = 112;
if (offset >= invoffset && offset < (uint)(invoffset + MAX_INV * sizeof(short))) {
ci->inv[(offset - invoffset) / sizeof(short)] = val;
update_invorder();
return;
}
// TODO: for safety, find out which of the following fields
// must be readonly, and add assertions for them, i.e.:
// cc_error("ScriptCharacter: attempt to write readonly 'short' variable at offset %d", offset);
switch (offset) {
// +9 int32 = 36
case 36:
ci->following = val;
break;
case 38:
ci->followinfo = val;
break;
// 40 +1 int32 = 44
case 44:
ci->idletime = val;
break;
case 46:
ci->idleleft = val;
break;
case 48:
ci->transparency = val;
break;
case 50:
ci->baseline = val;
break;
// 52 +3 int32 = 64
case 64:
ci->blinkview = val;
break;
case 66:
ci->blinkinterval = val;
break;
case 68:
ci->blinktimer = val;
break;
case 70:
ci->blinkframe = val;
break;
case 72:
ci->walkspeed_y = val;
break;
case 74:
ci->pic_yoffs = val;
break;
// 76 +2 int32 = 84
case 84:
ci->speech_anim_speed = val;
break;
case 86:
ci->idle_anim_speed = val;
break;
case 88:
ci->blocking_width = val;
break;
case 90:
ci->blocking_height = val;
break;
// 92 +1 int32 = 96
case 96:
ci->pic_xoffs = val;
break;
case 98:
ci->walkwaitcounter = val;
break;
case 100:
ci->loop = val;
break;
case 102:
ci->frame = val;
break;
case 104:
ci->walking = val;
break;
case 106:
ci->animating = val;
break;
case 108:
ci->walkspeed = val;
break;
case 110:
ci->animspeed = val;
break;
// 112 +301 int16 = 714 (skip inventory)
case 714:
ci->actx = val;
break;
case 716:
ci->acty = val;
break;
default:
cc_error("ScriptCharacter: unsupported 'short' variable offset %d", offset);
break;
}
}
int32_t CCCharacter::ReadInt32(void *address, intptr_t offset) {
const CharacterInfo *ci = static_cast<CharacterInfo *>(address);
switch (offset) {
case 0:
return ci->defview;
case 4:
return ci->talkview;
case 8:
return ci->view;
case 12:
return ci->room;
case 16:
return ci->prevroom;
case 20:
return ci->x;
case 24:
return ci->y;
case 28:
return ci->wait;
case 32:
return ci->flags;
// 36 +2 int16 = 40
case 40:
return ci->idleview;
// 44 +4 int16 = 52
case 52:
return ci->activeinv;
case 56:
return ci->talkcolor;
case 60:
return ci->thinkview;
// 64 +6 int16 = 76
case 76:
return ci->z;
case 80:
return ci->walkwait;
// 84 +4 int16 = 100
case 92:
return ci->index_id;
default:
cc_error("ScriptCharacter: unsupported 'int' variable offset %d", offset);
return 0;
}
}
void CCCharacter::WriteInt32(void *address, intptr_t offset, int32_t val) {
CharacterInfo *ci = static_cast<CharacterInfo *>(address);
// TODO: for safety, find out which of the following fields
// must be readonly, and add assertions for them, i.e.:
// cc_error("ScriptCharacter: attempt to write readonly 'int' variable at offset %d", offset);
switch (offset) {
case 0:
ci->defview = val;
break;
case 4:
ci->talkview = val;
break;
case 8:
ci->view = val;
break;
case 12:
ci->room = val;
break;
case 16:
ci->prevroom = val;
break;
case 20:
ci->x = val;
break;
case 24:
ci->y = val;
break;
case 28:
ci->wait = val;
break;
case 32:
ci->flags = val;
break;
// 36 +2 int16 = 40
case 40:
ci->idleview = val;
break;
// 44 +4 int16 = 52
case 52:
ci->activeinv = val;
break;
case 56:
ci->talkcolor = val;
break;
case 60:
ci->thinkview = val;
break;
// 64 +6 int16 = 76
case 76:
ci->z = val;
break;
case 80:
ci->walkwait = val;
break;
// 84 +4 int16 = 100
case 92:
ci->index_id = val;
break;
default:
cc_error("ScriptCharacter: unsupported 'int' variable offset %d", offset);
break;
}
}
} // namespace AGS3

View File

@@ -0,0 +1,58 @@
/* 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/>.
*
*/
//=============================================================================
//
// Wrapper around script "Character" struct, managing access to its variables.
// Assumes object data contains CharacterInfo object.
//
//=============================================================================
#ifndef AGS_ENGINE_AC_DYNOBJ_CC_CHARACTER_H
#define AGS_ENGINE_AC_DYNOBJ_CC_CHARACTER_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
struct CCCharacter final : AGSCCDynamicObject {
public:
// return the type name of the object
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
uint8_t ReadInt8(void *address, intptr_t offset) override;
int16_t ReadInt16(void *address, intptr_t offset) override;
int32_t ReadInt32(void *address, intptr_t offset) override;
void WriteInt8(void *address, intptr_t offset, uint8_t val) override;
void WriteInt16(void *address, intptr_t offset, int16_t val) override;
void WriteInt32(void *address, intptr_t offset, int32_t val) override;
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,55 @@
/* 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 "ags/engine/ac/dynobj/cc_dialog.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/engine/ac/dialog.h"
#include "ags/shared/ac/dialog_topic.h"
#include "ags/shared/ac/game_struct_defines.h"
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
// return the type name of the object
const char *CCDialog::GetType() {
return "Dialog";
}
size_t CCDialog::CalcSerializeSize(const void * /*address*/) {
return sizeof(int32_t);
}
// serialize the object into BUFFER (which is BUFSIZE bytes)
// return number of bytes used
void CCDialog::Serialize(const void *address, Stream *out) {
const ScriptDialog *shh = static_cast<const ScriptDialog *>(address);
out->WriteInt32(shh->id);
}
void CCDialog::Unserialize(int index, Stream *in, size_t data_sz) {
int num = in->ReadInt32();
ccRegisterUnserializedObject(index, &_GP(scrDialog)[num], this);
}
} // namespace AGS3

View File

@@ -0,0 +1,44 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_CC_DIALOG_H
#define AGS_ENGINE_AC_DYNOBJ_CC_DIALOG_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
struct CCDialog final : AGSCCDynamicObject {
// return the type name of the object
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,114 @@
/* 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 "ags/engine/ac/dynobj/cc_dynamic_array.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/engine/ac/dynobj/script_string.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
const char *CCDynamicArray::TypeName = "CCDynamicArray";
// return the type name of the object
const char *CCDynamicArray::GetType() {
return TypeName;
}
int CCDynamicArray::Dispose(void *address, bool force) {
// If it's an array of managed objects, release their ref counts;
// except if this array is forcefully removed from the managed pool,
// in which case just ignore these.
if (!force) {
const Header &hdr = GetHeader(address);
bool is_managed = (hdr.ElemCount & ARRAY_MANAGED_TYPE_FLAG) != 0;
const uint32_t el_count = hdr.ElemCount & (~ARRAY_MANAGED_TYPE_FLAG);
if (is_managed) { // Dynamic array of managed pointers: subref them directly
const uint32_t *handles = reinterpret_cast<const uint32_t *>(address);
for (uint32_t i = 0; i < el_count; ++i) {
if (handles[i] > 0)
ccReleaseObjectReference(handles[i]);
}
}
}
delete[] (static_cast<uint8_t *>(address) - MemHeaderSz);
return 1;
}
size_t CCDynamicArray::CalcSerializeSize(const void *address) {
const Header &hdr = GetHeader(address);
return hdr.TotalSize + FileHeaderSz;
}
void CCDynamicArray::Serialize(const void *address, Stream *out) {
const Header &hdr = GetHeader(address);
out->WriteInt32(hdr.ElemCount);
out->WriteInt32(hdr.TotalSize);
out->Write(address, hdr.TotalSize); // elements
}
void CCDynamicArray::Unserialize(int index, Stream *in, size_t data_sz) {
uint8_t *new_arr = new uint8_t[(data_sz - FileHeaderSz) + MemHeaderSz];
Header &hdr = reinterpret_cast<Header &>(*new_arr);
hdr.ElemCount = in->ReadInt32();
hdr.TotalSize = in->ReadInt32();
in->Read(new_arr + MemHeaderSz, data_sz - FileHeaderSz);
ccRegisterUnserializedObject(index, &new_arr[MemHeaderSz], this);
}
/* static */ DynObjectRef CCDynamicArray::Create(int numElements, int elementSize, bool isManagedType) {
uint8_t *new_arr = new uint8_t[numElements * elementSize + MemHeaderSz];
memset(new_arr, 0, numElements * elementSize + MemHeaderSz);
Header &hdr = reinterpret_cast<Header &>(*new_arr);
hdr.ElemCount = numElements | (ARRAY_MANAGED_TYPE_FLAG * isManagedType);
hdr.TotalSize = elementSize * numElements;
void *obj_ptr = &new_arr[MemHeaderSz];
int32_t handle = ccRegisterManagedObject(obj_ptr, &_GP(globalDynamicArray));
if (handle == 0) {
delete[] new_arr;
return DynObjectRef();
}
return DynObjectRef(handle, obj_ptr, &_GP(globalDynamicArray));
}
DynObjectRef DynamicArrayHelpers::CreateStringArray(const std::vector<const char *> items) {
// NOTE: we need element size of "handle" for array of managed pointers
DynObjectRef arr = _GP(globalDynamicArray).Create(items.size(), sizeof(int32_t), true);
if (!arr.Obj)
return arr;
// Create script strings and put handles into array
int32_t *slots = static_cast<int32_t *>(arr.Obj);
for (auto s : items) {
DynObjectRef str = ScriptString::Create(s);
// We must add reference count, because the string is going to be saved
// within another object (array), not returned to script directly
ccAddObjectReference(str.Handle);
*(slots++) = str.Handle;
}
return arr;
}
} // namespace AGS3

View File

@@ -0,0 +1,80 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_CC_DYNAMICARRAY_H
#define AGS_ENGINE_AC_DYNOBJ_CC_DYNAMICARRAY_H
#include "common/std/vector.h"
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
#include "ags/shared/util/stream.h"
namespace AGS3 {
#define ARRAY_MANAGED_TYPE_FLAG 0x80000000
struct CCDynamicArray final : AGSCCDynamicObject {
public:
static const char *TypeName;
struct Header {
// May contain ARRAY_MANAGED_TYPE_FLAG
uint32_t ElemCount = 0u;
// TODO: refactor and store "elem size" instead
uint32_t TotalSize = 0u;
};
CCDynamicArray() = default;
~CCDynamicArray() = default;
inline static const Header &GetHeader(const void *address) {
return reinterpret_cast<const Header &>(*(static_cast<const uint8_t *>(address) - MemHeaderSz));
}
// Create managed array object and return a pointer to the beginning of a buffer
static DynObjectRef Create(int numElements, int elementSize, bool isManagedType);
// return the type name of the object
const char *GetType() override;
int Dispose(void *address, bool force) override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
private:
// The size of the array's header in memory, prepended to the element data
static const size_t MemHeaderSz = sizeof(Header);
// The size of the serialized header
static const size_t FileHeaderSz = sizeof(uint32_t) * 2;
// Savegame serialization
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
// Helper functions for setting up dynamic arrays.
namespace DynamicArrayHelpers {
// Create array of managed strings
DynObjectRef CreateStringArray(const std::vector<const char *>);
} // namespace DynamicArrayHelpers
} // namespace AGS3
#endif

View File

@@ -0,0 +1,53 @@
/* 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 "ags/engine/ac/dynobj/cc_gui.h"
#include "ags/engine/ac/dynobj/script_gui.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
// return the type name of the object
const char *CCGUI::GetType() {
return "GUI";
}
size_t CCGUI::CalcSerializeSize(const void * /*address*/) {
return sizeof(int32_t);
}
// serialize the object into BUFFER (which is BUFSIZE bytes)
// return number of bytes used
void CCGUI::Serialize(const void *address, Stream *out) {
const ScriptGUI *shh = static_cast<const ScriptGUI *>(address);
out->WriteInt32(shh->id);
}
void CCGUI::Unserialize(int index, Stream *in, size_t data_sz) {
int num = in->ReadInt32();
ccRegisterUnserializedObject(index, &_GP(scrGui)[num], this);
}
} // namespace AGS3

View File

@@ -0,0 +1,44 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_CCGUI_H
#define AGS_ENGINE_AC_DYNOBJ_CCGUI_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
struct CCGUI final : AGSCCDynamicObject {
// return the type name of the object
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,57 @@
/* 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 "ags/engine/ac/dynobj/cc_gui_object.h"
#include "ags/engine/ac/dynobj/script_gui.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/shared/gui/gui_main.h"
#include "ags/shared/gui/gui_object.h"
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
// return the type name of the object
const char *CCGUIObject::GetType() {
return "GUIObject";
}
size_t CCGUIObject::CalcSerializeSize(const void * /*address*/) {
return sizeof(int32_t) * 2;
}
// serialize the object into BUFFER (which is BUFSIZE bytes)
// return number of bytes used
void CCGUIObject::Serialize(const void *address, Stream *out) {
const GUIObject *guio = static_cast<const GUIObject *>(address);
out->WriteInt32(guio->ParentId);
out->WriteInt32(guio->Id);
}
void CCGUIObject::Unserialize(int index, Stream *in, size_t data_sz) {
int guinum = in->ReadInt32();
int objnum = in->ReadInt32();
ccRegisterUnserializedObject(index, _GP(guis)[guinum].GetControl(objnum), this);
}
} // namespace AGS3

View File

@@ -0,0 +1,44 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_CC_GUI_OBJECT_H
#define AGS_ENGINE_AC_DYNOBJ_CC_GUI_OBJECT_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
struct CCGUIObject final : AGSCCDynamicObject {
// return the type name of the object
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,55 @@
/* 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 "ags/engine/ac/dynobj/cc_hotspot.h"
#include "ags/engine/ac/dynobj/script_hotspot.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/shared/ac/common_defines.h"
#include "ags/shared/game/room_struct.h"
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
// return the type name of the object
const char *CCHotspot::GetType() {
return "Hotspot";
}
size_t CCHotspot::CalcSerializeSize(const void * /*address*/) {
return sizeof(int32_t);
}
// serialize the object into BUFFER (which is BUFSIZE bytes)
// return number of bytes used
void CCHotspot::Serialize(const void *address, Stream *out) {
const ScriptHotspot *shh = static_cast<const ScriptHotspot *>(address);
out->WriteInt32(shh->id);
}
void CCHotspot::Unserialize(int index, Stream *in, size_t data_sz) {
int num = in->ReadInt32();
ccRegisterUnserializedObject(index, &_G(scrHotspot)[num], this);
}
} // namespace AGS3

View File

@@ -0,0 +1,44 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_CC_HOTSPOT_H
#define AGS_ENGINE_AC_DYNOBJ_CC_HOTSPOT_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
struct CCHotspot final : AGSCCDynamicObject {
// return the type name of the object
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,54 @@
/* 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 "ags/engine/ac/dynobj/cc_inventory.h"
#include "ags/engine/ac/dynobj/script_inv_item.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/shared/ac/character_info.h"
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
// return the type name of the object
const char *CCInventory::GetType() {
return "Inventory";
}
size_t CCInventory::CalcSerializeSize(const void * /*address*/) {
return sizeof(int32_t);
}
// serialize the object into BUFFER (which is BUFSIZE bytes)
// return number of bytes used
void CCInventory::Serialize(const void *address, Stream *out) {
const ScriptInvItem *shh = static_cast<const ScriptInvItem *>(address);
out->WriteInt32(shh->id);
}
void CCInventory::Unserialize(int index, Stream *in, size_t data_sz) {
int num = in->ReadInt32();
ccRegisterUnserializedObject(index, &_G(scrInv)[num], this);
}
} // namespace AGS3

View File

@@ -0,0 +1,44 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_CCINVENTORY_H
#define AGS_ENGINE_AC_DYNOBJ_CCINVENTORY_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
struct CCInventory final : AGSCCDynamicObject {
// return the type name of the object
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,55 @@
/* 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 "ags/engine/ac/dynobj/cc_object.h"
#include "ags/engine/ac/dynobj/script_object.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/shared/ac/common_defines.h"
#include "ags/shared/game/room_struct.h"
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
// return the type name of the object
const char *CCObject::GetType() {
return "Object";
}
size_t CCObject::CalcSerializeSize(const void * /*address*/) {
return sizeof(int32_t);
}
// serialize the object into BUFFER (which is BUFSIZE bytes)
// return number of bytes used
void CCObject::Serialize(const void *address, Stream *out) {
const ScriptObject *shh = static_cast<const ScriptObject *>(address);
out->WriteInt32(shh->id);
}
void CCObject::Unserialize(int index, Stream *in, size_t data_sz) {
int num = in->ReadInt32();
ccRegisterUnserializedObject(index, &_G(scrObj)[num], this);
}
} // namespace AGS3

View File

@@ -0,0 +1,44 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_CCOBJECT_H
#define AGS_ENGINE_AC_DYNOBJ_CCOBJECT_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
struct CCObject final : AGSCCDynamicObject {
// return the type name of the object
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,55 @@
/* 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 "ags/engine/ac/dynobj/cc_region.h"
#include "ags/engine/ac/dynobj/script_region.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/shared/ac/common_defines.h"
#include "ags/shared/game/room_struct.h"
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
// return the type name of the object
const char *CCRegion::GetType() {
return "Region";
}
size_t CCRegion::CalcSerializeSize(const void * /*address*/) {
return sizeof(int32_t);
}
// serialize the object into BUFFER (which is BUFSIZE bytes)
// return number of bytes used
void CCRegion::Serialize(const void *address, Stream *out) {
const ScriptRegion *shh = static_cast<const ScriptRegion *>(address);
out->WriteInt32(shh->id);
}
void CCRegion::Unserialize(int index, Stream *in, size_t data_sz) {
int num = in->ReadInt32();
ccRegisterUnserializedObject(index, &_G(scrRegion)[num], this);
}
} // namespace AGS3

View File

@@ -0,0 +1,44 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_CCREGION_H
#define AGS_ENGINE_AC_DYNOBJ_CCREGION_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
struct CCRegion final : AGSCCDynamicObject {
// return the type name of the object
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,128 @@
/* 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/>.
*
*/
//=============================================================================
//
// IScriptObject: script managed object interface.
// Provides interaction with a object which allocation and lifetime is
// managed by the engine and/or the managed pool rather than the script VM.
// These may be both static objects existing throughout the game, and
// dynamic objects allocated by the script command.
//
//=============================================================================
#ifndef AGS_ENGINE_AC_DYNOBJ_CC_SCRIPT_OBJECT_H
#define AGS_ENGINE_AC_DYNOBJ_CC_SCRIPT_OBJECT_H
#include "common/std/utility.h"
#include "ags/shared/core/types.h"
namespace AGS3 {
// Forward declaration
namespace AGS {
namespace Shared {
class Stream;
} // namespace Shared
} // namespace AGS
struct IScriptObject;
// A convenience struct for grouping handle and dynamic object
struct DynObjectRef {
const int Handle = 0;
void *const Obj = nullptr;
IScriptObject *const Mgr = nullptr;
DynObjectRef() = default;
DynObjectRef(int handle, void *obj, IScriptObject *mgr)
: Handle(handle), Obj(obj), Mgr(mgr) {}
};
struct IScriptObject {
// WARNING: The first section of this interface is also a part of the AGS plugin API!
// when a ref count reaches 0, this is called with the address
// of the object. Return 1 to remove the object from memory, 0 to
// leave it
// The "force" flag tells system to detach the object, breaking any links and references
// to other managed objects or game resources (instead of disposing these too).
// TODO: it might be better to rewrite the managed pool and remove this flag at all,
// because it makes the use of this interface prone to mistakes.
virtual int Dispose(void *address, bool force = false) = 0;
// return the type name of the object
virtual const char *GetType() = 0;
// serialize the object into BUFFER (which is BUFSIZE bytes)
// return number of bytes used
// TODO: pass savegame format version
virtual int Serialize(void *address, uint8_t *buffer, int bufsize) = 0;
// WARNING: following section is not a part of plugin API, therefore these methods
// should **never** be called for kScValPluginObject script objects!
// Legacy support for reading and writing object values by their relative offset.
// These methods allow to "remap" script struct field access, by taking the
// legacy offset, and using it rather as a field ID than an address, for example.
// Consequently these also let trigger side-effects, such as updating an object
// after a field value is written to.
// RE: GetFieldPtr() -
// According to AGS script specification, when the old-string pointer or char array is passed
// as an argument, the byte-code does not include any specific command for the member variable
// retrieval and instructs to pass an address of the object itself with certain offset.
// This results in functions like StrCopy writing directly over object address.
// There may be other implementations, but the big question is: how to detect when this is
// necessary, because byte-code does not contain any distinct operation for this case.
// The worst thing here is that with the current byte-code structure we can never tell whether
// offset 0 means getting pointer to whole object or a pointer to its first field.
virtual void *GetFieldPtr(void *address, intptr_t offset) = 0;
virtual void Read(void *address, intptr_t offset, uint8_t *dest, size_t size) = 0;
virtual uint8_t ReadInt8(void *address, intptr_t offset) = 0;
virtual int16_t ReadInt16(void *address, intptr_t offset) = 0;
virtual int32_t ReadInt32(void *address, intptr_t offset) = 0;
virtual float ReadFloat(void *address, intptr_t offset) = 0;
virtual void Write(void *address, intptr_t offset, const uint8_t *src, size_t size) = 0;
virtual void WriteInt8(void *address, intptr_t offset, uint8_t val) = 0;
virtual void WriteInt16(void *address, intptr_t offset, int16_t val) = 0;
virtual void WriteInt32(void *address, intptr_t offset, int32_t val) = 0;
virtual void WriteFloat(void *address, intptr_t offset, float val) = 0;
protected:
IScriptObject() {}
virtual ~IScriptObject() {}
};
// The interface of a script objects deserializer that handles multiple types.
struct ICCObjectCollectionReader {
virtual ~ICCObjectCollectionReader() {}
// TODO: pass savegame format version
virtual void Unserialize(int32_t handle, const char *objectType, const char *serializedData, int dataSize) = 0;
};
// The interface of a script objects deserializer that handles a single type.
// WARNING: a part of the plugin API.
struct ICCObjectReader {
virtual ~ICCObjectReader() {}
virtual void Unserialize(int32_t handle, const char *serializedData, int dataSize) = 0;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,140 @@
/* 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 "ags/shared/util/memory_stream.h"
#include "ags/engine/ac/dynobj/cc_serializer.h"
#include "ags/engine/ac/dynobj/all_dynamic_classes.h"
#include "ags/engine/ac/dynobj/all_script_classes.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/engine/ac/dynobj/cc_dynamic_array.h"
#include "ags/engine/ac/dynobj/script_user_object.h"
#include "ags/engine/ac/dynobj/script_camera.h"
#include "ags/engine/ac/dynobj/script_containers.h"
#include "ags/engine/ac/dynobj/script_file.h"
#include "ags/engine/ac/dynobj/script_viewport.h"
#include "ags/engine/ac/game.h"
#include "ags/engine/debugging/debug_log.h"
#include "ags/plugins/plugin_engine.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
// *** De-serialization of script objects
void AGSDeSerializer::Unserialize(int32_t index, const char *objectType, const char *serializedData, int dataSize) {
if (dataSize < 0) {
quitprintf("Unserialise: invalid data size (%d) for object type '%s'", dataSize, objectType);
return; // TODO: don't quit, return error
}
// Note that while our builtin classes may accept Stream object,
// classes registered by plugin cannot, because streams are not (yet)
// part of the plugin API.
size_t data_sz = static_cast<size_t>(dataSize);
assert(data_sz <= INT32_MAX); // dynamic object API does not support size > int32
MemoryStream mems(reinterpret_cast<const uint8_t *>(serializedData), dataSize);
// TODO: consider this: there are object types that are part of the
// script's foundation, because they are created by the bytecode ops:
// such as DynamicArray and UserObject. *Maybe* these should be moved
// to certain "base serializer" class which guarantees their restoration.
//
// TODO: should we support older save versions here (DynArray, UserObj)?
// might have to use older class names to distinguish save formats
if (strcmp(objectType, CCDynamicArray::TypeName) == 0) {
_GP(globalDynamicArray).Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, ScriptUserObject::TypeName) == 0) {
ScriptUserObject *suo = new ScriptUserObject();
suo->Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "GUIObject") == 0) {
_GP(ccDynamicGUIObject).Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "Character") == 0) {
_GP(ccDynamicCharacter).Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "Hotspot") == 0) {
_GP(ccDynamicHotspot).Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "Region") == 0) {
_GP(ccDynamicRegion).Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "Inventory") == 0) {
_GP(ccDynamicInv).Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "Dialog") == 0) {
_GP(ccDynamicDialog).Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "GUI") == 0) {
_GP(ccDynamicGUI).Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "Object") == 0) {
_GP(ccDynamicObject).Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "String") == 0) {
_GP(myScriptStringImpl).Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "File") == 0) {
// files cannot be restored properly -- so just recreate
// the object; attempting any operations on it will fail
sc_File *scf = new sc_File();
ccRegisterUnserializedObject(index, scf, scf);
} else if (strcmp(objectType, "Overlay") == 0) {
ScriptOverlay *scf = new ScriptOverlay();
scf->Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "DateTime") == 0) {
ScriptDateTime *scf = new ScriptDateTime();
scf->Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "ViewFrame") == 0) {
ScriptViewFrame *scf = new ScriptViewFrame();
scf->Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "DynamicSprite") == 0) {
ScriptDynamicSprite *scf = new ScriptDynamicSprite();
scf->Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "DrawingSurface") == 0) {
ScriptDrawingSurface *sds = new ScriptDrawingSurface();
sds->Unserialize(index, &mems, data_sz);
if (sds->isLinkedBitmapOnly) {
_G(dialogOptionsRenderingSurface) = sds;
}
} else if (strcmp(objectType, "DialogOptionsRendering") == 0) {
_GP(ccDialogOptionsRendering).Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "StringDictionary") == 0) {
Dict_Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "StringSet") == 0) {
Set_Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "Viewport2") == 0) {
Viewport_Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "Camera2") == 0) {
Camera_Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "AudioChannel") == 0) {
_GP(ccDynamicAudio).Unserialize(index, &mems, data_sz);
} else if (strcmp(objectType, "AudioClip") == 0) {
_GP(ccDynamicAudioClip).Unserialize(index, &mems, data_sz);
} else {
// check if the type is read by a plugin
for (const auto &pr : _GP(pluginReaders)) {
if (pr.Type == objectType) {
if (dataSize == 0) { // avoid unserializing stubbed plugins
debug(0, "Skipping %s plugin unserialization (dataSize = 0)", objectType);
return;
}
pr.Reader->Unserialize(index, serializedData, dataSize);
return;
}
}
quitprintf("Unserialise: unknown object type: '%s'", objectType);
}
}
} // namespace AGS3

View File

@@ -0,0 +1,35 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_SERIALIZER_H
#define AGS_ENGINE_AC_DYNOBJ_SERIALIZER_H
#include "ags/engine/ac/dynobj/cc_script_object.h"
namespace AGS3 {
struct AGSDeSerializer : ICCObjectCollectionReader {
void Unserialize(int32_t index, const char *objectType, const char *serializedData, int dataSize) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,88 @@
/* 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 "ags/engine/ac/dynobj/cc_static_array.h"
#include "ags/engine/ac/dynobj/cc_script_object.h"
namespace AGS3 {
void CCStaticArray::Create(IScriptObject *mgr, size_t elem_script_size, size_t elem_mem_size, size_t elem_count) {
_mgr = mgr;
_elemScriptSize = elem_script_size;
_elemMemSize = elem_mem_size;
_elemCount = elem_count;
}
void *CCStaticArray::GetFieldPtr(void *address, intptr_t offset) {
return GetElementPtr(address, offset);
}
void CCStaticArray::Read(void *address, intptr_t offset, uint8_t *dest, size_t size) {
void *el_ptr = GetElementPtr(address, offset);
return _mgr->Read(el_ptr, offset % _elemScriptSize, dest, size);
}
uint8_t CCStaticArray::ReadInt8(void *address, intptr_t offset) {
void *el_ptr = GetElementPtr(address, offset);
return _mgr->ReadInt8(el_ptr, offset % _elemScriptSize);
}
int16_t CCStaticArray::ReadInt16(void *address, intptr_t offset) {
void *el_ptr = GetElementPtr(address, offset);
return _mgr->ReadInt16(el_ptr, offset % _elemScriptSize);
}
int32_t CCStaticArray::ReadInt32(void *address, intptr_t offset) {
void *el_ptr = GetElementPtr(address, offset);
return _mgr->ReadInt32(el_ptr, offset % _elemScriptSize);
}
float CCStaticArray::ReadFloat(void *address, intptr_t offset) {
void *el_ptr = GetElementPtr(address, offset);
return _mgr->ReadFloat(el_ptr, offset % _elemScriptSize);
}
void CCStaticArray::Write(void *address, intptr_t offset, const uint8_t *src, size_t size) {
void *el_ptr = GetElementPtr(address, offset);
return _mgr->Write(el_ptr, offset % _elemScriptSize, src, size);
}
void CCStaticArray::WriteInt8(void *address, intptr_t offset, uint8_t val) {
void *el_ptr = GetElementPtr(address, offset);
return _mgr->WriteInt8(el_ptr, offset % _elemScriptSize, val);
}
void CCStaticArray::WriteInt16(void *address, intptr_t offset, int16_t val) {
void *el_ptr = GetElementPtr(address, offset);
return _mgr->WriteInt16(el_ptr, offset % _elemScriptSize, val);
}
void CCStaticArray::WriteInt32(void *address, intptr_t offset, int32_t val) {
void *el_ptr = GetElementPtr(address, offset);
return _mgr->WriteInt32(el_ptr, offset % _elemScriptSize, val);
}
void CCStaticArray::WriteFloat(void *address, intptr_t offset, float val) {
void *el_ptr = GetElementPtr(address, offset);
return _mgr->WriteFloat(el_ptr, offset % _elemScriptSize, val);
}
} // namespace AGS3

View 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/>.
*
*/
//=============================================================================
//
// CCStaticArray manages access to an array of script objects,
// where an element's size counted by script's bytecode may differ from the
// real element size in the engine's memory.
// The purpose of this is to remove size restriction from the engine's structs
// exposed to scripts.
//
// FIXME: [ivan-mogilko] the above was meant to work, but in reality it doesn't
// and won't, at least not without some extra workarounds.
// The problem that I missed here is following:
// when the script compiler is told to get an Nth element of a global struct
// array, such as character[n], it calculates the memory address as
// array address + sizeof(Character) * n.
// If this address is used for the read/write operations, these ops can be
// intercepted by interpreter and remapped into the real fields
// (see IScriptObject::ReadN, WriteN interface)
// But if this address is used IN POINTER COMPARISON, then we cannot do
// anything. And if our real struct in the engine is stored on a different
// relative memory offset than one expected by compiler, then this pointer
// comparison will fail, e.g. script expression like
// if (player == character[n])
//
// NOTE: on the other hand, similar effect could be achieved by separating
// object data into two or more structs, where "base" structs are stored in
// the exposed arrays (part of API), while extending structs are stored
// separately. This is more an issue of engine data design.
//
//=============================================================================
#ifndef AGS_ENGINE_AC_DYNOBJ_STATIC_ARRAY_H
#define AGS_ENGINE_AC_DYNOBJ_STATIC_ARRAY_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
struct CCStaticArray : public AGSCCStaticObject {
public:
~CCStaticArray() override {}
void Create(IScriptObject *mgr, size_t elem_script_size, size_t elem_mem_size, size_t elem_count = SIZE_MAX /*unknown*/);
inline IScriptObject *GetObjectManager() const {
return _mgr;
}
// Legacy support for reading and writing object values by their relative offset
inline void *GetElementPtr(void *address, intptr_t legacy_offset) {
return static_cast<uint8_t *>(address) + (legacy_offset / _elemScriptSize) * _elemMemSize;
}
void *GetFieldPtr(void *address, intptr_t offset) override;
void Read(void *address, intptr_t offset, uint8_t *dest, size_t size) override;
uint8_t ReadInt8(void *address, intptr_t offset) override;
int16_t ReadInt16(void *address, intptr_t offset) override;
int32_t ReadInt32(void *address, intptr_t offset) override;
float ReadFloat(void *address, intptr_t offset) override;
void Write(void *address, intptr_t offset, const uint8_t *src, size_t size) override;
void WriteInt8(void *address, intptr_t offset, uint8_t val) override;
void WriteInt16(void *address, intptr_t offset, int16_t val) override;
void WriteInt32(void *address, intptr_t offset, int32_t val) override;
void WriteFloat(void *address, intptr_t offset, float val) override;
private:
IScriptObject *_mgr = nullptr;
size_t _elemScriptSize = 0u;
size_t _elemMemSize = 0u;
size_t _elemCount = 0u;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,154 @@
/* 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/>.
*
*/
//=============================================================================
//
// C-Script run-time interpreter (c) 2001 Chris Jones
//
// You must DISABLE OPTIMIZATIONS AND REGISTER VARIABLES in your compiler
// when compiling this, or strange results can happen.
//
// There is a problem with importing functions on 16-bit compilers: the
// script system assumes that all parameters are passed as 4 bytes, which
// ints are not on 16-bit systems. Be sure to define all parameters as longs,
// or join the 21st century and switch to DJGPP or Visual C++.
//
//=============================================================================
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/shared/core/platform.h"
#include "ags/engine/ac/dynobj/managed_object_pool.h"
#include "ags/shared/debugging/out.h"
#include "ags/shared/script/cc_common.h"
#include "ags/shared/script/cc_internal.h"
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
// register a memory handle for the object and allow script
// pointers to point to it
int32_t ccRegisterManagedObject(void *object, IScriptObject *callback, ScriptValueType obj_type) {
int32_t handl = _GP(pool).AddObject(object, callback, obj_type);
ManagedObjectLog("Register managed object type '%s' handle=%d addr=%08X",
((callback == NULL) ? "(unknown)" : callback->GetType()), handl, object);
return handl;
}
// register a de-serialized object
int32_t ccRegisterUnserializedObject(int index, void *object, IScriptObject *callback, ScriptValueType obj_type) {
return _GP(pool).AddUnserializedObject(object, callback, obj_type, index);
}
// unregister a particular object
int ccUnRegisterManagedObject(void *object) {
return _GP(pool).RemoveObject(object);
}
// remove all registered objects
void ccUnregisterAllObjects() {
_GP(pool).reset();
}
// serialize all objects to disk
void ccSerializeAllObjects(Stream *out) {
_GP(pool).WriteToDisk(out);
}
// un-serialise all objects (will remove all currently registered ones)
int ccUnserializeAllObjects(Stream *in, ICCObjectCollectionReader *callback) {
return _GP(pool).ReadFromDisk(in, callback);
}
// dispose the object if RefCount==0
void ccAttemptDisposeObject(int32_t handle) {
_GP(pool).CheckDispose(handle);
}
// translate between object handles and memory addresses
int32_t ccGetObjectHandleFromAddress(void *address) {
// set to null
if (address == nullptr)
return 0;
int32_t handl = _GP(pool).AddressToHandle(address);
ManagedObjectLog("Line %d WritePtr: %08X to %d", _G(currentline), address, handl);
if (handl == 0) {
cc_error("Pointer cast failure: the object being pointed to is not in the managed object pool");
return -1;
}
return handl;
}
void *ccGetObjectAddressFromHandle(int32_t handle) {
if (handle == 0) {
return nullptr;
}
void *addr = _GP(pool).HandleToAddress(handle);
ManagedObjectLog("Line %d ReadPtr: %d to %08X", _G(currentline), handle, addr);
if (addr == nullptr) {
cc_error("Error retrieving pointer: invalid handle %d", handle);
return nullptr;
}
return addr;
}
ScriptValueType ccGetObjectAddressAndManagerFromHandle(int32_t handle, void *&object, IScriptObject *&manager) {
if (handle == 0) {
object = nullptr;
manager = nullptr;
return kScValUndefined;
}
ScriptValueType obj_type = _GP(pool).HandleToAddressAndManager(handle, object, manager);
if (obj_type == kScValUndefined) {
cc_error("Error retrieving pointer: invalid handle %d", handle);
}
return obj_type;
}
int ccAddObjectReference(int32_t handle) {
if (handle == 0)
return 0;
return _GP(pool).AddRef(handle);
}
int ccReleaseObjectReference(int32_t handle) {
if (handle == 0)
return 0;
if (_GP(pool).HandleToAddress(handle) == nullptr) {
cc_error("Error releasing pointer: invalid handle %d", handle);
return -1;
}
return _GP(pool).SubRef(handle);
}
} // namespace AGS3

View File

@@ -0,0 +1,73 @@
/* 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/>.
*
*/
//=============================================================================
//
// Dynamic object management utilities.
// TODO: frankly, many of these functions could be factored out by a direct
// use of ManagedPool class.
//
//=============================================================================
#ifndef AGS_ENGINE_AC_DYNOBJ_MANAGER_H
#define AGS_ENGINE_AC_DYNOBJ_MANAGER_H
#include "ags/shared/core/types.h"
#include "ags/engine/script/runtime_script_value.h"
#include "ags/engine/ac/dynobj/cc_script_object.h"
namespace AGS3 {
// Forward declaration
namespace AGS {
namespace Shared {
class Stream;
} // namespace Shared
} // namespace AGS
using namespace AGS; // FIXME later
// register a memory handle for the object and allow script
// pointers to point to it
extern int32_t ccRegisterManagedObject(void *object, IScriptObject *, ScriptValueType obj_type = kScValScriptObject);
// register a de-serialized object
extern int32_t ccRegisterUnserializedObject(int index, void *object, IScriptObject *, ScriptValueType obj_type = kScValScriptObject);
// unregister a particular object
extern int ccUnRegisterManagedObject(void *object);
// remove all registered objects
extern void ccUnregisterAllObjects();
// serialize all objects to disk
extern void ccSerializeAllObjects(Shared::Stream *out);
// un-serialise all objects (will remove all currently registered ones)
extern int ccUnserializeAllObjects(Shared::Stream *in, ICCObjectCollectionReader *callback);
// dispose the object if RefCount==0
extern void ccAttemptDisposeObject(int32_t handle);
// translate between object handles and memory addresses
extern int32_t ccGetObjectHandleFromAddress(void *address);
extern void *ccGetObjectAddressFromHandle(int32_t handle);
extern ScriptValueType ccGetObjectAddressAndManagerFromHandle(int32_t handle, void *&object, IScriptObject *&manager);
extern int ccAddObjectReference(int32_t handle);
extern int ccReleaseObjectReference(int32_t handle);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,347 @@
/* 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/std/vector.h"
#include "ags/engine/ac/dynobj/managed_object_pool.h"
#include "ags/shared/debugging/out.h"
#include "ags/shared/util/string_utils.h" // fputstring, etc
#include "ags/shared/script/cc_common.h"
#include "ags/shared/script/cc_internal.h"
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
const auto OBJECT_CACHE_MAGIC_NUMBER = 0xa30b;
const auto SERIALIZE_BUFFER_SIZE = 10240;
const auto GARBAGE_COLLECTION_INTERVAL = 1024;
const auto RESERVED_SIZE = 2048;
int ManagedObjectPool::Remove(ManagedObject &o, bool force) {
const bool can_remove = o.callback->Dispose(o.addr, force) != 0;
if (!(can_remove || force))
return 0;
available_ids.push(o.handle);
handleByAddress.erase(o.addr);
ManagedObjectLog("Line %d Disposed managed object handle=%d", currentline, o.handle);
o = ManagedObject();
return 1;
}
int32_t ManagedObjectPool::AddRef(int32_t handle) {
if (handle < 1 || (size_t)handle >= objects.size())
return 0;
auto &o = objects[handle];
if (!o.isUsed())
return 0;
o.refCount++;
ManagedObjectLog("Line %d AddRef: handle=%d new refcount=%d", _G(currentline), o.handle, o.refCount);
return o.refCount;
}
int ManagedObjectPool::CheckDispose(int32_t handle) {
if (handle < 1 || (size_t)handle >= objects.size())
return 1;
auto &o = objects[handle];
if (!o.isUsed()) {
return 1;
}
if (o.refCount >= 1) {
return 0;
}
return Remove(o);
}
int32_t ManagedObjectPool::SubRef(int32_t handle) {
if (handle < 1 || (size_t)handle >= objects.size()) {
return 0;
}
auto &o = objects[handle];
if (!o.isUsed()) {
return 0;
}
o.refCount--;
const auto newRefCount = o.refCount;
const auto canBeDisposed = (o.addr != disableDisposeForObject);
if (canBeDisposed && o.refCount <= 0) {
Remove(o);
}
// object could be removed at this point, don't use any values.
ManagedObjectLog("Line %d SubRef: handle=%d new refcount=%d canBeDisposed=%d", _G(currentline), handle, newRefCount, canBeDisposed);
return newRefCount;
}
int32_t ManagedObjectPool::AddressToHandle(void *addr) {
if (addr == nullptr) {
return 0;
}
auto it = handleByAddress.find(addr);
if (it == handleByAddress.end()) {
return 0;
}
return it->_value;
}
// this function is called often (whenever a pointer is used)
void *ManagedObjectPool::HandleToAddress(int32_t handle) {
if (handle < 1 || (size_t)handle >= objects.size()) {
return nullptr;
}
auto &o = objects[handle];
if (!o.isUsed()) {
return nullptr;
}
return o.addr;
}
// this function is called often (whenever a pointer is used)
ScriptValueType ManagedObjectPool::HandleToAddressAndManager(int32_t handle, void *&object, IScriptObject *&manager) {
if ((handle < 1 || (size_t)handle >= objects.size()) || !objects[handle].isUsed()) {
object = nullptr;
manager = nullptr;
return kScValUndefined;
}
auto &o = objects[handle];
object = (void *)(o.addr); // WARNING: This strips the const from the char* pointer.
manager = o.callback;
return o.obj_type;
}
int ManagedObjectPool::RemoveObject(void *address) {
if (address == nullptr) {
return 0;
}
auto it = handleByAddress.find(address);
if (it == handleByAddress.end()) {
return 0;
}
auto &o = objects[it->_value];
return Remove(o, true);
}
void ManagedObjectPool::RunGarbageCollectionIfAppropriate() {
if (objectCreationCounter <= GARBAGE_COLLECTION_INTERVAL) {
return;
}
RunGarbageCollection();
objectCreationCounter = 0;
}
void ManagedObjectPool::RunGarbageCollection() {
for (int i = 1; i < nextHandle; i++) {
auto &o = objects[i];
if (!o.isUsed()) {
continue;
}
if (o.refCount < 1) {
Remove(o);
}
}
ManagedObjectLog("Ran garbage collection");
}
int ManagedObjectPool::Add(int handle, void *address, IScriptObject *callback, ScriptValueType obj_type)
{
auto &o = objects[handle];
assert(!o.isUsed());
o = ManagedObject(obj_type, handle, address, callback);
handleByAddress.insert({address, handle});
ManagedObjectLog("Allocated managed object type=%s, handle=%d, addr=%08X", callback->GetType(), handle, address);
return handle;
}
int ManagedObjectPool::AddObject(void *address, IScriptObject *callback, ScriptValueType obj_type) {
int32_t handle;
if (!available_ids.empty()) {
handle = available_ids.front();
available_ids.pop();
} else {
handle = nextHandle++;
if ((size_t)handle >= objects.size()) {
objects.resize(handle + 1024, ManagedObject());
}
}
objectCreationCounter++;
return Add(handle, address, callback, obj_type);
}
int ManagedObjectPool::AddUnserializedObject(void *address, IScriptObject *callback, ScriptValueType obj_type, int handle) {
if (handle < 1) {
cc_error("Attempt to assign invalid handle: %d", handle);
return 0;
}
if ((size_t)handle >= objects.size()) {
objects.resize(handle + 1024, ManagedObject());
}
return Add(handle, address, callback, obj_type);
}
void ManagedObjectPool::WriteToDisk(Stream *out) {
// use this opportunity to clean up any non-referenced pointers
RunGarbageCollection();
std::vector<uint8_t> serializeBuffer;
serializeBuffer.resize(SERIALIZE_BUFFER_SIZE);
out->WriteInt32(OBJECT_CACHE_MAGIC_NUMBER);
out->WriteInt32(2); // version
int size = 0;
for (int i = 1; i < nextHandle; i++) {
auto const &o = objects[i];
if (o.isUsed()) {
size += 1;
}
}
out->WriteInt32(size);
for (int i = 1; i < nextHandle; i++) {
auto const &o = objects[i];
if (!o.isUsed()) {
continue;
}
// handle
out->WriteInt32(o.handle);
// write the type of the object
StrUtil::WriteCStr(o.callback->GetType(), out);
// now write the object data
int bytesWritten = o.callback->Serialize(o.addr, &serializeBuffer.front(), serializeBuffer.size());
if ((bytesWritten < 0) && ((size_t)(-bytesWritten) > serializeBuffer.size())) {
// buffer not big enough, re-allocate with requested size
serializeBuffer.resize(-bytesWritten);
bytesWritten = o.callback->Serialize(o.addr, &serializeBuffer.front(), serializeBuffer.size());
}
assert(bytesWritten >= 0);
out->WriteInt32(bytesWritten);
out->Write(&serializeBuffer.front(), bytesWritten);
out->WriteInt32(o.refCount);
ManagedObjectLog("Wrote handle = %d", o.handle);
}
}
int ManagedObjectPool::ReadFromDisk(Stream *in, ICCObjectCollectionReader *reader) {
if (in->ReadInt32() != OBJECT_CACHE_MAGIC_NUMBER) {
cc_error("Data was not written by ccSeralize");
return -1;
}
char typeNameBuffer[200];
std::vector<char> serializeBuffer;
serializeBuffer.resize(SERIALIZE_BUFFER_SIZE);
auto version = in->ReadInt32();
switch (version) {
case 1: {
// IMPORTANT: numObjs is "nextHandleId", which is why we iterate from 1 to numObjs-1
int numObjs = in->ReadInt32();
for (int i = 1; i < numObjs; i++) {
StrUtil::ReadCStr(typeNameBuffer, in, sizeof(typeNameBuffer));
if (typeNameBuffer[0] != 0) {
size_t numBytes = in->ReadInt32();
if (numBytes > serializeBuffer.size()) {
serializeBuffer.resize(numBytes);
}
in->Read(&serializeBuffer.front(), numBytes);
// Delegate work to ICCObjectReader
reader->Unserialize(i, typeNameBuffer, &serializeBuffer.front(), numBytes);
objects[i].refCount = in->ReadInt32();
ManagedObjectLog("Read handle = %d", objects[i].handle);
}
}
}
break;
case 2: {
// This is actually number of objects written.
int objectsSize = in->ReadInt32();
for (int i = 0; i < objectsSize; i++) {
auto handle = in->ReadInt32();
assert(handle >= 1);
StrUtil::ReadCStr(typeNameBuffer, in, sizeof(typeNameBuffer));
assert(typeNameBuffer[0] != 0);
size_t numBytes = in->ReadInt32();
if (numBytes > serializeBuffer.size()) {
serializeBuffer.resize(numBytes);
}
in->Read(&serializeBuffer.front(), numBytes);
// Delegate work to ICCObjectReader
reader->Unserialize(handle, typeNameBuffer, &serializeBuffer.front(), numBytes);
objects[handle].refCount = in->ReadInt32();
ManagedObjectLog("Read handle = %d", objects[i].handle);
}
}
break;
default:
cc_error("Invalid data version: %d", version);
return -1;
}
// re-adjust next handles. (in case saved in random order)
available_ids = std::queue<int32_t>();
nextHandle = 1;
for (const auto &o : objects) {
if (o.isUsed()) {
nextHandle = o.handle + 1;
}
}
for (int i = 1; i < nextHandle; i++) {
if (!objects[i].isUsed()) {
available_ids.push(i);
}
}
return 0;
}
// de-allocate all objects
void ManagedObjectPool::reset() {
for (int i = 1; i < nextHandle; i++) {
auto &o = objects[i];
if (!o.isUsed()) {
continue;
}
Remove(o, true);
}
available_ids = std::queue<int32_t>();
nextHandle = 1;
}
ManagedObjectPool::ManagedObjectPool() : objectCreationCounter(0), nextHandle(1), available_ids(), objects(RESERVED_SIZE, ManagedObject()), handleByAddress() {
handleByAddress.reserve(RESERVED_SIZE);
}
} // namespace AGS3

View File

@@ -0,0 +1,113 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_CC_MANAGED_OBJECT_POOL_H
#define AGS_ENGINE_AC_DYNOBJ_CC_MANAGED_OBJECT_POOL_H
#include "common/std/vector.h"
#include "common/std/queue.h"
#include "common/std/map.h"
#include "ags/shared/core/platform.h"
#include "ags/engine/script/runtime_script_value.h"
#include "ags/engine/ac/dynobj/cc_script_object.h" // IScriptObject
namespace AGS3 {
namespace AGS {
namespace Shared {
class Stream;
} // namespace Shared
} // namespace AGS
using namespace AGS; // FIXME later
struct Pointer_Hash {
uint operator()(void *v) const {
return static_cast<uint>(reinterpret_cast<uintptr>(v));
}
};
struct ManagedObjectPool final {
private:
// TODO: find out if we can make handle size_t
struct ManagedObject {
ScriptValueType obj_type;
int32_t handle;
void *addr;
IScriptObject *callback;
int refCount;
bool isUsed() const {
return obj_type != kScValUndefined;
}
ManagedObject() : obj_type(kScValUndefined), handle(0), addr(nullptr),
callback(nullptr), refCount(0) {}
ManagedObject(ScriptValueType theType, int32_t theHandle,
void *theAddr, IScriptObject *theCallback)
: obj_type(theType), handle(theHandle), addr(theAddr),
callback(theCallback), refCount(0) {
}
};
int objectCreationCounter; // used to do garbage collection every so often
int32_t nextHandle{}; // TODO: manage nextHandle's going over INT32_MAX !
std::queue<int32_t> available_ids;
std::vector<ManagedObject> objects;
std::unordered_map<void *, int32_t, Pointer_Hash> handleByAddress;
int Add(int handle, void *address, IScriptObject *callback, ScriptValueType obj_type);
int Remove(ManagedObject &o, bool force = false);
void RunGarbageCollection();
public:
int32_t AddRef(int32_t handle);
int CheckDispose(int32_t handle);
int32_t SubRef(int32_t handle);
int32_t AddressToHandle(void *addr);
void *HandleToAddress(int32_t handle);
ScriptValueType HandleToAddressAndManager(int32_t handle, void *&object, IScriptObject *&manager);
int RemoveObject(void *address);
void RunGarbageCollectionIfAppropriate();
int AddObject(void *address, IScriptObject *callback, ScriptValueType obj_type);
int AddUnserializedObject(void *address, IScriptObject *callback, ScriptValueType obj_type, int handle);
void WriteToDisk(Shared::Stream *out);
int ReadFromDisk(Shared::Stream *in, ICCObjectCollectionReader *reader);
void reset();
ManagedObjectPool();
void *disableDisposeForObject{ nullptr };
};
// Extreme(!!) verbosity managed memory pool log
#if DEBUG_MANAGED_OBJECTS
#define ManagedObjectLog(...) Debug::Printf(kDbgGroup_ManObj, kDbgMsg_Debug, __VA_ARGS__)
#else
#define ManagedObjectLog(...)
#endif
} // namespace AGS3
#endif

View File

@@ -0,0 +1,34 @@
/* 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 AGS_ENGINE_DYNOBJ__SCRIPTAUDIOCHANNEL_H
#define AGS_ENGINE_DYNOBJ__SCRIPTAUDIOCHANNEL_H
namespace AGS3 {
struct ScriptAudioChannel {
int id = 0;
int reserved = 0;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,74 @@
/* 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 "ags/engine/ac/dynobj/script_camera.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/engine/ac/game_state.h"
#include "ags/shared/util/bbop.h"
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
ScriptCamera::ScriptCamera(int id) : _id(id) {
}
const char *ScriptCamera::GetType() {
return "Camera2";
}
int ScriptCamera::Dispose(void *address, bool force) {
// Note that ScriptCamera is a reference to actual Camera object,
// and this deletes the reference, while camera may remain in GameState.
delete this;
return 1;
}
size_t ScriptCamera::CalcSerializeSize(const void * /*address*/) {
return sizeof(int32_t);
}
void ScriptCamera::Serialize(const void *address, Stream *out) {
out->WriteInt32(_id);
}
void ScriptCamera::Unserialize(int index, Stream *in, size_t data_sz) {
_id = in->ReadInt32();
ccRegisterUnserializedObject(index, this, this);
}
ScriptCamera *Camera_Unserialize(int handle, Stream *in, size_t data_sz) {
// The way it works now, we must not create a new script object,
// but acquire one from the GameState, which keeps the first reference.
// This is essential because GameState should be able to invalidate any
// script references when Camera gets removed.
const int id = in->ReadInt32();
if (id >= 0) {
auto scam = _GP(play).RegisterRoomCamera(id, handle);
if (scam)
return scam;
}
return new ScriptCamera(-1); // make invalid reference
}
} // namespace AGS3

View 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/>.
*
*/
#ifndef AGS_ENGINE_AC_DYNOBJ_SCRIPT_CAMERA_H
#define AGS_ENGINE_AC_DYNOBJ_SCRIPT_CAMERA_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
// ScriptCamera keeps a reference to actual room Camera in script.
struct ScriptCamera final : AGSCCDynamicObject {
public:
ScriptCamera(int id);
// Get camera index; negative means the camera was deleted
int GetID() const {
return _id;
}
void SetID(int id) {
_id = id;
}
// Reset camera index to indicate that this reference is no longer valid
void Invalidate() {
_id = -1;
}
const char *GetType() override;
int Dispose(void *address, bool force) override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
private:
int _id = -1; // index of camera in the game state array
};
// Unserialize camera from the memory stream
ScriptCamera *Camera_Unserialize(int handle, AGS::Shared::Stream *in, size_t data_sz);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,41 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_SCRIPTCONTAINERS_H
#define AGS_ENGINE_AC_DYNOBJ_SCRIPTCONTAINERS_H
namespace AGS3 {
class ScriptDictBase;
class ScriptSetBase;
// Create and register new dictionary
ScriptDictBase *Dict_Create(bool sorted, bool case_sensitive);
// Unserialize dictionary from the memory stream
ScriptDictBase *Dict_Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz);
// Create and register new set
ScriptSetBase *Set_Create(bool sorted, bool case_sensitive);
// Unserialize set from the memory stream
ScriptSetBase *Set_Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz);
} // namespace AGS3
#endif

View File

@@ -0,0 +1,71 @@
/* 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 "ags/engine/ac/dynobj/script_date_time.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/shared/util/stream.h"
namespace AGS3 {
using namespace AGS::Shared;
int ScriptDateTime::Dispose(void *address, bool force) {
// always dispose a DateTime
delete this;
return 1;
}
const char *ScriptDateTime::GetType() {
return "DateTime";
}
size_t ScriptDateTime::CalcSerializeSize(const void * /*address*/) {
return sizeof(int32_t) * 7;
}
void ScriptDateTime::Serialize(const void *address, Stream *out) {
out->WriteInt32(year);
out->WriteInt32(month);
out->WriteInt32(day);
out->WriteInt32(hour);
out->WriteInt32(minute);
out->WriteInt32(second);
out->WriteInt32(rawUnixTime);
}
void ScriptDateTime::Unserialize(int index, Stream *in, size_t data_sz) {
year = in->ReadInt32();
month = in->ReadInt32();
day = in->ReadInt32();
hour = in->ReadInt32();
minute = in->ReadInt32();
second = in->ReadInt32();
rawUnixTime = in->ReadInt32();
ccRegisterUnserializedObject(index, this, this);
}
ScriptDateTime::ScriptDateTime() {
year = month = day = 0;
hour = minute = second = 0;
rawUnixTime = 0;
}
} // namespace AGS3

View File

@@ -0,0 +1,49 @@
/* 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 AGS_ENGINE_DYNOBJ_SCRIPT_DATE_TIME_H
#define AGS_ENGINE_DYNOBJ_SCRIPT_DATE_TIME_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
struct ScriptDateTime final : AGSCCDynamicObject {
int year, month, day;
int hour, minute, second;
int rawUnixTime;
int Dispose(void *address, bool force) override;
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
ScriptDateTime();
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,34 @@
/* 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 AGS_ENGINE_DYNOBJ__SCRIPTDIALOG_H
#define AGS_ENGINE_DYNOBJ__SCRIPTDIALOG_H
namespace AGS3 {
struct ScriptDialog {
int id;
int reserved;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,69 @@
/* 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 "ags/engine/ac/dynobj/script_dialog_options_rendering.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/shared/util/stream.h"
namespace AGS3 {
using namespace AGS::Shared;
// return the type name of the object
const char *ScriptDialogOptionsRendering::GetType() {
return "DialogOptionsRendering";
}
size_t ScriptDialogOptionsRendering::CalcSerializeSize(const void * /*address*/) {
return 0;
}
// serialize the object into BUFFER (which is BUFSIZE bytes)
// return number of bytes used
void ScriptDialogOptionsRendering::Serialize(const void *address, Stream *out) {
}
void ScriptDialogOptionsRendering::Unserialize(int index, Stream *in, size_t data_sz) {
ccRegisterUnserializedObject(index, this, this);
}
void ScriptDialogOptionsRendering::Reset() {
x = 0;
y = 0;
width = 0;
height = 0;
hasAlphaChannel = false;
parserTextboxX = 0;
parserTextboxY = 0;
parserTextboxWidth = 0;
dialogID = 0;
surfaceToRenderTo = nullptr;
surfaceAccessed = false;
activeOptionID = -1;
chosenOptionID = -1;
needRepaint = false;
}
ScriptDialogOptionsRendering::ScriptDialogOptionsRendering() {
Reset();
}
} // namespace AGS3

View File

@@ -0,0 +1,59 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_SCRIPT_DIALOG_OPTIONS_RENDERING_H
#define AGS_ENGINE_AC_DYNOBJ_SCRIPT_DIALOG_OPTIONS_RENDERING_H
#include "ags/engine/ac/dynobj/script_drawing_surface.h"
namespace AGS3 {
struct ScriptDialogOptionsRendering final : AGSCCDynamicObject {
int x, y, width, height;
bool hasAlphaChannel;
int parserTextboxX, parserTextboxY;
int parserTextboxWidth;
int dialogID;
int activeOptionID;
int chosenOptionID;
ScriptDrawingSurface *surfaceToRenderTo;
bool surfaceAccessed;
bool needRepaint;
// return the type name of the object
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
void Reset();
ScriptDialogOptionsRendering();
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,54 @@
/* 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 "ags/engine/ac/dynobj/script_dict.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
namespace AGS3 {
int ScriptDictBase::Dispose(void *address, bool force) {
Clear();
delete this;
return 1;
}
const char *ScriptDictBase::GetType() {
return "StringDictionary";
}
size_t ScriptDictBase::CalcSerializeSize(const void * /*address*/) {
return CalcContainerSize();
}
void ScriptDictBase::Serialize(const void *address, Stream *out) {
out->WriteInt32(IsSorted());
out->WriteInt32(IsCaseSensitive());
SerializeContainer(out);
}
void ScriptDictBase::Unserialize(int index, Stream *in, size_t data_sz) {
// NOTE: we expect sorted/case flags are read by external reader;
// this is awkward, but I did not find better design solution atm
UnserializeContainer(in);
ccRegisterUnserializedObject(index, this, this);
}
} // namespace AGS3

View File

@@ -0,0 +1,193 @@
/* 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/>.
*
*/
//=============================================================================
//
// Managed script object wrapping std::map<String, String> and
// unordered_map<String, String>.
//
// TODO: support wrapping non-owned Dictionary, passed by the reference, -
// that would let expose internal engine's dicts using same interface.
// TODO: maybe optimize key lookup operations further by not creating a String
// object from const char*. It seems, C++14 standard allows to use convertible
// types as keys; need to research what performance impact that would make.
//
//=============================================================================
#ifndef AGS_ENGINE_AC_DYNOBJ_SCRIPTDICT_H
#define AGS_ENGINE_AC_DYNOBJ_SCRIPTDICT_H
#include "common/std/map.h"
#include "common/std/map.h"
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
#include "ags/shared/util/stream.h"
#include "ags/shared/util/string.h"
#include "ags/shared/util/string_types.h"
namespace AGS3 {
using namespace AGS::Shared;
class ScriptDictBase : public AGSCCDynamicObject {
public:
int Dispose(void *address, bool force) override;
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
virtual bool IsCaseSensitive() const = 0;
virtual bool IsSorted() const = 0;
virtual void Clear() = 0;
virtual bool Contains(const char *key) = 0;
virtual const char *Get(const char *key) = 0;
virtual bool Remove(const char *key) = 0;
virtual bool Set(const char *key, const char *value) = 0;
virtual int GetItemCount() = 0;
virtual void GetKeys(std::vector<const char *> &buf) const = 0;
virtual void GetValues(std::vector<const char *> &buf) const = 0;
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
private:
virtual size_t CalcContainerSize() = 0;
virtual void SerializeContainer(AGS::Shared::Stream *out) = 0;
virtual void UnserializeContainer(AGS::Shared::Stream *in) = 0;
};
template <typename TDict, bool is_sorted, bool is_casesensitive>
class ScriptDictImpl final : public ScriptDictBase {
public:
typedef typename TDict::const_iterator ConstIterator;
ScriptDictImpl() {}
bool IsCaseSensitive() const override {
return is_casesensitive;
}
bool IsSorted() const override {
return is_sorted;
}
void Clear() override {
for (auto it = _dic.begin(); it != _dic.end(); ++it)
DeleteItem(it);
_dic.clear();
}
bool Contains(const char *key) override {
#ifdef AGS_PLATFORM_SCUMMVM
return _dic.find(String::Wrapper(key)) != _dic.end();
#else
return _dic.count(String::Wrapper(key)) != 0;
#endif
}
const char *Get(const char *key) override {
auto it = _dic.find(String::Wrapper(key));
if (it == _dic.end()) return nullptr;
return it->_value.GetCStr();
}
bool Remove(const char *key) override {
auto it = _dic.find(String::Wrapper(key));
if (it == _dic.end()) return false;
DeleteItem(it);
_dic.erase(it);
return true;
}
bool Set(const char *key, const char *value) override {
if (!key)
return false;
if (!value) {
// Remove keys with null value
Remove(key);
return true;
}
return TryAddItem(String(key), String(value));
}
int GetItemCount() override {
return _dic.size();
}
void GetKeys(std::vector<const char *> &buf) const override {
for (auto it = _dic.begin(); it != _dic.end(); ++it)
buf.push_back(it->_key.GetCStr());
}
void GetValues(std::vector<const char *> &buf) const override {
for (auto it = _dic.begin(); it != _dic.end(); ++it)
buf.push_back(it->_value.GetCStr());
}
private:
bool TryAddItem(const String &key, const String &value) {
_dic[key] = value;
return true;
}
void DeleteItem(ConstIterator /*it*/) { /* do nothing */ }
size_t CalcContainerSize() override {
// 2 class properties + item count
size_t total_sz = sizeof(int32_t) * 3;
// (int32 + string buffer) per item
for (auto it = _dic.begin(); it != _dic.end(); ++it) {
total_sz += sizeof(int32_t) + it->_key.GetLength();
total_sz += sizeof(int32_t) + it->_value.GetLength();
}
return total_sz;
}
void SerializeContainer(AGS::Shared::Stream *out) override
{
out->WriteInt32((int)_dic.size());
for (auto it = _dic.begin(); it != _dic.end(); ++it)
{
out->WriteInt32((int)it->_key.GetLength());
out->Write(it->_key.GetCStr(), it->_key.GetLength());
out->WriteInt32((int)it->_value.GetLength());
out->Write(it->_value.GetCStr(), it->_value.GetLength());
}
}
void UnserializeContainer(AGS::Shared::Stream *in) override {
size_t item_count = in->ReadInt32();
for (size_t i = 0; i < item_count; ++i) {
size_t key_len = in->ReadInt32();
String key = String::FromStreamCount(in, key_len);
size_t value_len = in->ReadInt32();
if (value_len != (size_t)-1) // do not restore keys with null value (old format)
{
String value = String::FromStreamCount(in, value_len);
TryAddItem(key, value);
}
}
}
TDict _dic;
};
typedef ScriptDictImpl< std::map<String, String>, true, true > ScriptDict;
typedef ScriptDictImpl< std::map<String, String, IgnoreCase_LessThan>, true, false > ScriptDictCI;
typedef ScriptDictImpl< std::unordered_map<String, String>, false, true > ScriptHashDict;
typedef ScriptDictImpl< std::unordered_map<String, String, IgnoreCase_Hash, IgnoreCase_EqualTo>, false, false > ScriptHashDictCI;
} // namespace AGS3
#endif

View File

@@ -0,0 +1,138 @@
/* 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 "ags/engine/ac/dynobj/script_drawing_surface.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/shared/ac/sprite_cache.h"
#include "ags/engine/ac/runtime_defines.h"
#include "ags/shared/ac/common.h"
#include "ags/engine/ac/draw.h"
#include "ags/engine/ac/drawing_surface.h"
#include "ags/engine/ac/game_state.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/shared/game/room_struct.h"
#include "ags/shared/gfx/bitmap.h"
#include "ags/globals.h"
namespace AGS3 {
using namespace AGS::Shared;
Bitmap *ScriptDrawingSurface::GetBitmapSurface() {
// TODO: consider creating weak_ptr here, and store one in the DrawingSurface!
if (roomBackgroundNumber >= 0)
return _GP(thisroom).BgFrames[roomBackgroundNumber].Graphic.get();
else if (dynamicSpriteNumber >= 0)
return _GP(spriteset)[dynamicSpriteNumber];
else if (dynamicSurfaceNumber >= 0)
return _G(dynamicallyCreatedSurfaces)[dynamicSurfaceNumber].get();
else if (linkedBitmapOnly != nullptr)
return linkedBitmapOnly;
else if (roomMaskType > kRoomAreaNone)
return _GP(thisroom).GetMask(roomMaskType);
quit("!DrawingSurface: attempted to use surface after Release was called");
return nullptr;
}
Bitmap *ScriptDrawingSurface::StartDrawing() {
return this->GetBitmapSurface();
}
void ScriptDrawingSurface::FinishedDrawingReadOnly() {
}
void ScriptDrawingSurface::FinishedDrawing() {
FinishedDrawingReadOnly();
modified = 1;
}
int ScriptDrawingSurface::Dispose(void *address, bool force) {
// dispose the drawing surface
DrawingSurface_Release(this);
delete this;
return 1;
}
const char *ScriptDrawingSurface::GetType() {
return "DrawingSurface";
}
size_t ScriptDrawingSurface::CalcSerializeSize(const void * /*address*/) {
return sizeof(int32_t) * 9;
}
void ScriptDrawingSurface::Serialize(const void *address, Stream *out) {
// pack mask type in the last byte of a negative integer
// note: (-1) is reserved for "unused", for backward compatibility
if (roomMaskType > 0)
out->WriteInt32(0xFFFFFF00 | roomMaskType);
else
out->WriteInt32(roomBackgroundNumber);
out->WriteInt32(dynamicSpriteNumber);
out->WriteInt32(dynamicSurfaceNumber);
out->WriteInt32(currentColour);
out->WriteInt32(currentColourScript);
out->WriteInt32(highResCoordinates);
out->WriteInt32(modified);
out->WriteInt32(hasAlphaChannel);
out->WriteInt32(isLinkedBitmapOnly ? 1 : 0);
}
void ScriptDrawingSurface::Unserialize(int index, Stream *in, size_t data_sz) {
int room_ds = in->ReadInt32();
if (room_ds >= 0)
roomBackgroundNumber = room_ds;
// negative value may contain a mask type
else if ((room_ds & 0xFF) != 0xFF)
roomMaskType = (RoomAreaMask)(room_ds & 0xFF);
dynamicSpriteNumber = in->ReadInt32();
dynamicSurfaceNumber = in->ReadInt32();
currentColour = in->ReadInt32();
currentColourScript = in->ReadInt32();
highResCoordinates = in->ReadInt32();
modified = in->ReadInt32();
hasAlphaChannel = in->ReadInt32();
isLinkedBitmapOnly = (in->ReadInt32() != 0);
ccRegisterUnserializedObject(index, this, this);
}
ScriptDrawingSurface::ScriptDrawingSurface() {
roomBackgroundNumber = -1;
roomMaskType = kRoomAreaNone;
dynamicSpriteNumber = -1;
dynamicSurfaceNumber = -1;
isLinkedBitmapOnly = false;
linkedBitmapOnly = nullptr;
currentColour = _GP(play).raw_color;
currentColourScript = 0;
modified = 0;
hasAlphaChannel = 0;
highResCoordinates = 0;
// NOTE: Normally in contemporary games coordinates ratio will always be 1:1.
// But we still support legacy drawing, so have to set this up even for modern games,
// otherwise we'd have to complicate conversion conditions further.
if (_GP(game).IsLegacyHiRes() && _GP(game).IsDataInNativeCoordinates()) {
highResCoordinates = 1;
}
}
} // namespace AGS3

View File

@@ -0,0 +1,71 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_SCRIPT_DRAWING_SURFACE_H
#define AGS_ENGINE_AC_DYNOBJ_SCRIPT_DRAWING_SURFACE_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
#include "ags/shared/game/room_struct.h"
#include "ags/shared/gfx/bitmap.h"
#include "ags/shared/util/stream.h"
namespace AGS3 {
struct ScriptDrawingSurface final : AGSCCDynamicObject {
// These numbers and types are used to determine the source of this drawing surface;
// only one of them can be valid for this surface.
int roomBackgroundNumber;
RoomAreaMask roomMaskType;
int dynamicSpriteNumber;
int dynamicSurfaceNumber;
bool isLinkedBitmapOnly;
AGS::Shared::Bitmap *linkedBitmapOnly;
int currentColour;
int currentColourScript;
int highResCoordinates;
int modified;
int hasAlphaChannel;
//Shared::Bitmap* abufBackup;
int Dispose(void *address, bool force) override;
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
AGS::Shared::Bitmap *GetBitmapSurface();
AGS::Shared::Bitmap *StartDrawing();
void PointToGameResolution(int *xcoord, int *ycoord);
void SizeToGameResolution(int *width, int *height);
void SizeToGameResolution(int *adjustValue);
void SizeToDataResolution(int *adjustValue);
void FinishedDrawing();
void FinishedDrawingReadOnly();
ScriptDrawingSurface();
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,67 @@
/* 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 "ags/engine/ac/dynobj/script_dynamic_sprite.h"
#include "ags/engine/ac/dynobj/dynobj_manager.h"
#include "ags/shared/util/stream.h"
#include "ags/engine/ac/dynamic_sprite.h"
namespace AGS3 {
using namespace AGS::Shared;
int ScriptDynamicSprite::Dispose(void *address, bool force) {
// always dispose
if ((slot) && (!force))
free_dynamic_sprite(slot);
delete this;
return 1;
}
const char *ScriptDynamicSprite::GetType() {
return "DynamicSprite";
}
size_t ScriptDynamicSprite::CalcSerializeSize(const void * /*address*/) {
return sizeof(int32_t);
}
void ScriptDynamicSprite::Serialize(const void *address, Stream *out) {
out->WriteInt32(slot);
}
void ScriptDynamicSprite::Unserialize(int index, Stream *in, size_t data_sz) {
slot = in->ReadInt32();
ccRegisterUnserializedObject(index, this, this);
}
ScriptDynamicSprite::ScriptDynamicSprite(int theSlot) {
slot = theSlot;
ccRegisterManagedObject(this, this);
}
ScriptDynamicSprite::ScriptDynamicSprite() {
slot = 0;
}
} // namespace AGS3

View File

@@ -0,0 +1,48 @@
/* 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 AGS_ENGINE_AC_DYNOBJ_SCRIPTDYNAMICSPRITE_H
#define AGS_ENGINE_AC_DYNOBJ_SCRIPTDYNAMICSPRITE_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
struct ScriptDynamicSprite final : AGSCCDynamicObject {
int slot;
int Dispose(void *address, bool force) override;
const char *GetType() override;
void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
ScriptDynamicSprite(int slot);
ScriptDynamicSprite();
protected:
// Calculate and return required space for serialization, in bytes
size_t CalcSerializeSize(const void *address) override;
// Write object data into the provided stream
void Serialize(const void *address, AGS::Shared::Stream *out) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,66 @@
/* 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 "ags/engine/ac/dynobj/script_file.h"
#include "ags/engine/ac/global_file.h"
namespace AGS3 {
// CHECKME: actually NULLs here will be equal to kFile_Open & kFile_Read
const Shared::FileOpenMode sc_File::fopenModes[] =
{ Shared::kFile_Open/*CHECKME, was undefined*/, Shared::kFile_Open, Shared::kFile_CreateAlways, Shared::kFile_Create };
const Shared::FileWorkMode sc_File::fworkModes[] =
{ Shared::kFile_Read/*CHECKME, was undefined*/, Shared::kFile_Read, Shared::kFile_Write, Shared::kFile_Write };
int sc_File::Dispose(void *address, bool force) {
Close();
delete this;
return 1;
}
const char *sc_File::GetType() {
return "File";
}
int sc_File::Serialize(void *address, uint8_t *buffer, int bufsize) {
// we cannot serialize an open file, so it will get closed
return 0;
}
int sc_File::OpenFile(const char *filename, int mode) {
handle = FileOpen(filename, fopenModes[mode], fworkModes[mode]);
if (handle <= 0)
return 0;
return 1;
}
void sc_File::Close() {
if (handle > 0) {
FileClose(handle);
handle = 0;
}
}
sc_File::sc_File() {
handle = 0;
}
} // namespace AGS3

View File

@@ -0,0 +1,56 @@
/* 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 AGS_ENGINE_DYNOBJ__SCRIPTFILE_H
#define AGS_ENGINE_DYNOBJ__SCRIPTFILE_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
#include "ags/shared/util/file.h"
namespace AGS3 {
using namespace AGS; // FIXME later
#define scFileRead 1
#define scFileWrite 2
#define scFileAppend 3
struct sc_File final : CCBasicObject {
int32_t handle;
static const Shared::FileOpenMode fopenModes[];
static const Shared::FileWorkMode fworkModes[];
int Dispose(void *address, bool force) override;
const char *GetType() override;
int Serialize(void *address, uint8_t *buffer, int bufsize) override;
int OpenFile(const char *filename, int mode);
void Close();
sc_File();
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,442 @@
/* 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 "ags/engine/debugging/debug_log.h"
#include "ags/engine/ac/dynobj/script_game.h"
#include "ags/engine/ac/game.h"
#include "ags/engine/ac/game_state.h"
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/shared/gui/gui_main.h"
#include "ags/shared/script/cc_common.h" // cc_error
#include "ags/globals.h"
namespace AGS3 {
int32_t CCScriptGame::ReadInt32(void *address, intptr_t offset) {
const int index = offset / sizeof(int32_t);
if (index >= 5 && index < 5 + MAXGLOBALVARS)
return _GP(play).globalvars[index - 5];
switch (index) {
case 0:
return _GP(play).score;
case 1:
return _GP(play).usedmode;
case 2:
return _GP(play).disabled_user_interface;
case 3:
return _GP(play).gscript_timer;
case 4:
return _GP(play).debug_mode;
// 5 -> 54: _GP(play).globalvars
case 55:
return _GP(play).messagetime;
case 56:
return _GP(play).usedinv;
case 57:
return _GP(play).inv_top;
case 58:
return _GP(play).inv_numdisp;
case 59:
return _GP(play).inv_numorder;
case 60:
return _GP(play).inv_numinline;
case 61:
return _GP(play).text_speed;
case 62:
return _GP(play).sierra_inv_color;
case 63:
return _GP(play).talkanim_speed;
case 64:
return _GP(play).inv_item_wid;
case 65:
return _GP(play).inv_item_hit;
case 66:
return _GP(play).speech_text_shadow;
case 67:
return _GP(play).swap_portrait_side;
case 68:
return _GP(play).speech_textwindow_gui;
case 69:
return _GP(play).follow_change_room_timer;
case 70:
return _GP(play).totalscore;
case 71:
return _GP(play).skip_display;
case 72:
return _GP(play).no_multiloop_repeat;
case 73:
return _GP(play).roomscript_finished;
case 74:
return _GP(play).used_inv_on;
case 75:
return _GP(play).no_textbg_when_voice;
case 76:
return _GP(play).max_dialogoption_width;
case 77:
return _GP(play).no_hicolor_fadein;
case 78:
return _GP(play).bgspeech_game_speed;
case 79:
return _GP(play).bgspeech_stay_on_display;
case 80:
return _GP(play).unfactor_speech_from_textlength;
case 81:
return _GP(play).mp3_loop_before_end;
case 82:
return _GP(play).speech_music_drop;
case 83:
return _GP(play).in_cutscene;
case 84:
return _GP(play).fast_forward;
case 85:
return _GP(play).room_width;
case 86:
return _GP(play).room_height;
case 87:
return _GP(play).game_speed_modifier;
case 88:
return _GP(play).score_sound;
case 89:
return _GP(play).takeover_data;
case 90:
return 0; // _GP(play).replay_hotkey
case 91:
return _GP(play).dialog_options_x;
case 92:
return _GP(play).dialog_options_y;
case 93:
return _GP(play).narrator_speech;
case 94:
return _GP(play).ambient_sounds_persist;
case 95:
return _GP(play).lipsync_speed;
case 96:
return _GP(play).close_mouth_speech_time;
case 97:
return _GP(play).disable_antialiasing;
case 98:
return _GP(play).text_speed_modifier;
case 99:
return _GP(play).text_align;
case 100:
return _GP(play).speech_bubble_width;
case 101:
return _GP(play).min_dialogoption_width;
case 102:
return _GP(play).disable_dialog_parser;
case 103:
return _GP(play).anim_background_speed;
case 104:
return _GP(play).top_bar_backcolor;
case 105:
return _GP(play).top_bar_textcolor;
case 106:
return _GP(play).top_bar_bordercolor;
case 107:
return _GP(play).top_bar_borderwidth;
case 108:
return _GP(play).top_bar_ypos;
case 109:
return _GP(play).screenshot_width;
case 110:
return _GP(play).screenshot_height;
case 111:
return _GP(play).top_bar_font;
case 112:
return _GP(play).speech_text_align;
case 113:
return _GP(play).auto_use_walkto_points;
case 114:
return _GP(play).inventory_greys_out;
case 115:
return _GP(play).skip_speech_specific_key;
case 116:
return _GP(play).abort_key;
case 117:
return _GP(play).fade_to_red;
case 118:
return _GP(play).fade_to_green;
case 119:
return _GP(play).fade_to_blue;
case 120:
return _GP(play).show_single_dialog_option;
case 121:
return _GP(play).keep_screen_during_instant_transition;
case 122:
return _GP(play).read_dialog_option_colour;
case 123:
return _GP(play).stop_dialog_at_end;
case 124:
return _GP(play).speech_portrait_placement;
case 125:
return _GP(play).speech_portrait_x;
case 126:
return _GP(play).speech_portrait_y;
case 127:
return _GP(play).speech_display_post_time_ms;
case 128:
return _GP(play).dialog_options_highlight_color;
default:
cc_error("ScriptGame: unsupported variable offset %d", offset);
return 0;
}
}
void CCScriptGame::WriteInt32(void *address, intptr_t offset, int32_t val) {
const int index = offset / sizeof(int32_t);
if (index >= 5 && index < 5 + MAXGLOBALVARS) {
_GP(play).globalvars[index - 5] = val;
return;
}
switch (index) {
case 0:
_GP(play).score = val;
break;
case 1:
_GP(play).usedmode = val;
break;
case 2:
_GP(play).disabled_user_interface = val;
break;
case 3:
_GP(play).gscript_timer = val;
break;
case 4:
set_debug_mode(val != 0);
break; // _GP(play).debug_mode
// 5 -> 54: _GP(play).globalvars
case 55:
_GP(play).messagetime = val;
break;
case 56:
_GP(play).usedinv = val;
break;
case 57:
_GP(play).inv_top = val;
GUI::MarkInventoryForUpdate(_GP(game).playercharacter, true);
break;
case 58: // play.inv_numdisp
case 59: // play.inv_numorder
case 60: // play.inv_numinline
debug_script_warn("ScriptGame: attempt to write in readonly variable at offset %d, value %d", offset, val);
break;
case 61:
_GP(play).text_speed = val;
break;
case 62:
_GP(play).sierra_inv_color = val;
break;
case 63:
_GP(play).talkanim_speed = val;
break;
case 64:
_GP(play).inv_item_wid = val;
break;
case 65:
_GP(play).inv_item_hit = val;
break;
case 66:
_GP(play).speech_text_shadow = val;
break;
case 67:
_GP(play).swap_portrait_side = val;
break;
case 68:
_GP(play).speech_textwindow_gui = val;
break;
case 69:
_GP(play).follow_change_room_timer = val;
break;
case 70:
_GP(play).totalscore = val;
break;
case 71:
_GP(play).skip_display = val;
break;
case 72:
_GP(play).no_multiloop_repeat = val;
break;
case 73:
_GP(play).roomscript_finished = val;
break;
case 74:
_GP(play).used_inv_on = val;
break;
case 75:
_GP(play).no_textbg_when_voice = val;
break;
case 76:
_GP(play).max_dialogoption_width = val;
break;
case 77:
_GP(play).no_hicolor_fadein = val;
break;
case 78:
_GP(play).bgspeech_game_speed = val;
break;
case 79:
_GP(play).bgspeech_stay_on_display = val;
break;
case 80:
_GP(play).unfactor_speech_from_textlength = val;
break;
case 81:
_GP(play).mp3_loop_before_end = val;
break;
case 82:
_GP(play).speech_music_drop = val;
break;
case 83: // _GP(play).in_cutscene
case 84: // _GP(play).fast_forward;
case 85: // _GP(play).room_width;
case 86: // _GP(play).room_height;
debug_script_warn("ScriptGame: attempt to write in readonly variable at offset %d, value %d", offset, val);
break;
case 87:
_GP(play).game_speed_modifier = val;
break;
case 88:
_GP(play).score_sound = val;
break;
case 89:
_GP(play).takeover_data = val;
break;
case 90:
break; // _GP(play).replay_hotkey
case 91:
_GP(play).dialog_options_x = val;
break;
case 92:
_GP(play).dialog_options_y = val;
break;
case 93:
_GP(play).narrator_speech = val;
break;
case 94:
_GP(play).ambient_sounds_persist = val;
break;
case 95:
_GP(play).lipsync_speed = val;
break;
case 96:
_GP(play).close_mouth_speech_time = val;
break;
case 97:
_GP(play).disable_antialiasing = val;
break;
case 98:
_GP(play).text_speed_modifier = val;
break;
case 99:
_GP(play).text_align = ReadScriptAlignment(val);
break;
case 100:
_GP(play).speech_bubble_width = val;
break;
case 101:
_GP(play).min_dialogoption_width = val;
break;
case 102:
_GP(play).disable_dialog_parser = val;
break;
case 103:
_GP(play).anim_background_speed = val;
break;
case 104:
_GP(play).top_bar_backcolor = val;
break;
case 105:
_GP(play).top_bar_textcolor = val;
break;
case 106:
_GP(play).top_bar_bordercolor = val;
break;
case 107:
_GP(play).top_bar_borderwidth = val;
break;
case 108:
_GP(play).top_bar_ypos = val;
break;
case 109:
_GP(play).screenshot_width = val;
break;
case 110:
_GP(play).screenshot_height = val;
break;
case 111:
_GP(play).top_bar_font = val;
break;
case 112:
_GP(play).speech_text_align = ReadScriptAlignment(val);
break;
case 113:
_GP(play).auto_use_walkto_points = val;
break;
case 114:
_GP(play).inventory_greys_out = val;
break;
case 115:
_GP(play).skip_speech_specific_key = val;
break;
case 116:
_GP(play).abort_key = val;
break;
case 117: // _GP(play).fade_to_red;
case 118: // _GP(play).fade_to_green;
case 119: // _GP(play).fade_to_blue;
debug_script_warn("ScriptGame: attempt to write in readonly variable at offset %d, value %d", offset, val);
break;
case 120:
_GP(play).show_single_dialog_option = val;
break;
case 121:
_GP(play).keep_screen_during_instant_transition = val;
break;
case 122:
_GP(play).read_dialog_option_colour = val;
break;
case 123:
_GP(play).stop_dialog_at_end = val;
break;
case 124:
_GP(play).speech_portrait_placement = val;
break;
case 125:
_GP(play).speech_portrait_x = val;
break;
case 126:
_GP(play).speech_portrait_y = val;
break;
case 127:
_GP(play).speech_display_post_time_ms = val;
break;
case 128:
_GP(play).dialog_options_highlight_color = val;
break;
default:
cc_error("ScriptGame: unsupported variable offset %d", offset);
break;
}
}
} // namespace AGS3

View File

@@ -0,0 +1,42 @@
/* 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/>.
*
*/
//=============================================================================
//
// Wrapper around script "GameState" struct, managing access to its variables.
//
//=============================================================================
#ifndef AGS_ENGINE_AC_DYNOBJ_AGS_SCRIPT_GAME_H
#define AGS_ENGINE_AC_DYNOBJ_AGS_SCRIPT_GAME_H
#include "ags/engine/ac/dynobj/cc_ags_dynamic_object.h"
namespace AGS3 {
struct CCScriptGame : public AGSCCStaticObject {
int32_t ReadInt32(void *address, intptr_t offset) override;
void WriteInt32(void *address, intptr_t offset, int32_t val) override;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,35 @@
/* 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 AGS_ENGINE_DYNOBJ__SCRIPTGUI_H
#define AGS_ENGINE_DYNOBJ__SCRIPTGUI_H
namespace AGS3 {
// 64 bit: This struct must be 8 byte long
struct ScriptGUI {
int id;
int __padding;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,34 @@
/* 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 AGS_ENGINE_DYNOBJ__SCRIPTHOTSPOT_H
#define AGS_ENGINE_DYNOBJ__SCRIPTHOTSPOT_H
namespace AGS3 {
struct ScriptHotspot {
int id = 0;
int reserved = 0;
};
} // namespace AGS3
#endif

View File

@@ -0,0 +1,34 @@
/* 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 AGS_ENGINE_DYNOBJ__SCRIPTINVITEM_H
#define AGS_ENGINE_DYNOBJ__SCRIPTINVITEM_H
namespace AGS3 {
struct ScriptInvItem {
int id = 0;
int reserved = 0;
};
} // namespace AGS3
#endif

View 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/>.
*
*/
#include "ags/engine/debugging/debug_log.h"
#include "ags/engine/ac/dynobj/script_mouse.h"
#include "ags/shared/script/cc_common.h" // cc_error
namespace AGS3 {
int32_t ScriptMouse::ReadInt32(void *address, intptr_t offset) {
switch (offset) {
case 0:
return x;
case 4:
return y;
default:
cc_error("ScriptMouse: unsupported variable offset %d", offset);
return 0;
}
}
void ScriptMouse::WriteInt32(void *address, intptr_t offset, int32_t val) {
switch (offset) {
case 0:
case 4:
debug_script_warn("ScriptMouse: attempt to write in readonly variable at offset %d, value", offset, val);
break;
default:
cc_error("ScriptMouse: unsupported variable offset %d", offset);
break;
}
}
} // namespace AGS3

Some files were not shown because too many files have changed in this diff Show More