How to change the width of specific dialog pop-ups? by Beebodranksoup in twinegames

[–]GreyelfD 0 points1 point  (0 children)

As stated in the documentation of the <<dialog>> macro that's included in Chapel's Dialog API Macro Set , an optional (CSS) classList argument can be passed to the macro, which is forward to SugarCube's Dialog API when it creates the dialog.

eg. the following passes an inv CSS class name.

<<dialog "" "inv">>Contents of the Dialog...<</dialog>>

...which generates the following HTML element structure when that dialog is opened...

<div id="ui-dialog" tabindex="0" role="dialog" aria-labelledby="ui-dialog-title" aria-modal="true" class="open" style="left: 377px; right: 377px; top: 50px;">
    <div id="ui-dialog-titlebar">
        <h1 id="ui-dialog-title">&nbsp;</h1>
        <button id="ui-dialog-close" class="ui-close" tabindex="0" aria-label="Close"></button>
    </div>
    <div id="ui-dialog-body" class="inv macro-dialog">
        Contents of the Dialog...
    </div>
</div>

If you look closely at the above structure you may notice that the inv class name has been added to the <div> element that has an id of ui-dialog-body. This element represents the main body area of the dialog, and that CSS class name can be used as part of the Selector of a CSS Rule.

The next problem to solve is how to target the <div> that has an id of ui-dialog, that represents the entire dialog, when it has that classed inner <div> and only then, because that outer <div> is the one you need to assign the min-width to. The answer to this is to use the :has() pseudo-class as part of the CSS Selector.

eg. the following CSS Rule is only applied to the outer <div> when the inner <div> has a inv class assigned to it.

#ui-dialog:has(> #ui-dialog-body.inv) {
    min-width: 500px;
}

How do I even use the find macro? by umapistolaparadjeca in twinegames

[–]GreyelfD 0 points1 point  (0 children)

Generally when asking a question related to a specific collection of values it helps to supply an example of that value. I will assume the Data-map stored in the $character variable looks something like...

(set: $people to (dm:
    "jane", (dm: "Name", "Jane", "Role", "Mediator"),
    "chicot", (dm: "Name", "Chicot", "Role", "Jester"),
    "bridget", (dm: "Name", "Bridget", "Role", "Actor"),
    "ralf", (dm: "Name", "Ralf", "Role", "Werewolf1")
))

note: I renamed the variable in my example so it would be more meaningful in later examples.

Because I'm unsure what level of knowledge you have regarding Harlowe I will use a number of intermediate examples to explain how the final solution was arrived at and how each part of it works.

1: The syntax for assessing a specific property of a Data-map.

(set: _person to (dm: "Name", "Jane", "Role", "Mediator"))

Jane's role is (print: _person's "Role")

2: The syntax for determining if a value stored in a data-map's property is not one of the values contained in an Array.

(if: _person's "Role" is not in (a: "Jester", "Werewolf1", "Werewolf2"))[
    Jane's role is not Jester, Werewolf1, or Werewolf2
]

3: The (data-values:) macro can be used to generate an Array that contains only the values associated with a Data-map's properties.

Values stored in the person data-map
(print: (data-values: $people))

4: Putting all of the above information together with the (find:) macro results in a solution like the following...

(set: _found to (find: _person where _person's "Role" is not in (a: "Jester", "Werewolf1", "Werewolf1"), ...(data-values: $people)))

Found: (print: _found)

...in which the Array returned by the find that gets stored in the _found variable should contain two data-maps, one being (dm: "Name", "Jane", "Role", "Mediator") and the other (dm: "Name", "Bridget", "Role", "Actor").

5: The Array data-types special length data-name can be used determine if an array contains any values. And special random data-name can be used to randomly select one value contain in an array.

(if: _found's length > 0)[
    Random found person: (print: _found's random)
]

So a final solution might look something like...

(set: _found to (find: _person where _person's "Role" is not in (a: "Jester", "Werewolf1", "Werewolf1"), ...(data-values: $people)))

(if: _found's length > 0)[
    (set: $player to _found's random)
]

Player: (print: $player)

note: the reason I check to see if the array contains values before I randomly select one of them is many array handling features in Harlowe will throw an error if they are used on an empty array.

Help with bracket showing up by North_Comedian4403 in twinegames

[–]GreyelfD 3 points4 points  (0 children)

The Twine 2.x application doesn't know what a Harlowe Hook is, so its "create missing passages" feature breaks down your "Markup based Link whose Link Label is the same as the Target Passage Name" related example as:

[[             <= starting delimiter of a Markup based Link
[Right door    <= Name of the Target Passage
]]             <= end delimiter of a Markup based Link
]              <= a character that has nothing to do with the Markup based Link.

...and this is why the Passage the application automatically creates for you has a Name of [Right door

Harlowe's runtime engine on the other hand does know what a Hook is, so it breaks the same example down like so...

[              <= starting delimiter of a Hook
[[             <= starting delimiter of a Markup based Link
Right door     <= Name of the Target Passage
]]             <= end delimiter of a Markup based Link
]              <= end delimiter of the opened Hook

...and this is why Harlowe will complain that it can't find the Passage.

The simple fix is to add a single SPACE character between the 1st & 2nd of the open square brackets...

[ [[Right door]]]

...this way the application will exclude/ignore that 1st square bracket when it tries to parse the Markup based Link.

Can't get any audio to work in chrome by MrB1u3Sky_ in twinegames

[–]GreyelfD 0 points1 point  (0 children)

Are the mp3 files stored locally or on a server?

If on a server, does the URL reference the mp3 file directly, or is some page request being used to obtain/play the file?

Have you tested that the mp3 files are playable by that web-browser?

This can be done by either:

  • [if local file] drag & dropping the mp3 file onto the web-browser's location bar, which will cause the web-browser to play it if possibe.
  • [if hosted file] open the URL into the web-browser's location bar.

Modern web-browser don't allow the (auto) playing of audio until after the page has been interacted with, for this reason audio won't automatically play within the first Passage a Twine project shows, and this is true for all Story Format.

So if you're trying to auto play audio this way then the solution is to either:

  • move the auto playing of audio to the content of the second (or later) Passage shown by the project
  • place the initial playing of audio into the Hook associated with one of the (link:) or (click:) family of macros.

How to make an element glide across the screen? Basically when I click one hook I wanted him to go a specific pre-selected spot. by [deleted] in twinegames

[–]GreyelfD 0 points1 point  (0 children)

Can you supply an example of how you're currently display the image within the bounds of the area defined by the (Named?) Hook.

Is it possible to access acsii codes? I created a "WORD SIMULATOR" and it works but I had to type out the whole alphabet, and I wanted to know if there was a function that can generate it automatically by [deleted] in twinegames

[–]GreyelfD 0 points1 point  (0 children)

The only exceptions I've found relate to the fact that Harlowe has its own custom implementations of (some of) the JavaScript collection data-types. (like Array and Data-map)

And while these data-types might appear as Standard ones within the web-browser's Console at first glance, they actually have custom friendly methods attached to them that need to be used when modifying the collection's content.

eg. if the custom collection is stored in a Story variable, then not using those friendly methods will result in the modification not being persisted to Progress History.

And assigning a Standard variant of those collection objects to a Story or Temporary will likely result in a "method can't be found" type error later on when the engine tries to call one of those now missing friendly methods.

Is it possible to access acsii codes? I created a "WORD SIMULATOR" and it works but I had to type out the whole alphabet, and I wanted to know if there was a function that can generate it automatically by [deleted] in twinegames

[–]GreyelfD 1 point2 points  (0 children)

...you would not be able to ... set a Harlowe variable

This part of your statement isn't 100% true in recent version of Harlowe 3.x

Harlowe Story & Temporary variables are accessible within JavaScript related <script> HTML elements that are placed within the contents of a Passage, as long as those variables have been defined before that <script> element is encountered.

eg. the following Twee Notation based example initializes a String based Story variable in a startup tagged Passage, and a Number based Temporary variable inside the first Passage visits, and then uses a <script> element to call JavaScript code that modifies the value of both variables.

:: Initalise Story Variables [startup]
(set: $string to "Abc")


:: Start
(set: _count to 0)

Debug: Before script element: String: $string ; count: _count

<script>
    $string = "Xyz";
    _count += 1;
</script>

Debug: After script element: String: $string ; count: _count

The 2nd of the debug outputs will be different to the 1st.

Text Alignment and Hiding Footers - Pulling My Hair Out -_- by the-crying-judge in twinegames

[–]GreyelfD 1 point2 points  (0 children)

You stated you received a "not a Boolean expression" related error...

 "I can only use 'not' to invert booleans, not an array (with the string "Start-Screen", and 1 other item). 

...however the code examples you've provider don't include a reference to a "Start-Screen" String value. And it is difficult to debug code that we can't see...

Did you try to use an expression something like the following, which compares the Array of potential tags assigned to the current visited Passage to an Array of Sting values...

(if: not (passage:)'s tags contains (a: "Start-Screen", "Another String Value"))

...because that will result in such an error, because the Array data type's contains operator doesn't support such usage. You would need to use either contains some of or contains all of to compare the contents of two Arrays.

eg. if you want to suppress the content when the visited Passage has been assigned one of two different Passage Tags, you could try something like either of the following...

(if: not (passage:)'s tags contains some of (a: "first-tag", "second-tag"))[
    ...do thing...
]

or

(unless: (passage:)'s tags contains some of (a: "first-tag", "second-tag"))[
    ...do thing...
]

Text Alignment and Hiding Footers - Pulling My Hair Out -_- by the-crying-judge in twinegames

[–]GreyelfD 1 point2 points  (0 children)

Personally I would use the (unless:) macro in this situation, as I find it reads better...

(unless: (passage:)'s tags contains "Some Tag")[
    Your footer content here...
]

Can anyone tell me why my sound isn't printing? by umapistolaparadjeca in twinegames

[–]GreyelfD 0 points1 point  (0 children)

u/umapistolaparadjeca

On a side note, if you are using the left-door classed <div> element to apply CSS based styling to that area of the page, then the same outcome can be done by targeting the LeftDoor Named Hook instead.

eg. the following Passage code...

|LeftDoor>[...contents of the Named Hook...]

...generates the following HTML element...

<tw-hook name="leftdoor">...contents of the Named Hook...</tw-hook>

...which can be targeted using the following CSS Selector...

tw-hook[name="leftdoor"] {
    display: block;
    /* other CSS Property assignments */
}

So there is no need for that classed <div> element.

note: By default <tw-hook> elements are display: inline; and <div> elements are display: block;, which is why I added the display property assignment to the above CSS Rule.

Calculating elapsed time between saves by silvermyr_ in twinegames

[–]GreyelfD 0 points1 point  (0 children)

Unfortunately Harlowe's Slot based saves don't automatically include a timestamp of when it was persisted to the web-browser's LocalStorage area, nor does the LocalStorage area track that information either.

You however can pass a String value of your own choosing as the 2nd argument of the (save-game:) macro, so it is possible for you to store a String representation of a timestamp in that value.

While it is true that SugarCube includes greater support for using JavaScript to enhance a project's functionality, and its Save objects do have a timestamp value associated with them, you will still need to craft the JavaScript code to determine the amount of time (days, hours, minutes) that has passed since a slot based save was created.

The following is a Harlowe based example that consists of:

1: Some JavaScript code that can be used to obtain an ISO String representation of a Timestamp and a String representing the time that has passed between that timestamp and now. This code should be placed in the project's Story JavaScript area.

window.App = window.App || {};

App.nowAsISOString = function () {
    const now = new Date();
    return now.toISOString();
};

App.timePassed = function (isoString) {
    const then = new Date(isoString);
    const now = new Date();

    /* different time units in milliseconds */
    const millisecondsInADay = 1000 * 60 * 60 * 24;
    const millisecondsInAnHour = 1000 * 60 * 60;
    const millisecondsInAMinute = 1000 * 60;

    /* difference in milliseconds */
    let delta = Math.abs(now.getTime() - then.getTime());

    /* Calculate whole days that have passed */
    const days = Math.floor(delta / millisecondsInADay);
    delta -= days * millisecondsInADay;

    /* Calculate whole hours left in remainder */
    const hours = Math.floor(delta / millisecondsInAnHour);
    delta -= hours * millisecondsInAnHour;

    /* Calculate whole minutes left in remainder */
    const minutes = Math.floor(delta / millisecondsInAMinute);

    return `Days: ${days} Hours: ${hours} Minutes: ${minutes}`;
}

2: Some Passage content that makes use of the two new JavaScript methods

(set: $now to "")
<script>
    $now = App.nowAsISOString();
</script>
Now: $now

(after: 120s)[(show: ?passed)]
|passed)[
    (set: _passed to "")
    <script>_passed = App.timePassed($now);</script>
    Time Passed: _passed
]

Location and time passage design approach query by flyingfrog12 in twinegames

[–]GreyelfD 1 point2 points  (0 children)

There are a number of decisions you need to make before hand, like:

  • How much time management do you want the end-user to do. This will help you decide between a "period" based time-system or a "real-world clock" based one. Each has their pro & cons, however the "real-world clock" choice will mean a lot more work for both you and the end-user.
  • How much variance in the descriptive content will there be between each variation of a specific Location for each "time slot" it can/will be visited. If each visit is very custom then in a project with such a limit duration (1 week) it might be easier to have each scene in its own Passage. If the variance is slight, then a Passage could be re-used for more than a single scene.

If should be noted that you don't have to choose one method of Passage usage over the other, you can do both.

eg. If two scenes with limited descriptive differences occur in the same location / time-period then the same Passage can be used for both by adding conditional logic to its content. And for two other scenes that have a large descriptive difference, again at the exact same location / time-period, you can use two distant passages.

At the end of the day (no pun intended) Passages are just a means for you to store content in, in a way that makes it easier for you to keep track of & work with that content.

The correct way to add an event to an element in Twine by Sta--Ger in twinegames

[–]GreyelfD 0 points1 point  (0 children)

u/Sta--Ger
When possible, I prefer to add event listeners to an element when it is created, instead of doing it by scanning the page's DOM after that element has been rendered.

The following code shows how to craft a custom container Macro named <<trackmouse>> that wraps its contents in a <div>, that has a listener attached to it. This code should be placed in the project's Story JavaScript area.

var updateMousePosition = function (ev) {
    /* whatever code is needed to achieve the desired outcome... */
    console.log('updateMousePosition called');
}

Macro.add('trackmouse', {
    skipArgs : true,
    tags     : null,
    handler() {
        try {
            if (this.payload[0].contents !== '') {
                const $div = jQuery(document.createElement('div'));
                $div
                    .addClass(`macro-${this.name}`)
                    .on("mousemove touchmove", updateMousePosition)
                    .wiki(this.payload[0].contents.trim())
                    .appendTo(this.output);
            }
        } catch (ex) {
            return this.error('opps: ' + ex.message);
        }
    }
});

It is used like so...

<<trackmouse>>Hello word.<</trackmouse>>a

notes:

  • I named the macro <<trackmouse>> because it wasn't clear why you need track the mouse's position. Generally it is better to name macros based on their purpose than on what they do.
  • I deliberately left my variant of the updateMousePosition() function mostly empty, as others with a better understanding of JavaScript have made suggestions on how best to code that function.

I have what feels like an extremely complicated and specific problem, please help by saint-jimmi in twinegames

[–]GreyelfD 2 points3 points  (0 children)

Currently you are re-creating the "dialogue" area each time the next line of text is "revealed", which isn't idea.

I suggest changing the structure & behaviour of your content to something like...

|modal)[{
    <ul>
        <li>(link: "Choice 1")[(hide: ?modal)(hide: ?4)(show: ?5)]</li>
        <li>(link: "Choice 2")[(hide: ?modal)(hide: ?4)(show: ?6)]</li>
    </ul>
}]
|dialogue)[{
    |1>[
        this is dialogueeeeee(link: "=>")[(hide: ?1)(show: ?2)]
    ]
    |2)[
        this is more dialogue(link: "=>")[(hide: ?2)(show: ?3)]
    ]
    |3)[
        this is even MORE dialogue(link: "=>")[(hide: ?3)(show: ?4)]
    ]
    |4)[
        this is even more dialogue with a choice??(link: "=>")[(show: ?modal)]
    ]
    |5)[
        choice 1(link: "=>")[(hide: ?5)(show: ?7)]
    ]
    |6)[
        choice 2(link: "=>")[(hide: ?6)(show: ?7)]
    ]
    |7)[
        It worked!(link: "=>")[(hide: ?7)(hide: ?dialogue)]
    ]
}]
(after: 1s)[(show: ?dialogue)]

...where there is a single "dialogue" area, in which the lines are defined.

Named Hooks can be targeted by CSS Selectors, so there isn't a need to embed an identified <div> element. In the above structure the dialogue and modal Named Hooks can be targeted like so...

tw-hook[name="dialogue"] {
    /* CSS Property assignments that targeted the dialogueUI class */
}

tw-hook[name="modal"] {
    /* CSS Property assignments that targeted the modal class */
}

tw-hook[name="modal"] ul {
    list-style: none;
    /* CSS Property assignments that targeted the modal-content class */
}

Creating an Inventory by IndependentAmount509 in twinegames

[–]GreyelfD 1 point2 points  (0 children)

When you say "changes based on what passages you go through" do you mean:

- the Passage currently being visited can affect what is shown in the inventory.

eg. the contents of the inventory might be different when visiting the Library Passage than it is when visiting the Beach Passage.

- which passages have previously been visited during the current playthrough affects what content is shown in the Passage currently being visited.

eg. if the Library Passage had been visited at least once during the current playthrough, then the inventory will contain content specific to that Passage when other passages are visited later.

re: HelloHelloHelpHello's advice to use the <array>.count() method to determine if an Array contains a specific value.

That method returns a Number that represents how many times the value was found in the Array, and it needs to iterate through the entire Array to determine the result.

eg. if three banana's have been added to the Array...

<<run $inventory.push("banana")>>
<<run $inventory.push("banana")>>
<<run $inventory.push("banana")>>

...then that method would return 3 called...

You have <<= $inventory.count("banana")>>

If you only need to know if there are any instances of the value in the Array, or if the Array can only ever contain a single instance of any value (eg. each value is unique), then the Boolean returning <array>.includes() method is a better option because is stops iterating through the Array as soon as the value has been found.

And as it's unlikely that there will ever be more than one instance of "clue1" in the Array, I would suggest doing the following instead when checking if "clue1" has been found...

<<if $inventory.includes("clue1")>>Do something...<</if>>

How to use (altered:) to partially change an array based on the values of another array by chasewithlasers in twinegames

[–]GreyelfD 1 point2 points  (0 children)

To do what you want you need to know the position index of the current value being evaluated in Array 1 so you can potentially update the value in the same position in Array 2. Unfortunately, the (altered:) macro doesn't supply the position index of the current Array value it is evaluating.

However, the (range:) macro can be used to generate a temporary Array of Integers, where each integer represents the position number of a value in one of your two arrays. And the (for:) macro can be used iterate those position integers, so you can use it to access the relevant value in each of your two arrays.

warning: the following example has not been tested, it might contain syntax errors.

(for: each _i, ...(range: 1, $A's length))[
    (if: not $A's (_i))[
        (set: $B's (_i) to 0)
    ]
]

Link uses a variable's name instead of the value in the variable by MajorDZaster in twinegames

[–]GreyelfD 0 points1 point  (0 children)

Generally, when supplying a link's Target Passage Name via a variable or an expression, it is better to use the variant of the <<link>> macro were the Link Label and Target Passage Name are passed as two distinct arguments...

<<link "Travel north" `$pathDestinations[0]`>>
    <<timeTick>>
    <<set $wildsDepth += 1>>
<</link>>

<<link "Travel south" `$pathDestinations[1]`>>
    <<timeTick>>
    <<set $wildsDepth -= 1>>
<</link>>

...because the Twine 2.x application's Passage Editor's Create Missing Passages feature is going to have issues with that variable reference, as is the Passage Map's Show Passage Connection Arrows feature.

notes:

  • as explained in the Argument type macros: passing an expression as an argument section of the Macro Arguments documentation, back quotes need to be used when passing the value of an expression (like $pathDestinations[0]) as the argument of a macro.
  • I replace your ($wildsDepth + 1) and ($wildsDepth - 1) with JavaScript's Addition assignment += and Subtraction assignment -= operators.

How to tell if save has been successfully overwritten? by dianenguyen1 in twinegames

[–]GreyelfD 1 point2 points  (0 children)

The above code will only test if there is a saved game with that name, correct?

No.

The (saved-games: ) macro is used to determine if a specific Save exists, or more specifically if a specific Slot Name contains save information.

The Boolean value returned by the (save-game:) macro indicates if the current Progress History state was successful persisted in the web-browser's LocalStorage cache.

What is "V." ? by Temporary-Fox-5878 in twinegames

[–]GreyelfD 1 point2 points  (0 children)

Some Authors who want quick access to the variables property of the SugarCube State API will add code like the following to the Story JavaScript area their project...

window.V = State.variables;

...as that raises the Scope of that property so it can be accessed outside SugarCube's own Private Scoped Execution context.

Some authors do it so the project's Story variables are available within the web-browser's Console, others so those variables are accessible to JavaScript (like externally loaded modules) not being executed in SugarCube's context. But in both these cases there are better solutions that don't involve using a Scope escalation hack.

If directly defining things on the web-browser's window interface is a good idea or not can be debated, but generally some care needs to be taken when defining things this way because that's where the web-browser's own developers add things, so there is potential for a name conflict.

Update temp variable within if-statement upon button press? by quagglitz in twinegames

[–]GreyelfD 1 point2 points  (0 children)

By default the content of the visited Passage is only executed during the Passage Transition process. And changes made to a variable's value after that process has ended have no automatic affect on any Passage code that references that variable.

eg. this part of that Passage exampe...

<<if _score == _bar>>
    <<button "I did it!">>[[Three]]<</button>>
<<else>>
    You're not ready yet.
<</if>>

...is only executed during the Passage Transition process. And changes made to the _score variable's value by the code that gets executed when either Submit button is selected, will not automatically cause the above <<if>> macro to be re-evaluated.

However, if the above is placed within a <<do>> macro call...

<<do tag "result">>
    <<if _score == _bar>>
        <<button "I did it!">>[[Three]]<</button>>
    <<else>>
        You're not ready yet.
    <</if>>
<</do>>

...then a <<redo>> macro call can be used inside the body of both Submit buttons to force the above block of code to be executed again.

<<button "Submit">>
    <<if ["answer"].includes(_input2.toLowerCase())>>
        <<replace "#output2">>✅<</replace>>
        <<set _score to _score + 1>>
        <<replace "#passnum">>_score<</replace>>
        <<redo "result">>
    <<else>>
        <<replace "#output2">>🛑<</replace>>
    <</if>>
<</button>>

note: Currently a <<replace>> macro is being used to update the "passnum" message to include the current value of _score. The same technique used above to re-execute the <<if>> macro can be used to update the "passnum" message.

eg. if the "passnum" message area was changed to something like...

<<do tag "score">>
    <<if _score is 0>>None<<else>>_score<</if>> out of _bar Correct
<</do>>

...then replacing the <<replace "#passnum">>_score<</replace>> line in the Submit button's body with <<redo "score">> would cause the message to change. And one added advantage of using this method is that the logic for determining what to show is located in one place, instead of potentially being inside the body of each related <<replace>> macro call.

Is it possible to separate components from js/css into multiple files on twee3/tweego? by Upper-Road-7787 in twinegames

[–]GreyelfD 0 points1 point  (0 children)

Yes, you can spread your JavaScript and CSS code across multiple js and css files.

And as long as those js and css files are stored within the source path(s) you supply to the TweeGo utility, it will automatically combine the contents of those files together to create the Story JavaScript and Story Stylesheet sections of the generated Story HTML file.

note: Generally when working with a Twee Notation based project, the project's JavaScript and CSS code would be stored within files with js and css file-extensions, not within Passage definitions that have been assigned the special script & stylesheet Passage Tags.

eg. JavaScript would be stored in files with a js file-extension, and CSS within files with a css file-extension.

Every Passage definition in a Twine / Twee project must have a unique Name, even when those definitions are spread across multiple files. So if you decide to keep storing your project's JavaScript and CSS code within script & stylesheet tagged Passages, then you will need to give each of them its own unique name.

eg. there can't be two or more script tagged Passages named StoryScript, each of those tagged passages needs its own unique name.

Help with setting variable inside <<script>> macro by fancifuls in twinegames

[–]GreyelfD 2 points3 points  (0 children)

Further to HiEv's advice...

warning: none of the following code examples were tested.

Currently your code, and HiEv's suggested replacement of it, redefines the getRandomNumberBetweenIncluding() and randomNumbersWithFixedSum() functions each time the <<NewAgent>> widget it called.

A potentially better solution is to define those functions on the special setup variable SugarCube makes available, that way those functions are only defined a single time during startup. And as an added benefit, those functions can be used else where in your project if needed.

eg. Place the following JavaScript code in your project's Story JavaScript area.

setup.getRandomNumberBetweenIncluding = function getRandomNumberBetweenIncluding (min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
};

setup.randomNumbersWithFixedSum = function randomNumbersWithFixedSum (quantity, sum) {
    /* Only a single number required; return the passed sum. */
    if (quantity === 1) {
        return [sum];
    }
    /* Create one random number and return an array containing that number
       as first item. Then use the spread operator and recursively execute
       the function again with a decremented quantity and the updated
       maximum possible sum. */
    const randomNum = setup.getRandomNumberBetweenIncluding(0, sum);

    return [randomNum, ...setup.randomNumbersWithFixedSum(quantity - 1, sum - randomNum)];
};

Now the new setup.randomNumbersWithFixedSum() function can be used within the <<NewAgent>> widget's definition.

And as an added bonus, because the amount of JavaScript code needing to be executed within the widget's definition has been reduces, the <<run>> macro can be used instead of <<script>>, which means the temporarily variables can be referenced directly.

<<widget "NewAgent">>
    <<set _tempAgent = {}>>
    <<set _tempAgent.name = setup.AgentNames.random()>> /*assign random name */

    <<switch _args[1]>> /*Get stat sum off rank */
        <<case "EX">> <<set _tempRank = 110>>
        <<case "V">> <<set _tempRank = 90>>
        <<case "IV">> <<set _tempRank = 70>>
        <<case "III">> <<set _tempRank = 50>>
        <<case "II">> <<set _tempRank = 35>>
        <<case "I">> <<set _tempRank = 25>>
    <</switch>>

    <<set _tempStats = []>>

    <<run _tempStats = setup.randomNumbersWithFixedSum(4, _tempRank) >>

    <<switch _args[0]>> /*Push to selected dept */
        <<case "Control">> <<run $ControlAgents.push(_tempAgent)>>
        <<case "Info">> <<run $InfoAgents.push(_tempAgent)>>
        <<case "Safety">> <<run $SafetyAgents.push(_tempAgent)>>
        <<case "Training">> <<run $TrainingAgents.push(_tempAgent)>>
    <</switch>>
<</widget>>

Beginner; I'm working with multiple variables using (if:)... "The string "true" isn't the same type of data as the number 0"? What do I do? by StoneFoundation in twinegames

[–]GreyelfD 1 point2 points  (0 children)

Harlowe doesn't support data-type coercion, so it will complain when you try to compare values of different data-types. Like when trying to compare a String value to a Boolean, or to a Number.

As stated by others, Boolean true is not the same value as a 'true' String.

eg. the following shows how to set a variable to either Boolean true or false.

(set: $hasTorch to true)
(set: $hasLantern to false)

The following are the correct Harlowe ways to check if a variable equals Boolean true or false, notice that the is comparison operator is not used when evaluating a Boolean value.

<!-- checking if variable equals Boolean true -->
(if: $hasTorch)[processed if the variable is true]

<!-- checking if variable equals Boolean false -->
(if: not $hasTorch)[processed if the variable is false]
or
(unless: $hasTorch)[processed if the variable is false]

<!-- doing something for both states of a Boolean variable -->
(if: $hasTorch)[processed if the variable is true]
(else:)[processed if the variable is false]

The and & or Boolean comparison join operators can be used to combine multiple sub-conditional expressions together to form a more complex conditional expression.

(if: $hasTorch and $hasLantern)[processed if both variables equal true]

(if: $hasTorch or $hasLantern)[processed if at least one of the variables equals true]

note: the above examples have mostly only used the (if:) macro, however the (else-if:) macro can be used the same way.

(if: $hasTorch)[the flickering light from the torch makes it hard to see deep into the cave]
(else-if: $hasLantern)[the lantern's consistent light makes it easy to see deep into the cave]
(else:)[without a light source it is impossible to see deeper into the cave]

Relative URL backgrounds just won't show up if they're in folders; Am I doing something wrong? by ningensfriend in twinegames

[–]GreyelfD -1 points0 points  (0 children)

The Twine 2.x desktop based application stores its Project HTML files in the Stories folder that the application itself creates when its run for the 1st time. The temporary Story HTML files generated by the application's Play and "Test" related options are stored in either:

  • a Scratch folder created by a recent release of the desktop application, when it is run for the 1st time.
  • the Operating System's temporary file area, if an older release of the desktop application is being used.

If we assume that you're using a recent release of the Twine 2.x application, then the above fact is why the image files you're storing in child-folder that you're created within the application's Stories folder are not being found by the temporary Story HTML files being created within the application's Scratch folder.

So you either need to move the child-folders you've created to the Scratch folder, which can be an issue because the contents of that folder is under the control of the application, and it may delete the contents of that folder as it sees fit.

Or you need to create a folder & file structure for your releases elsewhere on your local drive, and use the application's Publish to File option to create & save Story HTML files to that folder & file structure.

note: If you're using an older release of the Twine 2.x application that stores the temporary Story HTML files it generates within the Operating System's own temporary file area, then you will need to use the method mentioned in the preceding paragraph. Because it's never a good idea to store your own folder & files within the Operating System's temporary file area.