Howdy, Stranger!

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

Making a "Don't Move" segment in Harlowe

Basically, I want to make a segment in my game where the player has to hide from a monster. They go to their hiding place, and the monster comes up to it, suspicious. The game says "Something inside you says 'DON'T MOVE.'" When you click the link, the screen is blank. If the player moves the mouse during this, they get caught by the monster. If the player doesn't move the mouse for about 10 seconds, the monster goes away. How could I achieve this in a game? I've never worked with mouse-based commands before, so I'd appreciate if you explained the commands a bit. Thanks in advance!

Comments

  • warning: I have limited knowledge of Javascript so the following solution may either contain misunderstanding or not be fully compatible with all web-browser brands/versions, you may want to get a more experienced Javascript coder to either look at this solution or suggest a better one.

    There are three issues you need to overcome to do what you want, they are:

    1. Harlowe does not have a macro that monitors mouse movement, so you will need to use Javascript to over come this.

    2. The (live:) macro has no documented way to externally stop it before it's associated timer fires, which you need to do in the 'player moved mouse' (failed) use-case. You will need to use Javascript to implement the delay so it can be stopped in the failed use-case.

    3. Harlowe has no documented Javascript API which means there is documented way to update the content of the page, which you need to do in both the success and failed use-cases. You can use Javascript to inject text into the current page but any Harlowe markup or macros in that text will not be executed.

    You can get around this by using CSS to hide both the success and failure outputs and then use Javascript to make the relevant output visible later.

    The following proof of concept is made up of three parts:

    A. The Javascript code that defines a MY namespace than contains a monitorMouseMovement function you can use to monitor mouse movement, the code needs to be placed with your story's Story Javascript area.
    if (typeof window.MY == 'undefined') {
      // create a new namespace
      window.MY = {};
    }
    
    /* The position of the cursor when monitoring starts. */
    window.MY.startX = 0;
    window.MY.startY = 0;
    
    /* ID of the Timer being used to track the success delay. */
    window.MY.successTimer;
    
    window.MY.showNamedHook = function(hookName) {
    	var selector = 'tw-hook[name="' + hookName + '"]';
    	$(selector).css("display", "inline");
    };
    
    /* What happens if the mouse moves. */
    window.MY.onMoveMovement = function(event) {
    
    	var hookName = event.data.hookName;
    	
    	// Determine how far the mouse has moved.
    	var diffX = Math.abs(event.pageX - MY.startX);
    	var diffY = Math.abs(event.pageY - MY.startY);
    	
    	// If the mouse has moved to much.
    	if (diffX > 2 || diffY > 2) {
    		console.log('onMoveMovement: failed: hook: ' + hookName);
    
    		// Stop the success timer.
    		clearTimeout(MY.successTimer);
    		
    		// Stop monitoring mouse movement.
    		$(this).off(event);
    		
    		// Display the relevant named hook.
    		MY.showNamedHook(hookName);
    	}
    };
    
    /* Start monitoring mouse movement on the tw-passage element.
    * @param successHook - the name of the hook containing the content to display if the player DID NOT move.
    * @param failureHook - the name of the hook containing the content to display if the player DID move.
    * @param delay - the time in milliseconds to delay the success outcome.
    */
    window.MY.monitorMouseMovement = function(successHook, failureHook, delay) {
    
    	// Determine the current position of the mouse cursor.
    	$('tw-passage').one('mousemove', function(event){
    			MY.startX = event.pageX;
    			MY.startY = event.pageY;
    		});
    	
    	// Setup the success timer
    	MY.successTimer = setTimeout(function(hookName) {
    
    		// Stop monitoring mouse movement.
    		$('tw-passage').off('mousemove', MY.onMoveMovement);
    
    		// Display the relevant named hook.
    		MY.showNamedHook(hookName);
    
    	}.bind(this, successHook), delay);
    
    	// Start monitoring mouse movement.
    	var data = {hookName: failureHook};
    	$('tw-passage').on('mousemove', data, MY.onMoveMovement);
    };
    


    B. The CSS code used to hide both the success and failed named hooks that will be used in part C, this code needs to be placed with your story's Story Stylesheet area.
    tw-hook[name="success"], tw-hook[name="failed"] {
    	display: none;
    }
    


    C. The markup / code used within the passage you want mouse movement monitoring to happen in, it will look something like following.
    |success>[The monster wanders away, you think you can [[move now->Run Away]].]\
    |failed>[You MOVED and the monster [[found you->DEAD]]!]
    
    <script>MY.monitorMouseMovement('success', 'failed', 10000));</script>
    

    As you can see in both part A and part C the monitorMouseMovement method takes there parameters:

    i. The name of the hook to make visible if the play DOES NOT move. (success)
    ii. The name of the hook to make visible if the play DOES move. (failed)
    iii. The time (in milliseconds) to wait before showing the success related name hook. (10 seconds x 1000 = 10000)

    note: You can change the names of the two named hooks if you want to, just remember to change the first two parameters sent to the monitorMouseMovement method in part C as well as the CSS selectors used in part B.

    more warnings:
    1. It is possible for both the success and failed content to become visible if the player moves the mouse at exactly the same time that the success timer runs out.

    2. The Javascript in part A makes a number of assumptions and contain no error catching to check that those assumptions are true.
  • greyelf wrote: »
    warning: I have limited knowledge of Javascript so the following solution may either contain misunderstanding or not be fully compatible with all web-browser brands/versions, you may want to get a more experienced Javascript coder to either look at this solution or suggest a better one.

    There are three issues you need to overcome to do what you want, they are:

    1. Harlowe does not have a macro that monitors mouse movement, so you will need to use Javascript to over come this.

    2. The (live:) macro has no documented way to externally stop it before it's associated timer fires, which you need to do in the 'player moved mouse' (failed) use-case. You will need to use Javascript to implement the delay so it can be stopped in the failed use-case.

    3. Harlowe has no documented Javascript API which means there is documented way to update the content of the page, which you need to do in both the success and failed use-cases. You can use Javascript to inject text into the current page but any Harlowe markup or macros in that text will not be executed.

    You can get around this by using CSS to hide both the success and failure outputs and then use Javascript to make the relevant output visible later.

    The following proof of concept is made up of three parts:

    A. The Javascript code that defines a MY namespace than contains a monitorMouseMovement function you can use to monitor mouse movement, the code needs to be placed with your story's Story Javascript area.
    if (typeof window.MY == 'undefined') {
      // create a new namespace
      window.MY = {};
    }
    
    /* The position of the cursor when monitoring starts. */
    window.MY.startX = 0;
    window.MY.startY = 0;
    
    /* ID of the Timer being used to track the success delay. */
    window.MY.successTimer;
    
    window.MY.showNamedHook = function(hookName) {
    	var selector = 'tw-hook[name="' + hookName + '"]';
    	$(selector).css("display", "inline");
    };
    
    /* What happens if the mouse moves. */
    window.MY.onMoveMovement = function(event) {
    
    	var hookName = event.data.hookName;
    	
    	// Determine how far the mouse has moved.
    	var diffX = Math.abs(event.pageX - MY.startX);
    	var diffY = Math.abs(event.pageY - MY.startY);
    	
    	// If the mouse has moved to much.
    	if (diffX > 2 || diffY > 2) {
    		console.log('onMoveMovement: failed: hook: ' + hookName);
    
    		// Stop the success timer.
    		clearTimeout(MY.successTimer);
    		
    		// Stop monitoring mouse movement.
    		$(this).off(event);
    		
    		// Display the relevant named hook.
    		MY.showNamedHook(hookName);
    	}
    };
    
    /* Start monitoring mouse movement on the tw-passage element.
    * @param successHook - the name of the hook containing the content to display if the player DID NOT move.
    * @param failureHook - the name of the hook containing the content to display if the player DID move.
    * @param delay - the time in milliseconds to delay the success outcome.
    */
    window.MY.monitorMouseMovement = function(successHook, failureHook, delay) {
    
    	// Determine the current position of the mouse cursor.
    	$('tw-passage').one('mousemove', function(event){
    			MY.startX = event.pageX;
    			MY.startY = event.pageY;
    		});
    	
    	// Setup the success timer
    	MY.successTimer = setTimeout(function(hookName) {
    
    		// Stop monitoring mouse movement.
    		$('tw-passage').off('mousemove', MY.onMoveMovement);
    
    		// Display the relevant named hook.
    		MY.showNamedHook(hookName);
    
    	}.bind(this, successHook), delay);
    
    	// Start monitoring mouse movement.
    	var data = {hookName: failureHook};
    	$('tw-passage').on('mousemove', data, MY.onMoveMovement);
    };
    


    B. The CSS code used to hide both the success and failed named hooks that will be used in part C, this code needs to be placed with your story's Story Stylesheet area.
    tw-hook[name="success"], tw-hook[name="failed"] {
    	display: none;
    }
    


    C. The markup / code used within the passage you want mouse movement monitoring to happen in, it will look something like following.
    |success>[The monster wanders away, you think you can [[move now->Run Away]].]\
    |failed>[You MOVED and the monster [[found you->DEAD]]!]
    
    <script>MY.monitorMouseMovement('success', 'failed', 10000));</script>
    

    As you can see in both part A and part C the monitorMouseMovement method takes there parameters:

    i. The name of the hook to make visible if the play DOES NOT move. (success)
    ii. The name of the hook to make visible if the play DOES move. (failed)
    iii. The time (in milliseconds) to wait before showing the success related name hook. (10 seconds x 1000 = 10000)

    note: You can change the names of the two named hooks if you want to, just remember to change the first two parameters sent to the monitorMouseMovement method in part C as well as the CSS selectors used in part B.

    more warnings:
    1. It is possible for both the success and failed content to become visible if the player moves the mouse at exactly the same time that the success timer runs out.

    2. The Javascript in part A makes a number of assumptions and contain no error catching to check that those assumptions are true.

    I'm not sure if it was your intention, but I simply copy/pasted your code into my story's Javascript/Story Stylesheet/Passage, and it didn't work. It displayed a pop-up error saying "Sorry to interrupt, but this page's code has got itself in a mess." The only experience I have with coding is with Twine and using Inspect Element to change words on pages, so I can't fix it. I'll wait and see if anyone else can help. This should get me/them started, so thanks for the help!
  • […] I'll wait and see if anyone else can help. […]
    The in-passage script has one too many closing parenthesis. It should look like the following:
    |success>[The monster wanders away, you think you can [[move now->Run Away]].]\
    |failed>[You MOVED and the monster [[found you->DEAD]]!]
    
    <script>MY.monitorMouseMovement('success', 'failed', 10000);</script>
    
  • greyelf wrote: »
    code and stuff
    […] I'll wait and see if anyone else can help. […]
    The in-passage script has one too many closing parenthesis. It should look like the following:
    |success>[The monster wanders away, you think you can [[move now->Run Away]].]\
    |failed>[You MOVED and the monster [[found you->DEAD]]!]
    
    <script>MY.monitorMouseMovement('success', 'failed', 10000);</script>
    

    Alright, with MadExile's fix, the "Don't Move" passage kind of works. The failure message only appears when the mouse is moved onto the message's position. Is there any way I could set it to be the opposite, where the player clicks the "don't move" link, and a small invisible text shows up there, and if the mouse moves out of that text, the failure text appears?
Sign In or Register to comment.