Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - Khris

#1
Screenshots of code... haven't seen those in a long time :-D
Please post code in text form. Like this:

Code: ags
function abc() {
  Display("ABC");
}

Anyway, it's a very common beginner's problem: you typed or pasted the function into the script yourself and therefore AGS doesn't know it exists. You need to open the room's event pane (thunderbolt icon) and link the function by pasting its name into the field next to the event. Check the event pane in the room where it's working to see how it's supposed to be set up.
(See also https://adventuregamestudio.github.io/ags-manual/acintro3.html#adding-some-interaction)
#2
Another way is to map the mouse position to the viewport directly. This works best if your map has a similar aspect ratio as your screen.

Code: ags
// add this above room_RepExec
int map_width = 1400;
int map_height = 900;

function MoveMap() {
  int sw = Game.Camera.Width, sh = Game.Camera.Height;
  Game.Camera.SetAt(mouse.x * (map_width - sw) / sw, mouse.y * (map_height - sh) / sh);
}

  // add this inside room_RepExec
  MoveMap();
#3
When I click on my testing hotspot the message appears right away. Walking to a hotspot in the Tumbleweed template is implemented by calling Verbs.Goto() or Verbs.MovePlayer(x, y) iirc. In other words, the player shouldn't approach the door first.

Plus, the debug message should always appear. If your game doesn't react to looking at the door, something is wrong on a fundamental level.

To clarify, you did start a new game using the Tumbleweed template, right?
And when you check the events pane for the door hotspot, it does say "htokitchen_AnyClick" next to "any click on hotspot"?
#4
That's weird. Let's do basic debugging first:

Code: ags
function htokitchen_AnyClick(Hotspot *theHotspot, CursorMode mode)
{

  Display("htokitchen_AnyClick called, Look is %d", Verbs.UsedAction(eGA_LookAt));

  if (Verbs.UsedAction(eGA_LookAt)) {
    player.Say("Pretty door");
  }
}
#5
Fixing this might be as simple as
Code: ags
  // rep_exe
  if (player.ActiveInventory == null) HandleCursors();

In other words, if the player has picked an inventory item, skip the automatic cursor mode change.

However, your problem description seems to contradict that, so I'm not sure how to address this.
#6
If you import the sprites in numerical order and use an object for the padlock dial, you can change the .Graphic property using a simple calculation.

39 frames means if you start the import at sprite slot 12, the last sprite is in slot 50. So the code could look like this:
Code: ags
function RotateLockBy(int by) {
  int first = 12, last = 50; // sprite slots
  int slot = oPadlock.Graphic + by;
  if (slot < first) slot = last;
  else if (slot > last) slot = first;
  oPadlock.Graphic = slot;
}

Put this above your arrow hotspot click handlers and simply call RotateLockBy(-1); and RotateLockBy(1); respectively.
#7
It's weird that the player moves in one case and not the other.

Anyway, having the verbs means you're using the Tumbleweed (9-Verb / Lucasarts) Template, not the Sierra one.
In that template, a single left-click runs the WalkTo action code.
Did you try clicking on the "Look At" button first? The template also has an action line which should display something like "walk to door" when you're moving the mouse over the door.
#8
I assume you're using the "mouse moves over hotspot" event?

Anyway, like Laura says, use a custom property. I'd get rid of all the existing hotspot events and just implement this globally.
The cursor modes all have enum/int values, so you can use these to set it up. eModeInteract for instance is 2.

So I'd add a custom property "cursor_mode" like this:

(or skip characters here if you're always showing a talkto cursor)

Next you need to track what's under the cursor to implement your own "moves over" and "leaves" events:
Code: ags
// above rep_exe
LocationType prevLocation;
Hotspot* prevH;
Object* prevO;
Character* prevC;

void HandleCursors() {
  int mx = mouse.x, my = mouse.y;
  Hotspot* currH = Hotspot.GetAtScreenXY(mx, my);
  Object* currO = Object.GetAtScreenXY(mx, my);
  Character* currC = Character.GetAtScreenXY(mx, my);
  LocationType currLocation = GetLocationType(mx, my);
  bool isH = currLocation == eLocationHotspot, isO = currLocation == eLocationObject, isC = currLocation == eLocationCharacter;
  bool typeChange = currLocation != prevLocation;

  CursorMode newMode = eModeWalkto;
  if (isH && (typeChange || currH != prevH)) newMode = currH.GetProperty("cursor_mode");
  else if (isO && (typeChange || currO != prevO)) newMode = currO.GetProperty("cursor_mode");
  else if (isC && (typeChange || currC != prevC)) newMode = eModeTalkto;
  
  if (newMode != mouse.Mode) mouse.Mode = newMode;
  prevH = currH;
  prevO = currO;
  prevC = currC;
}

  // inside rep_exe
  HandleCursors();

If most of your objects are supposed to show a pick up cursor instead, create two separate properties and use the 2nd one for objects with a default value of 5. This also requires changing the GetProperty string in the object line above.
#9
The formula seems to be:

Code: ags
  int gameloops = ((StrLen(text) / game.text_speed) + 1) * GetGameSpeed();

https://www.adventuregamestudio.co.uk/wiki/AGS_tidbits_%26_snippets (about halfway down)

game.text_speed has a default value of 15. So 15 characters are displayed for two seconds, 30 characters for three seconds, etc.
#10
Judging from the screenshots of your inventory GUI in the editor, I don't think you've actually used the new background with the border?

For me it looks like this:
#11
Can you describe how it's not working?

I did a test with the Sierra template.

(This template has the inv GUI set to "pause game when shown", which will prevent rep_exe code from running, so this might actually be a much easier way to solve this.)

Anyway, I added a transparent border to the background image png, changed the position and size of the GUI accordingly and moved all the controls to their proper spots.
Then I used this code to display the hotspot's name in the status bar:

Code: ags
// called on every game cycle, except when the game is blocked
function repeatedly_execute()
{
  String name = Game.GetLocationName(mouse.x, mouse.y);
  lblOverHotspot.Text = name;
}

The transparent background prevented this mechanism from displaying the hotspot's name while the GUI was open, so it worked as expected. I.e. a transparent but clickable GUI catches room clicks and GetLocationType / Game.GetLocationName.
#12
#13
One easy way to fix this is to make the GUI as big as the screen and use a background image with a transparent border. A clickable GUI will prevent the cursor from reacting to hotspots even if the background is completely transparent.

Another way is to check if the GUI is open before changing the cursor. Is this done in repeatedly_execute code? Or are you using the built-in way (AnimateOnlyOnHotspots)?
If it's the former, you can just wrap the code in
Code: ags
  if (!gInventory.Visible && !gInterface.Visible) {
and
Code: ags
  }
#14
Edit:
I'm now sure I misunderstood the question. Removed my initial answer.

So do you mean like disabling certain verbs altogether until the player learns how to push stuff for instance...?
To make a button invisible at the start of the game, open GlobalScript.asc from the Scripts node and go to the game_start function. Now put this command inside the function:
Code: ags
// called when the game starts, before the first room is loaded
// =======================================================================================
function game_start() 
{
  SetRestartPoint();
  btnVerb7.Visible = false;  // add this line
}

To show the button again, just set it back to "true".
To find out which button you need to address, open GUIs -> gMain and click on the button once. This will show its script name at the top of the properties pane.

(IIRC, translating your game to another language might actually change the button that is used for "Push", but this should do for now.)
#15
The correct time to learn how to use variables is always yesterday, because it means you can suddenly solve 99% of your problems today ;)

Seriously, don't put this off. Adding a global variable is very easy thanks to the project tree node of the same name.
Create an int named pizza_state with an initial value of 0.
A value of 1 means the pizza was ordered and has appeared in the other room. A value of 2 means the pizza is gone again.

Code: ags
  // ordering pizza
  if (pizza_state == 0) pizza_state = 1;

In the other room's room_Load:
Code: ags
  oPizza.Visible = pizza_state == 1; // turn object visible if state is 1

When the pizza is taken:
Code: ags
  pizza_state = 2;
  oPizza.Visible = false;
#16
Quote from: brushfe on Tue 27/05/2025 02:11:42Regarding the code, it sounds like Start/EndCutscene are the foundation. Is it safe to say that each cutscene could exist as its own function, and the code within bookended by Start/EndCutscene?
No, because you can change rooms during a cutscene, or have non-blocking events.
What happens at an engine level is that if the player skips a cutscene, AGS basically switches into lightspeed mode where the game runs really really fast, until it hits an EndCutscene() command. So it can be literally anywhere in your code, as long as you make sure it gets hit.
#17
One thing I've noticed:
Code: ags
  i_loop - 8*(i_loop/8) == 2
can be shortened to
Code: ags
  i_loop % 8 == 2

Anyway, it sounds like the game is using the wrong view, a view with too many frames. So you're using the correct loop and frame but a view that has more frames, and in your animation you first see the correct first X frames, then also frames X+1, X+2 which still have sprites in them from a different view.

Your code uses view_to_generate to grab the number of frames but then uses i_view to actually populate it. If these values differ, and they appear to, then the loop might end up putting the wrong number of frames in the view, i.e. less.

Edit:
Scratch that, I just saw that you are saving the generated frames using view_generate.
If I had to debug this I'd probably refactor the code into a bunch of separate functions. Using one massive function and tons of variables makes this a pain to debug.
#18
It's a single layer, so the only way to undo this is to click the undo button. If it's too late for that, I suggest using a paint program instead and importing the mask instead of painting it in the editor.
#19
Hotspots are objects, not in the AGS sense but in terms of programming. References to them are stored in pointers, declaring those requires an asterisk.

Code: ags
int a; // primitive value, initially 0
Hotspot* h; // pointer, initially pointing to null

(JavaScript is weakly typed and therefore doesn't require asterisks, every variable can store all data types.)

Regarding the general feature you're implementing, AGS already has a built-in handler for unhandled events:
https://adventuregamestudio.github.io/ags-manual/Globalfunctions_Event.html#unhandled_event

Unfortunately, there's no way to get the actual hotspot inside this function. One way to fix this is to add some lines above and inside on_mouse_click:
Code: ags
// above
LocationType _lt;
Hotspot* _h;
Object* _o;
Character* _c;

    // inside on_mouse_click / eMouseLeft in a standard template
    int x = mouse.x, y = mouse.y;
    _lt = GetLocationType(x, y);
    if (_lt == eLocationHotspot) _h = Hotspot.GetAtScreenXY(x, y); else _h = null;
    if (_lt == eLocationObject) _o = Object.GetAtScreenXY(x, y); else _o = null;
    if (_lt == eLocationCharacter) _c = Character.GetAtScreenXY(x, y); else _c = null;
    Room.ProcessClick(x, y, mouse.Mode); // this is the existing line

Now put this somewhere below the on_mouse_click function:
Code: ags
function unhandled_event(int what, int type)
{
  if (what == 1) // hotspots
  {
    if (type == 4) // TalkTo was used
    {
      Display("You can't talk to the %s.", _h.Name); // should be save to assume _h isn't null
    }
  }
}


--

You can also skip the somewhat outdated built-in function like this:
Code: ags
function Unhandled_Hotspot(Hotspot* h, CursorMode mode) {
  if (mode == eModeTalkto) Display("You can't talk to the %s.", h.Name);
}

    // on_mouse_click / eMouseLeft
    int x = mouse.x, y = mouse.y;
    if (!IsInteractionAvailable(x, y, mouse.Mode)) {
      int lt = GetLocationType(x, y);
      if (lt == eLocationHotspot) Unhandled_Hotspot(Hotspot.GetAtScreenXY(x, y), mouse.Mode);
    }
    else Room.ProcessClick(x, y, mouse.Mode);
#20
The Tumbleweed template puts the screenshots on the buttons in OptionGui.asc / CustomSave::ShowLoadDialog.
Editing that to achieve the desired look is pretty involved, so since AGS 4 supports rotated buttons, you might want to consider doing that instead.
SMF spam blocked by CleanTalk