Basic gameplay loop, need to balance values
This commit is contained in:
@@ -15,6 +15,8 @@ add_executable(gamejam
|
|||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/Game.cpp
|
src/Game.cpp
|
||||||
src/Application.cpp
|
src/Application.cpp
|
||||||
|
src/Creature.cpp
|
||||||
|
src/Round.cpp
|
||||||
src/backend/RaylibSystemInterface.cpp
|
src/backend/RaylibSystemInterface.cpp
|
||||||
src/backend/RaylibRenderInterface.cpp
|
src/backend/RaylibRenderInterface.cpp
|
||||||
src/backend/RaylibInput.cpp
|
src/backend/RaylibInput.cpp
|
||||||
|
|||||||
@@ -6,6 +6,37 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body data-model="game_data">
|
<body data-model="game_data">
|
||||||
<h1>Hello from RmlUi!</h1>
|
|
||||||
|
<div id="hud" data-if="!round_over">
|
||||||
|
<p>Round {{round_number}} — Creature {{creature_number}} / {{creature_count}}</p>
|
||||||
|
<p>Species: {{creature_species}}</p>
|
||||||
|
<p>Marshmallows: {{marshmallows_remaining}}</p>
|
||||||
|
<p>Score: {{score}}</p>
|
||||||
|
<p class="timer" data-class-urgent="time_urgent">Time left: {{time_remaining_seconds}}s</p>
|
||||||
|
|
||||||
|
<p>------------------------------------------------------------------------------------------</p>
|
||||||
|
|
||||||
|
<!-- <p><strong>DEBUG</strong></p>
|
||||||
|
<p>Appearance tier: {{debug_appearance_tier}}</p>
|
||||||
|
<p>Is cryptid: {{debug_is_cryptid}}</p>
|
||||||
|
<p>Behaves normally: {{debug_behaves_normally}}</p>
|
||||||
|
|
||||||
|
<p>------------------------------------------------------------------------------------------</p> -->
|
||||||
|
|
||||||
|
<button class="menu-btn" data-event-click="feed_creature">Offer Marshmallow</button>
|
||||||
|
<button class="menu-btn" data-event-click="capture_creature">Capture</button>
|
||||||
|
<button class="menu-btn" data-event-click="release_creature">Carry On</button>
|
||||||
|
<button class="menu-btn" data-event-click="pause_game">Pause</button>
|
||||||
|
<p>{{feed_reaction_text}}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="round-end" data-if="round_over">
|
||||||
|
<h1>Round Over</h1>
|
||||||
|
<p>{{round_result_text}}</p>
|
||||||
|
<p>Score: {{score}}</p>
|
||||||
|
<button class="menu-btn" data-event-click="continue_next_round">Continue</button>
|
||||||
|
<button class="menu-btn" data-event-click="end_current_game">Return to Main Menu</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</rml>
|
</rml>
|
||||||
@@ -11,12 +11,14 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 24px;
|
font-size: 24dp;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 15dp;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15dp;
|
||||||
|
display: block;
|
||||||
|
font-size: 18dp;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
@@ -40,3 +42,8 @@ p {
|
|||||||
.menu-btn:active {
|
.menu-btn:active {
|
||||||
background-color: #2e4e70;
|
background-color: #2e4e70;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.timer.urgent {
|
||||||
|
color: #ff4444;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
@@ -24,8 +24,7 @@ bool Application::Init(int width, int height, const char *title) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rml::DataModelHandle game_data_model_handle;
|
if (!game.SetupRmlDataBinding()) {
|
||||||
if (!game.SetupRmlDataBinding(game_data_model_handle)) {
|
|
||||||
TraceLog(LOG_ERROR, "Failed to initialize data bindings for 'game_data'.");
|
TraceLog(LOG_ERROR, "Failed to initialize data bindings for 'game_data'.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,3 +18,31 @@ bool WouldEatMarshmallow(const Creature &c) {
|
|||||||
bool n = SpeciesEatsWhenNormal(c.species);
|
bool n = SpeciesEatsWhenNormal(c.species);
|
||||||
return c.behaves_normally ? n : !n;
|
return c.behaves_normally ? n : !n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *SpeciesName(Species species) {
|
||||||
|
switch (species) {
|
||||||
|
case Species::Raccoon:
|
||||||
|
return "Raccoon";
|
||||||
|
case Species::Opossum:
|
||||||
|
return "Opossum";
|
||||||
|
case Species::Fox:
|
||||||
|
return "Fox";
|
||||||
|
case Species::Deer:
|
||||||
|
return "Deer";
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *AppearanceTierName(AppearanceTier tier) {
|
||||||
|
switch (tier) {
|
||||||
|
case AppearanceTier::Standard:
|
||||||
|
return "Standard";
|
||||||
|
case AppearanceTier::Subtle1:
|
||||||
|
return "Subtle 1";
|
||||||
|
case AppearanceTier::Subtle2:
|
||||||
|
return "Subtle 2";
|
||||||
|
case AppearanceTier::Obvious:
|
||||||
|
return "Obvious";
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
@@ -18,3 +18,6 @@ struct Creature {
|
|||||||
|
|
||||||
bool SpeciesEatsWhenNormal(Species species);
|
bool SpeciesEatsWhenNormal(Species species);
|
||||||
bool WouldEatMarshmallow(const Creature &c);
|
bool WouldEatMarshmallow(const Creature &c);
|
||||||
|
|
||||||
|
const char *SpeciesName(Species species);
|
||||||
|
const char *AppearanceTierName(AppearanceTier tier);
|
||||||
276
src/Game.cpp
276
src/Game.cpp
@@ -1,6 +1,8 @@
|
|||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
|
#include "Creature.h"
|
||||||
#include <RmlUi/Core/DataModelHandle.h>
|
#include <RmlUi/Core/DataModelHandle.h>
|
||||||
#include <RmlUi/Core/Types.h>
|
#include <RmlUi/Core/Types.h>
|
||||||
|
#include <cmath>
|
||||||
#include <raylib.h>
|
#include <raylib.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -25,6 +27,20 @@ bool IsMenuState(GameState state) {
|
|||||||
return state == OPTIONS_MENU || state == STATS_MENU;
|
return state == OPTIONS_MENU || state == STATS_MENU;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Color ColorForTier(AppearanceTier tier) {
|
||||||
|
switch (tier) {
|
||||||
|
case AppearanceTier::Standard:
|
||||||
|
return GRAY;
|
||||||
|
case AppearanceTier::Subtle1:
|
||||||
|
return YELLOW;
|
||||||
|
case AppearanceTier::Subtle2:
|
||||||
|
return ORANGE;
|
||||||
|
case AppearanceTier::Obvious:
|
||||||
|
return RED;
|
||||||
|
}
|
||||||
|
return WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool Game::Init(Rml::Context *context) {
|
bool Game::Init(Rml::Context *context) {
|
||||||
@@ -34,18 +50,29 @@ bool Game::Init(Rml::Context *context) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Game::SetupRmlDataBinding(Rml::DataModelHandle &model_handle) {
|
bool Game::SetupRmlDataBinding() {
|
||||||
Rml::DataModelConstructor dmc = ctx->CreateDataModel("game_data");
|
Rml::DataModelConstructor dmc = ctx->CreateDataModel("game_data");
|
||||||
if (!dmc) {
|
if (!dmc) {
|
||||||
TraceLog(LOG_ERROR, "Failed to construct data model.");
|
TraceLog(LOG_ERROR, "Failed to construct data model.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- Member bindings ----------
|
|
||||||
|
|
||||||
dmc.Bind("game_state", &game_data.game_state);
|
dmc.Bind("game_state", &game_data.game_state);
|
||||||
|
dmc.Bind("marshmallows_remaining", &game_data.marshmallows_remaining);
|
||||||
// ---------- Event callback bindings ----------
|
dmc.Bind("creature_number", &game_data.creature_number);
|
||||||
|
dmc.Bind("creature_count", &game_data.creature_count);
|
||||||
|
dmc.Bind("creature_species", &game_data.creature_species);
|
||||||
|
dmc.Bind("debug_appearance_tier", &game_data.debug_appearance_tier);
|
||||||
|
dmc.Bind("debug_is_cryptid", &game_data.debug_is_cryptid);
|
||||||
|
dmc.Bind("debug_behaves_normally", &game_data.debug_behaves_normally);
|
||||||
|
dmc.Bind("has_fed_current", &game_data.has_fed_current);
|
||||||
|
dmc.Bind("feed_reaction_text", &game_data.feed_reaction_text);
|
||||||
|
dmc.Bind("score", &game_data.score);
|
||||||
|
dmc.Bind("round_number", &game_data.round_number);
|
||||||
|
dmc.Bind("round_over", &game_data.round_over);
|
||||||
|
dmc.Bind("round_result_text", &game_data.round_result_text);
|
||||||
|
dmc.Bind("time_remaining_seconds", &game_data.time_remaining_seconds);
|
||||||
|
dmc.Bind("time_urgent", &game_data.time_urgent);
|
||||||
|
|
||||||
dmc.BindEventCallback("start_game", [this](Rml::DataModelHandle, Rml::Event &,
|
dmc.BindEventCallback("start_game", [this](Rml::DataModelHandle, Rml::Event &,
|
||||||
const Rml::VariantList &) {
|
const Rml::VariantList &) {
|
||||||
@@ -79,7 +106,30 @@ bool Game::SetupRmlDataBinding(Rml::DataModelHandle &model_handle) {
|
|||||||
[this](Rml::DataModelHandle, Rml::Event &,
|
[this](Rml::DataModelHandle, Rml::Event &,
|
||||||
const Rml::VariantList &) { GoBack(); });
|
const Rml::VariantList &) { GoBack(); });
|
||||||
|
|
||||||
model_handle = dmc.GetModelHandle();
|
// --- gameplay actions ---
|
||||||
|
dmc.BindEventCallback("feed_creature",
|
||||||
|
[this](Rml::DataModelHandle, Rml::Event &,
|
||||||
|
const Rml::VariantList &) { FeedCreature(); });
|
||||||
|
|
||||||
|
dmc.BindEventCallback(
|
||||||
|
"capture_creature",
|
||||||
|
[this](Rml::DataModelHandle, Rml::Event &, const Rml::VariantList &) {
|
||||||
|
CaptureCreature();
|
||||||
|
});
|
||||||
|
|
||||||
|
dmc.BindEventCallback(
|
||||||
|
"release_creature",
|
||||||
|
[this](Rml::DataModelHandle, Rml::Event &, const Rml::VariantList &) {
|
||||||
|
ReleaseCreature();
|
||||||
|
});
|
||||||
|
|
||||||
|
dmc.BindEventCallback(
|
||||||
|
"continue_next_round",
|
||||||
|
[this](Rml::DataModelHandle, Rml::Event &, const Rml::VariantList &) {
|
||||||
|
ContinueToNextRound();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelHandle = dmc.GetModelHandle();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,14 +194,192 @@ void Game::GoBack() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Game::StartNewGame() {
|
void Game::StartNewGame() {
|
||||||
// TODO
|
roundIndex = 0;
|
||||||
|
game_data.score = 0;
|
||||||
|
|
||||||
// --- placeholder testing stuff ---
|
BeginRound();
|
||||||
playerPos = {GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::ResetGame() {
|
void Game::ResetGame() {
|
||||||
// TODO
|
roundIndex = 0;
|
||||||
|
game_data.score = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::BeginRound() {
|
||||||
|
RoundConfig cfg = MakeRoundConfig(roundIndex);
|
||||||
|
currentRound = GenerateRound(cfg);
|
||||||
|
|
||||||
|
game_data.round_number = roundIndex + 1;
|
||||||
|
game_data.round_over = false;
|
||||||
|
game_data.round_result_text = "";
|
||||||
|
|
||||||
|
creatureTimeLimit = TimeLimitForRound(roundIndex);
|
||||||
|
RefreshCreatureBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Game::TimeLimitForRound(int roundIndex) const {
|
||||||
|
return std::max(15.0f, 40.0f - roundIndex * 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::ResetCreatureTimer() {
|
||||||
|
creatureTimeRemaining = creatureTimeLimit;
|
||||||
|
game_data.time_remaining_seconds = (int)std::ceil(creatureTimeRemaining);
|
||||||
|
game_data.time_urgent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::UpdateCreatureTimer(float dt) {
|
||||||
|
if (game_data.round_over)
|
||||||
|
return;
|
||||||
|
|
||||||
|
creatureTimeRemaining -= dt;
|
||||||
|
|
||||||
|
int secondsLeft = (int)std::ceil(std::max(creatureTimeRemaining, 0.0f));
|
||||||
|
bool urgent = creatureTimeRemaining <= 2.0f;
|
||||||
|
|
||||||
|
if (secondsLeft != game_data.time_remaining_seconds ||
|
||||||
|
urgent != game_data.time_urgent) {
|
||||||
|
game_data.time_remaining_seconds = secondsLeft;
|
||||||
|
game_data.time_urgent = urgent;
|
||||||
|
if (modelHandle) {
|
||||||
|
modelHandle.DirtyVariable("time_remaining_seconds");
|
||||||
|
modelHandle.DirtyVariable("time_urgent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (creatureTimeRemaining <= 0.0f) {
|
||||||
|
ReleaseCreature();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::RefreshCreatureBindings() {
|
||||||
|
Creature *c = currentRound.CurrentCreature();
|
||||||
|
|
||||||
|
game_data.marshmallows_remaining = currentRound.marshmallowsRemaining;
|
||||||
|
game_data.creature_count = currentRound.conf.creature_count;
|
||||||
|
game_data.creature_number = currentRound.currentIndex + 1;
|
||||||
|
|
||||||
|
if (c) {
|
||||||
|
game_data.creature_species = SpeciesName(c->species);
|
||||||
|
game_data.debug_appearance_tier = AppearanceTierName(c->appearance);
|
||||||
|
game_data.debug_is_cryptid = c->is_cryptid;
|
||||||
|
game_data.debug_behaves_normally = c->behaves_normally;
|
||||||
|
game_data.has_fed_current = c->has_been_fed;
|
||||||
|
game_data.feed_reaction_text = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetCreatureTimer();
|
||||||
|
MarkDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::MarkDirty() {
|
||||||
|
if (!modelHandle)
|
||||||
|
return;
|
||||||
|
modelHandle.DirtyVariable("marshmallows_remaining");
|
||||||
|
modelHandle.DirtyVariable("creature_number");
|
||||||
|
modelHandle.DirtyVariable("creature_count");
|
||||||
|
modelHandle.DirtyVariable("creature_species");
|
||||||
|
modelHandle.DirtyVariable("debug_appearance_tier");
|
||||||
|
modelHandle.DirtyVariable("debug_is_cryptid");
|
||||||
|
modelHandle.DirtyVariable("debug_behaves_normally");
|
||||||
|
modelHandle.DirtyVariable("has_fed_current");
|
||||||
|
modelHandle.DirtyVariable("feed_reaction_text");
|
||||||
|
modelHandle.DirtyVariable("score");
|
||||||
|
modelHandle.DirtyVariable("round_number");
|
||||||
|
modelHandle.DirtyVariable("round_over");
|
||||||
|
modelHandle.DirtyVariable("round_result_text");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::FeedCreature() {
|
||||||
|
if (game_data.round_over)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!FeedCurrentCreature(currentRound))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Creature *c = currentRound.CurrentCreature();
|
||||||
|
if (c) {
|
||||||
|
bool ate = WouldEatMarshmallow(*c);
|
||||||
|
game_data.feed_reaction_text =
|
||||||
|
std::string(SpeciesName(c->species)) +
|
||||||
|
(ate ? " eats the marshmallow!" : " ignores it.");
|
||||||
|
game_data.has_fed_current = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
game_data.marshmallows_remaining = currentRound.marshmallowsRemaining;
|
||||||
|
MarkDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::CaptureCreature() {
|
||||||
|
if (game_data.round_over)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool died = ResolveCurrentCreature(currentRound, true);
|
||||||
|
if (currentRound.CurrentCreature() &&
|
||||||
|
!currentRound.CurrentCreature()->is_cryptid) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Creature &resolved = currentRound.creatures[currentRound.currentIndex - 1];
|
||||||
|
game_data.score += resolved.is_cryptid ? 1 : -2;
|
||||||
|
|
||||||
|
if (died) {
|
||||||
|
EndRound(true, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentRound.IsComplete()) {
|
||||||
|
EndRound(false, currentRound.Passed());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshCreatureBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::ReleaseCreature() {
|
||||||
|
if (game_data.round_over)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool died = ResolveCurrentCreature(currentRound, false);
|
||||||
|
|
||||||
|
if (died) {
|
||||||
|
EndRound(true, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentRound.IsComplete()) {
|
||||||
|
EndRound(false, currentRound.Passed());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshCreatureBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::EndRound(bool died, bool passed) {
|
||||||
|
game_data.round_over = true;
|
||||||
|
|
||||||
|
if (died) {
|
||||||
|
game_data.round_result_text = "A cryptid got away. You did not survive.";
|
||||||
|
} else if (passed) {
|
||||||
|
game_data.round_result_text =
|
||||||
|
"Round complete. Success rate: " +
|
||||||
|
std::to_string((int)(currentRound.SuccessRate() * 100.0f)) + "%";
|
||||||
|
} else {
|
||||||
|
game_data.round_result_text =
|
||||||
|
"Too many mistakes. Round failed. Success rate: " +
|
||||||
|
std::to_string((int)(currentRound.SuccessRate() * 100.0f)) + "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
MarkDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::ContinueToNextRound() {
|
||||||
|
if (!game_data.round_over)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (currentRound.Passed()) {
|
||||||
|
roundIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
BeginRound();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::Update() {
|
void Game::Update() {
|
||||||
@@ -159,32 +387,24 @@ void Game::Update() {
|
|||||||
|
|
||||||
if (CurrentState() != IN_GAME)
|
if (CurrentState() != IN_GAME)
|
||||||
return;
|
return;
|
||||||
// TODO
|
|
||||||
|
|
||||||
// --- placeholder testing stuff ---
|
|
||||||
float dt = GetFrameTime();
|
float dt = GetFrameTime();
|
||||||
|
UpdateCreatureTimer(dt);
|
||||||
if (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))
|
|
||||||
playerPos.y -= playerSpeed * dt;
|
|
||||||
if (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))
|
|
||||||
playerPos.y += playerSpeed * dt;
|
|
||||||
if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))
|
|
||||||
playerPos.x -= playerSpeed * dt;
|
|
||||||
if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))
|
|
||||||
playerPos.x += playerSpeed * dt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::Draw2D() {
|
void Game::Draw2D() {
|
||||||
if (CurrentState() != IN_GAME)
|
if (CurrentState() != IN_GAME)
|
||||||
return;
|
return;
|
||||||
// TODO
|
|
||||||
|
|
||||||
// --- placeholder testing stuff ---
|
Creature *c = currentRound.CurrentCreature();
|
||||||
// DrawText("Hello from raylib!", 50, 50, 20, RAYWHITE);
|
if (c && !game_data.round_over) {
|
||||||
if (CurrentState() != IN_GAME)
|
Color tierColor = ColorForTier(c->appearance);
|
||||||
return;
|
int size = 120;
|
||||||
DrawCircleV(playerPos, 24.0f, BLUE);
|
int x = GetScreenWidth() / 2 - size / 2;
|
||||||
DrawText("WASD/Arrows to move, ESC to pause", 20, 20, 20, RAYWHITE);
|
int y = GetScreenHeight() / 2 - size / 2;
|
||||||
|
DrawRectangle(x, y, size, size, tierColor);
|
||||||
|
DrawRectangleLines(x, y, size, size, BLACK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::HandleInput() {
|
void Game::HandleInput() {
|
||||||
|
|||||||
48
src/Game.h
48
src/Game.h
@@ -1,8 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Round.h"
|
||||||
#include <RmlUi/Core/Context.h>
|
#include <RmlUi/Core/Context.h>
|
||||||
#include <RmlUi/Core/DataModelHandle.h>
|
#include <RmlUi/Core/DataModelHandle.h>
|
||||||
#include <RmlUi/Core/ElementDocument.h>
|
#include <RmlUi/Core/ElementDocument.h>
|
||||||
|
#include <RmlUi/Core/Types.h>
|
||||||
#include <raylib.h>
|
#include <raylib.h>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@@ -10,6 +12,27 @@ enum GameState { START_MENU, PAUSE_MENU, STATS_MENU, OPTIONS_MENU, IN_GAME };
|
|||||||
|
|
||||||
struct GameData {
|
struct GameData {
|
||||||
int game_state = START_MENU;
|
int game_state = START_MENU;
|
||||||
|
|
||||||
|
int marshmallows_remaining = 0;
|
||||||
|
int creature_number = 0;
|
||||||
|
int creature_count = 0;
|
||||||
|
|
||||||
|
Rml::String creature_species;
|
||||||
|
Rml::String debug_appearance_tier;
|
||||||
|
bool debug_is_cryptid = false;
|
||||||
|
bool debug_behaves_normally = false;
|
||||||
|
|
||||||
|
bool has_fed_current = false;
|
||||||
|
Rml::String feed_reaction_text;
|
||||||
|
|
||||||
|
int time_remaining_seconds = 0;
|
||||||
|
bool time_urgent = false;
|
||||||
|
|
||||||
|
int score = 0;
|
||||||
|
int round_number = 1;
|
||||||
|
|
||||||
|
bool round_over = false;
|
||||||
|
Rml::String round_result_text;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Game {
|
class Game {
|
||||||
@@ -17,7 +40,7 @@ public:
|
|||||||
GameData game_data;
|
GameData game_data;
|
||||||
|
|
||||||
bool Init(Rml::Context *context);
|
bool Init(Rml::Context *context);
|
||||||
bool SetupRmlDataBinding(Rml::DataModelHandle &model_handle);
|
bool SetupRmlDataBinding();
|
||||||
|
|
||||||
void Update();
|
void Update();
|
||||||
void Draw2D();
|
void Draw2D();
|
||||||
@@ -32,18 +55,35 @@ public:
|
|||||||
void StartNewGame();
|
void StartNewGame();
|
||||||
void ResetGame();
|
void ResetGame();
|
||||||
|
|
||||||
|
void FeedCreature();
|
||||||
|
void CaptureCreature();
|
||||||
|
void ReleaseCreature();
|
||||||
|
void ContinueToNextRound();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Rml::ElementDocument *LoadOrGetDocument(GameState state);
|
Rml::ElementDocument *LoadOrGetDocument(GameState state);
|
||||||
void ShowDocumentForState(GameState state);
|
void ShowDocumentForState(GameState state);
|
||||||
void HandleInput();
|
void HandleInput();
|
||||||
|
|
||||||
|
void BeginRound();
|
||||||
|
void RefreshCreatureBindings();
|
||||||
|
void EndRound(bool died, bool passed);
|
||||||
|
void MarkDirty();
|
||||||
|
|
||||||
Rml::Context *ctx = nullptr;
|
Rml::Context *ctx = nullptr;
|
||||||
std::unordered_map<GameState, Rml::ElementDocument *> documents;
|
std::unordered_map<GameState, Rml::ElementDocument *> documents;
|
||||||
Rml::ElementDocument *activeDocument = nullptr;
|
Rml::ElementDocument *activeDocument = nullptr;
|
||||||
|
Rml::DataModelHandle modelHandle;
|
||||||
|
|
||||||
GameState previousState = START_MENU;
|
GameState previousState = START_MENU;
|
||||||
|
|
||||||
// --- placeholder testing stuff ---
|
Round currentRound;
|
||||||
Vector2 playerPos{};
|
int roundIndex = 0;
|
||||||
float playerSpeed = 300.0f;
|
|
||||||
|
void UpdateCreatureTimer(float dt);
|
||||||
|
void ResetCreatureTimer();
|
||||||
|
float TimeLimitForRound(int roundIndex) const;
|
||||||
|
|
||||||
|
float creatureTimeLimit = 40.0f;
|
||||||
|
float creatureTimeRemaining = 0.0f;
|
||||||
};
|
};
|
||||||
@@ -35,11 +35,17 @@ float IncorrectBehaviourChance(AppearanceTier tier) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Species RandomSpecies() { return static_cast<Species>(GetRandomValue(0, 3)); }
|
Species RandomSpecies() { return static_cast<Species>(GetRandomValue(0, 3)); }
|
||||||
|
|
||||||
|
int ComputeCryptidCount(int creatureCount, float cryptidRatio) {
|
||||||
|
return std::clamp((int)std::round(creatureCount * cryptidRatio), 1,
|
||||||
|
creatureCount - 1);
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
RoundConfig MakeRoundConfig(int roundIndex) {
|
RoundConfig MakeRoundConfig(int roundIndex) {
|
||||||
RoundConfig cfg;
|
RoundConfig cfg;
|
||||||
cfg.creature_count = std::min(5 + roundIndex * 2, 20);
|
cfg.creature_count = std::min(5 + roundIndex * 2, 20);
|
||||||
|
cfg.cryptid_ratio = 0.35f;
|
||||||
|
|
||||||
float rampedStandard = std::min(0.35f + roundIndex * 0.03f, 0.70f);
|
float rampedStandard = std::min(0.35f + roundIndex * 0.03f, 0.70f);
|
||||||
float remaining = 1.0f - rampedStandard;
|
float remaining = 1.0f - rampedStandard;
|
||||||
@@ -49,11 +55,8 @@ RoundConfig MakeRoundConfig(int roundIndex) {
|
|||||||
cfg.w_subtle2 = remaining * (0.20f / baseRemaining);
|
cfg.w_subtle2 = remaining * (0.20f / baseRemaining);
|
||||||
cfg.w_obvious = remaining * (0.15f / baseRemaining);
|
cfg.w_obvious = remaining * (0.15f / baseRemaining);
|
||||||
|
|
||||||
cfg.cryptid_ratio = 0.35f;
|
int cryptidCount = ComputeCryptidCount(cfg.creature_count, cfg.cryptid_ratio);
|
||||||
float ambiguousFraction =
|
cfg.marshmallow_supply = cryptidCount;
|
||||||
(1.0f - cfg.cryptid_ratio) + cfg.cryptid_ratio * cfg.w_standard;
|
|
||||||
float ambiguousCount = cfg.creature_count * ambiguousFraction;
|
|
||||||
cfg.marshmallow_supply = std::max(2, (int)std::round(ambiguousCount * 0.85f));
|
|
||||||
|
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
@@ -64,8 +67,7 @@ Round GenerateRound(const RoundConfig &config) {
|
|||||||
round.marshmallowsRemaining = config.marshmallow_supply;
|
round.marshmallowsRemaining = config.marshmallow_supply;
|
||||||
|
|
||||||
int cryptidCount =
|
int cryptidCount =
|
||||||
std::clamp((int)std::round(config.creature_count * config.cryptid_ratio),
|
ComputeCryptidCount(config.creature_count, config.cryptid_ratio);
|
||||||
1, config.creature_count - 1);
|
|
||||||
|
|
||||||
for (int i = 0; i < config.creature_count; ++i) {
|
for (int i = 0; i < config.creature_count; ++i) {
|
||||||
Creature c;
|
Creature c;
|
||||||
@@ -8,10 +8,10 @@ struct RoundConfig {
|
|||||||
float cryptid_ratio = 0.35f;
|
float cryptid_ratio = 0.35f;
|
||||||
|
|
||||||
// Appearance weights
|
// Appearance weights
|
||||||
float w_standard = 0.35f;
|
float w_standard = 0.30f;
|
||||||
float w_subtle1 = 0.30f;
|
float w_subtle1 = 0.35f;
|
||||||
float w_subtle2 = 0.20f;
|
float w_subtle2 = 0.30f;
|
||||||
float w_obvious = 0.15f;
|
float w_obvious = 0.10f;
|
||||||
};
|
};
|
||||||
|
|
||||||
RoundConfig MakeRoundConfig(int roundIndex);
|
RoundConfig MakeRoundConfig(int roundIndex);
|
||||||
|
|||||||
Reference in New Issue
Block a user