Countdown Timer (Twine 1, Sugarcane)

Hello all!

I’m making a game for schools using Twine at the moment, and at a few points in the game we want the students to stop and talk about what they’ve read so far.
We thought the best way to do this was to add a little countdown clock than can be set for 5 minutes or so, and I found a neat little sample game to help implement one.

http://www.glorioustrainwrecks.com/files/Timer%20Test.tws

(download link)

the problem with this however is that the clock is displayed in the games sidebar, and we do not have one in our game (or a back button, since we don’t want kids gaming the choices).
I’ve tried my best to move the clock to the body of the game instead but all my effors only result in it not displaying at all…

The addon in the sample game reads as follows:

<<silently>>
<<set $TimerAddon = 
function()
{
	var div_timer_container = document.createElement('div');
	div_timer_container.setAttribute('id', 'timer');

	var div_timer_canvas = document.createElement('canvas');
	div_timer_canvas.setAttribute('id', 'timer_canvas');
	div_timer_canvas.setAttribute('width', '150');
	div_timer_canvas.setAttribute('height', '150');
	div_timer_canvas.width = div_timer_canvas.width;
	div_timer_container.appendChild(div_timer_canvas);
	
	var div_timer_text = document.createElement('span');
	div_timer_text.setAttribute('id', 'timer_text');
	div_timer_text.innerHTML = "Hurry Up!";
	div_timer_container.appendChild(div_timer_text);

	var Timer_Active = false;
  var Timer_Paused = false;
	var Timer_Mode = 'none';
	var Timer_Param = "";
	var Timer_Max = 0;
	var Timer_Now = 0;
  
	var div_jonah_floater = document.getElementById('floater');
	if(div_jonah_floater) 		div_jonah_floater.appendChild(div_timer_container);

	var div_sugarcane_menu = document.getElementById('sidebar');
	if(div_sugarcane_menu) div_sugarcane_menu.appendChild(div_timer_container);

	function StartTimer(val)
	{
		Timer_Active = true;
    Timer_Paused = false;
		Timer_Max = val;
		Timer_Now = val;
		div_timer_text.style.display = 'block';
		div_timer_canvas.style.display = 'block';
	}

	function StopTimer()
	{
		Timer_Active = false;
		div_timer_text.style.display = 'none';
		div_timer_canvas.style.display = 'none';
	}

	function PauseTimer()
	{
		Timer_Paused = true;
	}

	function ResumeTimer()
	{
		Timer_Paused = false;
	}

	function SetTimerText(text)
	{
		div_timer_text.innerHTML = text;
	}

	function SetTimerMode(mode)
	{
		if(mode == 'display')
		{
			Timer_Mode = 'display';
		}
		else if(mode == 'restart')
		{
			Timer_Mode = 'restart';
		}
		else Timer_Mode = 'none';
	}

	function SetTimerParam(param)
	{
		Timer_Param = param;
	}

	function OnTimerEnd()
	{
		StopTimer();
		if(Timer_Mode == 'restart')
		{
			state.restart();
			if(Timer_Param) alert(Timer_Param);
			window.location.reload(true);
			
		} 
		else if(Timer_Mode == 'display')
		{
			state.display(Timer_Param[0]);
		}
	}

	function OnTimerTick()
	{
		if(Timer_Active && !Timer_Paused)
		{
			Timer_Now = Timer_Now - 0.1;

			if(Timer_Now <= 0) Timer_Now = 0;
			
			var context = div_timer_canvas.getContext("2d");
			div_timer_canvas.width = div_timer_canvas.width;

			var x = div_timer_canvas.width / 2;
	      var y = div_timer_canvas.height / 2;
	      var radius = 65;
	      var startAngle = 1.5 * Math.PI;
	      
	      var endAngle = (1.5 + (2 / Timer_Max * Timer_Now)) * Math.PI;
	      var counterClockwise = false;

	      context.beginPath();
	      context.arc(x, y, radius, startAngle, endAngle, counterClockwise);
	      context.lineWidth = 15;
        if(div_jonah_floater) context.strokeStyle = "black";
	      if(div_sugarcane_menu)  context.strokeStyle = "#BBBBBB";
	      context.stroke();

			context.font = 'bold 30px sans-serif';
			if(div_jonah_floater) context.fillStyle = "black";
			if(div_sugarcane_menu)  context.fillStyle = "#BBBBBB";
			context.fillText(Timer_Now.toFixed(1), 55, 85);

			if(Timer_Now <= 0) OnTimerEnd();
		}
	}

	setInterval(function(){ OnTimerTick(); }, 100); 

	macros['start_timer'] =
	{
		handler: function(obj, fnc, val)
		{
			StartTimer(val);
		}
	}

	macros['stop_timer'] =
	{
		handler: function(obj, fnc, val)
		{
			StopTimer();
		}
	}

	macros['pause_timer'] =
	{
		handler: function(obj, fnc, val)
		{
			PauseTimer();
		}
	}

	macros['resume_timer'] =
	{
		handler: function(obj, fnc, val)
		{
			ResumeTimer();
		}
	}

	macros['set_timer_text'] =
	{
		handler: function(obj, fnc, val)
		{
			SetTimerText(val);
		}
	}

	macros['set_timer_mode'] =
	{
		handler: function(obj, fnc, val)
		{
			SetTimerMode(val);
		}
	}

	macros['set_timer_param'] =
	{
		handler: function(obj, fnc, val)
		{
			SetTimerParam(val);
		}
	}

}
>>
<<print $TimerAddon()>>
<<endsilently>>

Plenty of this can be removed for our game (everything about Jonah for example) but the change i’m looking for is how to get it out of that nonexistant sidebar.

…can anyone help?

Well, first of all, I’d recommend updating to Twine v2 and the SugarCube v2 story format (which is based on Sugarcane). There have been a number of security updates to browsers which the older versions of the various story formats may not always handle well.

You should find that most Sugarcane code translates fairly well to SugarCube, except things like <<endif>> became <</if>>, <<endsilently>> became <</silently>>, etc… You can use SugarValidator to help check your published HTML file to make sure your code is up to date.

Once you’ve done that, this “Countdown Timer” sample code written for Twine/SugarCube, should help you out. (Click “Jump to Start” on the UI bar to see other sample code there.)

Let me know if you need any help adapting that to your particular needs. :slight_smile:

I would also recommend replacing your usage of Sugarcane with the 2.x series of SugerCube.

Recommending which GUI application to use, the older discontinued Twine 1.x application or the newer Twine 2.x application, is a more difficult topic because each of the unrelated GUI applications has their pros & cons.

The Twine 1.x application’s pros are:

  1. it allows the user to decide where a Project’s file is stored.
  2. it allows a project to be split across multiple sub-project files, where one file acts as a ‘parent’ which references the other ‘child’ project files.
  3. it allows you to edit multiple Passages at the same time, which allows easy copy-n-paste between those passage.
  4. it supports importing and exporting of TWEE notation files.

The Twine 1.x application’s main con is that it no longer receives updates.

The Twine 2.x application’s pros are:

  1. it is currently being developed, which means it receives updates now and then.
  2. It supports some story formats that the Twine 1.x application doesn’t.
  3. It can be run in a web-browser or as a Desktop application.

The Twine 2.x application’s main cons are that it doesn’t do any of the pros listed for the Twine 1.x application. eg. you can only edit the contents of a single passage at a time, you don’t get to control where a project file gets saved, and you can’t combine the contents of multiple ‘child’ projects together.

Thanks for your help, unfortunately we do really need to carry on using Twine 1 as the project is quite far along in development and there are other pieces of custom JavaScript and stylesheets that are being used, so switch to 2 just breaks most of the game.

So I really am looking for a fix in the older version.

You can stay in Twine 1, if need be, but you can also still update to using SugarCube v2 in Twine 1. Just download the latest version of SugarCube for Twine v1.4 and then follow the Twine 1 installation instructions.

That said, other than possibly embedded images (which you probably shouldn’t be using anyways), I’m not aware of any reason why converting to Twine 2 (using the “Twine 1 compiled HTML to Twine 2 archive converter”) would break anything. Twine 2 also supports custom JavaScript and stylesheets.

You might at least want to give it a try.

Hope that helps! :slight_smile: