/* 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 . * */ /* * This code is based on the CRAB engine * * Copyright (c) Arvind Raja Yadav * * Licensed under MIT * */ #include "crab/XMLDoc.h" #include "crab/ScreenSettings.h" #include "crab/level/level.h" namespace Crab { using namespace TMX; using namespace pyrodactyl::stat; using namespace pyrodactyl::anim; using namespace pyrodactyl::level; using namespace pyrodactyl::image; using namespace pyrodactyl::people; using namespace pyrodactyl::input; //------------------------------------------------------------------------ // Purpose: Used to sort background sprites //------------------------------------------------------------------------ static bool compSpriteLayer(const Sprite &a, const Sprite &b) { return (a._layer < b._layer); } //------------------------------------------------------------------------ // Purpose: Load the level //------------------------------------------------------------------------ void Level::load(const Common::Path &filename, pyrodactyl::event::Info &info, pyrodactyl::event::TriggerSet &gameOver, const int &playerX, const int &playerY) { reset(); XMLDoc conf(filename); if (conf.ready()) { rapidxml::xml_node *node = conf.doc()->first_node("level"); if (nodeValid(node)) { Common::String vis; loadStr(vis, "map", node, false); if (vis == "false") _showmap.set(false); else _showmap.set(true); if (nodeValid("preview", node)) loadPath(_previewPath, "path", node->first_node("preview")); if (nodeValid("music", node)) { loadNum(_music._id, "id", node->first_node("music")); g_engine->_musicManager->playMusic(_music._id); } if (nodeValid("map", node)) { rapidxml::xml_node *mapnode = node->first_node("map"); Common::Path path; Common::String tmxfile; loadPath(path, "path", mapnode); loadStr(tmxfile, "file", mapnode); _terrain.load(path, tmxfile); // Remember to load the terrain data before constructing the pathfinding grid _pathfindingGrid.setupNodes(_terrain); _terrain._grid = &_pathfindingGrid; if (nodeValid("loc", mapnode)) _mapLoc.load(mapnode->first_node("loc")); if (nodeValid("clip", mapnode)) { rapidxml::xml_node *clipnode = mapnode->first_node("clip"); loadNum(_mapClip._id, "id", clipnode); _mapClip._rect.load(clipnode); } } if (nodeValid("sprites", node)) { rapidxml::xml_node *spritenode = node->first_node("sprites"); int count = 0; for (auto n = spritenode->first_node(); n != nullptr; n = n->next_sibling(), ++count) { Sprite s; s.load(n, _animSet); Common::String str = n->name(); if (str == "player") { _playerIndex = _objects.size(); if (playerX != -1 && playerY != -1) { s.x(playerX); s.y(playerY); } } _objects.push_back(s); } } if (nodeValid("background", node)) { rapidxml::xml_node *spritenode = node->first_node("background"); for (auto n = spritenode->first_node(); n != nullptr; n = n->next_sibling()) { Sprite s; s.load(n, _animSet); _background.push_back(s); } Common::sort(_background.begin(), _background.end(), compSpriteLayer); } if (nodeValid("fly", node)) { rapidxml::xml_node *spritenode = node->first_node("fly"); for (auto n = spritenode->first_node(); n != nullptr; n = n->next_sibling()) { Sprite s; s.load(n, _animSet); // Set the timer target for the first time //s.ai_data.walk.timer.Target(sc_default.fly.delay_min + (gRandom.Num() % sc_default.fly.delay_max)); s._aiData._walk._timer.target(_scDefault._fly._delayMax); _fly.push_back(s); } } if (nodeValid("movement", node)) { rapidxml::xml_node *movnode = node->first_node("movement"); for (auto n = movnode->first_node("set"); n != nullptr; n = n->next_sibling("set")) _moveSet.push_back(n); } if (nodeValid("popup", node, false)) _pop.load(node->first_node("popup")); gameOver.clear(); if (nodeValid("game_over", node, false)) gameOver.load(node->first_node("game_over")); else if (_playerIndex < _objects.size()) { // The default lose condition is the death of the player using namespace pyrodactyl::event; Trigger t; t._type = TRIG_STAT; t._target = STATNAME_HEALTH; t._subject = _objects[_playerIndex].id(); t._operation = "<"; t._val = "1"; gameOver.add(t); } calcProperties(info); } } setCamera(); } //------------------------------------------------------------------------ // Purpose: Build an index of all animation files, called once at start //------------------------------------------------------------------------ void Level::loadMoves(const Common::Path &filename) { XMLDoc movList(filename); if (movList.ready()) { rapidxml::xml_node *node = movList.doc()->first_node("movelist"); for (auto n = node->first_node("set"); n != nullptr; n = n->next_sibling("set")) { uint pos = _animSet.size(); loadNum(pos, "id", n); if (pos >= _animSet.size()) _animSet.resize(pos + 1); // See if there is an alternate moveset for low quality setting // If no, just load the regular one if (!g_engine->_screenSettings->_quality) { if (!loadPath(_animSet[pos], "path_low", n)) loadPath(_animSet[pos], "path", n); } else loadPath(_animSet[pos], "path", n); } } } //------------------------------------------------------------------------ // Purpose: Load the default sprite constant parameters //------------------------------------------------------------------------ void Level::loadConst(const Common::Path &filename) { XMLDoc doc(filename); if (doc.ready()) { rapidxml::xml_node *node = doc.doc()->first_node("constant"); if (nodeValid(node)) _scDefault.load(node); } } //------------------------------------------------------------------------ // Purpose: Save all sprite positions to save file //------------------------------------------------------------------------ void Level::saveState(rapidxml::xml_document<> &doc, rapidxml::xml_node *root) { root->append_attribute(doc.allocate_attribute("player_index", g_engine->_stringPool->get(_playerIndex))); for (auto &i : _objects) { rapidxml::xml_node *child = doc.allocate_node(rapidxml::node_element, "sprite"); i.saveState(doc, child); root->append_node(child); } } //------------------------------------------------------------------------ // Purpose: Load all sprite positions from save file //------------------------------------------------------------------------ void Level::loadState(rapidxml::xml_node *node) { loadNum(_playerIndex, "player_index", node); auto i = _objects.begin(); for (auto *n = node->first_node("sprite"); n != nullptr && i != _objects.end(); n = n->next_sibling("sprite"), ++i) i->loadState(n); } } // End of namespace Crab