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

#1
This project started off as many do: someone in a chatroom asking a question about how to do something. Someone asked about how to make text scroll in AGS for a parser game, and something about the question got me thinking about how I might do it.



Four days later, I've managed to hack together a working prototype of the module, dusted it off, and named it Promptly.
Promptly can do a couple of unique things, and it's all based on hacks on top of hacks. This text parser can:

  • Print out and parse dialog options, and then run them.
  • Check inventory items, and interact with them.
  • Provides context fallbacks, so that you don't get gummed up switching from, say, working with items, to talking to people.
  • Lets you show and hide images for things like dialogue and inspecting items
  • Provides scrollback, but lets you take it away in moments that the player needs to focus on just one thing.

There are still a few things I'd like to do with this, such as theme customization / palette swap support for 8-bit projects. Some of the code is a bit messy and requires clean-up, but I'm really happy with what I've managed to accomplish so far. Big shout out to CrimsonWizard for helping me figure out how to split up strings from the parser!

> SOURCE CODE
> DOCUMENTATION
#2
Hey, just in case you were curious about ways to implement this thing, I'm building an inventory-based dialogue system for one of my games. My project is fully open source; you're welcome to take a gander and see how stuff works:

Kind of in a transitional phase right now from an older system, so it currently only works on one character (blonde guy at a party) and most of it needs to be restructured, but it might give you some ideas!

https://codeberg.org/UnicornSausage/deep-cuts/issues/5

The basic concept I have is that certain items are assigned to an off-screen character whose only purpose is to store them.

A speech GUI draws some buttons and counts how many items that character has, then does some magic by filling single-item inventory windows above each button. Those windows ignore clicks, the button underneath checks what item is supposed to be in that slot, then it runs a dialog action on characters when clicked.
#3
General Discussion / Re: Development hell
Fri 24/03/2023 05:50:17
Pretty much all of my game projects are in development hell, but the situation and my state of mind are actually improving.

My Achilles Heel in the past was mainly due to scope creep - a simple idea took on bigger and bigger elements. Early in a game's life, it feels as though your creation can go in any direction: you can design a completely bespoke interface and way of doing things, introduce novel game mechanics, and attempt to do rich world-building.

In my case, this can become a nightmare. An RPG can become an elaborate simulation where the NPCs are designed to follow paths resembling everyday life. A conversation game can expand into an entire cityscape with a transit system and puzzles with a dozen different solutions and outcomes.

This can seem fanciful, and to some degree, can allow you to develop amazing things. But, it can impede development of the actual game, where very little functions outside of the crazy part you built.

My other problem is that I'm a one-man team. I don't like managing other people, can't afford to commission designers, and generally don't feel confident about recruiting a bunch of people or building some kind of community. So, this means that I do all of the programming, character design, background art, animation, music composition, and writing. In the service of making the products of my imagination a reality, it's worth it...but it's exhausting.

What ultimately has helped me is to slow down, take a step back, and take a long break. Game development for me is a hobby, where I explore what's possible for me to create. Taking the whole thing less seriously, and also simplifying the scope of what I want to accomplish, very much help move my closer to the goal.
#4
Quote from: eminigalaxy on Fri 17/03/2023 00:15:30Hey sorry to bother but saw this and was wondering if you ever did end up making a template for the quest UI?  :-D

Not yet, but I could probably knock something together this weekend.
#5
Quote from: Khris on Fri 17/03/2023 08:54:44emini: try sending him a PM maybe :)

Re the code, this:
Code: ags
// Create a Quest
createQuest("Get Ready for Work", "You need to get ready to go to the office. Don't be late!");

// Assign three Tasks to Quest 1
createTask("Get Ready for Work", "Get Dressed");
createTask("Get Ready for Work", "Eat Breakfast");
createTask("Get Ready for Work", "Drink Coffee");
seems pretty redundant, I'd store the quest name in a temporary variable and use that to assign the tasks.

Yeah, that's pretty good feedback. Right now, Tasks and Quests are created by two separate functions for two separate structs, and related to one another by the Quest name.

I guess you could wrap them in another function and pass through Tasks and Quests at the same time, but you'd still have to use the method I illustrated to assign subsequent Tasks to an existing Quest later on.
#6
Quote from: CaptainD on Sat 24/12/2022 10:20:35Aw, you don't count me as a cool person you found on there!  :shocked:  :grin:

Anyway, Here I am although in truth, I'm unlikely to spend a lot of time there unless Twitter does actually implode.

Oops! I thought I had added you!  :shocked:
List updated.  :grin:
#7
With everything going on with Twitter these days, there's been an increased interest in alternative spaces for people to congregate. One of these spaces is Mastodon, which in some aspects is very similar to Twitter due to being designed around microblogging. One fundamental difference, though, is that the network is not just on one website, but many sites that can all talk to each other.

Over the past few months, Mastodon has increased by roughly 2.5 million people, across a staggering 9,000+ servers (called "instances"). With all that being said, Mastodon is part of an even bigger network, though: the fediverse. It's kind of like a modern incarnation of Usenet, but with the conveniences of web interfaces and mobile apps. There are replacements for many different kinds of services: YouTube, Instagram, GoodReads, Reddit, MeetUp, GrooveShark, and Apple Podcasts. WordPress blogs and Drupal sites can integrate with it, and Tumblr and Flickr are mulling over native integrations to the network, too. All of these things are capable of talking to each other through a common protocol: a Mastodon user can follow someone from Pixelfed and see their photos in the stream, or do the same with a PeerTube user and see their videos. The Mastodon user's replies show up as native comments at the post's point of origin.

Anyway, with that explainer out of the way, I wanted to ask: are you on the network somewhere? I've noticed a handful of notable current and former AGS'ers on there so far, many of whom are on GameDev Place. Here are a couple of cool people I've found from the community:


Along with a couple of famous gamedevs:

Those are all of the ones I've managed to find so far. There are tons of indie game devs outside of the AGS Community, and in fact, lots of people from all walks of life on the network. Anyway, if you're interested in joining, or already on here, feel free to drop your link in th replies, and I'll update the list!
#8
Oh, man! This is exactly what I need right now for my projects. Very exciting to see this, would love to get my games working on SteamDeck!
#9
Really cute adaptation, good job!
#10
Hey, so just wanted to bring this to a close - I ended up solving this today! I've been messing with Structs a lot for a different game, and the solution of associating things together ended up being a lot simpler than I was initially giving credit.

TL;DR - The Quest system I was trying to build failed because it was trying to use some kind of ID as a reference point to look up related tasks, but everything was convoluted and built on some very silly assumptions of how best to generate Struct objects and parse through them.

Here's a breakdown of what my system actually does now:

First - I found it much easier to create Struct objects as a separate function not related at all to the Struct object itself. Instead, just get a count of all Quests and Tasks in the game, and increment that number to determine the ID of the next one.

Code: ags

function createQuest(String questTitle,  String questDesc) {
    // Currently refactoring, might not work
  int i = questCount;
  quest[i].title = questTitle;
  quest[i].desc = questDesc;
  quest[i].complete = false;
  questList.AddItem(quest[i].title);
  questCount++;
}

function createTask(String pq, String taskDesc) {
  int i = taskCount;
  task[i].desc = taskDesc;
  task[i].parentQuest = pq;
  task[i].complete = false;
  taskCount++;
}


What I was trying to do before for Struct object generation was super unwieldy. Here's a before and after comparison:

Before:
Code: ags

// Create a Quest
quest[1].Create("Get Ready for Work", "You need to get ready to go to the office. Don't be late!");

// Assign three Tasks to Quest 1
task[0].Create(1`, "Get Dressed");
task[1].Create(1, "Eat Breakfast");
task[2].Create(1, "Drink Coffee");


After:
Code: ags

// Create a Quest
createQuest("Get Ready for Work", "You need to get ready to go to the office. Don't be late!");

// Assign three Tasks to Quest 1
createTask("Get Ready for Work", "Get Dressed");
createTask("Get Ready for Work", "Eat Breakfast");
createTask("Get Ready for Work", "Drink Coffee");


The great thing now is that, the new way of doing things doesn't care about IDs - they're unimportant. Quest names in this system are unique enough, and are what I'm using to do all the lookup logic! AGS literally doesn't care.


Code: ags

// Refreshing the Quest List, which is called every time the Quest UI is invoked.
function refreshQuests() {
  // Refresh the List of Quests
  if (questList.ItemCount > 0) {
    questList.Clear();
  }
  
  // Add Completed Quests
    for (int i = 0; i < questCount; i++) {
      if (quest[i].complete == false) {
         questList.AddItem(quest[i].title);
      }
  }
  
  // Set the Selected Index
  int comboIndex = questList.SelectedIndex + completedQuests;
  
  // Refresh the tasks for good measure
  lookupTasks(quest[comboIndex].title);
  

// Refreshing the Task List, technically this shows up higher in the script so that the prior function can call it.
function lookupTasks(String questTitle) {
  
    // Refresh the Task List
    if (subTasks.ItemCount > 0) {
      subTasks.Clear();
    }
    
    // List all of the Tasks the game knows about
    for (int i = 0; i < taskCount; i++) {
      if ((task[i].parentQuest == questTitle) && (task[i].complete == false)) {
      subTasks.AddItem(task[i].desc);
    }
  }
}



A few weird things I had to do to cover my butt:

Whenever the Quest UI is invoked, it needs to be told to select the topmost item on the Quest list, in order to pull in the Task items associated with a quest.

Code: ags

function btnQuests_OnClick(GUIControl *control, MouseButton button)
{
  int comboIndex = questList.SelectedIndex + completedQuests;
  if (questList.ItemCount > 0) { // Game crashes if it tries to do all this stuff with no quests in the system, so this loop is an extra bit of protection
    refreshQuests();
    qDesc.Text = quest[comboIndex].desc; // Sets the description of the Current Text
    gQuests.Visible = true;
  }
  else {
    Display("You have no quests.");
  }
}


Whenever a selection on the Quest list gets updated, I have to dynamically change everything:

Code: ags

function questList_OnSelectionChanged(GUIControl *control)
{
 int comboIndex = questList.SelectedIndex + completedQuests;
 String selectedQuest = quest[comboIndex].title;
 String selectedDesc = quest[comboIndex].desc;
  
  // Check the quest details
  lookupQuest(selectedQuest);
  
  //Adjust Label
  qDesc.Text = selectedDesc;
  
  
  // Update Listed Tasks
  lookupTasks(selectedQuest);
  
}


Finally, I had to do one really weird thing to account for Completed quests that no longer show up in the UI:

Code: ags

function completeQuest(String questTitle) {


  // Mark all matching tasks as Complete
  for (int i = 0; i < questCount; i++) {
    if ((quest[i].title == questTitle) && (quest[i].complete == false)) {
       quest[i].complete = true;
       completedQuests++; // Increment this so that other quests still properly align with the index
    }
  }

// Mark all matching tasks as Complete
  for (int i = 0; i < taskCount; i++) {
    if ((task[i].parentQuest == questTitle) && (task[i].complete == false)) {
      task[i].complete = true;
    }
  }
  
}


The last part is really important, and addresses the Phantom Task issue from before: when you remove an item from a ListBox, the index for each item in that ListBox changes. If you have a Quest called "Go to Work" with an ID of 0, and a Quest called "Go Home" with an ID of 1, and then mark "Go to Work" as Complete...the selected Index value of "Go Home" is 0 on the ListBox, which will confuse all of the fancy lookup scripts that we've put together. This is gross, and sucks, but the solution is basically to adjust the index value to compensate for the amount of quests that are now marked as Completed.

So, basically, that's it! I apologize for the eyebleed here. In the future, I'd like to offer some kind of template that makes this all super easy, so that other AGS devs can have fun building their own quests with it.
#11
Quote from: Crimson Wizard on Sat 08/01/2022 06:46:31
I'm sorry, I did not have time to look into this yet, and I definitely should have written a better documentation.
Hey, it's totally okay - again, I appreciate what you've made, and am getting great use out of it!

That switch example is nice - I didn't even know about those!
#12
I ended up doing a dirty hack for the time being, but I hope to sit down and adapt it properly at some point. :)

Code: ags

function checkGUI() {
  if (GUI.GetAtScreenXY(mouse.x, mouse.y)) {
    hoverOver = GUI.GetAtScreenXY(mouse.x, mouse.y);
      if ((hoverOver == gExplorer)) {
        DragDrop.AutoTrackHookKey = true;
      }
      else if ((hoverOver == gDetails)) {
        DragDrop.AutoTrackHookKey = true;
      }
      else if ((hoverOver == gResume)) {
        DragDrop.AutoTrackHookKey = true;
      }
      else if ((hoverOver == gConfirmation)) {
       DragDrop.AutoTrackHookKey = true;
      }
      else if ((hoverOver == gDropdown)) {
       DragDrop.AutoTrackHookKey = false;
      }
      else if ((hoverOver == gMainMenu)) {
       DragDrop.AutoTrackHookKey = false;
      }
  }
  else {
    hoverOver = null;
    DragDrop.AutoTrackHookKey = false;
  }
}


Hacky, but it works for now.
#13
This is an amazing module, but I'm absolutely confused by the examples and the reference. Please do not take my following comments as hostile or critical - I have nothing but praise for what you're doing. What's confusing for me is this: the module does a fabulous job when the automation is turned on...but I'm having a very hard time grasping how to do the behavior without automation.

In a nutshell, what I'm trying to do is make draggable GUIs to create a fake desktop. However, I only want to target a specific handful of game GUIs, not all of them. If I have DragDrop.AutoTrackHookKey set to true in my script, every game GUI is draggable in the way that I expect...except, this works with every GUI in the game, not just some of them.

I've tried to follow the examples and use a little scripting to get it to work, but I'm stuck. Here's what I've managed to do so far:

Global Script:
Code: ags


function dragWindow(GUI* Window) { // This is wrong
  if (DragDrop.HookKeyDown()){
    DragDrop.HookObject(1, mouse.x, mouse.y, Window.ID);
  }
  else if (DragDrop.HookKeyUp())
    DragDrop.Drop();{
  }
}


function checkGUI() // running under repeatedly_execute_always{
  if (GUI.GetAtScreenXY(mouse.x, mouse.y)) {
    hoverOver = GUI.GetAtScreenXY(mouse.x, mouse.y);
      if ((hoverOver == gConfirmation)) {
        dragWindow(hoverOver);
      }
  }
  else {
    hoverOver = null;
  }
}



function game_start()
{
  Mouse.Mode = eModePointer;
  DragDrop.Enabled = true;
  DragDropCommon.ModeEnabled[eDragDropGUI] = true;
  DragDropCommon.DragMove = eDDCmnMoveSelf;
  DragDrop.AutoTrackHookKey = false;
  DragDrop.DefaultHookKey = 0;
  DragDrop.DefaultHookMouseButton = eMouseLeft;
  DragDrop.DragMinDistance = 10;
  DragDrop.DragMinTime = 0;
}




I've tried a few different variations for my dragGUI function. If I include the DragDrop.Drop() call, dragging doesn't work at all. If I omit it, the window instead just sticks to my cursor the minute I hover over it.
I've tried going over your code examples and post a couple of times, but I'm feeling pretty lost. What's the proper order of operations for hooking a GUI, dragging it, and dropping it?


I guess what's also confusing involves figuring out how to register when the player is holding down the mouse button and dragging the mouse. Is HookKeyDown supposed to be called from within on_mouse_click / on_key_press and nowhere else?
#14
Okay, I think I've gotten to the root of the phantom task issue now!  :cheesy: (It's not solved yet, but after extensive testing and hacking around, I think I understand the problem).

First off, here's the code of what I have so far.

Code: ags

// Initialize some variables
Quest quest[60];
export quest;
Task task[100];
export task;

int task_counter = -1;
int quest_counter = -1;
int active_quest[60];
int active_quests = 0;

// Creation Functions

function Quest::Create(const string questTitle, const string questDesc) {
  this.title = questTitle;
  this.desc = questDesc;
  this.complete = false;
  questList.AddItem(this.title);
  // Generate an ID using a counter function. Basically, we guess what the array should be based on what the counter says. 
  // We start from an index of -1, because the actual index starts from 0.
  quest_counter +=1;
  this.ID = quest_counter;
  // Set the quest as Active
  active_quest[active_quests] = this.ID;
  active_quests++;
}

function Task::Create(int questID,  const string taskDesc) {
  this.desc = taskDesc;
  this.parent = questID;
  this.complete = false;
  quest[this.parent].children ++;
  task_counter +=1;
  this.ID = task_counter;
}

// Completion Functions

 function Task::Complete() {
  this.complete = true;
}

function Quest::Complete() {
  this.complete = true;
  int q = questList.ItemCount;
  while (q > 0) {
    q--;
    if (questList.Items[q] == this.title) {
      questList.RemoveItem(q);
    }
  }
}

// Update Tasks in the GUI (called later on)

function updateTasks(int quest_id, int task_count) {
// First, clear out the task list completely
 if (subTasks.ItemCount > 0) {
      subTasks.Clear(); // for now, we'll just destroy the task upon completion until we have a better way to update ListBox
    }

// Populate the List with just the relevant Tasks.
  for (int i = 0; i < task_count; i++) {
    if ((task[i].parent == quest_id) && (task[i].complete == false)) {
      subTasks.AddItem(task[i].desc);
    }
}

// Quest GUI Functions
function questList_OnSelectionChanged(GUIControl *control)
{
 int quest_id = quest[active_quest[questList.SelectedIndex]].ID;
 int task_count = quest[quest_id].children;
 String splain = String.Format("%s", quest[quest_id].desc);
  
  //Adjust Label
  qDesc.Text = splain;
  
  
  // Update Listed Tasks
  updateTasks(quest_id, task_count);
  
}



So, this almost, ALMOST works! The problem I'm up against now: it seems that the quest_id in the GUI never increments above 0 when it comes to rendering the subTasks list. In other words, Tasks only show up for the first quest, but nothing after that. I have no idea why this is, or how I might go about fixing it. I'm almost convinced it has something to do with this block:

Code: ags

// Populate the List with just the relevant Tasks.
  for (int i = 0; i < task_count; i++) {
    if ((task[i].parent == quest_id) && (task[i].complete == false)) {
      subTasks.AddItem(task[i].desc);
    }
}


It seems like maybe the "i" variable is getting parsed as zero when
Code: ags
task[i].parent
is called. I'm still noodling on how to work around this...
#15
I'm back from the abyss! I was away at Air Force Basic Training for the last few months, but I have some time now to pick this up again.

I'm still having some challenges with this code. I've managed to clean up a lot of things, but there are two outstanding problems:

1. The "phantom task" issue still happens. No matter what I do, tasks for the secondary quest never show up, despite my counter indicating that
2. If I mark a Quest as "Completed", any Tasks that aren't complete end up getting appended to whatever the next active Quest is.

The biggest wall I seem to be running into is that of associating objects in one array to objects in another array. With regards to the second problem, it would seem straightforward that I could somehow set a boolean state on child objects associated with a parent. However, the process of trying to write some kind of query that can properly crawl from parent to child feels pretty much impossible.

I thought maybe one solution would be to nest arrays inside of a struct in some fashion. It'd be gross, but if I could get an array containing a list of task IDs...it wouldn't be impossible to parse out, because some kind of hierarchy exists to map tasks to a quest. Unfortunately, I can't figure out for the life of me how that might work.
#16
It's a good thought, but unfortunately no dice.

One thought is that maybe new tasks are somehow getting pinned to whatever quest is highlighted at the top of the ListBox. Weird thought, but maybe the creation call for tasks doesn't need to actually add the item to the ListBox at all?

My gut says that maybe this is what is happening: the task gets added to the highlighted active quest. Then, my validation check to see if the IDs match removes it from the listBox, because of course it doesn't match (my latest version of the population code has a second if statement using a NOT operator). Only, because I've done this, there's now no task to actually append to the correct quest.

One fun part of programming is realizing how many potential edge cases a seemingly simple idea misses.  :wink:
#17
So, I've almost got it working now, but with one caveat: the first quest can add and remove quests without any problem, but any subsequent quest never gets the task added, despite the system confirming it exists in the relevant struct.

Global Creation Function:
Code: ags

function Quest::Create(int questID,  const string questTitle, const string questDesc) {
  this.title = questTitle;
  this.desc = questDesc;
  this.complete = false;
  this.ID = questID;
  questList.AddItem(this.title);
  active_quest[active_quests] = this.ID;
  active_quests++;
}

function Task::Create(int questID,  const string taskDesc) {
  this.desc = taskDesc;
  this.parent = questID;
  this.complete = false;
  if (this.parent == quest[this.parent].ID && this.complete == false) {
      subTasks.AddItem(this.desc);
    }
  quest[this.parent].children ++; // Tell the parent quest how many tasks it has
}


Global Header Declarations:
Code: ags

struct Quest {
  String title;
  String desc;
  int ID;
  int children;
  bool complete;
  bool active;
  import function Create(int questID, const string questTitle, const string questDesc);
  import function Complete();
};

struct Task {
  String desc;
  int parent;
  bool complete;
  import function Create(int questID,  const string taskDesc);
  import function Complete();
  int count;
};

import Quest quest[60];
import Task task[100];


GUI Function:

Code: ags

unction questList_OnSelectionChanged(GUIControl *control)
{
  // Dummy function for deriving values from selection
 int quest_id = quest[active_quest[questList.SelectedIndex]].ID;
 int task_count = quest[quest_id].children;
  }
  
  //Adjust Label
  qDesc.Text = quest[active_quest[questList.SelectedIndex]].desc;
  
  
  // Clear the List
 subTasks.Clear();
  
  
  // Populate the List with just the relevant Tasks.
  for (int i = 0; i < task_count; i++) {
    if (task[i].parent == quest[quest_id].ID && task[i].complete == false) {
      subTasks.AddItem(task[i].desc);
    }
  }
  
}


The problem is this: if I create a second quest and a task for that second quest, it briefly shows up appended to the first quest's tasks in the UI:

Creating tasks for first quest:
Code: ags

 quest[0].Create(0,  "Get Ready for Work", "You need to get ready to go to the office. Don't be late!");
 task[0].Create(0, "Get Dressed");
 task[1].Create(0, "Eat Breakfast");
 task[2].Create(0, "Drink Coffee");


Creating tasks for second quest:
Code: ags

   quest[1].Create(1,  "Second Quest", "It's time to start the second adventure!");
   task[4].Create(1,  "Do the first action of the second quest.");


Additionally, "Completing" the first quest causes an off-by-one error with the second quest, where the values being returned are the details of the first quest.
I think now that the problem might have something to do with how the activequest is being declared: currently, it's declared in the create function for every single quest.
#18
Yeah, for some reason I was worried that the list_id was actually just reflecting the integer of the list position, and that I needed to further derive the actual ID stored inside of the struct for that. Removing a quest from the list causes subsequent quests coming after it to spit everything out for a prior entry.

I'll putz around in the morning and see what comes of this.  :wink:
#19
I apologize if my multiple posts on this thread have been disruptive; this is largely serving as an internal monologue as I stumble through ideas of how to do this.

For updating the list of subTasks, the best I can figure is that I'll need to:

1. derive the quest ID from the currently selected quest.
2. Look up which Task structs have a matching quest ID in them
3. Clear the list so that there's nothing on it.
4. Repopulate the List with relevant Task items

I may just bang my head against the wall some more tonight and give up, but I'll probably have something put together tomorrow.

Deriving the ID, Spitting Out Values

So, for the first experiment, I tried to pluck out the ID by running a check: if the existing selection's title matches with an existing Quest struct, spit out relevant details about what's stored in that particular quest struct.

Code: ags


function questList_OnSelectionChanged(GUIControl *control)
{
  // Dummy function for deriving values from selection
  String current_selection = quest[active_quest[questList.SelectedIndex]].title;
  int list_id = quest[active_quest[questList.SelectedIndex]].ID;
  int struct_id = quest[list_id].ID;
  
  // If the current selection matches a Struct that has this title, tell us some stuff.
  if (current_selection == quest[struct_id].title) {
    Display("This matches an existing Quest struct!");
    String name = String.Format("%s",  quest[struct_id].title);
    String splain = String.Format("%s", quest[struct_id].desc);
    bool status = quest[struct_id].complete;
    String fin = String.Format("%d", status);
    Display("%s is the quest", name);
    Display("%d is the ID", struct_id);
    Display("Description: %s", splain);
    Display("Completion status: %s", fin);
  }
  
  // Existing function to adjust the label
  qDesc.Text = quest[active_quest[questList.SelectedIndex]].desc;
}}



So, this part of the debugging script mostly works...if I click on a Sample Quest with a questID of 1, the script will spit out the Title of Sample Quest, an associated description, an ID of 1, and a Completion Status of 0.

Here's the part where everything goes to hell: Let's say that I complete a quest, removing it from the list. Somehow, suddenly, the items that came after the Sample Quest now suffer from an off-by-one error, meaning that the second entry returns the output of what should've come before it.

It seems that somehow, the struct_id value is still somehow dependent on the list's position integer. I was hoping that putting together struct_id would be a way to work around this, but here we are...

I might have to try constructing this another way...
#20
Okay, this has been really fruitful! I guess the last thing at this point: I have a secondary listbox that contains Tasks. A Task Struct looks like this:

Code: ags

struct Task {
  String desc;
  int ID;
  bool complete;
  import function Create(int questID,  const string taskDesc);
};


The associated Task creation function looks like this:

Code: ags

function Task::Create(int questID,  const string taskDesc) {
  this.desc = taskDesc;
  this.ID = questID;
  this.complete = false;
  subTasks.AddItem(this.desc);
}


Tasks get added to a secondary ListBox in the UI called subTasks. How can I change the content of this ListBox when selecting its corresponding Quest?
The Task Struct does accept a questID parameter just like Quests do... I was hoping perhaps there was a way to associate one struct to the other this way, but it's not completely clear how the subTasks ListBox might get updated accordingly.
SMF spam blocked by CleanTalk