/* SPDX-License-Identifier: MIT */ /* * konworld.h * * 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. */ #ifndef KONWORLD_H #define KONWORLD_H #include "konmath.h" #include "kontile.h" #include "kon3d.h" #include "konsector.h" #ifdef __cplusplus extern "C" { #endif /*** definition ***/ /* one place to ask "can I stand here, can I walk there" for everything a 3d game is built from: the rooms of a sector map, a terrain, a tilemap, and any shapes you add yourself. x and z are the plan, y is up. a world only points at the maps and the terrain, they stay yours and can change between frames. nothing here knows what a door or a monster is, a door is a box that the game moves */ #define KON_NO_GROUND (-1.0e30f) typedef enum kon_colliderType { KON_COLLIDER_NONE = 0, KON_COLLIDER_BOX, /* min and max, sides line up with the axes */ KON_COLLIDER_CYLINDER, /* standing on the ground, center is the middle of its bottom */ KON_COLLIDER_SPHERE, /* blocks only, you can't stand on one */ KON_COLLIDER_PLANE /* everything behind the plane is solid. a plane that faces mostly up is a floor, a ramp if it is tilted */ } kon_colliderType_t; typedef struct kon_collider { int type; int enabled; /* 0 and it is ignored, 1 */ int user; /* yours, like the entity this belongs to */ kon_vec3_t min, max; /* box */ kon_vec3_t center; /* cylinder, sphere */ float radius, height; /* cylinder, sphere (no height) */ kon_vec3_t normal; /* plane, the side it faces, length 1. points p with dot(normal, p) < offset are solid */ float offset; } kon_collider_t; kon_collider_t kon_colliderBox(kon_vec3_t min, kon_vec3_t max); kon_collider_t kon_colliderCylinder(kon_vec3_t bottom, float radius, float height); kon_collider_t kon_colliderSphere(kon_vec3_t center, float radius); kon_collider_t kon_colliderPlane(kon_vec3_t point, kon_vec3_t normal); typedef struct kon_world { const kon_sectorMap_t *sectors; /* any of these three can be NULL */ const kon_terrain_t *terrain; const kon_tilemap_t *tiles; /* one unit per tile like the rest of kon3d */ const uint8_t *solid; /* indexed by tile id like kon_tilemapMove */ int solidCount; float tileHeight; /* how tall the solid tiles are, 1 */ kon_collider_t *colliders; int colliderCount, colliderCap; float gravity; /* 20 */ float stepHeight; /* the biggest step you walk up without jumping, 0.35 */ float maxSlope; /* rise over run you can climb on terrain, 1 is 45 degrees */ } kon_world_t; /* a thing that walks. position is where the feet are, the body is a cylinder going up from there */ typedef struct kon_body { kon_vec3_t position; float radius, height; /* 0.2 and 1.0 are fine for a person in this scale */ float vy; /* up speed, only jumps and falls change it */ float jumpSpeed; /* how fast a jump starts, 6 is about a metre. 0 turns jumping off for this body, like in a doom-style game */ int onGround; int sector; /* the room it is in, -1 if none, kept up to date by kon_worldMove */ int touching; /* the collider it was pushed by in the last move, -1 if none. a monster hitting a door, say */ } kon_body_t; /*** the world ***/ void kon_worldInit(kon_world_t *world); void kon_worldFree(kon_world_t *world); /* returns the index, or -1 if there is no memory. the index stays the same until the collider is removed */ int kon_worldAdd(kon_world_t *world, kon_collider_t collider); /* NULL if the index is not a collider. change it any time, a door opens by raising min.y */ kon_collider_t *kon_worldCollider(kon_world_t *world, int index); void kon_worldRemove(kon_world_t *world, int index); /* the height of the highest thing to stand on at x, z that is not more than a step above feet, or KON_NO_GROUND outside the world. hint is the sector you were in, -1 if you have none. sector gets the room the point is in, it can be NULL */ float kon_worldGround(const kon_world_t *world, float x, float z, float feet, int hint, int *sector); /*** bodies ***/ void kon_bodyInit(kon_body_t *body, float x, float y, float z, float radius, float height); /* does nothing in the air or when jumpSpeed is 0. returns 1 if it jumped */ int kon_bodyJump(kon_body_t *body); /* puts the feet on the ground under them right away */ void kon_worldPlace(const kon_world_t *world, kon_body_t *body); /* moves by dx, dz and slides along whatever is in the way: walls, shapes, steps that are too high, slopes that are too steep, the edge of the world. then gravity: steps up are taken at once and drops are falls. dt is seconds */ void kon_worldMove(kon_world_t *world, kon_body_t *body, float dx, float dz, double dt); /*** rays ***/ typedef enum kon_hitKind { KON_HIT_NONE = 0, KON_HIT_WALL, /* a wall of a sector, or the step or lintel of an opening */ KON_HIT_FLOOR, KON_HIT_CEILING, KON_HIT_COLLIDER, KON_HIT_TERRAIN, KON_HIT_TILE } kon_hitKind_t; typedef struct kon_rayHit { int kind; float distance; kon_vec3_t point, normal; /* normal faces back at the ray */ int sector; /* the room a wall, floor or ceiling belongs to, -1 for the rest */ int edge; /* which wall of that room, -1 for the rest */ int collider; /* the index, -1 for the rest */ } kon_rayHit_t; /* shoots a ray and finds the first thing it hits within maxDistance. direction does not have to be 1 long. hit can be NULL. a ray through the rooms of a sector map has to start inside one. shapes the ray starts inside are not hit */ int kon_worldRay(const kon_world_t *world, kon_vec3_t origin, kon_vec3_t direction, float maxDistance, kon_rayHit_t *hit); /* 1 if nothing is between the two points. for "can the monster see me" and "is the light reaching here" */ int kon_worldVisible(const kon_world_t *world, kon_vec3_t from, kon_vec3_t to); #ifdef __cplusplus } #endif #endif