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

#101
What you call your variables/pointers is completely up to you. The important thing is the correct type, in this case AudioChannel*.
#102
Edit: nevermind, you can use Game.GetViewFrame() to grab the view's frame, then use .LinkedAudio to set the clip.


You can use a struct to assign the sounds:

Header:
Code: ags
struct ObjectFrameSound {
  int room, oID, frame;
  AudioClip* clip;
  AudioChannel* channel;
};

import void AddFrameSound(this Object*, int frame, AudioClip* clip);

Add this to the Global Script:
Code: ags
ObjectFrameSound ofs[20];
int ofs_count;

void AddFrameSound(this Object*, int frame, AudioClip* clip) {
  ofs[ofs_count].room = player.Room;
  ofs[ofs_count].frame = frame;
  ofs[ofs_count].oID = this.ID;
  ofs[ofs_count].clip = clip;
  ofs_count++;
}

void PlayObjectFrameSounds() {
  for (int i = 0; i < ofs_count; i++) {
    if (ofs[i].room == player.Room && object[ofs[i].oID].Frame == ofs[i].frame) {
      if (ofs[i].channel == null || !ofs[i].channel.IsPlaying) ofs[i].channel = ofs[i].clip.Play();
    }
  }
}

Now add PlayObjectFrameSounds(); to the global repeatedly_execute.

In your room_Load, use
Code: ags
  oEgyptRun1.AddFrameSound(127, aGUNSHOT1);
#103
Oh no. :(
#104
The forum for site issues is here: https://www.adventuregamestudio.co.uk/forums/site-forum-reports/
Somebody will probably move the thread soon.
#105
Here's a fixed version: https://drive.google.com/file/d/1QnSQz-qSLhTmkMyyroxRf7zAtyihp2_0/view?usp=sharing
It also shows characters now and works with scrolling rooms :P
And it detects the Tumbleweed GUI and uses the proper hotspot name without >x.
#106
Here's the module I wrote some time ago:
[link removed, see below]

Import it and call this in game_start:
Code: ags
  ShowHotspots.SetKey(eKeyH); // default key is space
  ShowHotspots.Enable();

Hold down the key to show hotspots and objects. It draws everything on the room background so doesn't require a GUI.
#107
I checked this in the Tumbleweed template; the wait cursor is the crosshairs but it disappears during the fade. So I don't think it's just a matter of a missing Wait cursor sprite.
#108
Sounds like the built-in room fade is hard-coded into the engine in a way that completely halts the main loop, whereas the script command is simply a blocking function like any other which doesn't pause the main loop.

Not sure it qualifies as a bug, but a lot of legacy stuff is somewhat inconsistent internally and should be "fixed" at some point, I guess. Definitely a worthy suggestion for AGS 4.
#109
You need to add padding to the sprite itself afaik. Either manually or in your script (by using a persistent dynamic sprite and drawing the actual sprite on its surface at the desired position).
#110
You have changed the graphic, not the mode. For that reason mouse.SaveCursorUntilItLeaves(); has no effect.
To be fair, the function should probably be named mouse.SaveModeUntilItLeaves(); :P

Anyway, to change the cursor over a hotspot, you should implement this behavior globally. Here's one way:

Code: ags
// above repeatedly_execute
LocationType plt;

  // inside repeatedly_execute
  LocationType lt = GetLocationType(mouse.x, mouse.y);
  if (lt != eLocationNothing && plt == eLocationNothing) {
    // mouse moved over active area
    mouse.ChangeModeGraphic(eModeInteract, 24);
  }
  else if (lt == eLocationNothing && plt != eLocationNothing) {
    // mouse left active area
    mouse.ChangeModeGraphic(eModeInteract, 23); // correct sprite slot goes here
  }
  plt = lt;
#111
Create a GUI (gSpeech) and put a label (lblSpeech) on it.
Set the GUI's y position to a suitable one near the bottom and make it as wide as the screen. Then set its border and background color to a suitable color or to 0 to just display the text.
Make the label as wide as the GUI and adjust the alignment, font and text size to your liking.

Next, add a function to the global script:

Code: ags
function CustomSay(this Character*, String text) {
  int gameloops = ((text.Length / game.text_speed) + 1) * GetGameSpeed();
  lblSpeech.Text = text;
  lbl.TextColor = this.SpeechColor;
  gSpeech.Visible = true;
  SetTimer(1, gameloops);
}

We're using a timer to hide the GUI again, so add this to the repeatedly_execute function:
Code: ags
  if (IsTimerExired(1)) gSpeech.Visible = false;

Next we need to import the function in the global header so we can call it in room scripts, too:
Code: ags
import function CustomSay(this Character*, String text);

You can now do
Code: ags
  player.CustomSay("Hello!");

Note that calling this a second time right after will currently simply overwrite the text and reset the timer, so if you want say commands to queue but still be non-blocking, you will have to program a literal queue mechanism.
#112
In that case you can replace line 6 with
Code: ags
  if (item_is_held && !item_was_held) {
    if (player.ActiveInventory == iBook) aPickupBook.Play();
    if (player.ActiveInventory == iBottle) aPickupBottle.Play();
    // etc.
  }
#113
Your original problem is that you put that code directly into the room script, as opposed to inside a function.
AGS is event-based, so whatever you do, whether it's interact with something, enter a room, click a GUI button, etc. you can associate a function with this event, then put appropriate code inside that function.

AGS complains because while you can in fact use a line like
Code: ags
int trans = 0;
outside a function (this will create a room variable and initialize it to zero) only constant values are allowed here, so your line caused an error.

The main question you need to ask yourself is: *when* do you want that code to run? Fading out an object presumable happens after the player has interacted with something, so you need to create and link the event function as explained in the manual's tutorial, then put the code inside the new function.
#114
If your game is still set to default inv click handling, AGS never runs the "interact with inventory" handler, it simply makes the item active and that's it.

Since using custom inv click handling just to play a sound seems like overkill, you can instead track the mouse Mode:

Code: ags
// above repeatedy_execute in Global Script
bool item_was_held;

  // inside repeatedy_execute
  item_is_held = mouse.Mode == eModeUseinv && player.ActiveInventory != null;
  if (item_is_held && !item_was_held) aPickup.Play();
  item_was_held = item_is_held;
#115
Also note that your question title and lots of its text suggest this problem has something to do with characters or conversations, but that is absolutely not the case. It's simply about reacting differently to the same event.

Identifying what is irrelevant information will usually make it much easier to find existing solutions.
#116
In the engine there's code similar to this:

Code: ags
class Character {
  int x, y;

  function Move(...) {
    ...
  }

  function Say(...) {
    ...
  }

  ...
}

An extender function allows you to "extend" this character class by your own functions, this way you can use them in your scripts like the built-in ones, using dot notation.

When you call player.SayNew(...), "this" refers to the player. When you call cNPC.SayNew(...), "this" refers to cNPC, and so on.
#117
The logo is also a unicode letter: 𝕏

Plus, the trademark for the letter X is owned by a bunch of other companies, Microsoft and Meta among them.
https://www.reuters.com/technology/problem-with-x-meta-microsoft-hundreds-more-own-trademarks-new-twitter-name-2023-07-25/

Incredibly rich, surrounded by yes men and really, actually stupid, like IQ 80 stupid. What could go wrong?

The only nice thing about this is watching the fanboys squirm, wrestling with the fact that supporting their idol will look more and more embarrassing as time goes on and twitter's value goes down.
#118
Nice work, just wanted to point out that AGS supports so-called extender functions.

Code: ags
void SayNew(this Character*, int cue, String text)
{
  String cueString = String.Format("&%d", cue);
  {
    if (Speech.VoiceMode == eSpeechVoiceAndText && IsSpeechVoxAvailable())
    {
      AudioChannel* ch = Game.PlayVoiceClip(myCharacter, cue);
      this.Say(text);
      if (ch != null)
      {
        ch.Stop();
      }
    }
    else if (Speech.VoiceMode == eSpeechVoiceOnly && IsSpeechVoxAvailable())
    {
      this.Say(cueString);
    }      
    else if (Speech.VoiceMode == eSpeechTextOnly)
    {
      this.Say(text);
    }
  }
}

Now you can use
Code: ags
cCharacter.SayNew(1, "the text string you wish the character to say");
#119
First, make sure you have properly unpacked downloaded AGS Editor files. I for instance use D:\AGS\AGS_XXX for each editor version. This way I will never get any issues with disk access or read only errors or the like.

Next you should definitely make a backup copy of the main game source folder. Simply copy the folder, then paste it into the AGS 2.72 folder.

Next, open AGS 2.72, select "Load an existing game", then open the game folder and select the ac2game.dta file.
The game should open. Save and exit.

Now move the folder into a modern version's folder, like AGS 3.2.1 and try to open it there (same basic process, "open existing game", select the main game file)
And so on.
#120
330 pages! Wow :-D

unhandled_event is a built in function. If it exists, AGS calls it whenever an event runs that doesn't have a handler function associated with it. It's closely related to the multi-cursor Sierra GUI and according event functions, so if your game uses the BASS or Tumbleweed template for instance, you're not going to really use it anyway.
Or rather, you should probably implement your own way of handling unhandled events.

Moving stuff into custom functions is a good idea in general, not only makes it your scripts more readable and organized but you can easily change global aspects, indeed.
SMF spam blocked by CleanTalk