Files
GDevelop/Extensions/PathBehavior/PathBehavior.cpp
T
Florian Rival a8559bfbbc Add clang-format to format (C++) source files automatically (#491)
* Update all CMakeLists of extensions to use clang-format
* Run clang-format on all Extensions
* Update GDCore CMakeLists.txt to add clang-format
* Run clang-format on GDCore files
* Update GDJS and GDCpp CMakeLists.txt to add clang-format
* Run clang-format on GDCpp and GDJS files
2018-05-09 15:57:38 -07:00

357 lines
10 KiB
C++

/**
GDevelop - Path Behavior Extension
Copyright (c) 2010-2016 Florian Rival (Florian.Rival@gmail.com)
This project is released under the MIT License.
*/
#include "PathBehavior.h"
#include <cmath>
#include <memory>
#include "GDCpp/Runtime/CommonTools.h"
#include "GDCpp/Runtime/Project/Layout.h"
#include "GDCpp/Runtime/RuntimeObject.h"
#include "GDCpp/Runtime/RuntimeScene.h"
#include "GDCpp/Runtime/Serialization/SerializerElement.h"
#include "PathBehaviorEditor.h"
#include "RuntimeScenePathDatas.h"
PathBehavior::PathBehavior()
: speed(200),
timeOnSegment(0),
totalSegmentTime(0),
currentSegment(0),
stopAtEnd(false),
reverseAtEnd(false),
followAngle(false),
angleOffset(0),
isPathLoaded(false),
futureSegment(-1),
futurePosition(-1),
runtimeScenesPathDatas(NULL) {
Reset();
SetPath("Object main path", std::vector<sf::Vector2f>(1, sf::Vector2f(0, 0)));
ChangeCurrentPath("Object main path");
}
PathBehavior::PathBehavior(const PathBehavior &cl) : Behavior(cl) { Init(cl); }
PathBehavior &PathBehavior::operator=(const PathBehavior &cl) {
Init(cl);
return *this;
}
void PathBehavior::Init(const PathBehavior &cl) {
name = cl.name;
type = cl.type;
isPathLoaded = false;
pathName = cl.pathName;
path = cl.path;
offset = cl.offset;
speed = cl.speed;
timeOnSegment = cl.timeOnSegment;
totalSegmentTime = cl.totalSegmentTime;
currentSegment = cl.currentSegment;
stopAtEnd = cl.stopAtEnd;
reverseAtEnd = cl.reverseAtEnd;
followAngle = cl.followAngle;
angleOffset = cl.angleOffset;
localePaths = cl.localePaths;
runtimeScenesPathDatas = cl.runtimeScenesPathDatas;
}
void PathBehavior::Reset() { EnterSegment(0); }
void PathBehavior::EnterSegment(std::size_t segmentNumber) {
currentSegment = segmentNumber;
if (!path.empty() && currentSegment < path.size() - 1) {
sf::Vector2f newPath = (path[currentSegment + 1] - path[currentSegment]);
totalSegmentTime = sqrtf(newPath.x * newPath.x + newPath.y * newPath.y);
timeOnSegment = 0;
if (followAngle) object->SetAngle(GetAngleOfSegment(newPath) + angleOffset);
}
}
PathBehavior::~PathBehavior() {}
#if defined(GD_IDE_ONLY)
void PathBehavior::EditBehavior(wxWindow *parent,
gd::Project &game_,
gd::Layout *scene,
gd::MainFrameWrapper &mainFrameWrapper_) {
#if !defined(GD_NO_WX_GUI)
PathBehaviorEditor editor(parent, game_, scene, *this, mainFrameWrapper_);
editor.ShowModal();
#endif
}
#endif
/**
* Called at each frame before events :
* Position the object on the path
*/
void PathBehavior::DoStepPreEvents(RuntimeScene &scene) {
if (!isPathLoaded) {
LoadPath(scene);
Reset();
}
// add to the current time along the path
timeOnSegment +=
static_cast<double>(object->GetElapsedTime(scene)) / 1000000.0 * speed;
// if I reached the end of this segment, move to a new segment
if (timeOnSegment >= totalSegmentTime && currentSegment < path.size())
EnterSegment(currentSegment + 1);
// Position object on the segment
sf::Vector2f newPos;
if (!path.empty() && currentSegment < path.size() - 1)
newPos = offset + path[currentSegment] +
(path[currentSegment + 1] - path[currentSegment]) *
(timeOnSegment / totalSegmentTime);
else {
if (stopAtEnd && !path.empty())
newPos = path.back() + offset;
else if (reverseAtEnd) {
std::reverse(path.begin(), path.end());
EnterSegment(0);
if (!path.empty()) newPos = path.front() + offset;
} else {
EnterSegment(0);
if (!path.empty()) newPos = path.front() + offset;
}
}
object->SetX(newPos.x);
object->SetY(newPos.y);
return;
}
void PathBehavior::DoStepPostEvents(RuntimeScene &scene) {
if (!isPathLoaded) {
LoadPath(scene);
Reset();
}
if (futureSegment != -1) {
EnterSegment(futureSegment);
futureSegment = -1;
}
if (futurePosition != -1) {
timeOnSegment = futurePosition * totalSegmentTime;
futurePosition = -1;
}
}
#if defined(GD_IDE_ONLY)
void PathBehavior::SerializeTo(gd::SerializerElement &element) const {
SerializePathsTo(element.AddChild("paths"));
element.SetAttribute("currentPath", GetCurrentPathName());
element.SetAttribute("speed", GetSpeed());
element.SetAttribute("offsetX", GetOffsetX());
element.SetAttribute("offsetY", GetOffsetY());
element.SetAttribute("angleOffset", angleOffset);
element.SetAttribute("reverseAtEnd", ReverseAtEnd());
element.SetAttribute("stopAtEnd", StopAtEnd());
element.SetAttribute("followAngle", FollowAngle());
}
void PathBehavior::SerializePathsTo(gd::SerializerElement &element) const {
element.ConsiderAsArrayOf("path");
for (std::map<gd::String, std::vector<sf::Vector2f> >::const_iterator it =
localePaths.begin();
it != localePaths.end();
it++) {
gd::SerializerElement &pathElement = element.AddChild("path");
pathElement.SetAttribute("name", it->first);
pathElement.SetAttribute("coords",
GetStringFromCoordsVector(it->second, '/', ';'));
}
}
#endif
void PathBehavior::UnserializeFrom(const gd::SerializerElement &element) {
UnserializePathsFrom(element.GetChild("paths", 0, "Paths"));
ChangeCurrentPath(element.GetStringAttribute("currentPath"));
speed = element.GetDoubleAttribute("speed");
offset.x = element.GetDoubleAttribute("offsetX");
offset.y = element.GetDoubleAttribute("offsetY");
angleOffset = element.GetDoubleAttribute("angleOffset");
reverseAtEnd = element.GetBoolAttribute("reverseAtEnd");
stopAtEnd = element.GetBoolAttribute("stopAtEnd");
followAngle = element.GetBoolAttribute("followAngle");
}
void PathBehavior::UnserializePathsFrom(const gd::SerializerElement &element) {
localePaths.clear();
element.ConsiderAsArrayOf("path", "Path");
for (std::size_t i = 0; i < element.GetChildrenCount(); ++i) {
const gd::SerializerElement &pathElement = element.GetChild(i);
localePaths[pathElement.GetStringAttribute("name")] =
GetCoordsVectorFromString(
pathElement.GetStringAttribute("coords"), '/', ';');
}
if (localePaths.empty())
localePaths["Object main path"] =
std::vector<sf::Vector2f>(1, sf::Vector2f(0, 0));
}
float PathBehavior::GetOffsetX() const { return offset.x; }
void PathBehavior::SetOffsetX(float off) { offset.x = off; }
float PathBehavior::GetOffsetY() const { return offset.y; }
void PathBehavior::SetOffsetY(float off) { offset.y = off; }
void PathBehavior::SetSpeed(float sp) { speed = sp; }
float PathBehavior::GetSpeed() const { return speed; }
void PathBehavior::SetAngleOffset(float off) {
angleOffset = off;
// Update the angle
if (isPathLoaded) {
sf::Vector2f newPath = (path[currentSegment + 1] - path[currentSegment]);
if (followAngle) object->SetAngle(GetAngleOfSegment(newPath) + angleOffset);
}
}
float PathBehavior::GetAngleOffset() const { return angleOffset; }
void PathBehavior::Reverse() {
float tempTimeOnSegment = timeOnSegment;
std::reverse(path.begin(), path.end());
EnterSegment(path.size() - currentSegment - 2);
timeOnSegment = totalSegmentTime - tempTimeOnSegment;
}
const std::vector<sf::Vector2f> &PathBehavior::GetPath(
const gd::String &_name) const {
return localePaths.at(_name);
}
void PathBehavior::SetPath(const gd::String &_name,
std::vector<sf::Vector2f> _path) {
localePaths[_name] = _path;
}
void PathBehavior::ChangeCurrentPath(const gd::String &_name) {
pathName = _name;
isPathLoaded = false;
Reset();
}
const gd::String &PathBehavior::GetCurrentPathName() const { return pathName; }
void PathBehavior::LoadPath(RuntimeScene &scene) {
if (localePaths.count(pathName) == 0) {
if (runtimeScenesPathDatas == NULL)
runtimeScenesPathDatas = static_cast<RuntimeScenePathDatas *>(
scene.GetBehaviorSharedData(name).get());
if (runtimeScenesPathDatas != NULL &&
runtimeScenesPathDatas->globalPaths.count(pathName) != 0)
path = std::vector<sf::Vector2f>(
runtimeScenesPathDatas->globalPaths.at(pathName));
} else {
path = std::vector<sf::Vector2f>(GetPath(pathName));
}
isPathLoaded = true;
}
std::vector<gd::String> PathBehavior::GetListOfPathsNames() const {
std::vector<gd::String> names;
for (std::map<gd::String, std::vector<sf::Vector2f> >::const_iterator it =
localePaths.begin();
it != localePaths.end();
it++) {
names.push_back((*it).first);
}
return names;
}
void PathBehavior::DeleteAllPaths() { localePaths.clear(); }
void PathBehavior::SetCurrentSegment(std::size_t seg) { futureSegment = seg; }
int PathBehavior::GetCurrentSegment() {
return futureSegment == -1 ? currentSegment : futureSegment;
}
float PathBehavior::GetPositionOnSegment() {
return futurePosition == -1 ? (timeOnSegment / totalSegmentTime)
: futurePosition;
}
void PathBehavior::SetPositionOnSegment(float pos) {
// timeOnSegment = pos * totalSegmentTime;
futurePosition = pos;
}
gd::String PathBehavior::GetStringFromCoordsVector(
const std::vector<sf::Vector2f> &vec,
char32_t coordsSep,
char32_t composantSep) {
gd::String coordsStr;
for (std::size_t a = 0; a < vec.size(); a++) {
coordsStr += gd::String::From(vec.at(a).x);
coordsStr.push_back(composantSep);
coordsStr += gd::String::From(vec.at(a).y);
if (a != vec.size() - 1) coordsStr.push_back(coordsSep);
}
return coordsStr;
}
std::vector<sf::Vector2f> PathBehavior::GetCoordsVectorFromString(
const gd::String &str, char32_t coordsSep, char32_t composantSep) {
std::vector<sf::Vector2f> coordsVec;
std::vector<gd::String> coordsDecomposed = str.Split(coordsSep);
for (std::size_t a = 0; a < coordsDecomposed.size(); a++) {
std::vector<gd::String> coordXY =
coordsDecomposed.at(a).Split(composantSep);
if (coordXY.size() != 2) continue;
sf::Vector2f newCoord(coordXY.at(0).To<float>(), coordXY.at(1).To<float>());
coordsVec.push_back(newCoord);
}
return coordsVec;
}
#define PI 3.14159265
float PathBehavior::GetAngleOfSegment(sf::Vector2f &seg) {
float norm = std::abs(sqrtf(pow(seg.x, 2) + pow(seg.y, 2)));
if (seg.y >= 0) {
return acos(seg.x / norm) * 180.0 / PI;
} else {
return -acos(seg.x / norm) * 180.0 / PI;
}
}