Show Posts

You can view here all posts made by this member. Note that you can only see posts made in areas to which you currently have access.

Messages - Snarky

Pages: [1] 2 3 ... 283
I'm not sure how to do those kinds of walks off the top of my head, but I know it's part of the Tumbleweed Verbs template (see "Semi-blocking movement functions").

This question is not for a specific game, I just experiment with AGS and try to see what is possbile, so it can depend on any of the things you listed.

And that's why it's not built in to AGS: Unlike hotspots, which are stationary and easy to deal with, there's no one standard behavior that is right for characters. It depends, and you have to decide what you want to happen before we can figure out how to make that happen.

I just thought about additional problem. If the player character is already near the NPC character, but the offset makes it walk to a different position, the player will walk to it instead speaking to the NPC from the current position.

So put a test for that in ApproachCharacter().

If characters can be anywhere on the screen, with different WalkTo behavior in different parts of the screen, you might need to define regions, and a lookup table from each region to its WalkTo point/offset.

This part I do not understand.

OK, imagine that there's a game where you're a prisoner in a prison. There's a room with other characters (prisoners and guards) walking around dynamically. You want to have a rule like:

-If you try to talk to a prisoner or guard who's in the main hall, you just walk up to them.
-If you try to talk to a a guard who's on the catwalk above (where you can't go), you walk to a point beneath them to the side.
-If you try to talk to a prisoner who's in one of the locked cells, you walk to the nearest ventilation duct.

(All of this is in one big, scrolling background.)

One way to do this would be to define different regions on the background. The hall would be one region, the catwalk another region, the cells a third region. When you try to talk to a character, the game checks what region they're in, and looks up the x and y offset for that region in the lookup table. E.g. for the hallway that could just be x+10 (you stand next to them). For the catwalk it might be y+150, x-20 (you stand below them to the side). For the locked cells it might be x-40 (you stand by the ventilation duct outside the cell).

In extreme situations such as this you might want to use different logic/calculations for each case, not just different values, but hopefully you see the point.

1. Every interaction (look, talk, interact etc) type of every character needs to have this boilerplate code.

You can make it a function and reduce the boilerplate to one line:

Code: Adventure Game Studio
  1. void ApproachCharacter(this Character*, Character* npc, int xOffset, int yOffset)
  2. {
  3.   this.Walk(npc.x + xOffset, npc.y + yOffset, eBlock, eWalkableAreas);
  4.   this.FaceCharacter(npc);
  5.   npc.FaceCharacter(this);
  6. }

Now you can call it like so:

Code: Adventure Game Studio
  1. function cNPC_Look()
  2. {
  3.   cPlayer.ApproachCharacter(cNPC, 0, 15);
  4.   cPlayer.Say("I have no idea who is this");
  5. }

If that's still too much work, you can write some helper functions, e.g. for Say():

Code: Adventure Game Studio
  1. void SayToCharacter(this Character*, Character* npc, int xOffset, int yOffset, String message);
  2. {
  3.   this.ApproachCharacter(npc, xOffset, yOffset);
  4.   this.Say(message);
  5. }

And now the whole function is a one-liner again:

Code: Adventure Game Studio
  1. function cNPC_Look()
  2. {
  3.   cPlayer.SayToCharacter(cNPC, 0, 15, "I have no idea who is this");
  4. }

2. The "15" magic number needs to be separate for each character and updated after every change to the position of the character.

Does the "magic number" depend on the character (e.g. with cAlice you should always be 10 pixels away), on where they are on the screen (e.g. if you're talking to a character who's behind the desk, you should always stand in a certain position), or both? Does it need to change depending on the character scaling? Do your characters stand in one of a number of pre-defined locations, or do they wander about anywhere on the walkable area?

If it just depends on their position on the screen (from some limited list of possible positions), you should define a lookup table from each position to the corresponding WalkTo point. If characters can be anywhere on the screen, with different WalkTo behavior in different parts of the screen, you might need to define regions, and a lookup table from each region to its WalkTo point/offset. If it just depends on the Character, you should make a custom property. If it depends on both, you need to do both, and define the formula to calculate the combined value. If it depends on scaling, you need to include that in the calculation.

3. If a mistake is made, there is no way to catch it before testing. Even indirect changes can have effect. For example a walk area can be reduced to exclude this point.

If a WalkTo point falls outside a walkable area, the character should walk to the closest point it can reach. If that's not acceptable, what is it that you want to happen?

4. If the character can move during the game, it requires "15" to be calculated dynamically (how?)

See above.

You're still not indenting the code correctly. Here's how it should be formatted:

Code: Adventure Game Studio
  1. function oController_Interact()
  2. {
  3.   cBob.LockView(2);
  4.   cBob.Animate(1, 5, eRepeat, eNoBlock, eForwards);
  5.   cBob.UnlockView();
  6. }

The rule is really very simple: between the { and } brackets, you indent by one level (which is usually two spaces in AGS). You align the matching brackets vertically with each other. All together, this makes up a code block. If you have another block inside this block (for example after an if-statement, or the body of a loop), you indent it by another level. For example:

Code: Adventure Game Studio
  1. function oController_Interact()
  2. {
  3.   cBob.Say("Let's see... Do I feel like playing computer games right now?");
  4.   if(bobWantsToPlayComputerGames)
  5.   {
  6.     cBob.LockView(2);
  7.     cBob.Animate(1, 5, eRepeat, eNoBlock, eForwards);
  8.     cBob.UnlockView();
  9.   }
  10.   else
  11.   {
  12.     cBob.Say("Nah!");
  13.   }
  14. }

Except that if you have a block with only one line of code (like the else-block here), you don't need the curly brackets, so you can just write:

Code: Adventure Game Studio
  1. function oController_Interact()
  2. {
  3.   cBob.Say("Let's see... Do I feel like playing computer games right now?");
  4.   if(bobWantsToPlayComputerGames)
  5.   {
  6.     cBob.LockView(2);
  7.     cBob.Animate(1, 5, eRepeat, eNoBlock, eForwards);
  8.     cBob.UnlockView();
  9.   }
  10.   else
  11.     cBob.Say("Nah!");
  12. }

See? Easy!

2. I was trying to make it so the character would sit in front of the TV until any click happened, and then he'd return to the original walking view. Since the click was not associated with an object, I wasn't sure if I needed to declare a new function or not.


I just didn't know if I needed to create a different function when I needed a mouseclick without an object.

Well, there are ways to make sure a function is called on any click even if it's not over an object, but it's a little more involved. You can't just name the function AnyClick() and expect AGS to automatically call it. AGS doesn't for the most part understand or care about your function names. There are a few built-in names with special meaning, but usually you have to make sure the function is hooked up to an event, or called from some other function. In this case, you could for example call it from the on_mouse_click() function (on_mouse_click() is one of those built-in functions, and automatically gets called any time the mouse is clicked). However, you would need to keep track of when to call it, since you don't want this to happen on any mouse click anywhere in the game.

There's an easier way, which doesn't require a separate function. First, let's see why your function doesn't quite work. It's because when you call cBob.Animate() with eNoBlock, that function doesn't block (logically enough). This means the program immediately goes on to the next line, where you unlock Bob's view. When that happens, Bob reverts to the standard standing/walking view, and the animation is interrupted.

So you do need it to block during the animation. Unfortunately, Animate() can't take something like eBlockUntilMouseClick as an option, but what you can do is insert a WaitMouseKey() line between the Animate() and UnlockView() calls:

Code: Adventure Game Studio
  1. function oController_Interact()
  2. {
  3.   cBob.LockView(2);
  4.   cBob.Animate(1, 5, eRepeat, eNoBlock, eForwards);
  5.   WaitMouseKey(GetGameSpeed()*60); // Blocks for one minute, or until player presses a mouse button or keyboard key
  6.   cBob.UnlockView();
  7. }

This does almost what you specified. It will block until a user clicks a mouse button... or a keyboard key... and it will time out automatically after a minute. (You can make the duration longer, but the maximum is about 13:40 at the default game speed.)

Maybe that's good enough for you. If not, you would have to write it some other, more complicated, way. Or you could use the code I recently wrote for exactly this situation. Create a new script, copy that code into it, and call it like this:

Code: Adventure Game Studio
  1. function oController_Interact()
  2. {
  3.   cBob.LockView(2);
  4.   cBob.Animate(1, 5, eRepeat, eNoBlock, eForwards);
  5.   BlockMouse(); // Blocks until player clicks a mouse button
  6.   cBob.UnlockView();
  7. }

So, in my character's code, this is what i have in its entirety for this function.
Code: Adventure Game Studio
  1. function oController_AnyClick()
  2. {
  3.   cBob.ChangeView(2);
  4.   cBob.ChangeRoom(1, 540, 320);
  5. cBob.Animate(1, 5,  eOnce, eNoBlock);
  7. function AnyClick()
  8. {
  9.   cBob.ChangeView(1);
  10. }

Any help is appreciated!

There are three problems with this code:

1. You're missing the end-curly-bracket } at the end of oController_AnyClick(). That's why it's complaining about nested functions: you're trying to declare function AnyClick() inside another function.
2. When do you want function AnyClick() to be called, and how do you expect this to happen?
3. Line 5, cBob.Animate(), is wrongly indented. This is just a matter of formatting, but it's an important part of writing code, and it's better to get into the right habits from the start. If you'd been consistent about this, maybe you would have spotted problem 1 yourself.

Points 1 and 2 make me wonder if you really understand what a function is, and how the syntax works.

General Discussion / Re: Learning good coding practices
« on: 14 Dec 2017, 17:13 »
Yeah, don't know about courses or resources. StackOverflow can have some good tips and discussions that you can glean useful principles from, but they want very specific, concrete questions.

As for my advice, some simple rules of thumb:
  • Indent.
  • Avoid using hardcoded numbers in your code. Use enums where it makes sense, create constants, or even just a variable. It makes your code much easier to follow (as long as you use good names, as Mandle already mentioned).
  • Don't Repeat Yourself (DRY): If you're writing the same code over and over again, make it a function! Sometimes even a one-line function can be useful and make your code more readable (for example something like, max(), min() and abs()).
  • In-fucking-dent!

And some more general principles:

Naming is important not just for variables, but for functions, types, modules, properties... everything! I read somewhere not too long ago that (good) professional programmers spend a significant chunk of their time worrying about naming things correctly. The reason for this is that comments are well and good, but it's better if the code itself is clear about what it does. By naming things well, your code will be self-documenting. Compare this:

Code: Adventure Game Studio
  1. trk = pick(c, 634, 2);

With this:

Code: Adventure Game Studio
  1. jukeboxTrack = dialogNpc.JukeBoxSelect(JUKEBOX_TRACK_MAX, eMusicCountry);

Don't be afraid to rename a variable or a function after the fact (using Search-Replace, for example).

Indentation is just one important part of keeping your code well organized. Related functions should go together. If you can put them in a script module, do it! If some function is messy and confusing, break it up into smaller chunks (e.g. create a helper function to do part of the work). And be consistent in how you name things and how you format things!

A related concept is encapsulation: wrapping up some chunk of code so that you can use it without worrying about just how it works. For example, in AGS, Character.Walk() is a piece of encapsulated functionality: it does pathfinding, movement, animation, (potentially) sound, etc., but you don't have to worry about all of that when you use it. By bundling together some useful behavior and giving it a meaningful name, it provides something really powerful and simple to use. The simplest form of encapsulation (apart from just a variable) is functions. Structs offer more advanced encapsulation, where you can offer a bunch of related functions and data. Script modules are another way to encapsulate code.

Finally, I have two general approaches for how you get there:

If you're anything like me, your first goal when you program is just to get something working. But once your code does (more or less) what you wanted, go back and fix it up. Does your function have the right name? Maybe it should take different arguments, or be an extender function? Maybe you can generalize it a little so that it can be used other places as well. Maybe some complicated part of it should be its own function. Maybe this code should be its own module. Maybe some part of it is hard to follow and needs comments. In this post I start from a chunk of code and show how you can turn it into a useful module through a few rounds of refactoring.

Top-Down Coding
Alternatively, it's sometimes helpful to work top-down: Just create a placeholder function that "magically" does what you need it to do. Instead of code, put in comments about what needs to happen. Once you've done that, imagine that you have the functions and data structures that you need to achieve the task easily, and put in calls and references to them. Rinse and repeat for each function. (The data structures you just have to actually set up at some point.) For example, in this thread we talked about writing a 3x3 slider puzzle, and I suggested starting by defining the high-level functions you need:


With these functions, coding a slider puzzle should be pretty easy. Then we just have to figure out how to write them... So, for example:

Code: Adventure Game Studio
  1. bool isPuzzleSolved()
  2. {
  3.   // Loop through each tile, check that every one is in the right position
  4. }

Knowing when to apply top-down vs. bottom-up coding, and when to refactor, is what it's all about. It's really just something that you learn from experience.

Thanks! I also enjoy Miez's Indiana Jones and the Temple of Spheres. I was also thinking about non-AGS adventures. Are there any commercial games set at Christmas?

Christmas! It's the most wonderful time of the year, according to some. I'm a total sucker for all the sappy, romanticized Christmas kitsch, and I'm sitting here looking at the snow falling outside, about to bake some lussekatter (saffron buns).

So, in the spirit of the season, what are some good Christmas-y adventure games?

Modules & Plugins / Re: Speech Bubble Module (v0.7.6)
« on: 12 Dec 2017, 19:19 »
Thanks again, Dave! ;-D

I've forgotten to mention that bx83 hired me to develop this module for his upcoming game (and agreed to let it be made available to the AGS community), so he deserves the kudos for its existence. Check out his game when it's finished: from what I've seen it looks pretty slick!

Not Wineskin as such, but I've been playing several AGS games with Wine on my MacBook Pro recently, including Technobabylon. It works well. The only little issue I'm having is that you have to click-tap the touchpad to click (normally you can just tap without actually pressing the pad down).

The Rumpus Room / Re: *Guess the Movie Title*
« on: 11 Dec 2017, 17:53 »
The Long March?

Audio stuttering is standard AGS behavior when some operation takes too long: the engine doesn't get around to filling the audio buffer in time, and so it replays the last few milliseconds. When you Alt-Tab, Windows probably has to give CPU time to a lot of other processes, so AGS doesn't get enough to run smoothly.

There's basically no way to prevent it, unless you want to try the build that has audio processing in a separate thread.

So yeah, it turns out that on_key_press() and on_mouse_click() aren't called during a Wait() (which I guess sort of makes sense given that you check for whether the game is blocked with IsInterfaceEnabled()). I therefore had to implement it with Mouse.IsButtonDown() and IsKeyPressed(). I referred to monkey0506's KeyPressAlways module for some of the keycode logic. It seems to work pretty well after I make sure to check whether a button/key was down last loop as well as right now.

Can't be bothered to do all the cleanup and package it up as a module, but here's the raw code:

Code: Adventure Game Studio
  1. //Header
  3. /// Like eKeyNone, a value used to accept any mouse button
  4. #define eMouseNone 0
  6. /// The way in which a block came to an end
  7. enum BlockEndType
  8. {
  9.   eBlockTimeOut,
  10.   eBlockKeyInterrupt,
  11.   eBlockMouseInterrupt,
  12.   eBlockUnknownInterrupt
  13. };
  15. /// Blocks for loops number of game loops. Just like Wait(), but you can have loops > 32767
  16. import BlockEndType Block(int loops);
  17. /// Blocks for loops number of game loops, or until a key is pressed. If loops<0, will wait indefinitely. If keyCode is set, will ignore other keys
  18. import BlockEndType BlockKey(int loops=-1, eKeyCode keyCode=eKeyNone);
  19. /// Blocks for loops number of game loops, or until a mouse button is pressed. If loops<0, will wait indefinitely. If button is set (must be eMouseLeft, eMouseRight or eMouseMiddle), will ignore other mouse buttons
  20. import BlockEndType BlockMouse(int loops=-1, MouseButton button=eMouseNone);
  21. /// Blocks for loops number of game loops, or until a key or mouse button is pressed. If loops<0, will wait indefinitely. If keyCode is set, will ignore other keys. If button is set (must be eMouseLeft, eMouseRight or eMouseMiddle), will ignore other mouse buttons
  22. import BlockEndType BlockMouseKey(int loops=-1, eKeyCode keyCode=eKeyNone, MouseButton button=eMouseNone);
  23. /// Blocks for loops number of game loops, or until interrupted, using the current Speech.SkipStyle and .SkipKey settings. If the current SkipStyle doesn't use a time-out, it will only time out if forceTimeOut is set to true  
  24. import BlockEndType BlockSpeech(int loops, bool forceTimeOut=false);

Code: Adventure Game Studio
  1. // Script
  3. #define MAX_WAIT 32767
  5. #define CTRL_OFFSET 64
  6. #define ALT_OFFSET (-236)
  7. #define CTRL_RANGE_START 1
  8. #define CTRL_RANGE_END 26
  9. #define ALT_RANGE_START 301
  10. #define ALT_RANGE_END 326
  11. #define MAX_KEYCODE 435
  13. #define eKeyLeftShift 403
  14. #define eKeyRightShift 404
  15. #define eKeyLeftCtrl 405
  16. #define eKeyRightCtrl 406
  17. #define eKeyLeftAlt 407
  18. #define eKeyRightAlt 420
  20. int _waitLoops;
  21. eKeyCode _interruptKey;
  22. MouseButton _interruptButton;
  24. bool _wasButtonDown;
  25. bool _wasKeyDown;
  27. int _maxInt(int a, int b)
  28. {
  29.   if(a>b) return a;
  30.   return b;
  31. }
  33. int _minInt(int a, int b)
  34. {
  35.   if(a<b) return a;
  36.   return b;
  37. }
  39. bool IsButtonDownAny(static Mouse)
  40. {
  41.   return Mouse.IsButtonDown(eMouseLeft) || Mouse.IsButtonDown(eMouseRight) || Mouse.IsButtonDown(eMouseMiddle);
  42. }
  44. bool IsKeyPressedCombi(eKeyCode keyCode)
  45. {
  46.   if(keyCode >= CTRL_RANGE_START && keyCode <= CTRL_RANGE_END)
  47.   {
  48.     return (IsKeyPressed(eKeyLeftCtrl) || IsKeyPressed(eKeyRightCtrl)) && IsKeyPressed(keyCode+CTRL_OFFSET);
  49.   }
  50.   else if(keyCode >= ALT_RANGE_START && keyCode <= ALT_RANGE_END)
  51.   {
  52.     return (IsKeyPressed(eKeyLeftAlt) || IsKeyPressed(eKeyRightAlt)) && IsKeyPressed(keyCode+ALT_OFFSET);
  53.   }
  54.   else
  55.     return IsKeyPressed(keyCode);
  56. }
  58. bool IsModifier(eKeyCode keyCode)
  59. {
  60.   return (keyCode == eKeyLeftShift || keyCode == eKeyRightShift ||
  61.           keyCode == eKeyLeftCtrl  || keyCode == eKeyRightCtrl  ||
  62.           keyCode == eKeyLeftAlt   || keyCode == eKeyRightAlt);
  63. }
  65. eKeyCode GetKeyPressed()
  66. {
  67.   for(int i=1; i<MAX_KEYCODE; i++)
  68.   {
  69.     if(!IsModifier(i) && IsKeyPressed(i))
  70.       return i;
  71.   }
  72.   return 0;
  73. }
  75. bool IsKeyPressedAny()
  76. {
  77.   return (GetKeyPressed() != 0);
  78. }
  80. bool CheckKey(eKeyCode keyCode)
  81. {
  82.   if(keyCode == eKeyNone)
  83.     return IsKeyPressedAny();
  84.   else
  85.     return IsKeyPressedCombi(keyCode);
  86. }
  88. bool CheckMouse(MouseButton button)
  89. {
  90.   if(button == eMouseNone)
  91.     return Mouse.IsButtonDownAny();
  92.   else
  93.     return Mouse.IsButtonDown(button);
  94. }
  96. BlockEndType EndBlock(BlockEndType endType)
  97. {
  98.   _waitLoops = 0;
  99.   _wasButtonDown = false;
  100.   _wasKeyDown = false;
  101.   return endType;
  102. }
  104. BlockEndType Block(int loops)
  105. {
  106.   while(loops>0)
  107.   {
  108.     _waitLoops = _minInt(MAX_WAIT, loops);
  109.     loops -= _waitLoops;
  110.     Wait(_waitLoops);
  111.   }
  112.   return eBlockTimeOut;
  113. }
  115. BlockEndType BlockKey(int loops, eKeyCode keyCode)
  116. {
  117.   _interruptKey = keyCode;
  118.   if(loops<0)
  119.   {
  120.     // Keep waiting indefinitely until the WaitKey() was interrupted
  121.     while(true)
  122.     {
  123.       _waitLoops = MAX_WAIT;
  124.       WaitKey(MAX_WAIT);
  125.       // If the wrong key, ignore the interruption
  126.       if((keyCode == eKeyNone && _waitLoops != 0) || IsKeyPressedCombi(keyCode) || _wasKeyDown)
  127.         return EndBlock(eBlockKeyInterrupt);
  128.     }
  129.   }
  131.   while(loops>0)
  132.   {
  133.     _waitLoops = _minInt(MAX_WAIT, loops);
  134.     loops -= _waitLoops;
  135.     WaitKey(_waitLoops);
  136.     if((keyCode == eKeyNone && _waitLoops != 0) || IsKeyPressedCombi(keyCode) || _wasKeyDown)
  137.       return EndBlock(eBlockKeyInterrupt);
  138.     loops += _waitLoops;
  139.   }
  140.   return EndBlock(eBlockTimeOut);
  141. }
  143. BlockEndType BlockMouse(int loops, MouseButton button)
  144. {
  145.   _interruptButton = button;
  146.   if(loops<0)
  147.   {
  148.     while(true)
  149.     {
  150.       _waitLoops = MAX_WAIT;
  151.       WaitMouseKey(MAX_WAIT);
  152.       if(CheckMouse(button) || _wasButtonDown)
  153.         return EndBlock(eBlockMouseInterrupt);
  154.     }
  155.   }
  157.   while(loops>0)
  158.   {
  159.     _waitLoops = _minInt(MAX_WAIT, loops);
  160.     loops -= _waitLoops;
  161.     WaitMouseKey(_waitLoops);
  162.     if(CheckMouse(button) || _wasButtonDown)
  163.       return EndBlock(eBlockMouseInterrupt);
  164.     loops += _waitLoops;
  165.   }
  166.   return EndBlock(eBlockTimeOut);
  167. }
  169. BlockEndType BlockMouseKey(int loops, eKeyCode keyCode, MouseButton button)
  170. {
  171.   _interruptKey = keyCode;
  172.   _interruptButton = button;
  173.   if(loops<0)
  174.   {
  175.     // Keep waiting indefinitely until the WaitKey() was interrupted
  176.     while(true)
  177.     {
  178.       _waitLoops = MAX_WAIT;
  179.       WaitMouseKey(MAX_WAIT);
  180.       if(CheckMouse(button) || _wasButtonDown)
  181.         return EndBlock(eBlockMouseInterrupt);
  182.       if(CheckKey(keyCode) || _wasKeyDown)
  183.         return EndBlock(eBlockKeyInterrupt);
  184.       // Fallback if we can't identify what was pressed, but it doesn't matter
  185.       if(_waitLoops != 0 && button == eMouseNone && keyCode == eKeyNone)
  186.         return EndBlock(eBlockUnknownInterrupt);
  187.     }
  188.   }
  190.   while(loops>0)
  191.   {
  192.     _waitLoops = _minInt(MAX_WAIT, loops);
  193.     loops -= _waitLoops;
  194.     WaitMouseKey(_waitLoops);
  195.     if(CheckMouse(button) || _wasButtonDown)
  196.       return EndBlock(eBlockMouseInterrupt);
  197.     if(CheckKey(keyCode) || _wasKeyDown)
  198.       return EndBlock(eBlockKeyInterrupt);
  199.     // Fallback if we can't identify what was pressed, but it doesn't matter
  200.     if(_waitLoops != 0 && button == eMouseNone && keyCode == eKeyNone)
  201.       return EndBlock(eBlockUnknownInterrupt);
  202.     loops += _waitLoops;
  203.   }
  204.   return EndBlock(eBlockTimeOut);
  205. }
  207. BlockEndType BlockSpeech(int loops, bool forceTimeOut)
  208. {
  209.   switch(Speech.SkipStyle)
  210.   {
  211.     case eSkipKey:
  212.       if(forceTimeOut)
  213.         return BlockKey(loops, Speech.SkipKey);
  214.       else
  215.         return BlockKey(-1, Speech.SkipKey);
  216.     case eSkipKeyMouse:
  217.       if(forceTimeOut)
  218.         return BlockMouseKey(loops, Speech.SkipKey);
  219.       else
  220.         return BlockMouseKey(-1, Speech.SkipKey);
  221.     case eSkipKeyMouseTime:
  222.       return BlockMouseKey(loops, Speech.SkipKey);
  223.     case eSkipKeyTime:
  224.       return BlockKey(loops, Speech.SkipKey);
  225.     case eSkipMouse:
  226.       if(forceTimeOut)
  227.         return BlockMouse(loops);
  228.       else
  229.         return BlockMouse(-1);
  230.     case eSkipMouseTime:
  231.       return BlockMouse(loops);
  232.     case -1: // In AGS 3.4.0, Speech.SkipStyle reports -1 instead of eSkipTime(==2) when set to that value
  233.     case eSkipTime:
  234.       return Block(loops);
  235.   }
  236. }
  238. function repeatedly_execute_always()
  239. {
  240.   if(_waitLoops != 0)
  241.   {
  242.     _wasButtonDown = CheckMouse(_interruptButton);
  243.     _wasKeyDown = CheckKey(_interruptKey);
  244.   }
  245.   if(_waitLoops > 0)
  246.     _waitLoops--;
  247. }

Sorry, I missed that. Thanks!

I'm having a weird issue with Speech.SkipStyle. When it is set to eSkipTime (which is 2), it reports its value as -1 instead:

Code: Adventure Game Studio
  1.   Speech.SkipStyle = eSkipTime;
  2.   Display("Speech.Skipstyle is %d. eSkipTime is %d", Speech.SkipStyle, eSkipTime);

This displays "Speech.Skipstyle is -1. eSkipTime is 2"

Other than that, it appears to work OK. Speech behaves correctly: it times out but cannot be dismissed with mouse or keyboard.

If I try to actively set Speech.SkipStyle to -1, the game crashes, with an error message saying I used an illegal value.

(Though since there's no WaitMouse(), I'll probably have to build my own solution anyway, with Wait(1); in a loop, and flag that gets set on the keyboard/mouse events.)
It seems safer to go with on_key_press() and on_mouse_click().

I just tried to implement this, but it doesn't seem like on_key_press() or on_mouse_click() get called during a Wait()?

Modules & Plugins / Re: Speech Bubble Module (v0.7.6)
« on: 09 Dec 2017, 16:00 »
Yeah, I just came across that issue as well. I've put in a check for it. Thanks!

Modules & Plugins / Re: Speech Bubble Module (v0.7.6)
« on: 09 Dec 2017, 14:29 »

You mean this template? I haven't used it, but as far as I can tell it doesn't really change anything to do with speech, so you should be able to use both the Tumbleweed Verbs template and the SpeechBubble module at the same time without issue. So you just import the module into your game project. (Save the scm file to your computer, and from the AGS "Explore Project" pane, right-click on "Scripts" and choose "Import Script...")

To get the speech bubble effect, you have to call Character.SayBubble(). So you should put that wherever you had Character.Say() before (where Character is replaced by the name of some character, or the special player variable).

So instead of this:

Code: Adventure Game Studio
  1.   cRoger.Say("It's a blue cup.");

You write this:

Code: Adventure Game Studio
  1.   cRoger.SayBubble("It's a blue cup.");

In dialog scripts, you must use the command instead of the simple "name: line" format, and have to indent the line with spaces or tabs. So instead of this:

Code: Adventure Game Studio
  1. player: What we have here is a failure to communicate.
  2. Davy: What was that?

Put this (Notice that the lines start with two spaces):

Code: Adventure Game Studio
  1.   player.SayBubble("What we have here is a failure to communicate");
  2.   cDavy.SayBubble("What was that?");

There's not much more to it. Hope that answers your question!

Pages: [1] 2 3 ... 283