farmlands/src/components/player/Player.cpp

405 lines
9.6 KiB
C++

/*
* Player.cpp
*
* Created on: Dec 10, 2016
* Author: tibi
*/
#include <GameState.h>
#include <components/items/Pickable.h>
#include <components/player/Player.h>
#include <input/Input.h>
#include <math/GameMath.h>
#include <iostream>
using namespace farmlands::components::basic;
using namespace farmlands::components::items;
using namespace farmlands::graphics;
using namespace farmlands::input;
using namespace farmlands::model;
namespace farmlands {
namespace components {
namespace player {
static const float PlayerWalkVelocity = 2.0f; // The default velocity of the player when walking (units/sec).
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.
/**
* 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;
Player::Player()
: selectedItemIndex(-1),
selectedItem(nullptr),
selectedWeapon(nullptr),
itemActionTimeLeft(0),
facingDirection(Direction::South),
walking(false),
running(false),
lookX(0), lookY(0),
hp(100), maxHp(100),
energy(100), maxEnergy(100),
money(0),
m_inventory(nullptr),
m_grid(nullptr),
m_pickables(nullptr)
{
}
Player::~Player()
{
}
Component* Player::clone()
{
Player* clone = new Player();
// Inventory
clone->selectedItemIndex = selectedItemIndex;
clone->itemActionTimeLeft = itemActionTimeLeft;
// Movement
clone->facingDirection = facingDirection;
clone->walking = walking;
clone->running = running;
// The position at which the player is looking
clone->lookX = lookX;
clone->lookY = lookY;
// Health
clone->hp = hp;
clone->energy = energy;
// Cash
clone->money = money;
return clone;
}
void Player::dump(unsigned level)
{
for (unsigned i = 0; i < level; i++)
std::cout<<" ";
std::cout << " .Component: Player\n";
}
void Player::onInitialize()
{
m_inventory = gameObject->component<Inventory>();
auto root = &GameState::current().scene->root;
// Search for the object which has the grid
auto gridIt = root->findByComponent<Grid>();
Assert(gridIt != root->childrenEnd(), "Cannot find grid component.");
m_grid = (*gridIt)->component<Grid>();
// Search for the object named "Pickables"
auto pickablesIt = root->findByName("Pickables");
Assert(pickablesIt != root->childrenEnd(), "Cannot find grid component.");
m_pickables = *pickablesIt;
initializeInventory();
}
bool Player::onEvent(SDL_Event& event)
{
handleInventoryDropEvent(event);
handleInventorySetEvent(event);
handleActionEvents(event);
return false;
}
void Player::onUpdateLogic()
{
itemActionTimeLeft -= GameState::current().deltaTime;
hp = clamp(hp, 0, maxHp);
energy = clamp(energy, 0, maxEnergy);
updateMovement();
updatePickables();
}
void Player::onPreRender()
{
preRenderSelectedItem();
preRenderMovement();
}
void Player::initializeInventory()
{
Assert(m_inventory != nullptr, "Inventory component is missing!");
// Get currently selected item
if (selectedItem >= 0 && selectedItemIndex < m_inventory->capacity())
{
selectedItem = m_inventory->get(static_cast<size_t>(selectedItemIndex));
selectedWeapon = (selectedItem != nullptr) ? selectedItem->component<items::Weapon>() : nullptr;
}
// Set enabled state of inventory items
for (size_t i = 0; i < m_inventory->capacity(); i++)
{
GameObject* obj = m_inventory->get(i);
if (obj)
{
obj->setEnabled(i == static_cast<size_t>(selectedItemIndex));
}
}
}
void Player::handleInventoryDropEvent(SDL_Event& event)
{
bool inventoryDrop = Input::instance().down(GameKey::InventoryDrop, event);
if (inventoryDrop && selectedItemIndex >= 0)
{
// Remove object and add it to "pickables"
m_inventory->remove(static_cast<size_t>(selectedItemIndex));
gameObject->removeChild(selectedItem);
m_pickables->addChild(selectedItem);
// Make pickable
selectedItem->addComponent(new Pickable());
// Set location
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;
selectedItem = nullptr;
selectedWeapon = nullptr;
}
}
void Player::handleInventorySetEvent(SDL_Event& event)
{
// See what key was pressed
int slot = -1;
if (Input::instance().down(GameKey::Inventory1, event)) slot = 0;
if (Input::instance().down(GameKey::Inventory2, event)) slot = 1;
if (Input::instance().down(GameKey::Inventory3, event)) slot = 2;
if (Input::instance().down(GameKey::Inventory4, event)) slot = 3;
if (Input::instance().down(GameKey::Inventory5, event)) slot = 4;
if (Input::instance().down(GameKey::Inventory6, event)) slot = 5;
if (Input::instance().down(GameKey::Inventory7, event)) slot = 6;
if (Input::instance().down(GameKey::Inventory8, event)) slot = 7;
if (Input::instance().down(GameKey::Inventory9, event)) slot = 8;
if (Input::instance().down(GameKey::Inventory10, event)) slot = 9;
if (0 <= slot && slot < m_inventory->capacity())
{
// Disable old object
if (selectedItem != nullptr)
selectedItem->setEnabled(false);
// Obtain new object
selectedItemIndex = slot;
selectedItem = m_inventory->get(slot);
selectedWeapon = (selectedItem != nullptr) ? selectedItem->component<items::Weapon>() : nullptr;
// Enable new object
if (selectedItem)
selectedItem->setEnabled(true);
}
}
void Player::preRenderSelectedItem()
{
// Set item position
if (selectedItem)
{
if (facingDirection == Direction::East)
{
selectedItem->transform.x = 0.2f;
selectedItem->transform.y = -0.8f;
}
else if (facingDirection == Direction::West)
{
selectedItem->transform.x = -0.8f;
selectedItem->transform.y = -0.8f;
}
}
}
void Player::preRenderMovement()
{
// Get sprite
Sprite* sprite = gameObject->component<Sprite>();
// Compute current state
std::string stateName = (walking) ? "Walking " : "Idle ";
switch (facingDirection)
{
case Direction::East:
stateName += "right";
break;
case Direction::West:
stateName += "left";
break;
case Direction::North:
stateName += "up";
break;
case Direction::South:
stateName += "down";
break;
}
sprite->setState(stateName);
// Set animation velocity
float animVelocity = (running) ? 1.0f : 0.7f;
if (itemActionTimeLeft > 0)
animVelocity = 0.1f;
// TODO: move this animation velocity change somewhere else
sprite->animationSpeed = animVelocity;
// Set camera
GameObject* cam = GameState::current().renderContext.cameraObj();
cam->transform.x = gameObject->transform.x;
cam->transform.y = gameObject->transform.y - 1;
}
void Player::updateMovement()
{
running = Input::instance().pressed(GameKey::Run);
walking = false;
// Compute movement velocity
float velMultiplier = PlayerWalkVelocity;
if (itemActionTimeLeft > 0)
velMultiplier = PlayerAttackVelocity;
else if (running)
velMultiplier = PlayerRunVelocity;
velMultiplier *= GameState::current().deltaTime;
// Compute movement positions
float vx = Input::instance().getX() * velMultiplier;
float vy = Input::instance().getY() * velMultiplier;
float newX = gameObject->transform.x + vx;
float newY = gameObject->transform.y + vy;
if ((vx || vy) && canMove(newX, newY))
{
gameObject->transform.x = newX;
gameObject->transform.y = newY;
walking = true;
facingDirection = getDirection(vx, vy);
lookX = newX;
lookY = newY;
move(&lookX, &lookY, facingDirection, PlayerLookDistance);
}
}
Direction Player::getDirection(float vx, float vy)
{
if (vx != 0)
return (vx > 0) ? Direction::East : Direction::West;
return (vy > 0) ? Direction::South : Direction::North;
}
void Player::handleActionEvents(SDL_Event& event)
{
bool action1 = Input::instance().down(GameKey::Action, event);
bool action2 = Input::instance().down(GameKey::Action2, event);
if (action1 && itemActionTimeLeft <= 0)
{
performAction(selectedItem);
}
if (action2 && itemActionTimeLeft <= 0)
{
GameObject* obj = m_grid->get(lookX, lookY);
performAction(obj);
}
}
void Player::performAction(model::GameObject* obj)
{
if (obj == nullptr)
return;
// Call components which implement ITool
for (auto it = obj->componentsBegin(); it != obj->componentsEnd(); it++)
{
IPlayerAction* action = dynamic_cast<IPlayerAction*>(it->second);
if (action != nullptr)
{
float timeCost = 0;
float hpCost = 0;
float energyCost = 0;
action->performAction(lookX, lookY, &timeCost, &hpCost, &energyCost);
itemActionTimeLeft += timeCost;
hp -= hpCost;
energy -= energyCost;
}
}
}
void Player::updatePickables()
{
// Don't do anything if inventory is full
if (m_inventory->emptySlots() <= 0)
return;
std::vector<GameObject*> toPickUp;
for (auto it = m_pickables->childrenBegin(); it != m_pickables->childrenEnd(); it++)
{
GameObject* obj = *it;
Pickable* pickable = obj->component<Pickable>();
if (pickable)
{
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);
}
}
for (auto obj : toPickUp)
{
// Object no longer pickable
obj->destroyComponent<Pickable>();
// 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 */
} /* namespace farmlands */