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
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]
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).
TT_SYMBOL m
TT_NAME 'MAGIC MEAT'
TT_CONSUMABLE 3
+TT_PROLIFERATE 255
TT_ID 0
TT_CORPSE_ID 5
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.");
}
|| 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))
{
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
-char * s[41];
+char * s[42];
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";
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,
extern void init_strings();
-extern char * s[41];
+extern char * s[42];
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)
#define _POSIX_C_SOURCE 200809L
#include "run.h"
#include <stddef.h> /* NULL */
-#include <stdint.h> /* uint8_t, uint16_t, uint32_t */
+#include <stdint.h> /* uint8_t, uint16_t, uint32_t, int16_t */
#include <stdio.h> /* FILE, printf(), fflush() */
#include <stdlib.h> /* free() */
#include <string.h> /* strlen(), strcmp(), strncmp(), strdup() */
#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 */
*/
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();
+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);
}
#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(), strcmp(), strdup() */
+#include <string.h> /* 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 */
t->lifepoints = tt->lifepoints;
t->pos.y = y;
t->pos.x = x;
+ if (t->lifepoints && world.exists)
+ {
+ build_fov_map(t);
+ }
return t;
}
+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;
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
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);
*/
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.