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

#1
So I've almost gotten the text width-constrained highlighting to work. It waits until you are actually over the text on the x-axis before highlighting, but there is a side-effect where, if you mouse above a line of text, parallel to its x-axis but not on its y-axis, it will still highlight it.

Here's the relevant part of the script, with my addition starting at the "&&" on line 8:

Code: ags

int ypos = 0;
// Find the option that corresponds to where the mouse cursor is
for (int i = 1; i <= info.DialogToRender.OptionCount; i++)
{
  if (info.DialogToRender.GetOptionState(i) == eOptionOn)
  {
    ypos += GetTextHeight(info.DialogToRender.GetOptionText(i), eFontFont0, info.Width - 10);
    if ((mouse.y - info.Y) < ypos && (mouse.x < GetTextWidth(info.DialogToRender.GetOptionText(i), eFontFont0)))
    {
      info.ActiveOptionID = i;
      return;
    }
  }
}


Any ideas?

Here's the complete code from the global script, if anyone would like to test it on their end:

Code: ags

int dlg_opt_acolor = 13;
int dlg_opt_ncolor = 4;

function dialog_options_get_dimensions(DialogOptionsRenderingInfo *info)
{
  // Create a 400x200 dialog options area at (50,100)
  info.X = 50;
  info.Y = 100;
  info.Width = 400;
  info.Height = 200;
  // Enable alpha channel for the drawing surface
  info.HasAlphaChannel = true;
}

function dialog_options_render(DialogOptionsRenderingInfo *info)
{
  int ypos = 0;
 
  for (int i = 1; i <= info.DialogToRender.OptionCount; i++)
  {
    if (info.DialogToRender.GetOptionState(i) == eOptionOn)
    {
      if (info.ActiveOptionID == i)
        info.Surface.DrawingColor = dlg_opt_acolor;
      else
        info.Surface.DrawingColor = dlg_opt_ncolor;

      info.Surface.DrawStringWrapped(5, ypos, info.Width - 10,
              eFontFont0, eAlignLeft, info.DialogToRender.GetOptionText(i));
      ypos += GetTextHeight(info.DialogToRender.GetOptionText(i), eFontFont0, info.Width - 10);
    }
  }
}

function dialog_options_repexec(DialogOptionsRenderingInfo *info)
{
  info.ActiveOptionID = 0;
  if (mouse.y < info.Y || mouse.y >= info.Y + info.Height ||
      mouse.x < info.X || mouse.x >= info.X + info.Width)
  {
    return; // return if the mouse is outside UI bounds
  }

  int ypos = 0;
  // Find the option that corresponds to where the mouse cursor is
  for (int i = 1; i <= info.DialogToRender.OptionCount; i++)
  {
    if (info.DialogToRender.GetOptionState(i) == eOptionOn)
    {
      ypos += GetTextHeight(info.DialogToRender.GetOptionText(i), eFontFont0, info.Width - 10);
      if ((mouse.y - info.Y) < ypos && (mouse.x < GetTextWidth(info.DialogToRender.GetOptionText(i), eFontFont0)))
      {
        info.ActiveOptionID = i;
        return;
      }
    }
  }
}

function dialog_options_mouse_click(DialogOptionsRenderingInfo *info, MouseButton button)
{
  if (button == eMouseLeft) {
    if (info.ActiveOptionID > 0)
    info.RunActiveOption();
  }
}
#2
OK, I missed one small (but integral) detail on my first run through of the 'custom dialog options rendering' manual entry - the bit that says, "you need to do the following:" and that BOTH the dialog_options_get_dimensions and dialog_options_render functions are mandatory. On my first attempt, I tried using only one of the functions, which is why nothing was happening. So I've got it working now...

...partially...

I've got the text colors, placement, and sizing all how I want them, but am still unsure as to how to implement page arrows for overflow options.

Also, I would still like to limit the highlighting action to when you hover over only the actual text of each option, instead of the full width of the drawing surface. I imagine this might be achieved with a GetTextWidth command, similar to the GetTextHeight bits already being used in the code, but am not sure quite how to write that out, and in which part(s) of the script to place it.

Here's my current code, copied and adapted from one of the examples given in the manual article:

Code: ags

int dlg_opt_acolor = 13;
int dlg_opt_ncolor = 4;

function dialog_options_get_dimensions(DialogOptionsRenderingInfo *info)
{
  // Create a 400x200 dialog options area at (50,100)
  info.X = 50;
  info.Y = 100;
  info.Width = 400;
  info.Height = 200;
  // Enable alpha channel for the drawing surface
  info.HasAlphaChannel = true;
}

function dialog_options_render(DialogOptionsRenderingInfo *info)
{
  int ypos = 0;
 
  for (int i = 1; i <= info.DialogToRender.OptionCount; i++)
  {
    if (info.DialogToRender.GetOptionState(i) == eOptionOn)
    {
      if (info.ActiveOptionID == i)
        info.Surface.DrawingColor = dlg_opt_acolor;
      else
        info.Surface.DrawingColor = dlg_opt_ncolor;

      info.Surface.DrawStringWrapped(5, ypos, info.Width - 10,
              eFontFont0, eAlignLeft, info.DialogToRender.GetOptionText(i));
      ypos += GetTextHeight(info.DialogToRender.GetOptionText(i), eFontFont0, info.Width - 10);
    }
  }
}

function dialog_options_repexec(DialogOptionsRenderingInfo *info)
{
  info.ActiveOptionID = 0;
  if (mouse.y < info.Y || mouse.y >= info.Y + info.Height ||
      mouse.x < info.X || mouse.x >= info.X + info.Width)
  {
    return; // return if the mouse is outside UI bounds
  }

  int ypos = 0;
  // Find the option that corresponds to where the mouse cursor is
  for (int i = 1; i <= info.DialogToRender.OptionCount; i++)
  {
    if (info.DialogToRender.GetOptionState(i) == eOptionOn)
    {
      ypos += GetTextHeight(info.DialogToRender.GetOptionText(i), eFontFont0, info.Width - 10);
      if ((mouse.y - info.Y) < ypos)
      {
        info.ActiveOptionID = i;
        return;
      }
    }
  }
}

function dialog_options_mouse_click(DialogOptionsRenderingInfo *info, MouseButton button)
{
  if (button == eMouseLeft) {
    if (info.ActiveOptionID > 0)
    info.RunActiveOption();
  }
}
#3
Hi, folks.

Well I'm now venturing into dialog territory, and I wanted to set up a system akin to the CMI style - a transparent background overlay, different text colors than AGS' defaults, arrows for scrolling copious dialog options, etc.

I quickly gathered that I couldn't have all of these things with the built-in system, so naturally I turned toward using a custom GUI (not a text window GUI either, because I wish to control the placement of the GUI and don't want a border), but now I've hit a few roadblocks. I read through several older posts about similar matters, but am not sure if some things that were previously considered impossible now have workarounds. I also went through the 'custom dialog options rendering' article in the manual and tried implementing some of the functions, but it wasn't quite working out for me (not sure if it's not applicable for what I'm trying to do or if I was just doing it wrong).

Here's what I'm after but haven't been able to figure out:

1. The default color for dialog options. I believe this is normally tied to the player character's speech color, but (if possible) I'd like to be able to adjust this.

2. The highlight color for dialog options when hovered over with the mouse. I know this can be done with a text window GUI, but again I'm not using that here (for the reasons given above), and I saw that someone in an older thread was able to figure out how to change this for Sierra style dialog, but at the time it seemed like it wasn't possible to do so with the method I'm using. I'm hoping this has changed or that one of you clever wizards can come up with something.

3. How would I implement arrows (using custom graphics, no less) for scrolling through excess options? Is it anything like how it's done for inventory pages?

4. This one's just a minor niggle: Is there any way to constrain the act of highlighting an option to hovering over only the actual text of each option, rather than the entire width of the GUI for each line?


Any assistance with these is much appreciated.
#4
Great! Thank you. Seems obvious now, looking back at it.

And thanks again for the clever idea. I'll report back if I encounter any roadblocks implementing it.
#5
Quote from: timid_dolphin on 22 Nov 2021, 19:26
Ok, so here's how it works:

first you need to go into the character editor and add a custom property to the schema for your alternate views, eg angryView. For each character you can initialise this in the character editor with the view you want to use.
Hey, timid_dolphin. I like the sound of this approach for the organization and streamlined scripting, but I'm a little unclear on how to set up the custom properties (i.e., what to set as the type and value, etc.). Are you simply setting the type to "number" and the value to the number of the appropriate view?
#6
Quote from: timid_dolphin on 17 Nov 2021, 04:45
I've got a bunch of general mood functions in a script file with all of my little character animations.
So all of the logic around changing character views is encapsulated in these, and they can be easily called from anywhere in the script, or within dialogue scripts.

eg:
  paulThinking();
PAUL: Hey, wait a second...
  paulAngry();
PAUL: That guy didn't pay for those bananas!
  paulShakeFist();
  paulSigh();
  paulNeutral();
PAUL: Oh well...
Yeah, something like that is what I had in mind for the mood views. Did you just put all of the called functions in your global script or did you create a new script for all of them?
#7
Yeah, the mood-specific view changes would be in addition to the normal view variations, and in those cases, you are exactly right.
#8
Hey, Khris. Maybe I'm not following what you're suggesting, but my idea for a function under repeatedly_execute was for randomizing the plain (moodless) speech view between, say, 3-4 different animations, so that each time the character says something, it won't be the same single view every time, without the need to manually write in a view change for every line of speech. The thought was that whenever the character speaks, it will play the currently assigned view animation, which is continuously, automatically randomizing in the background, without me needing to tell it to use a different view each time. Is there a better approach for such a result?
#9
Yeah, I figured it would come down to having a bunch of different views and changing SpeechView each time, I was just wondering if there was a more clever and efficient way of doing it that I wasn't aware of. Maybe I can throw a function under repeatedly_execute that randomizes the standard speech view automatically, so that I'd only have to write in a manual view change when the character takes on a specific mood, rather than with every single line of dialogue. I'll think on it some more.

Thanks for the feedback, all.
#10
Quote from: Khris on 12 Nov 2021, 08:22
For a second idle view you can use
Code: ags
  cEgo.SetIdleView(ROGERIDLE + Random(1) * (ROGERLEGIDLE - ROGERIDLE), Random(5)+5);

in that case, this will play either view randomly.
Thanks, Khris! And if I wanted to have the foot-tapping happen less frequently than the blinking (say, between every four blinking animations)? Maybe just set a counter within the randomized blinking code that would trigger the foot-tapping instead? Seems fairly straightforward.

Quote from: Cassiebsg on 12 Nov 2021, 16:05
Make sure that all the sprites are in the correct loop.
On my latest game I had a "bug" that was driving me nuts, as my character was facing the wrong way on a single view/loop, and I couldn't figure out why. After several days trying to fix it, I looked at the sprites and noticed that I had a single wrong sprite in that exact loop.  (roll)

Not saying it's your case, but just make sure.  ;)
Thank you for the suggestion, Cassiebsg, but I was sure to double and triple-check and re-assign the sprites again, but the error still occurred. I'm thinking it's either something else in my scripts throwing it off, the idle view not honoring diagonal directions, or indeed a bug, as Khris said.
#11
Quote from: Khris on 11 Nov 2021, 23:27
You mean like the character is both tapping their foot and blinking, independently? That's much harder because AGS doesn't really support animating the head and body separately the way the Lucasfilm games did. It's possible somehow but can't be achieved with just a few lines of code unfortunately.
The animations don't have to occur independently. The foot tapping (or whatever else) can happen between blinking animations by itself, either at specific intervals or randomly too. Thoughts on how best to approach that? Another timer within the function we already created, perhaps?


I've also noticed a strange issue with the blinking animations. Do idle views not honor diagonal loops like walking views do? The wrong blink loops seem to be firing after my character has walked in a diagonal direction and gone idle. The four primary directions seem to be using the correct loops.
#12
Hi folks,

I'd like to figure out (if possible) how to variate characters' speech views so that the player won't see the exact same animation every time they talk.

For example, in a game like CMI, you'll notice that the characters' heads bob and mouths move in a different pattern for each line, even if you repeat the same line of dialogue over and over. This small detail helps to break up the monotony and add pleasing variety to the talking portions of the game.

So how can something like this be achieved in AGS?

I was thinking maybe you could randomize which frame of a long speech view to start with each time a character speaks, but the SpeechView function doesn't have a parameter for the starting frame. I know that the Animate function does, but is that what we'd want to use here?


Getting even more advanced than that, there are also different animations depending on the mood of the dialogue. For instance, the character might be shocked, their eyes wide and arms outstretched, or angry, with a furrowed brow and hands on their hips. And these animations too appear to be varied in their patterns. I'd love to be able to look under the hood and see how they went about doing all of this back then.

But can these things also be done in AGS? I reckon it'll take a significant number of views to animate, but I'm up for it if the results can be as desired.

Thoughts?
#13
Thanks for the suggestions, guys!

Khris, your method did the trick. I also added the cEgo.SetIdleView(ROGERIDLE, Random(5)+5); line to the game_start function, so that the blinking can start right away once the character is idle, rather than after the default idle view start delay. Do you see any potential problems in doing that?


On another note, what would be a good way to implement other idle animations, such as the character tapping their foot, initiated amongst the blinking animations?
#14
Hi folks,

So I'm trying to have my player character blink at random intervals while idle, and I'm having a little trouble wrapping my brain around how to get it to work right. (I know not to use the Blinking View, as I'm not doing a Sierra-style game.)

I have a view (#4) with a single blink animation. Then, in my global script, I tried this:

Code: ags

function repeatedly_execute()
{
  int i;
  i = Random(10);
  cEgo.SetIdleView(4, i);
}


But that's not working as desired. The animation appears to be zipping by more rapidly than if I left it as an ordinary, evenly spaced idle view, and is also occurring way too frequently. I imagine it's got something to do with my randomized SetIdleView delay (which I assumed, from my understanding of the manual description, would be the delay between each playing of the view), but I'm unsure as to what exactly the problem is, and how to fix it.

Any insights (or tips on how to better achieve this in general)?
#15
Eureka! Your last bit of clarification helped me crack it.

In the verb coin script body (not header), I've got the declaration and export:
Code: ags
String region_direction[AGS_MAX_REGIONS];
export region_direction;

(Placement of the export command doesn't appear to need to be at the end of the script after any functions that use the variable...?)

And I put the import command in the global script header so that I don't need to include it in every room script:
Code: ags
import String region_direction[AGS_MAX_REGIONS];


After that, all I needed to do was tell the cursor change function which sprite to use with each directional string text and define the appropriate direction for each region in the "room_Load" function, and voila, works like a charm. I'll cross my fingers that it continues to work in subsequent rooms, but the logic of the scripts implies that it should be fine.

Thanks a bunch, CW! Please let me know if there's any way I may reward you for your time and attention.
#16
Thanks again, CW.

After deleting the global variable, I read the full manual article and thought I was following the instructions correctly, but apparently not, as I'm getting this pop-up error when the game tries to load:
Loading game failed with error: Script link failed. Runtime error: unresolved import 'region_direction'.

So in the GlobalScript header, I declared:
Code: ags
String region_direction[AGS_MAX_REGIONS];


In the room script, after the "room_Load" function that sets the region direction, I exported the variable:
Code: ags
export region_direction;


And in my verb coin script (where the other cursor functions are), I imported it:
Code: ags
import String region_direction[AGS_MAX_REGIONS];


Anything obvious jump out to you as wrong there? Maybe the order in which scripts are read?


Quote from: Crimson Wizard on 24 Oct 2021, 22:09
... but you seem to already had experience with that as you have "region[0]" in your code.
To be fair, that part of the function was a direct copy-paste from the manual section about region functions, although I understood it on a basic level.
#17
Thanks for your assistance, CW. Arrays have been beyond my current skill level.

I'm getting an error on the first line:
Code: ags
String region_direction[AGS_MAX_REGIONS];

Error: Attributes of identifier do not match prototype

Any ideas? Something to do with my global variable, perhaps? (It is set as String type with "left" as the initial value.)

Quote from: Crimson Wizard on 24 Oct 2021, 07:22
Your first code snippet (one with room_Load) does not make much sense, you only test a region under the mouse cursor once at the room load, what if the mouse is not over the region at that time? Then the variable is never set.
Yeah, that didn't seem right. Initially, I had it under "repeatedly_execute", but through my trial and error it eventually ended up under "room_Load" and just stayed there until I came to ask for help.
#18
Hey guys,

I'm trying to have the cursor switch to one of four directional arrows when over different regions that lead to other rooms.

- I have a sprite for each arrow: left (sprite slot 46), right (47), up (48), and down (49).

- I made an "Arrow" cursor mode out of the Usermode1 cursor, with the default image set to 46 (the "left" arrow sprite).

- I have a global variable set up (in the Global Variables pane) named "region_direction", string type, initial value: left.

In my room script, I have the following function to set the "region_direction" accordingly depending on the specific region. In this instance, for region 1, I want to use the "up" arrow.
Code: ags

function room_Load()
{
  Region* r = Region.GetAtScreenXY(mouse.x, mouse.y);
  if (r == region[1]) {
    region_direction = "up";
  }
}


Finally, in the verb coin script (where all of the other cursor related functions are), in the "repeatedly_execute" function, I have the following code to switch the cursor back and forth from the arrow mode to the standard walkto cursor, with the intention of the arrow graphic changing to the appropriate sprite depending on the "region_direction" variable (in this case, sprite 48 for "up"):
Code: ags

Region* r = Region.GetAtScreenXY(mouse.x, mouse.y);
if (r != region[0]) {
  if (region_direction == "up") {
    mouse.ChangeModeGraphic(eModeArrow, 48);
  }
  mouse.Mode = eModeArrow;
}
else {
  mouse.Mode = eModeWalkto;
}


With this, the cursor switches back and forth as expected, but the arrow sprite does not change, just stays on the initial "left" sprite.


I've been trying different variations of this all day. It seemed to make sense in my head but I just can't get it to work. I've read several older threads of similar scenarios, but nothing's seemed to help me so far. I don't know if I'm just missing one important point here, like how script types work with each other, or if the solution is more complex than I initially thought.

Can someone please tell me what I'm doing wrong, or how else to achieve what I'm trying to?

Thank you!
#19
That appears to have done the trick, Khris! Thanks so much. I don't think I would have figured out that conversion necessity on my own, even having read the manual entry. Enjoy your coffee. I'm sure there will be plenty more where that came from ;)
#20
Thank you guys for chiming in.

Quote from: Khris on 08 Oct 2021, 08:14
How and when are you (re-)populating the list of save games?
I have a function that calls it when the save GUI is opened from my main options GUI:
Code: ags

function bSave_OnClick(GUIControl *control, MouseButton button)
{
  lstSaveList.FillSaveGameList();    //call save game list
  lstSaveList.SelectedIndex = -1;    //no selection in list
  gSaveGame.Visible = true;    //open save game GUI
}


Quote from: Gilbert on 08 Oct 2021, 10:44
1. I guess that slot_selected somehow is never set to true, so the false path is always chosen in the if-else if group. (So apart from just checking only the selected index in a Display() you may also check this bool variable.)
The function starting at line 3 of the script in my OP sets the bool to true when the player makes a selection in the list box, confirmed by a "1" display-check of the bool.

Quote from: Gilbert on 08 Oct 2021, 10:44
2. The ItemCount property of a listbos is readonly so you should not write something like "lstSaveList.ItemCount ++". Instead that related line should be SaveGameSlot(lstSaveList.ItemCount +1, tbSaveName.Text); in order to save to a new slot.
When you repopulate the listbox the next time the ItemCount will be updated correspondingly.
I had actually tried it both ways, to the same result, but I've set it back to "+1" in light of Crimson Wizard's note, which I had not gleaned from the part in the scripting tutorial about the "++" shortcut.

Quote from: Gilbert on 08 Oct 2021, 10:44
If this is the case, though, it would also overwrite the same existing slot if you do not choose any slot in the list. Have you checked that too?
With nothing in the list selected, the save gets saved to a new slot, as expected. It's only when an existing slot (any slot) is selected that the top slot (and only the top slot) gets overwritten.
SMF spam blocked by CleanTalk