Translations of this page:

This is an old revision of the document!
—-

6. L'inventaire

Dans ce cahpitre nous allons nous focaliser sur le fonctionnement de l'inventaire. Comme il va y avoir beaucoup de problème à couvrir, nous devrions aller directement à eux.

D'abord clarifions ce qu'est l'inventaire. Un des aspect typique du jeu d'aventure est la possibilités de collecter diverses ordures qui trainent dans le monde du jeu et de les utiliser avec une autre afin que le joueur atteigne son ultime but, comme sauvez le monde, etc… Aussi parfois vous pourriez vouloir vous venger et utiliser de ces détritus (en parlant des articles de l'inventaire) sur quelque chose dans le monde virtuel.

WME a un excellent support pour l'inventaire incluant les articles combinés et l'article stocké. WME supporte aussi plusieurs inventaires, au cas où vous auriez plus d'un acteur principal ou que vous vouliez intégrer du commerce dans votre jeu.

L'inventaire a deux principaux fichiers de définition:

  1. La définition de la boite d'inventaire - définit l'endroit où votre inventaire sera stocké. Il se compose de zones rectangulaires et de barres de défilement pour naviguer dans votre inventaire.
  2. La définition des articles de l'inventaire – définit les articles individuels et les scripts attachés pour que vous puissiez mettre en place des interactions nécessaires.

Ces deux fichiers sont attribué dans le gestionnaire de projet et nous en avons parlé précédemment. Mais pour vous rappeler, c'est le fichier inventory.def et le fichier items.items. Bien sûr vous pouvez les nommer comme vous voulez, mais restons pour le moment avec les éléments qu'on a sous la main.

Avant de commencer avec ça, copiez quelque part le contenu de la ressource pour le chapitre 5. Il se compose du projet vierge que nous avons vu précédemment et on va, éventuellement, construire un jeu simple sur la base de ces fichiers.

Commençons avec la définition de la boite d'inventaire. Nous allons baser notre interface sur l'image inventory.bmp qui se trouve dans le dossier data/interface. L'image ressemble à ça:

C'est très simple, néanmoins nous pouvons expliquer vaguement y expliquer la mécanique de l'inventaire. Avant de nous plongez dans le fichier de définition lui-même, nous devrions regarder l'image. Nous voyons deux rectangles rouge, que nous ne verrons pas dans notre jeu, car ils servent uniquement à réserver la place pour les touches utilisées dans l'inventaire, défiler vers la gauche ou vers la droite. Puis on vois 10 carré qui seront les emplacements pour nos articles.

Bon avec ça en tête, regardons le fichier inventory.def se trouvant dans le dossier de l'interface.

INVENTORY_BOX 
{ 
  ITEM_WIDTH = 65 
  ITEM_HEIGHT = 65

Ce sont les dimensions de chaque articles et elles correspondent au dimensions de chaque carré de la boite d'inventaire.

SPACING = 10

Ceci défini à quelle distance sont chaque carré (articles). Cette valeur est en pixels.

  SCROLL_BY = 1

Quand le joueur sur le bouton précédent ou suivant, les articles se deplace respectivement vers la gauche ou vers la droite. Cette valeur spécifie combien d'article seront déplacé (1 par 1, 2 par 2…)

  HIDE_SELECTED = TRUE

Si le joueur sélectionne un article de l'inventaire, il disparait de la boite d'inventaire (pour empêcher d'utiliser l'article sur lui même par exemple).

AREA { 30, 12, 770, 77 }

AREA définit la zone des articles de l'inventaire. C'est relatif à la fenêtre d'inventaire, pas à l'écran. Donc même si vous souhaitez afficher l'inventaire en bas, ces valeurs restent les mêmes. Ces valeurs sont les coordonnées X,Y du coin supérieur gauche et les coordonnées X,Y du coin inférieur droit de la fenêtre d'inventaire. En d'autres termes, les coordonnées (30,12) correspondent au coin supérieur gauche du premier article et (770,77) correspondent au coin inférieur droit du 10e article de l'inventaire.

  EXCLUSIVE = FALSE

Si c'est réglé sur TRUE, le joueur ne peut rien faire d'autre tant qu'il n'a pas fermé la fenêtre d'inventaire.

Maintenant arrive la définition de la fenêtre proprement dit. Notez que notre prochain chapitre traitera des fenêtres et des contrôles plus largement, mais nous allons présenter ici ce qui doit être expliqué.

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

Nous commençons par définir la position et les dimensions de la fenêtre. Ces nombres sont absolus, donc contrairement à AREA, elle font référence à l'écran. Notre fenêtre sera positionnée en haut de l'écran et fera 800 pixels de large et 90 pixels de haut. Si nous avions voulu positionner cette fenêtre en bas de l’écran (800x600), nous aurions simplement passer Y à 510 (600-90).

IMAGE = "interface\inventory.bmp"

L'image de fond qui sera utilisée pour l'inventaire - Nous l'avons déjà vu plus-haut.

    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 
    }

Arrive maintenant la définition des deux boutons. Pour que l'inventaire fonctionne correctement, ils doivent être nommé prev pour le défilement à gauche et next pour le défilement à droite dans la boite de l'inventaire.
TEXT est ce que nous allons écrire sur le bouton (dans notre cas < et >). X,Y,WIDTH et HEIGHT définissent les dimensions et la position, ils doivent couvrir les zones rouges. Notez que la position est à nouveau relative à la fenêtre parent.
TEMPLATE c'est le modèle de bouton. Nous verrons ça dans le chapitre des fenêtres, pour l'instant, utilisons celui par défaut.

  }

Nous terminons le bloc WINDOW.

}

Nous terminons le bloc INVENTORY_BOX

Ok, voyons à quoi ressemble notre inventaire. Ouvrez le fichier game.script et avant

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


mettez

Game.InventoryVisible = true;


Bien, c’était pas trop dur, n'est ce pas? Lancer le jeu et regardez l'inventaire en haut.

La prochaine étape dans la découverte de l'inventaire sera la définition des articles. Pour que les articles soit bien utilisés dans le jeu nous devons définir leurs bloc de données (datablock). Ces datablock apparaissent dans e fichier items.items et nous les avons dans le répertoire data/items. Jusqu'à présent notre fichier ressemble à ceci:

ITEM 
{ 
   CURSOR_COMBINED = TRUE // Défini si vous voyez pas seulement l'article mais aussi le pointeur standard quand vous avez sélectionnez l'article. 
 
   CAPTION = "WME User's Guide"  // Légende de l'article quand la souris passe par dessus. 
 
   NAME = "book"  // Nom dans le jeu. Vous ferez référence a celui-ci dans vos scripts 
 
   SPRITE = "items\book.bmp"  // Image ou sprite de l'article dans l'inventaire. 
 
   CURSOR = "items\book.bmp" // image pointeur (sprite) quand vous avez sélectionné l'article. 
 
   CURSOR_HOVER = "items\book_h.bmp"  // Défini une image à utiliser comme pointeur de souris quand cet article est sélectionné et est sur un hot-spot actif.. 
 
   SCRIPT = "items\book.script" // Script attaché à l'article qui contient les interactions. 
}

Nous pouvons évidemment définir d'autre paramètres pour l'article:

SPRITE_HOVER – Quand la souris passe au dessus de l'image de l'inventaire elle peut changer pour une autre.
ALPHA – Spécifie la transparence de l'image de l'article (0 = invisible, 255 = Complétement visible)
TALK - l'animation de parler pour cet article.
FONT - quelle police doit être utilisée pour les sous-titres et pour afficher la quantité.
AMOUNT - Quantité actuel d'articles
DISPLAY_AMOUNT – (TRUE / FALSE) si le la quantité actuelle d'article devrait être affichée?
AMOUNT_ALIGN - L'alignement de l’étiquette quantité ("left"(gauche), "right"(droite) ou "center"(centré))
AMOUNT_OFFSET_X - Le décalage en X en pixels de l’étiquette quantité relatif à la position de l'article.
AMOUNT_OFFSET_Y - Le décalage en Y en pixels de l’étiquette quantité relatif à la position de l'article.

Nous sommes, maintenant, capable de définir notre inventaire et chaque articles et regardons la façons de lier les articles avec le jeu actuel.

Comme je l'ai déjà écrit, il y a deux façon d'aborder l'inventaire - Globale (Un inventaire pour tout le jeu) et basé sur l'acteur - Chaque acteur a son propre inventaire.

Faisons e sorte que notre première interaction avec l'article arrive. Qu'allons nous faire là? - Nous plaçons un article (Livre) dans la scène. dès que qu'il y un clic gauche de notre souris sur cet article et qu'il est ramassé. L'article disparait de la scène et apparait dans l'inventaire.

  • ouvrir notre salle dans l'éditeur de scene.
  • Ajouter un élément Sprite et choisir le fichier data\items\book.bmp comme fichier graphique
  • Comme légende et nom écrire Book
  • Dans le champ Item écrire book (en minuscule)
  • Attacher un script a cet élément (Vous devriez déjà etre familiarisé avec cette façon de faire, ou retournez au chapitre précédent)
  • Remplir dans Walk to: et régler la direction en fonction de la position de votre livre.

Nous devrions avoir le resultat suivant :

Sauvegardez la scène et ouvrir le fichier appelé book.script dans votre répertoire data\scenes\Room\scr.

Écrivez dedans ce qui suit:

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

Sauvegardez et test le jeu. Cool, Molly va vers le livre et le place dans l'inventaire! Le livre a également disparu. Mais pourquoi?

Si nous remplissons la valeur de Item avec le nom correspondant au fichier items.items, il devient liée donc à chaque fois que nous appelons la méthode TakeItem, il disparait et chaque fois que nous appelons DropItem il réapparaît. Modifions notre petit script pour démontrer cela:

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

Pas tres logique cette facon de jouer, hein? Mais ça montre ce que je veux dire. Quelque fois c'est bien de détruire complétement l'article, par exemple si on veut combiner deux articles pour en faire un ou si nous utilisons l'article et le joueur ne veut pas avoir la possibilité de le recupérer. Pour cela nous avons en stock la méthode DeleteItem.

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

 
fr/wmebook/ch6.1319106878.txt.gz · Last modified: 2011/10/20 12:34 by Anto0085
Recent changes RSS feed Creative Commons License Driven by DokuWiki