Inventory

From The DarkMod Wiki
Jump to: navigation, search

This article describes how to set up inventory items and how they can be given to an entity. An entity's inventory can be seen as some sort of "container" for various items. Inventory items are distinguished by their category, such as Tools, Maps, Potions and Weapons (yes, a weapon is also a special kind of inventory item). All entities can have an inventory, even though it may only make sense for AI and the player to actually have one. Also included is a script interface, so that a mapper can interact with the inventory in a more sophisticated way, if the default behaviour is not sufficient.

Definitions

The spawnargs can be either set in the def file as a default, or in the map file to override settings for specific entities. Naturaly this also means that they can be manipulated by scripts as well.

IMPORTANT! Normally items will vanish from the map as soon as the item is successfully put into the inventory. If this doesn't work, then the item will stay visible. This is a intentionally so, to make the mapper aware of the problem. Check the console and/or enable the inventory logclass in the darkmod.log file to see what went wrong.

There are several terms and types of items that can be used in the inventory:

  • Anonymous items An anonymous object means that it will not show up as an item in the inventory. Examples are gold coins and all the other (mostly loot) items that are collected but not being used individually. In this case they only count for the total of loot and are discarded afterwards. Anonymous items also can not be dropped after they are placed into the inventory because they essentially no longer exist. For the loot count it obviously doesn't matter wether an item is anonymous or not, as all are added up into the loot categories. The same holds for ammonition (arrows). These get added to the weapon's ammo and are removed from the map.
  • Stackable items These are items that can be stacked in the inventory. This means that the player can acquire multiple instances of such items, but only one entry is shown in the inventory with a counter of how many currently are available. An example would be a health potion. The player can possess more than one health potion at a time and this would be indicated by the counter on the HUD. Stackable items are identified by setting the stackable property. They also must always use the same name and category. If this doesn't match, they are considered to be different. This also allows for dealing out i.e. fake health potions which have no effect or deal damage instead (as an example).
  • Category Each item has a Category, which is used to group items. This allows the player to faster scroll through the inventory because he doesn't have to cycle through all the individual items. For example a category would be 'Readables' where books and scrolls are stored, and another Category would be 'Tools' where lockpicks and other items can be stored. Note that the actual category names are entirely up to the mapper. It doesn't have any consequence as to the playability and is only a convenience for the player. The inventory category is briefly displayed on the player HUD when cycling through the items.
  • Cursor (This is coding-related information) An inventory can have multiple cursors. Each cursor can point to a different place in the same inventory, and can restrict the movement to certain categories. This way, you can implement different HUDs or GUIs which can show different parts of the inventory in different places even though all the items are in one single inventory. Weapons are handled this way; a separate cursor is used which is only allowed to cycle through the weapon category.

Spawnargs

inv_name | string [required]
Specifies the name the object should display when it is activated in the inventory. Keep in mind that the display name and the category together are used to determine whether two items are of the same type. This is important to determine whether items can be "stacked". Both inv_name and inv_category must match.
Example: "inv_name" "Health Potion"
inv_category | string [required]
Specifies the category that the object should be filed under.
Example: "inv_category" "Potions"
inv_icon | string (D3 path to icon file)
Points to an icon that is to be displayed on the player HUD when the object is selected in the inventory. (This can also point to a shader name.) Note that you don't need to specify the file extension.
Example: "inv_icon" "guis/assets/hud/inventory_icons/icon_health_potion"
inv_stackable | [0/1]
Determines whether an item can be stacked on top of other items of the same type. An example would be the health potion item. The player can pick up more than one health potion, but in the inventory he will only get one item with a counter. Note that loot items need not to be stackable, they are handled separately by special code.
inv_item_id | string
This key can be set by the mapper to a value of his or her choice. This is needed if the mapper wants to address specific items or groups of items in order to implement custom behaviour, even if the inv_name is the same for this group of items. For example, a mapper can create a fake health potion which shows up in the inventory just like a regular one. If the mapper desired, he could set up a script to check for this exact potion and trigger custom behaviour when the item is used.
inv_count | integer number
Defines how many instances of an item is to be added to the inventory. Applies only to items with inv_stackable set to 1. Non-stackable items have always an item count of 1 and this argument is ignored. As an example, the mapper could set up a stack of health potions as single entity (some sort of quiver) which immediately adds 5 potions to the player's inventory.
inv_droppable | [0|1]
If set to 1 the item can be dropped into the map by the player. This will not work for anonymous items (loot, ammo), since their entity will be removed from the map after they had been put into the inventory.
drop_to_hands | [0|1]
If set to 1 (default) the item drops to the hands (grabber manipulator) of the player instead of to the ground. Set to 0 and the item drops directly to the floor when dropped.
inv_loot_type | [ 0 | 1 | 2 | 3 ]
  • 0 = No lootitem (default)
  • 1 = Jewelry/Gems
  • 2 = Gold
  • 3 = Goods
This has to be set in order to define what kind of item it is and if it is a loot item, which category of loot it should account for. Note: it's not possible to choose other values (e.g. loot type = 4), the code will probably crash.
inv_loot_value | integer
Determines the amount of loot this item accounts for. This spawnarg is only applicable for items with a non-zero inv_loot_type.
inv_ammo_amount | integer (default is 0)
Determines whether this item is an ammo item and how much ammonition this item represents. When this argument is 0, the item is not recognised as ammo. Ordinary ammonition items like atdm:ammo_broadhead have inv_ammo_amount set to 1, but it's perfectly valid to set it to higher values for items like quivers. Don't forget to add the inv_weapon_name spawnarg, which is needed by the code to determine which weapon this ammo belongs to.
inv_weapon_name | string
This spawnarg applies to weapon and ammo items. Each weapon must have a unique inv_weapon_name, like "broadhead" or "blackjack". This weapon name is used to determine which weapon an ammonition item is belonging to. If the inv_weapon_name is missing on ammonition, it cannot be recognised as loot (a warning is emitted to the console and the log).
inv_use_on_frob | [0|1] (default is 0)
When this spawnarg is set to 1, it can be used just by hitting the "frob" button (of course it must be selected in the inventory). The code then attempts to "use" this item on the highlighted entity in front of the player. The system is still in the works, so this information might not be fully accurate.
inv_map_start | [0|1]
Determines wether this object should be automatically moved to the inventory at map start.
inv_target | string (entityname, optional, defaults to "player1")
If the inv_map_start argument is set to "1", this defines the name of the entity that should receive it, otherwise the value is ignored. To give an item to the player, you would use player1 as the name (which is the default value anyway).
inv_hud | string (D3 path to .gui file)
If this key is set it should point to a GUI file, which defines the hud for this particular item. As an example, you can look at the file tdm_loot.hud, which implements a custom hud that does not use the regular inventory item HUD. The mapper is responsible to maintain the state for such an item on his own. In order to do this, there is an initialization message sent to the specified script and other messages as well like selecting or deselecting an item. In order to make this work, the mapper must set up the correct scriptobject and add a few events to it. An example description of how to operate a custom HUD will be described in the scripting section on this article.
inv_hud_layer | integer (layernumber)
If a custom hud is used for an item by setting the key inv_hud, this key can be set to specify a layer number the custom hud should be displayed on. Normally this value can be set to 0 or left blank when using the default screen for the player, but for huds on other objects in the map, this might be needed.
inv_persistent | [0|1] (default is 0)
If this is set to "1", the item can be taken to the next map. This is useful for designing campaigns, where important items or weapons can be taken to the next level.
inv_lgmodifier | int (default is 0, maximum is DARKMOD_LG_MAX = 32)
This can be used to modify the lightgem brightness when this item is selected in the inventory. Useful for weapons like the fire arrow.
inv_movement_modifier | float | default is 1, must be positive
This can be used to slow down the player when this item is equipped (e.g. for weapons like the shortsword). This value is multiplied on the maximum movement speed.
inv_no_pickup_message | [0|1] | default is 0
Setting this to 1 disables the "pick up message" when this item is put into the player's inventory.

Default equipment for map start

This describes how to give items to the player at map start time, but the same mechanism can be applied to any other AI (or entity for that matter) as well.

  • First create your object in the editor, just like you would if you wanted to put it in the map. Choose Create Entity and select the inventory item of your choice (a health potion for example). Place it where you like, but not outside in the void.
  • Set the key inv_map_start on the object entity to the value 1.
  • (Optional) Set the key inv_target to the name of the entity you would like this item to go to.

The object will be moved to the target entity's inventory right at map start (and will be hidden or removed). The player will not be able to see the item (unless there was a mistake in the setup).

Inventory CVARs

The following cvars can be used to modify the behaviour of some inventory properties. Some of them are probably only of interest to modders, while others are also of interest to players and might be added to a customization menu.

tdm_inv_hud_file | string | default "guis/tdm_inv.gui"
Specifies the gui file that should be used for the inventory. this can be changed for mods, that wish to implement a different GUI, or provide alternative skins for the player.
tdm_inv_loot_item_def | string | default "atdm
inv_loot_info_item"
Specifies the entityDef that should be used for the loot info slot in the inventory. This entityDef holds the references for the loot HUD.
tdm_inv_hud_pickupmessages | [0|1] | default is 1
If set to 1, the HUD is displaying the item the player has just picked up.
tdm_inv_loot_sound | string | default "frob_loot"
Specifies the default path to the soundfile that is played when loot is aquired. Note that this can be overriden on a per item level, and wont have an effect for such items. (greebo: Should be removed)
tdm_inv_use_on_frob | [0|1] | default is 0 (for now)
When set to '1' the code attempts to use the currently selected inventory on frob, provided the selected item supports this action.
tdm_inv_use_on_frob_visual_feedback | [0|1] | default is 1
When set to '1' the HUD is giving visual feedback when the currently highlighted item is used.

Related CVARs

tdm_hud_opacity | float [0..1]
Defines the opacity of the HUD GUIs, also affects the lightgem and the weapon HUD.


Key bindings

For inventory key bindings see Bindings_and_User_Settings#Inventory_Related


Scriptsupport

The following events are available for scripters. These events can be called on any entity (as every entity has an inventory), but I assume for 99% of the cases, you will want to use these on $player1.

scriptEvent float getLootAmount(float Type);
Returns the amount of loot for the given type (e.g. LOOT_GOODS). There are a few loot constants defined in tdm_events.script file. Pass LOOT_TOTAL to return the sum of all loot types.
  • LOOT_TOTAL
  • LOOT_JEWELRY
  • LOOT_GOLD
  • LOOT_GOODS
scriptEvent float changeLootAmount(float type, float amount);
Changes the loot amount of the given Type (e.g. GOODS) by <amount>. The new value of the changed type is returned (e.g. the new GOODS value if this has been changed). Note: The LOOT_TOTAL type can't be changed and 0 is returned.
scriptEvent void addInvItem(entity inv_item);
Adds the given item to the inventory. Depending on the type, the passed entity will be removed from the game (as for loot items) or hidden. If the item couldn't be added, a warning is emitted to the console.
scriptEvent float replaceInvItem(entity oldItem, entity newItem);
Replaces the entity <oldItem> with <newItem> in the inventory, while keeping <oldItem>'s inventory position intact.
Note: The position guarantee only applies if <oldItem> and newItem share the same category. If the categories are different, the position of <newItem> is likely to be different than the one of <oldItem>.
Note that <oldItem> will be removed from the inventory. If <newItem> is the $null_entity, <oldItem> is just removed and no replacement happens.
Returns: 1 if the operation was successful, 0 otherwise.
scriptEvent entity getNextInvItem();
scriptEvent entity getPrevInvItem();
Cycles the standard cursor to the next/prev inventory item.
Returns: the item entity pointed to after the next/prev operation is complete.
scriptEvent float setCurInvCategory(string categoryName);
Sets the inventory cursor to the first item of the named category.
Returns: 1 on success, 0 on failure (e.g. wrong category name).
scriptEvent entity setCurInvItem(string itemName);
Sets the inventory cursor to the named item.
Returns: the item entity of the newly selected item (can be the $null_entity).
scriptEvent string getCurInvCategory();
Returns the name of the currently highlighted inventory category.
scriptEvent entity getCurInvItemEntity();
Returns the currently highlighted inventory item entity.
scriptEvent string getCurInvItemName();
Returns the name of the currently highlighted inventory item (the one defined in inv_name).
scriptEvent string getCurInvItemId();
Returns the name of the currently highlighted inventory item (the one defined in inv_item_id). Most items will return an empty string, unless the "inv_item_id" is set on purpose.
scriptEvent string getCurInvIcon();
Returns the icon of the currently highlighted inventory item.
scriptEvent void changeInvItemCount(string name, string category, float amount);
Decreases the inventory item stack count by amount. The item is addressed using the name and category of the item. These are usually defined on the inventory item entity (inv_name, inv_category). Amount can be both negative and positive.
scriptEvent void changeInvLightgemModifier(string name, string category, float modifier);
Sets the lightgem modifier value of the given item. Valid arguments are between 0 and 32 (which is the maximum lightgem value).
scriptEvent void changeInvIcon(string name, string category, string icon);
Sets the inventory icon of the given item in the given category to <icon>.

Example

A short example of the above script events in action is the lantern script:

// Set the icon to the "on" icon
userEntity.changeInvIcon(getKey("inv_name"), getKey("inv_category"), getKey("inv_icon_on"));

The first call is retrieving the inv_name and inv_category spawnargs from the lantern entityDef and takes them as argument for te changeInvIcon call. The inv_name and inv_category are necessary for the code to lookup the item in the player's inventory. The third parameter defines which icon the lantern inventory item should have from now on.

Custom HUDs

It is possible to implement a custom HUD for each inventory item. Examples for inventory items with custom HUDs are the player compass and the loot info item. This describes how to set up a custom HUD for your item.

  1. First off, you need an entityDef with your inventory item. Look at atdm:playertools_compass for an example.
  2. Your inventory item entity needs to have its own script object, so you'll need to name that in your entityDef using the "scriptobject" spawnarg.
  3. The scriptobject (which will be defined in a .script file) needs to implement a small set functions which are invoked by the code when the item is initialised, selected or de-selected.

Set up the entityDef

Again, the compass is given as an example:

entityDef atdm:playertools_compass
{
    "inherit"				"atdm:playertool"
    "spawnclass"			"idStaticEntity"

    "editor_usage"			"Compass"

    "model"				"models/darkmod/player_equipment/compass.ase"
    "scriptobject"			"playertools_compass"

    "inv_name"				"Compass"
    "inv_category"			"Compass"
    "inv_map_start"			"1"

    "inv_hud"				"guis/playertools/compass.gui"
    "inv_hud_layer"			"11" // greebo: Readables are on layer 10
    "inv_icon"				""
    "inv_droppable"			"0"
}

The compass is set up like any other inventory item - the important thing is to specify the inv_hud and inv_hud_layer spawnargs. The inv_hud argument is pointing to a sepcific GUI file, which will be displayed when the inventory item is selected by the player. The inv_hud_layer defines on which layer the GUI is being drawn (GUIS with higher overlay numbers are drawn after the ones with lower numbers).

Also note that scriptobject spawnarg which points to the script class, in this case playertools_compass. See the next chapter for information about how to set this up.

Set up the scriptobject

As next step, you'll need to add a custom scriptobject which defines the methods to be called by the SDK code. Create a .script file and define a scriptobject with the following methods:

/*
 * Compass scriptobject, derives from player_tools
 */
object playertools_compass : player_tools
{
	// greebo: Gets called upon addition to the inventory
	void inventory_item_init(entity userEntity, float overlayHandle, string overlayName);	

	// Gets called when the player switches to another inventory item 
	void inventory_item_unselect(entity userEntity, float overlayHandle); 

	// Gets called when this item is selected in the inventory
	void inventory_item_select(entity userEntity, float overlayHandle);

	// Gets called to update the HUD (not each frame!)
	void inventory_item_update(entity userEntity, float overlayHandle);
};

As you can gather from the comments, the various functions are called by the SDK code on certain occasions. The userEntity argument always defines the entity which holds the inventory item, which is the $player1 entity in almost all cases. The second parameter overlayHandle defines the GUI handle which can be used to set the GUI state variables of the custom HUD. These depend on your implementation, of course. I won't go into the details here (you can look at the actual files in the scripts/ folder), but as an example I can pick the inventory_item_select() method of the compass scriptobject:

void playertools_compass::inventory_item_select(entity userEntity, float overlayHandle)
{
	_overlayHandle = overlayHandle;

	// Disable the default inventory
	userEntity.setGuiFloat(userEntity.getInventoryOverlay(), "Inventory_ItemVisible", 0);

	// Make the compass GUI visible
	userEntity.setGuiFloat(_overlayHandle, "CompassVisible", 1);

	// Start a new thread 
	_updateThreadNum = thread updateLoop(userEntity);
}

The interesting part is how to set the GUI state variables, which is easy: just use the setGui*** methods on the userEntity (usually the player), passing the overlayHandle along to specify which HUD you are addressing.

Setting up Weapons

Setting up weapons is not that hard actually from the entityDef point of view. The hard part is to get the models and animations right, which is mostly artwork. However, this short chapter should explain how the various entityDefs are connected to each other.

Weapon

First of all, you'll need a weapon entityDef, one that is inheriting from atdm:weapon_base. The entityDef needs a unique inv_weapon_name serving as identifier for ammunition and scripting. The atdm:weapon_base carries some editor_* spawnargs which explain the various options you have to "configure" your weapon (like whether ammunition is required, or whether it is a toggleable weapon, or the scriptobject, etc.).

Let's assume you set up a weapon with ammunition (i.e. the weapon carries the spawanrg "ammo_required" "1"). In this case you'll need a couple of additional entityDefs, namely ammo, attachment, projectile and result entities.

Ammo

For weapons requiring ammunition, you'll need to add the def_ammo spawnarg, which should point to an entityDef inheriting from atdm:ammo_base. This links the weapon to the correct ammunition entityDef. To link back the ammo to the weapon, add the inv_weapon_name spawnarg to the ammo entityDef pointing back to the weapon (e.g. "broadhead").

The Weapon Attachment

Each arrow weapon in TDM needs an attachment entity for display during the bow animations. For instance, whenever the player selects the water arrow weapon and starts to draw the bow string, the entity of type atdm:attachment_waterarrow is attached to the bow viewmodel. The name of the attachment entityDef is defined on the weapon with the spawnarg def_attach.

The Projectile

Once the player releases the bow string, the projectile is fired. The def_projectile spawnarg on the weapon points to the desired projectile entityDef.

The Projectile Result

The launched projectile will impact somewhere, and this is the point in space where the so-called projectile result will be spawned. The projectile entityDef holds the spawnarg def_result pointing to the correct result entityDef. The result in turn has to implement the correct script methods which are called by the projectile collision code in the SDK. These script methods on the result's scriptobject will implement the actual behaviour of the arrow (like playing sounds, deploying ropes, etc.).

How to make inventory items to interact with world objects

Kingsal has reported how to use inventory items on items frob-highlighted in the game world. The report is here: http://forums.thedarkmod.com/topic/18505-script-for-placing-the-right-object-in-the-right-place/?p=397333

This is a custom set up for an inventory item you would like the player to use_on_frob when highlighting an object in the world. The scenario in this example is placing a skull on a pedestal, but there are many different uses/ scenarios. There are several ways to do this. Check out http://wiki.thedarkmod.com/index.php?title=Tool,_Key,_custom_used_by_inventory_actions for another method.

You will need the def files and script file below.

This assumes you have basic knowledge of creating .def files and .scripts. If not. PM me (kingsal) for help.

Def files. Place this in your def file.

//++++++++++++++++++++++++Use Inventory Item++++++++++++++++++++++++++++++ // The inventory_use_item's "inv_name" spawnarg MUST match the "item_name" spawnarg on the Altar.

entityDef inventory__use_item //Change the name if you'd like
{
	"inherit"							"atdm:moveable_small_base" 
	//editor junk
		"editor_displayFolder"			"interactables/special" // Change to a location you'd like it to show up in DR.
		"editor_usage"					"Custom use-on-frob item"
		"editor_color"					"0.922 0.039 0.855" 
		
	//Model
		"model"							"models/darkmod/graveyard/bones/skull2.ase" // Change to appropriate model. Should have a collision mesh.
		"mass"							"10" // If item can be dropped, Set this higher to avoid the player from lobbing it too far.
		"friction"						"0.7"
		"solid"							"0"
	
	//Inventory stuff
		"inv_name"            			                "custom item name" //Rename it something fun! MUST match the name on the Altar.
		"inv_icon"			                        "guis\assets\hud\inventory_icons\skull_inv_icon" // GUI icon.
		"inv_droppable"         		                "1" //Allows the player to drop this. Beware they might drop it someplace where it can't be retrieved. 
		"inv_category"					        "Custom Items" //Rename this to the appropiate category.
		"inv_stackable"						"1" //This is only needed if their are multiple of these items that you want to stack in the inventory.
		
	//sound
		"snd_acquire"					"tool_pickup"
		"snd_bounce"					"tdm_impact_metal_med_lesser"
		
	
}

//++++++++++++++++++++++++Altar++++++++++++++++++++++++++++++

entityDef use_item_altar //Change the name if you'd like
{
	"inherit"					        "atdm:entity_base"
	"spawnclass"						"idStaticEntity"

	//editor junk
		"editor_displayFolder"			        "interactables/special" // Change to a location you'd like it to show up in DR.
		"editor_usage"					"Altar for placing inventory_use_items"
		"editor_color"					"0.922 0.039 0.855"
		
	//Model
		"model"							"models/darkmod/furniture/tables/builder_pedestal01.lwo" //Change to appropriate model
		"solid"							"1" //Must be solid

	//Interaction
		"frobable"						"1"
		"frob_action_script"			                "use_altar"	
		"item_name"						"custom item name" //This must match the inv_name spawnarg of the custom item
}

Script. This is the script that goes in your mymap.script. Or fms/scripts which requires adding the script name to your tdm_custom_script file.

/////////// EVENT SCRIPTS //////////////
//Place this in your fms/maps/ as a mymapname.script.
void use_altar(entity altar)
{
		
    entity curr_item = $player1.getCurInvItemEntity();
    if ($player1.getCurInvItemName() == altar.getKey("item_name"))
		{
			$player1.changeInvItemCount(curr_item.getKey("inv_name"), curr_item.getKey("inv_category"), -1); //Removes item count by one.
			altar.activateTargets( $player1 ); //Activates the altar's targets. 
			altar.setFrobable( 0 ); //Sets to none frobable. One time use. REMOVE THIS TO MAKE RE-USABLE.
			altar.startSoundShader ("mage_fireball_impact", SND_CHANNEL_VOICE ); //Play a sound you like!
			sys.wait( 1 );
		}
		
}

Set-up

By default the custom items can be found in DR under yourfms/interactables/special. Unless you changed the path


1. Add the inventory_use_item and use_item_altar to your map.

2. Be sure to change the "inv_name" spawnarg on the custom item and the "item_name" spawnarg on the altar. They must match to work.

3. Place a func_static of the item's model on the altar and set spawnarg "hidden" to "0". Have the altar target the func_static and it will appear, giving the illusion it was placed there.