1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-25 11:52:40 +01:00

Further implement weapons and pickups

Inventory can now by cycled
Weapon Pickups are created infront of the spawn
This commit is contained in:
Daniel Evans 2014-07-04 21:06:56 +01:00
parent ddf4bd3585
commit b8860722fd
11 changed files with 340 additions and 5 deletions

View File

@ -47,9 +47,10 @@ struct GameObject
enum Type
{
Instance,
Character,
Instance,
Character,
Vehicle,
Pickup,
Unknown
};

View File

@ -141,6 +141,10 @@ public:
void setActiveItem( int slot );
InventoryItem* getActiveItem();
void destroyItem( int slot );
void cycleInventory( bool up );
const std::map<int, InventoryItem*>& getInventory() const { return _inventory; }
};
#endif

View File

@ -0,0 +1,22 @@
#pragma once
#ifndef _ITEMPICKUP_HPP_
#define _ITEMPICKUP_HPP_
#include <objects/PickupObject.hpp>
#include <glm/glm.hpp>
#include <data/WeaponData.hpp>
/**
* @brief The ItemPickup class
* Inserts an item into a characters inventory on pickup.
*/
class ItemPickup : public PickupObject
{
std::shared_ptr<WeaponData> _data;
public:
ItemPickup(GameWorld* world, const glm::vec3& position, std::shared_ptr<WeaponData> weapon);
bool onCharacterTouch(CharacterObject* character);
};
#endif

View File

@ -0,0 +1,40 @@
#pragma once
#ifndef _PICKUPOBJECT_HPP_
#define _PICKUPOBJECT_HPP_
#include <engine/GameObject.hpp>
#include <bullet/btBulletCollisionCommon.h>
#include <BulletCollision/CollisionDispatch/btGhostObject.h>
#include <glm/glm.hpp>
class CharacterObject;
/**
* @brief The PickupObject class
* Implements interface and base behaviour for pickups
*/
class PickupObject : public GameObject
{
btPairCachingGhostObject* _ghost;
btSphereShape* _shape;
bool _enabled;
float _enableTimer;
int _modelID;
public:
PickupObject(GameWorld* world, const glm::vec3& position, int modelID);
~PickupObject();
int getModelID() const { return _modelID; }
Type type() { return Pickup; }
void tick(float dt);
virtual bool onCharacterTouch(CharacterObject* character) = 0;
bool isEnabled() const { return _enabled; }
void setEnabled(bool enabled);
};
#endif

View File

@ -12,9 +12,12 @@ class Model;
class ModelFrame;
class GameWorld;
class GameObject;
class CharacterObject;
class VehicleObject;
class InstanceObject;
class PickupObject;
class Animator;
class InventoryItem;
@ -85,6 +88,7 @@ public:
void renderPedestrian(CharacterObject* pedestrian);
void renderVehicle(VehicleObject* vehicle);
void renderInstance(InstanceObject* instance);
void renderPickup(PickupObject* pickup);
void renderWheel(Model*, const glm::mat4& matrix, const std::string& name);

View File

@ -80,7 +80,8 @@ void CharacterObject::createActor(const glm::vec3& size)
physCharacter->setVelocityForTimeInterval(btVector3(1.f, 1.f, 0.f), 1.f);
physCharacter->setGravity(engine->dynamicsWorld->getGravity().length());
engine->dynamicsWorld->addCollisionObject(physObject, btBroadphaseProxy::KinematicFilter, btBroadphaseProxy::StaticFilter);
engine->dynamicsWorld->addCollisionObject(physObject, btBroadphaseProxy::KinematicFilter,
btBroadphaseProxy::StaticFilter|btBroadphaseProxy::SensorTrigger);
engine->dynamicsWorld->addAction(physCharacter);
}
}
@ -466,3 +467,37 @@ void CharacterObject::destroyItem(int slot)
_inventory[slot] = nullptr;
}
void CharacterObject::cycleInventory(bool up)
{
if( up ) {
for(auto it = _inventory.begin(); it != _inventory.end(); ++it) {
if( it->first < 0 ) continue;
if( it->first > _activeInventoryItem ) {
setActiveItem(it->first);
return;
}
}
// if there's no higher slot, set the first item.
auto next = _inventory.lower_bound(0);
if( next != _inventory.end() ) {
setActiveItem(next->first);
}
}
else {
for(auto it = _inventory.rbegin(); it != _inventory.rend(); ++it) {
if( it->first < 0 ) break;
if( it->first < _activeInventoryItem ) {
setActiveItem(it->first);
return;
}
}
// if there's no lower slot, set the last item.
auto next = _inventory.rbegin();
if( next != _inventory.rend() ) {
setActiveItem(next->first);
}
}
}

View File

@ -0,0 +1,16 @@
#include <objects/ItemPickup.hpp>
#include <objects/CharacterObject.hpp>
#include <engine/GameWorld.hpp>
#include <items/WeaponItem.hpp>
ItemPickup::ItemPickup(GameWorld *world, const glm::vec3 &position, std::shared_ptr<WeaponData> weapon)
: PickupObject(world, position, weapon->modelID), _data(weapon)
{
}
bool ItemPickup::onCharacterTouch(CharacterObject *character)
{
character->addToInventory(new WeaponItem(_data));
return true;
}

View File

@ -0,0 +1,78 @@
#include <objects/PickupObject.hpp>
#include <objects/CharacterObject.hpp>
#include <engine/GameWorld.hpp>
PickupObject::PickupObject(GameWorld *world, const glm::vec3 &position, int modelID)
: GameObject(world, position, glm::quat(), nullptr),
_ghost(nullptr), _shape(nullptr), _enabled(false), _modelID(modelID)
{
btTransform tf;
tf.setIdentity();
tf.setOrigin(btVector3(position.x, position.y, position.z));
_ghost = new btPairCachingGhostObject;
_ghost->setUserPointer(this);
_ghost->setWorldTransform(tf);
_shape = new btSphereShape(0.5f);
_ghost->setCollisionShape(_shape);
_ghost->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT|btCollisionObject::CF_NO_CONTACT_RESPONSE);
setEnabled(true);
}
PickupObject::~PickupObject()
{
if(_ghost) {
setEnabled(false);
delete _ghost;
delete _shape;
}
}
void PickupObject::tick(float dt)
{
if(! _enabled) {
_enableTimer -= dt;
if( _enableTimer <= 0.f ) {
setEnabled(true);
}
}
if(_enabled) {
// Sort out interactions with things that may or may not be players.
btManifoldArray manifoldArray;
btBroadphasePairArray& pairArray = _ghost->getOverlappingPairCache()->getOverlappingPairArray();
int numPairs = pairArray.size();
for (int i=0;i<numPairs;i++)
{
manifoldArray.clear();
const btBroadphasePair& pair = pairArray[i];
auto otherObject = static_cast<const btCollisionObject*>(
pair.m_pProxy0->m_clientObject == _ghost ? pair.m_pProxy1->m_clientObject : pair.m_pProxy0->m_clientObject);
if(otherObject->getUserPointer()) {
GameObject* object = static_cast<GameObject*>(otherObject->getUserPointer());
if(object->type() == Character) {
CharacterObject* character = static_cast<CharacterObject*>(object);
setEnabled( !onCharacterTouch(character) );
if( ! _enabled ) {
_enableTimer = 60.f;
}
}
}
}
}
}
void PickupObject::setEnabled(bool enabled)
{
if( ! _enabled && enabled ) {
engine->dynamicsWorld->addCollisionObject(_ghost, btBroadphaseProxy::SensorTrigger);
}
else if( _enabled && ! enabled ) {
engine->dynamicsWorld->removeCollisionObject(_ghost);
}
_enabled = enabled;
}

View File

@ -7,6 +7,8 @@
#include <objects/CharacterObject.hpp>
#include <objects/InstanceObject.hpp>
#include <objects/VehicleObject.hpp>
#include <objects/PickupObject.hpp>
#include <ai/CharacterController.hpp>
#include <data/ObjectData.hpp>
#include <items/InventoryItem.hpp>
@ -261,6 +263,9 @@ void GameRenderer::renderWorld(float alpha)
case GameObject::Instance:
renderInstance(static_cast<InstanceObject*>(object));
break;
case GameObject::Pickup:
renderPickup(static_cast<PickupObject*>(object));
break;
default: break;
}
}
@ -526,6 +531,31 @@ void GameRenderer::renderInstance(InstanceObject *instance)
}
}
void GameRenderer::renderPickup(PickupObject *pickup)
{
if( ! pickup->isEnabled() ) return;
glm::mat4 modelMatrix = glm::translate(glm::mat4(), pickup->getPosition());
modelMatrix = glm::rotate(modelMatrix, engine->gameTime, glm::vec3(0.f, 0.f, 1.f));
std::shared_ptr<ObjectData> odata = engine->objectTypes[pickup->getModelID()];
auto weapons = engine->gameData.models["weapons"];
if( weapons && weapons->model ) {
auto itemModel = weapons->model->findFrame(odata->modelName + "_l0");
auto matrix = glm::inverse(itemModel->getTransform());
if(itemModel) {
renderFrame(weapons->model, itemModel, modelMatrix * matrix, nullptr);
}
else {
std::cerr << "weapons.dff missing frame " << odata->modelName << std::endl;
}
}
else {
std::cerr << "weapons.dff not loaded" << std::endl;
}
}
void GameRenderer::renderWheel(Model* model, const glm::mat4 &matrix, const std::string& name)
{
for (const ModelFrame* f : model->frames)

View File

@ -4,6 +4,7 @@
#include "debugstate.hpp"
#include <objects/CharacterObject.hpp>
#include <objects/VehicleObject.hpp>
#include <objects/ItemPickup.hpp>
#include <render/Model.hpp>
#include <items/WeaponItem.hpp>
@ -15,9 +16,16 @@ IngameState::IngameState()
setPlayerCharacter( _playerCharacter );
auto bat = new WeaponItem(getWorld()->gameData.weaponData["ak47"]);
/*auto bat = new WeaponItem(getWorld()->gameData.weaponData["ak47"]);
_playerCharacter->addToInventory(bat);
_playerCharacter->setActiveItem(bat->getInventorySlot());
_playerCharacter->setActiveItem(bat->getInventorySlot());*/
getWorld()->objects.insert(new ItemPickup(getWorld(), {-1000.f, -980.f, 11.5f},
getWorld()->gameData.weaponData["ak47"]));
getWorld()->objects.insert(new ItemPickup(getWorld(), {-1002.f, -980.f, 11.5f},
getWorld()->gameData.weaponData["colt45"]));
getWorld()->objects.insert(new ItemPickup(getWorld(), {-1004.f, -980.f, 11.5f},
getWorld()->gameData.weaponData["shotgun"]));
float j = 0;
auto carPos = glm::vec3( -1000.f, -1000.f, 12.f );
@ -221,6 +229,9 @@ void IngameState::handleEvent(const sf::Event &event)
default: break;
}
break;
case sf::Event::MouseWheelMoved:
_playerCharacter->cycleInventory(event.mouseWheel.delta > 0);
break;
default: break;
}
State::handleEvent(event);

94
tests/test_pickup.cpp Normal file
View File

@ -0,0 +1,94 @@
#include <boost/test/unit_test.hpp>
#include <objects/PickupObject.hpp>
#include <objects/ItemPickup.hpp>
#include <items/InventoryItem.hpp>
#include <data/WeaponData.hpp>
#include <objects/CharacterObject.hpp>
#include "test_globals.hpp"
class TestPickup : public PickupObject
{
public:
bool picked_up = false;
TestPickup(GameWorld* engine, const glm::vec3& position)
: PickupObject(engine, position, 0)
{}
bool onCharacterTouch(CharacterObject *character) {
picked_up = true;
return true;
}
};
BOOST_AUTO_TEST_SUITE(PickupTests)
BOOST_AUTO_TEST_CASE(test_pickup_interaction)
{
{
auto character = Global::get().e->createPedestrian(1, { 30.1f, 0.f, 0.f });
BOOST_REQUIRE( character != nullptr );
TestPickup* p = new TestPickup(Global::get().e, { 30.f, 0.f, 0.f } );
Global::get().e->objects.insert(p);
BOOST_CHECK( ! p->picked_up );
Global::get().e->dynamicsWorld->stepSimulation(0.016f);
p->tick(0.f);
BOOST_CHECK( p->picked_up );
p->picked_up = false;
BOOST_CHECK( ! p->picked_up );
Global::get().e->dynamicsWorld->stepSimulation(0.016f);
p->tick(0.f);
BOOST_CHECK( ! p->picked_up );
Global::get().e->dynamicsWorld->stepSimulation(0.016f);
p->tick(60.5f);
BOOST_CHECK( p->picked_up );
Global::get().e->destroyObject(p);
Global::get().e->destroyObject(character);
}
}
BOOST_AUTO_TEST_CASE(test_item_pickup)
{
{
auto character = Global::get().e->createPedestrian(1, { 30.1f, 0.f, 0.f });
BOOST_REQUIRE( character != nullptr );
auto item = Global::get().e->gameData.weaponData["ak47"];
ItemPickup* p = new ItemPickup(Global::get().e, { 30.f, 0.f, 0.f }, item );
Global::get().e->objects.insert(p);
// Check the characters inventory is empty.
BOOST_CHECK( character->getInventory().empty() );
Global::get().e->dynamicsWorld->stepSimulation(0.016f);
p->tick(0.f);
BOOST_CHECK( ! character->getInventory().empty() );
auto inventory = character->getInventory();
BOOST_CHECK( std::any_of( inventory.begin(), inventory.end(),
[&](const std::pair<int, InventoryItem*>& i)
{ return i.second->getModelID() == item->modelID; }) );
Global::get().e->destroyObject(p);
Global::get().e->destroyObject(character);
}
}
BOOST_AUTO_TEST_SUITE_END()