|
|
@ -735,6 +735,8 @@ typedef struct GameState {
|
|
|
|
Entity entities[MAX_ENTITIES];
|
|
|
|
Entity entities[MAX_ENTITIES];
|
|
|
|
} GameState;
|
|
|
|
} GameState;
|
|
|
|
GameState gs = {0};
|
|
|
|
GameState gs = {0};
|
|
|
|
|
|
|
|
double unprocessed_fixed_timestep_time = 0.0;
|
|
|
|
|
|
|
|
#define FIXED_TIMESTEP (1.0/60.0)
|
|
|
|
|
|
|
|
|
|
|
|
EntityRef frome(Entity *e)
|
|
|
|
EntityRef frome(Entity *e)
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -2569,44 +2571,20 @@ void frame(void)
|
|
|
|
|
|
|
|
|
|
|
|
assert(player != NULL);
|
|
|
|
assert(player != NULL);
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEVTOOLS
|
|
|
|
// fixed timestep loop
|
|
|
|
dbgsquare(screen_to_world(mouse_pos));
|
|
|
|
Entity *interacting_with = 0; // used by rendering to figure out who to draw dialog box on
|
|
|
|
|
|
|
|
int num_timestep_loops = 0;
|
|
|
|
// tile coord
|
|
|
|
|
|
|
|
if(show_devtools)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
TileCoord hovering = world_to_tilecoord(screen_to_world(mouse_pos));
|
|
|
|
unprocessed_fixed_timestep_time += dt;
|
|
|
|
Vec2 points[4] ={0};
|
|
|
|
while(unprocessed_fixed_timestep_time > FIXED_TIMESTEP)
|
|
|
|
AABB q = tile_aabb(hovering);
|
|
|
|
|
|
|
|
dbgrect(q);
|
|
|
|
|
|
|
|
draw_text((TextParams){false, false, tprint("%d", get_tile(&level_level0, hovering).kind), world_to_screen(tilecoord_to_world(hovering)), BLACK, 1.0f});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// debug draw font image
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
draw_quad((DrawParams){true, quad_centered(V2(0.0, 0.0), V2(250.0, 250.0)), image_font,full_region(image_font), WHITE});
|
|
|
|
num_timestep_loops++;
|
|
|
|
}
|
|
|
|
unprocessed_fixed_timestep_time -= FIXED_TIMESTEP;
|
|
|
|
|
|
|
|
float dt = (float)FIXED_TIMESTEP;
|
|
|
|
// statistics
|
|
|
|
|
|
|
|
if(show_devtools)
|
|
|
|
|
|
|
|
PROFILE_SCOPE("statistics")
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Vec2 pos = V2(0.0, screen_size().Y);
|
|
|
|
|
|
|
|
int num_entities = 0;
|
|
|
|
|
|
|
|
ENTITIES_ITER(gs.entities) num_entities++;
|
|
|
|
|
|
|
|
char *stats = tprint("Frametime: %.1f ms\nProcessing: %.1f ms\nEntities: %d\nDraw calls: %d\nProfiling: %s\n", dt*1000.0, last_frame_processing_time*1000.0, num_entities, num_draw_calls, profiling ? "yes" : "no");
|
|
|
|
|
|
|
|
AABB bounds = draw_text((TextParams){false, true, stats, pos, BLACK, 1.0f});
|
|
|
|
|
|
|
|
pos.Y -= bounds.upper_left.Y - screen_size().Y;
|
|
|
|
|
|
|
|
bounds = draw_text((TextParams){false, true, stats, pos, BLACK, 1.0f});
|
|
|
|
|
|
|
|
// background panel
|
|
|
|
|
|
|
|
colorquad(false, quad_aabb(bounds), (Color){1.0, 1.0, 1.0, 0.3f});
|
|
|
|
|
|
|
|
draw_text((TextParams){false, false, stats, pos, BLACK, 1.0f});
|
|
|
|
|
|
|
|
num_draw_calls = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // devtools
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// process gs.entities
|
|
|
|
// process gs.entities
|
|
|
|
PROFILE_SCOPE("entity processing")
|
|
|
|
PROFILE_SCOPE("entity processing")
|
|
|
|
|
|
|
|
{
|
|
|
|
ENTITIES_ITER(gs.entities)
|
|
|
|
ENTITIES_ITER(gs.entities)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
assert(!(it->exists && it->generation == 0));
|
|
|
|
assert(!(it->exists && it->generation == 0));
|
|
|
@ -2865,6 +2843,7 @@ void frame(void)
|
|
|
|
assert(false);
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PROFILE_SCOPE("Destroy gs.entities")
|
|
|
|
PROFILE_SCOPE("Destroy gs.entities")
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -2879,13 +2858,10 @@ void frame(void)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PROFILE_SCOPE("process player and render player character")
|
|
|
|
PROFILE_SCOPE("process player")
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Vec2 character_sprite_pos = AddV2(player->pos, V2(0.0, 20.0f));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// do dialog
|
|
|
|
// do dialog
|
|
|
|
Entity *closest_interact_with = NULL;
|
|
|
|
Entity *closest_interact_with = 0;
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// find closest to talk to
|
|
|
|
// find closest to talk to
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -2919,23 +2895,13 @@ void frame(void)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Entity *interacting_with = closest_interact_with;
|
|
|
|
interacting_with = closest_interact_with;
|
|
|
|
if(player->state == CHARACTER_TALKING)
|
|
|
|
if(player->state == CHARACTER_TALKING)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
interacting_with = gete(player->talking_to);
|
|
|
|
interacting_with = gete(player->talking_to);
|
|
|
|
assert(interacting_with);
|
|
|
|
assert(interacting_with);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// if somebody, show their dialog panel
|
|
|
|
|
|
|
|
if(interacting_with)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// interaction circle
|
|
|
|
|
|
|
|
draw_quad((DrawParams){true, quad_centered(interacting_with->pos, V2(TILE_SIZE, TILE_SIZE)), image_dialog_circle, full_region(image_dialog_circle), WHITE});
|
|
|
|
|
|
|
|
if(interacting_with->is_npc)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
draw_dialog_panel(interacting_with);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// process dialog and display dialog box when talking to NPC
|
|
|
|
// process dialog and display dialog box when talking to NPC
|
|
|
|
if(player->state == CHARACTER_TALKING)
|
|
|
|
if(player->state == CHARACTER_TALKING)
|
|
|
@ -2948,6 +2914,7 @@ void frame(void)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// roll input management, sometimes means talk to the npc
|
|
|
|
// roll input management, sometimes means talk to the npc
|
|
|
|
if(player->state != CHARACTER_TALKING && roll_just_pressed && closest_interact_with)
|
|
|
|
if(player->state != CHARACTER_TALKING && roll_just_pressed && closest_interact_with)
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -2994,8 +2961,6 @@ void frame(void)
|
|
|
|
BUFF_CLEAR(&player->done_damage_to_this_swing);
|
|
|
|
BUFF_CLEAR(&player->done_damage_to_this_swing);
|
|
|
|
player->swing_progress = 0.0;
|
|
|
|
player->swing_progress = 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// roll processing
|
|
|
|
// roll processing
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if(player->state != CHARACTER_IDLE && player->state != CHARACTER_WALKING)
|
|
|
|
if(player->state != CHARACTER_IDLE && player->state != CHARACTER_WALKING)
|
|
|
@ -3015,7 +2980,6 @@ void frame(void)
|
|
|
|
if(!player->is_rolling) player->time_not_rolling += dt;
|
|
|
|
if(!player->is_rolling) player->time_not_rolling += dt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Vec2 target_vel = {0};
|
|
|
|
Vec2 target_vel = {0};
|
|
|
|
float speed = 0.0f;
|
|
|
|
float speed = 0.0f;
|
|
|
|
|
|
|
|
|
|
|
@ -3033,17 +2997,6 @@ void frame(void)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
speed *= 1.0f + ((float)player->boots_modifier * 0.1f);
|
|
|
|
speed *= 1.0f + ((float)player->boots_modifier * 0.1f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(player->is_rolling)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
draw_animated_sprite(&knight_rolling, player->roll_progress, player->facing_left, character_sprite_pos, WHITE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
draw_animated_sprite(&knight_running, elapsed_time, player->facing_left, character_sprite_pos, WHITE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(LenV2(movement) == 0.0)
|
|
|
|
if(LenV2(movement) == 0.0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
player->state = CHARACTER_IDLE;
|
|
|
|
player->state = CHARACTER_IDLE;
|
|
|
@ -3054,14 +3007,6 @@ void frame(void)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(player->state == CHARACTER_IDLE)
|
|
|
|
else if(player->state == CHARACTER_IDLE)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if(player->is_rolling)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
draw_animated_sprite(&knight_rolling, player->roll_progress, player->facing_left, character_sprite_pos, WHITE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
draw_animated_sprite(&knight_idle, elapsed_time, player->facing_left, character_sprite_pos, WHITE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if(LenV2(movement) > 0.01) player->state = CHARACTER_WALKING;
|
|
|
|
if(LenV2(movement) > 0.01) player->state = CHARACTER_WALKING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(player->state == CHARACTER_ATTACK)
|
|
|
|
else if(player->state == CHARACTER_ATTACK)
|
|
|
@ -3073,9 +3018,7 @@ void frame(void)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
request_do_damage(*it, player->pos, 0.2f);
|
|
|
|
request_do_damage(*it, player->pos, 0.2f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
player->swing_progress += dt;
|
|
|
|
player->swing_progress += dt;
|
|
|
|
draw_animated_sprite(&knight_attack, player->swing_progress, player->facing_left, character_sprite_pos, WHITE);
|
|
|
|
|
|
|
|
if(player->swing_progress > anim_sprite_duration(&knight_attack))
|
|
|
|
if(player->swing_progress > anim_sprite_duration(&knight_attack))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
player->state = CHARACTER_IDLE;
|
|
|
|
player->state = CHARACTER_IDLE;
|
|
|
@ -3083,11 +3026,10 @@ void frame(void)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(player->state == CHARACTER_TALKING)
|
|
|
|
else if(player->state == CHARACTER_TALKING)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
draw_animated_sprite(&knight_idle, elapsed_time, player->facing_left, character_sprite_pos, WHITE);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
|
assert(false); // unknown character state? not defined how to draw
|
|
|
|
assert(false); // unknown character state? not defined how to process
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// velocity processing
|
|
|
|
// velocity processing
|
|
|
@ -3096,14 +3038,66 @@ void frame(void)
|
|
|
|
player->vel = LerpV2(player->vel, dt * 15.0f, target_vel);
|
|
|
|
player->vel = LerpV2(player->vel, dt * 15.0f, target_vel);
|
|
|
|
player->pos = move_and_slide((MoveSlideParams){player, player->pos, player->vel});
|
|
|
|
player->pos = move_and_slide((MoveSlideParams){player, player->pos, player->vel});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// health
|
|
|
|
// health
|
|
|
|
if(player->damage >= 1.0)
|
|
|
|
if(player->damage >= 1.0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
reset_level();
|
|
|
|
reset_level();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} // while loop
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PROFILE_SCOPE("render player")
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Vec2 character_sprite_pos = AddV2(player->pos, V2(0.0, 20.0f));
|
|
|
|
|
|
|
|
// if somebody, show their dialog panel
|
|
|
|
|
|
|
|
if(interacting_with)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// interaction circle
|
|
|
|
|
|
|
|
draw_quad((DrawParams){true, quad_centered(interacting_with->pos, V2(TILE_SIZE, TILE_SIZE)), image_dialog_circle, full_region(image_dialog_circle), WHITE});
|
|
|
|
|
|
|
|
if(interacting_with->is_npc)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
draw_dialog_panel(interacting_with);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(player->state == CHARACTER_WALKING)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if(player->is_rolling)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
draw_animated_sprite(&knight_rolling, player->roll_progress, player->facing_left, character_sprite_pos, WHITE);
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
draw_animated_sprite(&knight_running, elapsed_time, player->facing_left, character_sprite_pos, WHITE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if(player->state == CHARACTER_IDLE)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if(player->is_rolling)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
draw_animated_sprite(&knight_rolling, player->roll_progress, player->facing_left, character_sprite_pos, WHITE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
draw_animated_sprite(&knight_idle, elapsed_time, player->facing_left, character_sprite_pos, WHITE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if(player->state == CHARACTER_ATTACK)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
draw_animated_sprite(&knight_attack, player->swing_progress, player->facing_left, character_sprite_pos, WHITE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if(player->state == CHARACTER_TALKING)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
draw_animated_sprite(&knight_idle, elapsed_time, player->facing_left, character_sprite_pos, WHITE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
assert(false); // unknown character state? not defined how to draw
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// hurt vignette
|
|
|
|
|
|
|
|
if(player->damage > 0.0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
draw_quad((DrawParams){false, (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}});
|
|
|
|
draw_quad((DrawParams){false, (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}});
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -3287,7 +3281,6 @@ void frame(void)
|
|
|
|
draw_all_translucent();
|
|
|
|
draw_all_translucent();
|
|
|
|
|
|
|
|
|
|
|
|
// ui
|
|
|
|
// ui
|
|
|
|
|
|
|
|
|
|
|
|
#define HELPER_SIZE 250.0f
|
|
|
|
#define HELPER_SIZE 250.0f
|
|
|
|
if(!mobile_controls)
|
|
|
|
if(!mobile_controls)
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -3312,6 +3305,43 @@ void frame(void)
|
|
|
|
draw_quad((DrawParams){false, quad_centered(attack_button_pos(), V2(mobile_button_size(), mobile_button_size())), IMG(image_mobile_button), WHITE, .y_coord_sorting = 1.0f});
|
|
|
|
draw_quad((DrawParams){false, quad_centered(attack_button_pos(), V2(mobile_button_size(), mobile_button_size())), IMG(image_mobile_button), WHITE, .y_coord_sorting = 1.0f});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEVTOOLS
|
|
|
|
|
|
|
|
dbgsquare(screen_to_world(mouse_pos));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// tile coord
|
|
|
|
|
|
|
|
if(show_devtools)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
TileCoord hovering = world_to_tilecoord(screen_to_world(mouse_pos));
|
|
|
|
|
|
|
|
Vec2 points[4] ={0};
|
|
|
|
|
|
|
|
AABB q = tile_aabb(hovering);
|
|
|
|
|
|
|
|
dbgrect(q);
|
|
|
|
|
|
|
|
draw_text((TextParams){false, false, tprint("%d", get_tile(&level_level0, hovering).kind), world_to_screen(tilecoord_to_world(hovering)), BLACK, 1.0f});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// debug draw font image
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
draw_quad((DrawParams){true, quad_centered(V2(0.0, 0.0), V2(250.0, 250.0)), image_font,full_region(image_font), WHITE});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// statistics
|
|
|
|
|
|
|
|
if(show_devtools)
|
|
|
|
|
|
|
|
PROFILE_SCOPE("statistics")
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Vec2 pos = V2(0.0, screen_size().Y);
|
|
|
|
|
|
|
|
int num_entities = 0;
|
|
|
|
|
|
|
|
ENTITIES_ITER(gs.entities) num_entities++;
|
|
|
|
|
|
|
|
char *stats = tprint("Frametime: %.1f ms\nProcessing: %.1f ms\nEntities: %d\nDraw calls: %d\nProfiling: %s\nNumber fixed timestep loops: %d\n", dt*1000.0, last_frame_processing_time*1000.0, num_entities, num_draw_calls, profiling ? "yes" : "no", num_timestep_loops);
|
|
|
|
|
|
|
|
AABB bounds = draw_text((TextParams){false, true, stats, pos, BLACK, 1.0f});
|
|
|
|
|
|
|
|
pos.Y -= bounds.upper_left.Y - screen_size().Y;
|
|
|
|
|
|
|
|
bounds = draw_text((TextParams){false, true, stats, pos, BLACK, 1.0f});
|
|
|
|
|
|
|
|
// background panel
|
|
|
|
|
|
|
|
colorquad(false, quad_aabb(bounds), (Color){1.0, 1.0, 1.0, 0.3f});
|
|
|
|
|
|
|
|
draw_text((TextParams){false, false, stats, pos, BLACK, 1.0f});
|
|
|
|
|
|
|
|
num_draw_calls = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // devtools
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// update camera position
|
|
|
|
// update camera position
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Vec2 target = MulV2F(player->pos, -1.0f * cam.scale);
|
|
|
|
Vec2 target = MulV2F(player->pos, -1.0f * cam.scale);
|
|
|
|