I made a next version. The vocabulary file is no longer a mandatory separate file, there is now one story file that may include other files. But the biggest change is that there is no longer a prescribed layout for the story file.
In this new version, there are still sections for:
- common flags
- common descriptions
- common triggers
- common attributes
- locations
- objects
- timers
- verbs, nouns, adjective, …
But now different sections can be in arbitrary order and each section may occur multiple times in the story file and its included files. When the compiler must compile a specific section it will just browse through all the files and compile all occurences it finds of this specific section. To implement this, I had to prefix the section keywords with $ so the compiler can tell them from dictionary words.
So now you can isolate different section types in one file to create libraries.
Example: file status_window.lib contains object and timer to make a status window for the Glk version.
[spoiler][code]#============================================
Glk Status Window
#============================================
#-------------------------------------------------------------------------------
$OBJECT o_status_window
DESCRIPTIONS
d_nodescr “”
CONTAINED in o_player
FLAGS
f_hidden = 1
TRIGGERS
“restore” -> t_restore
t_entrance
agree()
t_exit
agree()
t_update
# updates the status window (only in Glk version)
# triggered by m_statuswindow timer
clearstatus()
setcursor(0,2)
if islit(l_location) then
printstatus(l_location)
else
printstatus(“Darkness”)
endif
t_restore
restore()
# timers are not called after save and restore
if trigger(t_update) then
agree()
END_OBJ
#-------------------------------------------------------------------------------
$TIMERS
m_status_window
init 0
step 0
direction up
interval 1
state go
trigger_at 0
execute o_status_window.t_update
#-------------------------------------------------------------------------------[/code][/spoiler]
If you want a status window, just add $insert “status_window.lib” somewhere in the story file and you get a status window.
I refactored the sample stories. This is what Cloak of Darkness looks like now.
[spoiler][code]#============================================
XVAN implementation of “Cloak of Darkness”
#============================================
TITLE “Cloak of Darkness”
VERSION “1.0”
for Linux, replace \ in pathnames with //
$insert “.\includes\dictionary.lib”
$insert “.\includes\actions.lib”
$insert “.\includes\commons.lib”
$insert “.\includes\common_triggers.lib”
$insert “.\includes\nosuchthing.xvn”
$insert “.\includes\status_window.lib”
#===========================
Locations part
#===========================
#-------------------------------------------------------------------------------
$LOCATION l_foyer
DESCRIPTIONS
d_sys “the foyer hall”, “the foyer”, “a spacious hall”, “the opera house”
d_entr_long “You are standing in a spacious hall, splendidly decorated in red /
and gold, with glittering chandeliers overhead. The entrance from /
the street is to the north, and there are doorways south and west.”
d_entr_short “Foyer of the Opera House”
d_no_go “You’ve only just arrived, and besides, the wheather outside seems /
to be getting worse…”
EXITS
south -> l_bar
w -> l_cloakroom
FLAGS
f_lit = 1
TRIGGERS
“look” -> t_look
“examine [l_foyer]” -> t_look
“go north” -> t_no_go
“go to north” -> t_no_go
t_no_go
printcr(d_no_go)
disagree()
END_LOC
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
$LOCATION l_cloakroom
DESCRIPTIONS
d_sys “the cloakroom”, “the wall”, “the walls”
d_entr_long “The walls of this room were clearly once lined with hooks, /
though now only one remains. The exit is a door to the east.”
d_entr_short “Cloakroom”
EXITS
e -> l_foyer
FLAGS
f_lit = 1
TRIGGERS
“look” -> t_look
“examine [l_cloakroom]” -> t_look
END_LOC
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
$LOCATION l_bar
DESCRIPTIONS
d_sys “the foyer bar”
d_entr_long “The bar, much rougher than you’d have guessed after the opulence /
of the foyer to the north, is completely empty. There seems to /
be some sort of message scrawled in the sawdust on the floor.”
d_entr_short “Foyer bar”
EXITS
n -> l_foyer
FLAGS
f_bypass = 1
TRIGGERS
“look” -> t_look
“x [l_bar]” -> t_look
“[dir]” -> t_move
t_move
if not(islit(%this)) then
if not(equal(%dir, north)) then
o_message.r_damage += 2
printcr(“Blundering around in the dark isn’t a good idea!”)
endif
else
nomatch()
t_default
if not(islit(%this)) then
o_message.r_damage += 1
printcr(“In the dark? You could easily disturb something!”)
disagree()
else
nomatch()
END_LOC
#-------------------------------------------------------------------------------
#======================
OBJECTS part
#======================
#-------------------------------------------------------------------------------
$OBJECT o_player
The o_player object is predefined and represents the human player.
DESCRIPTIONS
d_sys “You”
d_init "Hurrying through the rainswept November night, you’re glad to see the /
bright lights of the Opera House. It’s surprising that there aren’t /
more people about but, hey, what do you expect in a cheap demo game…?
/ Cloak of Darkness
/ A basic IF demonstration
/ "
CONTAINED in l_foyer
ATTRIBUTES
r_is = are
r_have = have
r_score = 0
r_max_score = 2
TRIGGERS
“[dir]” -> t_move # if the player enters a (compass) direction, execute trigger t_move
t_init
# This trigger initializes things. It is started by counter
# m_init that goes off right after starting the story.
background(blue)
printcr(d_init)
printcr("")
entrance(owner(%this))
t_entrance
agree() # prevents calling common t_entrance trigger for player.
t_move
if valdir(l_location, %dir) then
# it’s a valid direction
if exit(l_location) then
# no object objects to the player leaving the room
move(o_player, %dir) # move updates current location
entrance(l_location)
endif
else
nomatch() # let other objects or verb default code react.
endif
agree()
t_end
printcr("") printcr("")
printcr(“Your score is [r_score] out of a possible /
[r_max_score], in [m_init] moves.”)
quit()
END_OBJ
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
$OBJECT o_cloak
DESCRIPTIONS
d_sys “a black velvet cloak”
d_entr_short “There is [a] [o_cloak] here.”
d_exa “A handsome cloak, of velvet trimmed with satin, and slightly /
spattered with raindrops. Its blackness is so deep that it /
almost seems to suck the light from the room.”
d_drop “This isn’t the best place to leave a smart cloak lying around.”
CONTAINED in o_player
FLAGS
f_bypass = 1 # exclude from interpreter’s visibility check
f_takeable = 1
f_wearable = 1
f_worn = 1 # initially, the player is wearing the cloak
TRIGGERS
“take off [o_cloak]” -> t_take_off_cloak
“wear [o_cloak]” -> t_wear
“drop the [o_cloak]” -> t_drop
“hang the [o_cloak] on the [o_hook]” -> t_hang
“examine the [o_cloak]” -> t_exa
“look” -> t_look
“inventory” -> t_i
t_take_off_cloak
if testflag(f_worn) then
printcr("")
printcr(" [[Taking off [the] [o_cloak]]")
clearflag(f_worn)
endif
t_wear
if not(owns(o_player, o_cloak)) then
printcr(" [[picking up the cloak first]")
move(o_cloak, o_player)
endif
printcr(“Ok, you are now wearing [the] [o_cloak].”)
setflag(o_cloak.f_worn)
clearflag(l_bar.f_lit)
t_drop
if equal(l_location, l_cloakroom) then
if trigger(t_take_off_cloak) then
printcr(“You drop [the] [o_cloak] on the floor.”)
move(o_cloak, l_cloakroom)
setflag(l_bar.f_lit)
endif
else
printcr(d_drop)
endif
t_hang
if trigger(t_take_off_cloak) then
move(o_cloak, o_hook)
setflag(l_bar.f_lit)
o_cloak.r_preposition = on
printcr(“Ok, the cloak is now on the hook.”)
o_player.r_score += 1
printcr("")
printcr(" [[Your score has just gone up by 1 point]")
t_i
if not(owns(o_player, %this)) then
nomatch()
else
indent()
print("[a] [o_cloak]")
if testflag(f_worn) then
printcr(", being worn")
else
printcr("")
endif
agree()
endif
END_OBJ
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
$OBJECT o_hook
DESCRIPTIONS
d_sys “the small brass hook”
d_entr_short “There is [a] [o_hook] on one of the walls.”
CONTAINED in l_cloakroom
TRIGGERS
“look” -> t_look
“examine [o_hook]” -> t_exa
“take [o_hook]” -> t_take
t_exa
print("It’s just [a] [o_hook], ")
if owns(o_hook, o_cloak) then
printcr(“with [a] [o_cloak] hanging on it.”)
else
printcr(“screwed to the wall.”)
endif
t_take
printcr("[the] [o_hook] is firmly attached to the wall.")
END_OBJ
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
$OBJECT o_message
DESCRIPTIONS
d_sys “the scrawled message”
d_entr_short “There seems to be some sort of message scrawled in the sawdust on the floor.”
d_exa_won "The message, neatly marked in the sawdust, reads …
*** You have won ***"
d_exa_lost "The message has been carelessly trampled, making it difficult to read. You /
can just distinguish the words …
*** You have lost ***"
CONTAINED in o_sawdust
ATTRIBUTES
r_damage = 0
TRIGGERS
“examine [o_message]” -> t_exa
“read [o_message]” -> t_exa
“read [o_message] in [o_sawdust]” -> t_exa
t_exa
if lt(r_damage, 2) then
o_player.r_score += 1
printcr(d_exa_won)
else
printcr(d_exa_lost)
endif
if trigger(o_player.t_end) then
printcr("")
endif
END_OBJ
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
$OBJECT o_sawdust
DESCRIPTIONS
d_sys “the sawdust”, “the floor”
d_entr_short “”
d_take “That doesn’t sound like a good idea. It would /
scatter the message…”
d_exa “There is a message written in the sawdust.”
CONTAINED in l_bar
FLAGS
f_takeable = 1
TRIGGERS
“take [o_sawdust]” -> t_take
“x [o_sawdust]” -> t_exa
t_entrance
agree()
t_take
printcr(d_take)
t_exa
printcr(d_exa)
END_OBJ
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
$OBJECT O_foyer_scenery
Descriptions
d_sys “the red decoration”, “the gold decoration”, “the glittering chandeliers”
d_scenery_1 “You examine [the] [o_foyer_scenery] and on closer look decide that you /
don’t need to bother about it to complete this story.”
d_scenery_2 “Just looks like [o_foyer_scenery].”
d_scenery_3 “Beautiful [o_foyer_scenery], very contemporary.”
CONTAINED in l_foyer
FLAGS
f_hidden = 1
f_bypass = 1
f_swap = 1
ATTRIBUTES
r_random = 1
TRIGGERS
“examine [o_foyer_scenery]” -> t_exa
t_entrance
agree()
t_exa
r_random = rnd(1,3)
if equal(r_random, 1) then
printcr(d_scenery_1)
else
if equal(r_random, 2) then
printcr(d_scenery_2)
else
printcr(d_scenery_3)
endif
endif
agree()
END_OBJ
#-------------------------------------------------------------------------------
#===========
Timer part
#===========
$TIMERS
Timer m_init is used to start the game and keep track
of the number of moves
m_init
init 0
step 1
direction up
interval 1
state go
trigger_at 1
execute o_player.t_init[/code][/spoiler]
The new version 2.3 can be found here.
Next thing I want to work on is the embedding that jkj yuio suggested, so XVAN can serve as an engine to external (G)UIs.