The "best" way to manage inventory item interactions

Started by Bobsy, Thu 27/08/2009 11:11:04

Previous topic - Next topic

Bobsy

Something I've found in AGS is that the default way round for defining using inventory items "on" other objects is to have the reactions dealt with from the target first, rather than being defined by the item being used. So for instance, I try to use the WEASEL from my inventory with the DOOR. As I understand it, the basic way to write a reaction is to use

Code: ags
 function oDoor_useinv()
  cEgo.say("That item had no effect on the door");


So my question is this: is there a simple way of defining the interactions from the inventory item, rather than the target? It's pretty common in the Lucasarts style, especially later games. So that when I try to use the WEASEL on most items the game automatically uses a generic "weasel doesn't work here" message. Is this easy to implement, or am I weaselling up the wrong tree?

lsb

umm i'm not exactly sure, but if you want a specific reaction from a specific inventory item, you should use the if statement
Code: ags
 function oDoor_useinv()
    if(player.ActiveInventory == iWeasel) //or whatever the inventory name 
   {
       cEgo.Say("weasel doesn't work here");
     }
   else cEgo.Say("whatever you want for other items");


that will make your player say something different if the inventory item used is the weasel.

Calin Leafshade

Yeah i just have a series of else if statements with the final else being "that doesnt work" or whatever. Then you just add various else ifs for you items

Bobsy

I was going to have a bit of a moan that that wasn't really what I was after, but you lovely chaps managed to get me thinking, and I reckon I've come up with a decent method for item-defined "doesn't work" dialogue.

Once you have all the game's inventory items planned, script for each one of them not working. For example:

Code: ags
 function oGeneric_useinv()
  if (player.activeinventory == iWeasel)
  {
    cEgo.say("That doesn't need weaselling.");
  }

  else if (player.activeinventory == iCrowbar)
  {
    cEgo.say("The crowbar wouldn't be any good here.");
  }

  else if (player.activeinventory == iMonalisa)
  {
    cEgo.say("NOT an appropriate place to hang that!");
  }


  else if (player.activeinventory == iSkeletonkey)
  {
    cEgo.say("There's no lock.");
  }


  else if (player.activeinventory == iFlorence)
  {
    cEgo.say("Florence Nightingale is quite happy to stay in my inventory, thankyou!");
  }

And so on, for each inventory item in the game. Then, save this code to a text file so it's safe, and have it on hand for when you do actions for every hotspot, object and character. Past it in for the useinv functions for each one, and then you can simply replace the "doesn't work" lines as and where necessary for the item which DOES have a different effect on that hotspot, object or character.

The advantages to this method are, in my opinion, that if you are voicing your game you don't end up with a massive long script with different lines needing to be recorded for EVERY single combination of EVERY single item, like Zombie Cow do (quite brilliantly) in their games. Plus, it avoids having to fall back on having a single generic "That doesn't work" for each action, which would quickly get tiresome for the player.

You can also easily go back and change a particular line for interaction between object and inverntory item if a joke suddenly springs to mind, or a hint would come in handy, or for whatever reason. Of course, the major DISadvantage is that it requires you to have all your inventory items planned in advance... and may cause problems if you add others at a later stage.

Nonetheless, the theory for this has me motivated, and I think I'll be using it from now on. I'll keep you lovely peeps informed as to how it's working out.

Tijne

What I've done is used the "unhandled_events" function in the Global Script to account for any and all events that aren't apart of the game; using inventory items where nothing works, or trying to talk to inanimate objects, or whatever.  This makes it so all events that I don't account for are.. well.. handled. ;D  Then, I place an "import function unhandled_event (int what, int type);" into the header file so I can call the unhandled_events function from anywhere.

Code: ags

function hDoor_UseInv()
{
  if (player.activeinventory == iKey)  OpenDoor();  // Opens the door if the key is used.
  else unhandled_events(1,3);  // If the key isn't used, give generic response.
}



This is kind of like a simplified version of your previous solution. ^_^

Bobsy

Quote from: Tijne on Sun 30/08/2009 02:40:49
What I've done is used the "unhandled_events" function in the Global Script to account for any and all events that aren't apart of the game; using inventory items where nothing works, or trying to talk to inanimate objects, or whatever.  This makes it so all events that I don't account for are.. well.. handled. ;D  Then, I place an "import function unhandled_event (int what, int type);" into the header file so I can call the unhandled_events function from anywhere.

Code: ags

function hDoor_UseInv()
{
  if (player.activeinventory == iKey)  OpenDoor();  // Opens the door if the key is used.
  else unhandled_events(1,3);  // If the key isn't used, give generic response.
}



This is kind of like a simplified version of your previous solution. ^_^

Simplified indeed... but presumably what this doesn't allow for is a seperate "doesn't work" message for each inventory item, which is what I'm currently after. Correct me if I'm wrong - I've not used importing functions thus far.

Ali

I'm no expert, but within the unhandled event script you could add ActiveInventory conditions (exactly like the ones in your code above) for each of the inventory items that require a specific response. I imagine that's what Tijne was assuming, because it would mean you wouldn't need to replicate that script hundreds of times.

Tijne

Yeah, :) as Ali said, you can still do-
Code: ags

...
if (player.activeinventory == iKey)  player.say("That doesn't need unlocking!");
else if (player.activeinventory == iTorch)  player.say("I don't want to burn that!");
..

-inside of the unhandled_events script. ^^;;  Going further, it allows you to differentiate between interacting with hotspots, objects, characters; along with what different type of action (looking, using inventory, interacting) you try to do it.    The "what" and "type" values are different depending on what 'event' is called by what; their is a list of what their values mean in the manual. ^_^  (Which I just normally comment directly into the code for easy-access. xD)
It can become a little complicated, but it makes things easier to edit, to read, and to work with in the future. ^^;


And honestly, I just learned about both the importing functions and this unhandled_events function with the last MAGS game I made xD.  So I'm definitely not an expert on this either!

MiteWiseacreLives!

I'm having a problem with this too.
I think we want the same things.
The problem is that once you've set a possible Item combination, if the player tries a "wrong" combination AGS won't run the unhandled_event function.. because it is "handled" (I think).
I think we need to create a "function bad_combo()" that has some randomized responses or If statements like

if (player.activeinventory == iWeasel)
  {
    cEgo.say("That doesn't need weaselling.");
  }

and then call function bad_combo within the oObject_UseInv() script
and also call that funtion within the unhandled_event function...

Would this work? is it the right way to go?



Billbis

The way I deal with that is to create a InvItem custom property named DefaultSentence, which I fill with a funny sentence ("I won't paint this in red !" for a red paint pot).
Then I have this function:
Code: AGS
function DefaultInvInteraction(InventoryItem* item)
{
  player.Say(item.GetTextProperty("DefaultSentence"));
}

Which I use like this:
Code: AGS
function hGreyBin_UseInv()
{
  DefaultInvInteraction(player.ActiveInventory);
}

Hope that helps...

Khris

It should be possible to import the unhandled_event function itself in the global header and use that directly in room scripts.
If that doesn't work, then yes, using a second function like you described is the way to go.

MiteWiseacreLives!

Thanks!
Importing the unhandled_event funcion in the GlobalScript.ash works as well.
Question though,
when I declare the function in the room script I have to define "what" and "type"
ex.
Code: ags

    else
    { unhandled_event(1, 3);
    }


are these global variables? is there a way to call the funtion and let "it" define the variables?
ex. (this doesn't work)
Code: ags

    else
    { unhandled_event(what, type);
    }


Ghost

What is the "action" (look, get, use), type is the "object type" (hotspot, object, character). The help file has a list:
Spoiler

WHAT TYPE Description
  1    1   Look at hotspot
  1    2   Interact with hotspot
  1    3   Use inventory on hotspot
  1    4   Talk to hotspot
  1    7   Pick up hotspot
  1    8   Cursor Mode 8 on hotspot
  1    9   Cursor Mode 9 on hotspot
  2    0   Look at object
  2    1   Interact with object
  2    2   Talk to object
  2    3   Use inventory on object
  2    5   Pick up object
  2    6   Cursor Mode 8 on object
  2    7   Cursor Mode 9 on object
  3    0   Look at character
  3    1   Interact with character
  3    2   Speak to character
  3    3   Use inventory on character
  3    5   Pick up character
  3    6   Cursor Mode 8 on character
  3    7   Cursor Mode 9 on character
  4    1   Look at nothing (ie. no hotspot)
  4    2   Interact with nothing
  4    3   Use inventory with nothing
  4    4   Talk to nothing
  5    0   Look at inventory
  5    1   Interact with inventory (currently not possible)
  5    2   Speak to inventory
  5    3   Use an inventory item on another
  5    4   Other click on inventory
[close]

Unfortunately it's not a consistent list (look for hotspot is different from look for object, for example), but the values are rather easy to use- you can get the type with GetLocationType(x,y),and you can also get your mouse mode. So yes, it should be possible to code an automated unhandled_event.

Khris

You can't set the values automatically; if you call the function yourself, you have to provide the proper values.
Since that's a hassle, I'd suggest to use your own function. Instead of what and type, use GetLocationType() and mouse.Mode like Ghost suggested.

Just do this:
Code: ags
function Unhandled() {

  int lt = GetLocactionType(mouse.x, mouse.y);
  int mm = mouse.Mode;

  if (lt == eLocationCharacter) {
   ...
  }
  ...
}

function unhandled_event(int w, int t) {
  Unhandled();
}


Now you can use Unhandled(); in your own interactions, and AGS calling its own function will "redirect" to your substitute as well.

SMF spam blocked by CleanTalk