Allow to pick up dropped items.

This commit is contained in:
Felix Ableitner 2013-07-21 10:52:50 +02:00
parent 9401ea53c1
commit 19de12d62a
8 changed files with 79 additions and 15 deletions

View file

@ -173,7 +173,7 @@ Game::keyUp(const sf::Event& event) {
mPlayer->setDirection(Player::Direction::RIGHT, true); mPlayer->setDirection(Player::Direction::RIGHT, true);
break; break;
case sf::Keyboard::F: case sf::Keyboard::F:
mPlayer->swapGadgets(); mPlayer->pickUpItem();
break; break;
default: default:
break; break;

View file

@ -41,13 +41,20 @@ World::insertCharacter(std::shared_ptr<Character> character) {
insert(character); insert(character);
} }
void
World::remove(std::shared_ptr<Sprite> drawable) {
Sprite::Category cat = drawable->getCategory();
auto item = std::find(mDrawables[cat].begin(), mDrawables[cat].end(), drawable);
mDrawables[cat].erase(item);
}
/** /**
* Returns all characters that are within maxDistance from position. * Returns all characters that are within maxDistance from position.
*/ */
std::vector<std::shared_ptr<Character> > std::vector<std::shared_ptr<Character> >
World::getCharacters(const sf::Vector2f& position, float maxDistance) const { World::getCharacters(const sf::Vector2f& position, float maxDistance) const {
std::vector<std::shared_ptr<Character> > visible; std::vector<std::shared_ptr<Character> > visible;
for (auto it : mCharacters) { for (const auto& it : mCharacters) {
if (position == it->getPosition()) if (position == it->getPosition())
continue; continue;
if (thor::squaredLength(position - it->getPosition()) <= if (thor::squaredLength(position - it->getPosition()) <=
@ -88,7 +95,7 @@ void
World::applyMovement(std::shared_ptr<Sprite> sprite, int elapsed) { World::applyMovement(std::shared_ptr<Sprite> sprite, int elapsed) {
sf::Vector2f offset = sprite->getSpeed() * (elapsed / 1000.0f); sf::Vector2f offset = sprite->getSpeed() * (elapsed / 1000.0f);
for (auto w = mDrawables.begin(); w != mDrawables.end(); w++) { for (auto w = mDrawables.begin(); w != mDrawables.end(); w++) {
for (auto& other : w->second) { for (const auto& other : w->second) {
if (sprite == other) if (sprite == other)
continue; continue;
// Ignore anything that is filtered by masks. // Ignore anything that is filtered by masks.
@ -135,7 +142,7 @@ World::draw(sf::RenderTarget& target, sf::RenderStates states) const {
screen.left += target.getView().getCenter().x - target.getView().getSize().x / 2; screen.left += target.getView().getCenter().x - target.getView().getSize().x / 2;
screen.top += target.getView().getCenter().y - target.getView().getSize().y / 2; screen.top += target.getView().getCenter().y - target.getView().getSize().y / 2;
for (auto v = mDrawables.begin(); v != mDrawables.end(); v++) { for (auto v = mDrawables.begin(); v != mDrawables.end(); v++) {
for (auto item : v->second) { for (const auto& item : v->second) {
if (item->isInside(screen)) if (item->isInside(screen))
target.draw(static_cast<sf::Drawable&>(*item), states); target.draw(static_cast<sf::Drawable&>(*item), states);
} }
@ -184,3 +191,21 @@ World::raycast(const sf::Vector2f& lineStart,
} }
return true; return true;
} }
/**
* Returns the item closest to position after linear search.
*/
std::shared_ptr<Item>
World::getNearestItem(const sf::Vector2f& position) const {
std::shared_ptr<Item> closest;
float distance = std::numeric_limits<float>::max();
for (const auto& v : mDrawables) {
for (const auto& d : v.second) {
std::shared_ptr<Item> converted = std::dynamic_pointer_cast<Item>(d);
if (converted.get() != nullptr &&
thor::squaredLength(converted->getPosition() - position) < distance)
closest = converted;
}
}
return closest;
}

View file

@ -24,12 +24,14 @@ class World : public sf::Drawable {
public: public:
void insert(std::shared_ptr<Sprite> drawable); void insert(std::shared_ptr<Sprite> drawable);
void insertCharacter(std::shared_ptr<Character> character); void insertCharacter(std::shared_ptr<Character> character);
void remove(std::shared_ptr<Sprite> drawable);
void step(int elapsed); void step(int elapsed);
void think(int elapsed); void think(int elapsed);
std::vector<std::shared_ptr<Character> > std::vector<std::shared_ptr<Character> >
getCharacters(const sf::Vector2f& position, float maxDistance) const; getCharacters(const sf::Vector2f& position, float maxDistance) const;
bool raycast(const sf::Vector2f& lineStart, bool raycast(const sf::Vector2f& lineStart,
const sf::Vector2f& lineEnd) const; const sf::Vector2f& lineEnd) const;
std::shared_ptr<Item> getNearestItem(const sf::Vector2f& position) const;
private: private:

View file

@ -18,6 +18,7 @@
const float Character::VISION_DISTANCE = 500.0f; const float Character::VISION_DISTANCE = 500.0f;
const float Character::POINT_REACHED_DISTANCE = 1.0f; const float Character::POINT_REACHED_DISTANCE = 1.0f;
const float Character::ITEM_PICKUP_MAX_DISTANCE_SQUARED = 2500.0f;
/** /**
* Saves pointer to this instance in static var for think(). * Saves pointer to this instance in static var for think().
@ -259,7 +260,34 @@ Character::getRightGadgetName() const {
return mRightGadget->getName(); return mRightGadget->getName();
} }
/**
* Picks up the nearest weapon or gadget, and drops the active weapon (or right gadget).
*
* If no item is nearby, gadgets are swapped instead.
*/
void void
Character::swapGadgets() { Character::pickUpItem() {
std::swap(mLeftGadget, mRightGadget); std::shared_ptr<Item> item = mWorld.getNearestItem(getPosition());
if (item != nullptr && thor::squaredLength(item->getPosition() - getPosition()) <=
ITEM_PICKUP_MAX_DISTANCE_SQUARED) {
std::shared_ptr<Weapon> weapon = std::dynamic_pointer_cast<Weapon>(item);
if (weapon != nullptr) {
mWorld.insert(mActiveWeapon);
mActiveWeapon->drop(getPosition());
mActiveWeapon = weapon;
mActiveWeapon->setHolder(*this);
}
std::shared_ptr<Gadget> gadget = std::dynamic_pointer_cast<Gadget>(item);
if (gadget != nullptr) {
mWorld.insert(mRightGadget);
mRightGadget->drop(getPosition());
mRightGadget = mLeftGadget;
mLeftGadget = gadget;
}
// TODO: implement (or just make invisible?)
mWorld.remove(item);
}
else {
std::swap(mLeftGadget, mRightGadget);
}
} }

View file

@ -63,7 +63,7 @@ protected:
void useRightGadget(); void useRightGadget();
std::string getLeftGadgetName() const; std::string getLeftGadgetName() const;
std::string getRightGadgetName() const; std::string getRightGadgetName() const;
void swapGadgets(); void pickUpItem();
protected: protected:
@ -76,6 +76,8 @@ private:
/// Distance to a path point where it will be considered as reached (to /// Distance to a path point where it will be considered as reached (to
/// avoid floating point equality check). /// avoid floating point equality check).
static const float POINT_REACHED_DISTANCE; static const float POINT_REACHED_DISTANCE;
/// Maximum distance from character where an item can be picked up.
static const float ITEM_PICKUP_MAX_DISTANCE_SQUARED;
friend class World; friend class World;
World& mWorld; World& mWorld;
Pathfinder& mPathfinder; Pathfinder& mPathfinder;

View file

@ -16,7 +16,7 @@
Weapon::Weapon(World& world, Character& holder, const Yaml& config) : Weapon::Weapon(World& world, Character& holder, const Yaml& config) :
Item(sf::Vector2f(80, 30), "weapon.png"), Item(sf::Vector2f(80, 30), "weapon.png"),
mWorld(world), mWorld(world),
mHolder(holder), mHolder(&holder),
mName(config.get("name", std::string())), mName(config.get("name", std::string())),
mProjectile(config.get("bullet", std::string("bullet.yaml"))), mProjectile(config.get("bullet", std::string("bullet.yaml"))),
mDamage(config.get("damage", 0)), mDamage(config.get("damage", 0)),
@ -142,6 +142,11 @@ Weapon::cancelReload() {
mTimer.restart(sf::milliseconds(mFireInterval)); mTimer.restart(sf::milliseconds(mFireInterval));
} }
void
Weapon::setHolder(Character& holder) {
mHolder = &holder;
}
/** /**
* Creates a new projectile and inserts it into the world. * Creates a new projectile and inserts it into the world.
* *
@ -149,18 +154,18 @@ Weapon::cancelReload() {
*/ */
void void
Weapon::insertProjectile(float angle) { Weapon::insertProjectile(float angle) {
sf::Vector2f offset(mHolder.getDirection() * mHolder.getRadius()); sf::Vector2f offset(mHolder->getDirection() * mHolder->getRadius());
float spread = (mHolder.getSpeed() == sf::Vector2f()) float spread = (mHolder->getSpeed() == sf::Vector2f())
? mSpread ? mSpread
: mSpreadMoving; : mSpreadMoving;
std::uniform_real_distribution<float> distribution(- spread, spread); std::uniform_real_distribution<float> distribution(- spread, spread);
angle += distribution(mGenerator) + 90.0f; angle += distribution(mGenerator) + 90.0f;
sf::Vector2f direction(thor::rotatedVector(mHolder.getDirection(), angle)); sf::Vector2f direction(thor::rotatedVector(mHolder->getDirection(), angle));
std::shared_ptr<Sprite> projectile(new Bullet(mHolder.getPosition() + offset, std::shared_ptr<Sprite> projectile(new Bullet(mHolder->getPosition() + offset,
mHolder, direction, mProjectile, mProjectileSpeed, *mHolder, direction, mProjectile, mProjectileSpeed,
mDamage, mMaxRange)); mDamage, mMaxRange));
mWorld.insert(projectile); mWorld.insert(projectile);
} }

View file

@ -35,6 +35,7 @@ public:
std::string getName() const; std::string getName() const;
void reload(); void reload();
void cancelReload(); void cancelReload();
void setHolder(Character& holder);
private: private:
void fire(); void fire();
@ -42,7 +43,8 @@ private:
private: private:
World& mWorld; World& mWorld;
Character& mHolder; /// Non-owning pointer instead of reference to allow reassigning.
Character* mHolder;
thor::Timer mTimer; thor::Timer mTimer;
const std::string mName; const std::string mName;

View file

@ -50,7 +50,7 @@ public:
using Character::getHealth; using Character::getHealth;
using Character::getLeftGadgetName; using Character::getLeftGadgetName;
using Character::getRightGadgetName; using Character::getRightGadgetName;
using Character::swapGadgets; using Character::pickUpItem;
private: private:
void onThink(int elapsed) override; void onThink(int elapsed) override;