1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-03 17:19:46 +02:00

Remove InventoryItem and WeaponItem

They served no purpose other than to awkwardly implement weapon firing
This is now handled in the Weapon::fire* functions, and everything else
has been changed to reference weapon data or inventory indices directly
This commit is contained in:
Daniel Evans 2016-10-19 02:22:33 +01:00
parent 44f73af9e9
commit 5c78930c1b
26 changed files with 254 additions and 519 deletions

View File

@ -58,9 +58,8 @@ set(RWENGINE_SOURCES
src/engine/SaveGame.hpp
src/engine/ScreenText.cpp
src/engine/ScreenText.hpp
src/items/InventoryItem.hpp
src/items/WeaponItem.cpp
src/items/WeaponItem.hpp
src/items/Weapon.cpp
src/items/Weapon.hpp
src/loaders/DataLoader.cpp
src/loaders/DataLoader.hpp
src/loaders/GenericDATLoader.cpp

View File

@ -1,11 +1,12 @@
#include <btBulletDynamicsCommon.h>
#include <ai/CharacterController.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/VehicleObject.hpp>
#include <data/Model.hpp>
#include <engine/Animator.hpp>
#include <items/WeaponItem.hpp>
#include <engine/GameData.hpp>
#include <engine/GameWorld.hpp>
#include <items/Weapon.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/VehicleObject.hpp>
#include <rw/defines.hpp>
constexpr float kCloseDoorIdleTime = 2.f;
@ -384,14 +385,20 @@ bool Activities::ExitVehicle::update(CharacterObject *character,
return false;
}
#include <data/Model.hpp>
#include <engine/GameData.hpp>
#include <engine/GameWorld.hpp>
bool Activities::ShootWeapon::update(CharacterObject *character,
CharacterController *controller) {
bool Activities::UseItem::update(CharacterObject *character,
CharacterController *controller) {
RW_UNUSED(controller);
auto &wepdata = _item->getWeaponData();
if (itemslot >= kMaxInventorySlots) {
return true;
}
auto world = character->engine;
const auto &weapon = world->data->weaponData.at(itemslot);
auto &state = character->getCurrentState().weapons[itemslot];
auto animator = character->animator;
auto shootanim = world->data->animations[weapon->animation1];
auto throwanim = world->data->animations[weapon->animation2];
// Instant hit weapons loop their anim
// Thrown projectiles have lob / throw.
@ -400,85 +407,73 @@ bool Activities::ShootWeapon::update(CharacterObject *character,
character->setRotation(
glm::angleAxis(character->getLook().x, glm::vec3{0.f, 0.f, 1.f}));
RW_CHECK(wepdata->inventorySlot < maxInventorySlots,
"Inventory slot out of bounds");
auto &itemState =
character->getCurrentState().weapons[wepdata->inventorySlot];
if (itemState.bulletsClip == 0 && itemState.bulletsTotal > 0) {
itemState.bulletsClip +=
std::min(int(itemState.bulletsTotal), wepdata->clipSize);
itemState.bulletsTotal -= itemState.bulletsClip;
if (state.bulletsClip == 0 && state.bulletsTotal > 0) {
state.bulletsClip +=
std::min(int(state.bulletsTotal), weapon->clipSize);
state.bulletsTotal -= state.bulletsClip;
}
bool hasammo = itemState.bulletsClip > 0;
bool hasammo = state.bulletsClip > 0;
if (wepdata->fireType == WeaponData::INSTANT_HIT) {
if (_item->isFiring(character) && hasammo) {
auto shootanim =
character->engine->data->animations[wepdata->animation1];
if (shootanim) {
if (character->animator->getAnimation(AnimIndexAction) !=
shootanim) {
character->playActivityAnimation(shootanim, false, false);
}
auto loopstart = wepdata->animLoopStart / 100.f;
auto loopend = wepdata->animLoopEnd / 100.f;
auto firetime = wepdata->animFirePoint / 100.f;
auto currID =
character->animator->getAnimationTime(AnimIndexAction);
if (currID >= firetime && !_fired) {
itemState.bulletsClip--;
_item->fire(character);
_fired = true;
}
if (currID > loopend) {
character->animator->setAnimationTime(AnimIndexAction,
loopstart);
_fired = false;
}
if (weapon->fireType == WeaponData::INSTANT_HIT) {
if (!character->getCurrentState().primaryActive) {
// Character is no longer firing
return true;
}
if (hasammo && shootanim) {
if (animator->getAnimation(AnimIndexAction) != shootanim) {
character->playActivityAnimation(shootanim, false, false);
}
} else {
if (character->animator->isCompleted(AnimIndexAction)) {
return true;
auto loopstart = weapon->animLoopStart / 100.f;
auto loopend = weapon->animLoopEnd / 100.f;
auto firetime = weapon->animFirePoint / 100.f;
auto currenttime = animator->getAnimationTime(AnimIndexAction);
if (currenttime >= firetime && !fired) {
state.bulletsClip--;
Weapon::fireHitscan(weapon.get(), character);
fired = true;
}
if (currenttime > loopend) {
animator->setAnimationTime(AnimIndexAction, loopstart);
fired = false;
}
} else if (animator->isCompleted(AnimIndexAction)) {
// Should we exit this state when out of ammo?
return true;
}
}
/// @todo Use Thrown flag instead of project (RPG isn't thrown eg.)
else if (wepdata->fireType == WeaponData::PROJECTILE && hasammo) {
auto shootanim =
character->engine->data->animations[wepdata->animation1];
auto throwanim =
character->engine->data->animations[wepdata->animation2];
if (character->animator->getAnimation(AnimIndexAction) == shootanim) {
if (character->animator->isCompleted(AnimIndexAction)) {
else if (weapon->fireType == WeaponData::PROJECTILE && hasammo) {
if (animator->getAnimation(AnimIndexAction) == shootanim) {
if (character->getCurrentState().primaryActive) {
power = animator->getAnimationTime(AnimIndexAction) / 0.5f;
}
if (animator->isCompleted(AnimIndexAction)) {
character->playActivityAnimation(throwanim, false, false);
}
} else if (character->animator->getAnimation(AnimIndexAction) ==
throwanim) {
auto firetime = wepdata->animCrouchFirePoint / 100.f;
auto currID =
character->animator->getAnimationTime(AnimIndexAction);
} else if (animator->getAnimation(AnimIndexAction) == throwanim) {
auto firetime = weapon->animCrouchFirePoint / 100.f;
auto currID = animator->getAnimationTime(AnimIndexAction);
if (currID >= firetime && !_fired) {
itemState.bulletsClip--;
_item->fire(character);
_fired = true;
if (currID >= firetime && !fired) {
state.bulletsClip--;
Weapon::fireProjectile(weapon.get(), character, power);
fired = true;
}
if (character->animator->isCompleted(AnimIndexAction)) {
if (animator->isCompleted(AnimIndexAction)) {
return true;
}
} else {
character->playActivityAnimation(throwanim, false, true);
character->playActivityAnimation(shootanim, false, true);
}
} else if (wepdata->fireType == WeaponData::MELEE) {
RW_CHECK(wepdata->fireType != WeaponData::MELEE,
} else if (weapon->fireType == WeaponData::MELEE) {
RW_CHECK(weapon->fireType != WeaponData::MELEE,
"Melee attacks not implemented");
return true;
} else {
RW_ERROR("Unrecognized fireType: " << wepdata->fireType);
RW_ERROR("Unrecognized fireType: " << weapon->fireType);
return true;
}

View File

@ -148,7 +148,6 @@ public:
}
// TODO: Refactor this with an ugly macro to reduce code dup.
class WeaponItem;
/**
* @brief Activities for CharacterController behaviour
@ -217,13 +216,14 @@ struct ExitVehicle : public CharacterController::Activity {
bool update(CharacterObject* character, CharacterController* controller);
};
struct ShootWeapon : public CharacterController::Activity {
DECL_ACTIVITY(ShootWeapon)
struct UseItem : public CharacterController::Activity {
DECL_ACTIVITY(UseItem)
WeaponItem* _item;
bool _fired;
int itemslot;
bool fired = false;
float power = 0.f;
ShootWeapon(WeaponItem* item) : _item(item), _fired(false) {
UseItem(int slot) : itemslot(slot) {
}
bool update(CharacterObject* character, CharacterController* controller);

View File

@ -9,7 +9,6 @@
#include <ai/TrafficDirector.hpp>
#include <data/Model.hpp>
#include <data/WeaponData.hpp>
#include <items/WeaponItem.hpp>
#include <job/WorkContext.hpp>
#include <loaders/LoaderIDE.hpp>
#include <loaders/LoaderIPL.hpp>
@ -99,11 +98,6 @@ GameWorld::GameWorld(Logger* log, WorkContext* work, GameData* dat)
new btGhostPairCallback());
gContactProcessedCallback = ContactProcessedCallback;
dynamicsWorld->setInternalTickCallback(PhysicsTickCallback, this);
// Populate inventory items
for (auto& w : data->weaponData) {
inventoryItems.push_back(new WeaponItem(inventoryItems.size(), w));
}
}
GameWorld::~GameWorld() {
@ -433,14 +427,13 @@ PickupObject* GameWorld::createPickup(const glm::vec3& pos, int id, int type) {
PickupObject* pickup = nullptr;
auto pickuptype = (PickupObject::PickupType)type;
// Attempt to find an InventoryItem associated with this model
auto it = std::find_if(
inventoryItems.begin(), inventoryItems.end(),
[=](InventoryItem* itm) { return itm->getModelID() == id; });
data->weaponData.begin(), data->weaponData.end(),
[=](const std::shared_ptr<WeaponData>& x) { return x->modelID == id; });
// If nothing, create a generic pickup instead of an item pickup
if (it != inventoryItems.end()) {
pickup = new ItemPickup(this, pos, modelInfo, pickuptype, *it);
if (it != data->weaponData.end()) {
pickup = new ItemPickup(this, pos, modelInfo, pickuptype, it->get());
} else {
RW_UNIMPLEMENTED("Non-weapon pickups");
pickup = new PickupObject(this, pos, modelInfo, pickuptype);
@ -640,14 +633,6 @@ float GameWorld::getGameTime() const {
return state->gameTime;
}
InventoryItem* GameWorld::getInventoryItem(uint16_t weaponId) const {
RW_CHECK(weaponId < inventoryItems.size(), "InventoryItem ID out of range");
if (weaponId >= inventoryItems.size()) {
return nullptr;
}
return inventoryItems[weaponId];
}
void handleVehicleResponse(GameObject* object, btManifoldPoint& mp, bool isA) {
bool isVehicle = object->type() == GameObject::Vehicle;
if (!isVehicle) return;

View File

@ -27,7 +27,6 @@ class ViewCamera;
#include <render/VisualFX.hpp>
struct BlipData;
class InventoryItem;
struct WeaponScan;
struct VehicleGenerator;
@ -179,13 +178,6 @@ public:
float getGameTime() const;
/**
* @brief getInventoryItem
* @param weaponId The Weapon ID (inventory slot) of the weapon to fetch
* @return Instance of the weapon
*/
InventoryItem* getInventoryItem(uint16_t weaponId) const;
/**
* Game data
*/
@ -353,11 +345,6 @@ private:
std::vector<AreaIndicatorInfo> areaIndicators;
/**
* Inventory Item instances
*/
std::vector<InventoryItem*> inventoryItems;
/**
* Flag for pausing the simulation
*/

View File

@ -4,7 +4,6 @@
#include <engine/GameState.hpp>
#include <engine/GameWorld.hpp>
#include <engine/SaveGame.hpp>
#include <items/WeaponItem.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/GameObject.hpp>
#include <objects/InstanceObject.hpp>

View File

@ -1,66 +0,0 @@
#pragma once
#ifndef _INVENTORYITEM_HPP_
#define _INVENTORYITEM_HPP_
class GameObject;
class CharacterObject;
/**
* @brief The InventoryItem class
*
* Instanciated once per item type
*/
class InventoryItem {
int _itemID;
int _inventorySlot;
int _modelID;
protected:
InventoryItem(int itemID, int invSlot, int model)
: _itemID(itemID), _inventorySlot(invSlot), _modelID(model) {
}
public:
virtual ~InventoryItem() {
}
/**
* @brief getObject
* @return The ID of the model associated with the item.
*/
int getModelID() {
return _modelID;
}
/**
* @brief getItemID
* @return The index of this item in the item list
*/
int getItemID() {
return _itemID;
}
/**
* @brief getInventorySlot
* @return The inventory slot number for this item
*/
int getInventorySlot() const {
return _inventorySlot;
}
/**
* @brief primary Implements mouse 1 action
* @param owner The character using this item
*/
virtual void primary(CharacterObject* owner) = 0;
/**
* @see primary
* @param owner The character using this item
*/
virtual void secondary(CharacterObject* owner) = 0;
constexpr static int NO_INVSLOT = -1;
};
#endif

View File

@ -0,0 +1,48 @@
#include <data/Skeleton.hpp>
#include <engine/GameWorld.hpp>
#include <items/Weapon.hpp>
#include <objects/ProjectileObject.hpp>
void Weapon::fireHitscan(WeaponData* weapon, CharacterObject* owner) {
auto handFrame = owner->getModel()->findFrame("srhand");
glm::mat4 handMatrix;
if (handFrame) {
while (handFrame->getParent()) {
handMatrix =
owner->skeleton->getMatrix(handFrame->getIndex()) * handMatrix;
handFrame = handFrame->getParent();
}
}
auto farTarget =
owner->getPosition() +
owner->getRotation() * glm::vec3(0.f, weapon->hitRange, 0.f);
auto handPos = glm::vec3(handMatrix * glm::vec4(0.f, 0.f, 0.f, 1.f));
auto fireOrigin = owner->getPosition() + owner->getRotation() * handPos;
owner->engine->doWeaponScan(
WeaponScan(weapon->damage, fireOrigin, farTarget, weapon));
}
void Weapon::fireProjectile(WeaponData* weapon, CharacterObject* owner,
float force) {
auto handPos = glm::vec3(0.f, 1.5f, 1.f);
auto fireOrigin = owner->getPosition() + owner->getRotation() * handPos;
auto direction =
owner->getRotation() * glm::normalize(glm::vec3{0.f, 1.f, 1.f});
auto pt = weapon->name == "grenade" ? ProjectileObject::Grenade
: ProjectileObject::Molotov;
force = std::max(0.1f, force);
auto projectile = new ProjectileObject(
owner->engine, fireOrigin,
{pt, direction,
17.f * force, /// @todo pull a better velocity from somewhere
3.5f, weapon});
auto& pool = owner->engine->getTypeObjectPool(projectile);
pool.insert(projectile);
owner->engine->allObjects.push_back(projectile);
}

View File

@ -0,0 +1,12 @@
#ifndef RWENGINE_WEAPON_HPP
#define RWENGINE_WEAPON_HPP
#include <data/WeaponData.hpp>
#include <objects/CharacterObject.hpp>
namespace Weapon {
void fireProjectile(WeaponData* wepon, CharacterObject* character, float force);
void fireHitscan(WeaponData* wepon, CharacterObject* character);
}
#endif

View File

@ -1,160 +0,0 @@
#include <ai/CharacterController.hpp>
#include <data/Model.hpp>
#include <data/Skeleton.hpp>
#include <engine/Animator.hpp>
#include <engine/GameData.hpp>
#include <engine/GameWorld.hpp>
#include <items/WeaponItem.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/ProjectileObject.hpp>
void WeaponItem::fireHitscan(CharacterObject* owner) {
auto handFrame = owner->getModel()->findFrame("srhand");
glm::mat4 handMatrix;
if (handFrame) {
while (handFrame->getParent()) {
handMatrix =
owner->skeleton->getMatrix(handFrame->getIndex()) * handMatrix;
handFrame = handFrame->getParent();
}
}
auto farTarget =
owner->getPosition() +
owner->getRotation() * glm::vec3(0.f, _wepData->hitRange, 0.f);
auto handPos = glm::vec3(handMatrix * glm::vec4(0.f, 0.f, 0.f, 1.f));
auto fireOrigin = owner->getPosition() + owner->getRotation() * handPos;
owner->engine->doWeaponScan(
WeaponScan(_wepData->damage, fireOrigin, farTarget, _wepData.get()));
// Particle FX involved:
// - smokeII emited around barrel
// - Some circle particle used for the tracer
// - smoke emited at hit point
// - gunflash
#if 0 // Should be merged into the VisualFX system
auto flashDir = owner->getRotation() * glm::vec3{0.f, 0.f, 1.f};
auto flashUp = owner->getRotation() * glm::vec3{0.f, -1.f, 0.f};
auto tracerTex = owner->engine->data->findTexture("shad_exp")->getName();
auto flashTex = owner->engine->data->findTexture("gunflash2")->getName();
auto flashTex1 = owner->engine->data->findTexture("gunflash1")->getName();
float tracertime = 0.1f;
auto distance = glm::distance(fireOrigin, farTarget);
const float tracerspeed = distance / tracertime * 0.5f;
float tracersize = _wepData->hitRange / 4.f;
float flashtime = 0.015f;
auto shotdir = glm::normalize(farTarget - fireOrigin);
/// @TODO move this into rendering logic.
/*_character->engine->renderer.addParticle({
fireOrigin + shotdir * tracersize / 2.f,
shotdir,
tracerspeed,
GameRenderer::FXParticle::UpCamera,
_character->engine->gameTime, tracertime,
tracerTex,
{0.04f, tracersize},
{0.f, 0.f, 0.f}
});
_character->engine->renderer.addParticle({
fireOrigin,
flashDir,
0.f,
GameRenderer::FXParticle::Free,
_character->engine->gameTime, flashtime,
flashTex,
{0.2f, 0.2f},
flashUp
});
_character->engine->renderer.addParticle({
fireOrigin + shotdir * 0.1f,
flashDir,
0.f,
GameRenderer::FXParticle::Free,
_character->engine->gameTime, flashtime,
flashTex,
{0.2f, 0.2f},
flashUp
});
_character->engine->renderer.addParticle({
fireOrigin + shotdir * 0.2f,
flashDir,
0.f,
GameRenderer::FXParticle::Free,
_character->engine->gameTime, flashtime,
flashTex1,
{0.2f, 0.2f},
flashUp
});
*/
#endif
}
void WeaponItem::fireProjectile(CharacterObject* owner) {
auto handPos = glm::vec3(0.f, 1.5f, 1.f);
auto fireOrigin = owner->getPosition() + owner->getRotation() * handPos;
auto direction =
owner->getRotation() * glm::normalize(glm::vec3{0.f, 1.f, 1.f});
auto pt = _wepData->name == "grenade" ? ProjectileObject::Grenade
: ProjectileObject::Molotov;
// Work out the velocity multiplier as a function of how long the player
// Was holding down the fire button. If _fireStop < 0.f then the player
// is still holding the button down.
float throwTime = owner->engine->getGameTime() -
owner->getCurrentState().primaryStartTime / 1000.f;
float forceFactor = throwTime;
if (owner->getCurrentState().primaryEndTime >=
owner->getCurrentState().primaryStartTime) {
uint32_t heldTime = owner->getCurrentState().primaryEndTime -
owner->getCurrentState().primaryStartTime;
forceFactor = (heldTime) / 1000.f;
}
forceFactor = std::max(0.1f, forceFactor / throwTime);
auto projectile = new ProjectileObject(
owner->engine, fireOrigin,
{pt, direction,
17.f * forceFactor, /// @todo pull a better velocity from somewhere
3.5f, _wepData});
auto& pool = owner->engine->getTypeObjectPool(projectile);
pool.insert(projectile);
owner->engine->allObjects.push_back(projectile);
}
void WeaponItem::primary(CharacterObject* owner) {
if (owner->getCurrentState().primaryActive) {
// ShootWeapon will call ::fire() on us at the appropriate time.
owner->controller->setNextActivity(new Activities::ShootWeapon(this));
}
}
void WeaponItem::secondary(CharacterObject* owner) {
RW_UNUSED(owner);
}
void WeaponItem::fire(CharacterObject* owner) {
switch (_wepData->fireType) {
case WeaponData::INSTANT_HIT:
fireHitscan(owner);
break;
case WeaponData::PROJECTILE:
fireProjectile(owner);
break;
default:
/// @todo meele
break;
}
}
bool WeaponItem::isFiring(CharacterObject* owner) {
return owner->getCurrentState().primaryActive;
}

View File

@ -1,40 +0,0 @@
#pragma once
#ifndef _WEAPONITEM_HPP_
#define _WEAPONITEM_HPP_
#include <data/WeaponData.hpp>
#include <items/InventoryItem.hpp>
#include <memory>
/**
* @brief The WeaponItem class
* Logic for basic weapon types
*
* This is instanciated once -per item type-, so state is shared between
* all instances of the same weapon. Timing is controlled by the CharacterState
*/
class WeaponItem : public InventoryItem {
std::shared_ptr<WeaponData> _wepData;
void fireHitscan(CharacterObject* owner);
void fireProjectile(CharacterObject* owner);
public:
WeaponItem(int itemID, std::shared_ptr<WeaponData> data)
: InventoryItem(itemID, data->inventorySlot, data->modelID)
, _wepData(data) {
}
void primary(CharacterObject* owner);
void secondary(CharacterObject* owner);
void fire(CharacterObject* owner);
bool isFiring(CharacterObject* owner);
std::shared_ptr<WeaponData>& getWeaponData() {
return _wepData;
}
};
#endif

View File

@ -3,7 +3,6 @@
#include <engine/Animator.hpp>
#include <engine/GameData.hpp>
#include <engine/GameWorld.hpp>
#include <items/InventoryItem.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/VehicleObject.hpp>
#include <rw/defines.hpp>
@ -14,8 +13,7 @@ static glm::vec3 enter_offset(0.81756252f, 0.34800607f, -0.486281008f);
const float CharacterObject::DefaultJumpSpeed = 2.f;
CharacterObject::CharacterObject(GameWorld* engine, const glm::vec3& pos,
const glm::quat& rot,
BaseModelInfo *modelinfo)
const glm::quat& rot, BaseModelInfo* modelinfo)
: GameObject(engine, pos, rot, modelinfo)
, currentState({})
, currentVehicle(nullptr)
@ -521,12 +519,11 @@ void CharacterObject::activityFinished() {
motionBlockedByActivity = false;
}
void CharacterObject::addToInventory(InventoryItem* item) {
RW_CHECK(item->getInventorySlot() < maxInventorySlots,
"Inventory Slot greater than maxInventorySlots");
if (item->getInventorySlot() < maxInventorySlots) {
currentState.weapons[item->getInventorySlot()].weaponId =
item->getItemID();
void CharacterObject::addToInventory(int slot, int ammo) {
RW_CHECK(slot < kMaxInventorySlots, "Slot greater than kMaxInventorySlots");
if (slot < kMaxInventorySlots) {
currentState.weapons[slot].weaponId = slot;
currentState.weapons[slot].bulletsTotal += ammo;
}
}
@ -534,19 +531,16 @@ void CharacterObject::setActiveItem(int slot) {
currentState.currentWeapon = slot;
}
InventoryItem* CharacterObject::getActiveItem() {
if (currentVehicle) return nullptr;
auto weaponId = currentState.weapons[currentState.currentWeapon].weaponId;
return engine->getInventoryItem(weaponId);
}
void CharacterObject::removeFromInventory(int slot) {
currentState.weapons[slot].weaponId = 0;
if (currentState.currentWeapon == slot) {
currentState.currentWeapon = 0;
}
}
void CharacterObject::cycleInventory(bool up) {
if (up) {
for (int j = currentState.currentWeapon + 1; j < maxInventorySlots;
for (int j = currentState.currentWeapon + 1; j < kMaxInventorySlots;
++j) {
if (currentState.weapons[j].weaponId != 0) {
currentState.currentWeapon = j;
@ -565,7 +559,7 @@ void CharacterObject::cycleInventory(bool up) {
}
// Nothing? set the highest
for (int j = maxInventorySlots - 1; j >= 0; --j) {
for (int j = kMaxInventorySlots - 1; j >= 0; --j) {
if (currentState.weapons[j].weaponId != 0 || j == 0) {
currentState.currentWeapon = j;
return;
@ -575,17 +569,21 @@ void CharacterObject::cycleInventory(bool up) {
}
void CharacterObject::useItem(bool active, bool primary) {
if (getActiveItem()) {
/// @todo verify if this is the correct logic
auto item = getActiveItem();
if (currentState.weapons[item].weaponId == unsigned(item)) {
if (primary) {
if (active)
currentState.primaryStartTime = engine->getGameTime() * 1000.f;
else
currentState.primaryEndTime = engine->getGameTime() * 1000.f;
if (!currentState.primaryActive && active) {
// If we've just started, activate
controller->setNextActivity(new Activities::UseItem(item));
}
else if (currentState.primaryActive && !active) {
// UseItem will cancel itself upon !primaryActive
}
currentState.primaryActive = active;
getActiveItem()->primary(this);
} else {
currentState.secondaryActive = active;
getActiveItem()->secondary(this);
/// @todo handle scopes and sights
}
}
}

View File

@ -8,7 +8,7 @@
#include <glm/glm.hpp>
#include <objects/GameObject.hpp>
constexpr int maxInventorySlots = 13;
constexpr int kMaxInventorySlots = 13;
// Animation slots used for character animation blending
constexpr unsigned int AnimIndexMovement = 0;
@ -26,18 +26,15 @@ struct CharacterState {
float rotation;
float health = 100.f;
float armour = 0.f;
std::array<CharacterWeaponSlot, maxInventorySlots> weapons;
std::array<CharacterWeaponSlot, kMaxInventorySlots> weapons;
uint16_t currentWeapon = 0;
uint32_t lastFireTimeMS = 0;
bool primaryActive = false;
bool secondaryActive = false;
uint32_t primaryStartTime = 0;
uint32_t primaryEndTime = 0;
};
class VehicleObject;
class GameWorld;
class InventoryItem;
struct AnimationGroup {
Animation* idle;
@ -234,9 +231,21 @@ public:
*/
void activityFinished();
void addToInventory(InventoryItem* item);
/**
* @brief addToInventory Adds ammo to the specified item slot
* @param slot The slot to add ammo for
* @param ammo The quanity of ammunition to add
*
* Will give the weapon (set the ID) if it is not possessed
*/
void addToInventory(int slot, int ammo);
void setActiveItem(int slot);
InventoryItem* getActiveItem();
int getActiveItem() const { return currentState.currentWeapon; }
/**
* @brief removeFromInventory Removes item at slot from inventory
*/
void removeFromInventory(int slot);
/**

View File

@ -1,22 +1,20 @@
#include <data/WeaponData.hpp>
#include <engine/GameData.hpp>
#include <engine/GameWorld.hpp>
#include <items/WeaponItem.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/ItemPickup.hpp>
#include <rw/defines.hpp>
ItemPickup::ItemPickup(GameWorld *world, const glm::vec3 &position,
BaseModelInfo *modelinfo, PickupType type,
InventoryItem *item)
WeaponData *item)
: PickupObject(world, position, modelinfo, type), item(item) {
RW_CHECK(item != nullptr, "Pickup created with null item");
}
bool ItemPickup::onCharacterTouch(CharacterObject *character) {
character->addToInventory(item);
auto &wep = character->getCurrentState().weapons[item->getInventorySlot()];
auto totalRounds = 0, clipRounds = 0;
auto totalRounds = 0;
switch (item->getModelID()) {
switch (item->modelID) {
case 173: /* Pistol */
totalRounds = 45;
break;
@ -50,8 +48,7 @@ bool ItemPickup::onCharacterTouch(CharacterObject *character) {
totalRounds /= 5;
}
wep.bulletsTotal = totalRounds;
wep.bulletsClip = clipRounds;
character->addToInventory(item->inventorySlot, totalRounds);
return true;
}

View File

@ -1,21 +1,19 @@
#pragma once
#ifndef _ITEMPICKUP_HPP_
#define _ITEMPICKUP_HPP_
#include <data/WeaponData.hpp>
#include <glm/glm.hpp>
#include <objects/PickupObject.hpp>
class InventoryItem;
/**
* @brief The ItemPickup class
* Inserts an item into a characters inventory on pickup.
*/
class ItemPickup : public PickupObject {
InventoryItem* item;
WeaponData* item;
public:
ItemPickup(GameWorld* world, const glm::vec3& position, BaseModelInfo *modelinfo, PickupType type,
InventoryItem* item);
ItemPickup(GameWorld* world, const glm::vec3& position,
BaseModelInfo* modelinfo, PickupType type, WeaponData* item);
bool onCharacterTouch(CharacterObject* character);
};

View File

@ -25,7 +25,7 @@ public:
/** Time to dentonation or removal */
float time;
std::shared_ptr<WeaponData> weapon;
WeaponData* weapon;
};
private:

View File

@ -12,7 +12,6 @@
#include <ai/CharacterController.hpp>
#include <data/ModelData.hpp>
#include <items/InventoryItem.hpp>
#include <data/CutsceneData.hpp>
#include <data/Skeleton.hpp>

View File

@ -32,7 +32,6 @@ class ProjectileObject;
class CutsceneObject;
class Animator;
class InventoryItem;
class Renderer;

View File

@ -7,7 +7,6 @@
#include <render/ObjectRenderer.hpp>
// Objects that we know how to turn into renderlist entries
#include <items/InventoryItem.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/CutsceneObject.hpp>
#include <objects/InstanceObject.hpp>
@ -302,31 +301,30 @@ void ObjectRenderer::renderCharacter(CharacterObject* pedestrian,
renderFrame(pedestrian->getModel(), root->getChildren()[0], matrixModel,
pedestrian, 1.f, outList);
if (pedestrian->getActiveItem()) {
auto item = pedestrian->getActiveItem();
auto item = pedestrian->getActiveItem();
const auto& weapon = pedestrian->engine->data->weaponData[item];
if (item->getModelID() == -1) {
return; // No model for this item
}
auto handFrame = pedestrian->getModel()->findFrame("srhand");
glm::mat4 localMatrix;
if (handFrame) {
while (handFrame->getParent()) {
localMatrix =
pedestrian->skeleton->getMatrix(handFrame->getIndex()) *
localMatrix;
handFrame = handFrame->getParent();
}
}
// Assume items are all simple
auto simple =
m_world->data->findModelInfo<SimpleModelInfo>(item->getModelID());
auto geometry = simple->getAtomic(0)->getGeometries().at(0);
renderGeometry(simple->getModel(), geometry, matrixModel * localMatrix,
1.f, nullptr, outList);
if (weapon->modelID == -1) {
return; // No model for this item
}
auto handFrame = pedestrian->getModel()->findFrame("srhand");
glm::mat4 localMatrix;
if (handFrame) {
while (handFrame->getParent()) {
localMatrix =
pedestrian->skeleton->getMatrix(handFrame->getIndex()) *
localMatrix;
handFrame = handFrame->getParent();
}
}
// Assume items are all simple
auto simple =
m_world->data->findModelInfo<SimpleModelInfo>(weapon->modelID);
auto geometry = simple->getAtomic(0)->getGeometries().at(0);
renderGeometry(simple->getModel(), geometry, matrixModel * localMatrix, 1.f,
nullptr, outList);
}
void ObjectRenderer::renderVehicle(VehicleObject* vehicle,

View File

@ -1,8 +1,8 @@
#include "DrawUI.hpp"
#include <ai/PlayerController.hpp>
#include <engine/GameState.hpp>
#include <items/WeaponItem.hpp>
#include <objects/CharacterObject.hpp>
#include <data/WeaponData.hpp>
#include <render/GameRenderer.hpp>
#include <glm/gtc/constants.hpp>
@ -152,16 +152,14 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world,
render->text.renderText(ti);
#endif
InventoryItem* current = player->getCharacter()->getActiveItem();
auto item = player->getCharacter()->getActiveItem();
auto weapon = world->data->weaponData[item];
std::string itemTextureName = "fist";
if (current) {
uint16_t model = current->getModelID();
if (model > 0) {
auto weaponData =
world->data->findModelInfo<SimpleModelInfo>(model);
if (weaponData != nullptr) {
itemTextureName = weaponData->name;
}
if (weapon && weapon->modelID > 0) {
auto model =
world->data->findModelInfo<SimpleModelInfo>(weapon->modelID);
if (model != nullptr) {
itemTextureName = model->name;
}
}
// Urgh
@ -179,25 +177,20 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world,
glm::vec4(iconX, iconY, ui_weaponSize, ui_weaponSize));
}
if (current) {
WeaponItem* wep = static_cast<WeaponItem*>(current);
if (wep->getWeaponData()->fireType != WeaponData::MELEE) {
const CharacterState& cs =
player->getCharacter()->getCurrentState();
const CharacterWeaponSlot& slotInfo = cs.weapons[cs.currentWeapon];
ti.text = GameStringUtil::fromString(
std::to_string(slotInfo.bulletsClip) + "-" +
std::to_string(slotInfo.bulletsTotal));
if (weapon->fireType != WeaponData::MELEE) {
const CharacterState& cs = player->getCharacter()->getCurrentState();
const CharacterWeaponSlot& slotInfo = cs.weapons[cs.currentWeapon];
ti.text = GameStringUtil::fromString(
std::to_string(slotInfo.bulletsClip) + "-" +
std::to_string(slotInfo.bulletsTotal));
ti.baseColour = ui_shadowColour;
ti.font = 2;
ti.size = ui_ammoSize;
ti.align = TextRenderer::TextInfo::Center;
ti.screenPosition =
glm::vec2(iconX + ui_weaponSize / 2.f,
iconY + ui_weaponSize - ui_ammoHeight);
render->text.renderText(ti);
}
ti.baseColour = ui_shadowColour;
ti.font = 2;
ti.size = ui_ammoSize;
ti.align = TextRenderer::TextInfo::Center;
ti.screenPosition = glm::vec2(iconX + ui_weaponSize / 2.f,
iconY + ui_weaponSize - ui_ammoHeight);
render->text.renderText(ti);
}
}

View File

@ -4,7 +4,6 @@
#include <engine/GameState.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/string_cast.hpp>
#include <items/InventoryItem.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/InstanceObject.hpp>
#include <objects/VehicleObject.hpp>
@ -279,11 +278,10 @@ Menu* DebugState::createWeaponMenu() {
[=] { this->enterMenu(createDebugMenu()); },
kDebugEntryHeight));
for (int i = 1; i < maxInventorySlots; ++i) {
auto item = getWorld()->getInventoryItem(i);
for (int i = 1; i < kMaxInventorySlots; ++i) {
auto& name = getWorld()->data->weaponData[i]->name;
m->addEntry(
Menu::lambda(name, [=] { giveItem(item); }, kDebugEntryHeight));
Menu::lambda(name, [=] { giveItem(i); }, kDebugEntryHeight));
}
return m;
@ -475,17 +473,14 @@ void DebugState::spawnFollower(unsigned int id) {
}
}
void DebugState::giveItem(InventoryItem* item) {
void DebugState::giveItem(int slot) {
CharacterObject* player = nullptr;
if (game->getPlayer()) {
player = game->getPlayer()->getCharacter();
}
if (player) {
player->addToInventory(item);
auto& wep = player->getCurrentState().weapons[item->getInventorySlot()];
wep.bulletsTotal = 100;
wep.bulletsClip = 0;
player->addToInventory(slot, 100);
}
}

View File

@ -33,7 +33,7 @@ public:
void spawnVehicle(unsigned int id);
void spawnFollower(unsigned int id);
void giveItem(InventoryItem* item);
void giveItem(int slot);
const ViewCamera& getCamera();
};

View File

@ -6,11 +6,11 @@
#include <ai/PlayerController.hpp>
#include <data/Model.hpp>
#include <data/WeaponData.hpp>
#include <dynamics/CollisionInstance.hpp>
#include <dynamics/RaycastCallbacks.hpp>
#include <engine/GameState.hpp>
#include <engine/GameWorld.hpp>
#include <items/WeaponItem.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/ItemPickup.hpp>
#include <objects/VehicleObject.hpp>
@ -105,9 +105,9 @@ void IngameState::startTest() {
getWorld()->state->playerObject = playerChar->getGameObjectID();
glm::vec3 itemspawn(276.5f, -609.f, 36.5f);
for (int i = 1; i < maxInventorySlots; ++i) {
auto item = getWorld()->getInventoryItem(i);
getWorld()->createPickup(itemspawn, item->getModelID(),
for (int i = 1; i < getWorld()->data->weaponData.size(); ++i) {
auto& item = getWorld()->data->weaponData[i];
getWorld()->createPickup(itemspawn, item->modelID,
PickupObject::OnStreet);
itemspawn.x += 2.5f;
}

View File

@ -1,5 +1,4 @@
#include <boost/test/unit_test.hpp>
#include <items/WeaponItem.hpp>
#include <objects/CharacterObject.hpp>
#include "test_globals.hpp"
@ -11,24 +10,17 @@ BOOST_AUTO_TEST_CASE(test_character_inventory) {
auto character = Global::get().e->createPedestrian(1, {0.f, 0.f, 0.f});
BOOST_REQUIRE(character != nullptr);
auto item = Global::get().e->getInventoryItem(4);
auto fist = Global::get().e->getInventoryItem(0);
character->addToInventory(1, 10);
BOOST_REQUIRE(item != nullptr);
BOOST_REQUIRE(fist != nullptr);
BOOST_CHECK_NE(fist, item);
BOOST_CHECK_EQUAL(character->getActiveItem(), 0);
character->addToInventory(item);
character->setActiveItem(1);
BOOST_CHECK_EQUAL(character->getActiveItem(), fist);
BOOST_CHECK_EQUAL(character->getActiveItem(), 1);
character->setActiveItem(item->getInventorySlot());
character->removeFromInventory(1);
BOOST_CHECK_EQUAL(character->getActiveItem(), item);
character->removeFromInventory(item->getInventorySlot());
BOOST_CHECK_EQUAL(character->getActiveItem(), fist);
BOOST_CHECK_EQUAL(character->getActiveItem(), 0);
Global::get().e->destroyObject(character);
}

View File

@ -1,6 +1,5 @@
#include <boost/test/unit_test.hpp>
#include <data/WeaponData.hpp>
#include <items/InventoryItem.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/ItemPickup.hpp>
#include <objects/PickupObject.hpp>
@ -65,16 +64,16 @@ BOOST_AUTO_TEST_CASE(test_item_pickup) {
Global::get().e->createPedestrian(1, {30.1f, 0.f, 0.f});
BOOST_REQUIRE(character != nullptr);
auto item = Global::get().e->getInventoryItem(3);
BOOST_REQUIRE(item != nullptr);
auto pistol = Global::get().d->weaponData[1].get();
auto model = Global::get().d->modelinfo[pistol->modelID].get();
ItemPickup* p = new ItemPickup(Global::get().e, {30.f, 0.f, 0.f},
nullptr, PickupObject::OnStreet, item);
ItemPickup* p = new ItemPickup(Global::get().e, {30.f, 0.f, 0.f}, model,
PickupObject::OnStreet, pistol);
Global::get().e->allObjects.push_back(p);
// Check the characters inventory is empty.
for (int i = 0; i < maxInventorySlots; ++i) {
for (int i = 0; i < kMaxInventorySlots; ++i) {
BOOST_CHECK(character->getCurrentState().weapons[i].weaponId == 0);
}
@ -84,8 +83,7 @@ BOOST_AUTO_TEST_CASE(test_item_pickup) {
auto& inventory = character->getCurrentState().weapons;
BOOST_CHECK(std::any_of(std::begin(inventory), std::end(inventory),
[&](const CharacterWeaponSlot& i) {
return i.weaponId ==
item->getInventorySlot();
return i.weaponId == pistol->inventorySlot;
}));
Global::get().e->destroyObject(p);

View File

@ -30,7 +30,7 @@ BOOST_AUTO_TEST_CASE(TestProjectile) {
auto character = Global::get().e->createPedestrian(1, {25.f, 0.f, 0.f});
BOOST_REQUIRE(character != nullptr);
auto wepdata = Global::get().e->data->weaponData[5];
auto wepdata = Global::get().e->data->weaponData[5].get();
auto projectile = new ProjectileObject(
Global::get().e, {26.f, 1.f, 10.f},
@ -63,7 +63,7 @@ BOOST_AUTO_TEST_CASE(TestProjectile) {
auto character = Global::get().e->createPedestrian(1, {25.f, 0.f, 0.f});
BOOST_REQUIRE(character != nullptr);
auto wepdata = Global::get().e->data->weaponData[6];
auto wepdata = Global::get().e->data->weaponData[6].get();
auto projectile = new ProjectileObject(
Global::get().e, {26.f, 1.f, 10.f},
@ -90,7 +90,7 @@ BOOST_AUTO_TEST_CASE(TestProjectile) {
auto character = Global::get().e->createPedestrian(1, {25.f, 0.f, 0.f});
BOOST_REQUIRE(character != nullptr);
auto wepdata = Global::get().e->data->weaponData[7];
auto wepdata = Global::get().e->data->weaponData[7].get();
auto projectile = new ProjectileObject(
Global::get().e, {26.f, 1.f, 10.f},