Compare commits
15 Commits
b5568d5493
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b2edd1359 | |||
|
|
1f1ac0db4d | ||
|
|
c2f0188174 | ||
|
|
b76604bacf | ||
|
|
85663e3c9c | ||
|
|
a81a549fa1 | ||
|
|
1745899487 | ||
|
|
d028224fd0 | ||
|
|
0f4ba92de5 | ||
|
|
2d2faebd10 | ||
|
|
aa70543411 | ||
|
|
228c87d0ee | ||
|
|
20bc2837e6 | ||
|
|
1b67f1c14b | ||
|
|
c30a9e3520 |
41
.github/workflows/build.yml
vendored
Normal file
41
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Game
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Project
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
- name: "Install dependencies"
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y \
|
||||
gnome-desktop-testing libasound2-dev libpulse-dev libaudio-dev libjack-dev libsndio-dev \
|
||||
libusb-1.0-0-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev \
|
||||
libxss-dev libxtst-dev libwayland-dev libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev \
|
||||
libgles2-mesa-dev libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev fcitx-libs-dev
|
||||
|
||||
- name: CMake Build
|
||||
run: |
|
||||
mkdir build
|
||||
cmake -S . -B ./build -DSDL_UNIX_CONSOLE_BUILD=ON
|
||||
make -C build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: linux-build
|
||||
path: build/snivy
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -16,3 +16,6 @@
|
||||
[submodule "external/libanm2"]
|
||||
path = external/libanm2
|
||||
url = https://github.com/shweetsstuff/libanm2
|
||||
[submodule "external/physfs"]
|
||||
path = external/physfs
|
||||
url = https://github.com/icculus/physfs
|
||||
|
||||
1
external/physfs
vendored
Submodule
1
external/physfs
vendored
Submodule
Submodule external/physfs added at d70c3fcf06
@@ -8,5 +8,6 @@ namespace game
|
||||
{
|
||||
public:
|
||||
MeasurementSystem measurementSystem{MeasurementSystem::METRIC};
|
||||
int volume{100};
|
||||
};
|
||||
}
|
||||
@@ -12,6 +12,12 @@ namespace game::resource
|
||||
return mixer;
|
||||
}
|
||||
|
||||
void Audio::set_gain(float gain)
|
||||
{
|
||||
auto mixer = mixer_get();
|
||||
MIX_SetMasterGain(mixer, gain);
|
||||
}
|
||||
|
||||
void Audio::retain()
|
||||
{
|
||||
if (refCount) ++(*refCount);
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace game::resource
|
||||
MIX_Audio* internal{nullptr};
|
||||
MIX_Track* track{nullptr};
|
||||
int* refCount{nullptr};
|
||||
MIX_Mixer* mixer_get();
|
||||
static MIX_Mixer* mixer_get();
|
||||
void unload();
|
||||
void retain();
|
||||
void release();
|
||||
@@ -27,5 +27,6 @@ namespace game::resource
|
||||
void play(bool isLoop = false);
|
||||
void stop();
|
||||
bool is_playing() const;
|
||||
static void set_gain(float vol);
|
||||
};
|
||||
}
|
||||
@@ -21,4 +21,8 @@ namespace game
|
||||
}
|
||||
|
||||
void Resources::sound_play(audio::Type type) { audio[type].play(); }
|
||||
|
||||
void Resources::set_audio_gain(float vol) {
|
||||
Audio::set_gain(vol);
|
||||
}
|
||||
}
|
||||
@@ -124,5 +124,6 @@ namespace game
|
||||
|
||||
Resources();
|
||||
void sound_play(audio::Type);
|
||||
void set_audio_gain(float vol);
|
||||
};
|
||||
}
|
||||
@@ -123,8 +123,7 @@ namespace game
|
||||
((Item::SPAWN_Y_MAX - Item::SPAWN_Y_MIN) * math::random()) + Item::SPAWN_Y_MIN);
|
||||
items.emplace_back(&resources.anm2s[anm2::ITEMS], position, type);
|
||||
|
||||
inventory.values[type]--;
|
||||
if (inventory.values[type] == 0) inventory.values.erase(type);
|
||||
inventory.adjust_item(type, -1);
|
||||
type = Item::NONE;
|
||||
inventory.isQueued = false;
|
||||
}
|
||||
@@ -159,7 +158,7 @@ namespace game
|
||||
{
|
||||
if (Item::queuedReturnItem->state == Item::DEFAULT)
|
||||
{
|
||||
inventory.values[item.type]++;
|
||||
inventory.adjust_item(item.type);
|
||||
resources.sound_play(audio::RETURN);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -9,6 +9,31 @@ using namespace glm;
|
||||
|
||||
namespace game::window
|
||||
{
|
||||
Inventory::Inventory()
|
||||
{
|
||||
count = 0;
|
||||
|
||||
for (auto& [type, quantity] : values)
|
||||
{
|
||||
count += quantity;
|
||||
}
|
||||
}
|
||||
|
||||
void Inventory::adjust_item(Item::Type type, int quantity)
|
||||
{
|
||||
values[type] += quantity;
|
||||
count += quantity;
|
||||
}
|
||||
|
||||
void Inventory::set_item(Item::Type type, int value)
|
||||
{
|
||||
count -= values[type];
|
||||
values[type] = value;
|
||||
count += value;
|
||||
}
|
||||
|
||||
int Inventory::get_item(Item::Type type) { return values.contains(type) ? values[type] : 0; }
|
||||
|
||||
void Inventory::update(Resources& resources, Character& character, GameData& gameData)
|
||||
{
|
||||
auto& texture = resources.anm2s[anm2::ITEMS].content.spritesheets.at(0).texture;
|
||||
@@ -18,9 +43,13 @@ namespace game::window
|
||||
auto cursorPos = ImGui::GetCursorPos();
|
||||
auto cursorStartX = ImGui::GetCursorPosX();
|
||||
|
||||
for (auto& [type, quantity] : values)
|
||||
for (int index = 0; index < Item::ITEM_COUNT; index++)
|
||||
{
|
||||
if (quantity == 0) continue;
|
||||
auto type = (Item::Type)index;
|
||||
auto seen = values.contains(type);
|
||||
|
||||
//Hide invalid items and impossible-to-obtain items when not seen.
|
||||
if (!seen && (Item::CATEGORIES[type] == Item::INVALID || Item::RARITIES[type] == Item::IMPOSSIBLE)) continue;
|
||||
|
||||
auto columns = (int)(texture.size.x / ITEM_SIZE.x);
|
||||
auto crop = vec2(type % columns, type / columns) * ITEM_SIZE;
|
||||
@@ -30,13 +59,83 @@ namespace game::window
|
||||
ImGui::PushID(type);
|
||||
ImGui::SetCursorPos(cursorPos);
|
||||
auto cursorScreenPos = ImGui::GetCursorScreenPos();
|
||||
if (ImGui::ImageButton("##Image Button", texture.id, IMAGE_SIZE, imgui::to_imvec2(uvMin),
|
||||
imgui::to_imvec2(uvMax)))
|
||||
{
|
||||
queuedItemType = type;
|
||||
isQueued = true;
|
||||
|
||||
resources.sound_play(audio::SUMMON);
|
||||
auto quantity = 0;
|
||||
|
||||
if (seen)
|
||||
{
|
||||
quantity = values[type];
|
||||
|
||||
ImGui::BeginDisabled(quantity < 1);
|
||||
if (ImGui::ImageButton("##Image Button", texture.id, IMAGE_SIZE, imgui::to_imvec2(uvMin),
|
||||
imgui::to_imvec2(uvMax)) &&
|
||||
quantity > 0)
|
||||
{
|
||||
queuedItemType = type;
|
||||
isQueued = true;
|
||||
|
||||
resources.sound_play(audio::SUMMON);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
auto& category = Item::CATEGORIES[type];
|
||||
auto& rarity = Item::RARITIES[type];
|
||||
auto& flavor = Item::FLAVORS[type];
|
||||
auto& calories = Item::CALORIES[type];
|
||||
auto& digestionRateBonus = Item::DIGESTION_RATE_BONUSES[type];
|
||||
auto& eatSpeedBonus = Item::EAT_SPEED_BONUSES[type];
|
||||
|
||||
ImGui::Text("%s (x%i)", Item::NAMES[type], quantity);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(GRAY)));
|
||||
ImGui::Text("-- %s (%s) --", Item::CATEGORY_NAMES[category], Item::RARITY_NAMES[rarity]);
|
||||
if (category == Item::FOOD)
|
||||
{
|
||||
ImGui::Separator();
|
||||
if (flavor != Item::FLAVORLESS) ImGui::Text("Flavor: %s", Item::FLAVOR_NAMES[flavor]);
|
||||
if (calories != 0) ImGui::Text("%0.0f kcal", calories);
|
||||
if (digestionRateBonus > 0)
|
||||
ImGui::Text("Digestion Rate Bonus: +%0.2f%% / sec", digestionRateBonus * 60.0f);
|
||||
else if (digestionRateBonus < 0)
|
||||
ImGui::Text("Digestion Rate Penalty: %0.2f%% / sec", digestionRateBonus * 60.0f);
|
||||
if (eatSpeedBonus > 0) ImGui::Text("Eat Speed Bonus: +%0.2fx ", eatSpeedBonus);
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::TextUnformatted(Item::DESCRIPTIONS[type]);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
ImGui::PushFont(resources.font.get(), Font::BIG);
|
||||
auto text = std::format("x{}", quantity);
|
||||
auto textPos = ImVec2(cursorScreenPos.x + IMAGE_SIZE.x - ImGui::CalcTextSize(text.c_str()).x,
|
||||
cursorScreenPos.y + IMAGE_SIZE.y - ImGui::GetTextLineHeight());
|
||||
ImGui::GetWindowDrawList()->AddText(textPos, ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Text)),
|
||||
text.c_str());
|
||||
ImGui::PopFont();
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::BeginDisabled(true);
|
||||
ImGui::ImageButton("##Image Button", texture.id, IMAGE_SIZE, imgui::to_imvec2(uvMin), imgui::to_imvec2(uvMax),
|
||||
ImVec4(0, 0, 0, 0), ImVec4(0, 0, 0, 1));
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
ImGui::TextUnformatted("??? (x0)");
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(GRAY)));
|
||||
ImGui::TextUnformatted("-- ??? (\?\?\?) --");
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::TextUnformatted("???");
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
auto increment = ImGui::GetItemRectSize().x + ImGui::GetStyle().ItemSpacing.x;
|
||||
@@ -48,47 +147,10 @@ namespace game::window
|
||||
cursorPos.y += increment;
|
||||
}
|
||||
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
auto& category = Item::CATEGORIES[type];
|
||||
auto& rarity = Item::RARITIES[type];
|
||||
auto& flavor = Item::FLAVORS[type];
|
||||
auto& calories = Item::CALORIES[type];
|
||||
auto& digestionRateBonus = Item::DIGESTION_RATE_BONUSES[type];
|
||||
auto& eatSpeedBonus = Item::EAT_SPEED_BONUSES[type];
|
||||
|
||||
ImGui::Text("%s (x%i)", Item::NAMES[type], quantity);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(GRAY)));
|
||||
ImGui::Text("-- %s (%s) --", Item::CATEGORY_NAMES[category], Item::RARITY_NAMES[rarity]);
|
||||
if (category == Item::FOOD)
|
||||
{
|
||||
ImGui::Separator();
|
||||
if (flavor != Item::FLAVORLESS) ImGui::Text("Flavor: %s", Item::FLAVOR_NAMES[flavor]);
|
||||
if (calories != 0) ImGui::Text("%0.0f kcal", calories);
|
||||
if (digestionRateBonus > 0)
|
||||
ImGui::Text("Digestion Rate Bonus: +%0.2f%% / sec", digestionRateBonus * 60.0f);
|
||||
else if (digestionRateBonus < 0)
|
||||
ImGui::Text("Digestion Rate Penalty: %0.2f%% / sec", digestionRateBonus * 60.0f);
|
||||
if (eatSpeedBonus > 0) ImGui::Text("Eat Speed Bonus: +%0.2fx ", eatSpeedBonus);
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::TextUnformatted(Item::DESCRIPTIONS[type]);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
ImGui::PushFont(resources.font.get(), Font::BIG);
|
||||
auto text = std::format("x{}", quantity);
|
||||
auto textPos = ImVec2(cursorScreenPos.x + IMAGE_SIZE.x - ImGui::CalcTextSize(text.c_str()).x,
|
||||
cursorScreenPos.y + IMAGE_SIZE.y - ImGui::GetTextLineHeight());
|
||||
ImGui::GetWindowDrawList()->AddText(textPos, ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Text)),
|
||||
text.c_str());
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
if (values.empty()) ImGui::Text("Check the \"Play\" tab to earn rewards!");
|
||||
if (count == 0) ImGui::Text("Check the \"Play\" tab to earn rewards!");
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
@@ -11,20 +11,26 @@ namespace game::window
|
||||
{
|
||||
class Inventory
|
||||
{
|
||||
public:
|
||||
static constexpr auto ITEM_SIZE = glm::vec2(48, 48);
|
||||
static constexpr auto IMAGE_SIZE = ImVec2(48, 48);
|
||||
static constexpr auto BUTTON_ROUNDING = 32.0f;
|
||||
|
||||
private:
|
||||
std::map<Item::Type, int> values = {{Item::POKE_PUFF_BASIC_SWEET, 1},
|
||||
{Item::POKE_PUFF_BASIC_CITRUS, 1},
|
||||
{Item::POKE_PUFF_BASIC_MINT, 1},
|
||||
{Item::POKE_PUFF_BASIC_MOCHA, 1},
|
||||
{Item::POKE_PUFF_BASIC_SPICE, 1}};
|
||||
|
||||
public:
|
||||
static constexpr auto ITEM_SIZE = glm::vec2(48, 48);
|
||||
static constexpr auto IMAGE_SIZE = ImVec2(48, 48);
|
||||
static constexpr auto BUTTON_ROUNDING = 32.0f;
|
||||
int count;
|
||||
|
||||
bool isQueued{};
|
||||
Item::Type queuedItemType{};
|
||||
|
||||
Inventory();
|
||||
void update(Resources&, Character&, GameData&);
|
||||
void adjust_item(Item::Type, int = 1);
|
||||
void set_item(Item::Type, int);
|
||||
int get_item(Item::Type);
|
||||
};
|
||||
}
|
||||
@@ -6,6 +6,7 @@ namespace game::window
|
||||
ImVec2 pos)
|
||||
{
|
||||
MeasurementSystem& measurementSystem = gameData.measurementSystem;
|
||||
int& volume = gameData.volume;
|
||||
|
||||
ImGui::SetNextWindowSize(size);
|
||||
ImGui::SetNextWindowPos(pos);
|
||||
@@ -48,6 +49,13 @@ namespace game::window
|
||||
ImGui::RadioButton("Metric", (int*)&measurementSystem, MeasurementSystem::METRIC);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("Imperial", (int*)&measurementSystem, MeasurementSystem::IMPERIAL);
|
||||
|
||||
ImGui::SeparatorText("Sound");
|
||||
if (ImGui::SliderInt("Volume", (int*)&volume, 0, 100))
|
||||
{
|
||||
resources.set_audio_gain((float)volume / 100);
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
@@ -115,9 +123,16 @@ namespace game::window
|
||||
ImGui::PushItemWidth(100);
|
||||
for (int i = 0; i < Item::ITEM_COUNT; i++)
|
||||
{
|
||||
if (i == Item::INVALID) continue;
|
||||
if (Item::CATEGORIES[i] == Item::INVALID) continue;
|
||||
ImGui::PushID(i);
|
||||
ImGui::DragInt(Item::NAMES[i], &inventory.values[(Item::Type)i], 0.1f, 0, 999);
|
||||
|
||||
//TODO: Probably a cleaner way to do this, maybe
|
||||
int value = inventory.get_item((Item::Type)i);
|
||||
if (ImGui::DragInt(Item::NAMES[i], &value, 0.1f, 0, 999))
|
||||
{
|
||||
inventory.set_item((Item::Type)i, value);
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
@@ -85,6 +85,18 @@ namespace game::window
|
||||
auto barMin = ImVec2(position.x + (size.x * 0.5f) - (spacing * 0.5f), position.y + (spacing * 2.0f));
|
||||
auto barMax = ImVec2(barMin.x + (spacing * 2.0f), barMin.y + size.y - (spacing * 4.0f));
|
||||
|
||||
bool mouseHovering = ImGui::IsMouseHoveringRect(barMin, barMax);
|
||||
auto endTimerProgress = (float)endTimer / endTimerMax;
|
||||
|
||||
if (mouseHovering)
|
||||
{
|
||||
auto color = RECT_COLOR;
|
||||
//Ease out and back in again
|
||||
color.w = isActive ? 1.0f : (4 * pow(endTimerProgress, 2) - 4 * endTimerProgress + 1);
|
||||
drawList->AddRect(ImVec2(barMin.x - 1, barMin.y - 1), ImVec2(barMax.x + 1, barMax.y + 1),
|
||||
ImGui::GetColorU32(color));
|
||||
}
|
||||
|
||||
drawList->AddRectFilled(barMin, barMax, ImGui::GetColorU32(BG_COLOR));
|
||||
|
||||
auto barWidth = barMax.x - barMin.x;
|
||||
@@ -132,8 +144,6 @@ namespace game::window
|
||||
}
|
||||
};
|
||||
|
||||
auto endTimerProgress = (float)endTimer / endTimerMax;
|
||||
|
||||
range_draw(challenge.range, isActive ? 1.0f : 0.0f);
|
||||
|
||||
auto lineMin = ImVec2(barMin.x - LINE_WIDTH_BONUS, barMin.y + (barHeight * tryValue));
|
||||
@@ -171,8 +181,7 @@ namespace game::window
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Space) ||
|
||||
(ImGui::IsMouseHoveringRect(barMin, barMax) && ImGui::IsMouseClicked(ImGuiMouseButton_Left)))
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Space) || (mouseHovering && ImGui::IsMouseClicked(ImGuiMouseButton_Left)))
|
||||
{
|
||||
Grade grade{MISS};
|
||||
auto subRanges = sub_ranges_get(challenge.range);
|
||||
@@ -203,7 +212,7 @@ namespace game::window
|
||||
resources.sound_play(audio::HIGH_SCORE_BIG);
|
||||
isHighScoreBigAchieved = true;
|
||||
|
||||
inventory.values[Item::POKE_PUFF_SUPREME_HONOR]++;
|
||||
inventory.adjust_item(Item::POKE_PUFF_SUPREME_HONOR);
|
||||
|
||||
auto toastItemPosition =
|
||||
ImVec2(math::random_in_range(barMax.x + ITEM_SIZE.x, barMax.x + (size.x * 0.5f) - ITEM_SIZE.x),
|
||||
@@ -265,7 +274,7 @@ namespace game::window
|
||||
resources.sound_play(audio::FALL);
|
||||
resources.sound_play(RARITY_SOUNDS.at(rarity));
|
||||
|
||||
inventory.values[rewardType]++;
|
||||
inventory.adjust_item(rewardType);
|
||||
|
||||
auto toastItemPosition =
|
||||
ImVec2(math::random_in_range(barMax.x + ITEM_SIZE.x, barMax.x + (size.x * 0.5f) - ITEM_SIZE.x),
|
||||
|
||||
Reference in New Issue
Block a user