new wave system
authorNiki Roo <niki@nikiroo.be>
Tue, 8 Apr 2025 20:32:55 +0000 (22:32 +0200)
committerNiki Roo <niki@nikiroo.be>
Tue, 8 Apr 2025 20:32:55 +0000 (22:32 +0200)
src/tdef/enemy.c
src/tdef/enemy.h
src/tdef/engine.c
src/tdef/engine.h
src/tdef/event.c
src/tdef/event.h
src/tdef/map.c
src/tdef/tdef.c
src/tdef/wave.c [new file with mode: 0644]
src/tdef/wave.h [new file with mode: 0644]

index 1918bb8b0b44072fdfbd8334ef217abd20b287f3..b2940edaf3994b8a129c0d8740c7008d71291114 100644 (file)
@@ -22,9 +22,9 @@
 
 #include "enemy.h"
 
-enemy_t *new_enemy(size_t start_tick) {
+enemy_t *new_enemy(int id, size_t start_tick) {
        enemy_t *self = malloc(sizeof(enemy_t));
-       if (!init_enemy(self, start_tick)) {
+       if (!init_enemy(self, id, start_tick)) {
                free(self);
                self = NULL;
        }
@@ -32,13 +32,14 @@ enemy_t *new_enemy(size_t start_tick) {
        return self;
 }
 
-int init_enemy(enemy_t *self, size_t start_tick) {
+int init_enemy(enemy_t *self, int id, size_t start_tick) {
        size_t sz = sizeof(self->CNAME);
        strncpy(self->CNAME, "[enemy        ", sz);
        self->CNAME[sz - 2] = ']';
        self->CNAME[sz - 1] = '\0';
        
        self->start_tick = start_tick;
+       self->id    = id;
        self->hp    = 1; 
        self->speed = 1;
        self->index = -1;
index ee91c2a24cd9894944beba59321d60d59766a02d..3f196261330e8af2e788c940f6c9d7fe308b0eb4 100644 (file)
@@ -22,6 +22,7 @@
 typedef struct {
        char CNAME[10];
        size_t start_tick;
+       int id;    // unique per wave
        int hp;
        int speed; // paths per tick
        int index; // index in paths
@@ -31,6 +32,7 @@ typedef struct {
 /**
  * Create a new enemy.
  * 
+ * @param id the unique number (per wave) of this enemy
  * @param start_tick the tick at which the enemy will start on the map
  *
  * @note always identical to <tt>malloc</tt> + <tt>init_enemy</tt>
@@ -40,14 +42,15 @@ typedef struct {
  *
  * @return a new enemy (you must later call `free_enemy()`)
  */
-enemy_t *new_enemy(size_t start_tick);
+enemy_t *new_enemy(int id, size_t start_tick);
 
 /**
  * Initialise a new enemy.
  * 
+ * @param id the unique number (per wave) of this enemy
  * @param start_tick the tick at which the enemy will start on the map
  */
-int init_enemy(enemy_t *self, size_t start_tick);
+int init_enemy(enemy_t *self, int id, size_t start_tick);
 
 /** 
  * Free the resources held for the given enemy: you must not use it any more.
index b965d812ca0b602d06625e6401d3a504dc11264c..91cd5c8354b176f08974f1bb533c70ac4d5c634e 100644 (file)
@@ -23,6 +23,8 @@
 #include "engine.h"
 #include "event.h"
 
+int tickit();
+
 engine_t *new_engine(map_t *map) {
        engine_t *self = malloc(sizeof(engine_t));
        if (!init_engine(self, map)) {
@@ -41,12 +43,12 @@ int init_engine(engine_t *self, map_t *map) {
        
        self->map          = map;
        self->current_tick = 0;
-       self->enemies      = new_array(sizeof(enemy_t), 100);
-       self->events       = new_array(sizeof(event_t),  20);
-       self->last_enemy   = NULL;
+       self->waves        = new_array(sizeof(wave_t) , 10);
+       self->events       = new_array(sizeof(event_t), 20);
+       self->current_wave = -1;
        self->life         = 10;
        
-       if (!self->enemies || !self->events) {
+       if (!self->waves || !self->events) {
                uninit_engine(self);
                return 0;
        }
@@ -64,31 +66,42 @@ void free_engine(engine_t *self) {
 void uninit_engine(engine_t *self) {
        self->CNAME[0] = '!';
        
-       if (self->enemies) {
-               array_loop(self->enemies, enemy, enemy_t) {
-                       uninit_enemy(enemy);
+       if (self->waves) {
+               array_loop(self->waves, wave, wave_t) {
+                       uninit_wave(wave);
                }
        }
        
-       free_map(self->map)      ; self->map = NULL;
-       free_array(self->events) ; self->events  = NULL;
-       free_array(self->enemies); self->enemies = NULL;
+       free_map(self->map)      ; self->map    = NULL;
+       free_array(self->events) ; self->events = NULL;
+       free_array(self->waves)  ; self->waves  = NULL;
 }
 
 void engine_tick(engine_t *self) {
        array_clear(self->events);
        
-       enemy_t *enemy;
-       if (!self->last_enemy)
-               enemy = array_first(self->enemies);
-       else
-               enemy = array_next(self->enemies, self->last_enemy);
-       
        // Note that multiple ennemies per tick will trigger the backfill
-       while (enemy && enemy->start_tick >= self->current_tick) {
-               self->last_enemy = enemy;
-               map_enemy_enters(self->map, enemy, self->events);
-               enemy = array_next(self->enemies, enemy);
+       array_loop_i (self->waves, wave, wave_t, i) {
+               if (wave->done)
+                       continue;
+               
+               enemy_t *enemy = wave_next_enemy(wave, self->current_tick);
+               
+               int cw = self->current_wave;
+               if (enemy && (cw < 0 || cw < i)) {
+                       self->current_wave = i;
+                       event_t event;
+                       init_event(&event, WAVE);
+                       event.x = i,
+                       array_push(self->events, &event);
+               }
+               
+               while (enemy) {
+                       map_enemy_enters(self->map, enemy, self->events);
+                       
+                       enemy = wave_next_enemy(wave, self->current_tick);
+               }
+                               
        }
        
        map_move_1(self->map, self->events);
@@ -98,10 +111,23 @@ void engine_tick(engine_t *self) {
        self->current_tick++;
 }
 
-int setup_enemy(engine_t *self, enemy_t *enemy) {
-       if (!array_push(self->enemies, enemy))
+int setup_wave(engine_t *self, int start_tick, int bits) {
+       wave_t *wave = array_new(self->waves);
+       if (!wave)
                return 0;
        
+       init_wave(wave, start_tick);
+       wave->bits = bits;
+       
        return 1;
 }
 
+int setup_enemy(engine_t *self, enemy_t *enemy) {
+       if (!self->waves->count)
+               if (!setup_wave(self, 0, 0))
+                       return 0;
+       
+       wave_t *wave = array_last(self->waves);
+       return wave_add_enemy(wave, enemy);
+}
+
index f01856ea17583ff7fd4ca5e7c23e4e3867d99624..0b537ba9363f98647b3faa189c488b6e8c89ac41 100644 (file)
@@ -15,6 +15,7 @@
 #include <stddef.h>
 #include "cutils/array.h"
 #include "map.h"
+#include "wave.h"
 
 /**
  * @brief bla
@@ -25,9 +26,10 @@ typedef struct {
        char CNAME[10];
        map_t *map;
        size_t current_tick;
-       array_t *enemies;
-       array_t *events;
+       array_t *waves;         // wave_t
+       array_t *events;        // event_t
        enemy_t *last_enemy;
+       int current_wave;
        int life;
 } engine_t;
 
@@ -80,5 +82,7 @@ void engine_tick(engine_t *self);
 
 int setup_enemy(engine_t *self, enemy_t *enemy);
 
+int setup_wave(engine_t *self, int start_tick, int bits);
+
 #endif /* ENGINE_H */
 
index 4c385efe647e27dc2ed7590eb83e675e470e6827..e7072386da1e15f6a15e4d8520326a3823da0eb1 100644 (file)
@@ -67,6 +67,7 @@ const char *event_name(event_t *self) {
        case MOVE    : return "MOVE"    ;
        case DIE     : return "DIE"     ;
        case BREACH  : return "BREACH"  ;
+       case WAVE    : return "WAVE"    ;
        case WIN     : return "WIN"     ;
        case LOOSE   : return "LOOSE"   ;
        }
index 0b4b096750888df1ce2595a6b572cc48a43e043d..7a90d29209f1a414e688d04ece369252092c35b7 100644 (file)
@@ -27,6 +27,7 @@ typedef enum {
        MOVE,
        DIE,
        BREACH,
+       WAVE,
        WIN,
        LOOSE,
 } event_type;
index 598466823c3c2afa7a3d3d878097f495d849045f..c4f504762e0daa91c9af04d3681a361219a845ac 100644 (file)
@@ -103,6 +103,7 @@ void map_enemy_enters(map_t *self, enemy_t *enemy, array_t *events) {
        
        event_t event;
        init_event(&event, ENTER);
+       event.x = enemy->id;
        array_push(events, &event);
        
        return;
@@ -120,8 +121,8 @@ void map_move_1(map_t *self, array_t *events) {
        
        if (dead > (self->alive->count / 2)) {
                array_clear(self->tmp_e);
-               array_loop(self->alive, ptr, enemy_t*) {
-                       if((*ptr)->alive)
+               array_loop (self->alive, ptr, enemy_t*) {
+                       if ((*ptr)->alive)
                                array_push(self->tmp_e, ptr);
                }
                array_steal_data(self->alive, self->tmp_e);
index fc6bd6ad28801ad5d682e4cc8461a92c39699a71..a87d50228e21f5dddc9b81cd56c28eaae059655a 100644 (file)
@@ -70,7 +70,10 @@ void output_event(event_t *self, FILE *file) {
        case BACKFILL: case DIE: case BREACH:
                fprintf(file, "EVENT:%s,%d,%d\n", name, self->x, self->y);
                break;
-       case ENTER: case WIN: case LOOSE:
+       case ENTER: case WAVE:
+               fprintf(file, "EVENT:%s,%d\n", name, self->x);
+               break;
+       case WIN: case LOOSE:
                fprintf(file, "EVENT:%s\n", name);
                break;
        }
@@ -101,7 +104,7 @@ int main(int argc, char **argv) {
        tower_t *tower = new_tower(2, 1);
        setup_tower(map, tower);
        
-       enemy_t *enemy = new_enemy(1);
+       enemy_t *enemy = new_enemy(0, 1);
        enemy->speed = 1;
        setup_enemy(engine, enemy);
        
@@ -117,7 +120,7 @@ int main(int argc, char **argv) {
                        }
                        if (displayMode) {
                                fprintf(stderr, 
-                                       "Tick is: %d\n", engine->current_tick);
+                                       "Tick is: %zu\n", engine->current_tick);
                                display(map);
                        }
                        break;
@@ -125,7 +128,7 @@ int main(int argc, char **argv) {
                        displayMode = !displayMode;
                        if (displayMode) {
                                fprintf(stderr, 
-                                       "Tick is: %d\n", engine->current_tick);
+                                       "Tick is: %zu\n", engine->current_tick);
                                display(map);
                        }
                        break;
diff --git a/src/tdef/wave.c b/src/tdef/wave.c
new file mode 100644 (file)
index 0000000..332c9a3
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * TDef: small tower defense game
+ *
+ * Copyright (C) 2025 Niki Roo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "wave.h"
+
+wave_t *new_wave(size_t start_tick) {
+       wave_t *self = malloc(sizeof(wave_t));
+       if (!init_wave(self, start_tick)) {
+               free(self);
+               self = NULL;
+       }
+
+       return self;
+}
+
+int init_wave(wave_t *self, size_t start_tick) {
+       size_t sz = sizeof(self->CNAME);
+       strncpy(self->CNAME, "[wave        ", sz);
+       self->CNAME[sz - 2] = ']';
+       self->CNAME[sz - 1] = '\0';
+       
+       self->start_tick = start_tick;
+       self->enemies    = new_array(sizeof(enemy_t), 20);
+       self->tmp_e      = new_array(sizeof(enemy_t), 20);
+       self->last_enemy = NULL;
+       self->bits       = 0;
+       self->done       = 0;
+       
+       return 1;
+}
+
+void free_wave(wave_t *self) {
+       if (self)
+               uninit_wave(self);
+
+       free(self);
+}
+
+void uninit_wave(wave_t *self) {
+       self->CNAME[0] = '!';
+       
+       if (self->enemies) {
+               array_loop(self->enemies, enemy, enemy_t) {
+                       uninit_enemy(enemy);
+               }
+       }
+       
+       free_array(self->enemies); self->enemies = NULL;
+       free_array(self->tmp_e)  ; self->tmp_e   = NULL;
+       self->last_enemy = NULL;
+}
+
+int wave_add_enemy(wave_t *self, enemy_t *enemy) {
+       if (!array_push(self->enemies, enemy))
+               return 0;
+       return 1;
+}
+
+enemy_t *wave_next_enemy(wave_t *self, int tick) {
+       if (self->done)
+               return NULL;
+
+       enemy_t *enemy;
+       if (!self->last_enemy)
+               enemy = array_first(self->enemies);
+       else
+               enemy = array_next(self->enemies, self->last_enemy);
+       
+       if (enemy && (self->start_tick + enemy->start_tick) >= tick) {
+               self->last_enemy = enemy;
+               return enemy;
+       }
+       
+       int dead = 0;
+       array_loop (self->enemies, enemy, enemy_t) {
+               if (!enemy->alive)
+                       dead++;
+       }
+       
+       if (dead == self->enemies->count) {
+               self->done = 1;
+       }
+       
+       return NULL;
+}
+
+
diff --git a/src/tdef/wave.h b/src/tdef/wave.h
new file mode 100644 (file)
index 0000000..38bd41e
--- /dev/null
@@ -0,0 +1,80 @@
+/** 
+ * @file wave.h
+ * @author Niki
+ * @date 2025
+ * 
+ * @brief bla
+ *
+ * blablabla
+ *
+ */
+
+#ifndef WAVE_H
+#define WAVE_H
+
+#include <stddef.h>
+#include "cutils/array.h"
+#include "enemy.h"
+
+/**
+ * @brief bla
+ *
+ * blablabla
+ */
+typedef struct {
+       char CNAME[10];
+       size_t start_tick;
+       array_t *enemies; // enemy_t
+       array_t *tmp_e;   // enemy_t
+       enemy_t *last_enemy;
+       int bits;
+       int done;
+} wave_t;
+
+/**
+ * Create a new wave.
+ * 
+ * @param start_tick the tick at which the wave will start on the map
+ *
+ * @note always identical to <tt>malloc</tt> + <tt>init_wave</tt>
+ *
+ * @see malloc()
+ * @see init_wave(wave_t *self)
+ *
+ * @return a new wave (you must later call `free_wave()`)
+ */
+wave_t *new_wave(size_t start_tick);
+
+/**
+ * Initialise a new wave.
+ * 
+ * @param start_tick the tick at which the wave will start on the map
+ */
+int init_wave(wave_t *self, size_t start_tick);
+
+/** 
+ * Free the resources held for the given wave: you must not use it any more.
+ *
+ * @note always equivalent to <tt>uninit_wave</tt> + <tt>free</tt>
+ *
+ * @see uninit_wave(wave_t *self)
+ * @see free(void *data)
+ */
+void free_wave(wave_t *self);
+
+/**
+ * Free the resources held for the given wave: you must not use it any more
+ * unless you call <tt>init_wave</tt> on it again.
+ *
+ * The memory pointed to by <tt>self</tt> is <i>not</i> free'd.
+ *
+ *
+ * @see init_wave(wave_t *self)
+ */
+void uninit_wave(wave_t *self);
+
+int wave_add_enemy(wave_t *self, enemy_t *enemy);
+
+enemy_t *wave_next_enemy(wave_t *self, int tick);
+
+#endif /* WAVE_H */