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:
parent
ddf4bd3585
commit
b8860722fd
@ -47,9 +47,10 @@ struct GameObject
|
||||
|
||||
enum Type
|
||||
{
|
||||
Instance,
|
||||
Character,
|
||||
Instance,
|
||||
Character,
|
||||
Vehicle,
|
||||
Pickup,
|
||||
Unknown
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
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 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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
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/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)
|
||||
|
@ -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
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