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

#1
Hi!

This comes from a Discord chat, but I'm putting it here for future reference and hopefully someone will find it useful!
I was asking on Discord if there's a way to scroll a label in a GUI. eri0o suggested the approach which to take, and crimson wizard developed the code.

The idea here is to use a large label on a much smaller GUI. The GUI should be the size of area that you want the text to be visible on. Any part of the label that goes beyond the GUI will not be drawn to the screen. You can then add a slider which will move the label's position, and so revealing more text as you scroll.

crimson wizard was kind enough to work on the code to make the scrolling as nice as possible. You can see it in the snippet below.

Code: ags
int DEFAULT_LABEL_Y = 10;
int DEFAULT_PAGE_HEIGHT = 178;
int LABEL_LINE_SPACING = 1;

void DisplayQuest(String text)
{
    gQuestGui.Visible = true;
 
    lblQuestDetails.Text = text;
    int text_height = GetTextHeight(text, lblQuestDetails.Font, lblQuestDetails.Width + 1);
    int line_height = GetTextHeight("AAA", lblQuestDetails.Font, 100);
    int num_lines = text_height / line_height;
    lblQuestDetails.Height = text_height + (num_lines - 1) * LABEL_LINE_SPACING;
    
    lblQuestDetails.Y = DEFAULT_LABEL_Y;
    if (text_height <= DEFAULT_PAGE_HEIGHT)
    {
        sldQuestDetails.Visible = false;
    }
    else
    {
        sldQuestDetails.Visible = true;
        sldQuestDetails.Min = 0;
        sldQuestDetails.Max = lblQuestDetails.Height - DEFAULT_PAGE_HEIGHT;
        sldQuestDetails.Value = sldQuestDetails.Max;
    }
}

function sldQuestDetails_OnChange(GUIControl *control)
{
    lblQuestDetails.Y = DEFAULT_LABEL_Y -(sldQuestDetails.Max - sldQuestDetails.Value);
}


Again, big thanks to eri0o and crimson wizard for the help!
#2
Great! Thank you for the help!

I will let you know if the problem persists. It's just hard to tell straight away.
#3
You've given me quite a lot of feedback, so my Trello board is now full of things to change/fix/improve! (laugh)

I followed the instructions on the lip sync found in the Header of the script to find out how to set this up:
Code: ags

  /*
  Note that to get text-based lip sync to work, you need to provide an invisible font,
  and set the SpeechBubble.InvisibleFont property accordingly. You may download one here:
                                                                                         
  http://www.angelfire.com/pr/pgpf/if.html
  */


After importing the font to AGS I simply added this to game_start()
Code: ags
SpeechBubble.InvisibleFont = eFontInvisibleFont;


After some testing I didn't (yet) notice any line skipping, but I might have been just (un)lucky. There is definitely a noticeable change to the dialogue though. Before I've set the invisible font, you could have held down the left mouse button and the dialogue would skip automatically after around one second on each line. After setting the invisible font, nothing happens when you hold down the LMB, so hopefully things will be fixed now.

Is this all there is to it or did I miss something big time? It looks suspiciously too simple to bypass the custom blocking code. Thank you for the response!
#4
Hey! I really like this module, but I have a small hiccup with it recently. Occasionally, the dialogue skips lines waaaay too fast and it's very frustrating. Because it happens only every now and then, it's hard to determine why and when it occurs. I was wondering if anyone else has this issue, or if I messed something up at some stage.

In general settings I've set "Allow speech to be skipped by which events" to Mouse or keyboard. I've tried setting Game.IgnoreUserInputAfterTextTimeoutMs to ridiculous values, but it doesn't change anything. I also played around with some values in the module itself but to no avail.

The only settings I changed for the dialogues are below, although I highly doubt they would have an impact on skipping the lines since they're purely aesthetic changes.
Code: ags

  // Lowers the dialog options a little bit
  game.dialog_options_x = 10;
  game.dialog_options_y = 10;
  // Changes the colour of highlighted dialogue option
  game.dialog_options_highlight_color = 19695;
  // Edit the Speech Bubble Module
  SpeechBubble.BorderColor = Game.GetColorFromRGB(10,12,14);
  SpeechBubble.BackgroundColor = Game.GetColorFromRGB(10,12,14);
  SpeechBubble.BackgroundTransparency = 0;
  SpeechBubble.PaddingTop = 8;
  SpeechBubble.PaddingBottom = 8;
  SpeechBubble.PaddingLeft = 8;
  SpeechBubble.PaddingRight = 8;
  SpeechBubble.MaxTextWidth = 230;
  SpeechBubble.CornerRoundingRadius = 0;
  SpeechBubble.HeightOverHead = 8;
  SpeechBubble.TextAlign = eAlignCentre;


The only other thing I've changed in the module is this:
Code: ags

bool animateSpeech(this Character*, String message)
{
  if(this.Moving)
    this.StopMoving();
  
  if(this.SpeechView > 0)
  {
    this.LockView(this.SpeechView);
    if(Game.GetFrameCountForLoop(this.SpeechView, this.Loop) > 1)
      this.Animate(this.Loop, this.SpeechAnimationDelay, eRepeat, eNoBlock, eForwards);
  }
  
  int speechDuration = calculateDuration(message);
  if(BlockSpeech(speechDuration, true) == eBlockTimeOut)
  {
    //this.UnlockView();                          <---------------------- Commented this out
    int speechPause = calculateSpeechPause();
    return (BlockSpeech(speechPause, false) != eBlockTimeOut);
  }
  else
  {
    //this.UnlockView();                          <---------------------- Commented this out
    return false;
  }
}

When the above wasn't commented out, the characters would keep unlocking the view, so they always went back to idle/talking view when new line of dialogue appeared, so I couldn't get characters to talk while in a special animation (someone sitting on the ground and talking for example). Still, I don't think this would impact skipping the dialogue?

I'm using v0.8.0.

I'm really stuck with this issue and I'm not sure if this is AGS thing or the module or if there could be something else affecting the dialogues. If anyone has any ideas how to go about fixing the dialogue skipping problem, please let me know! Many thanks.
#5
Thanks for speedy a response!

I knew it was something as simple as that. I would have never come up with the fact that you have to check for both things. I thought the check for "null" that I did would have been enough, but it clearly wasn't.

Thanks again!
#6
Hi,

I have multiple buttons in my GUI, and all of them do fairly similar stuff. To avoid re-writing same code over and over in GlobalScript, I wanted to create a function which will be used by multiple buttons. The function detects which button you're clicking on, and based on that you can do various stuff.

The problem I'm having is that the game keeps crashing with "Null pointer referenced", and I have no idea how to fix this. I even simplified the code to just couple of lines to see if I made mistake elsewhere.

I tried running the following code in one function that is hooked to multiple buttons:
Code: ags

  GUIControl* button = GUIControl.GetAtScreenXY(mouse.x, mouse.y);
  if ((button.AsButton.NormalGraphic != 0) && (button != null)) //The game keeps crashing on this line
  {
    Display("It works");
  }


The most frustrating thing is that this code works half of the time! Once you can click on bunch of buttons and everything works, and sometimes the game just crashes.

Any ideas how to make this work? I don't really want to re-write lots of code over and over, but it seems like the only solution at the moment.
#7
Oh yes, I see. Thanks again!

The float is very useful! :-D
#8
Wow, this is awesome! The map is working like a charm now!

I made a small change in the repeatedly execute so that the map doesn't scroll when it's not visible:

Code: ags
int mouse_px = -1; // previous mouse x
int mouse_py = -1; // previous mouse y
GUI* g; // Added GUI pointer
 
function repeatedly_execute( )
{
  g = GUI.GetAtScreenXY(mouse.x, mouse.y); // Check if the you are clicking on the map
    if (mouse.IsButtonDown(eMouseLeft) && (mouse_px!=-1) && (mouse_py!=-1) && (g == gMap)) // Pan around when clicking on the gMap
    {
        readonly int DRAG_SPEED_MULTIPLIER = 1;
        int dx = (mouse.x - mouse_px) * DRAG_SPEED_MULTIPLIER;
        int dy = (mouse.y - mouse_py) * DRAG_SPEED_MULTIPLIER;
        Map.SetViewport(Map.GetViewportX()-dx, Map.GetViewportY()-dy);
    }
    
    mouse_px = mouse.x;
    mouse_py = mouse.y;
}


Thank you so much for this! :-D
#9
Setting up the map as a button seems to work pretty well!

I started playing around with it, and I have managed to make the map stop at the edges of the screen (the map in my game is fullscreen, rather than a section of it).

Code: ags

int mouse_px = -1; // previous mouse x
int mouse_py = -1; // previous mouse y
 
void repeatedly_execute( )
{
  if (mouse.IsButtonDown(eMouseLeft) && (mouse_px!=-1) && (mouse_py!=-1))   
  {
    int dx = mouse.x - mouse_px;
    int dy = mouse.y - mouse_py;
      
    for (int i=0; i < gMap.ControlCount; i++)
    {
      gMap.Controls[i].X += dx;
      gMap.Controls[i].Y += dy;
    }
      
    // Check X
    if (gMap.Controls[0].X > gMap.Width - System.ViewportWidth) gMap.Controls[0].X = System.ViewportWidth - gMap.Width; //gMap.Controls[0] is the button that has the map background
    if (gMap.Controls[0].X < (gMap.X - (gMap.Controls[0].Width - System.ViewportWidth))) gMap.Controls[0].X = gMap.X - (gMap.Controls[0].Width - System.ViewportWidth);
      
    // Check Y
    if (gMap.Controls[0].Y > gMap.Height - System.ViewportHeight) gMap.Controls[0].Y = System.ViewportHeight - gMap.Height;
    if (gMap.Controls[0].Y < (gMap.Y - (gMap.Controls[0].Height - System.ViewportHeight))) gMap.Controls[0].Y = gMap.Y - (gMap.Controls[0].Height - System.ViewportHeight);
  }
  
  mouse_px = mouse.x;
  mouse_py = mouse.y;
}


The problem I'm encountering now is that the other buttons (so buildings) keep sliding after the map background reaches any of the edge.
I tried setting up some kind of bool to stop it from scrolling when you meet the edge, or storing last positions for the buildings, but I can't get it to work properly.
Any idea how to stop the buttons from moving when the map reaches the edge?


EDIT: Wait, hang on, don't reply with solution! I think I got this!

EDIT2: Ok, it's getting somewhere. I now started storing the position of each button, which later is applied. The problem is that if you drag the map real fast, the buildings move by couple of pixel. If I understand correctly, the game doesn't call all the script fast enough, so I imagine I would need to speed up the game (never did this thought, so I'm not sure if that would help here at all?)

Code: ags

int mouse_px = -1; // previous mouse x
int mouse_py = -1; // previous mouse y
int btnX[10];
int btnY[10];
 
void repeatedly_execute( )
{
    if (mouse.IsButtonDown(eMouseLeft) && (mouse_px!=-1) && (mouse_py!=-1))
    {
      // Store button locations
      for (int i=1; i < gMap.ControlCount; i++)
      {
        btnX[i] = gMap.Controls[i].X;
        btnY[i] = gMap.Controls[i].Y;
      }
      
      int dx = mouse.x - mouse_px;
      int dy = mouse.y - mouse_py;
      
      for (int i=0; i < gMap.ControlCount; i++)
      {
        gMap.Controls[i].X += dx;
        gMap.Controls[i].Y += dy;
      }


      // Check X
      if (gMap.Controls[0].X > gMap.Width - System.ViewportWidth) 
      {
        gMap.Controls[0].X = System.ViewportWidth - gMap.Width;
        
        for (int i=1; i < gMap.ControlCount; i++)
        {
          gMap.Controls[i].X = btnX[i];
        }
      }
      if (gMap.Controls[0].X < (gMap.X - (gMap.Controls[0].Width - System.ViewportWidth))) 
      {
        gMap.Controls[0].X = gMap.X - (gMap.Controls[0].Width - System.ViewportWidth);
        
        for (int i=1; i < gMap.ControlCount; i++)
        {
          gMap.Controls[i].X = btnX[i];
        }
      }
      
      
      // Check Y
      if (gMap.Controls[0].Y > gMap.Height - System.ViewportHeight) 
      {
        gMap.Controls[0].Y = System.ViewportHeight - gMap.Height;
        for (int i=1; i < gMap.ControlCount; i++)
        {
          gMap.Controls[i].Y = btnY[i];
        }
      }
      
      if (gMap.Controls[0].Y < (gMap.Y - (gMap.Controls[0].Height - System.ViewportHeight))) 
      {
        gMap.Controls[0].Y = gMap.Y - (gMap.Controls[0].Height - System.ViewportHeight);
        for (int i=1; i < gMap.ControlCount; i++)
        {
          gMap.Controls[i].Y = btnY[i];
        }
      }
    }
 
    mouse_px = mouse.x;
    mouse_py = mouse.y;
}
#10
Quote from: Snarky on Tue 29/08/2017 22:18:36
Sprites don't have x/y properties because you can draw them anywhere on screen. Or, to put it another way, their x/y properties will be the same as the GUI they're being rendered on.

I guess that makes sense, but the reason I tried to figure out the sprite's position is that it seems the GUI's position is useless, since I don't want to drag the GUI, but rather the crop of the map. The GUI should always stay at 0,0 but the crop needs to change and if the GUI is at 0,0 how can you determine the cropped area?

Regarding the "Figure out how to get mouse-dragging to work":
I combined the code from dragging the GUI with Snarky's directions. You can now drag the map, but it's not ideal.

1) It leaves a trail of the map behind (basically the sprites duplicates so many times it creates a pattern)
2) The map jumps back to original position when you unclick and click again (not exactly original position, I guess it's based on the place where you click the second time).


Here's the code:
Code: ags

bool map_is_dragged;
GUI*dragged_map;
int ox, oy; // map offest x/y
int sx, sy; // stored mouse x/y
int dx, dy; // difference x/y
int currentX, currentY;

//When user presses button
void on_event (EventType event, int data) {
  if (event == eEventGUIMouseDown && mouse.IsButtonDown(eMouseLeft)) {
    GUIControl* gc = GUIControl.GetAtScreenXY(mouse.x, mouse.y);
    GUI* g = GUI.GetAtScreenXY(mouse.x, mouse.y);
    if (g == gMapDyna) {
      //Store the map offset
      ox = g.X;
      oy = g.Y;
      
      //Store the mouse coordinates
      sx = mouse.x;
      sy = mouse.y;
      
      map_is_dragged = true;
    }
  }
}
 
//While button is pressed
void repeatedly_execute() {
  if (map_is_dragged) {
    if (!mouse.IsButtonDown(eMouseLeft)) map_is_dragged = false;
    else {
      //Calculate difference between current and stored mouse coordinates
      dx = mouse.x - sx;
      dy = mouse.y - sy;
      
      //If difference is not zero
      if ((dx != 0)&&(dy != 0))
      {
        //Add/subtract difference from the stored map offset
        ox =  ox - dx;
        oy =  oy - dy;
        
        //Update the stored mouse coordinates
        sx = mouse.x;
        sy = mouse.y;
      }
      
      //Re-render the map
      sprite = DynamicSprite.CreateFromExistingSprite(80);
      sprite.Crop(ox, oy, 384, 216);
      
      surface = mapSprite.GetDrawingSurface();
      surface.DrawImage(0, 0, sprite.Graphic);
      surface.Release();
      sprite.Delete();
    }
  }
}



Btw, I know the names of the variables are not so great, I realise that's something I need to work on!
#11
Thank you all for the replies.

I was worried that DynamicSprite + GUI would be the best solution for what I need, because DynamicSprites are... terrifying.

It seems that even something as simple as panning is fairly tricky to do, as DynamicSprites don't have .X/.Y property (unless I'm wrong).
Originally I thought I could use this piece of code and modify it to work with DynamicSprites.

Closest I got to panning the map is this:
Code: ags

DynamicSprite* mapSprite;
DynamicSprite* sprite;
DrawingSurface* surface;

void SetMap()
{  
  mapSprite = DynamicSprite.Create(384, 216);
  
  gMapDyna.BackgroundGraphic = mapSprite.Graphic;

  sprite = DynamicSprite.CreateFromExistingSprite(80);
  sprite.Crop(0, 0, 384, 216);

  surface = mapSprite.GetDrawingSurface();
  surface.DrawImage(0, 0, sprite.Graphic);
  surface.Release();
}

bool map_is_dragged;
int xa, ya;

//Drag the map 
void on_event (EventType event, int data) {
  if (event == eEventGUIMouseDown && mouse.IsButtonDown(eMouseLeft)) {
      map_is_dragged = true;
    }
  }
 
void repeatedly_execute() {
  if (map_is_dragged) {
    if (!mouse.IsButtonDown(eMouseLeft)) map_is_dragged = false;
    else {
      xa = mouse.x;
      ya = mouse.y;
      
      sprite = DynamicSprite.CreateFromExistingSprite(80);
      sprite.Crop(xa, ya, 384, 216);
      
      surface = mapSprite.GetDrawingSurface();
      surface.DrawImage(0, 0, sprite.Graphic);
      surface.Release();
    }
  }
}

Which as you may guess, does not provide desired results.

What happens is that the map follows the mouse in a weird fashion. If I understand correctly, to "move" a DynamicSprite you need to delete it and draw again at a new position. The code above doesn't actually delete anything, because calling sprite.Delete(); does nothing and mapSprite.Delete(); simply gets rid of the map when clicked.

Currently I'm wondering if I need to somehow calculate the map's position using the DynamicSprite.Width/Height properties... But I can't see how that would help. Is there anyway to simply detect DynamicSprite's X and Y values? Surely there isn't anything about it in the manual.
#12
Hey,

I've been recently playing around with a map screen for my game.
The main issue is that the desired map is much bigger then original game size (Game Resolution: 384x216, Map: 975x618) and because of that I struggle to decide on the best approach to set the whole thing up.

Brief idea behind the map's functionality is that you should be able to pan it around. The character does not directly move around the map, but rather it has various locations on top, which you can click to access info about it. A label of some sort would be in place to show the location's name.


I looked around on forums, and it seems that there are three possible approaches, but all with some disadvantages. I wonder if I'm missing anything, or if I just need to stick to one of them and accept certain quirks.

Map as a GUI - seems like the ideal approach for my needs. I can add locations as buttons. I can add lists for location names. Clicking on button can trigger related the stuff. The issue? As you may already figured it out, a GUI cannot be bigger than the game's size. Using this approach gives me a warning when playing the game, but other than that everything works as intended. I guess just ignoring the warning is not acceptable?

Map as a separate Room - at glance, this could be a good alternative for the GUI map. I can pan around with SetViewport(), locations could be in a form of hotspots/objects - I'm not entirely sure about the labels though. The main issue here seems to be more of a visual one - having the map in a separate room means that I will need to have the fade in/out effect when accessing the map. The way all of the GUI in the game is meant to work is that it has this "flow". When you switch between various menus, they slide from one to another, and having this one menu to have to fade in/out is just very out of place.

DynamicSprite Approach - from what I found on the forums, it seems to be a good idea to create a DynamicSprite for the map. From what I can understand you create a DynamicSprite, then create a copy and crop it. I imagine that you can later pan it around achieving the desired effect. However, it seems to me that adding locations or UI info (e.g. label with name) to the map would be a huge hassle. I guess you could create another DynamicSprites for each location on top of the map one, but to declare x,y positions for each location seems like a nightmare, where I can't easily see how the location presents itself on the map.



As already mentioned above, GUI approach would be my favourite, where I can easily move the buildings (buttons) on the map to make sure they look just fine. I can easily add labels, and trigger anything I may need when the building is clicked. I was wondering if there would be some kind of way to perhaps crop the GUI so it does't give me the warning message? So basically the GUI is larger than screen in the editor, but when you start the game the GUI size is cropped down and you can pan around when dragging.

Are there any other ways to deal with the maps in AGS that I missed? Perhaps there's something I missed about the any of the above approaches?
Any suggestions or pointers in the right directions are welcome!
#13
Advanced Technical Forum / Re: Queue System
Thu 13/07/2017 20:22:54
Thank you Snarky!

I understand most of the code from your samples and I can see how it's mean to work. There's one part I still fail to grasp unfortunately - SetupStoreQuests() and how it's meant to be used. How do I go about making use of "Character* client;"? And if I understand correctly, the storedQuests[].questID is meant to help determine which quest needs to be used/referenced ("index to quests[]"). But how do I determine the index to storedQuests[].questID?

Code: ags

// The problem is when I click the character, 
// I want to call the dialogue from "his" quest:
    dialog[quests[storedQuests[???].questID].dialogueID].Start();


All the array-ception is giving me a headache >:(


Also, if I understand correctly, the bool to check the quests, is being used as a function? So I can just call "checkQuest(1)" to run the entire thing at once?


EDIT:

Would I be correct to use this:
Code: ags

function ComeForward(this Character*){
  dialog[storedQuests[this].questID].Start();

//alternatively:
  dialog[storedQuests[this].client].Start();
}


If so, don't they both kind of do the same thing?
#14
Advanced Technical Forum / Re: Queue System
Wed 12/07/2017 20:25:50
Sigh...
I worked on this over the last weekend and after work during weekday - I just can't seem to figure it out. I tried asking around on the AGS discord chat, and every time I thought "I got this!" I actually didn't...

Anyway, I think I nailed down exactly what I need to do and how things should work. I want to set up a pre-defined quest in a struct, later when you click "new day" button, it generates the clients for you. Each person has a quest "assigned" to it (randomly from the list). It seems to me that I'm just missing a way to connect the quests to the characters, here's what I mean.

Code: ags

//I created the struct for the quests, just including the most important info
struct Quest{
    int view;    //how the character looks
    String name;    //name of the character
    String title;    //title of the quest (to be displayed in quest log later)
    String description;    //same as title
    ???    objective;    //no idea how to determine these
    int dialogueID;    //dialogue ID number determines which dialogue should be loaded for the quest
};
import Quest quests[MAX_QUESTS];


In game_start, I call all the quest structs with views, names (picked random from list) and titles hard-coded in (as the quests are pre-defined, I imagine hard-coding them is not considered "bad"?)

I then made another struct, which is basically a duplicate of the Quest struct, but it's used to select the quest from list at random and store information from the quest.

Code: ags

//the struct is exactly the same as quest one
//I'm calling the function when "new day" button is pressed:
StoreQuest(Random(MAX_STORED_QUESTS));

//and here's the set up of the StoredQuests
void StoreQuest(int index){
  int i = 0;
  index = Random(MAX_STORED_QUESTS);
    storedQuests[i].view = quests[index].view; //index ideally sets the quest from the list at random
    storedQuests[i].name = quests[index].name;;
    storedQuests[i].title = quests[index].title;
    storedQuests[i].description = quests[index].description;
    storedQuests[i].dialogueID = quests[index].dialogueID;
    i++;

    index = Random(MAX_STORED_QUESTS);      
    storedQuests[i].view = quests[index].view;
    storedQuests[i].name = quests[index].name;;
    storedQuests[i].title = quests[index].title;
    storedQuests[i].description = quests[index].description;
    storedQuests[i].dialogueID = quests[index].dialogueID;
    i++; 
    etc...


I then created code (a bit buggy at the moment) to create the queue when you click "new day":
Code: ags

//makes people queue up
function QueueUp(){
  int index = 1;
  int qID;
  int idealSpot = 110; //x value for ideal standing spot in queue
  int reduceSpot = 0; //reduce the ideal spot
  
  
  while(index <= MAX_CHARACTERS){    
    characterValue[index] = storedQuests[qID].dialogueID;    //ideally assigns dialogue (and other info) to this character
    
    character[index].ChangeRoom(1, -8-reduceSpot, 195); //spawns character in
    character[index].Walk(idealSpot-reduceSpot, 195, eNoBlock, eAnywhere); //moves it to the position, with each character it's a bit more to the left
    
    index++;
    reduceSpot += 17;
  }
}


So I got the queue working - people are spawning, even moving to correct spots... The problem is when you click on the client to talk to them:
Code: ags

function ComeForward(this Character*){
  this.Walk(160,180, eBlock, eAnywhere); //walks up to the counter
  dialog[storedQuests[qID]].Start();    //ideally the characters quest should start*
}


*This should ideally recognize that character B has a dialogue from the storedQuests struct, but instead it appears to always give me the same dialogue ID depending on the number of the person in the queue. So for example, character A always gives me dialogue01, character B always has dialogue02 and so on. Ideally, when you select character B from the queue, it should have the dialogue that was assigned to him at the QueueUp stage - so something random, not dialogue02 every time.


Hopefully someone can make some sense out of all this. I feel like if I can find a way to assign all the necessary info to each person, I should be able to move on fairly easily with the extra fluff I have in mind. As always, any suggestions are extremely appreciated, but for now I will try to dig deeper into this programming madness. Thanks in advance!
#15
Advanced Technical Forum / Re: Queue System
Sat 08/07/2017 14:35:45
Thank you for the explanation on the enums and I apologise for the confusions and general vagueness. I didn't want to just drop everything at once in the thread, and I thought I can take care of the queue sprites separate from the quests, and figure out the quests at later time (trying to solve the problems one at a time).

Regarding the quests, they would be pre-defined (so like "a"), but I hoped that some parts (like the name or the view) could be picked from random ("b"), hence my initial post was talking about generating people at random. Would this not be efficient thing to do (from programming point of view) and should I just stick to one of the methods (a or b)?
#16
Advanced Technical Forum / Re: Queue System
Sat 08/07/2017 12:10:02
Thank you very much for the replies! Most of this makes perfect sense, but I have a few additional questions/problems.

- I don't understand how "Gender gender;" works - are you basically adding a new property to the struct Gender? Can you not simply declare it in the struct itself?

- I thought a little bit more about how to handle the people in queue. I think that the queue is nothing else than just a representation of quests in the game. I'm thinking of creating a quest struct which will determine all the info I need for the character.

What I mean is: imagine a card game where each card is a quest. When you start the game, you have all the cards available. To generate the "queue" you draw 3 cards each turn at random from the card pile. In the game, the card is represented with a person in the queue, so at the start of the day/turn you draw the cards (people) into the queue and you deal with them one by one.

At first I thought that the person in the queue determines what quests you should have, but after Snarky's post I think it's better to let the quests determine the person it needs to display.

Code: AGS

// new module header
#define MAX_QUESTS 10
struct Quest {
  int questID; //to help determine which quest is picked, not sure if necessary?
  String gender; //I guess this one shouldn't be a string, but the "Gender gender thing" Snarky used in his example?
  String name; //to pick random name from the list (like from Snarky's sample)
  String surname; //same here

  //things below would probably need to be hard-coded 
  String title; //quest title
  String description; //quest description
  int objective; //sample quest would be: get 5 carrots, make a burger etc. not exactly sure how to store this later on
  int dialogueID; //which dialogue to load
  int timeLimit; //how many turns to complete
};
import Quest quests[MAX_QUESTS];


Objective is a tricky one: I imagine it could be an int, but not exactly sure how to determine that you need for example 5 carrots, or tomatoes? Ideally in game you would see: Get 5 carrots. So would I just put these in the struct: "int objectiveNumberOf;, String objectiveItemType;". Later in the quest-log display: "lblObjective.Text = String.Format("Get %d %s", objectiveNumberOf, objectiveItemType);".

I guess I have an idea of setting everything up now, the problem is how to actually call the things in the game. I understand the "void generateClient(int index)" part, but I can't think of a good way to actually use it when the person shows up in the queue. Currently I'm thinking of having something like "CurrentQuest Struct" that will access all the info from the pre-defined quest and which is determined at the beginning of the day, but even then I feel like I'm missing some programming knowledge to fill in the gap from setting up the names, quests etc and later to accessing them in the game. How do you spawn the character with all his info to the room? Would I do "generateClient()" at the beginning of the turn, but tell that void to also show the character on screen, move it etc?


I understand that this suddenly became a lot larger thing than I initially asked, so I will keep looking through the forums and see what I can put togehter.
#17
Advanced Technical Forum / Queue System
Wed 05/07/2017 22:43:45
Hi, I thought this question will be more complex and it felt like advanced section of the forum would be suitable - sorry if that's not the case!

I was playing around with AGS for a little while now, constantly trying to do some more complex bits of code.
I'm currently planning out on how to do a queue system in my project. Imagine a restaurant-type game, where you need to prepare burgers etc. You have various customers that come from left side, stop, the queue gets bigger and you can call each customer to talk to him. He walks up to the counter, orders stuff (through dialogue) and then goes away. There's a bunch of tricks though. I would like the customers to be different every time, and they order different stuff each time, so each customer needs to "hold" some (also random) information.

I'm trying to plan things ahead (I heard that's what real programmers do ;) ), and see what would be the best method to do all of this. I'm not sure if this questions isn't too broad at the moment, so I can always come back here later.

I'm thinking of creating a struct for the characters, so for example:
Code: ags

struct Client {
  String name;
  String foodType;
  int sprite;
  int gender;
};
import Client clients[MAX_CLIENTS];


And then set things up:
Code: ags

clients.name = "Bob";
clients.foodType = "burger";
clients.sprite = Random(10); //select random sprite from 10
clients.gender = Random(2); //1 = male   2 = female


I was thinking of creating two characters in the game: cMale and cFemale. Based on "gender" I decide (TBC how) if we are using cMale or cFemale, and based on the "sprite" I was thinking to use the number to attach the view to the character. I was thinking of using something like: vMale1, vMale2 etc and same for female. That way I could attach a random view to the character. My worry is that I won't be able to have cMale multiple times on the screen, right? In that case I would need cMale1, cMale2 etc? I'm planning to have up to 10 people in the queue at once.

Would a queue system like the one I described (or something similar) be possible in AGS?
I would really appreciate any pointers in the right direction.
#18
Works perfectly.

I really appreciate it Khris.
I guess I owe you a pint (or two)  ;-D
#19
Hmm... Chat.Clear doesn't seem to be working for me. I just double checked. It keeps crashing the game, with the error like in my previous post.

What I'm trying to achieve is something similar to Papers, Please. Basically you chat with a different person every time, and when you're done, it's gone and no longer relevant, so there's no need to store what the previous character has said.

I currently got this to work by creating a new empty chat every time you finish talking to a person. Creating a new chat clears the previous messages, and since there are no messages to display it keeps the chat empty until I do: Chat.Prepare and Chat.Advance.

The problem I have is that after 10 chats have been created, the game crashes. Of course, I can simply increase MAX_CHATS, but I was wondering if there would be a better way to do this.
#20
Hey,

Sorry, it's me again. I'm struggling with something different this time around regarding the chat.

I'm trying to get the chat to disappear after talking to someone and then to reappear when you talk to a different person. The chat should stay visible for the period you are talking to the same character, so for example; you get two messages, they stay on the screen, you solve a puzzle, new messages ADD to the same chat. When you have completely finished talking to this character, the chat clears. When you get a new message, from someone else, all the previous messages disappear and new ones are in place. The idea is that the stuff you do for different characters are not directly related to each other, but it all should be using the same chat window.

Simply creating a new chat each time around seems to do the trick (it clears the previous chat, and lets me to add new messages to the new one) but it seems like I'm just throwing more stuff at the game. I would imagine that deleting a chat and creating a new one (named the same) would just replace the old one, but eventually I reach the limit of the chats available and the game crashes:
"Array index out of bounds", line 155
Code: ags
chatWindow[chatWindows].button = button;

I could increase the number of chats, but it doesn't feel like an elegant way of doing this stuff.

I tried doing a "Chat.CleanUp()" or "Chat.Clear()" after all messages are displayed but I get an error: "DynamicSprite: Cannot get graphic, sprite has been deleted" on line 315.
Code: ags
chatWindow[c].button.NormalGraphic = chatWindow[c].currentView.Graphic;

and on line 358
Code: ags
if (b != null && b.OwningGUI.Visible && b.Visible) UpdateChatWindow(i);


I was trying to figure out a way to get around it, but I'm not sure how you could approach this. Would creating a new chat each round be actually fine (but increase the number of chats limit)? Or should I try something else?
SMF spam blocked by CleanTalk