Mercurial > fife-parpg
diff engine/core/model/structures/instance.cpp @ 0:4a0efb7baf70
* Datasets becomes the new trunk and retires after that :-)
author | mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Sun, 29 Jun 2008 18:44:17 +0000 |
parents | |
children | 90005975cdbb |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/engine/core/model/structures/instance.cpp Sun Jun 29 18:44:17 2008 +0000 @@ -0,0 +1,485 @@ +/*************************************************************************** + * Copyright (C) 2005-2008 by the FIFE team * + * http://www.fifengine.de * + * This file is part of FIFE. * + * * + * FIFE 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +// Standard C++ library includes +#include <iostream> + +// 3rd party library includes +#include <SDL.h> + +// FIFE includes +// These includes are split up in two parts, separated by one empty line +// First block: files included from the FIFE root src directory +// Second block: files included from the same folder +#include "util/log/logger.h" +#include "util/base/exception.h" +#include "util/math/fife_math.h" +#include "model/metamodel/grids/cellgrid.h" +#include "model/metamodel/abstractpather.h" +#include "model/metamodel/action.h" +#include "model/metamodel/timeprovider.h" +#include "model/structures/layer.h" +#include "model/structures/map.h" +#include "model/structures/instancetree.h" + +#include "instance.h" + +namespace FIFE { + static Logger _log(LM_INSTANCE); + + class ActionInfo { + public: + ActionInfo(AbstractPather* pather, const Location& curloc): + m_action(NULL), + m_target(NULL), + m_speed(0), + m_repeating(false), + m_action_start_time(SDL_GetTicks()), + m_prev_call_time(m_action_start_time), + m_pather_session_id(-1), + m_pather(pather), + m_leader(NULL) {} + + ~ActionInfo() { + if (m_pather_session_id != -1) { + m_pather->cancelSession(m_pather_session_id); + } + delete m_target; + m_target = NULL; + } + + // Current action, owned by object + Action* m_action; + // target location for ongoing movement + Location* m_target; + // current movement speed + double m_speed; + // should action be repeated? used only for non-moving actions, moving ones repeat until movement is finished + bool m_repeating; + // action start time (ticks) + unsigned int m_action_start_time; + // ticks since last call + unsigned int m_prev_call_time; + // current time for action processing (set by Instance::update), optimized to avoid multiple calls to GetTicks + unsigned int m_cur_time; + // session id for pather + int m_pather_session_id; + // pather + AbstractPather* m_pather; + // leader for follow activity + Instance* m_leader; + }; + + class SayInfo { + public: + SayInfo(const std::string& txt, unsigned int duration): + m_txt(txt), + m_duration(duration), + m_start_time(SDL_GetTicks()) { + } + std::string m_txt; + unsigned int m_duration; + unsigned int m_start_time; + }; + + Instance::InstanceActivity::InstanceActivity(Instance& source): + m_location(source.m_location), + m_facinglocation(), + m_action(), + m_speed(0), + m_timemultiplier(1.0), + m_saytxt(""), + m_changelisteners(), + m_actionlisteners(), + m_actioninfo(NULL), + m_sayinfo(NULL), + m_timeprovider(NULL) { + if (source.m_facinglocation) { + m_facinglocation = *source.m_facinglocation; + } + } + + Instance::InstanceActivity::~InstanceActivity() { + delete m_actioninfo; + delete m_sayinfo; + delete m_timeprovider; + } + + void Instance::InstanceActivity::update(Instance& source) { + source.m_changeinfo = ICHANGE_NO_CHANGES; + if (m_location != source.m_location) { + source.m_changeinfo |= ICHANGE_LOC; + m_location = source.m_location; + } + if (source.m_facinglocation && (m_facinglocation != *source.m_facinglocation)) { + source.m_changeinfo |= ICHANGE_FACING_LOC; + m_facinglocation = *source.m_facinglocation; + } + if (m_actioninfo && (m_speed != m_actioninfo->m_speed)) { + source.m_changeinfo |= ICHANGE_SPEED; + m_speed = m_actioninfo->m_speed; + } + if (m_actioninfo && (m_action != m_actioninfo->m_action)) { + source.m_changeinfo |= ICHANGE_ACTION; + m_action = m_actioninfo->m_action; + } + if (m_timeprovider && (m_timemultiplier != m_timeprovider->getMultiplier())) { + source.m_changeinfo |= ICHANGE_TIME_MULTIPLIER; + m_timemultiplier = m_timeprovider->getMultiplier(); + } + if (m_sayinfo && (m_saytxt != m_sayinfo->m_txt)) { + source.m_changeinfo |= ICHANGE_SAYTEXT; + m_saytxt = m_sayinfo->m_txt; + } + + if (source.m_changeinfo != ICHANGE_NO_CHANGES) { + std::vector<InstanceChangeListener*>::iterator i = m_changelisteners.begin(); + while (i != m_changelisteners.end()) { + (*i)->onInstanceChanged(&source, source.m_changeinfo); + ++i; + } + } + } + + Instance::Instance(Object* object, const Location& location, const std::string& identifier): + m_id(identifier), + m_rotation(0), + m_activity(NULL), + m_changeinfo(ICHANGE_NO_CHANGES), + m_object(object), + m_location(location), + m_facinglocation(NULL), + m_visual(NULL) { + } + + Instance::~Instance() { + delete m_activity; + delete m_facinglocation; + delete m_visual; + } + + void Instance::initializeChanges() { + if (!m_activity) { + m_activity = new InstanceActivity(*this); + } + } + + void Instance::setLocation(const Location& loc) { + initializeChanges(); + m_location = loc; + bindTimeProvider(); + } + + void Instance::setRotation(int rotation) { + m_rotation = rotation; + m_changeinfo |= ICHANGE_ROTATION; + } + + void Instance::addActionListener(InstanceActionListener* listener) { + initializeChanges(); + m_activity->m_actionlisteners.push_back(listener); + } + + void Instance::removeActionListener(InstanceActionListener* listener) { + if (!m_activity) { + return; + } + std::vector<InstanceActionListener*>::iterator i = m_activity->m_actionlisteners.begin(); + while (i != m_activity->m_actionlisteners.end()) { + if ((*i) == listener) { + m_activity->m_actionlisteners.erase(i); + return; + } + ++i; + } + FL_WARN(_log, "Cannot remove unknown listener"); + } + + void Instance::addChangeListener(InstanceChangeListener* listener) { + initializeChanges(); + m_activity->m_changelisteners.push_back(listener); + } + + void Instance::removeChangeListener(InstanceChangeListener* listener) { + if (!m_activity) { + return; + } + std::vector<InstanceChangeListener*>::iterator i = m_activity->m_changelisteners.begin(); + while (i != m_activity->m_changelisteners.end()) { + if ((*i) == listener) { + m_activity->m_changelisteners.erase(i); + return; + } + ++i; + } + FL_WARN(_log, "Cannot remove unknown listener"); + } + + void Instance::initalizeAction(const std::string& action_name) { + assert(m_object); + assert(m_activity); + if (m_activity->m_actioninfo) { + delete m_activity->m_actioninfo; + m_activity->m_actioninfo = NULL; + } + m_activity->m_actioninfo = new ActionInfo(m_object->getPather(), m_location); + m_activity->m_actioninfo->m_action = m_object->getAction(action_name); + if (!m_activity->m_actioninfo->m_action) { + delete m_activity->m_actioninfo; + m_activity->m_actioninfo = NULL; + throw NotFound(std::string("action ") + action_name + " not found"); + } + } + + void Instance::move(const std::string& action_name, const Location& target, const double speed) { + initializeChanges(); + initalizeAction(action_name); + m_activity->m_actioninfo->m_target = new Location(target); + m_activity->m_actioninfo->m_speed = speed; + setFacingLocation(target); + FL_DBG(_log, LMsg("starting action ") << action_name << " from" << m_location << " to " << target << " with speed " << speed); + } + + void Instance::follow(const std::string& action_name, Instance* leader, const double speed) { + initializeChanges(); + initalizeAction(action_name); + m_activity->m_actioninfo->m_target = new Location(leader->getLocationRef()); + m_activity->m_actioninfo->m_speed = speed; + m_activity->m_actioninfo->m_leader = leader; + setFacingLocation(*m_activity->m_actioninfo->m_target); + FL_DBG(_log, LMsg("starting action ") << action_name << " from" << m_location << " to " << *m_activity->m_actioninfo->m_target << " with speed " << speed); + } + + void Instance::act(const std::string& action_name, const Location& direction, bool repeating) { + initializeChanges(); + initalizeAction(action_name); + m_activity->m_actioninfo->m_repeating = repeating; + setFacingLocation(direction); + } + + void Instance::say(const std::string& text, unsigned int duration) { + initializeChanges(); + delete m_activity->m_sayinfo; + m_activity->m_sayinfo = NULL; + + if (text != "") { + m_activity->m_sayinfo = new SayInfo(text, duration); + } + } + + const std::string* Instance::getSayText() const { + if (m_activity && m_activity->m_sayinfo) { + return &m_activity->m_sayinfo->m_txt; + } + return NULL; + } + + void Instance::setFacingLocation(const Location& loc) { + if (!m_facinglocation) { + m_facinglocation = new Location(loc); + } else { + *m_facinglocation = loc; + } + } + + bool Instance::process_movement() { + FL_DBG(_log, "Moving..."); + ActionInfo* info = m_activity->m_actioninfo; + // timeslice for this movement + unsigned int timedelta = scaleTime(getTotalTimeMultiplier(), info->m_cur_time - info->m_prev_call_time); + FL_DBG(_log, LMsg("timedelta ") << timedelta << " prevcalltime " << info->m_prev_call_time); + // how far we can travel + double distance_to_travel = (static_cast<double>(timedelta) / 1000.0) * info->m_speed; + FL_DBG(_log, LMsg("dist ") << distance_to_travel); + + Location nextLocation = m_location; + info->m_pather_session_id = info->m_pather->getNextLocation( + this, *info->m_target, + distance_to_travel, nextLocation, *m_facinglocation, + info->m_pather_session_id); + m_location.getLayer()->getInstanceTree()->removeInstance(this); + m_location = nextLocation; + //ExactModelCoordinate a = nextLocation.getMapCoordinates(); + //ExactModelCoordinate b = m_actioninfo->m_target->getMapCoordinates(); + m_location.getLayer()->getInstanceTree()->addInstance(this); + // return if we are close enough to target to stop + if (info->m_pather_session_id == -1) { + return true; + } + return false; + } + + InstanceChangeInfo Instance::update(unsigned int curticks) { + if (!m_activity) { + return ICHANGE_NO_CHANGES; + } + m_activity->update(*this); + if (!m_activity->m_timeprovider) { + bindTimeProvider(); + } + + if (curticks == 0) { + curticks = SDL_GetTicks(); + } + ActionInfo* info = m_activity->m_actioninfo; + if (info) { + info->m_cur_time = curticks; + FL_DBG(_log, LMsg("updating instance, ticks = ") << curticks); + + if (info->m_target) { + FL_DBG(_log, "action contains target for movement"); + // update target if needed + if (info->m_leader && (info->m_leader->getLocationRef() != *info->m_target)) { + *info->m_target = info->m_leader->getLocation(); + } + bool movement_finished = process_movement(); + if (movement_finished) { + FL_DBG(_log, "movement finished"); + finalizeAction(); + } + } else { + FL_DBG(_log, "action does not contain target for movement"); + if (scaleTime(getTotalTimeMultiplier(), curticks - info->m_action_start_time) >= info->m_action->getDuration()) { + if (info->m_repeating) { + info->m_action_start_time = curticks; + } else { + finalizeAction(); + } + } + } + + m_activity->m_actioninfo->m_prev_call_time = curticks; + } + if (m_activity->m_sayinfo) { + if (m_activity->m_sayinfo->m_duration > 0) { + if (scaleTime(getTotalTimeMultiplier(), curticks - m_activity->m_sayinfo->m_start_time) > m_activity->m_sayinfo->m_duration) { + say(""); + } + } + } + return m_changeinfo; + } + + void Instance::finalizeAction() { + FL_DBG(_log, "finalizing action"); + assert(m_activity); + assert(m_activity->m_actioninfo); + + Action* action = m_activity->m_actioninfo->m_action; + delete m_activity->m_actioninfo; + m_activity->m_actioninfo = NULL; + + std::vector<InstanceActionListener*>::iterator i = m_activity->m_actionlisteners.begin(); + while (i != m_activity->m_actionlisteners.end()) { + (*i)->onInstanceActionFinished(this, action); + ++i; + } + } + + Action* Instance::getCurrentAction() const { + if (m_activity && m_activity->m_actioninfo) { + return m_activity->m_actioninfo->m_action; + } + return NULL; + } + + Location Instance::getTargetLocation() const { + if (m_activity && m_activity->m_actioninfo && m_activity->m_actioninfo->m_target) { + return *m_activity->m_actioninfo->m_target; + } + return m_location; + } + + double Instance::getMovementSpeed() const { + if (m_activity && m_activity->m_actioninfo) { + return m_activity->m_actioninfo->m_speed; + } + return 0; + } + + Location Instance::getFacingLocation() { + return this->getFacingLocationRef(); + } + + Location& Instance::getFacingLocationRef() { + if (!m_facinglocation) { + m_facinglocation = new Location(m_location); + m_facinglocation->setExactLayerCoordinates(m_facinglocation->getExactLayerCoordinates() + ExactModelCoordinate(1.0, 0.0)); + //m_facinglocation->setLayerCoordinates(ModelCoordinate(1,0)); + } + return *m_facinglocation; + } + + int Instance::getActionRuntime() const { + if (m_activity && m_activity->m_actioninfo) { + return SDL_GetTicks() - m_activity->m_actioninfo->m_action_start_time; + } + return -1; + } + + void Instance::bindTimeProvider() { + float multiplier = 1.0; + if (m_activity->m_timeprovider) { + multiplier = m_activity->m_timeprovider->getMultiplier(); + } + delete m_activity->m_timeprovider; + m_activity->m_timeprovider = NULL; + + if (m_location.getLayer()) { + Map* map = m_location.getLayer()->getMap(); + if (map) { + m_activity->m_timeprovider = new TimeProvider(map->getTimeProvider()); + } + } + if (!m_activity->m_timeprovider) { + m_activity->m_timeprovider = new TimeProvider(NULL); + } + m_activity->m_timeprovider->setMultiplier(multiplier); + } + + void Instance::refresh() { + initializeChanges(); + bindTimeProvider(); + } + + void Instance::setTimeMultiplier(float multip) { + initializeChanges(); + if (!m_activity->m_timeprovider) { + bindTimeProvider(); + } + m_activity->m_timeprovider->setMultiplier(multip); + } + + float Instance::getTimeMultiplier() { + if (m_activity && m_activity->m_timeprovider) { + return m_activity->m_timeprovider->getMultiplier(); + } + return 1.0; + } + + float Instance::getTotalTimeMultiplier() { + if (m_activity && m_activity->m_timeprovider) { + return m_activity->m_timeprovider->getTotalMultiplier(); + } + return 1.0; + } +} +