Initial commit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shweetandClaude Opus 5.5 committed 2026-11-08 01:30:33 -05:00
commit 2007fb7edc
200 files changed
+27710

No files matched your search

+217
View File
@@ -0,0 +1,217 @@
#pragma once
#include "dmath.h"
#include <SDL3/SDL_filesystem.h>
#include <SDL3/SDL_stdinc.h>
#include <cmath>
#include <string>
constexpr const char* GAME_ID = "smack-a-brother";
constexpr const char* GAME_NAME = "Smack a Brother";
constexpr int CANVAS_HEIGHT = 360;
constexpr int CANVAS_WIDTH = 640;
constexpr int TILE_SIZE = 16;
constexpr float GRAVITY = 1000.0f;
constexpr float PI = 3.14159265f;
constexpr float TICK = 1.0f / 60.0f;
struct Vec2
{
float x = 0;
float y = 0;
};
struct Rect
{
float x = 0;
float y = 0;
float w = 0;
float h = 0;
};
struct Color
{
float r = 1;
float g = 1;
float b = 1;
float a = 1;
};
constexpr Color BLACK = {0, 0, 0, 1};
constexpr Color GRAY = {128 / 255.0f, 128 / 255.0f, 128 / 255.0f, 1};
constexpr Color WHITE = {1, 1, 1, 1};
inline Vec2 operator+(Vec2 a, Vec2 b)
{
return {a.x + b.x, a.y + b.y};
}
inline Vec2 operator-(Vec2 a, Vec2 b)
{
return {a.x - b.x, a.y - b.y};
}
inline Vec2 operator*(Vec2 v, float scale)
{
return {v.x * scale, v.y * scale};
}
inline float vec2_dot(Vec2 a, Vec2 b)
{
return a.x * b.x + a.y * b.y;
}
inline float vec2_length(Vec2 v)
{
return std::sqrt(v.x * v.x + v.y * v.y);
}
inline Vec2 vec2_normalize(Vec2 v)
{
float length = vec2_length(v);
return length > 0 ? v * (1 / length) : Vec2{1, 0};
}
inline float vec2_angle(Vec2 v)
{
return math_atan2(v.y, v.x);
}
inline Vec2 vec2_polar(float angle, float length)
{
return {math_cos(angle) * length, math_sin(angle) * length};
}
inline Vec2 vec2_lerp(Vec2 from, Vec2 to, float t)
{
return from + (to - from) * t;
}
// A 2D affine transform taking x to (a·x + c·y + x0, b·x + d·y + y0).
struct Affine
{
float a = 1;
float b = 0;
float c = 0;
float d = 1;
float x0 = 0;
float y0 = 0;
};
inline Affine operator*(Affine m, Affine n)
{
return {m.a * n.a + m.c * n.b, m.b * n.a + m.d * n.b, m.a * n.c + m.c * n.d,
m.b * n.c + m.d * n.d, m.a * n.x0 + m.c * n.y0 + m.x0, m.b * n.x0 + m.d * n.y0 + m.y0};
}
inline Vec2 operator*(Affine m, Vec2 v)
{
return {m.a * v.x + m.c * v.y + m.x0, m.b * v.x + m.d * v.y + m.y0};
}
// Moves to position, turns by angle and scales, about origin.
inline Affine affine_get(Vec2 position, float angle, Vec2 scale, Vec2 origin)
{
float cosine = math_cos(angle);
float sine = math_sin(angle);
Affine m = {cosine * scale.x, sine * scale.x, -sine * scale.y, cosine * scale.y, position.x, position.y};
return m * Affine{1, 0, 0, 1, -origin.x, -origin.y};
}
// Blends angles the short way round.
inline float angle_lerp(float from, float to, float t)
{
return from + std::remainder(to - from, 2 * PI) * t;
}
// Cosmetic randomness: particles, flicker, shake. Gameplay draws from Rng instead.
inline float random_get(float min, float max)
{
return min + SDL_randf() * (max - min);
}
constexpr int RNG_FRACTION_BITS = 24;
constexpr int RNG_SHIFT_A = 13;
constexpr int RNG_SHIFT_B = 17;
constexpr int RNG_SHIFT_C = 5;
constexpr Uint32 RNG_SEED = 0x9E3779B9;
// A small seeded xorshift generator for gameplay, saved with the simulation so a replay draws the same numbers.
struct Rng
{
Uint32 state = RNG_SEED;
float random_get(float min, float max)
{
state ^= state << RNG_SHIFT_A;
state ^= state >> RNG_SHIFT_B;
state ^= state << RNG_SHIFT_C;
float unit = (state >> (32 - RNG_FRACTION_BITS)) / (float)(1 << RNG_FRACTION_BITS);
return min + unit * (max - min);
}
};
inline bool is_point_in_rect(Vec2 point, Rect rect)
{
return point.x >= rect.x && point.y >= rect.y && point.x < rect.x + rect.w && point.y < rect.y + rect.h;
}
inline bool is_rect_overlapping(Rect a, Rect b)
{
return a.x < b.x + b.w && b.x < a.x + a.w && a.y < b.y + b.h && b.y < a.y + a.h;
}
// Darkens a colour by amount, keeping its alpha.
inline Color color_shade(Color color, float amount)
{
return {color.r * amount, color.g * amount, color.b * amount, color.a};
}
inline Color color_fade(Color color, float alpha)
{
return {color.r, color.g, color.b, color.a * alpha};
}
// A list of up to N items held in place, without the heap: game state kept in one, unlike in a std::vector, is plain
// bytes that mean the same on every platform (see Fighter). Pushing past N drops the item.
template <class T, int N> struct FixedList
{
T items[N] = {};
int count = 0;
void clear()
{
count = 0;
}
void push_back(const T& item)
{
if (count < N)
items[count++] = item;
}
T* begin()
{
return items;
}
T* end()
{
return items + count;
}
int size() const
{
return count;
}
template <class Predicate> void erase_if(Predicate predicate)
{
int kept = 0;
for (int i = 0; i < count; i++)
if (!predicate(items[i]))
items[kept++] = items[i];
count = kept;
}
};
// Absolute path to a file inside the resources folder next to the executable.
inline std::string path_get(const char* relative)
{
return std::string(SDL_GetBasePath()) + "resources/" + relative;
}