How to make a page have a chance to appear after doing specific actions

Trying to have an enemy appear after doing any action in a specific area (Except any actions during battle). I’m not exactly sure how I should do this with a $ variable…

For more clarification I guess what I want to know is how to set up a random passage opening every single time they do an action that could warrant it, so any actions (anything that goes to a different page) that I choose, that way any combat actions don’t count towards it and force a different encounter mid battle.

Using Twine 2.12 and Sugarcube 2.37.3 if that’s useful

This sounds like a job for Config.navigation.override, which is a way to hijack the passage the player would normally go to and replace it with another one:

Config.navigation.override = function (dest) {
	if (tags(dest).includes("area") && random(5) == 0) {
		return "enemypassage";
	}
};

This way if the destination passage is tagged area, there is a chance of 1 in 5 to go to the passage enemypassage instead. This way you can tag all the passages where you want the enemy to appear, and the player will only encounter the enemy when going to those passages.

There are different ways to do this depending on how exactly your story is set up (for example, you could have certain links set a variable and then have the override check for that variable. Without knowing exactly how your story works, I can’t say which one is the best.

So, in general, the way to have specific actions do something different, is to use the <<link>> macro without a destination, and then use <<goto>> to direct where you want the player to go.

So, if we imagine a variable $didTheThing has been set to true and you want to redirect the player …

<<link "Go into the Kitchen">>
   <<if $didTheThing>>
       <<goto "portal to the other world">>
   <<else>>
       <<goto "kitchen">>
   <</if>>
<</link>>

If you want different text for your link, you can reverse this and have two links instead:

<<if $didTheThing>>
   <<link [[Go through the portal->portal to the other world]]>><</link>>
<<else>>
   <<link [[Go into the Kitchen->kitchen]]>><</link>>
<</if>>

If you wanted every link to have the chance to go somewhere else, you could use the Config.navigation.override function (in your Javascript section) to do this:

Config.navigation.override = function(destination) {
   if (State.variables.didTheThing) { 
       return "portal to the other world";
   } else {
       return destination;
   }
};

However, there isn’t a great way to have all links except a few links have your redirect. You can use a version of the navigation override and check that the current passage isn’t your combat passage, perhaps, but then the first link leading out of the combat passage will need to do the <<if>> ... <<goto>> trick.

Config.navigation.override = function(destination) {
   if (State.variables.didTheThing && passage() != "combat passage") { 
       return "portal to the other world";
   } else {
       return destination;
   }
};

This is exactly what I was looking for! I also have another question… would it be possible to set it up so that instead of “enemypassage” being the destination, it would be the tag “enemypassage” which then would choose between multiple different passages with the specific tag.

Essentially is it possible to set it up so that the destination is randomly chosen between passages with the “enemypassage” tag

Sure, you can use the Story.filter() method to do something like this:

Config.navigation.override = function (dest) {
	if (tags(dest).includes("area")) {
		passages = Story.filter(function (p) {
			return p.tags.includes("enemypassage");
		});
		return passages.random().name;
	}
};

Are there any variables I need to change depending on what I have written down other than the tags… It’s not exactly working for me

No, only the tags need to be changed. I tested the code, so it should work. There are two things I can think of that might be going wrong.

One, the tags are case sensitive, so make sure you’re using the same capitalization everywhere.

Two, the Story.filter() method was added recently, so if you’re using an older version of Sugarcube it won’t work. I’d recommend upgrading, but if for whatever reason you don’t want to you can use Story.lookupWith() instead.

hmm.. both don’t work, and I am using the newest version of Sugarcube I double checked… this is what I have written so far in JavaScript

Config.navigation.override = function (dest) {
	if (tags(dest).includes("BarrenPlanet")) {
		passages = Story.filter(function (p) {
			return p.tags.includes("BarrenPlanetEnemy");
		});
		return passages.random().name;
	}
};

Well, that code works on my end, so there’s likely something in your story that’s messing with it, but without seeing the code I can’t tell you what it is.

It… works now and I have no idea why, it works too well though… (100% of the time) Im assuming for a 1 in 10 chance I do

Config.navigation.override = function (dest) {
	if (tags(dest).includes("BarrenPlanet")) {
		passages = Story.filter(function (p) {
			return p.tags.includes("BarrenPlanetEnemy");
		});
		return passages.random(10) == 0).name;
	}
};

But I’m not 100% sure

If you want a 1 in 10 chance, you want this:

Config.navigation.override = function (dest) {
	if (tags(dest).includes("BarrenPlanet") && random(10) == 0) {
		passages = Story.filter(function (p) {
			return p.tags.includes("BarrenPlanetEnemy");
		});
		return passages.random().name;
	}
};

Perfect! Thank you so much!

If you don’t specify the lower bound of random() it uses 0, so you are generating a 1 in 11 chance, not a one in 10. To get a 1 in 10 chance, you should use either of these:

random(1,10) == 1
random(9) == 0