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 - Snarky

#221
Yes, it's a project setting (introduced in v3.5.0) called something like "Custom Say/Narrate function in dialog scripts."
#222
General Discussion / Re: Welcome back!
Tue 21/02/2023 15:09:46
There isn't a whole lot of activity on the forums these days, but when they were down I found I kept trying to go here several times per day.
#223
(1) The answer to this depends heavily on the answer to (2), so let's start there...

(2) No, not using the built-in support for voiced speech. What you will have to do is to play the audio separately from doing the speech:

Code: ags
  aGuybrushSpeechBlips.Play();
  cGuybush.Say("Blah blah blah");
  aGuybrushSpeechBlips.Stop();

The last line ensures that the "speaking" audio is cut off if the player skips the line.
(You can also set the audioclip to loop if you want it to keep going for as long as the speech is displayed.)

(1, again) Given that you need multiple lines of code to get the effect you want, the best way to simplify it is to use a helper function. It's similar to a problem I once gave a very detailed explanation of how to solve, so take a look at that (especially steps 3–7), and I'll just put the solution here:

Code: ags
void SayBlip(this Character*, String message)
{
  int audioId = this.GetProperty("SpeechClipId");
  if(audioId > 0 && audioId < Game.AudioClipCount)
    Game.AudioClips[audioId].Play();
  this.Say(message);
  if(audioId > 0 && audioId < Game.AudioClipCount)
    Game.AudioClips[audioId].Stop();
}

And now you can call it like this:

Code: ags
  cGuybrush.SayBlip("Blah blah blah!");

This requires you to have a Custom Character Property called "SpeechClipId". To set the AudioClip that will play when you call Character.SayBlip(), you would have something like this in game_start():

Code: ags
function game_start()
{
  cGuybrush.SetProperty("SpeechClipId", aGuybrushSpeechBlips.ID);
  cElaine.SetProperty("SpeechClipId", aElaineSpeechBlips.ID);
  // And so on ...
}
#224
In on_event(), checking for eEventRestoreGame:

Code: ags
function on_event(EventType event, int data)
{
  if(event == eEventRestoreGame)
  {
    // Code goes here
  }
}
#225
Perhaps, but I feel it would be a shame to take away regular members' ability to revive old threads if appropriate.

I don't really think locking old threads would stop spambots from joining, and currently it serves as a fairly easy way to identify them, so I'd rather have this than something more sneaky.

AGA has talked about bringing back the forum registration quiz, which seems to have been more effective at keeping out the spambots than any of the off-the-shelf protections. IMO that would be a good idea, as long as it doesn't keep bonafide new members from signing up.
#226
To quote myself on the topic of Options Considered Harmful:

Quote from: Snarky on Tue 18/08/2015 14:49:35I think adding another configuration setting so that everyone gets to have it however they like is not a good idea. One definition of design is to impose constraints on a problem, or in other words, to make decisions. But since Pumaman, CW and the other AGS developers have always been bombarded with different opinions and ideas, all too often the "decision" has been to have it both ways: to make it optional, configurable. In the long run, all these configuration options makes the system hard to learn and understand, difficult to support and debug (if someone has a problem, you first need to figure out exactly which of a million possible configurations they're using), and increasingly impossible to maintain.

So I would strongly argue that it's better to just make a decision, one way or another, than take the "coward's way out" and add another configuration option.

I personally would like to keep them (and update them), but maybe the presentation could be tweaked to make it less jarring, to put the pictures into more context.
#227
Well, it also depends on the distance over which the fade happens. If the distance is 800 pixels, then each 8-pixel step is only 1% of that anyway, so we can't get any more smoothness. But if the distance is, say, 200 pixels, then each step is a 4% change in transparency, and it makes sense to smooth it out.

This approach does introduce an absolute limit to the speed at which the fade can happen (1% per game loop, so 2.5 seconds to complete the whole fade), and therefore if your character can run across the distance much faster than that, we might have to add another condition to speed it up in that case, so the player doesn't have to wait for the fade to catch up at the end. For example:

Code: ags
function Room_RepExec() {
  int startX = 123; // Change to the X coordinate where you want the fade-in to begin
  int endX = 321;   // ... and where you want it to complete
  int tr = 100*(endX - player.x)/(endX-startX); 
  if (tr < 0) tr = 0; // clamp value to allowed range
  if (tr > 100) tr = 100;

  if(gSomeGUI.Transparency + 6 < tr) gSomeGUI.Transparency = gSomeGUI.Transparency + 2;
  else if(gSomeGUI.Transparency < tr) gSomeGUI.Transparency = gSomeGUI.Transparency + 1;
  else if(gSomeGUI.Transparency - 6 > tr) gSomeGUI.Transparency = gSomeGUI.Transparency - 2;
  else if(gSomeGUI.Transparency > tr) gSomeGUI.Transparency = gSomeGUI.Transparency - 1;
}

(This makes it fade twice as quickly if the actual transparency differs by more than 6 percentage points from what it should be at this position.)
#228
So that code worked for you? Great!

There is another small improvement we can make in case a particular problem arises:

If the character movement is linked to animation (often called "anti-glide"), and the character movement speed (the distance it moves per animation frame) is large, and the animation delay is significant, then the fade-in effect could appear "jerky," with sudden jumps in transparency.

Specifically, this could become noticeable if AnimationDelay > 2, and 100*MovementSpeed/(endX-startX) >= 2 (but especially if it is some higher number like 4 or 5).

If this is not the case, we don't need to worry about it, but if it does arise, we can achieve a smoother fade this way:

Code: ags
function Room_RepExec() {
  int startX = 123; // Change to the X coordinate where you want the fade-in to begin
  int endX = 321;   // ... and where you want it to complete
  int tr = 100*(endX - player.x)/(endX-startX); 
  if (tr < 0) tr = 0; // clamp value to allowed range
  if (tr > 100) tr = 100;

  if(gSomeGUI.Transparency < tr) gSomeGUI.Transparency = gSomeGUI.Transparency + 1;
  if(gSomeGUI.Transparency > tr) gSomeGUI.Transparency = gSomeGUI.Transparency - 1;
}
#229
Quote from: glurex on Mon 13/02/2023 06:28:00First of all: I never said Khris code doesn't work wich would be disrespectful.

I'm sure that's appreciated, but the reason I asked you to explain how something doesn't work is for effective communication, not respect. And you implied that his code didn't do what you wanted it to, without explaining how it differed:

Quote from: glurex on Sun 12/02/2023 21:14:53Well, I undestand what your code does, but I realise I didn't explain myself well. What I want to do is that the GUI appears slowly as my character walks from A to B, being A gDark.Transparency=100; and B gDark.Transparency=0;. But in the middle of the path from A to B, gDark should has a range of transparency (99, 98 ... 1, 0). So the effect is that the gDark appears very slowly while the character walks up to the B point (a region that trigger an event).   

... Because what you describe is exactly what Khris's code ought to do, so without you describing how his code fails to match what you want, it's hard to offer any further help.

Here's a slightly modified version where you just need to enter the X coordinates of Point A and Point B:

Code: ags
function Room_RepExec() {
  int startX = 123; // Change to the X coordinate where you want the fade-in to begin
  int endX = 321;   // ... and where you want it to complete
  int tr = 100*(endX - player.x)/(endX-startX); 
  if (tr < 0) tr = 0; // clamp value to allowed range
  if (tr > 100) tr = 100;
  gSomeGUI.Transparency = tr;
}

Note that we don't use player.Moving or any kind of loop: the transparency is directly tied to the character position, so it will automatically update as the character moves. And therefore we don't need any Waits or anything like that: the mapping of the position to transparency ensures that it fades in to reach full opacity only when the character arrives at the target X.
#230
Quote from: glurex on Mon 13/02/2023 06:10:09With Khris code as is, the GUI becomes solid very fast.

Well, that's probably because the distance you want the GUI to fade in over is more than 100 pixels. You would need to scale it depending on the actual coordinates in your game, like you started to do in one of your earlier examples. That's just a simple tweak.

In general, if something doesn't work, please don't just say "it doesn't work," but explain what it does and how that differs from what you want to happen.

Where do you put the code in your workaround, BTW? Putting a Wait() in a repeatedly_execute function is a bad idea.
#231
Why couldn't you just use Khris's code?

The only issue I can see with it is that depending on your animation speed, it could get jerky, since it will only update when the character changes position.
#232
Are you sure you included line 1 of Khris's code? What you describe would be the expected behavior, I think, without that line, because you would be restarting the walk or animation every frame, which would block it from ever progressing.
#233
Oof. I think there are only 6 possibilities, so on that first guess you were at least guaranteed not to miss.

Spoiler
stare, state, stage, stake, stale, stave

I assume Wordle wouldn't pick "stade" or "stane".
[close]
#234
Wordle 596 2/6*

🟨🟩⬜🟨⬜
🟩🟩🟩🟩🟩

Spoiler
CASER, DANCE
[close]

How about Quordle scores?

Daily Quordle 377
6️⃣5️⃣
4️⃣7️⃣
quordle.com
⬜⬜⬜⬜🟨 ⬜⬜⬜⬜⬜
⬜🟩⬜🟩⬜ 🟨🟩⬜⬜⬜
⬜⬜⬜⬜🟨 ⬜⬜⬜⬜⬜
⬜⬜⬜⬜🟨 🟩⬜⬜⬜⬜
⬜🟩⬜⬜🟩 🟩🟩🟩🟩🟩
🟩🟩🟩🟩🟩 ⬛⬛⬛⬛⬛

🟨⬜⬜⬜⬜ ⬜⬜🟩⬜⬜
⬜⬜⬜🟨🟨 ⬜⬜⬜⬜🟨
⬜🟨⬜⬜🟩 ⬜⬜⬜⬜⬜
🟩🟩🟩🟩🟩 🟩🟩⬜⬜⬜
⬛⬛⬛⬛⬛ 🟩⬜⬜⬜⬜
⬛⬛⬛⬛⬛ ⬜⬜🟨⬜⬜
⬛⬛⬛⬛⬛ 🟩🟩🟩🟩🟩
#235
Quote from: xboxown on Sat 04/02/2023 18:37:53
Code: ags
    if ((cHelperCX100.y-itemY)>20)
    {
      cHelperCX100.Walk(cHelperCX100.x-(cHelperCX100.x-itemX),cHelperCX100.y-(cHelperCX100.y-itemY),eBlock,eWalkableAreas);
    }
    else if (cHelperCX100.y-itemY<20)
    {
      cHelperCX100.Walk(cHelperCX100.x-(cHelperCX100.x-itemX),cHelperCX100.y-(cHelperCX100.y+itemY),eBlock,eWalkableAreas);
    }
    else
    {
      cHelperCX100.Walk(cHelperCX100.x-(cHelperCX100.x-itemX),cHelperCX100.y,eBlock,eWalkableAreas);
    }

Let's examine your code a little more closely. The cHelperXC100.Walk() commands have inline calculations where you first add the cHelperCX100.x/y values, and then subtract them again. That will of course cancel out, so if we simplify, we get:

Code: ags
    if ((cHelperCX100.y-itemY)>20)
    {
      cHelperCX100.Walk(itemX,itemY,eBlock,eWalkableAreas);
    }
    else if (cHelperCX100.y-itemY<20)
    {
      cHelperCX100.Walk(itemX,-itemY,eBlock,eWalkableAreas);
    }
    else
    {
      cHelperCX100.Walk(itemX,cHelperCX100.y,eBlock,eWalkableAreas);
    }

It now should become apparent that the case if (cHelperCX100.y-itemY<20) is wrong. You're telling it to go to the -itemY coordinate, but this will be some point off screen.

I would also point out that the tests here are... odd. You're checking if some value is <20, then if the exact same value is >20, and finally, if neither is the case, you do the horizontal walk. In other words, you only do a horizontal walk if cHelperCX100 is exactly 20 pixels below itemY. I doubt that this is the effect you intend. I presume that what you're actually trying to do is to say that if the character Y value is within 20 pixels of the item Y value, we're close enough, so just move horizontally. Otherwise move in both the x and y direction. To do this, the tests need to be done like:

Code: ags
    if ((cHelperCX100.y-itemY)<20 && itemY-cHelperCX100.y<20)
    {
      cHelperCX100.Walk(itemX,cHelperCX100.y,eBlock,eWalkableAreas);
    }
    else
    {
      cHelperCX100.Walk(itemX,itemY,eBlock,eWalkableAreas);
    }

It would be even better to use an Abs() function, but let's skip that for now.
#236
Yes, it's possible.

There are three main approaches I can think of, depending on how the appearances vary:

1. They only vary in color. In this case, you may be able to just recolor the sprites (though AGS has limited support for this, and it will take some coding). This is how Zanthia's different outfits in Kyrandia II: Hand of Fate (not an AGS game) were done, using palette mapping.
2. They vary in some small additional element (like, for example, a hat). In this case you may have them as a separate sprite that follows the character.
3. In the most general case, you can just switch out the character sprites/animations with another set of sprites/animations, to achieve any kind of variation you want.

I'll assume the third case, which is the simplest to implement (but requires you to create more different sprites). Basically, you'll have to create different versions of every animation, one for each appearance. You set these up in separate Views.

Then there are two possibilities: You can make each variation a separate Character (with its Normal and Speech views set to the appropriate Views), and to choose the appearance you set that Character as your player character. In that case, you'll have to store any other animations as additional loops under those views, and make sure the loop numbers match in the different variations.

Or you can have just the one Character, and instead change its Normal/Speech views when you change the appearance. In that case, you can have other animations in other views, but you'll have to use some logic to play the right version each time.
#237
The error means that the save is incompatible with the current game, because you have made changes to the game project.

Keep in mind that saves persist across each time you run the game, so if you don't save first on this run, it will load a save from the last time you ran the game. And if you've made changes to the game in the mean time, you will get this error.
#238
The Rumpus Room / Re: Guess the TV show
Fri 03/02/2023 06:42:24
Oh wow, I was way off! I somehow got it into my head that it was a sitcom I vaguely recalled about two tacky (Florida?) women, and I was trying to remember the name of it.

It's GLOW, of course. Good show!

(If someone else wants to have a go, you can take my turn, so it's not just a back-and-forth.)
#239
As a script module writer, some modules can get very complex, and it would sometimes be useful to break them up into separate scripts. This would help keep individual files simpler, but also add flexibility for users about what functionality they need and what they don't, and allow the creation of "libraries" to provide features used within multiple script modules, without requiring code duplication.

For example, for the SpeechBubble module, it would be useful to have the code for text formatting/rendering and for formatting GUIs with outlines, corners and backgrounds as separate pieces of functionality in their own scripts, as well as the code that replicates AGS's speech blocking logic (and other missing Wait() functions).

Of course, you can do this already, but it means that there are dependencies between the scripts, and there is no good way to distribute them all together.

Similarly, someone might create a script module that uses e.g. Tween, or some other pre-existing module, but then they have to explain to users that they need the other module, and that it has to be placed higher in the hierarchy, etc.

I think a good way to solve this problem would be if it were possible to save and distribute the module as a (sorted) script folder. When imported, it would create the folder in the script hierarchy. (And to forestall one objection: it would be easy as a module author to use #ifdefs to avoid a case where the same script is imported twice in two folders, e.g. a bundled copy of Tween into a project that already has it.)
#240
I would also add:

-each time the player quits/exits to main menu/brings up the main menu

The only little wrinkle is that you should then ensure that when the game is loaded, it doesn't start off with those menus open. You can do this by closing them in on_event(), inside a check if(data==eEventRestoreGame).
SMF spam blocked by CleanTalk