Inventory-System
Besides 3D features Gorilla3D also provides basic utility components like the inventory system. Most games or multimedia apps need some kind of inventory or item management.
By the TGorillaInventory component you are able to:
- manage item templates, groups and manufacturers
- hold items with thumbnail and preview models
- manage groups with thumbnails
- support a multilingual system (f.e.: for group/item names)
- have up- and downgradable items
- setup manufacturers for crafting and item combinations
- manufacture multiple items
- craft items in editable time in asynchrone backend thread
- manage max quantity of items and groups
- manage weight of items and groups
- collect items pooled or each as single instance
- collect items in dependencies, f.e. only if you have collected an ax, you can collect wood
- set variables to implement gaming elements
- set parameters to interact back with your game/application
Layout
An inventory consists of 3 basic components:
- item templates
- item groups
- manufacturers
First of all the inventory needs to know all possible items. These are called item templates. Item templates can be linked to item groups, to somehow categorize your collection. If you are interested in crafting or combining, manufacturers come in place.
Item Template
An item template holds information about the item itself. For example: a maximum global number of elements, its weight or dependencies to other items.
See in the following how you can add templates at runtime.
uses Gorilla.Utils.Inventory, Gorilla.Utils.Language; var FInventory : TGorillaInventory; procedure TForm1.FormCreate(Sender: TObject); var LMainGrp : TGorillaInventoryGroup; LAxe : TGorillaInventoryItemTemplate; LWood : TGorillaInventoryItemTemplate; LGrass : TGorillaInventoryItemTemplate; begin // create inventory system FInventory := TGorillaInventory.Create(Self); // we need a first group, where the templates belong to LMainGrp := FInventory.AddGroup('Rucksack'); // get the language identifiers LLangEN_US := GetLanguageName(GORILLA_LANG_EN_US); LLangDE_DE := GetLanguageName(GORILLA_LANG_DE_DE); // create the axe item template LAxe := FInventory.AddItemTemplate('Axe'); LAxe.Group := LMainGrp; LAxe.ImageIndex := 4; // add multilingual texts LAxe.Name.Add(LLangEN_US, 'axe'); LAxe.Name.Add(LLangDE_DE, 'Axt'); // set quantity and weight values LAxe.MaxQuantity := 1; LAxe.Weight := 1; LAxe.MaxWeight := 1; // create the wood item template LWood := FInventory.AddItemTemplate('Wood'); LWood.Group := LMainGrp; LWood.ImageIndex := 0; // we collect wood, but it will be stacked LWood.IsPooled := true; // add dependency to an axe - only if we have an axe collected, we can colled wood LWood.AddDependency(LAxe, 1); // add multilingual texts LWood.Name.Add(LLangEN_US, 'wood'); LWood.Name.Add(LLangDE_DE, 'Holz'); // create a grass item template LGrass := FInventory.AddItemTemplate('Grass'); LGrass.Group := LMainGrp; LGrass.ImageIndex := 2; LGrass.Name.Add(LLangEN_US, 'grass'); LGrass.Name.Add(LLangDE_DE, 'Gras'); end;
Item Group
As seen above we can simply add item groups at runtime and attach them to items.
LMainGrp := FInventory.AddGroup('Rucksack');
LWood.Group := LMainGrp;
Manufacturer
Manufacturers allow us to craft, combine or up-/downgrade items. As mentioned also this resulting items need to be registered as templates in the inventory.
Crafting
LFire : TGorillaInventoryItemTemplate; LMan : TGorillaInventoryManufacturer; [...] // register the resulting fire item template LFire := FInventory.AddItemTemplate('Fire'); LFire.Group := LMainGrp; LFire.ImageIndex := 3; LFire.Name.Add(LLangEN_US, 'fire'); LFire.Name.Add(LLangDE_DE, 'Feuer'); // add a new manufacturer LMan := FInventory.AddManufacturer('Campfire'); // setup a time for how long the manufacturing process takes (in milliseconds) LMan.TimeNeeded := 10000; // add items which are necessary to build a result item LMan.AddIngredient(LWood, 3); LMan.AddIngredient(LGrass, 1); // set the resulting item template LMan.ResultItem.Template := LFire;
Combining
Combining items works the same way like crafting. The only difference is the used time. While crafting expects a predefined time span, combining should work immediatly.
So simply set TimeNeeded property on manufacturer construction to less-equal zero.
LMan.TimeNeeded := 0;
The Manufacture() method will then not start an async thread for construction, instead it will directly build the new item.
Up-/Downgrading
To allow up-/downgrading simple link each template to the other.
// register the upgrade item template LChimney := FInventory.AddItemTemplate('Chimney'); LChimney.Group := LMainGrp; LChimney.ImageIndex := 6; LChimney.Name.Add(LLangEN_US, 'chimney'); LChimney.Name.Add(LLangDE_DE, 'Kamin'); // link with fire template for possible downgrade LChimney.DowngradeItem := LFire; // link with chimney template for possible upgrade LFire.UpgradeItem := LChimney;
To downgrade we call the Downgrade() method with the specific collected item:
procedure TForm1.MenuItem8Click(Sender: TObject); var LColl : TGorillaInventoryCollectable; begin // downgrade the item LColl := PopUpMenu1.TagObject as TGorillaInventoryCollectable; if not Assigned(LColl) then Exit; // check if the item is downgradable if LColl.IsDowngradable() then FInventory.Downgrade(LColl) else ShowMessage('Collected item not downgradable!'); end;
Equivalent to downgrading we call Upgrade() with the specific collected item to level up our item:
procedure TForm1.MenuItem7Click(Sender: TObject); var LColl : TGorillaInventoryCollectable; begin // upgrade the item LColl := PopUpMenu1.TagObject as TGorillaInventoryCollectable; if not Assigned(LColl) then Exit; // Check if the item is upgradable if LColl.IsUpgradable() then FInventory.Upgrade(LColl) else ShowMessage('Collected item not upgradable!'); end;
Multilingual
The component supports multilingual text content for your items and groups names. To set the value for a specific language we need the language-name, requested by GetLanguageName() function. By this index we can set a multilingual name for the item as shown below:
uses Gorilla.Utils.Language; var LLangEN_US, LLangDE_DE : String; LLangEN_US := GetLanguageName(GORILLA_LANG_EN_US); LLangDE_DE := GetLanguageName(GORILLA_LANG_DE_DE); [...] LAxe.Name.Add(LLangEN_US, 'axe'); LAxe.Name.Add(LLangDE_DE, 'Axt');
In the inventory component itself we can set the active language. This is the base setting for auto-language detection by the user-interface.
FInventory.Language := GORILLA_LANG_EN_US; FIFrame.UpdateInventory();
Currently supported languages are:
ID | Language |
---|---|
GORILLA_LANG_EN_US | english |
GORILLA_LANG_EN_UK | english |
GORILLA_LANG_DE_DE | german |
GORILLA_LANG_DE_AT | german |
GORILLA_LANG_DE_CH | german |
Collect
Collecting items is the most used feature of the inventory component running your app. Due to settings it is not always possible to really collect an item. Imagine, you already have 10 blocks of wood and the maximum is reached.
Until you can collect an item you'll need the item template to supply to the Collect() method. Afterwards the method will output a state value, telling you if collecting was successful.
Take a look at the following code:
procedure TForm1.Button1Click(Sender: TObject); var LIdx : Integer; LTemp : TGorillaInventoryItemTemplate; LState : TGorillaInventoryCollectState; begin // select a specific item template from a combobox LIdx := TemplateSelector.ItemIndex; if (LIdx < 0) then Exit; LTemp := TemplateSelector.Items.Objects[LIdx] as TGorillaInventoryItemTemplate; // run the collecting process FInventory.Collect(LTemp, LState); // check feedback if (LState <> TGorillaInventoryCollectState.Collected) then begin ShowMessage('Not collected: ' + GetEnumName(TypeInfo(TGorillaInventoryCollectState), Ord(LState))); Exit; end; // update the user-interface FIFrame.UpdateInventory(); end;
Drop
In some cases you'll need a method to release collected items from your inventory. Maybe you have a weight managed inventory and need more space, or you just lose an items due to some story event in your game.
procedure TForm1.MenuItem6Click(Sender: TObject); var LColl : TGorillaInventoryCollectable; LCount : Integer; begin // get the collected item you want to release LColl := [...] // check if it is a pooled item and we want to release all of it LCount := -1; if LColl.Template.IsPooled then begin if (FMX.DialogService.Sync.TDialogServiceSync.MessageDialog( 'Would you like to drop all instances of that item?', TMsgDlgType.mtConfirmation, [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], TMsgDlgBtn.mbYes, 0) = mrNo) then begin LCount := 1; end; end; // drop the item with a supplied count of it FInventory.DropCollectedItem(LColl, LCount); // update the user-interface FIFrame.UpdateInventory(); end;
Manufacture
Manufacture manually
Manually means, you are selecting the itended manufacturer and supply a number of collected items. The Manufacture() method will return a process instance and start building. Depending on your settings this can take some time. Because in most cases you'd like to simulate realistic manufacturing.
You will receive nil as result of Manufacture() if ingredients are missing or supplied items are not suitable.
procedure TForm1.Button4Click(Sender: TObject); var LMan : TGorillaInventoryManufacturer; LProc : TGorillaInventoryManufacturingProcess; begin // manufacture with a specific manufacturer if Assigned(ManufacturerSelector.Selected) then begin LMan := ManufacturerSelector.Selected.Data as TGorillaInventoryManufacturer; if Assigned(LMan) then begin LProc := FInventory.Manufacture(LMan, FInventory.CollectedItems.ToArray()); if not Assigned(LProc) then begin ShowMessage('Some ingredients missing!'); end; end; end else ShowMessage('Please select a manufacturer at first.'); end;
Manufacture automatically
The Gorilla3D inventory component allows to automatically manufacture items by a supplied array of items. This means: It will automatically detect the first suitable manufacturer by the provided items and start the process.
The method returns a process instance in case the manufacturing was started.
procedure TForm1.Button2Click(Sender: TObject); var LProc : TGorillaInventoryManufacturingProcess; begin // start manufacturing process by automatically detecting LProc := FInventory.Manufacture( FInventory.CollectedItems.ToArray() ); // if result was nil, no suitable manufacturer could be found if not Assigned(LProc) then begin ShowMessage('Some ingredients missing!'); end; end;
UserInterface
The inventory component is a non-visual component which needs an user-interface to make your items visible. You can extend the TInventoryFrame, TInventoryGroup and TInventoryCollectedItem [Gorilla.UI.Inventory] component to build your own user-interface.
Class | Description |
---|---|
TInventoryFrame | A custom inventory frame component to manage a inventory system. |
TInventoryGroup | A inventory grouping component. Use this component to categorize your collected items into groups. |
TInventoryCollectedItem | A custom collected item component. Use this component as basis for all of your collected items. |
Visual Items
To allow better abstraction and separation between the logical and the visual part of an inventory, we introduced visual items in v0.8.3.1812+. You can now define 3 levels of detail plus a model filename for each visual item. (This may be extended in future releases.)
A visual item standalone entity inside of the inventory component and can be freely linked by image-index of each item template. You can also link different item templates to the same visual item.
To add a visual item at runtime, use the following code:
/// add a visual representation for an axe GorillaInventory1.AddVisualItem(0, 'Item1', 'item1_lod0.png', 'item1_lod1.png', 'item1_lod2.png', 'axe.obj'); /// add a visual representation for a pickaxe GorillaInventory1.AddVisualItem(1, 'Item2', 'item2_lod0.png', 'item2_lod1.png', 'item2_lod2.png', 'pickaxe.obj');
To remove a visual item, use:
/// add a visual representation for an axe GorillaInventory1.RemoveVisualItem('Item1');
This is also the first step to work together with asset packages. At the moment you can use the “OnLoadResource” callback event of your inventory component. This is getting called on each visual item resource request.
[...] /// define callback event for assets loading GorillaInventory.OnLoadResource := DoOnInventoryLoadResource; [...] procedure TForm1.DoOnInventoryLoadResource(const AInventory : TGorillaInventory; const AResIdx : Integer; const AResource : String; const AItem : TGorillaInventoryVisualItem; const ADestBitmap : TBitmap); var LPckg : TGorillaAssetsPackage; LAsset : TGorillaAsset; LBmp : FMX.Graphics.TBitmap; begin /// get your package previously created LPckg := GorillaAssetsManager1.GetPackage('InMemory'); if not Assigned(LPckg) then raise Exception.Create('package "InMemory" not found'); /// try to find an asset by resource filename + extension LAsset := LPckg.FindAsset(AResource); if not Assigned(LAsset) then raise Exception.CreateFmt('asset "%s" not found', [AResource]); /// check if it really is a texture / image asset if not (LAsset is TGorillaTextureAsset) then raise Exception.CreateFmt('asset "%s" not a valid texture asset', [AResource]); /// copy the image to inventory image (duplication!) LBmp := TGorillaTextureAsset(LAsset).GetBitmap(); try ADestBitmap.Assign(LBmp); finally FreeAndNil(LBmp); end; end;
Inventory-Designer
We offer a free ui-tool for easy inventory design. You can save your designed inventory to file and load it in your application.
The tool provides a sandbox mode, where you can test your item and manufacturer settings directly.
Inventory-Files are also supported in AssetsManager packages.
DesignTime Editor
With v0.8.4+ we've added a design time editor for an inventory component.
You can design your inventory inside the designtime editor, but you cannot store it in the DFM structure of your form. Due to a Bug, it will store a property “Group.Id” which cannot be resolved on loading at runtime.
We recommend to use the editor to design and save as file. Load this file at runtime.
Next step: Dialogues