2301 lines
83 KiB
C++
2301 lines
83 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
|