Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Visual countdown timer (sugarcube2.11.0)

Would it be possible to place a temporary timer in the sidebar, which appears when triggered in a passage, and counts down to zero from a specified time, and then have it disappear when it reaches zero?

Comments

  • edited December 2016
    Yes. How would depend on, exactly, what you want to the timer's appearance to be.

    Here's a simple proof of concept 10 second text countdown timer using the <<repeat>> and <<replace>> macros with a temporary variable.

    First the target element—an easy place to put it would be into your StoryCaption special passage, though you could place it elsewhere:
    <span id="timer-display"></span>
    
    And then the example timer code, triggered via a <<link>>:
    <<link "Countdown!">>
    <<set _timer to 10>>
    <<replace "#timer-display">>_timer<</replace>>
    <<repeat 1s>>
    \<<replace "#timer-display">>
    \<<if _timer is 1>>
    \<<replace "#timer-display">><</replace>><<stop>>
    \<<else>>
    \<<set _timer to _timer - 1>>_timer
    \<</if>>
    \<</replace>>
    \<</repeat>>
    <</link>>
    
  • Thanks very much. I'll try and implement is and get back with my results
  • edited December 2016
    Flippin marvellous!

    Couple of things. I'd like the countdown to start at 5 minutes (300,000ms) but it looks a little weird counting down from 300000.

    Having said that, this is sci-fi so it could actually look quite good.

    Also, how would I target the font's colour in my css?
  • edited December 2016
    Okay, I've figured out how to style the font using the timer-display ID, but there's a problem with this now that I've tested it more thoroughly.

    I've removed the link aspect because I need the timer to trigger automatically, but now the timer resets every time the player switches passages.
  • edited December 2016
    Perhaps I should explain further my reasons for wanting this.

    The player starts trapped in a room in which the air supply is running out. There are no other rooms of course, but the player does move between passages in the course of trying to figure out how to escape.

    For this reason I need the clock not to reset with each new passage, and I also need the timer to kill the player when it gets to zero.

    Apologies for not making this clear from the start.
  • edited December 2016
    Just resurfacing this as I suspect I gave the impression all was well.

    Just to reiterate:
    Jud_Casper wrote: »
    The player starts trapped in a room in which the air supply is running out. There are no other rooms of course, but the player does move between passages in the course of trying to figure out how to escape.

    For this reason I need to prevent the clock resetting with each new passage, and I also need the timer to kill the player when it gets to zero.

    Apologies for not making this clear from the start.

  • I was neither ignoring you nor did I think that all was well. My responses earlier today in another thread were made from my tablet, which I do not write detailed posts on because tablets aren't cut out for it. You have not been abandoned. Breathe.

    Jud_Casper wrote: »
    […] I need the clock not to reset with each new passage […]

    Apologies for not making this clear from the start.
    That knowledge would have been helpful earlier, yes.

    You cannot use either of the timer macros for this—they both terminate on passage navigation as a safety feature—so you'll have to use a JavaScript countdown mechanism. Give me a minute to throw something together.
  • Jud_Casper wrote: »
    A minute. :open_mouth:

    Yep. He's awesome at pulling things like that together :) He's helped me a lot.

  • Okay. Here's a countdown timer script which will continue to run as the player moves from passage to passage.

    It automatically creates its display element, so you're not required to do that manually—if you need to do it manually for some reason, let me know I and can modify the script to support that. The element is currently inserted into the UI bar after the story caption element and its ID for selection and styling is #countdown-timer-display.


    The countdown timer script: (Twine 2: goes in Story JavaScript; Twine 1: goes in a script-tagged passage)
    /* COUNTDOWN TIMER FUNCTION */
    (function () {
    	function formatMinutes(milliseconds) {
    		var base = Number(milliseconds + 'e-3');
    		var mins = Math.trunc(base / 60);
    		var secs = Math.trunc(base % 60);
    
    		if (secs < 10) {
    			secs = '0' + secs;
    		}
    
    		return mins + ':' + secs;
    	}
    
    	$('<span id="countdown-timer-display"></span>').insertAfter('#story-caption');
    
    	var timerId;
    
    	setup.countdown = function (seconds, passage) {
    		if (timerId) {
    			throw new Error('countdown timer already running, terminating');
    		}
    
    		if (!seconds) {
    			throw new Error('invalid countdown duration: ' + seconds);
    		}
    
    		if (!Story.has(passage)) {
    			throw new Error('the passage "' + passage + '" does not exist');
    		}
    
    		var startTime = Date.now();
    		var duration = seconds * 1000;
    		timerId = setInterval(function () {
    			var $display = $('#countdown-timer-display');
    
    			if ($display.length === 0) {
    				return;
    			}
    
    			var msRemaining = duration - (Date.now() - startTime);
    
    			if (msRemaining > 1000) {
    				$display.text(formatMinutes(msRemaining));
    			}
    			else {
    				clearInterval(timerId);
    				timerId = null;
    				$display.empty();
    				new Wikifier(null, Story.get(passage).text);
    			}
    		}, 100);
    	};
    })();
    


    Usage would be something like the following:
    <<if visited() is 1>><<script>>
    setup.countdown(
    	// Countdown duration (in seconds).
    	5 * 60,
    
    	// Name of passage to evaluate when countdown reaches zero.
    	'CountdownDone'
    );
    <</script>><</if>>\
    You should see a countdown timer running right now.
    
    NOTE: The visited() usage is to prevent it being called again if the player goes back to the passage. If the player cannot go back to the passage once they've left it, then you could probably do away with the <<if>>.

    The passage you specify is silently evaluated when the countdown runs out. You can have whatever game logic you need in that passage, like a <<goto "You Died">> or whatever.
  • edited December 2016
    Well, I got pulled away for something else, so it took a little more than a minute.

    An additional note about the countdown timer as currently written. To keep the display code simple, I set it up so that you may only have one running at a time—attempting to start another while a countdown is in progress will throw an error. That can be changed if necessary.

    I didn't get too complicated with it to start because I wanted to get something out for you to try.

    I'm sure some tweaking will be necessary.
  • edited December 2016
    Well here goes. Thank you very much as always.

    I'll be back with the results.

    It works perfectly, except...

    I'm not altogether sure how I send the player to the 'game over' passage at 0. I know you say use a simple <<goto>> macro in the passage where the timer is first triggered, but I don't exactly where in the passage that goes.

    I tried putting the name of the passage where you have 'CountdownDone', but nothing happens at 0.
  • edited December 2016
    Got there!!

    I had to create a passage called (I chose killplayer), then put that name where you had CountdownDone, and then in THAT passage I put the <<goto "dead">>

    Thanks again. I won't be needing additional timers to run, and absolutely no tweaking was required :)
  • edited December 2016
    Ahh, hold on.

    I need a 'win' script, so that if the player escapes before the time runs out, the timer is deactivated.

    I'll try and work something out myself in the meantime. I know it need to go in the corridor outside the room from which you're trying to escape, but not sure what the script will be.

    Mmmm, thought I had it. I added the tag win to the passage where the door is successfully unlocked, and then in the CSS I put:
    .win #countdown-timer-display {display: none;}
    
    This hides the counter, but doesn't stop it killing the player at 0.
  • edited December 2016
    Jud_Casper wrote: »
    I need a 'win' script, so that if the player escapes before the time runs out, the timer is deactivated.
    You could handle that with some logic in the "killplayer" passage, however, it would be better to properly stop the timer. That being the case, I'll rework the script a bit to allow cancelling the countdown.

    Jud_Casper wrote: »
    Mmmm, thought I had it. I added the tag win to the passage where the door is successfully unlocked, and then in the CSS I put:
    .win #countdown-timer-display {display: none;}
    
    This hides the counter, but doesn't stop it killing the player at 0.
    Hiding the countdown display does not affect the countdown timer itself.


    Here's a new version of the script, which allows you to stop the timer at will.

    The countdown timer script: (Twine 2: goes in Story JavaScript; Twine 1: goes in a script-tagged passage)
    /* COUNTDOWN TIMER FUNCTION */
    (function () {
    	function formatMinutes(milliseconds) {
    		var base = Number(milliseconds + 'e-3');
    		var mins = Math.trunc(base / 60);
    		var secs = Math.trunc(base % 60);
    
    		if (secs < 10) {
    			secs = '0' + secs;
    		}
    
    		return mins + ':' + secs;
    	}
    
    	var $display = $('<span id="countdown-timer-display"></span>');
    	var timerId;
    
    	function stopCountdown() {
    		if (timerId) {
    			clearInterval(timerId);
    			timerId = null;
    			$display.empty();
    		}
    	}
    
    	function startCountdown(seconds, passage) {
    		if (timerId) {
    			throw new Error('countdown timer already running, terminating');
    		}
    
    		if (!seconds) {
    			throw new Error('invalid countdown duration: ' + seconds);
    		}
    
    		if (!Story.has(passage)) {
    			throw new Error('the passage "' + passage + '" does not exist');
    		}
    
    		var startTime = Date.now();
    		var duration = seconds * 1000;
    		timerId = setInterval(function () {
    			var msRemaining = duration - (Date.now() - startTime);
    
    			if (msRemaining > 1000) {
    				$display.text(formatMinutes(msRemaining));
    			}
    			else {
    				stopCountdown();
    				new Wikifier(null, Story.get(passage).text);
    			}
    		}, 100);
    	}
    
    	$display.insertAfter('#story-caption');
    
    	setup.startCountdown = startCountdown;
    	setup.stopCountdown = stopCountdown;
    })();
    


    There's a new stopCountdown() function, which would be used like the following:
    <<script>>setup.stopCountdown();<</script>>
    
    That should go at the top of your "win" passage.


    I also changed the name of the countdown() function to startCountdown(), so starting the countdown would be something like the following now:
    <<script>>setup.startCountdown(5 * 60, 'killplayer');<</script>>
    
  • edited December 2016
    Brilliant. It works.

    Thank you. I love how clear your instructions are.
Sign In or Register to comment.