Sim holds one whole game (tick, snapshots, checksum) and talks to the screen only through Cues, which the client's Scene plays and the server ignores. All drawing moved to scene.cpp. Match takes this screen's wishes as request flags instead of reading the menu, input and settings. The sim library builds without SDL and the server links it, ready to run games itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
174 lines
6.6 KiB
C++
174 lines
6.6 KiB
C++
#pragma once
|
|
|
|
#include "input.h"
|
|
#include "trinket.h"
|
|
#include "world.h"
|
|
|
|
constexpr float CRAWL_STRIDE = 5;
|
|
constexpr float ROLL_RADIUS = 10;
|
|
constexpr float STEP_SPEED = 40;
|
|
constexpr float WALK_STRIDE = 7;
|
|
|
|
struct Attack;
|
|
struct Sim;
|
|
|
|
constexpr int ECHO_MAX = 64;
|
|
constexpr int MAX_FIGHTERS = SPAWN_COUNT;
|
|
constexpr float BOUNCE_KEEP = 0.75f;
|
|
constexpr float SECONDARY_SHADE = 0.5f;
|
|
constexpr int COLOR_COUNT = 16;
|
|
constexpr int COLOR_UNASSIGNED = -1;
|
|
// The colours fighters can wear, in the order fighters without one are given them.
|
|
constexpr Color PALETTE[COLOR_COUNT] = {
|
|
{1, 0.35f, 0.35f, 1}, {0.4f, 0.6f, 1, 1}, {1, 0.9f, 0.3f, 1}, {0.4f, 1, 0.45f, 1},
|
|
{1, 0.6f, 0.2f, 1}, {0.7f, 0.45f, 1, 1}, {0.3f, 0.95f, 1, 1}, {1, 0.5f, 0.8f, 1},
|
|
{0.75f, 1, 0.3f, 1}, {0.2f, 0.75f, 0.7f, 1}, {1, 0.3f, 1, 1}, {0.6f, 0.85f, 1, 1},
|
|
{1, 0.78f, 0.6f, 1}, {0.8f, 0.75f, 1, 1}, {0.75f, 0.5f, 0.3f, 1}, {0.75f, 0.78f, 0.85f, 1},
|
|
};
|
|
constexpr const char* COLOR_NAMES[COLOR_COUNT] = {"Red", "Blue", "Yellow", "Green", "Orange", "Purple",
|
|
"Cyan", "Pink", "Lime", "Teal", "Magenta", "Sky",
|
|
"Peach", "Lavender", "Brown", "Silver"};
|
|
constexpr float BOUNCE_MIN = 120;
|
|
constexpr float BOX_HEIGHT = 24;
|
|
constexpr float BOX_WIDTH = 12;
|
|
constexpr float JUMP_SPEED = 400;
|
|
constexpr float KNOCK_DECAY = 1.2f;
|
|
constexpr float LAUNCH_END_SPEED = 200;
|
|
constexpr float RUN_SPEED = 150;
|
|
constexpr float RACE_KNOCK = 0.5f;
|
|
constexpr float SLAM_SPEED = 400;
|
|
constexpr float CHARGE_TIME = 1.0f;
|
|
constexpr float CHARGE_VISIBLE = 0.15f;
|
|
// Shock Absorber takes off this share of what it takes off weapon movement from knockback.
|
|
constexpr float ABSORB_KNOCK_SHARE = 2 / 3.0f;
|
|
|
|
// A platforming fighter driven by Controls: movement, aiming, the shared weapon charge, and everything it carries.
|
|
// Loadouts hold weapon and trinket numbers (-1 for empty), with only their first slots counts in use (the rest stay
|
|
// empty). The same item can sit in several slots, each copy with its own level (slot levels); item levels are what a
|
|
// copy gets when equipped, and what an unequipped item is described at. Copies always stack: weapon copies are separate
|
|
// weapons with their own cooldowns (Dual Wield can hold two of one), and trinket copies add up. Cooldowns (per weapon
|
|
// key, see weapon_key), Doubler echoes and the kick animation belong to its weapons. Index is its place in the game's
|
|
// fighter list, which its attacks record. Hitstop freezes it (and its attacks) after landing or taking a heavy hit. A
|
|
// big hit launches it: it flies, bouncing off whatever it hits, tumbling, with no say in where it goes until it slows
|
|
// down. Crouching on the ground lowers it, slows it to a waddle and halves the knockback it takes. Spinning a flail
|
|
// lightens its gravity. Hard bounces hurt, credited to the launcher, whoever landed the last hit. In a race, hits only
|
|
// knock it about (half as hard), and touching a pit puts it back on the last ground it stood squarely on. Slopes slow
|
|
// it climbing and speed it up descending. Crouching on one slides it down, rolling, and the slide carries on over flat
|
|
// ground until it slows.
|
|
class Fighter
|
|
{
|
|
friend class Scene;
|
|
|
|
public:
|
|
Body body;
|
|
Controls controls;
|
|
Vec2 aim = {1, 0};
|
|
FixedList<Echo, ECHO_MAX> echoes;
|
|
float charge = 0;
|
|
float cooldowns[SLOT_MAX] = {};
|
|
float flash = 0;
|
|
float health = 0;
|
|
float hitstop = 0;
|
|
float kickFrom = 0;
|
|
float kickTime = 1;
|
|
float kickTo = 0;
|
|
float scale = 1;
|
|
float shockCooldown = 0;
|
|
float contactCooldown = 0;
|
|
int index = 0;
|
|
int launcher = 0;
|
|
int offSlot = -1;
|
|
int slot = 0;
|
|
int trinketItemLevels[TRINKET_COUNT] = {};
|
|
int trinketSlotLevels[SLOT_MAX] = {};
|
|
int trinketLoadout[SLOT_MAX];
|
|
int trinketSlots = SLOTS_DEFAULT;
|
|
int weaponItemLevels[WEAPON_COUNT] = {};
|
|
int weaponSlotLevels[SLOT_MAX] = {};
|
|
int weaponLoadout[SLOT_MAX];
|
|
int weaponSlots = SLOTS_DEFAULT;
|
|
bool isAlive = true;
|
|
bool isBot = false;
|
|
bool isCrouching = false;
|
|
bool isSpinning = false;
|
|
bool isLaunched = false;
|
|
// In the sandbox, whether it fights other fighters (both need it on); without it, it's drawn darkened.
|
|
bool isPvp = false;
|
|
bool isSliding = false;
|
|
|
|
void init(Sim& sim, int number);
|
|
void state_reset(Sim& sim, float maxHealth);
|
|
void position_reset(Sim& sim);
|
|
void position_set(Sim& sim, Vec2 feet);
|
|
void slots_set(int weapons, int trinkets);
|
|
void damage_take(Sim& sim, Damage damage);
|
|
void update(Sim& sim);
|
|
void blend(float alpha);
|
|
void impulse_add(Vec2 impulse, bool isDamped = true);
|
|
void teleport_to(Vec2 centre);
|
|
void burst_add(Vec2 impulse);
|
|
void charge_cancel(Sim& sim);
|
|
int weapon_get();
|
|
int weapon_key(int weaponSlot);
|
|
int off_slot_get();
|
|
int off_weapon_get();
|
|
int slot_level_get(int weaponSlot);
|
|
const LevelDef& level_get(int weaponSlot);
|
|
void item_set(bool isTrinket, int itemSlot, int item);
|
|
void levels_fill(int weaponLevel, int trinketLevel);
|
|
float trinket_get(TrinketId trinket, float neutral);
|
|
float trinket_best_get(TrinketId trinket, float neutral);
|
|
float trinket_sum_get(int trinket, const float* table);
|
|
int trinket_level_get(int trinket);
|
|
int trinket_count(int trinket);
|
|
float trinket_cooldown_get(int trinket);
|
|
float knock_absorb_get();
|
|
float pound_area_get();
|
|
Vec2 center_get();
|
|
Vec2 hand_get();
|
|
Vec2 offhand_get();
|
|
bool is_any_pressed();
|
|
bool is_down(Control control);
|
|
bool is_pressed(Control control);
|
|
bool is_released(Control control);
|
|
bool is_trinket_equipped(int trinket);
|
|
int wall_get(Sim& sim);
|
|
bool is_crawling();
|
|
bool is_left();
|
|
|
|
private:
|
|
Vec2 cornerPrevious;
|
|
Vec2 safe;
|
|
float coyoteTimer = 0;
|
|
float dashCooldown = 0;
|
|
float dashTimer = 0;
|
|
float jumpBuffer = 0;
|
|
float kickTimer = 0;
|
|
float kickPose = 0;
|
|
float slideTimer = 0;
|
|
float stepDistance = 0;
|
|
float wallMemory = 0;
|
|
float roll = 0;
|
|
float rollSpin = 0;
|
|
float tumble = 0;
|
|
float wallSpeed = 0;
|
|
int slideWall = 0;
|
|
Uint16 buttonsPrevious = 0;
|
|
bool isCharging = false;
|
|
bool isFireBlocked = false;
|
|
bool isFacingLeft = false;
|
|
bool isJumping = false;
|
|
|
|
void slot_update(Sim& sim);
|
|
void safe_update(Sim& sim);
|
|
void size_update();
|
|
void dash_update(Sim& sim, bool isJumpPressed);
|
|
void climb_update(Sim& sim);
|
|
Vec2 wall_point_get(int wall);
|
|
void wall_push(Sim& sim, int wall, Vec2 base);
|
|
void bounce_resolve(Sim& sim, Vec2 before);
|
|
void run_update(Attack* hook);
|
|
void rope_update(Attack& hook);
|
|
void charge_update(Sim& sim);
|
|
};
|