some more work
authorNiki <niki@nikiroo.be>
Mon, 7 Apr 2025 21:21:50 +0000 (23:21 +0200)
committerNiki <niki@nikiroo.be>
Mon, 7 Apr 2025 21:21:50 +0000 (23:21 +0200)
17 files changed:
src/tdef/any.c
src/tdef/any.h
src/tdef/enemy.c
src/tdef/enemy.h
src/tdef/engine.c
src/tdef/engine.h
src/tdef/event.c [new file with mode: 0644]
src/tdef/event.h [new file with mode: 0644]
src/tdef/map.c
src/tdef/map.h
src/tdef/path.c
src/tdef/proj.c [new file with mode: 0644]
src/tdef/proj.h [new file with mode: 0644]
src/tdef/reader.c
src/tdef/tdef.c
src/tdef/tower.c
src/tdef/tower.h

index 0315baae72d82afb2ba57d92b5ab4daad5b3c014..5d20b01779ae1ee96dbb7e97fc5d6b8ac31b8631 100644 (file)
@@ -22,8 +22,8 @@
 
 #include "any.h"
 
-path_t *anyloc(any_t *self) {
-       if (self && self->_.type == 'l')
+path_t *anypath(any_t *self) {
+       if (self && self->_.type == 'p')
                return (path_t*)(void*)self;
        return NULL;
 }
@@ -40,7 +40,7 @@ enemy_t *anyenemy(any_t *self) {
        return NULL;
 }
 
-any_t *loc2any(path_t *self) {
+any_t *path2any(path_t *self) {
        return (any_t*)(void*)self;
 }
 
index 33a755efbceddf8dbf38c53a78ea4a593fb39270..9a1e7b2df6aeb2402b28d560ff3696c1ab9bcb0d 100644 (file)
@@ -28,13 +28,13 @@ typedef union {
        enemy_t _e;
 } any_t;
 
-path_t *anyloc(any_t *self);
+path_t *anypath(any_t *self);
 
 tower_t *anytower(any_t *self);
 
 enemy_t *anyenemy(any_t *self);
 
-any_t *loc2any(path_t *self);
+any_t *path2any(path_t *self);
 
 any_t *tower2any(tower_t *self);
 
index 79a7302baf97e1620ec58623ad391732e172668a..1918bb8b0b44072fdfbd8334ef217abd20b287f3 100644 (file)
@@ -39,7 +39,10 @@ int init_enemy(enemy_t *self, size_t start_tick) {
        self->CNAME[sz - 1] = '\0';
        
        self->start_tick = start_tick;
+       self->hp    = 1; 
+       self->speed = 1;
        self->index = -1;
+       self->alive = 0;
        
        return 1;
 }
index 2e1ca96269ae6883bd0be428dbca36e26cc61a9f..ee91c2a24cd9894944beba59321d60d59766a02d 100644 (file)
@@ -25,6 +25,7 @@ typedef struct {
        int hp;
        int speed; // paths per tick
        int index; // index in paths
+       int alive;
 } enemy_t;
 
 /**
index bdc62393f3758d614e62dea0bed28edc20036ee1..b965d812ca0b602d06625e6401d3a504dc11264c 100644 (file)
@@ -21,8 +21,7 @@
 #include <stdlib.h>
 
 #include "engine.h"
-
-void move(engine_t *self, enemy_t *enemy);
+#include "event.h"
 
 engine_t *new_engine(map_t *map) {
        engine_t *self = malloc(sizeof(engine_t));
@@ -40,12 +39,19 @@ int init_engine(engine_t *self, map_t *map) {
        self->CNAME[sz - 2] = ']';
        self->CNAME[sz - 1] = '\0';
        
-       self->map = map;
+       self->map          = map;
        self->current_tick = 0;
-       self->events = new_array(sizeof(int), 10);
-       self->last_enemy = NULL;
+       self->enemies      = new_array(sizeof(enemy_t), 100);
+       self->events       = new_array(sizeof(event_t),  20);
+       self->last_enemy   = NULL;
+       self->life         = 10;
        
-       return (self->events != NULL);
+       if (!self->enemies || !self->events) {
+               uninit_engine(self);
+               return 0;
+       }
+       
+       return 1;
 }
 
 void free_engine(engine_t *self) {
@@ -57,70 +63,45 @@ void free_engine(engine_t *self) {
 
 void uninit_engine(engine_t *self) {
        self->CNAME[0] = '!';
-       free_map(self->map);
-       self->map = NULL;
-       free_array(self->events);
-       self->events = NULL;
+       
+       if (self->enemies) {
+               array_loop(self->enemies, enemy, enemy_t) {
+                       uninit_enemy(enemy);
+               }
+       }
+       
+       free_map(self->map)      ; self->map = NULL;
+       free_array(self->events) ; self->events  = NULL;
+       free_array(self->enemies); self->enemies = NULL;
 }
 
 void engine_tick(engine_t *self) {
        array_clear(self->events);
        
-       // TODO: all the rest
-       array_deloop(self->map->alive, ptr, enemy_t*) {
-               enemy_t *alive = *ptr;
-               move(self, alive);
-       }
-       
        enemy_t *enemy;
-       if (self->last_enemy)
-               enemy = array_next(self->map->enemies, self->last_enemy);
+       if (!self->last_enemy)
+               enemy = array_first(self->enemies);
        else
-               enemy = array_first(self->map->enemies);
+               enemy = array_next(self->enemies, self->last_enemy);
        
-       // We allow only one enemy per tick (because one per path!)
-       if (enemy && enemy->start_tick == self->current_tick) {
+       // Note that multiple ennemies per tick will trigger the backfill
+       while (enemy && enemy->start_tick >= self->current_tick) {
                self->last_enemy = enemy;
-               array_push(self->map->alive, &enemy);
-               move(self, enemy);
+               map_enemy_enters(self->map, enemy, self->events);
+               enemy = array_next(self->enemies, enemy);
        }
        
+       map_move_1(self->map, self->events);
+       map_fire_1(self->map, self->current_tick, self->events);
+       map_hit_1(self->map, self->current_tick, self->events);
+       
        self->current_tick++;
 }
 
-
-void move(engine_t *self, enemy_t *enemy) {
-       int source = enemy->index;
-       int target = source >= 0 ? source+1 : 0;
-       
-       path_t *path_from = NULL;
-       if (enemy->index >= 0)
-               path_from = array_get(self->map->paths, enemy->index);
-       path_t *path_to = array_get(self->map->paths, target);
-       
-       if (path_to && path_to->enemy)
-               printf("woops: TODO\n");
-       
-       if (path_from) {
-               if (path_from->enemy != enemy) {
-                       printf("TODO: DATA ERROR\n");
-                       return;
-               }
-               path_from->enemy = NULL;
-       }
+int setup_enemy(engine_t *self, enemy_t *enemy) {
+       if (!array_push(self->enemies, enemy))
+               return 0;
        
-       if (!path_to) {
-               printf("TODO: enemy left!\n");
-               return;
-       }
-       
-       if (path_to) {  
-               path_to->enemy = enemy;
-               enemy->index = path_to->index;
-       }
-printf("enemy moved from %d (%d,%d) to %d (%d,%d)\n",
-source, path_from?path_from->x:-1, path_from?path_from->y:-1,
-target, path_to  ?path_to  ->x:-1, path_to  ?path_to  ->y:-1
-);
+       return 1;
 }
 
index b19d6e984631f690fdcc6de9ad3f68d458b888aa..f01856ea17583ff7fd4ca5e7c23e4e3867d99624 100644 (file)
@@ -25,8 +25,10 @@ typedef struct {
        char CNAME[10];
        map_t *map;
        size_t current_tick;
+       array_t *enemies;
        array_t *events;
        enemy_t *last_enemy;
+       int life;
 } engine_t;
 
 /**
@@ -76,5 +78,7 @@ void uninit_engine(engine_t *self);
 // will clear events on start!
 void engine_tick(engine_t *self);
 
+int setup_enemy(engine_t *self, enemy_t *enemy);
+
 #endif /* ENGINE_H */
 
diff --git a/src/tdef/event.c b/src/tdef/event.c
new file mode 100644 (file)
index 0000000..4c385ef
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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 "event.h"
+
+event_t *new_event(event_type type) {
+       event_t *self = malloc(sizeof(event_t));
+       if (!init_event(self, type)) {
+               free(self);
+               self = NULL;
+       }
+
+       return self;
+}
+
+int init_event(event_t *self, event_type type) {
+       size_t sz = sizeof(self->CNAME);
+       strncpy(self->CNAME, "[event        ", sz);
+       self->CNAME[sz - 2] = ']';
+       self->CNAME[sz - 1] = '\0';
+       
+       self->type = type;
+       self->x = -1;
+       self->y = -1;
+       self->move_to_x = -1;
+       self->move_to_y = -1;
+       
+       return 1;
+}
+
+void free_event(event_t *self) {
+       if (self)
+               uninit_event(self);
+
+       free(self);
+}
+
+void uninit_event(event_t *self) {
+       self->CNAME[0] = '!';
+}
+
+const char *event_name(event_t *self) {
+       switch (self->type) {
+       case FIRE    : return "FIRE"    ; 
+       case HIT     : return "HIT"     ;
+       case ENTER   : return "ENTER"   ;
+       case BACKFILL: return "BACKFILL";
+       case MOVE    : return "MOVE"    ;
+       case DIE     : return "DIE"     ;
+       case BREACH  : return "BREACH"  ;
+       case WIN     : return "WIN"     ;
+       case LOOSE   : return "LOOSE"   ;
+       }
+       return "Unknown";
+}
diff --git a/src/tdef/event.h b/src/tdef/event.h
new file mode 100644 (file)
index 0000000..0b4b096
--- /dev/null
@@ -0,0 +1,92 @@
+/** 
+ * @file event.h
+ * @author Niki
+ * @date 2025
+ * 
+ * @brief bla
+ *
+ * blablabla
+ *
+ */
+
+#ifndef EVENT_H
+#define EVENT_H
+
+#include <stddef.h>
+
+/**
+ * @brief bla
+ *
+ * blablabla
+ */
+typedef enum {
+       FIRE,
+       HIT,
+       ENTER,
+       BACKFILL,
+       MOVE,
+       DIE,
+       BREACH,
+       WIN,
+       LOOSE,
+} event_type;
+
+/**
+ * @brief bla
+ *
+ * blablabla
+ */
+typedef struct {
+       char CNAME[10];
+       event_type type;
+       int x;
+       int y;
+       int move_to_x;
+       int move_to_y;
+} event_t;
+
+/**
+ * Create a new event.
+ * 
+ * @param type the kind of event that happended
+ *
+ * @note always identical to <tt>malloc</tt> + <tt>init_event</tt>
+ *
+ * @see malloc()
+ * @see init_event(event_t *self)
+ *
+ * @return a new event (you must later call `free_event()`)
+ */
+event_t *new_event(event_type type);
+
+/**
+ * Initialise a new event.
+ * 
+ * @param type the kind of event that happended
+ */
+int init_event(event_t *self, event_type type);
+
+/** 
+ * Free the resources held for the given event: you must not use it any more.
+ *
+ * @note always equivalent to <tt>uninit_event</tt> + <tt>free</tt>
+ *
+ * @see uninit_event(event_t *self)
+ * @see free(void *data)
+ */
+void free_event(event_t *self);
+
+/**
+ * Free the resources held for the given event: you must not use it any more
+ * unless you call <tt>init_event</tt> on it again.
+ *
+ * The memory pointed to by <tt>self</tt> is <i>not</i> free'd.
+ *
+ *
+ * @see init_event(event_t *self)
+ */
+void uninit_event(event_t *self);
+
+const char *event_name(event_t *self);
+
+#endif /* EVENT_H */
index 91c31c85921947cd5a09eec077a070673b67ba2d..5bb3a34cd2b49f4707181fc756495455dbb0577f 100644 (file)
 #include <string.h>
 
 #include "map.h"
+#include "event.h"
+#include "proj.h"
+
+void move(map_t *self, enemy_t *enemy, array_t *events, int reverse);
+void hit(map_t *self, proj_t *proj, array_t *events);
 
 map_t *new_map(int width, int height) {
        map_t *self = malloc(sizeof(map_t));
@@ -43,14 +48,16 @@ int init_map(map_t *self, int width, int height) {
        self->height = height;
        self->data   = calloc((width*height), sizeof(any_t*));
        
-       self->paths   = new_array(sizeof(path_t   ), 100);
-       self->towers  = new_array(sizeof(tower_t*),  20);
-       self->enemies = new_array(sizeof(enemy_t ), 100);
-       self->alive   = new_array(sizeof(enemy_t*),  10);
+       self->paths    = new_array(sizeof(path_t  ), 100);
+       self->towers   = new_array(sizeof(tower_t*),  20);
+       self->alive    = new_array(sizeof(enemy_t*),  10);
+       self->breached = new_array(sizeof(enemy_t*),  20);
+       self->backfill = new_array(sizeof(enemy_t*),  10);
+       
+       self->projs = new_proj(-1, -1);
        
        if (!self->data || !self->paths || !self->towers 
-               || !self->enemies || !self->alive
-       ) {
+               || !self->alive || !self->breached || !self->backfill) {
                uninit_map(self);
                return 0;
        }
@@ -74,20 +81,88 @@ void uninit_map(map_t *self) {
                }
        }
        
-       if (self->enemies) {
-               array_loop(self->enemies, enemy, enemy_t) {
-                       uninit_enemy(enemy);
+       free      (self->data    ); self->data     = NULL;
+       free_array(self->paths   ); self->paths    = NULL;
+       free_array(self->towers  ); self->towers   = NULL;
+       free_array(self->alive   ); self->alive    = NULL;
+       free_array(self->breached); self->breached = NULL;
+       free_array(self->backfill); self->backfill = NULL;
+       
+       while (self->projs) {
+               proj_t *p = self->projs;
+               self->projs = p->next;
+               free_proj(p);
+       }
+}
+
+void map_enemy_enters(map_t *self, enemy_t *enemy, array_t *events) {
+       enemy->alive = 1;
+       array_push(self->alive, &enemy);
+       
+       event_t event;
+       init_event(&event, ENTER);
+       array_push(events, &event);
+       
+       return;
+}
+
+void map_move_1(map_t *self, array_t *events) {
+       int dead = 0;
+       
+       array_deloop(self->alive, ptr, enemy_t*) {
+               enemy_t *alive = *ptr;
+               if (alive->alive)
+                       move(self, alive, events, 0);
+               else
+                       dead++;
+       }
+       
+       if (dead > self->alive->count) {
+               dead = 0;
+               array_deloop(self->alive, ptr, enemy_t*) {
+                       if((*ptr)->alive)
+                               break;
+                       dead++;
                }
+               
+               array_cut_at(self->alive, self->alive->count - dead);
        }
        
-       free      (self->data   ); self->data    = NULL;
-       free_array(self->paths  ); self->paths   = NULL;
-       free_array(self->towers ); self->towers  = NULL;
-       free_array(self->enemies); self->enemies = NULL;
-       free_array(self->alive)  ; self->alive   = NULL;
+       if (self->backfill->count) {
+               enemy_t **ptr = array_first(self->backfill);
+               enemy_t *enemy = *ptr;
+               map_enemy_enters(self, enemy, events);
+       }
 }
 
-int map_path(map_t *self, int x, int y) {
+void map_fire_1(map_t *self, int current_tick, array_t *events) {
+       array_loop(self->towers, tower, tower_t) {
+               tower_rearm(tower);
+               proj_t *target = tower_fire(tower, current_tick,
+                               self->alive, self->paths);
+               if (!target)
+                       continue;
+               target->next = self->projs->next;
+               self->projs->next = target;
+       }
+}
+
+void map_hit_1(map_t *self, int current_tick, array_t *events) {
+       proj_t *prev = self->projs;
+       for (proj_t *p = prev->next ; p ; p = p->next) {
+               if (p->boom_tick <= current_tick) {
+                       hit(self, p, events);
+                       
+                       prev->next = p->next;
+                       free_proj(p);
+                       p = prev;
+               }
+               
+               prev = p;
+       }
+}
+
+int setup_path(map_t *self, int x, int y) {
        if (x < 0 || y < 0)
                return 0;
        if (x >= self->width || y >= self->height)
@@ -99,11 +174,11 @@ int map_path(map_t *self, int x, int y) {
        init_path(loc, x, y);
        loc->index = self->paths->count - 1;
        
-       self->data[y * self->width + x] = loc2any(loc);
+       self->data[y * self->width + x] = path2any(loc);
        return 1;
 }
 
-int map_add_tower(map_t *self, tower_t *tower) {
+int setup_tower(map_t *self, tower_t *tower) {
        int x = tower->x;
        int y = tower->y;
        if (x < 0 || y < 0)
@@ -120,10 +195,73 @@ int map_add_tower(map_t *self, tower_t *tower) {
        return 1;
 }
 
-int map_enemy(map_t *self, enemy_t *enemy) {
-       if (!array_push(self->enemies, enemy))
-               return 0;
+void move(map_t *self, enemy_t *enemy, array_t *events, int reverse) {
+       int source = enemy->index;
+       int target = source >= 0 ? source+1 : 0;
+       event_t event;
+       init_event(&event, MOVE);
        
-       return 1;
+       path_t *path_from = NULL;
+       if (enemy->index >= 0) {
+               path_from = array_get(self->paths, enemy->index);
+               path_from->enemy = NULL;
+               event.x = path_from->x;
+               event.y = path_from->y;
+       }
+       
+       if (reverse) {
+               target = source-1;
+               if (target == -1) { // backfill
+                       enemy->alive = 0;
+                       array_push(self->backfill, &enemy);
+                       event.type = BACKFILL;
+                       array_push(events, &event);
+                       return;
+               }
+       }
+       
+       path_t *path_to = array_get(self->paths, target);
+       if (path_to && path_to->enemy) // replace
+               move(self, path_to->enemy, events, 1);
+       
+       if (path_to) { // move
+               event.move_to_x = path_to->x;
+               event.move_to_y = path_to->y;
+               path_to->enemy = enemy;
+               enemy->index = path_to->index;
+       } else { // breached
+               enemy->alive = 0;
+               array_push(self->breached, &enemy);
+               event.type = BREACH;
+       }
+       
+       array_push(events, &event);
+       
+       return;
+}
+
+void hit(map_t *self, proj_t *proj, array_t *events) {
+       // TODO: loop through alive for radius? tower needs the same
+       any_t *any = self->data[proj->y * self->width + proj->x];
+       path_t *path = anypath(any);
+       enemy_t *e = (path) ? path->enemy : NULL;
+       
+       if (e && e->hp > 0) {
+               e->hp -= proj->desc.damage;
+               
+               event_t event;
+               if (e->hp <= 0) {
+                       e->hp = 0;
+                       e->alive = 0;
+                       path->enemy = NULL;
+                       init_event(&event, DIE);
+               } else {
+                       init_event(&event, HIT);
+               }
+               
+               event.x = path->x;
+               event.y = path->y;
+               array_push(events, &event);
+       }
 }
 
index e8c4ff182c09fed1c4457c515d97a698db0e3f93..16899f6165714726e6ee5e113b77309739089452 100644 (file)
@@ -26,10 +26,12 @@ typedef struct {
        int width;
        int height;
        any_t **data;
-       array_t *paths;
-       array_t *towers;
-       array_t *enemies;
-       array_t *alive;
+       array_t *paths;         // path
+       array_t *towers;        // tower* 
+       array_t *alive;         // enemy*
+       array_t *breached;      // enemy*
+       array_t *backfill;      // enemy*
+       proj_t *projs;          // first one is fake (list head)
 } map_t;
 
 /**
@@ -76,11 +78,17 @@ void free_map(map_t *self);
  */
 void uninit_map(map_t *self);
 
-int map_path(map_t *self, int x, int y);
+void map_enemy_enters(map_t *self, enemy_t *enemy, array_t *events);
 
-int map_add_tower(map_t *self, tower_t *tower);
+void map_move_1(map_t *self, array_t *events);
 
-int map_enemy(map_t *self, enemy_t *enemy);
+void map_fire_1(map_t *self, int current_tick, array_t *events);
+
+void map_hit_1(map_t *self, int current_tick, array_t *events);
+
+int setup_path(map_t *self, int x, int y);
+
+int setup_tower(map_t *self, tower_t *tower);
 
 #endif /* MAP_H */
 
index 0f8bb82e57650e0dc07513b925fb7cdda0db9bbb..70cf97939896aa192b7951be0d32d787f72b610a 100644 (file)
@@ -34,7 +34,7 @@ path_t *new_path(int x, int y) {
 
 int init_path(path_t *self, int x, int y) {
        size_t sz = sizeof(self->CNAME);
-       strncpy(self->CNAME, "[loc          ", sz);
+       strncpy(self->CNAME, "[path          ", sz);
        self->CNAME[sz - 2] = ']';
        self->CNAME[sz - 1] = '\0';
        
diff --git a/src/tdef/proj.c b/src/tdef/proj.c
new file mode 100644 (file)
index 0000000..c405dcf
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "proj.h"
+
+int init_proj_desc(proj_desc_t *self) {
+       size_t sz = sizeof(self->CNAME);
+       strncpy(self->CNAME, "[proj_desc          ", sz);
+       self->CNAME[sz - 2] = ']';
+       self->CNAME[sz - 1] = '\0';
+       
+       self->damage        = 0;
+       self->splash_damage = 0;
+       self->radius        = 0;
+       
+       return 1;
+}
+
+proj_t *new_proj(int x, int y) {
+       proj_t *self = malloc(sizeof(proj_t));
+       if (!init_proj(self, x, y)) {
+               free(self);
+               self = NULL;
+       }
+
+       return self;
+}
+
+int init_proj(proj_t *self, int x, int y) {
+       size_t sz = sizeof(self->CNAME);
+       strncpy(self->CNAME, "[proj          ", sz);
+       self->CNAME[sz - 2] = ']';
+       self->CNAME[sz - 1] = '\0';
+       
+       self->x = x;
+       self->y = y;
+       init_proj_desc(&self->desc);
+       self->next = NULL;
+       
+       return 1;
+}
+
+void free_proj(proj_t *self) {
+       if (self)
+               uninit_proj(self);
+
+       free(self);
+}
+
+void uninit_proj(proj_t *self) {
+       self->CNAME[0] = '!';
+       self->desc.CNAME[0] = '!';
+}
+
diff --git a/src/tdef/proj.h b/src/tdef/proj.h
new file mode 100644 (file)
index 0000000..8dfb3d3
--- /dev/null
@@ -0,0 +1,87 @@
+/** 
+ * @file proj.h
+ * @author Niki
+ * @date 2025
+ * 
+ * @brief bla
+ *
+ * blablabla
+ *
+ */
+
+#ifndef PROJ_H
+#define PROJ_H
+
+#include "cutils/array.h"
+
+typedef struct {
+       char CNAME[10];
+       int damage;
+       int splash_damage;
+       int radius; // 0 = no splash damage
+} proj_desc_t;
+
+int init_proj_desc(proj_desc_t *self);
+
+/**
+ * @brief bla
+ *
+ * blablabla
+ */
+struct proj_struct;
+struct proj_struct {
+       char CNAME[10];
+       int x; // target X
+       int y; // target Y
+       int boom_tick;
+       proj_desc_t desc;
+       struct proj_struct *next;
+};
+typedef struct proj_struct proj_t;
+
+/**
+ * Create a new proj.
+ *
+ * @param x the X coordinate of the projectile's target location
+ * @param y the Y coordinate of the projectile's target location
+ *
+ * @note always identical to <tt>malloc</tt> + <tt>init_proj</tt>
+ *
+ * @see malloc()
+ * @see init_proj(proj_t *self)
+ *
+ * @return a new proj (you must later call `free_proj()`)
+ */
+proj_t *new_proj(int x, int y);
+
+/**
+ * Initialise a new proj.
+ *
+ * @param x the X coordinate of the projectile's target location
+ * @param y the Y coordinate of the projectile's target location
+ */
+int init_proj(proj_t *self, int x, int y);
+
+/** 
+ * Free the resources held for the given proj: you must not use it any more.
+ *
+ * @note always equivalent to <tt>uninit_proj</tt> + <tt>free</tt>
+ *
+ * @see uninit_proj(proj_t *self)
+ * @see free(void *data)
+ */
+void free_proj(proj_t *self);
+
+/**
+ * Free the resources held for the given proj: you must not use it any more
+ * unless you call <tt>init_proj</tt> on it again.
+ *
+ * The memory pointed to by <tt>self</tt> is <i>not</i> free'd.
+ *
+ *
+ * @see init_proj(proj_t *self)
+ */
+void uninit_proj(proj_t *self);
+
+#endif /* PROJ_H */
+
index 73a99d51889815d1c5e240df399aa62ad8fc09c3..dab3b465efb330e2745729c903297cae22936124 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * TDef: small reader defense game
+ * TDef: small tower defense game
  *
  * Copyright (C) 2025 Niki Roo
  *
 #include "reader.h"
 
 
+// TODO: configuration:
+// map size
+// path: (1,1), (1,2)...
+// basic: setMoney, setLife, start...
+// tower: configTower('1', ...)
+// enemy: configEnemy('a', ...)
+
+// TODO:
+// display: could be a mode (auto after tick)
+
 command read_command(FILE *file) {
        static cstring_t *line = NULL;
        
@@ -33,7 +43,7 @@ command read_command(FILE *file) {
        if (cstring_readline(line, file)) {
                if (!strcmp(".", line->string)) {
                        return TICK;
-               } else if (!strcmp("=", line->string)) {
+               } else if (!strcmp("display", line->string)) {
                        return DISPLAY;
                } else {
                        return UNKNOWN;
index 8afc523a3908e1df4b0c8c13904e9056977f211b..edfce3b632e96f83e2f12b64090a2e2733569d14 100644 (file)
 #include "map.h"
 #include "engine.h"
 #include "reader.h"
+#include "event.h"
 
 void display(map_t *self) {
-       printf("==========\n\n");
+       fprintf(stderr, "==========\n\n");
        for (int y = 0 ; y < self->height; y++) {
        for (int x = 0 ; x < self->width ; x++) {
                any_t *any = self->data[y * self->width + x];
-               path_t *path = anyloc(any);
+               path_t *path = anypath(any);
                tower_t *tower = anytower(any);
                if (tower) {
-                       printf("T");
+                       fprintf(stderr, "T");
                } else if (path) {
                        if (path->enemy) {
-                               printf("e");
+                               fprintf(stderr, "e");
                        }
                        else if (path->index == 0) {
-                               printf(">");
+                               fprintf(stderr, ">");
                        } else if (path->index == self->paths->count - 1) {
-                               printf(">");
+                               fprintf(stderr, ">");
                        } else {
-                               printf("#");
+                               fprintf(stderr, "#");
                        }
                } else if (any) {
-                       printf("?");
-                       //printf("\n<%s>\n", any->CNAME);
+                       fprintf(stderr, "?");
+                       //fprintf("stderr, \n<%s>\n", any->CNAME);
                } else {
-                       printf(".");
+                       fprintf(stderr, ".");
                }
        }
-       printf("\n");
+       fprintf(stderr, "\n");
        }
-       printf("\n");
+       fprintf(stderr, "\n");
+}
+
+void display_event(event_t *self) {
+       const char *name = event_name(self);
+       
+       switch (self->type) {
+       case FIRE: case HIT: case MOVE: 
+               fprintf(stderr, "[*] %s: (%d,%d) -> (%d,%d)\n", name,
+                       self->x, self->y, self->move_to_x, self->move_to_y);
+               break;
+       case BACKFILL: case DIE: case BREACH:
+               fprintf(stderr, "[*] %s: (%d,%d)\n", name, self->x, self->y);
+               break;
+       case ENTER: case WIN: case LOOSE:
+               fprintf(stderr, "[*] %s\n", name);
+               break;
+       }
+}
+
+void output_event(event_t *self, FILE *file) {
+       const char *name = event_name(self);
+       fwrite(name, 1, strlen(name), file);
+       fwrite("\n",1, 1, file);
 }
 
 int main(int argc, char **argv) {
@@ -65,38 +89,57 @@ int main(int argc, char **argv) {
                return 1;
        }
        
-       map_path(map, 0, 0);
-       map_path(map, 1, 0);
-       map_path(map, 2, 0);
-       map_path(map, 3, 0);
-       map_path(map, 3, 1);
-       map_path(map, 3, 2);
-       map_path(map, 4, 2);
-       map_path(map, 5, 2);
+       engine_t *engine = new_engine(map);
+       if (!engine) {
+               fprintf(stderr, "Cannot create the engine");
+               return 2;
+       }
+       
+       setup_path(map, 0, 0);
+       setup_path(map, 1, 0);
+       setup_path(map, 2, 0);
+       setup_path(map, 3, 0);
+       setup_path(map, 3, 1);
+       setup_path(map, 3, 2);
+       setup_path(map, 4, 2);
+       setup_path(map, 5, 2);
        
        tower_t *tower = new_tower(2, 1);
-       map_add_tower(map, tower);
+       setup_tower(map, tower);
        
        enemy_t *enemy = new_enemy(1);
        enemy->speed = 1;
-       map_enemy(map, enemy);
-       
-       engine_t *engine = new_engine(map);
+       setup_enemy(engine, enemy);
        
-       printf("Paths: %d\n", map->paths->count);
        
+       int displayMode = 0;    
        command cmd = read_command(stdin);
        while (cmd != QUIT) {
                switch(cmd) {
                case TICK:
-                       printf("Tick is: %d\n", engine->current_tick);
                        engine_tick(engine);
+                       array_loop(engine->events, event, event_t) {
+                               output_event(event, stdout);
+                       }
+                       if (displayMode) {
+                               fprintf(stderr, 
+                                       "Tick is: %d\n", engine->current_tick);
+                               array_loop(engine->events, event, event_t) {
+                                       display_event(event);
+                               }
+                               display(map);
+                       }
                        break;
                case DISPLAY:
-                       display(map);
+                       displayMode = !displayMode;
+                       if (displayMode) {
+                               fprintf(stderr, 
+                                       "Tick is: %d\n", engine->current_tick);
+                               display(map);
+                       }
                        break;
                case UNKNOWN:
-                       printf("Unrecognized command\n");
+                       fprintf(stderr, "Unrecognized command\n");
                        break;
                case QUIT:
                        // cannot happen, but kept for warning purposes
@@ -106,7 +149,6 @@ int main(int argc, char **argv) {
                cmd = read_command(stdin);
        }
        
-       
        free_engine(engine);
        return 0;
 }
index 40aa981369b5723c44e5055d5594171e4ac01302..de58076f7659e16549679244d5ca13c3f66254ad 100644 (file)
@@ -38,8 +38,15 @@ int init_tower(tower_t *self, int x, int y) {
        self->CNAME[sz - 2] = ']';
        self->CNAME[sz - 1] = '\0';
        
-       self->x = x;
-       self->y = y;
+       self->x            = x;
+       self->y            = y;
+       self->range        = 0;
+       self->speed        = 0;
+       self->super        = 0;
+       self->fire_delay   = 0;
+       self->fire_delayed = 0;
+       
+       init_proj_desc(&self->projectile);
        
        return 1;
 }
@@ -55,3 +62,32 @@ void uninit_tower(tower_t *self) {
        self->CNAME[0] = '!';
 }
 
+void tower_rearm(tower_t *self) {
+       if (self->fire_delayed > 0)
+               self->fire_delayed--;
+}
+
+proj_t *tower_fire(tower_t *self, int current_tick, array_t *enemies, 
+               array_t *path) {
+       
+       if (self->fire_delayed > 0)
+               return NULL;
+
+       // TODO:
+       // select target (3 modes)
+       // check target expected position
+       // fire at position if <= range
+       // (skip to next if not)
+       
+       // If not found: return NULL;
+
+       proj_t *proj = new_proj(3, 0); // TODO
+       proj->desc = self->projectile;
+       proj->desc.damage = 10; // TODO
+       proj->boom_tick = current_tick + 3; // TODO: compute from speed
+       
+       self->fire_delayed = self->fire_delay;
+       
+       return proj;
+}
+
index 890b45478f94e22381e2335349e41c3935b1d8f0..103b92e850b915ea1671296527013b51a234184d 100644 (file)
@@ -12,6 +12,9 @@
 #ifndef TOWER_H
 #define TOWER_H
 
+#include "cutils/array.h"
+#include "proj.h"
+
 /**
  * @brief bla
  *
@@ -21,6 +24,12 @@ typedef struct {
        char CNAME[10];
        int x;
        int y;
+       int range;
+       int speed; // speed of the proj in locs per tick 
+       int super;
+       int fire_delay; // in ticks
+       int fire_delayed; // in ticks still to wait
+       proj_desc_t projectile;
 } tower_t;
 
 /**
@@ -67,4 +76,9 @@ void free_tower(tower_t *self);
  */
 void uninit_tower(tower_t *self);
 
+void tower_rearm(tower_t *self);
+
+proj_t *tower_fire(tower_t *self, int current_tick, array_t *enemies, 
+               array_t *path);
+
 #endif /* TOWER_H */