How to Check if Object has Already Been Looked At

Started by TheMagician, Tue 27/11/2012 19:30:38

Previous topic - Next topic

TheMagician

Hi guys,

when you look at objects in my game the player character describes them in a nice and delightfully humorous way. However, some players just don't seem to appreciate this hard programming work and click on the objects in "interact mode" right away which (in most cases) lets them pick up the objects without ever having listened to the "look at" line.

My idea is to play the "look at" line in these cases - just before you pick up the object - and only if the object hasn't been looked at before.

The question is: how do I check if an object has been looked at before?

Is there any way to extend the Object type to include a variable like oBanana.has_been_looked_at?

Or do I have to create a custom array object_has_been_looked_at[NUMBER_OF_OBJECTS_IN_ROOM], set these all to 0 and then modify them accordingly and do that for each and every room ... which would be a hassle.

Many thanks in advance,
Stefan

MurrayL

#1
EDIT: Ignore all this. It won't work. Check out my later reply instead!

You can add custom properties in the editor and have them apply to all things of a certain type (i.e. Object) - see the AGS manual for more info on this.

Add a new custom boolean property like 'looked_at', and have the default value be 'false'. Now, in your globalscript, you can do something like this:

Code: AGS

function on_mouse_click(MouseButton button) // called when a mouse button is clicked. button is either LEFT or RIGHT
{
    if (IsGamePaused() == 1){// Game is paused, so do nothing (ie. don't allow mouse click)
        return;
    }else if(button == eMouseLeft)
	{
                Object* tempObj = Object.GetAtScreenXY(mouse.x, mouse.y);
		if(tempObj != null && tempObj.GetProperty("looked_at") == false){
	            ProcessClick(mouse.x, mouse.y, eModeLookAt);
                }else{
                    ProcessClick(mouse.x, mouse.y, eModeInteract);
                }
        }
    }
}

Khris

This might work (untested):

Code: ags
bool obj_looked_at[12000];

// in on_mouse_click, eMouseLeft

  if (GetLocationType(mouse.x, mouse.y) == eLocationObject) {
    Object*o = Object.GetAtScreenXY(mouse.x, mouse.y);
    int oi = player.Room * 40 + o.ID;
    if (mouse.Mode == eModeLookat) obj_looked_at[oi] = true;
    if (mouse.Mode == eModeInteract && !obj_looked_at[oi]) o.RunInteraction(eModeLookat);
  }
  ProcessClick(...);


o.RunInteraction() should get queued first and executed before ProcessCLick().

Edit: Custom properties cannot be changed at runtime.

MurrayL

#3
Oh yeah. Just noticed that I didn't try to set 'looked_at' when you look at something  :-[

Edit: What about something like this:

Code: AGS

Object* tempObj = Object.GetAtScreenXY(mouse.x, mouse.y);
if(tempObj != null){
    if(Game.DoOnceOnly("%s %d", tempObj.Name, player.Room)){
        tempObj.RunInteraction(eModeLookAt);
    }else{
        tempObj.RunInteraction(eModeInteract);
    }
}

Crimson Wizard

#4
Although the right direction, this -
Quote from: MurrayL on Tue 27/11/2012 19:50:16
Code: ags

    if(Game.DoOnceOnly("%s %d"),tempObj.Name,player.Room){

is totally wrong thing wrong to write. DoOnceOnly takes only 1 param of type String, secondly you are putting params outside function parameter list.

This could work, however:
Code: ags

String action_name;
action_name.Format("look at obj %d %d", tempObj.ID, player.Room);
if (Game.DoOnceOnly(action_name)) {


Note: I added "look at obj" prefix to make the string more unique (and distinct), and used object ID instead of Name just because it is more simple.

MurrayL

Quote from: Crimson Wizard on Tue 27/11/2012 20:11:49
Although the right direction, this -
Quote from: MurrayL on Tue 27/11/2012 19:50:16
Code: ags

    if(Game.DoOnceOnly("%s %d"),tempObj.Name,player.Room){

is totally wrong thing wrong to write.

Whoops! Yes, you're right - I got the syntax all jumbled up :)

Khris

Using Game.DoOnceOnly in that way is really neat, however, the syntax is still wrong:

Code: ags
  if (Game.DoOnceOnly(String.Format("look at obj %d %d", tempObj.ID, player.Room))) ...


String.Format is static and can't (actually: shouldn't, since it is possible, unfortunately) be used like that, the proper way is:

Code: ags
  String my_text = String.Format("%d", obj.ID);


While it is possible (however again: bad practice) to replace "String" with the actual variable name, the result has to be assigned to a string, otherwise the function has no effect.

Crimson Wizard

Lol, I keep falling into this, working with C++ library that has Format as non-static member.

TheMagician

Fantastic input guys. Thanks a lot. :)

@Khris: nice way of assigning the objects to the array.

@MurrayL: clever idea to use Game.DoOnceOnly.

Both codes work. I ended up using Khris' because it is a bit more flexible. If you ever need to reset one of the objects you can do that with his code but not with Murray's.

Also, because I only want this special behaviour with items that can actually be picked up I added a custom property to the objects called "pickup" which is either 0 or 1.

Here's the final code:

On top of global script:
Code: AGS

int obj_looked_at[12000];


In section on_mouse_click of global script:
Code: AGS

if (button == eMouseLeft)
{    
  if (GetLocationType(mouse.x, mouse.y) == eLocationObject)
  {
    Object*o = Object.GetAtScreenXY(mouse.x, mouse.y);
    if (o.GetProperty("pickup"))
    {
      int oi = player.Room * 40 + o.ID;
      if (mouse.Mode == eModeLookat) obj_looked_at[oi] = true;
      if ((mouse.Mode == eModeInteract) && (!obj_looked_at[oi]))
      {
        obj_looked_at[oi] = true;
        o.RunInteraction(eModeLookat);
      }
    }
  }
  ProcessClick(mouse.x, mouse.y, mouse.Mode); 
}

SMF spam blocked by CleanTalk