Roaming monsters.

This commit is contained in:
Linus Probert 2017-12-17 13:43:41 +01:00
parent 7389c2d588
commit 6007976d23
20 changed files with 302 additions and 33 deletions

4
.vimrc
View File

@ -1,3 +1,3 @@
nnoremap <F1> :Make -C build --no-print-directory -l<cr> nnoremap <F1> :Make<cr>
set makeprg=make\ -C\ build\ --no-print-directory\ -l au FileType c setl makeprg=ninja\ -C\ build
let g:syntastic_c_include_dirs = [ 'linkedlist', 'hashtable' ] let g:syntastic_c_include_dirs = [ 'linkedlist', 'hashtable' ]

View File

@ -46,6 +46,7 @@ add_executable(breakhack
src/monster src/monster
src/stats src/stats
src/actiontext src/actiontext
src/random
) )
target_link_libraries(breakhack target_link_libraries(breakhack

View File

@ -6,10 +6,14 @@ x Add enemies (generated through lua)
x Making some better generation and randomeness x Making some better generation and randomeness
x Move "clip" from texture to sprite x Move "clip" from texture to sprite
x Hitting enemies x Hitting enemies
- Nicer enemy hits (Text textures, healthbars?) x Nicer enemy hits (Text textures, healthbars?)
- Moving enemies - This could need some love later on. (Misses seem to be broken) (Add hits to stats)
x Moving enemies
x Stupid roaming enemies
- Smart agressive enemies
- Fleeing enemies
- Lower levels - Lower levels
- XP o XP
- gui - gui
- Items - Items
- More gui - More gui

59
bhack_raw.out Normal file
View File

@ -0,0 +1,59 @@
==32279== Memcheck, a memory error detector
==32279== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==32279== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==32279== Command: ./build/breakhack
==32279== Parent PID: 16002
==32279==
==32279== Invalid write of size 1
==32279== at 0x10E8B7: monster_move (monster.c:112)
==32279== by 0x10C634: map_move_monsters (map.c:122)
==32279== by 0x10AF7C: run (main.c:155)
==32279== by 0x10B0CF: main (main.c:195)
==32279== Address 0x104ae5b0 is 2,992 bytes inside an unallocated block of size 11,760 in arena "client"
==32279==
==32279== Invalid write of size 1
==32279== at 0x10E9AB: monster_move (monster.c:127)
==32279== by 0x10C634: map_move_monsters (map.c:122)
==32279== by 0x10AF7C: run (main.c:155)
==32279== by 0x10B0CF: main (main.c:195)
==32279== Address 0x104ac1b0 is 240 bytes inside a block of size 704 free'd
==32279== at 0x4C2E14B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==32279== by 0x70D07B3: ??? (in /usr/lib/libfreetype.so.6.15.0)
==32279== by 0x70D0C99: ??? (in /usr/lib/libfreetype.so.6.15.0)
==32279== by 0x70D1378: ??? (in /usr/lib/libfreetype.so.6.15.0)
==32279== by 0x7080C4A: FT_Load_Glyph (in /usr/lib/libfreetype.so.6.15.0)
==32279== by 0x59E134D: ??? (in /usr/lib/libSDL2_ttf-2.0.so.0.14.0)
==32279== by 0x59E24A4: TTF_SizeUTF8 (in /usr/lib/libSDL2_ttf-2.0.so.0.14.0)
==32279== by 0x59E28A0: TTF_RenderUTF8_Solid (in /usr/lib/libSDL2_ttf-2.0.so.0.14.0)
==32279== by 0x59E2CB9: TTF_RenderText_Solid (in /usr/lib/libSDL2_ttf-2.0.so.0.14.0)
==32279== by 0x10B2CA: texture_load_from_text (texture.c:69)
==32279== by 0x10EC23: actiontext_set_text (actiontext.c:25)
==32279== by 0x10E4D8: monster_load_texts (monster.c:20)
==32279== Block was alloc'd at
==32279== at 0x4C2EF35: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==32279== by 0x7DD3D50: hb_set_create (in /usr/lib/libharfbuzz.so.0.10702.0)
==32279== by 0x70D04BE: ??? (in /usr/lib/libfreetype.so.6.15.0)
==32279== by 0x70D0C99: ??? (in /usr/lib/libfreetype.so.6.15.0)
==32279== by 0x70D1378: ??? (in /usr/lib/libfreetype.so.6.15.0)
==32279== by 0x7080C4A: FT_Load_Glyph (in /usr/lib/libfreetype.so.6.15.0)
==32279== by 0x59E134D: ??? (in /usr/lib/libSDL2_ttf-2.0.so.0.14.0)
==32279== by 0x59E24A4: TTF_SizeUTF8 (in /usr/lib/libSDL2_ttf-2.0.so.0.14.0)
==32279== by 0x59E28A0: TTF_RenderUTF8_Solid (in /usr/lib/libSDL2_ttf-2.0.so.0.14.0)
==32279== by 0x59E2CB9: TTF_RenderText_Solid (in /usr/lib/libSDL2_ttf-2.0.so.0.14.0)
==32279== by 0x10B2CA: texture_load_from_text (texture.c:69)
==32279== by 0x10EC23: actiontext_set_text (actiontext.c:25)
==32279==
==32279==
==32279== HEAP SUMMARY:
==32279== in use at exit: 23,660,148 bytes in 6,478 blocks
==32279== total heap usage: 145,650 allocs, 139,172 frees, 128,822,768 bytes allocated
==32279==
==32279== LEAK SUMMARY:
==32279== definitely lost: 0 bytes in 0 blocks
==32279== indirectly lost: 0 bytes in 0 blocks
==32279== possibly lost: 0 bytes in 0 blocks
==32279== still reachable: 0 bytes in 0 blocks
==32279== suppressed: 23,660,148 bytes in 6,478 blocks
==32279==
==32279== For counts of detected and suppressed errors, rerun with: -v
==32279== ERROR SUMMARY: 359 errors from 2 contexts (suppressed: 59281051 from 1900)

View File

@ -83,6 +83,15 @@ local enemies = {
{ texturePaths.pest0, texturePaths.pest1, 16, 112 }, { texturePaths.pest0, texturePaths.pest1, 16, 112 },
{ texturePaths.pest0, texturePaths.pest1, 32, 112 }, { texturePaths.pest0, texturePaths.pest1, 32, 112 },
{ texturePaths.pest0, texturePaths.pest1, 48, 112 }, { texturePaths.pest0, texturePaths.pest1, 48, 112 },
{ texturePaths.undead0, texturePaths.undead1, 0, 32 };
{ texturePaths.undead0, texturePaths.undead1, 16, 32 };
{ texturePaths.undead0, texturePaths.undead1, 32, 32 };
{ texturePaths.undead0, texturePaths.undead1, 48, 32 };
{ texturePaths.undead0, texturePaths.undead1, 64, 32 };
{ texturePaths.undead0, texturePaths.undead1, 80, 32 };
{ texturePaths.undead0, texturePaths.undead1, 96, 32 };
{ texturePaths.undead0, texturePaths.undead1, 112, 32 };
} }
local function repack(data) local function repack(data)

View File

@ -35,7 +35,7 @@ actiontext_render(ActionText *t, Camera *cam)
timer_start(t->timer); timer_start(t->timer);
Position cameraPos = camera_to_camera_position(cam, &t->pos); Position cameraPos = camera_to_camera_position(cam, &t->pos);
if (timer_get_ticks(t->timer) < 100) { if (timer_get_ticks(t->timer) < 300) {
texture_render(t->texture, &cameraPos, cam); texture_render(t->texture, &cameraPos, cam);
} else { } else {
timer_stop(t->timer); timer_stop(t->timer);

View File

@ -104,6 +104,7 @@ bool init()
gCamera.renderer = gRenderer; gCamera.renderer = gRenderer;
gRoomMatrix = roommatrix_create(); gRoomMatrix = roommatrix_create();
} }
return result; return result;
} }
@ -150,6 +151,16 @@ void run()
&gPlayer->sprite->pos); &gPlayer->sprite->pos);
roommatrix_build_lightmap(gRoomMatrix); roommatrix_build_lightmap(gRoomMatrix);
if (gPlayer->steps == gPlayer->stats.speed) {
player_print(gPlayer);
gPlayer->steps = 0;
Position rp =
position_to_matrix_coords(&gPlayer->sprite->pos);
gRoomMatrix->spaces[rp.x][rp.y].occupied = true;
gRoomMatrix->spaces[rp.x][rp.y].player = gPlayer;
map_move_monsters(gMap, gRoomMatrix);
}
SDL_RenderClear(gRenderer); SDL_RenderClear(gRenderer);
map_render(gMap, &gCamera); map_render(gMap, &gCamera);

View File

@ -110,6 +110,19 @@ map_add_monster(Map *map, Monster *m)
linkedlist_append(&map->monsters, m); linkedlist_append(&map->monsters, m);
} }
void
map_move_monsters(Map *map, RoomMatrix *rm)
{
LinkedList *m = map->monsters;
while (m) {
Monster *monster = m->data;
m = m->next;
if (!position_in_room(&monster->sprite->pos, &map->currentRoom))
continue;
monster_move(monster, rm);
}
}
int map_add_texture(Map *map, const char *path, SDL_Renderer *renderer) int map_add_texture(Map *map, const char *path, SDL_Renderer *renderer)
{ {
Texture *t = texture_create(); Texture *t = texture_create();
@ -156,7 +169,8 @@ void map_tile_render(Map *map, MapTile *tile, Position *pos, Camera *cam)
void map_render(Map *map, Camera *cam) void map_render(Map *map, Camera *cam)
{ {
unsigned int i, j, monster_count; unsigned int i, j;
LinkedList *monsterItem;
Room *room; Room *room;
if (!timer_started(map->renderTimer)) { if (!timer_started(map->renderTimer)) {
@ -184,9 +198,10 @@ void map_render(Map *map, Camera *cam)
} }
} }
monster_count = linkedlist_size(map->monsters); monsterItem = map->monsters;
for (i = 0; i < monster_count; ++i) { while (monsterItem) {
Monster *monster = linkedlist_get(&map->monsters, i); Monster *monster = monsterItem->data;
monsterItem = monsterItem->next;
monster_render(monster, cam); monster_render(monster, cam);
} }
} }

View File

@ -48,10 +48,12 @@ void map_add_decoration(Map *map, Position *tile_pos, MapTile*);
Texture* map_add_monster_texture(Map*, const char *path, SDL_Renderer*); Texture* map_add_monster_texture(Map*, const char *path, SDL_Renderer*);
void map_clear_dead_monsters(Map*);
void map_add_monster(Map*, Monster*); void map_add_monster(Map*, Monster*);
void map_move_monsters(Map*, RoomMatrix*);
void map_clear_dead_monsters(Map*);
void map_render(Map*, Camera*); void map_render(Map*, Camera*);
void map_set_current_room(Map*, Position*); void map_set_current_room(Map*, Position*);

View File

@ -1,7 +1,9 @@
#include <assert.h>
#include "monster.h" #include "monster.h"
#include "util.h" #include "util.h"
#include "player.h" #include "player.h"
#include "monster.h" #include "monster.h"
#include "random.h"
static void static void
monster_load_texts(Monster *m, SDL_Renderer *renderer) monster_load_texts(Monster *m, SDL_Renderer *renderer)
@ -27,7 +29,7 @@ monster_create(SDL_Renderer *renderer)
Monster *m = ec_malloc(sizeof(Monster)); Monster *m = ec_malloc(sizeof(Monster));
m->sprite = sprite_create(); m->sprite = sprite_create();
m->sprite->clip = (SDL_Rect) { 0, 0, 16, 16 }; m->sprite->clip = (SDL_Rect) { 0, 0, 16, 16 };
m->stats = (Stats) { 11, 1, 0, 0, 1 }; m->stats = (Stats) { 11, 1, 0, 0, 1, 1 };
monster_load_texts(m, renderer); monster_load_texts(m, renderer);
@ -45,6 +47,93 @@ monster_update_pos(Monster *m, Position pos)
m->missText->pos = textPos; m->missText->pos = textPos;
} }
static bool
has_collided(Monster *monster, RoomMatrix *matrix)
{
if (!position_in_room(&monster->sprite->pos, &matrix->roomPos))
return true;
Position roomPos = position_to_matrix_coords(&monster->sprite->pos);
RoomSpace *space = &matrix->spaces[roomPos.x][roomPos.y];
return space->occupied;
}
static bool
move_left(Monster *m, RoomMatrix *rm)
{
m->sprite->pos.x -= TILE_DIMENSION;
if (has_collided(m, rm)) {
m->sprite->pos.x += TILE_DIMENSION;
return false;
}
return true;
}
static bool
move_right(Monster *m, RoomMatrix *rm)
{
m->sprite->pos.x += TILE_DIMENSION;
if (has_collided(m, rm)) {
m->sprite->pos.x -= TILE_DIMENSION;
return false;
}
return true;
}
static bool
move_up(Monster *m, RoomMatrix *rm)
{
m->sprite->pos.y -= TILE_DIMENSION;
if (has_collided(m, rm)) {
m->sprite->pos.y += TILE_DIMENSION;
return false;
}
return true;
}
static bool
move_down(Monster *m, RoomMatrix *rm)
{
m->sprite->pos.y += TILE_DIMENSION;
if (has_collided(m, rm)) {
m->sprite->pos.y -= TILE_DIMENSION;
return false;
}
return true;
}
void
monster_move(Monster *m, RoomMatrix *rm)
{
unsigned int i, maxMoveAttempts = 6;
Position monsterRoomPos;
if (get_random(4) == 0)
return;
monsterRoomPos = position_to_matrix_coords(&m->sprite->pos);
rm->spaces[monsterRoomPos.x][monsterRoomPos.y].occupied = false;
rm->spaces[monsterRoomPos.x][monsterRoomPos.y].monster = NULL;
for (i = 0; i < maxMoveAttempts; ++i) {
int move = get_random(3);
if (move == 0 && move_left(m, rm))
break;
else if (move == 1 && move_right(m, rm))
break;
else if (move == 2 && move_up(m, rm))
break;
else if (move == 3 && move_down(m, rm))
break;
}
monster_update_pos(m, m->sprite->pos);
monsterRoomPos = position_to_matrix_coords(&m->sprite->pos);
rm->spaces[monsterRoomPos.x][monsterRoomPos.y].occupied = true;
rm->spaces[monsterRoomPos.x][monsterRoomPos.y].monster = m;
}
void void
monster_render(Monster *m, Camera *cam) monster_render(Monster *m, Camera *cam)
{ {

View File

@ -17,6 +17,8 @@ Monster* monster_create(SDL_Renderer*);
void monster_update_pos(Monster*, Position); void monster_update_pos(Monster*, Position);
void monster_move(Monster*, RoomMatrix*);
void monster_render(Monster*, Camera*); void monster_render(Monster*, Camera*);
void monster_destroy(Monster*); void monster_destroy(Monster*);

View File

@ -3,11 +3,11 @@
#include "monster.h" #include "monster.h"
static Stats classStats[] = { static Stats classStats[] = {
(Stats) { 11, 5, 7, 2, 1 }, /* ENGINEER */ (Stats) { 11, 5, 7, 2, 1, 1 }, /* ENGINEER */
(Stats) { 11, 5, 7, 2, 1 }, /* MAGE */ (Stats) { 11, 5, 7, 2, 1, 1 }, /* MAGE */
(Stats) { 11, 5, 7, 2, 1 }, /* PALADIN */ (Stats) { 11, 5, 7, 2, 1, 1 }, /* PALADIN */
(Stats) { 11, 5, 7, 2, 2 }, /* ROGUE */ (Stats) { 11, 5, 7, 2, 2, 1 }, /* ROGUE */
(Stats) { 11, 5, 7, 2, 1 }, /* WARRIOR */ (Stats) { 11, 5, 7, 2, 1, 1 }, /* WARRIOR */
}; };
static bool static bool
@ -27,22 +27,42 @@ has_collided(Player *player, RoomMatrix *matrix)
if (space->monster != NULL) { if (space->monster != NULL) {
unsigned int hit = stats_fight(&player->stats, unsigned int hit = stats_fight(&player->stats,
&space->monster->stats); &space->monster->stats);
if (hit > 0) if (hit > 0) {
space->monster->hitText->active = true; space->monster->hitText->active = true;
else space->monster->missText->active = false;
} else {
// TODO(Linus): The misses seem to be missing
space->monster->missText->active = true; space->monster->missText->active = true;
space->monster->hitText->active = false;
}
if (space->monster->stats.hp <= 0) {
// TODO(Linus): This needs some love later on.
player->xp += 10;
}
} }
return collided; return collided;
} }
static void
player_step(Player *p, RoomMatrix* m)
{
Position pos = position_to_matrix_coords(&p->sprite->pos);
m->spaces[pos.x][pos.y].occupied = true;
p->total_steps++;
p->steps++;
}
static void static void
move_left(Player *player, RoomMatrix *matrix) move_left(Player *player, RoomMatrix *matrix)
{ {
player->sprite->clip.y = 16; player->sprite->clip.y = 16;
player->sprite->pos.x -= TILE_DIMENSION; player->sprite->pos.x -= TILE_DIMENSION;
if (has_collided(player, matrix)) if (has_collided(player, matrix)) {
player->sprite->pos.x += TILE_DIMENSION; player->sprite->pos.x += TILE_DIMENSION;
}
player_step(player, matrix);
} }
static static
@ -50,8 +70,10 @@ void move_right(Player *player, RoomMatrix *matrix)
{ {
player->sprite->clip.y = 32; player->sprite->clip.y = 32;
player->sprite->pos.x += TILE_DIMENSION; player->sprite->pos.x += TILE_DIMENSION;
if (has_collided(player, matrix)) if (has_collided(player, matrix)) {
player->sprite->pos.x -= TILE_DIMENSION; player->sprite->pos.x -= TILE_DIMENSION;
}
player_step(player, matrix);
} }
static static
@ -59,8 +81,10 @@ void move_up(Player *player, RoomMatrix *matrix)
{ {
player->sprite->clip.y = 48; player->sprite->clip.y = 48;
player->sprite->pos.y -= TILE_DIMENSION; player->sprite->pos.y -= TILE_DIMENSION;
if (has_collided(player, matrix)) if (has_collided(player, matrix)) {
player->sprite->pos.y += TILE_DIMENSION; player->sprite->pos.y += TILE_DIMENSION;
}
player_step(player, matrix);
} }
static static
@ -68,8 +92,10 @@ void move_down(Player *player, RoomMatrix *matrix)
{ {
player->sprite->clip.y = 0; player->sprite->clip.y = 0;
player->sprite->pos.y += TILE_DIMENSION; player->sprite->pos.y += TILE_DIMENSION;
if (has_collided(player, matrix)) if (has_collided(player, matrix)) {
player->sprite->pos.y -= TILE_DIMENSION; player->sprite->pos.y -= TILE_DIMENSION;
}
player_step(player, matrix);
} }
static static
@ -109,6 +135,9 @@ player_create(class_t class, SDL_Renderer *renderer)
{ {
Player *player = malloc(sizeof(Player)); Player *player = malloc(sizeof(Player));
player->sprite = sprite_create(); player->sprite = sprite_create();
player->total_steps = 0;
player->steps = 0;
player->xp = 0;
char asset[100]; char asset[100];
switch (class) { switch (class) {
@ -144,6 +173,21 @@ player_create(class_t class, SDL_Renderer *renderer)
return player; return player;
} }
void
player_print(Player *p)
{
Position roomPos = position_to_matrix_coords(&p->sprite->pos);
Position pos = p->sprite->pos;
printf("\n");
printf("--------=== <[ Player Stats ]> ===--------\n");
printf("HP: %u\n", p->stats.hp);
printf("Level: %u XP: %u\n", p->stats.lvl, p->xp);
printf("Pos: %dx%d RoomPos: %dx%d\n", pos.x, pos.y, roomPos.x, roomPos.y);
printf("Steps: %u\n", p->total_steps);
printf("------------------------------------------\n");
}
void void
player_destroy(Player *player) player_destroy(Player *player)
{ {

View File

@ -11,11 +11,16 @@ typedef enum PlayerClass class_t;
typedef struct Player_t { typedef struct Player_t {
Sprite *sprite; Sprite *sprite;
Stats stats; Stats stats;
unsigned int xp;
unsigned int total_steps;
unsigned int steps;
void (*handle_event)(struct Player_t*, RoomMatrix*, SDL_Event*); void (*handle_event)(struct Player_t*, RoomMatrix*, SDL_Event*);
} Player; } Player;
Player* player_create(class_t, SDL_Renderer*); Player* player_create(class_t, SDL_Renderer*);
void player_print(Player*);
void player_destroy(Player*); void player_destroy(Player*);
#endif // PLAYER_H_ #endif // PLAYER_H_

View File

@ -29,6 +29,12 @@ Position position_to_room_coords(Position *src)
return pos; return pos;
} }
bool
position_equals(const Position *p1, const Position *p2)
{
return p1->x == p2->x && p1->y == p2->y;
}
bool position_in_room(Position *pos, Position *roomPos) bool position_in_room(Position *pos, Position *roomPos)
{ {
unsigned int room_px_width, room_px_height, room_x_px, room_y_px; unsigned int room_px_width, room_px_height, room_x_px, room_y_px;

View File

@ -14,4 +14,6 @@ Position position_to_room_coords(Position*);
bool position_in_room(Position *pos, Position *roomPos); bool position_in_room(Position *pos, Position *roomPos);
bool position_equals(const Position*, const Position*);
#endif // POSITION_H_ #endif // POSITION_H_

10
src/random.c Normal file
View File

@ -0,0 +1,10 @@
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
unsigned int
get_random(unsigned int max)
{
srand(time(NULL));
return rand() % (max + 1);
}

7
src/random.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef RANDOM_H_
#define RANDOM_H_
unsigned int
get_random(unsigned int max);
#endif // RANDOM_H_

View File

@ -12,10 +12,11 @@ RoomMatrix* roommatrix_create()
void roommatrix_populate_from_map(RoomMatrix *rm, Map *m) void roommatrix_populate_from_map(RoomMatrix *rm, Map *m)
{ {
int i, j, monster_count; int i, j;
Position monster_matrix_pos; Position monster_matrix_pos;
Room *r; Room *r;
Monster *monster; Monster *monster;
LinkedList *monsterItem;
roommatrix_reset(rm); roommatrix_reset(rm);
@ -41,11 +42,14 @@ void roommatrix_populate_from_map(RoomMatrix *rm, Map *m)
} }
} }
monster_count = linkedlist_size(m->monsters); monsterItem = m->monsters;
for (i = 0; i < monster_count; ++i) { while (monsterItem) {
monster = linkedlist_get(&m->monsters, i); monster = monsterItem->data;
monsterItem = monsterItem->next;
if (!position_in_room(&monster->sprite->pos, &m->currentRoom)) if (!position_in_room(&monster->sprite->pos, &m->currentRoom))
continue; continue;
monster_matrix_pos = monster_matrix_pos =
position_to_matrix_coords(&monster->sprite->pos); position_to_matrix_coords(&monster->sprite->pos);

View File

@ -1,10 +1,9 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h> #include <stdbool.h>
#include <SDL2/SDL_ttf.h> #include <SDL2/SDL_ttf.h>
#include "stats.h" #include "stats.h"
#include "random.h"
unsigned int unsigned int
stats_fight(Stats *attacker, Stats *defender) stats_fight(Stats *attacker, Stats *defender)
@ -12,12 +11,11 @@ stats_fight(Stats *attacker, Stats *defender)
unsigned int atkRoll, defRoll, dmgRoll; unsigned int atkRoll, defRoll, dmgRoll;
bool critical = false; bool critical = false;
srand(time(NULL)); atkRoll = get_random(19) + 1;
atkRoll = (rand() % 20);
if (atkRoll == 20) if (atkRoll == 20)
critical = true; critical = true;
atkRoll += attacker->atk; atkRoll += attacker->atk;
defRoll = (rand() % 20) + defender->def; defRoll = (get_random(19) + 1) + defender->def;
dmgRoll = 0; dmgRoll = 0;
//printf("Attacking: %u, Defending: %u\n", atkRoll, defRoll); //printf("Attacking: %u, Defending: %u\n", atkRoll, defRoll);

View File

@ -7,6 +7,7 @@ typedef struct {
int atk; /* Attack rating */ int atk; /* Attack rating */
int def; /* Defence rating */ int def; /* Defence rating */
int speed; /* Speed */ int speed; /* Speed */
int lvl; /* Level */
} Stats; } Stats;
unsigned int unsigned int