Scripting a Lucasarts Gui.

Started by Mr_Threepwood2, Tue 05/06/2007 02:29:53

Previous topic - Next topic

Mr_Threepwood2

I have read several threads about Lucas Arts style of GUI, I have even made a fully working one before.  The problem I am now facing is more of a "will I trap myself by making it this way" style question.

Basically I have two ideas for making a Lucas Arts Style GUI.

IDEA 1:

This is the idea involves making separate cursors for every cursor mode, which will let me use those nice and easy drop down selections for most of the interactions.  A status line will be based off the current mouse mode.  The problems I am looking at with this style are I don't know if it will cause some troubles setting up a right click (which will do the default interaction on an object), and it will involve some extra coding for.  Another big problem here is unhandled_event won't get called for cursors past 9 (usermode 2).

IDEA 2:

Make the cursor always use usermode 1 unless walking or clicked use on an inv item.  So the actual "mode" of the cursor remains the same but it can be changed using a global int that represents the real mode I'm on.  This means that all interactions will have to dealt with using code under the "any click on" property of objects, characters etc.  Another problem I may have here is that I don't know how I'd go about setting the "use inv" up, and how I'd go about having animated cursors since the cursor is never really changing from usermode 1 (I guess I could set up a function to swap the cursor view based on the global int?).

Khris

I'd use the second method, mainly because the interactions will be grouped together nicely in "any click on...".

Every problem you've mentioned can be solved regardless of the chosen method.
E.g. the appearance of the cursor can be changed using Mouse.ChangeModeView.

Having said that, why do you want to make you own LA-GUI? There are several working ones out there, some using the first method, others using the second.

Akatosh

Quote from: Mr_Threepwood2 on Tue 05/06/2007 02:29:53
Another big problem here is unhandled_event won't get called for cursors past 9 (usermode 2).

As far as I know, they still call the method itself - they just don't get the parameters assigned. You should still be able to make checks like

Code: ags

if (mouse.Mode==eModeUsermode3); player.Say("Oh, but that works only on roadrunners!");


and put them in the unhandled_event section of the script. Correct me if I'm wrong, though.

Mr_Threepwood2

I'm fairly sure that cursors past usermode 2 don't make the call to unhandled_event, I was doing it the way you suggested before and it wouldn't make the call (unless you manually called it using the any click action).

Why would grouping them all together be more beneficial do you think?  Just so the code is more compact and not spread out in different sections?

Another solution I came up with is essentially a combo of 1 and 2, it involves making usermode 2 be two cursors (open and close) using a global into to keep track of them, while the rest of the actions all have their own cursor.


The main concern I have is speed, if I do it all using a global int will it cause more slowdown changing cursor icons all the time than the built in way that AGS does it?  Some of the stuff we plan on doing will be pressing AGS (though we're going to try and workaround thinks like screen filters since those really slow down the game), not to mention the game will be 640x480.

Ashen

Mr_Threepwood is right as far as I can tell, modes above 9 don't call unhandled_event by default. You could call it manually, from the 'any click on...' interaction - and in that case, having all the interactions grouped together there makes sense as it's easy to track what interactions shouldn't trigger unhandled_event (just a series of if ... else ifs for the used modes, ending with else unhandled_event(...)).

The combo method makes sense to me, as it should trim the needed cursor mode below 9. You could also probably combine other modes in that way, to reduce it further.

Even at 640x480, the GlobalInt method shouldn't cause noticable slowdown in cursor changes. Unless you're really pressing AGS with the other stuff - you can either give more details to see if it's likely to hog resources, or just try it for yourself and see.
I know what you're thinking ... Don't think that.

Khris

Quote from: Mr_Threepwood2 on Tue 05/06/2007 15:21:01Why would grouping them all together be more beneficial do you think?  Just so the code is more compact and not spread out in different sections?
Exactly.

About unhandled_event:
According to a test I did and the manual, cursor modes above 9 won't call it.
Solution:
In on_mouse_click, use:
Code: ags
  if (button==eMouseLeft) {
    if (!IsInteractionAvailable(mouse.x, mouse.y, mouse.Mode))
      unhandled(GetLocationType(mouse.x, mouse.y), mouse.Mode);
    else
      ProcessClick(mouse.x, mouse.y, mouse.Mode);
  }

Then code your own unhandled function. The what/type stuff is a bit uncomfortable anyways; the custom function could look like this:
Code: ags
function unhandled(int lt, int mm) {
  if (lt==eLocationObject) {
    if (mm==1) ...
    else if (mm==2) ...
    ...
  else if (lt==eLocationHotspot) {
...


Speed:
Changing the cursor won't affect speed in the least, neither will using GlobalInts.

Ashen

The only (minor) problem I see with that code is that IIRR 'Any click on ...' interactions mess up IsInteractionAvailable - if there's an 'Any click' interaction, it always returns true, even if the current mode isn't actually handled. So, if you were using 'Any click' interactions at any point, you'd also have to call unhandled from there.

I agree with making your own function being a little more comfortable that using the default, in this case. However, if it's only going to be used with the current cursor mode, does it actually need the mm parameter?
I know what you're thinking ... Don't think that.

Khris

Quote from: Ashen on Tue 05/06/2007 16:15:55if you were using 'Any click' interactions at any point, you'd also have to call unhandled from there.
Correct, I maybe should have added this to my post but thought it's obvious anyway.

Right, the second parameter isn't really needed. I left it in there by mistake because at first I tried to expand unhandled_event; unfortunately, neither the mouse modes nor the location types match their respective what/type values.

Having said that, if I'm going to test a variable such as mouse.Mode multiple times, I usually use a much shorter substitute. So I probably would have used int mm=mouse.Mode; right at the beginning of the function anyway so including it as a parameter is in fact shorter and cleaner in this case ;)

Mr_Threepwood2

#8
Ok I think I'm going to go with the global int option since it keeps the code less spread out and doesn't require additional calls to an unhandled event.

The only problem I am facing here is the "Use inv" problem, if I use usermode 8 always is there a way that I can set the cursor to be the active inventory's icon, while preserving the hotspot position of the inventory item that I made in AGS (like where the cursor counts as being over a spot).

I know I can do something like get the active inv and then gets its graphic, but will that change the hotspot of the item as a cursor?

Edit:

Or perhaps the answer is to still keep "use inv" as usermode 4 somehow?

I'd really like to get this part right as it will effect the whole game so I'm trying to find benefits/problems with ways of doing it.  Looking through the help file there doesn't seem to be a cursor.setToInv type thing that preserves the hotspot.  My understanding of cursor mode 4 though is that it is native to the game and has to be called by cursormode 2, which would mean that if I had use as part of the global int I'd have to process a fake click on the inv item (or make use always be usermode 2, and just have the rest as 8 with a global int).

Khris

Unfortunately it is not possible to read the editor settings of an InventoryItem's hotspot, so you'll have to use an array and store the hotspot coordinates manually.

Code: ags
int hx[20], hy[20];

function init_inv_hotspots() {
  hx[1]=5; hy[1]=7;
  hx[2]=8; hy[2]=3;
  ...
}

Then call this function in game_start and use this later in the game:
Code: ags
  InventoryItem*ii=player.ActiveInventory;
  mouse.ChangeModeGraphic(mouse.Mode, ii.Graphic);
  mouse.ChangeModeHotspot(mouse.Mode, hx[ii.ID], hy[ii.ID]);

Ashen

Quote
My understanding of cursor mode 4 though is that it is native to the game and has to be called by cursormode 2,

Nope, you just have to have an ActiveInventory item to use it (no ActiveInv means the mode isn't available), but apart from that you can change to it just like any other cursor Mode. In fact, setting the ActiveInventory item (e.g. player.ActiveInventory = iKey;) automatically changes to eModeUseInv - by default, mode 2 (eModeInteract) just sets the Item you clicked as active, which is why it seems to call eModeUseInv.

As for going the other way, I was going to suggest using Properties to store the cursor hotspots for Items, but using arrays as Khris suggests is much neater. Just don't forget to change it back to the 'normal' position for non-Item cursors. Of course, giving all Items the same hotspot would be easier still...
I know what you're thinking ... Don't think that.

Mr_Threepwood2

#11
Yeah maybe I'll just give them all the same hotspot, it would be much easier for something that most people don't even notice anyways.  Plus all inv items have to be the same size to look good in the inventory window anyways, so this shouldn't be a big deal.

I'm thinking that I'll do it all using mode 8 and 0 now since I considered this:

If I do use cursor mode 4 for use inv then it will split up some scripting which makes things uglier, plus I'll still have the same cursor problem when I do "give inv" anyways, so I might as well solve them both the same way.

Great to get this step of the game out of the way, it's what I considered the hardest on my last (still in development, on hold since artist is busy) game.  Last game I also used the global int style but I had just started AGS so I didn't understand a lot of what I was doing, and I didn't change the icons.

Hopefully all goes well with my current project, the biggest problem I always have is getting the artists to keep doing drawings.  The friend I'm working with is pretty committed though so hopefully we can actually get something done.

FYI I am also Mr_Threepwood on these forums but my old email account is gone, and I can't remember the password, hence Mr_Threepwood2 was born.

Khris

Just some additional info:

The cursors' hotspots are set seperately, so if you choose to indeed use mode 4, you don't have to reset the hotspot afterwards.

That's what I'd do, btw. (using 4 for Use and Give)
From experience, reactions to using an inv item with something will generate about as much code as reactions to looking at, opening/closing or pushing/pulling something.
So splitting it up in those two groups isn't that bad.
If anything that's tidier, IMO.

Another thing: don't use GlobalInts. Global variables will do a much better job when it comes to readability and transparency.

Mr_Threepwood2

So you're saying this:

I use a global variable to keep track of the mode, no matter what the current mode is.

If the player clicks use or give on an inventory item, set the global variable appropriatly (so that the status line will still be right), and switch to cursor mode 4.

This will mean that I have to use AGS' default unhandled_event since unhandled calls through mouse mode 4 will happen there.

This also means that for some hotspots/objects I will need to use two of the event triggers (usermode 1, and use inv on object).



That actually sounds pretty good, since it would preserve the inventory item hotspots, and can seperate some of the longer code (since using an inv item on an object may do several things).

The one thing I don't like about it though is having to use the unhandled_event, I can deal with that though it's not so bad.

SMF spam blocked by CleanTalk