#include "World.h"
+#include "Collision.h"
+#include "Entity.h"
#include "../graphics/const.h"
+#include <algorithm>
+#include <cmath>
+
+using namespace std;
+
namespace orbi {
, count(size.x * size.y)
, gravity(0, 5)
, terminal(50, 50)
+, fixSpeed(5)
, tiles(count) {
}
-void World::Update(int delta) {
- for (int i = 0; i < delta; ++i) {
- const float dt = 1e-3;
- for (Entity &e : entities) {
- e.Update(dt, gravity, terminal);
+void World::Update(float dt) {
+ for (Entity *e : entities) {
+ if (e->onGround) {
+ e->Update(dt, Vector<float>(), terminal);
+ e->onGround = false;
+ } else {
+ e->Update(dt, gravity, terminal);
+ }
- const AABB &b = e.Bounds();
+ BoundsCollision(*e, dt);
+ TileCollision(*e, dt);
- // world bounds collision
- if (b.Top() < 0) e.Move(Vector<float>(0, -b.Top()));
- if (b.Right() > size.x) e.Move(Vector<float>(size.x - b.Right(), 0));
- if (b.Bottom() > size.y) e.Move(Vector<float>(0, size.y - b.Bottom()));
- if (b.Left() < 0) e.Move(Vector<float>(-b.Left(), 0));
+ }
+}
- const Vector<int> cBegin(b.Left(), b.Top());
- const Vector<int> cEnd(b.Right(), b.Bottom());
+void World::BoundsCollision(Entity &e, float dt) {
+ if (e.vbox.Top() < 0) {
+ e.Move(Vector<float>(0, -e.vbox.Top()));
+ if (e.vel.y < 0) {
+ e.vel.y = 0;
+ }
+ }
+ if (e.vbox.Bottom() > size.y) {
+ e.Move(Vector<float>(0, size.y - e.vbox.Bottom()));
+ if (e.vel.y > 0) {
+ e.vel.y = 0;
+ }
+ e.onGround = true;
+ }
+ if (e.hbox.Right() > size.x) {
+ e.Move(Vector<float>(size.x - e.hbox.Right(), 0));
+ if (e.vel.x > 0) {
+ e.vel.x = 0;
+ }
+ }
+ if (e.hbox.Left() < 0) {
+ e.Move(Vector<float>(-e.hbox.Left(), 0));
+ if (e.vel.x < 0) {
+ e.vel.x = 0;
+ }
+ }
+}
- Vector<float> topResponse;
- for (Vector<int> pos(cBegin); pos.x < cEnd.x; ++pos.x) {
- if (TileAt(pos).IsSolid()) {
- topResponse = Vector<float>(0, pos.y + 1 - b.Top());
- break;
- }
+void World::TileCollision(Entity &e, float dt) {
+ Vector<float> response;
+
+ // top
+ for (int x = e.vbox.Left(), y = e.vbox.Top(),
+ end = std::ceil(e.vbox.Right()); x < end; ++x) {
+ const Tile &tile = TileAt(Vector<int>(x, y));
+ if (tile.IsSolid()) {
+ response.y = y + 1 - e.vbox.Top();
+ if (e.vel.y < 0) {
+ e.vel.y = 0;
}
- Vector<float> bottomResponse;
- for (Vector<int> pos(cBegin.x, cEnd.y); pos.x < cEnd.x; ++pos.x) {
- if (TileAt(pos).IsSolid()) {
- bottomResponse = Vector<float>(0, pos.y - b.Bottom());
- break;
- }
+ break;
+ }
+ }
+
+ // bottom
+ for (int x = e.vbox.Left(), y = e.vbox.Bottom(),
+ end = std::ceil(e.vbox.Right()); x < end; ++x) {
+ const Tile &tile = TileAt(Vector<int>(x, y));
+ if (tile.IsSolid()) {
+ response.y = y - e.vbox.Bottom();
+ e.onGround = true;
+ if (e.vel.y > 0) {
+ e.vel.y = 0;
}
- if (!IsZero(topResponse)) {
- if (IsZero(bottomResponse)) {
- e.Move(topResponse);
+ break;
+ }
+ }
+ if (response.y <= 0) {
+ // bottom, second row
+ // due to the hbox's huge bottom gap
+ for (int x = e.vbox.Left(), y = e.vbox.Bottom() - 1,
+ end = std::ceil(e.vbox.Right()); x < end; ++x) {
+ const Tile &tile = TileAt(Vector<int>(x, y));
+ if (tile.IsSolid()) {
+ response.y = -1;
+ e.onGround = true;
+ if (e.vel.y > 0) {
+ e.vel.y = 0;
}
- } else if (!IsZero(bottomResponse)) {
- e.Move(bottomResponse);
+ break;
}
+ }
+ }
- Vector<float> leftResponse;
- for (Vector<int> pos(cBegin); pos.y < cEnd.y; ++pos.y) {
- if (TileAt(pos).IsSolid()) {
- leftResponse = Vector<float>(pos.x + 1 - b.Left(), 0);
- break;
- }
- }
- Vector<float> rightResponse;
- for (Vector<int> pos(cEnd.x, cBegin.y); pos.y < cEnd.y; ++pos.y) {
- if (TileAt(pos).IsSolid()) {
- rightResponse = Vector<float>(pos.x - b.Right(), 0);
- break;
- }
+ // left
+ for (int y = e.hbox.Top(), x = e.hbox.Left(),
+ end = std::ceil(e.hbox.Bottom()); y < end; ++y) {
+ const Tile &tile = TileAt(Vector<int>(x, y));
+ if (tile.IsSolid()) {
+ response.x = x + 1 - e.hbox.Left();
+ if (e.vel.x < 0) {
+ e.vel.x = 0;
}
- if (!IsZero(leftResponse)) {
- if (IsZero(rightResponse)) {
- e.Move(leftResponse);
- }
- } else if (!IsZero(rightResponse)) {
- e.Move(rightResponse);
+ break;
+ }
+ }
+
+ // right
+ for (int y = e.hbox.Top(), x = e.hbox.Right(),
+ end = std::ceil(e.hbox.Bottom()); y < end; ++y) {
+ const Tile &tile = TileAt(Vector<int>(x, y));
+ if (tile.IsSolid()) {
+ response.x = x - e.hbox.Right();
+ if (e.vel.x > 0) {
+ e.vel.x = 0;
}
+ break;
}
}
+
+ if (response.x > fixSpeed * dt) {
+ response.x = fixSpeed * dt;
+ } else if (response.x < -fixSpeed * dt) {
+ response.x = -fixSpeed * dt;
+ }
+ if (response.y > fixSpeed * dt) {
+ response.y = fixSpeed * dt;
+ } else if (response.y < -fixSpeed * dt) {
+ response.y = -fixSpeed * dt;
+ }
+ e.Move(response);
}
+void World::EntityCollision() {
+ if (entities.size() <= 1) return;
-Entity &World::AddEntity(const Entity &e) {
- entities.emplace_back(e);
- return entities.back();
+ auto firstEnd(entities.end());
+ --firstEnd;
+ for (auto first(entities.begin()); first != firstEnd; ++first) {
+ auto second(first);
+ ++second;
+ for (auto secondEnd(entities.end()); second != secondEnd; ++second) {
+ if ((*first)->bounds.Intersects((*second)->bounds)) {
+ // damage + knockback
+ }
+ }
+ }
}
+
}