waves and enemy spawn sequences
This commit is contained in:
parent
2c8f74cb2f
commit
72e154a618
|
@ -6,8 +6,8 @@ from PIL import Image, ImageChops
|
|||
def convert(input, output, params, target):
|
||||
recognized = True
|
||||
|
||||
if params["custom-type"] == "level":
|
||||
o = convert_level(input, params)
|
||||
if params["custom-type"] == "level_map":
|
||||
o = convert_level_map(input, params)
|
||||
elif params["custom-type"] == "animation":
|
||||
o = convert_animation(input, params)
|
||||
else:
|
||||
|
@ -19,7 +19,7 @@ def convert(input, output, params, target):
|
|||
|
||||
return 1
|
||||
|
||||
def convert_level(input, params):
|
||||
def convert_level_map(input, params):
|
||||
RE_CATEGORY = re.compile(r'^\[([^\]]+)\]$', re.MULTILINE)
|
||||
|
||||
with open(input, "r") as fp:
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
*.txt:
|
||||
custom-type: level
|
||||
name_regex: (.*)\.txt lv_\1
|
||||
custom-type: level_map
|
||||
name_regex: (.*)\.txt lv_map_\1
|
||||
|
|
45
src/game.c
45
src/game.c
|
@ -1,5 +1,6 @@
|
|||
#include "game.h"
|
||||
#include "util.h"
|
||||
#include "enemies.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
@ -8,8 +9,8 @@ bool game_load(game_t *g, level_t *level)
|
|||
game_unload(g);
|
||||
|
||||
map_t *m = &g->map;
|
||||
m->width = level->width;
|
||||
m->height = level->height;
|
||||
m->width = level->map->width;
|
||||
m->height = level->map->height;
|
||||
m->tiles = malloc(m->width * m->height * sizeof *m->tiles);
|
||||
if(!m->tiles) {
|
||||
game_unload(g);
|
||||
|
@ -19,8 +20,8 @@ bool game_load(game_t *g, level_t *level)
|
|||
for(int y = 0; y < m->height; y++)
|
||||
for(int x = 0; x < m->width; x++) {
|
||||
struct tile *t = map_tile(m, x, y);
|
||||
t->base = level->tiles[level->width * y + x].base;
|
||||
t->decor = level->tiles[level->width * y + x].decor;
|
||||
t->base = level->map->tiles[level->map->width * y + x].base;
|
||||
t->decor = level->map->tiles[level->map->width * y + x].decor;
|
||||
t->solid = (t->base == 0) || (t->base >= 16);
|
||||
}
|
||||
|
||||
|
@ -37,7 +38,11 @@ bool game_load(game_t *g, level_t *level)
|
|||
g->particles = NULL;
|
||||
g->particle_count = 0;
|
||||
|
||||
g->level = level;
|
||||
g->wave = 1;
|
||||
g->wave_spawned = 0;
|
||||
g->time_wave = fix(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -60,6 +65,22 @@ void game_unload(game_t *g)
|
|||
free(g->particles);
|
||||
}
|
||||
|
||||
level_wave_t const *game_current_wave(game_t const *g)
|
||||
{
|
||||
if(!g->level) return NULL;
|
||||
if(g->wave < 1 || g->wave > g->level->wave_count) return NULL;
|
||||
return &g->level->waves[g->wave-1];
|
||||
}
|
||||
|
||||
void game_next_wave(game_t *g)
|
||||
{
|
||||
if(g->wave >= g->level->wave_count) return;
|
||||
|
||||
g->wave++;
|
||||
g->wave_spawned = 0;
|
||||
g->time_wave = fix(0);
|
||||
}
|
||||
|
||||
//---
|
||||
// Object management functions
|
||||
//---
|
||||
|
@ -232,6 +253,22 @@ void game_try_move_entity(game_t *g, entity_t *e,
|
|||
// Per-frame update functions
|
||||
//---
|
||||
|
||||
void game_spawn_enemies(game_t *g)
|
||||
{
|
||||
level_wave_t const *wave = game_current_wave(g);
|
||||
if(!wave) return;
|
||||
|
||||
for(int i = g->wave_spawned; i < wave->enemy_count; i++) {
|
||||
level_wave_spawn_t *s = &wave->enemies[i];
|
||||
if(s->time > g->time_wave) break;
|
||||
|
||||
entity_t *e = enemy_spawn(s->identity, s->level);
|
||||
fpoint_t pos = { fix(s->x) + fix(1)/2, fix(s->y) + fix(1)/2 };
|
||||
game_spawn_entity(g, e, pos);
|
||||
g->wave_spawned++;
|
||||
}
|
||||
}
|
||||
|
||||
void game_update_animations(game_t *g, fixed_t dt)
|
||||
{
|
||||
for(int i = 0; i < g->entity_count; i++) {
|
||||
|
|
18
src/game.h
18
src/game.h
|
@ -35,8 +35,13 @@ typedef struct game {
|
|||
int particle_count;
|
||||
/* Field of movement to reach the player (used by most enemy AIs) */
|
||||
pfg_all2one_t paths_to_player;
|
||||
/* Current wave */
|
||||
|
||||
/* Level begin played */
|
||||
level_t *level;
|
||||
/* Current wave, number of enemies spawned in wave, time spent in wave */
|
||||
int wave;
|
||||
int wave_spawned;
|
||||
fixed_t time_wave;
|
||||
|
||||
} game_t;
|
||||
|
||||
|
@ -46,6 +51,12 @@ bool game_load(game_t *g, level_t *level);
|
|||
/* Free resources allocated for the level. */
|
||||
void game_unload(game_t *g);
|
||||
|
||||
/* Current wave */
|
||||
level_wave_t const *game_current_wave(game_t const *g);
|
||||
|
||||
/* Move to next wave */
|
||||
void game_next_wave(game_t *g);
|
||||
|
||||
//---
|
||||
// Adding dynamic game elements
|
||||
//---
|
||||
|
@ -73,12 +84,13 @@ void game_spawn_entity(game_t *g, entity_t *e, fpoint_t pos);
|
|||
void game_try_move_entity(game_t *g, entity_t *e,
|
||||
entity_movement_t const *next_movement);
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Per-frame update functions
|
||||
//---
|
||||
|
||||
/* Spawn enemies from the current wave. */
|
||||
void game_spawn_enemies(game_t *g);
|
||||
|
||||
/* Update all entities' and effect areas' animations. */
|
||||
void game_update_animations(game_t *g, fixed_t dt);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "fixed.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Directions */
|
||||
enum { UP=0, RIGHT=1, DOWN=2, LEFT=3 };
|
||||
|
@ -73,6 +74,15 @@ static inline fpoint_t fdir(int dir)
|
|||
};
|
||||
}
|
||||
|
||||
/* Direction closest to specified vector */
|
||||
static inline int frdir(fpoint_t v)
|
||||
{
|
||||
if(v.x >= abs(v.y)) return RIGHT;
|
||||
if(v.x <= -abs(v.y)) return LEFT;
|
||||
if(v.y < 0) return UP;
|
||||
return DOWN;
|
||||
}
|
||||
|
||||
/* Normalize vector */
|
||||
static inline fpoint_t fnormalize(fpoint_t v)
|
||||
{
|
||||
|
|
34
src/level.c
34
src/level.c
|
@ -1 +1,35 @@
|
|||
#include "level.h"
|
||||
#include "enemies.h"
|
||||
|
||||
/* List of maps */
|
||||
extern level_map_t lv_map_demo;
|
||||
extern level_map_t lv_map_1;
|
||||
|
||||
level_t lv_demo = {
|
||||
.map = &lv_map_demo,
|
||||
};
|
||||
|
||||
level_t lv_1 = {
|
||||
.map = &lv_map_1,
|
||||
.wave_count = 2,
|
||||
.waves = (level_wave_t []){
|
||||
/* Wave 1: Just some slimes */
|
||||
{ .enemy_count = 6,
|
||||
.enemies = (level_wave_spawn_t []){
|
||||
{ ENEMY_SLIME, 1, 2,1, FIX(0)/2 },
|
||||
{ ENEMY_SLIME, 1, 19,9, FIX(1)/2 },
|
||||
{ ENEMY_SLIME, 1, 2,1, FIX(2)/2 },
|
||||
{ ENEMY_SLIME, 1, 19,9, FIX(3)/2 },
|
||||
{ ENEMY_SLIME, 2, 2,1, FIX(4)/2 },
|
||||
{ ENEMY_SLIME, 2, 19,9, FIX(5)/2 },
|
||||
}},
|
||||
/* Wave 2: Stronger slimes and a bat */
|
||||
{ .enemy_count = 4,
|
||||
.enemies = (level_wave_spawn_t []){
|
||||
{ ENEMY_SLIME, 2, 2,1, FIX(0)/2 },
|
||||
{ ENEMY_SLIME, 2, 19,9, FIX(0)/2 },
|
||||
{ ENEMY_BAT, 1, 2,1, FIX(2)/2 },
|
||||
{ ENEMY_BAT, 1, 19,9, FIX(2)/2 },
|
||||
}},
|
||||
},
|
||||
};
|
||||
|
|
31
src/level.h
31
src/level.h
|
@ -9,7 +9,29 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "fixed.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
/* Enemy type */
|
||||
uint8_t identity;
|
||||
/* Enemy level */
|
||||
uint8_t level;
|
||||
/* Spawn location */
|
||||
uint8_t x, y;
|
||||
/* Spawn time */
|
||||
fixed_t time;
|
||||
|
||||
} level_wave_spawn_t;
|
||||
|
||||
typedef struct {
|
||||
/* Number of enemies */
|
||||
int enemy_count;
|
||||
/* List of enemies */
|
||||
level_wave_spawn_t *enemies;
|
||||
|
||||
} level_wave_t;
|
||||
|
||||
typedef struct {
|
||||
/* Dimensions of the level (yeah no surprise right) */
|
||||
|
@ -20,6 +42,15 @@ typedef struct {
|
|||
uint8_t decor;
|
||||
} tiles[];
|
||||
|
||||
} level_map_t;
|
||||
|
||||
typedef struct {
|
||||
level_map_t *map;
|
||||
|
||||
/* Waves */
|
||||
int wave_count;
|
||||
level_wave_t *waves;
|
||||
|
||||
} level_t;
|
||||
|
||||
/* List of levels */
|
||||
|
|
50
src/main.c
50
src/main.c
|
@ -62,7 +62,7 @@ int main(void)
|
|||
memset(&debug, 0, sizeof debug);
|
||||
|
||||
//---
|
||||
// Load random entities on the map
|
||||
// Spawn player
|
||||
//---
|
||||
|
||||
entity_movement_params_t emp_player = {
|
||||
|
@ -73,27 +73,22 @@ int main(void)
|
|||
.dash_cooldown = fix(1),
|
||||
};
|
||||
|
||||
for(int i = 0; i < 10; i++) {
|
||||
int type = (rand() & 1) ? ENEMY_BAT : ENEMY_SLIME;
|
||||
entity_t *e = enemy_spawn(type, 1);
|
||||
if(!e) continue;
|
||||
entity_t *player = enemy_spawn(ENEMY_BAT, 1);
|
||||
|
||||
int x=1, y=1;
|
||||
for(int i = 0; i < 1000; i++) {
|
||||
x = rand() % m->width;
|
||||
y = rand() % m->height;
|
||||
int x=1, y=1;
|
||||
for(int i = 0; i < 1000; i++) {
|
||||
x = rand() % m->width;
|
||||
y = rand() % m->height;
|
||||
|
||||
struct tile *t = map_tile(m, x, y);
|
||||
if(t && !t->solid) break;
|
||||
}
|
||||
e->movement.x = fix(x) + fix(1) / 2;
|
||||
e->movement.y = fix(y) + fix(1) / 2;
|
||||
|
||||
game_spawn_entity(&game, e, entity_pos(e));
|
||||
struct tile *t = map_tile(m, x, y);
|
||||
if(t && !t->solid) break;
|
||||
}
|
||||
player->movement.x = fix(x) + fix(1) / 2;
|
||||
player->movement.y = fix(y) + fix(1) / 2;
|
||||
|
||||
entity_t *player = game.entities[5];
|
||||
game_add_entity(&game, player);
|
||||
game.player = player;
|
||||
|
||||
player->HP += 100;
|
||||
player->movement_params = &emp_player;
|
||||
player->identity = 0;
|
||||
|
@ -133,12 +128,6 @@ int main(void)
|
|||
dclear(C_BLACK);
|
||||
render_game(&game, debug.show_hitboxes);
|
||||
|
||||
/* if(game.time_victory != 0) dprint(1, 1, C_WHITE, "Victory in %.1f s!",
|
||||
f2double(game.time_victory));
|
||||
else if(game.time_defeat != 0) dprint(1, 1, C_WHITE, "Defeat! :(");
|
||||
else dprint(1, 1, C_WHITE, "HP:%d ATK:%d DEF:%d",
|
||||
player->HP, player->ATK, player->DEF); */
|
||||
|
||||
/* Developer/tweaking menu */
|
||||
if(debug.show_vars) {
|
||||
uint32_t *vram = (void *)gint_vram;
|
||||
|
@ -250,6 +239,8 @@ int main(void)
|
|||
perf_simul = prof_make();
|
||||
prof_enter(perf_simul);
|
||||
|
||||
game_spawn_enemies(&game);
|
||||
|
||||
game_update_animations(&game, dt);
|
||||
|
||||
key_event_t ev;
|
||||
|
@ -402,8 +393,11 @@ int main(void)
|
|||
frect_t hitbox = {
|
||||
-fix(4)/16, fix(3)/16, -fix(4)/16, fix(3)/16,
|
||||
};
|
||||
hitbox = rect_rotate(hitbox, UP, e->movement.facing);
|
||||
fpoint_t dir = fdir(e->movement.facing);
|
||||
int facing = frdir((fpoint_t){
|
||||
player->movement.x - e->movement.x,
|
||||
player->movement.y - e->movement.y });
|
||||
hitbox = rect_rotate(hitbox, UP, facing);
|
||||
fpoint_t dir = fdir(facing);
|
||||
|
||||
fpoint_t anchor = rect_center(entity_sprite(e));
|
||||
anchor.x += fmul(fix(3)/4, dir.x);
|
||||
|
@ -461,12 +455,18 @@ int main(void)
|
|||
game_update_particles(&game, dt);
|
||||
|
||||
game.time_total += dt;
|
||||
game.time_wave += dt;
|
||||
|
||||
if(game.time_defeat == 0 && player->HP == 0)
|
||||
game.time_defeat = game.time_total;
|
||||
if(game.time_victory == 0 && player->HP > 0 && game.entity_count == 1)
|
||||
game.time_victory = game.time_total;
|
||||
|
||||
/* Next wave */
|
||||
if(game.time_victory > 0 && game.time_total > game.time_victory+fix(2)
|
||||
&& game.wave < game.level->wave_count)
|
||||
game_next_wave(&game);
|
||||
|
||||
/* Visual pathfinding debug */
|
||||
if(debug.show_path) {
|
||||
pfg_path_free(&debug.grid_path);
|
||||
|
|
13
src/render.c
13
src/render.c
|
@ -208,10 +208,19 @@ void render_game(game_t const *g, bool show_hitboxes)
|
|||
|
||||
if(enemies_left > 0)
|
||||
dprint_opt(DWIDTH / 2, 2, C_WHITE, C_NONE, DTEXT_CENTER, DTEXT_TOP,
|
||||
"Wave %d: %d enemies left", g->wave, enemies_left);
|
||||
else
|
||||
"Wave %d: %d/%d enemies left", g->wave, enemies_left,
|
||||
g->level->waves[g->wave-1].enemy_count);
|
||||
else if(g->wave_spawned >= game_current_wave(g)->enemy_count)
|
||||
dprint_opt(DWIDTH / 2, 2, C_WHITE, C_NONE, DTEXT_CENTER, DTEXT_TOP,
|
||||
"Wave %d: Victory!", g->wave);
|
||||
else
|
||||
dprint_opt(DWIDTH / 2, 2, C_WHITE, C_NONE, DTEXT_CENTER, DTEXT_TOP,
|
||||
"Wave %d: Starting...", g->wave);
|
||||
|
||||
entity_t const *player = g->player;
|
||||
dprint(2, 2, C_WHITE, "HP: %d", player->HP);
|
||||
dprint_opt(DWIDTH - 2, 2, C_WHITE, C_NONE, DTEXT_RIGHT, DTEXT_TOP,
|
||||
"ATK:%d DEF:%d", player->ATK, player->DEF);
|
||||
|
||||
dfont(old_font);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue