Translations of this page:

6. Working with inventory

In this chapter we’ll focus on the work with inventory. As there will be a lot of issues to cover, we should hop straight to it.

First let’s clarify, what inventory is. One of the typical adventure game aspects is the ability to collect various junk lying around in the game world and by using it on another junk player achieves ultimate goals like saving world etc. Also sometimes you might want to strike back and use the inventory junk (referred as items) on something in the game world.

WME has excellent support for inventory including combining items and item stocking. WME also supports multiple inventories in case you’d have more than one main actor or you’d want to include trading in your game.

Inventory has two main definition files:

  1. Inventory box definition - defines the place where your inventory will be stored. It consists of rectangular areas and scrollbars used to navigate in your inventory.
  2. Inventory items definition – defines individual items and attached scripts so you can set up necessary interactions.

Both files are assigned in the Project Manager and we’ve discussed them earlier but to recap, they’re inventory.def file and items.items file. Of course you can name it however you like, but let’s stick for now with the items at hand.

Before we start with that, copy somewhere contents of the resource for chapter 5. It consists of the blank project we discussed earlier and we’re going to eventually build a simple game on top of those files.

Let’s start with a inventory box definition. We’re going to base our interface upon the image inventory.bmp located in data/interface folder. The image looks like that

It’s very simple, yet we can easily explain the inventory mechanics on it. Before we dive into the definition file itself, we should look at the image. We see two red rectangles, which we won’t see in our game as they will serve only as a place holders for the buttons used for scrolling inventory left or right. Then we see 10 square parts which will be placeholders for our items.

So with this in mind, let’s look at the inventory.def file located in interface folder.

INVENTORY_BOX 
{ 
  ITEM_WIDTH = 65 
  ITEM_HEIGHT = 65

Those are dimensions of individual items and they correspond to dimensions of the individual squares in the inventory box.

SPACING = 10

This defines how far apart are individual squares (items). This value is in pixels.

  SCROLL_BY = 1

When player clicks on previous or next button, the items rotate left or right respectively. This value specifies how many items will be rotated.

  HIDE_SELECTED = TRUE

If player selects an inventory item, it disappears from the inventory box (to prevent using item on itself for example).

AREA { 30, 12, 770, 77 }

Area defines the inventory item area. It’s relative to the inventory window, not to the screen. So even if you wanted to display inventory at the bottom, those values will still remain the same. Now these values are X,Y coordinates of the upper-left corner and X,Y coordinates of the lower-right corner of the inventory window. In other words, coordinates (30,12) refer to the upper-left corner of first inventory item and (770,77) refers to lower-right corner of 10th inventory item.

  EXCLUSIVE = FALSE

If set to true, player can’t do anything else until he closes the inventory window again.

Now comes the definition of the window itself. Note that our next chapters will deal with windows and controls much more extensively, but we’ll cover here what needs to be explained.

  WINDOW 
  { 
    X = 0 
    Y = 0 
    WIDTH = 800 
    HEIGHT = 90

We start with defining window position and dimensions. Those numbers are absolute, so unlike the Area, they refer to the screen position. Our window will be positioned at the top of the screen and will be 800 pixels wide and 90 pixels tall. If we wanted to position this window to the bottom of the screen, we’d simply change Y to 510 (600 – 90).

IMAGE = "interface\inventory.bmp"

The background image to be used for the inventory – we’ve seen it above already.

    BUTTON 
    { 
      TEMPLATE = "ui_elements\template\but.button" 
      NAME = "prev" 
      TEXT = "<" 
      X = 0 
      Y = 0 
      WIDTH = 30 
      HEIGHT = 90 
    } 
 
    BUTTON 
    { 
      TEMPLATE = "ui_elements\template\but.button" 
      NAME = "next" 
      TEXT = ">" 
      X = 770 
      Y = 0 
      WIDTH = 30 
      HEIGHT = 90 
    }

Now comes the definition of two buttons. For inventory to work properly, they have to be named prev for scrolling left and next for scrolling right in the inventory box. Text is what will be written over the button (in our case < and > ). We define dimensions and position so it will cover the red areas. Note that position is again relative to the parent window. Template is a button template. We’ll look in them in the window chapter, for now, let’s use the default.

  }

We end the window block.

}

We end the inventory block.

Okay, let’s see how our inventory looks like. Open the game.script file and before

Game.ChangeScene("scenes\Room\Room.scene");


put

Game.InventoryVisible = true;


Well, this wasn’t too hard, was it? Run the game and look at the inventory box on top.

Our next step in the inventory discoveries will be an items definition. For item to be successfully used in the game we need to define it’s datablock. Those datablocks appear in the file items.items and we have them in data/items folder. So far our file looks like this:

ITEM
{

 CURSOR_COMBINED = TRUE - defines if you see not only the item but also the standard cursor when you select the item. 
 CAPTION = "WME User's Guide"  - caption of the item on mouse hover. 
 NAME = "book"  - in game name. You’d reference it from your scripts 
 SPRITE = "items\book.bmp"  - Image or sprite of the item in the inventory. 
 CURSOR = "items\book.bmp" – Cursor image (sprite) when you select the item. 
 CURSOR_HOVER = "items\book_h.bmp"  - Defines an image to be used as a mouse cursor when the item is selected and is over an active hotspot. 
 SCRIPT = "items\book.script" - Attached script to the item which handles the interaction. 

}

We can of course define other item parameters as well:

SPRITE_HOVER – when mouse goes over the inventory image it can change to another.
ALPHA – specifies the transparency of the item image (0 = invisible, 255 = fully visible)
TALK - the talking animation for this item.
FONT - which font should be used for talk subtitles and amount display
AMOUNT - current amount of items
DISPLAY_AMOUNT – (TRUE / FALSE) should the current amount be displayed?
AMOUNT_ALIGN - the alignment of the amount label ("left", "right" or "center")
AMOUNT_OFFSET_X - the X offset in pixels of the amount label relative to item's position
AMOUNT_OFFSET_Y - the Y offset in pixels of the amount label relative to item's position

Now we are able to define our inventory and individual items and let’s look at the way, how you can connect items with the actual game.

As I’ve written already, there are two ways, how to approach inventory – Global (one inventory for the whole game) and Actor based – each actor has his own inventory.

Let’s get moving and make our first item interaction happen. Here’s what we’ll do – we place an item (book) in the scene. Upon mouse Left Click our actor goes to that item and picks it up. Item disappears from the scene and appears in the inventory.

  • Open our Room scene in Scene Edit.
  • Add a Sprite Entity and choose a file data\items\book.bmp as a graphic file
  • As a Caption and Name write Book
  • In the Item field write book (lowercase)
  • Attach a script to this entity (you should already be familiar with the way how to do this, or return back to the previous chapters)
  • Fill in the Walk to: and set the direction according to your book position.

We should get the following result:

Save the scene and open the file called book.script in your data\scenes\Room\scr folder.

Inside write the following:

on "LeftClick" 
{ 
	actor.GoToObject(this); 
	Game.TakeItem("book");	 
}

Save and test the game. Cool, Molly went to the book and placed it into the inventory! The book also disappeared. But why?

If we fill in the Item value with a name corresponding to the file items.items, it becomes linked so whenever we call TakeItem method, it disappears and whenever we call DropItem it reappears. Let’s modify our little script to demonstrate this behavior:

on "LeftClick" 
{ 
	actor.GoToObject(this); 
	Game.TakeItem("book");	 
	actor.GoTo(100,100); 
	Game.DropItem("book"); 
}

Not very logical game play, eh? But it shows the point. Sometimes it’s good to destroy item altogether for example if we want to combine two items to get one or if we use the item and don’t want player to have the possibility to reacquire it. For this we have in stock the DeleteItem method.

Before we look more into items handling, let’s look at the item in the inventory. We know, that in items.items there’s a script assigned to it. This is the file, which takes care of the item when it gets to the inventory. It’s not limited to a single scene as our previous script was and this way we should count with the fact that the player would try to use this item anywhere in the game. It would be really painful to create individual responses for each hotspot so basically we want to have unique responses for logical things and some generic “It doesn’t make any sense to use the book here.” for the rest of the hotspots.

Revert the book.script to the original version (without dropping the item) and open the data\items\book.script. Here modify the contents to read:

on "LeftClick" 
{ 
    Game.SelectedItem = "book"; 
}

This single line means, that we assign inventory item as the active item and so all mouse clicks will “use” that item in the game environment. If SelectedItem is null, we don’t have anything selected and thus the normal cursor actions apply.

Test the game and you can see, that if we select the book from the inventory, we can use it elsewhere but we can get rid of it. As we’re now going to build the generic action we’ve decided that if we use right click with selected item, we return that item to the inventory box.

So as it’s generic action fro the whole game, open the scripts/game.script and add a RightClick handler, which will take care of this problem.

on "RightClick" 
{ 
  if (Game.SelectedItem != null){ 
    Game.SelectedItem = null; 
  } 
}

Basically we only check if we have selected any item and if so, we return it to the inventory.

So far good and now we’re going to present the big change to our room. We’ll introduce the new character, which we will call Sally. They are twins so they’ll share the same graphics.

Switch to the actors folder and copy the file molly.actor to sally.actor and molly.script to sally.script.

In molly.actor change the beginning to look like:

  NAME = "molly" 
  CAPTION="Molly" 
  SCALABLE = TRUE 
  INTERACTIVE = TRUE 
  X = 100 
  Y = 100 
  SCRIPT="actors\molly\molly.script"

In sally.actor change the beginning to look like:

  NAME = "sally" 
  CAPTION="Sally" 
  SCALABLE = TRUE 
  INTERACTIVE = TRUE 
  X = 460 
  Y = 400 
  SCRIPT="actors\molly\sally.script"

Open the data\scripts\base.inc and add the lines

global molly; 
global sally;

Then open the game.script file and add a new actor just after the loading of the first:

// load our main actor 
molly = Game.LoadActor("actors\molly\molly.actor"); 
sally = Game.LoadActor("actors\molly\sally.actor"); 
actor = molly; 
Game.MainObject = actor;

Last open the data/scenes/Room/scr/scene_init.script and modify the beginning to look like:

#include "scripts\base.inc" 
 
molly.SkipTo(400, 400); 
molly.Direction = DI_DOWN; 
molly.Active = true; 
 
sally.SkipTo(100, 400); 
sally.Direction = DI_DOWN; 
sally.Active = true;

Now what we’ve done is that we’ve created another actor named Sally which uses the same graphics as a Molly, but it’s entirely different person. We have her reference stored in the global variable actor2 and we’ve placed her in our Room scene. Next thing we’re going to do is that we’re going to implement a neat character switching.

If player clicks on Molly or Sally (without an inventory item selected) game gives control to the second actor.

Now remember that everything in our scripts is set to use the global variable “actor”. So if we want to implement character switching, we simply set this variable to character of our liking. Also we have to change the Game.MainObject because it drives the screen scrolling and we want to maintain this functionality as well. So let’s open the file molly.script in the actor folder and add here the LeftClick handler:

on "LeftClick" 
{ 
	if (Game.MainObject == molly) actor.Talk("I am already selected!"); 
	else 
	{ 
		actor = molly; 
		Game.MainObject = actor; 
	} 
}

Analogically we’ll modify the sally.script to read:

on "LeftClick" 
{ 
	if (Game.MainObject == sally) actor.Talk("I am already selected!"); 
	else 
	{ 
		actor = sally; 
		Game.MainObject = actor; 
	}	 
}

Although we could use single file for this, later on it pays off to have those separated when we want to perform different tasks with each actor.

Now run the game and note that we can switch between actors by simple Left Click on them.

Having this done, we’ll return to our inventory and we try some interactions. First thing what we need to do is open our trusty game.script and actually code in the generic logic of inventory item interaction. What we want to achieve is that whenever we left click with a selected item on a hotspot, we try to call the corresponding method named by an item name. So if we click with a book, we’d try to call a method “Book” of the target entity.

This can be easily done by modifying the Left Click event to read:

on "LeftClick" 
{ 
  var ActObj = Game.ActiveObject; 
  if(ActObj!=null) 
  { 
    if(Game.SelectedItem != null && Game.SelectedItem!=ActObj) 
    { 
      var Item = Game.SelectedItem; 
      if(ActObj.CanHandleEvent(Item.Name)) ActObj.ApplyEvent(Item.Name); 
    } 
    else 
	     ActObj.ApplyEvent("LeftClick"); 
  } 
  else 
  { 
    Scene.ApplyEvent("LeftClick"); 
  } 
}

What are we programming here is the fact, that if we have selected inventory item and we’re not clicking with this item on itself (which would be applicable if we didn’t “hide” the inventory item upon selecting), then we test if the target hotspot can handle the event by the name of our item and if so, this event is fired.

Take some time to think about this concept and when you’re sure you understand it, read up.

Now in our case we have two active objects on the screen (provided that you already picked up the book) - Molly and Sally. So our goal is to make them react to the book usage.



Let’s start with the concept. We want the following: if Molly is active and reads a book, she should be able to read the book. If however you try to read a book with Sally while Molly is active, Sally refuses. The same applies vice versa.

Let’s open molly.script first and add the following code:

on "book" 
{	 
	if (actor == molly) actor.Talk("It's always important to read WME documentation! I'll do it now!"); 
	else 
	molly.Talk("Read it yourself Sally!"); 
}

Then open the sally.script and write there the following:

on "book" 
{	 
	if (actor == sally) actor.Talk("It's always important to read WME documentation! I'll do it now!"); 
	else 
	sally.Talk("Read it yourself Molly!"); 
}

Again it’s very simple logic. If actor variable containing the active actor is set to the clicked actor, we read the documentation or we say the other line. The event “book” is called because we modified the game.script to do this. Clear as mud?

Now we have one illogical thing. It doesn’t matter who picks up the book, both have the book in the inventory. This is the global inventory approach and it’s ok if we have one actor on the stage. But as we’ve introduced two actors, we should separate their inventories.

We’re going to try the following scenario. Whoever picks up the book will get it in his personal inventory. If this person use the book on the other character, she gives the book to the other.

We’re going to introduce new Game attribute called InventoryObject. This attribute accepts the actor variable and we’re going to modify the actor scripts that together with the actor switching also the inventory object gets switched.

But it’s not enough. We also no longer use Game.TakeItem (Game.DropItem, Game.DeleteItem etc.) but we’ll use actor.TakeItem (actor.DropItem, actor.DeleteItem).

So our first stop is in game.script in our too familiar place:

actor = molly; 
 
Game.MainObject = actor; 
Game.InventoryObject = actor;

Next stop is in data\scenes\Room\scr\book.script:

on "LeftClick" 
{ 
	actor.GoToObject(this); 
	actor.TakeItem("book");	 
}

And our last stop for now is molly and sally script adding in the Left Click handler of both this line:

Game.MainObject = actor; 
Game.InventoryObject = actor;

Now when you test the game, you see, that those girls have separate inventories and moreover who grabs the book, has it.

Now let’s handle the book giving:

sally.script

on "book" 
{	 
	if (actor == sally) actor.Talk("It's always important to read WME documentation! I'll do it now!"); 
	else 
	{ 
		Game.SelectedItem = null; 
 		molly.DropItem("book"); 
		sally.TakeItem("book");	 
		sally.Talk("Thank you"); 
	} 
}

If we clicked by Molly with a book on Sally, we first put away the item (SelectedItem = null), then Molly drops the book and Sally takes it. This way the item gets transferred. At the end Sally thanks Molly for the book. If we didn’t call the DropItem method, both girls would have the book in their inventories.

Molly script is almost the same:

on "book" 
{	 
	if (actor == molly) actor.Talk("It's always important to read WME documentation! I'll do it now!"); 
	else 
	{ 
		Game.SelectedItem = null; 
		sally.DropItem("book"); 
		molly.TakeItem("book");	 
		molly.Talk("Thank you"); 
	} 
}

Last couple of methods closing this chapter are dealing with the additional item methods.

Game.IsItemTaken([item name]); returns true if anyone has an item in his/her inventory.

If we want to check specific inventory for item, we’d query it through sally.HasItem([item name]); and again this method returns true or false.
(So in praxis Game.IsItemTaken("book"); or sally.HasItem("book");)

That’s enough for the items apart from the important tip. Sometimes you don’t need an actor for a NPC as it can be for example only a shadow or a head mounted on the wall. WME doesn’t limit you by that and so even entities can have their inventories!

Documentation link:
Contents→ Inside a game→ Working with the inventory - for the general description of the inventory work

 
wmebook/ch6.txt · Last modified: 2008/01/10 12:04 by metamorphium
Recent changes RSS feed Creative Commons License Driven by DokuWiki