Re-wrote and re-factored lots of stuff to facilitate this.
MAP_LENGTH 64
PLAYER_TYPE 0
-ACTION 1
-NAME wait
-EFFORT 1
+THING_ACTION 1
+TA_EFFORT 1
+TA_NAME wait
-ACTION 2
-NAME move
-EFFORT 5
+THING_ACTION 2
+TA_EFFORT 5
+TA_NAME move
-ACTION 3
-NAME pick_up
-EFFORT 15
+THING_ACTION 3
+TA_EFFORT 15
+TA_NAME pick_up
-ACTION 4
-NAME drop
-EFFORT 5
+THING_ACTION 4
+TA_EFFORT 5
+TA_NAME drop
-ACTION 5
-NAME use
-EFFORT 45
+THING_ACTION 5
+TA_EFFORT 45
+TA_NAME use
-THINGTYPE 0
-NAME HUMAN
-SYMBOL @
-LIFEPOINTS 5
-CORPSE_ID 5
-CONSUMABLE 0
-START_NUMBER 1
+THING_TYPE 0
+TT_START_NUMBER 1
+TT_LIFEPOINTS 5
+TT_SYMBOL @
+TT_NAME HUMAN
+TT_CONSUMABLE 0
-THINGTYPE 1
-NAME ANT
-SYMBOL a
-LIFEPOINTS 1
-CORPSE_ID 4
-CONSUMABLE 0
-START_NUMBER 27
+THING_TYPE 1
+TT_START_NUMBER 27
+TT_LIFEPOINTS 1
+TT_SYMBOL a
+TT_NAME ANT
+TT_CONSUMABLE 0
-THINGTYPE 2
-NAME ZOMBIE
-SYMBOL z
-LIFEPOINTS 3
-CORPSE_ID 5
-CONSUMABLE 0
-START_NUMBER 9
+THING_TYPE 2
+TT_START_NUMBER 9
+TT_LIFEPOINTS 3
+TT_SYMBOL z
+TT_NAME ZOMBIE
+TT_CONSUMABLE 0
-THINGTYPE 3
-NAME SHOGGOTH
-SYMBOL S
-LIFEPOINTS 9
-CORPSE_ID 6
-CONSUMABLE 0
-START_NUMBER 3
+THING_TYPE 3
+TT_START_NUMBER 3
+TT_LIFEPOINTS 9
+TT_SYMBOL S
+TT_NAME SHOGGOTH
+TT_CONSUMABLE 0
-THINGTYPE 4
-NAME DIRT
-SYMBOL #
-LIFEPOINTS 0
-CORPSE_ID 4
-CONSUMABLE 0
-START_NUMBER 9
+THING_TYPE 4
+TT_START_NUMBER 9
+TT_LIFEPOINTS 0
+TT_SYMBOL #
+TT_NAME DIRT
+TT_CONSUMABLE 0
-THINGTYPE 5
-NAME SKELETON
-SYMBOL %
-LIFEPOINTS 0
-CORPSE_ID 4
-CONSUMABLE 0
-START_NUMBER 9
+THING_TYPE 5
+TT_START_NUMBER 9
+TT_LIFEPOINTS 0
+TT_SYMBOL %
+TT_NAME SKELETON
+TT_CONSUMABLE 0
-THINGTYPE 6
-NAME 'MAGIC MEAT'
-SYMBOL m
-LIFEPOINTS 0
-CORPSE_ID 4
-CONSUMABLE 3
-START_NUMBER 1
+THING_TYPE 6
+TT_START_NUMBER 1
+TT_LIFEPOINTS 0
+TT_SYMBOL m
+TT_NAME 'MAGIC MEAT'
+TT_CONSUMABLE 3
+
+THING_TYPE 0
+TT_CORPSE_ID 5
+THING_TYPE 1
+TT_CORPSE_ID 4
+THING_TYPE 2
+TT_CORPSE_ID 5
+THING_TYPE 3
+TT_CORPSE_ID 6
+THING_TYPE 4
+TT_CORPSE_ID 4
+THING_TYPE 5
+TT_CORPSE_ID 4
+THING_TYPE 6
+TT_CORPSE_ID 4
#include <stdint.h> /* uint8_t */
#include <stdlib.h> /* free() */
#include <string.h> /* strcmp(), strdup() */
-#include "../common/parse_file.h" /* EDIT_STARTED,parse_init_entry(),
- * parse_id_uniq(), parse_unknown_arg(),
- * parsetest_too_many_values(), parse_file(),
- * parse_and_reduce_to_readyflag(),
- * parse_flagval()
- */
#include "array_append.h" /* array_append() */
+#include "parse.h" /* EDIT_STARTED, parse_init_entry(), parse_id_uniq(),
+ * parse_unknown_arg(), parsetest_too_many_values(),
+ * parse_file(), parse_and_reduce_to_readyflag(),
+ * parse_flagval()
+ */
#include "world.h" /* global world */
#include "cleanup.h" /* set_cleanup_flag() */
{
char * f_name = "build_server_message_with_argument()";
uint8_t command_size = strlen(cmd->server_msg);
- char * arg_str;
- uint8_t arg_size;
+ char * arg_str = "";
+ uint8_t arg_size = 0;
if ('i' == cmd->arg)
{
arg_size = 3;
#include <stdio.h> /* FILE, sprintf() */
#include <string.h> /* strchr(), strcmp(), strdup(), strlen() */
#include <unistd.h> /* optarg, getopt() */
-#include "../common/parse_file.h" /* EDIT_STARTED, parse_file(),parse_flagval(),
- * token_from_line(), parsetest_singlechar(),
- * parse_and_reduce_to_readyflag(),
- * parsetest_defcontext(),parse_unknown_arg(),
- * parsetest_too_many_values(),
- * parse_id_uniq(), parse_init_entry()
- */
+#include "../common/parse_file.h" /* token_from_line(),parsetset_singlechar() */
#include "../common/readwrite.h" /* atomic_write_start(), atomic_write_finish(),
* detect_atomic_leftover(), try_fwrite()
*/
#include "command_db.h" /* get_command() */
#include "keybindings.h" /* KeyBinding, KeyBindingDB, get_command_to_keycode()*/
#include "map.h" /* map_center() */
+#include "parse.h" /* EDIT_STARTED, parse_file(), parse_flagval(),
+ * parse_and_reduce_to_readyflag(), parse_id_uniq()
+ * parsetest_defcontext(), parse_unknown_arg(),
+ * parsetest_too_many_values(), parse_init_entry()
+ */
#include "wincontrol.h" /* toggle_window() */
#include "windows.h" /* Win, free_winDB(), make_v_screen_and_init_win_sizes() */
#include "world.h" /* global world */
--- /dev/null
+/* src/client/parse.c */
+
+#define _POSIX_C_SOURCE 200809L /* strdup() */
+#include "parse.h"
+#include <stddef.h> /* size_t, NULL */
+#include <stdio.h> /* FILE, snprintf() */
+#include <stdint.h> /* uint8_t, uint32_t */
+#include <stdlib.h> /* free() */
+#include <string.h> /* strdup(), strlen() */
+#include <unistd.h> /* access(), F_OK */
+#include "../common/parse_file.h" /* set_err_line_options(), err_line_inc(),
+ * err_line_zero(), token_from_line()
+ */
+#include "../common/readwrite.h" /* try_fopen(),try_fclose(),textfile_width() */
+#include "../common/rexit.h" /* exit_err(), exit_trouble() */
+#include "../common/try_malloc.h" /* try_malloc() */
+
+
+
+extern void parse_file(char * path, void (* token_to_entry) (char *, char *))
+{
+ char * f_name = "read_new_config_file()";
+ char * prefix = "Failed reading config file: \"";
+ char * affix = "\". ";
+ size_t size = strlen(prefix) + strlen(path) + strlen(affix) + 1;
+ char * errline_intro = try_malloc(size, f_name);
+ int test = snprintf(errline_intro, size, "%s%s%s", prefix, path, affix);
+ exit_trouble(test < 0, f_name, "snprintf()");
+ exit_err(access(path, F_OK), errline_intro);
+ FILE * file = try_fopen(path, "r", f_name);
+ uint32_t linemax = textfile_width(file);
+ char * errline_line = try_malloc(linemax + 1, f_name);
+ set_err_line_options(errline_intro, errline_line, 1);
+ err_line_zero();
+ err_line(0 == linemax, "File is empty.");
+ char * token0 = NULL; /* For final token_to_entry() if while() stagnates. */
+ char * token1 = NULL;
+ char * err_val = "No value given.";
+ while (try_fgets(errline_line, linemax + 1, file, f_name))
+ {
+ err_line_inc();
+ // err_line(UINT32_MAX == err_line_count, "Line reaches max lines limit.");
+ char * line_copy = strdup(errline_line);
+ token0 = token_from_line(line_copy);
+ if (token0)
+ {
+ err_line(0 == (token1 = token_from_line(NULL)), err_val);
+ token_to_entry(token0, token1);
+ token0 = NULL;
+ }
+ free(line_copy);
+ }
+ token_to_entry(token0, token1);
+ try_fclose(file, f_name);
+ free(errline_line);
+ free(errline_intro);
+}
+
+
+
+extern void parsetest_defcontext(uint8_t flags)
+{
+ err_line(!(flags & EDIT_STARTED),"Outside appropriate definition context.");
+}
+
+
+
+extern void parsetest_too_many_values()
+{
+ err_line(NULL != token_from_line(NULL), "Too many values.");
+}
+
+
+
+extern void parse_id_uniq(int test)
+{
+ err_line(0 != test, "Declaration of ID already used.");
+}
+
+
+
+extern void parse_unknown_arg()
+{
+ err_line(1, "Unknown argument.");
+}
+
+
+
+extern char * parse_init_entry(uint8_t * flags, size_t size)
+{
+ char * f_name = "parse_init_entry()";
+ *flags = EDIT_STARTED;
+ char * p = try_malloc(size, f_name);
+ memset(p, 0, size);
+ return p;
+}
+
+
+
+extern uint8_t parse_flagval(char * token0, char * token1, char * comparand,
+ uint8_t * flags, uint8_t set_flag, char type,
+ char * element)
+{
+ if (parse_val(token0, token1, comparand, type, element))
+ {
+ parsetest_defcontext(*flags);
+ *flags = *flags | set_flag;
+ return 1;
+ }
+ return 0;
+}
+
+
+
+extern void parse_and_reduce_to_readyflag(uint8_t * flags, uint8_t ready_flag)
+{
+ char * err_fin = "Last definition block not finished yet.";
+ err_line((*flags & ready_flag) ^ ready_flag, err_fin);
+ *flags = ready_flag;
+}
--- /dev/null
+/* src/client/parse.h
+ *
+ * Routines for file parsing.
+ */
+
+#ifndef PARSE_H
+#define PARSE_H
+
+#include <stddef.h> /* size_t */
+#include <stdint.h> /* uint8_t */
+
+
+
+enum parse_flags
+{
+ EDIT_STARTED = 0x01
+};
+
+
+
+/* Parse file at "path" by passing each line's first two tokens to
+ * "token_to_entry". Ignore empty lines. Non-empty lines must feature at least
+ * two tokens as delimited either be whitespace or single quotes (to allow for
+ * tokens featuring whitespaces). When EOF is reached, token_to_entry() is
+ * called a last time with a first token of NULL.
+ */
+extern void parse_file(char * path, void ( *token_to_entry) (char *, char *));
+
+/* Calls err_line() with fitting message if EDIT_STARTED not set in "flags". */
+extern void parsetest_defcontext(uint8_t flags);
+
+/* Ensure token_from_line() does not find any more tokens on the line. */
+extern void parsetest_too_many_values();
+
+/* Trigger err_line() with "Unknown argument" message. */
+extern void parse_unknown_arg();
+
+/* If "test"!=0 call err_line() with "Declaration of ID already used" message.*/
+extern void parse_id_uniq(int test);
+
+/* Set "flags"=EDIT_STARTED and return pointer to new zeroed array of "size". */
+extern char * parse_init_entry(uint8_t * flags, size_t size);
+
+/* Wrapper to parse_val() that sets "flags" to "flags"|"set_flag" on success. */
+extern uint8_t parse_flagval(char * token0, char * token1, char * comparand,
+ uint8_t * flags, uint8_t set_flag, char type,
+ char * element);
+
+/* Check "ready_flag" is set in "flags", re-set "flags" to "ready_flag" only. */
+extern void parse_and_reduce_to_readyflag(uint8_t * flags, uint8_t ready_flag);
+
+
+
+#endif
+
#include <stdint.h> /* int16_t,uint8_t,uint32_t, INT16_MIN, UINT{8,16,32}_MAX */
#include <stdlib.h> /* atoi(), free() */
#include <string.h> /* strchr, strcmp(), strdup(), strlen() */
-#include <unistd.h> /* access(), F_OK */
-#include "readwrite.h" /* try_fopen(), try_fclose(), textfile_width() */
#include "rexit.h" /* exit_err(), exit_trouble() */
#include "try_malloc.h" /* try_malloc() */
-extern void parse_file(char * path, void (* token_to_entry) (char *, char *))
+extern void set_err_line_options(char * intro, char * line, uint8_t exit)
{
- char * f_name = "read_new_config_file()";
- char * prefix = "Failed reading config file: \"";
- char * affix = "\". ";
- size_t size = strlen(prefix) + strlen(path) + strlen(affix) + 1;
- char * errline_intro = try_malloc(size, f_name);
- int test = snprintf(errline_intro, size, "%s%s%s", prefix, path, affix);
- exit_trouble(test < 0, f_name, "snprintf()");
- exit_err(access(path, F_OK), errline_intro);
- FILE * file = try_fopen(path, "r", f_name);
- uint32_t linemax = textfile_width(file);
- char * errline_line = try_malloc(linemax + 1, f_name);
- set_err_line_options(errline_intro, errline_line, 0, 1);
- err_line(0 == linemax, "File is empty.");
- char * token0 = NULL; /* For final token_to_entry() if while() stagnates. */
- char * token1 = NULL;
- char * err_val = "No value given.";
- while (try_fgets(errline_line, linemax + 1, file, f_name))
- {
- err_line_count++;
- err_line(UINT32_MAX == err_line_count, "Line reaches max lines limit.");
- char * line_copy = strdup(errline_line);
- token0 = token_from_line(line_copy);
- if (token0)
- {
- err_line(0 == (token1 = token_from_line(NULL)), err_val);
- token_to_entry(token0, token1);
- token0 = NULL;
- }
- free(line_copy);
- }
- token_to_entry(token0, token1);
- try_fclose(file, f_name);
- free(errline_line);
- free(errline_intro);
+ err_line_line = line;
+ err_line_intro = intro;
+ err_line_exit = exit;
}
-extern void set_err_line_options(char * intro, char * line, uint32_t count,
- uint8_t exit)
+extern void err_line_inc()
{
- err_line_count = count;
- err_line_line = line;
- err_line_intro = intro;
- err_line_exit = exit;
+ err_line_count++;
+ err_line(UINT32_MAX == err_line_count, "Line reaches max lines limit.");
+}
+
+
+
+extern void err_line_zero()
+{
+ err_line_count = 0;
}
}
char * f_name = "err_line()";
char * prefix = " Offending line ";
- char * affix = ":\n";
+ char * affix = ": ";
size_t size = strlen(err_line_intro) + strlen(msg) + strlen(prefix)
+ 10 /* strlen for uint32_t representations */
+ strlen(affix) + strlen(err_line_line) + 1;
-extern void parsetest_defcontext(uint8_t flags)
-{
- err_line(!(flags & EDIT_STARTED),"Outside appropriate definition context.");
-}
-
-
-
extern uint8_t parsetest_singlechar(char * string)
{
return err_line(1 !=strlen(string),"Value must be single ASCII character.");
-extern void parsetest_too_many_values()
-{
- err_line(NULL != token_from_line(NULL), "Too many values.");
-}
-
-
-
-extern void parse_id_uniq(int test)
-{
- err_line(0 != test, "Declaration of ID already used.");
-}
-
-
-
-extern void parse_unknown_arg()
-{
- err_line(1, "Unknown argument.");
-}
-
-
-
-extern char * parse_init_entry(uint8_t * flags, size_t size)
-{
- char * f_name = "parse_init_entry()";
- *flags = EDIT_STARTED;
- char * p = try_malloc(size, f_name);
- memset(p, 0, size);
- return p;
-}
-
-
-
extern uint8_t parse_val(char * token0, char * token1, char * comparand,
char type, char * element)
{
{
if ('s' == type)
{
+ free(* (char **) element);
* (char **) element = strdup(token1);
}
- else if ('c' == type && !parsetest_singlechar(token1))
+ else if ('c' == type)
{
- *element = (token1)[0];
+ if (!parsetest_singlechar(token1))
+ {
+ *element = (token1)[0];
+ }
}
else if (!parsetest_int(token1, type))
{
}
return 0;
}
-
-
-
-extern uint8_t parse_flagval(char * token0, char * token1, char * comparand,
- uint8_t * flags, uint8_t set_flag, char type,
- char * element)
-{
- if (parse_val(token0, token1, comparand, type, element))
- {
- parsetest_defcontext(*flags);
- *flags = *flags | set_flag;
- return 1;
- }
- return 0;
-}
-
-
-
-extern void parse_and_reduce_to_readyflag(uint8_t * flags, uint8_t ready_flag)
-{
- char * err_fin = "Last definition block not finished yet.";
- err_line((*flags & ready_flag) ^ ready_flag, err_fin);
- *flags = ready_flag;
-}
/* src/common/parse_file.h
*
- * Tools for parsing config files.
+ * Tools for parsing files.
*/
#ifndef PARSE_FILE_H
#define PARSE_FILE_H
-#include <stddef.h> /* size_t */
#include <stdint.h> /* uint8_t */
-enum parse_flags
-{
- EDIT_STARTED = 0x01
-};
-
-
-
-/* Parse file at "path" by passing each line's first two tokens to
- * "token_to_entry". Ignore empty lines. Non-empty lines must feature at least
- * two tokens as delimited either be whitespace or single quotes (to allow for
- * tokens featuring whitespaces). When EOF is reached, token_to_entry() is
- * called a last time with a first token of NULL.
- */
-extern void parse_file(char * path, void ( *token_to_entry) (char *, char *));
-
/* Set err_line() options: "intro" message, char array used to store analyzed
- * lines ("line"), line start "count", whether to "exit" on error message.
+ * lines ("line"), and whether to "exit" on error message.
*/
-extern void set_err_line_options(char * intro, char * line, uint32_t count,
- uint8_t exit);
+extern void set_err_line_options(char * intro, char * line, uint8_t exit);
+
+/* Increment and reset (to zero) err_line() line counter. */
+extern void err_line_inc();
+extern void err_line_zero();
/* If "test", output "msg", faulty line, its number and exit if so defined by
* set_err_line_options(), else return 1 on "test" and 0 if "test" is 0.
/* Test for "string" to be of length 1 (excluding "\0"). Return 1 on failure. */
extern uint8_t parsetest_singlechar(char * string);
-/* Calls err_line() with fitting message if EDIT_STARTED not set in "flags". */
-extern void parsetest_defcontext(uint8_t flags);
-
-/* Ensure token_from_line() does not find any more tokens on the line. */
-extern void parsetest_too_many_values();
-
-/* Trigger err_line() with "Unknown argument" message. */
-extern void parse_unknown_arg();
-
-/* If "test"!=0 call err_line() with "Declaration of ID already used" message.*/
-extern void parse_id_uniq(int test);
-
-/* Set "flags"=EDIT_STARTED and return pointer to new zeroed array of "size". */
-extern char * parse_init_entry(uint8_t * flags, size_t size);
-
/* If "token0" fits "comparand", set "element" to value read from "token1" as
* string (type: "s"), char ("c") uint8 ("8"), uint16 ("u"), uint32 ("U") or
* int16 ("i"), and return 1; else 0.
extern uint8_t parse_val(char * token0, char * token1, char * comparand,
char type, char * element);
-/* Wrapper to parse_val() that sets "flags" to "flags"|"set_flag" on success. */
-extern uint8_t parse_flagval(char * token0, char * token1, char * comparand,
- uint8_t * flags, uint8_t set_flag, char type,
- char * element);
-
-/* Check "ready_flag" is set in "flags", re-set "flags" to "ready_flag" only. */
-extern void parse_and_reduce_to_readyflag(uint8_t * flags, uint8_t ready_flag);
-
#endif
+++ /dev/null
-/* src/server/configfile.c */
-
-#include <stddef.h> /* size_t, NULL */
-#include <stdio.h> /* snprintf() */
-#include <stdint.h> /* uint8_t */
-#include <stdlib.h> /* atoi(), free() */
-#include <string.h> /* strcmp() */
-#include "../common/parse_file.h" /* EDIT_STARTED, parsetest_int(),parse_file(),
- * parsetest_too_many_values(),parse_id_uniq()
- * parse_unknown_arg(), parse_init_entry(),
- * parse_and_reduce_to_readyflag(),
- * parse_flagval()
- */
-#include "../common/rexit.h" /* exit_err(), exit_trouble() */
-#include "../common/try_malloc.h" /* try_malloc() */
-#include "cleanup.h" /* set_cleanup_flag(), CLEANUP_THING_TYPES,
- * CLEANUP_THING_ACTIONS
- */
-#include "hardcoded_strings.h" /* s */
-#include "thing_actions.h" /* ThingAction */
-#include "things.h" /* Thing, ThingType */
-#include "world.h" /* world global */
-
-
-
-/* Flags defining state of thing type and action entry reading ((un-)finished /
- * ready for starting the reading of a new definition etc.)
- */
-enum flag
-{
- HEIGHT_SET = 0x02,
- WIDTH_SET = 0x04,
- NAME_SET = 0x02,
- EFFORT_SET = 0x04,
- CORPSE_ID_SET = 0x04,
- SYMBOL_SET = 0x08,
- LIFEPOINTS_SET = 0x10,
- CONSUMABLE_SET = 0x20,
- START_N_SET = 0x40,
- READY_ACTION = NAME_SET | EFFORT_SET,
- READY_THING = NAME_SET | CORPSE_ID_SET | SYMBOL_SET | LIFEPOINTS_SET
- | CONSUMABLE_SET | START_N_SET
-};
-
-
-
-/* What ThingType and ThingAction structs have in common at their top. Use this
- * to allow same functions run over structs of both types.
- */
-struct EntryHead
-{
- uint8_t id;
- struct EntryHead * next;
-};
-
-
-
-/* Interpret "token0" and "token1" as data to write into the ThingAction /
- * ThingType DB.
- *
- * Individual ThingType / ThingAction DB entries are put together line by line
- * before being written. Writing only happens after all necessary members of an
- * entry have been assembled, and when additionally a) a new entry is started by
- * a "token0" of "ACTION" or "THINGTYPE"; or b) "token0" is NULL.
- *
- * Also check against the line parse_file() read tokens from having more tokens.
- */
-static void tokens_into_entries(char * token0, char * token1);
-
-/* Start reading a new DB entry of "size" from tokens if "token0" matches
- * "comparand". Set EDIT_STARTED in "flags" to mark beginning of new entry
- * reading. Check that "token1" id of new entry has not already been used in DB
- * starting at "entry_cmp".
- */
-static uint8_t start_entry(char * token0, char * token1, char * comparand,
- uint8_t * flags, size_t size,
- struct EntryHead ** entry,
- struct EntryHead * entry_cmp);
-
-/* Write DB entry pointed to by "entry" to its appropriate location. */
-static void write_if_entry(struct EntryHead ** entry,
- struct EntryHead *** entry_p_p_p);
-
-/* Ensure that all .corpse_id members in the ThingType DB fit .id members of
- * ThingType DB entries.
- */
-static void test_corpse_ids();
-
-/* If "token0" matches "comparand", set world.player_type to int in "token1". */
-static uint8_t set_player_type(char * token0, char * comparand, char * token1);
-
-/* If "token0" matches "comparand", set world.map.length to int in "token1". */
-static uint8_t set_map_length(char * token0, char * comparand, char * token1);
-
-/* Try to read tokens as members for the definition currently edited, which may
- * be "tt" or "ta". What member of which of the two is set depends on which of
- * the flags has EDIT_STARTED set and on the key name in "token0". Return 1 if
- * interpretation succeeds, else 0.
- *
- * Note that ThingAction entries' .name also determines their .func.
- */
-static uint8_t set_members(char * token0, char * token1,
- uint8_t * thing_flags, uint8_t * action_flags,
- struct ThingType * tt, struct ThingAction * ta);
-
-/* If "name" fits "ta"->name, set "ta"->func to "func". (Derives ThingAction
- * .func from .name for set_members().
- */
-static uint8_t try_func_name(struct ThingAction * ta,
- char * name, void (* func) (struct Thing *));
-
-
-
-static void tokens_into_entries(char * token0, char * token1)
-{
- char * str_action = "ACTION";
- char * str_thing = "THINGTYPE";
- char * str_player = "PLAYER_TYPE";
- char * str_map_length = "MAP_LENGTH";
- static struct ThingAction ** ta_p_p = &world.thing_actions;
- static struct ThingType ** tt_p_p = &world.thing_types;
- static uint8_t action_flags = READY_ACTION;
- static uint8_t thing_flags = READY_THING;
- static struct EntryHead * ta = NULL;
- static struct EntryHead * tt = NULL;
- if (!token0 || !strcmp(token0, str_action) || !strcmp(token0, str_thing)
- || !strcmp(token0, str_player))
- {
- parse_and_reduce_to_readyflag(&action_flags, READY_ACTION);
- parse_and_reduce_to_readyflag(&thing_flags, READY_THING);
- write_if_entry(&ta, (struct EntryHead ***) &ta_p_p);
- write_if_entry(&tt, (struct EntryHead ***) &tt_p_p);
- }
- if (token0)
- {
- parsetest_too_many_values();
- if (start_entry(token0, token1, str_action, &action_flags,
- sizeof(struct ThingAction), (struct EntryHead**) &ta,
- (struct EntryHead *) world.thing_actions))
- {
- err_line(0 == atoi(token1), "Value must not be 0.");
- }
- else if (!( start_entry(token0, token1, str_thing, &thing_flags,
- sizeof(struct ThingType),
- (struct EntryHead**) &tt,
- (struct EntryHead *) world.thing_types)
- || set_player_type(token0, str_player, token1)
- || set_map_length(token0, str_map_length, token1)
- || set_members(token0, token1, &thing_flags, &action_flags,
- (struct ThingType *) tt,
- (struct ThingAction *) ta)))
- {
- parse_unknown_arg();
- }
- }
-}
-
-
-
-static uint8_t start_entry(char * token0, char * token1, char * comparand,
- uint8_t * flags, size_t size,
- struct EntryHead ** entry,
- struct EntryHead * entry_cmp)
-{
- if (strcmp(token0, comparand))
- {
- return 0;
- }
- *entry = (struct EntryHead *) parse_init_entry(flags, size);
- parsetest_int(token1, '8');
- (*entry)-> id = atoi(token1);
- for (; NULL != entry_cmp; entry_cmp = entry_cmp->next)
- {
- parse_id_uniq((*entry)->id == entry_cmp->id);
- }
- return 1;
-}
-
-
-
-static void write_if_entry(struct EntryHead ** entry,
- struct EntryHead *** entry_p_p_p)
-{
- if (*entry)
- {
- (*entry)->next = NULL;
- **entry_p_p_p = *entry;
- *entry_p_p_p = &((*entry)->next);
- *entry = NULL; /* So later runs of this don't re-append same entry. */
- }
-}
-
-
-
-static void test_corpse_ids()
-{
- char * f_name = "test_corpse_ids()";
- char * prefix = "In the thing types DB, one thing corpse ID does not "
- "reference any known thing type in the DB. ID of "
- "responsible thing type: ";
- size_t size = strlen(prefix) + 3 + 1; /* 3: uint8_t representation strlen */
- char * err_corpse = try_malloc(size, f_name);
- struct ThingType * test_entry_0 = world.thing_types;
- for (; test_entry_0; test_entry_0 = test_entry_0->next)
- {
- uint8_t corpse_id_found = 0;
- struct ThingType * test_entry_1 = world.thing_types;
- for (; test_entry_1; test_entry_1 = test_entry_1->next)
- {
- if (test_entry_0->corpse_id == test_entry_1->id)
- {
- corpse_id_found = 1;
- }
- }
- int test = snprintf(err_corpse, size, "%s%d", prefix, test_entry_0->id);
- exit_trouble(test < 0, f_name, "snprintf()");
- exit_err(!corpse_id_found, err_corpse);
- }
- free(err_corpse);
-}
-
-
-
-static uint8_t set_player_type(char * token0, char * comparand, char * token1)
-{
- if (strcmp(token0, comparand))
- {
- return 0;
- }
- parsetest_int(token1, '8');
- world.player_type = atoi(token1);
- return 1;
-}
-
-
-
-static uint8_t set_map_length(char * token0, char * comparand, char * token1)
-{
- if (strcmp(token0, comparand))
- {
- return 0;
- }
- parsetest_int(token1, 'i');
- int test = atoi(token1) > 256 || atoi(token1) < 1;
- err_line(test, "Value must be >= 1 and <= 256.");
- world.map.length = atoi(token1);
- return 1;
-}
-
-
-
-static uint8_t set_members(char * token0, char * token1, uint8_t * thing_flags,
- uint8_t * action_flags,
- struct ThingType * tt, struct ThingAction * ta)
-{
- if ( *action_flags & EDIT_STARTED
- && parse_flagval(token0, token1, "NAME", action_flags,
- NAME_SET, 's', (char *) &ta->name))
- {
- if (!( try_func_name(ta, s[S_CMD_MOVE], actor_move)
- || try_func_name(ta, s[S_CMD_PICKUP], actor_pick)
- || try_func_name(ta, s[S_CMD_DROP], actor_drop)
- || try_func_name(ta, s[S_CMD_USE], actor_use)))
- {
- ta->func = actor_wait;
- }
- *action_flags = *action_flags | NAME_SET;
- return 1;
- }
- else if ( parse_flagval(token0, token1, "NAME", thing_flags,
- NAME_SET, 's', (char *) &tt->name)
- || parse_flagval(token0, token1, "SYMBOL", thing_flags,
- SYMBOL_SET, 'c', (char *) &tt->char_on_map)
- || parse_flagval(token0, token1, "EFFORT", action_flags,
- EFFORT_SET, '8', (char *) &ta->effort)
- || parse_flagval(token0, token1, "START_NUMBER", thing_flags,
- START_N_SET, '8', (char *) &tt->start_n)
- || parse_flagval(token0, token1, "LIFEPOINTS", thing_flags,
- LIFEPOINTS_SET, '8', (char *) &tt->lifepoints)
- || parse_flagval(token0, token1, "CONSUMABLE", thing_flags,
- CONSUMABLE_SET, '8', (char *) &tt->consumable)
- || parse_flagval(token0, token1, "CORPSE_ID", thing_flags,
- CORPSE_ID_SET, '8', (char *) &tt->corpse_id))
- {
- return 1;
- }
- return 0;
-}
-
-
-
-static uint8_t try_func_name(struct ThingAction * ta, char * name,
- void (* func) (struct Thing *))
-{
- if (0 == strcmp(ta->name, name))
- {
- ta->func = func;
- return 1;
- }
- return 0;
-}
-
-
-
-extern void read_config_file()
-{
- parse_file(s[S_PATH_CONFIG], tokens_into_entries);
- exit_err(!world.map.length, "Map size not defined in config file.");
- uint8_t player_type_is_valid = 0;
- struct ThingType * tt;
- for (tt = world.thing_types; NULL != tt; tt = tt->next)
- {
- if (world.player_type == tt->id)
- {
- player_type_is_valid = 1;
- break;
- }
- }
- exit_err(!player_type_is_valid, "No valid thing type set for player.");
- set_cleanup_flag(CLEANUP_THING_ACTIONS | CLEANUP_THING_TYPES);
- test_corpse_ids();
-}
+++ /dev/null
-/* src/server/configfile.h
- *
- * Reading in the config file of thing types and thing actions.
- */
-
-#ifndef CONFIGFILE_H
-#define CONFIGFILE_H
-
-
-
-/* Parse thing type / action definitons file nto thing type and thing action
- * definitions at world.thing_types and world.thing_actions.
- */
-extern void read_config_file();
-
-
-
-#endif
--- /dev/null
+/* src/server/god_commands.c */
+
+#include "god_commands.h"
+#include <stddef.h> /* NULL */
+#include <stdint.h> /* uint8_t */
+#include <stdlib.h> /* atoi(), free() */
+#include <string.h> /* strcmp() */
+#include <unistd.h> /* F_OK, access(), unlink() */
+#include "../common/parse_file.h" /* err_line(), parse_val(), parsetest_int() */
+#include "../common/rexit.h" /* exit_trouble() */
+#include "cleanup.h" /* unset_cleanup_flag() */
+#include "field_of_view.h" /* build_fov_map() */
+#include "hardcoded_strings.h" /* s */
+#include "init.h" /* remake_world() */
+#include "map.h" /* remake_map() */
+#include "thing_actions.h" /* ThingAction, actor_wait(), actor_move(),
+ * actor_use(), actor_pickup(), actor_drop()
+ */
+#include "things.h" /* Thing, ThingType, add_thing(), get_thing(), own_thing(),
+ * free_things()
+ */
+#include "world.h" /* world */
+
+
+
+/* Parse/apply god command in "tok0"/tok1" to manipulate a ThingType*/
+static uint8_t parse_thingtype_manipulation(char * tok0, char * tok1);
+
+/* If "name" fits "ta"->name, set "ta"->func to "func". (Derives ThingAction
+ * .func from .name for set_members().
+ */
+static uint8_t try_func_name(struct ThingAction * ta, char * name,
+ void (* func) (struct Thing *));
+
+/* Parse/apply god command in "tok0"/"tok1" to manipulate a ThingAction. */
+static uint8_t parse_thingaction_manipulation(char * tok0, char * tok1);
+
+/* Parse/apply god command in "tok0"/"tok1" oo setting "t"'s thing type. */
+static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t);
+
+/* Parse/apply god command in "tok0"/"tok1" on setting up thing "t". */
+static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t);
+
+/* Parse/apply god command in "tok0"/"tok1" on positioning a thing "t". */
+static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t);
+
+/* Parse/apply god command in "tok0"/"tok1" on "t" owning another thing. */
+static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t);
+
+/* Parse/apply god command in "tok0"/"tok1" to manipulate a Thing. */
+static uint8_t parse_thing_manipulation(char * tok0, char * tok1);
+
+/* Performs parse_world_active()'s world activation legality tests. */
+static uint8_t world_may_be_set_active();
+
+/* Parse/apply god command in "tok0"/"tok1" on toggling world.exists. Unset if
+ * argument is 0 and unlink worldstate file, but only set it on positive
+ * argument if it is not already set and a thing action of name S_CMD_WAIT, a
+ * player thing and a map are defined. On setting it, rebuild all FOVs.
+ */
+static uint8_t parse_world_active(char * tok0, char * tok1);
+
+/* Parse/apply god command in "tok0"/"tok1" to reset world.map.length. On
+ * re-set, set world.exists to 0, remove all things and free world.map.cells
+ */
+static uint8_t set_map_length(char * tok0, char * tok1);
+
+
+
+static uint8_t parse_thingtype_manipulation(char * tok0, char * tok1)
+{
+ static struct ThingType * tt = NULL;
+ if (!tt &&
+ ( !strcmp(tok0, s[S_CMD_TT_CONSUM]) || !strcmp(tok0, s[S_CMD_TT_SYMB])
+ || !strcmp(tok0, s[S_CMD_TT_STARTN]) || !strcmp(tok0, s[S_CMD_TT_NAME])
+ || !strcmp(tok0, s[S_CMD_TT_CORPS]) || !strcmp(tok0, s[S_CMD_TT_HP])))
+ {
+ err_line(1, "No thing type defined to manipulate yet.");
+ return 1;
+ }
+ uint8_t id;
+ if ( parse_val(tok0,tok1,s[S_CMD_TT_CONSUM],'8',(char *) &tt->consumable)
+ || parse_val(tok0,tok1,s[S_CMD_TT_HP],'8',(char *) &tt->lifepoints)
+ || parse_val(tok0,tok1,s[S_CMD_TT_STARTN],'8',(char *) &tt->start_n)
+ || parse_val(tok0,tok1,s[S_CMD_TT_SYMB],'c',(char *) &tt->char_on_map)
+ || parse_val(tok0,tok1,s[S_CMD_TT_NAME],'s',(char *) &tt->name));
+ else if (parse_val(tok0, tok1, s[S_CMD_TT_CORPS],'8',(char *)&id))
+ {
+ if (!get_thing_type(id))
+ {
+ err_line(1, "Corpse ID belongs to no known thing type.");
+ return 1;
+ }
+ tt->corpse_id = id;
+ }
+ else if (parse_val(tok0, tok1, s[S_CMD_THINGTYPE], '8', (char *) &id))
+ {
+ tt = get_thing_type(id);
+ if (!tt)
+ {
+ tt = add_thing_type(id);
+ }
+ }
+ else
+ {
+ return 0;
+ }
+ return 1;
+}
+
+
+
+static uint8_t try_func_name(struct ThingAction * ta, char * name,
+ void (* func) (struct Thing *))
+{
+ if (0 == strcmp(ta->name, name))
+ {
+ ta->func = func;
+ return 1;
+ }
+ return 0;
+}
+
+
+
+static uint8_t parse_thingaction_manipulation(char * tok0, char * tok1)
+{
+ static struct ThingAction * ta = NULL;
+ if (!ta &&
+ (!strcmp(tok0, s[S_CMD_TA_EFFORT]) || !strcmp(tok0, s[S_CMD_TA_NAME])))
+ {
+ err_line(1, "No thing action defined to manipulate yet.");
+ return 1;
+ }
+ uint8_t id;
+ if (parse_val(tok0, tok1, s[S_CMD_TA_EFFORT],'8',(char *) &ta->effort));
+ else if (parse_val(tok0, tok1, s[S_CMD_TA_NAME], 's', (char *) &ta->name))
+ {
+ if (!( try_func_name(ta, s[S_CMD_MOVE], actor_move)
+ || try_func_name(ta, s[S_CMD_PICKUP], actor_pick)
+ || try_func_name(ta, s[S_CMD_WAIT], actor_wait)
+ || try_func_name(ta, s[S_CMD_DROP], actor_drop)
+ || try_func_name(ta, s[S_CMD_USE], actor_use)))
+ {
+ err_line(1, "Invalid action function name.");
+ return 1;
+ } /* Legal worlds have at least one thing action for waiting. */
+ if (world.exists)
+ {
+ world.exists = 0 != get_thing_action_id_by_name(s[S_CMD_WAIT]);
+ }
+ }
+ else if (parse_val(tok0, tok1, s[S_CMD_THINGACTION], '8', (char *) &id))
+ {
+ ta = get_thing_action(id);
+ if (!ta)
+ {
+ ta = add_thing_action(id);
+ }
+ }
+ else
+ {
+ return 0;
+ }
+ return 1;
+}
+
+
+
+static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t)
+{
+ uint8_t type;
+ if (parse_val(tok0, tok1, s[S_CMD_T_TYPE], '8', (char *) &type))
+ {
+ struct ThingType * tt = get_thing_type(type);
+ if (!err_line(!tt, "Thing type does not exist."))
+ {
+ t->type = type;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+
+
+static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t)
+{
+ uint8_t command;
+ if (parse_val(tok0, tok1, s[S_CMD_T_COMMAND], '8', (char *) &command))
+ {
+ if (!command)
+ {
+ t->command = command;
+ return 1;
+ }
+ struct ThingAction * ta = world.thing_actions;
+ for (; ta && command != ta->id; ta = ta->next);
+ if (!err_line(!ta, "Thing action does not exist."))
+ {
+ t->command = command;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+
+
+static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t)
+{
+ char axis = 0;
+ if (!strcmp(tok0, s[S_CMD_T_POSY]))
+ {
+ axis = 'y';
+ }
+ else if (!strcmp(tok0, s[S_CMD_T_POSX]))
+ {
+ axis = 'x';
+ }
+ if (axis && !parsetest_int(tok1, '8'))
+ {
+ uint8_t length = atoi(tok1);
+ char * err = "Position is outside of map.";
+ if (!err_line(length >= world.map.length, err))
+ {
+ if ('y' == axis)
+ {
+ t->pos.y = length;
+ }
+ else if ('x' == axis)
+ {
+ t->pos.x = length;
+ }
+ free(t->fov_map);
+ if (world.exists && t->lifepoints)
+ {
+ t->fov_map = build_fov_map(t);
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+
+
+
+static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t)
+{
+ uint8_t id;
+ if (parse_val(tok0, tok1, s[S_CMD_T_CARRIES], '8', (char *) &id))
+ {
+ if (!err_line(id == t->id, "Thing cannot carry itself."))
+ {
+ struct Thing * o = get_thing(world.things, id, 0);
+ if (!err_line(!o, "Thing not available for carrying."))
+ {
+ own_thing(&(t->owns), &world.things, id);
+ o->pos = t->pos;
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+
+
+
+static uint8_t parse_thing_manipulation(char * tok0, char * tok1)
+{
+ static struct Thing * t = NULL;
+ if (!t &&
+ ( !strcmp(tok0, s[S_CMD_T_PROGRESS]) || !strcmp(tok0, s[S_CMD_T_TYPE])
+ || !strcmp(tok0, s[S_CMD_T_CARRIES]) || !strcmp(tok0, s[S_CMD_T_POSY])
+ || !strcmp(tok0, s[S_CMD_T_POSY]) || !strcmp(tok0, s[S_CMD_T_ARGUMENT])
+ || !strcmp(tok0, s[S_CMD_T_HP]) || !strcmp(tok0, s[S_CMD_T_COMMAND])))
+ {
+ err_line(1, "No thing defined to manipulate yet.");
+ return 1;
+ }
+ uint8_t id;
+ if ( parse_thing_type(tok0, tok1, t)
+ || parse_thing_command(tok0, tok1, t)
+ || parse_val(tok0,tok1, s[S_CMD_T_ARGUMENT], '8', (char *)&t->arg)
+ || parse_val(tok0,tok1, s[S_CMD_T_PROGRESS], '8', (char *)&t->progress)
+ || parse_val(tok0,tok1, s[S_CMD_T_HP], '8', (char *) &t->lifepoints)
+ || parse_position(tok0, tok1, t)
+ || parse_carry(tok0, tok1, t));
+ else if (parse_val(tok0, tok1, s[S_CMD_THING], 'i', (char *) &id))
+ {
+ t = get_thing(world.things, id, 1);
+ char * err = "No thing type found to initialize new thing.";
+ if (!t && !err_line(NULL == world.thing_types, err))
+ {
+ t = add_thing(id, world.thing_types->id, 0, 0);
+ if (world.exists && t->lifepoints)
+ {
+ t->fov_map = build_fov_map(t);
+ }
+ }
+ }
+ else
+ {
+ return 0;
+ }
+ return 1;
+}
+
+
+
+static uint8_t world_may_be_set_active()
+{
+ if (!get_thing_action_id_by_name(s[S_CMD_WAIT]))
+ {
+ err_line(1, "No thing action of name 'wait' found.");
+ return 0;
+ }
+ if (!get_player())
+ {
+ err_line(1, "No un-owned player thing (of id=0) found.");
+ return 0;
+ }
+ if (!world.map.cells)
+ {
+ err_line(1, "No map found.");
+ return 0;
+ }
+ return 1;
+}
+
+
+
+static uint8_t parse_world_active(char * tok0, char * tok1)
+{
+ char * f_name = "parse_world_active()";
+ if (!strcmp(tok0, s[S_CMD_WORLD_ACTIVE]) && !parsetest_int(tok1, '8'))
+ {
+ if (!parsetest_int(tok1, '8'))
+ {
+ uint8_t argument = atoi(tok1);
+ if (!argument)
+ {
+ if (!access(s[S_PATH_WORLDSTATE], F_OK))
+ {
+ int test = unlink(s[S_PATH_WORLDSTATE]);
+ exit_trouble(-1 == test, f_name, "unlink()");
+ }
+ world.exists = 0;
+ }
+ else if (world.exists)
+ {
+ err_line(1, "World already active.");
+ }
+ else if (world_may_be_set_active())
+ {
+ struct Thing * ti;
+ for (ti = world.things; ti; ti = ti->next)
+ {
+ if (ti->lifepoints)
+ {
+ if (ti->fov_map)
+ {
+ free(ti->fov_map);
+ }
+ ti->fov_map = build_fov_map(ti);
+ }
+ }
+ world.exists = 1;
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+
+static uint8_t set_map_length(char * tok0, char * tok1)
+{
+ if (!strcmp(tok0, s[S_CMD_MAPLENGTH]) && !parsetest_int(tok1, 'u'))
+ {
+ uint16_t argument = atoi(tok1);
+ if (argument < 1 || argument > 256)
+ {
+ err_line(1, "Value must be >= 1 and <= 256.");
+ return 1;
+ }
+ world.exists = 0;
+ free_things(world.things);
+ free(world.map.cells);
+ world.map.cells = NULL; /* Since remake_map() runs free() on this. */
+ world.map.length = argument;
+ return 1;
+ }
+ return 0;
+}
+
+
+
+extern uint8_t parse_god_command_1arg(char * tok0, char * tok1)
+{
+ if ( parse_thingtype_manipulation(tok0, tok1)
+ || parse_thingaction_manipulation(tok0, tok1)
+ || parse_thing_manipulation(tok0, tok1)
+ || set_map_length(tok0,tok1)
+ || parse_val(tok0,tok1,s[S_CMD_SEED_RAND],'U', (char *)&world.seed)
+ || parse_val(tok0,tok1,s[S_CMD_TURN],'u',(char *)&world.turn)
+ || parse_val(tok0,tok1,s[S_CMD_PLAYTYPE],'8',(char *)&world.player_type)
+ || parse_world_active(tok0, tok1));
+ else if (parse_val(tok0,tok1,s[S_CMD_SEED_MAP],'U',(char *)&world.seed_map))
+
+ {
+ remake_map();
+ }
+ else if (parse_val(tok0, tok1, s[S_CMD_MAKE_WORLD],'U',(char *)&world.seed))
+ {
+ uint8_t test = remake_world();
+ err_line(1 == test, "No player type with start number of >0 defined.");
+ err_line(2 == test, "No thing action with name 'wait' defined.");
+ }
+ else
+ {
+ return 0;
+ }
+ return 1;
+}
--- /dev/null
+/* src/server/god_commands.h
+ *
+ * God commands and their interpretation by the server.
+ */
+
+
+
+#ifndef GOD_COMMANDS_H
+#define GOD_COMMANDS_H
+
+#include <stdint.h> /* uint8_t */
+
+
+/* Parse/apply god command "tok0" with argument "tok1". */
+extern uint8_t parse_god_command_1arg(char * tok1, char * tok2);
+
+
+
+#endif
-char * s[26];
+char * s[38];
s[S_PATH_RECORD] = "record";
s[S_PATH_SAVE] = "savefile";
s[S_CMD_MAKE_WORLD] = "MAKE_WORLD";
- s[S_CMD_DO_FOV] = "BUILD_FOVS";
+ s[S_CMD_WORLD_ACTIVE] = "WORLD_ACTIVE";
s[S_CMD_SEED_MAP] = "SEED_MAP";
s[S_CMD_SEED_RAND] = "SEED_RANDOMNESS";
s[S_CMD_TURN] = "TURN";
s[S_CMD_THING] = "THING";
- s[S_CMD_TYPE] = "TYPE";
- s[S_CMD_POS_Y] = "POS_Y";
- s[S_CMD_POS_X] = "POS_X";
- s[S_CMD_COMMAND] = "COMMAND";
- s[S_CMD_ARGUMENT] = "ARGUMENT";
- s[S_CMD_PROGRESS] = "PROGRESS";
- s[S_CMD_LIFEPOINTS] = "LIFEPOINTS";
- s[S_CMD_CARRIES] = "CARRIES";
+ s[S_CMD_T_TYPE] = "T_TYPE";
+ s[S_CMD_T_POSY] = "T_POSY";
+ s[S_CMD_T_POSX] = "T_POSX";
+ s[S_CMD_T_COMMAND] = "T_COMMAND";
+ s[S_CMD_T_ARGUMENT] = "T_ARGUMENT";
+ s[S_CMD_T_PROGRESS] = "T_PROGRESS";
+ s[S_CMD_T_HP] = "T_LIFEPOINTS";
+ s[S_CMD_T_CARRIES] = "T_CARRIES";
s[S_CMD_WAIT] = "wait";
s[S_CMD_MOVE] = "move";
s[S_CMD_PICKUP] = "pick_up";
s[S_CMD_DROP] = "drop";
s[S_CMD_USE] = "use";
s[S_FCN_SPRINTF] = "sprintf()";
+ s[S_CMD_THINGTYPE] = "THING_TYPE";
+ s[S_CMD_TT_CONSUM] = "TT_CONSUMABLE";
+ s[S_CMD_TT_STARTN] = "TT_START_NUMBER";
+ s[S_CMD_TT_HP] = "TT_LIFEPOINTS";
+ s[S_CMD_TT_SYMB] = "TT_SYMBOL";
+ s[S_CMD_TT_NAME] = "TT_NAME";
+ s[S_CMD_TT_CORPS] = "TT_CORPSE_ID";
+ s[S_CMD_THINGACTION] = "THING_ACTION";
+ s[S_CMD_TA_EFFORT] = "TA_EFFORT";
+ s[S_CMD_TA_NAME] = "TA_NAME";
+ s[S_CMD_MAPLENGTH] = "MAP_LENGTH";
+ s[S_CMD_PLAYTYPE] = "PLAYER_TYPE";
}
S_PATH_RECORD,
S_PATH_SAVE,
S_CMD_MAKE_WORLD,
- S_CMD_DO_FOV,
+ S_CMD_WORLD_ACTIVE,
S_CMD_SEED_MAP,
S_CMD_SEED_RAND,
S_CMD_TURN,
S_CMD_THING,
- S_CMD_TYPE,
- S_CMD_POS_Y,
- S_CMD_POS_X,
- S_CMD_COMMAND,
- S_CMD_ARGUMENT,
- S_CMD_PROGRESS,
- S_CMD_LIFEPOINTS,
- S_CMD_CARRIES,
+ S_CMD_T_TYPE,
+ S_CMD_T_POSY,
+ S_CMD_T_POSX,
+ S_CMD_T_COMMAND,
+ S_CMD_T_ARGUMENT,
+ S_CMD_T_PROGRESS,
+ S_CMD_T_HP,
+ S_CMD_T_CARRIES,
S_CMD_WAIT,
S_CMD_MOVE,
S_CMD_PICKUP,
S_CMD_DROP,
S_CMD_USE,
- S_FCN_SPRINTF
+ S_FCN_SPRINTF,
+ S_CMD_THINGTYPE,
+ S_CMD_TT_CONSUM,
+ S_CMD_TT_STARTN,
+ S_CMD_TT_HP,
+ S_CMD_TT_SYMB,
+ S_CMD_TT_NAME,
+ S_CMD_TT_CORPS,
+ S_CMD_THINGACTION,
+ S_CMD_TA_EFFORT,
+ S_CMD_TA_NAME,
+ S_CMD_MAPLENGTH,
+ S_CMD_PLAYTYPE
};
extern void init_strings();
-extern char * s[26];
+extern char * s[38];
#include <sys/stat.h> /* mkdir() */
#include <sys/types.h> /* defines pid_t, time_t */
#include <time.h> /* time() */
-#include <unistd.h> /* optarg, getopt(), access(), unlink(), getpid() */
+#include <unistd.h> /* optarg, getopt(), access(), getpid() */
+#include "../common/parse_file.h" /* err_line_zero(), err_line_inc() */
#include "../common/readwrite.h" /* try_fopen(), try_fclose(), textfile_width(),
* try_fgets(), try_fwrite(),
* detect_atomic_leftover()
#include "hardcoded_strings.h" /* s */
#include "map.h" /* remake_map() */
#include "things.h" /* Thing, ThingType, free_things(), add_things(),
- * get_player()
+ * get_thing_id_action_id_by_name()
*/
#include "run.h" /* obey_msg(), io_loop() */
#include "world.h" /* global world */
+/* Pass to obey_msg() lines from file at "path", on "record" write to same. Do
+ * not pass lines that consist only of a newline character. Transform newline
+ * in the line passed to \0.
+ */
+static void obey_lines_from_file(char * path, uint8_t record);
+
/* Replay game from record file up to the turn named in world.replay, then turn
* over to manual replay via io_loop().
*/
static void replay_game();
+/* Return 1 if the type defined by world.player_type has a .start_n of 0.
+ * Return 2 if no thing action with .name of s[S_CMD_WAIT] is defined.
+ * Else, return 0.
+ */
+static uint8_t world_cannot_be_made();
+
+
+static void obey_lines_from_file(char * path, uint8_t record)
+{
+ char * f_name = "obey_lines_from_file()";
+ FILE * file = try_fopen(path, "r", f_name);
+ uint32_t linemax = textfile_width(file);
+ char * line = try_malloc(linemax + 1, f_name);
+ while (NULL != try_fgets(line, linemax + 1, file, f_name))
+ {
+ if (strlen(line))
+ {
+ if (strcmp("\n", line))
+ {
+ char * nl = strchr(line, '\n');
+ if (nl)
+ {
+ *nl = '\0';
+ }
+ obey_msg(line, record, 1);
+ }
+ err_line_inc();
+ }
+ }
+ free(line);
+ try_fclose(file, f_name);
+}
+
static void replay_game()
while ( world.turn < world.replay
&& NULL != try_fgets(line, linemax + 1, file, f_name))
{
- obey_msg(line, 0);
+ obey_msg(line, 0, 1);
+ err_line_inc();
}
uint8_t end = 0;
while (!io_loop())
end = (NULL == try_fgets(line, linemax + 1, file, f_name));
if (!end)
{
- obey_msg(line, 0);
+ obey_msg(line, 0, 1);
+ err_line_inc();
}
}
}
+static uint8_t world_cannot_be_made()
+{
+ uint8_t player_will_be_generated = 0;
+ struct ThingType * tt;
+ for (tt = world.thing_types; NULL != tt; tt = tt->next)
+ {
+ if (world.player_type == tt->id)
+ {
+ player_will_be_generated = 0 < tt->start_n;
+ break;
+ }
+ }
+ if (!player_will_be_generated)
+ {
+ return 1;
+ }
+ if (!get_thing_action_id_by_name(s[S_CMD_WAIT]))
+ {
+ return 2;
+ }
+ return 0;
+}
+
+
+
extern void obey_argv(int argc, char * argv[])
{
int opt;
-extern void remake_world()
+extern uint8_t remake_world()
{
- char * f_name = "remake_world()";
+ uint8_t test = world_cannot_be_made();
+ if (test)
+ {
+ return test;
+ }
free(world.log);
world.log = NULL; /* thing_actions.c's update_log() checks for this. */
world.seed_map = world.seed;
free_things(world.things);
- world.do_update = 1;
remake_map();
struct ThingType * tt;
for (tt = world.thing_types; NULL != tt; tt = tt->next)
add_things(tt->id, tt->start_n);
}
}
- set_cleanup_flag(CLEANUP_THINGS);
struct Thing * t;
for (t = world.things; NULL != t; t = t->next)
{
t->fov_map = t->lifepoints ? build_fov_map(t) : NULL;
}
- if (!world.replay && !access(s[S_PATH_RECORD], F_OK))
- {
- exit_trouble(unlink(s[S_PATH_RECORD]), f_name, "unlink()");
- }
world.turn = 1;
+ world.do_update = 1;
+ world.exists = 1;
+ return 0;
}
char * f_name = "run_game()";
detect_atomic_leftover(s[S_PATH_SAVE]);
detect_atomic_leftover(s[S_PATH_RECORD]);
+ err_line_zero();
if (world.replay)
{
replay_game();
return;
}
- char * path_savefile = s[S_PATH_SAVE];
- if (!access(path_savefile, F_OK))
+ if (!access(s[S_PATH_SAVE], F_OK))
{
- FILE * file = try_fopen(path_savefile, "r", f_name);
- uint32_t linemax = textfile_width(file);
- char * line = try_malloc(linemax + 1, f_name);
- while (NULL != try_fgets(line, linemax + 1, file, f_name))
- {
- if (strlen(line) && strcmp("\n", line))
- {
- obey_msg(line, 0);
- }
- }
- free(line);
- try_fclose(file, f_name);
+ obey_lines_from_file(s[S_PATH_SAVE], 0);
}
else
{
+ char * err = "No world config file from which to start a new world.";
+ exit_err(access(s[S_PATH_CONFIG], F_OK), err);
+ obey_lines_from_file(s[S_PATH_CONFIG], 1);
+ err_line_zero();
char * command = s[S_CMD_MAKE_WORLD];
char * msg = try_malloc(strlen(command) + 1 + 11 + 1, f_name);
int test = sprintf(msg, "%s %d", command, (int) time(NULL));
exit_trouble(test < 0, f_name, s[S_FCN_SPRINTF]);
- obey_msg(msg, 1);
+ obey_msg(msg, 1, 1);
free(msg);
}
+ err_line_zero();
io_loop();
}
#ifndef INIT_H
#define INIT_H
-#include <stdint.h> /* uint32_t */
+#include <stdint.h> /* uint8_t */
extern void setup_server_io();
/* Dissolves old game world if it exists, generates a new one from world.seed.
- * Unlinks any pre-existing record file.
- *
- * Thing (action) definitions read in from server config directory are not
- * affected. The map is populated accordingly. world.turn is set to 1, as is
- * world.do_update, so that io_round() is told to update the worldstate file.
+ * The map is populated according to world.thing_types start numbers. world.turn
+ * is set to 1, as is .exists and .do_update, so that io_round() is told to
+ * update the worldstate file. Returns 0 on success, and if the world cannot be
+ * generated 1 since there is no player type or it has .n_start of 0, 2 if no
+ * "wait" thing action is defined.
*/
-extern void remake_world();
+extern uint8_t remake_world();
/* Create a game world state, then enter play or replay mode.
*
* If replay mode is called for, try for the record file and follow its commands
+ up to the turn specified by the user, then enter manual replay. Otherwise,
* start into play mode after having either recreated a game world state from
- * the savefile, or, if none exists, having created a new world with the
- * MAKE_WORLD command. Manual replay as well as play mode take place inside
- * io_loop().
+ * the savefile, or, if none exists, having created a new world with first
+ * following the commands from the world config file, then running the
+ * MAKE_WORLD command. Manual replay as well as manual play mode take place
+ * inside io_loop().
*/
extern void run_game();
#include <limits.h> /* PIPE_BUF */
#include <stddef.h> /* size_t, NULL */
#include <stdint.h> /* uint8_t, uint16_t, uint32_t */
-#include <stdio.h> /* defines EOF, FILE, sprintf() */
+#include <stdio.h> /* defines EOF, FILE, sprintf(), fprintf() */
#include <stdlib.h> /* free() */
-#include <string.h> /* strlen(), memcpy(), memset() */
+#include <string.h> /* strlen(), memcpy(), memset(), strchr() */
#include <sys/types.h> /* time_t */
#include <time.h> /* time(), nanosleep() */
#include "../common/readwrite.h" /* atomic_write_start(), atomic_write_finish(),
#include "field_of_view.h" /* VISIBLE */
#include "hardcoded_strings.h" /* s */
#include "map.h" /* yx_to_map_pos() */
-#include "things.h" /* Thing, ThingType, get_thing_type(), get_player() */
+#include "things.h" /* Thing, ThingType, ThingAction, get_thing_type(),
+ * get_player()
+ */
#include "world.h" /* global world */
/* Write to "file" god commands (one per line) to recreate thing "t". */
static void write_key_value(FILE * file, char * key, uint32_t value);
+static void write_key_string(FILE * file, char * key, char * string);
/* Write to "file" \n-delimited line of "key" + space + "value" as string. */
static void write_thing(FILE * file, struct Thing * t);
+static void write_key_string(FILE * file, char * key, char * string)
+{
+ char * f_name = "write_key_string()";
+ try_fwrite(key, strlen(key), 1, file, f_name);
+ try_fputc(' ', file, f_name);
+ uint8_t contains_space = NULL != strchr(string, ' ');
+ if (contains_space)
+ {
+ try_fputc('\'', file, f_name);
+ }
+ try_fwrite(string, strlen(string), 1, file, f_name);
+ if (contains_space)
+ {
+ try_fputc('\'', file, f_name);
+ }
+ try_fputc('\n', file, f_name);
+}
+
+
+
static void write_thing(FILE * file, struct Thing * t)
{
char * f_name = "write_thing()";
write_thing(file, o);
}
write_key_value(file, s[S_CMD_THING], t->id);
- write_key_value(file, s[S_CMD_TYPE], t->type);
- write_key_value(file, s[S_CMD_POS_Y], t->pos.y);
- write_key_value(file, s[S_CMD_POS_X], t->pos.x);
- write_key_value(file, s[S_CMD_COMMAND], t->command);
- write_key_value(file, s[S_CMD_ARGUMENT], t->arg);
- write_key_value(file, s[S_CMD_PROGRESS], t->progress);
- write_key_value(file, s[S_CMD_LIFEPOINTS], t->lifepoints);
+ write_key_value(file, s[S_CMD_T_TYPE], t->type);
+ write_key_value(file, s[S_CMD_T_POSY], t->pos.y);
+ write_key_value(file, s[S_CMD_T_POSX], t->pos.x);
+ write_key_value(file, s[S_CMD_T_COMMAND], t->command);
+ write_key_value(file, s[S_CMD_T_ARGUMENT], t->arg);
+ write_key_value(file, s[S_CMD_T_PROGRESS], t->progress);
+ write_key_value(file, s[S_CMD_T_HP], t->lifepoints);
for (o = t->owns; o; o = o->next)
{
- write_key_value(file, s[S_CMD_CARRIES], o->id);
+ write_key_value(file, s[S_CMD_T_CARRIES], o->id);
}
try_fputc('\n', file, f_name);
}
}
atomic_write_finish(file, s[S_PATH_WORLDSTATE], path_tmp);
set_cleanup_flag(CLEANUP_WORLDSTATE);
- char * dot = ".\n";;
+ char * dot = ".\n";
try_fwrite(dot, strlen(dot), 1, world.file_out, f_name);
fflush(world.file_out);
}
char * f_name = "save_world()";
char * path_tmp;
FILE * file = atomic_write_start(s[S_PATH_SAVE], &path_tmp);
- write_key_value(file, s[S_CMD_DO_FOV], 0);
+ write_key_value(file, s[S_CMD_MAPLENGTH], world.map.length);
+ write_key_value(file, s[S_CMD_PLAYTYPE], world.player_type);
+ try_fputc('\n', file, f_name);
+ struct ThingAction * ta;
+ for (ta = world.thing_actions; ta; ta = ta->next)
+ {
+ write_key_value(file, s[S_CMD_THINGACTION], ta->id);
+ write_key_value(file, s[S_CMD_TA_EFFORT], ta->effort);
+ write_key_string(file, s[S_CMD_TA_NAME], ta->name);
+ try_fputc('\n', file, f_name);
+ }
+ struct ThingType * tt;
+ for (tt = world.thing_types; tt; tt = tt->next)
+ {
+ write_key_value(file, s[S_CMD_THINGTYPE], tt->id);
+ write_key_value(file, s[S_CMD_TT_STARTN], tt->start_n);
+ write_key_value(file, s[S_CMD_TT_HP], tt->lifepoints);
+ int test = fprintf(file, "%s %c\n", s[S_CMD_TT_SYMB], tt->char_on_map);
+ exit_trouble(test < 0, f_name, "fprintf()");
+ write_key_string(file, s[S_CMD_TT_NAME], tt->name);
+ write_key_value(file, s[S_CMD_TT_CONSUM], tt->consumable);
+ try_fputc('\n', file, f_name);
+ }
+ for (tt = world.thing_types; tt; tt = tt->next)
+ {
+ write_key_value(file, s[S_CMD_THINGTYPE], tt->id);
+ write_key_value(file, s[S_CMD_TT_CORPS], tt->corpse_id);
+ }
try_fputc('\n', file, f_name);
write_key_value(file, s[S_CMD_SEED_MAP], world.seed_map);
write_key_value(file, s[S_CMD_SEED_RAND], world.seed);
{
write_thing(file, t);
}
- write_key_value(file, s[S_CMD_DO_FOV], 1);
+ write_key_value(file, s[S_CMD_WORLD_ACTIVE], 1);
atomic_write_finish(file, s[S_PATH_SAVE], path_tmp);
}
#include <stdlib.h> /* exit() */
#include "../common/rexit.h" /* exit_err, set_cleanup_func() */
#include "cleanup.h" /* set_cleanup_flag(), cleanup() */
-#include "configfile.h" /* read_config_file() */
#include "hardcoded_strings.h" /* s */
#include "init.h" /* run_game(), obey_argv(), obey_argv(), setup_server_io() */
#include "world.h" /* struct World */
if (world.replay)
{
test = printf("Replay mode. Auto-replaying up to turn %d.\n",
- world.replay);
+ world.replay);
exit_err(-1 == test, printf_err);
}
}
+ world.map.length = 64; /* Just a sane default value. */
- /* Init config file and server i/o files. */
- read_config_file();
+ /* Init server i/o, Enter play or replay mode loops, then leave properly. */
setup_server_io();
-
- /* Enter play or replay mode loops, then leave properly. */
run_game();
cleanup();
exit(EXIT_SUCCESS);
#include <stddef.h> /* NULL */
#include <stdint.h> /* uint8_t, uint16_t, uint32_t */
#include <stdio.h> /* FILE, printf(), fflush() */
-#include <stdlib.h> /* free(), atoi() */
-#include <string.h> /* strlen(), strcmp() strncmp(), strdup() */
+#include <stdlib.h> /* free() */
+#include <string.h> /* strlen(), strcmp(), strncmp(), strdup() */
#include <unistd.h> /* access() */
#include "../common/parse_file.h" /* set_err_line_options(), token_from_line(),
- * err_line()
+ * err_line(), err_line_inc(), parse_val()
*/
#include "../common/readwrite.h" /* try_fopen(), try_fcose(), try_fwrite(),
* try_fgets(), textfile_width(), try_fputc()
#include "../common/rexit.h" /* exit_trouble(), exit_err() */
#include "../common/try_malloc.h" /* try_malloc() */
#include "ai.h" /* ai() */
-#include "cleanup.h" /* set_cleanup_flag(), unset_cleanup_flag() */
-#include "field_of_view.h" /* build_fov_map() */
+#include "cleanup.h" /* unset_cleanup_flag() */
+#include "god_commands.h" /* parse_god_command_1arg() */
#include "hardcoded_strings.h" /* s */
-#include "init.h" /* remake_world() */
#include "io.h" /* io_round(), save_world() */
-#include "map.h" /* remake_map() */
-#include "thing_actions.h" /* ThingAction */
-#include "things.h" /* Thing, get_thing(), own_thing(), add_thing(),
- * get_thing_action_id_by_name(), get_player()
- */
+#include "things.h" /* Thing, get_thing_action_id_by_name(), get_player() */
#include "world.h" /* world */
-/* Parse/apply god command in "tok0"/"tok1" on "t" owning another thing. */
-static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t);
-
-/* Parse/apply god commansd in "tok0"/"tok1" on positioning a thing "t". */
-static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t);
-
-/* Parse/apply god command in "tok0"/"tok1" oo setting "t"'s thing type. */
-static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t);
-
-/* Parse/apply god command in "tok0"/"tok1" on setting up thing "t". */
-static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t);
-
-/* Parse/apply god command on enabling/disabling generation of fields of view on
- * god commands that may affect them, via static global "do_fov". On enabling,
- * (re-)generate all animate things' fields of view.
- */
-static uint8_t parse_do_fov(char * tok0, char * tok1);
+/* If "string" and "comparand" match in string, set "c_to_set" to value." */
+static uint8_t set_char_by_string_comparison(char * string, char * comparand,
+ char * c_to_set, char value);
-/* Parse/apply god command in "tok0"/"tok1" manipulating a thing's state. */
-static uint8_t parse_thing_manipulation(char * tok0, char * tok1);
+/* Return 1 on world.exists, else 0 and err_line() appropriate error message. */
+static uint8_t player_commands_allowed();
/* Parse player command "tok0" with no argument to player action, comment on
* invalidity of non-zero "tok1" (but do not abort in that case).
*/
static uint8_t parse_player_command_0arg(char * tok0, char * tok1);
-/* If "string" and "comparand" match in string, set "c_to_set" to value." */
-static uint8_t set_char_by_string_comparison(char * string, char * comparand,
- char * c_to_set, char value);
-
/* Parse player command "tok0" with one argument "tok1" to player action. */
static uint8_t parse_player_command_1arg(char * tok0, char * tok1);
-/* Parse/apply commadn "tok0" with argument "tok1" and test the line for further
- * tokens, commenting on their invalidity (but don't abort on findingthem).
+/* Parse/apply command "tok0" with argument "tok1" and test the line for further
+ * tokens, commenting on their invalidity (but don't abort on finding them).
*/
static uint8_t parse_command_1arg(char * tok0, char * tok1);
-/* Do god commands to create / position things generate their fields of view? */
-static uint8_t do_fov = 0;
-
-
-
-static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t)
-{
- uint8_t id;
- if (parse_val(tok0, tok1, s[S_CMD_CARRIES], '8', (char *) &id))
- {
- if (!err_line(id == t->id, "Thing cannot carry itself."))
- {
- struct Thing * o = get_thing(world.things, id, 0);
- if (!err_line(!o, "Thing cannot carry thing that does not exist."))
- {
- own_thing(&(t->owns), &world.things, id);
- o->pos = t->pos;
- }
- }
- return 1;
- }
- return 0;
-}
-
-
-
-static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t)
-{
- char axis = 0;
- if (!strcmp(tok0, s[S_CMD_POS_Y]))
- {
- axis = 'y';
- }
- else if (!strcmp(tok0, s[S_CMD_POS_X]))
- {
- axis = 'x';
- }
- if (axis && !parsetest_int(tok1, '8'))
- {
- uint8_t length = atoi(tok1);
- char * err = "Position is outside of map.";
- if (!err_line(length >= world.map.length, err))
- {
- if ('y' == axis)
- {
- t->pos.y = length;
- }
- else if ('x' == axis)
- {
- t->pos.x = length;
- }
- free(t->fov_map);
- t->fov_map= do_fov && t->lifepoints ? build_fov_map(t) : t->fov_map;
- }
- return 1;
- }
- return 0;
-}
-
-
-
-static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t)
-{
- uint8_t type;
- if (parse_val(tok0, tok1, s[S_CMD_TYPE], '8', (char *) &type))
- {
- struct ThingType * tt = world.thing_types;
- for (; NULL != tt && type != tt->id; tt = tt->next);
- if (!err_line(!tt, "Thing type does not exist."))
- {
- t->type = type;
- }
- return 1;
- }
- return 0;
-}
-
-
-static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t)
-{
- uint8_t command;
- if (parse_val(tok0, tok1, s[S_CMD_COMMAND], '8', (char *) &command))
- {
- if (!command)
- {
- t->command = command;
- return 1;
- }
- struct ThingAction * ta = world.thing_actions;
- for (; NULL != ta && command != ta->id; ta = ta->next);
- if (!err_line(!ta, "Thing action does not exist."))
- {
- t->command = command;
- }
- return 1;
- }
- return 0;
-}
-
-
-
-static uint8_t parse_do_fov(char * tok0, char * tok1)
+static uint8_t set_char_by_string_comparison(char * string, char * comparand,
+ char * c_to_set, char value)
{
- if (parse_val(tok0, tok1, s[S_CMD_DO_FOV], '8', (char *) &do_fov))
+ if (!strcmp(string, comparand))
{
- if (do_fov)
- {
- struct Thing * ti;
- for (ti = world.things; ti; ti = ti->next)
- {
- ti->fov_map = ti->lifepoints ? build_fov_map(ti) : ti->fov_map;
- }
- }
+ * c_to_set = value;
return 1;
}
return 0;
-static uint8_t parse_thing_manipulation(char * tok0, char * tok1)
+static uint8_t player_commands_allowed()
{
- uint8_t id;
- static struct Thing * t = NULL;
- if (t && ( parse_thing_type(tok0, tok1, t)
- || parse_thing_command(tok0, tok1, t)
- || parse_val(tok0, tok1, s[S_CMD_ARGUMENT], '8', (char *)&t->arg)
- || parse_val(tok0,tok1,s[S_CMD_PROGRESS],'8',(char *)&t->progress)
- || parse_val(tok0, tok1, s[S_CMD_LIFEPOINTS], '8',
- (char *) &t->lifepoints)
- || parse_position(tok0, tok1, t)
- || parse_carry(tok0, tok1, t)));
- else if (parse_val(tok0, tok1, s[S_CMD_THING], '8', (char *) &id))
- {
- t = get_thing(world.things, id, 1);
- if (!t)
- {
- t = add_thing(id, 0, 0);
- set_cleanup_flag(CLEANUP_THINGS);
- t->fov_map= do_fov && t->lifepoints ? build_fov_map(t) : t->fov_map;
- }
- }
- else
+ if (!world.exists)
{
+ err_line(1, "No world exists in which to run player commands.");
return 0;
}
return 1;
struct Thing * player = get_player();
if (!strcmp(tok0, s[S_CMD_WAIT]) || !strcmp(tok0, s[S_CMD_PICKUP]))
{
- player->command = get_thing_action_id_by_name(tok0);
- player->arg = 0;
- turn_over();
- err_line (NULL != tok1, "No arguments expected, ignoring arguments.");
- return 1;
- }
- return 0;
-}
-
-
-
-static uint8_t set_char_by_string_comparison(char * string, char * comparand,
- char * c_to_set, char value)
-{
- if (!strcmp(string, comparand))
- {
- * c_to_set = value;
+ if (player_commands_allowed())
+ {
+ player->command = get_thing_action_id_by_name(tok0);
+ player->arg = 0;
+ turn_over();
+ err_line (NULL != tok1, "No arguments expected, ignoring them.");
+ }
return 1;
}
return 0;
static uint8_t parse_player_command_1arg(char * tok0, char * tok1)
{
struct Thing * player = get_player();
- if (
- parse_val(tok0, tok1, s[S_CMD_DROP], '8', (char *) &player->arg)
- || parse_val(tok0, tok1, s[S_CMD_USE], '8', (char *) &player->arg))
+ if ( ( parse_val(tok0, tok1, s[S_CMD_DROP], '8', (char *) &player->arg)
+ || parse_val(tok0, tok1, s[S_CMD_USE], '8', (char *) &player->arg))
+ && player_commands_allowed())
{
player->command = get_thing_action_id_by_name(tok0);
turn_over();
}
- else if (!strcmp(tok0, s[S_CMD_MOVE]))
+ else if (!strcmp(tok0, s[S_CMD_MOVE]) && player_commands_allowed())
{
char dir = '\0';
if (!( set_char_by_string_comparison(tok1, "east", &dir, 'd')
static uint8_t parse_command_1arg(char * tok0, char * tok1)
{
char * tok2 = token_from_line(NULL);
- if ( parse_thing_manipulation(tok0, tok1)
- || parse_player_command_1arg(tok0, tok1)
- || parse_val(tok0, tok1, s[S_CMD_SEED_RAND], 'U', (char *) &world.seed)
- || parse_val(tok0, tok1, s[S_CMD_TURN], 'u', (char *) &world.turn)
- || parse_do_fov(tok0, tok1));
- else if (parse_val(tok0,tok1,s[S_CMD_SEED_MAP],'U',(char *)&world.seed_map))
-
- {
- remake_map();
- }
- else if (parse_val(tok0, tok1, s[S_CMD_MAKE_WORLD],'U',(char *)&world.seed))
- {
- remake_world();
- }
+ if ( parse_player_command_1arg(tok0, tok1)
+ || parse_god_command_1arg(tok0, tok1));
else
{
return 0;
ai(thing);
}
thing->progress++;
- struct ThingAction * ta = world.thing_actions;
- while (ta->id != thing->command)
- {
- ta = ta->next;
- }
+
+ struct ThingAction * ta = get_thing_action(thing->command);
if (thing->progress == ta->effort)
{
ta->func(thing);
-extern void obey_msg(char * msg, uint8_t do_record)
+extern void obey_msg(char * msg, uint8_t do_record, uint8_t do_verbose)
{
- set_err_line_options("Trouble with message: ", msg, 0, 0);
+ char * f_name = "obey_msg()";
+ if (world.is_verbose && do_verbose)
+ {
+ exit_trouble(-1 == printf("Input: %s\n", msg), f_name, "printf()");
+ }
+ set_err_line_options("Trouble with message: ", msg, 0);
char * msg_copy = strdup(msg);
+ if (msg[0] == 'm')
+ {
+ int a = 5;
+ a = a;
+ }
char * tok0 = token_from_line(msg_copy);
if (NULL != tok0)
{
if ( parse_player_command_0arg(tok0, tok1)
|| (tok1 && parse_command_1arg(tok0, tok1)))
{
- world.do_update = 1;
+ if (world.exists)
+ {
+ world.do_update = 1;
+ }
if (do_record)
{
save_world();
return;
}
}
- err_line(1, "Unknown command or bad number of tokens.");
+ err_line(1, "Unknown command/argument or bad number of tokens.");
free(msg_copy);
}
free(msg);
return 0;
}
- obey_msg(msg, 1);
+ obey_msg(msg, 1, 0);
free(msg);
}
}
/* Try parsing "msg" into a command to apply, and apply it. Record commands to
- * the file at world.path_record if "do_record" is set.
+ * the file at world.path_record if "do_record" is set, and output them to
+ * stdout if "do_verbose" and world.is_verbose are set.
*/
-extern void obey_msg(char * msg, uint8_t do_record);
+extern void obey_msg(char * msg, uint8_t do_record, uint8_t do_verbose);
/* Loop for receiving commands via io_round() and acting on them. Exits with 1
* on "QUIT" command. In replay mode, exits with 0 on each non-"QUIT" command.
#include <stdint.h> /* uint8_t, uint16_t */
#include <stdio.h> /* sprintf() */
#include <stdlib.h> /* free() */
-#include <string.h> /* strlen(), strcmp(), memcpy(), strncmp() */
-#include "../common/rexit.h" /* exit_err(), exit_trouble() */
+#include <string.h> /* strlen(), memcpy(), strncmp() */
+#include "../common/rexit.h" /* exit_trouble() */
#include "../common/try_malloc.h" /* try_malloc() */
#include "../common/yx_uint8.h" /* struct yx_uint8 */
#include "field_of_view.h" /* build_fov_map() */
#include "hardcoded_strings.h" /* s */
-#include "things.h" /* structs Thing, ThingType, get_player(), own_thing(),
+#include "things.h" /* Thing, ThingType, get_player(), own_thing(),
* set_thing_position(), get_thing_type()
*/
#include "map.h" /* is_passable() */
-extern void free_thing_actions(struct ThingAction * ta)
-{
- if (NULL == ta)
- {
- return;
- }
- free(ta->name);
- free_thing_actions(ta->next);
- free(ta);
-}
-
-
-
-extern uint8_t get_thing_action_id_by_name(char * name)
-{
- struct ThingAction * ta = world.thing_actions;
- while (NULL != ta)
- {
- if (0 == strcmp(ta->name, name))
- {
- break;
- }
- ta = ta->next;
- }
- exit_err(NULL == ta, "get_thing_action_id_by_name() did not find action.");
- return ta->id;
-}
-
-
-
extern void actor_wait(struct Thing * t)
{
if (t == get_player())
#ifndef THING_ACTIONS_H
#define THING_ACTIONS_H
-#include <stdint.h> /* uint8_t */
struct Thing;
-struct ThingAction
-{
- uint8_t id; /* identifies action in Thing.command; therefore must be >0 */
- struct ThingAction * next;
- void (* func) (struct Thing *); /* function called after .effort turns */
- char * name; /* human-readable identifier */
- uint8_t effort; /* how many turns the action takes */
-};
-
-
-
-/* Free ThingAction * chain starting at "ta". */
-extern void free_thing_actions(struct ThingAction * ta);
-
-/* Return world.thing_actions ThingAction.id for "name". */
-extern uint8_t get_thing_action_id_by_name(char * name);
-
/* Actor "t" does nothing. */
extern void actor_wait(struct Thing * t);
/* src/server/things.c */
+#define _POSIX_C_SOURCE 200809L /* strdup() */
#include "things.h"
-#include <stddef.h> /* NULL */
-#include <stdint.h> /* uint8_t, uint16_t, UINT8_MAX, UINT16_MAX */
+#include <stddef.h> /* NULL, size_t */
+#include <stdint.h> /* uint8_t, uint16_t, int16_t, UINT8_MAX, UINT16_MAX */
#include <stdlib.h> /* free() */
-#include <string.h> /* memset(), strlen() */
-#include "../common/rexit.h" /* exit_err(), exit_trouble() */
+#include <string.h> /* memset(), strcmp(), strdup() */
+#include "../common/rexit.h" /* exit_err() */
#include "../common/try_malloc.h" /* try_malloc() */
#include "../common/yx_uint8.h" /* yx_uint8 */
+#include "cleanup.h" /* set_cleanup_flag() */
#include "hardcoded_strings.h" /* s */
#include "map.h" /* is_passable() */
#include "rrand.h" /* rrand() */
-#include "world.h" /* global world */
+#include "thing_actions.h" /* actor_wait */
+#include "world.h" /* world */
#include "yx_uint8.h" /* yx_uint8_cmp() */
+/* Used to treat structs Thing, ThingType and ThingAction the same. */
+struct NextAndId
+{
+ struct NextAndId * next;
+ uint8_t id;
+};
+
+
-/* Return lowest unused id for new thing. */
-static uint8_t get_lowest_unused_id();
+/* Return lowest unused id for new thing ("sel"==0), thing type ("sel"==1) or
+ * thing action ("sel"==2).
+ */
+static uint8_t get_unused_id(uint8_t sel);
+/* To linked list of NextAndId structs (or rather structs whose start region is
+ * compatible to it) starting at "start", add newly allocated element of
+ * "n_size" and an ID that is either "id" or, if "id" is <= UINT8_MAX and >=
+ * "id_start", get ID from get_unused_id("struct_id").
+ */
+static struct NextAndId * add_to_struct_list(size_t n_size, uint8_t start_id,
+ int16_t id, uint8_t struct_id,
+ struct NextAndId ** start);
-static uint8_t get_lowest_unused_id()
+
+static uint8_t get_unused_id(uint8_t sel)
{
uint8_t i = 0;
while (1)
{
- if (!get_thing(world.things, i, 1))
+ if ( (0 == sel && !get_thing(world.things, i, 1))
+ || (1 == sel && !get_thing_type(i))
+ || (2 == sel && !get_thing_action(i)))
{
return i;
}
- exit_err(i == UINT8_MAX, "No unused ID available to add new thing.");
+ exit_err(i == UINT8_MAX, "No unused ID available to add to ID list.");
i++;
}
}
-extern struct Thing * get_thing(struct Thing * ptr, uint8_t id, uint8_t deep)
+static struct NextAndId * add_to_struct_list(size_t n_size, uint8_t start_id,
+ int16_t id, uint8_t struct_id,
+ struct NextAndId ** start)
{
- while (1)
+ char * f_name = "add_to_struct_list()";
+ struct NextAndId * nai = try_malloc(n_size, f_name);
+ memset(nai, 0, n_size);
+ nai->id = (start_id<=id && id<=UINT8_MAX) ? id : get_unused_id(struct_id);
+ struct NextAndId ** nai_ptr_ptr = start;
+ for (; NULL != * nai_ptr_ptr; nai_ptr_ptr = &(*nai_ptr_ptr)->next);
+ *nai_ptr_ptr = nai;
+ return nai;
+}
+
+
+
+extern struct ThingAction * add_thing_action(int16_t id)
+{
+ struct ThingAction * ta;
+ ta = (struct ThingAction *) add_to_struct_list(sizeof(struct ThingAction),
+ 1, id, 2,
+ (struct NextAndId **)
+ &world.thing_actions);
+ set_cleanup_flag(CLEANUP_THING_ACTIONS);
+ ta->name = strdup(s[S_CMD_WAIT]);
+ ta->effort = 1;
+ ta->func = actor_wait;
+ return ta;
+}
+
+
+
+extern struct ThingType * add_thing_type(int16_t id)
+{
+ struct ThingType * tt;
+ tt = (struct ThingType *) add_to_struct_list(sizeof(struct ThingType),
+ 0, id, 1,
+ (struct NextAndId **)
+ &world.thing_types);
+ set_cleanup_flag(CLEANUP_THING_TYPES);
+ tt->name = strdup("(none)");
+ return tt;
+}
+
+
+
+extern struct Thing * add_thing(int16_t id, uint8_t type, uint8_t y, uint8_t x)
+{
+ struct Thing * t;
+ t = (struct Thing *) add_to_struct_list(sizeof(struct Thing), 0, id, 0,
+ (struct NextAndId **) &world.things);
+ struct ThingType * tt = get_thing_type(type);
+ set_cleanup_flag(CLEANUP_THINGS);
+ t->type = tt->id;
+ t->lifepoints = tt->lifepoints;
+ t->pos.y = y;
+ t->pos.x = x;
+ return t;
+}
+
+
+
+extern void free_thing_actions(struct ThingAction * ta)
+{
+ if (NULL == ta)
{
- if (NULL == ptr || id == ptr->id)
- {
- return ptr;
- }
- if (deep)
- {
- struct Thing * owned_thing = get_thing(ptr->owns, id, 1);
- if (NULL != owned_thing)
- {
- return ptr;
- }
- }
- ptr = ptr->next;
+ return;
}
+ free_thing_actions(ta->next);
+ free(ta->name);
+ free(ta);
}
-extern void free_thing_types(struct ThingType * tt_start)
+extern void free_thing_types(struct ThingType * tt)
{
- if (NULL == tt_start)
+ if (NULL == tt)
{
return;
}
- free_thing_types(tt_start->next);
- free(tt_start->name);
- free(tt_start);
+ free_thing_types(tt->next);
+ free(tt->name);
+ free(tt);
}
-extern struct Thing * add_thing(int16_t id, uint8_t type, uint8_t find_pos)
+extern void free_things(struct Thing * t)
{
- char * f_name = "add_thing()";
- struct ThingType * tt = get_thing_type(type);
- struct Thing * t = try_malloc(sizeof(struct Thing), f_name);
- memset(t, 0, sizeof(struct Thing));
- t->id = (0 <= id && id <= UINT8_MAX) ? id : get_lowest_unused_id();
- t->type = tt->id;
- t->lifepoints = tt->lifepoints;
- char * err = "Space to put thing on too hard to find. Map too small?";
- uint16_t i = 0;
- memset(&(t->pos), 0, sizeof(struct yx_uint8));
- while (find_pos)
+ if (NULL == t)
{
- struct yx_uint8 pos;
- for (pos.y = pos.x = 0; 0 == is_passable(pos); i++)
+ return;
+ }
+ free_things(t->owns);
+ free_things(t->next);
+ free(t->fov_map);
+ free(t);
+ if (t == world.things) /* So add_things()' NULL-delimited thing */
+ { /* iteration loop does not iterate over */
+ world.things = NULL; /* freed memory when called the first time */
+ } /* after world re-seeding. */
+}
+
+
+
+extern struct ThingAction * get_thing_action(uint8_t id)
+{
+ struct ThingAction * ta = world.thing_actions;
+ for (; NULL != ta && id != ta->id; ta = ta->next);
+ return ta;
+}
+
+
+
+extern struct ThingType * get_thing_type(uint8_t id)
+{
+ struct ThingType * tt = world.thing_types;
+ for (; NULL != tt && id != tt->id; tt = tt->next);
+ return tt;
+}
+
+
+
+extern uint8_t get_thing_action_id_by_name(char * name)
+{
+ struct ThingAction * ta = world.thing_actions;
+ while (NULL != ta)
+ {
+ if (0 == strcmp(ta->name, name))
{
- exit_err(UINT16_MAX == i, err);
- pos.y = rrand() % world.map.length;
- pos.x = rrand() % world.map.length;
+ break;
}
- struct Thing * t_ptr;
- uint8_t clear = 1;
- for (t_ptr = world.things; t_ptr != NULL; t_ptr = t_ptr->next)
+ ta = ta->next;
+ }
+ if (!ta)
+ {
+ return 0;
+ }
+ return ta->id;
+}
+
+
+
+extern struct Thing * get_thing(struct Thing * ptr, uint8_t id, uint8_t deep)
+{
+ while (1)
+ {
+ if (NULL == ptr || id == ptr->id)
{
- if (yx_uint8_cmp(&pos, &t_ptr->pos) && 0 != t_ptr->lifepoints)
- {
- clear = 0;
- break;
- }
+ return ptr;
}
- if (1 == clear)
+ if (deep)
{
- t->pos = pos;
- break;
+ struct Thing * owned_thing = get_thing(ptr->owns, id, 1);
+ if (NULL != owned_thing)
+ {
+ return ptr;
+ }
}
+ ptr = ptr->next;
}
- struct Thing ** t_ptr_ptr = &world.things;
- for (; NULL != * t_ptr_ptr; t_ptr_ptr = &(*t_ptr_ptr)->next);
- * t_ptr_ptr = t;
- return t;
}
-extern void add_things(uint8_t type, uint8_t n)
+extern struct Thing * get_player()
{
- uint8_t i;
- for (i = 0; i < n; i++)
- {
- add_thing(-1, type, 1);
- }
+ return get_thing(world.things, 0, 0);
}
-extern void free_things(struct Thing * t_start)
+extern void add_things(uint8_t type, uint8_t n)
{
- if (NULL == t_start)
+ uint8_t i;
+ for (i = 0; i < n; i++)
{
- return;
+ struct yx_uint8 pos;
+ while (1)
+ {
+ char * err = "Space to put thing on too hard to find."
+ "Map too small?";
+ uint16_t i_pos = 0;
+ for (pos.y = pos.x = 0; 0 == is_passable(pos); i_pos++)
+ {
+ exit_err(UINT16_MAX == i_pos, err);
+ pos.y = rrand() % world.map.length;
+ pos.x = rrand() % world.map.length;
+ }
+ struct Thing * t;
+ uint8_t clear = 1;
+ for (t = world.things; t; t = t->next)
+ {
+ if (yx_uint8_cmp(&pos, &t->pos) && 0 != t->lifepoints)
+ {
+ clear = 0;
+ break;
+ }
+ }
+ if (1 == clear)
+ {
+ break;
+ }
+ }
+ add_thing(-1, type, pos.y, pos.x);
}
- free_things(t_start->owns);
- free_things(t_start->next);
- free(t_start->fov_map);
- free(t_start);
- if (t_start == world.things) /* So add_things()' NULL-delimited thing */
- { /* iteration loop does not iterate over */
- world.things = NULL; /* freed memory when called the first time */
- } /* after world re-seeding. */
}
-extern struct Thing * get_player()
-{
- return get_thing(world.things, 0, 1);
-}
-
-
-
-extern struct ThingType * get_thing_type(uint8_t id)
-{
- char * f_name = "get_thing_type()";
- struct ThingType * tt = world.thing_types;
- for (; NULL != tt && id != tt->id; tt = tt->next);
- char * err_intro = "Requested thing type of unused ID ";
- uint16_t size = strlen(err_intro) + 3 + 1 + 1;
- char * err = try_malloc(size, f_name);
- exit_trouble(sprintf(err,"%s%d.",err_intro,id) < 0,f_name,s[S_FCN_SPRINTF]);
- exit_err(NULL == tt, err);
- free(err);
- return tt;
-}
-
-
-
extern void set_thing_position(struct Thing * t, struct yx_uint8 pos)
{
t->pos = pos;
/* src/server/things.h
*
- * Structs for things and their type definitions, and routines to initialize
- * these and load and save them from/to files.
+ * Structs for things and their type and action definitions, and routines to
+ * initialize these.
*/
#ifndef THINGS_H
#define THINGS_H
-#include <stdint.h> /* uint8_t */
+#include <stdint.h> /* uint8_t, int16_t */
#include "../common/yx_uint8.h" /* yx_uint8 structs */
struct Thing
{
- struct Thing * next; /* pointer to next one in things chain */
+ struct Thing * next;
+ uint8_t id; /* individual thing's unique identifier */
struct Thing * owns; /* chain of things owned / in inventory */
struct yx_uint8 pos; /* coordinate on map */
uint8_t * fov_map; /* map of the thing's field of view */
- uint8_t id; /* individual thing's unique identifier */
uint8_t type; /* ID of appropriate thing definition */
uint8_t lifepoints; /* 0: thing is inanimate; >0: hitpoints */
uint8_t command; /* thing's current action; 0 if none */
struct ThingType
{
- uint8_t id; /* thing type identifier / sets .type */
struct ThingType * next;
+ uint8_t id; /* thing type identifier / sets .type */
char char_on_map; /* thing symbol to appear on map */
char * name; /* string to describe thing in game log */
uint8_t corpse_id; /* type to change thing into upon destruction */
uint8_t start_n; /* how many of these does the map start with? */
};
+struct ThingAction
+{
+ struct ThingAction * next;
+ uint8_t id; /* identifies action in Thing.command; therefore must be >0 */
+ void (* func) (struct Thing *); /* function called after .effort turns */
+ char * name; /* human-readable identifier */
+ uint8_t effort; /* how many turns the action takes */
+};
-/* Return thing of "id" in chain at "ptr", search inventories too if "deep". */
-extern struct Thing * get_thing(struct Thing * ptr, uint8_t id, uint8_t deep);
-/* Free thing types chain starting at "tt_start". */
-extern void free_thing_types(struct ThingType * tt_start);
+/* Add thing action of "id" to world.thing_actions, with .name defaulting to
+ * s[S_CMD_WAIT], .func to actor_wait() and .effort to 1. If "id" is not >= 1
+ * and <= UINT8_MAX, use lowest unused id. Return thing action.
+ */
+extern struct ThingAction * add_thing_action(int16_t id);
-/* Add thing of "id" and "type" to map on random passable position (positions
- * which contain an actor are not deemed passable) if "find_pos", else on y=0,
- * x=0. If "id" is >= 0 and <= UINT8_MAX, use lowest unused id. Return thing.
+/* Add thing type of "id", with .corpse_id defaulting to "id" to
+ * world.thing_types, .name to "(none)" and the remaining values to 0. If "id"
+ * is not >= 0 and <= UINT8_MAX, use lowest unused id. Return thing type.
*/
-extern struct Thing * add_thing(int16_t id, uint8_t type, uint8_t find_pos);
+extern struct ThingType * add_thing_type(int16_t id);
-/* Add thing(s) ("n": how many?) of "type" to map on random position(s). New
- * animate things are never placed in the same square with other animate ones.
+/* Add thing of "id" and "type" on position of "y"/x" to world.things.If "id" is
+ * not >= 0 and <= UINT8_MAX, use lowest unused id. Return thing.
*/
-extern void add_things(uint8_t type, uint8_t n);
+extern struct Thing * add_thing(int16_t id, uint8_t type, uint8_t y, uint8_t x);
-/* Free things in things chain starting at "t_start. */
-extern void free_things(struct Thing * t_start);
+/* Free ThingAction/ThingType/Thing * chain starting at "ta"/"tt"/"t". */
+extern void free_thing_actions(struct ThingAction * ta);
+extern void free_thing_types(struct ThingType * tt);
+extern void free_things(struct Thing * t);
-/* Move thing of "id" from "source" inventory to "target" inventory. */
-extern void own_thing(struct Thing ** target, struct Thing ** source,
- uint8_t id);
+/* Return pointer to ThingAction/ThingType of "id", or NULL if none found. */
+extern struct ThingAction * get_thing_action(uint8_t id);
+extern struct ThingType * get_thing_type(uint8_t id);
-/* Get pointer to the Thing struct that represents the player. */
+/* Return world.thing_actions ThingAction.id for "name" or 0 if none found. */
+extern uint8_t get_thing_action_id_by_name(char * name);
+
+/* Return thing of "id" in chain at "ptr", search inventories too if "deep".
+ * Return NULL if nothing found.
+ */
+extern struct Thing * get_thing(struct Thing * ptr, uint8_t id, uint8_t deep);
+
+/* Get pointer to the non-owend Thing struct that represents the player, or NULL
+ * if none found.
+ */
extern struct Thing * get_player();
-/* Get pointer to the thing type of identifier "def_id". */
-extern struct ThingType * get_thing_type(uint8_t id);
+/* Add thing(s) ("n": how many?) of "type" to map on random passable
+ * position(s). New animate things are never placed in the same square with
+ * other animate ones.
+ */
+extern void add_things(uint8_t type, uint8_t n);
+
+/* Move thing of "id" from "source" inventory to "target" inventory. */
+extern void own_thing(struct Thing ** target, struct Thing ** source,
+ uint8_t id);
/* Move not only "t" to "pos", but also all things owned by it. */
extern void set_thing_position(struct Thing * t, struct yx_uint8 pos);
uint32_t seed_map; /* Map seed. */
uint16_t replay; /* Turn up to which to replay game. No replay if zero. */
uint16_t turn; /* Current game turn. */
- uint16_t do_update; /* Update worldstate file if !0. */
+ uint8_t do_update; /* Update worldstate file if !0. */
+ uint8_t exists; /* If !0, remake_world() has been run successfully. */
uint8_t player_type; /* Thing type that player will start as. */
uint8_t is_verbose; /* Should server send debugging info to stdout? */
};