Skip to content

Commit

Permalink
NWN2: Create new item in inventory using ResRef template
Browse files Browse the repository at this point in the history
  • Loading branch information
rjshae authored and DrMcCoy committed Mar 4, 2019
1 parent 35d5293 commit 848f3f2
Show file tree
Hide file tree
Showing 14 changed files with 270 additions and 58 deletions.
4 changes: 4 additions & 0 deletions src/engines/nwn2/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,10 @@ void Creature::setLootable(bool lootable) {
_isLootable = lootable;
}

Item *Creature::createItemOnObject(const Common::UString &blueprint, uint16 stackSize, const Common::UString &tag) {
return createItem(blueprint, stackSize, tag);
}

/**
* Perform a skill check against the DC.
*
Expand Down
3 changes: 3 additions & 0 deletions src/engines/nwn2/creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ class Creature : public Object, public Inventory {
void setPlotFlag(bool plotFlag);
void setLootable(bool lootable);

/** Create an item in the creature's inventory. */
Item *createItemOnObject(const Common::UString &blueprint, uint16 stackSize, const Common::UString &tag);

// Reputation

uint8 getReputation(Object *source) const;
Expand Down
153 changes: 116 additions & 37 deletions src/engines/nwn2/inventory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/

#include <iterator>
#include <vector>

#include "src/common/error.h"

Expand All @@ -49,58 +50,134 @@ Inventory::~Inventory() {

/** Return the first item in the inventory. */
Item *Inventory::getFirstItemInInventory() {
uint16 equipSize = (uint16) kInventorySlotMax;

// Cycle through equipped items
_lastRetrieved = 0;
for (std::vector<Item *>::const_iterator item = equippedItems.begin(); item != equippedItems.end(); ++item) {
if (*item != nullptr)
return *item;

// Stay in bounds
if (++_lastRetrieved >= equipSize)
break;
}

// Cycle through the inventory
_lastRetrieved = equipSize;
for (std::vector<Item *>::const_iterator item = inventoryItems.begin(); item != inventoryItems.end(); ++item) {
if (*item != nullptr)
return *item;

if (!equippedItems.empty()) {
return equippedItems.begin()->second;
} else if (!inventoryItems.empty()) {
return inventoryItems.begin()->second;
_lastRetrieved++;
}
return 0;

return nullptr;
}

/** Return the next item in the inventory. */
Item *Inventory::getNextItemInInventory() {
// Find the matching item
const size_t equipSize = equippedItems.size();
// Check for the end of inventory
if (_lastRetrieved == UINT16_MAX)
return nullptr;

// Increment the counter
_lastRetrieved++;

// Cycle through the remaining equipped items
const uint16 equipSize = (uint16) kInventorySlotMax;
if (_lastRetrieved < equipSize) {
std::map<uint16, Item *>::const_iterator item = equippedItems.begin();
std::vector<Item *>::const_iterator item = equippedItems.begin();
std::advance(item, _lastRetrieved);
return item->second;
} else {
const size_t inventorySize = inventoryItems.size();
if (_lastRetrieved < equipSize + inventorySize) {
// TODO: Check for an item with nested inventory (Ex.: magic bag)
std::map<uint16, Item *>::const_iterator item = inventoryItems.begin();
std::advance(item, _lastRetrieved - equipSize);
return item->second;
for (; item != equippedItems.end(); item++) {
if (*item != nullptr)
return *item;

// Stay in bounds
if(++_lastRetrieved >= equipSize)
break;
}
}

// Cycle through the remaining inventory
const size_t inventorySize = inventoryItems.size();
if (_lastRetrieved < equipSize + inventorySize) {
// TODO: Check for an item with nested inventory (Ex.: magic bag)
std::vector<Item *>::const_iterator item = inventoryItems.begin();
std::advance(item, _lastRetrieved - equipSize);
for (; item != inventoryItems.end(); item++)
if (*item != nullptr) {
return *item;
_lastRetrieved++;
}
}

// End of the inventory
_lastRetrieved = UINT32_MAX;
return 0;
_lastRetrieved = UINT16_MAX;
return nullptr;
}

void Inventory::clear() {
std::map<uint16, Item *>::iterator item;
Item *Inventory::createItem(const Common::UString &blueprint, uint16 stackSize, const Common::UString &tag) {
if (blueprint.empty() || stackSize == 0)
return nullptr;

// Create and insert the item
Item *item = new Item(blueprint, stackSize, tag);
insertItem(item);
return item;
}

void Inventory::insertItem(Item *item, uint16 slot) {
if (item == nullptr)
return;

// Determine the maximum slot to be occupied
size_t max = inventoryItems.size();
if (slot > max)
max = slot;

// Make sure there's enough space allocated
if (max + 2 > inventoryItems.capacity())
inventoryItems.resize(max + 8, nullptr); // Add a full row

// Find an empty inventory slot
if (inventoryItems[slot] != nullptr)
slot = getFirstEmptySlot();

// Insert the item pointer
inventoryItems[slot] = item;
}

void Inventory::clear() {
// Purge all equipped item instances
for (item = equippedItems.begin(); item != equippedItems.end(); ++item)
if (item->second)
delete(item->second);
for (std::vector<Item *>::iterator item = equippedItems.begin(); item != equippedItems.end(); ++item)
if (*item)
delete *item;

// Purge all inventory item instances
for (item = inventoryItems.begin(); item != inventoryItems.end(); ++item)
if (item->second)
delete(item->second);
for (std::vector<Item *>::iterator item = inventoryItems.begin(); item != inventoryItems.end(); ++item)
if (*item)
delete *item;

// Clear the maps
equippedItems.clear();
inventoryItems.clear();
}

uint16 Inventory::getFirstEmptySlot() const {
// Look for the first open slot
uint16 slot = 0;

for (std::vector<Item *>::const_iterator item = inventoryItems.begin(); item != inventoryItems.end(); ++item) {
if (*item == nullptr)
return slot;
slot++;
}

// Failsafe
return slot;
}

InventorySlot Inventory::getSlotFromBitFlag(uint32 bitFlag) const {
// Bit flags used for the 'EquipableSlots' column of 'baseitem.2da'
static const uint32 kSlotBitFlagHead = (uint32)(0x1 << (int) kInventorySlotHead);
Expand Down Expand Up @@ -171,29 +248,31 @@ void Inventory::load(const Aurora::GFF3Struct &inventory) {
for (Aurora::GFF3List::const_iterator it = itemList.begin(); it != itemList.end(); ++it) {
const Aurora::GFF3Struct &item = **it;
uint16 slot = item.getUint("Repos_Index");

// TODO: Find an empty inventory slot instead
if (inventoryItems.find(slot) != inventoryItems.end())
throw Common::Exception("Inventory slot already occupied: %d", slot);

inventoryItems[slot] = new Item(item);
insertItem(new Item(item), slot);
}
}

// Load equipped items (creature only)
if (inventory.hasField("Equip_ItemList")) {
// Allocate space for all slots
size_t capacity = equippedItems.capacity();
if (capacity < (size_t) kInventorySlotMax)
equippedItems.resize((size_t) kInventorySlotMax, nullptr);

const Aurora::GFF3List &itemList = inventory.getList("Equip_ItemList");
for (Aurora::GFF3List::const_iterator it = itemList.begin(); it != itemList.end(); ++it) {
const Aurora::GFF3Struct &item = **it;

// Slot number is given by the struct id bit flag
uint16 slot = getSlotFromBitFlag(item.getID());

// TODO: Move item to general inventory instead
if (equippedItems.find(slot) != equippedItems.end())
throw Common::Exception("Equip slot already occupied: %d", slot);

equippedItems[slot] = new Item(item);
// Check for an open equipment slot
if (slot < (uint16) kInventorySlotMax && equippedItems[slot] == nullptr) {
equippedItems[slot] = new Item(item);
} else {
// Move to the general inventory instead
insertItem(new Item(item));
}
}
}
}
Expand Down
21 changes: 16 additions & 5 deletions src/engines/nwn2/inventory.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#ifndef ENGINES_NWN2_INVENTORY_H
#define ENGINES_NWN2_INVENTORY_H

#include <vector>

#include "src/engines/nwn2/types.h"

namespace Engines {
Expand All @@ -42,19 +44,28 @@ class Inventory {
Item *getFirstItemInInventory();
Item *getNextItemInInventory();

/** Add a new item to the inventory using the blueprint template. */
Item *createItem(const Common::UString &blueprint, uint16 stackSize, const Common::UString &tag);

private:

typedef std::map<uint16, Item *> ItemSlotMap;
typedef std::vector<Item *> ItemSlots;

ItemSlots equippedItems;
ItemSlots inventoryItems;

uint16 _lastRetrieved { UINT16_MAX }; ///< Index of last retrieved item.

ItemSlotMap equippedItems;
ItemSlotMap inventoryItems;
/** Insert item in the slot. */
void insertItem(Item *item, uint16 slot = 0);

uint32 _lastRetrieved { UINT32_MAX }; ///< Index of last retrieved item.
/** Get the first empty inventory slot. */
uint16 getFirstEmptySlot() const;

/** Convert a bit flag to an equipment slot. */
InventorySlot getSlotFromBitFlag(uint32 bitFlag) const;

/** Delete all items in inventory then clear the map. */
/** Delete all items in inventory then clear the maps. */
void clear();

/** Load from an item list. */
Expand Down
43 changes: 37 additions & 6 deletions src/engines/nwn2/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ Item::Item(const Aurora::GFF3Struct &item) :
load(item);
}

Item::Item(const Common::UString &blueprint, uint16 stackSize, const Common::UString &tag) :
Object(kObjectTypeItem), Inventory() {

load(blueprint, stackSize, tag);
}

Item::~Item() {
}

Expand Down Expand Up @@ -123,25 +129,50 @@ void Item::setItemIcon(uint32 icon) {
}

void Item::setItemStackSize(uint16 stackSize) {
// Check if the value is in the allowed range
const Aurora::TwoDARow &row = TwoDAReg.get2DA("baseitems").getRow((size_t) _baseItem);
uint32 maxStack = (uint32) row.getInt("Stacking");
if (stackSize == 0 || stackSize > maxStack)
if (stackSize == 0)
return;

_stackSize = stackSize;
// Constrain stack to be no more than allowed by the baseitem
uint16 maxStack = getMaxStackSize();
_stackSize = (stackSize > maxStack) ? maxStack : stackSize;
}

uint16 Item::getMaxStackSize() const {
const Aurora::TwoDARow &row = TwoDAReg.get2DA("baseitems").getRow((size_t) _baseItem);
return (uint16) row.getInt("Stacking");
}

Item *Item::createItemOnObject(const Common::UString &blueprint, uint16 stackSize, const Common::UString &tag) {
if (!getHasInventory())
return 0;

return createItem(blueprint, stackSize, tag);
}

void Item::load(const Aurora::GFF3Struct &item) {
Common::UString temp = item.getString("TemplateResRef");

Common::ScopedPtr<Aurora::GFF3File> uti;
if (!temp.empty())
uti.reset(loadOptionalGFF3(temp, Aurora::kFileTypeUTD, MKTAG('U', 'T', 'I', ' ')));
uti.reset(loadOptionalGFF3(temp, Aurora::kFileTypeUTI, MKTAG('U', 'T', 'I', ' ')));

load(item, uti ? &uti->getTopLevel() : 0);
}

void Item::load(const Common::UString &blueprint, uint16 stackSize, const Common::UString &tag) {
Common::ScopedPtr<Aurora::GFF3File> uti;
uti.reset(loadOptionalGFF3(blueprint, Aurora::kFileTypeUTI, MKTAG('U', 'T', 'I', ' ')));
if (!uti)
return;

loadProperties(uti->getTopLevel());

// Apply the input arguments
setItemStackSize(stackSize);
if (!tag.empty())
_tag = tag;
}

void Item::load(const Aurora::GFF3Struct &instance, const Aurora::GFF3Struct *blueprint) {

if (blueprint)
Expand Down
6 changes: 6 additions & 0 deletions src/engines/nwn2/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ namespace NWN2 {
class Item : public Object, public Inventory {
public:
Item(const Aurora::GFF3Struct &item);
Item(const Common::UString &blueprint, uint16 stackSize, const Common::UString &tag);
~Item();

bool getDroppableFlag() const;
Expand All @@ -55,10 +56,14 @@ class Item : public Object, public Inventory {
ItemType getBaseItemType() const;
uint32 getItemIcon() const;
uint16 getItemStackSize() const;
uint16 getMaxStackSize() const;

void setItemIcon(uint32 icon);
void setItemStackSize(uint16 stackSize);

/** Create an item in the creature's inventory. */
Item *createItemOnObject(const Common::UString &blueprint, uint16 stackSize, const Common::UString &tag);

private:
uint32 _icon; ///< Icon number for inventory UI.
uint32 _cost; ///< Base price in gp.
Expand All @@ -75,6 +80,7 @@ class Item : public Object, public Inventory {

/* Load from an item instance. */
void load(const Aurora::GFF3Struct &item);
void load(const Common::UString &blueprint, uint16 stackSize, const Common::UString &tag);
void load(const Aurora::GFF3Struct &instance, const Aurora::GFF3Struct *blueprint);
void loadProperties(const Aurora::GFF3Struct &gff);
};
Expand Down
6 changes: 6 additions & 0 deletions src/engines/nwn2/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "src/engines/nwn2/object.h"
#include "src/engines/nwn2/area.h"
#include "src/engines/nwn2/module.h"
#include "src/engines/nwn2/item.h"
#include "src/engines/nwn2/faction.h"

static const uint8 kRepEnemyMax = 10; // Maximum reputation for an enemy
Expand Down Expand Up @@ -176,6 +177,11 @@ void Object::setOrientation(float x, float y, float z, float angle) {
_orientation[3] = angle;
}

Item *Object::createItemOnObject(const Common::UString &UNUSED(blueprint),
uint16 UNUSED(stackSize), const Common::UString &UNUSED(tag)) {
return 0;
}

void Object::enter() {
}

Expand Down

0 comments on commit 848f3f2

Please sign in to comment.