Added Vector2 class to replace sf::Vector2, featuring comparison operator.

This commit is contained in:
Felix Ableitner 2013-08-07 17:39:43 +02:00
parent 602cc605e8
commit e469211e95
36 changed files with 271 additions and 230 deletions

View file

@ -23,14 +23,14 @@ const int Game::FPS_GOAL = 60;
*/ */
Game::Game(tgui::Window& window) : Game::Game(tgui::Window& window) :
mWindow(window), mWindow(window),
mWorldView(sf::Vector2f(0, 0), mWindow.getView().getSize()), mWorldView(Vector2f(0, 0), mWindow.getView().getSize()),
mGenerator(mWorld, mPathfinder), mGenerator(mWorld, mPathfinder),
mQuit(false), mQuit(false),
mPaused(false) { mPaused(false) {
mWindow.setFramerateLimit(FPS_GOAL); mWindow.setFramerateLimit(FPS_GOAL);
mWindow.setKeyRepeatEnabled(false); mWindow.setKeyRepeatEnabled(false);
mGenerator.generateCurrentAreaIfNeeded(sf::Vector2f()); mGenerator.generateCurrentAreaIfNeeded(Vector2f());
mPlayer = std::shared_ptr<Player>(new Player(mWorld, mPathfinder, mPlayer = std::shared_ptr<Player>(new Player(mWorld, mPathfinder,
mGenerator.getPlayerSpawn())); mGenerator.getPlayerSpawn()));
mPlayer->setLeftGadget(std::shared_ptr<Gadget>(new Heal())); mPlayer->setLeftGadget(std::shared_ptr<Gadget>(new Heal()));
@ -221,9 +221,9 @@ Game::keyDown(const sf::Event& event) {
/** /**
* Converts a screen coordinate to a world coordinate. * Converts a screen coordinate to a world coordinate.
*/ */
sf::Vector2<float> Vector2<float>
Game::convertCoordinates(int x, int y) { Game::convertCoordinates(int x, int y) {
return mWindow.mapPixelToCoords(sf::Vector2i(x, y), mWorldView); return mWindow.mapPixelToCoords(Vector2i(x, y), mWorldView);
} }
void void

View file

@ -36,7 +36,7 @@ private:
void mouseDown(const sf::Event& event); void mouseDown(const sf::Event& event);
void mouseUp(const sf::Event& event); void mouseUp(const sf::Event& event);
sf::Vector2<float> convertCoordinates(int x, int y); Vector2<float> convertCoordinates(int x, int y);
void updateGui(); void updateGui();
private: private:

View file

@ -32,7 +32,7 @@ Pathfinder::astarArea(Area* start, Area* end) const {
assert(start); assert(start);
assert(end); assert(end);
auto heuristic_cost_estimate = [](Area* start, Area* end) { auto heuristic_cost_estimate = [](Area* start, Area* end) {
return thor::length(sf::Vector2f(end->center - start->center)); return thor::length(Vector2f(end->center - start->center));
}; };
std::set<Area*> closed; std::set<Area*> closed;
@ -91,37 +91,37 @@ Pathfinder::astarArea(Area* start, Area* end) const {
* @param radius Radius of the moving object. * @param radius Radius of the moving object.
* @return Path from end to start (path from start to end in reverse order). * @return Path from end to start (path from start to end in reverse order).
*/ */
std::vector<sf::Vector2f> std::vector<Vector2f>
Pathfinder::getPath(const sf::Vector2f& start, const sf::Vector2f& end, Pathfinder::getPath(const Vector2f& start, const Vector2f& end,
float radius) const { float radius) const {
if (!getArea(end)) if (!getArea(end))
return std::vector<sf::Vector2f>(); return std::vector<Vector2f>();
std::vector<Portal*> portals = astarArea(getArea(start), getArea(end)); std::vector<Portal*> portals = astarArea(getArea(start), getArea(end));
if (portals.empty()) if (portals.empty())
return std::vector<sf::Vector2f>(); return std::vector<Vector2f>();
std::vector<sf::Vector2f> path; std::vector<Vector2f> path;
path.push_back(end); path.push_back(end);
for (auto p : portals) { for (auto p : portals) {
// Find the point on the line of the portal closest to the previous point. // Find the point on the line of the portal closest to the previous point.
sf::Vector2f startToEnd = sf::Vector2f(p->end - p->start); Vector2f startToEnd = Vector2f(p->end - p->start);
float percentage = thor::dotProduct(startToEnd, path.back() - sf::Vector2f(p->start)) / float percentage = thor::dotProduct(startToEnd, path.back() - Vector2f(p->start)) /
thor::squaredLength(startToEnd); thor::squaredLength(startToEnd);
sf::Vector2f point; Vector2f point;
if (percentage < 0 || percentage > 1.0f) { if (percentage < 0 || percentage > 1.0f) {
if (thor::squaredLength(sf::Vector2f(p->start) - path.back()) < if (thor::squaredLength(Vector2f(p->start) - path.back()) <
thor::squaredLength(sf::Vector2f(p->end) - path.back())) { thor::squaredLength(Vector2f(p->end) - path.back())) {
thor::setLength(startToEnd, WALL_DISTANCE_MULTIPLIER * radius); thor::setLength(startToEnd, WALL_DISTANCE_MULTIPLIER * radius);
point = sf::Vector2f(p->start) + startToEnd; point = Vector2f(p->start) + startToEnd;
} }
else { else {
thor::setLength(startToEnd, WALL_DISTANCE_MULTIPLIER * radius); thor::setLength(startToEnd, WALL_DISTANCE_MULTIPLIER * radius);
point = sf::Vector2f(p->end) - startToEnd; point = Vector2f(p->end) - startToEnd;
} }
} }
else else
point = sf::Vector2f(p->start) + startToEnd * percentage; point = Vector2f(p->start) + startToEnd * percentage;
// Take two points on a line orthogonal to the portal. // Take two points on a line orthogonal to the portal.
thor::setLength(startToEnd, radius); thor::setLength(startToEnd, radius);
@ -148,7 +148,7 @@ Pathfinder::insertArea(const sf::FloatRect& rect) {
rect.top * Tile::TILE_SIZE.y - Tile::TILE_SIZE.y / 2.0f, rect.top * Tile::TILE_SIZE.y - Tile::TILE_SIZE.y / 2.0f,
rect.width * Tile::TILE_SIZE.x, rect.width * Tile::TILE_SIZE.x,
rect.height * Tile::TILE_SIZE.y); rect.height * Tile::TILE_SIZE.y);
a.center = sf::Vector2f(a.area.left + a.area.width / 2, a.center = Vector2f(a.area.left + a.area.width / 2,
a.area.top + a.area.height / 2); a.area.top + a.area.height / 2);
mAreas.push_back(a); mAreas.push_back(a);
} }
@ -174,8 +174,8 @@ Pathfinder::generatePortals() {
.getOverlap(Interval::IntervalFromPoints(other.area.top, .getOverlap(Interval::IntervalFromPoints(other.area.top,
other.area.top + other.area.height)); other.area.top + other.area.height));
if (overlap.getLength() > 0) { if (overlap.getLength() > 0) {
portal.start = sf::Vector2f(other.area.left, overlap.start); portal.start = Vector2f(other.area.left, overlap.start);
portal.end = sf::Vector2f(other.area.left, overlap.end); portal.end = Vector2f(other.area.left, overlap.end);
it.portals.push_back(portal); it.portals.push_back(portal);
} }
} }
@ -185,8 +185,8 @@ Pathfinder::generatePortals() {
.getOverlap(Interval::IntervalFromPoints(other.area.top, .getOverlap(Interval::IntervalFromPoints(other.area.top,
other.area.top + other.area.height)); other.area.top + other.area.height));
if (overlap.getLength() > 0) { if (overlap.getLength() > 0) {
portal.start = sf::Vector2f(it.area.left, overlap.start); portal.start = Vector2f(it.area.left, overlap.start);
portal.end = sf::Vector2f(it.area.left, overlap.end); portal.end = Vector2f(it.area.left, overlap.end);
it.portals.push_back(portal); it.portals.push_back(portal);
} }
} }
@ -196,8 +196,8 @@ Pathfinder::generatePortals() {
.getOverlap(Interval::IntervalFromPoints(other.area.left, .getOverlap(Interval::IntervalFromPoints(other.area.left,
other.area.left + other.area.width)); other.area.left + other.area.width));
if (overlap.getLength() > 0) { if (overlap.getLength() > 0) {
portal.start = sf::Vector2f(overlap.start, other.area.top); portal.start = Vector2f(overlap.start, other.area.top);
portal.end = sf::Vector2f(overlap.end, other.area.top); portal.end = Vector2f(overlap.end, other.area.top);
it.portals.push_back(portal); it.portals.push_back(portal);
} }
} }
@ -207,8 +207,8 @@ Pathfinder::generatePortals() {
.getOverlap(Interval::IntervalFromPoints(other.area.left, .getOverlap(Interval::IntervalFromPoints(other.area.left,
other.area.left + other.area.width)); other.area.left + other.area.width));
if (overlap.getLength() > 0) { if (overlap.getLength() > 0) {
portal.start = sf::Vector2f(overlap.start, it.area.top); portal.start = Vector2f(overlap.start, it.area.top);
portal.end = sf::Vector2f(overlap.end, it.area.top); portal.end = Vector2f(overlap.end, it.area.top);
it.portals.push_back(portal); it.portals.push_back(portal);
} }
} }
@ -220,7 +220,7 @@ Pathfinder::generatePortals() {
* Returns the area where point is in. * Returns the area where point is in.
*/ */
Pathfinder::Area* Pathfinder::Area*
Pathfinder::getArea(const sf::Vector2f& point) const { Pathfinder::getArea(const Vector2f& point) const {
for (auto& area : mAreas) { for (auto& area : mAreas) {
if (area.area.contains(point)) if (area.area.contains(point))
// Make the return value non-const for convenience. // Make the return value non-const for convenience.
@ -236,8 +236,8 @@ Pathfinder::getArea(const sf::Vector2f& point) const {
void void
Pathfinder::draw(sf::RenderTarget& target, sf::RenderStates states) const { Pathfinder::draw(sf::RenderTarget& target, sf::RenderStates states) const {
for (auto& area : mAreas) { for (auto& area : mAreas) {
sf::RectangleShape rect(sf::Vector2f(area.area.width, area.area.height)); sf::RectangleShape rect(Vector2f(area.area.width, area.area.height));
rect.setPosition(sf::Vector2f(area.area.left, area.area.top)); rect.setPosition(Vector2f(area.area.left, area.area.top));
rect.setFillColor(sf::Color(area.area.width * 30, 127, 0, 96)); rect.setFillColor(sf::Color(area.area.width * 30, 127, 0, 96));
target.draw(rect); target.draw(rect);
} }

View file

@ -11,6 +11,8 @@
#include <SFML/System.hpp> #include <SFML/System.hpp>
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
#include "util/Vector.h"
/** /**
* Used to find paths between points in the world. * Used to find paths between points in the world.
* *
@ -25,11 +27,11 @@ private:
public: public:
void insertArea(const sf::FloatRect& rect); void insertArea(const sf::FloatRect& rect);
void generatePortals(); void generatePortals();
std::vector<sf::Vector2f> getPath(const sf::Vector2f& start, std::vector<Vector2f> getPath(const Vector2f& start,
const sf::Vector2f& end, float radius) const; const Vector2f& end, float radius) const;
private: private:
Area* getArea(const sf::Vector2f& point) const; Area* getArea(const Vector2f& point) const;
std::vector<Portal*> astarArea(Area* start, Area* end) const; std::vector<Portal*> astarArea(Area* start, Area* end) const;
void draw(sf::RenderTarget& target, sf::RenderStates states) const; void draw(sf::RenderTarget& target, sf::RenderStates states) const;
@ -44,8 +46,8 @@ private:
* Redundant data as portals are saved twice. * Redundant data as portals are saved twice.
*/ */
struct Pathfinder::Portal { struct Pathfinder::Portal {
sf::Vector2f start; Vector2f start;
sf::Vector2f end; Vector2f end;
Area* area; Area* area;
}; };
@ -54,7 +56,7 @@ struct Pathfinder::Portal {
*/ */
struct Pathfinder::Area { struct Pathfinder::Area {
sf::FloatRect area; sf::FloatRect area;
sf::Vector2f center; Vector2f center;
std::vector<Portal> portals; std::vector<Portal> portals;
}; };

View file

@ -52,7 +52,7 @@ World::remove(std::shared_ptr<Sprite> drawable) {
* Returns all characters that are within maxDistance from position. * Returns all characters that are within maxDistance from position.
*/ */
std::vector<std::shared_ptr<Character> > std::vector<std::shared_ptr<Character> >
World::getCharacters(const sf::Vector2f& position, float maxDistance) const { World::getCharacters(const Vector2f& position, float maxDistance) const {
std::vector<std::shared_ptr<Character> > visible; std::vector<std::shared_ptr<Character> > visible;
for (const auto& it : mCharacters) { for (const auto& it : mCharacters) {
if (position == it->getPosition()) if (position == it->getPosition())
@ -81,7 +81,7 @@ World::step(int elapsed) {
it--; it--;
} }
// Don't run collision tests if sprite is not moving. // Don't run collision tests if sprite is not moving.
else if ((*it)->getSpeed() != sf::Vector2f()) else if ((*it)->getSpeed() != Vector2f())
applyMovement(*it, elapsed); applyMovement(*it, elapsed);
} }
} }
@ -93,7 +93,7 @@ World::step(int elapsed) {
*/ */
void void
World::applyMovement(std::shared_ptr<Sprite> sprite, int elapsed) { World::applyMovement(std::shared_ptr<Sprite> sprite, int elapsed) {
sf::Vector2f offset = sprite->getSpeed() * (elapsed / 1000.0f); Vector2f offset = sprite->getSpeed() * (elapsed / 1000.0f);
for (auto w = mDrawables.begin(); w != mDrawables.end(); w++) { for (auto w = mDrawables.begin(); w != mDrawables.end(); w++) {
for (const auto& other : w->second) { for (const auto& other : w->second) {
if (sprite == other) if (sprite == other)
@ -158,19 +158,19 @@ World::draw(sf::RenderTarget& target, sf::RenderStates states) const {
* @return True if the ray was not blocked. * @return True if the ray was not blocked.
*/ */
bool bool
World::raycast(const sf::Vector2f& lineStart, World::raycast(const Vector2f& lineStart,
const sf::Vector2f& lineEnd) const { const Vector2f& lineEnd) const {
assert(lineStart != lineEnd); assert(lineStart != lineEnd);
sf::Vector2f lineCenter = lineStart + 0.5f * (lineEnd - lineStart); Vector2f lineCenter = lineStart + 0.5f * (lineEnd - lineStart);
for (const auto& it : mDrawables.at(Sprite::Category::CATEGORY_WORLD)) { for (const auto& it : mDrawables.at(Sprite::Category::CATEGORY_WORLD)) {
if (!it->collisionEnabled(Sprite::CATEGORY_ACTOR)) if (!it->collisionEnabled(Sprite::CATEGORY_ACTOR))
continue; continue;
sf::Vector2f axis = it->getPosition() - lineCenter; Vector2f axis = it->getPosition() - lineCenter;
if (axis == sf::Vector2f()) if (axis == Vector2f())
return false; return false;
axis = thor::unitVector(axis); axis = thor::unitVector(axis);
sf::Vector2f halfsize = it->getSize() / 2.0f; Vector2f halfsize = it->getSize() / 2.0f;
float rectPosProjected = thor::dotProduct(axis, it->getPosition()); float rectPosProjected = thor::dotProduct(axis, it->getPosition());
float lineStartProjected = thor::dotProduct(axis, lineStart); float lineStartProjected = thor::dotProduct(axis, lineStart);
float lineEndProjected = thor::dotProduct(axis, lineEnd); float lineEndProjected = thor::dotProduct(axis, lineEnd);
@ -180,7 +180,7 @@ World::raycast(const sf::Vector2f& lineStart,
float rectHalfWidthProjected = std::max( float rectHalfWidthProjected = std::max(
abs(thor::dotProduct(axis, halfsize)), abs(thor::dotProduct(axis, halfsize)),
abs(thor::dotProduct(axis, abs(thor::dotProduct(axis,
sf::Vector2f(halfsize.x, -halfsize.y)))); Vector2f(halfsize.x, -halfsize.y))));
Interval line = Interval::IntervalFromPoints(lineStartProjected, Interval line = Interval::IntervalFromPoints(lineStartProjected,
lineEndProjected); lineEndProjected);
Interval rect = Interval::IntervalFromRadius(rectPosProjected, Interval rect = Interval::IntervalFromRadius(rectPosProjected,
@ -196,7 +196,7 @@ World::raycast(const sf::Vector2f& lineStart,
* Returns the item closest to position after linear search. * Returns the item closest to position after linear search.
*/ */
std::shared_ptr<Item> std::shared_ptr<Item>
World::getNearestItem(const sf::Vector2f& position) const { World::getNearestItem(const Vector2f& position) const {
std::shared_ptr<Item> closest; std::shared_ptr<Item> closest;
float distance = std::numeric_limits<float>::max(); float distance = std::numeric_limits<float>::max();
for (const auto& v : mDrawables) { for (const auto& v : mDrawables) {

View file

@ -28,10 +28,10 @@ public:
void step(int elapsed); void step(int elapsed);
void think(int elapsed); void think(int elapsed);
std::vector<std::shared_ptr<Character> > std::vector<std::shared_ptr<Character> >
getCharacters(const sf::Vector2f& position, float maxDistance) const; getCharacters(const Vector2f& position, float maxDistance) const;
bool raycast(const sf::Vector2f& lineStart, bool raycast(const Vector2f& lineStart,
const sf::Vector2f& lineEnd) const; const Vector2f& lineEnd) const;
std::shared_ptr<Item> getNearestItem(const sf::Vector2f& position) const; std::shared_ptr<Item> getNearestItem(const Vector2f& position) const;
private: private:

View file

@ -23,7 +23,7 @@ const float Character::ITEM_PICKUP_MAX_DISTANCE_SQUARED = 2500.0f;
/** /**
* Saves pointer to this instance in static var for think(). * Saves pointer to this instance in static var for think().
*/ */
Character::Character(const sf::Vector2f& position, Category category, Character::Character(const Vector2f& position, Category category,
unsigned short mask, const Yaml& config, World& world, unsigned short mask, const Yaml& config, World& world,
Pathfinder& pathfinder) : Pathfinder& pathfinder) :
Circle(position, category, mask, config), Circle(position, category, mask, config),
@ -133,7 +133,7 @@ Character::releaseTrigger() {
* @return True if a path was found. * @return True if a path was found.
*/ */
bool bool
Character::setDestination(const sf::Vector2f& destination) { Character::setDestination(const Vector2f& destination) {
mPath = mPathfinder.getPath(getPosition(), destination, getRadius()); mPath = mPathfinder.getPath(getPosition(), destination, getRadius());
return !mPath.empty(); return !mPath.empty();
} }
@ -152,7 +152,7 @@ Character::move() {
if (thor::length(mPath.back() - getPosition()) < POINT_REACHED_DISTANCE) { if (thor::length(mPath.back() - getPosition()) < POINT_REACHED_DISTANCE) {
mPath.pop_back(); mPath.pop_back();
if (mPath.empty()) if (mPath.empty())
setSpeed(sf::Vector2f(), 0); setSpeed(Vector2f(), 0);
} }
} }
@ -168,7 +168,7 @@ Character::isMoving() const {
* Tests if a target is visible from the current position. * Tests if a target is visible from the current position.
*/ */
bool bool
Character::isVisible(const sf::Vector2f& target) const { Character::isVisible(const Vector2f& target) const {
return mWorld.raycast(getPosition(), target); return mWorld.raycast(getPosition(), target);
} }
@ -284,7 +284,6 @@ Character::pickUpItem() {
mRightGadget = mLeftGadget; mRightGadget = mLeftGadget;
mLeftGadget = gadget; mLeftGadget = gadget;
} }
// TODO: implement (or just make invisible?)
mWorld.remove(item); mWorld.remove(item);
} }
else { else {

View file

@ -31,7 +31,7 @@ public:
static const float VISION_DISTANCE; static const float VISION_DISTANCE;
public: public:
explicit Character(const sf::Vector2f& position, Category category, explicit Character(const Vector2f& position, Category category,
unsigned short mask, const Yaml& config, World& world, unsigned short mask, const Yaml& config, World& world,
Pathfinder& pathfinder); Pathfinder& pathfinder);
virtual ~Character() = 0; virtual ~Character() = 0;
@ -45,9 +45,9 @@ protected:
float getMovementSpeed() const; float getMovementSpeed() const;
void pullTrigger(); void pullTrigger();
void releaseTrigger(); void releaseTrigger();
bool setDestination(const sf::Vector2f& destination); bool setDestination(const Vector2f& destination);
bool isMoving() const; bool isMoving() const;
bool isVisible(const sf::Vector2f& target) const; bool isVisible(const Vector2f& target) const;
std::vector<std::shared_ptr<Character> > getCharacters() const; std::vector<std::shared_ptr<Character> > getCharacters() const;
int getMagazineAmmo() const; int getMagazineAmmo() const;
int getTotalAmmo() const; int getTotalAmmo() const;
@ -90,8 +90,8 @@ private:
std::shared_ptr<Weapon> mActiveWeapon; std::shared_ptr<Weapon> mActiveWeapon;
std::shared_ptr<Gadget> mLeftGadget; std::shared_ptr<Gadget> mLeftGadget;
std::shared_ptr<Gadget> mRightGadget; std::shared_ptr<Gadget> mRightGadget;
std::vector<sf::Vector2f> mPath; //< Contains nodes to reach a set destination. std::vector<Vector2f> mPath; //< Contains nodes to reach a set destination.
sf::Vector2f mLastPosition; Vector2f mLastPosition;
Faction mFaction; Faction mFaction;
}; };

View file

@ -10,10 +10,10 @@
#include "Rectangle.h" #include "Rectangle.h"
#include "../util/Yaml.h" #include "../util/Yaml.h"
Circle::Circle(const sf::Vector2f& position, Category category, Circle::Circle(const Vector2f& position, Category category,
unsigned short mask, const Yaml& config, unsigned short mask, const Yaml& config,
const sf::Vector2f& direction) : const Vector2f& direction) :
Sprite(position, category, mask, config.get("size", sf::Vector2f()), Sprite(position, category, mask, config.get("size", Vector2f()),
config.get("texture", std::string()), direction) { config.get("texture", std::string()), direction) {
} }
@ -23,7 +23,7 @@ Circle::Circle(const sf::Vector2f& position, Category category,
*/ */
bool bool
Circle::testCollision(std::shared_ptr<Sprite> other, Circle::testCollision(std::shared_ptr<Sprite> other,
sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond) { Vector2f& offsetFirst, const Vector2f& offsetSecond) {
Rectangle* rect = dynamic_cast<Rectangle*>(other.get()); Rectangle* rect = dynamic_cast<Rectangle*>(other.get());
Circle* circle = dynamic_cast<Circle*>(other.get()); Circle* circle = dynamic_cast<Circle*>(other.get());
if (circle != nullptr) if (circle != nullptr)

View file

@ -18,13 +18,13 @@ class Yaml;
*/ */
class Circle : public CollisionModel, public Sprite { class Circle : public CollisionModel, public Sprite {
public: public:
explicit Circle(const sf::Vector2f& position, Category category, explicit Circle(const Vector2f& position, Category category,
unsigned short mask, const Yaml& config, unsigned short mask, const Yaml& config,
const sf::Vector2f& direction = sf::Vector2f(0, 0)); const Vector2f& direction = Vector2f(0, 0));
virtual ~Circle() = default; virtual ~Circle() = default;
bool testCollision(std::shared_ptr<Sprite> other, bool testCollision(std::shared_ptr<Sprite> other,
sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond); Vector2f& offsetFirst, const Vector2f& offsetSecond);
float getRadius() const; float getRadius() const;
}; };

View file

@ -29,10 +29,10 @@ CollisionModel::~CollisionModel() {
*/ */
bool bool
CollisionModel::testCollision(const Circle& circle, const Rectangle& rect, CollisionModel::testCollision(const Circle& circle, const Rectangle& rect,
sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond) { Vector2f& offsetFirst, const Vector2f& offsetSecond) {
sf::Vector2f halfSize = rect.getSize() / 2.0f; Vector2f halfSize = rect.getSize() / 2.0f;
sf::Vector2f rectNewPos = rect.getPosition() + offsetSecond; Vector2f rectNewPos = rect.getPosition() + offsetSecond;
sf::Vector2f circleRotatedPos = circle.getPosition() + offsetFirst - rectNewPos; Vector2f circleRotatedPos = circle.getPosition() + offsetFirst - rectNewPos;
circleRotatedPos = thor::rotatedVector(circleRotatedPos, -rect.mShape.getRotation()); circleRotatedPos = thor::rotatedVector(circleRotatedPos, -rect.mShape.getRotation());
circleRotatedPos += rectNewPos; circleRotatedPos += rectNewPos;
@ -44,7 +44,7 @@ CollisionModel::testCollision(const Circle& circle, const Rectangle& rect,
.getOverlap(Interval::IntervalFromRadius(rectNewPos.y, halfSize.y)) .getOverlap(Interval::IntervalFromRadius(rectNewPos.y, halfSize.y))
.getLength(); .getLength();
offsetFirst += ((circleRotatedPos.y > rectNewPos.y) ? 1.0f : - 1.0f) * offsetFirst += ((circleRotatedPos.y > rectNewPos.y) ? 1.0f : - 1.0f) *
thor::rotatedVector(sf::Vector2f(0, overlapY), rect.mShape.getRotation()); thor::rotatedVector(Vector2f(0, overlapY), rect.mShape.getRotation());
return overlapY > 0; return overlapY > 0;
} }
// Same here (just switched x/y). // Same here (just switched x/y).
@ -55,14 +55,14 @@ CollisionModel::testCollision(const Circle& circle, const Rectangle& rect,
.getOverlap(Interval::IntervalFromRadius(rectNewPos.x, halfSize.x)) .getOverlap(Interval::IntervalFromRadius(rectNewPos.x, halfSize.x))
.getLength(); .getLength();
offsetFirst += ((circleRotatedPos.x > rectNewPos.x) ? 1.0f : - 1.0f) * offsetFirst += ((circleRotatedPos.x > rectNewPos.x) ? 1.0f : - 1.0f) *
thor::rotatedVector(sf::Vector2f(overlapX, 0), rect.mShape.getRotation()); thor::rotatedVector(Vector2f(overlapX, 0), rect.mShape.getRotation());
return overlapX > 0; return overlapX > 0;
} }
// Test if the circle is colliding with a corner of the rectangle, using // Test if the circle is colliding with a corner of the rectangle, using
// the same method as circle-circle collision (distance to corner instead // the same method as circle-circle collision (distance to corner instead
// of radius. // of radius.
else { else {
sf::Vector2f axis(thor::unitVector(rectNewPos - circleRotatedPos)); Vector2f axis(thor::unitVector(rectNewPos - circleRotatedPos));
// Use correct vector for corner projections (positive/negative // Use correct vector for corner projections (positive/negative
// direction does not matter). // direction does not matter).
@ -72,7 +72,7 @@ CollisionModel::testCollision(const Circle& circle, const Rectangle& rect,
rectHalfSizeProjected = thor::dotProduct(axis, halfSize); rectHalfSizeProjected = thor::dotProduct(axis, halfSize);
else else
rectHalfSizeProjected = thor::dotProduct(axis, rectHalfSizeProjected = thor::dotProduct(axis,
sf::Vector2f(halfSize.x, -halfSize.y)); Vector2f(halfSize.x, -halfSize.y));
Interval projectedCircle = Interval::IntervalFromRadius( Interval projectedCircle = Interval::IntervalFromRadius(
thor::dotProduct(axis, circleRotatedPos), thor::dotProduct(axis, circleRotatedPos),
@ -100,8 +100,8 @@ CollisionModel::testCollision(const Circle& circle, const Rectangle& rect,
*/ */
bool bool
CollisionModel::testCollision(const Circle& first, const Circle& second, CollisionModel::testCollision(const Circle& first, const Circle& second,
sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond) { Vector2f& offsetFirst, const Vector2f& offsetSecond) {
sf::Vector2f axis(thor::unitVector(second.getPosition() + offsetFirst - Vector2f axis(thor::unitVector(second.getPosition() + offsetFirst -
(first.getPosition() + offsetSecond))); (first.getPosition() + offsetSecond)));
Interval projectedFirst = Interval::IntervalFromRadius( Interval projectedFirst = Interval::IntervalFromRadius(
thor::dotProduct(axis, first.getPosition() + offsetFirst), thor::dotProduct(axis, first.getPosition() + offsetFirst),
@ -125,6 +125,6 @@ CollisionModel::testCollision(const Circle& first, const Circle& second,
*/ */
bool bool
CollisionModel::testCollision(const Rectangle& first, const Rectangle& second, CollisionModel::testCollision(const Rectangle& first, const Rectangle& second,
sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond) { Vector2f& offsetFirst, const Vector2f& offsetSecond) {
return false; return false;
} }

View file

@ -11,7 +11,7 @@
class Circle; class Circle;
class Rectangle; class Rectangle;
#include <SFML/System.hpp> #include "../util/Vector.h"
/** /**
* Abstract class providing helper functions to test for collisions between shapes. * Abstract class providing helper functions to test for collisions between shapes.
@ -24,11 +24,11 @@ public:
protected: protected:
static bool testCollision(const Circle& circle, const Rectangle& rect, static bool testCollision(const Circle& circle, const Rectangle& rect,
sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond); Vector2f& offsetFirst, const Vector2f& offsetSecond);
static bool testCollision(const Circle& first, const Circle& second, static bool testCollision(const Circle& first, const Circle& second,
sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond); Vector2f& offsetFirst, const Vector2f& offsetSecond);
static bool testCollision(const Rectangle& first, const Rectangle& second, static bool testCollision(const Rectangle& first, const Rectangle& second,
sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond); Vector2f& offsetFirst, const Vector2f& offsetSecond);
}; };
#endif /* DG_COLLISIONMODEL_H_ */ #endif /* DG_COLLISIONMODEL_H_ */

View file

@ -10,10 +10,10 @@
#include "Circle.h" #include "Circle.h"
#include "../util/Yaml.h" #include "../util/Yaml.h"
Rectangle::Rectangle(const sf::Vector2f& position, Category category, Rectangle::Rectangle(const Vector2f& position, Category category,
unsigned short mask, const Yaml& config, unsigned short mask, const Yaml& config,
const sf::Vector2f& direction) : const Vector2f& direction) :
Sprite(position, category, mask, config.get("size", sf::Vector2f()), Sprite(position, category, mask, config.get("size", Vector2f()),
config.get("texture", std::string()), direction) { config.get("texture", std::string()), direction) {
} }
@ -23,7 +23,7 @@ Rectangle::Rectangle(const sf::Vector2f& position, Category category,
*/ */
bool bool
Rectangle::testCollision(std::shared_ptr<Sprite> other, Rectangle::testCollision(std::shared_ptr<Sprite> other,
sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond) { Vector2f& offsetFirst, const Vector2f& offsetSecond) {
Rectangle* rect = dynamic_cast<Rectangle*>(other.get()); Rectangle* rect = dynamic_cast<Rectangle*>(other.get());
Circle* circle = dynamic_cast<Circle*>(other.get()); Circle* circle = dynamic_cast<Circle*>(other.get());
if (circle != nullptr) if (circle != nullptr)

View file

@ -18,13 +18,13 @@ class Yaml;
*/ */
class Rectangle : public CollisionModel, public Sprite { class Rectangle : public CollisionModel, public Sprite {
public: public:
explicit Rectangle(const sf::Vector2f& position, Category category, explicit Rectangle(const Vector2f& position, Category category,
unsigned short mask, const Yaml& config, unsigned short mask, const Yaml& config,
const sf::Vector2f& direction = sf::Vector2f(0, 0)); const Vector2f& direction = sf::Vector2f(0, 0));
virtual ~Rectangle() = default; virtual ~Rectangle() = default;
bool testCollision(std::shared_ptr<Sprite> other, bool testCollision(std::shared_ptr<Sprite> other,
sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond); Vector2f& offsetFirst, const Vector2f& offsetSecond);
}; };
#endif /* DG_RECTANGLE_H_ */ #endif /* DG_RECTANGLE_H_ */

View file

@ -13,14 +13,14 @@
#include "../util/Log.h" #include "../util/Log.h"
#include "../util/ResourceManager.h" #include "../util/ResourceManager.h"
Sprite::Sprite(const sf::Vector2f& position, Category category, Sprite::Sprite(const Vector2f& position, Category category,
unsigned short mask, const sf::Vector2f& size, unsigned short mask, const Vector2f& size,
const std::string& texture, const sf::Vector2f& direction) : const std::string& texture, const Vector2f& direction) :
mCategory(category), mCategory(category),
mMask(mask) { mMask(mask) {
mShape.setSize(size); mShape.setSize(size);
mShape.setOrigin(size / 2.0f); mShape.setOrigin(size / 2.0f);
mShape.setTextureRect(sf::IntRect(sf::Vector2i(), sf::Vector2i(size))); mShape.setTextureRect(sf::IntRect(Vector2i(), Vector2i(size)));
setPosition(position); setPosition(position);
setDirection(direction); setDirection(direction);
try { try {
@ -37,7 +37,7 @@ Sprite::Sprite(const sf::Vector2f& position, Category category,
/** /**
* Returns the position of the sprite (center). * Returns the position of the sprite (center).
*/ */
sf::Vector2f Vector2f
Sprite::getPosition() const { Sprite::getPosition() const {
return mShape.getPosition(); return mShape.getPosition();
} }
@ -45,7 +45,7 @@ Sprite::getPosition() const {
/** /**
* Returns the movement speed of the sprite. * Returns the movement speed of the sprite.
*/ */
sf::Vector2f Vector2f
Sprite::getSpeed() const { Sprite::getSpeed() const {
return mSpeed; return mSpeed;
} }
@ -53,9 +53,9 @@ Sprite::getSpeed() const {
/** /**
* Returns the angle of the sprite. * Returns the angle of the sprite.
*/ */
sf::Vector2f Vector2f
Sprite::getDirection() const { Sprite::getDirection() const {
return thor::rotatedVector(sf::Vector2f(0, - 1), mShape.getRotation()); return thor::rotatedVector(Vector2f(0, - 1), mShape.getRotation());
} }
/** /**
@ -78,10 +78,10 @@ Sprite::getCategory() const {
* Returns the size of the sprite as a vector (bottom left to top right), * Returns the size of the sprite as a vector (bottom left to top right),
* does not consider rotation. * does not consider rotation.
*/ */
sf::Vector2f Vector2f
Sprite::getSize() const { Sprite::getSize() const {
sf::FloatRect bounds = mShape.getLocalBounds(); sf::FloatRect bounds = mShape.getLocalBounds();
return sf::Vector2f(bounds.width, bounds.height); return Vector2f(bounds.width, bounds.height);
} }
void void
@ -127,8 +127,8 @@ Sprite::setDelete(bool value) {
* @param speed Movement speed in pixels per second. * @param speed Movement speed in pixels per second.
*/ */
void void
Sprite::setSpeed(sf::Vector2f direction, float speed) { Sprite::setSpeed(Vector2f direction, float speed) {
if (direction != sf::Vector2f()) if (direction != Vector2f())
thor::setLength(direction, speed); thor::setLength(direction, speed);
mSpeed = direction; mSpeed = direction;
} }
@ -138,8 +138,8 @@ Sprite::setSpeed(sf::Vector2f direction, float speed) {
* but is otherwise meaningless. * but is otherwise meaningless.
*/ */
void void
Sprite::setDirection(const sf::Vector2f& direction) { Sprite::setDirection(const Vector2f& direction) {
if (direction != sf::Vector2f()) if (direction != Vector2f())
mShape.setRotation(thor::polarAngle(direction) + 90); mShape.setRotation(thor::polarAngle(direction) + 90);
} }
@ -147,6 +147,6 @@ Sprite::setDirection(const sf::Vector2f& direction) {
* Sets the position of thr Sprite. * Sets the position of thr Sprite.
*/ */
void void
Sprite::setPosition(const sf::Vector2f& position) { Sprite::setPosition(const Vector2f& position) {
mShape.setPosition(position); mShape.setPosition(position);
} }

View file

@ -12,6 +12,8 @@
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
#include "../util/Vector.h"
/** /**
* An sprite that is rendered in the world. * An sprite that is rendered in the world.
*/ */
@ -38,30 +40,30 @@ public:
// Public functions. // Public functions.
public: public:
explicit Sprite(const sf::Vector2f& position, Category category, explicit Sprite(const Vector2f& position, Category category,
unsigned short mask, const sf::Vector2f& size, unsigned short mask, const Vector2f& size,
const std::string& texture, const sf::Vector2f& direction); const std::string& texture, const Vector2f& direction);
virtual ~Sprite() = default; virtual ~Sprite() = default;
sf::Vector2f getPosition() const; Vector2f getPosition() const;
sf::Vector2f getSpeed() const; Vector2f getSpeed() const;
sf::Vector2f getDirection() const; Vector2f getDirection() const;
bool getDelete() const; bool getDelete() const;
Category getCategory() const; Category getCategory() const;
sf::Vector2f getSize() const; Vector2f getSize() const;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const;
bool collisionEnabled(Category category) const; bool collisionEnabled(Category category) const;
bool isInside(const sf::FloatRect& rect) const; bool isInside(const sf::FloatRect& rect) const;
virtual bool testCollision(std::shared_ptr<Sprite> other, virtual bool testCollision(std::shared_ptr<Sprite> other,
sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond) = 0; Vector2f& offsetFirst, const Vector2f& offsetSecond) = 0;
virtual void onCollide(std::shared_ptr<Sprite> other); virtual void onCollide(std::shared_ptr<Sprite> other);
protected: protected:
void setDelete(bool value); void setDelete(bool value);
void setSpeed(sf::Vector2f direction, float speed); void setSpeed(Vector2f direction, float speed);
void setDirection(const sf::Vector2f& direction); void setDirection(const Vector2f& direction);
void setPosition(const sf::Vector2f& position); void setPosition(const Vector2f& position);
private: private:
friend class CollisionModel; friend class CollisionModel;
@ -69,7 +71,7 @@ private:
sf::RectangleShape mShape; sf::RectangleShape mShape;
std::shared_ptr<sf::Texture> mTexture; std::shared_ptr<sf::Texture> mTexture;
sf::Vector2f mSpeed; Vector2f mSpeed;
Category mCategory; Category mCategory;
unsigned short mMask; unsigned short mMask;
bool mDelete = false; bool mDelete = false;

View file

@ -20,8 +20,8 @@
* @param world Box2d world. * @param world Box2d world.
* @param texture Texture to display for bullet. * @param texture Texture to display for bullet.
*/ */
Bullet::Bullet(const sf::Vector2f& position, Character& shooter, Bullet::Bullet(const Vector2f& position, Character& shooter,
sf::Vector2f direction, const Yaml& config, float speed, Vector2f direction, const Yaml& config, float speed,
float damage, float maxRange) : float damage, float maxRange) :
Circle(position, CATEGORY_PARTICLE, ~CATEGORY_PARTICLE, Circle(position, CATEGORY_PARTICLE, ~CATEGORY_PARTICLE,
config, thor::rotatedVector(direction, -90.0f)), config, thor::rotatedVector(direction, -90.0f)),

View file

@ -18,8 +18,8 @@ class Yaml;
*/ */
class Bullet : public Circle { class Bullet : public Circle {
public: public:
explicit Bullet(const sf::Vector2f& position, Character& shooter, explicit Bullet(const Vector2f& position, Character& shooter,
sf::Vector2f direction, const Yaml& config, float speed, Vector2f direction, const Yaml& config, float speed,
float damage, float maxRange); float damage, float maxRange);
void onCollide(std::shared_ptr<Sprite> other); void onCollide(std::shared_ptr<Sprite> other);
@ -29,7 +29,7 @@ private:
const int mDamage; const int mDamage;
const float mSpeed; const float mSpeed;
const float mMaxRangeSquared; const float mMaxRangeSquared;
sf::Vector2f mStartPoint; Vector2f mStartPoint;
}; };
#endif /* DG_BULLET_H_ */ #endif /* DG_BULLET_H_ */

View file

@ -37,27 +37,24 @@ Generator::Generator(World& world, Pathfinder& pathfinder) :
* GENERATE_AREA_SIZE and GENERATE_AREA_RANGE). * GENERATE_AREA_SIZE and GENERATE_AREA_RANGE).
*/ */
void void
Generator::generateCurrentAreaIfNeeded(const sf::Vector2f& playerPosition) { Generator::generateCurrentAreaIfNeeded(const Vector2f& playerPosition) {
auto compare = [](const sf::Vector2i& a, const sf::Vector2i& b) { std::map<Vector2i, float> open;
return a.x < b.x || (a.x == b.x && a.y < b.y); std::set<Vector2i> closed;
};
std::map<sf::Vector2i, float, decltype(compare)> open(compare);
std::set<sf::Vector2i, decltype(compare)> closed(compare);
sf::Vector2i start((int) floor(playerPosition.x / Tile::TILE_SIZE.x), Vector2i start((int) floor(playerPosition.x / Tile::TILE_SIZE.x),
(int) floor(playerPosition.y / Tile::TILE_SIZE.y)); (int) floor(playerPosition.y / Tile::TILE_SIZE.y));
start /= GENERATE_AREA_SIZE; start /= GENERATE_AREA_SIZE;
auto makePair = [&start](const sf::Vector2i& point) { auto makePair = [&start](const Vector2i& point) {
return std::make_pair(point, thor::length(sf::Vector2f(point - start))); return std::make_pair(point, thor::length(Vector2f(point - start)));
}; };
open.insert(makePair(start)); open.insert(makePair(start));
while (!open.empty()) { while (!open.empty()) {
auto intComp = [](const std::pair<sf::Vector2i, float>& left, auto intComp = [](const std::pair<Vector2i, float>& left,
const std::pair<sf::Vector2i, float>& right) { const std::pair<Vector2i, float>& right) {
return left.second < right.second; return left.second < right.second;
}; };
sf::Vector2i current = Vector2i current =
std::min_element(open.begin(), open.end(), intComp)->first; std::min_element(open.begin(), open.end(), intComp)->first;
float distance = open[current]; float distance = open[current];
open.erase(current); open.erase(current);
@ -65,8 +62,8 @@ Generator::generateCurrentAreaIfNeeded(const sf::Vector2f& playerPosition) {
if (!mGenerated[current.x][current.y] && distance <= GENERATE_AREA_RANGE) { if (!mGenerated[current.x][current.y] && distance <= GENERATE_AREA_RANGE) {
mGenerated[current.x][current.y] = true; mGenerated[current.x][current.y] = true;
sf::IntRect area(current * GENERATE_AREA_SIZE - sf::IntRect area(current * GENERATE_AREA_SIZE -
sf::Vector2i(GENERATE_AREA_SIZE, GENERATE_AREA_SIZE) / 2, Vector2i(GENERATE_AREA_SIZE, GENERATE_AREA_SIZE) / 2,
sf::Vector2i(GENERATE_AREA_SIZE, GENERATE_AREA_SIZE)); Vector2i(GENERATE_AREA_SIZE, GENERATE_AREA_SIZE));
generateTiles(area); generateTiles(area);
for (const auto& enemyPosition : getEnemySpawns(area)) { for (const auto& enemyPosition : getEnemySpawns(area)) {
float distance = thor::length(enemyPosition - playerPosition); float distance = thor::length(enemyPosition - playerPosition);
@ -76,14 +73,14 @@ Generator::generateCurrentAreaIfNeeded(const sf::Vector2f& playerPosition) {
} }
} }
if (mGenerated[current.x][current.y] && distance <= GENERATE_AREA_RANGE) { if (mGenerated[current.x][current.y] && distance <= GENERATE_AREA_RANGE) {
if (closed.find(sf::Vector2i(current.x + 1, current.y)) == closed.end()) if (closed.find(Vector2i(current.x + 1, current.y)) == closed.end())
open.insert(makePair(sf::Vector2i(current.x + 1, current.y))); open.insert(makePair(Vector2i(current.x + 1, current.y)));
if (closed.find(sf::Vector2i(current.x, current.y + 1)) == closed.end()) if (closed.find(Vector2i(current.x, current.y + 1)) == closed.end())
open.insert(makePair(sf::Vector2i(current.x, current.y + 1))); open.insert(makePair(Vector2i(current.x, current.y + 1)));
if (closed.find(sf::Vector2i(current.x - 1, current.y)) == closed.end()) if (closed.find(Vector2i(current.x - 1, current.y)) == closed.end())
open.insert(makePair(sf::Vector2i(current.x - 1, current.y))); open.insert(makePair(Vector2i(current.x - 1, current.y)));
if (closed.find(sf::Vector2i(current.x, current.y - 1)) == closed.end()) if (closed.find(Vector2i(current.x, current.y - 1)) == closed.end())
open.insert(makePair(sf::Vector2i(current.x, current.y - 1))); open.insert(makePair(Vector2i(current.x, current.y - 1)));
} }
} }
} }
@ -94,16 +91,16 @@ Generator::generateCurrentAreaIfNeeded(const sf::Vector2f& playerPosition) {
* *
* FIXME: Some nodes are selected more than once. * FIXME: Some nodes are selected more than once.
*/ */
std::vector<sf::Vector2i> std::vector<Vector2i>
Generator::createMinimalSpanningTree(const sf::Vector2i& start, Generator::createMinimalSpanningTree(const Vector2i& start,
const float limit) { const float limit) {
std::vector<sf::Vector2i> open; std::vector<Vector2i> open;
std::vector<sf::Vector2i> selected; std::vector<Vector2i> selected;
open.push_back(start); open.push_back(start);
float totalWeight = 0.0f; float totalWeight = 0.0f;
while (totalWeight < limit) { while (totalWeight < limit) {
sf::Vector2i current; Vector2i current;
float minValue = std::numeric_limits<float>::max(); float minValue = std::numeric_limits<float>::max();
for (auto& o : open) { for (auto& o : open) {
if (mTileNoise.getNoise(o.x, o.y) + 1.0f < minValue) { if (mTileNoise.getNoise(o.x, o.y) + 1.0f < minValue) {
@ -115,15 +112,15 @@ Generator::createMinimalSpanningTree(const sf::Vector2i& start,
selected.push_back(current); selected.push_back(current);
totalWeight += minValue; totalWeight += minValue;
auto insertOnlyNew = [&open, &selected](const sf::Vector2i& v) { auto insertOnlyNew = [&open, &selected](const Vector2i& v) {
if (std::find(open.begin(), open.end(), v) == open.end() if (std::find(open.begin(), open.end(), v) == open.end()
&& std::find(selected.begin(), selected.end(), v) == selected.end()) && std::find(selected.begin(), selected.end(), v) == selected.end())
open.push_back(v); open.push_back(v);
}; };
insertOnlyNew(sf::Vector2i(current.x + 1, current.y)); insertOnlyNew(Vector2i(current.x + 1, current.y));
insertOnlyNew(sf::Vector2i(current.x, current.y + 1)); insertOnlyNew(Vector2i(current.x, current.y + 1));
insertOnlyNew(sf::Vector2i(current.x - 1, current.y)); insertOnlyNew(Vector2i(current.x - 1, current.y));
insertOnlyNew(sf::Vector2i(current.x, current.y - 1)); insertOnlyNew(Vector2i(current.x, current.y - 1));
} }
return selected; return selected;
} }
@ -144,19 +141,19 @@ Generator::generateTiles(const sf::IntRect& area) {
assert(area.width && !(area.width & (area.width - 1))); assert(area.width && !(area.width & (area.width - 1)));
assert(area.height && !(area.height & (area.height - 1))); assert(area.height && !(area.height & (area.height - 1)));
sf::Vector2i start; Vector2i start;
float minValue = std::numeric_limits<float>::max(); float minValue = std::numeric_limits<float>::max();
// Find lowest value for tree start. // Find lowest value for tree start.
for (int x = area.left; x < area.left + area.width; x++) for (int x = area.left; x < area.left + area.width; x++)
for (int y = area.top; y < area.top + area.height; y++) { for (int y = area.top; y < area.top + area.height; y++) {
if (mTileNoise.getNoise(x, y) + 1.0f < minValue) { if (mTileNoise.getNoise(x, y) + 1.0f < minValue) {
start = sf::Vector2i(x, y); start = Vector2i(x, y);
minValue = mTileNoise.getNoise(x, y) + 1.0f; minValue = mTileNoise.getNoise(x, y) + 1.0f;
} }
} }
std::vector<sf::Vector2i> selected = createMinimalSpanningTree(start, 12.0f); std::vector<Vector2i> selected = createMinimalSpanningTree(start, 12.0f);
// For rooms, take minimum bounding box of spanning tree. // For rooms, take minimum bounding box of spanning tree.
@ -191,22 +188,22 @@ Generator::generateTiles(const sf::IntRect& area) {
* *
* @param area Area for which enemy spawns should be returned. * @param area Area for which enemy spawns should be returned.
*/ */
std::vector<sf::Vector2f> std::vector<Vector2f>
Generator::getEnemySpawns(const sf::IntRect& area) { Generator::getEnemySpawns(const sf::IntRect& area) {
auto compare = [](const sf::Vector2f& a, const sf::Vector2f& b) { auto compare = [](const Vector2f& a, const Vector2f& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y); return a.x < b.x || (a.x == b.x && a.y < b.y);
}; };
std::set<sf::Vector2f, decltype(compare)> ret(compare); std::set<Vector2f, decltype(compare)> ret(compare);
for (int x = area.left; x < area.left + area.width; x++) { for (int x = area.left; x < area.left + area.width; x++) {
for (int y = area.top; y < area.top + area.height; y++) { for (int y = area.top; y < area.top + area.height; y++) {
if (mCharacterNoise.getNoise(x, y) < -0.85f) { if (mCharacterNoise.getNoise(x, y) < -0.85f) {
sf::Vector2i tilePosition = findClosestFloor(sf::Vector2i(x, y)); Vector2i tilePosition = findClosestFloor(Vector2i(x, y));
ret.insert(sf::Vector2f(tilePosition.x * Tile::TILE_SIZE.x, ret.insert(Vector2f(tilePosition.x * Tile::TILE_SIZE.x,
tilePosition.y * Tile::TILE_SIZE.y)); tilePosition.y * Tile::TILE_SIZE.y));
} }
} }
} }
return std::vector<sf::Vector2f>(ret.begin(), ret.end()); return std::vector<Vector2f>(ret.begin(), ret.end());
} }
/** /**
@ -245,10 +242,10 @@ Generator::generateAreas(const sf::IntRect& area) {
/** /**
* Returns a valid position (floor) for the player to spawn at. * Returns a valid position (floor) for the player to spawn at.
*/ */
sf::Vector2f Vector2f
Generator::getPlayerSpawn() const { Generator::getPlayerSpawn() const {
sf::Vector2i spawn = findClosestFloor(sf::Vector2i(0, 0)); Vector2i spawn = findClosestFloor(Vector2i(0, 0));
return sf::Vector2f(spawn.x * Tile::TILE_SIZE.x, return Vector2f(spawn.x * Tile::TILE_SIZE.x,
spawn.y * Tile::TILE_SIZE.y); spawn.y * Tile::TILE_SIZE.y);
} }
@ -258,25 +255,22 @@ Generator::getPlayerSpawn() const {
* @warn Will fail if no floor tile has been generated yet. * @warn Will fail if no floor tile has been generated yet.
* @position Point to start search for a floor tile from. * @position Point to start search for a floor tile from.
*/ */
sf::Vector2i Vector2i
Generator::findClosestFloor(const sf::Vector2i& position) const { Generator::findClosestFloor(const Vector2i& position) const {
auto compare = [](const sf::Vector2i& a, const sf::Vector2i& b) { std::map<Vector2i, float> open;
return a.x < b.x || (a.x == b.x && a.y < b.y); std::set<Vector2i> closed;
}; Vector2i start = position;
std::map<sf::Vector2i, float, decltype(compare)> open(compare); auto makePair = [&start](const Vector2i& point) {
std::set<sf::Vector2i, decltype(compare)> closed(compare); return std::make_pair(point, thor::length(Vector2f(point - start)));
sf::Vector2i start = position;
auto makePair = [&start](const sf::Vector2i& point) {
return std::make_pair(point, thor::length(sf::Vector2f(point - start)));
}; };
open.insert(makePair(start)); open.insert(makePair(start));
while (!open.empty()) { while (!open.empty()) {
auto intComp = [](const std::pair<sf::Vector2i, float>& left, auto intComp = [](const std::pair<Vector2i, float>& left,
const std::pair<sf::Vector2i, float>& right) { const std::pair<Vector2i, float>& right) {
return left.second < right.second; return left.second < right.second;
}; };
sf::Vector2i current = std::min_element(open.begin(), open.end(), intComp)->first; Vector2i current = std::min_element(open.begin(), open.end(), intComp)->first;
open.erase(current); open.erase(current);
closed.insert(current); closed.insert(current);
if (mTiles.count(current.x) != 0 && if (mTiles.count(current.x) != 0 &&
@ -285,17 +279,17 @@ Generator::findClosestFloor(const sf::Vector2i& position) const {
return current; return current;
} }
else { else {
if (closed.find(sf::Vector2i(current.x + 1, current.y)) == closed.end()) if (closed.find(Vector2i(current.x + 1, current.y)) == closed.end())
open.insert(makePair(sf::Vector2i(current.x + 1, current.y))); open.insert(makePair(Vector2i(current.x + 1, current.y)));
if (closed.find(sf::Vector2i(current.x, current.y + 1)) == closed.end()) if (closed.find(Vector2i(current.x, current.y + 1)) == closed.end())
open.insert(makePair(sf::Vector2i(current.x, current.y + 1))); open.insert(makePair(Vector2i(current.x, current.y + 1)));
if (closed.find(sf::Vector2i(current.x - 1, current.y)) == closed.end()) if (closed.find(Vector2i(current.x - 1, current.y)) == closed.end())
open.insert(makePair(sf::Vector2i(current.x - 1, current.y))); open.insert(makePair(Vector2i(current.x - 1, current.y)));
if (closed.find(sf::Vector2i(current.x, current.y - 1)) == closed.end()) if (closed.find(Vector2i(current.x, current.y - 1)) == closed.end())
open.insert(makePair(sf::Vector2i(current.x, current.y - 1))); open.insert(makePair(Vector2i(current.x, current.y - 1)));
} }
} }
// No floor tile found in the entire world. // No floor tile found in the entire world.
assert(false); assert(false);
return sf::Vector2i(); return Vector2i();
} }

View file

@ -12,6 +12,7 @@
#include "../sprites/Tile.h" #include "../sprites/Tile.h"
#include "SimplexNoise.h" #include "SimplexNoise.h"
#include "../util/Vector.h"
class World; class World;
class Pathfinder; class Pathfinder;
@ -22,9 +23,9 @@ class Pathfinder;
class Generator { class Generator {
public: public:
explicit Generator(World& world, Pathfinder& pathfinder); explicit Generator(World& world, Pathfinder& pathfinder);
void generateCurrentAreaIfNeeded(const sf::Vector2f& position); void generateCurrentAreaIfNeeded(const Vector2f& position);
sf::Vector2f getPlayerSpawn() const; Vector2f getPlayerSpawn() const;
std::vector<sf::Vector2f> getEnemySpawns(const sf::IntRect& area); std::vector<Vector2f> getEnemySpawns(const sf::IntRect& area);
private: private:
typedef std::map<int, std::map<int, Tile::Type> > array; typedef std::map<int, std::map<int, Tile::Type> > array;
@ -32,9 +33,9 @@ private:
private: private:
void generateAreas(const sf::IntRect& area); void generateAreas(const sf::IntRect& area);
void generateTiles(const sf::IntRect& area); void generateTiles(const sf::IntRect& area);
sf::Vector2i findClosestFloor(const sf::Vector2i& position) const; Vector2i findClosestFloor(const Vector2i& position) const;
std::vector<sf::Vector2i> createMinimalSpanningTree( std::vector<Vector2i> createMinimalSpanningTree(
const sf::Vector2i& start, const float limit); const Vector2i& start, const float limit);
private: private:
static const int GENERATE_AREA_SIZE; static const int GENERATE_AREA_SIZE;

View file

@ -9,18 +9,18 @@
#include "../World.h" #include "../World.h"
Item::Item(const sf::Vector2f& size, const std::string& texture) : Item::Item(const Vector2f& size, const std::string& texture) :
Sprite(sf::Vector2f(), CATEGORY_NONSOLID, MASK_NONE, size, texture, Sprite(Vector2f(), CATEGORY_NONSOLID, MASK_NONE, size, texture,
sf::Vector2f()) { Vector2f()) {
} }
void void
Item::drop(const sf::Vector2f& position) { Item::drop(const Vector2f& position) {
setPosition(position); setPosition(position);
} }
bool bool
Item::testCollision(std::shared_ptr<Sprite> other, Item::testCollision(std::shared_ptr<Sprite> other,
sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond) { Vector2f& offsetFirst, const Vector2f& offsetSecond) {
return false; return false;
} }

View file

@ -14,12 +14,12 @@ class World;
class Item : public Sprite { class Item : public Sprite {
public: public:
Item(const sf::Vector2f& size, const std::string& texture); Item(const Vector2f& size, const std::string& texture);
virtual ~Item() {}; virtual ~Item() {};
void drop(const sf::Vector2f& position); void drop(const Vector2f& position);
bool testCollision(std::shared_ptr<Sprite> other, bool testCollision(std::shared_ptr<Sprite> other,
sf::Vector2f& offsetFirst, const sf::Vector2f& offsetSecond); Vector2f& offsetFirst, const Vector2f& offsetSecond);
}; };
#endif /* DG_ITEM_H_ */ #endif /* DG_ITEM_H_ */

View file

@ -20,7 +20,7 @@ Shield::onUse(Character& character) {
mCharacter = &character; mCharacter = &character;
if (mRotatingShield) if (mRotatingShield)
mRotatingShield->setDelete(true); mRotatingShield->setDelete(true);
sf::Vector2f offset = mCharacter->getDirection() * mCharacter->getRadius(); Vector2f offset = mCharacter->getDirection() * mCharacter->getRadius();
mRotatingShield = std::shared_ptr<RotatingShield>( mRotatingShield = std::shared_ptr<RotatingShield>(
new RotatingShield(mCharacter->getPosition() + offset)); new RotatingShield(mCharacter->getPosition() + offset));
mCharacter->mWorld.insert(mRotatingShield); mCharacter->mWorld.insert(mRotatingShield);

View file

@ -14,7 +14,7 @@
#include "../util/Yaml.h" #include "../util/Yaml.h"
Weapon::Weapon(World& world, Character& holder, const Yaml& config) : Weapon::Weapon(World& world, Character& holder, const Yaml& config) :
Item(sf::Vector2f(80, 30), "weapon.png"), Item(Vector2f(80, 30), "weapon.png"),
mWorld(world), mWorld(world),
mHolder(&holder), mHolder(&holder),
mName(config.get("name", std::string())), mName(config.get("name", std::string())),
@ -154,15 +154,15 @@ Weapon::setHolder(Character& holder) {
*/ */
void void
Weapon::insertProjectile(float angle) { Weapon::insertProjectile(float angle) {
sf::Vector2f offset(mHolder->getDirection() * mHolder->getRadius()); Vector2f offset(mHolder->getDirection() * mHolder->getRadius());
float spread = (mHolder->getSpeed() == sf::Vector2f()) float spread = (mHolder->getSpeed() == Vector2f())
? mSpread ? mSpread
: mSpreadMoving; : mSpreadMoving;
std::uniform_real_distribution<float> distribution(- spread, spread); std::uniform_real_distribution<float> distribution(- spread, spread);
angle += distribution(mGenerator) + 90.0f; angle += distribution(mGenerator) + 90.0f;
sf::Vector2f direction(thor::rotatedVector(mHolder->getDirection(), angle)); Vector2f direction(thor::rotatedVector(mHolder->getDirection(), angle));
std::shared_ptr<Sprite> projectile(new Bullet(mHolder->getPosition() + offset, std::shared_ptr<Sprite> projectile(new Bullet(mHolder->getPosition() + offset,
*mHolder, direction, mProjectile, mProjectileSpeed, *mHolder, direction, mProjectile, mProjectileSpeed,

View file

@ -9,7 +9,7 @@
#include "../util/Yaml.h" #include "../util/Yaml.h"
Corpse::Corpse(const sf::Vector2f& position) : Corpse::Corpse(const Vector2f& position) :
Circle(position, CATEGORY_NONSOLID, MASK_NONE, Yaml("corpse.yaml")) { Circle(position, CATEGORY_NONSOLID, MASK_NONE, Yaml("corpse.yaml")) {
} }

View file

@ -12,7 +12,7 @@
class Corpse : public Circle { class Corpse : public Circle {
public: public:
explicit Corpse(const sf::Vector2f& position); explicit Corpse(const Vector2f& position);
}; };
#endif /* DG_CORPSE_H_ */ #endif /* DG_CORPSE_H_ */

View file

@ -12,7 +12,7 @@
#include "../util/Yaml.h" #include "../util/Yaml.h"
Enemy::Enemy(World& world, Pathfinder& pathfinder, Enemy::Enemy(World& world, Pathfinder& pathfinder,
const sf::Vector2f& position) : const Vector2f& position) :
Character(position, CATEGORY_ACTOR, MASK_ALL, Yaml("enemy.yaml"), world, Character(position, CATEGORY_ACTOR, MASK_ALL, Yaml("enemy.yaml"), world,
pathfinder) { pathfinder) {
} }
@ -41,6 +41,6 @@ Enemy::onThink(int elapsed) {
} }
else { else {
releaseTrigger(); releaseTrigger();
setSpeed(sf::Vector2f(), 0); setSpeed(Vector2f(), 0);
} }
} }

View file

@ -15,7 +15,7 @@ class World;
class Enemy : public Character { class Enemy : public Character {
public: public:
explicit Enemy(World& world, Pathfinder& pathfinder, explicit Enemy(World& world, Pathfinder& pathfinder,
const sf::Vector2f& position); const Vector2f& position);
private: private:
virtual void onThink(int elapsed) override; virtual void onThink(int elapsed) override;

View file

@ -15,7 +15,7 @@
* Initializes Sprite. * Initializes Sprite.
*/ */
Player::Player(World& world, Pathfinder& pathfinder, Player::Player(World& world, Pathfinder& pathfinder,
const sf::Vector2f& position) : const Vector2f& position) :
Character(position, CATEGORY_ACTOR, MASK_ALL, Yaml("player.yaml"), world, Character(position, CATEGORY_ACTOR, MASK_ALL, Yaml("player.yaml"), world,
pathfinder), pathfinder),
mDirection(0) { mDirection(0) {
@ -27,7 +27,7 @@ Player::Player(World& world, Pathfinder& pathfinder,
* @param Absolute world coordinates of the crosshair. * @param Absolute world coordinates of the crosshair.
*/ */
void void
Player::setCrosshairPosition(const sf::Vector2f& position) { Player::setCrosshairPosition(const Vector2f& position) {
mCrosshairPosition = position - getPosition(); mCrosshairPosition = position - getPosition();
} }
@ -46,7 +46,7 @@ Player::setDirection(Direction direction, bool unset) {
mDirection = mDirection | direction; mDirection = mDirection | direction;
// Convert directions into a vector. // Convert directions into a vector.
sf::Vector2f dirVec(0, 0); Vector2f dirVec(0, 0);
if (mDirection & Direction::RIGHT) if (mDirection & Direction::RIGHT)
dirVec.x += 1.0f; dirVec.x += 1.0f;
if (mDirection & Direction::LEFT) if (mDirection & Direction::LEFT)
@ -59,7 +59,7 @@ Player::setDirection(Direction direction, bool unset) {
} }
void void
Player::setDestination(const sf::Vector2f& destination) { Player::setDestination(const Vector2f& destination) {
mDirection = 0; mDirection = 0;
Character::setDestination(destination); Character::setDestination(destination);
} }

View file

@ -29,13 +29,13 @@ public:
public: public:
explicit Player(World& world, Pathfinder& pathfinder, explicit Player(World& world, Pathfinder& pathfinder,
const sf::Vector2f& position); const Vector2f& position);
void setCrosshairPosition(const sf::Vector2f& position); void setCrosshairPosition(const Vector2f& position);
using Character::pullTrigger; using Character::pullTrigger;
using Character::releaseTrigger; using Character::releaseTrigger;
void setDirection(Direction direction, bool unset); void setDirection(Direction direction, bool unset);
void setDestination(const sf::Vector2f& destination); void setDestination(const Vector2f& destination);
using Character::getMagazineAmmo; using Character::getMagazineAmmo;
using Character::getTotalAmmo; using Character::getTotalAmmo;
using Character::getWeaponName; using Character::getWeaponName;
@ -56,7 +56,7 @@ private:
void onThink(int elapsed) override; void onThink(int elapsed) override;
private: private:
sf::Vector2f mCrosshairPosition; //< Relative position of the point to fire at (mouse cursor). Vector2f mCrosshairPosition; //< Relative position of the point to fire at (mouse cursor).
unsigned char mDirection; //< Current movement direction for direct control. unsigned char mDirection; //< Current movement direction for direct control.
}; };

View file

@ -9,7 +9,7 @@
#include "../util/Yaml.h" #include "../util/Yaml.h"
RotatingShield::RotatingShield(const sf::Vector2f& position) : RotatingShield::RotatingShield(const Vector2f& position) :
Rectangle(position, CATEGORY_WORLD, MASK_ALL, Rectangle(position, CATEGORY_WORLD, MASK_ALL,
Yaml("rotating_shield.yaml")) { Yaml("rotating_shield.yaml")) {
} }

View file

@ -12,7 +12,7 @@
class RotatingShield : public Rectangle { class RotatingShield : public Rectangle {
public: public:
explicit RotatingShield(const sf::Vector2f& position); explicit RotatingShield(const Vector2f& position);
private: private:
using Sprite::setDelete; using Sprite::setDelete;

View file

@ -14,7 +14,7 @@
#include "../util/Yaml.h" #include "../util/Yaml.h"
#include "../World.h" #include "../World.h"
const sf::Vector2i Tile::TILE_SIZE = sf::Vector2i(75, 75); const Vector2i Tile::TILE_SIZE = sf::Vector2i(75, 75);
/** /**
* Constructs a tile. * Constructs a tile.
@ -24,7 +24,7 @@ const sf::Vector2i Tile::TILE_SIZE = sf::Vector2i(75, 75);
* @param world Box2D world object. * @param world Box2D world object.
*/ */
Tile::Tile(Type type, int x, int y) : Tile::Tile(Type type, int x, int y) :
Rectangle(sf::Vector2f(x * TILE_SIZE.x, y * TILE_SIZE.y), Rectangle(Vector2f(x * TILE_SIZE.x, y * TILE_SIZE.y),
CATEGORY_WORLD, (isSolid(type)) ? 0xffff : 0, CATEGORY_WORLD, (isSolid(type)) ? 0xffff : 0,
Yaml(getConfig(type))), Yaml(getConfig(type))),
mType(type) { mType(type) {

View file

@ -26,7 +26,7 @@ public:
Type getType() const; Type getType() const;
public: public:
static const sf::Vector2i TILE_SIZE; //< Tile size in pixels. static const Vector2i TILE_SIZE; //< Tile size in pixels.
public: public:
static std::string getConfig(Type type); static std::string getConfig(Type type);

44
source/util/Vector.h Normal file
View file

@ -0,0 +1,44 @@
/*
* Vector.h
*
* Created on: 07.08.2013
* Author: Felix
*/
#ifndef DG_VECTOR_H_
#define DG_VECTOR_H_
#include <SFML/System.hpp>
/**
* Vector class with comparison operator. All other operators are inherited
* from sf::Vector2.
*/
template <typename T>
class Vector2 : public sf::Vector2<T> {
public:
Vector2() : sf::Vector2<T>() {};
Vector2(T x, T y) : sf::Vector2<T>(x, y) {};
/**
* Implicitly catches and converts any vectors that have been converted to
* sf::Vector2 (as done by arithmethic operations defined by sf::Vector2).
*/
template <typename U>
Vector2(const sf::Vector2<U>& vector) : sf::Vector2<T>(vector) {};
};
/**
* Comparison operator meant for containers like std::set.
*
* Provides a clearly defined, but otherwise meaningless sorting of vectors.
*/
template <typename T>
bool operator <(const Vector2<T>& left, const Vector2<T>& right) {
return left.x < right.x || (left.x == right.x && left.y < right.y);
}
typedef Vector2<int> Vector2i;
typedef Vector2<float> Vector2f;
#endif /* DG_VECTOR_H_ */

View file

@ -13,9 +13,8 @@
#include <yaml-cpp/yaml.h> #include <yaml-cpp/yaml.h>
#include <SFML/System.hpp>
#include "Log.h" #include "Log.h"
#include "Vector.h"
/** /**
* Interface to a YAML file. * Interface to a YAML file.
@ -45,12 +44,12 @@ private:
* Error handling is done in Yaml::get. * Error handling is done in Yaml::get.
*/ */
namespace { namespace {
void operator>>(const YAML::Node& node, sf::Vector2i& vector) { void operator>>(const YAML::Node& node, Vector2i& vector) {
node[0] >> vector.x; node[0] >> vector.x;
node[1] >> vector.y; node[1] >> vector.y;
} }
void operator>>(const YAML::Node& node, sf::Vector2f& vector) { void operator>>(const YAML::Node& node, Vector2f& vector) {
node[0] >> vector.x; node[0] >> vector.x;
node[1] >> vector.y; node[1] >> vector.y;
} }