#pragma once #include "dmath.h" #include #include #include using Uint8 = std::uint8_t; using Uint16 = std::uint16_t; using Uint32 = std::uint32_t; using Uint64 = std::uint64_t; 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}; constexpr Color CLEAR = {0, 0, 0, 0}; 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; } 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; // Describes a spray of square particles, fanned around angle by spread. Fill is how far their color takes over their // black middle. struct Burst { float angle = 0; float fill = 0; float gravity = 0; float life = 0.4f; float size = 3; float speed = 100; float spread = PI; int count = 8; }; // 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 color 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 color amount of the way from one to another. inline Color color_mix(Color from, Color to, float amount) { return {from.r + (to.r - from.r) * amount, from.g + (to.g - from.g) * amount, from.b + (to.b - from.b) * amount, from.a + (to.a - from.a) * amount}; } // 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 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 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; } };