/* 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 . * */ #include "common/debug.h" #include "common/file.h" #include "common/tokenizer.h" #include "alg/scene.h" namespace Alg { SceneInfo::SceneInfo() { } SceneInfo::~SceneInfo() { for (auto scene : _scenes) { delete scene; } for (auto zone : _zones) { delete zone; } _scenes.clear(); _zones.clear(); } void SceneInfo::loadScnFile(const Common::Path &path) { debug("loading scene script: %s", path.toString().c_str()); if (!_scnFile.open(path)) { error("SceneInfo::loadScnFile(): Can't open scene file '%s'", path.toString().c_str()); } bool done = false; while (_scnFile.pos() < _scnFile.size() && !done) { Common::String line = _scnFile.readLine(); line.trim(); if (ignoreScriptLine(line)) { continue; } Common::StringTokenizer tokenizer(line, " "); int8 token = getToken(_mainTokens, tokenizer.nextToken()); uint32 startFrame = 0, endFrame = 0; Common::String sceneName = nullptr, zoneName = nullptr; switch (token) { case 0: // ; break; case 1: // ZONE zoneName = tokenizer.nextToken(); startFrame = atoi(tokenizer.nextToken().c_str()); endFrame = atoi(tokenizer.nextToken().c_str()); parseZone(zoneName, startFrame, endFrame); break; case 2: // SCENE sceneName = tokenizer.nextToken(); startFrame = atoi(tokenizer.nextToken().c_str()); endFrame = atoi(tokenizer.nextToken().c_str()); sceneName.toLowercase(); parseScene(sceneName, startFrame, endFrame); break; case 3: // MSG error("SceneInfo::loadScnFile(): MSG Not implemented: %s", line.c_str()); break; case 4: // START _startScene = tokenizer.nextToken(); break; case 5: // GLOBAL error("SceneInfo::loadScnFile(): GLOBAL Not implemented: %s", line.c_str()); break; case 6: // END done = true; break; default: error("SceneInfo::loadScnFile(): Unknown script section encountered: %s", line.c_str()); break; } } _scnFile.close(); addZonesToScenes(); } void SceneInfo::parseScene(const Common::String &sceneName, uint32 startFrame, uint32 endFrame) { Scene *scene = new Scene(sceneName, startFrame, endFrame); bool done = false; while (_scnFile.pos() < _scnFile.size() && !done) { Common::String line = _scnFile.readLine(); line.trim(); if (ignoreScriptLine(line)) { continue; } Common::StringTokenizer tokenizer(line, " "); int8 token = getToken(_sceneTokens, tokenizer.nextToken()); switch (token) { case 0: // EOF case 1: // NEXT scene->_next = tokenizer.nextToken(); break; case 2: // ZONES scene->_zonesStart = tokenizer.nextToken(); scene->_zonesStart2 = tokenizer.nextToken(); scene->_zonesStart3 = tokenizer.nextToken(); break; case 3: // PREOP scene->_preop = tokenizer.nextToken(); scene->_preopParam = tokenizer.nextToken(); break; case 4: // SHOWMSG / SCNMSG scene->_scnmsg = tokenizer.nextToken(); scene->_scnmsgParam = tokenizer.nextToken(); break; case 5: // INSOP scene->_insop = tokenizer.nextToken(); scene->_insopParam = tokenizer.nextToken(); break; case 6: // WEPDWN scene->_wepdwn = tokenizer.nextToken(); break; case 7: // SCNSCR scene->_scnscr = tokenizer.nextToken(); scene->_scnscrParam = atoi(tokenizer.nextToken().c_str()); break; case 8: // NXTFRM scene->_nxtfrm = tokenizer.nextToken(); break; case 9: // NXTSCN scene->_nxtscn = tokenizer.nextToken(); // ignore next token if existing tokenizer.nextToken(); break; case 10: // DATA scene->_dataParam1 = atoi(tokenizer.nextToken().c_str()); scene->_dataParam2 = atoi(tokenizer.nextToken().c_str()); scene->_dataParam3 = atoi(tokenizer.nextToken().c_str()); scene->_dataParam4 = atoi(tokenizer.nextToken().c_str()); scene->_dataParam5 = tokenizer.nextToken().c_str(); scene->_dataParam6 = tokenizer.nextToken().c_str(); break; case 11: // DIFF scene->_diff = atoi(tokenizer.nextToken().c_str()); break; case 12: // MISSEDRECTS scene->_missedRects = tokenizer.nextToken(); // ignore next token if existing tokenizer.nextToken(); break; case 13: // DIFFICULTY_MOD scene->_difficultyMod = atoi(tokenizer.nextToken().c_str()); break; case 14: // ; done = true; break; default: error("SceneInfo::parseScene(): Unknown scene token found: %s", line.c_str()); break; } Common::String nextToken = tokenizer.nextToken(); if (!nextToken.empty()) { error("SceneInfo::parseScene(): missed token %s in line %s", nextToken.c_str(), line.c_str()); } } _scenes.push_back(scene); } void SceneInfo::parseZone(const Common::String &zoneName, uint32 startFrame, uint32 endFrame) { Zone *zone = new Zone(zoneName, startFrame, endFrame); bool done = false; while (_scnFile.pos() < _scnFile.size() && !done) { Common::String line = _scnFile.readLine(); line.trim(); if (ignoreScriptLine(line)) { continue; } Common::StringTokenizer tokenizer(line, " "); int8 token = getToken(_zoneTokens, tokenizer.nextToken()); Rect *rect = nullptr; switch (token) { case 0: // EOF break; case 1: // NEXT zone->_next = tokenizer.nextToken(); break; case 2: // PTRFB zone->_ptrfb = tokenizer.nextToken(); break; case 3: // RECT { Common::String temp = tokenizer.nextToken(); if (temp == "MOVING") { rect = new Rect(); rect->_isMoving = true; rect->left = atoi(tokenizer.nextToken().c_str()); rect->top = atoi(tokenizer.nextToken().c_str()); rect->right = atoi(tokenizer.nextToken().c_str()); rect->bottom = atoi(tokenizer.nextToken().c_str()); rect->_dest.left = atoi(tokenizer.nextToken().c_str()); rect->_dest.top = atoi(tokenizer.nextToken().c_str()); rect->_dest.right = atoi(tokenizer.nextToken().c_str()); rect->_dest.bottom = atoi(tokenizer.nextToken().c_str()); rect->_scene = tokenizer.nextToken(); rect->_score = atoi(tokenizer.nextToken().c_str()); rect->_rectHit = tokenizer.nextToken(); rect->_unknown = tokenizer.nextToken(); zone->_rects.push_back(rect); } else { rect = new Rect(); rect->_isMoving = false; rect->left = atoi(temp.c_str()); rect->top = atoi(tokenizer.nextToken().c_str()); rect->right = atoi(tokenizer.nextToken().c_str()); rect->bottom = atoi(tokenizer.nextToken().c_str()); rect->_scene = tokenizer.nextToken(); rect->_score = atoi(tokenizer.nextToken().c_str()); rect->_rectHit = tokenizer.nextToken(); rect->_unknown = tokenizer.nextToken(); zone->_rects.push_back(rect); } } break; case 4: // ; done = true; break; default: error("SceneInfo::parseZone(): Unknown zone token found: %s", line.c_str()); break; } Common::String nextToken = tokenizer.nextToken(); if (!nextToken.empty()) { error("SceneInfo::parseZone(): missed token %s in line %s", nextToken.c_str(), line.c_str()); } } _zones.push_back(zone); } void SceneInfo::addZonesToScenes() { for (auto &scene : _scenes) { if (!scene->_zonesStart.empty()) { Zone *zone = findZone(scene->_zonesStart); while (true) { if (zone != nullptr) { zone = zone->clone(); zone->_difficulty = 0; scene->_zones.push_back(zone); if (zone->_next.empty()) { break; } else { zone = findZone(zone->_next); if (zone == nullptr) { break; } } } } } if (!scene->_zonesStart2.empty()) { Zone *zone = findZone(scene->_zonesStart); while (true) { if (zone != nullptr) { zone = zone->clone(); zone->_difficulty = 1; scene->_zones.push_back(zone); if (zone->_next.empty()) { break; } else { zone = findZone(zone->_next); if (zone == nullptr) { break; } } } } } if (!scene->_zonesStart3.empty()) { Zone *zone = findZone(scene->_zonesStart); while (true) { if (zone != nullptr) { zone = zone->clone(); zone->_difficulty = 2; scene->_zones.push_back(zone); if (zone->_next.empty()) { break; } else { zone = findZone(zone->_next); if (zone == nullptr) { break; } } } } } } } void SceneInfo::addScene(Scene *scene) { _scenes.push_back(scene); } Zone *SceneInfo::findZone(const Common::String &zoneName) { for (auto &zone : _zones) { if (zone->_name.equalsIgnoreCase(zoneName)) { return zone; } } warning("SceneInfo::findZone(): Cannot find zone %s", zoneName.c_str()); return nullptr; } Scene *SceneInfo::findScene(const Common::String &sceneName) { for (auto &scene : _scenes) { if (scene->_name.equalsIgnoreCase(sceneName)) { return scene; } } error("SceneInfo::findScene(): Cannot find scene %s", sceneName.c_str()); } int8 SceneInfo::getToken(const TokenEntry *tokenList, const Common::String &token) { for (int i = 0; tokenList[i].name != nullptr; i++) { if (token == tokenList[i].name) { return tokenList[i].value; } } return -1; } bool SceneInfo::ignoreScriptLine(const Common::String &line) { if (line.empty()) { return true; // empty line } else if (line.hasPrefix("//")) { return true; // comment } else if (line.hasPrefix("*")) { return true; // doc comment } else if (line.hasPrefix("NXET")) { return true; // typo in Maddog2 } else if (line.hasPrefix("DATA$")) { return true; // typo in DrugWars } else if (line.hasPrefix("NUMBER_OF_")) { return true; // unnecessary numbers } return false; } Scene::Scene(const Common::String &name, uint32 startFrame, uint32 endFrame) { _preop = "DEFAULT"; _insop = "DEFAULT"; _scnmsg = "DEFAULT"; _wepdwn = "DEFAULT"; _scnscr = "DEFAULT"; _nxtfrm = "DEFAULT"; _nxtscn = "DEFAULT"; _missedRects = "DEFAULT"; _scnscrParam = 0; _dataParam1 = 0; _dataParam2 = 0; _dataParam3 = 0; _dataParam4 = 0; _dataParam5 = ""; _name = name; _startFrame = startFrame; _endFrame = endFrame; _diff = 0; _difficultyMod = 0; } Scene::~Scene() { for (auto zone : _zones) { delete zone; } _zones.clear(); } Zone::Zone(const Common::String &name, const Common::String &ptrfb) { _name = name; _ptrfb = ptrfb; } Zone::Zone(const Common::String &name, uint32 startFrame, uint32 endFrame) { _name = name; _startFrame = startFrame; _endFrame = endFrame; } Zone::~Zone() { for (auto rect : _rects) { delete rect; } _rects.clear(); } void Zone::addRect(int16 left, int16 top, int16 right, int16 bottom, const Common::String scene, uint32 score, const Common::String rectHit, const Common::String unknown) { Rect *rect = new Rect(); rect->left = left; rect->top = top; rect->right = right; rect->bottom = bottom; rect->_scene = scene; rect->_score = score; rect->_rectHit = rectHit; rect->_unknown = unknown; _rects.push_back(rect); } Zone *Zone::clone() { Zone *clone = new Zone(_name, _startFrame, _endFrame); clone->_ptrfb = _ptrfb; clone->_next = _next; for (auto rect : _rects) { Rect *cloneRect = rect->clone(); clone->_rects.push_back(cloneRect); } return clone; } void Rect::center(int16 cx, int16 cy, int16 w, int16 h) { right = cx + (w / 2); left = cx - (w / 2); top = cy - (h / 2); bottom = cy + (h / 2); } Common::Rect Rect::getInterpolatedRect(uint32 startFrame, uint32 endFrame, uint32 currentFrame) { if (_isMoving && currentFrame >= startFrame && currentFrame < endFrame) { Rect interpolated; uint32 totalFrames = endFrame - startFrame; uint32 relativeFrame = currentFrame - startFrame; double percentage = relativeFrame * 100.0f / totalFrames; interpolated.top = top + ((_dest.top - top) / 100.0f * percentage); interpolated.left = left + ((_dest.left - left) / 100.0f * percentage); interpolated.bottom = bottom + ((_dest.bottom - bottom) / 100.0f * percentage); interpolated.right = right + ((_dest.right - right) / 100.0f * percentage); return interpolated; } else if (_isMoving && currentFrame >= endFrame) { Rect finished; finished.top = _dest.top; finished.left = _dest.left; finished.bottom = _dest.bottom; finished.right = _dest.right; return finished; } else { Rect normal; normal.top = top; normal.left = left; normal.bottom = bottom; normal.right = right; return normal; } } Rect *Rect::clone() { Rect *clone = new Rect(); clone->top = top; clone->left = left; clone->bottom = bottom; clone->right = right; clone->_scene = _scene; clone->_score = _score; clone->_rectHit = _rectHit; clone->_unknown = _unknown; clone->_isMoving = _isMoving; clone->_dest = _dest; return clone; } } // End of namespace Alg