COMMAND_TOWER,
COMMAND_TOWER_LVL,
COMMAND_TOWER_SP,
- COMMAND_WAVE,
COMMAND_ENEMY,
+ COMMAND_WAVE,
COMMAND_ADD_TOWER,
+ COMMAND_ADD_ENEMY,
COMMAND_QUIT,
#include "enemy.h"
-enemy_t *new_enemy(int id, size_t start_tick) {
+enemy_t *new_enemy(enemy_base_t *base, int id, size_t start_tick) {
enemy_t *self = malloc(sizeof(enemy_t));
- if (!init_enemy(self, id, start_tick)) {
+ if (!init_enemy(self, base, id, start_tick)) {
free(self);
self = NULL;
}
return self;
}
-int init_enemy(enemy_t *self, int id, size_t start_tick) {
+int init_enemy(enemy_t *self, enemy_base_t *base, 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->base = base;
self->id = id;
self->hp = 1;
- self->speed = 1;
self->index = -1;
- self->bits = 0;
self->alive = 0;
return 1;
self->CNAME[0] = '!';
}
+int init_enemy_base(enemy_base_t *self, int type, int hp, int speed, int bits){
+ self->type = type;
+ self->max_hp = hp;
+ self->speed = speed;
+ self->bits = bits;
+
+ return 1;
+}
typedef struct {
int type; // identify the unique enemy type
+ int max_hp;
int speed; // paths per tick
- int index; // index in paths
int bits; // on death
} enemy_base_t;
*/
typedef struct {
char CNAME[10];
+ enemy_base_t *base;
size_t start_tick;
+ int index; // index in paths
int id; // unique per wave
int hp;
- // should move to enemy_base_t
- int speed; // paths per tick
- int index; // index in paths
- int bits; // on death
-
int alive;
} enemy_t;
/**
* Create a new enemy.
*
+ * @param base the type of enemy (will NOT be free'd by this object)
* @param id the unique number (per wave) of this enemy
* @param start_tick the tick at which the enemy will start on the map
*
*
* @return a new enemy (you must later call `free_enemy()`)
*/
-enemy_t *new_enemy(int id, size_t start_tick);
+enemy_t *new_enemy(enemy_base_t *base, int id, size_t start_tick);
/**
* Initialise a new enemy.
*
+ * @param base the type of enemy (will NOT be free'd by this object)
* @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, int id, size_t start_tick);
+int init_enemy(enemy_t *self, enemy_base_t *base, int id, size_t start_tick);
/**
* Free the resources held for the given enemy: you must not use it any more.
*/
void uninit_enemy(enemy_t *self);
+int init_enemy_base(enemy_base_t *self, int type, int hp, int speed, int bits);
+
#endif /* ENEMY_H */
return 0;
tower_base_t *base = engine_get_tower_base(self, type);
- if (!base)
+ if (!base) {
+ event_t event;
+ init_event(&event, EVENT_ERROR);
+ event.error = EEVENT_UNKNOWN_TYPE;
+ array_push(self->events, &event);
+
return 0;
+ }
if (self->bits < base->cost) {
event_t event;
return NULL;
}
+enemy_base_t *engine_get_enemy_base(engine_t *self, int type) {
+ array_loop (self->ebases, base, enemy_base_t) {
+ if (base->type == type)
+ return base;
+ }
+
+ return NULL;
+}
+
+tower_base_t *setup_tower_base(engine_t *self, int type, int cost) {
+ tower_base_t *base = engine_get_tower_base(self, type);
+ if (base) { // already configured
+ event_t event;
+ init_event(&event, EVENT_ERROR);
+ event.error = EEVENT_ALREADY_CONFIGURED;
+ array_push(self->events, &event);
+
+ return NULL;
+ }
+
+ base = array_new(self->tbases);
+ init_tower_base(base, type);
+ base->cost = cost;
+
+ return base;
+}
+
+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;
+}
+
+enemy_base_t *setup_enemy_base(
+ engine_t *self, int type, int hp, int speed, int bits) {
+ enemy_base_t *base = engine_get_enemy_base(self, type);
+ if (base) { // already configured
+ event_t event;
+ init_event(&event, EVENT_ERROR);
+ event.error = EEVENT_ALREADY_CONFIGURED;
+ array_push(self->events, &event);
+
+ return NULL;
+ }
+
+ base = array_new(self->ebases);
+ init_enemy_base(base, type, hp, speed, bits);
+
+ return base;
+}
+
+enemy_t *setup_enemy(engine_t *self, int type, int id, size_t start_tick) {
+ if (!self->waves->count)
+ if (!setup_wave(self, 0, 0))
+ return 0;
+
+ //TODO: id must be unique (total, not only per wave)
+
+ enemy_base_t *base = engine_get_enemy_base(self, type);
+ if (!base) {
+ event_t event;
+ init_event(&event, EVENT_ERROR);
+ event.error = EEVENT_UNKNOWN_TYPE;
+ array_push(self->events, &event);
+
+ return 0;
+ }
+
+ wave_t *wave = array_last(self->waves);
+ return wave_add_enemy(wave, base, id, start_tick);
+}
+
+int setup_path(engine_t *self, int x, int y) {
+ if (!self->map) {
+ event_t event;
+ init_event(&event, EVENT_ERROR);
+ event.error = EEVENT_MAP_NOT_INITIALISED;
+ array_push(self->events, &event);
+
+ return 0;
+ }
+
+ return map_add_path(self->map, x, y);
+}
+
tower_base_t *engine_get_tower_base(engine_t *self, int type);
+enemy_base_t *engine_get_enemy_base(engine_t *self, int type);
+
+// Setup:
+
+tower_base_t *setup_tower_base(engine_t *self, int type, int cost);
+
+int setup_wave(engine_t *self, int start_tick, int bits);
+
+enemy_base_t *setup_enemy_base(
+ engine_t *self, int type, int hp, int speed, int bits);
+
+enemy_t *setup_enemy(engine_t *self, int type, int id, size_t start_tick);
+
+int setup_path(engine_t *self, int x, int y);
+
#endif /* ENGINE_H */
switch(self->x) {
case EEVENT_SUCCESS : return "SUCCESS" ;
case EEVENT_MAP_NOT_INITIALISED : return "MAP_NOT_INITIALISED";
+ case EEVENT_UNKNOWN_TYPE : return "UNKNOWN_TYPE" ;
case EEVENT_ALREADY_CONFIGURED : return "ALREADY_CONFIGURED" ;
case EEVENT_NOT_ENOUGH_BITS : return "NOT_ENOUGH_BITS" ;
}
typedef enum {
EEVENT_SUCCESS,
EEVENT_MAP_NOT_INITIALISED,
+ EEVENT_UNKNOWN_TYPE,
EEVENT_ALREADY_CONFIGURED,
EEVENT_NOT_ENOUGH_BITS,
} eevent_type;
void move(map_t *self, enemy_t *enemy, array_t *events, int reverse) {
int source = enemy->index;
- int target = source >= 0 ? source+1 : 0;
+ int target = source >= 0 ? (source + enemy->base->speed) : 0;
event_t event;
init_event(&event, EVENT_MOVE);
e->alive = 0;
path->enemy = NULL;
init_event(&event, EVENT_DIE);
- bits = e->bits;
+ bits = e->base->bits;
event.move_to_x = bits;
} else {
init_event(&event, EVENT_HIT);
// > (0) type : the unique identifier for this tower type
// > (1) super_cost
// > (2) super_power
+// * enemy : will setup a new enemy type
+// > (0) type : the unique identifier for this enemy type
+// > (1) hp : life/hit points the enemy can take
+// > (2) speed : speed in paths per tick
+// > (3) bits : how much bits received on enemy death
// * wave : configure a new wave TODO
-// * enemy : add a new enemy in the current wave (can create the first one)
-// > type : the enemy type
-// > id : the *unique* id of this enemy
-// > start_tick : the tick at which it will appear (wave-relative)
//
-// Runtime commands:
+// Add commands:
// * add_tower: add a new tower on the map (will cost bits)
// > (0) type : the type of tower to add
// > (1) X position: the X coordinate
// > (2) Y position: the Y coordinate
+// * add_enemy : add a new enemy in the current wave (can create the first one)
+// > type : the enemy type
+// > id : the *unique* id of this enemy
+// > start_tick : the tick at which it will appear (wave-relative)
//
// Other commands:
// * display : will toggle display (debug) mode
cmd->type = COMMAND_INVALID;
if (command_read_count(cmd, line->string) > 2)
cmd->type = COMMAND_INVALID;
+ if (command_read_count(cmd, line->string) != 1)
+ cmd->type = COMMAND_INVALID;
} else if (is_cmd(line, "bits")) {
cmd->type = COMMAND_BITS;
if (!command_read_int(cmd, line->string, 0, 0))
cmd->type = COMMAND_INVALID;
if (command_read_count(cmd, line->string) > 2)
cmd->type = COMMAND_INVALID;
+ if (command_read_count(cmd, line->string) != 1)
+ cmd->type = COMMAND_INVALID;
} else if (is_cmd(line, "path")) {
cmd->type = COMMAND_PATH;
if (!command_read_int(cmd, line->string, 0, -1))
cmd->type = COMMAND_TOWER;
if (!command_read_int(cmd, line->string, 0, 1))
cmd->type = COMMAND_INVALID;
+ if (command_read_count(cmd, line->string) != 2)
+ cmd->type = COMMAND_INVALID;
} else if (is_cmd(line, "tower_lvl")) {
cmd->type = COMMAND_TOWER_LVL;
if (!command_read_int(cmd, line->string, 0, 4))
cmd->type = COMMAND_INVALID;
+ if (command_read_count(cmd, line->string) != 5)
+ cmd->type = COMMAND_INVALID;
} else if (is_cmd(line, "tower_sp")) {
cmd->type = COMMAND_TOWER_SP;
// TODO
- } else if (is_cmd(line, "wave")) {
- cmd->type = COMMAND_WAVE;
- // TODO
} else if (is_cmd(line, "enemy")) {
cmd->type = COMMAND_ENEMY;
- if (!command_read_int(cmd, line->string, 0, 2))
+ if (!command_read_int(cmd, line->string, 0, 3))
+ cmd->type = COMMAND_INVALID;
+ if (command_read_count(cmd, line->string) != 4)
cmd->type = COMMAND_INVALID;
+ } else if (is_cmd(line, "wave")) {
+ cmd->type = COMMAND_WAVE;
+ // TODO
} else if (is_cmd(line, "add_tower")) {
cmd->type = COMMAND_ADD_TOWER;
if (!command_read_int(cmd, line->string, 0, 2))
cmd->type = COMMAND_INVALID;
+ if (command_read_count(cmd, line->string) != 3)
+ cmd->type = COMMAND_INVALID;
+ } else if (is_cmd(line, "add_enemy")) {
+ cmd->type = COMMAND_ADD_ENEMY;
+ if (!command_read_int(cmd, line->string, 0, 2))
+ cmd->type = COMMAND_INVALID;
+ if (command_read_count(cmd, line->string) != 3)
+ cmd->type = COMMAND_INVALID;
} else if (is_cmd(line, "quit")) {
cmd->type = COMMAND_QUIT;
if (command_read_count(cmd, line->string))
cmd->type = COMMAND_INVALID;
+ if (command_read_count(cmd, line->string))
+ cmd->type = COMMAND_INVALID;
}
}
+++ /dev/null
-/*
- * 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 "setup.h"
-#include "event.h"
-
-tower_base_t *setup_tower_base(engine_t *self, int type, int cost) {
- tower_base_t *base = engine_get_tower_base(self, type);
- if (base) { // already configured
- event_t event;
- init_event(&event, EVENT_ERROR);
- event.error = EEVENT_ALREADY_CONFIGURED;
- array_push(self->events, &event);
-
- return NULL;
- }
-
- base = array_new(self->tbases);
- init_tower_base(base, type);
- base->cost = cost;
-
- return base;
-}
-
-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;
-}
-
-enemy_t *setup_enemy(engine_t *self, int type, int id, size_t start_tick) {
- if (!self->waves->count)
- if (!setup_wave(self, 0, 0))
- return 0;
-
- //TODO: type and enemy_base_t
- //TODO: id must be unique (total, not only per wave)
-
- wave_t *wave = array_last(self->waves);
- return wave_add_enemy(wave, id, start_tick);
-}
-
-int setup_path(engine_t *self, int x, int y) {
- if (!self->map) {
- event_t event;
- init_event(&event, EVENT_ERROR);
- event.error = EEVENT_MAP_NOT_INITIALISED;
- array_push(self->events, &event);
-
- return 0;
- }
-
- return map_add_path(self->map, x, y);
-}
-
+++ /dev/null
-/**
- * @file setup.h
- * @author Niki
- * @date 2025
- *
- * @brief bla
- *
- * blablabla
- *
- */
-
-#ifndef SETUP_H
-#define SETUP_H
-
-#include "engine.h"
-
-tower_base_t *setup_tower_base(engine_t *self, int type, int cost);
-
-int setup_wave(engine_t *self, int start_tick, int bits);
-
-enemy_t *setup_enemy(engine_t *self, int type, int id, size_t start_tick);
-
-int setup_path(engine_t *self, int x, int y);
-
-#endif /* SETUP_H */
#include "reader.h"
#include "event.h"
#include "command.h"
-#include "setup.h"
int displayMode = 0;
// TODO
printf("TODO: command_tower_sp\n");
break;
+ case COMMAND_ENEMY:
+ setup_enemy_base(
+ engine,
+ command_get_int(cmd, 0),
+ command_get_int(cmd, 1),
+ command_get_int(cmd, 2),
+ command_get_int(cmd, 3)
+ );
+ break;
case COMMAND_WAVE:
// TODO
printf("TODO: command_wave\n");
break;
- case COMMAND_ENEMY:
- ;enemy_t *enemy = setup_enemy(
+ case COMMAND_ADD_TOWER:
+ engine_add_tower(
engine,
command_get_int(cmd, 0),
command_get_int(cmd, 1),
command_get_int(cmd, 2)
);
- // TODO: enemy_base:
- enemy->speed = 2;
- enemy->bits = 5;
- // end-of-TODO
break;
- case COMMAND_ADD_TOWER:
- engine_add_tower(
+ case COMMAND_ADD_ENEMY:
+ setup_enemy(
engine,
command_get_int(cmd, 0),
command_get_int(cmd, 1),
// If not found: return NULL;
+ int attack = self->base->stats[self->lvl_attack].attack;
+ int speed = self->base->stats[self->lvl_speed].speed;
+
proj_t *proj = new_proj(3, 0); // TODO
proj->desc = self->projectile;
- proj->desc.damage = 10; // TODO
+ proj->desc.damage = attack;
proj->boom_tick = current_tick + 3; // TODO: compute from speed
self->fire_delayed = self->base->stats[self->lvl_speed].attack;
self->last_enemy = NULL;
}
-enemy_t *wave_add_enemy(wave_t *self, int id, size_t start_tick) {
+enemy_t *wave_add_enemy(
+ wave_t *self, enemy_base_t *base, int id, size_t start_tick) {
enemy_t *enemy = array_new(self->enemies);
if (!enemy)
return NULL;
- init_enemy(enemy, id, start_tick);
+ init_enemy(enemy, base, id, start_tick);
return enemy;
}
*/
void uninit_wave(wave_t *self);
-enemy_t *wave_add_enemy(wave_t *self, int id, size_t start_tick);
+enemy_t *wave_add_enemy(
+ wave_t *self, enemy_base_t *base, int id, size_t start_tick);
enemy_t *wave_next_enemy(wave_t *self, int tick);