Quick Code Samples
Moderators: Ice Cream Jonsey, joltcountry
-
- Posts: 2256
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
Quick Code Samples
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.
-
- Posts: 2256
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
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:
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.
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
}
I'm thinking that in future releases of Roodylib, I'll have DoUndo clear verbroutine after a successful undo. We'll see.
-
- Posts: 2256
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
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:
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.
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."
}
Last edited by Roody_Yogurt on Wed Jan 09, 2013 6:07 pm, edited 1 time in total.
-
- Posts: 2256
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
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:
There are still ways code like that could get you into trouble, though, so be careful with how you use it.
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
}
}
}
Last edited by Roody_Yogurt on Wed Jan 09, 2013 6:08 pm, edited 1 time in total.
- Tdarcos
- Posts: 9556
- Joined: Fri May 16, 2008 9:25 am
- Location: Arlington, Virginia
- Contact:
Context switch
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
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.
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
Slickest thing I've ever seen.
"When I die, I want it easy and peaceful in my sleep, like my uncle.
Not screaming and crying like his passengers."
Not screaming and crying like his passengers."
- Ice Cream Jonsey
- Posts: 30184
- Joined: Sat Apr 27, 2002 2:44 pm
- Location: Colorado
- Contact:
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.
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!
-
- Posts: 2256
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
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.Ice Cream Jonsey wrote:One one hand, I'm a big fan of completing a project and being through with it.
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.
I think that's a good idea.Ice Cream Jonsey wrote:Maybe after I finish Cyberganked I'll do that. Just give a 10 year refresh on all the old games.
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
-
- Posts: 89
- Joined: Wed Jan 04, 2012 2:57 pm
- Location: Texas
This is an issue I've been meaning to discuss for awhile now.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.
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
- Ice Cream Jonsey
- Posts: 30184
- Joined: Sat Apr 27, 2002 2:44 pm
- Location: Colorado
- Contact:
(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 ...
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 ...
- Tdarcos
- Posts: 9556
- Joined: Fri May 16, 2008 9:25 am
- Location: Arlington, Virginia
- Contact:
Well, pick a different property or array or whatever; it's the context switch in one statement that was the point I was making.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, 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. TakingStill, 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
Code: Select all
ThisUser = 3 - ThisUser
"When I die, I want it easy and peaceful in my sleep, like my uncle.
Not screaming and crying like his passengers."
Not screaming and crying like his passengers."
- Flack
- Posts: 9090
- Joined: Tue Nov 18, 2008 3:02 pm
- Location: Oklahoma
- Contact:
-
- Posts: 2256
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
Thanks for chiming in! I think it'll make future update-or-not decisions a lot easier.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 ...
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.
-
- Posts: 89
- Joined: Wed Jan 04, 2012 2:57 pm
- Location: Texas
-
- Posts: 2256
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
"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:
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.
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
}
}
-
- Posts: 2256
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
Ok, I added it to Roodylib version 2.9, officially uploaded today: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.
http://hugo.gerynarsabode.org/index.php?title=Roodylib
-
- Posts: 2256
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
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:
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."
}
}
}
-
- Posts: 2256
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
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:
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:
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.
In the first room (a car), I was dismayed to get this response:
(notice the lack of a content-listing)>OPEN GLOVE COMPARTMENT
Opened.
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
}
-
- Posts: 2256
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
Infocom-Style PrintStatusLine (With Correct Spacing)
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:
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
{
window display.statusline_height
{
cls
}
}
display.statusline_height = new_height
window display.statusline_height
{
color SL_TEXTCOLOR, SL_BGCOLOR
cls
locate 1, 1
print "\_";
StringPrint(_temp_string)
if display.statusline_height = 2
{
locate 2,2
}
elseif (display.screenwidth - (len + 30)) >= 10
print to (display.screenwidth - 30);
else
{
print to (display.screenwidth - 16);
}
if ((display.screenwidth - (len + 30)) >= 10) or
(display.statusline_height = 2)
{
print "Score: "; number score;
if display.statusline_height = 1
print to (display.screenwidth - 14);
else
print to 17;
print "Moves: "; number counter;
}
else
{
print "S: "; number score;
print to (display.screenwidth - 9);
print "M: "; number counter;
}
}
color TEXTCOLOR, BGCOLOR
Font(DEFAULT_FONT)
it_obj = temp_it_obj
}
-
- Posts: 2256
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
"travel mode"
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).
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.
Code: Select all
#ifclear _SYSTEM_H
#include "system.h"
#endif
#if undefined PauseForkey
routine PauseForKey(p) ! Where p is a prompt, if it ends up being used
{
local key
key = system(READ_KEY)
if system_status or system(MINIMAL_INTERFACE)
{
! If READ_KEY isn't available, we have to use the
! regular pause-with-cursor (and maybe a prompt)
if p
{
if not system(MINIMAL_INTERFACE)
! If we give a prompt, it always goes at the bottom
locate (display.screenwidth-20), display.screenheight
Font(PROP_ON | ITALIC_ON | BOLD_OFF)
print p;
Font(DEFAULT_FONT | ITALIC_OFF)
}
pause
key = word[0]
}
else
{
while true
{
key = system(READ_KEY)
system(PAUSE_100TH_SECOND)
if key:break
}
}
return key
}
#endif ! if undefined PauseForKey
routine DoTravelMode
{
local a, r
while true
{
print "[Travel mode. Press an arrow key to move in a cardinal direction
or press ESC to go back to regular mode.]"
r = 0
a = PauseForKey
select a
case UP_ARROW: r = n_obj
case DOWN_ARROW : r = s_obj
case RIGHT_ARROW : r = e_obj
case LEFT_ARROW : r = w_obj
case ESCAPE_KEY
{
break
}
if r
{
if Perform(&DoGo, r)
{
main
}
""
}
}
"\n[Returning to normal mode...]"
}