commit 4c8d5339
Add a player create and edit menu
Changed files
| M | src/ImGuiHairSelect.cpp before |
| M | src/ImGuiHairSelect.hpp before |
| M | src/ImGuiMainMenu.cpp before |
| M | src/ImGuiMainMenu.hpp before |
| M | src/ImGuiPlayerSelect.cpp before |
| M | src/ImGuiPlayerSelect.hpp before |
diff --git a/src/ImGuiHairSelect.cpp b/src/ImGuiHairSelect.cpp
index 9b4085a..d0e7837 100644
--- a/src/ImGuiHairSelect.cpp
+++ b/src/ImGuiHairSelect.cpp
@@ -29,7 +29,7 @@ https://github.com/sago007/saland
ImGuiHairSelect::ImGuiHairSelect() {
currentHair = globalData.player.hair;
-
+
// Define available hair options
hairOptions = {
{"standard_hair", "Redhead"},
@@ -77,22 +77,22 @@ void ImGuiHairSelect::Draw(SDL_Renderer* target) {
// Display hair options as buttons
for (const auto& option : hairOptions) {
ImGui::SetCursorPosX(button_x);
-
+
// Highlight current selection
if (option.id == currentHair) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.6f, 0.3f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.4f, 0.7f, 0.4f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.2f, 0.5f, 0.2f, 1.0f));
}
-
+
if (ImGui::Button(option.displayName.c_str(), ImVec2(button_width, button_height))) {
pendingHair = option.id;
}
-
+
if (option.id == currentHair) {
ImGui::PopStyleColor(3);
}
-
+
ImGui::Dummy(ImVec2(0.0f, 5.0f));
}
diff --git a/src/ImGuiHairSelect.hpp b/src/ImGuiHairSelect.hpp
index 1660e9b..d8bbfb3 100644
--- a/src/ImGuiHairSelect.hpp
+++ b/src/ImGuiHairSelect.hpp
@@ -49,12 +49,12 @@ private:
bool active = true;
std::string pendingHair;
std::string currentHair;
-
+
struct HairOption {
std::string id;
std::string displayName;
};
-
+
std::vector<HairOption> hairOptions;
};
diff --git a/src/ImGuiMainMenu.cpp b/src/ImGuiMainMenu.cpp
index 02f1927..0bacd10 100644
--- a/src/ImGuiMainMenu.cpp
+++ b/src/ImGuiMainMenu.cpp
@@ -44,7 +44,7 @@ void ImGuiMainMenu::UpdateLabels() {
void ImGuiMainMenu::Draw(SDL_Renderer* target) {
// Center the menu window
ImGuiIO& io = ImGui::GetIO();
- ImVec2 window_size(400, 570);
+ ImVec2 window_size(400, 500);
ImVec2 window_pos(
(io.DisplaySize.x - window_size.x) * 0.5f,
(io.DisplaySize.y - window_size.y) * 0.5f
@@ -90,13 +90,6 @@ void ImGuiMainMenu::Draw(SDL_Renderer* target) {
pendingAction = PendingAction::PlayerSelect;
}
- // Hair Select button
- ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10);
- ImGui::SetCursorPosX(button_x);
- if (ImGui::Button("Hair Style", ImVec2(button_width, button_height))) {
- pendingAction = PendingAction::HairSelect;
- }
-
// Options button
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10);
ImGui::SetCursorPosX(button_x);
@@ -138,9 +131,6 @@ void ImGuiMainMenu::ExecutePendingAction() {
runPlayerSelect();
UpdateLabels();
break;
- case PendingAction::HairSelect:
- runHairSelect();
- break;
case PendingAction::Options:
runMenuOptions();
break;
diff --git a/src/ImGuiMainMenu.hpp b/src/ImGuiMainMenu.hpp
index f792b73..ca8e1e8 100644
--- a/src/ImGuiMainMenu.hpp
+++ b/src/ImGuiMainMenu.hpp
@@ -34,7 +34,6 @@ union SDL_Event;
void runStartGame();
void runWorldSelect();
void runPlayerSelect();
-void runHairSelect();
void runMenuOptions();
void runHelpAbout();
@@ -56,7 +55,6 @@ private:
StartGame,
WorldSelect,
PlayerSelect,
- HairSelect,
Options,
About
};
diff --git a/src/ImGuiPlayerSelect.cpp b/src/ImGuiPlayerSelect.cpp
index 8a9f31e..4a7d4e8 100644
--- a/src/ImGuiPlayerSelect.cpp
+++ b/src/ImGuiPlayerSelect.cpp
@@ -24,10 +24,132 @@ https://github.com/sago007/saland
#include "ImGuiPlayerSelect.hpp"
#include "Libs/imgui/imgui.h"
#include "common.h"
+#include "saland/globals.hpp"
+#include "sago/SagoMisc.hpp"
#include <SDL.h>
+#include <algorithm>
+#include <cstring>
+#include <format>
ImGuiPlayerSelect::ImGuiPlayerSelect() {
- currentPlayer = Config::getInstance()->getString("player");
+ // Define available hair options
+ hairOptions = {
+ {"standard_hair", "Redhead"},
+ {"hair_blonde", "Blonde"},
+ {"hair_brunette", "Brunette"},
+ {"hair_raven", "Black"},
+ {"hair_blue", "Blue"},
+ {"hair_1", "Short Blue"}
+ };
+
+ LoadPlayerList();
+ LoadSelectedPlayer();
+}
+
+void ImGuiPlayerSelect::LoadPlayerList() {
+ playerNames.clear();
+ auto files = sago::GetFileList("players");
+ for (const auto& file : files) {
+ if (file.size() > 5 && file.substr(file.size() - 5) == ".json") {
+ std::string playerName = file.substr(0, file.size() - 5);
+ playerNames.push_back(playerName);
+ }
+ }
+ std::sort(playerNames.begin(), playerNames.end());
+
+ // Find current player in list
+ std::string currentPlayer = Config::getInstance()->getString("player");
+ selectedPlayerIndex = 0;
+ for (size_t i = 0; i < playerNames.size(); i++) {
+ if (playerNames[i] == currentPlayer) {
+ selectedPlayerIndex = i;
+ break;
+ }
+ }
+}
+
+void ImGuiPlayerSelect::LoadSelectedPlayer() {
+ if (selectedPlayerIndex >= 0 && selectedPlayerIndex < static_cast<int>(playerNames.size())) {
+ std::string filename = std::format("players/{}.json", playerNames[selectedPlayerIndex]);
+ if (sago::FileExists(filename.c_str())) {
+ nlohmann::json j = nlohmann::json::parse(sago::GetFileContent(filename.c_str()));
+ editingPlayer = j;
+ editingPlayer.save_name = playerNames[selectedPlayerIndex];
+ } else {
+ editingPlayer = Player();
+ editingPlayer.save_name = playerNames[selectedPlayerIndex];
+ }
+ } else {
+ editingPlayer = Player();
+ editingPlayer.save_name = "player1";
+ }
+
+ // Update UI buffers
+ strncpy(nameBuffer, editingPlayer.save_name.c_str(), sizeof(nameBuffer) - 1);
+ nameBuffer[sizeof(nameBuffer) - 1] = '\0';
+
+ // Set race index
+ selectedRaceIndex = (editingPlayer.race == "male") ? 0 : 1;
+
+ // Set hair index
+ selectedHairIndex = 0;
+ for (size_t i = 0; i < hairOptions.size(); i++) {
+ if (hairOptions[i].id == editingPlayer.hair) {
+ selectedHairIndex = i;
+ break;
+ }
+ }
+
+ needsSave = false;
+}
+
+void ImGuiPlayerSelect::SaveCurrentPlayer() {
+ editingPlayer.save_name = std::string(nameBuffer);
+ nlohmann::json j = editingPlayer;
+ sago::WriteFileContent(std::format("players/{}.json", editingPlayer.save_name).c_str(), j.dump());
+ needsSave = false;
+}
+
+void ImGuiPlayerSelect::CreateNewPlayer() {
+ int newPlayerNum = 1;
+ std::string newName;
+ do {
+ newName = std::format("player{}", newPlayerNum);
+ newPlayerNum++;
+ } while (std::find(playerNames.begin(), playerNames.end(), newName) != playerNames.end());
+
+ playerNames.push_back(newName);
+ std::sort(playerNames.begin(), playerNames.end());
+
+ // Find the new player index
+ for (size_t i = 0; i < playerNames.size(); i++) {
+ if (playerNames[i] == newName) {
+ selectedPlayerIndex = i;
+ break;
+ }
+ }
+
+ editingPlayer = Player();
+ editingPlayer.save_name = newName;
+ strncpy(nameBuffer, newName.c_str(), sizeof(nameBuffer) - 1);
+ nameBuffer[sizeof(nameBuffer) - 1] = '\0';
+
+ SaveCurrentPlayer();
+}
+
+void ImGuiPlayerSelect::DeleteCurrentPlayer() {
+ if (playerNames.size() <= 1) {
+ return; // Don't delete the last player
+ }
+
+ std::string toDelete = playerNames[selectedPlayerIndex];
+ playerNames.erase(playerNames.begin() + selectedPlayerIndex);
+
+ if (selectedPlayerIndex >= static_cast<int>(playerNames.size())) {
+ selectedPlayerIndex = playerNames.size() - 1;
+ }
+
+ needsRefresh = true;
}
bool ImGuiPlayerSelect::IsActive() {
@@ -37,7 +159,7 @@ bool ImGuiPlayerSelect::IsActive() {
void ImGuiPlayerSelect::Draw(SDL_Renderer* target) {
// Center the menu window
ImGuiIO& io = ImGui::GetIO();
- ImVec2 window_size(400, 300);
+ ImVec2 window_size(600, 500);
ImVec2 window_pos(
(io.DisplaySize.x - window_size.x) * 0.5f,
(io.DisplaySize.y - window_size.y) * 0.5f
@@ -46,7 +168,7 @@ void ImGuiPlayerSelect::Draw(SDL_Renderer* target) {
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always);
ImGui::SetNextWindowSize(window_size, ImGuiCond_Always);
- if (!ImGui::Begin("Player Select", &active,
+ if (!ImGui::Begin("Player Management", &active,
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoCollapse)) {
@@ -54,28 +176,101 @@ void ImGuiPlayerSelect::Draw(SDL_Renderer* target) {
return;
}
- // Center buttons horizontally
- float button_width = 300.0f;
- float button_height = 60.0f;
- float window_width = ImGui::GetWindowSize().x;
- float button_x = (window_width - button_width) * 0.5f;
+ // Left panel - Player list
+ ImGui::BeginChild("PlayerList", ImVec2(200, 0), true);
+ ImGui::Text("Players");
+ ImGui::Separator();
+
+ for (size_t i = 0; i < playerNames.size(); i++) {
+ if (ImGui::Selectable(playerNames[i].c_str(), selectedPlayerIndex == static_cast<int>(i))) {
+ if (needsSave) {
+ SaveCurrentPlayer();
+ }
+ selectedPlayerIndex = i;
+ LoadSelectedPlayer();
+ }
+ }
+
+ ImGui::Dummy(ImVec2(0.0f, 10.0f));
+
+ if (ImGui::Button("New Player", ImVec2(-1, 30))) {
+ if (needsSave) {
+ SaveCurrentPlayer();
+ }
+ CreateNewPlayer();
+ }
+
+ if (playerNames.size() > 1) {
+ if (ImGui::Button("Delete", ImVec2(-1, 30))) {
+ DeleteCurrentPlayer();
+ }
+ }
+
+ ImGui::EndChild();
+
+ ImGui::SameLine();
+
+ // Right panel - Player details
+ ImGui::BeginChild("PlayerDetails", ImVec2(0, 0), true);
+ ImGui::Text("Player Details");
+ ImGui::Separator();
+
+ ImGui::Dummy(ImVec2(0.0f, 10.0f));
+
+ // Name input
+ ImGui::Text("Name:");
+ ImGui::PushItemWidth(-1);
+ if (ImGui::InputText("##name", nameBuffer, sizeof(nameBuffer))) {
+ needsSave = true;
+ }
+ ImGui::PopItemWidth();
+
+ ImGui::Dummy(ImVec2(0.0f, 10.0f));
+
+ // Gender/Race selection
+ ImGui::Text("Gender:");
+ const char* races[] = { "Male", "Female" };
+ if (ImGui::Combo("##race", &selectedRaceIndex, races, 2)) {
+ editingPlayer.race = (selectedRaceIndex == 0) ? "male" : "female";
+ needsSave = true;
+ }
+
+ ImGui::Dummy(ImVec2(0.0f, 10.0f));
+
+ // Hair selection
+ ImGui::Text("Hair Style:");
+ std::vector<const char*> hairNames;
+ for (const auto& option : hairOptions) {
+ hairNames.push_back(option.displayName.c_str());
+ }
+ if (ImGui::Combo("##hair", &selectedHairIndex, hairNames.data(), hairNames.size())) {
+ editingPlayer.hair = hairOptions[selectedHairIndex].id;
+ needsSave = true;
+ }
+
+ ImGui::Dummy(ImVec2(0.0f, 20.0f));
+
+ // Action buttons
+ ImGui::Separator();
+ ImGui::Dummy(ImVec2(0.0f, 10.0f));
- // Add some top spacing
- ImGui::SetCursorPosY(50);
+ float button_width = (ImGui::GetContentRegionAvail().x - 10) / 2;
- // Player 1 button
- ImGui::SetCursorPosX(button_x);
- if (ImGui::Button("Player 1", ImVec2(button_width, button_height))) {
- pendingPlayer = "player1";
+ if (ImGui::Button("Save & Select", ImVec2(button_width, 40))) {
+ SaveCurrentPlayer();
+ Config::getInstance()->setString("player", editingPlayer.save_name);
+ globalData.player = editingPlayer;
+ active = false;
}
- // Player 2 button
- ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10);
- ImGui::SetCursorPosX(button_x);
- if (ImGui::Button("Player 2", ImVec2(button_width, button_height))) {
- pendingPlayer = "player2";
+ ImGui::SameLine();
+
+ if (ImGui::Button("Cancel", ImVec2(button_width, 40))) {
+ active = false;
}
+ ImGui::EndChild();
+
ImGui::End();
}
@@ -89,14 +284,10 @@ void ImGuiPlayerSelect::ProcessInput(const SDL_Event& event, bool& processed) {
}
}
-void ImGuiPlayerSelect::SelectPlayer(const std::string& playerName) {
- Config::getInstance()->setString("player", playerName);
- active = false;
-}
-
void ImGuiPlayerSelect::Update() {
- if (!pendingPlayer.empty()) {
- SelectPlayer(pendingPlayer);
- pendingPlayer.clear();
+ if (needsRefresh) {
+ LoadPlayerList();
+ LoadSelectedPlayer();
+ needsRefresh = false;
}
}
diff --git a/src/ImGuiPlayerSelect.hpp b/src/ImGuiPlayerSelect.hpp
index 98a4a83..3c0584b 100644
--- a/src/ImGuiPlayerSelect.hpp
+++ b/src/ImGuiPlayerSelect.hpp
@@ -25,7 +25,9 @@ https://github.com/sago007/saland
#define IMGUI_PLAYER_SELECT_HPP
#include "sago/GameStateInterface.hpp"
+#include "saland/model/Player.hpp"
#include <string>
+#include <vector>
struct SDL_Renderer;
union SDL_Event;
@@ -43,11 +45,28 @@ public:
virtual ~ImGuiPlayerSelect() = default;
private:
- void SelectPlayer(const std::string& playerName);
+ void LoadPlayerList();
+ void LoadSelectedPlayer();
+ void SaveCurrentPlayer();
+ void CreateNewPlayer();
+ void DeleteCurrentPlayer();
bool active = true;
- std::string pendingPlayer;
- std::string currentPlayer;
+ std::vector<std::string> playerNames;
+ int selectedPlayerIndex = 0;
+ Player editingPlayer;
+ char nameBuffer[64] = "";
+ int selectedRaceIndex = 0;
+ int selectedHairIndex = 0;
+
+ struct HairOption {
+ std::string id;
+ std::string displayName;
+ };
+ std::vector<HairOption> hairOptions;
+
+ bool needsSave = false;
+ bool needsRefresh = false;
};
#endif /* IMGUI_PLAYER_SELECT_HPP */