rework timers, update overlap/intersect and other procedures, some other things i dont remember
This commit is contained in:
parent
a527036436
commit
a22bcfd97e
@ -11,9 +11,9 @@
|
||||
|
||||
|
||||
static void update_timers(Player *player) {
|
||||
tick_timer(&player->jump_air_timer);
|
||||
tick_timer(&player->jump_coyote_timer);
|
||||
tick_timer(&player->jump_buffer_timer);
|
||||
player->jump_air_timer = timer_tick_frames(player->jump_air_timer);
|
||||
player->jump_coyote_timer = timer_tick_frames(player->jump_coyote_timer);
|
||||
player->jump_buffer_timer = timer_tick_frames(player->jump_buffer_timer);
|
||||
}
|
||||
|
||||
|
||||
|
@ -39,7 +39,7 @@ static void drawdef_debug(struct World *world) {
|
||||
for (size_t i = 0; i < world->tilemap_height * world->tilemap_width; ++i) {
|
||||
if (world->tiles[i].type == TILE_TYPE_VOID) continue;
|
||||
|
||||
draw_rectangle(to_frect(world->tiles[i].rect),
|
||||
draw_rectangle(to_rect(world->tiles[i].rect),
|
||||
(Color) { 255, 0, 255, 128 });
|
||||
}
|
||||
}
|
||||
@ -106,7 +106,7 @@ void world_drawdef(struct World *world) {
|
||||
if (world->tiles[i].type == TILE_TYPE_VOID)
|
||||
continue;
|
||||
|
||||
m_sprite("/assets/white.png", to_frect(world->tiles[i].rect));
|
||||
m_sprite("/assets/white.png", to_rect(world->tiles[i].rect));
|
||||
}
|
||||
|
||||
drawdef_debug(world);
|
||||
@ -121,19 +121,12 @@ bool world_find_intersect_frect(struct World *world, Rect rect, Rect *intersecti
|
||||
if (world->tiles[i].type == TILE_TYPE_VOID)
|
||||
continue;
|
||||
|
||||
Rect tile_frect = {
|
||||
.x = (float)(world->tiles[i].rect.x),
|
||||
.y = (float)(world->tiles[i].rect.y),
|
||||
.w = (float)(world->tiles[i].rect.w),
|
||||
.h = (float)(world->tiles[i].rect.h),
|
||||
};
|
||||
Rect const tile_frect = to_rect(world->tiles[i].rect);
|
||||
|
||||
if (intersection == NULL) {
|
||||
Rect temp;
|
||||
is_intersecting = overlap_frect(&rect, &tile_frect, &temp);
|
||||
} else {
|
||||
is_intersecting = overlap_frect(&rect, &tile_frect, intersection);
|
||||
}
|
||||
is_intersecting = intersect_rect(rect, tile_frect);
|
||||
|
||||
if (intersection)
|
||||
*intersection = overlap_rect(rect, tile_frect);
|
||||
|
||||
if (is_intersecting)
|
||||
break;
|
||||
@ -151,14 +144,12 @@ bool world_find_intersect_rect(struct World *world, Recti rect, Recti *intersect
|
||||
if (world->tiles[i].type == TILE_TYPE_VOID)
|
||||
continue;
|
||||
|
||||
Recti *tile_rect = &world->tiles[i].rect;
|
||||
Recti const tile_rect = world->tiles[i].rect;
|
||||
|
||||
if (intersection == NULL) {
|
||||
Recti temp;
|
||||
is_intersecting = overlap_rect(&rect, tile_rect, &temp);
|
||||
} else {
|
||||
is_intersecting = overlap_rect(&rect, tile_rect, intersection);
|
||||
}
|
||||
is_intersecting = intersect_recti(rect, tile_rect);
|
||||
|
||||
if (intersection)
|
||||
*intersection = overlap_recti(rect, tile_rect);
|
||||
|
||||
if (is_intersecting)
|
||||
break;
|
||||
|
@ -3,11 +3,13 @@ api needs to facilitate easy interoperability with other languages and tools,
|
||||
for that certain considerations are taken:
|
||||
|
||||
* number of public api calls is kept at the minimum
|
||||
* procedure signatures can only use basic types, no aggregates, with exception of Vec/Matrix types and alike,
|
||||
* procedure parameters can only use basic types, no aggregates, with exception of Vec/Matrix types and alike,
|
||||
with no expectation on new additions (see [/include/twn_types.h](/include/twn_types.h))
|
||||
* optionals can be expressed via pointer passage of value primitives, with NULL expressive default, but they should be immutable
|
||||
* opaque types are passed around as pointers
|
||||
* opaque typed parameters are passed around as pointers
|
||||
* when mutation on input is done, - it shouldn't be achieved from a mutable pointer, but return value
|
||||
* return value could be a simple aggregate that is translatable to value-only dictionary
|
||||
* module prefix is used for namespacing, actual symbols come after the prefix (`module_symbol`)
|
||||
* symbols should not contain letters at the start nor after the namespace prefix
|
||||
* floats are preferred over integers
|
||||
* [/include/twn_api.json](/include/twn_api.json) file is hand-kept with a schema to aid automatic binding generation and other tooling
|
||||
|
@ -24,74 +24,67 @@
|
||||
TWN_API void *crealloc(void *ptr, size_t size);
|
||||
TWN_API void *ccalloc(size_t num, size_t size);
|
||||
|
||||
TWN_API void log_info(const char *restrict format, ...);
|
||||
TWN_API void log_critical(const char *restrict format, ...);
|
||||
TWN_API void log_warn(const char *restrict format, ...);
|
||||
|
||||
/* saves all texture atlases as BMP files in the write directory */
|
||||
TWN_API void textures_dump_atlases(void);
|
||||
|
||||
/* returns true if str ends with suffix */
|
||||
TWN_API bool strends(const char *str, const char *suffix);
|
||||
|
||||
/* TODO: this is why generics were invented. sorry, i'm tired today */
|
||||
TWN_API double clamp(double d, double min, double max);
|
||||
TWN_API float clampf(float f, float min, float max);
|
||||
TWN_API int clampi(int i, int min, int max);
|
||||
|
||||
/* sets buf_out to a pointer to a byte buffer which must be freed. */
|
||||
/* returns the size of this buffer. */
|
||||
TWN_API int64_t file_to_bytes(const char *path, unsigned char **buf_out);
|
||||
|
||||
/* returns a pointer to a string which must be freed */
|
||||
TWN_API char *file_to_str(const char *path);
|
||||
|
||||
/* returns true if the file exists in the filesystem */
|
||||
TWN_API bool file_exists(const char *path);
|
||||
|
||||
#endif /* TWN_NOT_C */
|
||||
|
||||
|
||||
TWN_API void log_info(const char *restrict format, ...);
|
||||
TWN_API void log_critical(const char *restrict format, ...);
|
||||
TWN_API void log_warn(const char *restrict format, ...);
|
||||
|
||||
|
||||
/* TODO: this is why generics were invented. sorry, i'm tired today */
|
||||
TWN_API double clamp(double d, double min, double max);
|
||||
TWN_API float clampf(float f, float min, float max);
|
||||
TWN_API int clampi(int i, int min, int max);
|
||||
|
||||
|
||||
/* sets buf_out to a pointer to a byte buffer which must be freed. */
|
||||
/* returns the size of this buffer. */
|
||||
TWN_API int64_t file_to_bytes(const char *path, unsigned char **buf_out);
|
||||
|
||||
/* returns a pointer to a string which must be freed */
|
||||
TWN_API char *file_to_str(const char *path);
|
||||
|
||||
/* returns true if the file exists in the filesystem */
|
||||
TWN_API bool file_exists(const char *path);
|
||||
|
||||
|
||||
/* saves all texture atlases as BMP files in the write directory */
|
||||
TWN_API void textures_dump_atlases(void);
|
||||
|
||||
|
||||
/* returns true if str ends with suffix */
|
||||
TWN_API TWN_API bool strends(const char *str, const char *suffix);
|
||||
|
||||
|
||||
/* */
|
||||
/* GAME LOGIC UTILITIES */
|
||||
/* */
|
||||
|
||||
/* calculates the overlap of two rectangles and places it in result. */
|
||||
/* result may be NULL. if this is the case, it will simply be ignored. */
|
||||
/* returns true if the rectangles are indeed intersecting. */
|
||||
TWN_API bool overlap_rect(const Recti *a, const Recti *b, Recti *result);
|
||||
TWN_API bool overlap_frect(const Rect *a, const Rect *b, Rect *result);
|
||||
/* calculates the overlap of two rectangles */
|
||||
TWN_API Recti overlap_recti(const Recti a, const Recti b);
|
||||
TWN_API Rect overlap_rect(const Rect a, const Rect b);
|
||||
|
||||
/* returns true if two rectangles are intersecting */
|
||||
TWN_API bool intersect_rect(const Recti *a, const Recti *b);
|
||||
TWN_API bool intersect_frect(const Rect *a, const Rect *b);
|
||||
TWN_API bool intersect_recti(const Recti a, const Recti b);
|
||||
TWN_API bool intersect_rect(const Rect a, const Rect b);
|
||||
|
||||
/* TODO: generics and specials (see m_vec2_from() for an example)*/
|
||||
TWN_API Rect to_frect(Recti rect);
|
||||
TWN_API Recti to_recti(Rect rect);
|
||||
TWN_API Rect to_rect(Recti rect);
|
||||
|
||||
TWN_API Vec2 frect_center(Rect rect);
|
||||
TWN_API Vec2i center_recti(Recti rect);
|
||||
TWN_API Vec2 center_rect(Rect rect);
|
||||
|
||||
|
||||
/* decrements an lvalue (which should be an int), stopping at 0 */
|
||||
/* decrements an integer value, stopping at 0 */
|
||||
/* meant for tick-based timers in game logic */
|
||||
/*
|
||||
* example:
|
||||
* tick_timer(&player->jump_air_timer);
|
||||
*/
|
||||
TWN_API void tick_timer(int *value);
|
||||
TWN_API int32_t timer_tick_frames(int32_t frames_left);
|
||||
|
||||
/* decrements a floating point second-based timer, stopping at 0.0 */
|
||||
/* decrements a floating point second-based timer, stopping at 0.0f */
|
||||
/* meant for poll based real time logic in game logic */
|
||||
/* note that it should be decremented only on the next tick after its creation */
|
||||
TWN_API void tick_ftimer(float *value);
|
||||
TWN_API float timer_tick_seconds(float seconds_left);
|
||||
|
||||
/* same as `tick_ftimer` but instead of clamping it repeats */
|
||||
/* returns true if value was cycled */
|
||||
TWN_API bool repeat_ftimer(float *value, float at);
|
||||
TWN_API struct timer_elapse_frames_result {
|
||||
bool elapsed; int32_t frames_left;
|
||||
} timer_elapse_frames(int32_t frames_left, int32_t interval);
|
||||
|
||||
TWN_API struct timer_elapse_seconds_result {
|
||||
bool elapsed; float seconds_left;
|
||||
} timer_elapse_seconds(float seconds_left, float interval);
|
||||
|
||||
#endif
|
||||
|
@ -216,7 +216,7 @@ void render_sprite_batch(const Primitive2D primitives[],
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
const Vec2 c = frect_center(sprite.rect);
|
||||
const Vec2 c = center_rect(sprite.rect);
|
||||
const Vec2 t = fast_cossine(sprite.rotation + (float)M_PI_4);
|
||||
const Vec2 d = {
|
||||
.x = t.x * sprite.rect.w * (float)M_SQRT1_2,
|
||||
@ -231,7 +231,7 @@ void render_sprite_batch(const Primitive2D primitives[],
|
||||
} else {
|
||||
/* rotated non-square case*/
|
||||
|
||||
const Vec2 c = frect_center(sprite.rect);
|
||||
const Vec2 c = center_rect(sprite.rect);
|
||||
const Vec2 t = fast_cossine(sprite.rotation);
|
||||
|
||||
const Vec2 h = { sprite.rect.w / 2, sprite.rect.h / 2 };
|
||||
|
111
src/twn_util.c
111
src/twn_util.c
@ -197,49 +197,57 @@ bool strends(const char *str, const char *suffix) {
|
||||
}
|
||||
|
||||
|
||||
bool overlap_rect(const Recti *a, const Recti *b, Recti *result) {
|
||||
SDL_Rect a_sdl = { a->x, a->y, a->w, a->h };
|
||||
SDL_Rect b_sdl = { b->x, b->y, b->w, b->h };
|
||||
/* TODO: have our own */
|
||||
Recti overlap_recti(const Recti a, const Recti b) {
|
||||
SDL_Rect a_sdl = { a.x, a.y, a.w, a.h };
|
||||
SDL_Rect b_sdl = { b.x, b.y, b.w, b.h };
|
||||
SDL_Rect result_sdl = { 0 };
|
||||
|
||||
bool intersection = SDL_IntersectRect(&a_sdl, &b_sdl, &result_sdl);
|
||||
(void)SDL_IntersectRect(&a_sdl, &b_sdl, &result_sdl);
|
||||
|
||||
if (result != NULL)
|
||||
*result = (Recti){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h };
|
||||
|
||||
return intersection;
|
||||
return (Recti){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h };
|
||||
}
|
||||
|
||||
|
||||
bool overlap_frect(const Rect *a, const Rect *b, Rect *result) {
|
||||
SDL_FRect a_sdl = { a->x, a->y, a->w, a->h };
|
||||
SDL_FRect b_sdl = { b->x, b->y, b->w, b->h };
|
||||
/* TODO: have our own */
|
||||
Rect overlap_rect(const Rect a, const Rect b) {
|
||||
SDL_FRect a_sdl = { a.x, a.y, a.w, a.h };
|
||||
SDL_FRect b_sdl = { b.x, b.y, b.w, b.h };
|
||||
SDL_FRect result_sdl = { 0 };
|
||||
|
||||
bool intersection = SDL_IntersectFRect(&a_sdl, &b_sdl, &result_sdl);
|
||||
(void)SDL_IntersectFRect(&a_sdl, &b_sdl, &result_sdl);
|
||||
|
||||
if (result != NULL)
|
||||
*result = (Rect){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h };
|
||||
|
||||
return intersection;
|
||||
return (Rect){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h };
|
||||
}
|
||||
|
||||
|
||||
bool intersect_rect(const Recti *a, const Recti *b) {
|
||||
SDL_Rect a_sdl = { a->x, a->y, a->w, a->h };
|
||||
SDL_Rect b_sdl = { b->x, b->y, b->w, b->h };
|
||||
/* TODO: have our own */
|
||||
bool intersect_recti(const Recti a, const Recti b) {
|
||||
SDL_Rect a_sdl = { a.x, a.y, a.w, a.h };
|
||||
SDL_Rect b_sdl = { b.x, b.y, b.w, b.h };
|
||||
return SDL_HasIntersection(&a_sdl, &b_sdl);
|
||||
}
|
||||
|
||||
|
||||
bool intersect_frect(const Rect *a, const Rect *b) {
|
||||
SDL_FRect a_sdl = { a->x, a->y, a->w, a->h };
|
||||
SDL_FRect b_sdl = { b->x, b->y, b->w, b->h };
|
||||
/* TODO: have our own */
|
||||
bool intersect_rect(const Rect a, const Rect b) {
|
||||
SDL_FRect a_sdl = { a.x, a.y, a.w, a.h };
|
||||
SDL_FRect b_sdl = { b.x, b.y, b.w, b.h };
|
||||
return SDL_HasIntersectionF(&a_sdl, &b_sdl);
|
||||
}
|
||||
|
||||
|
||||
Rect to_frect(Recti rect) {
|
||||
Recti to_recti(Rect rect) {
|
||||
return (Recti) {
|
||||
.h = (int32_t)rect.h,
|
||||
.w = (int32_t)rect.w,
|
||||
.x = (int32_t)rect.x,
|
||||
.y = (int32_t)rect.y,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Rect to_rect(Recti rect) {
|
||||
return (Rect) {
|
||||
.h = (float)rect.h,
|
||||
.w = (float)rect.w,
|
||||
@ -249,7 +257,15 @@ Rect to_frect(Recti rect) {
|
||||
}
|
||||
|
||||
|
||||
Vec2 frect_center(Rect rect) {
|
||||
Vec2i center_recti(Recti rect) {
|
||||
return (Vec2i){
|
||||
.x = rect.x + rect.w / 2,
|
||||
.y = rect.y + rect.h / 2,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Vec2 center_rect(Rect rect) {
|
||||
return (Vec2){
|
||||
.x = rect.x + rect.w / 2,
|
||||
.y = rect.y + rect.h / 2,
|
||||
@ -257,25 +273,52 @@ Vec2 frect_center(Rect rect) {
|
||||
}
|
||||
|
||||
|
||||
void tick_timer(int *value) {
|
||||
*value = MAX(*value - 1, 0);
|
||||
int32_t timer_tick_frames(int32_t frames_left) {
|
||||
SDL_assert(frames_left >= 0);
|
||||
return MAX(frames_left - 1, 0);
|
||||
}
|
||||
|
||||
|
||||
void tick_ftimer(float *value) {
|
||||
*value = MAX(*value - ((float)ctx.delta_time / (float)ctx.clocks_per_second), 0.0f);
|
||||
float timer_tick_seconds(float seconds_left) {
|
||||
SDL_assert(seconds_left >= 0);
|
||||
return MAX(seconds_left - ((float)ctx.delta_time / (float)ctx.clocks_per_second), 0.0f);
|
||||
}
|
||||
|
||||
|
||||
bool repeat_ftimer(float *value, float at) {
|
||||
*value -= (float)ctx.delta_time / (float)ctx.clocks_per_second;
|
||||
if (*value < 0.0f) {
|
||||
*value += at;
|
||||
return true;
|
||||
struct timer_elapse_frames_result timer_elapse_frames(int32_t frames_left, int32_t interval) {
|
||||
SDL_assert(frames_left >= 0);
|
||||
SDL_assert(interval > 0);
|
||||
|
||||
frames_left -= 1;
|
||||
bool elapsed = false;
|
||||
if (frames_left <= 0) {
|
||||
elapsed = true;
|
||||
frames_left += interval;
|
||||
}
|
||||
return false;
|
||||
return (struct timer_elapse_frames_result) {
|
||||
.elapsed = elapsed,
|
||||
.frames_left = frames_left
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
struct timer_elapse_seconds_result timer_elapse_seconds(float seconds_left, float interval) {
|
||||
SDL_assert(seconds_left >= 0);
|
||||
SDL_assert(interval > 0);
|
||||
|
||||
seconds_left -= (float)ctx.delta_time / (float)ctx.clocks_per_second;
|
||||
bool elapsed = false;
|
||||
if (seconds_left <= 0.0f) {
|
||||
elapsed = true;
|
||||
seconds_left += interval;
|
||||
}
|
||||
return (struct timer_elapse_seconds_result) {
|
||||
.elapsed = elapsed,
|
||||
.seconds_left = seconds_left
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/* TODO: handle utf8 */
|
||||
char *expand_asterisk(const char *mask, const char *to) {
|
||||
const char *offset = SDL_strchr(mask, '*');
|
||||
|
Loading…
Reference in New Issue
Block a user