HTML [solved] [Sugarcube] using twine arrays in javascript for quest system

Frogface29

Newbie
Feb 22, 2022
43
41
Hi!

I am stuck on this issue now for over a day. I am trying to create a way for me to dynamically create quests. For that, I use a widget
Code:
<<widget "createQuest">>
        <<set _questID = _args[0]>>
        <<set _questName = _args[1]>>
        <<set _questDescription = _args[2]>>
        <<set _questMoneyReward = _args[3] || 0>>
        <<set _questXPReward = _args[4] || 0>>
        <<set _questConsequences = _args[5] || "N/A">>
        <<set _questConditions = _args[6] || "N/A">>

        <<set State.variables[_questID] = {
            name: _questName,
            description: _questDescription,
            moneyReward: _questMoneyReward,
            xpReward: _questXPReward,
            consequences: _questConsequences,
            conditions: _questConditions,
        }>>

        <<run $activeQuests.push(_name[])>>

        <<print "Created Quest: " + JSON.stringify(State.variables[_name])>>
    <<else>>
        <<print "Quest with name '" + _name + "' already exists. Use a different name.">>
    <</if>>
<</widget>>
My issue is the following:
I want to use this array now in javascript to create a custom modal. I have created one that works when i put in the values manually.
You don't have permission to view the spoiler content. Log in or register now.

What I can't figure out is how to connect the two, so that I can use something like this
<<class "nameOfQuestionModal">><<link "He offers you a quest">>
<</link>><</class>>


to create the specific modal using the name I declared in the widget.

I hope my explanation made sense.
 

guest1492

Member
Apr 28, 2018
311
262
NOTE: I'm going to ignore the JS you have for now.

In your widget, you have <<else>> and <</if>> but no <<if>>. I'm going to assume you left that out accidentally during your copy/paste.

Code:
<<run $activeQuests.push(_name[])>>
I don't understand why there are square brackets there. You also didn't define _name in the widget, so it will only work if you had it defined elsewhere. (You also used _name in the print macro call.) You probably meant to use _questname. If that's true, are you sure you want to put the quest name in the array and not the quest ID?

Code:
<<class "nameOfQuestionModal">><<link "He offers you a quest">>
<</link>><</class>>
I'm not sure what you intend here. Are you trying to call up a modal window that'll have the link, or are you trying to set up a link that will call up a modal window? (Also, do you not know how to use SugarCube's buit-in dialog or did you just want to create your own?)

How does the widget factor into this? Does the widget get called before the link is clicked or after? If it's before, are you sure you want to be adding the quest to $activeQuests before the player accepts? Or are quests automatically accepted and only removed from $activeQuests when they're completed?
 
Last edited:

Frogface29

Newbie
Feb 22, 2022
43
41
NOTE: I'm going to ignore the JS you have for now.

In your widget, you have <<else>> and <</if>> but no <<if>>. I'm going to assume you left that out accidentally during your copy/paste.

Code:
<<run $activeQuests.push(_name[])>>
I don't understand why there are square brackets there. You also didn't define _name in the widget, so it will only work if you had it defined elsewhere. (You also used _name in the print macro call.) You probably meant to use _questname. If that's true, are you sure you want to put the quest name in the array and not the quest ID?

Code:
<<class "nameOfQuestionModal">><<link "He offers you a quest">>
<</link>><</class>>
I'm not sure what you intend here. Are you trying to call up a modal window that'll have the link, or are you trying to set up a link that will call up a modal window? (Also, do you not know how to use SugarCube's buit-in dialog or did you just want to create your own?)

How does the widget factor into this? Does the widget get called before the link is clicked or after? If it's before, are you sure you want to be adding the quest to $activeQuests before the player accepts? Or are quests automatically accepted and only removed from $activeQuests when they're completed?
Sorry for the confusion. While posting the code I made some changes to my twine code in twine and forgot to transfer all over. You are correct, I use _questID.
<<run $activeQuests.push(_questID)>>
For printing the array, I now use <<print "Created Quest: " + JSON.stringify(State.variables[$activeQuests[0]])>> which works well. - I have also added <<update>> to the widget from the liveupdate macro of CyCy.

Next I will use
You don't have permission to view the spoiler content. Log in or register now.
to get my questID. Now I want to use the questID that I got by putting it in the span to pull the quest with the same questID from the $activeQuests array.
Using this pulled quest I want to input the "details" of it into variables that I will then put into the modal

So in short: quest widget creates a quest with questID. In the same or a later passage, I use a span and a link to run the code that displays the modal. The first class name in the span will be the questID that I want to pull from the activeQuests array (created in the widget). Then I want to pull the individual "details" of the pulled-out quest and put it into the modal.
 

Frogface29

Newbie
Feb 22, 2022
43
41
How does the widget factor into this? Does the widget get called before the link is clicked or after? If it's before, are you sure you want to be adding the quest to $activeQuests before the player accepts? Or are quests automatically accepted and only removed from $activeQuests when they're completed?
Just saw this. You are right. Should be stored first in $pendingQuests and then moved into $activeQuests when accepted! Would I move the quest if accepted using State.variables.activeQuests.push(State.variables.pendingQuests(questID))?

Thanks
 

guest1492

Member
Apr 28, 2018
311
262
Code:
<span class="questID target"><<link "click me">><<goto "nexxt">><</link>></span>

Right now, assuming all your code is complete and you get it to work, clicking the button both opens up your modal and sends the player to another passage immediately. Is that really the functionality you're looking for?

Anyway, the way you are doing this seems a little overly complex for something that can be done in a simpler way. I would have just done something like this:

Code:
<<link "Show Active Quest">><<run setup.showQuest("insert quest ID here")>><<goto "nexxt">><</link>>
<<link "Show Pending Quest">><<run setup.showQuest("insert quest ID here", true)>><<goto "nexxt">><</link>>
You don't have permission to view the spoiler content. Log in or register now.
 
Last edited:

Satori6

Member
Game Developer
Aug 29, 2023
330
600
Would I move the quest if accepted using State.variables.activeQuests.push(State.variables.pendingQuests(questID))?
I assume you'd also want to remove it from the pending quests.
Code:
State.variables.activeQuests.push(State.variables.pendingQuests.splice(State.variables.pendingQuests.indexOf(questID),1)[0]);
 

Frogface29

Newbie
Feb 22, 2022
43
41
Thank you for your help so far. My bad for the confusion, I should have provided my full code/the document. I have removed anything unnecessary from the document. The document contains code where the modal works when i hardcode the variables - instead of using an array.

The <<goto>> part was for me to see if the link actually works or is broken.

If you don't feel comfortable downloading stuff from strangers - and you are still interested in helping -, i have put all the javascript code
You don't have permission to view the spoiler content. Log in or register now.

I am sorry if the code is a bit all over the place. I am teaching myself to code so I am guessing whatever I am doing is pretty inefficient. I need the 4 spans for the border styling of the modal.

The issue why I can't implement your code is that in my code the creation of the modal and the showing of the modal separate.

I am sure there is a better way to do it but what i think would work is if i take a JS variable called questID that i specify on the passage and then use that questID to pull the matching questID out of the $pendingQuestions array and implant it into another JS variable.

In pseudo code:
var questID = "quest1" //quest1 comes from the passage
var quest = State.variables.pendingQuestions(questID) //the quest with the matching questID gets pulled out and put in quest
contentSection.innerHTML = `Quest name: ${quest.name}`;


Would this as a concept work or am I mistaken here? If it is a viable solution, how would I go about it?
 

guest1492

Member
Apr 28, 2018
311
262
If you want to handle your modal using multiple separate functions, then that's your choice. At any rate, I still think you are overcomplicating things by trying to read attributes and properties of HTML elements in order to pass a value to your function(s).

If you don't know how to pass values as arguments to a function, you can try reading up on it on or .

If you look at my previous post, you can see an example of calling a function with parameters by using a run macro inside a link macro.

Side note:
Sorry, I didn't realize you were using Hituro's macros, so your initial SugarCube code confused me because I thought <<class "nameOfQuestionModal">> was some sort of pseudo-code that meant you were going to wrap the link with an HTML element that has its class set to a quest ID. That or you wanted to create your own macro that calls up a modal.


EDIT:
If you do know how to call a function with parameters, but it just doesn't work for you, it's because you are not using SugarCube's object. You can see how I have my function assigned to the setup object in the previous post as well.

If you want to know why it works this way, it's because of scope or namespace. It's sort of complicated and I'm not very good at explaining stuff so you may want to look it up yourself.

You don't have permission to view the spoiler content. Log in or register now.
 
Last edited:
  • Like
Reactions: Frogface29

Alcahest

Engaged Member
Donor
Game Developer
Jul 28, 2017
3,119
4,021
I am sorry if the code is a bit all over the place. I am teaching myself to code so I am guessing whatever I am doing is pretty inefficient.
My suggestion is that you start simpler, from the beginning, to learn more about basic things and SugarCube, instead of running away to do more complicated things. In this and earlier threads asking for help, you have relatively complicated code, often unnecessarily so, for someone who is a beginner, while asking about basic things like variables.

If you don't understand everything you're doing when you're doing it, you will produce bad code that is hard to maintain later, and when you release the game and people start reporting bugs, you risk abandoning the project because it's too much of a mess or you don't know how to fix the issues. There are loads of such abandoned games here. Try to avoid becoming another one.

Instead of immediately get to work on a something like a complete quest system, you can make a simple prototype of what you want do, or just one part at a time of it. It will be easier for you, and if you need help, it will also be easier for people to understand and to help with what you're asking than if you dump a complicated code mess. And then, once you understand all the parts, you can add all the rest.

Think about taking advantage of SugarCube more than you do. In this case, instead of making your own modal, you can use SugarCube's Dialog.

Just a friendly suggestion.
 
Last edited:

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,098
3,350
Thrashing around trying to do some cool stuff while not really knowing the tools is almost a rite-of-passage for beginner developers. Along the way you get exposed to lots of new concepts that are universal programming paradigms, and also the messy details of the specific programming language/runtime environment you are working within. To a beginner, it is not obvious where the difference between these two groups lies. So while Alcahest's advice is very good in general, it's understandable if you feel overwhelmed.

Asking questions about the "right" way to do it is absolutely a good way to go ahead, rather than throwing together some complex rube goldberg machine that works but you don't really know why.

Keep it simple keep it simple keep it simple.
 

Frogface29

Newbie
Feb 22, 2022
43
41
Thanks for all the tips! I have looked at all the links provided and read up on a bit of javascript. Thanks for the links they helped a lot. You guys are right, my code was extremely overly complicated!
I was able to get my code to work the way I wanted it to. It now successfully displays the quests (based on questID) that I dynamically created using a widget.

I also removed lots of nonsense code. Now it is significantly shorter...


Code:
// Function to hide the modal
function hideModal() {
    var modal = document.getElementById('confirmationModal');
    if (modal) {
        modal.style.display = 'none';
        document.body.removeChild(modal);
    }
    $(document).trigger(":liveupdate");
    UIBar.unstow();
}

// Combined Function to Create and Show Quest Modal
setup.showQuest = function(questID) {

    var modal = document.createElement('div');
    modal.classList.add('modal');
    modal.id = 'confirmationModal';

    var modalContent = document.createElement('div');
    modalContent.classList.add('modal-content');

    // Adding spans
    for (let i = 0; i < 4; i++) {
        var span = document.createElement('span');
        modalContent.appendChild(span);
    }

    // Content Section
    var contentSection = document.createElement('div');
    contentSection.classList.add('content');

    // Retrieve quest information based on questID
    let sv = State.variables;
    let quest = sv[sv.pendingQuests[sv.pendingQuests.indexOf(questID)]];

    contentSection.innerHTML = `
        <h2>${quest.name}</h2>
        ${quest.description}
        <div class="section-label">Rewards: ${quest.xpReward}xp, $${quest.moneyReward} Deadline: NEEDS TO BE ADDED days</div>
        <div class="section-label">Consequences: ${quest.consequences}</div>
        <div class="section-label">Conditions for failure: ${quest.conditions}</div>
        <div class="action-buttons">
            <button id="confirmButton">Accept Quest</button>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <button id="cancelButton">Refuse</button>
        </div>`;

    modalContent.appendChild(contentSection);
    modal.appendChild(modalContent);
    document.body.appendChild(modal);

    modal.style.display = 'flex';
    UIBar.stow();

    // Attach event listeners to confirmButton and cancelButton from inner HTML
    document.getElementById('confirmButton').addEventListener('click', function() {
        hideModal();
        State.variables.q1Accepted = true;
    });

    document.getElementById('cancelButton').addEventListener('click', function() {
        hideModal();
        State.variables.q1Accepted = false;
    });
};
Let me know if this code now makes more sense.

I will work on easier projects now, get the basics down, and come back to this later when I am better at JS :)
 
  • Red Heart
Reactions: osanaiko

Satori6

Member
Game Developer
Aug 29, 2023
330
600
I will work on easier projects now, get the basics down, and come back to this later when I am better at JS :)
That's a good call - it's exactly what I'm doing. I was getting stuck all the time because I didn't know JS, so I started a new project with the sole purpose of learning HTML/JS.

I'm not there yet, but at this point I'm only stopping to read documentation or figure out how to do something once or twice a week, instead of spending 90% of the time trying to get something to work only for it to break the day after like before.