diff --git a/.cproject b/.cproject index e214799..a2cfea1 100644 --- a/.cproject +++ b/.cproject @@ -29,7 +29,6 @@ - @@ -39,7 +38,6 @@ - @@ -105,7 +103,6 @@ - @@ -115,7 +112,6 @@ - diff --git a/assets/plants/Plantable.xml b/assets/plants/Plantable.xml index 4113cee..08cefcc 100644 --- a/assets/plants/Plantable.xml +++ b/assets/plants/Plantable.xml @@ -3,21 +3,22 @@ - + - + - + - + - + - + diff --git a/assets/plants/Seeds.xml b/assets/plants/Seeds.xml index 23effae..faab391 100644 --- a/assets/plants/Seeds.xml +++ b/assets/plants/Seeds.xml @@ -3,9 +3,10 @@ - + - + diff --git a/assets/sprites/Player.sprite b/assets/sprites/Player.sprite index 0d0ee29..f45cac2 100644 --- a/assets/sprites/Player.sprite +++ b/assets/sprites/Player.sprite @@ -1,41 +1,41 @@ - - + - + - + - + - - + + - - + + - - + + - - + + \ No newline at end of file diff --git a/assets/sprites/items/HoeStone.sprite b/assets/sprites/items/HoeStone.sprite index a36b04b..129761a 100644 --- a/assets/sprites/items/HoeStone.sprite +++ b/assets/sprites/items/HoeStone.sprite @@ -1,10 +1,10 @@ - - + - + \ No newline at end of file diff --git a/assets/sprites/items/Lvl1Sword.sprite b/assets/sprites/items/Lvl1Sword.sprite index 53336ec..1bce530 100644 --- a/assets/sprites/items/Lvl1Sword.sprite +++ b/assets/sprites/items/Lvl1Sword.sprite @@ -1,10 +1,10 @@ - - + - + \ No newline at end of file diff --git a/assets/sprites/items/Lvl2Sword.sprite b/assets/sprites/items/Lvl2Sword.sprite index 0c82476..f36b997 100644 --- a/assets/sprites/items/Lvl2Sword.sprite +++ b/assets/sprites/items/Lvl2Sword.sprite @@ -1,10 +1,10 @@ - - + - + \ No newline at end of file diff --git a/assets/sprites/items/RustyWateringCan.sprite b/assets/sprites/items/RustyWateringCan.sprite index 4a576cd..2953f66 100644 --- a/assets/sprites/items/RustyWateringCan.sprite +++ b/assets/sprites/items/RustyWateringCan.sprite @@ -1,10 +1,10 @@ - - + - + \ No newline at end of file diff --git a/src/components/basic/Grid.cpp b/src/components/basic/Grid.cpp index 17c80fc..3eeeab3 100644 --- a/src/components/basic/Grid.cpp +++ b/src/components/basic/Grid.cpp @@ -60,17 +60,14 @@ void Grid::onInitialize() GameObject* obj = *it; // Compute grid position(s) - Rect bounds(obj->transform.x, obj->transform.y, roundf(obj->transform.w), roundf(obj->transform.h)); - if (!bounds.intersects(m_bounds)) + if (!m_bounds.contains(obj->transform.x, obj->transform.y)) { std::cerr << "Grid: ignoring object " << obj->name << ": object outside allowed bounds."; continue; } // Set - for (int y = bounds.y; y < bounds.y + bounds.h; y++) - for (int x = bounds.x; x < bounds.x + bounds.w; x++) - set(obj, x, y, false); + set(obj, (int)obj->transform.x, (int)obj->transform.y, false); } } diff --git a/src/components/basic/Sprite.cpp b/src/components/basic/Sprite.cpp index f50e3f3..4b9615c 100644 --- a/src/components/basic/Sprite.cpp +++ b/src/components/basic/Sprite.cpp @@ -17,12 +17,12 @@ namespace components { namespace basic { Sprite::Sprite() - : name(), + : w(1), h(1), anchorX(0), anchorY(0), - animationVelocity(1.0f), + animationSpeed(1.0f), m_states(), m_stateNames(), - m_currentState(0), + m_currentState((size_t)-1), m_currentFrame(0), m_currentFrameTimeLeft(0) { @@ -37,12 +37,13 @@ model::Component* Sprite::clone() Sprite* clone = new Sprite(); // Copy public fields - clone->name = name; + clone->w = w; + clone->h = h; clone->anchorX = anchorX; clone->anchorY = anchorY; - clone->animationVelocity = animationVelocity; + clone->animationSpeed = animationSpeed; - // Copy private memberes + // Copy private members std::copy(m_states.begin(), m_states.end(), std::back_inserter(clone->m_states)); std::copy(m_stateNames.begin(), m_stateNames.end(), std::inserter(clone->m_stateNames, clone->m_stateNames.end())); clone->m_currentState = m_currentState; @@ -58,7 +59,7 @@ void Sprite::dump(unsigned level) std::cout<<" "; std::cout << " .Component: Sprite "; - std::cout << "name="<transform.globalScaleX(); + const float sY = gameObject->transform.globalScaleY(); + + return utils::RectF( + gameObject->transform.globalX() - sX * anchorX * w, + gameObject->transform.globalY() - sY * anchorY * h, + sX * w, sY * h); +} + void Sprite::addState(const SpriteState& state) { - Assert(state.frames.size() > 0, "State must have at least one frame!"); -#ifdef BUILD_DEBUG - float totalDuration = 0; - for (auto frame : state.frames) - totalDuration += frame.duration; - - Assert(totalDuration > 0, "State must have a frame which last at least one tick."); -#endif - Assert(m_stateNames.count(state.name) == 0, "A state with the same name already added!"); + Assert(m_stateNames.count(state.name) == 0, "A state with the same name already exists!"); m_states.push_back(state); m_stateNames.emplace(state.name, m_states.size() - 1); + + // First added state? + if (m_states.size() == 1) + { + m_currentState = 0; + } +} + +SpriteState& Sprite::createState(const std::string& name) +{ + SpriteState state = { .name = name }; + addState(state); + + return m_states.back(); +} + +SpriteState& Sprite::state(const std::string& name) +{ + Assert(m_stateNames.count(name) != 0, "State doesn't exist."); + + size_t index = m_stateNames.at(name); + return m_states.at(index); +} + +SpriteState& Sprite::state(size_t stateId) +{ + Assert(stateId < m_states.size(), "State ID doesn't exist."); + return m_states.at(stateId); +} + +size_t Sprite::stateCount() const +{ + return m_states.size(); } void Sprite::setState(size_t stateId) { Assert(stateId < m_states.size(), "Inexistent state."); - // Avoid resetting state - if (stateId == m_currentState) - return; - - m_currentState = stateId; - m_currentFrame = 0; - m_currentFrameTimeLeft = currentFrame().duration; + // Avoid restarting the animation at every function call + if (stateId != m_currentState) + { + m_currentState = stateId; + m_currentFrame = 0; + m_currentFrameTimeLeft = currentFrame().duration; + } } void Sprite::setState(const std::string& name) @@ -103,21 +140,22 @@ void Sprite::setState(const std::string& name) SpriteState& Sprite::currentState() { + Assert(m_currentState != (size_t)-1, "No states have been added!!!"); Assert(m_states.size() > 0, "Sprite must have at least one state!"); return m_states.at(m_currentState); } Frame& Sprite::currentFrame() { - Assert(currentState().frames.size() > 0, "State must have at least one frame!"); + Assert(m_currentFrame < currentState().frames.size(), "State must have at least one frame!"); return currentState().frames.at(m_currentFrame); } void Sprite::advanceTime(float fractions) { - Assert(m_states.size() > 0, "Sprite must have at least one state!"); + Assert(m_currentState != (size_t)-1, "No states have been added!"); - m_currentFrameTimeLeft -= fractions * animationVelocity; + m_currentFrameTimeLeft -= fractions * animationSpeed; while (m_currentFrameTimeLeft <= 0) { diff --git a/src/components/basic/Sprite.h b/src/components/basic/Sprite.h index 594b5f4..9588d17 100644 --- a/src/components/basic/Sprite.h +++ b/src/components/basic/Sprite.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -25,7 +26,6 @@ namespace basic { { uint32_t tileSetId; uint32_t tileSetCell; - uint32_t width, height; float duration; }; @@ -53,28 +53,31 @@ namespace basic { virtual void onPreRender() override; /** - * Adds a state to the sprite. + * Computes the boundaries of the sprite, considering all the elements: + * - global coordinates and scale + * - width and height + * - anchor point */ + utils::RectF boundaries() const; + + // State management void addState(const SpriteState& state); + SpriteState& createState(const std::string& name); + SpriteState& state(const std::string& name); + SpriteState& state(size_t stateId); + size_t stateCount() const; - /** - * Sets the current state. - */ + // Current state & frame void setState(size_t stateId); - - /** - * Sets the current state. - */ void setState(const std::string& name); - - // Getters SpriteState& currentState(); Frame& currentFrame(); // Public fields - std::string name; + float w, h; float anchorX, anchorY; - float animationVelocity; + + float animationSpeed; private: void advanceTime(float fractions); diff --git a/src/components/player/Player.cpp b/src/components/player/Player.cpp index 9d73e1c..f3fd436 100644 --- a/src/components/player/Player.cpp +++ b/src/components/player/Player.cpp @@ -27,24 +27,12 @@ static const float PlayerWalkVelocity = 2.0f; // The default velocity of the p static const float PlayerRunVelocity = 4.0f; // The default velocity of the player when running (units/sec). static const float PlayerAttackVelocity = 0.1f; // Movement speed when attacking. -static const float PickablePickTreshold = 0.7f; - /** * Distance from player position to "look" position. * This position is used for picking the cell which will be affected by the player's actions. */ static const float PlayerLookDistance = 0.5f; -/** - * Map between velocity and direction. - */ -static const Direction VelocitySignDirections[3][3] = -{ - { Direction::NorthWest, Direction::West, Direction::SouthWest }, - { Direction::North, Direction::None, Direction::South }, - { Direction::NorthEast, Direction::East, Direction::SouthEast }, -}; - Player::Player() : selectedItemIndex(-1), selectedItem(nullptr), @@ -183,8 +171,9 @@ void Player::handleInventoryDropEvent(SDL_Event& event) selectedItem->addComponent(new Pickable()); // Set location - translate(gameObject->transform.x, gameObject->transform.y, facingDirection, 2.0f, - &selectedItem->transform.x, &selectedItem->transform.y); + selectedItem->transform.x = gameObject->transform.x; + selectedItem->transform.y = gameObject->transform.y; + move(&selectedItem->transform.x, &selectedItem->transform.y, facingDirection, 2.0f); // Remove selection selectedItemIndex = -1; @@ -231,12 +220,12 @@ void Player::preRenderSelectedItem() // Set item position if (selectedItem) { - if (facingDirection & Direction::East) + if (facingDirection == Direction::East) { selectedItem->transform.x = 0.2f; selectedItem->transform.y = -0.8f; } - else if (facingDirection & Direction::West) + else if (facingDirection == Direction::West) { selectedItem->transform.x = -0.8f; selectedItem->transform.y = -0.8f; @@ -252,14 +241,21 @@ void Player::preRenderMovement() // Compute current state std::string stateName = (walking) ? "Walking " : "Idle "; - if (facingDirection & Direction::East) + switch (facingDirection) + { + case Direction::East: stateName += "right"; - else if (facingDirection & Direction::West) + break; + case Direction::West: stateName += "left"; - else if (facingDirection & Direction::North) + break; + case Direction::North: stateName += "up"; - else + break; + case Direction::South: stateName += "down"; + break; + } sprite->setState(stateName); @@ -269,7 +265,7 @@ void Player::preRenderMovement() animVelocity = 0.1f; // TODO: move this animation velocity change somewhere else - sprite->animationVelocity = animVelocity; + sprite->animationSpeed = animVelocity; // Set camera GameObject* cam = GameState::current().renderContext.cameraObj(); @@ -306,16 +302,17 @@ void Player::updateMovement() walking = true; facingDirection = getDirection(vx, vy); - translate(newX, newY, facingDirection, PlayerLookDistance, &lookX, &lookY); + lookX = newX; + lookY = newY; + move(&lookX, &lookY, facingDirection, PlayerLookDistance); } } Direction Player::getDirection(float vx, float vy) { - int xx = (0 < vx) - (vx < 0); - int yy = (0 < vy) - (vy < 0); - - return VelocitySignDirections[xx + 1][yy + 1]; + if (vx != 0) + return (vx > 0) ? Direction::East : Direction::West; + return (vy > 0) ? Direction::South : Direction::North; } void Player::handleActionEvents(SDL_Event& event) @@ -374,15 +371,14 @@ void Player::updatePickables() if (pickable) { - // Compute distance from player - float dist = distanceSq(gameObject->transform.x, gameObject->transform.y, obj->transform.x, obj->transform.y); - - // Pick the object up - if (dist < PickablePickTreshold * PickablePickTreshold) + if (checkCollision(gameObject, obj)) { // We can't modify the container now, so queue the item for picking up toPickUp.push_back(obj); } + // Compute distance from player +// float dist = distanceSq(gameObject->transform.x, gameObject->transform.y, obj->transform.x, obj->transform.y); + } } diff --git a/src/graphics/SpriteRenderer.cpp b/src/graphics/SpriteRenderer.cpp index 99133dc..af5a9fe 100644 --- a/src/graphics/SpriteRenderer.cpp +++ b/src/graphics/SpriteRenderer.cpp @@ -44,20 +44,20 @@ void SpriteRenderer::onInitialize() void SpriteRenderer::onRender() { - float posX = m_context->xToScreen(gameObject->transform.globalX()); - float posY = m_context->yToScreen(gameObject->transform.globalY()); - - float w = m_sprite->currentFrame().width * m_context->viewport.pixelsPerUnitX; - float h = m_sprite->currentFrame().height * m_context->viewport.pixelsPerUnitY; + utils::RectF bounds = m_sprite->boundaries(); + float spriteW = bounds.w * m_context->viewport.pixelsPerUnitX; + float spriteH = bounds.h * m_context->viewport.pixelsPerUnitY; // Compute destination rectangle float scale = m_context->camera()->scale; - SDL_Rect dest; - dest.x = posX - m_sprite->anchorX * w * scale; - dest.y = posY - m_sprite->anchorY * h * scale; - dest.w = w * scale; - dest.h = h * scale; + SDL_Rect dest = + { + .x = static_cast(m_context->xToScreen(bounds.x)), + .y = static_cast(m_context->yToScreen(bounds.y)), + .w = static_cast(spriteW * scale), + .h = static_cast(spriteH * scale) + }; if (m_context->visible(dest)) { @@ -68,8 +68,8 @@ void SpriteRenderer::onRender() // Compute source rectangle SDL_Rect src; getCell(texture, m_sprite->currentFrame().tileSetCell, &src.x, &src.y); - src.w = w; - src.h = h; + src.w = spriteW; + src.h = spriteH; // Draw SdlRenderer::instance().renderTexture(texture, &src, &dest); diff --git a/src/math/GameMath.cpp b/src/math/GameMath.cpp index 1dfbaaf..122a138 100644 --- a/src/math/GameMath.cpp +++ b/src/math/GameMath.cpp @@ -5,36 +5,23 @@ * Author: tibi */ +#include #include +#include +#include #include +using namespace farmlands::components::basic; + namespace farmlands { -static const float Sqrt2 = 1.41421356237309504880f; - -static const float DirectionVectorX[] = +void move(float *x, float *y, model::Direction direction, float distance) { - 0, 1, 0, Sqrt2, // none, E, N, NE - -1, 0, -Sqrt2, 0, // W, inv, NW, inv - 0, Sqrt2, 0, 0, // S, SE, inv, inv - -Sqrt2, 0, 0, 0 // SW, inv, inv, inv -}; + float dx = cosf(degToRad(direction)); + float dy = sinf(degToRad(direction)); -static const float DirectionVectorY[] = -{ - 0, 0, -1, -Sqrt2, // none, E, N, NE - 0, 0, -Sqrt2, 0, // W, inv, NW, inv - 1, Sqrt2, 0, 0, // S, SE, inv, inv - Sqrt2, 0, 0, 0 // SW, inv, inv, inv -}; - -void translate(float x, float y, model::Direction direction, float distance, float* outX, float *outY) -{ - float dx = DirectionVectorX[direction]; - float dy = DirectionVectorY[direction]; - - *outX = x + dx * distance; - *outY = y + dy * distance; + *x += dx * distance; + *x += dy * distance; } void moveTowards(float *x, float *y, float towardsX, float towardsY, float speed) @@ -49,4 +36,24 @@ float distanceSq(float x0, float y0, float x1, float y1) return (x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1); } +bool checkCollision(model::GameObject* objA, model::GameObject* objB) +{ + Sprite* sprA = objA->component(); + Sprite* sprB = objB->component(); + + // If both have sprites, they intersect if their rectangles intersect + if (sprA && sprB) + return sprA->boundaries().intersects(sprB->boundaries()); + + // Only one has sprite. They collide if sprite rectangle contains the other point + if (sprA) + return sprA->boundaries().contains(objB->transform.globalX(), objB->transform.globalY()); + if (sprB) + return sprB->boundaries().contains(objA->transform.globalX(), objA->transform.globalY()); + + // Objects collide if their coordinates are equal. + return std::abs(objA->transform.globalX() - objB->transform.globalX()) < 1e-10 + && std::abs(objA->transform.globalY() - objB->transform.globalY()) < 1e-10; +} + } diff --git a/src/math/GameMath.h b/src/math/GameMath.h index e69840c..ca696b9 100644 --- a/src/math/GameMath.h +++ b/src/math/GameMath.h @@ -9,9 +9,20 @@ #define MATH_GAMEMATH_H_ #include +#include + +#define _USE_MATH_DEFINES +#include namespace farmlands { + void move(float* x, float* y, model::Direction direction, float distance); + void moveTowards(float *x, float *y, float towardsX, float towardsY, float speed); + + float distanceSq(float x0, float y0, float x1, float y1); + + bool checkCollision(model::GameObject* objA, model::GameObject* objB); + template TVal clamp (TVal value, TMin min, TMax max) { @@ -24,9 +35,12 @@ namespace farmlands { return value; } - void translate(float x, float y, model::Direction direction, float distance, float* outX, float* outY); - void moveTowards(float *x, float *y, float towardsX, float towardsY, float speed); - float distanceSq(float x0, float y0, float x1, float y1); - + /** + * Converts from degrees to radians + */ + inline float degToRad(float angle) + { + return angle * (float)M_PI / 360.0f; + } } #endif /* MATH_GAMEMATH_H_ */ diff --git a/src/model/Direction.h b/src/model/Direction.h index d866204..7b2e683 100644 --- a/src/model/Direction.h +++ b/src/model/Direction.h @@ -14,14 +14,10 @@ namespace model { enum Direction { None = 0, - East = 1, - North = 2, - West = 4, - South = 8, - NorthEast = North | East, - NorthWest = North | West, - SouthWest = South | West, - SouthEast = South | East + East = 0, + North = 90, + West = 180, + South = 270 }; } diff --git a/src/model/GameObject.cpp b/src/model/GameObject.cpp index 5e2339d..d88a736 100644 --- a/src/model/GameObject.cpp +++ b/src/model/GameObject.cpp @@ -8,7 +8,7 @@ #include #include #include - +#include #include namespace farmlands { diff --git a/src/model/Transform.cpp b/src/model/Transform.cpp index 57398a7..65d36b8 100644 --- a/src/model/Transform.cpp +++ b/src/model/Transform.cpp @@ -1,58 +1,71 @@ -/* - * Transform.cpp - * - * Created on: Dec 2, 2016 - * Author: tibi - */ - #include #include -#include - namespace farmlands { namespace model { Transform::Transform(GameObject* obj) : x(0), y(0), - w(0), h(0), + scaleX(1), scaleY(1), gameObject(obj) { } float Transform::globalX() const { - if (gameObject->parent()) - return gameObject->parent()->transform.globalX() + x; - - return x; + return (gameObject->parent()) + ? x + gameObject->parent()->transform.globalX() + : x; } float Transform::globalY() const { - if (gameObject->parent()) - return gameObject->parent()->transform.globalY() + y; - - return y; + return (gameObject->parent()) + ? y + gameObject->parent()->transform.globalY() + : y; } -void Transform::setGlobalX(float x) +void Transform::setGlobalX(float gx) { - if (gameObject->parent()) - this->x = x - gameObject->parent()->transform.globalX(); - - this->x = x; + x = (gameObject->parent()) + ? gx - gameObject->parent()->transform.globalX() + : gx; } -void Transform::setGlobalY(float y) +void Transform::setGlobalY(float gy) { - if (gameObject->parent()) - this->y = y - gameObject->parent()->transform.globalY(); + y = (gameObject->parent()) + ? gy - gameObject->parent()->transform.globalY() + : gy; +} - this->y = y; +float Transform::globalScaleX() const +{ + return (gameObject->parent()) + ? scaleX * gameObject->parent()->transform.globalScaleX() + : scaleX; +} + +float Transform::globalScaleY() const +{ + return (gameObject->parent()) + ? scaleY * gameObject->parent()->transform.globalScaleY() + : scaleY; +} + +void Transform::setGlobalScaleX(float gscaleX) +{ + scaleX = (gameObject->parent()) + ? gscaleX / gameObject->parent()->transform.globalScaleX() + : gscaleX; +} + +void Transform::setGlobalScaleY(float gscaleY) +{ + scaleY = (gameObject->parent()) + ? gscaleY / gameObject->parent()->transform.globalScaleY() + : gscaleY; } } } /* namespace farmlands */ - - diff --git a/src/model/Transform.h b/src/model/Transform.h index e981ec1..6bfaeb1 100644 --- a/src/model/Transform.h +++ b/src/model/Transform.h @@ -21,15 +21,20 @@ namespace model { // Getters float globalX() const; float globalY() const; + float globalScaleX() const; + float globalScaleY() const; // Setters - void setGlobalX(float x); - void setGlobalY(float y); + void setGlobalX(float gx); + void setGlobalY(float gy); + void setGlobalScaleX(float gscaleX); + void setGlobalScaleY(float gscaleY); // Local coordinates (relative to parent) float x, y; - float w, h; + float scaleX, scaleY; + // Parent object GameObject* gameObject; }; diff --git a/src/storage/Parsers.cpp b/src/storage/Parsers.cpp index 99ad4c1..0bbdc4b 100644 --- a/src/storage/Parsers.cpp +++ b/src/storage/Parsers.cpp @@ -123,14 +123,10 @@ components::basic::Frame* parse (boost::property_tree: components::basic::Frame* frame = new components::basic::Frame(); - // Obtine tile set id + // Set properties std::string tileSetPath = root.get(".tileSet"); frame->tileSetId = resources::ResourceManager::instance().getId(tileSetPath); - - // Set properties frame->tileSetCell = root.get(".cell"); - frame->width = root.get(".w", 1u); - frame->height = root.get(".h", 1u); frame->duration = root.get(".duration"); return frame; @@ -202,9 +198,11 @@ components::basic::Sprite* parse (boost::property_tre // Parse components::basic::Sprite components::basic::Sprite* sprite = new components::basic::Sprite(); + sprite->w = root.get(".w"); + sprite->h = root.get(".h"); sprite->anchorX = root.get(".anchorX"); sprite->anchorY = root.get(".anchorY"); - sprite->animationVelocity = root.get(".animationVelocity", 1.0f); + sprite->animationSpeed = root.get(".animationSpeed", 1.0f); for (auto child : root) { @@ -451,14 +449,14 @@ model::Direction parseDirection(std::string directionStr) model::Direction direction = model::Direction::None; boost::to_lower(directionStr); - if (boost::contains(directionStr, "east")) - direction = (model::Direction) (direction | model::Direction::East); - if (boost::contains(directionStr, "north")) - direction = (model::Direction) (direction | model::Direction::North); - if (boost::contains(directionStr, "west")) - direction = (model::Direction) (direction | model::Direction::West); - if (boost::contains(directionStr, "south")) - direction = (model::Direction) (direction | model::Direction::South); + if (directionStr == "east") + direction = model::Direction::East; + if (directionStr == "north") + direction = model::Direction::North; + if (directionStr == "west") + direction = model::Direction::West; + if (directionStr == "south") + direction = model::Direction::South; return direction; } @@ -533,8 +531,8 @@ void parseTransform (model::Transform& transform, boost::property_tree::ptree& r transform.x = root.get(".x", 0.0f); transform.y = root.get(".y", 0.0f); - transform.w = root.get(".w", 1.0f); - transform.h = root.get(".h", 1.0f); + transform.scaleX = root.get(".scaleX", 1.0f); + transform.scaleY = root.get(".scaleY", 1.0f); } template <>