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

96
engines/crab/GameClock.h Normal file
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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_GAMECLOCK_H
#define CRAB_GAMECLOCK_H
#include "crab/numstr.h"
#include "crab/timer.h"
namespace Crab {
class GameClock {
Timer _timer;
uint32 _start;
Common::String _seperator;
public:
GameClock() : _seperator(" : ") { _start = 0; }
void start(uint32 initialTime = 0) {
_start = initialTime;
_timer.start();
}
void start(const Common::String &str) {
uint32 ms = 0, hr = 0, min = 0, sec = 0;
Common::String strHrs, strMin, strSec;
size_t found1 = str.findFirstOf(_seperator);
if (found1 > 0 && found1 != Common::String::npos) {
strHrs = str.substr(0, found1);
hr = stringToNumber<uint32>(strHrs);
size_t found2 = str.findFirstOf(_seperator);
if (found2 > 0 && found2 != Common::String::npos) {
strSec = str.substr(found2 + 1, Common::String::npos);
sec = stringToNumber<uint32>(strSec);
strMin = str.substr(found1 + _seperator.size(), found2 - (2 * _seperator.size()));
min = stringToNumber<uint32>(strMin);
}
}
ms = 3600000 * hr + 60000 * min + 1000 * sec;
start(ms);
}
Common::String getTime() {
uint32 ms = _start + _timer.ticks();
uint32 x = ms / 1000;
uint32 seconds = x % 60;
x /= 60;
uint32 minutes = x % 60;
uint32 hours = x / 60;
Common::String timeStr = numberToString(hours);
timeStr += _seperator;
timeStr += numberToString(minutes);
timeStr += _seperator;
timeStr += numberToString(seconds);
return timeStr;
}
};
} // End of namespace Crab
#endif // CRAB_GAMECLOCK_H

145
engines/crab/GameParam.cpp Normal file
View File

@@ -0,0 +1,145 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/GameParam.h"
#include "crab/XMLDoc.h"
namespace Crab {
// Are we in debug mode or not?
bool GameDebug = false;
FilePaths::FilePaths() : _common("res/levels/common.xml"),
_modPath("mods/"),
_modExt(".unrmod"),
_modCur("res/default.xml"),
_mainmenuL("res/layout/main_menu_l.xml"),
_mainmenuR("res/layout/main_menu_r.xml"),
_soundEffect("res/sounds/effects.xml"),
_soundMusic("res/sounds/music.xml"),
_font("res/fonts/fonts.xml"),
_icon("res/gfx/icon.bmp"),
_saveDir("save/"),
_saveExt(".unr"),
_shaders("res/shaders/list.xml"),
_colors("res/gfx/colors.xml"),
_currentR("res/layout/main_menu_r.xml") {
_level.clear();
_loaded = false;
warning("FilePaths::FilePaths() setting appdata directory to game dir");
_appdata = "./";
}
void FilePaths::loadLevel(const Common::Path &filename) {
_level.clear();
XMLDoc lev_list(filename);
if (lev_list.ready()) {
rapidxml::xml_node<char> *node = lev_list.doc()->first_node("world");
for (rapidxml::xml_node<char> *n = node->first_node("loc"); n != nullptr; n = n->next_sibling("loc")) {
LevelPath l;
l.load(n);
Common::String id;
loadStr(id, "id", n);
_level[id] = Common::move(l);
}
}
}
void FilePaths::load(const Common::Path &filename) {
XMLDoc settings(filename);
if (settings.ready()) {
rapidxml::xml_node<char> *node = settings.doc()->first_node("paths");
if (nodeValid(node) && !_loaded) {
if (nodeValid("icon", node)) {
rapidxml::xml_node<char> *iconode = node->first_node("icon");
_icon = iconode->value();
}
if (nodeValid("common", node)) {
rapidxml::xml_node<char> *commonnode = node->first_node("common");
_common = commonnode->value();
}
if (nodeValid("font", node)) {
rapidxml::xml_node<char> *fontnode = node->first_node("font");
_font = fontnode->value();
}
if (nodeValid("shader", node)) {
rapidxml::xml_node<char> *shadnode = node->first_node("shader");
_shaders = shadnode->value();
}
if (nodeValid("color", node)) {
rapidxml::xml_node<char> *colnode = node->first_node("color");
_colors = colnode->value();
}
if (nodeValid("mod", node)) {
rapidxml::xml_node<char> *modnode = node->first_node("mod");
loadPath(_modPath, "path", modnode);
loadPath(_modCur, "cur", modnode);
loadStr(_modExt, "ext", modnode);
}
if (nodeValid("main_menu", node)) {
rapidxml::xml_node<char> *menunode = node->first_node("main_menu");
loadPath(_mainmenuL, "l", menunode);
loadPath(_mainmenuR, "r", menunode);
_currentR = _mainmenuR;
}
if (nodeValid("sound", node)) {
rapidxml::xml_node<char> *soundnode = node->first_node("sound");
loadPath(_soundEffect, "effect", soundnode);
loadPath(_soundMusic, "music", soundnode);
}
if (nodeValid("save", node)) {
rapidxml::xml_node<char> *savenode = node->first_node("save");
loadPath(_saveDir, "dir", savenode);
loadStr(_saveExt, "ext", savenode);
Common::String custom_path;
if (loadStr(custom_path, "custom", savenode)) {
warning("In FilePaths::load(), customPath : %s", custom_path.c_str());
}
}
_loaded = true;
}
}
}
} // End of namespace Crab

180
engines/crab/GameParam.h Normal file
View File

@@ -0,0 +1,180 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_GAMEPARAM_H
#define CRAB_GAMEPARAM_H
#include "common/hashmap.h"
#include "common/hash-str.h"
#include "common/list.h"
#include "common/rect.h"
#include "common/str.h"
#include "crab/loaders.h"
#include "crab/rapidxml/rapidxml.hpp"
namespace Crab {
// The index for all levels in the game
struct LevelPath {
// The file paths
Common::Path _layout, _asset;
// The name of the level
Common::String _name;
LevelPath() {}
void load(rapidxml::xml_node<char> *node) {
loadStr(_name, "name", node);
loadPath(_layout, "layout", node);
loadPath(_asset, "res", node);
}
};
// Stores all layout paths for the game
struct FilePaths {
// Resources common to all levels and states
Common::Path _common;
// Mod file location, current mod and their extension
Common::Path _modPath, _modCur;
Common::String _modExt;
// Main menu resources
Common::Path _mainmenuL, _mainmenuR;
// Sounds
Common::Path _soundEffect, _soundMusic;
// Fonts and window icon file
Common::Path _font, _icon;
// Save directory and extension
Common::Path _saveDir;
Common::String _saveExt;
// The location of the shader index file
Common::Path _shaders;
// The location of the color index file
Common::Path _colors;
// The list of levels in the game
Common::HashMap<Common::String, LevelPath> _level;
// The file path of the current resource file
Common::Path _currentR;
// The application data path (where saves and settings are stored)
Common::Path _appdata;
// Has this been loaded?
bool _loaded;
FilePaths();
void load(const Common::Path &filename);
void loadLevel(const Common::Path &filename);
};
// Storage pool used for saving numbers as strings
class StringPool {
// Store integer strings here
// std::unordered_map<int, Common::String> pool_i;
Common::HashMap<int, Common::String> poolI;
// Store floating point strings here
struct FloatString {
float _val;
Common::String _str;
FloatString() {
_val = 0.0f;
}
};
// std::list<FloatString> pool_f;
Common::List<FloatString> poolF;
public:
StringPool() {
poolI.clear();
poolF.clear();
}
const char *get(const int &num) {
if (poolI.contains(num) == false)
poolI[num] = numberToString<int>(num);
return poolI.getVal(num).c_str();
}
const char *fGet(const float &num) {
for (auto &i : poolF)
if (i._val == num)
return i._str.c_str();
FloatString fs;
fs._val = num;
fs._str = numberToString<float>(num);
poolF.push_back(fs);
auto ret = poolF.back();
return ret._str.c_str();
}
};
struct TempValue {
// Differences between normal mode and iron man mode
// save button - iron man saves in existing file
// load button - hidden in iron man
// exit - saves and exits in iron man
// quick save and quick load - operate on the iron man file in iron man mode
bool _ironman;
// This is the filename a player chose when selecting "new game" + iron man mode
Common::String _filename;
// We use this to see whether the player is exiting to main menu or to credits
bool _credits;
TempValue() : _filename("No IronMan") {
_ironman = false;
_credits = false;
}
};
// Our global objects
// Whether to draw debug outlines on polygons
extern bool GameDebug;
} // End of namespace Crab
#endif // CRAB_GAMEPARAM_H

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_LEVELRESULT_H
#define CRAB_LEVELRESULT_H
#include "common/str.h"
namespace Crab {
enum LevelResultType {
LR_NONE,
LR_LEVEL,
LR_GAMEOVER
};
struct LevelResult {
LevelResultType _type;
Common::String _val;
int _x, _y;
LevelResult() {
_type = LR_NONE;
_x = -1;
_y = -1;
}
};
} // End of namespace Crab
#endif // CRAB_LEVELRESULT_H

72
engines/crab/Line.h Normal file
View File

@@ -0,0 +1,72 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_LINE_H
#define CRAB_LINE_H
#include "crab/vectors.h"
namespace Crab {
// Find if 2 lines intersect and store the point of intersection
template<typename T>
bool collideLine(const T &p0X, const T &p0Y, const T &p1X, const T &p1Y,
const T &p2X, const T &p2Y, const T &p3X, const T &p3Y,
T *x = nullptr, T *y = nullptr) {
Vector2D<T> s1, s2;
s1.x = p1X - p0X;
s1.y = p1Y - p0Y;
s2.x = p3X - p2X;
s2.y = p3Y - p2Y;
float d = (-s2.x * s1.y + s1.x * s2.y);
if (d != 0) {
float s, t;
s = (-s1.y * (p0X - p2X) + s1.x * (p0Y - p2Y)) / d;
t = (s2.x * (p0Y - p2Y) - s2.y * (p0X - p2X)) / d;
if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
// Collision detected
if (x != nullptr)
*x = p0X + (t * s1.x);
if (y != nullptr)
*y = p0Y + (t * s1.y);
return true;
}
}
return false; // No collision
}
} // End of namespace Crab
#endif // CRAB_LINE_H

View File

@@ -0,0 +1,93 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "graphics/screen.h"
#include "crab/crab.h"
#include "crab/LoadingScreen.h"
#include "crab/XMLDoc.h"
namespace Crab {
void LoadingScreen::load() {
Common::Path filename("res/layout/loading.xml");
XMLDoc doc(filename);
if (doc.ready()) {
rapidxml::xml_node<char> *node = doc.doc()->first_node("loading");
if (nodeValid(node)) {
if (nodeValid("screens", node)) {
rapidxml::xml_node<char> *scrnode = node->first_node("screens");
for (auto n = scrnode->first_node("screen"); n != nullptr; n = n->next_sibling("screen"))
_screen.push_back(n);
}
if (nodeValid("text", node))
_text.load(node->first_node("text"), "img");
}
}
}
void LoadingScreen::draw() {
// Change to a random screen
change();
// Draw the current screen
if (_cur < _screen.size())
_screen[_cur].draw();
// Draw the loading text
_text.draw((g_engine->_screenSettings->_cur.w - _text.w()) / 2, (g_engine->_screenSettings->_cur.h - _text.h()) / 2);
// Update the screen
g_engine->_screen->update();
}
void LoadingScreen::dim() {
warning("STUB: LoadingScreen::dim()");
#if 0
// This is used when starting or loading a game from the main menu in order to dim the screen
// until an actual loading screen is drawn
SDL_SetRenderDrawBlendMode(gRenderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 200);
SDL_RenderFillRect(gRenderer, NULL);
// Update the screen
SDL_RenderPresent(gRenderer);
#endif
}
void LoadingScreen::quit() {
_text.deleteImage();
for (auto &i : _screen)
i.clear();
}
} // End of namespace Crab

View File

@@ -0,0 +1,84 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_LOADINGSCREEN_H
#define CRAB_LOADINGSCREEN_H
#include "crab/image/Image.h"
#include "crab/ScreenSettings.h"
#include "crab/timer.h"
#include "crab/vectors.h"
namespace Crab {
class LoadingScreen {
struct Screen {
// The background image
pyrodactyl::image::Image _bg;
Screen(rapidxml::xml_node<char> *node) {
_bg.load(node, "bg");
}
void clear() {
_bg.deleteImage();
}
void draw() {
_bg.draw((g_engine->_screenSettings->_cur.w - _bg.w()) / 2, (g_engine->_screenSettings->_cur.h - _bg.h()) / 2);
}
};
// The different loading screens
Common::Array<Screen> _screen;
// The current loading screen
uint _cur;
// The text image (says loading)
pyrodactyl::image::Image _text;
public:
LoadingScreen() { _cur = 0; };
~LoadingScreen(){};
void change() {
_cur = g_engine->getRandomNumber(_screen.size() - 1);
}
void load();
void draw();
void dim();
void quit();
};
} // End of namespace Crab
#endif // CRAB_LOADINGSCREEN_H

2
engines/crab/POTFILES Normal file
View File

@@ -0,0 +1,2 @@
engines/crab/metaengine.cpp
engines/crab/input/input.cpp

View File

@@ -0,0 +1,312 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "common/system.h"
#include "crab/crab.h"
#include "crab/PathfindingAgent.h"
namespace Crab {
// This keeps the PriorityQueue organized based on the cost of the paths.
static bool compareNodes(PlannerNode const *nodeA, PlannerNode const *nodeB) {
return nodeA->getFinalCost() > nodeB->getFinalCost();
}
PathfindingAgent::PathfindingAgent() : _nodeQueue(compareNodes) {
_grid = nullptr;
_destinationSet = false;
_destinationReachable = false;
_nodeBufferDistance = 1.0f;
_solutionFound = _noSolution = false;
_startTile = nullptr;
_goalTile = nullptr;
_clickedTile = nullptr;
}
PathfindingAgent::~PathfindingAgent() {
}
void PathfindingAgent::initialize(PathfindingGrid *g) {
_grid = g;
_nodeBufferDistance = _grid->getCellSize().x / 2.0f;
_nodeBufferDistance *= _nodeBufferDistance;
}
void PathfindingAgent::setDestination(Vector2i d, bool r) {
Vector2f iVec = Vector2f((float)d.x, (float)d.y);
setDestination(iVec, r);
}
void PathfindingAgent::setDestination(Vector2i d) {
setDestination(d, true);
}
void PathfindingAgent::setDestination(Vector2f d) {
setDestination(d, true);
}
void PathfindingAgent::setDestination(Vector2f d, bool r) {
if (_grid == nullptr)
return;
_destination = d;
// TODO: This could be optimized to cache the route somehow... (SZ)
reset();
_startTile = _grid->getNodeAtPoint(_position);
// m_pGoalTile = grid->GetNodeAtPoint(d);
// I am now tracking the goal node and the clicked tile separately to solve problems
// with hangups and trying to reach un-reachable destinations.
_clickedTile = _grid->getNodeAtPoint(d);
_goalTile = _grid->getNearestOpenNode(d, _position);
PlannerNode *startingNode = new PlannerNode();
startingNode->setLocation(_startTile);
startingNode->setHCost((_position - _destination).magnitude());
startingNode->setFinalCost((_position - _destination).magnitude());
startingNode->setGivenCost(0.0);
_nodeQueue.push(startingNode);
_createdList[_startTile] = (startingNode);
_destinationSet = true;
_solutionFound = _noSolution = false;
_destinationReachable = r;
}
void PathfindingAgent::update(uint32 timeslice) {
uint32 prevTime = g_system->getMillis();
uint32 timeLeft = timeslice;
double dTempCost;
if (_solutionFound) {
if (_vSolution.size() > 0) {
float distSqr = (_position - _vSolution.back()->getPosition()).magSqr();
if (distSqr < _nodeBufferDistance) { // Have to find the right deadzone buffer
_vSolution.pop_back();
}
}
if (_vSolution.size() > 0) {
_immediateDest = Vector2i(_vSolution.back()->getPosition().x, _vSolution.back()->getPosition().y);
} else {
if (_destinationReachable)
_immediateDest = Vector2i((int)_destination.x, (int)_destination.y);
else
_immediateDest = Vector2i((int)_position.x, (int)_position.y);
}
return;
}
// No nodes, no pathing.
if (_nodeQueue.empty()) {
return;
}
Common::StableMap<PathfindingGraphNode *, PlannerNode *>::iterator currentIter;
do {
PlannerNode *current = _nodeQueue.front();
_nodeQueue.pop();
if (current->getLocation() == _goalTile) { // We're done.
// m_vSolution = getSolution();
_vSolution = getPrunedSolution(nullptr);
_solutionFound = true;
return;
} else if (current->getLocation()->getMovementCost() > 0 && current->getLocation()->adjacentToNode(_clickedTile) && _clickedTile->getMovementCost() < 0) {
_vSolution = getPrunedSolution(current->getLocation());
_solutionFound = true;
return;
}
for (auto &i : current->_location->_neighborNodes) {
if (i->getMovementCost() > 0) {
// Compute the temp given cost
dTempCost = current->getGivenCost() + i->getMovementCost() * distExact(i, current->getLocation());
// If it's a duplicate...
currentIter = _createdList.find(i);
if (currentIter != _createdList.end()) {
if (dTempCost < currentIter->second->getGivenCost()) {
// If the current planner node has already been added, but the current path is cheaper,
// replace it.
_nodeQueue.remove(currentIter->second);
currentIter->second->setGivenCost(dTempCost);
currentIter->second->setFinalCost(
currentIter->second->getHCost() * 1.1 +
currentIter->second->getGivenCost());
currentIter->second->setParent(current);
_nodeQueue.push(currentIter->second);
}
} else { // Otherwise...
PlannerNode *successor = new PlannerNode();
successor->setLocation(i);
// Set the new heuristic (distance from node to the goal)
successor->setHCost(distExact(i, _goalTile));
successor->setGivenCost(dTempCost);
// Final cost is the distance to goal (scaled by 10%) plus the distance of the path.
successor->setFinalCost(successor->getHCost() * 1.1 + successor->getGivenCost());
successor->setParent(current);
_createdList[i] = (successor);
_nodeQueue.push(successor); // When the node is pushed onto the PriorityQueue it ends up beings sorted cheapest -> most expensive
}
}
}
// Update the time
if (timeslice != 0) {
timeLeft -= (g_system->getMillis() - prevTime);
prevTime = g_system->getMillis();
}
} while (!isDone() && ((int32)timeLeft >= 0 || timeslice == 0));
_noSolution = true; // You can't get there from here (SZ)
}
bool PathfindingAgent::isDone() const {
if (_nodeQueue.empty())
return true;
return false;
}
// Clear everything.
void PathfindingAgent::reset() {
for (auto &iter : _createdList)
delete iter.second;
_nodeQueue.clear();
_createdList.clear();
_vSolution.clear();
_solutionFound = false;
_goalTile = nullptr;
_startTile = nullptr;
}
void PathfindingAgent::shutdown() {
reset();
_grid = nullptr;
}
Common::Array<PathfindingGraphNode const *> const PathfindingAgent::getSolution(PathfindingGraphNode *destNode) const {
Common::Array<PathfindingGraphNode const *> temp;
PlannerNode *current = nullptr;
if (_createdList.find(_goalTile) != _createdList.end()) {
current = _createdList.find(_goalTile)->second;
} else if (destNode != nullptr) {
// If the dest node passed in is not null, that means we did not reach the goal but came close
// so we should start with that node instead when we are constructing our path
current = _createdList.find(destNode)->second;
}
// Iterate through the planner nodes to create a vector to return.
while (current) {
if (current->getLocation() != _startTile) {
// You don't have to path to the start
if (current->getLocation() != _startTile)
temp.push_back(current->getLocation());
}
current = current->getParent();
}
return temp;
}
Common::Array<PathfindingGraphNode const *> const PathfindingAgent::getPrunedSolution(PathfindingGraphNode *destNode) {
Common::Array<PathfindingGraphNode const *> temp = getSolution(destNode);
Common::Array<PathfindingGraphNode const *> returnVec = temp;
// Any node that is not adjacent to an obstacle or an obstacle corner can be removed.
for (int i = 0; (uint)i < temp.size(); ++i) {
if (!temp[i]->adjacentToObstacle()) {
if (i > 0 && (uint)i < temp.size() - 1) {
// This check to see if the node is a "corner" to an obstacle that should not be pruned
// to prevent hanging on corners.
Common::Array<PathfindingGraphNode *> corners = _grid->cornerCheck(temp[i - 1], temp[i + 1]);
if (corners.size() == 0) {
Common::Array<PathfindingGraphNode const *>::iterator theEnd = Common::remove(returnVec.begin(), returnVec.end(), temp[i]);
returnVec.erase(theEnd);
}
}
}
}
return returnVec;
}
double PathfindingAgent::distSquared(PathfindingGraphNode *tileA, PathfindingGraphNode *tileB) {
Vector2f vecTo = tileA->getPosition() - tileB->getPosition();
return vecTo.magSqr();
}
double PathfindingAgent::distExact(PathfindingGraphNode *tileA, PathfindingGraphNode *tileB) {
Vector2f vecTo = tileA->getPosition() - tileB->getPosition();
return vecTo.magnitude();
}
bool PathfindingAgent::adjacentToGoal(PathfindingGraphNode *node) {
for (const auto &iter : node->_neighborNodes) {
if (iter == _goalTile) {
return true;
}
}
return false;
}
} // End of namespace Crab

View File

@@ -0,0 +1,220 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_PATHFINDINGAGENT_H
#define CRAB_PATHFINDINGAGENT_H
#include "common/stablemap.h"
#include "crab/PathfindingGrid.h"
#include "crab/PriorityQueue.h"
#include "crab/vectors.h"
namespace Crab {
// This class represents the actual pathfinding and following agent that utilizes
// the pathfinding grid
class PlannerNode {
PlannerNode *_parent;
PlannerNode *_child;
double _cost; // Heuristic cost equivalent to cost to reach goal from planner node's position.
double _finalCost; // Final cost of route through the planner node. Used to determine optimal path.
double _givenCost; // The current distance of the route.
public:
PathfindingGraphNode *_location;
PlannerNode() {
_location = nullptr;
_parent = nullptr;
_child = nullptr;
_cost = 0;
_finalCost = 0;
_givenCost = 0;
}
~PlannerNode() {}
PathfindingGraphNode *getLocation() {
return _location;
}
PlannerNode *getParent() {
return _parent;
}
PlannerNode *getChild() {
return _child;
}
double getHCost() const {
return _cost;
}
double getFinalCost() const {
return _finalCost;
}
double getGivenCost() const {
return _givenCost;
}
void setLocation(PathfindingGraphNode *loc) {
_location = loc;
}
void setParent(PlannerNode *p) {
_parent = p;
}
void setChild(PlannerNode *c) {
_child = c;
}
void setHCost(double c) {
_cost = c;
}
void setFinalCost(double c) {
_finalCost = c;
}
void setGivenCost(double c) {
_givenCost = c;
}
};
class PathfindingAgent {
Vector2f _position;
Vector2f _prevPosition; // Used to determine that we are making progress toward the goal
Vector2i _immediateDest; // The next stop on the AI's path
bool _destinationSet; // Was a destination specified.
bool _destinationReachable; // Can the agent actually get to the destination?
float _nodeBufferDistance; // How much leeway is there for reaching the destination
public:
PathfindingAgent();
~PathfindingAgent();
PathfindingGrid *_grid;
Vector2f _destination;
PathfindingGraphNode *_startTile; // The system originally used tiles, but this one uses discreet points.
PathfindingGraphNode *_goalTile; // The tile we are trying to reach. May not be the tile that was clicked if the clicked tile is blocked.
PathfindingGraphNode *_clickedTile; // The tile that was clicked. If it is open, it will be the goal node.
bool _solutionFound;
bool _noSolution;
Common::Array<PathfindingGraphNode const *> _vSolution;
void setNodeBufferDistance(float w) {
_nodeBufferDistance = w;
}
float getNodeBufferDistance() {
return _nodeBufferDistance;
}
// Added for Greedy search
double distSquared(PathfindingGraphNode *tileA, PathfindingGraphNode *tileB);
// Added for A* search
double distExact(PathfindingGraphNode *tileA, PathfindingGraphNode *tileB);
PriorityQueue<PlannerNode> _nodeQueue;
Common::StableMap<PathfindingGraphNode *, PlannerNode *> _createdList;
// void SetSprite(pyrodactyl::anim::Sprite* s){entitySprite = s;}
//! \brief Sets the tile map.
//!
//! Invoked when the user opens a tile map file.
//!
//! \param _tileMap the data structure that this algorithm will use
//! to access each tile's location and weight data.
void initialize(PathfindingGrid *g);
void setDestination(Vector2f d);
void setDestination(Vector2f d, bool r);
void setDestination(Vector2i d);
void setDestination(Vector2i d, bool r);
void setPosition(Vector2f p) {
_position = p;
}
void setPrevPosition(Vector2f p) {
_prevPosition = p;
}
Vector2f getPosition() {
return _position;
}
bool positionChanged() {
return _position != _prevPosition;
}
Vector2i getImmediateDest() {
return _immediateDest;
}
//! \brief Performs the main part of the algorithm until the specified time has elapsed or
//! no nodes are left open.
void update(uint32 timeslice);
//! \brief Returns <code>true</code> if and only if no nodes are left open.
//!
//! \return <code>true</code> if no nodes are left open, <code>false</code> otherwise.
bool isDone() const;
//! \brief Returns an unmodifiable view of the solution path found by this algorithm.
Common::Array<PathfindingGraphNode const *> const getSolution(PathfindingGraphNode *destNode) const;
// Get the solution removing any nodes that are completely surrounded by open space.
// This will result in a more linear path to the goal.
Common::Array<PathfindingGraphNode const *> const getPrunedSolution(PathfindingGraphNode *destNode);
//! \brief Resets the algorithm.
void reset();
//! \brief Uninitializes the algorithm before the tile map is unloaded.
void shutdown();
// Returns true if the node connects to the goal node
bool adjacentToGoal(PathfindingGraphNode *node);
};
} // End of namespace Crab
#endif // CRAB_PATHFINDINGAGENT_H

View File

@@ -0,0 +1,94 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/PathfindingGraphNode.h"
namespace Crab {
PathfindingGraphNode::PathfindingGraphNode() {
_id = -1;
_movementCost = -1.0f;
}
PathfindingGraphNode::PathfindingGraphNode(Vector2f pos, int i) : _position(pos) {
_id = i;
_movementCost = -1.0f;
}
PathfindingGraphNode::~PathfindingGraphNode() {
}
void PathfindingGraphNode::addNeighbor(PathfindingGraphNode *node) {
addNeighbor(node, false);
}
void PathfindingGraphNode::addNeighbor(PathfindingGraphNode *node, bool ignoreDistance) {
// You can't be your own neighbor. Sorry.
if (node->_id == this->_id)
return;
// Make sure that the node is not already a neighbor (SZ)
for (uint i = 0; i < _neighborNodes.size(); ++i) {
if (_neighborNodes[i]->_id == node->_id) {
return;
}
}
_neighborNodes.push_back(node);
// Determine the cost.
if (ignoreDistance) {
_neighborCosts.push_back(node->_movementCost);
} else {
Vector2f distVec = node->_position - this->_position;
_neighborCosts.push_back(distVec.magnitude() * node->_movementCost);
}
}
bool PathfindingGraphNode::adjacentToObstacle() const {
for (const auto &iter : _neighborNodes) {
if (iter->getMovementCost() < 0)
return true;
}
return false;
}
bool PathfindingGraphNode::adjacentToNode(PathfindingGraphNode *otherNode) {
for (uint i = 0; i < _neighborNodes.size(); ++i) {
if (_neighborNodes[i] == otherNode)
return true;
}
return false;
}
} // End of namespace Crab

View File

@@ -0,0 +1,92 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_PATHFINDINGGRAPHNODE_H
#define CRAB_PATHFINDINGGRAPHNODE_H
#include "crab/Rectangle.h"
#include "crab/vectors.h"
namespace Crab {
// This is the basic pathfinding node that will construct the pathfinding graph. (SZ)
// Although Unrest is using a square grid based pathfinding map, this is made to be a general use pathfinding node.
class PathfindingGraphNode {
friend class PathfindingGrid;
int _id; // Each ID will be assigned when the pathfinding graph is generated and will identify each node.
float _movementCost; // 1 is open terrain, >1 is impeding terrain, <0 is completely obstructed
Vector2f _position; // Position of the node
Rect _collisionRect; // Represents spaced covered by the node.
public:
Common::Array<PathfindingGraphNode *> _neighborNodes;
Common::Array<float> _neighborCosts; // The movement cost for the neighbor nodes (distance to the node X the nodes movement cost)
// This is stored to prevent having to recalculate each frame.
PathfindingGraphNode();
PathfindingGraphNode(Vector2f pos, int i);
~PathfindingGraphNode();
float getMovementCost() {
return _movementCost;
}
Vector2f getPosition() const {
return _position;
}
// Adds node to neighbor vector and cost to neighbor costs
void addNeighbor(PathfindingGraphNode *node);
// Same as above, but does not calculate distance. Used when all nodes
// are equidistant
void addNeighbor(PathfindingGraphNode *node, bool ignoreDistance);
// const Common::Array< PathfindingGraphNode*>& GetNeighbors() const;
Rect getRect() const {
return _collisionRect;
}
// Return true if the node is adjacent to a blocked node
bool adjacentToObstacle() const;
// Return true if the node is adjacent to the otherNode
bool adjacentToNode(PathfindingGraphNode *otherNode);
};
} // End of namespace Crab
#endif // CRAB_PATHFINDINGGRAPHNODE_H

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/PathfindingGrid.h"
#include "crab/TMX/TMXMap.h"
namespace Crab {
using namespace TMX;
PathfindingGrid::PathfindingGrid() {
_blockedCost = BLOCKED;
_openCost = OPEN;
_stairsCost = STAIRS;
_nodes = nullptr;
_dimensions.x = 0;
_dimensions.y = 0;
_cellSize.x = 0.0;
_cellSize.y = 0.0;
}
PathfindingGrid::~PathfindingGrid() {
reset();
}
void PathfindingGrid::reset() {
for (int x = 0; x < _dimensions.x; ++x) {
delete[] _nodes[x];
}
delete[] _nodes;
_nodes = nullptr;
_dimensions.x = 0;
_dimensions.y = 0;
_cellSize.x = 0.0;
_cellSize.y = 0.0;
}
void PathfindingGrid::setupNodes(const TMX::TMXMap &map) {
// delete nodes if they exist
reset();
_dimensions.x = map._pathRows; // Logically, this is incorrect but it matches the format of cols and rows used elsewhere (SZ)
_dimensions.y = map._pathCols;
_cellSize.x = (float)map._pathSize.x;
_cellSize.y = (float)map._pathSize.y;
// Check to see if the costs have been loaded from the level file.
// If not, assign to defaults.
if (map._movementCosts._noWalk != 0) {
_blockedCost = map._movementCosts._noWalk;
}
if (map._movementCosts._open != 0) {
_openCost = map._movementCosts._open;
}
if (map._movementCosts._stairs != 0) {
_stairsCost = map._movementCosts._stairs;
}
_nodes = new PathfindingGraphNode *[_dimensions.x];
// Allocate some nodes!
// TODO: probably want to change this to a one chunk allocation...
for (int i = 0; i < _dimensions.x; ++i) {
_nodes[i] = new PathfindingGraphNode[_dimensions.y];
}
// Fill up those nodes!
int idCounter = 0;
Vector2f pos = Vector2f(0.0f, 0.0f);
Vector2f topLeftPos = pos;
// Initialize the nodes
for (int x = 0; x < _dimensions.x; ++x) {
for (int y = 0; y < _dimensions.y; ++y) {
// PathfindingGraphNode* newNode = new PathfindingGraphNode(pos, idCounter++);
// nodes[x][y] = *newNode;
_nodes[x][y]._collisionRect = Rect(pos.x, pos.y, _cellSize.x, _cellSize.y);
_nodes[x][y]._position.x = pos.x + _cellSize.x / 2.0f;
_nodes[x][y]._position.y = pos.y + _cellSize.y / 2.0f;
_nodes[x][y]._id = idCounter++;
_nodes[x][y]._movementCost = _openCost;
_nodes[x][y]._neighborCosts.reserve(4); // since its a square based grid, 4 is the greatest number of costs and nodes possible.
_nodes[x][y]._neighborNodes.reserve(4);
pos.y += _cellSize.y;
const Common::Array<Shape> &noWalk = map.areaNoWalk();
// Check if the square should count as blocked
for (const auto &i : noWalk) {
if (i.collide(_nodes[x][y]._collisionRect)._intersect) {
_nodes[x][y]._movementCost = (float)_blockedCost;
break;
}
}
// Check for stairs if the cell isn't blocked
if (_nodes[x][y]._movementCost >= 0.0f) {
const Common::Array<pyrodactyl::level::Stairs> &stairs = map.areaStairs();
for (const auto &i : stairs) {
if (i.collide(_nodes[x][y]._collisionRect)._intersect) {
_nodes[x][y]._movementCost = (float)_stairsCost;
break;
}
}
}
// More collision checks can be added for the node as long as it checks for the high cost objects first
// since the highest cost collider in any given tile would be used for the path cost. (SZ)
}
pos.x += _cellSize.x;
pos.y = topLeftPos.y;
}
// Connect the nodes
for (int x = 0; x < _dimensions.x; ++x) {
for (int y = 0; y < _dimensions.y; ++y) {
// Check horizontal
if (x < _dimensions.x - 1) {
connectNodes(&_nodes[x][y], &_nodes[x + 1][y]);
// Check diagonals
// This causes hangups since the collider has a greater width to take into account when traveling
// diagonally compared to horizontal or vertical. (SZ)
/*if( y < dimensions.y - 2)
{
ConnectNodes(&nodes[x][y], &nodes[x + 1][y + 1]);
nodes[x][y].neighborCosts[nodes[x][y].neighborCosts.size() - 1] *= 1.41f;
nodes[x + 1][y + 1].movementCost *= 1.41f;
}
if(y > 0)
{
ConnectNodes(&nodes[x][y], &nodes[x + 1][y - 1]);
nodes[x][y].neighborCosts[nodes[x][y].neighborCosts.size() - 1] *= 1.41f;
nodes[x + 1][y - 1].movementCost *= 1.41f;
}*/
}
// Check vertical
if (y < _dimensions.y - 1) {
connectNodes(&_nodes[x][y], &_nodes[x][y + 1]);
}
}
}
////Check for adjacencies
////This could be used if additional weight should be applied to nodes adjacent to blocked nodes.
// for(int x = 0; x < dimensions.x; ++x)
//{
// for(int y = 0; y < dimensions.y; ++y)
// {
// for(int i = 0; i < nodes[x][y].neighborNodes.size(); ++i)
// {
// if(nodes[x][y].neighborNodes[i]->movementCost == blockedCost)
// {
// nodes[x][y].movementCost *= 2.0f;
// break;
// }
// }
// }
// }
}
void PathfindingGrid::connectNodes(PathfindingGraphNode *node1, PathfindingGraphNode *node2) {
node1->addNeighbor(node2, true);
node2->addNeighbor(node1, true);
}
PathfindingGraphNode *PathfindingGrid::getNodeAtPoint(Vector2f point) {
int x = (int)floor(point.x / _cellSize.x);
int y = (int)floor(point.y / _cellSize.y);
return &_nodes[x][y];
}
Common::Array<PathfindingGraphNode *> PathfindingGrid::cornerCheck(const PathfindingGraphNode *node1, const PathfindingGraphNode *node2) {
Common::Array<PathfindingGraphNode *> returnNodes;
// Iterat through both nodes neighbors. If a blocked neighbor is found that is shared between the two,
// It is a corner to them.
for (auto iter : node1->_neighborNodes) {
for (auto iter2 : node2->_neighborNodes) {
if (iter == iter2 && iter->_movementCost < 0) {
if (returnNodes.size() == 0 || (*(Common::find(returnNodes.begin(), returnNodes.end(), iter))) == nullptr)
returnNodes.push_back(iter);
}
}
}
return returnNodes;
}
PathfindingGraphNode *PathfindingGrid::getNearestOpenNode(Vector2f nodePos, Vector2f comparePos) {
PathfindingGraphNode *startNode = getNodeAtPoint(nodePos);
if (startNode->getMovementCost() > 0) // If the clicked node is open, we're done!
return startNode;
PathfindingGraphNode *returnNode = nullptr;
float shortestDistance = 0.0f;
Common::List<PathfindingGraphNode *> checkNodes;
checkNodes.push_back(startNode);
Common::Array<PathfindingGraphNode *> allUsedNodes;
allUsedNodes.push_back(startNode);
// Iterate through the nodes, check if they are open then check their distance from the compare point.
while (!checkNodes.empty()) {
if (checkNodes.front()->getMovementCost() > 0) {
float distance = (comparePos - checkNodes.front()->getPosition()).magSqr();
if (shortestDistance == 0.0f || distance) { // If this is the new shortest distance, this becomes the new return.
shortestDistance = distance;
returnNode = checkNodes.front();
}
} else {
for (uint i = 0; i < checkNodes.front()->_neighborNodes.size(); ++i) {
// If the neighbor hasn't been checked yet, add it to the list to check.
if (Common::find(allUsedNodes.begin(), allUsedNodes.end(), checkNodes.front()->_neighborNodes[i]) == allUsedNodes.end()) {
allUsedNodes.push_back(checkNodes.front()->_neighborNodes[i]);
checkNodes.push_back(checkNodes.front()->_neighborNodes[i]);
}
}
}
if (returnNode != nullptr) // If a node has been found, we are done. We don't want to continue iterating through neighbors since it would take us further from the clicked node.
return returnNode;
checkNodes.pop_front();
}
return nullptr;
}
} // End of namespace Crab

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_PATHFINDINGGRID_H
#define CRAB_PATHFINDINGGRID_H
#include "crab/PathfindingGraphNode.h"
namespace Crab {
namespace TMX {
class TMXMap;
}
// This is the grid of pathfinding nodes that is formed for the level (SZ)
class PathfindingGrid {
friend class PathfindingGraphNode;
PathfindingGraphNode **_nodes; // 2D array of nodes (size is [dimensions.x][dimensions.y]
Vector2i _dimensions; // rows and columns of nodes.
Vector2f _cellSize; // size of a cell in width and height
// Neighbor node1 to node2.
void connectNodes(PathfindingGraphNode *node1, PathfindingGraphNode *node2);
public:
// these are the default graph node costs.
// they can be overwritten by values stored in the level's file.(SZ)
static const int BLOCKED = -1;
static const int OPEN = 1;
static const int STAIRS = 5;
// These are the actual data members used to assign costs. (SZ)
int _blockedCost;
int _openCost;
int _stairsCost;
PathfindingGrid();
~PathfindingGrid();
void reset();
void setupNodes(const TMX::TMXMap &map);
// Return the node at the given point (SZ)
PathfindingGraphNode *getNodeAtPoint(Vector2f point);
// Return the node at the given coordinates (SZ)
PathfindingGraphNode *getNodeAtCoords(int x, int y) {
return &_nodes[x][y];
}
Vector2i getDimensions() {
return _dimensions;
}
Vector2f getCellSize() {
return _cellSize;
}
// Returns the nearest open node to the compare spot, starting with the given nodePos
// and iterating through its neighbors. (SZ)
PathfindingGraphNode *getNearestOpenNode(Vector2f nodePos, Vector2f comparePos);
// Return true if two nodes share and adjacency to the same blocked node.
// Can be used to find corners that shouldn't be cut.
Common::Array<PathfindingGraphNode *> cornerCheck(const PathfindingGraphNode *node1, const PathfindingGraphNode *node2);
};
} // End of namespace Crab
#endif // CRAB_PATHFINDINGGRID_H

275
engines/crab/Polygon.cpp Normal file
View File

@@ -0,0 +1,275 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "graphics/screen.h"
#include "crab/crab.h"
#include "crab/Polygon.h"
namespace Crab {
// Calculate the distance between [minA, maxA] and [minB, maxB]
// The distance will be negative if the intervals overlap
float IntervalDistance(float minA, float maxA, float minB, float maxB) {
if (minA < minB)
return minB - maxA;
return minA - maxB;
}
void Polygon2D::addPoint(const Vector2f &ref, const Common::String &x, const Common::String &y, Vector2f &min, Vector2f &max) {
Vector2f p;
p.x = ref.x + stringToNumber<float>(x);
p.y = ref.y + stringToNumber<float>(y);
if (p.x < min.x)
min.x = p.x;
if (p.x > max.x)
max.x = p.x;
if (p.y < min.y)
min.y = p.y;
if (p.y > max.y)
max.y = p.y;
_point.push_back(p);
}
void Polygon2D::load(rapidxml::xml_node<char> *node, Rect &bounds) {
Vector2f ref;
ref.load(node);
// Converting a polygon to an axis aligned bounding box is easy - just record the minimum and maximum values of x and y
// for the vertices of the polygon, then minimum = top left corner, max - min = dimensions
Vector2f min(std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
Vector2f max(-std::numeric_limits<float>::max(), -std::numeric_limits<float>::max());
_point.clear();
rapidxml::xml_node<char> *polynode = node->first_node("polygon");
if (polynode != nullptr) {
Common::String points, x, y;
loadStr(points, "points", polynode);
bool comma = false;
for (const auto &i : points) {
if (i == ',')
comma = true;
else if (i == ' ') {
addPoint(ref, x, y, min, max);
comma = false;
x.clear();
y.clear();
} else if (comma)
y += i;
else
x += i;
}
addPoint(ref, x, y, min, max);
bounds.x = min.x;
bounds.y = min.y;
bounds.w = max.x - min.x;
bounds.h = max.y - min.y;
}
setEdge();
}
void Polygon2D::setEdge() {
_edge.clear();
Vector2f p1, p2, res;
for (uint i = 0; i < _point.size(); i++) {
p1 = _point[i];
if (i + 1 >= _point.size())
p2 = _point[0];
else
p2 = _point[i + 1];
res.x = p2.x - p1.x;
res.y = p2.y - p1.y;
_edge.push_back(res);
}
}
Vector2f Polygon2D::center() const {
Vector2f total;
for (uint i = 0; i < _point.size(); i++) {
total.x += _point[i].x;
total.y += _point[i].y;
}
Vector2f ret;
if (_point.size() > 0) {
ret.x = total.x / _point.size();
ret.y = total.y / _point.size();
}
return ret;
}
void Polygon2D::offset(const float &x, const float &y) {
for (auto &i : _point) {
i.x += x;
i.y += y;
}
}
void Polygon2D::project(const Vector2f &axis, float &min, float &max) const {
// To project a point on an axis use the dot product
float d = axis.dotProduct(_point[0]);
min = d;
max = d;
for (auto i = _point.begin(); i != _point.end(); ++i) {
d = i->dotProduct(axis);
if (d < min)
min = d;
else if (d > max)
max = d;
}
}
PolygonCollisionResult Polygon2D::collide(const Rect &rect) const {
Polygon2D polyB;
Vector2f p;
p.x = rect.x;
p.y = rect.y;
polyB._point.push_back(p);
p.x = rect.x + rect.w;
p.y = rect.y;
polyB._point.push_back(p);
p.x = rect.x + rect.w;
p.y = rect.y + rect.h;
polyB._point.push_back(p);
p.x = rect.x;
p.y = rect.y + rect.h;
polyB._point.push_back(p);
polyB.setEdge();
return collide(polyB);
}
PolygonCollisionResult Polygon2D::collide(const Polygon2D &polyB) const {
PolygonCollisionResult result;
result._intersect = true;
int edgeCountA = _edge.size();
int edgeCountB = polyB._edge.size();
float minIntervalDistance = std::numeric_limits<float>::max();
Vector2f translationAxis;
Vector2f e;
// Loop through all the edges of both polygons
for (int edgeIndex = 0; edgeIndex < edgeCountA + edgeCountB; edgeIndex++) {
if (edgeIndex < edgeCountA)
e = _edge[edgeIndex];
else
e = polyB._edge[edgeIndex - edgeCountA];
// ===== 1. Find if the Polygon2Ds are currently intersecting =====
// Find the axis perpendicular to the current edge
Vector2f axis(-e.y, e.x);
axis.normalize();
// Find the projection of the Polygon2D on the current axis
float minA = 0;
float minB = 0;
float maxA = 0;
float maxB = 0;
project(axis, minA, maxA);
polyB.project(axis, minB, maxB);
// Check if the Polygon2D projections are currently intersecting
float intervalDistance = IntervalDistance(minA, maxA, minB, maxB);
if (intervalDistance > 0) {
// If the Polygon2Ds are not intersecting and won't intersect, exit the loop
result._intersect = false;
break;
}
// Check if the current interval distance is the minimum one. If so store
// the interval distance and the current distance.
// This will be used to calculate the minimum translation vector
intervalDistance = abs(intervalDistance);
if (intervalDistance < minIntervalDistance) {
minIntervalDistance = intervalDistance;
translationAxis = axis;
Vector2f d, ca, cb;
ca = center();
cb = polyB.center();
d.x = ca.x - cb.x;
d.y = ca.y - cb.y;
if (d.dotProduct(translationAxis) < 0) {
translationAxis.x = -translationAxis.x;
translationAxis.y = -translationAxis.y;
}
}
}
// The minimum translation vector can be used to push the Polygon2Ds apart.
// First moves the Polygon2Ds by their velocity
// then move polyA by MinimumTranslationVector.
if (result._intersect) {
result._mtv.x = translationAxis.x * minIntervalDistance;
result._mtv.y = translationAxis.y * minIntervalDistance;
}
return result;
}
bool Polygon2D::contains(const float &x, const float &y) {
bool result = false;
for (uint i = 0, j = _point.size() - 1; i < _point.size(); j = i++) {
if (((_point[i].y > y) != (_point[j].y > y)) &&
(x < (_point[j].x - _point[i].x) * (y - _point[i].y) / (_point[j].y - _point[i].y) + _point[i].x))
result = !result;
}
return result;
}
void Polygon2D::draw(const int &xOffset, const int &yOffset, const uint8 &r, const uint8 &g, const uint8 &b, const uint8 &a) {
Vector2f p1, p2;
for (uint i = 0; i < _point.size(); i++) {
p1 = _point[i];
if (i + 1 >= _point.size())
p2 = _point[0];
else
p2 = _point[i + 1];
g_engine->_screen->drawLine(p1.x + xOffset, p1.y + yOffset, p2.x + xOffset, p2.y + yOffset, g_engine->_format.ARGBToColor(a, r, g, b));
}
}
} // End of namespace Crab

103
engines/crab/Polygon.h Normal file
View File

@@ -0,0 +1,103 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_POLYGON_H
#define CRAB_POLYGON_H
#include "crab/Rectangle.h"
#include "crab/vectors.h"
namespace Crab {
//------------------------------------------------------------------------
// Purpose: The result of a collision
//------------------------------------------------------------------------
struct PolygonCollisionResult {
// Are the Polygons currently intersecting?
bool _intersect;
// The translation to apply to polygon A to push the polygons apart
// Also known as the minimum translation vector
Vector2f _mtv;
PolygonCollisionResult() {
_intersect = false;
}
};
//------------------------------------------------------------------------
// Purpose: A simple 2D Polygon class
//------------------------------------------------------------------------
class Polygon2D {
void addPoint(const Vector2f &ref, const Common::String &x, const Common::String &y, Vector2f &min, Vector2f &max);
public:
// A list of all points
Common::Array<Vector2f> _point;
// A list of all edges
Common::Array<Vector2f> _edge;
Polygon2D() {}
Polygon2D(rapidxml::xml_node<char> *node, Rect &bounds) {
load(node, bounds);
}
// Returns the approximate axis aligned bounding box of the polygon
void load(rapidxml::xml_node<char> *node, Rect &bounds);
void setEdge();
Vector2f center() const;
void offset(const float &x, const float &y);
void offset(const Vector2f &v) {
offset(v.x, v.y);
}
// Check if Polygon2D A is going to collide with Polygon2D B for the given velocity
// PolyA is this polygon
PolygonCollisionResult collide(const Polygon2D &polyB) const;
// Code for collision with a rectangle
PolygonCollisionResult collide(const Rect &rect) const;
// Find if a point is inside this polygon
bool contains(const float &x, const float &y);
// Calculate the projection of a polygon on an axis and returns it as a [min, max] interval
void project(const Vector2f &axis, float &min, float &max) const;
void draw(const int &xOffset = 0, const int &yOffset = 0,
const uint8 &r = 0, const uint8 &g = 0, const uint8 &b = 0.0f, const uint8 &a = 255);
};
} // End of namespace Crab
#endif // CRAB_POLYGON_H

View File

@@ -0,0 +1,147 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_PRIORITYQUEUE_H
#define CRAB_PRIORITYQUEUE_H
namespace Crab {
//! \brief The open heap used by all cost-based search algorithms.
//!
//! This class template is basically a thin wrapper on top of both the <code>std::deque</code>
//! class template and the <a href="http://www.sgi.com/tech/stl/">STL</a> heap operations.
template<typename Node>
class PriorityQueue {
Common::Array<Node *> _open;
bool (*compare)(Node const *, Node const *);
public:
//! \brief Constructs a new <code>%PriorityQueue</code> that heap-sorts nodes
//! using the specified comparator.
explicit PriorityQueue(bool (*)(Node const *, Node const *));
//! \brief Returns <code>true</code> if the heap contains no nodes,
//! <code>false</code> otherwise.
bool empty() const;
//! \brief Removes all nodes from the heap.
//!
//! \post
//! - <code>empty()</code>
void clear();
//! \brief Returns the number of nodes currently in the heap.
size_t size() const;
//! \brief Pushes the specified node onto the heap.
//!
//! The heap will maintain the ordering of its nodes during this operation.
//!
//! \post
//! - <code>! empty()</code>
void push(Node *node);
//! \brief Returns the least costly node in the heap.
//!
//! \pre
//! - <code>! empty()</code>
Node *front() const;
//! \brief Removes the least costly node from the heap.
//!
//! The heap will maintain the ordering of its nodes during this operation.
//!
//! \pre
//! - <code>! empty()</code>
void pop();
//! \brief Removes all instances of the specified node from the heap.
void remove(Node const *node);
//! \brief Enumerates all nodes in the heap so far.
//!
//! \param sorted the container to which each node will be added.
//!
//! \pre
//! - There must be no <code>NULL</code> pointers in the heap.
//! \post
//! - All nodes should be sorted by this heap's comparator.
void enumerate(Common::Array<Node const *> &sorted) const;
};
template<typename Node>
PriorityQueue<Node>::PriorityQueue(bool (*c)(Node const *, Node const *)) : _open(), compare(c) {
}
template<typename Node>
bool PriorityQueue<Node>::empty() const {
return _open.empty();
}
template<typename Node>
void PriorityQueue<Node>::clear() {
_open.clear();
}
template<typename Node>
size_t PriorityQueue<Node>::size() const {
return _open.size();
}
template<typename Node>
void PriorityQueue<Node>::push(Node *node) {
_open.insert(Common::upperBound(_open.begin(), _open.end(), node, compare), node);
}
template<typename Node>
Node *PriorityQueue<Node>::front() const {
return _open.back();
}
template<typename Node>
void PriorityQueue<Node>::pop() {
_open.pop_back();
}
template<typename Node>
void PriorityQueue<Node>::remove(Node const *node) {
_open.erase(Common::remove(_open.begin(), _open.end(), node), _open.end());
}
template<typename Node>
void PriorityQueue<Node>::enumerate(Common::Array<Node const *> &sorted) const {
sorted.resize(_open.size());
Common::copy(_open.begin(), _open.end(), sorted.begin());
}
} // End of namespace Crab
#endif // CRAB_PRIORITYQUEUE_H

132
engines/crab/Rectangle.cpp Normal file
View File

@@ -0,0 +1,132 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "graphics/screen.h"
#include "crab/crab.h"
#include "crab/GameParam.h"
#include "crab/Rectangle.h"
namespace Crab {
bool Rect::load(rapidxml::xml_node<char> *node, const bool &echo, const Common::String &x_name, const Common::String &y_name,
const Common::String &w_name, const Common::String &h_name) {
return loadNum(x, x_name, node, echo) && loadNum(y, y_name, node, echo) && loadNum(w, w_name, node, echo) && loadNum(h, h_name, node, echo);
}
bool Rect::collide(Rect box) const {
if (box.x + box.w < x)
return false; // just checking if their
if (box.x > x + w)
return false; // bounding boxes even touch
if (box.y + box.h < y)
return false;
if (box.y > y + h)
return false;
return true; // bounding boxes intersect
}
void Rect::extend(Rect box) {
int left1 = x;
int right1 = x + w;
int top1 = y;
int bottom1 = y + h;
const int left2 = box.x;
const int right2 = box.x + box.w;
const int top2 = box.y;
const int bottom2 = box.y + box.h;
left1 = MIN(left1, left2);
right1 = MAX(right1, right2);
top1 = MIN(top1, top2);
bottom1 = MAX(bottom1, bottom2);
x = left1;
y = top1;
w = right1 - left1;
h = bottom1 - top1;
}
Direction Rect::resolveX(Rect collider) {
// Check left edge of collider
if (x < collider.x && collider.x < x + w)
return DIRECTION_RIGHT;
// Check right edge of collider
if (x < collider.x + collider.w && collider.x + collider.w < x + w)
return DIRECTION_LEFT;
return DIRECTION_NONE;
}
Direction Rect::resolveY(Rect collider) {
// Check top edge of collider
if (y < collider.y && collider.y < y + h)
return DIRECTION_DOWN;
// Check bottom edge of collider
if (y < collider.y + collider.h && collider.y + collider.h < y + h)
return DIRECTION_UP;
return DIRECTION_NONE;
}
void Rect::flip(const TextureFlipType &flip, const Vector2i &axis) {
if (flip == FLIP_NONE)
return;
if (flip == FLIP_X || flip == FLIP_XY)
x = 2 * axis.x - x - w;
if (flip == FLIP_Y || flip == FLIP_XY)
y = 2 * axis.y - y - h;
}
void Rect::draw(const int &xOffset, const int &yOffset, const uint8 &r, const uint8 &g, const uint8 &b, const uint8 &a) {
int X = x + xOffset, Y = y + yOffset;
uint32 col = g_engine->_format.ARGBToColor(a, r, g, b);
g_engine->_screen->drawLine(X, Y, X + w, Y, col);
g_engine->_screen->drawLine(X, Y, X, Y + h, col);
g_engine->_screen->drawLine(X + w, Y, X + w, Y + h, col);
g_engine->_screen->drawLine(X, Y + h, X + w, Y + h, col);
}
void Rect::saveState(rapidxml::xml_document<> &doc, rapidxml::xml_node<char> *root, const char *name) {
rapidxml::xml_node<char> *child = doc.allocate_node(rapidxml::node_element, name);
child->append_attribute(doc.allocate_attribute("x", g_engine->_stringPool->get(x)));
child->append_attribute(doc.allocate_attribute("y", g_engine->_stringPool->get(y)));
child->append_attribute(doc.allocate_attribute("w", g_engine->_stringPool->get(w)));
child->append_attribute(doc.allocate_attribute("h", g_engine->_stringPool->get(h)));
root->append_node(child);
}
} // End of namespace Crab

103
engines/crab/Rectangle.h Normal file
View File

@@ -0,0 +1,103 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_RECTANGLE_H
#define CRAB_RECTANGLE_H
#include "crab/Line.h"
#include "crab/vectors.h"
namespace Crab {
//------------------------------------------------------------------------
// Purpose: A simple rectangle class
//------------------------------------------------------------------------
struct Rect {
int x;
int y;
int w;
int h;
Rect(int X = 0, int Y = 0, int W = 0, int H = 0) {
x = X;
y = Y;
w = W;
h = H;
}
bool load(rapidxml::xml_node<char> *node, const bool &echo = true, const Common::String &x_name = "x",
const Common::String &y_name = "y", const Common::String &w_name = "w", const Common::String &h_name = "h");
// Is this rectangle colliding with another rectangle?
bool collide(Rect box) const;
// Extend this rectangle to fully contain another rectangle
void extend(Rect box);
// Resolving a collision means we need to correct the position of the target rectangle to just outside of the reference rectangle
// To do this, first we need to determine which edges of the rectangles are colliding
// Resolve a collision in the X plane
Direction resolveX(Rect collider);
// Resolve a collision in the Y plane
Direction resolveY(Rect collider);
// Does this rectangle contain a point?
bool contains(int x1, int y1) {
return (x1 > x && x1 < x + w && y1 > y && y1 < y + h);
}
bool contains(Vector2i v) {
return contains(v.x, v.y);
}
// Does this rectangle contain another rectangle?
bool contains(Rect box) {
return (x < box.x && x + w > box.x + box.w && y < box.y && y + h > box.y + box.h);
}
// Flip a rectangle with respect to an axis
void flip(const TextureFlipType &flip, const Vector2i &axis);
// Draw the borders of the rectangle
void draw(const int &xOffset = 0, const int &yOffset = 0, const uint8 &r = 0, const uint8 &g = 0, const uint8 &b = 0, const uint8 &a = 255);
// Check if a rectangle is the same as another
bool operator==(const Rect &r) { return r.x == x && r.y == y && r.w == w && r.h == h; }
// Save to file
void saveState(rapidxml::xml_document<> &doc, rapidxml::xml_node<char> *root, const char *name);
};
} // End of namespace Crab
#endif // CRAB_RECTANGLE_H

View File

@@ -0,0 +1,118 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "common/config-manager.h"
#include "common/system.h"
#include "crab/crab.h"
#include "crab/GameParam.h"
#include "crab/loaders.h"
#include "crab/ScreenSettings.h"
namespace Crab {
void ScreenSettings::load(rapidxml::xml_node<char> *node) {
loadNum(_cur.w, "w", node);
loadNum(_cur.h, "h", node);
loadNum(_fps, "fps", node);
loadNum(_gamma, "gamma", node);
loadNum(_textSpeed, "text_speed", node);
// The following values are read from ConfMan instead of the XML
//loadBool(_vsync, "vsync", node);
//loadBool(_fullscreen, "fullscreen", node);
loadBool(_border, "border", node);
loadBool(_saveOnExit, "save_on_exit", node);
//loadBool(_mouseTrap, "mouse_trap", node);
loadBool(_quality, "quality", node);
if (ConfMan.hasKey("mousetrap"))
_mouseTrap = ConfMan.getBool("mousetrap");
if (ConfMan.hasKey("fullscreen"))
_fullscreen = ConfMan.getBool("fullscreen");
if (ConfMan.hasKey("vsync"))
_vsync = ConfMan.getBool("vsync");
}
void ScreenSettings::internalEvents() {
if (g_system->hasFeature(OSystem::kFeatureFullscreenMode))
_fullscreen = g_system->getFeatureState(OSystem::kFeatureFullscreenMode);
if (g_system->hasFeature(OSystem::kFeatureVSync))
_vsync = g_system->getFeatureState(OSystem::kFeatureVSync);
}
void ScreenSettings::toggleFullScreen() {
if (g_system->hasFeature(OSystem::kFeatureFullscreenMode)) {
_fullscreen = !_fullscreen;
g_system->beginGFXTransaction();
g_system->setFeatureState(OSystem::kFeatureFullscreenMode, _fullscreen);
g_system->endGFXTransaction();
}
}
void ScreenSettings::toggleVsync() {
if (g_system->hasFeature(OSystem::kFeatureVSync)) {
_vsync = !_vsync;
g_system->beginGFXTransaction();
g_system->setFeatureState(OSystem::kFeatureVSync, _vsync);
g_system->endGFXTransaction();
}
}
void ScreenSettings::saveState() {
ConfMan.setBool("fullscreen", _fullscreen);
ConfMan.setBool("vsync", _vsync);
ConfMan.setBool("mousetrap", _mouseTrap);
#if 0
root->append_attribute(doc.allocate_attribute("version", g_engine->_stringPool->get(_version)));
rapidxml::xml_node<char> *child = doc.allocate_node(rapidxml::node_element, "screen");
child->append_attribute(doc.allocate_attribute("w", g_engine->_stringPool->get(_cur.w)));
child->append_attribute(doc.allocate_attribute("h", g_engine->_stringPool->get(_cur.h)));
child->append_attribute(doc.allocate_attribute("fps", g_engine->_stringPool->get(_fps)));
child->append_attribute(doc.allocate_attribute("gamma", g_engine->_stringPool->fGet(_gamma)));
child->append_attribute(doc.allocate_attribute("text_speed", g_engine->_stringPool->fGet(_textSpeed)));
saveBool(_vsync, "vsync", doc, child);
saveBool(_border, "border", doc, child);
saveBool(_fullscreen, "fullscreen", doc, child);
saveBool(_saveOnExit, "save_on_exit", doc, child);
saveBool(_quality, "quality", doc, child);
saveBool(_mouseTrap, "mouse_trap", doc, child);
root->append_node(child);
#endif
}
} // End of namespace Crab

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_SCREENSETTINGS_H
#define CRAB_SCREENSETTINGS_H
#include "crab/rapidxml/rapidxml.hpp"
namespace Crab {
struct Dimension {
int w, h;
Dimension() {
w = 1280;
h = 720;
}
bool operator<(const Dimension &d) { return (w < d.w && h < d.h); }
};
class ScreenAttributes {
public:
// Frames per second
int _fps;
// The current screen dimensions
Dimension _cur;
// The minimum dimension where we draw the image without scaling
// Any lower than this and we draw at the minimum resolution, then scale it down
// Dimension min;
// Is the window full-screen?
bool _fullscreen;
// Does the window have a border?
bool _border;
// Is vertical sync enabled?
bool _vsync;
// The brightness of a window
float _gamma;
// The video flags
uint32 _videoflags;
// should we save on exit?
bool _saveOnExit;
// This flag is true if we want to load high quality images, false otherwise
bool _quality;
// Is the mouse trapped within the window
bool _mouseTrap;
// The text speed (used only for popup text)
float _textSpeed;
ScreenAttributes() {
_fps = 60;
_fullscreen = false;
_border = true;
_vsync = true;
_saveOnExit = true;
_quality = true;
_mouseTrap = false;
_gamma = 1.0f;
_textSpeed = 1.0f;
_videoflags = 0; //SDL_WINDOW_SHOWN;
}
};
// Screen attributes
class ScreenSettings : public ScreenAttributes {
public:
// The desktop dimensions
Dimension _desktop;
// True if we are in game, false otherwise
bool _inGame;
// Set to true when we have to call setUI() for rearranging UI after a resolution change
bool _changeInterface;
// The version of the settings
uint _version;
ScreenSettings() {
_inGame = false;
_version = 0;
_changeInterface = false;
}
~ScreenSettings() {}
bool validDimension(Dimension d) {
return d.w <= _desktop.w && d.h <= _desktop.h;
}
void load(rapidxml::xml_node<char> *node);
void internalEvents();
void toggleFullScreen();
void toggleVsync();
void saveState();
};
}
#endif // CRAB_SCREENSETTINGS_H

85
engines/crab/Shape.cpp Normal file
View File

@@ -0,0 +1,85 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/Shape.h"
namespace Crab {
void Shape::load(rapidxml::xml_node<char> *node, const bool &echo) {
if (nodeValid("polygon", node, echo)) {
_type = SHAPE_POLYGON;
_poly.load(node, _rect);
} else {
_rect.load(node, echo, "x", "y", "width", "height");
if (nodeValid("ellipse", node, echo))
_type = SHAPE_ELLIPSE;
else
_type = SHAPE_RECT;
}
}
CollisionData Shape::collide(Rect box) const {
CollisionData res;
res._intersect = _rect.collide(box);
if (res._intersect) {
res._type = _type;
if (_type == SHAPE_POLYGON) {
PolygonCollisionResult pcr = _poly.collide(box);
res._intersect = pcr._intersect;
res._data.x = pcr._mtv.x;
res._data.y = pcr._mtv.y;
return res;
} else
res._data = _rect;
}
return res;
}
bool Shape::contains(const Vector2i &pos) {
if (_rect.contains(pos)) {
if (_type == SHAPE_POLYGON)
return _poly.contains(pos.x, pos.y);
else
return true;
}
return false;
}
void Shape::draw(const int &xOffset, const int &yOffset, const uint8 &r, const uint8 &g, const uint8 &b, const uint8 &a) {
if (_type == SHAPE_POLYGON)
_poly.draw(xOffset, yOffset, r, g, b, a);
else
_rect.draw(xOffset, yOffset, r, g, b, a);
}
} // End of namespace Crab

91
engines/crab/Shape.h Normal file
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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_SHAPE_H
#define CRAB_SHAPE_H
#include "crab/Polygon.h"
#include "crab/vectors.h"
namespace Crab {
// The kind of shape
enum ShapeType {
SHAPE_RECT,
SHAPE_POLYGON,
SHAPE_ELLIPSE
};
struct CollisionData {
// Store the type of shape
ShapeType _type;
// Do the two shapes intersect?
bool _intersect;
// If Shape is Polygon, the .x and .y of this rectangle contain the minimum translation vector
// If Shape is Rectangle, this contains the colliding rectangle
Rect _data;
CollisionData() {
_type = SHAPE_RECT;
_intersect = false;
}
};
class Shape {
public:
// The type of shape
ShapeType _type;
// This stores both the ellipse and rectangle data
Rect _rect;
// This stores the polygon data
Polygon2D _poly;
Shape() {
_type = SHAPE_RECT;
}
~Shape() {}
void load(rapidxml::xml_node<char> *node, const bool &echo = true);
CollisionData collide(Rect box) const;
bool contains(const Vector2i &pos);
void draw(const int &xOffset = 0, const int &yOffset = 0,
const uint8 &r = 0, const uint8 &g = 0, const uint8 &b = 0, const uint8 &a = 255);
};
} // End of namespace Crab
#endif // CRAB_SHAPE_H

View File

@@ -0,0 +1,107 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/TMX/TMXLayer.h"
namespace Crab {
using namespace TMX;
bool Layer::load(rapidxml::xml_node<char> *node) {
if (nodeValid(node))
return loadStr(_name, "name", node) && loadNum(_w, "width", node) && loadNum(_h, "height", node);
return false;
}
bool MapLayer::load(const Common::Path &path, rapidxml::xml_node<char> *node) {
if (Layer::load(node)) {
if (nodeValid("image", node, false)) {
_type = LAYER_IMAGE;
rapidxml::xml_node<char> *imgnode = node->first_node("image");
if (imgnode->first_attribute("source") != nullptr)
_img.load(path.append(imgnode->first_attribute("source")->value()));
} else {
_type = LAYER_NORMAL;
int i = 0;
Common::Array<TileInfo> t;
//.tmx stores tiles row-first
for (auto n = node->first_node("data")->first_node("tile"); n != nullptr; n = n->next_sibling("tile")) {
t.push_back(n);
if (++i >= _w) {
_tile.push_back(t);
t.clear();
i = 0;
}
}
}
// load properties associated with the layer, such as:
// Is it a prop layer or not? If yes, the prop dimensions
// The rate of scrolling of the layer, used for parallax scrolling
if (nodeValid("properties", node, false)) {
Common::String n, v;
for (auto p = node->first_node("properties")->first_node("property"); p != nullptr; p = p->next_sibling("property")) {
if (loadStr(n, "name", p) && loadStr(v, "value", p)) {
if (n == "prop" && v == "true")
_type = LAYER_PROP;
else if (n == "autohide" && v == "true")
_type = LAYER_AUTOHIDE;
else if (n == "autoshow" && v == "true")
_type = LAYER_AUTOSHOW;
else if (n == "x") {
_pos.x = stringToNumber<int>(v);
} else if (n == "y") {
_pos.y = stringToNumber<int>(v);
} else if (n == "w") {
_pos.w = stringToNumber<int>(v);
} else if (n == "h") {
_pos.h = stringToNumber<int>(v);
} else if (n == "scroll_rate_x") {
_rate.x = stringToNumber<float>(v);
_type = LAYER_PARALLAX;
} else if (n == "scroll_rate_y") {
_rate.y = stringToNumber<float>(v);
_type = LAYER_PARALLAX;
}
}
}
}
return true;
}
return false;
}
} // End of namespace Crab

115
engines/crab/TMX/TMXLayer.h Normal file
View File

@@ -0,0 +1,115 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_TMXLAYER_H
#define CRAB_TMXLAYER_H
#include "crab/Rectangle.h"
#include "crab/image/Image.h"
#include "crab/TMX/TileInfo.h"
#include "common/list.h"
namespace Crab {
namespace TMX {
enum LayerType {
// Run of the mill layer full of tiles
LAYER_NORMAL,
// Layer containing a simple image instead of tiles
LAYER_IMAGE,
// We do not draw prop layers along with the non-prop layers,
// because their draw order depends on the positions of the sprites
LAYER_PROP,
// Parallax layers are drawn differently, but are still composed of tiles
LAYER_PARALLAX,
// Auto-hide layers are primarily used for interiors
// These are only visible if the player is colliding with them
LAYER_AUTOHIDE,
// Auto-show layers are the reverse of auto-hide layers
// These are only visible if the player is not colliding with them
LAYER_AUTOSHOW
};
class Layer {
public:
// Name of the layer
Common::String _name;
// Dimensions of the layer in terms of tiles
int _w, _h;
Layer() {
_w = 0;
_h = 0;
}
bool load(rapidxml::xml_node<char> *node);
};
// Currently we just use one general purpose layer object instead of multiple inherited classes and stuff
class MapLayer : public Layer {
public:
// The tiles in the layer
Common::Array<Common::Array<TileInfo>> _tile;
// The type of layer
LayerType _type;
// The image in the layer
pyrodactyl::image::Image _img;
// The coordinates to draw the prop in(x,y) and dimensions of the area(w,h) in terms of tiles
// This is also the collision rectangle of the prop and auto hide layer
Rect _pos;
Common::List<Rect> _boundRect;
// The rate of scrolling of image, used for parallax
Vector2f _rate;
// Is the player colliding with the layer? (used for auto hide layer)
bool _collide;
MapLayer() : _rate(1, 1) {
_type = LAYER_NORMAL;
_collide = false;
}
bool load(const Common::Path &path, rapidxml::xml_node<char> *node);
};
} // End of namespace TMX
} // End of namespace Crab
#endif // CRAB_TMXLAYER_H

374
engines/crab/TMX/TMXMap.cpp Normal file
View File

@@ -0,0 +1,374 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/image/ImageManager.h"
#include "crab/text/TextManager.h"
#include "crab/TMX/TMXMap.h"
namespace Crab {
using namespace TMX;
using namespace pyrodactyl::image;
static bool propCompare(const MapLayer &l1, const MapLayer &l2) {
return l1._pos.y + l1._pos.h < l2._pos.y + l2._pos.h;
}
TMXMap::TMXMap() {
_tileRows = 0;
_tileCols = 0;
_pathRows = 0;
_pathCols = 0;
_w = 0;
_h = 0;
_spriteLayer = 0;
_grid = nullptr;
_movementCosts._noWalk = 0;
_movementCosts._open = 0;
_movementCosts._stairs = 0;
}
//------------------------------------------------------------------------
// Purpose: Load stuff via a .tmx file set to xml storage (no compression)
//------------------------------------------------------------------------
void TMXMap::load(const Common::Path &path, const Common::String &filename) {
XMLDoc conf(path.appendComponent(filename));
if (conf.ready()) {
rapidxml::xml_node<char> *node = conf.doc()->first_node("map");
if (nodeValid(node)) {
loadNum(_tileRows, "width", node);
loadNum(_tileCols, "height", node);
loadNum(_tileSize.x, "tilewidth", node);
loadNum(_tileSize.y, "tileheight", node);
// Pathfinding info load
_pathSize.x = 0;
_pathSize.y = 0;
loadNum(_pathSize.x, "pathwidth", node);
loadNum(_pathSize.y, "pathheight", node);
// Load costs...Not sure if this is right. (SZ)
loadNum(_movementCosts._open, "opencost", node);
loadNum(_movementCosts._noWalk, "nowalkcost", node);
loadNum(_movementCosts._stairs, "stairscost", node);
// if(path_size.x == 0)
// path_size.x = tile_size.x;
// if(path_size.y == 0)
// path_size.y = tile_size.y;
// Testing
if (_pathSize.x == 0)
_pathSize.x = 40;
if (_pathSize.y == 0)
_pathSize.y = 40;
_w = _tileRows * _tileSize.x;
_h = _tileCols * _tileSize.y;
_pathRows = (int)ceil((float)_w / (float)_pathSize.x + .5f); // Adding .5 before casting in order to round up (SZ)
_pathCols = (int)ceil((float)_h / (float)_pathSize.y + .5f);
g_engine->_imageManager->_tileset.load(path, node);
// Reset the layer at which sprites are drawn
_spriteLayer = 0;
uint layerCount = 0;
// We need to cycle through all tile and object layers in order to
// see the level at which the sprites will be drawn
for (auto groupnode = node->first_node(); groupnode != nullptr; groupnode = groupnode->next_sibling()) {
// Store the name for easy comparison
Common::String name = groupnode->name();
if (name == "layer" || name == "imagelayer") { // Is this a tile or an image layer
MapLayer l;
l.load(path, groupnode);
l._pos.x *= _tileSize.x;
l._pos.y *= _tileSize.y;
l._pos.w *= _tileSize.x;
l._pos.h *= _tileSize.y;
if (l._type == LAYER_PROP)
_prop.push_back(l);
else
_layer.push_back(l);
layerCount++;
} else if (name == "objectgroup") { // Is this an object layer
Common::String groupName;
loadStr(groupName, "name", groupnode);
if (groupName == "exit") {
for (auto n = groupnode->first_node("object"); n != nullptr; n = n->next_sibling("object")) {
pyrodactyl::level::Exit le(n);
_areaExit.push_back(le);
}
} else if (groupName == "walk") {
auto n = groupnode->first_node("object");
if (n != nullptr)
_areaWalk.load(n, true, "x", "y", "width", "height");
} else if (groupName == "no_walk") {
for (auto n = groupnode->first_node("object"); n != nullptr; n = n->next_sibling("object")) {
Shape s;
s.load(n);
_areaNowalk.push_back(s);
}
} else if (groupName == "trigger") {
for (auto n = groupnode->first_node("object"); n != nullptr; n = n->next_sibling("object")) {
Shape s;
s.load(n);
uint pos = _areaTrig.size();
loadNum(pos, "name", n);
if (_areaTrig.size() <= pos)
_areaTrig.resize(pos + 1);
_areaTrig[pos] = s;
}
} else if (groupName == "stairs") {
for (auto n = groupnode->first_node("object"); n != nullptr; n = n->next_sibling("object")) {
pyrodactyl::level::Stairs s;
s.load(n);
_areaStairs.push_back(s);
}
} else if (groupName == "music") {
for (auto n = groupnode->first_node("object"); n != nullptr; n = n->next_sibling("object")) {
pyrodactyl::level::MusicArea ma;
ma.load(n);
_areaMusic.push_back(ma);
}
} else if (groupName == "sprites")
_spriteLayer = layerCount;
}
}
// Sort the props in the level according to y axis
Common::sort(_prop.begin(), _prop.end(), propCompare);
}
}
}
//------------------------------------------------------------------------
// Purpose: Use this if you want to load a poly line from tmx
//------------------------------------------------------------------------
// void TMXMap::LoadPath(rapidxml::xml_node<char> *node)
//{
// int pos = 0;
// for (auto n = node->first_node("object"); n != nullptr; n = n->next_sibling("object"), ++pos)
// {
// Vector2i start;
// start.load(n);
//
// rapidxml::xml_node<char> *linenode = n->first_node("polyline");
// if (linenode != nullptr)
// {
// Common::String points, x, y;
// loadStr(points, "points", linenode);
//
// path.resize(pos + 1);
// bool comma = false;
// for (auto i = points.begin(); i != points.end(); ++i)
// {
// if (*i == ',') comma = true;
// else if (*i == ' ')
// {
// path[pos].push_back(GetPoint(start, x, y));
// comma = false;
// x.clear();
// y.clear();
// }
// else if (comma) y.push_back(*i);
// else x.push_back(*i);
// }
// path[pos].push_back(GetPoint(start, x, y));
// }
// }
//}
//------------------------------------------------------------------------
// Purpose: Convert point from string to vector
//------------------------------------------------------------------------
// const Vector2i TMXMap::GetPoint(const Vector2i &ref, Common::String &x, Common::String &y)
//{
// Vector2i v;
// v.x = ref.x + StringToNumber<int>(x);
// v.y = ref.y + StringToNumber<int>(y);
// return v;
//}
//------------------------------------------------------------------------
// Purpose: Clear all data from the level
//------------------------------------------------------------------------
void TMXMap::reset() {
g_engine->_imageManager->_tileset.reset();
_layer.clear();
_areaNowalk.clear();
_areaExit.clear();
_areaTrig.clear();
_areaStairs.clear();
_areaMusic.clear();
_prop.clear();
_spriteLayer = 0;
}
//------------------------------------------------------------------------
// Purpose: Draw functions
//------------------------------------------------------------------------
void TMXMap::drawDebug(const Rect &camera) {
using namespace pyrodactyl::text;
for (auto &i : _areaTrig)
i.draw(-camera.x, -camera.y, 0, 0, 254, 254);
for (auto &i : _areaExit)
i._dim.draw(-camera.x, -camera.y, 0, 254, 254, 254);
for (auto &i : _prop)
i._pos.draw(-camera.x, -camera.y, 254, 0, 254, 254);
for (auto &i : _areaNowalk)
i.draw(-camera.x, -camera.y, 254, 0, 0, 254);
for (auto &i : _areaMusic)
i.draw(-camera.x, -camera.y, 254, 254, 0, 254);
for (auto &i : _areaStairs) {
i.draw(-camera.x, -camera.y, 0, 254, 0, 254);
}
// Draw the pathfinding grid (SZ)
for (int x = 0; x < _grid->getDimensions().x; ++x) {
for (int y = 0; y < _grid->getDimensions().y; ++y) {
if (_grid->getNodeAtCoords(x, y)->getMovementCost() < 0.0f)
_grid->getNodeAtCoords(x, y)->getRect().draw(-camera.x, -camera.y, 0, 0, 0, 254);
}
}
for (auto &i : _layer)
i._pos.draw(-camera.x, -camera.y, 254, 216, 0);
_areaWalk.draw(-camera.x, -camera.y, 254, 254, 254, 254);
}
//------------------------------------------------------------------------
// Purpose: Collision functions
//------------------------------------------------------------------------
void TMXMap::collideWithNoWalk(const Rect boundingBox, Common::List<CollisionData> &colliders) {
CollisionData res;
for (auto &i : _areaNowalk) {
res = i.collide(boundingBox);
if (res._intersect)
colliders.push_back(res);
}
}
bool TMXMap::insideNoWalk(const Vector2i &pos) {
for (auto &i : _areaNowalk)
if (i.contains(pos))
return true;
return false;
}
bool TMXMap::insideWalk(const Rect &boundingBox) {
if (_areaWalk.contains(boundingBox))
return true;
return false;
}
bool TMXMap::insideWalk(const Vector2i &pos) {
if (_areaWalk.contains(pos))
return true;
return false;
}
bool TMXMap::collideWithTrigger(const Rect rect, int index) {
if (_areaTrig.size() > (uint)index)
return _areaTrig[index].collide(rect)._intersect;
return false;
}
void TMXMap::collideWithTrigger(const Rect rect, Common::Array<int> &collisionTable) {
int index = 0;
collisionTable.clear();
for (auto i = _areaTrig.begin(); i != _areaTrig.end(); ++i, ++index)
if (i->collide(rect)._intersect)
collisionTable.push_back(index);
}
bool TMXMap::collideWithExit(const Rect rect, LevelResult &res) {
for (const auto &i : _areaExit)
if (i._dim.collide(rect)._intersect) {
res._val = i._name;
res._x = i._entry.x;
res._y = i._entry.y;
return true;
}
return false;
}
bool TMXMap::collideWithStairs(const Rect rect, Vector2f &velMod) {
for (const auto &i : _areaStairs) {
if (i.collide(rect)._intersect) {
velMod = i._modifier;
return true;
}
}
// We are not colliding with any stairs, reset the modifier
velMod.x = 1.0f;
velMod.y = 1.0f;
return false;
}
bool TMXMap::collideWithMusic(const Rect rect, pyrodactyl::level::MusicInfo &music) {
for (const auto &i : _areaMusic) {
if (i.collide(rect)._intersect) {
music._id = i._id;
music._track = i._track;
music._loops = i._loops;
return true;
}
}
return false;
}
} // End of namespace Crab

148
engines/crab/TMX/TMXMap.h Normal file
View File

@@ -0,0 +1,148 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_TMXMAP_H
#define CRAB_TMXMAP_H
#include "crab/LevelResult.h"
#include "crab/Shape.h"
#include "crab/XMLDoc.h"
#include "crab/ai/spriteai.h"
#include "crab/level/LevelExit.h"
#include "crab/level/MusicArea.h"
#include "crab/level/Stairs.h"
#include "crab/TMX/TMXTileSet.h"
namespace Crab {
namespace TMX {
// For TMX version 1.0, orthogonal maps only
class TMXMap {
protected:
// The actual dimensions of the level
int _w, _h;
// The area you can move in
Rect _areaWalk;
// The non-walk able areas in the level
Common::Array<Shape> _areaNowalk;
// The trigger rectangles in the level
Common::Array<Shape> _areaTrig;
// Stairs modify the player walking speed
Common::Array<pyrodactyl::level::Stairs> _areaStairs;
// Music areas change the music if player collides with them
Common::Array<pyrodactyl::level::MusicArea> _areaMusic;
// Archived methods for loading poly lines in tiled
// void LoadPath(rapidxml::xml_node<char> *node);
// const Vector2i GetPoint(const Vector2i &ref, Common::String &x, Common::String &y);
public:
// The exits to different levels
Common::Array<pyrodactyl::level::Exit> _areaExit;
// The layer on top of which objects walk
uint _spriteLayer;
// Dimensions of the level in terms of tiles
int _tileRows, _tileCols;
// Dimensions of the level in terms of pathfinding grid cells (SZ)
int _pathRows, _pathCols;
// The width and height of tiles
Vector2i _tileSize;
// The width and height of pathfinding grid cells (SZ)
Vector2i _pathSize;
// The layers of tiles in the level
Common::Array<MapLayer> _layer;
// The props in the level
Common::Array<MapLayer> _prop;
PathfindingGrid *_grid; // The grid of graph nodes used for navigating.
// Movement costs
struct {
int _open, _noWalk, _stairs;
} _movementCosts;
TMXMap();
~TMXMap() {}
void reset();
void load(const Common::Path &path, const Common::String &filename);
void drawDebug(const Rect &camera);
bool insideWalk(const Rect &boundingBox);
bool insideWalk(const Vector2i &pos);
bool insideNoWalk(const Vector2i &pos);
void collideWithNoWalk(const Rect boundingBox, Common::List<CollisionData> &colliders);
bool collideWithExit(const Rect rect, LevelResult &res);
bool collideWithStairs(const Rect rect, Vector2f &velMod);
bool collideWithMusic(const Rect rect, pyrodactyl::level::MusicInfo &music);
bool collideWithTrigger(const Rect rect, int index);
void collideWithTrigger(const Rect rect, Common::Array<int> &collisionTable);
int w() {
return _w;
}
int h() {
return _h;
}
const Rect &areaWalk() const {
return _areaWalk;
}
const Common::Array<Shape> &areaNoWalk() const {
return _areaNowalk;
}
const Common::Array<pyrodactyl::level::Stairs> &areaStairs() const {
return _areaStairs;
}
};
} // End of namespace TMX
} // End of namespace Crab
#endif // CRAB_TMXMAP_H

View File

@@ -0,0 +1,270 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "graphics/screen.h"
#include "crab/crab.h"
#include "crab/text/TextManager.h"
#include "crab/TMX/TMXTileSet.h"
namespace Crab {
using namespace TMX;
void TileSet::load(const Common::Path &path, rapidxml::xml_node<char> *node) {
if (nodeValid(node)) {
loadNum(_firstGid, "firstgid", node);
loadStr(_name, "name", node);
loadNum(_tileW, "tilewidth", node);
loadNum(_tileH, "tileheight", node);
_clip.w = _tileW;
_clip.h = _tileH;
if (nodeValid("image", node)) {
rapidxml::xml_node<char> *imgnode = node->first_node("image");
Common::String filename;
loadStr(filename, "source", imgnode);
_loc = path.appendComponent(filename);
_img.load(_loc);
_totalRows = _img.h() / _tileH;
_totalCols = _img.w() / _tileW;
debugC(kDebugGraphics, "Total rows : %d Total cols: %d gid: %d", _totalRows, _totalCols, _firstGid);
}
}
// Prevent divide by zero errors later
if (_totalCols == 0)
_totalCols = 1;
}
void TileSetGroup::reset() {
for (auto &i : _tileset)
i._img.deleteImage();
_tileset.clear();
}
void TileSetGroup::load(const Common::Path &path, rapidxml::xml_node<char> *node) {
reset();
for (auto n = node->first_node("tileset"); n != nullptr; n = n->next_sibling("tileset")) {
TileSet t;
t.load(path, n);
_tileset.push_back(t);
}
}
void TileSet::draw(const Vector2i &pos, const TileInfo &tile) {
if (tile._gid != 0) {
_clip.x = ((tile._gid - _firstGid) % _totalCols) * _tileW;
_clip.y = ((tile._gid - _firstGid) / _totalCols) * _tileH;
_img.draw(pos.x, pos.y, &_clip, tile._flip);
}
}
void TileSet::preDraw(const Vector2i &pos, const TileInfo &tile, Graphics::ManagedSurface *surf) {
if (tile._gid != 0) {
_clip.x = ((tile._gid - _firstGid) % _totalCols) * _tileW;
_clip.y = ((tile._gid - _firstGid) / _totalCols) * _tileH;
_img.draw(pos.x, pos.y, &_clip, tile._flip, surf);
}
}
void TileSetGroup::preDraw(MapLayer &layer, const Vector2i &tileSize, Graphics::ManagedSurface *surf) {
if (layer._type == LAYER_IMAGE || layer._type == LAYER_AUTOSHOW)
return;
_start.x = 0;
_start.y = 0;
_finish.x = layer._tile.size();
_finish.y = layer._tile[0].size();
_v.x = _start.y * tileSize.x;
_v.y = _start.x * tileSize.y;
for (int x = _start.x; x < _finish.x; ++x) {
for (int y = _start.y; y < _finish.y; ++y) {
for (int i = _tileset.size() - 1; i >= 0; --i)
if (layer._tile[x][y]._gid >= _tileset[i]._firstGid) {
_tileset[i].preDraw(_v, layer._tile[x][y], surf);
layer._boundRect.push_back(Rect(_v.x, _v.y, tileSize.x, tileSize.y));
break;
}
_v.x += tileSize.x;
}
_v.x = _start.y * tileSize.x;
_v.y += tileSize.y;
}
Common::List<Rect>::iterator rOuter, rInner;
// Process the bound rect list to find any rects to merge
for (rOuter = layer._boundRect.begin(); rOuter != layer._boundRect.end(); ++rOuter) {
rInner = rOuter;
while (++rInner != layer._boundRect.end()) {
if ((*rOuter).collide(*rInner)) {
rOuter->extend(*rInner);
layer._boundRect.erase(rInner);
rInner = rOuter;
}
}
}
}
void TileSetGroup::forceDraw(MapLayer &layer, const Rect &camera, const Vector2i &tileSize, const Rect &playerPos) {
if (layer._type == LAYER_IMAGE)
return;
layer._collide = layer._pos.collide(playerPos);
// Normal and prop layers are drawn this way
// The row and column we start drawing at
_start.x = playerPos.y / tileSize.y;
_start.y = playerPos.x / tileSize.x;
if (_start.x < 0 || _start.y < 0)
return;
// The row and column we end drawing at
_finish.x = (playerPos.y + playerPos.h) / tileSize.y + 1;
_finish.y = (playerPos.x + playerPos.w) / tileSize.x + 1;
if (layer._type == LAYER_AUTOSHOW) {
if (layer._collide)
return;
_start.x = camera.y / tileSize.y;
_start.y = camera.x / tileSize.x;
//The row and column we end drawing at
_finish.x = (camera.y + camera.h) / tileSize.y + 1;
_finish.y = (camera.x + camera.w) / tileSize.x + 1;
}
if (_finish.x > (int)layer._tile.size())
_finish.x = layer._tile.size();
if (_finish.y > (int)layer._tile[0].size())
_finish.y = layer._tile[0].size();
_v.x = _start.y * tileSize.x - camera.x;
_v.y = _start.x * tileSize.y - camera.y;
for (int x = _start.x; x < _finish.x; ++x) {
for (int y = _start.y; y < _finish.y; ++y) {
for (int i = _tileset.size() - 1; i >= 0; --i)
if (layer._tile[x][y]._gid >= _tileset[i]._firstGid) {
_tileset[i].draw(_v, layer._tile[x][y]);
break;
}
_v.x += tileSize.x;
}
_v.x = _start.y * tileSize.x - camera.x;
_v.y += tileSize.y;
}
}
void TileSetGroup::draw(MapLayer &layer, const Rect &camera, const Vector2i &tileSize, const Rect &playerPos, pyrodactyl::image::Image &img) {
if (layer._type == LAYER_IMAGE)
layer._img.draw(-1.0f * camera.x * layer._rate.x, -1.0f * camera.y * layer._rate.y);
else if (layer._type == LAYER_PARALLAX) {
// The row and column we start drawing at
_start.x = 0;
_start.y = 0;
// The row and column we end drawing at
_finish.x = layer._tile.size() - 1;
_finish.y = layer._tile[0].size() - 1;
_v.x = (_start.y * tileSize.x - camera.x) * layer._rate.x;
_v.y = (_start.x * tileSize.y - camera.y) * layer._rate.y;
for (int x = _start.x; x < _finish.x; ++x) {
for (int y = _start.y; y < _finish.y; ++y) {
for (int i = _tileset.size() - 1; i >= 0; --i)
if (layer._tile[x][y]._gid >= _tileset[i]._firstGid) {
_tileset[i].draw(_v, layer._tile[x][y]);
break;
}
_v.x += tileSize.x;
}
_v.x = (_start.y * tileSize.x - camera.x) * layer._rate.x;
_v.y += tileSize.y;
}
} else {
layer._collide = layer._pos.collide(playerPos);
// If player is inside the layer bounds, draw normally - else skip drawing
if (layer._type == LAYER_AUTOHIDE && !layer._collide)
return;
// If the player is outside the layer bounds, draw normally - else skip drawing
if (layer._type == LAYER_AUTOSHOW && layer._collide)
return;
// Normal and prop layers are drawn this way
// The row and column we start drawing at
_start.x = camera.y / tileSize.y;
_start.y = camera.x / tileSize.x;
// The row and column we end drawing at
_finish.x = (camera.y + camera.h) / tileSize.y + 1;
_finish.y = (camera.x + camera.w) / tileSize.x + 1;
if (_finish.x > (int)layer._tile.size())
_finish.x = layer._tile.size();
if (_finish.y > (int)layer._tile[0].size())
_finish.y = layer._tile[0].size();
_v.x = camera.x;
_v.y = camera.y;
Vector2i end;
end.x = camera.x + g_engine->_screen->w;
end.y = camera.y + g_engine->_screen->h;
Rect clip(_v.x, _v.y, end.x - _v.x, end.y - _v.y);
img.fastDraw(0, 0, &clip);
}
}
} // End of namespace Crab

View File

@@ -0,0 +1,109 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_TMXTILESET_H
#define CRAB_TMXTILESET_H
#include "crab/image/Image.h"
#include "crab/TMX/TMXLayer.h"
namespace Crab {
namespace TMX {
struct TileSet {
// The name of the tileset
Common::String _name;
// The location of the tileset image on the disk
Common::Path _loc;
// The first gid of the tileset
GidFormat _firstGid;
// Dimensions of tiles
int _tileW, _tileH;
// Number of rows and columns of tiles
int _totalRows, _totalCols;
// The image used by the tileset
pyrodactyl::image::Image _img;
// Stuff used to store temporary data
// The rectangle used to store clip info
Rect _clip;
void init() {
_firstGid = 1;
_tileW = 1;
_tileH = 1;
_totalRows = 1;
_totalCols = 1;
}
TileSet() {
init();
}
TileSet(const Common::Path &path, rapidxml::xml_node<char> *node) {
init();
load(path, node);
}
void load(const Common::Path &path, rapidxml::xml_node<char> *node);
void draw(const Vector2i &pos, const TileInfo &tile);
void preDraw(const Vector2i &pos, const TileInfo &tile, Graphics::ManagedSurface *surf);
};
class TileSetGroup {
Common::Array<TileSet> _tileset;
// The latest tile position
Vector2i _v;
// The area that we have to draw
Vector2i _start, _finish;
public:
TileSetGroup() {}
void reset();
void load(const Common::Path &path, rapidxml::xml_node<char> *node);
void draw(MapLayer &layer, const Rect &camera, const Vector2i &tileSize, const Rect &playerPos, pyrodactyl::image::Image &img);
void preDraw(MapLayer &layer, const Vector2i &tileSize, Graphics::ManagedSurface *surf);
void forceDraw(MapLayer &layer, const Rect &camera, const Vector2i &tileSize, const Rect &playerPos);
};
} // End of namespace TMX
} // End of namespace Crab
#endif // CRAB_TMXTILESET_H

100
engines/crab/TMX/TileInfo.h Normal file
View File

@@ -0,0 +1,100 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_TILEINFO_H
#define CRAB_TILEINFO_H
#include "crab/loaders.h"
namespace Crab {
namespace TMX {
typedef uint GidFormat;
// Bits on the far end of the 32-bit global tile ID are used for tile flags
const static GidFormat FlippedHorizontallyFlag = 0x80000000;
const static GidFormat FlippedVerticallyFlag = 0x40000000;
const static GidFormat FlippedAntiDiagonallyFlag = 0x20000000;
struct TileInfo {
// The gid of the tile
GidFormat _gid;
// Do we need to flip this tile?
TextureFlipType _flip;
TileInfo() {
_gid = 0;
_flip = FLIP_NONE;
}
TileInfo(rapidxml::xml_node<char> *node) {
// Load the gid of the tile
if (!loadNum(_gid, "gid", node))
_gid = 0;
bool horizontal = (_gid & FlippedHorizontallyFlag) != 0;
bool vertical = (_gid & FlippedVerticallyFlag) != 0;
bool antidiagonal = (_gid & FlippedAntiDiagonallyFlag) != 0;
// Find how the tile is flipped
if (horizontal) {
if (vertical) {
if (antidiagonal)
_flip = FLIP_XYD;
else
_flip = FLIP_XY;
} else if (antidiagonal)
_flip = FLIP_DX;
else
_flip = FLIP_X;
} else if (vertical) {
if (antidiagonal)
_flip = FLIP_DY;
else
_flip = FLIP_Y;
} else if (antidiagonal)
_flip = FLIP_D;
else
_flip = FLIP_NONE;
// Clear the flags
_gid &= ~(FlippedHorizontallyFlag | FlippedVerticallyFlag | FlippedAntiDiagonallyFlag);
}
bool operator==(const TileInfo& other) const {
return (_gid == other._gid) && (_flip == other._flip);
}
};
} // End of namespace TMX
} // End of namespace Crab
#endif // CRAB_TILEINFO_H

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/>.
*
*/
#include "crab/TTSHandler.h"
#include "common/system.h"
namespace Crab {
void TTSHandler::onEntry(const Common::String &dialog) const {
Common::TextToSpeechManager *_ttsMan = g_system->getTextToSpeechManager();
if (_ttsMan) {
_ttsMan->enable(true);
_ttsMan->setPitch(50);
_ttsMan->setVolume(100);
_ttsMan->setRate(20);
_ttsMan->setVoice(1);
_ttsMan->say(dialog);
}
}
void TTSHandler::onExit() const {
Common::TextToSpeechManager *_ttsMan = g_system->getTextToSpeechManager();
if(_ttsMan) {
_ttsMan->stop();
}
}
} // End of namespace Crab

37
engines/crab/TTSHandler.h Normal file
View File

@@ -0,0 +1,37 @@
/* 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 CRAB_TTSHANDLER_H
#define CRAB_TTSHANDLER_H
#include "common/text-to-speech.h"
namespace Crab {
class TTSHandler {
public:
void onEntry(const Common::String &dialog) const;
void onExit() const;
};
} // End of namespace Crab
#endif // CRAB_TTSHANDLER_H

56
engines/crab/XMLDoc.cpp Normal file
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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/XMLDoc.h"
namespace rapidxml {
void parse_error_handler(char const* what, void* where) {
warning("RapidXML error handler: %s", what);
}
}
namespace Crab {
void XMLDoc::load(const Common::Path &filename) {
if (ready())
_doc.clear();
if (fileOpen(filename, _text) && _text)
_doc.parse<0>(_text);
}
const rapidxml::xml_document<> *XMLDoc::doc() const {
if (_text != nullptr)
return &_doc;
else
return nullptr;
}
} // End of namespace Crab

76
engines/crab/XMLDoc.h Normal file
View File

@@ -0,0 +1,76 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_XMLDOC_H
#define CRAB_XMLDOC_H
#include "crab/filesystem.h"
#include "crab/rapidxml/rapidxml.hpp"
namespace Crab {
class XMLDoc {
rapidxml::xml_document<char> _doc;
char *_text;
public:
XMLDoc() {
_text = nullptr;
}
XMLDoc(const Common::Path &filename) {
_text = nullptr;
load(filename);
}
XMLDoc(uint8 *data) {
_text = (char*)data;
_doc.parse<0>(_text);
}
~XMLDoc() {
delete[] _text;
}
// Load the text from the specified file into the rapidxml format
// Each function that references it must parse it there
void load(const Common::Path &filename);
// Check if document is ready for parsing
bool ready() const {
return _text != nullptr;
}
const rapidxml::xml_document<> *doc() const;
};
} // End of namespace Crab
#endif // CRAB_XMLDOC_H

View File

@@ -0,0 +1,72 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/ai/SpriteConstant.h"
namespace Crab {
using namespace pyrodactyl::ai;
FlyerConstant::FlyerConstant() : _start(10, 40), _vel(8.0f, 0.0f) {
_delayMin = 5000;
_delayMax = 20000;
}
void FlyerConstant::load(rapidxml::xml_node<char> *node) {
if (nodeValid("start", node))
_start.load(node->first_node("start"));
if (nodeValid("vel", node))
_vel.load(node->first_node("vel"));
if (nodeValid("delay", node)) {
auto n = node->first_node("delay");
loadNum(_delayMin, "min", n);
loadNum(_delayMax, "max", n);
}
}
SpriteConstant::SpriteConstant() : _walkVelMod(0.9f, 0.63f) {
_planeW = 20;
_tweening = 0.2f;
}
void SpriteConstant::load(rapidxml::xml_node<char> *node) {
loadNum(_planeW, "plane_width", node);
loadNum(_tweening, "tweening", node);
if (nodeValid("_walkVelMod", node))
_walkVelMod.load(node->first_node("_walkVelMod"));
if (nodeValid("fly", node))
_fly.load(node->first_node("fly"));
}
} // End of namespace Crab

View File

@@ -0,0 +1,79 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_SPRITECONSTANT_H
#define CRAB_SPRITECONSTANT_H
#include "crab/vectors.h"
namespace Crab {
namespace pyrodactyl {
namespace ai {
// These parameters control aspects of sprites flying across the screen
struct FlyerConstant {
// How far does a flier sprite start from the camera (an offset, not the whole value)
Vector2i _start;
// The value of the delay for fliers
uint32 _delayMin, _delayMax;
// The velocity of fliers
Vector2f _vel;
FlyerConstant();
void load(rapidxml::xml_node<char> *node);
};
// These values are used in various sprite related tasks
struct SpriteConstant {
// Plane width decides the maximum difference in sprite Y values that is considered on the same plane
int _planeW;
// Tweening constant controls the acceleration curve of every sprite
float _tweening;
// The modifiers of x and y movement speeds
Vector2f _walkVelMod;
// Data for flying sprites
FlyerConstant _fly;
SpriteConstant();
void load(rapidxml::xml_node<char> *node);
};
} // End of namespace ai
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_SPRITECONSTANT_H

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/ai/moveeffect.h"
namespace Crab {
using namespace pyrodactyl::anim;
FightMoveEffect::FightMoveEffect() {
_activate = -1;
_hit = -1;
_dmg = 0;
_stun = 0;
_hurt = -1;
_death = -1;
}
void FightMoveEffect::load(rapidxml::xml_node<char> *node) {
loadNum(_stun, "stun", node);
loadNum(_dmg, "damage", node);
loadNum(_hurt, "hurt", node);
loadNum(_death, "death", node);
if (nodeValid("image", node, false))
_img.load(node->first_node("image"));
if (nodeValid("sound", node)) {
rapidxml::xml_node<char> *soundnode = node->first_node("sound");
if (!loadNum(_activate, "activate", soundnode, false))
_activate = -1;
if (!loadNum(_activate, "hit", soundnode, false))
_activate = -1;
}
}
} // End of namespace Crab

View File

@@ -0,0 +1,72 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_MOVEEFFECT_H
#define CRAB_MOVEEFFECT_H
#include "crab/animation/imageeffect.h"
#include "crab/music/musicparam.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
struct FightMoveEffect {
// The image displayed on being hit
ImageEffect _img;
// The sound played by this move when it's activated
pyrodactyl::music::ChunkKey _activate;
// The sound played by this move when it hits opponent
pyrodactyl::music::ChunkKey _hit;
// The move the sprite hit by our current move performs - if living (hurt animation)
int _hurt;
// The move the sprite hit by our current move performs - if it dies as a result (dying animation)
int _death;
// The stun time for the enemy if this move hits a sprite
uint _stun;
// The base damage of the move if it hits a sprite
int _dmg;
FightMoveEffect();
void load(rapidxml::xml_node<char> *node);
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_MOVEEFFECT_H

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/ai/movement.h"
namespace Crab {
using namespace pyrodactyl::ai;
//------------------------------------------------------------------------
// Purpose: Walk in preset paths
//------------------------------------------------------------------------
void MovementSet::load(rapidxml::xml_node<char> *node) {
_enabled = true;
loadBool(_repeat, "repeat", node);
for (auto n = node->first_node("walk"); n != nullptr; n = n->next_sibling("walk"))
_path.push_back(n);
}
//------------------------------------------------------------------------
// Purpose: To make the AI patrol/wait along certain points
//------------------------------------------------------------------------
bool MovementSet::internalEvents(const Rect rect) {
if (_enabled) {
// If we are at the current waypoint, get to the next waypoint
if (_path[_cur]._target.collide(rect)) {
_cur = (_cur + 1) % _path.size();
_timer.start();
}
// Wait according to the delay value in the node
if (_timer.ticks() >= _path[_cur]._delay)
return true;
}
return false;
}
} // End of namespace Crab

View File

@@ -0,0 +1,92 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_MOVEMENT_H
#define CRAB_MOVEMENT_H
#include "crab/Rectangle.h"
#include "crab/timer.h"
#include "crab/vectors.h"
namespace Crab {
namespace pyrodactyl {
namespace ai {
struct MovementSet {
struct Movement {
// The position this sprite has to move to
Rect _target;
// The time the sprite waits before it starts moving to pos
uint32 _delay;
Movement(rapidxml::xml_node<char> *node) {
_target.load(node);
_delay = 0;
loadNum(_delay, "delay", node);
}
};
// The path followed by the sprite
Common::Array<Movement> _path;
// If true, sprite repeats the path pattern after reaching the last co-ordinate
bool _repeat;
// The current path node we are traveling to
uint _cur;
// The time the sprite has spent waiting is calculated here
Timer _timer;
// Is this set enabled?
bool _enabled;
MovementSet() {
_cur = 0;
_repeat = false;
_enabled = false;
}
MovementSet(rapidxml::xml_node<char> *node) : MovementSet() {
load(node);
}
void load(rapidxml::xml_node<char> *node);
bool internalEvents(const Rect rect);
Rect target() { return _path[_cur]._target; }
};
} // End of namespace ai
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_MOVEMENT_H

View File

@@ -0,0 +1,315 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/crab.h"
#include "crab/ScreenSettings.h"
#include "crab/animation/sprite.h"
namespace Crab {
using namespace pyrodactyl::anim;
using namespace pyrodactyl::ai;
//------------------------------------------------------------------------
// Purpose: Calculate distance between two sprites
//------------------------------------------------------------------------
double Sprite::distSq(const Sprite &s) {
double result = (_pos.x - s._pos.x) * (_pos.x - s._pos.x) + (_pos.y - s._pos.y) * (_pos.y - s._pos.y);
return result;
}
//------------------------------------------------------------------------
// Purpose: Used for player movement
//------------------------------------------------------------------------
void Sprite::moveToDest(pyrodactyl::event::Info &info, const SpriteConstant &sc) {
if (_aiData._dest._active) {
int num = 0;
info.statGet(_id, pyrodactyl::stat::STAT_SPEED, num);
++num;
float playerSpeed = static_cast<float>(num);
if (moveToLoc(_aiData._dest, playerSpeed, sc)) {
_aiData._dest._active = false;
xVel(0.0f);
yVel(0.0f);
}
}
}
void Sprite::moveToDestPathfinding(pyrodactyl::event::Info &info, const SpriteConstant &sc) {
if (_aiData._dest._active) {
int num = 0;
info.statGet(_id, pyrodactyl::stat::STAT_SPEED, num);
++num;
float playerSpeed = static_cast<float>(num);
Rect b = boundRect();
// Use to provide a bit of leeway with reaching the goal.
_pathing.setNodeBufferDistance((b.w * b.w) / 2.0f);
// IF we either have a solution, have reached our destination, and it was our final destination OR
// IF there is no solution OR
// IF we haven't yet found a solution
// THEN stop.
if ((moveToLocPathfinding(_aiData._dest, playerSpeed, sc) && _pathing._solutionFound &&
_pathing.getImmediateDest() == Vector2i(_pathing._destination.x, _pathing._destination.y)) ||
_pathing._noSolution || !_pathing._solutionFound) {
_aiData._dest._active = false;
xVel(0.0f);
yVel(0.0f);
}
}
}
//------------------------------------------------------------------------
// Purpose: Move towards a location without any path finding
//------------------------------------------------------------------------
bool Sprite::moveToLoc(Vector2i &dest, const float &velocity, const SpriteConstant &sc) {
// Use the bound rectangle dimensions
Rect b = boundRect();
// X axis
if (b.x + b.w < dest.x)
xVel(velocity * sc._walkVelMod.x);
else if (b.x > dest.x)
xVel(-velocity * sc._walkVelMod.x);
else
xVel(0.0f);
// Y axis
if (b.y + b.h < dest.y)
yVel(velocity * sc._walkVelMod.y);
else if (b.y > dest.y)
yVel(-velocity * sc._walkVelMod.y);
else
yVel(0.0f);
return b.contains(dest);
}
// Move toward the location using pathfinding.
bool Sprite::moveToLocPathfinding(Vector2i &dest, const float &velocity, const SpriteConstant &sc) {
// Rect b = BoundRect();
Vector2i immediateDest = _pathing.getImmediateDest();
Vector2f vecTo = Vector2f((float)immediateDest.x, (float)immediateDest.y) - _pathing.getPosition();
// If the destination is not the pathing goal, we must reach it exactly before moving on.
if (immediateDest != Vector2i(_pathing._destination.x, _pathing._destination.y)) {
Timer fps;
float deltaTime = 1.0f / (float)g_engine->_screenSettings->_fps;
// Project how far we will travel next frame.
Vector2f velVec = Vector2f(sc._walkVelMod.x * velocity * deltaTime, sc._walkVelMod.y * velocity * deltaTime);
if (vecTo.magnitude() > velVec.magnitude()) {
vecTo.normalize();
xVel(vecTo.x * sc._walkVelMod.x * velocity);
yVel(vecTo.y * sc._walkVelMod.y * velocity);
} else {
xVel(0.0f);
yVel(0.0f);
}
} else {
Vector2i loc = _pathing.getImmediateDest();
moveToLoc(loc, velocity, sc);
}
// return(MoveToLoc(pathing.GetImmediateDest(), vel, sc) || (xVel() == 0.0f && yVel() == 0.0f));
return (xVel() == 0.0f && yVel() == 0.0f);
}
//------------------------------------------------------------------------
// Purpose: AI routine for running to the nearest exit, then disappearing
//------------------------------------------------------------------------
void Sprite::flee(pyrodactyl::event::Info &info, Common::Array<pyrodactyl::level::Exit> &areaExit, const SpriteConstant &sc) {
switch (_aiData._flee._state) {
case FLEESTATE_GETNEARESTEXIT: {
if (areaExit.empty()) {
// No valid exits in the level
_aiData._flee._state = FLEESTATE_CANTFLEE;
break;
} else {
_aiData._flee._state = FLEESTATE_GETNEARESTEXIT;
// Standard way to find nearest exit
int min_dist = INT_MAX;
// Find the nearest exit
for (auto &i : areaExit) {
// Compare distance to the rough center of each exit
int dist = distance2D(_pos.x, _pos.y, i._dim._rect.x + i._dim._rect.w / 2, i._dim._rect.y + i._dim._rect.h / 2);
if (dist < min_dist) {
min_dist = dist;
// Set the destination of sprite to this exit
_aiData.dest(i._dim._rect.x + i._dim._rect.w / 2, i._dim._rect.y + i._dim._rect.h / 2);
_pathing.setDestination(Vector2f((float)_aiData._dest.x, (float)_aiData._dest.y));
}
}
}
} break;
case FLEESTATE_RUNTOEXIT: {
Rect b = boundRect();
if (b.contains(_aiData._dest)) {
// We have reached the exit, time to make the sprite disappear
_aiData._flee._state = FLEESTATE_DISAPPEAR;
break;
} else {
int num = 0;
info.statGet(_id, pyrodactyl::stat::STAT_SPEED, num);
++num;
float velocity = static_cast<float>(num);
// MoveToLoc(_aiData.dest, vel, sc);
moveToLocPathfinding(_aiData._dest, velocity, sc);
}
} break;
case FLEESTATE_DISAPPEAR:
_visible.result(false);
break;
default:
break;
}
}
//------------------------------------------------------------------------
// Purpose: AI routine for fighting the player
//------------------------------------------------------------------------
void Sprite::attack(pyrodactyl::event::Info &info, Sprite &targetSp, const SpriteConstant &sc) {
switch (_aiData._fight._state) {
case FIGHTSTATE_GETNEXTMOVE: {
_aiData._fight._state = FIGHTSTATE_GETINRANGE;
_aiData._fight._delay.start();
uint size = _aiData._fight._attack.size();
if (size > 1)
_animSet._fight.next(_aiData._fight._attack[g_engine->getRandomNumber(_aiData._fight._attack.size())]);
else if (size <= 0)
_aiData._fight._state = FIGHTSTATE_CANTFIGHT;
else
_animSet._fight.next(_aiData._fight._attack[0]);
}
break;
case FIGHTSTATE_GETINRANGE: {
// Set destination path to the player location
Rect b = targetSp.boundRect();
Vector2i dest(b.x + b.w / 2, b.y + b.h / 2);
setDestPathfinding(dest);
Rect p = boundRect();
_pathing.setPosition(Vector2f((float)(p.x + p.w / 2), (float)p.y + p.h / 2));
_pathing.update(0);
FightMove f;
if (_animSet._fight.nextMove(f) && fightCollide(targetSp.boxV(), targetSp.boundRect(), f._ai._range, sc)) {
if (_aiData._fight._delay.ticks() > f._ai._delay)
_aiData._fight._state = FIGHTSTATE_EXECUTEMOVE;
} else if (_input.idle())
moveToDestPathfinding(info, sc);
}
break;
case FIGHTSTATE_EXECUTEMOVE:
updateMove(_animSet._fight.next());
_aiData._fight._state = FIGHTSTATE_GETNEXTMOVE;
_aiData._fight._delay.stop();
break;
default:
break;
}
}
void Sprite::flyAround(const Rect &camera, const SpriteConstant &sc) {
// Is this sprite flying right now?
if (_aiData._walk._enabled) {
// We're flying towards the left edge
if (xVel() < 0) {
// Are we completely out of the left edge of the camera?
if (x() < camera.x - w()) {
_aiData._walk._enabled = false;
// Start the timer, set a semi-random time
_aiData._walk._timer.target(sc._fly._delayMin + (g_engine->getRandomNumber(sc._fly._delayMax)));
_aiData._walk._timer.start();
}
} else if (xVel() > 0) { // Flying towards the right edge
// Are we completely out of the left edge of the camera?
if (x() > camera.x + camera.w + w()) {
_aiData._walk._enabled = false;
// Start the timer, set a semi-random time
_aiData._walk._timer.target(sc._fly._delayMin + (g_engine->getRandomNumber(sc._fly._delayMax)));
_aiData._walk._timer.start();
}
}
move(sc);
} else {
// Safety condition in case timer isn't running
if (!_aiData._walk._timer.started())
_aiData._walk._timer.start();
// Is it time to start flying?
if (_aiData._walk._timer.targetReached()) {
// Stop the timer
_aiData._walk._timer.stop();
// Decide if the sprite flies from the left or right of the camera
if (g_engine->getRandomNumber(1)) {
// Fly in from the right
x(camera.x + camera.w + sc._fly._start.x);
xVel(-1.0f * sc._fly._vel.x);
// Sprite needs to face left
_dir = DIRECTION_LEFT;
} else {
// Fly in from the left
x(camera.x - w() - sc._fly._start.x);
xVel(sc._fly._vel.x);
// Sprite needs to face right
_dir = DIRECTION_RIGHT;
}
y(camera.y + sc._fly._start.y + (g_engine->getRandomNumber(camera.h - (2 * sc._fly._start.y))));
yVel(sc._fly._vel.y);
// Set state to flying
_aiData._walk._enabled = true;
}
}
}
} // End of namespace Crab

118
engines/crab/ai/spriteai.h Normal file
View File

@@ -0,0 +1,118 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_SPRITEAI_H
#define CRAB_SPRITEAI_H
#include "crab/ai/movement.h"
#include "crab/PathfindingAgent.h"
namespace Crab {
// class PathfindingAgent;
namespace pyrodactyl {
namespace ai {
// States of a fighting sprite
enum AIFightState {
FIGHTSTATE_GETNEXTMOVE,
FIGHTSTATE_GETINRANGE,
FIGHTSTATE_EXECUTEMOVE,
FIGHTSTATE_CANTFIGHT
};
// States of a fleeing sprite
enum AIFleeState {
FLEESTATE_GETNEARESTEXIT,
FLEESTATE_RUNTOEXIT,
FLEESTATE_DISAPPEAR,
FLEESTATE_CANTFLEE
};
struct SpriteAIData {
// Data required for fighting
struct FightData {
// The state of the sprite
AIFightState _state;
// Used to count down the time NPCs wait before their next move
// Usually varies per move which is why we don't load target for it
Timer _delay;
// The list of moves that can be performed while attacking
Common::Array<uint> _attack;
FightData() {
_state = FIGHTSTATE_GETNEXTMOVE;
}
} _fight;
// The pattern a peaceful sprite walks in
MovementSet _walk;
// Data required to flee
struct FleeData {
AIFleeState _state;
FleeData() {
_state = FLEESTATE_GETNEARESTEXIT;
}
} _flee;
// The next location the sprite has to reach
// PLAYER: Used for adventure game style point-n-click movement
// AI: Used for path-finding (usually to the player's location)
struct Destination : public Vector2i {
// Are we trying to reach the destination?
bool _active;
Destination() {
_active = false;
}
} _dest;
SpriteAIData() {}
void dest(const int &x, const int &y, const bool &active = true) {
_dest.x = x;
_dest.y = y;
_dest._active = active;
}
void dest(const Vector2i &v, const bool &active = true) {
dest(v.x, v.y, active);
}
};
} // End of namespace ai
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_SPRITEAI_H

View File

@@ -0,0 +1,102 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_ANIMATIONEFFECT_H
#define CRAB_ANIMATIONEFFECT_H
#include "crab/loaders.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
// Types of fade effects
enum FadeType {
FADE_NONE,
FADE_IN,
FADE_OUT
};
// Sometimes we need to stop drawing the game for proper fade effects
// Use DRAW_STOP to stop drawing the game until DRAW_START is called. DRAW_SAME doesn't change anything
enum DrawType {
DRAW_SAME,
DRAW_STOP,
DRAW_START
};
struct AnimationEffect {
// What sort of effect do we apply to the image
FadeType _type;
// The duration of the effect relative to the start of this animation
uint32 _start, _finish;
// Warning: the only way to start drawing the game again is having another animation event with DRAW_START
DrawType _drawGame;
AnimationEffect() {
_type = FADE_NONE;
_drawGame = DRAW_SAME;
_start = 0;
_finish = 0;
}
AnimationEffect(rapidxml::xml_node<char> *node) : AnimationEffect() {
if (nodeValid("effect", node)) {
rapidxml::xml_node<char> *effnode = node->first_node("effect");
loadNum(_start, "start", effnode);
loadNum(_finish, "finish", effnode);
Common::String str;
loadStr(str, "type", effnode);
if (str == "fade_in")
_type = FADE_IN;
else if (str == "fade_out")
_type = FADE_OUT;
else
_type = FADE_NONE;
loadStr(str, "game_draw", effnode);
if (str == "start")
_drawGame = DRAW_START;
else if (str == "stop")
_drawGame = DRAW_STOP;
else
_drawGame = DRAW_SAME;
}
}
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_ANIMATIONEFFECT_H

View File

@@ -0,0 +1,106 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/crab.h"
#include "crab/animation/AnimationFrame.h"
namespace Crab {
using namespace pyrodactyl::image;
using namespace pyrodactyl::anim;
AnimationFrame::AnimationFrame(rapidxml::xml_node<char> *node) : AnimationFrame() {
_eff = AnimationEffect(node);
Vector2i::load(node);
loadImgKey(_img, "img", node);
loadNum(_start, "start", node);
loadNum(_finish, "finish", node);
loadColor(_col, node);
if (nodeValid("text", node, false))
_text.load(node->first_node("text"));
reset();
}
void AnimationFrame::reset() {
switch (_eff._type) {
case FADE_IN:
_col.a = 0;
break;
case FADE_OUT:
_col.a = 255;
break;
default:
_col.a = 255;
break;
}
}
void AnimationFrame::draw(const uint32 &timestamp) {
warning("STUB: AnimationFrame::draw()");
#if 0
// Only draw the frame in the specified duration
if (timestamp >= start && timestamp <= finish) {
// Fill the screen with the color indicated
SDL_SetRenderDrawBlendMode(gRenderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(gRenderer, col.r, col.g, col.b, col.a);
SDL_RenderFillRect(gRenderer, NULL);
g_engine->_imageManager->draw(x, y, img);
text.draw();
}
#endif
}
DrawType AnimationFrame::internalEvents(const uint32 &timestamp) {
// Vary alpha according to the effect values in the variation time frame
if (timestamp >= _eff._start && timestamp <= _eff._finish) {
// These equations courtesy of linear algebra
switch (_eff._type) {
case FADE_IN:
_col.a = (255 * (timestamp - _eff._start)) / (_eff._finish - _eff._start);
break;
case FADE_OUT:
_col.a = (255 * (_eff._finish - timestamp)) / (_eff._finish - _eff._start);
break;
default:
break;
}
return _eff._drawGame;
}
return DRAW_SAME;
}
} // End of namespace Crab

View File

@@ -0,0 +1,81 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_ANIMATIONFRAME_H
#define CRAB_ANIMATIONFRAME_H
#include "crab/animation/AnimationEffect.h"
#include "crab/image/ImageManager.h"
#include "crab/text/TextManager.h"
#include "crab/ui/HoverInfo.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
struct AnimationFrame : public Vector2i {
// The image drawn in this frame
ImageKey _img;
// This is the time in we draw the frame milliseconds relative to the start of the entire animation
uint32 _start, _finish;
// The effect applied to the image
AnimationEffect _eff;
// In case we want to display any words during the animation
pyrodactyl::ui::HoverInfo _text;
// The color drawn on the screen
Color _col;
AnimationFrame() {
_img = 0;
_start = 0;
_finish = 0;
_col.r = 0;
_col.g = 0;
_col.b = 0;
_col.a = 255;
}
AnimationFrame(rapidxml::xml_node<char> *node);
void reset();
void draw(const uint32 &timestamp);
DrawType internalEvents(const uint32 &timestamp);
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_ANIMATIONFRAME_H

View File

@@ -0,0 +1,133 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/ScreenSettings.h"
#include "crab/animation/PopUp.h"
namespace Crab {
using namespace pyrodactyl::anim;
using namespace pyrodactyl::event;
//------------------------------------------------------------------------
// Purpose: Load from xml
//------------------------------------------------------------------------
void PopUp::load(rapidxml::xml_node<char> *node) {
_duration.load(node, "duration", false);
_delay.load(node, "delay");
loadStr(_text, "text", node);
loadNum(_next, "next", node);
bool end = false;
loadBool(end, "end", node, false);
if (end)
_next = -1;
_visible.load(node);
_effect.clear();
for (rapidxml::xml_node<char> *n = node->first_node("effect"); n != nullptr; n = n->next_sibling("effect")) {
Effect e;
e.load(n);
_effect.push_back(e);
}
}
void PopUpCollection::load(rapidxml::xml_node<char> *node) {
loadBool(_loop, "loop", node);
for (auto n = node->first_node("dialog"); n != nullptr; n = n->next_sibling("dialog"))
_element.push_back(n);
}
//------------------------------------------------------------------------
// Purpose: Internal events
//------------------------------------------------------------------------
bool PopUp::internalEvents(pyrodactyl::event::Info &info, const Common::String &playerId,
Common::Array<EventResult> &result, Common::Array<EventSeqInfo> &endSeq) {
if (_visible.evaluate(info) || _startedShow) {
if (_delay.targetReached()) {
if (_duration.targetReached(g_engine->_screenSettings->_textSpeed)) {
_show = false;
for (auto &i : _effect)
i.execute(info, playerId, result, endSeq);
return true;
} else {
_startedShow = true;
_show = true;
}
} else
_show = false;
} else
_show = false;
return false;
}
void PopUpCollection::internalEvents(pyrodactyl::event::Info &info, const Common::String &playerId,
Common::Array<EventResult> &result, Common::Array<EventSeqInfo> &endSeq) {
if (_cur >= 0 && (uint)_cur < _element.size()) {
if (_element[_cur].internalEvents(info, playerId, result, endSeq)) {
if (_element[_cur]._next <= 0 || (uint)_element[_cur]._next >= _element.size()) {
// This means that this popup is the "end" node, we must loop back to start or end this
if (_loop) {
_cur = 0;
_element[_cur].reset();
} else
_cur = -1;
} else {
_cur = _element[_cur]._next;
_element[_cur].reset();
}
}
}
}
//------------------------------------------------------------------------
// Purpose: Draw functions
//------------------------------------------------------------------------
void PopUp::draw(const int &x, const int &y, pyrodactyl::ui::ParagraphData &pop, const Rect &camera) {
if (_show) {
if (x + pop.x < camera.w / 3)
g_engine->_textManager->draw(x + pop.x, y + pop.y, _text, pop._col, pop._font, ALIGN_LEFT, pop._line.x, pop._line.y, true);
else if (x + pop.x > (2 * camera.w) / 3)
g_engine->_textManager->draw(x + pop.x, y + pop.y, _text, pop._col, pop._font, ALIGN_RIGHT, pop._line.x, pop._line.y, true);
else
g_engine->_textManager->draw(x + pop.x, y + pop.y, _text, pop._col, pop._font, ALIGN_CENTER, pop._line.x, pop._line.y, true);
}
}
void PopUpCollection::draw(const int &x, const int &y, pyrodactyl::ui::ParagraphData &pop, const Rect &camera) {
if (_cur >= 0 && (uint)_cur < _element.size())
_element[_cur].draw(x, y, pop, camera);
}
} // End of namespace Crab

View File

@@ -0,0 +1,127 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_POPUP_H
#define CRAB_POPUP_H
#include "crab/timer.h"
#include "crab/event/effect.h"
#include "crab/event/triggerset.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
struct PopUp {
// The total time the popup stays on the screen
Timer _duration;
// The time we wait before showing the trigger for the first time
Timer _delay;
// Should we draw this or not? (Decided by internal events)
bool _show;
// Popups with "talk key pressed" condition need to be shown once the key is pressed
bool _startedShow;
// Triggers for when you only want to display this in certain conditions
pyrodactyl::event::TriggerSet _visible;
// Effects for changing variables and other related stuff
Common::Array<pyrodactyl::event::Effect> _effect;
// The text displayed
Common::String _text;
// The next popup we should go to, negative values means the end
int _next;
PopUp() {
_next = -1;
reset();
}
PopUp(rapidxml::xml_node<char> *node) : PopUp() {
load(node);
}
void reset() {
_show = false;
_startedShow = false;
_delay.stop();
_duration.stop();
}
void load(rapidxml::xml_node<char> *node);
void draw(const int &x, const int &y, pyrodactyl::ui::ParagraphData &pop, const Rect &camera);
// return true if we should proceed to next event, false otherwise
bool internalEvents(pyrodactyl::event::Info &info, const Common::String &playerId,
Common::Array<pyrodactyl::event::EventResult> &result, Common::Array<pyrodactyl::event::EventSeqInfo> &endSeq);
};
struct PopUpCollection {
// Collection of environmental dialog
Common::Array<PopUp> _element;
// The current dialog being played
int _cur;
// true if dialog needs to loop, false otherwise
bool _loop;
PopUpCollection() {
_cur = 0;
_loop = true;
}
// Return true if any of the popup dialog is visible, false otherwise
bool show() {
for (auto &i : _element)
if (i._show)
return true;
return false;
}
void load(rapidxml::xml_node<char> *node);
void internalEvents(pyrodactyl::event::Info &info, const Common::String &playerId,
Common::Array<pyrodactyl::event::EventResult> &result, Common::Array<pyrodactyl::event::EventSeqInfo> &endSeq);
void draw(const int &x, const int &y, pyrodactyl::ui::ParagraphData &pop, const Rect &camera);
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_POPUP_H

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/animation/animation.h"
namespace Crab {
using namespace pyrodactyl::image;
using namespace pyrodactyl::anim;
Animation::Animation(rapidxml::xml_node<char> *node) {
loadNum(_length, "length", node);
for (auto n = node->first_node("frame"); n != nullptr; n = n->next_sibling("frame"))
_frame.push_back(n);
}
void Animation::draw() {
uint32 timestamp = _timer.ticks();
for (auto &frame : _frame)
frame.draw(timestamp);
}
bool Animation::internalEvents(DrawType &gameDraw) {
uint32 timestamp = _timer.ticks();
for (auto &frame : _frame) {
DrawType result = frame.internalEvents(timestamp);
// if (result != DRAW_SAME)
gameDraw = result;
}
return _timer.ticks() >= _length;
}
void Animation::reset() {
for (auto &frame : _frame)
frame.reset();
}
} // End of namespace Crab

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_ANIMATION_H
#define CRAB_ANIMATION_H
#include "crab/timer.h"
#include "crab/animation/AnimationEffect.h"
#include "crab/animation/AnimationFrame.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
class Animation {
// All the frames are updated simultaneously rather than sequentially
Common::Array<AnimationFrame> _frame;
// Length of the entire animation in milliseconds
uint32 _length;
// Keep track of the time
Timer _timer;
public:
Animation() {
_length = 0;
}
Animation(rapidxml::xml_node<char> *node);
void draw();
void reset();
bool internalEvents(DrawType &gameDraw);
void start() {
reset();
_timer.start();
}
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB__ANIMATION_H

View File

@@ -0,0 +1,109 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/animation/animframe.h"
namespace Crab {
using namespace pyrodactyl::anim;
void AnimFrame::load(rapidxml::xml_node<char> *node, const Rect &VBOX, const uint32 &rep, const int &AX, const int &AY) {
_clip.load(node);
if (rep == 0)
loadNum(_repeat, "repeat", node);
else
_repeat = rep;
if (AX == 0.0f && AY == 0.0f) {
if (nodeValid("anchor", node, false))
_anchor.load(node->first_node("anchor"));
} else {
_anchor.x = AX;
_anchor.y = AY;
}
if (VBOX.w == 0 || VBOX.h == 0) {
if (nodeValid("box_v", node))
_boxV.load(node->first_node("box_v"));
} else
_boxV = VBOX;
}
void AnimationFrames::load(rapidxml::xml_node<char> *node) {
loadTextureFlipType(_flip, node);
if (!loadNum(_repeat, "repeat", node, false))
_repeat = 0;
loadBool(_random, "random", node, false);
if (nodeValid("anchor", node, false))
_anchor.load(node->first_node("anchor"));
if (nodeValid("box_v", node))
_boxV.load(node->first_node("box_v"));
if (nodeValid("shadow", node)) {
_shadow.load(node->first_node("shadow"));
_shadow._valid = true;
}
if (nodeValid("frames", node)) {
_frame.clear();
rapidxml::xml_node<char> *framenode = node->first_node("frames");
for (auto n = framenode->first_node("frame"); n != nullptr; n = n->next_sibling("frame")) {
AnimFrame af;
af.load(n, _boxV, _repeat, _anchor.x, _anchor.y);
_frame.push_back(af);
}
}
if (_random)
_currentClip = g_engine->getRandomNumber(_frame.size() - 1);
else
_currentClip = 0;
}
bool AnimationFrames::updateClip() {
if (_currentClip < _frame.size()) {
_currentClip = (_currentClip + 1) % _frame.size();
return true;
} else
_currentClip = 0;
return false;
}
const AnimFrame &AnimationFrames::currentFrame() {
return _frame[_currentClip];
}
} // End of namespace Crab

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_ANIMFRAME_H
#define CRAB_ANIMFRAME_H
#include "crab/Rectangle.h"
#include "crab/vectors.h"
#include "crab/animation/shadow.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
struct AnimFrame {
// Portion of sprite to show
Rect _clip;
// The duration for which the frame must be repeated on screen
uint32 _repeat;
// The anchor point of the frame
Vector2i _anchor;
// The vulnerable hit box for this frame
Rect _boxV;
AnimFrame() {
_repeat = 0;
}
void load(rapidxml::xml_node<char> *node, const Rect &VBOX,
const uint32 &REP = 0, const int &AX = 0, const int &AY = 0);
};
class AnimationFrames {
// The global vulnerable hit box for all the frames
// If the W or H of this is 0, then use individual frame values
Rect _boxV;
public:
// The frames for the animation
Common::Array<AnimFrame> _frame;
// The currentClip
uint _currentClip;
// Should we flip the images in the frame rectangle?
TextureFlipType _flip;
// The global repeat value for all the frames
// If this is 0, then use individual frame values
uint32 _repeat;
// The global anchor value for all the frames
// If this is 0, then use individual frame values
Vector2i _anchor;
// true if animation starts at a random frame
// used for idle animations so that every sprite doesn't animate in sync
bool _random;
// Does this set of animation frames need a specific shadow offset?
ShadowOffset _shadow;
AnimationFrames() {
reset();
_flip = FLIP_NONE;
_repeat = 0;
_random = false;
}
void reset() {
_currentClip = 0;
}
void load(rapidxml::xml_node<char> *node);
bool updateClip();
const AnimFrame &currentFrame();
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_ANIMFRAME_H

View File

@@ -0,0 +1,89 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/XMLDoc.h"
#include "crab/animation/animset.h"
namespace Crab {
using namespace pyrodactyl::anim;
void AnimSet::load(const Common::Path &filename) {
XMLDoc conf(filename);
if (conf.ready()) {
rapidxml::xml_node<char> *node = conf.doc()->first_node();
if (nodeValid(node)) {
_fight.load(node);
_walk.load(node);
if (nodeValid("bounds", node))
_bounds.load(node->first_node("bounds"));
if (nodeValid("shadow", node))
_shadow.load(node->first_node("shadow"));
if (nodeValid("focus", node))
_focus.load(node->first_node("focus"));
}
}
}
TextureFlipType AnimSet::flip(const Direction &dir) {
TextureFlipType ret;
if (_fight.flip(ret, dir))
return ret;
return _walk.flip(dir);
}
const ShadowOffset &AnimSet::shadow(const Direction &dir) {
if (_fight.validMove())
return _fight.shadow(dir);
return _walk.shadow(dir);
}
int AnimSet::anchorX(const Direction &dir) {
FightAnimFrame faf;
if (_fight.curFrame(faf, dir))
return faf._anchor.x;
return _walk.anchorX(dir);
}
int AnimSet::anchorY(const Direction &dir) {
FightAnimFrame faf;
if (_fight.curFrame(faf, dir))
return faf._anchor.y;
return _walk.anchorY(dir);
}
} // End of namespace Crab

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_ANIMSET_H
#define CRAB_ANIMSET_H
#include "crab/animation/fightmoves.h"
#include "crab/animation/shadow.h"
#include "crab/animation/walkframes.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
// Container for all the possible animations an object can have
struct AnimSet {
// The frames relevant to fighting moves
FightMoves _fight;
// The frames relevant to walking animations
WalkFrames _walk;
// The bounding box of the character used for level collision
Rect _bounds;
// The sprite shadow
ShadowData _shadow;
// The camera focus point
Vector2i _focus;
AnimSet() {}
void load(const Common::Path &filename);
TextureFlipType flip(const Direction &dir);
const ShadowOffset &shadow(const Direction &dir);
int anchorX(const Direction &dir);
int anchorY(const Direction &dir);
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_ANIMSET_H

View File

@@ -0,0 +1,86 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/animation/fightanim.h"
namespace Crab {
using namespace pyrodactyl::anim;
//------------------------------------------------------------------------
// Purpose: Load a single frame of a fighting move
//------------------------------------------------------------------------
void FightAnimFrame::load(rapidxml::xml_node<char> *node, const Rect &VBOX, const uint32 &rep, const int &AX, const int &AY) {
AnimFrame::load(node, VBOX, rep, AX, AY);
if (nodeValid("box_d", node, false))
_boxD.load(node->first_node("box_d"));
if (nodeValid("shift", node, false))
_delta.load(node->first_node("shift"));
if (!loadNum(_state, "state", node, false))
_state = 0;
loadBool(_branch, "branch", node, false);
}
//------------------------------------------------------------------------
// Purpose: Load a fighting move
//------------------------------------------------------------------------
void FightAnimFrames::load(rapidxml::xml_node<char> *node) {
loadTextureFlipType(_flip, node);
if (!loadNum(_repeat, "repeat", node, false))
_repeat = 0;
if (nodeValid("anchor", node, false))
_anchor.load(node->first_node("anchor"));
if (nodeValid("box_v", node))
_boxV.load(node->first_node("box_v"));
if (nodeValid("shadow", node)) {
_shadow.load(node->first_node("shadow"));
_shadow._valid = true;
}
if (nodeValid("frames", node)) {
_frame.clear();
rapidxml::xml_node<char> *framenode = node->first_node("frames");
for (auto n = framenode->first_node("frame"); n != nullptr; n = n->next_sibling("frame")) {
FightAnimFrame faf;
faf.load(n, _boxV, _repeat, _anchor.x, _anchor.y);
_frame.push_back(faf);
}
}
}
} // End of namespace Crab

View File

@@ -0,0 +1,110 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_FIGHTANIM_H
#define CRAB_FIGHTANIM_H
#include "crab/ai/moveeffect.h"
#include "crab/animation/fm_ai_data.h"
#include "crab/input/fightinput.h"
#include "crab/event/triggerset.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
// A single frame of a fighting move
struct FightAnimFrame : public AnimFrame {
// The hit box of the player WITH RESPECT TO the sprite bounding box
//boxV is the vulnerable hit box, boxD is the damage hit box
Rect _boxD;
// The displacement in the position caused by the frame
Vector2i _delta;
// The sprite state for the duration of the frame
uint _state;
// Can we cancel/branch to another move from this frame?
bool _branch;
FightAnimFrame() {
_state = 0;
_branch = false;
}
void load(rapidxml::xml_node<char> *node, const Rect &VBOX,
const uint32 &REP = 0, const int &AX = 0, const int &AY = 0);
};
// All data related to a single fighting move in a single direction
class FightAnimFrames {
// The global vulnerable hit box for all the frames
// If the W or H of this is 0, then use individual frame values
Rect _boxV;
public:
// The individual frames for each direction
Common::Array<FightAnimFrame> _frame;
// The currentClip
uint _currentClip;
// Should we flip the images in the frame rectangle?
TextureFlipType _flip;
// The amount of time in milliseconds each animation frame needs to be on screen
// If this is zero then use the value in each individual frame
uint32 _repeat;
// The global anchor value for all the frames
// If this is 0, then use individual frame values
Vector2i _anchor;
// Does this set of animation frames need a specific shadow offset?
ShadowOffset _shadow;
FightAnimFrames() {
reset();
_flip = FLIP_NONE;
_repeat = 0;
}
void reset() {
_currentClip = 0;
}
void load(rapidxml::xml_node<char> *node);
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_FIGHTANIM_H

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/animation/fightmove.h"
namespace Crab {
using namespace pyrodactyl::anim;
void FightMove::load(rapidxml::xml_node<char> *node) {
_frames[DIRECTION_DOWN].load(node->first_node("down"));
_frames[DIRECTION_UP].load(node->first_node("up"));
_frames[DIRECTION_LEFT].load(node->first_node("left"));
_frames[DIRECTION_RIGHT].load(node->first_node("right"));
if (nodeValid("input", node))
_input.load(node->first_node("input"));
if (nodeValid("unlock", node, false))
_unlock.load(node->first_node("unlock"));
if (nodeValid("effect", node))
_eff.load(node->first_node("effect"));
if (nodeValid("ai", node, false))
_ai.load(node->first_node("ai"));
}
} // End of namespace Crab

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_FIGHTMOVE_H
#define CRAB_FIGHTMOVE_H
#include "crab/animation/fightanim.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
// All info for a single fighting move in all four directions
struct FightMove {
// Frames for all four directions
FightAnimFrames _frames[DIRECTION_TOTAL];
// The input required
pyrodactyl::input::FightInput _input;
// The conditions to unlock this move for player use
pyrodactyl::event::TriggerSet _unlock;
// The effects of this move - hurt animation, sound effect and so on
FightMoveEffect _eff;
// The data needed by an AI sprite to execute this move
FightMoveAIData _ai;
FightMove() {}
~FightMove() {}
void load(rapidxml::xml_node<char> *node);
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_FIGHTMOVE_H

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/animation/fightmoves.h"
namespace Crab {
using namespace pyrodactyl::anim;
//------------------------------------------------------------------------
// Purpose: Constructor
//------------------------------------------------------------------------
FightMoves::FightMoves() {
_cur = -1;
_next = -1;
_start = false;
_frameCur = 0;
_frameTotal = 0;
_move.clear();
_timer.start();
}
//------------------------------------------------------------------------
// Purpose: Load from file
//------------------------------------------------------------------------
void FightMoves::load(rapidxml::xml_node<char> *node) {
for (auto n = node->first_node("move"); n != nullptr; n = n->next_sibling("move")) {
FightMove fm;
fm.load(n);
_move.push_back(fm);
}
}
//------------------------------------------------------------------------
// Purpose: Return current or next move
//------------------------------------------------------------------------
bool FightMoves::curMove(FightMove &fm) {
if (_cur >= 0 && (uint)_cur < _move.size()) {
fm = _move[_cur];
return true;
}
return false;
}
bool FightMoves::nextMove(FightMove &fm) {
if (_next >= 0 && (uint)_next < _move.size()) {
fm = _move[_next];
return true;
}
return false;
}
//------------------------------------------------------------------------
// Purpose: Get the current frame of the sprite
//------------------------------------------------------------------------
bool FightMoves::curFrame(FightAnimFrame &faf, const Direction &d) {
// Check validity of current move
if (_cur >= 0 && (uint)_cur < _move.size()) {
// Check validity of current frame
if (_frameCur < _frameTotal && _frameCur < _move[_cur]._frames[d]._frame.size()) {
faf = _move[_cur]._frames[d]._frame[_frameCur];
return true;
}
}
return false;
}
//------------------------------------------------------------------------
// Purpose: Update frame
//------------------------------------------------------------------------
FrameUpdateResult FightMoves::updateFrame(const Direction &d) {
// Check validity of current move
if (_cur >= 0 && (uint)_cur < _move.size()) {
// Check validity of current frame
if (_frameCur < _frameTotal && _frameCur < _move[_cur]._frames[d]._frame.size()) {
// Has the current frame finished playing?
// OR Is this the first frame of the move?
if (_timer.ticks() >= _move[_cur]._frames[d]._frame[_frameCur]._repeat || _start) {
_frameCur++;
_timer.start();
_start = false;
return FUR_SUCCESS;
} else
return FUR_WAIT;
}
}
return FUR_FAIL;
}
//------------------------------------------------------------------------
// Purpose: Find a move corresponding to the input and sprite state
//------------------------------------------------------------------------
uint FightMoves::findMove(const pyrodactyl::input::FightAnimationType &type, const int &state) {
uint pos = 0;
for (auto i = _move.begin(); i != _move.end(); ++i, ++pos)
if (i->_input._type == type && i->_input._state == (uint)state)
return pos;
pos = 0;
for (auto i = _move.begin(); i != _move.end(); ++i, ++pos)
if (i->_input._type == type && i->_input._state == SPRITE_STATE_OVERRIDE)
return pos;
return SPRITE_STATE_OVERRIDE;
}
//------------------------------------------------------------------------
// Purpose: Function for AI
//------------------------------------------------------------------------
void FightMoves::listAttackMoves(Common::Array<uint> &list) {
list.clear();
uint pos = 0;
for (auto i = _move.begin(); i != _move.end(); ++i, ++pos)
if (i->_ai._type == MOVE_ATTACK)
list.push_back(pos);
}
//------------------------------------------------------------------------
// Purpose: Force update to a new move
//------------------------------------------------------------------------
bool FightMoves::forceUpdate(const uint &index, pyrodactyl::input::FightInput &input, const Direction &d) {
_frameCur = 0;
_cur = index;
if ((uint)_cur < _move.size()) {
if (_move[_cur]._unlock.result()) {
_frameTotal = _move[_cur]._frames[d]._frame.size();
if (_frameTotal > 0) {
input = _move[_cur]._input;
input._state = _move[_cur]._frames[d]._frame[0]._state;
} else
input.reset();
_timer.start();
_start = true;
g_engine->_musicManager->playEffect(_move[_cur]._eff._activate, 0);
return true;
}
}
_cur = 0;
_frameTotal = 0;
return false;
}
//------------------------------------------------------------------------
// Purpose: Set unlock status
//------------------------------------------------------------------------
void FightMoves::evaluate(pyrodactyl::event::Info &info) {
for (auto &i : _move)
i._unlock.evaluate(info);
}
//------------------------------------------------------------------------
// Purpose: Find which style to flip the texture in
//------------------------------------------------------------------------
bool FightMoves::flip(TextureFlipType &flip, Direction d) {
// Check validity of current move
if (validMove()) {
flip = _move[_cur]._frames[d]._flip;
return true;
}
return false;
}
} // End of namespace Crab

View File

@@ -0,0 +1,133 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_FIGHTMOVES_H
#define CRAB_FIGHTMOVES_H
#include "crab/timer.h"
#include "crab/animation/fightmove.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
enum FrameUpdateResult {
FUR_FAIL,
FUR_WAIT,
FUR_SUCCESS
};
// This state value indicates that the move should execute regardless of actual sprite state
const uint SPRITE_STATE_OVERRIDE = std::numeric_limits<uint>::max();
class FightMoves {
// The fighting moves of a sprite
Common::Array<FightMove> _move;
// The currently selected move
int _cur;
// For AI - the move about to be executed
int _next;
// The timer used for playing animations
Timer _timer;
// We need to instantly show the new frame for a move that has started
bool _start;
// The current frame and total frames
uint _frameCur, _frameTotal;
public:
FightMoves();
~FightMoves() {}
void reset() {
_cur = -1;
}
void load(rapidxml::xml_node<char> *node);
bool curMove(FightMove &fm);
bool nextMove(FightMove &fm);
FrameUpdateResult updateFrame(const Direction &d);
bool curFrame(FightAnimFrame &faf, const Direction &d);
uint findMove(const pyrodactyl::input::FightAnimationType &type, const int &state);
void listAttackMoves(Common::Array<uint> &list);
bool forceUpdate(const uint &index, pyrodactyl::input::FightInput &input, const Direction &d);
bool lastFrame() {
return _frameCur >= _frameTotal;
}
void frameIndex(uint val) {
_frameCur = val;
}
void curCombo(pyrodactyl::input::FightInput &input) {
input = _move[_cur]._input;
}
bool validMove() {
return _cur >= 0 && (uint)_cur < _move.size();
}
bool empty() {
return _move.empty();
}
bool flip(TextureFlipType &flip, Direction d);
const ShadowOffset &shadow(Direction d) {
return _move[_cur]._frames[d]._shadow;
}
void next(int val) {
_next = val;
}
int next() {
return _next;
}
void evaluate(pyrodactyl::event::Info &info);
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_FIGHTMOVES_H

View File

@@ -0,0 +1,82 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_FM_AI_DATA_H
#define CRAB_FM_AI_DATA_H
#include "crab/animation/range.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
enum AIMoveType {
MOVE_NONE, // AI sprites do not use this move
MOVE_ATTACK, // AI sprites use this move to attack you
MOVE_DEFEND // AI sprites use this move to dodge or defend
};
struct FightMoveAIData {
// Can this move be used by AI to attack
AIMoveType _type;
// The range of the move
Range _range;
// The AI delays executing the move by this long
uint _delay;
FightMoveAIData() {
_type = MOVE_NONE;
_delay = 0;
}
void load(rapidxml::xml_node<char> *node) {
if (!loadNum(_delay, "delay", node, false))
_delay = 0;
_range.load(node->first_node("range"));
Common::String str;
loadStr(str, "type", node, false);
if (str == "attack")
_type = MOVE_ATTACK;
else if (str == "defend")
_type = MOVE_DEFEND;
else
_type = MOVE_NONE;
}
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_FM_AI_DATA_H

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/crab.h"
#include "crab/animation/imageeffect.h"
#include "crab/image/ImageManager.h"
namespace Crab {
using namespace pyrodactyl::anim;
void ImageEffect::load(rapidxml::xml_node<char> *node) {
if (nodeValid(node, false)) {
if (loadImgKey(_img, "img", node) && loadXY(x, y, node))
_visible = true;
}
}
void ImageEffect::draw(const int &xOffset, const int &yOffset) {
if (_visible)
g_engine->_imageManager->draw(x + xOffset, y + yOffset, _img);
}
} // End of namespace Crab

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_IMAGEEFFECT_H
#define CRAB_IMAGEEFFECT_H
#include "crab/image/ImageManager.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
struct ImageEffect : public Vector2i {
ImageKey _img;
bool _visible;
ImageEffect() {
_visible = false;
_img = 0;
}
void load(rapidxml::xml_node<char> *node);
void draw(const int &xOffset, const int &yOffset);
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_IMAGEEFFECT_H

View File

@@ -0,0 +1,65 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_RANGE_H
#define CRAB_RANGE_H
#include "crab/animation/animframe.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
struct Range {
bool _valid;
Rect _val[DIRECTION_TOTAL];
Range() {
_valid = false;
}
void load(rapidxml::xml_node<char> *node) {
if (nodeValid(node, false)) {
_val[DIRECTION_DOWN].load(node->first_node("down"));
_val[DIRECTION_UP].load(node->first_node("up"));
_val[DIRECTION_LEFT].load(node->first_node("left"));
_val[DIRECTION_RIGHT].load(node->first_node("right"));
_valid = true;
} else
_valid = false;
}
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_RANGE_H

View File

@@ -0,0 +1,83 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_SHADOW_H
#define CRAB_SHADOW_H
#include "crab/crab.h"
#include "crab/vectors.h"
#include "crab/image/ImageManager.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
struct ShadowData {
// The image of the sprite's shadow
ImageKey _img;
// Size of image
Vector2i _size;
// The default shadow offset
Vector2i _offset;
ShadowData() : _size(1, 1) {
_img = 0;
}
void load(rapidxml::xml_node<char> *node) {
loadImgKey(_img, "img", node);
_offset.load(node);
using namespace pyrodactyl::image;
Image dat;
g_engine->_imageManager->getTexture(_img, dat);
_size.x = dat.w() / 2;
_size.y = dat.h() / 2;
}
};
// Used when a set of animation frames needs a specific shadow offset
class ShadowOffset : public Vector2i {
public:
// Only use this offset if this is true
bool _valid;
ShadowOffset() {
_valid = false;
}
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_SHADOW_H

View File

@@ -0,0 +1,668 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/animation/sprite.h"
#include "crab/input/cursor.h"
namespace Crab {
using namespace pyrodactyl::ai;
using namespace pyrodactyl::anim;
using namespace pyrodactyl::input;
using namespace pyrodactyl::event;
//------------------------------------------------------------------------
// Purpose: Constructor
//------------------------------------------------------------------------
Sprite::Sprite() : _imgSize(1, 1), _velMod(1.0f, 1.0f) {
_dir = DIRECTION_LEFT;
_layer = -1;
_image = 0;
visible(true);
_damageDone = false;
_hover = false;
// _pathing.SetSprite(this);
}
//------------------------------------------------------------------------
// Purpose: Load sprite from XML and animations from the index of all animation files
//------------------------------------------------------------------------
void Sprite::load(rapidxml::xml_node<char> *node, Common::Array<Common::Path> &animations) {
using namespace pyrodactyl::image;
if (nodeValid(node)) {
loadStr(_id, "id", node);
// The amount by which the sprite x,y should be multiplied
int multiply = 1;
loadNum(multiply, "multiply", node, false);
loadNum(_pos.x, "x", node);
loadNum(_pos.y, "y", node);
_pos.x *= multiply;
_pos.y *= multiply;
loadImgKey(_image, "img", node);
loadNum(_layer, "layer", node, false);
Image dat;
g_engine->_imageManager->getTexture(_image, dat);
_imgSize.x = dat.w();
_imgSize.y = dat.h();
uint index = 0;
loadNum(index, "moveset", node);
if (index < animations.size())
_animSet.load(animations[index]);
_animSet._fight.listAttackMoves(_aiData._fight._attack);
loadDirection(_dir, node);
_clip = _animSet._walk.clip(_dir);
_boxV = _animSet._walk.boxV(_dir);
if (nodeValid("visible", node, false))
_visible.load(node->first_node("visible"));
if (nodeValid("movement", node, false))
_aiData._walk.load(node->first_node("movement"));
if (nodeValid("popup", node, false))
_popup.load(node->first_node("popup"));
}
}
//------------------------------------------------------------------------
// Purpose: Move along x and y axis
//------------------------------------------------------------------------
void Sprite::move(const SpriteConstant &sc) {
if (_target.x == 0.0f && (_vel.x > -sc._tweening && _vel.x < sc._tweening))
_vel.x = 0.0f;
else {
_vel.x += (_target.x - _vel.x) * sc._tweening;
_pos.x += _vel.x;
}
if (_target.y == 0.0f && (_vel.y > -sc._tweening && _vel.y < sc._tweening))
_vel.y = 0.0f;
else {
_vel.y += (_target.y - _vel.y) * sc._tweening;
_pos.y += _vel.y;
}
}
//------------------------------------------------------------------------
// Purpose: Resolve collision with shapes excluding the level bounding rectangle
//------------------------------------------------------------------------
void Sprite::resolveCollide() {
// NOTE: we don't check i->intersect here because we do that in the level functions
for (const auto &i : _collideData) {
Rect bounds = boundRect();
if (i._type == SHAPE_POLYGON) {
_pos.x -= i._data.x;
_pos.y -= i._data.y;
} else {
Direction d = bounds.resolveY(i._data);
if (d == DIRECTION_UP)
_pos.y -= i._data.y + i._data.h - _animSet._bounds.y + _animSet.anchorY(_dir) + 1;
else if (d == DIRECTION_DOWN)
_pos.y -= i._data.y - bounds.h - _animSet._bounds.y + _animSet.anchorY(_dir) - 1;
d = bounds.resolveX(i._data);
if (d == DIRECTION_LEFT)
_pos.x -= i._data.x + i._data.w - _animSet._bounds.x + _animSet.anchorX(_dir) + 1;
else if (d == DIRECTION_RIGHT)
_pos.x -= i._data.x - bounds.w - _animSet._bounds.x + _animSet.anchorX(_dir) - 1;
}
}
// Clear the collision data
_collideData.clear();
}
//------------------------------------------------------------------------
// Purpose: Resolve collision with the level bounding rectangle
//------------------------------------------------------------------------
void Sprite::resolveInside(Rect collider) {
Rect bounds = boundRect();
Direction d = bounds.resolveX(collider);
if (d == DIRECTION_RIGHT)
_pos.x = collider.x - _animSet._bounds.x + _animSet.anchorX(_dir) + 1;
else if (d == DIRECTION_LEFT)
_pos.x = collider.x + collider.w - _animSet._bounds.x - bounds.w + _animSet.anchorX(_dir) - 1;
bounds = boundRect();
d = bounds.resolveY(collider);
if (d == DIRECTION_DOWN)
_pos.y = collider.y - _animSet._bounds.y + _animSet.anchorY(_dir) + 1;
else if (d == DIRECTION_UP)
_pos.y = collider.y + collider.h - _animSet._bounds.y - bounds.h + _animSet.anchorY(_dir) - 1;
}
//------------------------------------------------------------------------
// Purpose: Various rectangles of the sprite
// Note: Every sprite in the game uses bottom center coordinates:
// draw_x = x - clip.w/2, draw_y = y - clip.h
//------------------------------------------------------------------------
// Used for walking and level geometry collision
Rect Sprite::boundRect() {
Rect rect;
rect.x = _pos.x + _animSet._bounds.x - _animSet.anchorX(_dir);
rect.y = _pos.y + _animSet._bounds.y - _animSet.anchorY(_dir);
rect.w = _animSet._bounds.w;
rect.h = _animSet._bounds.h;
return rect;
}
// Used for fighting
Rect Sprite::boxV() {
Rect rect;
rect.x = _pos.x + _boxV.x;
rect.y = _pos.y + _boxV.y;
rect.w = _boxV.w;
rect.h = _boxV.h;
return rect;
}
// Used for fighting
Rect Sprite::boxD() {
Rect rect;
rect.x = _pos.x + _boxD.x;
rect.y = _pos.y + _boxD.y;
rect.w = _boxD.w;
rect.h = _boxD.h;
return rect;
}
// Used for drawing object notifications over stuff
Rect Sprite::posRect() {
Rect rect;
rect.x = _pos.x - _animSet.anchorX(_dir);
rect.y = _pos.y - _animSet.anchorY(_dir);
rect.w = _clip.w;
rect.h = _clip.h;
return rect;
}
// The range rectangle is relative to the bounding rectangle, and needs to be updated according to the actual position
// Used for enemy sprite to find their required spot to attack the player
Rect Sprite::rangeRect(const Rect &bounds, const Range &range) {
Rect rect;
rect.x = bounds.x + range._val[_dir].x;
rect.y = bounds.y + range._val[_dir].y;
rect.w = range._val[_dir].w;
rect.h = range._val[_dir].h;
return rect;
}
// Used for focusing the camera on the sprite
Vector2i Sprite::camFocus() {
Vector2i v;
v.x = _pos.x + _animSet._focus.x;
v.y = _pos.y + _animSet._focus.y;
return v;
}
//------------------------------------------------------------------------
// Purpose: Draw the sprite
//------------------------------------------------------------------------
void Sprite::draw(pyrodactyl::event::Info &info, const Rect &camera) {
using namespace pyrodactyl::image;
using namespace pyrodactyl::text;
int x = _pos.x - camera.x - _animSet.anchorX(_dir), y = _pos.y - camera.y - _animSet.anchorY(_dir);
// Draw the shadow image relative to the bottom center of the sprite
ShadowOffset sh = _animSet.shadow(_dir);
if (sh._valid) {
// Draw using custom offset
g_engine->_imageManager->draw(x + _clip.w / 2 - _animSet._shadow._size.x + sh.x,
y + _clip.h - _animSet._shadow._size.y + sh.y,
_animSet._shadow._img);
} else {
// Draw using default offset
g_engine->_imageManager->draw(x + _clip.w / 2 - _animSet._shadow._size.x + _animSet._shadow._offset.x,
y + _clip.h - _animSet._shadow._size.y + _animSet._shadow._offset.y,
_animSet._shadow._img);
}
g_engine->_imageManager->draw(x, y, _image, &_clip, _animSet.flip(_dir));
_imgEff.draw(x, y);
if (g_engine->_debugDraw & DRAW_SPRITE_BOUNDS) {
// Nice boxes for the frames and boxV, boxD
Rect bounds = boundRect(), vul = boxV(), dmg = boxD(), debugpos = posRect();
bounds.draw(-camera.x, -camera.y);
debugpos.draw(-camera.x, -camera.y, 255, 255, 255);
dmg.draw(-camera.x, -camera.y, 255, 0, 0);
vul.draw(-camera.x, -camera.y, 0, 0, 255);
FightMove fm;
if (_animSet._fight.nextMove(fm)) {
Rect actualRange;
actualRange.x = bounds.x + fm._ai._range._val[_dir].x;
actualRange.y = bounds.y + fm._ai._range._val[_dir].y;
actualRange.w = fm._ai._range._val[_dir].w;
actualRange.h = fm._ai._range._val[_dir].h;
actualRange.draw(-camera.x, -camera.y, 255, 0, 255);
}
}
if (g_engine->_debugDraw & DRAW_PATHING) {
for (const auto &iter : _pathing._vSolution) {
bool nextToWall = false;
for (const auto &neighbor : iter->_neighborNodes) {
if (neighbor->getMovementCost() < 0 || neighbor->getMovementCost() > 1) {
nextToWall = true;
break;
}
}
if (nextToWall)
iter->getRect().draw(-camera.x, -camera.y, 0, 0, 0, 254);
else
iter->getRect().draw(-camera.x, -camera.y, 200, 200, 0, 254);
}
if (_pathing._goalTile && _pathing._startTile) {
_pathing._goalTile->getRect().draw(-camera.x, -camera.y, 0, 0, 200, 254);
_pathing._startTile->getRect().draw(-camera.x, -camera.y, 0, 200, 0, 254);
}
Rect destinationRect = Rect((int)_pathing._destination.x - 5,
(int)_pathing._destination.y - 5,
10,
10);
destinationRect.draw(-camera.x, -camera.y, 0, 200, 0, 254);
}
}
void Sprite::drawPopup(pyrodactyl::ui::ParagraphData &pop, const Rect &camera) {
// This is different from draw because we draw the popup centered over the head
int x = _pos.x - camera.x - _animSet.anchorX(_dir) + (_animSet._bounds.w / 2);
int y = _pos.y - camera.y - _animSet.anchorY(_dir);
_popup.draw(x, y, pop, camera);
}
//------------------------------------------------------------------------
// Purpose: Handle the movement in a level for the player only
//------------------------------------------------------------------------
void Sprite::handleEvents(Info &info, const Rect &camera, const SpriteConstant &sc, const Common::Event &event) {
int num = 0;
info.statGet(_id, pyrodactyl::stat::STAT_SPEED, num);
++num;
float playerSpeed = static_cast<float>(num);
// This is for Diablo style hold-mouse-button-in-direction-of-movement
// This is only used if - point and click movement isn't being used, cursor is not inside the hud, the cursor is a normal cursor and the mouse is pressed
if (!_aiData._dest._active && !g_engine->_mouse->_insideHud && !g_engine->_mouse->_hover && g_engine->_mouse->pressed()) {
// To find where the click is w.r.t sprite, we need to see where it is being drawn
int x = _pos.x - camera.x - _animSet.anchorX(_dir), y = _pos.y - camera.y - _animSet.anchorY(_dir);
// Just use the bound rectangle dimensions
Rect b = boundRect();
int w = b.w, h = b.h;
// X axis
if (g_engine->_mouse->_motion.x > x + w)
xVel(playerSpeed * sc._walkVelMod.x);
else if (g_engine->_mouse->_motion.x < x)
xVel(-playerSpeed * sc._walkVelMod.x);
else
xVel(0.0f);
// Y axis
if (g_engine->_mouse->_motion.y > y + h)
yVel(playerSpeed * sc._walkVelMod.y);
else if (g_engine->_mouse->_motion.y < y)
yVel(-playerSpeed * sc._walkVelMod.y);
else
yVel(0.0f);
} else { // Keyboard movement
// Disable destination as soon as player presses a direction key
// X axis
if (g_engine->_inputManager->state(IG_LEFT)) {
_aiData._dest._active = false;
xVel(-playerSpeed * sc._walkVelMod.x);
} else if (g_engine->_inputManager->state(IG_RIGHT)) {
_aiData._dest._active = false;
xVel(playerSpeed * sc._walkVelMod.x);
} else if (!_aiData._dest._active)
xVel(0.0f);
// Y axis
if (g_engine->_inputManager->state(IG_UP)) {
_aiData._dest._active = false;
yVel(-playerSpeed * sc._walkVelMod.y);
} else if (g_engine->_inputManager->state(IG_DOWN)) {
_aiData._dest._active = false;
yVel(playerSpeed * sc._walkVelMod.y);
} else if (!_aiData._dest._active)
yVel(0.0f);
}
updateMove(_input.handleEvents(event));
// This is to prevent one frame of drawing with incorrect parameters
animate(info);
}
//------------------------------------------------------------------------
// Purpose: Set destination for sprite movement
//------------------------------------------------------------------------
void Sprite::setDestPathfinding(const Vector2i &dest, bool reachable) {
_aiData.dest(dest, true);
_pathing.setDestination(dest, reachable);
}
//------------------------------------------------------------------------
// Purpose: Walking animation
//------------------------------------------------------------------------
void Sprite::walk(const pyrodactyl::people::PersonState &pst) {
_imgEff._visible = false;
bool firstX = true;
if (_aiData._dest._active) {
Rect b = boundRect();
if (_pos.x - _aiData._dest.x > -b.w && _pos.x - _aiData._dest.x < b.w)
firstX = false;
}
bool reset = _animSet._walk.type(_vel, _dir, pst, firstX);
if (reset)
_animSet._walk.resetClip(_dir);
walk(reset);
}
void Sprite::walk(const bool &reset) {
if (_animSet._walk.updateClip(_dir, reset)) {
_clip = _animSet._walk.clip(_dir);
_boxV = _animSet._walk.boxV(_dir);
}
}
//------------------------------------------------------------------------
// Purpose: Decide which animation to play
//------------------------------------------------------------------------
void Sprite::animate(Info &info) {
if (_input.idle())
walk(info.state(_id));
else
updateFrame(info.state(_id));
}
void Sprite::animate(const pyrodactyl::people::PersonState &pst) {
walk(pst);
}
//------------------------------------------------------------------------
// Purpose: We need to find if the vulnerable area of this sprite collides
// with hitbox (usually the damage area of another sprite)
//------------------------------------------------------------------------
bool Sprite::fightCollide(Rect hitbox, Rect enemyBounds, Range &range, const SpriteConstant &sc) {
Rect bounds = boundRect();
if (range._valid) {
Rect actualRange = rangeRect(bounds, range);
// The second part is a sanity check so the stray hitbox of a sprite 1000 pixels below does not cause damage
if (hitbox.collide(actualRange) && abs(bounds.y + bounds.h - enemyBounds.y - enemyBounds.h) < sc._planeW)
return true;
} else {
if (hitbox.collide(bounds) && abs(bounds.y + bounds.h - enemyBounds.y - enemyBounds.h) < sc._planeW)
return true;
}
return false;
}
//------------------------------------------------------------------------
// Purpose: Update the frame info of the sprite
//------------------------------------------------------------------------
void Sprite::updateFrame(const pyrodactyl::people::PersonState &pst, const bool &repeat) {
FrameUpdateResult res = _animSet._fight.updateFrame(_dir);
if (res == FUR_SUCCESS) {
assignFrame();
} else if (res == FUR_FAIL) {
_damageDone = false;
stop();
if (repeat == false)
resetFrame(pst);
else
_animSet._fight.frameIndex(0);
}
}
void Sprite::assignFrame() {
FightAnimFrame faf;
if (_animSet._fight.curFrame(faf, _dir)) {
_clip = faf._clip;
boxV(faf._boxV);
boxD(faf._boxD);
_pos.x += faf._delta.x;
_pos.y += faf._delta.y;
_input._state = faf._state;
}
}
//------------------------------------------------------------------------
// Purpose: Update the move info of the player sprite
//------------------------------------------------------------------------
void Sprite::updateMove(const FightAnimationType &combo) {
if (combo != FA_IDLE) {
if (_input.idle())
forceUpdateMove(combo);
else {
FightAnimFrame faf;
if (_animSet._fight.curFrame(faf, _dir))
if (faf._branch)
forceUpdateMove(combo);
}
}
}
void Sprite::forceUpdateMove(const FightAnimationType &combo) {
uint index = _animSet._fight.findMove(combo, _input._state);
forceUpdateMove(index);
}
//------------------------------------------------------------------------
// Purpose: Update the move info of the AI or player sprite
//------------------------------------------------------------------------
void Sprite::updateMove(const uint &index) {
if (_input.idle())
forceUpdateMove(index);
}
void Sprite::forceUpdateMove(const uint &index) {
if (_animSet._fight.forceUpdate(index, _input, _dir)) {
// This sets the sprite input to the current move input
_animSet._fight.curCombo(_input);
stop();
assignFrame();
} else
_input.reset();
}
//------------------------------------------------------------------------
// Purpose: Reset the frame info of the sprite
//------------------------------------------------------------------------
void Sprite::resetFrame(const pyrodactyl::people::PersonState &pst) {
_input.reset();
walk(true);
_animSet._fight.reset();
_boxD.w = 0;
_boxD.h = 0;
}
//------------------------------------------------------------------------
// Purpose: Check if both sprites in the same plane
//------------------------------------------------------------------------
bool Sprite::damageValid(Sprite &s, const SpriteConstant &sc) {
// Get the y coordinates where these sprites are standing
float Y = _pos.y + _clip.h, SY = s._pos.y + s._clip.h;
if (abs(Y - SY) < sc._planeW)
return true;
return false;
}
//------------------------------------------------------------------------
// Purpose: Checks about dealing damage to sprite
//------------------------------------------------------------------------
void Sprite::calcProperties(Info &info) {
_visible.evaluate(info);
_animSet._fight.evaluate(info);
}
//------------------------------------------------------------------------
// Purpose: Checks about dealing damage to sprite
//------------------------------------------------------------------------
bool Sprite::takingDamage(Sprite &sp, const SpriteConstant &sc) {
if (damageValid(sp, sc))
if (boxV().w > 0 && boxV().h > 0 && sp.boxD().w > 0 && sp.boxD().h > 0)
if (boxV().collide(sp.boxD()))
return true;
/*Common::String words = NumberToString(BoxV().x) + " " + NumberToString(BoxV().y)+ " " + NumberToString(BoxV().w)
+ " " + NumberToString(BoxV().h) + "\n" + NumberToString(sp.BoxD().x) + " " + NumberToString(sp.BoxD().y)
+ " " + NumberToString(sp.BoxD().w) + " " + NumberToString(sp.BoxD().h) + "\n";
fprintf(stdout,words.c_str());*/
return false;
}
//------------------------------------------------------------------------
// Purpose: We know we are taking damage, now is the time to update stats
//------------------------------------------------------------------------
void Sprite::takeDamage(Info &info, Sprite &s) {
using namespace pyrodactyl::stat;
using namespace pyrodactyl::music;
FightMove f;
if (s._animSet._fight.curMove(f) && info.personValid(s.id()) && info.personValid(_id)) {
int dmg = -1 * (f._eff._dmg + info.personGet(s.id())._stat._val[STAT_ATTACK]._cur - info.personGet(_id)._stat._val[STAT_DEFENSE]._cur);
if (dmg >= 0)
dmg = -1;
info.statChange(_id, STAT_HEALTH, dmg);
int health = 1;
info.statGet(_id, STAT_HEALTH, health);
// Play death animation if dead, hurt animation otherwise
if (health <= 0 && f._eff._death != -1)
forceUpdateMove(f._eff._death);
else if (f._eff._hurt != -1)
forceUpdateMove(f._eff._hurt);
g_engine->_musicManager->playEffect(f._eff._hit, 0);
_imgEff = f._eff._img;
}
stop();
s._damageDone = true;
}
//------------------------------------------------------------------------
// Purpose: We have 2 sprites, *this and s.
// Check damage between s.boxV and this->boxD and vice versa
//------------------------------------------------------------------------
void Sprite::exchangeDamage(Info &info, Sprite &s, const SpriteConstant &sc) {
using namespace pyrodactyl::people;
using namespace pyrodactyl::stat;
// This object is taking damage from s
if (!s._damageDone && takingDamage(s, sc))
takeDamage(info, s);
// Is the other sprite taking damage from this sprite?
if (!_damageDone && s.takingDamage(*this, sc))
s.takeDamage(info, *this);
// We change the animation to dying in order to give time to the death animation to play out
int num = 0;
info.statGet(s.id(), STAT_HEALTH, num);
if (num <= 0) {
info.state(s.id(), PST_DYING);
info.statChange(s.id(), STAT_HEALTH, 1);
}
info.statGet(_id, STAT_HEALTH, num);
if (num <= 0) {
info.state(_id, PST_DYING);
info.statChange(_id, STAT_HEALTH, 1);
}
}
//------------------------------------------------------------------------
// Purpose: Update status of ambient dialog via popup object
//------------------------------------------------------------------------
void Sprite::internalEvents(Info &info, const Common::String &player_id, Common::Array<EventResult> &result, Common::Array<EventSeqInfo> &end_seq) {
_popup.internalEvents(info, player_id, result, end_seq);
}
//------------------------------------------------------------------------
// Purpose: Save all sprite positions to save file
//------------------------------------------------------------------------
void Sprite::saveState(rapidxml::xml_document<> &doc, rapidxml::xml_node<char> *root) {
root->append_attribute(doc.allocate_attribute("id", _id.c_str()));
root->append_attribute(doc.allocate_attribute("x", g_engine->_stringPool->get(_pos.x)));
root->append_attribute(doc.allocate_attribute("y", g_engine->_stringPool->get(_pos.y)));
}
//------------------------------------------------------------------------
// Purpose: Load all sprite positions from save file
//------------------------------------------------------------------------
void Sprite::loadState(rapidxml::xml_node<char> *node) {
loadNum(_pos.x, "x", node);
loadNum(_pos.y, "y", node);
}
} // End of namespace Crab

View File

@@ -0,0 +1,295 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_SPRITE_H
#define CRAB_SPRITE_H
#include "crab/collision.h"
#include "crab/timer.h"
#include "crab/ai/spriteai.h"
#include "crab/ai/SpriteConstant.h"
#include "crab/animation/animset.h"
#include "crab/animation/PopUp.h"
#include "crab/level/LevelExit.h"
namespace Crab {
class PathfindingAgent;
namespace pyrodactyl {
namespace anim {
class Sprite {
protected:
// Used to sync sprite to character
Common::String _id;
// The position of the sprite
Vector2i _pos;
// The velocity of the sprite, target velocity is our maximum velocity
Vector2f _vel, _target;
// The image of the sprite and it's dimensions
ImageKey _image;
Vector2i _imgSize;
// Clip is the portion of the sprite map to be drawn
Rect _clip;
// The hit boxes of the character - v is vulnerable hit box, d is damage hit box
Rect _boxV, _boxD;
// The direction the sprite is facing
Direction _dir;
// The currently playing image effect
ImageEffect _imgEff;
// The complete animation set for the sprite
AnimSet _animSet;
// The conditions for sprite visibility
pyrodactyl::event::TriggerSet _visible;
// Current sprite combo and input for the sprite
pyrodactyl::input::FightInput _input;
// Have we done damage for this frame - used to avoid repeated damage for the same frame
bool _damageDone;
// Dialog shown without events
PopUpCollection _popup;
protected:
void resetFrame(const pyrodactyl::people::PersonState &pst);
bool fightCollide(Rect hitbox, Rect enemy_bounds, Range &range, const pyrodactyl::ai::SpriteConstant &sc);
bool damageValid(Sprite &s, const pyrodactyl::ai::SpriteConstant &sc);
void clip(const Rect &rect) { _clip = rect; }
void boxV(const Rect &rect) { _boxV = rect; }
void boxD(const Rect &rect) { _boxD = rect; }
public:
// The AI data for the sprite
pyrodactyl::ai::SpriteAIData _aiData;
PathfindingAgent _pathing;
// The modifier applied to the sprite velocity
Vector2f _velMod;
// The layer associated with the sprite (used to show/hide sprite according to auto hide layers)
int _layer;
// Is the mouse hovering over this sprite?
bool _hover;
// The list of collisions currently taking place with the sprite
Common::List<CollisionData> _collideData;
Sprite();
~Sprite() {
_pathing.reset();
}
void visible(bool val) {
_visible.result(val);
}
bool visible() {
return _visible.result();
}
void calcProperties(pyrodactyl::event::Info &info);
void x(int X) {
_pos.x = X;
}
void y(int Y) {
_pos.y = Y;
}
int x() {
return _pos.x;
}
int y() {
return _pos.y;
}
void walkPattern(const pyrodactyl::ai::MovementSet &set) {
_aiData._walk = set;
}
void move(const pyrodactyl::ai::SpriteConstant &sc);
// Resolve collisions for polygons we want to be outside of
void resolveCollide();
// Resolve collisions for the walk rectangle
void resolveInside(Rect collider);
void stop() {
_vel.Set();
_target.Set();
_aiData._dest._active = false;
}
void inputStop() {
_input.reset();
}
void xVel(const float &val) {
_target.x = val * _velMod.x;
}
void yVel(const float &val) {
_target.y = val * _velMod.y;
}
float xVel() {
return _vel.x;
}
float yVel() {
return _vel.y;
}
Vector2f vel() {
return _vel;
}
const Common::String &id() {
return _id;
}
int w() {
return _clip.w;
}
int h() {
return _clip.h;
}
const ImageKey &img() {
return _image;
}
Rect dialogClip(const pyrodactyl::people::PersonState &state) {
return _animSet._walk.dialogClip(state);
}
void dialogUpdateClip(const pyrodactyl::people::PersonState &state) {
_animSet._walk.updateClip(state);
}
bool popupShow() {
return _popup.show();
}
Rect boundRect();
Rect boxV();
Rect boxD();
Rect posRect();
Rect rangeRect(const Rect &bounds, const Range &range);
Vector2i camFocus();
double distSq(const Sprite &s);
void effectImg(bool vis) {
_imgEff._visible = vis;
}
bool lastFrame() {
return _animSet._fight.lastFrame();
}
bool takingDamage(Sprite &sp, const pyrodactyl::ai::SpriteConstant &sc);
void takeDamage(pyrodactyl::event::Info &info, Sprite &s);
void exchangeDamage(pyrodactyl::event::Info &info, Sprite &s, const pyrodactyl::ai::SpriteConstant &sc);
void load(rapidxml::xml_node<char> *node, Common::Array<Common::Path> &animations);
void internalEvents(pyrodactyl::event::Info &info, const Common::String &player_id,
Common::Array<pyrodactyl::event::EventResult> &result, Common::Array<pyrodactyl::event::EventSeqInfo> &end_seq);
void draw(pyrodactyl::event::Info &info, const Rect &camera);
void drawPopup(pyrodactyl::ui::ParagraphData &pop, const Rect &camera);
void walk(const bool &reset);
void walk(const pyrodactyl::people::PersonState &pst);
void updateFrame(const pyrodactyl::people::PersonState &pst, const bool &repeat = false);
void assignFrame();
void updateMove(const pyrodactyl::input::FightAnimationType &combo);
void forceUpdateMove(const pyrodactyl::input::FightAnimationType &combo);
void updateMove(const uint &index);
void forceUpdateMove(const uint &index);
// Set sprite destination
void setDestPathfinding(const Vector2i &dest, bool reachable = true);
// Used for sprite movement controlled by player input (usually the player sprite)
void handleEvents(pyrodactyl::event::Info &info, const Rect &camera, const pyrodactyl::ai::SpriteConstant &sc, const Common::Event &event);
// This is for sprites with valid object ids
void animate(pyrodactyl::event::Info &info);
// This is for objects without valid ids - like <background> and <fly> sprites
void animate(const pyrodactyl::people::PersonState &pst);
// AI behavior routine for sprites attacking the player
void attack(pyrodactyl::event::Info &info, Sprite &targetSp, const pyrodactyl::ai::SpriteConstant &sc);
// AI behavior routine for sprites running away from the player
// Requires every exit in the level be accessible
void flee(pyrodactyl::event::Info &info, Common::Array<pyrodactyl::level::Exit> &areaExit, const pyrodactyl::ai::SpriteConstant &sc);
// Used for sprites that fly across semi randomly on the screen
void flyAround(const Rect &camera, const pyrodactyl::ai::SpriteConstant &sc);
// Used for the player destination movement
void moveToDest(pyrodactyl::event::Info &info, const pyrodactyl::ai::SpriteConstant &sc);
void moveToDestPathfinding(pyrodactyl::event::Info &info, const pyrodactyl::ai::SpriteConstant &sc);
// Used for AI movement - returns true if at the destination, false otherwise
bool moveToLoc(Vector2i &dest, const float &vel, const pyrodactyl::ai::SpriteConstant &sc);
bool moveToLocPathfinding(Vector2i &dest, const float &vel, const pyrodactyl::ai::SpriteConstant &sc);
void saveState(rapidxml::xml_document<> &doc, rapidxml::xml_node<char> *root);
void loadState(rapidxml::xml_node<char> *node);
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_SPRITE_H

View File

@@ -0,0 +1,155 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/animation/walkframes.h"
namespace Crab {
using namespace pyrodactyl::anim;
using namespace pyrodactyl::people;
//------------------------------------------------------------------------
// Purpose: Loader Function
//------------------------------------------------------------------------
void WalkFrames::load(rapidxml::xml_node<char> *node) {
if (nodeValid("stand", node))
_set[WT_STAND].load(node->first_node("stand"));
if (nodeValid("walk", node))
_set[WT_WALK].load(node->first_node("walk"));
if (nodeValid("fight", node))
_set[WT_FIGHT].load(node->first_node("fight"));
if (nodeValid("ko", node))
_set[WT_KO].load(node->first_node("ko"));
}
//------------------------------------------------------------------------
// Purpose: Used for walking inside levels
//------------------------------------------------------------------------
bool WalkFrames::updateClip(Direction d, bool reset) {
if (_timer.ticks() > _set[_cur].frames[d].currentFrame()._repeat || reset) {
_timer.start();
return _set[_cur].frames[d].updateClip();
}
return false;
}
void WalkFrames::resetClip(Direction d) {
_set[_cur].frames[d].reset();
_timer.start();
}
//------------------------------------------------------------------------
// Purpose: Used inside dialog box
//------------------------------------------------------------------------
void WalkFrames::updateClip(WalkAnimType type, Direction d) {
if (!_timer.started())
_timer.start();
if (_timer.ticks() > _set[type].frames[d].currentFrame()._repeat) {
_set[type].frames[d].updateClip();
_timer.start();
}
}
Rect WalkFrames::dialogClip(const PersonState &state) {
if (state == PST_FIGHT)
return _set[WT_FIGHT].frames[DIRECTION_DOWN].currentFrame()._clip;
else if (state == PST_KO)
return _set[WT_KO].frames[DIRECTION_DOWN].currentFrame()._clip;
return _set[WT_STAND].frames[DIRECTION_DOWN].currentFrame()._clip;
}
void WalkFrames::updateClip(const PersonState &state) {
if (state == PST_FIGHT)
updateClip(WT_FIGHT, DIRECTION_DOWN);
else if (state == PST_KO)
updateClip(WT_KO, DIRECTION_DOWN);
else
updateClip(WT_STAND, DIRECTION_DOWN);
}
//------------------------------------------------------------------------
// Purpose: Decide direction and type of animation, return whether it has changed
//------------------------------------------------------------------------
bool WalkFrames::type(const Vector2f &vel, Direction &dir, const pyrodactyl::people::PersonState &pst, const bool &firstX) {
Direction prevDir = dir;
WalkAnimType prevType = _cur;
if (pst == PST_KO) {
_cur = WT_KO;
} else if (firstX) {
// If we prioritize the X direction, X velocity is checked first for direction and then Y velocity
if (vel.x > 0) {
dir = DIRECTION_RIGHT;
_cur = WT_WALK;
} else if (vel.x < 0) {
dir = DIRECTION_LEFT;
_cur = WT_WALK;
} else if (vel.y > 0) {
dir = DIRECTION_DOWN;
_cur = WT_WALK;
} else if (vel.y < 0) {
dir = DIRECTION_UP;
_cur = WT_WALK;
} else {
_cur = WT_STAND;
}
} else {
// If we prioritize the Y direction, Y velocity is checked first for direction and then Y velocity
if (vel.y > 0) {
dir = DIRECTION_DOWN;
_cur = WT_WALK;
} else if (vel.y < 0) {
dir = DIRECTION_UP;
_cur = WT_WALK;
} else if (vel.x > 0) {
dir = DIRECTION_RIGHT;
_cur = WT_WALK;
} else if (vel.x < 0) {
dir = DIRECTION_LEFT;
_cur = WT_WALK;
} else {
_cur = WT_STAND;
}
}
if (prevDir != dir || prevType != _cur)
return true;
return false;
}
} // End of namespace Crab

View File

@@ -0,0 +1,130 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_WALKFRAMES_H
#define CRAB_WALKFRAMES_H
#include "crab/timer.h"
#include "crab/ai/movement.h"
#include "crab/animation/animframe.h"
#include "crab/people/personbase.h"
namespace Crab {
namespace pyrodactyl {
namespace anim {
enum WalkAnimType {
WT_STAND,
WT_WALK,
WT_FIGHT,
WT_KO,
WT_TOTAL
};
class WalkFrames {
struct WalkFrameSet {
AnimationFrames frames[DIRECTION_TOTAL];
void load(rapidxml::xml_node<char> *node) {
frames[DIRECTION_DOWN].load(node->first_node("down"));
frames[DIRECTION_UP].load(node->first_node("up"));
frames[DIRECTION_LEFT].load(node->first_node("left"));
frames[DIRECTION_RIGHT].load(node->first_node("right"));
}
};
// The walking animations of the sprite
WalkFrameSet _set[WT_TOTAL];
// The current walking animation
WalkAnimType _cur;
// The timers used for animation playing
Timer _timer;
// Dialog box related
void updateClip(WalkAnimType type, Direction d);
public:
WalkFrames() {
_cur = WT_STAND;
_timer.start();
}
~WalkFrames() {}
void load(rapidxml::xml_node<char> *node);
bool updateClip(Direction d, bool reset);
void resetClip(Direction d);
void type(WalkAnimType type) {
_cur = type;
}
WalkAnimType type() {
return _cur;
}
bool type(const Vector2f &vel, Direction &dir, const pyrodactyl::people::PersonState &pst, const bool &first_x);
const Rect &clip(Direction d) {
return _set[_cur].frames[d].currentFrame()._clip;
}
const Rect &boxV(Direction d) {
return _set[_cur].frames[d].currentFrame()._boxV;
}
const TextureFlipType &flip(Direction d) {
return _set[_cur].frames[d]._flip;
}
const ShadowOffset &shadow(Direction d) {
return _set[_cur].frames[d]._shadow;
}
int anchorX(Direction d) {
return _set[_cur].frames[d].currentFrame()._anchor.x;
}
int anchorY(Direction d) {
return _set[_cur].frames[d].currentFrame()._anchor.y;
}
// Dialog box related
Rect dialogClip(const pyrodactyl::people::PersonState &state);
void updateClip(const pyrodactyl::people::PersonState &state);
};
} // End of namespace anim
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_WALKFRAMES_H

201
engines/crab/app.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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/app.h"
#include "common/events.h"
#include "graphics/screen.h"
#include "crab/crab.h"
#include "crab/game.h"
#include "crab/mainmenu.h"
#include "crab/splash.h"
namespace Crab {
void App::run() {
// State IDs
GameStateID currentStateId = GAMESTATE_NULL, nextStateId = GAMESTATE_TITLE;
bool shouldChangeState = true;
// Set the current game state object
GameState *currentState = nullptr;
Timer fps;
Common::Event e;
int fpscount = 0, fpsval = 1, lasts = 0;
_game = new Game();
g_engine->_filePath->load("res/paths.xml");
loadSettings("res/settings.xml");
g_engine->_screenSettings->_inGame = false;
// While the user hasn't quit - This is the main game loop
while (currentStateId != GAMESTATE_EXIT && !g_engine->shouldQuit()) {
// Start the frame timer
fps.start();
// Change state if needed
if (shouldChangeState) {
// Delete the current state
if (currentState != _game) {
delete currentState;
currentState = nullptr;
}
if (nextStateId == GAMESTATE_EXIT)
break;
// Change the state
switch (nextStateId) {
case GAMESTATE_TITLE:
currentState = new Splash();
g_engine->_screenSettings->_inGame = false;
break;
case GAMESTATE_MAIN_MENU:
currentState = new MainMenu();
g_engine->_screenSettings->_inGame = false;
break;
case GAMESTATE_NEW_GAME:
_game->startNewGame();
currentState = _game;
g_engine->_screenSettings->_inGame = true;
break;
case GAMESTATE_LOAD_GAME:
currentState = _game;
g_engine->_screenSettings->_inGame = true;
break;
default:
// Encountering an undefined state, exit with an error code
return;
}
// Change the current state ID
currentStateId = nextStateId;
// NULL the next state ID
nextStateId = GAMESTATE_NULL;
// No need to change state until further notice
shouldChangeState = false;
}
// Do state InternalEvents
currentState->internalEvents(shouldChangeState, nextStateId);
while (g_system->getEventManager()->pollEvent(e)) {
switch (e.type) {
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
g_engine->_inputManager->_ivState[e.customType] = true;
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
g_engine->_inputManager->_ivState[e.customType] = false;
break;
// explicitly specify the default block to turn off unhandled case warnings
default:
break;
}
// Do state Event handling
currentState->handleEvents(e, shouldChangeState, nextStateId);
}
// Do we have to reposition our interface?
if (g_engine->_screenSettings->_changeInterface) {
currentState->setUI();
g_engine->_screenSettings->_changeInterface = false;
}
// Do state Drawing
currentState->draw();
if (g_system->getMillis() - lasts > 1000) {
lasts = g_system->getMillis();
fpsval = fpscount;
fpscount = 1;
} else
++fpscount;
if ((g_engine->_debugDraw & DRAW_FPS) && currentStateId >= 0)
g_engine->_textManager->draw(0, 0, numberToString(fpsval).c_str(), 0);
g_engine->_screen->update();
// Cap the frame rate
if (fps.ticks() < 1000u / g_engine->_screenSettings->_fps) {
uint32 delay = (1000u / g_engine->_screenSettings->_fps) - fps.ticks();
g_system->delayMillis(delay);
}
}
if (currentState != _game)
delete currentState;
delete _game;
}
void App::loadSettings(const Common::Path &filename) {
XMLDoc settings(filename);
if (settings.ready()) {
rapidxml::xml_node<char> *node = settings.doc()->first_node("settings");
if (nodeValid(node)) {
// Load the version
loadNum(g_engine->_screenSettings->_version, "version", node);
// Load screen settings
if (nodeValid("screen", node))
g_engine->_screenSettings->load(node->first_node("screen"));
// Start the sound subsystem
g_engine->_musicManager->load(node);
g_system->lockMouse(g_engine->_screenSettings->_mouseTrap);
}
}
}
App::~App() {
// Return setting to default when game exits
g_system->lockMouse(false);
g_engine->_imageManager->quit();
g_engine->_musicManager->quit();
g_engine->_textManager->quit();
g_engine->_loadingScreen->quit();
g_engine->_mouse->quit();
}
} // End of namespace Crab

54
engines/crab/app.h Normal file
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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_APP_H
#define CRAB_APP_H
#include "common/path.h"
namespace Crab {
class Game;
class App {
private:
Game *_game;
void loadSettings(const Common::Path &filename);
public:
~App();
void run();
Game *getGame() const {
return _game;
}
};
} // End of namespace Crab
#endif // CRAB_APP_H

View File

@@ -0,0 +1,60 @@
/* 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 CRAB_BACKINSERTER_H
#define CRAB_BACKINSERTER_H
#include "common/util.h"
namespace Crab {
template<class Container>
class backInserterIterator {
protected:
Container *_container;
public:
explicit backInserterIterator(Container &x) : _container(&x) {}
backInserterIterator &operator=(const typename Container::value_type &value) {
*_container += value;
return *this;
}
backInserterIterator &operator=(const typename Container::value_type &&value) {
*_container += Common::move(value);
return *this;
}
backInserterIterator &operator*() { return *this; }
backInserterIterator &operator++() { return *this; }
backInserterIterator &operator++(int) { return *this; }
};
template<class Container>
backInserterIterator<Container>
backInserter(Container &x) {
return backInserterIterator<Container>(x);
}
} // End of namespace Crab
#endif

223
engines/crab/collision.cpp Normal file
View File

@@ -0,0 +1,223 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/collision.h"
namespace Crab {
// Find if a line and a rectangle intersect
bool collideLineRect(int p0X, int p0Y, int p1X, int p1Y, const Rect &rect) {
int q0_x = rect.x;
int q0_y = rect.y;
int q1_x = rect.x + rect.w;
int q1_y = rect.y;
if (collideLine<int>(p0X, p0Y, p1X, p1Y, q0_x, q0_y, q1_x, q1_y))
return true;
q0_x = rect.x;
q0_y = rect.y;
q1_x = rect.x;
q1_y = rect.y + rect.h;
if (collideLine<int>(p0X, p0Y, p1X, p1Y, q0_x, q0_y, q1_x, q1_y))
return true;
q0_x = rect.x + rect.w;
q0_y = rect.y;
q1_x = rect.x + rect.w;
q1_y = rect.y + rect.h;
if (collideLine<int>(p0X, p0Y, p1X, p1Y, q0_x, q0_y, q1_x, q1_y))
return true;
q0_x = rect.x;
q0_y = rect.y + rect.h;
q1_x = rect.x + rect.w;
q1_y = rect.y + rect.h;
if (collideLine<int>(p0X, p0Y, p1X, p1Y, q0_x, q0_y, q1_x, q1_y))
return true;
return false;
}
// Find if there is a clear line of sight between two rectangles
bool lineOfSight(const Rect &a, const Rect &b, const Rect &obstacle) {
int p0_x = a.x;
int p0_y = a.y;
int p1_x = b.x;
int p1_y = b.y;
if (collideLineRect(p0_x, p0_y, p1_x, p1_y, obstacle))
return false;
p0_x = a.x + a.w;
p0_y = a.y;
p1_x = b.x + b.w;
p1_y = b.y;
if (collideLineRect(p0_x, p0_y, p1_x, p1_y, obstacle))
return false;
p0_x = a.x;
p0_y = a.y + a.h;
p1_x = b.x;
p1_y = b.y + b.h;
if (collideLineRect(p0_x, p0_y, p1_x, p1_y, obstacle))
return false;
p0_x = a.x + a.w;
p0_y = a.y + a.h;
p1_x = b.x + b.w;
p1_y = b.y + b.h;
if (collideLineRect(p0_x, p0_y, p1_x, p1_y, obstacle))
return false;
return true;
}
// float IntervalDistance(float minA, float maxA, float minB, float maxB)
//{
// if (minA < minB)
// return minB - maxA;
//
// return minA - maxB;
// }
//
//// Calculate the projection of a polygon on an axis and returns it as a [min, max] interval
// void ProjectPolygon(const Vector2i &axis, const Polygon2D &poly, float &min, float &max)
//{
// // To project a point on an axis use the dot product
// float d = axis.DotProduct(poly.point[0]);
// min = d;
// max = d;
//
// for(auto i = poly.point.begin(); i != poly.point.end(); ++i)
// {
// d = i->DotProduct(axis);
//
// if (d < min) min = d;
// else if (d > max) max = d;
// }
// }
//
// PolygonCollisionResult Collide(const Polygon2D &polyA, const Polygon2D &polyB, const Vector2i &velocity)
//{
// PolygonCollisionResult result;
//
// int edgeCountA = polyA.edge.size();
// int edgeCountB = polyB.edge.size();
// float minIntervalDistance = std::numeric_limits<float>::max();
// Vector2i translationAxis;
// Vector2i edge;
//
// // Loop through all the edges of both polygons
// for(int edgeIndex = 0; edgeIndex < edgeCountA + edgeCountB; edgeIndex++)
// {
// if (edgeIndex < edgeCountA)
// edge = polyA.edge[edgeIndex];
// else
// edge = polyB.edge[edgeIndex - edgeCountA];
//
// // ===== 1. Find if the Polygon2Ds are currently intersecting =====
//
// // Find the axis perpendicular to the current edge
// Vector2i axis(-edge.y, edge.x);
// axis.Normalize();
//
// // Find the projection of the Polygon2D on the current axis
// float minA = 0; float minB = 0; float maxA = 0; float maxB = 0;
// ProjectPolygon(axis, polyA, minA, maxA);
// ProjectPolygon(axis, polyB, minB, maxB);
//
// // Check if the Polygon2D projections are currently intersecting
// if (IntervalDistance(minA, maxA, minB, maxB) > 0)
// result.intersect = false;
//
// // ===== 2. Now find if the Polygon2Ds *will* intersect =====
//
// // Project the velocity on the current axis
// float velocityProjection = axis.DotProduct(velocity);
//
// // Get the projection of Polygon2D A during the movement
// if (velocityProjection < 0)
// minA += velocityProjection;
// else
// maxA += velocityProjection;
//
// // Do the same test as above for the new projection
// float intervalDistance = IntervalDistance(minA, maxA, minB, maxB);
// if (intervalDistance > 0)
// result.will_intersect = false;
//
// // If the Polygon2Ds are not intersecting and won't intersect, exit the loop
// if (!result.intersect && !result.will_intersect)
// break;
//
// // Check if the current interval distance is the minimum one. If so store
// // the interval distance and the current distance.
// // This will be used to calculate the minimum translation vector
// intervalDistance = abs(intervalDistance);
// if (intervalDistance < minIntervalDistance)
// {
// minIntervalDistance = intervalDistance;
// translationAxis = axis;
//
// Vector2i d, ca, cb;
// ca = polyA.Center();
// cb = polyB.Center();
//
// d.x = ca.x - cb.x;
// d.y = ca.y - cb.y;
//
// if (d.DotProduct(translationAxis) < 0)
// {
// translationAxis.x = -translationAxis.x;
// translationAxis.y = -translationAxis.y;
// }
// }
// }
//
// // The minimum translation vector can be used to push the Polygon2Ds apart.
// // First moves the Polygon2Ds by their velocity
// // then move polyA by MinimumTranslationVector.
// if (result.will_intersect)
// {
// result.mtv.x = translationAxis.x * minIntervalDistance;
// result.mtv.y = translationAxis.y * minIntervalDistance;
// }
//
// return result;
// }
} // End of namespace Crab

53
engines/crab/collision.h Normal file
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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_COLLISION_H
#define CRAB_COLLISION_H
#include "crab/Polygon.h"
#include "crab/Rectangle.h"
namespace Crab {
// Find if a line and a rectangle intersect
bool collideLineRect(int p0X, int p0Y, int p1X, int p1Y, const Rect &rect);
// Find if there is a clear line of sight between two rectangles
bool lineOfSight(const Rect &a, const Rect &b, const Rect &obstacle);
// Find the distance between two points
template<typename T>
double distance2D(const T &p0X, const T &p0Y, const T &p1X, const T &p1Y) {
return sqrt((p0X - p1X) * (p0X - p1X) + (p0Y - p1Y) * (p0Y - p1Y));
}
} // End of namespace Crab
#endif // CRAB_COLLISION_H

35
engines/crab/color.h Normal file
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 CRAB_COLOR_H
#define CRAB_COLOR_H
namespace Crab {
struct Color {
uint8 r;
uint8 g;
uint8 b;
uint8 a;
};
} // End of namespace Crab
#endif // CRAB_COLOR_H

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 crab "CRAB" yes "" "" "16bit highres png freetype2 vorbis"

90
engines/crab/console.cpp Normal file
View File

@@ -0,0 +1,90 @@
/* 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 "crab/crab.h"
#include "crab/console.h"
#include "crab/input/input.h"
namespace Crab {
using namespace pyrodactyl::input;
Console::Console() : GUI::Debugger() {
registerCmd("draw", WRAP_METHOD(Console, cmdDraw));
registerCmd("what", WRAP_METHOD(Console, cmdWhat));
}
Console::~Console() {
}
bool Console::cmdDraw(int argc, const char **argv) {
if (argc > 1) {
for (int i = 1; i < argc; i++) {
if (!scumm_stricmp(argv[i], "OFF"))
g_engine->_debugDraw = 0;
else if (!scumm_stricmp(argv[i], "TMX"))
g_engine->_debugDraw |= DRAW_TMX;
else if (!scumm_stricmp(argv[i], "PROPS"))
g_engine->_debugDraw |= DRAW_PROP_BOUNDS;
else if (!scumm_stricmp(argv[i], "SPRITE"))
g_engine->_debugDraw |= DRAW_SPRITE_BOUNDS;
else if (!scumm_stricmp(argv[i], "PATHING"))
g_engine->_debugDraw |= DRAW_PATHING;
else if (!scumm_stricmp(argv[i], "FPS"))
g_engine->_debugDraw |= DRAW_FPS;
else if (!scumm_stricmp(argv[i], "ALL"))
g_engine->_debugDraw = DRAW_TMX | DRAW_PROP_BOUNDS | DRAW_SPRITE_BOUNDS | DRAW_PATHING | DRAW_FPS;
else
debugPrintf("Valid parameters are 'TMX', 'PROPS', 'SPRITE', 'PATHING', 'FPS', 'ALL' or 'OFF'\n");
}
}
return true;
}
bool Console::cmdWhat(int argc, const char **argv) {
if (argc > 1) {
for (int i = 1; i < argc; i++) {
if (!scumm_stricmp(argv[i], "keymap")) {
switch (g_engine->_inputManager->getKeyBindingMode()) {
case KBM_NONE:
debugPrintf("KBM_NONE\n");
break;
case KBM_GAME:
debugPrintf("KBM_GAME\n");
break;
case KBM_UI:
debugPrintf("KBM_UI\n");
break;
default:
debugPrintf("Unknown KBM\n");
break;
}
} else
debugPrintf("Valid parameters are 'keymap'\n");
}
}
return true;
}
} // End of namespace Crab

41
engines/crab/console.h Normal file
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 CRAB_CONSOLE_H
#define CRAB_CONSOLE_H
#include "gui/debugger.h"
namespace Crab {
class Console : public GUI::Debugger {
private:
bool cmdDraw(int argc, const char **argv);
bool cmdWhat(int argc, const char **argv);
public:
Console();
~Console() override;
};
} // End of namespace Crab
#endif // CRAB_CONSOLE_H

214
engines/crab/crab.cpp Normal file
View File

@@ -0,0 +1,214 @@
/* 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 "engines/util.h"
#include "graphics/cursorman.h"
#include "graphics/managed_surface.h"
#include "graphics/screen.h"
#include "crab/crab.h"
#include "crab/app.h"
#include "crab/console.h"
#include "crab/game.h"
#include "crab/GameParam.h"
#include "crab/LoadingScreen.h"
#include "crab/input/cursor.h"
namespace Crab {
CrabEngine *g_engine;
CrabEngine::CrabEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst),
_gameDescription(gameDesc), _randomSource("Crab") {
_app = nullptr;
_thumbnail = nullptr;
_debugDraw = 0;
g_engine = this;
}
CrabEngine::~CrabEngine() {
delete _app;
delete _screenSettings;
delete _stringPool;
delete _tempData;
delete _filePath;
delete _loadMenu;
delete _optionMenu;
delete _helpScreen;
delete _loadingScreen;
delete _eventStore;
delete _mouse;
delete _musicManager;
delete _inputManager;
delete _textManager;
delete _imageManager;
delete _thumbnail;
delete _screen;
}
uint32 CrabEngine::getFeatures() const {
return _gameDescription->flags;
}
Common::String CrabEngine::getGameId() const {
return _gameDescription->gameId;
}
Common::Error CrabEngine::run() {
// Prefer this format to avoid converting PNG files.
const Graphics::PixelFormat bestFormat = Graphics::PixelFormat::createFormatRGBA32();
// Find a suitable 32-bit format with alpha, currently we don't support other color depths
Common::List<Graphics::PixelFormat> formats = g_system->getSupportedFormats();
for (Common::List<Graphics::PixelFormat>::iterator it = formats.begin(); it != formats.end(); ++it) {
if (it->bytesPerPixel != 4 || it->aBits() == 0) {
it = formats.reverse_erase(it);
} else if (*it == bestFormat) {
formats.clear();
formats.push_back(bestFormat);
break;
}
}
// Even if no formats are found, it's possible that the backend
// can handle software conversion.
if (formats.empty())
formats.push_back(bestFormat);
initGraphics(1280, 720, formats);
_format = g_system->getScreenFormat();
if (_format.bytesPerPixel != 4)
return Common::kUnsupportedColorMode;
_screen = new Graphics::Screen(1280, 720, _format);
_thumbnail = new Graphics::ManagedSurface(1280, 720, _format);
_imageManager = new pyrodactyl::image::ImageManager();
_textManager = new pyrodactyl::text::TextManager();
_inputManager = new pyrodactyl::input::InputManager();
_musicManager = new pyrodactyl::music::MusicManager();
_mouse = new pyrodactyl::input::Cursor();
_eventStore = new pyrodactyl::event::GameEventStore();
_loadingScreen = new LoadingScreen();
_helpScreen = new pyrodactyl::ui::SlideShow();
_optionMenu = new pyrodactyl::ui::OptionMenu();
_loadMenu = new pyrodactyl::ui::FileMenu<pyrodactyl::ui::SaveFileData>();
_filePath = new FilePaths();
_tempData = new TempValue();
_stringPool = new StringPool();
_screenSettings = new ScreenSettings();
_debugDraw = 0;
CursorMan.showMouse(true);
_mouse->reset();
_inputManager->populateKeyTable();
_inputManager->setKeyBindingMode(pyrodactyl::input::KBM_GAME);
// Set the engine's debugger console
setDebugger(new Console());
// If a savegame was selected from the launcher, load it
int saveSlot = ConfMan.getInt("save_slot");
if (saveSlot != -1)
(void)loadGameState(saveSlot);
_app = new App();
_app->run();
return Common::kNoError;
}
void CrabEngine::initializePath(const Common::FSNode &gamePath) {
SearchMan.addDirectory(gamePath, 0, 5);
}
Common::Error CrabEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(getSaveStateName(slot));
if (!saveFile)
return Common::kWritingFailed;
_app->getGame()->saveState(saveFile);
getMetaEngine()->appendExtendedSave(saveFile, getTotalPlayTime(), desc, isAutosave);
saveFile->finalize();
delete saveFile;
return Common::kNoError;
}
Common::Error CrabEngine::loadGameState(int slot) {
Common::Error res = Common::kReadingFailed;
saveAutosaveIfEnabled();
Common::InSaveFile *saveFile = _saveFileMan->openForLoading(getSaveStateName(slot));
if (!saveFile)
return res;
if (_app->getGame()->loadState(saveFile)) {
ExtendedSavegameHeader header;
if (MetaEngine::readSavegameHeader(saveFile, &header))
setTotalPlayTime(header.playtime);
res = Common::kNoError;
}
delete saveFile;
return res;
}
Common::Error CrabEngine::syncGame(Common::Serializer &s) {
// The Serializer has methods isLoading() and isSaving()
// if you need to specific steps; for example setting
// an array size after reading it's length, whereas
// for saving it would write the existing array's length
int dummy = 0;
s.syncAsUint32LE(dummy);
return Common::kNoError;
}
bool CrabEngine::canSaveGameStateCurrently(Common::U32String *msg) {
return _screenSettings->_inGame;
}
void CrabEngine::syncSoundSettings() {
Engine::syncSoundSettings();
_musicManager->syncSettings();
}
} // End of namespace Crab

179
engines/crab/crab.h Normal file
View File

@@ -0,0 +1,179 @@
/* 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 CRAB_H
#define CRAB_H
#include "common/random.h"
#include "common/serializer.h"
#include "graphics/pixelformat.h"
#include "crab/detection.h"
namespace Graphics {
class ManagedSurface;
class Screen;
} // End of namespace Graphics
namespace Crab {
class App;
struct CrabGameDescription;
class LoadingScreen;
struct FilePaths;
struct TempValue;
class StringPool;
class ScreenSettings;
namespace pyrodactyl {
namespace event {
struct GameEventStore;
} // End of namespace event
namespace ui {
class SlideShow;
class OptionMenu;
template<typename FileType>
class FileMenu;
class SaveFileData;
} // End of namespace ui
namespace image {
class ImageManager;
} // End of namespace image
namespace input {
class Cursor;
class InputManager;
} // End of namespace input
namespace music {
class MusicManager;
} // End of namespace music
namespace text {
class TextManager;
} // End of namespace text
} // End of namespace pyrodactyl
enum DebugDraw {
DRAW_TMX = 1 << 0,
DRAW_PROP_BOUNDS = 1 << 1,
DRAW_SPRITE_BOUNDS = 1 << 2,
DRAW_PATHING = 1 << 3,
DRAW_FPS = 1 << 4
};
class CrabEngine : public Engine {
private:
const ADGameDescription *_gameDescription;
Common::RandomSource _randomSource;
App *_app;
protected:
// Engine APIs
Common::Error run() override;
public:
Graphics::Screen *_screen = nullptr;
Graphics::PixelFormat _format;
pyrodactyl::image::ImageManager *_imageManager = nullptr;
pyrodactyl::text::TextManager *_textManager = nullptr;
pyrodactyl::input::InputManager *_inputManager = nullptr;
pyrodactyl::music::MusicManager *_musicManager = nullptr;
pyrodactyl::input::Cursor *_mouse = nullptr;
pyrodactyl::event::GameEventStore *_eventStore = nullptr;
// Should these really be inside the Engine class?
LoadingScreen *_loadingScreen = nullptr;
pyrodactyl::ui::SlideShow *_helpScreen = nullptr;
pyrodactyl::ui::OptionMenu *_optionMenu = nullptr;
pyrodactyl::ui::FileMenu<pyrodactyl::ui::SaveFileData> *_loadMenu = nullptr;
FilePaths *_filePath = nullptr;
TempValue *_tempData = nullptr;
StringPool *_stringPool = nullptr;
ScreenSettings *_screenSettings = nullptr;
// Keeps a copy of latest screen for thumbnail
Graphics::ManagedSurface *_thumbnail;
// What components to draw lines for (if any)
uint32 _debugDraw;
Common::FSNode _gameDataDir;
CrabEngine(OSystem *syst, const ADGameDescription *gameDesc);
~CrabEngine() override;
void initializePath(const Common::FSNode &gamePath) override;
uint32 getFeatures() const;
/**
* Returns the game Id
*/
Common::String getGameId() const;
/**
* Gets a random number
*/
uint32 getRandomNumber(uint maxNum) {
return _randomSource.getRandomNumber(maxNum);
}
bool hasFeature(EngineFeature f) const override {
return (f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime) ||
(f == kSupportsReturnToLauncher);
};
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
return true;
}
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
/**
* Uses a serializer to allow implementing savegame
* loading and saving using a single method
*/
Common::Error syncGame(Common::Serializer &s);
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave) override;
Common::Error loadGameState(int slot) override;
void syncSoundSettings() override;
};
extern CrabEngine *g_engine;
} // End of namespace Crab
#endif // CRAB_H

3
engines/crab/credits.pl Normal file
View File

@@ -0,0 +1,3 @@
begin_section("CRAB");
add_person("Kartik Agarwala", "hax0kartik", "");
end_section();

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 "base/plugins.h"
#include "engines/advancedDetector.h"
#include "crab/detection.h"
const DebugChannelDef CrabMetaEngineDetection::debugFlagList[] = {
{Crab::kDebugGraphics, "Graphics", "Graphics debug level"},
{Crab::kDebugPath, "Path", "Pathfinding debug level"},
{Crab::kDebugFilePath, "FilePath", "File path debug level"},
{Crab::kDebugScan, "Scan", "Scan for unrecognised games"},
{Crab::kDebugScript, "Script", "Enable debug script dump"},
DEBUG_CHANNEL_END};
static const PlainGameDescriptor crabGames[] = {
{"unrest", "Unrest"},
{nullptr, nullptr}};
namespace Crab {
static const ADGameDescription gameDescriptions[] = {
{
"unrest",
"",
AD_ENTRY2s("characters/backer_chars.xml", "d9028280f75836192d93e56df7313d8c", 2517,
"traits/tanya_intelligent.png", "441221becc2b70e7c9d7838d6a8bd990", 12593),
Common::EN_ANY,
Common::kPlatformUnknown, // These files are same on Windows, Linux and MacOS. Therefore, do not specify platform.
ADGF_DROPLANGUAGE | ADGF_DROPPLATFORM,
GUIO3(GUIO_NOSPEECH, GUIO_NOLAUNCHLOAD, GUIO_NOMIDI),
},
{
"unrest",
"Demo",
AD_ENTRY2s("characters/backer_chars.xml", "5ba20126349bc57b3774623a1ec89627", 2127,
"traits/tanya_intelligent.png", "441221becc2b70e7c9d7838d6a8bd990", 12593),
Common::EN_ANY,
Common::kPlatformUnknown, // These files are same on Windows, Linux and MacOS. Therefore, do not specify platform.
ADGF_DROPPLATFORM | ADGF_DEMO,
GUIO3(GUIO_NOSPEECH, GUIO_NOLAUNCHLOAD, GUIO_NOMIDI),
},
AD_TABLE_END_MARKER};
}
CrabMetaEngineDetection::CrabMetaEngineDetection() : AdvancedMetaEngineDetection(Crab::gameDescriptions,
crabGames) {
_flags = kADFlagMatchFullPaths;
}
REGISTER_PLUGIN_STATIC(CRAB_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, CrabMetaEngineDetection);

65
engines/crab/detection.h Normal file
View File

@@ -0,0 +1,65 @@
/* 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 CRAB_DETECTION_H
#define CRAB_DETECTION_H
#include "engines/advancedDetector.h"
namespace Crab {
enum CrabDebugChannels {
kDebugGraphics = 1,
kDebugPath,
kDebugScan,
kDebugFilePath,
kDebugScript,
};
#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
} // End of namespace Crab
class CrabMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
static const DebugChannelDef debugFlagList[];
public:
CrabMetaEngineDetection();
~CrabMetaEngineDetection() override {}
const char *getName() const override {
return "crab";
}
const char *getEngineName() const override {
return "Crab";
}
const char *getOriginalCopyright() const override {
return "Unrest (c) Pyrodactyl Games 2009-2023";
}
const DebugChannelDef *getDebugChannels() const override {
return debugFlagList;
}
};
#endif // CRAB_DETECTION_H

View File

@@ -0,0 +1,100 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/event/EventSeqGroup.h"
namespace Crab {
using namespace pyrodactyl::event;
void EventSeqGroup::endSeq(const uint &id) {
_seq.erase(id);
_end.push_back(id);
}
void EventSeqGroup::addSeq(const uint &id, const Common::Path &path) {
_seq[id].load(path);
}
bool EventSeqGroup::eventInProgress(const uint &id) {
return _seq.contains(id) && _seq[id].eventInProgress();
}
GameEvent *EventSeqGroup::curEvent(const uint &id) {
return _seq[id].currentEvent();
}
void EventSeqGroup::nextEvent(const uint &id, Info &info, const Common::String &playerId,
Common::Array<EventResult> &result, Common::Array<EventSeqInfo> &endSeq, const int choice) {
return _seq[id].nextEvent(info, playerId, result, endSeq, choice);
}
void EventSeqGroup::internalEvents(Info &info) {
for (auto &it : _seq)
it._value.internalEvents(info);
}
bool EventSeqGroup::activeSeq(uint &activeSeq) {
for (auto &i : _seq) {
if (i._value.eventInProgress()) {
activeSeq = i._key;
return true;
}
}
activeSeq = UINT_MAX;
return false;
}
void EventSeqGroup::saveState(rapidxml::xml_document<> &doc, rapidxml::xml_node<char> *root) {
for (const auto &i : _end) {
rapidxml::xml_node<char> *child = doc.allocate_node(rapidxml::node_element, "end");
child->value(g_engine->_stringPool->get(i));
root->append_node(child);
}
for (auto &i : _seq)
i._value.saveState(doc, root, g_engine->_stringPool->get(i._key));
}
void EventSeqGroup::loadState(rapidxml::xml_node<char> *node) {
for (rapidxml::xml_node<char> *i = node->first_node("end"); i != nullptr; i = i->next_sibling("end"))
endSeq(stringToNumber<uint>(i->value()));
for (auto n = node->first_node("set"); n != nullptr; n = n->next_sibling("set"))
if (n->first_attribute("name") != nullptr) {
uint id = stringToNumber<uint>(n->first_attribute("name")->value());
if (_seq.contains(id))
_seq[id].loadState(n);
}
}
} // End of namespace Crab

View File

@@ -0,0 +1,72 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_EVENTSEQGROUP_H
#define CRAB_EVENTSEQGROUP_H
#include "crab/event/EventSequence.h"
namespace Crab {
namespace pyrodactyl {
namespace event {
class EventSeqGroup {
typedef Common::HashMap<uint, EventSequence> SeqMap;
// The event sequences in this group
SeqMap _seq;
// The sequences that have ended in this group
Common::Array<uint> _end;
public:
EventSeqGroup() {}
~EventSeqGroup() {}
void addSeq(const uint &id, const Common::Path &path);
void endSeq(const uint &id);
bool eventInProgress(const uint &id);
bool activeSeq(uint &activeSeq);
GameEvent *curEvent(const uint &id);
void nextEvent(const uint &id, Info &info, const Common::String &playerId, Common::Array<EventResult> &result,
Common::Array<EventSeqInfo> &endSeq, const int choice = -1);
void internalEvents(Info &info);
void saveState(rapidxml::xml_document<> &doc, rapidxml::xml_node<char> *root);
void loadState(rapidxml::xml_node<char> *node);
};
} // End of namespace event
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_EVENTSEQGROUP_H

View File

@@ -0,0 +1,148 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/XMLDoc.h"
#include "crab/event/EventSequence.h"
namespace Crab {
using namespace pyrodactyl::event;
//------------------------------------------------------------------------
// Purpose: Load
//------------------------------------------------------------------------
void EventSequence::load(const Common::Path &filename) {
XMLDoc conf(filename);
if (conf.ready()) {
rapidxml::xml_node<char> *node = conf.doc()->first_node("events");
for (auto n = node->first_node("event"); n != nullptr; n = n->next_sibling("event")) {
GameEvent e(n);
_events.push_back(e);
}
}
}
//------------------------------------------------------------------------
// Purpose: Check for events happening
//------------------------------------------------------------------------
void EventSequence::internalEvents(pyrodactyl::event::Info &info) {
for (const auto &nxe : _next) {
if (nxe < _events.size()) {
if (_events[nxe]._trig.evaluate(info)) {
_eventInProgress = true;
_cur = nxe;
break;
}
}
}
}
//------------------------------------------------------------------------
// Purpose: Go to next event
//------------------------------------------------------------------------
void EventSequence::nextEvent(pyrodactyl::event::Info &info, const Common::String &player_id, Common::Array<EventResult> &result,
Common::Array<EventSeqInfo> &end_seq, int NextEventChoice) {
bool sync = false;
_eventInProgress = false;
// Execute all effects associated with the event
for (auto &i : _events[_cur]._effect)
if (i.execute(info, player_id, result, end_seq))
sync = true;
// Play a notification sound
using namespace pyrodactyl::music;
if (info._sound._repDec)
g_engine->_musicManager->playEffect(g_engine->_musicManager->_repDec, 0);
else if (info._sound._repInc)
g_engine->_musicManager->playEffect(g_engine->_musicManager->_repInc, 0);
else if (info._sound._notify)
g_engine->_musicManager->playEffect(g_engine->_musicManager->_notify, 0);
info._sound._notify = false;
info._sound._repDec = false;
info._sound._repInc = false;
if (!result.empty() || sync) {
EventResult r;
r._type = ER_SYNC;
result.push_back(r);
}
// Clear the next event list
_next.clear();
// Add the next event to the event list
if (NextEventChoice != -1)
_next.push_back(NextEventChoice);
else {
for (const auto &i : _events[_cur]._next)
_next.push_back(i);
}
}
//------------------------------------------------------------------------
// Purpose: Save the state of the object
//------------------------------------------------------------------------
void EventSequence::saveState(rapidxml::xml_document<char> &doc, rapidxml::xml_node<char> *root, const char *name) {
rapidxml::xml_node<char> *seqnode = doc.allocate_node(rapidxml::node_element, "set");
// Write current event id and name to node
seqnode->append_attribute(doc.allocate_attribute("name", name));
seqnode->append_attribute(doc.allocate_attribute("current", g_engine->_stringPool->get(_cur)));
// Prepare strings of next events and write them
for (uint i = 0; i < _next.size(); i++) {
rapidxml::xml_node<char> *child = doc.allocate_node(rapidxml::node_element, "next");
child->value(g_engine->_stringPool->get(_next[i]));
seqnode->append_node(child);
}
root->append_node(seqnode);
}
//------------------------------------------------------------------------
// Purpose: Load the state of the object
//------------------------------------------------------------------------
void EventSequence::loadState(rapidxml::xml_node<char> *node) {
rapidxml::xml_attribute<char> *curid = node->first_attribute("current");
if (curid != nullptr)
_cur = stringToNumber<uint>(curid->value());
_next.clear();
for (auto n = node->first_node("next"); n != nullptr; n = n->next_sibling("next"))
_next.push_back(stringToNumber<uint>(n->value()));
if (_next.empty())
_next.push_back(0);
}
} // End of namespace Crab

View File

@@ -0,0 +1,89 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_EVENTSEQUENCE_H
#define CRAB_EVENTSEQUENCE_H
#include "crab/event/gameevent.h"
namespace Crab {
namespace pyrodactyl {
namespace event {
class EventSequence {
Common::Array<GameEvent> _events;
bool _eventInProgress;
// The event currently in execution - updated only when all trigger conditions are met in InternalEvents
uint _cur;
// The events that can happen next - these are updated when the cur event is over
// This means cur and next operate in an alternating way
// scan next until find event, make it cur, end cur and update next, repeat
Common::Array<uint> _next;
public:
EventSequence() {
_eventInProgress = false;
_next.push_back(0);
_cur = 0;
}
~EventSequence() {}
GameEvent *currentEvent() {
return &_events[_cur];
}
// See if we should trigger any event
void internalEvents(pyrodactyl::event::Info &info);
void nextEvent(Info &info, const Common::String &playerId, Common::Array<EventResult> &result,
Common::Array<EventSeqInfo> &endSeq, int nextEventChoice = -1);
bool eventInProgress() {
return _eventInProgress;
}
void eventInProgress(bool val) {
_eventInProgress = val;
}
// Load and save
void load(const Common::Path &filename);
void saveState(rapidxml::xml_document<char> &doc, rapidxml::xml_node<char> *root, const char *name);
void loadState(rapidxml::xml_node<char> *node);
};
} // End of namespace event
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_EVENTSEQUENCE_H

View File

@@ -0,0 +1,398 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/XMLDoc.h"
#include "crab/event/eventstore.h"
#include "crab/event/GameEventInfo.h"
namespace Crab {
namespace pyrodactyl {
namespace event {
bool isChar(char c) {
if (c >= '0' && c <= '9')
return false;
return true;
}
} // End of namespace event
} // End of namespace pyrodactyl
using namespace pyrodactyl::people;
using namespace pyrodactyl::event;
//------------------------------------------------------------------------
// Purpose: Load from xml
//------------------------------------------------------------------------
void Info::load(rapidxml::xml_node<char> *node) {
if (nodeValid("people", node)) {
rapidxml::xml_node<char> *pnode = node->first_node("people");
_stem.load(pnode->first_attribute("templates")->value());
XMLDoc conf(pnode->first_attribute("list")->value());
if (conf.ready()) {
rapidxml::xml_node<char> *cnode = conf.doc()->first_node("characters");
if (nodeValid(cnode)) {
loadNum(OPINION_MIN, "op_min", cnode);
loadNum(OPINION_MAX, "op_max", cnode);
for (auto n = cnode->first_node("group"); n != nullptr; n = n->next_sibling("group"))
loadPeople(n->value());
}
}
}
if (nodeValid("objective", node))
_journal.load(node->first_node("objective")->first_attribute("layout")->value());
if (nodeValid("inventory", node)) {
rapidxml::xml_node<char> *inode = node->first_node("inventory");
_inv.load(inode->first_attribute("layout")->value());
}
curLocID(node->first_node("level")->first_attribute("start")->value());
_inv.itemFile(node->first_node("item")->first_attribute("list")->value());
}
void Info::loadPeople(const Common::Path &filename) {
XMLDoc conf(filename);
if (conf.ready()) {
rapidxml::xml_node<char> *node = conf.doc()->first_node("people");
if (nodeValid(node)) {
for (auto n = node->first_node(); n != nullptr; n = n->next_sibling()) {
Common::String str;
loadStr(str, "id", n);
_people[str].load(n, _stem);
}
}
}
}
//------------------------------------------------------------------------
// Purpose: Get/Set information about object type
//------------------------------------------------------------------------
void Info::type(const Common::String &id, const PersonType &val) {
if (_people.contains(id))
_people[id]._type = val;
}
PersonType Info::type(const Common::String &id) {
if (_people.contains(id))
return _people[id]._type;
return PE_NEUTRAL;
}
//------------------------------------------------------------------------
// Purpose: Get/Set information about object state
//------------------------------------------------------------------------
void Info::state(const Common::String &id, const PersonState &val) {
if (_people.contains(id))
_people[id]._state = val;
}
PersonState Info::state(const Common::String &id) {
if (_people.contains(id))
return _people[id]._state;
return PST_NORMAL;
}
//------------------------------------------------------------------------
// Purpose: Get/Set information about variables
//------------------------------------------------------------------------
bool Info::varGet(const Common::String &name, int &val) {
if (!_var.contains(name))
return false;
else
val = _var[name];
return true;
}
void Info::varSet(const Common::String &name, const Common::String &val) {
int varVal = 0;
bool assignToVar = Common::find_if(val.begin(), val.end(), isChar) != val.end();
if (assignToVar)
varGet(val, varVal);
else
varVal = stringToNumber<int>(val);
_var[name] = varVal;
}
void Info::varAdd(const Common::String &name, const int &val) {
if (!_var.contains(name))
varSet(name, 0);
_var[name] += val;
}
void Info::varSub(const Common::String &name, const int &val) {
if (!_var.contains(name))
varSet(name, 0);
_var[name] -= val;
}
void Info::varMul(const Common::String &name, const int &val) {
if (!_var.contains(name))
varSet(name, 0);
_var[name] *= val;
}
void Info::varDiv(const Common::String &name, const int &val) {
if (!_var.contains(name))
varSet(name, 0);
_var[name] /= val;
}
void Info::varDel(const Common::String &name) {
_var.erase(name);
}
//------------------------------------------------------------------------
// Purpose: Get/Set person traits
//------------------------------------------------------------------------
void Info::traitAdd(const Common::String &perId, const int &traitId) {
if (personValid(perId)) { // Valid person id
if (traitId >= 0 && (uint)traitId < g_engine->_eventStore->_trait.size()) { // Valid trait id
// Check for duplicate traits, DONT award anything if duplicate found
Person *p = &personGet(perId);
for (const auto &i : p->_trait)
if (i._id == traitId)
return;
p->_trait.push_back(g_engine->_eventStore->_trait[traitId]);
g_engine->_eventStore->setAchievement(g_engine->_eventStore->_trait[traitId]._id);
}
}
}
void Info::traitDel(const Common::String &perId, const int &traitId) {
if (personValid(perId)) { // Valid person id
if (traitId > 0 && (uint)traitId < g_engine->_eventStore->_trait.size()) { // Valid trait id
Person *p = &personGet(perId);
for (auto j = p->_trait.begin(); j != p->_trait.end(); ++j) {
if (j->_id == traitId) {
p->_trait.erase(j);
break;
}
}
}
}
}
//------------------------------------------------------------------------
// Purpose: Get/Set information about object opinion
//------------------------------------------------------------------------
bool Info::opinionGet(const Common::String &name, const pyrodactyl::people::OpinionType &type, int &val) {
if (!_people.contains(name))
return false;
val = _people[name]._opinion._val[type];
return true;
}
void Info::opinionChange(const Common::String &name, const pyrodactyl::people::OpinionType &type, int val) {
if (_people.contains(name))
_people[name]._opinion.change(type, val);
}
void Info::opinionSet(const Common::String &name, const pyrodactyl::people::OpinionType &type, int val) {
if (_people.contains(name))
_people[name]._opinion.set(type, val);
}
//------------------------------------------------------------------------
// Purpose: Get/Set information about object stats
//------------------------------------------------------------------------
bool Info::statGet(const Common::String &name, const pyrodactyl::stat::StatType &type, int &num) {
if (!_people.contains(name))
return false;
num = _people[name]._stat._val[type]._cur;
return true;
}
void Info::statSet(const Common::String &name, const pyrodactyl::stat::StatType &type, const int &num) {
if (_people.contains(name))
_people[name]._stat.set(type, num);
}
void Info::statChange(const Common::String &name, const pyrodactyl::stat::StatType &type, const int &num) {
if (_people.contains(name))
_people[name]._stat.change(type, num);
}
//------------------------------------------------------------------------
// Purpose: Get person object
//------------------------------------------------------------------------
bool Info::personGet(const Common::String &id, pyrodactyl::people::Person &p) {
if (!_people.contains(id))
return false;
p = _people[id];
return true;
}
bool Info::personValid(const Common::String &id) {
return _people.contains(id);
}
pyrodactyl::people::Person &Info::personGet(const Common::String &id) {
// Make sure to check PersonValid before doing this!
// Only use this to change parts of an object
return _people[id];
}
bool Info::collideWithTrigger(const Common::String &id, int rectIndex) {
if (_people.contains(id)) {
for (const auto &i : _people[id]._trig)
if (i == rectIndex)
return true;
}
return false;
}
//------------------------------------------------------------------------
// Purpose: Replace all #values with their appropriate names in a string
//------------------------------------------------------------------------
void Info::insertName(Common::String &msg) {
// We scan the dialog for #id values, which we convert to actual NPC names
for (uint i = 0; i < msg.size(); ++i) {
// The # symbol indicates that the next string until an end character needs to be replaced by the name
if (msg[i] == '#') {
// The position we want to start from, and the length of the substring
uint start = i, end = i + 1, len = 0;
// First make sure # wasn't the end of the string
for (; end < msg.size(); ++end, ++len)
if (msg[end] == ',' || msg[end] == '.' || msg[end] == '!' || msg[end] == ' ' ||
msg[end] == '?' || msg[end] == '-' || msg[end] == '\'' || msg[end] == '\"')
break;
if (end < msg.size()) {
// We use start+1 here because # isn't part of the id
Common::String s = msg.substr(start + 1, len);
// We use length+1 here because otherwise it lets the last character stay in dialog
if (personValid(s))
msg.replace(start, len + 1, personGet(s)._name);
}
}
}
}
Common::String Info::getName(const Common::String &id) {
if (personValid(id))
return personGet(id)._name;
return id;
}
//------------------------------------------------------------------------
// Purpose: Save and load object state
//------------------------------------------------------------------------
void Info::saveState(rapidxml::xml_document<> &doc, rapidxml::xml_node<char> *root) {
for (const auto &v : _var) {
rapidxml::xml_node<char> *child = doc.allocate_node(rapidxml::node_element, "var");
child->append_attribute(doc.allocate_attribute("id", v._key.c_str()));
child->append_attribute(doc.allocate_attribute("val", g_engine->_stringPool->get(v._value)));
root->append_node(child);
}
for (auto &p : _people)
p._value.saveState(doc, root);
rapidxml::xml_node<char> *childUnr = doc.allocate_node(rapidxml::node_element, "unread");
saveBool(_unread._inventory, "inventory", doc, childUnr);
saveBool(_unread._journal, "journal", doc, childUnr);
saveBool(_unread._trait, "trait", doc, childUnr);
saveBool(_unread._map, "map", doc, childUnr);
root->append_node(childUnr);
rapidxml::xml_node<char> *child_img = doc.allocate_node(rapidxml::node_element, "img");
child_img->append_attribute(doc.allocate_attribute("index", g_engine->_stringPool->get(_playerImg)));
root->append_node(child_img);
rapidxml::xml_node<char> *child_money = doc.allocate_node(rapidxml::node_element, "money");
child_money->append_attribute(doc.allocate_attribute("var", _moneyVar.c_str()));
root->append_node(child_money);
_journal.saveState(doc, root);
_inv.saveState(doc, root);
}
void Info::loadState(rapidxml::xml_node<char> *node) {
for (rapidxml::xml_node<char> *v = node->first_node("var"); v != nullptr; v = v->next_sibling("var"))
_var[v->first_attribute("id")->value()] = stringToNumber<int>(v->first_attribute("val")->value());
for (rapidxml::xml_node<char> *p = node->first_node("object"); p != nullptr; p = p->next_sibling("object")) {
Common::String id;
loadStr(id, "id", p);
_people[id].loadState(p);
}
if (nodeValid("unread", node)) {
rapidxml::xml_node<char> *unrnode = node->first_node("unread");
loadBool(_unread._inventory, "inventory", unrnode);
loadBool(_unread._journal, "journal", unrnode);
loadBool(_unread._trait, "trait", unrnode);
loadBool(_unread._map, "map", unrnode);
}
if (nodeValid("img", node))
loadNum(_playerImg, "index", node->first_node("img"));
if (nodeValid("money", node))
loadStr(_moneyVar, "var", node->first_node("money"));
_journal.loadState(node);
_inv.loadState(node);
}
//------------------------------------------------------------------------
// Purpose: Calculate UI positions after change in screen size
//------------------------------------------------------------------------
void Info::setUI() {
_journal.setUI();
_inv.setUI();
}
} // End of namespace Crab

View File

@@ -0,0 +1,243 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_GAMEEVENTINFO_H
#define CRAB_GAMEEVENTINFO_H
#include "crab/stat/StatTemplate.h"
#include "crab/ui/Inventory.h"
#include "crab/ui/journal.h"
namespace Crab {
namespace pyrodactyl {
namespace event {
bool isChar(char c);
class Info {
// The characters in the game
pyrodactyl::people::PersonMap _people;
// The stat templates used in declaring person objects
pyrodactyl::stat::StatTemplates _stem;
// The variables set by the events so far
typedef Common::HashMap<Common::String, int> VarMap;
VarMap _var;
// The last object player interacted with
Common::String _lastobj;
// Is the game Iron Man or not?
// Iron man means only one save - no reloading
bool _ironman;
// The player's current location
struct PlayerLoc {
// id of the player's current location
Common::String _id;
// The name of the player's current location
Common::String _name;
PlayerLoc() {}
} _loc;
// This image changes to reflect the playable character
int _playerImg;
void loadPeople(const Common::Path &filename);
public:
// The player's one stop shop for objectives and lore
pyrodactyl::ui::Journal _journal;
// The current player inventory
pyrodactyl::ui::Inventory _inv;
// This structure keeps track of unread indicators
struct UnreadData {
bool _inventory, _journal, _trait, _map;
UnreadData() {
_inventory = false;
_journal = false;
_trait = true;
_map = false;
}
} _unread;
// This is the variable corresponding to money that is drawn
Common::String _moneyVar;
// Has the player pressed the talk key
bool _talkKeyDown;
// Used so we only play one sound per event
struct NotifySounds {
bool _notify, _repInc, _repDec;
NotifySounds() {
_notify = false;
_repInc = false;
_repDec = false;
}
} _sound;
Info() {
Init();
}
~Info() {}
void Init() {
_lastobj = "";
_ironman = false;
_playerImg = 0;
_talkKeyDown = false;
}
void load(rapidxml::xml_node<char> *node);
// Person related stuff
void type(const Common::String &id, const pyrodactyl::people::PersonType &val);
pyrodactyl::people::PersonType type(const Common::String &id);
void state(const Common::String &id, const pyrodactyl::people::PersonState &val);
pyrodactyl::people::PersonState state(const Common::String &id);
// Opinion
bool opinionGet(const Common::String &name, const pyrodactyl::people::OpinionType &type, int &val);
void opinionSet(const Common::String &name, const pyrodactyl::people::OpinionType &type, int val);
void opinionChange(const Common::String &name, const pyrodactyl::people::OpinionType &type, int val);
// Stats
bool statGet(const Common::String &name, const pyrodactyl::stat::StatType &type, int &num);
void statSet(const Common::String &name, const pyrodactyl::stat::StatType &type, const int &num);
void statChange(const Common::String &name, const pyrodactyl::stat::StatType &type, const int &num);
// Variables
bool varGet(const Common::String &name, int &val);
void varSet(const Common::String &name, const Common::String &val);
void varAdd(const Common::String &name, const int &val);
void varSub(const Common::String &name, const int &val);
void varMul(const Common::String &name, const int &val);
void varDiv(const Common::String &name, const int &val);
void varDel(const Common::String &name);
// The trait functions
void traitAdd(const Common::String &perId, const int &traitId);
void traitDel(const Common::String &perId, const int &traitId);
// Player character button
void playerImg(const int &val) {
_playerImg = val;
}
int playerImg() {
return _playerImg;
}
Common::String curLocID() {
return _loc._id;
}
void curLocID(const Common::String &id) {
_loc._id = id;
}
Common::String curLocName() {
return _loc._name;
}
void curLocName(const Common::String &name) {
_loc._name = name;
}
// Player stuff
Common::String lastPerson() {
return _lastobj;
}
void lastPerson(const Common::String &name) {
_lastobj = name;
}
// Return the variable map for stuff like visibility checks
VarMap &mapGet() {
return _var;
}
bool personGet(const Common::String &id, pyrodactyl::people::Person &p);
bool personValid(const Common::String &id);
pyrodactyl::people::Person &personGet(const Common::String &id);
// Is an object colliding with a trigger area
bool collideWithTrigger(const Common::String &id, int rectIndex);
// Replace all #values with their appropriate names in a string
void insertName(Common::String &msg);
Common::String getName(const Common::String &id);
// Draw the inventory
void invDraw(const Common::String &id) {
if (_var.contains(_moneyVar))
_inv.draw(_people[id], _var[_moneyVar]);
else
_inv.draw(_people[id], 0);
}
// Get whether game is iron man or not
bool ironMan() {
return _ironman;
}
void ironMan(const bool &val) {
_ironman = val;
}
void loadIronMan(rapidxml::xml_node<char> *node) {
Common::String str;
loadStr(str, "diff", node);
_ironman = (str == "Iron Man");
}
void saveState(rapidxml::xml_document<> &doc, rapidxml::xml_node<char> *root);
void loadState(rapidxml::xml_node<char> *node);
void setUI();
};
} // End of namespace event
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_GAMEEVENTINFO_H

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_CHANGEVAL_H
#define CRAB_CHANGEVAL_H
#include "common/str.h"
#include "crab/loaders.h"
#include "crab/people/opinion.h"
namespace Crab {
using namespace pyrodactyl::people;
namespace pyrodactyl {
namespace event {
struct ChangeVal {
// The person whose opinion is changed
Common::String _id;
// How much does opinion change?
int _val[pyrodactyl::people::OPI_TOTAL];
ChangeVal() {
_val[OPI_LIKE] = 0;
_val[OPI_RESPECT] = 0;
_val[OPI_FEAR] = 0;
}
ChangeVal(rapidxml::xml_node<char> *node) : ChangeVal() {
load(node);
}
void load(rapidxml::xml_node<char> *node) {
loadStr(_id, "id", node);
loadNum(_val[OPI_LIKE], "like", node);
loadNum(_val[OPI_RESPECT], "respect", node);
loadNum(_val[OPI_FEAR], "fear", node);
}
};
} // End of namespace event
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_CHANGEVAL_H

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/event/conversationdata.h"
namespace Crab {
using namespace pyrodactyl::event;
void ReplyChoice::load(rapidxml::xml_node<char> *node) {
loadStr(_text, "text", node);
loadNum(_tone, "tone", node);
loadNum(_nextid, "next", node);
if (nodeValid("unlock", node, false))
_unlock.load(node->first_node("unlock"));
if (nodeValid("change", node, false))
for (auto n = node->first_node("change"); n != nullptr; n = n->next_sibling("change"))
_change.push_back(n);
}
void ConversationData::load(rapidxml::xml_node<char> *node) {
for (auto n = node->first_node("reply"); n != nullptr; n = n->next_sibling("reply"))
_reply.push_back(n);
}
} // End of namespace Crab

View File

@@ -0,0 +1,83 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_CONVERSATIONDATA_H
#define CRAB_CONVERSATIONDATA_H
#include "crab/event/changeval.h"
#include "crab/event/triggerset.h"
namespace Crab {
namespace pyrodactyl {
namespace event {
struct ReplyChoice {
// The text for the reply
Common::String _text;
// The tone of the response
uint _tone;
// The next event id
uint _nextid;
// The conditions to unlock this choice
TriggerSet _unlock;
// The changes to opinion that are possible for this reply
// All NPCs affected by this conversation
Common::Array<ChangeVal> _change;
ReplyChoice() {
_tone = 0;
_nextid = 0;
}
ReplyChoice(rapidxml::xml_node<char> *node) : ReplyChoice() {
load(node);
}
void load(rapidxml::xml_node<char> *node);
};
struct ConversationData {
// The set of replies
Common::Array<ReplyChoice> _reply;
ConversationData() {}
void load(rapidxml::xml_node<char> *node);
};
} // End of namespace event
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_CONVERSATIONDATA_H

View File

@@ -0,0 +1,289 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/crab.h"
#include "crab/event/effect.h"
namespace Crab {
using namespace pyrodactyl::event;
using namespace pyrodactyl::music;
void Effect::load(rapidxml::xml_node<char> *node) {
Common::String ty;
loadStr(ty, "type", node);
// Should we throw a warning about missing fields? Depends on the type of effect
bool echoOp = true, echoSub = true, echoVal = true;
if (ty == "var")
_type = EFF_VAR;
else if (ty == "journal")
_type = EFF_JOURNAL;
else if (ty == "person")
_type = EFF_OBJ;
else if (ty == "item")
_type = EFF_ITEM;
else if (ty == "like")
_type = EFF_LIKE;
else if (ty == "fear")
_type = EFF_FEAR;
else if (ty == "respect")
_type = EFF_RESPECT;
else if (ty == STATNAME_HEALTH)
_type = EFF_HEALTH;
else if (ty == "sound")
_type = EFF_SOUND;
else if (ty == "money")
_type = EFF_MONEY;
else if (ty == "end") {
_type = EFF_END;
echoSub = false;
echoVal = false;
} else if (ty == "map")
_type = EFF_MAP;
else if (ty == "dest")
_type = EFF_DEST;
else if (ty == "img") {
_type = EFF_IMG;
echoOp = false;
echoSub = false;
} else if (ty == "trait") {
_type = EFF_TRAIT;
echoOp = false;
} else if (ty == "move") {
_type = EFF_MOVE;
echoOp = false;
} else if (ty == "level")
_type = EFF_LEVEL;
else if (ty == "player") {
_type = EFF_PLAYER;
echoOp = false;
echoSub = false;
} else if (ty == "save") {
_type = EFF_SAVE;
echoOp = false;
echoSub = false;
echoVal = false;
} else if (ty == "quit") {
_type = EFF_QUIT;
echoOp = false;
echoSub = false;
echoVal = false;
} else
_type = EFF_VAR;
loadStr(_subject, "subject", node, echoSub);
loadStr(_operation, "operation", node, echoOp);
loadStr(_val, "val", node, echoVal);
}
void Effect::changeOpinion(pyrodactyl::event::Info &info, pyrodactyl::people::OpinionType opType) {
int oldOp = 0;
// Only bother if the person exists and has a valid opinion
if (info.opinionGet(_subject, opType, oldOp)) {
if (_operation == "=")
info.opinionSet(_subject, opType, stringToNumber<int>(_val));
else if (_operation == "+")
info.opinionChange(_subject, opType, stringToNumber<int>(_val));
else if (_operation == "-")
info.opinionChange(_subject, opType, -1 * stringToNumber<int>(_val));
int newOp = 0;
info.opinionGet(_subject, opType, newOp);
if (newOp > oldOp)
info._sound._repInc = true;
else if (newOp < oldOp)
info._sound._repDec = true;
}
}
bool Effect::execute(pyrodactyl::event::Info &info, const Common::String &playerId,
Common::Array<EventResult> &result, Common::Array<EventSeqInfo> &endSeq) {
if (_type < EFF_MOVE) {
switch (_type) {
case EFF_VAR:
if (_operation == "=")
info.varSet(_subject, _val);
else if (_operation == "del")
info.varDel(_subject);
else if (_operation == "+")
info.varAdd(_subject, stringToNumber<int>(_val));
else if (_operation == "-")
info.varSub(_subject, stringToNumber<int>(_val));
else if (_operation == "*")
info.varMul(_subject, stringToNumber<int>(_val));
else if (_operation == "/")
info.varDiv(_subject, stringToNumber<int>(_val));
break;
case EFF_JOURNAL:
if (_subject == "finish")
info._journal.move(playerId, _operation, true);
else if (_subject == "start")
info._journal.move(playerId, _operation, false);
else
info._journal.add(playerId, _subject, _operation, _val);
// Update unread status of journal
info._unread._journal = true;
// used so we only play one notify sound per event
info._sound._notify = true;
break;
case EFF_OBJ:
if (_operation == "type")
info.type(_subject, pyrodactyl::people::stringToPersonType(_val));
else if (_operation == "state")
info.state(_subject, pyrodactyl::people::stringToPersonState(_val));
break;
case EFF_ITEM:
if (_operation == "del")
info._inv.delItem(_subject, _val);
else
info._inv.loadItem(_subject, _val);
// Update unread status of inventory
info._unread._inventory = true;
// used so we only play one notify sound per event
info._sound._notify = true;
break;
case EFF_LIKE:
changeOpinion(info, pyrodactyl::people::OPI_LIKE);
break;
case EFF_FEAR:
changeOpinion(info, pyrodactyl::people::OPI_FEAR);
break;
case EFF_RESPECT:
changeOpinion(info, pyrodactyl::people::OPI_RESPECT);
break;
case EFF_HEALTH: {
using namespace pyrodactyl::stat;
int num = stringToNumber<int>(_val);
if (_operation == "=")
info.statSet(_subject, STAT_HEALTH, num);
else if (_operation == "+")
info.statChange(_subject, STAT_HEALTH, num);
else if (_operation == "-")
info.statChange(_subject, STAT_HEALTH, -1 * num);
} break;
case EFF_SOUND:
if (_subject == "music") {
if (_operation == "play") {
MusicKey m = stringToNumber<MusicKey>(_val);
g_engine->_musicManager->playMusic(m);
} else if (_operation == "stop")
g_engine->_musicManager->stop();
else if (_operation == "pause")
g_engine->_musicManager->pause();
else if (_operation == "resume")
g_engine->_musicManager->resume();
} else
g_engine->_musicManager->playEffect(stringToNumber<ChunkKey>(_val), 0);
break;
case EFF_MONEY:
info._moneyVar = _val;
break;
case EFF_END:
if (_operation == "cur")
endSeq.push_back(true);
else {
EventSeqInfo seqinfo;
seqinfo._loc = _subject;
seqinfo._val = _val;
endSeq.push_back(seqinfo);
}
break;
default:
break;
}
return true;
} else {
EventResult r;
r._val = _subject;
r._x = stringToNumber<int>(_operation);
r._y = stringToNumber<int>(_val);
switch (_type) {
case EFF_MOVE:
r._type = ER_MOVE;
break;
case EFF_MAP:
r._type = ER_MAP;
break;
case EFF_DEST:
r._type = ER_DEST;
break;
case EFF_IMG:
r._type = ER_IMG;
info.playerImg(stringToNumber<int>(_val));
break;
case EFF_TRAIT:
r._type = ER_TRAIT;
info._unread._trait = true;
info._sound._notify = true;
break;
case EFF_LEVEL:
r._type = ER_LEVEL;
break;
case EFF_PLAYER:
r._type = ER_PLAYER;
break;
case EFF_SAVE:
r._type = ER_SAVE;
break;
case EFF_QUIT:
r._type = ER_QUIT;
break;
default:
break;
}
result.push_back(r);
}
return false;
}
} // End of namespace Crab

127
engines/crab/event/effect.h Normal file
View File

@@ -0,0 +1,127 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_EFFECT_H
#define CRAB_EFFECT_H
#include "crab/loaders.h"
#include "crab/event/GameEventInfo.h"
#include "crab/people/person.h"
#include "crab/rapidxml/rapidxml.hpp"
namespace Crab {
namespace pyrodactyl {
namespace event {
enum EventResultType {
ER_NONE, // Do nothing
ER_MAP, // Change the map visible to player
ER_DEST, // Add or remove a destination on world map
ER_IMG, // Change the character button image
ER_TRAIT, // Add or remove a trait from a character
ER_LEVEL, // Change level
ER_MOVE, // Move sprite
ER_PLAYER, // Switch the player sprite
ER_SAVE, // Save game
ER_SYNC, // Sync the level
ER_QUIT // Quit to main menu
};
struct EventResult {
EventResultType _type;
Common::String _val;
int _x, _y;
EventResult() {
_type = ER_NONE;
_x = -1;
_y = -1;
}
};
struct EventSeqInfo {
bool _cur;
Common::String _loc, _val;
EventSeqInfo() {
_cur = false;
}
EventSeqInfo(const bool &cur) {
_cur = cur;
}
};
enum EffectType {
EFF_VAR, // variable operations like adding, removing etc
EFF_JOURNAL, // Add an objective to the player quest book
EFF_OBJ, // Change status (hostile, coward etc), state (stand, fight, flee, KO etc) of a character
EFF_ITEM, // Add/remove an item in the player's inventory
EFF_LIKE, // Change opinion of a character (charm)
EFF_FEAR, // Change opinion of a character (intimidate)
EFF_RESPECT, // Change opinion of a character (respect)
EFF_HEALTH, // Change health of a character
EFF_SOUND, // Manipulate the game music
EFF_MONEY, // Set the money variable (not its value, just that which variable is the current money variable)
EFF_END, // End of the event sequence, remove it from active sequences
// EFFECT DIVISION HERE
EFF_MOVE, // Make a character move
EFF_MAP, // Change the world map
EFF_DEST, // Add a destination to the world map
EFF_IMG, // Change the player button image
EFF_TRAIT, // Add or remove a trait from a character
EFF_LEVEL, // Load a new level
EFF_PLAYER, // Swap the player sprite
EFF_SAVE, // Auto save the game
EFF_QUIT // Quit to main menu
};
struct Effect {
EffectType _type;
Common::String _subject, _operation, _val;
Effect() {
_type = EFF_VAR;
}
~Effect() {}
void load(rapidxml::xml_node<char> *node);
bool execute(pyrodactyl::event::Info &info, const Common::String &playerId,
Common::Array<EventResult> &result, Common::Array<EventSeqInfo> &endSeq);
void changeOpinion(pyrodactyl::event::Info &info, pyrodactyl::people::OpinionType type);
};
} // End of namespace event
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_EFFECT_H

View File

@@ -0,0 +1,102 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/XMLDoc.h"
#include "crab/event/eventstore.h"
namespace Crab {
using namespace pyrodactyl::event;
void GameEventStore::addConv(rapidxml::xml_node<char> *node, uint &index) {
ConversationData c;
if (nodeValid("talk", node))
c.load(node->first_node("talk"));
index = _con.size();
_con.push_back(c);
}
void GameEventStore::load(const Common::Path &filename) {
// Request current user stats from Steam
// m_pSteamUserStats = SteamUserStats();
// if (m_pSteamUserStats != nullptr)
// m_pSteamUserStats->RequestCurrentStats();
XMLDoc conf(filename);
if (conf.ready()) {
rapidxml::xml_node<char> *node = conf.doc()->first_node("store");
if (nodeValid("animations", node)) {
rapidxml::xml_node<char> *animnode = node->first_node("animations");
for (auto n = animnode->first_node("anim"); n != nullptr; n = n->next_sibling("anim"))
_anim.push_back(n);
}
if (nodeValid("tones", node)) {
rapidxml::xml_node<char> *tonenode = node->first_node("tones");
for (auto n = tonenode->first_node("tone"); n != nullptr; n = n->next_sibling("tone")) {
ToneData dat;
loadStr(dat._text, "text", n);
_tone.push_back(dat);
}
}
if (nodeValid("images", node)) {
rapidxml::xml_node<char> *imgnode = node->first_node("images");
for (auto n = imgnode->first_node("img"); n != nullptr; n = n->next_sibling("img"))
_img.push_back(n);
}
if (nodeValid("traits", node)) {
rapidxml::xml_node<char> *traitnode = node->first_node("traits");
for (auto n = traitnode->first_node("trait"); n != nullptr; n = n->next_sibling("trait"))
_trait.push_back(n);
}
}
}
void GameEventStore::setAchievement(const int &id) {
/*
if (m_pSteamUserStats != nullptr)
{
if (id == 0)
m_pSteamUserStats->SetAchievement("a0");
else
m_pSteamUserStats->SetAchievement(trait[id].id_str.c_str());
m_pSteamUserStats->StoreStats();
}
*/
}
} // End of namespace Crab

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_EVENTSTORE_H
#define CRAB_EVENTSTORE_H
#include "crab/animation/animation.h"
#include "crab/event/conversationdata.h"
#include "crab/ui/StateButton.h"
namespace Crab {
namespace pyrodactyl {
namespace event {
// This structure is responsible for storing the special data structures for events like replies, conversations
struct GameEventStore {
// Data related to conversation events
Common::Array<ConversationData> _con;
// Data related to animations
Common::Array<pyrodactyl::anim::Animation> _anim;
// Data related to the tones of a character
struct ToneData {
Common::String _text;
};
// This sets the text the player sees as "tone" during the reply menu
Common::Array<ToneData> _tone;
// We need to change player character images when switching between characters
Common::Array<pyrodactyl::ui::StateButtonImage> _img;
// The set of traits for various characters
Common::Array<pyrodactyl::people::Trait> _trait;
// Steam UserStats interface
// ISteamUserStats *m_pSteamUserStats;
GameEventStore() {
// Huge number to prevent lots of resizing and stuff
_con.reserve(9999);
_trait.reserve(120);
_tone.reserve(120);
}
void clear() {
_con.clear();
_anim.clear();
}
void load(const Common::Path &filename);
void addConv(rapidxml::xml_node<char> *node, uint &index);
void setAchievement(const int &id);
};
} // End of namespace event
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_EVENTSTORE_H

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/event/gameevent.h"
namespace Crab {
using namespace pyrodactyl::event;
using namespace pyrodactyl::people;
GameEvent::GameEvent() {
_id = 0;
_type = EVENT_DIALOG;
_special = 0;
_state = PST_NORMAL;
}
void GameEvent::load(rapidxml::xml_node<char> *node) {
if (!LoadEventID(_id, "id", node))
_id = 0;
loadStr(_title, "title", node);
loadStr(_dialog, "dialog", node);
loadEnum(_state, "state", node, false);
Common::String type;
loadStr(type, "type", node);
if (type == "dlg") {
_type = EVENT_DIALOG;
_special = 0;
} else if (type == "reply") {
_type = EVENT_REPLY;
g_engine->_eventStore->addConv(node, _special);
} else if (type == "animation") {
_type = EVENT_ANIM;
loadNum(_special, "anim", node);
} else if (type == "silent") {
_type = EVENT_SILENT;
_special = 0;
} else if (type == "text") {
_type = EVENT_TEXT;
_special = 0;
} else {
_type = EVENT_SPLASH;
_special = 0;
}
_trig.load(node);
_next.clear();
for (rapidxml::xml_node<char> *i = node->first_node("next"); i != nullptr; i = i->next_sibling("next"))
if (i->first_attribute("id") != nullptr)
_next.push_back(stringToNumber<EventID>(i->first_attribute("id")->value()));
_effect.clear();
for (rapidxml::xml_node<char> *it = node->first_node("effect"); it != nullptr; it = it->next_sibling("effect")) {
Effect e;
e.load(it);
_effect.push_back(e);
}
}
} // End of namespace Crab

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/>.
*
*/
/*
* This code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_GAMEEVENT_H
#define CRAB_GAMEEVENT_H
#include "crab/event/effect.h"
#include "crab/event/eventstore.h"
#include "crab/people/person.h"
#include "crab/event/triggerset.h"
namespace Crab {
// An uinteger is our event id format
typedef uint EventID;
// Just map loading function to number load
#define LoadEventID loadNum
namespace pyrodactyl {
namespace event {
enum EventType {
EVENT_DIALOG,
EVENT_REPLY,
EVENT_TEXT,
EVENT_ANIM,
EVENT_SILENT,
EVENT_SPLASH
};
struct GameEvent {
// The event identifier
EventID _id;
// The heading for the dialog spoken
Common::String _title;
// The dialog spoken in the event
Common::String _dialog;
// The state decides which animation is drawn in the dialog box
pyrodactyl::people::PersonState _state;
// Event type and related data index
EventType _type;
uint _special;
// The variables changed/added in the event
Common::Array<Effect> _effect;
// The triggers for the event
TriggerSet _trig;
// The id of the next event
Common::Array<EventID> _next;
GameEvent();
GameEvent(rapidxml::xml_node<char> *node) : GameEvent() {
load(node);
}
~GameEvent() {}
void load(rapidxml::xml_node<char> *node);
};
} // End of namespace event
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_GAMEEVENT_H

View File

@@ -0,0 +1,371 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/event/gameeventmanager.h"
namespace Crab {
using namespace pyrodactyl::people;
using namespace pyrodactyl::event;
using namespace pyrodactyl::level;
using namespace pyrodactyl::image;
using namespace pyrodactyl::ui;
void Manager::init() {
_eventMap.clear();
_activeSeq = UINT_MAX;
_curSp = nullptr;
_player = false;
_curEvent = nullptr;
_drawGame = true;
}
//------------------------------------------------------------------------
// Purpose: Load this
//------------------------------------------------------------------------
void Manager::load(rapidxml::xml_node<char> *node, ParagraphData &popup) {
if (nodeValid(node)) {
XMLDoc conf(node->first_attribute("list")->value());
if (conf.ready()) {
rapidxml::xml_node<char> *lnode = conf.doc()->first_node("event_list");
for (rapidxml::xml_node<char> *loc = lnode->first_node("loc"); loc != nullptr; loc = loc->next_sibling("loc")) {
Common::String locName;
loadStr(locName, "name", loc);
for (auto n = loc->first_node("file"); n != nullptr; n = n->next_sibling("file")) {
uint id;
Common::Path path;
loadNum(id, "name", n);
loadPath(path, "path", n);
_eventMap[locName].addSeq(id, path);
}
}
}
_activeSeq = UINT_MAX;
conf.load(node->first_attribute("layout")->value());
if (conf.ready()) {
rapidxml::xml_node<char> *layout = conf.doc()->first_node("layout");
if (nodeValid(layout)) {
if (nodeValid("character", layout))
_oh.load(layout->first_node("character"));
if (nodeValid("popup", layout))
popup.load(layout->first_node("popup"));
if (nodeValid("intro", layout))
_intro.load(layout->first_node("intro"));
}
}
_reply.load(node->first_attribute("conversation")->value());
_per.load(node->first_attribute("char")->value());
}
}
//------------------------------------------------------------------------
// Purpose: Handle events
//------------------------------------------------------------------------
void Manager::handleEvents(Info &info, const Common::String &playerId, Common::Event &event, HUD &hud, Level &level, Common::Array<EventResult> &result) {
// If an event is already being performed
if (_eventMap.contains(info.curLocID()) && _eventMap[info.curLocID()].eventInProgress(_activeSeq)) {
switch (_curEvent->_type) {
case EVENT_DIALOG:
if (_oh._showJournal) {
info._journal.handleEvents(playerId, event);
if (hud._back.handleEvents(event) == BUAC_LCLICK || hud._pausekey.handleEvents(event))
_oh._showJournal = false;
} else {
// If journal button is select from within an event, go to the entry corresponding to that person's name
if (_oh.handleCommonEvents(event)) {
if (info.personValid(_curEvent->_title)) {
Person &p = info.personGet(_curEvent->_title);
if (p._altJournalName)
info._journal.open(playerId, JE_PEOPLE, p._journalName);
else
info._journal.open(playerId, JE_PEOPLE, p._name);
}
}
if (_oh.handleDlboxEvents(event)) {
_oh.onExit();
_eventMap[info.curLocID()].nextEvent(_activeSeq, info, playerId, result, _endSeq);
_oh._showJournal = false;
}
}
break;
case EVENT_ANIM:
// Skip animation if key pressed or mouse pressed
if (event.type == Common::EVENT_LBUTTONUP || event.type == Common::EVENT_RBUTTONUP)
_eventMap[info.curLocID()].nextEvent(_activeSeq, info, playerId, result, _endSeq);
break;
case EVENT_REPLY:
if (_oh._showJournal) {
info._journal.handleEvents(playerId, event);
if (hud._back.handleEvents(event) == BUAC_LCLICK || hud._pausekey.handleEvents(event))
_oh._showJournal = false;
} else {
// If journal button is select from within an event, go to the entry corresponding to that person's name
if (_oh.handleCommonEvents(event))
if (info.personValid(_curEvent->_title))
info._journal.open(playerId, JE_PEOPLE, info.personGet(_curEvent->_title)._name);
unsigned int option = static_cast<unsigned int>(_reply.hoverIndex());
const auto &replyChoice = g_engine->_eventStore->_con[_curEvent->_special]._reply;
if (option < replyChoice.size()) {
_intro.onEntry(replyChoice[option]._text);
}
int choice = _reply.handleEvents(info, g_engine->_eventStore->_con[_curEvent->_special], _curEvent->_title, _oh, event);
if (choice >= 0) {
_reply.onExit();
_eventMap[info.curLocID()].nextEvent(_activeSeq, info, playerId, result, _endSeq, choice);
_oh._showJournal = false;
}
}
break;
case EVENT_TEXT:
// If journal button is select from within an event, go to the entry corresponding to that person's name
if (_oh.handleCommonEvents(event))
if (info.personValid(_curEvent->_title))
info._journal.open(playerId, JE_PEOPLE, info.personGet(_curEvent->_title)._name);
if (_textin.handleEvents(event))
_eventMap[info.curLocID()].nextEvent(_activeSeq, info, playerId, result, _endSeq);
break;
case EVENT_SPLASH:
if (_intro._showTraits) {
_per.handleEvents(info, _curEvent->_title, event);
if (hud._back.handleEvents(event) == BUAC_LCLICK || hud._pausekey.handleEvents(event))
_intro._showTraits = false;
} else {
if (_intro.handleEvents(event)) {
_intro.onExit();
_eventMap[info.curLocID()].nextEvent(_activeSeq, info, playerId, result, _endSeq);
}
if (_intro._showTraits)
_per.Cache(info, level.playerId(), level);
}
break;
default:
break;
}
endSequence(info.curLocID());
}
}
//------------------------------------------------------------------------
// Purpose: Internal Events
//------------------------------------------------------------------------
void Manager::internalEvents(Info &info, Level &level, Common::Array<EventResult> &result) {
if (_eventMap.contains(info.curLocID())) {
if (_eventMap[info.curLocID()].eventInProgress(_activeSeq)) {
switch (_curEvent->_type) {
case EVENT_DIALOG:
// fallthrough intended
case EVENT_REPLY:
// fallthrough intended
case EVENT_SPLASH:
updateDialogBox(info, level);
break;
case EVENT_ANIM: {
using namespace pyrodactyl::anim;
DrawType drawVal = DRAW_SAME;
if (g_engine->_eventStore->_anim[_curEvent->_special].internalEvents(drawVal))
_eventMap[info.curLocID()].nextEvent(_activeSeq, info, level.playerId(), result, _endSeq);
if (drawVal == DRAW_STOP)
_drawGame = false;
else if (drawVal == DRAW_START)
_drawGame = true;
} break;
case EVENT_SILENT:
_eventMap[info.curLocID()].nextEvent(_activeSeq, info, level.playerId(), result, _endSeq);
break;
default:
break;
}
endSequence(info.curLocID());
} else {
_eventMap[info.curLocID()].internalEvents(info);
calcActiveSeq(info, level, level.camera());
}
}
}
void Manager::updateDialogBox(Info &info, pyrodactyl::level::Level &level) {
_oh.internalEvents(_curEvent->_state, _curSp);
}
//------------------------------------------------------------------------
// Purpose: Draw
//------------------------------------------------------------------------
void Manager::draw(Info &info, HUD &hud, Level &level) {
if (_eventMap.contains(info.curLocID()) && _eventMap[info.curLocID()].eventInProgress(_activeSeq)) {
switch (_curEvent->_type) {
case EVENT_ANIM:
g_engine->_eventStore->_anim[_curEvent->_special].draw();
break;
case EVENT_DIALOG:
g_engine->_imageManager->dimScreen();
if (_oh._showJournal) {
info._journal.draw(level.playerId());
hud._back.draw();
} else
_oh.draw(info, _curEvent, _curEvent->_title, _player, _curSp);
break;
case EVENT_REPLY:
g_engine->_imageManager->dimScreen();
if (_oh._showJournal) {
info._journal.draw(level.playerId());
hud._back.draw();
} else {
_oh.draw(info, _curEvent, _curEvent->_title, _player, _curSp);
_reply.draw();
}
break;
case EVENT_TEXT:
_oh.draw(info, _curEvent, _curEvent->_title, _player, _curSp);
_textin.draw();
break;
case EVENT_SPLASH:
g_engine->_imageManager->dimScreen();
if (_intro._showTraits) {
_per.draw(info, _curEvent->_title);
hud._back.draw();
} else
_intro.draw(info, _curEvent->_dialog, _curSp, _curEvent->_state);
break;
default:
break;
}
}
}
//------------------------------------------------------------------------
// Purpose: Calculate the current sequence in progress
//------------------------------------------------------------------------
void Manager::calcActiveSeq(Info &info, pyrodactyl::level::Level &level, const Rect &camera) {
if (_eventMap[info.curLocID()].activeSeq(_activeSeq)) {
// Set all the pointers to the new values
_curEvent = _eventMap[info.curLocID()].curEvent(_activeSeq);
_oh.reset(_curEvent->_title);
_curSp = level.getSprite(_curEvent->_title);
// The player character's dialog is drawn a bit differently compared to others
_player = (_curEvent->_title == level.playerId());
switch (_curEvent->_type) {
case EVENT_DIALOG:
_oh.onEntry(_curEvent->_dialog);
break;
case EVENT_ANIM:
g_engine->_eventStore->_anim[_curEvent->_special].start();
break;
case EVENT_REPLY:
_reply.onEntry(_curEvent->_dialog);
_reply.cache(info, g_engine->_eventStore->_con[_curEvent->_special]);
break;
case EVENT_SPLASH:
_intro.onEntry(_curEvent->_dialog);
break;
default:
break;
}
}
}
//------------------------------------------------------------------------
// Purpose: Get/set info
//------------------------------------------------------------------------
void Manager::endSequence(const Common::String &curloc) {
if (_endSeq.empty())
return;
for (const auto &i : _endSeq)
if (i._cur)
_eventMap[curloc].endSeq(_activeSeq);
else if (_eventMap.contains(i._loc))
_eventMap[i._loc].endSeq(stringToNumber<uint>(i._val));
_activeSeq = UINT_MAX;
_endSeq.clear();
}
bool Manager::eventInProgress() {
if (_activeSeq == UINT_MAX)
return false;
return true;
}
//------------------------------------------------------------------------
// Purpose: Save the state of the object
//------------------------------------------------------------------------
void Manager::saveState(rapidxml::xml_document<> &doc, rapidxml::xml_node<char> *root) {
for (auto &i : _eventMap) {
rapidxml::xml_node<char> *child = doc.allocate_node(rapidxml::node_element, "loc");
child->append_attribute(doc.allocate_attribute("name", i._key.c_str()));
i._value.saveState(doc, child);
root->append_node(child);
}
}
//------------------------------------------------------------------------
// Purpose: Load the state of the object
//------------------------------------------------------------------------
void Manager::loadState(rapidxml::xml_node<char> *node) {
for (auto n = node->first_node("loc"); n != nullptr; n = n->next_sibling("loc")) {
if (n->first_attribute("name") != nullptr) {
Common::String name = n->first_attribute("name")->value();
if (_eventMap.contains(name))
_eventMap[name].loadState(n);
}
}
}
//------------------------------------------------------------------------
// Purpose: Function called when window size is changed to adjust UI
//------------------------------------------------------------------------
void Manager::setUI() {
_oh.setUI();
_reply.setUI();
_textin.setUI();
_per.setUI();
}
} // End of namespace Crab

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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#ifndef CRAB_GAMEEVENTMANAGER_H
#define CRAB_GAMEEVENTMANAGER_H
#include "crab/event/EventSeqGroup.h"
#include "crab/ui/ChapterIntro.h"
#include "crab/ui/hud.h"
#include "crab/ui/journal.h"
#include "crab/ui/PersonHandler.h"
#include "crab/ui/PersonScreen.h"
#include "crab/ui/ReplyMenu.h"
#include "crab/ui/textarea.h"
namespace Crab {
namespace pyrodactyl {
namespace event {
class Manager {
protected:
// All the events in the game
Common::HashMap<Common::String, EventSeqGroup> _eventMap;
// The currently happening or active sequence
uint _activeSeq;
// THIS IS NOT THE DEFINITIVE LIST OF ENDED SEQUENCES
// JUST A TEMPORARY LIST OF EVENT SEQUENCES TO PASS AROUND
Common::Array<EventSeqInfo> _endSeq;
// The objects used to draw the dialog box and opinion bars
pyrodactyl::ui::PersonHandler _oh;
// The _reply menu and the colors and font of the text
pyrodactyl::ui::ReplyMenu _reply;
// The field for text input
pyrodactyl::ui::TextArea _textin;
// The info for intro events
pyrodactyl::ui::ChapterIntro _intro;
// Store the current event data temporarily
GameEvent *_curEvent;
bool _player;
pyrodactyl::anim::Sprite *_curSp;
void updateDialogBox(Info &info, pyrodactyl::level::Level &level);
public:
// The object used to draw the character screen
pyrodactyl::ui::PersonScreen _per;
// A flag used to stop drawing the game for a better fade in/fade out experience
bool _drawGame;
Manager() {
init();
}
~Manager() {}
void init();
void load(rapidxml::xml_node<char> *node, pyrodactyl::ui::ParagraphData &popup);
void draw(Info &info, pyrodactyl::ui::HUD &hud, pyrodactyl::level::Level &level);
// cur_per is also updated here
void internalEvents(Info &info, pyrodactyl::level::Level &level, Common::Array<EventResult> &result);
void handleEvents(Info &info, const Common::String &playerId, Common::Event &event,
pyrodactyl::ui::HUD &hud, pyrodactyl::level::Level &level, Common::Array<EventResult> &result);
void calcActiveSeq(Info &info, pyrodactyl::level::Level &level, const Rect &camera);
void endSequence(const Common::String &curloc);
bool eventInProgress();
void saveState(rapidxml::xml_document<> &doc, rapidxml::xml_node<char> *root);
void loadState(rapidxml::xml_node<char> *node);
void setUI();
};
} // End of namespace event
} // End of namespace pyrodactyl
} // End of namespace Crab
#endif // CRAB_GAMEEVENTMANAGER_H

View File

@@ -0,0 +1,68 @@
/* 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 code is based on the CRAB engine
*
* Copyright (c) Arvind Raja Yadav
*
* Licensed under MIT
*
*/
#include "crab/event/quest.h"
namespace Crab {
using namespace pyrodactyl::event;
Quest::Quest(const Common::String &title, const Common::String &text, const bool &unread, const bool &marker) : _title(title) {
_text.insert_at(0, text);
_unread = unread;
_marker = marker;
}
void Quest::loadState(rapidxml::xml_node<char> *node) {
loadStr(_title, "title", node);
loadBool(_unread, "unread", node);
loadBool(_marker, "marker", node);
for (rapidxml::xml_node<char> *n = node->first_node("info"); n != nullptr; n = n->next_sibling("info"))
_text.push_back(n->value());
}
void Quest::saveState(rapidxml::xml_document<> &doc, rapidxml::xml_node<char> *root) {
rapidxml::xml_node<char> *child = doc.allocate_node(rapidxml::node_element, "quest");
child->append_attribute(doc.allocate_attribute("title", _title.c_str()));
saveBool(_unread, "unread", doc, child);
saveBool(_marker, "marker", doc, child);
for (const auto &i : _text) {
rapidxml::xml_node<char> *grandchild = doc.allocate_node(rapidxml::node_element, "info");
grandchild->value(i.c_str());
child->append_node(grandchild);
}
root->append_node(child);
}
} // End of namespace Crab

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