some basic turret placement
This commit is contained in:
parent
37d2c89388
commit
c192ae6e1f
Binary file not shown.
After Width: | Height: | Size: 6.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
|
@ -1,10 +1,11 @@
|
|||
##################
|
||||
##################
|
||||
> # # x##
|
||||
##### ## # # #####
|
||||
## ## # # #####
|
||||
#### ### # #####
|
||||
########## # #####
|
||||
########## #####
|
||||
##################
|
||||
##################
|
||||
####################
|
||||
####################
|
||||
#> # # x##
|
||||
##### ## # # #######
|
||||
## ## # # #######
|
||||
#### ### # #######
|
||||
########## # #######
|
||||
########## #######
|
||||
########## # #######
|
||||
####################
|
||||
####################
|
||||
|
|
|
@ -13,18 +13,22 @@ def convert(input, output, params, target):
|
|||
#
|
||||
# Format of the map:
|
||||
#
|
||||
# [general data: 16 bytes]
|
||||
# - width (1 byte)
|
||||
# - height (1 byte)
|
||||
# - camera_x (1 byte)
|
||||
# - camera_y (1 byte)
|
||||
# - spawn_x (1 byte), where to spawn enemies
|
||||
# - spawn_y (1 byte), where to spawn enemies
|
||||
# - cur_x (1 byte), default cursor pos
|
||||
# - cur_y (1 byte), default cursor pos
|
||||
# - number of enemy path elements (1 byte)
|
||||
# - unused (9 bytes)
|
||||
#
|
||||
# [offsets and stuff: 16 bytes]
|
||||
# - offset of grid data from file start (2 bytes, big endian)
|
||||
# - offset of enemy path data from file start (2 bytes, big endian)
|
||||
# - unused (12 bytes)
|
||||
#
|
||||
# [big data thingies]
|
||||
# - grid data (variable)
|
||||
# - enemy path (variable)
|
||||
#
|
||||
|
@ -33,6 +37,7 @@ def convert(input, output, params, target):
|
|||
# 0x00: basic placement tile
|
||||
# 0x01: basic enemy path
|
||||
# 0x02: castle
|
||||
# 0x03: wall
|
||||
#
|
||||
# every element of enemy path is:
|
||||
# - delta (1 byte):
|
||||
|
@ -43,19 +48,24 @@ def convert(input, output, params, target):
|
|||
|
||||
lines = open(input, "r").read().splitlines()
|
||||
|
||||
w, h = 18, 10
|
||||
spawn_x, spawn_y = None, None
|
||||
arr_x, arr_y = None, None
|
||||
grid_data = b''
|
||||
|
||||
if len(lines) != h:
|
||||
raise fxconv.FxconvError(f"should have 10 lines, has {len(lines)}")
|
||||
h = len(lines)
|
||||
if h <= 0:
|
||||
raise fxconv.FxconvError(f"should have one or more lines")
|
||||
|
||||
w = None
|
||||
for line_no, line in enumerate(lines):
|
||||
line = line.strip()
|
||||
if len(line) != w:
|
||||
raise fxconv.FxconvError(f"line no. {line_no + 1} "
|
||||
f"should have 18 columns, has {len(line)}")
|
||||
if w is None:
|
||||
w = len(line)
|
||||
if w <= 0:
|
||||
raise fxconv.FxconvError(f"should have one or more columns")
|
||||
elif len(line) != w:
|
||||
raise fxconv.FxconvError(f"inconsistent width: {w} on first line, "
|
||||
f"{len(line)} on line {line_no + 1}")
|
||||
|
||||
for car_no, c in enumerate(line):
|
||||
if c == '#':
|
||||
|
@ -92,6 +102,8 @@ def convert(input, output, params, target):
|
|||
|
||||
if (x, y) == (arr_x, arr_y):
|
||||
return previous + ((x, y),)
|
||||
elif not x in range(w) or not y in range(h):
|
||||
pass
|
||||
elif grid_data[y * w + x] != 1:
|
||||
pass
|
||||
elif (x, y) not in previous:
|
||||
|
@ -122,9 +134,9 @@ def convert(input, output, params, target):
|
|||
return x + b'\0' * (round4len(x) - len(x))
|
||||
|
||||
data = int(w).to_bytes(1, 'big') + int(h).to_bytes(1, 'big')
|
||||
data += int(1).to_bytes(1, 'big') + int(1).to_bytes(1, 'big')
|
||||
data += int(spawn_x).to_bytes(1, 'big')
|
||||
data += int(spawn_y).to_bytes(1, 'big')
|
||||
data += int(1).to_bytes(1, 'big') + int(1).to_bytes(1, 'big')
|
||||
data += int(len(path_data)).to_bytes(1, 'big')
|
||||
data += b'\0' * 9
|
||||
data += int(32).to_bytes(2, 'big')
|
||||
|
|
274
src/game.c
274
src/game.c
|
@ -2,7 +2,7 @@
|
|||
#include <gint/display.h>
|
||||
#include <gint/keyboard.h>
|
||||
#include <gint/timer.h>
|
||||
#define NENEMIES 96
|
||||
#include <gint/std/stdlib.h>
|
||||
|
||||
/* State definition, as used throughout the main game.
|
||||
*
|
||||
|
@ -22,6 +22,8 @@
|
|||
* slowed down or have various speeds, allowing some enemies to overtake
|
||||
* others. */
|
||||
|
||||
#define NENEMIES 256
|
||||
|
||||
struct enemy {
|
||||
int alive;
|
||||
|
||||
|
@ -34,6 +36,20 @@ struct enemy {
|
|||
struct enemy *pdr; /* previous enemy to be drawn */
|
||||
};
|
||||
|
||||
/* For each turret, the data is:
|
||||
*
|
||||
* - x, y: the coordinates of the turret. */
|
||||
|
||||
#define NTURRETS 256
|
||||
|
||||
struct turret {
|
||||
int exists;
|
||||
|
||||
int x, y;
|
||||
};
|
||||
|
||||
/* And the main state definition. */
|
||||
|
||||
struct state {
|
||||
/* Map information:
|
||||
*
|
||||
|
@ -58,20 +74,23 @@ struct state {
|
|||
int tbsp, tnsp;
|
||||
int nae;
|
||||
struct enemy *first_enemy;
|
||||
struct enemy *enemies;
|
||||
|
||||
/* Turrets information:
|
||||
*
|
||||
* - net: number of existing turrents.
|
||||
* - turrets: the turrets. */
|
||||
|
||||
int net;
|
||||
struct turret *turrets;
|
||||
|
||||
/* Camera, cursor and display information:
|
||||
*
|
||||
* - cam_x, cam_y: camera coordinates, always displays 16x8 tiles
|
||||
* - cur_x, cur_y: map coordinate for the cursor.
|
||||
* - drf: draw frame. */
|
||||
|
||||
int cam_x, cam_y;
|
||||
int cur_x, cur_y;
|
||||
int drf; /* draw frame, varies from 0 to 255 and looping */
|
||||
|
||||
/* Raw data */
|
||||
|
||||
struct enemy enemies[NENEMIES];
|
||||
};
|
||||
|
||||
static struct state state;
|
||||
|
@ -80,6 +99,102 @@ static struct state state;
|
|||
* Game functions.
|
||||
* --- */
|
||||
|
||||
/* tile_at(x, y): return the type of the tile at coordinates (x, y).
|
||||
*
|
||||
* If out of bounds, a basic placement tile (id. 0) will be returned */
|
||||
|
||||
static inline int tile_at(int x, int y)
|
||||
{
|
||||
if (x < 0 || x >= state.w || y < 0 || y >= state.h)
|
||||
return 3;
|
||||
return state.grid[y * state.w + x];
|
||||
}
|
||||
|
||||
/* init(map): initialize the game state. */
|
||||
|
||||
static void init(uint8_t const *raw)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Initialize the map information. */
|
||||
|
||||
state.w = raw[0];
|
||||
state.h = raw[1];
|
||||
state.sp_x = raw[2];
|
||||
state.sp_y = raw[3];
|
||||
state.nep = raw[6];
|
||||
state.grid = &raw[(raw[16] << 8) | raw[17]];
|
||||
state.ep = &raw[(raw[18] << 8) | raw[19]];
|
||||
|
||||
/* Initialize AI information */
|
||||
|
||||
state.tbsp = 99;
|
||||
state.tnsp = state.tbsp;
|
||||
state.nae = 0;
|
||||
state.first_enemy = NULL;
|
||||
|
||||
state.enemies = malloc(NENEMIES * sizeof (struct enemy));
|
||||
for (i = 0; i < NENEMIES; i++) {
|
||||
state.enemies[i].alive = 0;
|
||||
state.enemies[i].pdr = NULL;
|
||||
}
|
||||
|
||||
/* Initialize turret information */
|
||||
|
||||
state.net = 0;
|
||||
|
||||
state.turrets = malloc(NTURRETS * sizeof (struct turret));
|
||||
for (i = 0; i < NTURRETS; i++)
|
||||
state.turrets[i].exists = 0;
|
||||
|
||||
/* Initialize display information */
|
||||
|
||||
state.cur_x = raw[4];
|
||||
state.cur_y = raw[5];
|
||||
state.drf = 0;
|
||||
}
|
||||
|
||||
static void deinit()
|
||||
{
|
||||
free(state.enemies);
|
||||
}
|
||||
|
||||
/* place_turret(): place a turret at the cursor position. */
|
||||
|
||||
static void place_turret()
|
||||
{
|
||||
int i;
|
||||
struct turret *tp, *ntp = NULL;
|
||||
|
||||
/* Check if we can place a turret here. */
|
||||
|
||||
if (tile_at(state.cur_x, state.cur_y) != 0)
|
||||
return ;
|
||||
|
||||
/* Check if the slot is free. */
|
||||
|
||||
for (i = 0; i < NTURRETS; i++) {
|
||||
tp = &state.turrets[i];
|
||||
if (!tp->exists) {
|
||||
if (!ntp)
|
||||
ntp = tp;
|
||||
continue ;
|
||||
}
|
||||
|
||||
if (tp->x != state.cur_x || tp->y != state.cur_y)
|
||||
continue ;
|
||||
return ;
|
||||
}
|
||||
|
||||
/* The slot is free, let's go */
|
||||
|
||||
if (!ntp)
|
||||
return ;
|
||||
ntp->exists = 1;
|
||||
ntp->x = state.cur_x;
|
||||
ntp->y = state.cur_y;
|
||||
}
|
||||
|
||||
/* tick(): tick the game engine. */
|
||||
|
||||
static void tick()
|
||||
|
@ -146,7 +261,7 @@ static void tick()
|
|||
|
||||
/* TODO: use a given type of enemy? */
|
||||
|
||||
ep->nstepf = 20;
|
||||
ep->nstepf = 5;
|
||||
ep->nfwait = ep->nstepf;
|
||||
|
||||
state.nae++;
|
||||
|
@ -154,35 +269,39 @@ static void tick()
|
|||
}
|
||||
}
|
||||
|
||||
/* tile_cam(x, y): return the type of the tile at coordinates (x, y) relative
|
||||
* to the current camera position.
|
||||
*
|
||||
* If out of bounds, a basic placement tile (id. 0) will be returned */
|
||||
|
||||
static inline int tile_cam(int x, int y)
|
||||
{
|
||||
x += state.cam_x;
|
||||
y += state.cam_y;
|
||||
|
||||
if (x < 0 || x >= state.w || y < 0 || y >= state.h)
|
||||
return 0;
|
||||
return state.grid[y * state.w + x];
|
||||
}
|
||||
|
||||
/* draw(): basically the drawing function for everything. */
|
||||
|
||||
#define tile_cam(x, y) \
|
||||
(tile_at((x) + state.cur_x - 7, (y) + state.cur_y - 3))
|
||||
#define x_cam(x) \
|
||||
((x) - ((state.cur_x - 7) << 3))
|
||||
#define y_cam(y) \
|
||||
((y) - ((state.cur_y - 3) << 3))
|
||||
|
||||
static void draw()
|
||||
{
|
||||
extern bopti_image_t img_enemy_base, img_castle, img_wall;
|
||||
int x, y;
|
||||
|
||||
dclear(C_WHITE);
|
||||
|
||||
/* draw the grid */
|
||||
/* Draw the enemy base. */
|
||||
|
||||
dimage(x_cam(state.sp_x << 3), y_cam(state.sp_y << 3), &img_enemy_base);
|
||||
|
||||
/* Draw the grid. */
|
||||
|
||||
for (y = 0; y < 8; y++) {
|
||||
for (x = 0; x < 16; x++) {
|
||||
int ox = x << 3, oy = y << 3;
|
||||
int tile = tile_cam(x, y);
|
||||
int ox = x * 8, oy = y * 8;
|
||||
|
||||
/* Get the tiles. */
|
||||
|
||||
if (tile == 2)
|
||||
dimage(ox, oy, &img_castle);
|
||||
else if (tile == 3)
|
||||
dimage(ox, oy, &img_wall);
|
||||
|
||||
if (tile == 1 || tile == 2) {
|
||||
/* above, below, left, right */
|
||||
|
@ -215,16 +334,29 @@ static void draw()
|
|||
extern bopti_image_t img_enemy;
|
||||
|
||||
for (ep = state.first_enemy; ep; ep = ep->pdr)
|
||||
dimage(ep->x - (state.cam_x << 3), ep->y - (state.cam_y << 3),
|
||||
&img_enemy);
|
||||
dimage(x_cam(ep->x), y_cam(ep->y), &img_enemy);
|
||||
}
|
||||
|
||||
/* Draw the turrets. */
|
||||
|
||||
{
|
||||
struct turret *tp;
|
||||
extern bopti_image_t img_basic_turret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NTURRETS; i++) {
|
||||
tp = &state.turrets[i];
|
||||
|
||||
if (tp->exists)
|
||||
dimage(x_cam(tp->x << 3), y_cam(tp->y << 3), &img_basic_turret);
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw the cursor */
|
||||
|
||||
{
|
||||
int d = state.drf & 128 ? 2 : 0;
|
||||
int ox = state.cur_x - state.cam_x + 1;
|
||||
int oy = state.cur_y - state.cam_y + 1;
|
||||
int d = state.drf & 4 ? 2 : 0;
|
||||
int ox = 7 << 3, oy = 3 << 3;
|
||||
|
||||
dline(ox + d, oy, ox + d + 1, oy, C_BLACK);
|
||||
dline(ox + d + 4, oy, ox + d + 5, oy, C_BLACK);
|
||||
|
@ -237,10 +369,6 @@ static void draw()
|
|||
dline(ox + 7, oy + d + 4, ox + 7, oy + d + 5, C_BLACK);
|
||||
}
|
||||
|
||||
/* Debug data. */
|
||||
|
||||
dprint(16, 96, C_BLACK, "state.tnsp = 0x%08X", state.tnsp);
|
||||
|
||||
/* okay we can update now */
|
||||
|
||||
dupdate();
|
||||
|
@ -254,6 +382,10 @@ static void draw()
|
|||
* It's a best-effort system, so as the default timer is 128Hz, we'll check
|
||||
* each time for 54ms, which is 7/128. */
|
||||
|
||||
#define TIMERES_US 7813
|
||||
#define TIMECNT_DI 6
|
||||
#define TIMECNT_EV 4
|
||||
|
||||
static int timer_callback(int volatile *tick_finished)
|
||||
{
|
||||
*tick_finished = 1;
|
||||
|
@ -265,56 +397,23 @@ menu_t *game(menu_t *last_menu)
|
|||
extern uint8_t const map_basic[];
|
||||
int timer;
|
||||
int volatile tick_finished = 0;
|
||||
int etickleft = 2, dtickleft = 1;
|
||||
int etickleft = TIMECNT_EV, dtickleft = TIMECNT_DI;
|
||||
|
||||
(void)last_menu;
|
||||
|
||||
{
|
||||
uint8_t const *raw = map_basic;
|
||||
int i;
|
||||
|
||||
/* Initialize the map information. */
|
||||
|
||||
state.w = raw[0];
|
||||
state.h = raw[1];
|
||||
state.sp_x = raw[4];
|
||||
state.sp_y = raw[5];
|
||||
state.nep = raw[6];
|
||||
state.grid = &raw[(raw[16] << 8) | raw[17]];
|
||||
state.ep = &raw[(raw[18] << 8) | raw[19]];
|
||||
|
||||
/* Initialize AI information */
|
||||
|
||||
state.tbsp = 100;
|
||||
state.tnsp = state.tbsp;
|
||||
state.nae = 0;
|
||||
state.first_enemy = NULL;
|
||||
|
||||
for (i = 0; i < NENEMIES; i++) {
|
||||
state.enemies[i].alive = 0;
|
||||
state.enemies[i].pdr = NULL;
|
||||
}
|
||||
|
||||
/* Initialize display information */
|
||||
|
||||
state.cam_x = raw[2];
|
||||
state.cam_y = raw[3];
|
||||
state.cur_x = 1;
|
||||
state.cur_y = 1;
|
||||
state.drf = 0;
|
||||
}
|
||||
init(map_basic);
|
||||
|
||||
/* Setup the timer. */
|
||||
|
||||
timer = timer_setup(TIMER_ANY, 54000, &timer_callback, &tick_finished);
|
||||
timer = timer_setup(TIMER_ANY, TIMERES_US, &timer_callback, &tick_finished);
|
||||
timer_start(timer);
|
||||
|
||||
/* Main loop.
|
||||
* Some event processing and ticks thingies. */
|
||||
|
||||
do {
|
||||
key_event_t event = getkey_opt(GETKEY_MOD_SHIFT | GETKEY_BACKLIGHT,
|
||||
&tick_finished);
|
||||
key_event_t event = getkey_opt(GETKEY_MOD_SHIFT | GETKEY_BACKLIGHT
|
||||
| GETKEY_MENU, &tick_finished);
|
||||
|
||||
switch (event.type) {
|
||||
case KEYEV_NONE:
|
||||
|
@ -324,12 +423,12 @@ menu_t *game(menu_t *last_menu)
|
|||
tick_finished = 0;
|
||||
|
||||
if (!--etickleft) {
|
||||
etickleft = 2;
|
||||
etickleft = TIMECNT_EV;
|
||||
tick();
|
||||
}
|
||||
|
||||
if (!--dtickleft) {
|
||||
dtickleft = 1;
|
||||
dtickleft = TIMECNT_DI;
|
||||
state.drf = (state.drf + 1) & 255;
|
||||
|
||||
draw();
|
||||
|
@ -338,17 +437,36 @@ menu_t *game(menu_t *last_menu)
|
|||
|
||||
case KEYEV_DOWN:
|
||||
/* Incredible, the player is not dead!
|
||||
* Let's see what we can do to fulfill their request. */
|
||||
* Let's see what we can do to fulfill their request. */
|
||||
|
||||
if (event.key == KEY_EXE)
|
||||
goto game_end;
|
||||
switch (event.key) {
|
||||
case KEY_UP:
|
||||
if (state.cur_y > 0)
|
||||
state.cur_y--;
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
if (state.cur_y < state.h - 1)
|
||||
state.cur_y++;
|
||||
break;
|
||||
case KEY_LEFT:
|
||||
if (state.cur_x > 0)
|
||||
state.cur_x--;
|
||||
break;
|
||||
case KEY_RIGHT:
|
||||
if (state.cur_x < state.w - 1)
|
||||
state.cur_x++;
|
||||
break;
|
||||
case KEY_XOT:
|
||||
place_turret();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
game_end:
|
||||
/* Deinitialise things that need deinitialising. Duh. */
|
||||
|
||||
timer_stop(timer);
|
||||
deinit();
|
||||
return no_menu;
|
||||
}
|
||||
|
|
Reference in New Issue