player into gamestate, 3d collision system, entity struct nicer

main
parent 03a0d2b5d1
commit 560e9d8db7

BIN
art/art.blend (Stored with Git LFS)

Binary file not shown.

381
main.c

@ -238,9 +238,6 @@ typedef struct Arena
size_t cur; size_t cur;
} Arena; } Arena;
EntityRef player_ref = {0};
Entity *player = 0; // up here, used in text backend callback
typedef struct AudioSample typedef struct AudioSample
{ {
float *pcm_data; // allocated by loader, must be freed float *pcm_data; // allocated by loader, must be freed
@ -625,25 +622,25 @@ Vec2 entity_aabb_size(Entity *e)
{ {
if (e->is_character) if (e->is_character)
{ {
return V2(TILE_SIZE*0.9f, TILE_SIZE*0.5f); return V2(1.0f*0.9f, 1.0f*0.5f);
} }
else if (e->is_npc) else if (e->is_npc)
{ {
if (npc_is_knight_sprite(e)) if (npc_is_knight_sprite(e))
{ {
return V2(TILE_SIZE*0.5f, TILE_SIZE*0.5f); return V2(1.0f*0.5f, 1.0f*0.5f);
} }
else if (e->npc_kind == NPC_Pile) else if (e->npc_kind == NPC_Pile)
{ {
return V2(TILE_SIZE, TILE_SIZE); return V2(1.0f, 1.0f);
} }
else if (e->npc_kind == NPC_Door) else if (e->npc_kind == NPC_Door)
{ {
return V2(TILE_SIZE*2.0f, TILE_SIZE*2.0f); return V2(1.0f*2.0f, 1.0f*2.0f);
} }
else if (e->npc_kind == NPC_Arrow) else if (e->npc_kind == NPC_Arrow)
{ {
return V2(TILE_SIZE*2.0f, TILE_SIZE*2.0f); return V2(1.0f*2.0f, 1.0f*2.0f);
} }
else else
{ {
@ -653,21 +650,21 @@ Vec2 entity_aabb_size(Entity *e)
} }
else if (e->is_prop) else if (e->is_prop)
{ {
return V2(TILE_SIZE*0.5f, TILE_SIZE*0.5f); return V2(1.0f*0.5f, 1.0f*0.5f);
} }
else if (e->is_item) else if (e->is_item)
{ {
return V2(TILE_SIZE*0.5f, TILE_SIZE*0.5f); return V2(1.0f*0.5f, 1.0f*0.5f);
} }
else if(e->is_machine) else if(e->is_machine)
{ {
if(e->machine_kind == MACH_idol_dispenser) if(e->machine_kind == MACH_idol_dispenser)
{ {
return V2(TILE_SIZE*0.5f, TILE_SIZE*0.5f); return V2(1.0f*0.5f, 1.0f*0.5f);
} }
else if(e->machine_kind == MACH_arrow_shooter) else if(e->machine_kind == MACH_arrow_shooter)
{ {
return V2(TILE_SIZE*1.0f, TILE_SIZE*1.0f); return V2(1.0f*1.0f, 1.0f*1.0f);
} }
else else
{ {
@ -844,10 +841,17 @@ Mesh load_mesh(MD_Arena *arena, MD_String8 binary_file, MD_String8 mesh_name)
return out; return out;
} }
typedef struct CollisionCube
{
struct CollisionCube *next;
AABB bounds;
} CollisionCube;
typedef struct typedef struct
{ {
Mesh *mesh_list; Mesh *mesh_list;
PlacedMesh *placed_mesh_list; PlacedMesh *placed_mesh_list;
CollisionCube *collision_list;
} ThreeDeeLevel; } ThreeDeeLevel;
ThreeDeeLevel load_level(MD_Arena *arena, MD_String8 binary_file) ThreeDeeLevel load_level(MD_Arena *arena, MD_String8 binary_file)
@ -926,6 +930,19 @@ ThreeDeeLevel load_level(MD_Arena *arena, MD_String8 binary_file)
} }
} }
MD_u64 num_collision_cubes;
ser_MD_u64(&ser, &num_collision_cubes);
for(MD_u64 i = 0; i < num_collision_cubes; i++)
{
CollisionCube *new_cube = MD_PushArray(arena, CollisionCube, 1);
Vec2 twodee_pos;
Vec2 size;
ser_Vec2(&ser, &twodee_pos);
ser_Vec2(&ser, &size);
new_cube->bounds = aabb_centered(twodee_pos, size);
MD_StackPush(out.collision_list, new_cube);
}
assert(!ser.cur_error.failed); assert(!ser.cur_error.failed);
MD_ReleaseScratch(scratch); MD_ReleaseScratch(scratch);
@ -1376,9 +1393,9 @@ void cause_action_side_effects(Entity *from, Action a)
if(a.kind == ACT_fights_player) if(a.kind == ACT_fights_player)
{ {
from->standing = STANDING_FIGHTING; from->standing = STANDING_FIGHTING;
player->talking_to = frome(from); gs.player->talking_to = frome(from);
player->state = CHARACTER_TALKING; gs.player->state = CHARACTER_TALKING;
assert(is_fighting(player)); assert(is_fighting(gs.player));
} }
if(a.kind == ACT_stops_fighting_player && from->npc_kind == NPC_Arrow) if(a.kind == ACT_stops_fighting_player && from->npc_kind == NPC_Arrow)
{ {
@ -1501,7 +1518,7 @@ bool perform_action(Entity *from, Action a)
MemoryContext context = {0}; MemoryContext context = {0};
context.author_npc_kind = from->npc_kind; context.author_npc_kind = from->npc_kind;
if(from == player && gete(from->talking_to)) if(from == gs.player && gete(from->talking_to))
{ {
context.was_talking_to_somebody = true; context.was_talking_to_somebody = true;
context.talking_to_kind = gete(from->talking_to)->npc_kind; context.talking_to_kind = gete(from->talking_to)->npc_kind;
@ -1571,13 +1588,13 @@ bool eq(EntityRef ref1, EntityRef ref2)
return ref1.index == ref2.index && ref1.generation == ref2.generation; return ref1.index == ref2.index && ref1.generation == ref2.generation;
} }
Entity *new_entity() Entity *new_entity(GameState *gs)
{ {
for (int i = 0; i < ARRLEN(gs.entities); i++) for (int i = 0; i < ARRLEN(gs->entities); i++)
{ {
if (!gs.entities[i].exists) if (!gs->entities[i].exists)
{ {
Entity *to_return = &gs.entities[i]; Entity *to_return = &gs->entities[i];
int gen = to_return->generation; int gen = to_return->generation;
*to_return = (Entity) { 0 }; *to_return = (Entity) { 0 };
to_return->exists = true; to_return->exists = true;
@ -1589,22 +1606,6 @@ Entity *new_entity()
return NULL; return NULL;
} }
void update_player_from_entities()
{
player_ref = (EntityRef){0};
ENTITIES_ITER(gs.entities)
{
if (it->is_character)
{
assert(gete(player_ref) == 0);
player_ref = frome(it);
}
}
player = gete(player_ref);
assert(player != 0);
player->npc_kind = NPC_Player; // bad, but needed because not set by loading from level I think? investigate this.
}
typedef struct ToVisit { typedef struct ToVisit {
struct ToVisit *next; struct ToVisit *next;
struct ToVisit *prev; struct ToVisit *prev;
@ -1727,26 +1728,23 @@ int parse_enumstr_impl(MD_Arena *arena, MD_String8 enum_str, char **enumstr_arra
#define parse_enumstr(arena, enum_str, errors, string_array, enum_kind_name, prefix) parse_enumstr_impl(arena, enum_str, string_array, ARRLEN(string_array), errors, enum_kind_name, prefix) #define parse_enumstr(arena, enum_str, errors, string_array, enum_kind_name, prefix) parse_enumstr_impl(arena, enum_str, string_array, ARRLEN(string_array), errors, enum_kind_name, prefix)
void reset_level() void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *level)
{ {
// load level memset(gs, 0, sizeof(GameState));
gs.won = false; gs->player = new_entity(gs);
if(!player) gs->player->is_character = true;
{ gs->player->npc_kind = NPC_Player;
Entity *pl = new_entity(); gs->world_entity = new_entity(gs);
pl->is_character = true; gs->world_entity->is_world = true;
}
update_player_from_entities();
player->pos = V2(0,0);
#ifdef DEVTOOLS #ifdef DEVTOOLS
if(false) if(false)
{ {
for (int i = 0; i < 20; i++) for (int i = 0; i < 20; i++)
BUFF_APPEND(&player->held_items, ITEM_GoldCoin); BUFF_APPEND(&gs->player->held_items, ITEM_GoldCoin);
} }
ENTITIES_ITER(gs.entities) ENTITIES_ITER(gs->entities)
{ {
if(false) if(false)
if (it->npc_kind == NPC_TheBlacksmith) if (it->npc_kind == NPC_TheBlacksmith)
@ -1854,7 +1852,7 @@ void reset_level()
if(drama_errors.node_count == 0) if(drama_errors.node_count == 0)
{ {
bool found = false; bool found = false;
ENTITIES_ITER(gs.entities) ENTITIES_ITER(gs->entities)
{ {
if(it->is_npc && it->npc_kind == want) if(it->is_npc && it->npc_kind == want)
{ {
@ -1890,13 +1888,20 @@ void reset_level()
} }
ENTITIES_ITER(gs.entities) ENTITIES_ITER(gs->entities)
{ {
it->perceptions_dirty = false; // nobody should say anything about jester memories it->perceptions_dirty = false; // nobody should say anything about jester memories
} }
} }
} }
ThreeDeeLevel level_threedee = {0};
void reset_level()
{
initialize_gamestate_from_threedee_level(&gs, &level_threedee);
}
enum enum
{ {
V0, V0,
@ -1938,6 +1943,8 @@ void ser_entity(SerState *ser, Entity *e)
SER_BUFF(ser, ItemKind, &e->held_items); SER_BUFF(ser, ItemKind, &e->held_items);
ser_bool(ser, &e->is_world);
ser_bool(ser, &e->is_prop); ser_bool(ser, &e->is_prop);
ser_PropKind(ser, &e->prop_kind); ser_PropKind(ser, &e->prop_kind);
@ -2064,6 +2071,34 @@ void ser_GameState(SerState *ser, GameState *gs)
ser_entity(ser, &(gs->entities[i])); ser_entity(ser, &(gs->entities[i]));
} }
} }
gs->player = 0;
gs->world_entity = 0;
if(!ser->cur_error.failed)
{
ARR_ITER(Entity, gs->entities)
{
if(it->is_character)
{
gs->player = it;
}
if(it->is_world)
{
gs->world_entity = it;
}
}
if(gs->player == 0)
{
ser->cur_error = (SerError){.failed = true, .why = MD_S8Lit("No player entity found in deserialized entities")};
}
if(gs->world_entity == 0)
{
ser->cur_error = (SerError){.failed = true, .why = MD_S8Lit("No world entity found in deserialized entities")};
}
}
} }
// error_out is allocated onto arena if it fails // error_out is allocated onto arena if it fails
@ -2174,7 +2209,6 @@ void read_from_save_data(char *data, size_t length)
else else
{ {
gs = new_gs; gs = new_gs;
update_player_from_entities();
} }
MD_ReleaseScratch(scratch); MD_ReleaseScratch(scratch);
@ -2210,7 +2244,7 @@ void end_text_input(char *what_player_said_cstr)
memcpy(to_perform.speech, what_player_said.str, what_player_said.size); memcpy(to_perform.speech, what_player_said.str, what_player_said.size);
to_perform.speech_length = (int)what_player_said.size; to_perform.speech_length = (int)what_player_said.size;
perform_action(player, to_perform); perform_action(gs.player, to_perform);
} }
MD_ReleaseScratch(scratch); MD_ReleaseScratch(scratch);
} }
@ -2436,9 +2470,12 @@ void do_serialization_tests()
Log("Testing serialization...\n"); Log("Testing serialization...\n");
MD_ArenaTemp scratch = MD_GetScratch(0, 0); MD_ArenaTemp scratch = MD_GetScratch(0, 0);
ThreeDeeLevel level = {0};
GameState gs = {0};
initialize_gamestate_from_threedee_level(&gs, &level);
reset_level(); gs.player->pos = V2(50.0f, 0.0);
player->pos = V2(50.0f, 0.0);
MD_String8 error = {0}; MD_String8 error = {0};
MD_String8 saved = save_to_string(scratch.arena, scratch.arena, &error, &gs); MD_String8 saved = save_to_string(scratch.arena, scratch.arena, &error, &gs);
@ -2447,10 +2484,9 @@ void do_serialization_tests()
assert(saved.size > 0); assert(saved.size > 0);
assert(saved.str != 0); assert(saved.str != 0);
reset_level(); initialize_gamestate_from_threedee_level(&gs, &level);
gs = load_from_string(persistent_arena, scratch.arena, saved, &error); gs = load_from_string(persistent_arena, scratch.arena, saved, &error);
update_player_from_entities(); assert(gs.player->pos.x == 50.0f);
assert(player->pos.x == 50.0f);
assert(error.size == 0); assert(error.size == 0);
Log("Default save data size is %lld bytes\n", saved.size); Log("Default save data size is %lld bytes\n", saved.size);
@ -2460,7 +2496,6 @@ void do_serialization_tests()
#endif #endif
Mesh mesh_player = {0}; Mesh mesh_player = {0};
ThreeDeeLevel level_threedee = {0};
void stbi_flip_into_correct_direction(bool do_it) void stbi_flip_into_correct_direction(bool do_it)
{ {
@ -3142,6 +3177,7 @@ void colorquad(Quad q, Color col)
draw_quad((DrawParams) { q, image_white_square, full_region(image_white_square), col, .layer = LAYER_UI }); draw_quad((DrawParams) { q, image_white_square, full_region(image_white_square), col, .layer = LAYER_UI });
} }
Vec2 NormV2_or_zero(Vec2 v) Vec2 NormV2_or_zero(Vec2 v)
{ {
if(v.x == 0.0f && v.y == 0.0f) if(v.x == 0.0f && v.y == 0.0f)
@ -3300,6 +3336,50 @@ void dbg3dline(Vec3 from, Vec3 to)
dbgline(from_screenspace, to_screenspace); dbgline(from_screenspace, to_screenspace);
} }
Vec3 plane_point(Vec2 p)
{
return V3(p.x, 0.0, p.y);
}
void colorquadplane(Quad q, Color col)
{
Quad warped = {0};
for(int i = 0; i < 4; i++)
{
q.points[i] = threedee_to_screenspace(plane_point(q.points[i]));
}
colorquad(warped, col);
}
void dbgplanesquare(Vec2 at)
{
if(!show_devtools) return;
colorquadplane(quad_centered(at, V2(3.0,3.0)), debug_color);
}
void dbgplaneline(Vec2 from, Vec2 to)
{
if(!show_devtools) return;
dbg3dline(plane_point(from), plane_point(to));
}
void dbgplanevec(Vec2 from, Vec2 vec)
{
if(!show_devtools) return;
Vec2 to = AddV2(from, vec);
dbgplaneline(from, to);
}
void dbgplanerect(AABB aabb)
{
if(!show_devtools) return;
Quad q = quad_aabb(aabb);
dbgplaneline(q.ul, q.ur);
dbgplaneline(q.ur, q.lr);
dbgplaneline(q.lr, q.ll);
dbgplaneline(q.ll, q.ul);
}
typedef struct TextParams typedef struct TextParams
{ {
bool dry_run; bool dry_run;
@ -3495,7 +3575,7 @@ Overlapping get_overlapping(AABB aabb)
PROFILE_SCOPE("checking the entities") PROFILE_SCOPE("checking the entities")
ENTITIES_ITER(gs.entities) ENTITIES_ITER(gs.entities)
{ {
if (!(it->is_character && it->is_rolling) && overlapping(aabb, entity_aabb(it))) if (!(it->is_character && it->is_rolling) && !it->is_world && overlapping(aabb, entity_aabb(it)))
{ {
BUFF_APPEND(&to_return, it); BUFF_APPEND(&to_return, it);
} }
@ -3539,12 +3619,18 @@ Vec2 move_and_slide(MoveSlideParams p)
} CollisionObj; } CollisionObj;
BUFF(CollisionObj, 256) to_check = { 0 }; BUFF(CollisionObj, 256) to_check = { 0 };
// add world boxes
for(CollisionCube *cur = level_threedee.collision_list; cur; cur = cur->next)
{
BUFF_APPEND(&to_check, ((CollisionObj){cur->bounds, gs.world_entity}));
}
// add entity boxes // add entity boxes
if (!p.dont_collide_with_entities && !(p.from->is_character && p.from->is_rolling)) if (!p.dont_collide_with_entities && !(p.from->is_character && p.from->is_rolling))
{ {
ENTITIES_ITER(gs.entities) ENTITIES_ITER(gs.entities)
{ {
if (!(it->is_character && it->is_rolling) && it != p.from && !(it->is_npc && it->dead) && !it->is_item && !(it->is_npc && it->npc_kind == NPC_Door && it->opened)) if (!(it->is_character && it->is_rolling) && it != p.from && !(it->is_npc && it->dead) && !it->is_world && !it->is_item && !(it->is_npc && it->npc_kind == NPC_Door && it->opened))
{ {
BUFF_APPEND(&to_check, ((CollisionObj){aabb_centered(it->pos, entity_aabb_size(it)), it})); BUFF_APPEND(&to_check, ((CollisionObj){aabb_centered(it->pos, entity_aabb_size(it)), it}));
} }
@ -3602,7 +3688,7 @@ Vec2 move_and_slide(MoveSlideParams p)
{ {
dbgcol(GREEN) dbgcol(GREEN)
{ {
dbgrect(it->aabb); dbgplanerect(it->aabb);
} }
} }
@ -3611,11 +3697,11 @@ Vec2 move_and_slide(MoveSlideParams p)
BUFF_ITER(CollisionObj, &actually_overlapping) BUFF_ITER(CollisionObj, &actually_overlapping)
dbgcol(WHITE) dbgcol(WHITE)
dbgrect(it->aabb); dbgplanerect(it->aabb);
BUFF_ITER(CollisionObj, &overlapping_smallest_first) BUFF_ITER(CollisionObj, &overlapping_smallest_first)
dbgcol(WHITE) dbgcol(WHITE)
dbgsquare(aabb_center(it->aabb)); dbgplanesquare(aabb_center(it->aabb));
CollisionInfo info = { 0 }; CollisionInfo info = { 0 };
for (int col_iter_i = 0; col_iter_i < 1; col_iter_i++) for (int col_iter_i = 0; col_iter_i < 1; col_iter_i++)
@ -3652,7 +3738,7 @@ Vec2 move_and_slide(MoveSlideParams p)
assert(closest_index != -1); assert(closest_index != -1);
Vec2 move_dir = compass_dirs[closest_index]; Vec2 move_dir = compass_dirs[closest_index];
info.normal = move_dir; info.normal = move_dir;
dbgvec(from_point, MulV2F(move_dir, 30.0f)); dbgplanevec(from_point, MulV2F(move_dir, 30.0f));
Vec2 move = MulV2F(move_dir, move_dist); Vec2 move = MulV2F(move_dir, move_dist);
at_new.upper_left = AddV2(at_new.upper_left, move); at_new.upper_left = AddV2(at_new.upper_left, move);
at_new.lower_right = AddV2(at_new.lower_right, move); at_new.lower_right = AddV2(at_new.lower_right, move);
@ -3689,7 +3775,7 @@ Vec2 move_and_slide(MoveSlideParams p)
if (p.col_info_out) *p.col_info_out = info; if (p.col_info_out) *p.col_info_out = info;
Vec2 result_pos = aabb_center(at_new); Vec2 result_pos = aabb_center(at_new);
dbgrect(aabb_centered(result_pos, collision_aabb_size)); dbgplanerect(aabb_centered(result_pos, collision_aabb_size));
return result_pos; return result_pos;
} }
@ -4137,12 +4223,6 @@ void draw_item(ItemKind kind, AABB in_aabb, float alpha)
void frame(void) void frame(void)
{ {
if(gete(player_ref) == 0 || gete(player_ref)->npc_kind != NPC_Player)
{
update_player_from_entities();
player = gete(player_ref);
assert(player);
}
static float speed_factor = 1.0f; static float speed_factor = 1.0f;
// elapsed_time // elapsed_time
double unwarped_dt_double = 0.0; double unwarped_dt_double = 0.0;
@ -4193,7 +4273,7 @@ void frame(void)
uint64_t time_start_frame = stm_now(); uint64_t time_start_frame = stm_now();
Vec3 player_pos = V3(player->pos.x, 0.0, player->pos.y); Vec3 player_pos = V3(gs.player->pos.x, 0.0, gs.player->pos.y);
dbg3dline(player_pos, V3(0,0,0)); dbg3dline(player_pos, V3(0,0,0));
//dbgline(V2(0,0), V2(500, 500)); //dbgline(V2(0,0), V2(500, 500));
const float vertical_to_horizontal_ratio = 0.8f; const float vertical_to_horizontal_ratio = 0.8f;
@ -4263,7 +4343,7 @@ void frame(void)
draw_placed(view, projection, cur); draw_placed(view, projection, cur);
} }
draw_placed(view, projection, &(PlacedMesh){.draw_with = &mesh_player, .offset = V3(player->pos.x, 0.0, player->pos.y), .rotation = Make_Q(0, 0, 0, 1), .scale = V3(1, 1, 1)}); draw_placed(view, projection, &(PlacedMesh){.draw_with = &mesh_player, .offset = V3(gs.player->pos.x, 0.0, gs.player->pos.y), .rotation = Make_Q(0, 0, 0, 1), .scale = V3(1, 1, 1)});
sg_end_pass(); sg_end_pass();
@ -4346,7 +4426,7 @@ void frame(void)
} }
#endif #endif
assert(player != NULL); assert(gs.player != NULL);
// gameplay processing loop, do multiple if lagging // gameplay processing loop, do multiple if lagging
// these are static so that, on frames where no gameplay processing is necessary and just rendering, the rendering uses values from last frame // these are static so that, on frames where no gameplay processing is necessary and just rendering, the rendering uses values from last frame
@ -4356,7 +4436,7 @@ void frame(void)
float speed_target; float speed_target;
// pausing the game // pausing the game
if (player->in_conversation_mode || gs.won) if (gs.player->in_conversation_mode || gs.won)
{ {
speed_target = 0.0f; speed_target = 0.0f;
} }
@ -4440,7 +4520,7 @@ void frame(void)
player_in_combat = false; // in combat set by various enemies when they fight the player player_in_combat = false; // in combat set by various enemies when they fight the player
PROFILE_SCOPE("entity processing") PROFILE_SCOPE("entity processing")
{ {
if(player->knighted) if(gs.player->knighted)
{ {
gs.won = true; gs.won = true;
} }
@ -4550,15 +4630,15 @@ void frame(void)
} }
it->being_hovered = false; it->being_hovered = false;
if (player->in_conversation_mode) if (gs.player->in_conversation_mode)
{ {
if (has_point(entity_aabb(it), screen_to_world(mouse_pos))) if (has_point(entity_aabb(it), screen_to_world(mouse_pos)))
{ {
it->being_hovered = true; it->being_hovered = true;
if (pressed.mouse_down) if (pressed.mouse_down)
{ {
player->talking_to = frome(it); gs.player->talking_to = frome(it);
player->state = CHARACTER_TALKING; gs.player->state = CHARACTER_TALKING;
} }
} }
} }
@ -4595,7 +4675,7 @@ void frame(void)
{ {
it->word_anim_in = 0; it->word_anim_in = 0;
} }
float dist = LenV2(SubV2(it->pos, player->pos)); float dist = LenV2(SubV2(it->pos, gs.player->pos));
float volume = Lerp(-0.6f, clamp01(dist / 70.0f), -1.0f); float volume = Lerp(-0.6f, clamp01(dist / 70.0f), -1.0f);
AudioSample * possible_grunts[] = { AudioSample * possible_grunts[] = {
&sound_grunt_0, &sound_grunt_0,
@ -4624,7 +4704,7 @@ void frame(void)
} }
} }
Vec2 target = get_point_along_trail(BUFF_MAKEREF(&player->position_history), (float)place_in_line * TILE_SIZE); Vec2 target = get_point_along_trail(BUFF_MAKEREF(&gs.player->position_history), (float)place_in_line * TILE_SIZE);
it->pos = LerpV2(it->pos, dt*5.0f, target); it->pos = LerpV2(it->pos, dt*5.0f, target);
} }
@ -4634,7 +4714,7 @@ void frame(void)
if(false) if(false)
if (it->standing == STANDING_FIGHTING || it->standing == STANDING_JOINED) if (it->standing == STANDING_FIGHTING || it->standing == STANDING_JOINED)
{ {
Entity *targeting = player; Entity *targeting = gs.player;
/* /*
G cost: distance from the current node to the start node G cost: distance from the current node to the start node
@ -4673,7 +4753,7 @@ void frame(void)
const float jump_size = TILE_SIZE / 2.0f; const float jump_size = TILE_SIZE / 2.0f;
BUFF_APPEND(&nodes, ((AStarNode) { .in_open_set = true, .pos = from })); BUFF_APPEND(&nodes, ((AStarNode) { .in_open_set = true, .pos = from }));
Vec2 from_hash = V2_HASH(from); Vec2 from_hash = V2_HASH(from);
float got_there_tolerance = max_coord(entity_aabb_size(player))*1.5f; float got_there_tolerance = max_coord(entity_aabb_size(gs.player))*1.5f;
hmput(node_cache, from_hash, &nodes.data[0]); hmput(node_cache, from_hash, &nodes.data[0]);
bool should_quit = false; bool should_quit = false;
@ -4965,8 +5045,8 @@ void frame(void)
{ {
if (it->held_by_player) if (it->held_by_player)
{ {
Vec2 held_spot = V2(15.0f * (player->facing_left ? -1.0f : 1.0f), 7.0f); Vec2 held_spot = V2(15.0f * (gs.player->facing_left ? -1.0f : 1.0f), 7.0f);
it->pos = AddV2(player->pos, held_spot); it->pos = AddV2(gs.player->pos, held_spot);
} }
else else
{ {
@ -4992,13 +5072,16 @@ void frame(void)
if(it->arrow_timer >= SECONDS_PER_ARROW) if(it->arrow_timer >= SECONDS_PER_ARROW)
{ {
it->arrow_timer = 0.0; it->arrow_timer = 0.0;
Entity *new_arrow = new_entity(); Entity *new_arrow = new_entity(&gs);
new_arrow->is_npc = true; new_arrow->is_npc = true;
new_arrow->npc_kind = NPC_Arrow; new_arrow->npc_kind = NPC_Arrow;
new_arrow->pos = AddV2(it->pos, V2(entity_aabb_size(new_arrow).x + 0.01f, 0.0)); new_arrow->pos = AddV2(it->pos, V2(entity_aabb_size(new_arrow).x + 0.01f, 0.0));
} }
} }
} }
else if (it->is_world)
{
}
else else
{ {
assert(false); assert(false);
@ -5161,7 +5244,7 @@ void frame(void)
{ {
// find closest to talk to // find closest to talk to
{ {
AABB dialog_rect = aabb_centered(player->pos, V2(dialog_interact_size , dialog_interact_size)); AABB dialog_rect = aabb_centered(gs.player->pos, V2(dialog_interact_size , dialog_interact_size));
dbgrect(dialog_rect); dbgrect(dialog_rect);
Overlapping possible_dialogs = get_overlapping(dialog_rect); Overlapping possible_dialogs = get_overlapping(dialog_rect);
float closest_interact_with_dist = INFINITY; float closest_interact_with_dist = INFINITY;
@ -5181,7 +5264,7 @@ void frame(void)
if (entity_interactible) if (entity_interactible)
{ {
float dist = LenV2(SubV2((*it)->pos, player->pos)); float dist = LenV2(SubV2((*it)->pos, gs.player->pos));
if (dist < closest_interact_with_dist) if (dist < closest_interact_with_dist)
{ {
closest_interact_with_dist = dist; closest_interact_with_dist = dist;
@ -5192,35 +5275,35 @@ void frame(void)
} }
// maybe get rid of talking to // maybe get rid of talking to
if (player->state == CHARACTER_TALKING) if (gs.player->state == CHARACTER_TALKING)
{ {
if (gete(player->talking_to) == 0) if (gete(gs.player->talking_to) == 0)
{ {
player->state = CHARACTER_IDLE; gs.player->state = CHARACTER_IDLE;
} }
} }
else else
{ {
player->talking_to = (EntityRef) { 0 }; gs.player->talking_to = (EntityRef) { 0 };
} }
interacting_with = closest_interact_with; interacting_with = closest_interact_with;
if (player->state == CHARACTER_TALKING) if (gs.player->state == CHARACTER_TALKING)
{ {
interacting_with = gete(player->talking_to); interacting_with = gete(gs.player->talking_to);
assert(interacting_with); assert(interacting_with);
} }
} }
if (interact) if (interact)
{ {
if (player->state == CHARACTER_TALKING) if (gs.player->state == CHARACTER_TALKING)
{ {
// don't add extra stuff to be done when changing state because in several // don't add extra stuff to be done when changing state because in several
// places it's assumed to end dialog I can just do player->state = CHARACTER_IDLE // places it's assumed to end dialog I can just do player->state = CHARACTER_IDLE
if(!is_fighting(player)) if(!is_fighting(gs.player))
{ {
player->state = CHARACTER_IDLE; gs.player->state = CHARACTER_IDLE;
} }
} }
else if (closest_interact_with) else if (closest_interact_with)
@ -5241,7 +5324,7 @@ void frame(void)
} }
if(members_in_party >= 3) if(members_in_party >= 3)
{ {
BUFF_APPEND(&player->held_items, ITEM_Idol); BUFF_APPEND(&gs.player->held_items, ITEM_Idol);
closest_interact_with->has_given_idol = true; closest_interact_with->has_given_idol = true;
} }
else else
@ -5254,8 +5337,8 @@ void frame(void)
else if (closest_interact_with->is_npc) else if (closest_interact_with->is_npc)
{ {
// begin dialog with closest npc // begin dialog with closest npc
player->state = CHARACTER_TALKING; gs.player->state = CHARACTER_TALKING;
player->talking_to = frome(closest_interact_with); gs.player->talking_to = frome(closest_interact_with);
} }
else else
{ {
@ -5264,33 +5347,33 @@ void frame(void)
} }
} }
if(is_fighting(player)) if(is_fighting(gs.player))
{ {
player->state = CHARACTER_TALKING; gs.player->state = CHARACTER_TALKING;
} }
float speed = 0.0f; float speed = 0.0f;
{ {
Vec2 target_vel = { 0 }; Vec2 target_vel = { 0 };
if (player->state == CHARACTER_WALKING) if (gs.player->state == CHARACTER_WALKING)
{ {
speed = PLAYER_SPEED; speed = PLAYER_SPEED;
if (player->is_rolling) speed = PLAYER_ROLL_SPEED; if (gs.player->is_rolling) speed = PLAYER_ROLL_SPEED;
if (LenV2(movement) == 0.0) if (LenV2(movement) == 0.0)
{ {
player->state = CHARACTER_IDLE; gs.player->state = CHARACTER_IDLE;
} }
else else
{ {
} }
} }
else if (player->state == CHARACTER_IDLE) else if (gs.player->state == CHARACTER_IDLE)
{ {
if (LenV2(movement) > 0.01) player->state = CHARACTER_WALKING; if (LenV2(movement) > 0.01) gs.player->state = CHARACTER_WALKING;
} }
else if (player->state == CHARACTER_TALKING) else if (gs.player->state == CHARACTER_TALKING)
{ {
} }
else else
@ -5302,30 +5385,30 @@ void frame(void)
// velocity processing // velocity processing
{ {
Vec2 target_vel = MulV2F(movement, pixels_per_meter * speed); Vec2 target_vel = MulV2F(movement, pixels_per_meter * speed);
player->vel = LerpV2(player->vel, dt * 15.0f, target_vel); gs.player->vel = LerpV2(gs.player->vel, dt * 15.0f, target_vel);
player->pos = move_and_slide((MoveSlideParams) { player, player->pos, MulV2F(player->vel, dt) }); gs.player->pos = move_and_slide((MoveSlideParams) { gs.player, gs.player->pos, MulV2F(gs.player->vel, dt) });
bool should_append = false; bool should_append = false;
// make it so no snap when new points added // make it so no snap when new points added
if(player->position_history.cur_index > 0) if(gs.player->position_history.cur_index > 0)
{ {
player->position_history.data[player->position_history.cur_index - 1] = player->pos; gs.player->position_history.data[gs.player->position_history.cur_index - 1] = gs.player->pos;
} }
if(player->position_history.cur_index > 2) if(gs.player->position_history.cur_index > 2)
{ {
should_append = LenV2(SubV2(player->position_history.data[player->position_history.cur_index - 2], player->pos)) > TILE_SIZE; should_append = LenV2(SubV2(gs.player->position_history.data[gs.player->position_history.cur_index - 2], gs.player->pos)) > TILE_SIZE;
} }
else else
{ {
should_append = true; should_append = true;
} }
if(should_append) BUFF_QUEUE_APPEND(&player->position_history, player->pos); if(should_append) BUFF_QUEUE_APPEND(&gs.player->position_history, gs.player->pos);
} }
// health // health
if (player->damage >= 1.0) if (gs.player->damage >= 1.0)
{ {
reset_level(); reset_level();
} }
@ -5357,24 +5440,24 @@ void frame(void)
{ {
DrawnAnimatedSprite to_draw = { 0 }; DrawnAnimatedSprite to_draw = { 0 };
if(player->position_history.cur_index > 0) if(gs.player->position_history.cur_index > 0)
{ {
float trail_len = get_total_trail_len(BUFF_MAKEREF(&player->position_history)); float trail_len = get_total_trail_len(BUFF_MAKEREF(&gs.player->position_history));
if(trail_len > 0.0f) // fmodf returns nan if(trail_len > 0.0f) // fmodf returns nan
{ {
float along = fmodf((float)elapsed_time*100.0f, 200.0f); float along = fmodf((float)elapsed_time*100.0f, 200.0f);
Vec2 at = get_point_along_trail(BUFF_MAKEREF(&player->position_history), along); Vec2 at = get_point_along_trail(BUFF_MAKEREF(&gs.player->position_history), along);
dbgbigsquare(at); dbgbigsquare(at);
dbgbigsquare(get_point_along_trail(BUFF_MAKEREF(&player->position_history), 50.0f)); dbgbigsquare(get_point_along_trail(BUFF_MAKEREF(&gs.player->position_history), 50.0f));
} }
BUFF_ITER_I(Vec2, &player->position_history, i) BUFF_ITER_I(Vec2, &gs.player->position_history, i)
{ {
if(i == player->position_history.cur_index - 1) if(i == gs.player->position_history.cur_index - 1)
{ {
} }
else else
{ {
dbgline(*it, player->position_history.data[i + 1]); dbgline(*it, gs.player->position_history.data[i + 1]);
} }
} }
} }
@ -5386,7 +5469,7 @@ void frame(void)
if (!mobile_controls) if (!mobile_controls)
{ {
float size = 100.0f; float size = 100.0f;
Vec2 midpoint = MulV2F(AddV2(interacting_with->pos, player->pos), 0.5f); Vec2 midpoint = MulV2F(AddV2(interacting_with->pos, gs.player->pos), 0.5f);
draw_quad((DrawParams) { quad_centered(AddV2(midpoint, V2(0.0, 5.0f + sinf((float)elapsed_time*3.0f)*5.0f)), V2(size, size)), IMG(image_e_icon), blendalpha(WHITE, clamp01(1.0f - learned_e)), .layer = LAYER_UI_FG }); draw_quad((DrawParams) { quad_centered(AddV2(midpoint, V2(0.0, 5.0f + sinf((float)elapsed_time*3.0f)*5.0f)), V2(size, size)), IMG(image_e_icon), blendalpha(WHITE, clamp01(1.0f - learned_e)), .layer = LAYER_UI_FG });
} }
@ -5394,13 +5477,13 @@ void frame(void)
draw_quad((DrawParams) { quad_centered(interacting_with->pos, V2(TILE_SIZE, TILE_SIZE)), image_hovering_circle, full_region(image_hovering_circle), WHITE, .layer = LAYER_UI }); draw_quad((DrawParams) { quad_centered(interacting_with->pos, V2(TILE_SIZE, TILE_SIZE)), image_hovering_circle, full_region(image_hovering_circle), WHITE, .layer = LAYER_UI });
} }
if (player->state == CHARACTER_WALKING) if (gs.player->state == CHARACTER_WALKING)
{ {
} }
else if (player->state == CHARACTER_IDLE) else if (gs.player->state == CHARACTER_IDLE)
{ {
} }
else if (player->state == CHARACTER_TALKING) else if (gs.player->state == CHARACTER_TALKING)
{ {
} }
else else
@ -5409,18 +5492,18 @@ void frame(void)
} }
// hurt vignette // hurt vignette
if (player->damage > 0.0) if (gs.player->damage > 0.0)
{ {
draw_quad((DrawParams) { (Quad) { .ul = V2(0.0f, screen_size().Y), .ur = screen_size(), .lr = V2(screen_size().X, 0.0f) }, image_hurt_vignette, full_region(image_hurt_vignette), (Color) { 1.0f, 1.0f, 1.0f, player->damage }, .layer = LAYER_SCREENSPACE_EFFECTS, }); draw_quad((DrawParams) { (Quad) { .ul = V2(0.0f, screen_size().Y), .ur = screen_size(), .lr = V2(screen_size().X, 0.0f) }, image_hurt_vignette, full_region(image_hurt_vignette), (Color) { 1.0f, 1.0f, 1.0f, gs.player->damage }, .layer = LAYER_SCREENSPACE_EFFECTS, });
} }
player->anim_change_timer += dt; gs.player->anim_change_timer += dt;
if (player->anim_change_timer >= 0.05f) if (gs.player->anim_change_timer >= 0.05f)
{ {
player->anim_change_timer = 0.0f; gs.player->anim_change_timer = 0.0f;
player->cur_animation = to_draw.anim; gs.player->cur_animation = to_draw.anim;
} }
to_draw.anim = player->cur_animation; to_draw.anim = gs.player->cur_animation;
if (to_draw.anim) if (to_draw.anim)
{ {
@ -5441,11 +5524,11 @@ void frame(void)
if (it->is_npc) if (it->is_npc)
{ {
float dist = LenV2(SubV2(it->pos, player->pos)); float dist = LenV2(SubV2(it->pos, gs.player->pos));
dist -= 10.0f; // radius around point where dialog is completely opaque dist -= 10.0f; // radius around point where dialog is completely opaque
float max_dist = dialog_interact_size / 2.0f; float max_dist = dialog_interact_size / 2.0f;
float alpha = 1.0f - (float)clamp(dist / max_dist, 0.0, 1.0); float alpha = 1.0f - (float)clamp(dist / max_dist, 0.0, 1.0);
if (gete(player->talking_to) == it && player->state == CHARACTER_TALKING) alpha = 0.0f; if (gete(gs.player->talking_to) == it && gs.player->state == CHARACTER_TALKING) alpha = 0.0f;
if (it->being_hovered) if (it->being_hovered)
{ {
draw_quad((DrawParams) { quad_centered(it->pos, V2(TILE_SIZE, TILE_SIZE)), IMG(image_hovering_circle), WHITE }); draw_quad((DrawParams) { quad_centered(it->pos, V2(TILE_SIZE, TILE_SIZE)), IMG(image_hovering_circle), WHITE });
@ -5534,9 +5617,9 @@ void frame(void)
PROFILE_SCOPE("dialog menu") // big dialog panel draw big dialog panel PROFILE_SCOPE("dialog menu") // big dialog panel draw big dialog panel
{ {
static float on_screen = 0.0f; static float on_screen = 0.0f;
Entity *talking_to = gete(player->talking_to); Entity *talking_to = gete(gs.player->talking_to);
on_screen = Lerp(on_screen, unwarped_dt*9.0f, talking_to ? 1.0f : 0.0f); on_screen = Lerp(on_screen, unwarped_dt*9.0f, talking_to ? 1.0f : 0.0f);
if(is_fighting(player)) if(is_fighting(gs.player))
{ {
assert(talking_to); assert(talking_to);
draw_centered_text((TextParams){ false, MD_S8Fmt(frame_arena, "%s is fighting you. You can't leave until they stop fighting you", characters[talking_to->npc_kind].name), V2(screen_size().x*0.75f, screen_size().y*0.5f), WHITE, 1.0f}); draw_centered_text((TextParams){ false, MD_S8Fmt(frame_arena, "%s is fighting you. You can't leave until they stop fighting you", characters[talking_to->npc_kind].name), V2(screen_size().x*0.75f, screen_size().y*0.5f), WHITE, 1.0f});
@ -5548,9 +5631,9 @@ void frame(void)
if (aabb_is_valid(panel_aabb)) if (aabb_is_valid(panel_aabb))
{ {
if (!item_grid_state.open && pressed.mouse_down && !has_point(panel_aabb, mouse_pos) && !is_fighting(player)) if (!item_grid_state.open && pressed.mouse_down && !has_point(panel_aabb, mouse_pos) && !is_fighting(gs.player))
{ {
player->state = CHARACTER_IDLE; gs.player->state = CHARACTER_IDLE;
} }
draw_quad((DrawParams) { quad_aabb(panel_aabb), IMG(image_white_square), blendalpha(BLACK, 0.7f) }); draw_quad((DrawParams) { quad_aabb(panel_aabb), IMG(image_white_square), blendalpha(BLACK, 0.7f) });
@ -5706,7 +5789,7 @@ void frame(void)
// item grid modal draw item grid choose item pick item give item // item grid modal draw item grid choose item pick item give item
{ {
static float visible = 0.0f; static float visible = 0.0f;
static float hovered_state[ARRLEN(player->held_items.data)] = { 0 }; static float hovered_state[ARRLEN(gs.player->held_items.data)] = { 0 };
float target = 0.0f; float target = 0.0f;
if (item_grid_state.open) target = 1.0f; if (item_grid_state.open) target = 1.0f;
visible = Lerp(visible, unwarped_dt*9.0f, target); visible = Lerp(visible, unwarped_dt*9.0f, target);
@ -5733,7 +5816,7 @@ void frame(void)
const int horizontal_item_count = 10; const int horizontal_item_count = 10;
const int vertical_item_count = 6; const int vertical_item_count = 6;
assert(ARRLEN(player->held_items.data) < horizontal_item_count * vertical_item_count); assert(ARRLEN(gs.player->held_items.data) < horizontal_item_count * vertical_item_count);
Vec2 space_for_items = SubV2(aabb_size(grid_aabb), V2(padding*2.0f, padding*2.0f)); Vec2 space_for_items = SubV2(aabb_size(grid_aabb), V2(padding*2.0f, padding*2.0f));
float item_icon_width = (space_for_items.x - (horizontal_item_count - 1)*padding_btwn_items) / horizontal_item_count; float item_icon_width = (space_for_items.x - (horizontal_item_count - 1)*padding_btwn_items) / horizontal_item_count;
@ -5741,7 +5824,7 @@ void frame(void)
Vec2 cursor = AddV2(grid_aabb.upper_left, V2(padding, -padding)); Vec2 cursor = AddV2(grid_aabb.upper_left, V2(padding, -padding));
int pressed_index = -1; int pressed_index = -1;
BUFF_ITER_I(ItemKind, &player->held_items, i) BUFF_ITER_I(ItemKind, &gs.player->held_items, i)
{ {
Vec2 real_size = LerpV2(item_icon_size, hovered_state[i], MulV2F(item_icon_size, 1.25f)); Vec2 real_size = LerpV2(item_icon_size, hovered_state[i], MulV2F(item_icon_size, 1.25f));
Vec2 item_center = AddV2(cursor, MulV2F(V2(item_icon_size.x, -item_icon_size.y), 0.5f)); Vec2 item_center = AddV2(cursor, MulV2F(V2(item_icon_size.x, -item_icon_size.y), 0.5f));
@ -5778,19 +5861,19 @@ void frame(void)
if (pressed_index > -1) if (pressed_index > -1)
{ {
item_grid_state.open = false; item_grid_state.open = false;
ItemKind selected_item = player->held_items.data[pressed_index]; ItemKind selected_item = gs.player->held_items.data[pressed_index];
if(item_grid_state.for_giving && player->state == CHARACTER_TALKING) if(item_grid_state.for_giving && gs.player->state == CHARACTER_TALKING)
{ {
Entity *to = gete(player->talking_to); Entity *to = gete(gs.player->talking_to);
assert(to); assert(to);
Action give_action = {.kind = ACT_gift_item_to_targeting, .argument = { .item_to_give = selected_item }, .talking_to_somebody = true, .talking_to_kind = to->npc_kind}; Action give_action = {.kind = ACT_gift_item_to_targeting, .argument = { .item_to_give = selected_item }, .talking_to_somebody = true, .talking_to_kind = to->npc_kind};
perform_action(player, give_action); perform_action(gs.player, give_action);
} }
else else
{ {
use_item(player, selected_item); use_item(gs.player, selected_item);
} }
} }
} }
@ -5884,7 +5967,7 @@ void frame(void)
// update camera position // update camera position
{ {
Vec2 target = MulV2F(player->pos, -1.0f * cam.scale); Vec2 target = MulV2F(gs.player->pos, -1.0f * cam.scale);
if (LenV2(SubV2(target, cam.pos)) <= 0.2) if (LenV2(SubV2(target, cam.pos)) <= 0.2)
{ {
cam.pos = target; cam.pos = target;

@ -240,6 +240,14 @@ typedef struct Entity
bool destroy; bool destroy;
int generation; int generation;
// the kinds are at the top so you can quickly see what kind an entity is in the debugger
bool is_world;
bool is_prop;
bool is_machine;
bool is_item;
bool is_npc;
bool is_character;
// fields for all gs.entities // fields for all gs.entities
Vec2 pos; Vec2 pos;
Vec2 vel; // only used sometimes, like in old man and bullet Vec2 vel; // only used sometimes, like in old man and bullet
@ -248,28 +256,28 @@ typedef struct Entity
double dead_time; double dead_time;
bool dead; bool dead;
// the static world. An entity is always returned when you collide with something so support that here
// npcs and player // npcs and player
BUFF(ItemKind, 32) held_items; BUFF(ItemKind, 32) held_items;
// props // props
bool is_prop;
PropKind prop_kind; PropKind prop_kind;
// machines, like the machine that gives the player the idol, or the ones that // machines, like the machine that gives the player the idol, or the ones that
// shoot arrows // shoot arrows
bool is_machine;
MachineKind machine_kind; MachineKind machine_kind;
bool has_given_idol; bool has_given_idol;
float idol_reminder_opacity; // fades out float idol_reminder_opacity; // fades out
float arrow_timer; float arrow_timer;
// items // items
bool is_item;
bool held_by_player; bool held_by_player;
ItemKind item_kind; ItemKind item_kind;
// npcs // npcs
bool is_npc;
bool being_hovered; bool being_hovered;
bool perceptions_dirty; bool perceptions_dirty;
TextChunk *errorlist_first; TextChunk *errorlist_first;
@ -298,7 +306,6 @@ typedef struct Entity
double swing_timer; double swing_timer;
// character // character
bool is_character;
bool knighted; bool knighted;
bool in_conversation_mode; bool in_conversation_mode;
Vec2 to_throw_direction; Vec2 to_throw_direction;
@ -426,6 +433,10 @@ void fill_available_actions(Entity *it, AvailableActions *a)
typedef struct GameState { typedef struct GameState {
uint64_t tick; uint64_t tick;
bool won; bool won;
// these must point entities in its own array.
Entity *player;
Entity *world_entity;
Entity entities[MAX_ENTITIES]; Entity entities[MAX_ENTITIES];
} GameState; } GameState;

Loading…
Cancel
Save