Friday, September 26, 2014

phones and stuff

The other month, I was taking a look at Cardinal Teulbach's phone code in the Hugo game source directory at the IF Archive, partly because the phone in Cryptozookeeper frustrated me and partly just because I was wondering if I could improve upon it, knowing that people still use whatever they find in that directory for their games today.

I think it might have been easier to just write a new phone class from scratch, so while I made some improvements, I'm not entirely sure how happy I am with the whole thing.  Really, there are just so many options on how to design a phone in a game that it seems kind of like wasted effort to perfectly hone this one approach.

Still, while I don't like the results enough to upload my code to the IF Archive, I figure I should share it somewhere so I'll share it here.

EDIT: misterman83 over at the joltcountry Hugo forum suggested moving answer/dial responses to the phone number object properties, and I agree that it's a good idea.  The code has been updated accordingly.

First off, your game would need the following grammar:

verb "dial", "call"
*                                               DoVague
* object                                        DoDial
* object "on" xobject                           DoDial

verb "answer"
*                                               DoVague
* object                                        DoAnswer

verb "hang"
* "up"                                          DoHangup
* "up" object                                   DoHangup

And then here is the rest of the code:

attribute someone_on_line alias switchedon
property current_number alias misc
property react_dial alias react_before
property react_answer alias react_after
class phone "telephone_class"
{
type phone
is open, platform
nouns "telephone", "phone"
article "a"
parse_rank 3
long_desc
{
"It's just an ordinary telephone.";
if Contains(player, receiver)
{
print AFTER_PERIOD;
"The receiver is off the hook."
}
else : ""
}
before
{
object DoGet,DoListen, DoHangup, DoAnswer
{
Perform(verbroutine, receiver)
}
xobject DoPutIn
{
if object = receiver
Perform(&DoPutIn, receiver, hook)
else : return false
}
xobject DoGet
{
if object = receiver
Perform(&DoGet, receiver, hook)
else : return false
}
}
react_before
{
if verbroutine = &DoGo
{
if object.type = direction and
((location.(object.dir_to)).type = room or
(location.(object.dir_to)).type = door ) and
Contains(player, receiver)
{
"You hang up the phone before leaving the room."
move receiver to hook
receiver is hidden
receiver is not someone_on_line
}
}
elseif verbroutine = &DoListen and not object
{
if receiver is someone_on_line and not Contains(player,receiver)
{
"The phone is ringing."
return true
}
}
return false
}
react_after
{
if (verbroutine = &DoGo, &DoExit, &DoEnter) and location ~= old_location
{
receiver.found_in = location
receiver.attached_to = self
hook.part_of = self
move receiver to hook
}
else
return false
}
}
class phone_number
{
type phone_number
is known
in_scope
{
return player
}
before
{
object DoDial
{
receiver.current_number = self
Perform(&DoDial, receiver)
}
object
{
if verbroutine = &DoLook
return false
print "You can't do that with "; self.name ;"."
}
}
}
!\--------------------------------------------------------------------------
Next come the receiver and hook. Note that the receiver is of the attachable
class. This will prevent the player from moving off to another location
while he's holding it. He'll also get a nice little inventory description
telling him that the receiver he's holding is attached to the telephone,
which impresses the hell out of people from Estonia.
---------------------------------------------------------------------------\!
attachable receiver "telephone receiver"
{
is hidden
found_in 0
current_number 0
adjective "telephone", "phone"
nouns "receiver", "handset"
article "a"
attached_to 0
attached_desc "attached to"
long_desc
{
if not Contains(player,self): "The receiver is on the hook."
else : "The receiver is off the hook."
}
before
{
object DoAnswer, DoGet
{
if Contains(player,self)
"The phone is already off the hook."
elseif receiver is not someone_on_line
{
move receiver to player
receiver is not hidden
if verbroutine = &DoGet
"You pick up the phone."
else
"You pick up the phone, but there's no one on the line."
}
else
{
move receiver to player
receiver is not hidden
run (receiver.current_number).react_answer
}
}
object DoHangup
{
if not Contains(player,self)
"The phone is already hung up."
else
{
move receiver to hook
receiver is hidden
receiver is not someone_on_line
"You hang up the phone."
}
}
object DoDrop
{
Perform(&DoHangup, self)
}
object DoPutIn
{
if xobject.type ~= phone
{
"That shouldn't go there."
}
else
return false
}
}
after
{
object DoListen
{
if not Contains(player,self)
{
if receiver is someone_on_line
"The phone is ringing."
else
"The phone is silent."
}
elseif receiver is someone_on_line
"The other party is speaking to you."
else
"You hear a dial tone."
}
}
}
component hook "phone hook"
{
is open, platform
part_of 0
nouns "hook", "cradle"
article "the"
long_desc
{
if Contains(self,receiver): "There's a receiver on the hook."
else : "The hook is empty."
}
before
{
xobject DoPutIn
{
if object = receiver
Perform(&DoDrop, receiver)
else : "You can't put that on the hook."
}
xobject DoGet
{
if object = receiver
Perform(&DoGet, receiver)
else : "That's not on the hook."
}
object DoMove ! DoPush
{
if not Contains(self, receiver)
{
if receiver is someone_on_line
{
receiver is not someone_on_line
"You press the cradle, breaking the connection."
}
else : "You press the cradle."
}
else : "The receiver is in the way."
}
}
}
routine DoAnswer
{
"Sure. Ok, buddy. Whatever."
}
routine DoDial
{
if not object
"Dial it where?"
elseif (object.type ~= phone,phone_number) and object ~= receiver or
(xobject and xobject.type ~= phone)
"You can't dial that."
elseif receiver.found_in ~= location
"There is no phone here."
else
{
if not Contains(player, receiver)
"You'd better pick up the phone first."
elseif receiver is someone_on_line
"That would be rude."
else
run (receiver.current_number).react_dial
return true
}
return false
}
routine DoHangup
{
if receiver.found_in = location : Perform(&DoHangup, receiver)
elseif not object : "There's nothing here to hang up."
else : "You can't hang that up."
}
#ifset _ROODYLIB_H
object 1st_room_phone_detector
{
in main_instructions
execute
{
local a
a = FindObjectOfType(phone,location)
if a
{
receiver.found_in = location
receiver.attached_to = a
hook.part_of = a
move receiver to hook
}
remove self
}
}
#else
event
{
if counter < 1
{
local a
a = FindObjectOfType(phone,location)
if a
{
receiver.found_in = location
receiver.attached_to = a
hook.part_of = a
move receiver to hook
}
}
}
#endif
#if undefined FindObjectOfType
routine FindObjectOfType(t, loc)
{
local i, obj, suspect
if loc = 0: loc = location
for i in loc
{
if i.type = t
{
if suspect
return nothing
suspect = i
}
elseif children(i) and (i is not container or i is open or i is not openable)
{
obj = FindObjectOfType(t, i)
if obj
{
if suspect
! More than 1
return nothing
else
suspect = obj
}
}
}
! Only do the whole-tree check when loc is a room-level object:
if parent(loc) = nothing and not suspect
{
for (i=1; i<=objects; i++)
{
if i.type = t and i ~= suspect
{
if FindObject(i, location)
{
if suspect
! More than one
return nothing
else
suspect = obj
}
}
}
}
return suspect
}
#endif
phone red_phone "red phone"
{
article "a"
noun "phone"
adjective "red"
in east_room
}
phone black_phone "black phone"
{
article "a"
noun "phone"
adjective "black"
in STARTLOCATION
}
phone_number glorias_number "Gloria's number"
{
is known
in_scope
return player
nouns "555-1212", "5551212", "number"
adjectives "gloria's"
long_desc
"Gloria's number is 555-1212."
react_dial ! what happens when we dial her
"You dial 555-1212."
react_answer ! what happens if we answer the phone when she's calling
{
"You answer the phone. It's your friend, Gloria."
}
}
view raw phoneclass hosted with ❤ by GitHub

Like the original code, there's some example code mixed in there which you'd basically just want to ignore for your own game.  I don't remember everything I did with this and I don't care enough to look, but I think I took some steps to make it more efficient about object and attribute usage and stuff.

Since guns have come up fairly regularly in Robb Sherwin's games, I've been thinking of writing a gun class.  I made a little progress on that but it's complicated enough that I just decided to shelve it for a while.  Besides doing things like making the grammar support "FIRE GUN" and "SHOOT BAD GUY," there's just a bunch of things to implement for a gun class done right (like, checking ammo type and quantity in a multiple-gun-type game and having >RELOAD work appropriately) and making sure SHOOT BAD GUY'S GUN doesn't give you a "You're not holding that." type message.

Anyhow, it's a lot of work considering any game of mine probably wouldn't even have an ammo system, which means I'd also have to code options to turn this or that feature off, and that's even more work (all surmountable, of course, but just annoying enough that I put it off).

No comments:

Post a Comment