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 DoDialverb "answer"* DoVague* object DoAnswerverb "hang"* "up" DoHangup* "up" object DoHangup
And then here is the rest of the code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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." | |
} | |
} |
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