mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-22 10:22:52 +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:
parent
ddf4bd3585
commit
b8860722fd
@ -47,9 +47,10 @@ struct GameObject
|
|||||||
|
|
||||||
enum Type
|
enum Type
|
||||||
{
|
{
|
||||||
Instance,
|
Instance,
|
||||||
Character,
|
Character,
|
||||||
Vehicle,
|
Vehicle,
|
||||||
|
Pickup,
|
||||||
Unknown
|
Unknown
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -141,6 +141,10 @@ public:
|
|||||||
void setActiveItem( int slot );
|
void setActiveItem( int slot );
|
||||||
InventoryItem* getActiveItem();
|
InventoryItem* getActiveItem();
|
||||||
void destroyItem( int slot );
|
void destroyItem( int slot );
|
||||||
|
|
||||||
|
void cycleInventory( bool up );
|
||||||
|
|
||||||
|
const std::map<int, InventoryItem*>& getInventory() const { return _inventory; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
22
rwengine/include/objects/ItemPickup.hpp
Normal file
22
rwengine/include/objects/ItemPickup.hpp
Normal 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
|
40
rwengine/include/objects/PickupObject.hpp
Normal file
40
rwengine/include/objects/PickupObject.hpp
Normal 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
|
@ -12,9 +12,12 @@ class Model;
|
|||||||
class ModelFrame;
|
class ModelFrame;
|
||||||
class GameWorld;
|
class GameWorld;
|
||||||
class GameObject;
|
class GameObject;
|
||||||
|
|
||||||
class CharacterObject;
|
class CharacterObject;
|
||||||
class VehicleObject;
|
class VehicleObject;
|
||||||
class InstanceObject;
|
class InstanceObject;
|
||||||
|
class PickupObject;
|
||||||
|
|
||||||
class Animator;
|
class Animator;
|
||||||
class InventoryItem;
|
class InventoryItem;
|
||||||
|
|
||||||
@ -85,6 +88,7 @@ public:
|
|||||||
void renderPedestrian(CharacterObject* pedestrian);
|
void renderPedestrian(CharacterObject* pedestrian);
|
||||||
void renderVehicle(VehicleObject* vehicle);
|
void renderVehicle(VehicleObject* vehicle);
|
||||||
void renderInstance(InstanceObject* instance);
|
void renderInstance(InstanceObject* instance);
|
||||||
|
void renderPickup(PickupObject* pickup);
|
||||||
|
|
||||||
void renderWheel(Model*, const glm::mat4& matrix, const std::string& name);
|
void renderWheel(Model*, const glm::mat4& matrix, const std::string& name);
|
||||||
|
|
||||||
|
@ -80,7 +80,8 @@ void CharacterObject::createActor(const glm::vec3& size)
|
|||||||
physCharacter->setVelocityForTimeInterval(btVector3(1.f, 1.f, 0.f), 1.f);
|
physCharacter->setVelocityForTimeInterval(btVector3(1.f, 1.f, 0.f), 1.f);
|
||||||
physCharacter->setGravity(engine->dynamicsWorld->getGravity().length());
|
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);
|
engine->dynamicsWorld->addAction(physCharacter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -466,3 +467,37 @@ void CharacterObject::destroyItem(int slot)
|
|||||||
_inventory[slot] = nullptr;
|
_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
16
rwengine/src/objects/ItemPickup.cpp
Normal file
16
rwengine/src/objects/ItemPickup.cpp
Normal 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;
|
||||||
|
}
|
78
rwengine/src/objects/PickupObject.cpp
Normal file
78
rwengine/src/objects/PickupObject.cpp
Normal 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;
|
||||||
|
}
|
@ -7,6 +7,8 @@
|
|||||||
#include <objects/CharacterObject.hpp>
|
#include <objects/CharacterObject.hpp>
|
||||||
#include <objects/InstanceObject.hpp>
|
#include <objects/InstanceObject.hpp>
|
||||||
#include <objects/VehicleObject.hpp>
|
#include <objects/VehicleObject.hpp>
|
||||||
|
#include <objects/PickupObject.hpp>
|
||||||
|
|
||||||
#include <ai/CharacterController.hpp>
|
#include <ai/CharacterController.hpp>
|
||||||
#include <data/ObjectData.hpp>
|
#include <data/ObjectData.hpp>
|
||||||
#include <items/InventoryItem.hpp>
|
#include <items/InventoryItem.hpp>
|
||||||
@ -261,6 +263,9 @@ void GameRenderer::renderWorld(float alpha)
|
|||||||
case GameObject::Instance:
|
case GameObject::Instance:
|
||||||
renderInstance(static_cast<InstanceObject*>(object));
|
renderInstance(static_cast<InstanceObject*>(object));
|
||||||
break;
|
break;
|
||||||
|
case GameObject::Pickup:
|
||||||
|
renderPickup(static_cast<PickupObject*>(object));
|
||||||
|
break;
|
||||||
default: 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)
|
void GameRenderer::renderWheel(Model* model, const glm::mat4 &matrix, const std::string& name)
|
||||||
{
|
{
|
||||||
for (const ModelFrame* f : model->frames)
|
for (const ModelFrame* f : model->frames)
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "debugstate.hpp"
|
#include "debugstate.hpp"
|
||||||
#include <objects/CharacterObject.hpp>
|
#include <objects/CharacterObject.hpp>
|
||||||
#include <objects/VehicleObject.hpp>
|
#include <objects/VehicleObject.hpp>
|
||||||
|
#include <objects/ItemPickup.hpp>
|
||||||
#include <render/Model.hpp>
|
#include <render/Model.hpp>
|
||||||
#include <items/WeaponItem.hpp>
|
#include <items/WeaponItem.hpp>
|
||||||
|
|
||||||
@ -15,9 +16,16 @@ IngameState::IngameState()
|
|||||||
|
|
||||||
setPlayerCharacter( _playerCharacter );
|
setPlayerCharacter( _playerCharacter );
|
||||||
|
|
||||||
auto bat = new WeaponItem(getWorld()->gameData.weaponData["ak47"]);
|
/*auto bat = new WeaponItem(getWorld()->gameData.weaponData["ak47"]);
|
||||||
_playerCharacter->addToInventory(bat);
|
_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;
|
float j = 0;
|
||||||
auto carPos = glm::vec3( -1000.f, -1000.f, 12.f );
|
auto carPos = glm::vec3( -1000.f, -1000.f, 12.f );
|
||||||
@ -221,6 +229,9 @@ void IngameState::handleEvent(const sf::Event &event)
|
|||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case sf::Event::MouseWheelMoved:
|
||||||
|
_playerCharacter->cycleInventory(event.mouseWheel.delta > 0);
|
||||||
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
State::handleEvent(event);
|
State::handleEvent(event);
|
||||||
|
94
tests/test_pickup.cpp
Normal file
94
tests/test_pickup.cpp
Normal 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()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user