diff --git a/assets/scenes/Game.scene b/assets/scenes/Game.scene index f10be84..87c5bdf 100644 --- a/assets/scenes/Game.scene +++ b/assets/scenes/Game.scene @@ -32,6 +32,10 @@ money="50" /> + + + + diff --git a/src/components/basic/Grid.cpp b/src/components/basic/Grid.cpp index 78292b4..17c80fc 100644 --- a/src/components/basic/Grid.cpp +++ b/src/components/basic/Grid.cpp @@ -6,7 +6,6 @@ */ #include -#include #include #include @@ -60,16 +59,8 @@ void Grid::onInitialize() { GameObject* obj = *it; - // Get transform - Transform* tr = obj->component(); - if (tr == nullptr) - { - std::cerr << "Grid: ignoring object " << obj->name << ": object has no transform."; - continue; - } - // Compute grid position(s) - Rect bounds(tr->x, tr->y, roundf(tr->w), roundf(tr->h)); + Rect bounds(obj->transform.x, obj->transform.y, roundf(obj->transform.w), roundf(obj->transform.h)); if (!bounds.intersects(m_bounds)) { std::cerr << "Grid: ignoring object " << obj->name << ": object outside allowed bounds."; @@ -139,11 +130,8 @@ void Grid::set(model::GameObject* obj, int x, int y, bool throwOnOverwrite) } m_grid[index] = obj; - - // Set transform as well - Transform* transf = obj->component(); - transf->x = x; - transf->y = y; + obj->transform.x = x; + obj->transform.y = y; } } /* namespace basic */ diff --git a/src/components/basic/Inventory.cpp b/src/components/basic/Inventory.cpp index 6bb68c9..8fa4aff 100644 --- a/src/components/basic/Inventory.cpp +++ b/src/components/basic/Inventory.cpp @@ -142,7 +142,17 @@ void Inventory::setCapacity(size_t capacity) m_capacity = capacity; } +size_t Inventory::emptySlots() const +{ + size_t count = 0; + + for (size_t i = 0; i < m_capacity; i++) + if (m_items[i] == nullptr) + ++count; + + return count; +} + } /* namespace basic */ } /* namespace components */ } /* namespace farmlands */ - diff --git a/src/components/basic/Inventory.h b/src/components/basic/Inventory.h index b363388..a0c691e 100644 --- a/src/components/basic/Inventory.h +++ b/src/components/basic/Inventory.h @@ -65,6 +65,11 @@ namespace basic { */ size_t capacity() const { return m_capacity; } + /** + * Gets the number of empty slots. + */ + size_t emptySlots() const; + private: size_t m_capacity; model::GameObject** m_items; diff --git a/src/components/basic/Sprite.cpp b/src/components/basic/Sprite.cpp index 5f5588a..f50e3f3 100644 --- a/src/components/basic/Sprite.cpp +++ b/src/components/basic/Sprite.cpp @@ -6,7 +6,6 @@ */ #include -#include #include #include diff --git a/src/components/basic/Transform.cpp b/src/components/basic/Transform.cpp deleted file mode 100644 index 9e1886d..0000000 --- a/src/components/basic/Transform.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Transform.cpp - * - * Created on: Dec 2, 2016 - * Author: tibi - */ - -#include -#include - -#include - -namespace farmlands { -namespace components { -namespace basic { - -Transform::Transform() - : x(0), y(0), - w(0), h(0), - m_parent(nullptr) -{ -} - -Transform::~Transform() -{ -} - -model::Component* Transform::clone() -{ - Transform* clone = new Transform(); - clone->x = x; - clone->y = y; - clone->w = w; - clone->h = h; - return clone; -} - -void Transform::onCreate() -{ - if (gameObject->parent() != nullptr) - m_parent = gameObject->parent()->component(); -} - -float Transform::globalX() const -{ - return (m_parent) ? m_parent->globalX() + x : x; -} - -float Transform::globalY() const -{ - return (m_parent) ? m_parent->globalY() + y : y; -} - -void Transform::setGlobalX(float x) -{ - this->x = (m_parent) ? x - m_parent->globalX() : x; -} - -void Transform::setGlobalY(float y) -{ - this->y = (m_parent) ? y - m_parent->globalY() : y; -} - -void Transform::dump(unsigned level) -{ - for (unsigned i = 0; i < level; i++) - std::cout<<" "; - - std::cout << " .Component: Transform "; - std::cout << "x="< +#include #include #include #include @@ -26,6 +27,8 @@ 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. @@ -54,9 +57,9 @@ Player::Player() hp(100), maxHp(100), energy(100), maxEnergy(100), money(0), - m_transform(nullptr), m_inventory(nullptr), - m_grid(nullptr) + m_grid(nullptr), + m_pickables(nullptr) { } @@ -101,15 +104,20 @@ void Player::dump(unsigned level) void Player::onInitialize() { - m_transform = gameObject->component(); m_inventory = gameObject->component(); - // Search for the object which has the grid auto root = &GameState::current().scene->root; + + // Search for the object which has the grid auto gridIt = root->findByComponent(); Assert(gridIt != root->childrenEnd(), "Cannot find grid component."); m_grid = (*gridIt)->component(); + // Search for the object named "Pickables" + auto pickablesIt = root->findByName("Pickables"); + Assert(pickablesIt != root->childrenEnd(), "Cannot find grid component."); + m_pickables = *pickablesIt; + initializeInventory(); } @@ -129,6 +137,7 @@ void Player::onUpdateLogic() energy = clamp(energy, 0, maxEnergy); updateMovement(); + updatePickables(); } void Player::onPreRender() @@ -165,10 +174,19 @@ void Player::handleInventoryDropEvent(SDL_Event& event) if (inventoryDrop && selectedItemIndex >= 0) { - // TODO: convert to 'pickable' item instead of destroying + // Remove object and add it to "pickables" m_inventory->remove(static_cast(selectedItemIndex)); - gameObject->destroyChild(selectedItem); + gameObject->removeChild(selectedItem); + m_pickables->addChild(selectedItem); + // Make pickable + selectedItem->addComponent(new Pickable()); + + // Set location + translate(gameObject->transform.x, gameObject->transform.y, facingDirection, 2.0f, + &selectedItem->transform.x, &selectedItem->transform.y); + + // Remove selection selectedItemIndex = -1; selectedItem = nullptr; selectedWeapon = nullptr; @@ -213,17 +231,15 @@ void Player::preRenderSelectedItem() // Set item position if (selectedItem) { - basic::Transform* itemTransf = selectedItem->component(); - if (facingDirection & Direction::East) { - itemTransf->x = 0.2f; - itemTransf->y = -0.8f; + selectedItem->transform.x = 0.2f; + selectedItem->transform.y = -0.8f; } else if (facingDirection & Direction::West) { - itemTransf->x = -0.8f; - itemTransf->y = -0.8f; + selectedItem->transform.x = -0.8f; + selectedItem->transform.y = -0.8f; } } } @@ -256,9 +272,9 @@ void Player::preRenderMovement() sprite->animationVelocity = animVelocity; // Set camera - Transform* cam = GameState::current().renderContext.cameraTransform(); - cam->x = m_transform->x; - cam->y = m_transform->y - 1; + GameObject* cam = GameState::current().renderContext.cameraObj(); + cam->transform.x = gameObject->transform.x; + cam->transform.y = gameObject->transform.y - 1; } void Player::updateMovement() @@ -279,13 +295,13 @@ void Player::updateMovement() // Compute movement positions float vx = Input::instance().getX() * velMultiplier; float vy = Input::instance().getY() * velMultiplier; - float newX = m_transform->x + vx; - float newY = m_transform->y + vy; + float newX = gameObject->transform.x + vx; + float newY = gameObject->transform.y + vy; if ((vx || vy) && canMove(newX, newY)) { - m_transform->x = newX; - m_transform->y = newY; + gameObject->transform.x = newX; + gameObject->transform.y = newY; walking = true; facingDirection = getDirection(vx, vy); @@ -343,6 +359,49 @@ void Player::performAction(model::GameObject* obj) } } +void Player::updatePickables() +{ + // Don't do anything if inventory is full + if (m_inventory->emptySlots() <= 0) + return; + + std::vector toPickUp; + + for (auto it = m_pickables->childrenBegin(); it != m_pickables->childrenEnd(); it++) + { + GameObject* obj = *it; + Pickable* pickable = obj->component(); + + 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) + { + // We can't modify the container now, so queue the item for picking up + toPickUp.push_back(obj); + } + } + } + + for (auto obj : toPickUp) + { + // Object no longer pickable + obj->destroyComponent(); + + // Make object a child of player + m_pickables->removeChild(obj); + gameObject->addChild(obj); + + // Add to inventory + m_inventory->add(obj); + obj->setEnabled(false); + obj->transform.x = 0; + obj->transform.y = 0; + } +} } /* namespace player */ } /* namespace components */ diff --git a/src/components/player/Player.h b/src/components/player/Player.h index 22e9ce2..d2538aa 100644 --- a/src/components/player/Player.h +++ b/src/components/player/Player.h @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -18,6 +17,8 @@ #include +#include + namespace farmlands { namespace components { namespace player { @@ -75,9 +76,13 @@ namespace player { void handleActionEvents(SDL_Event& event); void performAction(model::GameObject* obj); - basic::Transform* m_transform; + // Picking up + void updatePickables(); + basic::Inventory* m_inventory; basic::Grid* m_grid; + + model::GameObject* m_pickables; }; } /* namespace player */ diff --git a/src/graphics/MapRenderer.cpp b/src/graphics/MapRenderer.cpp index aefd166..0ec54e2 100644 --- a/src/graphics/MapRenderer.cpp +++ b/src/graphics/MapRenderer.cpp @@ -58,10 +58,10 @@ void MapRenderer::onRender() float cellsOnScreenX = m_context->viewport.width / cellW; float cellsOnScreenY = m_context->viewport.height / cellH; - int minCellX = floorf(m_context->cameraTransform()->x - cellsOnScreenX / 2); - int maxCellX = ceilf(m_context->cameraTransform()->x + cellsOnScreenX / 2); - int minCellY = floorf(m_context->cameraTransform()->y - cellsOnScreenY / 2); - int maxCellY = ceilf(m_context->cameraTransform()->y + cellsOnScreenY / 2); + int minCellX = floorf(m_context->cameraObj()->transform.x - cellsOnScreenX / 2); + int maxCellX = ceilf(m_context->cameraObj()->transform.x + cellsOnScreenX / 2); + int minCellY = floorf(m_context->cameraObj()->transform.y - cellsOnScreenY / 2); + int maxCellY = ceilf(m_context->cameraObj()->transform.y + cellsOnScreenY / 2); // Clamp cell positions minCellX = clamp(minCellX, 0, (int)m_map->width - 1); diff --git a/src/graphics/MapRenderer.h b/src/graphics/MapRenderer.h index 27dd9e0..9e2e9b7 100644 --- a/src/graphics/MapRenderer.h +++ b/src/graphics/MapRenderer.h @@ -10,7 +10,6 @@ #include #include -#include #include #include diff --git a/src/graphics/RenderContext.cpp b/src/graphics/RenderContext.cpp index feb7e45..652d7ac 100644 --- a/src/graphics/RenderContext.cpp +++ b/src/graphics/RenderContext.cpp @@ -6,7 +6,6 @@ */ #include -#include #include #include @@ -19,25 +18,25 @@ namespace graphics { float RenderContext::xToWorld(float x) { float cellW = viewport.pixelsPerUnitX * m_camera->scale; - return (x - viewport.width / 2) / cellW + m_cameraTransform->x; + return (x - viewport.width / 2) / cellW + m_cameraObj->transform.x; } float RenderContext::yToWorld(float y) { float cellH = viewport.pixelsPerUnitY * m_camera->scale; - return (y - viewport.height / 2) / cellH + m_cameraTransform->y; + return (y - viewport.height / 2) / cellH + m_cameraObj->transform.y; } float RenderContext::xToScreen(float x) { float cellW = viewport.pixelsPerUnitX * m_camera->scale; - return (x - m_cameraTransform->x) * cellW + viewport.width / 2; + return (x - m_cameraObj->transform.x) * cellW + viewport.width / 2; } float RenderContext::yToScreen(float y) { float cellH = viewport.pixelsPerUnitY * m_camera->scale; - return (y - m_cameraTransform->y) * cellH + viewport.height / 2; + return (y - m_cameraObj->transform.y) * cellH + viewport.height / 2; } bool RenderContext::visible(SDL_Rect& rect) @@ -49,7 +48,6 @@ bool RenderContext::visible(SDL_Rect& rect) void RenderContext::setCamera(GameObject* camera) { m_cameraObj = camera; - m_cameraTransform = camera->component(); m_camera = camera->component(); } diff --git a/src/graphics/RenderContext.h b/src/graphics/RenderContext.h index c6350de..42148f1 100644 --- a/src/graphics/RenderContext.h +++ b/src/graphics/RenderContext.h @@ -8,6 +8,7 @@ #ifndef GRAPHICS_RENDERCONTEXT_H_ #define GRAPHICS_RENDERCONTEXT_H_ +#include #include #include @@ -32,8 +33,6 @@ namespace graphics { inline model::GameObject* cameraObj() { return m_cameraObj; } inline components::basic::Camera* camera() { return m_camera; } - inline components::basic::Transform* cameraTransform() { return m_cameraTransform; } - void setCamera(model::GameObject* camera); /** @@ -44,7 +43,6 @@ namespace graphics { private: model::GameObject* m_cameraObj; - components::basic::Transform* m_cameraTransform; components::basic::Camera* m_camera; }; diff --git a/src/graphics/SpriteRenderer.cpp b/src/graphics/SpriteRenderer.cpp index 0ede68c..99133dc 100644 --- a/src/graphics/SpriteRenderer.cpp +++ b/src/graphics/SpriteRenderer.cpp @@ -23,7 +23,6 @@ namespace graphics { SpriteRenderer::SpriteRenderer() : m_context(nullptr), - m_transform(nullptr), m_sprite(nullptr) { } @@ -40,14 +39,13 @@ model::Component* SpriteRenderer::clone() void SpriteRenderer::onInitialize() { m_context = &GameState::current().renderContext; - m_transform = gameObject->component(); m_sprite = gameObject->component(); } void SpriteRenderer::onRender() { - float posX = m_context->xToScreen(m_transform->globalX()); - float posY = m_context->yToScreen(m_transform->globalY()); + 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; diff --git a/src/graphics/SpriteRenderer.h b/src/graphics/SpriteRenderer.h index 954f0de..74886a7 100644 --- a/src/graphics/SpriteRenderer.h +++ b/src/graphics/SpriteRenderer.h @@ -11,7 +11,6 @@ #include #include #include -#include #include @@ -35,7 +34,6 @@ namespace graphics { // Private fields graphics::RenderContext* m_context; - components::basic::Transform* m_transform; components::basic::Sprite* m_sprite; }; diff --git a/src/math/GameMath.cpp b/src/math/GameMath.cpp index cef12a3..1dfbaaf 100644 --- a/src/math/GameMath.cpp +++ b/src/math/GameMath.cpp @@ -6,6 +6,7 @@ */ #include +#include namespace farmlands { @@ -36,4 +37,16 @@ void translate(float x, float y, model::Direction direction, float distance, flo *outY = y + dy * distance; } +void moveTowards(float *x, float *y, float towardsX, float towardsY, float speed) +{ + float angle = atan2f(towardsX - *x, towardsY - *y); + *x += cosf(angle) * speed; + *y += sinf(angle) * speed; +} + +float distanceSq(float x0, float y0, float x1, float y1) +{ + return (x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1); +} + } diff --git a/src/math/GameMath.h b/src/math/GameMath.h index 12f2b5d..e69840c 100644 --- a/src/math/GameMath.h +++ b/src/math/GameMath.h @@ -24,7 +24,9 @@ namespace farmlands { return value; } - void translate(float x, float y, model::Direction direction, float distance, float* outX, float *outY); + 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); } #endif /* MATH_GAMEMATH_H_ */ diff --git a/src/model/GameObject.cpp b/src/model/GameObject.cpp index 2cdedfc..5e2339d 100644 --- a/src/model/GameObject.cpp +++ b/src/model/GameObject.cpp @@ -17,6 +17,7 @@ namespace model { GameObject::GameObject() : name("unnamed"), visible(true), + transform(this), m_components(), m_children(), m_parent(nullptr), @@ -115,6 +116,7 @@ GameObject* GameObject::removeChild(GameObject* obj) if (*it == obj) { m_children.erase(it); + obj->m_parent = nullptr; return *it; } } diff --git a/src/model/GameObject.h b/src/model/GameObject.h index cd70dc9..bba05c3 100644 --- a/src/model/GameObject.h +++ b/src/model/GameObject.h @@ -8,6 +8,7 @@ #ifndef GAMEOBJECT_H_ #define GAMEOBJECT_H_ +#include #include #include #include @@ -97,6 +98,7 @@ namespace model { // Other properties std::string name; bool visible; + Transform transform; private: // Components @@ -169,7 +171,16 @@ namespace model { void GameObject::destroyComponent() { std::type_index typeIndex(typeid(T)); - m_components.erase(typeIndex); + T* comp = nullptr; + + auto it = m_components.find(typeIndex); + if (it != m_components.end()) + { + T* comp = dynamic_cast(it->second); + m_components.erase(it); + + delete comp; + } } template diff --git a/src/model/Transform.cpp b/src/model/Transform.cpp new file mode 100644 index 0000000..57398a7 --- /dev/null +++ b/src/model/Transform.cpp @@ -0,0 +1,58 @@ +/* + * 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), + gameObject(obj) +{ +} + +float Transform::globalX() const +{ + if (gameObject->parent()) + return gameObject->parent()->transform.globalX() + x; + + return x; +} + +float Transform::globalY() const +{ + if (gameObject->parent()) + return gameObject->parent()->transform.globalY() + y; + + return y; +} + +void Transform::setGlobalX(float x) +{ + if (gameObject->parent()) + this->x = x - gameObject->parent()->transform.globalX(); + + this->x = x; +} + +void Transform::setGlobalY(float y) +{ + if (gameObject->parent()) + this->y = y - gameObject->parent()->transform.globalY(); + + this->y = y; +} + +} +} /* namespace farmlands */ + + diff --git a/src/components/basic/Transform.h b/src/model/Transform.h similarity index 58% rename from src/components/basic/Transform.h rename to src/model/Transform.h index d513298..e981ec1 100644 --- a/src/components/basic/Transform.h +++ b/src/model/Transform.h @@ -8,22 +8,15 @@ #ifndef BASE_TRANSFORM_H_ #define BASE_TRANSFORM_H_ -#include - namespace farmlands { -namespace components { -namespace basic { +namespace model { - class Transform: public model::Component + class GameObject; + + class Transform { public: - Transform(); - virtual ~Transform(); - - virtual model::Component* clone() override; - virtual void dump(unsigned level) override; - - virtual void onCreate(); + Transform(GameObject* obj); // Getters float globalX() const; @@ -37,11 +30,9 @@ namespace basic { float x, y; float w, h; - private: - Transform* m_parent; + GameObject* gameObject; }; -} } } /* namespace farmlands */ diff --git a/src/resources/ResourceManager.cpp b/src/resources/ResourceManager.cpp index 5fbf08c..9af87a6 100644 --- a/src/resources/ResourceManager.cpp +++ b/src/resources/ResourceManager.cpp @@ -95,11 +95,18 @@ void ResourceManager::loadGame() model::GameObject* player = *root->findByName("Player"); components::basic::Inventory* inventory = player->component(); + model::GameObject* pickables = *root->findByName("Pickables"); + // Give player all items for (auto prefab : GameState::current().itemPrefabs) { - model::GameObject* item = model::GameObject::instantiate(prefab, "inv item", player); - inventory->add(item); + float x = player->transform.x + 15 * GameState::current().random.getFloat(); + float y = player->transform.y + 15 * GameState::current().random.getFloat(); + + model::GameObject* item = model::GameObject::instantiate(prefab, "Pickable item", pickables); + item->transform.x = x; + item->transform.y = y; + item->addComponent(new components::items::Pickable()); } for (auto prefab : GameState::current().seedsPrefabs) diff --git a/src/storage/Parsers.cpp b/src/storage/Parsers.cpp index 932404f..99ad4c1 100644 --- a/src/storage/Parsers.cpp +++ b/src/storage/Parsers.cpp @@ -242,22 +242,6 @@ components::basic::SpriteState* parse (boost::pr return spriteState; } -template <> -components::basic::Transform* parse (boost::property_tree::ptree& root) -{ - // Ensure we are on the correct node (property tree seems to add root of its own) - if (root.size() > 0 && root.front().first == "Transform") - root = root.front().second; - - components::basic::Transform* transform = new components::basic::Transform(); - 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); - - return transform; -} - template <> components::DebugController* parse (boost::property_tree::ptree& root) { @@ -541,6 +525,18 @@ graphics::SpriteRenderer* parse (boost::property_tree: /****** Model ******/ +void parseTransform (model::Transform& transform, boost::property_tree::ptree& root) +{ + // Ensure we are on the correct node (property tree seems to add root of its own) + if (root.size() > 0 && root.front().first == "Transform") + root = root.front().second; + + 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); +} + template <> model::GameObject* parse (boost::property_tree::ptree& root) { @@ -559,6 +555,9 @@ model::GameObject* parse (boost::property_tree::ptree& root) if (child.first == "GameObject") gameObj->addChild(parse(child.second)); + else if (child.first == "Transform") + parseTransform(gameObj->transform, child.second); + // Components::basic else if (child.first == "Camera") gameObj->addComponent(parse(child.second)); @@ -575,9 +574,6 @@ model::GameObject* parse (boost::property_tree::ptree& root) else if (child.first == "Sprite") gameObj->addComponent(parse(child.second)); - else if (child.first == "Transform") - gameObj->addComponent(parse(child.second)); - // Components::items else if (child.first == "Axe") gameObj->addComponent(parse(child.second)); diff --git a/src/storage/Parsers.h b/src/storage/Parsers.h index b35869a..714e11a 100644 --- a/src/storage/Parsers.h +++ b/src/storage/Parsers.h @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -68,9 +67,6 @@ namespace storage { template <> components::basic::SpriteState* parse (boost::property_tree::ptree& root); - template <> - components::basic::Transform* parse (boost::property_tree::ptree& root); - template <> components::DebugController* parse (boost::property_tree::ptree& root);