/* 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/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; using namespace pyrodactyl::event; //------------------------------------------------------------------------ // Purpose: Let the level tick along and notify if we go into an exit //------------------------------------------------------------------------ LevelResult Level::internalEvents(Info &info, Common::Array &result, Common::Array &endSeq, bool eventInProgress) { LevelResult lResult; // input.internalEvents(); calcTrigCollide(info); if (_terrain.collideWithExit(_objects[_playerIndex].boundRect(), lResult)) { if (playerInCombat(info)) { _insideExit = true; } else if (_insideExit == false) { lResult._type = LR_LEVEL; return lResult; } } else { _insideExit = false; if (playerInCombat(info)) _showmap._current = false; else _showmap._current = _showmap._normal; } if (eventInProgress) playerStop(); else think(info, result, endSeq, lResult._val); if (lResult._val != "") info.lastPerson(lResult._val); else if (collidingWithObject(info, lResult._val)) info.lastPerson(lResult._val); else info.lastPerson(""); return lResult; } //------------------------------------------------------------------------ // Purpose: Let the AI think and animate the level //------------------------------------------------------------------------ void Level::think(pyrodactyl::event::Info &info, Common::Array &result, Common::Array &endSeq, Common::String &id) { uint index = 0; for (auto i = _objects.begin(); i != _objects.end(); ++i, ++index) { // Only bother if the sprite is visible if (i->visible()) { // TODO: Find a place where this should be initialized... (SZ) if (i->_pathing._grid == nullptr) i->_pathing.initialize(&_pathfindingGrid); // If a sprite is dead, nothing else matters if (info.state(i->id()) == PST_DYING) { if (i->lastFrame()) { info.statSet(i->id(), STAT_HEALTH, 0); info.state(i->id(), PST_KO); i->effectImg(false); i->inputStop(); } } else if (info.state(i->id()) != PST_KO) { Rect boundRect = i->boundRect(); i->_pathing.setPosition(Vector2f((float)(boundRect.x + boundRect.w / 2), (float)boundRect.y + boundRect.h / 2)); i->_pathing.update(0); // For the AI sprites if (index != _playerIndex) { switch (info.state(i->id())) { case PST_FIGHT: { // Only attack if the player is alive if (info.state(_objects[_playerIndex].id()) < PST_KO) i->attack(info, _objects[_playerIndex], _scDefault); } break; case PST_FLEE: i->flee(info, _terrain._areaExit, _scDefault); break; case PST_NORMAL: if (i->takingDamage(_objects[_playerIndex], _scDefault)) { if (_firstHit) { battleAlert(info); _firstHit = false; } } /*else TraversePath(*i);*/ break; default: break; } // Only do this if the player is alive if (info.state(_objects[_playerIndex].id()) < PST_KO) _objects[_playerIndex].exchangeDamage(info, *i, _scDefault); } else { // For the player sprite boundRect = i->boundRect(); i->_pathing.setPosition(Vector2f((float)(boundRect.x + boundRect.w / 2), (float)boundRect.y + boundRect.h / 2)); i->_pathing.update(0); i->moveToDestPathfinding(info, _scDefault); } i->internalEvents(info, playerId(), result, endSeq); moveObject(info, *i); } i->animate(info); } } // Background sprites don't move for (auto &i : _background) i.animate(PST_NORMAL); // Flier sprites fly across the screen from left to right or vice versa // The movement is semi-random for (auto &i : _fly) { i.flyAround(_camera, _scDefault); i.animate(PST_NORMAL); } } //------------------------------------------------------------------------ // Purpose: Once a player provokes any sprite, call this function //------------------------------------------------------------------------ void Level::battleAlert(pyrodactyl::event::Info &info) { uint index = 0; for (auto i = _objects.begin(); i != _objects.end(); ++i, ++index) { if (index != _playerIndex && i->visible() && info.state(i->id()) != PST_KO) { switch (info.type(i->id())) { case PE_NEUTRAL: case PE_HOSTILE: info.state(i->id(), PST_FIGHT); break; case PE_COWARD: info.state(i->id(), PST_FLEE); break; default: break; } } } } //------------------------------------------------------------------------ // Purpose: Move a sprite according to its velocity //------------------------------------------------------------------------ void Level::moveObject(pyrodactyl::event::Info &info, pyrodactyl::anim::Sprite &s) { // Update x,y coordinates according to velocity s.move(_scDefault); // First check collision with objects and forbidden areas inside a level if (collidingWithLevel(info, s)) s.resolveCollide(); // Finally see if we are inside the overall level bounds if (!_terrain.insideWalk(s.boundRect())) s.resolveInside(_terrain.areaWalk()); } //------------------------------------------------------------------------ // Purpose: If any other object is trying to kill you, you are in combat //------------------------------------------------------------------------ bool Level::playerInCombat(pyrodactyl::event::Info &info) { uint index = 0; for (auto i = _objects.begin(); i != _objects.end(); ++i, ++index) if (index != _playerIndex && info.state(i->id()) == PST_FIGHT && i->visible()) return true; return false; } } // End of namespace Crab