BASS Stub - A Lightweight BASS template

Started by Ghost, Thu 13/06/2013 11:09:45

Previous topic - Next topic

Ghost

Lightweight BASS Template v2.0


Download LW_BASS v2.0

The LW BASS is a small, clutter-free template that implements the "Beneath A Steel Sky" two-button interface: Left-click is walk/interact, right-click is examine.

Based on the many helpful suggestions I've rewritten the thing from scratch, with readability and simplicity in mind. The template requires no setup at all and keeps
things as simple as possible.

Feature List:
* Two buttons, two interactions. LMB is "interact", RMB is "examine"
* BASS-style inventory (GUI pops up when top of screen is touched)
* Nicer fonts with full umlaut/foreign character support
* Custom inventory property "propInstantUse" allows inventory items to be used "on their own" (blow whiste, eat sandwich) instead of requiring a "use inv on" action

v2 changes:
* full code rewrite, incorporating most suggestions to improve readability
* removed tween module
* full comments, and license
* as suggested, only "interact" is used, instead of additional "talk" for characters
* moved all mouse handling to on_mouse_click
* support for "instant use" of inventory items
* inv popup position is now a makro
* added nicer unhandled_event template
* fleshed-out test room
* removed Revenant


Phemar

#2
Awesome. I was just thinking of releasing something like this, after all the recent topics in the technical forums. I can see a lot of newbies benefiting from an updated ALARCOST template :D

Edit: Would it not be easier to just release this as a standalone module? It would make integration easier for people with existing projects.

Ghost

Quote from: Phemar on Thu 13/06/2013 12:37:12
Edit: Would it not be easier to just release this as a standalone module? It would make integration easier for people with existing projects.

Probably, yes, my reason to make this an actual template was to offer some additional extras- fonts and tween, in this case. But a module can easily be done if there's enough interest ;)

Crimson Wizard

I would remove the things it does not really rely on if I was adding this to AGS distributive, just to keep things clean.
In my opinion, since the concept of the template is to emulate certain game style, it should do only that (+ have a example room(s) maybe, like 9-verb/Verb Coin templates do if it really needs to demonstrate its features).

awakening

Thanks a bunch for this Ghost - no really.
It's really helpful.  I've edited a few things to suit my needs but it's all really easy to understand and it's laid out nicely, so cheers for that.
I'll credit you in my game for the time you'll have saved me with coding, if that's alright? :)

Ghost

Credit is always nice but not really required. I took much of the code from an existing template myself, and as far as I am concerned templates like this really are more of a "working base".
Will polish it a bit though, and eventually it may become a full-fledged "stub". Then I want a penny each time someone says its name  (laugh)

Phemar

I still don't understand why everyone keeps scripting different actions for Talking and Interacting when using 2 click interfaces if the actions never overlap.

I really think Talk To should be abandoned in favor of Interact for simplicity's sake.

Crimson Wizard

Quote from: Phemar on Mon 24/06/2013 21:38:45
I still don't understand why everyone keeps scripting different actions for Talking and Interacting when using 2 click interfaces if the actions never overlap.

I really think Talk To should be abandoned in favor of Interact for simplicity's sake.
I agree completely. If the UI style assumes that there's only one type of activity with any kind of object possible, then there should be only one interaction type.

Ghost

It's just a habit for me. When I look at a function and see a nice friendly XYZ_Talk, then I know I wanted to script some talk there.
It is, of course, a subjective decision and settling for one interaction type would remove the need to distinguish between objects/hotspots and characters.

Way easy enough to change, though.

Billbis

Well, IMO it easily allow to it dynamically change cursor aspect between "Talk To" mouse graphics and "Use" mouse graphics. There is other way to do that, but setting eModeTalkTo when mouse is on character is an easy way to do it.
Not saying that we should do it that way, but that how I understand people "keeps scripting different actions for Talking and Interacting when using 2 click interfaces if the actions never overlap".

Phemar

Quote from: Billbis on Mon 24/06/2013 22:05:07
Well, IMO it easily allow to it dynamically change cursor aspect between "Talk To" mouse graphics and "Use" mouse graphics. There is other way to do that, but setting eModeTalkTo when mouse is on character is an easy way to do it.
Not saying that we should do it that way, but that how I understand people "keeps scripting different actions for Talking and Interacting when using 2 click interfaces if the actions never overlap".

This is why we have Mouse.UseModeGraphic!

Ghost

#12
Thanks for the feedback/discussion, guys. It helps to refine this in v1.2  (nod)

- Using "Interact" for everything is a sensible request, so yes, I'll do that.
- I like the idea of changing the mouse cursor to display an interaction. AFAIK this is not in the original BASS but it would give nice visual feedback. Thoughts?
- The original BASS changed to an "exit" signs when over places where the character would leave the room. Useful, in my opinion, will do that.
- I got PMs asking for a better "showcase room". Indeed.

Has anyone tried this out and really HATED the alternative fonts? That's something I'm curious about.

I really want to make this worthwile, so keep it coming!

Crimson Wizard

#13
What caught my eye.

1. The UI assumes there's only 1 cursor mode all the time and never changes. Instead of setting cursor images for all built-in cursors, you could set it only for one (I'd vote for mode 6: Pointer).
Also, disable all modes except chosen one in game_start:
Code: ags

mouse.DisableMode(eModeInteract);
mouse.DisableMode(eModeLook);
... etc ...
mouse.Mode = eModePointer;


In GlobalScript:
1. You do this in repeatedly_execute:
Code: ags

if (gInventory.Visible)
{
    player.StopMoving();

Is it what BASS did? I don't like it, because that will be very annoying during action sequences. Though, I guess, user can remove this line.

2. Maybe make constant/macros instead of hardcoding "12" pixel edge which triggers inventory right in the function body?

In custom module:
4. The huge "if (!IsGamePaused())" condition block that covers all function body. This is a matter of personal code style, though I would suggest to make it instead
Code: ags

if (IsGamePaused())
    return;

This will reduce nesting level making code more clear to user.

5. "if (mouse.Mode == eModeUseinv) " condition will never be true, because you never set mouse.Mode. Instead, you can just test "player.ActiveInventory == null", or "!= null". This refers to both "if (button == eMouseLeft)" and "else if (button == eMouseRight)" blocks.
6. This, and merging al location types will produce the code:
Code: ags

if (GetLocationType(mouse.x, mouse.y) != eLocationNone) 
{
    if (player.ActiveInventory == null)
        ProcessClick(mouse.x, mouse.y, eModeInteract);
    else
        ProcessClick(mouse.x, mouse.y, eModeUseInv);
}


7. You are using "inventory[game.inv_activated]". IMHO that's bad. Although that may work, "game.inv_activated" is an obsolete variable, and also old-style code (also it is not very obvious how engine sets it).
I'd suggest to just use InventoryItem.GetAtScreenXY, it might be little slower, but it is a click handler, therefore does not matter really. Also you call GetLocationName anyway, and with GetAtScreenXY you'll get InventoryItem pointer with Name and everything. You won't need to compare names too (comparing strings is slowest operation of all, except only getting object under mouse), you will simply compare pointers:
Code: ags

InventoryItem *item = InventoryItem.GetAtScreenXY(mouse.x, mouse.y);
if (player.ActiveInventory != item)
    item.RunInteraction(eModeUseinv);


8. I don't think it is good to add "data == theGUI.ID" condition when you check that player clicks on gui. There may be other guis too, and the item should probably be deselected anyway.

9. I think it is simplier to test "if (button == eMouseRightInv)" in "on_mouse_click" to make player LookAt inventory item, rather than in "on_event". This way you won't need to test which mouse button is down, nor which Control is clicked.


//--------------------------------
EDIT:
I suggest to nullify info label at the start of mouse_click. Otherwise, the label text will stay on screen while player talks / does blocking actions.

Ghost

Crimson, thanks for the extensive feedback. I think it is obvious that you're "deeper" into the actual coding stuff than I am ;) I learned a good bit from your list, and will see to add these tricks to my toolbox. I see how it'll make the code more readable, too, many thanks for that.

But:
Quote5. "if (mouse.Mode == eModeUseinv) " condition will never be true, because you never set mouse.Mode

I don't get that. The manual states that an interact click on an inventory item will be translated into an useinv action- basically, as soon as your cursor becomes the selected item you *are* in eModeUseInv. So I don't see why this test will fail? (And if it would fail, the template wouldn't work the way I want, but it... seems to do just that  :tongue: )

Crimson Wizard

#15
Quote from: Ghost on Wed 26/06/2013 05:57:37
I don't get that. The manual states that an interact click on an inventory item will be translated into an useinv action- basically, as soon as your cursor becomes the selected item you *are* in eModeUseInv. So I don't see why this test will fail? (And if it would fail, the template wouldn't work the way I want, but it... seems to do just that  :tongue: )
Yes, I was wrong... something I forgot about, that engine automatically sets mode to eModeUseInv when you set ActiveInventory.

Other than that, I think I am mistaken about "game.inv_activated", it is not obsolete, and probably can be used, just needs little different code to remove excessive commands.
Code: ags

if (inventory[game.inv_activated].IsInteractionAvailable(eModeUseinv) == 1 && mouse.Mode == eModeUseinv) 
{
   if (player.ActiveInventory.Name != Game.GetLocationName(mouse.x, mouse.y))
      inventory[game.inv_activated].RunInteraction(eModeUseinv);
}
else if (inventory[game.inv_activated].IsInteractionAvailable(eModeInteract) == 1) 
   inventory[game.inv_activated].RunInteraction(eModeInteract);
else 
   player.ActiveInventory = inventory[game.inv_activated];

There is no need to call Game.GetLocationName here, because you already know that it is "game.inv_activated" item id, therefore you can use inventory[game.inv_activated].Name; but further, you don't really need to compare Names, you can just compare pointers:
Code: ags

if (player.ActiveInventory != inventory[game.inv_activated])
      inventory[game.inv_activated].RunInteraction(eModeUseinv);


Another thing that worries me is whether this combination of conditions is proper.
1st condition will succeed if both mode is "use inv" and clicked item has "use inv" handler.
2nd condition will succeed if either mode is not "use inv" or clicked item has no "use inv" handler and item has "interact" handler.
This means that 2nd condition will succeed too if player used an item on another item that does not have "use inv" handler.
This also means that 3rd condition (else) will succeed if player used an item on another item that does not have any event handlers - in which case he will select the latter.

This is, of course, a choice of design. I don't remember how BASS handled this (if you follow BASS UI style). But I may suggest do it this way instead:
Code: ags

// if player has active item in hand, use it no matter what (we may get "unhandled event" here)
if (mouse.Mode == eModeUseinv)
{
   if (player.ActiveInventory.Name != Game.GetLocationName(mouse.x, mouse.y))
      inventory[game.inv_activated].RunInteraction(eModeUseinv);
}

Kitai

Quote from: Phemar on Tue 25/06/2013 16:10:32
Quote from: Billbis on Mon 24/06/2013 22:05:07
Well, IMO it easily allow to it dynamically change cursor aspect between "Talk To" mouse graphics and "Use" mouse graphics. There is other way to do that, but setting eModeTalkTo when mouse is on character is an easy way to do it.
Not saying that we should do it that way, but that how I understand people "keeps scripting different actions for Talking and Interacting when using 2 click interfaces if the actions never overlap".

This is why we have Mouse.UseModeGraphic!
I think Billbis meant to refer to the IsInteractionAvailable function: it's an easy way to check which cursor mode/aspect you should set depending on what the mouse is currently on. You might have character instances that are only associated with interact events and objects instances that are only associated with talk-to events: GetLocationType will do it wrong in that case.

Quote from: Ghost on Tue 25/06/2013 16:21:11
- I like the idea of changing the mouse cursor to display an interaction. AFAIK this is not in the original BASS but it would give nice visual feedback. Thoughts?
Well, if you use only one mode, you can no longer use IsInteractionAvailable to determine which cursor to use. So you're left with GetLocationType.

To deal with "talkative" objects and "non-talkative" characters, you can use custom properties, but it might go against simplicity for a template.

We already had a discussion with Billbis on how to dynamically change cursor's aspect on this thread of the French forums.

Billbis

Well, what I think is that there is many different ways of coding a 2 click interface in AGS, and that I am far to new into AGSscript to determine the best one (if any).
But I also think that there is a ENORMOUS need for 2 clicks templates, and that the defalut template packaged with AGS should be one of them.
So thanks a lot Ghost for this template.

Ghost

Quote from: Crimson Wizard on Wed 26/06/2013 08:20:41
This also means that 3rd condition (else) will succeed if player used an item on another item that does not have any event handlers - in which case he will select the latter.
That would trigger unhandled_event, though. If the user makes use of it, so there *is* potential for the code failing...
Your suggestion is lean and nicer to read anyway, so I see no reason not to use it.

I'll rewrite that thing. It's nice to throw away some old coding habits, and if the end result is useful, it's a win/win situation and they are nice because everyone wins  (laugh)

frenzykitty

Thank you very much. :):) :=

Nice and clean and functional <3

SMF spam blocked by CleanTalk