diff options
Diffstat (limited to 'kon/kon2d.c')
| -rw-r--r-- | kon/kon2d.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/kon/kon2d.c b/kon/kon2d.c new file mode 100644 index 0000000..2cc9fdd --- /dev/null +++ b/kon/kon2d.c @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: MIT */ +/* + * kon2d.c + * + * Copyright (c) 2026 KonAki + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "kon2d.h" + +#include <math.h> + +#define STEP (1.0 / 120.0) +#define MAX_FRAME 0.25 + +/*** platformer ***/ + +void kon_platformerInit(kon_platformer_t *p, kon_box2_t box) +{ + kon_platformer_t zero = {0}; + + *p = zero; + p->box = box; + p->runSpeed = 120.0f; + p->groundAccel = 18.0f; + p->airAccel = 8.0f; + p->gravity = 700.0f; + p->jumpSpeed = 260.0f; + p->jumpCut = 60.0f; + p->maxFall = 400.0f; + p->coyoteTime = 0.08f; + p->bufferTime = 0.1f; +} + +int kon_platformerStep(kon_platformer_t *p, const kon_tilemap_t *map, const uint8_t *solid, int solidCount, float dt) +{ + float run = (float)p->moveX * p->runSpeed; + float ease = (p->onGround ? p->groundAccel : p->airAccel) * dt; + int flags; + + if (ease > 1.0f) ease = 1.0f; + p->velocity.x += (run - p->velocity.x) * ease; + p->velocity.y += p->gravity * dt; + if (p->velocity.y > p->maxFall) p->velocity.y = p->maxFall; + + if (p->jumpHeld && !p->jumpWas) p->buffer = p->bufferTime; + p->jumpWas = p->jumpHeld; + if (p->buffer > 0.0f) p->buffer -= dt; + if (p->coyote > 0.0f) p->coyote -= dt; + + if (p->buffer > 0.0f && p->coyote > 0.0f) { + p->velocity.y = -p->jumpSpeed; + p->buffer = 0.0f; + p->coyote = 0.0f; + } + if (!p->jumpHeld && p->velocity.y < -p->jumpCut) p->velocity.y = -p->jumpCut; + + flags = kon_tilemapMove(map, &p->box, p->velocity.x * dt, p->velocity.y * dt, solid, solidCount); + + if (flags & (KON_HIT_LEFT | KON_HIT_RIGHT)) p->velocity.x = 0.0f; + if (flags & (KON_HIT_UP | KON_HIT_DOWN)) p->velocity.y = 0.0f; + p->onGround = (flags & KON_HIT_DOWN) != 0; + if (p->onGround) p->coyote = p->coyoteTime; + return flags; +} + +void kon_platformerUpdate(kon_platformer_t *p, const kon_tilemap_t *map, const uint8_t *solid, int solidCount, double dt) +{ + if (!(dt > 0.0)) return; + if (dt > MAX_FRAME) dt = MAX_FRAME; + + p->leftover += (float)dt; + while (p->leftover >= (float)STEP) { + kon_platformerStep(p, map, solid, solidCount, (float)STEP); + p->leftover -= (float)STEP; + } +} + +/*** camera ***/ + +static float clampAxis(float value, float view, float bounds) +{ + if (bounds <= view) return (bounds - view) * 0.5f; + if (value < 0.0f) return 0.0f; + if (value > bounds - view) return bounds - view; + return value; +} + +void kon_camera2Init(kon_camera2_t *cam, int viewWidth, int viewHeight, float boundsWidth, float boundsHeight) +{ + cam->x = cam->y = 0.0f; + cam->viewWidth = viewWidth; + cam->viewHeight = viewHeight; + cam->boundsWidth = boundsWidth; + cam->boundsHeight = boundsHeight; + cam->stiffness = 8.0f; + cam->x = clampAxis(0.0f, (float)viewWidth, boundsWidth); + cam->y = clampAxis(0.0f, (float)viewHeight, boundsHeight); +} + +void kon_camera2Snap(kon_camera2_t *cam, float targetX, float targetY) +{ + cam->x = clampAxis(targetX - (float)cam->viewWidth * 0.5f, (float)cam->viewWidth, cam->boundsWidth); + cam->y = clampAxis(targetY - (float)cam->viewHeight * 0.5f, (float)cam->viewHeight, cam->boundsHeight); +} + +void kon_camera2Follow(kon_camera2_t *cam, float targetX, float targetY, float dt) +{ + float wantX = clampAxis(targetX - (float)cam->viewWidth * 0.5f, (float)cam->viewWidth, cam->boundsWidth); + float wantY = clampAxis(targetY - (float)cam->viewHeight * 0.5f, (float)cam->viewHeight, cam->boundsHeight); + float ease = 1.0f; + + if (cam->stiffness > 0.0f && dt > 0.0f) ease = 1.0f - expf(-cam->stiffness * dt); + cam->x += (wantX - cam->x) * ease; + cam->y += (wantY - cam->y) * ease; +} + +int kon_camera2X(const kon_camera2_t *cam) +{ + return (int)floorf(cam->x + 0.5f); +} + +int kon_camera2Y(const kon_camera2_t *cam) +{ + return (int)floorf(cam->y + 0.5f); +} + +/*** animation ***/ + +void kon_animPlay(kon_anim_t *anim, int first, int count, float fps, int loop) +{ + if (anim->first == first && anim->count == count && anim->fps == fps && anim->loop == loop) return; + anim->first = first; + anim->count = count; + anim->fps = fps; + anim->loop = loop; + anim->time = 0.0f; +} + +void kon_animStep(kon_anim_t *anim, float dt) +{ + if (anim->count <= 0 || !(dt > 0.0f)) return; + anim->time += dt; + if (anim->loop) { + float length = (float)anim->count / anim->fps; + + if (anim->fps > 0.0f && anim->time >= length) anim->time = fmodf(anim->time, length); + } +} + +int kon_animFrame(const kon_anim_t *anim) +{ + int n; + + if (anim->count <= 0) return anim->first; + if (!(anim->fps > 0.0f)) return anim->first; + n = (int)(anim->time * anim->fps); + if (n >= anim->count) n = anim->loop ? n % anim->count : anim->count - 1; + return anim->first + n; +} + +int kon_animDone(const kon_anim_t *anim) +{ + if (anim->loop || anim->count <= 0) return 0; + return anim->fps > 0.0f && (int)(anim->time * anim->fps) >= anim->count; +} + +void kon_drawFrame(kon_framebuffer_t *fb, const kon_image *sheet, int frameWidth, int frameHeight, int index, int x, int y, int flip) +{ + kon_rect_t src = kon_frameRect(sheet, frameWidth, frameHeight, index); + + if (src.w <= 0) return; + if (!flip) { + kon_drawImageRegion(fb, x, y, sheet, src); + return; + } + kon_drawImageEx(fb, sheet, src, (float)x + (float)frameWidth * 0.5f, (float)y + (float)frameHeight * 0.5f, + (float)frameWidth * 0.5f, (float)frameHeight * 0.5f, -1.0f, 1.0f, 0.0f, KON_FILTER_NEAREST); +} |
