home · contact · privacy
Server: New command TT_PROLIFERATE sets chance for thing proliferation.
authorChristian Heller <c.heller@plomlompom.de>
Wed, 15 Oct 2014 01:14:50 +0000 (03:14 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Wed, 15 Oct 2014 01:14:50 +0000 (03:14 +0200)
README
SERVER_COMMANDS
confserver/world
src/server/god_commands.c
src/server/hardcoded_strings.c
src/server/hardcoded_strings.h
src/server/io.c
src/server/run.c
src/server/things.c
src/server/things.h

diff --git a/README b/README
index d50abd9910e4b35250277ebb641ba957855c1a65..15fc87f41e72cd35db5e9729f67135609ce5f6c5 100644 (file)
--- 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
index 8af195fbdc5283a6e396666e669ff3dcf6716533..0a85cd381588c6379ded56b8a324be45ddfb467a 100644 (file)
@@ -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).
index 0c26815e93d4f4cb8ac11acc307a2e2fbf4f7184..c967a1955ff041ab0329269982df7d41634f644d 100644 (file)
@@ -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
index ebe0d5f91be8955f6f81435c02a9a985efe025a9..06abcfec87626d6677daa9586db3bf0e35dc8042 100644 (file)
@@ -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
index d3560f65fa1b3d81d90eab44cff28c6adfe8bfbf..a0a21735683d4a96c8338aed534c4e74b629e455 100644 (file)
@@ -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";
index 70d29c678ff49ba674c258c9ab8266637db7b2cf..d11082779a4bb3a8b6029753e69f8859ef282891 100644 (file)
@@ -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];
 
 
 
index eb54799c5dc052d6245717ddb21f44afec2118fe..0f7c3a8bd60367e5a338db65b6e2648227309b7e 100644 (file)
@@ -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)
index b715ca4e74329617085f9dc4317e4f4202d060b2..334fe410fcf9200713725ee2f8adcc81b22f2c53 100644 (file)
@@ -8,7 +8,7 @@
 #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() */
@@ -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);
 }
 
 
index 5a866bb2f35a7f6395b4b7d005b8dcece45507da..eaeeb9ee8b9b09d9a5e511e6a082504e2a8b830d 100644 (file)
 #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 */
@@ -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;
index 940d108ab2f2b44d613737d08ce39fd3586e65a2..0f20fd90b8a14031b42fc4e3deb3791cd079028e 100644 (file)
@@ -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.