Quick Code Samples

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: 2179
Joined: Mon Apr 29, 2002 6:23 pm
Location: Milwaukee

Quick Code Samples

Post by Roody_Yogurt »

This is a new thread for code on how to do simple things that are not worthy of their own thread. Some of this will eventually end up on Hugo By Example, but I thought it'd be good to share it here first.

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

Post by Roody_Yogurt »

Anchorhead-style Object-Grabbing:

Somebody on the ifMUD the other day was reminiscing about how Anchorhead did this thing where if you take an object that hasn't been examined yet, you automatically examine it as you take it. She thought this was a neat effect.

I can see how it might be appealing, although I have to admit that I was more intrigued by the coding challenge. Here's what I came up with:

Code: Select all

! I declare the attribute this way so the author has the option of declaring it
! earlier and aliasing it to something like "special" (if it isn't being used
! for anything)

#if undefined examined
attribute examined
#endif

player_character you "you"
{
	react_after
	{
		if verbroutine = &DoLook and object and word[1] ~= "undo"
		{
			if object is not examined
				object is examined
		}
		return false
	}
}

replace NewVMessages(r, num, a, b)
{
        select r

	case &DoGet
	{
		select num
		case 8
		{
			if object is not examined
			{
				print "You pick up "; The(object);".";
				if &object.long_desc
				{
					print " ";
					run object.long_desc
					object is examined
				}
				else
					""
			}
			else
				print "Taken."
		}
		case else : return false
	}

        case else : return false

       return true ! this line is only reached if we replaced something
}
The weirdest thing about the code above is that I found out that after a successful UNDO, the verbroutine global is set to the verb being undone, which is why in the player.react_before property, I had to check to make sure word[1] wasn't "undo".

I'm thinking that in future releases of Roodylib, I'll have DoUndo clear verbroutine after a successful undo. We'll see.

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

Post by Roody_Yogurt »

Phoenix-style no-description games:

On the other end of the spectrum, there were the Phoenix mainframe games, which completely forsook the concept of object-examination. If anything important was to be known about an object, it'd be listed in its name or short_desc.

Being used to Infocom games, it takes a little bit to get used to these games, but I've found that the style does not always come at the cost of story and immersion. I'm interested in writing at least one new game in this style.

Here's the code:

Code: Select all

! grammar before "verblib.g" is included
verb "look", "l", "examine", "x", "watch"
	*                                                       DoLookAround
	* "around"						DoLookAround
	* "in"/"inside" (PhoenixRules)                               DoLookIn
	* "on" (PhoenixRules)                                       DoLookIn
	* "at"/"to" (PhoenixRules)                                    DoLook
	* "out"/"through" (PhoenixRules)                                DoLookThrough
	* "under"/"underneath"/"beneath"/"below" (PhoenixRules)       DoLookUnder
	* "beside"/"behind"/"around" (PhoenixRules)                    DoLookUnder
	* (PhoenixRules)                                               DoLook


! . . .

routine PhoenixRules
{
	"You don't need to examine or look around anything in this game."
}
Using the routine-as-grammar-token method makes it so the command is understood yet no turn passes when the player accidentally tries to look at something.
Last edited by Roody_Yogurt on Wed Jan 09, 2013 6:07 pm, edited 1 time in total.

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

Post by Roody_Yogurt »

Oops, forgot I had another code sample all ready:

Skipping picking up for DoPutIn:

Now, normally, DoPutIn requires that the object be held. With the checkheld system, it'll automatically pick up a unheld object first which works great almost all of the time.

Still, there might be instances where there is some kind of object manipulation where you don't want to let it seem like the object was picked up or possibly you just want to save the player the extra turn.

Here is some code for that:

Code: Select all

! before "verblib.g" is included
#set USE_CHECKHELD


! Then, code the special DoPutIn objects like this:
object coin "coin"
{
	noun "coin"
	article "a"
	in STARTLOCATION
	before
	{
		object DoPutIn_CheckHeld
		{
			if xobject = fountain
			{
				Perform(&DoPutIn, self, xobject)
			}
			else
				return false
		}
	}
}
There are still ways code like that could get you into trouble, though, so be careful with how you use it.
Last edited by Roody_Yogurt on Wed Jan 09, 2013 6:08 pm, edited 1 time in total.

User avatar
Tdarcos
Posts: 9333
Joined: Fri May 16, 2008 9:25 am
Location: Arlington, Virginia
Contact:

Context switch

Post by Tdarcos »

Fast Context Switch

I saw this one once in a game where you were playing against another player, and it was used for a context switch, to switch from one player to the next.

If you have two items where you need to fast switch between the one being used and the other one, this is the best way I've ever seen to do it.

Let's say you have Item[1] and Item[2], so you can just use this

Code: Select all

    TheItem = 1  ! Start with this one
   
    Item[ TheItem ].Visited = True

! Here's the switch:

    TheItem = 3 - TheItem   
That's all of it. You take the count of the sum of both and subtract the current value from that. It works for any two different numbers. If you have 0 and 1, then 1 - item will switch 1 to 0 and 0 to 1. In this case, 3 - theitem switches 2 to 1 and 1 to 2.

Slickest thing I've ever seen.
Alan Francis wrote a book containing everything men understand about women. It consisted of 100 blank pages.

User avatar
Ice Cream Jonsey
Posts: 28877
Joined: Sat Apr 27, 2002 2:44 pm
Location: Colorado
Contact:

Post by Ice Cream Jonsey »

That Anchorhead thing where you take and examine is great. I wish all my games now had that now!

Part of me wants to open every last Hugo game I've made, write some automated tests and start putting things like that in, and support in each game for the YouCanGo extension (for non Hugo-programmers, that is the one where you get directions on where you can go, if you try to go in a direction you can't).

Also, knowing what I know about proportional fonts, I could make the Health and Ree counters in Fallacy of Dawn line up perfectly. I don't have OCD but the method I used does bug me, with one bar just a bit longer than the other, when they are both full.

One one hand, I'm a big fan of completing a project and being through with it.

On the other, man, we've all done so much cool stuff since then. I could make every graphic in A Crimson Spring better. I could use the music engine I put together for it, so it doesn't do that awkward, "Enter a room, get a new song" thing.

Maybe after I finish Cyberganked I'll do that. Just give a 10 year refresh on all the old games.
the dark and gritty...Ice Cream Jonsey!

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

Post by Roody_Yogurt »

Ice Cream Jonsey wrote:One one hand, I'm a big fan of completing a project and being through with it.
It's weird because, logically, I think the IF Archive is meant to be continuously updated, but on the other hand, uploading something there kind of feels like laying down cement; you really don't want to disturb the foundation unless there are some serious problems.

Other than having a cool old date next to your game in the archive listing ("Oh yeah, THAT's what I did in 1999!"), I think that probably overall, updates are better. I admit that one feels oddly crummy about it, though.
Ice Cream Jonsey wrote:Maybe after I finish Cyberganked I'll do that. Just give a 10 year refresh on all the old games.
I think that's a good idea.


Also, as far as Tdarcos' contribution to this thread goes, let us point out that his code would not actually work. "visited" is an attribute and cannot be used as a property like he does there. Still, I guess it could be used for a property that can be one of two values. If the two values are 0 and 1, though, you could also just do this:

Code: Select all

x = not x

loafingcoyote
Posts: 89
Joined: Wed Jan 04, 2012 2:57 pm
Location: Texas

Post by loafingcoyote »

Roody_Yogurt wrote: It's weird because, logically, I think the IF Archive is meant to be continuously updated, but on the other hand, uploading something there kind of feels like laying down cement; you really don't want to disturb the foundation unless there are some serious problems.
This is an issue I've been meaning to discuss for awhile now.

Right after "The Hugo Clock" was uploaded to the archive I noticed an awful bug, but didn't want to immediately update it, since I didn't want to be a nuisance to the archive maintainers. I was thinking that, you know, those guys have lives too and I'm sure they don't want to spend all their time re-uploading my stuff.

After some time had passed, however, I had moved on to other projects and lost the desire to work on it. My question is, how many updates within what span of time is reasonable? Is uploading stuff to the archive a burden for the maintainers or not? It would help me to know one way or the other, because I agree that works in the archive should be updated. I'm just not sure how often is too often.

-lc

User avatar
Ice Cream Jonsey
Posts: 28877
Joined: Sat Apr 27, 2002 2:44 pm
Location: Colorado
Contact:

Post by Ice Cream Jonsey »

I've never felt that it was possible to annoy the IF Archive updaters. I think they probably go after the upload directory every few days, so if you really had a bunch of submissions, they'd just take the latest one.
the dark and gritty...Ice Cream Jonsey!

DavidK

Post by DavidK »

(I'm one of the Archive maintainers: Jonathan Blask asked me to comment.)

As ICJ says, generally the uploads get looked at every few days, so if there's a bunch of versions of the same thing, I'll just grab the last one. Also, putting a new version of a file into the Archive is really easy, as I'll just overwrite the old version and edit the index entry if needed.

So, in short, don't worry about it and upload. If you're uploading more than a dozen a day, that would be the point to consider slowing down ...

User avatar
Tdarcos
Posts: 9333
Joined: Fri May 16, 2008 9:25 am
Location: Arlington, Virginia
Contact:

Post by Tdarcos »

Roody_Yogurt wrote:Also, as far as Tdarcos' contribution to this thread goes, let us point out that his code would not actually work. "visited" is an attribute and cannot be used as a property like he does there.
Well, pick a different property or array or whatever; it's the context switch in one statement that was the point I was making.
Still, I guess it could be used for a property that can be one of two values. If the two values are 0 and 1, though, you could also just do this:

Code: Select all

x = not x
Well, that only works for 0 and 1. Some conditions might use 1 and 2, which was the example I saw. Sometimes you can't use 0 as an index because the language doesn't support 0 as an array subscript. Taking

Code: Select all

ThisUser = 3 - ThisUser
switches between 1 and 2 very nicely. Or, any two different values, add the two and subtract the current value from that, and it automatically switches between the two values.
Alan Francis wrote a book containing everything men understand about women. It consisted of 100 blank pages.

User avatar
Flack
Posts: 8822
Joined: Tue Nov 18, 2008 3:02 pm
Location: Oklahoma
Contact:

Post by Flack »

Roody, I think you might be able to alleviate some of TDarcos's confusion by changing the thread topic from "Quick Code Samples" to "Quick Hugo Code Samples that Work".
"I failed a savings throw and now I am back."

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

Post by Roody_Yogurt »

DavidK wrote:(I'm one of the Archive maintainers: Jonathan Blask asked me to comment.)

As ICJ says, generally the uploads get looked at every few days, so if there's a bunch of versions of the same thing, I'll just grab the last one. Also, putting a new version of a file into the Archive is really easy, as I'll just overwrite the old version and edit the index entry if needed.

So, in short, don't worry about it and upload. If you're uploading more than a dozen a day, that would be the point to consider slowing down ...
Thanks for chiming in! I think it'll make future update-or-not decisions a lot easier.

Also, authors, I was thinking last night that we could add a >HISTORY command to our games which would give a release history so new uploads don't necessarily feel like you are negating past work. It's an idea, anyway.

loafingcoyote
Posts: 89
Joined: Wed Jan 04, 2012 2:57 pm
Location: Texas

Post by loafingcoyote »

Thank you for the answers guys, I appreciate it. Mostly I just didn't want to violate any unwritten rules that the archive may have developed over the years. This makes me far more enthusiastic about updating my stuff in the future.

Thanks again!

-lc

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

Post by Roody_Yogurt »

"Turn-less" Looking

Now, this one is extremely easy to do, but I just want to list it here so authors remember that doing something like this is always an option. Some authors have decided that passive commands like looking should not take up a turn, so in turn-intensive scenes, players feel free to examine things as much as they want. To be honest, I think this is a good idea, but I have rarely added it to my games.

Of course, in Hugo, the way to do this is to just replace DoLook and have it not return true:

Code: Select all

replace DoLook
{
	local i

	if not light_source
		VMessage(&DoLook, 1)     ! "It's too dark to see anything."
	else
	{
		if not object.long_desc
			! "Looks just like you'd expect..."
			VMessage(&DoLook, 2)

!		if object is living, transparent, not quiet
		if ((object is living, transparent) or
			object is platform or
			(object is container and (object is open or object is not openable))) and
			object is not quiet and object is not already_listed
		{
			for i in object
			{
				if i is not hidden
					break
			}
			if i and object ~= player
			{
				local tempformat
				tempformat = FORMAT
				FORMAT = FORMAT | NOINDENT_F
				list_nest = 0
				print ""
				WhatsIn(object)
				FORMAT = tempformat
			}
		}

		run object.after
!		return true   ! commented out
	}
}
You can do the same for DoLookAround, DoLookIn, and DoLookUnder, if you'd like. Hmm, maybe Roodylib should have a flag for easily turning look-turns on and off. Something to think about.

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

Post by Roody_Yogurt »

Roody_Yogurt wrote:"Turn-less" Looking

You can do the same for DoLookAround, DoLookIn, and DoLookUnder, if you'd like. Hmm, maybe Roodylib should have a flag for easily turning look-turns on and off. Something to think about.
Ok, I added it to Roodylib version 2.9, officially uploaded today:
http://hugo.gerynarsabode.org/index.php?title=Roodylib

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

Post by Roody_Yogurt »

In Hugo, characters default to being transparent (allowing you to see what they are carrying). The tricky thing is, unless the the character is set to unfriendly, the player can grab whatever the character is holding, and there isn't even any special acknowledgement that the character allows it or anything.

In case you have friendly characters in your game, you might want to have something like the following:

Code: Select all

replace character
{
	type character
	pronouns "he", "him", "his", "himself"
	capacity 50
	holding 0
	is living, transparent, static
	exclude_from_all true
	before
	{
		parent(object) DoGet
		{
			local l
			l = string(_temp_string, self.name)
			l--
			print "Nah, that is "; The(self);
			if _temp_string[l] = 's'
			{
				"'."
			}
			else
				"'s."
		}
	}
}

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

Post by Roody_Yogurt »

Last year, I volunteered to betatest this guy's Inform implementation of the ICOM game, The Uninvited.

In the first room (a car), I was dismayed to get this response:
>OPEN GLOVE COMPARTMENT
Opened.
(notice the lack of a content-listing)

Now, part of the problem was that the glove compartment was empty, but any suggestion that I have to LOOK IN a container right after opening it just sends me into a fury (I don't know why).

For a long while, I've been meaning to take a closer look at how Hugo handles it. It turns out that if the container has children, Hugo will list them if the container is not set quiet (so, not until you have LOOKed IN it).

Here is an alternative behavior, where the container lists its children (or lack thereof) on the first opening regardless- but does not list them again:

Code: Select all

replace DoOpen
{
	if not CheckReach(object):  return false

	if object is not openable
	{
		VMessage(&DoOpen, 1)             ! "You can't open that."
		return
	}
	elseif object is open
		VMessage(&DoOpen, 2)             ! "It's already open."
	elseif object is locked
		VMessage(&DoOpen, 3)             ! "It's locked."
	else
	{
		object is open
		if not object.after
		{
			VMessage(&DoOpen, 4)     ! "Opened."

			FindLight(location)     ! in case the light source
						! has been revealed
			if object is not moved
			{
				""
				Perform(&DoLookIn, object)
			}
		}
		object is moved
	}
	return true
}
Now, this code will only really work with non-takeable containers, as objects are also given the moved attribute when taken. Still, I think I am happier with this than the default, and I have not landed on anything that would work well for everything.

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

Infocom-Style PrintStatusLine (With Correct Spacing)

Post by Roody_Yogurt »

It has long bothered me that, as far as I can tell, no system had a nice status-line-drawing example that spaced things out exactly like how Infocom did it, as I thought Infocom's method looks surprisingly good on different screen widths.

For the longest time, I figured their screenwidth-drawing routine must have a really clever algorithm. More recently, I decided that, no, the 5 or so spaces between "Moves: " and "Score: " just gives it a nice stretched-out look no matter where in the screen it is.

Long story short, I put together a routine to emulate the Infocom look as much as possible (although I complicate it a bit more than I really needed to). Since it has different spacing than my other PrintStatusLine code, instead of trying to get it to play nice with my modular system, I'm just releasing it as a standalone routine:

Code: Select all

replace PrintStatusline
{
   local temp_it_obj, len, new_height

	! since calling Art() might change our pronoun, we save it
   temp_it_obj = it_obj

	text to _temp_string
	if not light_source
		print "In the dark";
	else
	{
		print capital location.name;
		if player not in location
		{
			if parent(player).prep
				print ", "; parent(player).prep; " ";
			else
				print ", "; IN_WORD; " ";
			print Art(parent(player));
		}
	}
	text to 0
	len = StringLength(_temp_string)
	if (len + 21) > display.screenwidth
	{
		new_height = 2
	}
	else
		new_height = 1

	Font(BOLD_OFF | ITALIC_OFF | UNDERLINE_OFF | PROP_OFF)

	if new_height < display.statusline_height
	&#123;
		window display.statusline_height
		&#123;
			cls
		&#125;
	&#125;
	display.statusline_height = new_height
	window display.statusline_height
	&#123;
		color SL_TEXTCOLOR, SL_BGCOLOR
		cls
		locate 1, 1
		print "\_";
		StringPrint&#40;_temp_string&#41;
		if display.statusline_height = 2
		&#123;
			locate 2,2
		&#125;
		elseif &#40;display.screenwidth - &#40;len + 30&#41;&#41; >= 10
			print to &#40;display.screenwidth - 30&#41;;
		else
		&#123;
			print to &#40;display.screenwidth - 16&#41;;
		&#125;
		if &#40;&#40;display.screenwidth - &#40;len + 30&#41;&#41; >= 10&#41; or
		&#40;display.statusline_height = 2&#41;
		&#123;
			print "Score&#58; "; number score;
			if display.statusline_height = 1
				print to &#40;display.screenwidth - 14&#41;;
			else
				print to 17;
			print "Moves&#58; "; number counter;
		&#125;
		else
		&#123;
			 print "S&#58; "; number score;
			 print to &#40;display.screenwidth - 9&#41;;
			 print "M&#58; "; number counter;
		&#125;
	&#125;
	color TEXTCOLOR, BGCOLOR
	Font&#40;DEFAULT_FONT&#41;
   it_obj = temp_it_obj
&#125;

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

"travel mode"

Post by Roody_Yogurt »

For Robb's WIP, the sheer amount of traveling made it sound to me like the game could use a quicker way to move around. I thought a keypress system would work best. This example is not complete- it only handles cardinal directions- but here is some code for doing such a system (as I imagine it- or a variation thereof- could be applied to the rare IF game, too).

Code: Select all

#ifclear _SYSTEM_H
#include "system.h"
#endif

#if undefined PauseForkey
routine PauseForKey&#40;p&#41;	! Where p is a prompt, if it ends up being used
&#123;
	local key

	key = system&#40;READ_KEY&#41;
	if system_status or system&#40;MINIMAL_INTERFACE&#41;
	&#123;
		! If READ_KEY isn't available, we have to use the
		! regular pause-with-cursor &#40;and maybe a prompt&#41;
		if p
		&#123;
			if not system&#40;MINIMAL_INTERFACE&#41;
			! If we give a prompt, it always goes at the bottom
				locate &#40;display.screenwidth-20&#41;, display.screenheight
			Font&#40;PROP_ON | ITALIC_ON | BOLD_OFF&#41;
			print p;
			Font&#40;DEFAULT_FONT | ITALIC_OFF&#41;
		&#125;
		pause
		key = word&#91;0&#93;
	&#125;
	else
	&#123;
		while true
		&#123;
			key = system&#40;READ_KEY&#41;
			system&#40;PAUSE_100TH_SECOND&#41;
			if key&#58;break
		&#125;
	&#125;

	return key
&#125;
#endif  ! if undefined PauseForKey

routine DoTravelMode
&#123;
	local a, r
	while true
	&#123;
	print "&#91;Travel mode. Press an arrow key to move in a cardinal direction
	or press ESC to go back to regular mode.&#93;"
		r = 0
		a = PauseForKey
		select a
			case UP_ARROW&#58; r = n_obj
			case DOWN_ARROW &#58; r = s_obj
			case RIGHT_ARROW &#58; r = e_obj
			case LEFT_ARROW &#58; r = w_obj
			case ESCAPE_KEY
			&#123;
				break
			&#125;
		if r
		&#123;
			if Perform&#40;&DoGo, r&#41;
			&#123;
				main
			&#125;
			""
		&#125;
	&#125;
	"\n&#91;Returning to normal mode...&#93;"
&#125;
The main thing to be aware of is that an >UNDO will take the player back to before "travel mode" was initiated, as long as memory allows it. Since memory probably won't allow it half of the time, you might want to disallow UNDO for a step after travel mode just for consistency.

Post Reply