From 08787351493beb2ad649e94d24eebca0e97192c8 Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Wed, 15 Oct 2014 03:14:50 +0200 Subject: [PATCH] Server: New command TT_PROLIFERATE sets chance for thing proliferation. --- README | 3 +- SERVER_COMMANDS | 8 +++- confserver/world | 1 + src/server/god_commands.c | 8 ++-- src/server/hardcoded_strings.c | 3 +- src/server/hardcoded_strings.h | 3 +- src/server/io.c | 1 + src/server/run.c | 81 +++++++++++++++++++++++++++------- src/server/things.c | 56 ++++++++++++++++++++++- src/server/things.h | 24 ++++++---- 10 files changed, 153 insertions(+), 35 deletions(-) diff --git a/README b/README index d50abd9..15fc87f 100644 --- a/README +++ b/README @@ -8,7 +8,8 @@ You can move around a player on an island and meet different enemies. You have 5 hitpoints to lose before death. Enemies start with different amounts of hitpoints, depending on their species. Dead enemies become dirt, skeletons or "magic meat" -- such objects can be collected, and "magic meat" can be consumed -to gain hitpoints. Note that different kinds of movements/actions take different +to gain hitpoints (if allowed to lie on the ground for a while it may even +multiply ...). Note that different kinds of movements/actions take different numbers of turns to finish. Enemies' AI is very dumb so far: Each turn, they try to move towards their diff --git a/SERVER_COMMANDS b/SERVER_COMMANDS index 8af195f..0a85cd3 100644 --- a/SERVER_COMMANDS +++ b/SERVER_COMMANDS @@ -126,7 +126,7 @@ T_ID [-32768 to 32767] Select thing to manipulate by argument as ID. If argument is <0 or >255, change it to the lowest unused thing ID. If thing of ID does not exist yet, create it with default position of y=0/x=0, the first thing type's ID, and that type's -lifepoints value. If world is active and the thing is animate, build its field +lifepoints value; if world is active and the thing is animate, build its field of view. T_TYPE [0 to 255] @@ -197,3 +197,9 @@ game log and the inventory. TT_CORPSE_ID [0-255] Set to argument the ID of the thing type that things of the selected type transform into when their state changes from animate to inanimate. + +TT_PROLIFERATE [0-255] +If non-zero, there is a chance of 1 divided by the given value each turn for any +thing of the selected type to emit an offspring to a random neighbor cell if one +passable is available (and, if the thing is of an animate type, not inhabited by +another animate thing). diff --git a/confserver/world b/confserver/world index 0c26815..c967a19 100644 --- a/confserver/world +++ b/confserver/world @@ -69,6 +69,7 @@ TT_LIFEPOINTS 0 TT_SYMBOL m TT_NAME 'MAGIC MEAT' TT_CONSUMABLE 3 +TT_PROLIFERATE 255 TT_ID 0 TT_CORPSE_ID 5 diff --git a/src/server/god_commands.c b/src/server/god_commands.c index ebe0d5f..06abcfe 100644 --- a/src/server/god_commands.c +++ b/src/server/god_commands.c @@ -88,7 +88,8 @@ static uint8_t parse_thingtype_manipulation(char * tok0, char * tok1) 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]))) + || !strcmp(tok0, s[S_CMD_TT_CORPS]) || !strcmp(tok0, s[S_CMD_TT_HP]) + || !strcmp(tok0, s[S_CMD_TT_PROL]))) { return err_line(1, "No thing type defined to manipulate yet."); } @@ -97,6 +98,7 @@ static uint8_t parse_thingtype_manipulation(char * tok0, char * tok1) || 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_PROL],'8',(char *) &tt->proliferate) || 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)) { @@ -304,10 +306,6 @@ static uint8_t parse_thing_manipulation_1arg(char * tok0, char * tok1) if (!t && !err_line(!world.thing_types, err)) { t = add_thing(id, world.thing_types->id, 0, 0); - if (world.exists && t->lifepoints) - { - build_fov_map(t); - } } } else diff --git a/src/server/hardcoded_strings.c b/src/server/hardcoded_strings.c index d3560f6..a0a2173 100644 --- a/src/server/hardcoded_strings.c +++ b/src/server/hardcoded_strings.c @@ -9,7 +9,7 @@ -char * s[41]; +char * s[42]; @@ -32,6 +32,7 @@ extern void init_strings() 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_TT_PROL] = "TT_PROLIFERATE"; s[S_CMD_T_ID] = "T_ID"; s[S_CMD_T_TYPE] = "T_TYPE"; s[S_CMD_T_POSY] = "T_POSY"; diff --git a/src/server/hardcoded_strings.h b/src/server/hardcoded_strings.h index 70d29c6..d110827 100644 --- a/src/server/hardcoded_strings.h +++ b/src/server/hardcoded_strings.h @@ -31,6 +31,7 @@ enum string_num S_CMD_TT_SYMB, S_CMD_TT_NAME, S_CMD_TT_CORPS, + S_CMD_TT_PROL, S_CMD_T_ID, S_CMD_T_TYPE, S_CMD_T_POSY, @@ -59,7 +60,7 @@ enum string_num extern void init_strings(); -extern char * s[41]; +extern char * s[42]; diff --git a/src/server/io.c b/src/server/io.c index eb54799..0f7c3a8 100644 --- a/src/server/io.c +++ b/src/server/io.c @@ -455,6 +455,7 @@ extern void save_world() exit_trouble(test < 0, __func__, "fprintf"); write_key_space_string(file, s[S_CMD_TT_NAME], tt->name); write_key_space_value(file, s[S_CMD_TT_CONSUM], tt->consumable); + write_key_space_value(file, s[S_CMD_TT_PROL], tt->proliferate); try_fputc('\n', file, __func__); } for (tt = world.thing_types; tt; tt = tt->next) diff --git a/src/server/run.c b/src/server/run.c index b715ca4..334fe41 100644 --- a/src/server/run.c +++ b/src/server/run.c @@ -8,7 +8,7 @@ #define _POSIX_C_SOURCE 200809L #include "run.h" #include /* NULL */ -#include /* uint8_t, uint16_t, uint32_t */ +#include /* uint8_t, uint16_t, uint32_t, int16_t */ #include /* FILE, printf(), fflush() */ #include /* free() */ #include /* strlen(), strcmp(), strncmp(), strdup() */ @@ -28,7 +28,9 @@ #include "god_commands.h" /* parse_god_command_(1|2|3)arg() */ #include "hardcoded_strings.h" /* s */ #include "io.h" /* io_round(), save_world() */ -#include "things.h" /* Thing, get_thing_action_id_by_name(), get_player() */ +#include "things.h" /* Thing, get_thing_action_id_by_name(), get_player(), + * try_thing_proliferation() + */ #include "world.h" /* world */ @@ -56,8 +58,16 @@ static uint8_t parse_command(char * tok0); */ static void server_test(); +/* Return array of IDs of non-owned things in game world, ended by non-ID -1. */ +static int16_t * build_whitelist(); + +/* Return 1 if value of "id" appears in "whitelist", else 0. */ +static uint8_t thing_in_whitelist(uint8_t id, int16_t * whitelist); + /* Run the game world and its inhabitants (and their actions) until the player - * avatar is free to receive new commands (or is dead). + * avatar is free to receive new commands (or is dead). Only actions and + * proliferations for non-owned things are performed that exist at the start of + * the turn jumped into, or started anew by the cycle. */ static void turn_over(); @@ -204,40 +214,77 @@ static void server_test() +static int16_t * build_whitelist() +{ + uint16_t i_things = NULL != world.things; + struct Thing * t = world.things; + for (; t; t = t->next, i_things++); + int16_t * whitelist = try_malloc(i_things * sizeof(int16_t), __func__); + for (i_things = 0, t = world.things; t; + whitelist[i_things] = t->id, t = t->next, i_things++) + whitelist[i_things] = -1; + return whitelist; +} + + + +static uint8_t thing_in_whitelist(uint8_t id, int16_t * whitelist) +{ + int16_t i; + for (i = 0; -1 < whitelist[i]; i++) + { + if ((int16_t) id == whitelist[i]) + { + return 1; + } + } + return 0; +} + + + static void turn_over() { struct Thing * player = get_player(); struct Thing * thing = player; uint16_t start_turn = world.turn; + int16_t * whitelist = build_whitelist(); while ( 0 < player->lifepoints || (0 == player->lifepoints && start_turn == world.turn)) - { + { /* TODO: check meaning and refactorability of 2nd condition */ if (!thing) { world.turn++; thing = world.things; + free(whitelist); + whitelist = build_whitelist(); } - if (0 < thing->lifepoints) + if (thing_in_whitelist(thing->id, whitelist)) { - if (0 == thing->command) + if (0 < thing->lifepoints) { - if (thing == player) + if (0 == thing->command) { - break; + if (thing == player) + { + break; + } + ai(thing); + } + thing->progress++; + struct ThingAction * ta = get_thing_action(thing->command); + if (thing->progress == ta->effort) + { + ta->func(thing); + thing->command = 0; + thing->progress = 0; } - ai(thing); - } - thing->progress++; - struct ThingAction * ta = get_thing_action(thing->command); - if (thing->progress == ta->effort) - { - ta->func(thing); - thing->command = 0; - thing->progress = 0; } + try_thing_proliferation(thing); } thing = thing->next; } + free(whitelist); } diff --git a/src/server/things.c b/src/server/things.c index 5a866bb..eaeeb9e 100644 --- a/src/server/things.c +++ b/src/server/things.c @@ -10,12 +10,14 @@ #include /* NULL, size_t */ #include /* uint8_t, uint16_t, int16_t, UINT8_MAX, UINT16_MAX */ #include /* free() */ -#include /* memset(), strcmp(), strdup() */ +#include /* memset(), strcmp(), strdup(), strlen() */ #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 "field_of_view.h" /* build_fov_map() */ +#include "map.h" /* mv_yx_in_dir_legal() */ #include "rrand.h" /* rrand() */ #include "thing_actions.h" /* actor_wait */ #include "world.h" /* world */ @@ -119,6 +121,10 @@ extern struct Thing * add_thing(int16_t id, uint8_t type, uint8_t y, uint8_t x) t->lifepoints = tt->lifepoints; t->pos.y = y; t->pos.x = x; + if (t->lifepoints && world.exists) + { + build_fov_map(t); + } return t; } @@ -262,6 +268,54 @@ extern struct Thing * get_player() +extern void try_thing_proliferation(struct Thing * t) +{ + struct ThingType * tt = get_thing_type(t->type); + if (tt->proliferate) + { + if (1 == tt->proliferate || 1 == (rrand() % tt->proliferate)) + { + struct yx_uint8 candidates[6]; + uint8_t n_candidates = 0; + char dirs[7] = "dxswed"; + struct yx_uint8 start = t->pos; + uint8_t i; + for (i = 0; i < strlen(dirs); i++) + { + if ( mv_yx_in_dir_legal(dirs[i], &start) + && '.' == world.map.cells[start.y*world.map.length+start.x]) + { + uint8_t drop = 0; + if (tt->lifepoints) + { + for (t = world.things; t; t = t->next) + { + if ( t->lifepoints + && start.y == t->pos.y && start.x == t->pos.x) + { + drop = 1; + } + } + } + if (!drop) + { + candidates[n_candidates] = start; + n_candidates++; + } + } + } + if (!n_candidates) + { + return; + } + i = rrand() % n_candidates; + add_thing(-1, tt->id, candidates[i].y, candidates[i].x); + } + } +} + + + extern void add_things(uint8_t type, uint8_t n) { uint8_t i; diff --git a/src/server/things.h b/src/server/things.h index 940d108..0f20fd9 100644 --- a/src/server/things.h +++ b/src/server/things.h @@ -42,13 +42,14 @@ struct ThingInMemory struct ThingType { 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 lifepoints; /* default start value for thing's .lifepoints */ - uint8_t consumable; /* can be eaten if !0, for so much hitpoint win */ - uint8_t start_n; /* how many of these does the map start with? */ + 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 lifepoints; /* default start value for thing's .lifepoints */ + uint8_t consumable; /* can be eaten if !0, for so much hitpoint win */ + uint8_t start_n; /* how many of these does the map start with? */ + uint8_t proliferate; /* if >0: inverse of chance to proliferate */ }; struct ThingAction @@ -75,7 +76,8 @@ extern struct ThingAction * add_thing_action(uint8_t id); extern struct ThingType * add_thing_type(int16_t id); /* 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. + * is not >= 0 and <= UINT8_MAX, use lowest unused id. Build .fov_map if + * world.exists is non-zero. Return thing. */ extern struct Thing * add_thing(int16_t id, uint8_t type, uint8_t y, uint8_t x); @@ -108,6 +110,12 @@ extern struct Thing * get_thing(struct Thing * ptr, uint8_t id, uint8_t deep); */ extern struct Thing * get_player(); +/* Try to create "t" offspring on random passable neighbor cell if available (and, + * if "t" is of animate thing type, not inhabited by animate thing) and "t"'s + * type's .proliferation is >0, with a chance of 1/.proliferation. + */ +extern void try_thing_proliferation(struct Thing * t); + /* 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. -- 2.30.2