Introduction
The documentation for newer versions of the asset has been moved to a new location!
Click here to be redirected.
New versions of exINV are released for Game Maker: Studio 2 only. For the GMS 1 version, please refer to the
legacy documentation
About
exINV is a general purpose inventory system that provides ready to use assets to manage an item database,
multiple inventories and a comprehensive user interface built on top of that,
including features like crafting and equipment selection and saving / loading mechanisms.
exINV can be seen as a collection of three main modules: Database system,
Inventory system and User Interface & Logic (UI).
It is important to understand that while the Database system & the Inventory system are generic enough to be
used as they are in any project, the UI and its logic may differ greatly from one project to another,
depending on the desired result.
The UI and the other modules are, however, totally decoupled. This means that you can safely import all the
scripts into any project and they'll work out of the box, since they do not reference any pre-defined object.
The provided UI instead is a structured and commented example built on top of that; you will need to edit
and adapt it to your needs, or simply start from scratch and build your own.
Getting started
Before integrating the asset into an existing project, you are strongly encouraged to test it out
by importing everything into a blank project, and running the demo to get a feel of what you can do and
expect beforehand.
Integrating exINV into your existing project requires a few steps, described below:
1. Importing the "ex" folders
While you may not need everything included in the package, it is strongly suggested to work in a subtractive
way, by adding all ex folders from the package resource tree to your game, and edit / remove what
you don't need afterwards. All the assets in those folders are namespaced so you should not find any name clash
with your current resources.
The few assets outside of those folders are part of the demo. You are encouraged look at them, but are not meant
to be imported directly into your project.
2. Initializing the system
The first thing you have to do is initialize the inventory system. You should only do that once, ideally at game start.
In the demo project this is done in obj_init_rm_controller, in your project you should add this code in
your game initialization routine. Note that you need to provide the full sandbox path to your CSV files.
ex_init();
ex_db_load("ex/inv_armor.csv","ex/inv_food.csv","ex/inv_potions.csv","ex/inv_weapons.csv");
ex_craft_init();
3. Creating an inventory
Once the system is initialized, you may start creating inventories.
global.inv_player = ex_inv_create(30);
4. Inserting items
Items are identified by a unique item key (as string). This and the other item properties are defined
in the item database loaded in step 2.
ex_item_add(global.inv_player,"weapon_fire_arrow",64);
ex_item_add(global.inv_player,"weapon_wooden_bow",1);
5. Displaying the inventory
So far we loaded the item database, created an inventory, and added some items in it.
But this is all handled in memory, if you try to run the code above, you will not see anything
graphically. This is because inventory managament and UI are decoupled and mostly independent.
The next step therefore is to display the inventory we just created, by creating an instance of a
panel that is able to diplay that specific inventory.
ex_ui_panel_show_layer(global.inv_player,obj_inv_panel_backpack,32,32,"inventory_panels");
You are strongly encouraged to look at the provided demo and go through every event of obj_inv_controller,
since this is where most of the above (and a lot more) is defined, and use that to build your own.
It is also worth noting that inventories (as created by ex_inv_create()) are to be treated exactly like data structures. This means that inventory data
is persistent across room changes, and needs to be released from the memory when not needed anymore by using ex_inv_destroy().
You should therefore avoid creating new player inventories from scratch in every room, just be sure to do so in the
first room of a new game and use panel objects to display them in one or more rooms.
6. Customize
The next step is to customize the inventory system to your needs. Some obvious things you may want to change are:
- Modify the item database defined in the included CSV files
- Edit the default sprites
- Extend the crafting system and define the recipes
- Edit the behavior of the default panels
Inventory system
Introduction
An inventory is a special data structure holding a predefined number of slots. Like regular data structures, they are kept in memory as long as the program
is running or the inventory explicitly destroyed with ex_inv_destroy();
While inventories are meant to be displayed and interact with the player input, in exINV they are not tied to a visual representation,
since that's the UI role. You can create and perform all kind of operations on inventories without ever displaying them.
The only exception to the above is represented by the script ex_inv_updated, which is called every time
an inventory changes, and includes the code required to update the UI objects accordingly (if present).
Inventories
The following scripts exist to deal with inventories.
Scripts reference
Clears an inventory by deleting all its contents, but does NOT destroy the inventory iteself.
inv {integer} inventory to clear
ex_inv_clear(global.inv_player);
Returns the amount of items matching the provided key.
inv {integer} inventory
key {string} key of the items to count
returns {integer} number of items matching the key
var amount = ex_inv_count(global.inv_player,"food_cheese");
Creates a new empty inventory having the specified number of slot
size {integer} number of slots
returns {integer} the inventory id
global.inv_player = ex_inv_create(30);
Destroys an inventory and all its contents. This generates an inventory_destroyed event for the related UI panels,
automatically closing them.
inv {integer} inventory to destroy
ex_inv_destroy(global.inv_player);
Returns the total number of slots of an inventory, either empty or full. Use ex_inv_size to get
only the number of non-empty slots.
inv {integer} inventory
returns {integer} total number of slots
var free_slots = ex_inv_max_size(global.inv_player) - ex_inv_size(global.inv_player);
Creates and returns an inventory from a string generated using ex_inv_write.
data {string} string containing inventory data
returns {integer} the inventory id
ex_inv_destroy(global.inv_player);
global.inv_player = ex_inv_read(save_data);
Resizes an inventory by adding or removing slots according to the new size. Items in extra slots are removed.
This function generates an inventory_resized event on its open UI panels. By default panels create or destroy the slot
instances according to the new size, but you should probably override this event in your panel objects according to the
behavior you expect.
inv {integer} inventory to resize
new_size {integer} new size
ex_inv_resize(global.inv_player,8);
Returns the number non-empty of slots of an inventory. Use ex_inv_max_size instead
if you need the amount of (either empty or filled) slots of an inventory.
inv {integer} inventory
returns {integer} number of non empty slots
var free_slots = ex_inv_max_size(global.inv_player) - ex_inv_size(global.inv_player);
Sorts the inventory base on a specific attribute. When sorting, items will always have higher priority
over empty slots.
sort_by can either be an item attribute as defined in the database like "name" or "key", or
a meta value like the stack amount. In the first case, you just pass the attribute as a string. In the second,
you'll need to use a value from the EX_COLS enum. The most useful are:
EX_COLS.index - Sorts by slot index
EX_COLS.key - Sorts by item key
EX_COLS.amount - Sorts by stack amount
inv {integer} inventory to sort
sort_by {integer|string} sort attribute
order {boolean} if true, sorts in ascending order, descending otherwise
ex_inv_sort(global.player_inv,"name",true);
This script gets called whenever an inventory changes in some way. You should not call this script directly.
If you are implementing your own UI on top of exinv, you should adapt this script to suit your needs.
event {integer} update event (from EX_EVENTS enum)
[affected_slots] {ds_list} list of slots affected by the update (only for EX_EVENTS.slots_updated)
Returns a JSON encoded string of the specified inventory. You can use the output of this function
to serialize the inventory to file, and read it back using ex_inv_read.
inv {integer} inventory to encode
returns {string} a JSON string representation of the provided inventory
var data = ex_inv_write(equipment_inv);
var file = file_text_open_write("equipment.inv");
file_text_write_string(file,data);
file_text_close(file);
Inventory items
An item in an inventory slot is a collection of 6 values, all of which can be accessed using an appropriate function:
-
index: the slot index (number) where the item is stored (starts at 0, being the first slot)
-
amount: the amount of items in the slot (stack). Default: 0
-
key: the key of the item. Default: ""
-
item: a reference to the item data (a ds_map) in the database. Default: -1
-
stack_id: a string used to test item stacking (more on that in the tags section). Default: ""
-
tags: a ds_map holding the specific item tags (if any). Default: -1
You can access those properties at any time using the ex_item_get_* functions.
It is important to note that every item of the same type (that is, having the same key), references the same data in the database.
You should not change the item data in any way, as this would change the database directly. If you want to give an item some
extra attributes, you should use tags.
Scripts reference
Returns an attribute at the given slot index of the specified inventory.
inv {integer} inventory
slot {integer} slot index
returns the attribute at the specified slot
var item = ex_item_get_item(global.inv_player,0);
if(item >= 0) {
show_debug_message("Name of the item in slot 0: " + item[? "name"]);
}
else {
show_debug_message("The slot is empty!");
}
Inserts an item into an inventory in the first available slot(s), or in the specific slot.
If a slot is specified and the item you are trying to add can not be stacked, the items are not added (
or only partially if reaching stack limit).
inv {integer} inventory
key {string} key of the item to insert
amount {integer} amount of items to insert
[slot] {integer} (optional) slot to insert the item(s) into. Default: -1
[tags] {ds_map} (optional) a ds_map holding the item tags.
returns {integer} the number of items inserted (same as amount given enough space in the inventory or slot)
IMPORTANT: you can optionally assign tags to an item directly, by passing a ds_map to this function.
if you do that, the inventory always stores a copy of the ds_map. You are free to edit or destroy this map
afterwards without affecting the inventory.
var n = ex_item_add(global.player_inv,"food_carrot",16,3);
show_debug_message(string(n) + " carrots have been inserted in slot 3 of the toolbar!");
Clears (removes) the content of a slot
inv {integer} inventory
slot {integer} slot index
ex_item_clear(global.inv_player,0);
Adds an item to an inventory or slot by copying the contents from another inventory.
Tags are copied as well. inv1 and inv2 do not necessarily need to be different,
it is possible to copy items from one slot to another on the same inventory.
inv1 {integer} inventory to copy the item from
slot1 {integer} slot index in inv1
inv2 {integer} inventory to copy the item to
amount {integer} amount to copy. If set to -1, all items in slot 1 are copied
[slot2] {integer} (optional) slot index in inv2. If omitted, the item will be copied in the first available slot.
returns {integer} the amount of items actually copied.
ex_item_copy(global.inv_player,0,global.inv_toolbar,-1);
Returns the index of the first slot holding an item with the specified key.
When multiple copies of the item having the specified key are present, the result
depends on the find type applied. Find types are defined in the EX_FIND enum, as follows:
EX_FIND.first: returns the first slot index found
EX_FIND.last: returns the last slot index found
EX_FIND.low: returns the slot index having the lowest amount of items in the stack
EX_FIND.high: returns the slot index having the highest amount of items in the stack
inv {integer} inventory
key {string} key of the item to look for
find_type {integer} type of search operation defined by the EX_FIND enum
returns {integer} the slot index if found, or -1 if nothing is found.
var slot = ex_item_find(global.inv_toolbar,"food_carrot",EX_FIND.first);
if(slot >= 0) {
show_debug_message("Found some carrots in slot " + string(slot));
}
else {
show_debug_message("No carrots found");
}
Moves a specific amount of items (or stack) in a slot to another inventory (or another slot in the same
inventory).
inv1 {integer} inventory to move the item from
slot1 {integer} slot index in inv1
inv2 {integer} inventory to move the item to
amount {integer} amount to move. If set to -1, all items in slot 1 are moved
[slot2] {integer} (optional) slot index in inv2. If omitted, the item will be moved in the first available slot.
returns {integer} the amount if items actually moved.
ex_item_move(global.inv_toolbar,0,global.inv_player,-1);
Removes item(s) from an inventory, or a specific slot
inv {integer} inventory
key {string} key of the item(s) to remove
amount {integer} amount of items to remove. If set to -1, removes all items from a slot (if specified), or all items having that specific key in the inventory.
[slot] {integer} (optional) slot to remove the item(s) from
returns {integer} the amount if items actually removed
var n = ex_item_remove(global.inv_player,"food_carrot",8);
show_debug_message(string(n) + " carrots have been removed from the inventory!");
Inserts an item into an inventory slot, replacing any previous content.
inv {integer} inventory
key {string} key of the item to set
amount {integer} amount of items to add
slot {integer} slot to set items into
[tags] {ds_map} (optional) a ds_map holding the item tags.
returns {integer} the amount if items actually inserted
IMPORTANT: you can optionally assign tags to the item directly, by passing a ds_map to this function.
If you do that, the inventory always stores a copy of the ds_map. You are free to edit or destroy this map
afterwards without affecting the inventory.
ex_item_set(global.inv_toolbar,"item_carrot",1,0);
Swaps the items of the specified slots (either in the same inventory or different ones)
inv1 {integer} first inventory
slot1 {integer} slot in the first inventory
inv2 {integer} second inventory
slot2 {integer} slot in the second inventory
ex_item_switch(global.inv_toolbar,0,global.inv_toolbar,1);
Returns the items that would be added by calling ex_item_add() with the same arguments, but doesn't actually
add them to the inventory.
inv {integer} inventory
key {string} key of the item to test
amount {integer} amount of items to test
[slot] {integer} (optional) slot to test the item(s) insertion. Default: -1
[tags] {ds_map} (optional) a ds_map holding the item tags.
returns {integer} the number of items that would be inserted by ex_item_add
Returns the items that would be added by calling ex_item_copy() with the same arguments, but doesn't actually
add them to the inventory.
inv1 {integer} inventory to copy the item from
slot1 {integer} slot index in inv1
inv2 {integer} inventory to copy the item to
amount {integer} amount to copy. If set to -1, all items in slot 1 are copied
[slot2] {integer} (optional) slot index in inv2. If omitted, the item will be copied in the first available slot.
returns {integer} the number of items that would be inserted by ex_item_copy
Tags
As mentioned in the previous sections, item properties in the database are to be considered immutable.
An item having a certain key shares the same attributes as all the other items having that same key.
It is common though for specific items to have some unique and independent properties that need to be set at runtime:
that's where tags come into play. Tags can be assigned to any item or stack in a specific slot and allow defining any number
of attributes specific to that item or stack.
While there are some functions allowing tags manipulation, it is worth keeping in mind that tags for a specific slot are stored in
a ds_map. When getting the tags of a slot using ex_item_get_tags(), you are in fact returning a ds_map (or -1 if no tags are set) and
you free to read them as you normally would using
ds_map functions and accessors. You should definitely avoid though altering the ds_map directly
in any way, due to stacking.
Tags vs stacking
In short, two items having the same key but with different tags will not stack, ever. If both items have no tags, or the tags
happen to be exactly equal, they will stack as usual (if the item is stackable to begin with).
It may be worth knowing how the above works in practice, since comparing ds_maps is a slow process and you may be concerned about
performance.
Whenever you set or change the tags of an item using the tag scripts, the contents of the ds_map are serialized in a string,
hashed using MD5 and the result is stored in the stack_id property of the inventory slot. This property is then
used to tell when to stack or not stack items when moving them around.
You are free to change how the stack id is generated by editing the _ex_prv_generate_stack_id script.
Scripts reference
Deletes the tag from the item (or stack) in the given slot
inv {integer} inventory
slot {integer} slot index
tag {string} tag to remove
ex_tag_delete(global.inv_player,0,"fire_damage");
Returns true if the tag exists for the item (or stack) in the specified slot
inv {integer} inventory
slot {integer} slot index
tag {string} tag
returns {boolean}
if(ex_tag_exists(global.inv_player,0,"fire_damage")) {
var fd = ex_tag_get(global.inv_player,0,"fire_damage");
show_debug_message("Fire damage: " + string(fd));
}
Returns the value of the specified tag.
inv {integer} inventory
slot {integer} slot index
tag {string} tag to return
returns the tag value
var durability = ex_tag_get(global.inv_toolbar,0,"durability");
if(durability <= 0) {
ex_item_clear(global.inv_toolbar,0);
}
Sets the value for the specified tag, creating the tag if it doesn't exist, or replacing the value of the existing tag.
inv {integer} inventory
slot {integer} slot index
tag {string} tag
value {any} value
var durability = ex_tag_get(global.inv_toolbar,0,"durability");
ex_tag_set(global.inv_toolbar,0,"durability",durability-1);
Replaces ALL the tags of an item (or stack) in a slot with the values in the provided ds_map
inv {integer} inventory
slot {integer} slot index
tags {ds_map|integer} new tags as ds_map, or -1 to remove the current tags
Returns the number of tags for the item in the given slot
inv {integer} inventory
slot {integer} slot index
returns {integer} number of tags for the item
User Interface
Introduction
The user interface determines how inventories are displayed to the user, and all the logic that handles how
the inventory behaves when the user interacts with them.
As such, while the inventory system and database system are generally project agnostic, the UI is instead an opinionated resource, and differs
greatly from game to game, not just in appearance but in functionality too. exINV provides a flexible base for you to work on top, with some common inventories
like crafting, player inventory, toolbar and equipment management, but it's up to the programmer to refine or rewrite this part based on his needs.
It is important to understand that the inventory itself and the UI are decoupled, meaning that there is no reference to
objects or instances inside ex_inv scripts, with the notable exception of ex_inv_updated . This is the script
that ties UI and inventories, and is in charge of notifying the UI that something changed inside the inventories.
The UI is managed and displayed by using 3 main objects, found in the ex/core/ folder: Panels (obj_inv_panel), Slots (obj_inv_slot), Mouse, (obj_inv_mouse).
Panels
Panels are containers that handle the general logic of an inventory and its appearance, and are in charge
of maintaing a number of slot instances associated with the inventory.
obj_inv_panel defines the shared default logic of ALL of your inventories. In general, you do not want create instances
of obj_inv_panel directly; instead, every inventory should have its own panel object, as a child of obj_inv_panel, that overrides / defines
its specific behavior. Knowing how inheritance works in Game Maker is fundamental to customize and create panels.
Showing / hiding panels
The following scripts are available to show / hide panels:
Hides (destroys) a specific panel instance or object.
panel {object|instance} either an instance of a panel (to close that specific instance),
or an object, closing ALL panel instances belonging to that object.
Displays a panel at the specified coordinates at the specified depth.
inv {integer} inventory id
panel_obj {object} panel to show
x {integer} x position of the panel
y {integer} y position of the panel
depth {integer} depth at which the panel is instantiated
returns {instance} The panel instance
Displays a panel at the specified coordinates on the specified layer.
inv {integer} inventory id
panel_obj {object} panel to show
x {integer} x position of the panel
y {integer} y position of the panel
layer {string} layer where the panel is instantiated
returns {instance} The panel instance
It is worth noting that when opening / closing panels we are in fact creating and destroying instances of the
specific panel objects, without affecting the inventory data.
Opening a panel returns the panel instance, that by default lets you access the following variables:
inv: The inventory id referenced by the panel
slots: An array of slot instances that matches the size of the slots in inv
Defining new panels
The minimum requirement when creating a new panel object is to implement the "create slot" event (event user 0). This event
is in charge of creating the right amount of slots in the right position.
The folder ex/panels/ provides a few useful examples you can use to build your own logic. The most
basic one being obj_inv_panel_backpack, that simply implements the create slots event.
More complex examples are provided, like the equipment panel, where restrictions are applied to specific slots in
order to allow only the right type of item for each armor piece.
Panels events
Panels work by defining an implementation for specific events.
Those are defined by the INV_PANEL_EVENTS enum (in the create event) which references user events that get triggered automatically at specific times.
A panel needing a specific action for an event should override the default event, replacing the default
behavior defined by obj_inv_panel (if present).
Create slots event (event_user_0): Called right after the panel is created. Slot instances have to be created in this event.
Inventory refresh event (event_user_1): Called once after the inventory changes in some way. This is used for example in obj_inv_panel_equipment to update the armor rating whenever something some equipment is added / removed to the inventory
Inventory resized event (event_user_2): Called when the inventory gets resized by ex_inv_resize(). By default, destroys all the current slots and calls the create slots event again.
Inventory Destroyed event (event_user_3): Called when the inventory gets destroyed by ex_inv_destroy(). By default, destroys the panel instance and the slots.
Slot updated event (event_user_4): Called when a slot is updated. In this event, you can use other to refer to the specific slot.
Slot left pressed (event_user_5): Called when a slot is left clicked. In this event, you can use other to refer to the specific slot.
Slot right pressed (event_user_6): Called when a slot is right clicked. In this event, you can use other to refer to the specific slot.
Extending panels events
Panel events are meant to be extended based on the functionalities you need. If, for example, you want something to happen when the middle mouse button
is pressed on a slot, you'll have to:
-
1. add the event to the INV_PANEL_EVENTS enum in obj_inv_panel create event.
-
2. Add a middle mouse pressed event to obj_inv_slot that calls the event for the panel: with(panel) {event_user(INV_PANEL_EVENTS.slot_middle_pressed)}
-
3. Add the event user associated to the event number defined in step 1 to obj_inv_panel, and add the code that should be executed when this event gets called.
If the code is not common to all or most of the panels, leave it blank and implement it in the specific
panels that need it.
Slots
Panels hold a collection of obj_inv_slot instances visually representing the items of the inventory, and also in charge of detecting the user interaction (left click, right click, etc.).
Slot instances are generally created in the "create slot event" of the panel, using one of the following scripts:
Creates and returns a slot for the specified panel.
panel {instance} the panel instance the slot is associated to
index {integer} index of the slot referenced in the inventory (starting at 0)
x {integer} x position of the slot relative to the panel
y {integer} y position of the slot relative to the panel
depth_or_layer {integer|string} depht (as a real) or layer (as a string) where the slot instance is created
slot_object {object} slot object to instantiate. Usually obj_inv_slot, but you may define different kinds of slot objects if you need to.
returns {instance} The slot instance
Automatically generates ALL the slots for a panel, arranging them in a grid layout.
panel {instance} the panel instance the slots are associated to
slot_object {object} slot object used to instantiate. Usually obj_inv_slot, but you may define different kinds of slot objects if you need to.
columns {integer} how many slots per row to generate, before going to the next row
distance {integer} distance between slots in pixels (from origin to origin)
[x_offset] {integer} (optional) x position of the first slot relative to panel origin. Default: 0
[y_offset] {integer} (optional) y position of the first slot relative to panel origin. Default: 0
[depth_or_layer] {integer|string} (optional) depht (as a real) or layer (as a string) where the slot instances are created
returns {instance} The slot instance
Slot instances are automatically updated to reflect the contents of the inventory slot they are linked to.
You are able to access the following variables:
item: Reference to the database ds_map holding the item properties (-1 if empty)
key: Key of the item currently in the slot ("" if empty)
amount: Number of items in the slot (stack)
index: Index of the slot in the inventory
tags: Tags ds_map associated to the inventory item (-1 if no tags defined or slot empty)
rel_x: x position of the slot relative to the panel
rel_y: y position of the slot relative to the panel
panel: Rerefence to the panel instance holding the slot instance
inv: Reference to the inventory
It is important to understand that slots are slaves with respect to inventory data, changing for example
the item, amount or key variables of the slot instance will do absolutely nothing to the inventory itself.
Slots are automatically updated whenever the inventory changes.
Mouse
While not required, by default exINV assumes that the UI is managed by the player using the mouse, in charge of temporarily holding
the items being moved around.
obj_inv_mouse defines all the required logic to handle this.
obj_inv_mouse is in fact
programmed as a regular panel having a single slot. This means that, if you want to use the mouse in your game, you are in
charge of creating (and destroying) the mouse inventory yourself using ex_inv_create(1);. Like other panels, you are also supposed
to open / close the mouse panel whenever necessary.
Inventory controller
Unlike previous versions of exINV, in the current version the use of an inventory controller is not enforced anymore.
You are still encouraged to create one such object to manage all the high level logic though, and in fact the ex/demo/ folder
includes an example implementation (obj_inv_controller) that does all the heavy lifting of creating, opening, closing and positioning
of the UI panels in the room.
Other functionalities
Crafting system
Crafting is a broad term referring to in game item creation, generally by consuming other items. There is no single crafting system nor
implementation that fits every game, therefore the provided cafting system is simply an example of how this can be achieved using
exINV.
Crafting recipes are defined at game start by the script ex_craft_init(), which generates a recipes database,
each recipe holding the required items and the resulting item (as well as its quantity).
The panel obj_inv_panel_craft is in charge of checking the recipes and eventually produce the resulting item,
by consuming the recipe items.
It defines 4 slots that are dedicated to ingredients, and a special slot where the resulting items are generated.
The panel defines a very specific logic for the result slot, prohibiting
anything from being inserted there and consuming ingredients when the player actually get something out of it.
obj_inv_panel_craft also generates and keeps an updated ds_list of item keys based on the 4 ingredient
slots, which is then sorted and compared to all recipe ingredient lists whenever an item is inserted or removed from
the ingredient slots. By sorting both recipe and ingredients lists, we ensure that the order of the items plays no
role in determining if a recipe is valid.
Keep in mind that this approach is relatively simple, but also limited in scope. It does not consider, in contrast for example
with minecraft, the number of ingredient items in the same slot, nor the position in the crafting grid
Equipment
The equipment panel included in the package is a special example showing how you can easily arrange inventory slots in a non grid layout
while maintaining all the default functionalities, and also work on top of that setup in order to restrict the slots to specific item types.
The above has been achieved by keeping each slot instance in a variable, used in turn to test the contents before inserting an item.
The actual testing of the items against the slot is defined in the script ex_equipment_allowed, which should be adapted
in order to reflect the logic you require.