From a305a931726bc13c604afca1209a656a8fbedc46 Mon Sep 17 00:00:00 2001 From: Franklin Wei Date: Sat, 16 Apr 2016 21:16:44 -0400 Subject: allow for modular world loading --- .gitignore | 1 + Makefile | 22 +- SOURCES | 16 -- src/SOURCES | 15 ++ src/globals.h | 1 + src/obj.c | 3 +- src/server.c | 83 +++++-- src/world.c | 21 +- src/world.h | 16 +- tools/worldgen.c | 122 +++++++++ worlds/SOURCES | 2 + worlds/netcosm_default.c | 636 +++++++++++++++++++++++++++++++++++++++++++++++ worlds/template.c | 99 ++++++++ worlds/test.c | 636 ----------------------------------------------- 14 files changed, 990 insertions(+), 683 deletions(-) delete mode 100644 SOURCES create mode 100644 src/SOURCES create mode 100644 tools/worldgen.c create mode 100644 worlds/SOURCES create mode 100644 worlds/netcosm_default.c create mode 100644 worlds/template.c delete mode 100644 worlds/test.c diff --git a/.gitignore b/.gitignore index 229fb89..b6b8e0f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ a.out *~ *.d netcosm.log +build diff --git a/Makefile b/Makefile index 3b59ad4..5214414 100644 --- a/Makefile +++ b/Makefile @@ -27,19 +27,22 @@ PLATFORM = unix OUT = $(BUILDDIR)/$(PLATFORM).bin PREFIX = /usr/local -SRC = $(shell cat SOURCES) -OBJ = $(patsubst %.c,$(BUILDDIR)/%.o,$(SRC)) +SRC = $(shell cat src/SOURCES) +OBJ = $(patsubst %.c,$(BUILDDIR)/src/%.o,$(SRC)) + +WORLD_SRC = $(shell cat worlds/SOURCES) +WORLD_OBJ = $(patsubst %.c,$(BUILDDIR)/worlds/%.so,$(WORLD_SRC)) INCLUDES = -I src/ -I export/include/ WARNFLAGS = -Wall -Wextra -Wshadow -fno-strict-aliasing OPTFLAGS = -O2 -DEBUGFLAGS = -g -fstack-protector -D_FORTIFY_SOURCE=2 +DEBUGFLAGS = -g -fstack-protector CFLAGS = $(OPTFLAGS) $(DEBUGFLAGS) $(WARNFLAGS) -std=c99 $(INCLUDES) -LDFLAGS = -lev -lcrypto +LDFLAGS = -lev -lcrypto -ldl HEADERS = src/*.h export/include/*.h @@ -49,11 +52,11 @@ DEPS = $(patsubst %.c,$(BUILDDIR)/%.d,$(SRC)) ################################################################################ .PHONY: all -all: | $(OUT) +all: | $(OUT) $(WORLD_OBJ) $(OUT): $(OBJ) @echo "LD $@" - @$(CC) $(OBJ) $(CFLAGS) -o $@ $(LDFLAGS) + @$(CC) $(OBJ) $(CFLAGS) -o $@ $(LDFLAGS) -rdynamic $(OBJ): | $(BUILDDIR) @@ -65,6 +68,11 @@ $(BUILDDIR)/%.o: %.c $(BUILDDIR)/%.d Makefile @mkdir -p `dirname $@` @$(CC) -c $< $(CFLAGS) -o $@ +$(BUILDDIR)/%.so: %.c $(BUILDDIR)/%.d Makefile + @echo "CC $<" + @mkdir -p `dirname $@` + @$(CC) $< $(CFLAGS) -shared -o $@ -D_WORLD_MODULE_ -fPIC + # Dependencies ################################################################################ @@ -86,7 +94,7 @@ setcap: .PHONY: clean clean: @echo "Cleaning build directory..." - @rm -f $(OBJ) $(OUT) + @rm -f $(OBJ) $(OUT) $(WORLD_OBJ) .PHONY: veryclean veryclean: diff --git a/SOURCES b/SOURCES deleted file mode 100644 index 289b682..0000000 --- a/SOURCES +++ /dev/null @@ -1,16 +0,0 @@ -src/auth.c -src/client.c -src/client_reqs.c -src/hash.c -src/main.c -src/multimap.c -src/obj.c -src/room.c -src/server.c -src/server_reqs.c -src/telnet.c -src/userdb.c -src/util.c -src/verb.c -src/world.c -worlds/test.c diff --git a/src/SOURCES b/src/SOURCES new file mode 100644 index 0000000..5cb3c9b --- /dev/null +++ b/src/SOURCES @@ -0,0 +1,15 @@ +auth.c +client.c +client_reqs.c +hash.c +main.c +multimap.c +obj.c +room.c +server.c +server_reqs.c +telnet.c +userdb.c +util.c +verb.c +world.c diff --git a/src/globals.h b/src/globals.h index 193bed4..6e050ee 100644 --- a/src/globals.h +++ b/src/globals.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/src/obj.c b/src/obj.c index ce4a890..c842d70 100644 --- a/src/obj.c +++ b/src/obj.c @@ -21,6 +21,7 @@ #include "hash.h" #include "multimap.h" #include "obj.h" +#include "world.h" /* map of class names -> object classes */ static void *obj_class_map = NULL; @@ -41,8 +42,6 @@ struct object_t *obj_new(const char *class_name) { if(!obj_class_map) { - extern const struct obj_class_t netcosm_obj_classes[]; - extern const size_t netcosm_obj_classes_sz; obj_class_map = hash_init(netcosm_obj_classes_sz / 2 + 1, hash_djb, compare_strings); diff --git a/src/server.c b/src/server.c index a4558bc..55cb1f3 100644 --- a/src/server.c +++ b/src/server.c @@ -41,6 +41,8 @@ static uint16_t port = DEFAULT_PORT; static int server_socket; +static char *world_module = "build/worlds/netcosm_default.so"; + #define SAVE_INTERVAL 10 /* saves state periodically */ @@ -179,10 +181,28 @@ static void check_userfile(void) static void load_worldfile(void) { - extern const struct roomdata_t netcosm_world[]; - extern const size_t netcosm_world_sz; + /* load the world module */ + void *handle = dlopen(world_module, RTLD_NOW); + if(!handle) + error("cannot load world module `%s' (%s)", world_module, dlerror()); + + /* load symbols */ + size_t *ptr; + + netcosm_verb_classes = dlsym(handle, "netcosm_verb_classes"); + ptr = dlsym(handle, "netcosm_verb_classes_sz"); + netcosm_verb_classes_sz = *ptr; - extern const char *netcosm_world_name; + netcosm_obj_classes = dlsym(handle, "netcosm_obj_classes"); + ptr = dlsym(handle, "netcosm_obj_classes_sz"); + netcosm_obj_classes_sz = *ptr; + + netcosm_world = dlsym(handle, "netcosm_world"); + ptr = dlsym(handle, "netcosm_world_sz"); + netcosm_world_sz = *ptr; + + char **tmp = dlsym(handle, "netcosm_world_name"); + netcosm_world_name = *tmp; if(access(WORLDFILE, F_OK) < 0) { @@ -375,6 +395,21 @@ static void init_signals(void) error("sigaction"); } +static void __attribute__((noreturn)) print_help(char *argv[]) +{ + debugf("Usage: %s [OPTION]...\n", argv[0]); + debugf("NetCosm MUD server\n"); + debugf("\n"); + debugf(" -a USER PASS\tautomatic setup with USER/PASS\n"); + debugf(" -d PREFIX\tcreate and change to PREFIX before writing data files\n"); + debugf(" -h, -?\t\tshow this help\n"); + debugf(" -p PORT\tlisten on PORT\n"); + debugf(" -w MODULE\tuse a different world module\n"); + exit(0); +} + +static char *data_prefix = NULL; + static void parse_args(int argc, char *argv[]) { for(int i = 1; i < argc; ++i) @@ -388,20 +423,29 @@ static void parse_args(int argc, char *argv[]) switch(c) { case 'h': /* help */ - debugf("Usage: %s [-d PREFIX] [-a ]\n", argv[0]); - exit(0); + case '?': + print_help(argv); case 'a': /* automatic first-run config */ autoconfig = true; + if(i + 2 > argc) + print_help(argv); autouser = argv[++i]; autopass = argv[++i]; break; case 'd': /* set data prefix */ - mkdir(argv[++i], 0700); - if(chdir(argv[i]) < 0) - { - debugf("Cannot access data prefix.\n"); - exit(0); - } + if(i + 1 > argc) + print_help(argv); + data_prefix = argv[++i]; + break; + case 'p': /* set port */ + if(i + 1 > argc) + print_help(argv); + port = strtol(argv[++i], NULL, 10); + break; + case 'w': /* world */ + if(i + 1 > argc) + print_help(argv); + world_module = argv[++i]; break; default: c = 'h'; @@ -409,8 +453,6 @@ static void parse_args(int argc, char *argv[]) } } } - else - port = strtol(argv[i], NULL, 10); } } @@ -431,13 +473,24 @@ int server_main(int argc, char *argv[]) parse_args(argc, argv); + /* this must be done before any world module data is used */ + load_worldfile(); + + if(data_prefix) + { + mkdir(data_prefix, 0700); + if(chdir(data_prefix) < 0) + { + debugf("Cannot access data prefix.\n"); + exit(0); + } + } + userdb_init(USERFILE); /* also performs first-time setup: */ check_userfile(); - load_worldfile(); - /* initialize request map */ reqmap_init(); diff --git a/src/world.c b/src/world.c index b3bb4b3..d7c3590 100644 --- a/src/world.c +++ b/src/world.c @@ -23,6 +23,20 @@ #include "room.h" #include "world.h" +/* verb classes */ +const struct verb_class_t *netcosm_verb_classes; +size_t netcosm_verb_classes_sz; + +/* object classes */ +const struct obj_class_t *netcosm_obj_classes; +size_t netcosm_obj_classes_sz; + +/* rooms */ +const struct roomdata_t *netcosm_world; +size_t netcosm_world_sz; + +const char *netcosm_world_name; + /* processed world data */ static struct room_t *world; @@ -184,7 +198,10 @@ bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz read(fd, &world_sz, sizeof(world_sz)); if(world_sz != data_sz) + { + debugf("Incompatible world state.\n"); return false; + } world = calloc(world_sz, sizeof(struct room_t)); @@ -192,10 +209,12 @@ bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz if(strcmp(name, world_name)) { free(world_name); - debugf("Incompatible world state.\n"); + debugf("Incompatible world state (%s %s).\n", name, world_name); return false; } + debugf("Loading world `%s'.\n", world_name); + obj_set_idcounter(1); size_t n_global_verbs = read_size(fd); diff --git a/src/world.h b/src/world.h index 50f9769..5dcd85d 100644 --- a/src/world.h +++ b/src/world.h @@ -26,19 +26,23 @@ /* the world module MUST define all of the following: */ +#ifndef _WORLD_MODULE_ +/* note that these are now dynamically loaded */ + /* verb classes */ -extern const struct verb_class_t netcosm_verb_classes[]; -extern const size_t netcosm_verb_classes_sz; +extern const struct verb_class_t *netcosm_verb_classes; +extern size_t netcosm_verb_classes_sz; /* object classes */ -extern const struct obj_class_t netcosm_obj_classes[]; -extern const size_t netcosm_obj_classes_sz; +extern const struct obj_class_t *netcosm_obj_classes; +extern size_t netcosm_obj_classes_sz; /* rooms */ -extern const struct roomdata_t netcosm_world[]; -extern const size_t netcosm_world_sz; +extern const struct roomdata_t *netcosm_world; +extern size_t netcosm_world_sz; extern const char *netcosm_world_name; +#endif /*** loads the world into RAM for the first time, resets the game state ***/ void world_init(const struct roomdata_t *data, size_t sz, const char *name); diff --git a/tools/worldgen.c b/tools/worldgen.c new file mode 100644 index 0000000..e0cd947 --- /dev/null +++ b/tools/worldgen.c @@ -0,0 +1,122 @@ +/* generates a 3d world with no content */ + +#include +#include +#include + +/* x and y are horizontal and forward-back axes, respectively */ +/* z is the vertical axis */ + +struct direction_info_t { + enum direction_t dir; + int off_x, off_y, off_z; +} dirs[] = { + { DIR_N, 0, 1, 0 }, + { DIR_NE, 1, 1, 0 }, + { DIR_E, 1, 0, 0 }, + { DIR_SE, 1, -1, 0 }, + { DIR_S, 0, -1, 0 }, + { DIR_SW, -1, -1, 0 }, + { DIR_W, -1, 0, 0 }, + { DIR_NW, -1, 1, 0 }, + { DIR_UP, 0, 0, 1 }, + { DIR_DN, 0, 0, -1 }, +}; + +int main() +{ + printf("#include \n"); + printf("const struct roomdata_t netcosm_world[] = {\n"); + + for(int x = 0; x < WORLD_DIM; ++x) + for(int y = 0; y < WORLD_DIM; ++y) + for(int z = 0; z < WORLD_DIM; ++z) + { + printf("{\n"); + printf("\"room_%d_%d_%d\",\n", x, y, z); + printf("\"Room (%d,%d,%d)\",\n", x, y, z); + printf("\"You are in a room...\","); + + char *adj[ARRAYLEN(dirs)]; + for(int i = 0; i < ARRAYLEN(dirs); ++i) + { + int new_x = x + dirs[i].off_x, + new_y = y + dirs[i].off_y, + new_z = z + dirs[i].off_z; + + if(new_x < 0 || new_x >= WORLD_DIM || + new_y < 0 || new_y >= WORLD_DIM || + new_z < 0 || new_z >= WORLD_DIM) + asprintf(adj + i, "NULL"); + else + asprintf(adj + i, "\"room_%d_%d_%d\"", + new_x, + new_y, + new_z); + } + + printf("{ "); + for(int i = 0; i < ARRAYLEN(dirs); ++i) + { + printf("%s, ", adj[i]); + free(adj[i]); + } + printf("NONE_IN, NONE_OT },\n"); + + for(int i = 0; i < 6; ++i) + printf("NULL,\n"); + + printf("},\n"); + } + + printf("};\n"); + printf("const size_t netcosm_world_sz = ARRAYLEN(netcosm_world);\n"); + printf("const char *netcosm_world_name = \"World Name Here\";\n"); + + printf("static void generic_ser(int fd, struct object_t *obj)\n"); + printf("{\n"); + printf(" write_string(fd, obj->userdata);\n"); + printf("}\n"); + + printf("static void generic_deser(int fd, struct object_t *obj)\n"); + printf("{\n"); + printf(" obj->userdata = read_string(fd);\n"); + printf("}\n"); + + printf("static void generic_destroy(struct object_t *obj)\n"); + printf("{\n"); + printf(" free(obj->userdata);\n"); + printf("}\n"); + + printf("static const char *generic_desc(struct object_t *obj, user_t *user)\n"); + printf("{\n"); + printf(" (void) user;\n"); + printf(" return obj->userdata;\n"); + printf("}\n"); + + printf("static void *generic_dup(struct object_t *obj)\n"); + printf("{\n"); + printf(" return strdup(obj->userdata);\n"); + printf("}\n"); + + printf("const struct obj_class_t netcosm_obj_classes[] = {\n"); + printf(" {\n"); + printf(" \"/generic\",\n"); + printf(" generic_ser,\n"); + printf(" generic_deser,\n"); + printf(" NULL,\n"); + printf(" NULL,\n"); + printf(" generic_destroy,\n"); + printf(" generic_desc,\n"); + printf(" generic_dup,\n"); + printf(" },\n"); + printf("};\n"); + printf("const size_t netcosm_obj_classes_sz = ARRAYLEN(netcosm_obj_classes);\n"); + + printf("const struct verb_class_t netcosm_verb_classes[] = {\n"); + printf("\n"); + printf("};\n"); + printf("\n"); + printf("const size_t netcosm_verb_classes_sz = ARRAYLEN(netcosm_verb_classes);\n"); + +}; diff --git a/worlds/SOURCES b/worlds/SOURCES new file mode 100644 index 0000000..1fcc434 --- /dev/null +++ b/worlds/SOURCES @@ -0,0 +1,2 @@ +netcosm_default.c +template.c diff --git a/worlds/netcosm_default.c b/worlds/netcosm_default.c new file mode 100644 index 0000000..11096df --- /dev/null +++ b/worlds/netcosm_default.c @@ -0,0 +1,636 @@ +#include + +/* implements dunnet in NetCosm */ + +/************ ROOM DEFINITIONS ************/ + +static void deadend_init(room_id id) +{ + struct object_t *new = obj_new("/generic"); + new->name = strdup("shovel"); + new->userdata = strdup("It is a normal shovel with a price tag attached that says $19.99."); + + room_obj_add(id, new); + + new = obj_new("/generic/notake"); + new->name = strdup("trees"); + new->userdata = strdup("They are palm trees with a bountiful supply of coconuts in them."); + new->hidden = true; + + room_obj_add(id, new); + room_obj_add_alias(id, new, "tree"); + room_obj_add_alias(id, new, "palm"); + room_obj_add_alias(id, new, "palm tree"); + + /* add global verbs */ + struct verb_t *verb = verb_new("dig"); + verb->name = strdup("dig"); + world_verb_add(verb); + + verb = verb_new("put"); + verb->name = strdup("put"); + world_verb_add(verb); + + verb = verb_new("eat"); + verb->name = strdup("eat"); + world_verb_add(verb); + + verb = verb_new("shake"); + verb->name = strdup("shake"); + world_verb_add(verb); + + verb = verb_new("type"); + verb->name = strdup("type"); + world_verb_add(verb); +} + +static void ew_road_init(room_id id) +{ + struct object_t *new = obj_new("/generic/notake"); + new->name = strdup("large boulder"); + new->userdata = strdup("It is just a boulder. It cannot be moved."); + room_obj_add(id, new); + room_obj_add_alias(id, new, "boulder"); + room_obj_add_alias(id, new, "rock"); +} + +static void fork_init(room_id id) +{ + room_get(id)->userdata = calloc(1, sizeof(bool)); + /* flag for whether the user has already dug */ + bool *b = room_get(id)->userdata; + *b = false; +} + +static void bool_ser(room_id id, int fd) +{ + bool *b = room_get(id)->userdata; + write_bool(fd, *b); +} + +static void bool_deser(room_id id, int fd) +{ + bool *b = calloc(1, sizeof(bool)); + *b = read_bool(fd); + room_get(id)->userdata = b; +} + +static void bool_destroy(room_id id) +{ + free(room_get(id)->userdata); +} + +static void senw_init(room_id id) +{ + struct object_t *new = obj_new("/generic/dunnet/food"); + new->name = strdup("some food"); + new->userdata = strdup("It looks like some kind of meat. Smells pretty bad."); + new->default_article = false; + room_obj_add(id, new); + room_obj_add_alias(id, new, "food"); + room_obj_add_alias(id, new, "meat"); +} + +static void hangout_init(room_id id) +{ + struct object_t *new = obj_new("/generic/notake"); + new->name = strdup("ferocious bear"); + new->userdata = strdup("It looks like a grizzly to me."); + room_obj_add(id, new); + room_obj_add_alias(id, new, "bear"); +} + +static void hidden_init(room_id id) +{ + struct object_t *new = obj_new("/generic"); + new->name = strdup("emerald bracelet"); + new->userdata = strdup("I see nothing special about that."); + room_obj_add(id, new); + room_obj_add_alias(id, new, "bracelet"); +} + +static bool building_enter(room_id id, user_t *user) +{ + (void) id; + if(multimap_lookup(userdb_lookup(user->user)->objects, "shiny brass key", NULL)) + return true; + else + { + send_msg(user, "You don't have a key that can open this door.\n"); + return false; + } +} + +static void mailroom_init(room_id id) +{ + struct object_t *new = obj_new("/generic/notake"); + new->name = strdup("bins"); + new->hidden = true; + + /* insert IAC NOP to prevent the extra whitespace from being dropped */ + new->userdata = strdup("All of the bins are empty. Looking closely you can see that there are names written at the bottom of each bin, but most of them are faded away so that you cannot read them. You can only make out three names:\n\377\361 Jeffrey Collier\n\377\361 Robert Toukmond\n\377\361 Thomas Stock\n"); + room_obj_add(id, new); + room_obj_add_alias(id, new, "mail bins"); +} + +static void computer_room_init(room_id id) +{ + struct object_t *new = obj_new("/generic/notake"); + new->name = strdup("computer"); + new->userdata = strdup("I see nothing special about that."); + new->hidden = true; + + room_obj_add(id, new); + room_obj_add_alias(id, new, "vax"); + room_obj_add_alias(id, new, "pokey"); + + /* flag for whether computer is active */ + room_get(id)->userdata = malloc(sizeof(bool)); + bool *b = room_get(id)->userdata; + *b = false; +} + +const struct roomdata_t netcosm_world[] = { + { + "dead_end", + "Dead End", + "You are at a dead end of a dirt road. The road goes to the east. In the distance you can see that it will eventually fork off. The trees here are very tall royal palms, and they are spaced equidistant from each other.", + { NONE_N, NONE_NE, "ew_road", NONE_SE, NONE_S, NONE_SW, NONE_W, NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, + deadend_init, + NULL, + NULL, + NULL, + NULL, + NULL, + }, + + { + "ew_road", + "E/W Dirt road", + "You are on the continuation of a dirt road. There are more trees on both sides of you. The road continues to the east and west.", + { NONE_N, NONE_NE, "fork", NONE_SE, NONE_S, NONE_SW, "dead_end", NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, + ew_road_init, + NULL, + NULL, + NULL, + NULL, + NULL, + }, + + { + "fork", + "Fork", + "You are at a fork of two passages, one to the northeast, and one to the southeast. The ground here seems very soft. You can also go back west.", + { NONE_N, "nesw_road", NONE_E, "senw_road", NONE_S, NONE_SW, "ew_road", NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, + fork_init, + NULL, + NULL, + bool_ser, + bool_deser, + bool_destroy, + }, + + { + "senw_road", + "SE/NW road", + "You are on a southeast/northwest road.", + { NONE_N, NONE_NE, NONE_E, "bear_hangout", NONE_S, NONE_SW, NONE_W, "fork", NONE_UP, NONE_DN, NONE_IN, NONE_OT }, + senw_init, + NULL, + NULL, + NULL, + NULL, + NULL, + }, + + { + "bear_hangout", + "Bear Hangout", + "You are standing at the end of a road. A passage leads back to the northwest.", + { NONE_N, NONE_NE, NONE_E, NONE_SE, NONE_S, "hidden_area", NONE_W, "senw_road", NONE_UP, NONE_DN, NONE_IN, NONE_OT }, + hangout_init, + NULL, + NULL, + NULL, + NULL, + NULL, + }, + + { + "hidden_area", + "Hidden Area", + "You are in a well-hidden area off to the side of a road. Back to the northeast through the brush you can see the bear hangout.", + { NONE_N, "bear_hangout", NONE_E, NONE_SE, NONE_S, NONE_SW, NONE_W, NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, + hidden_init, + NULL, + NULL, + NULL, + NULL, + NULL, + }, + + { + "nesw_road", + "NE/SW road", + "You are on a northeast/southwest road.", + { NONE_N, "building_front", NONE_E, NONE_SE, NONE_S, "fork", NONE_W, NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + }, + + { + "building_front", + "Building Front", + "You are at the end of the road. There is a building in front of you to the northeast, and the road leads back to the southwest.", + { NONE_N, "building_hallway", NONE_E, NONE_SE, NONE_S, "nesw_road", NONE_W, NONE_NW, NONE_UP, NONE_DN, "building_hallway", NONE_OT }, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + }, + + { + "building_hallway", + "Old Building hallway", + "You are in the hallway of an old building. There are rooms to the east and west, and doors leading out to the north and south.", + { NONE_N, NONE_NE, "mailroom", NONE_SE, "building_front", NONE_SW, "computer_room", NONE_NW, NONE_UP, NONE_DN, NONE_IN, "building_front" }, + NULL, + building_enter, + NULL, + NULL, + NULL, + NULL, + }, + + { + "mailroom", + "Mailroom", + "You are in a mailroom. There are many bins where the mail is usually kept. The exit is to the west.", + { NONE_N, NONE_NE, NONE_E, NONE_SE, NONE_S, NONE_SW, "building_hallway", NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, + mailroom_init, + NULL, + NULL, + NULL, + NULL, + NULL, + }, + + { + "computer_room", + "Computer room", + "You are in a computer room. It seems like most of the equipment has been removed. There is a VAX 11/780 in front of you, however, with one of the cabinets wide open. A sign on the front of the machine says: This VAX is named 'pokey'. To type on the console, use the 'type' command. The exit is to the east.\nThe panel lights are steady and motionless.", + { NONE_N, NONE_NE, "building_hallway", NONE_SE, NONE_S, NONE_SW, NONE_W, NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, + computer_room_init, + NULL, + NULL, + bool_ser, + bool_deser, + bool_destroy, + }, +}; + +const size_t netcosm_world_sz = ARRAYLEN(netcosm_world); +const char *netcosm_world_name = "Dunnet 0.1"; + +/************ OBJECT DEFINITIONS ************/ + +static void generic_ser(int fd, struct object_t *obj) +{ + write_string(fd, obj->userdata); +} + +static void generic_deser(int fd, struct object_t *obj) +{ + obj->userdata = read_string(fd); +} + +static void generic_destroy(struct object_t *obj) +{ + free(obj->userdata); +} + +static const char *generic_desc(struct object_t *obj, user_t *user) +{ + (void) user; + return obj->userdata; +} + +static void *generic_dup(struct object_t *obj) +{ + return strdup(obj->userdata); +} + +static bool no_take(struct object_t *obj, user_t *user) +{ + (void) obj; (void) user; + return false; +} + +static bool food_drop(struct object_t *obj, user_t *user) +{ + if(room_obj_get(user->room, "bear")) + { + send_msg(user, "The bear takes the food and runs away with it. He left something behind.\n"); + + room_obj_del(user->room, "ferocious bear"); + room_obj_del_by_ptr(user->room, obj); + + struct object_t *new = obj_new("/generic"); + new->hidden = false; + + new->name = strdup("shiny brass key"); + new->userdata = strdup("I see nothing special about that."); + + room_obj_add(user->room, new); + room_obj_add_alias(user->room, new, "key"); + room_obj_add_alias(user->room, new, "shiny key"); + room_obj_add_alias(user->room, new, "brass key"); + } + + return true; +} + +const struct obj_class_t netcosm_obj_classes[] = { + /* a generic, takeable object class with userdata pointing to its description */ + { + "/generic", + generic_ser, + generic_deser, + NULL, + NULL, + generic_destroy, + generic_desc, + generic_dup, + }, + + /* a generic, non-takeable object class, inherits /generic */ + { + "/generic/notake", + generic_ser, + generic_deser, + no_take, + NULL, + generic_destroy, + generic_desc, + generic_dup, + }, + + /* a specialized "food" object for dunnet, inherits /generic */ + { + "/generic/dunnet/food", + generic_ser, + generic_deser, + NULL, + food_drop, + generic_destroy, + generic_desc, + generic_dup, + }, +}; + +const size_t netcosm_obj_classes_sz = ARRAYLEN(netcosm_obj_classes); + +/**************** VERB DEFINITIONS ****************/ + +static void dig_exec(struct verb_t *verb, char *args, user_t *user) +{ + (void) verb; + (void) args; + if(!multimap_lookup(userdb_lookup(user->user)->objects, "shovel", NULL)) + { + send_msg(user, "You have nothing with which to dig.\n"); + return; + } + + if(!strcmp(room_get(user->room)->data.name, "Fork")) + { + bool *b = room_get(user->room)->userdata; + if(!*b) + { + *b = true; + struct object_t *new = obj_new("/generic"); + new->name = strdup("CPU card"); + new->userdata = strdup("The CPU board has a VAX chip on it. It seems to have 2 Megabytes of RAM onboard."); + room_obj_add(user->room, new); + room_obj_add_alias(user->room, new, "cpu"); + room_obj_add_alias(user->room, new, "chip"); + room_obj_add_alias(user->room, new, "card"); + send_msg(user, "I think you found something.\n"); + } + else + { + goto nothing; + } + } + else + send_msg(user, "Digging here reveals nothing.\n"); + + return; + +nothing: + send_msg(user, "Digging here reveals nothing.\n"); +} + +static void put_exec(struct verb_t *verb, char *args, user_t *user) +{ + (void) verb; + char *save; + const char *obj_name = strtok_r(args, WSPACE, &save); + + if(!obj_name) + { + send_msg(user, "You must supply an object\n"); + return; + } + + args = NULL; + const struct multimap_list *list = multimap_lookup(userdb_lookup(user->user)->objects, + obj_name, NULL); + if(!list) + { + send_msg(user, "You don't have that.\n"); + return; + } + + struct object_t *obj = list->val; + + /* original dunnet ignores the preposition */ + const char *prep = strtok_r(args, WSPACE, &save); + (void) prep; + + const char *ind_obj_name = strtok_r(args, WSPACE, &save); + + if(!ind_obj_name) + { + send_msg(user, "You must supply an indirect object.\n"); + return; + } + + list = room_obj_get(user->room, ind_obj_name); + + if(!list) + { + send_msg(user, "I don't know what that indirect object is.\n"); + return; + } + + struct object_t *ind_obj = list->val; + + /* now execute the verb */ + if(!strcmp(obj->name, "CPU card") && !strcmp(ind_obj->name, "computer") && user->room == room_get_id("computer_room")) + { + userdb_del_obj_by_ptr(user->user, obj); + send_msg(user, "As you put the CPU board in the computer, it immediately springs to life. The lights start flashing, and the fans seem to startup.\n"); + bool *b = room_get(user->room)->userdata; + *b = true; + + free(room_get(user->room)->data.desc); + room_get(user->room)->data.desc = strdup("You are in a computer room. It seems like most of the equipment has been removed. There is a VAX 11/780 in front of you, however, with one of the cabinets wide open. A sign on the front of the machine says: This VAX is named 'pokey'. To type on the console, use the 'type' command. The exit is to the east.\nThe panel lights are flashing in a seemingly organized pattern."); + } + else + { + send_msg(user, "I don't know how to combine those objects. Perhaps you should just try dropping it.\n"); + } +} + +static void eat_exec(struct verb_t *verb, char *args, user_t *user) +{ + (void) verb; + char *save; + char *obj_name = strtok_r(args, WSPACE, &save); + if(!obj_name) + { + send_msg(user, "You must supply an object.\n"); + return; + } + + size_t n_objs; + const struct multimap_list *list = multimap_lookup(userdb_lookup(user->user)->objects, obj_name, &n_objs); + + if(!list) + { + if(!room_obj_get(user->room, obj_name)) + send_msg(user, "I don't know what that is.\n"); + else + send_msg(user, "You don't have that.\n"); + return; + } + + struct object_t *obj = list->val; + + if(!strcmp(obj->name, "some food")) + { + send_msg(user, "That tasted horrible.\n"); + } + else + { + char buf[MSG_MAX]; + send_msg(user, "You forcibly shove %s down your throat, and start choking.\n", + format_noun(buf, sizeof(buf), obj->name, n_objs, obj->default_article, false)); + + /* TODO: kill player */ + } + + userdb_del_obj(user->user, obj_name); +} + +static void shake_exec(struct verb_t *verb, char *args, user_t *user) +{ + (void) verb; + char *save; + char *obj_name = strtok_r(args, WSPACE, &save); + + if(!obj_name) + { + send_msg(user, "You must supply an object.\n"); + return; + } + + size_t n_objs_room, n_objs_inv; + const struct multimap_list *list_room = room_obj_get_size(user->room, obj_name, &n_objs_room); + + const struct multimap_list *list_inv = multimap_lookup(userdb_lookup(user->user)->objects, obj_name, &n_objs_inv); + + if(!list_room && !list_inv) + { + send_msg(user, "I don't know what that is.\n"); + return; + } + + if(list_room) + { + struct object_t *obj = list_room->val; + if(!strcmp(obj->name, "trees")) + send_msg(user, "You begin to shake a tree, and notice a coconut begin to fall from the air. As you try to get your hand up to block it, you feel the impact as it lands on your head.\n"); + else + send_msg(user, "You don't have that.\n"); + } + else if(list_inv) + { + struct object_t *obj = list_inv->val; + char buf[MSG_MAX]; + send_msg(user, "Shaking %s seems to have no effect.\n", + format_noun(buf, sizeof(buf), obj->name, + n_objs_inv, obj->default_article, + false)); + } +} + +static void type_exec(struct verb_t *verb, char *args, user_t *user) +{ + (void) verb; + (void) args; + + struct room_t *room = room_get(user->room); + if(strcmp(room->data.uniq_id, "computer_room")) + send_msg(user, "There is nothing here on which you could type.\n"); + else + { + bool *b = room->userdata; + + /* computer is not active */ + if(!*b) + { + send_msg(user, "You type on the keyboard, but your characters do not even echo.\n"); + return; + } + else + { + send_msg(user, "FIXME\n"); + } + } +} + +static void climb_exec(struct verb_t *verb, char *args, user_t *user) +{ + +} + +/* verb classes */ + +const struct verb_class_t netcosm_verb_classes[] = { + { "dig", + dig_exec }, + { "put", + put_exec }, + { "eat", + eat_exec }, + { "shake", + shake_exec }, + { "type", + type_exec }, + /* + { "climb", + climb_exec }, + { "feed", + feed_exec }, + */ +}; + +const size_t netcosm_verb_classes_sz = ARRAYLEN(netcosm_verb_classes); diff --git a/worlds/template.c b/worlds/template.c new file mode 100644 index 0000000..d22ecb4 --- /dev/null +++ b/worlds/template.c @@ -0,0 +1,99 @@ +/* + * NetCosm - a MUD server + * Copyright (C) 2016 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +/* This is a sample world implemented in NetCosm. */ + +/* A world is composed of rooms, object classes, and verb classes. For + * now they are defined as global arrays, but this is subject to change. + */ + +/* This is our array of rooms. Each contains multiple callbacks and + strings pointing to other rooms, see room.h for details */ +const struct roomdata_t netcosm_world [] = { + { + "uniq_id", // this must be globally unique + "Room name", + "Initial description", + + /* these can be replaced with strings identifying other rooms */ + { NONE_N, NONE_NE, NONE_E, NONE_SE, NONE_S, NONE_SW, NONE_W, NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, + + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + }, +}; + +const size_t netcosm_world_sz = ARRAYLEN(netcosm_world); +const char *netcosm_world_name = "World Name Here"; + +/********* OBJECTS *********/ + +static void generic_ser(int fd, struct object_t *obj) +{ + write_string(fd, obj->userdata); +} + +static void generic_deser(int fd, struct object_t *obj) +{ + obj->userdata = read_string(fd); +} + +static void generic_destroy(struct object_t *obj) +{ + free(obj->userdata); +} + +static const char *generic_desc(struct object_t *obj, user_t *user) +{ + (void) user; + return obj->userdata; +} + +static void *generic_dup(struct object_t *obj) +{ + return strdup(obj->userdata); +} + +const struct obj_class_t netcosm_obj_classes[] = { + { + "/generic", + generic_ser, + generic_deser, + NULL, + NULL, + generic_destroy, + generic_desc, + generic_dup, + }, +}; + +const size_t netcosm_obj_classes_sz = ARRAYLEN(netcosm_obj_classes); + +/********* VERBS *********/ + +const struct verb_class_t netcosm_verb_classes[] = { + +}; + +const size_t netcosm_verb_classes_sz = ARRAYLEN(netcosm_verb_classes); diff --git a/worlds/test.c b/worlds/test.c deleted file mode 100644 index 11096df..0000000 --- a/worlds/test.c +++ /dev/null @@ -1,636 +0,0 @@ -#include - -/* implements dunnet in NetCosm */ - -/************ ROOM DEFINITIONS ************/ - -static void deadend_init(room_id id) -{ - struct object_t *new = obj_new("/generic"); - new->name = strdup("shovel"); - new->userdata = strdup("It is a normal shovel with a price tag attached that says $19.99."); - - room_obj_add(id, new); - - new = obj_new("/generic/notake"); - new->name = strdup("trees"); - new->userdata = strdup("They are palm trees with a bountiful supply of coconuts in them."); - new->hidden = true; - - room_obj_add(id, new); - room_obj_add_alias(id, new, "tree"); - room_obj_add_alias(id, new, "palm"); - room_obj_add_alias(id, new, "palm tree"); - - /* add global verbs */ - struct verb_t *verb = verb_new("dig"); - verb->name = strdup("dig"); - world_verb_add(verb); - - verb = verb_new("put"); - verb->name = strdup("put"); - world_verb_add(verb); - - verb = verb_new("eat"); - verb->name = strdup("eat"); - world_verb_add(verb); - - verb = verb_new("shake"); - verb->name = strdup("shake"); - world_verb_add(verb); - - verb = verb_new("type"); - verb->name = strdup("type"); - world_verb_add(verb); -} - -static void ew_road_init(room_id id) -{ - struct object_t *new = obj_new("/generic/notake"); - new->name = strdup("large boulder"); - new->userdata = strdup("It is just a boulder. It cannot be moved."); - room_obj_add(id, new); - room_obj_add_alias(id, new, "boulder"); - room_obj_add_alias(id, new, "rock"); -} - -static void fork_init(room_id id) -{ - room_get(id)->userdata = calloc(1, sizeof(bool)); - /* flag for whether the user has already dug */ - bool *b = room_get(id)->userdata; - *b = false; -} - -static void bool_ser(room_id id, int fd) -{ - bool *b = room_get(id)->userdata; - write_bool(fd, *b); -} - -static void bool_deser(room_id id, int fd) -{ - bool *b = calloc(1, sizeof(bool)); - *b = read_bool(fd); - room_get(id)->userdata = b; -} - -static void bool_destroy(room_id id) -{ - free(room_get(id)->userdata); -} - -static void senw_init(room_id id) -{ - struct object_t *new = obj_new("/generic/dunnet/food"); - new->name = strdup("some food"); - new->userdata = strdup("It looks like some kind of meat. Smells pretty bad."); - new->default_article = false; - room_obj_add(id, new); - room_obj_add_alias(id, new, "food"); - room_obj_add_alias(id, new, "meat"); -} - -static void hangout_init(room_id id) -{ - struct object_t *new = obj_new("/generic/notake"); - new->name = strdup("ferocious bear"); - new->userdata = strdup("It looks like a grizzly to me."); - room_obj_add(id, new); - room_obj_add_alias(id, new, "bear"); -} - -static void hidden_init(room_id id) -{ - struct object_t *new = obj_new("/generic"); - new->name = strdup("emerald bracelet"); - new->userdata = strdup("I see nothing special about that."); - room_obj_add(id, new); - room_obj_add_alias(id, new, "bracelet"); -} - -static bool building_enter(room_id id, user_t *user) -{ - (void) id; - if(multimap_lookup(userdb_lookup(user->user)->objects, "shiny brass key", NULL)) - return true; - else - { - send_msg(user, "You don't have a key that can open this door.\n"); - return false; - } -} - -static void mailroom_init(room_id id) -{ - struct object_t *new = obj_new("/generic/notake"); - new->name = strdup("bins"); - new->hidden = true; - - /* insert IAC NOP to prevent the extra whitespace from being dropped */ - new->userdata = strdup("All of the bins are empty. Looking closely you can see that there are names written at the bottom of each bin, but most of them are faded away so that you cannot read them. You can only make out three names:\n\377\361 Jeffrey Collier\n\377\361 Robert Toukmond\n\377\361 Thomas Stock\n"); - room_obj_add(id, new); - room_obj_add_alias(id, new, "mail bins"); -} - -static void computer_room_init(room_id id) -{ - struct object_t *new = obj_new("/generic/notake"); - new->name = strdup("computer"); - new->userdata = strdup("I see nothing special about that."); - new->hidden = true; - - room_obj_add(id, new); - room_obj_add_alias(id, new, "vax"); - room_obj_add_alias(id, new, "pokey"); - - /* flag for whether computer is active */ - room_get(id)->userdata = malloc(sizeof(bool)); - bool *b = room_get(id)->userdata; - *b = false; -} - -const struct roomdata_t netcosm_world[] = { - { - "dead_end", - "Dead End", - "You are at a dead end of a dirt road. The road goes to the east. In the distance you can see that it will eventually fork off. The trees here are very tall royal palms, and they are spaced equidistant from each other.", - { NONE_N, NONE_NE, "ew_road", NONE_SE, NONE_S, NONE_SW, NONE_W, NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, - deadend_init, - NULL, - NULL, - NULL, - NULL, - NULL, - }, - - { - "ew_road", - "E/W Dirt road", - "You are on the continuation of a dirt road. There are more trees on both sides of you. The road continues to the east and west.", - { NONE_N, NONE_NE, "fork", NONE_SE, NONE_S, NONE_SW, "dead_end", NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, - ew_road_init, - NULL, - NULL, - NULL, - NULL, - NULL, - }, - - { - "fork", - "Fork", - "You are at a fork of two passages, one to the northeast, and one to the southeast. The ground here seems very soft. You can also go back west.", - { NONE_N, "nesw_road", NONE_E, "senw_road", NONE_S, NONE_SW, "ew_road", NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, - fork_init, - NULL, - NULL, - bool_ser, - bool_deser, - bool_destroy, - }, - - { - "senw_road", - "SE/NW road", - "You are on a southeast/northwest road.", - { NONE_N, NONE_NE, NONE_E, "bear_hangout", NONE_S, NONE_SW, NONE_W, "fork", NONE_UP, NONE_DN, NONE_IN, NONE_OT }, - senw_init, - NULL, - NULL, - NULL, - NULL, - NULL, - }, - - { - "bear_hangout", - "Bear Hangout", - "You are standing at the end of a road. A passage leads back to the northwest.", - { NONE_N, NONE_NE, NONE_E, NONE_SE, NONE_S, "hidden_area", NONE_W, "senw_road", NONE_UP, NONE_DN, NONE_IN, NONE_OT }, - hangout_init, - NULL, - NULL, - NULL, - NULL, - NULL, - }, - - { - "hidden_area", - "Hidden Area", - "You are in a well-hidden area off to the side of a road. Back to the northeast through the brush you can see the bear hangout.", - { NONE_N, "bear_hangout", NONE_E, NONE_SE, NONE_S, NONE_SW, NONE_W, NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, - hidden_init, - NULL, - NULL, - NULL, - NULL, - NULL, - }, - - { - "nesw_road", - "NE/SW road", - "You are on a northeast/southwest road.", - { NONE_N, "building_front", NONE_E, NONE_SE, NONE_S, "fork", NONE_W, NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - }, - - { - "building_front", - "Building Front", - "You are at the end of the road. There is a building in front of you to the northeast, and the road leads back to the southwest.", - { NONE_N, "building_hallway", NONE_E, NONE_SE, NONE_S, "nesw_road", NONE_W, NONE_NW, NONE_UP, NONE_DN, "building_hallway", NONE_OT }, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - }, - - { - "building_hallway", - "Old Building hallway", - "You are in the hallway of an old building. There are rooms to the east and west, and doors leading out to the north and south.", - { NONE_N, NONE_NE, "mailroom", NONE_SE, "building_front", NONE_SW, "computer_room", NONE_NW, NONE_UP, NONE_DN, NONE_IN, "building_front" }, - NULL, - building_enter, - NULL, - NULL, - NULL, - NULL, - }, - - { - "mailroom", - "Mailroom", - "You are in a mailroom. There are many bins where the mail is usually kept. The exit is to the west.", - { NONE_N, NONE_NE, NONE_E, NONE_SE, NONE_S, NONE_SW, "building_hallway", NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, - mailroom_init, - NULL, - NULL, - NULL, - NULL, - NULL, - }, - - { - "computer_room", - "Computer room", - "You are in a computer room. It seems like most of the equipment has been removed. There is a VAX 11/780 in front of you, however, with one of the cabinets wide open. A sign on the front of the machine says: This VAX is named 'pokey'. To type on the console, use the 'type' command. The exit is to the east.\nThe panel lights are steady and motionless.", - { NONE_N, NONE_NE, "building_hallway", NONE_SE, NONE_S, NONE_SW, NONE_W, NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, - computer_room_init, - NULL, - NULL, - bool_ser, - bool_deser, - bool_destroy, - }, -}; - -const size_t netcosm_world_sz = ARRAYLEN(netcosm_world); -const char *netcosm_world_name = "Dunnet 0.1"; - -/************ OBJECT DEFINITIONS ************/ - -static void generic_ser(int fd, struct object_t *obj) -{ - write_string(fd, obj->userdata); -} - -static void generic_deser(int fd, struct object_t *obj) -{ - obj->userdata = read_string(fd); -} - -static void generic_destroy(struct object_t *obj) -{ - free(obj->userdata); -} - -static const char *generic_desc(struct object_t *obj, user_t *user) -{ - (void) user; - return obj->userdata; -} - -static void *generic_dup(struct object_t *obj) -{ - return strdup(obj->userdata); -} - -static bool no_take(struct object_t *obj, user_t *user) -{ - (void) obj; (void) user; - return false; -} - -static bool food_drop(struct object_t *obj, user_t *user) -{ - if(room_obj_get(user->room, "bear")) - { - send_msg(user, "The bear takes the food and runs away with it. He left something behind.\n"); - - room_obj_del(user->room, "ferocious bear"); - room_obj_del_by_ptr(user->room, obj); - - struct object_t *new = obj_new("/generic"); - new->hidden = false; - - new->name = strdup("shiny brass key"); - new->userdata = strdup("I see nothing special about that."); - - room_obj_add(user->room, new); - room_obj_add_alias(user->room, new, "key"); - room_obj_add_alias(user->room, new, "shiny key"); - room_obj_add_alias(user->room, new, "brass key"); - } - - return true; -} - -const struct obj_class_t netcosm_obj_classes[] = { - /* a generic, takeable object class with userdata pointing to its description */ - { - "/generic", - generic_ser, - generic_deser, - NULL, - NULL, - generic_destroy, - generic_desc, - generic_dup, - }, - - /* a generic, non-takeable object class, inherits /generic */ - { - "/generic/notake", - generic_ser, - generic_deser, - no_take, - NULL, - generic_destroy, - generic_desc, - generic_dup, - }, - - /* a specialized "food" object for dunnet, inherits /generic */ - { - "/generic/dunnet/food", - generic_ser, - generic_deser, - NULL, - food_drop, - generic_destroy, - generic_desc, - generic_dup, - }, -}; - -const size_t netcosm_obj_classes_sz = ARRAYLEN(netcosm_obj_classes); - -/**************** VERB DEFINITIONS ****************/ - -static void dig_exec(struct verb_t *verb, char *args, user_t *user) -{ - (void) verb; - (void) args; - if(!multimap_lookup(userdb_lookup(user->user)->objects, "shovel", NULL)) - { - send_msg(user, "You have nothing with which to dig.\n"); - return; - } - - if(!strcmp(room_get(user->room)->data.name, "Fork")) - { - bool *b = room_get(user->room)->userdata; - if(!*b) - { - *b = true; - struct object_t *new = obj_new("/generic"); - new->name = strdup("CPU card"); - new->userdata = strdup("The CPU board has a VAX chip on it. It seems to have 2 Megabytes of RAM onboard."); - room_obj_add(user->room, new); - room_obj_add_alias(user->room, new, "cpu"); - room_obj_add_alias(user->room, new, "chip"); - room_obj_add_alias(user->room, new, "card"); - send_msg(user, "I think you found something.\n"); - } - else - { - goto nothing; - } - } - else - send_msg(user, "Digging here reveals nothing.\n"); - - return; - -nothing: - send_msg(user, "Digging here reveals nothing.\n"); -} - -static void put_exec(struct verb_t *verb, char *args, user_t *user) -{ - (void) verb; - char *save; - const char *obj_name = strtok_r(args, WSPACE, &save); - - if(!obj_name) - { - send_msg(user, "You must supply an object\n"); - return; - } - - args = NULL; - const struct multimap_list *list = multimap_lookup(userdb_lookup(user->user)->objects, - obj_name, NULL); - if(!list) - { - send_msg(user, "You don't have that.\n"); - return; - } - - struct object_t *obj = list->val; - - /* original dunnet ignores the preposition */ - const char *prep = strtok_r(args, WSPACE, &save); - (void) prep; - - const char *ind_obj_name = strtok_r(args, WSPACE, &save); - - if(!ind_obj_name) - { - send_msg(user, "You must supply an indirect object.\n"); - return; - } - - list = room_obj_get(user->room, ind_obj_name); - - if(!list) - { - send_msg(user, "I don't know what that indirect object is.\n"); - return; - } - - struct object_t *ind_obj = list->val; - - /* now execute the verb */ - if(!strcmp(obj->name, "CPU card") && !strcmp(ind_obj->name, "computer") && user->room == room_get_id("computer_room")) - { - userdb_del_obj_by_ptr(user->user, obj); - send_msg(user, "As you put the CPU board in the computer, it immediately springs to life. The lights start flashing, and the fans seem to startup.\n"); - bool *b = room_get(user->room)->userdata; - *b = true; - - free(room_get(user->room)->data.desc); - room_get(user->room)->data.desc = strdup("You are in a computer room. It seems like most of the equipment has been removed. There is a VAX 11/780 in front of you, however, with one of the cabinets wide open. A sign on the front of the machine says: This VAX is named 'pokey'. To type on the console, use the 'type' command. The exit is to the east.\nThe panel lights are flashing in a seemingly organized pattern."); - } - else - { - send_msg(user, "I don't know how to combine those objects. Perhaps you should just try dropping it.\n"); - } -} - -static void eat_exec(struct verb_t *verb, char *args, user_t *user) -{ - (void) verb; - char *save; - char *obj_name = strtok_r(args, WSPACE, &save); - if(!obj_name) - { - send_msg(user, "You must supply an object.\n"); - return; - } - - size_t n_objs; - const struct multimap_list *list = multimap_lookup(userdb_lookup(user->user)->objects, obj_name, &n_objs); - - if(!list) - { - if(!room_obj_get(user->room, obj_name)) - send_msg(user, "I don't know what that is.\n"); - else - send_msg(user, "You don't have that.\n"); - return; - } - - struct object_t *obj = list->val; - - if(!strcmp(obj->name, "some food")) - { - send_msg(user, "That tasted horrible.\n"); - } - else - { - char buf[MSG_MAX]; - send_msg(user, "You forcibly shove %s down your throat, and start choking.\n", - format_noun(buf, sizeof(buf), obj->name, n_objs, obj->default_article, false)); - - /* TODO: kill player */ - } - - userdb_del_obj(user->user, obj_name); -} - -static void shake_exec(struct verb_t *verb, char *args, user_t *user) -{ - (void) verb; - char *save; - char *obj_name = strtok_r(args, WSPACE, &save); - - if(!obj_name) - { - send_msg(user, "You must supply an object.\n"); - return; - } - - size_t n_objs_room, n_objs_inv; - const struct multimap_list *list_room = room_obj_get_size(user->room, obj_name, &n_objs_room); - - const struct multimap_list *list_inv = multimap_lookup(userdb_lookup(user->user)->objects, obj_name, &n_objs_inv); - - if(!list_room && !list_inv) - { - send_msg(user, "I don't know what that is.\n"); - return; - } - - if(list_room) - { - struct object_t *obj = list_room->val; - if(!strcmp(obj->name, "trees")) - send_msg(user, "You begin to shake a tree, and notice a coconut begin to fall from the air. As you try to get your hand up to block it, you feel the impact as it lands on your head.\n"); - else - send_msg(user, "You don't have that.\n"); - } - else if(list_inv) - { - struct object_t *obj = list_inv->val; - char buf[MSG_MAX]; - send_msg(user, "Shaking %s seems to have no effect.\n", - format_noun(buf, sizeof(buf), obj->name, - n_objs_inv, obj->default_article, - false)); - } -} - -static void type_exec(struct verb_t *verb, char *args, user_t *user) -{ - (void) verb; - (void) args; - - struct room_t *room = room_get(user->room); - if(strcmp(room->data.uniq_id, "computer_room")) - send_msg(user, "There is nothing here on which you could type.\n"); - else - { - bool *b = room->userdata; - - /* computer is not active */ - if(!*b) - { - send_msg(user, "You type on the keyboard, but your characters do not even echo.\n"); - return; - } - else - { - send_msg(user, "FIXME\n"); - } - } -} - -static void climb_exec(struct verb_t *verb, char *args, user_t *user) -{ - -} - -/* verb classes */ - -const struct verb_class_t netcosm_verb_classes[] = { - { "dig", - dig_exec }, - { "put", - put_exec }, - { "eat", - eat_exec }, - { "shake", - shake_exec }, - { "type", - type_exec }, - /* - { "climb", - climb_exec }, - { "feed", - feed_exec }, - */ -}; - -const size_t netcosm_verb_classes_sz = ARRAYLEN(netcosm_verb_classes); -- cgit v1.1