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

#241
You're welcome :)

But the real work is done by the people maintaining the engine, primarily Crimson Wizard. I'm just using the tools they provide :)
#242
Regarding the scrolling dialogs I wrote a module:

https://drive.google.com/file/d/19uyh4VBjeMkhg-oNX2RC-STsw7IGG9Bl/view?usp=share_link

Here's example setup code, however each line is optional:

Code: ags
  Dialogs.SetFont(eFontFont0);
  Dialogs.SetTextColors(11, 12); // normal text, hovered text, optionally: chosen text and hovered chosen text
  Dialogs.SetPadding(4, 4); // padding inside the GUI, first x then y
  Dialogs.SetVisibleOptions(4); // only show 4 options max, enable mouse scrolling (pass 0 to always show all)
  Dialogs.SetScrollArrowSlots(2170, 2171); // slots for the scroll arrows
  Dialogs.SetShowNumbers(true); // show line numbers

Preview:
#243
Hey Giacomo,

don't worry, finding something in the manuals or tutorials can be really hard, and the forum search is not exactly great to find specific solutions either.

Try my module:
https://www.adventuregamestudio.co.uk/forums/beginners-technical-questions/graphicalfunctional-questions-for-cmi-style-guiinventorymenus/msg636639852/#msg636639852
#244
Re 2:
My guess is when you draw the options you need to iterate over the options and only increment the y coordinate if it's on, similarly to what I do in the code you linked where I do the keypress detection.

Which template/module/dialog code are you using?

As for the balloon character, assigning an Idle View should work here. It can be used to animate the balloon whenever the character is standing still.
#245
Re 1:
You can create a copy of the button's background sprite, then draw the text on top:

Code: ags
DynamicSprite* CreateButtonSprite(int bg_slot, String text, bool highlight) {
  int font = Game.NormalFont; // set font
  DynamicSprite* r = DynamicSprite.CreateFromExistingSprite(bg_slot);
  DrawingSurface* ds = r.GetDrawingSurface();
  ds.DrawingColor = !highlight * 12345 + highlight * 23456; // AGS color values for text / highlighted text
  int y = (r.Height - GetFontHeight(font)) / 2;
  ds.DrawStringWrapped(0, y, r.Width, font, eAlignCentre, text);
  ds.Release();
  return r;
}

DynamicSprite* menuButtonSprite[5], menuButtonHSprite[5];

function PrepareButton(int index, Button* button, int bg_slot, String text) {
  menuButtonSprite[index] = CreateButtonSprite(bg_slot, text, false);
  menuButtonHSprite[index] = CreateButtonSprite(bg_slot, text, true);
  button.NormalGraphic = menuButtonSprite[index].Graphic;
  button.MouseOverGraphic = menuButtonHSprite[index].Graphic;
}

// call this in game_start (or put the lines in there instead)
function PrepareMenu() {
  PrepareButton(0, btnStartGame, 123, "Start Game");
  PrepareButton(1, btnLoadGame, 124, "Load Game");
}
#246
It's the same principle:

1. you declare an integer variable above the function so it retains its value (the name doesn't matter but should reflect the contents)
2. you increment it by one each time the player does the thing (this is currently missing from your code)
3. you give a different reaction depending on the value

The value of player.PreviousRoom is exactly what the name suggests: it's the room number of the room the player was in before the current one. A typical use-case is in the "enters room after fadein" event where you'll want to check by which exit the room was entered. It has nothing to do with interacting with hotspots.
#247
Damn, I didn't even notice that :P

I automatically read all these as   cMara.SpeechView = 6; because they used the correct command the first time.
#248
You can use Game.DoOnceOnly() here, but in general you can do this:

Code: ags
int fridgeLook = 0; // initial value, variable declared outside and above function so it retains its value

function hFridge_Look()
{
  cMara.FaceDirection(eDirectionDown);
  fridgeLook++; // increment by one

  if (fridgeLook == 1) {
    cMara.Say("All I know is that there's nothing in it.");
  }
  else if (fridgeLook == 2) {
    cMara.SpeechView = 4;
    cMara.Say("I'm fine not eating anyway.");
    aDistant_alarm.Play(eAudioPriorityNormal);
    cMara.FaceDirection(eDirectionUp);
    cMara.Say("That's my alarm clock.");
    cMara.SpeechView = 6;
    cMara.Say("Why is it going off at this time?");
    cMara.SpeechView = 2;
    cMara.Say("Gotta turn it off before it annoys the heck out of me.");
  }
  else {
    cMara.Say("It's the same empty fridge.");
  }
}

edit: fixed example code
#250
There's another way this can be done without a custom speech function:

Code: ags
AudioChannel* blips;

void HandleSpeechBlips() {
  int id = -1;
  for (int i = 0; i < Game.CharacterCount; i++) {
    if (character[i].Speaking) {
      id = i;
      break;
    }
  }
  if (id == -1) {
    if (blips != null && blips.IsPlaying) blips.Stop();
  }
  else {
    AudioClip* ac = aBlip;
    // different clips for certain characters
    if (id == aWizard.ID || id == aWolf.ID) ac = aGravellyBlip;
    if (blips == null || !blips.IsPlaying) blips = ac.Play();
  }
}

Put this code somewhere above repeatedly_execute_always() and HandleSpeechBlips() inside it.
#251
You don't even need to create a custom GUI for this, at least not initially.

Go to General Settings -> Dialog and change the SpeechStyle to "SierraWithBackground" like glurex said.
AGS will now display speech in the standard message GUI, next to a portrait that is animated using the character's speech view.

To fine-tune this, put suitable portrait sprites in the speech view instead of whole character sprites. You'll also want to pick a non-outline font as the speech font (inside the Global Script's game_start function):
Code: ags
  Game.SpeechFont = eFontStandard;

To further customize the appearance, you can now also use a custom TextWindowGUI.
#252
The manual says

"Not all renderers support changing vsync at runtime. If current one does not, then trying to change System.VSync will have no effect."

So if it doesn't work with the hardware renderers, then there's no way to do it.
#253
Wordle 605 4/6

🟩⬜⬜⬜⬜
🟩⬜🟨🟨⬜
🟩🟩🟩⬜⬜
🟩🟩🟩🟩🟩
#254
while is used to create a loop, and it will block everything else while the condition is true. AGS also doesn't advance to the next frame while the execution is stuck inside the loop running the lines over and over again (at maximum execution speed), so anything you change about the game doesn't become visible until the loop exits (because the screen never gets redrawn).

If it's ok that the while loop is blocking everything else, you can add Wait(1); inside the loop, this will advance the engine one frame and redraw the screen.
A use-case for that is a blocking cutscene where the camera pans across a room.

To run code in a loop without blocking the rest of the game, AGS has the three repeatedly_execute functions. Rooms use a separate event instead of the regular function, so if you want code to run continuously only in a specific room, you use the room event and put your code in the event's function instead.

Code: ags
function Room_RepExec() {
  int tr = 400 - player.x; // invisible below 300, fully visible at 400
  if (tr < 0) tr = 0; // clamp value to allowed range
  if (tr > 100) tr = 100;
  gSomeGUI.Transparency = tr;
}
#255
I'm relatively sure that .Moving an/or .Animating might not immediately become true, they might only be true in the next frame.

You can try rewriting the code like this:
Code: ags
  if (cLibrarian.Moving || cLibrarian.Animating) return;

  if (TimerStart) {
    location = Random(0); // Randomize 9 numbers
    TimerStart = false; // Stop timer from randomizing
    Walked = false;
    return;
  }

  if (location == 0) {
    if (cLibrarian.x == 444 && cLibrarian.y == 229) Walked = true;
    if (!Walked) {
      cLibrarian.Walk(444, 229, eNoBlock, eWalkableAreas);
      return;
    }
    cLibrarian.FaceDirection(eDirectionRight);
    cLibrarian.LockView(39);
    cLibrarian.Animate(2, 5, eOnce, eNoBlock, eForwards);
    return;
  }
  
  cLibrarian.UnlockView();
  Walked = false;
  TimerStart = true;

The main difference is basically that the else branch will only run during the next frame since the code keeps exiting the function using "return;".
Untested!
#256
Wordle 599 3/6

⬜⬜🟩⬜⬜
⬜🟩🟩🟩⬜
🟩🟩🟩🟩🟩

Spoiler
SPARE
CLAIM
FLAIL
[close]
#257
Looks like it, don't like it >:(

Wordle 598 2/6

⬜🟩🟨⬜🟩
🟩🟩🟩🟩🟩
#258
How many frames do you have in the up/down loops?
#259
Yeah, my bad; .TopItem is an integer. I fixed the code in my post :)
#260
Glad it works but please do not quote the entire previous post. There's a Reply button at the bottom ;)
SMF spam blocked by CleanTalk