Files
smack-a-brother/src/common.h
T
shweet 93cdf46d5a Room and lobby chat, /host, /fashion, player-colored lines, context tips, and American spelling
- Chat goes to your room (the sandbox, or your game) by default, and to the whole lobby with /all or a leading !.
  Lines from elsewhere say where they came from ([Sandbox], [Game 2]), lobby-wide ones from your room say [All].
  Typing shows only to your room. Protocol 8.
- Hosting: everyone is told who hosts now, and the host can hand it over with /host "player".
- /fashion <item> wears a fashion locally, like /color.
- Lines that mention a player show in that player's color, looked up as they draw.
- Tips are tagged with the item and situations they fit, and those that fit are 1.5x as likely. They turn over every
  10 seconds on one continuous run, show under the menu too, and can be turned off with Tips in HUD / Gameplay. The
  HUD hotbars hide while the menu is up (except on its item tabs).
- American spelling throughout: color, center, gray, mustache, and so on.
2026-11-10 19:37:19 -05:00

232 lines
5.4 KiB
C++

#pragma once
#include "dmath.h"
#include <cmath>
#include <cstdint>
#include <string>
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 <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;
}
};