4coder/test_data/lots_of_files/jam.cpp

2301 lines
85 KiB
C++

/*
* Overreact - Mr. 4th Dimention
* Allen Webster
* 03.09.2015 (mm.dd.yyyy)
*
* Game Layer.
*/
// TOP
internal Memory_Block
make_memory_block(void *base, i32 size){
Memory_Block block;
block.base = base;
block.size = size;
block.cursor = 0;
return block;
}
internal void*
get_memory_size(Memory_Block *block, i32 size){
Assert(block->cursor + size <= block->size);
void *result = (u8*)block->base + block->cursor;
block->cursor += size;
return result;
}
#define get_mem(t,block) (t*)get_memory_size(block, sizeof(t))
#define get_mem_size(block,size) get_memory_size(block, size)
#define get_mem_array(t,block,size) (t*)get_memory_size(block, sizeof(t)*(size))
#define remaining_mem_i32(block) (i32)((block).size - (block).cursor)
#define get_i(x,y) ((x)+(y)*WIDTH)
struct Temp_Memory{
Memory_Block *block;
i32 prev_cursor;
};
internal Temp_Memory
begin_temp_memory(Memory_Block *block){
Temp_Memory result;
result.block = block;
result.prev_cursor = block->cursor;
return result;
}
internal void
end_temp_memory(Temp_Memory temp){
temp.block->cursor = temp.prev_cursor;
}
struct Wave_Pair{
i32 left, right;
};
internal bool32
mix_track(Track *track, Wave_Pair *value){
bool32 result = 0;
if (track->playing && track->frames_delay == 0){
Sound sound = track->sound;
i16 *src_prev = sound.samples + track->sample_pos*2;
i16 *src_next;
if (track->sample_pos + 1 == sound.sample_count){
src_next = sound.samples;
}
else{
src_next = src_prev + 2;
}
real32 dec = track->sample_pos_dec;
real32 inv_dec = 1.f - dec;
dec *= track->volume;
inv_dec *= track->volume;
value->left += (i32)((*src_prev)*inv_dec + (*src_next)*dec);
++src_prev;
++src_next;
value->right += (i16)((*src_prev)*inv_dec + (*src_next)*dec);
track->sample_pos_dec += sound.scan_speed*track->bend;
if (track->sample_pos_dec >= 1.f){
track->sample_pos += (i32)(track->sample_pos_dec);
track->sample_pos_dec = DecPart(track->sample_pos_dec);
if (track->sample_pos >= sound.sample_count){
if (track->looping){
track->sample_pos -= sound.sample_count;
}
else{
track->playing = 0;
result = 1;
}
}
}
}
return result;
}
internal
SIG_APP_FILL_SAMPLES(game_fill_samples){
App_Vars *vars = (App_Vars*)memory.data;
if (vars->silence_timer == 0){
#if 0
i16 *dst = target.samples;
for (i32 i = 0; i < target.count_per_channel; ++i){
real32 wave_hz = 440.f;
real32 t = vars->wave_t * TAU32;
real32 v = sinf(t);
i16 value = (i16)(max_i16 * v);
*dst++ = value;
*dst++ = value;
vars->wave_t += wave_hz / target.samples_per_second;
}
vars->wave_t = DecPart(vars->wave_t);
#endif
i16 *dst = target.samples;
for (i32 i = 0; i < target.count_per_channel; ++i){
Wave_Pair value = {};
for (i32 j = 0; j < ArrayCount(vars->sfx_tracks); ++j){
Track *track = vars->sfx_tracks + j;
if (mix_track(track, &value)){
track->next_free = vars->sfx_free;
vars->sfx_free = track;
}
}
if (vars->music.playing){
if (mix_track(&vars->music, &value)){
vars->song_done = 1;
}
}
if (value.left > max_i16){
value.left = max_i16;
}
else if (value.left < min_i16){
value.left = min_i16;
}
if (value.right > max_i16){
value.right = max_i16;
}
else if (value.right < min_i16){
value.right = min_i16;
}
*dst++ = (i16)value.left;
*dst++ = (i16)value.right;
}
}
for (i32 j = 0; j < ArrayCount(vars->sfx_tracks); ++j){
Track *track = vars->sfx_tracks + j;
if (track->playing && track->frames_delay > 0){
--track->frames_delay;
}
}
}
#define A_SHIFT 24
#define R_SHIFT 16
#define G_SHIFT 8
#define B_SHIFT 0
#define A_MASK 0xFF000000
#define R_MASK 0x00FF0000
#define G_MASK 0x0000FF00
#define B_MASK 0x000000FF
internal u32
compress_color(Vec3 color){
return
((u8)(255*color.r) << R_SHIFT) +
((u8)(255*color.g) << G_SHIFT) +
((u8)(255*color.b) << B_SHIFT);
}
internal u32
compress_color(Vec4 color){
return
((u8)(255*color.a) << A_SHIFT) +
((u8)(255*color.r) << R_SHIFT) +
((u8)(255*color.g) << G_SHIFT) +
((u8)(255*color.b) << B_SHIFT);
}
internal Vec3
decompress_color(u32 color){
return V3(((color & R_MASK) >> R_SHIFT) / 255.f,
((color & G_MASK) >> G_SHIFT) / 255.f,
((color & B_MASK) >> B_SHIFT) / 255.f);
}
internal Vec4
decompress_color_alpha(u32 color){
return V4(((color & R_MASK) >> R_SHIFT) / 255.f,
((color & G_MASK) >> G_SHIFT) / 255.f,
((color & B_MASK) >> B_SHIFT) / 255.f,
((color & A_MASK) >> A_SHIFT) / 255.f);
}
internal void
draw_clear(Vec4 color){
glClearColor(color.r, color.g, color.b, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
internal Render_Texture
make_render_texture(Image image){
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height,
0, GL_RGBA, GL_UNSIGNED_BYTE, image.data);
Render_Texture texture;
texture.texid = tex;
texture.width = image.width;
texture.height = image.height;
texture.img_width = image.img_width;
texture.img_height = image.img_height;
texture.tex_x = (real32)texture.img_width / texture.width;
texture.tex_y = (real32)texture.img_height / texture.height;
return texture;
}
internal void
draw_texture(Render_Texture texture, Vec2 center, Vec2 halfdim,
real32 rotation = 0.f, Vec4 blend = {1.f, 1.f, 1.f, 1.f}){
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(center.x, center.y, 0.f);
glRotatef(rotation, 0, 0, 1);
glColor4f(blend.r, blend.g, blend.b, blend.a);
glBindTexture(GL_TEXTURE_2D, texture.texid);
glBegin(GL_QUADS);
{
glTexCoord2f(0.f, texture.tex_y);
glVertex3f(-halfdim.x, halfdim.y, 0.f);
glTexCoord2f(texture.tex_x, texture.tex_y);
glVertex3f(halfdim.x, halfdim.y, 0.f);
glTexCoord2f(texture.tex_x, 0.f);
glVertex3f(halfdim.x, -halfdim.y, 0.f);
glTexCoord2f(0.f, 0.f);
glVertex3f(-halfdim.x, -halfdim.y, 0.f);
}
glEnd();
glPopMatrix();
}
inline internal void
draw_texture(Render_Texture texture, Vec2 center,
real32 rotation = 0.f, Vec4 blend = {1.f, 1.f, 1.f, 1.f}){
Vec2 halfdim;
halfdim.x = texture.img_width * 0.5f;
halfdim.y = texture.img_height * 0.5f;
draw_texture(texture, center, halfdim, rotation, blend);
}
internal void
draw_rectangle(real32 x1, real32 y1,
real32 x2, real32 y2,
Vec4 color, real32 z = 0.f){
glBindTexture(GL_TEXTURE_2D, 0);
glColor4f(color.r, color.g, color.b, color.a);
glBegin(GL_POLYGON);
{
glVertex3f(x1, y2, z);
glVertex3f(x2, y2, z);
glVertex3f(x2, y1, z);
glVertex3f(x1, y1, z);
}
glEnd();
}
internal void
draw_text(Game_Render_Target *target, Font *font, real32 *x, real32 *y, char *str, i32 len, Vec4 color,
real32 start_x = 0.f, real32 x_limit = 10000.f){
u32 packed_color = compress_color(color);
for (i32 i = 0; i < len; ++i){
char c = str[i];
font_draw_glyph(target, font, c, *x, *y, packed_color);
*x += font->glyphs[c].advance;
if (*x > x_limit){
*x = start_x;
*y += font->height;
}
}
}
internal bool32
load_texture(char *filename, Render_Texture *texture, Memory_Block *block){
bool32 result = 0;
Image image;
Bitmap_File file = {};
bitmap_open_file(filename, &file);
if (file.file.data){
Temp_Memory temp = begin_temp_memory(block);
i32 size = bitmap_data_requirement(&file);
image.data = (u32*)get_mem_size(block, size);
bitmap_fill_image(&file, &image);
bitmap_free_file(&file);
*texture = make_render_texture(image);
end_temp_memory(temp);
result = 1;
}
return result;
}
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
internal bool32
load_texture_png(char *filename, Render_Texture *texture, Memory_Block *block){
Image image;
i32 n;
image.data = (u32*)stbi_load(filename, &image.width, &image.height, &n, 0);
TentativeAssert(image.data);
if (image.data){
Assert(n == 4);
Image POT_image;
POT_image.width = round_up_POT(image.width);
POT_image.height = round_up_POT(image.height);
POT_image.img_width = image.width;
POT_image.img_height = image.height;
Temp_Memory temp = begin_temp_memory(block);
POT_image.data = get_mem_array(u32, block, POT_image.width*POT_image.height);
u32 *src_line = image.data;
u32 *dst_line = POT_image.data;
for (i32 y = 0; y < image.height; ++y){
u32 *src = src_line;
u32 *dst = dst_line;
for (i32 x = 0; x < image.width; ++x){
*dst++ = *src++;
}
src_line += image.width;
dst_line += POT_image.width;
}
*texture = make_render_texture(POT_image);
end_temp_memory(temp);
stbi_image_free(image.data);
return 1;
}
return 0;
}
internal bool32
load_sound(char *filename, Sound *sound, i32 target_samples_per_second, Memory_Block *block){
bool32 result = 0;
Wav_File file;
wav_open_file(filename, &file);
if (file.file.data){
i32 size = wav_data_requirement(&file);
sound->samples = (i16*)get_mem_size(block, size);
wav_fill_sound(&file, sound, target_samples_per_second);
result = 1;
}
return result;
}
internal bool32
load_font(char *filename, Font *font, i32 pt_size, Memory_Block *block){
bool32 result = 0;
Temp_Memory temp = begin_temp_memory(block);
i32 size = remaining_mem_i32(*block);
void *mem = get_mem_size(block, size);
if (font_load(filename, font, pt_size, mem, size)){
result = 0;
}
end_temp_memory(temp);
return result;
}
internal bool32
can_move_to(App_Vars *vars, Entity *entity, i32 x, i32 y, bool32 *out_of_grid){
AllowLocal(entity);
bool32 result = 1;
if (y >= HEIGHT || y < 0 ||
x >= WIDTH || x < 0){
result = 0;
*out_of_grid = 1;
}
else{
Entity *other_entity = vars->grid[get_i(x, y)];
if (other_entity){
result = 0;
}
*out_of_grid = 0;
}
return result;
}
inline internal bool32
can_move_to(App_Vars *vars, Entity *entity, i32 x, i32 y){
bool32 throw_away;
return can_move_to(vars, entity, x, y, &throw_away);
}
internal Entity*
spawn_entity(App_Vars *vars){
Assert(vars->entity_count < vars->entity_max);
Entity *result = vars->entities + vars->entity_count++;
*result = {};
return result;
}
internal void
make_random_block(App_Vars *vars){
Assert(vars->spawn_count < vars->spawn_max);
Spawn_Request *spawn = vars->spawns + vars->spawn_count++;
spawn->random = 1;
}
internal void
make_zombie(App_Vars *vars, i32 x, i32 y){
Assert(vars->spawn_count < vars->spawn_max);
Spawn_Request *spawn = vars->spawns + vars->spawn_count++;
spawn->random = 0;
spawn->x = x;
spawn->y = y;
spawn->type = ZOMBIE;
}
internal Entity*
make_brain(App_Vars *vars, i32 x, i32 y){
Assert(vars->spawn_count < vars->spawn_max);
Spawn_Request *spawn = vars->spawns + vars->spawn_count++;
spawn->random = 0;
spawn->x = x;
spawn->y = y;
spawn->type = BRAIN;
}
internal void
move_entity(App_Vars *vars, Entity *entity, i32 x, i32 y){
i32 old_i = get_i(entity->grid_x, entity->grid_y);
i32 new_i = get_i(x, y);
Assert(vars->grid[old_i] == entity);
Assert(vars->grid[new_i] == 0);
vars->grid[old_i] = 0;
vars->grid[new_i] = entity;
entity->grid_x = x;
entity->grid_y = y;
}
internal Entity*
get_grid_entity(App_Vars *vars, i32 x, i32 y, bool32 *out_of_bounds){
if (x < 0 || y < 0 || x >= WIDTH || y >= HEIGHT){
*out_of_bounds = 1;
return 0;
}
else{
*out_of_bounds = 0;
return vars->grid[get_i(x, y)];
}
}
inline internal Entity*
get_grid_entity(App_Vars *vars, i32 x, i32 y){
bool32 throw_away;
return get_grid_entity(vars, x, y, &throw_away);
}
internal i32
points_for_destruction(Block_Type type){
i32 result = 0;
switch (type){
case ZOMBIE:
result = 15;
break;
case HUMAN:
result = 1;
break;
case BRAIN:
result = 1;
break;
case AMMO:
result = 1;
break;
case BOMB:
result = 5;
break;
case WALL:
result = 100;
break;
}
return result;
}
internal bool32
play_sound_effect(App_Vars *vars, Sound sound, real32 volume, real32 bend,
i32 delay = 0){
bool32 result = 0;
if (vars->sfx_free){
result = 1;
Track *track = vars->sfx_free;
vars->sfx_free = track->next_free;
*track = {};
track->sound = sound;
track->playing = 1;
track->volume = volume;
track->bend = bend;
track->frames_delay = delay;
}
return result;
}
internal real32
random_real32(App_Vars *vars, real32 min, real32 max){
real32 result;
u32 x = pcg32_random_r(&vars->rnd) % 1000000;
result = min + (x / 1000000.f)*(max - min);
return result;
}
globalvar real32 BEND_UP = 1.2f;
globalvar real32 BEND_DOWN = 0.8f;
internal void
mark_block_destroyed(App_Vars *vars, Entity *entity){
if (!entity->death_marked){
Assert(vars->free_count < vars->free_max);
vars->to_free[vars->free_count++] = entity;
vars->need_to_fill_gaps = 1;
vars->fill_timer = COMBO_TIME;
entity->death_marked = 1;
vars->score += points_for_destruction(entity->type);
vars->nonsense_score = pcg32_random_r(&vars->rnd) % 1000;
}
}
inline internal i32
get_entity_index(App_Vars *vars, Entity *entity){
Assert(entity >= vars->entities);
return (i32)(entity - vars->entities);
}
inline internal Grid_Pos
get_grid_pos(Entity *entity){
Grid_Pos result;
result.x = entity->grid_x;
result.y = entity->grid_y;
return result;
}
struct Entity_Y_Data{
i32 index, y;
};
internal i32
quick_partition(Entity_Y_Data *data, i32 start, i32 pivot){
i32 mid = (start + pivot)/2;
Swap(Entity_Y_Data, data[mid], data[pivot]);
i32 pivot_y = data[pivot].y;
for (i32 i = start; i < pivot; ++i){
if (data[i].y < pivot_y){
Swap(Entity_Y_Data, data[i], data[start]);
++start;
}
}
Swap(Entity_Y_Data, data[pivot], data[start]);
return start;
}
internal void
quick_sort(Entity_Y_Data *data, i32 start, i32 pivot){
if (start < pivot){
i32 mid = quick_partition(data, start, pivot);
quick_sort(data, start, mid-1);
quick_sort(data, mid+1, pivot);
}
}
internal i32
quick_partition(void **data, i32 start, i32 pivot){
i32 mid = (start + pivot)/2;
Swap(void*, data[mid], data[pivot]);
void *pivot_ptr = data[pivot];
for (i32 i = start; i < pivot; ++i){
if (data[i] > pivot_ptr){
Swap(void*, data[i], data[start]);
++start;
}
}
Swap(void*, data[pivot], data[start]);
return start;
}
internal void
quick_sort(void **data, i32 start, i32 pivot){
if (start < pivot){
i32 mid = quick_partition(data, start, pivot);
quick_sort(data, start, mid-1);
quick_sort(data, mid+1, pivot);
}
}
internal i32
int_to_string(char *buffer, i32 x, i32 force_len = 0){
Assert(x >= 0);
i32 i = 16;
while (x != 0){
char c = (char)(x % 10);
x /= 10;
c += '0';
buffer[--i] = c;
}
i32 l = 16 - i;
while (l < force_len){
++l;
buffer[--i] = '0';
}
return i;
}
internal bool32
do_text_field(i32 my_index, i32 active_index, bool32 blink_on,
Game_Render_Target *render_target, Game_Input input,
Font *font, real32 text_x, real32 text_y,
i32 *len, char *buffer, i32 max){
bool32 active = (my_index == active_index);
if (active){
if (input.key_input){
if (input.key_code == 0){
if (*len > 0){
*len -= 1;
}
}
else if (input.key_code != 1){
if (*len < max){
buffer[*len] = input.key_code;
*len += 1;
}
}
}
}
Vec4 color = V4(0.9f, 0.9f, 0.9f, 1.f);
if (active){
color = V4(1.f, 1.f, 1.f, 1.f);
}
draw_text(render_target, font, &text_x, &text_y, buffer, *len, color);
if (active && blink_on){
char cursor = '|';
draw_text(render_target, font, &text_x, &text_y, &cursor, 1, color);
}
return (active && input.key_input && input.key_code == 1);
}
internal bool32
do_button(i32 my_index, i32 active_index,
Game_Input input, Render_Texture *textures,
real32 x, real32 y){
bool32 active = (my_index == active_index);
draw_texture(textures[active], V2(x, y));
return (active && input.key_input && input.key_code == 1);
}
#define START_WITH_GAME_OVER 0
#define SKIP_TITLE_SCREEN 0
inline internal i32
get_level_fall_tick_time(App_Vars *vars, i32 level){
i32 real_level = level;
if (real_level >= ArrayCount(levels)){
real_level = ArrayCount(levels) - 1;
}
return (levels[real_level].fall_tick_time);
}
inline internal i32
get_level_min_score(App_Vars *vars, i32 level){
i32 result;
i32 real_level = level;
if (real_level >= ArrayCount(levels)){
result = levels[ArrayCount(levels)-1].fall_tick_time;
result = (level - ArrayCount(levels) + 1)*1200;
}
else{
result = (levels[real_level].min_score);
}
return result;
}
internal void
game_set_to_new(App_Vars *vars){
vars->level = 0;
vars->fall_timer = get_level_fall_tick_time(vars, vars->level);
vars->spawn_count = 0;
vars->spawn_max = ArrayCount(vars->spawns);
vars->free_count = 0;
vars->free_max = ArrayCount(vars->to_free);
vars->entity_count = 0;
vars->entity_max = ArrayCount(vars->entities);
vars->need_new_block = 1;
vars->particle_count = 0;
vars->particle_max = ArrayCount(vars->particles);
vars->refall_timer = 0;
vars->game_over = 0;
vars->need_to_fill_gaps = 0;
vars->chain_reacting = 0;
vars->reaction_timer = 0;
vars->fill_timer = 0;
vars->score = 0;
vars->nonsense_score = 0;
vars->active_field = 0;
vars->blink_timer = 0;
vars->user_name_len = 0;
vars->user_token_len = 0;
memset(vars->grid, 0, sizeof(Entity*)*WIDTH*HEIGHT);
vars->looked_up_teaser = 0;
vars->teaser_name_len = 0;
vars->teaser_score_len = 0;
vars->controls_screen = 0;
vars->title_screen = 1;
vars->music = {};
vars->music.sound = vars->menu_music;
vars->music.playing = 1;
vars->music.looping = 1;
vars->music.volume = 0.3f;
vars->music.bend = 1.f;
for (i32 i = ArrayCount(vars->sfx_tracks) - 2; i >= 0; --i){
vars->sfx_tracks[i].next_free = vars->sfx_tracks + i + 1;
}
vars->sfx_free = vars->sfx_tracks;
}
persist real32 GRID_STRIDE = 94.0f;
internal Vec2
get_screen_pos(Entity *entity){
persist Vec2 top_left = {265.f, 70.f - GRID_STRIDE};
Vec2 result;
result.x = entity->show_x*GRID_STRIDE;
result.y = entity->show_y*GRID_STRIDE;
result += top_left;
return result;
}
// THING GETS BROKEN
internal void
spawn_particles_type_1(App_Vars *vars, i32 prt_index, Vec2 pos, i32 count){
Particle_Type prt_type = vars->prt_types[prt_index];
Particle *particles = vars->particles;
i32 particle_count = vars->particle_count;
if (vars->particle_max < particle_count + count){
count = vars->particle_max - particle_count;
}
for (i32 r = count; r > 0; --r){
Particle part;
part.tex_index = (pcg32_random_r(&vars->rnd) % prt_type.tex_count) + prt_type.first_tex_id;
part.pos = pos;
part.vel.x = random_real32(vars, -20.f, 20.f);
part.vel.y = random_real32(vars, -20.f, 0.f);
part.life_counter = max_i32;
part.rot = random_real32(vars, 0.f, 360.f);
part.rot_vel = random_real32(vars, -10.f, 10.f);
Assert(particle_count < vars->particle_max);
particles[particle_count++] = part;
}
vars->particle_count = particle_count;
}
// AMMO BOX GETS USED
internal void
spawn_particles_type_2(App_Vars *vars, i32 prt_index, Vec2 pos, i32 count){
Particle_Type prt_type = vars->prt_types[prt_index];
Particle *particles = vars->particles;
i32 particle_count = vars->particle_count;
if (vars->particle_max < particle_count + count){
count = vars->particle_max - particle_count;
}
for (i32 r = count; r > 0; --r){
Particle part;
part.tex_index = (pcg32_random_r(&vars->rnd) % prt_type.tex_count) + prt_type.first_tex_id;
part.pos = pos;
part.vel.x = random_real32(vars, -10.f, 10.f);
part.vel.y = random_real32(vars, 0.f, 0.f);
part.life_counter = max_i32;
part.rot = random_real32(vars, 0.f, 360.f);
part.rot_vel = random_real32(vars, -10.f, 10.f);
Assert(particle_count < vars->particle_max);
particles[particle_count++] = part;
}
vars->particle_count = particle_count;
}
// STATIC PARTICLE
internal void
spawn_particles_type_3(App_Vars *vars, i32 prt_index, Vec2 pos, real32 rot){
Particle_Type prt_type = vars->prt_types[prt_index];
Particle *particles = vars->particles;
i32 particle_count = vars->particle_count;
if (particle_count < vars->particle_max){
Particle part;
part.tex_index = (pcg32_random_r(&vars->rnd) % prt_type.tex_count) + prt_type.first_tex_id;
part.pos = pos;
part.vel = {};
part.life_counter = 5;
part.rot = rot;
part.rot_vel = 0;
particles[particle_count++] = part;
vars->particle_count = particle_count;
}
}
internal
SIG_APP_STEP(game_step){
App_Vars *vars = (App_Vars*)memory.data;
Assert(sizeof(App_Vars) < memory.size);
if (first){
font_init();
*vars = {};
vars->block = make_memory_block((u8*)memory.data + sizeof(App_Vars),
memory.size - sizeof(App_Vars));
// TODO(allen): thread the gamejolt api?
vars->gj = gj_init(80985, "031106786fa9dc6103062c42ee6f04ea");
vars->table_id = 84092;
#if 0
gj_login(vars->gj, "OVERREACT", "F3DE5E");
gj_post_score(vars->gj, vars->table_id, "ZERO", 1, "", "");
#endif
{
Temp_Memory temp = begin_temp_memory(&vars->block);
GJ_Score_Data *scores = get_mem_array(GJ_Score_Data, &vars->block, 5);
i32 score_count = gj_get_scores(vars->gj, vars->table_id, 5, scores);
for (i32 i = 0; i < score_count; ++i){
gj_free_score(scores[i]);
}
end_temp_memory(temp);
}
vars->silence_timer = 70;
load_texture_png("BRAINZ_background.png", &vars->background, &vars->block);
load_texture_png("BRAINZ_human1.png", &vars->human[0][0], &vars->block);
load_texture_png("BRAINZ_humanzombie1_1.png", &vars->human[0][1], &vars->block);
load_texture_png("BRAINZ_humanzombie1_2.png", &vars->human[0][2], &vars->block);
load_texture_png("BRAINZ_humanzombie1_3.png", &vars->human[0][3], &vars->block);
load_texture_png("BRAINZ_zombie1.png", &vars->zombie[0], &vars->block);
load_texture_png("BRAINZ_ammo.png", &vars->ammo, &vars->block);
load_texture_png("BRAINZ_brain.png", &vars->brain, &vars->block);
load_texture_png("BRAINZ_tnt.png", &vars->bomb, &vars->block);
load_texture_png("BRAINZ_wall.png", &vars->wall, &vars->block);
load_texture_png("BRAINZ_score.png", &vars->scorename, &vars->block);
load_texture_png("BRAINZ_scoreback.png", &vars->scoreback, &vars->block);
load_texture_png("BRAINZ_gameover.png", &vars->gameover, &vars->block);
load_texture_png("BRAINZ_shadow.png", &vars->shadow, &vars->block);
load_texture_png("BRAINZ_gameover_button1.png", &vars->finish_button[0], &vars->block);
load_texture_png("BRAINZ_gameover_button2.png", &vars->finish_button[1], &vars->block);
load_texture_png("BRAINZ_OVERREACT.png", &vars->overreact, &vars->block);
load_texture_png("BRAINZ_title.png", &vars->title, &vars->block);
load_texture_png("BRAINZ_title_button.png", &vars->title_button, &vars->block);
load_texture_png("BRAINZ_controls.png", &vars->controls, &vars->block);
Particle_Textures prt_textures;
load_texture_png("prts\\BrainZ_AMMO_part_1.png", &prt_textures.prt_ammo[0], &vars->block);
load_texture_png("prts\\BrainZ_AMMO_part_2.png", &prt_textures.prt_ammo[1], &vars->block);
load_texture_png("prts\\BrainZ_AMMO_part_3.png", &prt_textures.prt_ammo[2], &vars->block);
load_texture_png("prts\\BrainZ_AMMO_part_4.png", &prt_textures.prt_ammo[3], &vars->block);
load_texture_png("prts\\BrainZ_AMMO_part_5.png", &prt_textures.prt_ammo[4], &vars->block);
load_texture_png("prts\\BrainZ_BLOOD_part_1.png", &prt_textures.prt_blood[0], &vars->block);
load_texture_png("prts\\BrainZ_BLOOD_part_2.png", &prt_textures.prt_blood[1], &vars->block);
load_texture_png("prts\\BrainZ_BLOOD_part_3.png", &prt_textures.prt_blood[2], &vars->block);
load_texture_png("prts\\BrainZ_BLOOD_part_4.png", &prt_textures.prt_blood[3], &vars->block);
load_texture_png("prts\\BrainZ_BLOOD_part_5.png", &prt_textures.prt_blood[4], &vars->block);
load_texture_png("prts\\BrainZ_BLOOD_part_6.png", &prt_textures.prt_blood[5], &vars->block);
load_texture_png("prts\\BrainZ_BONE_part_1.png", &prt_textures.prt_bone[0], &vars->block);
load_texture_png("prts\\BrainZ_BONE_part_2.png", &prt_textures.prt_bone[1], &vars->block);
load_texture_png("prts\\BrainZ_BONE_part_3.png", &prt_textures.prt_bone[2], &vars->block);
load_texture_png("prts\\BrainZ_BONE_part_4.png", &prt_textures.prt_bone[3], &vars->block);
load_texture_png("prts\\BrainZ_BONE_part_5.png", &prt_textures.prt_bone[4], &vars->block);
load_texture_png("prts\\BrainZ_BONE_part_6.png", &prt_textures.prt_bone[5], &vars->block);
load_texture_png("prts\\BrainZ_BRAIN_part_1.png", &prt_textures.prt_brain[0], &vars->block);
load_texture_png("prts\\BrainZ_BRAIN_part_2.png", &prt_textures.prt_brain[1], &vars->block);
load_texture_png("prts\\BrainZ_BRAIN_part_3.png", &prt_textures.prt_brain[2], &vars->block);
load_texture_png("prts\\BrainZ_BRAIN_part_4.png", &prt_textures.prt_brain[3], &vars->block);
load_texture_png("prts\\BrainZ_HUMAN_part_1.png", &prt_textures.prt_human[0], &vars->block);
load_texture_png("prts\\BrainZ_HUMAN_part_2.png", &prt_textures.prt_human[1], &vars->block);
load_texture_png("prts\\BrainZ_HUMAN_part_3.png", &prt_textures.prt_human[2], &vars->block);
load_texture_png("prts\\BrainZ_HUMAN_part_4.png", &prt_textures.prt_human[3], &vars->block);
load_texture_png("prts\\BrainZ_MUZZLE_part_1.png", &prt_textures.prt_muzzle[0], &vars->block);
load_texture_png("prts\\BrainZ_MUZZLE_part_2.png", &prt_textures.prt_muzzle[1], &vars->block);
load_texture_png("prts\\BrainZ_TNT_part_1.png", &prt_textures.prt_tnt[0], &vars->block);
load_texture_png("prts\\BrainZ_TNT_part_2.png", &prt_textures.prt_tnt[1], &vars->block);
load_texture_png("prts\\BrainZ_TNT_part_3.png", &prt_textures.prt_tnt[2], &vars->block);
load_texture_png("prts\\BrainZ_TNT_part_4.png", &prt_textures.prt_tnt[3], &vars->block);
load_texture_png("prts\\BrainZ_TNT_part_5.png", &prt_textures.prt_tnt[4], &vars->block);
load_texture_png("prts\\BrainZ_WALL_part_1.png", &prt_textures.prt_wall[0], &vars->block);
load_texture_png("prts\\BrainZ_WALL_part_2.png", &prt_textures.prt_wall[1], &vars->block);
load_texture_png("prts\\BrainZ_WALL_part_3.png", &prt_textures.prt_wall[2], &vars->block);
load_texture_png("prts\\BrainZ_WALL_part_4.png", &prt_textures.prt_wall[3], &vars->block);
load_texture_png("prts\\BrainZ_WALL_part_5.png", &prt_textures.prt_wall[4], &vars->block);
load_texture_png("prts\\BrainZ_ZOMBIE_part_1.png", &prt_textures.prt_zombie[0], &vars->block);
load_texture_png("prts\\BrainZ_ZOMBIE_part_2.png", &prt_textures.prt_zombie[1], &vars->block);
load_texture_png("prts\\BrainZ_ZOMBIE_part_3.png", &prt_textures.prt_zombie[2], &vars->block);
load_texture_png("prts\\BrainZ_ZOMBIE_part_4.png", &prt_textures.prt_zombie[3], &vars->block);
load_texture_png("prts\\BrainZ_BULLET_part_1.png", &prt_textures.prt_bullet[0], &vars->block);
i32 prt_tex_count = 0;
i32 prt_type_count = 0;
#define COUNT_PRTS(prt_type) prt_tex_count += ArrayCount(prt_textures.prt_type); ++prt_type_count;
LIST_PRT_TYPES(COUNT_PRTS);
#undef COUNT_PRTS
vars->prt_type_count = prt_type_count;
vars->prt_textures = get_mem_array(Render_Texture, &vars->block, prt_tex_count);
vars->prt_types = get_mem_array(Particle_Type, &vars->block, prt_type_count);
prt_tex_count = 0;
i32 type_i = 0;
#define COUNT_PRTS(prt_type) vars->prt_types[type_i].first_tex_id = prt_tex_count; vars->prt_types[type_i].tex_count = ArrayCount(prt_textures.prt_type); prt_tex_count += ArrayCount(prt_textures.prt_type); ++type_i;
LIST_PRT_TYPES(COUNT_PRTS);
#undef COUNT_PRTS
Temp_Memory prt_temp = begin_temp_memory(&vars->block);
Render_Texture **prt_texture_by_type = get_mem_array(Render_Texture*, &vars->block, prt_type_count);
type_i = 0;
#define COUNT_PRTS(prt_type) prt_texture_by_type[type_i++] = prt_textures.prt_type
LIST_PRT_TYPES(COUNT_PRTS);
#undef COUNT_PRTS
i32 prt_tex_i = 0;
for (i32 i = 0; i < prt_type_count; ++i){
i32 count = vars->prt_types[i].tex_count;
for (i32 j = 0; j < count; ++j){
vars->prt_textures[prt_tex_i++] = prt_texture_by_type[i][j];
}
}
end_temp_memory(prt_temp);
type_i = 0;
#define COUNT_PRTS(t) vars->t##_index = type_i++
LIST_PRT_TYPES(COUNT_PRTS);
#undef COUNT_PRTS
//load_sound("audtest.wav", &vars->music.sound, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\AmmoFlip_1.wav", &vars->ammo_flip1, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\AmmoFlip_2.wav", &vars->ammo_flip2, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\AmmoLand.wav", &vars->ammo_land, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\Brains_1.wav", &vars->brains1, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\Brains_2.wav", &vars->brains2, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\Explosion.wav", &vars->explosion, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\GunShot.wav", &vars->gun_shot, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\PersonFlip_1.wav", &vars->person_flip1, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\PersonFlip_2.wav", &vars->person_flip2, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\PersonLand.wav", &vars->person_land, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\Reload.wav", &vars->reload, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\SoftFlip_1.wav", &vars->soft_flip1, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\SoftFlip_2.wav", &vars->soft_flip2, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\SoftLand_1.wav", &vars->soft_land1, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\SoftLand_2.wav", &vars->soft_land2, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\WallFlip_1.wav", &vars->wall_flip1, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\WallFlip_2.wav", &vars->wall_flip2, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\WallLand.wav", &vars->wall_land, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\ZombieBreak.wav", &vars->zombie_break, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\SplatDeath.wav", &vars->splat_death, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\Score_1.wav", &vars->score1, target.audio_samples_per_second, &vars->block);
load_sound("SFX\\Score_2.wav", &vars->score2, target.audio_samples_per_second, &vars->block);
load_sound("music\\Menu.wav", &vars->menu_music, target.audio_samples_per_second, &vars->block);
load_sound("music\\Gameplay_1.wav", &vars->gameplay1, target.audio_samples_per_second, &vars->block);
load_sound("music\\Gameplay_2.wav", &vars->gameplay2, target.audio_samples_per_second, &vars->block);
load_font("Dimbo Regular.ttf", &vars->font, 36, &vars->block);
load_font("Dimbo Regular.ttf", &vars->small_font, 32, &vars->block);
game_set_to_new(vars);
#if START_WITH_GAME_OVER
vars->game_over = 1;
vars->score = 1000000;
#endif
#if SKIP_TITLE_SCREEN
vars->title_screen = 0;
#endif
}
// EVERY FRAME
gj_update(vars->gj);
if (!vars->looked_up_teaser){
vars->looked_up_teaser = 1;
GJ_Score_Data score_data;
i32 score_count;
score_count = gj_get_scores(vars->gj, vars->table_id,
1, &score_data);
if (score_count == 1){
vars->teaser_score_len = score_data.score_len;
vars->teaser_name_len = score_data.name_len;
if (vars->teaser_score_len > ArrayCount(vars->teaser_score)){
vars->teaser_score_len = ArrayCount(vars->teaser_score);
}
if (vars->teaser_name_len > ArrayCount(vars->teaser_name)){
vars->teaser_name_len = ArrayCount(vars->teaser_name);
}
memcpy(vars->teaser_score, score_data.score, vars->teaser_score_len);
memcpy(vars->teaser_name, score_data.name, vars->teaser_name_len);
gj_free_score(score_data);
}
else{
persist char zero_string[] = "ZERO";
persist char nobody_string[] = "NOBODY";
vars->teaser_score_len = ArrayCount(zero_string) - 1;
vars->teaser_name_len = ArrayCount(nobody_string) - 1;
memcpy(vars->teaser_score, nobody_string, vars->teaser_score_len);
memcpy(vars->teaser_name, nobody_string, vars->teaser_name_len);
}
}
if (vars->silence_timer > 0){
--vars->silence_timer;
}
if (vars->song_done){
vars->music = {};
vars->music.sound = vars->gameplay1;
vars->music.playing = 1;
vars->music.looping = 1;
vars->music.volume = 0.03f;
vars->music.bend = 1.f;
vars->song_done = 0;
}
if (vars->title_screen > 0){
// DO NOTHING
}
else if (vars->controls_screen){
// DO NOTHING
}
else{
i32 prev_score = vars->score;
if (!vars->game_over){
// TODO(allen): allow holding but slow it down
bool32 left = input.digital.left && !vars->prev_input.digital.left;
bool32 right = input.digital.right && !vars->prev_input.digital.right;
bool32 rot_left = input.button[1] && !vars->prev_input.button[1];
bool32 rot_right = input.button[2] && !vars->prev_input.button[2];
for (i32 i = 0; i < vars->entity_count; ++i){
Entity *entity = vars->entities + i;
if (entity->active){
if (left && !right){
i32 left = entity->grid_x - 1;
if (can_move_to(vars, entity, left, entity->grid_y)){
move_entity(vars, entity, left, entity->grid_y);
}
}
else if (right && !left){
i32 right = entity->grid_x + 1;
if (can_move_to(vars, entity, right, entity->grid_y)){
move_entity(vars, entity, right, entity->grid_y);
}
}
bool32 did_rotation = 0;
if (rot_left && !rot_right){
entity->facing += 1;
entity->facing %= 4;
did_rotation = 1;
}
else if (rot_right && !rot_left){
entity->facing += 3;
entity->facing %= 4;
did_rotation = 1;
}
if (did_rotation){
entity->wobble = TAU32 / 10.f;
Sound *snds[2];
switch (entity->type){
case AMMO:
{
snds[0] = &vars->ammo_flip1;
snds[1] = &vars->ammo_flip2;
}break;
case HUMAN:
case ZOMBIE:
{
snds[0] = &vars->person_flip1;
snds[1] = &vars->person_flip2;
}break;
case BRAIN:
{
snds[0] = &vars->soft_flip1;
snds[1] = &vars->soft_flip2;
}break;
case WALL:
case BOMB:
{
snds[0] = &vars->wall_flip1;
snds[1] = &vars->wall_flip2;
}break;
}
i32 which = (pcg32_random_r(&vars->rnd) % 2);
play_sound_effect(vars, *snds[which],
1.f, random_real32(vars, BEND_DOWN, BEND_UP));
}
}
}
if (!vars->chain_reacting){
bool32 board_check = 0;
if (vars->need_to_fill_gaps){
if (vars->fill_timer > 0){
--vars->fill_timer;
}
else{
// FILL DOWN
Temp_Memory temp = begin_temp_memory(&vars->block);
Entity_Y_Data *data = get_mem_array(Entity_Y_Data, &vars->block, vars->entity_count);
for (i32 i = 0; i < vars->entity_count; ++i){
data[i].index = i;
data[i].y = vars->entities[i].grid_y;
}
quick_sort(data, 0, vars->entity_count-1);
vars->need_to_fill_gaps = 0;
for (i32 i = 0; i < vars->entity_count; ++i){
Entity *entity = vars->entities + data[i].index;
if (can_move_to(vars, entity, entity->grid_x, entity->grid_y + 1)){
move_entity(vars, entity, entity->grid_x, entity->grid_y + 1);
vars->need_to_fill_gaps = 1;
}
}
if (!vars->need_to_fill_gaps){
board_check = 1;
}
else{
vars->fill_timer = COMBO_TIME;
}
end_temp_memory(temp);
}
}
else if (!vars->need_new_block){
// FALL TICK
if (input.digital.down){
if (vars->fall_timer > 4){
vars->fall_timer = 4;
}
}
bool32 land_now = 0;
--vars->fall_timer;
for (i32 i = 0; i < vars->entity_count; ++i){
Entity *entity = vars->entities + i;
if (entity->active){
if (vars->fall_timer <= 0){
vars->fall_timer += get_level_fall_tick_time(vars, vars->level);
if (can_move_to(vars, entity, entity->grid_x, entity->grid_y + 1)){
move_entity(vars, entity, entity->grid_x, entity->grid_y + 1);
}
else{
land_now = 1;
}
}
else{
if (!can_move_to(vars, entity, entity->grid_x, entity->grid_y + 1) &&
entity->show_x == entity->grid_x && entity->show_y == entity->grid_y){
land_now = 1;
}
}
if (land_now){
Sound *snd = 0;
switch (entity->type){
case AMMO:
{
snd = &vars->ammo_land;
}break;
case HUMAN:
case ZOMBIE:
{
snd = &vars->person_land;
}break;
case BRAIN:
{
if ((pcg32_random_r(&vars->rnd) % 2) == 0){
snd = &vars->soft_land1;
}
else{
snd = &vars->soft_land2;
}
}break;
case WALL:
{
if ((pcg32_random_r(&vars->rnd) % 2) == 0){
snd = &vars->wall_land;
}
else{
snd = &vars->wall_flip1;
}
}break;
case BOMB:
{
snd = &vars->person_flip2;
}break;
}
if (snd){
play_sound_effect(vars, *snd,
1.f, random_real32(vars, BEND_DOWN, BEND_UP));
}
entity->active = 0;
entity->wobble = 0;
if (entity->grid_y == 0){
vars->game_over = 1;
}
vars->need_new_block = 1;
board_check = 1;
}
}
}
}
// SPREAD INFECTION
for (i32 i = 0; i < vars->entity_count; ++i){
Entity *entity = vars->entities + i;
if (entity->type == HUMAN){
Grid_Pos center = get_grid_pos(entity);
i32 infection_counter = 0;
for (i32 i = 0; i < ArrayCount(von_neumann); ++i){
Grid_Pos pos = center + von_neumann[i];
Entity *neighbor = get_grid_entity(vars, pos.x, pos.y);
if (neighbor){
if (neighbor->type == ZOMBIE){
++infection_counter;
}
}
}
if (infection_counter == 0){
if (entity->infection_amount > 0){
entity->infection_amount -= 1;
}
}
else{
entity->infection_amount += infection_counter;
}
if (entity->infection_amount > ZOMBIE_TURN_THRESHOLD){
entity->type = ZOMBIE;
entity->facing += 3;
entity->facing %= 4;
board_check = 1;
}
}
}
// BOARD CHECK
if (board_check){
for (i32 i = 0; i < vars->entity_count; ++i){
Entity *entity = vars->entities + i;
switch (entity->type){
case AMMO:
{
bool32 ammo_used = 0;
Grid_Pos center = get_grid_pos(entity);
for (i32 i = 0; i < ArrayCount(von_neumann); ++i){
Grid_Pos pos = center + von_neumann[i];
Entity *neighbor = get_grid_entity(vars, pos.x, pos.y);
if (neighbor){
if (neighbor->type == HUMAN){
ammo_used = 1;
neighbor->firing = 1;
}
}
}
if (ammo_used){
play_sound_effect(vars, vars->reload,
0.5f, random_real32(vars, BEND_DOWN, BEND_UP));
mark_block_destroyed(vars, entity);
}
}break;
case BRAIN:
{
bool32 brain_eaten = 0;
Grid_Pos center = get_grid_pos(entity);
for (i32 i = 0; i < ArrayCount(von_neumann); ++i){
Grid_Pos pos = center + von_neumann[i];
Entity *neighbor = get_grid_entity(vars, pos.x, pos.y);
if (neighbor){
if (neighbor->type == ZOMBIE){
brain_eaten = 1;
neighbor->firing = 1;
}
}
}
if (brain_eaten){
mark_block_destroyed(vars, entity);
}
}break;
}
}
}
}
// RANDOM BRAINS
bool32 has_zombie = 0;
for (i32 i = 0; i < vars->entity_count; ++i){
Entity *entity = vars->entities + i;
if (entity->type == ZOMBIE){
has_zombie = 1;
break;
}
}
if (has_zombie && (pcg32_random_r(&vars->rnd) % 600) == 0){
Sound *snd;
if ((pcg32_random_r(&vars->rnd) % 2) == 0){
snd = &vars->brains1;
}
else{
snd = &vars->brains2;
}
play_sound_effect(vars, *snd,
1.f, random_real32(vars, BEND_DOWN, BEND_UP));
}
// FIRE
if (vars->reaction_timer > 0){
--vars->reaction_timer;
}
else{
vars->chain_reacting = 0;
Temp_Memory gun_temp = begin_temp_memory(&vars->block);
i32 reaction_fire_count = 0;
i32 reaction_fire_max = WIDTH*HEIGHT;
Entity **reaction_fire = get_mem_array(Entity*, &vars->block, reaction_fire_max);
for (i32 i = 0; i < vars->entity_count; ++i){
Entity *entity = vars->entities + i;
Assert(!(entity->firing && entity->step_forward));
if (entity->firing){
vars->chain_reacting = 1;
vars->reaction_timer = COMBO_TIME;
entity->firing = 0;
if (entity->type == HUMAN || entity->type == AMMO){
i32 step_count = ArrayCount(up_shot);
Grid_Pos *shot_pos = right_shot;
switch (entity->facing){
case UP:
shot_pos = up_shot;
break;
case LEFT:
shot_pos = left_shot;
break;
case DOWN:
shot_pos = down_shot;
break;
}
Grid_Pos center = get_grid_pos(entity);
for (i32 i = 0; i < step_count; ++i){
Grid_Pos pos = center + shot_pos[i];
bool32 out_of_grid;
Entity *hit_entity = get_grid_entity(vars, pos.x, pos.y, &out_of_grid);
if (out_of_grid){
break;
}
if (hit_entity){
if (hit_entity->type == WALL){
break;
}
else{
switch (hit_entity->type){
case HUMAN: case ZOMBIE: case BRAIN:
{
mark_block_destroyed(vars, hit_entity);
}break;
case AMMO: case BOMB:
{
Assert(reaction_fire_count < reaction_fire_max);
reaction_fire[reaction_fire_count++] = hit_entity;
}break;
}
}
}
}
play_sound_effect(vars, vars->gun_shot,
0.5f, random_real32(vars, BEND_DOWN, BEND_UP));
if (entity->type == AMMO){
mark_block_destroyed(vars, entity);
}
if (entity->type == HUMAN){
real32 rotation = 0.f;
Vec2 pos = get_screen_pos(entity);
persist real32 MUZZLE_HALF_WIDTH = 70.f;
switch (entity->facing){
case RIGHT:
rotation = 0.f;
pos.x += 43 + MUZZLE_HALF_WIDTH;
pos.y += 36;
break;
case UP:
rotation = 270.f;
pos.x += 36;
pos.y += -43 - MUZZLE_HALF_WIDTH;
break;
case LEFT:
rotation = 180.f;
pos.x += -43 - MUZZLE_HALF_WIDTH;
pos.y += -36;
break;
case DOWN:
rotation = 90.f;
pos.x += -36;
pos.y += 43 + MUZZLE_HALF_WIDTH;
break;
}
spawn_particles_type_3(vars, vars->prt_muzzle_index, pos, rotation);
}
}
else if (entity->type == BOMB){
Grid_Pos center = get_grid_pos(entity);
for (i32 i = 0; i < ArrayCount(moore); ++i){
Grid_Pos pos = center + moore[i];
Entity *hit_entity = get_grid_entity(vars, pos.x, pos.y);
if (hit_entity){
switch (hit_entity->type){
case HUMAN: case ZOMBIE: case BRAIN: case WALL:
{
mark_block_destroyed(vars, hit_entity);
}break;
case AMMO: case BOMB:
{
Assert(reaction_fire_count < reaction_fire_max);
reaction_fire[reaction_fire_count++] = hit_entity;
}break;
}
}
play_sound_effect(vars, vars->explosion,
0.03f, random_real32(vars, BEND_DOWN, BEND_UP));
}
mark_block_destroyed(vars, entity);
}
else if (entity->type == ZOMBIE){
Grid_Pos pos = get_grid_pos(entity);
switch (entity->facing){
case RIGHT:
pos.x += 1;
break;
case UP:
pos.y -= 1;
break;
case LEFT:
pos.x -= 1;
break;
case DOWN:
pos.y += 1;
break;
}
bool32 out_of_grid;
Entity *target = get_grid_entity(vars, pos.x, pos.y, &out_of_grid);
if (!out_of_grid){
if (target){
switch (target->type){
case BOMB: case AMMO:
{
Assert(reaction_fire_count < reaction_fire_max);
reaction_fire[reaction_fire_count++] = target;
}break;
default:
{
mark_block_destroyed(vars, target);
}break;
}
}
entity->step_forward = 3;
play_sound_effect(vars, vars->zombie_break,
0.1f, random_real32(vars, BEND_DOWN, BEND_UP));
}
}
else{
Assert(1);
}
}
}
for (i32 i = 0; i < vars->entity_count; ++i){
Entity *entity = vars->entities + i;
Assert(!(entity->firing && entity->step_forward));
if (entity->step_forward){
--entity->step_forward;
vars->chain_reacting = 1;
vars->reaction_timer = COMBO_TIME;
Assert(entity->type == ZOMBIE);
Grid_Pos pos = get_grid_pos(entity);
switch (entity->facing){
case RIGHT:
pos.x += 1;
break;
case UP:
pos.y -= 1;
break;
case LEFT:
pos.x -= 1;
break;
case DOWN:
pos.y += 1;
break;
}
bool32 out_of_grid;
if (can_move_to(vars, entity, pos.x, pos.y, &out_of_grid)){
move_entity(vars, entity, pos.x, pos.y);
entity->step_forward = 0;
Sound *snd;
if ((pcg32_random_r(&vars->rnd) % 2) == 0){
snd = &vars->brains1;
}
else{
snd = &vars->brains2;
}
play_sound_effect(vars, *snd,
1.f, random_real32(vars, BEND_DOWN, BEND_UP));
}
if (out_of_grid){
entity->step_forward = 0;
}
}
}
for (i32 i = 0; i < reaction_fire_count; ++i){
reaction_fire[i]->firing = 1;
}
end_temp_memory(gun_temp);
}
// TRY TO SPAWN BLOCK
if (vars->need_new_block && !vars->need_to_fill_gaps && !vars->chain_reacting){
make_random_block(vars);
vars->need_new_block = 0;
}
// DESTROY OBJECTS
Assert(vars->free_count <= vars->entity_count);
quick_sort((void**)vars->to_free, 0, vars->free_count-1);
for (i32 i = 0; i < vars->free_count; ++i){
Entity *entity = vars->to_free[i];
if (entity->active){
vars->need_new_block = 1;
}
i32 entity_index = get_entity_index(vars, entity);
bool32 play_splat_death = 0;
switch (entity->type){
case HUMAN:
{
spawn_particles_type_1(vars, vars->prt_human_index, get_screen_pos(entity), 8);
spawn_particles_type_1(vars, vars->prt_bone_index, get_screen_pos(entity), 6);
spawn_particles_type_1(vars, vars->prt_blood_index, get_screen_pos(entity), 15);
spawn_particles_type_1(vars, vars->prt_brain_index, get_screen_pos(entity), 2);
play_splat_death = 1;
}break;
case ZOMBIE:
{
spawn_particles_type_1(vars, vars->prt_zombie_index, get_screen_pos(entity), 8);
spawn_particles_type_1(vars, vars->prt_bone_index, get_screen_pos(entity), 6);
spawn_particles_type_1(vars, vars->prt_blood_index, get_screen_pos(entity), 15);
spawn_particles_type_1(vars, vars->prt_brain_index, get_screen_pos(entity), 2);
play_splat_death = 1;
}break;
case AMMO:
{
spawn_particles_type_2(vars, vars->prt_ammo_index, get_screen_pos(entity), 12);
}break;
case BRAIN:
{
spawn_particles_type_1(vars, vars->prt_brain_index, get_screen_pos(entity), 6);
spawn_particles_type_1(vars, vars->prt_blood_index, get_screen_pos(entity), 15);
play_splat_death = 1;
}break;
case WALL:
{
spawn_particles_type_1(vars, vars->prt_wall_index, get_screen_pos(entity), 12);
}break;
case BOMB:
{
spawn_particles_type_1(vars, vars->prt_tnt_index, get_screen_pos(entity), 12);
}break;
}
if (play_splat_death){
play_sound_effect(vars, vars->splat_death,
1.f, random_real32(vars, BEND_DOWN, BEND_UP));
}
vars->grid[get_i(entity->grid_x, entity->grid_y)] = 0;
--vars->entity_count;
if (vars->entity_count > entity_index){
Entity *end_entity = vars->entities + vars->entity_count;
vars->grid[get_i(end_entity->grid_x, end_entity->grid_y)] = entity;
*entity = *end_entity;
}
}
vars->free_count = 0;
// SPAWN OBJECTS
for (i32 i = 0; i < vars->spawn_count; ++i){
Spawn_Request spawn = vars->spawns[i];
i32 x, y;
Block_Type type;
i32 facing;
if (spawn.random){
x = pcg32_random_r(&vars->rnd) % WIDTH;
y = 0;
#if TEST_ORDER == 0
//type = (Block_Type)(ZOMBIE + pcg32_random_r(&vars->rnd) % type_count);
type = block_freq_table[pcg32_random_r(&vars->rnd) % ArrayCount(block_freq_table)];
#else
Block_Type test_types[] = {
AMMO,
HUMAN
};
type = test_types[vars->test_type_i++];
vars->test_type_i = vars->test_type_i % ArrayCount(test_types);
#endif
facing = (i32)(pcg32_random_r(&vars->rnd) % 4);
}
else{
x = spawn.x;
y = spawn.y;
type = spawn.type;
facing = (i32)(pcg32_random_r(&vars->rnd) % 4);
}
if (vars->grid[get_i(x, y)] == 0){
Entity *entity = spawn_entity(vars);
entity->type = type;
entity->grid_x = x;
entity->grid_y = y;
entity->show_x = (real32)x;
entity->show_y = (real32)y;
entity->active = 1;
entity->facing = facing;
vars->grid[get_i(x, y)] = entity;
}
else{
vars->game_over = 1;
}
}
vars->spawn_count = 0;
}
// CHECK EFFECTS FROM POINTS
i32 next_level = vars->level + 1;
if (vars->score >= get_level_min_score(vars, next_level)){
vars->level = next_level;
}
i32 score_increase = vars->score - prev_score;
if (score_increase > 0){
Sound *snd;
if (score_increase >= 100){
snd = &vars->score1;
}
else{
snd = &vars->score2;
}
play_sound_effect(vars, *snd,
1.1f, random_real32(vars, BEND_DOWN, BEND_UP),
15);
}
}
// RENDERING
Vec2 screen_center;
screen_center.x = (real32)target.render.width * 0.5f;
screen_center.y = (real32)target.render.height * 0.5f;
if (vars->title_screen > 0){
Vec4 black;
black = V4(0.f, 0.f, 0.f, 1.f);
draw_clear(black);
persist i32 PHASE_0 = 120;
persist i32 PHASE_1 = 150;
persist i32 PHASE_2 = 210;
persist i32 PHASE_3 = 330;
persist i32 PHASE_4 = 400;
if (vars->title_screen < PHASE_4){
++vars->title_screen;
}
if (vars->title_screen < PHASE_0){
real32 a = (vars->title_screen / (real32)(PHASE_0));
Vec4 color = V4(1.f, 1.f, 1.f, a);
draw_texture(vars->overreact, screen_center, screen_center, 0.f, color);
}
else if (vars->title_screen < PHASE_1){
Vec4 color = V4(1.f, 1.f, 1.f, 1.f);
draw_texture(vars->overreact, screen_center, screen_center, 0.f, color);
}
else if (vars->title_screen < PHASE_2){
real32 a = ((PHASE_2 - vars->title_screen) / (real32)(PHASE_2 - PHASE_1));
Vec4 color = V4(1.f, 1.f, 1.f, a);
draw_texture(vars->overreact, screen_center, screen_center, 0.f, color);
}
else{
real32 a = 1.f;
if (vars->title_screen < PHASE_3){
a = ((vars->title_screen - PHASE_2) / (real32)(PHASE_3 - PHASE_2));
}
Vec4 color = V4(1.f, 1.f, 1.f, a);
draw_texture(vars->title, screen_center, screen_center, 0.f, color);
if (vars->title_screen >= PHASE_3){
a = 1.f;
if (vars->title_screen < PHASE_4){
a = ((vars->title_screen - PHASE_3) / (real32)(PHASE_4 - PHASE_3));
}
if (input.button[0]){
vars->title_screen = 0;
vars->controls_screen = 1;
vars->music = {};
vars->music.sound = vars->gameplay2;
vars->music.playing = 1;
vars->music.volume = 0.03f;
vars->music.bend = 1.f;
}
color = V4(1.f, 1.f, 1.f, a);
draw_texture(vars->title_button, V2(400.f, 420.f), 0.f, color);
}
}
}
else{
draw_texture(vars->background, screen_center, screen_center);
draw_texture(vars->scoreback, V2(110.f, 80.f));
draw_texture(vars->scorename, V2(110.f, 50.f));
char score_string[16];
char post_string[16];
persist char mil_string[] = "MILLION";
i32 i, j;
i = int_to_string(score_string, vars->score);
j = int_to_string(post_string, vars->nonsense_score, 3);
real32 text_x, text_y, start_x, end_x;
text_x = 20.f;
text_y = 100.f;
start_x = 20.f;
end_x = 180.f;
Vec4 white = {1.f, 1.f, 1.f, 1.f};
AllowLocal(white);
Vec4 red = {1.f, 0.f, 0.f, 1.f};
draw_text(&target.render, &vars->small_font, &text_x, &text_y, score_string + i, 16 - i, red, start_x, end_x);
if (vars->score > 0){
draw_text(&target.render, &vars->small_font, &text_x, &text_y, post_string + j, 16 - j, red, start_x, end_x);
text_x = start_x;
text_y += vars->small_font.height;
draw_text(&target.render, &vars->small_font, &text_x, &text_y, mil_string, ArrayCount(mil_string) - 1, red, start_x, end_x);
}
text_x = 20.f;
text_y = 300.f;
start_x = 20.f;
end_x = 180.f;
persist char level_label[] = "LEVEL: ";
draw_text(&target.render, &vars->small_font, &text_x, &text_y, level_label, ArrayCount(level_label) - 1, red, start_x, end_x);
char level_string[16];
i = int_to_string(level_string, vars->level + 1);
draw_text(&target.render, &vars->small_font, &text_x, &text_y, level_string + i, 16 - i, red, start_x, end_x);
text_x = 20.f;
text_y = 475.f;
start_x = 20.f;
end_x = 180.f;
persist char best_label[] = "THE MOST SCORE";
draw_text(&target.render, &vars->small_font, &text_x, &text_y, best_label, ArrayCount(best_label) - 1, red, start_x, end_x);
text_x = start_x;
text_y += vars->small_font.height;
draw_text(&target.render, &vars->small_font, &text_x, &text_y, vars->teaser_score, vars->teaser_score_len, red, start_x, end_x);
text_x = start_x;
text_y += vars->small_font.height;
draw_text(&target.render, &vars->small_font, &text_x, &text_y, vars->teaser_name, vars->teaser_name_len, red, start_x, end_x);
for (i32 i = 0; i < vars->entity_count; ++i){
Entity *entity = vars->entities + i;
persist real32 BLOCK_LERP_SPEED = 0.5f;
if (entity->show_x != entity->grid_x){
entity->show_x = lerp(entity->show_x, BLOCK_LERP_SPEED, (real32)entity->grid_x);
if (abs(entity->show_x - entity->grid_x) < 0.05){
entity->show_x = (real32)(entity->grid_x);
}
}
if (entity->show_y != entity->grid_y){
entity->show_y = lerp(entity->show_y, BLOCK_LERP_SPEED, (real32)entity->grid_y);
if (abs(entity->show_y - entity->grid_y) < 0.05){
entity->show_y = (real32)(entity->grid_y);
}
}
real32 x, y;
x = entity->show_x;
y = entity->show_y;
Render_Texture *texture = 0;
switch (entity->type){
case ZOMBIE:
texture = &vars->zombie[0];
break;
case HUMAN:
{
i32 infection_level = (entity->infection_amount * 4) / ZOMBIE_TURN_THRESHOLD;
texture = &vars->human[0][infection_level];
}break;
case BRAIN:
texture = &vars->brain;
break;
case AMMO:
texture = &vars->ammo;
break;
case BOMB:
texture = &vars->bomb;
break;
case WALL:
texture = &vars->wall;
break;
}
Vec2 center = get_screen_pos(entity);
persist real32 SCALE_DOWN = 0.3168316f;
Vec2 halfdim;
halfdim.x = texture->img_width * .5f * SCALE_DOWN;
halfdim.y = texture->img_height * .5f * SCALE_DOWN;
real32 rotation = 0.f;
if (entity->type == ZOMBIE){
switch (entity->facing){
case RIGHT:
rotation = 0.f;
break;
case UP:
rotation = 270.f;
break;
case LEFT:
rotation = 180.f;
break;
case DOWN:
rotation = 90.f;
break;
}
}
else{
switch (entity->facing){
case RIGHT:
rotation = 90.f;
break;
case UP:
rotation = 0.f;
break;
case LEFT:
rotation = 270.f;
break;
case DOWN:
rotation = 180.f;
break;
}
}
if (entity->active){
rotation += 10.f*sinf(entity->wobble);
if (entity->wobble > 0.f && entity->wobble < TAU32){
entity->wobble += TAU32 / 5.f;
}
else{
entity->wobble = 0;
}
}
if (entity->firing){
switch (entity->type){
case BOMB:
{
halfdim *= 1.1f;
rotation += 10.f*sinf(entity->wobble);
entity->wobble += TAU32 / 5.f;
draw_texture(*texture, center, halfdim, rotation);
}break;
case AMMO:
{
Vec4 color = V4(1.f, 1.f, 1.f, 1.f);
color.g = abs(sinf(entity->wobble))*0.5f + 0.5f;
color.b = color.g;
entity->wobble += TAU32 / 20.f;
draw_texture(*texture, center, halfdim, rotation, color);
}break;
default:
{
draw_texture(*texture, center, halfdim, rotation);
}break;
}
}
else if (entity->step_forward){
halfdim *= (1.f + sinf(entity->wobble)*.15f + .15f);
entity->wobble += TAU32 / 25.f;
draw_texture(*texture, center, halfdim, rotation);
}
else{
draw_texture(*texture, center, halfdim, rotation);
}
}
// PARTICLE RENDER
Particle *part = vars->particles;
Particle *end_part = vars->particles + vars->particle_count;
Render_Texture *part_texs = vars->prt_textures;
for (; part < end_part;){
if ((--part->life_counter) > 0){
part->pos += part->vel;
part->rot += part->rot_vel;
part->vel.y += 0.5f;
if (part->pos.y < 700.f){
draw_texture(part_texs[part->tex_index], part->pos, part->rot);
++part;
}
else{
--end_part;
*part = *end_part;
}
}
else{
--end_part;
*part = *end_part;
}
}
vars->particle_count = (i32)(end_part - vars->particles);
Assert(vars->particle_count >= 0);
draw_texture(vars->shadow, screen_center, screen_center, 0.f, V4(1.f, 1.f, 1.f, 0.4f));
// CONTROLS SCREEN RENDER
if (vars->controls_screen){
draw_rectangle(0.f, 0.f, screen_center.x*2, screen_center.y*2,
V4(0.f, 0.f, 0.f, 0.5f));
draw_texture(vars->controls, screen_center, screen_center);
i32 field_index = 0;
if (do_button(field_index++, vars->active_field,
input, vars->finish_button,
690.f, 500.f)){
vars->controls_screen = 0;
}
if (input.digital.up && !vars->prev_input.digital.up){
vars->active_field += (field_index - 1);
vars->active_field %= field_index;
}
if (input.digital.down && !vars->prev_input.digital.down){
vars->active_field += 1;
vars->active_field %= field_index;
}
}
// GAME OVER RENDER
else if (vars->game_over){
draw_rectangle(0.f, 0.f, screen_center.x*2, screen_center.y*2,
V4(0.f, 0.f, 0.f, 0.5f));
draw_texture(vars->gameover, screen_center, screen_center);
i32 field_index = 0;
bool32 blink_on = (vars->blink_timer < 15);
vars->blink_timer = (vars->blink_timer + 1) % 30;
bool32 move_down = 0;
if (do_text_field(field_index++, vars->active_field, blink_on,
&target.render, input,
&vars->font, 305.f, 285.f,
&vars->user_name_len, vars->user_name, 15)){
move_down = 1;
}
if (do_text_field(field_index++, vars->active_field, blink_on,
&target.render, input,
&vars->font, 305.f, 342.f,
&vars->user_token_len, vars->user_token, 15)){
move_down = 1;
}
if (do_button(field_index++, vars->active_field,
input, vars->finish_button,
690.f, 500.f)){
if (vars->user_name_len != 0){
vars->user_name[vars->user_name_len] = 0;
vars->user_token[vars->user_token_len] = 0;
char score_string[64];
i32 i = 64, j;
if (vars->score > 0){
score_string[--i] = 0;
persist char short_mil_string[] = " MIL";
for (i32 k = ArrayCount(short_mil_string) - 2; k >= 0; --k){
score_string[--i] = short_mil_string[k];
}
j = int_to_string(score_string + i - 16, vars->nonsense_score, 3);
j = (16 - j);
i -= j;
j = int_to_string(score_string + i - 16, vars->score);
j = (16 - j);
i -= j;
}
else{
persist char zero_string[] = "ZERO";
score_string[--i] = 0;
for (i32 j = ArrayCount(zero_string) - 2; j >= 0; --j){
score_string[--i] = zero_string[j];
}
}
bool32 guest_score = 0;
if (vars->user_token_len != 0){
if (gj_login(vars->gj, vars->user_name, vars->user_token)){
gj_post_score(vars->gj, vars->table_id, score_string + i, vars->score, "", "");
gj_logout(vars->gj);
}
else{
guest_score = 1;
}
}
else{
guest_score = 1;
}
if (guest_score){
gj_post_score(vars->gj, vars->table_id, score_string + i, vars->score, "", vars->user_name);
}
}
game_set_to_new(vars);
}
if (input.digital.up && !vars->prev_input.digital.up){
vars->active_field += (field_index - 1);
vars->active_field %= field_index;
}
if ((input.digital.down && !vars->prev_input.digital.down) || move_down){
vars->active_field += 1;
vars->active_field %= field_index;
}
}
}
vars->prev_input = input;
return 0;
}
// BOTTOM