commit cc9bbf6125fec1aaa9a7a1a06574a537fc3c3b29 from: Omar Polo date: Wed Apr 01 20:16:27 2020 UTC initial commit commit - /dev/null commit + cc9bbf6125fec1aaa9a7a1a06574a537fc3c3b29 blob - /dev/null blob + 29cd95d90f161b219410c6181daa459ca15e440f (mode 644) --- /dev/null +++ .gitignore @@ -0,0 +1,5 @@ +*.o +*.core +object.h +object.c +adventure blob - /dev/null blob + 326004787dedf08975694a156caae88ed67e0459 (mode 644) --- /dev/null +++ Makefile @@ -0,0 +1,33 @@ +CC = cc +CFLAGS = -Wall -g -O0 +LDFLAGS = -lreadline + +.PHONY: all clean + +all: adventure + +OBJ = match.o parseexec.o toggle.c inventory.o \ + misc.o object.o io.o adventure.o + +adventure: object.h ${OBJ} + ${CC} -o adventure ${OBJ} ${LDFLAGS} + +object.c: object.data data_to_c.awk + awk -v pass=c1 -f data_to_c.awk object.data > object.c + awk -v pass=c2 -f data_to_c.awk object.data >> object.c + +object.h: object.data data_to_c.awk + awk -v pass=h -f data_to_c.awk object.data > object.h + +.SUFFIXES: .c .o +.c.o: + ${CC} ${CFLAGS} -c $< -o $@ + +clean: + rm -f *.o adventure object.c object.h map.gv map.png + +map.gv: map.awk object.data + awk -f map.awk object.data > $@ + +map.png: map.gv + dot -Tpng -o $@ map.gv blob - /dev/null blob + ff8b428ff5b02713dd5ed192c3c6ce852891f83e (mode 644) --- /dev/null +++ README.md @@ -0,0 +1,44 @@ +a text adventure +================= + +"a text adventure" is a text adventure based on the (unfortunately) +unfinished tutorial: +http://home.hccnet.nl/r.helderman/adventures/htpataic01.html + +The code is more or less the same that you'll find on the tutorial, +except for the stile and some minor difference in the structure. + + +building +-------- + + make + +It requires awk(1), a C compiler and the GNU readline library. + +It builds on OpenBSD without external packages installed btw. + + +grammar +------- + +While you can look for the grammar in `parseexec.c:/^parseexec`, I +would ask you not to do so: you may read spoilers. + +Instead, try to express what you want to accomplish in plain english, +it should work. (note: no punctuation or conjunctions are supported +as of now) + +Some examples + +> quit + +> look around + +> look guard + +> go to east + +> pick up the coin + +> ... blob - /dev/null blob + dd9c4e796cfe63fa782d616578cb3033971b34cd (mode 644) --- /dev/null +++ adventure.c @@ -0,0 +1,19 @@ +#include +#include +#include + +#include "adventure.h" + +int +main() +{ + printf("Welcome to Little Cave Adventure.\n"); + + exec_look_around(); + + while (getinput() && parseexec(line)) + ; + + printf("\nBye!\n"); + return 0; +} blob - /dev/null blob + a90e0d0ed0c4c829142d3bd19c00b6e035f6dfee (mode 644) --- /dev/null +++ adventure.h @@ -0,0 +1,76 @@ +#ifndef ADVENTURE_H +#define ADVENTURE_H + +#include + +enum distance { + dist_player, + dist_held, + dist_held_contained, + dist_location, + dist_here, + dist_here_contained, + dist_overthere, + dist_not_here, + dist_unknown_obj, + dist_no_obj_specified, +}; + +/* object.c */ +#include "object.h" + +/* match.c */ +#define MAX_PARAMS 26 +struct param { + const char *tag; + struct object *object; + enum distance distance; + size_t count; +}; +extern struct param params[MAX_PARAMS]; +#define param_by_letter(l) (params + (l) - 'A') +int match_command(const char*, const char*); + +/* parseexec.h */ +struct command { + int (*fn)(void); + const char *pattern; +}; +int exec_look_around(void); +int parseexec(const char*); + +/* misc.c */ +size_t list_objs_at_loc(struct object*); +struct object *person_here(void); +struct object *get_passage_to(struct object*); +enum distance distance_to(struct object*); +void move_player(struct object*); +int weight_of_contents(struct object*); +int object_within_reach(const char*, struct param*); + +/* io.c */ +extern char *line; +int getinput(); + +/* inventory.c */ +int move_object(struct param*, struct object *from, struct object *to); + +/* toggle.c */ +const char *cannot_be_opened(struct object*); +const char *cannot_be_closed(struct object*); +const char *cannot_be_locked(struct object*); +const char *cannot_be_unlocked(struct object*); + +const char *is_already_open(struct object*); +const char *is_already_closed(struct object*); +const char *is_already_locked(struct object*); +const char *is_already_unlocked(struct object*); + +const char *is_still_open(struct object*); +const char *is_still_locked(struct object*); + +const char *toggle_backdoor(struct object*); +const char *toggle_box(struct object*); +const char *toggle_box_lock(struct object*); + +#endif blob - /dev/null blob + 37001a87d2710754ab4376a189c2b3ae7e52d575 (mode 644) --- /dev/null +++ data_to_c.awk @@ -0,0 +1,104 @@ +BEGIN { + count = 0; + obj = ""; + if (pass == "h") { + print "#ifndef OBJECT_H"; + print "#define OBJECT_H"; + print ""; + } + if (pass == "c1") { + print "#include \"adventure.h\"\n"; + print ""; + print "static int always_true(struct object *o) { return 1; }"; + print ""; + } + if (pass == "c2") { + print "\nstruct object objs[] = {"; + } +} + +/^- / { + output_record(","); + obj = $2; + prop["condition"] = "always_true"; + prop["description"] = "NULL"; + prop["tags"] = ""; + prop["location"] = "NULL"; + prop["destination"] = "NULL"; + prop["prospect"] = ""; + prop["details"] = "\"You see nothing special.\""; + prop["contents"] = "\"You see\""; + prop["text_go"] = "\"You can't get any closer than this.\""; + prop["weight"] = "99"; + prop["capacity"] = "9999"; + prop["health"] = "0"; + prop["open"] = "cannot_be_opened"; + prop["close"] = "cannot_be_closed"; + prop["lock"] = "cannot_be_locked"; + prop["unlock"] = "cannot_be_unlocked"; +} + +obj && /^[ \t]+[a-z]/ { + name = $1; + $1 = ""; + if (name in prop) { + prop[name] = $0; + if (/^[ \t]*\{/) { + prop[name] = name count; + if (pass == "c1") + print "static int " prop[name] "(struct object *obj) " $0; + } + } else if (pass == "c2") { + print "#error \"" FILENAME " line " NR ": unknown attribute '" name "'\""; + } +} + +!obj && pass == (/^#include/ ? "c1" : "h") { + print; +} + +END { + output_record("\n};"); + if (pass == "h") { + print ""; + print "#define obj_end\t(objs + " count ")"; + print "#define valid_obj(obj)\t" \ + "((obj) != NULL && (*(obj)->condition)((obj)))"; + print "#define foreach_obj(obj)\t" \ + "for (obj = objs; obj < obj_end; ++obj) if (valid_obj(obj))"; + print ""; + print "#endif"; + } +} + +function output_record(separator) { + if (obj) { + if (pass == "h") { + print "#define " obj "\t(objs + " count ")"; + } else if (pass == "c1") { + print "static const char *tags" count "[] = {" prop["tags"] ", NULL};"; + } else if (pass == "c2") { + print "\t{\t/* " count " = " obj " */"; + print "\t\t" prop["condition"] ","; + print "\t\t" prop["description"] ","; + print "\t\ttags" count ","; + print "\t\t" prop["location"] ","; + print "\t\t" prop["destination"] ","; + print "\t\t" prop[prop["prospect"] ? "prospect" : "destination"] ","; + print "\t\t" prop["details"] ","; + print "\t\t" prop["contents"] ","; + print "\t\t" prop["text_go"] ","; + print "\t\t" prop["weight"] ","; + print "\t\t" prop["capacity"] ","; + print "\t\t" prop["health"] ","; + print "\t\t" prop["open"] ","; + print "\t\t" prop["close"] ","; + print "\t\t" prop["lock"] ","; + print "\t\t" prop["unlock"] ","; + print "\t}" separator; + delete prop; + } + + count++; + } +} blob - /dev/null blob + 77a11f7e7236ff90b70630acebcebb3bb20193bb (mode 644) --- /dev/null +++ inventory.c @@ -0,0 +1,64 @@ +#include +#include + +#include "adventure.h" + +int +move_object(struct param *par, struct object *from, struct object *to) +{ + struct object *obj = par->object; + enum distance dist = par->distance; + + if (obj == NULL || dist == dist_unknown_obj || dist == dist_not_here) + printf("I don't understand what item you mean.\n"); + else if (to == NULL) + printf("There is nobody here to give that to.\n"); + else if (obj == to) + printf("What's the meaning of putting a %s inside itself?\n", obj->tags[0]); + else if (from != obj->location) { + /* give the appropriate error message */ + switch (dist) { + case dist_player: + printf("You should not be doing that to yourself.\n"); + break; + + case dist_held: + printf("You already have %s.\n", obj->description); + break; + + case dist_location: + case dist_overthere: + printf("That's not an item.\n"); + break; + + case dist_here: + if (from == player) + printf("You have no %s.\n", par->tag); + else + printf("Sorry, %s has no %s.\n", + from->description, + par->tag); + break; + + case dist_held_contained: + case dist_here_contained: + printf("Sorry, %s is holding it.\n", + obj->location->description); + break; + + default: + /* we should have handled all other cases + * before this point */ + abort(); + } + } else if (obj->weight > to->capacity) { + printf("That is way too heavy.\n"); + } else if (obj->weight + weight_of_contents(to) > to->capacity) { + printf("That would becamo too heavy.\n"); + } else { + obj->location = to; + printf("OK.\n"); + } + + return 1; +} blob - /dev/null blob + f66b92f00ef5d3a6a19907bf40e89aa58af648d4 (mode 644) --- /dev/null +++ io.c @@ -0,0 +1,19 @@ +#include "adventure.h" + +/* this in only needed for readline */ +#include + +#include +#include + +char *line = NULL; + +int +getinput() +{ + line = readline("~> "); + if (line && *line) + add_history(line); + return line != NULL; +} + blob - /dev/null blob + 9a68b70c497797bb7dfabab1f996de78484735d2 (mode 644) --- /dev/null +++ map.awk @@ -0,0 +1,13 @@ +BEGIN { print "digraph map {"; } +/^. / { output_edge(); location = destination = ""; } +$1 == "location" { location = $2; } +$1 == "destination" { destination = $2; } +$1 == "prospect" { prospect = $2; } +END { output_edge(); print "}"; } + +function output_edge() { + if (location && destination) + print "\t" location " -> " destination; + if (location && prospect) + print "\t" location " -> " prospect " [style=dashed]"; +} blob - /dev/null blob + 0da6cc66ddcfc9ad148e6596be1aea80aef3b4d7 (mode 644) --- /dev/null +++ match.c @@ -0,0 +1,103 @@ +#include "adventure.h" + +#include +#include + +struct param params[MAX_PARAMS]; + +static const char * +skip_spaces(const char *src) +{ + while (isspace(*src)) + src++; + return src; +} + +static const char * +match_spaces(const char *src) +{ + return *src == '\0' || isspace(*src) ? skip_spaces(src) : NULL; +} + +static const char * +match_terminal(const char *src, char terminal) +{ + return terminal == ' ' + ? match_spaces(src) + : tolower(*src) == tolower(terminal) + ? src + 1 + : NULL; +} + +static const char * +match_tag(const char *src, const char *tag) +{ + while (src != NULL && *tag != '\0') + src = match_terminal(src, *tag++); + return src; +} + +static int +compare_with_param(const char *tag, enum distance dist, struct param *par) +{ + int diff = strlen(tag) - strlen(par->tag); + if (diff == 0) + diff = par->distance - dist; + if (diff == 0) + par->count++; + return diff; +} + +static const char * +match_param(const char *src, struct param *par, int loose) +{ + struct object *obj; + const char *rest_of_src = loose ? src + strlen(src) : NULL; + + par->tag = src; + par->distance = *src == '\0' ? dist_no_obj_specified : dist_unknown_obj; + + foreach_obj (obj) { + const char **tag; + enum distance dist = distance_to(obj); + for (tag = obj->tags; *tag != NULL; ++tag) { + const char *behind_match = match_tag(src, *tag); + if (behind_match != NULL && + compare_with_param(*tag, dist, par) > 0 && + (!loose || *skip_spaces(behind_match) == '\0')) { + par->tag = *tag; + par->object = obj; + par->distance = dist; + par->count = 1; + rest_of_src = behind_match; + } + } + } + + return rest_of_src; +} + +int +match_command(const char *src, const char *pattern) +{ + struct param *par; + + /* clean params */ + for (par = params; par < params + MAX_PARAMS; ++par) { + par->object = NULL; + par->distance = dist_no_obj_specified; + par->count = 0; + } + + /* actual parsing */ + for (src = skip_spaces(src); src != NULL && *pattern != '\0'; ++pattern) { + src = isupper(*pattern) + ? match_param(src, param_by_letter(*pattern), + pattern[1] == '?') + : *pattern == '?' + ? src + : match_terminal(src, *pattern); + } + + return src != NULL && *skip_spaces(src) == '\0'; +} blob - /dev/null blob + e00c37883127490644c8326a178aacfdc73c2646 (mode 644) --- /dev/null +++ misc.c @@ -0,0 +1,109 @@ +#include +#include + +#include "adventure.h" + +size_t +list_objs_at_loc(struct object *location) +{ + size_t count = 0; + struct object *obj; + + foreach_obj (obj) { + if (obj != player && obj->location == location) { + if (count++ == 0) + printf("%s:\n", location->contents); + printf("%s\n", obj->description); + } + } + + return count; +} + +struct object * +person_here(void) +{ + struct object *obj; + + foreach_obj (obj) { + if (distance_to(obj) == dist_here && obj->health > 0) + return obj; + } + + return NULL; +} + +struct object * +get_passage_to(struct object *target) +{ + struct object *obj; + + foreach_obj (obj) { + if (obj->location == player->location && + obj->prospect == target) + return obj; + } + + return NULL; +} + +enum distance +distance_to(struct object *obj) +{ + return + !valid_obj(obj) ? dist_unknown_obj : + obj == player ? dist_player : + obj == player->location ? dist_location : + obj->location == player ? dist_held : + obj->location == player->location ? dist_here : + get_passage_to(obj) != NULL ? dist_overthere : + !valid_obj(obj->location) ? dist_not_here : + obj->location->location == player ? dist_held_contained : + obj->location->location == player->location ? dist_here_contained : + dist_not_here; +} + +void +move_player(struct object *passage) +{ + printf("%s\n", passage->text_go); + if (passage->destination != NULL) { + player->location = passage->destination; + printf("\n"); + exec_look_around(); + } +} + +int +weight_of_contents(struct object *container) +{ + int sum = 0; + struct object *obj; + + foreach_obj (obj) { + if (obj->location == container) + sum += obj->weight; + } + return sum; +} + +int +object_within_reach(const char *verb, struct param *par) +{ + int ok = 0; + + enum distance dist = par->distance; + + if (dist > dist_not_here) + printf("I don't understand what you want to %s.\n", verb); + else if (dist == dist_not_here) + printf("You don't see any %s here.\n", par->tag); + else if (dist >= dist_here_contained) + printf("That is out of reach.\n"); + else if (par->count > 1) + printf("Multiple choices to %s; be more specific.\n", verb); + else + ok = 1; + + return ok; +} blob - /dev/null blob + 31f20e809ccc1ae773db23c1c049a64bfffb3908 (mode 644) --- /dev/null +++ object.data @@ -0,0 +1,203 @@ +#include + +struct object { + int (*condition)(struct object*); + const char *description; + const char **tags; + struct object *location; + struct object *destination; + struct object *prospect; + const char *details; + const char *contents; + const char *text_go; + int weight; + int capacity; + int health; + const char *(*open)(struct object*); + const char *(*close)(struct object*); + const char *(*lock)(struct object*); + const char *(*unlock)(struct object*); +}; + +extern struct object objs[]; + +- field + description "an open field" + tags "field" + details "The filed is a nice and quiet place under a clear blue sky." + +- cave + description "a little cave" + tags "cave" + details "The cave is just a cold, damp, rocky chamber." + +- silver + description "a silver coin" + tags "silver", "coin", "sirver coin" + location field + details "The coin has an eagle on the obverse." + weight 1 + +- gold + description "a gold coin" + tags "gold", "coin", "gold coin" + location cave + details "The shiny coin seems to be a rare and priceless artefact." + weight 1 + +- guard + description "a burly guard" + tags "guard", "burly guard" + location field + details "The guard is a really big fellow." + contents "He has" + health 100 + capacity 20 + +- player + description "yourself" + tags "me" + location field + details "You would need a mirror to look at yourself." + contents "You have" + health 100 + capacity 20 + +- into_cave + condition { return guard->health == 0 || silver->location == guard; } + description "a cave entrance to the east" + tags "east", "entrance" + location field + destination cave + details "The entrance is just a narrow opening in a small outcrop." + text_go "You walk into the cave." + open is_already_open + +- into_cave_blocked + condition { return guard->health > 0 && silver->location != guard; } + description "a cave entrance to the east" + tags "east", "entrance" + location field + prospect cave + details "The entrance is just a narrow opening in a small outcrop." + text_go "The guard stops you from walking into the cave." + open is_already_open + +- exit_cave + description "a way out to the west" + tags "west", "out" + location cave + destination field + details "Sunlight pours in through an opening in the cave's wall." + text_go "You walk out of the cave." + open is_already_open + +- wall_field + description "dense forest all around" + tags "west", "north", "south", "forest" + location field + details "The field is surrounded by trees and undergrowth." + text_go "Dense forest is blocking the way." + +- wall_cave + description "solid rock all around" + tags "east", "north", "rock" + location cave + details "carved in stone is a secret password 'abccb'." + text_go "Solid rock is blocking the way." + +- backroom + description "a backroom" + tags "backroom" + details "The room is dusty and messy." + +- wall_backroom + description "solid rock all around" + tags "east", "west", "south", "rock" + location backroom + details "Trendy wallpaper covers the rock walls." + text_go "Solid rock is blocking the way." + +- open_door_to_backroom + description "an open door to the south" + tags "south", "door", "doorway" + destination backroom + details "The door is open." + text_go "You walk through the door into the backroom." + open is_already_open + close toggle_backdoor + +- closed_door_to_backroom + description "a closed door to the south" + tags "south", "door", "doorway" + location cave + prospect backroom + details "The door is closed." + text_go "The door is closed." + open toggle_backdoor + close is_already_closed + +- open_door_to_cave + description "an open door to the north" + tags "north", "door", "doorway" + destination cave + details "The door is open" + text_go "You walk through the door into the cave." + open is_already_open + close toggle_backdoor + +- closed_door_to_cave + description "a closed door to the north" + tags "north", "door", "doorway" + location backroom + prospect cave + details "The door is closed." + text_go "The door is closed." + open toggle_backdoor + close is_already_closed + +- open_box + description "a wooden box" + tags "box", "wooden box" + details "The box is open." + weight 5 + capacity 10 + open is_already_open + close toggle_box + lock is_still_open + unlock is_already_open + +- closed_box + description "a wooden box" + tags "box", "wooden box" + details "The box is closed." + weight 5 + open toggle_box + close is_already_closed + lock toggle_box_lock + unlock is_already_unlocked + +- locked_box + description "a wooden box" + tags "box", "wooden box" + location backroom + details "The box is closed." + weight 5 + open is_still_locked + close is_already_closed + lock is_already_locked + unlock toggle_box_lock + +- key_for_box + description "a tiny key" + tags "key", "tiny key" + location cave + details "The key is really small and shiny." + weight 1 + +- knife + description "a small, rusty knife" + tags "knife", "rusty knife", "small knife" + location open_box + details "This knife has surely seen better times." + weight 1 blob - /dev/null blob + a85a81d1d2f614c61f3e32515811ffecd7c5ef12 (mode 644) --- /dev/null +++ parseexec.c @@ -0,0 +1,194 @@ +#include "adventure.h" + +#include + +int +exec_quit(void) +{ + return 0; +} + +int +exec_no_match(void) +{ + struct param *par = param_by_letter('A'); + + if (par->distance != dist_no_obj_specified) + printf("I don't know how to %s.\n", par->tag); + return 1; +} + +int +exec_look_around(void) +{ + printf("You are in %s.\n", player->location->description); + list_objs_at_loc(player->location); + return 1; +} + +int +exec_look(void) +{ + struct param *par = param_by_letter('A'); + struct object *obj = par->object; + enum distance dist = par->distance; + + if (dist >= dist_unknown_obj) + printf("I don't understand what you want to see.\n"); + else if (dist == dist_not_here) + printf("You don't see any %s here.\n", par->tag); + else if (dist == dist_overthere) + printf("You squeeze your eyes, but %s is too far away.\n", par->tag); + else if (dist == dist_here_contained) + printf("Hard to see, try to get it first.\n"); + else { + printf("%s\n", obj->details); + list_objs_at_loc(obj); + } + + return 1; +} + +int +exec_go(void) +{ + struct param *par = param_by_letter('A'); + struct object *obj = par->object; + enum distance dist = par->distance; + + if (dist >= dist_unknown_obj) + printf("I don't understand where you want to go.\n"); + else if (dist == dist_location) + printf("You are already there.\n"); + else if (dist == dist_overthere) + move_player(get_passage_to(obj)); + else if (dist == dist_here) + move_player(obj); + else if (dist < dist_not_here) + printf("You can't get any closer than this.\n"); + else + printf("You don't see any %s here.\n", "XXX"); + + return 1; +} + +int +exec_get_from(void) +{ + return move_object(param_by_letter('A'), param_by_letter('B')->object, player); +} + +int +exec_get(void) +{ + return move_object(param_by_letter('A'), player->location, player); +} + +int +exec_drop(void) +{ + return move_object(param_by_letter('A'), player, player->location); +} + +int +exec_give(void) +{ + return move_object(param_by_letter('A'), player, person_here()); +} + +int +exec_ask(void) +{ + return move_object(param_by_letter('A'), person_here(), player); +} + +int +exec_put_in(void) +{ + return move_object(param_by_letter('A'), player, param_by_letter('B')->object); +} + +int +exec_inventory(void) +{ + if (list_objs_at_loc(player) == 0) + printf("You are empty-handed.\n"); + return 1; +} + +int +exec_open(void) +{ + struct param *par = param_by_letter('A'); + if (object_within_reach("open", par)) + printf("%s\n", (par->object->open)(par->object)); + return 1; +} + +int +exec_close(void) +{ + struct param *par = param_by_letter('A'); + if (object_within_reach("close", par)) + printf("%s\n", (par->object->close)(par->object)); + return 1; +} + +int +exec_lock(void) +{ + struct param *par = param_by_letter('A'); + if (object_within_reach("lock", par)) + printf("%s\n", (par->object->lock)(par->object)); + return 1; +} + +int +exec_unlock(void) +{ + struct param *par = param_by_letter('A'); + if (object_within_reach("unlock", par)) + printf("%s\n", (par->object->unlock)(par->object)); + return 1; +} + +int +parseexec(const char *input) +{ + static const struct command commands[] = { + {&exec_quit, "quit"}, + {&exec_look_around, "look"}, + {&exec_look_around, "look around"}, + {&exec_look, "look at A?"}, + {&exec_look, "look A?"}, + {&exec_go, "go to the A?"}, + {&exec_go, "go to A?"}, + {&exec_go, "go A?"}, + {&exec_get_from, "get A from B?"}, + {&exec_get, "get the A?"}, + {&exec_get, "get A?"}, + {&exec_get, "pick up the A?"}, + {&exec_get, "pick up a A?"}, + {&exec_get, "pick up A?"}, + {&exec_get, "pick the A?"}, + {&exec_get, "pick a A?"}, + {&exec_get, "pick A?"}, + {&exec_put_in, "put A in B?"}, + {&exec_put_in, "drop A in B?"}, + {&exec_drop, "drop A?"}, + {&exec_give, "give A?"}, + {&exec_ask, "ask A?"}, + {&exec_inventory, "inventory"}, + {&exec_open, "open A?"}, + {&exec_close, "close A?"}, + {&exec_lock, "lock A?"}, + {&exec_unlock, "unlock A?"}, + {&exec_no_match, "A?"}, + }; + + const struct command *cmd; + for (cmd = commands; !match_command(input, cmd->pattern); ++cmd) + ; + + return (*cmd->fn)(); +} blob - /dev/null blob + b058a863c858125593a005c6c08423a24f67b742 (mode 644) --- /dev/null +++ toggle.c @@ -0,0 +1,102 @@ +#include + +#include "adventure.h" + +static void +swap_locations(struct object *a, struct object *b) +{ + struct object *t; + + assert(a != NULL); + assert(b != NULL); + + t = a->location; + a->location = b->location; + b->location = t; +} + +const char * +cannot_be_opened(struct object *o) +{ + return "That cannot be opened."; +} + +const char * +cannot_be_closed(struct object *o) +{ + return "That cannot be closed."; +} + +const char * +cannot_be_locked(struct object *o) +{ + return "That cannot be locked."; +} + +const char * +cannot_be_unlocked(struct object *o) +{ + return "That cannot be unlocked."; +} + +const char * +is_already_open(struct object *o) +{ + return "That is already open."; +} + +const char * +is_already_closed(struct object *o) +{ + return "That is already closed."; +} + +const char * +is_already_locked(struct object *o) +{ + return "That is already locked."; +} + +const char * +is_already_unlocked(struct object *o) +{ + return "That is already unlocked."; +} + +const char * +is_still_open(struct object *o) +{ + return "That is still open."; +} + +const char * +is_still_locked(struct object *o) +{ + return "That is still locked."; +} + +const char * +toggle_backdoor(struct object *o) +{ + swap_locations(open_door_to_backroom, closed_door_to_backroom); + swap_locations(open_door_to_cave, closed_door_to_cave); + return "OK."; +} + +const char * +toggle_box(struct object *o) +{ + swap_locations(open_box, closed_box); + return "OK."; +} + +const char * +toggle_box_lock(struct object *o) +{ + if (key_for_box->location == player) { + swap_locations(closed_box, locked_box); + return "OK."; + } else { + return "You try really hard, but the closed box won't open without a key."; + } +}