forcing a before property to not use up a turn

This is a discussion / support forum for the Hugo programming language by Kent Tessman. Hugo is a powerful programming language for making text games / interactive fiction with multimedia support.

Hugo download links: https://www.generalcoffee.com/hugo
Roody Yogurt's Hugo Blog: https://notdeadhugo.blogspot.com
The Hugor interpreter by RealNC: http://ifwiki.org/index.php/Hugor

Moderators: Ice Cream Jonsey, joltcountry

Roody_Yogurt
Posts: 2255
Joined: Mon Apr 29, 2002 6:23 pm
Location: Milwaukee

forcing a before property to not use up a turn

Post by Roody_Yogurt »

I had a couple conversations with Kent recently about the possible future of Hugo- not to get anyone's hopes up; it was just a casual chat daydreaming about what might be appealing one day. At one point, I shared some features that I would like to see. After the email was sent, I remembered how there have been times in Hugo where I wished the author had more control over whether a command used up a turn, but after thinking about it, I thought, hey, that's all handled by the library so that's completely under our control already.

Mainly, the issue is this: especially when an author is new to Hugo, one of the most appealing ways to give special responses is to use the before property routine system. In some of these cases, the author will want to do something like a "You can't get pass the rubble." message. "You can't go that way." messages normally don't use up a turn, so it's kind of jarring when there's no easy way to use a before property routine and have a matching behavior.

So, today, I attempted to allow for cases like this. Here is the necessary code:

Code: Select all

global force_false

replace Perform(action, obj, xobj, queue, isxverb)
{
	local r
	local objtemp, xobjtemp, verbtemp, actortemp, first_call
#ifclear NO_XVERBS
	local restoring
	if verbroutine = &DoRestore:  restoring = true
#endif

	if action = verbroutine and obj = object and xobj = xobject
		first_call = true

#ifset DEBUG
	if debug_flags & D_PARSE
	{
		print "\B[Perform("; number action; ", "; obj.name;
		if (debug_flags & D_OBJNUM) or queue = -1
			print " ["; number obj; "]";
		print ", "; xobj.name;
		if (debug_flags & D_OBJNUM) or queue = -1
			print " ["; number xobj; "]";
		if queue
			print ", "; number queue;
		print ")]\b"
	}
#endif

	if queue
		parser_data[PARSER_STATUS] |= PERFORM_QUEUE

	if not queue and object
		parser_data[LAST_SINGLE_OBJECT] = object
	else
		parser_data[LAST_SINGLE_OBJECT] = 0

	parser_data[VERB_IS_XVERB] = isxverb

!  These temp objects guarantee we go back to whatever the previous
!  settings were before Perform was called
	objtemp = object
	xobjtemp = xobject
	verbtemp = verbroutine
	actortemp = actor

	object = obj
	xobject = xobj
	verbroutine = action
	actor = player

!  some stuff we do when Perform is called by the engine
	if (parser_data[PARSER_STATUS] & PARSER_ACTIVE) and not isxverb
		DeactivateParser

#ifclear NO_OBJLIB
	if verbroutine = &DoGo and not object
		SetupDirectionObjects
#endif

	! Itemize each object in a list of multiple objects
	if queue > 0 and object > display
	{
#ifset USE_CHECKHELD
		! Check if an ImplicitTakeForDrop was just done, meaning we
		! need a newline before printing the next "object:"
		if checkheld is workflag
			print ""
		checkheld is not workflag
#endif
		print object.name; ":  ";
	}

	r = BeforeRoutines(queue)
	if not r ! if action not stopped by before routines
	{
		r = call action                 ! object.after and xobject.after
		                                ! run by verbroutine
#ifclear NO_XVERBS
		if restoring
			verbroutine = &DoRestore
#endif
		if r  ! if action was successful, run after routines
		{
			AfterRoutines
		}
	}

!:DonePerform area
#ifset USE_CHECKHELD
	ResetCheckHeld
#endif

	last_object = object

	verbroutine = verbtemp
	object = objtemp
	xobject = xobjtemp
	actor = actortemp

	if queue = -1
		last_object = -1

	parser_data[PARSER_STATUS] = PARSER_RESET
	if force_false and first_call
	{
		force_false = 0
		if not queue
			return false
	}
	return r
}
The relevant parts of the Perform replacement are near the beginning and the end. Perform can be called several times for one command (as one command is sometimes directed to another), so we want to make sure we are checking for our "force_false" global only once. To do this, I check the values of verbroutine, object, and xobject (which are initially set by the engine) against the routine arguments. If they match, the local variable "first_call_ is set true. At the end of the routine, we check the values of force_false, first_call, and queue (used by commands that support "all", like >GET ALL) and if the conditions are right, Perform will return false and no turn will be used.

So then we could have code like this for, say, a mine shaft or something:

Code: Select all

	before
	{
		object DoGo
		{
			if rubble in location
			{
				"You can't get past the rubble."
				force_false = true
				return true
			}
			else
				return false
		}
	}
I don't think I'll add this as a permanent feature to Roodylib or even write a Not Dead Hugo post about it since it's kind of hacky, so this goes here in case this would be useful to someone.