intfiction.org

The Interactive Fiction Community Forum
It is currently Mon Sep 25, 2017 1:34 pm

All times are UTC - 6 hours [ DST ]




Post new topic Reply to topic  [ 15 posts ]  Go to page Previous  1, 2
Author Message
PostPosted: Wed Aug 09, 2017 6:08 pm 
Offline
User avatar

Joined: Sat Jun 25, 2016 12:13 pm
Posts: 141
Hi, nice update.

I made some changes in `GetFileNames`, there are some bugfixes, but the usage message is optional but helpful.

Code:
int32_t GetFileNames(nr_args, args, input, output)
 int  nr_args;
 char **args;
 char *input;
 char *output;
{
  int i = 1;
  int ok = 1;

  /* the command 'compiler' itself is stored in argv[0] */

  input[0]  = '\0';
  output[0] = '\0';

  while (i<nr_args) {
    if (strcmp(args[i], "-i") == 0) {
      if (nr_args > i+1) {
        /* inputfile was specified */
        strncpy(input, args[++i], MAX_ID_LENGTH);
      }
    }
    else
      if (strcmp(args[i], "-o") == 0) {
        if (nr_args > i+1) {
          /* outputfile was specified */
          strncpy(output, args[++i], MAX_ID_LENGTH);
        }
      }
      else {
        if (strcmp(args[i], "-d") == 0) {
          /* set debug compile mode */
          /* implement this later   */
        }
        else {
          printf("Unknown commandline option: %s\n\n", args[i]);
          ok = 0;
        }
      }
    ++i;
  }

  if (!ok)
  {
      printf("Usage: %s [-i <inputfile>] [-o <outputfile>] [-d <level>]\n",
             args[0]);
      return ERROR;
  }


Top
 Profile Send private message  
 
PostPosted: Tue Aug 15, 2017 7:29 am 
Offline

Joined: Wed Aug 26, 2015 11:18 am
Posts: 73
Location: The Netherlands
Thanks for pointing that out. I feel really stupid, don know how that could have slipped through :-(


Top
 Profile Send private message  
 
PostPosted: Fri Sep 22, 2017 8:22 am 
Offline

Joined: Wed Aug 26, 2015 11:18 am
Posts: 73
Location: The Netherlands
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: show
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
#-------------------------------------------------------------------------------

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: show
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

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.


Top
 Profile Send private message  
 
PostPosted: Fri Sep 22, 2017 4:28 pm 
Offline
User avatar

Joined: Sat Jun 25, 2016 12:13 pm
Posts: 141
Hi Marnix,

This is a major step forward! Looking now at, say, cloak.xvn it is _just_ the story and all the boilerplate support is neatly tucked away in `include`. Also the annoying voc file has been eliminated.

For people that want to use XVAN, they just need to look at example STORY files and perhaps copy `include` into their projects.

Later, the stuff in `include` will converge to become a standard base lib for all games. Nice one.

So i have some fixes:

## XC\init.c\GetFileNames

Code:
  int i = 1;

  /* the command 'compiler' itself is stored in argv[0] */

  input[0]  = '\0';
  output[0] = '\0';

  while (i<nr_args) {
    if (strcmp(args[i], "-i") == 0) {
      if (nr_args > i+1) {
        /* inputfile was specified */
        strncpy(input, args[++i], MAX_ID_LENGTH);
      }
    }
    else
      if (strcmp(args[i], "-o") == 0) {
        if (nr_args > i+1) {
          /* outputfile was specified */
          strncpy(output, args[++i], MAX_ID_LENGTH);
        }
      }
      else {
        if (strcmp(args[i], "-d") == 0) {
          /* set debug compile mode */
          /* implement this later   */
        }
        else {
          printf("Unknown commandline option: %s\n\n", args[i]);
        }
      }
    ++i;
  }


To see why, try running "xc -fish"

## XVAN runtime; cleanup.c

Code:
void FreeTriggers(start_trigger)
 triggerInfo *start_trigger;
{
  if (start_trigger == NULL)
    return;
  else {
    FreeTriggers(start_trigger->next);
    if (start_trigger->code != NULL)   // <<<<<<---- look here
      free(start_trigger->code);
    free(start_trigger);
  }
}


## other stuff

I haven't forgotten to get back to you re the embedding ideas.

Basically, and this applies to anyone else interested as well, I'm thinking of making a thin JSON interface that can be attached to a back-end engine (eg XVAN) that exposes various necessary concepts for a GUI.

These are things like:

* inventory
* map
* pictures
* sounds
* options
* product info
* settings
* inputs to the back-end that aren't text commands.
etc.

The idea is to take what I've ended up with in Brahman and make that the _start point_ for further expansion.

You might think that inventory is just a list of strings - so did i at first! or perhaps pictures are just a URL (you'd think!). What happens, is quite quickly the information content needs to be expanded;

for example, inventory needs to also tell the UI; things worn, held in hand, inside other things etc. as well as fancy display labels and styles.

The idea is to start by defining a schema for the information and then move on to building a small library to implement it.

Underneath that, considering XVAN for example, it will then need to fill in the JSON structures. For example, i had a look at how XVAN might implement player inventory;

There appears to be utils to find things "owned" by other things. However, is the "player" a special object in the back-end, or just a general object? Could there be a util to find the list of things owned by the "player". From that we could build the inventory JSON. at least as a start. Then we'd need to look into how each such object is related to the player (eg worn, held etc.).

I need to read the code a bit more however...


Top
 Profile Send private message  
 
PostPosted: Mon Sep 25, 2017 8:31 am 
Offline

Joined: Wed Aug 26, 2015 11:18 am
Posts: 73
Location: The Netherlands
Hi jkj yuio,

Thank you for your comments (how did you spot that semicolon???).

XVAN has an Output() function, that can:
    - print text to the console window (printf() )
    - send text to a Glk library function, for printing in a Glk window
    - write text to a file (when transcript mode is on)

I think I can add JSON options for output: CreateJSON(), ExpandJSON(), ...... Starting the interpreter with a -e option would then invoke JSON mode.

Now about the inventory (I assume you mean a special command issued by the GUI to retrieve the player's inventory, not the regular 'i' that the user would type from the command line):

Regarding the player object, it's a normal object but it is mandatory because the interpreter refers to it in some cases (for example to update the current location variable).

The interpreter maintains 2 arrays of type dirInfo, called loc_dir and obj_dir. Each location and object has an entry in one of the arrays, indexed by their id. A dirInfo struct has a member called contained_objs, that holds all the contained objects. So, if you look up the player object in obj_dir, you can retrieve the contained_objs struct. But.... I think there is a better way.

Let me elaborate a bit about how the 'inventory' user command (or any other user input) from the command line is executed, as this is also important for the special inventory command.

After the user input is parsed, the interpreter will create an array of object/location ids who must respond to the user input. Simplified: it looks up the current location in loc_dir, gets the contained_objs struct and for each contained object it looks up the contained objects etc etc. In the end, an array of object ids will result.
Next, the parsed user input is offered to each object in the array to let it respond (or decide not to).

So, how does this work for "inventory" ?

First, the interpreter will query the inventory verb whether it has a prologue. The prologue, if present, is always the first thing to execute.
Inventory has a prologue and it will print the string "You are carrying:"

Next, the user input is offered to all objects in the array I described earlier. Each object will check if it must do something. Basically, it will check whether it is held by the player and if so, it will print the text "an <object name>". Objects know about special states; the cloak knows it can be worn and will check for it. If worn it will add "(being worn)" to the text. Likewise, a lamp could add "(providing light)" to the response.

After all objects have had their turn, the interpreter will check if anyone responded. If not, the inventory verb default will be executed, which will print "nothing, you are empty-handed."

My point is that many things are handled at the object level. The interpreter does not know about worn, lit, opened, closed, whether an object is in/on/under/... another object etc. It can only parse user input and give it to the objects for processing.

If we want a special inventory command that can be issued by the GUI so it can display the player's inventory in a special window, I think we come to your last bullet (inputs to the back-end that aren't text commands). Info must still come from the objects, but it should be in a different type of JSON structs, so the GUI knows it must not be printed as a normal reply.

We could even consider to expand the functionality of the Inventory command, so it can also return the inventory from a location. This could be helpful when drawing the map: items that are in the locations and already discoverd by the player could be depicted in the map.


Top
 Profile Send private message  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 15 posts ]  Go to page Previous  1, 2

All times are UTC - 6 hours [ DST ]


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group