Remove single-player mode to focus on multiplayer

Removed all single-player/offline mode functionality:
- Removed ~2,200 lines of SQLite database code
- Removed 11 public SP methods from GameHandler
- Removed SP member variables and state flags
- Removed SP UI elements (auth screen button, game settings)
- Removed SQLite3 build dependency
- Deleted docs/single-player.md
- Updated documentation (README, FEATURES, CHANGELOG)

Files modified:
- src/game/game_handler.cpp: 2,852 lines (down from 4,921)
- include/game/game_handler.hpp: Removed SP API
- src/core/application.cpp/hpp: Removed startSinglePlayer()
- src/ui/*: Removed SP UI logic
- CMakeLists.txt: Removed SQLite3

All online multiplayer features preserved and tested.
This commit is contained in:
kelsi davis 2026-02-06 23:52:16 -08:00
parent 82afb83591
commit fb2e9bfb3d
15 changed files with 4959 additions and 3536 deletions

View file

@ -214,21 +214,6 @@ void AuthScreen::render(auth::AuthHandler& authHandler) {
ImGui::Separator();
ImGui::Spacing();
// Single-player mode button
ImGui::TextColored(ImVec4(0.5f, 0.8f, 1.0f, 1.0f), "Single-Player Mode");
ImGui::TextWrapped("Skip server connection and play offline with local rendering.");
if (ImGui::Button("Start Single Player", ImVec2(240, 30))) {
// Call single-player callback
if (onSinglePlayer) {
onSinglePlayer();
}
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
// Info text
ImGui::TextWrapped("Enter your account credentials to connect to the authentication server.");
ImGui::TextWrapped("Default port is 3724.");

View file

@ -154,10 +154,8 @@ void CharacterScreen::render(game::GameHandler& gameHandler) {
ss << "Entering world with " << character.name << "...";
setStatus(ss.str());
// Only send CMSG_PLAYER_LOGIN in online mode
if (!gameHandler.isSinglePlayerMode()) {
gameHandler.selectCharacter(character.guid);
}
// Send CMSG_PLAYER_LOGIN to server
gameHandler.selectCharacter(character.guid);
// Call callback
if (onCharacterSelected) {

View file

@ -673,15 +673,8 @@ void GameScreen::processTargetInput(game::GameHandler& gameHandler) {
auto unit = std::static_pointer_cast<game::Unit>(target);
if (unit->getHealth() == 0 && unit->getMaxHealth() > 0) {
gameHandler.lootTarget(target->getGuid());
} else if (gameHandler.isSinglePlayerMode()) {
// Single-player: interact with friendly NPCs, otherwise attack
if (!unit->isHostile() && unit->isInteractable()) {
gameHandler.interactWithNpc(target->getGuid());
} else {
gameHandler.startAutoAttack(target->getGuid());
}
} else {
// Online mode: interact with friendly NPCs, otherwise attack
// Interact with friendly NPCs, otherwise attack
if (!unit->isHostile() && unit->isInteractable()) {
gameHandler.interactWithNpc(target->getGuid());
} else {
@ -761,12 +754,6 @@ void GameScreen::renderPlayerFrame(game::GameHandler& gameHandler) {
}
}
// Override with local player stats in single-player mode
if (gameHandler.isSinglePlayerMode() && gameHandler.getLocalPlayerMaxHealth() > 0) {
playerHp = gameHandler.getLocalPlayerHealth();
playerMaxHp = gameHandler.getLocalPlayerMaxHealth();
}
// Health bar
float pct = static_cast<float>(playerHp) / static_cast<float>(playerMaxHp);
ImVec4 hpColor = isDead ? ImVec4(0.5f, 0.5f, 0.5f, 1.0f) : ImVec4(0.2f, 0.8f, 0.2f, 1.0f);
@ -2789,27 +2776,6 @@ void GameScreen::renderSettingsWindow() {
break;
}
}
if (auto* gameHandler = core::Application::getInstance().getGameHandler()) {
if (gameHandler->isSinglePlayerMode()) {
game::GameHandler::SinglePlayerSettings spSettings;
if (gameHandler->getSinglePlayerSettings(spSettings)) {
pendingFullscreen = spSettings.fullscreen;
pendingVsync = spSettings.vsync;
pendingShadows = spSettings.shadows;
pendingMusicVolume = spSettings.musicVolume;
pendingSfxVolume = spSettings.sfxVolume;
pendingMouseSensitivity = spSettings.mouseSensitivity;
pendingInvertMouse = spSettings.invertMouse;
for (int i = 0; i < kResCount; i++) {
if (kResolutions[i][0] == spSettings.resWidth &&
kResolutions[i][1] == spSettings.resHeight) {
pendingResIndex = i;
break;
}
}
}
}
}
pendingUiOpacity = static_cast<int>(uiOpacity_ * 100.0f + 0.5f);
settingsInit = true;
}
@ -2909,21 +2875,6 @@ void GameScreen::renderSettingsWindow() {
cameraController->setInvertMouse(pendingInvertMouse);
}
}
if (auto* gameHandler = core::Application::getInstance().getGameHandler()) {
if (gameHandler->isSinglePlayerMode()) {
game::GameHandler::SinglePlayerSettings spSettings;
spSettings.fullscreen = pendingFullscreen;
spSettings.vsync = pendingVsync;
spSettings.shadows = pendingShadows;
spSettings.resWidth = kResolutions[pendingResIndex][0];
spSettings.resHeight = kResolutions[pendingResIndex][1];
spSettings.musicVolume = pendingMusicVolume;
spSettings.sfxVolume = pendingSfxVolume;
spSettings.mouseSensitivity = pendingMouseSensitivity;
spSettings.invertMouse = pendingInvertMouse;
gameHandler->setSinglePlayerSettings(spSettings);
}
}
}
ImGui::Spacing();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 10.0f));

View file

@ -371,8 +371,7 @@ void InventoryScreen::pickupFromEquipment(game::Inventory& inv, game::EquipSlot
void InventoryScreen::placeInBackpack(game::Inventory& inv, int index) {
if (!holdingItem) return;
if (gameHandler_ && !gameHandler_->isSinglePlayerMode() &&
heldSource == HeldSource::EQUIPMENT) {
if (gameHandler_ && heldSource == HeldSource::EQUIPMENT) {
// Online mode: avoid client-side unequip; wait for server update.
cancelPickup(inv);
return;
@ -394,7 +393,7 @@ void InventoryScreen::placeInBackpack(game::Inventory& inv, int index) {
void InventoryScreen::placeInEquipment(game::Inventory& inv, game::EquipSlot slot) {
if (!holdingItem) return;
if (gameHandler_ && !gameHandler_->isSinglePlayerMode()) {
if (gameHandler_) {
if (heldSource == HeldSource::BACKPACK && heldBackpackIndex >= 0) {
// Online mode: request server auto-equip and keep local state intact.
gameHandler_->autoEquipItemBySlot(heldBackpackIndex);
@ -1101,7 +1100,7 @@ void InventoryScreen::renderItemSlot(game::Inventory& inventory, const game::Ite
inventoryDirty = true;
}
} else if (kind == SlotKind::BACKPACK && backpackIndex >= 0) {
if (gameHandler_ && !gameHandler_->isSinglePlayerMode()) {
if (gameHandler_) {
if (item.inventoryType > 0) {
// Auto-equip (online)
gameHandler_->autoEquipItemBySlot(backpackIndex);
@ -1109,36 +1108,6 @@ void InventoryScreen::renderItemSlot(game::Inventory& inventory, const game::Ite
// Use consumable (online)
gameHandler_->useItemBySlot(backpackIndex);
}
} else if (item.inventoryType > 0 || item.armor > 0 ||
!item.subclassName.empty()) {
// Auto-equip (single-player)
uint8_t equippingType = item.inventoryType;
game::EquipSlot targetSlot = getEquipSlotForType(equippingType, inventory);
if (targetSlot != game::EquipSlot::NUM_SLOTS) {
const auto& eqSlot = inventory.getEquipSlot(targetSlot);
if (eqSlot.empty()) {
inventory.setEquipSlot(targetSlot, item);
inventory.clearBackpackSlot(backpackIndex);
} else {
game::ItemDef equippedItem = eqSlot.item;
inventory.setEquipSlot(targetSlot, item);
inventory.setBackpackSlot(backpackIndex, equippedItem);
}
if (targetSlot == game::EquipSlot::MAIN_HAND && equippingType == 17) {
const auto& offHand = inventory.getEquipSlot(game::EquipSlot::OFF_HAND);
if (!offHand.empty()) {
inventory.addItem(offHand.item);
inventory.clearEquipSlot(game::EquipSlot::OFF_HAND);
}
}
if (targetSlot == game::EquipSlot::OFF_HAND &&
inventory.getEquipSlot(game::EquipSlot::MAIN_HAND).item.inventoryType == 17) {
inventory.addItem(inventory.getEquipSlot(game::EquipSlot::MAIN_HAND).item);
inventory.clearEquipSlot(game::EquipSlot::MAIN_HAND);
}
equipmentDirty = true;
inventoryDirty = true;
}
}
}
}