Roody's Lessons Somewhat Learned

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

Roody's Lessons Somewhat Learned

Post by Roody_Yogurt »

I'm one of those guys who tries to write IF despite having almost no programming knowledge. Death To My Enemies would not exist without some help I received coding an array.

Anyhow, I've been keeping track of the problems I'm running into and their solutions since picking Hugo back up earlier this year. I'm going to share them here. Hopefully, some of it will help other people for whom programming is new.

Here's just a bunch of stuff to start off with:

scoring

I've noticed a couple things wrong with the routine PrintScore(end_of_game) in hugolib.h. First off, the line "print ranking[(score*100)/MAX_SCORE*MAX_RANK/100]" should end with a ";". As it is, there is a new line where there shouldn't be, resulting in something like:
You have scored a total of 10 out of 10, giving you the rank of MASTERMIND
.
Secondly, I've found that the default equation didn't work for my 30 point/ 4 ranked game. The comment describes it as thus:

! A complicated formula, since only
! integer division is allowed:
!

So I was led to believe that irrational numbers were screwing things up. In my game, I've replaced the line with "print ranking[(score*MAX_RANK)/MAX_SCORE];" MAX_RANK in this instance is the number of ranks, not including the ranking for 0 points. Anyhow, this may not be a perfect equation but it has worked in all of the examples I tested it with.

attributes vs. properties

In my Hugo game-coding so far, I found that I liked to check the state of my objects with the "special" attribute (examples: see if the player had searched something yet, whether something was broke or functional, etc.). The trouble with this is that I'd need another attribute if I had an object with more variables to check, and I was a bit confused about whether new attributes needed to be aliased to other attributes.

Eventually, I realized I could do a lot of what I was doing with properties instead. Properties don't only need to be used for drinks.left counts in soda can objects and the like. One of the results of this method, I've found, is that my new properties make the code easier to read since their names are usually a bit more specific than "special," and one property can be assigned as many states as I want instead of just the is/is not aspect of an attribute.

proper before/after usage

As mentioned in the manual, before and after have usage specifiers like object, xobject, actor, and location. In some instances, I found myself trying to catch verbs with the wrong usage specifier (using object or xobject when I should have been using location or actor). I think because object and xobject were the easiest for me to grasp, I gravitated towards those options unnecessarily.

Be sure to take a look at the order of before routine checks on page 127 of the manual to help determine where you need to catch a verb.

restart without prompt

In a joke game I wrote for a friend, I had a computer on which you could play the game you were already playing, so I wanted "play game" to act like "RESTART" without the "are you sure? y/n" prompt.

Now, this is kind of a horrible idea and shouldn't be emulated, but I thought I'd share the code in case a similar problem comes up for someone else for whom the answer isn't instantly obvious.

(In the game, the text adventure was on a phone, so >PLAY GAME would set phone as special and then Perform(&DoRestart) )

Code: Select all

player_character you "you"
{
	before
	{
		actor DoRestart
			{
				if phone is special
				{	word[1] = "yes"
					if YesOrNo = true
						{
						if not restart
						VMessage(&DoRestart, 2)  ! "Unable to restart."
						}   
				}
				else
				return false
			}
	}
}
It's worth noting that this approach did not work:

Code: Select all

	before
	{
		actor {
			if verbroutine = &DoRestart
		
edit: It occurs to me that all I need to do to force a restart was to add this code to the >PLAY GAME response:

Code: Select all

       if not restart
       "huh, it didn't restart"

capitalized nouns

I recently found that Hugo hates them (well, at least, it refused to let me refer to my character within the game). I'm sure it's mentioned somewhere, but it's just one of those things someone like myself might forget (and did).

clearing the screen

My work-in-progress uses Christopher Tate's converse.h extension, which uses the status line for dialogue choices during conversations. I found that restarting the game while in conversation left the choices on the top of the screen no matter how many times I put in lines changing status line height or cls's in my init code.

Johnny Rivera suggested I put "window 0" early in my init. This did the trick. I would suggest anyone who finds themselves in a similar situation do the same.
Last edited by Roody_Yogurt on Thu Jun 23, 2011 6:33 pm, edited 1 time in total.

Bainespal
Posts: 151
Joined: Fri Jul 09, 2010 8:59 am

Post by Bainespal »

The only programming I've ever done has been my many experiments with trying to write IF. Some of these sound like situations that I'll probably encounter eventually. In particular, the advice about properties versus attributes is well appreciated, as is the warning about the bug in the PrintScore routine. I'm planning on having some kind of scoring system, and your example has surely saved me some frustration. Also, I would never have thought to put the code for the modification of the Restart command in the PC object!

Thank you!

Isxek
Posts: 10
Joined: Sat Aug 15, 2009 11:08 am
Contact:

New version of Hugo?

Post by Isxek »

Has anyone heard back from Kent regarding Hugo's progress? The last version was from way back 2006.

I know I've asked this before on the other forum (www.intfiction.org/forum), but hopefully - maybe - Kent has mentioned something recently about updating Hugo in the near future. (Maybe even creating a web-based 'terp :D )

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

Post by Roody_Yogurt »

First off, as an answer to that last question, Kent was just pining the other day for a time when he'll be able to make some tweaks to Hugo. He's currently dedicating himself to another project, but he definitely hopes to return to it.

Back to things I've been learning!! Woo!

So, last night, I was trying to code something where an impromptu... um... prompt pops up, asking you to name this thing, at which point I wanted to change the self.name on the object.

That ended horribly. After much trying, I found the Vault of Hugo sample code (and section of the Hugo manual) where it explains that for that kind of stuff, you need to do dictionary entry stuff. Anyhow, I will take another look at that another day.

Still, I've also been curious if I were to ever have a word puzzle in my game, how I would do it- again using a prompt and accepting any non-dictionary word and matching it to the correct answer.

Merk has code for stuff like that in TTS (routine DoSayString in newverbs.h, routine IsEqual in utils.h, and the tempbuf arrays), but I was in denial that Hugo didn't have something built-in that I could use to do the same thing.

I did eventually get something to work. Now, Hugo stores the last quoted string in parse$. I found that to compare parse$ to another string, you first have to transfer it over to an array, at which point you use StringDictCompare, not StringCompare. If the two strings are identical, StringDictCompare returns as zero so your code should check for such.

Here is an example:

Code: Select all

!somewhere up top
array names[10]

! down in the game code
"The puzzlebot turns to you and asks, \"What is in the middle of holes?\""
GetInput("type SAY \"ANSWER\"")
string(names, parse$, 10)
if StringDictCompare(names, "l") = 0
	   print "Correct!"
else
           print "Wrong!"
Interestingly, the case of the letter provided by the player doesn't matter. I doubt this example is the most durable of code, but there you have it, some quick and easy puzzle answer shouting.

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

Post by Roody_Yogurt »

I did eventually get the naming thing working right. I just modified the Vault of Hugo example to work with what I already had so far.

Anyhow, let's go on to other things I have learned. In the category of 'basics I forget and have to relearn every so often':

- Verb routines have to return true for the turn counter to go up and PrintStatusLine to be updated.

- Before and after routines that do not specify verbroutines are applied to all verbs, like

Code: Select all

before {
           object
                   {
                    "Never mind the object!"
                    }
           }
(of course, you'll probably also have to trap for 'xobject' and so forth.)

- I still occasionally forget that you can't use parentheses over multiple lines and must use braces

- A quick alternative to 'if word[3] = "donkey" or word[3] = "mother"' is 'if word[3] = "donkey","mother"'

- In the last game I finished, I had forgotten the correct way to end a game. I was trying to call the routine EndGame(#) (with # being the endflag), when really (and somewhat unintuitively), all you have to do is set the global "endflag = #"

Next time, I will go into some PreParse and ParseError stuff I have learned!

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

Post by Roody_Yogurt »

So in my last joke game, I wanted to code some Easter Egg responses to some unimportant commands. I noticed the existence of PreParse and thought, hey, I can just check for some words and return true and never have to bother that Parser at all! The command in question was >THE HELL? so it especially didn't seem right to make some verb grammar entry for it.

The problem was that at least part of the parser is handled engine-side, so through normal methods, you can't just skip it. It turns out the only way to get the parser to shut up is to use ParseError().

ParseError lets you either replace parser error messages with something more fitting for your game. If you call ParseError(100) or higher, it calls the respective CustomError(). If there isn't a matching CustomError, it prints nothing, which is what we want in this instance.

So let's put the following code in player.before:

Code: Select all

		actor PreParse
			{
			if (word[1] = "hell", "hell?")
				{
				"I know, right?"
			ParseError(100)
				}
			else
			return false
			}
("hell is word[1] because "the" is ignored)

Initially, I had hoped that doing things this way would leave no parser clues that certain commands are understood, but since this seems to add "hell" to the dictionary, the parser will always admit that it recognizes it.

So the result isn't that much different from the verb grammar/verb routine route, but this way might save a little bit of time. and may be occasionally useful.

Post Reply