From 6efccef6e2d370eaaa60f3dc4c609db767368210 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 12 Aug 2023 00:51:48 +1000 Subject: [PATCH 001/146] * player callouts WIP --- src/actboulder.cpp | 12 + src/actplayer.cpp | 216 +++- src/collision.cpp | 7 +- src/collision.hpp | 3 +- src/draw.hpp | 19 + src/game.cpp | 10 +- src/init_game.cpp | 2 + src/interface/consolecommand.cpp | 5 + src/interface/drawstatus.cpp | 23 +- src/interface/interface.cpp | 1888 +++++++++++++++++++++++++++++ src/interface/interface.hpp | 173 ++- src/interface/playerinventory.cpp | 2 +- src/menu.cpp | 4 + src/net.cpp | 2 + src/player.cpp | 202 ++- src/player.hpp | 3 +- src/ui/GameUI.cpp | 242 +++- src/ui/GameUI.hpp | 1 + src/ui/MainMenu.cpp | 4 + 19 files changed, 2761 insertions(+), 57 deletions(-) diff --git a/src/actboulder.cpp b/src/actboulder.cpp index df5669c67..8caeeafe5 100644 --- a/src/actboulder.cpp +++ b/src/actboulder.cpp @@ -1296,15 +1296,27 @@ void actBoulder(Entity* my) #define BOULDERTRAP_FIRED my->skill[0] #define BOULDERTRAP_AMBIENCE my->skill[6] +#define BOULDERTRAPHOLE_INIT my->skill[0] void actBoulderTrapHole(Entity* my) { if ( multiplayer == CLIENT ) { + if ( !BOULDERTRAPHOLE_INIT ) + { + BOULDERTRAPHOLE_INIT = 1; + my->createWorldUITooltip(); + } return; } if ( !my ) { return; } + if ( !BOULDERTRAPHOLE_INIT ) + { + BOULDERTRAPHOLE_INIT = 1; + my->createWorldUITooltip(); + } + if ( my->z > -11.0 && my->z < -10 ) { // in ceiling, delete self if ceiling no longer exists diff --git a/src/actplayer.cpp b/src/actplayer.cpp index cc4d1667c..c325083f3 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -4042,6 +4042,7 @@ void actPlayer(Entity* my) bool shootmode = players[PLAYER_NUM]->shootmode; FollowerRadialMenu& followerMenu = FollowerMenu[PLAYER_NUM]; + CalloutRadialMenu& calloutMenu = CalloutMenu[PLAYER_NUM]; // object interaction if ( intro == false ) @@ -4101,8 +4102,9 @@ void actPlayer(Entity* my) } Input& input = Input::inputs[PLAYER_NUM]; + auto& b = (multiplayer != SINGLE && PLAYER_NUM != 0) ? Input::inputs[0].getBindings() : input.getBindings(); - if ( followerMenu.followerToCommand == nullptr && followerMenu.selectMoveTo == false ) + if ( !followerMenu.followerMenuIsOpen() && !calloutMenu.calloutMenuIsOpen() ) { bool clickedOnGUI = false; @@ -4173,7 +4175,7 @@ void actPlayer(Entity* my) selectedEntity[PLAYER_NUM] = nullptr; } } - else + else if ( followerMenu.followerMenuIsOpen() ) { selectedEntity[PLAYER_NUM] = NULL; @@ -4284,6 +4286,8 @@ void actPlayer(Entity* my) if ( players[PLAYER_NUM]->worldUI.isEnabled() ) { players[PLAYER_NUM]->worldUI.reset(); + players[PLAYER_NUM]->worldUI.tooltipView = Player::WorldUI_t::TooltipView::TOOLTIP_VIEW_RESCAN; + players[PLAYER_NUM]->worldUI.gimpDisplayTimer = 0; } followerMenu.selectMoveTo = false; @@ -4292,9 +4296,217 @@ void actPlayer(Entity* my) } } } + else if ( calloutMenu.calloutMenuIsOpen() ) + { + selectedEntity[PLAYER_NUM] = NULL; + // TODO CALLOUT? + if ( !players[PLAYER_NUM]->usingCommand() && players[PLAYER_NUM]->bControlEnabled && !gamePaused && input.binaryToggle("Use") ) + { + if ( !calloutMenu.menuToggleClick && calloutMenu.selectMoveTo ) + { + if ( calloutMenu.optionSelected == CalloutRadialMenu::CALLOUT_CMD_SELECT ) + { + // we're selecting a target for the ally. + Entity* target = entityClicked(nullptr, false, PLAYER_NUM, EntityClickType::ENTITY_CLICK_CALLOUT); + input.consumeBinaryToggle("Use"); + //input.consumeBindingsSharedWithBinding("Use"); + if ( target ) + { + Entity* parent = uidToEntity(target->skill[2]); + if ( target->behavior == &actMonster || (parent && parent->behavior == &actMonster) ) + { + // see if we selected a limb + if ( parent ) + { + target = parent; + } + } + else if ( target->sprite == 184 || target->sprite == 585 ) // switch base. + { + parent = uidToEntity(target->parent); + if ( parent ) + { + target = parent; + } + } + if ( true /*&& calloutMenu.allowedInteractEntity(*target)*/ ) + { + calloutMenu.createParticleCallout(target); + /*calloutMenu.holdWheel = false; + calloutMenu.selectMoveTo = false; + calloutMenu.bOpen = true; + calloutMenu.optionSelected = ALLY_CMD_CANCEL; + calloutMenu.initCalloutMenuGUICursor(true); + Player::soundActivate();*/ + } + } + + if ( players[PLAYER_NUM]->worldUI.isEnabled() ) + { + players[PLAYER_NUM]->worldUI.reset(); + players[PLAYER_NUM]->worldUI.tooltipView = Player::WorldUI_t::TooltipView::TOOLTIP_VIEW_RESCAN; + } + + calloutMenu.closeCalloutMenuGUI(); + strcpy(calloutMenu.interactText, ""); + } + } + } + } + + bool skipFollowerMenu = false; + if ( !players[PLAYER_NUM]->usingCommand() && players[PLAYER_NUM]->bControlEnabled + && !gamePaused ) + { + bool showCalloutCommandsOnGamepad = false; + auto showCalloutCommandsFind = b.find("Show Player Callouts"); + std::string showCalloutCommandsInputStr = ""; + if ( showCalloutCommandsFind != b.end() ) + { + showCalloutCommandsOnGamepad = (*showCalloutCommandsFind).second.isBindingUsingGamepad(); + showCalloutCommandsInputStr = (*showCalloutCommandsFind).second.input; + } + + if ( players[PLAYER_NUM]->worldUI.bTooltipInView && players[PLAYER_NUM]->worldUI.tooltipsInRange.size() > 1 ) + { + if ( showCalloutCommandsOnGamepad && + (showCalloutCommandsInputStr == input.binding("Interact Tooltip Next") + || showCalloutCommandsInputStr == input.binding("Interact Tooltip Prev")) ) + { + input.consumeBinaryToggle("Show Player Callouts"); + players[PLAYER_NUM]->hud.followerDisplay.bOpenFollowerMenuDisabled = true; + } + } + + if ( (input.binaryToggle("Show Player Callouts") && !showCalloutCommandsOnGamepad) + || (input.binaryToggle("Show Player Callouts") && showCalloutCommandsOnGamepad + && players[PLAYER_NUM]->shootmode /*&& !players[PLAYER_NUM]->worldUI.bTooltipInView*/) ) + { + if ( !calloutMenu.bOpen && !calloutMenu.selectMoveTo ) + { + if ( !players[PLAYER_NUM]->shootmode ) + { + players[PLAYER_NUM]->closeAllGUIs(CLOSEGUI_ENABLE_SHOOTMODE, CLOSEGUI_DONT_CLOSE_CALLOUTGUI); + } + input.consumeBinaryToggle("Show Player Callouts"); + calloutMenu.selectMoveTo = true; + calloutMenu.optionSelected = CalloutRadialMenu::CALLOUT_CMD_SELECT; + calloutMenu.lockOnEntityUid = 0; + Player::soundActivate(); + skipFollowerMenu = true; + + if ( players[PLAYER_NUM]->worldUI.isEnabled() ) + { + players[PLAYER_NUM]->worldUI.reset(); + players[PLAYER_NUM]->worldUI.tooltipView = Player::WorldUI_t::TooltipView::TOOLTIP_VIEW_RESCAN; + players[PLAYER_NUM]->worldUI.gimpDisplayTimer = 0; + } + } + else if ( calloutMenu.selectMoveTo ) + { + // we're selecting a target for the ally. + Entity* target = entityClicked(nullptr, true, PLAYER_NUM, EntityClickType::ENTITY_CLICK_CALLOUT); + + if ( target ) + { + Entity* parent = uidToEntity(target->skill[2]); + if ( target->behavior == &actMonster || (parent && parent->behavior == &actMonster) ) + { + // see if we selected a limb + if ( parent ) + { + target = parent; + } + } + else if ( target->sprite == 184 || target->sprite == 585 ) // switch base. + { + parent = uidToEntity(target->parent); + if ( parent ) + { + target = parent; + } + } + + calloutMenu.holdWheel = true; + if ( showCalloutCommandsOnGamepad ) + { + calloutMenu.holdWheel = false; + } + skipFollowerMenu = true; + calloutMenu.selectMoveTo = false; + calloutMenu.bOpen = true; + calloutMenu.initCalloutMenuGUICursor(true); + Player::soundActivate(); + calloutMenu.lockOnEntityUid = target->getUID(); + } + else + { + // we're selecting a point in the world + if ( players[PLAYER_NUM] && players[PLAYER_NUM]->entity ) + { + real_t startx = cameras[PLAYER_NUM].x * 16.0; + real_t starty = cameras[PLAYER_NUM].y * 16.0; + static ConsoleVariable cvar_followerStartZ("/follower_start_z", -2.5); + real_t startz = cameras[PLAYER_NUM].z + (4.5 - cameras[PLAYER_NUM].z) / 2.0 + *cvar_followerStartZ; + real_t pitch = cameras[PLAYER_NUM].vang; + if ( pitch < 0 || pitch > PI ) + { + pitch = 0; + } + + static ConsoleVariable cvar_followerMoveTo("/follower_moveto_z", 0.1); + static ConsoleVariable cvar_followerStartZLimit("/follower_start_z_limit", 7.5); + // draw line from the players height and direction until we hit the ground. + real_t previousx = startx; + real_t previousy = starty; + int index = 0; + const real_t yaw = cameras[PLAYER_NUM].ang; + for ( ; startz < *cvar_followerStartZLimit; startz += abs((*cvar_followerMoveTo) * tan(pitch)) ) + { + startx += 0.1 * cos(yaw); + starty += 0.1 * sin(yaw); + const int index_x = static_cast(startx) >> 4; + const int index_y = static_cast(starty) >> 4; + index = (index_y)*MAPLAYERS + (index_x)*MAPLAYERS * map.height; + if ( map.tiles[index] && !map.tiles[OBSTACLELAYER + index] ) + { + // store the last known good coordinate + previousx = startx;// + 16 * cos(yaw); + previousy = starty;// + 16 * sin(yaw); + } + if ( map.tiles[OBSTACLELAYER + index] ) + { + break; + } + } + + calloutMenu.holdWheel = true; + if ( showCalloutCommandsOnGamepad ) + { + calloutMenu.holdWheel = false; + } + skipFollowerMenu = true; + calloutMenu.selectMoveTo = false; + calloutMenu.bOpen = true; + calloutMenu.lockOnEntityUid = 0; + calloutMenu.moveToX = previousx; + calloutMenu.moveToY = previousy; + calloutMenu.initCalloutMenuGUICursor(true); + Player::soundActivate(); + } + else + { + calloutMenu.closeCalloutMenuGUI(); + } + } + } + } + } + if ( !players[PLAYER_NUM]->usingCommand() && players[PLAYER_NUM]->bControlEnabled && !gamePaused + && !skipFollowerMenu && !followerMenu.followerToCommand && followerMenu.recentEntity ) { auto& b = (multiplayer != SINGLE && PLAYER_NUM != 0) ? Input::inputs[0].getBindings() : input.getBindings(); diff --git a/src/collision.cpp b/src/collision.cpp index ec8c59fa6..2c7455372 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -166,7 +166,9 @@ Entity* entityClicked(bool* clickedOnGUI, bool clickCheckOverride, int player, E } if ( players[player]->worldUI.bTooltipActiveForPlayer(*tooltip) ) { - if ( tooltip->worldTooltipRequiresButtonHeld == 1 && *MainMenu::cvar_hold_to_activate ) + if ( tooltip->worldTooltipRequiresButtonHeld == 1 + && *MainMenu::cvar_hold_to_activate + && clicktype != ENTITY_CLICK_CALLOUT ) { if ( input.binaryHeldToggle("Use") ) { @@ -196,7 +198,8 @@ Entity* entityClicked(bool* clickedOnGUI, bool clickCheckOverride, int player, E } } - if ( !entity && !mute_player_monster_sounds && !clickCheckOverride ) + if ( !entity && !mute_player_monster_sounds && !clickCheckOverride + && clicktype != ENTITY_CLICK_CALLOUT ) { if ( players[player] && players[player]->entity && players[player]->movement.monsterEmoteGimpTimer == 0 ) { diff --git a/src/collision.hpp b/src/collision.hpp index 5f5e9e25b..340c4a9c0 100644 --- a/src/collision.hpp +++ b/src/collision.hpp @@ -21,7 +21,8 @@ enum EntityClickType ENTITY_CLICK_USE, ENTITY_CLICK_USE_TOOLTIPS_ONLY, ENTITY_CLICK_HELD_USE_TOOLTIPS_ONLY, - ENTITY_CLICK_FOLLOWER_INTERACT + ENTITY_CLICK_FOLLOWER_INTERACT, + ENTITY_CLICK_CALLOUT }; Entity* entityClicked(bool* clickedOnGUI, bool clickCheckOverride, int player, EntityClickType clicktype); bool entityInsideTile(Entity* entity, int x, int y, int z, bool checkSafeTiles = false); diff --git a/src/draw.hpp b/src/draw.hpp index 47a525771..42595cf33 100644 --- a/src/draw.hpp +++ b/src/draw.hpp @@ -41,6 +41,25 @@ vec4_t project( const mat4x4_t* model, const mat4x4_t* projview, const vec4_t* window); +struct ClipResult { + enum class Direction { + Invalid, + Left, + Right, + Top, + Bottom, + Front, + Behind, + }; + Direction direction = Direction::Invalid; + bool isBehind = false; + vec4_t clipped_coords; +}; +ClipResult project_clipped( + const vec4_t* world, + const mat4x4_t* model, + const mat4x4_t* projview, + const vec4_t* window); vec4_t unproject( const vec4_t* screenCoords, const mat4x4_t* model, diff --git a/src/game.cpp b/src/game.cpp index 18e6c2256..90626eee3 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1851,6 +1851,7 @@ void gameLogic(void) FollowerMenu[i].recentEntity = nullptr; FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; + CalloutMenu[i].closeCalloutMenuGUI(); } // stop all sounds @@ -2113,6 +2114,7 @@ void gameLogic(void) if ( players[i]->isLocalPlayer() ) { FollowerMenu[i].closeFollowerMenuGUI(true); + CalloutMenu[i].closeCalloutMenuGUI(); } players[i]->hud.followerBars.clear(); } @@ -5085,7 +5087,8 @@ void ingameHud() } if ( !shootmode ) // check we dont conflict with system bindings { - if ( players[player]->messageZone.logWindow || players[player]->minimap.mapWindow || FollowerMenu[player].followerMenuIsOpen() ) + if ( players[player]->messageZone.logWindow || players[player]->minimap.mapWindow + || FollowerMenu[player].followerMenuIsOpen() || CalloutMenu[player].calloutMenuIsOpen() ) { allowCasting = false; } @@ -5110,7 +5113,7 @@ void ingameHud() } else { - if ( FollowerMenu[player].followerMenuIsOpen() ) + if ( FollowerMenu[player].followerMenuIsOpen() || CalloutMenu[player].calloutMenuIsOpen() ) { if ( castMemorizedSpell ) { @@ -5353,6 +5356,7 @@ void ingameHud() players[player]->messageZone.processChatbox(); updateSkillUpFrame(player); updateLevelUpFrame(player); + CalloutMenu[player].update(); players[player]->inventoryUI.updateSelectedItemAnimation(); players[player]->inventoryUI.updateInventoryItemTooltip(); players[player]->inventoryUI.updateInventoryMiscTooltip(); @@ -6013,6 +6017,7 @@ static void doConsoleCommands() { for (int i = 0; i < MAXPLAYERS; ++i) { if (players[i]->isLocalPlayer() && inputs.bPlayerUsingKeyboardControl(i)) { FollowerMenu[i].closeFollowerMenuGUI(); + CalloutMenu[i].closeCalloutMenuGUI(); } } } @@ -6603,6 +6608,7 @@ int main(int argc, char** argv) FollowerMenu[i].recentEntity = nullptr; FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; + CalloutMenu[i].closeCalloutMenuGUI(); } // black background diff --git a/src/init_game.cpp b/src/init_game.cpp index 852022861..76e5f2e34 100644 --- a/src/init_game.cpp +++ b/src/init_game.cpp @@ -62,6 +62,7 @@ void initGameDatafiles(bool moddedReload) Player::CharacterSheet_t::loadCharacterSheetJSON(); StatusEffectQueue_t::loadStatusEffectsJSON(); FollowerRadialMenu::loadFollowerJSON(); + CalloutRadialMenu::loadCalloutJSON(); MonsterData_t::loadMonsterDataJSON(); ScriptTextParser.readAllScripts(); ShopkeeperConsumables_t::readFromFile(); @@ -292,6 +293,7 @@ int initGame() initClass(c); GenericGUI[c].setPlayer(c); FollowerMenu[c].setPlayer(c); + CalloutMenu[c].setPlayer(c); cameras[c].winx = 0; cameras[c].winy = 0; cameras[c].winw = xres; diff --git a/src/interface/consolecommand.cpp b/src/interface/consolecommand.cpp index 5cf2b0613..5f919ac89 100644 --- a/src/interface/consolecommand.cpp +++ b/src/interface/consolecommand.cpp @@ -3712,6 +3712,11 @@ namespace ConsoleCommands { messagePlayer(clientnum, MESSAGE_MISC, "Reloaded follower_wheel.json"); }); + static ConsoleCommand ccmd_loadcalloutwheel("/loadcalloutwheel", "", []CCMD{ + CalloutRadialMenu::loadCalloutJSON(); + messagePlayer(clientnum, MESSAGE_MISC, "Reloaded callout_wheel.json"); + }); + static ConsoleCommand ccmd_printleaderlist("/printleaderlist", "", []CCMD{ if (!(svFlags & SV_FLAG_CHEATS)) { diff --git a/src/interface/drawstatus.cpp b/src/interface/drawstatus.cpp index ede67ffb5..89e575f7f 100644 --- a/src/interface/drawstatus.cpp +++ b/src/interface/drawstatus.cpp @@ -2511,7 +2511,7 @@ void drawStatusNew(const int player) { drawHotBarTooltipOnCycle = false; } - if ( FollowerMenu[player].followerMenuIsOpen() ) + if ( FollowerMenu[player].followerMenuIsOpen() || CalloutMenu[player].calloutMenuIsOpen() ) { drawHotBarTooltipOnCycle = false; } @@ -2521,7 +2521,7 @@ void drawStatusNew(const int player) Frame* tooltipSlotFrame = nullptr; bool tooltipPromptFrameWasDisabled = true; - if ( (!shootmode && !FollowerMenu[player].followerMenuIsOpen()) || drawHotBarTooltipOnCycle) + if ( (!shootmode && !FollowerMenu[player].followerMenuIsOpen() && !CalloutMenu[player].calloutMenuIsOpen()) || drawHotBarTooltipOnCycle) { //Go back through all of the hotbar slots and draw the tooltips. for ( int num = 0; num < NUM_HOTBAR_SLOTS; ++num ) @@ -3497,6 +3497,25 @@ void drawStatusNew(const int player) FollowerMenu[player].followerFrame->setDisabled(true); } FollowerMenu[player].drawFollowerMenu(); + + if ( !CalloutMenu[player].calloutFrame ) + { + auto frame = gameUIFrame[player]->findFrame("callout"); + if ( !frame ) + { + CalloutMenu[player].calloutFrame = gameUIFrame[player]->addFrame("callout"); + } + else + { + CalloutMenu[player].calloutFrame = frame; + } + CalloutMenu[player].calloutFrame->setHollow(true); + CalloutMenu[player].calloutFrame->setBorder(0); + CalloutMenu[player].calloutFrame->setOwner(player); + CalloutMenu[player].calloutFrame->setInheritParentFrameOpacity(false); + CalloutMenu[player].calloutFrame->setDisabled(true); + } + CalloutMenu[player].drawCalloutMenu(); } int drawSpellTooltip(const int player, spell_t* spell, Item* item, SDL_Rect* src) diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index edb58ffb9..036617e25 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -151,6 +151,7 @@ const int inscriptionSlotHeight = 40; DamageIndicatorHandler_t DamageIndicatorHandler; EnemyHPDamageBarHandler enemyHPDamageBarHandler[MAXPLAYERS]; FollowerRadialMenu FollowerMenu[MAXPLAYERS]; +CalloutRadialMenu CalloutMenu[MAXPLAYERS]; GenericGUIMenu GenericGUI[MAXPLAYERS]; bool EnemyHPDamageBarHandler::bDamageGibTypesEnabled = false; @@ -1318,6 +1319,10 @@ void Player::openStatusScreen(const int whichGUIMode, const int whichInventoryMo { FollowerMenu[playernum].closeFollowerMenuGUI(); } + if ( whichGUIMode != GUI_MODE_NONE && whichGUIMode != GUI_MODE_CALLOUT ) + { + CalloutMenu[playernum].closeCalloutMenuGUI(); + } GenericGUI[playernum].closeGUI(); if ( minimap.mapWindow ) { @@ -1410,6 +1415,10 @@ void Player::closeAllGUIs(CloseGUIShootmode shootmodeAction, CloseGUIIgnore what { FollowerMenu[playernum].closeFollowerMenuGUI(); } + if ( whatToClose != CLOSEGUI_DONT_CLOSE_CALLOUTGUI ) + { + CalloutMenu[playernum].closeCalloutMenuGUI(); + } if ( whatToClose != CLOSEGUI_DONT_CLOSE_CHEST ) { if ( openedChest[playernum] ) @@ -6495,6 +6504,7 @@ void GenericGUIMenu::openGUI(int type, Item* effectItem, int effectBeatitude, in } FollowerMenu[gui_player].closeFollowerMenuGUI(); + CalloutMenu[gui_player].closeCalloutMenuGUI(); if ( openedChest[gui_player] ) { @@ -6532,6 +6542,7 @@ void GenericGUIMenu::openGUI(int type, bool experimenting, Item* itemOpenedWith) alchemyGUI.openAlchemyMenu(); FollowerMenu[gui_player].closeFollowerMenuGUI(); + CalloutMenu[gui_player].closeCalloutMenuGUI(); if ( openedChest[gui_player] ) { @@ -6584,6 +6595,7 @@ void GenericGUIMenu::openGUI(int type, Item* itemOpenedWith) } FollowerMenu[gui_player].closeFollowerMenuGUI(); + CalloutMenu[gui_player].closeCalloutMenuGUI(); if ( openedChest[gui_player] ) { @@ -21828,3 +21840,1879 @@ void GenericGUIMenu::ItemEffectGUI_t::createItemEffectMenu() } } } + +void CalloutRadialMenu::loadCalloutJSON() +{ + if ( !PHYSFS_getRealDir("/data/callout_wheel.json") ) + { + printlog("[JSON]: Error: Could not find file: data/callout_wheel.json"); + } + else + { + std::string inputPath = PHYSFS_getRealDir("/data/callout_wheel.json"); + inputPath.append("/data/callout_wheel.json"); + + File* fp = FileIO::open(inputPath.c_str(), "rb"); + if ( !fp ) + { + printlog("[JSON]: Error: Could not open json file %s", inputPath.c_str()); + } + else + { + char buf[65536]; + int count = fp->read(buf, sizeof(buf[0]), sizeof(buf)); + buf[count] = '\0'; + rapidjson::StringStream is(buf); + FileIO::close(fp); + rapidjson::Document d; + d.ParseStream(is); + if ( !d.HasMember("version") ) + { + printlog("[JSON]: Error: No 'version' value in json file, or JSON syntax incorrect! %s", inputPath.c_str()); + } + else + { + if ( d.HasMember("panel_center_x_offset") ) + { + CalloutRadialMenu::followerWheelFrameOffsetX = d["panel_center_x_offset"].GetInt(); + } + if ( d.HasMember("panel_center_y_offset") ) + { + CalloutRadialMenu::followerWheelFrameOffsetY = d["panel_center_y_offset"].GetInt(); + } + if ( d.HasMember("panel_radius") ) + { + CalloutRadialMenu::followerWheelRadius = d["panel_radius"].GetInt(); + } + if ( d.HasMember("panel_button_thickness") ) + { + CalloutRadialMenu::followerWheelButtonThickness = d["panel_button_thickness"].GetInt(); + } + if ( d.HasMember("panel_inner_circle_radius_offset") ) + { + CalloutRadialMenu::followerWheelInnerCircleRadiusOffset = d["panel_inner_circle_radius_offset"].GetInt(); + } + if ( d.HasMember("colors") ) + { + if ( d["colors"].HasMember("banner_default") ) + { + followerBannerTextColor = makeColor( + d["colors"]["banner_default"]["r"].GetInt(), + d["colors"]["banner_default"]["g"].GetInt(), + d["colors"]["banner_default"]["b"].GetInt(), + d["colors"]["banner_default"]["a"].GetInt()); + } + if ( d["colors"].HasMember("banner_highlight_default") ) + { + followerBannerTextHighlightColor = makeColor( + d["colors"]["banner_highlight_default"]["r"].GetInt(), + d["colors"]["banner_highlight_default"]["g"].GetInt(), + d["colors"]["banner_highlight_default"]["b"].GetInt(), + d["colors"]["banner_highlight_default"]["a"].GetInt()); + } + if ( d["colors"].HasMember("title") ) + { + followerTitleColor = makeColor( + d["colors"]["title"]["r"].GetInt(), + d["colors"]["title"]["g"].GetInt(), + d["colors"]["title"]["b"].GetInt(), + d["colors"]["title"]["a"].GetInt()); + } + if ( d["colors"].HasMember("title_creature_highlight") ) + { + followerTitleHighlightColor = makeColor( + d["colors"]["title_creature_highlight"]["r"].GetInt(), + d["colors"]["title_creature_highlight"]["g"].GetInt(), + d["colors"]["title_creature_highlight"]["b"].GetInt(), + d["colors"]["title_creature_highlight"]["a"].GetInt()); + } + } + if ( d.HasMember("panels") ) + { + CalloutRadialMenu::panelEntries.clear(); + for ( rapidjson::Value::ConstValueIterator itr = d["panels"].Begin(); + itr != d["panels"].End(); ++itr ) + { + CalloutRadialMenu::panelEntries.push_back(CalloutRadialMenu::PanelEntry()); + auto& entry = CalloutRadialMenu::panelEntries[CalloutRadialMenu::panelEntries.size() - 1]; + if ( (*itr).HasMember("x") ) + { + entry.x = (*itr)["x"].GetInt(); + } + if ( (*itr).HasMember("y") ) + { + entry.y = (*itr)["y"].GetInt(); + } + if ( (*itr).HasMember("path") ) + { + entry.path = (*itr)["path"].GetString(); + } + if ( (*itr).HasMember("path_hover") ) + { + entry.path_hover = (*itr)["path_hover"].GetString(); + } + if ( (*itr).HasMember("icon_offset_x") ) + { + entry.icon_offsetx = (*itr)["icon_offset_x"].GetInt(); + } + if ( (*itr).HasMember("icon_offset_y") ) + { + entry.icon_offsety = (*itr)["icon_offset_y"].GetInt(); + } + } + } + if ( d.HasMember("icons") ) + { + CalloutRadialMenu::iconEntries.clear(); + for ( rapidjson::Value::ConstValueIterator itr = d["icons"].Begin(); + itr != d["icons"].End(); ++itr ) + { + std::string actionName = ""; + if ( (*itr).HasMember("action") ) + { + actionName = (*itr)["action"].GetString(); + } + if ( actionName == "" ) + { + continue; + } + CalloutRadialMenu::iconEntries[actionName] = CalloutRadialMenu::IconEntry(); + CalloutRadialMenu::iconEntries[actionName].name = actionName; + if ( (*itr).HasMember("id") ) + { + CalloutRadialMenu::iconEntries[actionName].id = (*itr)["id"].GetInt(); + } + if ( (*itr).HasMember("path") ) + { + CalloutRadialMenu::iconEntries[actionName].path = (*itr)["path"].GetString(); + } + if ( (*itr).HasMember("path_active") ) + { + CalloutRadialMenu::iconEntries[actionName].path_active = (*itr)["path_active"].GetString(); + } + if ( (*itr).HasMember("path_hover") ) + { + CalloutRadialMenu::iconEntries[actionName].path_hover = (*itr)["path_hover"].GetString(); + } + if ( (*itr).HasMember("path_active_hover") ) + { + CalloutRadialMenu::iconEntries[actionName].path_active_hover = (*itr)["path_active_hover"].GetString(); + } + if ( (*itr).HasMember("text_maps") ) + { + for ( rapidjson::Value::ConstValueIterator itr2 = (*itr)["text_maps"].Begin(); + itr2 != (*itr)["text_maps"].End(); ++itr2 ) + { + for ( rapidjson::Value::ConstMemberIterator itr3 = itr2->MemberBegin(); + itr3 != itr2->MemberEnd(); ++itr3 ) + { + std::string mapKey = itr3->name.GetString(); + std::string mapText = itr3->value["text"].GetString(); + std::set mapHighlights; + for ( rapidjson::Value::ConstValueIterator highlightItr = itr3->value["word_highlights"].Begin(); + highlightItr != itr3->value["word_highlights"].End(); ++highlightItr ) + { + mapHighlights.insert(highlightItr->GetInt()); + } + CalloutRadialMenu::iconEntries[actionName].text_map[mapKey] = std::make_pair(mapText, mapHighlights); + } + } + } + } + } + printlog("[JSON]: Successfully read json file %s", inputPath.c_str()); + } + } + } +} + +void setCalloutBannerTextFormatted(const int player, Field* field, Uint32 color, std::set& highlights, char const* const text, ...) +{ + if ( !field ) { return; } + + char buf[256] = ""; + va_list argptr; + va_start(argptr, text); + vsnprintf(buf, sizeof(buf), text, argptr); + va_end(argptr); + + field->setText(buf); + field->clearWordsToHighlight(); + for ( auto v : highlights ) + { + field->addWordToHighlight(v, color); + } +} + +void setCalloutBannerTextUnformatted(const int player, Field* field, const char* iconName, const char* textKey, Uint32 color) +{ + if ( !field ) { return; } + if ( CalloutMenu[player].iconEntries.find(iconName) == CalloutMenu[player].iconEntries.end() ) + { + return; + } + auto& textMap = CalloutMenu[player].iconEntries[iconName].text_map[textKey]; + field->setText(textMap.first.c_str()); + field->clearWordsToHighlight(); + for ( auto v : textMap.second ) + { + field->addWordToHighlight(v, color); + } +} + +void CalloutRadialMenu::setCalloutBannerText(Field* field, const char* iconName, Uint32 color, + CalloutRadialMenu::CalloutCommand cmd) +{ + if ( !field ) { return; } + auto findIcon = CalloutRadialMenu::iconEntries.find(iconName); + if ( findIcon == CalloutRadialMenu::iconEntries.end() ) + { + return; + } + std::string key = "default"; + Entity* entity = uidToEntity(lockOnEntityUid); + const int player = getPlayer(); + + auto calloutType = getCalloutTypeForEntity(player, entity); + switch ( calloutType ) + { + case CALLOUT_TYPE_NO_TARGET: + break; + case CALLOUT_TYPE_NPC: + case CALLOUT_TYPE_NPC_ENEMY: + case CALLOUT_TYPE_NPC_PLAYERALLY: + if ( entity->behavior == &actMonster ) + { + int monsterType = entity->getMonsterTypeFromSprite(); + if ( monsterType >= NOTHING && monsterType < NUMMONSTERS ) + { + std::string monsterName = getMonsterLocalizedName((Monster)monsterType); + bool namedNPC = false; + if ( multiplayer != CLIENT && monsterType != SHOPKEEPER ) + { + if ( Stat* stats = entity->getStats() ) + { + if ( monsterNameIsGeneric(*stats) ) + { + monsterName = stats->name; + } + else if ( strcmp(stats->name, "") ) + { + monsterName = stats->name; + namedNPC = true; + } + } + } + + std::string key = "npc"; + if ( calloutType == CALLOUT_TYPE_NPC_PLAYERALLY ) + { + key = "npc_ally"; + } + else if ( calloutType == CALLOUT_TYPE_NPC_ENEMY ) + { + key = "npc_enemy"; + } + + if ( namedNPC + && findIcon->second.text_map.find(std::string(key + "_named")) != findIcon->second.text_map.end() ) + { + key += "_named"; + } + else if ( stringStartsWithVowel(monsterName) + && findIcon->second.text_map.find(std::string(key + "_an")) != findIcon->second.text_map.end() ) + { + key += "_an"; + } + + if ( findIcon->second.text_map.find(key) == findIcon->second.text_map.end() ) + { + key = "default"; + } + + auto& textMap = findIcon->second.text_map[key]; + auto highlights = textMap.second; + if ( highlights.size() > 0 ) + { + int indexStart = 0; + for ( auto highlight : highlights ) + { + indexStart = std::max(highlight, indexStart); + } + for ( auto c : monsterName ) + { + if ( c == ' ' ) + { + highlights.insert(indexStart + 1); + ++indexStart; + } + } + } + setCalloutBannerTextFormatted(player, field, color, highlights, textMap.first.c_str(), monsterName.c_str()); + return; + } + } + break; + case CALLOUT_TYPE_PLAYER: + break; + case CALLOUT_TYPE_LEVER: + break; + case CALLOUT_TYPE_BOULDER: + break; + case CALLOUT_TYPE_TRAP: + break; + case CALLOUT_TYPE_GENERIC_INTERACTABLE: + break; + case CALLOUT_TYPE_CHEST: + break; + case CALLOUT_TYPE_ITEM: + { + std::string itemName; + if ( entity && (multiplayer != CLIENT || (multiplayer == CLIENT && entity->itemReceivedDetailsFromServer == 1)) ) + { + if ( Item* item = newItemFromEntity(entity, true) ) + { + if ( item->type >= WOODEN_SHIELD && item->type < NUMITEMS ) + { + itemName = item->identified ? items[item->type].getIdentifiedName() : items[item->type].getUnidentifiedName(); + } + else + { + itemName = Language::get(3634); + } + free(item); + } + } + else + { + itemName = Language::get(3634); + } + std::string key = "item"; + if ( stringStartsWithVowel(itemName) && findIcon->second.text_map.find(std::string(key + "_an")) != findIcon->second.text_map.end() ) + { + key += "_an"; + } + + if ( findIcon->second.text_map.find(key) == findIcon->second.text_map.end() ) + { + key = "default"; + } + + auto& textMap = findIcon->second.text_map[key]; + auto highlights = textMap.second; + if ( highlights.size() > 0 ) + { + int indexStart = 0; + for ( auto highlight : highlights ) + { + indexStart = std::max(highlight, indexStart); + } + for ( auto c : itemName ) + { + if ( c == ' ' ) + { + highlights.insert(indexStart + 1); + ++indexStart; + } + } + } + setCalloutBannerTextFormatted(player, field, color, highlights, textMap.first.c_str(), itemName.c_str()); + return; + } + default: + break; + } + + setCalloutBannerTextUnformatted(player, field, iconName, key.c_str(), color); +} + +void CalloutRadialMenu::initCalloutMenuGUICursor(bool openInventory) +{ + bool oldshootmode = players[gui_player]->shootmode; + if ( openInventory ) + { + //players[gui_player]->openStatusScreen(GUI_MODE_INVENTORY, INVENTORY_MODE_ITEM); + players[gui_player]->closeAllGUIs(DONT_CHANGE_SHOOTMODE, CLOSEGUI_DONT_CLOSE_CALLOUTGUI); + players[gui_player]->openStatusScreen(GUI_MODE_CALLOUT, INVENTORY_MODE_ITEM); + } + + if ( !oldshootmode ) + { + Uint32 flags = (Inputs::SET_MOUSE | Inputs::SET_CONTROLLER | Inputs::UNSET_RELATIVE_MOUSE); + inputs.warpMouse(gui_player, + players[gui_player]->camera_x1() + (players[gui_player]->camera_width() / 2), + players[gui_player]->camera_y1() + (players[gui_player]->camera_height() / 2), flags); + } + + inputs.setMouse(gui_player, Inputs::OX, inputs.getMouse(gui_player, Inputs::X)); + inputs.setMouse(gui_player, Inputs::OY, inputs.getMouse(gui_player, Inputs::Y)); + + if ( menuX == -1 ) + { + menuX = inputs.getMouse(gui_player, Inputs::X); + } + if ( menuY == -1 ) + { + menuY = inputs.getMouse(gui_player, Inputs::Y); + } +} + +bool CalloutRadialMenu::calloutGUIHasBeenCreated() const +{ + if ( calloutFrame ) + { + if ( !calloutFrame->getFrames().empty() ) + { + for ( auto f : calloutFrame->getFrames() ) + { + if ( !f->isToBeDeleted() ) + { + return true; + } + } + return false; + } + else + { + return false; + } + } + return false; +} + +void CalloutRadialMenu::createCalloutMenuGUI() +{ + const int player = getPlayer(); + if ( !gui || !calloutFrame ) + { + return; + } + if ( calloutGUIHasBeenCreated() ) + { + return; + } + + const int midx = calloutFrame->getSize().w / 2; + const int midy = calloutFrame->getSize().h / 2; + + auto bgFrame = calloutFrame->addFrame("wheel base"); + bgFrame->setSize(SDL_Rect{ 0, 0, calloutFrame->getSize().w, calloutFrame->getSize().h }); + bgFrame->setHollow(false); + bgFrame->setDisabled(false); + bgFrame->setInheritParentFrameOpacity(false); + bgFrame->setOpacity(0.0); + + const char* font = "fonts/pixel_maz_multiline.ttf#16#2"; + + int panelIndex = 0; + for ( auto& entry : panelEntries ) + { + if ( panelIndex < PANEL_DIRECTION_END ) + { + SDL_Rect pos{ entry.x + midx, entry.y + midy, 0, 0 }; + char buf[32] = ""; + snprintf(buf, sizeof(buf), "panel %d", panelIndex); + Frame::image_t* img = bgFrame->addImage(pos, 0xFFFFFFFF, entry.path.c_str(), buf); + if ( auto imgGet = Image::get(img->path.c_str()) ) + { + img->pos.w = imgGet->getWidth(); + img->pos.h = imgGet->getHeight(); + } + img->ontop = true; + } + ++panelIndex; + } + + panelIndex = 0; + for ( auto& entry : panelEntries ) + { + if ( panelIndex < PANEL_DIRECTION_END ) + { + SDL_Rect pos{ entry.x + midx, entry.y + midy, 0, 0 }; + char buf[32] = ""; + snprintf(buf, sizeof(buf), "icon %d", panelIndex); + Frame::image_t* imgIcon = bgFrame->addImage(pos, 0xFFFFFFFF, "", buf); + imgIcon->disabled = true; + imgIcon->ontop = true; + } + ++panelIndex; + } + + { + // do center panel + auto& entry = panelEntries[panelEntries.size() - 1]; + SDL_Rect pos{ entry.x + midx, entry.y + midy, 0, 0 }; + char buf[32] = ""; + snprintf(buf, sizeof(buf), "panel %d", PANEL_DIRECTION_END); + Frame::image_t* img = bgFrame->addImage(pos, 0xFFFFFFFF, entry.path.c_str(), buf); + if ( auto imgGet = Image::get(img->path.c_str()) ) + { + img->pos.w = imgGet->getWidth(); + img->pos.h = imgGet->getHeight(); + } + } + + auto bannerFrame = calloutFrame->addFrame("banner frame"); + bannerFrame->setSize(SDL_Rect{ 0, 0, 0, 40 }); + bannerFrame->setHollow(false); + bannerFrame->setDisabled(false); + bannerFrame->setInheritParentFrameOpacity(false); + bannerFrame->addImage(SDL_Rect{ 0, 0, 42, 40 }, 0xFFFFFFFF, "#*images/ui/FollowerWheel/banner-cmd_l.png", "banner left"); + bannerFrame->addImage(SDL_Rect{ 0, 0, 42, 40 }, 0xFFFFFFFF, "#*images/ui/FollowerWheel/banner-cmd_r.png", "banner right"); + bannerFrame->addImage(SDL_Rect{ 0, 12, 0, 28 }, 0xFFFFFFFF, "*images/ui/FollowerWheel/banner-cmd_c.png", "banner center"); + auto bannerText = bannerFrame->addField("banner txt", 128); + bannerText->setFont(font); + bannerText->setText(""); + bannerText->setHJustify(Field::justify_t::LEFT); + bannerText->setVJustify(Field::justify_t::TOP); + bannerText->setSize(SDL_Rect{ 0, 0, 0, 24 }); + bannerText->setTextColor(followerBannerTextColor); + bannerText->setOutlineColor(makeColor(29, 16, 11, 255)); + auto bannerGlyph = bannerFrame->addImage(SDL_Rect{ 0, 0, 0, 0 }, 0xFFFFFFFF, "", "banner glyph"); + bannerGlyph->disabled = true; + auto bannerGlyph2 = bannerFrame->addImage(SDL_Rect{ 0, 0, 0, 0 }, 0xFFFFFFFF, "", "banner modifier glyph"); + bannerGlyph2->disabled = true; + + auto wheelTitleText = bgFrame->addField("wheel title", 128); + wheelTitleText->setFont(font); + wheelTitleText->setText(""); + wheelTitleText->setHJustify(Field::justify_t::LEFT); + wheelTitleText->setVJustify(Field::justify_t::TOP); + wheelTitleText->setSize(SDL_Rect{ 0, 0, 240, 24 }); + wheelTitleText->setTextColor(followerTitleColor); + wheelTitleText->setOutlineColor(makeColor(29, 16, 11, 255)); + + auto wheelSkillImg = bannerFrame->addImage(SDL_Rect{ 0, 0, 0, 0 }, 0xFFFFFFFF, "", "skill img"); + wheelSkillImg->disabled = true; + auto wheelStatImg = bannerFrame->addImage(SDL_Rect{ 0, 0, 0, 0 }, 0xFFFFFFFF, "", "stat img"); + wheelStatImg->disabled = true; +} + +bool CalloutRadialMenu::calloutMenuIsOpen() +{ + if ( selectMoveTo || bOpen ) + { + return true; + } + return false; +} + +std::vector CalloutRadialMenu::panelEntries; +std::map CalloutRadialMenu::iconEntries; +int CalloutRadialMenu::followerWheelButtonThickness = 70; +int CalloutRadialMenu::followerWheelRadius = 140; +int CalloutRadialMenu::followerWheelFrameOffsetX = 0; +int CalloutRadialMenu::followerWheelFrameOffsetY = 0; +int CalloutRadialMenu::followerWheelInnerCircleRadiusOffset = 0; +int CalloutRadialMenu::followerWheelInnerCircleRadiusOffsetAlternate = 0; +Uint32 CalloutRadialMenu::CalloutParticle_t::kParticleLifetime = TICKS_PER_SECOND * 5; + +std::vector& getPanelEntriesForCallout() +{ + return CalloutRadialMenu::panelEntries; +} + +CalloutRadialMenu::CalloutType CalloutRadialMenu::getCalloutTypeForEntity(const int player, Entity* parent) +{ + if ( !parent ) + { + return CALLOUT_TYPE_NO_TARGET; + } + CalloutType type = CALLOUT_TYPE_GENERIC_INTERACTABLE; + + if ( parent->behavior == &actSwitch || parent->behavior == &actSwitchWithTimer ) + { + } + else if ( parent->behavior == &actItem ) + { + type = CALLOUT_TYPE_ITEM; + } + else if ( parent->behavior == &actGoldBag ) + { + } + else if ( parent->behavior == &actFountain ) + { + } + else if ( parent->behavior == &actSink ) + { + } + else if ( parent->behavior == &actChestLid || parent->behavior == &actChest ) + { + type = CALLOUT_TYPE_CHEST; + } + else if ( parent->behavior == &actTorch ) + { + } + else if ( parent->behavior == &actCrystalShard ) + { + } + else if ( parent->behavior == &actHeadstone ) + { + } + else if ( parent->behavior == &actMonster ) + { + int monsterType = parent->getMonsterTypeFromSprite(); + type = CALLOUT_TYPE_NPC; + if ( monsterType == SHOPKEEPER ) + { + type = CALLOUT_TYPE_NPC; + } + else if ( (multiplayer != CLIENT && parent->checkEnemy(players[player]->entity) ) + || (multiplayer == CLIENT + && !parent->monsterAllyGetPlayerLeader() + && !monsterally[monsterType][stats[player]->type])) + { + type = CALLOUT_TYPE_NPC_ENEMY; + } + else if ( parent->monsterAllyGetPlayerLeader() ) + { + type = CALLOUT_TYPE_NPC_PLAYERALLY; + } + } + else if ( parent->behavior == &actGate ) + { + } + else if ( parent->behavior == &actSwitch || parent->behavior == &actSwitchWithTimer ) + { + if ( parent->skill[0] == 1 ) + { + } + else + { + } + } + else if ( parent->behavior == &actPowerCrystal ) + { + } + else if ( parent->behavior == &actPedestalBase ) + { + } + else if ( parent->behavior == &actCampfire ) + { + } + else if ( parent->behavior == &actLadder ) + { + + } + else if ( parent->behavior == &actPortal ) + { + } + else if ( parent->behavior == &actTeleporter ) + { + if ( parent->teleporterType == 2 ) // portal + { + } + else if ( parent->teleporterType == 1 ) // down ladder + { + } + else if ( parent->teleporterType == 0 ) // up ladder + { + } + } + else if ( parent->behavior == &::actTeleportShrine /*|| parent->behavior == &::actSpellShrine*/ ) + { + } + return type; +} + +CalloutRadialMenu::CalloutType CalloutRadialMenu::getCalloutTypeForUid(const int player, Uint32 uid) +{ + Entity* parent = uidToEntity(uid); + if ( !parent ) + { + return CALLOUT_TYPE_NO_TARGET; + } + + return CalloutRadialMenu::getCalloutTypeForEntity(player, parent); +} + +void CalloutRadialMenu::CalloutParticle_t::init(const int player) +{ + Entity* parent = uidToEntity(entityUid); + + if ( !parent ) + { + return; + } + + z = parent->z - 4; + + type = CalloutRadialMenu::getCalloutTypeForEntity(player, parent); +} + +void CalloutRadialMenu::closeCalloutMenuGUI() +{ + bOpen = false; + lockOnEntityUid = 0; + selectMoveTo = false; + menuX = -1; + menuY = -1; + moveToX = -1; + moveToY = -1; + menuToggleClick = false; + holdWheel = false; + optionSelected = -1; + if ( calloutFrame ) + { + calloutFrame->setDisabled(true); + for ( auto f : calloutFrame->getFrames() ) + { + f->removeSelf(); + } + } + animTitle = 0.0; + animWheel = 0.0; + openedThisTick = 0; + animInvalidAction = 0.0; + animInvalidActionTicks = 0; +} + +void CalloutRadialMenu::update() +{ + for ( auto& c : callouts ) + { + auto& callout = c.second; + Entity* entity = uidToEntity(callout.entityUid); + if ( entity ) + { + if ( TimerExperiments::bUseTimerInterpolation && entity->bUseRenderInterpolation ) + { + callout.x = entity->lerpRenderState.x.position * 16.0; + callout.y = entity->lerpRenderState.y.position * 16.0; + callout.z = entity->lerpRenderState.z.position + enemyBarSettings.getHeightOffset(entity); + callout.z -= 4; + } + else + { + callout.x = entity->x; + callout.y = entity->y; + callout.z = entity->z + enemyBarSettings.getHeightOffset(entity); + callout.z -= 4; + } + } + else if ( callout.entityUid != 0 ) + { + callout.expired = true; + } + } + + + if ( updatedThisTick == 0 || ticks != updatedThisTick ) + { + updatedThisTick = ticks; + for ( auto it = callouts.begin(); it != callouts.end(); ++it ) + { + it->second.ticks++; + if ( it->second.ticks >= CalloutParticle_t::kParticleLifetime ) + { + it->second.expired = true; + } + } + } + + for ( auto it = callouts.begin(); it != callouts.end(); ) + { + if ( it->second.expired ) + { + it = callouts.erase(it); + continue; + } + else + { + ++it; + } + } +} + +void CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu::CalloutCommand _cmd) +{ + if ( !entity ) { return; } + if ( _cmd == CALLOUT_CMD_CANCEL ) { return; } + callouts[entity->getUID()] = + CalloutRadialMenu::CalloutParticle_t(getPlayer(), entity->x, entity->y, entity->z, entity->getUID(), _cmd); +} +void CalloutRadialMenu::createParticleCallout(real_t x, real_t y, real_t z, Uint32 uid, CalloutRadialMenu::CalloutCommand _cmd) +{ + if ( _cmd == CALLOUT_CMD_CANCEL ) { return; } + CalloutMenu[clientnum].callouts[uid] = CalloutRadialMenu::CalloutParticle_t(getPlayer(), x, y, z, uid, _cmd); +} + +std::string CalloutRadialMenu::getIconPathForCommand(CalloutRadialMenu::CalloutCommand cmd, CalloutType type, bool highlight) +{ + if ( cmd == CALLOUT_CMD_LOOK ) + { + if ( highlight ) + { + return iconEntries["look_at"].path_hover; + } + else + { + return iconEntries["look_at"].path; + } + } + else if ( cmd == CALLOUT_CMD_HELP ) + { + if ( highlight ) + { + return iconEntries["help"].path_hover; + } + else + { + return iconEntries["help"].path; + } + } + else if ( cmd == CALLOUT_CMD_AFFIRMATIVE ) + { + if ( highlight ) + { + return iconEntries["affirmative"].path_hover; + } + else + { + return iconEntries["affirmative"].path; + } + } + else if ( cmd == CALLOUT_CMD_NEGATIVE ) + { + if ( highlight ) + { + return iconEntries["negative"].path_hover; + } + else + { + return iconEntries["negative"].path; + } + } + else if ( cmd == CALLOUT_CMD_MOVE ) + { + if ( highlight ) + { + return iconEntries["move"].path_hover; + } + else + { + return iconEntries["move"].path; + } + } + return ""; +} + +void CalloutRadialMenu::drawCalloutMenu() +{ + auto player = players[gui_player]; + if ( !player->isLocalPlayer() ) + { + closeCalloutMenuGUI(); + return; + } + + Input& input = Input::inputs[gui_player]; + + if ( selectMoveTo ) + { + if ( input.binaryToggle("MenuCancel") ) + { + input.consumeBinaryToggle("MenuCancel"); + input.consumeBindingsSharedWithBinding("MenuCancel"); + closeCalloutMenuGUI(); + Player::soundCancel(); + } + if ( calloutFrame ) + { + calloutFrame->setDisabled(true); + } + return; + } + + if ( !calloutFrame ) + { + return; + } + + calloutFrame->setSize(SDL_Rect{ players[gui_player]->camera_virtualx1(), + players[gui_player]->camera_virtualy1(), + players[gui_player]->camera_virtualWidth(), + players[gui_player]->camera_virtualHeight() }); + + int disableOption = 0; + bool keepWheelOpen = false; + + Sint32 omousex = inputs.getMouse(gui_player, Inputs::OX); + Sint32 omousey = inputs.getMouse(gui_player, Inputs::OY); + + std::map panelImages; + std::map panelIcons; + Frame* bannerFrame = nullptr; + Field* bannerTxt = nullptr; + Frame::image_t* bannerImgLeft = nullptr; + Frame::image_t* bannerImgRight = nullptr; + Frame::image_t* bannerImgCenter = nullptr; + Uint32 textHighlightColor = followerBannerTextHighlightColor; + + if ( !bOpen && (!calloutFrame->isDisabled() || players[gui_player]->gui_mode == GUI_MODE_CALLOUT) ) + { + closeCalloutMenuGUI(); + players[gui_player]->closeAllGUIs(CLOSEGUI_ENABLE_SHOOTMODE, CLOSEGUI_CLOSE_ALL); + return; + } + if ( calloutMenuIsOpen() && input.binaryToggle("MenuCancel") ) + { + input.consumeBinaryToggle("MenuCancel"); + input.consumeBindingsSharedWithBinding("MenuCancel"); + closeCalloutMenuGUI(); + players[gui_player]->closeAllGUIs(CLOSEGUI_ENABLE_SHOOTMODE, CLOSEGUI_CLOSE_ALL); + Player::soundCancel(); + return; + } + + //if ( ticks % 50 == 0 ) + //{ + // consoleCommand("/loadfollowerwheel"); + //} + + bool modifierPressed = false; + bool modifierActiveForOption = false; + if ( input.binary("Defend") ) + { + modifierPressed = true; + } + + if ( bOpen ) + { + { + const real_t fpsScale = getFPSScale(50.0); // ported from 50Hz + real_t setpointDiff = fpsScale * std::max(.1, (1.0 - animTitle)) / 2.5; + animTitle += setpointDiff; + animTitle = std::min(1.0, animTitle); + + real_t setpointDiff2 = fpsScale * std::max(.01, (1.0 - animWheel)) / 2.0; + animWheel += setpointDiff2; + animWheel = std::min(1.0, animWheel); + + // shaking feedback for invalid action + // constant decay for animation + real_t setpointDiffX = fpsScale * 1.0 / 25.0; + animInvalidAction -= setpointDiffX; + animInvalidAction = std::max(0.0, animInvalidAction); + } + + bool menuConfirmOnGamepad = input.input("MenuConfirm").isBindingUsingGamepad(); + bool menuLeftClickOnKeyboard = input.input("MenuLeftClick").isBindingUsingKeyboard() && !inputs.hasController(gui_player); + + // process commands if option selected on the wheel. + if ( !(players[gui_player]->bControlEnabled && !gamePaused && !players[gui_player]->usingCommand()) ) + { + // no action + } + else if ( (!menuToggleClick && !holdWheel + && !input.binaryToggle("Use") + && !input.binaryToggle("Show Player Callouts") + && !(input.binaryToggle("MenuConfirm") && menuConfirmOnGamepad) + && !(input.binaryToggle("MenuLeftClick") && menuLeftClickOnKeyboard)) + || (menuToggleClick && (input.binaryToggle("Use") || input.binaryToggle("Show Player Callouts"))) + || ((input.binaryToggle("MenuConfirm") && menuConfirmOnGamepad) + || (input.binaryToggle("MenuLeftClick") && menuLeftClickOnKeyboard) + || (input.binaryToggle("Use") && holdWheel)) + || (!input.binaryToggle("Show Player Callouts") && holdWheel && !menuToggleClick) + ) + { + //bool usingShowCmdRelease = (!input.binaryToggle("Show Player Callouts") && holdWheel && !menuToggleClick); + + if ( menuToggleClick ) + { + menuToggleClick = false; + if ( optionSelected == -1 ) + { + optionSelected = CALLOUT_CMD_CANCEL; + } + } + + input.consumeBinaryToggle("Use"); + input.consumeBinaryToggle("MenuConfirm"); + input.consumeBinaryToggle("MenuLeftClick"); + input.consumeBinaryToggle("Show Player Callouts"); + input.consumeBindingsSharedWithBinding("Use"); + input.consumeBindingsSharedWithBinding("MenuConfirm"); + input.consumeBindingsSharedWithBinding("MenuLeftClick"); + input.consumeBindingsSharedWithBinding("Show Player Callouts"); + + if ( disableOption != 0 ) + { + keepWheelOpen = true; + } + + bool sfxPlayed = false; + if ( disableOption != 0 ) + { + animInvalidAction = 1.0; + animInvalidActionTicks = ticks; + //if ( !usingShowCmdRelease ) + //{ + // // play bad feedback sfx + //} + playSound(90, 64); + sfxPlayed = true; + } + + if ( optionSelected != -1 ) + { + holdWheel = false; + if ( optionSelected != ALLY_CMD_ATTACK_CONFIRM && optionSelected != ALLY_CMD_MOVETO_CONFIRM ) + { + if ( !sfxPlayed && optionSelected != CALLOUT_CMD_CANCEL ) + { + playSound(139, 64); // click + sfxPlayed = true; + } + } + else + { + playSound(399, 48); // ping + } + // return to shootmode and close guis etc. TODO: tidy up interface code into 1 spot? + if ( !keepWheelOpen ) + { + if ( optionSelected == CALLOUT_CMD_CANCEL ) + { + players[gui_player]->closeAllGUIs(CLOSEGUI_ENABLE_SHOOTMODE, CLOSEGUI_DONT_CLOSE_CALLOUTGUI); + } + } + + if ( disableOption == 0 ) + { + if ( modifierPressed ) + { + if ( stats[gui_player]->shield && itemCategory(stats[gui_player]->shield) == SPELLBOOK ) + { + input.consumeBinaryToggle("Defend"); // don't try cast when menu closes. + } + } + + if ( multiplayer == CLIENT ) + { + /*if ( optionSelected == ALLY_CMD_ATTACK_CONFIRM ) + { + Uint32 olduid = followerToCommand->monsterAllyInteractTarget; + sendAllyCommandClient(gui_player, followerToCommand->getUID(), optionSelected, 0, 0, followerToCommand->monsterAllyInteractTarget); + Uint32 newuid = followerToCommand->monsterAllyInteractTarget; + if ( modifierPressed ) + { + followerToCommand->monsterAllyInteractTarget = olduid; + auto repeatCommandToFollowers = getAllOtherFollowersForSendAllCommand(gui_player, followerToCommand, followerStats->type, optionSelected); + for ( auto f : repeatCommandToFollowers ) + { + f->monsterAllyInteractTarget = olduid; + sendAllyCommandClient(gui_player, f->getUID(), optionSelected, 0, 0, f->monsterAllyInteractTarget); + } + followerToCommand->monsterAllyInteractTarget = newuid; + } + } + else if ( optionSelected == ALLY_CMD_MOVETO_CONFIRM ) + { + sendAllyCommandClient(gui_player, followerToCommand->getUID(), optionSelected, moveToX, moveToY); + if ( modifierPressed ) + { + auto repeatCommandToFollowers = getAllOtherFollowersForSendAllCommand(gui_player, followerToCommand, followerStats->type, optionSelected); + for ( auto f : repeatCommandToFollowers ) + { + sendAllyCommandClient(gui_player, f->getUID(), optionSelected, moveToX, moveToY); + } + } + } + else + { + sendAllyCommandClient(gui_player, followerToCommand->getUID(), optionSelected, 0, 0); + if ( modifierPressed ) + { + auto repeatCommandToFollowers = getAllOtherFollowersForSendAllCommand(gui_player, followerToCommand, followerStats->type, optionSelected); + for ( auto f : repeatCommandToFollowers ) + { + sendAllyCommandClient(gui_player, f->getUID(), optionSelected, 0, 0); + } + } + }*/ + } + else + { + if ( lockOnEntityUid ) + { + if ( Entity* target = uidToEntity(lockOnEntityUid) ) + { + createParticleCallout(target, (CalloutCommand)optionSelected); + } + } + else + { + createParticleCallout((real_t)moveToX, (real_t)moveToY, -4, 0, (CalloutCommand)optionSelected); + } + /*Uint32 olduid = followerToCommand->monsterAllyInteractTarget; + followerToCommand->monsterAllySendCommand(optionSelected, moveToX, moveToY, followerToCommand->monsterAllyInteractTarget); + Uint32 newuid = followerToCommand->monsterAllyInteractTarget; + if ( modifierPressed ) + { + followerToCommand->monsterAllyInteractTarget = olduid; + auto repeatCommandToFollowers = getAllOtherFollowersForSendAllCommand(gui_player, followerToCommand, followerStats->type, optionSelected); + for ( auto f : repeatCommandToFollowers ) + { + f->monsterAllyInteractTarget = olduid; + f->monsterAllySendCommand(optionSelected, moveToX, moveToY, f->monsterAllyInteractTarget); + } + followerToCommand->monsterAllyInteractTarget = newuid; + }*/ + } + } + + if ( optionSelected == CALLOUT_CMD_CANCEL && !sfxPlayed ) + { + Player::soundCancel(); + } + + if ( !keepWheelOpen ) + { + closeCalloutMenuGUI(); + } + optionSelected = -1; + } + else + { + menuToggleClick = true; + } + } + } + + if ( bOpen ) + { + if ( !calloutGUIHasBeenCreated() ) + { + createCalloutMenuGUI(); + } + calloutFrame->setDisabled(false); + + auto bgFrame = calloutFrame->findFrame("wheel base"); + bgFrame->setOpacity(100.0 * animWheel); + bannerFrame = calloutFrame->findFrame("banner frame"); + bannerImgLeft = bannerFrame->findImage("banner left"); + bannerImgRight = bannerFrame->findImage("banner right"); + bannerImgCenter = bannerFrame->findImage("banner center"); + bannerTxt = bannerFrame->findField("banner txt"); + bannerTxt->setText(""); + + int direction = NORTH; + const int midx = calloutFrame->getSize().w / 2; + const int midy = calloutFrame->getSize().h / 2; + for ( auto img : bgFrame->getImages() ) + { + if ( direction < PANEL_DIRECTION_END ) + { + panelImages[direction] = img; + img->pos.x = getPanelEntriesForCallout()[direction].x + midx + CalloutRadialMenu::followerWheelFrameOffsetX; + img->pos.y = getPanelEntriesForCallout()[direction].y + midy + CalloutRadialMenu::followerWheelFrameOffsetY; + img->path = getPanelEntriesForCallout()[direction].path; + } + else if ( direction < 2 * PANEL_DIRECTION_END ) + { + img->disabled = true; + img->path = ""; + int direction2 = direction - PANEL_DIRECTION_END; + panelIcons[direction2] = img; + panelIcons[direction2]->pos.x = panelImages[direction2]->pos.x + getPanelEntriesForCallout()[direction2].icon_offsetx; + panelIcons[direction2]->pos.y = panelImages[direction2]->pos.y + getPanelEntriesForCallout()[direction2].icon_offsety; + } + else if ( direction == 2 * PANEL_DIRECTION_END ) // center img + { + panelImages[PANEL_DIRECTION_END] = img; + img->pos.x = getPanelEntriesForCallout()[PANEL_DIRECTION_END].x + midx + CalloutRadialMenu::followerWheelFrameOffsetX; + img->pos.y = getPanelEntriesForCallout()[PANEL_DIRECTION_END].y + midy + CalloutRadialMenu::followerWheelFrameOffsetY; + img->path = getPanelEntriesForCallout()[PANEL_DIRECTION_END].path; + } + ++direction; + } + + const int centerx = players[gui_player]->camera_midx(); + const int centery = players[gui_player]->camera_midy(); + + SDL_Rect src; + src.x = centerx; + src.y = centery; + + int numoptions = 8; + real_t angleStart = PI / 2 - (PI / numoptions); + real_t angleMiddle = angleStart + PI / numoptions; + real_t angleEnd = angleMiddle + PI / numoptions; + int radius = 140; + int thickness = 70; + src.h = radius; + src.w = radius; + if ( players[gui_player]->camera_height() <= 768 ) + { + radius = 110; + thickness = 70; + src.h = 125; + src.w = 125; + } + + radius = CalloutRadialMenu::followerWheelRadius; + thickness = CalloutRadialMenu::followerWheelButtonThickness; + real_t menuScale = yres / (real_t)Frame::virtualScreenY; + radius *= menuScale; + thickness *= menuScale; + int centerButtonHighlightOffset = CalloutRadialMenu::followerWheelInnerCircleRadiusOffset; + + int highlight = -1; + int i = 0; + + if ( inputs.hasController(gui_player) ) + { + auto controller = inputs.getController(gui_player); + if ( controller ) + { + GameController::DpadDirection dir = controller->dpadDirToggle(); + if ( dir != GameController::DpadDirection::INVALID ) + { + if ( !controller->virtualDpad.consumed ) + { + Player::soundMovement(); + } + controller->consumeDpadDirToggle(); + switch ( dir ) + { + case GameController::DpadDirection::UP: + highlight = 0; + break; + case GameController::DpadDirection::UPLEFT: + highlight = 1; + break; + case GameController::DpadDirection::LEFT: + highlight = 2; + break; + case GameController::DpadDirection::DOWNLEFT: + highlight = 3; + break; + case GameController::DpadDirection::DOWN: + highlight = 4; + break; + case GameController::DpadDirection::DOWNRIGHT: + highlight = 5; + break; + case GameController::DpadDirection::RIGHT: + highlight = 6; + break; + case GameController::DpadDirection::UPRIGHT: + highlight = 7; + break; + default: + break; + } + real_t angleMiddleForOption = PI / 2 + dir * (2 * PI / numoptions); + omousex = centerx + (radius + thickness) * .75 * cos(angleMiddleForOption); + omousey = centery + (radius + thickness) * .75 * sin(angleMiddleForOption); + inputs.setMouse(gui_player, Inputs::OX, omousex); + inputs.setMouse(gui_player, Inputs::OY, omousey); + inputs.setMouse(gui_player, Inputs::X, omousex); + inputs.setMouse(gui_player, Inputs::Y, omousey); + + if ( highlight != -1 ) + { + inputs.getVirtualMouse(gui_player)->draw_cursor = false; + } + } + } + } + + bool mouseInCenterButton = sqrt(pow((omousex - menuX), 2) + pow((omousey - menuY), 2)) < (radius - thickness); + + angleStart = PI / 2 - (PI / numoptions); + angleMiddle = angleStart + PI / numoptions; + angleEnd = angleMiddle + PI / numoptions; + + const real_t mouseDetectionAdjust = PI / 128; + for ( i = 0; i < numoptions; ++i ) + { + // see if mouse cursor is within an option. + if ( highlight == -1 ) + { + if ( !mouseInCenterButton ) + { + real_t x1 = menuX + (radius + thickness + 45) * cos(angleEnd + mouseDetectionAdjust); + real_t y1 = menuY - (radius + thickness + 45) * sin(angleEnd + mouseDetectionAdjust); + real_t x2 = menuX + 5 * cos(angleMiddle); + real_t y2 = menuY - 5 * sin(angleMiddle); + real_t x3 = menuX + (radius + thickness + 45) * cos(angleStart - mouseDetectionAdjust); + real_t y3 = menuY - (radius + thickness + 45) * sin(angleStart - mouseDetectionAdjust); + real_t a = ((y2 - y3) * (omousex - x3) + (x3 - x2) * (omousey - y3)) / ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3)); + real_t b = ((y3 - y1) * (omousex - x3) + (x1 - x3) * (omousey - y3)) / ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3)); + real_t c = 1 - a - b; + if ( (0 <= a && a <= 1) && (0 <= b && b <= 1) && (0 <= c && c <= 1) ) + { + //barycentric calc for figuring if mouse point is within triangle. + highlight = i; + } + } + if ( !inputs.hasController(gui_player) ) + { + if ( highlight != -1 && optionSelected != highlight && optionSelected != -1 ) + { + Player::soundMovement(); + } + } + } + + SDL_Rect txt; + txt.x = src.x + src.w * cos(angleMiddle); + txt.y = src.y - src.h * sin(angleMiddle); + txt.w = 0; + txt.h = 0; + + // draw the text for the menu wheel. + + bool lockedOption = false; + { + /*if ( i == ALLY_CMD_ATTACK_SELECT ) + { + if ( i == highlight ) + { + panelIcons[i]->path = iconEntries["leader_attack"].path_hover; + setCalloutBannerText(gui_player, bannerTxt, "leader_attack", "default", textHighlightColor); + } + else + { + panelIcons[i]->path = iconEntries["leader_attack"].path; + } + } + else if ( i == ALLY_CMD_MOVETO_SELECT ) + { + if ( i == highlight ) + { + panelIcons[i]->path = iconEntries["leader_moveto"].path_hover; + setCalloutBannerText(gui_player, bannerTxt, "leader_moveto", "default", textHighlightColor); + } + else + { + panelIcons[i]->path = iconEntries["leader_moveto"].path; + } + } + else*/ + { + if ( i == CALLOUT_CMD_LOOK ) + { + if ( i == highlight ) + { + panelIcons[i]->path = iconEntries["look_at"].path_hover; + setCalloutBannerText(bannerTxt, "look_at", textHighlightColor, (CalloutCommand)i); + } + else + { + panelIcons[i]->path = iconEntries["look_at"].path; + } + } + else if ( i == CALLOUT_CMD_HELP ) + { + if ( i == highlight ) + { + panelIcons[i]->path = iconEntries["help"].path_hover; + setCalloutBannerText(bannerTxt, "help", textHighlightColor, (CalloutCommand)i); + } + else + { + panelIcons[i]->path = iconEntries["help"].path; + } + } + else if ( i == CALLOUT_CMD_AFFIRMATIVE ) + { + if ( i == highlight ) + { + panelIcons[i]->path = iconEntries["affirmative"].path_hover; + setCalloutBannerText(bannerTxt, "affirmative", textHighlightColor, (CalloutCommand)i); + } + else + { + panelIcons[i]->path = iconEntries["affirmative"].path; + } + } + else if ( i == CALLOUT_CMD_NEGATIVE ) + { + if ( i == highlight ) + { + panelIcons[i]->path = iconEntries["negative"].path_hover; + setCalloutBannerText(bannerTxt, "negative", textHighlightColor, (CalloutCommand)i); + } + else + { + panelIcons[i]->path = iconEntries["negative"].path; + } + } + else if ( i == CALLOUT_CMD_MOVE ) + { + if ( i == highlight ) + { + panelIcons[i]->path = iconEntries["move"].path_hover; + setCalloutBannerText(bannerTxt, "move", textHighlightColor, (CalloutCommand)i); + } + else + { + panelIcons[i]->path = iconEntries["move"].path; + } + } + } + } + + if ( highlight == i && !mouseInCenterButton ) + { + panelImages[i]->path = getPanelEntriesForCallout()[i].path_hover; + } + + if ( !lockedOption && panelIcons[i]->path != "" ) + { + if ( auto imgGet = Image::get(panelIcons[i]->path.c_str()) ) + { + panelIcons[i]->disabled = false; + panelIcons[i]->pos.w = imgGet->getWidth(); + panelIcons[i]->pos.h = imgGet->getHeight(); + panelIcons[i]->pos.x -= panelIcons[i]->pos.w / 2; + panelIcons[i]->pos.y -= panelIcons[i]->pos.h / 2; + } + } + + angleStart += 2 * PI / numoptions; + angleMiddle = angleStart + PI / numoptions; + angleEnd = angleMiddle + PI / numoptions; + } + + // draw center text. + if ( mouseInCenterButton ) + { + bool mouseInCenterHighlightArea = sqrt(pow((omousex - menuX), 2) + pow((omousey - menuY), 2)) < (radius - thickness + centerButtonHighlightOffset); + if ( mouseInCenterHighlightArea ) + { + panelImages[PANEL_DIRECTION_END]->path = getPanelEntriesForCallout()[PANEL_DIRECTION_END].path_hover; + } + + highlight = -1; + } + + if ( optionSelected == -1 && disableOption == 0 && highlight != -1 ) + { + // in case optionSelected is cleared, but we're still highlighting text (happens on next frame when clicking on disabled option.) + if ( highlight == CALLOUT_CMD_SELECT ) + { + disableOption = false;// optionDisabledForCreature(skillLVL, followerStats->type, highlight, followerToCommand); + } + else + { + disableOption = false;// optionDisabledForCreature(skillLVL, followerStats->type, highlight, followerToCommand); + } + } + + if ( highlight == -1 ) + { + setCalloutBannerTextUnformatted(gui_player, bannerTxt, "cancel", "default", hudColors.characterSheetRed); + } + + bool disableActionGlyph = false; + bool missingSkillLevel = false; + //if ( disableOption != 0 ) + //{ + // disableActionGlyph = true; + + // if ( disableOption == -2 ) // disabled due to cooldown + // { + // setCalloutBannerText(gui_player, bannerTxt, "invalid_action", "rest_cooldown", hudColors.characterSheetRed); + // } + // else if ( disableOption == -1 ) // disabled due to creature type + // { + // auto& textMap = FollowerMenu[gui_player].iconEntries["invalid_action"].text_map["command_unavailable"]; + // setCalloutBannerTextFormatted(gui_player, bannerTxt, hudColors.characterSheetRed, + // textMap.second, textMap.first.c_str(), + // getMonsterLocalizedName(HUMAN).c_str()); + // } + // else if ( disableOption == -3 ) // disabled due to tinkerbot quality + // { + // auto& textMap = FollowerMenu[gui_player].iconEntries["invalid_action"].text_map["tinker_quality_low"]; + // setCalloutBannerTextFormatted(gui_player, bannerTxt, hudColors.characterSheetRed, + // textMap.second, textMap.first.c_str(), + // getMonsterLocalizedName(HUMAN).c_str()); + // } + // else + // { + // std::string requirement = ""; + // std::string current = ""; + // int requirementVal = 0; + // int currentVal = 0; + // if ( highlight >= ALLY_CMD_DEFEND && highlight <= ALLY_CMD_END && highlight != CALLOUT_CMD_CANCEL ) + // { + // switch ( std::min(disableOption, SKILL_LEVEL_LEGENDARY) ) + // { + // case 0: + // requirement = Language::get(363); + // requirementVal = 0; + // break; + // case SKILL_LEVEL_NOVICE: + // requirement = Language::get(364); + // requirementVal = SKILL_LEVEL_NOVICE; + // break; + // case SKILL_LEVEL_BASIC: + // requirement = Language::get(365); + // requirementVal = SKILL_LEVEL_BASIC; + // break; + // case SKILL_LEVEL_SKILLED: + // requirement = Language::get(366); + // requirementVal = SKILL_LEVEL_SKILLED; + // break; + // case SKILL_LEVEL_EXPERT: + // requirement = Language::get(367); + // requirementVal = SKILL_LEVEL_EXPERT; + // break; + // case SKILL_LEVEL_MASTER: + // requirement = Language::get(368); + // requirementVal = SKILL_LEVEL_MASTER; + // break; + // case SKILL_LEVEL_LEGENDARY: + // requirement = Language::get(369); + // requirementVal = SKILL_LEVEL_LEGENDARY; + // break; + // default: + // break; + // } + // requirement.erase(std::remove(requirement.begin(), requirement.end(), ' '), requirement.end()); // trim whitespace + + // current = Language::get(363); + // current.erase(std::remove(current.begin(), current.end(), ' '), current.end()); // trim whitespace + // currentVal = 0; + // } + + // auto& textMap = FollowerMenu[gui_player].iconEntries["invalid_action"].text_map["skill_missing_leader"]; + // setFollowerBannerTextFormatted(gui_player, bannerTxt, hudColors.characterSheetRed, + // textMap.second, textMap.first.c_str(), + // currentVal, requirementVal); + // missingSkillLevel = true; + // } + //} + + auto wheelSkillImg = bannerFrame->findImage("skill img"); + wheelSkillImg->disabled = true; + auto wheelStatImg = bannerFrame->findImage("stat img"); + wheelStatImg->disabled = true; + + bannerFrame->setDisabled(false); + if ( auto textGet = bannerTxt->getTextObject() ) + { + SDL_Rect txtPos = bannerTxt->getSize(); + if ( !strcmp(bannerTxt->getText(), "") && txtPos.w == 0 ) + { + txtPos.w = 82; + } + else if ( strcmp(bannerTxt->getText(), "") ) + { + txtPos.w = textGet->getWidth(); + } + + auto bannerGlyph = bannerFrame->findImage("banner glyph"); + bannerGlyph->disabled = true; + if ( inputs.hasController(gui_player) ) + { + bannerGlyph->path = Input::inputs[gui_player].getGlyphPathForBinding("MenuConfirm"); + } + else + { + bannerGlyph->path = Input::inputs[gui_player].getGlyphPathForBinding("MenuLeftClick"); + } + //bannerGlyph->path = Input::inputs[gui_player].getGlyphPathForBinding("Use"); + auto bannerGlyphModifier = bannerFrame->findImage("banner modifier glyph"); + bannerGlyphModifier->disabled = true; + bannerGlyphModifier->path = Input::inputs[gui_player].getGlyphPathForBinding("Defend"); + if ( auto imgGet = Image::get(bannerGlyph->path.c_str()) ) + { + bannerGlyph->pos.w = imgGet->getWidth(); + bannerGlyph->pos.h = imgGet->getHeight(); + bannerGlyph->disabled = disableActionGlyph || !strcmp(bannerTxt->getText(), ""); + } + if ( auto imgGet = Image::get(bannerGlyphModifier->path.c_str()) ) + { + bannerGlyphModifier->pos.w = imgGet->getWidth(); + bannerGlyphModifier->pos.h = imgGet->getHeight(); + bannerGlyphModifier->disabled = bannerGlyph->disabled || !modifierActiveForOption; + } + + if ( !bannerGlyph->disabled ) + { + animInvalidAction = 0.0; + } + + bannerImgCenter->pos.w = txtPos.w + 16 + + (bannerGlyph->disabled ? 0 : ((bannerGlyph->pos.w + 8) / 2)) + + (bannerGlyphModifier->disabled ? 0 : (bannerGlyphModifier->pos.w + 2)); + int missingSkillLevelIconWidth = 0; + if ( missingSkillLevel ) + { + missingSkillLevelIconWidth = wheelStatImg->pos.w + wheelSkillImg->pos.w + 8; + } + bannerImgCenter->pos.w += missingSkillLevelIconWidth / 2; + const int totalWidth = bannerImgLeft->pos.w + bannerImgRight->pos.w + bannerImgCenter->pos.w; + + const int midx = calloutFrame->getSize().w / 2; + const int midy = calloutFrame->getSize().h / 2; + + SDL_Rect bannerSize = bannerFrame->getSize(); + bannerSize.w = totalWidth; + bannerSize.x = midx - (totalWidth / 2); + bannerSize.y = midy + CalloutRadialMenu::followerWheelRadius + CalloutRadialMenu::followerWheelButtonThickness + 4; + if ( players[gui_player]->bUseCompactGUIHeight() ) + { + bannerSize.y -= 16; + } + //bannerSize.y += 32 * (1.0 - animTitle); + bannerFrame->setSize(bannerSize); + bannerFrame->setOpacity(100.0 * animTitle); + bannerImgLeft->pos.x = 0; + bannerImgCenter->pos.x = bannerImgLeft->pos.x + bannerImgLeft->pos.w; + bannerImgRight->pos.x = bannerImgCenter->pos.x + bannerImgCenter->pos.w; + + txtPos.x = bannerImgCenter->pos.x + (bannerImgCenter->pos.w / 2) - (txtPos.w / 2); + txtPos.x += bannerGlyph->disabled ? 0 : ((bannerGlyph->pos.w + 8) / 2); + txtPos.x += bannerGlyphModifier->disabled ? 0 : ((bannerGlyphModifier->pos.w + 0) / 2); + if ( missingSkillLevel ) + { + txtPos.x -= (missingSkillLevelIconWidth / 2) - 4; + } + if ( txtPos.x % 2 == 1 ) + { + ++txtPos.x; + } + if ( animInvalidAction > 0.01 ) + { + txtPos.x += -2 + 2 * (cos(animInvalidAction * 4 * PI)); + } + txtPos.y = 17; + bannerTxt->setSize(txtPos); + + if ( missingSkillLevel ) + { + wheelSkillImg->pos.x = txtPos.x + txtPos.w; + wheelSkillImg->pos.y = txtPos.y - 3; + wheelSkillImg->disabled = false; + + wheelStatImg->pos.x = wheelSkillImg->pos.x + wheelSkillImg->pos.w; + wheelStatImg->pos.y = wheelSkillImg->pos.y; + wheelStatImg->disabled = false; + } + + bannerGlyph->pos.x = txtPos.x - bannerGlyph->pos.w - 8; + if ( bannerGlyph->pos.x % 2 == 1 ) + { + ++bannerGlyph->pos.x; + } + bannerGlyph->pos.y = txtPos.y + txtPos.h / 2 - bannerGlyph->pos.h / 2; + if ( bannerGlyph->pos.y % 2 == 1 ) + { + bannerGlyph->pos.y -= 1; + } + bannerSize.h = std::max(40, bannerGlyph->pos.y + bannerGlyph->pos.h); + if ( !bannerGlyphModifier->disabled ) + { + bannerGlyphModifier->pos.x = txtPos.x - bannerGlyphModifier->pos.w - 8; + bannerGlyph->pos.x = bannerGlyphModifier->pos.x - bannerGlyph->pos.w - 2; + + if ( bannerGlyphModifier->pos.x % 2 == 1 ) + { + ++bannerGlyphModifier->pos.x; + } + bannerGlyphModifier->pos.y = txtPos.y + txtPos.h / 2 - bannerGlyphModifier->pos.h / 2; + if ( bannerGlyphModifier->pos.y % 2 == 1 ) + { + bannerGlyphModifier->pos.y -= 1; + } + bannerSize.h = std::max(bannerSize.h, bannerGlyphModifier->pos.y + bannerGlyphModifier->pos.h); + } + bannerFrame->setSize(bannerSize); + + auto wheelTitleText = bgFrame->findField("wheel title"); + Stat* playerStats = stats[gui_player]; + if ( playerStats ) + { + char buf[128] = ""; + int spaces = 0; + int spaces2 = 0; + for ( int c = 0; c <= strlen(Language::get(4200)); ++c ) + { + if ( Language::get(4200)[c] == '\0' ) + { + break; + } + if ( Language::get(4200)[c] == ' ' ) + { + ++spaces; + } + } + if ( strcmp(playerStats->name, "") && strcmp(playerStats->name, "nothing") ) + { + snprintf(buf, sizeof(buf), Language::get(4200), playerStats->name); + } + else + { + snprintf(buf, sizeof(buf), Language::get(4200), getMonsterLocalizedName(playerStats->type).c_str()); + } + + for ( int c = 0; c <= strlen(buf); ++c ) + { + if ( buf[c] == '\0' ) + { + break; + } + if ( buf[c] == ' ' ) + { + ++spaces2; + } + } + wheelTitleText->setText(buf); + wheelTitleText->clearWordsToHighlight(); + int wordIndex = 1; + while ( spaces2 >= spaces ) // every additional space means +1 word to highlight for the monster's name + { + wheelTitleText->addWordToHighlight(wordIndex, followerTitleHighlightColor); + --spaces2; + ++wordIndex; + } + } + SDL_Rect titlePos = wheelTitleText->getSize(); + if ( auto textGet2 = wheelTitleText->getTextObject() ) + { + titlePos.w = textGet2->getWidth(); + titlePos.x = bannerSize.x + bannerSize.w / 2 - (titlePos.w / 2); + if ( titlePos.x % 2 == 1 ) + { + ++titlePos.x; + } + titlePos.y = midy - CalloutRadialMenu::followerWheelRadius - CalloutRadialMenu::followerWheelButtonThickness - 24; + titlePos.y -= 32 * (1.0 - animTitle); + ++titlePos.y; // add 1 to be even pixeled + wheelTitleText->setSize(titlePos); + } + } + + if ( !keepWheelOpen ) + { + optionSelected = highlight; // don't reselect if we're keeping the wheel open by using a toggle option. + } + } +} + +bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updateInteractText) +{ + if ( optionSelected != CALLOUT_CMD_SELECT ) + { + return false; + } + + if ( !players[gui_player] || !players[gui_player]->entity ) + { + return false; + } + + bool interactItems = true; //allowedInteractItems(followerStats->type) || allowedInteractFood(followerStats->type); + bool interactWorld = true; //allowedInteractWorld(followerStats->type); + bool enableAttack = true; + + if ( updateInteractText ) + { + strcpy(interactText, Language::get(4347)); // "Callout " + } + if ( selectedEntity.behavior == &actTorch && interactWorld ) + { + if ( updateInteractText ) + { + strcat(interactText, items[TOOL_TORCH].getIdentifiedName()); + } + } + else if ( (selectedEntity.behavior == &actSwitch || + selectedEntity.behavior == &actSwitchWithTimer || + selectedEntity.sprite == 184) && interactWorld ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(4044)); // "switch" + } + } + else if ( (selectedEntity.behavior == &actTeleportShrine) && (interactWorld || interactItems || enableAttack) ) + { + if ( updateInteractText ) + { + if ( !interactItems && !interactWorld && enableAttack ) + { + strcpy(interactText, Language::get(4347)); // "Callout " + } + strcat(interactText, Language::get(4309)); // "shrine" + } + } + else if ( (selectedEntity.behavior == &actTeleporter) && interactWorld ) + { + if ( updateInteractText ) + { + switch ( selectedEntity.teleporterType ) + { + case 0: + case 1: + strcat(interactText, Language::get(4310)); // "ladder" + break; + case 2: + strcat(interactText, Language::get(4311)); // "portal" + break; + default: + break; + } + } + } + else if ( selectedEntity.behavior == &actBomb && interactWorld ) + { + if ( updateInteractText ) + { + strcpy(interactText, Language::get(3093)); + strcat(interactText, Language::get(4045)); // "trap" + } + } + else if ( selectedEntity.behavior == &actBoulderTrapHole + && interactWorld ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(4349)); // "trap" + } + } + else if ( selectedEntity.behavior == &actItem && interactItems ) + { + if ( updateInteractText ) + { + if ( multiplayer != CLIENT ) + { + if ( selectedEntity.skill[15] == 0 ) + { + strcat(interactText, items[selectedEntity.skill[10]].getUnidentifiedName()); + } + else + { + strcat(interactText, items[selectedEntity.skill[10]].getIdentifiedName()); + } + } + else + { + strcat(interactText, Language::get(4046)); // "item" + } + } + } + else if ( selectedEntity.behavior == &actMonster && enableAttack && selectedEntity.getMonsterTypeFromSprite() != GYROBOT ) + { + if ( updateInteractText ) + { + int monsterType = selectedEntity.getMonsterTypeFromSprite(); + strcat(interactText, getMonsterLocalizedName((Monster)monsterType).c_str()); + } + } + else + { + if ( updateInteractText ) + { + strcpy(interactText, Language::get(4047)); // "No interactions available" + } + return false; + } + return true; +} \ No newline at end of file diff --git a/src/interface/interface.hpp b/src/interface/interface.hpp index 13a2b5756..517e86342 100644 --- a/src/interface/interface.hpp +++ b/src/interface/interface.hpp @@ -189,6 +189,7 @@ static const int GUI_MODE_MAGIC = 1; static const int GUI_MODE_SHOP = 2; static const int GUI_MODE_FOLLOWERMENU = 3; static const int GUI_MODE_SIGN = 4; +static const int GUI_MODE_CALLOUT = 5; extern int textscroll; extern int inventorycategory; @@ -1120,7 +1121,8 @@ enum CloseGUIIgnore : int CLOSEGUI_DONT_CLOSE_FOLLOWERGUI, CLOSEGUI_DONT_CLOSE_CHEST, CLOSEGUI_DONT_CLOSE_SHOP, - CLOSEGUI_DONT_CLOSE_INVENTORY + CLOSEGUI_DONT_CLOSE_INVENTORY, + CLOSEGUI_DONT_CLOSE_CALLOUTGUI }; static const int SCANCODE_UNASSIGNED_BINDING = 399; @@ -1331,6 +1333,175 @@ class FollowerRadialMenu }; extern FollowerRadialMenu FollowerMenu[MAXPLAYERS]; +struct CalloutRadialMenu +{ + int menuX; // starting mouse coordinates that are the center of the circle. + int menuY; // starting mouse coordinates that are the center of the circle. + int optionSelected; // current moused over option. + bool selectMoveTo; // player is choosing a point or target to interact with. + int moveToX; // x position for follower to move to. + int moveToY; // y position for follower to move to. + bool menuToggleClick; // user pressed menu key but did not select option before letting go. keeps the menu open without input. + bool holdWheel; // user pressed quick menu for last follower. + char interactText[128]; // user moused over object while selecting interact object. + int maxMonstersToDraw; + int gui_player = 0; + Frame* calloutFrame; + bool bOpen = false; + Uint32 lockOnEntityUid = 0; + + CalloutRadialMenu() : + calloutFrame(nullptr), + menuX(-1), + menuY(-1), + optionSelected(-1), + selectMoveTo(false), + moveToX(-1), + moveToY(-1), + menuToggleClick(false), + holdWheel(false), + bOpen(false) + { + memset(interactText, 0, 128); + } + + void createCalloutMenuGUI(); + bool calloutGUIHasBeenCreated() const; + static void loadCalloutJSON(); + enum PanelDirections : int + { + NORTH, + NORTHWEST, + WEST, + SOUTHWEST, + SOUTH, + SOUTHEAST, + EAST, + NORTHEAST, + PANEL_DIRECTION_END + }; + struct PanelEntry + { + int x = 0; + int y = 0; + std::string path = ""; + std::string path_hover = ""; + int icon_offsetx = 0; + int icon_offsety = 0; + }; + static std::vector panelEntries; + struct IconEntry + { + std::string name = ""; + int id = -1; + std::string path = ""; + std::string path_hover = ""; + std::string path_active = ""; + std::string path_active_hover = ""; + int icon_offsetx = 0; + int icon_offsety = 0; + std::map>> text_map; + }; + static std::map iconEntries; + static int followerWheelRadius; + static int followerWheelButtonThickness; + static int followerWheelFrameOffsetX; + static int followerWheelFrameOffsetY; + static int followerWheelInnerCircleRadiusOffset; + static int followerWheelInnerCircleRadiusOffsetAlternate; + + enum CalloutCommand : int + { + CALLOUT_CMD_LOOK, + CALLOUT_CMD_HELP, + CALLOUT_CMD_NEGATIVE, + CALLOUT_CMD_SOUTHWEST, + CALLOUT_CMD_SOUTH, + CALLOUT_CMD_SOUTHEAST, + CALLOUT_CMD_AFFIRMATIVE, + CALLOUT_CMD_MOVE, + CALLOUT_CMD_CANCEL, + CALLOUT_CMD_SELECT, + CALLOUT_CMD_END + }; + enum CalloutType : int + { + CALLOUT_TYPE_NO_TARGET, + CALLOUT_TYPE_NPC, + CALLOUT_TYPE_PLAYER, + CALLOUT_TYPE_LEVER, + CALLOUT_TYPE_BOULDER, + CALLOUT_TYPE_TRAP, + CALLOUT_TYPE_GENERIC_INTERACTABLE, + CALLOUT_TYPE_CHEST, + CALLOUT_TYPE_ITEM, + CALLOUT_TYPE_NPC_ENEMY, + CALLOUT_TYPE_NPC_PLAYERALLY + }; + struct CalloutParticle_t + { + real_t x = 0.0; + real_t y = 0.0; + real_t z = 0.0; + Uint32 entityUid = 0; + Uint32 ticks = 0; + CalloutCommand cmd = CALLOUT_CMD_END; + CalloutType type = CALLOUT_TYPE_NO_TARGET; + bool expired = false; + bool lockOnScreen = true; + void init(const int player); + CalloutParticle_t() = default; + CalloutParticle_t(const int player, real_t _x, real_t _y, real_t _z, Uint32 _uid, CalloutCommand _cmd) : + x(_x), + y(_y), + z(_z), + entityUid(_uid), + cmd(_cmd) + { + init(player); + }; + static Uint32 kParticleLifetime; + }; + std::map callouts; + + real_t animTitle = 0.0; + real_t animWheel = 0.0; + Uint32 openedThisTick = 0; + real_t animInvalidAction = 0.0; + Uint32 animInvalidActionTicks = 0; + Uint32 updatedThisTick = 0; + + bool calloutMenuIsOpen(); + void drawCalloutMenu(); + void initCalloutMenuGUICursor(bool openInventory); + void closeCalloutMenuGUI(); + bool allowedInteractEntity(Entity& selectedEntity, bool updateInteractText = true); + void createParticleCallout(real_t x, real_t y, real_t z, Uint32 uid, CalloutCommand _cmd = CALLOUT_CMD_LOOK); + void createParticleCallout(Entity* entity, CalloutCommand _cmd = CALLOUT_CMD_LOOK); + static std::string getIconPathForCommand(CalloutCommand cmd, CalloutType type, bool highlight); + void setCalloutBannerText(Field* field, const char* iconName, Uint32 color, CalloutCommand cmd); + static CalloutType getCalloutTypeForUid(const int player, Uint32 uid); + static CalloutType getCalloutTypeForEntity(const int player, Entity* parent); + /*void selectNextFollower(); + int numMonstersToDrawInParty(); + void updateScrollPartySheet(); + int optionDisabledForCreature(int playerSkillLVL, int monsterType, int option, Entity* follower); + bool allowedClassToggle(int monsterType); + bool allowedItemPickupToggle(int monsterType); + static bool allowedInteractFood(int monsterType); + static bool allowedInteractWorld(int monsterType); + bool allowedInteractItems(int monsterType); + bool attackCommandOnly(int monsterType); + void monsterGyroBotConvertCommand(int* option); + bool monsterGyroBotOnlyCommand(int option); + bool monsterGyroBotDisallowedCommands(int option); + bool isTinkeringFollower(int type);*/ + void setPlayer(const int p) { gui_player = p; } + const int getPlayer() const { return gui_player; } + void update(); +}; +extern CalloutRadialMenu CalloutMenu[MAXPLAYERS]; + std::string getItemSpritePath(const int player, Item& item); enum ItemContextMenuPrompts { diff --git a/src/interface/playerinventory.cpp b/src/interface/playerinventory.cpp index 45dbdd9a2..468611596 100644 --- a/src/interface/playerinventory.cpp +++ b/src/interface/playerinventory.cpp @@ -7536,7 +7536,7 @@ void Player::Inventory_t::updateInventory() if ( inputs.hasController(player) ) { - bool radialMenuOpen = FollowerMenu[player].followerMenuIsOpen(); + bool radialMenuOpen = FollowerMenu[player].followerMenuIsOpen() || CalloutMenu[player].calloutMenuIsOpen(); if ( radialMenuOpen ) { // do nothing? diff --git a/src/menu.cpp b/src/menu.cpp index af3e7a4f8..d94ba1276 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -9543,6 +9543,7 @@ void doNewGame(bool makeHighscore) { { // clear follower menu entities. FollowerMenu[i].closeFollowerMenuGUI(true); + CalloutMenu[i].closeCalloutMenuGUI(); } for ( int c = 0; c < NUMMONSTERS; c++ ) { @@ -9699,6 +9700,7 @@ void doNewGame(bool makeHighscore) { FollowerMenu[i].recentEntity = nullptr; FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; + CalloutMenu[i].closeCalloutMenuGUI(); } for ( int i = 0; i < MAXPLAYERS; ++i ) @@ -9972,6 +9974,7 @@ void doNewGame(bool makeHighscore) { FollowerMenu[i].recentEntity = nullptr; FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; + CalloutMenu[i].closeCalloutMenuGUI(); } client_disconnected[0] = false; @@ -10695,6 +10698,7 @@ void doEndgame(bool saveHighscore) { FollowerMenu[i].recentEntity = nullptr; FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; + CalloutMenu[i].closeCalloutMenuGUI(); } // load menu level diff --git a/src/net.cpp b/src/net.cpp index 9975d0411..3071adfaf 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1983,6 +1983,7 @@ static void changeLevel() { FollowerMenu[i].recentEntity = nullptr; FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; + CalloutMenu[i].closeCalloutMenuGUI(); } // stop all sounds @@ -2103,6 +2104,7 @@ static void changeLevel() { // clear follower menu entities. FollowerMenu[clientnum].closeFollowerMenuGUI(true); + CalloutMenu[clientnum].closeCalloutMenuGUI(); // load map file loading = true; diff --git a/src/player.cpp b/src/player.cpp index 01784044e..8df72e622 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -299,7 +299,7 @@ void GameController::handleAnalog(int player) //Right analog stick = look. - bool radialMenuOpen = FollowerMenu[player].followerMenuIsOpen(); + bool radialMenuOpen = FollowerMenu[player].followerMenuIsOpen() || CalloutMenu[player].calloutMenuIsOpen(); if ( !radialMenuOpen ) { consumeDpadDirToggle(); @@ -1210,6 +1210,7 @@ bool Player::GUI_t::bModuleAccessibleWithMouse(GUIModules moduleToAccess) if ( player.bookGUI.bBookOpen || player.skillSheet.bSkillSheetOpen || player.signGUI.bSignOpen || FollowerMenu[player.playernum].followerMenuIsOpen() + || CalloutMenu[player.playernum].calloutMenuIsOpen() || player.minimap.mapWindow || player.messageZone.logWindow ) { return false; @@ -3189,10 +3190,17 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) real_t maxDist = 24.0; real_t minDist = 4.0; - bool followerSelectInteract = false; + bool selectInteract = false; + bool callout = false; if ( FollowerMenu[player.playernum].followerMenuIsOpen() && FollowerMenu[player.playernum].selectMoveTo ) { - followerSelectInteract = (FollowerMenu[player.playernum].optionSelected == ALLY_CMD_ATTACK_SELECT); + selectInteract = (FollowerMenu[player.playernum].optionSelected == ALLY_CMD_ATTACK_SELECT); + maxDist = 256; + } + else if ( CalloutMenu[player.playernum].calloutMenuIsOpen() && CalloutMenu[player.playernum].selectMoveTo ) + { + selectInteract = (CalloutMenu[player.playernum].optionSelected == CalloutRadialMenu::CALLOUT_CMD_SELECT); + callout = true; maxDist = 256; } else if ( parent @@ -3233,11 +3241,21 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) real_t interactAngle = (PI / 8); if ( parent ) { - if ( followerSelectInteract ) + if ( selectInteract ) { - if ( !FollowerMenu[player.playernum].allowedInteractEntity(*parent, false) ) + if ( FollowerMenu[player.playernum].followerMenuIsOpen() && FollowerMenu[player.playernum].selectMoveTo ) { - return 0.0; + if ( !FollowerMenu[player.playernum].allowedInteractEntity(*parent, false) ) + { + return 0.0; + } + } + else if ( CalloutMenu[player.playernum].calloutMenuIsOpen() && CalloutMenu[player.playernum].selectMoveTo ) + { + if ( !CalloutMenu[player.playernum].allowedInteractEntity(*parent, false) ) + { + return 0.0; + } } Entity* ohitentity = hit.entity; @@ -3251,8 +3269,12 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) } hit.entity = ohitentity; } + else if ( parent->behavior == &actBoulderTrapHole ) + { + return 0.0; + } - if ( !followerSelectInteract && stats[player.playernum] && stats[player.playernum]->defending ) + if ( !selectInteract && stats[player.playernum] && stats[player.playernum]->defending ) { if ( stats[player.playernum]->shield && stats[player.playernum]->shield->type == TOOL_TINKERING_KIT ) { @@ -3314,8 +3336,15 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) { dist = std::max(0.02, dist - 4.0); // bonus priority for goldbag } + else if ( (parent->behavior == &actTorch || parent->behavior == &actCrystalShard) ) + { + if ( callout ) + { + dist += 8.0; // distance penalty when calling out + } + } - if ( followerSelectInteract ) + if ( selectInteract ) { if ( parent->behavior == &actMonster && ((multiplayer != CLIENT && parent->checkEnemy(player.entity)) @@ -3348,7 +3377,7 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) if ( (abs(tangent - playerYaw) < (interactAngle)) || (abs(tangent - playerYaw) > (2 * PI - interactAngle)) ) { //messagePlayer(0, "%.2f", tangent - playerYaw); - if ( !followerSelectInteract ) + if ( !selectInteract ) { Entity* ohitentity = hit.entity; real_t tangent2 = atan2(players[player.playernum]->entity->y - tooltip.y, players[player.playernum]->entity->x - tooltip.x); @@ -3363,7 +3392,7 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) return dist; } - if ( followerSelectInteract ) + if ( selectInteract ) { // perform head pitch check if ( false ) @@ -3413,6 +3442,65 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) particle->y = previousy; particle->z = 7.5;*/ } + else if ( parent && parent->behavior == &actBoulderTrapHole ) + { + // more accurate line of sight, look up + real_t startx = cameras[player.playernum].x * 16.0; + real_t starty = cameras[player.playernum].y * 16.0; + real_t startz = cameras[player.playernum].z + (4.5 - cameras[player.playernum].z) / 2.0 + -2.5; + real_t pitch = cameras[player.playernum].vang; + if ( pitch < PI ) // looking down + { + return 0.0; + } + + // draw line from the players height and direction until we hit the ground. + real_t previousx = startx; + real_t previousy = starty; + int index = 0; + const real_t yaw = cameras[player.playernum].ang; + + bool onCeilingLayer = parent->z > -11.0 && parent->z < -10; + real_t endz = onCeilingLayer ? -8.0 : -8.0 - 16.0; + for ( ; startz > endz; startz -= abs((0.1) * tan(pitch)) ) + { + startx += 0.1 * cos(yaw); + starty += 0.1 * sin(yaw); + const int index_x = static_cast(startx) >> 4; + const int index_y = static_cast(starty) >> 4; + index = (index_y)*MAPLAYERS + (index_x)*MAPLAYERS * map.height; + if ( !map.tiles[(OBSTACLELAYER) + index] ) + { + // store the last known good coordinate + previousx = startx;// + 16 * cos(yaw); + previousy = starty;// + 16 * sin(yaw); + } + if ( map.tiles[OBSTACLELAYER + index] ) + { + break; + } + if ( map.tiles[(OBSTACLELAYER + 1) + index] && !onCeilingLayer ) + { + break; + } + } + + Entity* particle = spawnMagicParticle(players[player.playernum]->entity); + particle->sprite = 942; + particle->x = previousx; + particle->y = previousy; + particle->z = startz; + + real_t lookDist = sqrt(pow(previousx - players[player.playernum]->entity->x, 2) + pow(previousy - players[player.playernum]->entity->y, 2)); + if ( abs(dist - lookDist) > 8.25 ) + { + return 0.0; // looking at a tile on the ground more than x units away from the tooltip + } + else if ( abs(startz - endz) > 0.25 ) + { + return 0.0; // if we're not fixated on the tile of the object return + } + } else { // more accurate line of sight @@ -3450,7 +3538,37 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) } real_t lookDist = sqrt(pow(previousx - players[player.playernum]->entity->x, 2) + pow(previousy - players[player.playernum]->entity->y, 2)); - if ( lookDist < dist ) + if ( callout ) + { + if ( parent ) + { + if ( cameras[player.playernum].vang > PI ) // looking above horizon + { + if ( parent->behavior == &actItem || parent->behavior == &actGoldBag + || parent->behavior == &actSwitch || parent->behavior == &actSwitchWithTimer ) + { + return 0.0; + } + } + } + if ( (lookDist < dist) ) + { + if ( abs(dist - lookDist) > 16.0 ) + { + return 0.0; // looking at a tile on the ground more than x units away from the tooltip + } + } + else if ( (parent->behavior == &actItem && parent->z > 4) || parent->behavior == &actGoldBag + || parent->behavior == &actSwitch || parent->behavior == &actSwitchWithTimer ) + { + if ( dist < 32.0 && abs(dist - lookDist) > 16.0 + || dist >= 32.0 && abs(dist - lookDist) > 24.0 ) + { + return 0.0; // looking at a tile on the ground more than x units away from the tooltip + } + } + } + else if ( lookDist < dist ) { if ( abs(dist - lookDist) > 24.0 ) { @@ -3510,6 +3628,12 @@ void Player::WorldUI_t::setTooltipActive(Entity& tooltip) { FollowerMenu[player.playernum].allowedInteractEntity(*parent, true); } + else if ( CalloutMenu[player.playernum].calloutMenuIsOpen() + && CalloutMenu[player.playernum].selectMoveTo + && CalloutMenu[player.playernum].optionSelected == CalloutRadialMenu::CALLOUT_CMD_SELECT ) + { + CalloutMenu[player.playernum].allowedInteractEntity(*parent, true); + } else if ( parent->behavior == &actItem ) { if ( foundTinkeringKit ) @@ -3951,6 +4075,7 @@ void Player::WorldUI_t::handleTooltips() if ( !players[player]->isLocalPlayerAlive() ) { players[player]->worldUI.reset(); + players[player]->worldUI.tooltipView = Player::WorldUI_t::TooltipView::TOOLTIP_VIEW_RESCAN; continue; } if ( players[player]->entity && players[player]->entity->ticks < TICKS_PER_SECOND / 2 ) @@ -3993,7 +4118,7 @@ void Player::WorldUI_t::handleTooltips() bool foundTinkeringKit = false; bool radialMenuOpen = FollowerMenu[player].followerMenuIsOpen(); - bool followerSelectInteract = false; + bool selectInteract = false; if ( radialMenuOpen ) { // follower menu can be "open" but selectMoveTo == true means the GUI is closed and selecting move or interact. @@ -4002,7 +4127,21 @@ void Player::WorldUI_t::handleTooltips() radialMenuOpen = true; } radialMenuOpen = false; - followerSelectInteract = (FollowerMenu[player].optionSelected == ALLY_CMD_ATTACK_SELECT); + selectInteract = (FollowerMenu[player].optionSelected == ALLY_CMD_ATTACK_SELECT); + } + else + { + radialMenuOpen = CalloutMenu[player].calloutMenuIsOpen(); + if ( radialMenuOpen ) + { + // callout menu can be "open" but selectMoveTo == true means the GUI is closed and selecting interact. + if ( CalloutMenu[player].selectMoveTo == false ) + { + radialMenuOpen = true; + } + radialMenuOpen = false; + selectInteract = (CalloutMenu[player].optionSelected == CalloutRadialMenu::CALLOUT_CMD_SELECT); + } } bool bDoingActionHideTooltips = false; @@ -4043,7 +4182,7 @@ void Player::WorldUI_t::handleTooltips() else { bDoingActionHideTooltips = true; - if ( FollowerMenu[player].selectMoveTo ) + if ( FollowerMenu[player].selectMoveTo || CalloutMenu[player].selectMoveTo ) { bDoingActionHideTooltips = false; // selecting follower target is OK if defending } @@ -4057,7 +4196,7 @@ void Player::WorldUI_t::handleTooltips() players[player]->entity->yaw, STRIKERANGE, 0, true); if ( hit.entity ) { - if ( hit.entity->behavior == &actMonster && followerSelectInteract ) + if ( hit.entity->behavior == &actMonster && selectInteract ) { // don't let hostile monsters get in the way of selection } @@ -4145,7 +4284,7 @@ void Player::WorldUI_t::handleTooltips() if ( newDist > 0.01 ) { players[player]->worldUI.tooltipsInRange.push_back(std::make_pair(tooltip, newDist)); - if ( followerSelectInteract && parent && closestTooltip && parent->behavior != &actMonster ) + if ( selectInteract && parent && closestTooltip && parent->behavior != &actMonster ) { // follower interaction - monsters have higher priority than interactibles. Entity* closestParent = uidToEntity(closestTooltip->parent); @@ -4164,6 +4303,7 @@ void Player::WorldUI_t::handleTooltips() if ( closestTooltip ) { players[player]->worldUI.playerLastYaw = players[player]->entity->yaw; + players[player]->worldUI.playerLastPitch = players[player]->camera().vang; while ( players[player]->worldUI.playerLastYaw >= 4 * PI ) { players[player]->worldUI.playerLastYaw -= 2 * PI; @@ -4172,6 +4312,14 @@ void Player::WorldUI_t::handleTooltips() { players[player]->worldUI.playerLastYaw += 2 * PI; } + while ( players[player]->worldUI.playerLastPitch >= 4 * PI ) + { + players[player]->worldUI.playerLastPitch -= 2 * PI; + } + while ( players[player]->worldUI.playerLastPitch < 2 * PI ) + { + players[player]->worldUI.playerLastPitch += 2 * PI; + } players[player]->worldUI.setTooltipActive(*closestTooltip); } std::sort(players[player]->worldUI.tooltipsInRange.begin(), players[player]->worldUI.tooltipsInRange.end(), @@ -4208,6 +4356,16 @@ void Player::WorldUI_t::handleTooltips() currentYaw += 2 * PI; } real_t yawDiff = players[player]->worldUI.playerLastYaw - currentYaw; + real_t currentPitch = players[player]->camera().vang; + while ( currentPitch >= 4 * PI ) + { + currentPitch -= 2 * PI; + } + while ( currentPitch < 2 * PI ) + { + currentPitch += 2 * PI; + } + real_t pitchDiff = players[player]->worldUI.playerLastPitch - currentPitch; if ( inputs.hasController(player) ) { real_t floatx = inputs.getController(player)->getLeftXPercent(); @@ -4230,11 +4388,20 @@ void Player::WorldUI_t::handleTooltips() continue; } } - if ( abs(yawDiff) > PI / 16 ) + if ( abs(yawDiff) > PI / 16 || abs(pitchDiff) > PI / 64 ) { players[player]->worldUI.tooltipView = TOOLTIP_VIEW_RESCAN; continue; } + if ( selectInteract && CalloutMenu[player].calloutMenuIsOpen() ) + { + if ( (players[player]->worldUI.playerLastPitch < PI && players[player]->camera().vang >= PI) + || (players[player]->worldUI.playerLastPitch >= PI && players[player]->camera().vang < PI) ) + { + players[player]->worldUI.tooltipView = TOOLTIP_VIEW_RESCAN; + continue; + } + } if ( FollowerMenu[player].selectMoveTo && FollowerMenu[player].optionSelected == ALLY_CMD_MOVETO_SELECT ) { // rescan constantly @@ -6369,6 +6536,7 @@ void Player::clearGUIPointers() playerInventoryFrames[playernum].autosortFrame = nullptr; FollowerMenu[playernum].followerFrame = nullptr; + CalloutMenu[playernum].calloutFrame = nullptr; } const char* Player::getAccountName() const { diff --git a/src/player.hpp b/src/player.hpp index 2db080829..4b1a81d99 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -1813,13 +1813,13 @@ class Player bool bEnabled = true; static const int UID_TOOLTIP_ACTIVE = -21; static const int UID_TOOLTIP_DISABLED = -20; + public: enum TooltipView { TOOLTIP_VIEW_FREE, TOOLTIP_VIEW_LOCKED, TOOLTIP_VIEW_RESCAN }; - public: struct WorldTooltipItem_t { Player& player; @@ -1951,6 +1951,7 @@ class Player std::vector> tooltipsInRange; static real_t tooltipHeightOffsetZ; real_t playerLastYaw = 0.0; + real_t playerLastPitch = 0.0; int gimpDisplayTimer = 0; void reset(); void setTooltipActive(Entity& tooltip); diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 611ea3b26..9a3370590 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -341,6 +341,28 @@ void camelCaseString(std::string& str) } } +bool stringStartsWithVowel(std::string& str) +{ + if ( str.size() < 1 ) { return false; } + switch ( str[0] ) + { + case 'a': + case 'e': + case 'i': + case 'o': + case 'u': + case 'A': + case 'E': + case 'I': + case 'O': + case 'U': + return true; + default: + break; + } + return false; +} + std::string EnemyBarSettings_t::getEnemyBarSpriteName(Entity* entity) { if ( !entity ) { return "default"; } @@ -2571,7 +2593,8 @@ void updateAllyBarFrame(const int player, Frame* baseFrame, int activeBars, int const int selectorAnimW = 4; if ( titleSelectorGlyph ) { - if ( halfWidthBars || (!players[player]->shootmode && !FollowerMenu[player].followerMenuIsOpen()) + if ( halfWidthBars + || (!players[player]->shootmode && !FollowerMenu[player].followerMenuIsOpen() && !CalloutMenu[player].calloutMenuIsOpen()) || followerDisplay.bCycleNextDisabled || list_Size(&stats[player]->FOLLOWERS) <= 1 ) { titleSelectorGlyph->disabled = true; @@ -2738,7 +2761,9 @@ void updateAllyFollowerFrame(const int player) return; } - if ( !players[player]->shootmode && !FollowerMenu[player].followerMenuIsOpen() && players[player]->gui_mode != GUI_MODE_NONE ) + if ( !players[player]->shootmode && !FollowerMenu[player].followerMenuIsOpen() + && !CalloutMenu[player].calloutMenuIsOpen() + && players[player]->gui_mode != GUI_MODE_NONE ) { baseFrame->setOpacity(0.0); baseFrame->setInheritParentFrameOpacity(false); @@ -3251,7 +3276,9 @@ void updateAllyPlayerFrame(const int player) return; } - if ( !players[player]->shootmode && !FollowerMenu[player].followerMenuIsOpen() && players[player]->gui_mode != GUI_MODE_NONE ) + if ( !players[player]->shootmode && !FollowerMenu[player].followerMenuIsOpen() + && !CalloutMenu[player].calloutMenuIsOpen() + && players[player]->gui_mode != GUI_MODE_NONE ) { baseFrame->setOpacity(0.0); baseFrame->setInheritParentFrameOpacity(false); @@ -3976,7 +4003,8 @@ void Player::HUD_t::updateUINavigation() bShowUINavigation = false; if ( player.gui_mode != GUI_MODE_NONE - && player.gui_mode != GUI_MODE_FOLLOWERMENU + && player.gui_mode != GUI_MODE_FOLLOWERMENU + && player.gui_mode != GUI_MODE_CALLOUT && player.gui_mode != GUI_MODE_SIGN && player.isLocalPlayer() && !player.shootmode ) { @@ -8349,6 +8377,7 @@ void Player::HUD_t::updateWorldTooltipPrompts() SDL_Rect promptPos{ player.camera_virtualWidth() / 2, player.camera_virtualHeight() / 2, 0, 0 }; FollowerRadialMenu& followerMenu = FollowerMenu[player.playernum]; + CalloutRadialMenu& calloutMenu = CalloutMenu[player.playernum]; auto icon = worldTooltipFrame->findImage("icon img"); icon->disabled = true; @@ -8386,10 +8415,14 @@ void Player::HUD_t::updateWorldTooltipPrompts() } } - if ( followerMenu.selectMoveTo && (followerMenu.optionSelected == ALLY_CMD_MOVETO_SELECT - || followerMenu.optionSelected == ALLY_CMD_ATTACK_SELECT) ) + bool followerInteract = followerMenu.selectMoveTo && (followerMenu.optionSelected == ALLY_CMD_MOVETO_SELECT + || followerMenu.optionSelected == ALLY_CMD_ATTACK_SELECT); + bool calloutInteract = calloutMenu.selectMoveTo && (calloutMenu.optionSelected == CalloutRadialMenu::CALLOUT_CMD_SELECT); + + if ( followerInteract || calloutInteract ) { bool forceBlankInteractText = false; + auto optionSelected = followerInteract ? followerMenu.optionSelected : calloutMenu.optionSelected; if ( !player.worldUI.isEnabled() ) { cursor->path = "#*images/ui/Crosshairs/cursor_xB.png"; @@ -8407,7 +8440,7 @@ void Player::HUD_t::updateWorldTooltipPrompts() } else { - if ( followerMenu.optionSelected == ALLY_CMD_MOVETO_SELECT ) + if ( optionSelected == ALLY_CMD_MOVETO_SELECT ) { cursor->path = "#*images/ui/Crosshairs/cursor_xB.png"; if ( auto imgGet = Image::get(cursor->path.c_str()) ) @@ -8596,7 +8629,7 @@ void Player::HUD_t::updateWorldTooltipPrompts() } - if ( followerMenu.optionSelected == ALLY_CMD_MOVETO_SELECT ) + if ( followerInteract && optionSelected == ALLY_CMD_MOVETO_SELECT ) { if ( followerMenu.followerToCommand && (followerMenu.followerToCommand->getMonsterTypeFromSprite() == SENTRYBOT @@ -8632,35 +8665,51 @@ void Player::HUD_t::updateWorldTooltipPrompts() } else { - if ( !strcmp(followerMenu.interactText, "") || forceBlankInteractText ) + if ( followerInteract ) { - if ( followerMenu.followerToCommand ) + if ( !strcmp(followerMenu.interactText, "") || forceBlankInteractText ) { - int type = followerMenu.followerToCommand->getMonsterTypeFromSprite(); - if ( followerMenu.allowedInteractItems(type) - || followerMenu.allowedInteractFood(type) - || followerMenu.allowedInteractWorld(type) - ) + if ( followerMenu.followerToCommand ) { - text->setDisabled(false); - text->setText(Language::get(4041)); // "Interact with..." + int type = followerMenu.followerToCommand->getMonsterTypeFromSprite(); + if ( followerMenu.allowedInteractItems(type) + || followerMenu.allowedInteractFood(type) + || followerMenu.allowedInteractWorld(type) + ) + { + text->setDisabled(false); + text->setText(Language::get(4041)); // "Interact with..." + } + else + { + text->setDisabled(false); + text->setText(Language::get(4042)); // "Attack..." + } } else { text->setDisabled(false); - text->setText(Language::get(4042)); // "Attack..." + text->setText(Language::get(4041)); // "Interact with..." } } else { text->setDisabled(false); - text->setText(Language::get(4041)); // "Interact with..." + text->setText(followerMenu.interactText); } } - else + else if ( calloutInteract ) { - text->setDisabled(false); - text->setText(followerMenu.interactText); + if ( !strcmp(calloutMenu.interactText, "") || forceBlankInteractText ) + { + text->setDisabled(false); + text->setText(Language::get(4348)); // "Call out..." + } + else + { + text->setDisabled(false); + text->setText(calloutMenu.interactText); + } } } @@ -9350,7 +9399,9 @@ void Player::HUD_t::updateActionPrompts() continue; } - if ( player.shootmode || player.gui_mode == GUI_MODE_FOLLOWERMENU || player.gui_mode == GUI_MODE_SIGN ) + if ( player.shootmode || player.gui_mode == GUI_MODE_FOLLOWERMENU + || player.gui_mode == GUI_MODE_SIGN + || player.gui_mode == GUI_MODE_CALLOUT ) { promptText->setDisabled(true); } @@ -9438,7 +9489,9 @@ void Player::HUD_t::updateActionPrompts() } glyph->path = Input::inputs[player.playernum].getGlyphPathForBinding(bindingName.c_str(), pressed); glyph->disabled = prompt->isDisabled(); - if ( !player.shootmode || player.gui_mode == GUI_MODE_FOLLOWERMENU || player.gui_mode == GUI_MODE_SIGN ) + if ( !player.shootmode || player.gui_mode == GUI_MODE_FOLLOWERMENU + || player.gui_mode == GUI_MODE_SIGN + || player.gui_mode == GUI_MODE_CALLOUT ) { glyph->disabled = true; } @@ -9573,6 +9626,132 @@ static void checkControllerState(int player) { controllerFrame->setHollow(true); } +void drawCallouts(const int playernum) +{ + for ( auto& callout : CalloutMenu[playernum].callouts ) + { + std::string iconPath = CalloutRadialMenu::getIconPathForCommand(callout.second.cmd, callout.second.type, + true); + if ( iconPath == "" ) + { + continue; + } + + vec4_t v; + mat4x4_t m, t; + + auto camera = &cameras[playernum]; + auto& player = players[playernum]; + + const int offset = 40; + int leftOfWindow = player->camera_virtualx1() + offset; + int rightOfWindow = player->camera_virtualx1() + player->camera_virtualWidth() - offset; + int topOfWindow = player->camera_virtualy1() + offset; + int bottomOfWindow = player->camera_virtualy2() - offset; + + mat4x4_t id; + vec4_t world{ (float)callout.second.x * 2.f, -(float)callout.second.z * 2.f, (float)callout.second.y * 2.f, 1.f }; + vec4_t window2{ (float)0, (float)0, + (float)player->camera_virtualWidth(), (float)player->camera_virtualHeight() }; + SDL_Rect dest{ 0, 0, 0, 0 }; + if ( callout.second.lockOnScreen ) + { + auto screen_position = project_clipped(&world, &id, &camera->projview, &window2); + dest = SDL_Rect{ player->camera_virtualx1() + (int)screen_position.clipped_coords.x, + player->camera_virtualy1() + Frame::virtualScreenY - (Frame::virtualScreenY - player->camera_virtualHeight()) - (int)screen_position.clipped_coords.y, + 14, 22 }; + if ( !screen_position.isBehind + && (screen_position.direction == ClipResult::Direction::Front + || screen_position.direction == ClipResult::Direction::Invalid) ) + { + callout.second.lockOnScreen = false; + } + + dest.x = std::min(rightOfWindow, std::max(leftOfWindow, dest.x)); + dest.y = std::min(bottomOfWindow, std::max(topOfWindow, dest.y)); + real_t tangent = atan2(camera->y * 32.0 - world.z, camera->x * 32.0 - world.x); + real_t camang = camera->ang; + while ( tangent >= 2 * PI ) + { + tangent -= PI * 2; + } + while ( tangent < 0 ) + { + tangent += PI * 2; + } + while ( camang >= 2 * PI ) + { + camang -= PI * 2; + } + while ( camang < 0 ) + { + camang += PI * 2; + } + real_t result = tangent - camang; + while ( result >= PI ) + { + result -= PI * 2; + } + while ( result < -PI ) + { + result += PI * 2; + } + //messagePlayer(player->playernum, MESSAGE_DEBUG, "%f", ((PI - abs(abs(tangent - camang) - PI)) * 2)); + //messagePlayer(player->playernum, MESSAGE_DEBUG, "%f", result); + if ( result >= 0.0 && result < PI / 2 ) + { + dest.x = leftOfWindow; + } + else if ( result < 0.0 && result > -PI / 2 ) + { + dest.x = rightOfWindow; + } + + if ( abs(result) < (3 * PI / 4) ) + { + real_t mult = std::min(1.0, ((3 * PI / 4) - abs(result)) / (PI / 2)); + dest.y += ((player->camera_virtualHeight() / 2) - dest.y) * mult; + } + } + else + { + auto screen_position = project(&world, &id, &camera->projview, &window2); + if ( screen_position.z >= 1.0 || screen_position.z < 0.0 ) + { + continue; + } + dest = SDL_Rect{ player->camera_virtualx1() + (int)screen_position.x, + player->camera_virtualy1() + Frame::virtualScreenY - (Frame::virtualScreenY - player->camera_virtualHeight()) - + (int)screen_position.y, + 14, 22 }; + } + + real_t lifePercent = callout.second.ticks / (real_t)CalloutRadialMenu::CalloutParticle_t::kParticleLifetime; + Uint32 alpha = 255; + if ( lifePercent >= 0.8 ) + { + alpha -= std::min((Uint32)255, (Uint32)(255 * (lifePercent - 0.8) / 0.2)); + } + Uint32 color = makeColor(255, 255, 255, alpha); + SDL_Rect iconPos = dest; + if ( auto image = Image::get(iconPath.c_str()) ) + { + iconPos.w = image->getWidth(); + iconPos.h = image->getHeight(); + iconPos.x -= iconPos.w / 2; + iconPos.y -= iconPos.h; + image->drawColor(nullptr, iconPos, + SDL_Rect{ 0, 0, Frame::virtualScreenX, Frame::virtualScreenY }, color); + } + + dest.x -= dest.w / 2; + dest.y -= dest.h / 2; + auto image = Image::get("*#images/ui/CalloutWheel/WorldIcons/cmd_playertagRed_sm.png"); + image->drawColor(nullptr, dest, + SDL_Rect{ 0, 0, Frame::virtualScreenX, Frame::virtualScreenY }, color); + } +} + void Player::HUD_t::processHUD() { const SDL_Rect hudSize{ @@ -9589,6 +9768,9 @@ void Player::HUD_t::processHUD() hudFrame->setHollow(true); hudFrame->setBorder(0); hudFrame->setOwner(player.playernum); + hudFrame->setDrawCallback([](const Widget& widget, SDL_Rect rect) { + drawCallouts(widget.getOwner()); + }); } if ( !minotaurSharedDisplay && player.playernum == 0 ) @@ -26103,6 +26285,7 @@ void Player::HUD_t::updateGameTimer() bool overrideGameTimerSetting = false; if ( splitscreen && !(player.bUseCompactGUIHeight() && player.bUseCompactGUIWidth()) && !player.shootmode && !FollowerMenu[player.playernum].followerMenuIsOpen() + && !CalloutMenu[player.playernum].calloutMenuIsOpen() && player.gui_mode != GUI_MODE_NONE ) { if ( compactLayoutMode == COMPACT_LAYOUT_INVENTORY || player.bUseCompactGUIWidth() ) @@ -26219,7 +26402,8 @@ void Player::HUD_t::updateXPBar() { tempHideXP = true; } - else if ( (player.gui_mode == GUI_MODE_FOLLOWERMENU || player.minimap.mapWindow || player.messageZone.logWindow) + else if ( (player.gui_mode == GUI_MODE_FOLLOWERMENU || player.gui_mode == GUI_MODE_CALLOUT + || player.minimap.mapWindow || player.messageZone.logWindow) && player.bUseCompactGUIHeight() ) { tempHideXP = true; @@ -28893,6 +29077,7 @@ void Player::Hotbar_t::updateHotbar() bool tempHideHotbar = false; if ( player.bUseCompactGUIHeight() && (player.gui_mode == GUI_MODE_FOLLOWERMENU + || player.gui_mode == GUI_MODE_CALLOUT || (player.hud.compactLayoutMode == Player::HUD_t::COMPACT_LAYOUT_CHARSHEET && !player.shootmode) || (player.gui_mode == GUI_MODE_MAGIC) || (player.shopGUI.bOpen) @@ -28966,7 +29151,8 @@ void Player::Hotbar_t::updateHotbar() hotbarStartY1 += animHide * abs(getHotbarStartY1()); hotbarStartY2 += animHide * abs(getHotbarStartY1()); - if ( !player.shootmode || FollowerMenu[player.playernum].followerMenuIsOpen() ) + if ( !player.shootmode || FollowerMenu[player.playernum].followerMenuIsOpen() + || CalloutMenu[player.playernum].calloutMenuIsOpen() ) { if (player.hotbar.useHotbarFaceMenu) { @@ -33542,8 +33728,8 @@ void Player::Inventory_t::SpellPanel_t::updateSpellPanel() { if ( bOpen ) { - slider->setDisabled(false); - } + slider->setDisabled(false); + } else { slider->setDisabled(true); diff --git a/src/ui/GameUI.hpp b/src/ui/GameUI.hpp index 375d190fe..4b95aa384 100644 --- a/src/ui/GameUI.hpp +++ b/src/ui/GameUI.hpp @@ -316,6 +316,7 @@ void openLogWindow(int player); void capitalizeString(std::string& str); void uppercaseString(std::string& str); void camelCaseString(std::string& str); +bool stringStartsWithVowel(std::string& str); struct MinotaurWarning_t { diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 0fe06601a..8d55b926e 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -114,6 +114,7 @@ namespace MainMenu { {"Spell List", "B", hiddenBinding, emptyBinding}, {"Skill Sheet", "K", hiddenBinding, emptyBinding}, {"Autosort Inventory", "R", hiddenBinding, emptyBinding}, + {"Show Player Callouts", "X", "DpadY+", emptyBinding}, {"Command NPC", "Q", "DpadX-", emptyBinding}, {"Show NPC Commands", "C", "DpadX+", emptyBinding}, {"Cycle NPCs", "E", "DpadY-", emptyBinding}, @@ -173,6 +174,7 @@ namespace MainMenu { {"Spell List", "B", hiddenBinding, emptyBinding}, {"Skill Sheet", "K", hiddenBinding, emptyBinding}, {"Autosort Inventory", "R", hiddenBinding, emptyBinding}, + {"Show Player Callouts", "X", "DpadY+", emptyBinding}, {"Command NPC", "Q", "DpadX-", emptyBinding}, {"Show NPC Commands", "C", "DpadX+", emptyBinding}, {"Cycle NPCs", "E", "DpadY-", emptyBinding}, @@ -232,6 +234,7 @@ namespace MainMenu { {"Spell List", "B", hiddenBinding, emptyBinding}, {"Skill Sheet", "K", hiddenBinding, emptyBinding}, {"Autosort Inventory", "R", hiddenBinding, emptyBinding}, + {"Show Player Callouts", "X", "DpadY+", emptyBinding}, #ifdef NINTENDO {"Command NPC", "Q", "ButtonY", emptyBinding}, {"Show NPC Commands", "C", "ButtonX", emptyBinding}, @@ -291,6 +294,7 @@ namespace MainMenu { {"Spell List", "B", hiddenBinding, emptyBinding}, {"Skill Sheet", "K", hiddenBinding, emptyBinding}, {"Autosort Inventory", "R", hiddenBinding, emptyBinding}, + {"Show Player Callouts", "X", emptyBinding, emptyBinding}, {"Command NPC", "Q", emptyBinding, emptyBinding}, {"Show NPC Commands", "C", emptyBinding, emptyBinding}, {"Cycle NPCs", "E", emptyBinding, emptyBinding}, From 90e1374e3dec90be16741d13f5b363275273bf3f Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 12 Aug 2023 00:54:25 +1000 Subject: [PATCH 002/146] * project_clipped for callouts --- src/draw.cpp | 20 +++---- src/draw.hpp | 5 ++ src/opengl.cpp | 132 ++++++++++++++++++++++++++++++++++++++++++++++ src/ui/GameUI.cpp | 2 +- 4 files changed, 148 insertions(+), 11 deletions(-) diff --git a/src/draw.cpp b/src/draw.cpp index bba67d1b1..7717be608 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -533,16 +533,16 @@ void createCommonDrawResources() { sprite_vertex_glsl, sizeof(sprite_vertex_glsl), sprite_dithered_fragment_glsl, sizeof(sprite_dithered_fragment_glsl)); - static const char sprite_bright_vertex_glsl[] = - "in vec3 iPosition;" - "in vec2 iTexCoord;" - "uniform mat4 uProj;" - "uniform mat4 uView;" - "uniform mat4 uModel;" - "out vec2 TexCoord;" - - "void main() {" - "gl_Position = uProj * uView * uModel * vec4(iPosition, 1.0);" + static const char sprite_bright_vertex_glsl[] = + "in vec3 iPosition;" + "in vec2 iTexCoord;" + "uniform mat4 uProj;" + "uniform mat4 uView;" + "uniform mat4 uModel;" + "out vec2 TexCoord;" + + "void main() {" + "gl_Position = uProj * uView * uModel * vec4(iPosition, 1.0);" "TexCoord = iTexCoord;" "}"; diff --git a/src/draw.hpp b/src/draw.hpp index 42595cf33..ed97a5934 100644 --- a/src/draw.hpp +++ b/src/draw.hpp @@ -60,6 +60,11 @@ ClipResult project_clipped( const mat4x4_t* model, const mat4x4_t* projview, const vec4_t* window); +ClipResult project_clipped2( // project_clipped, but will draw mirroed behind camera + const vec4_t* world, + const mat4x4_t* model, + const mat4x4_t* projview, + const vec4_t* window); vec4_t unproject( const vec4_t* screenCoords, const mat4x4_t* model, diff --git a/src/opengl.cpp b/src/opengl.cpp index 88fbcb6af..a24ab7baf 100644 --- a/src/opengl.cpp +++ b/src/opengl.cpp @@ -406,6 +406,138 @@ vec4_t project( return result; } +ClipResult project_clipped( + const vec4_t* world, + const mat4x4_t* model, + const mat4x4_t* projview, + const vec4_t* window +) { + ClipResult clipResult; + vec4_t& result = clipResult.clipped_coords; + result = *world; result.w = 1.f; + clipResult.isBehind = false; + + vec4 copy; + copy = vec4_copy(&result); mul_mat_vec4(&result, model, ©); + copy = vec4_copy(&result); mul_mat_vec4(&result, projview, ©); + + float w = result.w; + if ( w < CLIPNEAR ) { + w = CLIPNEAR; + result.x *= CLIPFAR; + result.y *= CLIPFAR; + if ( result.z < -w ) { + clipResult.direction = ClipResult::Direction::Behind; + clipResult.isBehind = true; + } + } + if ( result.x > w ) { + const float factor = w / result.x; + pow_vec4(&result, &result, factor); + clipResult.direction = ClipResult::Direction::Right; + } + else if ( result.x < -w ) { + const float factor = -w / result.x; + pow_vec4(&result, &result, factor); + clipResult.direction = ClipResult::Direction::Left; + } + if ( result.y > w ) { + const float factor = w / result.y; + pow_vec4(&result, &result, factor); + clipResult.direction = ClipResult::Direction::Top; + } + else if ( result.y < -w ) { + const float factor = -w / result.y; + pow_vec4(&result, &result, factor); + clipResult.direction = ClipResult::Direction::Bottom; + } + if ( result.z > w ) { + const float factor = w / result.z; + pow_vec4(&result, &result, factor); + clipResult.direction = ClipResult::Direction::Front; + } + else if ( result.z < -w ) { + const float factor = -w / result.z; + pow_vec4(&result, &result, factor); + clipResult.direction = ClipResult::Direction::Behind; + clipResult.isBehind = true; + } + + vec4 half(0.5f); + vec4 div(w); + div_vec4(&result, &result, &div); + mul_vec4(&result, &result, &half); + add_vec4(&result, &result, &half); + result.x = result.x * window->z + window->x; + result.y = result.y * window->w + window->y; + return clipResult; +} + +ClipResult project_clipped2( + const vec4_t* world, + const mat4x4_t* model, + const mat4x4_t* projview, + const vec4_t* window +) { + ClipResult clipResult; + vec4_t& result = clipResult.clipped_coords; + result = *world; result.w = 1.f; + clipResult.isBehind = false; + + vec4 copy; + copy = vec4_copy(&result); mul_mat_vec4(&result, model, ©); + copy = vec4_copy(&result); mul_mat_vec4(&result, projview, ©); + + float w = result.w; + if ( w < CLIPNEAR ) { + w = CLIPNEAR; + if ( result.z < -w ) { + clipResult.direction = ClipResult::Direction::Behind; + clipResult.isBehind = true; + } + } + if ( result.x > w ) { + const float factor = w / result.x; + pow_vec4(&result, &result, factor); + clipResult.direction = ClipResult::Direction::Right; + } + else if ( result.x < -w ) { + const float factor = -w / result.x; + pow_vec4(&result, &result, factor); + clipResult.direction = ClipResult::Direction::Left; + } + if ( result.y > w ) { + const float factor = w / result.y; + pow_vec4(&result, &result, factor); + clipResult.direction = ClipResult::Direction::Top; + } + else if ( result.y < -w ) { + const float factor = -w / result.y; + pow_vec4(&result, &result, factor); + clipResult.direction = ClipResult::Direction::Bottom; + } + if ( result.z > w ) { + const float factor = w / result.z; + pow_vec4(&result, &result, factor); + clipResult.direction = ClipResult::Direction::Front; + } + else if ( result.z < -w ) { + const float factor = -w / result.z; + pow_vec4(&result, &result, factor); + clipResult.direction = ClipResult::Direction::Behind; + clipResult.isBehind = true; + } + + vec4 half(0.5f); + vec4 div(w); + div_vec4(&result, &result, &div); + mul_vec4(&result, &result, &half); + add_vec4(&result, &result, &half); + result.x = result.x * window->z + window->x; + result.y = result.y * window->w + window->y; + return clipResult; +} + vec4_t unproject( const vec4_t* screenCoords, const mat4x4_t* model, diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 9a3370590..6bddb59f6 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -9656,7 +9656,7 @@ void drawCallouts(const int playernum) SDL_Rect dest{ 0, 0, 0, 0 }; if ( callout.second.lockOnScreen ) { - auto screen_position = project_clipped(&world, &id, &camera->projview, &window2); + auto screen_position = project_clipped2(&world, &id, &camera->projview, &window2); dest = SDL_Rect{ player->camera_virtualx1() + (int)screen_position.clipped_coords.x, player->camera_virtualy1() + Frame::virtualScreenY - (Frame::virtualScreenY - player->camera_virtualHeight()) - (int)screen_position.clipped_coords.y, 14, 22 }; From a8f664137a70dd01214a0efc48bb17a8d6bce7f8 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sun, 13 Aug 2023 03:05:33 +1000 Subject: [PATCH 003/146] * fix client classic victories --- src/net.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/net.cpp b/src/net.cpp index 3071adfaf..7fad31acd 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -4405,6 +4405,7 @@ static std::unordered_map clientPacketHandlers = { } } else if (net_packet->data[5] == 1) { // classic herx ending + victory = 1; switch ( race ) { default: case RACE_HUMAN: @@ -4427,6 +4428,7 @@ static std::unordered_map clientPacketHandlers = { } } else if (net_packet->data[5] == 2) { // classic baphomet ending + victory = 2; switch ( race ) { default: case RACE_HUMAN: From 295dbecac78fe595dde49f2cca52133bff63c4b5 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 15 Aug 2023 07:11:22 +1000 Subject: [PATCH 004/146] * call out stuff --- src/interface/interface.cpp | 149 ++++++++++++++++++++++++++++++++++-- src/player.cpp | 21 ++--- 2 files changed, 149 insertions(+), 21 deletions(-) diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index 036617e25..218e8a0b0 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -23616,14 +23616,15 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat { strcpy(interactText, Language::get(4347)); // "Callout " } - if ( selectedEntity.behavior == &actTorch && interactWorld ) + + /*if ( selectedEntity.behavior == &actTorch && interactWorld ) { if ( updateInteractText ) { strcat(interactText, items[TOOL_TORCH].getIdentifiedName()); } - } - else if ( (selectedEntity.behavior == &actSwitch || + }*/ + if ( (selectedEntity.behavior == &actSwitch || selectedEntity.behavior == &actSwitchWithTimer || selectedEntity.sprite == 184) && interactWorld ) { @@ -23661,14 +23662,100 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat } } } - else if ( selectedEntity.behavior == &actBomb && interactWorld ) + else if ( selectedEntity.behavior == &actLadder ) { if ( updateInteractText ) { - strcpy(interactText, Language::get(3093)); - strcat(interactText, Language::get(4045)); // "trap" + if ( secretlevel && selectedEntity.skill[3] == 1 ) // secret ladder + { + strcat(interactText, Language::get(4310)); // "ladder" + } + else if ( !secretlevel && selectedEntity.skill[3] == 1 ) // secret ladder + { + strcat(interactText, Language::get(4310)); // "ladder" + } + else + { + strcat(interactText, Language::get(4310)); // "ladder" + } } } + else if ( selectedEntity.behavior == &actPortal ) + { + if ( updateInteractText ) + { + if ( selectedEntity.skill[3] == 0 ) // secret entrance portal + { + if ( secretlevel ) + { + strcat(interactText, Language::get(4311)); // "portal" + } + else + { + strcat(interactText, Language::get(4311)); // "portal" + } + } + else + { + if ( !strcmp(map.name, "Hell") ) + { + strcat(interactText, Language::get(4311)); // "portal" + } + else if ( !strcmp(map.name, "Mages Guild") ) + { + strcat(interactText, Language::get(4311)); // "portal" + } + else + { + strcat(interactText, Language::get(4311)); // "portal" + } + } + } + } + else if ( selectedEntity.behavior == &::actMidGamePortal ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(4311)); // "portal" + } + } + else if ( selectedEntity.behavior == &actCustomPortal ) + { + if ( updateInteractText ) + { + if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_TUTORIAL ) + { + strcat(interactText, Language::get(4310)); // "ladder" + } + else + { + if ( selectedEntity.portalCustomSpriteAnimationFrames > 0 ) + { + strcat(interactText, Language::get(4311)); // "portal" + } + else + { + strcat(interactText, Language::get(4310)); // "ladder" + } + } + } + } + else if ( selectedEntity.behavior == &::actExpansionEndGamePortal + || selectedEntity.behavior == &actWinningPortal ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(4311)); // "portal" + } + } + //else if ( selectedEntity.behavior == &actBomb && interactWorld ) + //{ + // if ( updateInteractText ) + // { + // strcpy(interactText, Language::get(3093)); + // strcat(interactText, Language::get(4045)); // "trap" + // } + //} else if ( selectedEntity.behavior == &actBoulderTrapHole && interactWorld ) { @@ -23677,6 +23764,14 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat strcat(interactText, Language::get(4349)); // "trap" } } + else if ( selectedEntity.behavior == &actBoulder + && interactWorld ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(4358)); // "boulder" + } + } else if ( selectedEntity.behavior == &actItem && interactItems ) { if ( updateInteractText ) @@ -23698,6 +23793,48 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat } } } + else if ( selectedEntity.behavior == &actGoldBag ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(4353)); // "gold" + } + } + else if ( selectedEntity.behavior == &actFountain ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(4355)); // "fountain" + } + } + else if ( selectedEntity.behavior == &actSink ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(4354)); // "sink" + } + } + else if ( selectedEntity.behavior == &actHeadstone ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(4357)); // "grave" + } + } + else if ( selectedEntity.behavior == &actPowerCrystal || selectedEntity.behavior == &actPowerCrystalBase ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(4356)); // "crystal" + } + } + else if ( selectedEntity.behavior == &actChestLid || selectedEntity.behavior == &actChest ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(675)); // "chest" + } + } else if ( selectedEntity.behavior == &actMonster && enableAttack && selectedEntity.getMonsterTypeFromSprite() != GYROBOT ) { if ( updateInteractText ) diff --git a/src/player.cpp b/src/player.cpp index fcdbb79c9..885872770 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -3558,8 +3558,8 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) return 0.0; // looking at a tile on the ground more than x units away from the tooltip } } - else if ( (parent->behavior == &actItem && parent->z > 4) || parent->behavior == &actGoldBag - || parent->behavior == &actSwitch || parent->behavior == &actSwitchWithTimer ) + else if ( parent && ((parent->behavior == &actItem && parent->z > 4) || parent->behavior == &actGoldBag + || parent->behavior == &actSwitch || parent->behavior == &actSwitchWithTimer) ) { if ( dist < 32.0 && abs(dist - lookDist) > 16.0 || dist >= 32.0 && abs(dist - lookDist) > 24.0 ) @@ -4158,16 +4158,16 @@ void Player::WorldUI_t::handleTooltips() { bDoingActionHideTooltips = true; } - else if ( players[player]->hud.weapon && players[player]->hud.weapon->skill[0] != 0 ) + else if ( (players[player]->hud.weapon && players[player]->hud.weapon->skill[0] != 0) && !selectInteract ) { // hudweapon chop bDoingActionHideTooltips = true; } - else if ( players[player]->hud.bowFire || players[player]->hud.bowIsBeingDrawn ) + else if ( (players[player]->hud.bowFire || players[player]->hud.bowIsBeingDrawn) && !selectInteract ) { bDoingActionHideTooltips = true; } - else if ( cast_animation[player].active || cast_animation[player].active_spellbook ) + else if ( (cast_animation[player].active || cast_animation[player].active_spellbook) && !selectInteract ) { // spells bDoingActionHideTooltips = true; @@ -4182,7 +4182,7 @@ void Player::WorldUI_t::handleTooltips() else { bDoingActionHideTooltips = true; - if ( FollowerMenu[player].selectMoveTo || CalloutMenu[player].selectMoveTo ) + if ( selectInteract ) { bDoingActionHideTooltips = false; // selecting follower target is OK if defending } @@ -4393,15 +4393,6 @@ void Player::WorldUI_t::handleTooltips() players[player]->worldUI.tooltipView = TOOLTIP_VIEW_RESCAN; continue; } - if ( selectInteract && CalloutMenu[player].calloutMenuIsOpen() ) - { - if ( (players[player]->worldUI.playerLastPitch < PI && players[player]->camera().vang >= PI) - || (players[player]->worldUI.playerLastPitch >= PI && players[player]->camera().vang < PI) ) - { - players[player]->worldUI.tooltipView = TOOLTIP_VIEW_RESCAN; - continue; - } - } if ( FollowerMenu[player].selectMoveTo && FollowerMenu[player].optionSelected == ALLY_CMD_MOVETO_SELECT ) { // rescan constantly From 4a05d7eb37ef568cab1db1ab210fa391dbb9446d Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Wed, 16 Aug 2023 09:18:23 +1000 Subject: [PATCH 005/146] * automaton more likely to recycle items --- src/monster_automaton.cpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/monster_automaton.cpp b/src/monster_automaton.cpp index 549400644..61db81b88 100644 --- a/src/monster_automaton.cpp +++ b/src/monster_automaton.cpp @@ -1521,7 +1521,14 @@ void Entity::automatonRecycleItem() case 1: case 2: type = itemTypeWithinGoldValue(WEAPON, minGoldValue, maxGoldValue); - break; + if ( type == GEM_ROCK ) + { + // fall through, try again with next category + } + else + { + break; + } case 3: case 4: case 5: @@ -1529,7 +1536,14 @@ void Entity::automatonRecycleItem() case 7: case 8: type = itemTypeWithinGoldValue(ARMOR, minGoldValue, maxGoldValue); - break; + if ( type == GEM_ROCK ) + { + // fall through, try again with next category + } + else + { + break; + } case 9: type = itemTypeWithinGoldValue(THROWN, minGoldValue, maxGoldValue); break; @@ -1540,16 +1554,19 @@ void Entity::automatonRecycleItem() if ( type != GEM_ROCK ) // found an item in category { Item* item = nullptr; + Item* degraded = nullptr; // recycle item1 or item2, reduce durability. if ( local_rng.rand() % 2 == 0 ) { item = newItem(type, item1->status, item1->beatitude, 1, local_rng.rand(), item1->identified, &myStats->inventory); item1->status = static_cast(std::max(0, item1->status - 2)); + degraded = item1; } else { item = newItem(type, item2->status, item2->beatitude, 1, local_rng.rand(), item2->identified, &myStats->inventory); item2->status = static_cast(std::max(0, item2->status - 2)); + degraded = item2; } // drop newly created item. To pickup if possible or leave behind if overburdened. dropItemMonster(item, this, myStats); @@ -1571,6 +1588,11 @@ void Entity::automatonRecycleItem() } } //messagePlayer(0, "%d, %d", item1->ownerUid, item2->ownerUid); + if ( degraded && degraded->status == BROKEN && local_rng.rand() % 2 == 0 ) + { + // chance to clear the slot for more items + list_RemoveNode(degraded->node); + } } return; From 5f2f8f7ed14b41b416fa31935b00800efc5c9b17 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 17 Aug 2023 07:27:31 +1000 Subject: [PATCH 006/146] * greater numbers achievement award on game completion flag rather than credits timing --- src/actladder.cpp | 32 ++++++++++++++++++++++++++++++++ src/net.cpp | 16 ++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/actladder.cpp b/src/actladder.cpp index dd6ac246e..3ec09b3e1 100644 --- a/src/actladder.cpp +++ b/src/actladder.cpp @@ -554,6 +554,22 @@ void actWinningPortal(Entity* my) net_packet->len = 6; sendPacketSafe(net_sock, -1, net_packet, c - 1); } + + if ( victory > 0 ) + { + int k = 0; + for ( int c = 0; c < MAXPLAYERS; c++ ) + { + if ( players[c] && players[c]->entity ) + { + k++; + } + } + if ( k >= 2 ) + { + steamAchievement("BARONY_ACH_IN_GREATER_NUMBERS"); + } + } } if (cutscene == 1) { // classic herx ending @@ -765,6 +781,22 @@ void Entity::actExpansionEndGamePortal() net_packet->len = 6; sendPacketSafe(net_sock, -1, net_packet, c - 1); } + + if ( victory > 0 ) + { + int k = 0; + for ( int c = 0; c < MAXPLAYERS; c++ ) + { + if ( players[c] && players[c]->entity ) + { + k++; + } + } + if ( k >= 2 ) + { + steamAchievement("BARONY_ACH_IN_GREATER_NUMBERS"); + } + } } int race = RACE_HUMAN; diff --git a/src/net.cpp b/src/net.cpp index 7fad31acd..8814f7a15 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -4451,6 +4451,22 @@ static std::unordered_map clientPacketHandlers = { } } + if ( victory > 0 ) + { + int k = 0; + for ( int c = 0; c < MAXPLAYERS; c++ ) + { + if ( players[c] && players[c]->entity ) + { + k++; + } + } + if ( k >= 2 ) + { + steamAchievement("BARONY_ACH_IN_GREATER_NUMBERS"); + } + } + // force game to pause movie = true; pauseGame(2, false); From 430f1b59c8aa44c344c280eb2de36f0b635470c0 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Wed, 23 Aug 2023 13:28:33 +1000 Subject: [PATCH 007/146] * fixes for small font multiline messages to not draw more space than needed --- src/ui/GameUI.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 06451f19a..b7134fb8b 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -10249,7 +10249,9 @@ void Player::MessageZone_t::processChatbox() int textHeight = h; if ( !useBigFont ) { - textHeight = (int)(std::max(*cvar_log_lineheight_min, (int)textGet->getHeight()) * (int)current->text->lines + 2); + int h2 = (int)(std::max(*cvar_log_lineheight_min + 2, + ((int)textGet->getHeight() + textLinePadding) * textGet->getNumTextLines() + textLinePadding)); + textHeight = h2; h = textHeight + *cvar_log_lineheight_offset; } From 759c80bb11dffb2064c40cbbf27181b28603d147 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 24 Aug 2023 12:43:42 +1000 Subject: [PATCH 008/146] * lang updates --- lang/en.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lang/en.txt b/lang/en.txt index e962308f3..98c115c3f 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -6422,5 +6422,25 @@ for accessory to violence!# 4344 The %s shatters in your grasp!# 4345 Status Effects# 4346 No effects active# +4347 Call out # +4348 Call out...# +4349 boulder trap# +4350 spike trap# +4351 arrow trap# +4352 magic trap# +4353 gold# +4354 sink# +4355 fountain# +4356 crystal# +4357 grave# +4358 boulder# +4359 secret entrance# +4360 secret exit# +4361 level exit# +4362 trap# +4363 sign# +4364 pedestal# +4365 campfire# +4366 object# 4399 end# From 6b10929ed07cba063fb0e6221fba2453b1f23f88 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 26 Aug 2023 01:04:39 +1000 Subject: [PATCH 009/146] * fix steam statistic update for clients --- src/net.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 8814f7a15..b84f632cc 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2825,8 +2825,8 @@ static std::unordered_map clientPacketHandlers = { }}, // update steam statistic - {'SSTA', [](){ - const int player = std::min(net_packet->data[4], (Uint8)(MAXPLAYERS - 1)); + {'SSTA', []() { + const int statisticNum = static_cast(net_packet->data[4]); int value = static_cast(SDLNet_Read16(&net_packet->data[6])); steamStatisticUpdate(player, static_cast(net_packet->data[5]), value); }}, From 758722e21b5cacb73c56a291d4be751cb2b92e5c Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 26 Aug 2023 01:13:10 +1000 Subject: [PATCH 010/146] * callouts - move to dedicated frame, add world text * collider add callout tooltip --- src/actgeneral.cpp | 5 + src/actplayer.cpp | 66 +- src/game.cpp | 3 +- src/interface/interface.cpp | 1811 ++++++++++++++++++++++++++++++----- src/interface/interface.hpp | 79 +- src/menu.cpp | 2 +- src/net.cpp | 70 ++ src/player.cpp | 37 +- src/ui/GameUI.cpp | 129 --- 9 files changed, 1794 insertions(+), 408 deletions(-) diff --git a/src/actgeneral.cpp b/src/actgeneral.cpp index dd98f764e..dd71b3070 100644 --- a/src/actgeneral.cpp +++ b/src/actgeneral.cpp @@ -739,6 +739,11 @@ void actColliderDecoration(Entity* my) if ( my->isDamageableCollider() ) { + if ( my->ticks == 1 ) + { + my->createWorldUITooltip(); + } + auto& colliderData = EditorEntityData_t::colliderData[my->colliderDamageTypes]; if ( my->flags[BURNING] && my->flags[BURNABLE] ) { diff --git a/src/actplayer.cpp b/src/actplayer.cpp index ac73bd8a0..eabc1d389 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -4028,6 +4028,10 @@ void actPlayer(Entity* my) } } + static ConsoleVariable cvar_calloutStartZ("/callout_start_z", -2.5); + static ConsoleVariable cvar_calloutMoveTo("/callout_moveto_z", 0.1); + static ConsoleVariable cvar_calloutStartZLimit("/callout_start_z_limit", 7.5); + if ( players[PLAYER_NUM]->isLocalPlayer() ) { players[PLAYER_NUM]->entity = my; @@ -4331,7 +4335,11 @@ void actPlayer(Entity* my) } if ( true /*&& calloutMenu.allowedInteractEntity(*target)*/ ) { - calloutMenu.createParticleCallout(target); + calloutMenu.lockOnEntityUid = target->getUID(); + if ( calloutMenu.createParticleCallout(target) ) + { + calloutMenu.sendCalloutText(CalloutRadialMenu::CALLOUT_CMD_LOOK); + } /*calloutMenu.holdWheel = false; calloutMenu.selectMoveTo = false; calloutMenu.bOpen = true; @@ -4340,6 +4348,53 @@ void actPlayer(Entity* my) Player::soundActivate();*/ } } + else + { + // we're selecting a point in the world + if ( players[PLAYER_NUM] && players[PLAYER_NUM]->entity ) + { + real_t startx = cameras[PLAYER_NUM].x * 16.0; + real_t starty = cameras[PLAYER_NUM].y * 16.0; + real_t startz = cameras[PLAYER_NUM].z + (4.5 - cameras[PLAYER_NUM].z) / 2.0 + *cvar_calloutStartZ; + real_t pitch = cameras[PLAYER_NUM].vang; + if ( pitch < 0 || pitch > PI ) + { + pitch = 0; + } + + // draw line from the players height and direction until we hit the ground. + real_t previousx = startx; + real_t previousy = starty; + int index = 0; + const real_t yaw = cameras[PLAYER_NUM].ang; + for ( ; startz < *cvar_calloutStartZLimit; startz += abs((*cvar_calloutMoveTo) * tan(pitch)) ) + { + startx += 0.1 * cos(yaw); + starty += 0.1 * sin(yaw); + const int index_x = static_cast(startx) >> 4; + const int index_y = static_cast(starty) >> 4; + index = (index_y)*MAPLAYERS + (index_x)*MAPLAYERS * map.height; + if ( !map.tiles[OBSTACLELAYER + index] ) + { + // store the last known good coordinate + previousx = startx;// + 16 * cos(yaw); + previousy = starty;// + 16 * sin(yaw); + } + if ( map.tiles[OBSTACLELAYER + index] ) + { + break; + } + } + + calloutMenu.moveToX = previousx; + calloutMenu.moveToY = previousy; + calloutMenu.lockOnEntityUid = 0; + if ( calloutMenu.createParticleCallout(previousx, previousy, -4, 0, CalloutRadialMenu::CALLOUT_CMD_LOOK) ) + { + calloutMenu.sendCalloutText(CalloutRadialMenu::CALLOUT_CMD_LOOK); + } + } + } if ( players[PLAYER_NUM]->worldUI.isEnabled() ) { @@ -4446,29 +4501,26 @@ void actPlayer(Entity* my) { real_t startx = cameras[PLAYER_NUM].x * 16.0; real_t starty = cameras[PLAYER_NUM].y * 16.0; - static ConsoleVariable cvar_followerStartZ("/follower_start_z", -2.5); - real_t startz = cameras[PLAYER_NUM].z + (4.5 - cameras[PLAYER_NUM].z) / 2.0 + *cvar_followerStartZ; + real_t startz = cameras[PLAYER_NUM].z + (4.5 - cameras[PLAYER_NUM].z) / 2.0 + *cvar_calloutStartZ; real_t pitch = cameras[PLAYER_NUM].vang; if ( pitch < 0 || pitch > PI ) { pitch = 0; } - static ConsoleVariable cvar_followerMoveTo("/follower_moveto_z", 0.1); - static ConsoleVariable cvar_followerStartZLimit("/follower_start_z_limit", 7.5); // draw line from the players height and direction until we hit the ground. real_t previousx = startx; real_t previousy = starty; int index = 0; const real_t yaw = cameras[PLAYER_NUM].ang; - for ( ; startz < *cvar_followerStartZLimit; startz += abs((*cvar_followerMoveTo) * tan(pitch)) ) + for ( ; startz < *cvar_calloutStartZLimit; startz += abs((*cvar_calloutMoveTo) * tan(pitch)) ) { startx += 0.1 * cos(yaw); starty += 0.1 * sin(yaw); const int index_x = static_cast(startx) >> 4; const int index_y = static_cast(starty) >> 4; index = (index_y)*MAPLAYERS + (index_x)*MAPLAYERS * map.height; - if ( map.tiles[index] && !map.tiles[OBSTACLELAYER + index] ) + if ( !map.tiles[OBSTACLELAYER + index] ) { // store the last known good coordinate previousx = startx;// + 16 * cos(yaw); diff --git a/src/game.cpp b/src/game.cpp index 90626eee3..5f32abd46 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5356,7 +5356,6 @@ void ingameHud() players[player]->messageZone.processChatbox(); updateSkillUpFrame(player); updateLevelUpFrame(player); - CalloutMenu[player].update(); players[player]->inventoryUI.updateSelectedItemAnimation(); players[player]->inventoryUI.updateInventoryItemTooltip(); players[player]->inventoryUI.updateInventoryMiscTooltip(); @@ -5374,6 +5373,7 @@ void ingameHud() players[player]->skillSheet.processSkillSheet(); players[player]->signGUI.updateSignGUI(); players[player]->hud.updateStatusEffectTooltip(); // to create a tooltip in this order to draw over previous elements + CalloutRadialMenu::drawCallouts(player); players[player]->inventoryUI.updateItemContextMenuClickFrame(); players[player]->GUI.handleModuleNavigation(false); players[player]->inventoryUI.updateCursor(); @@ -6987,6 +6987,7 @@ int main(int argc, char** argv) for ( int player = 0; player < MAXPLAYERS; ++player ) { players[player]->messageZone.updateMessages(); + CalloutMenu[player].update(); } if ( !nohud ) { diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index 218e8a0b0..5e1373d10 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -38,6 +38,7 @@ #include "../ui/Image.hpp" #include "../ui/Button.hpp" #include "../ui/Slider.hpp" +#include "../collision.hpp" Uint32 svFlags = 30; Uint32 settings_svFlags = svFlags; @@ -21961,6 +21962,47 @@ void CalloutRadialMenu::loadCalloutJSON() } } } + if ( d.HasMember("world_icons") ) + { + CalloutRadialMenu::worldIconEntries.clear(); + CalloutRadialMenu::worldIconIDToEntryKey.clear(); + int id = 0; + for ( rapidjson::Value::ConstMemberIterator itr = d["world_icons"].MemberBegin(); + itr != d["world_icons"].MemberEnd(); ++itr ) + { + std::string key = (*itr).name.GetString(); + auto& entry = worldIconEntries[key]; + + std::string basePath = "*images/ui/CalloutWheel/WorldIcons/"; + entry.pathDefault = basePath + (*itr).value["default"].GetString(); + entry.pathPlayer1 = basePath + (*itr).value["0"].GetString(); + entry.pathPlayer2 = basePath + (*itr).value["1"].GetString(); + entry.pathPlayer3 = basePath + (*itr).value["2"].GetString(); + entry.pathPlayer4 = basePath + (*itr).value["3"].GetString(); + entry.pathPlayerX = basePath + (*itr).value["4"].GetString(); + entry.id = id; + CalloutRadialMenu::worldIconIDToEntryKey[id] = key; + ++id; + /*if ( auto img = Image::get(entry.pathDefault.c_str()) ) + { + } + if ( auto img = Image::get(entry.pathPlayer1.c_str()) ) + { + } + if ( auto img = Image::get(entry.pathPlayer2.c_str()) ) + { + } + if ( auto img = Image::get(entry.pathPlayer3.c_str()) ) + { + } + if ( auto img = Image::get(entry.pathPlayer4.c_str()) ) + { + } + if ( auto img = Image::get(entry.pathPlayerX.c_str()) ) + { + }*/ + } + } if ( d.HasMember("icons") ) { CalloutRadialMenu::iconEntries.clear(); @@ -22014,7 +22056,55 @@ void CalloutRadialMenu::loadCalloutJSON() { mapHighlights.insert(highlightItr->GetInt()); } - CalloutRadialMenu::iconEntries[actionName].text_map[mapKey] = std::make_pair(mapText, mapHighlights); + std::string worldMsg = ""; + std::string worldMsgSays = ""; + std::string worldMsgEmote = ""; + std::string worldMsgEmoteYou = ""; + std::string worldIcon = ""; + std::string worldIconMini = ""; + if ( itr3->value.HasMember("msg") ) + { + worldMsg = itr3->value["msg"].GetString(); + } + if ( itr3->value.HasMember("msg_says") ) + { + worldMsgSays = itr3->value["msg_says"].GetString(); + } + if ( itr3->value.HasMember("msg_emote") ) + { + worldMsgEmote = itr3->value["msg_emote"].GetString(); + } + if ( itr3->value.HasMember("msg_emote_you") ) + { + worldMsgEmoteYou = itr3->value["msg_emote_you"].GetString(); + } + if ( itr3->value.HasMember("world_icon") ) + { + worldIcon = itr3->value["world_icon"].GetString(); + } + if ( itr3->value.HasMember("world_icon_small") ) + { + worldIconMini = itr3->value["world_icon_small"].GetString(); + } + CalloutRadialMenu::iconEntries[actionName].text_map[mapKey] = CalloutRadialMenu::IconEntry::IconEntryText_t(); + auto& entry = CalloutRadialMenu::iconEntries[actionName].text_map[mapKey]; + entry.bannerText = mapText; + entry.bannerHighlights = mapHighlights; + entry.worldMsg = worldMsg; + entry.worldMsgSays = worldMsgSays; + entry.worldMsgEmote = worldMsgEmote; + entry.worldMsgEmoteYou = worldMsgEmoteYou; + entry.worldIconTag = worldIcon; + entry.worldIconTagMini = worldIconMini; + + if ( worldIcon != "" ) + { + assert(worldIconEntries.find(worldIcon) != worldIconEntries.end()); + } + if ( worldIconMini != "" ) + { + assert(worldIconEntries.find(worldIconMini) != worldIconEntries.end()); + } } } } @@ -22052,162 +22142,655 @@ void setCalloutBannerTextUnformatted(const int player, Field* field, const char* return; } auto& textMap = CalloutMenu[player].iconEntries[iconName].text_map[textKey]; - field->setText(textMap.first.c_str()); + field->setText(textMap.bannerText.c_str()); field->clearWordsToHighlight(); - for ( auto v : textMap.second ) + for ( auto v : textMap.bannerHighlights ) { field->addWordToHighlight(v, color); } } -void CalloutRadialMenu::setCalloutBannerText(Field* field, const char* iconName, Uint32 color, - CalloutRadialMenu::CalloutCommand cmd) +std::string CalloutRadialMenu::getCalloutMessage(const IconEntry::IconEntryText_t& text_map, const char* object, const int targetPlayer) { - if ( !field ) { return; } + if ( text_map.worldMsgEmote != "" ) + { + char buf[512] = ""; + if ( object ) + { + if ( targetPlayer >= 0 && getPlayer() == targetPlayer && text_map.worldMsgEmoteYou != "" ) + { + // messaging the player that owns the callout "you gesture to" + snprintf(buf, sizeof(buf), text_map.worldMsgEmoteYou.c_str(), object); + } + else + { + char shortname[32]; + stringCopy(shortname, stats[getPlayer()]->name, sizeof(shortname), 22); + snprintf(buf, sizeof(buf), text_map.worldMsgEmote.c_str(), shortname, object); + } + } + else + { + if ( targetPlayer >= 0 && getPlayer() == targetPlayer && text_map.worldMsgEmoteYou != "" ) + { + // messaging the player that owns the callout "you gesture to" + snprintf(buf, sizeof(buf), text_map.worldMsgEmoteYou.c_str()); + } + else + { + char shortname[32]; + stringCopy(shortname, stats[getPlayer()]->name, sizeof(shortname), 22); + snprintf(buf, sizeof(buf), text_map.worldMsgEmote.c_str(), shortname); + } + } + return buf; + } + else if ( text_map.worldMsgSays != "" ) + { + char buf[512] = ""; + if ( object ) + { + if ( targetPlayer >= 0 && getPlayer() == targetPlayer ) + { + // messaging the player that owns the callout "you say:" + snprintf(buf, sizeof(buf), text_map.worldMsgSays.c_str(), Language::get(739), object); + } + else + { + char shortname[32]; + stringCopy(shortname, stats[getPlayer()]->name, sizeof(shortname), 22); + std::string playerSays = shortname; + playerSays += ": "; + snprintf(buf, sizeof(buf), text_map.worldMsgSays.c_str(), playerSays.c_str(), object); + } + } + else + { + if ( targetPlayer >= 0 && getPlayer() == targetPlayer ) + { + // messaging the player that owns the callout "you say:" + snprintf(buf, sizeof(buf), text_map.worldMsgSays.c_str(), Language::get(739)); + } + else + { + char shortname[32]; + stringCopy(shortname, stats[getPlayer()]->name, sizeof(shortname), 22); + std::string playerSays = shortname; + playerSays += ": "; + snprintf(buf, sizeof(buf), text_map.worldMsgSays.c_str(), playerSays.c_str()); + } + } + return buf; + } + else + { + char buf[512] = ""; + if ( object ) + { + snprintf(buf, sizeof(buf), text_map.worldMsg.c_str(), object); + } + else + { + return text_map.worldMsg; + } + return buf; + } +} + +std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName, Uint32 color, + CalloutRadialMenu::CalloutCommand cmd, SetCalloutTextTypes setType, const int targetPlayer) +{ + if ( !field && setType == SET_CALLOUT_BANNER_TEXT ) { return ""; } auto findIcon = CalloutRadialMenu::iconEntries.find(iconName); if ( findIcon == CalloutRadialMenu::iconEntries.end() ) { - return; + return ""; } std::string key = "default"; Entity* entity = uidToEntity(lockOnEntityUid); + if ( lockOnEntityUid == 0 ) + { + if ( cmd == CALLOUT_CMD_AFFIRMATIVE || cmd == CALLOUT_CMD_NEGATIVE ) + { + entity = players[getPlayer()]->entity; + } + } const int player = getPlayer(); auto calloutType = getCalloutTypeForEntity(player, entity); + auto& text_map = findIcon->second.text_map; switch ( calloutType ) { - case CALLOUT_TYPE_NO_TARGET: - break; - case CALLOUT_TYPE_NPC: - case CALLOUT_TYPE_NPC_ENEMY: - case CALLOUT_TYPE_NPC_PLAYERALLY: - if ( entity->behavior == &actMonster ) + case CALLOUT_TYPE_NO_TARGET: + key = "location"; + break; + case CALLOUT_TYPE_PLAYER: + break; + case CALLOUT_TYPE_NPC: + case CALLOUT_TYPE_NPC_ENEMY: + case CALLOUT_TYPE_NPC_PLAYERALLY: + if ( entity->behavior == &actMonster ) + { + int monsterType = entity->getMonsterTypeFromSprite(); + if ( monsterType >= NOTHING && monsterType < NUMMONSTERS ) { - int monsterType = entity->getMonsterTypeFromSprite(); - if ( monsterType >= NOTHING && monsterType < NUMMONSTERS ) + std::string monsterName = getMonsterLocalizedName((Monster)monsterType); + bool namedNPC = false; + if ( multiplayer != CLIENT && monsterType != SHOPKEEPER ) { - std::string monsterName = getMonsterLocalizedName((Monster)monsterType); - bool namedNPC = false; - if ( multiplayer != CLIENT && monsterType != SHOPKEEPER ) + if ( Stat* stats = entity->getStats() ) { - if ( Stat* stats = entity->getStats() ) + if ( monsterNameIsGeneric(*stats) ) { - if ( monsterNameIsGeneric(*stats) ) - { - monsterName = stats->name; - } - else if ( strcmp(stats->name, "") ) - { - monsterName = stats->name; - namedNPC = true; - } + monsterName = stats->name; + } + else if ( strcmp(stats->name, "") ) + { + monsterName = stats->name; + namedNPC = true; } } + } - std::string key = "npc"; - if ( calloutType == CALLOUT_TYPE_NPC_PLAYERALLY ) - { - key = "npc_ally"; - } - else if ( calloutType == CALLOUT_TYPE_NPC_ENEMY ) - { - key = "npc_enemy"; - } + std::string key = "npc"; + if ( calloutType == CALLOUT_TYPE_NPC_PLAYERALLY ) + { + key = "npc_ally"; + } + else if ( calloutType == CALLOUT_TYPE_NPC_ENEMY ) + { + key = "npc_enemy"; + } - if ( namedNPC - && findIcon->second.text_map.find(std::string(key + "_named")) != findIcon->second.text_map.end() ) + if ( namedNPC + && text_map.find(std::string(key + "_named")) != text_map.end() ) + { + key += "_named"; + } + else if ( stringStartsWithVowel(monsterName) + && text_map.find(std::string(key + "_an")) != text_map.end() ) + { + key += "_an"; + } + + if ( text_map.find(key) == text_map.end() ) + { + key = "default"; + } + + if ( setType == SET_CALLOUT_ICON_KEY ) + { + return key; + } + auto& textMap = text_map[key]; + auto highlights = textMap.bannerHighlights; + if ( highlights.size() > 0 ) + { + int indexStart = 0; + for ( auto highlight : highlights ) { - key += "_named"; + indexStart = std::max(highlight, indexStart); } - else if ( stringStartsWithVowel(monsterName) - && findIcon->second.text_map.find(std::string(key + "_an")) != findIcon->second.text_map.end() ) + for ( auto c : monsterName ) { - key += "_an"; + if ( c == ' ' ) + { + highlights.insert(indexStart + 1); + ++indexStart; + } } - - if ( findIcon->second.text_map.find(key) == findIcon->second.text_map.end() ) + } + if ( setType == SET_CALLOUT_BANNER_TEXT ) + { + setCalloutBannerTextFormatted(player, field, color, highlights, textMap.bannerText.c_str(), monsterName.c_str()); + } + else + { + return getCalloutMessage(textMap, monsterName.c_str(), targetPlayer); + } + return ""; + } + } + break; + case CALLOUT_TYPE_SWITCH_ON: + key = "switch"; + if ( text_map.find(std::string(key + "_on")) != text_map.end() ) + { + key += "_on"; + } + break; + case CALLOUT_TYPE_SWITCH_OFF: + key = "switch"; + if ( text_map.find(std::string(key + "_off")) != text_map.end() ) + { + key += "_off"; + } + break; + case CALLOUT_TYPE_SWITCH: + key = "switch"; + break; + case CALLOUT_TYPE_CHEST: + key = "chest"; + break; + case CALLOUT_TYPE_ITEM: + { + std::string itemName; + if ( entity && (multiplayer != CLIENT || (multiplayer == CLIENT && entity->itemReceivedDetailsFromServer == 1)) ) + { + if ( Item* item = newItemFromEntity(entity, true) ) + { + if ( item->type >= WOODEN_SHIELD && item->type < NUMITEMS ) + { + char buf[256]; + bool manuallyInsertedNewline = false; + if ( !item->identified ) { - key = "default"; + if ( itemCategory(item) == BOOK ) + { + if ( setType == SET_CALLOUT_BANNER_TEXT ) + { + snprintf(buf, sizeof(buf), "\"%s\" (?)", getBookNameFromIndex(item->appearance% numbooks).c_str()); + } + else + { + snprintf(buf, sizeof(buf), "%s %s\n\"%s\" (?)", ItemTooltips.getItemStatusAdjective(item->type, item->status).c_str(), + Language::get(4214), getBookNameFromIndex(item->appearance % numbooks).c_str()); + manuallyInsertedNewline = true; + } + } + else if ( itemCategory(item) == SCROLL ) + { + if ( setType == SET_CALLOUT_BANNER_TEXT ) + { + snprintf(buf, sizeof(buf), "%s %s %s (?)", + items[item->type].getUnidentifiedName(), Language::get(4215), item->getScrollLabel()); + } + else + { + snprintf(buf, sizeof(buf), "%s %s\n%s %s (?)", ItemTooltips.getItemStatusAdjective(item->type, item->status).c_str(), + items[item->type].getUnidentifiedName(), Language::get(4215), item->getScrollLabel()); + manuallyInsertedNewline = true; + } + } + else + { + std::string name = item->getName(); + if ( setType == SET_CALLOUT_WORLD_TEXT && (name.find(' ') != std::string::npos) ) + { + snprintf(buf, sizeof(buf), "%s\n%s (?)", ItemTooltips.getItemStatusAdjective(item->type, item->status).c_str(), name.c_str()); + } + else + { + snprintf(buf, sizeof(buf), "%s %s (?)", ItemTooltips.getItemStatusAdjective(item->type, item->status).c_str(), name.c_str()); + } + } } - - auto& textMap = findIcon->second.text_map[key]; - auto highlights = textMap.second; - if ( highlights.size() > 0 ) + else { - int indexStart = 0; - for ( auto highlight : highlights ) + if ( item->type == TOOL_SENTRYBOT || item->type == TOOL_SPELLBOT || item->type == TOOL_DUMMYBOT + || item->type == TOOL_GYROBOT ) { - indexStart = std::max(highlight, indexStart); + int health = 100; + if ( !item->tinkeringBotIsMaxHealth() ) + { + health = 25 * (item->appearance % 10); + if ( health == 0 && item->status != BROKEN ) + { + health = 5; + } + } + std::string name = item->getName(); + if ( setType == SET_CALLOUT_WORLD_TEXT && (name.find(' ') != std::string::npos) ) + { + std::vector spaces; + size_t find = name.find(' '); + while ( find != std::string::npos ) + { + spaces.push_back(find); + find = name.find(' ', find + 1); + } + if ( spaces.size() == 1 ) + { + snprintf(buf, sizeof(buf), "%s\n%s", + ItemTooltips.getItemStatusAdjective(item->type, item->status).c_str(), name.c_str()); + } + else + { + size_t split = (spaces.size() / 2); + if ( spaces.size() > split ) + { + name.at(spaces[split]) = '\n'; + } + snprintf(buf, sizeof(buf), "%s %s", + ItemTooltips.getItemStatusAdjective(item->type, item->status).c_str(), name.c_str()); + } + } + else + { + snprintf(buf, sizeof(buf), "%s %s", ItemTooltips.getItemStatusAdjective(item->type, item->status).c_str(), + name.c_str()); + } + } + else if ( itemCategory(item) == BOOK ) + { + if ( setType == SET_CALLOUT_BANNER_TEXT ) + { + snprintf(buf, sizeof(buf), "\"%s\" (%+d)", getBookNameFromIndex(item->appearance % numbooks).c_str(), + item->beatitude); + } + else + { + snprintf(buf, sizeof(buf), "%s %s\n\"%s\" (%+d)", ItemTooltips.getItemStatusAdjective(item->type, item->status).c_str(), + Language::get(4214), getBookNameFromIndex(item->appearance % numbooks).c_str(), item->beatitude); // brand new copy of + manuallyInsertedNewline = true; + } } - for ( auto c : monsterName ) + else { - if ( c == ' ' ) + std::string name = item->getName(); + if ( setType == SET_CALLOUT_WORLD_TEXT && (name.find(' ') != std::string::npos) ) + { + std::vector spaces; + size_t find = name.find(' '); + while ( find != std::string::npos ) + { + spaces.push_back(find); + find = name.find(' ', find + 1); + } + if ( spaces.size() == 1 ) + { + snprintf(buf, sizeof(buf), "%s\n%s (%+d)", + ItemTooltips.getItemStatusAdjective(item->type, item->status).c_str(), name.c_str(), item->beatitude); + } + else + { + size_t split = (spaces.size() / 2); + if ( spaces.size() > split ) + { + name.at(spaces[split]) = '\n'; + } + snprintf(buf, sizeof(buf), "%s %s (%+d)", + ItemTooltips.getItemStatusAdjective(item->type, item->status).c_str(), name.c_str(), item->beatitude); + } + } + else { - highlights.insert(indexStart + 1); - ++indexStart; + snprintf(buf, sizeof(buf), "%s %s (%+d)", + ItemTooltips.getItemStatusAdjective(item->type, item->status).c_str(), name.c_str(), item->beatitude); } } } - setCalloutBannerTextFormatted(player, field, color, highlights, textMap.first.c_str(), monsterName.c_str()); - return; + itemName = buf; + if ( itemName.size() > 0 && itemName[0] >= 'A' && itemName[0] <= 'Z' ) + { + itemName[0] -= 'A' - 'a'; + } } + else + { + itemName = Language::get(3634); + } + free(item); } - break; - case CALLOUT_TYPE_PLAYER: - break; - case CALLOUT_TYPE_LEVER: - break; - case CALLOUT_TYPE_BOULDER: - break; - case CALLOUT_TYPE_TRAP: - break; - case CALLOUT_TYPE_GENERIC_INTERACTABLE: - break; - case CALLOUT_TYPE_CHEST: - break; - case CALLOUT_TYPE_ITEM: + } + else + { + itemName = Language::get(3634); + } + std::string key = "item"; + if ( stringStartsWithVowel(itemName) && text_map.find(std::string(key + "_an")) != text_map.end() ) { - std::string itemName; - if ( entity && (multiplayer != CLIENT || (multiplayer == CLIENT && entity->itemReceivedDetailsFromServer == 1)) ) + key += "_an"; + } + + if ( text_map.find(key) == text_map.end() ) + { + key = "default"; + } + if ( setType == SET_CALLOUT_ICON_KEY ) + { + return key; + } + auto& textMap = text_map[key]; + auto highlights = textMap.bannerHighlights; + if ( highlights.size() > 0 ) + { + int indexStart = 0; + for ( auto highlight : highlights ) { - if ( Item* item = newItemFromEntity(entity, true) ) + indexStart = std::max(highlight, indexStart); + } + for ( auto c : itemName ) + { + if ( c == ' ' ) { - if ( item->type >= WOODEN_SHIELD && item->type < NUMITEMS ) - { - itemName = item->identified ? items[item->type].getIdentifiedName() : items[item->type].getUnidentifiedName(); - } - else + highlights.insert(indexStart + 1); + ++indexStart; + } + } + } + if ( setType == SET_CALLOUT_BANNER_TEXT ) + { + setCalloutBannerTextFormatted(player, field, color, highlights, textMap.bannerText.c_str(), itemName.c_str()); + } + else + { + return getCalloutMessage(textMap, itemName.c_str(), targetPlayer); + } + return ""; + } + case CALLOUT_TYPE_BOULDER: + key = "boulder"; + break; + case CALLOUT_TYPE_TRAP: + { + key = "trap"; + if ( text_map.find(key) == text_map.end() ) + { + key = "default"; + } + if ( setType == SET_CALLOUT_ICON_KEY ) + { + return key; + } + std::string trapName = Language::get(4362); + if ( entity ) + { + if ( entity->behavior == &actBoulderTrapHole ) + { + trapName = Language::get(4349); + } + else if ( entity->behavior == &actArrowTrap ) + { + trapName = Language::get(4351); + } + else if ( entity->behavior == &actMagicTrap ) + { + trapName = Language::get(4352); + } + else if ( entity->behavior == &actMagicTrapCeiling ) + { + trapName = Language::get(4352); + } + else if ( entity->behavior == &actSpearTrap ) + { + trapName = Language::get(4350); + } + } + auto& textMap = text_map[key]; + auto highlights = textMap.bannerHighlights; + if ( highlights.size() > 0 ) + { + int indexStart = 0; + for ( auto highlight : highlights ) + { + indexStart = std::max(highlight, indexStart); + } + for ( auto c : trapName ) + { + if ( c == ' ' ) + { + highlights.insert(indexStart + 1); + ++indexStart; + } + } + } + if ( setType == SET_CALLOUT_BANNER_TEXT ) + { + setCalloutBannerTextFormatted(player, field, color, highlights, + textMap.bannerText.c_str(), trapName.c_str()); + } + else + { + return getCalloutMessage(textMap, trapName.c_str(), targetPlayer); + } + return ""; + } + case CALLOUT_TYPE_GENERIC_INTERACTABLE: + { + key = "generic_interactable"; + if ( text_map.find(key) == text_map.end() ) + { + key = "default"; + } + if ( setType == SET_CALLOUT_ICON_KEY ) + { + return key; + } + std::string objectName = Language::get(4366); + if ( entity ) + { + if ( entity->behavior == &actSink ) + { + objectName = Language::get(4354); + } + else if ( entity->behavior == &actHeadstone ) + { + objectName = Language::get(4357); + } + else if ( entity->behavior == &actCampfire ) + { + objectName = Language::get(4365); + } + else if ( entity->behavior == &actPowerCrystal ) + { + objectName = Language::get(4356); + } + else if ( entity->behavior == &actPedestalBase ) + { + objectName = Language::get(4364); + } + else if ( entity->behavior == &actFloorDecoration && entity->sprite == 991 ) + { + objectName = Language::get(4363); + } + } + auto& textMap = text_map[key]; + if ( setType == SET_CALLOUT_BANNER_TEXT ) + { + setCalloutBannerTextFormatted(player, field, color, textMap.bannerHighlights, + textMap.bannerText.c_str(), objectName.c_str()); + } + else + { + return getCalloutMessage(textMap, objectName.c_str(), targetPlayer); + } + return ""; + } + case CALLOUT_TYPE_SHRINE: + key = "shrine"; + break; + case CALLOUT_TYPE_EXIT: + key = "exit"; + break; + case CALLOUT_TYPE_SECRET_EXIT: + key = "secret_exit"; + break; + case CALLOUT_TYPE_SECRET_ENTRANCE: + key = "secret_entrance"; + break; + case CALLOUT_TYPE_GOLD: + key = "gold"; + break; + case CALLOUT_TYPE_FOUNTAIN: + key = "fountain"; + break; + case CALLOUT_TYPE_TELEPORTER_LADDER_UP: + key = "teleporter"; + if ( text_map.find(std::string(key + "_up")) != text_map.end() ) + { + key += "_up"; + } + break; + case CALLOUT_TYPE_TELEPORTER_LADDER_DOWN: + key = "teleporter"; + if ( text_map.find(std::string(key + "_down")) != text_map.end() ) + { + key += "_down"; + } + break; + case CALLOUT_TYPE_TELEPORTER_PORTAL: + key = "teleporter"; + if ( text_map.find(std::string(key + "_portal")) != text_map.end() ) + { + key += "_portal"; + } + break; + case CALLOUT_TYPE_BOMB_TRAP: + { + key = "bomb"; + if ( setType == SET_CALLOUT_ICON_KEY ) + { + return key; + } + std::string trapName = Language::get(4362); + if ( entity ) + { + auto highlights = text_map[key].bannerHighlights; + if ( entity->behavior == &actBomb ) + { + if ( entity->skill[21] >= WOODEN_SHIELD && entity->skill[21] < NUMITEMS ) + { + trapName = items[entity->skill[21]].getIdentifiedName(); + if ( highlights.size() > 0 ) { - itemName = Language::get(3634); + highlights.insert(*highlights.begin() + 1); } - free(item); } } - else + else if ( entity->behavior == &actBeartrap ) { - itemName = Language::get(3634); + trapName = items[TOOL_BEARTRAP].getIdentifiedName(); } - std::string key = "item"; - if ( stringStartsWithVowel(itemName) && findIcon->second.text_map.find(std::string(key + "_an")) != findIcon->second.text_map.end() ) + auto& textMap = text_map[key]; + if ( setType == SET_CALLOUT_BANNER_TEXT ) { - key += "_an"; + setCalloutBannerTextFormatted(player, field, color, highlights, + textMap.bannerText.c_str(), trapName.c_str()); } - - if ( findIcon->second.text_map.find(key) == findIcon->second.text_map.end() ) + else { - key = "default"; + return getCalloutMessage(textMap, trapName.c_str(), targetPlayer); } + } + return ""; + } + case CALLOUT_TYPE_COLLIDER_BREAKABLE: + { + key = "collider"; + if ( setType == SET_CALLOUT_ICON_KEY ) + { + return key; + } + if ( entity ) + { + auto highlights = text_map[key].bannerHighlights; + std::string objectName = Language::get(entity->getColliderLangName()); - auto& textMap = findIcon->second.text_map[key]; - auto highlights = textMap.second; if ( highlights.size() > 0 ) { - int indexStart = 0; - for ( auto highlight : highlights ) - { - indexStart = std::max(highlight, indexStart); - } - for ( auto c : itemName ) + int indexStart = *highlights.begin(); + for ( auto c : objectName ) { if ( c == ' ' ) { @@ -22216,14 +22799,54 @@ void CalloutRadialMenu::setCalloutBannerText(Field* field, const char* iconName, } } } - setCalloutBannerTextFormatted(player, field, color, highlights, textMap.first.c_str(), itemName.c_str()); - return; + auto& textMap = text_map[key]; + if ( setType == SET_CALLOUT_BANNER_TEXT ) + { + setCalloutBannerTextFormatted(player, field, color, highlights, + textMap.bannerText.c_str(), objectName.c_str()); + } + + else + { + return getCalloutMessage(textMap, objectName.c_str(), targetPlayer); + } + } + return ""; + break; + } + default: + break; + } + + if ( text_map.find(key) == text_map.end() ) + { + if ( setType == SET_CALLOUT_BANNER_TEXT ) + { + setCalloutBannerTextUnformatted(player, field, iconName, "default", color); } - default: - break; + else if ( setType == SET_CALLOUT_ICON_KEY ) + { + return "default"; + } + else + { + return getCalloutMessage(text_map["default"], nullptr, targetPlayer); + } + return ""; } - - setCalloutBannerTextUnformatted(player, field, iconName, key.c_str(), color); + if ( setType == SET_CALLOUT_BANNER_TEXT ) + { + setCalloutBannerTextUnformatted(player, field, iconName, key.c_str(), color); + } + else if ( setType == SET_CALLOUT_ICON_KEY ) + { + return key; + } + else + { + return getCalloutMessage(text_map[key], nullptr, targetPlayer); + } + return ""; } void CalloutRadialMenu::initCalloutMenuGUICursor(bool openInventory) @@ -22399,6 +23022,8 @@ bool CalloutRadialMenu::calloutMenuIsOpen() std::vector CalloutRadialMenu::panelEntries; std::map CalloutRadialMenu::iconEntries; +std::map CalloutRadialMenu::worldIconEntries; +std::map CalloutRadialMenu::worldIconIDToEntryKey; int CalloutRadialMenu::followerWheelButtonThickness = 70; int CalloutRadialMenu::followerWheelRadius = 140; int CalloutRadialMenu::followerWheelFrameOffsetX = 0; @@ -22420,8 +23045,9 @@ CalloutRadialMenu::CalloutType CalloutRadialMenu::getCalloutTypeForEntity(const } CalloutType type = CALLOUT_TYPE_GENERIC_INTERACTABLE; - if ( parent->behavior == &actSwitch || parent->behavior == &actSwitchWithTimer ) + if ( parent->behavior == &actPlayer ) { + type = CALLOUT_TYPE_PLAYER; } else if ( parent->behavior == &actItem ) { @@ -22429,25 +23055,33 @@ CalloutRadialMenu::CalloutType CalloutRadialMenu::getCalloutTypeForEntity(const } else if ( parent->behavior == &actGoldBag ) { + type = CALLOUT_TYPE_GOLD; } else if ( parent->behavior == &actFountain ) { + type = CALLOUT_TYPE_FOUNTAIN; } else if ( parent->behavior == &actSink ) { + type = CALLOUT_TYPE_GENERIC_INTERACTABLE; } else if ( parent->behavior == &actChestLid || parent->behavior == &actChest ) { type = CALLOUT_TYPE_CHEST; } - else if ( parent->behavior == &actTorch ) + /*else if ( parent->behavior == &actTorch ) { - } - else if ( parent->behavior == &actCrystalShard ) + }*/ + /*else if ( parent->behavior == &actCrystalShard ) { - } + }*/ else if ( parent->behavior == &actHeadstone ) { + type = CALLOUT_TYPE_GENERIC_INTERACTABLE; + } + else if ( parent->behavior == &actColliderDecoration && parent->isDamageableCollider() ) + { + type = CALLOUT_TYPE_COLLIDER_BREAKABLE; } else if ( parent->behavior == &actMonster ) { @@ -22469,102 +23103,605 @@ CalloutRadialMenu::CalloutType CalloutRadialMenu::getCalloutTypeForEntity(const type = CALLOUT_TYPE_NPC_PLAYERALLY; } } - else if ( parent->behavior == &actGate ) + /*else if ( parent->behavior == &actGate ) { - } + }*/ else if ( parent->behavior == &actSwitch || parent->behavior == &actSwitchWithTimer ) { if ( parent->skill[0] == 1 ) { + type = CALLOUT_TYPE_SWITCH_ON; } else { + type = CALLOUT_TYPE_SWITCH_OFF; } } else if ( parent->behavior == &actPowerCrystal ) { + type = CALLOUT_TYPE_GENERIC_INTERACTABLE; } else if ( parent->behavior == &actPedestalBase ) { + type = CALLOUT_TYPE_GENERIC_INTERACTABLE; } else if ( parent->behavior == &actCampfire ) { + type = CALLOUT_TYPE_GENERIC_INTERACTABLE; + } + else if ( parent->behavior == &actBoulderTrapHole ) + { + type = CALLOUT_TYPE_TRAP; + } + else if ( parent->behavior == &actFloorDecoration && parent->sprite == 991 ) + { + type = CALLOUT_TYPE_GENERIC_INTERACTABLE; + } + else if ( parent->behavior == &actBoulder ) + { + type = CALLOUT_TYPE_BOULDER; + } + else if ( parent->behavior == &actLadder ) + { + if ( secretlevel && parent->skill[3] == 1 ) // secret ladder + { + type = CALLOUT_TYPE_SECRET_EXIT; + } + else if ( !secretlevel && parent->skill[3] == 1 ) // secret ladder + { + type = CALLOUT_TYPE_SECRET_ENTRANCE; + } + else + { + type = CALLOUT_TYPE_EXIT; + } + } + else if ( parent->behavior == &actPortal ) + { + if ( parent->skill[3] == 0 ) // secret entrance portal + { + if ( secretlevel ) + { + type = CALLOUT_TYPE_SECRET_EXIT; + } + else + { + type = CALLOUT_TYPE_SECRET_ENTRANCE; + } + } + else + { + if ( !strcmp(map.name, "Hell") ) + { + type = CALLOUT_TYPE_EXIT; + } + else if ( !strcmp(map.name, "Mages Guild") ) + { + type = CALLOUT_TYPE_EXIT; + } + else + { + type = CALLOUT_TYPE_EXIT; + } + } + } + else if ( parent->behavior == &::actMidGamePortal ) + { + type = CALLOUT_TYPE_EXIT; + } + else if ( parent->behavior == &actCustomPortal ) + { + if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_TUTORIAL ) + { + type = CALLOUT_TYPE_EXIT; + } + else + { + if ( parent->portalCustomSpriteAnimationFrames > 0 ) + { + type = CALLOUT_TYPE_EXIT; + } + else + { + type = CALLOUT_TYPE_EXIT; + } + } + } + else if ( parent->behavior == &::actExpansionEndGamePortal + || parent->behavior == &actWinningPortal ) + { + type = CALLOUT_TYPE_EXIT; + } + else if ( parent->behavior == &actTeleporter ) + { + if ( parent->teleporterType == 2 ) // portal + { + type = CALLOUT_TYPE_TELEPORTER_PORTAL; + } + else if ( parent->teleporterType == 1 ) // down ladder + { + type = CALLOUT_TYPE_TELEPORTER_LADDER_DOWN; + } + else if ( parent->teleporterType == 0 ) // up ladder + { + type = CALLOUT_TYPE_TELEPORTER_LADDER_UP; + } + } + else if ( parent->behavior == &::actTeleportShrine /*|| parent->behavior == &::actSpellShrine*/ ) + { + type = CALLOUT_TYPE_SHRINE; + } + else if ( parent->behavior == &actBomb || parent->behavior == &actBeartrap ) + { + type = CALLOUT_TYPE_BOMB_TRAP; + } + return type; +} + +CalloutRadialMenu::CalloutType CalloutRadialMenu::getCalloutTypeForUid(const int player, Uint32 uid) +{ + Entity* parent = uidToEntity(uid); + if ( !parent ) + { + return CALLOUT_TYPE_NO_TARGET; + } + + return CalloutRadialMenu::getCalloutTypeForEntity(player, parent); +} + +void CalloutRadialMenu::CalloutParticle_t::init(const int player) +{ + creationTick = ::ticks; + messageSentTick = ::ticks; + for ( int i = 0; i < MAXPLAYERS; ++i ) + { + lockOnScreen[i] = true; + big[i] = true; + animateScaleForPlayerView[i] = 0.0; + } + Entity* parent = uidToEntity(entityUid); + + if ( !parent ) + { + return; + } + + z = parent->z - 4; + + type = CalloutRadialMenu::getCalloutTypeForEntity(player, parent); +} + +void CalloutRadialMenu::closeCalloutMenuGUI() +{ + bOpen = false; + lockOnEntityUid = 0; + selectMoveTo = false; + menuX = -1; + menuY = -1; + moveToX = -1; + moveToY = -1; + menuToggleClick = false; + holdWheel = false; + optionSelected = -1; + if ( calloutFrame ) + { + calloutFrame->setDisabled(true); + for ( auto f : calloutFrame->getFrames() ) + { + f->removeSelf(); + } + } + animTitle = 0.0; + animWheel = 0.0; + openedThisTick = 0; + animInvalidAction = 0.0; + animInvalidActionTicks = 0; +} + +void CalloutRadialMenu::drawCallouts(const int playernum) +{ + auto& pingFrame = CalloutMenu[playernum].calloutPingFrame; + if ( !pingFrame ) + { + pingFrame = gameUIFrame[playernum]->addFrame("callout pings"); + pingFrame->setHollow(true); + pingFrame->setDisabled(true); + pingFrame->setInheritParentFrameOpacity(false); + pingFrame->setBorder(0); + pingFrame->setOwner(playernum); + } + + if ( players[playernum]->hud.hudFrame ) + { + pingFrame->setDisabled(players[playernum]->hud.hudFrame->isDisabled()); + } + + pingFrame->setSize(SDL_Rect{ players[playernum]->camera_virtualx1(), + players[playernum]->camera_virtualy1(), + players[playernum]->camera_virtualWidth(), + players[playernum]->camera_virtualHeight() }); + + struct CalloutToDraw_t + { + Uint32 creationTick = 0; + real_t dist = 0.0; + std::string imgPath = ""; + Uint32 color = 0; + SDL_Rect pos; + CalloutToDraw_t(std::string _imgPath, Uint32 _color, SDL_Rect _pos, Uint32 _creationTick, real_t _dist) + { + imgPath = _imgPath; + color = _color; + pos = _pos; + creationTick = _creationTick; + dist = _dist; + } + }; + + auto compFunc = [](CalloutToDraw_t& lhs, CalloutToDraw_t& rhs) + { + return lhs.dist < rhs.dist; + }; + std::priority_queue, decltype(compFunc)> priorityQueue(compFunc); + + for ( int i = 0; i < MAXPLAYERS; ++i ) + { + for ( auto& callout : CalloutMenu[i].callouts ) + { + if ( i == playernum && callout.second.entityUid == achievementObserver.playerUids[playernum] ) + { + if ( players[i]->entity && players[i]->entity->skill[3] == 1 ) + { + // debug/thirdperson cam. + } + else + { + continue; // don't draw self callouts + } + } + + auto& iconPaths = CalloutRadialMenu::worldIconEntries[CalloutRadialMenu::worldIconIDToEntryKey[callout.second.tagID]]; + auto& iconPathsMini = CalloutRadialMenu::worldIconEntries[CalloutRadialMenu::worldIconIDToEntryKey[callout.second.tagSmallID]]; + std::string iconPath = ""; + std::string iconPathMini = ""; + switch ( i ) + { + case 0: + iconPath = iconPaths.pathPlayer1; + iconPathMini = iconPathsMini.pathPlayer1; + break; + case 1: + iconPath = iconPaths.pathPlayer2; + iconPathMini = iconPathsMini.pathPlayer2; + break; + case 2: + iconPath = iconPaths.pathPlayer3; + iconPathMini = iconPathsMini.pathPlayer3; + break; + case 3: + iconPath = iconPaths.pathPlayer4; + iconPathMini = iconPathsMini.pathPlayer4; + break; + default: + iconPath = iconPaths.pathPlayerX; + iconPathMini = iconPathsMini.pathPlayerX; + break; + } + + if ( iconPath == "" ) + { + continue; + } + + vec4_t v; + mat4x4_t m, t; + + auto camera = &cameras[playernum]; + auto& player = players[playernum]; + + const int offset = 40; + int leftOfWindow = player->camera_virtualx1() + offset; + int rightOfWindow = player->camera_virtualx1() + player->camera_virtualWidth() - offset; + int topOfWindow = player->camera_virtualy1() + offset; + int bottomOfWindow = player->camera_virtualy2() - offset; + + mat4x4_t id; + vec4_t world{ (float)callout.second.x * 2.f, -(float)callout.second.z * 2.f, (float)callout.second.y * 2.f, 1.f }; + vec4_t window2{ (float)0, (float)0, + (float)player->camera_virtualWidth(), (float)player->camera_virtualHeight() }; + SDL_Rect dest{ 0, 0, 0, 0 }; + if ( callout.second.lockOnScreen[playernum] ) + { + auto screen_position = project_clipped2(&world, &id, &camera->projview, &window2); + dest = SDL_Rect{ player->camera_virtualx1() + (int)screen_position.clipped_coords.x, + player->camera_virtualy1() + Frame::virtualScreenY - (Frame::virtualScreenY - player->camera_virtualHeight()) - (int)screen_position.clipped_coords.y, + 14, 22 }; + if ( !screen_position.isBehind + && (screen_position.direction == ClipResult::Direction::Front + || screen_position.direction == ClipResult::Direction::Invalid) ) + { + callout.second.lockOnScreen[playernum] = false; + } + + dest.x = std::min(rightOfWindow, std::max(leftOfWindow, dest.x)); + dest.y = std::min(bottomOfWindow, std::max(topOfWindow, dest.y)); + real_t tangent = atan2(camera->y * 32.0 - world.z, camera->x * 32.0 - world.x); + real_t camang = camera->ang; + while ( tangent >= 2 * PI ) + { + tangent -= PI * 2; + } + while ( tangent < 0 ) + { + tangent += PI * 2; + } + while ( camang >= 2 * PI ) + { + camang -= PI * 2; + } + while ( camang < 0 ) + { + camang += PI * 2; + } + real_t result = tangent - camang; + while ( result >= PI ) + { + result -= PI * 2; + } + while ( result < -PI ) + { + result += PI * 2; + } + //messagePlayer(player->playernum, MESSAGE_DEBUG, "%f", ((PI - abs(abs(tangent - camang) - PI)) * 2)); + //messagePlayer(player->playernum, MESSAGE_DEBUG, "%f", result); + if ( result >= 0.0 && result < PI / 2 ) + { + dest.x = leftOfWindow; + } + else if ( result < 0.0 && result > -PI / 2 ) + { + dest.x = rightOfWindow; + } + + if ( abs(result) < (3 * PI / 4) ) + { + real_t mult = std::min(1.0, ((3 * PI / 4) - abs(result)) / (PI / 2)); + dest.y += ((player->camera_virtualy1() + (player->camera_virtualHeight() / 2)) - dest.y) * mult; + } + } + else + { + auto screen_position = project(&world, &id, &camera->projview, &window2); + if ( screen_position.z >= 1.0 || screen_position.z < 0.0 ) + { + continue; + } + dest = SDL_Rect{ player->camera_virtualx1() + (int)screen_position.x, + player->camera_virtualy1() + Frame::virtualScreenY - (Frame::virtualScreenY - player->camera_virtualHeight()) - + (int)screen_position.y, + 14, 22 }; + } + + real_t lifePercent = callout.second.ticks / (real_t)CalloutRadialMenu::CalloutParticle_t::kParticleLifetime; + Uint32 alpha = 255; + if ( lifePercent >= 0.8 ) + { + alpha -= std::min((Uint32)255, (Uint32)(255 * (lifePercent - 0.8) / 0.2)); + } + Uint32 color = makeColor(255, 255, 255, alpha); + SDL_Rect iconPos = dest; + + bool drawMini = false; + if ( players[playernum]->entity && players[playernum]->entity->bodyparts.size() > 0 ) + { + auto bodypart = players[playernum]->entity->bodyparts[0]; + real_t tempx = bodypart->x; + real_t tempy = bodypart->y; + bodypart->x = callout.second.x; + bodypart->y = callout.second.y; + + real_t tangent = atan2(camera->y * 16.0 - bodypart->y, camera->x * 16.0 - bodypart->x); + Entity* ohitentity = hit.entity; + lineTraceTarget(bodypart, bodypart->x, bodypart->y, tangent, 256, 0, false, players[playernum]->entity); + + if ( hit.entity != players[playernum]->entity ) + { + // no line of sight through walls + drawMini = true; + } + hit.entity = ohitentity; + bodypart->x = tempx; + bodypart->y = tempy; + } + + callout.second.big[playernum] = !drawMini; + drawMini = false; + + real_t dist = pow(camera->y * 32.0 - world.z, 2) + pow(camera->x * 32.0 - world.x, 2); + + if ( !drawMini ) + { + if ( auto image = Image::get(iconPath.c_str()) ) + { + real_t scale = callout.second.scale - callout.second.animateScaleForPlayerView[playernum] * .5; + iconPos.w = image->getWidth() * scale; + iconPos.h = image->getHeight() * scale; + const int heightOffset = image->getHeight() - iconPos.h; + iconPos.x -= iconPos.w / 2; + iconPos.y -= iconPos.h + heightOffset / 2; + iconPos.y -= (iconPos.h / 4) * callout.second.animateBounce; + /*image->drawColor(nullptr, iconPos, + SDL_Rect{ 0, 0, Frame::virtualScreenX, Frame::virtualScreenY }, color);*/ + + iconPos.x -= players[playernum]->camera_virtualx1(); + iconPos.y -= players[playernum]->camera_virtualy1(); + + priorityQueue.push(CalloutToDraw_t(iconPath, color, iconPos, callout.second.creationTick, dist)); + } + } + else + { + if ( auto image = Image::get(iconPathMini.c_str()) ) + { + dest.w = image->getWidth(); + dest.h = image->getHeight(); + dest.x -= dest.w / 2; + dest.y -= dest.h; + image->drawColor(nullptr, dest, + SDL_Rect{ 0, 0, Frame::virtualScreenX, Frame::virtualScreenY }, color); + } + } + } + } + + auto& images = pingFrame->getImages(); + while ( images.size() > priorityQueue.size() ) + { + images.erase(images.begin()); } - else if ( parent->behavior == &actLadder ) + while ( images.size() < priorityQueue.size() ) { + pingFrame->addImage(SDL_Rect{ 0, 0, 0, 0 }, 0, "", "img"); + } + size_t index = 0; + for ( auto img : images ) + { + img->disabled = true; } - else if ( parent->behavior == &actPortal ) + while ( !priorityQueue.empty() ) { + auto& top = priorityQueue.top(); + if ( index < images.size() ) + { + auto img = images[index]; + img->color = top.color; + img->path = top.imgPath; + img->pos = top.pos; + img->disabled = false; + } + priorityQueue.pop(); + ++index; } - else if ( parent->behavior == &actTeleporter ) +} + +void CalloutRadialMenu::CalloutParticle_t::animate() +{ + static ConsoleVariable cvar_calloutanimspeed("/calloutanimspeed", 0.3); + static ConsoleVariable cvar_calloutbouncespeed("/calloutbouncespeed", 0.9); + static ConsoleVariable cvar_calloutbouncestate("/calloutbouncestate", 0); + real_t animspeed = 5.0 * *cvar_calloutanimspeed; + if ( animateState == 0 ) { - if ( parent->teleporterType == 2 ) // portal + if ( animateStateInit == 0 ) { + animateStateInit = 1; + animateBounce = 0.0; } - else if ( parent->teleporterType == 1 ) // down ladder + + scale = 0.25 + 1.25 * animateX; + if ( animateX >= 1.0 ) { + ++animateState; + animateX = 0.0; } - else if ( parent->teleporterType == 0 ) // up ladder + animspeed *= 2.0; + } + else if ( animateState == 1 ) + { + if ( animateStateInit == 1 ) + { + animateStateInit = 2; + } + + scale = 1.5 - .75 * animateX; + if ( animateX >= 1.0 ) { + ++animateState; + animateX = 0.0; } + animspeed *= 2.0; } - else if ( parent->behavior == &::actTeleportShrine /*|| parent->behavior == &::actSpellShrine*/ ) + else if ( animateState == 2 ) { + if ( animateStateInit == 2 ) + { + animateStateInit = 3; + } + + scale = 0.75 + 0.25 * animateX; + if ( animateX >= 1.0 ) + { + ++animateState; + } + animspeed *= 1.5; } - return type; -} + //else if ( animateState == 4 ) + //{ + // if ( animateStateInit == 4 ) + // { + // animateStateInit = 5; + // } -CalloutRadialMenu::CalloutType CalloutRadialMenu::getCalloutTypeForUid(const int player, Uint32 uid) -{ - Entity* parent = uidToEntity(uid); - if ( !parent ) + // scale = 1.5 - 0.5 * animateX; + // if ( animateX >= 1.0 ) + // { + // ++animateState; + // animateX = 0.0; + // } + // animspeed /= 2.0; + //} + else { - return CALLOUT_TYPE_NO_TARGET; + scale = 0.5 + 0.5 * animateX; } - return CalloutRadialMenu::getCalloutTypeForEntity(player, parent); -} - -void CalloutRadialMenu::CalloutParticle_t::init(const int player) -{ - Entity* parent = uidToEntity(entityUid); - - if ( !parent ) + const real_t fpsScale = getFPSScale(50.0); // ported from 50Hz + if ( animateState <= 2 ) { - return; + real_t setpointDiffX = fpsScale * std::max(.1, (1.0 - animateX)) / (animspeed); + animateX += setpointDiffX; + for ( int i = 0; i < MAXPLAYERS; ++i ) + { + animateScaleForPlayerView[i] = 0.0; + } } - - z = parent->z - 4; - - type = CalloutRadialMenu::getCalloutTypeForEntity(player, parent); -} - -void CalloutRadialMenu::closeCalloutMenuGUI() -{ - bOpen = false; - lockOnEntityUid = 0; - selectMoveTo = false; - menuX = -1; - menuY = -1; - moveToX = -1; - moveToY = -1; - menuToggleClick = false; - holdWheel = false; - optionSelected = -1; - if ( calloutFrame ) + else { - calloutFrame->setDisabled(true); - for ( auto f : calloutFrame->getFrames() ) + animateX = 1.0; + /*if ( big ) { - f->removeSelf(); + real_t setpointDiffX = fpsScale * std::max(.1, (1.0 - animateX)) / (animspeed); + animateX += setpointDiffX; + } + else + { + real_t setpointDiffX = fpsScale * std::max(.1, (animateX)) / (animspeed); + animateX -= setpointDiffX; + }*/ + for ( int i = 0; i < MAXPLAYERS; ++i ) + { + if ( !big[i] ) + { + real_t setpointDiffX = fpsScale * std::max(.1, (1.0 - animateScaleForPlayerView[i])) / (animspeed); + animateScaleForPlayerView[i] += setpointDiffX; + } + else + { + real_t setpointDiffX = fpsScale * std::max(.1, (animateScaleForPlayerView[i])) / (animspeed); + animateScaleForPlayerView[i] -= setpointDiffX; + } + animateScaleForPlayerView[i] = std::max(0.0, std::min(1.0, animateScaleForPlayerView[i])); } } - animTitle = 0.0; - animWheel = 0.0; - openedThisTick = 0; - animInvalidAction = 0.0; - animInvalidActionTicks = 0; + animateX = std::max(0.0, std::min(1.0, animateX)); + + animateBounce = sin(animateY * PI * 2); + if ( animateState >= *cvar_calloutbouncestate ) + { + animateY += fpsScale * 0.05 * *cvar_calloutbouncespeed; + animateY = std::min(1.0, animateY); + } } void CalloutRadialMenu::update() @@ -22573,6 +23710,7 @@ void CalloutRadialMenu::update() { auto& callout = c.second; Entity* entity = uidToEntity(callout.entityUid); + callout.animate(); if ( entity ) { if ( TimerExperiments::bUseTimerInterpolation && entity->bUseRenderInterpolation ) @@ -22624,17 +23762,186 @@ void CalloutRadialMenu::update() } } -void CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu::CalloutCommand _cmd) +bool CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu::CalloutCommand _cmd) +{ + if ( !entity ) { return false; } + if ( _cmd == CALLOUT_CMD_CANCEL ) { return false; } + + Uint32 exisitingMessageSent = 0; + for ( int i = 0; i < MAXPLAYERS; ++i ) + { + if ( CalloutMenu[i].callouts.find(entity->getUID()) != CalloutMenu[i].callouts.end() ) + { + auto& existingCallout = CalloutMenu[i].callouts[entity->getUID()]; + if ( i == getPlayer() && existingCallout.cmd == _cmd ) + { + exisitingMessageSent = existingCallout.messageSentTick; + } + CalloutMenu[i].callouts.erase(entity->getUID()); // delete other players pings on this object + } + } + + auto& callout = callouts[entity->getUID()]; + callout = CalloutRadialMenu::CalloutParticle_t(getPlayer(), entity->x, entity->y, entity->z, entity->getUID(), _cmd); + if ( exisitingMessageSent > 0 && multiplayer != CLIENT ) + { + if ( (callout.messageSentTick - exisitingMessageSent) < (TICKS_PER_SECOND * 3.5) ) + { + callout.doMessage = false; + callout.messageSentTick = exisitingMessageSent; + } + } + + std::string calloutTypeKey = getCalloutKeyForCommand(_cmd); + Uint32 oldTarget = lockOnEntityUid; + lockOnEntityUid = entity->getUID(); + std::string key = setCalloutText(nullptr, calloutTypeKey.c_str(), 0, _cmd, SET_CALLOUT_ICON_KEY, -1); + lockOnEntityUid = oldTarget; + + callout.tagID = worldIconEntries[iconEntries[calloutTypeKey].text_map[key].worldIconTag].id; + callout.tagSmallID = worldIconEntries[iconEntries[calloutTypeKey].text_map[key].worldIconTagMini].id; + + if ( multiplayer == SERVER ) + { + for ( int i = 1; i < MAXPLAYERS; ++i ) + { + if ( i == getPlayer() ) { continue; } // don't send clients their own callout + if ( players[i]->isLocalPlayer() ) { continue; } + if ( client_disconnected[i] ) { continue; } + + strcpy((char*)net_packet->data, "CALL"); + net_packet->data[4] = getPlayer(); + SDLNet_Write32(entity->getUID(), &net_packet->data[5]); + net_packet->data[9] = (Uint8)_cmd; + net_packet->len = 10; + net_packet->address.host = net_clients[i - 1].host; + net_packet->address.port = net_clients[i - 1].port; + sendPacketSafe(net_sock, -1, net_packet, i - 1); + } + } + + return callout.doMessage; +} +bool CalloutRadialMenu::createParticleCallout(real_t x, real_t y, real_t z, Uint32 uid, CalloutRadialMenu::CalloutCommand _cmd) +{ + if ( _cmd == CALLOUT_CMD_CANCEL ) { return false; } + + //for ( int i = 0; i < MAXPLAYERS; ++i ) + //{ + // if ( CalloutMenu[i].callouts.find(uid) != CalloutMenu[i].callouts.end() ) + // { + // CalloutMenu[i].callouts.erase(uid); + // } + //} + + auto& callout = callouts[uid]; + callout = CalloutRadialMenu::CalloutParticle_t(getPlayer(), x, y, z, uid, _cmd); + + std::string calloutTypeKey = getCalloutKeyForCommand(_cmd); + Uint32 oldTarget = lockOnEntityUid; + lockOnEntityUid = uid; + std::string key = setCalloutText(nullptr, calloutTypeKey.c_str(), 0, _cmd, SET_CALLOUT_ICON_KEY, -1); + lockOnEntityUid = oldTarget; + + callout.tagID = worldIconEntries[iconEntries[calloutTypeKey].text_map[key].worldIconTag].id; + callout.tagSmallID = worldIconEntries[iconEntries[calloutTypeKey].text_map[key].worldIconTagMini].id; + if ( uid == 0 && callout.cmd == CALLOUT_CMD_LOOK && multiplayer != CLIENT ) + { + callout.doMessage = false; + } + + if ( multiplayer == SERVER ) + { + for ( int i = 1; i < MAXPLAYERS; ++i ) + { + if ( i == getPlayer() ) { continue; } // don't send clients their own callout + if ( players[i]->isLocalPlayer() ) { continue; } + if ( client_disconnected[i] ) { continue; } + + strcpy((char*)net_packet->data, "CALL"); + net_packet->data[4] = getPlayer(); + SDLNet_Write32(uid, &net_packet->data[5]); + net_packet->data[9] = (Uint8)_cmd; + net_packet->len = 10; + if ( uid == 0 ) + { + Uint16 _x = std::min(std::max(0.0, x / 16), map.width - 1); + Uint16 _y = std::min(std::max(0.0, y / 16), map.height - 1); + SDLNet_Write16(_x, &net_packet->data[10]); + SDLNet_Write16(_y, &net_packet->data[12]); + net_packet->len = 14; + } + net_packet->address.host = net_clients[i - 1].host; + net_packet->address.port = net_clients[i - 1].port; + sendPacketSafe(net_sock, -1, net_packet, i - 1); + } + } + + return callout.doMessage; +} + +void CalloutRadialMenu::sendCalloutText(CalloutRadialMenu::CalloutCommand cmd) { - if ( !entity ) { return; } - if ( _cmd == CALLOUT_CMD_CANCEL ) { return; } - callouts[entity->getUID()] = - CalloutRadialMenu::CalloutParticle_t(getPlayer(), entity->x, entity->y, entity->z, entity->getUID(), _cmd); + if ( cmd == CALLOUT_CMD_CANCEL || cmd == CALLOUT_CMD_END ) + { + return; + } + if ( multiplayer == CLIENT ) + { + strcpy((char*)net_packet->data, "CALL"); + net_packet->data[4] = getPlayer(); + SDLNet_Write32(lockOnEntityUid, &net_packet->data[5]); + net_packet->data[9] = (Uint8)cmd; + net_packet->len = 10; + if ( lockOnEntityUid == 0 ) + { + Uint16 _x = std::min(std::max(0.0, moveToX / 16), map.width - 1); + Uint16 _y = std::min(std::max(0.0, moveToY / 16), map.height - 1); + SDLNet_Write16(_x, &net_packet->data[10]); + SDLNet_Write16(_y, &net_packet->data[12]); + net_packet->len = 14; + } + net_packet->address.host = net_server.host; + net_packet->address.port = net_server.port; + sendPacketSafe(net_sock, -1, net_packet, 0); + } + else + { + for ( int i = 0; i < MAXPLAYERS; ++i ) + { + std::string text = setCalloutText(nullptr, getCalloutKeyForCommand(cmd).c_str(), 0, cmd, SET_CALLOUT_WORLD_TEXT, i); + if ( text != "" ) + { + messagePlayerColor(i, MESSAGE_INTERACTION, playerColor(getPlayer(), colorblind_lobby, false), + text.c_str()); + } + } + } } -void CalloutRadialMenu::createParticleCallout(real_t x, real_t y, real_t z, Uint32 uid, CalloutRadialMenu::CalloutCommand _cmd) + +std::string CalloutRadialMenu::getCalloutKeyForCommand(CalloutRadialMenu::CalloutCommand cmd) { - if ( _cmd == CALLOUT_CMD_CANCEL ) { return; } - CalloutMenu[clientnum].callouts[uid] = CalloutRadialMenu::CalloutParticle_t(getPlayer(), x, y, z, uid, _cmd); + if ( cmd == CALLOUT_CMD_LOOK ) + { + return "look_at"; + } + else if ( cmd == CALLOUT_CMD_HELP ) + { + return "help"; + } + else if ( cmd == CALLOUT_CMD_AFFIRMATIVE ) + { + return "affirmative"; + } + else if ( cmd == CALLOUT_CMD_NEGATIVE ) + { + return "negative"; + } + else if ( cmd == CALLOUT_CMD_MOVE ) + { + return "move"; + } + return ""; } std::string CalloutRadialMenu::getIconPathForCommand(CalloutRadialMenu::CalloutCommand cmd, CalloutType type, bool highlight) @@ -22888,77 +24195,36 @@ void CalloutRadialMenu::drawCalloutMenu() } } - if ( multiplayer == CLIENT ) + if ( lockOnEntityUid == 0 ) { - /*if ( optionSelected == ALLY_CMD_ATTACK_CONFIRM ) + if ( (CalloutCommand)optionSelected == CALLOUT_CMD_AFFIRMATIVE + || (CalloutCommand)optionSelected == CALLOUT_CMD_NEGATIVE ) { - Uint32 olduid = followerToCommand->monsterAllyInteractTarget; - sendAllyCommandClient(gui_player, followerToCommand->getUID(), optionSelected, 0, 0, followerToCommand->monsterAllyInteractTarget); - Uint32 newuid = followerToCommand->monsterAllyInteractTarget; - if ( modifierPressed ) - { - followerToCommand->monsterAllyInteractTarget = olduid; - auto repeatCommandToFollowers = getAllOtherFollowersForSendAllCommand(gui_player, followerToCommand, followerStats->type, optionSelected); - for ( auto f : repeatCommandToFollowers ) - { - f->monsterAllyInteractTarget = olduid; - sendAllyCommandClient(gui_player, f->getUID(), optionSelected, 0, 0, f->monsterAllyInteractTarget); - } - followerToCommand->monsterAllyInteractTarget = newuid; - } + lockOnEntityUid = achievementObserver.playerUids[gui_player]; } - else if ( optionSelected == ALLY_CMD_MOVETO_CONFIRM ) + else if ( (CalloutCommand)optionSelected == CALLOUT_CMD_HELP + || (CalloutCommand)optionSelected == CALLOUT_CMD_MOVE ) { - sendAllyCommandClient(gui_player, followerToCommand->getUID(), optionSelected, moveToX, moveToY); - if ( modifierPressed ) - { - auto repeatCommandToFollowers = getAllOtherFollowersForSendAllCommand(gui_player, followerToCommand, followerStats->type, optionSelected); - for ( auto f : repeatCommandToFollowers ) - { - sendAllyCommandClient(gui_player, f->getUID(), optionSelected, moveToX, moveToY); - } - } + lockOnEntityUid = achievementObserver.playerUids[gui_player]; } - else - { - sendAllyCommandClient(gui_player, followerToCommand->getUID(), optionSelected, 0, 0); - if ( modifierPressed ) - { - auto repeatCommandToFollowers = getAllOtherFollowersForSendAllCommand(gui_player, followerToCommand, followerStats->type, optionSelected); - for ( auto f : repeatCommandToFollowers ) - { - sendAllyCommandClient(gui_player, f->getUID(), optionSelected, 0, 0); - } - } - }*/ } - else + + if ( lockOnEntityUid ) { - if ( lockOnEntityUid ) + if ( Entity* target = uidToEntity(lockOnEntityUid) ) { - if ( Entity* target = uidToEntity(lockOnEntityUid) ) + if ( createParticleCallout(target, (CalloutCommand)optionSelected) ) { - createParticleCallout(target, (CalloutCommand)optionSelected); + sendCalloutText((CalloutCommand)optionSelected); } } - else + } + else + { + if ( createParticleCallout((real_t)moveToX, (real_t)moveToY, -4, 0, (CalloutCommand)optionSelected) ) { - createParticleCallout((real_t)moveToX, (real_t)moveToY, -4, 0, (CalloutCommand)optionSelected); + sendCalloutText((CalloutCommand)optionSelected); } - /*Uint32 olduid = followerToCommand->monsterAllyInteractTarget; - followerToCommand->monsterAllySendCommand(optionSelected, moveToX, moveToY, followerToCommand->monsterAllyInteractTarget); - Uint32 newuid = followerToCommand->monsterAllyInteractTarget; - if ( modifierPressed ) - { - followerToCommand->monsterAllyInteractTarget = olduid; - auto repeatCommandToFollowers = getAllOtherFollowersForSendAllCommand(gui_player, followerToCommand, followerStats->type, optionSelected); - for ( auto f : repeatCommandToFollowers ) - { - f->monsterAllyInteractTarget = olduid; - f->monsterAllySendCommand(optionSelected, moveToX, moveToY, f->monsterAllyInteractTarget); - } - followerToCommand->monsterAllyInteractTarget = newuid; - }*/ } } @@ -23198,7 +24464,7 @@ void CalloutRadialMenu::drawCalloutMenu() if ( i == highlight ) { panelIcons[i]->path = iconEntries["look_at"].path_hover; - setCalloutBannerText(bannerTxt, "look_at", textHighlightColor, (CalloutCommand)i); + setCalloutText(bannerTxt, "look_at", textHighlightColor, (CalloutCommand)i, SET_CALLOUT_BANNER_TEXT, -1); } else { @@ -23210,7 +24476,7 @@ void CalloutRadialMenu::drawCalloutMenu() if ( i == highlight ) { panelIcons[i]->path = iconEntries["help"].path_hover; - setCalloutBannerText(bannerTxt, "help", textHighlightColor, (CalloutCommand)i); + setCalloutText(bannerTxt, "help", textHighlightColor, (CalloutCommand)i, SET_CALLOUT_BANNER_TEXT, -1); } else { @@ -23222,7 +24488,7 @@ void CalloutRadialMenu::drawCalloutMenu() if ( i == highlight ) { panelIcons[i]->path = iconEntries["affirmative"].path_hover; - setCalloutBannerText(bannerTxt, "affirmative", textHighlightColor, (CalloutCommand)i); + setCalloutText(bannerTxt, "affirmative", textHighlightColor, (CalloutCommand)i, SET_CALLOUT_BANNER_TEXT, -1); } else { @@ -23234,7 +24500,7 @@ void CalloutRadialMenu::drawCalloutMenu() if ( i == highlight ) { panelIcons[i]->path = iconEntries["negative"].path_hover; - setCalloutBannerText(bannerTxt, "negative", textHighlightColor, (CalloutCommand)i); + setCalloutText(bannerTxt, "negative", textHighlightColor, (CalloutCommand)i, SET_CALLOUT_BANNER_TEXT, -1); } else { @@ -23246,7 +24512,7 @@ void CalloutRadialMenu::drawCalloutMenu() if ( i == highlight ) { panelIcons[i]->path = iconEntries["move"].path_hover; - setCalloutBannerText(bannerTxt, "move", textHighlightColor, (CalloutCommand)i); + setCalloutText(bannerTxt, "move", textHighlightColor, (CalloutCommand)i, SET_CALLOUT_BANNER_TEXT, -1); } else { @@ -23637,10 +24903,6 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat { if ( updateInteractText ) { - if ( !interactItems && !interactWorld && enableAttack ) - { - strcpy(interactText, Language::get(4347)); // "Callout " - } strcat(interactText, Language::get(4309)); // "shrine" } } @@ -23668,15 +24930,15 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat { if ( secretlevel && selectedEntity.skill[3] == 1 ) // secret ladder { - strcat(interactText, Language::get(4310)); // "ladder" + strcat(interactText, Language::get(4360)); // "secret exit" } else if ( !secretlevel && selectedEntity.skill[3] == 1 ) // secret ladder { - strcat(interactText, Language::get(4310)); // "ladder" + strcat(interactText, Language::get(4359)); // "secret entrance" } else { - strcat(interactText, Language::get(4310)); // "ladder" + strcat(interactText, Language::get(4361)); // "level exit" } } } @@ -23688,26 +24950,26 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat { if ( secretlevel ) { - strcat(interactText, Language::get(4311)); // "portal" + strcat(interactText, Language::get(4360)); // "secret exit" } else { - strcat(interactText, Language::get(4311)); // "portal" + strcat(interactText, Language::get(4359)); // "secret entrance" } } else { if ( !strcmp(map.name, "Hell") ) { - strcat(interactText, Language::get(4311)); // "portal" + strcat(interactText, Language::get(4361)); // "level exit" } else if ( !strcmp(map.name, "Mages Guild") ) { - strcat(interactText, Language::get(4311)); // "portal" + strcat(interactText, Language::get(4361)); // "level exit" } else { - strcat(interactText, Language::get(4311)); // "portal" + strcat(interactText, Language::get(4361)); // "level exit" } } } @@ -23725,17 +24987,17 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat { if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_TUTORIAL ) { - strcat(interactText, Language::get(4310)); // "ladder" + strcat(interactText, Language::get(4361)); // "level exit" } else { if ( selectedEntity.portalCustomSpriteAnimationFrames > 0 ) { - strcat(interactText, Language::get(4311)); // "portal" + strcat(interactText, Language::get(4361)); // "level exit" } else { - strcat(interactText, Language::get(4310)); // "ladder" + strcat(interactText, Language::get(4361)); // "level exit" } } } @@ -23748,14 +25010,23 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat strcat(interactText, Language::get(4311)); // "portal" } } - //else if ( selectedEntity.behavior == &actBomb && interactWorld ) - //{ - // if ( updateInteractText ) - // { - // strcpy(interactText, Language::get(3093)); - // strcat(interactText, Language::get(4045)); // "trap" - // } - //} + else if ( selectedEntity.behavior == &actBomb || selectedEntity.behavior == &actBeartrap ) + { + if ( updateInteractText ) + { + if ( selectedEntity.behavior == &actBomb ) + { + if ( selectedEntity.skill[21] >= WOODEN_SHIELD && selectedEntity.skill[21] < NUMITEMS ) + { + strcat(interactText, items[selectedEntity.skill[21]].getIdentifiedName()); + } + } + else if ( selectedEntity.behavior == &actBeartrap ) + { + strcat(interactText, items[TOOL_BEARTRAP].getIdentifiedName()); + } + } + } else if ( selectedEntity.behavior == &actBoulderTrapHole && interactWorld ) { @@ -23821,6 +25092,13 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat strcat(interactText, Language::get(4357)); // "grave" } } + else if ( selectedEntity.behavior == &actCampfire ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(4365)); // "campfire" + } + } else if ( selectedEntity.behavior == &actPowerCrystal || selectedEntity.behavior == &actPowerCrystalBase ) { if ( updateInteractText ) @@ -23843,6 +25121,27 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat strcat(interactText, getMonsterLocalizedName((Monster)monsterType).c_str()); } } + else if ( selectedEntity.behavior == &actColliderDecoration && selectedEntity.isDamageableCollider() ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(selectedEntity.getColliderLangName())); + } + } + else if ( selectedEntity.behavior == &actFloorDecoration && selectedEntity.sprite == 991 ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(4363)); // "sign" + } + } + else if ( selectedEntity.behavior == &actPedestalBase ) + { + if ( updateInteractText ) + { + strcat(interactText, Language::get(4364)); + } + } else { if ( updateInteractText ) diff --git a/src/interface/interface.hpp b/src/interface/interface.hpp index 517e86342..a817aeae6 100644 --- a/src/interface/interface.hpp +++ b/src/interface/interface.hpp @@ -1346,12 +1346,14 @@ struct CalloutRadialMenu char interactText[128]; // user moused over object while selecting interact object. int maxMonstersToDraw; int gui_player = 0; - Frame* calloutFrame; + Frame* calloutFrame = nullptr; + Frame* calloutPingFrame = nullptr; bool bOpen = false; Uint32 lockOnEntityUid = 0; CalloutRadialMenu() : calloutFrame(nullptr), + calloutPingFrame(nullptr), menuX(-1), menuY(-1), optionSelected(-1), @@ -1400,9 +1402,32 @@ struct CalloutRadialMenu std::string path_active_hover = ""; int icon_offsetx = 0; int icon_offsety = 0; - std::map>> text_map; + struct IconEntryText_t + { + std::string bannerText = ""; + std::set bannerHighlights; + std::string worldMsgSays = ""; + std::string worldMsg = ""; + std::string worldMsgEmote = ""; + std::string worldMsgEmoteYou = ""; + std::string worldIconTag = ""; + std::string worldIconTagMini = ""; + }; + std::map text_map; }; static std::map iconEntries; + struct WorldIconEntry_t + { + std::string pathDefault = ""; + std::string pathPlayer1 = ""; + std::string pathPlayer2 = ""; + std::string pathPlayer3 = ""; + std::string pathPlayer4 = ""; + std::string pathPlayerX = ""; + int id = 0; + }; + static std::map worldIconEntries; + static std::map worldIconIDToEntryKey; static int followerWheelRadius; static int followerWheelButtonThickness; static int followerWheelFrameOffsetX; @@ -1429,14 +1454,28 @@ struct CalloutRadialMenu CALLOUT_TYPE_NO_TARGET, CALLOUT_TYPE_NPC, CALLOUT_TYPE_PLAYER, - CALLOUT_TYPE_LEVER, CALLOUT_TYPE_BOULDER, CALLOUT_TYPE_TRAP, CALLOUT_TYPE_GENERIC_INTERACTABLE, CALLOUT_TYPE_CHEST, CALLOUT_TYPE_ITEM, + CALLOUT_TYPE_SWITCH, + CALLOUT_TYPE_SWITCH_ON, + CALLOUT_TYPE_SWITCH_OFF, + CALLOUT_TYPE_SHRINE, + CALLOUT_TYPE_EXIT, + CALLOUT_TYPE_SECRET_EXIT, + CALLOUT_TYPE_SECRET_ENTRANCE, + CALLOUT_TYPE_GOLD, + CALLOUT_TYPE_FOUNTAIN, CALLOUT_TYPE_NPC_ENEMY, - CALLOUT_TYPE_NPC_PLAYERALLY + CALLOUT_TYPE_NPC_PLAYERALLY, + CALLOUT_TYPE_TELEPORTER_LADDER_UP, + CALLOUT_TYPE_TELEPORTER_LADDER_DOWN, + CALLOUT_TYPE_TELEPORTER_PORTAL, + CALLOUT_TYPE_BOMB_TRAP, + CALLOUT_TYPE_COLLIDER_BREAKABLE + /*,CALLOUT_TYPE_PEDESTAL*/ }; struct CalloutParticle_t { @@ -1445,10 +1484,25 @@ struct CalloutRadialMenu real_t z = 0.0; Uint32 entityUid = 0; Uint32 ticks = 0; + Uint32 creationTick = 0; CalloutCommand cmd = CALLOUT_CMD_END; CalloutType type = CALLOUT_TYPE_NO_TARGET; bool expired = false; - bool lockOnScreen = true; + bool lockOnScreen[MAXPLAYERS]; + int playerColor = -1; + int tagID = -1; + int tagSmallID = -1; + int animateState = 0; + int animateStateInit = 0; + real_t scale = 1.0; + real_t animateX = 0.0; + real_t animateScaleForPlayerView[MAXPLAYERS]; + real_t animateBounce = 0.0; + real_t animateY = 0.0; + bool doMessage = true; + Uint32 messageSentTick = 0; + bool big[MAXPLAYERS]; + void animate(); void init(const int player); CalloutParticle_t() = default; CalloutParticle_t(const int player, real_t _x, real_t _y, real_t _z, Uint32 _uid, CalloutCommand _cmd) : @@ -1476,12 +1530,21 @@ struct CalloutRadialMenu void initCalloutMenuGUICursor(bool openInventory); void closeCalloutMenuGUI(); bool allowedInteractEntity(Entity& selectedEntity, bool updateInteractText = true); - void createParticleCallout(real_t x, real_t y, real_t z, Uint32 uid, CalloutCommand _cmd = CALLOUT_CMD_LOOK); - void createParticleCallout(Entity* entity, CalloutCommand _cmd = CALLOUT_CMD_LOOK); + bool createParticleCallout(real_t x, real_t y, real_t z, Uint32 uid, CalloutCommand _cmd = CALLOUT_CMD_LOOK); // if true, send message + bool createParticleCallout(Entity* entity, CalloutCommand _cmd = CALLOUT_CMD_LOOK); // if true, send message static std::string getIconPathForCommand(CalloutCommand cmd, CalloutType type, bool highlight); - void setCalloutBannerText(Field* field, const char* iconName, Uint32 color, CalloutCommand cmd); + enum SetCalloutTextTypes : int { + SET_CALLOUT_BANNER_TEXT, + SET_CALLOUT_WORLD_TEXT, + SET_CALLOUT_ICON_KEY + }; + std::string setCalloutText(Field* field, const char* iconName, Uint32 color, CalloutCommand cmd, SetCalloutTextTypes setType, const int targetPlayer); + std::string getCalloutMessage(const IconEntry::IconEntryText_t& text_map, const char* object, const int targetPlayer); + void sendCalloutText(CalloutCommand cmd); + static std::string getCalloutKeyForCommand(CalloutCommand cmd); static CalloutType getCalloutTypeForUid(const int player, Uint32 uid); static CalloutType getCalloutTypeForEntity(const int player, Entity* parent); + static void drawCallouts(const int playernum); /*void selectNextFollower(); int numMonstersToDrawInParty(); void updateScrollPartySheet(); diff --git a/src/menu.cpp b/src/menu.cpp index d94ba1276..14d1f536d 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -10243,7 +10243,7 @@ void doEndgame(bool saveHighscore) { if ( victory ) { int k = 0; - for ( c = 0; c < MAXPLAYERS; c++ ) + for ( int c = 0; c < MAXPLAYERS; c++ ) { if (players[c] && players[c]->entity) { diff --git a/src/net.cpp b/src/net.cpp index b84f632cc..b233c67a0 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -3566,6 +3566,40 @@ static std::unordered_map clientPacketHandlers = { } }}, + // server forwarded a player callout + { 'CALL', []() { + const int pnum = std::min(net_packet->data[4], (Uint8)(MAXPLAYERS - 1)); + if ( pnum != clientnum ) + { + Uint32 uid = SDLNet_Read32(&net_packet->data[5]); + Entity* entity = nullptr; + if ( uid != 0 ) + { + entity = uidToEntity(uid); + if ( !entity ) + { + return; + } + } + CalloutMenu[pnum].lockOnEntityUid = uid; + CalloutRadialMenu::CalloutCommand cmd = (CalloutRadialMenu::CalloutCommand)net_packet->data[9]; + if ( uid ) + { + if ( entity ) + { + CalloutMenu[pnum].createParticleCallout(entity, cmd); + } + } + else + { + real_t x = SDLNet_Read16(&net_packet->data[10]); + real_t y = SDLNet_Read16(&net_packet->data[12]); + CalloutMenu[pnum].createParticleCallout( + x * 16.0 + 8.0, y * 16.0 + 8.0, -4, 0, cmd); + } + } + }}, + // textbox message {'MSGS', [](){ Uint32 color = SDLNet_Read32(&net_packet->data[4]); @@ -5204,6 +5238,42 @@ static std::unordered_map serverPacketHandlers = { messagePlayer(clientnum, MESSAGE_MISC, Language::get(1120), shortname); }}, + // client callout + {'CALL', []() { + const int pnum = std::min(net_packet->data[4], (Uint8)(MAXPLAYERS - 1)); + if ( pnum != clientnum ) + { + Uint32 uid = SDLNet_Read32(&net_packet->data[5]); + Entity* entity = uidToEntity(uid); + if ( uid != 0 ) + { + if ( !entity ) + { + return; + } + } + CalloutMenu[pnum].lockOnEntityUid = uid; + CalloutRadialMenu::CalloutCommand cmd = (CalloutRadialMenu::CalloutCommand)net_packet->data[9]; + + if ( uid == 0 ) + { + real_t x = SDLNet_Read16(&net_packet->data[10]); + real_t y = SDLNet_Read16(&net_packet->data[12]); + if ( CalloutMenu[pnum].createParticleCallout(x * 16.0 + 8.0, y * 16.0 + 8.0, -4, 0, cmd) ) + { + CalloutMenu[pnum].sendCalloutText(cmd); + } + } + else + { + if ( CalloutMenu[pnum].createParticleCallout(entity, cmd) ) + { + CalloutMenu[pnum].sendCalloutText(cmd); + } + } + } + }}, + // message {'MSGS', [](){ const int pnum = std::min(net_packet->data[4], (Uint8)(MAXPLAYERS - 1)); diff --git a/src/player.cpp b/src/player.cpp index 885872770..6a27bec27 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -3291,7 +3291,7 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) { return 0.0; } - if ( parent->getMonsterTypeFromSprite() == SHOPKEEPER ) + if ( parent->getMonsterTypeFromSprite() == SHOPKEEPER && !selectInteract ) { if ( !shopIsMysteriousShopkeeper(parent) && ShopkeeperPlayerHostility.isPlayerEnemy(player.playernum) ) { @@ -3485,11 +3485,15 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) } } - Entity* particle = spawnMagicParticle(players[player.playernum]->entity); - particle->sprite = 942; - particle->x = previousx; - particle->y = previousy; - particle->z = startz; + static ConsoleVariable cvar_calloutboulderdebug("/calloutboulderdebug", false); + if ( *cvar_calloutboulderdebug ) + { + Entity* particle = spawnMagicParticle(players[player.playernum]->entity); + particle->sprite = 942; + particle->x = previousx; + particle->y = previousy; + particle->z = startz; + } real_t lookDist = sqrt(pow(previousx - players[player.playernum]->entity->x, 2) + pow(previousy - players[player.playernum]->entity->y, 2)); if ( abs(dist - lookDist) > 8.25 ) @@ -3545,6 +3549,9 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) if ( cameras[player.playernum].vang > PI ) // looking above horizon { if ( parent->behavior == &actItem || parent->behavior == &actGoldBag + || parent->behavior == &actLadder + || parent->behavior == &actBeartrap + || parent->behavior == &actBomb || parent->behavior == &actSwitch || parent->behavior == &actSwitchWithTimer ) { return 0.0; @@ -3559,6 +3566,9 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) } } else if ( parent && ((parent->behavior == &actItem && parent->z > 4) || parent->behavior == &actGoldBag + || parent->behavior == &actLadder + || parent->behavior == &actBeartrap + || parent->behavior == &actBomb || parent->behavior == &actSwitch || parent->behavior == &actSwitchWithTimer) ) { if ( dist < 32.0 && abs(dist - lookDist) > 16.0 @@ -3602,6 +3612,20 @@ void Player::WorldUI_t::setTooltipActive(Entity& tooltip) setTooltipDisabled(tooltip); return; } + else if ( parent->behavior == &actColliderDecoration ) + { + if ( CalloutMenu[player.playernum].calloutMenuIsOpen() + && CalloutMenu[player.playernum].selectMoveTo + && CalloutMenu[player.playernum].optionSelected == CalloutRadialMenu::CALLOUT_CMD_SELECT ) + { + // allowed in callout mode, otherwise no + } + else + { + setTooltipDisabled(tooltip); + return; + } + } parent->highlightForUI = 1.0; if ( tooltip.worldTooltipRequiresButtonHeld == 1 && *MainMenu::cvar_hold_to_activate ) @@ -6528,6 +6552,7 @@ void Player::clearGUIPointers() FollowerMenu[playernum].followerFrame = nullptr; CalloutMenu[playernum].calloutFrame = nullptr; + CalloutMenu[playernum].calloutPingFrame = nullptr; } const char* Player::getAccountName() const { diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index b7134fb8b..9f2e007e7 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -9626,132 +9626,6 @@ static void checkControllerState(int player) { controllerFrame->setHollow(true); } -void drawCallouts(const int playernum) -{ - for ( auto& callout : CalloutMenu[playernum].callouts ) - { - std::string iconPath = CalloutRadialMenu::getIconPathForCommand(callout.second.cmd, callout.second.type, - true); - if ( iconPath == "" ) - { - continue; - } - - vec4_t v; - mat4x4_t m, t; - - auto camera = &cameras[playernum]; - auto& player = players[playernum]; - - const int offset = 40; - int leftOfWindow = player->camera_virtualx1() + offset; - int rightOfWindow = player->camera_virtualx1() + player->camera_virtualWidth() - offset; - int topOfWindow = player->camera_virtualy1() + offset; - int bottomOfWindow = player->camera_virtualy2() - offset; - - mat4x4_t id; - vec4_t world{ (float)callout.second.x * 2.f, -(float)callout.second.z * 2.f, (float)callout.second.y * 2.f, 1.f }; - vec4_t window2{ (float)0, (float)0, - (float)player->camera_virtualWidth(), (float)player->camera_virtualHeight() }; - SDL_Rect dest{ 0, 0, 0, 0 }; - if ( callout.second.lockOnScreen ) - { - auto screen_position = project_clipped2(&world, &id, &camera->projview, &window2); - dest = SDL_Rect{ player->camera_virtualx1() + (int)screen_position.clipped_coords.x, - player->camera_virtualy1() + Frame::virtualScreenY - (Frame::virtualScreenY - player->camera_virtualHeight()) - (int)screen_position.clipped_coords.y, - 14, 22 }; - if ( !screen_position.isBehind - && (screen_position.direction == ClipResult::Direction::Front - || screen_position.direction == ClipResult::Direction::Invalid) ) - { - callout.second.lockOnScreen = false; - } - - dest.x = std::min(rightOfWindow, std::max(leftOfWindow, dest.x)); - dest.y = std::min(bottomOfWindow, std::max(topOfWindow, dest.y)); - real_t tangent = atan2(camera->y * 32.0 - world.z, camera->x * 32.0 - world.x); - real_t camang = camera->ang; - while ( tangent >= 2 * PI ) - { - tangent -= PI * 2; - } - while ( tangent < 0 ) - { - tangent += PI * 2; - } - while ( camang >= 2 * PI ) - { - camang -= PI * 2; - } - while ( camang < 0 ) - { - camang += PI * 2; - } - real_t result = tangent - camang; - while ( result >= PI ) - { - result -= PI * 2; - } - while ( result < -PI ) - { - result += PI * 2; - } - //messagePlayer(player->playernum, MESSAGE_DEBUG, "%f", ((PI - abs(abs(tangent - camang) - PI)) * 2)); - //messagePlayer(player->playernum, MESSAGE_DEBUG, "%f", result); - if ( result >= 0.0 && result < PI / 2 ) - { - dest.x = leftOfWindow; - } - else if ( result < 0.0 && result > -PI / 2 ) - { - dest.x = rightOfWindow; - } - - if ( abs(result) < (3 * PI / 4) ) - { - real_t mult = std::min(1.0, ((3 * PI / 4) - abs(result)) / (PI / 2)); - dest.y += ((player->camera_virtualHeight() / 2) - dest.y) * mult; - } - } - else - { - auto screen_position = project(&world, &id, &camera->projview, &window2); - if ( screen_position.z >= 1.0 || screen_position.z < 0.0 ) - { - continue; - } - dest = SDL_Rect{ player->camera_virtualx1() + (int)screen_position.x, - player->camera_virtualy1() + Frame::virtualScreenY - (Frame::virtualScreenY - player->camera_virtualHeight()) - - (int)screen_position.y, - 14, 22 }; - } - - real_t lifePercent = callout.second.ticks / (real_t)CalloutRadialMenu::CalloutParticle_t::kParticleLifetime; - Uint32 alpha = 255; - if ( lifePercent >= 0.8 ) - { - alpha -= std::min((Uint32)255, (Uint32)(255 * (lifePercent - 0.8) / 0.2)); - } - Uint32 color = makeColor(255, 255, 255, alpha); - SDL_Rect iconPos = dest; - if ( auto image = Image::get(iconPath.c_str()) ) - { - iconPos.w = image->getWidth(); - iconPos.h = image->getHeight(); - iconPos.x -= iconPos.w / 2; - iconPos.y -= iconPos.h; - image->drawColor(nullptr, iconPos, - SDL_Rect{ 0, 0, Frame::virtualScreenX, Frame::virtualScreenY }, color); - } - - dest.x -= dest.w / 2; - dest.y -= dest.h / 2; - auto image = Image::get("*#images/ui/CalloutWheel/WorldIcons/cmd_playertagRed_sm.png"); - image->drawColor(nullptr, dest, - SDL_Rect{ 0, 0, Frame::virtualScreenX, Frame::virtualScreenY }, color); - } -} - void Player::HUD_t::processHUD() { const SDL_Rect hudSize{ @@ -9768,9 +9642,6 @@ void Player::HUD_t::processHUD() hudFrame->setHollow(true); hudFrame->setBorder(0); hudFrame->setOwner(player.playernum); - hudFrame->setDrawCallback([](const Widget& widget, SDL_Rect rect) { - drawCallouts(widget.getOwner()); - }); } if ( !minotaurSharedDisplay && player.playernum == 0 ) From 8bb1d9e5c832aac996ff18466429b56838e8dc74 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 26 Aug 2023 01:13:31 +1000 Subject: [PATCH 011/146] * fix steam statistic missing commit --- src/net.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index b233c67a0..eaef7b028 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2824,11 +2824,11 @@ static std::unordered_map clientPacketHandlers = { steamAchievement((char*)(&net_packet->data[4])); }}, - // update steam statistic + // update steam statistic {'SSTA', []() { const int statisticNum = static_cast(net_packet->data[4]); int value = static_cast(SDLNet_Read16(&net_packet->data[6])); - steamStatisticUpdate(player, static_cast(net_packet->data[5]), value); + steamStatisticUpdate(statisticNum, static_cast(net_packet->data[5]), value); }}, // pause game From eb2a3b0ed033467eb3e3f69d2a2e51fd18ceede2 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 29 Aug 2023 22:19:36 +1000 Subject: [PATCH 012/146] * torch no degrade while shapeshifted --- src/entity.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/entity.cpp b/src/entity.cpp index 06f328d28..737ae506d 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -3817,7 +3817,11 @@ void Entity::handleEffects(Stat* myStats) if ( (this->char_torchtime >= 7500 && myStats->shield->type == TOOL_TORCH) || (this->char_torchtime >= 10500) ) { this->char_torchtime = 0; - if ( myStats->shield->type == TOOL_TORCH && player >= 0 ) + if ( behavior == &actPlayer && effectShapeshift != NOTHING ) + { + // do nothing, shapeshifted + } + else if ( myStats->shield->type == TOOL_TORCH && player >= 0 ) { std::string itemName = myStats->shield->getName(); ItemType itemType = myStats->shield->type; From b611fef1850c46f17879858ec7bc2a2776271c04 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 31 Aug 2023 11:33:59 +1000 Subject: [PATCH 013/146] * only show warning for mods below 4.0.0 --- src/ui/MainMenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 0d44a0e79..b5f3f22c4 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -24160,7 +24160,7 @@ namespace MainMenu { if ( !viewMyItems ) { - if ( currentVersionNum > latestVersionNum ) + if ( latestVersionNum < 400 && currentVersionNum > latestVersionNum ) { auto imgPos = versionPos; imgPos.y += 2; From 26ba019b050d86ac0759374656a0672e90957281 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 31 Aug 2023 11:34:24 +1000 Subject: [PATCH 014/146] * gog version can use achievements to unlock classes for all races --- src/menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menu.cpp b/src/menu.cpp index 14d1f536d..d222b6b0b 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -628,7 +628,7 @@ bool isAchievementUnlockedForClassUnlock(PlayerRaces race) { return unlocked; } -#elif defined USE_EOS +#elif (defined USE_EOS || defined LOCAL_ACHIEVEMENTS) if ( enabledDLCPack1 && race == RACE_SKELETON && achievementUnlocked("BARONY_ACH_BONY_BARON") ) { return true; From 68db7ccc0e7e0ddb6c22521d9906af39df3f9416 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 31 Aug 2023 11:34:55 +1000 Subject: [PATCH 015/146] * /hunger console command can be used for clients to debug --- src/interface/consolecommand.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/interface/consolecommand.cpp b/src/interface/consolecommand.cpp index 5f919ac89..057832830 100644 --- a/src/interface/consolecommand.cpp +++ b/src/interface/consolecommand.cpp @@ -1208,6 +1208,11 @@ namespace ConsoleCommands { return; } + int player = clientnum; + if ( argc == 2 ) { + player = atoi(argv[1]); + } + if (multiplayer == SINGLE) { Stat* tempStats = players[clientnum]->entity->getStats(); @@ -1216,9 +1221,21 @@ namespace ConsoleCommands { tempStats->HUNGER = std::max(0, tempStats->HUNGER - 100); } } + else if ( multiplayer == SERVER ) + { + if ( player >= 0 && player < MAXPLAYERS ) + { + Stat* tempStats = players[player]->entity->getStats(); + if ( tempStats ) + { + tempStats->HUNGER = std::max(0, tempStats->HUNGER - 100); + } + serverUpdateHunger(player); + } + } else { - messagePlayer(clientnum, MESSAGE_MISC, Language::get(299)); + messagePlayer(clientnum, MESSAGE_MISC, Language::get(284)); } }); From e8d51b6a04a6c71d99c9c3a13a6c9fbe3cdcc577 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 31 Aug 2023 11:35:24 +1000 Subject: [PATCH 016/146] * fix stealth tooltip inaccuracies --- src/entity.cpp | 15 +++++++++++---- src/ui/GameUI.cpp | 3 ++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/entity.cpp b/src/entity.cpp index 737ae506d..5237f4770 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -849,7 +849,8 @@ int Entity::entityLightAfterReductions(Stat& myStats, Entity* observer) int player = -1; const int minLight = (int)(TOUCHRANGE * 1.5); int light = std::max(minLight, entityLight()); // max 255 light to start with. - if ( !isInvisible() ) + bool invis = isInvisible(); + if ( !invis ) { bool sneaking = false; if ( behavior == &actPlayer ) @@ -891,13 +892,19 @@ int Entity::entityLightAfterReductions(Stat& myStats, Entity* observer) } else { - light -= myStats.PROFICIENCIES[PRO_STEALTH] * 2; + if ( sneaking ) + { + light /= 2; // halve for sneaking + } + light -= (light - TOUCHRANGE) * (1.0 * (myStats.PROFICIENCIES[PRO_STEALTH] / 100.0)); // reduce to 32 as sneak approaches 100 } } - else + + if ( invis ) { - light = TOUCHRANGE; + light = std::min(light, TOUCHRANGE); } + light = std::max(light, 0); if ( myStats.type == DUMMYBOT ) { diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 9f2e007e7..21aaf5952 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -30307,7 +30307,8 @@ std::string formatSkillSheetEffects(int playernum, int proficiency, std::string& { if ( tag == "STEALTH_VIS_REDUCTION" ) { - val = stats[playernum]->PROFICIENCIES[proficiency] * 2 * 100 / 512.f; // % visibility reduction + //val = stats[playernum]->PROFICIENCIES[proficiency] * 2 * 100 / 512.f; // % visibility reduction + val = 100.f * (255 - TOUCHRANGE) * (1.0 * (stats[playernum]->PROFICIENCIES[PRO_STEALTH] / 100.f)) / 255.f; snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); } else if ( tag == "STEALTH_SNEAK_VIS" ) From 540dc88c2a4457fa56a71d8630f3ed2b5b0abc37 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 31 Aug 2023 11:41:26 +1000 Subject: [PATCH 017/146] * more callout tags for help, and sinks --- src/interface/interface.cpp | 227 +++++++++++++++++++++++++++++++----- src/interface/interface.hpp | 18 +++ src/net.cpp | 11 +- 3 files changed, 224 insertions(+), 32 deletions(-) diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index 5e1373d10..2757339dc 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -22228,12 +22228,12 @@ std::string CalloutRadialMenu::getCalloutMessage(const IconEntry::IconEntryText_ if ( object ) { snprintf(buf, sizeof(buf), text_map.worldMsg.c_str(), object); + return buf; } else { return text_map.worldMsg; } - return buf; } } @@ -22248,14 +22248,35 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName } std::string key = "default"; Entity* entity = uidToEntity(lockOnEntityUid); - if ( lockOnEntityUid == 0 ) + const int player = getPlayer(); + if ( lockOnEntityUid == 0 && players[player]->isLocalPlayer() ) { if ( cmd == CALLOUT_CMD_AFFIRMATIVE || cmd == CALLOUT_CMD_NEGATIVE ) { - entity = players[getPlayer()]->entity; + entity = players[player]->entity; + } + else if ( cmd == CALLOUT_CMD_HELP ) + { + entity = players[player]->entity; } } - const int player = getPlayer(); + + if ( cmd == CALLOUT_CMD_MOVE ) + { + if ( setType == SET_CALLOUT_BANNER_TEXT ) + { + setCalloutBannerTextUnformatted(player, field, iconName, "default", color); + } + else if ( setType == SET_CALLOUT_ICON_KEY ) + { + return "default"; + } + else + { + return getCalloutMessage(findIcon->second.text_map["default"], nullptr, targetPlayer); + } + return ""; + } auto calloutType = getCalloutTypeForEntity(player, entity); auto& text_map = findIcon->second.text_map; @@ -22265,6 +22286,102 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName key = "location"; break; case CALLOUT_TYPE_PLAYER: + if ( cmd == CALLOUT_CMD_HELP && entity && entity->behavior == &actPlayer + && player >= 0 && entity == players[player]->entity ) + { + if ( players[player]->isLocalPlayer() ) + { + clientCalloutHelpFlags = 0; + auto& fx = StatusEffectQueue[player]; + EntityHungerIntervals hunger = EntityHungerIntervals::HUNGER_INTERVAL_OVERSATIATED; + bool hungerBlood = false; + for ( auto& eff : fx.effectQueue ) + { + if ( eff.effect == StatusEffectQueue_t::kEffectBread || eff.effect == StatusEffectQueue_t::kEffectBloodHunger ) + { + if ( eff.effect == StatusEffectQueue_t::kEffectBloodHunger ) + { + hungerBlood = true; + } + if ( eff.customVariable <= getEntityHungerInterval(player, nullptr, stats[player], HUNGER_INTERVAL_STARVING) ) + { + clientCalloutHelpFlags |= hungerBlood ? CALLOUT_HELP_BLOOD_STARVING : CALLOUT_HELP_FOOD_STARVING; + } + else if ( eff.customVariable <= getEntityHungerInterval(player, nullptr, stats[player], HUNGER_INTERVAL_WEAK) ) + { + clientCalloutHelpFlags |= hungerBlood ? CALLOUT_HELP_BLOOD_WEAK : CALLOUT_HELP_FOOD_WEAK; + } + else if ( eff.customVariable <= getEntityHungerInterval(player, nullptr, stats[player], HUNGER_INTERVAL_HUNGRY) ) + { + clientCalloutHelpFlags |= hungerBlood ? CALLOUT_HELP_BLOOD_HUNGRY : CALLOUT_HELP_FOOD_HUNGRY; + } + } + else if ( eff.effect == StatusEffectQueue_t::kEffectAutomatonHunger ) + { + if ( eff.customVariable <= getEntityHungerInterval(player, nullptr, stats[player], HUNGER_INTERVAL_AUTOMATON_CRITICAL) ) + { + clientCalloutHelpFlags |= CALLOUT_HELP_STEAM_CRITICAL; + } + } + else if ( eff.effect >= 0 && eff.effect < NUMEFFECTS ) + { + if ( stats[player]->statusEffectRemovedByCureAilment(eff.effect, players[player]->entity) ) + { + clientCalloutHelpFlags |= CALLOUT_HELP_NEGATIVE_FX; + } + } + } + bool hpLow = stats[player]->HP < (3 * stats[player]->MAXHP / 5); + bool hpCritical = stats[player]->HP < (1 * stats[player]->MAXHP / 4); + clientCalloutHelpFlags |= hpLow ? CALLOUT_HELP_HP_LOW : 0; + clientCalloutHelpFlags |= hpCritical ? CALLOUT_HELP_HP_CRITICAL : 0; + } + + if ( clientCalloutHelpFlags & CALLOUT_HELP_BLOOD_STARVING ) + { + key = "condition_blood_starving"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_FOOD_STARVING ) + { + key = "condition_food_starving"; + } + else if ( (clientCalloutHelpFlags & CALLOUT_HELP_STEAM_CRITICAL) && (svFlags & SV_FLAG_HUNGER) ) + { + key = "condition_steam_empty"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_BLOOD_WEAK ) + { + key = "condition_blood_weak"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_FOOD_WEAK ) + { + key = "condition_food_weak"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_BLOOD_HUNGRY ) + { + key = "condition_blood_hungry"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_FOOD_HUNGRY ) + { + key = "condition_food_hungry"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_HP_CRITICAL ) + { + key = "condition_heal_urgent"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_HP_LOW ) + { + key = "condition_heal"; + } + else if ( (clientCalloutHelpFlags & CALLOUT_HELP_STEAM_CRITICAL) && !(svFlags & SV_FLAG_HUNGER) ) + { + key = "condition_steam_empty"; + } + /*else if ( clientCalloutHelpFlags & CALLOUT_HELP_NEGATIVE_FX ) + { + key = "condition_cure_ailment"; + }*/ + } break; case CALLOUT_TYPE_NPC: case CALLOUT_TYPE_NPC_ENEMY: @@ -22715,6 +22832,9 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName case CALLOUT_TYPE_FOUNTAIN: key = "fountain"; break; + case CALLOUT_TYPE_SINK: + key = "sink"; + break; case CALLOUT_TYPE_TELEPORTER_LADDER_UP: key = "teleporter"; if ( text_map.find(std::string(key + "_up")) != text_map.end() ) @@ -23063,7 +23183,7 @@ CalloutRadialMenu::CalloutType CalloutRadialMenu::getCalloutTypeForEntity(const } else if ( parent->behavior == &actSink ) { - type = CALLOUT_TYPE_GENERIC_INTERACTABLE; + type = CALLOUT_TYPE_SINK; } else if ( parent->behavior == &actChestLid || parent->behavior == &actChest ) { @@ -23253,9 +23373,17 @@ void CalloutRadialMenu::CalloutParticle_t::init(const int player) { creationTick = ::ticks; messageSentTick = ::ticks; + lifetime = kParticleLifetime; for ( int i = 0; i < MAXPLAYERS; ++i ) { - lockOnScreen[i] = true; + if ( i == player && entityUid == achievementObserver.playerUids[player] && players[i]->isLocalPlayer() ) + { + lockOnScreen[i] = true; + } + else + { + lockOnScreen[i] = true; + } big[i] = true; animateScaleForPlayerView[i] = 0.0; } @@ -23342,12 +23470,18 @@ void CalloutRadialMenu::drawCallouts(const int playernum) { return lhs.dist < rhs.dist; }; + auto compFunc2 = [](CalloutToDraw_t& lhs, CalloutToDraw_t& rhs) + { + return lhs.creationTick < rhs.creationTick; + }; std::priority_queue, decltype(compFunc)> priorityQueue(compFunc); + std::priority_queue, decltype(compFunc2)> priorityQueueSelf(compFunc2); for ( int i = 0; i < MAXPLAYERS; ++i ) { for ( auto& callout : CalloutMenu[i].callouts ) { + bool selfCallout = false; if ( i == playernum && callout.second.entityUid == achievementObserver.playerUids[playernum] ) { if ( players[i]->entity && players[i]->entity->skill[3] == 1 ) @@ -23356,7 +23490,8 @@ void CalloutRadialMenu::drawCallouts(const int playernum) } else { - continue; // don't draw self callouts + selfCallout = true; + //continue; // don't draw self callouts } } @@ -23407,6 +23542,11 @@ void CalloutRadialMenu::drawCallouts(const int playernum) mat4x4_t id; vec4_t world{ (float)callout.second.x * 2.f, -(float)callout.second.z * 2.f, (float)callout.second.y * 2.f, 1.f }; + if ( selfCallout ) + { + world.x = 32.0 * camera->x + 32.0 * cos(camera->ang); + world.z = 32.0 * camera->y + 32.0 * sin(camera->ang); + } vec4_t window2{ (float)0, (float)0, (float)player->camera_virtualWidth(), (float)player->camera_virtualHeight() }; SDL_Rect dest{ 0, 0, 0, 0 }; @@ -23482,7 +23622,12 @@ void CalloutRadialMenu::drawCallouts(const int playernum) 14, 22 }; } - real_t lifePercent = callout.second.ticks / (real_t)CalloutRadialMenu::CalloutParticle_t::kParticleLifetime; + real_t lifePercent = callout.second.ticks / (real_t)callout.second.lifetime; + if ( selfCallout ) + { + // fade early for the self callout player, but not others in splitscreen + lifePercent = callout.second.ticks / (real_t)((TICKS_PER_SECOND * 4) / 5); + } Uint32 alpha = 255; if ( lifePercent >= 0.8 ) { @@ -23527,6 +23672,15 @@ void CalloutRadialMenu::drawCallouts(const int playernum) iconPos.w = image->getWidth() * scale; iconPos.h = image->getHeight() * scale; const int heightOffset = image->getHeight() - iconPos.h; + + if ( selfCallout ) + { + real_t y = iconPos.y; + iconPos.y = players[playernum]->camera_virtualHeight() / 4; + real_t factor = players[playernum]->camera_virtualHeight() / (real_t)Frame::virtualScreenY; + iconPos.y += factor * 16.0 * (y - iconPos.y) / (real_t)players[playernum]->camera_virtualHeight(); + } + iconPos.x -= iconPos.w / 2; iconPos.y -= iconPos.h + heightOffset / 2; iconPos.y -= (iconPos.h / 4) * callout.second.animateBounce; @@ -23741,7 +23895,7 @@ void CalloutRadialMenu::update() for ( auto it = callouts.begin(); it != callouts.end(); ++it ) { it->second.ticks++; - if ( it->second.ticks >= CalloutParticle_t::kParticleLifetime ) + if ( it->second.ticks >= it->second.lifetime ) { it->second.expired = true; } @@ -23767,7 +23921,7 @@ bool CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu: if ( !entity ) { return false; } if ( _cmd == CALLOUT_CMD_CANCEL ) { return false; } - Uint32 exisitingMessageSent = 0; + Uint32 existingMessageSent = 0; for ( int i = 0; i < MAXPLAYERS; ++i ) { if ( CalloutMenu[i].callouts.find(entity->getUID()) != CalloutMenu[i].callouts.end() ) @@ -23775,7 +23929,7 @@ bool CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu: auto& existingCallout = CalloutMenu[i].callouts[entity->getUID()]; if ( i == getPlayer() && existingCallout.cmd == _cmd ) { - exisitingMessageSent = existingCallout.messageSentTick; + existingMessageSent = existingCallout.messageSentTick; } CalloutMenu[i].callouts.erase(entity->getUID()); // delete other players pings on this object } @@ -23783,12 +23937,12 @@ bool CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu: auto& callout = callouts[entity->getUID()]; callout = CalloutRadialMenu::CalloutParticle_t(getPlayer(), entity->x, entity->y, entity->z, entity->getUID(), _cmd); - if ( exisitingMessageSent > 0 && multiplayer != CLIENT ) + if ( existingMessageSent > 0 && multiplayer != CLIENT ) { - if ( (callout.messageSentTick - exisitingMessageSent) < (TICKS_PER_SECOND * 3.5) ) + if ( (callout.messageSentTick - existingMessageSent) < (TICKS_PER_SECOND * 3.5) ) { callout.doMessage = false; - callout.messageSentTick = exisitingMessageSent; + callout.messageSentTick = existingMessageSent; } } @@ -23813,7 +23967,8 @@ bool CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu: net_packet->data[4] = getPlayer(); SDLNet_Write32(entity->getUID(), &net_packet->data[5]); net_packet->data[9] = (Uint8)_cmd; - net_packet->len = 10; + SDLNet_Write32(clientCalloutHelpFlags, &net_packet->data[10]); + net_packet->len = 14; net_packet->address.host = net_clients[i - 1].host; net_packet->address.port = net_clients[i - 1].port; sendPacketSafe(net_sock, -1, net_packet, i - 1); @@ -23833,9 +23988,26 @@ bool CalloutRadialMenu::createParticleCallout(real_t x, real_t y, real_t z, Uint // CalloutMenu[i].callouts.erase(uid); // } //} - + Uint32 existingMessageSent = 0; + if ( _cmd == CALLOUT_CMD_MOVE && callouts.find(uid) != callouts.end() ) + { + auto& existingCallout = callouts[uid]; + if ( existingCallout.cmd == _cmd ) + { + existingMessageSent = existingCallout.messageSentTick; + } + } auto& callout = callouts[uid]; + callout = CalloutRadialMenu::CalloutParticle_t(getPlayer(), x, y, z, uid, _cmd); + if ( existingMessageSent > 0 && multiplayer != CLIENT ) + { + if ( (callout.messageSentTick - existingMessageSent) < (TICKS_PER_SECOND * 3.5) ) + { + callout.doMessage = false; + callout.messageSentTick = existingMessageSent; + } + } std::string calloutTypeKey = getCalloutKeyForCommand(_cmd); Uint32 oldTarget = lockOnEntityUid; @@ -23862,14 +24034,15 @@ bool CalloutRadialMenu::createParticleCallout(real_t x, real_t y, real_t z, Uint net_packet->data[4] = getPlayer(); SDLNet_Write32(uid, &net_packet->data[5]); net_packet->data[9] = (Uint8)_cmd; - net_packet->len = 10; + SDLNet_Write32(clientCalloutHelpFlags, &net_packet->data[10]); + net_packet->len = 14; if ( uid == 0 ) { Uint16 _x = std::min(std::max(0.0, x / 16), map.width - 1); Uint16 _y = std::min(std::max(0.0, y / 16), map.height - 1); - SDLNet_Write16(_x, &net_packet->data[10]); - SDLNet_Write16(_y, &net_packet->data[12]); - net_packet->len = 14; + SDLNet_Write16(_x, &net_packet->data[14]); + SDLNet_Write16(_y, &net_packet->data[16]); + net_packet->len = 18; } net_packet->address.host = net_clients[i - 1].host; net_packet->address.port = net_clients[i - 1].port; @@ -23892,14 +24065,15 @@ void CalloutRadialMenu::sendCalloutText(CalloutRadialMenu::CalloutCommand cmd) net_packet->data[4] = getPlayer(); SDLNet_Write32(lockOnEntityUid, &net_packet->data[5]); net_packet->data[9] = (Uint8)cmd; - net_packet->len = 10; + SDLNet_Write32(clientCalloutHelpFlags, &net_packet->data[10]); + net_packet->len = 14; if ( lockOnEntityUid == 0 ) { Uint16 _x = std::min(std::max(0.0, moveToX / 16), map.width - 1); Uint16 _y = std::min(std::max(0.0, moveToY / 16), map.height - 1); - SDLNet_Write16(_x, &net_packet->data[10]); - SDLNet_Write16(_y, &net_packet->data[12]); - net_packet->len = 14; + SDLNet_Write16(_x, &net_packet->data[14]); + SDLNet_Write16(_y, &net_packet->data[16]); + net_packet->len = 18; } net_packet->address.host = net_server.host; net_packet->address.port = net_server.port; @@ -24164,7 +24338,7 @@ void CalloutRadialMenu::drawCalloutMenu() if ( optionSelected != -1 ) { holdWheel = false; - if ( optionSelected != ALLY_CMD_ATTACK_CONFIRM && optionSelected != ALLY_CMD_MOVETO_CONFIRM ) + if ( optionSelected != CALLOUT_CMD_SELECT ) { if ( !sfxPlayed && optionSelected != CALLOUT_CMD_CANCEL ) { @@ -24202,8 +24376,7 @@ void CalloutRadialMenu::drawCalloutMenu() { lockOnEntityUid = achievementObserver.playerUids[gui_player]; } - else if ( (CalloutCommand)optionSelected == CALLOUT_CMD_HELP - || (CalloutCommand)optionSelected == CALLOUT_CMD_MOVE ) + else if ( (CalloutCommand)optionSelected == CALLOUT_CMD_HELP ) { lockOnEntityUid = achievementObserver.playerUids[gui_player]; } diff --git a/src/interface/interface.hpp b/src/interface/interface.hpp index a817aeae6..2232ede80 100644 --- a/src/interface/interface.hpp +++ b/src/interface/interface.hpp @@ -1468,6 +1468,7 @@ struct CalloutRadialMenu CALLOUT_TYPE_SECRET_ENTRANCE, CALLOUT_TYPE_GOLD, CALLOUT_TYPE_FOUNTAIN, + CALLOUT_TYPE_SINK, CALLOUT_TYPE_NPC_ENEMY, CALLOUT_TYPE_NPC_PLAYERALLY, CALLOUT_TYPE_TELEPORTER_LADDER_UP, @@ -1477,6 +1478,20 @@ struct CalloutRadialMenu CALLOUT_TYPE_COLLIDER_BREAKABLE /*,CALLOUT_TYPE_PEDESTAL*/ }; + enum CalloutHelpFlags : int + { + CALLOUT_HELP_FOOD_HUNGRY = 0b1, + CALLOUT_HELP_BLOOD_HUNGRY = 0b10, + CALLOUT_HELP_FOOD_WEAK = 0b100, + CALLOUT_HELP_BLOOD_WEAK = 0b1000, + CALLOUT_HELP_FOOD_STARVING = 0b10000, + CALLOUT_HELP_BLOOD_STARVING = 0b100000, + CALLOUT_HELP_STEAM_CRITICAL = 0b1000000, + CALLOUT_HELP_HP_LOW = 0b10000000, + CALLOUT_HELP_HP_CRITICAL = 0b100000000, + CALLOUT_HELP_NEGATIVE_FX = 0b1000000000, + }; + Uint32 clientCalloutHelpFlags = 0; struct CalloutParticle_t { real_t x = 0.0; @@ -1484,6 +1499,7 @@ struct CalloutRadialMenu real_t z = 0.0; Uint32 entityUid = 0; Uint32 ticks = 0; + Uint32 lifetime = 1; Uint32 creationTick = 0; CalloutCommand cmd = CALLOUT_CMD_END; CalloutType type = CALLOUT_TYPE_NO_TARGET; @@ -1499,6 +1515,8 @@ struct CalloutRadialMenu real_t animateScaleForPlayerView[MAXPLAYERS]; real_t animateBounce = 0.0; real_t animateY = 0.0; + bool noUpdate = false; + bool selfCallout = false; bool doMessage = true; Uint32 messageSentTick = 0; bool big[MAXPLAYERS]; diff --git a/src/net.cpp b/src/net.cpp index eaef7b028..9aadcd8b7 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -3583,6 +3583,7 @@ static std::unordered_map clientPacketHandlers = { } CalloutMenu[pnum].lockOnEntityUid = uid; CalloutRadialMenu::CalloutCommand cmd = (CalloutRadialMenu::CalloutCommand)net_packet->data[9]; + CalloutMenu[pnum].clientCalloutHelpFlags = SDLNet_Read32(&net_packet->data[10]); if ( uid ) { if ( entity ) @@ -3592,8 +3593,8 @@ static std::unordered_map clientPacketHandlers = { } else { - real_t x = SDLNet_Read16(&net_packet->data[10]); - real_t y = SDLNet_Read16(&net_packet->data[12]); + real_t x = SDLNet_Read16(&net_packet->data[14]); + real_t y = SDLNet_Read16(&net_packet->data[16]); CalloutMenu[pnum].createParticleCallout( x * 16.0 + 8.0, y * 16.0 + 8.0, -4, 0, cmd); } @@ -5254,11 +5255,11 @@ static std::unordered_map serverPacketHandlers = { } CalloutMenu[pnum].lockOnEntityUid = uid; CalloutRadialMenu::CalloutCommand cmd = (CalloutRadialMenu::CalloutCommand)net_packet->data[9]; - + CalloutMenu[pnum].clientCalloutHelpFlags = SDLNet_Read32(&net_packet->data[10]); if ( uid == 0 ) { - real_t x = SDLNet_Read16(&net_packet->data[10]); - real_t y = SDLNet_Read16(&net_packet->data[12]); + real_t x = SDLNet_Read16(&net_packet->data[14]); + real_t y = SDLNet_Read16(&net_packet->data[16]); if ( CalloutMenu[pnum].createParticleCallout(x * 16.0 + 8.0, y * 16.0 + 8.0, -4, 0, cmd) ) { CalloutMenu[pnum].sendCalloutText(cmd); From 9171300052f43719e9536ddc0e0e733a6c4f43a3 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 12 Sep 2023 09:59:09 +1000 Subject: [PATCH 018/146] * fix unarmed glove degrade chance on skillsheet --- src/ui/GameUI.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index fce40b37b..d4d32c40c 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -30484,6 +30484,7 @@ std::string formatSkillSheetEffects(int playernum, int proficiency, std::string& else if ( tag == "GLOVE_DEGRADE_CHANCE" ) { val = 100 + (stats[playernum]->type == GOBLIN ? 20 : 0); // chance to degrade on > 0 dmg + val += (static_cast(stats[playernum]->PROFICIENCIES[proficiency] / 20)) * 10; if ( svFlags & SV_FLAG_HARDCORE ) { val *= 2; @@ -30501,6 +30502,7 @@ std::string formatSkillSheetEffects(int playernum, int proficiency, std::string& else if ( tag == "GLOVE_DEGRADE0_CHANCE" ) { val = 8 + (stats[playernum]->type == GOBLIN ? 4 : 0); // chance to degrade on 0 dmg + val += static_cast(stats[playernum]->PROFICIENCIES[proficiency] / 20); if ( svFlags & SV_FLAG_HARDCORE ) { val *= 2; From 9767ff18df15de6a6406748b2f090bc914e8ed5f Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 16 Sep 2023 01:01:00 +1000 Subject: [PATCH 019/146] * more callout stuff --- src/actplayer.cpp | 17 +- src/game.cpp | 9 +- src/interface/drawminimap.cpp | 97 +++++ src/interface/interface.cpp | 777 ++++++++++++++++++++++++++++++---- src/interface/interface.hpp | 7 +- src/player.cpp | 10 + src/player.hpp | 1 + 7 files changed, 820 insertions(+), 98 deletions(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index eabc1d389..ad1ef7c0b 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -2579,6 +2579,11 @@ void actPlayer(Entity* my) if ( !intro ) { + if ( PLAYER_ALIVETIME == 0 ) + { + my->createWorldUITooltip(); + } + PLAYER_ALIVETIME++; if ( PLAYER_NUM == clientnum ) // specifically the host - in splitscreen we only process this once for all players. { @@ -4336,6 +4341,11 @@ void actPlayer(Entity* my) if ( true /*&& calloutMenu.allowedInteractEntity(*target)*/ ) { calloutMenu.lockOnEntityUid = target->getUID(); + if ( target->behavior == &actPlayer && target->skill[2] != PLAYER_NUM ) + { + target = my; + } + if ( calloutMenu.createParticleCallout(target) ) { calloutMenu.sendCalloutText(CalloutRadialMenu::CALLOUT_CMD_LOOK); @@ -4429,11 +4439,12 @@ void actPlayer(Entity* my) || showCalloutCommandsInputStr == input.binding("Interact Tooltip Prev")) ) { input.consumeBinaryToggle("Show Player Callouts"); - players[PLAYER_NUM]->hud.followerDisplay.bOpenFollowerMenuDisabled = true; + players[PLAYER_NUM]->hud.bOpenCalloutsMenuDisabled = true; } } - if ( (input.binaryToggle("Show Player Callouts") && !showCalloutCommandsOnGamepad) + if ( (input.binaryToggle("Show Player Callouts") && !showCalloutCommandsOnGamepad + && players[PLAYER_NUM]->shootmode) || (input.binaryToggle("Show Player Callouts") && showCalloutCommandsOnGamepad && players[PLAYER_NUM]->shootmode /*&& !players[PLAYER_NUM]->worldUI.bTooltipInView*/) ) { @@ -4544,7 +4555,7 @@ void actPlayer(Entity* my) calloutMenu.moveToX = previousx; calloutMenu.moveToY = previousy; calloutMenu.initCalloutMenuGUICursor(true); - Player::soundActivate(); + //Player::soundActivate(); } else { diff --git a/src/game.cpp b/src/game.cpp index 0484498ca..afd7d150f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1081,6 +1081,7 @@ void gameLogic(void) { players[c]->hud.followerDisplay.bCommandNPCDisabled = false; players[c]->hud.followerDisplay.bOpenFollowerMenuDisabled = false; + players[c]->hud.bOpenCalloutsMenuDisabled = false; if (c != clientnum && !splitscreen) { @@ -5336,7 +5337,12 @@ void ingameHud() } players[player]->hud.followerDisplay.bCycleNextDisabled = (worldUIBlocksFollowerCycle && players[player]->shootmode); bool allowCycle = true; - if ( FollowerMenu[player].followerMenuIsOpen() ) + if ( CalloutMenu[player].calloutMenuIsOpen() && !players[player]->shootmode ) + { + players[player]->hud.followerDisplay.bCycleNextDisabled = true; + allowCycle = false; + } + else if ( FollowerMenu[player].followerMenuIsOpen() ) { std::string cycleNPCbinding = input.binding("Cycle NPCs"); if ( cycleNPCbinding == input.binding("MenuCancel") ) @@ -7368,6 +7374,7 @@ int main(int argc, char** argv) } } Text::dumpCacheInMainLoop(); + achievementObserver.getCurrentPlayerUids(); DebugStats.t11End = std::chrono::high_resolution_clock::now(); // increase the cycle count diff --git a/src/interface/drawminimap.cpp b/src/interface/drawminimap.cpp index 0d84dfa70..ce450298b 100644 --- a/src/interface/drawminimap.cpp +++ b/src/interface/drawminimap.cpp @@ -728,6 +728,99 @@ void drawMinimap(const int player, SDL_Rect rect, bool drawingSharedMap) } } + bool blinkPlayers[MAXPLAYERS] = { false }; + { + struct CalloutPing_t + { + real_t x = 0.0; + real_t y = 0.0; + Uint32 creationTick = 0; + int player = 0; + int targetPlayer = -1; + Uint32 lifetime = 0; + Uint32 ticks = 0; + bool selfCallout = false; + CalloutPing_t(real_t _x, real_t _y, Uint32 _creationTick, + int _player, int _targetPlayer, + Uint32 _ticks, Uint32 _lifetime, bool _selfCallout) : + x(_x), + y(_y), + creationTick(_creationTick), + player(_player), + targetPlayer(_targetPlayer), + ticks(_ticks), + lifetime(_lifetime), + selfCallout(_selfCallout) + {}; + }; + auto compFunc = [](CalloutPing_t& lhs, CalloutPing_t& rhs) + { + return lhs.creationTick < rhs.creationTick; + }; + std::priority_queue, decltype(compFunc)> priorityQueue(compFunc); + for ( int i = 0; i < MAXPLAYERS; ++i ) + { + blinkPlayers[i] = false; + for ( auto& callout : CalloutMenu[i].callouts ) + { + bool selfCallout = false; + int targetPlayer = -1; + for ( int j = 0; j < MAXPLAYERS; ++j ) + { + if ( callout.second.entityUid == achievementObserver.playerUids[j] ) + { + selfCallout = true; + targetPlayer = j; + break; + } + } + priorityQueue.push(CalloutPing_t(callout.second.x / 16.0, callout.second.y / 16.0, + callout.second.creationTick, i, targetPlayer, + callout.second.ticks, + callout.second.kParticleLifetime, selfCallout)); + } + } + while ( !priorityQueue.empty() ) + { + auto& ping = priorityQueue.top(); + + Uint32 aliveTime = ::ticks - ping.creationTick; + if ( ping.targetPlayer >= 0 && ping.targetPlayer < MAXPLAYERS ) + { + blinkPlayers[ping.targetPlayer] = true; + } + if ( (aliveTime < (TICKS_PER_SECOND / 2) && (aliveTime % 10 < 5)) || aliveTime >= (TICKS_PER_SECOND / 2) ) + { + if ( ping.targetPlayer >= 0 && ping.targetPlayer < MAXPLAYERS ) + { + blinkPlayers[ping.targetPlayer] = false; + priorityQueue.pop(); + continue; + } + + // set color + Uint32 color = playerColor(ping.player, colorblind_lobby, false); + uint8_t r, g, b, a; + getColor(color, &r, &g, &b, &a); + + real_t lifePercent = ping.ticks / (real_t)ping.lifetime; + if ( ping.selfCallout && !drawingSharedMap ) + { + // fade early for the self callout player, but not others in splitscreen + lifePercent = ping.ticks / (real_t)((TICKS_PER_SECOND * 4) / 5); + } + Uint32 alpha = 255; + if ( lifePercent >= 0.8 ) + { + alpha -= std::min((Uint32)255, (Uint32)(255 * (lifePercent - 0.8) / 0.2)); + } + color = makeColor(r, g, b, alpha); + drawCircleMesh((real_t)ping.x + 0.5, (real_t)ping.y + 0.5, (real_t)1.0, rect, color); + } + priorityQueue.pop(); + } + } + // draw minotaur, players, and allies for ( int c = 0; c < 2; ++c ) { @@ -747,6 +840,10 @@ void drawMinimap(const int player, SDL_Rect rect, bool drawingSharedMap) else if ( entity->behavior == &actPlayer ) { foundplayer = entity->skill[2]; + if ( blinkPlayers[foundplayer] ) + { + continue; // skip this one since it's blinking from a callout + } } else if ( entity->behavior == &actMonster ) { diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index 73a5ccbd9..7a21207ba 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -21967,6 +21967,15 @@ void CalloutRadialMenu::loadCalloutJSON() } } } + if ( d.HasMember("help_strings") ) + { + CalloutRadialMenu::helpDescriptors.clear(); + for ( rapidjson::Value::ConstMemberIterator itr = d["help_strings"].MemberBegin(); + itr != d["help_strings"].MemberEnd(); ++itr ) + { + CalloutRadialMenu::helpDescriptors[itr->name.GetString()] = itr->value.GetString(); + } + } if ( d.HasMember("world_icons") ) { CalloutRadialMenu::worldIconEntries.clear(); @@ -22065,6 +22074,7 @@ void CalloutRadialMenu::loadCalloutJSON() std::string worldMsgSays = ""; std::string worldMsgEmote = ""; std::string worldMsgEmoteYou = ""; + std::string worldMsgEmoteToYou = ""; std::string worldIcon = ""; std::string worldIconMini = ""; if ( itr3->value.HasMember("msg") ) @@ -22083,6 +22093,10 @@ void CalloutRadialMenu::loadCalloutJSON() { worldMsgEmoteYou = itr3->value["msg_emote_you"].GetString(); } + if ( itr3->value.HasMember("msg_emote_to_you") ) + { + worldMsgEmoteToYou = itr3->value["msg_emote_to_you"].GetString(); + } if ( itr3->value.HasMember("world_icon") ) { worldIcon = itr3->value["world_icon"].GetString(); @@ -22099,6 +22113,7 @@ void CalloutRadialMenu::loadCalloutJSON() entry.worldMsgSays = worldMsgSays; entry.worldMsgEmote = worldMsgEmote; entry.worldMsgEmoteYou = worldMsgEmoteYou; + entry.worldMsgEmoteToYou = worldMsgEmoteToYou; entry.worldIconTag = worldIcon; entry.worldIconTagMini = worldIconMini; @@ -22254,15 +22269,29 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName std::string key = "default"; Entity* entity = uidToEntity(lockOnEntityUid); const int player = getPlayer(); - if ( lockOnEntityUid == 0 && players[player]->isLocalPlayer() ) + if ( players[player]->isLocalPlayer() ) { - if ( cmd == CALLOUT_CMD_AFFIRMATIVE || cmd == CALLOUT_CMD_NEGATIVE ) + if ( lockOnEntityUid == 0 ) { - entity = players[player]->entity; + if ( cmd == CALLOUT_CMD_AFFIRMATIVE || cmd == CALLOUT_CMD_NEGATIVE ) + { + entity = players[player]->entity; + } + else if ( cmd == CALLOUT_CMD_HELP ) + { + entity = players[player]->entity; + } } - else if ( cmd == CALLOUT_CMD_HELP ) + + if ( cmd == CALLOUT_CMD_SOUTH + || cmd == CALLOUT_CMD_SOUTHWEST + || cmd == CALLOUT_CMD_SOUTHEAST ) { - entity = players[player]->entity; + int toPlayer = getPlayerForDirectPlayerCmd(getPlayer(), cmd); + if ( toPlayer >= 0 && players[toPlayer] && players[toPlayer]->entity && !client_disconnected[toPlayer] ) + { + entity = players[toPlayer]->entity; + } } } @@ -22282,6 +22311,70 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName } return ""; } + else if ( cmd == CALLOUT_CMD_SOUTH + || cmd == CALLOUT_CMD_SOUTHWEST + || cmd == CALLOUT_CMD_SOUTHEAST ) + { + std::string targetPlayerName = ""; + + int toPlayer = getPlayerForDirectPlayerCmd(getPlayer(), cmd); + key = "player_wave"; + if ( toPlayer < 0 || client_disconnected[toPlayer] || (players[toPlayer] && !players[toPlayer]->entity) ) + { + key = "unavailable"; + } + + if ( setType == SET_CALLOUT_ICON_KEY ) + { + return key; + } + + if ( key != "unavailable" ) + { + char shortname[32]; + stringCopy(shortname, stats[toPlayer]->name, sizeof(shortname), 22); + targetPlayerName = shortname; + } + + auto& textMap = findIcon->second.text_map[key]; + auto highlights = textMap.bannerHighlights; + if ( highlights.size() > 0 ) + { + int indexStart = 0; + for ( auto highlight : highlights ) + { + indexStart = std::max(highlight, indexStart); + } + for ( auto c : targetPlayerName ) + { + if ( c == ' ' ) + { + highlights.insert(indexStart + 1); + ++indexStart; + } + } + } + if ( setType == SET_CALLOUT_BANNER_TEXT ) + { + setCalloutBannerTextFormatted(player, field, color, highlights, textMap.bannerText.c_str(), targetPlayerName.c_str()); + } + else + { + if ( entity && entity->skill[2] == targetPlayer ) + { + char shortname[32]; + stringCopy(shortname, stats[getPlayer()]->name, sizeof(shortname), 22); + char buf[128]; + snprintf(buf, sizeof(buf), textMap.worldMsgEmoteToYou.c_str(), shortname); + return buf; + } + else + { + return getCalloutMessage(textMap, targetPlayerName.c_str(), targetPlayer); + } + } + return ""; + } auto calloutType = getCalloutTypeForEntity(player, entity); auto& text_map = findIcon->second.text_map; @@ -22342,50 +22435,212 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName clientCalloutHelpFlags |= hpCritical ? CALLOUT_HELP_HP_CRITICAL : 0; } - if ( clientCalloutHelpFlags & CALLOUT_HELP_BLOOD_STARVING ) - { - key = "condition_blood_starving"; - } - else if ( clientCalloutHelpFlags & CALLOUT_HELP_FOOD_STARVING ) + if ( clientCalloutHelpFlags ) { - key = "condition_food_starving"; - } - else if ( (clientCalloutHelpFlags & CALLOUT_HELP_STEAM_CRITICAL) && (svFlags & SV_FLAG_HUNGER) ) - { - key = "condition_steam_empty"; - } - else if ( clientCalloutHelpFlags & CALLOUT_HELP_BLOOD_WEAK ) - { - key = "condition_blood_weak"; - } - else if ( clientCalloutHelpFlags & CALLOUT_HELP_FOOD_WEAK ) - { - key = "condition_food_weak"; + key = "help_all_conditions"; + if ( setType == SET_CALLOUT_ICON_KEY ) + { + if ( clientCalloutHelpFlags & CALLOUT_HELP_BLOOD_STARVING ) + { + key = "condition_blood_starving"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_FOOD_STARVING ) + { + key = "condition_food_starving"; + } + else if ( (clientCalloutHelpFlags & CALLOUT_HELP_STEAM_CRITICAL) && (svFlags & SV_FLAG_HUNGER) ) + { + key = "condition_steam_empty"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_BLOOD_WEAK ) + { + key = "condition_blood_weak"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_FOOD_WEAK ) + { + key = "condition_food_weak"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_BLOOD_HUNGRY ) + { + key = "condition_blood_hungry"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_FOOD_HUNGRY ) + { + key = "condition_food_hungry"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_HP_CRITICAL ) + { + key = "condition_heal_urgent"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_HP_LOW ) + { + key = "condition_heal"; + } + else if ( (clientCalloutHelpFlags & CALLOUT_HELP_STEAM_CRITICAL) && !(svFlags & SV_FLAG_HUNGER) ) + { + key = "condition_steam_empty"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_NEGATIVE_FX ) + { + key = "condition_cure_ailment"; + } + return key; + } + + auto& textMap = text_map[key]; + auto highlights = textMap.bannerHighlights; + + std::string helpText = ""; + + // hunger stats + { + if ( clientCalloutHelpFlags & CALLOUT_HELP_BLOOD_STARVING ) + { + helpText += helpDescriptors["starving_blood"]; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_FOOD_STARVING ) + { + helpText += helpDescriptors["starving"]; + } + else if ( (clientCalloutHelpFlags & CALLOUT_HELP_STEAM_CRITICAL) && (svFlags & SV_FLAG_HUNGER) ) + { + helpText += helpDescriptors["empty_steam"]; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_BLOOD_WEAK ) + { + helpText += helpDescriptors["very_hungry_blood"]; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_FOOD_WEAK ) + { + helpText += helpDescriptors["very_hungry"]; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_BLOOD_HUNGRY ) + { + helpText += helpDescriptors["hungry_blood"]; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_FOOD_HUNGRY ) + { + helpText += helpDescriptors["hungry"]; + } + else if ( (clientCalloutHelpFlags & CALLOUT_HELP_STEAM_CRITICAL) && !(svFlags & SV_FLAG_HUNGER) ) + { + helpText += helpDescriptors["empty_steam"]; + } + } + + // health stats + { + if ( clientCalloutHelpFlags & CALLOUT_HELP_HP_CRITICAL ) + { + if ( helpText.size() > 1 ) + { + helpText += helpDescriptors["separator"]; + } + helpText += helpDescriptors["healing_urgent"]; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_HP_LOW ) + { + if ( helpText.size() > 1 ) + { + helpText += helpDescriptors["separator"]; + } + helpText += helpDescriptors["healing"]; + } + } + + // effects + if ( clientCalloutHelpFlags & CALLOUT_HELP_NEGATIVE_FX ) + { + int numEffectsAdded = 0; + for ( int i = 0; i < NUMEFFECTS; ++i ) + { + if ( numEffectsAdded >= 3 ) + { + break; + } + if ( stats[player]->EFFECTS[i] + && (stats[player]->statusEffectRemovedByCureAilment(i, players[player]->entity) + || i == EFF_WITHDRAWAL && stats[player]->EFFECTS_TIMERS[EFF_WITHDRAWAL] == -2) ) + { + if ( StatusEffectQueue_t::StatusEffectDefinitions_t::allEffects[i].name == "" ) + { + continue; + } + if ( helpText.size() > 1 ) + { + helpText += helpDescriptors["separator"]; + } + helpText += StatusEffectQueue_t::StatusEffectDefinitions_t::allEffects[i].name; + ++numEffectsAdded; + } + } + } + + if ( setType == SET_CALLOUT_BANNER_TEXT ) + { + setCalloutBannerTextFormatted(player, field, color, highlights, textMap.bannerText.c_str(), helpText.c_str()); + } + else + { + return getCalloutMessage(textMap, helpText.c_str(), targetPlayer); + } + return ""; } - else if ( clientCalloutHelpFlags & CALLOUT_HELP_BLOOD_HUNGRY ) + } + else if ( cmd == CALLOUT_CMD_LOOK ) + { + std::string targetPlayerName = ""; + if ( entity && entity->behavior == &actPlayer ) { - key = "condition_blood_hungry"; + char shortname[32]; + stringCopy(shortname, stats[entity->skill[2]]->name, sizeof(shortname), 22); + targetPlayerName = shortname; } - else if ( clientCalloutHelpFlags & CALLOUT_HELP_FOOD_HUNGRY ) + + key = "player_wave"; + if ( setType == SET_CALLOUT_ICON_KEY ) { - key = "condition_food_hungry"; + return key; } - else if ( clientCalloutHelpFlags & CALLOUT_HELP_HP_CRITICAL ) + + auto& textMap = text_map[key]; + auto highlights = textMap.bannerHighlights; + if ( highlights.size() > 0 ) { - key = "condition_heal_urgent"; + int indexStart = 0; + for ( auto highlight : highlights ) + { + indexStart = std::max(highlight, indexStart); + } + for ( auto c : targetPlayerName ) + { + if ( c == ' ' ) + { + highlights.insert(indexStart + 1); + ++indexStart; + } + } } - else if ( clientCalloutHelpFlags & CALLOUT_HELP_HP_LOW ) + if ( setType == SET_CALLOUT_BANNER_TEXT ) { - key = "condition_heal"; + setCalloutBannerTextFormatted(player, field, color, highlights, textMap.bannerText.c_str(), targetPlayerName.c_str()); } - else if ( (clientCalloutHelpFlags & CALLOUT_HELP_STEAM_CRITICAL) && !(svFlags & SV_FLAG_HUNGER) ) + else { - key = "condition_steam_empty"; + if ( entity && entity->skill[2] == targetPlayer ) + { + char shortname[32]; + stringCopy(shortname, stats[getPlayer()]->name, sizeof(shortname), 22); + char buf[128]; + snprintf(buf, sizeof(buf), textMap.worldMsgEmoteToYou.c_str(), shortname); + return buf; + } + else + { + return getCalloutMessage(textMap, targetPlayerName.c_str(), targetPlayer); + } } - /*else if ( clientCalloutHelpFlags & CALLOUT_HELP_NEGATIVE_FX ) - { - key = "condition_cure_ailment"; - }*/ + return ""; } break; case CALLOUT_TYPE_NPC: @@ -23148,6 +23403,7 @@ bool CalloutRadialMenu::calloutMenuIsOpen() std::vector CalloutRadialMenu::panelEntries; std::map CalloutRadialMenu::iconEntries; std::map CalloutRadialMenu::worldIconEntries; +std::map CalloutRadialMenu::helpDescriptors; std::map CalloutRadialMenu::worldIconIDToEntryKey; int CalloutRadialMenu::followerWheelButtonThickness = 70; int CalloutRadialMenu::followerWheelRadius = 140; @@ -23487,9 +23743,9 @@ void CalloutRadialMenu::drawCallouts(const int playernum) for ( auto& callout : CalloutMenu[i].callouts ) { bool selfCallout = false; - if ( i == playernum && callout.second.entityUid == achievementObserver.playerUids[playernum] ) + if ( callout.second.entityUid == achievementObserver.playerUids[playernum] ) { - if ( players[i]->entity && players[i]->entity->skill[3] == 1 ) + if ( i == playernum && players[i]->entity && players[i]->entity->skill[3] == 1 ) { // debug/thirdperson cam. } @@ -23500,30 +23756,51 @@ void CalloutRadialMenu::drawCallouts(const int playernum) } } - auto& iconPaths = CalloutRadialMenu::worldIconEntries[CalloutRadialMenu::worldIconIDToEntryKey[callout.second.tagID]]; + auto* iconPaths = &CalloutRadialMenu::worldIconEntries[CalloutRadialMenu::worldIconIDToEntryKey[callout.second.tagID]]; auto& iconPathsMini = CalloutRadialMenu::worldIconEntries[CalloutRadialMenu::worldIconIDToEntryKey[callout.second.tagSmallID]]; + if ( selfCallout ) + { + std::string checkTag = CalloutRadialMenu::worldIconIDToEntryKey[callout.second.tagID] + "_display_self"; + if ( CalloutRadialMenu::worldIconEntries.find(checkTag) != CalloutRadialMenu::worldIconEntries.end() ) + { + iconPaths = &CalloutRadialMenu::worldIconEntries[checkTag]; + } + } + std::string iconPath = ""; std::string iconPathMini = ""; - switch ( i ) + int playerColor = i; + if ( callout.second.cmd == CALLOUT_CMD_SOUTH + || callout.second.cmd == CALLOUT_CMD_SOUTHWEST + || callout.second.cmd == CALLOUT_CMD_SOUTHEAST ) + { + playerColor = CalloutMenu[i].getPlayerForDirectPlayerCmd(i, callout.second.cmd); + if ( playernum == playerColor ) + { + iconPaths = &CalloutRadialMenu::worldIconEntries["tag_btn_player_wave_to_me"]; + } + } + + switch ( playerColor ) { case 0: - iconPath = iconPaths.pathPlayer1; + iconPath = iconPaths->pathPlayer1; iconPathMini = iconPathsMini.pathPlayer1; break; case 1: - iconPath = iconPaths.pathPlayer2; + iconPath = iconPaths->pathPlayer2; iconPathMini = iconPathsMini.pathPlayer2; break; case 2: - iconPath = iconPaths.pathPlayer3; + iconPath = iconPaths->pathPlayer3; iconPathMini = iconPathsMini.pathPlayer3; break; case 3: - iconPath = iconPaths.pathPlayer4; + iconPath = iconPaths->pathPlayer4; iconPathMini = iconPathsMini.pathPlayer4; break; default: - iconPath = iconPaths.pathPlayerX; + iconPath = iconPaths->pathPlayerX; iconPathMini = iconPathsMini.pathPlayerX; break; } @@ -23599,7 +23876,12 @@ void CalloutRadialMenu::drawCallouts(const int playernum) } //messagePlayer(player->playernum, MESSAGE_DEBUG, "%f", ((PI - abs(abs(tangent - camang) - PI)) * 2)); //messagePlayer(player->playernum, MESSAGE_DEBUG, "%f", result); - if ( result >= 0.0 && result < PI / 2 ) + if ( abs(result) < 0.0001 ) + { + // really small angle due to camera fluctuations, affix to left side to prevent flicker + dest.x = leftOfWindow; + } + else if ( result >= 0.0 && result < PI / 2 ) { dest.x = leftOfWindow; } @@ -23633,6 +23915,26 @@ void CalloutRadialMenu::drawCallouts(const int playernum) // fade early for the self callout player, but not others in splitscreen lifePercent = callout.second.ticks / (real_t)((TICKS_PER_SECOND * 4) / 5); } + else + { + for ( int i = 0; i < MAXPLAYERS; ++i ) + { + if ( callout.second.entityUid == achievementObserver.playerUids[i] ) + { + if ( callout.second.cmd == CALLOUT_CMD_AFFIRMATIVE + || callout.second.cmd == CALLOUT_CMD_NEGATIVE + || callout.second.cmd == CALLOUT_CMD_LOOK + || callout.second.cmd == CALLOUT_CMD_SOUTH + || callout.second.cmd == CALLOUT_CMD_SOUTHWEST + || callout.second.cmd == CALLOUT_CMD_SOUTHEAST ) + { + // fade early for simple thumbs up/down for players + lifePercent = callout.second.ticks / (real_t)((TICKS_PER_SECOND * 4) / 5); + } + break; + } + } + } Uint32 alpha = 255; if ( lifePercent >= 0.8 ) { @@ -23680,10 +23982,12 @@ void CalloutRadialMenu::drawCallouts(const int playernum) if ( selfCallout ) { - real_t y = iconPos.y; + real_t y = iconPos.y - players[playernum]->camera_virtualy1(); iconPos.y = players[playernum]->camera_virtualHeight() / 4; real_t factor = players[playernum]->camera_virtualHeight() / (real_t)Frame::virtualScreenY; - iconPos.y += factor * 16.0 * (y - iconPos.y) / (real_t)players[playernum]->camera_virtualHeight(); + iconPos.y += factor * 16.0 * (y - iconPos.y) + / (real_t)players[playernum]->camera_virtualHeight(); + iconPos.y += players[playernum]->camera_virtualy1(); } iconPos.x -= iconPos.w / 2; @@ -23921,6 +24225,11 @@ void CalloutRadialMenu::update() } } +int CalloutRadialMenu::CALLOUT_SFX_NEUTRAL = 612; +int CalloutRadialMenu::CALLOUT_SFX_NEGATIVE = 614; +int CalloutRadialMenu::CALLOUT_SFX_POSITIVE = 613; +static ConsoleVariable cvar_callout_sfx_vol("/callout_sfx_vol", 128); + bool CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu::CalloutCommand _cmd) { if ( !entity ) { return false; } @@ -23941,7 +24250,14 @@ bool CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu: } auto& callout = callouts[entity->getUID()]; - callout = CalloutRadialMenu::CalloutParticle_t(getPlayer(), entity->x, entity->y, entity->z, entity->getUID(), _cmd); + real_t x = entity->x; + real_t y = entity->y; + if ( TimerExperiments::bUseTimerInterpolation && entity->bUseRenderInterpolation ) + { + x = entity->lerpRenderState.x.position * 16.0; + y = entity->lerpRenderState.y.position * 16.0; + } + callout = CalloutRadialMenu::CalloutParticle_t(getPlayer(), x, y, entity->z, entity->getUID(), _cmd); if ( existingMessageSent > 0 && multiplayer != CLIENT ) { if ( (callout.messageSentTick - existingMessageSent) < (TICKS_PER_SECOND * 3.5) ) @@ -23951,6 +24267,31 @@ bool CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu: } } + for ( int i = 0; i < MAXPLAYERS; ++i ) + { + while ( CalloutMenu[i].callouts.size() > 3 ) + { + Uint32 earliestTick = ::ticks; + auto itToDelete = CalloutMenu[i].callouts.end(); + for ( auto it = CalloutMenu[i].callouts.begin(); it != CalloutMenu[i].callouts.end(); ++it ) + { + if ( it->second.creationTick < earliestTick ) + { + earliestTick = it->second.creationTick; + itToDelete = it; + } + } + if ( itToDelete == CalloutMenu[i].callouts.end() ) + { + break; + } + else + { + CalloutMenu[i].callouts.erase(itToDelete); + } + } + } + std::string calloutTypeKey = getCalloutKeyForCommand(_cmd); Uint32 oldTarget = lockOnEntityUid; lockOnEntityUid = entity->getUID(); @@ -23960,6 +24301,19 @@ bool CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu: callout.tagID = worldIconEntries[iconEntries[calloutTypeKey].text_map[key].worldIconTag].id; callout.tagSmallID = worldIconEntries[iconEntries[calloutTypeKey].text_map[key].worldIconTagMini].id; + if ( callout.cmd == CALLOUT_CMD_AFFIRMATIVE ) + { + playSound(CALLOUT_SFX_POSITIVE, *cvar_callout_sfx_vol); + } + else if ( callout.cmd == CALLOUT_CMD_NEGATIVE ) + { + playSound(CALLOUT_SFX_NEGATIVE, *cvar_callout_sfx_vol); + } + else + { + playSound(CALLOUT_SFX_NEUTRAL, *cvar_callout_sfx_vol); + } + if ( multiplayer == SERVER ) { for ( int i = 1; i < MAXPLAYERS; ++i ) @@ -24014,6 +24368,44 @@ bool CalloutRadialMenu::createParticleCallout(real_t x, real_t y, real_t z, Uint } } + if ( callout.cmd == CALLOUT_CMD_AFFIRMATIVE ) + { + playSound(CALLOUT_SFX_POSITIVE, *cvar_callout_sfx_vol); + } + else if ( callout.cmd == CALLOUT_CMD_NEGATIVE ) + { + playSound(CALLOUT_SFX_NEGATIVE, *cvar_callout_sfx_vol); + } + else + { + playSound(CALLOUT_SFX_NEUTRAL, *cvar_callout_sfx_vol); + } + + for ( int i = 0; i < MAXPLAYERS; ++i ) + { + while ( CalloutMenu[i].callouts.size() > 3 ) + { + Uint32 earliestTick = ::ticks; + auto itToDelete = CalloutMenu[i].callouts.end(); + for ( auto it = CalloutMenu[i].callouts.begin(); it != CalloutMenu[i].callouts.end(); ++it ) + { + if ( it->second.creationTick < earliestTick ) + { + earliestTick = it->second.creationTick; + itToDelete = it; + } + } + if ( itToDelete == CalloutMenu[i].callouts.end() ) + { + break; + } + else + { + CalloutMenu[i].callouts.erase(itToDelete); + } + } + } + std::string calloutTypeKey = getCalloutKeyForCommand(_cmd); Uint32 oldTarget = lockOnEntityUid; lockOnEntityUid = uid; @@ -24120,67 +24512,57 @@ std::string CalloutRadialMenu::getCalloutKeyForCommand(CalloutRadialMenu::Callou { return "move"; } - return ""; -} - -std::string CalloutRadialMenu::getIconPathForCommand(CalloutRadialMenu::CalloutCommand cmd, CalloutType type, bool highlight) -{ - if ( cmd == CALLOUT_CMD_LOOK ) + else if ( cmd == CALLOUT_CMD_SOUTH ) { - if ( highlight ) - { - return iconEntries["look_at"].path_hover; - } - else - { - return iconEntries["look_at"].path; - } + return "player_wave_1"; } - else if ( cmd == CALLOUT_CMD_HELP ) + else if ( cmd == CALLOUT_CMD_SOUTHWEST ) { - if ( highlight ) - { - return iconEntries["help"].path_hover; - } - else - { - return iconEntries["help"].path; - } + return "player_wave_2"; } - else if ( cmd == CALLOUT_CMD_AFFIRMATIVE ) + else if ( cmd == CALLOUT_CMD_SOUTHEAST ) { - if ( highlight ) + return "player_wave_3"; + } + return ""; +} + +int CalloutRadialMenu::getPlayerForDirectPlayerCmd(const int player, const CalloutRadialMenu::CalloutCommand cmd) +{ + if ( cmd == CALLOUT_CMD_SOUTH ) + { + if ( player == 0 ) { - return iconEntries["affirmative"].path_hover; + return 1; } else { - return iconEntries["affirmative"].path; + return 0; } } - else if ( cmd == CALLOUT_CMD_NEGATIVE ) + else if ( cmd == CALLOUT_CMD_SOUTHWEST ) { - if ( highlight ) + if ( player == 0 ) { - return iconEntries["negative"].path_hover; + return 2; } else { - return iconEntries["negative"].path; + return player == 1 ? 2 : 1; } } - else if ( cmd == CALLOUT_CMD_MOVE ) + else if ( cmd == CALLOUT_CMD_SOUTHEAST ) { - if ( highlight ) + if ( player == 0 || player == 1 ) { - return iconEntries["move"].path_hover; + return 3; } else { - return iconEntries["move"].path; + return player == 2 ? 3 : 2; } } - return ""; + return -1; } void CalloutRadialMenu::drawCalloutMenu() @@ -24282,6 +24664,25 @@ void CalloutRadialMenu::drawCalloutMenu() animInvalidAction = std::max(0.0, animInvalidAction); } + if ( optionSelected == CALLOUT_CMD_SOUTH + || optionSelected == CALLOUT_CMD_SOUTHEAST + || optionSelected == CALLOUT_CMD_SOUTHWEST ) + { + int targetPlayer = getPlayerForDirectPlayerCmd(getPlayer(), (CalloutCommand)optionSelected); + if ( targetPlayer < 0 || client_disconnected[targetPlayer] || (players[targetPlayer] && !players[targetPlayer]->entity) ) + { + disableOption = true; + } + else + { + disableOption = false; + } + } + else + { + disableOption = false; + } + bool menuConfirmOnGamepad = input.input("MenuConfirm").isBindingUsingGamepad(); bool menuLeftClickOnKeyboard = input.input("MenuLeftClick").isBindingUsingKeyboard() && !inputs.hasController(gui_player); @@ -24347,7 +24748,7 @@ void CalloutRadialMenu::drawCalloutMenu() { if ( !sfxPlayed && optionSelected != CALLOUT_CMD_CANCEL ) { - playSound(139, 64); // click + //playSound(139, 64); // click sfxPlayed = true; } } @@ -24387,10 +24788,46 @@ void CalloutRadialMenu::drawCalloutMenu() } } + if ( (CalloutCommand)optionSelected == CALLOUT_CMD_SOUTH + || (CalloutCommand)optionSelected == CALLOUT_CMD_SOUTHWEST + || (CalloutCommand)optionSelected == CALLOUT_CMD_SOUTHEAST ) + { + int toPlayer = getPlayerForDirectPlayerCmd(getPlayer(), (CalloutCommand)optionSelected); + if ( toPlayer >= 0 ) + { + lockOnEntityUid = achievementObserver.playerUids[toPlayer]; + } + } + if ( lockOnEntityUid ) { if ( Entity* target = uidToEntity(lockOnEntityUid) ) { + if ( target->behavior == &actPlayer && target->skill[2] != getPlayer() ) + { + if ( (CalloutCommand)optionSelected == CALLOUT_CMD_HELP ) + { + lockOnEntityUid = achievementObserver.playerUids[getPlayer()]; + target = uidToEntity(lockOnEntityUid); + } + else if ( (CalloutCommand)optionSelected == CALLOUT_CMD_LOOK + || (CalloutCommand)optionSelected == CALLOUT_CMD_AFFIRMATIVE + || (CalloutCommand)optionSelected == CALLOUT_CMD_NEGATIVE ) + { + target = players[getPlayer()]->entity; + } + else if ( (CalloutCommand)optionSelected == CALLOUT_CMD_SOUTH + || (CalloutCommand)optionSelected == CALLOUT_CMD_SOUTHWEST + || (CalloutCommand)optionSelected == CALLOUT_CMD_SOUTHEAST ) + { + int toPlayer = getPlayerForDirectPlayerCmd(getPlayer(), (CalloutCommand)optionSelected); + if ( toPlayer >= 0 ) + { + target = players[getPlayer()]->entity; + } + } + } + if ( createParticleCallout(target, (CalloutCommand)optionSelected) ) { sendCalloutText((CalloutCommand)optionSelected); @@ -24414,6 +24851,8 @@ void CalloutRadialMenu::drawCalloutMenu() if ( !keepWheelOpen ) { closeCalloutMenuGUI(); + players[gui_player]->closeAllGUIs(CLOSEGUI_ENABLE_SHOOTMODE, CLOSEGUI_CLOSE_ALL); + return; } optionSelected = -1; } @@ -24697,15 +25136,144 @@ void CalloutRadialMenu::drawCalloutMenu() panelIcons[i]->path = iconEntries["move"].path; } } + else if ( i == CALLOUT_CMD_SOUTH ) + { + int targetPlayer = getPlayerForDirectPlayerCmd(getPlayer(), (CalloutCommand)i); + if ( targetPlayer < 0 || client_disconnected[targetPlayer] || (players[targetPlayer] && !players[targetPlayer]->entity) ) + { + lockedOption = true; + } + if ( i == highlight ) + { + setCalloutText(bannerTxt, "player_wave_1", textHighlightColor, (CalloutCommand)i, SET_CALLOUT_BANNER_TEXT, -1); + } + + std::string key = (i == highlight) ? "tag_btn_player_wave_hover" : "tag_btn_player_wave"; + if ( lockedOption ) + { + panelIcons[i]->path = worldIconEntries[key].pathDefault; + } + else + { + switch ( targetPlayer ) + { + case 0: + panelIcons[i]->path = worldIconEntries[key].pathPlayer1; + break; + case 1: + panelIcons[i]->path = worldIconEntries[key].pathPlayer2; + break; + case 2: + panelIcons[i]->path = worldIconEntries[key].pathPlayer3; + break; + case 3: + panelIcons[i]->path = worldIconEntries[key].pathPlayer4; + break; + default: + panelIcons[i]->path = worldIconEntries[key].pathPlayerX; + break; + } + } + } + else if ( i == CALLOUT_CMD_SOUTHWEST ) + { + int targetPlayer = getPlayerForDirectPlayerCmd(getPlayer(), (CalloutCommand)i); + if ( targetPlayer < 0 || client_disconnected[targetPlayer] || (players[targetPlayer] && !players[targetPlayer]->entity) ) + { + lockedOption = true; + } + if ( i == highlight ) + { + setCalloutText(bannerTxt, "player_wave_2", textHighlightColor, (CalloutCommand)i, SET_CALLOUT_BANNER_TEXT, -1); + } + + std::string key = (i == highlight) ? "tag_btn_player_wave_hover" : "tag_btn_player_wave"; + if ( lockedOption ) + { + panelIcons[i]->path = worldIconEntries[key].pathDefault; + } + else + { + switch ( targetPlayer ) + { + case 0: + panelIcons[i]->path = worldIconEntries[key].pathPlayer1; + break; + case 1: + panelIcons[i]->path = worldIconEntries[key].pathPlayer2; + break; + case 2: + panelIcons[i]->path = worldIconEntries[key].pathPlayer3; + break; + case 3: + panelIcons[i]->path = worldIconEntries[key].pathPlayer4; + break; + default: + panelIcons[i]->path = worldIconEntries[key].pathPlayerX; + break; + } + } + } + else if ( i == CALLOUT_CMD_SOUTHEAST ) + { + int targetPlayer = getPlayerForDirectPlayerCmd(getPlayer(), (CalloutCommand)i); + if ( targetPlayer < 0 || client_disconnected[targetPlayer] || (players[targetPlayer] && !players[targetPlayer]->entity) ) + { + lockedOption = true; + } + if ( i == highlight ) + { + setCalloutText(bannerTxt, "player_wave_3", textHighlightColor, (CalloutCommand)i, SET_CALLOUT_BANNER_TEXT, -1); + } + + std::string key = (i == highlight) ? "tag_btn_player_wave_hover" : "tag_btn_player_wave"; + if ( lockedOption ) + { + panelIcons[i]->path = worldIconEntries[key].pathDefault; + } + else + { + switch ( targetPlayer ) + { + case 0: + panelIcons[i]->path = worldIconEntries[key].pathPlayer1; + break; + case 1: + panelIcons[i]->path = worldIconEntries[key].pathPlayer2; + break; + case 2: + panelIcons[i]->path = worldIconEntries[key].pathPlayer3; + break; + case 3: + panelIcons[i]->path = worldIconEntries[key].pathPlayer4; + break; + default: + panelIcons[i]->path = worldIconEntries[key].pathPlayerX; + break; + } + } + } } } - if ( highlight == i && !mouseInCenterButton ) + if ( lockedOption ) + { + if ( highlight == i && !mouseInCenterButton ) + { + panelImages[i]->path = getPanelEntriesForCallout()[i].path_hover; + } + else + { + panelImages[i]->path = getPanelEntriesForCallout()[i].path; + } + } + else if ( highlight == i && !mouseInCenterButton ) { panelImages[i]->path = getPanelEntriesForCallout()[i].path_hover; } - if ( !lockedOption && panelIcons[i]->path != "" ) + + if ( /*!lockedOption &&*/ panelIcons[i]->path != "" ) { if ( auto imgGet = Image::get(panelIcons[i]->path.c_str()) ) { @@ -24737,13 +25305,23 @@ void CalloutRadialMenu::drawCalloutMenu() if ( optionSelected == -1 && disableOption == 0 && highlight != -1 ) { // in case optionSelected is cleared, but we're still highlighting text (happens on next frame when clicking on disabled option.) - if ( highlight == CALLOUT_CMD_SELECT ) + if ( highlight == CALLOUT_CMD_SOUTH + || highlight == CALLOUT_CMD_SOUTHEAST + || highlight == CALLOUT_CMD_SOUTHWEST ) { - disableOption = false;// optionDisabledForCreature(skillLVL, followerStats->type, highlight, followerToCommand); + int targetPlayer = getPlayerForDirectPlayerCmd(getPlayer(), (CalloutCommand)highlight); + if ( targetPlayer < 0 || client_disconnected[targetPlayer] || (players[targetPlayer] && !players[targetPlayer]->entity) ) + { + disableOption = true; + } + else + { + disableOption = false; + } } else { - disableOption = false;// optionDisabledForCreature(skillLVL, followerStats->type, highlight, followerToCommand); + disableOption = false; } } @@ -25299,6 +25877,19 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat strcat(interactText, getMonsterLocalizedName((Monster)monsterType).c_str()); } } + else if ( selectedEntity.behavior == &actPlayer ) + { + if ( updateInteractText ) + { + int playernum = selectedEntity.skill[2]; + if ( playernum >= 0 && playernum < MAXPLAYERS ) + { + char shortname[32]; + stringCopy(shortname, stats[playernum]->name, sizeof(shortname), 22); + strcat(interactText, shortname); + } + } + } else if ( selectedEntity.behavior == &actColliderDecoration && selectedEntity.isDamageableCollider() ) { if ( updateInteractText ) @@ -25324,7 +25915,7 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat { if ( updateInteractText ) { - strcpy(interactText, Language::get(4047)); // "No interactions available" + strcpy(interactText, ""); } return false; } diff --git a/src/interface/interface.hpp b/src/interface/interface.hpp index 2232ede80..e26998c3c 100644 --- a/src/interface/interface.hpp +++ b/src/interface/interface.hpp @@ -1410,6 +1410,7 @@ struct CalloutRadialMenu std::string worldMsg = ""; std::string worldMsgEmote = ""; std::string worldMsgEmoteYou = ""; + std::string worldMsgEmoteToYou = ""; std::string worldIconTag = ""; std::string worldIconTagMini = ""; }; @@ -1427,6 +1428,7 @@ struct CalloutRadialMenu int id = 0; }; static std::map worldIconEntries; + static std::map helpDescriptors; static std::map worldIconIDToEntryKey; static int followerWheelRadius; static int followerWheelButtonThickness; @@ -1434,6 +1436,9 @@ struct CalloutRadialMenu static int followerWheelFrameOffsetY; static int followerWheelInnerCircleRadiusOffset; static int followerWheelInnerCircleRadiusOffsetAlternate; + static int CALLOUT_SFX_NEUTRAL; + static int CALLOUT_SFX_NEGATIVE; + static int CALLOUT_SFX_POSITIVE; enum CalloutCommand : int { @@ -1449,6 +1454,7 @@ struct CalloutRadialMenu CALLOUT_CMD_SELECT, CALLOUT_CMD_END }; + int getPlayerForDirectPlayerCmd(const int player, const CalloutCommand cmd); enum CalloutType : int { CALLOUT_TYPE_NO_TARGET, @@ -1550,7 +1556,6 @@ struct CalloutRadialMenu bool allowedInteractEntity(Entity& selectedEntity, bool updateInteractText = true); bool createParticleCallout(real_t x, real_t y, real_t z, Uint32 uid, CalloutCommand _cmd = CALLOUT_CMD_LOOK); // if true, send message bool createParticleCallout(Entity* entity, CalloutCommand _cmd = CALLOUT_CMD_LOOK); // if true, send message - static std::string getIconPathForCommand(CalloutCommand cmd, CalloutType type, bool highlight); enum SetCalloutTextTypes : int { SET_CALLOUT_BANNER_TEXT, SET_CALLOUT_WORLD_TEXT, diff --git a/src/player.cpp b/src/player.cpp index 6a27bec27..0cb92bcfb 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -3258,6 +3258,12 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) } } + if ( parent->behavior == &actPlayer && player.playernum == parent->skill[2] ) + { + // can't select self + return 0.0; + } + Entity* ohitentity = hit.entity; real_t tangent2 = atan2(players[player.playernum]->entity->y - parent->y, players[player.playernum]->entity->x - parent->x); lineTraceTarget(parent, parent->x, parent->y, tangent2, maxDist, 0, false, players[player.playernum]->entity); @@ -3273,6 +3279,10 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) { return 0.0; } + else if ( parent->behavior == &actPlayer ) + { + return 0.0; + } if ( !selectInteract && stats[player.playernum] && stats[player.playernum]->defending ) { diff --git a/src/player.hpp b/src/player.hpp index 4b1a81d99..f4953c474 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -1620,6 +1620,7 @@ class Player } bool bShowActionPrompts = true; bool bShortHPMPForActionBars = false; + bool bOpenCalloutsMenuDisabled = false; enum ActionPrompts : int { ACTION_PROMPT_MAINHAND, From 4ab9a7169c1ad80abaa918f7a852054c51f07a8c Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 16 Sep 2023 01:02:18 +1000 Subject: [PATCH 020/146] * fix hotkey binding strings for callout binding --- lang/en.txt | 1 + src/ui/MainMenu.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/lang/en.txt b/lang/en.txt index 285ba69aa..5632cdad1 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -7723,5 +7723,6 @@ Upload# 6019 Populate Hotbar# 6020 Preload Music Files# 6021 Preloading improves playback performance, but increases system memory usage. (Applied next game launch)# +6022 Show Player Callouts# 6100 end# diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 1d0022cb1..80b825be7 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -4493,10 +4493,19 @@ namespace MainMenu { static const char* translateBinding(const char* binding) { int c = 5970; + if ( !strcmp(binding, "Show Player Callouts") ) + { + return Language::get(6022); + } + for (auto& b : defaultBindings[0].bindings) { if (b.action == binding) { break; } + if ( b.action == "Show Player Callouts" ) + { + continue; // don't increment c, not in the linear language entries + } ++c; } return Language::get(c); From f986e5769bfbc8835fe3169e9a392dc579e4d130 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 16 Sep 2023 02:27:01 +1000 Subject: [PATCH 021/146] * callout/chat messages are linked to notification sound group * disable dithering of player nametags, visible through walls now * disabled player specific callouts make low transparency * item tooltips no longer show when callout menu open --- src/actplayer.cpp | 1 + src/draw.cpp | 4 ++- src/game.cpp | 4 +-- src/interface/interface.cpp | 50 ++++++++++++++++++++----------------- src/menu.cpp | 2 +- src/messages.hpp | 2 ++ src/net.cpp | 4 +-- src/opengl.cpp | 4 +++ src/ui/GameUI.cpp | 14 +++++++++++ src/ui/MainMenu.cpp | 2 +- 10 files changed, 57 insertions(+), 30 deletions(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index ad1ef7c0b..e26d403aa 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -2193,6 +2193,7 @@ void actPlayer(Entity* my) nametag->scalex = 0.2; nametag->scaley = 0.2; nametag->scalez = 0.2; + nametag->ditheringDisabled = true; nametag->skill[0] = PLAYER_NUM; nametag->skill[1] = playerColor(PLAYER_NUM, colorblind_lobby, false); diff --git a/src/draw.cpp b/src/draw.cpp index eb7873753..30a98fe12 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -1953,7 +1953,9 @@ void drawEntities3D(view_t* camera, int mode) const int y = entity->y / 16; if (x >= 0 && y >= 0 && x < map.width && y < map.height) { - if ( !camera->vismap[y + x * map.height] && entity->monsterEntityRenderAsTelepath != 1 ) + if ( !camera->vismap[y + x * map.height] + && entity->monsterEntityRenderAsTelepath != 1 + && !(entity->behavior == &actSpriteNametag && entity->ditheringDisabled) ) { decrease = true; goto end; diff --git a/src/game.cpp b/src/game.cpp index afd7d150f..7eda87992 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -6201,7 +6201,7 @@ static void doConsoleCommands() { strcat(chatstring, command_str); Uint32 color = playerColor(commandPlayer, colorblind_lobby, false); if (messagePlayerColor(commandPlayer, MESSAGE_CHAT, color, chatstring)) { - playSound(238, 64); + playSound(Message::CHAT_MESSAGE_SFX, 64); } // send message to server @@ -6228,7 +6228,7 @@ static void doConsoleCommands() { strcat(chatstring, command_str); Uint32 color = playerColor(commandPlayer, colorblind_lobby, false); if (messagePlayerColor(commandPlayer, MESSAGE_CHAT, color, chatstring)) { - playSound(238, 64); + playSound(Message::CHAT_MESSAGE_SFX, 64); } if (multiplayer == SERVER) { diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index 7a21207ba..1a8c2dee4 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -22356,6 +22356,10 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName } if ( setType == SET_CALLOUT_BANNER_TEXT ) { + if ( key == "unavailable" ) + { + color = hudColors.characterSheetRed; + } setCalloutBannerTextFormatted(player, field, color, highlights, textMap.bannerText.c_str(), targetPlayerName.c_str()); } else @@ -25049,6 +25053,7 @@ void CalloutRadialMenu::drawCalloutMenu() // draw the text for the menu wheel. bool lockedOption = false; + panelIcons[i]->color = makeColor(255, 255, 255, 255); { /*if ( i == ALLY_CMD_ATTACK_SELECT ) { @@ -25151,9 +25156,9 @@ void CalloutRadialMenu::drawCalloutMenu() std::string key = (i == highlight) ? "tag_btn_player_wave_hover" : "tag_btn_player_wave"; if ( lockedOption ) { - panelIcons[i]->path = worldIconEntries[key].pathDefault; + panelIcons[i]->color = makeColor(255, 255, 255, 64); } - else + { switch ( targetPlayer ) { @@ -25190,9 +25195,9 @@ void CalloutRadialMenu::drawCalloutMenu() std::string key = (i == highlight) ? "tag_btn_player_wave_hover" : "tag_btn_player_wave"; if ( lockedOption ) { - panelIcons[i]->path = worldIconEntries[key].pathDefault; + panelIcons[i]->color = makeColor(255, 255, 255, 64); } - else + { switch ( targetPlayer ) { @@ -25229,9 +25234,9 @@ void CalloutRadialMenu::drawCalloutMenu() std::string key = (i == highlight) ? "tag_btn_player_wave_hover" : "tag_btn_player_wave"; if ( lockedOption ) { - panelIcons[i]->path = worldIconEntries[key].pathDefault; + panelIcons[i]->color = makeColor(255, 255, 255, 64); } - else + { switch ( targetPlayer ) { @@ -25332,10 +25337,10 @@ void CalloutRadialMenu::drawCalloutMenu() bool disableActionGlyph = false; bool missingSkillLevel = false; - //if ( disableOption != 0 ) - //{ - // disableActionGlyph = true; - + if ( disableOption != 0 ) + { + disableActionGlyph = true; + } // if ( disableOption == -2 ) // disabled due to cooldown // { // setCalloutBannerText(gui_player, bannerTxt, "invalid_action", "rest_cooldown", hudColors.characterSheetRed); @@ -25548,30 +25553,29 @@ void CalloutRadialMenu::drawCalloutMenu() bannerFrame->setSize(bannerSize); auto wheelTitleText = bgFrame->findField("wheel title"); - Stat* playerStats = stats[gui_player]; - if ( playerStats ) + if ( !strcmp(wheelTitleText->getText(), "") ) { char buf[128] = ""; int spaces = 0; int spaces2 = 0; - for ( int c = 0; c <= strlen(Language::get(4200)); ++c ) + + if ( Entity* target = uidToEntity(lockOnEntityUid) ) { - if ( Language::get(4200)[c] == '\0' ) + bool allowed = allowedInteractEntity(*target, true); + if ( allowed ) { - break; + snprintf(buf, sizeof(buf), "%s...", interactText); } - if ( Language::get(4200)[c] == ' ' ) + else { - ++spaces; + snprintf(buf, sizeof(buf), Language::get(4348)); } - } - if ( strcmp(playerStats->name, "") && strcmp(playerStats->name, "nothing") ) - { - snprintf(buf, sizeof(buf), Language::get(4200), playerStats->name); + spaces = 1; } else { - snprintf(buf, sizeof(buf), Language::get(4200), getMonsterLocalizedName(playerStats->type).c_str()); + snprintf(buf, sizeof(buf), Language::get(4348)); + spaces = 1; } for ( int c = 0; c <= strlen(buf); ++c ) @@ -25587,7 +25591,7 @@ void CalloutRadialMenu::drawCalloutMenu() } wheelTitleText->setText(buf); wheelTitleText->clearWordsToHighlight(); - int wordIndex = 1; + int wordIndex = 2; while ( spaces2 >= spaces ) // every additional space means +1 word to highlight for the monster's name { wheelTitleText->addWordToHighlight(wordIndex, followerTitleHighlightColor); diff --git a/src/menu.cpp b/src/menu.cpp index d222b6b0b..2e6ed79b2 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -5996,7 +5996,7 @@ static void handleMainMenu(bool mode) keystatus[SDLK_RETURN] = 0; if ( multiplayer != CLIENT ) { - playSound(238, 64); + playSound(Message::CHAT_MESSAGE_SFX, 64); } char shortname[32] = {0}; diff --git a/src/messages.hpp b/src/messages.hpp index 2652abf55..9b6af5973 100644 --- a/src/messages.hpp +++ b/src/messages.hpp @@ -45,6 +45,8 @@ typedef struct Message * To ensure everything always works right. I guess. Maybe not necessary. Whatever. There are much bigger problems to worry about. */ Sint16 alpha; + + static const int CHAT_MESSAGE_SFX = 238; } Message; /* diff --git a/src/net.cpp b/src/net.cpp index 545847816..d6b62dd83 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -3612,7 +3612,7 @@ static std::unordered_map clientPacketHandlers = { const bool printed = messagePlayerColor(clientnum, type, color, "%s", msg); if (type == MESSAGE_CHAT && printed) { - playSound(238, 64); + playSound(Message::CHAT_MESSAGE_SFX, 64); } } @@ -5288,7 +5288,7 @@ static std::unordered_map serverPacketHandlers = { const int len = snprintf(fmt, sizeof(fmt), "%s: %s", shortname, (char*)(&net_packet->data[9])); messagePlayerColor(clientnum, type, color, fmt); - playSound(238, 64); + playSound(Message::CHAT_MESSAGE_SFX, 64); // relay message to all clients for ( int c = 1; c < MAXPLAYERS; c++ ) diff --git a/src/opengl.cpp b/src/opengl.cpp index cf179aa41..259f71f65 100644 --- a/src/opengl.cpp +++ b/src/opengl.cpp @@ -1498,6 +1498,10 @@ void glDrawWorldUISprite(view_t* camera, Entity* entity, int mode) } } if (player >= 0 && player < MAXPLAYERS) { + if ( CalloutMenu[player].calloutMenuIsOpen() ) + { + return; + } if (entity->worldTooltipPlayer != player) { return; } diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index d4d32c40c..35edee635 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -36610,10 +36610,24 @@ bool SkillUpAnimation_t::soundIndexUsedForNotification(const int index) { return true; } + else if ( index == CalloutRadialMenu::CALLOUT_SFX_NEGATIVE + || index == CalloutRadialMenu::CALLOUT_SFX_NEUTRAL + || index == CalloutRadialMenu::CALLOUT_SFX_POSITIVE ) + { + return true; + } + else if ( index == Message::CHAT_MESSAGE_SFX ) + { + return true; + } else if ( index == *cvar_lvl_ding_sfx ) { return true; } + else if ( index == *cvar_skill_newspell_sfx ) + { + return true; + } else { for ( auto skill : Player::SkillSheet_t::skillSheetData.skillEntries ) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 80b825be7..3685b456a 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -8417,7 +8417,7 @@ namespace MainMenu { const Uint32 seconds = getTime() % seconds_in_day; if (add_to_list) { - playSound(238, 64); + playSound(Message::CHAT_MESSAGE_SFX, 64); new_lobby_chat_message_alert = ticks; lobby_chat_messages.emplace_back(LobbyChatMessage{seconds, color, msg}); if (lobby_chat_messages.size() > lobby_chat_max_messages) { From 9ba379fcca510e0eee4c24dea9f6a88619e0436b Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 16 Sep 2023 02:28:08 +1000 Subject: [PATCH 022/146] * update lang file for notification sound group setting --- lang/en.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/en.txt b/lang/en.txt index 5632cdad1..f3de25bc4 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -6674,7 +6674,7 @@ The next input you activate will be bound to this action.# 5192 Environment Volume# 5193 Adjust the volume of flowing water and lava.# 5194 Notification Volume# -5195 Adjust the volume of skill increase and level up notifications.# +5195 Adjust the volume of callouts, chat messages, skill increases and level up notifications.# 5196 Music Volume# 5197 Adjust the volume of the game's soundtrack.# 5198 Options# From 2284ec1fe4b54b8a9f4565a663c37df1c6f6a109 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 16 Sep 2023 02:38:42 +1000 Subject: [PATCH 023/146] * re-enable damage gib coloring --- src/interface/interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index 1a8c2dee4..0e8812338 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -155,7 +155,7 @@ FollowerRadialMenu FollowerMenu[MAXPLAYERS]; CalloutRadialMenu CalloutMenu[MAXPLAYERS]; GenericGUIMenu GenericGUI[MAXPLAYERS]; -bool EnemyHPDamageBarHandler::bDamageGibTypesEnabled = false; +bool EnemyHPDamageBarHandler::bDamageGibTypesEnabled = true; std::map> EnemyHPDamageBarHandler::damageGibAnimCurves; int EnemyHPDamageBarHandler::maxTickLifetime = 120; int EnemyHPDamageBarHandler::maxTickFurnitureLifetime = 60; From 5396b282b5c5b99781e4142b734ea94ef7762741 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 16 Sep 2023 22:00:03 +1000 Subject: [PATCH 024/146] * final boss sfx dont play multiple in splitscreen --- src/actmonster.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/actmonster.cpp b/src/actmonster.cpp index 71a4e0bc1..ae7fdb2fb 100644 --- a/src/actmonster.cpp +++ b/src/actmonster.cpp @@ -6918,12 +6918,32 @@ void actMonster(Entity* my) { if ( myStats->type == LICH_FIRE ) { - playSoundPlayer(c, 376, 128); + if ( multiplayer == SINGLE ) + { + if ( c == clientnum ) + { + playSoundPlayer(c, 376, 128); // only play sfx once in splitscreen + } + } + else + { + playSoundPlayer(c, 376, 128); + } messagePlayerColor(c, MESSAGE_WORLD, uint32ColorOrange, Language::get(2646)); } else if ( myStats->type == LICH_ICE ) { - playSoundPlayer(c, 381, 128); + if ( multiplayer == SINGLE ) + { + if ( c == clientnum ) + { + playSoundPlayer(c, 381, 128); // only play sfx once in splitscreen + } + } + else + { + playSoundPlayer(c, 381, 128); + } messagePlayerColor(c, MESSAGE_WORLD, uint32ColorBaronyBlue, Language::get(2648)); } } From 3d7a4254314693ebcc188f5caabcd7e195840238 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 16 Sep 2023 22:00:32 +1000 Subject: [PATCH 025/146] * end-game achievements unlocked for every player class involved in splitscreen, not just player 1 --- src/menu.cpp | 144 +++++++++++++++++++++++++++------------------------ 1 file changed, 75 insertions(+), 69 deletions(-) diff --git a/src/menu.cpp b/src/menu.cpp index 2e6ed79b2..5ee0af8e9 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -10492,80 +10492,86 @@ void doEndgame(bool saveHighscore) { steamAchievement("BARONY_ACH_POST_HARDCORE"); } - if ( client_classes[clientnum] == CLASS_MESMER ) + for ( int i = 0; i < MAXPLAYERS; ++i ) { - steamAchievement("BARONY_ACH_COMMANDER_CHIEF"); - } - else if ( client_classes[clientnum] == CLASS_BREWER ) - { - steamAchievement("BARONY_ACH_DRUNK_POWER"); - } - else if ( client_classes[clientnum] == CLASS_ACCURSED ) - { - steamAchievement("BARONY_ACH_POWER_HUNGRY"); - if ( stats[clientnum]->EFFECTS[EFF_VAMPIRICAURA] && stats[clientnum]->EFFECTS_TIMERS[EFF_VAMPIRICAURA] == -2 ) + if ( players[i]->isLocalPlayer() && !client_disconnected[i] ) { - if ( stats[clientnum] && (svFlags & SV_FLAG_HUNGER) ) + if ( client_classes[i] == CLASS_MESMER ) { - steamAchievement("BARONY_ACH_BLOOD_IS_THE_LIFE"); + steamAchievement("BARONY_ACH_COMMANDER_CHIEF"); + } + else if ( client_classes[i] == CLASS_BREWER ) + { + steamAchievement("BARONY_ACH_DRUNK_POWER"); + } + else if ( client_classes[i] == CLASS_ACCURSED ) + { + steamAchievement("BARONY_ACH_POWER_HUNGRY"); + if ( stats[i]->EFFECTS[EFF_VAMPIRICAURA] && stats[i]->EFFECTS_TIMERS[EFF_VAMPIRICAURA] == -2 ) + { + if ( stats[i] && (svFlags & SV_FLAG_HUNGER) ) + { + steamAchievement("BARONY_ACH_BLOOD_IS_THE_LIFE"); + } + } + } + else if ( client_classes[i] == CLASS_HUNTER ) + { + steamAchievement("BARONY_ACH_RANGER_DANGER"); + if ( conductGameChallenges[CONDUCT_RANGED_ONLY] ) + { + steamAchievement("BARONY_ACH_GUDIPARIAN_BAZI"); + } + } + else if ( client_classes[i] == CLASS_CONJURER ) + { + steamAchievement("BARONY_ACH_TURN_UNDEAD"); + } + else if ( client_classes[i] == CLASS_SHAMAN ) + { + steamAchievement("BARONY_ACH_MY_FINAL_FORM"); + } + else if ( client_classes[i] == CLASS_PUNISHER ) + { + steamAchievement("BARONY_ACH_TIME_TO_SUFFER"); + } + else if ( client_classes[i] == CLASS_MACHINIST ) + { + steamAchievement("BARONY_ACH_LIKE_CLOCKWORK"); } - } - } - else if ( client_classes[clientnum] == CLASS_HUNTER ) - { - steamAchievement("BARONY_ACH_RANGER_DANGER"); - if ( conductGameChallenges[CONDUCT_RANGED_ONLY] ) - { - steamAchievement("BARONY_ACH_GUDIPARIAN_BAZI"); - } - } - else if ( client_classes[clientnum] == CLASS_CONJURER ) - { - steamAchievement("BARONY_ACH_TURN_UNDEAD"); - } - else if ( client_classes[clientnum] == CLASS_SHAMAN ) - { - steamAchievement("BARONY_ACH_MY_FINAL_FORM"); - } - else if ( client_classes[clientnum] == CLASS_PUNISHER ) - { - steamAchievement("BARONY_ACH_TIME_TO_SUFFER"); - } - else if ( client_classes[clientnum] == CLASS_MACHINIST ) - { - steamAchievement("BARONY_ACH_LIKE_CLOCKWORK"); - } - if ( stats[clientnum] && stats[clientnum]->appearance == 0 ) - { - switch ( stats[clientnum]->playerRace ) - { - case RACE_SKELETON: - steamAchievement("BARONY_ACH_BONY_BARON"); - break; - case RACE_SUCCUBUS: - steamAchievement("BARONY_ACH_BOMBSHELL_BARON"); - break; - case RACE_GOATMAN: - steamAchievement("BARONY_ACH_BLEATING_BARON"); - break; - case RACE_VAMPIRE: - steamAchievement("BARONY_ACH_BUCKTOOTH_BARON"); - break; - case RACE_INCUBUS: - steamAchievement("BARONY_ACH_BAD_BOY_BARON"); - break; - case RACE_INSECTOID: - steamAchievement("BARONY_ACH_BUGGAR_BARON"); - break; - case RACE_AUTOMATON: - steamAchievement("BARONY_ACH_BOILERPLATE_BARON"); - break; - case RACE_GOBLIN: - steamAchievement("BARONY_ACH_BAYOU_BARON"); - break; - default: - break; + if ( stats[i] && stats[i]->appearance == 0 ) + { + switch ( stats[i]->playerRace ) + { + case RACE_SKELETON: + steamAchievement("BARONY_ACH_BONY_BARON"); + break; + case RACE_SUCCUBUS: + steamAchievement("BARONY_ACH_BOMBSHELL_BARON"); + break; + case RACE_GOATMAN: + steamAchievement("BARONY_ACH_BLEATING_BARON"); + break; + case RACE_VAMPIRE: + steamAchievement("BARONY_ACH_BUCKTOOTH_BARON"); + break; + case RACE_INCUBUS: + steamAchievement("BARONY_ACH_BAD_BOY_BARON"); + break; + case RACE_INSECTOID: + steamAchievement("BARONY_ACH_BUGGAR_BARON"); + break; + case RACE_AUTOMATON: + steamAchievement("BARONY_ACH_BOILERPLATE_BARON"); + break; + case RACE_GOBLIN: + steamAchievement("BARONY_ACH_BAYOU_BARON"); + break; + default: + break; + } + } } } } From 99821710f384e20915b74a3feb28f67fe292bf5b Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 16 Sep 2023 22:29:23 +1000 Subject: [PATCH 026/146] * fix mouse clicks in splitscreen causing events on other players who don't own the keyboard --- src/interface/bookgui.cpp | 3 ++- src/interface/playerinventory.cpp | 4 ++-- src/ui/GameUI.cpp | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/interface/bookgui.cpp b/src/interface/bookgui.cpp index 889cc15dd..df6974948 100644 --- a/src/interface/bookgui.cpp +++ b/src/interface/bookgui.cpp @@ -311,7 +311,8 @@ void Player::BookGUI_t::updateBookGUI() auto nextPageBoundary = innerFrame->findFrame("next page mouse boundary"); // book gui - if ( Input::inputs[player.playernum].binaryToggle("MenuLeftClick") ) + if ( Input::inputs[player.playernum].binaryToggle("MenuLeftClick") + && inputs.bPlayerUsingKeyboardControl(player.playernum) ) { //book_t GUI next page button. if ( nextPageBoundary->capturesMouse() ) diff --git a/src/interface/playerinventory.cpp b/src/interface/playerinventory.cpp index 95da7f6d5..73c26a6da 100644 --- a/src/interface/playerinventory.cpp +++ b/src/interface/playerinventory.cpp @@ -3262,8 +3262,8 @@ void releaseItem(const int player) bool& toggleclick = inputs.getUIInteraction(player)->toggleclick; // releasing items - if ( (!Input::inputs[player].binary("MenuLeftClick") && !toggleclick) - || (Input::inputs[player].binaryToggle("MenuLeftClick") && toggleclick) + if ( (!Input::inputs[player].binary("MenuLeftClick") && inputs.bPlayerUsingKeyboardControl(player) && !toggleclick) + || (Input::inputs[player].binaryToggle("MenuLeftClick") && inputs.bPlayerUsingKeyboardControl(player) && toggleclick) || ( (Input::inputs[player].binaryToggle(getContextMenuOptionBindingName(player, PROMPT_GRAB).c_str()) || Input::inputs[player].binaryToggle("MenuConfirm")) && toggleclick) ) diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 35edee635..3a314c6b8 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -5793,7 +5793,7 @@ void Player::HUD_t::updateStatusEffectFocusedWindow() } if ( inputs.getVirtualMouse(player.playernum)->draw_cursor ) { - if ( Input::inputs[player.playernum].binaryToggle("MenuLeftClick") ) + if ( Input::inputs[player.playernum].binaryToggle("MenuLeftClick") && inputs.bPlayerUsingKeyboardControl(player.playernum) ) { Input::inputs[player.playernum].consumeBinaryToggle("MenuLeftClick"); if ( !bgFrame->capturesMouse() ) @@ -32342,7 +32342,7 @@ void Player::SkillSheet_t::processSkillSheet() skillSlideDirection = -1; } } - if ( Input::inputs[player.playernum].binaryToggle("MenuLeftClick") ) + if ( Input::inputs[player.playernum].binaryToggle("MenuLeftClick") && inputs.bPlayerUsingKeyboardControl(player.playernum) ) { selectSkill(i); Input::inputs[player.playernum].consumeBinaryToggle("MenuLeftClick"); From 09ad6473c4596e564bc682bcdd84da1b570888f1 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sun, 17 Sep 2023 01:09:32 +1000 Subject: [PATCH 027/146] * fix multiplayer player waves --- src/net.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/net.cpp b/src/net.cpp index d6b62dd83..2fc6d28d4 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -5266,6 +5266,25 @@ static std::unordered_map serverPacketHandlers = { } else { + if ( entity && entity->behavior == &actPlayer && entity->skill[2] != pnum ) + { + if ( cmd == CalloutRadialMenu::CALLOUT_CMD_LOOK + || cmd == CalloutRadialMenu::CALLOUT_CMD_AFFIRMATIVE + || cmd == CalloutRadialMenu::CALLOUT_CMD_NEGATIVE ) + { + entity = players[pnum]->entity; + } + else if ( cmd == CalloutRadialMenu::CALLOUT_CMD_SOUTH + || cmd == CalloutRadialMenu::CALLOUT_CMD_SOUTHWEST + || cmd == CalloutRadialMenu::CALLOUT_CMD_SOUTHEAST ) + { + int toPlayer = CalloutMenu[pnum].getPlayerForDirectPlayerCmd(pnum, cmd); + if ( toPlayer >= 0 ) + { + entity = players[pnum]->entity; + } + } + } if ( CalloutMenu[pnum].createParticleCallout(entity, cmd) ) { CalloutMenu[pnum].sendCalloutText(cmd); From 7a30c39167eb1cb63a9b90db94231eeea9382253 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 19 Sep 2023 00:47:59 +1000 Subject: [PATCH 028/146] * fix charm monster glitching allies in follower list --- src/interface/interface.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index 0e8812338..b8d664338 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -2164,6 +2164,20 @@ void FollowerRadialMenu::drawFollowerMenu() Uint32 textHighlightColor = followerBannerTextHighlightColor; bool tinkeringFollower = false; + if ( recentEntity ) + { + if ( recentEntity->monsterAllyIndex != gui_player ) // our ally left our service by charm or otherwise + { + recentEntity = nullptr; + if ( followerToCommand == recentEntity ) + { + closeFollowerMenuGUI(); + players[gui_player]->closeAllGUIs(CLOSEGUI_ENABLE_SHOOTMODE, CLOSEGUI_CLOSE_ALL); + return; + } + } + } + if ( !followerToCommand && (!followerFrame->isDisabled() || players[gui_player]->gui_mode == GUI_MODE_FOLLOWERMENU) ) { closeFollowerMenuGUI(); From e318d8ce887189869338fc98b3c3d796b12b4acc Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 21 Sep 2023 07:29:11 +1000 Subject: [PATCH 029/146] * initial player ghost code --- src/actplayer.cpp | 921 +++++++++++++++++++++++++++++++++- src/collision.cpp | 6 +- src/entity.cpp | 5 + src/game.cpp | 22 +- src/game.hpp | 1 + src/interface/drawminimap.cpp | 2 +- src/interface/interface.cpp | 112 +++-- src/interface/interface.hpp | 2 + src/menu.cpp | 3 + src/net.cpp | 164 ++++++ src/player.cpp | 88 +++- src/player.hpp | 26 + 12 files changed, 1281 insertions(+), 71 deletions(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index e26d403aa..fdafc875a 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -37,6 +37,9 @@ bool disablemouserotationlimit = true; bool settings_disablemouserotationlimit = false; bool swimDebuffMessageHasPlayed = false; bool partymode = false; +static ConsoleVariable cvar_calloutStartZ("/callout_start_z", -2.5); +static ConsoleVariable cvar_calloutMoveTo("/callout_moveto_z", 0.1); +static ConsoleVariable cvar_calloutStartZLimit("/callout_start_z_limit", 7.5); /*------------------------------------------------------------------------------- @@ -47,12 +50,863 @@ bool partymode = false; -------------------------------------------------------------------------------*/ +#define GHOSTCAM_PLAYERNUM my->skill[2] + +#define GHOSTCAM_BOB my->fskill[0] +#define GHOSTCAM_BOBMOVE my->fskill[1] +#define GHOSTCAM_WEAPONYAW my->fskill[2] +#define GHOSTCAM_DX my->fskill[3] +#define GHOSTCAM_DY my->fskill[4] +#define GHOSTCAM_DYAW my->fskill[5] +#define GHOSTCAM_ROTX my->fskill[6] +#define GHOSTCAM_ROTY my->fskill[7] + +void Player::Ghost_t::handleGhostCameraBobbing(bool useRefreshRateDelta) +{ +} +void Player::Ghost_t::handleGhostCameraPosition(bool useRefreshRateDelta) +{ +} + +void Player::Ghost_t::handleGhostMovement(const bool useRefreshRateDelta) +{ + if ( !my ) { return; } + + Input& input = Input::inputs[player.playernum]; + + double refreshRateDelta = 1.0; + if ( useRefreshRateDelta && fps > 0.0 ) + { + refreshRateDelta *= TICKS_PER_SECOND / (real_t)fpsLimit; + } + + // calculate movement forces + bool allowMovement = true; + + if ( ((!player.usingCommand() && player.bControlEnabled && !gamePaused)) + && allowMovement ) + { + //x_force and y_force represent the amount of percentage pushed on that respective axis. Given a keyboard, it's binary; either you're pushing "move left" or you aren't. On an analog stick, it can range from whatever value to whatever. + float x_force = 0; + float y_force = 0; + + { + double backpedalMultiplier = 0.25; + + if ( !inputs.hasController(player.playernum) ) + { + x_force = (input.binary("Move Right") - input.binary("Move Left")); + y_force = input.binary("Move Forward") - (double)input.binary("Move Backward") * backpedalMultiplier; + } + + if ( inputs.hasController(player.playernum) /*&& !input.binary("Move Left") && !input.binary("Move Right")*/ ) + { + x_force = inputs.getController(player.playernum)->getLeftXPercentForPlayerMovement(); + } + + if ( inputs.hasController(player.playernum) /*&& !input.binary("Move Forward") && !input.binary("Move Backward")*/ ) + { + y_force = inputs.getController(player.playernum)->getLeftYPercentForPlayerMovement(); + if ( y_force < 0 ) + { + y_force *= backpedalMultiplier; //Move backwards more slowly. + } + } + } + + real_t speedFactor = 12.0;// getSpeedFactor(weightratio, statGetDEX(stats[PLAYER_NUM], players[PLAYER_NUM]->entity)); + speedFactor *= refreshRateDelta; + my->vel_x += y_force * cos(my->yaw) * .045 * speedFactor; + my->vel_y += y_force * sin(my->yaw) * .045 * speedFactor; + my->vel_x += x_force * cos(my->yaw + PI / 2) * .0225 * speedFactor; + my->vel_y += x_force * sin(my->yaw + PI / 2) * .0225 * speedFactor; + + } + my->vel_x *= pow(0.75, refreshRateDelta); + my->vel_y *= pow(0.75, refreshRateDelta); + + /*for ( int i = 0; i < MAXPLAYERS; ++i ) + { + if ( players[i] && players[i]->entity ) + { + if ( players[i]->entity == my ) + { + continue; + } + if ( entityInsideEntity(my, players[i]->entity) ) + { + double tangent = atan2(my->y - players[i]->entity->y, my->x - players[i]->entity->x); + PLAYER_VELX += cos(tangent) * 0.075 * refreshRateDelta; + PLAYER_VELY += sin(tangent) * 0.075 * refreshRateDelta; + } + } + }*/ +} + +void Player::Ghost_t::startQuickTurn() +{ + if ( !my ) + { + return; + } + if ( bDoingQuickTurn ) + { + return; + } + + if ( gamePaused ) + { + return; + } + + quickTurnRotation = PI * player.settings.quickTurnDirection; + quickTurnStartTicks = my->ticks; + bDoingQuickTurn = true; +} + +bool Player::Ghost_t::handleQuickTurn(bool useRefreshRateDelta) +{ + if ( !my ) { return false; } + + if ( !bDoingQuickTurn ) + { + quickTurnRotation = 0.0; + bDoingQuickTurn = false; + quickTurnStartTicks = 0; + return false; + } + + Entity* my = players[player.playernum]->entity; + double refreshRateDelta = 1.0; + if ( useRefreshRateDelta && fps > 0.0 ) + { + refreshRateDelta *= TICKS_PER_SECOND / (real_t)fpsLimit; + } + + if ( abs(quickTurnRotation) > 0.001 ) + { + int dir = ((quickTurnRotation > 0) ? 1 : -1); + if ( my->ticks - quickTurnStartTicks < 15 ) + { + GHOSTCAM_ROTX = dir * players[player.playernum]->settings.quickTurnSpeed; + } + else + { + GHOSTCAM_ROTX = std::max(0.01, (dir * PI / 15) * pow(0.99, my->ticks - quickTurnStartTicks)); + } + + if ( dir == 1 ) + { + quickTurnRotation = std::max(0.0, quickTurnRotation - GHOSTCAM_ROTX * refreshRateDelta); + } + else + { + quickTurnRotation = std::min(0.0, quickTurnRotation - GHOSTCAM_ROTX * refreshRateDelta); + } + return true; + } + else + { + bDoingQuickTurn = false; + return false; + } +} + +void Player::Ghost_t::reset() +{ + quickTurnRotation = 0.0; + quickTurnStartTicks = 0; + bDoingQuickTurn = false; + my = nullptr; + uid = 0; +} + +void Player::Ghost_t::handleActions() +{ + Input& input = Input::inputs[player.playernum]; + CalloutRadialMenu& calloutMenu = CalloutMenu[player.playernum]; + auto& b = (multiplayer != SINGLE && player.playernum != 0) ? Input::inputs[0].getBindings() : input.getBindings(); + + if ( !calloutMenu.calloutMenuIsOpen() ) + { + bool clickedOnGUI = false; + + EntityClickType clickType = ENTITY_CLICK_USE; + bool tempDisableWorldUI = false; + bool skipUse = false; + if ( player.worldUI.isEnabled() ) + { + if ( !player.shootmode && input.input("Use").isBindingUsingGamepad() ) + { + skipUse = true; + } + else if ( !player.shootmode && inputs.bPlayerUsingKeyboardControl(player.playernum) ) + { + tempDisableWorldUI = true; + } + else if ( (!player.shootmode) + || (player.hotbar.useHotbarFaceMenu + && (player.hotbar.faceMenuButtonHeld != Player::Hotbar_t::GROUP_NONE)) ) + { + skipUse = true; + } + } + else if ( !player.worldUI.isEnabled() && input.input("Use").isBindingUsingGamepad() + && !player.shootmode ) + { + skipUse = true; + } + + if ( !skipUse ) + { + if ( tempDisableWorldUI ) + { + player.worldUI.disable(); + } + if ( player.worldUI.isEnabled() ) + { + clickType = ENTITY_CLICK_USE_TOOLTIPS_ONLY; + Entity* activeTooltipEntity = uidToEntity(player.worldUI.uidForActiveTooltip); + if ( activeTooltipEntity && activeTooltipEntity->bEntityTooltipRequiresButtonHeld() ) + { + clickType = ENTITY_CLICK_HELD_USE_TOOLTIPS_ONLY; + } + } + + selectedEntity[player.playernum] = entityClicked(&clickedOnGUI, false, player.playernum, clickType); // using objects + if ( !selectedEntity[player.playernum] && !clickedOnGUI ) + { + if ( clickType == ENTITY_CLICK_USE ) + { + // otherwise if we hold right click we'll keep trying this function, FPS will drop. + if ( input.binary("Use") ) + { + ++player.movement.selectedEntityGimpTimer; + } + } + } + + if ( tempDisableWorldUI ) + { + player.worldUI.enable(); + } + } + else + { + input.consumeBinaryToggle("Use"); + //input.consumeBindingsSharedWithBinding("Use"); + selectedEntity[player.playernum] = nullptr; + } + } + else if ( calloutMenu.calloutMenuIsOpen() ) + { + selectedEntity[player.playernum] = NULL; + // TODO CALLOUT? + if ( !player.usingCommand() && player.bControlEnabled && !gamePaused && input.binaryToggle("Use") ) + { + if ( !calloutMenu.menuToggleClick && calloutMenu.selectMoveTo ) + { + if ( calloutMenu.optionSelected == CalloutRadialMenu::CALLOUT_CMD_SELECT ) + { + // we're selecting a target for the ally. + Entity* target = entityClicked(nullptr, false, player.playernum, EntityClickType::ENTITY_CLICK_CALLOUT); + input.consumeBinaryToggle("Use"); + //input.consumeBindingsSharedWithBinding("Use"); + if ( target ) + { + Entity* parent = uidToEntity(target->skill[2]); + if ( target->behavior == &actMonster || (parent && parent->behavior == &actMonster) ) + { + // see if we selected a limb + if ( parent ) + { + target = parent; + } + } + else if ( target->sprite == 184 || target->sprite == 585 ) // switch base. + { + parent = uidToEntity(target->parent); + if ( parent ) + { + target = parent; + } + } + if ( true /*&& calloutMenu.allowedInteractEntity(*target)*/ ) + { + calloutMenu.lockOnEntityUid = target->getUID(); + if ( target->behavior == &actPlayer && target->skill[2] != player.playernum ) + { + target = my; + } + + if ( calloutMenu.createParticleCallout(target) ) + { + calloutMenu.sendCalloutText(CalloutRadialMenu::CALLOUT_CMD_LOOK); + } + /*calloutMenu.holdWheel = false; + calloutMenu.selectMoveTo = false; + calloutMenu.bOpen = true; + calloutMenu.optionSelected = ALLY_CMD_CANCEL; + calloutMenu.initCalloutMenuGUICursor(true); + Player::soundActivate();*/ + } + } + else + { + // we're selecting a point in the world + if ( my ) + { + real_t startx = cameras[player.playernum].x * 16.0; + real_t starty = cameras[player.playernum].y * 16.0; + real_t startz = cameras[player.playernum].z + (4.5 - cameras[player.playernum].z) / 2.0 + *cvar_calloutStartZ; + real_t pitch = cameras[player.playernum].vang; + if ( pitch < 0 || pitch > PI ) + { + pitch = 0; + } + + // draw line from the players height and direction until we hit the ground. + real_t previousx = startx; + real_t previousy = starty; + int index = 0; + const real_t yaw = cameras[player.playernum].ang; + for ( ; startz < *cvar_calloutStartZLimit; startz += abs((*cvar_calloutMoveTo) * tan(pitch)) ) + { + startx += 0.1 * cos(yaw); + starty += 0.1 * sin(yaw); + const int index_x = static_cast(startx) >> 4; + const int index_y = static_cast(starty) >> 4; + index = (index_y)*MAPLAYERS + (index_x)*MAPLAYERS * map.height; + if ( !map.tiles[OBSTACLELAYER + index] ) + { + // store the last known good coordinate + previousx = startx;// + 16 * cos(yaw); + previousy = starty;// + 16 * sin(yaw); + } + if ( map.tiles[OBSTACLELAYER + index] ) + { + break; + } + } + + calloutMenu.moveToX = previousx; + calloutMenu.moveToY = previousy; + calloutMenu.lockOnEntityUid = 0; + if ( calloutMenu.createParticleCallout(previousx, previousy, -4, 0, CalloutRadialMenu::CALLOUT_CMD_LOOK) ) + { + calloutMenu.sendCalloutText(CalloutRadialMenu::CALLOUT_CMD_LOOK); + } + } + } + + if ( player.worldUI.isEnabled() ) + { + player.worldUI.reset(); + player.worldUI.tooltipView = Player::WorldUI_t::TooltipView::TOOLTIP_VIEW_RESCAN; + } + + calloutMenu.closeCalloutMenuGUI(); + strcpy(calloutMenu.interactText, ""); + } + } + } + } + + if ( !player.usingCommand() && player.bControlEnabled + && !gamePaused ) + { + bool showCalloutCommandsOnGamepad = false; + auto showCalloutCommandsFind = b.find("Show Player Callouts"); + std::string showCalloutCommandsInputStr = ""; + if ( showCalloutCommandsFind != b.end() ) + { + showCalloutCommandsOnGamepad = (*showCalloutCommandsFind).second.isBindingUsingGamepad(); + showCalloutCommandsInputStr = (*showCalloutCommandsFind).second.input; + } + + if ( player.worldUI.bTooltipInView && player.worldUI.tooltipsInRange.size() > 1 ) + { + if ( showCalloutCommandsOnGamepad && + (showCalloutCommandsInputStr == input.binding("Interact Tooltip Next") + || showCalloutCommandsInputStr == input.binding("Interact Tooltip Prev")) ) + { + input.consumeBinaryToggle("Show Player Callouts"); + player.hud.bOpenCalloutsMenuDisabled = true; + } + } + + if ( (input.binaryToggle("Show Player Callouts") && !showCalloutCommandsOnGamepad + && player.shootmode) + || (input.binaryToggle("Show Player Callouts") && showCalloutCommandsOnGamepad + && player.shootmode /*&& !player.worldUI.bTooltipInView*/) ) + { + if ( !calloutMenu.bOpen && !calloutMenu.selectMoveTo ) + { + if ( !player.shootmode ) + { + player.closeAllGUIs(CLOSEGUI_ENABLE_SHOOTMODE, CLOSEGUI_DONT_CLOSE_CALLOUTGUI); + } + input.consumeBinaryToggle("Show Player Callouts"); + calloutMenu.selectMoveTo = true; + calloutMenu.optionSelected = CalloutRadialMenu::CALLOUT_CMD_SELECT; + calloutMenu.lockOnEntityUid = 0; + Player::soundActivate(); + + if ( player.worldUI.isEnabled() ) + { + player.worldUI.reset(); + player.worldUI.tooltipView = Player::WorldUI_t::TooltipView::TOOLTIP_VIEW_RESCAN; + player.worldUI.gimpDisplayTimer = 0; + } + } + else if ( calloutMenu.selectMoveTo ) + { + // we're selecting a target for the ally. + Entity* target = entityClicked(nullptr, true, player.playernum, EntityClickType::ENTITY_CLICK_CALLOUT); + + if ( target ) + { + Entity* parent = uidToEntity(target->skill[2]); + if ( target->behavior == &actMonster || (parent && parent->behavior == &actMonster) ) + { + // see if we selected a limb + if ( parent ) + { + target = parent; + } + } + else if ( target->sprite == 184 || target->sprite == 585 ) // switch base. + { + parent = uidToEntity(target->parent); + if ( parent ) + { + target = parent; + } + } + + calloutMenu.holdWheel = true; + if ( showCalloutCommandsOnGamepad ) + { + calloutMenu.holdWheel = false; + } + calloutMenu.selectMoveTo = false; + calloutMenu.bOpen = true; + calloutMenu.initCalloutMenuGUICursor(true); + Player::soundActivate(); + calloutMenu.lockOnEntityUid = target->getUID(); + } + else + { + // we're selecting a point in the world + if ( my ) + { + real_t startx = cameras[player.playernum].x * 16.0; + real_t starty = cameras[player.playernum].y * 16.0; + real_t startz = cameras[player.playernum].z + (4.5 - cameras[player.playernum].z) / 2.0 + *cvar_calloutStartZ; + real_t pitch = cameras[player.playernum].vang; + if ( pitch < 0 || pitch > PI ) + { + pitch = 0; + } + + // draw line from the players height and direction until we hit the ground. + real_t previousx = startx; + real_t previousy = starty; + int index = 0; + const real_t yaw = cameras[player.playernum].ang; + for ( ; startz < *cvar_calloutStartZLimit; startz += abs((*cvar_calloutMoveTo) * tan(pitch)) ) + { + startx += 0.1 * cos(yaw); + starty += 0.1 * sin(yaw); + const int index_x = static_cast(startx) >> 4; + const int index_y = static_cast(starty) >> 4; + index = (index_y)*MAPLAYERS + (index_x)*MAPLAYERS * map.height; + if ( !map.tiles[OBSTACLELAYER + index] ) + { + // store the last known good coordinate + previousx = startx;// + 16 * cos(yaw); + previousy = starty;// + 16 * sin(yaw); + } + if ( map.tiles[OBSTACLELAYER + index] ) + { + break; + } + } + + calloutMenu.holdWheel = true; + if ( showCalloutCommandsOnGamepad ) + { + calloutMenu.holdWheel = false; + } + calloutMenu.selectMoveTo = false; + calloutMenu.bOpen = true; + calloutMenu.lockOnEntityUid = 0; + calloutMenu.moveToX = previousx; + calloutMenu.moveToY = previousy; + calloutMenu.initCalloutMenuGUICursor(true); + //Player::soundActivate(); + } + else + { + calloutMenu.closeCalloutMenuGUI(); + } + } + } + } + } +} + +void Player::Ghost_t::handleGhostCameraUpdate(const bool useRefreshRateDelta) +{ + if ( !my ) { return; } + + real_t mousex_relative = mousexrel; + real_t mousey_relative = mouseyrel; + + mousex_relative = inputs.getMouseFloat(player.playernum, Inputs::ANALOGUE_XREL); + mousey_relative = inputs.getMouseFloat(player.playernum, Inputs::ANALOGUE_YREL); + + const bool smoothmouse = playerSettings[multiplayer ? 0 : player.playernum].smoothmouse; + const bool reversemouse = playerSettings[multiplayer ? 0 : player.playernum].reversemouse; + real_t mouse_speed = playerSettings[multiplayer ? 0 : player.playernum].mousespeed; + if ( inputs.getVirtualMouse(player.playernum)->lastMovementFromController ) + { + mouse_speed = 32.0; + } + + double refreshRateDelta = 1.0; + if ( useRefreshRateDelta && fps > 0.0 ) + { + refreshRateDelta *= TICKS_PER_SECOND / (real_t)fpsLimit; + } + + if ( player.shootmode && !player.usingCommand() + && !gamePaused + && player.bControlEnabled + && player.hotbar.faceMenuButtonHeld == Player::Hotbar_t::FaceMenuGroup::GROUP_NONE ) + { + if ( Input::inputs[player.playernum].consumeBinaryToggle("Quick Turn") ) + { + startQuickTurn(); + } + } + + // rotate + if ( !player.usingCommand() + && player.bControlEnabled && !gamePaused && my->isMobile() && !inputs.hasController(player.playernum) ) + { + if ( noclip ) + { + my->z -= (Input::inputs[player.playernum].analog("Turn Right") + - Input::inputs[player.playernum].analog("Turn Left")) * .25 * refreshRateDelta; + } + else + { + my->yaw += (Input::inputs[player.playernum].analog("Turn Right") + - Input::inputs[player.playernum].analog("Turn Left")) * .05 * refreshRateDelta; + } + } + bool shootmode = player.shootmode; + + if ( handleQuickTurn(useRefreshRateDelta) ) + { + // do nothing, override rotations. + } + else if ( shootmode && !gamePaused ) + { + if ( smoothmouse ) + { + if ( my->isMobile() ) + { + GHOSTCAM_ROTX += mousex_relative * .006 * (mouse_speed / 128.f); + } + if ( !disablemouserotationlimit ) + { + GHOSTCAM_ROTX = fmin(fmax(-0.35, GHOSTCAM_ROTX), 0.35); + } + GHOSTCAM_ROTX *= pow(0.5, refreshRateDelta); + } + else + { + if ( my->isMobile() ) + { + if ( disablemouserotationlimit ) + { + GHOSTCAM_ROTX = mousex_relative * .01f * (mouse_speed / 128.f); + } + else + { + GHOSTCAM_ROTX = std::min(std::max(-0.35f, mousex_relative * .01f * (mouse_speed / 128.f)), 0.35f); + } + } + else + { + GHOSTCAM_ROTX = 0; + } + } + } + + my->yaw += GHOSTCAM_ROTX * refreshRateDelta; + while ( my->yaw >= PI * 2 ) + { + my->yaw -= PI * 2; + } + while ( my->yaw < 0 ) + { + my->yaw += PI * 2; + } + + if ( smoothmouse ) + { + GHOSTCAM_ROTX *= pow(0.5, refreshRateDelta); + } + else + { + GHOSTCAM_ROTX = 0; + } + + // look up and down + if ( !player.usingCommand() + && player.bControlEnabled && !gamePaused && my->isMobile() && !inputs.hasController(player.playernum) ) + { + my->pitch += (Input::inputs[player.playernum].analog("Look Down") + - Input::inputs[player.playernum].analog("Look Up")) * .05 * refreshRateDelta; + } + if ( shootmode && !gamePaused ) + { + if ( smoothmouse ) + { + if ( my->isMobile() ) + { + GHOSTCAM_ROTY += mousey_relative * .006 * (mouse_speed / 128.f) * (reversemouse * 2 - 1); + } + GHOSTCAM_ROTY = fmin(fmax(-0.35, GHOSTCAM_ROTY), 0.35); + GHOSTCAM_ROTY *= pow(0.5, refreshRateDelta); + } + else + { + if ( my->isMobile() ) + { + GHOSTCAM_ROTY = std::min(std::max(-0.35f, + mousey_relative * .01f * (mouse_speed / 128.f) * (reversemouse * 2 - 1)), 0.35f); + } + else + { + GHOSTCAM_ROTY = 0; + } + } + } + my->pitch -= GHOSTCAM_ROTY * refreshRateDelta; + + if ( my->pitch > PI / 3 ) + { + my->pitch = PI / 3; + } + if ( my->pitch < -PI / 3 ) + { + my->pitch = -PI / 3; + } + + if ( smoothmouse ) + { + GHOSTCAM_ROTY *= pow(0.5, refreshRateDelta); + } + else + { + GHOSTCAM_ROTY = 0; + } + + if ( TimerExperiments::bUseTimerInterpolation ) + { + while ( TimerExperiments::cameraCurrentState[player.playernum].yaw.position >= PI * 2 ) + { + TimerExperiments::cameraCurrentState[player.playernum].yaw.position -= PI * 2; + } + while ( TimerExperiments::cameraCurrentState[player.playernum].yaw.position < 0 ) + { + TimerExperiments::cameraCurrentState[player.playernum].yaw.position += PI * 2; + } + real_t diff = my->yaw - TimerExperiments::cameraCurrentState[player.playernum].yaw.position; + if ( diff > PI ) + { + diff -= 2 * PI; + } + else if ( diff < -PI ) + { + diff += 2 * PI; + } + TimerExperiments::cameraCurrentState[player.playernum].yaw.velocity = diff * TimerExperiments::lerpFactor; + while ( TimerExperiments::cameraCurrentState[player.playernum].pitch.position >= PI ) + { + TimerExperiments::cameraCurrentState[player.playernum].pitch.position -= PI * 2; + } + while ( TimerExperiments::cameraCurrentState[player.playernum].pitch.position < -PI ) + { + TimerExperiments::cameraCurrentState[player.playernum].pitch.position += PI * 2; + } + diff = my->pitch - TimerExperiments::cameraCurrentState[player.playernum].pitch.position; + if ( diff >= PI ) + { + diff -= 2 * PI; + } + else if ( diff < -PI ) + { + diff += 2 * PI; + } + TimerExperiments::cameraCurrentState[player.playernum].pitch.velocity = diff * TimerExperiments::lerpFactor; + } +} + +void actDeathGhost(Entity* my) +{ + int playernum = GHOSTCAM_PLAYERNUM; + if ( playernum < 0 || playernum >= MAXPLAYERS ) + { + return; + } + + players[playernum]->ghost.my = my; + players[playernum]->ghost.uid = my->getUID(); + + auto player = players[playernum]; + + my->removeLightField(); + my->light = addLight(my->x / 16, my->y / 16, "deathcam"); + + if ( player->isLocalPlayer() ) + { + bool inputsEnabled = false; + if ( player->shootmode + && !players[playernum]->GUI.isGameoverActive() + && players[playernum]->bControlEnabled + && !players[playernum]->usingCommand() + && !gamePaused ) + { + inputsEnabled = true; + } + + if ( !usecamerasmoothing ) + { + player->ghost.handleGhostMovement(false); + player->ghost.handleGhostCameraUpdate(false); + } + + if ( inputsEnabled ) + { + player->ghost.handleActions(); + } + + real_t camx, camy, camz, camang, camvang; + camx = my->x / 16.f; + camy = my->y / 16.f; + camz = my->z * 2.f; + camang = my->yaw; + camvang = my->pitch; + + camx -= cos(my->yaw) * cos(my->pitch) * 1.5; + camy -= sin(my->yaw) * cos(my->pitch) * 1.5; + camz -= sin(my->pitch) * 16; + + if ( !TimerExperiments::bUseTimerInterpolation ) + { + cameras[playernum].x = camx; + cameras[playernum].y = camy; + cameras[playernum].z = camz; + cameras[playernum].ang = camang; + cameras[playernum].vang = camvang; + return; + } + else + { + TimerExperiments::cameraCurrentState[playernum].x.velocity = + TimerExperiments::lerpFactor * (camx - TimerExperiments::cameraCurrentState[playernum].x.position); + TimerExperiments::cameraCurrentState[playernum].y.velocity = + TimerExperiments::lerpFactor * (camy - TimerExperiments::cameraCurrentState[playernum].y.position); + TimerExperiments::cameraCurrentState[playernum].z.velocity = + TimerExperiments::lerpFactor * (camz - TimerExperiments::cameraCurrentState[playernum].z.position); + + real_t diff = camang - TimerExperiments::cameraCurrentState[playernum].yaw.position; + if ( diff > PI ) + { + diff -= 2 * PI; + } + else if ( diff < -PI ) + { + diff += 2 * PI; + } + TimerExperiments::cameraCurrentState[playernum].yaw.velocity = diff * TimerExperiments::lerpFactor; + diff = camvang - TimerExperiments::cameraCurrentState[playernum].pitch.position; + if ( diff >= PI ) + { + diff -= 2 * PI; + } + else if ( diff < -PI ) + { + diff += 2 * PI; + } + TimerExperiments::cameraCurrentState[playernum].pitch.velocity = diff * TimerExperiments::lerpFactor; + } + } + + if ( player->isLocalPlayer() ) + { + // send movement updates to server + if ( multiplayer == CLIENT ) + { + strcpy((char*)net_packet->data, "GMOV"); + net_packet->data[4] = playernum; + net_packet->data[5] = currentlevel; + SDLNet_Write16((Sint16)(my->x * 32), &net_packet->data[6]); + SDLNet_Write16((Sint16)(my->y * 32), &net_packet->data[8]); + SDLNet_Write16((Sint16)(my->vel_x * 128), &net_packet->data[10]); + SDLNet_Write16((Sint16)(my->vel_y * 128), &net_packet->data[12]); + SDLNet_Write16((Sint16)(my->yaw * 128), &net_packet->data[14]); + SDLNet_Write16((Sint16)(my->pitch * 128), &net_packet->data[16]); + net_packet->data[18] = secretlevel; + net_packet->address.host = net_server.host; + net_packet->address.port = net_server.port; + net_packet->len = 19; + sendPacket(net_sock, -1, net_packet, 0); + } + + // perform collision detection + real_t dist = clipMove(&my->x, &my->y, my->vel_x, my->vel_y, my); + } + + if ( !player->isLocalPlayer() && multiplayer == SERVER ) + { + // PLAYER_VEL* skills updated by messages sent to server from client + + // move (dead reckoning) + /*if ( noclip == false )*/ + { + // from PMOV in serverHandlePacket - new_x and new_y are accumulated positions + if ( my->new_x > 0.001 ) + { + my->x = my->new_x; + } + if ( my->new_y > 0.001 ) + { + my->y = my->new_y; + } + + real_t dist = clipMove(&my->x, &my->y, my->vel_x, my->vel_y, my); + } + } + + if ( !player->isLocalPlayer() && multiplayer == CLIENT ) + { + real_t dist = sqrt(my->vel_x * my->vel_x + my->vel_y * my->vel_y); + } +} + #define DEATHCAM_TIME my->skill[0] #define DEATHCAM_PLAYERTARGET my->skill[1] #define DEATHCAM_PLAYERNUM my->skill[2] #define DEATHCAM_IDLETIME my->skill[3] #define DEATHCAM_IDLEROTATEDIRYAW my->skill[4] #define DEATHCAM_IDLEROTATEPITCHINIT my->skill[5] +#define DEATHCAM_CREATEDGHOST my->skill[6] #define DEATHCAM_ROTX my->fskill[0] #define DEATHCAM_ROTY my->fskill[1] #define DEATHCAM_IDLEPITCH my->fskill[2] @@ -69,8 +923,9 @@ void actDeathCam(Entity* my) }*/ DEATHCAM_TIME++; - Uint32 deathcamGameoverPromptTicks = *MainMenu::cvar_fastRestart ? TICKS_PER_SECOND : - (splitscreen ? TICKS_PER_SECOND * 3 : TICKS_PER_SECOND * 6); + /*Uint32 deathcamGameoverPromptTicks = *MainMenu::cvar_fastRestart ? TICKS_PER_SECOND : + (splitscreen ? TICKS_PER_SECOND * 3 : TICKS_PER_SECOND * 6);*/ + Uint32 deathcamGameoverPromptTicks = 25; if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_TUTORIAL ) { deathcamGameoverPromptTicks = TICKS_PER_SECOND * 3; @@ -117,7 +972,7 @@ void actDeathCam(Entity* my) } bool shootmode = players[DEATHCAM_PLAYERNUM]->shootmode; - if ( shootmode && !gamePaused ) + if ( shootmode && !gamePaused && DEATHCAM_CREATEDGHOST == 0 ) { if ( !players[DEATHCAM_PLAYERNUM]->GUI.isGameoverActive() ) { @@ -249,6 +1104,56 @@ void actDeathCam(Entity* my) && (Input::inputs[DEATHCAM_PLAYERNUM].consumeBinaryToggle("Attack") || Input::inputs[DEATHCAM_PLAYERNUM].consumeBinaryToggle("MenuConfirm")) ) { + if ( DEATHCAM_CREATEDGHOST == 0 ) + { + DEATHCAM_CREATEDGHOST = 1; + + // deathcam + if ( multiplayer != CLIENT ) + { + int sprite = 1233 + (DEATHCAM_PLAYERNUM < 4 ? DEATHCAM_PLAYERNUM : 4); + Entity* entity = newEntity(sprite, 1, map.entities, nullptr); //Ghost entity. + entity->x = my->x; + entity->y = my->y; + entity->z = -4; + entity->flags[PASSABLE] = true; + entity->flags[INVISIBLE] = false;// true; + entity->behavior = &actDeathGhost; + entity->skill[2] = DEATHCAM_PLAYERNUM; + entity->sizex = 4; + entity->sizey = 4; + entity->yaw = my->yaw; + entity->pitch = 0; + if ( DEATHCAM_PLAYERNUM == clientnum && multiplayer == CLIENT ) + { + entity->flags[UPDATENEEDED] = false; + } + else + { + entity->flags[UPDATENEEDED] = true; + } + players[DEATHCAM_PLAYERNUM]->ghost.my = entity; + players[DEATHCAM_PLAYERNUM]->ghost.uid = entity->getUID(); + } + + if ( multiplayer == CLIENT ) + { + strcpy((char*)net_packet->data, "GHOS"); + net_packet->data[4] = DEATHCAM_PLAYERNUM; + net_packet->data[5] = currentlevel; + + int x = (my->x / 16) + 8; + int y = (my->y / 16) + 8; + SDLNet_Write16((Sint16)(x), &net_packet->data[6]); + SDLNet_Write16((Sint16)(y), &net_packet->data[8]); + net_packet->data[10] = secretlevel; + net_packet->address.host = net_server.host; + net_packet->address.port = net_server.port; + net_packet->len = 11; + sendPacketSafe(net_sock, -1, net_packet, 0); + } + } + DEATHCAM_PLAYERTARGET++; if (DEATHCAM_PLAYERTARGET >= MAXPLAYERS) { @@ -284,6 +1189,12 @@ void actDeathCam(Entity* my) } my->removeLightField(); + + if ( DEATHCAM_CREATEDGHOST != 0 ) + { + return; + } + my->light = addLight(my->x / 16, my->y / 16, "deathcam"); real_t camx, camy, camz, camang, camvang; @@ -4034,10 +4945,6 @@ void actPlayer(Entity* my) } } - static ConsoleVariable cvar_calloutStartZ("/callout_start_z", -2.5); - static ConsoleVariable cvar_calloutMoveTo("/callout_moveto_z", 0.1); - static ConsoleVariable cvar_calloutStartZLimit("/callout_start_z_limit", 7.5); - if ( players[PLAYER_NUM]->isLocalPlayer() ) { players[PLAYER_NUM]->entity = my; diff --git a/src/collision.cpp b/src/collision.cpp index 2c7455372..dc97dc723 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -61,8 +61,10 @@ Entity* entityClicked(bool* clickedOnGUI, bool clickCheckOverride, int player, E Input& input = Input::inputs[player]; - if ( gamePaused || movie || !players[player] || !players[player]->entity - || players[player]->entity->ticks < (TICKS_PER_SECOND / 2) + Entity* playerEntity = Player::getPlayerInteractEntity(player); + + if ( gamePaused || movie || !players[player] || !playerEntity + || playerEntity->ticks < (TICKS_PER_SECOND / 2) || fadeout || (players[player]->usingCommand() && input.input("Use").type == Input::binding_t::KEYBOARD) ) { diff --git a/src/entity.cpp b/src/entity.cpp index 5237f4770..8ed877c71 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -593,6 +593,11 @@ Entity::~Entity() players[i]->entity = nullptr; //TODO: PLAYERSWAP VERIFY. Should this do anything to the player itself? players[i]->cleanUpOnEntityRemoval(); } + if ( this == players[i]->ghost.my ) + { + players[i]->ghost.my = nullptr; + players[i]->ghost.reset(); + } } // destroy my children list_FreeAll(&this->children); diff --git a/src/game.cpp b/src/game.cpp index 7eda87992..34909f0c1 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1689,6 +1689,7 @@ void gameLogic(void) if ( gameloopFreezeEntities && entity->behavior != &actPlayer && entity->behavior != &actPlayerLimb + && entity->behavior != &actDeathGhost && entity->behavior != &actHudWeapon && entity->behavior != &actHudShield && entity->behavior != &actHudAdditional @@ -1849,6 +1850,7 @@ void gameLogic(void) players[i]->hud.weapon = nullptr; players[i]->hud.magicLeftHand = nullptr; players[i]->hud.magicRightHand = nullptr; + players[i]->ghost.reset(); FollowerMenu[i].recentEntity = nullptr; FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; @@ -2999,6 +3001,7 @@ void gameLogic(void) { if ( gameloopFreezeEntities && entity->behavior != &actPlayer + && entity->behavior != &actDeathGhost && entity->behavior != &actPlayerLimb && entity->behavior != &actHudWeapon && entity->behavior != &actHudShield @@ -3048,7 +3051,8 @@ void gameLogic(void) double ox = 0, oy = 0, onewx = 0, onewy = 0; // move the bodyparts of these otherwise the limbs will get left behind in this adjustment. - if ( entity->behavior == &actPlayer || entity->behavior == &actMonster ) + if ( entity->behavior == &actPlayer || entity->behavior == &actMonster + || entity->behavior == &actDeathGhost ) { ox = entity->x; oy = entity->y; @@ -3064,7 +3068,8 @@ void gameLogic(void) } // move the bodyparts of these otherwise the limbs will get left behind in this adjustment. - if ( entity->behavior == &actPlayer || entity->behavior == &actMonster ) + if ( entity->behavior == &actPlayer || entity->behavior == &actMonster + || entity->behavior == &actDeathGhost ) { for ( Entity *bodypart : entity->bodyparts ) { @@ -3080,7 +3085,9 @@ void gameLogic(void) if ( fabs(entity->vel_x) > 0.0001 || fabs(entity->vel_y) > 0.0001 ) { double ox = 0, oy = 0, onewx = 0, onewy = 0; - if ( entity->behavior == &actPlayer || entity->behavior == &actMonster ) + if ( entity->behavior == &actPlayer + || entity->behavior == &actMonster + || entity->behavior == &actDeathGhost ) { ox = entity->x; oy = entity->y; @@ -3089,7 +3096,8 @@ void gameLogic(void) } real_t dist = clipMove(&entity->x, &entity->y, entity->vel_x, entity->vel_y, entity); real_t new_dist = clipMove(&entity->new_x, &entity->new_y, entity->vel_x, entity->vel_y, entity); - if ( entity->behavior == &actPlayer || entity->behavior == &actMonster ) + if ( entity->behavior == &actPlayer || entity->behavior == &actMonster + || entity->behavior == &actDeathGhost ) { for (Entity *bodypart : entity->bodyparts) { @@ -5911,6 +5919,11 @@ void drawAllPlayerCameras() { players[c]->movement.handlePlayerMovement(true); players[c]->movement.handlePlayerCameraUpdate(true); players[c]->movement.handlePlayerCameraPosition(true); + + players[c]->ghost.handleGhostCameraBobbing(true); + players[c]->ghost.handleGhostMovement(true); + players[c]->ghost.handleGhostCameraUpdate(true); + players[c]->ghost.handleGhostCameraPosition(true); //messagePlayer(0, "%3.2f | %3.2f", players[c]->entity->yaw, oldYaw); } } @@ -6725,6 +6738,7 @@ int main(int argc, char** argv) players[i]->hud.weapon = nullptr; players[i]->hud.magicLeftHand = nullptr; players[i]->hud.magicRightHand = nullptr; + players[i]->ghost.reset(); FollowerMenu[i].recentEntity = nullptr; FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; diff --git a/src/game.hpp b/src/game.hpp index d8adf7100..d141d5e30 100644 --- a/src/game.hpp +++ b/src/game.hpp @@ -228,6 +228,7 @@ void actStatueAnimator(Entity* my); void actStatue(Entity* my); void actDoorFrame(Entity* my); void actDeathCam(Entity* my); +void actDeathGhost(Entity* my); void actPlayerLimb(Entity* my); void actTorch(Entity* my); void actCrystalShard(Entity* my); diff --git a/src/interface/drawminimap.cpp b/src/interface/drawminimap.cpp index ce450298b..76e67d827 100644 --- a/src/interface/drawminimap.cpp +++ b/src/interface/drawminimap.cpp @@ -767,7 +767,7 @@ void drawMinimap(const int player, SDL_Rect rect, bool drawingSharedMap) int targetPlayer = -1; for ( int j = 0; j < MAXPLAYERS; ++j ) { - if ( callout.second.entityUid == achievementObserver.playerUids[j] ) + if ( CalloutRadialMenu::uidMatchesPlayer(j, callout.second.entityUid) ) { selfCallout = true; targetPlayer = j; diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index b8d664338..b641c7e85 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -22283,17 +22283,20 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName std::string key = "default"; Entity* entity = uidToEntity(lockOnEntityUid); const int player = getPlayer(); + + Entity* playerEntity = Player::getPlayerInteractEntity(player); + if ( players[player]->isLocalPlayer() ) { if ( lockOnEntityUid == 0 ) { if ( cmd == CALLOUT_CMD_AFFIRMATIVE || cmd == CALLOUT_CMD_NEGATIVE ) { - entity = players[player]->entity; + entity = playerEntity; } else if ( cmd == CALLOUT_CMD_HELP ) { - entity = players[player]->entity; + entity = playerEntity; } } @@ -22302,9 +22305,11 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName || cmd == CALLOUT_CMD_SOUTHEAST ) { int toPlayer = getPlayerForDirectPlayerCmd(getPlayer(), cmd); - if ( toPlayer >= 0 && players[toPlayer] && players[toPlayer]->entity && !client_disconnected[toPlayer] ) + Entity* toPlayerEntity = Player::getPlayerInteractEntity(toPlayer); + if ( toPlayer >= 0 && toPlayer < MAXPLAYERS + && toPlayerEntity && !client_disconnected[toPlayer] ) { - entity = players[toPlayer]->entity; + entity = toPlayerEntity; } } } @@ -22333,10 +22338,21 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName int toPlayer = getPlayerForDirectPlayerCmd(getPlayer(), cmd); key = "player_wave"; - if ( toPlayer < 0 || client_disconnected[toPlayer] || (players[toPlayer] && !players[toPlayer]->entity) ) + + + if ( toPlayer < 0 || toPlayer >= MAXPLAYERS + || client_disconnected[toPlayer] ) { key = "unavailable"; } + else + { + Entity* toPlayerEntity = Player::getPlayerInteractEntity(toPlayer); + if ( !toPlayerEntity ) + { + key = "unavailable"; + } + } if ( setType == SET_CALLOUT_ICON_KEY ) { @@ -22402,8 +22418,8 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName key = "location"; break; case CALLOUT_TYPE_PLAYER: - if ( cmd == CALLOUT_CMD_HELP && entity && entity->behavior == &actPlayer - && player >= 0 && entity == players[player]->entity ) + if ( cmd == CALLOUT_CMD_HELP && entity && (entity->behavior == &actPlayer || entity->behavior == &actDeathGhost) + && player >= 0 && player < MAXPLAYERS && entity == playerEntity ) { if ( players[player]->isLocalPlayer() ) { @@ -22608,7 +22624,7 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName else if ( cmd == CALLOUT_CMD_LOOK ) { std::string targetPlayerName = ""; - if ( entity && entity->behavior == &actPlayer ) + if ( entity && (entity->behavior == &actPlayer || entity->behavior == &actDeathGhost) ) { char shortname[32]; stringCopy(shortname, stats[entity->skill[2]]->name, sizeof(shortname), 22); @@ -23444,7 +23460,7 @@ CalloutRadialMenu::CalloutType CalloutRadialMenu::getCalloutTypeForEntity(const } CalloutType type = CALLOUT_TYPE_GENERIC_INTERACTABLE; - if ( parent->behavior == &actPlayer ) + if ( parent->behavior == &actPlayer || parent->behavior == &actDeathGhost ) { type = CALLOUT_TYPE_PLAYER; } @@ -23648,6 +23664,32 @@ CalloutRadialMenu::CalloutType CalloutRadialMenu::getCalloutTypeForUid(const int return CalloutRadialMenu::getCalloutTypeForEntity(player, parent); } +bool CalloutRadialMenu::uidMatchesPlayer(const int playernum, const Uint32 uid) +{ + if ( uid == 0 ) { return false; } + if ( achievementObserver.playerUids[playernum] == uid ) + { + return true; + } + else if ( players[playernum]->ghost.uid == uid ) + { + return true; + } + return false; +} + +Uint32 CalloutRadialMenu::getPlayerUid(const int playernum) +{ + if ( players[playernum]->ghost.isActive() ) + { + return players[playernum]->ghost.uid; + } + else + { + return achievementObserver.playerUids[playernum]; + } +} + void CalloutRadialMenu::CalloutParticle_t::init(const int player) { creationTick = ::ticks; @@ -23655,7 +23697,7 @@ void CalloutRadialMenu::CalloutParticle_t::init(const int player) lifetime = kParticleLifetime; for ( int i = 0; i < MAXPLAYERS; ++i ) { - if ( i == player && entityUid == achievementObserver.playerUids[player] && players[i]->isLocalPlayer() ) + if ( i == player && uidMatchesPlayer(player, entityUid) && players[i]->isLocalPlayer() ) { lockOnScreen[i] = true; } @@ -23761,7 +23803,7 @@ void CalloutRadialMenu::drawCallouts(const int playernum) for ( auto& callout : CalloutMenu[i].callouts ) { bool selfCallout = false; - if ( callout.second.entityUid == achievementObserver.playerUids[playernum] ) + if ( uidMatchesPlayer(playernum, callout.second.entityUid) ) { if ( i == playernum && players[i]->entity && players[i]->entity->skill[3] == 1 ) { @@ -23937,7 +23979,7 @@ void CalloutRadialMenu::drawCallouts(const int playernum) { for ( int i = 0; i < MAXPLAYERS; ++i ) { - if ( callout.second.entityUid == achievementObserver.playerUids[i] ) + if ( uidMatchesPlayer(i, callout.second.entityUid) ) { if ( callout.second.cmd == CALLOUT_CMD_AFFIRMATIVE || callout.second.cmd == CALLOUT_CMD_NEGATIVE @@ -23962,9 +24004,11 @@ void CalloutRadialMenu::drawCallouts(const int playernum) SDL_Rect iconPos = dest; bool drawMini = false; - if ( players[playernum]->entity && players[playernum]->entity->bodyparts.size() > 0 ) + + Entity* playerEntity = Player::getPlayerInteractEntity(playernum); + if ( playerEntity && playerEntity->bodyparts.size() > 0 ) { - auto bodypart = players[playernum]->entity->bodyparts[0]; + auto bodypart = playerEntity->bodyparts[0]; real_t tempx = bodypart->x; real_t tempy = bodypart->y; bodypart->x = callout.second.x; @@ -23972,9 +24016,16 @@ void CalloutRadialMenu::drawCallouts(const int playernum) real_t tangent = atan2(camera->y * 16.0 - bodypart->y, camera->x * 16.0 - bodypart->x); Entity* ohitentity = hit.entity; - lineTraceTarget(bodypart, bodypart->x, bodypart->y, tangent, 256, 0, false, players[playernum]->entity); - if ( hit.entity != players[playernum]->entity ) + bool oldPassable = playerEntity->flags[PASSABLE]; + if ( playerEntity->behavior == &actDeathGhost ) + { + playerEntity->flags[PASSABLE] = false; // hack to make ghosts linetraceable + } + lineTraceTarget(bodypart, bodypart->x, bodypart->y, tangent, 256, 0, false, playerEntity); + playerEntity->flags[PASSABLE] = oldPassable; + + if ( hit.entity != playerEntity ) { // no line of sight through walls drawMini = true; @@ -24687,7 +24738,7 @@ void CalloutRadialMenu::drawCalloutMenu() || optionSelected == CALLOUT_CMD_SOUTHWEST ) { int targetPlayer = getPlayerForDirectPlayerCmd(getPlayer(), (CalloutCommand)optionSelected); - if ( targetPlayer < 0 || client_disconnected[targetPlayer] || (players[targetPlayer] && !players[targetPlayer]->entity) ) + if ( targetPlayer < 0 || client_disconnected[targetPlayer] || (!Player::getPlayerInteractEntity(targetPlayer)) ) { disableOption = true; } @@ -24798,11 +24849,11 @@ void CalloutRadialMenu::drawCalloutMenu() if ( (CalloutCommand)optionSelected == CALLOUT_CMD_AFFIRMATIVE || (CalloutCommand)optionSelected == CALLOUT_CMD_NEGATIVE ) { - lockOnEntityUid = achievementObserver.playerUids[gui_player]; + lockOnEntityUid = getPlayerUid(gui_player); } else if ( (CalloutCommand)optionSelected == CALLOUT_CMD_HELP ) { - lockOnEntityUid = achievementObserver.playerUids[gui_player]; + lockOnEntityUid = getPlayerUid(gui_player); } } @@ -24813,7 +24864,7 @@ void CalloutRadialMenu::drawCalloutMenu() int toPlayer = getPlayerForDirectPlayerCmd(getPlayer(), (CalloutCommand)optionSelected); if ( toPlayer >= 0 ) { - lockOnEntityUid = achievementObserver.playerUids[toPlayer]; + lockOnEntityUid = getPlayerUid(toPlayer); } } @@ -24821,18 +24872,19 @@ void CalloutRadialMenu::drawCalloutMenu() { if ( Entity* target = uidToEntity(lockOnEntityUid) ) { - if ( target->behavior == &actPlayer && target->skill[2] != getPlayer() ) + if ( (target->behavior == &actPlayer || target->behavior == &actDeathGhost) + && target->skill[2] != getPlayer() ) { if ( (CalloutCommand)optionSelected == CALLOUT_CMD_HELP ) { - lockOnEntityUid = achievementObserver.playerUids[getPlayer()]; + lockOnEntityUid = getPlayerUid(getPlayer()); target = uidToEntity(lockOnEntityUid); } else if ( (CalloutCommand)optionSelected == CALLOUT_CMD_LOOK || (CalloutCommand)optionSelected == CALLOUT_CMD_AFFIRMATIVE || (CalloutCommand)optionSelected == CALLOUT_CMD_NEGATIVE ) { - target = players[getPlayer()]->entity; + target = Player::getPlayerInteractEntity(getPlayer()); } else if ( (CalloutCommand)optionSelected == CALLOUT_CMD_SOUTH || (CalloutCommand)optionSelected == CALLOUT_CMD_SOUTHWEST @@ -24841,7 +24893,7 @@ void CalloutRadialMenu::drawCalloutMenu() int toPlayer = getPlayerForDirectPlayerCmd(getPlayer(), (CalloutCommand)optionSelected); if ( toPlayer >= 0 ) { - target = players[getPlayer()]->entity; + target = Player::getPlayerInteractEntity(getPlayer()); } } } @@ -25158,7 +25210,7 @@ void CalloutRadialMenu::drawCalloutMenu() else if ( i == CALLOUT_CMD_SOUTH ) { int targetPlayer = getPlayerForDirectPlayerCmd(getPlayer(), (CalloutCommand)i); - if ( targetPlayer < 0 || client_disconnected[targetPlayer] || (players[targetPlayer] && !players[targetPlayer]->entity) ) + if ( targetPlayer < 0 || client_disconnected[targetPlayer] || !Player::getPlayerInteractEntity(targetPlayer) ) { lockedOption = true; } @@ -25197,7 +25249,7 @@ void CalloutRadialMenu::drawCalloutMenu() else if ( i == CALLOUT_CMD_SOUTHWEST ) { int targetPlayer = getPlayerForDirectPlayerCmd(getPlayer(), (CalloutCommand)i); - if ( targetPlayer < 0 || client_disconnected[targetPlayer] || (players[targetPlayer] && !players[targetPlayer]->entity) ) + if ( targetPlayer < 0 || client_disconnected[targetPlayer] || !Player::getPlayerInteractEntity(targetPlayer) ) { lockedOption = true; } @@ -25236,7 +25288,7 @@ void CalloutRadialMenu::drawCalloutMenu() else if ( i == CALLOUT_CMD_SOUTHEAST ) { int targetPlayer = getPlayerForDirectPlayerCmd(getPlayer(), (CalloutCommand)i); - if ( targetPlayer < 0 || client_disconnected[targetPlayer] || (players[targetPlayer] && !players[targetPlayer]->entity) ) + if ( targetPlayer < 0 || client_disconnected[targetPlayer] || !Player::getPlayerInteractEntity(targetPlayer) ) { lockedOption = true; } @@ -25329,7 +25381,7 @@ void CalloutRadialMenu::drawCalloutMenu() || highlight == CALLOUT_CMD_SOUTHWEST ) { int targetPlayer = getPlayerForDirectPlayerCmd(getPlayer(), (CalloutCommand)highlight); - if ( targetPlayer < 0 || client_disconnected[targetPlayer] || (players[targetPlayer] && !players[targetPlayer]->entity) ) + if ( targetPlayer < 0 || client_disconnected[targetPlayer] || !Player::getPlayerInteractEntity(targetPlayer) ) { disableOption = true; } @@ -25643,7 +25695,7 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat return false; } - if ( !players[gui_player] || !players[gui_player]->entity ) + if ( !players[gui_player] || !Player::getPlayerInteractEntity(gui_player) ) { return false; } @@ -25895,7 +25947,7 @@ bool CalloutRadialMenu::allowedInteractEntity(Entity& selectedEntity, bool updat strcat(interactText, getMonsterLocalizedName((Monster)monsterType).c_str()); } } - else if ( selectedEntity.behavior == &actPlayer ) + else if ( selectedEntity.behavior == &actPlayer || selectedEntity.behavior == &actDeathGhost ) { if ( updateInteractText ) { diff --git a/src/interface/interface.hpp b/src/interface/interface.hpp index e26998c3c..5f930a02c 100644 --- a/src/interface/interface.hpp +++ b/src/interface/interface.hpp @@ -1584,6 +1584,8 @@ struct CalloutRadialMenu bool isTinkeringFollower(int type);*/ void setPlayer(const int p) { gui_player = p; } const int getPlayer() const { return gui_player; } + static bool uidMatchesPlayer(const int playernum, const Uint32 uid); + static Uint32 getPlayerUid(const int playernum); void update(); }; extern CalloutRadialMenu CalloutMenu[MAXPLAYERS]; diff --git a/src/menu.cpp b/src/menu.cpp index 5ee0af8e9..d854a2529 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -9697,6 +9697,7 @@ void doNewGame(bool makeHighscore) { players[i]->hud.weapon = nullptr; players[i]->hud.magicLeftHand = nullptr; players[i]->hud.magicRightHand = nullptr; + players[i]->ghost.reset(); FollowerMenu[i].recentEntity = nullptr; FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; @@ -9971,6 +9972,7 @@ void doNewGame(bool makeHighscore) { players[i]->hud.weapon = nullptr; players[i]->hud.magicLeftHand = nullptr; players[i]->hud.magicRightHand = nullptr; + players[i]->ghost.reset(); FollowerMenu[i].recentEntity = nullptr; FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; @@ -10701,6 +10703,7 @@ void doEndgame(bool saveHighscore) { players[i]->hud.weapon = nullptr; players[i]->hud.magicLeftHand = nullptr; players[i]->hud.magicRightHand = nullptr; + players[i]->ghost.reset(); FollowerMenu[i].recentEntity = nullptr; FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; diff --git a/src/net.cpp b/src/net.cpp index 2fc6d28d4..595be4988 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1869,6 +1869,35 @@ void clientActions(Entity* entity) case 130: entity->behavior = &actGoldBag; break; + case 1233: + case 1234: + case 1235: + case 1236: + case 1237: + // player ghosts + playernum = SDLNet_Read32(&net_packet->data[30]); + if ( playernum >= 0 && playernum < MAXPLAYERS ) + { + if ( players[playernum] ) + { + players[playernum]->ghost.my = entity; + } + entity->skill[2] = playernum; + entity->behavior = &actDeathGhost; + if ( playernum == clientnum && multiplayer == CLIENT ) + { + entity->flags[UPDATENEEDED] = false; + } + else + { + entity->flags[UPDATENEEDED] = true; + } + entity->flags[PASSABLE] = true; + entity->flags[INVISIBLE] = false;// true; + entity->sizex = 4; + entity->sizey = 4; + } + break; default: if ( entity->isPlayerHeadSprite() ) { @@ -1980,6 +2009,7 @@ static void changeLevel() { players[i]->hud.weapon = nullptr; players[i]->hud.magicLeftHand = nullptr; players[i]->hud.magicRightHand = nullptr; + players[i]->ghost.reset(); FollowerMenu[i].recentEntity = nullptr; FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; @@ -2274,6 +2304,10 @@ static std::unordered_map clientPacketHandlers = { { // don't update my player } + else if ( entity->behavior == &actDeathGhost && entity->skill[2] == clientnum ) + { + // don't update my ghost + } else if ( entity->flags[NOUPDATE] ) { // inform the server that it tried to update a no-update entity @@ -2469,6 +2503,16 @@ static std::unordered_map clientPacketHandlers = { players[clientnum]->entity->y = ((Sint16)SDLNet_Read16(&net_packet->data[6])) / 32.0; }}, + // player ghost movement correction + {'GMOV', []() { + if ( players[clientnum] == nullptr || players[clientnum]->ghost.my == nullptr ) + { + return; + } + players[clientnum]->ghost.my->x = ((Sint16)SDLNet_Read16(&net_packet->data[4])) / 32.0; + players[clientnum]->ghost.my->y = ((Sint16)SDLNet_Read16(&net_packet->data[6])) / 32.0; + }}, + // update health {'UPHP', [](){ if ( (Monster)SDLNet_Read32(&net_packet->data[8]) != NOTHING ) @@ -2936,6 +2980,11 @@ static std::unordered_map clientPacketHandlers = { players[j]->entity = nullptr; players[j]->cleanUpOnEntityRemoval(); } + else if ( entity == players[j]->ghost.my ) + { + players[j]->ghost.my = nullptr; + players[j]->ghost.reset(); + } } if ( entity->light ) { @@ -5127,6 +5176,121 @@ static std::unordered_map serverPacketHandlers = { } }}, + // player ghost move + {'GMOV', []() { + const int player = net_packet->data[4]; + if ( player < 0 || player >= MAXPLAYERS ) + { + return; + } + client_keepalive[player] = ticks; + if ( players[player] == nullptr || players[player]->ghost.my == nullptr ) + { + return; + } + + // check if the info is outdated + if ( net_packet->data[5] != currentlevel || net_packet->data[18] != secretlevel ) + { + return; + } + + // get info from client + auto dx = ((Sint16)SDLNet_Read16(&net_packet->data[6])) / 32.0; + auto dy = ((Sint16)SDLNet_Read16(&net_packet->data[8])) / 32.0; + auto velx = ((Sint16)SDLNet_Read16(&net_packet->data[10])) / 128.0; + auto vely = ((Sint16)SDLNet_Read16(&net_packet->data[12])) / 128.0; + auto yaw = ((Sint16)SDLNet_Read16(&net_packet->data[14])) / 128.0; + auto pitch = ((Sint16)SDLNet_Read16(&net_packet->data[16])) / 128.0; + + // update rotation + players[player]->ghost.my->yaw = yaw; + players[player]->ghost.my->pitch = pitch; + + // update player's internal velocity variables + players[player]->ghost.my->vel_x = velx; // PLAYER_VELX + players[player]->ghost.my->vel_y = vely; // PLAYER_VELY + + // store old coordinates + // since this function runs more often than actPlayer runs, we need to keep track of the accumulated position in new_x/new_y + real_t ox = players[player]->ghost.my->x; + real_t oy = players[player]->ghost.my->y; + players[player]->ghost.my->x = players[player]->ghost.my->new_x; + players[player]->ghost.my->y = players[player]->ghost.my->new_y; + + // calculate distance + dx -= players[player]->ghost.my->x; + dy -= players[player]->ghost.my->y; + auto dist = sqrt(dx * dx + dy * dy); + + // move player with collision detection + real_t result = clipMove(&players[player]->ghost.my->x, &players[player]->ghost.my->y, dx, dy, players[player]->ghost.my); + if ( result < dist - .025 ) + { + // player encountered obstacle on path + // stop updating position on server side and send client corrected position + const int j = net_packet->data[4]; + if ( j > 0 && j < MAXPLAYERS ) + { + strcpy((char*)net_packet->data, "GMOV"); + SDLNet_Write16((Sint16)(players[j]->ghost.my->x * 32), &net_packet->data[4]); + SDLNet_Write16((Sint16)(players[j]->ghost.my->y * 32), &net_packet->data[6]); + net_packet->address.host = net_clients[j - 1].host; + net_packet->address.port = net_clients[j - 1].port; + net_packet->len = 8; + sendPacket(net_sock, -1, net_packet, j - 1); + } + } + + // clipMove sent any corrections to the client, now let's save the updated coordinates. + players[player]->ghost.my->new_x = players[player]->ghost.my->x; + players[player]->ghost.my->new_y = players[player]->ghost.my->y; + // return x/y to their original state as this can update more than actPlayer and causes stuttering. use new_x/new_y in actPlayer. + players[player]->ghost.my->x = ox; + players[player]->ghost.my->y = oy; + }}, + + // player created ghost + {'GHOS', []() { + // check if the info is outdated + if ( net_packet->data[5] != currentlevel || net_packet->data[10] != secretlevel ) + { + return; + } + + int player = net_packet->data[4]; + + if ( player < 0 || player >= MAXPLAYERS ) + { + return; + } + + if ( players[player]->ghost.my ) + { + list_RemoveNode(players[player]->ghost.my->mynode); + players[player]->ghost.my = nullptr; + } + players[player]->ghost.reset(); + + // deathcam + int sprite = 1233 + (player < 4 ? player : 4); + Entity* entity = newEntity(sprite, 1, map.entities, nullptr); //Ghost entity. + players[player]->ghost.my = entity; + players[player]->ghost.uid = entity->getUID(); + entity->x = SDLNet_Read16(&net_packet->data[6]); + entity->y = SDLNet_Read16(&net_packet->data[8]); + entity->z = -4; + entity->flags[PASSABLE] = true; + entity->flags[INVISIBLE] = false;// true; + entity->behavior = &actDeathGhost; + entity->skill[2] = player; + entity->yaw = 0.0; + entity->pitch = 0; + entity->sizex = 4; + entity->sizey = 4; + entity->flags[UPDATENEEDED] = true; + }}, + // tried to update {'NOUP', [](){ Uint32 uid = SDLNet_Read32(&net_packet->data[5]); diff --git a/src/player.cpp b/src/player.cpp index 0cb92bcfb..4be822d31 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -3012,6 +3012,7 @@ Player::Player(int in_playernum, bool in_local_host) : characterSheet(*this), skillSheet(*this), movement(*this), + ghost(*this), messageZone(*this), worldUI(*this), hotbar(*this), @@ -3071,6 +3072,15 @@ const bool Player::isLocalPlayerAlive() const return (isLocalPlayer() && entity && !client_disconnected[playernum]); } +Entity* Player::getPlayerInteractEntity(const int playernum) +{ + if ( playernum < 0 || playernum >= MAXPLAYERS ) + { + return nullptr; + } + return players[playernum]->ghost.isActive() ? players[playernum]->ghost.my : players[playernum]->entity; +} + void Player::PlayerMovement_t::reset() { quickTurnRotation = 0.0; @@ -3098,6 +3108,10 @@ void Player::WorldUI_t::reset() bool monsterIsFriendlyForTooltip(const int player, Entity& entity) { + if ( !players[player]->entity ) + { + return false; + } if ( multiplayer != CLIENT ) { if ( !entity.checkEnemy(players[player]->entity) ) @@ -3179,12 +3193,18 @@ bool monsterIsFriendlyForTooltip(const int player, Entity& entity) real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) { - if ( !players[player.playernum]->isLocalPlayerAlive() ) + if ( !players[player.playernum]->isLocalPlayerAlive() && !player.ghost.isActive() ) { return 0.0; } - real_t dist = entityDist(&tooltip, players[player.playernum]->entity); + Entity* playerEntity = Player::getPlayerInteractEntity(player.playernum); + if ( !playerEntity ) + { + return 0.0; + } + + real_t dist = entityDist(&tooltip, playerEntity); Entity* parent = uidToEntity(tooltip.parent); real_t maxDist = 24.0; @@ -3219,7 +3239,7 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) if ( dist < maxDist && dist > minDist ) { - real_t tangent = atan2(tooltip.y - players[player.playernum]->entity->y, tooltip.x - players[player.playernum]->entity->x); + real_t tangent = atan2(tooltip.y - playerEntity->y, tooltip.x - playerEntity->x); while ( tangent >= 2 * PI ) { tangent -= 2 * PI; @@ -3228,7 +3248,7 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) { tangent += 2 * PI; } - real_t playerYaw = players[player.playernum]->entity->yaw; + real_t playerYaw = playerEntity->yaw; while ( playerYaw >= 2 * PI ) { playerYaw -= 2 * PI; @@ -3265,9 +3285,15 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) } Entity* ohitentity = hit.entity; - real_t tangent2 = atan2(players[player.playernum]->entity->y - parent->y, players[player.playernum]->entity->x - parent->x); - lineTraceTarget(parent, parent->x, parent->y, tangent2, maxDist, 0, false, players[player.playernum]->entity); - if ( hit.entity != players[player.playernum]->entity ) + real_t tangent2 = atan2(playerEntity->y - parent->y, playerEntity->x - parent->x); + bool oldPassable = playerEntity->flags[PASSABLE]; + if ( playerEntity->behavior == &actDeathGhost ) + { + playerEntity->flags[PASSABLE] = false; // hack to make ghosts linetraceable + } + lineTraceTarget(parent, parent->x, parent->y, tangent2, maxDist, 0, false, playerEntity); + playerEntity->flags[PASSABLE] = oldPassable; + if ( hit.entity != playerEntity ) { // no line of sight through walls hit.entity = ohitentity; @@ -3390,9 +3416,15 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) if ( !selectInteract ) { Entity* ohitentity = hit.entity; - real_t tangent2 = atan2(players[player.playernum]->entity->y - tooltip.y, players[player.playernum]->entity->x - tooltip.x); - lineTraceTarget(&tooltip, tooltip.x, tooltip.y, tangent2, maxDist, 0, false, players[player.playernum]->entity); - if ( hit.entity != players[player.playernum]->entity ) + real_t tangent2 = atan2(playerEntity->y - tooltip.y, playerEntity->x - tooltip.x); + bool oldPassable = playerEntity->flags[PASSABLE]; + if ( playerEntity->behavior == &actDeathGhost ) + { + playerEntity->flags[PASSABLE] = false; // hack to make ghosts linetraceable + } + lineTraceTarget(&tooltip, tooltip.x, tooltip.y, tangent2, maxDist, 0, false, playerEntity); + playerEntity->flags[PASSABLE] = oldPassable; + if ( hit.entity != playerEntity ) { // no line of sight through walls hit.entity = ohitentity; @@ -3408,10 +3440,10 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) if ( false ) { // old method, not entirely accurate - real_t startx = players[player.playernum]->entity->x; - real_t starty = players[player.playernum]->entity->y; + real_t startx = playerEntity->x; + real_t starty = playerEntity->y; real_t startz = -4; - real_t pitch = players[player.playernum]->entity->pitch; + real_t pitch = playerEntity->pitch; if ( pitch < 0 ) { //pitch = 0; - unneeded - negative pitch looks in a cone upwards as well - good check @@ -3423,9 +3455,9 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) int index = 0; for ( ; startz < 0.f; startz += abs(0.25 * tan(pitch)) ) { - startx += 0.5 * cos(players[player.playernum]->entity->yaw); - starty += 0.5 * sin(players[player.playernum]->entity->yaw); - index = (static_cast(starty + 16 * sin(players[player.playernum]->entity->yaw)) >> 4) * MAPLAYERS + (static_cast(startx + 16 * cos(players[player.playernum]->entity->yaw)) >> 4) * MAPLAYERS * map.height; + startx += 0.5 * cos(playerEntity->yaw); + starty += 0.5 * sin(playerEntity->yaw); + index = (static_cast(starty + 16 * sin(playerEntity->yaw)) >> 4) * MAPLAYERS + (static_cast(startx + 16 * cos(playerEntity->yaw)) >> 4) * MAPLAYERS * map.height; if ( !map.tiles[OBSTACLELAYER + index] ) { // store the last known good coordinate @@ -3437,7 +3469,7 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) break; } } - real_t lookDist = sqrt(pow(previousx - players[player.playernum]->entity->x, 2) + pow(previousy - players[player.playernum]->entity->y, 2)); + real_t lookDist = sqrt(pow(previousx - playerEntity->x, 2) + pow(previousy - playerEntity->y, 2)); if ( lookDist < dist ) { if ( abs(dist - lookDist) > 8.0 ) @@ -3446,7 +3478,7 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) } } - /*Entity* particle = spawnMagicParticle(players[player.playernum]->entity); + /*Entity* particle = spawnMagicParticle(playerEntity); particle->sprite = 576; particle->x = previousx; particle->y = previousy; @@ -3498,14 +3530,14 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) static ConsoleVariable cvar_calloutboulderdebug("/calloutboulderdebug", false); if ( *cvar_calloutboulderdebug ) { - Entity* particle = spawnMagicParticle(players[player.playernum]->entity); + Entity* particle = spawnMagicParticle(playerEntity); particle->sprite = 942; particle->x = previousx; particle->y = previousy; particle->z = startz; } - real_t lookDist = sqrt(pow(previousx - players[player.playernum]->entity->x, 2) + pow(previousy - players[player.playernum]->entity->y, 2)); + real_t lookDist = sqrt(pow(previousx - playerEntity->x, 2) + pow(previousy - playerEntity->y, 2)); if ( abs(dist - lookDist) > 8.25 ) { return 0.0; // looking at a tile on the ground more than x units away from the tooltip @@ -3551,7 +3583,7 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) } } - real_t lookDist = sqrt(pow(previousx - players[player.playernum]->entity->x, 2) + pow(previousy - players[player.playernum]->entity->y, 2)); + real_t lookDist = sqrt(pow(previousx - playerEntity->x, 2) + pow(previousy - playerEntity->y, 2)); if ( callout ) { if ( parent ) @@ -3596,7 +3628,7 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) } } - /*Entity* particle = spawnMagicParticle(players[player.playernum]->entity); + /*Entity* particle = spawnMagicParticle(playerEntity); particle->sprite = 942; particle->x = previousx; particle->y = previousy; @@ -4106,7 +4138,7 @@ void Player::WorldUI_t::handleTooltips() for ( int player = 0; player < MAXPLAYERS && !gamePaused; ++player ) { players[player]->worldUI.worldTooltipDialogue.update(); - if ( !players[player]->isLocalPlayerAlive() ) + if ( !players[player]->isLocalPlayerAlive() && !players[player]->ghost.isActive() ) { players[player]->worldUI.reset(); players[player]->worldUI.tooltipView = Player::WorldUI_t::TooltipView::TOOLTIP_VIEW_RESCAN; @@ -4119,6 +4151,8 @@ void Player::WorldUI_t::handleTooltips() continue; } + Entity* playerEntity = Player::getPlayerInteractEntity(player); + #ifdef NINTENDO players[player]->worldUI.bEnabled = true; #else @@ -4226,8 +4260,8 @@ void Player::WorldUI_t::handleTooltips() if ( !bDoingActionHideTooltips ) { Entity* ohitentity = hit.entity; - lineTrace(players[player]->entity, players[player]->entity->x, players[player]->entity->y, - players[player]->entity->yaw, STRIKERANGE, 0, true); + lineTrace(playerEntity, playerEntity->x, playerEntity->y, + playerEntity->yaw, STRIKERANGE, 0, true); if ( hit.entity ) { if ( hit.entity->behavior == &actMonster && selectInteract ) @@ -4336,7 +4370,7 @@ void Player::WorldUI_t::handleTooltips() } if ( closestTooltip ) { - players[player]->worldUI.playerLastYaw = players[player]->entity->yaw; + players[player]->worldUI.playerLastYaw = playerEntity->yaw; players[player]->worldUI.playerLastPitch = players[player]->camera().vang; while ( players[player]->worldUI.playerLastYaw >= 4 * PI ) { @@ -4380,7 +4414,7 @@ void Player::WorldUI_t::handleTooltips() continue; } - real_t currentYaw = players[player]->entity->yaw; + real_t currentYaw = playerEntity->yaw; while ( currentYaw >= 4 * PI ) { currentYaw -= 2 * PI; diff --git a/src/player.hpp b/src/player.hpp index f4953c474..2a7f2e4f4 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -696,6 +696,7 @@ class Player const bool bAlignGUINextToInventoryCompact() const; // if chest/shop etc appears alongside inventory as opposed to opposite of viewport in compact view const bool usingCommand() const; void clearGUIPointers(); + static Entity* getPlayerInteractEntity(const int playernum); enum PanelJustify_t { @@ -1756,6 +1757,31 @@ class Player void reset(); } movement; + class Ghost_t + { + real_t quickTurnRotation = 0.0; + Uint32 quickTurnStartTicks = 0; + bool bDoingQuickTurn = false; + Player& player; + public: + Ghost_t(Player& p) : player(p) + {}; + ~Ghost_t() {}; + + Entity* my = nullptr; + Uint32 uid = 0; + + bool handleQuickTurn(bool useRefreshRateDelta); + void startQuickTurn(); + void handleGhostCameraUpdate(bool useRefreshRateDelta); + void handleGhostCameraBobbing(bool useRefreshRateDelta); + void handleGhostMovement(bool useRefreshRateDelta); + void handleGhostCameraPosition(bool useRefreshRateDelta); + void handleActions(); + bool isActive() { return my != nullptr; } + void reset(); + } ghost; + class MessageZone_t { //Time in seconds before the message starts fading. From 8cd87d09d30f5c487a1e3bfa0421b21dc0169c30 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 21 Sep 2023 07:29:25 +1000 Subject: [PATCH 030/146] * don't disable item tooltips when callout menu is open? --- src/opengl.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/opengl.cpp b/src/opengl.cpp index 259f71f65..7e781023c 100644 --- a/src/opengl.cpp +++ b/src/opengl.cpp @@ -1498,10 +1498,16 @@ void glDrawWorldUISprite(view_t* camera, Entity* entity, int mode) } } if (player >= 0 && player < MAXPLAYERS) { - if ( CalloutMenu[player].calloutMenuIsOpen() ) - { - return; - } + //if ( CalloutMenu[player].calloutMenuIsOpen() ) + //{ + // real_t dx, dy; + // dx = camera->x * 16.0 - entity->x; + // dy = camera->y * 16.0 - entity->y; + // if ( sqrt(dx * dx + dy * dy) > 24.0 ) + // { + // return; // too far, ignore drawing + // } + //} if (entity->worldTooltipPlayer != player) { return; } From 5e4519c462fbaed0e29857722a14ad4946396e4f Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 21 Sep 2023 22:12:06 +1000 Subject: [PATCH 031/146] * fix some ghost interactions, cycle interact not working --- src/actplayer.cpp | 19 +++++++++++++++++-- src/entity.cpp | 2 +- src/net.cpp | 4 ++-- src/player.cpp | 21 +++++++++++++++++---- src/player.hpp | 1 + 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index fdafc875a..3bebd6447 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -221,6 +221,21 @@ void Player::Ghost_t::reset() uid = 0; } +bool Player::Ghost_t::allowedInteractEntity(Entity& entity) +{ + if ( entity.behavior == &actItem + || entity.behavior == &actDoor + || entity.behavior == &actSwitch + || entity.behavior == &actSwitchWithTimer + || entity.behavior == &actPowerCrystal + || entity.behavior == &actPowerCrystalBase + || entity.behavior == &actTeleportShrine ) + { + return true; + } + return false; +} + void Player::Ghost_t::handleActions() { Input& input = Input::inputs[player.playernum]; @@ -1142,8 +1157,8 @@ void actDeathCam(Entity* my) net_packet->data[4] = DEATHCAM_PLAYERNUM; net_packet->data[5] = currentlevel; - int x = (my->x / 16) + 8; - int y = (my->y / 16) + 8; + int x = (my->x / 16); + int y = (my->y / 16); SDLNet_Write16((Sint16)(x), &net_packet->data[6]); SDLNet_Write16((Sint16)(y), &net_packet->data[8]); net_packet->data[10] = secretlevel; diff --git a/src/entity.cpp b/src/entity.cpp index 8ed877c71..3683c320a 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -19856,7 +19856,7 @@ void Entity::createWorldUITooltip() { for ( int i = 0; i < MAXPLAYERS; ++i ) { - if ( !players[i]->isLocalPlayerAlive() ) + if ( !players[i]->isLocalPlayer() ) { continue; } diff --git a/src/net.cpp b/src/net.cpp index 595be4988..707f6e9d7 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -5277,8 +5277,8 @@ static std::unordered_map serverPacketHandlers = { Entity* entity = newEntity(sprite, 1, map.entities, nullptr); //Ghost entity. players[player]->ghost.my = entity; players[player]->ghost.uid = entity->getUID(); - entity->x = SDLNet_Read16(&net_packet->data[6]); - entity->y = SDLNet_Read16(&net_packet->data[8]); + entity->x = (SDLNet_Read16(&net_packet->data[6]) * 16) + 8; + entity->y = (SDLNet_Read16(&net_packet->data[8]) * 16) + 8; entity->z = -4; entity->flags[PASSABLE] = true; entity->flags[INVISIBLE] = false;// true; diff --git a/src/player.cpp b/src/player.cpp index 4be822d31..7d36a325a 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -3310,7 +3310,7 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) return 0.0; } - if ( !selectInteract && stats[player.playernum] && stats[player.playernum]->defending ) + if ( !selectInteract && stats[player.playernum] && stats[player.playernum]->defending && player.entity ) { if ( stats[player.playernum]->shield && stats[player.playernum]->shield->type == TOOL_TINKERING_KIT ) { @@ -3668,6 +3668,14 @@ void Player::WorldUI_t::setTooltipActive(Entity& tooltip) return; } } + else if ( player.ghost.isActive() && !CalloutMenu[player.playernum].calloutMenuIsOpen() ) + { + if ( !player.ghost.allowedInteractEntity(*parent) ) + { + setTooltipDisabled(tooltip); + return; + } + } parent->highlightForUI = 1.0; if ( tooltip.worldTooltipRequiresButtonHeld == 1 && *MainMenu::cvar_hold_to_activate ) @@ -3680,7 +3688,7 @@ void Player::WorldUI_t::setTooltipActive(Entity& tooltip) } bool foundTinkeringKit = false; - if ( stats[player.playernum] && stats[player.playernum]->defending ) + if ( stats[player.playernum] && stats[player.playernum]->defending && player.entity ) { if ( stats[player.playernum]->shield && stats[player.playernum]->shield->type == TOOL_TINKERING_KIT ) { @@ -4123,6 +4131,10 @@ bool entityBlocksTooltipInteraction(const int player, Entity& entity) } else if ( entity.behavior == &actMonster ) { + if ( players[player]->ghost.isActive() ) + { + return false; + } if ( monsterIsFriendlyForTooltip(player, entity) ) { return false; @@ -4138,7 +4150,8 @@ void Player::WorldUI_t::handleTooltips() for ( int player = 0; player < MAXPLAYERS && !gamePaused; ++player ) { players[player]->worldUI.worldTooltipDialogue.update(); - if ( !players[player]->isLocalPlayerAlive() && !players[player]->ghost.isActive() ) + if ( !players[player]->isLocalPlayer() + || (!players[player]->isLocalPlayerAlive() && !players[player]->ghost.isActive()) ) { players[player]->worldUI.reset(); players[player]->worldUI.tooltipView = Player::WorldUI_t::TooltipView::TOOLTIP_VIEW_RESCAN; @@ -4240,7 +4253,7 @@ void Player::WorldUI_t::handleTooltips() // spells bDoingActionHideTooltips = true; } - else if ( stats[player] && stats[player]->defending ) + else if ( stats[player] && stats[player]->defending && players[player]->entity ) { if ( stats[player]->shield && stats[player]->shield->type == TOOL_TINKERING_KIT ) { diff --git a/src/player.hpp b/src/player.hpp index 2a7f2e4f4..e8052e3d5 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -1780,6 +1780,7 @@ class Player void handleActions(); bool isActive() { return my != nullptr; } void reset(); + bool allowedInteractEntity(Entity& entity); } ghost; class MessageZone_t From d7f35d8a6ac8e5af611987dcf43951b5945f18df Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Fri, 22 Sep 2023 00:29:41 +1000 Subject: [PATCH 032/146] * update qod banner --- src/ui/MainMenu.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index cc6beaf93..fe27806b7 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -22516,7 +22516,7 @@ namespace MainMenu { void(*banner_funcs[])(Button&) = { [](Button&){ // banner #1 if (enabledDLCPack1 && enabledDLCPack2) { - openURLTryWithOverlay("https://www.baronygame.com/blog/qod-update-launched"); + openURLTryWithOverlay("https://www.baronygame.com/blog/life-after-death-announcement"); } else { openDLCPrompt(enabledDLCPack1 ? 1 : 0); } @@ -22527,8 +22527,8 @@ namespace MainMenu { #else const char* banner_images[][2] = { { - "*#images/ui/Main Menus/Banners/UI_MainMenu_QoDPatchNotes1_base.png", - "*#images/ui/Main Menus/Banners/UI_MainMenu_QoDPatchNotes1_high.png", + "*#images/ui/Main Menus/Banners/banner_lifeafterdeath-preview.png", + "*#images/ui/Main Menus/Banners/banner_lifeafterdeath-preview_hover.png", }, { "*#images/ui/Main Menus/Banners/UI_MainMenu_ComboBanner1_base.png", @@ -22549,7 +22549,7 @@ namespace MainMenu { } void(*banner_funcs[])(Button&) = { [](Button&) { // banner #1 - openURLTryWithOverlay("https://www.baronygame.com/blog/qod-update-launched"); + openURLTryWithOverlay("https://www.baronygame.com/blog/life-after-death-announcement"); }, [](Button&) { // banner #2 openDLCPrompt(enabledDLCPack1 ? 1 : 0); From d7658397e895e5cae33cfdc994a2a9e0c28f5e2c Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sun, 24 Sep 2023 21:27:04 +1000 Subject: [PATCH 033/146] * more chat message sanitise % signs --- src/messages.cpp | 9 +++++++++ src/net.cpp | 2 ++ src/ui/GameUI.cpp | 7 ++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/messages.cpp b/src/messages.cpp index ac3da3893..215ccbbe1 100644 --- a/src/messages.cpp +++ b/src/messages.cpp @@ -279,6 +279,15 @@ std::string messageSanitizePercentSign(std::string src, int* percentSignsFound) { while ( found != std::string::npos ) { + if ( found + 1 < commandString.size() ) + { + if ( commandString.at(found + 1) == '%' ) + { + // this command has already been escaped, no need for more % + found = commandString.find('%', found + 2); + continue; + } + } commandString.insert(found, "%"); found = commandString.find('%', found + 2); if ( percentSignsFound ) diff --git a/src/net.cpp b/src/net.cpp index 707f6e9d7..bf34518e3 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -319,6 +319,8 @@ bool messagePlayer(int player, Uint32 type, char const * const message, ...) vsnprintf( str, Player::MessageZone_t::ADD_MESSAGE_BUFFER_LENGTH - 1, message, argptr ); va_end( argptr ); + strncpy(str, messageSanitizePercentSign(str, nullptr).c_str(), Player::MessageZone_t::ADD_MESSAGE_BUFFER_LENGTH - 1); + return messagePlayerColor(player, type, 0xFFFFFFFF, str); } diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 3a314c6b8..5c30bc41f 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -10112,7 +10112,12 @@ void Player::MessageZone_t::processChatbox() { entry->setDisabled(false); entry->setColor(color); - entry->setText(current->text->data); + + std::string data = messageSanitizePercentSign(current->text->data, nullptr); + char str[ADD_MESSAGE_BUFFER_LENGTH]; + snprintf(str, sizeof(str), data.c_str()); + + entry->setText(str); entry->setFont(useBigFont ? bigfont : smallfont); entry->setPaddingPerLine(useBigFont ? 0 : *cvar_log_multiline_pady); Font* fontGet = Font::get(entry->getFont()); From c21131aa5e7e16cb158aad39a8ebbfe536a2e83d Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sun, 24 Sep 2023 22:59:36 +1000 Subject: [PATCH 034/146] * fix ghost quickturn crash --- src/actplayer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index 3bebd6447..1014c540b 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -176,7 +176,6 @@ bool Player::Ghost_t::handleQuickTurn(bool useRefreshRateDelta) return false; } - Entity* my = players[player.playernum]->entity; double refreshRateDelta = 1.0; if ( useRefreshRateDelta && fps > 0.0 ) { From af149bc050fcaba76a30e1b84d7f0e20a18d24a3 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sun, 24 Sep 2023 23:00:03 +1000 Subject: [PATCH 035/146] * fix calloutwheel on gamepad --- src/interface/interface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index b641c7e85..04ce9da4f 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -1374,7 +1374,8 @@ void Player::openStatusScreen(const int whichGUIMode, const int whichInventoryMo if ( inputs.hasController(playernum) && ((oldgui == GUI_MODE_NONE && whichGUIMode != GUI_MODE_NONE) || (oldmodule != GUI.activeModule && GUI.activeModule == GUI_t::MODULE_INVENTORY)) - && !FollowerMenu[playernum].followerToCommand ) + && !FollowerMenu[playernum].followerToCommand + && !CalloutMenu[playernum].bOpen ) { warpMouseToInventorySlot = true; } From 73f12b0c0629e17113799f672eaaa3e6f9b2af7e Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Mon, 25 Sep 2023 00:53:31 +1000 Subject: [PATCH 036/146] * fix editor new map variables not being set correctly --- src/buttons.cpp | 97 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 32 deletions(-) diff --git a/src/buttons.cpp b/src/buttons.cpp index be6e6fb66..7555a2749 100644 --- a/src/buttons.cpp +++ b/src/buttons.cpp @@ -314,32 +314,20 @@ void buttonNew(button_t* my) snprintf(skyboxtext, 4, "%d", map.skybox); for ( int z = 0; z < MAPFLAGS; ++z ) { - snprintf(mapflagtext[z], 4, "%d", map.flags[z]); - } - if ( map.flags[MAP_FLAG_DISABLETRAPS] > 0 ) - { - strcpy(mapflagtext[MAP_FLAG_DISABLETRAPS], "[x]"); - } - else - { - strcpy(mapflagtext[MAP_FLAG_DISABLETRAPS], "[ ]"); - } - if ( map.flags[MAP_FLAG_DISABLEMONSTERS] > 0 ) - { - strcpy(mapflagtext[MAP_FLAG_DISABLEMONSTERS], "[x]"); - } - else - { - strcpy(mapflagtext[MAP_FLAG_DISABLEMONSTERS], "[ ]"); - } - if ( map.flags[MAP_FLAG_DISABLELOOT] > 0 ) - { - strcpy(mapflagtext[MAP_FLAG_DISABLELOOT], "[x]"); - } - else - { - strcpy(mapflagtext[MAP_FLAG_DISABLELOOT], "[ ]"); + if ( z < MAP_FLAG_GENBYTES1 || z > MAP_FLAG_GENBYTES6 ) + { + snprintf(mapflagtext[z], 4, "%d", map.flags[z]); + } } + snprintf(mapflagtext[MAP_FLAG_GENTOTALMIN], 4, "%d", (map.flags[MAP_FLAG_GENBYTES1] >> 24) & static_cast(0xFF)); + snprintf(mapflagtext[MAP_FLAG_GENTOTALMAX], 4, "%d", (map.flags[MAP_FLAG_GENBYTES1] >> 16) & static_cast(0xFF)); + snprintf(mapflagtext[MAP_FLAG_GENMONSTERMIN], 4, "%d", (map.flags[MAP_FLAG_GENBYTES1] >> 8) & static_cast(0xFF)); + snprintf(mapflagtext[MAP_FLAG_GENMONSTERMAX], 4, "%d", (map.flags[MAP_FLAG_GENBYTES1] >> 0) & static_cast(0xFF)); + snprintf(mapflagtext[MAP_FLAG_GENLOOTMIN], 4, "%d", (map.flags[MAP_FLAG_GENBYTES2] >> 24) & static_cast(0xFF)); + snprintf(mapflagtext[MAP_FLAG_GENLOOTMAX], 4, "%d", (map.flags[MAP_FLAG_GENBYTES2] >> 16) & static_cast(0xFF)); + snprintf(mapflagtext[MAP_FLAG_GENDECORATIONMIN], 4, "%d", (map.flags[MAP_FLAG_GENBYTES2] >> 8) & static_cast(0xFF)); + snprintf(mapflagtext[MAP_FLAG_GENDECORATIONMAX], 4, "%d", (map.flags[MAP_FLAG_GENBYTES2] >> 0) & static_cast(0xFF)); + snprintf(mapflagtext[MAP_FLAG_PERIMETER_GAP], 4, "%d", (map.flags[MAP_FLAG_GENBYTES4] >> 0) & static_cast(0xFF)); if ( (map.flags[MAP_FLAG_GENBYTES3] >> 24) & static_cast(0xFF) ) { strcpy(mapflagtext[MAP_FLAG_DISABLEDIGGING], "[x]"); @@ -348,6 +336,7 @@ void buttonNew(button_t* my) { strcpy(mapflagtext[MAP_FLAG_DISABLEDIGGING], "[ ]"); } + if ( (map.flags[MAP_FLAG_GENBYTES3] >> 16) & static_cast(0xFF) ) { strcpy(mapflagtext[MAP_FLAG_DISABLETELEPORT], "[x]"); @@ -356,6 +345,7 @@ void buttonNew(button_t* my) { strcpy(mapflagtext[MAP_FLAG_DISABLETELEPORT], "[ ]"); } + if ( (map.flags[MAP_FLAG_GENBYTES3] >> 8) & static_cast(0xFF) ) { strcpy(mapflagtext[MAP_FLAG_DISABLELEVITATION], "[x]"); @@ -364,6 +354,7 @@ void buttonNew(button_t* my) { strcpy(mapflagtext[MAP_FLAG_DISABLELEVITATION], "[ ]"); } + if ( (map.flags[MAP_FLAG_GENBYTES3] >> 0) & static_cast(0xFF) ) { strcpy(mapflagtext[MAP_FLAG_GENADJACENTROOMS], "[x]"); @@ -396,6 +387,31 @@ void buttonNew(button_t* my) { strcpy(mapflagtext[MAP_FLAG_DISABLEHUNGER], "[ ]"); } + + if ( map.flags[MAP_FLAG_DISABLETRAPS] > 0 ) + { + strcpy(mapflagtext[MAP_FLAG_DISABLETRAPS], "[x]"); + } + else + { + strcpy(mapflagtext[MAP_FLAG_DISABLETRAPS], "[ ]"); + } + if ( map.flags[MAP_FLAG_DISABLEMONSTERS] > 0 ) + { + strcpy(mapflagtext[MAP_FLAG_DISABLEMONSTERS], "[x]"); + } + else + { + strcpy(mapflagtext[MAP_FLAG_DISABLEMONSTERS], "[ ]"); + } + if ( map.flags[MAP_FLAG_DISABLELOOT] > 0 ) + { + strcpy(mapflagtext[MAP_FLAG_DISABLELOOT], "[x]"); + } + else + { + strcpy(mapflagtext[MAP_FLAG_DISABLELOOT], "[ ]"); + } cursorflash = ticks; menuVisible = 0; subwindow = 1; @@ -482,24 +498,40 @@ void buttonNewConfirm(button_t* my) map.flags[MAP_FLAG_DISABLELOOT] = 0; } } + else if ( z == MAP_FLAG_GENBYTES1 ) + { + map.flags[z] = 0; + map.flags[z] |= (atoi(mapflagtext[MAP_FLAG_GENTOTALMIN]) & 0xFF) << 24; + map.flags[z] |= (atoi(mapflagtext[MAP_FLAG_GENTOTALMAX]) & 0xFF) << 16; + map.flags[z] |= (atoi(mapflagtext[MAP_FLAG_GENMONSTERMIN]) & 0xFF) << 8; + map.flags[z] |= (atoi(mapflagtext[MAP_FLAG_GENMONSTERMAX]) & 0xFF) << 0; + } + else if ( z == MAP_FLAG_GENBYTES2 ) + { + map.flags[z] = 0; + map.flags[z] |= (atoi(mapflagtext[MAP_FLAG_GENLOOTMIN]) & 0xFF) << 24; + map.flags[z] |= (atoi(mapflagtext[MAP_FLAG_GENLOOTMAX]) & 0xFF) << 16; + map.flags[z] |= (atoi(mapflagtext[MAP_FLAG_GENDECORATIONMIN]) & 0xFF) << 8; + map.flags[z] |= (atoi(mapflagtext[MAP_FLAG_GENDECORATIONMAX]) & 0xFF) << 0; + } else if ( z == MAP_FLAG_GENBYTES3 ) { map.flags[z] = 0; if ( !strncmp(mapflagtext[MAP_FLAG_DISABLEDIGGING], "[x]", 3) ) { - map.flags[z] |= (1 << 24) & 0xFF; + map.flags[z] |= (1 << 24); } if ( !strncmp(mapflagtext[MAP_FLAG_DISABLETELEPORT], "[x]", 3) ) { - map.flags[z] |= (1 << 16) & 0xFF; + map.flags[z] |= (1 << 16); } if ( !strncmp(mapflagtext[MAP_FLAG_DISABLELEVITATION], "[x]", 3) ) { - map.flags[z] |= (1 << 8) & 0xFF; + map.flags[z] |= (1 << 8); } if ( !strncmp(mapflagtext[MAP_FLAG_GENADJACENTROOMS], "[x]", 3) ) { - map.flags[z] |= (1 << 0) & 0xFF; + map.flags[z] |= (1 << 0); } } else if ( z == MAP_FLAG_GENBYTES4 ) @@ -507,16 +539,17 @@ void buttonNewConfirm(button_t* my) map.flags[z] = 0; if ( !strncmp(mapflagtext[MAP_FLAG_DISABLEOPENING], "[x]", 3) ) { - map.flags[z] |= (1 << 24) & 0xFF; + map.flags[z] |= (1 << 24); } if ( !strncmp(mapflagtext[MAP_FLAG_DISABLEMESSAGES], "[x]", 3) ) { - map.flags[z] |= (1 << 16) & 0xFF; + map.flags[z] |= (1 << 16); } if ( !strncmp(mapflagtext[MAP_FLAG_DISABLEHUNGER], "[x]", 3) ) { - map.flags[z] |= (1 << 8) & 0xFF; + map.flags[z] |= (1 << 8); } + map.flags[z] |= atoi(mapflagtext[MAP_FLAG_PERIMETER_GAP]) & 0xFF; } else { From 214a3258731f6b7e9c70609acd44ce38c0f0de2f Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Mon, 25 Sep 2023 00:55:11 +1000 Subject: [PATCH 037/146] * fix ghost interact list not being culled --- src/player.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/player.cpp b/src/player.cpp index 7d36a325a..a706b7bb2 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -3277,6 +3277,13 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) return 0.0; } } + else if ( player.ghost.isActive() ) + { + if ( !player.ghost.allowedInteractEntity(*parent) ) + { + return 0.0; + } + } if ( parent->behavior == &actPlayer && player.playernum == parent->skill[2] ) { From 563f933bb4688c45dfc1b6ee393a5dc71a432c73 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Mon, 25 Sep 2023 00:55:25 +1000 Subject: [PATCH 038/146] * draw cursor for ghost --- src/ui/GameUI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 5c30bc41f..14b233b40 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -8726,7 +8726,7 @@ void Player::HUD_t::updateWorldTooltipPrompts() } else { - if ( !player.entity ) + if ( !player.entity && !player.ghost.isActive() ) { cursor->disabled = true; } @@ -9782,7 +9782,7 @@ void Player::HUD_t::processHUD() #endif // NINTENDO } - if ( !gamePaused && player.entity && player.shootmode ) + if ( !gamePaused && (player.entity || player.ghost.isActive()) && player.shootmode) { inputs.getVirtualMouse(player.playernum)->draw_cursor = false; } From a7cd0815456423faf60a43da40cb0c0b7c5c4416 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Tue, 19 Sep 2023 19:05:11 -0700 Subject: [PATCH 039/146] timed holiday themes xcode includes themes directory from baronyupdate unified physfsReloadMusic and loadMusic Signed-off-by: SheridanR --- src/editor.cpp | 1 - src/engine/audio/music.cpp | 356 +++--------------- src/engine/audio/sound.cpp | 248 ++++++------ src/files.cpp | 59 ++- src/files.hpp | 10 + src/init.cpp | 21 +- src/main.cpp | 19 +- src/main.hpp | 1 + src/mod_tools.cpp | 9 +- xcode/Barony/Barony.xcodeproj/project.pbxproj | 4 + 10 files changed, 294 insertions(+), 434 deletions(-) diff --git a/src/editor.cpp b/src/editor.cpp index 80ce1b6fc..48eeab47b 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1561,7 +1561,6 @@ int main(int argc, char** argv) } // initialize - useModelCache = true; verticalSync = true; if ( (x = initApp("Barony Editor", fullscreen)) ) { diff --git a/src/engine/audio/music.cpp b/src/engine/audio/music.cpp index 061fe4362..b93cd7251 100644 --- a/src/engine/audio/music.cpp +++ b/src/engine/audio/music.cpp @@ -15,6 +15,7 @@ #include "../../entity.hpp" #include "../../player.hpp" #include "../../prng.hpp" +#include "../../files.hpp" #ifdef NINTENDO #include "../../nintendo/music.hpp" #else @@ -23,309 +24,60 @@ bool loadMusic() { - int c = 0; - - // load music -#ifdef SOUND -#ifdef USE_OPENAL -#define fmod_system 0 -#define FMOD_SOFTWARE 0 -#define fmod_system->createStream(A, B, C, D, E) OPENAL_CreateStreamSound(B, E) -#define FMOD::Sound OPENAL_BUFFER -int fmod_result; -#endif - - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_INTRODUCTION, FMOD_DEFAULT, nullptr, &introductionmusic); //TODO: FMOD_SOFTWARE -> FMOD_DEFAULT? - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_INTERMISSION, FMOD_DEFAULT, nullptr, &intermissionmusic); - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_MINETOWN, FMOD_DEFAULT, nullptr, &minetownmusic); - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_SPLASH, FMOD_DEFAULT, nullptr, &splashmusic); - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_LIBRARY, FMOD_DEFAULT, nullptr, &librarymusic); - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_SHOP, FMOD_DEFAULT, nullptr, &shopmusic); - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_HERXBOSS, FMOD_DEFAULT, nullptr, &herxmusic); - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_TEMPLE, FMOD_DEFAULT, nullptr, &templemusic); - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_ENDGAME, FMOD_DEFAULT, nullptr, &endgamemusic); - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_ESCAPE, FMOD_DEFAULT, nullptr, &escapemusic); - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_DEVIL, FMOD_DEFAULT, nullptr, &devilmusic); - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_SANCTUM, FMOD_DEFAULT, nullptr, &sanctummusic); - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_TUTORIAL, FMOD_DEFAULT, nullptr, &tutorialmusic); - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_INTROSTORY, FMOD_DEFAULT, nullptr, &introstorymusic); - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_GAMEOVER, FMOD_DEFAULT, nullptr, &gameovermusic); - if ( PHYSFS_getRealDir(MUSIC_FILEPATH_GNOMISH_MINES) != nullptr ) - { - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_GNOMISH_MINES, FMOD_DEFAULT, nullptr, &gnomishminesmusic); - } - if ( PHYSFS_getRealDir(MUSIC_FILEPATH_GREATCASTLE) != nullptr ) - { - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_GREATCASTLE, FMOD_DEFAULT, nullptr, &greatcastlemusic); - } - if ( PHYSFS_getRealDir(MUSIC_FILEPATH_SOKOBAN) != nullptr ) - { - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_SOKOBAN, FMOD_DEFAULT, nullptr, &sokobanmusic); - } - if ( PHYSFS_getRealDir(MUSIC_FILEPATH_CAVES_LAIR) != nullptr ) - { - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_CAVES_LAIR, FMOD_DEFAULT, nullptr, &caveslairmusic); - } - if ( PHYSFS_getRealDir(MUSIC_FILEPATH_BRAMS_CASTLE) != nullptr ) - { - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_BRAMS_CASTLE, FMOD_DEFAULT, nullptr, &bramscastlemusic); - } - if ( PHYSFS_getRealDir(MUSIC_FILEPATH_HAMLET) != nullptr ) - { - fmod_result = fmod_system->createSound(MUSIC_FILEPATH_HAMLET, FMOD_DEFAULT, nullptr, &hamletmusic); - } - - if ( NUMMINESMUSIC > 0 ) - { - minesmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMMINESMUSIC); - for ( c = 0; c < NUMMINESMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_MINES, c); - fmod_result = fmod_system->createSound(tempstr, FMOD_DEFAULT, nullptr, &minesmusic[c]); - } - } - if ( NUMSWAMPMUSIC > 0 ) - { - swampmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMSWAMPMUSIC); - for ( c = 0; c < NUMSWAMPMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_SWAMP, c); - fmod_result = fmod_system->createSound(tempstr, FMOD_DEFAULT, nullptr, &swampmusic[c]); - } - } - if ( NUMLABYRINTHMUSIC > 0 ) - { - labyrinthmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMLABYRINTHMUSIC); - for ( c = 0; c < NUMLABYRINTHMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_LABYRINTH, c); - fmod_result = fmod_system->createSound(tempstr, FMOD_DEFAULT, nullptr, &labyrinthmusic[c]); - } - } - if ( NUMRUINSMUSIC > 0 ) - { - ruinsmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMRUINSMUSIC); - for ( c = 0; c < NUMRUINSMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_RUINS, c); - fmod_result = fmod_system->createSound(tempstr, FMOD_DEFAULT, nullptr, &ruinsmusic[c]); - } - } - if ( NUMUNDERWORLDMUSIC > 0 ) - { - underworldmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMUNDERWORLDMUSIC); - for ( c = 0; c < NUMUNDERWORLDMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_UNDERWORLD, c); - fmod_result = fmod_system->createSound(tempstr, FMOD_DEFAULT, nullptr, &underworldmusic[c]); - } - } - if ( NUMHELLMUSIC > 0 ) - { - hellmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMHELLMUSIC); - for ( c = 0; c < NUMHELLMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_HELL, c); - fmod_result = fmod_system->createSound(tempstr, FMOD_DEFAULT, nullptr, &hellmusic[c]); - } - } - if ( NUMMINOTAURMUSIC > 0 ) - { - minotaurmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMMINOTAURMUSIC); - for ( c = 0; c < NUMMINOTAURMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_MINOTAUR, c); - fmod_result = fmod_system->createSound(tempstr, FMOD_DEFAULT, nullptr, &minotaurmusic[c]); - } - } - if ( NUMCAVESMUSIC > 0 ) - { - cavesmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMCAVESMUSIC); - for ( c = 0; c < NUMCAVESMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_CAVES, c); - fmod_result = fmod_system->createSound(tempstr, FMOD_DEFAULT, nullptr, &cavesmusic[c]); - } - } - if ( NUMCITADELMUSIC > 0 ) - { - citadelmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMCITADELMUSIC); - for ( c = 0; c < NUMCITADELMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_CITADEL, c); - fmod_result = fmod_system->createSound(tempstr, FMOD_DEFAULT, nullptr, &citadelmusic[c]); - } - } - if ( NUMINTROMUSIC > 0 ) - { - intromusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMINTROMUSIC); - for ( c = 0; c < NUMINTROMUSIC; c++ ) - { - if ( c == 0 ) - { - strcpy(tempstr, MUSIC_FILEPATH_INTRO); - } - else - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_ADDITIONAL_INTROS, c); - } - fmod_result = fmod_system->createSound(tempstr, FMOD_DEFAULT, nullptr, &intromusic[c]); - } - } - } - else - { - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_INTRODUCTION, FMOD_DEFAULT, nullptr, &introductionmusic); //TODO: FMOD_SOFTWARE -> FMOD_DEFAULT? - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_INTERMISSION, FMOD_DEFAULT, nullptr, &intermissionmusic); - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_MINETOWN, FMOD_DEFAULT, nullptr, &minetownmusic); - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_SPLASH, FMOD_DEFAULT, nullptr, &splashmusic); - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_LIBRARY, FMOD_DEFAULT, nullptr, &librarymusic); - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_SHOP, FMOD_DEFAULT, nullptr, &shopmusic); - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_HERXBOSS, FMOD_DEFAULT, nullptr, &herxmusic); - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_TEMPLE, FMOD_DEFAULT, nullptr, &templemusic); - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_ENDGAME, FMOD_DEFAULT, nullptr, &endgamemusic); - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_ESCAPE, FMOD_DEFAULT, nullptr, &escapemusic); - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_DEVIL, FMOD_DEFAULT, nullptr, &devilmusic); - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_SANCTUM, FMOD_DEFAULT, nullptr, &sanctummusic); - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_TUTORIAL, FMOD_DEFAULT, nullptr, &tutorialmusic); - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_INTROSTORY, FMOD_DEFAULT, nullptr, &introstorymusic); - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_GAMEOVER, FMOD_DEFAULT, nullptr, &gameovermusic); - if ( PHYSFS_getRealDir(MUSIC_FILEPATH_GNOMISH_MINES) != nullptr ) - { - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_GNOMISH_MINES, FMOD_DEFAULT, nullptr, &gnomishminesmusic); - } - if ( PHYSFS_getRealDir(MUSIC_FILEPATH_GREATCASTLE) != nullptr ) - { - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_GREATCASTLE, FMOD_DEFAULT, nullptr, &greatcastlemusic); - } - if ( PHYSFS_getRealDir(MUSIC_FILEPATH_SOKOBAN) != nullptr ) - { - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_SOKOBAN, FMOD_DEFAULT, nullptr, &sokobanmusic); - } - if ( PHYSFS_getRealDir(MUSIC_FILEPATH_CAVES_LAIR) != nullptr ) - { - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_CAVES_LAIR, FMOD_DEFAULT, nullptr, &caveslairmusic); - } - if ( PHYSFS_getRealDir(MUSIC_FILEPATH_BRAMS_CASTLE) != nullptr ) - { - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_BRAMS_CASTLE, FMOD_DEFAULT, nullptr, &bramscastlemusic); - } - if ( PHYSFS_getRealDir(MUSIC_FILEPATH_HAMLET) != nullptr ) - { - fmod_result = fmod_system->createStream(MUSIC_FILEPATH_HAMLET, FMOD_DEFAULT, nullptr, &hamletmusic); - } - - if ( NUMMINESMUSIC > 0 ) - { - minesmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMMINESMUSIC); - for ( c = 0; c < NUMMINESMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_MINES, c); - fmod_result = fmod_system->createStream(tempstr, FMOD_DEFAULT, nullptr, &minesmusic[c]); - } - } - if ( NUMSWAMPMUSIC > 0 ) - { - swampmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMSWAMPMUSIC); - for ( c = 0; c < NUMSWAMPMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_SWAMP, c); - fmod_result = fmod_system->createStream(tempstr, FMOD_DEFAULT, nullptr, &swampmusic[c]); - } - } - if ( NUMLABYRINTHMUSIC > 0 ) - { - labyrinthmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMLABYRINTHMUSIC); - for ( c = 0; c < NUMLABYRINTHMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_LABYRINTH, c); - fmod_result = fmod_system->createStream(tempstr, FMOD_DEFAULT, nullptr, &labyrinthmusic[c]); - } - } - if ( NUMRUINSMUSIC > 0 ) - { - ruinsmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMRUINSMUSIC); - for ( c = 0; c < NUMRUINSMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_RUINS, c); - fmod_result = fmod_system->createStream(tempstr, FMOD_DEFAULT, nullptr, &ruinsmusic[c]); - } - } - if ( NUMUNDERWORLDMUSIC > 0 ) - { - underworldmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMUNDERWORLDMUSIC); - for ( c = 0; c < NUMUNDERWORLDMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_UNDERWORLD, c); - fmod_result = fmod_system->createStream(tempstr, FMOD_DEFAULT, nullptr, &underworldmusic[c]); - } - } - if ( NUMHELLMUSIC > 0 ) - { - hellmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMHELLMUSIC); - for ( c = 0; c < NUMHELLMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_HELL, c); - fmod_result = fmod_system->createStream(tempstr, FMOD_DEFAULT, nullptr, &hellmusic[c]); - } - } - if ( NUMMINOTAURMUSIC > 0 ) - { - minotaurmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMMINOTAURMUSIC); - for ( c = 0; c < NUMMINOTAURMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_MINOTAUR, c); - fmod_result = fmod_system->createStream(tempstr, FMOD_DEFAULT, nullptr, &minotaurmusic[c]); - } - } - if ( NUMCAVESMUSIC > 0 ) - { - cavesmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMCAVESMUSIC); - for ( c = 0; c < NUMCAVESMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_CAVES, c); - fmod_result = fmod_system->createStream(tempstr, FMOD_DEFAULT, nullptr, &cavesmusic[c]); - } - } - if ( NUMCITADELMUSIC > 0 ) - { - citadelmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMCITADELMUSIC); - for ( c = 0; c < NUMCITADELMUSIC; c++ ) - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_CITADEL, c); - fmod_result = fmod_system->createStream(tempstr, FMOD_DEFAULT, nullptr, &citadelmusic[c]); - } - } - if ( NUMINTROMUSIC > 0 ) - { - intromusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMINTROMUSIC); - for ( c = 0; c < NUMINTROMUSIC; c++ ) - { - if ( c == 0 ) - { - strcpy(tempstr, MUSIC_FILEPATH_INTRO); - } - else - { - snprintf(tempstr, 1000, MUSIC_FILEPATH_ADDITIONAL_INTROS, c); - } - fmod_result = fmod_system->createStream(tempstr, FMOD_DEFAULT, nullptr, &intromusic[c]); - } - } - } + if ( NUMMINESMUSIC > 0 ) + { + minesmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMMINESMUSIC); + memset(minesmusic, 0, sizeof(FMOD::Sound*) * NUMMINESMUSIC); + } + if ( NUMSWAMPMUSIC > 0 ) + { + swampmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMSWAMPMUSIC); + memset(swampmusic, 0, sizeof(FMOD::Sound*) * NUMSWAMPMUSIC); + } + if ( NUMLABYRINTHMUSIC > 0 ) + { + labyrinthmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMLABYRINTHMUSIC); + memset(labyrinthmusic, 0, sizeof(FMOD::Sound*) * NUMLABYRINTHMUSIC); + } + if ( NUMRUINSMUSIC > 0 ) + { + ruinsmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMRUINSMUSIC); + memset(ruinsmusic, 0, sizeof(FMOD::Sound*) * NUMRUINSMUSIC); + } + if ( NUMUNDERWORLDMUSIC > 0 ) + { + underworldmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMUNDERWORLDMUSIC); + memset(underworldmusic, 0, sizeof(FMOD::Sound*) * NUMUNDERWORLDMUSIC); + } + if ( NUMHELLMUSIC > 0 ) + { + hellmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMHELLMUSIC); + memset(hellmusic, 0, sizeof(FMOD::Sound*) * NUMHELLMUSIC); + } + if ( NUMMINOTAURMUSIC > 0 ) + { + minotaurmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMMINOTAURMUSIC); + memset(minotaurmusic, 0, sizeof(FMOD::Sound*) * NUMMINOTAURMUSIC); + } + if ( NUMCAVESMUSIC > 0 ) + { + cavesmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMCAVESMUSIC); + memset(cavesmusic, 0, sizeof(FMOD::Sound*) * NUMCAVESMUSIC); + } + if ( NUMCITADELMUSIC > 0 ) + { + citadelmusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMCITADELMUSIC); + memset(citadelmusic, 0, sizeof(FMOD::Sound*) * NUMCITADELMUSIC); + } + if ( NUMINTROMUSIC > 0 ) + { + intromusic = (FMOD::Sound**)malloc(sizeof(FMOD::Sound*) * NUMINTROMUSIC); + memset(intromusic, 0, sizeof(FMOD::Sound*) * NUMINTROMUSIC); + } -#ifdef USE_OPENAL -#undef fmod_system -#undef FMOD_SOFTWARE -#undef fmod_system->createStream -#undef FMOD::Sound -#endif - -#endif - - return true; + bool introMusicChanged; + physfsReloadMusic(introMusicChanged, true); + return true; } void stopMusic() diff --git a/src/engine/audio/sound.cpp b/src/engine/audio/sound.cpp index e52393b8a..f94329eee 100644 --- a/src/engine/audio/sound.cpp +++ b/src/engine/audio/sound.cpp @@ -1230,20 +1230,20 @@ FMOD_RESULT physfsReloadMusic_helper_reloadMusicArray(uint32_t numMusic, const c if ( musicArray ) { musicArray[c]->release(); - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &musicArray[c]); //TODO: Any other FMOD_MODEs should be used here? FMOD_SOFTWARE -> what now? FMOD_2D? LOOP? - } - else - { - fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &musicArray[c]); //TODO: Any other FMOD_MODEs should be used here? FMOD_SOFTWARE -> what now? FMOD_2D? LOOP? - } - if (fmod_result != FMOD_OK) - { - printlog("[PhysFS]: ERROR: Failed reloading music file \"%s\"."); - return fmod_result; - } } + if ( musicPreload ) + { + fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &musicArray[c]); //TODO: Any other FMOD_MODEs should be used here? FMOD_SOFTWARE -> what now? FMOD_2D? LOOP? + } + else + { + fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &musicArray[c]); //TODO: Any other FMOD_MODEs should be used here? FMOD_SOFTWARE -> what now? FMOD_2D? LOOP? + } + if (fmod_result != FMOD_OK) + { + printlog("[PhysFS]: ERROR: Failed reloading music file \"%s\"."); + return fmod_result; + } } } } @@ -1307,169 +1307,169 @@ void physfsReloadMusic(bool &introMusicChanged, bool reloadAll) //TODO: This sho if ( introductionmusic ) { introductionmusic->release(); - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &introductionmusic); //TODO: FMOD_SOFTWARE -> what now? FMOD_2D? FMOD_LOOP_NORMAL? More things? Something else? - } - else - { - fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &introductionmusic); //TODO: FMOD_SOFTWARE -> what now? FMOD_2D? FMOD_LOOP_NORMAL? More things? Something else? - } } + if ( musicPreload ) + { + fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &introductionmusic); //TODO: FMOD_SOFTWARE -> what now? FMOD_2D? FMOD_LOOP_NORMAL? More things? Something else? + } + else + { + fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &introductionmusic); //TODO: FMOD_SOFTWARE -> what now? FMOD_2D? FMOD_LOOP_NORMAL? More things? Something else? + } break; case 1: if ( intermissionmusic ) { intermissionmusic->release(); - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &intermissionmusic); - } - else - { - fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &intermissionmusic); - } } + if ( musicPreload ) + { + fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &intermissionmusic); + } + else + { + fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &intermissionmusic); + } break; case 2: if ( minetownmusic ) { minetownmusic->release(); - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &minetownmusic); - } - else - { - fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &minetownmusic); - } } + if ( musicPreload ) + { + fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &minetownmusic); + } + else + { + fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &minetownmusic); + } break; case 3: if ( splashmusic ) { splashmusic->release(); - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &splashmusic); - } - else - { - fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &splashmusic); - } } + if ( musicPreload ) + { + fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &splashmusic); + } + else + { + fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &splashmusic); + } break; case 4: if ( librarymusic ) { librarymusic->release(); - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &librarymusic); - } - else - { - fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &librarymusic); - } } + if ( musicPreload ) + { + fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &librarymusic); + } + else + { + fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &librarymusic); + } break; case 5: if ( shopmusic ) { shopmusic->release(); - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &shopmusic); - } - else - { - fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &shopmusic); - } } + if ( musicPreload ) + { + fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &shopmusic); + } + else + { + fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &shopmusic); + } break; case 6: if ( herxmusic ) { herxmusic->release(); - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &herxmusic); - } - else - { - fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &herxmusic); - } } + if ( musicPreload ) + { + fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &herxmusic); + } + else + { + fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &herxmusic); + } break; case 7: if ( templemusic ) { templemusic->release(); - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &templemusic); - } - else - { - fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &templemusic); - } } + if ( musicPreload ) + { + fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &templemusic); + } + else + { + fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &templemusic); + } break; case 8: if ( endgamemusic ) { endgamemusic->release(); - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &endgamemusic); - } - else - { - fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &endgamemusic); - } } + if ( musicPreload ) + { + fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &endgamemusic); + } + else + { + fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &endgamemusic); + } break; case 9: if ( escapemusic ) { escapemusic->release(); - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &escapemusic); - } - else - { - fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &escapemusic); - } } + if ( musicPreload ) + { + fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &escapemusic); + } + else + { + fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &escapemusic); + } break; case 10: if ( devilmusic ) { devilmusic->release(); - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &devilmusic); - } - else - { - fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &devilmusic); - } } + if ( musicPreload ) + { + fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &devilmusic); + } + else + { + fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &devilmusic); + } break; case 11: if ( sanctummusic ) { sanctummusic->release(); - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &sanctummusic); - } - else - { - fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &sanctummusic); - } - } + } + if ( musicPreload ) + { + fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &sanctummusic); + } + else + { + fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &sanctummusic); + } break; case 12: if ( gnomishminesmusic ) @@ -1660,21 +1660,21 @@ void physfsReloadMusic(bool &introMusicChanged, bool reloadAll) //TODO: This sho if ( music ) { music[c]->release(); - if ( musicPreload ) - { - fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &music[c]); - } - else - { - fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &music[c]); - } - introChanged = true; - if (fmod_result != FMOD_OK) - { - printlog("[PhysFS]: ERROR: Failed reloading music file \"%s\"."); - break; //TODO: Handle the error? - } } + if ( musicPreload ) + { + fmod_result = fmod_system->createSound(musicDir.c_str(), FMOD_2D, nullptr, &music[c]); + } + else + { + fmod_result = fmod_system->createStream(musicDir.c_str(), FMOD_2D, nullptr, &music[c]); + } + introChanged = true; + if (fmod_result != FMOD_OK) + { + printlog("[PhysFS]: ERROR: Failed reloading music file \"%s\"."); + break; //TODO: Handle the error? + } } } } diff --git a/src/files.cpp b/src/files.cpp index ca37ca345..1078d7ddd 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -34,6 +34,52 @@ #include "editor.hpp" #endif +char datadir[PATH_MAX]; +char outputdir[PATH_MAX]; +const char* holidayThemeDirs[HolidayTheme::THEME_MAX] = { + "", + "themes/scarony/", + "themes/christmas/" +}; + +#ifndef EDITOR +#include "interface/consolecommand.hpp" +static ConsoleVariable cvar_forceHoliday("/force_holiday", 0); +static ConsoleVariable cvar_disableHoliday("/disable_holiday", false); +#endif + +HolidayTheme getCurrentHoliday() { +#ifndef EDITOR + if (*cvar_disableHoliday) { + return HolidayTheme::THEME_NONE; + } + if (*cvar_forceHoliday) { + const int holiday = std::clamp(*cvar_forceHoliday, 0, (int)HolidayTheme::THEME_MAX - 1); + return static_cast(holiday); + } +#endif + static bool gotTime = false; + static int year, month, day; + if (!gotTime) { + gotTime = true; + getTimeAndDate(getTime(), &year, &month, &day, + nullptr, nullptr, nullptr); + } + if (month == 10 || (month == 11 && day == 1)) { + return HolidayTheme::THEME_HALLOWEEN; + } + else if ((month == 11 && day >= 24) || (month == 12) || (month == 1 && day == 1)) { + return HolidayTheme::THEME_XMAS; + } + else { + return HolidayTheme::THEME_NONE; + } +} + +bool isCurrentHoliday() { + return getCurrentHoliday() != HolidayTheme::THEME_NONE; +} + std::unordered_map mapHashes = { { "boss.lmp", 2376307 }, { "bramscastle.lmp", 3370696 }, @@ -1383,7 +1429,17 @@ bool completePath(char *dest, const char * const filename, const char *base) { } #endif - snprintf(dest, PATH_MAX, "%s/%s", base, filename); + if (base == datadir && isCurrentHoliday()) { + const auto holiday = getCurrentHoliday(); + const auto holiday_dir = holidayThemeDirs[holiday]; + assert(holiday_dir[0]); + snprintf(dest, PATH_MAX, "%s/%s%s", base, holiday_dir, filename); + if (!dataPathExists(dest, false)) { + snprintf(dest, PATH_MAX, "%s/%s", base, filename); + } + } else { + snprintf(dest, PATH_MAX, "%s/%s", base, filename); + } return true; } @@ -1410,6 +1466,7 @@ DIR* openDataDir(const char * const name) { bool dataPathExists(const char * const path, bool complete) { #ifdef NINTENDO + // TODO for themes!!! return true; #endif if (complete) { diff --git a/src/files.hpp b/src/files.hpp index 5ca42a936..bed0a1dfa 100644 --- a/src/files.hpp +++ b/src/files.hpp @@ -269,6 +269,16 @@ class FileIO { } }; +enum HolidayTheme { + THEME_NONE, + THEME_HALLOWEEN, + THEME_XMAS, + THEME_MAX +}; +extern const char* holidayThemeDirs[HolidayTheme::THEME_MAX]; +HolidayTheme getCurrentHoliday(); +bool isCurrentHoliday(); + extern char datadir[PATH_MAX]; //PATH_MAX as defined in main.hpp -- maybe define in Config.hpp? extern char outputdir[PATH_MAX]; void glLoadTexture(SDL_Surface* image, int texnum); diff --git a/src/init.cpp b/src/init.cpp index 3352db992..c03e18ec6 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -112,17 +112,32 @@ int initApp(char const * const title, int fullscreen) if ( !PHYSFS_isInit() ) { - printlog("[PhysFS]: failed to initialize! Error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + printlog("[PhysFS]: failed to initialize! Error: %s", + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); return 13; } else { - printlog("[PhysFS]: successfully initialized, last error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + printlog("[PhysFS]: successfully initialized, last error: %s", + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); } + if (isCurrentHoliday()) { + useModelCache = false; // don't use model cache on holidays. + const auto holiday = getCurrentHoliday(); + const auto holiday_dir = holidayThemeDirs[holiday]; + const auto holiday_dir_str = (std::string(datadir) + "/") + holiday_dir; + if (!PHYSFS_mount(holiday_dir_str.c_str(), NULL, 1)) { + printlog("[PhysFS]: unsuccessfully mounted holiday %s folder. Error: %s", + holiday_dir_str.c_str(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return 13; + } + } + if ( !PHYSFS_mount(datadir, NULL, 1) ) { - printlog("[PhysFS]: unsuccessfully mounted base %s folder. Error: %s", datadir, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + printlog("[PhysFS]: unsuccessfully mounted base %s folder. Error: %s", + datadir, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); return 13; } diff --git a/src/main.cpp b/src/main.cpp index 73cc375db..ded20ebab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -210,11 +210,10 @@ int mainloop = 1; bool initialized = false; Uint32 ticks = 0; bool stop = false; -char datadir[PATH_MAX]; -char outputdir[PATH_MAX]; SDL_bool EnableMouseCapture = SDL_TRUE; // disable if mouse capture causes problem debugging in Linux bool& enableDebugKeys = cvar_enableDebugKeys.data; + // input stuff Uint32 impulses[NUMIMPULSES]; Uint32 joyimpulses[NUM_JOY_IMPULSES]; @@ -829,3 +828,19 @@ char* getTimeAndDateFormatted(time_t t, char* buf, size_t size) { return buf; } #endif + +void getTimeAndDate(time_t t, int* year, int* month, int* day, int* hour, int* min, int* second) { + char buf[32]; + getTimeAndDateFormatted(t, buf, sizeof(buf)); + + int _year, _month, _day, _hour, _min, _second; + const int result = sscanf(buf, "%d-%d-%d %d-%d-%d", &_year, &_month, &_day, &_hour, &_min, &_second); + assert(result == 6); + + if (year) { *year = _year; } + if (month) { *month = _month; } + if (day) { *day = _day; } + if (hour) { *hour = _hour; } + if (min) { *min = _min; } + if (second) { *second = _second; } +} diff --git a/src/main.hpp b/src/main.hpp index 62ffb25d7..10c90c3e3 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -928,6 +928,7 @@ void finishStackTraceUnique(); extern bool ENABLE_STACK_TRACES; time_t getTime(); +void getTimeAndDate(time_t t, int* year, int* month, int* day, int* hour, int* min, int* second); char* getTimeFormatted(time_t t, char* buf, size_t size); char* getTimeAndDateFormatted(time_t t, char* buf, size_t size); diff --git a/src/mod_tools.cpp b/src/mod_tools.cpp index 87f274a64..37f49453e 100644 --- a/src/mod_tools.cpp +++ b/src/mod_tools.cpp @@ -8144,8 +8144,15 @@ bool Mods::clearAllMountedPaths() char** i; for ( i = PHYSFS_getSearchPath(); *i != NULL; i++ ) { + const std::string xmas = (std::string(datadir) + "/") + holidayThemeDirs[HolidayTheme::THEME_XMAS]; + const std::string halloween = (std::string(datadir) + "/") + holidayThemeDirs[HolidayTheme::THEME_HALLOWEEN]; + std::string line = *i; - if ( line.compare(outputdir) != 0 && line.compare(datadir) != 0 && line.compare("./") != 0 ) // don't unmount the base ./ directory + if (line.compare(outputdir) != 0 && + line.compare(datadir) != 0 && + line.compare(halloween) != 0 && + line.compare(xmas) != 0 && + line.compare("./") != 0) // don't unmount the base directories { if ( PHYSFS_unmount(*i) == 0 ) { diff --git a/xcode/Barony/Barony.xcodeproj/project.pbxproj b/xcode/Barony/Barony.xcodeproj/project.pbxproj index 1e9811887..20068d54a 100644 --- a/xcode/Barony/Barony.xcodeproj/project.pbxproj +++ b/xcode/Barony/Barony.xcodeproj/project.pbxproj @@ -239,6 +239,7 @@ 84C3DB30292028790084EAD5 /* playernames-male.txt in Resources */ = {isa = PBXBuildFile; fileRef = 84C3DB2D292028790084EAD5 /* playernames-male.txt */; }; 84C3DB31292028790084EAD5 /* lang in Resources */ = {isa = PBXBuildFile; fileRef = 84C3DB2E292028790084EAD5 /* lang */; }; 84C3DB32292028790084EAD5 /* playernames-female.txt in Resources */ = {isa = PBXBuildFile; fileRef = 84C3DB2F292028790084EAD5 /* playernames-female.txt */; }; + 84C69F2C2ABA83190014D8FF /* themes in Resources */ = {isa = PBXBuildFile; fileRef = 84C69F2B2ABA83190014D8FF /* themes */; }; 84F71060292327D900AE0D03 /* libsteam_api.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F7105F292327D900AE0D03 /* libsteam_api.dylib */; }; 84F710632923280500AE0D03 /* libEOSSDK-Mac-Shipping.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 84F71061292327F100AE0D03 /* libEOSSDK-Mac-Shipping.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 84F710642923280D00AE0D03 /* libsteam_api.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 84F7105F292327D900AE0D03 /* libsteam_api.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; @@ -560,6 +561,7 @@ 84C3DB2D292028790084EAD5 /* playernames-male.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "playernames-male.txt"; path = "../../playernames-male.txt"; sourceTree = ""; }; 84C3DB2E292028790084EAD5 /* lang */ = {isa = PBXFileReference; lastKnownFileType = folder; name = lang; path = ../../lang; sourceTree = ""; }; 84C3DB2F292028790084EAD5 /* playernames-female.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "playernames-female.txt"; path = "../../playernames-female.txt"; sourceTree = ""; }; + 84C69F2B2ABA83190014D8FF /* themes */ = {isa = PBXFileReference; lastKnownFileType = folder; name = themes; path = ../../../baronyupdate/themes; sourceTree = ""; }; 84F7105F292327D900AE0D03 /* libsteam_api.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsteam_api.dylib; path = ../../../steamworks/sdk/redistributable_bin/osx/libsteam_api.dylib; sourceTree = ""; }; 84F71061292327F100AE0D03 /* libEOSSDK-Mac-Shipping.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libEOSSDK-Mac-Shipping.dylib"; path = "../../../eos/EOS-SDK-21924193-v1/SDK/Bin/libEOSSDK-Mac-Shipping.dylib"; sourceTree = ""; }; 84F71089292350C500AE0D03 /* Vorbis.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Vorbis.framework; path = ../../../Vorbis.framework; sourceTree = ""; }; @@ -873,6 +875,7 @@ 84C3DB22292028630084EAD5 /* models */, 84C3DB21292028630084EAD5 /* music */, 84C3DB1F292028630084EAD5 /* sound */, + 84C69F2B2ABA83190014D8FF /* themes */, 847B3BF1296BF89F002D8852 /* libphysfs.1.dylib */, 847B3BEF296BF890002D8852 /* libpng16.16.dylib */, 84C3DB12291F799D0084EAD5 /* Config.hpp */, @@ -1021,6 +1024,7 @@ 84C3DB31292028790084EAD5 /* lang in Resources */, 84C3DB24292028630084EAD5 /* items in Resources */, 84C3DB25292028630084EAD5 /* maps in Resources */, + 84C69F2C2ABA83190014D8FF /* themes in Resources */, 84C3DB26292028630084EAD5 /* data in Resources */, 84C3DB27292028630084EAD5 /* books in Resources */, 84C3DB28292028630084EAD5 /* sound in Resources */, From 99236e1821fd2430e94bd868b5a0f5b1e40d81dc Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 20 Sep 2023 11:52:41 -0700 Subject: [PATCH 040/146] settings for holiday content Signed-off-by: SheridanR --- lang/en.txt | 9 +++ src/draw.cpp | 6 +- src/files.cpp | 5 +- src/files.hpp | 6 ++ src/init.cpp | 166 +++++++++++++++++++++++++++----------------- src/init.hpp | 2 + src/ui/MainMenu.cpp | 67 ++++++++++++------ 7 files changed, 170 insertions(+), 91 deletions(-) diff --git a/lang/en.txt b/lang/en.txt index 03736e564..db02687ad 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -7724,4 +7724,13 @@ Upload# 6020 Preload Music Files# 6021 Preloading improves playback performance, but increases system memory usage. (Applied next game launch)# +6022 Holiday Themes# +6023 Festive Content# +6024 Toggle holiday themed content on or off as desired. Bah, humbug!# +6025 Credits# +6026 Display# +6027 View credits for holiday content.# +6028 Credits go here# +6029 Okay# + 6100 end# diff --git a/src/draw.cpp b/src/draw.cpp index 3cb164214..2ef8d47c0 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -16,7 +16,7 @@ #include "entity.hpp" #include "player.hpp" #include "ui/Frame.hpp" -#ifndef NINTENDO +#ifdef EDITOR #include "editor.hpp" #endif #include "items.hpp" @@ -2281,7 +2281,7 @@ void drawEntities2D(long camx, long camy) // draw hover text for entities over the top of sprites. for ( node = map.entities->first; node != nullptr -#ifndef NINTENDO +#ifdef EDITOR && (openwindow == 0 && savewindow == 0) #endif @@ -2702,7 +2702,7 @@ void drawEntities2D(long camx, long camy) else if ( (omousex / TEXTURESIZE) * 32 == pos.x && (omousey / TEXTURESIZE) * 32 == pos.y && selectedEntity[0] == NULL -#ifndef NINTENDO +#ifdef EDITOR && hovertext #endif ) diff --git a/src/files.cpp b/src/files.cpp index 1078d7ddd..6b131d58b 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -43,9 +43,8 @@ const char* holidayThemeDirs[HolidayTheme::THEME_MAX] = { }; #ifndef EDITOR -#include "interface/consolecommand.hpp" -static ConsoleVariable cvar_forceHoliday("/force_holiday", 0); -static ConsoleVariable cvar_disableHoliday("/disable_holiday", false); +ConsoleVariable cvar_forceHoliday("/force_holiday", 0); +ConsoleVariable cvar_disableHoliday("/disable_holiday", false); #endif HolidayTheme getCurrentHoliday() { diff --git a/src/files.hpp b/src/files.hpp index bed0a1dfa..17c210b5d 100644 --- a/src/files.hpp +++ b/src/files.hpp @@ -279,6 +279,12 @@ extern const char* holidayThemeDirs[HolidayTheme::THEME_MAX]; HolidayTheme getCurrentHoliday(); bool isCurrentHoliday(); +#ifndef EDITOR +#include "interface/consolecommand.hpp" +extern ConsoleVariable cvar_forceHoliday; +extern ConsoleVariable cvar_disableHoliday; +#endif + extern char datadir[PATH_MAX]; //PATH_MAX as defined in main.hpp -- maybe define in Config.hpp? extern char outputdir[PATH_MAX]; void glLoadTexture(SDL_Surface* image, int texnum); diff --git a/src/init.cpp b/src/init.cpp index c03e18ec6..9555fcea9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -26,7 +26,7 @@ #include "init.hpp" #include "light.hpp" #include "net.hpp" -#ifndef NINTENDO +#ifdef EDITOR #include "editor.hpp" #endif // NINTENDO #include "menu.hpp" @@ -46,6 +46,7 @@ #include "ui/Button.hpp" #include "ui/LoadingScreen.hpp" #ifndef EDITOR +#include "mod_tools.hpp" #include "ui/MainMenu.hpp" #include "interface/consolecommand.hpp" static ConsoleVariable cvar_sdl_disablejoystickrawinput("/sdl_joystick_rawinput_disable", false, "disable SDL rawinput for gamepads (helps SDL_HapticOpen())"); @@ -55,6 +56,104 @@ static ConsoleVariable cvar_sdl_disablejoystickrawinput("/sdl_joystick_raw #include #include +bool mountBaseDataFolders() { + if (isCurrentHoliday()) { + useModelCache = false; // don't use model cache on holidays. + const auto holiday = getCurrentHoliday(); + const auto holiday_dir = holidayThemeDirs[holiday]; + const auto holiday_dir_str = (std::string(datadir) + "/") + holiday_dir; + if (!PHYSFS_mount(holiday_dir_str.c_str(), NULL, 1)) { + printlog("[PhysFS]: unsuccessfully mounted holiday %s folder. Error: %s", + holiday_dir_str.c_str(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return false; + } + } + + if ( !PHYSFS_mount(datadir, NULL, 1) ) + { + printlog("[PhysFS]: unsuccessfully mounted base %s folder. Error: %s", + datadir, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return false; + } + + if ( PHYSFS_mount(outputdir, NULL, 1) ) + { + printlog("[PhysFS]: successfully mounted output \"%s\" folder", outputdir); + if ( PHYSFS_setWriteDir(outputdir) ) + { + PHYSFS_mkdir("books"); + PHYSFS_mkdir("savegames"); + //TODO: Will these need special NINTENDO handling? + PHYSFS_mkdir("crashlogs"); + PHYSFS_mkdir("logfiles"); + PHYSFS_mkdir("data"); + PHYSFS_mkdir("data/custom-monsters"); + PHYSFS_mkdir("data/statues"); + PHYSFS_mkdir("data/scripts"); + PHYSFS_mkdir("config"); +#ifdef STEAMWORKS + PHYSFS_mkdir("workshop_cache"); +#endif +#ifdef NINTENDO + PHYSFS_mkdir("mods"); + std::string path = outputdir; + path.append(PHYSFS_getDirSeparator()).append("mods"); + PHYSFS_setWriteDir(path.c_str()); //Umm...should it really be doing that? First off, it didn't actually create this directory. Second off, what about the rest of the directories it created? + printlog("[PhysFS]: successfully set write folder %s", path.c_str()); +#else // NINTENDO + if ( PHYSFS_mkdir("mods") ) + { + std::string path = outputdir; + path.append(PHYSFS_getDirSeparator()).append("mods"); + PHYSFS_setWriteDir(path.c_str()); + printlog("[PhysFS]: successfully set write folder %s", path.c_str()); + } + else + { + printlog("[PhysFS]: unsuccessfully created mods/ folder. Error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return false; + } +#endif // !NINTENDO + } + } + else + { + printlog("[PhysFS]: unsuccessfully mounted base %s folder. Error: %s", outputdir, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return false; + } + + return true; +} + +bool remountBaseDataFolders() { +#ifdef EDITOR + return false; // don't do anything +#else + // first unmount everything. + bool success = true; + char** i; + for ( i = PHYSFS_getSearchPath(); *i != NULL; i++ ) { + if ( PHYSFS_unmount(*i) == 0 ) { + success = false; + printlog("[%s] unsuccessfully removed from the search path.\n", *i); + } else { + printlog("[%s] is removed from the search path.\n", *i); + } + } + PHYSFS_freeList(*i); + + // then mount base data folders + success = mountBaseDataFolders() ? success : false; + + // now mount any desired mods + success = Mods::mountAllExistingPaths() ? success : false; + + // and reload all content! + Mods::loadMods(); + + return success; +#endif +} /*------------------------------------------------------------------------------- @@ -122,71 +221,10 @@ int initApp(char const * const title, int fullscreen) PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); } - if (isCurrentHoliday()) { - useModelCache = false; // don't use model cache on holidays. - const auto holiday = getCurrentHoliday(); - const auto holiday_dir = holidayThemeDirs[holiday]; - const auto holiday_dir_str = (std::string(datadir) + "/") + holiday_dir; - if (!PHYSFS_mount(holiday_dir_str.c_str(), NULL, 1)) { - printlog("[PhysFS]: unsuccessfully mounted holiday %s folder. Error: %s", - holiday_dir_str.c_str(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - return 13; - } + if (!mountBaseDataFolders()) { + return 13; } - if ( !PHYSFS_mount(datadir, NULL, 1) ) - { - printlog("[PhysFS]: unsuccessfully mounted base %s folder. Error: %s", - datadir, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - return 13; - } - - if ( PHYSFS_mount(outputdir, NULL, 1) ) - { - printlog("[PhysFS]: successfully mounted output \"%s\" folder", outputdir); - if ( PHYSFS_setWriteDir(outputdir) ) - { - PHYSFS_mkdir("books"); - PHYSFS_mkdir("savegames"); - //TODO: Will these need special NINTENDO handling? - PHYSFS_mkdir("crashlogs"); - PHYSFS_mkdir("logfiles"); - PHYSFS_mkdir("data"); - PHYSFS_mkdir("data/custom-monsters"); - PHYSFS_mkdir("data/statues"); - PHYSFS_mkdir("data/scripts"); - PHYSFS_mkdir("config"); -#ifdef STEAMWORKS - PHYSFS_mkdir("workshop_cache"); -#endif -#ifdef NINTENDO - PHYSFS_mkdir("mods"); - std::string path = outputdir; - path.append(PHYSFS_getDirSeparator()).append("mods"); - PHYSFS_setWriteDir(path.c_str()); //Umm...should it really be doing that? First off, it didn't actually create this directory. Second off, what about the rest of the directories it created? - printlog("[PhysFS]: successfully set write folder %s", path.c_str()); -#else // NINTENDO - if ( PHYSFS_mkdir("mods") ) - { - std::string path = outputdir; - path.append(PHYSFS_getDirSeparator()).append("mods"); - PHYSFS_setWriteDir(path.c_str()); - printlog("[PhysFS]: successfully set write folder %s", path.c_str()); - } - else - { - printlog("[PhysFS]: unsuccessfully created mods/ folder. Error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - return 13; - } -#endif // !NINTENDO - } - } - else - { - printlog("[PhysFS]: unsuccessfully mounted base %s folder. Error: %s", outputdir, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - return 13; - } - // initialize SDL window_title = title; printlog("initializing SDL...\n"); diff --git a/src/init.hpp b/src/init.hpp index d8b064746..a5e5c78ff 100644 --- a/src/init.hpp +++ b/src/init.hpp @@ -21,3 +21,5 @@ void reloadModels(int start, int end); void generateTileTextures(); void destroyTileTextures(); void bindTextureAtlas(int index); +bool mountBaseDataFolders(); +bool remountBaseDataFolders(); diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index d350d7ea4..5a5e73805 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -567,6 +567,7 @@ namespace MainMenu { bool bobbing_enabled = true; bool light_flicker_enabled = true; bool hold_to_activate_enabled = true; + bool holiday_themes_enabled = true; struct Video video; bool use_frame_interpolation = true; bool vertical_split_enabled = false; @@ -1742,7 +1743,6 @@ namespace MainMenu { const char* okay_text, void (*okay_callback)(Button&) ) { - soundActivate(); return monoPromptGeneric(window_text, okay_text, okay_callback, false); } @@ -2721,6 +2721,11 @@ namespace MainMenu { bobbing = bobbing_enabled; flickerLights = light_flicker_enabled; *cvar_hold_to_activate = hold_to_activate_enabled; + bool oldDisableHoliday = *cvar_disableHoliday; + *cvar_disableHoliday = !holiday_themes_enabled; + if (oldDisableHoliday != *cvar_disableHoliday) { + (void)remountBaseDataFolders(); + } result |= video.save() ? VideoRefresh::Video : VideoRefresh::None; *vertical_splitscreen = vertical_split_enabled; *staggered_splitscreen = staggered_split_enabled; @@ -2833,6 +2838,7 @@ namespace MainMenu { settings.bobbing_enabled = bobbing; settings.light_flicker_enabled = flickerLights; settings.hold_to_activate_enabled = *cvar_hold_to_activate; + settings.holiday_themes_enabled = !*cvar_disableHoliday; if (video) { settings.video = Video::load(); } else { @@ -2887,7 +2893,7 @@ namespace MainMenu { } bool AllSettings::serialize(FileInterface* file) { - int version = 18; + int version = 19; file->property("version", version); file->property("mods", mods); file->property("crossplay_enabled", crossplay_enabled); @@ -2929,6 +2935,7 @@ namespace MainMenu { file->property("bobbing_enabled", bobbing_enabled); file->property("light_flicker_enabled", light_flicker_enabled); file->propertyVersion("hold_to_activate_enabled", version >= 17, hold_to_activate_enabled); + file->propertyVersion("holiday_themes_enabled", version >= 19, holiday_themes_enabled); if (version >= 1) { file->property("video", video); file->property("vertical_split_enabled", vertical_split_enabled); @@ -6763,6 +6770,20 @@ namespace MainMenu { return; } int y = 0; + + auto holiday_credits_fn = [](Button& button){ + monoPromptXL(Language::get(6028), Language::get(6029), [](Button&){soundCancel(); closeMono();}); + }; + + y += settingsAddSubHeader(*settings_subwindow, y, "holiday_themes", Language::get(6022)); + y += settingsAddBooleanOption(*settings_subwindow, y, "holiday_themes", Language::get(6023), Language::get(6024), + allSettings.holiday_themes_enabled, [](Button& button){soundToggleSetting(button); allSettings.holiday_themes_enabled = button.isPressed();}); + y += settingsAddCustomize(*settings_subwindow, y, "holiday_credits", Language::get(6025), Language::get(6027), holiday_credits_fn); + + auto holiday_credits_button = settings_subwindow->findButton("setting_holiday_credits_customize_button"); + if (holiday_credits_button) { + holiday_credits_button->setText(Language::get(6026)); + } #if 0 y += settingsAddSubHeader(*settings_subwindow, y, "general", Language::get(5241)); @@ -6770,9 +6791,9 @@ namespace MainMenu { allSettings.show_ip_address_enabled, [](Button& button){soundToggleSetting(button); allSettings.show_ip_address_enabled = button.isPressed();}); #endif +#ifndef NINTENDO char port_desc[1024]; snprintf(port_desc, sizeof(port_desc), Language::get(5244), DEFAULT_PORT); - char buf[16]; snprintf(buf, sizeof(buf), "%hu", (Uint16)allSettings.port_number); y += settingsAddSubHeader(*settings_subwindow, y, "lan", Language::get(5245)); @@ -6791,17 +6812,35 @@ namespace MainMenu { } } }); -#if defined(USE_EOS) && (defined(STEAMWORKS) || defined(NINTENDO)) +#endif + +#if !defined(NINTENDO) +#if defined(USE_EOS) && defined(STEAMWORKS) y += settingsAddSubHeader(*settings_subwindow, y, "crossplay", Language::get(5247)); y += settingsAddBooleanOption(*settings_subwindow, y, "crossplay", Language::get(5248), Language::get(5249), allSettings.crossplay_enabled, [](Button& button){soundToggleSetting(button); allSettings.crossplay_enabled = button.isPressed();}); hookSettings(*settings_subwindow, - {{Setting::Type::Field, "port_number"}, - {Setting::Type::Boolean, "crossplay"}}); + { + {Setting::Type::Boolean, "holiday_themes"}, + {Setting::Type::Customize, "holiday_credits"}, + {Setting::Type::Field, "port_number"}, + {Setting::Type::Boolean, "crossplay"}, + }); #else hookSettings(*settings_subwindow, - {{Setting::Type::Field, "port_number"}}); + { + {Setting::Type::Boolean, "holiday_themes"}, + {Setting::Type::Customize, "holiday_credits"}, + {Setting::Type::Field, "port_number"}, + }); +#endif +#else // defined(NINTENDO) + hookSettings(*settings_subwindow, + { + {Setting::Type::Boolean, "holiday_themes"}, + {Setting::Type::Customize, "holiday_credits"}, + }); #endif settingsSubwindowFinalize(*settings_subwindow, y, {Setting::Type::Field, "port_number"}); @@ -20747,9 +20786,7 @@ namespace MainMenu { "Controls", }; if (intro) { -#ifndef NINTENDO tabs.push_back("Online"); -#endif } else { tabs.push_back("Game"); } @@ -20787,9 +20824,7 @@ namespace MainMenu { {"Controls", Language::get(5621), settingsControls}, }; if (intro) { -#ifndef NINTENDO tabs.push_back({"Online", Language::get(5622), settingsOnline}); -#endif } else { tabs.push_back({"Game", Language::get(5623), settingsGame}); } @@ -20867,9 +20902,7 @@ namespace MainMenu { "Controls", }; if (intro) { -#ifndef NINTENDO tabs.push_back("Online"); -#endif } else { tabs.push_back("Game"); } @@ -20903,11 +20936,7 @@ namespace MainMenu { tab_right->setWidgetBack("discard_and_exit"); tab_right->setWidgetPageLeft("tab_left"); tab_right->setWidgetPageRight("tab_right"); -#ifdef NINTENDO - tab_right->setWidgetLeft(intro ? "Controls" : "Game"); -#else tab_right->setWidgetLeft(intro ? "Online" : "Game"); -#endif tab_right->setWidgetDown("confirm_and_exit"); tab_right->addWidgetAction("MenuAlt1", "restore_defaults"); tab_right->addWidgetAction("MenuStart", "confirm_and_exit"); @@ -20920,9 +20949,7 @@ namespace MainMenu { "General", }; if (intro) { -#ifndef NINTENDO tabs.insert(tabs.begin(), "Online"); -#endif } else { tabs.insert(tabs.begin(), "Game"); } @@ -20980,9 +21007,7 @@ namespace MainMenu { "General", }; if (intro) { -#ifndef NINTENDO tabs.insert(tabs.begin(), "Online"); -#endif } else { tabs.insert(tabs.begin(), "Game"); } From 3b5849d6780e6ff9adc1bcc5b953666fb05b679d Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 20 Sep 2023 11:54:41 -0700 Subject: [PATCH 041/146] nudged highscore text Signed-off-by: SheridanR --- lang/en.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/en.txt b/lang/en.txt index db02687ad..27881d2ad 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -7440,7 +7440,7 @@ has been lost.# to complete the test!# 5832 You will be revived if your party makes it to the next level.# -5833 You placed at %d +5833 You placed at position (%d) in local highscores!# 5834 You failed to place in local highscores.# From ba3c918ec4a48d19dc0ff19a8b2fd124343b414d Mon Sep 17 00:00:00 2001 From: SheridanR Date: Mon, 25 Sep 2023 15:59:58 -0700 Subject: [PATCH 042/146] hide useless xcode log messages Signed-off-by: SheridanR # Conflicts: # xcode/Barony/Barony.xcodeproj/xcshareddata/xcschemes/Barony Editor.xcscheme --- .../xcshareddata/xcschemes/Barony Editor.xcscheme | 7 +++++++ .../xcshareddata/xcschemes/Barony.xcscheme | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/xcode/Barony/Barony.xcodeproj/xcshareddata/xcschemes/Barony Editor.xcscheme b/xcode/Barony/Barony.xcodeproj/xcshareddata/xcschemes/Barony Editor.xcscheme index e70b9a573..d6ca799c2 100644 --- a/xcode/Barony/Barony.xcodeproj/xcshareddata/xcschemes/Barony Editor.xcscheme +++ b/xcode/Barony/Barony.xcodeproj/xcshareddata/xcschemes/Barony Editor.xcscheme @@ -49,6 +49,13 @@ ReferencedContainer = "container:Barony.xcodeproj"> + + + + + + + + Date: Wed, 20 Sep 2023 12:38:46 -0700 Subject: [PATCH 043/146] do not write model cache if themed content is loaded Signed-off-by: SheridanR --- src/files.cpp | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/files.cpp b/src/files.cpp index 6b131d58b..742f7ea60 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -4160,20 +4160,22 @@ void generatePolyModels(int start, int end, bool forceCacheRebuild) list_FreeAll(&quads); } #ifndef NINTENDO - std::string cache_path = std::string(outputdir) + "/models.cache"; - if ( useModelCache && (model_cache = openDataFile(cache_path.c_str(), "wb")) ) - { - char modelCacheHeader[32] = "BARONY"; - strcat(modelCacheHeader, VERSION); - model_cache->write(&modelCacheHeader, sizeof(char), strlen(modelCacheHeader)); - for ( size_t model_index = 0; model_index < nummodels; model_index++ ) - { - polymodel_t* cur = &polymodels[model_index]; - model_cache->write(&cur->numfaces, sizeof(cur->numfaces), 1); - model_cache->write(cur->faces, sizeof(polytriangle_t), cur->numfaces); - } - FileIO::close(model_cache); - } + if (!isCurrentHoliday()) { + std::string cache_path = std::string(outputdir) + "/models.cache"; + if ( useModelCache && (model_cache = openDataFile(cache_path.c_str(), "wb")) ) + { + char modelCacheHeader[32] = "BARONY"; + strcat(modelCacheHeader, VERSION); + model_cache->write(&modelCacheHeader, sizeof(char), strlen(modelCacheHeader)); + for ( size_t model_index = 0; model_index < nummodels; model_index++ ) + { + polymodel_t* cur = &polymodels[model_index]; + model_cache->write(&cur->numfaces, sizeof(cur->numfaces), 1); + model_cache->write(cur->faces, sizeof(polytriangle_t), cur->numfaces); + } + FileIO::close(model_cache); + } + } #endif } From ff5a79f1923187de2edd6a67a1e8f226c9d550c7 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Sun, 24 Sep 2023 09:16:32 -0700 Subject: [PATCH 044/146] mod loading stuff fix [LT] in language file to [ZL] (nintendo) Signed-off-by: SheridanR --- src/files.cpp | 2 +- src/init.cpp | 12 +++--------- src/mod_tools.cpp | 35 ++++++++++++++++++++++++----------- src/mod_tools.hpp | 2 +- src/ui/MainMenu.cpp | 2 +- 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/files.cpp b/src/files.cpp index 742f7ea60..2df4280e6 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -67,7 +67,7 @@ HolidayTheme getCurrentHoliday() { if (month == 10 || (month == 11 && day == 1)) { return HolidayTheme::THEME_HALLOWEEN; } - else if ((month == 11 && day >= 24) || (month == 12) || (month == 1 && day == 1)) { + else if (month == 12 || (month == 1 && day == 1)) { return HolidayTheme::THEME_XMAS; } else { diff --git a/src/init.cpp b/src/init.cpp index 9555fcea9..7d3f7331a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -145,11 +145,8 @@ bool remountBaseDataFolders() { // then mount base data folders success = mountBaseDataFolders() ? success : false; - // now mount any desired mods - success = Mods::mountAllExistingPaths() ? success : false; - - // and reload all content! - Mods::loadMods(); + // reload files + Mods::unloadMods(true); return success; #endif @@ -920,10 +917,7 @@ int Language::loadLanguage(char const * const lang, bool forceLoadBaseDirectory) languageCode = lang; tmpEntries.clear(); - if ( forceLoadBaseDirectory ) - { - entries.clear(); - } + entries.clear(); // read file Uint32 line; diff --git a/src/mod_tools.cpp b/src/mod_tools.cpp index 37f49453e..b4df1bdb1 100644 --- a/src/mod_tools.cpp +++ b/src/mod_tools.cpp @@ -8267,7 +8267,7 @@ void Mods::loadModels(int start, int end) { generateVBOs(start, end); } -void Mods::unloadMods() +void Mods::unloadMods(bool force) { #ifndef EDITOR isLoading = true; @@ -8284,8 +8284,21 @@ void Mods::unloadMods() updateLoadingScreen(10); doLoadingScreen(); - - if ( Mods::modelsListRequiresReloadUnmodded || !Mods::modelsListModifiedIndexes.empty() ) + + if (force) { + modelsListModifiedIndexes.clear(); + for (int c = 0; c < nummodels; ++c) { + modelsListModifiedIndexes.push_back(c); + } + for (int c = 0; c < numsounds; ++c) { + soundsListModifiedIndexes.push_back(c); + } + for (const auto& pair : systemResourceImages) { + Mods::systemResourceImagesToReload.push_back(pair); + } + } + + if ( force || Mods::modelsListRequiresReloadUnmodded || !Mods::modelsListModifiedIndexes.empty() ) { int modelsIndexUpdateStart = 1; int modelsIndexUpdateEnd = nummodels; @@ -8304,7 +8317,7 @@ void Mods::unloadMods() updateLoadingScreen(20); doLoadingScreen(); - if ( Mods::soundListRequiresReloadUnmodded || !Mods::soundsListModifiedIndexes.empty() ) + if ( force || Mods::soundListRequiresReloadUnmodded || !Mods::soundsListModifiedIndexes.empty() ) { physfsReloadSounds(true); Mods::soundListRequiresReloadUnmodded = false; @@ -8315,7 +8328,7 @@ void Mods::unloadMods() updateLoadingScreen(30); doLoadingScreen(); - if ( Mods::tileListRequireReloadUnmodded ) + if ( force || Mods::tileListRequireReloadUnmodded ) { physfsReloadTiles(true); Mods::tileListRequireReloadUnmodded = false; @@ -8324,7 +8337,7 @@ void Mods::unloadMods() updateLoadingScreen(40); doLoadingScreen(); - if ( Mods::spriteImagesRequireReloadUnmodded ) + if ( force || Mods::spriteImagesRequireReloadUnmodded ) { physfsReloadSprites(true); Mods::spriteImagesRequireReloadUnmodded = false; @@ -8333,7 +8346,7 @@ void Mods::unloadMods() updateLoadingScreen(50); doLoadingScreen(); - if ( Mods::booksRequireReloadUnmodded ) + if ( force || Mods::booksRequireReloadUnmodded ) { physfsReloadBooks(); Mods::booksRequireReloadUnmodded = false; @@ -8342,7 +8355,7 @@ void Mods::unloadMods() updateLoadingScreen(60); doLoadingScreen(); - if ( Mods::musicRequireReloadUnmodded ) + if ( force || Mods::musicRequireReloadUnmodded ) { gamemodsUnloadCustomThemeMusic(); bool reloadIntroMusic = false; @@ -8359,7 +8372,7 @@ void Mods::unloadMods() updateLoadingScreen(70); doLoadingScreen(); - if ( Mods::langRequireReloadUnmodded ) + if ( force || Mods::langRequireReloadUnmodded ) { Language::reset(); Language::reloadLanguage(); @@ -8369,7 +8382,7 @@ void Mods::unloadMods() updateLoadingScreen(80); doLoadingScreen(); - if ( Mods::monsterLimbsRequireReloadUnmodded ) + if ( force || Mods::monsterLimbsRequireReloadUnmodded ) { physfsReloadMonsterLimbFiles(); Mods::monsterLimbsRequireReloadUnmodded = false; @@ -8378,7 +8391,7 @@ void Mods::unloadMods() updateLoadingScreen(85); doLoadingScreen(); - if ( Mods::systemImagesReloadUnmodded ) + if ( force || Mods::systemImagesReloadUnmodded ) { physfsReloadSystemImages(); Mods::systemImagesReloadUnmodded = false; diff --git a/src/mod_tools.hpp b/src/mod_tools.hpp index d3c0c6ef9..8feeaa186 100644 --- a/src/mod_tools.hpp +++ b/src/mod_tools.hpp @@ -3276,7 +3276,7 @@ struct Mods static bool clearAllMountedPaths(); static bool removePathFromMountedFiles(std::string findStr); static bool isPathInMountedFiles(std::string findStr); - static void unloadMods(); + static void unloadMods(bool force = false); static void loadMods(); static void loadModels(int start, int end); static void verifyAchievements(const char* fullpath, bool ignoreBaseFolder); diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 5a5e73805..f9bd06613 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -2723,7 +2723,7 @@ namespace MainMenu { *cvar_hold_to_activate = hold_to_activate_enabled; bool oldDisableHoliday = *cvar_disableHoliday; *cvar_disableHoliday = !holiday_themes_enabled; - if (oldDisableHoliday != *cvar_disableHoliday) { + if (initialized && oldDisableHoliday != *cvar_disableHoliday) { (void)remountBaseDataFolders(); } result |= video.save() ? VideoRefresh::Video : VideoRefresh::None; From d0a720742e3666b4ad3626ab661ccb937f1a4fee Mon Sep 17 00:00:00 2001 From: SheridanR Date: Mon, 25 Sep 2023 16:03:55 -0700 Subject: [PATCH 045/146] Holiday banners + holiday credits Signed-off-by: SheridanR # Conflicts: # src/ui/MainMenu.cpp --- lang/en.txt | 99 +++++++++++++- src/files.cpp | 12 +- src/files.hpp | 4 +- src/interface/drawminimap.cpp | 9 +- src/ui/MainMenu.cpp | 235 ++++++++++++++++++++++++++-------- 5 files changed, 287 insertions(+), 72 deletions(-) diff --git a/lang/en.txt b/lang/en.txt index 27881d2ad..de5dda9f3 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -7730,7 +7730,102 @@ Upload# 6025 Credits# 6026 Display# 6027 View credits for holiday content.# -6028 Credits go here# -6029 Okay# +6028 Next +Page# +6029 Close# + +# Scarony Barony Credits: + +6030 caves00 - Among Us by Myuu +caves01 - Fatal Shrine by Myuu +caves02 - Tormented Souls by Myuu +citadel00 - Evil Returns by Myuu +citadel01 - Friday the 13th by Myuu +citadel02 - October 31st by Myuu# + +6031 endgame - The Order's Theme by Myuu +herxboss - Soul Rail by Myuu +intermission - Echo by Myuu +intro - Ghost Town by Myuu +intro01 - Countdown by Myuu +intro02 - Untold Secrets by Myuu# + +6032 labyrinth00 - Final Showdown by Myuu +labyrinth01 - Living in the Dark by Myuu +labyrinth02 - Silent Turmoil by Myuu +library - Countdown by Myuu +mines00 - Look Out by Myuu +mines01 - Haunted House by Myuu# + +6033 mines02 - Nebula by Myuu +mines03 - Strange Vision by Myuu +mines04 - The Crypt by Myuu +minetown - Haunted By Screams by Myuu +minotaur00 - Run! by Myuu +minotaur01 - Cornered by Myuu# + +6034 ruins00 - You're Next by Myuu +ruins01 - Into the Depths by Myuu +ruins02 - The Backrooms by Myuu +sanctum - Metamorphosis by Myuu +shop - Carnival Freaks by Myuu +swamp00 - Poltergeist by Myuu# + +6035 swamp01 - Abyss by Myuu +swamp02 - Danger by Myuu +swamp03 - Growing Shadows +temple - Extinction by Myuu# + +# Merry Barony credits: + +6036 caves00 - Sleigh Bells by Myuu +caves01 - Deck the Halls by Myuu +caves02 - Snow Wonders by Myuu +citadel00 - Jingle Bells by Myuu +citadel01 - Underneath the Christmas Tree by Myuu +citadel02 - God Rest Ye Merry Gentlemen by Myuu# + +6037 endgame - Disintegrating by Myuu +escape - Deck the Halls by Myuu +hell00 - Deck the Halls by Myuu +hell01 - Santa's Tricks by Myuu +hell02 - Winter Trouble by Myuu +herxboss - Krampus is Here by Myuu# + +6038 intermission - Disintegrating by Myuu +introduction - Disintegrating by Myuu +labyrinth00 - Ice Land (Part 1) by Myuu +labyrinth01 - Holiday Macabre by Myuu +labyrinth02 - Disintegrating by Myuu +library - Santa's Tricks by Myuu# + +6039 mines00 - Sleigh Bells by Myuu +mines01 - God Rest Ye Merry Gentlemen by Myuu +mines02 - Silent Night by Myuu +mines03 - Carol of the Bells by Myuu +mines04 - Angels We Have Heard on High by Myuu +minetown - Underneath the Christmas Tree by Myuu# + +6040 minotaur00 - Krampus is Here by Myuu +minotaur01 - Deck the Halls by Myuu +ruins00 - Ice Land (Part 2) by Myuu +ruins01 - Winter Trouble by Myuu +ruins02 - Snow Wonders by Myuu +sanctum - Krampus is Here by Myuu# + +6041 shop - Sleigh Bells by Myuu +swamp00 - Jingle Bells by Myuu +swamp01 - Rudolph the Red Nosed Reindeer by Myuu +swamp02 - Dance of the Sugar Plum Fairy by Myuu +swamp03 - What Child is This by Myuu +temple - Santa's Tricks by Myuu# + +6042 underworld00 - Ice Land (Part 1) by Myuu +underworld01 - Deck the Halls by Myuu +underworld02 - Winter Trouble by Myuu# + +6043 No holiday currently active.# + +6044 Show Player Callouts# 6100 end# diff --git a/src/files.cpp b/src/files.cpp index 2df4280e6..741ad4977 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -39,17 +39,17 @@ char outputdir[PATH_MAX]; const char* holidayThemeDirs[HolidayTheme::THEME_MAX] = { "", "themes/scarony/", - "themes/christmas/" + "themes/merry/" }; #ifndef EDITOR -ConsoleVariable cvar_forceHoliday("/force_holiday", 0); +ConsoleVariable cvar_forceHoliday("/force_holiday", 2); ConsoleVariable cvar_disableHoliday("/disable_holiday", false); #endif -HolidayTheme getCurrentHoliday() { +HolidayTheme getCurrentHoliday(bool force) { #ifndef EDITOR - if (*cvar_disableHoliday) { + if (*cvar_disableHoliday && !force) { return HolidayTheme::THEME_NONE; } if (*cvar_forceHoliday) { @@ -75,8 +75,8 @@ HolidayTheme getCurrentHoliday() { } } -bool isCurrentHoliday() { - return getCurrentHoliday() != HolidayTheme::THEME_NONE; +bool isCurrentHoliday(bool force) { + return getCurrentHoliday(force) != HolidayTheme::THEME_NONE; } std::unordered_map mapHashes = { diff --git a/src/files.hpp b/src/files.hpp index 17c210b5d..e3b1ea32e 100644 --- a/src/files.hpp +++ b/src/files.hpp @@ -276,8 +276,8 @@ enum HolidayTheme { THEME_MAX }; extern const char* holidayThemeDirs[HolidayTheme::THEME_MAX]; -HolidayTheme getCurrentHoliday(); -bool isCurrentHoliday(); +HolidayTheme getCurrentHoliday(bool force = false); +bool isCurrentHoliday(bool force = false); #ifndef EDITOR #include "interface/consolecommand.hpp" diff --git a/src/interface/drawminimap.cpp b/src/interface/drawminimap.cpp index 0d84dfa70..d24f22a5a 100644 --- a/src/interface/drawminimap.cpp +++ b/src/interface/drawminimap.cpp @@ -45,12 +45,6 @@ static Mesh circle_mesh; static Mesh triangle_mesh = { { 1.f, .0f, 0.f, - -.5f, .5f, 0.f, - 0.f, .0f, 0.f, - 1.f, .0f, 0.f, - 0.f, 0.f, 0.f, - -.5f, -.5f, 0.f, - 0.f, 0.f, 0.f, -.5f, .5f, 0.f, -.5f, -.5f, 0.f, }, // positions @@ -889,6 +883,9 @@ void drawMinimap(const int player, SDL_Rect rect, bool drawingSharedMap) // draw triangle_mesh.draw(); + + // for outline: + //triangle_mesh.draw(GL_LINE_LOOP); }; const real_t size = entity->sprite == 239 ? 2.0 : 1.0; diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index f9bd06613..936201c97 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -843,6 +843,7 @@ namespace MainMenu { static void settingsVideo(Button&); static void settingsAudio(Button&); static void settingsControls(Button&); + static void settingsOnlinePopulate(Button&, bool); static void settingsOnline(Button&); static void settingsGame(Button&); @@ -1447,30 +1448,33 @@ namespace MainMenu { return field->getText(); // note: this will only be valid for one frame! } - static Frame* binaryPrompt( + static Frame* binaryPromptGeneric( const char* window_text, const char* okay_text, const char* cancel_text, void (*okay_callback)(Button&), void (*cancel_callback)(Button&), - bool leftRed = true, - bool rightRed = false + bool leftRed, + bool rightRed, + bool isSmall ) { soundActivate(); - Frame* frame = createPrompt("binary_prompt"); + Frame* frame = createPrompt("binary_prompt", isSmall); if (!frame) { return nullptr; } auto text = frame->addField("text", 1024); - text->setSize(SDL_Rect{30, 28, 304, 46}); + text->setSize(SDL_Rect{30, 28, frame->getSize().w - 60, isSmall ? 46 : 134}); text->setFont(smallfont_no_outline); text->setText(window_text); text->setJustify(Field::justify_t::CENTER); + + const int buttonsWidth = (leftRed ? 130 : 108) + (rightRed ? 130 : 108) + 4; auto okay = frame->addButton("okay"); - okay->setSize(SDL_Rect{leftRed ? 58 : 72, 78, leftRed ? 130 : 108, 52}); + okay->setSize(SDL_Rect{(frame->getSize().w - buttonsWidth) / 2, frame->getSize().h - 98, leftRed ? 130 : 108, 52}); okay->setBackground(leftRed ? "*images/ui/Main Menus/Disconnect/UI_Disconnect_Button_Abandon00.png" : "*images/ui/Main Menus/Disconnect/UI_Disconnect_Button_GoBack00.png"); @@ -1503,7 +1507,7 @@ namespace MainMenu { }); auto cancel = frame->addButton("cancel"); - cancel->setSize(SDL_Rect{leftRed ? 196 : 188, 78, rightRed ? 130 : 108, 52}); + cancel->setSize(SDL_Rect{(frame->getSize().w - buttonsWidth) / 2 + (leftRed ? 130 : 108) + 4, frame->getSize().h - 98, rightRed ? 130 : 108, 52}); cancel->setBackground(rightRed ? "*images/ui/Main Menus/Disconnect/UI_Disconnect_Button_Abandon00.png" : "*images/ui/Main Menus/Disconnect/UI_Disconnect_Button_GoBack00.png"); @@ -1527,6 +1531,32 @@ namespace MainMenu { return frame; } + static Frame* binaryPrompt( + const char* window_text, + const char* okay_text, + const char* cancel_text, + void (*okay_callback)(Button&), + void (*cancel_callback)(Button&), + bool leftRed = true, + bool rightRed = false + ) { + return binaryPromptGeneric(window_text, okay_text, cancel_text, + okay_callback, cancel_callback, leftRed, rightRed, true); + } + + static Frame* binaryPromptXL( + const char* window_text, + const char* okay_text, + const char* cancel_text, + void (*okay_callback)(Button&), + void (*cancel_callback)(Button&), + bool leftRed = true, + bool rightRed = false + ) { + return binaryPromptGeneric(window_text, okay_text, cancel_text, + okay_callback, cancel_callback, leftRed, rightRed, false); + } + static void closeBinary() { closePrompt("binary_prompt"); } @@ -4887,8 +4917,8 @@ namespace MainMenu { return result; } - static Frame* settingsSubwindowSetup(const char* tab) { - if (!video_refresh) { + static Frame* settingsSubwindowSetup(const char* tab, bool quiet) { + if (!quiet) { soundActivate(); } settings_tab_name = tab; @@ -5869,7 +5899,7 @@ namespace MainMenu { static void settingsGeneral(Button& button) { Frame* settings_subwindow; - if ((settings_subwindow = settingsSubwindowSetup(button.getName())) == nullptr) { + if ((settings_subwindow = settingsSubwindowSetup(button.getName(), false)) == nullptr) { auto settings = main_menu_frame->findFrame("settings"); assert(settings); auto settings_subwindow = settings->findFrame("settings_subwindow"); assert(settings_subwindow); settingsSelect(*settings_subwindow, {Setting::Type::Boolean, "fast_restart"}); @@ -6024,7 +6054,7 @@ namespace MainMenu { static void settingsVideo(Button& button) { Frame* settings_subwindow; - if ((settings_subwindow = settingsSubwindowSetup(button.getName())) == nullptr) { + if ((settings_subwindow = settingsSubwindowSetup(button.getName(), video_refresh != 0)) == nullptr) { auto settings = main_menu_frame->findFrame("settings"); assert(settings); auto settings_subwindow = settings->findFrame("settings_subwindow"); assert(settings_subwindow); #ifdef NINTENDO @@ -6158,7 +6188,7 @@ namespace MainMenu { static void settingsAudio(Button& button) { Frame* settings_subwindow; - if ((settings_subwindow = settingsSubwindowSetup(button.getName())) == nullptr) { + if ((settings_subwindow = settingsSubwindowSetup(button.getName(), false)) == nullptr) { auto settings = main_menu_frame->findFrame("settings"); assert(settings); auto settings_subwindow = settings->findFrame("settings_subwindow"); assert(settings_subwindow); #if defined(NINTENDO) || !defined(USE_FMOD) @@ -6287,7 +6317,7 @@ namespace MainMenu { bound_profile = profile; Frame* settings_subwindow; - if ((settings_subwindow = settingsSubwindowSetup("Controls")) == nullptr) { + if ((settings_subwindow = settingsSubwindowSetup("Controls", false)) == nullptr) { auto settings = main_menu_frame->findFrame("settings"); assert(settings); auto settings_subwindow = settings->findFrame("settings_subwindow"); assert(settings_subwindow); settingsSelect(*settings_subwindow, {Setting::Type::Customize, "bindings"}); @@ -6761,9 +6791,9 @@ namespace MainMenu { settingsControlsPopulate(player, device, profile, {Setting::Type::Dropdown, "player_dropdown_button"}); } - static void settingsOnline(Button& button) { + static void settingsOnlinePopulate(Button& button, bool quiet) { Frame* settings_subwindow; - if ((settings_subwindow = settingsSubwindowSetup(button.getName())) == nullptr) { + if ((settings_subwindow = settingsSubwindowSetup(button.getName(), quiet)) == nullptr) { auto settings = main_menu_frame->findFrame("settings"); assert(settings); auto settings_subwindow = settings->findFrame("settings_subwindow"); assert(settings_subwindow); settingsSelect(*settings_subwindow, {Setting::Type::Field, "port_number"}); @@ -6772,7 +6802,45 @@ namespace MainMenu { int y = 0; auto holiday_credits_fn = [](Button& button){ - monoPromptXL(Language::get(6028), Language::get(6029), [](Button&){soundCancel(); closeMono();}); + static int page_num, page_start; + + const auto holiday = getCurrentHoliday(); + switch (holiday) { + default: + case HolidayTheme::THEME_NONE: page_num = page_start = 6043; break; + case HolidayTheme::THEME_HALLOWEEN: page_num = page_start = 6030; break; + case HolidayTheme::THEME_XMAS: page_num = page_start = 6036; break; + } + + auto next_page_fn = [](Button& button){ + soundActivate(); + ++page_num; + + int page_limit; + const auto holiday = getCurrentHoliday(); + switch (holiday) { + default: + case HolidayTheme::THEME_NONE: page_num = page_limit = 6044; break; + case HolidayTheme::THEME_HALLOWEEN: page_limit = 6036; break; + case HolidayTheme::THEME_XMAS: page_limit = 6043; break; + } + if (page_num >= page_limit) { + page_num = page_start; + } + + auto frame = static_cast(button.getParent()); assert(frame); + auto text = frame->findField("text"); assert(text); + text->setText(Language::get(page_num)); + }; + + binaryPromptXL(Language::get(page_num), Language::get(6028), Language::get(6029), + next_page_fn, [](Button&){ + soundCancel(); + closeBinary(); + auto settings = main_menu_frame->findFrame("settings"); assert(settings); + auto settings_subwindow = settings->findFrame("settings_subwindow"); assert(settings_subwindow); + settingsSelect(*settings_subwindow, {Setting::Type::Customize, "holiday_credits"}); + }, false, true); }; y += settingsAddSubHeader(*settings_subwindow, y, "holiday_themes", Language::get(6022)); @@ -6842,14 +6910,17 @@ namespace MainMenu { {Setting::Type::Customize, "holiday_credits"}, }); #endif - - settingsSubwindowFinalize(*settings_subwindow, y, {Setting::Type::Field, "port_number"}); - settingsSelect(*settings_subwindow, {Setting::Type::Field, "port_number"}); + settingsSubwindowFinalize(*settings_subwindow, y, {Setting::Type::Boolean, "holiday_themes"}); + settingsSelect(*settings_subwindow, {Setting::Type::Boolean, "holiday_themes"}); } + + static void settingsOnline(Button& button) { + settingsOnlinePopulate(button, false); + } static void settingsGame(Button& button) { Frame* settings_subwindow; - if ((settings_subwindow = settingsSubwindowSetup(button.getName())) == nullptr) { + if ((settings_subwindow = settingsSubwindowSetup(button.getName(), false)) == nullptr) { auto settings = main_menu_frame->findFrame("settings"); assert(settings); auto settings_subwindow = settings->findFrame("settings_subwindow"); assert(settings_subwindow); settingsSelect(*settings_subwindow, {Setting::Type::Boolean, "hunger"}); @@ -22407,6 +22478,11 @@ namespace MainMenu { }); int back = c - 1 < 0 ? num_options - 1 : c - 1; int forward = c + 1 >= num_options ? 0 : c + 1; + if (!ingame && c == 0 && isCurrentHoliday(true)) { + button->setWidgetUp("holiday_banner"); + } else { + button->setWidgetUp(options[back].name); + } #ifdef NINTENDO if (ingame || c + 1 < num_options || (enabledDLCPack1 && enabledDLCPack2)) { button->setWidgetDown(options[forward].name); @@ -22420,7 +22496,6 @@ namespace MainMenu { button->setWidgetDown("banner1"); } #endif - button->setWidgetUp(options[back].name); if (!ingame) { button->setWidgetBack("back_button"); } else { @@ -22502,38 +22577,80 @@ namespace MainMenu { ); if (!ingame) { + // holiday notification + const char* holiday_banner_images[2] = { "", "" }; + const auto holiday = getCurrentHoliday(true); + switch (holiday) { + default: break; + case HolidayTheme::THEME_HALLOWEEN: + holiday_banner_images[0] = "*#images/ui/Main Menus/Banners/banner_halloween.png"; + holiday_banner_images[1] = "*#images/ui/Main Menus/Banners/banner_halloween_hover.png"; + break; + case HolidayTheme::THEME_XMAS: + holiday_banner_images[0] = "*#images/ui/Main Menus/Banners/banner_holidays.png"; + holiday_banner_images[1] = "*#images/ui/Main Menus/Banners/banner_holidays_hover.png"; + break; + } + if (holiday != HolidayTheme::THEME_NONE) { + auto notification = main_menu_frame->findFrame("notification"); assert(notification); + const int note_y = notification->getSize().y; + notification->removeSelf(); + notification = main_menu_frame->addFrame("notification"); + notification->setSize(SDL_Rect{(Frame::virtualScreenX - 472) / 2, note_y, 472, 76}); + notification->setActualSize(SDL_Rect{0, 0, notification->getSize().w, notification->getSize().h}); + + auto banner_fn = [](Button& button){ + mainSettings(button); + settingsOnlinePopulate(button, true); + }; + + auto banner = notification->addButton("holiday_banner"); + banner->setBackground(holiday_banner_images[0]); + banner->setBackgroundHighlighted(holiday_banner_images[1]); + banner->setSize(SDL_Rect{0, 0, 472, 76}); + banner->setCallback(banner_fn); + banner->setButtonsOffset(SDL_Rect{0, 8, 0, 0}); + banner->setColor(uint32ColorWhite); + banner->setHighlightColor(uint32ColorWhite); + banner->setWidgetBack("back_button"); + banner->setWidgetDown("Play Game"); +#ifdef NINTENDO + banner->setWidgetUp("Settings"); +#else + banner->setWidgetUp("Quit"); +#endif + } + #ifdef NINTENDO const char* banner_images[][2] = { { "*#images/ui/Main Menus/Banners/UI_MainMenu_QoDPatchNotes1_base.png", "*#images/ui/Main Menus/Banners/UI_MainMenu_QoDPatchNotes1_high.png", }, - { - "#images/ui/Main Menus/Banners/UI_MainMenu_DiscordLink_base.png", - "#images/ui/Main Menus/Banners/UI_MainMenu_DiscordLink_high.png", - }, }; - if (!enabledDLCPack1 && !enabledDLCPack2) { - banner_images[0][0] = "*#images/ui/Main Menus/Banners/UI_MainMenu_ComboBanner1_base.png"; - banner_images[0][1] = "*#images/ui/Main Menus/Banners/UI_MainMenu_ComboBanner1_high.png"; - } - else if (!enabledDLCPack1) { - banner_images[0][0] = "*#images/ui/Main Menus/Banners/UI_MainMenu_MnOBanner1_base.png"; - banner_images[0][1] = "*#images/ui/Main Menus/Banners/UI_MainMenu_MnOBanner1_high.png"; - } - else if (!enabledDLCPack2) { - banner_images[0][0] = "*#images/ui/Main Menus/Banners/UI_MainMenu_LnPBanner1_base.png"; - banner_images[0][1] = "*#images/ui/Main Menus/Banners/UI_MainMenu_LnPBanner1_high.png"; - } - void(*banner_funcs[])(Button&) = { - [](Button&){ // banner #1 - if (enabledDLCPack1 && enabledDLCPack2) { - openURLTryWithOverlay("https://www.baronygame.com/blog/life-after-death-announcement"); - } else { - openDLCPrompt(enabledDLCPack1 ? 1 : 0); - } + + // customize DLC banner. + { + if (!enabledDLCPack1 && !enabledDLCPack2) { + banner_images[0][0] = "*#images/ui/Main Menus/Banners/UI_MainMenu_ComboBanner1_base.png"; + banner_images[0][1] = "*#images/ui/Main Menus/Banners/UI_MainMenu_ComboBanner1_high.png"; + } + else if (!enabledDLCPack1) { + banner_images[0][0] = "*#images/ui/Main Menus/Banners/UI_MainMenu_MnOBanner1_base.png"; + banner_images[0][1] = "*#images/ui/Main Menus/Banners/UI_MainMenu_MnOBanner1_high.png"; + } + else if (!enabledDLCPack2) { + banner_images[0][0] = "*#images/ui/Main Menus/Banners/UI_MainMenu_LnPBanner1_base.png"; + banner_images[0][1] = "*#images/ui/Main Menus/Banners/UI_MainMenu_LnPBanner1_high.png"; + } + } + + void(*banner_func[])(Button&) = { + [](Button&){ + openDLCPrompt(enabledDLCPack1 ? 1 : 0); } }; + const int num_banners = (enabledDLCPack1 && enabledDLCPack2) ? 0 : 1; #else @@ -22547,18 +22664,23 @@ namespace MainMenu { "*#images/ui/Main Menus/Banners/UI_MainMenu_ComboBanner1_high.png", } }; - if ( !enabledDLCPack1 && !enabledDLCPack2 ) { - banner_images[1][0] = "*#images/ui/Main Menus/Banners/UI_MainMenu_ComboBanner1_base.png"; - banner_images[1][1] = "*#images/ui/Main Menus/Banners/UI_MainMenu_ComboBanner1_high.png"; - } - else if ( !enabledDLCPack1 ) { - banner_images[1][0] = "*#images/ui/Main Menus/Banners/UI_MainMenu_MnOBanner1_base.png"; - banner_images[1][1] = "*#images/ui/Main Menus/Banners/UI_MainMenu_MnOBanner1_high.png"; - } - else if ( !enabledDLCPack2 ) { - banner_images[1][0] = "*#images/ui/Main Menus/Banners/UI_MainMenu_LnPBanner1_base.png"; - banner_images[1][1] = "*#images/ui/Main Menus/Banners/UI_MainMenu_LnPBanner1_high.png"; - } + + // customize DLC banner. + { + if (!enabledDLCPack1 && !enabledDLCPack2) { + banner_images[1][0] = "*#images/ui/Main Menus/Banners/UI_MainMenu_ComboBanner1_base.png"; + banner_images[1][1] = "*#images/ui/Main Menus/Banners/UI_MainMenu_ComboBanner1_high.png"; + } + else if (!enabledDLCPack1) { + banner_images[1][0] = "*#images/ui/Main Menus/Banners/UI_MainMenu_MnOBanner1_base.png"; + banner_images[1][1] = "*#images/ui/Main Menus/Banners/UI_MainMenu_MnOBanner1_high.png"; + } + else if (!enabledDLCPack2) { + banner_images[1][0] = "*#images/ui/Main Menus/Banners/UI_MainMenu_LnPBanner1_base.png"; + banner_images[1][1] = "*#images/ui/Main Menus/Banners/UI_MainMenu_LnPBanner1_high.png"; + } + } + void(*banner_funcs[])(Button&) = { [](Button&) { // banner #1 openURLTryWithOverlay("https://www.baronygame.com/blog/life-after-death-announcement"); @@ -22567,6 +22689,7 @@ namespace MainMenu { openDLCPrompt(enabledDLCPack1 ? 1 : 0); }, }; + const int num_banners = (enabledDLCPack1 && enabledDLCPack2) ? 1 : sizeof(banner_funcs) / sizeof(banner_funcs[0]); #endif auto banners = main_menu_frame->addFrame("banners"); From 3bea9ed17d24f67a1d98e15ad6a996d51ea366e6 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 26 Sep 2023 10:33:18 +1000 Subject: [PATCH 046/146] * update callout binding lang entry --- src/ui/MainMenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index fe27806b7..478f07167 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -4495,7 +4495,7 @@ namespace MainMenu { int c = 5970; if ( !strcmp(binding, "Show Player Callouts") ) { - return Language::get(6022); + return Language::get(6044); } for (auto& b : defaultBindings[0].bindings) { From c4b334d01bd80afbe3dd1193c9867d72b90e3e31 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 26 Sep 2023 10:35:23 +1000 Subject: [PATCH 047/146] * fix loot bag wrong appearance for clients and secret level duplicate key --- src/interface/playerinventory.cpp | 4 ++-- src/items.cpp | 10 +++++----- src/stat.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/interface/playerinventory.cpp b/src/interface/playerinventory.cpp index 73c26a6da..79bca70bd 100644 --- a/src/interface/playerinventory.cpp +++ b/src/interface/playerinventory.cpp @@ -2224,7 +2224,7 @@ std::string getItemSpritePath(const int player, Item& item) { if ( colorblind_lobby ) { - int playerOwner = (item.appearance) % MAXPLAYERS; + int playerOwner = item.getLootBagPlayer(); Uint32 index = 4; switch ( playerOwner ) { @@ -2248,7 +2248,7 @@ std::string getItemSpritePath(const int player, Item& item) } else { - imagePathsNode = list_Node(&items[item.type].images, item.appearance % MAXPLAYERS); + imagePathsNode = list_Node(&items[item.type].images, item.getLootBagPlayer()); } } else diff --git a/src/items.cpp b/src/items.cpp index 6392080a7..f5cc14382 100644 --- a/src/items.cpp +++ b/src/items.cpp @@ -966,7 +966,7 @@ Sint32 itemModel(const Item* const item) { if ( colorblind_lobby ) { - int playerOwner = (item->appearance) % MAXPLAYERS; + int playerOwner = item->getLootBagPlayer(); Uint32 index = 4; switch ( playerOwner ) { @@ -989,7 +989,7 @@ Sint32 itemModel(const Item* const item) } else { - return items[item->type].index + item->appearance % MAXPLAYERS; + return items[item->type].index + item->getLootBagPlayer(); } } return items[item->type].index + item->appearance % items[item->type].variations; @@ -1055,7 +1055,7 @@ SDL_Surface* itemSprite(Item* const item) { if ( colorblind_lobby ) { - int playerOwner = (item->appearance) % MAXPLAYERS; + int playerOwner = item->getLootBagPlayer(); Uint32 index = 4; switch ( playerOwner ) { @@ -1078,7 +1078,7 @@ SDL_Surface* itemSprite(Item* const item) } else { - node = list_Node(&items[item->type].surfaces, (item->appearance) % MAXPLAYERS); + node = list_Node(&items[item->type].surfaces, item->getLootBagPlayer()); } } else @@ -6185,7 +6185,7 @@ void clientUnequipSlotAndUpdateServer(const int player, const EquipItemSendToSer int Item::getLootBagPlayer() const { - return (int)(appearance & 0x000000FF) % MAXPLAYERS; + return (int)(appearance & 0xF) % MAXPLAYERS; } int Item::getLootBagNumItems() const { diff --git a/src/stat.cpp b/src/stat.cpp index a7ed04b3a..dfe9d3561 100644 --- a/src/stat.cpp +++ b/src/stat.cpp @@ -1309,7 +1309,7 @@ Uint32 Stat::getLootingBagKey(const int player) { Uint32 lootingBagKey = player & 0xF; Uint16 levelKey = currentlevel & 0xFFF; - levelKey |= ((secretlevel ? 1 : 0) << 3); + levelKey |= ((secretlevel ? 1 : 0) << 11); lootingBagKey |= (levelKey << 4); return lootingBagKey; From 3abe784f581c5577fa6df946a231f6b18b51b019 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 26 Sep 2023 10:37:06 +1000 Subject: [PATCH 048/146] * ghosts can interact doors/items * fix item tooltips for ghost clients --- src/actdoor.cpp | 7 ++- src/actitem.cpp | 143 ++++++++++++++++++++++++++++++---------------- src/actplayer.cpp | 61 +++++++++++++++++++- src/net.cpp | 41 ++++++++++--- src/player.cpp | 11 ++-- src/player.hpp | 5 ++ 6 files changed, 200 insertions(+), 68 deletions(-) diff --git a/src/actdoor.cpp b/src/actdoor.cpp index 1c5a4bb7c..06bce8bad 100644 --- a/src/actdoor.cpp +++ b/src/actdoor.cpp @@ -137,21 +137,22 @@ void actDoor(Entity* my) { if ( selectedEntity[i] == my || client_selected[i] == my ) { - if ( players[i]->entity && inrange[i]) + if ( Player::getPlayerInteractEntity(i) && inrange[i]) { + Entity* playerEntity = Player::getPlayerInteractEntity(i); if ( !my->doorLocked ) // door unlocked { if ( !my->doorDir && !my->doorStatus ) { // open door - my->doorStatus = 1 + (players[i]->entity->x > my->x); + my->doorStatus = 1 + (playerEntity->x > my->x); playSoundEntity(my, 21, 96); messagePlayer(i, MESSAGE_INTERACTION, Language::get(464)); } else if ( my->doorDir && !my->doorStatus ) { // open door - my->doorStatus = 1 + (players[i]->entity->y < my->y); + my->doorStatus = 1 + (playerEntity->y < my->y); playSoundEntity(my, 21, 96); messagePlayer(i, MESSAGE_INTERACTION, Language::get(464)); } diff --git a/src/actitem.cpp b/src/actitem.cpp index 933bf7575..f99274fdf 100644 --- a/src/actitem.cpp +++ b/src/actitem.cpp @@ -124,7 +124,7 @@ void actItem(Entity* my) net_packet->len = 9; sendPacketSafe(net_sock, -1, net_packet, 0); } - else if ( my->skill[10] == 0 && my->itemReceivedDetailsFromServer == 0 && players[clientnum] && players[clientnum]->entity ) + else if ( my->skill[10] == 0 && my->itemReceivedDetailsFromServer == 0 && players[clientnum] ) { // request itemtype and beatitude if ( ticks % (TICKS_PER_SECOND * 6) == my->getUID() % (TICKS_PER_SECOND * 6) ) @@ -265,7 +265,39 @@ void actItem(Entity* my) { if ( selectedEntity[i] == my || client_selected[i] == my ) { - if ( inrange[i] && players[i] && players[i]->entity ) + if ( inrange[i] && players[i] && players[i]->ghost.isActive() ) + { + my->vel_x += 1.0 * cos(players[i]->ghost.my->yaw); + my->vel_y += 1.0 * sin(players[i]->ghost.my->yaw); + my->z = std::max(my->z - 0.1, 0.0); + my->vel_z = 2 * (-10 - local_rng.rand() % 20) * .01; + my->itemNotMoving = 0; + my->itemNotMovingClient = 0; + my->flags[USERFLAG1] = false; // enable collision + if ( multiplayer == SERVER ) + { + for ( int c = 1; c < MAXPLAYERS; c++ ) + { + if ( client_disconnected[c] || players[c]->isLocalPlayer() ) + { + continue; + } + strcpy((char*)net_packet->data, "GHOI"); + SDLNet_Write32(my->getUID(), &net_packet->data[4]); + SDLNet_Write16((Sint16)(my->x * 32), &net_packet->data[8]); + SDLNet_Write16((Sint16)(my->y * 32), &net_packet->data[10]); + SDLNet_Write16((Sint16)(my->z * 32), &net_packet->data[12]); + SDLNet_Write16((Sint16)(my->vel_x * 32), &net_packet->data[14]); + SDLNet_Write16((Sint16)(my->vel_y * 32), &net_packet->data[16]); + SDLNet_Write16((Sint16)(my->vel_z * 32), &net_packet->data[18]); + net_packet->address.host = net_clients[c - 1].host; + net_packet->address.port = net_clients[c - 1].port; + net_packet->len = 20; + sendPacketSafe(net_sock, -1, net_packet, c - 1); + } + } + } + else if ( inrange[i] && players[i] && players[i]->entity ) { bool trySalvage = false; if ( static_cast(my->itemAutoSalvageByPlayer) == players[i]->entity->getUID() ) @@ -365,68 +397,83 @@ void actItem(Entity* my) } } + my->removeLightField(); + switch ( my->sprite ) + { + case 610: // orbs (blue) + if ( !my->light ) + { + my->light = addLight(my->x / 16, my->y / 16, "orb_blue"); + } + break; + case 611: // red + if ( !my->light ) + { + my->light = addLight(my->x / 16, my->y / 16, "orb_red"); + } + break; + case 612: // purple + if ( !my->light ) + { + my->light = addLight(my->x / 16, my->y / 16, "orb_purple"); + } + break; + case 613: // green + if ( !my->light ) + { + my->light = addLight(my->x / 16, my->y / 16, "orb_green"); + } + break; + case 1206: // loot bags (yellow) + if ( !my->light ) + { + my->light = addLight(my->x / 16, my->y / 16, "lootbag_yellow"); + } + break; + case 1207: // green + if ( !my->light ) + { + my->light = addLight(my->x / 16, my->y / 16, "lootbag_green"); + } + break; + case 1208: // red + if ( !my->light ) + { + my->light = addLight(my->x / 16, my->y / 16, "lootbag_red"); + } + break; + case 1209: // pink + if ( !my->light ) + { + my->light = addLight(my->x / 16, my->y / 16, "lootbag_pink"); + } + break; + case 1210: // white + if ( !my->light ) + { + my->light = addLight(my->x / 16, my->y / 16, "lootbag_white"); + } + break; + default: + break; + } + if ( my->itemNotMoving ) { switch ( my->sprite ) { case 610: // orbs (blue) my->spawnAmbientParticles(80, my->sprite - 4, 10 + local_rng.rand() % 40, 1.0, false); - if ( !my->light ) - { - my->light = addLight(my->x / 16, my->y / 16, "orb_blue"); - } break; case 611: // red my->spawnAmbientParticles(80, my->sprite - 4, 10 + local_rng.rand() % 40, 1.0, false); - if ( !my->light ) - { - my->light = addLight(my->x / 16, my->y / 16, "orb_red"); - } break; case 612: // purple my->spawnAmbientParticles(80, my->sprite - 4, 10 + local_rng.rand() % 40, 1.0, false); - if ( !my->light ) - { - my->light = addLight(my->x / 16, my->y / 16, "orb_purple"); - } break; case 613: // green my->spawnAmbientParticles(80, my->sprite - 4, 10 + local_rng.rand() % 40, 1.0, false); - if ( !my->light ) - { - my->light = addLight(my->x / 16, my->y / 16, "orb_green"); - } break; - case 1206: // loot bags (yellow) - if ( !my->light ) - { - my->light = addLight(my->x / 16, my->y / 16, "lootbag_yellow"); - } - break; - case 1207: // green - if ( !my->light ) - { - my->light = addLight(my->x / 16, my->y / 16, "lootbag_green"); - } - break; - case 1208: // red - if ( !my->light ) - { - my->light = addLight(my->x / 16, my->y / 16, "lootbag_red"); - } - break; - case 1209: // pink - if ( !my->light ) - { - my->light = addLight(my->x / 16, my->y / 16, "lootbag_pink"); - } - break; - case 1210: // white - if ( !my->light ) - { - my->light = addLight(my->x / 16, my->y / 16, "lootbag_white"); - } - break; default: break; } diff --git a/src/actplayer.cpp b/src/actplayer.cpp index 1014c540b..092047c11 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -218,6 +218,8 @@ void Player::Ghost_t::reset() bDoingQuickTurn = false; my = nullptr; uid = 0; + + player.cleanUpOnEntityRemoval(); } bool Player::Ghost_t::allowedInteractEntity(Entity& entity) @@ -568,6 +570,63 @@ void Player::Ghost_t::handleActions() } } } + + if ( selectedEntity[player.playernum] != NULL ) + { + Entity* parent = uidToEntity(selectedEntity[player.playernum]->skill[2]); + if ( selectedEntity[player.playernum] ) + { + input.consumeBinaryToggle("Use"); + //input.consumeBindingsSharedWithBinding("Use"); + if ( entityDist(my, selectedEntity[player.playernum]) <= TOUCHRANGE ) + { + inrange[player.playernum] = true; + } + else + { + inrange[player.playernum] = false; + } + if ( multiplayer == CLIENT ) + { + if ( inrange[player.playernum] ) + { + strcpy((char*)net_packet->data, "CKIR"); + } + else + { + strcpy((char*)net_packet->data, "CKOR"); + } + net_packet->data[4] = player.playernum; + if ( selectedEntity[player.playernum]->behavior == &actPlayerLimb ) + { + SDLNet_Write32((Uint32)players[selectedEntity[player.playernum]->skill[2]]->entity->getUID(), &net_packet->data[5]); + } + else + { + Entity* tempEntity = uidToEntity(selectedEntity[player.playernum]->skill[2]); + if ( tempEntity ) + { + if ( tempEntity->behavior == &actMonster ) + { + SDLNet_Write32((Uint32)tempEntity->getUID(), &net_packet->data[5]); + } + else + { + SDLNet_Write32((Uint32)selectedEntity[player.playernum]->getUID(), &net_packet->data[5]); + } + } + else + { + SDLNet_Write32((Uint32)selectedEntity[player.playernum]->getUID(), &net_packet->data[5]); + } + } + net_packet->address.host = net_server.host; + net_packet->address.port = net_server.port; + net_packet->len = 9; + sendPacketSafe(net_sock, -1, net_packet, 0); + } + } + } } void Player::Ghost_t::handleGhostCameraUpdate(const bool useRefreshRateDelta) @@ -1125,7 +1184,7 @@ void actDeathCam(Entity* my) // deathcam if ( multiplayer != CLIENT ) { - int sprite = 1233 + (DEATHCAM_PLAYERNUM < 4 ? DEATHCAM_PLAYERNUM : 4); + int sprite = Player::Ghost_t::GHOST_MODEL_P1 + (DEATHCAM_PLAYERNUM < 4 ? DEATHCAM_PLAYERNUM : 4); Entity* entity = newEntity(sprite, 1, map.entities, nullptr); //Ghost entity. entity->x = my->x; entity->y = my->y; diff --git a/src/net.cpp b/src/net.cpp index bf34518e3..f195585ac 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1871,11 +1871,11 @@ void clientActions(Entity* entity) case 130: entity->behavior = &actGoldBag; break; - case 1233: - case 1234: - case 1235: - case 1236: - case 1237: + case Player::Ghost_t::GHOST_MODEL_P1: + case Player::Ghost_t::GHOST_MODEL_P2: + case Player::Ghost_t::GHOST_MODEL_P3: + case Player::Ghost_t::GHOST_MODEL_P4: + case Player::Ghost_t::GHOST_MODEL_PX: // player ghosts playernum = SDLNet_Read32(&net_packet->data[30]); if ( playernum >= 0 && playernum < MAXPLAYERS ) @@ -2553,6 +2553,25 @@ static std::unordered_map clientPacketHandlers = { } }}, + // ghost interact item + { 'GHOI', []() { + Uint32 uid = SDLNet_Read32(&net_packet->data[4]); + Entity* entity = uidToEntity(uid); + if ( entity ) + { + entity->itemNotMoving = 0; + entity->itemNotMovingClient = 0; + entity->flags[USERFLAG1] = false; // enable collision + + entity->x = ((Sint16)SDLNet_Read16(&net_packet->data[8])) / 32.0; + entity->y = ((Sint16)SDLNet_Read16(&net_packet->data[10])) / 32.0; + entity->z = ((Sint16)SDLNet_Read16(&net_packet->data[12])) / 32.0; + + entity->vel_x = ((Sint16)SDLNet_Read16(&net_packet->data[14])) / 32.0; + entity->vel_y = ((Sint16)SDLNet_Read16(&net_packet->data[16])) / 32.0; + entity->vel_z = ((Sint16)SDLNet_Read16(&net_packet->data[18])) / 32.0; + } + }}, // spawn an explosion {'EXPL', [](){ @@ -5068,6 +5087,10 @@ static std::unordered_map serverPacketHandlers = { statusBeatitudeQuantityAppearance |= ((static_cast(entity->skill[12]) & 0xFF) << 16); // beatitude statusBeatitudeQuantityAppearance |= ((static_cast(entity->skill[13]) & 0xFF) << 8); // quantity Uint8 appearance = entity->skill[14] % items[entity->skill[10]].variations; + if ( entity->skill[10] == TOOL_PLAYER_LOOT_BAG ) + { + appearance = (entity->skill[14] & 0xF) % items[entity->skill[10]].variations; + } statusBeatitudeQuantityAppearance |= (static_cast(appearance) & 0xFF); // appearance SDLNet_Write32(statusBeatitudeQuantityAppearance, &net_packet->data[12]); @@ -5275,7 +5298,7 @@ static std::unordered_map serverPacketHandlers = { players[player]->ghost.reset(); // deathcam - int sprite = 1233 + (player < 4 ? player : 4); + int sprite = Player::Ghost_t::GHOST_MODEL_P1 + (player < 4 ? player : 4); Entity* entity = newEntity(sprite, 1, map.entities, nullptr); //Ghost entity. players[player]->ghost.my = entity; players[player]->ghost.uid = entity->getUID(); @@ -5432,13 +5455,13 @@ static std::unordered_map serverPacketHandlers = { } else { - if ( entity && entity->behavior == &actPlayer && entity->skill[2] != pnum ) + if ( entity && (entity->behavior == &actPlayer || entity->behavior == &actDeathGhost) && entity->skill[2] != pnum ) { if ( cmd == CalloutRadialMenu::CALLOUT_CMD_LOOK || cmd == CalloutRadialMenu::CALLOUT_CMD_AFFIRMATIVE || cmd == CalloutRadialMenu::CALLOUT_CMD_NEGATIVE ) { - entity = players[pnum]->entity; + entity = Player::getPlayerInteractEntity(pnum); } else if ( cmd == CalloutRadialMenu::CALLOUT_CMD_SOUTH || cmd == CalloutRadialMenu::CALLOUT_CMD_SOUTHWEST @@ -5447,7 +5470,7 @@ static std::unordered_map serverPacketHandlers = { int toPlayer = CalloutMenu[pnum].getPlayerForDirectPlayerCmd(pnum, cmd); if ( toPlayer >= 0 ) { - entity = players[pnum]->entity; + entity = Player::getPlayerInteractEntity(pnum); } } } diff --git a/src/player.cpp b/src/player.cpp index a706b7bb2..6e9c00942 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -3277,13 +3277,6 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) return 0.0; } } - else if ( player.ghost.isActive() ) - { - if ( !player.ghost.allowedInteractEntity(*parent) ) - { - return 0.0; - } - } if ( parent->behavior == &actPlayer && player.playernum == parent->skill[2] ) { @@ -3316,6 +3309,10 @@ real_t Player::WorldUI_t::tooltipInRange(Entity& tooltip) { return 0.0; } + else if ( player.ghost.isActive() && !player.ghost.allowedInteractEntity(*parent) ) + { + return 0.0; + } if ( !selectInteract && stats[player.playernum] && stats[player.playernum]->defending && player.entity ) { diff --git a/src/player.hpp b/src/player.hpp index e8052e3d5..9a92f6ef9 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -1781,6 +1781,11 @@ class Player bool isActive() { return my != nullptr; } void reset(); bool allowedInteractEntity(Entity& entity); + static const int GHOST_MODEL_P1 = 1238; + static const int GHOST_MODEL_P2 = 1239; + static const int GHOST_MODEL_P3 = 1240; + static const int GHOST_MODEL_P4 = 1241; + static const int GHOST_MODEL_PX = 1242; } ghost; class MessageZone_t From 55e8e71f2113c726bcce4f0874ab5df2b822453e Mon Sep 17 00:00:00 2001 From: SheridanR Date: Mon, 25 Sep 2023 18:50:49 -0700 Subject: [PATCH 049/146] fix /force_holiday value Signed-off-by: SheridanR --- src/files.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/files.cpp b/src/files.cpp index 741ad4977..649fc87a3 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -43,7 +43,7 @@ const char* holidayThemeDirs[HolidayTheme::THEME_MAX] = { }; #ifndef EDITOR -ConsoleVariable cvar_forceHoliday("/force_holiday", 2); +ConsoleVariable cvar_forceHoliday("/force_holiday", 0); ConsoleVariable cvar_disableHoliday("/disable_holiday", false); #endif From afa1ec2950161345eab0763b74c16d2e35f0bde2 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 26 Sep 2023 12:21:25 +1000 Subject: [PATCH 050/146] * ghost help text shows dead rather than status effects --- src/interface/interface.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index 04ce9da4f..7ddf52a55 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -22611,6 +22611,11 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName } } + if ( stats[player]->HP == 0 || !players[player]->entity ) + { + helpText = helpDescriptors["ghost"]; + } + if ( setType == SET_CALLOUT_BANNER_TEXT ) { setCalloutBannerTextFormatted(player, field, color, highlights, textMap.bannerText.c_str(), helpText.c_str()); From 9331b5391b01fa7e7b560865338661fb06200c37 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 26 Sep 2023 12:21:39 +1000 Subject: [PATCH 051/146] * burning items stop burning out of lava --- src/actitem.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/actitem.cpp b/src/actitem.cpp index f99274fdf..46d20fc89 100644 --- a/src/actitem.cpp +++ b/src/actitem.cpp @@ -529,6 +529,8 @@ void actItem(Entity* my) } } + my->flags[BURNING] = false; + if ( my->z < groundheight ) { // fall From 6272318daac6f24e23b5c445c9481375324c73be Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 26 Sep 2023 12:22:19 +1000 Subject: [PATCH 052/146] * ghost can interact with shrines and crystals --- src/actmagictrap.cpp | 4 ++-- src/actpowercrystal.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/actmagictrap.cpp b/src/actmagictrap.cpp index 0cdcffb2b..24f4e4cf6 100644 --- a/src/actmagictrap.cpp +++ b/src/actmagictrap.cpp @@ -389,7 +389,7 @@ void Entity::actTeleportShrine() { if ( selectedEntity[i] == this || client_selected[i] == this ) { - if ( inrange[i] && players[i]->entity ) + if ( inrange[i] && Player::getPlayerInteractEntity(i) ) { if ( shrineActivateDelay > 0 ) { @@ -438,7 +438,7 @@ void Entity::actTeleportShrine() spellTimer->particleTimerCountdownSprite = 625; spellTimer->particleTimerTarget = static_cast(selectedShrine->getUID()); // get the target to teleport around. spellTimer->particleTimerVariable1 = 1; // distance of teleport in tiles - spellTimer->particleTimerVariable2 = players[i]->entity->getUID(); // which player to teleport + spellTimer->particleTimerVariable2 = Player::getPlayerInteractEntity(i)->getUID(); // which player to teleport if ( multiplayer == SERVER ) { serverSpawnMiscParticles(this, PARTICLE_EFFECT_SHRINE_TELEPORT, 625); diff --git a/src/actpowercrystal.cpp b/src/actpowercrystal.cpp index 3652db4ae..22f3f55b3 100644 --- a/src/actpowercrystal.cpp +++ b/src/actpowercrystal.cpp @@ -195,7 +195,7 @@ void Entity::actPowerCrystal() { if ( inrange[i] ) { - if ( players[i] && players[i]->entity && crystalInitialised ) + if ( players[i] && Player::getPlayerInteractEntity(i) && crystalInitialised ) { playSoundEntity(this, 151, 128); crystalTurning = 1; From ddae10c20904d6cd37f766598ac6f334e63f10ca Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 26 Sep 2023 12:23:20 +1000 Subject: [PATCH 053/146] * ghosts move through monsters * left click inspect works in ghost mode * can't right click non-ghost interactables as ghost with interact assist off --- src/actplayer.cpp | 10 ++++------ src/collision.cpp | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index 092047c11..0968261f1 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -243,6 +243,8 @@ void Player::Ghost_t::handleActions() CalloutRadialMenu& calloutMenu = CalloutMenu[player.playernum]; auto& b = (multiplayer != SINGLE && player.playernum != 0) ? Input::inputs[0].getBindings() : input.getBindings(); + clickDescription(player.playernum, NULL); // inspecting objects + if ( !calloutMenu.calloutMenuIsOpen() ) { bool clickedOnGUI = false; @@ -849,8 +851,7 @@ void actDeathGhost(Entity* my) if ( player->isLocalPlayer() ) { bool inputsEnabled = false; - if ( player->shootmode - && !players[playernum]->GUI.isGameoverActive() + if ( !players[playernum]->GUI.isGameoverActive() && players[playernum]->bControlEnabled && !players[playernum]->usingCommand() && !gamePaused ) @@ -864,10 +865,7 @@ void actDeathGhost(Entity* my) player->ghost.handleGhostCameraUpdate(false); } - if ( inputsEnabled ) - { - player->ghost.handleActions(); - } + player->ghost.handleActions(); real_t camx, camy, camz, camang, camvang; camx = my->x / 16.f; diff --git a/src/collision.cpp b/src/collision.cpp index dc97dc723..2121da62b 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -199,6 +199,16 @@ Entity* entityClicked(bool* clickedOnGUI, bool clickCheckOverride, int player, E } } } + else + { + if ( playerEntity->behavior == &actDeathGhost && entity ) + { + if ( !players[player]->ghost.allowedInteractEntity(*entity) ) + { + return nullptr; + } + } + } if ( !entity && !mute_player_monster_sounds && !clickCheckOverride && clicktype != ENTITY_CLICK_CALLOUT ) @@ -638,6 +648,10 @@ int barony_clear(real_t tx, real_t ty, Entity* my) { continue; // monsters don't have hard collision with door frames } + if ( my->behavior == &actDeathGhost && (entity->behavior == &actMonster || entity->behavior == &actPlayer) ) + { + continue; + } Stat* myStats = stats; //my->getStats(); //SEB <<< Stat* yourStats = entity->getStats(); if ( my->behavior == &actPlayer && entity->behavior == &actPlayer ) From b33fff5d95ebea7c921ecc5b733340092cb19b7d Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 26 Sep 2023 12:25:44 +1000 Subject: [PATCH 054/146] * shared minimap multiplayer /ghost mode raycasts grey map tiles --- src/draw.cpp | 72 ++++++++++++++++--- src/draw.hpp | 2 +- src/interface/drawminimap.cpp | 130 +++++++++++++++++++++------------- 3 files changed, 141 insertions(+), 63 deletions(-) diff --git a/src/draw.cpp b/src/draw.cpp index 30a98fe12..b405718bb 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -1601,7 +1601,7 @@ void drawClearBuffers() #include "net.hpp" #endif -void raycast(const view_t& camera, Sint8 (*minimap)[MINIMAP_MAX_DIMENSION]) +void raycast(const view_t& camera, Sint8 (*minimap)[MINIMAP_MAX_DIMENSION], bool fillWithColor) { // originally we cast a ray for every column of pixels in the // camera viewport. now we just shoot out a few hundred rays to @@ -1647,6 +1647,7 @@ void raycast(const view_t& camera, Sint8 (*minimap)[MINIMAP_MAX_DIMENSION]) const Sint32* tiles; const vec4_t* lights; Sint8 (*minimap)[MINIMAP_MAX_DIMENSION]; + bool fillWithColor; }; auto shoot_ray = [](const ins_t&& ins) -> std::vector{ std::vector result; @@ -1740,15 +1741,39 @@ void raycast(const view_t& camera, Sint8 (*minimap)[MINIMAP_MAX_DIMENSION]) } auto& l = lights[iny2 + inx2 * mh]; const auto light = std::max({0.f, l.x, l.y, l.z}); + bool visible = light > 1.f; + if ( !visible && !ins.fillWithColor ) + { + // remote players fill in the map within 3x3 area, as they do not have ambient light + real_t dist = pow(posx - inx2, 2) + pow(posy - iny2, 2); + if ( dist < 10.0 ) + { + visible = true; + } + } // update minimap if (d < 16 && z == OBSTACLELAYER) { - if (light > 1.f) { + if ( visible ) { // wall space if (WriteOutsSequentially) { - result.push_back({inx, iny, 2}); + if ( ins.fillWithColor ) + { + result.push_back({inx, iny, 2}); + } + else if ( minimap[iny][inx] != 2 ) + { + result.push_back({inx, iny, 4}); + } } else { - minimap[iny][inx] = 2; + if ( ins.fillWithColor ) + { + minimap[iny][inx] = 2; + } + else if ( minimap[iny][inx] != 2 ) + { + minimap[iny][inx] = 4; + } } } } @@ -1756,21 +1781,46 @@ void raycast(const view_t& camera, Sint8 (*minimap)[MINIMAP_MAX_DIMENSION]) // update minimap to show empty region auto& l = lights[iny2 + inx2 * mh]; const auto light = std::max({0.f, l.x, l.y, l.z}); + bool visible = light > 1.f; + if ( !visible && !ins.fillWithColor ) + { + // remote players fill in the map within 3x3 area, as they do not have ambient light + real_t dist = pow(posx - inx2, 2) + pow(posy - iny2, 2); + if ( dist < 10.0 ) + { + visible = true; + } + } + if (d < 16) { - if (light > 1.f && tiles[iny * MAPLAYERS + inx * MAPLAYERS * mh]) { + if ( visible && tiles[iny * MAPLAYERS + inx * MAPLAYERS * mh]) { // walkable space if (WriteOutsSequentially) { - result.push_back({inx, iny, 1}); + if ( ins.fillWithColor ) + { + result.push_back({inx, iny, 1}); + } + else if ( minimap[iny][inx] != 1 ) + { + result.push_back({inx, iny, 3}); + } } else { - minimap[iny][inx] = 1; + if ( ins.fillWithColor ) + { + minimap[iny][inx] = 1; + } + else if ( minimap[iny][inx] != 1 ) + { + minimap[iny][inx] = 3; + } } } else if (tiles[z + iny * MAPLAYERS + inx * MAPLAYERS * mh]) { // no floor - if (WriteOutsSequentially) { + /*if (WriteOutsSequentially) { result.push_back({inx, iny, 0}); } else { minimap[iny][inx] = 0; - } + }*/ } } } @@ -1806,7 +1856,7 @@ void raycast(const view_t& camera, Sint8 (*minimap)[MINIMAP_MAX_DIMENSION]) std::vector>> tasks; for (int x = 0; x < NumRays; x += NumRaysPerJob) { tasks.emplace_back(std::async(std::launch::async, shoot_ray, - ins_t{x, (int)map.width, (int)map.height, camera, map.tiles, lightmap, minimap})); + ins_t{x, (int)map.width, (int)map.height, camera, map.tiles, lightmap, minimap, fillWithColor})); } for (int x = (int)tasks.size() - 1; x >= 0; --x) { auto out_list = tasks[x].get(); @@ -1817,7 +1867,7 @@ void raycast(const view_t& camera, Sint8 (*minimap)[MINIMAP_MAX_DIMENSION]) } } else { for (int x = 0; x < NumRays; x += NumRaysPerJob) { - auto out_list = shoot_ray(ins_t{x, (int)map.width, (int)map.height, camera, map.tiles, lightmap, minimap}); + auto out_list = shoot_ray(ins_t{x, (int)map.width, (int)map.height, camera, map.tiles, lightmap, minimap, fillWithColor}); for (auto& it : out_list) { minimap[it.y][it.x] = it.value; } diff --git a/src/draw.hpp b/src/draw.hpp index 611107c48..2b842e5a2 100644 --- a/src/draw.hpp +++ b/src/draw.hpp @@ -260,7 +260,7 @@ void drawLayer(long camx, long camy, int z, map_t* map); void drawBackground(long camx, long camy); void drawForeground(long camx, long camy); void drawClearBuffers(); -void raycast(const view_t& camera, Sint8 (*minimap)[MINIMAP_MAX_DIMENSION]); +void raycast(const view_t& camera, Sint8 (*minimap)[MINIMAP_MAX_DIMENSION], bool fillWithColor); void drawFloors(view_t* camera); void drawSky(SDL_Surface* srfc); void drawVoxel(view_t* camera, Entity* entity); diff --git a/src/interface/drawminimap.cpp b/src/interface/drawminimap.cpp index 76e67d827..e299b2c34 100644 --- a/src/interface/drawminimap.cpp +++ b/src/interface/drawminimap.cpp @@ -45,12 +45,6 @@ static Mesh circle_mesh; static Mesh triangle_mesh = { { 1.f, .0f, 0.f, - -.5f, .5f, 0.f, - 0.f, .0f, 0.f, - 1.f, .0f, 0.f, - 0.f, 0.f, 0.f, - -.5f, -.5f, 0.f, - 0.f, 0.f, 0.f, -.5f, .5f, 0.f, -.5f, -.5f, 0.f, }, // positions @@ -821,6 +815,55 @@ void drawMinimap(const int player, SDL_Rect rect, bool drawingSharedMap) } } + auto drawTriangle = [](real_t x, real_t y, real_t ang, real_t size, SDL_Rect rect, Uint32 color, bool outlineOnly) { + const int mapGCD = std::max(map.width, map.height); + const int xmin = ((int)map.width - mapGCD) / 2; + const int ymin = ((int)map.height - mapGCD) / 2; + const real_t unitX = (real_t)rect.w / (real_t)mapGCD; + const real_t unitY = (real_t)rect.h / (real_t)mapGCD; + const real_t zoom = getMinimapZoom() / 100.0 * size; + x = (x - xmin) * unitX + rect.x; + y = (y - ymin) * unitY + rect.y; + + // bind shader + auto& shader = minimap_shader; + shader.bind(); + + // upload color + Uint8 r, g, b, a; + getColor(color, &r, &g, &b, &a); + float cv[] = { r / 255.f, g / 255.f, b / 255.f, a / 255.f }; + GL_CHECK_ERR(glUniform4fv(shader.uniform("uColor"), 1, cv)); + + vec4_t v; + mat4x4 m; + + // projection matrix + mat4x4 proj(1.f); + (void)ortho(&proj, 0, Frame::virtualScreenX, 0, Frame::virtualScreenY, -1.f, 1.f); + GL_CHECK_ERR(glUniformMatrix4fv(shader.uniform("uProj"), 1, GL_FALSE, (float*)&proj)); + + // view matrix + mat4x4 view(1.f); + v = { (float)x, (float)(Frame::virtualScreenY - y), 0.f, 0.f }; + (void)translate_mat(&m, &view, &v); view = m; + v = { (float)(unitX * zoom), (float)(unitY * zoom), 0.f, 0.f }; + (void)scale_mat(&m, &view, &v); view = m; + v = { 0.f, 0.f, -1.f, 0.f }; + (void)rotate_mat(&m, &view, ang * 180.f / PI, &v); view = m; + GL_CHECK_ERR(glUniformMatrix4fv(shader.uniform("uView"), 1, GL_FALSE, (float*)&view)); + + // draw + if ( outlineOnly ) + { + triangle_mesh.draw(GL_LINE_LOOP); + } + else + { + triangle_mesh.draw(); + } + }; + // draw minotaur, players, and allies for ( int c = 0; c < 2; ++c ) { @@ -946,61 +989,46 @@ void drawMinimap(const int player, SDL_Rect rect, bool drawingSharedMap) triangle_mesh.init(); } - auto drawTriangle = [](real_t x, real_t y, real_t ang, real_t size, SDL_Rect rect, Uint32 color){ - const int mapGCD = std::max(map.width, map.height); - const int xmin = ((int)map.width - mapGCD) / 2; - const int ymin = ((int)map.height - mapGCD) / 2; - const real_t unitX = (real_t)rect.w / (real_t)mapGCD; - const real_t unitY = (real_t)rect.h / (real_t)mapGCD; - const real_t zoom = getMinimapZoom() / 100.0 * size; - x = (x - xmin) * unitX + rect.x; - y = (y - ymin) * unitY + rect.y; - - // bind shader - auto& shader = minimap_shader; - shader.bind(); - - // upload color - Uint8 r, g, b, a; - getColor(color, &r, &g, &b, &a); - float cv[] = {r / 255.f, g / 255.f, b / 255.f, a / 255.f}; - GL_CHECK_ERR(glUniform4fv(shader.uniform("uColor"), 1, cv)); - - vec4_t v; - mat4x4 m; - - // projection matrix - mat4x4 proj(1.f); - (void)ortho(&proj, 0, Frame::virtualScreenX, 0, Frame::virtualScreenY, -1.f, 1.f); - GL_CHECK_ERR(glUniformMatrix4fv(shader.uniform("uProj"), 1, GL_FALSE, (float*)&proj)); - - // view matrix - mat4x4 view(1.f); - v = {(float)x, (float)(Frame::virtualScreenY - y), 0.f, 0.f}; - (void)translate_mat(&m, &view, &v); view = m; - v = {(float)(unitX * zoom), (float)(unitY * zoom), 0.f, 0.f}; - (void)scale_mat(&m, &view, &v); view = m; - v = {0.f, 0.f, -1.f, 0.f}; - (void)rotate_mat(&m, &view, ang * 180.f / PI, &v); view = m; - GL_CHECK_ERR(glUniformMatrix4fv(shader.uniform("uView"), 1, GL_FALSE, (float*)&view)); - - // draw - triangle_mesh.draw(); - }; - const real_t size = entity->sprite == 239 ? 2.0 : 1.0; const real_t x = entity->x / 16.0; const real_t y = entity->y / 16.0; const real_t ang = entity->yaw; if (*cvar_outlineTriangles) { - drawTriangle(x, y, ang, size, rect, color_edge); - drawTriangle(x, y, ang, size - 0.25, rect, color); + drawTriangle(x, y, ang, size, rect, color, true); } else { - drawTriangle(x, y, ang, size, rect, color); + drawTriangle(x, y, ang, size, rect, color, false); } } } } + + // draw ghosts + for ( int i = 0; i < MAXPLAYERS; ++i ) + { + Entity* entity = nullptr; + if ( players[i] && players[i]->ghost.isActive() ) + { + entity = players[i]->ghost.my; + } + if ( !entity ) + { + continue; + } + if ( blinkPlayers[i] ) + { + continue; // skip this one since it's blinking from a callout + } + Uint32 color = playerColor(i, colorblind_lobby, false); + if ( !triangle_mesh.isInitialized() ) { + triangle_mesh.init(); + } + + const real_t size = entity->sprite == 239 ? 2.0 : 1.0; + const real_t x = entity->x / 16.0; + const real_t y = entity->y / 16.0; + const real_t ang = entity->yaw; + drawTriangle(x, y, ang, size, rect, color, true); + } } void minimapPingAdd(const int srcPlayer, const int destPlayer, MinimapPing newPing) From 85cd8edc5978072650a85d99dda8ef38c5d616c1 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 26 Sep 2023 12:25:57 +1000 Subject: [PATCH 055/146] * fix ghost not being able to interact after dying and charging hud weapon --- src/player.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/player.cpp b/src/player.cpp index 6e9c00942..e92a021a3 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -4231,7 +4231,7 @@ void Player::WorldUI_t::handleTooltips() bool bDoingActionHideTooltips = false; if ( players[player]->hotbar.useHotbarFaceMenu - && players[player]->hotbar.faceMenuButtonHeld != Player::Hotbar_t::GROUP_NONE ) + && players[player]->hotbar.faceMenuButtonHeld != Player::Hotbar_t::GROUP_NONE && players[player]->entity ) { bDoingActionHideTooltips = true; } @@ -4239,20 +4239,20 @@ void Player::WorldUI_t::handleTooltips() { bDoingActionHideTooltips = true; } - else if ( FollowerMenu[player].selectMoveTo && FollowerMenu[player].optionSelected == ALLY_CMD_MOVETO_SELECT ) + else if ( FollowerMenu[player].selectMoveTo && FollowerMenu[player].optionSelected == ALLY_CMD_MOVETO_SELECT && players[player]->entity ) { bDoingActionHideTooltips = true; } - else if ( (players[player]->hud.weapon && players[player]->hud.weapon->skill[0] != 0) && !selectInteract ) + else if ( (players[player]->hud.weapon && players[player]->hud.weapon->skill[0] != 0) && !selectInteract && players[player]->entity ) { // hudweapon chop bDoingActionHideTooltips = true; } - else if ( (players[player]->hud.bowFire || players[player]->hud.bowIsBeingDrawn) && !selectInteract ) + else if ( (players[player]->hud.bowFire || players[player]->hud.bowIsBeingDrawn) && !selectInteract && players[player]->entity ) { bDoingActionHideTooltips = true; } - else if ( (cast_animation[player].active || cast_animation[player].active_spellbook) && !selectInteract ) + else if ( (cast_animation[player].active || cast_animation[player].active_spellbook) && !selectInteract && players[player]->entity ) { // spells bDoingActionHideTooltips = true; From 6874c4abcffdbfe7349187b5643434c2d6f17ce2 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 26 Sep 2023 12:27:45 +1000 Subject: [PATCH 056/146] * shared minimap in online multiplayer is default * ghosts uncover minimap in grey * fix dying while blind not altering camera blackness to normals --- src/game.cpp | 74 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 34909f0c1..74665d7a1 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5933,6 +5933,31 @@ void drawAllPlayerCameras() { occlusionCulling(map, camera); glBeginCamera(&camera, true); + // shared minimap progress + if ( !splitscreen/*gameplayCustomManager.inUse() && gameplayCustomManager.minimapShareProgress && !splitscreen*/ ) + { + for ( int i = 0; i < MAXPLAYERS; ++i ) + { + if ( i != clientnum && players[i] && Player::getPlayerInteractEntity(i) ) + { + if ( !players[i]->entity || (players[i]->entity && !players[i]->entity->isBlind()) ) + { + real_t x = camera.x; + real_t y = camera.y; + real_t ang = camera.ang; + + camera.x = Player::getPlayerInteractEntity(i)->x / 16.0; + camera.y = Player::getPlayerInteractEntity(i)->y / 16.0; + camera.ang = Player::getPlayerInteractEntity(i)->yaw; + raycast(camera, minimap, false); // update minimap from other players' perspectives, player or ghost + camera.x = x; + camera.y = y; + camera.ang = ang; + } + } + } + } + if ( players[c] && players[c]->entity ) { if ( players[c]->entity->isBlind() ) @@ -6002,37 +6027,42 @@ void drawAllPlayerCameras() { DebugStats.drawWorldT3 = std::chrono::high_resolution_clock::now(); if ( !players[c]->entity->isBlind() ) { - raycast(camera, minimap); // update minimap + raycast(camera, minimap, true); // update minimap } DebugStats.drawWorldT4 = std::chrono::high_resolution_clock::now(); glDrawWorld(&camera, REALCOLORS); - - if ( gameplayCustomManager.inUse() && gameplayCustomManager.minimapShareProgress && !splitscreen ) + } + else + { + // undo blindness effects + if ( globalLightModifierActive == GLOBAL_LIGHT_MODIFIER_INUSE ) { - for ( int i = 0; i < MAXPLAYERS; ++i ) + for ( node_t* mapNode = map.creatures->first; mapNode != nullptr; mapNode = mapNode->next ) { - if ( i != clientnum && players[i] && players[i]->entity ) + Entity* mapCreature = (Entity*)mapNode->element; + if ( mapCreature ) { - if ( !players[i]->entity->isBlind() ) - { - real_t x = camera.x; - real_t y = camera.y; - real_t ang = camera.ang; - - camera.x = players[i]->entity->x / 16.0; - camera.y = players[i]->entity->y / 16.0; - camera.ang = players[i]->entity->yaw; - raycast(camera, minimap); // update minimap from other players' perspectives - camera.x = x; - camera.y = y; - camera.ang = ang; - } + mapCreature->monsterEntityRenderAsTelepath = 0; } } } - } - else - { + globalLightModifierActive = GLOBAL_LIGHT_MODIFIER_DISSIPATING; + globalLightModifierEntities = 0.f; + if ( globalLightModifier < 1.f ) + { + globalLightModifier += 0.01; + } + else + { + globalLightModifier = 1.01; + globalLightModifierActive = GLOBAL_LIGHT_MODIFIER_STOPPED; + } + + if ( players[c] && players[c]->ghost.isActive() ) + { + raycast(camera, minimap, false); // update minimap for ghost + } + // player is dead, spectate glDrawWorld(&camera, REALCOLORS); } From 7164fd998803410ed72246333d31f958d902b3eb Mon Sep 17 00:00:00 2001 From: SheridanR Date: Mon, 25 Sep 2023 19:33:43 -0700 Subject: [PATCH 057/146] fix lang issue Signed-off-by: SheridanR --- lang/en.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/en.txt b/lang/en.txt index de5dda9f3..e1bfb1962 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -6984,7 +6984,7 @@ Complexity:# 5434 Enter a name for Player %d# 5435 View Game Settings# 5436 *** Press [LB] to suggest a class for your party ***# -5437 *** Press [ZL] to suggest a class for your party ***# +5437 *** Press [L] to suggest a class for your party ***# 5438 *** Press [L1] to suggest a class for your party ***# 5439 *** Press [L1] to suggest a class for your party ***# 5440 Ready# From bb3967e18500d4884e4e307d9386bd91f1eb5fa2 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Tue, 26 Sep 2023 18:22:07 -0700 Subject: [PATCH 058/146] loading holiday skins is considerably faster Signed-off-by: SheridanR --- src/files.cpp | 245 ++++++++++++++++----------------------- src/files.hpp | 2 +- src/init.cpp | 1 - src/menu.cpp | 2 +- src/mod_tools.cpp | 223 ++++++++++++++++++----------------- src/mod_tools.hpp | 1 - src/ui/LoadingScreen.cpp | 6 - src/ui/MainMenu.cpp | 2 +- 8 files changed, 220 insertions(+), 262 deletions(-) diff --git a/src/files.cpp b/src/files.cpp index 649fc87a3..a2293f848 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -1464,12 +1464,8 @@ DIR* openDataDir(const char * const name) { bool dataPathExists(const char * const path, bool complete) { -#ifdef NINTENDO - // TODO for themes!!! - return true; -#endif if (complete) { - char full_path[PATH_MAX]; + char full_path[PATH_MAX]; completePath(full_path, path); return access(full_path, F_OK) != -1; } else { @@ -3018,7 +3014,7 @@ bool physfsSearchModelsToUpdate() return false; } -bool physfsModelIndexUpdate(int &start, int &end, bool freePreviousModels) +bool physfsModelIndexUpdate(int &start, int &end) { if ( !PHYSFS_getRealDir("models/models.txt") ) { @@ -3026,128 +3022,99 @@ bool physfsModelIndexUpdate(int &start, int &end, bool freePreviousModels) return false; } - std::atomic_bool loading_done{ false }; - auto loading_task = std::async(std::launch::async, [&loading_done, &start, &end]() { - std::string modelsDirectory = PHYSFS_getRealDir("models/models.txt"); - modelsDirectory.append(PHYSFS_getDirSeparator()).append("models/models.txt"); + std::string modelsDirectory = PHYSFS_getRealDir("models/models.txt"); + modelsDirectory.append(PHYSFS_getDirSeparator()).append("models/models.txt"); - File* fp = openDataFile(modelsDirectory.c_str(), "rb"); - if ( !fp ) - { - loading_done = true; - return 0; - } - char modelName[PATH_MAX]; - int startnum = 0; - int endnum = nummodels; - for ( int c = 0; !fp->eof(); c++ ) + File* fp = openDataFile(modelsDirectory.c_str(), "rb"); + if ( !fp ) + { + return false; + } + char modelName[PATH_MAX]; + int startnum = 0; + int endnum = nummodels; + for ( int c = 0; !fp->eof(); c++ ) + { + fp->gets2(modelName, PATH_MAX); + bool modelHasBeenModified = false; + // has this model index been modified? + std::vector::iterator it = Mods::modelsListModifiedIndexes.end(); + if ( !Mods::modelsListModifiedIndexes.empty() ) { - fp->gets2(modelName, PATH_MAX); - bool modelHasBeenModified = false; - // has this model index been modified? - std::vector::iterator it = Mods::modelsListModifiedIndexes.end(); - if ( !Mods::modelsListModifiedIndexes.empty() ) + it = std::find(Mods::modelsListModifiedIndexes.begin(), + Mods::modelsListModifiedIndexes.end(), c); + if ( it != Mods::modelsListModifiedIndexes.end() ) { - it = std::find(Mods::modelsListModifiedIndexes.begin(), - Mods::modelsListModifiedIndexes.end(), c); - if ( it != Mods::modelsListModifiedIndexes.end() ) - { - modelHasBeenModified = true; // found the model in the vector. - } + modelHasBeenModified = true; // found the model in the vector. } + } - if ( !PHYSFS_getRealDir(modelName) ) + if ( !PHYSFS_getRealDir(modelName) ) + { + printlog("error: could not find file: %s", modelName); + continue; + } + std::string modelPath = PHYSFS_getRealDir(modelName); + if ( modelHasBeenModified || modelPath.compare("./") != 0 ) + { + if ( !modelHasBeenModified ) { - printlog("error: could not find file: %s", modelName); - continue; + // add this model index to say we've modified it as the base dir is not default. + Mods::modelsListModifiedIndexes.push_back(c); } - std::string modelPath = PHYSFS_getRealDir(modelName); - if ( modelHasBeenModified || modelPath.compare("./") != 0 ) + else { - if ( !modelHasBeenModified ) - { - // add this model index to say we've modified it as the base dir is not default. - Mods::modelsListModifiedIndexes.push_back(c); - } - else + if ( modelPath.compare("./") == 0 ) { - if ( modelPath.compare("./") == 0 ) - { - // model returned to base directory, remove from the modified index list. - Mods::modelsListModifiedIndexes.erase(it); - } + // model returned to base directory, remove from the modified index list. + Mods::modelsListModifiedIndexes.erase(it); } + } - if ( c < nummodels ) + if ( c < nummodels ) + { + if ( models[c] != NULL ) { - if ( models[c] != NULL ) + if ( models[c]->data ) { - if ( models[c]->data ) - { - free(models[c]->data); - } - free(models[c]); + free(models[c]->data); } - } - else - { - printlog("[PhysFS]: WARNING: Loading a new model: %d outside normal nummodels: %d range - Need special handling case to free model after use", c, nummodels); - } - models[c] = loadVoxel(modelName); - - // this index is not found in the normal models folder. - // store the lowest found model number inside startnum. - if ( startnum == 0 || c < startnum ) - { - startnum = c; - } - - // store the higher end model num in endnum. - if ( endnum == nummodels ) - { - endnum = c + 1; - } - else if ( c + 1 > endnum ) - { - endnum = c + 1; + free(models[c]); } } - } - if ( startnum == endnum ) - { - endnum = std::min(static_cast(nummodels), endnum + 1); // if both indices are the same, then models won't load. - } - printlog("[PhysFS]: Models file not in default directory... reloading models from index %d to %d\n", startnum, endnum); - start = startnum; - end = endnum; - - FileIO::close(fp); - loading_done = true; - return 0; - }); - while ( !loading_done ) - { - doLoadingScreen(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } + else + { + printlog("[PhysFS]: WARNING: Loading a new model: %d outside normal nummodels: %d range - Need special handling case to free model after use", c, nummodels); + } + models[c] = loadVoxel(modelName); - // now free polymodels as we'll be loading them up later. - if ( freePreviousModels ) { - for (int c = start; c < end && c < nummodels; ++c) { - if ( polymodels[c].faces ) { - free(polymodels[c].faces); + // this index is not found in the normal models folder. + // store the lowest found model number inside startnum. + if ( startnum == 0 || c < startnum ) + { + startnum = c; } - if ( polymodels[c].vao ) { - GL_CHECK_ERR(glDeleteVertexArrays(1, &polymodels[c].vao)); - } - if ( polymodels[c].vbo ) { - GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].vbo)); + + // store the higher end model num in endnum. + if ( endnum == nummodels ) + { + endnum = c + 1; } - if ( polymodels[c].colors ) { - GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].colors)); + else if ( c + 1 > endnum ) + { + endnum = c + 1; } } } + if ( startnum == endnum ) + { + endnum = std::min(static_cast(nummodels), endnum + 1); // if both indices are the same, then models won't load. + } + printlog("[PhysFS]: Models file not in default directory... reloading models from index %d to %d\n", startnum, endnum); + start = startnum; + end = endnum; + + FileIO::close(fp); return true; } @@ -3160,6 +3127,30 @@ bool physfsModelIndexUpdate(int &start, int &end, bool freePreviousModels) -------------------------------------------------------------------------------*/ +void saveModelCache() { + File* model_cache; + const std::string cache_path = std::string(outputdir) + "/models.cache"; + if (model_cache = openDataFile(cache_path.c_str(), "wb")) { + char modelCacheHeader[32] = "BARONY"; + strcat(modelCacheHeader, VERSION); + model_cache->write(&modelCacheHeader, sizeof(char), strlen(modelCacheHeader)); + for (size_t model_index = 0; model_index < nummodels; model_index++) { + polymodel_t* cur = &polymodels[model_index]; + model_cache->write(&cur->numfaces, sizeof(cur->numfaces), 1); + model_cache->write(cur->faces, sizeof(polytriangle_t), cur->numfaces); + } + FileIO::close(model_cache); + } +} + +#ifndef EDITOR +#include "interface/consolecommand.hpp" +static ConsoleCommand ccmd_writeModelCache("/write_model_cache", "", + [](int argc, const char** argv){ + saveModelCache(); + }); +#endif + void generatePolyModels(int start, int end, bool forceCacheRebuild) { Sint32 x, y, z; @@ -3215,10 +3206,7 @@ void generatePolyModels(int start, int end, bool forceCacheRebuild) if ( !forceCacheRebuild ) { for ( size_t model_index = 0; model_index < nummodels; model_index++ ) { - if ( !Mods::isLoading ) - { - updateLoadingScreen(30 + ((real_t)model_index / nummodels) * 30.0); - } + updateLoadingScreen(30 + ((real_t)model_index / nummodels) * 30.0); polymodel_t* cur = &polymodels[model_index]; model_cache->read(&cur->numfaces, sizeof(cur->numfaces), 1); cur->faces = (polytriangle_t*)calloc(sizeof(polytriangle_t), cur->numfaces); @@ -3240,14 +3228,7 @@ void generatePolyModels(int start, int end, bool forceCacheRebuild) for ( c = start; c < end; ++c ) { - if ( !Mods::isLoading ) - { - updateLoadingScreen(30 + ((real_t)(c - start) / (end - start)) * 30.0); - } - else - { - doLoadingScreen(); - } + updateLoadingScreen(30 + ((real_t)(c - start) / (end - start)) * 30.0); numquads = 0; polymodels[c].numfaces = 0; voxel_t* model = models[c]; @@ -4160,21 +4141,8 @@ void generatePolyModels(int start, int end, bool forceCacheRebuild) list_FreeAll(&quads); } #ifndef NINTENDO - if (!isCurrentHoliday()) { - std::string cache_path = std::string(outputdir) + "/models.cache"; - if ( useModelCache && (model_cache = openDataFile(cache_path.c_str(), "wb")) ) - { - char modelCacheHeader[32] = "BARONY"; - strcat(modelCacheHeader, VERSION); - model_cache->write(&modelCacheHeader, sizeof(char), strlen(modelCacheHeader)); - for ( size_t model_index = 0; model_index < nummodels; model_index++ ) - { - polymodel_t* cur = &polymodels[model_index]; - model_cache->write(&cur->numfaces, sizeof(cur->numfaces), 1); - model_cache->write(cur->faces, sizeof(polytriangle_t), cur->numfaces); - } - FileIO::close(model_cache); - } + if (!isCurrentHoliday() && useModelCache) { + saveModelCache(); } #endif } @@ -4327,10 +4295,7 @@ void generateVBOs(int start, int end) #endif const int current = (int)c - start; - if ( !Mods::isLoading ) - { - updateLoadingScreen(80 + (10 * current) / count); - } + updateLoadingScreen(80 + (10 * current) / count); doLoadingScreen(); } } @@ -4467,11 +4432,7 @@ void physfsReloadSounds(bool reloadAll) OPENAL_Sound_Release(sounds[c]); } OPENAL_CreateSound(soundFile.c_str(), true, &sounds[c]); -#endif - if ( Mods::isLoading ) - { - doLoadingScreen(); - } +#endif } } } diff --git a/src/files.hpp b/src/files.hpp index e3b1ea32e..d35030dbd 100644 --- a/src/files.hpp +++ b/src/files.hpp @@ -307,7 +307,7 @@ int loadMainMenuMap(bool blessedAdditionMaps, bool forceVictoryMap, int forcemap int physfsLoadMapFile(int levelToLoad, Uint32 seed, bool useRandSeed, int *checkMapHash = nullptr); std::list physfsGetFileNamesInDirectory(const char* dir); std::string physfsFormatMapName(char const * const levelfilename); -bool physfsModelIndexUpdate(int &start, int &end, bool freePreviousModels); +bool physfsModelIndexUpdate(int &start, int &end); bool physfsSearchModelsToUpdate(); bool physfsSearchSoundsToUpdate(); void physfsReloadSounds(bool reloadAll); diff --git a/src/init.cpp b/src/init.cpp index 7d3f7331a..c33c8fdf0 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -58,7 +58,6 @@ static ConsoleVariable cvar_sdl_disablejoystickrawinput("/sdl_joystick_raw bool mountBaseDataFolders() { if (isCurrentHoliday()) { - useModelCache = false; // don't use model cache on holidays. const auto holiday = getCurrentHoliday(); const auto holiday_dir = holidayThemeDirs[holiday]; const auto holiday_dir_str = (std::string(datadir) + "/") + holiday_dir; diff --git a/src/menu.cpp b/src/menu.cpp index 9f6af34cc..bbf2f5562 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -1910,7 +1910,7 @@ static void handleMainMenu(bool mode) ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, Language::get(2990)); GO_SwapBuffers(screen); - physfsModelIndexUpdate(modelsIndexUpdateStart, modelsIndexUpdateEnd, true); + physfsModelIndexUpdate(modelsIndexUpdateStart, modelsIndexUpdateEnd); generatePolyModels(modelsIndexUpdateStart, modelsIndexUpdateEnd, false); generateVBOs(modelsIndexUpdateStart, modelsIndexUpdateEnd); gamemods_modelsListLastStartedUnmodded = true; diff --git a/src/mod_tools.cpp b/src/mod_tools.cpp index b4df1bdb1..af2ef6b0e 100644 --- a/src/mod_tools.cpp +++ b/src/mod_tools.cpp @@ -7836,7 +7836,6 @@ bool Mods::customContentLoadedFirstTime = false; bool Mods::disableSteamAchievements = false; bool Mods::lobbyDisableSteamAchievements = false; bool Mods::isLoading = false; -Uint32 Mods::loadingTicks = 0; void Mods::updateModCounts() { mods_loaded_local.clear(); @@ -8271,20 +8270,15 @@ void Mods::unloadMods(bool force) { #ifndef EDITOR isLoading = true; - loadingTicks = 0; - loading = true; createLoadingScreen(5); doLoadingScreen(); + // start loading mountedFilepathsSaved = mountedFilepaths; clearAllMountedPaths(); mountedFilepaths.clear(); Mods::disableSteamAchievements = false; - - updateLoadingScreen(10); - doLoadingScreen(); - if (force) { modelsListModifiedIndexes.clear(); for (int c = 0; c < nummodels; ++c) { @@ -8296,138 +8290,143 @@ void Mods::unloadMods(bool force) for (const auto& pair : systemResourceImages) { Mods::systemResourceImagesToReload.push_back(pair); } + Mods::tileListRequireReloadUnmodded = true; + Mods::modelsListRequiresReloadUnmodded = true; + Mods::spriteImagesRequireReloadUnmodded = true; + Mods::musicRequireReloadUnmodded = true; + Mods::langRequireReloadUnmodded = true; + Mods::monsterLimbsRequireReloadUnmodded = true; + Mods::systemImagesReloadUnmodded = true; } - - if ( force || Mods::modelsListRequiresReloadUnmodded || !Mods::modelsListModifiedIndexes.empty() ) - { - int modelsIndexUpdateStart = 1; - int modelsIndexUpdateEnd = nummodels; - physfsModelIndexUpdate(modelsIndexUpdateStart, modelsIndexUpdateEnd, true); - bool oldModelCache = useModelCache; - useModelCache = false; - //loadModels(modelsIndexUpdateStart, modelsIndexUpdateEnd); - generatePolyModels(modelsIndexUpdateStart, modelsIndexUpdateEnd, true); - generateVBOs(modelsIndexUpdateStart, modelsIndexUpdateEnd); - useModelCache = oldModelCache; - Mods::modelsListRequiresReloadUnmodded = false; - } - - Mods::modelsListModifiedIndexes.clear(); - - updateLoadingScreen(20); - doLoadingScreen(); - - if ( force || Mods::soundListRequiresReloadUnmodded || !Mods::soundsListModifiedIndexes.empty() ) - { - physfsReloadSounds(true); - Mods::soundListRequiresReloadUnmodded = false; - } - - Mods::soundsListModifiedIndexes.clear(); - - updateLoadingScreen(30); + updateLoadingScreen(10); doLoadingScreen(); - if ( force || Mods::tileListRequireReloadUnmodded ) + // update tiles + if (Mods::tileListRequireReloadUnmodded) { physfsReloadTiles(true); Mods::tileListRequireReloadUnmodded = false; } - - updateLoadingScreen(40); doLoadingScreen(); - if ( force || Mods::spriteImagesRequireReloadUnmodded ) + // reload sprites + if (Mods::spriteImagesRequireReloadUnmodded) { physfsReloadSprites(true); Mods::spriteImagesRequireReloadUnmodded = false; } - - updateLoadingScreen(50); doLoadingScreen(); - if ( force || Mods::booksRequireReloadUnmodded ) + // reload system images + if (Mods::systemImagesReloadUnmodded) { - physfsReloadBooks(); - Mods::booksRequireReloadUnmodded = false; + physfsReloadSystemImages(); + Mods::systemImagesReloadUnmodded = false; + systemResourceImagesToReload.clear(); } - updateLoadingScreen(60); + updateLoadingScreen(20); doLoadingScreen(); - if ( force || Mods::musicRequireReloadUnmodded ) - { - gamemodsUnloadCustomThemeMusic(); - bool reloadIntroMusic = false; - physfsReloadMusic(reloadIntroMusic, true); - if ( reloadIntroMusic ) - { -#ifdef SOUND - playMusic(intromusic[local_rng.rand() % (NUMINTROMUSIC - 1)], false, true, true); -#endif - } - Mods::musicRequireReloadUnmodded = false; - } - - updateLoadingScreen(70); - doLoadingScreen(); + static int modelsIndexUpdateStart = 1; + static int modelsIndexUpdateEnd = nummodels; - if ( force || Mods::langRequireReloadUnmodded ) - { - Language::reset(); - Language::reloadLanguage(); - Mods::langRequireReloadUnmodded = false; - } + // begin async load process + std::atomic_bool loading_done{ false }; + auto loading_task = std::async(std::launch::async, [&loading_done]() { + initGameDatafilesAsync(true); - updateLoadingScreen(80); - doLoadingScreen(); + // update sounds + if (Mods::soundListRequiresReloadUnmodded || !Mods::soundsListModifiedIndexes.empty()) + { + physfsReloadSounds(true); + Mods::soundListRequiresReloadUnmodded = false; + } + Mods::soundsListModifiedIndexes.clear(); + updateLoadingScreen(30); - if ( force || Mods::monsterLimbsRequireReloadUnmodded ) - { - physfsReloadMonsterLimbFiles(); - Mods::monsterLimbsRequireReloadUnmodded = false; - } + // update models + if (Mods::modelsListRequiresReloadUnmodded || !Mods::modelsListModifiedIndexes.empty()) + { + physfsModelIndexUpdate(modelsIndexUpdateStart, modelsIndexUpdateEnd); + for (int c = modelsIndexUpdateStart; c < modelsIndexUpdateEnd && c < nummodels; ++c) { + if (polymodels[c].faces) { + free(polymodels[c].faces); + } + } + generatePolyModels(modelsIndexUpdateStart, modelsIndexUpdateEnd, true); + Mods::modelsListRequiresReloadUnmodded = false; + } + Mods::modelsListModifiedIndexes.clear(); + updateLoadingScreen(60); - updateLoadingScreen(85); - doLoadingScreen(); + // reload books + if (Mods::booksRequireReloadUnmodded) + { + physfsReloadBooks(); + Mods::booksRequireReloadUnmodded = false; + } + updateLoadingScreen(70); - if ( force || Mods::systemImagesReloadUnmodded ) - { - physfsReloadSystemImages(); - Mods::systemImagesReloadUnmodded = false; - systemResourceImagesToReload.clear(); - } + // reload lang file + if (Mods::langRequireReloadUnmodded) + { + Language::reset(); + Language::reloadLanguage(); + Mods::langRequireReloadUnmodded = false; + } - updateLoadingScreen(90); - doLoadingScreen(); + // reload monster limb offsets + if (Mods::monsterLimbsRequireReloadUnmodded) + { + physfsReloadMonsterLimbFiles(); + Mods::monsterLimbsRequireReloadUnmodded = false; + } - initGameDatafiles(true); + // reload lights + loadLights(); - updateLoadingScreen(95); - doLoadingScreen(); + updateLoadingScreen(80); - std::atomic_bool loading_done{ false }; - auto loading_task = std::async(std::launch::async, [&loading_done]() { - initGameDatafilesAsync(true); - loading_done = true; - return 0; + loading_done = true; + return 0; }); - while ( !loading_done ) + while (!loading_done) { doLoadingScreen(); std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - loadLights(); - + // final loading steps + initGameDatafiles(true); + for (int c = modelsIndexUpdateStart; c < modelsIndexUpdateEnd && c < nummodels; ++c) { + if (polymodels[c].vao) { + GL_CHECK_ERR(glDeleteVertexArrays(1, &polymodels[c].vao)); + } + if (polymodels[c].vbo) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].vbo)); + } + if (polymodels[c].colors) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].colors)); + } + } + generateVBOs(modelsIndexUpdateStart, modelsIndexUpdateEnd); consoleCommand("/dumpcache"); - while ( loadingTicks < TICKS_PER_SECOND / 2 ) // artificial delay to look nicer if loading time is short + // reload music + if (Mods::musicRequireReloadUnmodded) { - doLoadingScreen(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + gamemodsUnloadCustomThemeMusic(); + bool reloadIntroMusic = false; + physfsReloadMusic(reloadIntroMusic, true); + if (reloadIntroMusic) + { +#ifdef SOUND + playMusic(intromusic[local_rng.rand() % (NUMINTROMUSIC - 1)], false, true, true); +#endif + } + Mods::musicRequireReloadUnmodded = false; } - destroyLoadingScreen(); loading = false; isLoading = false; @@ -8440,7 +8439,6 @@ void Mods::loadMods() Mods::disableSteamAchievements = false; Mods::verifyAchievements(nullptr, false); - loadingTicks = 0; isLoading = true; loading = true; createLoadingScreen(5); @@ -8458,8 +8456,21 @@ void Mods::loadMods() int modelsIndexUpdateEnd = nummodels; bool oldModelCache = useModelCache; useModelCache = false; - physfsModelIndexUpdate(modelsIndexUpdateStart, modelsIndexUpdateEnd, true); - //loadModels(modelsIndexUpdateStart, modelsIndexUpdateEnd); + physfsModelIndexUpdate(modelsIndexUpdateStart, modelsIndexUpdateEnd); + for (int c = modelsIndexUpdateStart; c < modelsIndexUpdateEnd && c < nummodels; ++c) { + if (polymodels[c].faces) { + free(polymodels[c].faces); + } + if (polymodels[c].vao) { + GL_CHECK_ERR(glDeleteVertexArrays(1, &polymodels[c].vao)); + } + if (polymodels[c].vbo) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].vbo)); + } + if (polymodels[c].colors) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].colors)); + } + } generatePolyModels(modelsIndexUpdateStart, modelsIndexUpdateEnd, true); generateVBOs(modelsIndexUpdateStart, modelsIndexUpdateEnd); useModelCache = oldModelCache; @@ -8622,12 +8633,6 @@ void Mods::loadMods() consoleCommand("/dumpcache"); - while ( loadingTicks < TICKS_PER_SECOND / 2 ) // artificial delay to look nicer if loading time is short - { - doLoadingScreen(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - destroyLoadingScreen(); loading = false; diff --git a/src/mod_tools.hpp b/src/mod_tools.hpp index 8feeaa186..c0d464c10 100644 --- a/src/mod_tools.hpp +++ b/src/mod_tools.hpp @@ -3247,7 +3247,6 @@ struct Mods static bool disableSteamAchievements; static bool lobbyDisableSteamAchievements; static bool isLoading; - static Uint32 loadingTicks; #ifdef STEAMWORKS static std::vector workshopSubscribedItemList; static std::vector> workshopLoadedFileIDMap; diff --git a/src/ui/LoadingScreen.cpp b/src/ui/LoadingScreen.cpp index dd2952f1f..5b3694824 100644 --- a/src/ui/LoadingScreen.cpp +++ b/src/ui/LoadingScreen.cpp @@ -156,12 +156,6 @@ void doLoadingScreen() { const Uint32 oldTicks = loadingticks; (void)handleEvents(); if (oldTicks != loadingticks) { - - if ( Mods::isLoading ) - { - ++Mods::loadingTicks; - } - // spinning widget auto spinning_widget = loading_frame->findImage("spinning_widget"); if (spinning_widget) { diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 936201c97..b1dec7ede 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -22645,7 +22645,7 @@ namespace MainMenu { } } - void(*banner_func[])(Button&) = { + void(*banner_funcs[])(Button&) = { [](Button&){ openDLCPrompt(enabledDLCPack1 ? 1 : 0); } From b3ac3111302ffd62e9b0d9c97279d1d224816adc Mon Sep 17 00:00:00 2001 From: SheridanR Date: Tue, 26 Sep 2023 19:47:24 -0700 Subject: [PATCH 059/146] merge fixes to nintendo holiday theme loading Signed-off-by: SheridanR --- src/files.cpp | 6 ++++-- src/ui/Font.cpp | 4 ---- src/ui/Image.cpp | 9 --------- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/files.cpp b/src/files.cpp index a2293f848..3a7442e8b 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -1410,12 +1410,14 @@ bool completePath(char *dest, const char * const filename, const char *base) { // Already absolute if (filename[0] == '/') { - strncpy(dest, filename, PATH_MAX); + strcpy(dest, filename); return true; } + #ifdef NINTENDO + // Already absolute on nintendo if (strncmp(filename, base, strlen(base)) == 0) { - strncpy(dest, filename, PATH_MAX); + strcpy(dest, filename); return true; } #endif diff --git a/src/ui/Font.cpp b/src/ui/Font.cpp index 8367f9fbf..8a056e5d4 100644 --- a/src/ui/Font.cpp +++ b/src/ui/Font.cpp @@ -21,16 +21,12 @@ Font::Font(const char* _name) { } else { path = name; } -#ifdef NINTENDO - path = "rom:/" + path; -#else if ( PHYSFS_getRealDir(path.c_str()) ) { std::string realPath = PHYSFS_getRealDir(path.c_str()); path.insert(0, PHYSFS_getDirSeparator()); path.insert(0, realPath); } -#endif if ((font = TTF_OpenFont(path.c_str(), pointSize)) == NULL) { printlog("failed to load '%s': %s", path.c_str(), TTF_GetError()); return; diff --git a/src/ui/Image.cpp b/src/ui/Image.cpp index 7fbedfe9c..e2349608e 100644 --- a/src/ui/Image.cpp +++ b/src/ui/Image.cpp @@ -23,14 +23,6 @@ Image::Image(const char* _name) { } } while (1); -#ifdef NINTENDO - std::string path; - if (memcmp(clippedName, "rom:", 4) && memcmp(clippedName, "save:", 5)) { - path = std::string("rom:/") + clippedName; - } else { - path = clippedName; - } -#else std::string path = clippedName; if ( PHYSFS_getRealDir(clippedName) != nullptr ) { @@ -41,7 +33,6 @@ Image::Image(const char* _name) { path.insert(0, baseDir); } } -#endif printlog("loading image '%s'...", path.c_str()); if ((surf = IMG_Load(path.c_str())) == NULL) { printlog("failed to load image '%s'", path.c_str()); From 9888e6c972d1e36e529d3b18aa6b4d09724344c2 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Tue, 26 Sep 2023 20:00:13 -0700 Subject: [PATCH 060/146] fix more issues unloading mods Signed-off-by: SheridanR --- src/mod_tools.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mod_tools.cpp b/src/mod_tools.cpp index af2ef6b0e..9e59fef57 100644 --- a/src/mod_tools.cpp +++ b/src/mod_tools.cpp @@ -8349,12 +8349,12 @@ void Mods::unloadMods(bool force) if (Mods::modelsListRequiresReloadUnmodded || !Mods::modelsListModifiedIndexes.empty()) { physfsModelIndexUpdate(modelsIndexUpdateStart, modelsIndexUpdateEnd); - for (int c = modelsIndexUpdateStart; c < modelsIndexUpdateEnd && c < nummodels; ++c) { + for (int c = 0; c < nummodels; ++c) { if (polymodels[c].faces) { free(polymodels[c].faces); } } - generatePolyModels(modelsIndexUpdateStart, modelsIndexUpdateEnd, true); + generatePolyModels(0, nummodels, true); Mods::modelsListRequiresReloadUnmodded = false; } Mods::modelsListModifiedIndexes.clear(); @@ -8399,7 +8399,7 @@ void Mods::unloadMods(bool force) // final loading steps initGameDatafiles(true); - for (int c = modelsIndexUpdateStart; c < modelsIndexUpdateEnd && c < nummodels; ++c) { + for (int c = 0; c < nummodels; ++c) { if (polymodels[c].vao) { GL_CHECK_ERR(glDeleteVertexArrays(1, &polymodels[c].vao)); } @@ -8410,7 +8410,7 @@ void Mods::unloadMods(bool force) GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].colors)); } } - generateVBOs(modelsIndexUpdateStart, modelsIndexUpdateEnd); + generateVBOs(0, nummodels); consoleCommand("/dumpcache"); // reload music From 170d94feaa9f122fd8cd20ae243181acefbdd315 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Tue, 26 Sep 2023 20:23:30 -0700 Subject: [PATCH 061/146] fix fucked up EOS shit, unloadMods() Signed-off-by: SheridanR --- src/eos.cpp | 1341 ++++++++++++++++++++++--------------------- src/eos.hpp | 6 +- src/files.cpp | 9 + src/mod_tools.cpp | 2 +- src/net.cpp | 8 +- src/ui/MainMenu.cpp | 4 +- 6 files changed, 699 insertions(+), 671 deletions(-) diff --git a/src/eos.cpp b/src/eos.cpp index ffa6f4fa7..b355f38d4 100644 --- a/src/eos.cpp +++ b/src/eos.cpp @@ -48,17 +48,17 @@ void EOS_CALL EOSFuncs::AuthLoginCompleteCallback(const EOS_Auth_LoginCallbackIn { EOS_HAuth AuthHandle = EOS_Platform_GetAuthInterface(EOS.PlatformHandle); EOS.AccountManager.waitingForCallback = false; - if ( !data ) + if (!data) { EOSFuncs::logError("Login Callback error: null data"); } - else if ( data->ResultCode == EOS_EResult::EOS_Success ) + else if (data->ResultCode == EOS_EResult::EOS_Success) { EOSFuncs::logInfo("Login Callback: success"); EOS.AccountManager.AccountAuthenticationStatus = EOS_EResult::EOS_Success; const int numAccounts = EOS_Auth_GetLoggedInAccountsCount(AuthHandle); - for ( int accIndex = 0; accIndex < numAccounts; ++accIndex ) + for (int accIndex = 0; accIndex < numAccounts; ++accIndex) { EOS.CurrentUserInfo.epicAccountId = EOSFuncs::Helpers_t::epicIdToString(EOS_Auth_GetLoggedInAccountByIndex(AuthHandle, accIndex)); EOS_ELoginStatus LoginStatus; @@ -74,21 +74,21 @@ void EOS_CALL EOSFuncs::AuthLoginCompleteCallback(const EOS_Auth_LoginCallbackIn } return; } - else if ( data->ResultCode == EOS_EResult::EOS_OperationWillRetry ) + else if (data->ResultCode == EOS_EResult::EOS_OperationWillRetry) { EOSFuncs::logError("Login Callback: retrying"); return; } - else if ( data->ResultCode == EOS_EResult::EOS_Auth_PinGrantCode ) + else if (data->ResultCode == EOS_EResult::EOS_Auth_PinGrantCode) { EOSFuncs::logError("Login Callback: PIN required"); } - else if ( data->ResultCode == EOS_EResult::EOS_Auth_MFARequired ) + else if (data->ResultCode == EOS_EResult::EOS_Auth_MFARequired) { EOSFuncs::logError("Login Callback: MFA required"); } #ifdef NINTENDO - else if ( data->ResultCode == EOS_EResult::EOS_InvalidUser ) + else if (data->ResultCode == EOS_EResult::EOS_InvalidUser) { // need to sign in with Nintendo Account and relogin } @@ -104,7 +104,7 @@ void EOS_CALL EOSFuncs::AuthLoginCompleteCallback(const EOS_Auth_LoginCallbackIn void EOS_CALL EOSFuncs::ConnectLoginCompleteCallback(const EOS_Connect_LoginCallbackInfo* data) { - if ( !data ) + if (!data) { EOSFuncs::logError("Connect Login Callback: null data"); EOS.CurrentUserInfo.setProductUserIdHandle(nullptr); @@ -112,7 +112,7 @@ void EOS_CALL EOSFuncs::ConnectLoginCompleteCallback(const EOS_Connect_LoginCall return; } - if ( data->ResultCode == EOS_EResult::EOS_Success ) + if (data->ResultCode == EOS_EResult::EOS_Success) { EOS.CurrentUserInfo.setProductUserIdHandle(data->LocalUserId); EOS.CurrentUserInfo.bUserLoggedIn = true; @@ -121,19 +121,19 @@ void EOS_CALL EOSFuncs::ConnectLoginCompleteCallback(const EOS_Connect_LoginCall // load achievement data #ifndef LOCAL_ACHIEVEMENTS - if ( !EOS.Achievements.bAchievementsInit ) + if (!EOS.Achievements.bAchievementsInit) { EOS.loadAchievementData(); } #endif - // cache friend data - EOS.queryFriends(); + // cache friend data + EOS.queryFriends(); } - else if ( data->ResultCode == EOS_EResult::EOS_InvalidUser ) + else if (data->ResultCode == EOS_EResult::EOS_InvalidUser) { EOSFuncs::logInfo("Connect Login Callback: creating new user"); - EOS_Connect_CreateUserOptions CreateUserOptions; + EOS_Connect_CreateUserOptions CreateUserOptions{}; CreateUserOptions.ApiVersion = EOS_CONNECT_CREATEUSER_API_LATEST; CreateUserOptions.ContinuanceToken = data->ContinuanceToken; @@ -152,17 +152,17 @@ void EOS_CALL EOSFuncs::ConnectLoginCrossplayCompleteCallback(const EOS_Connect_ { EOS.CrossplayAccountManager.awaitingConnectCallback = false; - if ( !data ) + if (!data) { EOSFuncs::logError("Crossplay Connect Login Callback: null data"); EOS.CurrentUserInfo.setProductUserIdHandle(nullptr); EOS.CurrentUserInfo.bUserLoggedIn = false; return; } - + EOS.CrossplayAccountManager.connectLoginStatus = data->ResultCode; - if ( data->ResultCode == EOS_EResult::EOS_Success ) + if (data->ResultCode == EOS_EResult::EOS_Success) { EOS.CurrentUserInfo.setProductUserIdHandle(data->LocalUserId); EOS.CurrentUserInfo.bUserLoggedIn = true; @@ -171,7 +171,7 @@ void EOS_CALL EOSFuncs::ConnectLoginCrossplayCompleteCallback(const EOS_Connect_ #if defined(STEAMWORKS) || defined(NINTENDO) EOS_ELoginStatus authLoginStatus = EOS_Auth_GetLoginStatus(EOS_Platform_GetAuthInterface(EOS.PlatformHandle), EOSFuncs::Helpers_t::epicIdFromString(EOS.CurrentUserInfo.epicAccountId.c_str())); - if ( authLoginStatus != EOS_ELoginStatus::EOS_LS_LoggedIn ) + if (authLoginStatus != EOS_ELoginStatus::EOS_LS_LoggedIn) { EOS.queryLocalExternalAccountId(EOS_EExternalAccountType::EOS_EAT_STEAM); } @@ -180,7 +180,7 @@ void EOS_CALL EOSFuncs::ConnectLoginCrossplayCompleteCallback(const EOS_Connect_ #ifdef NINTENDO // load achievement data #ifndef LOCAL_ACHIEVEMENTS - if ( !EOS.Achievements.bAchievementsInit ) + if (!EOS.Achievements.bAchievementsInit) { EOS.loadAchievementData(); } @@ -188,11 +188,11 @@ void EOS_CALL EOSFuncs::ConnectLoginCrossplayCompleteCallback(const EOS_Connect_ #endif EOSFuncs::logInfo("Crossplay Connect Login Callback success: %s", EOS.CurrentUserInfo.getProductUserIdStr()); } - else if ( data->ResultCode == EOS_EResult::EOS_InvalidUser ) + else if (data->ResultCode == EOS_EResult::EOS_InvalidUser) { - if ( EOS.CrossplayAccountManager.acceptedEula ) + if (EOS.CrossplayAccountManager.acceptedEula) { - EOS_Connect_CreateUserOptions CreateUserOptions; + EOS_Connect_CreateUserOptions CreateUserOptions{}; CreateUserOptions.ApiVersion = EOS_CONNECT_CREATEUSER_API_LATEST; CreateUserOptions.ContinuanceToken = data->ContinuanceToken; @@ -215,14 +215,14 @@ void EOS_CALL EOSFuncs::ConnectLoginCrossplayCompleteCallback(const EOS_Connect_ void EOS_CALL EOSFuncs::OnCreateUserCallback(const EOS_Connect_CreateUserCallbackInfo* data) { - if ( !data ) + if (!data) { EOSFuncs::logError("OnCreateUserCallback: null data"); EOS.CurrentUserInfo.setProductUserIdHandle(nullptr); EOS.CurrentUserInfo.bUserLoggedIn = false; return; } - if ( data->ResultCode == EOS_EResult::EOS_Success ) + if (data->ResultCode == EOS_EResult::EOS_Success) { EOS.CurrentUserInfo.setProductUserIdHandle(data->LocalUserId); EOS.CurrentUserInfo.bUserLoggedIn = true; @@ -231,14 +231,14 @@ void EOS_CALL EOSFuncs::OnCreateUserCallback(const EOS_Connect_CreateUserCallbac // load achievement data #ifndef LOCAL_ACHIEVEMENTS - if ( !EOS.Achievements.bAchievementsInit ) + if (!EOS.Achievements.bAchievementsInit) { EOS.loadAchievementData(); } #endif - // cache friend data - EOS.queryFriends(); + // cache friend data + EOS.queryFriends(); } else { @@ -252,14 +252,14 @@ void EOS_CALL EOSFuncs::OnCreateUserCrossplayCallback(const EOS_Connect_CreateUs { EOS.CrossplayAccountManager.awaitingCreateUserCallback = false; EOS.CrossplayAccountManager.connectLoginStatus = EOS_EResult::EOS_NotConfigured; - if ( !data ) + if (!data) { EOSFuncs::logError("OnCreateUserCrossplayCallback: null data"); EOS.CurrentUserInfo.setProductUserIdHandle(nullptr); EOS.CurrentUserInfo.bUserLoggedIn = false; return; } - if ( data->ResultCode == EOS_EResult::EOS_Success ) + if (data->ResultCode == EOS_EResult::EOS_Success) { EOS.CurrentUserInfo.setProductUserIdHandle(data->LocalUserId); EOS.CurrentUserInfo.bUserLoggedIn = true; @@ -269,7 +269,7 @@ void EOS_CALL EOSFuncs::OnCreateUserCrossplayCallback(const EOS_Connect_CreateUs EOS.CrossplayAccountManager.connectLoginStatus = EOS_EResult::EOS_Success; EOS_ELoginStatus authLoginStatus = EOS_Auth_GetLoginStatus(EOS_Platform_GetAuthInterface(EOS.PlatformHandle), EOSFuncs::Helpers_t::epicIdFromString(EOS.CurrentUserInfo.epicAccountId.c_str())); - if ( authLoginStatus != EOS_ELoginStatus::EOS_LS_LoggedIn ) + if (authLoginStatus != EOS_ELoginStatus::EOS_LS_LoggedIn) { EOS.queryLocalExternalAccountId(EOS_EExternalAccountType::EOS_EAT_STEAM); } @@ -287,13 +287,13 @@ void EOS_CALL EOSFuncs::OnCreateUserCrossplayCallback(const EOS_Connect_CreateUs void EOS_CALL EOSFuncs::FriendsQueryCallback(const EOS_Friends_QueryFriendsCallbackInfo* data) { - if ( !data ) + if (!data) { EOSFuncs::logError("FriendsQueryCallback: null data"); return; } EOS_HFriends FriendsHandle = EOS_Platform_GetFriendsInterface(EOS.PlatformHandle); - EOS_Friends_GetFriendsCountOptions FriendsCountOptions; + EOS_Friends_GetFriendsCountOptions FriendsCountOptions{}; FriendsCountOptions.ApiVersion = EOS_FRIENDS_GETFRIENDSCOUNT_API_LATEST; FriendsCountOptions.LocalUserId = data->LocalUserId; int numFriends = EOS_Friends_GetFriendsCount(FriendsHandle, &FriendsCountOptions); @@ -301,17 +301,17 @@ void EOS_CALL EOSFuncs::FriendsQueryCallback(const EOS_Friends_QueryFriendsCallb EOS.CurrentUserInfo.Friends.clear(); - EOS_Friends_GetFriendAtIndexOptions IndexOptions; + EOS_Friends_GetFriendAtIndexOptions IndexOptions{}; IndexOptions.ApiVersion = EOS_FRIENDS_GETFRIENDATINDEX_API_LATEST; IndexOptions.LocalUserId = data->LocalUserId; - for ( int i = 0; i < numFriends; ++i ) + for (int i = 0; i < numFriends; ++i) { IndexOptions.Index = i; EOS_EpicAccountId FriendUserId = EOS_Friends_GetFriendAtIndex(FriendsHandle, &IndexOptions); - if ( EOSFuncs::Helpers_t::epicIdIsValid(FriendUserId) ) + if (EOSFuncs::Helpers_t::epicIdIsValid(FriendUserId)) { - EOS_Friends_GetStatusOptions StatusOptions; + EOS_Friends_GetStatusOptions StatusOptions{}; StatusOptions.ApiVersion = EOS_FRIENDS_GETSTATUS_API_LATEST; StatusOptions.LocalUserId = data->LocalUserId; StatusOptions.TargetUserId = FriendUserId; @@ -332,32 +332,32 @@ void EOS_CALL EOSFuncs::FriendsQueryCallback(const EOS_Friends_QueryFriendsCallb void EOS_CALL EOSFuncs::UserInfoCallback(const EOS_UserInfo_QueryUserInfoCallbackInfo* data) { - if ( !data ) + if (!data) { EOSFuncs::logError("UserInfoCallback: null data"); return; } UserInfoQueryData_t* userInfoQueryData = (static_cast(data->ClientData)); - if ( data->ResultCode == EOS_EResult::EOS_Success ) + if (data->ResultCode == EOS_EResult::EOS_Success) { - EOS_UserInfo_CopyUserInfoOptions UserInfoOptions; + EOS_UserInfo_CopyUserInfoOptions UserInfoOptions{}; UserInfoOptions.ApiVersion = EOS_USERINFO_COPYUSERINFO_API_LATEST; UserInfoOptions.LocalUserId = data->LocalUserId; UserInfoOptions.TargetUserId = data->TargetUserId; EOS_UserInfo* userInfo; // result is managed by application to free the memory. - if ( EOS_UserInfo_CopyUserInfo(EOS_Platform_GetUserInfoInterface(EOS.PlatformHandle), - &UserInfoOptions, &userInfo) == EOS_EResult::EOS_Success ) + if (EOS_UserInfo_CopyUserInfo(EOS_Platform_GetUserInfoInterface(EOS.PlatformHandle), + &UserInfoOptions, &userInfo) == EOS_EResult::EOS_Success) { - if ( userInfoQueryData->queryType == EOS.USER_INFO_QUERY_LOCAL ) + if (userInfoQueryData->queryType == EOS.USER_INFO_QUERY_LOCAL) { // local user EOS.CurrentUserInfo.Name = userInfo->DisplayName; EOS.CurrentUserInfo.bUserInfoRequireUpdate = false; EOSFuncs::logInfo("UserInfoCallback: Current User Name: %s", userInfo->DisplayName); } - else if ( userInfoQueryData->queryType == EOS.USER_INFO_QUERY_FRIEND ) + else if (userInfoQueryData->queryType == EOS.USER_INFO_QUERY_FRIEND) { - if ( EOS.CurrentUserInfo.Friends.empty() ) + if (EOS.CurrentUserInfo.Friends.empty()) { EOSFuncs::logInfo("UserInfoCallback: friend info request failed due empty friends list"); } @@ -365,9 +365,9 @@ void EOS_CALL EOSFuncs::UserInfoCallback(const EOS_UserInfo_QueryUserInfoCallbac { bool foundFriend = false; std::string queryTarget = EOSFuncs::Helpers_t::epicIdToString(userInfoQueryData->epicAccountId); - for ( auto& it : EOS.CurrentUserInfo.Friends ) + for (auto& it : EOS.CurrentUserInfo.Friends) { - if ( it.EpicAccountId.compare(queryTarget) == 0 ) + if (it.EpicAccountId.compare(queryTarget) == 0) { foundFriend = true; it.Name = userInfo->DisplayName; @@ -376,16 +376,16 @@ void EOS_CALL EOSFuncs::UserInfoCallback(const EOS_UserInfo_QueryUserInfoCallbac break; } } - if ( !foundFriend ) + if (!foundFriend) { - EOSFuncs::logInfo("UserInfoCallback: could not find player in current lobby with account %s", + EOSFuncs::logInfo("UserInfoCallback: could not find player in current lobby with account %s", EOSFuncs::Helpers_t::epicIdToString(userInfoQueryData->epicAccountId)); } } } - else if ( userInfoQueryData->queryType == EOS.USER_INFO_QUERY_LOBBY_MEMBER ) + else if (userInfoQueryData->queryType == EOS.USER_INFO_QUERY_LOBBY_MEMBER) { - if ( !EOS.CurrentLobbyData.currentLobbyIsValid() || EOS.CurrentLobbyData.playersInLobby.empty() ) + if (!EOS.CurrentLobbyData.currentLobbyIsValid() || EOS.CurrentLobbyData.playersInLobby.empty()) { EOSFuncs::logInfo("UserInfoCallback: lobby member request failed due to invalid or no player data in lobby"); } @@ -393,9 +393,9 @@ void EOS_CALL EOSFuncs::UserInfoCallback(const EOS_UserInfo_QueryUserInfoCallbac { bool foundMember = false; std::string queryTarget = EOSFuncs::Helpers_t::epicIdToString(userInfoQueryData->epicAccountId); - for ( auto& it : EOS.CurrentLobbyData.playersInLobby ) + for (auto& it : EOS.CurrentLobbyData.playersInLobby) { - if ( queryTarget.compare(it.memberEpicAccountId.c_str()) == 0 ) + if (queryTarget.compare(it.memberEpicAccountId.c_str()) == 0) { foundMember = true; it.name = userInfo->DisplayName; @@ -404,9 +404,9 @@ void EOS_CALL EOSFuncs::UserInfoCallback(const EOS_UserInfo_QueryUserInfoCallbac break; } } - if ( !foundMember ) + if (!foundMember) { - EOSFuncs::logInfo("UserInfoCallback: could not find player in current lobby with account %s", + EOSFuncs::logInfo("UserInfoCallback: could not find player in current lobby with account %s", EOSFuncs::Helpers_t::epicIdToString(userInfoQueryData->epicAccountId)); } } @@ -424,21 +424,21 @@ void EOS_CALL EOSFuncs::UserInfoCallback(const EOS_UserInfo_QueryUserInfoCallbac void EOS_CALL EOSFuncs::OnCreateLobbyFinished(const EOS_Lobby_CreateLobbyCallbackInfo* data) { EOS.CurrentLobbyData.bAwaitingCreationCallback = false; - if ( !data ) + if (!data) { EOSFuncs::logError("OnCreateLobbyFinished: null data"); return; } EOS.CurrentLobbyData.LobbyCreationResult = data->ResultCode; - if ( data->ResultCode == EOS_EResult::EOS_Success ) + if (data->ResultCode == EOS_EResult::EOS_Success) { EOS.CurrentLobbyData.LobbyId = data->LobbyId; #ifdef NINTENDO EOS.CurrentLobbyData.LobbyAttributes.lobbyName = MainMenu::getHostname(); #else - EOS.CurrentLobbyData.LobbyAttributes.lobbyName = EOS.CurrentUserInfo.Name + "'s lobby"; + EOS.CurrentLobbyData.LobbyAttributes.lobbyName = EOS.CurrentUserInfo.Name + "'s lobby"; #endif strncpy(EOS.currentLobbyName, EOS.CurrentLobbyData.LobbyAttributes.lobbyName.c_str(), 31); @@ -461,50 +461,50 @@ void EOS_CALL EOSFuncs::OnCreateLobbyFinished(const EOS_Lobby_CreateLobbyCallbac void EOS_CALL EOSFuncs::OnLobbySearchFinished(const EOS_LobbySearch_FindCallbackInfo* data) { EOS.bRequestingLobbies = false; - if ( !data ) + if (!data) { EOSFuncs::logError("OnLobbySearchFinished: null data"); return; } - else if ( data->ResultCode == EOS_EResult::EOS_Success ) + else if (data->ResultCode == EOS_EResult::EOS_Success) { - EOS_LobbySearch_GetSearchResultCountOptions SearchResultOptions; + EOS_LobbySearch_GetSearchResultCountOptions SearchResultOptions{}; SearchResultOptions.ApiVersion = EOS_LOBBYSEARCH_GETSEARCHRESULTCOUNT_API_LATEST; int NumSearchResults = EOS_LobbySearch_GetSearchResultCount(EOS.LobbySearchResults.CurrentLobbySearch, &SearchResultOptions); int* searchOptions = static_cast(data->ClientData); - EOS_LobbySearch_CopySearchResultByIndexOptions IndexOptions; + EOS_LobbySearch_CopySearchResultByIndexOptions IndexOptions{}; IndexOptions.ApiVersion = EOS_LOBBYSEARCH_COPYSEARCHRESULTBYINDEX_API_LATEST; - for ( int i = 0; i < NumSearchResults; ++i ) + for (int i = 0; i < NumSearchResults; ++i) { LobbyData_t newLobby; EOS_HLobbyDetails LobbyDetails = nullptr; IndexOptions.LobbyIndex = i; EOS_EResult Result = EOS_LobbySearch_CopySearchResultByIndex(EOS.LobbySearchResults.CurrentLobbySearch, &IndexOptions, &LobbyDetails); - if ( Result == EOS_EResult::EOS_Success && LobbyDetails ) + if (Result == EOS_EResult::EOS_Success && LobbyDetails) { EOS.setLobbyDetailsFromHandle(LobbyDetails, &newLobby); - EOSFuncs::logInfo("OnLobbySearchFinished: Found lobby: %s, Owner: %s, MaxPlayers: %d", + EOSFuncs::logInfo("OnLobbySearchFinished: Found lobby: %s, Owner: %s, MaxPlayers: %d", newLobby.LobbyId.c_str(), newLobby.OwnerProductUserId.c_str(), newLobby.MaxPlayers); EOS.LobbySearchResults.results.push_back(newLobby); - if ( searchOptions[EOSFuncs::LobbyParameters_t::JOIN_OPTIONS] - == static_cast(EOSFuncs::LobbyParameters_t::LOBBY_JOIN_FIRST_SEARCH_RESULT) ) + if (searchOptions[EOSFuncs::LobbyParameters_t::JOIN_OPTIONS] + == static_cast(EOSFuncs::LobbyParameters_t::LOBBY_JOIN_FIRST_SEARCH_RESULT)) { // set the handle to be used for joining. EOS.LobbyParameters.lobbyToJoin = LobbyDetails; EOS.joinLobby(&newLobby); } - else if ( searchOptions[EOSFuncs::LobbyParameters_t::JOIN_OPTIONS] - == static_cast(EOSFuncs::LobbyParameters_t::LOBBY_DONT_JOIN) ) + else if (searchOptions[EOSFuncs::LobbyParameters_t::JOIN_OPTIONS] + == static_cast(EOSFuncs::LobbyParameters_t::LOBBY_DONT_JOIN)) { // we can release this handle. EOS_LobbyDetails_Release(LobbyDetails); } - else if ( searchOptions[EOSFuncs::LobbyParameters_t::JOIN_OPTIONS] - == static_cast(EOSFuncs::LobbyParameters_t::LOBBY_UPDATE_CURRENTLOBBY) ) + else if (searchOptions[EOSFuncs::LobbyParameters_t::JOIN_OPTIONS] + == static_cast(EOSFuncs::LobbyParameters_t::LOBBY_UPDATE_CURRENTLOBBY)) { // TODO need? EOS.setLobbyDetailsFromHandle(LobbyDetails, &EOS.CurrentLobbyData); @@ -519,13 +519,13 @@ void EOS_CALL EOSFuncs::OnLobbySearchFinished(const EOS_LobbySearch_FindCallback } } - if ( NumSearchResults == 0 ) + if (NumSearchResults == 0) { EOSFuncs::logInfo("OnLobbySearchFinished: Found 0 lobbies!"); int* searchOptions = static_cast(data->ClientData); - if ( searchOptions[EOSFuncs::LobbyParameters_t::JOIN_OPTIONS] - == static_cast(EOSFuncs::LobbyParameters_t::LOBBY_JOIN_FIRST_SEARCH_RESULT) ) + if (searchOptions[EOSFuncs::LobbyParameters_t::JOIN_OPTIONS] + == static_cast(EOSFuncs::LobbyParameters_t::LOBBY_JOIN_FIRST_SEARCH_RESULT)) { // we were trying to join a lobby, set error message. EOS.bConnectingToLobbyWindow = false; @@ -537,7 +537,7 @@ void EOS_CALL EOSFuncs::OnLobbySearchFinished(const EOS_LobbySearch_FindCallback EOS.LobbySearchResults.sortResults(); return; } - else if ( data->ResultCode == EOS_EResult::EOS_NotFound ) + else if (data->ResultCode == EOS_EResult::EOS_NotFound) { EOSFuncs::logError("OnLobbySearchFinished: Requested lobby no longer exists", static_cast(data->ResultCode)); } @@ -546,31 +546,31 @@ void EOS_CALL EOSFuncs::OnLobbySearchFinished(const EOS_LobbySearch_FindCallback EOSFuncs::logError("OnLobbySearchFinished: Callback failure: %d", static_cast(data->ResultCode)); } - if ( data->ResultCode != EOS_EResult::EOS_OperationWillRetry ) - { - int* searchOptions = static_cast(data->ClientData); - if ( searchOptions[EOSFuncs::LobbyParameters_t::JOIN_OPTIONS] - == static_cast(EOSFuncs::LobbyParameters_t::LOBBY_JOIN_FIRST_SEARCH_RESULT) ) - { - // we were trying to join a lobby, set error message. - EOS.bConnectingToLobbyWindow = false; - EOS.bConnectingToLobby = false; - EOS.ConnectingToLobbyStatus = static_cast(data->ResultCode); + if (data->ResultCode != EOS_EResult::EOS_OperationWillRetry) + { + int* searchOptions = static_cast(data->ClientData); + if (searchOptions[EOSFuncs::LobbyParameters_t::JOIN_OPTIONS] + == static_cast(EOSFuncs::LobbyParameters_t::LOBBY_JOIN_FIRST_SEARCH_RESULT)) + { + // we were trying to join a lobby, set error message. + EOS.bConnectingToLobbyWindow = false; + EOS.bConnectingToLobby = false; + EOS.ConnectingToLobbyStatus = static_cast(data->ResultCode); multiplayer = SINGLE; - } - } + } + } } void EOS_CALL EOSFuncs::OnLobbyJoinCallback(const EOS_Lobby_JoinLobbyCallbackInfo* data) { - if ( !data ) + if (!data) { EOSFuncs::logError("OnLobbyJoinCallback: null data"); EOS.bConnectingToLobby = false; EOS.bConnectingToLobbyWindow = false; EOS.ConnectingToLobbyStatus = static_cast(EOS_EResult::EOS_UnexpectedError); } - else if ( data->ResultCode == EOS_EResult::EOS_Success ) + else if (data->ResultCode == EOS_EResult::EOS_Success) { EOSFuncs::logInfo("OnLobbyJoinCallback: Joined lobby id: %s", data->LobbyId); /*if ( static_cast(data->ClientData) == &EOS.CurrentLobbyData ) @@ -579,7 +579,7 @@ void EOS_CALL EOSFuncs::OnLobbyJoinCallback(const EOS_Lobby_JoinLobbyCallbackInf }*/ EOS.bConnectingToLobby = false; - if ( EOS.CurrentLobbyData.bDenyLobbyJoinEvent && EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0 ) + if (EOS.CurrentLobbyData.bDenyLobbyJoinEvent && EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0) { // early return, immediately exit lobby. EOS.CurrentLobbyData.bDenyLobbyJoinEvent = false; @@ -612,23 +612,23 @@ void EOS_CALL EOSFuncs::OnLobbyJoinCallback(const EOS_Lobby_JoinLobbyCallbackInf void EOS_CALL EOSFuncs::OnLobbyLeaveCallback(const EOS_Lobby_LeaveLobbyCallbackInfo* data) { - if ( !data ) + if (!data) { EOSFuncs::logError("OnLobbyLeaveCallback: null data"); } - else if ( data->ResultCode == EOS_EResult::EOS_Success ) + else if (data->ResultCode == EOS_EResult::EOS_Success) { EOSFuncs::logInfo("OnLobbyLeaveCallback: Left lobby id: %s", data->LobbyId); - if ( EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0 ) + if (EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0) { LobbyLeaveCleanup(EOS.CurrentLobbyData); } return; } - else if ( data->ResultCode == EOS_EResult::EOS_NotFound ) + else if (data->ResultCode == EOS_EResult::EOS_NotFound) { EOSFuncs::logInfo("OnLobbyLeaveCallback: Could not find lobby id to leave: %s", data->LobbyId); - if ( EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0 ) + if (EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0) { LobbyLeaveCleanup(EOS.CurrentLobbyData); } @@ -642,17 +642,17 @@ void EOS_CALL EOSFuncs::OnLobbyLeaveCallback(const EOS_Lobby_LeaveLobbyCallbackI void EOS_CALL EOSFuncs::OnIncomingConnectionRequest(const EOS_P2P_OnIncomingConnectionRequestInfo* data) { - if ( data ) + if (data) { std::string SocketName = data->SocketId->SocketName; - if ( SocketName != "CHAT" ) + if (SocketName != "CHAT") { EOSFuncs::logError("OnIncomingConnectionRequest: bad socket id: %s", SocketName.c_str()); return; } EOS_HP2P P2PHandle = EOS_Platform_GetP2PInterface(EOS.PlatformHandle); - EOS_P2P_AcceptConnectionOptions Options; + EOS_P2P_AcceptConnectionOptions Options{}; Options.ApiVersion = EOS_P2P_ACCEPTCONNECTION_API_LATEST; Options.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle(); Options.RemoteUserId = data->RemoteUserId; @@ -663,7 +663,7 @@ void EOS_CALL EOSFuncs::OnIncomingConnectionRequest(const EOS_P2P_OnIncomingConn Options.SocketId = &SocketId; EOS_EResult Result = EOS_P2P_AcceptConnection(P2PHandle, &Options); - if ( Result != EOS_EResult::EOS_Success ) + if (Result != EOS_EResult::EOS_Success) { EOSFuncs::logError("OnIncomingConnectionRequest: error while accepting connection, code: %d", static_cast(Result)); } @@ -680,15 +680,15 @@ void EOS_CALL EOSFuncs::OnIncomingConnectionRequest(const EOS_P2P_OnIncomingConn void EOS_CALL EOSFuncs::OnLobbyUpdateFinished(const EOS_Lobby_UpdateLobbyCallbackInfo* data) { - if ( data ) + if (data) { - if ( EOS.LobbyModificationHandle ) + if (EOS.LobbyModificationHandle) { EOS_LobbyModification_Release(EOS.LobbyModificationHandle); EOS.LobbyModificationHandle = nullptr; } - if ( data->ResultCode != EOS_EResult::EOS_Success ) + if (data->ResultCode != EOS_EResult::EOS_Success) { EOSFuncs::logError("OnLobbyUpdateFinished: Callback failure: %d", static_cast(data->ResultCode)); return; @@ -706,15 +706,15 @@ void EOS_CALL EOSFuncs::OnLobbyUpdateFinished(const EOS_Lobby_UpdateLobbyCallbac void EOS_CALL EOSFuncs::OnLobbyMemberUpdateFinished(const EOS_Lobby_UpdateLobbyCallbackInfo* data) { - if ( data ) + if (data) { - if ( EOS.LobbyMemberModificationHandle ) + if (EOS.LobbyMemberModificationHandle) { EOS_LobbyModification_Release(EOS.LobbyMemberModificationHandle); EOS.LobbyMemberModificationHandle = nullptr; } - if ( data->ResultCode != EOS_EResult::EOS_Success && data->ResultCode != EOS_EResult::EOS_NoChange ) + if (data->ResultCode != EOS_EResult::EOS_Success && data->ResultCode != EOS_EResult::EOS_NoChange) { EOSFuncs::logError("OnLobbyMemberUpdateFinished: Callback failure: %d", static_cast(data->ResultCode)); return; @@ -732,24 +732,24 @@ void EOS_CALL EOSFuncs::OnLobbyMemberUpdateFinished(const EOS_Lobby_UpdateLobbyC void EOS_CALL EOSFuncs::OnQueryAccountMappingsCallback(const EOS_Connect_QueryProductUserIdMappingsCallbackInfo* data) { - if ( data ) + if (data) { - if ( data->ResultCode == EOS_EResult::EOS_Success ) + if (data->ResultCode == EOS_EResult::EOS_Success) { EOSFuncs::logInfo("OnQueryAccountMappingsCallback: Success"); bool authEnabledForLocalUser = false; // if we're using auth() interface we can use EpicAccountIds for queries - EOS_ELoginStatus loginStatus = EOS_Auth_GetLoginStatus(EOS_Platform_GetAuthInterface(EOS.PlatformHandle), + EOS_ELoginStatus loginStatus = EOS_Auth_GetLoginStatus(EOS_Platform_GetAuthInterface(EOS.PlatformHandle), EOSFuncs::Helpers_t::epicIdFromString(EOS.CurrentUserInfo.epicAccountId.c_str())); - if ( loginStatus == EOS_ELoginStatus::EOS_LS_LoggedIn ) + if (loginStatus == EOS_ELoginStatus::EOS_LS_LoggedIn) { authEnabledForLocalUser = true; } std::vector MappingsReceived; - for ( const EOS_ProductUserId& productId : EOS.ProductIdsAwaitingAccountMappingCallback ) + for (const EOS_ProductUserId& productId : EOS.ProductIdsAwaitingAccountMappingCallback) { - EOS_Connect_GetProductUserIdMappingOptions Options = {}; + EOS_Connect_GetProductUserIdMappingOptions Options{}; Options.ApiVersion = EOS_CONNECT_GETPRODUCTUSERIDMAPPING_API_LATEST; Options.AccountIdType = EOS_EExternalAccountType::EOS_EAT_EPIC; Options.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle(); @@ -759,30 +759,30 @@ void EOS_CALL EOSFuncs::OnQueryAccountMappingsCallback(const EOS_Connect_QueryPr char buffer[EOS_CONNECT_EXTERNAL_ACCOUNT_ID_MAX_LENGTH]; int bufferSize = EOS_CONNECT_EXTERNAL_ACCOUNT_ID_MAX_LENGTH; EOS_EResult Result = EOS_Connect_GetProductUserIdMapping(ConnectHandle, &Options, buffer, &bufferSize); - if ( Result == EOS_EResult::EOS_Success ) + if (Result == EOS_EResult::EOS_Success) { std::string receivedStr(buffer, bufferSize); EOS_EpicAccountId epicAccountId = EOSFuncs::Helpers_t::epicIdFromString(receivedStr.c_str()); // insert the ids into the global map - if ( authEnabledForLocalUser ) + if (authEnabledForLocalUser) { - EOS.AccountMappings.insert(std::pair(productId,epicAccountId)); + EOS.AccountMappings.insert(std::pair(productId, epicAccountId)); } else { EOS.ExternalAccountMappings.insert(std::pair(productId, buffer)); } - for ( LobbyData_t::PlayerLobbyData_t& player : EOS.CurrentLobbyData.playersInLobby ) + for (LobbyData_t::PlayerLobbyData_t& player : EOS.CurrentLobbyData.playersInLobby) { - if ( EOSFuncs::Helpers_t::isMatchingProductIds(productId, EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str())) ) + if (EOSFuncs::Helpers_t::isMatchingProductIds(productId, EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str()))) { player.memberEpicAccountId = receivedStr; - if ( authEnabledForLocalUser ) + if (authEnabledForLocalUser) { EOS.getUserInfo(epicAccountId, EOSFuncs::USER_INFO_QUERY_LOBBY_MEMBER, 0); - EOSFuncs::logInfo("OnQueryAccountMappingsCallback: product id: %s, epic account id: %s", + EOSFuncs::logInfo("OnQueryAccountMappingsCallback: product id: %s, epic account id: %s", player.memberProductUserId.c_str(), player.memberEpicAccountId.c_str()); } else @@ -794,71 +794,71 @@ void EOS_CALL EOSFuncs::OnQueryAccountMappingsCallback(const EOS_Connect_QueryPr } MappingsReceived.push_back(productId); } - else if ( Result == EOS_EResult::EOS_NotFound ) + else if (Result == EOS_EResult::EOS_NotFound) { // try different account types Options.AccountIdType = EOS_EExternalAccountType::EOS_EAT_STEAM; Result = EOS_Connect_GetProductUserIdMapping(ConnectHandle, &Options, buffer, &bufferSize); - if ( Result == EOS_EResult::EOS_Success ) + if (Result == EOS_EResult::EOS_Success) { EOS.ExternalAccountMappings.insert(std::pair(productId, buffer)); - if ( EOSFuncs::Helpers_t::isMatchingProductIds(EOS.CurrentUserInfo.getProductUserIdHandle(), productId) ) + if (EOSFuncs::Helpers_t::isMatchingProductIds(EOS.CurrentUserInfo.getProductUserIdHandle(), productId)) { EOS.getExternalAccountUserInfo(productId, EOSFuncs::USER_INFO_QUERY_LOCAL); } - for ( LobbyData_t::PlayerLobbyData_t& player : EOS.CurrentLobbyData.playersInLobby ) + for (LobbyData_t::PlayerLobbyData_t& player : EOS.CurrentLobbyData.playersInLobby) { - if ( EOSFuncs::Helpers_t::isMatchingProductIds(productId, EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str())) ) + if (EOSFuncs::Helpers_t::isMatchingProductIds(productId, EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str()))) { EOS.getExternalAccountUserInfo(productId, EOSFuncs::USER_INFO_QUERY_LOBBY_MEMBER); } } MappingsReceived.push_back(productId); } - else if ( Result == EOS_EResult::EOS_NotFound ) - { - // try different account types - Options.AccountIdType = EOS_EExternalAccountType::EOS_EAT_NINTENDO; - Result = EOS_Connect_GetProductUserIdMapping(ConnectHandle, &Options, buffer, &bufferSize); - if ( Result == EOS_EResult::EOS_Success ) - { - EOS.ExternalAccountMappings.insert(std::pair(productId, buffer)); - if ( EOSFuncs::Helpers_t::isMatchingProductIds(EOS.CurrentUserInfo.getProductUserIdHandle(), productId) ) - { - EOS.getExternalAccountUserInfo(productId, EOSFuncs::USER_INFO_QUERY_LOCAL); - } - for ( LobbyData_t::PlayerLobbyData_t& player : EOS.CurrentLobbyData.playersInLobby ) - { - if ( EOSFuncs::Helpers_t::isMatchingProductIds(productId, EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str())) ) - { - EOS.getExternalAccountUserInfo(productId, EOSFuncs::USER_INFO_QUERY_LOBBY_MEMBER); - } - } - MappingsReceived.push_back(productId); - } - else - { - // Result does not return EOS_Success querying type EOS_EExternalAccountType::EOS_EAT_NINTENDO, so try get info anyway. - EOS.ExternalAccountMappings.insert(std::pair(productId, "")); - for ( LobbyData_t::PlayerLobbyData_t& player : EOS.CurrentLobbyData.playersInLobby ) - { - if ( EOSFuncs::Helpers_t::isMatchingProductIds(productId, EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str())) ) - { - EOS.getExternalAccountUserInfo(productId, EOSFuncs::USER_INFO_QUERY_LOBBY_MEMBER); - } - } - MappingsReceived.push_back(productId); - } - } + else if (Result == EOS_EResult::EOS_NotFound) + { + // try different account types + Options.AccountIdType = EOS_EExternalAccountType::EOS_EAT_NINTENDO; + Result = EOS_Connect_GetProductUserIdMapping(ConnectHandle, &Options, buffer, &bufferSize); + if (Result == EOS_EResult::EOS_Success) + { + EOS.ExternalAccountMappings.insert(std::pair(productId, buffer)); + if (EOSFuncs::Helpers_t::isMatchingProductIds(EOS.CurrentUserInfo.getProductUserIdHandle(), productId)) + { + EOS.getExternalAccountUserInfo(productId, EOSFuncs::USER_INFO_QUERY_LOCAL); + } + for (LobbyData_t::PlayerLobbyData_t& player : EOS.CurrentLobbyData.playersInLobby) + { + if (EOSFuncs::Helpers_t::isMatchingProductIds(productId, EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str()))) + { + EOS.getExternalAccountUserInfo(productId, EOSFuncs::USER_INFO_QUERY_LOBBY_MEMBER); + } + } + MappingsReceived.push_back(productId); + } + else + { + // Result does not return EOS_Success querying type EOS_EExternalAccountType::EOS_EAT_NINTENDO, so try get info anyway. + EOS.ExternalAccountMappings.insert(std::pair(productId, "")); + for (LobbyData_t::PlayerLobbyData_t& player : EOS.CurrentLobbyData.playersInLobby) + { + if (EOSFuncs::Helpers_t::isMatchingProductIds(productId, EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str()))) + { + EOS.getExternalAccountUserInfo(productId, EOSFuncs::USER_INFO_QUERY_LOBBY_MEMBER); + } + } + MappingsReceived.push_back(productId); + } + } } } - for ( const EOS_ProductUserId& productId : MappingsReceived ) + for (const EOS_ProductUserId& productId : MappingsReceived) { EOS.ProductIdsAwaitingAccountMappingCallback.erase(productId); } } - else if ( data->ResultCode != EOS_EResult::EOS_OperationWillRetry ) + else if (data->ResultCode != EOS_EResult::EOS_OperationWillRetry) { EOSFuncs::logError("OnQueryAccountMappingsCallback: retrying"); } @@ -867,19 +867,19 @@ void EOS_CALL EOSFuncs::OnQueryAccountMappingsCallback(const EOS_Connect_QueryPr void EOSFuncs::getExternalAccountUserInfo(EOS_ProductUserId targetId, UserInfoQueryType queryType) { - EOS_Connect_CopyProductUserInfoOptions CopyProductUserInfoOptions = {}; + EOS_Connect_CopyProductUserInfoOptions CopyProductUserInfoOptions{}; CopyProductUserInfoOptions.ApiVersion = EOS_CONNECT_COPYPRODUCTUSERINFO_API_LATEST; CopyProductUserInfoOptions.TargetUserId = targetId; EOS_Connect_ExternalAccountInfo* ExternalAccountInfo = nullptr; EOS_EResult CopyResult = EOS_Connect_CopyProductUserInfo(ConnectHandle, &CopyProductUserInfoOptions, &ExternalAccountInfo); - if ( CopyResult != EOS_EResult::EOS_Success ) + if (CopyResult != EOS_EResult::EOS_Success) { EOSFuncs::logInfo("getExternalAccountUserInfo: Error %d", static_cast(CopyResult)); EOS_Connect_ExternalAccountInfo_Release(ExternalAccountInfo); return; } - else if ( !ExternalAccountInfo ) + else if (!ExternalAccountInfo) { EOSFuncs::logInfo("getExternalAccountUserInfo: Info was null"); return; @@ -887,16 +887,16 @@ void EOSFuncs::getExternalAccountUserInfo(EOS_ProductUserId targetId, UserInfoQu EOSFuncs::logInfo("getExternalAccountUserInfo: Received info for product id: %s", EOSFuncs::Helpers_t::shortProductIdToString(targetId).c_str()); - if ( queryType == UserInfoQueryType::USER_INFO_QUERY_LOCAL ) + if (queryType == UserInfoQueryType::USER_INFO_QUERY_LOCAL) { // local user EOS.CurrentUserInfo.Name = ExternalAccountInfo->DisplayName; EOS.CurrentUserInfo.bUserInfoRequireUpdate = false; EOSFuncs::logInfo("getExternalAccountUserInfo: Current User Name: %s", ExternalAccountInfo->DisplayName); } - else if ( queryType == UserInfoQueryType::USER_INFO_QUERY_LOBBY_MEMBER ) + else if (queryType == UserInfoQueryType::USER_INFO_QUERY_LOBBY_MEMBER) { - if ( !CurrentLobbyData.currentLobbyIsValid() || CurrentLobbyData.playersInLobby.empty() ) + if (!CurrentLobbyData.currentLobbyIsValid() || CurrentLobbyData.playersInLobby.empty()) { EOSFuncs::logInfo("getExternalAccountUserInfo: lobby member request failed due to invalid or no player data in lobby"); } @@ -904,9 +904,9 @@ void EOSFuncs::getExternalAccountUserInfo(EOS_ProductUserId targetId, UserInfoQu { bool foundMember = false; std::string queryTarget = EOSFuncs::Helpers_t::productIdToString(targetId); - for ( auto& it : EOS.CurrentLobbyData.playersInLobby ) + for (auto& it : EOS.CurrentLobbyData.playersInLobby) { - if ( queryTarget.compare(it.memberProductUserId.c_str()) == 0 ) + if (queryTarget.compare(it.memberProductUserId.c_str()) == 0) { foundMember = true; it.name = ExternalAccountInfo->DisplayName; @@ -916,7 +916,7 @@ void EOSFuncs::getExternalAccountUserInfo(EOS_ProductUserId targetId, UserInfoQu break; } } - if ( !foundMember ) + if (!foundMember) { EOSFuncs::logInfo("getExternalAccountUserInfo: could not find player in current lobby with product id %s", EOSFuncs::Helpers_t::shortProductIdToString(targetId).c_str()); @@ -928,9 +928,9 @@ void EOSFuncs::getExternalAccountUserInfo(EOS_ProductUserId targetId, UserInfoQu void EOS_CALL EOSFuncs::OnMemberUpdateReceived(const EOS_Lobby_LobbyMemberUpdateReceivedCallbackInfo* data) { - if ( data ) + if (data) { - if ( EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0 ) + if (EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0) { EOS.CurrentLobbyData.updateLobby(); EOSFuncs::logInfo("OnMemberUpdateReceived: received user: %s, updating lobby", EOSFuncs::Helpers_t::shortProductIdToString(data->TargetUserId).c_str()); @@ -948,76 +948,76 @@ void EOS_CALL EOSFuncs::OnMemberUpdateReceived(const EOS_Lobby_LobbyMemberUpdate void EOS_CALL EOSFuncs::OnMemberStatusReceived(const EOS_Lobby_LobbyMemberStatusReceivedCallbackInfo* data) { - if ( data ) + if (data) { - switch ( data->CurrentStatus ) + switch (data->CurrentStatus) { - case EOS_ELobbyMemberStatus::EOS_LMS_CLOSED: - case EOS_ELobbyMemberStatus::EOS_LMS_DISCONNECTED: - case EOS_ELobbyMemberStatus::EOS_LMS_KICKED: - case EOS_ELobbyMemberStatus::EOS_LMS_LEFT: - if ( EOS.P2PConnectionInfo.isPeerIndexed(data->TargetUserId) ) - { - EOS_P2P_CloseConnectionOptions closeOptions = {}; - closeOptions.ApiVersion = EOS_P2P_CLOSECONNECTIONS_API_LATEST; - closeOptions.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle(); - closeOptions.RemoteUserId = data->TargetUserId; - - EOS_P2P_SocketId SocketId = {}; - SocketId.ApiVersion = EOS_P2P_SOCKETID_API_LATEST; - strncpy(SocketId.SocketName, "CHAT", 5); - closeOptions.SocketId = &SocketId; - EOS_EResult result = EOS_P2P_CloseConnection(EOS_Platform_GetP2PInterface(EOS.PlatformHandle), &closeOptions); - EOSFuncs::logInfo("OnMemberStatusReceived: closing P2P connections, result: %d", static_cast(result)); - - EOS.P2PConnectionInfo.assignPeerIndex(data->TargetUserId, -1); - } + case EOS_ELobbyMemberStatus::EOS_LMS_CLOSED: + case EOS_ELobbyMemberStatus::EOS_LMS_DISCONNECTED: + case EOS_ELobbyMemberStatus::EOS_LMS_KICKED: + case EOS_ELobbyMemberStatus::EOS_LMS_LEFT: + if (EOS.P2PConnectionInfo.isPeerIndexed(data->TargetUserId)) + { + EOS_P2P_CloseConnectionOptions closeOptions{}; + closeOptions.ApiVersion = EOS_P2P_CLOSECONNECTIONS_API_LATEST; + closeOptions.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle(); + closeOptions.RemoteUserId = data->TargetUserId; + + EOS_P2P_SocketId SocketId = {}; + SocketId.ApiVersion = EOS_P2P_SOCKETID_API_LATEST; + strncpy(SocketId.SocketName, "CHAT", 5); + closeOptions.SocketId = &SocketId; + EOS_EResult result = EOS_P2P_CloseConnection(EOS_Platform_GetP2PInterface(EOS.PlatformHandle), &closeOptions); + EOSFuncs::logInfo("OnMemberStatusReceived: closing P2P connections, result: %d", static_cast(result)); + + EOS.P2PConnectionInfo.assignPeerIndex(data->TargetUserId, -1); + } - if ( EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0 ) + if (EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0) + { + if (data->CurrentStatus == EOS_ELobbyMemberStatus::EOS_LMS_CLOSED + || (data->CurrentStatus == EOS_ELobbyMemberStatus::EOS_LMS_KICKED + && data->TargetUserId == EOS.CurrentUserInfo.getProductUserIdHandle()) + ) { - if ( data->CurrentStatus == EOS_ELobbyMemberStatus::EOS_LMS_CLOSED - || (data->CurrentStatus == EOS_ELobbyMemberStatus::EOS_LMS_KICKED - && data->TargetUserId == EOS.CurrentUserInfo.getProductUserIdHandle()) - ) - { - // if lobby closed or we got kicked, then clear data. - LobbyLeaveCleanup(EOS.CurrentLobbyData); - switch (data->CurrentStatus) { - case EOS_ELobbyMemberStatus::EOS_LMS_CLOSED: - //MainMenu::disconnectedFromServer("The host has shutdown the lobby."); - break; - case EOS_ELobbyMemberStatus::EOS_LMS_KICKED: - //MainMenu::disconnectedFromServer("You have been kicked\nfrom the online lobby."); - break; - default: - break; - } - } - else - { - EOS.CurrentLobbyData.updateLobby(); - EOSFuncs::logInfo("OnMemberStatusReceived: received user: %s, event: %d, updating lobby", - EOSFuncs::Helpers_t::shortProductIdToString(data->TargetUserId).c_str(), - static_cast(data->CurrentStatus)); - return; + // if lobby closed or we got kicked, then clear data. + LobbyLeaveCleanup(EOS.CurrentLobbyData); + switch (data->CurrentStatus) { + case EOS_ELobbyMemberStatus::EOS_LMS_CLOSED: + //MainMenu::disconnectedFromServer("The host has shutdown the lobby."); + break; + case EOS_ELobbyMemberStatus::EOS_LMS_KICKED: + //MainMenu::disconnectedFromServer("You have been kicked\nfrom the online lobby."); + break; + default: + break; } } - break; - case EOS_ELobbyMemberStatus::EOS_LMS_JOINED: - case EOS_ELobbyMemberStatus::EOS_LMS_PROMOTED: - if ( EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0 ) + else { EOS.CurrentLobbyData.updateLobby(); - EOSFuncs::logInfo("OnMemberStatusReceived: received user: %s, event: %d, updating lobby", + EOSFuncs::logInfo("OnMemberStatusReceived: received user: %s, event: %d, updating lobby", EOSFuncs::Helpers_t::shortProductIdToString(data->TargetUserId).c_str(), static_cast(data->CurrentStatus)); return; } - break; - default: - break; + } + break; + case EOS_ELobbyMemberStatus::EOS_LMS_JOINED: + case EOS_ELobbyMemberStatus::EOS_LMS_PROMOTED: + if (EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0) + { + EOS.CurrentLobbyData.updateLobby(); + EOSFuncs::logInfo("OnMemberStatusReceived: received user: %s, event: %d, updating lobby", + EOSFuncs::Helpers_t::shortProductIdToString(data->TargetUserId).c_str(), + static_cast(data->CurrentStatus)); + return; + } + break; + default: + break; } - EOSFuncs::logInfo("OnMemberStatusReceived: success, received user: %s | status: %d", + EOSFuncs::logInfo("OnMemberStatusReceived: success, received user: %s | status: %d", EOSFuncs::Helpers_t::shortProductIdToString(data->TargetUserId).c_str(), static_cast(data->CurrentStatus)); } else @@ -1046,9 +1046,9 @@ void EOS_CALL EOSFuncs::OnMemberStatusReceived(const EOS_Lobby_LobbyMemberStatus void EOS_CALL EOSFuncs::OnLobbyUpdateReceived(const EOS_Lobby_LobbyUpdateReceivedCallbackInfo* data) { - if ( data ) + if (data) { - if ( EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0 ) + if (EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0) { EOS.CurrentLobbyData.updateLobby(); } @@ -1062,9 +1062,9 @@ void EOS_CALL EOSFuncs::OnLobbyUpdateReceived(const EOS_Lobby_LobbyUpdateReceive void EOS_CALL EOSFuncs::OnDestroyLobbyFinished(const EOS_Lobby_DestroyLobbyCallbackInfo* data) { - if ( data ) + if (data) { - if ( data->ResultCode != EOS_EResult::EOS_Success ) + if (data->ResultCode != EOS_EResult::EOS_Success) { EOSFuncs::logError("OnDestroyLobbyFinished: error destroying lobby id: %s code: %d", data->LobbyId, static_cast(data->ResultCode)); } @@ -1081,12 +1081,12 @@ void EOS_CALL EOSFuncs::OnDestroyLobbyFinished(const EOS_Lobby_DestroyLobbyCallb void EOS_CALL EOSFuncs::ConnectAuthExpirationCallback(const EOS_Connect_AuthExpirationCallbackInfo* data) { - if ( data ) + if (data) { EOSFuncs::logInfo("ConnectAuthExpirationCallback: connect auth expiring - product id: %s", EOSFuncs::Helpers_t::productIdToString(data->LocalUserId)); #if defined(STEAMWORKS) || defined(NINTENDO) - if ( LobbyHandler.crossplayEnabled ) + if (LobbyHandler.crossplayEnabled) { EOSFuncs::logInfo("ConnectAuthExpirationCallback: Reconnecting crossplay account"); EOS.CrossplayAccountManager.autologin = true; @@ -1156,14 +1156,14 @@ bool EOSFuncs::initPlatform(bool enableLogging) return true; } #ifdef NINTENDO - EOS_Switch_InitializeOptions SwitchOptions; + EOS_Switch_InitializeOptions SwitchOptions{}; SwitchOptions.ApiVersion = EOS_SWITCH_INITIALIZEOPTIONS_API_LATEST; //SwitchOptions.OnNetworkRequested = &Game_OnNetworkRequested; SwitchOptions.OnNetworkRequested_DEPRECATED = nullptr; SwitchOptions.CacheStorageSizekB = 0; // EOS_SWITCH_MIN_CACHE_STORAGE_SIZE_KB SwitchOptions.CacheStorageIndex = 0; - EOS_InitializeOptions InitializeOptions; + EOS_InitializeOptions InitializeOptions{}; InitializeOptions.ProductName = "Barony"; InitializeOptions.ProductVersion = VERSION; InitializeOptions.ApiVersion = EOS_INITIALIZE_API_LATEST; @@ -1174,7 +1174,7 @@ bool EOSFuncs::initPlatform(bool enableLogging) InitializeOptions.SystemInitializeOptions = &SwitchOptions; InitializeOptions.OverrideThreadAffinity = nullptr; #else - EOS_InitializeOptions InitializeOptions; + EOS_InitializeOptions InitializeOptions{}; InitializeOptions.ProductName = "Barony"; InitializeOptions.ProductVersion = VERSION; InitializeOptions.ApiVersion = EOS_INITIALIZE_API_LATEST; @@ -1186,7 +1186,7 @@ bool EOSFuncs::initPlatform(bool enableLogging) InitializeOptions.OverrideThreadAffinity = nullptr; #endif EOS_EResult result = EOS_Initialize(&InitializeOptions); - if ( result != EOS_EResult::EOS_Success && result != EOS_EResult::EOS_AlreadyConfigured ) + if (result != EOS_EResult::EOS_Success && result != EOS_EResult::EOS_AlreadyConfigured) { logError("initPlatform: Failure to initialize - error code: %d", static_cast(result)); return false; @@ -1196,7 +1196,7 @@ bool EOSFuncs::initPlatform(bool enableLogging) logInfo("initPlatform: Initialize success"); } - if ( enableLogging ) + if (enableLogging) { EOS_EResult SetLogCallbackResult = EOS_Logging_SetCallback(&this->LoggingCallback); if (SetLogCallbackResult != EOS_EResult::EOS_Success && @@ -1211,7 +1211,7 @@ bool EOSFuncs::initPlatform(bool enableLogging) } } - EOS_Platform_Options PlatformOptions = {}; + EOS_Platform_Options PlatformOptions{}; PlatformOptions.ApiVersion = EOS_PLATFORM_OPTIONS_API_LATEST; PlatformOptions.Reserved = nullptr; PlatformOptions.ProductId = BUILD_ENV_PR; @@ -1231,6 +1231,16 @@ bool EOSFuncs::initPlatform(bool enableLogging) static std::string EncryptionKey(64, '1'); PlatformOptions.EncryptionKey = EncryptionKey.c_str(); PlatformOptions.CacheDirectory = nullptr; // important - needs double slashes and absolute path + PlatformOptions.TickBudgetInMilliseconds = 0; // do all available work + + EOS_Platform_RTCOptions rtcOptions{}; + rtcOptions.ApiVersion = EOS_PLATFORM_RTCOPTIONS_API_LATEST; + rtcOptions.BackgroundMode = EOS_ERTCBackgroundMode::EOS_RTCBM_KeepRoomsAlive; + rtcOptions.PlatformSpecificOptions = nullptr; // must be null initialized + PlatformOptions.RTCOptions = &rtcOptions; + + PlatformOptions.IntegratedPlatformOptionsContainerHandle = nullptr; // must be null initialized + PlatformOptions.SystemSpecificOptions = nullptr; // must be null initialized PlatformHandle = EOS_Platform_Create(&PlatformOptions); PlatformOptions.bIsServer = EOS_TRUE; @@ -1242,7 +1252,7 @@ bool EOSFuncs::initPlatform(bool enableLogging) PlatformOptions.SandboxId = nullptr; PlatformOptions.DeploymentId = nullptr; - if ( !PlatformHandle || !ServerPlatformHandle ) + if (!PlatformHandle || !ServerPlatformHandle) { logError("PlatformHandle: Platform failed to initialize - invalid handle"); return false; @@ -1338,11 +1348,11 @@ void EOSFuncs::initConnectLogin() // should not handle for Steam connect logins return; } - EOS_Connect_UserLoginInfo Info; + EOS_Connect_UserLoginInfo Info{}; Info.ApiVersion = EOS_CONNECT_USERLOGININFO_API_LATEST; Info.DisplayName = MainMenu::getUsername(); - EOS_Connect_LoginOptions Options; + EOS_Connect_LoginOptions Options{}; Options.ApiVersion = EOS_CONNECT_LOGIN_API_LATEST; Options.Credentials = &Credentials; Options.UserLoginInfo = &Info; @@ -1356,8 +1366,8 @@ void EOSFuncs::initConnectLogin() // should not handle for Steam connect logins EOS_Auth_CopyUserAuthTokenOptions CopyTokenOptions = { 0 }; CopyTokenOptions.ApiVersion = EOS_AUTH_COPYUSERAUTHTOKEN_API_LATEST; - if ( EOS_Auth_CopyUserAuthToken(AuthHandle, &CopyTokenOptions, - EOSFuncs::Helpers_t::epicIdFromString(CurrentUserInfo.epicAccountId.c_str()), &UserAuthToken) == EOS_EResult::EOS_Success ) + if (EOS_Auth_CopyUserAuthToken(AuthHandle, &CopyTokenOptions, + EOSFuncs::Helpers_t::epicIdFromString(CurrentUserInfo.epicAccountId.c_str()), &UserAuthToken) == EOS_EResult::EOS_Success) { logInfo("initConnectLogin: Auth expires: %f", UserAuthToken->ExpiresIn); @@ -1366,7 +1376,7 @@ void EOSFuncs::initConnectLogin() // should not handle for Steam connect logins Credentials.Type = EOS_EExternalCredentialType::EOS_ECT_EPIC; // change this to steam etc for different account providers. Credentials.Token = UserAuthToken->AccessToken; - EOS_Connect_LoginOptions Options; + EOS_Connect_LoginOptions Options{}; Options.ApiVersion = EOS_CONNECT_LOGIN_API_LATEST; Options.Credentials = &Credentials; Options.UserLoginInfo = nullptr; @@ -1379,11 +1389,11 @@ void EOSFuncs::initConnectLogin() // should not handle for Steam connect logins void EOSFuncs::readFromFile() { - if ( PHYSFS_getRealDir("/data/eos.json") ) + if (PHYSFS_getRealDir("/data/eos.json")) { std::string inputPath = PHYSFS_getRealDir("/data/eos.json"); inputPath.append("/data/eos.json"); - if ( FileHelper::readObject(inputPath.c_str(), *this) ) + if (FileHelper::readObject(inputPath.c_str(), *this)) { EOSFuncs::logInfo("[JSON]: Successfully read json file %s", inputPath.c_str()); } @@ -1392,14 +1402,14 @@ void EOSFuncs::readFromFile() void EOSFuncs::readFromCmdLineArgs() { - for ( auto& arg : CommandLineArgs ) + for (auto& arg : CommandLineArgs) { - if ( arg.find("-AUTH_PASSWORD=") != std::string::npos ) + if (arg.find("-AUTH_PASSWORD=") != std::string::npos) { EOSFuncs::logInfo("Launching from store..."); CredentialName = arg.substr(strlen("-AUTH_PASSWORD=")); } - else if ( arg.find("-AUTH_TYPE=exchangecode") != std::string::npos ) + else if (arg.find("-AUTH_TYPE=exchangecode") != std::string::npos) { EOS.AccountManager.AuthType = EOS_ELoginCredentialType::EOS_LCT_ExchangeCode; } @@ -1408,20 +1418,20 @@ void EOSFuncs::readFromCmdLineArgs() bool EOSFuncs::HandleReceivedMessages(EOS_ProductUserId* remoteIdReturn) { - if ( !CurrentUserInfo.isValid() ) + if (!CurrentUserInfo.isValid()) { //logError("HandleReceivedMessages: Invalid local user Id: %s", CurrentUserInfo.getProductUserIdStr()); return false; } - if ( !net_packet ) + if (!net_packet) { return false; } EOS_HP2P P2PHandle = EOS_Platform_GetP2PInterface(PlatformHandle); - EOS_P2P_ReceivePacketOptions ReceivePacketOptions; + EOS_P2P_ReceivePacketOptions ReceivePacketOptions{}; ReceivePacketOptions.ApiVersion = EOS_P2P_RECEIVEPACKET_API_LATEST; ReceivePacketOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle(); ReceivePacketOptions.MaxDataSizeBytes = 512; @@ -1433,14 +1443,14 @@ bool EOSFuncs::HandleReceivedMessages(EOS_ProductUserId* remoteIdReturn) Uint32 bytesWritten = 0; EOS_EResult result = EOS_P2P_ReceivePacket(P2PHandle, &ReceivePacketOptions, remoteIdReturn, &SocketId, &Channel, net_packet->data, &bytesWritten); - if ( result == EOS_EResult::EOS_NotFound + if (result == EOS_EResult::EOS_NotFound || result == EOS_EResult::EOS_InvalidAuth - || result == EOS_EResult::EOS_InvalidUser ) + || result == EOS_EResult::EOS_InvalidUser) { //no more packets, just end return false; } - else if ( result == EOS_EResult::EOS_Success ) + else if (result == EOS_EResult::EOS_Success) { net_packet->len = bytesWritten; //char buffer[512] = ""; @@ -1459,14 +1469,14 @@ bool EOSFuncs::HandleReceivedMessages(EOS_ProductUserId* remoteIdReturn) // function to empty the packet queue on main lobby. bool EOSFuncs::HandleReceivedMessagesAndIgnore(EOS_ProductUserId* remoteIdReturn) { - if ( !CurrentUserInfo.isValid() ) + if (!CurrentUserInfo.isValid()) { //logError("HandleReceivedMessages: Invalid local user Id: %s", CurrentUserInfo.getProductUserIdStr()); return false; } EOS_HP2P P2PHandle = EOS_Platform_GetP2PInterface(PlatformHandle); - EOS_P2P_ReceivePacketOptions ReceivePacketOptions; + EOS_P2P_ReceivePacketOptions ReceivePacketOptions{}; ReceivePacketOptions.ApiVersion = EOS_P2P_RECEIVEPACKET_API_LATEST; ReceivePacketOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle(); ReceivePacketOptions.MaxDataSizeBytes = 512; @@ -1479,23 +1489,23 @@ bool EOSFuncs::HandleReceivedMessagesAndIgnore(EOS_ProductUserId* remoteIdReturn Uint32 bytesWritten = 0; Uint8 dummyData[512]; EOS_EResult result = EOS_P2P_ReceivePacket(P2PHandle, &ReceivePacketOptions, remoteIdReturn, &SocketId, &Channel, dummyData, &bytesWritten); - if ( result == EOS_EResult::EOS_NotFound + if (result == EOS_EResult::EOS_NotFound || result == EOS_EResult::EOS_InvalidAuth - || result == EOS_EResult::EOS_InvalidUser ) + || result == EOS_EResult::EOS_InvalidUser) { //no more packets, just end return false; } - else if ( result == EOS_EResult::EOS_Success ) + else if (result == EOS_EResult::EOS_Success) { char buffer[512] = ""; strncpy(buffer, (char*)dummyData, 512 - 1); buffer[4] = '\0'; std::string remoteStr = EOSFuncs::Helpers_t::productIdToString(*remoteIdReturn); - if ( (int)buffer[3] < '0' + if ((int)buffer[3] < '0' && (int)buffer[0] == 0 && (int)buffer[1] == 0 - && (int)buffer[2] == 0 ) + && (int)buffer[2] == 0) { logInfo("Clearing P2P packet queue: remote id: %s received: %d", remoteStr.c_str(), (int)buffer[3]); } @@ -1514,13 +1524,13 @@ bool EOSFuncs::HandleReceivedMessagesAndIgnore(EOS_ProductUserId* remoteIdReturn void EOSFuncs::SendMessageP2P(EOS_ProductUserId RemoteId, const void* data, int len) { - if ( !EOSFuncs::Helpers_t::productIdIsValid(RemoteId) ) + if (!EOSFuncs::Helpers_t::productIdIsValid(RemoteId)) { logError("SendMessageP2P: Invalid remote Id: %s", EOSFuncs::Helpers_t::productIdToString(RemoteId)); return; } - if ( !CurrentUserInfo.isValid() ) + if (!CurrentUserInfo.isValid()) { logError("SendMessageP2P: Invalid local user Id: %s", CurrentUserInfo.getProductUserIdStr()); return; @@ -1532,7 +1542,7 @@ void EOSFuncs::SendMessageP2P(EOS_ProductUserId RemoteId, const void* data, int SocketId.ApiVersion = EOS_P2P_SOCKETID_API_LATEST; strncpy(SocketId.SocketName, "CHAT", 5); - EOS_P2P_SendPacketOptions SendPacketOptions = {}; + EOS_P2P_SendPacketOptions SendPacketOptions{}; SendPacketOptions.ApiVersion = EOS_P2P_SENDPACKET_API_LATEST; SendPacketOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle(); SendPacketOptions.RemoteUserId = RemoteId; @@ -1545,7 +1555,7 @@ void EOSFuncs::SendMessageP2P(EOS_ProductUserId RemoteId, const void* data, int SendPacketOptions.Data = (char*)data; EOS_EResult Result = EOS_P2P_SendPacket(P2PHandle, &SendPacketOptions); - if ( Result != EOS_EResult::EOS_Success ) + if (Result != EOS_EResult::EOS_Success) { logError("SendMessageP2P: error while sending data, code: %d", static_cast(Result)); } @@ -1553,7 +1563,7 @@ void EOSFuncs::SendMessageP2P(EOS_ProductUserId RemoteId, const void* data, int void EOSFuncs::LobbyData_t::setLobbyAttributesFromGame(HostUpdateLobbyTypes updateType) { - if ( updateType == LOBBY_UPDATE_MAIN_MENU ) + if (updateType == LOBBY_UPDATE_MAIN_MENU) { LobbyAttributes.lobbyName = EOS.currentLobbyName; LobbyAttributes.gameVersion = VERSION; @@ -1565,7 +1575,7 @@ void EOSFuncs::LobbyData_t::setLobbyAttributesFromGame(HostUpdateLobbyTypes upda LobbyAttributes.friendsOnly = EOS.bFriendsOnly; LobbyAttributes.maxplayersCompatible = MAXPLAYERS; } - else if ( updateType == LOBBY_UPDATE_DURING_GAME ) + else if (updateType == LOBBY_UPDATE_DURING_GAME) { LobbyAttributes.serverFlags = svFlags; LobbyAttributes.gameCurrentLevel = currentlevel; @@ -1574,7 +1584,7 @@ void EOSFuncs::LobbyData_t::setLobbyAttributesFromGame(HostUpdateLobbyTypes upda void EOSFuncs::LobbyData_t::setBasicCurrentLobbyDataFromInitialJoin(LobbyData_t* lobbyToJoin) { - if ( !lobbyToJoin ) + if (!lobbyToJoin) { logError("setBasicCurrentLobbyDataFromInitialJoin: invalid lobby passed."); return; @@ -1589,7 +1599,7 @@ void EOSFuncs::LobbyData_t::setBasicCurrentLobbyDataFromInitialJoin(LobbyData_t* EOS.P2PConnectionInfo.serverProductId = EOSFuncs::Helpers_t::productIdFromString(OwnerProductUserId.c_str()); EOS.P2PConnectionInfo.peerProductIds.clear(); - for ( PlayerLobbyData_t& player : playersInLobby ) + for (PlayerLobbyData_t& player : playersInLobby) { EOS_ProductUserId productId = EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str()); EOS.P2PConnectionInfo.insertProductIdIntoPeers(productId); @@ -1598,7 +1608,7 @@ void EOSFuncs::LobbyData_t::setBasicCurrentLobbyDataFromInitialJoin(LobbyData_t* bool EOSFuncs::LobbyData_t::currentUserIsOwner() { - if ( currentLobbyIsValid() && OwnerProductUserId.compare(EOS.CurrentUserInfo.getProductUserIdStr()) == 0 ) + if (currentLobbyIsValid() && OwnerProductUserId.compare(EOS.CurrentUserInfo.getProductUserIdStr()) == 0) { return true; } @@ -1607,27 +1617,27 @@ bool EOSFuncs::LobbyData_t::currentUserIsOwner() bool EOSFuncs::LobbyData_t::updateLobbyForHost(HostUpdateLobbyTypes updateType) { - if ( !EOSFuncs::Helpers_t::isMatchingProductIds(EOSFuncs::Helpers_t::productIdFromString(this->OwnerProductUserId.c_str()), - EOS.CurrentUserInfo.getProductUserIdHandle()) ) + if (!EOSFuncs::Helpers_t::isMatchingProductIds(EOSFuncs::Helpers_t::productIdFromString(this->OwnerProductUserId.c_str()), + EOS.CurrentUserInfo.getProductUserIdHandle())) { EOSFuncs::logError("updateLobby: current user is not lobby owner"); return false; } EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle); - EOS_Lobby_UpdateLobbyModificationOptions ModifyOptions = {}; + EOS_Lobby_UpdateLobbyModificationOptions ModifyOptions{}; ModifyOptions.ApiVersion = EOS_LOBBY_UPDATELOBBYMODIFICATION_API_LATEST; ModifyOptions.LobbyId = this->LobbyId.c_str(); ModifyOptions.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle(); - if ( EOS.LobbyModificationHandle ) + if (EOS.LobbyModificationHandle) { EOS_LobbyModification_Release(EOS.LobbyModificationHandle); EOS.LobbyModificationHandle = nullptr; } EOS_HLobbyModification LobbyModification = nullptr; EOS_EResult result = EOS_Lobby_UpdateLobbyModification(LobbyHandle, &ModifyOptions, &LobbyModification); - if ( result != EOS_EResult::EOS_Success ) + if (result != EOS_EResult::EOS_Success) { EOSFuncs::logError("updateLobby: Could not create lobby modification. Error code: %d", static_cast(result)); return false; @@ -1636,19 +1646,19 @@ bool EOSFuncs::LobbyData_t::updateLobbyForHost(HostUpdateLobbyTypes updateType) EOS.LobbyModificationHandle = LobbyModification; setLobbyAttributesFromGame(updateType); - /*EOS_LobbyModification_SetPermissionLevelOptions permissionOptions = {}; + /*EOS_LobbyModification_SetPermissionLevelOptions permissionOptions{}; permissionOptions.ApiVersion = EOS_LOBBYMODIFICATION_SETPERMISSIONLEVEL_API_LATEST; permissionOptions.PermissionLevel = EOS.CurrentLobbyData.PermissionLevel; EOS_LobbyModification_SetPermissionLevel(LobbyModification, &permissionOptions);*/ // build the list of attributes: - for ( int i = 0; i < EOSFuncs::LobbyData_t::kNumAttributes; ++i ) + for (int i = 0; i < EOSFuncs::LobbyData_t::kNumAttributes; ++i) { EOS_Lobby_AttributeData data = {}; data.ApiVersion = EOS_LOBBY_ATTRIBUTEDATA_API_LATEST; data.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING; std::pair dataPair = getAttributePair(static_cast(i)); - if ( dataPair.first.compare("empty") == 0 || dataPair.second.compare("empty") == 0 ) + if (dataPair.first.compare("empty") == 0 || dataPair.second.compare("empty") == 0) { EOSFuncs::logError("updateLobby: invalid data value index: %d first: %s, second: %s", i, dataPair.first.c_str(), dataPair.second.c_str()); continue; @@ -1660,19 +1670,19 @@ bool EOSFuncs::LobbyData_t::updateLobbyForHost(HostUpdateLobbyTypes updateType) data.Key = dataPair.first.c_str(); data.Value.AsUtf8 = dataPair.second.c_str(); - if ( dataPair.first.compare("MAXPLAYERS") == 0 ) + if (dataPair.first.compare("MAXPLAYERS") == 0) { data.Value.AsInt64 = LobbyAttributes.maxplayersCompatible; data.ValueType = EOS_ELobbyAttributeType::EOS_AT_INT64; } - EOS_LobbyModification_AddAttributeOptions addAttributeOptions; + EOS_LobbyModification_AddAttributeOptions addAttributeOptions{}; addAttributeOptions.ApiVersion = EOS_LOBBYMODIFICATION_ADDATTRIBUTE_API_LATEST; addAttributeOptions.Visibility = EOS_ELobbyAttributeVisibility::EOS_LAT_PUBLIC; addAttributeOptions.Attribute = &(data); result = EOS_LobbyModification_AddAttribute(LobbyModification, &addAttributeOptions); - if ( result != EOS_EResult::EOS_Success ) + if (result != EOS_EResult::EOS_Success) { EOSFuncs::logError("updateLobby: Could not add attribute %s. Error code: %d", addAttributeOptions.Attribute->Value.AsUtf8, static_cast(result)); } @@ -1683,13 +1693,13 @@ bool EOSFuncs::LobbyData_t::updateLobbyForHost(HostUpdateLobbyTypes updateType) } // update our client number on the lobby backend - if ( assignClientnumMemberAttribute(EOS.CurrentUserInfo.getProductUserIdHandle(), 0) ) + if (assignClientnumMemberAttribute(EOS.CurrentUserInfo.getProductUserIdHandle(), 0)) { modifyLobbyMemberAttributeForCurrentUser(); } //Trigger lobby update - EOS_Lobby_UpdateLobbyOptions UpdateOptions; + EOS_Lobby_UpdateLobbyOptions UpdateOptions{}; UpdateOptions.ApiVersion = EOS_LOBBY_UPDATELOBBY_API_LATEST; UpdateOptions.LobbyModificationHandle = EOS.LobbyModificationHandle; EOS_Lobby_UpdateLobby(LobbyHandle, &UpdateOptions, nullptr, OnLobbyUpdateFinished); @@ -1700,26 +1710,26 @@ bool EOSFuncs::LobbyData_t::updateLobbyForHost(HostUpdateLobbyTypes updateType) bool EOSFuncs::LobbyData_t::modifyLobbyMemberAttributeForCurrentUser() { - if ( !EOS.CurrentUserInfo.isValid() ) + if (!EOS.CurrentUserInfo.isValid()) { EOSFuncs::logError("modifyLobbyMemberAttributeForCurrentUser: current user is not valid"); return false; } EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle); - EOS_Lobby_UpdateLobbyModificationOptions ModifyOptions; + EOS_Lobby_UpdateLobbyModificationOptions ModifyOptions{}; ModifyOptions.ApiVersion = EOS_LOBBY_UPDATELOBBYMODIFICATION_API_LATEST; ModifyOptions.LobbyId = this->LobbyId.c_str(); ModifyOptions.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle(); - if ( EOS.LobbyMemberModificationHandle ) + if (EOS.LobbyMemberModificationHandle) { EOS_LobbyModification_Release(EOS.LobbyMemberModificationHandle); EOS.LobbyMemberModificationHandle = nullptr; } EOS_HLobbyModification LobbyMemberModification = nullptr; EOS_EResult result = EOS_Lobby_UpdateLobbyModification(LobbyHandle, &ModifyOptions, &LobbyMemberModification); - if ( result != EOS_EResult::EOS_Success ) + if (result != EOS_EResult::EOS_Success) { EOSFuncs::logError("updateLobby: Could not create lobby modification. Error code: %d", static_cast(result)); return false; @@ -1728,10 +1738,10 @@ bool EOSFuncs::LobbyData_t::modifyLobbyMemberAttributeForCurrentUser() EOS.LobbyMemberModificationHandle = LobbyMemberModification; // add attributes for current member - for ( auto& player : playersInLobby ) + for (auto& player : playersInLobby) { EOS_ProductUserId productId = EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str()); - if ( productId != EOS.CurrentUserInfo.getProductUserIdHandle() ) + if (productId != EOS.CurrentUserInfo.getProductUserIdHandle()) { continue; } @@ -1742,13 +1752,13 @@ bool EOSFuncs::LobbyData_t::modifyLobbyMemberAttributeForCurrentUser() memberData.Key = "CLIENTNUM"; memberData.Value.AsInt64 = player.clientNumber; - EOS_LobbyModification_AddMemberAttributeOptions addMemberData; + EOS_LobbyModification_AddMemberAttributeOptions addMemberData{}; addMemberData.ApiVersion = EOS_LOBBYMODIFICATION_ADDMEMBERATTRIBUTE_API_LATEST; addMemberData.Visibility = EOS_ELobbyAttributeVisibility::EOS_LAT_PUBLIC; addMemberData.Attribute = &memberData; result = EOS_LobbyModification_AddMemberAttribute(LobbyMemberModification, &addMemberData); - if ( result != EOS_EResult::EOS_Success ) + if (result != EOS_EResult::EOS_Success) { EOSFuncs::logError("updateLobby: Could not add member attribute %d. Error code: %d", addMemberData.Attribute->Value.AsInt64, static_cast(result)); @@ -1756,7 +1766,7 @@ bool EOSFuncs::LobbyData_t::modifyLobbyMemberAttributeForCurrentUser() } //Trigger lobby update - EOS_Lobby_UpdateLobbyOptions UpdateOptions; + EOS_Lobby_UpdateLobbyOptions UpdateOptions{}; UpdateOptions.ApiVersion = EOS_LOBBY_UPDATELOBBY_API_LATEST; UpdateOptions.LobbyModificationHandle = EOS.LobbyMemberModificationHandle; EOS_Lobby_UpdateLobby(LobbyHandle, &UpdateOptions, nullptr, OnLobbyMemberUpdateFinished); @@ -1767,22 +1777,22 @@ bool EOSFuncs::LobbyData_t::modifyLobbyMemberAttributeForCurrentUser() bool EOSFuncs::LobbyData_t::assignClientnumMemberAttribute(EOS_ProductUserId targetId, int clientNumToSet) { - if ( !EOS.CurrentUserInfo.isValid() ) + if (!EOS.CurrentUserInfo.isValid()) { EOSFuncs::logError("assignClientnumMemberAttribute: current user is not valid"); return false; } - if ( !currentLobbyIsValid() ) + if (!currentLobbyIsValid()) { EOSFuncs::logError("assignClientnumMemberAttribute: current lobby is not valid"); return false; } - for ( auto& player : playersInLobby ) + for (auto& player : playersInLobby) { EOS_ProductUserId playerId = EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str()); - if ( playerId && playerId == targetId ) + if (playerId && playerId == targetId) { player.clientNumber = clientNumToSet; return true; @@ -1793,22 +1803,22 @@ bool EOSFuncs::LobbyData_t::assignClientnumMemberAttribute(EOS_ProductUserId tar int EOSFuncs::LobbyData_t::getClientnumMemberAttribute(EOS_ProductUserId targetId) { - if ( !EOS.CurrentUserInfo.isValid() ) + if (!EOS.CurrentUserInfo.isValid()) { EOSFuncs::logError("getClientnumMemberAttribute: current user is not valid"); return -2; } - if ( !currentLobbyIsValid() ) + if (!currentLobbyIsValid()) { EOSFuncs::logError("getClientnumMemberAttribute: current lobby is not valid"); return -2; } - for ( auto& player : playersInLobby ) + for (auto& player : playersInLobby) { EOS_ProductUserId playerId = EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str()); - if ( playerId && playerId == targetId ) + if (playerId && playerId == targetId) { return player.clientNumber; } @@ -1818,7 +1828,7 @@ int EOSFuncs::LobbyData_t::getClientnumMemberAttribute(EOS_ProductUserId targetI void EOSFuncs::LobbyData_t::getLobbyAttributes(EOS_HLobbyDetails LobbyDetails) { - if ( !currentLobbyIsValid() ) + if (!currentLobbyIsValid()) { EOSFuncs::logError("getLobbyAttributes: invalid current lobby - no ID set"); return; @@ -1826,7 +1836,7 @@ void EOSFuncs::LobbyData_t::getLobbyAttributes(EOS_HLobbyDetails LobbyDetails) EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle); - /*EOS_Lobby_CopyLobbyDetailsHandleOptions CopyHandleOptions; + /*EOS_Lobby_CopyLobbyDetailsHandleOptions CopyHandleOptions{}; CopyHandleOptions.ApiVersion = EOS_LOBBY_COPYLOBBYDETAILSHANDLE_API_LATEST; CopyHandleOptions.LobbyId = this->LobbyId.c_str(); CopyHandleOptions.LocalUserId = EOSFuncs::Helpers_t::productIdFromString(EOS.CurrentUserInfo.getProductUserIdStr()); @@ -1839,19 +1849,19 @@ void EOSFuncs::LobbyData_t::getLobbyAttributes(EOS_HLobbyDetails LobbyDetails) return; }*/ - EOS_LobbyDetails_GetAttributeCountOptions CountOptions; + EOS_LobbyDetails_GetAttributeCountOptions CountOptions{}; CountOptions.ApiVersion = EOS_LOBBYDETAILS_GETATTRIBUTECOUNT_API_LATEST; int numAttributes = EOS_LobbyDetails_GetAttributeCount(LobbyDetails, &CountOptions); - for ( int i = 0; i < numAttributes; ++i ) + for (int i = 0; i < numAttributes; ++i) { - EOS_LobbyDetails_CopyAttributeByIndexOptions AttrOptions; + EOS_LobbyDetails_CopyAttributeByIndexOptions AttrOptions{}; AttrOptions.ApiVersion = EOS_LOBBYDETAILS_COPYATTRIBUTEBYINDEX_API_LATEST; AttrOptions.AttrIndex = i; EOS_Lobby_Attribute* attributePtr = nullptr; EOS_EResult result = EOS_LobbyDetails_CopyAttributeByIndex(LobbyDetails, &AttrOptions, &attributePtr); - if ( result == EOS_EResult::EOS_Success && attributePtr->Data ) + if (result == EOS_EResult::EOS_Success && attributePtr->Data) { EOS_Lobby_AttributeData data; data.ApiVersion = EOS_LOBBY_ATTRIBUTEDATA_API_LATEST; @@ -1863,7 +1873,7 @@ void EOSFuncs::LobbyData_t::getLobbyAttributes(EOS_HLobbyDetails LobbyDetails) EOS_Lobby_Attribute_Release(attributePtr); } - if ( !EOS.CurrentLobbyData.currentUserIsOwner() ) + if (!EOS.CurrentLobbyData.currentUserIsOwner()) { strncpy(EOS.currentLobbyName, LobbyAttributes.lobbyName.c_str(), 31); } @@ -1878,11 +1888,11 @@ void EOSFuncs::createLobby() return; }*/ - if ( CurrentLobbyData.bAwaitingLeaveCallback ) + if (CurrentLobbyData.bAwaitingLeaveCallback) { logInfo("createLobby: CurrentLobbyData.bAwaitingLeaveCallback is true"); } - if ( CurrentLobbyData.bAwaitingCreationCallback ) + if (CurrentLobbyData.bAwaitingCreationCallback) { logInfo("createLobby: CurrentLobbyData.bAwaitingCreationCallback is true"); } @@ -1895,14 +1905,14 @@ void EOSFuncs::createLobby() #else bFriendsOnly = loadingsavegame ? false : bFriendsOnlyUserConfigured; #endif - if ( loadingsavegame ) + if (loadingsavegame) { currentPermissionLevel = EOS_ELobbyPermissionLevel::EOS_LPL_PUBLICADVERTISED; } else { currentPermissionLevel = currentPermissionLevelUserConfigured; - if ( currentPermissionLevel == EOS_ELobbyPermissionLevel::EOS_LPL_JOINVIAPRESENCE ) + if (currentPermissionLevel == EOS_ELobbyPermissionLevel::EOS_LPL_JOINVIAPRESENCE) { bFriendsOnly = false; bFriendsOnlyUserConfigured = false; @@ -1910,7 +1920,7 @@ void EOSFuncs::createLobby() } LobbyHandle = EOS_Platform_GetLobbyInterface(PlatformHandle); - EOS_Lobby_CreateLobbyOptions CreateOptions; + EOS_Lobby_CreateLobbyOptions CreateOptions{}; CreateOptions.ApiVersion = EOS_LOBBY_CREATELOBBY_API_LATEST; CreateOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle(); CreateOptions.MaxLobbyMembers = MAXPLAYERS; @@ -1931,20 +1941,20 @@ void EOSFuncs::createLobby() void EOSFuncs::joinLobby(LobbyData_t* lobby) { - if ( !lobby ) + if (!lobby) { return; } LobbyHandle = EOS_Platform_GetLobbyInterface(PlatformHandle); - if ( CurrentLobbyData.currentLobbyIsValid() ) + if (CurrentLobbyData.currentLobbyIsValid()) { - if ( CurrentLobbyData.LobbyId.compare(lobby->LobbyId) == 0 ) + if (CurrentLobbyData.LobbyId.compare(lobby->LobbyId) == 0) { logInfo("joinLobby: attempting to join current lobby"); return; } - if ( CurrentLobbyData.bAwaitingLeaveCallback ) + if (CurrentLobbyData.bAwaitingLeaveCallback) { logInfo("joinLobby: CurrentLobbyData.bAwaitingLeaveCallback is true"); } @@ -1958,24 +1968,24 @@ void EOSFuncs::joinLobby(LobbyData_t* lobby) CurrentLobbyData.setBasicCurrentLobbyDataFromInitialJoin(lobby); bool errorOnJoin = false; - if ( CurrentLobbyData.OwnerProductUserId.compare("NULL") == 0 ) + if (CurrentLobbyData.OwnerProductUserId.compare("NULL") == 0) { // this is unexpected - perhaps an attempt to join a lobby that was freshly abandoned ConnectingToLobbyStatus = LobbyHandler_t::EResult_LobbyFailures::LOBBY_NO_OWNER; logError("joinLobby: attempting to join a lobby with a NULL owner: %s, aborting.", CurrentLobbyData.LobbyId.c_str()); errorOnJoin = true; } - else if ( lobby->LobbyAttributes.isLobbyLoadingSavedGame != loadingsavegame ) + else if (lobby->LobbyAttributes.isLobbyLoadingSavedGame != loadingsavegame) { // loading save game, but incorrect assertion from client side. - if ( loadingsavegame == 0 ) + if (loadingsavegame == 0) { // try reload from your other savefiles since this didn't match the default savegameIndex. bool foundSave = false; - for ( int c = 0; c < SAVE_GAMES_MAX; ++c ) { + for (int c = 0; c < SAVE_GAMES_MAX; ++c) { auto info = getSaveGameInfo(false, c); - if ( info.game_version != -1 ) { - if ( info.gamekey == lobby->LobbyAttributes.isLobbyLoadingSavedGame ) { + if (info.game_version != -1) { + if (info.gamekey == lobby->LobbyAttributes.isLobbyLoadingSavedGame) { savegameCurrentFileIndex = c; foundSave = true; break; @@ -1983,27 +1993,27 @@ void EOSFuncs::joinLobby(LobbyData_t* lobby) } } - if ( foundSave ) { + if (foundSave) { loadingsavegame = lobby->LobbyAttributes.isLobbyLoadingSavedGame; auto info = getSaveGameInfo(false, savegameCurrentFileIndex); - for ( int c = 0; c < MAXPLAYERS; ++c ) { - if ( info.players_connected[c] ) { + for (int c = 0; c < MAXPLAYERS; ++c) { + if (info.players_connected[c]) { loadGame(c, info); } } } - else + else { ConnectingToLobbyStatus = LobbyHandler_t::EResult_LobbyFailures::LOBBY_USING_SAVEGAME; errorOnJoin = true; } } - else if ( loadingsavegame > 0 && lobby->LobbyAttributes.isLobbyLoadingSavedGame == 0 ) + else if (loadingsavegame > 0 && lobby->LobbyAttributes.isLobbyLoadingSavedGame == 0) { ConnectingToLobbyStatus = LobbyHandler_t::EResult_LobbyFailures::LOBBY_NOT_USING_SAVEGAME; errorOnJoin = true; } - else if ( loadingsavegame > 0 && lobby->LobbyAttributes.isLobbyLoadingSavedGame > 0 ) + else if (loadingsavegame > 0 && lobby->LobbyAttributes.isLobbyLoadingSavedGame > 0) { ConnectingToLobbyStatus = LobbyHandler_t::EResult_LobbyFailures::LOBBY_WRONG_SAVEGAME; errorOnJoin = true; @@ -2014,7 +2024,7 @@ void EOSFuncs::joinLobby(LobbyData_t* lobby) errorOnJoin = true; } } - else if ( lobby->LobbyAttributes.gameCurrentLevel >= 0 ) + else if (lobby->LobbyAttributes.gameCurrentLevel >= 0) { /*if ( lobby->LobbyAttributes.gameCurrentLevel == 0 ) { @@ -2027,7 +2037,7 @@ void EOSFuncs::joinLobby(LobbyData_t* lobby) errorOnJoin = true; } - if ( errorOnJoin ) + if (errorOnJoin) { bConnectingToLobbyWindow = false; bConnectingToLobby = false; @@ -2057,7 +2067,7 @@ void EOSFuncs::leaveLobby() //} // attempt to destroy the lobby if leaving and we are the owner. - if ( CurrentLobbyData.currentUserIsOwner() ) + if (CurrentLobbyData.currentUserIsOwner()) { CurrentLobbyData.destroyLobby(); return; @@ -2065,7 +2075,7 @@ void EOSFuncs::leaveLobby() LobbyHandle = EOS_Platform_GetLobbyInterface(PlatformHandle); - EOS_Lobby_LeaveLobbyOptions LeaveOptions; + EOS_Lobby_LeaveLobbyOptions LeaveOptions{}; LeaveOptions.ApiVersion = EOS_LOBBY_LEAVELOBBY_API_LATEST; LeaveOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle(); LeaveOptions.LobbyId = CurrentLobbyData.LobbyId.c_str(); @@ -2080,104 +2090,104 @@ void EOSFuncs::searchLobbies(LobbyParameters_t::LobbySearchOptions searchType, { LobbySearchResults.lastResultWasFiltered = false; - if ( LobbySearchResults.useLobbyCode ) + if (LobbySearchResults.useLobbyCode) { - for ( int c = 0; c < 4 && EOS.lobbySearchByCode[c] != 0; ++c ) - { - if ( EOS.lobbySearchByCode[c] >= 'A' && EOS.lobbySearchByCode[c] <= 'Z' ) - { - EOS.lobbySearchByCode[c] = 'a' + (EOS.lobbySearchByCode[c] - 'A'); // to lowercase. - } - } + for (int c = 0; c < 4 && EOS.lobbySearchByCode[c] != 0; ++c) + { + if (EOS.lobbySearchByCode[c] >= 'A' && EOS.lobbySearchByCode[c] <= 'Z') + { + EOS.lobbySearchByCode[c] = 'a' + (EOS.lobbySearchByCode[c] - 'A'); // to lowercase. + } + } } LobbyHandle = EOS_Platform_GetLobbyInterface(PlatformHandle); logInfo("searchLobbies: starting search"); - EOS_Lobby_CreateLobbySearchOptions CreateSearchOptions = {}; + EOS_Lobby_CreateLobbySearchOptions CreateSearchOptions{}; CreateSearchOptions.ApiVersion = EOS_LOBBY_CREATELOBBYSEARCH_API_LATEST; CreateSearchOptions.MaxResults = kMaxLobbiesToSearch; EOS_HLobbySearch LobbySearch = nullptr; - if ( LobbySearchResults.CurrentLobbySearch != nullptr ) + if (LobbySearchResults.CurrentLobbySearch != nullptr) { EOS_LobbySearch_Release(LobbySearchResults.CurrentLobbySearch); LobbySearchResults.CurrentLobbySearch = nullptr; } EOS_EResult result = EOS_Lobby_CreateLobbySearch(LobbyHandle, &CreateSearchOptions, &LobbySearch); - if ( result != EOS_EResult::EOS_Success ) + if (result != EOS_EResult::EOS_Success) { logError("searchLobbies: EOS_Lobby_CreateLobbySearch failure: %d", static_cast(result)); return; } LobbySearchResults.CurrentLobbySearch = LobbySearch; - for ( auto& result : LobbySearchResults.results ) + for (auto& result : LobbySearchResults.results) { result.ClearData(); } LobbySearchResults.results.clear(); LobbySearchResults.resultsSortedForDisplay.clear(); - /*EOS_LobbySearch_SetTargetUserIdOptions SetLobbyOptions = {}; + /*EOS_LobbySearch_SetTargetUserIdOptions SetLobbyOptions{}; SetLobbyOptions.ApiVersion = EOS_LOBBYSEARCH_SETLOBBYID_API_LATEST; SetLobbyOptions.TargetUserId = CurrentUserInfo.Friends.at(0).UserId; Result = EOS_LobbySearch_SetTargetUserId(LobbySearch, &SetLobbyOptions);*/ - if ( searchType != LobbyParameters_t::LOBBY_SEARCH_BY_LOBBYID ) - { - EOS_LobbySearch_SetParameterOptions ParamOptions = {}; - ParamOptions.ApiVersion = EOS_LOBBYSEARCH_SETPARAMETER_API_LATEST; - ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_NOTANYOF; - - EOS_Lobby_AttributeData AttrData; - AttrData.ApiVersion = EOS_LOBBY_ATTRIBUTEDATA_API_LATEST; - ParamOptions.Parameter = &AttrData; - AttrData.Key = "VER"; - AttrData.Value.AsUtf8 = "0.0.0"; - AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING; - EOS_EResult resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions); - - if ( LobbySearchResults.useLobbyCode && strcmp(lobbySearchByCode, "") ) - { - ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_EQUAL; - AttrData.Key = "JOINKEY"; - AttrData.Value.AsUtf8 = lobbySearchByCode; - AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING; - resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions); - } - else - { - /*ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_EQUAL; - AttrData.Key = "PERMISSIONLEVEL"; - AttrData.Value.AsUtf8 = "0"; - AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING; - resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions);*/ - } - - ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_EQUAL; - AttrData.Key = "MAXPLAYERS"; - AttrData.Value.AsInt64 = MAXPLAYERS; - AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_INT64; - resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions); - - if ( !LobbySearchResults.showLobbiesInProgress ) - { - ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_EQUAL; - AttrData.Key = "CURRENTLEVEL"; - AttrData.Value.AsUtf8 = "-1"; - AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING; - resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions); - } - } - else if ( searchType == LobbyParameters_t::LOBBY_SEARCH_BY_LOBBYID ) + if (searchType != LobbyParameters_t::LOBBY_SEARCH_BY_LOBBYID) + { + EOS_LobbySearch_SetParameterOptions ParamOptions{}; + ParamOptions.ApiVersion = EOS_LOBBYSEARCH_SETPARAMETER_API_LATEST; + ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_NOTANYOF; + + EOS_Lobby_AttributeData AttrData; + AttrData.ApiVersion = EOS_LOBBY_ATTRIBUTEDATA_API_LATEST; + ParamOptions.Parameter = &AttrData; + AttrData.Key = "VER"; + AttrData.Value.AsUtf8 = "0.0.0"; + AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING; + EOS_EResult resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions); + + if (LobbySearchResults.useLobbyCode && strcmp(lobbySearchByCode, "")) + { + ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_EQUAL; + AttrData.Key = "JOINKEY"; + AttrData.Value.AsUtf8 = lobbySearchByCode; + AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING; + resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions); + } + else + { + /*ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_EQUAL; + AttrData.Key = "PERMISSIONLEVEL"; + AttrData.Value.AsUtf8 = "0"; + AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING; + resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions);*/ + } + + ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_EQUAL; + AttrData.Key = "MAXPLAYERS"; + AttrData.Value.AsInt64 = MAXPLAYERS; + AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_INT64; + resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions); + + if (!LobbySearchResults.showLobbiesInProgress) + { + ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_EQUAL; + AttrData.Key = "CURRENTLEVEL"; + AttrData.Value.AsUtf8 = "-1"; + AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING; + resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions); + } + } + else if (searchType == LobbyParameters_t::LOBBY_SEARCH_BY_LOBBYID) { // appends criteria to search for within the normal search function - EOS_LobbySearch_SetLobbyIdOptions SetLobbyOptions = {}; + EOS_LobbySearch_SetLobbyIdOptions SetLobbyOptions{}; SetLobbyOptions.ApiVersion = EOS_LOBBYSEARCH_SETLOBBYID_API_LATEST; SetLobbyOptions.LobbyId = lobbyIdToSearch; EOS_LobbySearch_SetLobbyId(LobbySearch, &SetLobbyOptions); } - EOS_LobbySearch_FindOptions FindOptions; + EOS_LobbySearch_FindOptions FindOptions{}; FindOptions.ApiVersion = EOS_LOBBYSEARCH_FIND_API_LATEST; FindOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle(); @@ -2188,13 +2198,13 @@ void EOSFuncs::searchLobbies(LobbyParameters_t::LobbySearchOptions searchType, void EOSFuncs::LobbyData_t::destroyLobby() { - if ( !currentLobbyIsValid() ) + if (!currentLobbyIsValid()) { EOSFuncs::logError("destroyLobby: invalid current lobby - no ID set"); return; } - if ( !currentUserIsOwner() ) + if (!currentUserIsOwner()) { EOSFuncs::logError("destroyLobby: current user is not lobby owner"); return; @@ -2202,7 +2212,7 @@ void EOSFuncs::LobbyData_t::destroyLobby() EOS.LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle); - EOS_Lobby_DestroyLobbyOptions DestroyOptions; + EOS_Lobby_DestroyLobbyOptions DestroyOptions{}; DestroyOptions.ApiVersion = EOS_LOBBY_DESTROYLOBBY_API_LATEST; DestroyOptions.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle(); DestroyOptions.LobbyId = LobbyId.c_str(); @@ -2217,21 +2227,21 @@ void EOSFuncs::LobbyData_t::destroyLobby() void EOSFuncs::LobbyData_t::updateLobbyDuringGameLoop() { - if ( !currentLobbyIsValid() ) + if (!currentLobbyIsValid()) { return; } bool doUpdate = false; - if ( LobbyAttributes.gameCurrentLevel != currentlevel ) + if (LobbyAttributes.gameCurrentLevel != currentlevel) { doUpdate = true; } - if ( LobbyAttributes.serverFlags != svFlags ) + if (LobbyAttributes.serverFlags != svFlags) { doUpdate = true; } - if ( doUpdate ) + if (doUpdate) { updateLobbyForHost(LOBBY_UPDATE_DURING_GAME); } @@ -2239,7 +2249,7 @@ void EOSFuncs::LobbyData_t::updateLobbyDuringGameLoop() void EOSFuncs::LobbyData_t::updateLobby() { - if ( !EOS.CurrentUserInfo.isValid() ) + if (!EOS.CurrentUserInfo.isValid()) { EOSFuncs::logError("updateLobby: invalid current user"); return; @@ -2247,14 +2257,14 @@ void EOSFuncs::LobbyData_t::updateLobby() EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle); - EOS_Lobby_CopyLobbyDetailsHandleOptions CopyHandleOptions; + EOS_Lobby_CopyLobbyDetailsHandleOptions CopyHandleOptions{}; CopyHandleOptions.ApiVersion = EOS_LOBBY_COPYLOBBYDETAILSHANDLE_API_LATEST; CopyHandleOptions.LobbyId = LobbyId.c_str(); CopyHandleOptions.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle(); EOS_HLobbyDetails LobbyDetailsHandle = nullptr; EOS_EResult result = EOS_Lobby_CopyLobbyDetailsHandle(LobbyHandle, &CopyHandleOptions, &LobbyDetailsHandle); - if ( result != EOS_EResult::EOS_Success ) + if (result != EOS_EResult::EOS_Success) { EOSFuncs::logError("OnLobbyUpdateReceived: can't get lobby info handle. Error code: %d", static_cast(result)); return; @@ -2267,34 +2277,34 @@ void EOSFuncs::LobbyData_t::updateLobby() void EOSFuncs::LobbyData_t::getLobbyMemberInfo(EOS_HLobbyDetails LobbyDetails) { - if ( !currentLobbyIsValid() ) + if (!currentLobbyIsValid()) { EOSFuncs::logError("getLobbyMemberInfo: invalid current lobby - no ID set"); return; } EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle); - EOS_LobbyDetails_GetMemberCountOptions MemberCountOptions; + EOS_LobbyDetails_GetMemberCountOptions MemberCountOptions{}; MemberCountOptions.ApiVersion = EOS_LOBBYDETAILS_GETMEMBERCOUNT_API_LATEST; Uint32 numPlayers = EOS_LobbyDetails_GetMemberCount(LobbyDetails, &MemberCountOptions); EOSFuncs::logInfo("getLobbyMemberInfo: NumPlayers in lobby: %d", numPlayers); // so we don't have to wait for a new callback to retrieve names - std::unordered_map previousPlayerNames; - for ( auto& player : playersInLobby ) + std::unordered_map previousPlayerNames; + for (auto& player : playersInLobby) { EOS_ProductUserId id = EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str()); - if ( id ) + if (id) { previousPlayerNames.insert(std::pair(id, player.name)); } } this->playersInLobby.clear(); - EOS_LobbyDetails_GetMemberByIndexOptions MemberByIndexOptions; + EOS_LobbyDetails_GetMemberByIndexOptions MemberByIndexOptions{}; MemberByIndexOptions.ApiVersion = EOS_LOBBYDETAILS_GETMEMBERBYINDEX_API_LATEST; - for ( Uint32 i = 0; i < numPlayers; ++i ) + for (Uint32 i = 0; i < numPlayers; ++i) { MemberByIndexOptions.MemberIndex = i; EOS_ProductUserId memberId = EOS_LobbyDetails_GetMemberByIndex(LobbyDetails, &MemberByIndexOptions); @@ -2304,40 +2314,40 @@ void EOSFuncs::LobbyData_t::getLobbyMemberInfo(EOS_HLobbyDetails LobbyDetails) newPlayer.memberProductUserId = EOSFuncs::Helpers_t::productIdToString(memberId); newPlayer.name = "Pending..."; auto idMapping = EOS.AccountMappings.find(memberId); - if ( idMapping != EOS.AccountMappings.end() && idMapping->second != nullptr ) + if (idMapping != EOS.AccountMappings.end() && idMapping->second != nullptr) { newPlayer.memberEpicAccountId = EOSFuncs::Helpers_t::epicIdToString(idMapping->second); } auto previousPlayer = previousPlayerNames.find(memberId); - if ( previousPlayer != previousPlayerNames.end() ) + if (previousPlayer != previousPlayerNames.end()) { // replace "pending..." with the player name we previously knew about. newPlayer.name = previousPlayer->second; } //member attributes - EOS_LobbyDetails_GetMemberAttributeCountOptions MemberAttributeCountOptions; + EOS_LobbyDetails_GetMemberAttributeCountOptions MemberAttributeCountOptions{}; MemberAttributeCountOptions.ApiVersion = EOS_LOBBYDETAILS_GETMEMBERATTRIBUTECOUNT_API_LATEST; MemberAttributeCountOptions.TargetUserId = memberId; const Uint32 numAttributes = EOS_LobbyDetails_GetMemberAttributeCount(LobbyDetails, &MemberAttributeCountOptions); - for ( Uint32 j = 0; j < numAttributes; ++j ) + for (Uint32 j = 0; j < numAttributes; ++j) { - EOS_LobbyDetails_CopyMemberAttributeByIndexOptions MemberAttributeCopyOptions; + EOS_LobbyDetails_CopyMemberAttributeByIndexOptions MemberAttributeCopyOptions{}; MemberAttributeCopyOptions.ApiVersion = EOS_LOBBYDETAILS_COPYMEMBERATTRIBUTEBYINDEX_API_LATEST; MemberAttributeCopyOptions.AttrIndex = j; MemberAttributeCopyOptions.TargetUserId = memberId; EOS_Lobby_Attribute* MemberAttribute = nullptr; EOS_EResult result = EOS_LobbyDetails_CopyMemberAttributeByIndex(LobbyDetails, &MemberAttributeCopyOptions, &MemberAttribute); - if ( result != EOS_EResult::EOS_Success ) + if (result != EOS_EResult::EOS_Success) { - EOSFuncs::logError("getLobbyMemberInfo: can't copy member attribute, error code: %d", + EOSFuncs::logError("getLobbyMemberInfo: can't copy member attribute, error code: %d", static_cast(result)); continue; } std::string key = MemberAttribute->Data->Key; - if ( key.compare("CLIENTNUM") == 0 ) + if (key.compare("CLIENTNUM") == 0) { newPlayer.clientNumber = static_cast(MemberAttribute->Data->Value.AsInt64); EOSFuncs::logInfo("Read clientnum: %d for user: %s", newPlayer.clientNumber, newPlayer.memberProductUserId.c_str()); @@ -2355,7 +2365,7 @@ void EOSFuncs::LobbyData_t::getLobbyMemberInfo(EOS_HLobbyDetails LobbyDetails) void EOSFuncs::queryLocalExternalAccountId(EOS_EExternalAccountType accountType) { - EOS_Connect_QueryProductUserIdMappingsOptions QueryOptions = {}; + EOS_Connect_QueryProductUserIdMappingsOptions QueryOptions{}; QueryOptions.ApiVersion = EOS_CONNECT_QUERYPRODUCTUSERIDMAPPINGS_API_LATEST; QueryOptions.AccountIdType_DEPRECATED = accountType; QueryOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle(); @@ -2364,10 +2374,10 @@ void EOSFuncs::queryLocalExternalAccountId(EOS_EExternalAccountType accountType) QueryOptions.ProductUserIdCount = ids.size(); QueryOptions.ProductUserIds = ids.data(); - for ( EOS_ProductUserId& id : ids ) + for (EOS_ProductUserId& id : ids) { auto findExternalAccounts = ExternalAccountMappings.find(id); - if ( findExternalAccounts == ExternalAccountMappings.end() ) + if (findExternalAccounts == ExternalAccountMappings.end()) { // if the mapping doesn't exist, add to set. otherwise we already know the account id for the given product id ProductIdsAwaitingAccountMappingCallback.insert(id); @@ -2384,15 +2394,15 @@ void EOSFuncs::queryLocalExternalAccountId(EOS_EExternalAccountType accountType) void EOSFuncs::queryAccountIdFromProductId(LobbyData_t* lobby/*, std::vector& accountsToQuery*/) { - if ( !lobby ) + if (!lobby) { return; } - if ( lobby->lobbyMembersQueueToMappingUpdate.empty() ) + if (lobby->lobbyMembersQueueToMappingUpdate.empty()) { return; } - EOS_Connect_QueryProductUserIdMappingsOptions QueryOptions = {}; + EOS_Connect_QueryProductUserIdMappingsOptions QueryOptions{}; QueryOptions.ApiVersion = EOS_CONNECT_QUERYPRODUCTUSERIDMAPPINGS_API_LATEST; //QueryOptions.AccountIdType_DEPRECATED = EOS_EExternalAccountType::EOS_EAT_EPIC; QueryOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle(); @@ -2400,13 +2410,13 @@ void EOSFuncs::queryAccountIdFromProductId(LobbyData_t* lobby/*, std::vectorlobbyMembersQueueToMappingUpdate.size(); QueryOptions.ProductUserIds = lobby->lobbyMembersQueueToMappingUpdate.data(); - for ( EOS_ProductUserId& id : lobby->lobbyMembersQueueToMappingUpdate ) + for (EOS_ProductUserId& id : lobby->lobbyMembersQueueToMappingUpdate) { auto findEpicAccounts = AccountMappings.find(id); auto findExternalAccounts = ExternalAccountMappings.find(id); - if ( findEpicAccounts == AccountMappings.end() || (*findEpicAccounts).second == nullptr ) + if (findEpicAccounts == AccountMappings.end() || (*findEpicAccounts).second == nullptr) { - if ( findExternalAccounts == ExternalAccountMappings.end() ) + if (findExternalAccounts == ExternalAccountMappings.end()) { // if the mapping doesn't exist, add to set. otherwise we already know the account id for the given product id ProductIdsAwaitingAccountMappingCallback.insert(id); @@ -2436,62 +2446,62 @@ std::pair EOSFuncs::LobbyData_t::getAttributePair(Attr std::pair attributePair; attributePair.first = "empty"; attributePair.second = "empty"; - switch ( type ) + switch (type) { - case LOBBY_NAME: - attributePair.first = "NAME"; - attributePair.second = this->LobbyAttributes.lobbyName; - break; - case GAME_VERSION: - attributePair.first = "VER"; - attributePair.second = this->LobbyAttributes.gameVersion; - break; - case GAME_MAXPLAYERS: - attributePair.first = "MAXPLAYERS"; - attributePair.second = ""; - break; - case SERVER_FLAGS: - attributePair.first = "SVFLAGS"; - char svFlagsChar[32]; - snprintf(svFlagsChar, 31, "%d", this->LobbyAttributes.serverFlags); - attributePair.second = svFlagsChar; - break; - case LOADING_SAVEGAME: - attributePair.first = "LOADINGSAVEGAME"; - attributePair.second = std::to_string(this->LobbyAttributes.isLobbyLoadingSavedGame); - break; - case GAME_MODS: - attributePair.first = "SVNUMMODS"; - attributePair.second = std::to_string(this->LobbyAttributes.numServerMods); - break; - case GAME_MODS_DISABLE_ACHIEVEMENTS: - attributePair.first = "SVMODDISABLEACH"; - attributePair.second = this->LobbyAttributes.modsDisableAchievements ? "1" : "0"; - break; - case CREATION_TIME: - attributePair.first = "CREATETIME"; - attributePair.second = std::to_string(this->LobbyAttributes.lobbyCreationTime); - break; - case GAME_CURRENT_LEVEL: - attributePair.first = "CURRENTLEVEL"; - attributePair.second = std::to_string(this->LobbyAttributes.gameCurrentLevel); - break; - case GAME_JOIN_KEY: - attributePair.first = "JOINKEY"; - attributePair.second = this->LobbyAttributes.gameJoinKey; - break; - case LOBBY_PERMISSION_LEVEL: - attributePair.first = "PERMISSIONLEVEL"; - char permissionLevel[32]; - snprintf(permissionLevel, sizeof(permissionLevel), "%d", this->LobbyAttributes.PermissionLevel); - attributePair.second = permissionLevel; - break; - case FRIENDS_ONLY: - attributePair.first = "FRIENDSONLY"; - attributePair.second = this->LobbyAttributes.friendsOnly ? "true" : "false"; - break; - default: - break; + case LOBBY_NAME: + attributePair.first = "NAME"; + attributePair.second = this->LobbyAttributes.lobbyName; + break; + case GAME_VERSION: + attributePair.first = "VER"; + attributePair.second = this->LobbyAttributes.gameVersion; + break; + case GAME_MAXPLAYERS: + attributePair.first = "MAXPLAYERS"; + attributePair.second = ""; + break; + case SERVER_FLAGS: + attributePair.first = "SVFLAGS"; + char svFlagsChar[32]; + snprintf(svFlagsChar, 31, "%d", this->LobbyAttributes.serverFlags); + attributePair.second = svFlagsChar; + break; + case LOADING_SAVEGAME: + attributePair.first = "LOADINGSAVEGAME"; + attributePair.second = std::to_string(this->LobbyAttributes.isLobbyLoadingSavedGame); + break; + case GAME_MODS: + attributePair.first = "SVNUMMODS"; + attributePair.second = std::to_string(this->LobbyAttributes.numServerMods); + break; + case GAME_MODS_DISABLE_ACHIEVEMENTS: + attributePair.first = "SVMODDISABLEACH"; + attributePair.second = this->LobbyAttributes.modsDisableAchievements ? "1" : "0"; + break; + case CREATION_TIME: + attributePair.first = "CREATETIME"; + attributePair.second = std::to_string(this->LobbyAttributes.lobbyCreationTime); + break; + case GAME_CURRENT_LEVEL: + attributePair.first = "CURRENTLEVEL"; + attributePair.second = std::to_string(this->LobbyAttributes.gameCurrentLevel); + break; + case GAME_JOIN_KEY: + attributePair.first = "JOINKEY"; + attributePair.second = this->LobbyAttributes.gameJoinKey; + break; + case LOBBY_PERMISSION_LEVEL: + attributePair.first = "PERMISSIONLEVEL"; + char permissionLevel[32]; + snprintf(permissionLevel, sizeof(permissionLevel), "%d", this->LobbyAttributes.PermissionLevel); + attributePair.second = permissionLevel; + break; + case FRIENDS_ONLY: + attributePair.first = "FRIENDSONLY"; + attributePair.second = this->LobbyAttributes.friendsOnly ? "true" : "false"; + break; + default: + break; } return attributePair; } @@ -2499,51 +2509,51 @@ std::pair EOSFuncs::LobbyData_t::getAttributePair(Attr void EOSFuncs::LobbyData_t::setLobbyAttributesAfterReading(EOS_Lobby_AttributeData* data) { std::string keyName = data->Key; - if ( keyName.compare("NAME") == 0 ) + if (keyName.compare("NAME") == 0) { this->LobbyAttributes.lobbyName = data->Value.AsUtf8; } - else if ( keyName.compare("VER") == 0 ) + else if (keyName.compare("VER") == 0) { this->LobbyAttributes.gameVersion = data->Value.AsUtf8; } - else if ( keyName.compare("MAXPLAYERS") == 0 ) + else if (keyName.compare("MAXPLAYERS") == 0) { this->LobbyAttributes.maxplayersCompatible = data->Value.AsInt64; } - else if ( keyName.compare("SVFLAGS") == 0 ) + else if (keyName.compare("SVFLAGS") == 0) { this->LobbyAttributes.serverFlags = std::stoi(data->Value.AsUtf8); } - else if ( keyName.compare("LOADINGSAVEGAME") == 0 ) + else if (keyName.compare("LOADINGSAVEGAME") == 0) { this->LobbyAttributes.isLobbyLoadingSavedGame = std::stoul(data->Value.AsUtf8); } - else if ( keyName.compare("SVNUMMODS") == 0 ) + else if (keyName.compare("SVNUMMODS") == 0) { this->LobbyAttributes.numServerMods = std::stoi(data->Value.AsUtf8); } - else if ( keyName.compare("SVMODDISABLEACH") == 0 ) + else if (keyName.compare("SVMODDISABLEACH") == 0) { this->LobbyAttributes.modsDisableAchievements = std::stoi(data->Value.AsUtf8) > 0 ? 1 : 0; } - else if ( keyName.compare("CREATETIME") == 0 ) + else if (keyName.compare("CREATETIME") == 0) { this->LobbyAttributes.lobbyCreationTime = std::stoll(data->Value.AsUtf8); } - else if ( keyName.compare("CURRENTLEVEL") == 0 ) + else if (keyName.compare("CURRENTLEVEL") == 0) { this->LobbyAttributes.gameCurrentLevel = std::stoi(data->Value.AsUtf8); } - else if ( keyName.compare("JOINKEY") == 0 ) + else if (keyName.compare("JOINKEY") == 0) { this->LobbyAttributes.gameJoinKey = data->Value.AsUtf8; } - else if ( keyName.compare("PERMISSIONLEVEL") == 0 ) + else if (keyName.compare("PERMISSIONLEVEL") == 0) { this->LobbyAttributes.PermissionLevel = std::stoi(data->Value.AsUtf8); } - else if ( keyName.compare("FRIENDSONLY") == 0 ) + else if (keyName.compare("FRIENDSONLY") == 0) { this->LobbyAttributes.friendsOnly = strcmp(data->Value.AsUtf8, "true") == 0; } @@ -2551,14 +2561,14 @@ void EOSFuncs::LobbyData_t::setLobbyAttributesAfterReading(EOS_Lobby_AttributeDa EOS_ProductUserId EOSFuncs::P2PConnectionInfo_t::getPeerIdFromIndex(int index) const { - if ( index == 0 && multiplayer == CLIENT ) + if (index == 0 && multiplayer == CLIENT) { return serverProductId; } - for ( auto& pair : peerProductIds ) + for (auto& pair : peerProductIds) { - if ( pair.second == index ) + if (pair.second == index) { return pair.first; } @@ -2569,13 +2579,13 @@ EOS_ProductUserId EOSFuncs::P2PConnectionInfo_t::getPeerIdFromIndex(int index) c int EOSFuncs::P2PConnectionInfo_t::getIndexFromPeerId(EOS_ProductUserId id) const { - if ( !id ) + if (!id) { return 0; } - for ( auto& pair : peerProductIds ) + for (auto& pair : peerProductIds) { - if ( pair.first == id ) + if (pair.first == id) { return pair.second; } @@ -2586,13 +2596,13 @@ int EOSFuncs::P2PConnectionInfo_t::getIndexFromPeerId(EOS_ProductUserId id) cons bool EOSFuncs::P2PConnectionInfo_t::isPeerIndexed(EOS_ProductUserId id) { - if ( id == serverProductId ) + if (id == serverProductId) { return true; } - for ( auto& pair : peerProductIds ) + for (auto& pair : peerProductIds) { - if ( pair.first == id ) + if (pair.first == id) { return true; } @@ -2602,9 +2612,9 @@ bool EOSFuncs::P2PConnectionInfo_t::isPeerIndexed(EOS_ProductUserId id) bool EOSFuncs::P2PConnectionInfo_t::assignPeerIndex(EOS_ProductUserId id, int index) { - for ( auto& pair : peerProductIds ) + for (auto& pair : peerProductIds) { - if ( pair.first == id ) + if (pair.first == id) { pair.second = index; return true; @@ -2615,11 +2625,11 @@ bool EOSFuncs::P2PConnectionInfo_t::assignPeerIndex(EOS_ProductUserId id, int in bool EOSFuncs::P2PConnectionInfo_t::isPeerStillValid(int index) const { - if ( !getPeerIdFromIndex(index) ) + if (!getPeerIdFromIndex(index)) { return false; } - if ( !Helpers_t::productIdIsValid(getPeerIdFromIndex(index)) ) + if (!Helpers_t::productIdIsValid(getPeerIdFromIndex(index))) { return false; } @@ -2628,10 +2638,10 @@ bool EOSFuncs::P2PConnectionInfo_t::isPeerStillValid(int index) const void EOSFuncs::P2PConnectionInfo_t::resetPeersAndServerData() { - EOS_P2P_CloseConnectionsOptions closeOptions = {}; + EOS_P2P_CloseConnectionsOptions closeOptions{}; closeOptions.ApiVersion = EOS_P2P_CLOSECONNECTIONS_API_LATEST; closeOptions.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle(); - EOS_P2P_SocketId SocketId = {}; + EOS_P2P_SocketId SocketId{}; SocketId.ApiVersion = EOS_P2P_SOCKETID_API_LATEST; strncpy(SocketId.SocketName, "CHAT", 5); closeOptions.SocketId = &SocketId; @@ -2649,16 +2659,16 @@ void EOSFuncs::LobbyData_t::SubscribeToLobbyUpdates() EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle); - EOS_Lobby_AddNotifyLobbyUpdateReceivedOptions UpdateNotifyOptions; + EOS_Lobby_AddNotifyLobbyUpdateReceivedOptions UpdateNotifyOptions{}; UpdateNotifyOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYLOBBYUPDATERECEIVED_API_LATEST; LobbyUpdateNotification = EOS_Lobby_AddNotifyLobbyUpdateReceived(LobbyHandle, &UpdateNotifyOptions, nullptr, OnLobbyUpdateReceived); - EOS_Lobby_AddNotifyLobbyMemberUpdateReceivedOptions MemberUpdateOptions; + EOS_Lobby_AddNotifyLobbyMemberUpdateReceivedOptions MemberUpdateOptions{}; MemberUpdateOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYLOBBYMEMBERUPDATERECEIVED_API_LATEST; LobbyMemberUpdateNotification = EOS_Lobby_AddNotifyLobbyMemberUpdateReceived(LobbyHandle, &MemberUpdateOptions, nullptr, OnMemberUpdateReceived); - EOS_Lobby_AddNotifyLobbyMemberStatusReceivedOptions MemberStatusOptions; + EOS_Lobby_AddNotifyLobbyMemberStatusReceivedOptions MemberStatusOptions{}; MemberStatusOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYLOBBYMEMBERSTATUSRECEIVED_API_LATEST; LobbyMemberStatusNotification = EOS_Lobby_AddNotifyLobbyMemberStatusReceived(LobbyHandle, &MemberStatusOptions, nullptr, OnMemberStatusReceived); @@ -2667,7 +2677,7 @@ void EOSFuncs::LobbyData_t::SubscribeToLobbyUpdates() void EOSFuncs::LobbyData_t::UnsubscribeFromLobbyUpdates() { - if ( LobbyUpdateNotification != EOS_INVALID_NOTIFICATIONID ) + if (LobbyUpdateNotification != EOS_INVALID_NOTIFICATIONID) { EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle); @@ -2675,7 +2685,7 @@ void EOSFuncs::LobbyData_t::UnsubscribeFromLobbyUpdates() LobbyUpdateNotification = EOS_INVALID_NOTIFICATIONID; } - if ( LobbyMemberUpdateNotification != EOS_INVALID_NOTIFICATIONID ) + if (LobbyMemberUpdateNotification != EOS_INVALID_NOTIFICATIONID) { EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle); @@ -2683,7 +2693,7 @@ void EOSFuncs::LobbyData_t::UnsubscribeFromLobbyUpdates() LobbyMemberUpdateNotification = EOS_INVALID_NOTIFICATIONID; } - if ( LobbyMemberStatusNotification != EOS_INVALID_NOTIFICATIONID ) + if (LobbyMemberStatusNotification != EOS_INVALID_NOTIFICATIONID) { EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle); @@ -2700,51 +2710,51 @@ void EOS_CALL EOSFuncs::OnAchievementQueryComplete(const EOS_Achievements_OnQuer EOS.Achievements.definitionsAwaitingCallback = false; - if ( data->ResultCode != EOS_EResult::EOS_Success ) + if (data->ResultCode != EOS_EResult::EOS_Success) { logError("OnAchievementQueryComplete: Failed to load achievements"); return; } - EOS_Achievements_GetAchievementDefinitionCountOptions AchievementDefinitionsCountOptions = {}; + EOS_Achievements_GetAchievementDefinitionCountOptions AchievementDefinitionsCountOptions{}; AchievementDefinitionsCountOptions.ApiVersion = EOS_ACHIEVEMENTS_GETACHIEVEMENTDEFINITIONCOUNT_API_LATEST; uint32_t AchievementDefinitionsCount = EOS_Achievements_GetAchievementDefinitionCount(EOS.AchievementsHandle, &AchievementDefinitionsCountOptions); - EOS_Achievements_CopyAchievementDefinitionV2ByIndexOptions CopyOptions = {}; + EOS_Achievements_CopyAchievementDefinitionV2ByIndexOptions CopyOptions{}; CopyOptions.ApiVersion = EOS_ACHIEVEMENTS_COPYDEFINITIONV2BYACHIEVEMENTID_API_LATEST; - for ( CopyOptions.AchievementIndex = 0; CopyOptions.AchievementIndex < AchievementDefinitionsCount; ++CopyOptions.AchievementIndex ) + for (CopyOptions.AchievementIndex = 0; CopyOptions.AchievementIndex < AchievementDefinitionsCount; ++CopyOptions.AchievementIndex) { EOS_Achievements_DefinitionV2* AchievementDef = NULL; EOS_EResult CopyAchievementDefinitionsResult = EOS_Achievements_CopyAchievementDefinitionV2ByIndex(EOS.AchievementsHandle, &CopyOptions, &AchievementDef); - if ( CopyAchievementDefinitionsResult != EOS_EResult::EOS_Success ) + if (CopyAchievementDefinitionsResult != EOS_EResult::EOS_Success) { logError("CopyAchievementDefinitions Failure!"); return; } - if ( AchievementDef->bIsHidden ) + if (AchievementDef->bIsHidden) { achievementHidden.emplace(std::string(AchievementDef->AchievementId)); } - if ( AchievementDef->UnlockedDisplayName ) + if (AchievementDef->UnlockedDisplayName) { achievementNames.emplace(std::make_pair( std::string(AchievementDef->AchievementId), std::string(AchievementDef->UnlockedDisplayName))); } - if ( AchievementDef->UnlockedDescription ) + if (AchievementDef->UnlockedDescription) { auto result = achievementDesc.emplace(std::make_pair( std::string(AchievementDef->AchievementId), std::string(AchievementDef->UnlockedDescription))); - if ( result.second == true ) // insertion success + if (result.second == true) // insertion success { - if ( result.first->second.back() != '.' ) // add punctuation if missing. + if (result.first->second.back() != '.') // add punctuation if missing. { result.first->second += '.'; } @@ -2767,36 +2777,36 @@ void EOSFuncs::OnPlayerAchievementQueryComplete(const EOS_Achievements_OnQueryPl EOS.Achievements.playerDataAwaitingCallback = false; - if ( data->ResultCode != EOS_EResult::EOS_Success ) + if (data->ResultCode != EOS_EResult::EOS_Success) { logError("OnPlayerAchievementQueryComplete: Failed to load player achievement status"); return; } - EOS_Achievements_GetPlayerAchievementCountOptions AchievementsCountOptions = {}; + EOS_Achievements_GetPlayerAchievementCountOptions AchievementsCountOptions{}; AchievementsCountOptions.ApiVersion = EOS_ACHIEVEMENTS_GETPLAYERACHIEVEMENTCOUNT_API_LATEST; AchievementsCountOptions.UserId = EOS.CurrentUserInfo.getProductUserIdHandle(); uint32_t AchievementsCount = EOS_Achievements_GetPlayerAchievementCount(EOS.AchievementsHandle, &AchievementsCountOptions); - EOS_Achievements_CopyPlayerAchievementByIndexOptions CopyOptions = {}; + EOS_Achievements_CopyPlayerAchievementByIndexOptions CopyOptions{}; CopyOptions.ApiVersion = EOS_ACHIEVEMENTS_COPYPLAYERACHIEVEMENTBYINDEX_API_LATEST; CopyOptions.TargetUserId = EOS.CurrentUserInfo.getProductUserIdHandle(); CopyOptions.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle(); - for ( CopyOptions.AchievementIndex = 0; CopyOptions.AchievementIndex < AchievementsCount; ++CopyOptions.AchievementIndex ) + for (CopyOptions.AchievementIndex = 0; CopyOptions.AchievementIndex < AchievementsCount; ++CopyOptions.AchievementIndex) { EOS_Achievements_PlayerAchievement* PlayerAchievement = NULL; EOS_EResult CopyPlayerAchievementResult = EOS_Achievements_CopyPlayerAchievementByIndex(EOS.AchievementsHandle, &CopyOptions, &PlayerAchievement); - if ( CopyPlayerAchievementResult == EOS_EResult::EOS_Success ) + if (CopyPlayerAchievementResult == EOS_EResult::EOS_Success) { - if ( PlayerAchievement->UnlockTime != EOS_ACHIEVEMENTS_ACHIEVEMENT_UNLOCKTIME_UNDEFINED ) + if (PlayerAchievement->UnlockTime != EOS_ACHIEVEMENTS_ACHIEVEMENT_UNLOCKTIME_UNDEFINED) { achievementUnlockedLookup.insert(std::string(PlayerAchievement->AchievementId)); auto find = achievementUnlockTime.find(PlayerAchievement->AchievementId); - if ( find == achievementUnlockTime.end() ) + if (find == achievementUnlockTime.end()) { achievementUnlockTime.emplace(std::make_pair(std::string(PlayerAchievement->AchievementId), PlayerAchievement->UnlockTime)); } @@ -2806,14 +2816,14 @@ void EOSFuncs::OnPlayerAchievementQueryComplete(const EOS_Achievements_OnQueryPl } } - if ( PlayerAchievement->StatInfoCount > 0 ) + if (PlayerAchievement->StatInfoCount > 0) { - for ( int statNum = 0; statNum < NUM_STEAM_STATISTICS; ++statNum ) + for (int statNum = 0; statNum < NUM_STEAM_STATISTICS; ++statNum) { - if ( steamStatAchStringsAndMaxVals[statNum].first.compare(PlayerAchievement->AchievementId) == 0 ) + if (steamStatAchStringsAndMaxVals[statNum].first.compare(PlayerAchievement->AchievementId) == 0) { auto find = achievementProgress.find(PlayerAchievement->AchievementId); - if ( find == achievementProgress.end() ) + if (find == achievementProgress.end()) { achievementProgress.emplace(std::make_pair(std::string(PlayerAchievement->AchievementId), statNum)); } @@ -2838,7 +2848,7 @@ void EOSFuncs::OnPlayerAchievementQueryComplete(const EOS_Achievements_OnQueryPl void EOSFuncs::OnIngestStatComplete(const EOS_Stats_IngestStatCompleteCallbackInfo* data) { assert(data != NULL); - if ( data->ResultCode == EOS_EResult::EOS_Success ) + if (data->ResultCode == EOS_EResult::EOS_Success) { logInfo("Stats updated"); } @@ -2851,15 +2861,15 @@ void EOSFuncs::OnIngestStatComplete(const EOS_Stats_IngestStatCompleteCallbackIn bool EOSFuncs::initAchievements() { logInfo("Initializing EOS achievements"); - if ( !PlatformHandle ) + if (!PlatformHandle) { return false; } - if ( (AchievementsHandle = EOS_Platform_GetAchievementsInterface(PlatformHandle)) == nullptr ) + if ((AchievementsHandle = EOS_Platform_GetAchievementsInterface(PlatformHandle)) == nullptr) { return false; } - if ( (StatsHandle = EOS_Platform_GetStatsInterface(PlatformHandle)) == nullptr ) + if ((StatsHandle = EOS_Platform_GetStatsInterface(PlatformHandle)) == nullptr) { return false; } @@ -2871,7 +2881,7 @@ void EOS_CALL EOSFuncs::OnUnlockAchievement(const EOS_Achievements_OnUnlockAchie assert(data != NULL); //int64_t t = getTime(); //achievementUnlockTime.emplace(std::make_pair(std::string((const char*)data->ClientData), t)); - if ( data->ResultCode == EOS_EResult::EOS_Success ) + if (data->ResultCode == EOS_EResult::EOS_Success) { logInfo("EOS achievement successfully unlocked"); return; @@ -2885,11 +2895,11 @@ void EOS_CALL EOSFuncs::OnUnlockAchievement(const EOS_Achievements_OnUnlockAchie void EOSFuncs::loadAchievementData() { - if ( Achievements.definitionsAwaitingCallback || Achievements.playerDataAwaitingCallback ) + if (Achievements.definitionsAwaitingCallback || Achievements.playerDataAwaitingCallback) { return; } - if ( !CurrentUserInfo.isValid() || !CurrentUserInfo.isLoggedIn() ) + if (!CurrentUserInfo.isValid() || !CurrentUserInfo.isLoggedIn()) { return; } @@ -2898,13 +2908,13 @@ void EOSFuncs::loadAchievementData() logInfo("Loading EOS achievements"); - if ( !Achievements.definitionsLoaded ) + if (!Achievements.definitionsLoaded) { Achievements.definitionsAwaitingCallback = true; achievementNames.clear(); achievementDesc.clear(); achievementHidden.clear(); - EOS_Achievements_QueryDefinitionsOptions Options = {}; + EOS_Achievements_QueryDefinitionsOptions Options{}; Options.ApiVersion = EOS_ACHIEVEMENTS_QUERYDEFINITIONS_API_LATEST; //Options.EpicUserId = EOSFuncs::Helpers_t::epicIdFromString(EOS.CurrentUserInfo.epicAccountId.c_str()); Options.LocalUserId = CurrentUserInfo.getProductUserIdHandle(); @@ -2918,7 +2928,7 @@ void EOSFuncs::loadAchievementData() Achievements.playerDataAwaitingCallback = true; //achievementProgress.clear(); //achievementUnlockTime.clear(); - EOS_Achievements_QueryPlayerAchievementsOptions PlayerAchievementOptions = {}; + EOS_Achievements_QueryPlayerAchievementsOptions PlayerAchievementOptions{}; PlayerAchievementOptions.ApiVersion = EOS_ACHIEVEMENTS_QUERYPLAYERACHIEVEMENTS_API_LATEST; PlayerAchievementOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle(); PlayerAchievementOptions.TargetUserId = CurrentUserInfo.getProductUserIdHandle(); @@ -2930,7 +2940,7 @@ void EOSFuncs::loadAchievementData() void EOSFuncs::unlockAchievement(const char* name) { //logInfo("unlocking EOS achievement '%s'", name); - EOS_Achievements_UnlockAchievementsOptions UnlockAchievementsOptions = {}; + EOS_Achievements_UnlockAchievementsOptions UnlockAchievementsOptions{}; UnlockAchievementsOptions.ApiVersion = EOS_ACHIEVEMENTS_UNLOCKACHIEVEMENTS_API_LATEST; UnlockAchievementsOptions.UserId = CurrentUserInfo.getProductUserIdHandle(); UnlockAchievementsOptions.AchievementsCount = 1; @@ -2950,7 +2960,7 @@ void EOSFuncs::ingestStat(int stat_num, int value) StatsToIngest[0].StatName = g_SteamStats[stat_num].m_pchStatName; StatsToIngest[0].IngestAmount = value; - EOS_Stats_IngestStatOptions Options = {}; + EOS_Stats_IngestStatOptions Options{}; Options.ApiVersion = EOS_STATS_INGESTSTAT_API_LATEST; Options.Stats = StatsToIngest; Options.StatsCount = sizeof(StatsToIngest) / sizeof(StatsToIngest[0]); @@ -2962,16 +2972,16 @@ void EOSFuncs::ingestStat(int stat_num, int value) static void EOS_CALL OnIngestGlobalStatComplete(const EOS_Stats_IngestStatCompleteCallbackInfo* data) { assert(data != NULL); - if ( data->ResultCode == EOS_EResult::EOS_Success ) + if (data->ResultCode == EOS_EResult::EOS_Success) { EOSFuncs::logInfo("Successfully stored global stats"); - for ( Uint32 i = 0; i < NUM_GLOBAL_STEAM_STATISTICS; ++i ) + for (Uint32 i = 0; i < NUM_GLOBAL_STEAM_STATISTICS; ++i) { g_SteamGlobalStats[i].m_iValue = 0; } return; } - else if ( data->ResultCode == EOS_EResult::EOS_TooManyRequests ) + else if (data->ResultCode == EOS_EResult::EOS_TooManyRequests) { return; } @@ -2984,11 +2994,11 @@ static void EOS_CALL OnIngestGlobalStatComplete(const EOS_Stats_IngestStatComple void EOSFuncs::queueGlobalStatUpdate(int stat_num, int value) { - if ( stat_num <= STEAM_GSTAT_INVALID || stat_num >= NUM_GLOBAL_STEAM_STATISTICS ) + if (stat_num <= STEAM_GSTAT_INVALID || stat_num >= NUM_GLOBAL_STEAM_STATISTICS) { return; } - if ( StatGlobalManager.bIsDisabled ) + if (StatGlobalManager.bIsDisabled) { return; } @@ -2998,11 +3008,11 @@ void EOSFuncs::queueGlobalStatUpdate(int stat_num, int value) void EOSFuncs::StatGlobal_t::updateQueuedStats() { - if ( !bDataQueued || bIsDisabled ) + if (!bDataQueued || bIsDisabled) { return; } - if ( (ticks - lastUpdateTicks) < TICKS_PER_SECOND * 30 ) + if ((ticks - lastUpdateTicks) < TICKS_PER_SECOND * 30) { return; } @@ -3013,32 +3023,32 @@ void EOSFuncs::StatGlobal_t::updateQueuedStats() void EOSFuncs::ingestGlobalStats() { - if ( StatGlobalManager.bIsDisabled ) + if (StatGlobalManager.bIsDisabled) { return; } Uint32 numStats = 0; std::vector StatNames; - for ( Uint32 i = 0; i < NUM_GLOBAL_STEAM_STATISTICS; ++i ) + for (Uint32 i = 0; i < NUM_GLOBAL_STEAM_STATISTICS; ++i) { - if ( g_SteamGlobalStats[i].m_iValue > 0 ) + if (g_SteamGlobalStats[i].m_iValue > 0) { StatNames.push_back(g_SteamGlobalStats[i].m_pchStatName); ++numStats; } } - if ( numStats == 0 ) + if (numStats == 0) { return; } EOS_Stats_IngestData* StatsToIngest = new EOS_Stats_IngestData[numStats]; Uint32 currentIndex = 0; - for ( Uint32 i = 0; i < NUM_GLOBAL_STEAM_STATISTICS && currentIndex < StatNames.size(); ++i ) + for (Uint32 i = 0; i < NUM_GLOBAL_STEAM_STATISTICS && currentIndex < StatNames.size(); ++i) { - if ( g_SteamGlobalStats[i].m_iValue > 0 ) + if (g_SteamGlobalStats[i].m_iValue > 0) { StatsToIngest[currentIndex].ApiVersion = EOS_STATS_INGESTDATA_API_LATEST; StatsToIngest[currentIndex].StatName = StatNames[currentIndex].c_str(); @@ -3048,7 +3058,7 @@ void EOSFuncs::ingestGlobalStats() } } - EOS_Stats_IngestStatOptions Options = {}; + EOS_Stats_IngestStatOptions Options{}; Options.ApiVersion = EOS_STATS_INGESTSTAT_API_LATEST; Options.Stats = StatsToIngest; Options.StatsCount = numStats; @@ -3062,16 +3072,16 @@ void EOSFuncs::ingestGlobalStats() void EOS_CALL EOSFuncs::OnQueryAllStatsCallback(const EOS_Stats_OnQueryStatsCompleteCallbackInfo* data) { - if ( !data ) + if (!data) { EOSFuncs::logError("OnQueryAllStatsCallback: null data"); return; } - else if ( data->ResultCode == EOS_EResult::EOS_Success ) + else if (data->ResultCode == EOS_EResult::EOS_Success) { EOS.StatsHandle = EOS_Platform_GetStatsInterface(EOS.PlatformHandle); - EOS_Stats_GetStatCountOptions StatCountOptions = {}; + EOS_Stats_GetStatCountOptions StatCountOptions{}; StatCountOptions.ApiVersion = EOS_STATS_GETSTATCOUNT_API_LATEST; StatCountOptions.TargetUserId = EOS.CurrentUserInfo.getProductUserIdHandle(); @@ -3079,24 +3089,24 @@ void EOS_CALL EOSFuncs::OnQueryAllStatsCallback(const EOS_Stats_OnQueryStatsComp EOSFuncs::logInfo("OnQueryAllStatsCallback: read %d stats", numStats); - EOS_Stats_CopyStatByIndexOptions CopyByIndexOptions = {}; + EOS_Stats_CopyStatByIndexOptions CopyByIndexOptions{}; CopyByIndexOptions.ApiVersion = EOS_STATS_COPYSTATBYINDEX_API_LATEST; CopyByIndexOptions.TargetUserId = EOS.CurrentUserInfo.getProductUserIdHandle(); EOS_Stats_Stat* copyStat = NULL; - for ( Uint32 i = 0; i < NUM_STEAM_STATISTICS; ++i ) + for (Uint32 i = 0; i < NUM_STEAM_STATISTICS; ++i) { g_SteamStats[i].m_iValue = 0; } - for ( CopyByIndexOptions.StatIndex = 0; CopyByIndexOptions.StatIndex < numStats; ++CopyByIndexOptions.StatIndex ) + for (CopyByIndexOptions.StatIndex = 0; CopyByIndexOptions.StatIndex < numStats; ++CopyByIndexOptions.StatIndex) { EOS_EResult result = EOS_Stats_CopyStatByIndex(EOS.StatsHandle, &CopyByIndexOptions, ©Stat); - if ( result == EOS_EResult::EOS_Success && copyStat ) + if (result == EOS_EResult::EOS_Success && copyStat) { SteamStat_t* statLookup = nullptr; - if ( statLookup = EOS.getStatStructFromString(std::string(copyStat->Name)) ) + if (statLookup = EOS.getStatStructFromString(std::string(copyStat->Name))) { statLookup->m_iValue = copyStat->Value; } @@ -3113,14 +3123,14 @@ void EOS_CALL EOSFuncs::OnQueryAllStatsCallback(const EOS_Stats_OnQueryStatsComp SteamStat_t* EOSFuncs::getStatStructFromString(const std::string& str) { - if ( !statMappings.size() ) + if (!statMappings.size()) { EOSFuncs::logError("getStatStructFromString: Empty stat mappings"); return nullptr; } auto find = statMappings.find(str); - if ( find != statMappings.end() ) + if (find != statMappings.end()) { return (*find).second; } @@ -3129,9 +3139,9 @@ SteamStat_t* EOSFuncs::getStatStructFromString(const std::string& str) void EOSFuncs::queryAllStats() { - if ( !statMappings.size() ) + if (!statMappings.size()) { - for ( Uint32 i = 0; i < NUM_STEAM_STATISTICS; ++i ) + for (Uint32 i = 0; i < NUM_STEAM_STATISTICS; ++i) { statMappings[g_SteamStats[i].m_pchStatName] = &g_SteamStats[i]; } @@ -3140,7 +3150,7 @@ void EOSFuncs::queryAllStats() StatsHandle = EOS_Platform_GetStatsInterface(PlatformHandle); // Query Player Stats - EOS_Stats_QueryStatsOptions StatsQueryOptions = {}; + EOS_Stats_QueryStatsOptions StatsQueryOptions{}; StatsQueryOptions.ApiVersion = EOS_STATS_QUERYSTATS_API_LATEST; StatsQueryOptions.TargetUserId = CurrentUserInfo.getProductUserIdHandle(); StatsQueryOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle(); @@ -3150,9 +3160,9 @@ void EOSFuncs::queryAllStats() StatsQueryOptions.EndTime = EOS_STATS_TIME_UNDEFINED; StatsQueryOptions.StatNamesCount = NUM_STEAM_STATISTICS; - StatsQueryOptions.StatNames = new const char*[NUM_STEAM_STATISTICS]; - - for ( int i = 0; i < NUM_STEAM_STATISTICS; ++i ) + StatsQueryOptions.StatNames = new const char* [NUM_STEAM_STATISTICS]; + + for (int i = 0; i < NUM_STEAM_STATISTICS; ++i) { StatsQueryOptions.StatNames[i] = g_SteamStats[i].m_pchStatName; } @@ -3164,7 +3174,7 @@ void EOSFuncs::queryAllStats() void EOSFuncs::showFriendsOverlay() { UIHandle = EOS_Platform_GetUIInterface(PlatformHandle); - EOS_UI_SetDisplayPreferenceOptions DisplayOptions; + EOS_UI_SetDisplayPreferenceOptions DisplayOptions{}; DisplayOptions.ApiVersion = EOS_UI_SETDISPLAYPREFERENCE_API_LATEST; DisplayOptions.NotificationLocation = EOS_UI_ENotificationLocation::EOS_UNL_TopRight; @@ -3172,7 +3182,7 @@ void EOSFuncs::showFriendsOverlay() EOSFuncs::logInfo("showFriendsOverlay: result: %d", static_cast(result)); UIHandle = EOS_Platform_GetUIInterface(PlatformHandle); - EOS_UI_ShowFriendsOptions Options = {}; + EOS_UI_ShowFriendsOptions Options{}; Options.ApiVersion = EOS_UI_SHOWFRIENDS_API_LATEST; Options.LocalUserId = EOSFuncs::Helpers_t::epicIdFromString(CurrentUserInfo.epicAccountId.c_str()); @@ -3181,7 +3191,7 @@ void EOSFuncs::showFriendsOverlay() void EOS_CALL EOSFuncs::ShowFriendsCallback(const EOS_UI_ShowFriendsCallbackInfo* data) { - if ( data ) + if (data) { EOSFuncs::logInfo("ShowFriendsCallback: result: %d", static_cast(data->ResultCode)); } @@ -3194,7 +3204,7 @@ bool EOSFuncs::initAuth(std::string hostname, std::string tokenName) EOS_Auth_Credentials Credentials = {}; Credentials.ApiVersion = EOS_AUTH_CREDENTIALS_API_LATEST; - if ( hostname.compare("") == 0 ) + if (hostname.compare("") == 0) { Credentials.Id = nullptr; } @@ -3202,7 +3212,7 @@ bool EOSFuncs::initAuth(std::string hostname, std::string tokenName) { Credentials.Id = hostname.c_str(); } - if ( tokenName.compare("") == 0 ) + if (tokenName.compare("") == 0) { #ifdef NINTENDO static char token[4096]; @@ -3232,7 +3242,7 @@ bool EOSFuncs::initAuth(std::string hostname, std::string tokenName) break; } - EOS_Auth_LoginOptions LoginOptions = {}; + EOS_Auth_LoginOptions LoginOptions{}; LoginOptions.ApiVersion = EOS_AUTH_LOGIN_API_LATEST; LoginOptions.ScopeFlags = static_cast(EOS_EAuthScopeFlags::EOS_AS_BasicProfile | EOS_EAuthScopeFlags::EOS_AS_FriendsList | EOS_EAuthScopeFlags::EOS_AS_Presence); LoginOptions.Credentials = &Credentials; @@ -3245,11 +3255,11 @@ bool EOSFuncs::initAuth(std::string hostname, std::string tokenName) Uint32 startAuthTicks = SDL_GetTicks(); Uint32 currentAuthTicks = startAuthTicks; #if !defined(STEAMWORKS) - while ( EOS.AccountManager.AccountAuthenticationStatus == EOS_EResult::EOS_NotConfigured ) + while (EOS.AccountManager.AccountAuthenticationStatus == EOS_EResult::EOS_NotConfigured) { #if defined(APPLE) SDL_Event event; - while ( SDL_PollEvent(&event) != 0 ) + while (SDL_PollEvent(&event) != 0) { //Makes Mac work because Apple had to do it different. } @@ -3257,7 +3267,7 @@ bool EOSFuncs::initAuth(std::string hostname, std::string tokenName) EOS_Platform_Tick(PlatformHandle); SDL_Delay(1); currentAuthTicks = SDL_GetTicks(); - if ( currentAuthTicks - startAuthTicks >= 3000 ) // spin the wheels for 3 seconds + if (currentAuthTicks - startAuthTicks >= 3000) // spin the wheels for 3 seconds { break; } @@ -3272,21 +3282,21 @@ bool EOSFuncs::initAuth(std::string hostname, std::string tokenName) void EOSFuncs::LobbySearchResults_t::sortResults() { resultsSortedForDisplay.clear(); - if ( results.empty() ) + if (results.empty()) { return; } int index = 0; - for ( auto it = results.begin(); it != results.end(); ++it ) + for (auto it = results.begin(); it != results.end(); ++it) { resultsSortedForDisplay.push_back(std::make_pair(EOSFuncs::LobbyData_t::LobbyAttributes_t((*it).LobbyAttributes), index)); ++index; } - std::sort(resultsSortedForDisplay.begin(), resultsSortedForDisplay.end(), + std::sort(resultsSortedForDisplay.begin(), resultsSortedForDisplay.end(), [](const std::pair& lhs, const std::pair& rhs) { - if ( lhs.first.gameCurrentLevel == -1 && rhs.first.gameCurrentLevel == -1 ) + if (lhs.first.gameCurrentLevel == -1 && rhs.first.gameCurrentLevel == -1) { return (lhs.first.lobbyCreationTime > rhs.first.lobbyCreationTime); } @@ -3302,21 +3312,21 @@ void EOSFuncs::Accounts_t::handleLogin() return; #endif - if ( !initPopupWindow && popupType == POPUP_TOAST ) + if (!initPopupWindow && popupType == POPUP_TOAST) { UIToastNotificationManager.createEpicLoginNotification(); initPopupWindow = true; } - if ( !waitingForCallback && (AccountAuthenticationStatus == EOS_EResult::EOS_Success || AccountAuthenticationCompleted == EOS_EResult::EOS_Success) ) + if (!waitingForCallback && (AccountAuthenticationStatus == EOS_EResult::EOS_Success || AccountAuthenticationCompleted == EOS_EResult::EOS_Success)) { firstTimeSetupCompleted = true; - if ( AccountAuthenticationCompleted != EOS_EResult::EOS_Success ) + if (AccountAuthenticationCompleted != EOS_EResult::EOS_Success) { - if ( popupType == POPUP_TOAST ) + if (popupType == POPUP_TOAST) { UIToastNotification* n = UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_EOS_ACCOUNT); - if ( n ) + if (n) { n->showMainCard(); n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE); @@ -3332,15 +3342,15 @@ void EOSFuncs::Accounts_t::handleLogin() AccountAuthenticationCompleted = EOS_EResult::EOS_NotConfigured; // handle errors below... - if ( initPopupWindow && popupType == POPUP_TOAST ) + if (initPopupWindow && popupType == POPUP_TOAST) { - if ( !waitingForCallback ) + if (!waitingForCallback) { - if ( AccountAuthenticationStatus != EOS_EResult::EOS_NotConfigured ) + if (AccountAuthenticationStatus != EOS_EResult::EOS_NotConfigured) { UIToastNotification* n = UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_EOS_ACCOUNT); // update the status here. - if ( n ) + if (n) { n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_ACTION_BUTTON); n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE); @@ -3376,11 +3386,11 @@ static void nxTokenRequest() Credentials.Token = token; Credentials.Type = EOS_EExternalCredentialType::EOS_ECT_NINTENDO_NSA_ID_TOKEN; // change this to steam etc for different account providers. - EOS_Connect_UserLoginInfo Info; + EOS_Connect_UserLoginInfo Info{}; Info.ApiVersion = EOS_CONNECT_USERLOGININFO_API_LATEST; Info.DisplayName = MainMenu::getUsername(); - EOS_Connect_LoginOptions Options; + EOS_Connect_LoginOptions Options{}; Options.ApiVersion = EOS_CONNECT_LOGIN_API_LATEST; Options.Credentials = &Credentials; Options.UserLoginInfo = &Info; @@ -3402,20 +3412,20 @@ void EOSFuncs::CrossplayAccounts_t::handleLogin() return; } - if ( logOut ) + if (logOut) { - if ( awaitingAppTicketResponse || awaitingConnectCallback || awaitingCreateUserCallback ) + if (awaitingAppTicketResponse || awaitingConnectCallback || awaitingCreateUserCallback) { EOSFuncs::logInfo("Crossplay logout pending callbacks..."); - if ( awaitingAppTicketResponse ) + if (awaitingAppTicketResponse) { EOSFuncs::logInfo("Callback AppTicket still pending..."); } - if ( awaitingConnectCallback ) + if (awaitingConnectCallback) { EOSFuncs::logInfo("Callback Connect still pending..."); } - if ( awaitingCreateUserCallback ) + if (awaitingCreateUserCallback) { EOSFuncs::logInfo("Callback CreateUser still pending..."); } @@ -3427,9 +3437,9 @@ void EOSFuncs::CrossplayAccounts_t::handleLogin() EOS.CurrentUserInfo.bUserLoggedIn = false; resetOnFailure(); - for ( auto it = UIToastNotificationManager.allNotifications.begin(); it != UIToastNotificationManager.allNotifications.end(); ) + for (auto it = UIToastNotificationManager.allNotifications.begin(); it != UIToastNotificationManager.allNotifications.end(); ) { - if ( (*it).cardType == UIToastNotification::CardType::UI_CARD_CROSSPLAY_ACCOUNT ) + if ((*it).cardType == UIToastNotification::CardType::UI_CARD_CROSSPLAY_ACCOUNT) { it = UIToastNotificationManager.allNotifications.erase(it); continue; @@ -3442,22 +3452,22 @@ void EOSFuncs::CrossplayAccounts_t::handleLogin() } bool initLogin = false; - if ( autologin ) + if (autologin) { initLogin = true; autologin = false; } - if ( trySetupFromSettingsMenu ) + if (trySetupFromSettingsMenu) { initLogin = true; trySetupFromSettingsMenu = false; - if ( subwindow ) + if (subwindow) { buttonCloseSubwindow(nullptr); } } - if ( initLogin ) + if (initLogin) { #ifdef STEAMWORKS cpp_SteamMatchmaking_RequestAppTicket(); @@ -3468,31 +3478,32 @@ void EOSFuncs::CrossplayAccounts_t::handleLogin() if (nxConnectedToNetwork()) { awaitingAppTicketResponse = true; nxTokenRequest(); - } else { + } + else { printlog("[NX] not connected to network, can't login to EOS"); } #endif // STEAMWORKS return; } - if ( awaitingAppTicketResponse ) + if (awaitingAppTicketResponse) { return; } - if ( awaitingConnectCallback ) + if (awaitingConnectCallback) { return; } - if ( connectLoginStatus == EOS_EResult::EOS_Success ) + if (connectLoginStatus == EOS_EResult::EOS_Success) { - if ( connectLoginCompleted != EOS_EResult::EOS_Success ) + if (connectLoginCompleted != EOS_EResult::EOS_Success) { connectLoginCompleted = EOS_EResult::EOS_Success; LobbyHandler.crossplayEnabled = true; #ifndef NINTENDO UIToastNotification* n = UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_CROSSPLAY_ACCOUNT); - if ( n ) + if (n) { n->actionFlags &= ~(UIToastNotification::ActionFlags::UI_NOTIFICATION_ACTION_BUTTON); n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE); @@ -3511,18 +3522,18 @@ void EOSFuncs::CrossplayAccounts_t::handleLogin() return; } - if ( connectLoginCompleted == EOS_EResult::EOS_Success ) + if (connectLoginCompleted == EOS_EResult::EOS_Success) { return; } - if ( connectLoginStatus != EOS_EResult::EOS_NotConfigured ) + if (connectLoginStatus != EOS_EResult::EOS_NotConfigured) { - if ( connectLoginStatus == EOS_EResult::EOS_InvalidUser ) + if (connectLoginStatus == EOS_EResult::EOS_InvalidUser) { #ifndef NINTENDO UIToastNotification* n = UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_CROSSPLAY_ACCOUNT); - if ( n ) + if (n) { n->actionFlags &= ~(UIToastNotification::ActionFlags::UI_NOTIFICATION_ACTION_BUTTON); n->showMainCard(); @@ -3539,7 +3550,7 @@ void EOSFuncs::CrossplayAccounts_t::handleLogin() #ifndef NINTENDO UIToastNotification* n = UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_CROSSPLAY_ACCOUNT); // update the status here. - if ( n ) + if (n) { n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE); char buf[128] = ""; @@ -3578,9 +3589,9 @@ void EOSFuncs::CrossplayAccounts_t::acceptCrossplay() { promptActive = false; acceptedEula = true; - if ( continuanceToken ) + if (continuanceToken) { - EOS_Connect_CreateUserOptions CreateUserOptions; + EOS_Connect_CreateUserOptions CreateUserOptions{}; CreateUserOptions.ApiVersion = EOS_CONNECT_CREATEUSER_API_LATEST; CreateUserOptions.ContinuanceToken = continuanceToken; @@ -3614,7 +3625,7 @@ void EOSFuncs::CrossplayAccounts_t::viewPrivacyPolicy() void EOSFuncs::CrossplayAccounts_t::createDialogue() { promptActive = true; - MainMenu::crossplayPrompt(); + MainMenu::crossplayPrompt(); } bool EOSFuncs::CrossplayAccounts_t::isLoggingIn() @@ -3631,12 +3642,12 @@ std::string EOSFuncs::getLobbyCodeFromGameKey(Uint32 key) { const char allChars[37] = "0123456789abcdefghijklmnppqrstuvwxyz"; std::string code = ""; - while ( key != 0 ) + while (key != 0) { code += (allChars[key % 36]); key /= 36; } - while ( code.size() < 4 ) + while (code.size() < 4) { code += '0'; } @@ -3647,19 +3658,19 @@ Uint32 EOSFuncs::getGameKeyFromLobbyCode(std::string& code) const char allChars[37] = "0123456789abcdefghijklmnppqrstuvwxyz"; Uint32 result = 0; Uint32 bit = 0; - for ( int i = 0; i < code.size(); ++i ) + for (int i = 0; i < code.size(); ++i) { - if ( code[i] >= 'A' && code[i] <= 'Z' ) + if (code[i] >= 'A' && code[i] <= 'Z') { code[i] = 'a' + (code[i] - 'A'); } - if ( code[i] >= '0' && code[i] <= '9' ) + if (code[i] >= '0' && code[i] <= '9') { result += static_cast(code[i] - '0') * static_cast(pow(36, bit)); ++bit; } - else if ( code[i] >= 'a' && code[i] <= 'z' ) + else if (code[i] >= 'a' && code[i] <= 'z') { result += (static_cast(code[i] - 'a') + 10) * static_cast(pow(36, bit)); ++bit; @@ -3672,13 +3683,13 @@ void EOSFuncs::queryDLCOwnership() { EcomHandle = EOS_Platform_GetEcomInterface(PlatformHandle); - EOS_Ecom_QueryEntitlementsOptions options = {}; + EOS_Ecom_QueryEntitlementsOptions options{}; options.ApiVersion = EOS_ECOM_QUERYENTITLEMENTS_API_LATEST; options.bIncludeRedeemed = true; std::vector entitlements; entitlements.push_back("fced51d547714291869b8847fdd770e8"); entitlements.push_back("7ea3754f8bfa4069938fd0bee3e7197b"); - + options.EntitlementNames = entitlements.data(); options.EntitlementNameCount = entitlements.size(); options.LocalUserId = EOSFuncs::Helpers_t::epicIdFromString(CurrentUserInfo.epicAccountId.c_str()); @@ -3688,25 +3699,25 @@ void EOSFuncs::queryDLCOwnership() void EOS_CALL EOSFuncs::OnEcomQueryEntitlementsCallback(const EOS_Ecom_QueryEntitlementsCallbackInfo* data) { - if ( !data ) + if (!data) { EOSFuncs::logError("OnEcomQueryEntitlementsCallback: null data"); return; } - else if ( data->ResultCode == EOS_EResult::EOS_Success ) + else if (data->ResultCode == EOS_EResult::EOS_Success) { EOSFuncs::logInfo("OnEcomQueryEntitlementsCallback: callback success"); EOS.EcomHandle = EOS_Platform_GetEcomInterface(EOS.PlatformHandle); - EOS_Ecom_GetEntitlementsCountOptions countOptions = {}; + EOS_Ecom_GetEntitlementsCountOptions countOptions{}; countOptions.ApiVersion = EOS_ECOM_GETENTITLEMENTSCOUNT_API_LATEST; countOptions.LocalUserId = EOSFuncs::Helpers_t::epicIdFromString(EOS.CurrentUserInfo.epicAccountId.c_str()); Uint32 numEntitlements = EOS_Ecom_GetEntitlementsCount(EOS.EcomHandle, &countOptions); //EOSFuncs::logInfo("OnEcomQueryEntitlementsCallback: %d entitlements", numEntitlements); - for ( int i = 0; i < numEntitlements; ++i ) + for (int i = 0; i < numEntitlements; ++i) { - EOS_Ecom_CopyEntitlementByIndexOptions copyOptions; + EOS_Ecom_CopyEntitlementByIndexOptions copyOptions{}; copyOptions.ApiVersion = EOS_ECOM_COPYENTITLEMENTBYINDEX_API_LATEST; copyOptions.EntitlementIndex = i; copyOptions.LocalUserId = EOSFuncs::Helpers_t::epicIdFromString(EOS.CurrentUserInfo.epicAccountId.c_str()); @@ -3714,15 +3725,15 @@ void EOS_CALL EOSFuncs::OnEcomQueryEntitlementsCallback(const EOS_Ecom_QueryEnti EOS_Ecom_Entitlement* e = nullptr; EOS_EResult result = EOS_Ecom_CopyEntitlementByIndex(EOS.EcomHandle, ©Options, &e); //EOSFuncs::logInfo("%d:", static_cast(result)); - if ( (result == EOS_EResult::EOS_Success || result == EOS_EResult::EOS_Ecom_EntitlementStale) && e ) + if ((result == EOS_EResult::EOS_Success || result == EOS_EResult::EOS_Ecom_EntitlementStale) && e) { std::string id = e->EntitlementName; - if ( id.compare("fced51d547714291869b8847fdd770e8") == 0 ) + if (id.compare("fced51d547714291869b8847fdd770e8") == 0) { enabledDLCPack1 = true; EOSFuncs::logInfo("Myths & Outcasts DLC Enabled"); } - else if ( id.compare("7ea3754f8bfa4069938fd0bee3e7197b") == 0 ) + else if (id.compare("7ea3754f8bfa4069938fd0bee3e7197b") == 0) { enabledDLCPack2 = true; EOSFuncs::logInfo("Legends & Pariahs DLC Enabled"); @@ -3741,14 +3752,14 @@ void EOS_CALL EOSFuncs::OnEcomQueryEntitlementsCallback(const EOS_Ecom_QueryEnti void EOS_CALL EOSFuncs::OnEcomQueryOwnershipCallback(const EOS_Ecom_QueryOwnershipCallbackInfo* data) { - if ( !data ) + if (!data) { EOSFuncs::logError("OnEcomQueryOwnershipCallback: null data"); return; } - else if ( data->ResultCode == EOS_EResult::EOS_Success ) + else if (data->ResultCode == EOS_EResult::EOS_Success) { - for ( int i = 0; i < data->ItemOwnershipCount; ++i ) + for (int i = 0; i < data->ItemOwnershipCount; ++i) { EOSFuncs::logInfo("OnEcomQueryOwnershipCallback: Ownership status: %d, %d", static_cast(data->ItemOwnership[i].OwnershipStatus), data->ItemOwnershipCount); } @@ -3761,45 +3772,45 @@ void EOS_CALL EOSFuncs::OnEcomQueryOwnershipCallback(const EOS_Ecom_QueryOwnersh static void EOS_CALL OnQueryGlobalStatsCallback(const EOS_Stats_OnQueryStatsCompleteCallbackInfo* data) { - if ( !data ) + if (!data) { EOSFuncs::logError("OnQueryGlobalStatsCallback: null data"); return; } - else if ( data->ResultCode == EOS_EResult::EOS_Success ) + else if (data->ResultCode == EOS_EResult::EOS_Success) { - EOS_Stats_GetStatCountOptions StatCountOptions = {}; + EOS_Stats_GetStatCountOptions StatCountOptions{}; StatCountOptions.ApiVersion = EOS_STATS_GETSTATCOUNT_API_LATEST; StatCountOptions.TargetUserId = EOS.StatGlobalManager.getProductUserIdHandle(); - Uint32 numStats = EOS_Stats_GetStatsCount(EOS_Platform_GetStatsInterface(EOS.ServerPlatformHandle), + Uint32 numStats = EOS_Stats_GetStatsCount(EOS_Platform_GetStatsInterface(EOS.ServerPlatformHandle), &StatCountOptions); EOSFuncs::logInfo("OnQueryGlobalStatsCallback: read %d stats", numStats); - EOS_Stats_CopyStatByIndexOptions CopyByIndexOptions = {}; + EOS_Stats_CopyStatByIndexOptions CopyByIndexOptions{}; CopyByIndexOptions.ApiVersion = EOS_STATS_COPYSTATBYINDEX_API_LATEST; CopyByIndexOptions.TargetUserId = EOS.StatGlobalManager.getProductUserIdHandle(); EOS_Stats_Stat* copyStat = NULL; - for ( CopyByIndexOptions.StatIndex = 0; CopyByIndexOptions.StatIndex < numStats; ++CopyByIndexOptions.StatIndex ) + for (CopyByIndexOptions.StatIndex = 0; CopyByIndexOptions.StatIndex < numStats; ++CopyByIndexOptions.StatIndex) { - EOS_EResult result = EOS_Stats_CopyStatByIndex(EOS_Platform_GetStatsInterface(EOS.ServerPlatformHandle), + EOS_EResult result = EOS_Stats_CopyStatByIndex(EOS_Platform_GetStatsInterface(EOS.ServerPlatformHandle), &CopyByIndexOptions, ©Stat); - if ( result == EOS_EResult::EOS_Success && copyStat ) + if (result == EOS_EResult::EOS_Success && copyStat) { - if ( !strcmp(copyStat->Name, "STAT_GLOBAL_DISABLE") ) + if (!strcmp(copyStat->Name, "STAT_GLOBAL_DISABLE")) { - if ( copyStat->Value == 1 ) + if (copyStat->Value == 1) { //EOSFuncs::logInfo("OnQueryGlobalStatsCallback: disabled"); EOS.StatGlobalManager.bIsDisabled = true; } } - else if ( !strcmp(copyStat->Name, "STAT_GLOBAL_PROMO") ) + else if (!strcmp(copyStat->Name, "STAT_GLOBAL_PROMO")) { - if ( copyStat->Value == 1 ) + if (copyStat->Value == 1) { EOS.StatGlobalManager.bPromoEnabled = true; //EOSFuncs::logInfo("OnQueryGlobalStatsCallback: received"); @@ -3829,7 +3840,7 @@ void EOSFuncs::StatGlobal_t::queryGlobalStatUser() init(); // Query Player Stats - EOS_Stats_QueryStatsOptions StatsQueryOptions = {}; + EOS_Stats_QueryStatsOptions StatsQueryOptions{}; StatsQueryOptions.ApiVersion = EOS_STATS_QUERYSTATS_API_LATEST; StatsQueryOptions.LocalUserId = getProductUserIdHandle(); StatsQueryOptions.TargetUserId = getProductUserIdHandle(); @@ -3839,14 +3850,14 @@ void EOSFuncs::StatGlobal_t::queryGlobalStatUser() StatsQueryOptions.EndTime = EOS_STATS_TIME_UNDEFINED; StatsQueryOptions.StatNamesCount = NUM_GLOBAL_STEAM_STATISTICS; - StatsQueryOptions.StatNames = new const char*[NUM_GLOBAL_STEAM_STATISTICS]; + StatsQueryOptions.StatNames = new const char* [NUM_GLOBAL_STEAM_STATISTICS]; - for ( int i = 0; i < NUM_GLOBAL_STEAM_STATISTICS; ++i ) + for (int i = 0; i < NUM_GLOBAL_STEAM_STATISTICS; ++i) { StatsQueryOptions.StatNames[i] = g_SteamGlobalStats[i].m_pchStatName; } - EOS_Stats_QueryStats(EOS_Platform_GetStatsInterface(EOS.ServerPlatformHandle), + EOS_Stats_QueryStats(EOS_Platform_GetStatsInterface(EOS.ServerPlatformHandle), &StatsQueryOptions, nullptr, OnQueryGlobalStatsCallback); delete[] StatsQueryOptions.StatNames; } diff --git a/src/eos.hpp b/src/eos.hpp index 1abd0fb5f..017898426 100644 --- a/src/eos.hpp +++ b/src/eos.hpp @@ -520,8 +520,10 @@ class EOSFuncs { //Makes Mac work because Apple had to do it different. } -#endif - EOS_Platform_Tick(PlatformHandle); +#endif] + if (PlatformHandle) { + EOS_Platform_Tick(PlatformHandle); + } SDL_Delay(1); if (SDL_GetTicks() - shutdownTicks >= 1000) { diff --git a/src/files.cpp b/src/files.cpp index 3a7442e8b..50ecc6626 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -4435,6 +4435,9 @@ void physfsReloadSounds(bool reloadAll) } OPENAL_CreateSound(soundFile.c_str(), true, &sounds[c]); #endif + if (Mods::isLoading) { + updateLoadingScreen(20.f + (c / (float)numsounds) * 10.f); + } } } } @@ -4526,6 +4529,9 @@ void physfsReloadSprites(bool reloadAll) //TODO: NX PORT: Any changes needed her } } } + if (Mods::isLoading) { + doLoadingScreen(); + } } FileIO::close(fp); } @@ -4575,6 +4581,9 @@ void physfsReloadTiles(bool reloadAll) } else { for ( int c = 0; !fp->eof(); c++ ) { + if (Mods::isLoading) { + doLoadingScreen(); + } char name[PATH_MAX]; fp->gets2(name, PATH_MAX); if ( PHYSFS_getRealDir(name) != NULL ) diff --git a/src/mod_tools.cpp b/src/mod_tools.cpp index 9e59fef57..d8605becf 100644 --- a/src/mod_tools.cpp +++ b/src/mod_tools.cpp @@ -8354,7 +8354,7 @@ void Mods::unloadMods(bool force) free(polymodels[c].faces); } } - generatePolyModels(0, nummodels, true); + generatePolyModels(0, nummodels, false); Mods::modelsListRequiresReloadUnmodded = false; } Mods::modelsListModifiedIndexes.clear(); diff --git a/src/net.cpp b/src/net.cpp index 004e954ff..94a582a78 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -91,8 +91,12 @@ void pollNetworkForShutdown() { SteamAPI_RunCallbacks(); #endif // STEAMWORKS #ifdef USE_EOS - EOS_Platform_Tick(EOS.PlatformHandle); - EOS_Platform_Tick(EOS.ServerPlatformHandle); + if (EOS.PlatformHandle) { + EOS_Platform_Tick(EOS.PlatformHandle); + } + if (EOS.ServerPlatformHandle) { + EOS_Platform_Tick(EOS.ServerPlatformHandle); + } #endif // USE_EOS } diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index b1dec7ede..ad77fa604 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -823,7 +823,9 @@ namespace MainMenu { Uint32 startTicks = SDL_GetTicks(); Uint32 checkTicks = startTicks; while ((checkTicks - startTicks) < msMin) { - EOS_Platform_Tick(EOS.PlatformHandle); + if (EOS.PlatformHandle) { + EOS_Platform_Tick(EOS.PlatformHandle); + } if (EOS.HandleReceivedMessagesAndIgnore(&newRemoteProductId)) { checkTicks = SDL_GetTicks(); // found a packet, extend the wait time. } From 643bc7066a8f6e2aba8cf100072faab46c9e9390 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 27 Sep 2023 11:45:36 -0700 Subject: [PATCH 062/146] add new menu music for merry barony --- lang/en.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lang/en.txt b/lang/en.txt index e1bfb1962..d6bb82dca 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -7822,7 +7822,8 @@ temple - Santa's Tricks by Myuu# 6042 underworld00 - Ice Land (Part 1) by Myuu underworld01 - Deck the Halls by Myuu -underworld02 - Winter Trouble by Myuu# +underworld02 - Winter Trouble by Myuu +intro - God Rest Ye Merry Gentlemen by NaturesEye# 6043 No holiday currently active.# From 2b06e5a756d77b7857ea1cea23ceedd4af695a1b Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Fri, 29 Sep 2023 03:38:13 +1000 Subject: [PATCH 063/146] * teleporter works with ghosts --- src/actteleporter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/actteleporter.cpp b/src/actteleporter.cpp index e2d098e92..11932ee57 100644 --- a/src/actteleporter.cpp +++ b/src/actteleporter.cpp @@ -72,7 +72,7 @@ void Entity::actTeleporter() { if ( selectedEntity[i] == this || client_selected[i] == this ) { - if ( inrange[i] ) + if ( inrange[i] && Player::getPlayerInteractEntity(i) ) { switch ( teleporterType ) { @@ -88,7 +88,7 @@ void Entity::actTeleporter() default: break; } - players[i]->entity->teleporterMove(teleporterX, teleporterY, teleporterType); + Player::getPlayerInteractEntity(i)->teleporterMove(teleporterX, teleporterY, teleporterType); return; } } From 491bc85aff86416612170cf47ac38ebb612da04c Mon Sep 17 00:00:00 2001 From: SheridanR Date: Sat, 30 Sep 2023 19:03:44 -0700 Subject: [PATCH 064/146] more safety checks for models.cache, if something fails to load at any stage then it just regenerates Signed-off-by: SheridanR --- src/files.cpp | 78 +++++++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/files.cpp b/src/files.cpp index 50ecc6626..1e4359eed 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -3155,31 +3155,19 @@ static ConsoleCommand ccmd_writeModelCache("/write_model_cache", "", void generatePolyModels(int start, int end, bool forceCacheRebuild) { - Sint32 x, y, z; - Sint32 c, i; - Uint32 index, indexdown[3]; - Uint8 newcolor, oldcolor; - bool buildingquad; - polyquad_t* quad1, * quad2; - Uint32 numquads; - list_t quads; - File* model_cache; - bool generateAll = start == 0 && end == nummodels; - - quads.first = NULL; - quads.last = NULL; + const bool generateAll = start == 0 && end == nummodels; if ( generateAll ) { polymodels = (polymodel_t*)malloc(sizeof(polymodel_t) * nummodels); - if ( useModelCache ) + if ( useModelCache && !forceCacheRebuild ) { #ifndef NINTENDO std::string cache_path = std::string(outputdir) + "/models.cache"; #else std::string cache_path = "models.cache"; #endif - model_cache = openDataFile(cache_path.c_str(), "rb"); + auto model_cache = openDataFile(cache_path.c_str(), "rb"); if ( model_cache ) { printlog("loading model cache...\n"); @@ -3195,38 +3183,60 @@ void generatePolyModels(int start, int end, bool forceCacheRebuild) if ( strncmp(polymodelsVersionStr, VERSION, strlen(VERSION)) ) { // different version. - forceCacheRebuild = true; printlog("[MODEL CACHE]: Detected outdated version number %s - current is %s. Upgrading cache...", polymodelsVersionStr, VERSION); + FileIO::close(model_cache); + goto generate; } } else { printlog("[MODEL CACHE]: Detected legacy cache without embedded version data, upgrading cache to %s...", VERSION); - model_cache->rewind(); - forceCacheRebuild = true; // upgrade from legacy cache - } - if ( !forceCacheRebuild ) - { - for ( size_t model_index = 0; model_index < nummodels; model_index++ ) { - updateLoadingScreen(30 + ((real_t)model_index / nummodels) * 30.0); - polymodel_t* cur = &polymodels[model_index]; - model_cache->read(&cur->numfaces, sizeof(cur->numfaces), 1); - cur->faces = (polytriangle_t*)calloc(sizeof(polytriangle_t), cur->numfaces); - model_cache->read(polymodels[model_index].faces, sizeof(polytriangle_t), cur->numfaces); - } - FileIO::close(model_cache); - return; - } - else - { - printlog("failed to load model cache"); FileIO::close(model_cache); + goto generate; } + + for ( size_t model_index = 0; model_index < nummodels; model_index++ ) { + updateLoadingScreen(30 + ((real_t)model_index / nummodels) * 30.0); + polymodel_t* cur = &polymodels[model_index]; + + size_t readsize; + readsize = model_cache->read(&cur->numfaces, sizeof(cur->numfaces), 1); + if (readsize == 1) { + cur->faces = (polytriangle_t*)calloc(sizeof(polytriangle_t), cur->numfaces); + readsize = model_cache->read(polymodels[model_index].faces, sizeof(polytriangle_t), cur->numfaces); + if (readsize != cur->numfaces) { + printlog("[MODEL CACHE]: Error loading model cache, rebuilding..."); + FileIO::close(model_cache); + goto generate; + } + } else { + printlog("[MODEL CACHE]: Error loading model cache, rebuilding..."); + FileIO::close(model_cache); + goto generate; + } + } + + printlog("successfully loaded model cache.\n"); + FileIO::close(model_cache); + return; } } } printlog("generating poly models...\n"); + + generate: + + Sint32 x, y, z; + Sint32 c, i; + Uint32 index, indexdown[3]; + Uint8 newcolor, oldcolor; + bool buildingquad; + polyquad_t* quad1, * quad2; + Uint32 numquads; + list_t quads; + quads.first = NULL; + quads.last = NULL; for ( c = start; c < end; ++c ) { From 3d98b58895ca173695a20ef4e9daa44df573cff5 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Sun, 1 Oct 2023 15:30:39 -0700 Subject: [PATCH 065/146] fix memory leak when aborting model cache load more safeguards when loading model cache Signed-off-by: SheridanR --- src/files.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/files.cpp b/src/files.cpp index 1e4359eed..fcc01dad4 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -3156,10 +3156,12 @@ static ConsoleCommand ccmd_writeModelCache("/write_model_cache", "", void generatePolyModels(int start, int end, bool forceCacheRebuild) { const bool generateAll = start == 0 && end == nummodels; + constexpr auto LARGEST_POLYMODEL_FACES_ALLOWED = (1<<15); // 32768 if ( generateAll ) { polymodels = (polymodel_t*)malloc(sizeof(polymodel_t) * nummodels); + memset(polymodels, 0, sizeof(polymodel_t) * nummodels); if ( useModelCache && !forceCacheRebuild ) { #ifndef NINTENDO @@ -3202,9 +3204,14 @@ void generatePolyModels(int start, int end, bool forceCacheRebuild) size_t readsize; readsize = model_cache->read(&cur->numfaces, sizeof(cur->numfaces), 1); if (readsize == 1) { - cur->faces = (polytriangle_t*)calloc(sizeof(polytriangle_t), cur->numfaces); - readsize = model_cache->read(polymodels[model_index].faces, sizeof(polytriangle_t), cur->numfaces); - if (readsize != cur->numfaces) { + readsize = 0; + if (cur->numfaces && cur->numfaces <= LARGEST_POLYMODEL_FACES_ALLOWED) { + cur->faces = (polytriangle_t*)calloc(sizeof(polytriangle_t), cur->numfaces); + if (cur->faces) { + readsize = model_cache->read(polymodels[model_index].faces, sizeof(polytriangle_t), cur->numfaces); + } + } + if (!readsize || readsize != cur->numfaces) { printlog("[MODEL CACHE]: Error loading model cache, rebuilding..."); FileIO::close(model_cache); goto generate; @@ -4127,6 +4134,9 @@ void generatePolyModels(int start, int end, bool forceCacheRebuild) } // translate quads into triangles + if (polymodels[c].faces) { + free(polymodels[c].faces); + } polymodels[c].faces = (polytriangle_t*)malloc(sizeof(polytriangle_t) * polymodels[c].numfaces); for ( uint64_t i = 0; i < polymodels[c].numfaces; i++ ) { @@ -4157,6 +4167,12 @@ void generatePolyModels(int start, int end, bool forceCacheRebuild) saveModelCache(); } #endif + + uint64_t greatest = 0; + for (uint32_t c = 0; c < nummodels; ++c) { + greatest = std::max(greatest, polymodels[c].numfaces); + } + printlog("greatest number of faces on any model: %lld", greatest); } void reloadModels(int start, int end) { From ed717fcef9aac5382e20b599bda07872d85119e8 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 3 Oct 2023 10:25:33 +1100 Subject: [PATCH 066/146] * more ghost stuff --- lang/en.txt | 3 +- lang/item_names.json | 3 + src/actplayer.cpp | 806 +++++++++++++++++++++++++++++++++-- src/collision.cpp | 26 +- src/draw.cpp | 30 +- src/editor.cpp | 2 + src/entity.cpp | 46 +- src/game.cpp | 4 +- src/game.hpp | 1 + src/interface/drawstatus.cpp | 2 +- src/interface/interface.cpp | 10 + src/magic/actmagic.cpp | 237 +++++++++- src/magic/castSpell.cpp | 52 ++- src/magic/magic.cpp | 1 + src/magic/magic.hpp | 8 +- src/magic/setupSpells.cpp | 30 ++ src/magic/spell.cpp | 11 + src/maps.cpp | 3 + src/net.cpp | 120 +++++- src/player.cpp | 9 +- src/player.hpp | 15 + 21 files changed, 1320 insertions(+), 99 deletions(-) diff --git a/lang/en.txt b/lang/en.txt index 4a99c1c0c..6d8af1a9f 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -7723,6 +7723,7 @@ Upload# 6019 Populate Hotbar# 6020 Preload Music Files# 6021 Preloading improves playback performance, but increases system memory usage. (Applied next game launch)# -6022 Show Player Callouts# +6044 Show Player Callouts# +6045 Push item# 6100 end# diff --git a/lang/item_names.json b/lang/item_names.json index ea597dbb5..cc25b4f5f 100644 --- a/lang/item_names.json +++ b/lang/item_names.json @@ -1320,6 +1320,9 @@ }, "spell_crab_web": { "name": "Spray Web" + }, + "spell_ghost_bolt": { + "name": "Ghostbolt" } } } \ No newline at end of file diff --git a/src/actplayer.cpp b/src/actplayer.cpp index 0968261f1..28b269171 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -50,19 +50,100 @@ static ConsoleVariable cvar_calloutStartZLimit("/callout_start_z_limit", -------------------------------------------------------------------------------*/ +#define GHOSTCAM_INIT my->skill[0] #define GHOSTCAM_PLAYERNUM my->skill[2] - +#define GHOSTCAM_SNEAKING my->skill[3] +#define GHOSTCAM_BOBMODE my->skill[4] +#define GHOSTCAM_SQUISH_TIME my->skill[5] +#define GHOSTCAM_SQUISH_DELAY my->skill[6] #define GHOSTCAM_BOB my->fskill[0] #define GHOSTCAM_BOBMOVE my->fskill[1] -#define GHOSTCAM_WEAPONYAW my->fskill[2] #define GHOSTCAM_DX my->fskill[3] #define GHOSTCAM_DY my->fskill[4] #define GHOSTCAM_DYAW my->fskill[5] #define GHOSTCAM_ROTX my->fskill[6] #define GHOSTCAM_ROTY my->fskill[7] +#define GHOSTCAM_SQUISH my->fskill[8] +#define GHOSTCAM_SQUISH_ANGLE my->fskill[9] +#define GHOSTCAM_WEAVE my->fskill[10] +#define GHOSTCAM_HOVER my->fskill[11] void Player::Ghost_t::handleGhostCameraBobbing(bool useRefreshRateDelta) { + if ( !my ) + { + return; + } + + int playernum = player.playernum; + + double refreshRateDelta = 1.0; + if ( useRefreshRateDelta && fps > 0.0 ) + { + refreshRateDelta *= TICKS_PER_SECOND / (real_t)fpsLimit; + } + + Input& input = Input::inputs[playernum]; + static ConsoleVariable cvar_ghostBob("/ghost_bob", 0.25); + static ConsoleVariable cvar_ghostBobSpeed("/ghost_bob_speed", 4.0); + + // camera bobbing + if ( bobbing ) + { + bool reset = false; + + if ( !gamePaused + && ((!inputs.hasController(playernum) + && ((input.binary("Move Forward") || input.binary("Move Backward")) + || (input.binary("Move Left") - input.binary("Move Right")))) + || (inputs.hasController(playernum) + && (inputs.getController(playernum)->getLeftXPercentForPlayerMovement() + || inputs.getController(playernum)->getLeftYPercentForPlayerMovement()))) ) + { + if ( !player.usingCommand() + && player.bControlEnabled ) + { + if ( !GHOSTCAM_SNEAKING ) + { + GHOSTCAM_BOBMOVE += 0.0125 * *cvar_ghostBobSpeed; + } + else + { + //GHOSTCAM_BOBMOVE += 0.025; + reset = true; + } + } + else + { + reset = true; + } + } + else + { + reset = true; + } + + if ( reset ) + { + if ( GHOSTCAM_BOBMOVE > 0.001 && GHOSTCAM_BOBMOVE < 1.0 ) + { + GHOSTCAM_BOBMOVE += 0.0125 * *cvar_ghostBobSpeed; + } + } + if ( GHOSTCAM_BOBMOVE >= 1.0 ) + { + GHOSTCAM_BOBMOVE = 0.0; + } + + real_t bobSpeed = *cvar_ghostBob; + GHOSTCAM_BOB = -bobSpeed + bobSpeed * sin((PI / 2) + GHOSTCAM_BOBMOVE * refreshRateDelta * 2 * PI); + } + else + { + GHOSTCAM_BOBMOVE = 0; + GHOSTCAM_BOB = 0; + GHOSTCAM_BOBMODE = 0; + } } void Player::Ghost_t::handleGhostCameraPosition(bool useRefreshRateDelta) { @@ -82,6 +163,8 @@ void Player::Ghost_t::handleGhostMovement(const bool useRefreshRateDelta) // calculate movement forces bool allowMovement = true; + static ConsoleVariable cvar_ghostSpeed("/ghost_speed", 1.5); + static ConsoleVariable cvar_ghostDrag("/ghost_drag", 0.95); if ( ((!player.usingCommand() && player.bControlEnabled && !gamePaused)) && allowMovement ) @@ -114,16 +197,16 @@ void Player::Ghost_t::handleGhostMovement(const bool useRefreshRateDelta) } } - real_t speedFactor = 12.0;// getSpeedFactor(weightratio, statGetDEX(stats[PLAYER_NUM], players[PLAYER_NUM]->entity)); + real_t speedFactor = *cvar_ghostSpeed;// getSpeedFactor(weightratio, statGetDEX(stats[PLAYER_NUM], players[PLAYER_NUM]->entity)); speedFactor *= refreshRateDelta; - my->vel_x += y_force * cos(my->yaw) * .045 * speedFactor; - my->vel_y += y_force * sin(my->yaw) * .045 * speedFactor; - my->vel_x += x_force * cos(my->yaw + PI / 2) * .0225 * speedFactor; - my->vel_y += x_force * sin(my->yaw + PI / 2) * .0225 * speedFactor; + my->vel_x += y_force * cos(my->yaw) * .045 * speedFactor / (1 + GHOSTCAM_SNEAKING); + my->vel_y += y_force * sin(my->yaw) * .045 * speedFactor / (1 + GHOSTCAM_SNEAKING); + my->vel_x += x_force * cos(my->yaw + PI / 2) * .0225 * speedFactor / (1 + GHOSTCAM_SNEAKING); + my->vel_y += x_force * sin(my->yaw + PI / 2) * .0225 * speedFactor / (1 + GHOSTCAM_SNEAKING); } - my->vel_x *= pow(0.75, refreshRateDelta); - my->vel_y *= pow(0.75, refreshRateDelta); + my->vel_x *= pow(*cvar_ghostDrag, refreshRateDelta); + my->vel_y *= pow(*cvar_ghostDrag, refreshRateDelta); /*for ( int i = 0; i < MAXPLAYERS; ++i ) { @@ -230,13 +313,320 @@ bool Player::Ghost_t::allowedInteractEntity(Entity& entity) || entity.behavior == &actSwitchWithTimer || entity.behavior == &actPowerCrystal || entity.behavior == &actPowerCrystalBase - || entity.behavior == &actTeleportShrine ) + || entity.behavior == &actTeleportShrine + || entity.behavior == &actTeleporter ) { return true; } return false; } +void Player::Ghost_t::handleAttack() +{ + if ( !casting ) + { + castingHeldDuration = 0; + } + Input& input = Input::inputs[player.playernum]; + bool attack = false; + bool defending = false; + if ( !player.usingCommand() + && player.bControlEnabled + && !gamePaused ) + { + if ( player.shootmode && input.binaryToggle("Attack") ) + { + attack = true; + } + if ( input.binary("Defend") ) + { + defending = true; + } + } + + if ( GHOSTCAM_SNEAKING != defending || ticks % 120 == 0 ) + { + if ( multiplayer == CLIENT ) + { + strcpy((char*)net_packet->data, "GHOD"); + net_packet->data[4] = player.playernum; + net_packet->data[5] = defending; + net_packet->address.host = net_server.host; + net_packet->address.port = net_server.port; + net_packet->len = 6; + sendPacketSafe(net_sock, -1, net_packet, 0); + } + else if ( multiplayer == SERVER ) + { + strcpy((char*)net_packet->data, "GHOD"); + net_packet->data[4] = player.playernum; + net_packet->data[5] = defending; + net_packet->len = 6; + for ( int c = 1; c < MAXPLAYERS; ++c ) + { + // relay packet to other players + if ( client_disconnected[c] ) + { + continue; + } + net_packet->address.host = net_clients[c - 1].host; + net_packet->address.port = net_clients[c - 1].port; + sendPacketSafe(net_sock, -1, net_packet, c - 1); + } + } + } + GHOSTCAM_SNEAKING = defending; + + const int castLoopDuration = 20; + const int teleportLoopDuration = 1; + bool pushSpell = true; + if ( !attack ) + { + if ( casting && castingHeldDuration >= castLoopDuration ) + { + if ( !pushSpell ) + { + int tx = spawnX; + int ty = spawnY; + Entity* target = nullptr; + if ( teleportToPlayer >= MAXPLAYERS ) + { + teleportToPlayer = -1; + tx = startRoomX; + ty = startRoomY; + } + else + { + ++teleportToPlayer; + while ( teleportToPlayer >= 0 && teleportToPlayer < MAXPLAYERS ) + { + if ( teleportToPlayer != player.playernum && Player::getPlayerInteractEntity(teleportToPlayer) ) + { + target = Player::getPlayerInteractEntity(teleportToPlayer); + tx = static_cast(target->x) / 16; + ty = static_cast(target->y) / 16; + break; + } + ++teleportToPlayer; + } + if ( !target ) + { + if ( teleportToPlayer >= MAXPLAYERS ) + { + tx = spawnX; + ty = spawnY; + } + } + } + + Entity* spellTimer = createParticleTimer(my, 1, 593); + spellTimer->particleTimerPreDelay = 0; // wait x ticks before animation. + spellTimer->particleTimerEndAction = PARTICLE_EFFECT_GHOST_TELEPORT; // teleport behavior of timer. + spellTimer->particleTimerEndSprite = 593; // sprite to use for end of timer function. + spellTimer->particleTimerCountdownAction = 0; + spellTimer->particleTimerCountdownSprite = -1; + if ( target != nullptr ) + { + spellTimer->particleTimerTarget = static_cast(target->getUID()); // get the target to teleport around. + } + spellTimer->particleTimerVariable1 = 1; // distance of teleport in tiles + spellTimer->particleTimerVariable2 = (tx & 0xFFFF) << 16; + spellTimer->particleTimerVariable2 |= ty & 0xFFFF; + if ( multiplayer == SERVER ) + { + serverSpawnMiscParticles(my, PARTICLE_EFFECT_GHOST_TELEPORT, 593); + } + } + else + { + if ( auto projectile = castSpell(uid, &spell_ghost_bolt, false, true) ) + { + projectile->actmagicSpellbookBonus = getSpellPower() * 100.0; + } + } + casting = false; + castingHeldDuration = 0; + + + for ( int i = 0; i < 5; ++i ) + { + Entity* entity = spawnGib(my); + entity->flags[INVISIBLE] = false; + entity->flags[SPRITE] = true; + entity->flags[NOUPDATE] = true; + entity->flags[UPDATENEEDED] = false; + entity->flags[OVERDRAW] = true; + entity->lightBonus = vec4(0.2f, 0.2f, 0.2f, 0.f); + real_t scale = 0.15f; + entity->scalex = scale; + entity->scaley = scale; + entity->scalez = scale; + entity->sprite = 16; + entity->x = 8; + entity->y = 0; + entity->z = (cameras[player.playernum].z * .5 - my->z) + 7 + -2; + entity->z -= 4.75; + entity->z = local_rng.uniform(entity->z, entity->z - 4); + entity->z += 5.0; + entity->yaw = -cameravars[player.playernum].shakex2; + entity->yaw = ((local_rng.rand() % 6) * 60) * PI / 180.0; + entity->pitch = (local_rng.rand() % 360) * PI / 180.0; + entity->roll = (local_rng.rand() % 360) * PI / 180.0; + entity->vel_x = cos(entity->yaw) * .1; + entity->vel_y = sin(entity->yaw) * .1; + entity->vel_z = -.15; + entity->fskill[3] = 0.01; + entity->skill[11] = player.playernum; + } + return; + } + } + + if ( !casting ) + { + if ( attack ) + { + casting = true; + playSoundEntity(my, 170, 128); + } + } + else + { + ++castingHeldDuration; + + if ( !pushSpell ) + { + float x = 6; + float y = 0.1; + float z = 5.5; + // boosty boost + for ( int i = 0; i < 3 && castingHeldDuration == 1; ++i ) + { + Entity* entity = newEntity(1245, 1, map.entities, nullptr); + entity->yaw = i * 2 * PI / 3; + entity->x = x; + entity->y = y; + entity->z = z; + double missile_speed = 4; + entity->vel_x = 0.0; + entity->vel_y = 0.0; + entity->actmagicIsOrbiting = 2; + entity->actmagicOrbitDist = 16.0; + entity->actmagicOrbitStationaryCurrentDist = 0.0; + entity->actmagicOrbitStartZ = entity->z; + //entity->roll -= (PI / 8); + entity->actmagicOrbitVerticalSpeed = -0.3; + entity->actmagicOrbitVerticalDirection = 1; + entity->actmagicOrbitLifetime = TICKS_PER_SECOND; + entity->actmagicOrbitStationaryX = x; + entity->actmagicOrbitStationaryY = y; + entity->vel_z = -0.1; + entity->behavior = &actHUDMagicParticleCircling; + + entity->flags[PASSABLE] = true; + entity->flags[NOUPDATE] = true; + entity->flags[UNCLICKABLE] = true; + entity->flags[UPDATENEEDED] = false; + entity->flags[OVERDRAW] = true; + entity->skill[11] = player.playernum; + if ( multiplayer != CLIENT ) + { + entity_uids--; + } + entity->setUID(-3); + } + + if ( ticks % 5 == 0 ) + { + Entity* entity = spawnGib(my); + entity->flags[INVISIBLE] = false; + entity->flags[SPRITE] = true; + entity->flags[NOUPDATE] = true; + entity->flags[UPDATENEEDED] = false; + entity->flags[OVERDRAW] = true; + entity->lightBonus = vec4(0.2f, 0.2f, 0.2f, 0.f); + real_t scale = 0.15f; + entity->scalex = scale; + entity->scaley = scale; + entity->scalez = scale; + entity->sprite = 16; + entity->x = 8; + entity->y = 0; + entity->z = (cameras[player.playernum].z * .5 - my->z) + 7 + -2; + entity->z -= 6.75; + entity->z = local_rng.uniform(entity->z, entity->z - 4); + entity->z += 5.0; + entity->yaw = -cameravars[player.playernum].shakex2; + entity->yaw = ((local_rng.rand() % 6) * 60) * PI / 180.0; + entity->pitch = (local_rng.rand() % 360) * PI / 180.0; + entity->roll = (local_rng.rand() % 360) * PI / 180.0; + entity->vel_x = cos(entity->yaw) * .1; + entity->vel_y = sin(entity->yaw) * .1; + entity->vel_z = -.15; + entity->fskill[3] = 0.01; + entity->skill[11] = player.playernum; + } + } + else + { + float x = 6; + float y = 0.1; + float z = 1.5; + // boosty boost + for ( int i = 1; i < 3; ++i ) + { + Uint32 animTick = castingHeldDuration >= castLoopDuration ? castLoopDuration : castingHeldDuration; + + Entity* entity = newEntity(1243, 1, map.entities, nullptr); //Particle entity. + entity->x = x - 0.01 * (5 + local_rng.rand() % 11); + entity->y = y - 0.01 * (5 + local_rng.rand() % 11); + entity->z = z - 0.01 * (10 + local_rng.rand() % 21); + if ( i == 1 ) + { + entity->y += -2; + entity->y += -2 * sin(2 * PI * (animTick % 40) / 40.f); + } + else + { + entity->y += -(-2); + entity->y -= -2 * sin(2 * PI * (animTick % 40) / 40.f); + } + + entity->focalz = -2; + + real_t scale = 0.05f; + scale += (animTick) * 0.025f; + scale = std::min(scale, 0.5); + + entity->scalex = scale; + entity->scaley = scale; + entity->scalez = scale; + entity->sizex = 1; + entity->sizey = 1; + entity->yaw = 0; + entity->roll = i * 2 * PI / 3; + entity->pitch = PI + ((animTick % 40) / 40.f) * 2 * PI; + entity->ditheringDisabled = true; + entity->flags[PASSABLE] = true; + entity->flags[NOUPDATE] = true; + entity->flags[UNCLICKABLE] = true; + entity->flags[UPDATENEEDED] = false; + entity->flags[OVERDRAW] = true; + entity->lightBonus = vec4(0.25f, 0.25f, + 0.25f, 0.f); + entity->behavior = &actHUDMagicParticle; + entity->vel_z = 0; + entity->skill[11] = player.playernum; + if ( multiplayer != CLIENT ) + { + entity_uids--; + } + entity->setUID(-3); + } + } + } +} + void Player::Ghost_t::handleActions() { Input& input = Input::inputs[player.playernum]; @@ -631,6 +1021,13 @@ void Player::Ghost_t::handleActions() } } +void Player::Ghost_t::createBounceAnimate() +{ + if ( !my ) { return; } + + GHOSTCAM_SQUISH_ANGLE = Player::Ghost_t::GHOST_SQUISH_START_ANGLE / 100.f; +} + void Player::Ghost_t::handleGhostCameraUpdate(const bool useRefreshRateDelta) { if ( !my ) { return; } @@ -832,6 +1229,70 @@ void Player::Ghost_t::handleGhostCameraUpdate(const bool useRefreshRateDelta) } } +int Player::Ghost_t::getSpriteForPlayer(const int player) +{ + if ( !colorblind_lobby ) + { + return ((player < 4) ? (GHOST_MODEL_P1 + player) : GHOST_MODEL_PX); + } + Uint32 index = 4; + switch ( player ) + { + case 0: + index = 2; + break; + case 1: + index = 3; + break; + case 2: + index = 1; + break; + case 3: + index = 4; + break; + default: + break; + } + return GHOST_MODEL_P1 + index; +} + +void actDeathGhostLimb(Entity* my) +{ + int playernum = GHOSTCAM_PLAYERNUM; + if ( playernum < 0 || playernum >= MAXPLAYERS ) + { + return; + } + + if ( !players[playernum] || !players[playernum]->ghost.my ) + { + list_RemoveNode(my->mynode); + return; + } +} + +void Player::Ghost_t::initStartRoomLocation(int x, int y) +{ + startRoomX = x; + startRoomY = y; +} + +void Player::Ghost_t::initTeleportLocations(int x, int y) +{ + teleportToPlayer = -1; + spawnX = x; + spawnY = y; + + if ( startRoomX == -1 ) + { + startRoomX = x; + } + if ( startRoomY == -1 ) + { + startRoomY = y; + } +} + void actDeathGhost(Entity* my) { int playernum = GHOSTCAM_PLAYERNUM; @@ -843,40 +1304,136 @@ void actDeathGhost(Entity* my) players[playernum]->ghost.my = my; players[playernum]->ghost.uid = my->getUID(); + if ( !GHOSTCAM_INIT ) + { + GHOSTCAM_INIT = 1; + + // body model + Entity* entity = newEntity(my->sprite, 1, map.entities, nullptr); + entity->behavior = &actDeathGhostLimb; + entity->sizex = 4; + entity->sizey = 4; + entity->flags[PASSABLE] = true; + entity->flags[UPDATENEEDED] = false; + entity->flags[NOUPDATE] = true; + entity->flags[GENIUS] = true; + entity->skill[2] = GHOSTCAM_PLAYERNUM; + node_t* node = list_AddNodeLast(&my->children); + node->element = entity; + node->deconstructor = &emptyDeconstructor; + node->size = sizeof(Entity*); + my->bodyparts.push_back(entity); + + // eyes model + entity = newEntity(1237, 1, map.entities, nullptr); + entity->behavior = &actDeathGhostLimb; + entity->sizex = 4; + entity->sizey = 4; + entity->flags[PASSABLE] = true; + entity->flags[UPDATENEEDED] = false; + entity->flags[NOUPDATE] = true; + entity->flags[GENIUS] = true; + entity->skill[2] = GHOSTCAM_PLAYERNUM; + node = list_AddNodeLast(&my->children); + node->element = entity; + node->deconstructor = &emptyDeconstructor; + node->size = sizeof(Entity*); + my->bodyparts.push_back(entity); + + players[playernum]->ghost.initTeleportLocations(my->x / 16, my->y / 16); + } + + auto player = players[playernum]; my->removeLightField(); - my->light = addLight(my->x / 16, my->y / 16, "deathcam"); + char* light_type = nullptr; + bool ambientLight = false; + if ( GHOSTCAM_SNEAKING ) + { + ambientLight = true; + } + + if ( !ambientLight ) + { + switch ( my->sprite ) + { + case Player::Ghost_t::GHOST_MODEL_P1: + light_type = "ghost_yellow"; + break; + case Player::Ghost_t::GHOST_MODEL_P2: + light_type = "ghost_green"; + break; + case Player::Ghost_t::GHOST_MODEL_P3: + light_type = "ghost_red"; + break; + case Player::Ghost_t::GHOST_MODEL_P4: + light_type = "ghost_pink"; + break; + default: + light_type = "ghost_white"; + break; + } + + my->light = addLight(my->x / 16, my->y / 16, light_type, 0, 0); + } + else if ( players[playernum]->isLocalPlayer() ) + { + switch ( my->sprite ) + { + case Player::Ghost_t::GHOST_MODEL_P1: + light_type = "ghost_yellow_sneaking_ambient"; + break; + case Player::Ghost_t::GHOST_MODEL_P2: + light_type = "ghost_green_sneaking_ambient"; + break; + case Player::Ghost_t::GHOST_MODEL_P3: + light_type = "ghost_red_sneaking_ambient"; + break; + case Player::Ghost_t::GHOST_MODEL_P4: + light_type = "ghost_pink_sneaking_ambient"; + break; + default: + light_type = "ghost_white_sneaking_ambient"; + break; + } + my->light = addLight(my->x / 16, my->y / 16, light_type, 0, playernum + 1); + } + + static ConsoleVariable cvar_ghostSquish("/ghost_squish", 6.f); + static ConsoleVariable cvar_ghostSquishFactor("/ghost_squish_factor", 0.3f); if ( player->isLocalPlayer() ) { - bool inputsEnabled = false; - if ( !players[playernum]->GUI.isGameoverActive() - && players[playernum]->bControlEnabled - && !players[playernum]->usingCommand() - && !gamePaused ) + if ( autoLimbReload && ticks % 20 == 0 && (playernum == clientnum) ) { - inputsEnabled = true; + consoleCommand("/reloadlimbs"); } if ( !usecamerasmoothing ) { + player->ghost.handleGhostCameraBobbing(false); player->ghost.handleGhostMovement(false); player->ghost.handleGhostCameraUpdate(false); } player->ghost.handleActions(); + player->ghost.handleAttack(); real_t camx, camy, camz, camang, camvang; camx = my->x / 16.f; camy = my->y / 16.f; - camz = my->z * 2.f; + camz = my->z * 2.f + GHOSTCAM_BOB; camang = my->yaw; camvang = my->pitch; - camx -= cos(my->yaw) * cos(my->pitch) * 1.5; - camy -= sin(my->yaw) * cos(my->pitch) * 1.5; - camz -= sin(my->pitch) * 16; + static ConsoleVariable cvar_ghostThirdPerson("/ghost_thirdperson", false); + if ( *cvar_ghostThirdPerson || !keystatus[SDLK_g] ) + { + camx -= cos(my->yaw) * cos(my->pitch) * 1.5; + camy -= sin(my->yaw) * cos(my->pitch) * 1.5; + camz -= sin(my->pitch) * 16; + } if ( !TimerExperiments::bUseTimerInterpolation ) { @@ -885,7 +1442,6 @@ void actDeathGhost(Entity* my) cameras[playernum].z = camz; cameras[playernum].ang = camang; cameras[playernum].vang = camvang; - return; } else { @@ -919,6 +1475,8 @@ void actDeathGhost(Entity* my) } } + static ConsoleVariable cvar_ghostBounce("/ghost_bounce", -1.0); + real_t dist = 0.0; if ( player->isLocalPlayer() ) { // send movement updates to server @@ -934,40 +1492,200 @@ void actDeathGhost(Entity* my) SDLNet_Write16((Sint16)(my->yaw * 128), &net_packet->data[14]); SDLNet_Write16((Sint16)(my->pitch * 128), &net_packet->data[16]); net_packet->data[18] = secretlevel; + // continued after clipmove... + } + + // perform collision detection + dist = clipMove(&my->x, &my->y, my->vel_x, my->vel_y, my); + bool bounceAnimate = false; + if ( dist != sqrt(my->vel_x * my->vel_x + my->vel_y * my->vel_y) ) + { + if ( !hit.side ) + { + my->vel_x *= *cvar_ghostBounce; + my->vel_y *= *cvar_ghostBounce; + if ( abs(my->vel_x) > 0.15 || abs(my->vel_y) > 0.15 ) + { + bounceAnimate = true; + } + } + else if ( hit.side == HORIZONTAL ) + { + my->vel_x *= *cvar_ghostBounce; + if ( abs(my->vel_x) > 0.15 ) + { + bounceAnimate = true; + } + } + else + { + my->vel_y *= *cvar_ghostBounce; + if ( abs(my->vel_y) > 0.15 ) + { + bounceAnimate = true; + } + } + if ( bounceAnimate ) + { + if ( GHOSTCAM_SQUISH_ANGLE < 0.01 && GHOSTCAM_SQUISH_DELAY == 0 ) + { + GHOSTCAM_SQUISH_ANGLE = Player::Ghost_t::GHOST_SQUISH_START_ANGLE / 100.f; + GHOSTCAM_SQUISH_DELAY = TICKS_PER_SECOND / 2; + } + else + { + bounceAnimate = false; + } + } + my->vel_x = std::max(-4.0, std::min(my->vel_x, 4.0)); + my->vel_y = std::max(-4.0, std::min(my->vel_y, 4.0)); + } + + if ( multiplayer == CLIENT ) + { + net_packet->data[19] = bounceAnimate ? 1 : 0; net_packet->address.host = net_server.host; net_packet->address.port = net_server.port; - net_packet->len = 19; + net_packet->len = 20; sendPacket(net_sock, -1, net_packet, 0); } - - // perform collision detection - real_t dist = clipMove(&my->x, &my->y, my->vel_x, my->vel_y, my); + if ( multiplayer == SERVER && bounceAnimate ) + { + serverUpdateEntityFSkill(my, 9); + } } + --GHOSTCAM_SQUISH_DELAY; + GHOSTCAM_SQUISH_DELAY = std::max(0, GHOSTCAM_SQUISH_DELAY); + if ( !player->isLocalPlayer() && multiplayer == SERVER ) { // PLAYER_VEL* skills updated by messages sent to server from client // move (dead reckoning) - /*if ( noclip == false )*/ + // from GMOV in serverHandlePacket - new_x and new_y are accumulated positions + if ( my->new_x > 0.001 ) { - // from PMOV in serverHandlePacket - new_x and new_y are accumulated positions - if ( my->new_x > 0.001 ) - { - my->x = my->new_x; - } - if ( my->new_y > 0.001 ) - { - my->y = my->new_y; - } - - real_t dist = clipMove(&my->x, &my->y, my->vel_x, my->vel_y, my); + my->x = my->new_x; } + if ( my->new_y > 0.001 ) + { + my->y = my->new_y; + } + + dist = clipMove(&my->x, &my->y, my->vel_x, my->vel_y, my); } if ( !player->isLocalPlayer() && multiplayer == CLIENT ) { - real_t dist = sqrt(my->vel_x * my->vel_x + my->vel_y * my->vel_y); + dist = sqrt(my->vel_x * my->vel_x + my->vel_y * my->vel_y); + } + + + real_t dir = my->yaw - atan2(my->vel_y, my->vel_x); + while ( dir < 0 ) + { + dir += 2 * PI; + } + while ( dir > 2 * PI ) + { + dir -= 2 * PI; + } + //messagePlayer(0, MESSAGE_DEBUG, "%.2f", dir); + + const real_t weaveSpeed = 0.05; + if ( dist < 0.15 || (abs(dir - PI / 2) < PI / 16) || (abs(dir - 3 * PI / 2) < PI / 16) ) + { + if ( GHOSTCAM_WEAVE >= 0.0 ) + { + GHOSTCAM_WEAVE -= weaveSpeed; + GHOSTCAM_WEAVE = std::max(GHOSTCAM_WEAVE, 0.0); + } + else + { + GHOSTCAM_WEAVE += weaveSpeed; + GHOSTCAM_WEAVE = std::min(GHOSTCAM_WEAVE, 0.0); + } + } + else if ( dir <= PI / 2 || dir >= 3 * PI / 2 ) + { + GHOSTCAM_WEAVE += weaveSpeed; + GHOSTCAM_WEAVE = std::min(GHOSTCAM_WEAVE, PI / 8); + } + else if ( dir > PI / 2 && dir < 3 * PI / 2 ) + { + GHOSTCAM_WEAVE -= weaveSpeed; + GHOSTCAM_WEAVE = std::max(GHOSTCAM_WEAVE, -PI / 8); + } + + + /*GHOSTCAM_WEAVE += weaveSpeed / 5; + while ( GHOSTCAM_WEAVE >= 1.0 ) + { + GHOSTCAM_WEAVE -= 1.0; + }*/ + + if ( abs(GHOSTCAM_HOVER - (PI / 2)) < PI / 64 ) + { + GHOSTCAM_HOVER += PI / 320; + } + else if ( abs(GHOSTCAM_HOVER - (3 * PI / 2)) < PI / 64 ) + { + GHOSTCAM_HOVER += PI / 320; + } + else + { + GHOSTCAM_HOVER += PI / 64; + } + while ( GHOSTCAM_HOVER >= 2 * PI ) + { + GHOSTCAM_HOVER -= 2 * PI; + } + + const real_t squishRate = *cvar_ghostSquish; + real_t squishFactor = *cvar_ghostSquishFactor; + if ( GHOSTCAM_SQUISH_ANGLE < 0.0 ) + { + squishFactor *= std::max(0.0, (1.0 + GHOSTCAM_SQUISH_ANGLE)); + } + const real_t inc = squishRate * (PI / TICKS_PER_SECOND); + //GHOSTCAM_SQUISH = fmod(GHOSTCAM_SQUISH + inc, PI * 2); + GHOSTCAM_SQUISH = GHOSTCAM_SQUISH_ANGLE * 2 * PI; + const real_t squish = sin(GHOSTCAM_SQUISH) * squishFactor; + GHOSTCAM_SQUISH_ANGLE -= squishRate * (0.5 / TICKS_PER_SECOND); + GHOSTCAM_SQUISH_ANGLE = std::max(GHOSTCAM_SQUISH_ANGLE, -1.0); + + my->flags[INVISIBLE] = true; + int index = -1; + for ( auto bodypart : my->bodyparts ) + { + ++index; + + bodypart->yaw = my->yaw; + bodypart->pitch = my->pitch; + bodypart->roll = my->roll; + bodypart->x = my->x; + bodypart->y = my->y; + bodypart->z = my->z; + bodypart->scalex = 1.0 - squish; + bodypart->scaley = 1.0 - squish; + bodypart->scalez = 1.0 + squish; + + if ( index == 0 ) + { + bodypart->z += 0.5 * sin(GHOSTCAM_HOVER); + bodypart->pitch += GHOSTCAM_WEAVE; + bodypart->fskill[0] += PI / 64; + while ( bodypart->fskill[0] >= 2 * PI ) + { + bodypart->fskill[0] -= 2 * PI; + } + } + else if ( index == 1 ) + { + bodypart->z += 0.4 * sin(GHOSTCAM_HOVER); + bodypart->focalx = 2.25; + } } } @@ -1172,6 +1890,7 @@ void actDeathCam(Entity* my) && players[DEATHCAM_PLAYERNUM]->bControlEnabled && !players[DEATHCAM_PLAYERNUM]->usingCommand() && !gamePaused + && !players[DEATHCAM_PLAYERNUM]->ghost.isActive() && (Input::inputs[DEATHCAM_PLAYERNUM].consumeBinaryToggle("Attack") || Input::inputs[DEATHCAM_PLAYERNUM].consumeBinaryToggle("MenuConfirm")) ) { @@ -1182,17 +1901,18 @@ void actDeathCam(Entity* my) // deathcam if ( multiplayer != CLIENT ) { - int sprite = Player::Ghost_t::GHOST_MODEL_P1 + (DEATHCAM_PLAYERNUM < 4 ? DEATHCAM_PLAYERNUM : 4); + int sprite = Player::Ghost_t::getSpriteForPlayer(DEATHCAM_PLAYERNUM); Entity* entity = newEntity(sprite, 1, map.entities, nullptr); //Ghost entity. entity->x = my->x; entity->y = my->y; entity->z = -4; entity->flags[PASSABLE] = true; - entity->flags[INVISIBLE] = false;// true; + entity->flags[INVISIBLE] = true; + entity->flags[GENIUS] = true; entity->behavior = &actDeathGhost; entity->skill[2] = DEATHCAM_PLAYERNUM; - entity->sizex = 4; - entity->sizey = 4; + entity->sizex = 2; + entity->sizey = 2; entity->yaw = my->yaw; entity->pitch = 0; if ( DEATHCAM_PLAYERNUM == clientnum && multiplayer == CLIENT ) diff --git a/src/collision.cpp b/src/collision.cpp index 2121da62b..b6148cb39 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -421,6 +421,7 @@ bool entityInsideEntity(Entity* entity1, Entity* entity2) bool entityInsideSomething(Entity* entity) { + if ( !entity ) { return false; } #ifdef __ARM_NEON__ const float f[2] = { (float)entity->x, (float)entity->y }; int32x2_t xy = vcvt_s32_f32(vmul_n_f32(vld1_f32(f), 1.f/16.f)); @@ -450,6 +451,13 @@ bool entityInsideSomething(Entity* entity) { continue; } + if ( entity->behavior == &actDeathGhost ) + { + if ( testEntity->behavior == &actMonster || testEntity->behavior == &actPlayer ) + { + continue; + } + } if ( entityInsideEntity(entity, testEntity) ) { return true; @@ -714,10 +722,18 @@ int barony_clear(real_t tx, real_t ty, Entity* my) continue; } } - else if ( multiplayer != CLIENT && parent && parentStats && yourStats - && tryReduceCollisionSize ) + else if ( multiplayer != CLIENT && tryReduceCollisionSize ) { - reduceCollisionSize = useSmallCollision(*parent, *parentStats, *entity, *yourStats); + if ( parent && parentStats && yourStats ) + { + reduceCollisionSize = useSmallCollision(*parent, *parentStats, *entity, *yourStats); + } + else if ( parent && parent->behavior == &actDeathGhost + && (entity->behavior == &actPlayer + || (entity->behavior == &actMonster && entity->monsterAllyGetPlayerLeader())) ) + { + reduceCollisionSize = true; + } } if ( multiplayer == CLIENT ) @@ -1759,6 +1775,10 @@ int checkObstacle(long x, long y, Entity* my, Entity* target, bool useTileEntity { continue; } + if ( my && my->behavior == &actDeathGhost && (entity->behavior == &actPlayer || entity->behavior == &actMonster) ) + { + continue; + } if ( x >= (int)(entity->x - entity->sizex) && x <= (int)(entity->x + entity->sizex) ) { if ( y >= (int)(entity->y - entity->sizey) && y <= (int)(entity->y + entity->sizey) ) diff --git a/src/draw.cpp b/src/draw.cpp index b405718bb..39d1520e1 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -1955,12 +1955,26 @@ void drawEntities3D(view_t* camera, int mode) } if ( entity->flags[GENIUS] ) { - // genius entities are not drawn when the camera is inside their bounding box - if ( camera->x >= (entity->x - entity->sizex) / 16 && camera->x <= (entity->x + entity->sizex) / 16 ) - if ( camera->y >= (entity->y - entity->sizey) / 16 && camera->y <= (entity->y + entity->sizey) / 16 ) - { - continue; - } + // genius entities are not drawn when the camera is inside their bounding box +#ifndef EDITOR + if ( entity->behavior == &actDeathGhost ) + { + // ghost have small collision box + if ( camera->x >= (entity->x - std::max(4, entity->sizex)) / 16 && camera->x <= (entity->x + std::max(4, entity->sizex)) / 16 ) + if ( camera->y >= (entity->y - std::max(4, entity->sizey)) / 16 && camera->y <= (entity->y + std::max(4, entity->sizey)) / 16 ) + { + continue; + } + } + else +#endif + { + if ( camera->x >= (entity->x - entity->sizex) / 16 && camera->x <= (entity->x + entity->sizex) / 16 ) + if ( camera->y >= (entity->y - entity->sizey) / 16 && camera->y <= (entity->y + entity->sizey) / 16 ) + { + continue; + } + } } if ( entity->flags[OVERDRAW] && splitscreen ) { @@ -1970,7 +1984,9 @@ void drawEntities3D(view_t* camera, int mode) if ( entity->behavior == &actHudWeapon || entity->behavior == &actHudArm || entity->behavior == &actGib - || entity->behavior == &actFlame ) + || entity->behavior == &actFlame + || entity->behavior == &actHUDMagicParticle + || entity->behavior == &actHUDMagicParticleCircling ) { // the gibs are from casting magic in the HUD if ( entity->skill[11] != currentPlayerViewport ) diff --git a/src/editor.cpp b/src/editor.cpp index 80ce1b6fc..275e7054e 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -69,6 +69,8 @@ GenericGUIMenu GenericGUI[MAXPLAYERS]; void actGib(Entity* my) {} // dummy for draw.cpp void actHudArm(Entity* my) {} // dummy for draw.cpp void actHudWeapon(Entity* my) {} // dummy for draw.cpp +void actHUDMagicParticle(Entity* my) {} // dummy for draw.cpp +void actHUDMagicParticleCircling(Entity* my) {} // dummy for draw.cpp void actHudShield(Entity* my) {} // dummy for draw.cpp void actHudAdditional(Entity* my) {} // dummy for draw.cpp void actHudArrowModel(Entity* my) {} // dummy for draw.cpp diff --git a/src/entity.cpp b/src/entity.cpp index 3683c320a..2ae2f194f 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -877,7 +877,7 @@ int Entity::entityLightAfterReductions(Stat& myStats, Entity* observer) { light /= 2; // halve for sneaking } - light -= (light - TOUCHRANGE) * (1.0 * (myStats.PROFICIENCIES[PRO_STEALTH] / 100.0)); // reduce to 32 as sneak approaches 100 + light -= (std::max(0, light - TOUCHRANGE)) * (1.0 * (myStats.PROFICIENCIES[PRO_STEALTH] / 100.0)); // reduce to 32 as sneak approaches 100 Stat* observerStats = observer->getStats(); if ( observerStats && observerStats->EFFECTS[EFF_BLIND] ) { @@ -901,7 +901,7 @@ int Entity::entityLightAfterReductions(Stat& myStats, Entity* observer) { light /= 2; // halve for sneaking } - light -= (light - TOUCHRANGE) * (1.0 * (myStats.PROFICIENCIES[PRO_STEALTH] / 100.0)); // reduce to 32 as sneak approaches 100 + light -= (std::max(0, light - TOUCHRANGE)) * (1.0 * (myStats.PROFICIENCIES[PRO_STEALTH] / 100.0)); // reduce to 32 as sneak approaches 100 } } @@ -10485,14 +10485,22 @@ bool Entity::teleport(int tele_x, int tele_y) } } - if ( strstr(map.name, "Minotaur") || checkObstacle((tele_x << 4) + 8, (tele_y << 4) + 8, this, NULL) ) + if ( (strstr(map.name, "Minotaur") && behavior != &actDeathGhost) + || checkObstacle((tele_x << 4) + 8, (tele_y << 4) + 8, this, NULL) ) { messagePlayer(player, MESSAGE_HINT, Language::get(707)); return false; } // play sound effect - playSoundEntity(this, 77, 64); + if ( behavior == &actDeathGhost ) + { + playSoundEntity(this, 630 + local_rng.rand() % 3, 128); + } + else + { + playSoundEntity(this, 77, 64); + } spawnPoof(x, y, 0, 1.0, true); // relocate entity @@ -10542,7 +10550,14 @@ bool Entity::teleport(int tele_x, int tele_y) } // play second sound effect - playSoundEntity(this, 77, 64); + if ( behavior == &actDeathGhost ) + { + playSoundEntity(this, 630 + local_rng.rand() % 3, 128); + } + else + { + playSoundEntity(this, 77, 64); + } const float poofx = x + cosf(yaw) * 4.f; const float poofy = y + sinf(yaw) * 4.f; spawnPoof(poofx, poofy, 0, 1.0, true); @@ -10815,7 +10830,15 @@ bool Entity::teleportAroundEntity(Entity* target, int dist, int effectType) y = (iy << 4) + 8; if ( !entityInsideSomething(this) ) { - bool onTrap = teleportCoordHasTrap(ix, iy); + bool onTrap = true; + if ( behavior == &actDeathGhost ) + { + onTrap = false; + } + else + { + onTrap = teleportCoordHasTrap(ix, iy); + } if ( !onTrap ) { forceSpot = true; @@ -10845,7 +10868,14 @@ bool Entity::teleportAroundEntity(Entity* target, int dist, int effectType) y = (iy << 4) + 8; if ( !entityInsideSomething(this) ) { - goodspots.push_back(Coord_t(ix, iy, teleportCoordHasTrap(ix, iy))); + if ( behavior == &actDeathGhost ) + { + goodspots.push_back(Coord_t(ix, iy, false)); + } + else + { + goodspots.push_back(Coord_t(ix, iy, teleportCoordHasTrap(ix, iy))); + } numlocations++; } // restore coordinates. @@ -10932,7 +10962,7 @@ bool Entity::teleportAroundEntity(Entity* target, int dist, int effectType) ty = coord.y; } } - if ( behavior == &actPlayer ) + if ( behavior == &actPlayer || behavior == &actDeathGhost ) { // pretend player has teleported, get the angle needed. real_t tmpx = x; diff --git a/src/game.cpp b/src/game.cpp index 74665d7a1..77b17cd8e 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -308,6 +308,7 @@ void TimerExperiments::updateEntityInterpolationPosition(Entity* entity) || entity->behavior == &actHudArrowModel || entity->behavior == &actLeftHandMagic || entity->behavior == &actRightHandMagic + || entity->behavior == &actCircuit || entity->behavior == &actDoor ) { entity->bUseRenderInterpolation = false; @@ -3046,7 +3047,8 @@ void gameLogic(void) if ( ticks - entity->lastupdate <= TICKS_PER_SECOND / 16 ) { // interpolate to new position - if ( entity->behavior != &actPlayerLimb || entity->skill[2] != clientnum ) + if ( (entity->behavior != &actPlayerLimb && entity->behavior != &actDeathGhostLimb) + || entity->skill[2] != clientnum ) { double ox = 0, oy = 0, onewx = 0, onewy = 0; diff --git a/src/game.hpp b/src/game.hpp index d141d5e30..b2544e787 100644 --- a/src/game.hpp +++ b/src/game.hpp @@ -229,6 +229,7 @@ void actStatue(Entity* my); void actDoorFrame(Entity* my); void actDeathCam(Entity* my); void actDeathGhost(Entity* my); +void actDeathGhostLimb(Entity* my); void actPlayerLimb(Entity* my); void actTorch(Entity* my); void actCrystalShard(Entity* my); diff --git a/src/interface/drawstatus.cpp b/src/interface/drawstatus.cpp index 846135f49..13fad22d0 100644 --- a/src/interface/drawstatus.cpp +++ b/src/interface/drawstatus.cpp @@ -48,7 +48,7 @@ void updateEnemyBar(Entity* source, Entity* target, const char* name, Sint32 hp, for (c = 0; c < MAXPLAYERS; c++) { - if (source == players[c]->entity) + if (source == players[c]->entity || source == players[c]->ghost.my ) { player = c; break; diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index 7ddf52a55..c4166849b 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -24409,6 +24409,11 @@ bool CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu: } } + if ( players[getPlayer()]->ghost.isActive() ) + { + players[getPlayer()]->ghost.createBounceAnimate(); + } + return callout.doMessage; } bool CalloutRadialMenu::createParticleCallout(real_t x, real_t y, real_t z, Uint32 uid, CalloutRadialMenu::CalloutCommand _cmd) @@ -24522,6 +24527,11 @@ bool CalloutRadialMenu::createParticleCallout(real_t x, real_t y, real_t z, Uint } } + if ( players[getPlayer()]->ghost.isActive() ) + { + players[getPlayer()]->ghost.createBounceAnimate(); + } + return callout.doMessage; } diff --git a/src/magic/actmagic.cpp b/src/magic/actmagic.cpp index 77d5e1322..c6d07f21b 100644 --- a/src/magic/actmagic.cpp +++ b/src/magic/actmagic.cpp @@ -37,6 +37,7 @@ static const char* colorForSprite(int sprite, bool darker) { case 983: case 171: return "magic_green_flicker"; case 592: + case 1244: case 172: return "magic_blue_flicker"; case 625: case 173: return "magic_purple_flicker"; @@ -58,6 +59,7 @@ static const char* colorForSprite(int sprite, bool darker) { case 983: case 171: return "magic_green"; case 592: + case 1244: case 172: return "magic_blue"; case 625: case 173: return "magic_purple"; @@ -1108,7 +1110,7 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. if ( hit.entity ) { // alert the hit entity if it was a monster - if ( hit.entity->behavior == &actMonster && parent != nullptr ) + if ( hit.entity->behavior == &actMonster && parent != nullptr && parent->behavior != &actDeathGhost ) { if ( parent->behavior == &actMagicTrap || parent->behavior == &actMagicTrapCeiling ) { @@ -1249,9 +1251,16 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. } real_t spellbookDamageBonus = (my->actmagicSpellbookBonus / 100.f); - if ( my->actmagicCastByMagicstaff == 0 && my->actmagicCastByTinkerTrap == 0 ) + if ( parent && parent->behavior == &actDeathGhost ) { - spellbookDamageBonus += getBonusFromCasterOfSpellElement(parent, nullptr, element); + // no extra bonus here + } + else + { + if ( my->actmagicCastByMagicstaff == 0 && my->actmagicCastByTinkerTrap == 0 ) + { + spellbookDamageBonus += getBonusFromCasterOfSpellElement(parent, nullptr, element); + } } if (!strcmp(element->element_internal_name, spellElement_force.element_internal_name)) @@ -2304,6 +2313,76 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. } } } + else if ( !strcmp(element->element_internal_name, spellElement_ghostBolt.element_internal_name) ) + { + if ( hit.entity ) + { + if ( hit.entity->behavior == &actMonster ) + { + Entity* parent = uidToEntity(my->parent); + real_t pushbackMultiplier = 0.6;// +(0.2 * spellbookDamageBonus); + if ( !hit.entity->isMobile() ) + { + pushbackMultiplier += 0.3; + } + + bool doSlow = true; + const int duration = TICKS_PER_SECOND * 2; + if ( hitstats ) + { + if ( hitstats->EFFECTS[EFF_SLOW] || hitstats->EFFECTS_TIMERS[EFF_SLOW] > duration ) + { + doSlow = false; + } + } + + if ( doSlow ) + { + hit.entity->setEffect(EFF_SLOW, true, duration, false); + } + + if ( hit.entity->setEffect(EFF_KNOCKBACK, true, 30, false) ) + { + if ( parent ) + { + real_t tangent = atan2(hit.entity->y - parent->y, hit.entity->x - parent->x); + hit.entity->vel_x = cos(tangent) * pushbackMultiplier; + hit.entity->vel_y = sin(tangent) * pushbackMultiplier; + hit.entity->monsterKnockbackVelocity = 0.01; + hit.entity->monsterKnockbackUID = my->parent; + hit.entity->monsterKnockbackTangentDir = tangent; + //hit.entity->lookAtEntity(*parent); + } + else + { + real_t tangent = atan2(hit.entity->y - my->y, hit.entity->x - my->x); + hit.entity->vel_x = cos(tangent) * pushbackMultiplier; + hit.entity->vel_y = sin(tangent) * pushbackMultiplier; + hit.entity->monsterKnockbackVelocity = 0.01; + hit.entity->monsterKnockbackTangentDir = tangent; + hit.entity->monsterKnockbackUID = 0; + //hit.entity->lookAtEntity(*my); + } + } + /*if ( hit.entity->monsterAttack == 0 ) + { + hit.entity->monsterHitTime = std::max(HITRATE - 12, hit.entity->monsterHitTime); + }*/ + } + else + { + //if ( parent ) + //{ + // if ( parent->behavior == &actPlayer || parent->behavior == &actDeathGhost ) + // { + // messagePlayer(parent->skill[2], MESSAGE_COMBAT, Language::get(401)); // "No telling what it did..." + // } + //} + } + + spawnMagicEffectParticles(hit.entity->x, hit.entity->y, hit.entity->z, my->sprite); + } + } else if (!strcmp(element->element_internal_name, spellElement_locking.element_internal_name)) { if ( hit.entity ) @@ -3194,6 +3273,80 @@ void actMagicParticle(Entity* my) static ConsoleVariable cvar_magic_fx_light_bonus("/magic_fx_light_bonus", 0.25f); +void actHUDMagicParticle(Entity* my) +{ + my->x += my->vel_x; + my->y += my->vel_y; + my->z += my->vel_z; + my->scalex -= 0.05; + my->scaley -= 0.05; + my->scalez -= 0.05; + if ( my->scalex <= 0 ) + { + my->scalex = 0; + my->scaley = 0; + my->scalez = 0; + list_RemoveNode(my->mynode); + return; + } +} + +void actHUDMagicParticleCircling(Entity* my) +{ + int turnRate = 4; + my->yaw += 0.2; + turnRate = 4; + my->x = my->actmagicOrbitStationaryX + my->actmagicOrbitStationaryCurrentDist * cos(my->yaw); + my->y = my->actmagicOrbitStationaryY + my->actmagicOrbitStationaryCurrentDist * sin(my->yaw); + my->actmagicOrbitStationaryCurrentDist = + std::min(my->actmagicOrbitStationaryCurrentDist + 0.5, static_cast(my->actmagicOrbitDist)); + my->z += my->vel_z * my->actmagicOrbitVerticalDirection; + + my->vel_z = std::min(my->actmagicOrbitVerticalSpeed, my->vel_z / 0.95); + my->roll += (PI / 8) / (turnRate / my->vel_z) * my->actmagicOrbitVerticalDirection; + my->roll = std::max(my->roll, -PI / 4); + + --my->actmagicOrbitLifetime; + if ( my->actmagicOrbitLifetime <= 0 ) + { + list_RemoveNode(my->mynode); + return; + } + + { + Entity* entity; + + entity = newEntity(my->sprite, 1, map.entities, nullptr); //Particle entity. + + entity->x = my->x + (local_rng.rand() % 50 - 25) / 200.f; + entity->y = my->y + (local_rng.rand() % 50 - 25) / 200.f; + entity->z = my->z + (local_rng.rand() % 50 - 25) / 200.f; + entity->scalex = 0.7; + entity->scaley = 0.7; + entity->scalez = 0.7; + entity->sizex = 1; + entity->sizey = 1; + entity->yaw = my->yaw; + entity->pitch = my->pitch; + entity->roll = my->roll; + entity->flags[NOUPDATE] = true; + entity->flags[PASSABLE] = true; + entity->flags[UNCLICKABLE] = true; + entity->flags[NOUPDATE] = true; + entity->flags[UPDATENEEDED] = false; + entity->flags[OVERDRAW] = true; + entity->lightBonus = vec4(*cvar_magic_fx_light_bonus, *cvar_magic_fx_light_bonus, + *cvar_magic_fx_light_bonus, 0.f); + entity->behavior = &actHUDMagicParticle; + entity->skill[11] = my->skill[11]; + if ( multiplayer != CLIENT ) + { + entity_uids--; + } + entity->setUID(-3); + } +} + Entity* spawnMagicParticle(Entity* parentent) { if ( !parentent ) @@ -4450,6 +4603,84 @@ void actParticleTimer(Entity* my) } } } + else if ( my->particleTimerEndAction == PARTICLE_EFFECT_GHOST_TELEPORT ) + { + // teleport to target spell. + if ( Entity* parent = uidToEntity(my->parent) ) + { + if ( my->particleTimerTarget != 0 ) + { + if ( Entity* target = uidToEntity(static_cast(my->particleTimerTarget)) ) + { + bool teleported = false; + createParticleErupt(parent, my->particleTimerEndSprite); + serverSpawnMiscParticles(parent, PARTICLE_EFFECT_ERUPT, my->particleTimerEndSprite); + teleported = parent->teleportAroundEntity(target, my->particleTimerVariable1); + if ( teleported ) + { + createParticleErupt(parent, my->particleTimerEndSprite); + // teleport success. + if ( multiplayer == SERVER ) + { + serverSpawnMiscParticles(parent, PARTICLE_EFFECT_ERUPT, my->particleTimerEndSprite); + } + } + } + } + else + { + int tx = (my->particleTimerVariable2 >> 16) & 0xFFFF; + int ty = (my->particleTimerVariable2 >> 0) & 0xFFFF; + int dist = my->particleTimerVariable1; + bool forceSpot = false; + std::vector> goodspots; + for ( int iy = std::max(1, ty - dist); !forceSpot && iy <= std::min(ty + dist, static_cast(map.height) - 1); ++iy ) + { + for ( int ix = std::max(1, tx - dist); !forceSpot && ix <= std::min(tx + dist, static_cast(map.width) - 1); ++ix ) + { + if ( !checkObstacle((ix << 4) + 8, (iy << 4) + 8, parent, NULL) ) + { + real_t tmpx = parent->x; + real_t tmpy = parent->y; + parent->x = (ix << 4) + 8; + parent->y = (iy << 4) + 8; + if ( !entityInsideSomething(parent) ) + { + if ( ix == tx && iy == ty ) + { + forceSpot = true; // directly ontop + goodspots.clear(); + } + goodspots.push_back(std::make_pair(ix, iy)); + } + // restore coordinates. + parent->x = tmpx; + parent->y = tmpy; + } + } + } + + if ( !goodspots.empty() ) + { + auto picked = goodspots.at(goodspots.size() - 1); + bool teleported = false; + createParticleErupt(parent, my->particleTimerEndSprite); + serverSpawnMiscParticles(parent, PARTICLE_EFFECT_ERUPT, my->particleTimerEndSprite); + teleported = parent->teleport(picked.first, picked.second); + if ( teleported ) + { + createParticleErupt(parent, my->particleTimerEndSprite); + // teleport success. + if ( multiplayer == SERVER ) + { + serverSpawnMiscParticles(parent, PARTICLE_EFFECT_ERUPT, my->particleTimerEndSprite); + } + } + } + } + + } + } } my->removeLightField(); list_RemoveNode(my->mynode); diff --git a/src/magic/castSpell.cpp b/src/magic/castSpell.cpp index d6229453a..0195ead91 100644 --- a/src/magic/castSpell.cpp +++ b/src/magic/castSpell.cpp @@ -334,20 +334,34 @@ Entity* castSpell(Uint32 caster_uid, spell_t* spell, bool using_magicstaff, bool if (clientnum != 0 && multiplayer == CLIENT) { - strcpy( (char*)net_packet->data, "SPEL" ); - net_packet->data[4] = clientnum; - SDLNet_Write32(spell->ID, &net_packet->data[5]); - if ( usingSpellbook ) + if ( caster->behavior == &actDeathGhost ) { - net_packet->data[9] = 1; + strcpy((char*)net_packet->data, "GHSP"); + net_packet->data[4] = clientnum; + SDLNet_Write32(spell->ID, &net_packet->data[5]); + if ( players[caster->skill[2]] ) + { + net_packet->data[9] = players[caster->skill[2]]->ghost.getSpellPower(); + } + net_packet->len = 10; } else { - net_packet->data[9] = 0; + strcpy( (char*)net_packet->data, "SPEL" ); + net_packet->data[4] = clientnum; + SDLNet_Write32(spell->ID, &net_packet->data[5]); + if ( usingSpellbook ) + { + net_packet->data[9] = 1; + } + else + { + net_packet->data[9] = 0; + } + net_packet->len = 10; } net_packet->address.host = net_server.host; net_packet->address.port = net_server.port; - net_packet->len = 10; sendPacketSafe(net_sock, -1, net_packet, 0); return NULL; } @@ -386,7 +400,7 @@ Entity* castSpell(Uint32 caster_uid, spell_t* spell, bool using_magicstaff, bool bool playerCastingFromKnownSpellbook = false; int spellBookBonusPercent = 0; int spellBookBeatitude = 0; - if ( !using_magicstaff && !trap) + if ( !using_magicstaff && !trap && stat ) { newbie = isSpellcasterBeginner(player, caster); @@ -484,7 +498,7 @@ Entity* castSpell(Uint32 caster_uid, spell_t* spell, bool using_magicstaff, bool } } - if ( newbie ) + if ( newbie && stat ) { //So This wizard is a newbie. @@ -560,7 +574,7 @@ Entity* castSpell(Uint32 caster_uid, spell_t* spell, bool using_magicstaff, bool { waterwalkingboots = true; } - if ( stat->shoes != NULL ) + if ( stat && stat->shoes != NULL ) { if (stat->shoes->type == IRON_BOOTS_WATERWALKING ) { @@ -1990,6 +2004,11 @@ Entity* castSpell(Uint32 caster_uid, spell_t* spell, bool using_magicstaff, bool traveltime = 15; missileEntity->skill[5] = traveltime; } + else if ( !strcmp(spell->spell_internal_name, spell_ghost_bolt.spell_internal_name) ) + { + traveltime = 10; + missileEntity->skill[5] = traveltime; + } int sound = spellGetCastSound(spell); if ( volume > 0 && sound > 0 ) @@ -2236,6 +2255,13 @@ Entity* castSpell(Uint32 caster_uid, spell_t* spell, bool using_magicstaff, bool missileEntity->sprite = 170; } } + else if ( !strcmp(element->element_internal_name, spellElement_ghostBolt.element_internal_name) ) + { + if ( propulsion == PROPULSION_MISSILE ) + { + missileEntity->sprite = 1244; + } + } else if ( !strcmp(element->element_internal_name, spellElement_bleed.element_internal_name) ) { if ( propulsion == PROPULSION_MISSILE ) @@ -2495,7 +2521,7 @@ Entity* castSpell(Uint32 caster_uid, spell_t* spell, bool using_magicstaff, bool } } - if ( !trap && usingSpellbook ) // degrade spellbooks on use. + if ( !trap && usingSpellbook && stat ) // degrade spellbooks on use. { int chance = 8; if ( stat->type == GOBLIN ) @@ -2589,6 +2615,10 @@ int spellGetCastSound(spell_t* spell) { return 171; } + else if ( !strcmp(spell->spell_internal_name, spell_ghost_bolt.spell_internal_name) ) + { + return 169; + } else if ( !strcmp(spell->spell_internal_name, spell_cold.spell_internal_name) ) { return 172; diff --git a/src/magic/magic.cpp b/src/magic/magic.cpp index 170783058..c668b1f03 100644 --- a/src/magic/magic.cpp +++ b/src/magic/magic.cpp @@ -78,6 +78,7 @@ void freeSpells() list_FreeAll(&spell_flutter.elements); list_FreeAll(&spell_dash.elements); list_FreeAll(&spell_polymorph.elements); + list_FreeAll(&spell_ghost_bolt.elements); } void spell_magicMap(int player) diff --git a/src/magic/magic.hpp b/src/magic/magic.hpp index 673e08e41..ec6fbb7fa 100644 --- a/src/magic/magic.hpp +++ b/src/magic/magic.hpp @@ -73,7 +73,8 @@ static const int SPELL_DASH = 51; static const int SPELL_SELF_POLYMORPH = 52; static const int SPELL_CRAB_FORM = 53; static const int SPELL_CRAB_WEB = 54; -static const int NUM_SPELLS = 55; +static const int SPELL_GHOST_BOLT = 55; +static const int NUM_SPELLS = 56; #define SPELLELEMENT_CONFUSE_BASE_DURATION 2//In seconds. @@ -145,6 +146,7 @@ static const int PARTICLE_EFFECT_PLAYER_AUTOMATON_DEATH = 23; static const int PARTICLE_EFFECT_DEVIL_SUMMON_MONSTER = 24; static const int PARTICLE_EFFECT_SHATTERED_GEM = 25; static const int PARTICLE_EFFECT_SHRINE_TELEPORT = 26; +static const int PARTICLE_EFFECT_GHOST_TELEPORT = 27; // actmagicIsVertical constants static const int MAGIC_ISVERTICAL_NONE = 0; @@ -397,6 +399,7 @@ extern spellElement_t spellElement_salvageItem; extern spellElement_t spellElement_flutter; extern spellElement_t spellElement_dash; extern spellElement_t spellElement_selfPolymorph; +extern spellElement_t spellElement_ghostBolt; /* */ //TODO: Differentiate between touch spells, enchantment spells, personal spells, ranged spells, area of effect spells, close blast/burst spells, and enemy/ally target spells. @@ -490,6 +493,7 @@ extern spell_t spell_salvageItem; extern spell_t spell_flutter; extern spell_t spell_dash; extern spell_t spell_polymorph; +extern spell_t spell_ghost_bolt; //TODO: Armor/protection/warding spells. //TODO: Targeting method? @@ -511,6 +515,8 @@ void actMagicMissile(Entity* my); void actMagicClient(Entity* my); void actMagicClientNoLight(Entity* my); void actMagicParticle(Entity* my); +void actHUDMagicParticle(Entity* my); +void actHUDMagicParticleCircling(Entity* my); Entity* spawnMagicParticle(Entity* parentent); Entity* spawnMagicParticleCustom(Entity* parentent, int sprite, real_t scale, real_t spreadReduce); void spawnMagicEffectParticles(Sint16 x, Sint16 y, Sint16 z, Uint32 sprite); diff --git a/src/magic/setupSpells.cpp b/src/magic/setupSpells.cpp index 472cf450c..6425ef88b 100644 --- a/src/magic/setupSpells.cpp +++ b/src/magic/setupSpells.cpp @@ -423,6 +423,14 @@ void setupSpells() ///TODO: Verify this function. spellElement_demonIllusion.duration = 0; strcpy(spellElement_demonIllusion.element_internal_name, "spell_element_demon_illu"); + spellElementConstructor(&spellElement_ghostBolt); + spellElement_ghostBolt.mana = 5; + spellElement_ghostBolt.base_mana = 5; + spellElement_ghostBolt.overload_multiplier = 1; + spellElement_ghostBolt.damage = 0; + spellElement_ghostBolt.duration = 75; + strcpy(spellElement_ghostBolt.element_internal_name, "spell_element_ghost_bolt"); + spellConstructor(&spell_forcebolt); strcpy(spell_forcebolt.spell_internal_name, "spell_forcebolt"); spell_forcebolt.ID = SPELL_FORCEBOLT; @@ -1292,4 +1300,26 @@ void setupSpells() ///TODO: Verify this function. node->deconstructor = &spellElementDeconstructor; element = (spellElement_t*)node->element; element->node = node; + + spellConstructor(&spell_ghost_bolt); + strcpy(spell_ghost_bolt.spell_internal_name, "spell_ghost_bolt"); + spell_ghost_bolt.ID = SPELL_GHOST_BOLT; + spell_ghost_bolt.difficulty = 100; + spell_ghost_bolt.elements.first = NULL; + spell_ghost_bolt.elements.last = NULL; + node = list_AddNodeLast(&spell_ghost_bolt.elements); + node->element = copySpellElement(&spellElement_missile); + node->size = sizeof(spellElement_t); + node->deconstructor = &spellElementDeconstructor; + element = (spellElement_t*)node->element; + element->node = node; //Tell the element what list it resides in. + //Now for the second element. + element->elements.first = NULL; + element->elements.last = NULL; + node = list_AddNodeLast(&element->elements); + node->element = copySpellElement(&spellElement_ghostBolt); + node->size = sizeof(spellElement_t); + node->deconstructor = &spellElementDeconstructor; + element = (spellElement_t*)node->element; + element->node = node; } diff --git a/src/magic/spell.cpp b/src/magic/spell.cpp index acec07a21..72c4f4d92 100644 --- a/src/magic/spell.cpp +++ b/src/magic/spell.cpp @@ -74,6 +74,7 @@ spellElement_t spellElement_salvageItem; spellElement_t spellElement_flutter; spellElement_t spellElement_dash; spellElement_t spellElement_selfPolymorph; +spellElement_t spellElement_ghostBolt; spell_t spell_forcebolt; spell_t spell_magicmissile; @@ -128,6 +129,7 @@ spell_t spell_salvageItem; spell_t spell_flutter; spell_t spell_dash; spell_t spell_polymorph; +spell_t spell_ghost_bolt; bool addSpell(int spell, int player, bool ignoreSkill) { @@ -305,6 +307,9 @@ bool addSpell(int spell, int player, bool ignoreSkill) case SPELL_CRAB_WEB: new_spell = copySpell(&spell_sprayWeb); break; + case SPELL_GHOST_BOLT: + new_spell = copySpell(&spell_ghost_bolt); + break; default: return false; } @@ -852,6 +857,9 @@ spell_t* getSpellFromID(int ID) case SPELL_CRAB_WEB: spell = &spell_sprayWeb; break; + case SPELL_GHOST_BOLT: + spell = &spell_ghost_bolt; + break; default: break; } @@ -1026,6 +1034,9 @@ int getSpellbookFromSpellID(int spellID) case SPELL_CRAB_WEB: itemType = SPELLBOOK_10; break; + case SPELL_GHOST_BOLT: + itemType = SPELLBOOK_9; + break; default: break; } diff --git a/src/maps.cpp b/src/maps.cpp index 2ce56f9c4..4bbc56915 100644 --- a/src/maps.cpp +++ b/src/maps.cpp @@ -4142,6 +4142,9 @@ void assignActions(map_t* map) } entity->behavior = &actPlayer; entity->addToCreatureList(map->creatures); + + players[numplayers]->ghost.initStartRoomLocation(entity->x / 16, entity->y / 16); + entity->x += 8; entity->y += 8; entity->z = -1; diff --git a/src/net.cpp b/src/net.cpp index f195585ac..f19383a95 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1895,9 +1895,10 @@ void clientActions(Entity* entity) entity->flags[UPDATENEEDED] = true; } entity->flags[PASSABLE] = true; - entity->flags[INVISIBLE] = false;// true; - entity->sizex = 4; - entity->sizey = 4; + entity->flags[INVISIBLE] = true; + entity->flags[GENIUS] = true; + entity->sizex = 2; + entity->sizey = 2; } break; default: @@ -2358,6 +2359,15 @@ static std::unordered_map clientPacketHandlers = { return; }}, + // ghost sneaking + { 'GHOD', []() { + const int player = std::min(net_packet->data[4], (Uint8)(MAXPLAYERS - 1)); + if ( players[player]->ghost.my ) + { + players[player]->ghost.my->skill[3] = net_packet->data[5]; + } + }}, + {'EFFE', [](){ /* * Packet breakdown: @@ -2927,24 +2937,25 @@ static std::unordered_map clientPacketHandlers = { // teleport player {'TELE', [](){ - if (players[clientnum] == nullptr || players[clientnum]->entity == nullptr) + if (players[clientnum] == nullptr || !Player::getPlayerInteractEntity(clientnum) ) { return; } int tele_x = net_packet->data[4]; int tele_y = net_packet->data[5]; Sint16 degrees = (Sint16)SDLNet_Read16(&net_packet->data[6]); - players[clientnum]->entity->yaw = degrees * PI / 180; - players[clientnum]->entity->x = (tele_x << 4) + 8; - players[clientnum]->entity->y = (tele_y << 4) + 8; - players[clientnum]->entity->bNeedsRenderPositionInit = true; - for (auto part : players[clientnum]->entity->bodyparts) { + Entity* playerEntity = Player::getPlayerInteractEntity(clientnum); + playerEntity->yaw = degrees * PI / 180; + playerEntity->x = (tele_x << 4) + 8; + playerEntity->y = (tele_y << 4) + 8; + playerEntity->bNeedsRenderPositionInit = true; + for (auto part : playerEntity->bodyparts) { part->bNeedsRenderPositionInit = true; } for (auto node = map.entities->first; node != nullptr; node = node->next) { auto entity = (Entity*)node->element; if (entity && entity->behavior == &actSpriteNametag) { - if (entity->parent == players[clientnum]->entity->getUID()) { + if (entity->parent == playerEntity->getUID()) { entity->bNeedsRenderPositionInit = true; } } @@ -2954,24 +2965,39 @@ static std::unordered_map clientPacketHandlers = { // teleport player {'TELM', [](){ - if ( players[clientnum] == nullptr || players[clientnum]->entity == nullptr ) + if ( players[clientnum] == nullptr || !Player::getPlayerInteractEntity(clientnum) ) { return; } int tele_x = net_packet->data[4]; int tele_y = net_packet->data[5]; int type = net_packet->data[6]; - players[clientnum]->entity->x = (tele_x << 4) + 8; - players[clientnum]->entity->y = (tele_y << 4) + 8; + Entity* playerEntity = Player::getPlayerInteractEntity(clientnum); + playerEntity->x = (tele_x << 4) + 8; + playerEntity->y = (tele_y << 4) + 8; + playerEntity->bNeedsRenderPositionInit = true; + for ( auto part : playerEntity->bodyparts ) { + part->bNeedsRenderPositionInit = true; + } + // play sound effect if ( type == 0 || type == 1 ) { - playSoundEntityLocal(players[clientnum]->entity, 96, 64); + playSoundEntityLocal(playerEntity, 96, 64); } else if ( type == 2 ) { - playSoundEntityLocal(players[clientnum]->entity, 154, 64); + playSoundEntityLocal(playerEntity, 154, 64); + } + for ( auto node = map.entities->first; node != nullptr; node = node->next ) { + auto entity = (Entity*)node->element; + if ( entity && entity->behavior == &actSpriteNametag ) { + if ( entity->parent == playerEntity->getUID() ) { + entity->bNeedsRenderPositionInit = true; + } + } } + temporarilyDisableDithering(); }}, // delete entity @@ -5227,6 +5253,7 @@ static std::unordered_map serverPacketHandlers = { auto vely = ((Sint16)SDLNet_Read16(&net_packet->data[12])) / 128.0; auto yaw = ((Sint16)SDLNet_Read16(&net_packet->data[14])) / 128.0; auto pitch = ((Sint16)SDLNet_Read16(&net_packet->data[16])) / 128.0; + bool bounce = ((int)net_packet->data[19] == 1) ? true : false; // update rotation players[player]->ghost.my->yaw = yaw; @@ -5273,6 +5300,27 @@ static std::unordered_map serverPacketHandlers = { // return x/y to their original state as this can update more than actPlayer and causes stuttering. use new_x/new_y in actPlayer. players[player]->ghost.my->x = ox; players[player]->ghost.my->y = oy; + + if ( bounce ) + { + players[player]->ghost.my->fskill[9] = Player::Ghost_t::GHOST_SQUISH_START_ANGLE / 100.f; + + for ( int c = 1; c < MAXPLAYERS; ++c ) // send to other players + { + if ( c == player || client_disconnected[c] || players[c]->isLocalPlayer() ) + { + continue; + } + strcpy((char*)net_packet->data, "ENFS"); + SDLNet_Write32(players[player]->ghost.my->getUID(), &net_packet->data[4]); + net_packet->data[8] = 9; + SDLNet_Write16(static_cast(players[player]->ghost.my->fskill[9] * 256), &net_packet->data[9]); + net_packet->address.host = net_clients[c - 1].host; + net_packet->address.port = net_clients[c - 1].port; + net_packet->len = 11; + sendPacketSafe(net_sock, -1, net_packet, c - 1); + } + } }}, // player created ghost @@ -5298,7 +5346,7 @@ static std::unordered_map serverPacketHandlers = { players[player]->ghost.reset(); // deathcam - int sprite = Player::Ghost_t::GHOST_MODEL_P1 + (player < 4 ? player : 4); + int sprite = Player::Ghost_t::getSpriteForPlayer(player); Entity* entity = newEntity(sprite, 1, map.entities, nullptr); //Ghost entity. players[player]->ghost.my = entity; players[player]->ghost.uid = entity->getUID(); @@ -5306,13 +5354,14 @@ static std::unordered_map serverPacketHandlers = { entity->y = (SDLNet_Read16(&net_packet->data[8]) * 16) + 8; entity->z = -4; entity->flags[PASSABLE] = true; - entity->flags[INVISIBLE] = false;// true; + entity->flags[INVISIBLE] = true; + entity->flags[GENIUS] = true; entity->behavior = &actDeathGhost; entity->skill[2] = player; entity->yaw = 0.0; entity->pitch = 0; - entity->sizex = 4; - entity->sizey = 4; + entity->sizex = 2; + entity->sizey = 2; entity->flags[UPDATENEEDED] = true; }}, @@ -5626,6 +5675,24 @@ static std::unordered_map serverPacketHandlers = { } }}, + // ghost sneaking + { 'GHOD', []() { + const int player = std::min(net_packet->data[4], (Uint8)(MAXPLAYERS - 1)); + if ( players[player]->ghost.my ) + { + players[player]->ghost.my->skill[3] = net_packet->data[5]; + for ( int c = 1; c < MAXPLAYERS; ++c ) { + // relay packet to other players + if ( client_disconnected[c] || c == player ) { + continue; + } + net_packet->address.host = net_clients[c - 1].host; + net_packet->address.port = net_clients[c - 1].port; + sendPacketSafe(net_sock, -1, net_packet, c - 1); + } + } + }}, + // close shop {'SHPC', [](){ Entity* entity = uidToEntity((Uint32)SDLNet_Read32(&net_packet->data[4])); @@ -6116,6 +6183,21 @@ static std::unordered_map serverPacketHandlers = { } }}, + //The client ghost cast a spell. + {'GHSP', []() { + const int player = std::min(net_packet->data[4], (Uint8)(MAXPLAYERS - 1)); + + spell_t* thespell = getSpellFromID(SDLNet_Read32(&net_packet->data[5])); + if ( players[player] && players[player]->ghost.isActive() ) + { + int power = net_packet->data[9]; + if ( auto projectile = castSpell(players[player]->ghost.my->getUID(), thespell, false, true) ) + { + projectile->actmagicSpellbookBonus = power * 100.0; + } + } + }}, + //The client added an item to the chest. {'CITM', [](){ const int player = std::min(net_packet->data[4], (Uint8)(MAXPLAYERS - 1)); diff --git a/src/player.cpp b/src/player.cpp index e92a021a3..e6773dc40 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -3720,7 +3720,14 @@ void Player::WorldUI_t::setTooltipActive(Entity& tooltip) } else { - interactText = Language::get(4000); // "Pick up item"; + if ( player.ghost.isActive() ) + { + interactText = Language::get(6045); // "Nudge item"; + } + else + { + interactText = Language::get(4000); // "Pick up item"; + } } } else if ( parent->behavior == &actGoldBag ) diff --git a/src/player.hpp b/src/player.hpp index 9a92f6ef9..eba21eef5 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -1762,6 +1762,14 @@ class Player real_t quickTurnRotation = 0.0; Uint32 quickTurnStartTicks = 0; bool bDoingQuickTurn = false; + bool casting = false; + Uint32 castingHeldDuration = 0; + int actionPoints = 5; + int spawnX = -1; + int spawnY = -1; + int startRoomX = -1; + int startRoomY = -1; + int teleportToPlayer = -1; Player& player; public: Ghost_t(Player& p) : player(p) @@ -1778,14 +1786,21 @@ class Player void handleGhostMovement(bool useRefreshRateDelta); void handleGhostCameraPosition(bool useRefreshRateDelta); void handleActions(); + void handleAttack(); bool isActive() { return my != nullptr; } + void initTeleportLocations(int x, int y); + void initStartRoomLocation(int x, int y); void reset(); bool allowedInteractEntity(Entity& entity); + Uint8 getSpellPower() { return std::max(1, std::min((int)castingHeldDuration / TICKS_PER_SECOND, 5)); } static const int GHOST_MODEL_P1 = 1238; static const int GHOST_MODEL_P2 = 1239; static const int GHOST_MODEL_P3 = 1240; static const int GHOST_MODEL_P4 = 1241; static const int GHOST_MODEL_PX = 1242; + static const int GHOST_SQUISH_START_ANGLE = 75; + static int getSpriteForPlayer(const int player); + void createBounceAnimate(); } ghost; class MessageZone_t From 11ae5f1ed76c1d0e5ed0801602faf127ce4c638f Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 3 Oct 2023 10:30:17 +1100 Subject: [PATCH 067/146] * sound index update --- src/entity.cpp | 4 ++-- src/interface/interface.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/entity.cpp b/src/entity.cpp index 2ae2f194f..ac8aabb93 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -10495,7 +10495,7 @@ bool Entity::teleport(int tele_x, int tele_y) // play sound effect if ( behavior == &actDeathGhost ) { - playSoundEntity(this, 630 + local_rng.rand() % 3, 128); + playSoundEntity(this, 608 + local_rng.rand() % 3, 128); } else { @@ -10552,7 +10552,7 @@ bool Entity::teleport(int tele_x, int tele_y) // play second sound effect if ( behavior == &actDeathGhost ) { - playSoundEntity(this, 630 + local_rng.rand() % 3, 128); + playSoundEntity(this, 608 + local_rng.rand() % 3, 128); } else { diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index c4166849b..cb2d44661 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -24300,9 +24300,9 @@ void CalloutRadialMenu::update() } } -int CalloutRadialMenu::CALLOUT_SFX_NEUTRAL = 612; -int CalloutRadialMenu::CALLOUT_SFX_NEGATIVE = 614; -int CalloutRadialMenu::CALLOUT_SFX_POSITIVE = 613; +int CalloutRadialMenu::CALLOUT_SFX_NEUTRAL = 605; +int CalloutRadialMenu::CALLOUT_SFX_NEGATIVE = 607; +int CalloutRadialMenu::CALLOUT_SFX_POSITIVE = 606; static ConsoleVariable cvar_callout_sfx_vol("/callout_sfx_vol", 128); bool CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu::CalloutCommand _cmd) From 96a7e83eb3201a260894da10882d78d3217821c5 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 3 Oct 2023 10:39:00 +1100 Subject: [PATCH 068/146] * lang sync --- lang/en.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lang/en.txt b/lang/en.txt index df359289c..df594dc82 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -7723,9 +7723,6 @@ Upload# 6019 Populate Hotbar# 6020 Preload Music Files# 6021 Preloading improves playback performance, but increases system memory usage. (Applied next game launch)# -6044 Show Player Callouts# -6045 Push item# - 6022 Holiday Themes# 6023 Festive Content# 6024 Toggle holiday themed content on or off as desired. Bah, humbug!# @@ -7830,5 +7827,6 @@ intro - God Rest Ye Merry Gentlemen by NaturesEye# 6043 No holiday currently active.# 6044 Show Player Callouts# +6045 Push item# 6100 end# From 7641b959348d15ed9017e25a73fb4bd3d1f4e20c Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Wed, 4 Oct 2023 07:44:20 +1100 Subject: [PATCH 069/146] * gates/doors/walls/pedestal wont trap ghosts --- src/actdoor.cpp | 3 ++- src/actgate.cpp | 3 ++- src/actpedestal.cpp | 2 +- src/actwallbuster.cpp | 6 +++++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/actdoor.cpp b/src/actdoor.cpp index 06bce8bad..d39386e07 100644 --- a/src/actdoor.cpp +++ b/src/actdoor.cpp @@ -240,7 +240,8 @@ void actDoor(Entity* my) for ( node = currentList->first; node != nullptr; node = node->next ) { Entity* entity = (Entity*)node->element; - if ( entity == my || entity->flags[PASSABLE] || entity->behavior == &actDoorFrame ) + if ( entity == my || (entity->flags[PASSABLE] && entity->behavior != &actDeathGhost) + || entity->behavior == &actDoorFrame ) { continue; } diff --git a/src/actgate.cpp b/src/actgate.cpp index e5bc8b143..41bf29d95 100644 --- a/src/actgate.cpp +++ b/src/actgate.cpp @@ -187,7 +187,8 @@ void Entity::actGate() for ( node = currentList->first; node != nullptr; node = node->next ) { Entity* entity = (Entity*)node->element; - if ( entity == this || entity->flags[PASSABLE] || entity->behavior == &actDoorFrame ) + if ( entity == this || (entity->flags[PASSABLE] && entity->behavior != &actDeathGhost) + || entity->behavior == &actDoorFrame ) { continue; } diff --git a/src/actpedestal.cpp b/src/actpedestal.cpp index b158d9e86..830c8a7f1 100644 --- a/src/actpedestal.cpp +++ b/src/actpedestal.cpp @@ -251,7 +251,7 @@ void Entity::actPedestalBase() for ( node2 = map.entities->first; node2 != nullptr; node2 = node2->next ) { Entity* entity = (Entity*)node2->element; - if ( entity == this || entity->flags[PASSABLE] + if ( entity == this || (entity->flags[PASSABLE] && entity->behavior != &actDeathGhost) || entity->behavior == &actDoorFrame || entity == orbEntity ) { continue; diff --git a/src/actwallbuster.cpp b/src/actwallbuster.cpp index 36b841947..b99309a9d 100644 --- a/src/actwallbuster.cpp +++ b/src/actwallbuster.cpp @@ -87,7 +87,11 @@ void actWallBuilder(Entity* my) for ( node_t* node = currentList->first; node != nullptr; node = node->next ) { Entity* entity = (Entity*)node->element; - if ( entity == my || entity->flags[PASSABLE] || entity->behavior == &actDoorFrame || (entity->behavior != &actMonster && entity->behavior != &actPlayer) ) + if ( entity == my || (entity->flags[PASSABLE] && entity->behavior != &actDeathGhost) + || entity->behavior == &actDoorFrame + || (entity->behavior != &actMonster + && entity->behavior != &actPlayer + && entity->behavior != &actDeathGhost) ) { continue; } From 785a9db863adedcfd63b4b8c9b7e8f4a252893bd Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Wed, 4 Oct 2023 07:45:38 +1100 Subject: [PATCH 070/146] * teleport spell for ghosts * fix casting/magic below 40 light spell interaction checking for both skills above 40 instead of one or the other --- src/magic/castSpell.cpp | 106 +++++++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 24 deletions(-) diff --git a/src/magic/castSpell.cpp b/src/magic/castSpell.cpp index 0195ead91..01ca11d6d 100644 --- a/src/magic/castSpell.cpp +++ b/src/magic/castSpell.cpp @@ -339,11 +339,7 @@ Entity* castSpell(Uint32 caster_uid, spell_t* spell, bool using_magicstaff, bool strcpy((char*)net_packet->data, "GHSP"); net_packet->data[4] = clientnum; SDLNet_Write32(spell->ID, &net_packet->data[5]); - if ( players[caster->skill[2]] ) - { - net_packet->data[9] = players[caster->skill[2]]->ghost.getSpellPower(); - } - net_packet->len = 10; + net_packet->len = 9; } else { @@ -866,7 +862,61 @@ Entity* castSpell(Uint32 caster_uid, spell_t* spell, bool using_magicstaff, bool } else if (!strcmp(element->element_internal_name, spellElement_teleportation.element_internal_name)) { - if ( caster->creatureShadowTaggedThisUid != 0 ) + if ( caster->behavior == &actDeathGhost ) + { + auto& ghost = players[caster->skill[2]]->ghost; + int tx = ghost.spawnX; + int ty = ghost.spawnY; + Entity* target = nullptr; + if ( ghost.teleportToPlayer >= MAXPLAYERS ) + { + ghost.teleportToPlayer = -1; + tx = ghost.startRoomX; + ty = ghost.startRoomY; + } + else + { + ++ghost.teleportToPlayer; + while ( ghost.teleportToPlayer >= 0 && ghost.teleportToPlayer < MAXPLAYERS ) + { + if ( ghost.teleportToPlayer != caster->skill[2] && Player::getPlayerInteractEntity(ghost.teleportToPlayer) ) + { + target = Player::getPlayerInteractEntity(ghost.teleportToPlayer); + tx = static_cast(target->x) / 16; + ty = static_cast(target->y) / 16; + break; + } + ++ghost.teleportToPlayer; + } + if ( !target ) + { + if ( ghost.teleportToPlayer >= MAXPLAYERS ) + { + tx = ghost.spawnX; + ty = ghost.spawnY; + } + } + } + + Entity* spellTimer = createParticleTimer(caster, 1, 593); + spellTimer->particleTimerPreDelay = 0; // wait x ticks before animation. + spellTimer->particleTimerEndAction = PARTICLE_EFFECT_GHOST_TELEPORT; // teleport behavior of timer. + spellTimer->particleTimerEndSprite = 593; // sprite to use for end of timer function. + spellTimer->particleTimerCountdownAction = 0; + spellTimer->particleTimerCountdownSprite = -1; + if ( target != nullptr ) + { + spellTimer->particleTimerTarget = static_cast(target->getUID()); // get the target to teleport around. + } + spellTimer->particleTimerVariable1 = 1; // distance of teleport in tiles + spellTimer->particleTimerVariable2 = (tx & 0xFFFF) << 16; + spellTimer->particleTimerVariable2 |= ty & 0xFFFF; + if ( multiplayer == SERVER ) + { + serverSpawnMiscParticles(caster, PARTICLE_EFFECT_GHOST_TELEPORT, 593); + } + } + else if ( caster->creatureShadowTaggedThisUid != 0 ) { Entity* entityToTeleport = uidToEntity(caster->creatureShadowTaggedThisUid); if ( entityToTeleport ) @@ -2482,12 +2532,19 @@ Entity* castSpell(Uint32 caster_uid, spell_t* spell, bool using_magicstaff, bool magicChance *= 2; } //messagePlayer(0, "Difficulty: %d, chance 1 in %d, 1 in %d", castDifficulty, spellCastChance, magicChance); - if ( (!strcmp(element->element_internal_name, spellElement_light.element_internal_name) || spell->ID == SPELL_REVERT_FORM) - && stat->PROFICIENCIES[PRO_SPELLCASTING] >= SKILL_LEVEL_SKILLED - && stat->PROFICIENCIES[PRO_MAGIC] >= SKILL_LEVEL_SKILLED ) + if ( (!strcmp(element->element_internal_name, spellElement_light.element_internal_name) || spell->ID == SPELL_REVERT_FORM) ) { + if ( stat->PROFICIENCIES[PRO_SPELLCASTING] >= SKILL_LEVEL_SKILLED ) + { + spellCastChance = 0; + } + if ( stat->PROFICIENCIES[PRO_MAGIC] >= SKILL_LEVEL_SKILLED ) + { + magicChance = 0; + } + // light provides no levelling past 40 in both spellcasting and magic. - if ( local_rng.rand() % 20 == 0 ) + if ( (magicChance == 0 && spellCastChance == 0) && local_rng.rand() % 20 == 0 ) { for ( int i = 0; i < MAXPLAYERS; ++i ) { @@ -2498,23 +2555,24 @@ Entity* castSpell(Uint32 caster_uid, spell_t* spell, bool using_magicstaff, bool } } } - else + + + if ( spellCastChance > 0 && (local_rng.rand() % spellCastChance == 0) ) { - if ( local_rng.rand() % spellCastChance == 0 ) - { - caster->increaseSkill(PRO_SPELLCASTING); - } + caster->increaseSkill(PRO_SPELLCASTING); + } - if ( local_rng.rand() % magicChance == 0 ) + bool magicIncreased = false; + if ( magicChance > 0 && (local_rng.rand() % magicChance == 0) ) + { + caster->increaseSkill(PRO_MAGIC); // otherwise you will basically never be able to learn all the spells in the game... + magicIncreased = true; + } + if ( magicIncreased && usingSpellbook && caster->behavior == &actPlayer ) + { + if ( stats[caster->skill[2]] && stats[caster->skill[2]]->playerRace == RACE_INSECTOID && stats[caster->skill[2]]->appearance == 0 ) { - caster->increaseSkill(PRO_MAGIC); // otherwise you will basically never be able to learn all the spells in the game... - if ( usingSpellbook && caster->behavior == &actPlayer ) - { - if ( stats[caster->skill[2]] && stats[caster->skill[2]]->playerRace == RACE_INSECTOID && stats[caster->skill[2]]->appearance == 0 ) - { - steamStatisticUpdateClient(caster->skill[2], STEAM_STAT_BOOKWORM, STEAM_STAT_INT, 1); - } - } + steamStatisticUpdateClient(caster->skill[2], STEAM_STAT_BOOKWORM, STEAM_STAT_INT, 1); } } } From 7b8c263cfacd9ffcd0a552a2ddef49475dd75ecf Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Wed, 4 Oct 2023 07:45:50 +1100 Subject: [PATCH 071/146] * sad ghost callout icons --- src/interface/interface.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index cb2d44661..e0daf8cab 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -22475,7 +22475,11 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName key = "help_all_conditions"; if ( setType == SET_CALLOUT_ICON_KEY ) { - if ( clientCalloutHelpFlags & CALLOUT_HELP_BLOOD_STARVING ) + if ( stats[player]->HP == 0 || !players[player]->entity ) + { + key = "help_deceased"; + } + else if ( clientCalloutHelpFlags & CALLOUT_HELP_BLOOD_STARVING ) { key = "condition_blood_starving"; } @@ -22522,6 +22526,11 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName return key; } + if ( stats[player]->HP == 0 || !players[player]->entity ) + { + key = "help_deceased"; + } + auto& textMap = text_map[key]; auto highlights = textMap.bannerHighlights; From 42db39c5b31492f3202734d0143acbebc046d3a4 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Wed, 4 Oct 2023 07:47:19 +1100 Subject: [PATCH 072/146] * ghost sneaking status/reticle * ghost action prompts * hp/mp/hotbar disable when ghosted * hide hotbar glyphs when dead --- src/ui/GameUI.cpp | 269 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 229 insertions(+), 40 deletions(-) diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 14b233b40..7dadeee93 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -304,6 +304,7 @@ MinotaurWarning_t minotaurWarning[MAXPLAYERS]; void updateLevelUpFrame(const int player); void updateSkillUpFrame(const int player); +void drawClockwiseSquareMesh(const char* texture, float lerp, SDL_Rect rect, Uint32 color); void capitalizeString(std::string& str) { @@ -6743,6 +6744,8 @@ void StatusEffectQueue_t::handleNavigation(std::map effectSet; @@ -6885,6 +6888,10 @@ void StatusEffectQueue_t::updateAllQueuedEffects() { effectActive = false; } + if ( !kAllowGhostStatusEffects && players[player]->ghost.isActive() ) + { + effectActive = false; + } if ( effectActive ) { @@ -6934,6 +6941,13 @@ void StatusEffectQueue_t::updateAllQueuedEffects() { miscEffects[i] = false; } + if ( players[player] && players[player]->ghost.isActive() && kAllowGhostStatusEffects ) + { + if ( players[player]->ghost.my->skill[3] == 1 ) // ghost sneaking + { + miscEffects[kEffectSneak] = true; + } + } if ( players[player] && players[player]->entity ) { if ( players[player]->entity->flags[BURNING] ) @@ -8397,23 +8411,30 @@ void Player::HUD_t::updateWorldTooltipPrompts() SDL_Rect textPos{ 0, 0, 0, 0 }; const int skillIconToGlyphPadding = 4; const int nominalGlyphHeight = 26; - - bool usingTinkeringKit = false; - bool useBracketsReticle = false; + + bool usingTinkeringKit = false; + bool useBracketsReticle = false; bool useSneakingReticle = false; - if (player.entity && stats[player.playernum]) { - if (stats[player.playernum]->defending) { - auto shield = stats[player.playernum]->shield; - if (shield && shield->type == TOOL_TINKERING_KIT) { - useBracketsReticle = true; - usingTinkeringKit = true; - } - } - if (stats[player.playernum]->sneaking) { - useBracketsReticle = true; + if ( player.entity && stats[player.playernum] ) { + if ( stats[player.playernum]->defending ) { + auto shield = stats[player.playernum]->shield; + if ( shield && shield->type == TOOL_TINKERING_KIT ) { + useBracketsReticle = true; + usingTinkeringKit = true; + } + } + if ( stats[player.playernum]->sneaking ) { + useBracketsReticle = true; useSneakingReticle = true; - } - } + } + } + else if ( player.ghost.isActive() ) + { + if ( player.ghost.my->skill[3] == 1 ) + { + useSneakingReticle = true; + } + } bool followerInteract = followerMenu.selectMoveTo && (followerMenu.optionSelected == ALLY_CMD_MOVETO_SELECT || followerMenu.optionSelected == ALLY_CMD_ATTACK_SELECT); @@ -9200,7 +9221,95 @@ void createActionPrompts(const int player) sneakText->setHJustify(Field::justify_t::CENTER); } +void drawActionPromptCooldownCallback(const Widget& widget, SDL_Rect rect) +{ + const int player = widget.getOwner(); + + const Frame* parent = static_cast(widget.getParent()); + { + SDL_Rect drawRect = rect; + drawRect.x += 22; + drawRect.y += 22; + drawRect.w = 44; + drawRect.h = 44; + real_t opacity = 192; + if ( parent && parent->getOpacity() < 100.0 ) + { + opacity *= parent->getOpacity() / 100.0; + } + const auto& ghost = players[player]->ghost; + int prompt = reinterpret_cast(widget.getUserData()) - 1; + real_t cooldownProgress = 0.0; + bool errorFlash = false; + int actionPoints = 0; + switch ( static_cast(prompt) ) + { + case Player::HUD_t::ACTION_PROMPT_MAGIC: + cooldownProgress = (ghost.cooldownTeleport / (real_t)ghost.cooldownTeleportDelay); + if ( ghost.errorFlashTeleportTicks > 0 && (Player::Ghost_t::errorFlashTicks - ghost.errorFlashTeleportTicks) % 20 < 10 ) + { + errorFlash = true; + } + break; + case Player::HUD_t::ACTION_PROMPT_OFFHAND: + if ( ghost.pushPoints > 0 ) + { + cooldownProgress = 0.0; + } + else + { + cooldownProgress = (ghost.cooldownPush / (real_t)ghost.cooldownPushDelay); + } + actionPoints = ghost.pushPoints; + if ( ghost.errorFlashPushTicks > 0 && (Player::Ghost_t::errorFlashTicks - ghost.errorFlashPushTicks) % 20 < 10 ) + { + errorFlash = true; + } + break; + case Player::HUD_t::ACTION_PROMPT_MAINHAND: + cooldownProgress = (ghost.cooldownChill / (real_t)ghost.cooldownChillDelay); + if ( ghost.errorFlashChillTicks > 0 && (Player::Ghost_t::errorFlashTicks - ghost.errorFlashChillTicks) % 20 < 10 ) + { + errorFlash = true; + } + break; + case Player::HUD_t::ACTION_PROMPT_SNEAK: + break; + default: + break; + } + if ( cooldownProgress > 0.0 ) + { + if ( errorFlash ) + { + drawClockwiseSquareMesh("images/ui/HUD/HUD_ActionPromptBacking00_02_Cooldown.png", + cooldownProgress, + drawRect, makeColor(255, 255, 255, opacity)); + } + else + { + Uint8 r, g, b, a; + getColor(hudColors.characterSheetRed, &r, &g, &b, &a); + drawClockwiseSquareMesh("images/ui/HUD/HUD_ActionPromptBacking00_02_Cooldown.png", + cooldownProgress, + drawRect, makeColor(r, g, b, opacity)); + } + } + /*if ( actionPoints > 0 ) + { + std::string str = std::to_string(actionPoints); + if ( auto textGet = Text::get(str.c_str(), smallfont_outline, makeColorRGB(255, 255, 255), 0) ) + { + textGet->drawColor(SDL_Rect{ 0,0,0,0 }, SDL_Rect{ drawRect.x - 6, drawRect.y + 1, 0, 0 }, + SDL_Rect{ 0, 0, Frame::virtualScreenX, Frame::virtualScreenY }, + makeColor(255, 255, 255, 255)); + } + }*/ + } +} + int Player::HUD_t::actionPromptOffsetX = 280; +int Player::HUD_t::actionPromptOffsetXGhostPrompts = 140; int Player::HUD_t::actionPromptOffsetY = 22; int Player::HUD_t::actionPromptBackingSize = 44; int Player::HUD_t::actionPromptIconSize = 32; @@ -9232,11 +9341,12 @@ void Player::HUD_t::updateActionPrompts() } } + bool ghostPrompts = player.ghost.isActive(); static ConsoleVariable actionPromptCompactHeightY("/actionpromptcompactheighty", 16); bShowActionPrompts = *disableActionPrompts; bShortHPMPForActionBars = false; - if ( playercount == 2 && !*MainMenu::vertical_splitscreen + if ( playercount == 2 && !*MainMenu::vertical_splitscreen && !ghostPrompts && !*disableActionPrompts && *MainMenu::clipped_splitscreen ) { bShortHPMPForActionBars = true; @@ -9250,9 +9360,12 @@ void Player::HUD_t::updateActionPrompts() || (!player.hotbar.useHotbarFaceMenu && *MainMenu::clipped_splitscreen))) || *disableActionPrompts ) { - actionPromptsFrame->setDisabled(true); - bShowActionPrompts = false; - return; + if ( !ghostPrompts ) + { + actionPromptsFrame->setDisabled(true); + bShowActionPrompts = false; + return; + } } actionPromptsFrame->setDisabled(false); actionPromptsFrame->setSize(SDL_Rect{ 0, 0, hudFrame->getSize().w, hudFrame->getSize().h }); @@ -9282,8 +9395,16 @@ void Player::HUD_t::updateActionPrompts() std::vector allPrompts; const std::string blockBinding = Input::inputs[player.playernum].binding("Defend"); const std::string sneakBinding = Input::inputs[player.playernum].binding("Sneak"); - const bool sneakingSeparateFromBlock = false;// blockBinding != sneakBinding; - if ( sneakingSeparateFromBlock ) + bool sneakingSeparateFromBlock = false;// blockBinding != sneakBinding; + if ( ghostPrompts ) + { + sneakingSeparateFromBlock = true; + allPrompts.emplace_back(PromptInfo{ "action sneak", ACTION_PROMPT_SNEAK, "Sneak" }); + allPrompts.emplace_back(PromptInfo{ "action offhand", ACTION_PROMPT_OFFHAND, "Use" }); + allPrompts.emplace_back(PromptInfo{ "action magic", ACTION_PROMPT_MAGIC, "Cast Spell" }); + allPrompts.emplace_back(PromptInfo{ "action mainhand", ACTION_PROMPT_MAINHAND, "Attack" }); + } + else if ( sneakingSeparateFromBlock ) { allPrompts.emplace_back(PromptInfo{ "action offhand", ACTION_PROMPT_OFFHAND, "Defend" }); allPrompts.emplace_back(PromptInfo{ "action sneak", ACTION_PROMPT_SNEAK, "Sneak" }); @@ -9319,7 +9440,17 @@ void Player::HUD_t::updateActionPrompts() promptPos.w = maxWidth; promptPos.h = promptHeight; int actionPromptOffsetXTotal = actionPromptOffsetX; - if ( !player.hotbar.useHotbarFaceMenu ) + prompt->setDrawCallback(nullptr); + prompt->setUserData(nullptr); + if ( ghostPrompts ) + { + actionPromptOffsetXTotal = actionPromptOffsetXGhostPrompts; + prompt->setUserData((void*)(intptr_t)(promptInfo.promptType + 1)); + prompt->setDrawCallback([](const Widget& widget, SDL_Rect rect) { + drawActionPromptCooldownCallback(widget, rect); + }); + } + else if ( !player.hotbar.useHotbarFaceMenu ) { actionPromptOffsetXTotal += 22; } @@ -9388,7 +9519,7 @@ void Player::HUD_t::updateActionPrompts() std::string textForPrompt = ""; int skillForPrompt = getActionIconForPlayer(promptInfo.promptType, textForPrompt); - if ( promptInfo.promptType == ACTION_PROMPT_OFFHAND && sneakingSeparateFromBlock + if ( promptInfo.promptType == ACTION_PROMPT_OFFHAND && sneakingSeparateFromBlock && !ghostPrompts && skillForPrompt == PRO_STEALTH ) { // offhand wants to display sneak - redundant, hide this prompt @@ -9457,6 +9588,46 @@ void Player::HUD_t::updateActionPrompts() imgBacking->path = actionPromptBackingIconPath00; } } + else if ( ghostPrompts ) + { + imgBacking->path = actionPromptBackingIconPath00; + switch ( promptInfo.promptType ) + { + case ACTION_PROMPT_MAGIC: + skillImg = "*images/ui/HUD/HUD_Ghost_Haunt.png"; + img->pos.x -= 2; + img->pos.y -= 2; + img->pos.w = 36; + img->pos.h = 36; + break; + case ACTION_PROMPT_MAINHAND: + skillImg = "*images/ui/HUD/HUD_Ghost_Chill.png"; + img->pos.x -= 2; + img->pos.y -= 2; + img->pos.w = 36; + img->pos.h = 36; + break; + case ACTION_PROMPT_OFFHAND: + skillImg = "*images/ui/HUD/HUD_Ghost_Push.png"; + img->pos.x -= 2; + img->pos.y -= 2; + img->pos.w = 36; + img->pos.h = 36; + break; + case ACTION_PROMPT_SNEAK: + for ( auto& skill : player.skillSheet.skillSheetData.skillEntries ) + { + if ( skill.skillId == PRO_STEALTH ) + { + skillImg = skill.skillIconPath32px; + break; + } + } + break; + default: + break; + } + } if ( skillImg == "" ) { prompt->setDisabled(true); @@ -9483,9 +9654,12 @@ void Player::HUD_t::updateActionPrompts() }*/ std::string bindingName = promptInfo.inputName.c_str(); - if ( skillForPrompt == PRO_STEALTH && promptInfo.promptType == ACTION_PROMPT_OFFHAND ) + if ( !ghostPrompts ) { - bindingName = "Sneak"; + if ( skillForPrompt == PRO_STEALTH && promptInfo.promptType == ACTION_PROMPT_OFFHAND ) + { + bindingName = "Sneak"; + } } glyph->path = Input::inputs[player.playernum].getGlyphPathForBinding(bindingName.c_str(), pressed); glyph->disabled = prompt->isDisabled(); @@ -9528,23 +9702,26 @@ void Player::HUD_t::updateActionPrompts() }*/ glyph->pos.y = prompt->getSize().y + img->pos.y + img->pos.h - glyphToImgPadY + pressedOffset; // just above the skill img with some padding - if ( promptInfo.promptType == ACTION_PROMPT_SNEAK ) + if ( !ghostPrompts ) { - if ( !(!stats[player.playernum]->defending && stats[player.playernum]->sneaking) ) + if ( promptInfo.promptType == ACTION_PROMPT_SNEAK ) { - //glyph->disabled = true; - img->color = iconFadedColor; - //imgBacking->color = iconFadedBackingColor; + if ( !(!stats[player.playernum]->defending && stats[player.playernum]->sneaking) ) + { + //glyph->disabled = true; + img->color = iconFadedColor; + //imgBacking->color = iconFadedBackingColor; + } } - } - if ( promptInfo.promptType == ACTION_PROMPT_OFFHAND && skillForPrompt == PRO_SHIELD - && sneakingSeparateFromBlock ) - { - if ( !stats[player.playernum]->defending ) + if ( promptInfo.promptType == ACTION_PROMPT_OFFHAND && skillForPrompt == PRO_SHIELD + && sneakingSeparateFromBlock ) { - //glyph->disabled = true; - img->color = iconFadedColor; - //imgBacking->color = iconFadedBackingColor; + if ( !stats[player.playernum]->defending ) + { + //glyph->disabled = true; + img->color = iconFadedColor; + //imgBacking->color = iconFadedBackingColor; + } } } } @@ -21810,6 +21987,10 @@ void loadHUDSettingsJSON() { Player::HUD_t::actionPromptOffsetX = d["action_prompts"]["x_offset"].GetInt(); } + if ( d["action_prompts"].HasMember("x_offset") ) + { + Player::HUD_t::actionPromptOffsetXGhostPrompts = d["action_prompts"]["x_offset_ghost_prompts"].GetInt(); + } if ( d["action_prompts"].HasMember("y_offset") ) { Player::HUD_t::actionPromptOffsetY = d["action_prompts"]["y_offset"].GetInt(); @@ -28463,6 +28644,8 @@ void Player::HUD_t::updateHPBar() hpProgressEndCapFlash->section.w = hpProgressEndCapFlash->pos.w; } } + + hpFrame->setDisabled(player.ghost.isActive()); } void Player::HUD_t::updateMPBar() @@ -28876,6 +29059,8 @@ void Player::HUD_t::updateMPBar() player.magic.noManaFeedbackTicks = 0; } } + + mpFrame->setDisabled(player.ghost.isActive()); } bool hotbar_slot_t::matchesExactLastItem(int player, Item* item) @@ -28956,7 +29141,11 @@ void Player::Hotbar_t::updateHotbar() hotbarFrame->setOpacity(hotbarFrame->getParent()->getOpacity()); bool tempHideHotbar = false; - if ( player.bUseCompactGUIHeight() + if ( player.ghost.isActive() ) + { + tempHideHotbar = true; + } + else if ( player.bUseCompactGUIHeight() && (player.gui_mode == GUI_MODE_FOLLOWERMENU || player.gui_mode == GUI_MODE_CALLOUT || (player.hud.compactLayoutMode == Player::HUD_t::COMPACT_LAYOUT_CHARSHEET && !player.shootmode) @@ -29243,7 +29432,7 @@ void Player::Hotbar_t::updateHotbar() if ( controller ) { glyph->disabled = slot->isDisabled(); - if ( !player.shootmode ) + if ( !player.shootmode || !player.entity ) { glyph->disabled = true; } From 5de136b7f844d0376e22714fdaa8e8a3dfce1558 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Wed, 4 Oct 2023 07:52:00 +1100 Subject: [PATCH 073/146] * ghost casting/attacking code more finished * can jump in out of ghost mode (press H temporarily) * hotbar navigation skip in ghost mode --- src/actplayer.cpp | 358 ++++++++++++++++++++++++++++++++-------------- src/entity.cpp | 22 ++- src/net.cpp | 28 +++- src/player.cpp | 85 ++++++++++- src/player.hpp | 38 +++-- 5 files changed, 396 insertions(+), 135 deletions(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index 28b269171..a6cb23751 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -56,6 +56,7 @@ static ConsoleVariable cvar_calloutStartZLimit("/callout_start_z_limit", #define GHOSTCAM_BOBMODE my->skill[4] #define GHOSTCAM_SQUISH_TIME my->skill[5] #define GHOSTCAM_SQUISH_DELAY my->skill[6] +#define GHOSTCAM_DEACTIVATED my->skill[7] #define GHOSTCAM_BOB my->fskill[0] #define GHOSTCAM_BOBMOVE my->fskill[1] #define GHOSTCAM_DX my->fskill[3] @@ -301,7 +302,14 @@ void Player::Ghost_t::reset() bDoingQuickTurn = false; my = nullptr; uid = 0; - + cooldownPush = 0; + cooldownPushTimeout = 0; + cooldownChill = 0; + cooldownTeleport = 0; + errorFlashPushTicks = 0; + errorFlashTeleportTicks = 0; + errorFlashChillTicks = 0; + pushPoints = MAX_PUSH_POINTS; player.cleanUpOnEntityRemoval(); } @@ -321,22 +329,108 @@ bool Player::Ghost_t::allowedInteractEntity(Entity& entity) return false; } +bool Player::Ghost_t::isActive() +{ + if ( my ) + { + if ( !GHOSTCAM_DEACTIVATED ) + { + return true; + } + } + return false; +} + void Player::Ghost_t::handleAttack() { - if ( !casting ) + if ( !isActive() ) + { + castingSpellAnimation = GHOST_SPELL_NONE; + } + if ( castingSpellAnimation == GHOST_SPELL_NONE ) { castingHeldDuration = 0; } + + if ( cooldownPush > 0 ) + { + --cooldownPush; + if ( cooldownPush == 0 ) + { + pushPoints += MAX_PUSH_POINTS; + pushPoints = std::min(pushPoints, MAX_PUSH_POINTS); + } + } + if ( pushPoints < MAX_PUSH_POINTS && cooldownPush == 0 ) + { + cooldownPush = TICKS_PER_SECOND; + } + + if ( cooldownChill > 0 ) + { + --cooldownChill; + } + if ( cooldownTeleport > 0 ) + { + --cooldownTeleport; + } + if ( errorFlashChillTicks > 0 ) + { + --errorFlashChillTicks; + } + if ( errorFlashPushTicks > 0 ) + { + --errorFlashPushTicks; + } + if ( errorFlashTeleportTicks > 0 ) + { + --errorFlashTeleportTicks; + } + Input& input = Input::inputs[player.playernum]; bool attack = false; + bool useSpell = false; bool defending = false; if ( !player.usingCommand() && player.bControlEnabled && !gamePaused ) { - if ( player.shootmode && input.binaryToggle("Attack") ) + if ( player.shootmode ) { - attack = true; + if ( input.consumeBinaryToggle("Attack") ) + { + if ( cooldownChill > 0 ) + { + // no action + if ( errorFlashChillTicks == 0 && cooldownChill > (TICKS_PER_SECOND / 2) + && (cooldownChillDelay - cooldownChill > (TICKS_PER_SECOND / 2)) ) + { + errorFlashChillTicks = errorFlashTicks; + playSound(563, 64); + } + } + else + { + attack = true; + } + } + if ( input.consumeBinaryToggle("Cast Spell") ) + { + if ( cooldownTeleport > 0 ) + { + // no action + if ( errorFlashTeleportTicks == 0 && cooldownTeleport > (TICKS_PER_SECOND / 2) + && (cooldownTeleportDelay - cooldownTeleport > (TICKS_PER_SECOND / 2)) ) + { + errorFlashTeleportTicks = errorFlashTicks; + playSound(563, 64); + } + } + else + { + useSpell = true; + } + } } if ( input.binary("Defend") ) { @@ -377,124 +471,101 @@ void Player::Ghost_t::handleAttack() } GHOSTCAM_SNEAKING = defending; - const int castLoopDuration = 20; - const int teleportLoopDuration = 1; - bool pushSpell = true; - if ( !attack ) + const int castLoopDuration = 4 * TICKS_PER_SECOND / 10; + bool finishSpell = false; + if ( castingSpellAnimation == GHOST_SPELL_BOLT ) { - if ( casting && castingHeldDuration >= castLoopDuration ) + if ( castingHeldDuration >= castLoopDuration ) { - if ( !pushSpell ) - { - int tx = spawnX; - int ty = spawnY; - Entity* target = nullptr; - if ( teleportToPlayer >= MAXPLAYERS ) - { - teleportToPlayer = -1; - tx = startRoomX; - ty = startRoomY; - } - else - { - ++teleportToPlayer; - while ( teleportToPlayer >= 0 && teleportToPlayer < MAXPLAYERS ) - { - if ( teleportToPlayer != player.playernum && Player::getPlayerInteractEntity(teleportToPlayer) ) - { - target = Player::getPlayerInteractEntity(teleportToPlayer); - tx = static_cast(target->x) / 16; - ty = static_cast(target->y) / 16; - break; - } - ++teleportToPlayer; - } - if ( !target ) - { - if ( teleportToPlayer >= MAXPLAYERS ) - { - tx = spawnX; - ty = spawnY; - } - } - } + finishSpell = true; + } + } + else if ( castingSpellAnimation == GHOST_SPELL_TELEPORT ) + { + if ( castingHeldDuration >= castLoopDuration * 2 ) + { + finishSpell = true; + } + } - Entity* spellTimer = createParticleTimer(my, 1, 593); - spellTimer->particleTimerPreDelay = 0; // wait x ticks before animation. - spellTimer->particleTimerEndAction = PARTICLE_EFFECT_GHOST_TELEPORT; // teleport behavior of timer. - spellTimer->particleTimerEndSprite = 593; // sprite to use for end of timer function. - spellTimer->particleTimerCountdownAction = 0; - spellTimer->particleTimerCountdownSprite = -1; - if ( target != nullptr ) - { - spellTimer->particleTimerTarget = static_cast(target->getUID()); // get the target to teleport around. - } - spellTimer->particleTimerVariable1 = 1; // distance of teleport in tiles - spellTimer->particleTimerVariable2 = (tx & 0xFFFF) << 16; - spellTimer->particleTimerVariable2 |= ty & 0xFFFF; - if ( multiplayer == SERVER ) - { - serverSpawnMiscParticles(my, PARTICLE_EFFECT_GHOST_TELEPORT, 593); - } - } - else - { - if ( auto projectile = castSpell(uid, &spell_ghost_bolt, false, true) ) - { - projectile->actmagicSpellbookBonus = getSpellPower() * 100.0; - } - } - casting = false; - castingHeldDuration = 0; + if ( finishSpell ) + { + if ( castingSpellAnimation == GHOST_SPELL_TELEPORT ) + { + castSpell(uid, &spell_teleportation, false, true); + castingSpellAnimation = GHOST_SPELL_NONE; + } + else if ( castingSpellAnimation == GHOST_SPELL_BOLT ) + { + castSpell(uid, &spell_ghost_bolt, false, true); + castingSpellAnimation = GHOST_SPELL_POST_CASTING; + } + castingHeldDuration = 0; - for ( int i = 0; i < 5; ++i ) - { - Entity* entity = spawnGib(my); - entity->flags[INVISIBLE] = false; - entity->flags[SPRITE] = true; - entity->flags[NOUPDATE] = true; - entity->flags[UPDATENEEDED] = false; - entity->flags[OVERDRAW] = true; - entity->lightBonus = vec4(0.2f, 0.2f, 0.2f, 0.f); - real_t scale = 0.15f; - entity->scalex = scale; - entity->scaley = scale; - entity->scalez = scale; - entity->sprite = 16; - entity->x = 8; - entity->y = 0; - entity->z = (cameras[player.playernum].z * .5 - my->z) + 7 + -2; - entity->z -= 4.75; - entity->z = local_rng.uniform(entity->z, entity->z - 4); - entity->z += 5.0; - entity->yaw = -cameravars[player.playernum].shakex2; - entity->yaw = ((local_rng.rand() % 6) * 60) * PI / 180.0; - entity->pitch = (local_rng.rand() % 360) * PI / 180.0; - entity->roll = (local_rng.rand() % 360) * PI / 180.0; - entity->vel_x = cos(entity->yaw) * .1; - entity->vel_y = sin(entity->yaw) * .1; - entity->vel_z = -.15; - entity->fskill[3] = 0.01; - entity->skill[11] = player.playernum; - } - return; + for ( int i = 0; i < 5; ++i ) + { + Entity* entity = spawnGib(my); + entity->flags[INVISIBLE] = false; + entity->flags[SPRITE] = true; + entity->flags[NOUPDATE] = true; + entity->flags[UPDATENEEDED] = false; + entity->flags[OVERDRAW] = true; + entity->lightBonus = vec4(0.2f, 0.2f, 0.2f, 0.f); + real_t scale = 0.15f; + entity->scalex = scale; + entity->scaley = scale; + entity->scalez = scale; + entity->sprite = 16; + entity->x = 8; + entity->y = 0; + entity->z = (cameras[player.playernum].z * .5 - my->z) + 7 + -2; + entity->z -= 4.75; + entity->z = local_rng.uniform(entity->z, entity->z - 4); + entity->z += 5.0; + entity->yaw = -cameravars[player.playernum].shakex2; + entity->yaw = ((local_rng.rand() % 6) * 60) * PI / 180.0; + entity->pitch = (local_rng.rand() % 360) * PI / 180.0; + entity->roll = (local_rng.rand() % 360) * PI / 180.0; + entity->vel_x = cos(entity->yaw) * .1; + entity->vel_y = sin(entity->yaw) * .1; + entity->vel_z = -.15; + entity->fskill[3] = 0.01; + entity->skill[11] = player.playernum; } + return; } - if ( !casting ) + if ( castingSpellAnimation == GHOST_SPELL_POST_CASTING ) + { + if ( !attack ) + { + castingSpellAnimation = GHOST_SPELL_NONE; + } + } + + if ( castingSpellAnimation == GHOST_SPELL_NONE ) { if ( attack ) { - casting = true; + castingSpellAnimation = GHOST_SPELL_BOLT; + cooldownChill = cooldownChillDelay; + errorFlashChillTicks = 0; + playSoundEntity(my, 170, 128); + } + else if ( useSpell ) + { + castingSpellAnimation = GHOST_SPELL_TELEPORT; + cooldownTeleport = cooldownTeleportDelay; + errorFlashTeleportTicks = 0; playSoundEntity(my, 170, 128); } } - else + else if ( castingSpellAnimation == GHOST_SPELL_TELEPORT || castingSpellAnimation == GHOST_SPELL_BOLT ) { ++castingHeldDuration; - if ( !pushSpell ) + if ( castingSpellAnimation == GHOST_SPELL_TELEPORT ) { float x = 6; float y = 0.1; @@ -567,7 +638,7 @@ void Player::Ghost_t::handleAttack() entity->skill[11] = player.playernum; } } - else + else if ( castingSpellAnimation == GHOST_SPELL_BOLT ) { float x = 6; float y = 0.1; @@ -963,9 +1034,23 @@ void Player::Ghost_t::handleActions() } } - if ( selectedEntity[player.playernum] != NULL ) + if ( selectedEntity[player.playernum] != nullptr ) { Entity* parent = uidToEntity(selectedEntity[player.playernum]->skill[2]); + if ( selectedEntity[player.playernum]->behavior == &actItem && pushPoints == 0 ) + { + selectedEntity[player.playernum] = nullptr; + input.consumeBinaryToggle("Use"); + if ( cooldownPush > 0 ) + { + // no action + if ( errorFlashPushTicks == 0 && cooldownPush > (TICKS_PER_SECOND / 2) ) + { + errorFlashPushTicks = errorFlashTicks; + playSound(563, 64); + } + } + } if ( selectedEntity[player.playernum] ) { input.consumeBinaryToggle("Use"); @@ -973,6 +1058,15 @@ void Player::Ghost_t::handleActions() if ( entityDist(my, selectedEntity[player.playernum]) <= TOUCHRANGE ) { inrange[player.playernum] = true; + if ( selectedEntity[player.playernum]->behavior == &actItem ) + { + --pushPoints; + pushPoints = std::max(0, pushPoints); + if ( pushPoints == 0 ) + { + cooldownPush = cooldownPushDelay; + } + } } else { @@ -1229,6 +1323,10 @@ void Player::Ghost_t::handleGhostCameraUpdate(const bool useRefreshRateDelta) } } +Uint32 Player::Ghost_t::cooldownPushDelay = TICKS_PER_SECOND * 3; +Uint32 Player::Ghost_t::cooldownChillDelay = TICKS_PER_SECOND * 3; +Uint32 Player::Ghost_t::cooldownTeleportDelay = TICKS_PER_SECOND * 3; + int Player::Ghost_t::getSpriteForPlayer(const int player) { if ( !colorblind_lobby ) @@ -1293,6 +1391,22 @@ void Player::Ghost_t::initTeleportLocations(int x, int y) } } +void Player::Ghost_t::setActive(bool active) +{ + if ( my ) + { + Uint32 deactivated = (active ? 0 : 1); + if ( deactivated != GHOSTCAM_DEACTIVATED ) + { + GHOSTCAM_DEACTIVATED = deactivated; + if ( multiplayer == SERVER && player.isLocalPlayer() ) + { + serverUpdateEntitySkill(my, 7); + } + } + } +} + void actDeathGhost(Entity* my) { int playernum = GHOSTCAM_PLAYERNUM; @@ -1354,7 +1468,11 @@ void actDeathGhost(Entity* my) ambientLight = true; } - if ( !ambientLight ) + if ( !player->ghost.isActive() ) + { + // no light + } + else if ( !ambientLight ) { switch ( my->sprite ) { @@ -1403,7 +1521,7 @@ void actDeathGhost(Entity* my) static ConsoleVariable cvar_ghostSquish("/ghost_squish", 6.f); static ConsoleVariable cvar_ghostSquishFactor("/ghost_squish_factor", 0.3f); - if ( player->isLocalPlayer() ) + if ( player->isLocalPlayer() && player->ghost.isActive() ) { if ( autoLimbReload && ticks % 20 == 0 && (playernum == clientnum) ) { @@ -1428,6 +1546,11 @@ void actDeathGhost(Entity* my) camvang = my->pitch; static ConsoleVariable cvar_ghostThirdPerson("/ghost_thirdperson", false); + if ( keystatus[SDLK_h] ) + { + keystatus[SDLK_h] = 0; + player->ghost.setActive(false); + } if ( *cvar_ghostThirdPerson || !keystatus[SDLK_g] ) { camx -= cos(my->yaw) * cos(my->pitch) * 1.5; @@ -1544,6 +1667,10 @@ void actDeathGhost(Entity* my) if ( multiplayer == CLIENT ) { net_packet->data[19] = bounceAnimate ? 1 : 0; + if ( GHOSTCAM_DEACTIVATED ) + { + net_packet->data[19] |= (1 << 1); + } net_packet->address.host = net_server.host; net_packet->address.port = net_server.port; net_packet->len = 20; @@ -1670,6 +1797,7 @@ void actDeathGhost(Entity* my) bodypart->scalex = 1.0 - squish; bodypart->scaley = 1.0 - squish; bodypart->scalez = 1.0 + squish; + bodypart->flags[INVISIBLE] = !player->ghost.isActive(); if ( index == 0 ) { @@ -1761,7 +1889,7 @@ void actDeathCam(Entity* my) } bool shootmode = players[DEATHCAM_PLAYERNUM]->shootmode; - if ( shootmode && !gamePaused && DEATHCAM_CREATEDGHOST == 0 ) + if ( shootmode && !gamePaused && !players[DEATHCAM_PLAYERNUM]->ghost.isActive() ) { if ( !players[DEATHCAM_PLAYERNUM]->GUI.isGameoverActive() ) { @@ -1894,6 +2022,14 @@ void actDeathCam(Entity* my) && (Input::inputs[DEATHCAM_PLAYERNUM].consumeBinaryToggle("Attack") || Input::inputs[DEATHCAM_PLAYERNUM].consumeBinaryToggle("MenuConfirm")) ) { + if ( DEATHCAM_CREATEDGHOST == 1 ) + { + if ( players[DEATHCAM_PLAYERNUM]->ghost.my ) + { + players[DEATHCAM_PLAYERNUM]->ghost.setActive(true); + } + } + if ( DEATHCAM_CREATEDGHOST == 0 ) { DEATHCAM_CREATEDGHOST = 1; @@ -1981,7 +2117,7 @@ void actDeathCam(Entity* my) my->removeLightField(); - if ( DEATHCAM_CREATEDGHOST != 0 ) + if ( DEATHCAM_CREATEDGHOST != 0 && players[DEATHCAM_PLAYERNUM]->ghost.isActive() ) { return; } diff --git a/src/entity.cpp b/src/entity.cpp index ac8aabb93..aeb393a2d 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -10484,6 +10484,10 @@ bool Entity::teleport(int tele_x, int tele_y) return false; } } + else if ( behavior == &actDeathGhost ) + { + player = skill[2]; + } if ( (strstr(map.name, "Minotaur") && behavior != &actDeathGhost) || checkObstacle((tele_x << 4) + 8, (tele_y << 4) + 8, this, NULL) ) @@ -10493,13 +10497,15 @@ bool Entity::teleport(int tele_x, int tele_y) } // play sound effect + int sfx = 77; if ( behavior == &actDeathGhost ) { - playSoundEntity(this, 608 + local_rng.rand() % 3, 128); + sfx = 608 + local_rng.rand() % 3; + playSoundEntity(this, sfx, 128); } else { - playSoundEntity(this, 77, 64); + playSoundEntity(this, sfx, 64); } spawnPoof(x, y, 0, 1.0, true); @@ -10523,7 +10529,7 @@ bool Entity::teleport(int tele_x, int tele_y) { TileEntityList.updateEntity(*this); } - if ( player > 0 && multiplayer == SERVER && !players[player]->isLocalPlayer() ) + if ( multiplayer == SERVER && player > 0 && !players[player]->isLocalPlayer() ) { strcpy((char*)net_packet->data, "TELE"); net_packet->data[4] = tele_x; @@ -10552,11 +10558,11 @@ bool Entity::teleport(int tele_x, int tele_y) // play second sound effect if ( behavior == &actDeathGhost ) { - playSoundEntity(this, 608 + local_rng.rand() % 3, 128); + playSoundEntity(this, sfx, 128); } else { - playSoundEntity(this, 77, 64); + playSoundEntity(this, sfx, 64); } const float poofx = x + cosf(yaw) * 4.f; const float poofy = y + sinf(yaw) * 4.f; @@ -10610,8 +10616,8 @@ bool Entity::teleportRandom() messagePlayerColor(player, MESSAGE_HINT, color, Language::get(2381)); return false; } - } + for ( int iy = 1; iy < map.height; ++iy ) { for ( int ix = 1; ix < map.width; ++ix ) @@ -10724,6 +10730,10 @@ bool Entity::teleportAroundEntity(Entity* target, int dist, int effectType) return false; } } + else if ( behavior == &actDeathGhost ) + { + player = skill[2]; + } struct Coord_t { diff --git a/src/net.cpp b/src/net.cpp index e457f6eea..4c6fee7ed 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -5257,7 +5257,8 @@ static std::unordered_map serverPacketHandlers = { auto vely = ((Sint16)SDLNet_Read16(&net_packet->data[12])) / 128.0; auto yaw = ((Sint16)SDLNet_Read16(&net_packet->data[14])) / 128.0; auto pitch = ((Sint16)SDLNet_Read16(&net_packet->data[16])) / 128.0; - bool bounce = ((int)net_packet->data[19] == 1) ? true : false; + bool bounce = ((int)(net_packet->data[19] & 1) == 1) ? true : false; + int deactivated = ((int)((net_packet->data[19] >> 1) & 1) == 1) ? 1 : 0; // update rotation players[player]->ghost.my->yaw = yaw; @@ -5325,6 +5326,25 @@ static std::unordered_map serverPacketHandlers = { sendPacketSafe(net_sock, -1, net_packet, c - 1); } } + if ( deactivated != players[player]->ghost.my->skill[7] ) + { + players[player]->ghost.setActive(deactivated == 0 ? true : false); + for ( int c = 1; c < MAXPLAYERS; ++c ) // send to other players + { + if ( c == player || client_disconnected[c] || players[c]->isLocalPlayer() ) + { + continue; + } + strcpy((char*)net_packet->data, "ENTS"); + SDLNet_Write32(players[player]->ghost.my->getUID(), &net_packet->data[4]); + net_packet->data[8] = 7; + SDLNet_Write32(players[player]->ghost.my->skill[7], &net_packet->data[9]); + net_packet->address.host = net_clients[c - 1].host; + net_packet->address.port = net_clients[c - 1].port; + net_packet->len = 13; + sendPacketSafe(net_sock, -1, net_packet, c - 1); + } + } }}, // player created ghost @@ -6194,11 +6214,7 @@ static std::unordered_map serverPacketHandlers = { spell_t* thespell = getSpellFromID(SDLNet_Read32(&net_packet->data[5])); if ( players[player] && players[player]->ghost.isActive() ) { - int power = net_packet->data[9]; - if ( auto projectile = castSpell(players[player]->ghost.my->getUID(), thespell, false, true) ) - { - projectile->actmagicSpellbookBonus = power * 100.0; - } + castSpell(players[player]->ghost.my->getUID(), thespell, false, true); } }}, diff --git a/src/player.cpp b/src/player.cpp index e6773dc40..5d755df58 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1375,6 +1375,10 @@ Player::GUI_t::GUIModules Player::GUI_t::handleModuleNavigation(bool checkDestin } else if ( inputs.getUIInteraction(player.playernum)->selectedItem || bCompactView ) { + if ( player.ghost.isActive() ) + { + return MODULE_NONE; + } if ( !checkDestinationOnly ) { activateModule(MODULE_HOTBAR); @@ -1407,7 +1411,7 @@ Player::GUI_t::GUIModules Player::GUI_t::handleModuleNavigation(bool checkDestin else if ( activeModule == MODULE_SPELLS && (player.inventoryUI.spellPanel.bFirstTimeSnapCursor || checkDestinationOnly) ) { - if ( player.shopGUI.bOpen || player.inventoryUI.chestGUI.bOpen ) + if ( player.shopGUI.bOpen || player.inventoryUI.chestGUI.bOpen || player.ghost.isActive() ) { return MODULE_NONE; } @@ -1593,10 +1597,42 @@ Player::GUI_t::GUIModules Player::GUI_t::handleModuleNavigation(bool checkDestin warpControllerToModule(false); input.consumeBinaryToggle("UINavLeftBumper"); inputs.getVirtualMouse(player.playernum)->draw_cursor = false; - soundModuleNavigation(); + if ( !player.ghost.isActive() ) + { + soundModuleNavigation(); + } } return MODULE_CHARACTERSHEET; } + else if ( player.ghost.isActive() ) + { + if ( player.inventory_mode == INVENTORY_MODE_SPELL + && (player.inventoryUI.spellPanel.bFirstTimeSnapCursor || checkDestinationOnly) ) + { + if ( !checkDestinationOnly ) + { + activateModule(MODULE_SPELLS); + warpControllerToModule(false); + input.consumeBinaryToggle("UINavLeftBumper"); + inputs.getVirtualMouse(player.playernum)->draw_cursor = false; + soundModuleNavigation(); + } + return MODULE_SPELLS; + } + else if ( player.inventory_mode == INVENTORY_MODE_ITEM + && (player.inventoryUI.bFirstTimeSnapCursor || checkDestinationOnly) ) + { + if ( !checkDestinationOnly ) + { + activateModule(MODULE_INVENTORY); + warpControllerToModule(false); + input.consumeBinaryToggle("UINavLeftBumper"); + inputs.getVirtualMouse(player.playernum)->draw_cursor = false; + soundModuleNavigation(); + } + return MODULE_INVENTORY; + } + } else if ( !checkDestinationOnly ) { activateModule(MODULE_HOTBAR); @@ -1657,6 +1693,27 @@ Player::GUI_t::GUIModules Player::GUI_t::handleModuleNavigation(bool checkDestin { return MODULE_NONE; } + else if ( player.ghost.isActive() ) + { + if ( bCompactView ) + { + return MODULE_NONE; + } + if ( !checkDestinationOnly ) + { + activateModule(MODULE_CHARACTERSHEET); + if ( player.characterSheet.selectedElement == Player::CharacterSheet_t::SHEET_UNSELECTED + || !player.characterSheet.isSheetElementAllowedToNavigateTo(player.characterSheet.selectedElement) ) + { + player.characterSheet.selectElement(Player::CharacterSheet_t::SHEET_OPEN_MAP, false, false); + } + warpControllerToModule(false); + input.consumeBinaryToggle("UINavRightBumper"); + inputs.getVirtualMouse(player.playernum)->draw_cursor = false; + soundModuleNavigation(); + } + return MODULE_CHARACTERSHEET; + } else if ( GenericGUI[player.playernum].alchemyGUI.bOpen ) { if ( inputs.getUIInteraction(player.playernum)->selectedItem ) @@ -1711,7 +1768,7 @@ Player::GUI_t::GUIModules Player::GUI_t::handleModuleNavigation(bool checkDestin else if ( activeModule == MODULE_SPELLS && (player.inventoryUI.spellPanel.bFirstTimeSnapCursor || checkDestinationOnly ) ) { - if ( player.shopGUI.bOpen || player.inventoryUI.chestGUI.bOpen ) + if ( player.shopGUI.bOpen || player.inventoryUI.chestGUI.bOpen || player.ghost.isActive() ) { return MODULE_NONE; } @@ -4784,6 +4841,28 @@ Player::Hotbar_t::FaceMenuGroup Player::Hotbar_t::getFaceMenuGroupForSlot(int ho const int Player::HUD_t::getActionIconForPlayer(ActionPrompts prompt, std::string& promptString) const { + if ( player.ghost.isActive() ) + { + switch ( prompt ) + { + case ACTION_PROMPT_MAGIC: + promptString = Language::get(6047); // Haunt + break; + case ACTION_PROMPT_MAINHAND: + promptString = Language::get(6046); // Chill + break; + case ACTION_PROMPT_OFFHAND: + promptString = Language::get(6048); // Push + break; + case ACTION_PROMPT_SNEAK: + promptString = Language::get(4077); // Sneak + break; + default: + break; + } + return -1; + } + if ( prompt == ACTION_PROMPT_MAGIC ) { promptString = Language::get(4078); diff --git a/src/player.hpp b/src/player.hpp index eba21eef5..73b4b0b15 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -1648,6 +1648,7 @@ class Player const int ENEMYBAR_FRAME_START_Y = 182; const int ENEMYBAR_FRAME_HEIGHT = 44; static int actionPromptOffsetX; + static int actionPromptOffsetXGhostPrompts; static int actionPromptOffsetY; static int actionPromptBackingSize; static int actionPromptIconSize; @@ -1762,14 +1763,15 @@ class Player real_t quickTurnRotation = 0.0; Uint32 quickTurnStartTicks = 0; bool bDoingQuickTurn = false; - bool casting = false; + enum GhostSpells_t : int + { + GHOST_SPELL_NONE, + GHOST_SPELL_TELEPORT, + GHOST_SPELL_BOLT, + GHOST_SPELL_POST_CASTING + }; + GhostSpells_t castingSpellAnimation = GHOST_SPELL_NONE; Uint32 castingHeldDuration = 0; - int actionPoints = 5; - int spawnX = -1; - int spawnY = -1; - int startRoomX = -1; - int startRoomY = -1; - int teleportToPlayer = -1; Player& player; public: Ghost_t(Player& p) : player(p) @@ -1777,7 +1779,25 @@ class Player ~Ghost_t() {}; Entity* my = nullptr; + int spawnX = -1; + int spawnY = -1; + int startRoomX = -1; + int startRoomY = -1; + int teleportToPlayer = -1; Uint32 uid = 0; + Uint32 cooldownPush = 0; + Uint32 cooldownPushTimeout = 0; + Uint32 cooldownChill = 0; + Uint32 cooldownTeleport = 0; + Uint32 errorFlashPushTicks = 0; + Uint32 errorFlashTeleportTicks = 0; + Uint32 errorFlashChillTicks = 0; + static const int errorFlashTicks = TICKS_PER_SECOND * 2.5; + static const int MAX_PUSH_POINTS = 5; + int pushPoints = MAX_PUSH_POINTS; + static Uint32 cooldownPushDelay; + static Uint32 cooldownChillDelay; + static Uint32 cooldownTeleportDelay; bool handleQuickTurn(bool useRefreshRateDelta); void startQuickTurn(); @@ -1787,12 +1807,12 @@ class Player void handleGhostCameraPosition(bool useRefreshRateDelta); void handleActions(); void handleAttack(); - bool isActive() { return my != nullptr; } + bool isActive(); + void setActive(bool active); void initTeleportLocations(int x, int y); void initStartRoomLocation(int x, int y); void reset(); bool allowedInteractEntity(Entity& entity); - Uint8 getSpellPower() { return std::max(1, std::min((int)castingHeldDuration / TICKS_PER_SECOND, 5)); } static const int GHOST_MODEL_P1 = 1238; static const int GHOST_MODEL_P2 = 1239; static const int GHOST_MODEL_P3 = 1240; From f1ad32c9252e019fded3d327f93e52f8100e9286 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Wed, 4 Oct 2023 07:52:11 +1100 Subject: [PATCH 074/146] * new sound for polymorphing ending --- src/entity.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/entity.cpp b/src/entity.cpp index aeb393a2d..b6e604a34 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -1162,7 +1162,7 @@ void Entity::effectTimes() { if ( myStats->EFFECTS_TIMERS[c] == TICKS_PER_SECOND * 15 ) { - //playSoundPlayer(player, 32, 128); + playSoundPlayer(player, 611, 192); messagePlayer(player, MESSAGE_STATUS, Language::get(3193)); } } @@ -1170,7 +1170,7 @@ void Entity::effectTimes() { if ( myStats->EFFECTS_TIMERS[c] == TICKS_PER_SECOND * 15 ) { - playSoundPlayer(player, 32, 128); + playSoundPlayer(player, 611, 192); messagePlayer(player, MESSAGE_STATUS, Language::get(3475)); } } From fe1240b89897f62e778c32a53bae46a0decd0040 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Wed, 4 Oct 2023 07:52:51 +1100 Subject: [PATCH 075/146] * fix EOS crash - RTCOptions set to null as we're not using the feature rather than a struct with null in it --- src/eos.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/eos.cpp b/src/eos.cpp index b355f38d4..4536db531 100644 --- a/src/eos.cpp +++ b/src/eos.cpp @@ -1233,11 +1233,11 @@ bool EOSFuncs::initPlatform(bool enableLogging) PlatformOptions.CacheDirectory = nullptr; // important - needs double slashes and absolute path PlatformOptions.TickBudgetInMilliseconds = 0; // do all available work - EOS_Platform_RTCOptions rtcOptions{}; - rtcOptions.ApiVersion = EOS_PLATFORM_RTCOPTIONS_API_LATEST; - rtcOptions.BackgroundMode = EOS_ERTCBackgroundMode::EOS_RTCBM_KeepRoomsAlive; - rtcOptions.PlatformSpecificOptions = nullptr; // must be null initialized - PlatformOptions.RTCOptions = &rtcOptions; + //EOS_Platform_RTCOptions rtcOptions{}; + //rtcOptions.ApiVersion = EOS_PLATFORM_RTCOPTIONS_API_LATEST; + //rtcOptions.BackgroundMode = EOS_ERTCBackgroundMode::EOS_RTCBM_KeepRoomsAlive; + //rtcOptions.PlatformSpecificOptions = nullptr; // must be null initialized + PlatformOptions.RTCOptions = nullptr; PlatformOptions.IntegratedPlatformOptionsContainerHandle = nullptr; // must be null initialized PlatformOptions.SystemSpecificOptions = nullptr; // must be null initialized From 0cd8d7c5a595b5b6a03f91362a2931e543655a13 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Wed, 4 Oct 2023 07:53:21 +1100 Subject: [PATCH 076/146] * fix model cache max faces not high enough on windows? --- src/files.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/files.cpp b/src/files.cpp index fcc01dad4..c73be50b8 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -3156,7 +3156,7 @@ static ConsoleCommand ccmd_writeModelCache("/write_model_cache", "", void generatePolyModels(int start, int end, bool forceCacheRebuild) { const bool generateAll = start == 0 && end == nummodels; - constexpr auto LARGEST_POLYMODEL_FACES_ALLOWED = (1<<15); // 32768 + constexpr auto LARGEST_POLYMODEL_FACES_ALLOWED = (1<<17); // 131072 if ( generateAll ) { From 0eea23d1703de5215677a25e36b678a630523e3c Mon Sep 17 00:00:00 2001 From: SheridanR Date: Tue, 3 Oct 2023 19:09:47 -0700 Subject: [PATCH 077/146] ghost fog, normals for voxels, color filter on viewports --- src/draw.cpp | 103 +++++++++++++++++++++++++------ src/draw.hpp | 9 ++- src/files.cpp | 88 +++++++++++++++++--------- src/init.cpp | 19 +++--- src/interface/consolecommand.hpp | 9 +-- src/main.hpp | 6 +- src/mod_tools.cpp | 25 +++++--- src/opengl.cpp | 27 +++++++- 8 files changed, 212 insertions(+), 74 deletions(-) diff --git a/src/draw.cpp b/src/draw.cpp index 97eff8d8a..80df4ee3d 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -99,6 +99,7 @@ static void buildVoxelShader( shader.compile(f, size_f, Shader::Type::Fragment); shader.bindAttribLocation("iPosition", 0); shader.bindAttribLocation("iColor", 1); + shader.bindAttribLocation("iNormal", 2); shader.link(); if (lightmap) { shader.bind(); @@ -180,7 +181,7 @@ void createCommonDrawResources() { static const char fb_hdr_fragment_glsl[] = "in vec2 TexCoord;" "uniform sampler2D uTexture;" - "uniform float uBrightness;" + "uniform vec4 uBrightness;" "uniform float uGamma;" "uniform float uExposure;" "out vec4 FragColor;" @@ -206,7 +207,7 @@ void createCommonDrawResources() { // gamma correction "mapped = pow(mapped, vec3(1.0 / uGamma));" - "FragColor = vec4(mapped * uBrightness, color.a);" + "FragColor = vec4(mapped, color.a) * uBrightness;" "}"; framebuffer::hdrShader.init("hdr framebuffer"); @@ -228,27 +229,34 @@ void createCommonDrawResources() { static const char vox_vertex_glsl[] = "in vec3 iPosition;" "in vec3 iColor;" + "in vec3 iNormal;" "uniform mat4 uProj;" "uniform mat4 uView;" "uniform mat4 uModel;" "out vec3 Color;" "out vec4 WorldPos;" + "out vec3 Normal;" "void main() {" "WorldPos = uModel * vec4(iPosition, 1.0);" "gl_Position = uProj * uView * WorldPos;" "Color = iColor;" + "Normal = (uModel * vec4(iNormal, 0.0)).xyz;" "}"; static const char vox_fragment_glsl[] = "in vec3 Color;" + "in vec3 Normal;" "in vec4 WorldPos;" "uniform mat4 uColorRemap;" "uniform vec4 uLightFactor;" "uniform vec4 uLightColor;" "uniform vec4 uColorAdd;" + "uniform vec4 uCameraPos;" "uniform sampler2D uLightmap;" "uniform vec2 uMapDims;" + "uniform float uFogDistance;" + "uniform vec4 uFogColor;" "out vec4 FragColor;" "void main() {" @@ -259,6 +267,12 @@ void createCommonDrawResources() { "vec2 TexCoord = WorldPos.xz / (uMapDims.xy * 32.0);" "vec4 Lightmap = texture(uLightmap, TexCoord);" "FragColor = vec4(Remapped, 1.0) * uLightFactor * (Lightmap + uLightColor) + uColorAdd;" + + "if (uFogDistance > 0.0) {" + "float dist = length(uCameraPos.xyz - WorldPos.xyz);" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "FragColor = vec4(mixed, FragColor.a);" + "}" "}"; buildVoxelShader(voxelShader, "voxelShader", true, @@ -267,11 +281,15 @@ void createCommonDrawResources() { static const char vox_bright_fragment_glsl[] = "in vec3 Color;" + "in vec3 Normal;" "in vec4 WorldPos;" "uniform mat4 uColorRemap;" "uniform vec4 uLightFactor;" "uniform vec4 uLightColor;" "uniform vec4 uColorAdd;" + "uniform vec4 uCameraPos;" + "uniform float uFogDistance;" + "uniform vec4 uFogColor;" "out vec4 FragColor;" "void main() {" @@ -280,6 +298,12 @@ void createCommonDrawResources() { " (uColorRemap[1].rgb * Color.g)+" " (uColorRemap[2].rgb * Color.b);" "FragColor = vec4(Remapped, 1.0) * uLightFactor * uLightColor + uColorAdd;" + + "if (uFogDistance > 0.0) {" + "float dist = length(uCameraPos.xyz - WorldPos.xyz);" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "FragColor = vec4(mixed, FragColor.a);" + "}" "}"; buildVoxelShader(voxelBrightShader, "voxelBrightShader", false, @@ -288,14 +312,18 @@ void createCommonDrawResources() { static const char vox_dithered_fragment_glsl[] = "in vec3 Color;" + "in vec3 Normal;" "in vec4 WorldPos;" "uniform float uDitherAmount;" "uniform mat4 uColorRemap;" "uniform vec4 uLightFactor;" "uniform vec4 uLightColor;" "uniform vec4 uColorAdd;" + "uniform vec4 uCameraPos;" "uniform sampler2D uLightmap;" "uniform vec2 uMapDims;" + "uniform float uFogDistance;" + "uniform vec4 uFogColor;" "out vec4 FragColor;" "void dither(ivec2 pos, float amount) {" @@ -319,6 +347,12 @@ void createCommonDrawResources() { "vec2 TexCoord = WorldPos.xz / (uMapDims.xy * 32.0);" "vec4 Lightmap = texture(uLightmap, TexCoord);" "FragColor = vec4(Remapped, 1.0) * uLightFactor * (Lightmap + uLightColor) + uColorAdd;" + + "if (uFogDistance > 0.0) {" + "float dist = length(uCameraPos.xyz - WorldPos.xyz);" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "FragColor = vec4(mixed, FragColor.a);" + "}" "}"; buildVoxelShader(voxelDitheredShader, "voxelDitheredShader", true, @@ -352,12 +386,21 @@ void createCommonDrawResources() { "uniform sampler2D uTextures;" "uniform sampler2D uLightmap;" "uniform vec2 uMapDims;" + "uniform vec4 uCameraPos;" + "uniform float uFogDistance;" + "uniform vec4 uFogColor;" "out vec4 FragColor;" "void main() {" "vec2 LightCoord = WorldPos.xz / (uMapDims.xy * 32.0);" "vec4 Lightmap = texture(uLightmap, LightCoord);" "FragColor = texture(uTextures, TexCoord) * vec4(Color, 1.f) * uLightFactor * Lightmap;" + + "if (uFogDistance > 0.0) {" + "float dist = length(uCameraPos.xyz - WorldPos.xyz);" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "FragColor = vec4(mixed, FragColor.a);" + "}" "}"; buildWorldShader(worldShader, "worldShader", true, @@ -373,6 +416,9 @@ void createCommonDrawResources() { "uniform sampler2D uTextures;" "uniform sampler2D uLightmap;" "uniform vec2 uMapDims;" + "uniform vec4 uCameraPos;" + "uniform float uFogDistance;" + "uniform vec4 uFogColor;" "out vec4 FragColor;" "void dither(ivec2 pos, float amount) {" @@ -392,6 +438,12 @@ void createCommonDrawResources() { "vec2 LightCoord = WorldPos.xz / (uMapDims.xy * 32.0);" "vec4 Lightmap = texture(uLightmap, LightCoord);" "FragColor = texture(uTextures, TexCoord) * vec4(Color, 1.f) * uLightFactor * Lightmap;" + + "if (uFogDistance > 0.0) {" + "float dist = length(uCameraPos.xyz - WorldPos.xyz);" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "FragColor = vec4(mixed, FragColor.a);" + "}" "}"; buildWorldShader(worldDitheredShader, "worldDitheredShader", true, @@ -479,9 +531,12 @@ void createCommonDrawResources() { "uniform vec4 uLightFactor;" "uniform vec4 uLightColor;" "uniform vec4 uColorAdd;" + "uniform vec4 uCameraPos;" "uniform sampler2D uTexture;" "uniform sampler2D uLightmap;" "uniform vec2 uMapDims;" + "uniform float uFogDistance;" + "uniform vec4 uFogColor;" "out vec4 FragColor;" "void main() {" @@ -490,6 +545,12 @@ void createCommonDrawResources() { "vec4 Lightmap = texture(uLightmap, LightCoord);" "FragColor = Texture * uLightFactor * (Lightmap + uLightColor) + uColorAdd;" "if (FragColor.a <= 0) discard;" + + "if (uFogDistance > 0.0) {" + "float dist = length(uCameraPos.xyz - WorldPos.xyz);" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "FragColor = vec4(mixed, FragColor.a);" + "}" "}"; buildSpriteShader(spriteShader, "spriteShader", true, @@ -503,9 +564,12 @@ void createCommonDrawResources() { "uniform vec4 uLightFactor;" "uniform vec4 uLightColor;" "uniform vec4 uColorAdd;" + "uniform vec4 uCameraPos;" "uniform sampler2D uTexture;" "uniform sampler2D uLightmap;" "uniform vec2 uMapDims;" + "uniform float uFogDistance;" + "uniform vec4 uFogColor;" "out vec4 FragColor;" "void dither(ivec2 pos, float amount) {" @@ -527,41 +591,44 @@ void createCommonDrawResources() { "vec4 Lightmap = texture(uLightmap, LightCoord);" "FragColor = Texture * uLightFactor * (Lightmap + uLightColor) + uColorAdd;" "if (FragColor.a <= 0) discard;" + + "if (uFogDistance > 0.0) {" + "float dist = length(uCameraPos.xyz - WorldPos.xyz);" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "FragColor = vec4(mixed, FragColor.a);" + "}" "}"; buildSpriteShader(spriteDitheredShader, "spriteDitheredShader", true, sprite_vertex_glsl, sizeof(sprite_vertex_glsl), sprite_dithered_fragment_glsl, sizeof(sprite_dithered_fragment_glsl)); - - static const char sprite_bright_vertex_glsl[] = - "in vec3 iPosition;" - "in vec2 iTexCoord;" - "uniform mat4 uProj;" - "uniform mat4 uView;" - "uniform mat4 uModel;" - "out vec2 TexCoord;" - - "void main() {" - "gl_Position = uProj * uView * uModel * vec4(iPosition, 1.0);" - "TexCoord = iTexCoord;" - "}"; static const char sprite_bright_fragment_glsl[] = + "in vec4 WorldPos;" "in vec2 TexCoord;" "uniform vec4 uLightFactor;" "uniform vec4 uLightColor;" "uniform vec4 uColorAdd;" + "uniform vec4 uCameraPos;" "uniform sampler2D uTexture;" + "uniform float uFogDistance;" + "uniform vec4 uFogColor;" "out vec4 FragColor;" "void main() {" "vec4 Texture = texture(uTexture, TexCoord);" "FragColor = Texture * uLightFactor * uLightColor + uColorAdd;" "if (FragColor.a <= 0) discard;" + + "if (uFogDistance > 0.0) {" + "float dist = length(uCameraPos.xyz - WorldPos.xyz);" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "FragColor = vec4(mixed, FragColor.a);" + "}" "}"; buildSpriteShader(spriteBrightShader, "spriteBrightShader", false, - sprite_bright_vertex_glsl, sizeof(sprite_bright_vertex_glsl), + sprite_vertex_glsl, sizeof(sprite_vertex_glsl), sprite_bright_fragment_glsl, sizeof(sprite_bright_fragment_glsl)); spriteMesh.init(); @@ -815,9 +882,9 @@ void framebuffer::draw(float brightness) { mesh.draw(); } -void framebuffer::hdrDraw(float brightness, float gamma, float exposure) { +void framebuffer::hdrDraw(const Vector4& brightness, float gamma, float exposure) { hdrShader.bind(); - GL_CHECK_ERR(glUniform1f(hdrShader.uniform("uBrightness"), brightness)); + GL_CHECK_ERR(glUniform4fv(hdrShader.uniform("uBrightness"), 1, (float*)&brightness)); GL_CHECK_ERR(glUniform1f(hdrShader.uniform("uGamma"), gamma)); GL_CHECK_ERR(glUniform1f(hdrShader.uniform("uExposure"), exposure)); mesh.draw(); diff --git a/src/draw.hpp b/src/draw.hpp index 2b842e5a2..75a4c0c80 100644 --- a/src/draw.hpp +++ b/src/draw.hpp @@ -13,6 +13,13 @@ #include "shader.hpp" +struct Vector4 { + float x; + float y; + float z; + float w; +}; + vec4_t vec4_copy(const vec4_t* v); vec4_t* mul_mat_vec4(vec4_t* result, const mat4x4_t* m, const vec4_t* v); vec4_t* add_vec4(vec4_t* result, const vec4_t* a, const vec4_t* b); @@ -207,7 +214,7 @@ struct framebuffer { void unlock(); void draw(float brightness = 1.f); - void hdrDraw(float brightness, float gamma, float exposure); + void hdrDraw(const Vector4& brightness, float gamma, float exposure); static void unbindForWriting(); static void unbindForReading(); static void unbindAll(); diff --git a/src/files.cpp b/src/files.cpp index fcc01dad4..0ded7972c 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -4142,20 +4142,30 @@ void generatePolyModels(int start, int end, bool forceCacheRebuild) { node_t* node = list_Node(&quads, (int)i / 2); polyquad_t* quad = (polyquad_t*)node->element; - polymodels[c].faces[i].r = quad->r; - polymodels[c].faces[i].g = quad->g; - polymodels[c].faces[i].b = quad->b; + auto& face = polymodels[c].faces[i]; + switch (quad->side) { + case 0: face.normal = { 1.f, 0.f, 0.f}; break; // front + case 1: face.normal = {-1.f, 0.f, 0.f}; break; // back + case 2: face.normal = { 0.f, 1.f, 0.f}; break; // right + case 3: face.normal = { 0.f, -1.f, 0.f}; break; // left + case 4: face.normal = { 0.f, 0.f, 1.f}; break; // bottom + case 5: face.normal = { 0.f, 0.f, -1.f}; break; // top + default: printlog("[MODELS] this should never happen!"); assert(0); break; + } + face.r = quad->r; + face.g = quad->g; + face.b = quad->b; if ( i % 2 ) { - polymodels[c].faces[i].vertex[0] = quad->vertex[0]; - polymodels[c].faces[i].vertex[1] = quad->vertex[1]; - polymodels[c].faces[i].vertex[2] = quad->vertex[2]; + face.vertex[0] = quad->vertex[0]; + face.vertex[1] = quad->vertex[1]; + face.vertex[2] = quad->vertex[2]; } else { - polymodels[c].faces[i].vertex[0] = quad->vertex[0]; - polymodels[c].faces[i].vertex[1] = quad->vertex[2]; - polymodels[c].faces[i].vertex[2] = quad->vertex[3]; + face.vertex[0] = quad->vertex[0]; + face.vertex[1] = quad->vertex[2]; + face.vertex[2] = quad->vertex[3]; } } @@ -4205,15 +4215,18 @@ void reloadModels(int start, int end) { char name[128]; fp->gets2(name, sizeof(name)); if ( c >= start && c < end ) { - if ( polymodels[c].vao ) { + if (polymodels[c].vao) { GL_CHECK_ERR(glDeleteVertexArrays(1, &polymodels[c].vao)); } - if ( polymodels[c].vbo ) { - GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].vbo)); - } - if ( polymodels[c].colors ) { - GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].colors)); - } + if (polymodels[c].positions) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].positions)); + } + if (polymodels[c].colors) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].colors)); + } + if (polymodels[c].normals) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].normals)); + } } } @@ -4265,17 +4278,21 @@ void generateVBOs(int start, int end) std::unique_ptr vaos(new GLuint[count]); GL_CHECK_ERR(glGenVertexArrays(count, vaos.get())); - std::unique_ptr vbos(new GLuint[count]); - GL_CHECK_ERR(glGenBuffers(count, vbos.get())); + std::unique_ptr position_vbos(new GLuint[count]); + GL_CHECK_ERR(glGenBuffers(count, position_vbos.get())); + + std::unique_ptr color_vbos(new GLuint[count]); + GL_CHECK_ERR(glGenBuffers(count, color_vbos.get())); - std::unique_ptr color_buffers(new GLuint[count]); - GL_CHECK_ERR(glGenBuffers(count, color_buffers.get())); + std::unique_ptr normal_vbos(new GLuint[count]); + GL_CHECK_ERR(glGenBuffers(count, normal_vbos.get())); for ( uint64_t c = (uint64_t)start; c < (uint64_t)end; ++c ) { polymodel_t* model = &polymodels[c]; - std::unique_ptr points(new GLfloat[9 * model->numfaces]); + std::unique_ptr positions(new GLfloat[9 * model->numfaces]); std::unique_ptr colors(new GLfloat[9 * model->numfaces]); + std::unique_ptr normals(new GLfloat[9 * model->numfaces]); for ( uint64_t i = 0; i < (uint64_t)model->numfaces; i++ ) { const polytriangle_t* face = &model->faces[i]; @@ -4284,27 +4301,32 @@ void generateVBOs(int start, int end) const uint64_t data_index = i * 9 + vert_index * 3; const vertex_t* vert = &face->vertex[vert_index]; - points[data_index] = vert->x; - points[data_index + 1] = -vert->z; - points[data_index + 2] = vert->y; + positions[data_index] = vert->x; + positions[data_index + 1] = -vert->z; + positions[data_index + 2] = vert->y; colors[data_index] = face->r / 255.f; colors[data_index + 1] = face->g / 255.f; colors[data_index + 2] = face->b / 255.f; + + normals[data_index] = face->normal.x; + normals[data_index + 1] = -face->normal.z; + normals[data_index + 2] = face->normal.y; } } model->vao = vaos[c - start]; - model->vbo = vbos[c - start]; - model->colors = color_buffers[c - start]; + model->positions = position_vbos[c - start]; + model->colors = color_vbos[c - start]; + model->normals = normal_vbos[c - start]; // NOTE: OpenGL 2.1 does not support vertex array objects! #ifdef VERTEX_ARRAYS_ENABLED GL_CHECK_ERR(glBindVertexArray(model->vao)); #endif - // vertex data - GL_CHECK_ERR(glBindBuffer(GL_ARRAY_BUFFER, model->vbo)); - GL_CHECK_ERR(glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 9 * model->numfaces, points.get(), GL_STATIC_DRAW)); + // position data + GL_CHECK_ERR(glBindBuffer(GL_ARRAY_BUFFER, model->positions)); + GL_CHECK_ERR(glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 9 * model->numfaces, positions.get(), GL_STATIC_DRAW)); #ifdef VERTEX_ARRAYS_ENABLED GL_CHECK_ERR(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr)); GL_CHECK_ERR(glEnableVertexAttribArray(0)); @@ -4318,6 +4340,14 @@ void generateVBOs(int start, int end) GL_CHECK_ERR(glEnableVertexAttribArray(1)); #endif + // normal data + GL_CHECK_ERR(glBindBuffer(GL_ARRAY_BUFFER, model->normals)); + GL_CHECK_ERR(glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 9 * model->numfaces, normals.get(), GL_STATIC_DRAW)); +#ifdef VERTEX_ARRAYS_ENABLED + GL_CHECK_ERR(glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, nullptr)); + GL_CHECK_ERR(glEnableVertexAttribArray(2)); +#endif + #ifndef VERTEX_ARRAYS_ENABLED GL_CHECK_ERR(glBindBuffer(GL_ARRAY_BUFFER, 0)); #endif diff --git a/src/init.cpp b/src/init.cpp index c33c8fdf0..359e9382a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -789,10 +789,6 @@ void Language::reset() int Language::loadLanguage(char const * const lang, bool forceLoadBaseDirectory) { - char filename[128] = { 0 }; - File* fp; - int c; - // open log file if ( !logfile ) { @@ -800,6 +796,7 @@ int Language::loadLanguage(char const * const lang, bool forceLoadBaseDirectory) } // compose filename + char filename[128] = { 0 }; snprintf(filename, 127, "lang/%s.txt", lang); std::string langFilepath; if ( PHYSFS_isInit() && PHYSFS_getRealDir(filename) != NULL && !forceLoadBaseDirectory ) @@ -906,6 +903,7 @@ int Language::loadLanguage(char const * const lang, bool forceLoadBaseDirectory) TTF_SetFontHinting(ttf16, TTF_HINTING_MONO); // open language file + File* fp; if ( (fp = openDataFile(langFilepath.c_str(), "rb")) == NULL ) { printlog("error: unable to load language file: '%s'", langFilepath.c_str()); @@ -1251,12 +1249,15 @@ int deinitApp() if (polymodels[c].vao) { GL_CHECK_ERR(glDeleteVertexArrays(1, &polymodels[c].vao)); } - if (polymodels[c].vbo) { - GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].vbo)); - } - if (polymodels[c].colors) { + if (polymodels[c].positions) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].positions)); + } + if (polymodels[c].colors) { GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].colors)); - } + } + if (polymodels[c].normals) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].normals)); + } } } free(polymodels); diff --git a/src/interface/consolecommand.hpp b/src/interface/consolecommand.hpp index cf540c675..b4931ac3c 100644 --- a/src/interface/consolecommand.hpp +++ b/src/interface/consolecommand.hpp @@ -14,16 +14,11 @@ #include #include +#include "../main.hpp" +#include "../draw.hpp" const char* FindConsoleCommand(const char* str, int index); -struct Vector4 { - float x; - float y; - float z; - float w; -}; - /* * How to define a console command: * diff --git a/src/main.hpp b/src/main.hpp index 10c90c3e3..b3c7fd244 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -571,6 +571,7 @@ typedef struct polyquad_t typedef struct polytriangle_t { vertex_t vertex[3]; + vertex_t normal; Uint8 r, g, b; } polytriangle_t; @@ -580,8 +581,11 @@ typedef struct polymodel_t polytriangle_t* faces; uint64_t numfaces; GLuint vao; - GLuint vbo; + + // vbos + GLuint positions; GLuint colors; + GLuint normals; //GLuint colors_shifted; //GLuint grayscale_colors; //GLuint grayscale_colors_shifted; diff --git a/src/mod_tools.cpp b/src/mod_tools.cpp index d8605becf..18f698db0 100644 --- a/src/mod_tools.cpp +++ b/src/mod_tools.cpp @@ -8215,15 +8215,18 @@ void Mods::loadModels(int start, int end) { char name[128]; fp->gets2(name, sizeof(name)); if ( c >= start && c < end ) { - if ( polymodels[c].vao ) { + if (polymodels[c].vao) { GL_CHECK_ERR(glDeleteVertexArrays(1, &polymodels[c].vao)); } - if ( polymodels[c].vbo ) { - GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].vbo)); + if (polymodels[c].positions) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].positions)); } - if ( polymodels[c].colors ) { + if (polymodels[c].colors) { GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].colors)); } + if (polymodels[c].normals) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].normals)); + } } } @@ -8403,12 +8406,15 @@ void Mods::unloadMods(bool force) if (polymodels[c].vao) { GL_CHECK_ERR(glDeleteVertexArrays(1, &polymodels[c].vao)); } - if (polymodels[c].vbo) { - GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].vbo)); + if (polymodels[c].positions) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].positions)); } if (polymodels[c].colors) { GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].colors)); } + if (polymodels[c].normals) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].normals)); + } } generateVBOs(0, nummodels); consoleCommand("/dumpcache"); @@ -8464,12 +8470,15 @@ void Mods::loadMods() if (polymodels[c].vao) { GL_CHECK_ERR(glDeleteVertexArrays(1, &polymodels[c].vao)); } - if (polymodels[c].vbo) { - GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].vbo)); + if (polymodels[c].positions) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].positions)); } if (polymodels[c].colors) { GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].colors)); } + if (polymodels[c].normals) { + GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].normals)); + } } generatePolyModels(modelsIndexUpdateStart, modelsIndexUpdateEnd, true); generateVBOs(modelsIndexUpdateStart, modelsIndexUpdateEnd); diff --git a/src/opengl.cpp b/src/opengl.cpp index 3d5e6746c..8e5d03b03 100644 --- a/src/opengl.cpp +++ b/src/opengl.cpp @@ -682,6 +682,18 @@ static void uploadUniforms(Shader& shader, float* proj, float* view, float* mapD if (proj) { GL_CHECK_ERR(glUniformMatrix4fv(shader.uniform("uProj"), 1, false, proj)); } if (view) { GL_CHECK_ERR(glUniformMatrix4fv(shader.uniform("uView"), 1, false, view)); } if (mapDims) { GL_CHECK_ERR(glUniform2fv(shader.uniform("uMapDims"), 1, mapDims)); } + +#ifdef EDITOR + float fogDistance = 0.f; + float fogColor[4] = { 1.f, 1.f, 1.f, 1.f }; + GL_CHECK_ERR(glUniform4fv(shader.uniform("uFogColor"), 1, fogColor)); + GL_CHECK_ERR(glUniform4fv(shader.uniform("uFogDistance"), 1, fogDistance)); +#else + static ConsoleVariable cvar_fogDistance("/fog_distance", 0.f); + static ConsoleVariable cvar_fogColor("/fog_color", {1.f, 1.f, 1.f, 1.f}); + GL_CHECK_ERR(glUniform4fv(shader.uniform("uFogColor"), 1, (float*)&*cvar_fogColor)); + GL_CHECK_ERR(glUniform1f(shader.uniform("uFogDistance"), *cvar_fogDistance)); +#endif } // hsv values: @@ -726,6 +738,8 @@ static vec4_t* HSVtoRGB(vec4_t* result, const vec4_t* hsv){ } static void uploadLightUniforms(view_t* camera, Shader& shader, Entity* entity, int mode, bool remap) { + const float cameraPos[4] = {(float)camera->x * 32.f, -(float)camera->z, (float)camera->y * 32.f, 1.f}; + GL_CHECK_ERR(glUniform4fv(shader.uniform("uCameraPos"), 1, cameraPos)); if (mode == REALCOLORS) { if (remap) { bool doGrayScale = false; @@ -862,6 +876,7 @@ static void uploadLightUniforms(view_t* camera, Shader& shader, Entity* entity, } } +constexpr Vector4 defaultBrightness = {1.f, 1.f, 1.f, 1.f}; constexpr float defaultGamma = 0.75f; // default gamma level: 75% constexpr float defaultExposure = 0.5f; // default exposure level: 50% constexpr float defaultAdjustmentRate = 0.1f; // how fast your eyes adjust @@ -883,6 +898,7 @@ bool hdrEnabled = true; static ConsoleVariable cvar_hdrMultithread("/hdr_multithread", defaultMultithread); static ConsoleVariable cvar_hdrExposure("/hdr_exposure", defaultExposure); static ConsoleVariable cvar_hdrGamma("/hdr_gamma", defaultGamma); +static ConsoleVariable cvar_hdrBrightness("/hdr_brightness", defaultBrightness); static ConsoleVariable cvar_hdrAdjustment("/hdr_adjust_rate", defaultAdjustmentRate); static ConsoleVariable cvar_hdrLimitHigh("/hdr_limit_high", defaultLimitHigh); static ConsoleVariable cvar_hdrLimitLow("/hdr_limit_low", defaultLimitLow); @@ -994,6 +1010,7 @@ void glEndCamera(view_t* camera, bool useHDR) const bool hdr_multithread = defaultMultithread; const float hdr_exposure = defaultExposure; const float hdr_gamma = defaultGamma; + const Vector4& hdr_brightness = defaultBrightness; const float hdr_adjustment_rate = defaultAdjustmentRate; const float hdr_limit_high = defaultLimitHigh; const float hdr_limit_low = defaultLimitLow; @@ -1004,6 +1021,7 @@ void glEndCamera(view_t* camera, bool useHDR) const bool hdr_multithread = *cvar_hdrMultithread; const float hdr_exposure = *cvar_hdrExposure; const float hdr_gamma = *cvar_hdrGamma; + const Vector4& hdr_brightness = *cvar_hdrBrightness; const float hdr_adjustment_rate = *cvar_hdrAdjustment; const float hdr_limit_high = *cvar_hdrLimitHigh; const float hdr_limit_low = *cvar_hdrLimitLow; @@ -1094,7 +1112,7 @@ void glEndCamera(view_t* camera, bool useHDR) } camera->fb[fbIndex].unlock(); const float exposure = std::min(std::max(hdr_limit_low, hdr_exposure / camera->luminance), hdr_limit_high); - const float brightness = 1.f; + const auto& brightness = hdr_brightness; const float gamma = hdr_gamma * vidgamma; // blit framebuffer @@ -1219,6 +1237,10 @@ void glDrawVoxel(view_t* camera, Entity* entity, int mode) { GL_CHECK_ERR(glBindBuffer(GL_ARRAY_BUFFER, polymodels[modelindex].colors)); GL_CHECK_ERR(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr)); GL_CHECK_ERR(glEnableVertexAttribArray(1)); + + GL_CHECK_ERR(glBindBuffer(GL_ARRAY_BUFFER, polymodels[modelindex].normals)); + GL_CHECK_ERR(glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, nullptr)); + GL_CHECK_ERR(glEnableVertexAttribArray(2)); #endif GL_CHECK_ERR(glDrawArrays(GL_TRIANGLES, 0, (int)(3 * polymodels[modelindex].numfaces))); @@ -1228,6 +1250,7 @@ void glDrawVoxel(view_t* camera, Entity* entity, int mode) { #else GL_CHECK_ERR(glDisableVertexAttribArray(0)); GL_CHECK_ERR(glDisableVertexAttribArray(1)); + GL_CHECK_ERR(glDisableVertexAttribArray(2)); #endif // reset GL state @@ -1936,6 +1959,8 @@ void glDrawWorld(view_t* camera, int mode) if (&shader != &worldDarkShader) { const GLfloat light[4] = { (float)getLightAtModifier, (float)getLightAtModifier, (float)getLightAtModifier, 1.f }; GL_CHECK_ERR(glUniform4fv(shader.uniform("uLightFactor"), 1, light)); + const float cameraPos[4] = {(float)camera->x * 32.f, -(float)camera->z, (float)camera->y * 32.f, 1.f}; + GL_CHECK_ERR(glUniform4fv(shader.uniform("uCameraPos"), 1, cameraPos)); } const bool ditheringDisabled = ticks - ditherDisabledTime < TICKS_PER_SECOND; From 6e1665a377469111686479209c60d473a1da979b Mon Sep 17 00:00:00 2001 From: SheridanR Date: Tue, 3 Oct 2023 19:19:53 -0700 Subject: [PATCH 078/146] fix compilation issue on macOS Signed-off-by: SheridanR --- src/player.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/player.hpp b/src/player.hpp index 73b4b0b15..e82e30bb0 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -1792,8 +1792,8 @@ class Player Uint32 errorFlashPushTicks = 0; Uint32 errorFlashTeleportTicks = 0; Uint32 errorFlashChillTicks = 0; - static const int errorFlashTicks = TICKS_PER_SECOND * 2.5; - static const int MAX_PUSH_POINTS = 5; + static constexpr int errorFlashTicks = TICKS_PER_SECOND * 2.5; + static constexpr int MAX_PUSH_POINTS = 5; int pushPoints = MAX_PUSH_POINTS; static Uint32 cooldownPushDelay; static Uint32 cooldownChillDelay; From 8fb5de075db5ff0c917a3d89bafcfa25c2ac0992 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 5 Oct 2023 04:02:55 +1100 Subject: [PATCH 079/146] * fix /seteffect nullptr crash --- src/interface/consolecommand.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interface/consolecommand.cpp b/src/interface/consolecommand.cpp index 057832830..916bdb919 100644 --- a/src/interface/consolecommand.cpp +++ b/src/interface/consolecommand.cpp @@ -2597,7 +2597,7 @@ namespace ConsoleCommands { { effects.push_back(1); } - while ( num > 0 ) + while ( num > 0 && players[clientnum]->entity ) { --num; auto picked = local_rng.discrete(effects.data(), effects.size()); From b4ba4bbb7f11d4d25999f1ddc14b6d262585fb95 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 5 Oct 2023 04:11:57 +1100 Subject: [PATCH 080/146] * fix ghost enemy allegiance for callouts * add colorblind callout icons * allow "A" to be bound for callouts without controls conflicts * add slow message to ghost chill atk * rename callout binding to Call Out * callout bindings for gamepad * callout icon and prompts on HUD * show ghost in paper doll --- src/actplayer.cpp | 120 +++++------ src/game.cpp | 1 - src/interface/interface.cpp | 172 ++++++++-------- src/interface/interface.hpp | 1 + src/magic/actmagic.cpp | 18 +- src/player.cpp | 1 + src/player.hpp | 3 +- src/ui/GameUI.cpp | 395 ++++++++++++++++++++++++++++++++---- src/ui/MainMenu.cpp | 12 +- 9 files changed, 528 insertions(+), 195 deletions(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index a6cb23751..b7372d1c7 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -146,9 +146,6 @@ void Player::Ghost_t::handleGhostCameraBobbing(bool useRefreshRateDelta) GHOSTCAM_BOBMODE = 0; } } -void Player::Ghost_t::handleGhostCameraPosition(bool useRefreshRateDelta) -{ -} void Player::Ghost_t::handleGhostMovement(const bool useRefreshRateDelta) { @@ -303,13 +300,16 @@ void Player::Ghost_t::reset() my = nullptr; uid = 0; cooldownPush = 0; - cooldownPushTimeout = 0; cooldownChill = 0; cooldownTeleport = 0; errorFlashPushTicks = 0; errorFlashTeleportTicks = 0; errorFlashChillTicks = 0; pushPoints = MAX_PUSH_POINTS; + + castingSpellAnimation = GHOST_SPELL_NONE; + castingHeldDuration = 0; + player.cleanUpOnEntityRemoval(); } @@ -475,7 +475,7 @@ void Player::Ghost_t::handleAttack() bool finishSpell = false; if ( castingSpellAnimation == GHOST_SPELL_BOLT ) { - if ( castingHeldDuration >= castLoopDuration ) + if ( castingHeldDuration >= castLoopDuration * 1.5 ) { finishSpell = true; } @@ -895,7 +895,7 @@ void Player::Ghost_t::handleActions() && !gamePaused ) { bool showCalloutCommandsOnGamepad = false; - auto showCalloutCommandsFind = b.find("Show Player Callouts"); + auto showCalloutCommandsFind = b.find("Call Out"); std::string showCalloutCommandsInputStr = ""; if ( showCalloutCommandsFind != b.end() ) { @@ -909,14 +909,14 @@ void Player::Ghost_t::handleActions() (showCalloutCommandsInputStr == input.binding("Interact Tooltip Next") || showCalloutCommandsInputStr == input.binding("Interact Tooltip Prev")) ) { - input.consumeBinaryToggle("Show Player Callouts"); + input.consumeBinaryToggle("Call Out"); player.hud.bOpenCalloutsMenuDisabled = true; } } - if ( (input.binaryToggle("Show Player Callouts") && !showCalloutCommandsOnGamepad + if ( (input.binaryToggle("Call Out") && !showCalloutCommandsOnGamepad && player.shootmode) - || (input.binaryToggle("Show Player Callouts") && showCalloutCommandsOnGamepad + || (input.binaryToggle("Call Out") && showCalloutCommandsOnGamepad && player.shootmode /*&& !player.worldUI.bTooltipInView*/) ) { if ( !calloutMenu.bOpen && !calloutMenu.selectMoveTo ) @@ -925,7 +925,7 @@ void Player::Ghost_t::handleActions() { player.closeAllGUIs(CLOSEGUI_ENABLE_SHOOTMODE, CLOSEGUI_DONT_CLOSE_CALLOUTGUI); } - input.consumeBinaryToggle("Show Player Callouts"); + input.consumeBinaryToggle("Call Out"); calloutMenu.selectMoveTo = true; calloutMenu.optionSelected = CalloutRadialMenu::CALLOUT_CMD_SELECT; calloutMenu.lockOnEntityUid = 0; @@ -6259,7 +6259,7 @@ void actPlayer(Entity* my) && !gamePaused ) { bool showCalloutCommandsOnGamepad = false; - auto showCalloutCommandsFind = b.find("Show Player Callouts"); + auto showCalloutCommandsFind = b.find("Call Out"); std::string showCalloutCommandsInputStr = ""; if ( showCalloutCommandsFind != b.end() ) { @@ -6273,34 +6273,37 @@ void actPlayer(Entity* my) (showCalloutCommandsInputStr == input.binding("Interact Tooltip Next") || showCalloutCommandsInputStr == input.binding("Interact Tooltip Prev")) ) { - input.consumeBinaryToggle("Show Player Callouts"); + input.consumeBinaryToggle("Call Out"); players[PLAYER_NUM]->hud.bOpenCalloutsMenuDisabled = true; } } - if ( (input.binaryToggle("Show Player Callouts") && !showCalloutCommandsOnGamepad + if ( (input.binaryToggle("Call Out") && !showCalloutCommandsOnGamepad && players[PLAYER_NUM]->shootmode) - || (input.binaryToggle("Show Player Callouts") && showCalloutCommandsOnGamepad + || (input.binaryToggle("Call Out") && showCalloutCommandsOnGamepad && players[PLAYER_NUM]->shootmode /*&& !players[PLAYER_NUM]->worldUI.bTooltipInView*/) ) { if ( !calloutMenu.bOpen && !calloutMenu.selectMoveTo ) { - if ( !players[PLAYER_NUM]->shootmode ) + if ( !followerMenu.followerMenuIsOpen() ) { - players[PLAYER_NUM]->closeAllGUIs(CLOSEGUI_ENABLE_SHOOTMODE, CLOSEGUI_DONT_CLOSE_CALLOUTGUI); - } - input.consumeBinaryToggle("Show Player Callouts"); - calloutMenu.selectMoveTo = true; - calloutMenu.optionSelected = CalloutRadialMenu::CALLOUT_CMD_SELECT; - calloutMenu.lockOnEntityUid = 0; - Player::soundActivate(); - skipFollowerMenu = true; + if ( !players[PLAYER_NUM]->shootmode ) + { + players[PLAYER_NUM]->closeAllGUIs(CLOSEGUI_ENABLE_SHOOTMODE, CLOSEGUI_DONT_CLOSE_CALLOUTGUI); + } + input.consumeBinaryToggle("Call Out"); + calloutMenu.selectMoveTo = true; + calloutMenu.optionSelected = CalloutRadialMenu::CALLOUT_CMD_SELECT; + calloutMenu.lockOnEntityUid = 0; + Player::soundActivate(); + skipFollowerMenu = true; - if ( players[PLAYER_NUM]->worldUI.isEnabled() ) - { - players[PLAYER_NUM]->worldUI.reset(); - players[PLAYER_NUM]->worldUI.tooltipView = Player::WorldUI_t::TooltipView::TOOLTIP_VIEW_RESCAN; - players[PLAYER_NUM]->worldUI.gimpDisplayTimer = 0; + if ( players[PLAYER_NUM]->worldUI.isEnabled() ) + { + players[PLAYER_NUM]->worldUI.reset(); + players[PLAYER_NUM]->worldUI.tooltipView = Player::WorldUI_t::TooltipView::TOOLTIP_VIEW_RESCAN; + players[PLAYER_NUM]->worldUI.gimpDisplayTimer = 0; + } } } else if ( calloutMenu.selectMoveTo ) @@ -6443,41 +6446,44 @@ void actPlayer(Entity* my) } } - if ( (input.binaryToggle("Show NPC Commands") && !showNPCCommandsOnGamepad) - || (input.binaryToggle("Show NPC Commands") && showNPCCommandsOnGamepad - && players[PLAYER_NUM]->shootmode /*&& !players[PLAYER_NUM]->worldUI.bTooltipInView*/) ) + if ( !calloutMenu.calloutMenuIsOpen() ) { - if ( players[PLAYER_NUM] && players[PLAYER_NUM]->entity - && followerMenu.recentEntity->monsterTarget == players[PLAYER_NUM]->entity->getUID() ) + if ( (input.binaryToggle("Show NPC Commands") && !showNPCCommandsOnGamepad) + || (input.binaryToggle("Show NPC Commands") && showNPCCommandsOnGamepad + && players[PLAYER_NUM]->shootmode /*&& !players[PLAYER_NUM]->worldUI.bTooltipInView*/) ) { - // your ally is angry at you! - } - else - { - selectedEntity[PLAYER_NUM] = followerMenu.recentEntity; - followerMenu.holdWheel = true; - if ( showNPCCommandsOnGamepad ) + if ( players[PLAYER_NUM] && players[PLAYER_NUM]->entity + && followerMenu.recentEntity->monsterTarget == players[PLAYER_NUM]->entity->getUID() ) + { + // your ally is angry at you! + } + else { - followerMenu.holdWheel = false; + selectedEntity[PLAYER_NUM] = followerMenu.recentEntity; + followerMenu.holdWheel = true; + if ( showNPCCommandsOnGamepad ) + { + followerMenu.holdWheel = false; + } } } - } - else if ( (input.binaryToggle("Command NPC") && !lastNPCCommandOnGamepad) - || (input.binaryToggle("Command NPC") && lastNPCCommandOnGamepad && players[PLAYER_NUM]->shootmode/*&& !players[PLAYER_NUM]->worldUI.bTooltipInView*/) ) - { - if ( players[PLAYER_NUM] && players[PLAYER_NUM]->entity - && followerMenu.recentEntity->monsterTarget == players[PLAYER_NUM]->entity->getUID() ) - { - // your ally is angry at you! - input.consumeBinaryToggle("Command NPC"); - } - else if ( followerMenu.optionPrevious != -1 ) + else if ( (input.binaryToggle("Command NPC") && !lastNPCCommandOnGamepad) + || (input.binaryToggle("Command NPC") && lastNPCCommandOnGamepad && players[PLAYER_NUM]->shootmode/*&& !players[PLAYER_NUM]->worldUI.bTooltipInView*/) ) { - followerMenu.followerToCommand = followerMenu.recentEntity; - } - else - { - input.consumeBinaryToggle("Command NPC"); + if ( players[PLAYER_NUM] && players[PLAYER_NUM]->entity + && followerMenu.recentEntity->monsterTarget == players[PLAYER_NUM]->entity->getUID() ) + { + // your ally is angry at you! + input.consumeBinaryToggle("Command NPC"); + } + else if ( followerMenu.optionPrevious != -1 ) + { + followerMenu.followerToCommand = followerMenu.recentEntity; + } + else + { + input.consumeBinaryToggle("Command NPC"); + } } } } diff --git a/src/game.cpp b/src/game.cpp index 77b17cd8e..81edf5adf 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5925,7 +5925,6 @@ void drawAllPlayerCameras() { players[c]->ghost.handleGhostCameraBobbing(true); players[c]->ghost.handleGhostMovement(true); players[c]->ghost.handleGhostCameraUpdate(true); - players[c]->ghost.handleGhostCameraPosition(true); //messagePlayer(0, "%3.2f | %3.2f", players[c]->entity->yaw, oldYaw); } } diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index e0daf8cab..653475d64 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -23516,15 +23516,35 @@ CalloutRadialMenu::CalloutType CalloutRadialMenu::getCalloutTypeForEntity(const else if ( parent->behavior == &actMonster ) { int monsterType = parent->getMonsterTypeFromSprite(); + bool enemies = false; + if ( players[player]->entity ) + { + if ( multiplayer != CLIENT && parent->checkEnemy(players[player]->entity) ) + { + enemies = true; + } + else if ( multiplayer == CLIENT + && !parent->monsterAllyGetPlayerLeader() + && !monsterally[monsterType][stats[player]->type] ) + { + enemies = true; + } + } + else + { + if ( !parent->monsterAllyGetPlayerLeader() + && !monsterally[monsterType][stats[player]->type] ) + { + enemies = true; + } + } + type = CALLOUT_TYPE_NPC; if ( monsterType == SHOPKEEPER ) { type = CALLOUT_TYPE_NPC; } - else if ( (multiplayer != CLIENT && parent->checkEnemy(players[player]->entity) ) - || (multiplayer == CLIENT - && !parent->monsterAllyGetPlayerLeader() - && !monsterally[monsterType][stats[player]->type])) + else if ( enemies ) { type = CALLOUT_TYPE_NPC_ENEMY; } @@ -23737,6 +23757,10 @@ void CalloutRadialMenu::CalloutParticle_t::init(const int player) void CalloutRadialMenu::closeCalloutMenuGUI() { + if ( calloutMenuIsOpen() ) + { + players[gui_player]->worldUI.reset(); + } bOpen = false; lockOnEntityUid = 0; selectMoveTo = false; @@ -23762,6 +23786,44 @@ void CalloutRadialMenu::closeCalloutMenuGUI() animInvalidActionTicks = 0; } +std::string& CalloutRadialMenu::WorldIconEntry_t::getPlayerIconPath(const int playernum) +{ + if ( colorblind_lobby ) + { + switch ( playernum ) + { + case 0: + return pathPlayer3; + case 1: + return pathPlayer4; + case 2: + return pathPlayer2; + case 3: + return pathPlayerX; + default: + return pathPlayerX; + break; + } + } + else + { + switch ( playernum ) + { + case 0: + return pathPlayer1; + case 1: + return pathPlayer2; + case 2: + return pathPlayer3; + case 3: + return pathPlayer4; + default: + return pathPlayerX; + break; + } + } +} + void CalloutRadialMenu::drawCallouts(const int playernum) { auto& pingFrame = CalloutMenu[playernum].calloutPingFrame; @@ -23856,29 +23918,8 @@ void CalloutRadialMenu::drawCallouts(const int playernum) } } - switch ( playerColor ) - { - case 0: - iconPath = iconPaths->pathPlayer1; - iconPathMini = iconPathsMini.pathPlayer1; - break; - case 1: - iconPath = iconPaths->pathPlayer2; - iconPathMini = iconPathsMini.pathPlayer2; - break; - case 2: - iconPath = iconPaths->pathPlayer3; - iconPathMini = iconPathsMini.pathPlayer3; - break; - case 3: - iconPath = iconPaths->pathPlayer4; - iconPathMini = iconPathsMini.pathPlayer4; - break; - default: - iconPath = iconPaths->pathPlayerX; - iconPathMini = iconPathsMini.pathPlayerX; - break; - } + iconPath = iconPaths->getPlayerIconPath(playerColor); + iconPathMini = iconPathsMini.getPlayerIconPath(playerColor); if ( iconPath == "" ) { @@ -24778,6 +24819,14 @@ void CalloutRadialMenu::drawCalloutMenu() } bool menuConfirmOnGamepad = input.input("MenuConfirm").isBindingUsingGamepad(); + if ( menuConfirmOnGamepad ) + { + if ( input.input("Call Out").input + == input.input("MenuConfirm").input ) + { + menuConfirmOnGamepad = false; + } + } bool menuLeftClickOnKeyboard = input.input("MenuLeftClick").isBindingUsingKeyboard() && !inputs.hasController(gui_player); // process commands if option selected on the wheel. @@ -24787,17 +24836,17 @@ void CalloutRadialMenu::drawCalloutMenu() } else if ( (!menuToggleClick && !holdWheel && !input.binaryToggle("Use") - && !input.binaryToggle("Show Player Callouts") + && !input.binaryToggle("Call Out") && !(input.binaryToggle("MenuConfirm") && menuConfirmOnGamepad) && !(input.binaryToggle("MenuLeftClick") && menuLeftClickOnKeyboard)) - || (menuToggleClick && (input.binaryToggle("Use") || input.binaryToggle("Show Player Callouts"))) + || (menuToggleClick && (input.binaryToggle("Use") || input.binaryToggle("Call Out"))) || ((input.binaryToggle("MenuConfirm") && menuConfirmOnGamepad) || (input.binaryToggle("MenuLeftClick") && menuLeftClickOnKeyboard) || (input.binaryToggle("Use") && holdWheel)) - || (!input.binaryToggle("Show Player Callouts") && holdWheel && !menuToggleClick) + || (!input.binaryToggle("Call Out") && holdWheel && !menuToggleClick) ) { - //bool usingShowCmdRelease = (!input.binaryToggle("Show Player Callouts") && holdWheel && !menuToggleClick); + //bool usingShowCmdRelease = (!input.binaryToggle("Call Out") && holdWheel && !menuToggleClick); if ( menuToggleClick ) { @@ -24811,11 +24860,11 @@ void CalloutRadialMenu::drawCalloutMenu() input.consumeBinaryToggle("Use"); input.consumeBinaryToggle("MenuConfirm"); input.consumeBinaryToggle("MenuLeftClick"); - input.consumeBinaryToggle("Show Player Callouts"); + input.consumeBinaryToggle("Call Out"); input.consumeBindingsSharedWithBinding("Use"); input.consumeBindingsSharedWithBinding("MenuConfirm"); input.consumeBindingsSharedWithBinding("MenuLeftClick"); - input.consumeBindingsSharedWithBinding("Show Player Callouts"); + input.consumeBindingsSharedWithBinding("Call Out"); if ( disableOption != 0 ) { @@ -25251,24 +25300,7 @@ void CalloutRadialMenu::drawCalloutMenu() } { - switch ( targetPlayer ) - { - case 0: - panelIcons[i]->path = worldIconEntries[key].pathPlayer1; - break; - case 1: - panelIcons[i]->path = worldIconEntries[key].pathPlayer2; - break; - case 2: - panelIcons[i]->path = worldIconEntries[key].pathPlayer3; - break; - case 3: - panelIcons[i]->path = worldIconEntries[key].pathPlayer4; - break; - default: - panelIcons[i]->path = worldIconEntries[key].pathPlayerX; - break; - } + panelIcons[i]->path = worldIconEntries[key].getPlayerIconPath(targetPlayer); } } else if ( i == CALLOUT_CMD_SOUTHWEST ) @@ -25290,24 +25322,7 @@ void CalloutRadialMenu::drawCalloutMenu() } { - switch ( targetPlayer ) - { - case 0: - panelIcons[i]->path = worldIconEntries[key].pathPlayer1; - break; - case 1: - panelIcons[i]->path = worldIconEntries[key].pathPlayer2; - break; - case 2: - panelIcons[i]->path = worldIconEntries[key].pathPlayer3; - break; - case 3: - panelIcons[i]->path = worldIconEntries[key].pathPlayer4; - break; - default: - panelIcons[i]->path = worldIconEntries[key].pathPlayerX; - break; - } + panelIcons[i]->path = worldIconEntries[key].getPlayerIconPath(targetPlayer); } } else if ( i == CALLOUT_CMD_SOUTHEAST ) @@ -25329,24 +25344,7 @@ void CalloutRadialMenu::drawCalloutMenu() } { - switch ( targetPlayer ) - { - case 0: - panelIcons[i]->path = worldIconEntries[key].pathPlayer1; - break; - case 1: - panelIcons[i]->path = worldIconEntries[key].pathPlayer2; - break; - case 2: - panelIcons[i]->path = worldIconEntries[key].pathPlayer3; - break; - case 3: - panelIcons[i]->path = worldIconEntries[key].pathPlayer4; - break; - default: - panelIcons[i]->path = worldIconEntries[key].pathPlayerX; - break; - } + panelIcons[i]->path = worldIconEntries[key].getPlayerIconPath(targetPlayer); } } } diff --git a/src/interface/interface.hpp b/src/interface/interface.hpp index 5f930a02c..110d082d1 100644 --- a/src/interface/interface.hpp +++ b/src/interface/interface.hpp @@ -1426,6 +1426,7 @@ struct CalloutRadialMenu std::string pathPlayer4 = ""; std::string pathPlayerX = ""; int id = 0; + std::string& getPlayerIconPath(const int playernum); }; static std::map worldIconEntries; static std::map helpDescriptors; diff --git a/src/magic/actmagic.cpp b/src/magic/actmagic.cpp index c6d07f21b..a67bb8835 100644 --- a/src/magic/actmagic.cpp +++ b/src/magic/actmagic.cpp @@ -2338,7 +2338,23 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. if ( doSlow ) { - hit.entity->setEffect(EFF_SLOW, true, duration, false); + if ( hit.entity->setEffect(EFF_SLOW, true, duration, false) ) + { + //playSoundEntity(hit.entity, 396 + local_rng.rand() % 3, 64); + if ( parent ) + { + Uint32 color = makeColorRGB(0, 255, 0); + if ( parent->behavior == &actPlayer || parent->behavior == &actDeathGhost ) + { + messagePlayerMonsterEvent(parent->skill[2], color, *hitstats, Language::get(394), Language::get(393), MSG_COMBAT); + } + } + /*Uint32 color = makeColorRGB(255, 0, 0); + if ( player >= 0 ) + { + messagePlayerColor(player, MESSAGE_COMBAT, color, Language::get(395)); + }*/ + } } if ( hit.entity->setEffect(EFF_KNOCKBACK, true, 30, false) ) diff --git a/src/player.cpp b/src/player.cpp index 5d755df58..e2cb8a59a 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -6626,6 +6626,7 @@ void Player::clearGUIPointers() hud.allyFollowerTitleFrame = nullptr; hud.allyFollowerGlyphFrame = nullptr; hud.allyPlayerFrame = nullptr; + hud.calloutPromptFrame = nullptr; hud.enemyBarFrame = nullptr; hud.enemyBarFrameHUD = nullptr; hud.actionPromptsFrame = nullptr; diff --git a/src/player.hpp b/src/player.hpp index 73b4b0b15..ecab54de9 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -1430,6 +1430,7 @@ class Player Frame* allyFollowerTitleFrame = nullptr; Frame* allyFollowerGlyphFrame = nullptr; Frame* allyPlayerFrame = nullptr; + Frame* calloutPromptFrame = nullptr; Frame* enemyBarFrame = nullptr; Frame* enemyBarFrameHUD = nullptr; Frame* actionPromptsFrame = nullptr; @@ -1786,7 +1787,6 @@ class Player int teleportToPlayer = -1; Uint32 uid = 0; Uint32 cooldownPush = 0; - Uint32 cooldownPushTimeout = 0; Uint32 cooldownChill = 0; Uint32 cooldownTeleport = 0; Uint32 errorFlashPushTicks = 0; @@ -1804,7 +1804,6 @@ class Player void handleGhostCameraUpdate(bool useRefreshRateDelta); void handleGhostCameraBobbing(bool useRefreshRateDelta); void handleGhostMovement(bool useRefreshRateDelta); - void handleGhostCameraPosition(bool useRefreshRateDelta); void handleActions(); void handleAttack(); bool isActive(); diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 7dadeee93..cc05e710e 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -902,6 +902,184 @@ void createAllyFollowerFrame(const int player) } } +void createCalloutPromptFrame(const int player) +{ + auto& hud_t = players[player]->hud; + + auto frame = hud_t.hudFrame->addFrame("callout prompts"); + hud_t.calloutPromptFrame = frame; + frame->setHollow(true); + frame->setSize(SDL_Rect{ 0, 0, 300, 0 }); + frame->setDisabled(true); + frame->setScrollBarsEnabled(false); + frame->setAllowScrollBinds(false); + + auto glyph = frame->addImage(SDL_Rect{ 0, 0, 0, 0 }, 0xFFFFFFFF, "", "glyph"); + glyph->disabled = true; + auto icon = frame->addImage(SDL_Rect{ 0, 0, 0, 0 }, 0xFFFFFFFF, "", "icon"); + icon->disabled = true; + + auto text = frame->addField("prompt", 64); + text->setFont(smallfont_outline); + text->setHJustify(Field::justify_t::LEFT); + text->setVJustify(Field::justify_t::TOP); + text->setSize(SDL_Rect{ 0, 0, frame->getSize().w, 24 }); + text->setText(Language::get(6049)); + text->setColor(0xFFFFFFFF); + text->setOntop(true); + text->setDisabled(true); +} + +const int kPlayerBarsEntryFrameWidth = 214; + +void updateCalloutPromptFrame(const int player) +{ + auto& hud_t = players[player]->hud; + auto frame = hud_t.calloutPromptFrame; + if ( !frame ) + { + return; + } + + if ( !Player::getPlayerInteractEntity(player) ) + { + frame->setDisabled(true); + return; + } + else if ( !players[player]->shootmode && !FollowerMenu[player].followerMenuIsOpen() + && !CalloutMenu[player].calloutMenuIsOpen() ) + { + frame->setDisabled(true); + return; + } + + int alignX = 8; + int alignY = 0; + static ConsoleVariable cvar_callout_prompt_horizontal("/callout_prompt_horizontal", false); + if ( hud_t.allyPlayerFrame && !hud_t.allyPlayerFrame->isDisabled() + && hud_t.allyPlayerFrame->getOpacity() > 0.0 ) + { + if ( hud_t.playerBars.size() > 0 ) + { + if ( *cvar_callout_prompt_horizontal ) + { + alignX += kPlayerBarsEntryFrameWidth; + } + else + { + alignY += hud_t.playerBars.size() * 36; + } + } + } + + frame->setDisabled(false); + SDL_Rect framePos; + framePos.x = alignX; + framePos.y = players[player]->bUseCompactGUIWidth() ? AllyStatusBarSettings_t::FollowerBars_t::entrySettings.baseYSplitscreen : AllyStatusBarSettings_t::FollowerBars_t::entrySettings.baseY; + framePos.y -= 4; + framePos.y += alignY; + framePos.w = 300; + framePos.h = 50; + frame->setSize(framePos); + + auto glyph = frame->findImage("glyph"); + auto glyphPathUnpressed = Input::inputs[player].getGlyphPathForBinding("Call Out", false); + auto glyphPathPressed = Input::inputs[player].getGlyphPathForBinding("Call Out", true); + glyph->disabled = true; + + const int nominalGlyphHeight = 26; + int unpressedHeight = 0; + int unpressedY = 0; + if ( ticks % 50 < 25 && (CalloutMenu[player].calloutMenuIsOpen() && CalloutMenu[player].selectMoveTo) ) + { + glyph->path = glyphPathPressed; + if ( auto imgGet = Image::get(glyph->path.c_str()) ) + { + glyph->disabled = false; + SDL_Rect glyphPos{ 0, 8, (int)imgGet->getWidth(), (int)imgGet->getHeight() }; + glyph->pos = glyphPos; + if ( auto imgGetUnpressed = Image::get(glyphPathUnpressed.c_str()) ) + { + unpressedHeight = imgGetUnpressed->getHeight(); + unpressedY = glyph->pos.y; + if ( unpressedHeight != glyph->pos.h ) + { + glyph->pos.y -= (glyph->pos.h - unpressedHeight); + } + + if ( unpressedHeight != nominalGlyphHeight ) + { + unpressedY -= (unpressedHeight - nominalGlyphHeight) / 2; + glyph->pos.y -= (unpressedHeight - nominalGlyphHeight) / 2; + } + } + } + } + else + { + glyph->path = glyphPathUnpressed; + if ( auto imgGet = Image::get(glyph->path.c_str()) ) + { + glyph->disabled = false; + SDL_Rect glyphPos{ 0, 8, (int)imgGet->getWidth(), (int)imgGet->getHeight() }; + glyph->pos = glyphPos; + unpressedHeight = glyph->pos.h; + unpressedY = glyph->pos.y; + if ( glyph->pos.h != nominalGlyphHeight ) + { + unpressedY -= (glyph->pos.h - nominalGlyphHeight) / 2; + glyph->pos.y -= (glyph->pos.h - nominalGlyphHeight) / 2; + } + } + } + + int glyphAlignY = unpressedY + (unpressedHeight - nominalGlyphHeight) / 2; + + auto icon = frame->findImage("icon"); + icon->disabled = true; + static ConsoleVariable cvar_callout_prompt_wheel("/callout_prompt_wheel", false); + if ( !glyph->disabled ) + { + if ( *cvar_callout_prompt_wheel && + CalloutMenu[player].calloutMenuIsOpen() && CalloutMenu[player].selectMoveTo ) + { + icon->path = "*images/ui/HUD/HUD_Ally_Callout_Wheel_00.png"; + } + else + { + icon->path = "*images/ui/HUD/HUD_Ally_Callout_00.png"; + } + if ( auto imgGet = Image::get(icon->path.c_str()) ) + { + icon->disabled = false; + icon->pos.w = imgGet->getWidth(); + icon->pos.h = imgGet->getHeight(); + icon->pos.x = glyph->pos.x + glyph->pos.w + 4; + icon->pos.y = unpressedY + (unpressedHeight) / 2 - icon->pos.h / 2; + if ( icon->pos.y % 2 == 1 ) + { + ++icon->pos.y; + } + } + } + + auto text = frame->findField("prompt"); + text->setDisabled(true); + static ConsoleVariable cvar_callout_prompt_text("/callout_prompt_text", false); + if ( !icon->disabled && *cvar_callout_prompt_text ) + { + if ( CalloutMenu[player].calloutMenuIsOpen() && CalloutMenu[player].selectMoveTo ) + { + text->setDisabled(false); + text->setText(Language::get(6049)); + SDL_Rect pos = text->getSize(); + pos.x = icon->pos.x + icon->pos.w + 4; + pos.y = glyphAlignY - 10; + text->setSize(pos); + } + } +} + void createAllyFollowerTitleFrame(const int player) { auto& hud_t = players[player]->hud; @@ -941,7 +1119,7 @@ Frame* createAllyPlayerEntry(const int player) auto entry = baseFrame->addFrame("entry"); const int allyPlayerEntryHeight = 40; - entry->setSize(SDL_Rect{ 0, 0, 214, allyPlayerEntryHeight }); + entry->setSize(SDL_Rect{ 0, 0, kPlayerBarsEntryFrameWidth, allyPlayerEntryHeight }); entry->setHollow(true); entry->setInheritParentFrameOpacity(false); @@ -8297,6 +8475,12 @@ void createWorldTooltipPrompts(const int player) text2->setDisabled(true); text2->setSize(SDL_Rect{ 0, 0, 0, 0 }); + auto text3 = worldTooltipFrame->addField("prompt callout text", 256); + text3->setFont(promptFont); + text3->setText(""); + text3->setDisabled(true); + text3->setSize(SDL_Rect{ 0, 0, 0, 0 }); + const int iconSize = 24; SDL_Rect iconPos{ 0, 0, iconSize, iconSize }; @@ -8316,6 +8500,10 @@ void createWorldTooltipPrompts(const int player) 0xFFFFFFFF, "images/system/white.png", "glyph img 3"); glyphAdditional2->disabled = true; + auto glyphAdditional3 = worldTooltipFrame->addImage(SDL_Rect{ 0, 0, 0, 0 }, + 0xFFFFFFFF, "images/system/white.png", "glyph img 4"); + glyphAdditional3->disabled = true; + auto cursor = worldTooltipFrame->addImage(SDL_Rect{ 0, 0, 0, 0 }, 0xFFFFFFFF, "images/system/white.png", "cursor img"); cursor->disabled = true; @@ -8405,8 +8593,12 @@ void Player::HUD_t::updateWorldTooltipPrompts() text->setDisabled(true); auto textCycle = worldTooltipFrame->findField("prompt cycle text"); textCycle->setDisabled(true); + auto textCallout = worldTooltipFrame->findField("prompt callout text"); + textCallout->setDisabled(true); auto glyphCycle = worldTooltipFrame->findImage("glyph img 3"); glyphCycle->disabled = true; + auto glyphCallout = worldTooltipFrame->findImage("glyph img 4"); + glyphCallout->disabled = true; SDL_Rect textPos{ 0, 0, 0, 0 }; const int skillIconToGlyphPadding = 4; @@ -8625,19 +8817,26 @@ void Player::HUD_t::updateWorldTooltipPrompts() } } - for ( auto& skill : player.skillSheet.skillSheetData.skillEntries ) + if ( calloutInteract ) { - if ( skill.skillId == PRO_LEADERSHIP ) + icon->path = "*images/ui/HUD/HUD_Ally_Callout_00.png"; + } + else + { + for ( auto& skill : player.skillSheet.skillSheetData.skillEntries ) { - if ( skillCapstoneUnlocked(player.playernum, PRO_LEADERSHIP) ) - { - icon->path = skill.skillIconPathLegend; - } - else + if ( skill.skillId == PRO_LEADERSHIP ) { - icon->path = skill.skillIconPath; + if ( skillCapstoneUnlocked(player.playernum, PRO_LEADERSHIP) ) + { + icon->path = skill.skillIconPathLegend; + } + else + { + icon->path = skill.skillIconPath; + } + break; } - break; } } @@ -9008,6 +9207,94 @@ void Player::HUD_t::updateWorldTooltipPrompts() promptPos.w = std::max(cursor->pos.x + cursor->pos.w, promptPos.w); promptPos.h = std::max(cursor->pos.y + cursor->pos.h, promptPos.h); } + + auto prevGlyphPos = glyph->pos; + if ( false && !text->isDisabled() && !glyph->disabled ) + { + if ( CalloutMenu[player.playernum].calloutMenuIsOpen() ) + { + auto glyphPathUnpressed = Input::inputs[player.playernum].getGlyphPathForBinding("Call Out", false); + auto glyphPathPressed = Input::inputs[player.playernum].getGlyphPathForBinding("Call Out", true); + + if ( ticks % 50 < 25 ) + { + glyphCallout->path = glyphPathPressed; + if ( auto imgGet = Image::get(glyphCallout->path.c_str()) ) + { + glyphCallout->disabled = false; + glyphCallout->pos = SDL_Rect{ 0, 0, (int)imgGet->getWidth(), (int)imgGet->getHeight() }; + glyphCallout->pos.y = std::max(icon->pos.y + icon->pos.h - 4, prevGlyphPos.y + prevGlyphPos.h) + 4; + glyphCallout->pos.x = prevGlyphPos.x + prevGlyphPos.w / 2 - glyphCallout->pos.w / 2; + if ( glyphCallout->pos.x % 2 == 1 ) + { + ++glyphCallout->pos.x; + } + if ( auto imgGetUnpressed = Image::get(glyphPathUnpressed.c_str()) ) + { + const int unpressedHeight = imgGetUnpressed->getHeight(); + if ( unpressedHeight != glyphCallout->pos.h ) + { + glyphCallout->pos.y -= (glyphCallout->pos.h - unpressedHeight); + } + + /*if ( unpressedHeight != nominalGlyphHeight ) + { + prevGlyphPos.y -= (unpressedHeight - nominalGlyphHeight) / 2; + }*/ + } + textPos.x += prevGlyphPos.w; + } + } + else + { + glyphCallout->path = glyphPathUnpressed; + if ( auto imgGet = Image::get(glyphCallout->path.c_str()) ) + { + glyphCallout->disabled = false; + glyphCallout->pos = SDL_Rect{ 0, 0, (int)imgGet->getWidth(), (int)imgGet->getHeight() }; + glyphCallout->pos.y = std::max(icon->pos.y + icon->pos.h - 4, prevGlyphPos.y + prevGlyphPos.h) + 4; + glyphCallout->pos.x = prevGlyphPos.x + prevGlyphPos.w / 2 - glyphCallout->pos.w / 2; + if ( glyphCallout->pos.x % 2 == 1 ) + { + ++glyphCallout->pos.x; + } + } + } + + if ( !glyphCallout->disabled ) + { + glyphCallout->color = makeColor(255, 255, 255, 255); + textCallout->setText(Language::get(6049)); + textCallout->setColor(makeColor(255, 255, 255, 255)); + SDL_Rect textPos = text->getSize(); + textPos.x = glyphCallout->pos.x + glyphCallout->pos.w + 4; + textPos.y = glyphCallout->pos.y + 2; + + if ( auto imgGet = Image::get(glyphPathPressed.c_str()) ) + { + if ( imgGet->getHeight() != glyphCallout->pos.h ) + { + textPos.y += (glyphCallout->pos.h - imgGet->getHeight()) / 2; + } + } + if ( glyphCallout->pos.h != nominalGlyphHeight ) + { + textPos.y += (glyphCallout->pos.h - nominalGlyphHeight) / 2; + } + textPos.w = textCallout->getTextObject()->getWidth(); + textCallout->setSize(textPos); + textCallout->setDisabled(false); + + promptPos.w = std::max(textPos.x + textPos.w, promptPos.w); + promptPos.h = std::max(textPos.y + textPos.h, promptPos.h); + promptPos.w = std::max(glyphCallout->pos.x + glyphCallout->pos.w, promptPos.w); + promptPos.h = std::max(glyphCallout->pos.y + glyphCallout->pos.h, promptPos.h); + + prevGlyphPos = glyphCallout->pos; + } + } + } + if ( player.worldUI.isEnabled() ) { if ( !text->isDisabled() && !glyph->disabled && player.worldUI.tooltipsInRange.size() > 1 ) @@ -9024,8 +9311,8 @@ void Player::HUD_t::updateWorldTooltipPrompts() { glyphCycle->disabled = false; glyphCycle->pos = SDL_Rect{ 0, 0, (int)imgGet->getWidth(), (int)imgGet->getHeight() }; - glyphCycle->pos.y = glyph->pos.y + glyph->pos.h + 4; - glyphCycle->pos.x = glyph->pos.x + glyph->pos.w / 2 - glyphCycle->pos.w / 2; + glyphCycle->pos.y = prevGlyphPos.y + prevGlyphPos.h + 4; + glyphCycle->pos.x = prevGlyphPos.x + prevGlyphPos.w / 2 - glyphCycle->pos.w / 2; if ( glyphCycle->pos.x % 2 == 1 ) { ++glyphCycle->pos.x; @@ -9043,7 +9330,7 @@ void Player::HUD_t::updateWorldTooltipPrompts() glyph->pos.y -= (unpressedHeight - nominalGlyphHeight) / 2; }*/ } - textPos.x += glyph->pos.w; + textPos.x += prevGlyphPos.w; } } else @@ -9053,8 +9340,8 @@ void Player::HUD_t::updateWorldTooltipPrompts() { glyphCycle->disabled = false; glyphCycle->pos = SDL_Rect{ 0, 0, (int)imgGet->getWidth(), (int)imgGet->getHeight() }; - glyphCycle->pos.y = glyph->pos.y + glyph->pos.h + 4; - glyphCycle->pos.x = glyph->pos.x + glyph->pos.w / 2 - glyphCycle->pos.w / 2; + glyphCycle->pos.y = prevGlyphPos.y + prevGlyphPos.h + 4; + glyphCycle->pos.x = prevGlyphPos.x + prevGlyphPos.w / 2 - glyphCycle->pos.w / 2; if ( glyphCycle->pos.x % 2 == 1 ) { ++glyphCycle->pos.x; @@ -9984,6 +10271,10 @@ void Player::HUD_t::processHUD() { createHPMPBars(player.playernum); } + if ( !calloutPromptFrame ) + { + createCalloutPromptFrame(player.playernum); + } if ( !enemyBarFrame ) { createEnemyBar(player.playernum, enemyBarFrame); @@ -10016,6 +10307,7 @@ void Player::HUD_t::processHUD() updateXPBar(); updateHPBar(); updateMPBar(); + updateCalloutPromptFrame(player.playernum); updateActionPrompts(); updateUINavigation(); enemyHPDamageBarHandler[player.playernum].cullExpiredHPBars(); @@ -10226,6 +10518,10 @@ void Player::MessageZone_t::processChatbox() { SDL_Rect xpFramePos = player.hud.xpFrame->getSize(); topAlignedPaddingY = std::max(topAlignedPaddingY, 4 + std::max(xpFramePos.y, 0) + xpFramePos.h); + if ( !(player.bUseCompactGUIHeight() && player.bUseCompactGUIWidth()) ) + { + topAlignedPaddingY += 4; + } } SDL_Rect messageboxTopAlignedPos{ topAlignedPaddingX, @@ -20913,19 +21209,20 @@ void drawCharacterPreview(const int player, SDL_Rect pos, int fov, real_t offset ::fov = fov; //TempTexture* minimapTexture = new TempTexture(); + auto playerEntity = Player::getPlayerInteractEntity(player); - if ( players[player] != nullptr && players[player]->entity != nullptr ) + if ( playerEntity ) { GL_CHECK_ERR(glClear(GL_DEPTH_BUFFER_BIT)); static ConsoleVariable cvar_char_portrait_static_angle("/char_portrait_static_angle", true); - view.x = players[player]->entity->x / 16.0 + (.92 * cos(offsetyaw - + (*cvar_char_portrait_static_angle ? players[player]->entity->yaw : 0))); - view.y = players[player]->entity->y / 16.0 + (.92 * sin(offsetyaw - + (*cvar_char_portrait_static_angle ? players[player]->entity->yaw : 0))); - view.z = players[player]->entity->z * 2; + view.x = playerEntity->x / 16.0 + (.92 * cos(offsetyaw + + (*cvar_char_portrait_static_angle ? playerEntity->yaw : 0))); + view.y = playerEntity->y / 16.0 + (.92 * sin(offsetyaw + + (*cvar_char_portrait_static_angle ? playerEntity->yaw : 0))); + view.z = playerEntity->z * 2; view.ang = (offsetyaw - PI - + (*cvar_char_portrait_static_angle ? players[player]->entity->yaw : 0)); //5 * PI / 4; + + (*cvar_char_portrait_static_angle ? playerEntity->yaw : 0)); //5 * PI / 4; view.vang = PI / 20; view.winx = pos.x; @@ -20937,22 +21234,25 @@ void drawCharacterPreview(const int player, SDL_Rect pos, int fov, real_t offset view.winw = pos.w; view.winh = pos.h; glBeginCamera(&view, false); - bool b = players[player]->entity->flags[BRIGHT]; - if (!dark) { players[player]->entity->flags[BRIGHT] = true; } - if ( !players[player]->entity->flags[INVISIBLE] ) + bool b = playerEntity->flags[BRIGHT]; + if (!dark) { playerEntity->flags[BRIGHT] = true; } + if ( !playerEntity->flags[INVISIBLE] ) { - glDrawVoxel(&view, players[player]->entity, REALCOLORS); + glDrawVoxel(&view, playerEntity, REALCOLORS); } - players[player]->entity->flags[BRIGHT] = b; + playerEntity->flags[BRIGHT] = b; int c = 0; if ( multiplayer != CLIENT ) { - for ( node_t* node = players[player]->entity->children.first; node != nullptr; node = node->next ) + for ( node_t* node = playerEntity->children.first; node != nullptr; node = node->next ) { - if ( c == 0 ) + if ( playerEntity->behavior == &actPlayer ) { - c++; - continue; + if ( c == 0 ) + { + c++; + continue; + } } Entity* entity = (Entity*)node->element; if ( !entity->flags[INVISIBLE] ) @@ -20981,21 +21281,34 @@ void drawCharacterPreview(const int player, SDL_Rect pos, int fov, real_t offset for ( node_t* node = map.entities->first; node != NULL; node = node->next ) { Entity* entity = (Entity*)node->element; - if ( (entity->behavior == &actPlayerLimb && entity->skill[2] == player && !entity->flags[INVISIBLE]) || (Sint32)entity->getUID() == -4 ) + if ( playerEntity->behavior == &actPlayer ) { - if ( (Sint32)entity->getUID() == -4 ) // torch sprites + if ( (entity->behavior == &actPlayerLimb && entity->skill[2] == player && !entity->flags[INVISIBLE]) || (Sint32)entity->getUID() == -4 ) { - bool b = entity->flags[BRIGHT]; - if (!dark) { entity->flags[BRIGHT] = true; } - glDrawSprite(&view, entity, REALCOLORS); - entity->flags[BRIGHT] = b; + if ( (Sint32)entity->getUID() == -4 ) // torch sprites + { + bool b = entity->flags[BRIGHT]; + if (!dark) { entity->flags[BRIGHT] = true; } + glDrawSprite(&view, entity, REALCOLORS); + entity->flags[BRIGHT] = b; + } + else + { + bool b = entity->flags[BRIGHT]; + if (!dark) { entity->flags[BRIGHT] = true; } + glDrawVoxel(&view, entity, REALCOLORS); + entity->flags[BRIGHT] = b; + } } - else + } + else if ( playerEntity->behavior == &actDeathGhost ) + { + if ( entity->behavior == &actDeathGhostLimb && entity->skill[2] == player && !entity->flags[INVISIBLE] ) { - bool b = entity->flags[BRIGHT]; - if (!dark) { entity->flags[BRIGHT] = true; } + bool b = entity->flags[BRIGHT]; + if ( !dark ) { entity->flags[BRIGHT] = true; } glDrawVoxel(&view, entity, REALCOLORS); - entity->flags[BRIGHT] = b; + entity->flags[BRIGHT] = b; } } } diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 4d7772ddd..15a0e4162 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -114,7 +114,7 @@ namespace MainMenu { {"Spell List", "B", hiddenBinding, emptyBinding}, {"Skill Sheet", "K", hiddenBinding, emptyBinding}, {"Autosort Inventory", "R", hiddenBinding, emptyBinding}, - {"Show Player Callouts", "X", "DpadY+", emptyBinding}, + {"Call Out", "X", "ButtonLeftStick", emptyBinding}, {"Command NPC", "Q", "DpadX-", emptyBinding}, {"Show NPC Commands", "C", "DpadX+", emptyBinding}, {"Cycle NPCs", "E", "DpadY-", emptyBinding}, @@ -174,7 +174,7 @@ namespace MainMenu { {"Spell List", "B", hiddenBinding, emptyBinding}, {"Skill Sheet", "K", hiddenBinding, emptyBinding}, {"Autosort Inventory", "R", hiddenBinding, emptyBinding}, - {"Show Player Callouts", "X", "DpadY+", emptyBinding}, + {"Call Out", "X", "ButtonA", emptyBinding}, {"Command NPC", "Q", "DpadX-", emptyBinding}, {"Show NPC Commands", "C", "DpadX+", emptyBinding}, {"Cycle NPCs", "E", "DpadY-", emptyBinding}, @@ -234,7 +234,7 @@ namespace MainMenu { {"Spell List", "B", hiddenBinding, emptyBinding}, {"Skill Sheet", "K", hiddenBinding, emptyBinding}, {"Autosort Inventory", "R", hiddenBinding, emptyBinding}, - {"Show Player Callouts", "X", "DpadY+", emptyBinding}, + {"Call Out", "X", "ButtonLeftStick", emptyBinding}, #ifdef NINTENDO {"Command NPC", "Q", "ButtonY", emptyBinding}, {"Show NPC Commands", "C", "ButtonX", emptyBinding}, @@ -294,7 +294,7 @@ namespace MainMenu { {"Spell List", "B", hiddenBinding, emptyBinding}, {"Skill Sheet", "K", hiddenBinding, emptyBinding}, {"Autosort Inventory", "R", hiddenBinding, emptyBinding}, - {"Show Player Callouts", "X", emptyBinding, emptyBinding}, + {"Call Out", "X", emptyBinding, emptyBinding}, {"Command NPC", "Q", emptyBinding, emptyBinding}, {"Show NPC Commands", "C", emptyBinding, emptyBinding}, {"Cycle NPCs", "E", emptyBinding, emptyBinding}, @@ -4532,7 +4532,7 @@ namespace MainMenu { static const char* translateBinding(const char* binding) { int c = 5970; - if ( !strcmp(binding, "Show Player Callouts") ) + if ( !strcmp(binding, "Call Out") ) { return Language::get(6044); } @@ -4541,7 +4541,7 @@ namespace MainMenu { if (b.action == binding) { break; } - if ( b.action == "Show Player Callouts" ) + if ( b.action == "Call Out" ) { continue; // don't increment c, not in the linear language entries } From 81d78fcda2ad40dc91a3502a1d86a11ba8d1683b Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 4 Oct 2023 11:46:09 -0700 Subject: [PATCH 081/146] fix language file loaded before config file loaded Signed-off-by: SheridanR --- src/game.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 77b17cd8e..5363b2c41 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -6528,17 +6528,6 @@ int main(int argc, char** argv) } printlog("Data path is %s", datadir); printlog("Output path is %s", outputdir); - - // load default language file (english) - if ( Language::loadLanguage("en", true) ) - { - printlog("Fatal error: failed to load default language file!\n"); - if (logfile) - { - fclose(logfile); - } - exit(1); - } // init sdl Uint32 init_flags = SDL_INIT_VIDEO | SDL_INIT_EVENTS; @@ -6549,9 +6538,9 @@ int main(int argc, char** argv) return 1; } + // load game config Input::defaultBindings(); MainMenu::randomizeUsername(); - MainMenu::settingsReset(); MainMenu::settingsApply(); bool load_successful = MainMenu::settingsLoad(); @@ -6562,6 +6551,17 @@ int main(int argc, char** argv) skipintro = false; } + // load default language file (english) + if ( Language::loadLanguage("en", true) ) + { + printlog("Fatal error: failed to load default language file!\n"); + if (logfile) + { + fclose(logfile); + } + exit(1); + } + // initialize map map.tiles = nullptr; map.entities = (list_t*) malloc(sizeof(list_t)); From ce18c182742338019dd42a1641aa53795d74f843 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 4 Oct 2023 13:25:37 -0700 Subject: [PATCH 082/146] fix bugs with fog Signed-off-by: SheridanR --- src/draw.cpp | 2 +- src/init.cpp | 3 +++ src/opengl.cpp | 24 ++++++++++++++++++++++-- src/ui/Frame.cpp | 4 ++++ src/ui/LoadingScreen.cpp | 1 + 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/draw.cpp b/src/draw.cpp index 80df4ee3d..90dd4ae1c 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -1649,8 +1649,8 @@ void drawForeground(long camx, long camy) void drawClearBuffers() { + GL_CHECK_ERR(glClearColor(0.f, 0.f, 0.f, 1.f)); GL_CHECK_ERR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); - drawRect(NULL, 0, 255); } /*------------------------------------------------------------------------------- diff --git a/src/init.cpp b/src/init.cpp index 359e9382a..decaacf06 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -453,6 +453,7 @@ int initApp(char const * const title, int fullscreen) if (!hdrEnabled) { main_framebuffer.bindForWriting(); } + GL_CHECK_ERR(glClearColor(0.f, 0.f, 0.f, 1.f)); GL_CHECK_ERR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); //SDL_EnableUNICODE(1); @@ -1696,6 +1697,7 @@ bool changeVideoMode(int new_xres, int new_yres) if (!hdrEnabled) { main_framebuffer.bindForWriting(); } + GL_CHECK_ERR(glClearColor(0.f, 0.f, 0.f, 1.f)); GL_CHECK_ERR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); // success @@ -1734,6 +1736,7 @@ bool resizeWindow(int new_xres, int new_yres) if (!hdrEnabled) { main_framebuffer.bindForWriting(); } + GL_CHECK_ERR(glClearColor(0.f, 0.f, 0.f, 1.f)); GL_CHECK_ERR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); // success diff --git a/src/opengl.cpp b/src/opengl.cpp index 8e5d03b03..1d07d06e6 100644 --- a/src/opengl.cpp +++ b/src/opengl.cpp @@ -677,6 +677,11 @@ void beginGraphics() { updateChunks(); } +#ifndef EDITOR +static ConsoleVariable cvar_fogDistance("/fog_distance", 0.f); +static ConsoleVariable cvar_fogColor("/fog_color", {0.f, 0.f, 0.f, 0.f}); +#endif + static void uploadUniforms(Shader& shader, float* proj, float* view, float* mapDims) { shader.bind(); if (proj) { GL_CHECK_ERR(glUniformMatrix4fv(shader.uniform("uProj"), 1, false, proj)); } @@ -689,8 +694,6 @@ static void uploadUniforms(Shader& shader, float* proj, float* view, float* mapD GL_CHECK_ERR(glUniform4fv(shader.uniform("uFogColor"), 1, fogColor)); GL_CHECK_ERR(glUniform4fv(shader.uniform("uFogDistance"), 1, fogDistance)); #else - static ConsoleVariable cvar_fogDistance("/fog_distance", 0.f); - static ConsoleVariable cvar_fogColor("/fog_color", {1.f, 1.f, 1.f, 1.f}); GL_CHECK_ERR(glUniform4fv(shader.uniform("uFogColor"), 1, (float*)&*cvar_fogColor)); GL_CHECK_ERR(glUniform1f(shader.uniform("uFogDistance"), *cvar_fogDistance)); #endif @@ -918,8 +921,10 @@ void glBeginCamera(view_t* camera, bool useHDR) // setup viewport #ifdef EDITOR const bool hdr = useHDR; + const auto fog_color = Vector4{0.f, 0.f, 0.f, 0.f}; #else const bool hdr = useHDR ? *MainMenu::cvar_hdrEnabled : false; + const auto& fog_color = *cvar_fogColor; #endif if (hdr) { @@ -927,6 +932,7 @@ void glBeginCamera(view_t* camera, bool useHDR) const int fbIndex = camera->drawnFrames % numFbs; camera->fb[fbIndex].init(camera->winw, camera->winh, GL_LINEAR, GL_LINEAR); camera->fb[fbIndex].bindForWriting(); + GL_CHECK_ERR(glClearColor(fog_color.x, fog_color.y, fog_color.z, fog_color.w)); GL_CHECK_ERR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); GL_CHECK_ERR(glScissor(0, 0, camera->winw, camera->winh)); } else { @@ -1222,6 +1228,8 @@ void glDrawVoxel(view_t* camera, Entity* entity, int mode) { GL_CHECK_ERR(glUniform4fv(shader.uniform("uLightColor"), 1, light)); const GLfloat empty[4] = { 0.f, 0.f, 0.f, 0.f }; GL_CHECK_ERR(glUniform4fv(shader.uniform("uColorAdd"), 1, empty)); + const float cameraPos[4] = {(float)camera->x * 32.f, -(float)camera->z, (float)camera->y * 32.f, 1.f}; + GL_CHECK_ERR(glUniform4fv(shader.uniform("uCameraPos"), 1, cameraPos)); } else { uploadLightUniforms(camera, shader, entity, mode, true); } @@ -1385,6 +1393,8 @@ void glDrawEnemyBarSprite(view_t* camera, int mode, int playerViewport, void* en GL_CHECK_ERR(glUniform4fv(shader.uniform("uLightColor"), 1, light)); const GLfloat empty[4] = { 0.f, 0.f, 0.f, 0.f }; GL_CHECK_ERR(glUniform4fv(shader.uniform("uColorAdd"), 1, empty)); + const float cameraPos[4] = {(float)camera->x * 32.f, -(float)camera->z, (float)camera->y * 32.f, 1.f}; + GL_CHECK_ERR(glUniform4fv(shader.uniform("uCameraPos"), 1, cameraPos)); // draw spriteMesh.draw(); @@ -1485,6 +1495,8 @@ void glDrawWorldDialogueSprite(view_t* camera, void* worldDialogue, int mode) GL_CHECK_ERR(glUniform4fv(shader.uniform("uLightColor"), 1, light)); const GLfloat empty[4] = { 0.f, 0.f, 0.f, 0.f }; GL_CHECK_ERR(glUniform4fv(shader.uniform("uColorAdd"), 1, empty)); + const float cameraPos[4] = {(float)camera->x * 32.f, -(float)camera->z, (float)camera->y * 32.f, 1.f}; + GL_CHECK_ERR(glUniform4fv(shader.uniform("uCameraPos"), 1, cameraPos)); // draw spriteMesh.draw(); @@ -1622,6 +1634,8 @@ void glDrawWorldUISprite(view_t* camera, Entity* entity, int mode) GL_CHECK_ERR(glUniform4fv(shader.uniform("uLightColor"), 1, light)); const GLfloat empty[4] = { 0.f, 0.f, 0.f, 0.f }; GL_CHECK_ERR(glUniform4fv(shader.uniform("uColorAdd"), 1, empty)); + const float cameraPos[4] = {(float)camera->x * 32.f, -(float)camera->z, (float)camera->y * 32.f, 1.f}; + GL_CHECK_ERR(glUniform4fv(shader.uniform("uCameraPos"), 1, cameraPos)); // draw spriteMesh.draw(); @@ -1712,6 +1726,8 @@ void glDrawSprite(view_t* camera, Entity* entity, int mode) GL_CHECK_ERR(glUniform4fv(shader.uniform("uLightColor"), 1, light)); const GLfloat empty[4] = { 0.f, 0.f, 0.f, 0.f }; GL_CHECK_ERR(glUniform4fv(shader.uniform("uColorAdd"), 1, empty)); + const float cameraPos[4] = {(float)camera->x * 32.f, -(float)camera->z, (float)camera->y * 32.f, 1.f}; + GL_CHECK_ERR(glUniform4fv(shader.uniform("uCameraPos"), 1, cameraPos)); } else { uploadLightUniforms(camera, shader, entity, mode, false); } @@ -1831,6 +1847,8 @@ void glDrawSpriteFromImage(view_t* camera, Entity* entity, std::string text, int GL_CHECK_ERR(glUniform4fv(shader.uniform("uLightColor"), 1, light)); const GLfloat empty[4] = { 0.f, 0.f, 0.f, 0.f }; GL_CHECK_ERR(glUniform4fv(shader.uniform("uColorAdd"), 1, empty)); + const float cameraPos[4] = {(float)camera->x * 32.f, -(float)camera->z, (float)camera->y * 32.f, 1.f}; + GL_CHECK_ERR(glUniform4fv(shader.uniform("uCameraPos"), 1, cameraPos)); // draw spriteMesh.draw(); @@ -2117,6 +2135,7 @@ unsigned int GO_GetPixelU32(int x, int y, view_t& camera) } if (dirty) { + GL_CHECK_ERR(glClearColor(0.f, 0.f, 0.f, 0.f)); GL_CHECK_ERR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); glBeginCamera(&camera, false); glDrawWorld(&camera, ENTITYUIDS); @@ -2175,6 +2194,7 @@ void GO_SwapBuffers(SDL_Window* screen) if (!hdrEnabled) { main_framebuffer.unbindForWriting(); main_framebuffer.bindForReading(); + GL_CHECK_ERR(glClearColor(0.f, 0.f, 0.f, 0.f)); GL_CHECK_ERR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); main_framebuffer.draw(vidgamma); } diff --git a/src/ui/Frame.cpp b/src/ui/Frame.cpp index 1d0c13d42..6eb568d33 100644 --- a/src/ui/Frame.cpp +++ b/src/ui/Frame.cpp @@ -306,6 +306,7 @@ void Frame::predraw() { } gui_fb.bindForWriting(); + GL_CHECK_ERR(glClearColor(0.f, 0.f, 0.f, 0.f)); GL_CHECK_ERR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); GL_CHECK_ERR(glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); } @@ -327,6 +328,7 @@ void Frame::postdraw() { gui_fb.bindForReading(); if (*ui_downscale) { gui_fb_downscaled.bindForWriting(); + GL_CHECK_ERR(glClearColor(0.f, 0.f, 0.f, 0.f)); GL_CHECK_ERR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); gui_fb.draw(); gui_fb_downscaled.unbindForWriting(); @@ -335,6 +337,7 @@ void Frame::postdraw() { } else if (*ui_upscale) { gui_fb_upscaled.bindForWriting(); + GL_CHECK_ERR(glClearColor(0.f, 0.f, 0.f, 0.f)); GL_CHECK_ERR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); gui_fb.draw(); gui_fb_upscaled.unbindForWriting(); @@ -358,6 +361,7 @@ void Frame::predraw() { return; } gui_fb.bindForWriting(); + GL_CHECK_ERR(glClearColor(0.f, 0.f, 0.f, 0.f)); GL_CHECK_ERR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); } diff --git a/src/ui/LoadingScreen.cpp b/src/ui/LoadingScreen.cpp index 5b3694824..fe83c3bf9 100644 --- a/src/ui/LoadingScreen.cpp +++ b/src/ui/LoadingScreen.cpp @@ -106,6 +106,7 @@ static void baseCreateLoadingScreen(real_t progress, const char* background_imag // create framebuffer for background loading_fb.init(xres, yres, GL_LINEAR, GL_LINEAR); loading_fb.bindForWriting(); + GL_CHECK_ERR(glClearColor(0.f, 0.f, 0.f, 0.f)); GL_CHECK_ERR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); drawAllPlayerCameras(); loading_fb.unbindForWriting(); From 8d915ab4bc2373cf757dd481b47856e55182a4d4 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 5 Oct 2023 07:49:11 +1100 Subject: [PATCH 083/146] * more ghost fixes, spawn animation etc --- src/actplayer.cpp | 426 +++++++++++++++++++++++++++++++------------- src/net.cpp | 18 +- src/player.hpp | 6 + src/ui/MainMenu.cpp | 32 ++++ 4 files changed, 360 insertions(+), 122 deletions(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index b7372d1c7..c7dca7fcc 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -57,6 +57,8 @@ static ConsoleVariable cvar_calloutStartZLimit("/callout_start_z_limit", #define GHOSTCAM_SQUISH_TIME my->skill[5] #define GHOSTCAM_SQUISH_DELAY my->skill[6] #define GHOSTCAM_DEACTIVATED my->skill[7] +#define GHOSTCAM_SPAWN_ANIM my->skill[8] +#define GHOSTCAM_SPAWN_ANIM_COMPLETE my->skill[9] #define GHOSTCAM_BOB my->fskill[0] #define GHOSTCAM_BOBMOVE my->fskill[1] #define GHOSTCAM_DX my->fskill[3] @@ -160,7 +162,7 @@ void Player::Ghost_t::handleGhostMovement(const bool useRefreshRateDelta) } // calculate movement forces - bool allowMovement = true; + bool allowMovement = isControllable(); static ConsoleVariable cvar_ghostSpeed("/ghost_speed", 1.5); static ConsoleVariable cvar_ghostDrag("/ghost_drag", 0.95); @@ -393,7 +395,8 @@ void Player::Ghost_t::handleAttack() bool defending = false; if ( !player.usingCommand() && player.bControlEnabled - && !gamePaused ) + && !gamePaused + && isControllable() ) { if ( player.shootmode ) { @@ -700,6 +703,11 @@ void Player::Ghost_t::handleAttack() void Player::Ghost_t::handleActions() { + if ( !isControllable() ) + { + return; + } + Input& input = Input::inputs[player.playernum]; CalloutRadialMenu& calloutMenu = CalloutMenu[player.playernum]; auto& b = (multiplayer != SINGLE && player.playernum != 0) ? Input::inputs[0].getBindings() : input.getBindings(); @@ -1122,10 +1130,137 @@ void Player::Ghost_t::createBounceAnimate() GHOSTCAM_SQUISH_ANGLE = Player::Ghost_t::GHOST_SQUISH_START_ANGLE / 100.f; } +bool Player::Ghost_t::isControllable() +{ + if ( !my ) { return false; } + + if ( GHOSTCAM_SPAWN_ANIM_COMPLETE == 1 ) + { + return true; + } + return false; +} + +void Player::Ghost_t::pauseMenuSpectate(const int player) +{ + if ( !players[player]->isLocalPlayer() ) + { + return; + } + + if ( players[player]->ghost.my ) + { + players[player]->ghost.setActive(false); + } +} + +void Player::Ghost_t::pauseMenuSpawnGhost(const int player) +{ + if ( !players[player]->isLocalPlayer() ) + { + return; + } + + if ( players[player]->ghost.my ) + { + players[player]->ghost.setActive(true); + } + else + { + players[player]->ghost.spawnGhost(); + } +} + +bool Player::Ghost_t::gamemodeAllowsGhosts() +{ + if ( gameModeManager.getMode() == GameModeManager_t::GameModes::GAME_MODE_TUTORIAL ) + { + return false; + } + if ( multiplayer != SINGLE + || (multiplayer == SINGLE && splitscreen) ) + { + return true; + } + return false; +} + +bool Player::Ghost_t::gameoverOnDismiss(const int player) +{ + if ( !players[player]->isLocalPlayer() ) + { + return false; + } + + if ( gamemodeAllowsGhosts() ) + { + pauseMenuSpawnGhost(player); + return true; + } + return false; +} + +Entity* Player::Ghost_t::spawnGhost() +{ + if ( !player.isLocalPlayer() ) + { + return nullptr; + } + + if ( multiplayer != CLIENT ) + { + int sprite = Player::Ghost_t::getSpriteForPlayer(player.playernum); + Entity* entity = newEntity(sprite, 1, map.entities, nullptr); //Ghost entity. + entity->x = spawnX * 16.0 + 8; + entity->y = spawnY * 16.0 + 8; + entity->z = -4; + entity->flags[PASSABLE] = true; + entity->flags[INVISIBLE] = true; + entity->flags[GENIUS] = true; + entity->behavior = &actDeathGhost; + entity->skill[2] = player.playernum; + entity->sizex = 2; + entity->sizey = 2; + entity->yaw = 0.0; + entity->pitch = PI / 16; + if ( player.playernum == clientnum && multiplayer == CLIENT ) + { + entity->flags[UPDATENEEDED] = false; + } + else + { + entity->flags[UPDATENEEDED] = true; + } + players[player.playernum]->ghost.my = entity; + players[player.playernum]->ghost.uid = entity->getUID(); + return entity; + } + + if ( multiplayer == CLIENT ) + { + strcpy((char*)net_packet->data, "GHOS"); + net_packet->data[4] = player.playernum; + net_packet->data[5] = currentlevel; + + int x = (spawnX); + int y = (spawnY); + SDLNet_Write16((Sint16)(x), &net_packet->data[6]); + SDLNet_Write16((Sint16)(y), &net_packet->data[8]); + net_packet->data[10] = secretlevel; + net_packet->address.host = net_server.host; + net_packet->address.port = net_server.port; + net_packet->len = 11; + sendPacketSafe(net_sock, -1, net_packet, 0); + } + return nullptr; +} + void Player::Ghost_t::handleGhostCameraUpdate(const bool useRefreshRateDelta) { if ( !my ) { return; } + bool controllable = isControllable(); + real_t mousex_relative = mousexrel; real_t mousey_relative = mouseyrel; @@ -1149,7 +1284,7 @@ void Player::Ghost_t::handleGhostCameraUpdate(const bool useRefreshRateDelta) if ( player.shootmode && !player.usingCommand() && !gamePaused && player.bControlEnabled - && player.hotbar.faceMenuButtonHeld == Player::Hotbar_t::FaceMenuGroup::GROUP_NONE ) + && controllable ) { if ( Input::inputs[player.playernum].consumeBinaryToggle("Quick Turn") ) { @@ -1158,7 +1293,7 @@ void Player::Ghost_t::handleGhostCameraUpdate(const bool useRefreshRateDelta) } // rotate - if ( !player.usingCommand() + if ( !player.usingCommand() && controllable && player.bControlEnabled && !gamePaused && my->isMobile() && !inputs.hasController(player.playernum) ) { if ( noclip ) @@ -1178,7 +1313,7 @@ void Player::Ghost_t::handleGhostCameraUpdate(const bool useRefreshRateDelta) { // do nothing, override rotations. } - else if ( shootmode && !gamePaused ) + else if ( shootmode && !gamePaused && controllable ) { if ( smoothmouse ) { @@ -1232,37 +1367,44 @@ void Player::Ghost_t::handleGhostCameraUpdate(const bool useRefreshRateDelta) } // look up and down - if ( !player.usingCommand() - && player.bControlEnabled && !gamePaused && my->isMobile() && !inputs.hasController(player.playernum) ) - { - my->pitch += (Input::inputs[player.playernum].analog("Look Down") - - Input::inputs[player.playernum].analog("Look Up")) * .05 * refreshRateDelta; - } - if ( shootmode && !gamePaused ) + if ( controllable ) { - if ( smoothmouse ) + if ( !player.usingCommand() + && player.bControlEnabled && !gamePaused && my->isMobile() && !inputs.hasController(player.playernum) ) { - if ( my->isMobile() ) - { - GHOSTCAM_ROTY += mousey_relative * .006 * (mouse_speed / 128.f) * (reversemouse * 2 - 1); - } - GHOSTCAM_ROTY = fmin(fmax(-0.35, GHOSTCAM_ROTY), 0.35); - GHOSTCAM_ROTY *= pow(0.5, refreshRateDelta); + my->pitch += (Input::inputs[player.playernum].analog("Look Down") + - Input::inputs[player.playernum].analog("Look Up")) * .05 * refreshRateDelta; } - else + if ( shootmode && !gamePaused ) { - if ( my->isMobile() ) + if ( smoothmouse ) { - GHOSTCAM_ROTY = std::min(std::max(-0.35f, - mousey_relative * .01f * (mouse_speed / 128.f) * (reversemouse * 2 - 1)), 0.35f); + if ( my->isMobile() ) + { + GHOSTCAM_ROTY += mousey_relative * .006 * (mouse_speed / 128.f) * (reversemouse * 2 - 1); + } + GHOSTCAM_ROTY = fmin(fmax(-0.35, GHOSTCAM_ROTY), 0.35); + GHOSTCAM_ROTY *= pow(0.5, refreshRateDelta); } else { - GHOSTCAM_ROTY = 0; + if ( my->isMobile() ) + { + GHOSTCAM_ROTY = std::min(std::max(-0.35f, + mousey_relative * .01f * (mouse_speed / 128.f) * (reversemouse * 2 - 1)), 0.35f); + } + else + { + GHOSTCAM_ROTY = 0; + } } } + my->pitch -= GHOSTCAM_ROTY * refreshRateDelta; + } + else + { + my->pitch = PI / 16; } - my->pitch -= GHOSTCAM_ROTY * refreshRateDelta; if ( my->pitch > PI / 3 ) { @@ -1454,9 +1596,50 @@ void actDeathGhost(Entity* my) node->size = sizeof(Entity*); my->bodyparts.push_back(entity); - players[playernum]->ghost.initTeleportLocations(my->x / 16, my->y / 16); + if ( multiplayer == SERVER ) + { + players[playernum]->ghost.initTeleportLocations(my->x / 16, my->y / 16); + } + } + + if ( GHOSTCAM_DEACTIVATED ) + { + GHOSTCAM_SPAWN_ANIM = 0; + GHOSTCAM_SPAWN_ANIM_COMPLETE = 0; + } + + const Sint32 spawnAnimationDuration = TICKS_PER_SECOND * 1.2; + const Sint32 spawnAnimationHalfway = TICKS_PER_SECOND * 0.8; + const bool spawnAnimationPlaying = GHOSTCAM_SPAWN_ANIM < spawnAnimationDuration; + float floatAnimationPercent = std::min(1.f, (float)GHOSTCAM_SPAWN_ANIM / (spawnAnimationHalfway)); + float thirdPersonAnimationPercent = 0.f; + if ( spawnAnimationPlaying ) + { + thirdPersonAnimationPercent = 1.f; + if ( GHOSTCAM_SPAWN_ANIM > (spawnAnimationHalfway) ) + { + thirdPersonAnimationPercent -= (GHOSTCAM_SPAWN_ANIM - (spawnAnimationHalfway)) / (float)(spawnAnimationDuration - (spawnAnimationHalfway)); + } + thirdPersonAnimationPercent = std::max(0.f, thirdPersonAnimationPercent); } + if ( GHOSTCAM_SPAWN_ANIM < spawnAnimationDuration ) + { + GHOSTCAM_SPAWN_ANIM++; + if ( GHOSTCAM_SPAWN_ANIM == 1 ) + { + playSoundEntityLocal(my, 167, 128); + createParticleDropRising(my, 174, 1.0); + } + if ( GHOSTCAM_SPAWN_ANIM == spawnAnimationHalfway ) + { + players[playernum]->ghost.createBounceAnimate(); + } + } + else + { + GHOSTCAM_SPAWN_ANIM_COMPLETE = 1; + } auto player = players[playernum]; @@ -1521,6 +1704,11 @@ void actDeathGhost(Entity* my) static ConsoleVariable cvar_ghostSquish("/ghost_squish", 6.f); static ConsoleVariable cvar_ghostSquishFactor("/ghost_squish_factor", 0.3f); + if ( player->isLocalPlayer() && !player->ghost.isActive() ) + { + my->vel_x = 0.0; + my->vel_y = 0.0; + } if ( player->isLocalPlayer() && player->ghost.isActive() ) { if ( autoLimbReload && ticks % 20 == 0 && (playernum == clientnum) ) @@ -1546,16 +1734,21 @@ void actDeathGhost(Entity* my) camvang = my->pitch; static ConsoleVariable cvar_ghostThirdPerson("/ghost_thirdperson", false); - if ( keystatus[SDLK_h] ) + /*if ( keystatus[SDLK_h] ) { keystatus[SDLK_h] = 0; player->ghost.setActive(false); - } - if ( *cvar_ghostThirdPerson || !keystatus[SDLK_g] ) + }*/ + if ( *cvar_ghostThirdPerson || spawnAnimationPlaying ) { - camx -= cos(my->yaw) * cos(my->pitch) * 1.5; - camy -= sin(my->yaw) * cos(my->pitch) * 1.5; - camz -= sin(my->pitch) * 16; + real_t dist = 1.0; + if ( spawnAnimationPlaying ) + { + dist = cos((1.0 - thirdPersonAnimationPercent) * PI / 2); + } + camx -= dist * cos(my->yaw) * cos(my->pitch) * 1.5; + camy -= dist * sin(my->yaw) * cos(my->pitch) * 1.5; + camz -= dist * sin(my->pitch) * 16; } if ( !TimerExperiments::bUseTimerInterpolation ) @@ -1653,6 +1846,7 @@ void actDeathGhost(Entity* my) if ( GHOSTCAM_SQUISH_ANGLE < 0.01 && GHOSTCAM_SQUISH_DELAY == 0 ) { GHOSTCAM_SQUISH_ANGLE = Player::Ghost_t::GHOST_SQUISH_START_ANGLE / 100.f; + playSoundEntityLocal(my, 612 + local_rng.rand() % 3, 64); GHOSTCAM_SQUISH_DELAY = TICKS_PER_SECOND / 2; } else @@ -1678,7 +1872,21 @@ void actDeathGhost(Entity* my) } if ( multiplayer == SERVER && bounceAnimate ) { - serverUpdateEntityFSkill(my, 9); + for ( int c = 1; c < MAXPLAYERS; ++c ) // send to other players + { + if ( client_disconnected[c] || players[c]->isLocalPlayer() ) + { + continue; + } + strcpy((char*)net_packet->data, "GHFS"); + SDLNet_Write32(player->ghost.my->getUID(), &net_packet->data[4]); + net_packet->data[8] = 9; + SDLNet_Write16(static_cast(player->ghost.my->fskill[9] * 256), &net_packet->data[9]); + net_packet->address.host = net_clients[c - 1].host; + net_packet->address.port = net_clients[c - 1].port; + net_packet->len = 11; + sendPacketSafe(net_sock, -1, net_packet, c - 1); + } } } @@ -1720,38 +1928,51 @@ void actDeathGhost(Entity* my) } //messagePlayer(0, MESSAGE_DEBUG, "%.2f", dir); - const real_t weaveSpeed = 0.05; - if ( dist < 0.15 || (abs(dir - PI / 2) < PI / 16) || (abs(dir - 3 * PI / 2) < PI / 16) ) + const bool animateGhostAsRotation = true; + if ( !animateGhostAsRotation ) { - if ( GHOSTCAM_WEAVE >= 0.0 ) + const real_t weaveSpeed = 0.05; + if ( dist < 0.15 || (abs(dir - PI / 2) < PI / 16) || (abs(dir - 3 * PI / 2) < PI / 16) ) { - GHOSTCAM_WEAVE -= weaveSpeed; - GHOSTCAM_WEAVE = std::max(GHOSTCAM_WEAVE, 0.0); + if ( GHOSTCAM_WEAVE >= 0.0 ) + { + GHOSTCAM_WEAVE -= weaveSpeed; + GHOSTCAM_WEAVE = std::max(GHOSTCAM_WEAVE, 0.0); + } + else + { + GHOSTCAM_WEAVE += weaveSpeed; + GHOSTCAM_WEAVE = std::min(GHOSTCAM_WEAVE, 0.0); + } } - else + else if ( dir <= PI / 2 || dir >= 3 * PI / 2 ) { GHOSTCAM_WEAVE += weaveSpeed; - GHOSTCAM_WEAVE = std::min(GHOSTCAM_WEAVE, 0.0); + GHOSTCAM_WEAVE = std::min(GHOSTCAM_WEAVE, PI / 8); + } + else if ( dir > PI / 2 && dir < 3 * PI / 2 ) + { + GHOSTCAM_WEAVE -= weaveSpeed; + GHOSTCAM_WEAVE = std::max(GHOSTCAM_WEAVE, -PI / 8); } } - else if ( dir <= PI / 2 || dir >= 3 * PI / 2 ) - { - GHOSTCAM_WEAVE += weaveSpeed; - GHOSTCAM_WEAVE = std::min(GHOSTCAM_WEAVE, PI / 8); - } - else if ( dir > PI / 2 && dir < 3 * PI / 2 ) + else { - GHOSTCAM_WEAVE -= weaveSpeed; - GHOSTCAM_WEAVE = std::max(GHOSTCAM_WEAVE, -PI / 8); + const real_t weaveSpeed = 0.1; + if ( spawnAnimationPlaying ) + { + GHOSTCAM_WEAVE += (weaveSpeed / 5) * (4.0 - 3.0 * floatAnimationPercent); + } + else + { + GHOSTCAM_WEAVE += weaveSpeed / 5; + } + while ( GHOSTCAM_WEAVE >= 1.0 ) + { + GHOSTCAM_WEAVE -= 1.0; + } } - - /*GHOSTCAM_WEAVE += weaveSpeed / 5; - while ( GHOSTCAM_WEAVE >= 1.0 ) - { - GHOSTCAM_WEAVE -= 1.0; - }*/ - if ( abs(GHOSTCAM_HOVER - (PI / 2)) < PI / 64 ) { GHOSTCAM_HOVER += PI / 320; @@ -1784,12 +2005,29 @@ void actDeathGhost(Entity* my) my->flags[INVISIBLE] = true; int index = -1; + for ( auto bodypart : my->bodyparts ) { ++index; - bodypart->yaw = my->yaw; - bodypart->pitch = my->pitch; + if ( animateGhostAsRotation ) + { + if ( index == 0 ) + { + bodypart->yaw = GHOSTCAM_WEAVE * 2 * PI; + bodypart->pitch = 0.0; + } + else + { + bodypart->yaw = my->yaw; + bodypart->pitch = my->pitch; + } + } + else + { + bodypart->yaw = my->yaw; + bodypart->pitch = my->pitch; + } bodypart->roll = my->roll; bodypart->x = my->x; bodypart->y = my->y; @@ -1799,10 +2037,18 @@ void actDeathGhost(Entity* my) bodypart->scalez = 1.0 + squish; bodypart->flags[INVISIBLE] = !player->ghost.isActive(); + if ( spawnAnimationPlaying ) + { + bodypart->z += 7.5 * std::max(0.0, (1.0 - 2 * floatAnimationPercent)); + } + if ( index == 0 ) { bodypart->z += 0.5 * sin(GHOSTCAM_HOVER); - bodypart->pitch += GHOSTCAM_WEAVE; + if ( !animateGhostAsRotation ) + { + bodypart->pitch += GHOSTCAM_WEAVE; + } bodypart->fskill[0] += PI / 64; while ( bodypart->fskill[0] >= 2 * PI ) { @@ -1823,7 +2069,6 @@ void actDeathGhost(Entity* my) #define DEATHCAM_IDLETIME my->skill[3] #define DEATHCAM_IDLEROTATEDIRYAW my->skill[4] #define DEATHCAM_IDLEROTATEPITCHINIT my->skill[5] -#define DEATHCAM_CREATEDGHOST my->skill[6] #define DEATHCAM_ROTX my->fskill[0] #define DEATHCAM_ROTY my->fskill[1] #define DEATHCAM_IDLEPITCH my->fskill[2] @@ -2022,65 +2267,6 @@ void actDeathCam(Entity* my) && (Input::inputs[DEATHCAM_PLAYERNUM].consumeBinaryToggle("Attack") || Input::inputs[DEATHCAM_PLAYERNUM].consumeBinaryToggle("MenuConfirm")) ) { - if ( DEATHCAM_CREATEDGHOST == 1 ) - { - if ( players[DEATHCAM_PLAYERNUM]->ghost.my ) - { - players[DEATHCAM_PLAYERNUM]->ghost.setActive(true); - } - } - - if ( DEATHCAM_CREATEDGHOST == 0 ) - { - DEATHCAM_CREATEDGHOST = 1; - - // deathcam - if ( multiplayer != CLIENT ) - { - int sprite = Player::Ghost_t::getSpriteForPlayer(DEATHCAM_PLAYERNUM); - Entity* entity = newEntity(sprite, 1, map.entities, nullptr); //Ghost entity. - entity->x = my->x; - entity->y = my->y; - entity->z = -4; - entity->flags[PASSABLE] = true; - entity->flags[INVISIBLE] = true; - entity->flags[GENIUS] = true; - entity->behavior = &actDeathGhost; - entity->skill[2] = DEATHCAM_PLAYERNUM; - entity->sizex = 2; - entity->sizey = 2; - entity->yaw = my->yaw; - entity->pitch = 0; - if ( DEATHCAM_PLAYERNUM == clientnum && multiplayer == CLIENT ) - { - entity->flags[UPDATENEEDED] = false; - } - else - { - entity->flags[UPDATENEEDED] = true; - } - players[DEATHCAM_PLAYERNUM]->ghost.my = entity; - players[DEATHCAM_PLAYERNUM]->ghost.uid = entity->getUID(); - } - - if ( multiplayer == CLIENT ) - { - strcpy((char*)net_packet->data, "GHOS"); - net_packet->data[4] = DEATHCAM_PLAYERNUM; - net_packet->data[5] = currentlevel; - - int x = (my->x / 16); - int y = (my->y / 16); - SDLNet_Write16((Sint16)(x), &net_packet->data[6]); - SDLNet_Write16((Sint16)(y), &net_packet->data[8]); - net_packet->data[10] = secretlevel; - net_packet->address.host = net_server.host; - net_packet->address.port = net_server.port; - net_packet->len = 11; - sendPacketSafe(net_sock, -1, net_packet, 0); - } - } - DEATHCAM_PLAYERTARGET++; if (DEATHCAM_PLAYERTARGET >= MAXPLAYERS) { @@ -2117,7 +2303,7 @@ void actDeathCam(Entity* my) my->removeLightField(); - if ( DEATHCAM_CREATEDGHOST != 0 && players[DEATHCAM_PLAYERNUM]->ghost.isActive() ) + if ( players[DEATHCAM_PLAYERNUM]->ghost.isActive() ) { return; } @@ -6798,6 +6984,7 @@ void actPlayer(Entity* my) entity->yaw = my->yaw; entity->pitch = PI / 8; my->playerCreatedDeathCam = 1; + players[PLAYER_NUM]->ghost.initTeleportLocations(my->x / 16, my->y / 16); } createParticleExplosionCharge(my, 174, 100, 0.25); serverSpawnMiscParticles(my, PARTICLE_EFFECT_PLAYER_AUTOMATON_DEATH, 174); @@ -7001,6 +7188,7 @@ void actPlayer(Entity* my) entity->skill[2] = PLAYER_NUM; entity->yaw = my->yaw; entity->pitch = PI / 8; + players[PLAYER_NUM]->ghost.initTeleportLocations(my->x / 16, my->y / 16); } node_t* nextnode; diff --git a/src/net.cpp b/src/net.cpp index 4c6fee7ed..af5cb4364 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2372,6 +2372,16 @@ static std::unordered_map clientPacketHandlers = { } }}, + // update ghost bounce + {'GHFS', []() { + Entity* entity = uidToEntity((int)SDLNet_Read32(&net_packet->data[4])); + if ( entity ) + { + entity->fskill[net_packet->data[8]] = (SDLNet_Read16(&net_packet->data[9]) / 256.0); + playSoundEntityLocal(entity, 612 + local_rng.rand() % 3, 64); + } + }}, + {'EFFE', [](){ /* * Packet breakdown: @@ -2779,6 +2789,7 @@ static std::unordered_map clientPacketHandlers = { entity->skill[2] = clientnum; entity->yaw = cameras[clientnum].ang; entity->pitch = PI / 8; + players[clientnum]->ghost.initTeleportLocations(entity->x / 16, entity->y / 16); } } } @@ -3577,6 +3588,7 @@ static std::unordered_map clientPacketHandlers = { entity->skill[2] = clientnum; entity->yaw = cameras[clientnum].ang; entity->pitch = PI / 8; + players[clientnum]->ghost.initTeleportLocations(entity->x / 16, entity->y / 16); } //deleteSaveGame(multiplayer); // stops save scumming c: //Not here, because it'll make the game unresumable if the game crashes but not all players have died. @@ -5309,14 +5321,14 @@ static std::unordered_map serverPacketHandlers = { if ( bounce ) { players[player]->ghost.my->fskill[9] = Player::Ghost_t::GHOST_SQUISH_START_ANGLE / 100.f; - + playSoundEntityLocal(players[player]->ghost.my, 612 + local_rng.rand() % 3, 64); for ( int c = 1; c < MAXPLAYERS; ++c ) // send to other players { if ( c == player || client_disconnected[c] || players[c]->isLocalPlayer() ) { continue; } - strcpy((char*)net_packet->data, "ENFS"); + strcpy((char*)net_packet->data, "GHFS"); SDLNet_Write32(players[player]->ghost.my->getUID(), &net_packet->data[4]); net_packet->data[8] = 9; SDLNet_Write16(static_cast(players[player]->ghost.my->fskill[9] * 256), &net_packet->data[9]); @@ -5383,7 +5395,7 @@ static std::unordered_map serverPacketHandlers = { entity->behavior = &actDeathGhost; entity->skill[2] = player; entity->yaw = 0.0; - entity->pitch = 0; + entity->pitch = PI / 16; entity->sizex = 2; entity->sizey = 2; entity->flags[UPDATENEEDED] = true; diff --git a/src/player.hpp b/src/player.hpp index ecab54de9..bbc4933cd 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -1810,6 +1810,12 @@ class Player void setActive(bool active); void initTeleportLocations(int x, int y); void initStartRoomLocation(int x, int y); + bool isControllable(); + Entity* spawnGhost(); + static void pauseMenuSpectate(const int player); + static void pauseMenuSpawnGhost(const int player); + static bool gameoverOnDismiss(const int player); + static bool gamemodeAllowsGhosts(); void reset(); bool allowedInteractEntity(Entity& entity); static const int GHOST_MODEL_P1 = 1238; diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 15a0e4162..15e40c952 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -21321,6 +21321,30 @@ namespace MainMenu { } static void mainEndLife(Button& button) { + int player = getMenuOwner(); + if ( multiplayer == CLIENT ) + { + player = clientnum; + } + + if ( Player::Ghost_t::gamemodeAllowsGhosts() ) + { + if ( stats[player]->HP == 0 || !players[player]->entity ) + { + if ( players[player]->ghost.isActive() ) + { + Player::Ghost_t::pauseMenuSpectate(player); + } + else + { + Player::Ghost_t::pauseMenuSpawnGhost(player); + } + soundActivate(); + closeMainMenu(); + return; + } + } + binaryPrompt( Language::get(5633), // window text Language::get(5634), // okay text @@ -23138,11 +23162,15 @@ namespace MainMenu { dismiss->setTextColor(makeColor(170, 134, 102, 255)); dismiss->setTextHighlightColor(makeColor(170, 134, 102, 255)); dismiss->setTickCallback(dismiss_tick); + dismiss->setUserData((void*)(intptr_t)(player + 1)); dismiss->setCallback([](Button& button){ soundCancel(); auto window = static_cast(button.getParent()); auto frame = static_cast(window->getParent()); frame->removeSelf(); + + int player = reinterpret_cast(button.getUserData()) - 1; + Player::Ghost_t::gameoverOnDismiss(player); }); dismiss->select(); } else { @@ -23261,11 +23289,15 @@ namespace MainMenu { dismiss->setTextColor(makeColor(170, 134, 102, 255)); dismiss->setTextHighlightColor(makeColor(170, 134, 102, 255)); dismiss->setTickCallback(dismiss_tick); + dismiss->setUserData((void*)(intptr_t)(player + 1)); dismiss->setCallback([](Button& button){ soundCancel(); auto window = static_cast(button.getParent()); auto frame = static_cast(window->getParent()); frame->removeSelf(); + + int player = reinterpret_cast(button.getUserData()) - 1; + Player::Ghost_t::gameoverOnDismiss(player); }); dismiss->setWidgetLeft("restart"); } From b70f5431cfb45bc8ef317d50b51bdd383a7fe50a Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 4 Oct 2023 13:53:27 -0700 Subject: [PATCH 084/146] fix editor compilation Signed-off-by: SheridanR --- src/opengl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opengl.cpp b/src/opengl.cpp index 1d07d06e6..d21ce309a 100644 --- a/src/opengl.cpp +++ b/src/opengl.cpp @@ -692,7 +692,7 @@ static void uploadUniforms(Shader& shader, float* proj, float* view, float* mapD float fogDistance = 0.f; float fogColor[4] = { 1.f, 1.f, 1.f, 1.f }; GL_CHECK_ERR(glUniform4fv(shader.uniform("uFogColor"), 1, fogColor)); - GL_CHECK_ERR(glUniform4fv(shader.uniform("uFogDistance"), 1, fogDistance)); + GL_CHECK_ERR(glUniform1f(shader.uniform("uFogDistance"), fogDistance)); #else GL_CHECK_ERR(glUniform4fv(shader.uniform("uFogColor"), 1, (float*)&*cvar_fogColor)); GL_CHECK_ERR(glUniform1f(shader.uniform("uFogDistance"), *cvar_fogDistance)); From 8429b07c1b8a284b27c97e5153ea85c74006d30d Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 4 Oct 2023 13:58:43 -0700 Subject: [PATCH 085/146] remove includes from console command.hpp --- src/interface/consolecommand.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/interface/consolecommand.hpp b/src/interface/consolecommand.hpp index b4931ac3c..5daf87700 100644 --- a/src/interface/consolecommand.hpp +++ b/src/interface/consolecommand.hpp @@ -14,9 +14,6 @@ #include #include -#include "../main.hpp" -#include "../draw.hpp" - const char* FindConsoleCommand(const char* str, int index); /* From 61423dd011f0d3cc75b321598f6ff956bac66be9 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 4 Oct 2023 14:14:06 -0700 Subject: [PATCH 086/146] no clouds when fog > 0 Signed-off-by: SheridanR --- src/opengl.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/opengl.cpp b/src/opengl.cpp index d21ce309a..2e4173043 100644 --- a/src/opengl.cpp +++ b/src/opengl.cpp @@ -1870,15 +1870,22 @@ void glDrawSpriteFromImage(view_t* camera, Entity* entity, std::string text, int static bool shouldDrawClouds(const map_t& map, int* cloudtile = nullptr) { bool clouds = false; - if (cloudtile) { - *cloudtile = 77; // hell clouds - } - if ((!strncmp(map.name, "Hell", 4) || map.skybox != 0) && smoothlighting) { - clouds = true; +#ifdef EDITOR + const bool fog = false; +#else + const bool fog = *cvar_fogDistance > 0.f; +#endif + if (!fog) { if (cloudtile) { - if (strncmp(map.name, "Hell", 4)) { - // not a hell map, custom clouds - *cloudtile = map.skybox; + *cloudtile = 77; // hell clouds + } + if ((!strncmp(map.name, "Hell", 4) || map.skybox != 0) && smoothlighting) { + clouds = true; + if (cloudtile) { + if (strncmp(map.name, "Hell", 4)) { + // not a hell map, custom clouds + *cloudtile = map.skybox; + } } } } From 7a426058935091cb1a05c17aadef148029500594 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 4 Oct 2023 15:04:26 -0700 Subject: [PATCH 087/146] fog changes Signed-off-by: SheridanR --- src/draw.cpp | 24 ++++++++++++++++-------- src/opengl.cpp | 12 ++++++++---- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/draw.cpp b/src/draw.cpp index 90dd4ae1c..9692b3c1a 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -270,7 +270,8 @@ void createCommonDrawResources() { "if (uFogDistance > 0.0) {" "float dist = length(uCameraPos.xyz - WorldPos.xyz);" - "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "float lerp = (min(dist, uFogDistance) / uFogDistance) * uFogColor.a;" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, lerp);" "FragColor = vec4(mixed, FragColor.a);" "}" "}"; @@ -301,7 +302,8 @@ void createCommonDrawResources() { "if (uFogDistance > 0.0) {" "float dist = length(uCameraPos.xyz - WorldPos.xyz);" - "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "float lerp = (min(dist, uFogDistance) / uFogDistance) * uFogColor.a;" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, lerp);" "FragColor = vec4(mixed, FragColor.a);" "}" "}"; @@ -350,7 +352,8 @@ void createCommonDrawResources() { "if (uFogDistance > 0.0) {" "float dist = length(uCameraPos.xyz - WorldPos.xyz);" - "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "float lerp = (min(dist, uFogDistance) / uFogDistance) * uFogColor.a;" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, lerp);" "FragColor = vec4(mixed, FragColor.a);" "}" "}"; @@ -398,7 +401,8 @@ void createCommonDrawResources() { "if (uFogDistance > 0.0) {" "float dist = length(uCameraPos.xyz - WorldPos.xyz);" - "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "float lerp = (min(dist, uFogDistance) / uFogDistance) * uFogColor.a;" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, lerp);" "FragColor = vec4(mixed, FragColor.a);" "}" "}"; @@ -441,7 +445,8 @@ void createCommonDrawResources() { "if (uFogDistance > 0.0) {" "float dist = length(uCameraPos.xyz - WorldPos.xyz);" - "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "float lerp = (min(dist, uFogDistance) / uFogDistance) * uFogColor.a;" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, lerp);" "FragColor = vec4(mixed, FragColor.a);" "}" "}"; @@ -548,7 +553,8 @@ void createCommonDrawResources() { "if (uFogDistance > 0.0) {" "float dist = length(uCameraPos.xyz - WorldPos.xyz);" - "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "float lerp = (min(dist, uFogDistance) / uFogDistance) * uFogColor.a;" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, lerp);" "FragColor = vec4(mixed, FragColor.a);" "}" "}"; @@ -594,7 +600,8 @@ void createCommonDrawResources() { "if (uFogDistance > 0.0) {" "float dist = length(uCameraPos.xyz - WorldPos.xyz);" - "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "float lerp = (min(dist, uFogDistance) / uFogDistance) * uFogColor.a;" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, lerp);" "FragColor = vec4(mixed, FragColor.a);" "}" "}"; @@ -622,7 +629,8 @@ void createCommonDrawResources() { "if (uFogDistance > 0.0) {" "float dist = length(uCameraPos.xyz - WorldPos.xyz);" - "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, min(dist, uFogDistance) / uFogDistance);" + "float lerp = (min(dist, uFogDistance) / uFogDistance) * uFogColor.a;" + "vec3 mixed = mix(FragColor.rgb, uFogColor.rgb, lerp);" "FragColor = vec4(mixed, FragColor.a);" "}" "}"; diff --git a/src/opengl.cpp b/src/opengl.cpp index 2e4173043..8076ab5c4 100644 --- a/src/opengl.cpp +++ b/src/opengl.cpp @@ -924,7 +924,11 @@ void glBeginCamera(view_t* camera, bool useHDR) const auto fog_color = Vector4{0.f, 0.f, 0.f, 0.f}; #else const bool hdr = useHDR ? *MainMenu::cvar_hdrEnabled : false; - const auto& fog_color = *cvar_fogColor; + auto fog_color = *cvar_fogColor; + fog_color.x *= fog_color.w; + fog_color.y *= fog_color.w; + fog_color.z *= fog_color.w; + fog_color.w = 1.f; #endif if (hdr) { @@ -1868,14 +1872,14 @@ void glDrawSpriteFromImage(view_t* camera, Entity* entity, std::string text, int -------------------------------------------------------------------------------*/ -static bool shouldDrawClouds(const map_t& map, int* cloudtile = nullptr) { +static bool shouldDrawClouds(const map_t& map, int* cloudtile = nullptr, bool forceCheck = true) { bool clouds = false; #ifdef EDITOR const bool fog = false; #else const bool fog = *cvar_fogDistance > 0.f; #endif - if (!fog) { + if (!fog || forceCheck) { if (cloudtile) { *cloudtile = 77; // hell clouds } @@ -1959,7 +1963,7 @@ void glDrawWorld(view_t* camera, int mode) // determine whether we should draw clouds, and their texture int cloudtile; - const bool clouds = shouldDrawClouds(map, &cloudtile); + const bool clouds = shouldDrawClouds(map, &cloudtile, false); // select texture atlas constexpr int numTileAtlases = sizeof(AnimatedTile::indices) / sizeof(AnimatedTile::indices[0]); From 4768f5eb51c740f3ab30737672f5aa398cb3c9ca Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 5 Oct 2023 11:14:23 +1100 Subject: [PATCH 088/146] * fix ghost spawn anim --- src/actplayer.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index c7dca7fcc..4906e2500 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -1610,7 +1610,11 @@ void actDeathGhost(Entity* my) const Sint32 spawnAnimationDuration = TICKS_PER_SECOND * 1.2; const Sint32 spawnAnimationHalfway = TICKS_PER_SECOND * 0.8; - const bool spawnAnimationPlaying = GHOSTCAM_SPAWN_ANIM < spawnAnimationDuration; + bool spawnAnimationPlaying = GHOSTCAM_SPAWN_ANIM < spawnAnimationDuration; + if ( GHOSTCAM_DEACTIVATED ) + { + spawnAnimationPlaying = false; + } float floatAnimationPercent = std::min(1.f, (float)GHOSTCAM_SPAWN_ANIM / (spawnAnimationHalfway)); float thirdPersonAnimationPercent = 0.f; if ( spawnAnimationPlaying ) @@ -1624,16 +1628,18 @@ void actDeathGhost(Entity* my) } if ( GHOSTCAM_SPAWN_ANIM < spawnAnimationDuration ) { - - GHOSTCAM_SPAWN_ANIM++; - if ( GHOSTCAM_SPAWN_ANIM == 1 ) - { - playSoundEntityLocal(my, 167, 128); - createParticleDropRising(my, 174, 1.0); - } - if ( GHOSTCAM_SPAWN_ANIM == spawnAnimationHalfway ) + if ( !GHOSTCAM_DEACTIVATED ) { - players[playernum]->ghost.createBounceAnimate(); + GHOSTCAM_SPAWN_ANIM++; + if ( GHOSTCAM_SPAWN_ANIM == 1 ) + { + playSoundEntityLocal(my, 167, 128); + createParticleDropRising(my, 174, 1.0); + } + if ( GHOSTCAM_SPAWN_ANIM == spawnAnimationHalfway ) + { + players[playernum]->ghost.createBounceAnimate(); + } } } else From 369064b228655429134f6c34c442d9a8316fe8a3 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 5 Oct 2023 11:18:20 +1100 Subject: [PATCH 089/146] * lang update --- lang/en.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lang/en.txt b/lang/en.txt index df594dc82..f3eda4ea3 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -7826,7 +7826,11 @@ intro - God Rest Ye Merry Gentlemen by NaturesEye# 6043 No holiday currently active.# -6044 Show Player Callouts# +6044 Call Out# 6045 Push item# +6046 Chill# +6047 Haunt# +6048 Push# +6049 View Wheel# 6100 end# From ef31ca7f5895fe141680388378d0c9e0307855f1 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Thu, 5 Oct 2023 01:42:51 -0700 Subject: [PATCH 090/146] remove fog on UI sprites, fog active when ghost is active Signed-off-by: SheridanR --- src/draw.cpp | 6 ++++++ src/draw.hpp | 8 ++++++++ src/game.cpp | 14 ++++++++++++++ src/opengl.cpp | 26 +++++++++++++++++--------- src/shader.hpp | 4 ++++ 5 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/draw.cpp b/src/draw.cpp index 9692b3c1a..a49b3e5f1 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -76,6 +76,7 @@ Shader skyShader; Shader spriteShader; Shader spriteDitheredShader; Shader spriteBrightShader; +Shader spriteUIShader; TempTexture* lightmapTexture[MAXPLAYERS + 1]; static Shader gearShader; @@ -638,6 +639,10 @@ void createCommonDrawResources() { buildSpriteShader(spriteBrightShader, "spriteBrightShader", false, sprite_vertex_glsl, sizeof(sprite_vertex_glsl), sprite_bright_fragment_glsl, sizeof(sprite_bright_fragment_glsl)); + + buildSpriteShader(spriteUIShader, "spriteUIShader", false, + sprite_vertex_glsl, sizeof(sprite_vertex_glsl), + sprite_bright_fragment_glsl, sizeof(sprite_bright_fragment_glsl)); spriteMesh.init(); @@ -660,6 +665,7 @@ void destroyCommonDrawResources() { spriteShader.destroy(); spriteDitheredShader.destroy(); spriteBrightShader.destroy(); + spriteUIShader.destroy(); spriteMesh.destroy(); lineShader.destroy(); lineMesh.destroy(); diff --git a/src/draw.hpp b/src/draw.hpp index 75a4c0c80..8b562854f 100644 --- a/src/draw.hpp +++ b/src/draw.hpp @@ -317,6 +317,7 @@ extern Mesh skyMesh; extern Shader spriteShader; extern Shader spriteDitheredShader; extern Shader spriteBrightShader; +extern Shader spriteUIShader; extern Mesh spriteMesh; extern TempTexture* lightmapTexture[MAXPLAYERS + 1]; @@ -432,3 +433,10 @@ void glEndCamera(view_t* camera, bool useHDR); unsigned int GO_GetPixelU32(int x, int y, view_t& camera); extern bool hdrEnabled; + +#ifndef EDITOR +#include "interface/consolecommand.hpp" +extern ConsoleVariable cvar_hdrBrightness; +extern ConsoleVariable cvar_fogDistance; +extern ConsoleVariable cvar_fogColor; +#endif diff --git a/src/game.cpp b/src/game.cpp index 5363b2c41..b4a6428b6 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5929,6 +5929,13 @@ void drawAllPlayerCameras() { //messagePlayer(0, "%3.2f | %3.2f", players[c]->entity->yaw, oldYaw); } } + + // activate ghost fog (if necessary) + if (players[c]->ghost.isActive()) { + *cvar_hdrBrightness = {0.9f, 0.9f, 1.2f, 1.0f}; + *cvar_fogColor = {0.7f, 0.7f, 1.1f, 0.25f}; + *cvar_fogDistance = 350.f; + } // do occlusion culling from the perspective of this camera DebugStats.drawWorldT2 = std::chrono::high_resolution_clock::now(); @@ -6072,6 +6079,13 @@ void drawAllPlayerCameras() { DebugStats.drawWorldT5 = std::chrono::high_resolution_clock::now(); drawEntities3D(&camera, REALCOLORS); glEndCamera(&camera, true); + + // undo ghost fog + if (players[c]->ghost.isActive()) { + *cvar_hdrBrightness = {1.0f, 1.0f, 1.0f, 1.0f}; + *cvar_fogColor = {0.0f, 0.0f, 0.0f, 1.0f}; + *cvar_fogDistance = 0.0f; + } if (shaking && players[c] && players[c]->entity && !gamePaused) { diff --git a/src/opengl.cpp b/src/opengl.cpp index 8076ab5c4..44b9352fc 100644 --- a/src/opengl.cpp +++ b/src/opengl.cpp @@ -678,8 +678,8 @@ void beginGraphics() { } #ifndef EDITOR -static ConsoleVariable cvar_fogDistance("/fog_distance", 0.f); -static ConsoleVariable cvar_fogColor("/fog_color", {0.f, 0.f, 0.f, 0.f}); +ConsoleVariable cvar_fogDistance("/fog_distance", 0.f); +ConsoleVariable cvar_fogColor("/fog_color", {0.f, 0.f, 0.f, 0.f}); #endif static void uploadUniforms(Shader& shader, float* proj, float* view, float* mapDims) { @@ -694,8 +694,15 @@ static void uploadUniforms(Shader& shader, float* proj, float* view, float* mapD GL_CHECK_ERR(glUniform4fv(shader.uniform("uFogColor"), 1, fogColor)); GL_CHECK_ERR(glUniform1f(shader.uniform("uFogDistance"), fogDistance)); #else - GL_CHECK_ERR(glUniform4fv(shader.uniform("uFogColor"), 1, (float*)&*cvar_fogColor)); - GL_CHECK_ERR(glUniform1f(shader.uniform("uFogDistance"), *cvar_fogDistance)); + if (shader == spriteUIShader) { + float fogDistance = 0.f; + float fogColor[4] = { 1.f, 1.f, 1.f, 1.f }; + GL_CHECK_ERR(glUniform4fv(shader.uniform("uFogColor"), 1, fogColor)); + GL_CHECK_ERR(glUniform1f(shader.uniform("uFogDistance"), fogDistance)); + } else { + GL_CHECK_ERR(glUniform4fv(shader.uniform("uFogColor"), 1, (float*)&*cvar_fogColor)); + GL_CHECK_ERR(glUniform1f(shader.uniform("uFogDistance"), *cvar_fogDistance)); + } #endif } @@ -898,10 +905,10 @@ constexpr float defaultSamples = 4096; #ifdef EDITOR bool hdrEnabled = true; #else +ConsoleVariable cvar_hdrBrightness("/hdr_brightness", defaultBrightness); static ConsoleVariable cvar_hdrMultithread("/hdr_multithread", defaultMultithread); static ConsoleVariable cvar_hdrExposure("/hdr_exposure", defaultExposure); static ConsoleVariable cvar_hdrGamma("/hdr_gamma", defaultGamma); -static ConsoleVariable cvar_hdrBrightness("/hdr_brightness", defaultBrightness); static ConsoleVariable cvar_hdrAdjustment("/hdr_adjust_rate", defaultAdjustmentRate); static ConsoleVariable cvar_hdrLimitHigh("/hdr_limit_high", defaultLimitHigh); static ConsoleVariable cvar_hdrLimitLow("/hdr_limit_low", defaultLimitLow); @@ -1001,6 +1008,7 @@ void glBeginCamera(view_t* camera, bool useHDR) uploadUniforms(spriteShader, (float*)&proj, (float*)&view, (float*)&mapDims); uploadUniforms(spriteDitheredShader, (float*)&proj, (float*)&view, (float*)&mapDims); uploadUniforms(spriteBrightShader, (float*)&proj, (float*)&view, nullptr); + uploadUniforms(spriteUIShader, (float*)&proj, (float*)&view, nullptr); } #include @@ -1329,7 +1337,7 @@ void glDrawEnemyBarSprite(view_t* camera, int mode, int playerViewport, void* en // bind shader GL_CHECK_ERR(glEnable(GL_BLEND)); - auto& shader = spriteBrightShader; + auto& shader = spriteUIShader; shader.bind(); vec4_t v; @@ -1442,7 +1450,7 @@ void glDrawWorldDialogueSprite(view_t* camera, void* worldDialogue, int mode) // bind shader GL_CHECK_ERR(glEnable(GL_BLEND)); - auto& shader = spriteBrightShader; + auto& shader = spriteUIShader; shader.bind(); vec4_t v; @@ -1607,7 +1615,7 @@ void glDrawWorldUISprite(view_t* camera, Entity* entity, int mode) // bind shader GL_CHECK_ERR(glEnable(GL_BLEND)); - auto& shader = spriteBrightShader; + auto& shader = spriteUIShader; shader.bind(); vec4_t v; @@ -1812,7 +1820,7 @@ void glDrawSpriteFromImage(view_t* camera, Entity* entity, std::string text, int } // bind shader - auto& shader = spriteBrightShader; + auto& shader = spriteUIShader; shader.bind(); vec4_t v; diff --git a/src/shader.hpp b/src/shader.hpp index e45420da5..3b2eb0212 100644 --- a/src/shader.hpp +++ b/src/shader.hpp @@ -35,6 +35,10 @@ class Shader { void bindAttribLocation(const char* attribute, int location); bool compile(const char* source, size_t len, Type type); bool link(); + + bool operator==(const Shader& rhs) const { + return program == rhs.program; + } private: const char* name = "untitled"; From e3bc01e903790343b331fac5398c58493ad994f8 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Fri, 6 Oct 2023 05:10:13 +1100 Subject: [PATCH 091/146] * add "thanks" callout for players * fix ghost spawning inside boulder as client being out of the map * remove status fx when dead * fix callout wheel overlapping action prompts in compact height * end life button updated text for ghost/spectate modes * stop gameover window opening if ghost * can spectate between players and ghost entities * fix ghost teleporting over empty pits not working * ghosts can clip out of boulders if stuck inside --- src/actplayer.cpp | 19 ++++---- src/collision.cpp | 13 ++++- src/interface/interface.cpp | 96 ++++++++++++++++++++++++++++++++++--- src/interface/interface.hpp | 5 +- src/net.cpp | 14 ++++-- src/player.cpp | 4 ++ src/player.hpp | 1 + src/ui/GameUI.cpp | 96 ++++++++++++++++++++++++++++++++++--- src/ui/MainMenu.cpp | 22 +++++++++ 9 files changed, 239 insertions(+), 31 deletions(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index 4906e2500..7143ebd35 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -826,7 +826,7 @@ void Player::Ghost_t::handleActions() target = my; } - if ( calloutMenu.createParticleCallout(target) ) + if ( calloutMenu.createParticleCallout(target, CalloutRadialMenu::CALLOUT_CMD_LOOK) ) { calloutMenu.sendCalloutText(CalloutRadialMenu::CALLOUT_CMD_LOOK); } @@ -2091,9 +2091,8 @@ void actDeathCam(Entity* my) }*/ DEATHCAM_TIME++; - /*Uint32 deathcamGameoverPromptTicks = *MainMenu::cvar_fastRestart ? TICKS_PER_SECOND : - (splitscreen ? TICKS_PER_SECOND * 3 : TICKS_PER_SECOND * 6);*/ - Uint32 deathcamGameoverPromptTicks = 25; + Uint32 deathcamGameoverPromptTicks = *MainMenu::cvar_fastRestart ? TICKS_PER_SECOND : + (splitscreen ? TICKS_PER_SECOND * 3 : TICKS_PER_SECOND * 6); if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_TUTORIAL ) { deathcamGameoverPromptTicks = TICKS_PER_SECOND * 3; @@ -2124,7 +2123,7 @@ void actDeathCam(Entity* my) { gameModeManager.Tutorial.openGameoverWindow(); } - else + else if ( !players[DEATHCAM_PLAYERNUM]->ghost.isActive() ) { MainMenu::openGameoverWindow(DEATHCAM_PLAYERNUM); } @@ -2279,7 +2278,7 @@ void actDeathCam(Entity* my) DEATHCAM_PLAYERTARGET = 0; } int c = 0; - while (!players[DEATHCAM_PLAYERTARGET] || !players[DEATHCAM_PLAYERTARGET]->entity) + while ( !Player::getPlayerInteractEntity(DEATHCAM_PLAYERTARGET) ) { if (c > MAXPLAYERS) { @@ -2296,10 +2295,10 @@ void actDeathCam(Entity* my) if (DEATHCAM_PLAYERTARGET >= 0) { - if (players[DEATHCAM_PLAYERTARGET] && players[DEATHCAM_PLAYERTARGET]->entity) + if ( auto entity = Player::getPlayerInteractEntity(DEATHCAM_PLAYERTARGET) ) { - my->x = players[DEATHCAM_PLAYERTARGET]->entity->x; - my->y = players[DEATHCAM_PLAYERTARGET]->entity->y; + my->x = entity->x; + my->y = entity->y; } else { @@ -6373,7 +6372,7 @@ void actPlayer(Entity* my) target = my; } - if ( calloutMenu.createParticleCallout(target) ) + if ( calloutMenu.createParticleCallout(target, CalloutRadialMenu::CALLOUT_CMD_LOOK) ) { calloutMenu.sendCalloutText(CalloutRadialMenu::CALLOUT_CMD_LOOK); } diff --git a/src/collision.cpp b/src/collision.cpp index b6148cb39..44167a2cd 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -341,6 +341,10 @@ Entity* entityClicked(bool* clickedOnGUI, bool clickCheckOverride, int player, E bool entityInsideTile(Entity* entity, int x, int y, int z, bool checkSafeTiles) { + if ( !entity ) + { + return false; + } if ( x < 0 || x >= map.width || y < 0 || y >= map.height || z < 0 || z >= MAPLAYERS ) { return false; @@ -364,7 +368,10 @@ bool entityInsideTile(Entity* entity, int x, int y, int z, bool checkSafeTiles) { if ( !checkSafeTiles && !map.tiles[z + y * MAPLAYERS + x * MAPLAYERS * map.height] ) { - return true; + if ( entity->behavior != &actDeathGhost ) + { + return true; + } } else if ( checkSafeTiles && map.tiles[z + y * MAPLAYERS + x * MAPLAYERS * map.height] ) { @@ -656,7 +663,9 @@ int barony_clear(real_t tx, real_t ty, Entity* my) { continue; // monsters don't have hard collision with door frames } - if ( my->behavior == &actDeathGhost && (entity->behavior == &actMonster || entity->behavior == &actPlayer) ) + if ( my->behavior == &actDeathGhost && (entity->behavior == &actMonster + || entity->behavior == &actPlayer + || (entity->behavior == &actBoulder && entityInsideEntity(my, entity))) ) { continue; } diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index 653475d64..85fbe833f 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -22291,7 +22291,8 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName { if ( lockOnEntityUid == 0 ) { - if ( cmd == CALLOUT_CMD_AFFIRMATIVE || cmd == CALLOUT_CMD_NEGATIVE ) + if ( cmd == CALLOUT_CMD_AFFIRMATIVE + || cmd == CALLOUT_CMD_NEGATIVE ) { entity = playerEntity; } @@ -22691,6 +22692,69 @@ std::string CalloutRadialMenu::setCalloutText(Field* field, const char* iconName } return ""; } + else if ( (cmd == CALLOUT_CMD_AFFIRMATIVE && !(lockOnEntityUid == 0 || (entity && playerEntity == entity))) + || cmd == CALLOUT_CMD_THANKS ) + { + std::string targetPlayerName = ""; + if ( entity && (entity->behavior == &actPlayer || entity->behavior == &actDeathGhost) ) + { + char shortname[32]; + stringCopy(shortname, stats[entity->skill[2]]->name, sizeof(shortname), 22); + targetPlayerName = shortname; + } + + if ( cmd == CALLOUT_CMD_THANKS ) + { + key = "default"; + } + else + { + key = "player_thanks"; + } + if ( setType == SET_CALLOUT_ICON_KEY ) + { + return key; + } + + auto& textMap = text_map[key]; + auto highlights = textMap.bannerHighlights; + if ( highlights.size() > 0 ) + { + int indexStart = 0; + for ( auto highlight : highlights ) + { + indexStart = std::max(highlight, indexStart); + } + for ( auto c : targetPlayerName ) + { + if ( c == ' ' ) + { + highlights.insert(indexStart + 1); + ++indexStart; + } + } + } + if ( setType == SET_CALLOUT_BANNER_TEXT ) + { + setCalloutBannerTextFormatted(player, field, color, highlights, textMap.bannerText.c_str(), targetPlayerName.c_str()); + } + else + { + if ( entity && entity->skill[2] == targetPlayer ) + { + char shortname[32]; + stringCopy(shortname, stats[getPlayer()]->name, sizeof(shortname), 22); + char buf[128]; + snprintf(buf, sizeof(buf), textMap.worldMsgEmoteToYou.c_str(), shortname); + return buf; + } + else + { + return getCalloutMessage(textMap, targetPlayerName.c_str(), targetPlayer); + } + } + return ""; + } break; case CALLOUT_TYPE_NPC: case CALLOUT_TYPE_NPC_ENEMY: @@ -24038,6 +24102,7 @@ void CalloutRadialMenu::drawCallouts(const int playernum) if ( uidMatchesPlayer(i, callout.second.entityUid) ) { if ( callout.second.cmd == CALLOUT_CMD_AFFIRMATIVE + || callout.second.cmd == CALLOUT_CMD_THANKS || callout.second.cmd == CALLOUT_CMD_NEGATIVE || callout.second.cmd == CALLOUT_CMD_LOOK || callout.second.cmd == CALLOUT_CMD_SOUTH @@ -24355,7 +24420,7 @@ int CalloutRadialMenu::CALLOUT_SFX_NEGATIVE = 607; int CalloutRadialMenu::CALLOUT_SFX_POSITIVE = 606; static ConsoleVariable cvar_callout_sfx_vol("/callout_sfx_vol", 128); -bool CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu::CalloutCommand _cmd) +bool CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu::CalloutCommand _cmd, Uint32 overrideUID) { if ( !entity ) { return false; } if ( _cmd == CALLOUT_CMD_CANCEL ) { return false; } @@ -24419,14 +24484,21 @@ bool CalloutRadialMenu::createParticleCallout(Entity* entity, CalloutRadialMenu: std::string calloutTypeKey = getCalloutKeyForCommand(_cmd); Uint32 oldTarget = lockOnEntityUid; - lockOnEntityUid = entity->getUID(); + if ( overrideUID != 0 ) + { + lockOnEntityUid = overrideUID; + } + else + { + lockOnEntityUid = entity->getUID(); + } std::string key = setCalloutText(nullptr, calloutTypeKey.c_str(), 0, _cmd, SET_CALLOUT_ICON_KEY, -1); lockOnEntityUid = oldTarget; callout.tagID = worldIconEntries[iconEntries[calloutTypeKey].text_map[key].worldIconTag].id; callout.tagSmallID = worldIconEntries[iconEntries[calloutTypeKey].text_map[key].worldIconTagMini].id; - if ( callout.cmd == CALLOUT_CMD_AFFIRMATIVE ) + if ( callout.cmd == CALLOUT_CMD_AFFIRMATIVE || callout.cmd == CALLOUT_CMD_THANKS ) { playSound(CALLOUT_SFX_POSITIVE, *cvar_callout_sfx_vol); } @@ -24498,7 +24570,7 @@ bool CalloutRadialMenu::createParticleCallout(real_t x, real_t y, real_t z, Uint } } - if ( callout.cmd == CALLOUT_CMD_AFFIRMATIVE ) + if ( callout.cmd == CALLOUT_CMD_AFFIRMATIVE || callout.cmd == CALLOUT_CMD_THANKS ) { playSound(CALLOUT_SFX_POSITIVE, *cvar_callout_sfx_vol); } @@ -24639,6 +24711,10 @@ std::string CalloutRadialMenu::getCalloutKeyForCommand(CalloutRadialMenu::Callou { return "affirmative"; } + else if ( cmd == CALLOUT_CMD_THANKS ) + { + return "thanks"; + } else if ( cmd == CALLOUT_CMD_NEGATIVE ) { return "negative"; @@ -24946,6 +25022,7 @@ void CalloutRadialMenu::drawCalloutMenu() { if ( Entity* target = uidToEntity(lockOnEntityUid) ) { + Uint32 overrideUID = 0; if ( (target->behavior == &actPlayer || target->behavior == &actDeathGhost) && target->skill[2] != getPlayer() ) { @@ -24954,8 +25031,13 @@ void CalloutRadialMenu::drawCalloutMenu() lockOnEntityUid = getPlayerUid(getPlayer()); target = uidToEntity(lockOnEntityUid); } + else if ( (CalloutCommand)optionSelected == CALLOUT_CMD_AFFIRMATIVE ) + { + target = Player::getPlayerInteractEntity(getPlayer()); + overrideUID = lockOnEntityUid; + optionSelected = CALLOUT_CMD_THANKS; + } else if ( (CalloutCommand)optionSelected == CALLOUT_CMD_LOOK - || (CalloutCommand)optionSelected == CALLOUT_CMD_AFFIRMATIVE || (CalloutCommand)optionSelected == CALLOUT_CMD_NEGATIVE ) { target = Player::getPlayerInteractEntity(getPlayer()); @@ -24972,7 +25054,7 @@ void CalloutRadialMenu::drawCalloutMenu() } } - if ( createParticleCallout(target, (CalloutCommand)optionSelected) ) + if ( createParticleCallout(target, (CalloutCommand)optionSelected, overrideUID) ) { sendCalloutText((CalloutCommand)optionSelected); } diff --git a/src/interface/interface.hpp b/src/interface/interface.hpp index 110d082d1..0c2cfde3d 100644 --- a/src/interface/interface.hpp +++ b/src/interface/interface.hpp @@ -1453,7 +1453,8 @@ struct CalloutRadialMenu CALLOUT_CMD_MOVE, CALLOUT_CMD_CANCEL, CALLOUT_CMD_SELECT, - CALLOUT_CMD_END + CALLOUT_CMD_END, + CALLOUT_CMD_THANKS }; int getPlayerForDirectPlayerCmd(const int player, const CalloutCommand cmd); enum CalloutType : int @@ -1556,7 +1557,7 @@ struct CalloutRadialMenu void closeCalloutMenuGUI(); bool allowedInteractEntity(Entity& selectedEntity, bool updateInteractText = true); bool createParticleCallout(real_t x, real_t y, real_t z, Uint32 uid, CalloutCommand _cmd = CALLOUT_CMD_LOOK); // if true, send message - bool createParticleCallout(Entity* entity, CalloutCommand _cmd = CALLOUT_CMD_LOOK); // if true, send message + bool createParticleCallout(Entity* entity, CalloutCommand _cmd, Uint32 overrideUID = 0); // if true, send message enum SetCalloutTextTypes : int { SET_CALLOUT_BANNER_TEXT, SET_CALLOUT_WORLD_TEXT, diff --git a/src/net.cpp b/src/net.cpp index af5cb4364..ee169c89f 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -5388,6 +5388,8 @@ static std::unordered_map serverPacketHandlers = { players[player]->ghost.uid = entity->getUID(); entity->x = (SDLNet_Read16(&net_packet->data[6]) * 16) + 8; entity->y = (SDLNet_Read16(&net_packet->data[8]) * 16) + 8; + entity->new_x = entity->x; + entity->new_y = entity->y; entity->z = -4; entity->flags[PASSABLE] = true; entity->flags[INVISIBLE] = true; @@ -5540,10 +5542,16 @@ static std::unordered_map serverPacketHandlers = { } else { + Uint32 overrideUID = 0; if ( entity && (entity->behavior == &actPlayer || entity->behavior == &actDeathGhost) && entity->skill[2] != pnum ) { - if ( cmd == CalloutRadialMenu::CALLOUT_CMD_LOOK - || cmd == CalloutRadialMenu::CALLOUT_CMD_AFFIRMATIVE + if ( cmd == CalloutRadialMenu::CALLOUT_CMD_AFFIRMATIVE + || cmd == CalloutRadialMenu::CALLOUT_CMD_THANKS ) + { + entity = Player::getPlayerInteractEntity(pnum); + overrideUID = CalloutMenu[pnum].lockOnEntityUid; + } + else if ( cmd == CalloutRadialMenu::CALLOUT_CMD_LOOK || cmd == CalloutRadialMenu::CALLOUT_CMD_NEGATIVE ) { entity = Player::getPlayerInteractEntity(pnum); @@ -5559,7 +5567,7 @@ static std::unordered_map serverPacketHandlers = { } } } - if ( CalloutMenu[pnum].createParticleCallout(entity, cmd) ) + if ( CalloutMenu[pnum].createParticleCallout(entity, cmd, overrideUID) ) { CalloutMenu[pnum].sendCalloutText(cmd); } diff --git a/src/player.cpp b/src/player.cpp index e2cb8a59a..b53d68e8b 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -3135,6 +3135,10 @@ Entity* Player::getPlayerInteractEntity(const int playernum) { return nullptr; } + if ( !players[playernum] ) + { + return nullptr; + } return players[playernum]->ghost.isActive() ? players[playernum]->ghost.my : players[playernum]->entity; } diff --git a/src/player.hpp b/src/player.hpp index 390c6047a..60437dd2d 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -1441,6 +1441,7 @@ class Player real_t hudDamageTextVelocityX = 0.0; real_t hudDamageTextVelocityY = 0.0; real_t animHideXP = 0.0; + real_t animHideActionPrompts = 0.0; Entity* weapon = nullptr; Entity* arm = nullptr; diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index cc05e710e..123b045a9 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -6922,10 +6922,22 @@ void StatusEffectQueue_t::handleNavigation(std::mapentity && ((multiplayer == SINGLE && splitscreen) || multiplayer != SINGLE) ) + { + effectsEnabled = false; + resetQueue(); + } + if ( players[player]->ghost.isActive() && !kAllowGhostStatusEffects ) + { + effectsEnabled = false; + resetQueue(); + } + std::unordered_set effectSet; for ( auto it = effectQueue.rbegin(); it != effectQueue.rend(); ++it ) { @@ -6936,7 +6948,7 @@ void StatusEffectQueue_t::updateAllQueuedEffects() std::set effectsToSkipAnim; std::unordered_set spellsActive; int count = 0; //This is just for debugging purposes. - for ( node_t* node = channeledSpells[player].first; node; node = node->next, count++ ) + for ( node_t* node = channeledSpells[player].first; node && effectsEnabled; node = node->next, count++ ) { spell_t* spell = (spell_t*)node->element; if ( !spell ) @@ -6991,7 +7003,7 @@ void StatusEffectQueue_t::updateAllQueuedEffects() } } - for ( int i = 0; i <= NUMEFFECTS; ++i ) + for ( int i = 0; i <= NUMEFFECTS && effectsEnabled; ++i ) { if ( i == NUMEFFECTS ) { @@ -7066,9 +7078,10 @@ void StatusEffectQueue_t::updateAllQueuedEffects() { effectActive = false; } - if ( !kAllowGhostStatusEffects && players[player]->ghost.isActive() ) + + if ( !players[player]->entity ) { - effectActive = false; + skipAnim = true; } if ( effectActive ) @@ -7275,6 +7288,10 @@ void StatusEffectQueue_t::updateAllQueuedEffects() } else { + if ( !players[player]->entity ) + { + effectsToSkipAnim.insert(i); + } if ( i == kEffectSneak ) { effectsToSkipAnim.insert(i); @@ -7306,6 +7323,21 @@ void StatusEffectQueue_t::updateAllQueuedEffects() bool hungerIconActive = (effectSet.find(kEffectBread) != effectSet.end() || effectSet.find(kEffectBloodHunger) != effectSet.end() || effectSet.find(kEffectAutomatonHunger) != effectSet.end()); + if ( !players[player]->entity ) + { + if ( effectSet.find(kEffectBread) != effectSet.end() ) + { + effectsToSkipAnimThisFrame.push_back(kEffectBread); + } + if ( effectSet.find(kEffectBloodHunger) != effectSet.end() ) + { + effectsToSkipAnimThisFrame.push_back(kEffectBloodHunger); + } + if ( effectSet.find(kEffectAutomatonHunger) != effectSet.end() ) + { + effectsToSkipAnimThisFrame.push_back(kEffectAutomatonHunger); + } + } Frame* statusEffectFrame = getStatusEffectFrame(); auto automatonHungerFrame = statusEffectFrame->findFrame("automaton hunger notification"); @@ -8047,7 +8079,17 @@ void updateStatusEffectQueue(const int player) statusEffectQueue.deleteEffect(StatusEffectQueue_t::kEffectAutomatonHunger); } - if ( stats[player] && stats[player]->type != AUTOMATON + bool effectsEnabled = true; + if ( !players[player]->entity && ((multiplayer == SINGLE && splitscreen) || multiplayer != SINGLE) ) + { + effectsEnabled = false; + } + if ( players[player]->ghost.isActive() && !kAllowGhostStatusEffects ) + { + effectsEnabled = false; + } + + if ( effectsEnabled && stats[player] && stats[player]->type != AUTOMATON && (svFlags & SV_FLAG_HUNGER) && (stats[player]->HUNGER <= getEntityHungerInterval(player, nullptr, stats[player], HUNGER_INTERVAL_HUNGRY) || stats[player]->HUNGER >= getEntityHungerInterval(player, nullptr, stats[player], HUNGER_INTERVAL_OVERSATIATED)) ) @@ -8158,7 +8200,7 @@ void updateStatusEffectQueue(const int player) } } } - else + else if ( effectsEnabled ) { statusEffectQueue.deleteEffect(StatusEffectQueue_t::kEffectBloodHunger); statusEffectQueue.deleteEffect(StatusEffectQueue_t::kEffectBread); @@ -9657,6 +9699,46 @@ void Player::HUD_t::updateActionPrompts() actionPromptsFrame->setDisabled(false); actionPromptsFrame->setSize(SDL_Rect{ 0, 0, hudFrame->getSize().w, hudFrame->getSize().h }); + bool tempHideActionPrompts = false; + bool fadeOut = false; + if ( ghostPrompts ) + { + if ( player.bUseCompactGUIHeight() && CalloutMenu[player.playernum].calloutMenuIsOpen() && !CalloutMenu[player.playernum].selectMoveTo ) + { + tempHideActionPrompts = true; + } + } + + if ( tempHideActionPrompts ) + { + if ( fadeOut ) + { + const real_t fpsScale = getFPSScale(50.0); // ported from 50Hz + real_t setpointDiff = fpsScale * std::max(.1, (1.0 - animHideActionPrompts)) / 2.5; + animHideActionPrompts += setpointDiff; + animHideActionPrompts = std::min(1.0, animHideActionPrompts); + } + else + { + animHideActionPrompts = 1.0; + } + } + else + { + const real_t fpsScale = getFPSScale(50.0); // ported from 50Hz + real_t setpointDiff = fpsScale * std::max(.1, (animHideActionPrompts)) / 2.5; + animHideActionPrompts -= setpointDiff; + animHideActionPrompts = std::max(0.0, animHideActionPrompts); + } + + actionPromptsFrame->setInheritParentFrameOpacity(false); + real_t opacity = 1.0; + if ( auto parent = actionPromptsFrame->getParent() ) + { + opacity *= actionPromptsFrame->getParent()->getOpacity() / 100.0; + } + actionPromptsFrame->setOpacity((opacity - animHideActionPrompts) * 100.0); + const int iconSize = Player::HUD_t::actionPromptIconSize; const int glyphSize = 32; const int iconBackingSize = Player::HUD_t::actionPromptBackingSize; diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 15e40c952..41e0b9b26 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -22501,6 +22501,28 @@ namespace MainMenu { button->setHJustify(Button::justify_t::LEFT); button->setVJustify(Button::justify_t::CENTER); button->setText(options[c].text); + if ( !strcmp(button->getName(), "End Life") ) + { + int player = getMenuOwner(); + if ( multiplayer == CLIENT ) + { + player = clientnum; + } + if ( Player::Ghost_t::gamemodeAllowsGhosts() ) + { + if ( stats[player]->HP == 0 || !players[player]->entity ) + { + if ( players[player]->ghost.isActive() ) + { + button->setText(Language::get(6050)); // spectate + } + else + { + button->setText(Language::get(6051)); // spawn as ghost + } + } + } + } button->setFont(menu_option_font); button->setBackground("*#images/ui/Main Menus/Main/UI_MainMenu_SelectorBar00.png"); button->setHideSelectors(false); From 6073603b202d5ff034310ce68546e86cd6174d70 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Fri, 6 Oct 2023 05:11:24 +1100 Subject: [PATCH 092/146] * clean up callouts on level change/restart etc --- src/game.cpp | 6 ++++++ src/menu.cpp | 3 +++ src/net.cpp | 1 + 3 files changed, 10 insertions(+) diff --git a/src/game.cpp b/src/game.cpp index 7ade3e553..ed43616ab 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1856,6 +1856,7 @@ void gameLogic(void) FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; CalloutMenu[i].closeCalloutMenuGUI(); + CalloutMenu[i].callouts.clear(); } // stop all sounds @@ -5024,6 +5025,10 @@ void ingameHud() { players[player]->bControlEnabled = true; } + else if ( players[player]->ghost.isActive() ) + { + players[player]->bControlEnabled = true; + } #ifdef USE_IMGUI if ( ImGui_t::isInit ) { @@ -6788,6 +6793,7 @@ int main(int argc, char** argv) FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; CalloutMenu[i].closeCalloutMenuGUI(); + CalloutMenu[i].callouts.clear(); } // black background diff --git a/src/menu.cpp b/src/menu.cpp index 924c3c217..7ac63597e 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -9702,6 +9702,7 @@ void doNewGame(bool makeHighscore) { FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; CalloutMenu[i].closeCalloutMenuGUI(); + CalloutMenu[i].callouts.clear(); } for ( int i = 0; i < MAXPLAYERS; ++i ) @@ -9977,6 +9978,7 @@ void doNewGame(bool makeHighscore) { FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; CalloutMenu[i].closeCalloutMenuGUI(); + CalloutMenu[i].callouts.clear(); } client_disconnected[0] = false; @@ -10708,6 +10710,7 @@ void doEndgame(bool saveHighscore) { FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; CalloutMenu[i].closeCalloutMenuGUI(); + CalloutMenu[i].callouts.clear(); } // load menu level diff --git a/src/net.cpp b/src/net.cpp index ee169c89f..d8f8c01c6 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2021,6 +2021,7 @@ static void changeLevel() { FollowerMenu[i].followerToCommand = nullptr; FollowerMenu[i].entityToInteractWith = nullptr; CalloutMenu[i].closeCalloutMenuGUI(); + CalloutMenu[i].callouts.clear(); } // stop all sounds From 5d04adf9b467939639320c339b35a967e1f10d52 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Fri, 6 Oct 2023 05:19:45 +1100 Subject: [PATCH 093/146] * ghost help icon fade faster --- src/interface/interface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index 85fbe833f..b9c951e5f 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -24107,7 +24107,8 @@ void CalloutRadialMenu::drawCallouts(const int playernum) || callout.second.cmd == CALLOUT_CMD_LOOK || callout.second.cmd == CALLOUT_CMD_SOUTH || callout.second.cmd == CALLOUT_CMD_SOUTHWEST - || callout.second.cmd == CALLOUT_CMD_SOUTHEAST ) + || callout.second.cmd == CALLOUT_CMD_SOUTHEAST + || (callout.second.cmd == CALLOUT_CMD_HELP && players[i]->ghost.isActive()) ) { // fade early for simple thumbs up/down for players lifePercent = callout.second.ticks / (real_t)((TICKS_PER_SECOND * 4) / 5); From 637afdea5a7f75819ca92cab2335dac2e862a00a Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Fri, 6 Oct 2023 05:35:43 +1100 Subject: [PATCH 094/146] * disable callout inputs in singleplayer (/callout_debug to test in single) --- src/actplayer.cpp | 4 ++-- src/interface/interface.cpp | 14 ++++++++++++++ src/interface/interface.hpp | 1 + src/ui/GameUI.cpp | 10 ++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index 7143ebd35..82e734580 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -900,7 +900,7 @@ void Player::Ghost_t::handleActions() } if ( !player.usingCommand() && player.bControlEnabled - && !gamePaused ) + && !gamePaused && CalloutRadialMenu::calloutMenuEnabledForGamemode() ) { bool showCalloutCommandsOnGamepad = false; auto showCalloutCommandsFind = b.find("Call Out"); @@ -6447,7 +6447,7 @@ void actPlayer(Entity* my) bool skipFollowerMenu = false; if ( !players[PLAYER_NUM]->usingCommand() && players[PLAYER_NUM]->bControlEnabled - && !gamePaused ) + && !gamePaused && CalloutRadialMenu::calloutMenuEnabledForGamemode() ) { bool showCalloutCommandsOnGamepad = false; auto showCalloutCommandsFind = b.find("Call Out"); diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index b9c951e5f..a66092b57 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -23763,6 +23763,20 @@ CalloutRadialMenu::CalloutType CalloutRadialMenu::getCalloutTypeForUid(const int return CalloutRadialMenu::getCalloutTypeForEntity(player, parent); } +static ConsoleVariable cvar_callout_debug("/callout_debug", false); +bool CalloutRadialMenu::calloutMenuEnabledForGamemode() +{ + if ( *cvar_callout_debug ) + { + return true; + } + if ( multiplayer != SINGLE || (multiplayer == SINGLE && splitscreen) ) + { + return true; + } + return false; +} + bool CalloutRadialMenu::uidMatchesPlayer(const int playernum, const Uint32 uid) { if ( uid == 0 ) { return false; } diff --git a/src/interface/interface.hpp b/src/interface/interface.hpp index 0c2cfde3d..b590565a0 100644 --- a/src/interface/interface.hpp +++ b/src/interface/interface.hpp @@ -1588,6 +1588,7 @@ struct CalloutRadialMenu const int getPlayer() const { return gui_player; } static bool uidMatchesPlayer(const int playernum, const Uint32 uid); static Uint32 getPlayerUid(const int playernum); + static bool calloutMenuEnabledForGamemode(); void update(); }; extern CalloutRadialMenu CalloutMenu[MAXPLAYERS]; diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 123b045a9..2f59319a1 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -941,11 +941,21 @@ void updateCalloutPromptFrame(const int player) return; } + if ( !CalloutRadialMenu::calloutMenuEnabledForGamemode() ) + { + frame->setDisabled(true); + return; + } if ( !Player::getPlayerInteractEntity(player) ) { frame->setDisabled(true); return; } + if ( splitscreen && player == 3 ) + { + frame->setDisabled(true); // we overlap the minimap, so don't draw this one + return; + } else if ( !players[player]->shootmode && !FollowerMenu[player].followerMenuIsOpen() && !CalloutMenu[player].calloutMenuIsOpen() ) { From c532cf19b9e606cfd2c675f75205e8164f1f0572 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Fri, 6 Oct 2023 05:44:50 +1100 Subject: [PATCH 095/146] * console command to spawn as ghost if desired --- src/interface/consolecommand.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/interface/consolecommand.cpp b/src/interface/consolecommand.cpp index 916bdb919..612137768 100644 --- a/src/interface/consolecommand.cpp +++ b/src/interface/consolecommand.cpp @@ -4674,5 +4674,34 @@ namespace ConsoleCommands { static ConsoleCommand ccmd_reloadtiles("/reloadtiles", "reloads tile textures", []CCMD{ generateTileTextures(); }); + + static ConsoleCommand ccmd_spawnghost("/respawnasghost", "respawn as a ghost", []CCMD{ + if ( !(svFlags & SV_FLAG_CHEATS) ) + { + messagePlayer(clientnum, MESSAGE_MISC, Language::get(277)); + return; + } + + if ( players[clientnum]->ghost.my ) + { + players[clientnum]->ghost.setActive(!players[clientnum]->ghost.isActive()); + return; + } + + if ( stats[clientnum]->HP > 0 ) + { + stats[clientnum]->HP = 0; + } + + if ( players[clientnum]->entity ) + { + players[clientnum]->ghost.initTeleportLocations(players[clientnum]->entity->x / 16, players[clientnum]->entity->y / 16); + } + else + { + players[clientnum]->ghost.initTeleportLocations(players[clientnum]->ghost.startRoomX, players[clientnum]->ghost.startRoomY); + } + players[clientnum]->ghost.spawnGhost(); + }); } From 47c782af596bc1833d005cb7d8980adb83350975 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Fri, 6 Oct 2023 05:46:57 +1100 Subject: [PATCH 096/146] * main menu strings for spectate/spawn ghost --- lang/en.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lang/en.txt b/lang/en.txt index f3eda4ea3..486bd5260 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -7832,5 +7832,7 @@ intro - God Rest Ye Merry Gentlemen by NaturesEye# 6047 Haunt# 6048 Push# 6049 View Wheel# +6050 SPECTATE# +6051 SPAWN AS GHOST# 6100 end# From 9b6d0284fdc804907d467a561bc1442bb3b5c6f3 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Fri, 6 Oct 2023 05:59:39 +1100 Subject: [PATCH 097/146] * disable gameover if ghost mode was activated --- src/actplayer.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index 82e734580..e4d758e0d 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -2075,6 +2075,7 @@ void actDeathGhost(Entity* my) #define DEATHCAM_IDLETIME my->skill[3] #define DEATHCAM_IDLEROTATEDIRYAW my->skill[4] #define DEATHCAM_IDLEROTATEPITCHINIT my->skill[5] +#define DEATHCAM_DISABLE_GAMEOVER my->skill[6] #define DEATHCAM_ROTX my->fskill[0] #define DEATHCAM_ROTY my->fskill[1] #define DEATHCAM_IDLEPITCH my->fskill[2] @@ -2117,13 +2118,20 @@ void actDeathCam(Entity* my) DEATHCAM_PLAYERTARGET = DEATHCAM_PLAYERNUM; DEATHCAM_IDLEROTATEDIRYAW = (local_rng.rand() % 2 == 0) ? 1 : -1; } + else if ( DEATHCAM_TIME < deathcamGameoverPromptTicks ) + { + if ( players[DEATHCAM_PLAYERNUM]->ghost.isActive() ) + { + DEATHCAM_DISABLE_GAMEOVER = 1; + } + } else if ( DEATHCAM_TIME == deathcamGameoverPromptTicks ) { if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_TUTORIAL ) { gameModeManager.Tutorial.openGameoverWindow(); } - else if ( !players[DEATHCAM_PLAYERNUM]->ghost.isActive() ) + else if ( !players[DEATHCAM_PLAYERNUM]->ghost.isActive() && DEATHCAM_DISABLE_GAMEOVER == 0 ) { MainMenu::openGameoverWindow(DEATHCAM_PLAYERNUM); } From 4720c7f0e12e77057be9322a81584e46c29e456a Mon Sep 17 00:00:00 2001 From: SheridanR Date: Thu, 5 Oct 2023 16:51:09 -0700 Subject: [PATCH 098/146] bump game version to 4.1.0 Signed-off-by: SheridanR --- src/game.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game.hpp b/src/game.hpp index b2544e787..5964cf8d8 100644 --- a/src/game.hpp +++ b/src/game.hpp @@ -25,9 +25,9 @@ // REMEMBER TO CHANGE THIS WITH EVERY NEW OFFICIAL VERSION!!! #ifdef NINTENDO -static const char VERSION[] = "v4.0.3"; +static const char VERSION[] = "v4.1.0"; #else -static const char VERSION[] = "v4.0.2"; +static const char VERSION[] = "v4.1.0"; #endif #define GAME_CODE From 1c54b65cd57fd2edc1ebb3e84804542296cc03a9 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Thu, 5 Oct 2023 17:15:39 -0700 Subject: [PATCH 099/146] fix compilation and memory leaks with polymodels Signed-off-by: SheridanR --- src/actplayer.cpp | 2 +- src/files.cpp | 12 ++++++++++++ src/init.cpp | 2 ++ src/mod_tools.cpp | 7 +++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index e4d758e0d..f759fa008 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -1650,7 +1650,7 @@ void actDeathGhost(Entity* my) auto player = players[playernum]; my->removeLightField(); - char* light_type = nullptr; + const char* light_type = nullptr; bool ambientLight = false; if ( GHOSTCAM_SNEAKING ) { diff --git a/src/files.cpp b/src/files.cpp index f329ae886..21775cc8a 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -3160,6 +3160,16 @@ void generatePolyModels(int start, int end, bool forceCacheRebuild) if ( generateAll ) { + if (polymodels) { + for (int c = 0; c < nummodels; ++c) { + if (polymodels[c].faces) { + free(polymodels[c].faces); + polymodels[c].faces = nullptr; + } + } + free(polymodels); + polymodels = nullptr; + } polymodels = (polymodel_t*)malloc(sizeof(polymodel_t) * nummodels); memset(polymodels, 0, sizeof(polymodel_t) * nummodels); if ( useModelCache && !forceCacheRebuild ) @@ -4136,6 +4146,7 @@ void generatePolyModels(int start, int end, bool forceCacheRebuild) // translate quads into triangles if (polymodels[c].faces) { free(polymodels[c].faces); + polymodels[c].faces = nullptr; } polymodels[c].faces = (polytriangle_t*)malloc(sizeof(polytriangle_t) * polymodels[c].numfaces); for ( uint64_t i = 0; i < polymodels[c].numfaces; i++ ) @@ -4251,6 +4262,7 @@ void reloadModels(int start, int end) { if ( polymodels[c].faces ) { free(polymodels[c].faces); + polymodels[c].faces = nullptr; } models[c] = loadVoxel(name); } diff --git a/src/init.cpp b/src/init.cpp index decaacf06..360121273 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1243,6 +1243,7 @@ int deinitApp() for (int c = 0; c < nummodels; ++c) { if (polymodels[c].faces) { free(polymodels[c].faces); + polymodels[c].faces = nullptr; } } if (!disablevbos) { @@ -1262,6 +1263,7 @@ int deinitApp() } } free(polymodels); + polymodels = nullptr; } #ifndef EDITOR diff --git a/src/mod_tools.cpp b/src/mod_tools.cpp index 18f698db0..e0c23c7de 100644 --- a/src/mod_tools.cpp +++ b/src/mod_tools.cpp @@ -8251,6 +8251,7 @@ void Mods::loadModels(int start, int end) { if ( polymodels[c].faces ) { free(polymodels[c].faces); + polymodels[c].faces = nullptr; } models[c] = loadVoxel(name); } @@ -8355,8 +8356,11 @@ void Mods::unloadMods(bool force) for (int c = 0; c < nummodels; ++c) { if (polymodels[c].faces) { free(polymodels[c].faces); + polymodels[c].faces = nullptr; } } + free(polymodels); + polymodels = nullptr; generatePolyModels(0, nummodels, false); Mods::modelsListRequiresReloadUnmodded = false; } @@ -8466,6 +8470,7 @@ void Mods::loadMods() for (int c = modelsIndexUpdateStart; c < modelsIndexUpdateEnd && c < nummodels; ++c) { if (polymodels[c].faces) { free(polymodels[c].faces); + polymodels[c].faces = nullptr; } if (polymodels[c].vao) { GL_CHECK_ERR(glDeleteVertexArrays(1, &polymodels[c].vao)); @@ -8480,6 +8485,8 @@ void Mods::loadMods() GL_CHECK_ERR(glDeleteBuffers(1, &polymodels[c].normals)); } } + free(polymodels); + polymodels = nullptr; generatePolyModels(modelsIndexUpdateStart, modelsIndexUpdateEnd, true); generateVBOs(modelsIndexUpdateStart, modelsIndexUpdateEnd); useModelCache = oldModelCache; From 581fe858a43585a4073e45d7268a5e931f53f8dd Mon Sep 17 00:00:00 2001 From: SheridanR Date: Thu, 5 Oct 2023 18:12:28 -0700 Subject: [PATCH 100/146] fix unnecessary roomcode header on nintendo Signed-off-by: SheridanR --- src/ui/MainMenu.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 41e0b9b26..400980c23 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -16201,6 +16201,11 @@ namespace MainMenu { roomcode_header->setSize(SDL_Rect{Frame::virtualScreenX - 212 - 44 - 292 - 32, 0, 320, 35}); roomcode_header->setFont(smallfont_outline); if (directConnect) { +#ifdef NINTENDO + roomcode_header->setText(""); +#else + roomcode_header->setText(Language::get(5464)); +#endif roomcode_header->setText(Language::get(5464)); } else { roomcode_header->setText(Language::get(5465)); From fd7ec7e1f6af403894891a27a59521ddcaee87f9 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Thu, 5 Oct 2023 18:27:26 -0700 Subject: [PATCH 101/146] fix stupid mistake Signed-off-by: SheridanR --- src/ui/MainMenu.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 400980c23..d24b48052 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -16206,7 +16206,6 @@ namespace MainMenu { #else roomcode_header->setText(Language::get(5464)); #endif - roomcode_header->setText(Language::get(5464)); } else { roomcode_header->setText(Language::get(5465)); } From c3d24fba57a8a27b6aa0f4bee10cb7e5da206290 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 7 Oct 2023 00:36:22 +1100 Subject: [PATCH 102/146] * nintendo bindings for callouts --- src/ui/MainMenu.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 41e0b9b26..897c79e1a 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -174,7 +174,11 @@ namespace MainMenu { {"Spell List", "B", hiddenBinding, emptyBinding}, {"Skill Sheet", "K", hiddenBinding, emptyBinding}, {"Autosort Inventory", "R", hiddenBinding, emptyBinding}, +#ifdef NINTENDO + {"Call Out", "X", "ButtonB", emptyBinding}, +#else {"Call Out", "X", "ButtonA", emptyBinding}, +#endif {"Command NPC", "Q", "DpadX-", emptyBinding}, {"Show NPC Commands", "C", "DpadX+", emptyBinding}, {"Cycle NPCs", "E", "DpadY-", emptyBinding}, @@ -192,7 +196,7 @@ namespace MainMenu { #endif {"Hotbar Down / Cancel", hiddenBinding, "DpadY+", emptyBinding}, #ifdef NINTENDO - {"Interact Tooltip Next", "R", "ButtonB", emptyBinding }, + {"Interact Tooltip Next", "R", "DpadY+", emptyBinding }, #else {"Interact Tooltip Next", "R", "DpadY+", emptyBinding }, #endif From 8746f1d500b02a38c3833c7e2ad850a95030c38e Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 7 Oct 2023 01:20:07 +1100 Subject: [PATCH 103/146] * change classic layout tooltip next to dpad down --- src/ui/MainMenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 897c79e1a..f66f917b6 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -255,7 +255,7 @@ namespace MainMenu { {"Hotbar Right", "MouseWheelDown", "DpadX+", emptyBinding}, {"Hotbar Up / Select", "Mouse2", "DpadY-", emptyBinding}, {"Hotbar Down / Cancel", hiddenBinding, "DpadY+", emptyBinding}, - {"Interact Tooltip Next", "R", "ButtonB", emptyBinding }, + {"Interact Tooltip Next", "R", "DpadY+", emptyBinding }, {"Interact Tooltip Prev", emptyBinding, emptyBinding, emptyBinding }, {"Expand Inventory Tooltip", "X", hiddenBinding, emptyBinding }, {"Quick Turn", emptyBinding, "ButtonRightBumper", emptyBinding }, From 48f1c515d3268c868bc34771484d8067de98fda9 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 7 Oct 2023 01:34:14 +1100 Subject: [PATCH 104/146] * fix for menucancel being callout --- src/interface/interface.cpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index a66092b57..f7118a73c 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -24802,14 +24802,24 @@ void CalloutRadialMenu::drawCalloutMenu() Input& input = Input::inputs[gui_player]; + bool allowMenuCancel = true; + if ( input.input("Call Out").input + == input.input("MenuCancel").input ) + { + allowMenuCancel = false; + } + if ( selectMoveTo ) { if ( input.binaryToggle("MenuCancel") ) { input.consumeBinaryToggle("MenuCancel"); - input.consumeBindingsSharedWithBinding("MenuCancel"); - closeCalloutMenuGUI(); - Player::soundCancel(); + if ( allowMenuCancel ) + { + input.consumeBindingsSharedWithBinding("MenuCancel"); + closeCalloutMenuGUI(); + Player::soundCancel(); + } } if ( calloutFrame ) { @@ -24852,11 +24862,14 @@ void CalloutRadialMenu::drawCalloutMenu() if ( calloutMenuIsOpen() && input.binaryToggle("MenuCancel") ) { input.consumeBinaryToggle("MenuCancel"); - input.consumeBindingsSharedWithBinding("MenuCancel"); - closeCalloutMenuGUI(); - players[gui_player]->closeAllGUIs(CLOSEGUI_ENABLE_SHOOTMODE, CLOSEGUI_CLOSE_ALL); - Player::soundCancel(); - return; + if ( allowMenuCancel || (optionSelected != -1 && optionSelected != CALLOUT_CMD_END && optionSelected != CALLOUT_CMD_SELECT) ) + { + input.consumeBindingsSharedWithBinding("MenuCancel"); + closeCalloutMenuGUI(); + players[gui_player]->closeAllGUIs(CLOSEGUI_ENABLE_SHOOTMODE, CLOSEGUI_CLOSE_ALL); + Player::soundCancel(); + return; + } } //if ( ticks % 50 == 0 ) From 27dc6b2c09c1aaf810ef22c0d86da0123247fe33 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 7 Oct 2023 01:42:58 +1100 Subject: [PATCH 105/146] * callout is dpad down on expert rather than b --- src/ui/MainMenu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index f66f917b6..7fafe4a96 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -175,7 +175,7 @@ namespace MainMenu { {"Skill Sheet", "K", hiddenBinding, emptyBinding}, {"Autosort Inventory", "R", hiddenBinding, emptyBinding}, #ifdef NINTENDO - {"Call Out", "X", "ButtonB", emptyBinding}, + {"Call Out", "X", "DpadY+", emptyBinding}, #else {"Call Out", "X", "ButtonA", emptyBinding}, #endif @@ -198,7 +198,7 @@ namespace MainMenu { #ifdef NINTENDO {"Interact Tooltip Next", "R", "DpadY+", emptyBinding }, #else - {"Interact Tooltip Next", "R", "DpadY+", emptyBinding }, + {"Interact Tooltip Next", "R", "ButtonB", emptyBinding }, #endif {"Interact Tooltip Prev", emptyBinding, emptyBinding, emptyBinding }, {"Expand Inventory Tooltip", "X", hiddenBinding, emptyBinding }, From 7cc8718cb7424c0d46f41a82c52cd1b5c2fdf5d9 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 7 Oct 2023 01:47:15 +1100 Subject: [PATCH 106/146] * classic binding back to b --- src/ui/MainMenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 7fafe4a96..08d91664a 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -255,7 +255,7 @@ namespace MainMenu { {"Hotbar Right", "MouseWheelDown", "DpadX+", emptyBinding}, {"Hotbar Up / Select", "Mouse2", "DpadY-", emptyBinding}, {"Hotbar Down / Cancel", hiddenBinding, "DpadY+", emptyBinding}, - {"Interact Tooltip Next", "R", "DpadY+", emptyBinding }, + {"Interact Tooltip Next", "R", "ButtonB", emptyBinding }, {"Interact Tooltip Prev", emptyBinding, emptyBinding, emptyBinding }, {"Expand Inventory Tooltip", "X", hiddenBinding, emptyBinding }, {"Quick Turn", emptyBinding, "ButtonRightBumper", emptyBinding }, From d3b98d8cffba5c0ac00ca9f597ebee6038402b02 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 7 Oct 2023 02:12:49 +1100 Subject: [PATCH 107/146] * fix wrong order --- src/ui/MainMenu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 08d91664a..b73400b66 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -196,9 +196,9 @@ namespace MainMenu { #endif {"Hotbar Down / Cancel", hiddenBinding, "DpadY+", emptyBinding}, #ifdef NINTENDO - {"Interact Tooltip Next", "R", "DpadY+", emptyBinding }, -#else {"Interact Tooltip Next", "R", "ButtonB", emptyBinding }, +#else + {"Interact Tooltip Next", "R", "DpadY+", emptyBinding }, #endif {"Interact Tooltip Prev", emptyBinding, emptyBinding, emptyBinding }, {"Expand Inventory Tooltip", "X", hiddenBinding, emptyBinding }, From f777cb5ba11cf8e282737cb0eaa35f6d0ff3c6ac Mon Sep 17 00:00:00 2001 From: SheridanR Date: Fri, 6 Oct 2023 13:13:08 -0700 Subject: [PATCH 108/146] holiday themes disabled in editor Signed-off-by: SheridanR --- src/files.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/files.cpp b/src/files.cpp index f329ae886..b2c58014f 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -48,7 +48,9 @@ ConsoleVariable cvar_disableHoliday("/disable_holiday", false); #endif HolidayTheme getCurrentHoliday(bool force) { -#ifndef EDITOR +#ifdef EDITOR + return HolidayTheme::THEME_NONE; +#else if (*cvar_disableHoliday && !force) { return HolidayTheme::THEME_NONE; } @@ -56,7 +58,6 @@ HolidayTheme getCurrentHoliday(bool force) { const int holiday = std::clamp(*cvar_forceHoliday, 0, (int)HolidayTheme::THEME_MAX - 1); return static_cast(holiday); } -#endif static bool gotTime = false; static int year, month, day; if (!gotTime) { @@ -73,6 +74,7 @@ HolidayTheme getCurrentHoliday(bool force) { else { return HolidayTheme::THEME_NONE; } +#endif } bool isCurrentHoliday(bool force) { From 158d7234bbff58cda42439a24dfc57968439b3b4 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sun, 8 Oct 2023 12:15:21 +1100 Subject: [PATCH 109/146] * deprecate some unused old code --- src/interface/drawstatus.cpp | 493 +----------------------------- src/interface/playerinventory.cpp | 263 ---------------- src/magic/magic.hpp | 4 +- src/menu.cpp | 187 ------------ src/menu.hpp | 3 - 5 files changed, 6 insertions(+), 944 deletions(-) diff --git a/src/interface/drawstatus.cpp b/src/interface/drawstatus.cpp index 13fad22d0..59874df8c 100644 --- a/src/interface/drawstatus.cpp +++ b/src/interface/drawstatus.cpp @@ -1317,11 +1317,11 @@ void drawStatus(int player) spell_t* spell = getSpellFromItem(player, item); if ( drawHotBarTooltipOnCycle ) { - drawSpellTooltip(player, spell, item, &src); + //drawSpellTooltip(player, spell, item, &src); } else { - drawSpellTooltip(player, spell, item, nullptr); + //drawSpellTooltip(player, spell, item, nullptr); } } else @@ -1362,9 +1362,9 @@ void drawStatus(int player) int height = 1; char effectType[32] = ""; int spellID = getSpellIDFromSpellbook(item->type); - int damage = drawSpellTooltip(player, getSpellFromID(spellID), item, nullptr); + int damage = 0;;// drawSpellTooltip(player, getSpellFromID(spellID), item, nullptr); real_t dummy = 0.f; - getSpellEffectString(spellID, spellEffectText, effectType, damage, &height, &dummy); + //getSpellEffectString(spellID, spellEffectText, effectType, damage, &height, &dummy); int width = longestline(spellEffectText) * TTF12_WIDTH + 8; if ( width > src.w ) { @@ -3516,487 +3516,4 @@ void drawStatusNew(const int player) CalloutMenu[player].calloutFrame->setDisabled(true); } CalloutMenu[player].drawCalloutMenu(); -} - -int drawSpellTooltip(const int player, spell_t* spell, Item* item, SDL_Rect* src) -{ - const Sint32 mousex = inputs.getMouse(player, Inputs::X); - const Sint32 mousey = inputs.getMouse(player, Inputs::Y); - - SDL_Rect pos; - if ( src ) - { - pos.x = src->x; - pos.y = src->y; - } - else - { - pos.x = mousex + 16; - pos.y = mousey + 8; - } - bool spellbook = false; - if ( item && itemCategory(item) == SPELLBOOK ) - { - spellbook = true; - } - if ( spell ) - { - node_t* rootNode = spell->elements.first; - spellElement_t* elementRoot = nullptr; - if ( rootNode ) - { - elementRoot = (spellElement_t*)(rootNode->element); - } - int damage = 0; - int mana = 0; - int heal = 0; - spellElement_t* primaryElement = nullptr; - if ( elementRoot ) - { - node_t* primaryNode = elementRoot->elements.first; - mana = elementRoot->mana; - heal = mana; - if ( primaryNode ) - { - primaryElement = (spellElement_t*)(primaryNode->element); - if ( primaryElement ) - { - damage = primaryElement->damage; - } - } - if ( players[player] ) - { - int bonus = 0; - if ( spellbook ) - { - bonus = 25 * ((shouldInvertEquipmentBeatitude(stats[player]) ? abs(item->beatitude) : item->beatitude)); - if ( stats[player] ) - { - if ( players[player] && players[player]->entity ) - { - bonus += players[player]->entity->getINT() * 0.5; - } - else - { - bonus += statGetINT(stats[player], nullptr) * 0.5; - } - } - if ( bonus < 0 ) - { - bonus = 0; - } - } - damage += (damage * (bonus * 0.01 + getBonusFromCasterOfSpellElement(players[player]->entity, stats[player], primaryElement))); - heal += (heal * (bonus * 0.01 + getBonusFromCasterOfSpellElement(players[player]->entity, stats[player], primaryElement))); - } - if ( spell->ID == SPELL_HEALING || spell->ID == SPELL_EXTRAHEALING ) - { - damage = heal; - } - } - int spellInfoLines = 1; - char spellType[32] = ""; - char spellEffectText[256] = ""; - real_t sustainCostPerSecond = 0.f; - getSpellEffectString(spell->ID, spellEffectText, spellType, damage, &spellInfoLines, &sustainCostPerSecond); - char tempstr[64] = ""; - char spellNameString[128] = ""; - if ( item && item->appearance >= 1000 ) - { - // shapeshift spells, append the form name here. - switch ( spell->ID ) - { - case SPELL_SPEED: - case SPELL_DETECT_FOOD: - snprintf(spellNameString, 127, "%s (%s)", spell->getSpellName(), ItemTooltips.spellItems[SPELL_RAT_FORM].name.c_str()); - break; - case SPELL_POISON: - case SPELL_SPRAY_WEB: - snprintf(spellNameString, 127, "%s (%s)", spell->getSpellName(), ItemTooltips.spellItems[SPELL_SPIDER_FORM].name.c_str()); - break; - case SPELL_STRIKE: - case SPELL_FEAR: - case SPELL_TROLLS_BLOOD: - snprintf(spellNameString, 127, "%s (%s)", spell->getSpellName(), ItemTooltips.spellItems[SPELL_TROLL_FORM].name.c_str()); - break; - case SPELL_LIGHTNING: - case SPELL_CONFUSE: - case SPELL_AMPLIFY_MAGIC: - snprintf(spellNameString, 127, "%s (%s)", spell->getSpellName(), ItemTooltips.spellItems[SPELL_IMP_FORM].name.c_str()); - break; - default: - strncpy(spellNameString, spell->getSpellName(), 127); - break; - } - } - else - { - strncpy(spellNameString, spell->getSpellName(), 127); - } - - if ( spell->ID == SPELL_DOMINATE ) - { - snprintf(tempstr, 63, Language::get(2977), getCostOfSpell(spell)); - } - else if ( spell->ID == SPELL_DEMON_ILLUSION ) - { - snprintf(tempstr, 63, Language::get(3853), getCostOfSpell(spell)); - } - else - { - if ( players[player] && players[player]->entity ) - { - if ( sustainCostPerSecond > 0.01 ) - { - snprintf(tempstr, 31, Language::get(3325), - getCostOfSpell(spell, players[player]->entity), sustainCostPerSecond); - } - else - { - snprintf(tempstr, 31, Language::get(308), getCostOfSpell(spell, players[player]->entity)); - } - } - else - { - if ( sustainCostPerSecond > 0.01 ) - { - snprintf(tempstr, 31, Language::get(3325), - getCostOfSpell(spell), sustainCostPerSecond); - } - else - { - snprintf(tempstr, 31, Language::get(308), getCostOfSpell(spell)); - } - } - } - if ( strcmp(spellEffectText, "") ) - { - pos.w = (longestline(spellEffectText) + 1) * TTF12_WIDTH + 8; - } - else - { - pos.w = std::max(longestline(spellNameString), longestline(tempstr)) * TTF12_WIDTH + 8; - } - - if ( src ) - { - pos.x -= pos.w / 2; - } - - int furthestX = players[player]->camera_x2(); - if ( players[player]->characterSheet.proficienciesPage == 0 ) - { - if ( pos.y < players[player]->characterSheet.skillsSheetBox.y + players[player]->characterSheet.skillsSheetBox.h ) - { - furthestX = players[player]->camera_x2() - players[player]->characterSheet.skillsSheetBox.w; - } - } - else - { - if ( pos.y < players[player]->characterSheet.partySheetBox.y + players[player]->characterSheet.partySheetBox.h ) - { - furthestX = players[player]->camera_x2() - players[player]->characterSheet.partySheetBox.w; - } - } - - if ( pos.x + pos.w + 16 > furthestX ) // overflow right side of screen - { - pos.x -= (pos.w + 32); - } - pos.h = TTF12_HEIGHT * (2 + spellInfoLines + 1) + 8; - if ( spellInfoLines >= 4 ) - { - pos.h += 4; - } - else if ( spellInfoLines == 3 ) - { - pos.h += 2; - } - - if ( src ) - { - pos.y -= pos.h; - } - - if ( pos.y + pos.h + 16 > players[player]->camera_y2() ) // overflow bottom of screen - { - pos.y -= (pos.y + pos.h + 16 - players[player]->camera_y2()); - } - if ( spellbook ) - { - return damage; - } - - drawTooltip(&pos); - ttfPrintTextFormatted(ttf12, pos.x + 4, pos.y + 4, "%s\n%s\n%s", - spellNameString, tempstr, spellType); - Uint32 effectColor = uint32ColorLightBlue; - ttfPrintTextFormattedColor(ttf12, pos.x + 4, pos.y + 4, effectColor, - "\n\n\n%s", spellEffectText); - } - else - { - pos.w = longestline("Error: Spell doesn't exist!") * TTF12_WIDTH + 8; - pos.h = TTF12_HEIGHT + 8; - drawTooltip(&pos); - ttfPrintTextFormatted(ttf12, pos.x + 4, pos.y + 4, "%s", "Error: Spell doesn't exist!"); - } - return 0; -} - -void getSpellEffectString(int spellID, char effectTextBuffer[256], char spellType[32], int value, int* spellInfoLines, real_t* sustainCostPerSecond) -{ - if ( !spellInfoLines || !sustainCostPerSecond ) - { - return; - } - switch ( spellID ) - { - case SPELL_FORCEBOLT: - case SPELL_MAGICMISSILE: - case SPELL_LIGHTNING: - snprintf(effectTextBuffer, 255, Language::get(3289), value); - snprintf(spellType, 31, Language::get(3303)); - break; - case SPELL_COLD: - snprintf(effectTextBuffer, 255, Language::get(3290), value, Language::get(3294)); - snprintf(spellType, 31, Language::get(3303)); - *spellInfoLines = 2; - break; - case SPELL_POISON: - snprintf(effectTextBuffer, 255, Language::get(3290), value, Language::get(3300)); - snprintf(spellType, 31, Language::get(3303)); - *spellInfoLines = 2; - break; - case SPELL_FIREBALL: - snprintf(effectTextBuffer, 255, Language::get(3290), value, Language::get(3295)); - snprintf(spellType, 31, Language::get(3303)); - *spellInfoLines = 2; - break; - case SPELL_BLEED: - snprintf(effectTextBuffer, 255, Language::get(3291), value, Language::get(3297), Language::get(3294)); - *spellInfoLines = 2; - snprintf(spellType, 31, Language::get(3303)); - break; - case SPELL_SLOW: - snprintf(effectTextBuffer, 255, Language::get(3292), Language::get(3294)); - snprintf(spellType, 31, Language::get(3303)); - break; - case SPELL_SLEEP: - snprintf(effectTextBuffer, 255, Language::get(3292), Language::get(3298)); - snprintf(spellType, 31, Language::get(3303)); - break; - case SPELL_CONFUSE: - snprintf(effectTextBuffer, 255, Language::get(3292), Language::get(3299)); - snprintf(spellType, 31, Language::get(3303)); - break; - case SPELL_ACID_SPRAY: - snprintf(effectTextBuffer, 255, Language::get(3293), value, Language::get(3300)); - snprintf(spellType, 31, Language::get(3304)); - *spellInfoLines = 2; - break; - case SPELL_HEALING: - case SPELL_EXTRAHEALING: - { - snprintf(spellType, 31, Language::get(3301)); - snprintf(effectTextBuffer, 255, Language::get(3307), value); - *spellInfoLines = 2; - break; - } - case SPELL_REFLECT_MAGIC: - snprintf(spellType, 31, Language::get(3302)); - snprintf(effectTextBuffer, 255, Language::get(3308)); - *spellInfoLines = 2; - *sustainCostPerSecond = 6.f; - break; - case SPELL_LEVITATION: - snprintf(spellType, 31, Language::get(3302)); - snprintf(effectTextBuffer, 255, Language::get(3309)); - *sustainCostPerSecond = 0.6; - break; - case SPELL_INVISIBILITY: - snprintf(spellType, 31, Language::get(3302)); - snprintf(effectTextBuffer, 255, Language::get(3310)); - *sustainCostPerSecond = 1.f; - break; - case SPELL_LIGHT: - snprintf(spellType, 31, Language::get(3302)); - snprintf(effectTextBuffer, 255, Language::get(3311)); - *sustainCostPerSecond = 15.f; - break; - case SPELL_REMOVECURSE: - snprintf(spellType, 31, Language::get(3305)); - snprintf(effectTextBuffer, 255, Language::get(3312)); - break; - case SPELL_IDENTIFY: - snprintf(spellType, 31, Language::get(3305)); - snprintf(effectTextBuffer, 255, Language::get(3313)); - break; - case SPELL_MAGICMAPPING: - snprintf(spellType, 31, Language::get(3305)); - snprintf(effectTextBuffer, 255, Language::get(3314)); - *spellInfoLines = 2; - break; - case SPELL_TELEPORTATION: - snprintf(spellType, 31, Language::get(3305)); - snprintf(effectTextBuffer, 255, Language::get(3315)); - break; - case SPELL_OPENING: - snprintf(spellType, 31, Language::get(3303)); - snprintf(effectTextBuffer, 255, Language::get(3316)); - break; - case SPELL_LOCKING: - snprintf(spellType, 31, Language::get(3303)); - snprintf(effectTextBuffer, 255, Language::get(3317)); - break; - case SPELL_CUREAILMENT: - snprintf(spellType, 31, Language::get(3301)); - snprintf(effectTextBuffer, 255, Language::get(3318)); - *spellInfoLines = 2; - break; - case SPELL_DIG: - snprintf(spellType, 31, Language::get(3303)); - snprintf(effectTextBuffer, 255, Language::get(3319)); - break; - case SPELL_SUMMON: - snprintf(spellType, 31, Language::get(3306)); - snprintf(effectTextBuffer, 255, Language::get(3320)); - *spellInfoLines = 3; - break; - case SPELL_STONEBLOOD: - snprintf(spellType, 31, Language::get(3304)); - snprintf(effectTextBuffer, 255, Language::get(3292), Language::get(3296)); - break; - case SPELL_DOMINATE: - snprintf(spellType, 31, Language::get(3303)); - snprintf(effectTextBuffer, 255, Language::get(3321)); - *spellInfoLines = 4; - break; - case SPELL_STEAL_WEAPON: - snprintf(spellType, 31, Language::get(3303)); - snprintf(effectTextBuffer, 255, Language::get(3322)); - *spellInfoLines = 2; - break; - case SPELL_DRAIN_SOUL: - snprintf(spellType, 31, Language::get(3303)); - snprintf(effectTextBuffer, 255, Language::get(3323), value); - *spellInfoLines = 2; - break; - case SPELL_VAMPIRIC_AURA: - snprintf(spellType, 31, Language::get(3302)); - snprintf(effectTextBuffer, 255, Language::get(3324)); - *spellInfoLines = 4; - *sustainCostPerSecond = 0.33; - break; - case SPELL_CHARM_MONSTER: - snprintf(spellType, 31, Language::get(3303)); - snprintf(effectTextBuffer, 255, Language::get(3326)); - *spellInfoLines = 3; - break; - case SPELL_REVERT_FORM: - snprintf(spellType, 31, Language::get(3305)); - snprintf(effectTextBuffer, 255, Language::get(3851)); - *spellInfoLines = 1; - break; - case SPELL_RAT_FORM: - snprintf(spellType, 31, Language::get(3305)); - snprintf(effectTextBuffer, 255, Language::get(3847)); - *spellInfoLines = 2; - break; - case SPELL_SPIDER_FORM: - snprintf(spellType, 31, Language::get(3305)); - snprintf(effectTextBuffer, 255, Language::get(3848)); - *spellInfoLines = 2; - break; - case SPELL_TROLL_FORM: - snprintf(spellType, 31, Language::get(3305)); - snprintf(effectTextBuffer, 255, Language::get(3849)); - *spellInfoLines = 2; - break; - case SPELL_IMP_FORM: - snprintf(spellType, 31, Language::get(3305)); - snprintf(effectTextBuffer, 255, Language::get(3850)); - *spellInfoLines = 2; - break; - case SPELL_SPRAY_WEB: - snprintf(spellType, 31, Language::get(3304)); - snprintf(effectTextBuffer, 255, Language::get(3834)); - *spellInfoLines = 4; - break; - case SPELL_SPEED: - snprintf(spellType, 31, Language::get(3301)); - snprintf(effectTextBuffer, 255, Language::get(3835)); - *spellInfoLines = 2; - break; - case SPELL_FEAR: - snprintf(spellType, 31, Language::get(3301)); - snprintf(effectTextBuffer, 255, Language::get(3836)); - *spellInfoLines = 3; - break; - case SPELL_STRIKE: - snprintf(spellType, 31, Language::get(3305)); - snprintf(effectTextBuffer, 255, Language::get(3838)); - *spellInfoLines = 4; - break; - case SPELL_DETECT_FOOD: - snprintf(spellType, 31, Language::get(3305)); - snprintf(effectTextBuffer, 255, Language::get(3839)); - *spellInfoLines = 1; - break; - case SPELL_WEAKNESS: - snprintf(spellType, 31, Language::get(3303)); - snprintf(effectTextBuffer, 255, Language::get(3837)); - *spellInfoLines = 2; - break; - case SPELL_AMPLIFY_MAGIC: - snprintf(spellType, 31, Language::get(3302)); - snprintf(effectTextBuffer, 255, Language::get(3852)); - *spellInfoLines = 2; - *sustainCostPerSecond = 0.25; - break; - case SPELL_SHADOW_TAG: - snprintf(spellType, 31, Language::get(3303)); - snprintf(effectTextBuffer, 255, Language::get(3843)); - *spellInfoLines = 3; - break; - case SPELL_TELEPULL: - snprintf(spellType, 31, Language::get(3303)); - snprintf(effectTextBuffer, 255, Language::get(3844)); - *spellInfoLines = 3; - break; - case SPELL_DEMON_ILLUSION: - snprintf(spellType, 31, Language::get(3303)); - snprintf(effectTextBuffer, 255, Language::get(3845)); - *spellInfoLines = 3; - break; - case SPELL_TROLLS_BLOOD: - snprintf(spellType, 31, Language::get(3301)); - snprintf(effectTextBuffer, 255, Language::get(3840)); - *spellInfoLines = 2; - break; - case SPELL_SALVAGE: - snprintf(spellType, 31, Language::get(3301)); - snprintf(effectTextBuffer, 255, Language::get(3846)); - *spellInfoLines = 2; - break; - case SPELL_FLUTTER: - snprintf(spellType, 31, Language::get(3305)); - snprintf(effectTextBuffer, 255, Language::get(3841)); - *spellInfoLines = 1; - break; - case SPELL_DASH: - snprintf(spellType, 31, Language::get(3305)); - snprintf(effectTextBuffer, 255, Language::get(3842)); - *spellInfoLines = 3; - break; - case SPELL_SELF_POLYMORPH: - snprintf(spellType, 31, Language::get(3305)); - snprintf(effectTextBuffer, 255, Language::get(3886)); - *spellInfoLines = 2; - break; - case SPELL_CRAB_FORM: - case SPELL_CRAB_WEB: - default: - break; - } -} +} \ No newline at end of file diff --git a/src/interface/playerinventory.cpp b/src/interface/playerinventory.cpp index 79bca70bd..27fd325cd 100644 --- a/src/interface/playerinventory.cpp +++ b/src/interface/playerinventory.cpp @@ -653,269 +653,6 @@ Player::PaperDoll_t::PaperDollSlotType getPaperDollSlotFromItemType(Item& item) return dollSlot; } -void drawItemTooltip(const int player, Item* item, SDL_Rect& src) -{ - if ( !item ) - { - return; - } - - std::string tooltipString = ""; - char tooltipBuffer[1024]; - - src.w = std::max(13, longestline(item->description())) * TTF12_WIDTH + 8; - src.h = TTF12_HEIGHT * 4 + 8; - char spellEffectText[256] = ""; - if ( item->identified ) - { - bool learnedSpellbook = false; - if ( itemCategory(item) == SPELLBOOK ) - { - learnedSpellbook = playerLearnedSpellbook(player, item); - if ( !learnedSpellbook && stats[player] && players[player] && players[player]->entity ) - { - // spellbook tooltip shows if you have the magic requirement as well (for goblins) - int skillLVL = stats[player]->PROFICIENCIES[PRO_MAGIC] + statGetINT(stats[player], players[player]->entity); - spell_t* spell = getSpellFromID(getSpellIDFromSpellbook(item->type)); - if ( spell && skillLVL >= spell->difficulty ) - { - learnedSpellbook = true; - } - } - } - - if ( itemCategory(item) == WEAPON || itemCategory(item) == ARMOR || itemCategory(item) == THROWN - || itemTypeIsQuiver(item->type) ) - { - src.h += TTF12_HEIGHT; - } - else if ( itemCategory(item) == SCROLL && item->identified ) - { - src.h += TTF12_HEIGHT; - src.w = std::max((2 + longestline(Language::get(3862)) + longestline(item->getScrollLabel())) * TTF12_WIDTH + 8, src.w); - } - else if ( itemCategory(item) == SPELLBOOK && learnedSpellbook ) - { - int height = 1; - char effectType[32] = ""; - int spellID = getSpellIDFromSpellbook(item->type); - int damage = drawSpellTooltip(player, getSpellFromID(spellID), item, nullptr); - real_t dummy = 0.f; - getSpellEffectString(spellID, spellEffectText, effectType, damage, &height, &dummy); - int width = longestline(spellEffectText) * TTF12_WIDTH + 8; - if ( width > src.w ) - { - src.w = width; - } - src.h += height * TTF12_HEIGHT; - } - else if ( item->type == TOOL_GYROBOT || item->type == TOOL_DUMMYBOT - || item->type == TOOL_SENTRYBOT || item->type == TOOL_SPELLBOT - || (item->type == ENCHANTED_FEATHER && item->identified) ) - { - src.w += 7 * TTF12_WIDTH; - } - } - int furthestX = players[player]->camera_x2(); - if ( players[player]->characterSheet.proficienciesPage == 0 ) - { - if ( src.y < players[player]->characterSheet.skillsSheetBox.y + players[player]->characterSheet.skillsSheetBox.h ) - { - furthestX = players[player]->camera_x2() - players[player]->characterSheet.skillsSheetBox.w; - } - } - else - { - if ( src.y < players[player]->characterSheet.partySheetBox.y + players[player]->characterSheet.partySheetBox.h ) - { - furthestX = players[player]->camera_x2() - players[player]->characterSheet.partySheetBox.w; - } - } - if ( src.x + src.w + 16 > furthestX ) // overflow right side of screen - { - src.x -= (src.w + 32); - } - - drawTooltip(&src); - - Uint32 color = 0xFFFFFFFF; - if ( !item->identified ) - { - color = makeColorRGB(255, 255, 0); - ttfPrintTextFormattedColor(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT, color, Language::get(309)); - tooltipString += Language::get(309); - tooltipString += "\r\n"; - } - else - { - if ( item->beatitude < 0 ) - { - //Red if cursed - color = makeColorRGB(255, 0, 0); - ttfPrintTextFormattedColor(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT, color, Language::get(310)); - tooltipString += Language::get(310); - tooltipString += "\r\n"; - } - else if ( item->beatitude == 0 ) - { - //White if normal item. - color = 0xFFFFFFFF; - ttfPrintTextFormattedColor(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT, color, Language::get(311)); - tooltipString += Language::get(311); - tooltipString += "\r\n"; - } - else - { - //Green if blessed. - if ( colorblind ) - { - color = makeColorRGB(100, 245, 255); //Light blue if colorblind - } - else - { - color = makeColorRGB(0, 255, 0); - } - - ttfPrintTextFormattedColor(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT, color, Language::get(312)); - tooltipString += Language::get(312); - tooltipString += "\r\n"; - } - } - if ( item->beatitude == 0 || !item->identified ) - { - color = 0xFFFFFFFF; - } - - if ( item->type == TOOL_GYROBOT || item->type == TOOL_DUMMYBOT - || item->type == TOOL_SENTRYBOT || item->type == TOOL_SPELLBOT ) - { - int health = 100; - if ( !item->tinkeringBotIsMaxHealth() ) - { - health = 25 * (item->appearance % 10); - if ( health == 0 && item->status != BROKEN ) - { - health = 5; - } - } - ttfPrintTextFormattedColor(ttf12, src.x + 4, src.y + 4, color, "%s (%d%%)", item->description(), health); - - snprintf(tooltipBuffer, sizeof(tooltipBuffer), "%s (%d%%)", item->description(), health); - tooltipString += tooltipBuffer; - tooltipString += "\r\n"; - } - else if ( item->type == ENCHANTED_FEATHER && item->identified ) - { - ttfPrintTextFormattedColor(ttf12, src.x + 4, src.y + 4, color, "%s (%d%%)", item->description(), item->appearance % ENCHANTED_FEATHER_MAX_DURABILITY); - - snprintf(tooltipBuffer, sizeof(tooltipBuffer), "%s (%d%%)", item->description(), item->appearance % ENCHANTED_FEATHER_MAX_DURABILITY); - tooltipString += tooltipBuffer; - tooltipString += "\r\n"; - } - else - { - ttfPrintTextFormattedColor(ttf12, src.x + 4, src.y + 4, color, "%s", item->description()); - - tooltipString += item->description(); - tooltipString += "\r\n"; - } - int itemWeight = item->getWeight(); - ttfPrintTextFormatted(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT * 2, Language::get(313), itemWeight); - snprintf(tooltipBuffer, sizeof(tooltipBuffer), Language::get(313), itemWeight); - tooltipString += tooltipBuffer; - tooltipString += "\r\n"; - - ttfPrintTextFormatted(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT * 3, Language::get(314), item->sellValue(player)); - snprintf(tooltipBuffer, sizeof(tooltipBuffer), Language::get(314), item->sellValue(player)); - tooltipString += tooltipBuffer; - tooltipString += "\r\n"; - - if ( strcmp(spellEffectText, "") ) - { - ttfPrintTextFormattedColor(ttf12, src.x + 4, src.y + 4 + TTF12_HEIGHT * 4, makeColorRGB(0, 255, 255), spellEffectText); - tooltipString += spellEffectText; - tooltipString += "\r\n"; - } - - if ( item->identified ) - { - if ( itemCategory(item) == WEAPON || itemCategory(item) == THROWN - || itemTypeIsQuiver(item->type) ) - { - Monster tmpRace = stats[player]->type; - if ( stats[player]->type == TROLL - || stats[player]->type == RAT - || stats[player]->type == SPIDER - || stats[player]->type == CREATURE_IMP ) - { - // these monsters have 0 bonus from weapons, but want the tooltip to say the normal amount. - stats[player]->type = HUMAN; - } - - if ( item->weaponGetAttack(stats[player]) >= 0 ) - { - color = makeColorRGB(0, 255, 255); - } - else - { - color = makeColorRGB(255, 0, 0); - } - if ( stats[player]->type != tmpRace ) - { - color = makeColorRGB(127, 127, 127); // grey out the text if monster doesn't benefit. - } - - ttfPrintTextFormattedColor(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT * 4, color, Language::get(315), item->weaponGetAttack(stats[player])); - - snprintf(tooltipBuffer, sizeof(tooltipBuffer), Language::get(315), item->weaponGetAttack(stats[player])); - tooltipString += tooltipBuffer; - tooltipString += "\r\n"; - - stats[player]->type = tmpRace; - } - else if ( itemCategory(item) == ARMOR ) - { - Monster tmpRace = stats[player]->type; - if ( stats[player]->type == TROLL - || stats[player]->type == RAT - || stats[player]->type == SPIDER - || stats[player]->type == CREATURE_IMP ) - { - // these monsters have 0 bonus from weapons, but want the tooltip to say the normal amount. - stats[player]->type = HUMAN; - } - - if ( item->armorGetAC(stats[player]) >= 0 ) - { - color = makeColorRGB(0, 255, 255); - } - else - { - color = makeColorRGB(255, 0, 0); - } - if ( stats[player]->type != tmpRace ) - { - color = makeColorRGB(127, 127, 127); // grey out the text if monster doesn't benefit. - } - - ttfPrintTextFormattedColor(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT * 4, color, Language::get(316), item->armorGetAC(stats[player])); - snprintf(tooltipBuffer, sizeof(tooltipBuffer), Language::get(316), item->armorGetAC(stats[player])); - tooltipString += tooltipBuffer; - tooltipString += "\r\n"; - - stats[player]->type = tmpRace; - } - else if ( itemCategory(item) == SCROLL ) - { - color = makeColorRGB(0, 255, 255); - ttfPrintTextFormattedColor(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT * 4, color, "%s%s", Language::get(3862), item->getScrollLabel()); - snprintf(tooltipBuffer, sizeof(tooltipBuffer), "%s%s", Language::get(3862), item->getScrollLabel()); - tooltipString += tooltipBuffer; - tooltipString += "\r\n"; - } - } -} - Player::PaperDoll_t::PaperDollSlotType Player::PaperDoll_t::paperDollSlotFromCoordinates(int x, int y) const { auto slot = PaperDollSlotType::SLOT_MAX; diff --git a/src/magic/magic.hpp b/src/magic/magic.hpp index ec6fbb7fa..ef498d7c1 100644 --- a/src/magic/magic.hpp +++ b/src/magic/magic.hpp @@ -634,6 +634,4 @@ bool spellEffectTeleportPull(Entity* my, spellElement_t& element, Entity* parent void spellEffectShadowTag(Entity& my, spellElement_t& element, Entity* parent, int resistance); bool spellEffectDemonIllusion(Entity& my, spellElement_t& element, Entity* parent, Entity* target, int resistance); -void freeSpells(); -int drawSpellTooltip(const int player, spell_t* spell, Item* item, SDL_Rect* src); -void getSpellEffectString(int spellID, char effectTextBuffer[256], char spellType[32], int value, int* spellInfoLines, real_t* sustainCostPerSecond); +void freeSpells(); \ No newline at end of file diff --git a/src/menu.cpp b/src/menu.cpp index 7ac63597e..3e262da26 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -358,193 +358,6 @@ void changeSettingsTab(int option) } } -std::vector> menuOptions; -void initMenuOptions() -{ - menuOptions.clear(); -#if (defined USE_EOS && !defined STEAMWORKS) - menuOptions.push_back(std::make_pair(Language::get(1303), 1)); // start game - menuOptions.push_back(std::make_pair(Language::get(1304), 2)); // intro - menuOptions.push_back(std::make_pair(Language::get(3964), 3)); // hall of trials - menuOptions.push_back(std::make_pair(Language::get(1305), 4)); // statistics - menuOptions.push_back(std::make_pair(Language::get(3971), 5)); // achievements - menuOptions.push_back(std::make_pair(Language::get(1306), 6)); // settings - menuOptions.push_back(std::make_pair(Language::get(1307), 7)); // credits - menuOptions.push_back(std::make_pair(Language::get(2978), 8)); // custom content - menuOptions.push_back(std::make_pair(Language::get(1308), 9)); // quit -#else - menuOptions.push_back(std::make_pair(Language::get(1303), 1)); // start game - menuOptions.push_back(std::make_pair(Language::get(1304), 2)); // intro - menuOptions.push_back(std::make_pair(Language::get(3964), 3)); // hall of trials - menuOptions.push_back(std::make_pair(Language::get(1305), 4)); // statistics - menuOptions.push_back(std::make_pair(Language::get(1306), 5)); // settings - menuOptions.push_back(std::make_pair(Language::get(1307), 6)); // credits - menuOptions.push_back(std::make_pair(Language::get(2978), 7)); // custom content -#ifdef STEAMWORKS - menuOptions.push_back(std::make_pair(Language::get(2979), 8)); // workshop - menuOptions.push_back(std::make_pair(Language::get(1308), 9)); // quit -#else - menuOptions.push_back(std::make_pair(Language::get(1308), 8)); // quit -#endif -#endif -} - -void getPrevMenuOption(int& currentMenuOption) -{ - for ( auto it = menuOptions.begin(); it != menuOptions.end(); ) - { - if ( (*it).second == currentMenuOption ) - { - if ( it == menuOptions.begin() ) - { - currentMenuOption = menuOptions.back().second; - return; - } - --it; - currentMenuOption = (*it).second; - return; - } - ++it; - } - - currentMenuOption = menuOptions.at(0).second; // default value. -} - -void getNextMenuOption(int& currentMenuOption) -{ - for ( auto it = menuOptions.begin(); it != menuOptions.end(); ) - { - if ( (*it).second == currentMenuOption ) - { - ++it; - if ( it == menuOptions.end() ) - { - currentMenuOption = menuOptions.at(0).second; - return; - } - currentMenuOption = (*it).second; - return; - } - ++it; - } - - currentMenuOption = menuOptions.at(0).second; // default value. -} - -void navigateMainMenuItems(bool mode) -{ - if ( menuOptions.empty() ) - { - return; - } - - int numInGameMenuOptions = 4 + (multiplayer != CLIENT); - if ( !mode && gameModeManager.getMode() == GameModeManager_t::GAME_MODE_TUTORIAL ) - { - if ( !strcmp(map.name, "Tutorial Hub") ) - { - numInGameMenuOptions = 4; - } - } - -#if defined USE_EOS && !defined STEAMWORKS - numInGameMenuOptions += 1; -#endif - - int warpx, warpy; - if (menuselect == 0) - { - //No menu item selected. - if ( keystatus[SDLK_UP] || (inputs.bControllerInputPressed(clientnum, INJOY_DPAD_UP) && rebindaction == -1) ) - { - keystatus[SDLK_UP] = 0; - if ( rebindaction == -1 ) - { - inputs.controllerClearInput(clientnum, INJOY_DPAD_UP); - } - inputs.hideMouseCursors(); - menuselect = menuOptions.at(0).second; - //Warp cursor to menu item, for gamepad convenience. - warpx = 50 + 18; - warpy = (yres / 4) + 80 + (18 / 2); //I am a wizard. I hate magic numbers. - //SDL_WarpMouseInWindow(screen, warpx, warpy); - Uint32 flags = (Inputs::SET_MOUSE | Inputs::SET_CONTROLLER); - inputs.warpMouse(clientnum, warpx, warpy, flags); - } - else if ( keystatus[SDLK_DOWN] || (inputs.bControllerInputPressed(clientnum, INJOY_DPAD_DOWN) && rebindaction == -1) ) - { - keystatus[SDLK_DOWN] = 0; - if ( rebindaction == -1 ) - { - inputs.controllerClearInput(clientnum, INJOY_DPAD_DOWN); - } - inputs.hideMouseCursors(); - menuselect = menuOptions.at(0).second; - warpx = 50 + 18; - warpy = (yres / 4) + 80 + (18 / 2); - //SDL_WarpMouseInWindow(screen, warpx, warpy); - Uint32 flags = (Inputs::SET_MOUSE | Inputs::SET_CONTROLLER); - inputs.warpMouse(clientnum, warpx, warpy, flags); - } - } - else - { - if ( keystatus[SDLK_UP] || (inputs.bControllerInputPressed(clientnum, INJOY_DPAD_UP) && rebindaction == -1) ) - { - keystatus[SDLK_UP] = 0; - if ( rebindaction == -1 ) - { - inputs.controllerClearInput(clientnum, INJOY_DPAD_UP); - } - inputs.hideMouseCursors(); - if ( mode ) - { - getPrevMenuOption(menuselect); - } - else - { - menuselect--; - if ( menuselect == 0 ) - { - menuselect = numInGameMenuOptions; - } - } - - warpx = 50 + 18; - warpy = (((yres / 4) + 80 + (18 / 2)) + ((menuselect - 1) * 24)); - //SDL_WarpMouseInWindow(screen, warpx, warpy); - Uint32 flags = (Inputs::SET_MOUSE | Inputs::SET_CONTROLLER); - inputs.warpMouse(clientnum, warpx, warpy, flags); - } - else if (keystatus[SDLK_DOWN] || (inputs.bControllerInputPressed(clientnum, INJOY_DPAD_DOWN) && rebindaction == -1) ) - { - keystatus[SDLK_DOWN] = 0; - if ( rebindaction == -1 ) - { - inputs.controllerClearInput(clientnum, INJOY_DPAD_DOWN); - } - inputs.hideMouseCursors(); - if ( mode ) - { - getNextMenuOption(menuselect); - } - else - { - menuselect++; - if ( menuselect > numInGameMenuOptions ) - { - menuselect = 1; - } - } - warpx = 50 + 18; - warpy = (((yres / 4) + 80 + (18 / 2)) + ((menuselect - 1) * 24)); - //SDL_WarpMouseInWindow(screen, warpx, warpy); - Uint32 flags = (Inputs::SET_MOUSE | Inputs::SET_CONTROLLER); - inputs.warpMouse(clientnum, warpx, warpy, flags); - } - } -} - void inline printJoybindingNames(const SDL_Rect& currentPos, int c, bool &rebindingaction) { Sint32 omousex = inputs.getMouse(clientnum, Inputs::MouseInputs::OX); diff --git a/src/menu.hpp b/src/menu.hpp index bd08b1a55..5000a81e9 100644 --- a/src/menu.hpp +++ b/src/menu.hpp @@ -291,9 +291,6 @@ void buttonAcceptResolution(button_t* my); void buttonRevertResolution(button_t* my); void revertResolution(); -extern std::vector> menuOptions; -void initMenuOptions(); - class Stat; int isCharacterValidFromDLC(Stat& myStats, int characterClass); From b027e0acc3cfd7bc3f012ce5a8083055e172890f Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sun, 8 Oct 2023 12:16:02 +1100 Subject: [PATCH 110/146] * item_names and item_tooltips can be read partially from modded sources --- src/mod_tools.cpp | 338 ++++++++++++++++++++++++++-------------------- src/mod_tools.hpp | 4 +- 2 files changed, 197 insertions(+), 145 deletions(-) diff --git a/src/mod_tools.cpp b/src/mod_tools.cpp index 18f698db0..8198af251 100644 --- a/src/mod_tools.cpp +++ b/src/mod_tools.cpp @@ -957,7 +957,7 @@ void ItemTooltips_t::readItemsFromFile() } -void ItemTooltips_t::readItemLocalizationsFromFile() +void ItemTooltips_t::readItemLocalizationsFromFile(bool forceLoadBaseDirectory) { if ( !PHYSFS_getRealDir("/lang/item_names.json") ) { @@ -966,6 +966,22 @@ void ItemTooltips_t::readItemLocalizationsFromFile() } std::string inputPath = PHYSFS_getRealDir("/lang/item_names.json"); + if ( forceLoadBaseDirectory ) + { + inputPath = BASE_DATA_DIR; + } + else + { + if ( inputPath != BASE_DATA_DIR ) + { + readItemLocalizationsFromFile(true); // force load the base directory first, then modded paths later. + } + else + { + forceLoadBaseDirectory = true; + } + } + inputPath.append("/lang/item_names.json"); File* fp = FileIO::open(inputPath.c_str(), "rb"); @@ -1008,7 +1024,10 @@ void ItemTooltips_t::readItemLocalizationsFromFile() if ( d.HasMember("items") ) { - itemNameLocalizations.clear(); + if ( forceLoadBaseDirectory ) + { + itemNameLocalizations.clear(); + } for ( rapidjson::Value::ConstMemberIterator items_itr = d["items"].MemberBegin(); items_itr != d["items"].MemberEnd(); ++items_itr ) { @@ -1033,7 +1052,10 @@ void ItemTooltips_t::readItemLocalizationsFromFile() if ( d.HasMember("spell_names") ) { - spellNameLocalizations.clear(); + if ( forceLoadBaseDirectory ) + { + spellNameLocalizations.clear(); + } for ( rapidjson::Value::ConstMemberIterator spell_itr = d["spell_names"].MemberBegin(); spell_itr != d["spell_names"].MemberEnd(); ++spell_itr ) { @@ -1086,7 +1108,7 @@ void ItemTooltips_t::readItemLocalizationsFromFile() } #ifndef EDITOR -void ItemTooltips_t::readTooltipsFromFile() +void ItemTooltips_t::readTooltipsFromFile(bool forceLoadBaseDirectory) { if ( !PHYSFS_getRealDir("/items/item_tooltips.json") ) { @@ -1095,6 +1117,22 @@ void ItemTooltips_t::readTooltipsFromFile() } std::string inputPath = PHYSFS_getRealDir("/items/item_tooltips.json"); + if ( forceLoadBaseDirectory ) + { + inputPath = BASE_DATA_DIR; + } + else + { + if ( inputPath != BASE_DATA_DIR ) + { + readTooltipsFromFile(true); // force load the base directory first, then modded paths later. + } + else + { + forceLoadBaseDirectory = true; + } + } + inputPath.append("/items/item_tooltips.json"); File* fp = FileIO::open(inputPath.c_str(), "rb"); @@ -1128,24 +1166,30 @@ void ItemTooltips_t::readTooltipsFromFile() return; } - if ( !d.HasMember("version") || !d.HasMember("tooltips") ) + if ( !d.HasMember("version") ) { printlog("[JSON]: Error: No 'version' value in json file, or JSON syntax incorrect! %s", inputPath.c_str()); return; } int version = d["version"].GetInt(); - adjectives.clear(); - for ( rapidjson::Value::ConstMemberIterator adj_itr = d["adjectives"].MemberBegin(); - adj_itr != d["adjectives"].MemberEnd(); ++adj_itr ) + if ( forceLoadBaseDirectory ) + { + adjectives.clear(); + } + if ( d.HasMember("adjectives") ) { - std::map m; - for ( rapidjson::Value::ConstMemberIterator inner_itr = adj_itr->value.MemberBegin(); - inner_itr != adj_itr->value.MemberEnd(); ++inner_itr ) + for ( rapidjson::Value::ConstMemberIterator adj_itr = d["adjectives"].MemberBegin(); + adj_itr != d["adjectives"].MemberEnd(); ++adj_itr ) { - m[inner_itr->name.GetString()] = inner_itr->value.GetString(); + std::map m; + for ( rapidjson::Value::ConstMemberIterator inner_itr = adj_itr->value.MemberBegin(); + inner_itr != adj_itr->value.MemberEnd(); ++inner_itr ) + { + m[inner_itr->name.GetString()] = inner_itr->value.GetString(); + } + adjectives[adj_itr->name.GetString()] = m; } - adjectives[adj_itr->name.GetString()] = m; } if ( d.HasMember("default_text_colors") ) @@ -1192,7 +1236,10 @@ void ItemTooltips_t::readTooltipsFromFile() d["default_text_colors"]["faint_text"]["a"].GetInt()); } - templates.clear(); + if ( forceLoadBaseDirectory ) + { + templates.clear(); + } if ( d.HasMember("templates") ) { for ( rapidjson::Value::ConstMemberIterator template_itr = d["templates"].MemberBegin(); @@ -1213,179 +1260,184 @@ void ItemTooltips_t::readTooltipsFromFile() } } - tooltips.clear(); - + if ( forceLoadBaseDirectory ) + { + tooltips.clear(); + } std::unordered_set tagsRead; - for ( rapidjson::Value::ConstMemberIterator tooltipType_itr = d["tooltips"].MemberBegin(); - tooltipType_itr != d["tooltips"].MemberEnd(); ++tooltipType_itr ) + if ( d.HasMember("tooltips") ) { - ItemTooltip_t tooltip; - tooltip.setColorHeading(this->defaultHeadingTextColor); - tooltip.setColorDescription(this->defaultDescriptionTextColor); - tooltip.setColorDetails(this->defaultDetailsTextColor); - tooltip.setColorPositive(this->defaultPositiveTextColor); - tooltip.setColorNegative(this->defaultNegativeTextColor); - tooltip.setColorStatus(this->defaultStatusEffectTextColor); - tooltip.setColorFaintText(this->defaultFaintTextColor); - - if ( tooltipType_itr->value.HasMember("icons") ) + for ( rapidjson::Value::ConstMemberIterator tooltipType_itr = d["tooltips"].MemberBegin(); + tooltipType_itr != d["tooltips"].MemberEnd(); ++tooltipType_itr ) { - if ( !tooltipType_itr->value["icons"].IsArray() ) - { - printlog("[JSON]: Error: 'icons' entry for tooltip %s did not have [] format", tooltipType_itr->name.GetString()); - } - else + ItemTooltip_t tooltip; + tooltip.setColorHeading(this->defaultHeadingTextColor); + tooltip.setColorDescription(this->defaultDescriptionTextColor); + tooltip.setColorDetails(this->defaultDetailsTextColor); + tooltip.setColorPositive(this->defaultPositiveTextColor); + tooltip.setColorNegative(this->defaultNegativeTextColor); + tooltip.setColorStatus(this->defaultStatusEffectTextColor); + tooltip.setColorFaintText(this->defaultFaintTextColor); + + if ( tooltipType_itr->value.HasMember("icons") ) { - for ( auto icons = tooltipType_itr->value["icons"].Begin(); - icons != tooltipType_itr->value["icons"].End(); ++icons ) + if ( !tooltipType_itr->value["icons"].IsArray() ) { - // you need to FindMember() if getting objects from an array... - auto textMember = icons->FindMember("text"); - auto iconPathMember = icons->FindMember("icon_path"); - if ( !textMember->value.IsString() || !iconPathMember->value.IsString() ) + printlog("[JSON]: Error: 'icons' entry for tooltip %s did not have [] format", tooltipType_itr->name.GetString()); + } + else + { + for ( auto icons = tooltipType_itr->value["icons"].Begin(); + icons != tooltipType_itr->value["icons"].End(); ++icons ) { - printlog("[JSON]: Error: Icon text or path was not string!"); - continue; - } + // you need to FindMember() if getting objects from an array... + auto textMember = icons->FindMember("text"); + auto iconPathMember = icons->FindMember("icon_path"); + if ( !textMember->value.IsString() || !iconPathMember->value.IsString() ) + { + printlog("[JSON]: Error: Icon text or path was not string!"); + continue; + } - tooltip.icons.push_back(ItemTooltipIcons_t(iconPathMember->value.GetString(), textMember->value.GetString())); + tooltip.icons.push_back(ItemTooltipIcons_t(iconPathMember->value.GetString(), textMember->value.GetString())); - Uint32 color = this->defaultIconTextColor; - if ( icons->HasMember("color") && icons->FindMember("color")->value.HasMember("r") ) - { - // icons->FindMember("color")->value.isObject() always returning true?? so check for "r" member instead - color = makeColor( - icons->FindMember("color")->value["r"].GetInt(), - icons->FindMember("color")->value["g"].GetInt(), - icons->FindMember("color")->value["b"].GetInt(), - icons->FindMember("color")->value["a"].GetInt()); - } - tooltip.icons[tooltip.icons.size() - 1].setColor(color); - if ( icons->HasMember("conditional_attribute") ) - { - tooltip.icons[tooltip.icons.size() - 1].setConditionalAttribute(icons->FindMember("conditional_attribute")->value.GetString()); + Uint32 color = this->defaultIconTextColor; + if ( icons->HasMember("color") && icons->FindMember("color")->value.HasMember("r") ) + { + // icons->FindMember("color")->value.isObject() always returning true?? so check for "r" member instead + color = makeColor( + icons->FindMember("color")->value["r"].GetInt(), + icons->FindMember("color")->value["g"].GetInt(), + icons->FindMember("color")->value["b"].GetInt(), + icons->FindMember("color")->value["a"].GetInt()); + } + tooltip.icons[tooltip.icons.size() - 1].setColor(color); + if ( icons->HasMember("conditional_attribute") ) + { + tooltip.icons[tooltip.icons.size() - 1].setConditionalAttribute(icons->FindMember("conditional_attribute")->value.GetString()); + } } } } - } - if ( tooltipType_itr->value.HasMember("description") ) - { - if ( tooltipType_itr->value["description"].IsString() ) + if ( tooltipType_itr->value.HasMember("description") ) { - //printlog("[JSON]: Found template string '%s' for tooltip '%s'", tooltipType_itr->value["description"].GetString(), tooltipType_itr->name.GetString()); - if ( templates.find(tooltipType_itr->value["description"].GetString()) != templates.end() ) + if ( tooltipType_itr->value["description"].IsString() ) { - tooltip.descriptionText = templates[tooltipType_itr->value["description"].GetString()]; + //printlog("[JSON]: Found template string '%s' for tooltip '%s'", tooltipType_itr->value["description"].GetString(), tooltipType_itr->name.GetString()); + if ( templates.find(tooltipType_itr->value["description"].GetString()) != templates.end() ) + { + tooltip.descriptionText = templates[tooltipType_itr->value["description"].GetString()]; + } + else + { + printlog("[JSON]: Error: Could not find template tag '%s'", tooltipType_itr->value["description"].GetString()); + } } else { - printlog("[JSON]: Error: Could not find template tag '%s'", tooltipType_itr->value["description"].GetString()); + for ( auto descriptions = tooltipType_itr->value["description"].Begin(); + descriptions != tooltipType_itr->value["description"].End(); ++descriptions ) + { + tooltip.descriptionText.push_back(descriptions->GetString()); + } } } - else + + if ( tooltipType_itr->value.HasMember("details") ) { - for ( auto descriptions = tooltipType_itr->value["description"].Begin(); - descriptions != tooltipType_itr->value["description"].End(); ++descriptions ) + if ( !tooltipType_itr->value["details"].IsArray() ) { - tooltip.descriptionText.push_back(descriptions->GetString()); + printlog("[JSON]: Error: 'details' entry for tooltip '%s' did not have [] format!", tooltipType_itr->name.GetString()); } - } - } - - if ( tooltipType_itr->value.HasMember("details") ) - { - if ( !tooltipType_itr->value["details"].IsArray() ) - { - printlog("[JSON]: Error: 'details' entry for tooltip '%s' did not have [] format!", tooltipType_itr->name.GetString()); - } - else - { - for ( auto details_itr = tooltipType_itr->value["details"].Begin(); - details_itr != tooltipType_itr->value["details"].End(); ++details_itr ) + else { - for ( auto keyValue_itr = details_itr->MemberBegin(); - keyValue_itr != details_itr->MemberEnd(); ++keyValue_itr ) + for ( auto details_itr = tooltipType_itr->value["details"].Begin(); + details_itr != tooltipType_itr->value["details"].End(); ++details_itr ) { - tagsRead.insert(keyValue_itr->name.GetString()); - std::vector detailEntry; - if ( keyValue_itr->value.IsString() ) + for ( auto keyValue_itr = details_itr->MemberBegin(); + keyValue_itr != details_itr->MemberEnd(); ++keyValue_itr ) { - //printlog("[JSON]: Found template string '%s' for tooltip '%s'", keyValue_itr->value.GetString(), tooltipType_itr->name.GetString()); - if ( templates.find(keyValue_itr->value.GetString()) != templates.end() ) + tagsRead.insert(keyValue_itr->name.GetString()); + std::vector detailEntry; + if ( keyValue_itr->value.IsString() ) { - detailEntry = templates[keyValue_itr->value.GetString()]; + //printlog("[JSON]: Found template string '%s' for tooltip '%s'", keyValue_itr->value.GetString(), tooltipType_itr->name.GetString()); + if ( templates.find(keyValue_itr->value.GetString()) != templates.end() ) + { + detailEntry = templates[keyValue_itr->value.GetString()]; + } + else + { + printlog("[JSON]: Error: Could not find template tag '%s'", keyValue_itr->value.GetString()); + } } else { - printlog("[JSON]: Error: Could not find template tag '%s'", keyValue_itr->value.GetString()); - } - } - else - { - for ( auto detailTag = keyValue_itr->value.Begin(); - detailTag != keyValue_itr->value.End(); ++detailTag ) - { - detailEntry.push_back(detailTag->GetString()); + for ( auto detailTag = keyValue_itr->value.Begin(); + detailTag != keyValue_itr->value.End(); ++detailTag ) + { + detailEntry.push_back(detailTag->GetString()); + } } + tooltip.detailsText[keyValue_itr->name.GetString()] = detailEntry; + tooltip.detailsTextInsertOrder.push_back(keyValue_itr->name.GetString()); } - tooltip.detailsText[keyValue_itr->name.GetString()] = detailEntry; - tooltip.detailsTextInsertOrder.push_back(keyValue_itr->name.GetString()); } } } - } - if ( tooltipType_itr->value.HasMember("size") ) - { - if ( tooltipType_itr->value["size"].HasMember("min_width") ) - { - tooltip.minWidths["default"] = tooltipType_itr->value["size"]["min_width"].GetInt(); - } - else - { - tooltip.minWidths["default"] = 0; - } - if ( tooltipType_itr->value["size"].HasMember("max_width") ) - { - tooltip.maxWidths["default"] = tooltipType_itr->value["size"]["max_width"].GetInt(); - } - else - { - tooltip.maxWidths["default"] = 0; - } - if ( tooltipType_itr->value["size"].HasMember("max_header_width") ) + if ( tooltipType_itr->value.HasMember("size") ) { - tooltip.headerMaxWidths["default"] = tooltipType_itr->value["size"]["max_header_width"].GetInt(); - } - else - { - tooltip.headerMaxWidths["default"] = 0; - } + if ( tooltipType_itr->value["size"].HasMember("min_width") ) + { + tooltip.minWidths["default"] = tooltipType_itr->value["size"]["min_width"].GetInt(); + } + else + { + tooltip.minWidths["default"] = 0; + } + if ( tooltipType_itr->value["size"].HasMember("max_width") ) + { + tooltip.maxWidths["default"] = tooltipType_itr->value["size"]["max_width"].GetInt(); + } + else + { + tooltip.maxWidths["default"] = 0; + } + if ( tooltipType_itr->value["size"].HasMember("max_header_width") ) + { + tooltip.headerMaxWidths["default"] = tooltipType_itr->value["size"]["max_header_width"].GetInt(); + } + else + { + tooltip.headerMaxWidths["default"] = 0; + } - if ( tooltipType_itr->value["size"].HasMember("item_overrides") ) - { - for ( auto itemOverride_itr = tooltipType_itr->value["size"]["item_overrides"].MemberBegin(); - itemOverride_itr != tooltipType_itr->value["size"]["item_overrides"].MemberEnd(); ++itemOverride_itr ) + if ( tooltipType_itr->value["size"].HasMember("item_overrides") ) { - if ( itemOverride_itr->value.HasMember("min_width") ) - { - tooltip.minWidths[itemOverride_itr->name.GetString()] = itemOverride_itr->value["min_width"].GetInt(); - } - if ( itemOverride_itr->value.HasMember("max_width") ) - { - tooltip.maxWidths[itemOverride_itr->name.GetString()] = itemOverride_itr->value["max_width"].GetInt(); - } - if ( itemOverride_itr->value.HasMember("max_header_width") ) + for ( auto itemOverride_itr = tooltipType_itr->value["size"]["item_overrides"].MemberBegin(); + itemOverride_itr != tooltipType_itr->value["size"]["item_overrides"].MemberEnd(); ++itemOverride_itr ) { - tooltip.headerMaxWidths[itemOverride_itr->name.GetString()] = itemOverride_itr->value["max_header_width"].GetInt(); + if ( itemOverride_itr->value.HasMember("min_width") ) + { + tooltip.minWidths[itemOverride_itr->name.GetString()] = itemOverride_itr->value["min_width"].GetInt(); + } + if ( itemOverride_itr->value.HasMember("max_width") ) + { + tooltip.maxWidths[itemOverride_itr->name.GetString()] = itemOverride_itr->value["max_width"].GetInt(); + } + if ( itemOverride_itr->value.HasMember("max_header_width") ) + { + tooltip.headerMaxWidths[itemOverride_itr->name.GetString()] = itemOverride_itr->value["max_header_width"].GetInt(); + } } } } - } - tooltips[tooltipType_itr->name.GetString()] = tooltip; + tooltips[tooltipType_itr->name.GetString()] = tooltip; + } } printlog("[JSON]: Successfully read %d item tooltips from '%s'", tooltips.size(), inputPath.c_str()); diff --git a/src/mod_tools.hpp b/src/mod_tools.hpp index c0d464c10..5313090f3 100644 --- a/src/mod_tools.hpp +++ b/src/mod_tools.hpp @@ -2715,8 +2715,8 @@ class ItemTooltips_t void setColorFaintText(Uint32 color) { faintTextColor = color; } }; void readItemsFromFile(); - void readItemLocalizationsFromFile(); - void readTooltipsFromFile(); + void readItemLocalizationsFromFile(bool forceLoadBaseDirectory = false); + void readTooltipsFromFile(bool forceLoadBaseDirectory = false); std::vector tmpItems; std::map spellItems; std::map tooltips; From 62dbd7e8da0343ef84d06f784f79a89755e687cc Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sun, 8 Oct 2023 12:17:09 +1100 Subject: [PATCH 111/146] * move language file loading after PHYSFS to read alternate startup paths for holiday themes --- src/game.cpp | 11 ----------- src/init.cpp | 22 ++++++++++++++++++---- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index ed43616ab..0cb5a1f4d 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -6569,17 +6569,6 @@ int main(int argc, char** argv) skipintro = false; } - // load default language file (english) - if ( Language::loadLanguage("en", true) ) - { - printlog("Fatal error: failed to load default language file!\n"); - if (logfile) - { - fclose(logfile); - } - exit(1); - } - // initialize map map.tiles = nullptr; map.entities = (list_t*) malloc(sizeof(list_t)); diff --git a/src/init.cpp b/src/init.cpp index decaacf06..de160f35a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -221,6 +221,18 @@ int initApp(char const * const title, int fullscreen) return 13; } + // load default language file (english) + Language::languageCode = "en"; + if ( Language::reloadLanguage() ) + { + printlog("Fatal error: failed to load default language file!\n"); + if ( logfile ) + { + fclose(logfile); + } + exit(1); + } + // initialize SDL window_title = title; printlog("initializing SDL...\n"); @@ -904,8 +916,8 @@ int Language::loadLanguage(char const * const lang, bool forceLoadBaseDirectory) TTF_SetFontHinting(ttf16, TTF_HINTING_MONO); // open language file - File* fp; - if ( (fp = openDataFile(langFilepath.c_str(), "rb")) == NULL ) + File* fp = FileIO::open(langFilepath.c_str(), "rb"); + if ( !fp ) { printlog("error: unable to load language file: '%s'", langFilepath.c_str()); return 1; @@ -915,7 +927,10 @@ int Language::loadLanguage(char const * const lang, bool forceLoadBaseDirectory) languageCode = lang; tmpEntries.clear(); - entries.clear(); + if ( forceLoadBaseDirectory ) + { + entries.clear(); + } // read file Uint32 line; @@ -992,7 +1007,6 @@ int Language::loadLanguage(char const * const lang, bool forceLoadBaseDirectory) FileIO::close(fp); printlog( "successfully loaded language file '%s'\n", langFilepath.c_str()); - initMenuOptions(); return 0; } From c21616673a43fffcb2dea5f0826033c1437da019 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sun, 8 Oct 2023 12:17:23 +1100 Subject: [PATCH 112/146] * lights.json can be read partially from modded sources --- src/light.cpp | 47 +++++++++++++++++++++++++++++++++++------------ src/light.hpp | 2 +- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/light.cpp b/src/light.cpp index ce135bb21..6f25f47a0 100644 --- a/src/light.cpp +++ b/src/light.cpp @@ -179,20 +179,43 @@ light_t* lightSphere(int index, Sint32 x, Sint32 y, Sint32 radius, float r, floa #include "files.hpp" std::unordered_map lightDefs; -bool loadLights() { - lightDefs.clear(); - - File* fp = nullptr; - const char* path = "/data/lights.json"; - if (PHYSFS_getRealDir(path)) { - std::string fullpath = PHYSFS_getRealDir(path); - fullpath.append(path); - fp = FileIO::open(fullpath.c_str(), "rb"); +bool loadLights(bool forceLoadBaseDirectory) { + if ( !PHYSFS_getRealDir("/data/lights.json") ) + { + printlog("[JSON]: Error: Could not find file: data/lights.json"); + return false; + } + + std::string inputPath = PHYSFS_getRealDir("/data/lights.json"); + if ( forceLoadBaseDirectory ) + { + inputPath = BASE_DATA_DIR; } - if (!fp) { - printlog("[JSON]: Error: Could not locate json file %s", path); + else + { + if ( inputPath != BASE_DATA_DIR ) + { + loadLights(true); // force load the base directory first, then modded paths later. + } + else + { + forceLoadBaseDirectory = true; + } + } + + inputPath.append("/data/lights.json"); + + File* fp = FileIO::open(inputPath.c_str(), "rb"); + if ( !fp ) + { + printlog("[JSON]: Error: Could not open json file %s", inputPath.c_str()); return false; } + + if ( forceLoadBaseDirectory ) + { + lightDefs.clear(); + } char buf[65536]; int count = (int)fp->read(buf, sizeof(buf[0]), sizeof(buf)); @@ -214,7 +237,7 @@ bool loadLights() { const auto& b = it.value["b"]; def.b = b.GetFloat(); const auto& exp = it.value["falloff_exp"]; def.falloff_exp = exp.GetFloat(); const auto& shadows = it.value["shadows"]; def.shadows = shadows.GetBool(); - lightDefs.emplace(name, def); + lightDefs[name] = def; } } diff --git a/src/light.hpp b/src/light.hpp index fb52f2551..0003b1be7 100644 --- a/src/light.hpp +++ b/src/light.hpp @@ -25,7 +25,7 @@ light_t* lightSphereShadow(int index, Sint32 x, Sint32 y, Sint32 radius, float r light_t* lightSphere(int index, Sint32 x, Sint32 y, Sint32 radius, float r, float g, float b, float exp); light_t* newLight(int index, Sint32 x, Sint32 y, Sint32 radius); light_t* addLight(Sint32 x, Sint32 y, const char* name, int range_bonus = 0, int index = 0); -bool loadLights(); +bool loadLights(bool forceLoadBaseDirectory = false); struct LightDef { int radius = 0; From 5c18f476b27ce8783b7842b44419ab637def3326 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sun, 8 Oct 2023 12:45:53 +1100 Subject: [PATCH 113/146] * fix wrong hud_settings json check --- src/ui/GameUI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 2f59319a1..96915008a 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -22392,7 +22392,7 @@ void loadHUDSettingsJSON() { Player::HUD_t::actionPromptOffsetX = d["action_prompts"]["x_offset"].GetInt(); } - if ( d["action_prompts"].HasMember("x_offset") ) + if ( d["action_prompts"].HasMember("x_offset_ghost_prompts") ) { Player::HUD_t::actionPromptOffsetXGhostPrompts = d["action_prompts"]["x_offset_ghost_prompts"].GetInt(); } From 5874713ae8913a290a7a5d61373256b5c8feb879 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sun, 8 Oct 2023 12:49:18 +1100 Subject: [PATCH 114/146] * fix polymodels crash when nullptr --- src/files.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/files.cpp b/src/files.cpp index 9494feea2..442718ebe 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -3257,6 +3257,12 @@ void generatePolyModels(int start, int end, bool forceCacheRebuild) quads.first = NULL; quads.last = NULL; + if ( !polymodels ) + { + polymodels = (polymodel_t*)malloc(sizeof(polymodel_t) * nummodels); + memset(polymodels, 0, sizeof(polymodel_t) * nummodels); + } + for ( c = start; c < end; ++c ) { updateLoadingScreen(30 + ((real_t)(c - start) / (end - start)) * 30.0); From 452fc80e2bbdca0b692826e4d49a13b0cd217c96 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sun, 8 Oct 2023 12:50:16 +1100 Subject: [PATCH 115/146] * fix item tooltips reloading modded paths --- src/mod_tools.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mod_tools.cpp b/src/mod_tools.cpp index e7d268fe0..609412d5b 100644 --- a/src/mod_tools.cpp +++ b/src/mod_tools.cpp @@ -1251,10 +1251,15 @@ void ItemTooltips_t::readTooltipsFromFile(bool forceLoadBaseDirectory) } else { + std::string template_name = template_itr->name.GetString(); + if ( templates.find(template_name) != templates.end() ) + { + templates[template_name].clear(); + } for ( auto lines = template_itr->value.Begin(); lines != template_itr->value.End(); ++lines ) { - templates[template_itr->name.GetString()].push_back(lines->GetString()); + templates[template_name].push_back(lines->GetString()); } } } From 5f3d51d1de389b752ca6b33f16f03eb50b411109 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Mon, 9 Oct 2023 08:02:35 +1100 Subject: [PATCH 116/146] * fix exorcise player demon message --- src/magic/magic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/magic/magic.cpp b/src/magic/magic.cpp index c668b1f03..f677859da 100644 --- a/src/magic/magic.cpp +++ b/src/magic/magic.cpp @@ -2731,7 +2731,7 @@ bool spellEffectDemonIllusion(Entity& my, spellElement_t& element, Entity* paren if ( hitstats->monsterDemonHasBeenExorcised == 3 ) { Uint32 color = makeColorRGB(0, 255, 0); - messagePlayerColor(player, MESSAGE_COMBAT, color, Language::get(3468)); + messagePlayerColor(player, MESSAGE_COMBAT, color, Language::get(3737)); } } } From 1cf9821e4fcc466bb306b0052f63552c4e82363a Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Mon, 9 Oct 2023 08:02:57 +1100 Subject: [PATCH 117/146] * change hiscore race lang entry number --- src/ui/MainMenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index a650e3653..73d36c4f6 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -7459,7 +7459,7 @@ namespace MainMenu { assert(character_title); snprintf(buf, sizeof(buf), Language::get(5298), score->stats->LVL, - Language::get(3821 + score->stats->playerRace), + Language::get(5369 + score->stats->playerRace), playerClassLangEntry(score->classnum, 0)); character_title->setText(buf); From f183650888b91579d7f59ed5e407685929daccd9 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Mon, 9 Oct 2023 08:03:30 +1100 Subject: [PATCH 118/146] * remove class description/lobby browser old code --- src/entity.cpp | 27 ---- src/entity.hpp | 1 - src/lobbies.cpp | 326 ------------------------------------------------ src/lobbies.hpp | 1 - 4 files changed, 355 deletions(-) diff --git a/src/entity.cpp b/src/entity.cpp index b6e604a34..b3ace0f62 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -17847,33 +17847,6 @@ char const * playerClassLangEntry(int classnum, int playernum) /*------------------------------------------------------------------------------- -playerClassDescription -get text string for the description of player chosen classes. - --------------------------------------------------------------------------------*/ - -char const * playerClassDescription(int classnum, int playernum) -{ - if ( classnum >= CLASS_BARBARIAN && classnum <= CLASS_JOKER ) - { - return Language::get(10 + classnum); - } - else if ( classnum >= CLASS_CONJURER ) - { - return Language::get(3231 + classnum - CLASS_CONJURER); - } - else if ( classnum >= CLASS_SEXTON && classnum <= CLASS_MONK ) - { - return Language::get(2560 + classnum - CLASS_SEXTON); - } - else - { - return "undefined description"; - } -} - -/*------------------------------------------------------------------------------- - setHelmetLimbOffset Adjusts helmet offsets for all monsters, depending on the type of headwear. diff --git a/src/entity.hpp b/src/entity.hpp index 3774488cb..d43af2fbb 100644 --- a/src/entity.hpp +++ b/src/entity.hpp @@ -1187,7 +1187,6 @@ static const int MSG_TOOL_BOMB = 6; static const int MSG_COMBAT_BASIC = 7; void messagePlayerMonsterEvent(int player, Uint32 color, Stat& monsterStats, const char* msgGeneric, const char* msgNamed, int detailType, Entity* optionalEntity = nullptr); char const * playerClassLangEntry(int classnum, int playernum); -char const * playerClassDescription(int classnum, int playernum); //Some testing functions/commands. Entity* summonChest(long x, long y); diff --git a/src/lobbies.cpp b/src/lobbies.cpp index b3a28ff92..eaf04e8a4 100644 --- a/src/lobbies.cpp +++ b/src/lobbies.cpp @@ -462,332 +462,6 @@ Sint32 LobbyHandler_t::getDisplayedResultLobbyIndex(int selection) return lobbyDisplayedSearchResults.at(selection).first; } -void LobbyHandler_t::handleLobbyBrowser() -{ - updateSearchResults(); - - // epic/steam lobby browser - if ( subwindow && !strcmp(subtext, Language::get(1334)) ) - { - // draw backdrop for main list and slider - -#ifdef USE_EOS - if ( EOS.LobbySearchResults.lastResultWasFiltered ) - { - if ( !strcmp(EOS.LobbySearchResults.lobbyLastSearchByCode, "") ) - { - ttfPrintTextFormatted(ttf12, subx1 + 8 + (strlen(subtext) + 1) * TTF12_WIDTH, suby1 + 8, "(Filtered)"); - } - else - { - ttfPrintTextFormatted(ttf12, subx1 + 8 + (strlen(subtext) + 1) * TTF12_WIDTH, suby1 + 8, "(Filtered by lobby code: %s)", EOS.LobbySearchResults.lobbyLastSearchByCode); - } - } -#ifdef STEAMWORKS - if ( !EOS.CurrentUserInfo.bUserLoggedIn ) - { - ttfPrintTextFormatted(ttf12, subx2 - 8 - (strlen(Language::get(3994)) + 1) * TTF12_WIDTH, suby2 - TTF12_HEIGHT - 10, Language::get(3994)); - } -#endif -#endif - - SDL_Rect listExtents; - listExtents.x = subx1 + 8; - listExtents.y = suby1 + 24; - listExtents.w = (subx2 - 32) - listExtents.x; - listExtents.h = (suby2 - 64) - listExtents.y; - - SDL_Rect sliderExtents; - sliderExtents.x = subx2 - 32; - sliderExtents.y = suby1 + 24; - sliderExtents.w = (subx2 - 8) - sliderExtents.x; - sliderExtents.h = (suby2 - 64) - sliderExtents.y; - - drawDepressed(listExtents.x, listExtents.y, - listExtents.x + listExtents.w, listExtents.y + listExtents.h); - drawDepressed(sliderExtents.x, sliderExtents.y, - sliderExtents.x + sliderExtents.w, sliderExtents.y + sliderExtents.h); - - int numSearchResults = numLobbyDisplaySearchResults; - selectedLobbyInList = std::max(0, std::min(selectedLobbyInList, static_cast(numLobbyDisplaySearchResults - 1))); - - int maxLobbyResults = kNumSearchResults; - - // slider - slidersize = std::min(((suby2 - 65) - (suby1 + 25)), ((suby2 - 65) - (suby1 + 25)) / ((real_t)std::max(numSearchResults + 1, 1) / 20)); - slidery = std::min(std::max(suby1 + 25, slidery), suby2 - 65 - slidersize); - drawWindowFancy(subx2 - 31, slidery, subx2 - 9, slidery + slidersize); - - // directory list offset from slider - Sint32 y2 = ((real_t)(slidery - suby1 - 20) / ((suby2 - 52) - (suby1 + 20))) * (numSearchResults + 1); - Sint32 old_y2 = y2; - if ( inputs.bMouseLeft(clientnum) - && (omousex >= sliderExtents.x && omousex < sliderExtents.x + sliderExtents.w) - && (omousey >= sliderExtents.y && omousey < sliderExtents.y + sliderExtents.h) ) - { - slidery = oslidery + mousey - omousey; - } - else if ( mousestatus[SDL_BUTTON_WHEELUP] || mousestatus[SDL_BUTTON_WHEELDOWN] ) - { - slidery += 16 * mousestatus[SDL_BUTTON_WHEELDOWN] - 16 * mousestatus[SDL_BUTTON_WHEELUP]; - mousestatus[SDL_BUTTON_WHEELUP] = 0; - mousestatus[SDL_BUTTON_WHEELDOWN] = 0; - } - else - { - oslidery = slidery; - } - slidery = std::min(std::max(suby1 + 25, slidery), suby2 - 65 - slidersize); - y2 = ((real_t)(slidery - suby1 - 20) / ((suby2 - 52) - (suby1 + 20))) * (numSearchResults + 1); - - if ( old_y2 != y2 ) - { - selectedLobbyInList = std::min(std::max(y2, selectedLobbyInList), std::min(std::max(numSearchResults - 1, 0), y2 + 17)); - } - - // server flags tooltip variables - SDL_Rect flagsBox; - char flagsBoxText[256] = ""; - int hoveringSelection = -1; - Uint32 lobbySvFlags = 0; - int numSvFlags = 0; - int serverNumModsLoaded = 0; - - // select/inspect lobbies - if ( (omousex >= listExtents.x && omousex < listExtents.x + listExtents.w) - && (omousey >= listExtents.y + 2 && omousey < listExtents.y + listExtents.h - 4) ) - { - //Something is flawed somewhere in here, because commit 1bad2c5d9f67e0a503ca79f93b03101fbcc7c7ba had to fix the game using an inappropriate hoveringSelection. - //Perhaps it's as simple as setting hoveringSelection back to -1 if lobbyIDs[hoveringSelection] is in-fact null. - hoveringSelection = std::min(std::max(0, y2 + ((omousey - suby1 - 26) >> 4)), maxLobbyResults); - - LobbyServiceType lobbyType = getDisplayedResultLobbyType(hoveringSelection); - Sint32 lobbyIndex = getDisplayedResultLobbyIndex(hoveringSelection); - // lobby info tooltip - if ( lobbyType == LOBBY_STEAM ) - { -#ifdef STEAMWORKS - // lobby info tooltip - if ( lobbyIndex >= 0 && lobbyIDs[lobbyIndex] ) - { - const char* lobbySvFlagsChar = SteamMatchmaking()->GetLobbyData(*static_cast(lobbyIDs[lobbyIndex]), "svFlags"); - lobbySvFlags = atoi(lobbySvFlagsChar); - const char* serverNumModsChar = SteamMatchmaking()->GetLobbyData(*static_cast(lobbyIDs[lobbyIndex]), "svNumMods"); - serverNumModsLoaded = atoi(serverNumModsChar); - } -#endif - } - else if ( lobbyType == LOBBY_CROSSPLAY ) - { -#ifdef USE_EOS - // lobby info tooltip - if ( lobbyIndex >= 0 ) - { - lobbySvFlags = EOS.LobbySearchResults.getResultFromDisplayedIndex(lobbyIndex)->LobbyAttributes.serverFlags; - serverNumModsLoaded = EOS.LobbySearchResults.getResultFromDisplayedIndex(lobbyIndex)->LobbyAttributes.numServerMods; - } -#endif // USE_EOS - } - - for ( int c = 0; c < NUM_SERVER_FLAGS; ++c ) - { - if ( lobbySvFlags & power(2, c) ) - { - ++numSvFlags; - } - } - - flagsBox.w = strlen(Language::get(2919)) * 10 + 4; - flagsBox.h = 4 + (getHeightOfFont(ttf12) * (std::max(2, numSvFlags + 2))); - flagsBox.x = mousex + 8; - flagsBox.y = mousey + 8; - if ( serverNumModsLoaded > 0 ) - { - flagsBox.h += TTF12_HEIGHT; - flagsBox.w += 16; - } - strcpy(flagsBoxText, Language::get(1335)); - strcat(flagsBoxText, "\n"); - - if ( !numSvFlags ) - { - strcat(flagsBoxText, Language::get(1336)); - } - else - { - int y = 2; - for ( int c = 0; c < NUM_SERVER_FLAGS; c++ ) - { - if ( lobbySvFlags & power(2, c) ) - { - y += getHeightOfFont(ttf12); - strcat(flagsBoxText, "\n"); - char flagStringBuffer[256] = ""; - if ( c < 5 ) - { - strcpy(flagStringBuffer, Language::get(153 + c)); - } - else - { - strcpy(flagStringBuffer, Language::get(2917 - 5 + c)); - } - strcat(flagsBoxText, flagStringBuffer); - } - } - } - if ( serverNumModsLoaded > 0 ) - { - strcat(flagsBoxText, "\n"); - char numModsBuffer[32]; - snprintf(numModsBuffer, 32, "%2d mod(s) loaded", serverNumModsLoaded); - strcat(flagsBoxText, numModsBuffer); - } - - // selecting lobby - if ( inputs.bMouseLeft(clientnum) ) - { - inputs.mouseClearLeft(clientnum); - if ( getDisplayedResultLobbyType(hoveringSelection) == LOBBY_DISABLE && hoveringSelection > 0 && numSearchResults >= 1 ) - { - this->selectedLobbyInList = numSearchResults - 1; - } - else - { - this->selectedLobbyInList = hoveringSelection; - } - } - } - - // draw lobby list and selected window - if ( this->selectedLobbyInList >= 0 ) - { - LobbyServiceType lobbyType = getDisplayedResultLobbyType(this->selectedLobbyInList); - if ( lobbyType == LOBBY_STEAM ) - { -#ifdef STEAMWORKS - selectedSteamLobby = std::max(0, getDisplayedResultLobbyIndex(this->selectedLobbyInList)); - selectedSteamLobby = std::min(std::max(y2, selectedSteamLobby), std::min(std::max(numSearchResults - 1, 0), y2 + 17)); - //this->selectedLobbyInList = selectedSteamLobby; -#endif - } - else if ( lobbyType == LOBBY_CROSSPLAY ) - { -#ifdef USE_EOS - EOS.LobbySearchResults.selectedLobby = std::max(0, getDisplayedResultLobbyIndex(this->selectedLobbyInList)); - EOS.LobbySearchResults.selectedLobby = std::min(std::max(y2, EOS.LobbySearchResults.selectedLobby), std::min(std::max(static_cast(numSearchResults) - 1, 0), y2 + 17)); - //this->selectedLobbyInList = EOS.LobbySearchResults.selectedLobby; -#endif // USE_EOS - } - - // selected window - SDL_Rect pos; - pos.x = subx1 + 10; - pos.y = suby1 + 26 + (this->selectedLobbyInList - y2) * 16; - if ( lobbyType == LOBBY_DISABLE ) - { - pos.y = suby1 + 26 + (0 - y2) * 16; // don't flicker the rectangle on invalid selections. - } - pos.w = subx2 - subx1 - 44; - pos.h = 16; - drawRect(&pos, makeColorRGB(64, 64, 64), 255); - } - - // print all lobby entries - Sint32 x = subx1 + 10; - Sint32 y = suby1 + 28; - if ( numLobbyDisplaySearchResults > 0 ) - { - int searchResultLowestVisibleEntry = std::min(numLobbyDisplaySearchResults, static_cast(18 + y2)) + 1; - for ( Sint32 z = y2; z < searchResultLowestVisibleEntry; ++z ) - { - LobbyServiceType lobbyType = getDisplayedResultLobbyType(z); - Sint32 lobbyIndex = getDisplayedResultLobbyIndex(z); - if ( lobbyType == LOBBY_STEAM ) - { -#ifdef STEAMWORKS - ttfPrintTextFormatted(ttf12, x, y, lobbyText[lobbyIndex]); // name - ttfPrintTextFormatted(ttf12, subx2 - 72, y, "%d/%d", lobbyPlayers[lobbyIndex], MAXPLAYERS); // player count -#endif // STEAMWORKS - } - else if ( lobbyType == LOBBY_CROSSPLAY ) - { -#ifdef USE_EOS - // set the lobby data - const Uint32 lobbyNameSize = EOS.LobbySearchResults.getResultFromDisplayedIndex(lobbyIndex)->LobbyAttributes.lobbyName.size(); - const Uint32 maxCharacters = 54; - std::string lobbyDetailText = " "; - lobbyDetailText += "("; - lobbyDetailText += EOS.LobbySearchResults.getResultFromDisplayedIndex(lobbyIndex)->LobbyAttributes.gameVersion; - lobbyDetailText += ") "; -#ifdef STEAMWORKS - lobbyDetailText += "[CROSSPLAY]"; -#endif - /*if ( numMods > 0 ) - { - lobbyDetailText += "[MODDED]"; - }*/ - std::string displayedLobbyName = EOS.LobbySearchResults.getResultFromDisplayedIndex(lobbyIndex)->LobbyAttributes.lobbyName; - if ( displayedLobbyName.size() > (maxCharacters - lobbyDetailText.size()) ) - { - // no room, need to truncate lobbyName - displayedLobbyName = displayedLobbyName.substr(0, (maxCharacters - lobbyDetailText.size()) - 2); - displayedLobbyName += ".."; - } - - Uint32 color = uint32ColorWhite; - char buf[maxCharacters] = ""; - if ( EOS.LobbySearchResults.getResultFromDisplayedIndex(lobbyIndex)->LobbyAttributes.gameCurrentLevel >= 0 ) - { - color = uint32ColorYellow; - // hide lobby name for in progress. - snprintf(buf, maxCharacters - 1, "%s%s", "In-progress lobby", lobbyDetailText.c_str()); - } - else - { - snprintf(buf, maxCharacters - 1, "%s%s", displayedLobbyName.c_str(), lobbyDetailText.c_str()); - } - ttfPrintTextFormattedColor(ttf12, x, y, color, buf); // name - ttfPrintTextFormattedColor(ttf12, subx2 - 72, y, color, "%d/%d", - EOS.LobbySearchResults.getResultFromDisplayedIndex(lobbyIndex)->playersInLobby.size(), - EOS.LobbySearchResults.getResultFromDisplayedIndex(lobbyIndex)->MaxPlayers); // player count -#endif - } - y += 16; - } - } - else - { - ttfPrintText(ttf12, x, y, Language::get(1337)); - } - - // draw server flags tooltip (if applicable) - if ( hoveringSelection >= 0 && numLobbyDisplaySearchResults > 0 && (hoveringSelection < static_cast(numLobbyDisplaySearchResults)) ) - { - drawTooltip(&flagsBox); - ttfPrintTextFormatted(ttf12, flagsBox.x + 2, flagsBox.y + 4, flagsBoxText); - } - -#ifdef USE_EOS - if ( !SDL_IsTextInputActive() && showLobbyFilters && (searchType == LOBBY_CROSSPLAY || searchType == LOBBY_COMBINED) ) - { - inputstr = EOS.lobbySearchByCode; - SDL_StartTextInput(); - inputlen = 4; - } -#endif - } - else - { -#ifdef USE_EOS - if ( inputstr == EOS.lobbySearchByCode ) - { - SDL_StopTextInput(); - inputstr = nullptr; - } - showLobbyFilters = false; -#endif - } -} #ifdef STEAMWORKS void LobbyHandler_t::steamValidateAndJoinLobby(CSteamID& id) { diff --git a/src/lobbies.hpp b/src/lobbies.hpp index 2fcde3ff5..6b3a7e120 100644 --- a/src/lobbies.hpp +++ b/src/lobbies.hpp @@ -51,7 +51,6 @@ class LobbyHandler_t LobbyServiceType searchType = LOBBY_DISABLE; LobbyServiceType P2PType = LOBBY_DISABLE; void handleLobbyListRequests(); - void handleLobbyBrowser(); void updateSearchResults(); static void filterLobbyButton(button_t* my); static void searchLobbyWithFilter(button_t* my); From 1fb83ec20cb1d0644df52b0f649dd96c138d1508 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Mon, 9 Oct 2023 08:05:02 +1100 Subject: [PATCH 119/146] * little menu.cpp clean up of old stuff --- src/menu.cpp | 1025 -------------------------------------------------- src/menu.hpp | 1 - 2 files changed, 1026 deletions(-) diff --git a/src/menu.cpp b/src/menu.cpp index 3e262da26..16cc8ecc6 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -358,53 +358,6 @@ void changeSettingsTab(int option) } } -void inline printJoybindingNames(const SDL_Rect& currentPos, int c, bool &rebindingaction) -{ - Sint32 omousex = inputs.getMouse(clientnum, Inputs::MouseInputs::OX); - Sint32 omousey = inputs.getMouse(clientnum, Inputs::MouseInputs::OY); - ttfPrintText(ttf8, currentPos.x, currentPos.y, Language::get(1948 + c)); - if ( inputs.bMouseLeft(clientnum) && !rebindingaction ) - { - if ( omousex >= currentPos.x && omousex < subx2 - 24 ) - { - if ( omousey >= currentPos.y && omousey < currentPos.y + 12 ) - { - inputs.mouseClearLeft(clientnum); - if ( settings_joyimpulses[c] != UNBOUND_JOYBINDING ) - { - settings_joyimpulses[c] = UNBOUND_JOYBINDING; //Unbind the joybinding if clicked on. - } - else - { - lastkeypressed = 0; - rebindingaction = true; - rebindaction = c; - } - } - } - } - - if ( c != rebindaction ) - { - if ( !strcmp(getInputName(settings_joyimpulses[c]), "Unassigned key" )) - { - ttfPrintTextColor(ttf8, currentPos.x + 232, currentPos.y, uint32ColorBaronyBlue, true, getInputName(settings_joyimpulses[c])); - } - else if ( !strcmp(getInputName(settings_joyimpulses[c]), "Unknown key") || !strcmp(getInputName(settings_joyimpulses[c]), "Unknown trigger") ) - { - ttfPrintTextColor(ttf8, currentPos.x + 232, currentPos.y, uint32ColorRed, true, getInputName(settings_joyimpulses[c])); - } - else - { - ttfPrintText(ttf8, currentPos.x + 232, currentPos.y, getInputName(settings_joyimpulses[c])); - } - } - else - { - ttfPrintTextColor(ttf8, currentPos.x + 232, currentPos.y, uint32ColorGreen, true, "..."); - } -} - bool isAchievementUnlockedForClassUnlock(PlayerRaces race) { #ifdef STEAMWORKS @@ -608,764 +561,6 @@ int isCharacterValidFromDLC(Stat& myStats, int characterClass) return INVALID_CHARACTER; } -void inline pauseMenuOnInputPressed() -{ - inputs.mouseClearLeft(clientnum); - keystatus[SDLK_RETURN] = 0; - playSound(139, 64); - if ( rebindaction == -1 ) - { - inputs.controllerClearInput(clientnum, INJOY_MENU_NEXT); - } -} - -void handleInGamePauseMenu() -{ - Uint32 colorGray = uint32ColorGray; - - Sint32 mousex = inputs.getMouse(clientnum, Inputs::MouseInputs::X); - Sint32 mousey = inputs.getMouse(clientnum, Inputs::MouseInputs::Y); - Sint32 omousex = inputs.getMouse(clientnum, Inputs::MouseInputs::OX); - Sint32 omousey = inputs.getMouse(clientnum, Inputs::MouseInputs::OY); - const bool inputIsPressed = (inputs.bMouseLeft(clientnum) || keystatus[SDLK_RETURN] || (inputs.bControllerInputPressed(clientnum, INJOY_MENU_NEXT) && rebindaction == -1)); - SDL_Rect text; - text.x = 50; - text.h = 18; - text.w = 18; - - int numOption = 1; - - text.y = yres / 4 + 80; - if ( ((omousex >= text.x && omousex < text.x + strlen(Language::get(1309)) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 ) - { - // resume game - menuselect = 1; - ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, Language::get(1309)); - if ( inputIsPressed ) - { - pauseMenuOnInputPressed(); - pauseGame(1, MAXPLAYERS); - } - } - else - { - ttfPrintText(ttf16, text.x, text.y, Language::get(1309)); - } - - bool achievementsMenu = false; -#if !defined STEAMWORKS -#ifdef USE_EOS - achievementsMenu = true; -#endif -#endif - text.y += 24; - ++numOption; - if ( achievementsMenu ) - { - if ( ((omousex >= text.x && omousex < text.x + strlen(Language::get(3971)) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 ) - { - // settings menu - menuselect = numOption; - ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, Language::get(3971)); - if ( inputIsPressed ) - { - pauseMenuOnInputPressed(); - openAchievementsWindow(); - } - } - else - { - ttfPrintText(ttf16, text.x, text.y, Language::get(3971)); - } - text.y += 24; - ++numOption; - } - - if ( ((omousex >= text.x && omousex < text.x + strlen(Language::get(1306)) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 ) - { - // settings menu - menuselect = numOption; - ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, Language::get(1306)); - if ( inputIsPressed ) - { - pauseMenuOnInputPressed(); - openSettingsWindow(); - } - } - else - { - ttfPrintText(ttf16, text.x, text.y, Language::get(1306)); - } - const char* endgameText = NULL; - const char* quitgameText = Language::get(1313); - bool singleplayerAliveEndGameAndSave = false; - bool singleplayerBossLevelDisableSaveOnExit = false; - bool multiplayerAliveEndGameAndSave = false; - if ( multiplayer == SINGLE ) - { - if ( stats[clientnum] && stats[clientnum]->HP > 0 ) - { - endgameText = Language::get(3919); - singleplayerAliveEndGameAndSave = true; - if ( !strncmp(map.name, "Boss", 4) - || !strncmp(map.name, "Hell Boss", 9) - || !strncmp(map.name, "Sanctum", 7) ) - { - // boss floor, no save scumming easily! - singleplayerAliveEndGameAndSave = false; - singleplayerBossLevelDisableSaveOnExit = true; - endgameText = Language::get(1310); - quitgameText = Language::get(3987); - } - } - else - { - endgameText = Language::get(1310); - quitgameText = Language::get(3987); - singleplayerAliveEndGameAndSave = false; - } - } - else if ( multiplayer == SERVER ) - { - endgameText = Language::get(1310); - quitgameText = Language::get(3987); - for ( int i = 0; i < MAXPLAYERS; ++i ) - { - if ( !client_disconnected[i] && stats[i] && stats[i]->HP > 0 ) - { - multiplayerAliveEndGameAndSave = true; - quitgameText = Language::get(1313); - endgameText = Language::get(3019); - break; - } - } - } - else - { - endgameText = Language::get(3019); - } - - text.y += 24; - ++numOption; - if ( ((omousex >= text.x && omousex < text.x + strlen(endgameText) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 ) - { - // end game / return to main menu - menuselect = numOption; - ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, endgameText); - if ( inputIsPressed ) - { - pauseMenuOnInputPressed(); - - // create confirmation window - subwindow = 1; - if ( multiplayer == SINGLE ) - { - subx1 = xres / 2 - 144; - subx2 = xres / 2 + 144; - suby1 = yres / 2 - 64; - suby2 = yres / 2 + 64; - if ( singleplayerAliveEndGameAndSave ) - { - strcpy(subtext, Language::get(3920)); - subx1 = xres / 2 - 188; - subx2 = xres / 2 + 188; - suby1 = yres / 2 - 64; - suby2 = yres / 2 + 64; - - // add a cancel button - button_t* button = newButton(); - strcpy(button->label, Language::get(1316)); - button->x = subx2 - strlen(Language::get(1316)) * 12 - 16; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1316)) * 12 + 8; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - } - else - { - subx1 = xres / 2 - 188; - subx2 = xres / 2 + 188; - if ( singleplayerBossLevelDisableSaveOnExit ) - { - strcpy(subtext, Language::get(3990)); - } - else - { - strcpy(subtext, Language::get(1129)); - } - } - } - else - { - subx1 = xres / 2 - 224; - subx2 = xres / 2 + 224; - if ( multiplayer == SERVER ) - { - if ( multiplayerAliveEndGameAndSave ) - { - suby1 = yres / 2 - 100; - suby2 = yres / 2 + 100; - strcpy(subtext, Language::get(3021)); - } - else - { - suby1 = yres / 2 - 90; - suby2 = yres / 2 + 90; - strcpy(subtext, Language::get(3986)); - } - } - else if ( multiplayer == CLIENT ) - { - suby1 = yres / 2 - 112; - suby2 = yres / 2 + 112; - strcpy(subtext, Language::get(3020)); - } - } - - // close button - button_t* button = newButton(); - strcpy(button->label, "x"); - button->x = subx2 - 20; - button->y = suby1; - button->sizex = 20; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - button->key = SDLK_ESCAPE; - button->joykey = joyimpulses[INJOY_MENU_CANCEL]; - - // yes button - button = newButton(); - strcpy(button->label, Language::get(1314)); - button->x = subx1 + 8; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1314)) * 12 + 8; - button->sizey = 20; - if ( multiplayer == SINGLE ) - { - if ( singleplayerAliveEndGameAndSave ) - { - button->action = &buttonCloseAndEndGameConfirm; - } - else - { - button->action = &buttonEndGameConfirm; - } - } - else - { - button->action = &buttonCloseAndEndGameConfirm; - } - button->visible = 1; - button->focused = 1; - button->key = SDLK_RETURN; - button->joykey = joyimpulses[INJOY_MENU_NEXT]; - - if ( multiplayer == SINGLE && singleplayerAliveEndGameAndSave ) - { - // noop - button created earlier. - } - else - { - // cancel button - button = newButton(); - strcpy(button->label, Language::get(1316)); - button->x = subx2 - strlen(Language::get(1316)) * 12 - 16; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1316)) * 12 + 8; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - } - } - } - else - { - ttfPrintText(ttf16, text.x, text.y, endgameText); - } - - if ( multiplayer != CLIENT ) - { - text.y += 24; - ++numOption; - if ( ((omousex >= text.x && omousex < text.x + strlen(Language::get(1312)) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 ) - { - //restart game - menuselect = numOption; - ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, Language::get(1312)); - if ( inputIsPressed ) - { - pauseMenuOnInputPressed(); - - // create confirmation window - subwindow = 1; - subx1 = xres / 2 - 164; - subx2 = xres / 2 + 164; - suby1 = yres / 2 - 48; - suby2 = yres / 2 + 48; - strcpy(subtext, Language::get(1130)); - - // close button - button_t* button = newButton(); - strcpy(button->label, "x"); - button->x = subx2 - 20; - button->y = suby1; - button->sizex = 20; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - button->key = SDLK_ESCAPE; - button->joykey = joyimpulses[INJOY_MENU_CANCEL]; - - // yes button - button = newButton(); - strcpy(button->label, Language::get(1314)); - button->x = subx1 + 8; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1314)) * 12 + 8; - button->sizey = 20; - if ( multiplayer == SINGLE ) - { - button->action = &buttonStartSingleplayer; - } - else - { - button->action = &buttonStartServer; - } - button->visible = 1; - button->focused = 1; - button->key = SDLK_RETURN; - button->joykey = joyimpulses[INJOY_MENU_NEXT]; - - // no button - button = newButton(); - strcpy(button->label, Language::get(1315)); - button->x = subx2 - strlen(Language::get(1315)) * 12 - 16; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1315)) * 12 + 8; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - } - } - else - { - ttfPrintText(ttf16, text.x, text.y, Language::get(1312)); - } - } - - text.y += 24; - ++numOption; - if ( ((omousex >= text.x && omousex < text.x + strlen(quitgameText) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 ) - { - menuselect = numOption; - // save & quit - ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, quitgameText); - if ( inputIsPressed ) - { - pauseMenuOnInputPressed(); - - // create confirmation window - subwindow = 1; - subx1 = xres / 2 - 188; - subx2 = xres / 2 + 188; - suby1 = yres / 2 - 64; - suby2 = yres / 2 + 64; - strcpy(subtext, Language::get(1131)); - if ( multiplayer == SINGLE ) - { - if ( !singleplayerAliveEndGameAndSave ) - { - if ( singleplayerBossLevelDisableSaveOnExit ) - { - strcpy(subtext, Language::get(3991)); - } - else - { - strcpy(subtext, Language::get(3988)); - } - } - } - else if ( multiplayer == SERVER ) - { - if ( !multiplayerAliveEndGameAndSave ) - { - subx1 = xres / 2 - 224; - subx2 = xres / 2 + 224; - suby1 = yres / 2 - 64; - suby2 = yres / 2 + 64; - strcpy(subtext, Language::get(3989)); - } - } - - // yes button - button_t* button = newButton(); - strcpy(button->label, Language::get(1314)); - button->x = subx1 + 8; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1314)) * 12 + 8; - button->sizey = 20; - button->action = &buttonQuitConfirm; - button->visible = 1; - button->focused = 1; - button->key = SDLK_RETURN; - button->joykey = joyimpulses[INJOY_MENU_NEXT]; //TODO: Select which button to activate via dpad. - - // no button - // button = newButton(); - // strcpy(button->label, Language::get(1315)); - // button->sizex = strlen(Language::get(1315)) * 12 + 8; - // button->sizey = 20; - // button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2; - // button->y = suby2 - 28; - // button->action = &buttonQuitNoSaveConfirm; - // button->visible = 1; - // button->focused = 1; - - // cancel button - button = newButton(); - strcpy(button->label, Language::get(1316)); - button->x = subx2 - strlen(Language::get(1316)) * 12 - 16; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1316)) * 12 + 8; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - - // close button - button = newButton(); - strcpy(button->label, "x"); - button->x = subx2 - 20; - button->y = suby1; - button->sizex = 20; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - button->key = SDLK_ESCAPE; - button->joykey = joyimpulses[INJOY_MENU_CANCEL]; - } - } - else - { - ttfPrintText(ttf16, text.x, text.y, quitgameText); - } -} - -void handleTutorialPauseMenu() -{ - Sint32 mousex = inputs.getMouse(clientnum, Inputs::MouseInputs::X); - Sint32 mousey = inputs.getMouse(clientnum, Inputs::MouseInputs::Y); - Sint32 omousex = inputs.getMouse(clientnum, Inputs::MouseInputs::OX); - Sint32 omousey = inputs.getMouse(clientnum, Inputs::MouseInputs::OY); - - const Uint32 colorGray = uint32ColorGray; - const bool inputIsPressed = (inputs.bMouseLeft(clientnum) || keystatus[SDLK_RETURN] || (inputs.bControllerInputPressed(clientnum, INJOY_MENU_NEXT) && rebindaction == -1)); - SDL_Rect text; - text.x = 50; - text.h = 18; - text.w = 18; - - bool mapIsTutorialHub = false; - if ( !strcmp(map.name, "Tutorial Hub") ) - { - mapIsTutorialHub = true; - } - - int numOption = 1; - bool achievementsMenu = false; -#if !defined STEAMWORKS -#ifdef USE_EOS - achievementsMenu = true; -#endif -#endif - - text.y = yres / 4 + 80; - if ( ((omousex >= text.x && omousex < text.x + strlen(Language::get(1309)) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 ) - { - // resume game - menuselect = numOption; - ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, Language::get(1309)); - if ( inputIsPressed ) - { - pauseMenuOnInputPressed(); - pauseGame(1, MAXPLAYERS); - } - } - else - { - ttfPrintText(ttf16, text.x, text.y, Language::get(1309)); - } - - //++numOption; - //text.y += 24; - //if ( ((omousex >= text.x && omousex < text.x + strlen(Language::get(1306)) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 ) - //{ - // // settings menu - // menuselect = numOption; - // ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, Language::get(1306)); - // if ( inputIsPressed ) - // { - // pauseMenuOnInputPressed(); - // gameModeManager.Tutorial.Menu.open(); - // } - //} - //else - //{ - // ttfPrintText(ttf16, text.x, text.y, Language::get(1306)); - //} - - if ( achievementsMenu ) - { - ++numOption; - text.y += 24; - if ( ((omousex >= text.x && omousex < text.x + strlen(Language::get(3971)) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 ) - { - // achievements menu - menuselect = numOption; - ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, Language::get(3971)); - if ( inputIsPressed ) - { - pauseMenuOnInputPressed(); - openAchievementsWindow(); - } - } - else - { - ttfPrintText(ttf16, text.x, text.y, Language::get(3971)); - } - } - - ++numOption; - text.y += 24; - if ( ((omousex >= text.x && omousex < text.x + strlen(Language::get(1306)) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 ) - { - // settings menu - menuselect = numOption; - ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, Language::get(1306)); - if ( inputIsPressed ) - { - pauseMenuOnInputPressed(); - openSettingsWindow(); - } - } - else - { - ttfPrintText(ttf16, text.x, text.y, Language::get(1306)); - } - - ++numOption; - text.y += 24; - const char* returnToHubOptionText = Language::get(3958); - if ( mapIsTutorialHub ) - { - returnToHubOptionText = Language::get(3969); - } - - if ( ((omousex >= text.x && omousex < text.x + strlen(returnToHubOptionText) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 ) - { - // return to hub - menuselect = numOption; - ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, returnToHubOptionText); - if ( inputIsPressed ) - { - pauseMenuOnInputPressed(); - - // create confirmation window - subwindow = 1; - subx1 = xres / 2 - 144; - subx2 = xres / 2 + 144; - suby1 = yres / 2 - 48; - suby2 = yres / 2 + 48; - - if ( mapIsTutorialHub ) - { - strcpy(subtext, Language::get(3970)); - } - else - { - strcpy(subtext, Language::get(3960)); - } - - // add a cancel button - button_t* button = newButton(); - strcpy(button->label, Language::get(1316)); - button->x = subx2 - strlen(Language::get(1316)) * 12 - 16; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1316)) * 12 + 8; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - - // close button - button = newButton(); - strcpy(button->label, "x"); - button->x = subx2 - 20; - button->y = suby1; - button->sizex = 20; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - button->key = SDLK_ESCAPE; - button->joykey = joyimpulses[INJOY_MENU_CANCEL]; - - // yes button - button = newButton(); - strcpy(button->label, Language::get(1314)); - button->x = subx1 + 8; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1314)) * 12 + 8; - button->sizey = 20; - button->action = &gameModeManager.Tutorial.buttonReturnToTutorialHub; - button->visible = 1; - button->focused = 1; - button->key = SDLK_RETURN; - button->joykey = joyimpulses[INJOY_MENU_NEXT]; - } - } - else - { - ttfPrintText(ttf16, text.x, text.y, returnToHubOptionText); - } - - if ( !mapIsTutorialHub ) - { - ++numOption; - text.y += 24; - if ( ((omousex >= text.x && omousex < text.x + strlen(Language::get(3957)) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 ) - { - //restart game - menuselect = numOption; - ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, Language::get(3957)); - if ( inputIsPressed ) - { - pauseMenuOnInputPressed(); - - // create confirmation window - subwindow = 1; - subx1 = xres / 2 - 144; - subx2 = xres / 2 + 144; - suby1 = yres / 2 - 48; - suby2 = yres / 2 + 48; - strcpy(subtext, Language::get(3956)); - - // add a cancel button - button_t* button = newButton(); - strcpy(button->label, Language::get(1316)); - button->x = subx2 - strlen(Language::get(1316)) * 12 - 16; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1316)) * 12 + 8; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - - // close button - button = newButton(); - strcpy(button->label, "x"); - button->x = subx2 - 20; - button->y = suby1; - button->sizex = 20; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - button->key = SDLK_ESCAPE; - button->joykey = joyimpulses[INJOY_MENU_CANCEL]; - - // yes button - button = newButton(); - strcpy(button->label, Language::get(1314)); - button->x = subx1 + 8; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1314)) * 12 + 8; - button->sizey = 20; - button->action = &gameModeManager.Tutorial.buttonRestartTrial; - button->visible = 1; - button->focused = 1; - button->key = SDLK_RETURN; - button->joykey = joyimpulses[INJOY_MENU_NEXT]; - } - } - else - { - ttfPrintText(ttf16, text.x, text.y, Language::get(3957)); - } - } - - text.y += 24; - ++numOption; - - if ( ((omousex >= 50 && omousex < 50 + strlen(Language::get(3959)) * 18 && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 ) - { - menuselect = numOption; - // return to main menu - ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, Language::get(3959)); - if ( inputIsPressed ) - { - pauseMenuOnInputPressed(); - - // create confirmation window - subwindow = 1; - subx1 = xres / 2 - 144; - subx2 = xres / 2 + 144; - suby1 = yres / 2 - 48; - suby2 = yres / 2 + 48; - strcpy(subtext, Language::get(3961)); - - // yes button - button_t* button = newButton(); - strcpy(button->label, Language::get(1314)); - button->x = subx1 + 8; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1314)) * 12 + 8; - button->sizey = 20; - button->action = &buttonEndGameConfirm; - button->visible = 1; - button->focused = 1; - button->key = SDLK_RETURN; - button->joykey = joyimpulses[INJOY_MENU_NEXT]; //TODO: Select which button to activate via dpad. - - // cancel button - button = newButton(); - strcpy(button->label, Language::get(1316)); - button->x = subx2 - strlen(Language::get(1316)) * 12 - 16; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1316)) * 12 + 8; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - - // close button - button = newButton(); - strcpy(button->label, "x"); - button->x = subx2 - 20; - button->y = suby1; - button->sizex = 20; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - button->key = SDLK_ESCAPE; - button->joykey = joyimpulses[INJOY_MENU_CANCEL]; - } - } - else - { - ttfPrintText(ttf16, text.x, text.y, Language::get(3959)); - } -} - /*------------------------------------------------------------------------------- handleMainMenu @@ -2185,7 +1380,6 @@ static void handleMainMenu(bool mode) // process button actions handleButtons(); - LobbyHandler.handleLobbyBrowser(); } // character creation screen @@ -11812,84 +11006,6 @@ void buttonConfirmDeleteMultiplayerFile(button_t* my) playSound(153, 96); } - -void buttonDeleteScoreCancel(button_t* my) -{ - // close current window - buttonCloseSubwindow(nullptr); - list_FreeAll(&button_l); - deleteallbuttons = true; - - buttonOpenScoresWindow(nullptr); - score_window = score_window_to_delete; - scoreDisplayMultiplayer = score_window_delete_multiplayer; - score_window_to_delete = 0; - score_window_delete_multiplayer = false; - - loadScore(score_window - 1); -} - -void buttonDeleteScoreConfirm(button_t* my) -{ - buttonDeleteCurrentScore(nullptr); - buttonDeleteScoreCancel(nullptr); -} - -void buttonDeleteScoreWindow(button_t* my) -{ - score_window_to_delete = score_window; - score_window_delete_multiplayer = scoreDisplayMultiplayer; - - // close current window - buttonCloseSubwindow(nullptr); - list_FreeAll(&button_l); - deleteallbuttons = true; - - // create confirmation window - subwindow = 1; - subx1 = xres / 2 - 244; - subx2 = xres / 2 + 244; - suby1 = yres / 2 - 60; - suby2 = yres / 2 + 60; - strcpy(subtext, Language::get(3002)); - - // close button - button_t* button = newButton(); - strcpy(button->label, "x"); - button->x = subx2 - 20; - button->y = suby1; - button->sizex = 20; - button->sizey = 20; - button->action = &buttonDeleteScoreCancel; - button->visible = 1; - button->focused = 1; - button->key = SDLK_ESCAPE; - button->joykey = joyimpulses[INJOY_MENU_CANCEL]; - - // delete button - button = newButton(); - strcpy(button->label, Language::get(3001)); - button->sizex = strlen(Language::get(3001)) * 12 + 8; - button->sizey = 20; - button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2; - button->y = suby2 - 56; - button->action = &buttonDeleteScoreConfirm; - button->visible = 1; - button->focused = 1; - button->key = SDLK_RETURN; - - // close button - button = newButton(); - strcpy(button->label, Language::get(2962)); - button->sizex = strlen(Language::get(2962)) * 12 + 8; - button->sizey = 20; - button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2; - button->y = suby2 - 28; - button->action = &buttonDeleteScoreCancel; - button->visible = 1; - button->focused = 1; -} - void buttonOpenCharacterCreationWindow(button_t* my) { button_t* button; @@ -13655,147 +12771,6 @@ size_t serialHash(const std::string& input) } return hash; } - -void buttonConfirmSerial(button_t* my) -{ - serialVerifyWindow = 1; - if ( SDL_IsTextInputActive() ) - { - SDL_StopTextInput(); - } - list_FreeAll(&button_l); - deleteallbuttons = true; -} - -void windowSerialResult(int success) -{ - // close current window - if ( success > 0 ) - { - char path[PATH_MAX] = ""; - if ( success == 2 ) - { - completePath(path, "legendsandpariahs.key", outputdir); - } - else if ( success == 1 ) - { - completePath(path, "mythsandoutcasts.key", outputdir); - } - - // open the serial file - File* fp = nullptr; - if ( (fp = FileIO::open(path, "wb")) == NULL ) - { - printlog("ERROR: failed to save license file!\n"); - } - else - { - fp->write(serialInputText, sizeof(char), strlen(serialInputText)); - FileIO::close(fp); - } - } - buttonCloseSubwindow(nullptr); - list_FreeAll(&button_l); - deleteallbuttons = true; - - subwindow = 1; - subx1 = xres / 2 - 250; - subx2 = xres / 2 + 250; - suby1 = yres / 2 - 32; - suby2 = yres / 2 + 32; - - // ok button - button_t* button = newButton(); - strcpy(button->label, Language::get(1317)); - button->x = subx2 - strlen(Language::get(1317)) * 12 - 16; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1317)) * 12 + 8; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - - // close button - button = newButton(); - strcpy(button->label, "x"); - button->x = subx2 - 20; - button->y = suby1; - button->sizex = 20; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - button->key = SDLK_ESCAPE; - button->joykey = joyimpulses[INJOY_MENU_CANCEL]; - - if ( success > 0 ) - { - if ( success == 2 ) - { - strcpy(subtext, Language::get(3405)); - } - else if ( success == 1 ) - { - strcpy(subtext, Language::get(3404)); - } - playSound(402, 92); - } - else - { - strcpy(subtext, Language::get(3406)); - } -} - -void windowEnterSerialPrompt() -{ - // create confirmation window - subwindow = 1; - subx1 = xres / 2 - 300; - subx2 = xres / 2 + 300; - suby1 = yres / 2 - 64; - suby2 = yres / 2 + 64; - strcpy(subtext, Language::get(3403)); - strcpy(serialInputText, ""); - serialEnterWindow = true; - serialVerifyWindow = 0; - - // yes button - button_t* button = newButton(); - strcpy(button->label, "Submit"); - button->x = subx2 - strlen("Submit") * 12 - 16; - button->y = suby2 - 28 * 2; - button->sizex = strlen("Submit") * 12 + 8; - button->sizey = 20; - button->action = &buttonConfirmSerial; - button->visible = 1; - button->focused = 1; - button->key = SDLK_RETURN; - button->joykey = joyimpulses[INJOY_MENU_NEXT]; //TODO: Select which button to activate via dpad. - - // cancel button - button = newButton(); - strcpy(button->label, Language::get(1316)); - button->x = subx2 - strlen(Language::get(1316)) * 12 - 16; - button->y = suby2 - 28; - button->sizex = strlen(Language::get(1316)) * 12 + 8; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - - // close button - button = newButton(); - strcpy(button->label, "x"); - button->x = subx2 - 20; - button->y = suby1; - button->sizex = 20; - button->sizey = 20; - button->action = &buttonCloseSubwindow; - button->visible = 1; - button->focused = 1; - button->key = SDLK_ESCAPE; - button->joykey = joyimpulses[INJOY_MENU_CANCEL]; -} #endif // STEAMWORKS LastCreatedCharacter LastCreatedCharacterSettings; diff --git a/src/menu.hpp b/src/menu.hpp index 5000a81e9..18d87c621 100644 --- a/src/menu.hpp +++ b/src/menu.hpp @@ -87,7 +87,6 @@ void buttonLoadSingleplayerGame(button_t* my); void buttonLoadMultiplayerGame(button_t* my); void buttonRandomCharacter(button_t* my); bool replayLastCharacter(const int index, int multiplayer); -void buttonDeleteScoreWindow(button_t* my); void buttonOpenScoresWindow(button_t* my); void buttonRandomName(button_t* my); void buttonGamemodsOpenDirectory(button_t* my); From c531014de2c430830bb1c7afe916fee6a9733352 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Mon, 9 Oct 2023 08:05:28 +1100 Subject: [PATCH 120/146] * deprecated large sections of lang file --- lang/en.txt | 3570 ++++++++++++++------------------------------------- 1 file changed, 970 insertions(+), 2600 deletions(-) diff --git a/lang/en.txt b/lang/en.txt index 486bd5260..a2bc5827e 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -21,45 +21,16 @@ # class descriptions -10 A skilled combatant. What they lack in armor they make up for in strength and fighting -prowess. -Difficulty: Normal# - -11 The trained soldier. They are heavily armored and make capable leaders. - -Difficulty: Beginner# - -12 A talented physician. Though they are poor fighters, they come stocked with medical -supplies and other healing abilities. -Difficulty: Normal# - -13 The professional hooligan. Dextrous and skilled thieves, though lacking in power and -equipment. -Difficulty: Hard# - -14 Hardened traveler. Low in armor and combat ability, but well-equipped for the dungeon. - -Difficulty: Normal# - -15 Students of the church. Fairly well equipped and able in many ways, they are -well-rounded adventurers. -Difficulty: Beginner# - -16 A seasoned trader. They are skilled in the market and adept at identifying foreign -artifacts. -Difficulty: Hard# - -17 The wise magician. Though frail, they are extremely well-versed in magic. - -Difficulty: Hard# - -18 A cunning spellcaster. Less magically adept than the Wizard, but hardier and better -equipped. -Difficulty: Normal# - -19 The wild card. Jokers come with very little equipment, but they have a few tricks up -their sleeves nonetheless. -Difficulty: Extreme# +10 DEPRECATED# +11 DEPRECATED# +12 DEPRECATED# +13 DEPRECATED# +14 DEPRECATED# +15 DEPRECATED# +16 DEPRECATED# +17 DEPRECATED# +18 DEPRECATED# +19 DEPRECATED# # appearance names @@ -86,46 +57,46 @@ Difficulty: Extreme# # appearance descriptions -38 The balanced face of a landguard.# -39 The fair complexion of a northborn.# -40 The wild visage of a firebrand.# -41 The tough features of a hardbred.# -42 The refined look of a highwatch.# -43 The audacious haught of a gloomforge.# -44 The meticulous grooming of a pyrebloom.# -45 The ferocious spirit of a snakestone.# -46 The proud demeanor of a windclan.# -47 The unrivaled passion of a warblood.# -48 The conservative modesty of a millbound.# -49 The single-minded focus of a sunstalk.# -50 The practical appearance of a claymount.# -51 The sleek profile of a stormward.# -52 The brazen flair of a tradefall.# -53 The bold presence of a nighthill.# -54 The determined stoicism of a baytower.# -55 The natural elegance of a whetsong.# +38 DEPRECATED# +39 DEPRECATED# +40 DEPRECATED# +41 DEPRECATED# +42 DEPRECATED# +43 DEPRECATED# +44 DEPRECATED# +45 DEPRECATED# +46 DEPRECATED# +47 DEPRECATED# +48 DEPRECATED# +49 DEPRECATED# +50 DEPRECATED# +51 DEPRECATED# +52 DEPRECATED# +53 DEPRECATED# +54 DEPRECATED# +55 DEPRECATED# # credit titles -56 Lead Design & Programming# -57 Audio# -58 Programming# -59 Art# -60 Additional Art# -61 Writing# -62 Testing# -63 Special Thanks# -64 And of Course...# -65 You, the Player# +56 DEPRECATED# +57 DEPRECATED# +58 DEPRECATED# +59 DEPRECATED# +60 DEPRECATED# +61 DEPRECATED# +62 DEPRECATED# +63 DEPRECATED# +64 DEPRECATED# +65 DEPRECATED# -# legalese +# -66 Barony is a product of Turning Wheel LLC# -67 Copyright (c) 2018, all rights reserved.# -68 For more information, please visit# -69 http://www.baronygame.com/# +66 DEPRECATED# +67 DEPRECATED# +68 DEPRECATED# +69 DEPRECATED# # baron herx subtitles @@ -439,11 +410,11 @@ thou makest use of them.# 249 Shield# -# steam lobby type +# -250 Private lobby# -251 Public lobby# -252 Friends-only lobby# +250 DEPRECATED# +251 DEPRECATED# +252 DEPRECATED# # Left-click (entity spotting) messages @@ -509,23 +480,23 @@ thou makest use of them.# # status bar -306 HP# -307 MP# +306 DEPRECATED# +307 DEPRECATED# # items and inventory -308 Cost: %d MP# -309 unidentified# -310 cursed# -311 uncursed# -312 blessed# -313 (%-5d wght)# -314 (%-5d gold)# -315 %+d ATTACK# -316 %+d ARMOR# -317 Appraise Item# -318 Identify Item# +308 DEPRECATED# +309 DEPRECATED# +310 DEPRECATED# +311 DEPRECATED# +312 DEPRECATED# +313 DEPRECATED# +314 DEPRECATED# +315 DEPRECATED# +316 DEPRECATED# +317 DEPRECATED# +318 DEPRECATED# 319 This %s is already identified.# 320 Identified "%s".# @@ -578,38 +549,38 @@ thou makest use of them.# # character sheet -359 Level %d %s# -360 Exp:%3d/100# -361 Dungeon Level: %d# -1200 STR: %2d -> # -1201 DEX: %2d -> # -1202 CON: %2d -> # -1203 INT: %2d -> # -1204 PER: %2d -> # -1205 CHR: %2d -> # +359 DEPRECATED# +360 DEPRECATED# +361 DEPRECATED# +1200 DEPRECATED# +1201 DEPRECATED# +1202 DEPRECATED# +1203 DEPRECATED# +1204 DEPRECATED# +1205 DEPRECATED# # skill levels -363 None# -364 Novice# -365 Basic# -366 Skilled# -367 Expert# -368 Master# -369 Legend# +363 DEPRECATED# +364 DEPRECATED# +365 DEPRECATED# +366 DEPRECATED# +367 DEPRECATED# +368 DEPRECATED# +369 DEPRECATED# # rest of the character sheet -370 Gold:%13d Gold# -371 Armor:%12d AC# -372 Weight:%11d Wt.# +370 DEPRECATED# +371 DEPRECATED# +372 DEPRECATED# # chest inventory -373 Chest Inventory# +373 DEPRECATED# 374 Took %s from the chest.# @@ -1761,112 +1732,99 @@ Be inventive. Every item has its use.# # main menu -1303 Start Game# -1304 Introduction# -1305 Statistics# -1306 Settings# -1307 Credits# -1308 Quit# -1309 Resume Game# -1310 End Game# -1311 Disconnect# -1312 Restart Game# -1313 Save & Quit# +1303 DEPRECATED# +1304 DEPRECATED# +1305 DEPRECATED# +1306 DEPRECATED# +1307 DEPRECATED# +1308 DEPRECATED# +1309 DEPRECATED# +1310 DEPRECATED# +1311 DEPRECATED# +1312 DEPRECATED# +1313 DEPRECATED# 1314 Yes# 1315 No# -1316 Cancel# -1317 Accept# +1316 DEPRECATED# +1317 DEPRECATED# # character creation -1318 Character Creation# -1319 Sex:# -1320 Creates a %s character.# -1321 male# -1322 female# -1323 Class:# -1324 Appearance:# -1325 Name:# -1326 Enter your character's name.# -1327 Gamemode:# -1328 singleplayer# -1329 Start a singleplayer game.# -1330 host multiplayer# -1331 Host a multiplayer game.# -1332 join multiplayer# -1333 Join a multiplayer game.# +1318 DEPRECATED# +1319 DEPRECATED# +1320 DEPRECATED# +1321 DEPRECATED# +1322 DEPRECATED# +1323 DEPRECATED# +1324 DEPRECATED# +1325 DEPRECATED# +1326 DEPRECATED# +1327 DEPRECATED# +1328 DEPRECATED# +1329 DEPRECATED# +1330 DEPRECATED# +1331 DEPRECATED# +1332 DEPRECATED# +1333 DEPRECATED# # lobby browser -1334 Lobby Browser# -1335 Server flags:# -1336 -None# -1337 No lobbies found.# +1334 DEPRECATED# +1335 DEPRECATED# +1336 DEPRECATED# +1337 DEPRECATED# # settings window -1338 Resolution# -1339 Extra Options# -1340 smooth lighting# -1341 fullscreen# -1342 shaking# -1343 bobbing# -1344 blood# -1345 colorblind mode# -1346 Field of View (FOV)# -1347 Gamma# - -1348 General Sound Volume# -1349 Music Volume# - -1350 Click to rebind. ESC cancels.# -1351 Move Forward# -1352 Move Left# -1353 Move Backward# -1354 Move Right# -1355 Turn Left# -1356 Turn Right# -1357 Look Up# -1358 Look Down# -1359 Chat# -1360 Console Command# -1361 Character Status# -1362 Spell List# -1363 Cast Spell# -1364 Defend / Sneak# - -1365 Mouse Sensitivity# -1366 reverse mouse# -1367 smooth mouse# - -1368 The following mouse buttons cannot be -rebound:# - -1369 Left mouse button: -inspect object, drag item, attack# - -1370 Right mouse buttons: -use object, use item# - -1371 Miscellaneous Options# -1372 hide ip address# -1373 disable hud# -1374 add new items to hotbar# +1338 DEPRECATED# +1339 DEPRECATED# +1340 DEPRECATED# +1341 DEPRECATED# +1342 DEPRECATED# +1343 DEPRECATED# +1344 DEPRECATED# +1345 DEPRECATED# +1346 DEPRECATED# +1347 DEPRECATED# + +1348 DEPRECATED# +1349 DEPRECATED# + +1350 DEPRECATED# +1351 DEPRECATED# +1352 DEPRECATED# +1353 DEPRECATED# +1354 DEPRECATED# +1355 DEPRECATED# +1356 DEPRECATED# +1357 DEPRECATED# +1358 DEPRECATED# +1359 DEPRECATED# +1360 DEPRECATED# +1361 DEPRECATED# +1362 DEPRECATED# +1363 DEPRECATED# +1364 DEPRECATED# + +1365 DEPRECATED# +1366 DEPRECATED# +1367 DEPRECATED# + +1368 DEPRECATED# +1369 DEPRECATED# +1370 DEPRECATED# +1371 DEPRECATED# +1372 DEPRECATED# +1373 DEPRECATED# +1374 DEPRECATED# # multiplayer windows -1375 Gameflags# - -1376 -*** %s has left the game *** -# - -1377 -Connection denied: -# +1375 DEPRECATED# +1376 DEPRECATED# +1377 DEPRECATED# 1378 The server is already full.# 1379 Game version mismatch.# @@ -1876,474 +1834,154 @@ Connection denied: 1383 Savegames do not match.# 1384 Bad response from server.# -1385 -Waiting for the server to start the game. -Address connected to: # - -1386 -Waiting for the host to start the game. -# - -1387 - -Player list: -# - -1388 -*** %s has joined the game *** -# +1385 DEPRECATED# +1386 DEPRECATED# +1387 DEPRECATED# +1388 DEPRECATED# # highscore window -1389 You have no highscores yet!# -1390 Solo Highscores# -1391 Long Live# -1392 the Triumphant!# -1393 the Eternal!# -1394 Rest in Peace# -1395 the %s.# - -1396 Experience: %d/100# -1397 Total Gold: %dG# -1398 STR: %2d (%2d)# -1399 DEX: %2d (%2d)# -1400 CON: %2d (%2d)# -1401 INT: %2d (%2d)# -1402 PER: %2d (%2d)# -1403 CHR: %2d (%2d)# - -1404 Total Score: %d# -1405 Time# -1406 Conduct# -1407 No awards# -1408 Penniless# -1409 Foodless# -1410 Vegetarian# -1411 Illiterate# -1412 Kills:# -1413 none# +1389 DEPRECATED# +1390 DEPRECATED# +1391 DEPRECATED# +1392 DEPRECATED# +1393 DEPRECATED# +1394 DEPRECATED# +1395 DEPRECATED# +1396 DEPRECATED# +1397 DEPRECATED# +1398 DEPRECATED# +1399 DEPRECATED# +1400 DEPRECATED# +1401 DEPRECATED# +1402 DEPRECATED# +1403 DEPRECATED# +1404 DEPRECATED# +1405 DEPRECATED# +1406 DEPRECATED# +1407 DEPRECATED# +1408 DEPRECATED# +1409 DEPRECATED# +1410 DEPRECATED# +1411 DEPRECATED# +1412 DEPRECATED# +1413 DEPRECATED# # text crawl sequences -1414 Click to continue. ESC skips# - -1415 Once upon a time in a far away land, there stood a town called Hamlet.# - -1416 - -For generations, Hamlet served as a thriving center of commerce for all the -neighboring cities, and the luck of its people was second to none.# - -1417 - - - - -That was, until Baron Herx came to rule the land.# - -1418 - - - - - - -The Baron, in his endless greed, forced his citizens to dig the surrounding -hills for gold, though the earth had never yielded such treasure before.# - -1419 - - - - - - - - - -Meanwhile, as the people were forced to work, they schemed to undo their -unjust leader. Eventually, they were able to trick him with a promise of gold -and sealed him within the mines themselves.# - -1420 - - - - - - - - - - - - - -However, in the years that passed since the people of Hamlet did away with -their ruler, things did not go well for the people of the village.# - -1421 - - - - - - - - - - - - - - - - -In a twist of fate, a variety of monsters began to erupt from the ground -around the town. Some thought it to be Herx exacting his revenge from beyond -the grave, but none could be sure, and eventually, all trade in and out of -town stopped completely.# - -1422 - - - - - - - - - - - - - - - - - - - - - -Today, the region of Hamlet is considered cursed, and no one even knows if -the town still exists...# - -1423 Victorious, you step into the portal. You know not what to expect, but -are awash with relief. Somehow, you know that the Baron's curse has been -lifted from this land.# - -1424 - - - - - -Before you have time to think, you are suddenly standing before the mines -once more. But something is different; the howls of distant monsters have -vanished, and the skies, once a menacing black, are clearing to show the -light of day once again.# - -1425 - - - - - - - - - - - -Suddenly, the doors to the mine open wide. Sealed for centuries to contain -the evil within, they swing out from the inside, as a hundred grateful humans -rush out to greet the one who destroyed the Baron.# - -1426 - - - - - - - - - - - - - - - - -The hellish source of the Baron's power remains a mystery to you, and some -deliberation leads you to believe that a visit to the Magician's Guild is in -order for some deeper investigation. But for now, you simply attend to the -merriment of the people you just saved...# - -1427 - - -Hacking your way through every demon in your path, you leap through the -portal without a moment's hesitation. Anywhere is better than here - or so -you think.# - -1428 - - - - -Suddenly, you find yourself standing wearily in an endless white hall dotted -with enormous pillars that stretch higher than you can see. Out of -exhaustion, you drop your weapons and step forward.# - -1429 - - - - - - - - - -Surely, if anything were to happen now you would be finished; yet without -explanation, you suddenly sense power coming back to you. A voice booms out:# - -1430 - - - - - - - - - - - - -"Greetings, human. We have witnessed your trials and tribulations and we are -infinitely impressed by your fortitude and strength of will. Therefore, we -have a proposition for you..."# - -1431 - - - - - - - - - - - - - - - - - -As the voice addresses you, you find yourself either unwilling or unable to -reject what it tells you. Eventually, there is an inexplicable shift in your -mortal being, and you feel yourself fading away...# - -1432 - - - - - - - - - - - - - - - - - - - - - -"Evil fades from the mortal world, and its Guardian sleeps. The first age -comes to a close, and the age of chaos begins. Rest now, brave one: your time -will come again."# - +1414 DEPRECATED# +1415 DEPRECATED# +1416 DEPRECATED# +1417 DEPRECATED# +1418 DEPRECATED# +1419 DEPRECATED# +1420 DEPRECATED# +1421 DEPRECATED# +1422 DEPRECATED# +1423 DEPRECATED# +1424 DEPRECATED# +1425 DEPRECATED# +1426 DEPRECATED# +1427 DEPRECATED# +1428 DEPRECATED# +1429 DEPRECATED# +1430 DEPRECATED# +1431 DEPRECATED# +1432 DEPRECATED# # even more button and window text -1433 OK # -1434 Video# -1435 Audio# -1436 Bindings# -1437 Mouse# -1438 Misc# - -1439 -Failed to connect to server: +1433 DEPRECATED# +1434 DEPRECATED# +1435 DEPRECATED# +1436 DEPRECATED# +1437 DEPRECATED# +1438 DEPRECATED# +1439 DEPRECATED# +1440 DEPRECATED# +1441 DEPRECATED# +1442 DEPRECATED# +1443 DEPRECATED# +1444 DEPRECATED# +1445 DEPRECATED# +1446 DEPRECATED# +1447 DEPRECATED# +1448 DEPRECATED# +1449 DEPRECATED# +1450 DEPRECATED# +1451 DEPRECATED# +1452 DEPRECATED# +1453 DEPRECATED# +1454 DEPRECATED# +1455 DEPRECATED# +1456 DEPRECATED# +1457 DEPRECATED# +1458 DEPRECATED# +1459 DEPRECATED# +1460 DEPRECATED# +1461 DEPRECATED# +1462 DEPRECATED# +1463 DEPRECATED# +1464 DEPRECATED# +1465 DEPRECATED# +1466 DEPRECATED# +1467 DEPRECATED# -# - -1440 -Failed to start server: +# obituary messages -# +1499 %s's %s ally %s# +1500 dies randomly.# +1501 gets blasted by a magic tower.# +1502 gets blasted by a ceiling magic trap.# +1503 stumbles into an arrow trap.# +1504 stumbles into a beartrap and dies.# +1505 fails to dodge the incoming boulder.# +1506 goes for a swim in some lava.# +1507 walks into a spike trap and dies.# +1508 gets killed by a flying %s.# +1509 was betrayed by his fellow %s.# +1510 was betrayed by her fellow %s.# +1511 is violently dispatched by an angry human.# +1512 gets nibbled to death by a lowly rat.# +1513 is viciously flayed by a goblin.# +1514 was gooified by a slimy blob.# +1515 invades the troll's comfort zone and gets smacked.# +1516 makes a nice dinner for the hungry spider.# +1517 decides to become one with the undead.# +1518 gets spooked by a scary skeleton.# +1519 tries to make friends with the angry scorpion.# +1520 forgets that flying imps are not very friendly.# +1521 gets mugged by a greedy gnome.# +1522 gets quartered by a demon.# +1523 tries vainly to make love to the succubus.# +1524 gets on the Baron's bad-side.# +1525 is unceremoniously pulverized by the minotaur.# +1526 is damned to hell. Literally.# +1527 attempts a robbery and fails.# +1528 kills himself during an invocation.# +1529 kills herself during an invocation.# +1530 diets for a little too long.# +1531 is finished off by the poison.# +1532 bleeds to death.# +1533 burns to a crisp.# +1534 gets choked to death.# +1535 drinks the funny potion and dies.# -1441 -Failed to join the selected lobby. +# some random strings -# +1536 DEPRECATED# +1537 DEPRECATED# +1538 DEPRECATED# +1539 DEPRECATED# +1540 DEPRECATED# +1541 DEPRECATED# +1542 DEPRECATED# +1543 DEPRECATED# +1544 DEPRECATED# -1442 -Failed to create the lobby. - -# - -1443 -The connection to the server was lost. - -# - -1444 Requesting lobby list...# - -1445 Join Game# -1446 Refresh # - -1447 -Connecting to matchmaking lobby... -# - -1448 -Port# - -1449 Host# - -1450 -Address:Port# - -1451 Join# - -1452 You have entered the lobby. -# - -1453 NOTE: As you are loading a save game, only players with -compatible save games can join. -# - -1454 -Press "Start Game" when everyone has joined. -Port hosted on: # - -1455 -Press "Start Game" when everyone has joined. -# - -1456 - -Player list: -# - -1457 Start Game# -1458 Invite Friends# - -1459 -Connecting to server. Please wait... -# - -1460 -We've found save file(s) for a character: - -# - -1461 - - -Would you like to start a new game or continue a -save game? - -NOTE: Starting a new solo or multiplayer game will -erase the previous save file!# - -1462 Continue solo game# -1463 Start a new game (Enter)# -1464 Continue# -1465 Back# -1466 Random Character# - -1467 -Connecting to matchmaking lobby... -# - - -# obituary messages - -1499 %s's %s ally %s# -1500 dies randomly.# -1501 gets blasted by a magic tower.# -1502 gets blasted by a ceiling magic trap.# -1503 stumbles into an arrow trap.# -1504 stumbles into a beartrap and dies.# -1505 fails to dodge the incoming boulder.# -1506 goes for a swim in some lava.# -1507 walks into a spike trap and dies.# -1508 gets killed by a flying %s.# -1509 was betrayed by his fellow %s.# -1510 was betrayed by her fellow %s.# -1511 is violently dispatched by an angry human.# -1512 gets nibbled to death by a lowly rat.# -1513 is viciously flayed by a goblin.# -1514 was gooified by a slimy blob.# -1515 invades the troll's comfort zone and gets smacked.# -1516 makes a nice dinner for the hungry spider.# -1517 decides to become one with the undead.# -1518 gets spooked by a scary skeleton.# -1519 tries to make friends with the angry scorpion.# -1520 forgets that flying imps are not very friendly.# -1521 gets mugged by a greedy gnome.# -1522 gets quartered by a demon.# -1523 tries vainly to make love to the succubus.# -1524 gets on the Baron's bad-side.# -1525 is unceremoniously pulverized by the minotaur.# -1526 is damned to hell. Literally.# -1527 attempts a robbery and fails.# -1528 kills himself during an invocation.# -1529 kills herself during an invocation.# -1530 diets for a little too long.# -1531 is finished off by the poison.# -1532 bleeds to death.# -1533 burns to a crisp.# -1534 gets choked to death.# -1535 drinks the funny potion and dies.# - - -# some random strings - -1536 disable pop-up messages# -1537 (direct ip)# -1538 Host a multiplayer game without matchmaking, using a direct IP connection.# -1539 Join a multiplayer game without matchmaking, using a direct IP connection.# -1540 %s, level %d %s -dungeon level %d, singleplayer, %s# -1541 %s, level %d %s -dungeon level %d, server, %s# -1542 %s, level %d %s -dungeon level %d, client (player %d), %s# -1543 %s, level %d %s -dungeon level %d, server (direct ip), %s# -1544 %s, level %d %s -dungeon level %d, client (player %d) (direct ip), %s# - - -# item names +# item names 1545 DEPRECATED_ITEM_NAME# 1546 DEPRECATED_ITEM_NAME# @@ -2682,129 +2320,119 @@ dungeon level %d, client (player %d) (direct ip), %s# 1879 DEPRECATED_ITEM_NAME# 1880 DEPRECATED_ITEM_NAME# -# new random messages - -1882 skill in %s: %s# -1883 Skills# +1882 DEPRECATED# +1883 DEPRECATED# # menu subtitles -1910 Updated Logo Edition!# -1911 TowerClimb is also fun!# -1912 Rogue Wizards is spellbinding!# -1913 Try Crayon Chronicles!# -1914 Director's Cut!# -1915 The Pre-Sequel!# -1916 Return of the Return of the Return!# -1917 Now with Boulder Traps!# -1918 100% certified Minotaur!# -1919 Heart-Gold Version!# -1920 Enhanced RNG!# -1921 Now with Crossplay!# -1922 Baphomet Approved!# -1923 By Popular Demand!# -1924 Potato King's Pick!# -1925 Sharur Approved. It's true!# -1926 NOBODY expects the Spanish Inquisition!# -1927 Part of a balanced breakfast!# -1928 It goes to 11!# -1929 Right after these messages!# -1930 Share the load!# -1931 What do your elf eyes see?# -1932 They're taking the hobbits to Isengard!# -1933 You shall not pass!# -1934 What about breakfast?# -1935 A wizard is never late!# -1936 And my axe!# -1937 Troll in the dungeon!# -1938 Fool of a Took!# -1939 Off with her head!# +1910 DEPRECATED# +1911 DEPRECATED# +1912 DEPRECATED# +1913 DEPRECATED# +1914 DEPRECATED# +1915 DEPRECATED# +1916 DEPRECATED# +1917 DEPRECATED# +1918 DEPRECATED# +1919 DEPRECATED# +1920 DEPRECATED# +1921 DEPRECATED# +1922 DEPRECATED# +1923 DEPRECATED# +1924 DEPRECATED# +1925 DEPRECATED# +1926 DEPRECATED# +1927 DEPRECATED# +1928 DEPRECATED# +1929 DEPRECATED# +1930 DEPRECATED# +1931 DEPRECATED# +1932 DEPRECATED# +1933 DEPRECATED# +1934 DEPRECATED# +1935 DEPRECATED# +1936 DEPRECATED# +1937 DEPRECATED# +1938 DEPRECATED# +1939 DEPRECATED# # extra keybindings :) -1940 Attack# -1941 Use# +1940 DEPRECATED# +1941 DEPRECATED# # game/server flags tooltips # Cheats. -1942 NOTE: Enabling cheats will disable Achievements on your save files! # -# Friendly fire. -1943 NOTE: Enabling FF will allow you to damage your allies with attacks and magic! # -# Minotaurs. -1944 NOTE: Disabling this will prevent minotaurs from spawning in regular levels, -allowing you to take your time through each level.# -# Hunger. -1945 NOTE: Disabling hunger removes the need to eat food to prevent starvation. -Food will instead heal 5 HP each, and natural HP regeneration is disabled.# -# Traps. -1946 NOTE: Only removes randomly generated traps, such as boulders and arrow traps along -the edges and hallways of levels. Traps will only be found inside rooms that have -been designed to contain them.# +1942 DEPRECATED# +1943 DEPRECATED# +1944 DEPRECATED# +1945 DEPRECATED# +1946 DEPRECATED# # Gamepad settings menu stuff. -1947 Gamepad Bindings# +1947 DEPRECATED# # Bifunctional bindings. -1948 Character Status# -1949 Spell List# -1950 Menu/Escape# -1951 D-Pad Left# -1952 D-Pad Right# -1953 D-Pad Up# -1954 D-Pad Down# +1948 DEPRECATED# +1949 DEPRECATED# +1950 DEPRECATED# +1951 DEPRECATED# +1952 DEPRECATED# +1953 DEPRECATED# +1954 DEPRECATED# # Menu exclusive bindings. -1955 "Left Click" (in MENUs)# -1956 Next (in MENUs)# -1957 Cancel# -1958 Next Settings Tab# -1959 Previous Settings Tab# -1960 Refresh Lobby# -1961 Don't Load Save (New Game)# -1962 Random Name# -1963 Random Character# -1964 Previous Inventory Tab# -1965 Next Inventory Tab# -1966 Item Context Menu# -1967 Drop Item# -1968 Clear Hotbar Slot# -1969 Grab All From Chest# -1970 Previous Shop Category# -1971 Next Shop Category# -1972 Previous Book Page# -1973 Next Book Page# +1955 DEPRECATED# +1956 DEPRECATED# +1957 DEPRECATED# +1958 DEPRECATED# +1959 DEPRECATED# +1960 DEPRECATED# +1961 DEPRECATED# +1962 DEPRECATED# +1963 DEPRECATED# +1964 DEPRECATED# +1965 DEPRECATED# +1966 DEPRECATED# +1967 DEPRECATED# +1968 DEPRECATED# +1969 DEPRECATED# +1970 DEPRECATED# +1971 DEPRECATED# +1972 DEPRECATED# +1973 DEPRECATED# # Game exclusive bindings. -1974 Use# -1975 Raise Shield# -1976 Attack# -1977 Cast Spell# -1978 Activate Hotbar Slot# -1979 Next Hotbar Slot# -1980 Previous Hotbar Slot# -1981 Minimap Scale Toggle# -1982 Toggle Chatlog# -1983 NPC Menu Quick Access# -1984 Repeat Last Follower Command# -1985 Cycle Currently Selected Follower# +1974 DEPRECATED# +1975 DEPRECATED# +1976 DEPRECATED# +1977 DEPRECATED# +1978 DEPRECATED# +1979 DEPRECATED# +1980 DEPRECATED# +1981 DEPRECATED# +1982 DEPRECATED# +1983 DEPRECATED# +1984 DEPRECATED# +1985 DEPRECATED# # Other bindings. -1986 Autosort Inventory# -1987 Minimap Scale Toggle# -1988 Toggle Chatlog# -1989 NPC Menu Quick Access# -1990 Repeat Last NPC Command# -1991 Cycle Through NPCs# +1986 DEPRECATED# +1987 DEPRECATED# +1988 DEPRECATED# +1989 DEPRECATED# +1990 DEPRECATED# +1991 DEPRECATED# # More stuff on the gamepad bindings screen. -1994 Menu exclusive bindings:# -1995 Game exclusive bindings:# -1996 Click to unbind. Click an unbound action to assign binding.# +1994 DEPRECATED# +1995 DEPRECATED# +1996 DEPRECATED# -1997 auto appraise items# -1998 right click protection# +1997 DEPRECATED# +1998 DEPRECATED# # START DLC # monsters, reserve 2000-2049 for monster names, 2050 - 2099 for plurals, 2100-2150 for injury types @@ -3091,21 +2719,21 @@ but it retracts beyond your might!# 2398 DEPRECATED_SPELL_NAME# 2399 DEPRECATED_SPELL_NAME# -2400 Gamepad# +2400 DEPRECATED# -2401 Invert Left Analog Stick X-Axis# -2402 Invert Left Analog Stick Y-Axis# -2403 Invert Right Analog Stick X-Axis# -2404 Invert Right Analog Stick Y-Axis# -2405 Invert X-Axis in GUI# -2406 Invert Y-Axis in GUI# +2401 DEPRECATED# +2402 DEPRECATED# +2403 DEPRECATED# +2404 DEPRECATED# +2405 DEPRECATED# +2406 DEPRECATED# -2407 Right Analog Stick X-Axis Sensitivity# -2408 Right Analog Stick Y-Axis Sensitivity# -2409 Menu X-Axis Sensitivity# -2410 Menu Y-Axis Sensitivity# +2407 DEPRECATED# +2408 DEPRECATED# +2409 DEPRECATED# +2410 DEPRECATED# -2411 Frames Per Second (FPS)# +2411 DEPRECATED# #Equip Effects @@ -3125,7 +2753,7 @@ but it retracts beyond your might!# 2423 %s is severely wounded!# 2424 The %s is severely wounded!# 2425 You are severely wounded!# -2426 Trio Missile# +2426 DEPRECATED# 2427 You dominate %s into your service!# 2428 You dominate the %s into your service!# 2429 You are unable to dominate this one.# @@ -3147,7 +2775,7 @@ but it retracts beyond your might!# 2445 You feel restored!# 2446 Your spell (reflect magic) was interrupted!# 2447 Your spell (vampiric aura) was interrupted!# -2448 Failed to sustain spell (vampiric aura).# +2448 DEPRECATED# 2449 Your thirst for blood settles...# 2450 The craftsmanship of the walls here is immaculate.# @@ -3160,10 +2788,21 @@ but it retracts beyond your might!# 2456 %s's wounds rupture further open!# 2457 The %s's blow bounces off!# 2458 %s's blow bounces off!# +2459 DEPRECATED# +2460 DEPRECATED# +2461 DEPRECATED# +2462 DEPRECATED# +2463 DEPRECATED# +2464 DEPRECATED# +2465 DEPRECATED# +2466 DEPRECATED# +2467 DEPRECATED# +2468 DEPRECATED# +2469 DEPRECATED# 2470 You no longer feel magic weaken against you.# 2471 You no longer feel magic bounce off you.# -2472 You feel magic weaken against you.# -2473 You feel magic bounce off you.# +2472 DEPRECATED# +2473 DEPRECATED# 2474 Failed to sustain spell (reflect magic).# 2475 But it bounces off!# 2476 You feel the reflect magic spell fail as the @@ -3253,67 +2892,54 @@ last of your magic is drained!# 2546 Your strike flanks %s's defenses!# 2547 You assassinate the %s!# 2548 You assassinate %s!# -2549 Players online: %4d # +2549 DEPRECATED# # 2550 - 2560 new class names -2550 sexton# -2551 ninja# -2552 monk# -2553 test# -2554 blank# -2555 blank# -2556 blank# -2557 blank# -2558 blank# -2559 blank# +2550 DEPRECATED# +2551 DEPRECATED# +2552 DEPRECATED# +2553 DEPRECATED# +2554 DEPRECATED# +2555 DEPRECATED# +2556 DEPRECATED# +2557 DEPRECATED# +2558 DEPRECATED# +2559 DEPRECATED# # 2560 - 2570 new class descriptions -2560 A temple officer who serves unseen, using stealth and magic to slip their way -through the dungeon with the aid of a few rare tools. -Difficulty: Hard# -2561 A highly specialized assassin. They ambush foes with swords or ranged weapons, -using a few other tricks to get out of bad situations. -Difficulty: Hard# -2562 Disciplined and hardy. They have little in the way of offensive training and -material goods, but can rely on their excellent fortitude and adaptability. -Difficulty: Normal# -2563 A blank. -Difficulty: Normal# -2564 A blank. -Difficulty: Normal# -2565 A blank. -Difficulty: Normal# -2566 A blank. -Difficulty: Normal# -2567 A blank. -Difficulty: Normal# -2568 A blank. -Difficulty: Normal# -2569 A blank. -Difficulty: Normal# +2560 DEPRECATED# +2561 DEPRECATED# +2562 DEPRECATED# +2563 DEPRECATED# +2564 DEPRECATED# +2565 DEPRECATED# +2566 DEPRECATED# +2567 DEPRECATED# +2568 DEPRECATED# +2569 DEPRECATED# # More misc. -2570 https://docs.google.com/forms/d/e/1FAIpQLScywW5ROX4m8cp05PspI7xZNV0jkEPAWJu3lWegS9Tj9NBDOg/viewform# -2571 Weapons# -2572 Armor# -2573 Jewelry# -2574 Books# -2575 Tools# -2576 Thrown# -2577 Gems# -2578 Potions# -2579 Scrolls# -2580 Staves# -2581 Food# -2582 Spells# -2583 Item Categories for Auto Hotbar:# -2584 Click here to provide feedback for the Beta!# - -2585 A big shoutout to the open source community for their contributions!# -2586 # -2587 Report issues and checkout the code at:# -2588 https://github.com/TurningWheel/Barony# +2570 DEPRECATED# +2571 DEPRECATED# +2572 DEPRECATED# +2573 DEPRECATED# +2574 DEPRECATED# +2575 DEPRECATED# +2576 DEPRECATED# +2577 DEPRECATED# +2578 DEPRECATED# +2579 DEPRECATED# +2580 DEPRECATED# +2581 DEPRECATED# +2582 DEPRECATED# +2583 DEPRECATED# +2584 DEPRECATED# + +2585 DEPRECATED# +2586 DEPRECATED# +2587 DEPRECATED# +2588 DEPRECATED# 2589 Your %s ally %s# -2590 modify hotbar with numkeys# +2590 DEPRECATED# 2591 You have nothing else to learn from this spell.# 2592 The fountain blesses a piece of equipment.# @@ -3322,189 +2948,23 @@ Difficulty: Normal# 2595 The spellbook deteriorates.# 2596 The spellbook deteriorates and crumbles to ash!# 2597 You feel your mind detaching from your mortal body.# -2598 keep right sidebar active# +2598 DEPRECATED# 2599 You feel refreshed!# -2600 Having defeated the Baron and placed his violet orb on the pedestal, you -step into the portal. Powerful magic sends a tingle over your body, and you are -transported with a white flash.# - -2601 - - - - - -You arrive outside the Hamlet mines and watch the cursed blackness peel away -from the land. Warm sunlight flows over the countryside. As the doors of the -mine open wide, people carefully spread into the streets, acclimating to -their freedom.# - -2602 - - - - - - - - - - -You march to the Magician's Guild for answers about Herx's power, but the -doorman there turns you away. - -Seeing you haggard from your quest, he encourages you to take a rest. -"The answers you seek will still be here after you recover."# - -2603 - - - - - - - - - - - - - - - - - -You turn back to the streets and find a hundred people waiting for you, their -hero. You join them in well-earned revelry, but resolve to revisit the -Magicians' Guild once the merriment dies down.# - -2604 - - - - - - - - - - - - - - - - - - - - - - -Now advance when you and your allies are ready!# - -2605 - - - - - - - - - - - - - - - - - - - - - - -Now wait for your leader to advance onward...# - -2606 Click to continue.# - -2607 Through the portal, a weak magic tugs you from the Citadel. Your feet -hover over the floor as it shakes violently. Pipes and pistons crash down -in a muffled racket. You have a detached sense of peace, and a moment to -ponder what has occurred.# - -2608 - - - - - - - -The lich siblings join the Baron in death, voiding their deals done with -devils.# -2609 - - - - - - - - - - - - -Is it for the best that you escaped death here? Through your trials, you've -earned power greater than they, their shattered remains proving it.# - -2610 You open your eyes, standing in The Magician's Guild with a wizard and bishop. -Their faces relax in relief.# - -2611 - - - -In the following days, you work with them in secrecy to prevent the realms of -hell from stepping foot in Hamlet again using the artifacts underground.# - -2612 - - - - - - - - -Hamlet begins its ascent toward its former glory. The guilds unite under your -banner to clear out the straggling monsters and rebuild the city. The citizens -beg you to take the Baron's court and rule.# - - -2613 - - - - - - - - - - - - - - -You vow to keep the hellish source of the liches' power secret, to prevent -anyone seeking that power again, deep underground. # - -2614 Power you know will always be waiting.# +2600 DEPRECATED# +2601 DEPRECATED# +2602 DEPRECATED# +2603 DEPRECATED# +2604 DEPRECATED# +2605 DEPRECATED# +2606 DEPRECATED# +2607 DEPRECATED# +2608 DEPRECATED# +2609 DEPRECATED# +2611 DEPRECATED# +2612 DEPRECATED# +2613 DEPRECATED# +2614 DEPRECATED# # Orpheus and Erudyce chatter. 2615 "Brother, I sense someone has arrived. Shall we greet them?"# @@ -3797,67 +3257,54 @@ anyone seeking that power again, deep underground. # 2904 The shrine bestows fortitude and might!# 2905 Your spell had no effect on the %s!# 2906 Your spell had no effect on %s!# -2907 "Do not be afraid to go slowly, be afraid of stopping."# +2907 DEPRECATED# 2908 Your thoughts no longer reside in solitude as you wrap the blindfold.# 2909 The shrine bestows marksmanship and haste!# 2910 The shrine bestows arcane knowledge and resistance!# -2911 the Baron!# +2911 DEPRECATED# # 2912-2916 Autosort text: -2912 Item Categories for Inventory Autosort: (?)# -2913 Use arrows to set priority for autosort. 0 has no priority.# -2914 1 is lowest priority, 9 is highest priority to fill left to right. # -2915 -1 is lowest priority, -9 is highest priority to fill right to left.# -2916 Equipped# +2912 DEPRECATED# +2913 DEPRECATED# +2914 DEPRECATED# +2915 DEPRECATED# +2916 DEPRECATED# # New server flags. -2917 Hardcore# -2918 Classic# -2919 Keep Inventory On Death# -2920 +1 Life# -# Hardcore. -2921 NOTE: Experimental! Increases the stats of all enemies and improves their reaction -time. For experienced adventurers who wish for a more difficult challenge! # -# Classic -2922 Cursed Edition levels only. For those wanting a shorter playthrough, or trying their -hand at conquering the Classic leaderboard categories.# -# Keep Inventory -2923 Disable players dropping inventory items on death. (Multiplayer only) -Enjoy the dungeon without having to loot your allies! # -# Life Saving -2924 Spawn at the start of the game with an amulet of life saving, granting you -an additional life. -NOTE: Enabling this will disable Achievements on your save files! # +2917 DEPRECATED# +2918 DEPRECATED# +2919 DEPRECATED# +2920 DEPRECATED# +2921 DEPRECATED# +2922 DEPRECATED# +2923 DEPRECATED# +2924 DEPRECATED# # 2925 - 2957 New conducts. -2925 Hardcore# -2926 Discreet# -2927 Allied# -2928 Classic# -2929 Modded# -2930 Brawler# -2931 Ultra Speedrunner# -2932 Speedrunner# -2933 Inventory Keeper# -2934 +1 Lifer# -2935 Cursed# -2936 Ranger# - -2958 Multiplayer Highscores# -2959 Continue multiplayer game# -2960 Autosort Inventory (%s)# -2961 Delete file# -2962 Cancel# -2963 Are you sure you want to delete your singleplayer save file? - -# -2964 Are you sure you want to delete your multiplayer save file? - -# -2965 WARNING: CONTINUING WILL OVERWRITE YOUR OLDEST SINGLEPLAYER SAVEFILE!# -2966 WARNING: CONTINUING WILL OVERWRITE YOUR OLDEST MULTIPLAYER SAVEFILE!# -2967 light flicker# -2968 press [%s] to drop gold# +2925 DEPRECATED# +2926 DEPRECATED# +2927 DEPRECATED# +2928 DEPRECATED# +2929 DEPRECATED# +2930 DEPRECATED# +2931 DEPRECATED# +2932 DEPRECATED# +2933 DEPRECATED# +2934 DEPRECATED# +2935 DEPRECATED# +2936 DEPRECATED# + +2958 DEPRECATED# +2959 DEPRECATED# +2960 DEPRECATED# +2961 DEPRECATED# +2962 DEPRECATED# +2963 DEPRECATED# +2964 DEPRECATED# +2965 DEPRECATED# +2966 DEPRECATED# +2967 DEPRECATED# +2968 DEPRECATED# 2969 The gods of Sokoban greatly reward your abilities!# 2970 The gods of Sokoban were mildly entertained.# 2971 The gods of Sokoban were less than impressed.# @@ -3866,127 +3313,99 @@ NOTE: Enabling this will disable Achievements on your save files! # 2974 Vibrations pulse from the earth through your feet.# 2975 The cape's power surges through your body.# 2976 You're filled with otherworldly defiance.# -2977 Cost: %d + ?? MP# -2978 Custom Content# -2979 Workshop# -2980 Character Creation (Modded)# -2981 WARNING: MAKE SURE ALL LOBBY MEMBERS HAVE -IDENTICAL MOD LISTS LOADED!# -2982 NOTE: Modded sessions use their own savegame files.# -2983 keep game timer on HUD# -2984 Subscribe to host mod list# -2985 Mount host mod list# -2986 Savefile has cheats enabled# -2987 Loading modded sound files...# -2988 Unloading modded sound files...# -2989 Loading modded model files...# -2990 Unloading modded model files...# -2991 Loading modded book files...# -2992 Unloading modded book files...# -2993 Loading modded music files...# -2994 Unloading modded music files...# +2977 DEPRECATED# +2978 DEPRECATED# +2979 DEPRECATED# +2980 DEPRECATED# +2981 DEPRECATED# +2982 DEPRECATED# +2983 DEPRECATED# +2984 DEPRECATED# +2985 DEPRECATED# +2986 DEPRECATED# +2987 DEPRECATED# +2988 DEPRECATED# +2989 DEPRECATED# +2990 DEPRECATED# +2991 DEPRECATED# +2992 DEPRECATED# +2993 DEPRECATED# +2994 DEPRECATED# 2995 Brawler-mode active, only punches and throwing potions are allowed to attack!# 2996 Brawler-mode deactivated, all weapons permitted.# 2997 This tool is disabled in Brawler-mode!# 2998 Player savefile is not eligible for Brawler Achievement. Restart game to reset.# 2999 Spells are disabled in Brawler-mode!# -3000 Use Previous Character# -3001 Delete score# -3002 Are you sure you want to delete this high score? - -# -3003 Achievements are disabled# -3004 Loading modded language file...# -3005 Unloading modded language file...# -3006 Loading modded item sprites...# -3007 Unloading modded item sprites...# -3008 Loading modded items.txt properties...# -3009 Unloading modded items.txt properties...# +3000 DEPRECATED# +3001 DEPRECATED# +3002 DEPRECATED# +3003 DEPRECATED# +3004 DEPRECATED# +3005 DEPRECATED# +3006 DEPRECATED# +3007 DEPRECATED# +3008 DEPRECATED# +3009 DEPRECATED# 3010 fell to their death.# -3011 vertical sync# -3012 mute minimap pings# -3013 Loading modded monster limbs.txt files...# -3014 Unloading modded monster limbs.txt files...# -3015 Loading modded system resource image files...# -3016 Unloading modded system resource image files...# -3017 Loading modded game tiles...# -3018 Unloading modded game tiles...# -3019 Save & Disconnect# -3020 - Are you sure you want to disconnect - and return to the main menu? - - Your multiplayer progress has been saved - at the start of the current level. - - NOTE: - If your current party members advance to - the next dungeon floor without your character, - your savefile will no longer be able to - rejoin the session.# -3021 - Are you sure you want to disconnect - and return to the main menu? - - Your multiplayer progress has been saved - at the start of the current level. - - NOTE: - This will disconnect all party members and - save their current progress.# -3022 Minimap Options# -3023 Foreground Transparency (%)# -3024 Background Transparency (%)# -3025 Map Scale# -3026 Player/Ally Icon Zoom# -3027 Character Sheet Zoom# -3028 Skills/Party Sidebar Zoom# -3029 Hotbar Zoom# -3030 Chatlog Zoom# -3031 Player HP/MP Zoom# -3032 Inventory Zoom# -3033 Hide Chatlog# -3034 User Interface Options# +3011 DEPRECATED# +3012 DEPRECATED# +3013 DEPRECATED# +3014 DEPRECATED# +3015 DEPRECATED# +3016 DEPRECATED# +3017 DEPRECATED# +3018 DEPRECATED# +3019 DEPRECATED# +3020 DEPRECATED# +3021 DEPRECATED# +3022 DEPRECATED# +3023 DEPRECATED# +3024 DEPRECATED# +3025 DEPRECATED# +3026 DEPRECATED# +3027 DEPRECATED# +3028 DEPRECATED# +3029 DEPRECATED# +3030 DEPRECATED# +3031 DEPRECATED# +3032 DEPRECATED# +3033 DEPRECATED# +3034 DEPRECATED# 3035 There's no ground beneath your feet to place that!# -# Follower Radial Menu Text -3036 Follower Options:# -3037 Wait here# -3038 Class:# +3036 DEPRECATED# +3037 DEPRECATED# +3038 DEPRECATED# 3039 Move to...# -3040 Pickup -Items:# -3041 Move aside# -3042 Drop# -3043 Interact...# -3044 Rest# -# Begin secondary option text. -3045 Follow# -3046 # -3047 # -3048 # -3049 # -3050 # -3051 Interact / - Attack...# -3052 # -3053 [Mixed]# -3054 [Melee]# -3055 [Ranged]# -3056 [Unowned]# -3057 [None]# -3058 [All]# -3059 Weapon# -3060 Equipment# -3061 All# -3062 You do not meet the Leadership and Charisma requirements -for this option. (Required: %s, Current: %s)# +3040 DEPRECATED# +3041 DEPRECATED# +3042 DEPRECATED# +3043 DEPRECATED# +3044 DEPRECATED# +3045 DEPRECATED# +3046 DEPRECATED# +3047 DEPRECATED# +3048 DEPRECATED# +3049 DEPRECATED# +3050 DEPRECATED# +3051 DEPRECATED# +3052 DEPRECATED# +3053 DEPRECATED# +3054 DEPRECATED# +3055 DEPRECATED# +3056 DEPRECATED# +3057 DEPRECATED# +3058 DEPRECATED# +3059 DEPRECATED# +3060 DEPRECATED# +3061 DEPRECATED# +3062 DEPRECATED# 3063 Cancel# -3064 delete save file# -3065 Start a new game, or continue from a save file:# -3066 scroll with arrow keys or mouse wheel# -3067 %2d / %2d singleplayer slots used# -3068 %2d / %2d multiplayer slots used# +3064 DEPRECATED# +3065 DEPRECATED# +3066 DEPRECATED# +3067 DEPRECATED# +3068 DEPRECATED# # Follower responses. 3069 I'll stand ground here, %s.# @@ -4018,7 +3437,7 @@ for this option. (Required: %s, Current: %s)# # Follower responses end. # Misc -3095 view Steam Leaderboards# +3095 DEPRECATED# 3096 Your spell hits the %s!# 3097 door# 3098 gate# @@ -4028,8 +3447,8 @@ for this option. (Required: %s, Current: %s)# 3102 An arcane protection holds the gate firmly in place!# # Follower responses cont'd. -3103 Command not available for %s.# -3104 Attack...# +3103 DEPRECATED# +3104 DEPRECATED# 3105 The %s waits patiently.# 3106 %s waits patiently.# 3107 weapon is# @@ -4063,7 +3482,7 @@ for this option. (Required: %s, Current: %s)# 3135 Apologies %s, but I won't let go of my keepsakes.# # Misc -3136 Hide Player Nametags# +3136 DEPRECATED# 3137 The %s is charmed into your service!# 3138 %s is charmed into your service!# 3139 The charm seizes the %s's will to fight!# @@ -4074,49 +3493,35 @@ for this option. (Required: %s, Current: %s)# 3144 A magical charm seizes your will to fight!# 3145 The slime envelops and consumes the %s!# -3146 Networking# -3147 disable multithreaded Steam networking# -3148 Some clients may experience stuttering or poor framerates if enabled, -disable this or try direct-ip hosted lobbies to improve performance.# -3149 Hides IP address when typing in the direct-ip connect window. # -3150 Hides HUD elements in-game, such as hotbar, HP/MP, minimap etc. # -3151 Picked up items will automatically be added to your hotbar if enabled. You can -specify which categories of items that get added in the options to the right.# -3152 Automatically starts appraising unidentified items when you pick them up. -Will not interrupt any existing appraisal of an item.# -3153 Disables game event messages on the left-hand side message log. Messages will -still appear in the bottom scrollable message log.# -3154 If enabled, right clicking on an item in the inventory will not automatically equip or -use the item. The option must be selected from the right-click popup menu instead.# -3155 If enabled, hovering over an item in the inventory or hotbar panels and pressing the -keyboard keys 0-9 will send that item to the numbered hotbar slot. If hovered over the -hotbar, items will automatically swap positions if the specified slot is filled.# -3156 Keeps the right-side skills/party HUD pages open when the inventory is closed. Locking -the party page is helpful when keeping track of follower or player ally health. Can be -locked/unlocked in-game by left clicking the lock symbol on the HUD page.# -3157 Always shows the in-game timer when the inventory is closed in the upper right-hand section of -the screen. Useful to track speedrun times, when the minotaur will arrive, or how long your -current food situation will last.# - -3158 mute audio when game window focus is lost# -3159 Show Numerical Skill Values# - -3160 Race:# -3161 human# -3162 skeleton# -3163 vampire# -3164 succubus# -3165 goatman# -3166 automaton# -3167 incubus# -3168 goblin# -3169 insectoid# - -3175 Use left click or press left/right to toggle between active customization type.# - -3176 Gameplay Options:# -3177 use %s abilities# -3178 aesthetic skin only# +3146 DEPRECATED# +3147 DEPRECATED# +3148 DEPRECATED# +3149 DEPRECATED# +3150 DEPRECATED# +3151 DEPRECATED# +3152 DEPRECATED# +3153 DEPRECATED# +3154 DEPRECATED# +3155 DEPRECATED# +3156 DEPRECATED# +3157 DEPRECATED# +3158 DEPRECATED# +3159 DEPRECATED# +3160 DEPRECATED# +3161 DEPRECATED# +3162 DEPRECATED# +3163 DEPRECATED# +3164 DEPRECATED# +3165 DEPRECATED# +3166 DEPRECATED# +3167 DEPRECATED# +3168 DEPRECATED# +3169 DEPRECATED# + +3175 DEPRECATED# +3176 DEPRECATED# +3177 DEPRECATED# +3178 DEPRECATED# 3179 Skeletons have no use for food!# 3180 But wait! You expend your mana into new life!# @@ -4140,19 +3545,18 @@ current food situation will last.# 3197 Your summoned %s gained a new rank!# 3198 But wait! Your summoned allies imbue you with new life!# -3199 Locked - Myths & Outcasts DLC Required!# -3200 Locked - Myths & Outcasts DLC Required! -Left click to view on the store page.# +3199 DEPRECATED# +3200 DEPRECATED# 3201 Bleh! Your body rejects the solids in your stomach!# 3202 What a horrible night to have a curse...# 3203 Blecch! This is disgusting!# -3204 Unarmed# +3204 DEPRECATED# 3205 %s is stunned from your blow!# 3206 The %s is stunned from your blow!# 3207 You are stunned from %s's blow!# 3208 You are stunned from the %s's blow!# -3209 %+3d from skill# +3209 DEPRECATED# 3210 Your blade cuts deep into %s!# 3211 Your blade cuts deep into the %s!# 3212 %s's blade cuts deep into you!# @@ -4180,34 +3584,14 @@ Left click to view on the store page.# 3229 shaman# 3230 hunter# -3231 A frail but adept spellcaster able to conjure allies with mana. - -Difficulty: Normal# -3232 The Accursed suffer from bestial hunger, but gain supernatural magic power and speed. -An arcane library found deep within the dungeon may have a cure. -Difficulty: Hard# -3233 The Mesmer uses the Charm spell and leadership ability to enlist powerful allies. - -Difficulty: Hard# -3234 A talented alchemist who is also comfortable with the relationships and bar-room -brawls that a good brew will bring. -Difficulty: Normal# - -3235 A skilled craftsman, the Mechanist uses a toolkit to make and maintain mechanical -weapons, letting contraptions do the dirty work. -Difficulty: Normal# - -3236 The Punisher picks unfair fights, toying with foes using dark magic and a whip before -making the execution, or releasing one's inner demons to do the job. -Difficulty: Very Hard# - -3237 The Shaman is a mystic whose connection to nature spirits allows them to shapeshift -into bestial forms. Each form's talents provide diverse advantages in the dungeon. -Difficulty: Normal# - -3238 Equipped to track and bring down foes from afar, the Hunter uses special arrows and a -magic boomerang to ensure they never have to fight toe-to-toe. -Difficulty: Hard# +3231 DEPRECATED# +3232 DEPRECATED# +3233 DEPRECATED# +3234 DEPRECATED# +3235 DEPRECATED# +3236 DEPRECATED# +3237 DEPRECATED# +3238 DEPRECATED# 3239 Skill number invalid!# 3240 Your appraisal is not high enough to identify @@ -4228,186 +3612,38 @@ Difficulty: Hard# 3254 goes for a swim in some water.# # Skill tooltips -3255 -Chance to lockpick chests and doors: %5.1f%% -Scrap chance from unlocking chests: %5.1f%% -Scrap chance from lockpicking Automatons: %5.1f%% -Chance to disarm arrow traps: %5.1f%% -Chance for bonus salvaged scrap: %5.1f%% -Able to repair item with value (metal/magic): %4s -Maximum deployed tinkering allies: %5.0f -Available robot ally commands shown on NPC menu [%s] # -3256 -Player current visibility: %2.0f tile(s) -Visibility reduction from skill:%3.0f%% -Night vision when sneaking: +%2.0f Light -Backstab bonus damage: +%2.0f ATK# -3257 -Item buy price from shop: %3.2fx gold value -Item sell price to shop: %3.2fx gold value # -3258 -Time to appraise per item gold value: %3.2fs -Max item gold value that can be appraised: %4.0fG -Can appraise worthless glass: %3s # -3259 -Player movement speed in water/lava: %3.1f%% -# -3260 -Maximum followers (via [%-11s] recruiting): %2.0f -Bonus movement speed for followers: +%2.0f DEX -Charm Monster spell base chance: %3.0f%% -Available follower commands shown on NPC menu [%s]# -3261 -MP Regeneration base time: 1 MP / %1.1fs -Spellcasting beginner: %3s -(Beginners fumble spells and expend more mana)# -3262 -Highest skill level magic that can be learnt: %8s -Current tier spells: -# -3263 -Ranged damage output: %3.0f - 100%% -Ranged weapon degrade chance on use: %5.2f%% -Armor piercing arrow chance: %3.0f%% - (Piercing based on PER only) -Thrown weapon damage: %3.0f%%# -3264 -Sword damage output: %7.0f - 100%% -Normal sword usage degrade chance: %1.2f%% -Degrade chance when 0 damage is dealt: %5.2f%%# -3265 -Mace damage output: %7.0f - 100%% -Normal mace usage degrade chance: %1.2f%% -Degrade chance when 0 damage is dealt: %5.2f%%# -3266 -Axe damage output: %7.0f - 100%% -Normal axe usage degrade chance: %1.2f%% -Degrade chance when 0 damage is dealt: %5.2f%%# -3267 -Polearm damage output: %7.0f - 100%% -Normal polearm usage degrade chance: %1.2f%% -Degrade chance when 0 damage is dealt: %5.2f%%# -3268 -Defending armor increase: +%3.0f AC -Shield normal degrade chance on hit: %5.2f%% -Shield degrade chance when defending: %5.2f%%# -3269 -Unarmed damage output: %7.0f - 100%% -Melee glove normal degrade chance: %1.2f%% -Degrade chance when 0 damage is dealt: %5.2f%% -Bonus unarmed ATK: +%2.0f ATK -Knockback distance on fully charged strike: %3.0f%% - (Requires melee gloves equipped)# +3255 DEPRECATED# +3256 DEPRECATED# +3257 DEPRECATED# +3258 DEPRECATED# +3259 DEPRECATED# +3260 DEPRECATED# +3261 DEPRECATED# +3262 DEPRECATED# +3263 DEPRECATED# +3264 DEPRECATED# +3265 DEPRECATED# +3266 DEPRECATED# +3267 DEPRECATED# +3268 DEPRECATED# +3269 DEPRECATED# # Additional skill tooltip text -3270 - - - - - - - - -Legendary: - Bonus %d gold or an item when lockpicking chests - Able to repair any item using the tinkering kit# -3271 - - - - -Legendary: - Sneaking (using defend key without a shield) - grants invisibility. - Backstab bonus damage is doubled# -3272 - - -Legendary: - Sell item of any kind to any shop type# -3273 - - - -Legendary: - Appraise all items instantly# -3274 - -Legendary: - Permanent water-walking over water and lava# -3275 - - - - -Legendary: - Can recruit goblins, goatmen and insectoids# -3276 - - - -Legendary: - Auto learn [Forcebolt] spell, mana cost is 0# -3277 - - -Legendary: - Auto learn [Dominate] spell# -3278 - - - -Legendary: - +5 ATK and inflict 5s [bleeding] status - on fully charged strike. - (Targets invulnerable to bleeding take - +5 additional ATK)# -3279 - - - -Legendary: - +5 ATK and 25%% chance to inflict 1.5s - [paralyze] status on fully charged strike# -3280 - - - -Legendary: - +10 ATK and inflict 3s [slow] status - on fully charged strike# -3281 - - - -Legendary: - +5 ATK and inflict [knockback] status - on fully charged strike# -3282 - - - - - - -Legendary: - +5 ATK and inflict 1s [paralyze] status - on fully charged stealth backstab strike# -3283 - - - -Legendary: - Shields never degrade on hit# -3284 - - - - - -Legendary: - Ranged weapons never degrade on use# +3270 DEPRECATED# +3271 DEPRECATED# +3272 DEPRECATED# +3273 DEPRECATED# +3274 DEPRECATED# +3275 DEPRECATED# +3276 DEPRECATED# +3277 DEPRECATED# +3278 DEPRECATED# +3279 DEPRECATED# +3280 DEPRECATED# +3281 DEPRECATED# +3282 DEPRECATED# +3283 DEPRECATED# +3284 DEPRECATED# 3285 The ZAP Brigade has arrived! "The minotaur is coming! Let us slay any other foul beasts here as well!"# @@ -4415,106 +3651,67 @@ Legendary: 3287 This %s is unable to be repaired.# 3288 You have no items able to be repaired!# -3289 Deals %d magic damage# -3290 Deals %d magic damage - and inflicts [%s] status# -3291 Deals %d magic damage and inflicts - [%s] and [%s] status# -3292 Inflicts [%s] status# -3293 Deals %d magic damage - and inflicts [%s] status# -3294 slow# -3295 burning# -3296 paralyze# -3297 bleeding# -3298 sleep# -3299 confuse# -3300 poison# -3301 Type: Area# -3302 Type: Sustained# -3303 Type: Projectile# -3304 Type: Short Projectile (x3)# -3305 Type: Self# -3306 Type: Point Target# -3307 Restores %d HP to caster - and nearby allies# -3308 Grants magic reflection while sustained -Magic damage reflected is subtracted from caster's MP# -3309 Grants levitation while sustained# -3310 Grants invisibility while sustained# -3311 Grants light while sustained# -3312 Removes a curse from an item# -3313 Identifies an item# -3314 Reveals the level layout on - the current dungeon floor# -3315 Teleports caster to a random location# -3316 Opens doors, gates and locked containers# -3317 Locks doors and chests# -3318 Removes status effects from - caster and nearby allies# -3319 Breaks down a wall# -3320 Summons a skeleton ally that can be recalled -Expert Magic skill summons a second skeleton -Summon level is kept between casts# -3321 Dominates a target into servitude -MP Cost is based on the target's current HP -Caster's HP is consumed if spell cost is - greater than the caster's MP# -3322 Steals the weapon of a target and - adds it to the caster's inventory# -3323 Deals %d magic damage to HP and MP -HP/MP damage inflicted is restored to the caster# -3324 Grants unarmed attacks a chance to inflict - [lifesteal] and [bleeding] while sustained -HP regeneration is paused -Movement speed and hunger depletion is increased# -3325 Cost: %d MP (1 MP / %1.2fs)# -3326 Attempts to charm a monster into servitude -Only 1 monster may be charmed at one time -Inflicts [pacify] status on failure# +3289 DEPRECATED# +3290 DEPRECATED# +3291 DEPRECATED# +3292 DEPRECATED# +3293 DEPRECATED# +3294 DEPRECATED# +3295 DEPRECATED# +3296 DEPRECATED# +3297 DEPRECATED# +3298 DEPRECATED# +3299 DEPRECATED# +3300 DEPRECATED# +3301 DEPRECATED# +3302 DEPRECATED# +3303 DEPRECATED# +3304 DEPRECATED# +3305 DEPRECATED# +3306 DEPRECATED# +3307 DEPRECATED# +3308 DEPRECATED# +3309 DEPRECATED# +3310 DEPRECATED# +3311 DEPRECATED# +3312 DEPRECATED# +3313 DEPRECATED# +3314 DEPRECATED# +3315 DEPRECATED# +3316 DEPRECATED# +3317 DEPRECATED# +3318 DEPRECATED# +3319 DEPRECATED# +3320 DEPRECATED# +3321 DEPRECATED# +3322 DEPRECATED# +3323 DEPRECATED# +3324 DEPRECATED# +3325 DEPRECATED# +3326 DEPRECATED# 3327 You are unable to charm any more monsters into your service!# -3328 Alchemy: Choose a base potion# -3329 Alchemy: Choose a potion to mix with# -3330 a %+2d # -3331 %d %+2d # +3328 DEPRECATED# +3329 DEPRECATED# +3330 DEPRECATED# +3331 DEPRECATED# 3332 You combine the %s with a %s...# 3333 You combine the unidentified potion with a %s...# 3334 You combine the %s with the unidentified potion...# 3335 You combine the 2 unidentified potions...# 3336 The empty flask looks ideal for tapping liquids into.# 3337 You have no familiar potions to combine!# -3338 You have no familiar base potions to start mixing!# +3338 DEPRECATED# 3339 Brew# -3340 Alchemy# +3340 DEPRECATED# 3341 Stop Brewing# 3342 You have no potions left to mix!# -3343 You have no potions to mix!# -3344 Experimenting Alchemy: Choose a base potion# -3345 Experimenting Alchemy: Choose a potion to mix with# +3343 DEPRECATED# +3344 DEPRECATED# +3345 DEPRECATED# 3346 You learn about the %s base alchemy ingredient.# -3347 - - - - - - - - -Legendary: - Brewed potions have +2 blessing - Alembics never degrade# -3348 -Thrown potion impact damage: %3.0f%% -Potion effect damage: %3.0f%% -Water brewing duplication chance: %3.0f%% -Empty bottle %% on consume: %4.0f%% - (Plain or bubbly potions only) -Empty bottle %% on brew: %4.0f%% -Learnt alchemy ingredients: - Base Secondary -# +3347 DEPRECATED# +3348 DEPRECATED# 3349 You learn about the %s secondary alchemy ingredient.# 3350 blew the brew.# @@ -4525,268 +3722,60 @@ Learnt alchemy ingredients: 3355 Your strength returns back to normal.# 3356 The brew dissolves into water...# -3357 status effect icons# +3357 DEPRECATED# 3358 You feel energized?# -3359 the Triumphant %s!# -3360 the Eternal %s!# -3361 the %s Baron!# -3362 the %s %s.# -3363 Bony# -3364 Bucktooth# -3365 Bombshell# -3366 Bleating# -3367 Boilerplate# -3368 Bad Boy# -3369 Bayou# -3370 Buggar# - -3371 mute player monster sounds# -3372 Locked - Legends & Pariahs DLC Required!# -3373 Show Race Info# -3374 Hide Race Info# -3375 Human - -Friendly with: -Humans, Automatons - -Weaknesses: -None - -Resistances: -None - -Traits: -None# - -3376 Skeleton - -Friendly with: -Ghouls, Automatons - -Weaknesses: -Maces, polearms, unarmed, - blessed water - -Resistances: -Swords, ranged, axes, magic - -Traits: -- Cannot starve, or eat - (Potions still apply as normal) -- Self-resurrection at 75 MP -- 25% HP/MP regeneration rate -- 50% water movement speed -- Immune to burning# - -3377 Vampire - -Friendly with: -Vampires, Automatons - -Weaknesses: -Maces, polearms, water, solid foods - -Resistances: -Swords, ranged, axes, magic - -Traits: -- Converts HP to MP when casting - spells at 0 MP -- Starts with Bloodletting and - Levitation spells learnt -- Can only consume vials of blood to - sustain hunger -- Vials of blood drop on kills - (Higher chance if bleeding or - landing sneak attacks)# - -3378 Succubus - -Friendly with: -Succubi, Incubi, Automatons - -Weaknesses: -Swords - -Resistances: -Magic, polearms - -Traits: -- Cursed equipment is removable - and provides bonuses each curse -- Blessed gear is unremovable - (Still provides bonuses) -- Starts with Teleportation spell - learnt -- Can recruit drunk/confused humans# - -3379 Goatman - -Friendly with: -Goatmen, Automatons - -Weaknesses: -Magic, axes, polearms, ranged - -Resistances: -Swords - -Traits: -- Fountains have a chance to drop - 1-3 random potions on use -- Increased STR/CHR while drunk - (Immune to drunk dizziness) -- Suffers intermittent hangovers that - can be cured by intoxication -- Can eat tin cans without an opener -- Can recruit humans/goblins by - throwing booze while drunk# - -3380 Automaton - -Friendly with: -Humans, Automatons - -Weaknesses: -Maces, axes, magic - -Resistances: -Ranged, unarmed - -Traits: -- Require Heat (HT) to survive and - cast spells -- Consuming gemstones, scrolls, - non-spell books and scrap adds HT -- Cursed items can be unequipped -- Neutral to Incubi and Succubi -- Immune to burning -- Bonus +20 Tinkering to repairs -- Starts with Salvage spell learnt# - -3381 Incubus - -Friendly with: -Incubi, Succubi, Automatons - -Weaknesses: -Ranged, swords - -Resistances: -Magic, polearms - -Traits: -- Cursed equipment is removable - and provides bonuses each curse -- Blessed gear is unremovable - (Still provides bonuses) -- Starts with Teleportation and - Arcane Mark spells learnt# - -3382 Goblin - -Friendly with: -Goblins, Automatons - -Weaknesses: -Axes, polearms, ranged - -Resistances: -Unarmed, swords - -Traits: -- All worn equipment has a - reduced chance to degrade -- Increasing a melee weapon skill - also increases other melee skills - at the same time -- Weapon skills are slower to improve -- Unable to permanently learn - magic spells from spellbooks - (Can still cast with book in hand)# - -3383 Insectoid - -Friendly with: -Insectoids, scarabs, scorpions - -Weaknesses: -Axes, polearms, ranged - -Resistances: -Maces, unarmed - -Traits: -- Require Energy (EN) to survive and - cast spells -- Energy is regained through eating - and consuming sweet liquids -- Immune to poison -- Immune to sickness when eating - spoiled food -- Starts with Flutter, Dash - and Spray Acid spells learnt# - -3384 Slowed -Reduced movement speed# -3385 Bleeding -Taking damage over time# -3386 Asleep -Unable to move or see# -3387 Confused -Movement is reversed# -3388 Pacified -Unable to attack# -3389 Vampiric Aura -+ Strength and Dexterity -Increased hunger rate -Increased chance to inflict - [bleed] status while unarmed -Chance to lifesteal on unarmed - lethal stealth attacks# -3390 Vampiric Aura -+ Strength and Dexterity -Increased hunger rate -Increased chance to inflict - [bleed] status while unarmed -Chance to lifesteal on all - unarmed attacks -Natural HP regen is disabled# -3391 Paralyzed - Unable to move# -3392 Drunk -+ Strength -- Dexterity# -3393 Hungover -- Dexterity -Curable by intoxication# -3394 Strength -+ Strength -- Perception# -3395 Levitating -Able to float over chasms and - water/lava# -3396 Invisible -Unable to be detected by enemies - outside of melee range# -3397 Reflect Magic -Enemy spells are reflected -Magic damage reflected is consumed - from MP# -3398 Light -Sustaining a magical source of light# -3399 Polymorphed -Disguised as another monster# +3359 DEPRECATED# +3360 DEPRECATED# +3361 DEPRECATED# +3362 DEPRECATED# +3363 DEPRECATED# +3364 DEPRECATED# +3365 DEPRECATED# +3366 DEPRECATED# +3367 DEPRECATED# +3368 DEPRECATED# +3369 DEPRECATED# +3370 DEPRECATED# + +3371 DEPRECATED# +3372 DEPRECATED# +3373 DEPRECATED# +3374 DEPRECATED# +3375 DEPRECATED# + +3376 DEPRECATED# +3377 DEPRECATED# +3378 DEPRECATED# +3379 DEPRECATED# +3380 DEPRECATED# +3381 DEPRECATED# +3382 DEPRECATED# +3383 DEPRECATED# + +3384 DEPRECATED# +3385 DEPRECATED# +3386 DEPRECATED# +3387 DEPRECATED# +3388 DEPRECATED# +3389 DEPRECATED# +3390 DEPRECATED# +3391 DEPRECATED# +3392 DEPRECATED# +3393 DEPRECATED# +3394 DEPRECATED# +3395 DEPRECATED# +3396 DEPRECATED# +3397 DEPRECATED# +3398 DEPRECATED# +3399 DEPRECATED# 3400 Expending your vitality weakened the spell!# 3401 You sense the gods of Sokoban aiding your path...# -3402 Enter DLC serial key# -3403 Type in your serial key (case sensitive) and press submit:# -3404 Success! Myths and Outcasts DLC is now available. -Thank you for your support!# -3405 Success! Legends and Pariahs DLC is now available. -Thank you for your support!# -3406 ERROR: Invalid serial key.# +3402 DEPRECATED# +3403 DEPRECATED# +3404 DEPRECATED# +3405 DEPRECATED# +3406 DEPRECATED# 3407 DEPRECATED_SPELL_NAME# 3408 DEPRECATED_SPELL_NAME# @@ -4800,7 +3789,7 @@ Thank you for your support!# 3416 DEPRECATED_SPELL_NAME# 3417 Your shapeshift fades and you return to your normal form.# -3418 The shapeshift is washed away by the water!# +3418 DEPRECATED# 3419 You shapeshift into a %s!# 3420 You feel no different...# 3421 DEPRECATED_SPELL_NAME# @@ -4815,9 +3804,7 @@ Thank you for your support!# 3430 The %s is entangled in a web!# 3431 You are entangled in a web!# 3432 You are unable to use that in your current form!# -3433 Shapeshifted -Transformed into a creature - with special abilities# +3433 DEPRECATED# 3434 The %s cowers in fear!# 3435 %s cowers in fear!# 3436 You cower in fear!# @@ -4842,7 +3829,7 @@ Transformed into a creature 3455 Your whip strikes the weapon of %s!# 3456 Your whip strikes the shield of the %s!# 3457 Your whip strikes the shield of %s!# -3458 %+3d from attr. (50%% STR+DEX)# +3458 DEPRECATED# 3459 Your %s crumbles to ash!# 3460 You can't study a spellbook while it's equipped!# 3461 A magical force relocates you!# @@ -4876,10 +3863,8 @@ Transformed into a creature 3489 DEPRECATED_SPELL_NAME# 3490 Your veins feel warm!# 3491 Your bloodflow returns to normal.# -3492 Troll's Blood -HP Regeneration is increased# -3493 Speed -Increased Dexterity and movement speed# +3492 DEPRECATED# +3493 DEPRECATED# 3494 You hear a trap trigger in the distance.# 3495 You hear a trap trigger nearby!# 3496 stumbles into a trap and dies.# @@ -4998,39 +3983,37 @@ Increased Dexterity and movement speed# 3616 Your trap failed to teleport %s!# 3617 Your %s destroys the %s!# 3618 Your %s hits the %s!# -3619 Light:# -3620 [Off]# -3621 [Faint]# -3622 [Bright]# -3623 Detect:# -3624 [None]# -3625 [Metal]# -3626 [Magic]# -3627 [Traps]# -3628 [Exits]# -3629 [Monsters]# -3630 [Valuable]# +3619 DEPRECATED# +3620 DEPRECATED# +3621 DEPRECATED# +3622 DEPRECATED# +3623 DEPRECATED# +3624 DEPRECATED# +3625 DEPRECATED# +3626 DEPRECATED# +3627 DEPRECATED# +3628 DEPRECATED# +3629 DEPRECATED# +3630 DEPRECATED# 3631 returns to the ground.# -3632 Free roam# -3633 Drop# +3632 DEPRECATED# +3633 DEPRECATED# 3634 item# -3635 Return & - Land# -3636 Detect -items:# +3635 DEPRECATED# +3636 DEPRECATED# 3637 Your gyrobot can't carry any more items!# 3638 Your Leadership skill is not high enough to use the previous command on the %s.# 3639 Your Tinkering skill is not high enough to use the previous command on the %s.# 3640 Your previous command cannot be used on the %s.# -3641 Return to# -3642 Box# +3641 DEPRECATED# +3642 DEPRECATED# 3643 returns to its box.# 3644 Craft# 3645 Salvage# 3646 Repair# -3647 Scrap available: %3d %3d# +3647 DEPRECATED# 3648 You do not have enough materials to craft a %s!# -3649 Deactivate# +3649 DEPRECATED# 3650 Look at...# 3651 Your gyrobot retrieved some allies left behind!# 3652 Your Tinkering skill is not high enough to craft a %s!# @@ -5053,11 +4036,10 @@ items:# 3669 You can't salvage equipped items!# 3670 Tinker# 3671 Your gyrobot detects curious footsteps towards your noisemaker!# -3672 You do not meet the Tinkering and Perception requirements -for this option. (Required: %s, Current: %s)# +3672 DEPRECATED# 3673 Your %s needs to be higher quality to use this command.# -3674 Hold aim# -3675 Free look# +3674 DEPRECATED# +3675 DEPRECATED# 3676 The %s locks its rotor.# 3677 The %s returns to its patrol.# 3678 Your %s needs to be higher quality detect more object types.# @@ -5074,12 +4056,12 @@ any gyrobot findings.# 3687 You do not have enough materials to upgrade the %s!# 3688 Your Tinkering skill is not high enough to repair the %s!# 3689 You have nothing else to learn from unlocking the %s.# -3690 Tinkering# -3691 Kit: %10s# -3692 Decrepit# -3693 Worn# -3694 Servicable# -3695 Excellent# +3690 DEPRECATED# +3691 DEPRECATED# +3692 DEPRECATED# +3693 DEPRECATED# +3694 DEPRECATED# +3695 DEPRECATED# 3696 Your boiler is burning steady.# 3697 But it fails to produce any more heat.# 3698 You see your reflection.# @@ -5100,14 +4082,13 @@ any gyrobot findings.# 3713 Your spell fails to salvage any nearby items.# 3714 Your systems are failing...# 3715 The spell had no effect!# -3716 Scribing# -3717 Charge: %8d%%%%# +3716 DEPRECATED# +3717 DEPRECATED# 3718 Inscribe# 3719 Repair# 3720 Select a blank scroll to inscribe# -3721 Write %s on the scroll# -3722 Inscribing a -%+2d %s# +3721 DEPRECATED# +3722 DEPRECATED# 3723 Clear Slot# 3724 Inscribed %s!# 3725 You trace over some of the faded text...# @@ -5117,8 +4098,8 @@ any gyrobot findings.# the inscription!# 3729 Your %s begins to fade.# 3730 Your %s gets charged up!# -3731 You have no items able to be charged!# -3732 # +3731 DEPRECATED# +3732 DEPRECATED# 3733 Your shot glances off the %s!# 3734 Your shot glances off %s!# 3735 The %s's demons have already been exorcised!# @@ -5152,649 +4133,112 @@ any gyrobot findings.# 3763 a # 3764 DEPRECATED_SPELL_NAME# 3765 DEPRECATED_SPELL_NAME# -3766 Flutter -Levitating for a short duration# +3766 DEPRECATED# 3767 Your wings part your feet with the floor!# -3768 EN# +3768 DEPRECATED# 3769 Your noisemaker needs rewinding!# 3770 Your noisemaker breaks!# -3771 You arrive outside the Hamlet mines and watch the cursed blackness peel away -from the land. Warm sunlight flows over the countryside. As the doors of the -mine open wide, people carefully spread into the streets, acclimating to -their freedom.# -3772 - - - - - -You rush to nearby buildings, looking for a place to hide and figure out your -next move. There are too many humans and the sun is too bright for your liking.# -3773 - - - - - - - - -You break your way into what appears to be an inn. You spend the night under -the sheets of a comfortable bed while the sounds of revelry surround you.# -3774 - - - - - - - - - - - - - -Luckily you avoid detection. As humans party through the hostel you hear of -a power even greater than Herx's, said to be held in the Magician's Guild.# -3775 - - - - - - - - - - - - - - - - - - -A trapdoor in the bunkroom leads to a sewer. You wait for the humans to leave.# - -3776 Hacking your way through every demon in your path, you leap through the -portal without a moment's hesitation. No entity, mortal or immortal, can -challenge your power.# -3777 - - - -Suddenly, you find yourself standing wearily in an endless white hall dotted -with enormous pillars that stretch higher than you can see. The smell of -blood is thick on your breath, but you suspect more will need to be spilled.# -3778 - - - - - - - - -Can this supernatural place compete with Minotaurs, Demons; Baphomet himself? -You sense a great magic flow into the room. You are unnerved. A voice booms:# -3779 - - - - - - - - - - - -"We have witnessed your strength and lust for power. You were not expected here. -We are however impressed by your ferocity and focus. We ask you consider this -proposition, mighty one..."# -3780 - - - - - - - - - - - - - - - - -As the voice addresses you, you find you are tempted by its words. It speaks -of a power greater than Herx, or Baphomet. It claims that it is greater than -you. The prospect of a competitor shall not stand.# -3781 - - - - - - - - - - - - - - - - - - - - -You find yourself standing inside a Hamlet inn, its denizens too drunk or -weary to notice you at first. You steal yourself into a backroom; a trapdoor -leads to a sewer. A great magic from beyond there beckons you.# - -3782 Hacking your way through every demon in your path, you leap through the -portal without a moment's hesitation. No entity, mortal or immortal, can -challenge your power.# -3783 - - - -Suddenly, you find yourself standing wearily in an endless white hall dotted -with enormous pillars that stretch higher than you can see. Have you not -defeated the source of Herx's vile power? You wonder what danger now awaits.# -3784 - - - - - - - - -A sense of peace overwhelms and refreshes you. You know that Herx's power has -been removed. But Baphomet was just one devil. A voice comforts you:# -3785 - - - - - - - - - - - -"The lich Herx has become dust, but cleverer foes draw upon hellish evil. -Hamlet is free for now, but the world is not safe. In times such as these -a mighty one must tip the scales. We have a proposition for you..."# -3786 - - - - - - - - - - - - - - - - -As the voice addresses you, you find yourself either unwilling or unable to -reject what it tells you. You find your physical form shifting back into the -mortal realm. "You must sever evil from Hamlet or there will be no peace."# -3787 - - - - - - - - - - - - - - - - - - - - -With renewed courage, you find yourself standing in Hamlet. Evidence of a -massive party flows from the gates of the mines, now open, to a nearby hostel. -You know the Magician's Guild will aid you in your quest.# - -3788 Victorious, you step into the portal. You know not what to expect, but -are awash with delight. You expect that the source of the Baron's power -is now within your reach.# - -3789 - - - - -Before you have time to think, you are suddenly standing before the mines -once more. But something is different; the seductive power you sensed here -is further away. The black skies are clearing to show the light of day.# - -3790 - - - - - - - - - -Suddenly, the doors to the mine open wide. Sealed for centuries to contain -the evil within, they swing out from the inside, as a hundred grateful humans -rush out. It appears the Baron's power slipped through your grasp.# - -3791 - - - - - - - - - - - - - - -The hellish source of that dark power remains a mystery to you, and some -eavedropping from the shadows leads you to believe that the Magician's Guild -may have a lead on some dark magic. For now, you resolve to leave this land -and return another time to exploit the secrets of Hamlet.# - -3792 Hacking your way through every demon in your path, you leap through the -portal without a moment's hesitation. It seems no entity, mortal or -immortal, can challenge your power.# -3793 - - - -Suddenly, you find yourself standing wearily in an endless white hall dotted -with enormous pillars that stretch higher than you can see. The smell of -blood is thick on your breath, but you suspect more will need to be spilled.# -3794 - - - - - - - - -Can this supernatural place compete with Minotaurs, Demons; Baphomet himself? -You sense a great magic flow into the room. You are unnerved. A voice booms:# -3795 - - - - - - - - - - - -"We have witnessed your strength and lust for power. You were not expected here. -We are however impressed by your ferocity and focus. We ask you consider this -proposition, mighty one..."# -3796 - - - - - - - - - - - - - - - - -As the voice addresses you, you find yourself either unwilling or unable to -reject what it tells you. Eventually, there is an inexplicable shift in your -physical being, and you feel yourself fading away...# -3797 - - - - - - - - - - - - - - - - - - - - -You sense the supreme power of a great mind as you fade from the mortal world. -The first age comes to a close, and the age of chaos begins. You are sure that -your time is emminent and your purpose will be fulfilled.# - -3798 Through the portal, a weak magic tugs you from the Citadel. Your feet -hover over the floor as it shakes violently. Pipes and pistons crash down -in a muffled racket. Your inputs are dulled and you observe the transferrence -of your consciousness.# -3799 - - - - - - - -It seems demonic evil has been destroyed, at least, as far as Hamlet knows.# -3800 - - - - - - - - - - - - -You consider its citizens are lucky to have you. They could not have survived -without you, but alas, you would never have been invented without them.# -3801 Your systems are nominal. You stand with a wizard and bishop. Their -faces open into expressions of surprise.# -3802 - - - -They are shocked to see that a humble slave, a servant... an automaton has -rescued them from supernatural evil. Is your inhuman nature the reason?# -3803 - - - - - - - - -The guilds and the churches agree, there is no more perfect arbiter for the -well being of humans. You are entrusted with the tasks of law, assumed to -have perfect objectivity as the new Baron. Your rulings are infallible.# -3804 - - - - - - - - - - - - - -But is that so? What is it that makes you conscious? You lead Hamlet to -its former glory. But you think some problems may require more power.# -3805 Through the portal, a weak magic tugs you from the Citadel. Your feet -hover over the floor as it shakes violently. Pipes and pistons crash down -in a muffled racket. You clench your eyes knowing magic holds you tight. -You grimly await what it offers.# -3806 - - - - - - - -If the liches gained their powers from devils, what does that make you -who ended them? Is it now yours to bear? What use was it to them?# -3807 - - - - - - - - - - - - -It seems you got what you came here for, though not in the way you -anticipated. Power cannot just be held, it must be used.# -3808 You open your eyes and find yourself standing with two mortals. -They cower in your presence. You command them to bow before you.# -3809 - - - -Through demonic magic, you bind their souls hostage in servitude, using -their influence to bend factions to your secret purpose.# -3810 - - - - - - - - -The following years are not easy for the surface world. Eventually they learn -that joy and glory is found in strength. You slaughter all who oppose you. -Eventually they embrace your nature. The humans bear your banner.# -3811 - - - - - - - - - - - - - -Can any empire challenge what you've built? Prophets teach that the -human spirit is stronger than anything. You fear that power is real.# - -3812 Through the portal, a weak magic tugs you from the Citadel. Your feet -hover over the floor as it shakes violently. Pipes and pistons crash down -in a muffled racket. You try to shake your terror and close your eyes.# -3813 - - - - - - - -What have you to be afraid of? You have defeated mighty, evil masters. -Who can challenge your people now?# -3814 - - - - - - - - - - - - -You fear nothing. No, it is others who should fear you. A moment of clarity -causes you to wonder if fear is what you want from others.# - -3815 You open your eyes, standing in The Magician's Guild with a wizard and bishop. -Their faces are confused, but they await your reaction with patient wonder.# -3816 - - - -You suspect things may've gone differently if wiser men had not awaited you. -They recognized your heroism and offered gratitude for your deeds. Are these -the same people who murdered your kind so recently?# -3817 - - - - - - - - - -Though shaky at first, your peoples build an alliance toward peace.# -3818 - - - - - - - - - - - - - -Through your example, insectoids, humans, goatmen and goblins embrace courage -instead of fear. Trade prospers in Hamlet as people belay their lust for power.# -3819 %s's Epilogue: %s# -3820 %s's Interlude: %s# -3821 Human# -3822 Skeleton# -3823 Vampire# -3824 Succubus# -3825 Goatman# -3826 Automaton# -3827 Incubus# -3828 Goblin# -3829 Insectoid# -3830 Epilogue: %s# -3831 Interlude: %s# -3832 Now advance when you and your allies are ready!# -3833 Now wait for your leader to advance onward...# +3771 DEPRECATED# +3772 DEPRECATED# +3773 DEPRECATED# +3774 DEPRECATED# +3775 DEPRECATED# + +3776 DEPRECATED# +3777 DEPRECATED# +3778 DEPRECATED# +3779 DEPRECATED# +3780 DEPRECATED# +3781 DEPRECATED# + +3782 DEPRECATED# +3783 DEPRECATED# +3784 DEPRECATED# +3785 DEPRECATED# +3786 DEPRECATED# +3787 DEPRECATED# + +3788 DEPRECATED# + +3789 DEPRECATED# + +3790 DEPRECATED# + +3791 DEPRECATED# + +3792 DEPRECATED# +3793 DEPRECATED# +3794 DEPRECATED# +3795 DEPRECATED# +3796 DEPRECATED# +3797 DEPRECATED# + +3798 DEPRECATED# +3799 DEPRECATED# +3800 DEPRECATED# +3802 DEPRECATED# +3803 DEPRECATED# +3804 DEPRECATED# +3806 DEPRECATED# +3807 DEPRECATED# +3809 DEPRECATED# +3810 DEPRECATED# +3811 DEPRECATED# + +3812 DEPRECATED# +3813 DEPRECATED# +3814 DEPRECATED# + +3815 DEPRECATED# +3816 DEPRECATED# +3817 DEPRECATED# +3818 DEPRECATED# +3819 DEPRECATED# +3820 DEPRECATED# +3821 DEPRECATED# +3822 DEPRECATED# +3823 DEPRECATED# +3824 DEPRECATED# +3825 DEPRECATED# +3826 DEPRECATED# +3827 DEPRECATED# +3828 DEPRECATED# +3829 DEPRECATED# +3830 DEPRECATED# +3831 DEPRECATED# +3832 DEPRECATED# +3833 DEPRECATED# # new spell tooltips -3834 Inflicts an instance of [webbed] status -Webbed targets are slowed and are knocked back - by melee strikes -Each instance (max 3x) increases the effectiveness# -3835 Grants [speed] status to caster - and nearby allies# -3836 Inflicts [fear] status to all nearby enemies -Feared targets are unable to attack and draw their - focus to the caster# -3837 Inflicts [magic weakness] status -Increases magic damage taken from other spells# -3838 Charges a powerful physical strike that - inflicts 4x ATK damage -Caster is immobile while charging -Breaks boulders, doors and walls on impact# -3839 Reveals food sources left on the current dungeon floor# -3840 Grants additional HP regeneration for a duration - to caster and nearby allies# -3841 Grants levitation for a brief duration# -3842 Propels the caster in their current moving direction -Instantly breaks doors on impact -Performs a backwards leap if stationary# -3843 Marks a target with an arcane seal -Nullifies innate damage resistances and weaknesses -Teleportation magic is refocused around marked targets# -3844 Teleports a target towards the caster -Disorients the target for a duration proportional - to the distance travelled# -3845 Exorcises an inner demon that provokes a target -Damage dealt to the demon is returned to the attacker -The caster's current stats are imbued into the demon# -3846 Transmutes nearby items on the ground into metal - and magical scrap for crafting# -3847 Shapeshift temporarily into a rat -Grants bonus DEX, INT and PER while active# -3848 Shapeshift temporarily into an arthropod -Grants bonus STR, CON and PER while active# -3849 Shapeshift temporarily into a troll -Grants bonus STR and CON, lowers DEX while active# -3850 Shapeshift temporarily into a flying imp -Grants bonus INT and PER while active# -3851 Removes active shapeshift effects from the caster# -3852 Augments Fire, Cold, Lightning and Magic Missile spells - with increased damage and reduced range while sustained# -3853 Cost: %d MP + 10%% Max HP# -3854 Rat Form -+ DEX/INT/PER -Higher chance to gain DEX/INT on level up -Access to spells [Detect Food] and [Speed]# -3855 Troll Form -+ STR/CON -- DEX -Higher chance to gain STR/CON on level up -Access to spells [Power Strike], [Fear] and [Troll's Blood]# -3856 Arthropod Form -+ PER/STR/CON -Higher chance to gain PER/CON on level up -Access to spells [Spray Web] and [Poison]# -3857 Imp Form -Provides levitation -+ INT/PER -Higher chance to gain INT/DEX on level up -Access to spells [Lightning], [Confuse] and [Elemental Focus]# -3858 Arcane Mark (Active on %s) -Nullifies damage resistances/weaknesses on target -Teleportation spells are refocused to target# -3859 Webbed -Reduced movement speed -Knocked back when receiving melee strikes# -3860 Elemental Focus -Increased damage and shorter range when casting - [Fire], [Cold], [Lightning] or [Magic Missile] spells# -3861 Fear -Unable to attack# +3834 DEPRECATED# +3835 DEPRECATED# +3836 DEPRECATED# +3837 DEPRECATED# +3838 DEPRECATED# +3839 DEPRECATED# +3840 DEPRECATED# +3841 DEPRECATED# +3842 DEPRECATED# +3843 DEPRECATED# +3844 DEPRECATED# +3845 DEPRECATED# +3846 DEPRECATED# +3847 DEPRECATED# +3848 DEPRECATED# +3849 DEPRECATED# +3850 DEPRECATED# +3851 DEPRECATED# +3852 DEPRECATED# +3853 DEPRECATED# +3854 DEPRECATED# +3855 DEPRECATED# +3856 DEPRECATED# +3857 DEPRECATED# +3858 DEPRECATED# +3859 DEPRECATED# +3860 DEPRECATED# +3861 DEPRECATED# 3862 labelled # 3863 You can't read the spellbook while blind!# @@ -5824,8 +4268,7 @@ Unable to attack# 3883 You are unable to deploy any more tinkering machines with your current skill!# 3884 You are unable to deploy any more tinkering machines.# 3885 DEPRECATED_SPELL_NAME# -3886 Polymorphs the caster into a different creature for a duration -Non-humans are always polymorphed into a human# +3886 DEPRECATED# 3887 You are not skilled enough to empty the trap.# 3888 Oh! I must pay a visit to the goldsmith with this jewel! Though I'm not sure if green is really my color.# 3889 Hmm. They say these were artificed from centuries of knowledge. Would you believe most wizards lose their marbles?# @@ -5845,15 +4288,15 @@ fine wares.# 3900 You can't place that there!# # More keybinds -3901 Hotbar Left# -3902 Hotbar Right# -3903 Hotbar Up / Select# -3904 # -3905 # -3906 # -3907 # -3908 # -3909 # +3901 DEPRECATED# +3902 DEPRECATED# +3903 DEPRECATED# +3904 DEPRECATED# +3905 DEPRECATED# +3906 DEPRECATED# +3907 DEPRECATED# +3908 DEPRECATED# +3909 DEPRECATED# 3910 Remove thy garment before we deal!# @@ -5869,16 +4312,10 @@ deal!# before we deal!# 3916 Unhand thy lunch before we deal!# -3917 Locked - Legends & Pariahs DLC Required! -Left click to view on the store page.# -3918 disable mouse rotation speed limit# -3919 Return To Main Menu# -3920 - Are you sure to want to - return to the main menu? - - Progress has been saved at the - start of the current level.# +3917 DEPRECATED# +3918 DEPRECATED# +3919 DEPRECATED# +3920 DEPRECATED# 3921 Ranger-mode active, only non-melee weapons are allowed to attack!# 3922 Ranger-mode deactivated, all weapons permitted.# 3923 This weapon is disabled in Ranger-mode!# @@ -5894,132 +4331,65 @@ Left click to view on the store page.# 3932 Unlock achievement "Bad Boy Baron" to unlock this class for every race!# 3933 Unlock achievement "Bayou Baron" to unlock this class for every race!# 3934 Unlock achievement "Buggar Baron" to unlock this class for every race!# -3935 borderless# -3936 Welcome to Barony!# -3937 Logging in, please wait# -3938 When logging in for the first time, check your browser# -3939 to enable account integration with Barony.# -3940 Account integration is required to use Barony.# -3941 A login error has occurred (%d). Please retry.# -3942 Account integration has been denied. Please restart Barony to retry.# -3943 (Steamworks)# -3944 (Crossplay)# -3945 Join a multiplayer game hosted by Steamworks or crossplay services.# -3946 Host a multiplayer game using Steamworks.# -3947 Host a multiplayer game using crossplay services.# -3948 enable crossplay# -3950 Filters # -3951 Filters in use# -3952 Filters disabled# -3953 Filtered Search# -3954 Search by lobby code:# -3955 Show in-progress -crossplay lobbies: [%c]# -3956 Are you sure you want to - restart the current trial?# -3957 Restart Trial# -3958 Return to Hall of Trials# -3959 Quit To Main Menu# -3960 Are you sure you want to - exit the current trial and - return to the Hall of Trials?# -3961 Are you sure you want to - return to the main menu?# -3962 -Changing this gameflag is disabled in current game mode.# -3963 Achievements are disabled for current gamemode# -3964 Hall of Trials# -3965 Go to the Tutorial Trials# -3966 Skip for now# -3967 Barony is a challenging game! # -3968 - Would you like to go to the Hall of Trials - to learn a few things? - - You can always go back to the Hall of Trials - later from the Main Menu.# -3969 Restart Map# -3970 Are you sure you want to - reset the Hall of Trials - map to its original state?# -3971 Achievements# -3972 Ambient Sound Volume# -3973 Environment Sound Volume# +3935 DEPRECATED# +3936 DEPRECATED# +3937 DEPRECATED# +3938 DEPRECATED# +3939 DEPRECATED# +3940 DEPRECATED# +3941 DEPRECATED# +3942 DEPRECATED# +3943 DEPRECATED# +3944 DEPRECATED# +3945 DEPRECATED# +3946 DEPRECATED# +3947 DEPRECATED# +3948 DEPRECATED# +3950 DEPRECATED# +3951 DEPRECATED# +3952 DEPRECATED# +3953 DEPRECATED# +3954 DEPRECATED# +3955 DEPRECATED# +3956 DEPRECATED# +3957 DEPRECATED# +3958 DEPRECATED# +3959 DEPRECATED# +3960 DEPRECATED# +3961 DEPRECATED# +3962 DEPRECATED# +3963 DEPRECATED# +3964 DEPRECATED# +3965 DEPRECATED# +3966 DEPRECATED# +3967 DEPRECATED# +3968 DEPRECATED# +3969 DEPRECATED# +3970 DEPRECATED# +3971 DEPRECATED# +3972 DEPRECATED# +3973 DEPRECATED# 3974 The boulder cannot be moved that way.# -3975 - - Enabling Crossplay allows joining and hosting of multiplayer lobbies - between different distributions of Barony without port forwarding. - - For more information, click "About Crossplay" below. - - - By clicking accept, you agree to share your public Steam ID - and display name with Barony and associated Crossplay services.# -3976 Accept and Enable Crossplay# -3977 Cancel Setup# -3978 View Privacy Policy# -3979 Crossplay First-Time Setup# -3980 About Crossplay - - Barony uses Epic Online Services to facilitate Crossplay, - and will create a unique Crossplay ID within Barony that - corresponds to your public facing Steam account ID. - - - Your public Steam account ID and display name will be shared with - the service and other players when using Crossplay features in Barony. - No third party account is required or created when using Crossplay. - - Click "View Privacy Policy" below to see how Barony handles your data.# -3981 - Services In Use For Crossplay - - - - - Data That You Provide# -3982 Return to Setup# -3983 About Crossplay# -3984 https://www.epicgames.com/store/en-US/product/barony/myths-and-outcasts# -3985 https://www.epicgames.com/store/en-US/product/barony/legends-and-pariahs# -3986 - Are you sure you want to disconnect - and return to the main menu? - - As the rest of your party has perished, - save data for this session has been deleted. - - NOTE: - This will disconnect all party members.# -3987 Quit To Desktop# -3988 - Are you sure you want - to quit to desktop? - - As you have perished, your - save file will be deleted.# -3989 - Are you sure you want - to quit to desktop? - - As the rest of your party has perished, - save data for this session has been deleted.# -3990 - Are you sure you - want to end this game? - - Saving on this floor is disabled, - exiting will delete your save file.# -3991 - Are you sure you - want to quit to desktop? - - Saving on this floor is disabled, - exiting will delete your save file.# -3992 https://store.steampowered.com/app/1010820/Barony_Myths__Outcasts/# -3993 https://store.steampowered.com/app/1010821/Barony_Legends__Pariahs/# -3994 Crossplay search not enabled# +3975 DEPRECATED# +3976 DEPRECATED# +3977 DEPRECATED# +3978 DEPRECATED# +3979 DEPRECATED# +3980 DEPRECATED# +3981 DEPRECATED# +3982 DEPRECATED# +3983 DEPRECATED# +3984 DEPRECATED# +3985 DEPRECATED# +3986 DEPRECATED# +3987 DEPRECATED# +3988 DEPRECATED# +3989 DEPRECATED# +3990 DEPRECATED# +3991 DEPRECATED# +3992 DEPRECATED# +3993 DEPRECATED# +3994 DEPRECATED# 3995 You have no items able to be uncursed!# 3996 You have no items able to be identified!# 3997 You have no room left in your pack to unequip a From d552cdf422e6787f56d0bb7defc4af6f94a3a06a Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Mon, 9 Oct 2023 12:29:03 +1100 Subject: [PATCH 121/146] * fix accident class name deletion --- lang/en.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lang/en.txt b/lang/en.txt index a2bc5827e..54f86bc18 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -2895,16 +2895,16 @@ last of your magic is drained!# 2549 DEPRECATED# # 2550 - 2560 new class names -2550 DEPRECATED# -2551 DEPRECATED# -2552 DEPRECATED# -2553 DEPRECATED# -2554 DEPRECATED# -2555 DEPRECATED# -2556 DEPRECATED# -2557 DEPRECATED# -2558 DEPRECATED# -2559 DEPRECATED# +2550 sexton# +2551 ninja# +2552 monk# +2553 blank# +2554 blank# +2555 blank# +2556 blank# +2557 blank# +2558 blank# +2559 blank# # 2560 - 2570 new class descriptions 2560 DEPRECATED# 2561 DEPRECATED# From f23d99eb5c120d82a0daf89da3d797763ee00a41 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Mon, 9 Oct 2023 13:51:16 +1100 Subject: [PATCH 122/146] * dont draw minimap while loading possible crash --- src/interface/drawminimap.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/interface/drawminimap.cpp b/src/interface/drawminimap.cpp index e299b2c34..fb3627074 100644 --- a/src/interface/drawminimap.cpp +++ b/src/interface/drawminimap.cpp @@ -98,6 +98,10 @@ inline real_t getMinimapZoom() void drawMinimap(const int player, SDL_Rect rect, bool drawingSharedMap) { + if ( loading ) + { + return; + } if ( gameplayCustomManager.inUse() ) { if ( CustomHelpers::isLevelPartOfSet( currentlevel, secretlevel, gameplayCustomManager.minimapDisableFloors) ) { From e933eb4a8223c73a9ce0fad6d6a96b9faf5f8b1d Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 10 Oct 2023 09:45:32 +1100 Subject: [PATCH 123/146] * increase path limit for achievement checks --- src/paths.cpp | 5 +++++ src/paths.hpp | 3 ++- src/scores.cpp | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/paths.cpp b/src/paths.cpp index 8e8fad782..ed3280978 100644 --- a/src/paths.cpp +++ b/src/paths.cpp @@ -646,6 +646,7 @@ list_t* generatePath(int x1, int y1, int x2, int y2, Entity* my, Entity* target, static ConsoleVariable cvar_pathlimit_allyfollow("/pathlimit_allyfollow", 200); static ConsoleVariable cvar_pathlimit_bosses("/pathlimit_bosses", 2000); static ConsoleVariable cvar_pathlimit_commandmove("/pathlimit_commandmove", 1000); + static ConsoleVariable cvar_pathlimit_achievement("/pathlimit_achievement", 1600); if (pathingType == GeneratePathTypes::GENERATE_PATH_IDLE_WALK || pathingType == GeneratePathTypes::GENERATE_PATH_MOVEASIDE || pathingType == GeneratePathTypes::GENERATE_PATH_MONSTER_MOVE_BACKWARDS) @@ -678,6 +679,10 @@ list_t* generatePath(int x1, int y1, int x2, int y2, Entity* my, Entity* target, { maxtries = *cvar_pathlimit_commandmove; } + else if ( pathingType == GeneratePathTypes::GENERATE_PATH_ACHIEVEMENT ) + { + maxtries = *cvar_pathlimit_achievement; + } else if ( pathingType == GeneratePathTypes::GENERATE_PATH_BOSS_TRACKING_HUNT || pathingType == GeneratePathTypes::GENERATE_PATH_BOSS_TRACKING_IDLE || (pathingType == GeneratePathTypes::GENERATE_PATH_TO_HUNT_MONSTER_TARGET diff --git a/src/paths.hpp b/src/paths.hpp index f44012dcd..17e1b38d0 100644 --- a/src/paths.hpp +++ b/src/paths.hpp @@ -40,7 +40,8 @@ enum GeneratePathTypes GENERATE_PATH_PLAYER_ALLY_MOVETO, GENERATE_PATH_PLAYER_GYRO_RETURN, GENERATE_PATH_CHECK_EXIT, - GENERATE_PATH_MOVEASIDE + GENERATE_PATH_MOVEASIDE, + GENERATE_PATH_ACHIEVEMENT }; extern int lastGeneratePathTries; list_t* generatePath(int x1, int y1, int x2, int y2, Entity* my, Entity* target, GeneratePathTypes pathingType, bool lavaIsPassable = false); diff --git a/src/scores.cpp b/src/scores.cpp index ad38579ae..5048f4d16 100644 --- a/src/scores.cpp +++ b/src/scores.cpp @@ -5108,7 +5108,7 @@ bool AchievementObserver::PlayerAchievements::checkPathBetweenObjects(Entity* pl } list_t* playerPath = generatePath((int)floor(player->x / 16), (int)floor(player->y / 16), - (int)floor(target->x / 16), (int)floor(target->y / 16), player, target, GeneratePathTypes::GENERATE_PATH_DEFAULT, true); + (int)floor(target->x / 16), (int)floor(target->y / 16), player, target, GeneratePathTypes::GENERATE_PATH_ACHIEVEMENT, true); if ( playerPath == nullptr ) { // no path. From 2700e93e0ec7018630b0d7f961d750c295613f12 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 10 Oct 2023 09:57:22 +1100 Subject: [PATCH 124/146] * fix lang file --- lang/en.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lang/en.txt b/lang/en.txt index 54f86bc18..da423c434 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -562,13 +562,13 @@ thou makest use of them.# # skill levels -363 DEPRECATED# -364 DEPRECATED# -365 DEPRECATED# -366 DEPRECATED# -367 DEPRECATED# -368 DEPRECATED# -369 DEPRECATED# +363 None# +364 Novice# +365 Basic# +366 Skilled# +367 Expert# +368 Master# +369 Legend# # rest of the character sheet From bb7d73a3073d395f500a93ee71ee8ed0edaad2aa Mon Sep 17 00:00:00 2001 From: SheridanR Date: Mon, 9 Oct 2023 16:56:34 -0700 Subject: [PATCH 125/146] fix loading lang files on switch Signed-off-by: SheridanR --- src/init.cpp | 8 ++++++-- src/init_game.cpp | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 6f89daab0..39dd3c63b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -810,7 +810,7 @@ int Language::loadLanguage(char const * const lang, bool forceLoadBaseDirectory) // compose filename char filename[128] = { 0 }; - snprintf(filename, 127, "lang/%s.txt", lang); + snprintf(filename, 127, "/lang/%s.txt", lang); std::string langFilepath; if ( PHYSFS_isInit() && PHYSFS_getRealDir(filename) != NULL && !forceLoadBaseDirectory ) { @@ -819,7 +819,11 @@ int Language::loadLanguage(char const * const lang, bool forceLoadBaseDirectory) } else { +#ifdef NINTENDO + langFilepath = std::string(BASE_DATA_DIR) + filename; +#else langFilepath = filename; +#endif } // check if language file is valid @@ -1023,7 +1027,7 @@ int Language::reloadLanguage() if ( PHYSFS_isInit() && PHYSFS_getRealDir("lang/en.txt") != NULL ) { std::string langRealDir = PHYSFS_getRealDir("lang/en.txt"); - if ( langRealDir != "./" ) + if ( langRealDir != BASE_DATA_DIR ) { loadLanguage("en", true); // force load the base directory first, then modded paths later. } diff --git a/src/init_game.cpp b/src/init_game.cpp index 76e5f2e34..f84ce11d1 100644 --- a/src/init_game.cpp +++ b/src/init_game.cpp @@ -140,7 +140,7 @@ int initGame() // load achievement images #ifdef NINTENDO - Directory achievementsDir("rom:/images/achievements"); + Directory achievementsDir(BASE_DATA_DIR"/images/achievements"); #else Directory achievementsDir("images/achievements"); #endif From 2dec34bb1fd4dbeb4686f1c60ec4c526a704cd1f Mon Sep 17 00:00:00 2001 From: SheridanR Date: Mon, 9 Oct 2023 17:20:31 -0700 Subject: [PATCH 126/146] fix loading lights on switch Signed-off-by: SheridanR --- src/engine/audio/sound.cpp | 6 +++--- src/init.cpp | 4 +--- src/init_game.cpp | 2 ++ src/mod_tools.cpp | 3 --- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/engine/audio/sound.cpp b/src/engine/audio/sound.cpp index f94329eee..60ba08dcd 100644 --- a/src/engine/audio/sound.cpp +++ b/src/engine/audio/sound.cpp @@ -1226,7 +1226,7 @@ FMOD_RESULT physfsReloadMusic_helper_reloadMusicArray(uint32_t numMusic, const c if ( musicDir.compare("./") != 0 || reloadAll ) { musicDir.append(PHYSFS_getDirSeparator()).append(tempstr); - printlog("[PhysFS]: Reloading music file %s...", tempstr); + printlog("[PhysFS]: Loading music file %s...", tempstr); if ( musicArray ) { musicArray[c]->release(); @@ -1300,7 +1300,7 @@ void physfsReloadMusic(bool &introMusicChanged, bool reloadAll) //TODO: This sho if ( musicDir.compare("./") != 0 || reloadAll ) { musicDir += PHYSFS_getDirSeparator() + filename; - printlog("[PhysFS]: Reloading music file %s...", filename.c_str()); + printlog("[PhysFS]: Loading music file %s...", filename.c_str()); switch ( index ) { case 0: @@ -1655,7 +1655,7 @@ void physfsReloadMusic(bool &introMusicChanged, bool reloadAll) //TODO: This sho if ( musicDir.compare("./") != 0 || reloadAll ) { musicDir.append(PHYSFS_getDirSeparator()).append(tempstr); - printlog("[PhysFS]: Reloading music file %s...", tempstr); + printlog("[PhysFS]: Loading music file %s...", tempstr); music = intromusic; if ( music ) { diff --git a/src/init.cpp b/src/init.cpp index 39dd3c63b..5776b06c2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -746,9 +746,6 @@ int initApp(char const * const title, int fullscreen) updateLoadingScreen(80); - loadLights(); - updateLoadingScreen(81); - loading_done = true; return 0; }); @@ -763,6 +760,7 @@ int initApp(char const * const title, int fullscreen) { generateVBOs(0, nummodels); generateTileTextures(); + loadLights(); } #ifdef EDITOR diff --git a/src/init_game.cpp b/src/init_game.cpp index f84ce11d1..ad149b1da 100644 --- a/src/init_game.cpp +++ b/src/init_game.cpp @@ -71,6 +71,8 @@ void initGameDatafiles(bool moddedReload) MainMenu::RaceDescriptions::readFromFile(); MainMenu::ClassDescriptions::readFromFile(); StatueManager.readAllStatues(); + + loadLights(); } void initGameDatafilesAsync(bool moddedReload) diff --git a/src/mod_tools.cpp b/src/mod_tools.cpp index 609412d5b..a9c4465e1 100644 --- a/src/mod_tools.cpp +++ b/src/mod_tools.cpp @@ -8447,9 +8447,6 @@ void Mods::unloadMods(bool force) Mods::monsterLimbsRequireReloadUnmodded = false; } - // reload lights - loadLights(); - updateLoadingScreen(80); loading_done = true; From b2894878b1e46969a10c4f2df3771463964c1fd9 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 10 Oct 2023 16:40:48 +1100 Subject: [PATCH 127/146] * spawn dummy human command for big meat shield --- src/interface/consolecommand.cpp | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/interface/consolecommand.cpp b/src/interface/consolecommand.cpp index 612137768..891187051 100644 --- a/src/interface/consolecommand.cpp +++ b/src/interface/consolecommand.cpp @@ -4442,6 +4442,46 @@ namespace ConsoleCommands { } }); + static ConsoleCommand ccmd_spawndummyhuman("/spawndummyhuman", "", []CCMD{ + if ( !(svFlags & SV_FLAG_CHEATS) ) + { + messagePlayer(clientnum, MESSAGE_MISC, Language::get(277)); + return; + } + if ( multiplayer == CLIENT ) + { + messagePlayer(clientnum, MESSAGE_MISC, Language::get(284)); + return; + } + if ( players[clientnum]->entity ) + { + if ( Entity* monster = summonMonster(HUMAN, players[clientnum]->entity->x, players[clientnum]->entity->y) ) + { + if ( Stat* stat = monster->getStats() ) + { + stat->HP = 5000; + stat->MAXHP = 5000; + stat->CON = 0; + stat->RANDOM_CON = 0; + stat->LVL = 50; + stat->EFFECTS[EFF_STUNNED] = true; + stat->monsterForceAllegiance = Stat::MONSTER_FORCE_PLAYER_ENEMY; + serverUpdateEntityStatFlag(monster, 20); + stat->EDITOR_ITEMS[ITEM_SLOT_HELM] = 0; + stat->EDITOR_ITEMS[ITEM_SLOT_WEAPON] = 0; + stat->EDITOR_ITEMS[ITEM_SLOT_SHIELD] = 0; + stat->EDITOR_ITEMS[ITEM_SLOT_ARMOR] = 0; + stat->EDITOR_ITEMS[ITEM_SLOT_BOOTS] = 0; + stat->EDITOR_ITEMS[ITEM_SLOT_RING] = 0; + stat->EDITOR_ITEMS[ITEM_SLOT_AMULET] = 0; + stat->EDITOR_ITEMS[ITEM_SLOT_CLOAK] = 0; + stat->EDITOR_ITEMS[ITEM_SLOT_MASK] = 0; + stat->EDITOR_ITEMS[ITEM_SLOT_GLOVES] = 0; + } + } + } + }); + static ConsoleCommand ccmd_mesh_collider_debug("/mesh_collider_debug", "", []CCMD{ node_t* tmpNode = NULL; Entity* tmpEnt = NULL; From 058d0aff9f7bc2caef43665f3372e723b3d39f6f Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Wed, 11 Oct 2023 17:07:30 +1100 Subject: [PATCH 128/146] * fix ATK tooltip not using new damage calcs --- src/entity.cpp | 60 +++++++++----- src/entity.hpp | 1 + src/interface/updatecharactersheet.cpp | 107 +++++++++++++++---------- 3 files changed, 104 insertions(+), 64 deletions(-) diff --git a/src/entity.cpp b/src/entity.cpp index b3ace0f62..a949c7cae 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -7939,26 +7939,9 @@ void Entity::attack(int pose, int charge, Entity* target) { damage = (damage - chance) + (local_rng.rand() % chance) + 1; }*/ - real_t variance = 20; - real_t baseSkillModifier = 50.0; // 40-60 base - if ( weaponskill == PRO_UNARMED ) - { - variance = 30.0; - baseSkillModifier = 45.0; // 30-60 base - } - else if ( weaponskill == PRO_POLEARM ) - { - if ( gungnir ) - { - variance = 0.0; - baseSkillModifier = 60.0; - } - else - { - variance = 10.0; - baseSkillModifier = 55.0; // 50-60 base - } - } + real_t variance = 20; + real_t baseSkillModifier = 50.0; // 40-60 base + Entity::setMeleeDamageSkillModifiers(this, myStats, weaponskill, baseSkillModifier, variance); real_t skillModifier = baseSkillModifier - (variance / 2) + (myStats->PROFICIENCIES[weaponskill] / 2.0); skillModifier += (local_rng.rand() % (1 + static_cast(variance))); skillModifier /= 100.0; @@ -13025,6 +13008,43 @@ int getStatForProficiency(int skill) return statForProficiency; } +void Entity::setMeleeDamageSkillModifiers(Entity* my, Stat* myStats, int skill, real_t& baseSkillModifier, real_t& variance) +{ + bool shapeshifted = (my && my->behavior == &actPlayer && my->effectShapeshift != NOTHING); + bool gungnir = false; + + variance = 20; + baseSkillModifier = 50.0; // 40-60 base + + if ( !shapeshifted ) + { + if ( myStats && myStats->weapon && myStats->weapon->type == ARTIFACT_SPEAR ) + { + gungnir = true; + } + } + + if ( skill == PRO_UNARMED ) + { + variance = 30.0; + baseSkillModifier = 45.0; // 30-60 base + } + else if ( skill == PRO_POLEARM ) + { + if ( gungnir ) + { + variance = 0.0; + baseSkillModifier = 60.0; + } + else + { + variance = 10.0; + baseSkillModifier = 55.0; // 50-60 base + } + } + return; +} + int Entity::isEntityPlayer() const { diff --git a/src/entity.hpp b/src/entity.hpp index d43af2fbb..0e3410cf4 100644 --- a/src/entity.hpp +++ b/src/entity.hpp @@ -619,6 +619,7 @@ class Entity static Sint32 getAttack(Entity* my, Stat* myStats, bool isPlayer = false); static real_t getACEffectiveness(Entity* my, Stat* myStats, bool isPlayer, Entity* attacker, Stat* attackerStats); + static void Entity::setMeleeDamageSkillModifiers(Entity* my, Stat* myStats, int skill, real_t& baseSkillModifier, real_t& variance); Sint32 getBonusAttackOnTarget(Stat& hitstats); Sint32 getRangedAttack(); Sint32 getThrownAttack(); diff --git a/src/interface/updatecharactersheet.cpp b/src/interface/updatecharactersheet.cpp index 596892ef6..9a34fcd80 100644 --- a/src/interface/updatecharactersheet.cpp +++ b/src/interface/updatecharactersheet.cpp @@ -1883,15 +1883,22 @@ Sint32 displayAttackPower(const int player, AttackHoverText_t& output) output.mainAttributeBonus = statGetSTR(stats[player], entity); // bonus from main attribute output.equipmentAndEffectBonus = attack - statGetSTR(stats[player], entity) - BASE_PLAYER_UNARMED_DAMAGE - output.proficiencyBonus; // bonus from equipment - // get damage variances. - Sint32 variance = (attack / 2) * (100 - stats[player]->PROFICIENCIES[PRO_UNARMED]) / 100.f; - output.attackMaxRange = attack; - attack -= (variance / 2); // attack is the midpoint between max and min damage. - output.attackMinRange = output.totalAttack - variance; - output.proficiencyVariance = ((variance / 2) / static_cast(attack)) * 100.f;// return percent variance - output.totalAttack = attack; - output.proficiency = PRO_UNARMED; - output.totalAttack = output.attackMaxRange - ((output.attackMaxRange - output.attackMinRange) / 2.0); + { + real_t variance = 20; + real_t baseSkillModifier = 50.0; // 40-60 base + Entity::setMeleeDamageSkillModifiers(players[player]->entity, stats[player], PRO_UNARMED, baseSkillModifier, variance); + real_t skillModifierMin = baseSkillModifier - (variance / 2) + (stats[player]->PROFICIENCIES[PRO_UNARMED] / 2.0); + real_t skillModifierMax = skillModifierMin + variance; + skillModifierMin /= 100.0; + skillModifierMin = std::min(skillModifierMin, 1.0); + skillModifierMax /= 100.0; + skillModifierMax = std::min(skillModifierMax, 1.0); + output.proficiencyVariance = variance; + output.attackMaxRange = attack - static_cast((1.0 - skillModifierMax) * attack); + output.attackMinRange = attack - static_cast((1.0 - skillModifierMin) * attack); + output.proficiency = PRO_UNARMED; + output.totalAttack = output.attackMaxRange - ((output.attackMaxRange - output.attackMinRange) / 2.0); + } } else { @@ -1918,14 +1925,22 @@ Sint32 displayAttackPower(const int player, AttackHoverText_t& output) output.equipmentAndEffectBonus += attack - output.mainAttributeBonus - BASE_RANGED_DAMAGE - output.weaponBonus; // bonus from equipment - Sint32 variance = (attack / 2) * (100 - stats[player]->PROFICIENCIES[weaponskill]) / 100.f; - output.attackMaxRange = attack; - attack -= (variance / 2); - output.attackMinRange = output.totalAttack - variance; - output.proficiencyVariance = ((variance / 2) / static_cast(attack)) * 100.f;// return percent variance - output.totalAttack = attack; - output.proficiency = weaponskill; - output.totalAttack = output.attackMaxRange - ((output.attackMaxRange - output.attackMinRange) / 2.0); + { + real_t variance = 20; + real_t baseSkillModifier = 50.0; // 40-60 base + Entity::setMeleeDamageSkillModifiers(players[player]->entity, stats[player], weaponskill, baseSkillModifier, variance); + real_t skillModifierMin = baseSkillModifier - (variance / 2) + (stats[player]->PROFICIENCIES[weaponskill] / 2.0); + real_t skillModifierMax = skillModifierMin + variance; + skillModifierMin /= 100.0; + skillModifierMin = std::min(skillModifierMin, 1.0); + skillModifierMax /= 100.0; + skillModifierMax = std::min(skillModifierMax, 1.0); + output.proficiencyVariance = variance; + output.attackMaxRange = attack - static_cast((1.0 - skillModifierMax) * attack); + output.attackMinRange = attack - static_cast((1.0 - skillModifierMin) * attack); + output.proficiency = weaponskill; + output.totalAttack = output.attackMaxRange - ((output.attackMaxRange - output.attackMinRange) / 2.0); + } } else if ( stats[player]->weapon && stats[player]->weapon->type == TOOL_WHIP ) { @@ -1943,14 +1958,22 @@ Sint32 displayAttackPower(const int player, AttackHoverText_t& output) output.equipmentAndEffectBonus += attack - totalAttributeBonus - BASE_MELEE_DAMAGE - output.weaponBonus; // bonus from equipment - Sint32 variance = (attack / 2) * (100 - stats[player]->PROFICIENCIES[weaponskill]) / 100.f; - output.attackMaxRange = attack; - attack -= (variance / 2); - output.attackMinRange = output.totalAttack - variance; - output.proficiencyVariance = ((variance / 2) / static_cast(attack)) * 100.f;// return percent variance - output.totalAttack = attack; - output.proficiency = weaponskill; - output.totalAttack = output.attackMaxRange - ((output.attackMaxRange - output.attackMinRange) / 2.0); + { + real_t variance = 20; + real_t baseSkillModifier = 50.0; // 40-60 base + Entity::setMeleeDamageSkillModifiers(players[player]->entity, stats[player], weaponskill, baseSkillModifier, variance); + real_t skillModifierMin = baseSkillModifier - (variance / 2) + (stats[player]->PROFICIENCIES[weaponskill] / 2.0); + real_t skillModifierMax = skillModifierMin + variance; + skillModifierMin /= 100.0; + skillModifierMin = std::min(skillModifierMin, 1.0); + skillModifierMax /= 100.0; + skillModifierMax = std::min(skillModifierMax, 1.0); + output.proficiencyVariance = variance; + output.attackMaxRange = attack - static_cast((1.0 - skillModifierMax) * attack); + output.attackMinRange = attack - static_cast((1.0 - skillModifierMin) * attack); + output.proficiency = weaponskill; + output.totalAttack = output.attackMaxRange - ((output.attackMaxRange - output.attackMinRange) / 2.0); + } } else { @@ -2036,27 +2059,23 @@ Sint32 displayAttackPower(const int player, AttackHoverText_t& output) output.totalAttack += 1; attack += 1; } - // get damage variances. - Sint32 variance = 0; - if ( weaponskill == PRO_POLEARM ) - { - variance = (attack / 3) * (100 - stats[player]->PROFICIENCIES[weaponskill]) / 100.f; - if ( stats[player]->weapon->type == ARTIFACT_SPEAR ) - { - variance = 0; - } - } - else + { - variance = (attack / 2) * (100 - stats[player]->PROFICIENCIES[weaponskill]) / 100.f; + real_t variance = 20; + real_t baseSkillModifier = 50.0; // 40-60 base + Entity::setMeleeDamageSkillModifiers(players[player]->entity, stats[player], weaponskill, baseSkillModifier, variance); + real_t skillModifierMin = baseSkillModifier - (variance / 2) + (stats[player]->PROFICIENCIES[weaponskill] / 2.0); + real_t skillModifierMax = skillModifierMin + variance; + skillModifierMin /= 100.0; + skillModifierMin = std::min(skillModifierMin, 1.0); + skillModifierMax /= 100.0; + skillModifierMax = std::min(skillModifierMax, 1.0); + output.proficiencyVariance = variance; + output.attackMaxRange = attack - static_cast((1.0 - skillModifierMax) * attack); + output.attackMinRange = attack - static_cast((1.0 - skillModifierMin) * attack); + output.proficiency = weaponskill; + output.totalAttack = output.attackMaxRange - ((output.attackMaxRange - output.attackMinRange) / 2.0); } - output.attackMaxRange = attack; - attack -= (variance / 2); // attack is the midpoint between max and min damage. - output.attackMinRange = output.totalAttack - variance; - output.proficiencyVariance = ((variance / 2) / static_cast(attack)) * 100.f;// return percent variance - output.totalAttack = attack; - output.proficiency = weaponskill; - output.totalAttack = output.attackMaxRange - ((output.attackMaxRange - output.attackMinRange) / 2.0); } else if ( itemCategory(stats[player]->weapon) == MAGICSTAFF ) // staffs. { From 9132f1f45474d0a682a7c3448bed890e34637136 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 11 Oct 2023 14:31:52 -0700 Subject: [PATCH 129/146] update xcode to use new EOS sdk (1.16.1) Signed-off-by: SheridanR --- xcode/Barony/Barony.xcodeproj/project.pbxproj | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/xcode/Barony/Barony.xcodeproj/project.pbxproj b/xcode/Barony/Barony.xcodeproj/project.pbxproj index 20068d54a..99bf73ee7 100644 --- a/xcode/Barony/Barony.xcodeproj/project.pbxproj +++ b/xcode/Barony/Barony.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 843354EC2A771B6F0014D3CC /* libnfd.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 843354EA2A771B050014D3CC /* libnfd.a */; }; 843354F32A771DA20014D3CC /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 843354F12A771D960014D3CC /* Cocoa.framework */; }; 843354F62A771DAC0014D3CC /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 843354F12A771D960014D3CC /* Cocoa.framework */; }; + 843D8F9B2AD74AC9008E532C /* libEOSSDK-Mac-Shipping.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 843D8F9A2AD74AC9008E532C /* libEOSSDK-Mac-Shipping.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 843D8F9C2AD74AFD008E532C /* libEOSSDK-Mac-Shipping.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 843D8F9A2AD74AC9008E532C /* libEOSSDK-Mac-Shipping.dylib */; }; 844D4A262925E64C005907BA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 844D4A252925E64C005907BA /* Assets.xcassets */; }; 844D4A37292724EE005907BA /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84C3DB19291F8A1C0084EAD5 /* OpenGL.framework */; }; 844D4A3C292724FE005907BA /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 844D4A38292724FE005907BA /* SDL2.framework */; }; @@ -241,9 +243,7 @@ 84C3DB32292028790084EAD5 /* playernames-female.txt in Resources */ = {isa = PBXBuildFile; fileRef = 84C3DB2F292028790084EAD5 /* playernames-female.txt */; }; 84C69F2C2ABA83190014D8FF /* themes in Resources */ = {isa = PBXBuildFile; fileRef = 84C69F2B2ABA83190014D8FF /* themes */; }; 84F71060292327D900AE0D03 /* libsteam_api.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F7105F292327D900AE0D03 /* libsteam_api.dylib */; }; - 84F710632923280500AE0D03 /* libEOSSDK-Mac-Shipping.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 84F71061292327F100AE0D03 /* libEOSSDK-Mac-Shipping.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 84F710642923280D00AE0D03 /* libsteam_api.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 84F7105F292327D900AE0D03 /* libsteam_api.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 84F7106529232BB100AE0D03 /* libEOSSDK-Mac-Shipping.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F71061292327F100AE0D03 /* libEOSSDK-Mac-Shipping.dylib */; }; 84F7108D292350C500AE0D03 /* Vorbis.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F71089292350C500AE0D03 /* Vorbis.framework */; }; 84F7108E292350C500AE0D03 /* Ogg.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F7108A292350C500AE0D03 /* Ogg.framework */; }; 84F7108F292350C500AE0D03 /* Theora.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F7108B292350C500AE0D03 /* Theora.framework */; }; @@ -299,8 +299,8 @@ 847B3BF2296BF89F002D8852 /* libphysfs.1.dylib in Embed Libraries */, 847B3BF0296BF890002D8852 /* libpng16.16.dylib in Embed Libraries */, 845F4BB92920302F00D6B9D7 /* libfmod.dylib in Embed Libraries */, - 84F710632923280500AE0D03 /* libEOSSDK-Mac-Shipping.dylib in Embed Libraries */, 84F710642923280D00AE0D03 /* libsteam_api.dylib in Embed Libraries */, + 843D8F9B2AD74AC9008E532C /* libEOSSDK-Mac-Shipping.dylib in Embed Libraries */, ); name = "Embed Libraries"; runOnlyForDeploymentPostprocessing = 0; @@ -312,6 +312,7 @@ 84222CCB29ECB510000BF08E /* npcnames-male.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "npcnames-male.txt"; path = "../../npcnames-male.txt"; sourceTree = ""; }; 843354EA2A771B050014D3CC /* libnfd.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libnfd.a; path = "../../../nfd-build/libnfd.a"; sourceTree = ""; }; 843354F12A771D960014D3CC /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 843D8F9A2AD74AC9008E532C /* libEOSSDK-Mac-Shipping.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libEOSSDK-Mac-Shipping.dylib"; path = "../../../eos/EOS-SDK-27379709-v1/SDK/Bin/libEOSSDK-Mac-Shipping.dylib"; sourceTree = ""; }; 844D4A1F2925E64C005907BA /* Barony Editor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Barony Editor.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 844D4A252925E64C005907BA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 844D4A38292724FE005907BA /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = ../../../../../../Library/Frameworks/SDL2.framework; sourceTree = ""; }; @@ -563,7 +564,6 @@ 84C3DB2F292028790084EAD5 /* playernames-female.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "playernames-female.txt"; path = "../../playernames-female.txt"; sourceTree = ""; }; 84C69F2B2ABA83190014D8FF /* themes */ = {isa = PBXFileReference; lastKnownFileType = folder; name = themes; path = ../../../baronyupdate/themes; sourceTree = ""; }; 84F7105F292327D900AE0D03 /* libsteam_api.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsteam_api.dylib; path = ../../../steamworks/sdk/redistributable_bin/osx/libsteam_api.dylib; sourceTree = ""; }; - 84F71061292327F100AE0D03 /* libEOSSDK-Mac-Shipping.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libEOSSDK-Mac-Shipping.dylib"; path = "../../../eos/EOS-SDK-21924193-v1/SDK/Bin/libEOSSDK-Mac-Shipping.dylib"; sourceTree = ""; }; 84F71089292350C500AE0D03 /* Vorbis.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Vorbis.framework; path = ../../../Vorbis.framework; sourceTree = ""; }; 84F7108A292350C500AE0D03 /* Ogg.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Ogg.framework; path = ../../../Ogg.framework; sourceTree = ""; }; 84F7108B292350C500AE0D03 /* Theora.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Theora.framework; path = ../../../Theora.framework; sourceTree = ""; }; @@ -605,8 +605,8 @@ 84BD1E70296E9F2A00DC6F44 /* libphysfs.1.dylib in Frameworks */, 84BD1E6F296E9F1700DC6F44 /* libpng16.16.dylib in Frameworks */, 84B7E1802920290E00D1F0C5 /* libfmod.dylib in Frameworks */, - 84F7106529232BB100AE0D03 /* libEOSSDK-Mac-Shipping.dylib in Frameworks */, 84F71060292327D900AE0D03 /* libsteam_api.dylib in Frameworks */, + 843D8F9C2AD74AFD008E532C /* libEOSSDK-Mac-Shipping.dylib in Frameworks */, 843354EC2A771B6F0014D3CC /* libnfd.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -876,8 +876,6 @@ 84C3DB21292028630084EAD5 /* music */, 84C3DB1F292028630084EAD5 /* sound */, 84C69F2B2ABA83190014D8FF /* themes */, - 847B3BF1296BF89F002D8852 /* libphysfs.1.dylib */, - 847B3BEF296BF890002D8852 /* libpng16.16.dylib */, 84C3DB12291F799D0084EAD5 /* Config.hpp */, 847E18F729239843003BD4E9 /* Info.plist */, 84222CCA29ECB510000BF08E /* npcnames-female.txt */, @@ -907,6 +905,8 @@ 84C3D8F0291F5D370084EAD5 /* Frameworks */ = { isa = PBXGroup; children = ( + 847B3BEF296BF890002D8852 /* libpng16.16.dylib */, + 847B3BF1296BF89F002D8852 /* libphysfs.1.dylib */, 843354F12A771D960014D3CC /* Cocoa.framework */, 843354EA2A771B050014D3CC /* libnfd.a */, 844D4A3B292724FE005907BA /* SDL2_image.framework */, @@ -917,7 +917,7 @@ 84F7108B292350C500AE0D03 /* Theora.framework */, 84F7108C292350C500AE0D03 /* theoraplayer.framework */, 84F71089292350C500AE0D03 /* Vorbis.framework */, - 84F71061292327F100AE0D03 /* libEOSSDK-Mac-Shipping.dylib */, + 843D8F9A2AD74AC9008E532C /* libEOSSDK-Mac-Shipping.dylib */, 84F7105F292327D900AE0D03 /* libsteam_api.dylib */, 84B7E17F2920290E00D1F0C5 /* libfmod.dylib */, 84C3DB19291F8A1C0084EAD5 /* OpenGL.framework */, @@ -1280,7 +1280,7 @@ "\"$(SRCROOT)/../../../lpng1639\"", "\"$(SRCROOT)/../../../physfs-3.0.2/src\"", "\"$(SRCROOT)/../../../fmod/api/core/inc\"", - "\"$(SRCROOT)/../../../eos/EOS-SDK-21924193-v1/SDK/Include\"", + "\"$(SRCROOT)/../../../eos/EOS-SDK-27379709-v1/SDK/Include\"", "\"$(SRCROOT)/../../../steamworks/sdk/public\"", "\"$(SRCROOT)/../../../nfd-build\"", ); @@ -1294,7 +1294,7 @@ "\"$(SRCROOT)/../../../lpng1639\"", "\"$(SRCROOT)/../../../physfs-3.0.2/build\"", "\"$(SRCROOT)/../../../fmod/api/core/lib\"", - "\"$(SRCROOT)/../../../eos/EOS-SDK-21924193-v1/SDK/Bin\"", + "\"$(SRCROOT)/../../../eos/EOS-SDK-27379709-v1/SDK/Bin\"", "\"$(SRCROOT)/../../../steamworks/sdk/redistributable_bin/osx\"", "\"$(SRCROOT)/../../../nfd-build\"", ); @@ -1337,7 +1337,7 @@ "\"$(SRCROOT)/../../../lpng1639\"", "\"$(SRCROOT)/../../../physfs-3.0.2/src\"", "\"$(SRCROOT)/../../../fmod/api/core/inc\"", - "\"$(SRCROOT)/../../../eos/EOS-SDK-21924193-v1/SDK/Include\"", + "\"$(SRCROOT)/../../../eos/EOS-SDK-27379709-v1/SDK/Include\"", "\"$(SRCROOT)/../../../steamworks/sdk/public\"", "\"$(SRCROOT)/../../../nfd-build\"", ); @@ -1351,7 +1351,7 @@ "\"$(SRCROOT)/../../../lpng1639\"", "\"$(SRCROOT)/../../../physfs-3.0.2/build\"", "\"$(SRCROOT)/../../../fmod/api/core/lib\"", - "\"$(SRCROOT)/../../../eos/EOS-SDK-21924193-v1/SDK/Bin\"", + "\"$(SRCROOT)/../../../eos/EOS-SDK-27379709-v1/SDK/Bin\"", "\"$(SRCROOT)/../../../steamworks/sdk/redistributable_bin/osx\"", "\"$(SRCROOT)/../../../nfd-build\"", ); @@ -1504,7 +1504,7 @@ "\"$(SRCROOT)/../../../lpng1639\"", "\"$(SRCROOT)/../../../physfs-3.0.2/src\"", "\"$(SRCROOT)/../../../fmod/api/core/inc\"", - "\"$(SRCROOT)/../../../eos/EOS-SDK-21924193-v1/SDK/Include\"", + "\"$(SRCROOT)/../../../eos/EOS-SDK-27379709-v1/SDK/Include\"", "\"$(SRCROOT)/../../../steamworks/sdk/public\"", "\"$(SRCROOT)/../../../nfd-build\"", ); @@ -1529,7 +1529,7 @@ "\"$(SRCROOT)/../../../lpng1639\"", "\"$(SRCROOT)/../../../physfs-3.0.2/build\"", "\"$(SRCROOT)/../../../fmod/api/core/lib\"", - "\"$(SRCROOT)/../../../eos/EOS-SDK-21924193-v1/SDK/Bin\"", + "\"$(SRCROOT)/../../../eos/EOS-SDK-27379709-v1/SDK/Bin\"", "\"$(SRCROOT)/../../../steamworks/sdk/redistributable_bin/osx\"", "\"$(SRCROOT)/../../../nfd-build\"", ); @@ -1581,7 +1581,7 @@ "\"$(SRCROOT)/../../../lpng1639\"", "\"$(SRCROOT)/../../../physfs-3.0.2/src\"", "\"$(SRCROOT)/../../../fmod/api/core/inc\"", - "\"$(SRCROOT)/../../../eos/EOS-SDK-21924193-v1/SDK/Include\"", + "\"$(SRCROOT)/../../../eos/EOS-SDK-27379709-v1/SDK/Include\"", "\"$(SRCROOT)/../../../steamworks/sdk/public\"", "\"$(SRCROOT)/../../../nfd-build\"", ); @@ -1606,7 +1606,7 @@ "\"$(SRCROOT)/../../../lpng1639\"", "\"$(SRCROOT)/../../../physfs-3.0.2/build\"", "\"$(SRCROOT)/../../../fmod/api/core/lib\"", - "\"$(SRCROOT)/../../../eos/EOS-SDK-21924193-v1/SDK/Bin\"", + "\"$(SRCROOT)/../../../eos/EOS-SDK-27379709-v1/SDK/Bin\"", "\"$(SRCROOT)/../../../steamworks/sdk/redistributable_bin/osx\"", "\"$(SRCROOT)/../../../nfd-build\"", ); From 89808f56d45d13e1f0b643d277403feb71f81d5f Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 11 Oct 2023 14:33:18 -0700 Subject: [PATCH 130/146] remove extra Entity:: qualifier Signed-off-by: SheridanR --- src/entity.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity.hpp b/src/entity.hpp index 0e3410cf4..de1d61d1c 100644 --- a/src/entity.hpp +++ b/src/entity.hpp @@ -619,7 +619,7 @@ class Entity static Sint32 getAttack(Entity* my, Stat* myStats, bool isPlayer = false); static real_t getACEffectiveness(Entity* my, Stat* myStats, bool isPlayer, Entity* attacker, Stat* attackerStats); - static void Entity::setMeleeDamageSkillModifiers(Entity* my, Stat* myStats, int skill, real_t& baseSkillModifier, real_t& variance); + static void setMeleeDamageSkillModifiers(Entity* my, Stat* myStats, int skill, real_t& baseSkillModifier, real_t& variance); Sint32 getBonusAttackOnTarget(Stat& hitstats); Sint32 getRangedAttack(); Sint32 getThrownAttack(); From 27510f1102892da43d949b26b590d9053666e8d5 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Fri, 13 Oct 2023 12:41:56 +1100 Subject: [PATCH 131/146] * fix loading lang file? --- src/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index 5776b06c2..1cfd2ce30 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -820,7 +820,7 @@ int Language::loadLanguage(char const * const lang, bool forceLoadBaseDirectory) #ifdef NINTENDO langFilepath = std::string(BASE_DATA_DIR) + filename; #else - langFilepath = filename; + langFilepath = std::string(BASE_DATA_DIR) + filename; #endif } From 46f686855804b1a18b81d550b954e91ca0382453 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Fri, 13 Oct 2023 20:48:42 +1100 Subject: [PATCH 132/146] * fix skill names --- lang/en.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/en.txt b/lang/en.txt index da423c434..f822ae810 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -3551,7 +3551,7 @@ last of your magic is drained!# 3201 Bleh! Your body rejects the solids in your stomach!# 3202 What a horrible night to have a curse...# 3203 Blecch! This is disgusting!# -3204 DEPRECATED# +3204 Unarmed# 3205 %s is stunned from your blow!# 3206 The %s is stunned from your blow!# 3207 You are stunned from %s's blow!# @@ -3702,7 +3702,7 @@ last of your magic is drained!# 3337 You have no familiar potions to combine!# 3338 DEPRECATED# 3339 Brew# -3340 DEPRECATED# +3340 Alchemy# 3341 Stop Brewing# 3342 You have no potions left to mix!# 3343 DEPRECATED# From 825fbf0605463568cf3d85e3b6cc8ce5d498a58e Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Fri, 13 Oct 2023 20:52:42 +1100 Subject: [PATCH 133/146] * another lang fix --- lang/en.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lang/en.txt b/lang/en.txt index f822ae810..91f8baa23 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -5908,7 +5908,8 @@ Mod# Workshop# 5881 Open Workshop# -5882 Browse community created mods and 'subscribe' to\ndownload mods for use. +5882 Browse community created mods and 'subscribe' to +download mods for use. Subscribing while Barony is running may require closing the game in order for Steam to begin downloading. From f8d395dc9e041ab59ebedbe6db409abf35d9482d Mon Sep 17 00:00:00 2001 From: SheridanR Date: Sat, 14 Oct 2023 16:51:07 -0700 Subject: [PATCH 134/146] fix crashes caused by net_packet dereference after auto-disconnect in game.cpp fix crash caused by arrow sticking to wall while out of bounds fix crash caused by nintendo network loss while updating lobby Signed-off-by: SheridanR --- src/actarrow.cpp | 1 + src/game.cpp | 197 +++++++++++++++++++++++++------------------- src/ui/MainMenu.cpp | 8 +- 3 files changed, 117 insertions(+), 89 deletions(-) diff --git a/src/actarrow.cpp b/src/actarrow.cpp index 107d65505..09f4affda 100644 --- a/src/actarrow.cpp +++ b/src/actarrow.cpp @@ -309,6 +309,7 @@ void actArrow(Entity* my) bool arrowInGround = false; int index = (int)(my->y / 16) * MAPLAYERS + (int)(my->x / 16) * MAPLAYERS * map.height; + index = std::clamp(index, 0, (int)(MAPLAYERS * map.width * map.height) - 1); if ( map.tiles[index] ) { if ( my->sprite == PROJECTILE_BOLT_SPRITE || my->sprite == PROJECTILE_ROCK_SPRITE ) // bolt/rock diff --git a/src/game.cpp b/src/game.cpp index 0cb5a1f4d..43f626529 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1304,14 +1304,16 @@ void gameLogic(void) { nextnode = node->next; - // send the delete entity command to the client - strcpy((char*)net_packet->data, "ENTD"); - deleteent = (deleteent_t*)node->element; - SDLNet_Write32(deleteent->uid, &net_packet->data[4]); - net_packet->address.host = net_clients[i - 1].host; - net_packet->address.port = net_clients[i - 1].port; - net_packet->len = 8; - sendPacket(net_sock, -1, net_packet, i - 1); + if (net_packet && net_packet->data) { + // send the delete entity command to the client + strcpy((char*)net_packet->data, "ENTD"); + deleteent = (deleteent_t*)node->element; + SDLNet_Write32(deleteent->uid, &net_packet->data[4]); + net_packet->address.host = net_clients[i - 1].host; + net_packet->address.port = net_clients[i - 1].port; + net_packet->len = 8; + sendPacket(net_sock, -1, net_packet, i - 1); + } // quit reminding clients after a certain number of attempts deleteent->tries++; @@ -2027,7 +2029,7 @@ void gameLogic(void) } } - if ( multiplayer == SERVER ) + if ( multiplayer == SERVER && net_packet && net_packet->data ) { for ( c = 1; c < MAXPLAYERS; ++c ) { @@ -2362,7 +2364,7 @@ void gameLogic(void) steamAchievementClient(c, "BARONY_ACH_ESCORT"); } - if ( c > 0 && multiplayer == SERVER && !players[c]->isLocalPlayer() ) + if ( c > 0 && multiplayer == SERVER && !players[c]->isLocalPlayer() && net_packet && net_packet->data ) { strcpy((char*)net_packet->data, "LEAD"); SDLNet_Write32((Uint32)monster->getUID(), &net_packet->data[4]); @@ -2486,16 +2488,18 @@ void gameLogic(void) { continue; } - strcpy((char*)net_packet->data, "LVLC"); - net_packet->data[4] = secretlevel; - SDLNet_Write32(mapseed, &net_packet->data[5]); - SDLNet_Write32(lastEntityUIDs, &net_packet->data[9]); - net_packet->data[13] = currentlevel; - net_packet->data[14] = 0; - net_packet->address.host = net_clients[c - 1].host; - net_packet->address.port = net_clients[c - 1].port; - net_packet->len = 15; - sendPacketSafe(net_sock, -1, net_packet, c - 1); + if (net_packet && net_packet->data) { + strcpy((char*)net_packet->data, "LVLC"); + net_packet->data[4] = secretlevel; + SDLNet_Write32(mapseed, &net_packet->data[5]); + SDLNet_Write32(lastEntityUIDs, &net_packet->data[9]); + net_packet->data[13] = currentlevel; + net_packet->data[14] = 0; + net_packet->address.host = net_clients[c - 1].host; + net_packet->address.port = net_clients[c - 1].port; + net_packet->len = 15; + sendPacketSafe(net_sock, -1, net_packet, c - 1); + } } } @@ -2558,7 +2562,7 @@ void gameLogic(void) if (*cvar_enableKeepAlives) { for ( c = 1; c < MAXPLAYERS; c++ ) { - if ( client_disconnected[c] || players[c]->isLocalPlayer() ) + if ( client_disconnected[c] || players[c]->isLocalPlayer() || !net_packet || !net_packet->data ) { continue; } @@ -2609,12 +2613,14 @@ void gameLogic(void) if ( oassailant[c] != assailant[c] ) { oassailant[c] = assailant[c]; - strcpy((char*)net_packet->data, "MUSM"); - net_packet->address.host = net_clients[c - 1].host; - net_packet->address.port = net_clients[c - 1].port; - net_packet->data[4] = assailant[c]; - net_packet->len = 5; - sendPacketSafe(net_sock, -1, net_packet, c - 1); + if (net_packet && net_packet->data) { + strcpy((char*)net_packet->data, "MUSM"); + net_packet->address.host = net_clients[c - 1].host; + net_packet->address.port = net_clients[c - 1].port; + net_packet->data[4] = assailant[c]; + net_packet->len = 5; + sendPacketSafe(net_sock, -1, net_packet, c - 1); + } } } } @@ -2778,7 +2784,7 @@ void gameLogic(void) else if ( multiplayer == CLIENT ) { // keep alives - if ( *cvar_enableKeepAlives ) + if ( *cvar_enableKeepAlives && net_packet && net_packet->data ) { if ( ticks % (TICKS_PER_SECOND * 1) == 0 ) { @@ -2931,13 +2937,15 @@ void gameLogic(void) { if ( !entity->flags[NOUPDATE] && entity->getUID() > 0 && entity->getUID() != -2 && entity->getUID() != -3 && entity->getUID() != -4 ) { - strcpy((char*)net_packet->data, "ENTE"); - net_packet->data[4] = clientnum; - SDLNet_Write32(entity->getUID(), &net_packet->data[5]); - net_packet->address.host = net_server.host; - net_packet->address.port = net_server.port; - net_packet->len = 9; - sendPacket(net_sock, -1, net_packet, 0); + if (net_packet && net_packet->data) { + strcpy((char*)net_packet->data, "ENTE"); + net_packet->data[4] = clientnum; + SDLNet_Write32(entity->getUID(), &net_packet->data[5]); + net_packet->address.host = net_server.host; + net_packet->address.port = net_server.port; + net_packet->len = 9; + sendPacket(net_sock, -1, net_packet, 0); + } } } } @@ -3742,9 +3750,12 @@ bool handleEvents(void) ++numplayers; } } - char address[64]; + char address[64] = { '\0' }; + bool result = false; nxGetWirelessAddress(address, sizeof(address)); - bool result = nxUpdateLobby(address, MainMenu::getHostname(), svFlags, numplayers); + if (address[0]) { + result = nxUpdateLobby(address, MainMenu::getHostname(), svFlags, numplayers); + } if (!result) { MainMenu::timedOut(); } @@ -4816,22 +4827,26 @@ void pauseGame(int mode /* 0 == toggle, 1 == force unpause, 2 == force pause */, { continue; } - strcpy((char*)net_packet->data, "PAUS"); - net_packet->data[4] = clientnum; - net_packet->address.host = net_clients[c - 1].host; - net_packet->address.port = net_clients[c - 1].port; - net_packet->len = 5; - sendPacketSafe(net_sock, -1, net_packet, c - 1); + if (net_packet && net_packet->data) { + strcpy((char*)net_packet->data, "PAUS"); + net_packet->data[4] = clientnum; + net_packet->address.host = net_clients[c - 1].host; + net_packet->address.port = net_clients[c - 1].port; + net_packet->len = 5; + sendPacketSafe(net_sock, -1, net_packet, c - 1); + } } } else if ( multiplayer == CLIENT && ignoreplayer ) { - strcpy((char*)net_packet->data, "PAUS"); - net_packet->data[4] = clientnum; - net_packet->address.host = net_server.host; - net_packet->address.port = net_server.port; - net_packet->len = 5; - sendPacketSafe(net_sock, -1, net_packet, 0); + if (net_packet && net_packet->data) { + strcpy((char*)net_packet->data, "PAUS"); + net_packet->data[4] = clientnum; + net_packet->address.host = net_server.host; + net_packet->address.port = net_server.port; + net_packet->len = 5; + sendPacketSafe(net_sock, -1, net_packet, 0); + } } } else if ( (gamePaused && mode != 2) || mode == 1 ) @@ -4866,22 +4881,26 @@ void pauseGame(int mode /* 0 == toggle, 1 == force unpause, 2 == force pause */, { continue; } - strcpy((char*)net_packet->data, "UNPS"); - net_packet->data[4] = clientnum; - net_packet->address.host = net_clients[c - 1].host; - net_packet->address.port = net_clients[c - 1].port; - net_packet->len = 5; - sendPacketSafe(net_sock, -1, net_packet, c - 1); + if (net_packet && net_packet->data) { + strcpy((char*)net_packet->data, "UNPS"); + net_packet->data[4] = clientnum; + net_packet->address.host = net_clients[c - 1].host; + net_packet->address.port = net_clients[c - 1].port; + net_packet->len = 5; + sendPacketSafe(net_sock, -1, net_packet, c - 1); + } } } else if ( multiplayer == CLIENT && ignoreplayer ) { - strcpy((char*)net_packet->data, "UNPS"); - net_packet->data[4] = clientnum; - net_packet->address.host = net_server.host; - net_packet->address.port = net_server.port; - net_packet->len = 5; - sendPacketSafe(net_sock, -1, net_packet, 0); + if (net_packet && net_packet->data) { + strcpy((char*)net_packet->data, "UNPS"); + net_packet->data[4] = clientnum; + net_packet->address.host = net_server.host; + net_packet->address.port = net_server.port; + net_packet->len = 5; + sendPacketSafe(net_sock, -1, net_packet, 0); + } } } } @@ -6268,14 +6287,16 @@ static void doConsoleCommands() { } // send message to server - strcpy((char*)net_packet->data, "MSGS"); - net_packet->data[4] = commandPlayer; - SDLNet_Write32(color, &net_packet->data[5]); - strcpy((char*)(&net_packet->data[9]), command_str); - net_packet->address.host = net_server.host; - net_packet->address.port = net_server.port; - net_packet->len = 9 + strlen(command_str) + 1; - sendPacketSafe(net_sock, -1, net_packet, 0); + if (net_packet && net_packet->data) { + strcpy((char*)net_packet->data, "MSGS"); + net_packet->data[4] = commandPlayer; + SDLNet_Write32(color, &net_packet->data[5]); + strcpy((char*)(&net_packet->data[9]), command_str); + net_packet->address.host = net_server.host; + net_packet->address.port = net_server.port; + net_packet->len = 9 + strlen(command_str) + 1; + sendPacketSafe(net_sock, -1, net_packet, 0); + } } else { @@ -6302,24 +6323,26 @@ static void doConsoleCommands() { { continue; } - strcpy((char*)net_packet->data, "MSGS"); - // strncpy() does not copy N bytes if a terminating null is encountered first - // see http://www.cplusplus.com/reference/cstring/strncpy/ - // see https://en.cppreference.com/w/c/string/byte/strncpy - // GCC throws a warning (intended) when the length argument to strncpy() in any - // way depends on strlen(src) to discourage this (and related) construct(s). - - strncpy(chatstring, stats[0]->name, 22); - chatstring[std::min(strlen(stats[0]->name), 22)] = 0; //TODO: Why are size_t and int being compared? - strcat(chatstring, ": "); - strcat(chatstring, command_str); - SDLNet_Write32(color, &net_packet->data[4]); - SDLNet_Write32((Uint32)MESSAGE_CHAT, &net_packet->data[8]); - strcpy((char*)(&net_packet->data[12]), chatstring); - net_packet->address.host = net_clients[c - 1].host; - net_packet->address.port = net_clients[c - 1].port; - net_packet->len = 12 + strlen(chatstring) + 1; - sendPacketSafe(net_sock, -1, net_packet, c - 1); + if (net_packet && net_packet->data) { + strcpy((char*)net_packet->data, "MSGS"); + // strncpy() does not copy N bytes if a terminating null is encountered first + // see http://www.cplusplus.com/reference/cstring/strncpy/ + // see https://en.cppreference.com/w/c/string/byte/strncpy + // GCC throws a warning (intended) when the length argument to strncpy() in any + // way depends on strlen(src) to discourage this (and related) construct(s). + + strncpy(chatstring, stats[0]->name, 22); + chatstring[std::min(strlen(stats[0]->name), 22)] = 0; //TODO: Why are size_t and int being compared? + strcat(chatstring, ": "); + strcat(chatstring, command_str); + SDLNet_Write32(color, &net_packet->data[4]); + SDLNet_Write32((Uint32)MESSAGE_CHAT, &net_packet->data[8]); + strcpy((char*)(&net_packet->data[12]), chatstring); + net_packet->address.host = net_clients[c - 1].host; + net_packet->address.port = net_clients[c - 1].port; + net_packet->len = 12 + strlen(chatstring) + 1; + sendPacketSafe(net_sock, -1, net_packet, c - 1); + } } } } diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 73d36c4f6..6f01adf7f 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -9397,9 +9397,13 @@ namespace MainMenu { } } - char address[64]; + char address[64] = { '\0' }; + bool result = false; + nxGetWirelessAddress(address, sizeof(address)); - bool result = nxUpdateLobby(address, MainMenu::getHostname(), svFlags, numplayers); + if (address[0]) { + result = nxUpdateLobby(address, MainMenu::getHostname(), svFlags, numplayers); + } if (!result) { disconnectFromLobby(); destroyMainMenu(); From df5fac69513e0f97dd76bf827ffcf4de047525af Mon Sep 17 00:00:00 2001 From: SheridanR Date: Sun, 15 Oct 2023 14:10:34 -0700 Subject: [PATCH 135/146] fix being able to play a female incubus or a male succubus via randomize class Signed-off-by: SheridanR --- src/ui/MainMenu.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 73d36c4f6..41b197db0 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -14644,9 +14644,6 @@ namespace MainMenu { auto card = static_cast(button.getParent()); - // select a random sex - stats[index]->sex = (sex_t)(RNG.getU8() % 2); - // select a random race // there are 9 legal races that the player can select from the start. if (enabledDLCPack1 && enabledDLCPack2) { @@ -14672,16 +14669,28 @@ namespace MainMenu { stats[index]->appearance = 0; } - // update sex buttons after race selection: - // we might have chosen a succubus or incubus + // select a random sex (unless you're a succubus or an incubus) + if (stats[index]->playerRace == RACE_SUCCUBUS) { + stats[index]->sex = sex_t::FEMALE; + } + else if (stats[index]->playerRace == RACE_INCUBUS) { + stats[index]->sex = sex_t::MALE; + } + else { + stats[index]->sex = (sex_t)(RNG.getU8() % 2); + } + + // update sex buttons auto bottom = card->findFrame("bottom"); if (bottom) { auto male_button = bottom->findButton("male"); - auto female_button = bottom->findButton("female"); - if (male_button && female_button) { + if (male_button) { male_button->setPressed(stats[index]->sex == MALE); male_button->setColor(stats[index]->sex == MALE ? makeColorRGB(255, 255, 255) : makeColorRGB(127, 127, 127)); male_button->setHighlightColor(stats[index]->sex == MALE ? makeColorRGB(255, 255, 255) : makeColorRGB(127, 127, 127)); + } + auto female_button = bottom->findButton("female"); + if (female_button) { female_button->setPressed(stats[index]->sex == FEMALE); female_button->setColor(stats[index]->sex == FEMALE ? makeColorRGB(255, 255, 255) : makeColorRGB(127, 127, 127)); female_button->setHighlightColor(stats[index]->sex == FEMALE ? makeColorRGB(255, 255, 255) : makeColorRGB(127, 127, 127)); From 15ba8c6802f9530869dd40fd876667122cbe0220 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Sun, 15 Oct 2023 16:21:23 -0700 Subject: [PATCH 136/146] builds on linux --- src/eos.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/eos.cpp b/src/eos.cpp index 4536db531..92afc0343 100644 --- a/src/eos.cpp +++ b/src/eos.cpp @@ -1240,7 +1240,9 @@ bool EOSFuncs::initPlatform(bool enableLogging) PlatformOptions.RTCOptions = nullptr; PlatformOptions.IntegratedPlatformOptionsContainerHandle = nullptr; // must be null initialized +#ifndef LINUX PlatformOptions.SystemSpecificOptions = nullptr; // must be null initialized +#endif PlatformHandle = EOS_Platform_Create(&PlatformOptions); PlatformOptions.bIsServer = EOS_TRUE; From 537cd63e4de148eac5243685c22a0f8adfcc9d55 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Mon, 16 Oct 2023 22:01:48 +1100 Subject: [PATCH 137/146] * fix drain soul crash --- src/magic/magic.cpp | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/magic/magic.cpp b/src/magic/magic.cpp index f677859da..770f304fc 100644 --- a/src/magic/magic.cpp +++ b/src/magic/magic.cpp @@ -1003,15 +1003,6 @@ void spellEffectDrainSoul(Entity& my, spellElement_t& element, Entity* parent, i damage /= (1 + (int)resistance); hit.entity->colliderHandleDamageMagic(damage, my, parent); - if ( my.actmagicProjectileArc > 0 ) - { - spawnMagicTower(parent, my.x, my.y, SPELL_DRAIN_SOUL, nullptr); - } - if ( !(my.actmagicIsOrbiting == 2) ) - { - my.removeLightField(); - list_RemoveNode(my.mynode); - } return; } else if ( hit.entity->behavior == &actChest ) @@ -1020,15 +1011,6 @@ void spellEffectDrainSoul(Entity& my, spellElement_t& element, Entity* parent, i damage += (my.actmagicSpellbookBonus * damage); damage /= (1 + (int)resistance); hit.entity->chestHandleDamageMagic(damage, my, parent); - if ( my.actmagicProjectileArc > 0 ) - { - spawnMagicTower(parent, my.x, my.y, SPELL_DRAIN_SOUL, nullptr); - } - if ( !(my.actmagicIsOrbiting == 2) ) - { - my.removeLightField(); - list_RemoveNode(my.mynode); - } return; } else if ( hit.entity->behavior == &actFurniture ) @@ -1074,10 +1056,6 @@ void spellEffectDrainSoul(Entity& my, spellElement_t& element, Entity* parent, i } } playSoundEntity(hit.entity, 28, 128); - if ( my.actmagicProjectileArc > 0 ) - { - spawnMagicTower(parent, my.x, my.y, SPELL_DRAIN_SOUL, nullptr); - } } } } From 6e7bd6851fcdee3c38cf9f7593e011c2eb154915 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 17 Oct 2023 08:57:29 +1100 Subject: [PATCH 138/146] * update main menu banner url --- src/ui/MainMenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 73d36c4f6..f8f44ed9c 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -22752,7 +22752,7 @@ namespace MainMenu { void(*banner_funcs[])(Button&) = { [](Button&) { // banner #1 - openURLTryWithOverlay("https://www.baronygame.com/blog/life-after-death-announcement"); + openURLTryWithOverlay("https://www.baronygame.com/blog/410-update-summary"); }, [](Button&) { // banner #2 openDLCPrompt(enabledDLCPack1 ? 1 : 0); From a2fba1768e600f9e895f13dddffcbf45a4a191a1 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 17 Oct 2023 12:43:01 +1100 Subject: [PATCH 139/146] * remove unneeded real_t cast to max operation --- src/stat_shared.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stat_shared.cpp b/src/stat_shared.cpp index d21efd3cd..62688f00c 100644 --- a/src/stat_shared.cpp +++ b/src/stat_shared.cpp @@ -78,7 +78,7 @@ Stat::Stat(Sint32 sprite) : this->RANDOM_MAXMP = 0; this->RANDOM_MP = 0; int c; - for ( c = 0; c < std::max(NUMPROFICIENCIES, NUMEFFECTS); c++ ) + for ( c = 0; c < std::max(NUMPROFICIENCIES, NUMEFFECTS); c++ ) { if ( c < NUMPROFICIENCIES ) { From 3bd1c14409ccd5b8842a3a7dab62b5e0f6c44099 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 19 Oct 2023 03:06:09 +1100 Subject: [PATCH 140/146] * damage gib coloring for magic --- src/actbeartrap.cpp | 4 +- src/actthrown.cpp | 4 +- src/magic/actmagic.cpp | 64 ++++++++++++++++++++---------- src/magic/magic.cpp | 90 +++++++++++++++++++++++++++++++++++++----- 4 files changed, 129 insertions(+), 33 deletions(-) diff --git a/src/actbeartrap.cpp b/src/actbeartrap.cpp index d71517b25..15e37abc1 100644 --- a/src/actbeartrap.cpp +++ b/src/actbeartrap.cpp @@ -629,12 +629,12 @@ void bombDoEffect(Entity* my, Entity* triggered, real_t entityDistance, bool spa if ( !strcmp(stat->name, "") ) { updateEnemyBar(parent, triggered, getMonsterLocalizedName(stat->type).c_str(), stat->HP, stat->MAXHP, - false, DamageGib::DMG_TODO); + false, DamageGib::DMG_DEFAULT); } else { updateEnemyBar(parent, triggered, stat->name, stat->HP, stat->MAXHP, - false, DamageGib::DMG_TODO); + false, DamageGib::DMG_DEFAULT); } Entity* gib = spawnGib(triggered); serverSpawnGibForClient(gib); diff --git a/src/actthrown.cpp b/src/actthrown.cpp index 470236f71..b0d5f1abc 100644 --- a/src/actthrown.cpp +++ b/src/actthrown.cpp @@ -1167,12 +1167,12 @@ void actThrown(Entity* my) if ( !strcmp(hitstats->name, "") ) { updateEnemyBar(parent, hit.entity, getMonsterLocalizedName(hitstats->type).c_str(), hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, DamageGib::DMG_DEFAULT); } else { updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, DamageGib::DMG_DEFAULT); } } diff --git a/src/magic/actmagic.cpp b/src/magic/actmagic.cpp index a67bb8835..3c5466297 100644 --- a/src/magic/actmagic.cpp +++ b/src/magic/actmagic.cpp @@ -1243,11 +1243,35 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. // check for magic resistance... // resistance stacks diminishingly int resistance = 0; + DamageGib dmgGib = DMG_DEFAULT; + real_t damageMultiplier = 1.0; if ( hit.entity ) { resistance = Entity::getMagicResistance(hit.entity->getStats()); - - // TODO - magic impact weak/strong messages? + if ( (hit.entity->behavior == &actMonster || hit.entity->behavior == &actPlayer) && hitstats ) + { + damageMultiplier = Entity::getDamageTableMultiplier(hit.entity, *hitstats, DAMAGE_TABLE_MAGIC); + if ( damageMultiplier <= 0.75 ) + { + dmgGib = DMG_WEAKEST; + } + else if ( damageMultiplier <= 0.85 ) + { + dmgGib = DMG_WEAKER; + } + else if ( damageMultiplier >= 1.25 ) + { + dmgGib = resistance == 0 ? DMG_STRONGEST : DMG_WEAKER; + } + else if ( damageMultiplier >= 1.15 ) + { + dmgGib = resistance == 0 ? DMG_STRONGER : DMG_WEAKER; + } + else if ( resistance > 0 ) + { + dmgGib = DMG_WEAKEST; + } + } } real_t spellbookDamageBonus = (my->actmagicSpellbookBonus / 100.f); @@ -1274,7 +1298,7 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. int damage = element->damage; damage += (spellbookDamageBonus * damage); //damage += ((element->mana - element->base_mana) / static_cast(element->overload_multiplier)) * element->damage; - damage *= Entity::getDamageTableMultiplier(hit.entity, *hitstats, DAMAGE_TABLE_MAGIC); + damage *= damageMultiplier; damage /= (1 + (int)resistance); hit.entity->modHP(-damage); for (i = 0; i < damage; i += 2) //Spawn a gib for every two points of damage. @@ -1292,12 +1316,12 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. if ( !strcmp(hitstats->name, "") ) { updateEnemyBar(parent, hit.entity, getMonsterLocalizedName(hitstats->type).c_str(), hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } else { updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } if ( hitstats->HP <= 0 && parent) @@ -1406,7 +1430,7 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. } - damage *= Entity::getDamageTableMultiplier(hit.entity, *hitstats, DAMAGE_TABLE_MAGIC); + damage *= damageMultiplier; damage /= (1 + (int)resistance); hit.entity->modHP(-damage); for (i = 0; i < damage; i += 2) //Spawn a gib for every two points of damage. @@ -1425,12 +1449,12 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. if ( !strcmp(hitstats->name, "") ) { updateEnemyBar(parent, hit.entity, getMonsterLocalizedName(hitstats->type).c_str(), hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } else { updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } if ( hitstats->HP <= 0 && parent) @@ -1621,7 +1645,7 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. } damage = damage - local_rng.rand() % ((damage / 8) + 1); } - damage *= Entity::getDamageTableMultiplier(hit.entity, *hitstats, DAMAGE_TABLE_MAGIC); + damage *= damageMultiplier; if ( parent ) { Stat* casterStats = parent->getStats(); @@ -1672,12 +1696,12 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. if ( !strcmp(hitstats->name, "") ) { updateEnemyBar(parent, hit.entity, getMonsterLocalizedName(hitstats->type).c_str(), hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } else { updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } if ( oldHP > 0 && hitstats->HP <= 0 ) { @@ -1940,7 +1964,7 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. } //damage += ((element->mana - element->base_mana) / static_cast(element->overload_multiplier)) * element->damage; int oldHP = hitstats->HP; - damage *= Entity::getDamageTableMultiplier(hit.entity, *hitstats, DAMAGE_TABLE_MAGIC); + damage *= damageMultiplier; damage /= (1 + (int)resistance); hit.entity->modHP(-damage); Entity* gib = spawnGib(hit.entity); @@ -1956,12 +1980,12 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. if ( !strcmp(hitstats->name, "") ) { updateEnemyBar(parent, hit.entity, getMonsterLocalizedName(hitstats->type).c_str(), hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } else { updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } if ( parent ) { @@ -2166,7 +2190,7 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. } //damage += ((element->mana - element->base_mana) / static_cast(element->overload_multiplier)) * element->damage; int oldHP = hitstats->HP; - damage *= Entity::getDamageTableMultiplier(hit.entity, *hitstats, DAMAGE_TABLE_MAGIC); + damage *= damageMultiplier; damage /= (1 + (int)resistance); hit.entity->modHP(-damage); @@ -2180,12 +2204,12 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. if ( !strcmp(hitstats->name, "") ) { updateEnemyBar(parent, hit.entity, getMonsterLocalizedName(hitstats->type).c_str(), hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } else { updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } if ( oldHP > 0 && hitstats->HP <= 0 && parent) { @@ -2721,7 +2745,7 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. int damage = element->damage; damage += (spellbookDamageBonus * damage); //damage += ((element->mana - element->base_mana) / static_cast(element->overload_multiplier)) * element->damage; - damage *= Entity::getDamageTableMultiplier(hit.entity, *hitstats, DAMAGE_TABLE_MAGIC); + damage *= damageMultiplier; Stat* casterStats = nullptr; if ( parent ) { @@ -2795,12 +2819,12 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. if ( !strcmp(hitstats->name, "") ) { updateEnemyBar(parent, hit.entity, getMonsterLocalizedName(hitstats->type).c_str(), hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } else { updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } if ( hitstats->HP <= 0 && parent ) diff --git a/src/magic/magic.cpp b/src/magic/magic.cpp index 770f304fc..aa9975fd4 100644 --- a/src/magic/magic.cpp +++ b/src/magic/magic.cpp @@ -260,11 +260,36 @@ void spellEffectAcid(Entity& my, spellElement_t& element, Entity* parent, int re resistance += 2; hasamulet = true; } + + DamageGib dmgGib = DMG_DEFAULT; + real_t damageMultiplier = Entity::getDamageTableMultiplier(hit.entity, *hitstats, DAMAGE_TABLE_MAGIC); + if ( damageMultiplier <= 0.75 ) + { + dmgGib = DMG_WEAKEST; + } + else if ( damageMultiplier <= 0.85 ) + { + dmgGib = DMG_WEAKER; + } + else if ( damageMultiplier >= 1.25 ) + { + dmgGib = resistance == 0 ? DMG_STRONGEST : DMG_WEAKER; + } + else if ( damageMultiplier >= 1.15 ) + { + dmgGib = resistance == 0 ? DMG_STRONGER : DMG_WEAKER; + } + else if ( resistance > 0 ) + { + dmgGib = DMG_WEAKEST; + } + int oldHP = hitstats->HP; damage /= (1 + (int)resistance); - damage *= Entity::getDamageTableMultiplier(hit.entity, *hitstats, DAMAGE_TABLE_MAGIC); + damage *= damageMultiplier; hit.entity->modHP(-damage); + // write the obituary if ( parent ) { @@ -318,12 +343,12 @@ void spellEffectAcid(Entity& my, spellElement_t& element, Entity* parent, int re if ( !strcmp(hitstats->name, "") ) { updateEnemyBar(parent, hit.entity, getMonsterLocalizedName(hitstats->type).c_str(), hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } else { updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } if ( oldHP > 0 && hitstats->HP <= 0 && parent ) @@ -417,8 +442,32 @@ void spellEffectPoison(Entity& my, spellElement_t& element, Entity* parent, int resistance += 2; hasamulet = true; } + + DamageGib dmgGib = DMG_DEFAULT; + real_t damageMultiplier = Entity::getDamageTableMultiplier(hit.entity, *hitstats, DAMAGE_TABLE_MAGIC); + if ( damageMultiplier <= 0.75 ) + { + dmgGib = DMG_WEAKEST; + } + else if ( damageMultiplier <= 0.85 ) + { + dmgGib = DMG_WEAKER; + } + else if ( damageMultiplier >= 1.25 ) + { + dmgGib = resistance == 0 ? DMG_STRONGEST : DMG_WEAKER; + } + else if ( damageMultiplier >= 1.15 ) + { + dmgGib = resistance == 0 ? DMG_STRONGER : DMG_WEAKER; + } + else if ( resistance > 0 ) + { + dmgGib = DMG_WEAKEST; + } + damage /= (1 + (int)resistance); - damage *= Entity::getDamageTableMultiplier(hit.entity, *hitstats, DAMAGE_TABLE_MAGIC); + damage *= damageMultiplier; hit.entity->modHP(-damage); // write the obituary @@ -458,12 +507,12 @@ void spellEffectPoison(Entity& my, spellElement_t& element, Entity* parent, int if ( !strcmp(hitstats->name, "") ) { updateEnemyBar(parent, hit.entity, getMonsterLocalizedName(hitstats->type).c_str(), hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } else { updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } if ( hitstats->HP <= 0 && parent ) @@ -871,11 +920,34 @@ void spellEffectDrainSoul(Entity& my, spellElement_t& element, Entity* parent, i return; } + DamageGib dmgGib = DMG_DEFAULT; + real_t damageMultiplier = Entity::getDamageTableMultiplier(hit.entity, *hitstats, DAMAGE_TABLE_MAGIC); + if ( damageMultiplier <= 0.75 ) + { + dmgGib = DMG_WEAKEST; + } + else if ( damageMultiplier <= 0.85 ) + { + dmgGib = DMG_WEAKER; + } + else if ( damageMultiplier >= 1.25 ) + { + dmgGib = resistance == 0 ? DMG_STRONGEST : DMG_WEAKER; + } + else if ( damageMultiplier >= 1.15 ) + { + dmgGib = resistance == 0 ? DMG_STRONGER : DMG_WEAKER; + } + else if ( resistance > 0 ) + { + dmgGib = DMG_WEAKEST; + } + int damage = element.damage; damage += damage * ((my.actmagicSpellbookBonus / 100.f) + getBonusFromCasterOfSpellElement(parent, nullptr, &element)); //damage += ((element->mana - element->base_mana) / static_cast(element->overload_multiplier)) * element->damage; damage /= (1 + (int)resistance); - damage *= Entity::getDamageTableMultiplier(hit.entity, *hitstats, DAMAGE_TABLE_MAGIC); + damage *= damageMultiplier; if ( parent ) { @@ -912,12 +984,12 @@ void spellEffectDrainSoul(Entity& my, spellElement_t& element, Entity* parent, i if ( !strcmp(hitstats->name, "") ) { updateEnemyBar(parent, hit.entity, getMonsterLocalizedName(hitstats->type).c_str(), hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } else { updateEnemyBar(parent, hit.entity, hitstats->name, hitstats->HP, hitstats->MAXHP, - false, DamageGib::DMG_TODO); + false, dmgGib); } Uint32 color = makeColorRGB(255, 0, 0); From 9a0e16537c36b09ae0495208cf89733f988b2399 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 19 Oct 2023 03:07:42 +1100 Subject: [PATCH 141/146] * hash for items.json --- src/mod_tools.cpp | 22 +++++++++++++++++++++- src/mod_tools.hpp | 2 ++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/mod_tools.cpp b/src/mod_tools.cpp index a9c4465e1..72a49d0fc 100644 --- a/src/mod_tools.cpp +++ b/src/mod_tools.cpp @@ -595,6 +595,8 @@ void IRCHandler_t::handleMessage(std::string& msg) } #endif // !NINTENDO +Uint32 ItemTooltips_t::itemsJsonHashRead = 0; + void ItemTooltips_t::readItemsFromFile() { printlog("loading items...\n"); @@ -686,7 +688,8 @@ void ItemTooltips_t::readItemsFromFile() //itemValueTable.clear(); //itemValueTableByCategory.clear(); - + Uint32 shift = 0; + Uint32 hash = 0; for ( int i = 0; i < NUMITEMS && i < itemsRead; ++i ) { assert(i == tmpItems[i].itemId); @@ -827,6 +830,9 @@ void ItemTooltips_t::readItemsFromFile() items[i].item_slot = ItemEquippableSlot::EQUIPPABLE_IN_SLOT_HELM; } + hash += (Uint32)((Uint32)items[i].weight << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)items[i].value << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)items[i].level << (shift % 32)); ++shift; /*{ auto pair = std::make_pair(items[i].value, i); auto lower = std::lower_bound(itemValueTable.begin(), itemValueTable.end(), pair, @@ -846,6 +852,16 @@ void ItemTooltips_t::readItemsFromFile() }*/ } + itemsJsonHashRead = hash; + if ( itemsJsonHashRead != kItemsJsonHash ) + { + printlog("[JSON]: Notice: items.json unknown hash, achievements are disabled: %d", itemsJsonHashRead); + } + else + { + printlog("[JSON]: items.json hash verified successfully."); + } + spellItems.clear(); int spellsRead = 0; @@ -8148,6 +8164,10 @@ void Mods::verifyAchievements(const char* fullpath, bool ignoreBaseFolder) { disableSteamAchievements = true; } + if ( ItemTooltips_t::itemsJsonHashRead != ItemTooltips_t::kItemsJsonHash ) + { + disableSteamAchievements = true; + } } bool Mods::isPathInMountedFiles(std::string findStr) diff --git a/src/mod_tools.hpp b/src/mod_tools.hpp index 5313090f3..27de66fd3 100644 --- a/src/mod_tools.hpp +++ b/src/mod_tools.hpp @@ -2715,6 +2715,8 @@ class ItemTooltips_t void setColorFaintText(Uint32 color) { faintTextColor = color; } }; void readItemsFromFile(); + static const Uint32 kItemsJsonHash = 255104943; + static Uint32 itemsJsonHashRead; void readItemLocalizationsFromFile(bool forceLoadBaseDirectory = false); void readTooltipsFromFile(bool forceLoadBaseDirectory = false); std::vector tmpItems; From 2ba1a26a223ec7cbb5f6c95232565918a9b20097 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 19 Oct 2023 03:08:19 +1100 Subject: [PATCH 142/146] * new hash method for savefiles, check more fields --- src/scores.cpp | 171 ++++++++++++++++++++++++++++++++++++++++++++++--- src/scores.hpp | 3 + 2 files changed, 165 insertions(+), 9 deletions(-) diff --git a/src/scores.cpp b/src/scores.cpp index 5048f4d16..f0cd3603f 100644 --- a/src/scores.cpp +++ b/src/scores.cpp @@ -3202,11 +3202,18 @@ SaveGameInfo getSaveGameInfo(bool singleplayer, int saveIndex) hash = tm->tm_hour + tm->tm_mday * tm->tm_year + tm->tm_wday + tm->tm_yday; } if (info.players.size() > info.player_num) { - auto& stats = info.players[info.player_num].stats; - hash += stats.STR + stats.LVL + stats.DEX * stats.INT; - hash += stats.CON * stats.PER + std::min(stats.GOLD, 5000) - stats.CON; - hash += stats.HP - stats.MP; - hash += info.dungeon_lvl; + if ( info.game_version < 410 ) + { + auto& stats = info.players[info.player_num].stats; + hash += stats.STR + stats.LVL + stats.DEX * stats.INT; + hash += stats.CON * stats.PER + std::min(stats.GOLD, 5000) - stats.CON; + hash += stats.HP - stats.MP; + hash += info.dungeon_lvl; + } + else + { + info.computeHash(info.player_num, hash); + } } if (hash != info.hash) { info.hash = 0; @@ -3404,6 +3411,11 @@ void updatePlayerConductsInMainLoop() conductGameChallenges[CONDUCT_CHEATS_ENABLED] = 1; } } + if ( ItemTooltips_t::itemsJsonHashRead != ItemTooltips_t::kItemsJsonHash ) + { + conductGameChallenges[CONDUCT_MODDED] = 1; + Mods::disableSteamAchievements = true; + } if ( !conductGameChallenges[CONDUCT_MODDED_NO_ACHIEVEMENTS] ) { if ( Mods::disableSteamAchievements @@ -5280,6 +5292,139 @@ SteamGlobalStatIndexes getIndexForDeathType(int type) return STEAM_GSTAT_INVALID; } +void SaveGameInfo::computeHash(const int playernum, Uint32& hash) +{ + if ( players.size() <= playernum ) + { + return; + } + + Uint32 shift = 0; + hash += (Uint32)((Uint32)gamekey << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)mapseed << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)gametimer << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)svflags << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)multiplayer_type << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)dungeon_lvl << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)level_track << (shift % 32)); ++shift; + + auto& player = players[playernum]; + hash += (Uint32)((Uint32)player.char_class << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)player.race << (shift % 32)); ++shift; + + for ( auto k : player.kills ) + { + hash += (Uint32)((Uint32)k << (shift % 32)); ++shift; + } + + hash += (Uint32)((Uint32)conductPenniless << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)conductFoodless << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)conductVegetarian << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)conductIlliterate << (shift % 32)); ++shift; + for ( int i = 0; i < NUM_CONDUCT_CHALLENGES; ++i ) + { + hash += (Uint32)((Uint32)player.additionalConducts[i] << (shift % 32)); ++shift; + } + for ( int i = 0; i < NUM_GAMEPLAY_STATISTICS; ++i ) + { + hash += (Uint32)((Uint32)player.gameStatistics[i] << (shift % 32)); ++shift; + } + for ( int i = 0; i < NUM_HOTBAR_SLOTS; ++i ) + { + hash += (Uint32)((Uint32)player.hotbar[i] << (shift % 32)); ++shift; + for ( int j = 0; j < NUM_HOTBAR_ALTERNATES; ++j ) + { + hash += (Uint32)((Uint32)player.hotbar_alternate[j][i] << (shift % 32)); ++shift; + } + } + for ( auto k : player.spells ) + { + hash += (Uint32)((Uint32)k << (shift % 32)); ++shift; + } + + std::vector statsArr; + statsArr.push_back(&players[playernum].stats); + for ( auto& s : players[playernum].followers ) + { + statsArr.push_back(&s); + } + + for ( auto stats : statsArr ) + { + hash += (Uint32)((Uint32)stats->type << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->sex << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->appearance << (shift % 32)); ++shift; + + hash += (Uint32)((Uint32)stats->HP << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->maxHP << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->MP << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->maxMP << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->STR << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->DEX << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->CON << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->INT << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->PER << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->CHR << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->EXP << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->LVL << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->GOLD << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)stats->HUNGER << (shift % 32)); ++shift; + + for ( auto k : stats->PROFICIENCIES ) + { + hash += (Uint32)((Uint32)k << (shift % 32)); ++shift; + } + for ( auto k : stats->EFFECTS ) + { + hash += (Uint32)((Uint32)k << (shift % 32)); ++shift; + } + for ( auto k : stats->EFFECTS_TIMERS ) + { + hash += (Uint32)((Uint32)k << (shift % 32)); ++shift; + } + for ( auto k : stats->MISC_FLAGS ) + { + hash += (Uint32)((Uint32)k << (shift % 32)); ++shift; + } + for ( auto& pair : stats->player_equipment ) + { + hash += (Uint32)((Uint32)pair.second << (shift % 32)); ++shift; + } + for ( auto& pair : stats->npc_equipment ) + { + pair.second.computeHash(hash, shift); + } + for ( auto& item : stats->inventory ) + { + item.computeHash(hash, shift); + } + for ( auto& bag : stats->player_lootbags ) + { + hash += (Uint32)((Uint32)bag.first << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)bag.second.spawn_x << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)bag.second.spawn_y << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)bag.second.looted << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)bag.second.spawnedOnGround << (shift % 32)); ++shift; + for ( auto& item : bag.second.items ) + { + item.computeHash(hash, shift); + } + } + } +} + +void SaveGameInfo::Player::stat_t::item_t::computeHash(Uint32& hash, Uint32& shift) +{ + hash += (Uint32)((Uint32)type << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)status << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)appearance << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)beatitude << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)count << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)identified << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)x << (shift % 32)); ++shift; + hash += (Uint32)((Uint32)y << (shift % 32)); ++shift; +} + int saveGame(int saveIndex) { if (gameModeManager.getMode() != GameModeManager_t::GameModes::GAME_MODE_DEFAULT) { return 1; // can't save tutorial games @@ -5300,10 +5445,13 @@ int saveGame(int saveIndex) { // savefile hash info.hash = tm->tm_hour + tm->tm_mday * tm->tm_year + tm->tm_wday + tm->tm_yday; - info.hash += stats[clientnum]->STR + stats[clientnum]->LVL + stats[clientnum]->DEX * stats[clientnum]->INT; - info.hash += stats[clientnum]->CON * stats[clientnum]->PER + std::min(stats[clientnum]->GOLD, 5000) - stats[clientnum]->CON; - info.hash += stats[clientnum]->HP - stats[clientnum]->MP; - info.hash += currentlevel; + if ( info.game_version < 410 ) + { + info.hash += stats[clientnum]->STR + stats[clientnum]->LVL + stats[clientnum]->DEX * stats[clientnum]->INT; + info.hash += stats[clientnum]->CON * stats[clientnum]->PER + std::min(stats[clientnum]->GOLD, 5000) - stats[clientnum]->CON; + info.hash += stats[clientnum]->HP - stats[clientnum]->MP; + info.hash += currentlevel; + } // game info info.gamename = stats[clientnum]->name; @@ -5682,6 +5830,11 @@ int saveGame(int saveIndex) { static ConsoleVariable cvar_saveText("/save_text_format", true); + if ( info.game_version >= 410 ) + { + info.computeHash(info.player_num, info.hash); + } + char path[PATH_MAX] = ""; std::string savefile = setSaveGameFileName(multiplayer == SINGLE, SaveFileType::JSON, saveIndex); completePath(path, savefile.c_str(), outputdir); diff --git a/src/scores.hpp b/src/scores.hpp index 8b67471c8..8209eae7c 100644 --- a/src/scores.hpp +++ b/src/scores.hpp @@ -427,6 +427,7 @@ struct SaveGameInfo { fp->property("y", y); return true; } + void computeHash(Uint32& hash, Uint32& shift); }; struct lootbag_t @@ -590,6 +591,8 @@ struct SaveGameInfo { fp->property("map_messages", map_messages); return true; } + + void computeHash(const int playernum, Uint32& hash); }; int saveGame(int saveIndex = savegameCurrentFileIndex); From 935dee5091e86645178034b948303c199b69242e Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 21 Oct 2023 01:41:53 +1100 Subject: [PATCH 143/146] * all players dead prompt --- src/player.hpp | 2 ++ src/ui/GameUI.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/src/player.hpp b/src/player.hpp index 60437dd2d..d1c541426 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -1656,6 +1656,8 @@ class Player static int actionPromptIconSize; static int actionPromptIconOpacity; static int actionPromptIconBackingOpacity; + real_t animDeadPrompt = 0.0; + bool animDeadPromptDisplay = false; int offsetHUDAboveHotbarHeight = 0; void updateEnemyBar(Frame* whichFrame); void updateEnemyBar2(Frame* whichFrame, void* enemyHPDetails); diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 96915008a..b8ced98d6 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -10182,6 +10182,71 @@ static void checkControllerState(int player) { controllerFrame->setHollow(true); } +void HUDDrawGameEndHint(const int player, SDL_Rect rect) +{ + if ( multiplayer == CLIENT || multiplayer == SERVER ) + { + bool everyonedead = true; + for ( int i = 0; i < MAXPLAYERS; ++i ) + { + if ( players[i] ) + { + if ( multiplayer == SERVER && (!client_disconnected[i] && players[i]->entity) ) + { + everyonedead = false; + } + else if ( multiplayer == CLIENT && players[i]->entity ) + { + everyonedead = false; + } + } + } + + if ( players[player]->bControlEnabled ) + { + players[player]->hud.animDeadPromptDisplay = true; + } + + if ( everyonedead && players[player]->hud.animDeadPromptDisplay ) + { + static ConsoleVariable cvar_anim_dead_prompt_speed("/anim_dead_prompt_speed", 0.003); + players[player]->hud.animDeadPrompt += *cvar_anim_dead_prompt_speed; + if ( players[player]->hud.animDeadPrompt >= 1.0 ) + { + players[player]->hud.animDeadPrompt = 0.0; + } + rect.x += rect.w / 2; + rect.y += 8 - 1; + if ( players[player]->hud.xpFrame && !players[player]->hud.xpFrame->isDisabled() ) + { + rect.y += players[player]->hud.xpFrame->getSize().y; + rect.y += players[player]->hud.xpFrame->getSize().h; + } + if ( auto textGet = Text::get(Language::get(6052), smallfont_outline, makeColorRGB(255, 255, 255), 0) ) + { + Uint8 r, g, b, a; + getColor(hudColors.characterSheetRed, &r, &g, &b, nullptr); + real_t opacity = 0.5 + .4 * (1.0 * cos(players[player]->hud.animDeadPrompt * 2 * PI) + 1.0); + Uint32 color = makeColor(r, g, b, std::max(0.25, std::min(opacity, 1.0)) * 255); + rect.x -= textGet->getWidth() / 2; + textGet->drawColor(SDL_Rect{ 0, 0, 0, 0 }, SDL_Rect{ rect.x, rect.y, 0, 0 }, + SDL_Rect{ 0, 0, Frame::virtualScreenX, Frame::virtualScreenY }, + color); + } + } + else + { + players[player]->hud.animDeadPromptDisplay = false; + players[player]->hud.animDeadPrompt = 0.0; + } + } + else + { + players[player]->hud.animDeadPromptDisplay = false; + players[player]->hud.animDeadPrompt = 0.0; + } +} + void Player::HUD_t::processHUD() { const SDL_Rect hudSize{ @@ -10198,6 +10263,9 @@ void Player::HUD_t::processHUD() hudFrame->setHollow(true); hudFrame->setBorder(0); hudFrame->setOwner(player.playernum); + hudFrame->setDrawCallback([](const Widget& widget, SDL_Rect rect) { + HUDDrawGameEndHint(widget.getOwner(), rect); + }); } if ( !minotaurSharedDisplay && player.playernum == 0 ) From 368aa96df6b9efb514d5946181f77cba96ee393c Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 21 Oct 2023 01:42:05 +1100 Subject: [PATCH 144/146] * debug code for player door collision testing --- src/actplayer.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index f759fa008..d83736e12 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -7539,6 +7539,46 @@ void actPlayer(Entity* my) { hit.entity->doorHealth = 0; } + //else if ( hit.entity->behavior == &actDoorFrame && + // hit.entity->flags[INVISIBLE] ) + //{ + // // code that almost fixes door frame collision + // if ( hit.entity->yaw >= -0.1 && hit.entity->yaw <= 0.1 ) + // { + // // east/west doorway + // if ( my->y < floor(hit.entity->y / 16) * 16 + 8 ) + // { + // // slide south + // PLAYER_VELX = 0; + // PLAYER_VELY = .25; + // } + // else + // { + // // slide north + // PLAYER_VELX = 0; + // PLAYER_VELY = -.25; + // } + // } + // else + // { + // // north/south doorway + // if ( my->x < floor(hit.entity->x / 16) * 16 + 8 ) + // { + // // slide east + // PLAYER_VELX = .25; + // PLAYER_VELY = 0; + // } + // else + // { + // // slide west + // PLAYER_VELX = -.25; + // PLAYER_VELY = 0; + // } + // } + // my->x += PLAYER_VELX; + // my->y += PLAYER_VELY; + // dist = sqrt(PLAYER_VELX * PLAYER_VELX + PLAYER_VELY * PLAYER_VELY); + //} } } else From 80d34725a88b38640e7bd63d5d767bdea5176d1b Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 21 Oct 2023 01:46:23 +1100 Subject: [PATCH 145/146] * stop ghost magic destroying magic reflection --- src/magic/actmagic.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/magic/actmagic.cpp b/src/magic/actmagic.cpp index 3c5466297..adca14aa1 100644 --- a/src/magic/actmagic.cpp +++ b/src/magic/actmagic.cpp @@ -944,7 +944,11 @@ void actMagicMissile(Entity* my) //TODO: Verify this function. // Only degrade the equipment if Friendly Fire is ON or if it is (OFF && target is an enemy) bool bShouldEquipmentDegrade = false; - if ( (svFlags & SV_FLAG_FRIENDLYFIRE) ) + if ( parent && parent->behavior == &actDeathGhost ) + { + bShouldEquipmentDegrade = false; + } + else if ( (svFlags & SV_FLAG_FRIENDLYFIRE) ) { // Friendly Fire is ON, equipment should always degrade, as hit will register bShouldEquipmentDegrade = true; From 5cbf752456ce2a65191a6d5b3645a672f6efe055 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 21 Oct 2023 01:46:37 +1100 Subject: [PATCH 146/146] * lang update --- lang/en.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/lang/en.txt b/lang/en.txt index 91f8baa23..2a25907ea 100644 --- a/lang/en.txt +++ b/lang/en.txt @@ -6205,5 +6205,6 @@ intro - God Rest Ye Merry Gentlemen by NaturesEye# 6049 View Wheel# 6050 SPECTATE# 6051 SPAWN AS GHOST# +6052 Your party has been wiped out...# 6100 end#