405 lines
9.6 KiB
C++
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 */
|