From c9345d905f9ce46e6a0cd803fd6d66e8191af974 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sun, 19 Sep 2021 01:59:38 +1000 Subject: [PATCH 01/39] * fix scaling for non-720p in Field() - lost in merge --- src/ui/Field.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ui/Field.cpp b/src/ui/Field.cpp index f2522be8e..785b90865 100644 --- a/src/ui/Field.cpp +++ b/src/ui/Field.cpp @@ -228,10 +228,10 @@ void Field::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector(parent)->getOpacity() < 100.0 ) { From 545b19f51e5c4113ee70b67140393dd8f0311d3e Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Fri, 24 Sep 2021 00:56:40 +1000 Subject: [PATCH 02/39] * move books to after loading screen because issues in release build trying to render text. * add some nullptr guards for Text() rendering for book compiling - returns nullptrs for some reason? --- src/book.cpp | 1 + src/init_game.cpp | 2 +- src/ui/Text.cpp | 29 +++++++++++++++++++---------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/book.cpp b/src/book.cpp index 6f92b27dd..d80e69a5b 100644 --- a/src/book.cpp +++ b/src/book.cpp @@ -358,6 +358,7 @@ void BookParser_t::writeCompiledBooks() std::string inputPath = outputdir; inputPath.append(PHYSFS_getDirSeparator()); std::string fileName = "books/compiled_books.json"; + inputPath.append(fileName); File* fp = FileIO::open(inputPath.c_str(), "rb"); rapidjson::Document d; diff --git a/src/init_game.cpp b/src/init_game.cpp index e035aece3..39485c2f9 100644 --- a/src/init_game.cpp +++ b/src/init_game.cpp @@ -282,7 +282,6 @@ int initGame() } } FileIO::close(fp); - bookParser_t.createBooks(false); setupSpells(); #ifdef NINTENDO @@ -466,6 +465,7 @@ int initGame() int result = loading_task.get(); if (result == 0) { + bookParser_t.createBooks(false); Text::dumpCache(); // createBooks makes some invalid Text() surfaces, this cleans them up for re-rendering. gameModeManager.Tutorial.init(); diff --git a/src/ui/Text.cpp b/src/ui/Text.cpp index e3be601e1..d6fa8fa0e 100644 --- a/src/ui/Text.cpp +++ b/src/ui/Text.cpp @@ -151,16 +151,22 @@ void Text::render() { surf = newSurf; // load the new surface as a GL texture - SDL_LockSurface(surf); - glBindTexture(GL_TEXTURE_2D, texid); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); - SDL_UnlockSurface(surf); - - rendered = true; + if ( surf ) + { + SDL_LockSurface(surf); + glBindTexture(GL_TEXTURE_2D, texid); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); + SDL_UnlockSurface(surf); + rendered = true; + } + else + { + rendered = false; + } } void Text::draw(const SDL_Rect src, const SDL_Rect dest, const SDL_Rect viewport) { @@ -174,6 +180,9 @@ void Text::drawColor(const SDL_Rect _src, const SDL_Rect _dest, const SDL_Rect v if (!rendered) { return; } + if ( !surf ) { + return; + } glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); From ed0837eefbfd9c16b5310c89e1bd430c575aeab1 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 30 Sep 2021 00:13:46 +1000 Subject: [PATCH 03/39] * status effect enemy bar stuff - WIP --- src/entity_editor.cpp | 6 ++ src/init.cpp | 8 +++ src/interface/interface.cpp | 55 ++++++++++++++++- src/interface/interface.hpp | 11 ++++ src/interface/magicgui.cpp | 118 ++++++++++++++++++++++++++++++++++++ src/opengl.cpp | 11 ++-- 6 files changed, 204 insertions(+), 5 deletions(-) diff --git a/src/entity_editor.cpp b/src/entity_editor.cpp index e8c399654..cdab4047f 100644 --- a/src/entity_editor.cpp +++ b/src/entity_editor.cpp @@ -462,6 +462,12 @@ void actSpriteWorldTooltip(Entity* my) return; } +void actDamageGib(Entity* my) +{ + // dummy function + return; +} + void actSpriteWorldTooltip(Entity* my); void actGoldBag(Entity* my) diff --git a/src/init.cpp b/src/init.cpp index a2dd89a55..7d1c1a7a9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -395,7 +395,9 @@ int initApp(char const * const title, int fullscreen) } // init new ui engine +#ifndef EDITOR Frame::guiInit(); +#endif // cache language entries bool cacheText = false; @@ -2041,7 +2043,9 @@ int deinitApp() Text::dumpCache(); Image::dumpCache(); Font::dumpCache(); +#ifndef EDITOR Frame::guiDestroy(); +#endif printlog("freeing map data...\n"); if ( map.entities != NULL ) @@ -2621,7 +2625,9 @@ bool changeVideoMode() glDeleteTextures(MAXTEXTURES, texid); // destroy gui fbo +#ifndef EDITOR Frame::fboDestroy(); +#endif // delete vertex data if ( !disablevbos ) @@ -2689,7 +2695,9 @@ bool changeVideoMode() #endif // !EDITOR // create new frame fbo +#ifndef EDITOR Frame::fboInit(); +#endif #endif // success diff --git a/src/interface/interface.cpp b/src/interface/interface.cpp index dbfb66f6f..a3565504f 100644 --- a/src/interface/interface.cpp +++ b/src/interface/interface.cpp @@ -30,6 +30,7 @@ #include "../scores.hpp" #include "../scrolls.hpp" #include "../lobbies.hpp" +#include "../ui/GameUI.hpp" Uint32 svFlags = 30; Uint32 settings_svFlags = svFlags; @@ -8617,8 +8618,60 @@ void EnemyHPDamageBarHandler::addEnemyToList(Sint32 HP, Sint32 maxHP, Sint32 old details->animator.animateTicks = ticks; details->animator.damageTaken = std::max(-1, oldHP - HP); - spawnDamageGib(uidToEntity(uid), details->animator.damageTaken); + Entity* entity = uidToEntity(uid); + spawnDamageGib(entity, details->animator.damageTaken); lastEnemyUid = uid; + + if ( entity ) + { + details->worldX = entity->x; + details->worldY = entity->y; + if ( entity->behavior == &actDoor && entity->flags[PASSABLE] ) + { + if ( entity->doorStartAng == 0 ) + { + details->worldY -= 5; + } + else + { + details->worldX -= 5; + } + } + details->worldZ = entity->z + enemyBarSettings.getHeightOffset(entity); + details->screenDistance = enemyBarSettings.getScreenDistanceOffset(entity); + } + if ( entity && (entity->behavior == &actPlayer || entity->behavior == &actMonster) ) + { + if ( Stat* stat = entity->getStats() ) + { + details->enemy_statusEffects1 = 0; + details->enemy_statusEffects2 = 0; + details->enemy_statusEffectsLowDuration1 = 0; + details->enemy_statusEffectsLowDuration2 = 0; + for ( int i = 0; i < NUMEFFECTS; ++i ) + { + if ( stat->EFFECTS[i] ) + { + if ( i < 32 ) + { + details->enemy_statusEffects1 |= (1 << i); + if ( stat->EFFECTS_TIMERS[i] > 0 && stat->EFFECTS_TIMERS[i] < 5 * TICKS_PER_SECOND ) + { + details->enemy_statusEffectsLowDuration1 |= (1 << i); + } + } + else if ( i < 64 ) + { + details->enemy_statusEffects2 |= (1 << (i - 32)); + if ( stat->EFFECTS_TIMERS[i] > 0 && stat->EFFECTS_TIMERS[i] < 5 * TICKS_PER_SECOND ) + { + details->enemy_statusEffectsLowDuration2 |= (1 << i); + } + } + } + } + } + } } SDL_Rect getRectForSkillIcon(const int skill) diff --git a/src/interface/interface.hpp b/src/interface/interface.hpp index 53c7842f6..fede9624a 100644 --- a/src/interface/interface.hpp +++ b/src/interface/interface.hpp @@ -78,6 +78,10 @@ class EnemyHPDamageBarHandler Uint32 enemy_timer = 0; Uint32 enemy_bar_color = 0; Uint32 enemy_uid = 0; + Uint32 enemy_statusEffects1 = 0; + Uint32 enemy_statusEffects2 = 0; + Uint32 enemy_statusEffectsLowDuration1 = 0; + Uint32 enemy_statusEffectsLowDuration2 = 0; bool lowPriorityTick = false; bool shouldDisplay = true; bool hasDistanceCheck = false; @@ -114,6 +118,10 @@ class EnemyHPDamageBarHandler delete worldTexture; worldTexture = nullptr; } + if ( worldSurfaceSpriteStatusEffects ) { + SDL_FreeSurface(worldSurfaceSpriteStatusEffects); + worldSurfaceSpriteStatusEffects = nullptr; + } if ( worldSurfaceSprite ) { SDL_FreeSurface(worldSurfaceSprite); worldSurfaceSprite = nullptr; @@ -126,6 +134,8 @@ class EnemyHPDamageBarHandler real_t screenDistance = 0.0; TempTexture* worldTexture = nullptr; SDL_Surface* worldSurfaceSprite = nullptr; + SDL_Surface* worldSurfaceSpriteStatusEffects = nullptr; + SDL_Surface* blitEnemyBarStatusEffects(const int player); }; Uint32 enemy_bar_client_color = 0; @@ -295,6 +305,7 @@ void updateMagicGUI(); extern SDL_Surface* identifyGUI_img; void drawSustainedSpells(const int player); //Draws an icon for every sustained spell. +SDL_Surface* getStatusEffectSprite(Entity* entity, Stat* stat, const int effect, const int player); enum GUICurrentType { diff --git a/src/interface/magicgui.cpp b/src/interface/magicgui.cpp index 80a828d10..1092d4d90 100644 --- a/src/interface/magicgui.cpp +++ b/src/interface/magicgui.cpp @@ -197,6 +197,124 @@ // } //} +SDL_Surface* getStatusEffectSprite(Entity* entity, Stat* stat, const int effect, const int player) +{ + node_t* effectImageNode = nullptr; + SDL_Surface** sprite = nullptr; + + switch ( effect ) + { + case EFF_SLOW: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_SLOW); + break; + case EFF_BLEEDING: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_BLEED); + break; + case EFF_ASLEEP: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_SLEEP); + break; + case EFF_CONFUSED: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_CONFUSE); + break; + case EFF_PACIFY: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_CHARM_MONSTER); + break; + case EFF_FEAR: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_FEAR); + break; + case EFF_WEBBED: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_SPRAY_WEB); + break; + case EFF_MAGICAMPLIFY: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_AMPLIFY_MAGIC); + break; + case EFF_TROLLS_BLOOD: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_TROLLS_BLOOD); + break; + case EFF_FLUTTER: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_FLUTTER); + break; + case EFF_FAST: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_SPEED); + break; + case EFF_SHAPESHIFT: + if ( entity && entity->behavior == &actPlayer ) + { + switch ( entity->effectShapeshift ) + { + case RAT: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_RAT_FORM); + break; + case TROLL: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_TROLL_FORM); + break; + case SPIDER: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_SPIDER_FORM); + break; + case CREATURE_IMP: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_IMP_FORM); + break; + default: + break; + } + } + break; + case EFF_VAMPIRICAURA: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_VAMPIRIC_AURA); + break; + case EFF_PARALYZED: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_LIGHTNING); + break; + case EFF_DRUNK: + if ( effect_drunk_bmp ) + { + sprite = &effect_drunk_bmp; + } + break; + case EFF_POLYMORPH: + if ( effect_polymorph_bmp ) + { + sprite = &effect_polymorph_bmp; + } + break; + case EFF_WITHDRAWAL: + if ( effect_hungover_bmp ) + { + sprite = &effect_hungover_bmp; + } + break; + case EFF_POTION_STR: + sprite = &str_bmp64u; + break; + case EFF_LEVITATING: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_LEVITATION); + break; + case EFF_INVISIBLE: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_INVISIBILITY); + break; + case EFF_SHADOW_TAGGED: + effectImageNode = list_Node(&items[SPELL_ITEM].surfaces, SPELL_SHADOW_TAG); + break; + default: + effectImageNode = nullptr; + break; + } + + if ( effectImageNode || sprite ) + { + if ( !sprite ) + { + sprite = (SDL_Surface**)effectImageNode->element; + } + } + + if ( sprite ) + { + return *sprite; + } + return nullptr; +} + void drawSustainedSpells(const int player) { SDL_Surface** sprite; diff --git a/src/opengl.cpp b/src/opengl.cpp index 9eeeec77b..e938cdb2e 100644 --- a/src/opengl.cpp +++ b/src/opengl.cpp @@ -1060,18 +1060,21 @@ bool glDrawEnemyBarSprite(view_t* camera, int mode, void* enemyHPBarDetails, boo // //drawRect(&resPos, 0xFFFFFF00, 255); //} + int drawOffsetY = (enemybar->worldSurfaceSpriteStatusEffects ? -enemybar->worldSurfaceSpriteStatusEffects->h / 2 : 0); + drawOffsetY += enemybar->glWorldOffsetY; + if ( !doVisibilityCheckOnly ) { // draw quad glBegin(GL_QUADS); glTexCoord2f(0, 0); - glVertex3f(enemybar->screenDistance, GLfloat(sprite->h / 2) - enemybar->glWorldOffsetY, GLfloat(sprite->w / 2)); + glVertex3f(enemybar->screenDistance, GLfloat(sprite->h / 2) - drawOffsetY, GLfloat(sprite->w / 2)); glTexCoord2f(0, 1); - glVertex3f(enemybar->screenDistance, GLfloat(-sprite->h / 2) - enemybar->glWorldOffsetY, GLfloat(sprite->w / 2)); + glVertex3f(enemybar->screenDistance, GLfloat(-sprite->h / 2) - drawOffsetY, GLfloat(sprite->w / 2)); glTexCoord2f(1, 1); - glVertex3f(enemybar->screenDistance, GLfloat(-sprite->h / 2) - enemybar->glWorldOffsetY, GLfloat(-sprite->w / 2)); + glVertex3f(enemybar->screenDistance, GLfloat(-sprite->h / 2) - drawOffsetY, GLfloat(-sprite->w / 2)); glTexCoord2f(1, 0); - glVertex3f(enemybar->screenDistance, GLfloat(sprite->h / 2) - enemybar->glWorldOffsetY, GLfloat(-sprite->w / 2)); + glVertex3f(enemybar->screenDistance, GLfloat(sprite->h / 2) - drawOffsetY, GLfloat(-sprite->w / 2)); glEnd(); } glDepthRange(0, 1); From 5ddb71d27d5a5f98f26a0af381a8f3a20f8af902 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Thu, 30 Sep 2021 00:18:31 +1000 Subject: [PATCH 04/39] * missed status effect enemy bar WIP --- src/ui/GameUI.cpp | 178 ++++++++++++++++++++++++++++++++++++++-------- src/ui/GameUI.hpp | 19 ++++- 2 files changed, 167 insertions(+), 30 deletions(-) diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 29bc2b9ac..642625f8e 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -40,8 +40,9 @@ struct CustomColors_t Uint32 characterSheetGreen = 0xFFFFFFFF; Uint32 characterSheetRed = 0xFFFFFFFF; } hudColors; +EnemyBarSettings_t enemyBarSettings; -std::string getEnemyBarSpriteName(Entity* entity) +std::string EnemyBarSettings_t::getEnemyBarSpriteName(Entity* entity) { if ( !entity ) { return "default"; } @@ -77,22 +78,6 @@ std::string getEnemyBarSpriteName(Entity* entity) return "default"; } -struct EnemyBarSettings_t -{ - std::unordered_map heightOffsets; - std::unordered_map screenDistanceOffsets; - float getHeightOffset(Entity* entity) - { - if ( !entity ) { return 0.f; } - return heightOffsets[getEnemyBarSpriteName(entity)]; - } - float getScreenDistanceOffset(Entity* entity) - { - if ( !entity ) { return 0.f; } - return screenDistanceOffsets[getEnemyBarSpriteName(entity)]; - } -} enemyBarSettings; - void createHPMPBars(const int player) { auto& hud_t = players[player]->hud; @@ -1614,7 +1599,6 @@ void Player::CharacterSheet_t::updateAttributes() real_t resistance = 0.0; if ( players[player.playernum]->entity ) { - players[player.playernum]->entity; resistance = 100 - 100 / (players[player.playernum]->entity->getMagicResistance() + 1); } snprintf(buf, sizeof(buf), "%.f%%", resistance); @@ -3042,7 +3026,8 @@ void drawCharacterPreview(const int player, SDL_Rect pos) camera_charsheet.vang = PI / 20; camera_charsheet.winx = pos.x; - camera_charsheet.winy = pos.y; + // winy modification required due to new frame scaling method d49b1a5f34667432f2a2bd754c0abca3a09227c8 + camera_charsheet.winy = pos.y + (yres - Frame::virtualScreenY); //camera_charsheet.winx = x1 + 8; //camera_charsheet.winy = y1 + 8; @@ -3422,9 +3407,9 @@ void createPlayerInventory(const int player) charFrame->setDrawCallback([](Widget& widget, SDL_Rect pos) { drawCharacterPreview(widget.getOwner(), pos); }); - //charFrame->addImage(SDL_Rect{ 0, 0, charSize.w, charSize.h }, - // SDL_MapRGBA(mainsurface->format, 255, 255, 255, 255), - // "images/system/white.png", "inventory character preview bg"); + /*charFrame->addImage(SDL_Rect{ 0, 0, charSize.w, charSize.h }, + SDL_MapRGBA(mainsurface->format, 255, 255, 255, 255), + "images/system/white.png", "inventory character preview bg");*/ } /*auto selectedFrame = dollSlotsFrame->addFrame("paperdoll selected item"); @@ -4768,7 +4753,7 @@ void Player::HUD_t::updateXPBar() xpProgressEndCap->pos.x = xpProgress->pos.x + xpProgress->pos.w; } -SDL_Surface* blitEnemyBar(const int player) +SDL_Surface* blitEnemyBar(const int player, SDL_Surface* statusEffectSprite) { Frame* frame = players[player]->hud.enemyBarFrame; if ( !frame || !players[player]->isLocalPlayer() ) @@ -4790,7 +4775,15 @@ SDL_Surface* blitEnemyBar(const int player) auto skullFrame = frame->findFrame("skull frame"); int totalWidth = baseBg->pos.x + baseBg->pos.w + baseEndCap->pos.w; - SDL_Surface* sprite = SDL_CreateRGBSurface(0, totalWidth, frame->getSize().h, 32, + int totalHeight = frame->getSize().h; + int statusEffectOffsetY = 0; + if ( statusEffectSprite ) + { + statusEffectOffsetY = statusEffectSprite->h; + totalHeight += statusEffectOffsetY; + } + + SDL_Surface* sprite = SDL_CreateRGBSurface(0, totalWidth, totalHeight, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); for ( auto& img : frame->getImages() ) { @@ -4800,6 +4793,7 @@ SDL_Surface* blitEnemyBar(const int player) SDL_SetSurfaceAlphaMod(srcSurf, a * frameOpacity); SDL_SetSurfaceBlendMode(srcSurf, SDL_BLENDMODE_NONE); SDL_Rect pos = img->pos; + pos.y += statusEffectOffsetY; SDL_BlitScaled(srcSurf, nullptr, sprite, &pos); } for ( auto& img : dmgFrame->getImages() ) @@ -4812,6 +4806,7 @@ SDL_Surface* blitEnemyBar(const int player) SDL_Rect pos = img->pos; pos.x += dmgFrame->getSize().x; pos.y += dmgFrame->getSize().y; + pos.y += statusEffectOffsetY; SDL_BlitScaled(srcSurf, nullptr, sprite, &pos); } for ( auto& img : foregroundFrame->getImages() ) @@ -4824,6 +4819,7 @@ SDL_Surface* blitEnemyBar(const int player) SDL_Rect pos = img->pos; pos.x += foregroundFrame->getSize().x; pos.y += foregroundFrame->getSize().y; + pos.y += statusEffectOffsetY; SDL_BlitScaled(srcSurf, nullptr, sprite, &pos); } for ( auto& img : skullFrame->getImages() ) @@ -4835,6 +4831,7 @@ SDL_Surface* blitEnemyBar(const int player) SDL_Rect pos = img->pos; pos.x += skullFrame->getSize().x; pos.y += skullFrame->getSize().y; + pos.y += statusEffectOffsetY; SDL_BlitScaled(srcSurf, nullptr, sprite, &pos); } for ( auto& txt : frame->getFields() ) @@ -4845,12 +4842,129 @@ SDL_Surface* blitEnemyBar(const int player) pos.w = textGet->getWidth(); pos.h = textGet->getHeight(); pos.x = sprite->w / 2 - pos.w / 2; - pos.y = sprite->h / 2 - pos.h / 2; + pos.y = frame->getSize().h / 2 - pos.h / 2; + pos.y += statusEffectOffsetY; Uint8 r, g, b, a; SDL_GetRGBA(txt->getColor(), mainsurface->format, &r, &g, &b, &a); SDL_SetSurfaceAlphaMod(txtSurf, a * frameOpacity); SDL_BlitSurface(txtSurf, nullptr, sprite, &pos); } + if ( statusEffectSprite ) + { + SDL_Rect pos{0, 0, statusEffectSprite->w, statusEffectSprite->h}; + SDL_BlitSurface(statusEffectSprite, nullptr, sprite, &pos); + } + return sprite; +} + +SDL_Surface* EnemyHPDamageBarHandler::EnemyHPDetails::blitEnemyBarStatusEffects(const int player) +{ + Entity* entity = uidToEntity(enemy_uid); + if ( entity && (entity->behavior != &actPlayer && entity->behavior != &actMonster) ) + { + return nullptr; + } + Frame* frame = players[player]->hud.enemyBarFrame; + if ( !frame || !players[player]->isLocalPlayer() ) + { + return nullptr; + } + if ( enemy_statusEffects1 == 0 && enemy_statusEffects2 == 0 ) + { + return nullptr; + } + auto baseBg = frame->findImage("base img"); + auto baseEndCap = frame->findImage("base img endcap"); + real_t frameOpacity = frame->getOpacity() / 100.0; + const int maxWidth = baseBg->pos.x + baseBg->pos.w + baseEndCap->pos.w; + const int iconHeight = 32; + const int iconWidth = 32; + + SDL_Surface* sprite = SDL_CreateRGBSurface(0, maxWidth, iconHeight + 2, 32, + 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); + + int playernum = -1; + if ( entity && entity->behavior == &actPlayer ) + { + playernum = entity->skill[2]; + } + + int currentX = (maxWidth / 2); + bool anyStatusEffect = false; + + std::vector> statusEffectIcons; + if ( enemy_statusEffects1 != 0 ) + { + for ( int i = 0; i < 32; ++i ) + { + if ( (enemy_statusEffects1 & (1 << i)) != 0 ) + { + if ( SDL_Surface* srcSurf = getStatusEffectSprite(entity, + (entity ? entity->getStats() : nullptr), i, playernum) ) + { + bool blinking = false; + if ( (enemy_statusEffectsLowDuration1 & (1 << i)) != 0 ) + { + blinking = true; + } + statusEffectIcons.push_back(std::make_pair(srcSurf, blinking)); + } + } + } + } + if ( enemy_statusEffects2 != 0 ) + { + for ( int i = 0; i < 32; ++i ) + { + if ( (enemy_statusEffects2 & (1 << i)) != 0 ) + { + if ( SDL_Surface* srcSurf = getStatusEffectSprite(entity, + (entity ? entity->getStats() : nullptr), i + 32, playernum) ) + { + bool blinking = false; + if ( (enemy_statusEffectsLowDuration2 & (1 << i)) != 0 ) + { + blinking = true; + } + statusEffectIcons.push_back(std::make_pair(srcSurf, blinking)); + } + } + } + } + + const int numIcons = statusEffectIcons.size(); + const int iconTotalWidth = iconWidth + 2; + if ( numIcons % 2 == 1 ) // odd numbered + { + currentX -= ((iconTotalWidth) * (numIcons / 2)) + (iconTotalWidth / 2); + } + else + { + currentX -= ((iconTotalWidth) * (numIcons / 2)); + } + + for ( auto& icon : statusEffectIcons ) + { + anyStatusEffect = true; + int tickModifier = ticks % 25; + real_t alpha = 255 * frameOpacity; + if ( tickModifier >= 12 && icon.second ) + { + alpha = 0; + } + SDL_SetSurfaceAlphaMod(icon.first, alpha); + //SDL_SetSurfaceBlendMode(srcSurf, SDL_BLENDMODE_NONE); + SDL_Rect pos{ currentX, 0, iconWidth, iconHeight }; + SDL_BlitScaled(icon.first, nullptr, sprite, &pos); + currentX += (iconWidth + 2); + } + + if ( !anyStatusEffect ) + { + SDL_FreeSurface(sprite); + sprite = nullptr; + } + return sprite; } @@ -5198,7 +5312,7 @@ void Player::HUD_t::updateEnemyBar2(Frame* whichFrame, void* enemyHPDetails) if ( damageNumber >= 0 ) { char buf[128] = ""; - itoa(damageNumber, buf, 10); + snprintf(buf, sizeof(buf), "%d", damageNumber); dmgText->setText(buf); dmgText->setDisabled(false); SDL_Rect txtPos = dmgText->getSize(); @@ -5249,8 +5363,14 @@ void Player::HUD_t::updateEnemyBar2(Frame* whichFrame, void* enemyHPDetails) SDL_FreeSurface(enemyDetails->worldSurfaceSprite); enemyDetails->worldSurfaceSprite = nullptr; } + if ( enemyDetails->worldSurfaceSpriteStatusEffects ) + { + SDL_FreeSurface(enemyDetails->worldSurfaceSpriteStatusEffects); + enemyDetails->worldSurfaceSpriteStatusEffects = nullptr; + } - enemyDetails->worldSurfaceSprite = blitEnemyBar(player.playernum); + enemyDetails->worldSurfaceSpriteStatusEffects = enemyDetails->blitEnemyBarStatusEffects(player.playernum); + enemyDetails->worldSurfaceSprite = blitEnemyBar(player.playernum, enemyDetails->worldSurfaceSpriteStatusEffects); enemyDetails->worldTexture = new TempTexture(); enemyDetails->worldTexture->load(enemyDetails->worldSurfaceSprite, false, true); @@ -5567,7 +5687,7 @@ void Player::HUD_t::updateEnemyBar(Frame* whichFrame) if ( damageNumber >= 0 ) { char buf[128] = ""; - itoa(damageNumber, buf, 10); + snprintf(buf, sizeof(buf), "%d", damageNumber); dmgText->setText(buf); dmgText->setDisabled(false); SDL_Rect txtPos = dmgText->getSize(); @@ -5628,7 +5748,7 @@ void Player::HUD_t::updateEnemyBar(Frame* whichFrame) SDL_FreeSurface(enemyDetails->worldSurfaceSprite); enemyDetails->worldSurfaceSprite = nullptr; } - enemyDetails->worldSurfaceSprite = blitEnemyBar(player.playernum); + enemyDetails->worldSurfaceSprite = blitEnemyBar(player.playernum, enemyDetails->worldSurfaceSpriteStatusEffects); enemyDetails->worldTexture = new TempTexture(); enemyDetails->worldTexture->load(enemyDetails->worldSurfaceSprite, false, true); } diff --git a/src/ui/GameUI.hpp b/src/ui/GameUI.hpp index 6568c10f9..33eb4b67c 100644 --- a/src/ui/GameUI.hpp +++ b/src/ui/GameUI.hpp @@ -12,7 +12,24 @@ bool getSlotFrameXYFromMousePos(const int player, int& outx, int& outy); void resetInventorySlotFrames(const int player); void createPlayerInventorySlotFrameElements(Frame* slotFrame); void loadHUDSettingsJSON(); -SDL_Surface* blitEnemyBar(const int player); +SDL_Surface* blitEnemyBar(const int player, SDL_Surface* statusEffectSprite); +struct EnemyBarSettings_t +{ + std::unordered_map heightOffsets; + std::unordered_map screenDistanceOffsets; + std::string getEnemyBarSpriteName(Entity* entity); + float getHeightOffset(Entity* entity) + { + if ( !entity ) { return 0.f; } + return heightOffsets[getEnemyBarSpriteName(entity)]; + } + float getScreenDistanceOffset(Entity* entity) + { + if ( !entity ) { return 0.f; } + return screenDistanceOffsets[getEnemyBarSpriteName(entity)]; + } +}; +extern EnemyBarSettings_t enemyBarSettings; // if true, use the new user interface extern bool newui; From 9ec57d7c7b218658867f33a9731cd828125b24b3 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 2 Oct 2021 01:30:59 +1000 Subject: [PATCH 05/39] * Statue + Statue Animator sprites for editor * Add 2 GL buffers for grayscale color palettes * import/export statues from data/statues/ folder --- src/actgeneral.cpp | 114 +++++++ src/actplayer.cpp | 498 ++++++++++++++++++++++++++++--- src/buttons.cpp | 18 ++ src/draw.cpp | 4 + src/editor.cpp | 113 +++++++ src/entity.cpp | 11 +- src/entity.hpp | 8 +- src/entity_editor.cpp | 6 +- src/entity_shared.cpp | 23 +- src/files.cpp | 16 + src/game.hpp | 2 + src/init.cpp | 45 ++- src/interface/consolecommand.cpp | 21 +- src/interface/drawstatus.cpp | 3 +- src/main.hpp | 2 + src/maps.cpp | 23 ++ src/mod_tools.cpp | 190 +++++++++++- src/mod_tools.hpp | 52 +++- src/opengl.cpp | 46 ++- 19 files changed, 1141 insertions(+), 54 deletions(-) diff --git a/src/actgeneral.cpp b/src/actgeneral.cpp index d51b1a30e..2458c9694 100644 --- a/src/actgeneral.cpp +++ b/src/actgeneral.cpp @@ -22,6 +22,7 @@ #include "interface/interface.hpp" #include "items.hpp" #include "scores.hpp" +#include "mod_tools.hpp" /*------------------------------------------------------------------------------- @@ -384,6 +385,119 @@ void Entity::actStalagColumn() } +void actStatue(Entity* my) +{ + if ( my->statueInit == 0 && StatueManager.allStatues.size() > 0 ) + { + // needs to init. + if ( StatueManager.allStatues.find(my->statueId) != StatueManager.allStatues.end() ) + { + my->statueInit = 1; + if ( my->statueDir >= 0 && my->statueDir < StatueManager.directionKeys.size() ) + { + int index = 0; + real_t baseHeight = 0.0; + std::string directionString = StatueManager.directionKeys[my->statueDir]; + for ( auto& limb : StatueManager.allStatues[my->statueId].limbs[directionString] ) + { + Entity* childEntity = newEntity(limb.sprite, 1, map.entities, nullptr); + childEntity->parent = my->getUID(); + childEntity->x = my->x - limb.x; + childEntity->y = my->y - limb.y; + childEntity->z = limb.z + StatueManager.allStatues[my->statueId].heightOffset; + childEntity->focalx = limb.focalx; + childEntity->focaly = limb.focaly; + childEntity->focalz = limb.focalz; + childEntity->pitch = limb.pitch; + childEntity->roll = limb.roll; + childEntity->yaw = limb.yaw; + childEntity->flags[PASSABLE] = true; + childEntity->grayscaleGLRender = 1.0; + node_t* tempNode = list_AddNodeLast(&my->children); + tempNode->element = childEntity; // add the node to the children list. + tempNode->deconstructor = &emptyDeconstructor; + tempNode->size = sizeof(Entity*); + ++index; + } + } + } + } +} + +void actStatueAnimator(Entity* my) +{ + if ( !my ) + { + return; + } + + if ( StatueManager.processStatueExport() == 1 ) // in progress + { + if ( Entity* player = uidToEntity(StatueManager.editingPlayerUid) ) + { + player->yaw += PI / 2; + while ( player->yaw >= 2 * PI ) + { + player->yaw -= 2 * PI; + } + } + } + + for ( int i = 0; i < MAXPLAYERS; i++ ) + { + if ( (i == 0 && selectedEntity[0] == my) || (client_selected[i] == my) || (splitscreen && selectedEntity[i] == my) ) + { + if ( inrange[i] ) + { + if ( !StatueManager.activeEditing ) + { + StatueManager.statueEditorHeightOffset = 0.0; + } + StatueManager.activeEditing = !StatueManager.activeEditing; + messagePlayer(0, "Statue editing mode: %d", StatueManager.activeEditing); + + my->skill[0] = StatueManager.activeEditing ? 1 : 0; + } + } + } + + if ( StatueManager.activeEditing ) + { + if ( !players[1]->entity ) + { + client_disconnected[1] = false; + Entity* entity = newEntity(0, 1, map.entities, nullptr); + entity->behavior = &actPlayer; + entity->addToCreatureList(map.creatures); + + entity->x = my->x; + entity->y = my->y; + entity->z = my->z; + + entity->z -= 15 + StatueManager.statueEditorHeightOffset; + entity->focalx = limbs[HUMAN][0][0]; // 0 + entity->focaly = limbs[HUMAN][0][1]; // 0 + entity->focalz = limbs[HUMAN][0][2]; // -1.5 + entity->sprite = 113; // head model + entity->sizex = 4; + entity->sizey = 4; + entity->flags[GENIUS] = true; + entity->flags[BLOCKSIGHT] = true; + entity->flags[PASSABLE] = true; + entity->skill[2] = 1; // skill[2] == PLAYER_NUM + players[1]->entity = entity; + StatueManager.editingPlayerUid = entity->getUID(); + } + else + { + players[1]->entity->x = my->x; + players[1]->entity->y = my->y; + players[1]->entity->z = my->z; + players[1]->entity->z -= 15 + StatueManager.statueEditorHeightOffset; + } + } +} + void actColumn(Entity* my) { //TODO: something? diff --git a/src/actplayer.cpp b/src/actplayer.cpp index f5f764f29..d4fc69c18 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -27,6 +27,7 @@ #include "colors.hpp" #include "draw.hpp" #include "mod_tools.hpp" +#include "classdescriptions.hpp" bool smoothmouse = false; bool settings_smoothmouse = false; @@ -1128,6 +1129,426 @@ void Player::PlayerMovement_t::handlePlayerCameraPosition(bool useRefreshRateDel } } +void statueCycleItem(Item& item, bool dirForward) +{ + int cat = items[item.type].item_slot; + item.appearance = rand(); + if ( dirForward ) + { + for ( int i = item.type + 1; i <= NUMITEMS && i != item.type; ++i ) + { + if ( i == NUMITEMS ) + { + i = 0; + } + if ( items[i].item_slot == cat ) + { + item.type = ItemType(i); + break; + } + } + } + else + { + for ( int i = item.type - 1; i < NUMITEMS && i != item.type; --i ) + { + if ( i < 0 ) + { + i = NUMITEMS - 1; + } + if ( items[i].item_slot == cat ) + { + item.type = ItemType(i); + break; + } + } + } +} + +void doStatueEditor(int player) +{ + if ( !StatueManager.activeEditing ) { return; } + if ( player != clientnum ) { return; } + + Sint32 mouseX = inputs.getMouse(player, Inputs::OX); + Sint32 mouseY = inputs.getMouse(player, Inputs::OY); + bool shootmode = players[player]->shootmode; + + if ( ticks % 5 == 0 ) + { + Entity* underMouse = nullptr; + Uint32 uidnum = 0; + if ( !shootmode ) + { + uidnum = GO_GetPixelU32(mouseX, yres - mouseY, cameras[player]); + if ( uidnum > 0 ) + { + underMouse = uidToEntity(uidnum); + } + } + else + { + uidnum = GO_GetPixelU32(cameras[player].winw / 2, yres - cameras[player].winh / 2, cameras[player]); + if ( uidnum > 0 ) + { + underMouse = uidToEntity(uidnum); + } + } + if ( underMouse ) + { + underMouse->highlightForUI = 1.0; + if ( underMouse->behavior == &actPlayerLimb ) + { + underMouse = players[underMouse->skill[2]]->entity; + } + if ( underMouse->behavior == &actPlayer ) + { + StatueManager.editingPlayerUid = underMouse->getUID(); + StatueManager.lastEntityUnderMouse = uidnum; + } + } + } + + + if ( Entity* playerEntity = uidToEntity(StatueManager.editingPlayerUid) ) + { + playerEntity->highlightForUI = 0.0; + if ( StatueManager.drawGreyscale ) + { + playerEntity->grayscaleGLRender = 1.0; + } + else + { + playerEntity->grayscaleGLRender = 0.0; + } + for ( auto& bodypart : playerEntity->bodyparts ) + { + bodypart->highlightForUI = 0.0; + if ( StatueManager.drawGreyscale ) + { + bodypart->grayscaleGLRender = 1.0; + } + else + { + bodypart->grayscaleGLRender = 0.0; + } + } + } + + if ( !command ) + { + if ( Entity* limb = uidToEntity(StatueManager.lastEntityUnderMouse) ) + { + limb->highlightForUI = 1.0; + if ( keystatus[SDL_SCANCODE_O] ) + { + keystatus[SDL_SCANCODE_O] = 0; + limb->pitch += PI / 32; + } + if ( keystatus[SDL_SCANCODE_P] ) + { + keystatus[SDL_SCANCODE_P] = 0; + limb->pitch -= PI / 32; + } + if ( keystatus[SDL_SCANCODE_K] ) + { + keystatus[SDL_SCANCODE_K] = 0; + limb->roll += PI / 32; + } + if ( keystatus[SDL_SCANCODE_L] ) + { + keystatus[SDL_SCANCODE_L] = 0; + limb->roll -= PI / 32; + } + if ( keystatus[SDL_SCANCODE_COMMA] ) + { + keystatus[SDL_SCANCODE_COMMA] = 0; + limb->yaw += PI / 32; + } + if ( keystatus[SDL_SCANCODE_PERIOD] ) + { + keystatus[SDL_SCANCODE_PERIOD] = 0; + limb->yaw -= PI / 32; + } + } + + if ( Entity* playerEntity = uidToEntity(StatueManager.editingPlayerUid) ) + { + Stat* stats = playerEntity->getStats(); + + if ( keystatus[SDL_SCANCODE_LEFTBRACKET] ) + { + keystatus[SDL_SCANCODE_LEFTBRACKET] = 0; + StatueManager.statueEditorHeightOffset -= .25; + } + if ( keystatus[SDL_SCANCODE_RIGHTBRACKET] ) + { + keystatus[SDL_SCANCODE_RIGHTBRACKET] = 0; + StatueManager.statueEditorHeightOffset += .25; + } + + if ( keystatus[SDL_SCANCODE_F1] ) + { + keystatus[SDL_SCANCODE_F1] = 0; + + ++stats->playerRace; + if ( playerEntity->getMonsterFromPlayerRace(stats->playerRace) == HUMAN && stats->playerRace > 0 ) + { + stats->playerRace = RACE_HUMAN; + } + if ( playerEntity->getMonsterFromPlayerRace(stats->playerRace) != HUMAN ) + { + stats->appearance = 0; + } + } + if ( keystatus[SDL_SCANCODE_F2] ) + { + keystatus[SDL_SCANCODE_F2] = 0; + + ++stats->appearance; + if ( stats->appearance >= NUMAPPEARANCES ) + { + stats->appearance = 0; + } + } + if ( keystatus[SDL_SCANCODE_F3] ) + { + keystatus[SDL_SCANCODE_F3] = 0; + if ( stats->sex == MALE ) + { + stats->sex = FEMALE; + } + else + { + stats->sex = MALE; + } + } + if ( keystatus[SDL_SCANCODE_F4] ) + { + keystatus[SDL_SCANCODE_F4] = 0; + StatueManager.drawGreyscale = !StatueManager.drawGreyscale; + } + + if ( keystatus[SDL_SCANCODE_1] ) + { + keystatus[SDL_SCANCODE_1] = 0; + if ( keystatus[SDL_SCANCODE_LALT] || keystatus[SDL_SCANCODE_RALT] ) + { + if ( stats->helmet ) + { + list_RemoveNode(stats->helmet->node); + stats->helmet = nullptr; + } + } + else + { + if ( !stats->helmet ) + { + stats->helmet = newItem(LEATHER_HELM, EXCELLENT, 0, 1, rand(), true, &stats->inventory); + } + if ( keystatus[SDL_SCANCODE_LSHIFT] || keystatus[SDL_SCANCODE_RSHIFT] ) + { + statueCycleItem(*stats->helmet, false); + } + else + { + statueCycleItem(*stats->helmet, true); + } + } + } + if ( keystatus[SDL_SCANCODE_2] ) + { + keystatus[SDL_SCANCODE_2] = 0; + if ( keystatus[SDL_SCANCODE_LALT] || keystatus[SDL_SCANCODE_RALT] ) + { + if ( stats->breastplate ) + { + list_RemoveNode(stats->breastplate->node); + stats->breastplate = nullptr; + } + } + else + { + if ( !stats->breastplate ) + { + stats->breastplate = newItem(LEATHER_BREASTPIECE, EXCELLENT, 0, 1, rand(), true, &stats->inventory); + } + if ( keystatus[SDL_SCANCODE_LSHIFT] || keystatus[SDL_SCANCODE_RSHIFT] ) + { + statueCycleItem(*stats->breastplate, false); + } + else + { + statueCycleItem(*stats->breastplate, true); + } + } + } + if ( keystatus[SDL_SCANCODE_3] ) + { + keystatus[SDL_SCANCODE_3] = 0; + if ( keystatus[SDL_SCANCODE_LALT] || keystatus[SDL_SCANCODE_RALT] ) + { + if ( stats->gloves ) + { + list_RemoveNode(stats->gloves->node); + stats->gloves = nullptr; + } + } + else + { + if ( !stats->gloves ) + { + stats->gloves = newItem(GLOVES, EXCELLENT, 0, 1, rand(), true, &stats->inventory); + } + if ( keystatus[SDL_SCANCODE_LSHIFT] || keystatus[SDL_SCANCODE_RSHIFT] ) + { + statueCycleItem(*stats->gloves, false); + } + else + { + statueCycleItem(*stats->gloves, true); + } + } + } + if ( keystatus[SDL_SCANCODE_4] ) + { + keystatus[SDL_SCANCODE_4] = 0; + if ( keystatus[SDL_SCANCODE_LALT] || keystatus[SDL_SCANCODE_RALT] ) + { + if ( stats->shoes ) + { + list_RemoveNode(stats->shoes->node); + stats->shoes = nullptr; + } + } + else + { + if ( !stats->shoes ) + { + stats->shoes = newItem(LEATHER_BOOTS, EXCELLENT, 0, 1, rand(), true, &stats->inventory); + } + if ( keystatus[SDL_SCANCODE_LSHIFT] || keystatus[SDL_SCANCODE_RSHIFT] ) + { + statueCycleItem(*stats->shoes, false); + } + else + { + statueCycleItem(*stats->shoes, true); + } + } + } + if ( keystatus[SDL_SCANCODE_5] ) + { + keystatus[SDL_SCANCODE_5] = 0; + if ( keystatus[SDL_SCANCODE_LALT] || keystatus[SDL_SCANCODE_RALT] ) + { + if ( stats->weapon ) + { + list_RemoveNode(stats->weapon->node); + stats->weapon = nullptr; + } + } + else + { + if ( !stats->weapon ) + { + stats->weapon = newItem(BRONZE_SWORD, EXCELLENT, 0, 1, rand(), true, &stats->inventory); + } + if ( keystatus[SDL_SCANCODE_LSHIFT] || keystatus[SDL_SCANCODE_RSHIFT] ) + { + statueCycleItem(*stats->weapon, false); + } + else + { + statueCycleItem(*stats->weapon, true); + } + } + } + if ( keystatus[SDL_SCANCODE_6] ) + { + keystatus[SDL_SCANCODE_6] = 0; + if ( keystatus[SDL_SCANCODE_LALT] || keystatus[SDL_SCANCODE_RALT] ) + { + if ( stats->shield ) + { + list_RemoveNode(stats->shield->node); + stats->shield = nullptr; + } + } + else + { + if ( !stats->shield ) + { + stats->shield = newItem(WOODEN_SHIELD, EXCELLENT, 0, 1, rand(), true, &stats->inventory); + } + if ( keystatus[SDL_SCANCODE_LSHIFT] || keystatus[SDL_SCANCODE_RSHIFT] ) + { + statueCycleItem(*stats->shield, false); + } + else + { + statueCycleItem(*stats->shield, true); + } + } + } + if ( keystatus[SDL_SCANCODE_7] ) + { + keystatus[SDL_SCANCODE_7] = 0; + if ( keystatus[SDL_SCANCODE_LALT] || keystatus[SDL_SCANCODE_RALT] ) + { + if ( stats->mask ) + { + list_RemoveNode(stats->mask->node); + stats->mask = nullptr; + } + } + else + { + if ( !stats->mask ) + { + stats->mask = newItem(TOOL_GLASSES, EXCELLENT, 0, 1, rand(), true, &stats->inventory); + } + if ( keystatus[SDL_SCANCODE_LSHIFT] || keystatus[SDL_SCANCODE_RSHIFT] ) + { + statueCycleItem(*stats->mask, false); + } + else + { + statueCycleItem(*stats->mask, true); + } + } + } + if ( keystatus[SDL_SCANCODE_8] ) + { + keystatus[SDL_SCANCODE_8] = 0; + if ( keystatus[SDL_SCANCODE_LALT] || keystatus[SDL_SCANCODE_RALT] ) + { + if ( stats->cloak ) + { + list_RemoveNode(stats->cloak->node); + stats->cloak = nullptr; + } + } + else + { + if ( !stats->cloak ) + { + stats->cloak = newItem(CLOAK, EXCELLENT, 0, 1, rand(), true, &stats->inventory); + } + if ( keystatus[SDL_SCANCODE_LSHIFT] || keystatus[SDL_SCANCODE_RSHIFT] ) + { + statueCycleItem(*stats->cloak, false); + } + else + { + statueCycleItem(*stats->cloak, true); + } + } + } + } + } +} + void actPlayer(Entity* my) { if (!my) @@ -2099,11 +2520,11 @@ void actPlayer(Entity* my) } // debug stuff - if ( !command && keystatus[SDL_SCANCODE_O] ) + /*if ( !command && keystatus[SDL_SCANCODE_O] ) { consoleCommand("/facebaralternate"); keystatus[SDL_SCANCODE_O] = 0; - } + }*/ if ( inputs.hasController(PLAYER_NUM) ) { //if ( keystatus[SDL_SCANCODE_KP_1] ) @@ -2961,23 +3382,19 @@ void actPlayer(Entity* my) my->effectTimes(); } + bool freezeLimbMovements = false; + if ( StatueManager.editingPlayerUid > 0 + && uidToEntity(StatueManager.editingPlayerUid) == players[PLAYER_NUM]->entity ) + { + freezeLimbMovements = true; + } + // invisibility if ( !intro ) { - if ( players[PLAYER_NUM]->isLocalPlayer() || multiplayer == SERVER ) + if ( players[PLAYER_NUM]->isLocalPlayer() || multiplayer == SERVER || StatueManager.activeEditing ) { - if ( stats[PLAYER_NUM]->ring != NULL ) - if ( stats[PLAYER_NUM]->ring->type == RING_INVISIBILITY ) - { - wearingring = true; - } - if ( stats[PLAYER_NUM]->cloak != NULL ) - if ( stats[PLAYER_NUM]->cloak->type == CLOAK_INVISIBILITY ) - { - wearingring = true; - } - //if ( stats[PLAYER_NUM]->EFFECTS[EFF_INVISIBLE] == true || wearingring == true ) - if ( my->isInvisible() ) + if ( my->isInvisible() && !freezeLimbMovements ) { if ( !my->flags[INVISIBLE] ) { @@ -3457,6 +3874,7 @@ void actPlayer(Entity* my) if ( intro == false ) { clickDescription(PLAYER_NUM, NULL); // inspecting objects + doStatueEditor(PLAYER_NUM); if ( followerMenu.optionSelected == ALLY_CMD_ATTACK_SELECT ) { @@ -3943,7 +4361,7 @@ void actPlayer(Entity* my) } // server controls players primarily - if ( players[PLAYER_NUM]->isLocalPlayer() || multiplayer == SERVER ) + if ( players[PLAYER_NUM]->isLocalPlayer() || multiplayer == SERVER || StatueManager.activeEditing ) { // set head model if ( playerRace != HUMAN ) @@ -4722,7 +5140,7 @@ void actPlayer(Entity* my) dist = sqrt(PLAYER_VELX * PLAYER_VELX + PLAYER_VELY * PLAYER_VELY); } - if ( (players[PLAYER_NUM]->isLocalPlayer()) && ticks % 65 == 0 ) + if ( (players[PLAYER_NUM]->isLocalPlayer()) && ticks % 65 == 0 && stats[PLAYER_NUM]->EFFECTS[EFF_TELEPATH] ) { for ( node_t* mapNode = map.creatures->first; mapNode != nullptr; mapNode = mapNode->next ) { @@ -4929,20 +5347,23 @@ void actPlayer(Entity* my) } else { - if ( entity->pitch < 0 ) + if ( !freezeLimbMovements ) { - entity->pitch += 1 / fmax(limbSpeed * .1, 10.0); - if ( entity->pitch > 0 ) + if ( entity->pitch < 0 ) { - entity->pitch = 0; + entity->pitch += 1 / fmax(limbSpeed * .1, 10.0); + if ( entity->pitch > 0 ) + { + entity->pitch = 0; + } } - } - else if ( entity->pitch > 0 ) - { - entity->pitch -= 1 / fmax(limbSpeed * .1, 10.0); - if ( entity->pitch < 0 ) + else if ( entity->pitch > 0 ) { - entity->pitch = 0; + entity->pitch -= 1 / fmax(limbSpeed * .1, 10.0); + if ( entity->pitch < 0 ) + { + entity->pitch = 0; + } } } } @@ -5339,20 +5760,23 @@ void actPlayer(Entity* my) } else { - if ( entity->pitch < 0 ) + if ( !freezeLimbMovements ) { - entity->pitch += 1 / fmax(limbSpeed * .1, 10.0); - if ( entity->pitch > 0 ) + if ( entity->pitch < 0 ) { - entity->pitch = 0; + entity->pitch += 1 / fmax(limbSpeed * .1, 10.0); + if ( entity->pitch > 0 ) + { + entity->pitch = 0; + } } - } - else if ( entity->pitch > 0 ) - { - entity->pitch -= 1 / fmax(limbSpeed * .1, 10.0); - if ( entity->pitch < 0 ) + else if ( entity->pitch > 0 ) { - entity->pitch = 0; + entity->pitch -= 1 / fmax(limbSpeed * .1, 10.0); + if ( entity->pitch < 0 ) + { + entity->pitch = 0; + } } } } diff --git a/src/buttons.cpp b/src/buttons.cpp index ca3c69fe2..4abc19913 100644 --- a/src/buttons.cpp +++ b/src/buttons.cpp @@ -2280,6 +2280,20 @@ void buttonSpriteProperties(button_t* my) suby2 = yres / 2 + 60; strcpy(subtext, "Player Spawn Properties:"); break; + case 24: // statue + snprintf(spriteProperties[0], 4, "%d", static_cast(selectedEntity[0]->statueDir)); + snprintf(spriteProperties[1], 32, "%d", static_cast(selectedEntity[0]->statueId)); + inputstr = spriteProperties[0]; + cursorflash = ticks; + menuVisible = 0; + subwindow = 1; + newwindow = 29; + subx1 = xres / 2 - 170; + subx2 = xres / 2 + 170; + suby1 = yres / 2 - 60; + suby2 = yres / 2 + 60; + strcpy(subtext, "Statue Properties:"); + break; default: strcpy(message, "No properties available for current sprite."); messagetime = 60; @@ -3355,6 +3369,10 @@ void buttonSpritePropertiesConfirm(button_t* my) case 23: // player spawn selectedEntity[0]->playerStartDir = (Sint32)atoi(spriteProperties[0]); break; + case 24: // statue + selectedEntity[0]->statueDir = (Sint32)atoi(spriteProperties[0]); + selectedEntity[0]->statueId = (Sint32)atoi(spriteProperties[1]); + break; default: break; } diff --git a/src/draw.cpp b/src/draw.cpp index 8f4d5803c..9a38f0465 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -1572,6 +1572,7 @@ void drawEntities3D(view_t* camera, int mode) } } +#ifndef EDITOR for ( int i = 0; i < MAXPLAYERS; ++i ) { for ( auto& enemybar : enemyHPDamageBarHandler[i].HPBars ) @@ -1581,6 +1582,7 @@ void drawEntities3D(view_t* camera, int mode) spritesToDraw.push_back(std::make_tuple(camDist, &enemybar, SPRITE_HPBAR)); } } +#endif std::sort(spritesToDraw.begin(), spritesToDraw.end(), [](const std::tuple& lhs, const std::tuple& rhs) { @@ -1616,8 +1618,10 @@ void drawEntities3D(view_t* camera, int mode) } else if ( std::get<2>(distSpriteType) == SpriteTypes::SPRITE_HPBAR ) { +#ifndef EDITOR auto enemybar = (std::pair*)std::get<1>(distSpriteType); glDrawEnemyBarSprite(camera, mode, &enemybar->second, false); +#endif } } diff --git a/src/editor.cpp b/src/editor.cpp index 1d8feccc2..307089508 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -317,6 +317,12 @@ char playerSpawnPropertyNames[1][35] = "Spawn Facing Direction (-1 - 7)" }; +char statuePropertyNames[2][16] = +{ + "Direction (0-3)", + "Statue ID", +}; + const char* playerClassLangEntry(int classnum, int playernum) { if ( classnum >= CLASS_BARBARIAN && classnum <= CLASS_JOKER ) @@ -7457,6 +7463,113 @@ int main(int argc, char** argv) } } } + else if ( newwindow == 29 ) + { + if ( selectedEntity[0] != nullptr ) + { + int numProperties = sizeof(statuePropertyNames) / sizeof(statuePropertyNames[0]); //find number of entries in property list + const int lenProperties = sizeof(statuePropertyNames[0]) / sizeof(char); //find length of entry in property list + int spacing = 36; // 36 px between each item in the list. + int inputFieldHeader_y = suby1 + 28; // 28 px spacing from subwindow start. + int inputField_x = subx1 + 8; // 8px spacing from subwindow start. + int inputField_y = inputFieldHeader_y + 16; + int inputFieldWidth = 64; // width of the text field + int inputFieldFeedback_x = inputField_x + inputFieldWidth + 8; + char tmpPropertyName[lenProperties] = ""; + Uint32 color = SDL_MapRGB(mainsurface->format, 0, 255, 0); + Uint32 colorRandom = SDL_MapRGB(mainsurface->format, 0, 168, 255); + Uint32 colorError = SDL_MapRGB(mainsurface->format, 255, 0, 0); + + for ( int i = 0; i < numProperties; i++ ) + { + int propertyInt = atoi(spriteProperties[i]); + + strcpy(tmpPropertyName, statuePropertyNames[i]); + inputFieldHeader_y = suby1 + 28 + i * spacing; + inputField_y = inputFieldHeader_y + 16; + // box outlines then text + if ( i == 1 ) + { + inputFieldWidth = 80; // width of the text field + } + else + { + inputFieldWidth = 64; // width of the text field + } + drawDepressed(inputField_x - 4, inputField_y - 4, inputField_x - 4 + inputFieldWidth, inputField_y + 16 - 4); + // print values on top of boxes + printText(font8x8_bmp, inputField_x, suby1 + 44 + i * spacing, spriteProperties[i]); + printText(font8x8_bmp, inputField_x, inputFieldHeader_y, tmpPropertyName); + + if ( errorArr[i] != 1 ) + { + if ( i == 0 ) + { + if ( propertyInt > 3 || propertyInt < 0 ) + { + propertyPageError(i, 0); // reset to default 0. + } + else + { + char tmpStr[32] = ""; + switch ( propertyInt ) + { + case 0: + strcpy(tmpStr, "East"); + break; + case 1: + strcpy(tmpStr, "South"); + break; + case 2: + strcpy(tmpStr, "West"); + break; + case 3: + strcpy(tmpStr, "North"); + break; + default: + break; + } + printTextFormattedColor(font8x8_bmp, inputFieldFeedback_x, inputField_y, color, tmpStr); + } + } + else if ( i == 1 ) + { + if ( propertyInt < 0 ) + { + propertyPageError(i, 0); // reset to default 0. + } + } + else + { + // enter other row entries here + } + } + + if ( errorMessage ) + { + if ( errorArr[i] == 1 ) + { + printTextFormattedColor(font8x8_bmp, inputFieldFeedback_x, inputField_y, colorError, "Invalid ID!"); + } + } + } + + propertyPageTextAndInput(numProperties, inputFieldWidth); + + if ( editproperty < numProperties ) // edit + { + if ( !SDL_IsTextInputActive() ) + { + SDL_StartTextInput(); + inputstr = spriteProperties[0]; + } + + // set the maximum length allowed for user input + inputlen = 10; + propertyPageCursorFlash(spacing); + } + } + } else if ( newwindow == 25 ) { //if ( selectedEntity[0] != nullptr ) diff --git a/src/entity.cpp b/src/entity.cpp index 80a9c5de7..9a440c89e 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -301,6 +301,7 @@ Entity::Entity(Sint32 in_sprite, Uint32 pos, list_t* entlist, list_t* creatureli interactedByMonster(skill[47]), highlightForUI(fskill[29]), highlightForUIGlow(fskill[28]), + grayscaleGLRender(fskill[27]), soundSourceFired(skill[0]), soundSourceToPlay(skill[1]), soundSourceVolume(skill[2]), @@ -339,7 +340,10 @@ Entity::Entity(Sint32 in_sprite, Uint32 pos, list_t* entlist, list_t* creatureli worldTooltipInit(skill[3]), worldTooltipFadeDelay(skill[4]), worldTooltipIgnoreDrawing(skill[5]), - worldTooltipRequiresButtonHeld(skill[6]) + worldTooltipRequiresButtonHeld(skill[6]), + statueInit(skill[0]), + statueDir(skill[1]), + statueId(skill[3]) { int c; // add the entity to the entity list @@ -19057,6 +19061,11 @@ bool Entity::bEntityHighlightedForPlayer(const int player) const { return false; } + if ( (behavior == &actPlayer || behavior == &actPlayerLimb) + && StatueManager.activeEditing && highlightForUI > 0.001 ) + { + return true; + } if ( behavior == &actMonster || behavior == &actPlayer ) { return false; diff --git a/src/entity.hpp b/src/entity.hpp index 52c28d9b4..970dfccb3 100644 --- a/src/entity.hpp +++ b/src/entity.hpp @@ -219,6 +219,7 @@ class Entity Sint32& interactedByMonster; //skill[47] for use with monsterAllyInteractTarget real_t& highlightForUI; //fskill[29] for highlighting interactibles real_t& highlightForUIGlow; //fskill[28] for highlighting animation + real_t& grayscaleGLRender; //fskill[27] for grayscale rendering //--PUBLIC PLAYER SKILLS-- Sint32& playerLevelEntrySpeech; //skill[18] @@ -504,6 +505,11 @@ class Entity Sint32& worldTooltipIgnoreDrawing; //skill[5] Sint32& worldTooltipRequiresButtonHeld; //skill[6] + //--STATUES-- + Sint32& statueInit; //skill[0] + Sint32& statueDir; //skill[1] + Sint32& statueId; //skill[3] + void pedestalOrbInit(); // init orb properties // a pointer to the entity's location in a list (ie the map list of entities) @@ -1033,7 +1039,7 @@ void actTextSource(Entity* my); static const int NUM_ITEM_STRINGS = 290; static const int NUM_ITEM_STRINGS_BY_TYPE = 129; -static const int NUM_EDITOR_SPRITES = 168; +static const int NUM_EDITOR_SPRITES = 170; static const int NUM_EDITOR_TILES = 350; // furniture types. diff --git a/src/entity_editor.cpp b/src/entity_editor.cpp index cdab4047f..d7ba2dd9c 100644 --- a/src/entity_editor.cpp +++ b/src/entity_editor.cpp @@ -275,6 +275,7 @@ Entity::Entity(Sint32 in_sprite, Uint32 pos, list_t* entlist, list_t* creatureli interactedByMonster(skill[47]), highlightForUI(fskill[29]), highlightForUIGlow(fskill[28]), + grayscaleGLRender(fskill[27]), soundSourceFired(skill[0]), soundSourceToPlay(skill[1]), soundSourceVolume(skill[2]), @@ -310,7 +311,10 @@ Entity::Entity(Sint32 in_sprite, Uint32 pos, list_t* entlist, list_t* creatureli worldTooltipInit(skill[3]), worldTooltipFadeDelay(skill[4]), worldTooltipIgnoreDrawing(skill[5]), - worldTooltipRequiresButtonHeld(skill[6]) + worldTooltipRequiresButtonHeld(skill[6]), + statueInit(skill[0]), + statueDir(skill[1]), + statueId(skill[3]) { int c; // add the entity to the entity list diff --git a/src/entity_shared.cpp b/src/entity_shared.cpp index b7bd6b3a4..96ce98630 100644 --- a/src/entity_shared.cpp +++ b/src/entity_shared.cpp @@ -142,7 +142,9 @@ int checkSpriteType(Sint32 sprite) return 22; case 1: return 23; - break; + case 169: + // statue + return 24; default: return 0; break; @@ -889,7 +891,9 @@ char spriteEditorNameStrings[NUM_EDITOR_SPRITES][64] = "SPELLBOT", "DUMMYBOT", "GYROBOT", - "UNUSED" + "UNUSED", + "STATUE ANIMATOR", + "STATUE" }; char monsterEditorNameStrings[NUMMONSTERS][16] = @@ -1787,6 +1791,21 @@ void setSpriteAttributes(Entity* entityNew, Entity* entityToCopy, Entity* entity entityNew->playerStartDir = 0; } } + else if ( spriteType == 24 ) // statue + { + if ( entityToCopy != nullptr ) + { + // copy old entity attributes to newly created. + entityNew->statueDir = entityToCopy->statueDir; + entityNew->statueId = entityToCopy->statueId; + } + else + { + // set default new entity attributes. + entityNew->statueDir = 0; + entityNew->statueId = 0; + } + } if ( entityToCopy != nullptr ) { diff --git a/src/files.cpp b/src/files.cpp index fde37aea8..3a0b251c0 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -1179,6 +1179,10 @@ int loadMap(const char* filename2, map_t* destmap, list_t* entlist, list_t* crea fp->read(&entity->playerStartDir, sizeof(Sint32), 1); } break; + case 24: + fp->read(&entity->statueDir, sizeof(Sint32), 1); + fp->read(&entity->statueId, sizeof(Sint32), 1); + break; default: break; } @@ -1587,6 +1591,10 @@ int saveMap(const char* filename2) case 23: fp->write(&entity->playerStartDir, sizeof(Sint32), 1); break; + case 24: + fp->write(&entity->statueDir, sizeof(Sint32), 1); + fp->write(&entity->statueId, sizeof(Sint32), 1); + break; default: break; } @@ -2044,6 +2052,14 @@ bool physfsModelIndexUpdate(int &start, int &end, bool freePreviousModels) { SDL_glDeleteBuffers(1, &polymodels[c].colors_shifted); } + if ( polymodels[c].grayscale_colors ) + { + SDL_glDeleteVertexArrays(1, &polymodels[c].grayscale_colors); + } + if ( polymodels[c].grayscale_colors_shifted ) + { + SDL_glDeleteBuffers(1, &polymodels[c].grayscale_colors_shifted); + } } } diff --git a/src/game.hpp b/src/game.hpp index 82439132f..98a8004bb 100644 --- a/src/game.hpp +++ b/src/game.hpp @@ -220,6 +220,8 @@ void actLiquid(Entity* my); void actEmpty(Entity* my); void actFurniture(Entity* my); void actMCaxe(Entity* my); +void actStatueAnimator(Entity* my); +void actStatue(Entity* my); void actDoorFrame(Entity* my); void actDeathCam(Entity* my); void actPlayerLimb(Entity* my); diff --git a/src/init.cpp b/src/init.cpp index 7d1c1a7a9..0cacdcc84 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -136,6 +136,7 @@ int initApp(char const * const title, int fullscreen) PHYSFS_mkdir("logfiles"); PHYSFS_mkdir("data"); PHYSFS_mkdir("data/custom-monsters"); + PHYSFS_mkdir("data/statues"); #ifdef NINTENDO PHYSFS_mkdir("mods"); std::string path = outputdir; @@ -983,8 +984,8 @@ void generatePolyModels(int start, int end, bool forceCacheRebuild) { FileIO::close(model_cache); } + return; } - return; } } @@ -1943,12 +1944,20 @@ void generateVBOs(int start, int end) std::unique_ptr color_shifted_buffers(new GLuint[count]); SDL_glGenBuffers(count, color_shifted_buffers.get()); + std::unique_ptr grayscale_color_buffers(new GLuint[count]); + SDL_glGenBuffers(count, grayscale_color_buffers.get()); + + std::unique_ptr grayscale_color_shifted_buffers(new GLuint[count]); + SDL_glGenBuffers(count, grayscale_color_shifted_buffers.get()); + for ( int c = start; c < end; ++c ) { polymodel_t *model = &polymodels[c]; std::unique_ptr points(new GLfloat[9 * model->numfaces]); std::unique_ptr colors(new GLfloat[9 * model->numfaces]); std::unique_ptr colors_shifted(new GLfloat[9 * model->numfaces]); + std::unique_ptr grayscale_colors(new GLfloat[9 * model->numfaces]); + std::unique_ptr grayscale_colors_shifted(new GLfloat[9 * model->numfaces]); for ( int i = 0; i < model->numfaces; i++ ) { const polytriangle_t *face = &model->faces[i]; @@ -1968,12 +1977,23 @@ void generateVBOs(int start, int end) colors_shifted[data_index] = face->b / 255.f; colors_shifted[data_index + 1] = face->r / 255.f; colors_shifted[data_index + 2] = face->g / 255.f; + + real_t grayscaleFactor = (face->r + face->g + face->b) / 3.0; + grayscale_colors[data_index] = grayscaleFactor / 255.f; + grayscale_colors[data_index + 1] = grayscaleFactor / 255.f; + grayscale_colors[data_index + 2] = grayscaleFactor / 255.f; + + grayscale_colors_shifted[data_index] = grayscaleFactor / 255.f; + grayscale_colors_shifted[data_index + 1] = grayscaleFactor / 255.f; + grayscale_colors_shifted[data_index + 2] = grayscaleFactor / 255.f; } } model->va = vas[c - start]; model->vbo = vbos[c - start]; model->colors = color_buffers[c - start]; model->colors_shifted = color_shifted_buffers[c - start]; + model->grayscale_colors = grayscale_color_buffers[c - start]; + model->grayscale_colors_shifted = grayscale_color_shifted_buffers[c - start]; SDL_glBindVertexArray(model->va); // vertex data @@ -1988,6 +2008,14 @@ void generateVBOs(int start, int end) // shifted color data SDL_glBindBuffer(GL_ARRAY_BUFFER, model->colors_shifted); SDL_glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 9 * model->numfaces, colors_shifted.get(), GL_STATIC_DRAW); // Set the size and data of our VBO and set it to STATIC_DRAW + + // grayscale color data + SDL_glBindBuffer(GL_ARRAY_BUFFER, model->grayscale_colors); + SDL_glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 9 * model->numfaces, grayscale_colors.get(), GL_STATIC_DRAW); // Set the size and data of our VBO and set it to STATIC_DRAW + + // grayscale shifted color data + SDL_glBindBuffer(GL_ARRAY_BUFFER, model->grayscale_colors_shifted); + SDL_glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 9 * model->numfaces, grayscale_colors_shifted.get(), GL_STATIC_DRAW); // Set the size and data of our VBO and set it to STATIC_DRAW } } @@ -2178,6 +2206,18 @@ int deinitApp() { SDL_glDeleteVertexArrays(1, &polymodels[c].va); } + if ( polymodels[c].colors_shifted ) + { + SDL_glDeleteBuffers(1, &polymodels[c].colors_shifted); + } + if ( polymodels[c].grayscale_colors ) + { + SDL_glDeleteBuffers(1, &polymodels[c].grayscale_colors); + } + if ( polymodels[c].grayscale_colors_shifted ) + { + SDL_glDeleteBuffers(1, &polymodels[c].grayscale_colors_shifted); + } } } free(polymodels); @@ -2637,6 +2677,9 @@ bool changeVideoMode() SDL_glDeleteBuffers(1, &polymodels[c].vbo); SDL_glDeleteBuffers(1, &polymodels[c].colors); SDL_glDeleteVertexArrays(1, &polymodels[c].va); + SDL_glDeleteBuffers(1, &polymodels[c].colors_shifted); + SDL_glDeleteBuffers(1, &polymodels[c].grayscale_colors); + SDL_glDeleteBuffers(1, &polymodels[c].grayscale_colors_shifted); } } diff --git a/src/interface/consolecommand.cpp b/src/interface/consolecommand.cpp index df42c3038..61637334f 100644 --- a/src/interface/consolecommand.cpp +++ b/src/interface/consolecommand.cpp @@ -1377,6 +1377,14 @@ void consoleCommand(char const * const command_str) { SDL_glDeleteBuffers(1, &polymodels[c].colors_shifted); } + if ( polymodels[c].grayscale_colors ) + { + SDL_glDeleteBuffers(1, &polymodels[c].grayscale_colors); + } + if ( polymodels[c].grayscale_colors_shifted ) + { + SDL_glDeleteBuffers(1, &polymodels[c].grayscale_colors_shifted); + } } models[c] = loadVoxel(name2); } @@ -2710,7 +2718,7 @@ void consoleCommand(char const * const command_str) { networkTickrate = atoi(&command_str[10]); networkTickrate = std::max(1, networkTickrate); - messagePlayer(clientnum, "Set tickrate to %d, network processing allowed %3.0f percent of frame limit interval. Default value 2.", + messagePlayer(clientnum, "Set tickrate to %d, network processing allowed %3.0f percent of frame limit interval. Default value 2.", networkTickrate, 100.f / networkTickrate); } else if ( !strncmp(command_str, "/disablenetcodefpslimit", 23) ) @@ -2744,7 +2752,7 @@ void consoleCommand(char const * const command_str) messagePlayer(clientnum, language[284]); return; } - + Uint32 newseed = atoi(&command_str[12]); forceMapSeed = newseed; messagePlayer(clientnum, "Set next map seed to: %d", forceMapSeed); @@ -3456,6 +3464,15 @@ void consoleCommand(char const * const command_str) restrictPaperDollMovement = !restrictPaperDollMovement; messagePlayer(clientnum, "Set restrictPaperDollMovement to %d", restrictPaperDollMovement); } + else if ( !strncmp(command_str, "/exportstatue", 13) ) + { + StatueManager.exportActive = true; + } + else if ( !strncmp(command_str, "/importstatue ", 14) ) + { + int index = atoi(&command_str[14]); + StatueManager.readStatueFromFile(index); + } else { messagePlayer(clientnum, language[305], command_str); diff --git a/src/interface/drawstatus.cpp b/src/interface/drawstatus.cpp index 4918dc9f3..d6be8ae11 100644 --- a/src/interface/drawstatus.cpp +++ b/src/interface/drawstatus.cpp @@ -21,6 +21,7 @@ #include "../player.hpp" #include "interface.hpp" #include "../colors.hpp" +#include "../mod_tools.hpp" //Sint32 enemy_hp = 0, enemy_maxhp = 0, enemy_oldhp = 0; //Uint32 enemy_timer = 0, enemy_lastuid = 0; @@ -1784,7 +1785,7 @@ void drawStatus(int player) { // if hotbar_numkey_quick_add is enabled, then the number keys won't do the default equip function // skips equipping items if the mouse is in the hotbar or inventory area. otherwise the below code runs. - if ( inputs.bPlayerUsingKeyboardControl(player) ) + if ( inputs.bPlayerUsingKeyboardControl(player) && !StatueManager.activeEditing ) { if ( keystatus[SDL_SCANCODE_1] ) { diff --git a/src/main.hpp b/src/main.hpp index 65a64260d..3de94c3c0 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -514,6 +514,8 @@ typedef struct polymodel_t GLuint vbo; GLuint colors; GLuint colors_shifted; + GLuint grayscale_colors; + GLuint grayscale_colors_shifted; GLuint va; } polymodel_t; diff --git a/src/maps.cpp b/src/maps.cpp index e1d05e0b8..367cf328b 100644 --- a/src/maps.cpp +++ b/src/maps.cpp @@ -5646,6 +5646,29 @@ void assignActions(map_t* map) item = nullptr; } break; + case 168: + //Statue Animator + entity->sizex = 4; + entity->sizey = 4; + entity->x += 8; + entity->y += 8; + entity->z = 5; + entity->behavior = &actStatueAnimator; + entity->sprite = 245; + entity->skill[0] = 0; + entity->flags[BRIGHT] = true; + break; + case 169: + //Statue + entity->sizex = 4; + entity->sizey = 4; + entity->x += 8; + entity->y += 8; + entity->z = 3.5; + entity->behavior = &actStatue; + entity->sprite = 995; + entity->yaw = entity->statueDir * PI / 2; + break; default: break; } diff --git a/src/mod_tools.cpp b/src/mod_tools.cpp index 2d2ccb2f4..7002c2665 100644 --- a/src/mod_tools.cpp +++ b/src/mod_tools.cpp @@ -24,6 +24,7 @@ ItemTooltips_t ItemTooltips; #ifndef NINTENDO IRCHandler_t IRCHandler; #endif // !NINTENDO +StatueManager_t StatueManager; const std::vector MonsterStatCustomManager::itemStatusStrings = { @@ -3280,4 +3281,191 @@ void ItemTooltips_t::stripOutPositiveNegativeItemDetails(std::string& str, std:: index = std::distance(str.begin(), it); } } -#endif // !EDITOR \ No newline at end of file +#endif // !EDITOR + +Uint32 StatueManager_t::statueId = 0; +int StatueManager_t::processStatueExport() +{ + if ( !exportActive ) + { + return 0; + } + + Entity* player = uidToEntity(editingPlayerUid); + + if ( exportRotations >= 4 || !player ) + { + if ( player ) // save the file. + { + std::string outputPath = PHYSFS_getRealDir("/data/statues"); + outputPath.append(PHYSFS_getDirSeparator()); + std::string fileName = "data/statues/" + exportFileName; + outputPath.append(fileName.c_str()); + + exportRotations = 0; + exportFileName = ""; + exportActive = false; + + File* fp = FileIO::open(outputPath.c_str(), "wb"); + if ( !fp ) + { + return 0; + } + rapidjson::StringBuffer os; + rapidjson::PrettyWriter writer(os); + exportDocument.Accept(writer); + fp->write(os.GetString(), sizeof(char), os.GetSize()); + FileIO::close(fp); + return 2; // success + } + else + { + exportRotations = 0; + exportFileName = ""; + exportActive = false; + return 0; + } + } + + bool newDocument = false; + + if ( exportFileName == "" ) // find a new filename + { + int filenum = 0; + std::string testPath = "/data/statues/statue" + std::to_string(filenum) + ".json"; + while ( PHYSFS_getRealDir(testPath.c_str()) != nullptr && filenum < 1000 ) + { + ++filenum; + testPath = "/data/statues/statue" + std::to_string(filenum) + ".json"; + } + exportFileName = "statue" + std::to_string(filenum) + ".json"; + newDocument = true; + } + + if ( newDocument ) + { + if ( exportDocument.IsObject() ) + { + exportDocument.RemoveAllMembers(); + } + exportDocument.SetObject(); + CustomHelpers::addMemberToRoot(exportDocument, "version", rapidjson::Value(1)); + CustomHelpers::addMemberToRoot(exportDocument, "statue_id", rapidjson::Value(rand())); + CustomHelpers::addMemberToRoot(exportDocument, "height_offset", rapidjson::Value(0)); + rapidjson::Value limbsObject(rapidjson::kObjectType); + CustomHelpers::addMemberToRoot(exportDocument, "limbs", limbsObject); + } + + rapidjson::Value limbsArray(rapidjson::kArrayType); + + std::vector allLimbs; + allLimbs.push_back(player); + + for ( auto& bodypart : player->bodyparts ) + { + allLimbs.push_back(bodypart); + } + + int index = 0; + for ( auto& limb : allLimbs ) + { + if ( limb->flags[INVISIBLE] ) + { + continue; + } + rapidjson::Value limbsObj(rapidjson::kObjectType); + + if ( index != 0 ) + { + limbsObj.AddMember("x", rapidjson::Value(player->x - limb->x), exportDocument.GetAllocator()); + limbsObj.AddMember("y", rapidjson::Value(player->y - limb->y), exportDocument.GetAllocator()); + limbsObj.AddMember("z", rapidjson::Value(limb->z), exportDocument.GetAllocator()); + } + else + { + limbsObj.AddMember("x", rapidjson::Value(0), exportDocument.GetAllocator()); + limbsObj.AddMember("y", rapidjson::Value(0), exportDocument.GetAllocator()); + limbsObj.AddMember("z", rapidjson::Value(limb->z), exportDocument.GetAllocator()); + } + limbsObj.AddMember("pitch", rapidjson::Value(limb->pitch), exportDocument.GetAllocator()); + limbsObj.AddMember("roll", rapidjson::Value(limb->roll), exportDocument.GetAllocator()); + limbsObj.AddMember("yaw", rapidjson::Value(limb->yaw), exportDocument.GetAllocator()); + limbsObj.AddMember("focalx", rapidjson::Value(limb->focalx), exportDocument.GetAllocator()); + limbsObj.AddMember("focaly", rapidjson::Value(limb->focaly), exportDocument.GetAllocator()); + limbsObj.AddMember("focalz", rapidjson::Value(limb->focalz), exportDocument.GetAllocator()); + limbsObj.AddMember("sprite", rapidjson::Value(limb->sprite), exportDocument.GetAllocator()); + limbsArray.PushBack(limbsObj, exportDocument.GetAllocator()); + + ++index; + } + + CustomHelpers::addMemberToSubkey(exportDocument, "limbs", directionKeys[exportRotations], limbsArray); + ++exportRotations; + return 1; +} + +void StatueManager_t::readStatueFromFile(int index) +{ + std::string fileName = "/data/statues/statue" + std::to_string(index) + ".json"; + if ( PHYSFS_getRealDir(fileName.c_str()) ) + { + std::string inputPath = PHYSFS_getRealDir(fileName.c_str()); + inputPath.append(fileName); + + File* fp = FileIO::open(inputPath.c_str(), "rb"); + if ( !fp ) + { + printlog("[JSON]: Error: Could not locate json file %s", inputPath.c_str()); + return; + } + 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") || !d.HasMember("limbs") || !d.HasMember("statue_id") ) + { + printlog("[JSON]: Error: No 'version' value in json file, or JSON syntax incorrect! %s", inputPath.c_str()); + return; + } + int version = d["version"].GetInt(); + Uint32 statueId = d["statue_id"].GetUint(); + auto findStatue = allStatues.find(statueId); + if ( findStatue != allStatues.end() ) + { + allStatues.erase(findStatue); + } + allStatues.insert(std::make_pair(statueId, Statue_t())); + for ( rapidjson::Value::ConstMemberIterator limb_itr = d["limbs"].MemberBegin(); limb_itr != d["limbs"].MemberEnd(); ++limb_itr ) + { + auto& statue = allStatues[statueId]; + if ( d.HasMember("height_offset") ) + { + statue.heightOffset = d["height_offset"].GetDouble(); + } + for ( rapidjson::Value::ConstValueIterator dir_itr = limb_itr->value.Begin(); dir_itr != limb_itr->value.End(); ++dir_itr ) + { + const rapidjson::Value& attributes = *dir_itr; + std::string direction = limb_itr->name.GetString(); + auto& limbVector = statue.limbs[direction]; + limbVector.push_back(Statue_t::StatueLimb_t()); + auto& limb = limbVector[limbVector.size() - 1]; + limb.x = attributes["x"].GetDouble(); + limb.y = attributes["y"].GetDouble(); + limb.z = attributes["z"].GetDouble(); + limb.focalx = attributes["focalx"].GetDouble(); + limb.focaly = attributes["focaly"].GetDouble(); + limb.focalz = attributes["focalz"].GetDouble(); + limb.pitch = attributes["pitch"].GetDouble(); + limb.roll = attributes["roll"].GetDouble(); + limb.yaw = attributes["yaw"].GetDouble(); + limb.sprite = attributes["sprite"].GetInt(); + } + } + + printlog("[JSON]: Successfully read json file %s", inputPath.c_str()); + } +} \ No newline at end of file diff --git a/src/mod_tools.hpp b/src/mod_tools.hpp index 88adc28e0..66fe82fd9 100644 --- a/src/mod_tools.hpp +++ b/src/mod_tools.hpp @@ -2753,4 +2753,54 @@ class ItemTooltips_t void stripOutPositiveNegativeItemDetails(std::string& str, std::string& positiveValues, std::string& negativeValues); void stripOutHighlightBracketText(std::string& str, std::string& bracketText); }; -extern ItemTooltips_t ItemTooltips; \ No newline at end of file +extern ItemTooltips_t ItemTooltips; + +class StatueManager_t +{ +public: + StatueManager_t() {}; + ~StatueManager_t() {}; + int processStatueExport(); + + bool activeEditing = false; + Uint32 lastEntityUnderMouse = 0; + Uint32 editingPlayerUid = 0; + real_t statueEditorHeightOffset = 0.0; + bool drawGreyscale = false; + void readStatueFromFile(int index); + static Uint32 statueId; + std::string exportFileName = ""; + int exportRotations = 0; + bool exportActive = false; + rapidjson::Document exportDocument; + + class Statue_t + { + Uint32 id; + public: + struct StatueLimb_t + { + real_t x; + real_t y; + real_t z; + real_t pitch; + real_t roll; + real_t yaw; + real_t focalx; + real_t focaly; + real_t focalz; + Sint32 sprite; + bool visible = true; + }; + std::map> limbs; + Statue_t() { + id = statueId; + ++statueId; + } + real_t heightOffset = 0.0; + }; + + const std::vector directionKeys{ "east", "south", "west", "north" }; + std::map allStatues; +}; +extern StatueManager_t StatueManager; \ No newline at end of file diff --git a/src/opengl.cpp b/src/opengl.cpp index e938cdb2e..742866f85 100644 --- a/src/opengl.cpp +++ b/src/opengl.cpp @@ -525,6 +525,14 @@ void glDrawVoxel(view_t* camera, Entity* entity, int mode) } } + bool doGrayScale = false; + real_t grayScaleFactor = 0.0; + if ( entity->grayscaleGLRender > 0.001 ) + { + doGrayScale = true; + grayScaleFactor = entity->grayscaleGLRender; + } + // get shade factor if (!entity->flags[BRIGHT]) { @@ -716,22 +724,27 @@ void glDrawVoxel(view_t* camera, Entity* entity, int mode) { if ( mode == REALCOLORS ) { + Uint8 r, g, b; + r = polymodels[modelindex].faces[index].r; + b = polymodels[modelindex].faces[index].g; + g = polymodels[modelindex].faces[index].b; + if ( entity->flags[USERFLAG2] ) { if ( entity->behavior == &actMonster && (entity->isPlayerHeadSprite() || entity->sprite == 467 || !monsterChangesColorWhenAlly(nullptr, entity)) ) { // dont invert human heads, or automaton heads. - glColor3f((polymodels[modelindex].faces[index].r / 255.f)*s, (polymodels[modelindex].faces[index].g / 255.f)*s, (polymodels[modelindex].faces[index].b / 255.f)*s ); + glColor3f((r / 255.f)*s, (g / 255.f)*s, (b / 255.f)*s ); } else { - glColor3f((polymodels[modelindex].faces[index].b / 255.f)*s, (polymodels[modelindex].faces[index].r / 255.f)*s, (polymodels[modelindex].faces[index].g / 255.f)*s); + glColor3f((b / 255.f)*s, (r / 255.f)*s, (g / 255.f)*s); } } else { - glColor3f((polymodels[modelindex].faces[index].b / 255.f)*s, (polymodels[modelindex].faces[index].r / 255.f)*s, (polymodels[modelindex].faces[index].g / 255.f)*s ); + glColor3f((b / 255.f)*s, (r / 255.f)*s, (g / 255.f)*s ); } } else @@ -764,16 +777,37 @@ void glDrawVoxel(view_t* camera, Entity* entity, int mode) if ( entity->behavior == &actMonster && (entity->isPlayerHeadSprite() || entity->sprite == 467 || !monsterChangesColorWhenAlly(nullptr, entity)) ) { - SDL_glBindBuffer(GL_ARRAY_BUFFER, polymodels[modelindex].colors); + if ( doGrayScale ) + { + SDL_glBindBuffer(GL_ARRAY_BUFFER, polymodels[modelindex].grayscale_colors); + } + else + { + SDL_glBindBuffer(GL_ARRAY_BUFFER, polymodels[modelindex].colors); + } } else { - SDL_glBindBuffer(GL_ARRAY_BUFFER, polymodels[modelindex].colors_shifted); + if ( doGrayScale ) + { + SDL_glBindBuffer(GL_ARRAY_BUFFER, polymodels[modelindex].grayscale_colors_shifted); + } + else + { + SDL_glBindBuffer(GL_ARRAY_BUFFER, polymodels[modelindex].colors_shifted); + } } } else { - SDL_glBindBuffer(GL_ARRAY_BUFFER, polymodels[modelindex].colors); + if ( doGrayScale ) + { + SDL_glBindBuffer(GL_ARRAY_BUFFER, polymodels[modelindex].grayscale_colors); + } + else + { + SDL_glBindBuffer(GL_ARRAY_BUFFER, polymodels[modelindex].colors); + } } glColorPointer(3, GL_FLOAT, 0, 0); GLfloat params_col[4] = { static_cast(s), static_cast(s), static_cast(s), 1.f }; From aa9b9824b747160e27b0008cddd8bf11b69e5806 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 2 Oct 2021 01:33:44 +1000 Subject: [PATCH 06/39] * set statue animator sprite instead of temp boulder --- src/maps.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/maps.cpp b/src/maps.cpp index 367cf328b..a05b84f0b 100644 --- a/src/maps.cpp +++ b/src/maps.cpp @@ -5652,9 +5652,9 @@ void assignActions(map_t* map) entity->sizey = 4; entity->x += 8; entity->y += 8; - entity->z = 5; + entity->z = 3.5; entity->behavior = &actStatueAnimator; - entity->sprite = 245; + entity->sprite = 995; entity->skill[0] = 0; entity->flags[BRIGHT] = true; break; From e5a9c64504aede1c146bc02b6d1e7bc9387b6244 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Sat, 9 Oct 2021 17:15:44 -0700 Subject: [PATCH 07/39] changing video mode is easier resolutions sorted in a more sensible manner Signed-off-by: SheridanR --- src/game.cpp | 10 ---------- src/init.cpp | 22 ++++++++++++++++++++-- src/init.hpp | 2 +- src/interface/consolecommand.cpp | 10 ---------- src/menu.cpp | 23 ++++++++--------------- src/menu.hpp | 2 ++ 6 files changed, 31 insertions(+), 38 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 91eff26c2..120b1c89e 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3417,16 +3417,6 @@ void handleEvents(void) printlog("critical error! Attempting to abort safely...\n"); mainloop = 0; } - if (zbuffer != NULL) - { - free(zbuffer); - } - zbuffer = (real_t*)malloc(sizeof(real_t) * xres * yres); - if (clickmap != NULL) - { - free(clickmap); - } - clickmap = (Entity**)malloc(sizeof(Entity*)*xres * yres); } break; /*case SDL_CONTROLLERAXISMOTION: diff --git a/src/init.cpp b/src/init.cpp index a2dd89a55..331484453 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2609,9 +2609,15 @@ bool initVideo() -------------------------------------------------------------------------------*/ -bool changeVideoMode() +bool changeVideoMode(int new_xres, int new_yres) { - printlog("changing video mode.\n"); + if (new_xres) { + xres = new_xres; + } + if (new_yres) { + yres = new_yres; + } + printlog("changing video mode (%d x %d).\n", xres, yres); #ifdef PANDORA GO_InitFBO(); #else @@ -2692,6 +2698,18 @@ bool changeVideoMode() Frame::fboInit(); #endif + + if ( zbuffer != NULL ) + { + free(zbuffer); + } + zbuffer = (real_t*) malloc(sizeof(real_t) * xres * yres); + if ( clickmap != NULL ) + { + free(clickmap); + } + clickmap = (Entity**) malloc(sizeof(Entity*)*xres * yres); + // success return true; } diff --git a/src/init.hpp b/src/init.hpp index c035d2a3b..d7759a11e 100644 --- a/src/init.hpp +++ b/src/init.hpp @@ -13,7 +13,7 @@ int initApp(char const * const title, int fullscreen); int deinitApp(); bool initVideo(); -bool changeVideoMode(); +bool changeVideoMode(int new_xres = 0, int new_yres = 0); void generatePolyModels(int start, int end, bool forceCacheRebuild); void generateVBOs(int start, int end); int loadLanguage(char const * const lang); diff --git a/src/interface/consolecommand.cpp b/src/interface/consolecommand.cpp index df42c3038..e797f3860 100644 --- a/src/interface/consolecommand.cpp +++ b/src/interface/consolecommand.cpp @@ -495,16 +495,6 @@ void consoleCommand(char const * const command_str) printlog("critical error! Attempting to abort safely...\n"); mainloop = 0; } - if ( zbuffer != NULL ) - { - free(zbuffer); - } - zbuffer = (real_t*) malloc(sizeof(real_t) * xres * yres); - if ( clickmap != NULL ) - { - free(clickmap); - } - clickmap = (Entity**) malloc(sizeof(Entity*)*xres * yres); } } else if ( !strncmp(command_str, "/rscale", 7) ) diff --git a/src/menu.cpp b/src/menu.cpp index 591b8b7b3..e969b3fa4 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -168,7 +168,6 @@ int serialVerifyWindow = 0; bool scoreDisplayMultiplayer = false; int settings_xres, settings_yres; -typedef std::tuple resolution; std::list resolutions; Uint32 settings_fov; Uint32 settings_fps; @@ -12123,7 +12122,7 @@ void openGameoverWindow() } // get -void getResolutionList() +void getResolutionList(std::list& resolutions) { // for now just use the resolution modes on the first // display. @@ -12145,9 +12144,13 @@ void getResolutionList() } } - // Sort by total number of pixels + // Sort first by xres and then by yres resolutions.sort([](resolution a, resolution b) { - return std::get<0>(a) * std::get<1>(a) > std::get<0>(b) * std::get<1>(b); + if (std::get<0>(a) == std::get<0>(b)) { + return std::get<1>(a) > std::get<1>(b); + } else { + return std::get<0>(a) > std::get<0>(b); + } }); resolutions.unique(); @@ -12262,7 +12265,7 @@ void openSettingsWindow() button_t* button; int c; - getResolutionList(); + getResolutionList(resolutions); // set the "settings" variables settings_xres = xres; @@ -14204,16 +14207,6 @@ void applySettings() printlog("critical error! Attempting to abort safely...\n"); mainloop = 0; } - if ( zbuffer != NULL ) - { - free(zbuffer); - } - zbuffer = (real_t*) malloc(sizeof(real_t) * xres * yres); - if ( clickmap != NULL ) - { - free(clickmap); - } - clickmap = (Entity**) malloc(sizeof(Entity*)*xres * yres); } // set audio options sfxvolume = settings_sfxvolume; diff --git a/src/menu.hpp b/src/menu.hpp index 7ad8beb0d..a1938671e 100644 --- a/src/menu.hpp +++ b/src/menu.hpp @@ -283,6 +283,8 @@ extern Sint32 oldXres; extern Sint32 oldYres; extern button_t* revertResolutionButton; +typedef std::tuple resolution; +void getResolutionList(std::list&); void applySettings(); void openConfirmResolutionWindow(); void buttonAcceptResolution(button_t* my); From 04f9cda2783920d62e5b831653e9dce3811ea429 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Sat, 9 Oct 2021 17:16:12 -0700 Subject: [PATCH 08/39] Frame entries can be justified horizontally Settings window lets you change screen resolution Signed-off-by: SheridanR --- src/ui/Frame.cpp | 23 ++++--- src/ui/Frame.hpp | 18 ++++- src/ui/MainMenu.cpp | 163 ++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 179 insertions(+), 25 deletions(-) diff --git a/src/ui/Frame.cpp b/src/ui/Frame.cpp index 34873a11d..98cda4720 100644 --- a/src/ui/Frame.cpp +++ b/src/ui/Frame.cpp @@ -382,7 +382,11 @@ void Frame::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vectorgetHeight(); SDL_Rect pos; - pos.x = _size.x + border + listOffset.x - scroll.x; + switch (justify) { + case justify_t::LEFT: pos.x = _size.x + border + listOffset.x - scroll.x; break; + case justify_t::CENTER: pos.x = _size.x + (_size.w - textSizeW) / 2 + listOffset.x - scroll.x; break; + case justify_t::RIGHT: pos.x = _size.x + _size.w - textSizeW - border + listOffset.x - scroll.x; break; + } pos.y = _size.y + border + listOffset.y + i * entrySize - scroll.y; pos.w = textSizeW; pos.h = textSizeH; @@ -696,11 +700,11 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: if (this->actualSize.h <= size.h) { if (mousestatus[SDL_BUTTON_WHEELDOWN]) { mousestatus[SDL_BUTTON_WHEELDOWN] = 0; - this->actualSize.x += std::min(entrySize * 4, size.w); + this->actualSize.x += std::min(entrySize * 2, size.w); usable = result.usable = false; } else if (mousestatus[SDL_BUTTON_WHEELUP]) { mousestatus[SDL_BUTTON_WHEELUP] = 0; - this->actualSize.x -= std::min(entrySize * 4, size.w); + this->actualSize.x -= std::min(entrySize * 2, size.w); usable = result.usable = false; } } @@ -710,11 +714,11 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: if (this->actualSize.h > size.h) { if (mousestatus[SDL_BUTTON_WHEELDOWN]) { mousestatus[SDL_BUTTON_WHEELDOWN] = 0; - this->actualSize.y += std::min(entrySize * 4, size.h); + this->actualSize.y += std::min(entrySize * 2, size.h); usable = result.usable = false; } else if (mousestatus[SDL_BUTTON_WHEELUP]) { mousestatus[SDL_BUTTON_WHEELUP] = 0; - this->actualSize.y -= std::min(entrySize * 4, size.h); + this->actualSize.y -= std::min(entrySize * 2, size.h); usable = result.usable = false; } } @@ -793,7 +797,7 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: ticks = -1; // hack to fix sliders in drop downs } else if (rectContainsPoint(sliderRect, omousex, omousey)) { if (mousestatus[SDL_BUTTON_LEFT]) { - this->actualSize.x += omousex < handleRect.x ? -std::min(entrySize * 4, size.w) : std::min(entrySize * 4, size.w); + this->actualSize.x += omousex < handleRect.x ? -std::min(entrySize * 2, size.w) : std::min(entrySize * 2, size.w); this->actualSize.x = std::min(std::max(0, this->actualSize.x), std::max(0, this->actualSize.w - size.w)); mousestatus[SDL_BUTTON_LEFT] = 0; } @@ -843,7 +847,7 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: ticks = -1; // hack to fix sliders in drop downs } else if (rectContainsPoint(sliderRect, omousex, omousey)) { if (mousestatus[SDL_BUTTON_LEFT]) { - this->actualSize.y += omousey < handleRect.y ? -std::min(entrySize * 4, size.h) : std::min(entrySize * 4, size.h); + this->actualSize.y += omousey < handleRect.y ? -std::min(entrySize * 2, size.h) : std::min(entrySize * 2, size.h); this->actualSize.y = std::min(std::max(0, this->actualSize.y), std::max(0, this->actualSize.h - size.h)); mousestatus[SDL_BUTTON_LEFT] = 0; } @@ -1259,7 +1263,6 @@ void Frame::resizeForEntries() { entrySize = _font->height(); entrySize += entrySize / 2; } - actualSize.w = size.w; actualSize.h = (Uint32)list.size() * entrySize; actualSize.y = std::min(std::max(0, actualSize.y), std::max(0, actualSize.h - size.h)); } @@ -1440,7 +1443,7 @@ void Frame::enableScroll(bool enabled) { allowScrolling = enabled; } -void Frame::scrollToSelection() { +void Frame::scrollToSelection(bool scroll_to_top) { if (selection == -1) { return; } @@ -1457,7 +1460,7 @@ void Frame::scrollToSelection() { entrySize = _font->height(); entrySize += entrySize / 2; } - if (actualSize.y > index * entrySize) { + if (scroll_to_top || actualSize.y > index * entrySize) { actualSize.y = index * entrySize; } if (actualSize.y + size.h < (index + 1) * entrySize) { diff --git a/src/ui/Frame.hpp b/src/ui/Frame.hpp index b36d9e566..a52d9f80d 100644 --- a/src/ui/Frame.hpp +++ b/src/ui/Frame.hpp @@ -36,6 +36,14 @@ class Frame : public Widget { BORDER_MAX }; + //! list text justification + enum justify_t { + LEFT, + RIGHT, + CENTER, + JUSTIFY_TYPE_LENGTH + }; + //! frame image struct image_t { std::string name; @@ -254,6 +262,10 @@ class Frame : public Widget { //! @param scroll the amount by which to offset the image in x/y void drawImage(image_t* image, const SDL_Rect& _size, const SDL_Rect& scroll); + //! scroll to the current list entry selection in the frame + //! @param scroll_to_top if true, scroll the selection to the very top of the frame + void scrollToSelection(bool scroll_to_top = false); + virtual type_t getType() const override { return WIDGET_FRAME; } const char* getFont() const { return font.c_str(); } const int getBorder() const { return border; } @@ -276,6 +288,7 @@ class Frame : public Widget { int getSelection() const { return selection; } real_t getOpacity() const { return opacity; } const bool getInheritParentFrameOpacity() const { return inheritParentFrameOpacity; } + justify_t getJustify() const { return justify; } void setFont(const char* _font) { font = _font; } void setBorder(const int _border) { border = _border; } @@ -294,6 +307,7 @@ class Frame : public Widget { void setListOffset(SDL_Rect _size) { listOffset = _size; } void setInheritParentFrameOpacity(const bool _inherit) { inheritParentFrameOpacity = _inherit; } void setOpacity(const real_t _opacity) { opacity = _opacity; } + void setListJustify(justify_t _justify) { justify = _justify; } private: Uint32 ticks = 0; //!< number of engine ticks this frame has persisted @@ -320,6 +334,7 @@ class Frame : public Widget { SDL_Rect listOffset{0, 0, 0, 0}; //!< frame list offset in x, y real_t opacity = 100.0; //!< opacity multiplier of elements within this frame (image/fields etc) bool inheritParentFrameOpacity = true; //!< if true, uses parent frame opacity + justify_t justify = justify_t::LEFT; //!< frame list horizontal justification std::vector frames; std::vector buttons; @@ -328,9 +343,6 @@ class Frame : public Widget { std::vector sliders; std::vector list; - //! scroll to the current list entry selection in the frame - void scrollToSelection(); - //! activate the given list entry //! @param entry the entry to activate void activateEntry(entry_t& entry); diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 33238ecb0..b7ff94a96 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -6,6 +6,7 @@ #include "Slider.hpp" #include "Text.hpp" +#include "../init.hpp" #include "../net.hpp" #include "../player.hpp" #include "../stat.hpp" @@ -909,6 +910,112 @@ namespace MainMenu { ); } + static void settingsResolution(Button& button) { + auto settings = main_menu_frame->findFrame("settings"); assert(settings); + auto settings_subwindow = settings->findFrame("settings_subwindow"); assert(settings_subwindow); + auto resolution = settings_subwindow->findButton("setting_resolution_dropdown_button"); assert(resolution); + auto dropdown = settings_subwindow->addFrame("setting_resolution_dropdown"); assert(dropdown); + dropdown->setSize(SDL_Rect{ + resolution->getSize().x, + resolution->getSize().y, + 174, + 362 + }); + dropdown->setActualSize(SDL_Rect{0, 0, dropdown->getSize().w, dropdown->getSize().h}); + dropdown->setColor(0); + dropdown->setBorder(0); + dropdown->setDropDown(true); + + auto background = dropdown->addImage( + dropdown->getActualSize(), + 0xffffffff, + "images/ui/Main Menus/Settings/Settings_Drop_ScrollBG00.png", + "background" + ); + + int border = 4; + auto dropdown_list = dropdown->addFrame("list"); + dropdown_list->setSize(SDL_Rect{0, border, dropdown->getSize().w, dropdown->getSize().h - border*2}); + dropdown_list->setActualSize(SDL_Rect{0, 0, dropdown_list->getSize().w, dropdown_list->getSize().h}); + dropdown_list->setColor(0); + dropdown_list->setBorder(0); + dropdown_list->setFont(bigfont_outline); + dropdown_list->setListOffset(SDL_Rect{0, 12, 0, 0}); + dropdown_list->setListJustify(Frame::justify_t::CENTER); + dropdown_list->setScrollBarsEnabled(false); + dropdown_list->select(); + + for (int i = 0;; ++i) { + auto str = std::string("__") + std::to_string(i); + auto find = resolution->getWidgetActions().find(str); + if (find != resolution->getWidgetActions().end()) { + auto res = find->second.c_str(); + auto entry = dropdown_list->addEntry(res, false); + entry->text = res; + entry->click = [](Frame::entry_t& entry){ + int new_xres, new_yres; + sscanf(entry.name.c_str(), "%d x %d", &new_xres, &new_yres); + if (new_xres != xres || new_yres != yres) { + xres = new_xres; + yres = new_yres; + if ( !changeVideoMode() ) { + printlog("critical error! Attempting to abort safely...\n"); + mainloop = 0; + } + auto settings = main_menu_frame->findFrame("settings"); assert(settings); + auto settings_subwindow = settings->findFrame("settings_subwindow"); assert(settings_subwindow); + auto resolution = settings_subwindow->findButton("setting_resolution_dropdown_button"); assert(resolution); + resolution->setText(entry.name.c_str()); + } + }; + entry->ctrlClick = entry->click; + dropdown_list->resizeForEntries(); + auto size = dropdown_list->getActualSize(); + size.h += 14; + dropdown_list->setActualSize(size); + + int x, y; + sscanf(entry->name.c_str(), "%d x %d", &x, &y); + if (x == xres && y == yres) { + dropdown_list->setSelection(i); + dropdown_list->scrollToSelection(true); + } + } + else { + break; + } + } + + auto selection = dropdown_list->addImage( + SDL_Rect{8, 0, 158, 30}, + 0xffffffff, + "images/ui/Main Menus/Settings/Settings_Drop_SelectBacking00.png", + "selection" + ); + dropdown_list->setTickCallback([](Widget& widget){ + Frame* dropdown_list = static_cast(&widget); assert(dropdown_list); + auto selection = dropdown_list->findImage("selection"); assert(selection); + if (dropdown_list->getSelection() >= 0 && dropdown_list->getSelection() < dropdown_list->getEntries().size()) { + selection->disabled = false; + int entrySize = 0; + Font* _font = Font::get(bigfont_outline); + if (_font != nullptr) { + entrySize = _font->height(); + entrySize += entrySize / 2; + } + selection->pos.y = dropdown_list->getSelection() * entrySize + 12; + } else { + selection->disabled = true; + } + auto size = dropdown_list->getActualSize(); + size.x = 0; + dropdown_list->setActualSize(size); + }); + } + + static void settingsWindowMode(Button& button) { + } + static int settingsAddSubHeader(Frame& frame, int y, const char* name, const char* text) { std::string fullname = std::string("subheader_") + name; auto image = frame.addImage( @@ -1078,21 +1185,22 @@ namespace MainMenu { const char* text, const char* tip, const std::vector& items, + const char* selected, void (*callback)(Button&)) { std::string fullname = std::string("setting_") + name; int result = settingsAddOption(frame, y, name, text, tip); - auto button = frame.addButton((fullname + "_dropdown").c_str()); + auto button = frame.addButton((fullname + "_dropdown_button").c_str()); button->setSize(SDL_Rect{ 390, - y + 4, - 158, - 44}); - button->setFont(smallfont_outline); - button->setText(items[0]); + y, + 174, + 52}); + button->setFont(bigfont_outline); + button->setText(selected); button->setJustify(Button::justify_t::CENTER); button->setCallback(callback); - button->setBackground("images/ui/Main Menus/Settings/Settings_Button_Customize00.png"); + button->setBackground("images/ui/Main Menus/Settings/Settings_Drop_ScrollBG02.png"); button->setHighlightColor(makeColor(255,255,255,255)); button->setColor(makeColor(127,127,127,255)); button->setTextHighlightColor(makeColor(255,255,255,255)); @@ -1102,6 +1210,9 @@ namespace MainMenu { button->setWidgetPageRight("tab_right"); button->addWidgetAction("MenuAlt1", "restore_defaults"); button->addWidgetAction("MenuStart", "confirm_and_exit"); + for (int i = 0; i < items.size(); ++i) { + button->addWidgetAction((std::string("__") + std::to_string(i)).c_str(), items[i]); + } return result; } @@ -1296,7 +1407,7 @@ namespace MainMenu { std::string("setting_") + std::string(setting.name) + std::string("_customize_button")); default: return std::make_pair( - std::string("setting_") + std::string(setting.name) + std::string("_dropdown"), + std::string("setting_") + std::string(setting.name) + std::string("_dropdown_button"), std::string("")); } } @@ -1445,14 +1556,42 @@ namespace MainMenu { "Toggle the flickering appearance of torches and other light fixtures in the game world.", allSettings.light_flicker_enabled, [](Button& button){soundToggle(); allSettings.light_flicker_enabled = button.isPressed();}); + int selected_res = 0; + std::list resolutions; + getResolutionList(resolutions); + std::vector resolutions_formatted; + std::vector resolutions_formatted_ptrs; + resolutions_formatted.reserve(resolutions.size()); + resolutions_formatted_ptrs.reserve(resolutions.size()); + + int index; + std::list::iterator it; + for (index = 0, it = resolutions.begin(); it != resolutions.end(); ++it, ++index) { + auto& res = *it; + const int x = std::get<0>(res); + const int y = std::get<1>(res); + char buf[32]; + snprintf(buf, sizeof(buf), "%d x %d", x, y); + resolutions_formatted.push_back(std::string(buf)); + resolutions_formatted_ptrs.push_back(resolutions_formatted.back().c_str()); + if (xres == x && yres == y) { + selected_res = index; + } + } + + const char* selected_mode = fullscreen ? + borderless ? + "Borderless" : "Fullscreen": + "Windowed"; + y += settingsAddSubHeader(*settings_subwindow, y, "display", "Display"); #ifndef NINTENDO y += settingsAddDropdown(*settings_subwindow, y, "resolution", "Resolution", "Change the current window resolution.", - {"1280 x 720", "1920 x 1080"}, - nullptr); + resolutions_formatted_ptrs, resolutions_formatted_ptrs[selected_res], + settingsResolution); y += settingsAddDropdown(*settings_subwindow, y, "window_mode", "Window Mode", "Change the current display mode.", - {"Fullscreen", "Borderless", "Windowed"}, - nullptr); + {"Fullscreen", "Borderless", "Windowed"}, selected_mode, + settingsWindowMode); y += settingsAddBooleanOption(*settings_subwindow, y, "vsync", "Vertical Sync", "Prevent screen-tearing by locking the game's refresh rate to the current display.", allSettings.vsync_enabled, [](Button& button){soundToggle(); allSettings.vsync_enabled = button.isPressed();}); From 05e69e9ec1c96d3a8426266950ad2deb68106fea Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 13 Oct 2021 18:58:21 -0700 Subject: [PATCH 09/39] perfected dropdowns in settings window Signed-off-by: SheridanR --- src/input.cpp | 14 ++++ src/ui/Frame.cpp | 17 ++--- src/ui/MainMenu.cpp | 158 +++++++++++++++++++++++++++----------------- src/ui/MainMenu.hpp | 1 + 4 files changed, 119 insertions(+), 71 deletions(-) diff --git a/src/input.cpp b/src/input.cpp index c52ec3d08..8b831d0df 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -92,6 +92,20 @@ void Input::defaultBindings() { inputs[c].bind("HotbarSlot9", "9"); inputs[c].bind("HotbarSlot10", "0"); } +#ifndef NINTENDO + inputs[0].bind("MenuUp", "Up"); + inputs[0].bind("MenuLeft", "Left"); + inputs[0].bind("MenuRight", "Right"); + inputs[0].bind("MenuDown", "Down"); + inputs[0].bind("MenuConfirm", "Space"); + inputs[0].bind("MenuCancel", "Escape"); + inputs[0].bind("MenuAlt1", "Left Shift"); + inputs[0].bind("MenuAlt2", "Left Alt"); + inputs[0].bind("MenuStart", "Return"); + inputs[0].bind("MenuSelect", "Backspace"); + inputs[0].bind("MenuPageLeft", "["); + inputs[0].bind("MenuPageRight", "]"); +#endif } float Input::analog(const char* binding) const { diff --git a/src/ui/Frame.cpp b/src/ui/Frame.cpp index 98cda4720..dde48e1ab 100644 --- a/src/ui/Frame.cpp +++ b/src/ui/Frame.cpp @@ -635,9 +635,6 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: search->select(); } } - if (dropDown) { - toBeDeleted = true; - } } // choose a selection @@ -931,6 +928,7 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: result.tooltip = entry->tooltip.c_str(); if (mousestatus[SDL_BUTTON_LEFT]) { if (!entry->pressed) { + mousestatus[SDL_BUTTON_LEFT] = 0; entry->pressed = true; activateEntry(*entry); } @@ -1444,22 +1442,16 @@ void Frame::enableScroll(bool enabled) { } void Frame::scrollToSelection(bool scroll_to_top) { - if (selection == -1) { + if (selection < 0 || selection >= list.size()) { return; } - int index = 0; - for (auto entry : list) { - if (entry == list[selection]) { - break; - } - ++index; - } int entrySize = 20; Font* _font = Font::get(font.c_str()); if (_font != nullptr) { entrySize = _font->height(); entrySize += entrySize / 2; } + const int index = selection; if (scroll_to_top || actualSize.y > index * entrySize) { actualSize.y = index * entrySize; } @@ -1478,6 +1470,9 @@ void Frame::activateEntry(entry_t& entry) { (*entry.click)(entry); } } + if (dropDown) { + toBeDeleted = true; + } } void createTestUI() { diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index b7ff94a96..b10fba079 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -44,9 +44,10 @@ namespace MainMenu { static const char* bigfont_outline = "fonts/pixelmix.ttf#16#2"; static const char* bigfont_no_outline = "fonts/pixelmix.ttf#16#0"; - static const char* smallfont_outline = "fonts/pixel_maz.ttf#14#2"; - static const char* smallfont_no_outline = "fonts/pixel_maz.ttf#14#0"; - static const char* menu_option_font = "fonts/pixel_maz.ttf#24#2"; + static const char* smallfont_outline = "fonts/pixel_maz.ttf#32#2"; + static const char* smallfont_no_outline = "fonts/pixel_maz.ttf#32#0"; + static const char* menu_option_font = "fonts/pixel_maz.ttf#48#2"; + static const char* banner_font = "fonts/pixel_maz.ttf#64#2"; static inline void soundToggleMenu() { playSound(500, 48); @@ -448,11 +449,7 @@ namespace MainMenu { shaking = allSettings.shaking_enabled; bobbing = allSettings.bobbing_enabled; flickerLights = allSettings.light_flicker_enabled; - xres = allSettings.resolution_x; - yres = allSettings.resolution_y; - verticalSync = allSettings.vsync_enabled; vertical_splitscreen = allSettings.vertical_split_enabled; - vidgamma = allSettings.gamma / 100.f; fov = allSettings.fov; fpsLimit = allSettings.fps; master_volume = allSettings.master_volume; @@ -480,6 +477,31 @@ namespace MainMenu { svFlags = allSettings.extra_life_enabled ? svFlags | SV_FLAG_LIFESAVING : svFlags & ~(SV_FLAG_LIFESAVING); svFlags = allSettings.cheats_enabled ? svFlags | SV_FLAG_CHEATS : svFlags & ~(SV_FLAG_CHEATS); + // change video mode + switch (allSettings.window_mode) { + case 0: + fullscreen = false; + borderless = false; + break; + case 1: + fullscreen = true; + borderless = false; + break; + case 2: + fullscreen = true; + borderless = true; + break; + default: + assert("Unknown video mode" && 0); + break; + } + vidgamma = allSettings.gamma / 100.f; + verticalSync = allSettings.vsync_enabled; + if ( !changeVideoMode(allSettings.resolution_x, allSettings.resolution_y) ) { + printlog("critical error! Attempting to abort safely...\n"); + mainloop = 0; + } + // transmit server flags if ( !intro && multiplayer == SERVER ) { strcpy((char*)net_packet->data, "SVFL"); @@ -528,6 +550,7 @@ namespace MainMenu { allSettings.shaking_enabled = true; allSettings.bobbing_enabled = true; allSettings.light_flicker_enabled = true; + allSettings.window_mode = 0; allSettings.resolution_x = 1280; allSettings.resolution_y = 720; allSettings.vsync_enabled = true; @@ -910,16 +933,16 @@ namespace MainMenu { ); } - static void settingsResolution(Button& button) { + static void settingsOpenDropdown(Button& button, const char* name, bool small_dropdown, void(*entry_func)(Frame::entry_t&)) { + std::string dropdown_name = "setting_" + std::string(name) + "_dropdown"; auto settings = main_menu_frame->findFrame("settings"); assert(settings); auto settings_subwindow = settings->findFrame("settings_subwindow"); assert(settings_subwindow); - auto resolution = settings_subwindow->findButton("setting_resolution_dropdown_button"); assert(resolution); - auto dropdown = settings_subwindow->addFrame("setting_resolution_dropdown"); assert(dropdown); + auto dropdown = settings_subwindow->addFrame(dropdown_name.c_str()); assert(dropdown); dropdown->setSize(SDL_Rect{ - resolution->getSize().x, - resolution->getSize().y, + button.getSize().x, + button.getSize().y, 174, - 362 + small_dropdown ? 181 : 362 }); dropdown->setActualSize(SDL_Rect{0, 0, dropdown->getSize().w, dropdown->getSize().h}); dropdown->setColor(0); @@ -929,7 +952,9 @@ namespace MainMenu { auto background = dropdown->addImage( dropdown->getActualSize(), 0xffffffff, - "images/ui/Main Menus/Settings/Settings_Drop_ScrollBG00.png", + small_dropdown ? + "images/ui/Main Menus/Settings/Settings_Drop_ScrollBG01.png" : + "images/ui/Main Menus/Settings/Settings_Drop_ScrollBG00.png", "background" ); @@ -940,51 +965,34 @@ namespace MainMenu { dropdown_list->setColor(0); dropdown_list->setBorder(0); dropdown_list->setFont(bigfont_outline); - dropdown_list->setListOffset(SDL_Rect{0, 12, 0, 0}); + dropdown_list->setListOffset(SDL_Rect{0, 11, 0, 0}); dropdown_list->setListJustify(Frame::justify_t::CENTER); dropdown_list->setScrollBarsEnabled(false); dropdown_list->select(); + dropdown_list->activate(); for (int i = 0;; ++i) { auto str = std::string("__") + std::to_string(i); - auto find = resolution->getWidgetActions().find(str); - if (find != resolution->getWidgetActions().end()) { - auto res = find->second.c_str(); - auto entry = dropdown_list->addEntry(res, false); - entry->text = res; - entry->click = [](Frame::entry_t& entry){ - int new_xres, new_yres; - sscanf(entry.name.c_str(), "%d x %d", &new_xres, &new_yres); - if (new_xres != xres || new_yres != yres) { - xres = new_xres; - yres = new_yres; - if ( !changeVideoMode() ) { - printlog("critical error! Attempting to abort safely...\n"); - mainloop = 0; - } - auto settings = main_menu_frame->findFrame("settings"); assert(settings); - auto settings_subwindow = settings->findFrame("settings_subwindow"); assert(settings_subwindow); - auto resolution = settings_subwindow->findButton("setting_resolution_dropdown_button"); assert(resolution); - resolution->setText(entry.name.c_str()); - } - }; - entry->ctrlClick = entry->click; + auto find = button.getWidgetActions().find(str); + if (find != button.getWidgetActions().end()) { + auto entry_name = find->second.c_str(); + auto entry = dropdown_list->addEntry(entry_name, false); + entry->text = entry_name; + entry->click = entry_func; + entry->ctrlClick = entry_func; dropdown_list->resizeForEntries(); auto size = dropdown_list->getActualSize(); size.h += 14; dropdown_list->setActualSize(size); - - int x, y; - sscanf(entry->name.c_str(), "%d x %d", &x, &y); - if (x == xres && y == yres) { + if (strcmp(button.getText(), entry_name) == 0) { dropdown_list->setSelection(i); - dropdown_list->scrollToSelection(true); } } else { break; } } + dropdown_list->scrollToSelection(true); auto selection = dropdown_list->addImage( SDL_Rect{8, 0, 158, 30}, @@ -992,6 +1000,7 @@ namespace MainMenu { "images/ui/Main Menus/Settings/Settings_Drop_SelectBacking00.png", "selection" ); + dropdown_list->setTickCallback([](Widget& widget){ Frame* dropdown_list = static_cast(&widget); assert(dropdown_list); auto selection = dropdown_list->findImage("selection"); assert(selection); @@ -1003,17 +1012,53 @@ namespace MainMenu { entrySize = _font->height(); entrySize += entrySize / 2; } - selection->pos.y = dropdown_list->getSelection() * entrySize + 12; + selection->pos.y = dropdown_list->getSelection() * entrySize + 8; } else { selection->disabled = true; } - auto size = dropdown_list->getActualSize(); - size.x = 0; - dropdown_list->setActualSize(size); + }); + } + + static void settingsResolution(Button& button) { + settingsOpenDropdown(button, "resolution", false, [](Frame::entry_t& entry){ + int new_xres, new_yres; + sscanf(entry.name.c_str(), "%d x %d", &new_xres, &new_yres); + allSettings.resolution_x = new_xres; + allSettings.resolution_y = new_yres; + auto settings = main_menu_frame->findFrame("settings"); assert(settings); + auto settings_subwindow = settings->findFrame("settings_subwindow"); assert(settings_subwindow); + auto button = settings_subwindow->findButton("setting_resolution_dropdown_button"); assert(button); + auto dropdown = settings_subwindow->findFrame("setting_resolution_dropdown"); assert(dropdown); + button->setText(entry.name.c_str()); + dropdown->removeSelf(); + button->select(); }); } static void settingsWindowMode(Button& button) { + settingsOpenDropdown(button, "window_mode", true, [](Frame::entry_t& entry){ + do { + if (entry.name == "Windowed") { + allSettings.window_mode = 0; + break; + } + if (entry.name == "Fullscreen") { + allSettings.window_mode = 1; + break; + } + if (entry.name == "Borderless") { + allSettings.window_mode = 2; + break; + } + } while (0); + auto settings = main_menu_frame->findFrame("settings"); assert(settings); + auto settings_subwindow = settings->findFrame("settings_subwindow"); assert(settings_subwindow); + auto button = settings_subwindow->findButton("setting_window_mode_dropdown_button"); assert(button); + auto dropdown = settings_subwindow->findFrame("setting_window_mode_dropdown"); assert(dropdown); + button->setText(entry.name.c_str()); + dropdown->removeSelf(); + button->select(); + }); } static int settingsAddSubHeader(Frame& frame, int y, const char* name, const char* text) { @@ -1201,6 +1246,7 @@ namespace MainMenu { button->setJustify(Button::justify_t::CENTER); button->setCallback(callback); button->setBackground("images/ui/Main Menus/Settings/Settings_Drop_ScrollBG02.png"); + button->setBackgroundHighlighted("images/ui/Main Menus/Settings/Settings_Drop_ScrollBG02_Highlighted.png"); button->setHighlightColor(makeColor(255,255,255,255)); button->setColor(makeColor(127,127,127,255)); button->setTextHighlightColor(makeColor(255,255,255,255)); @@ -1574,15 +1620,12 @@ namespace MainMenu { snprintf(buf, sizeof(buf), "%d x %d", x, y); resolutions_formatted.push_back(std::string(buf)); resolutions_formatted_ptrs.push_back(resolutions_formatted.back().c_str()); - if (xres == x && yres == y) { + if (allSettings.resolution_x == x && allSettings.resolution_y == y) { selected_res = index; } } - const char* selected_mode = fullscreen ? - borderless ? - "Borderless" : "Fullscreen": - "Windowed"; + const char* selected_mode = fullscreen ? (borderless ? "Borderless" : "Fullscreen") : "Windowed"; y += settingsAddSubHeader(*settings_subwindow, y, "display", "Display"); #ifndef NINTENDO @@ -1590,7 +1633,7 @@ namespace MainMenu { resolutions_formatted_ptrs, resolutions_formatted_ptrs[selected_res], settingsResolution); y += settingsAddDropdown(*settings_subwindow, y, "window_mode", "Window Mode", "Change the current display mode.", - {"Fullscreen", "Borderless", "Windowed"}, selected_mode, + {"Windowed", "Fullscreen", "Borderless"}, selected_mode, settingsWindowMode); y += settingsAddBooleanOption(*settings_subwindow, y, "vsync", "Vertical Sync", "Prevent screen-tearing by locking the game's refresh rate to the current display.", @@ -3801,8 +3844,6 @@ namespace MainMenu { "backdrop" ); - static const char* banner_font = "fonts/pixel_maz.ttf#36#2"; - auto banner = card->addField("invite_banner", 64); banner->setText((std::string("PLAYER ") + std::to_string(index + 1)).c_str()); banner->setFont(banner_font); @@ -3854,8 +3895,6 @@ namespace MainMenu { "backdrop" ); - static const char* banner_font = "fonts/pixel_maz.ttf#36#2"; - auto banner = card->addField("invite_banner", 64); banner->setText("INVITE"); banner->setFont(banner_font); @@ -4257,6 +4296,7 @@ namespace MainMenu { allSettings.shaking_enabled = shaking; allSettings.bobbing_enabled = bobbing; allSettings.light_flicker_enabled = flickerLights; + allSettings.window_mode = fullscreen ? (borderless ? 2 : 1) : 0; allSettings.resolution_x = xres; allSettings.resolution_y = yres; allSettings.vsync_enabled = verticalSync; @@ -4333,10 +4373,8 @@ namespace MainMenu { } }); - static const char* pixel_maz_outline = "fonts/pixel_maz.ttf#36#2"; - auto window_title = settings->addField("window_title", 64); - window_title->setFont(pixel_maz_outline); + window_title->setFont(banner_font); window_title->setSize(SDL_Rect{394, 26, 338, 24}); window_title->setJustify(Field::justify_t::CENTER); window_title->setText("SETTINGS"); @@ -4356,7 +4394,7 @@ namespace MainMenu { auto button = settings->addButton(tabs[c].name); button->setCallback(tabs[c].callback); button->setText(tabs[c].name); - button->setFont(pixel_maz_outline); + button->setFont(banner_font); button->setBackground("images/ui/Main Menus/Settings/Settings_Button_SubTitle00.png"); button->setBackgroundActivated("images/ui/Main Menus/Settings/Settings_Button_SubTitleSelect00.png"); button->setSize(SDL_Rect{76 + (272 - 76) * c, 64, 184, 64}); diff --git a/src/ui/MainMenu.hpp b/src/ui/MainMenu.hpp index d317dd631..95276e005 100644 --- a/src/ui/MainMenu.hpp +++ b/src/ui/MainMenu.hpp @@ -54,6 +54,7 @@ namespace MainMenu { bool shaking_enabled; bool bobbing_enabled; bool light_flicker_enabled; + int window_mode; // 0 = windowed, 1 = fullscreen, 2 = borderless int resolution_x; int resolution_y; bool vsync_enabled; From e670447864adb352f6f24d2e02c41e1db26246b7 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 13 Oct 2021 19:19:03 -0700 Subject: [PATCH 10/39] fix some crashes, add sfx Signed-off-by: SheridanR --- src/ui/MainMenu.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index b10fba079..c756db62a 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -1021,6 +1021,7 @@ namespace MainMenu { static void settingsResolution(Button& button) { settingsOpenDropdown(button, "resolution", false, [](Frame::entry_t& entry){ + soundActivate(); int new_xres, new_yres; sscanf(entry.name.c_str(), "%d x %d", &new_xres, &new_yres); allSettings.resolution_x = new_xres; @@ -1037,6 +1038,7 @@ namespace MainMenu { static void settingsWindowMode(Button& button) { settingsOpenDropdown(button, "window_mode", true, [](Frame::entry_t& entry){ + soundActivate(); do { if (entry.name == "Windowed") { allSettings.window_mode = 0; @@ -1509,6 +1511,8 @@ namespace MainMenu { void settingsUI(Button& button) { Frame* settings_subwindow; if ((settings_subwindow = settingsSubwindowSetup(button)) == 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::BooleanWithCustomize, "add_items_to_hotbar"}); return; } @@ -1575,6 +1579,8 @@ namespace MainMenu { void settingsVideo(Button& button) { Frame* settings_subwindow; if ((settings_subwindow = settingsSubwindowSetup(button)) == 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, "content_control"}); return; } @@ -1689,6 +1695,8 @@ namespace MainMenu { void settingsAudio(Button& button) { Frame* settings_subwindow; if ((settings_subwindow = settingsSubwindowSetup(button)) == 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::Slider, "master_volume"}); return; } @@ -1752,6 +1760,8 @@ namespace MainMenu { void settingsControls(Button& button) { Frame* settings_subwindow; if ((settings_subwindow = settingsSubwindowSetup(button)) == 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"}); return; } @@ -1820,6 +1830,8 @@ namespace MainMenu { void settingsGame(Button& button) { Frame* settings_subwindow; if ((settings_subwindow = settingsSubwindowSetup(button)) == 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, "classic_mode"}); return; } From 7d916f67aaeaa2003c0a9b523ec1918921a52045 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 13 Oct 2021 23:02:46 -0700 Subject: [PATCH 11/39] fix broken stuff post-merge Signed-off-by: SheridanR --- src/editor.cpp | 4 ---- src/init_game.cpp | 2 +- src/input.cpp | 25 +++++++++++++++++++++++++ src/ui/GameUI.cpp | 2 +- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/editor.cpp b/src/editor.cpp index 1d8feccc2..2deff2639 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -73,10 +73,6 @@ void actHudArrowModel(Entity* my) {} // dummy for draw.cpp void actLeftHandMagic(Entity* my) {} // dummy for draw.cpp void actRightHandMagic(Entity* my) {} // dummy for draw.cpp void messagePlayer(int player, char const * const message, ...) {} // dummy -void updateLoadingScreen(real_t progress) {} // dummy -void doLoadingScreen() {} -void destroyLoadingScreen() {} -void createLoadingScreen(real_t progress) {} map_t copymap; diff --git a/src/init_game.cpp b/src/init_game.cpp index e035aece3..c291ecc25 100644 --- a/src/init_game.cpp +++ b/src/init_game.cpp @@ -282,7 +282,7 @@ int initGame() } } FileIO::close(fp); - bookParser_t.createBooks(false); + //bookParser_t.createBooks(false); setupSpells(); #ifdef NINTENDO diff --git a/src/input.cpp b/src/input.cpp index 8b831d0df..44c2594a8 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -1,5 +1,12 @@ +#include "main.hpp" #include "input.hpp" +#ifdef EDITOR +#ifndef TICKS_PER_SECOND +#define TICKS_PER_SECOND 50 +#endif +#else #include "player.hpp" +#endif #include @@ -115,6 +122,7 @@ float Input::analog(const char* binding) const { bool Input::binary(const char* binding) const { auto b = bindings.find(binding); +#ifndef EDITOR if ( b != bindings.end() ) { if ( (*b).second.type == binding_t::bindtype_t::KEYBOARD && ::inputs.bPlayerUsingKeyboardControl(player) == false ) @@ -122,11 +130,13 @@ bool Input::binary(const char* binding) const { return false; } } +#endif return b != bindings.end() ? (*b).second.binary : false; } bool Input::binaryToggle(const char* binding) const { auto b = bindings.find(binding); +#ifndef EDITOR if ( b != bindings.end() ) { if ( (*b).second.type == binding_t::bindtype_t::KEYBOARD && ::inputs.bPlayerUsingKeyboardControl(player) == false ) @@ -134,11 +144,13 @@ bool Input::binaryToggle(const char* binding) const { return false; } } +#endif return b != bindings.end() ? (*b).second.binary && !(*b).second.consumed : false; } bool Input::analogToggle(const char* binding) const { auto b = bindings.find(binding); +#ifndef EDITOR if ( b != bindings.end() ) { if ( (*b).second.type == binding_t::bindtype_t::KEYBOARD && ::inputs.bPlayerUsingKeyboardControl(player) == false ) @@ -146,11 +158,13 @@ bool Input::analogToggle(const char* binding) const { return false; } } +#endif return b != bindings.end() ? (*b).second.analog > analogToggleThreshold && !(*b).second.analogConsumed : false; } bool Input::binaryReleaseToggle(const char* binding) const { auto b = bindings.find(binding); +#ifndef EDITOR if ( b != bindings.end() ) { if ( (*b).second.type == binding_t::bindtype_t::KEYBOARD && ::inputs.bPlayerUsingKeyboardControl(player) == false ) @@ -158,11 +172,13 @@ bool Input::binaryReleaseToggle(const char* binding) const { return false; } } +#endif return b != bindings.end() ? (*b).second.binaryRelease && !(*b).second.binaryReleaseConsumed : false; } bool Input::consumeAnalogToggle(const char* binding) { auto b = bindings.find(binding); +#ifndef EDITOR if ( b != bindings.end() ) { if ( (*b).second.type == binding_t::bindtype_t::KEYBOARD && ::inputs.bPlayerUsingKeyboardControl(player) == false ) @@ -170,6 +186,7 @@ bool Input::consumeAnalogToggle(const char* binding) { return false; } } +#endif if ( b != bindings.end() && (*b).second.analog > analogToggleThreshold && !(*b).second.analogConsumed ) { (*b).second.analogConsumed = true; return true; @@ -181,6 +198,7 @@ bool Input::consumeAnalogToggle(const char* binding) { bool Input::consumeBinaryToggle(const char* binding) { auto b = bindings.find(binding); +#ifndef EDITOR if ( b != bindings.end() ) { if ( (*b).second.type == binding_t::bindtype_t::KEYBOARD && ::inputs.bPlayerUsingKeyboardControl(player) == false ) @@ -188,6 +206,7 @@ bool Input::consumeBinaryToggle(const char* binding) { return false; } } +#endif if (b != bindings.end() && (*b).second.binary && !(*b).second.consumed) { (*b).second.consumed = true; if ( (*b).second.type == binding_t::bindtype_t::MOUSE_BUTTON @@ -204,6 +223,7 @@ bool Input::consumeBinaryToggle(const char* binding) { bool Input::consumeBinaryReleaseToggle(const char* binding) { auto b = bindings.find(binding); +#ifndef EDITOR if ( b != bindings.end() ) { if ( (*b).second.type == binding_t::bindtype_t::KEYBOARD && ::inputs.bPlayerUsingKeyboardControl(player) == false ) @@ -211,6 +231,7 @@ bool Input::consumeBinaryReleaseToggle(const char* binding) { return false; } } +#endif if ( b != bindings.end() && (*b).second.binaryRelease && !(*b).second.binaryReleaseConsumed ) { (*b).second.binaryReleaseConsumed = true; return true; @@ -222,6 +243,7 @@ bool Input::consumeBinaryReleaseToggle(const char* binding) { bool Input::binaryHeldToggle(const char* binding) const { auto b = bindings.find(binding); +#ifndef EDITOR if ( b != bindings.end() ) { if ( (*b).second.type == binding_t::bindtype_t::KEYBOARD && ::inputs.bPlayerUsingKeyboardControl(player) == false ) @@ -229,6 +251,7 @@ bool Input::binaryHeldToggle(const char* binding) const { return false; } } +#endif return b != bindings.end() ? ((*b).second.binary && !(*b).second.consumed && (ticks - (*b).second.binaryHeldTicks) > BUTTON_HELD_TICKS) : false; @@ -236,6 +259,7 @@ bool Input::binaryHeldToggle(const char* binding) const { bool Input::analogHeldToggle(const char* binding) const { auto b = bindings.find(binding); +#ifndef EDITOR if ( b != bindings.end() ) { if ( (*b).second.type == binding_t::bindtype_t::KEYBOARD && ::inputs.bPlayerUsingKeyboardControl(player) == false ) @@ -243,6 +267,7 @@ bool Input::analogHeldToggle(const char* binding) const { return false; } } +#endif return b != bindings.end() ? ((*b).second.analog > analogToggleThreshold && !(*b).second.analogConsumed && (ticks - (*b).second.analogHeldTicks) > BUTTON_HELD_TICKS) : false; diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 29bc2b9ac..ca2471d9e 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -22,7 +22,7 @@ #include -bool newui = false; +bool newui = true; int selectedCursorOpacity = 255; int oldSelectedCursorOpacity = 255; int hotbarSlotOpacity = 255; From 3532256b78af7e573a11196c8cb1c810afebf371 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 13 Oct 2021 23:35:56 -0700 Subject: [PATCH 12/39] fix mousewheel scrolling fix text being wayyy too big Signed-off-by: SheridanR --- src/ui/Field.cpp | 8 ++++---- src/ui/Frame.cpp | 34 +++++++++++++++++----------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/ui/Field.cpp b/src/ui/Field.cpp index f2522be8e..785b90865 100644 --- a/src/ui/Field.cpp +++ b/src/ui/Field.cpp @@ -228,10 +228,10 @@ void Field::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector(parent)->getOpacity() < 100.0 ) { diff --git a/src/ui/Frame.cpp b/src/ui/Frame.cpp index dde48e1ab..cbd157cb2 100644 --- a/src/ui/Frame.cpp +++ b/src/ui/Frame.cpp @@ -690,6 +690,23 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: } } + // process frames + { + for (int i = frames.size() - 1; i >= 0; --i) { + Frame* frame = frames[i]; + result_t frameResult = frame->process(_size, actualSize, selectedWidgets, usable); + usable = result.usable = frameResult.usable; + if (!frameResult.removed) { + if (frameResult.tooltip != nullptr) { + result = frameResult; + } + } else { + delete frame; + frames.erase(frames.begin() + i); + } + } + } + // scroll with mouse wheel if (parent != nullptr && !hollow && rectContainsPoint(fullSize, omousex, omousey) && usable && allowScrolling && allowScrollBinds) { // x scroll with mouse wheel @@ -725,23 +742,6 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: this->actualSize.y = std::min(std::max(0, this->actualSize.y), std::max(0, this->actualSize.h - size.h)); } - // process frames - { - for (int i = frames.size() - 1; i >= 0; --i) { - Frame* frame = frames[i]; - result_t frameResult = frame->process(_size, actualSize, selectedWidgets, usable); - usable = result.usable = frameResult.usable; - if (!frameResult.removed) { - if (frameResult.tooltip != nullptr) { - result = frameResult; - } - } else { - delete frame; - frames.erase(frames.begin() + i); - } - } - } - // process (frame view) sliders if (parent != nullptr && !hollow && usable && scrollbars) { // filler in between sliders From 790f00858a3d6fbaf3f87a26fa501ed03a53c634 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Thu, 14 Oct 2021 12:21:56 -0700 Subject: [PATCH 13/39] constify UI draw functions moving mouse changes UI selection Signed-off-by: SheridanR --- src/ui/Button.cpp | 2 +- src/ui/Button.hpp | 2 +- src/ui/Field.cpp | 2 +- src/ui/Field.hpp | 2 +- src/ui/Frame.cpp | 58 +++++++++++++++++++++++++++++++---------------- src/ui/Frame.hpp | 6 ++--- src/ui/GameUI.cpp | 2 +- src/ui/Slider.cpp | 19 +++++++--------- src/ui/Slider.hpp | 2 +- src/ui/Widget.cpp | 11 ++++++++- src/ui/Widget.hpp | 12 ++++++---- 11 files changed, 74 insertions(+), 44 deletions(-) diff --git a/src/ui/Button.cpp b/src/ui/Button.cpp index 977ea5166..fbc328bba 100644 --- a/src/ui/Button.cpp +++ b/src/ui/Button.cpp @@ -59,7 +59,7 @@ static char* tokenize(char* str, const char* const delimiters) { } } -void Button::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets) { +void Button::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets) const { if (invisible) { return; } diff --git a/src/ui/Button.hpp b/src/ui/Button.hpp index ee25f28f1..ba365ef3e 100644 --- a/src/ui/Button.hpp +++ b/src/ui/Button.hpp @@ -61,7 +61,7 @@ class Button : public Widget { //! @param _size size and position of button's parent frame //! @param _actualSize offset into the parent frame space (scroll) //! @param selectedWidgets the currently selected widgets, if any - void draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets); + void draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets) const; //! handles button clicks, etc. //! @param _size size and position of button's parent frame diff --git a/src/ui/Field.cpp b/src/ui/Field.cpp index 785b90865..2c9549bf3 100644 --- a/src/ui/Field.cpp +++ b/src/ui/Field.cpp @@ -100,7 +100,7 @@ char* Field::tokenize(char* str, const char* const delimiters) { } } -void Field::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets) { +void Field::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets) const { if ( invisible || isDisabled() ) { return; } diff --git a/src/ui/Field.hpp b/src/ui/Field.hpp index 7466dc64c..40ecb3c3c 100644 --- a/src/ui/Field.hpp +++ b/src/ui/Field.hpp @@ -53,7 +53,7 @@ class Field : public Widget { //! @param _size size and position of field's parent frame //! @param _actualSize offset into the parent frame space (scroll) //! @param selectedWidgets the currently selected widgets, if any - void draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets); + void draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets) const; //! handles clicks, etc. //! @param _size size and position of field's parent frame diff --git a/src/ui/Frame.cpp b/src/ui/Frame.cpp index cbd157cb2..36beeb76c 100644 --- a/src/ui/Frame.cpp +++ b/src/ui/Frame.cpp @@ -151,7 +151,7 @@ Frame::~Frame() { clear(); } -void Frame::draw() { +void Frame::draw() const { SDL_glBindFramebuffer(GL_FRAMEBUFFER, gui_fbo); SDL_glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gui_fbo_color, 0); SDL_glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, gui_fbo_depth, 0); @@ -161,7 +161,7 @@ void Frame::draw() { //glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); SDL_glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); auto _actualSize = allowScrolling ? actualSize : SDL_Rect{0, 0, size.w, size.h}; - std::vector selectedWidgets; + std::vector selectedWidgets; findSelectedWidgets(selectedWidgets); Frame::draw(size, _actualSize, selectedWidgets); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -191,12 +191,7 @@ void Frame::draw() { glColor4f(1.f, 1.f, 1.f, 1.f); } -void Frame::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets) { - if ( parent && inheritParentFrameOpacity ) - { - setOpacity(static_cast(parent)->getOpacity()); - } - +void Frame::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets) const { if (disabled || invisible) return; @@ -267,11 +262,15 @@ void Frame::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector(parent)->getOpacity()); + } // warning: overloading member variable! SDL_Rect actualSize = allowScrolling ? this->actualSize : SDL_Rect{0, 0, size.w, size.h}; @@ -584,11 +586,15 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: Sint32 mousey = (::mousey / (float)yres) * (float)Frame::virtualScreenY; Sint32 omousex = (::omousex / (float)xres) * (float)Frame::virtualScreenX; Sint32 omousey = (::omousey / (float)yres) * (float)Frame::virtualScreenY; + Sint32 mousexrel = (::mousexrel / (float)xres) * (float)Frame::virtualScreenX; + Sint32 mouseyrel = (::mouseyrel / (float)yres) * (float)Frame::virtualScreenY; #else Sint32 mousex = (inputs.getMouse(owner, Inputs::X) / (float)xres) * (float)Frame::virtualScreenX; Sint32 mousey = (inputs.getMouse(owner, Inputs::Y) / (float)yres) * (float)Frame::virtualScreenY; Sint32 omousex = (inputs.getMouse(owner, Inputs::OX) / (float)xres) * (float)Frame::virtualScreenX; Sint32 omousey = (inputs.getMouse(owner, Inputs::OY) / (float)yres) * (float)Frame::virtualScreenY; + Sint32 mousexrel = (inputs.getMouse(owner, Inputs::XREL) / (float)xres) * (float)Frame::virtualScreenX; + Sint32 mouseyrel = (inputs.getMouse(owner, Inputs::YREL) / (float)yres) * (float)Frame::virtualScreenY; #endif Input& input = Input::inputs[owner]; @@ -714,11 +720,11 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: if (this->actualSize.h <= size.h) { if (mousestatus[SDL_BUTTON_WHEELDOWN]) { mousestatus[SDL_BUTTON_WHEELDOWN] = 0; - this->actualSize.x += std::min(entrySize * 2, size.w); + this->actualSize.x += std::min(entrySize, size.w); usable = result.usable = false; } else if (mousestatus[SDL_BUTTON_WHEELUP]) { mousestatus[SDL_BUTTON_WHEELUP] = 0; - this->actualSize.x -= std::min(entrySize * 2, size.w); + this->actualSize.x -= std::min(entrySize, size.w); usable = result.usable = false; } } @@ -728,11 +734,11 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: if (this->actualSize.h > size.h) { if (mousestatus[SDL_BUTTON_WHEELDOWN]) { mousestatus[SDL_BUTTON_WHEELDOWN] = 0; - this->actualSize.y += std::min(entrySize * 2, size.h); + this->actualSize.y += std::min(entrySize, size.h); usable = result.usable = false; } else if (mousestatus[SDL_BUTTON_WHEELUP]) { mousestatus[SDL_BUTTON_WHEELUP] = 0; - this->actualSize.y -= std::min(entrySize * 2, size.h); + this->actualSize.y -= std::min(entrySize, size.h); usable = result.usable = false; } } @@ -794,7 +800,7 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: ticks = -1; // hack to fix sliders in drop downs } else if (rectContainsPoint(sliderRect, omousex, omousey)) { if (mousestatus[SDL_BUTTON_LEFT]) { - this->actualSize.x += omousex < handleRect.x ? -std::min(entrySize * 2, size.w) : std::min(entrySize * 2, size.w); + this->actualSize.x += omousex < handleRect.x ? -std::min(entrySize, size.w) : std::min(entrySize, size.w); this->actualSize.x = std::min(std::max(0, this->actualSize.x), std::max(0, this->actualSize.w - size.w)); mousestatus[SDL_BUTTON_LEFT] = 0; } @@ -844,7 +850,7 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: ticks = -1; // hack to fix sliders in drop downs } else if (rectContainsPoint(sliderRect, omousex, omousey)) { if (mousestatus[SDL_BUTTON_LEFT]) { - this->actualSize.y += omousey < handleRect.y ? -std::min(entrySize * 2, size.h) : std::min(entrySize * 2, size.h); + this->actualSize.y += omousey < handleRect.y ? -std::min(entrySize, size.h) : std::min(entrySize, size.h); this->actualSize.y = std::min(std::max(0, this->actualSize.y), std::max(0, this->actualSize.h - size.h)); mousestatus[SDL_BUTTON_LEFT] = 0; } @@ -867,6 +873,9 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: if (usable && buttonResult.highlighted) { result.highlightTime = buttonResult.highlightTime; result.tooltip = buttonResult.tooltip; + if (mousexrel || mouseyrel) { + button->select(); + } if (buttonResult.clicked) { button->activate(); } @@ -894,6 +903,9 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: if (usable && sliderResult.highlighted) { result.highlightTime = sliderResult.highlightTime; result.tooltip = sliderResult.tooltip; + if (mousexrel || mouseyrel) { + slider->select(); + } if (sliderResult.clicked) { slider->fireCallback(); } @@ -920,12 +932,15 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: } SDL_Rect entryRect; - entryRect.x = _size.x + border - actualSize.x; entryRect.w = _size.w - border * 2; - entryRect.y = _size.y + border + i * entrySize - actualSize.y; entryRect.h = entrySize; + entryRect.x = _size.x + border - actualSize.x + listOffset.x; entryRect.w = _size.w - border * 2; + entryRect.y = _size.y + border + i * entrySize - actualSize.y + listOffset.y; entryRect.h = entrySize; if (rectContainsPoint(_size, omousex, omousey) && rectContainsPoint(entryRect, omousex, omousey)) { result.highlightTime = entry->highlightTime; result.tooltip = entry->tooltip.c_str(); + if (mousexrel || mouseyrel) { + selection = i; + } if (mousestatus[SDL_BUTTON_LEFT]) { if (!entry->pressed) { mousestatus[SDL_BUTTON_LEFT] = 0; @@ -965,8 +980,13 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: Field::result_t fieldResult = field->process(_size, actualSize, usable); if (usable) { - if (field->isSelected() && fieldResult.highlighted) { - result.usable = usable = false; + if (fieldResult.highlighted) { + if (mousexrel || mouseyrel) { + field->select(); + } + if (field->isSelected()) { + result.usable = usable = false; + } } } @@ -1584,7 +1604,7 @@ void createTestUI() { } } -void Frame::drawImage(image_t* image, const SDL_Rect& _size, const SDL_Rect& scroll) { +void Frame::drawImage(const image_t* image, const SDL_Rect& _size, const SDL_Rect& scroll) const { assert(image); const Image* actualImage = Image::get(image->path.c_str()); if (actualImage) { diff --git a/src/ui/Frame.hpp b/src/ui/Frame.hpp index a52d9f80d..b876dea0b 100644 --- a/src/ui/Frame.hpp +++ b/src/ui/Frame.hpp @@ -120,7 +120,7 @@ class Frame : public Widget { static void fboDestroy(); //! draws the frame and all of its subelements - void draw(); + void draw() const; //! handle clicks and other events //! @return compiled results of frame processing @@ -260,7 +260,7 @@ class Frame : public Widget { //! @param image the image to draw //! @param _size the size of the rectangle to clip against //! @param scroll the amount by which to offset the image in x/y - void drawImage(image_t* image, const SDL_Rect& _size, const SDL_Rect& scroll); + void drawImage(const image_t* image, const SDL_Rect& _size, const SDL_Rect& scroll) const; //! scroll to the current list entry selection in the frame //! @param scroll_to_top if true, scroll the selection to the very top of the frame @@ -351,7 +351,7 @@ class Frame : public Widget { //! @param _size real position of the frame onscreen //! @param _actualSize offset into the frame space (scroll) //! @param selectedWidgets the currently selected widgets, if any - void draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets); + void draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets) const; //! handle clicks and other events //! @param _size real position of the frame onscreen diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index ca2471d9e..d9dfe2c3e 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -3419,7 +3419,7 @@ void createPlayerInventory(const int player) charSize.w -= 2 * (inventorySlotSize + baseSlotOffsetX + 4); charFrame->setSize(charSize); - charFrame->setDrawCallback([](Widget& widget, SDL_Rect pos) { + charFrame->setDrawCallback([](const Widget& widget, SDL_Rect pos) { drawCharacterPreview(widget.getOwner(), pos); }); //charFrame->addImage(SDL_Rect{ 0, 0, charSize.w, charSize.h }, diff --git a/src/ui/Slider.cpp b/src/ui/Slider.cpp index 47df40839..b5451e5be 100644 --- a/src/ui/Slider.cpp +++ b/src/ui/Slider.cpp @@ -13,7 +13,7 @@ Slider::Slider(Frame& _parent) { _parent.adoptWidget(*this); } -void Slider::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets) { +void Slider::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets) const { if (invisible) { return; } @@ -23,14 +23,6 @@ void Slider::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets); + void draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets) const; //! handles slider clicks, etc. //! @param _size size and position of slider's parent frame diff --git a/src/ui/Widget.cpp b/src/ui/Widget.cpp index 3719bdce4..53cfcbf5f 100644 --- a/src/ui/Widget.cpp +++ b/src/ui/Widget.cpp @@ -143,6 +143,15 @@ void Widget::findSelectedWidgets(std::vector& outResult) { } } +void Widget::findSelectedWidgets(std::vector& outResult) const { + if (selected) { + outResult.push_back(this); + } + for (auto widget : widgets) { + widget->findSelectedWidgets(outResult); + } +} + Widget* Widget::findSelectedWidget(int owner) { if (selected && owner == this->owner) { return this; @@ -185,7 +194,7 @@ void Widget::adoptWidget(Widget& widget) { widgets.push_back(&widget); } -void Widget::drawGlyphs(const SDL_Rect size, const std::vector& selectedWidgets) { +void Widget::drawGlyphs(const SDL_Rect size, const std::vector& selectedWidgets) const { if (drawCallback) { drawCallback(*this, size); } diff --git a/src/ui/Widget.hpp b/src/ui/Widget.hpp index a3d7b3547..89cc0d45e 100644 --- a/src/ui/Widget.hpp +++ b/src/ui/Widget.hpp @@ -36,7 +36,7 @@ class Widget { Uint32 getHighlightTime() const { return highlightTime; } Sint32 getOwner() const { return owner; } void (*getTickCallback() const)(Widget&) { return tickCallback; } - void (*getDrawCallback() const)(Widget&, const SDL_Rect) { return drawCallback; } + void (*getDrawCallback() const)(const Widget&, const SDL_Rect) { return drawCallback; } const char* getWidgetSearchParent() const { return widgetSearchParent.c_str(); } auto& getWidgetActions() const { return widgetActions; } auto& getWidgetMovements() const { return widgetMovements; } @@ -50,7 +50,7 @@ class Widget { void setHideGlyphs(bool _hideGlyphs) { hideGlyphs = _hideGlyphs; } void setOwner(Sint32 _owner) { owner = _owner; } void setTickCallback(void (*const fn)(Widget&)) { tickCallback = fn; } - void setDrawCallback(void (*const fn)(Widget&, const SDL_Rect)) { drawCallback = fn; } + void setDrawCallback(void (*const fn)(const Widget&, const SDL_Rect)) { drawCallback = fn; } void setWidgetTab(const char* s) { widgetMovements.emplace("MenuTab", s); } void setWidgetRight(const char* s) { widgetMovements.emplace("MenuRight", s); widgetMovements.emplace("AltMenuRight", s); } void setWidgetDown(const char* s) { widgetMovements.emplace("MenuDown", s); widgetMovements.emplace("AltMenuDown", s); } @@ -105,6 +105,10 @@ class Widget { //! @param outResult a list containing all the selected widgets void findSelectedWidgets(std::vector& outResult); + //! build a list of all the selected widgets amongst our children (const only) + //! @param outResult a list containing all the selected widgets + void findSelectedWidgets(std::vector& outResult) const; + //! find the widget selected by the specified owner/player //! @param owner the player who owns the widget //! @return the selected widget or nullptr if it could not be found @@ -125,7 +129,7 @@ class Widget { Uint32 highlightTime = 0u; //!< records the time since the widget was highlighted Sint32 owner = 0; //!< which player owns this widget (0 = player 1, 1 = player 2, etc) void (*tickCallback)(Widget&) = nullptr; //!< the callback to run each frame for this widget - void (*drawCallback)(Widget&, const SDL_Rect) = nullptr; //!< the callback to run after the widget is drawn + void (*drawCallback)(const Widget&, const SDL_Rect) = nullptr; //!< the callback to run after the widget is drawn std::unordered_map widgetActions; //!< widgets to select and activate when input is pressed @@ -135,5 +139,5 @@ class Widget { Frame* findSearchRoot(); - void drawGlyphs(const SDL_Rect size, const std::vector& selectedWidgets); + void drawGlyphs(const SDL_Rect size, const std::vector& selectedWidgets) const; }; \ No newline at end of file From ac4791004bd79f02b6d35cc70e472bd0200f92b2 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Thu, 14 Oct 2021 12:38:36 -0700 Subject: [PATCH 14/39] fix range on sliders Signed-off-by: SheridanR --- src/ui/MainMenu.cpp | 5 +++++ src/ui/Slider.cpp | 12 ++++++------ src/ui/Slider.hpp | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index c756db62a..f02b5e0bd 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -831,6 +831,7 @@ namespace MainMenu { auto slider = window->addSlider((std::string("sort_slider") + std::to_string(c)).c_str()); slider->setMaxValue(6.f); slider->setMinValue(-6.f); + slider->setBorder(24); slider->setRailSize(SDL_Rect{158, 100 + 50 *c, 724, 24}); slider->setHandleSize(SDL_Rect{0, 0, 52, 54}); slider->setRailImage("images/ui/Main Menus/Settings/AutoSort/transparent.png"); @@ -1310,8 +1311,10 @@ namespace MainMenu { }); } auto slider = frame.addSlider((fullname + "_slider").c_str()); + slider->setOrientation(Slider::orientation_t::SLIDER_HORIZONTAL); slider->setMinValue(minValue); slider->setMaxValue(maxValue); + slider->setBorder(16); slider->setValue(value); slider->setRailSize(SDL_Rect{field->getSize().x + field->getSize().w + 32, y + 14, 450, 24}); slider->setHandleSize(SDL_Rect{0, 0, 52, 42}); @@ -1393,6 +1396,7 @@ namespace MainMenu { ); rock_background->tiled = true; auto slider = settings_subwindow->addSlider("scroll_slider"); + slider->setBorder(24); slider->setOrientation(Slider::SLIDER_VERTICAL); slider->setRailSize(SDL_Rect{1038, 16, 30, 440}); slider->setRailImage("images/ui/Main Menus/Settings/Settings_Slider_Backing00.png"); @@ -3405,6 +3409,7 @@ namespace MainMenu { slider->setRailImage("images/ui/Main Menus/Play/PlayerCreation/ClassSelection/ClassSelect_ScrollBar_00.png"); slider->setHandleImage("images/ui/Main Menus/Play/PlayerCreation/ClassSelection/ClassSelect_ScrollBar_SliderB_00.png"); slider->setOrientation(Slider::orientation_t::SLIDER_VERTICAL); + slider->setBorder(24); slider->setMinValue(0.f); slider->setMaxValue(subframe->getActualSize().h - 258); slider->setCallback([](Slider& slider){ diff --git a/src/ui/Slider.cpp b/src/ui/Slider.cpp index b5451e5be..434a1531e 100644 --- a/src/ui/Slider.cpp +++ b/src/ui/Slider.cpp @@ -128,11 +128,11 @@ Slider::result_t Slider::process(SDL_Rect _size, SDL_Rect _actualSize, const boo SDL_Rect _handleSize, _railSize; if (orientation == SLIDER_HORIZONTAL) { - handleSize.x = railSize.x - handleSize.w / 2 + ((float)(value - minValue) / (maxValue - minValue)) * railSize.w; + handleSize.x = (railSize.x + border) - handleSize.w / 2 + ((float)(value - minValue) / (maxValue - minValue)) * (railSize.w - border * 2); handleSize.y = railSize.y + railSize.h / 2 - handleSize.h / 2; } else if (orientation == SLIDER_VERTICAL) { handleSize.x = railSize.x + railSize.w / 2 - handleSize.w / 2; - handleSize.y = railSize.y - handleSize.h / 2 + ((float)(value - minValue) / (maxValue - minValue)) * railSize.h; + handleSize.y = (railSize.y + border) - handleSize.h / 2 + ((float)(value - minValue) / (maxValue - minValue)) * (railSize.h - border * 2); } _railSize.x = _size.x + std::max(0, railSize.x - _actualSize.x); @@ -145,8 +145,8 @@ Slider::result_t Slider::process(SDL_Rect _size, SDL_Rect _actualSize, const boo _handleSize.w = std::min(handleSize.w, _size.w - handleSize.x + _actualSize.x) + std::min(0, handleSize.x - _actualSize.x); _handleSize.h = std::min(handleSize.h, _size.h - handleSize.y + _actualSize.y) + std::min(0, handleSize.y - _actualSize.y); - int offX = _size.x + railSize.x - _actualSize.x; - int offY = _size.y + railSize.y - _actualSize.y; + int offX = _size.x + (railSize.x + border) - _actualSize.x; + int offY = _size.y + (railSize.y + border) - _actualSize.y; if (orientation == SLIDER_HORIZONTAL) { _size.x = std::max(_size.x, _railSize.x - _handleSize.w / 2); _size.y = std::max(_size.y, _railSize.y + _railSize.h / 2 - _handleSize.h / 2); @@ -192,10 +192,10 @@ Slider::result_t Slider::process(SDL_Rect _size, SDL_Rect _actualSize, const boo pressed = true; float oldValue = value; if (orientation == SLIDER_HORIZONTAL) { - value = ((float)(mousex - offX) / railSize.w) * (float)(maxValue - minValue) + minValue; + value = ((float)(mousex - offX) / (railSize.w - border * 2)) * (float)(maxValue - minValue) + minValue; } else if (orientation == SLIDER_VERTICAL) { - value = ((float)(mousey - offY) / railSize.h) * (float)(maxValue - minValue) + minValue; + value = ((float)(mousey - offY) / (railSize.h - border * 2)) * (float)(maxValue - minValue) + minValue; } value = std::min(std::max(minValue, value), maxValue); if (oldValue != value) { diff --git a/src/ui/Slider.hpp b/src/ui/Slider.hpp index f229431f8..d402a1297 100644 --- a/src/ui/Slider.hpp +++ b/src/ui/Slider.hpp @@ -101,7 +101,7 @@ class Slider : public Widget { float maxValue = 0.f; //!< maximum value float minValue = 0.f; //!< minimum value float valueSpeed = 1.f; //!< how fast the slider moves when we press a button - int border = 2; //!< border size in pixels + int border = 0; //!< border size in pixels bool activated = false; //!< if true, the slider captures all input SDL_Rect handleSize; //!< size of the handle in pixels SDL_Rect railSize; //!< size of the rail in pixels From 4b4e42b2202f59a030348658856037a61425e47a Mon Sep 17 00:00:00 2001 From: SheridanR Date: Fri, 15 Oct 2021 12:28:39 -0700 Subject: [PATCH 15/39] quit game confirmation Signed-off-by: SheridanR --- src/ui/MainMenu.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index f02b5e0bd..20662f4a2 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -4600,9 +4600,91 @@ namespace MainMenu { confirm_and_exit->addWidgetAction("MenuStart", "confirm_and_exit"); } + static int quit_motd = -1; + void mainQuit(Button& button) { + if (main_menu_frame->findFrame("quit_confirm")) { + return; + } + soundActivate(); - // TODO + + auto frame = main_menu_frame->addFrame("quit_confirm"); + frame->setSize(SDL_Rect{(Frame::virtualScreenX - 364) / 2, (Frame::virtualScreenY - 176) / 2, 364, 176}); + frame->setActualSize(SDL_Rect{0, 0, 364, 176}); + frame->setColor(0); + frame->setBorder(0); + frame->addImage( + frame->getActualSize(), + 0xffffffff, + "images/ui/Main Menus/Disconnect/UI_Disconnect_Window00.png", + "background" + ); + + static const char* quit_messages[][3] { + {"You want to leave, eh?\nThen get out and don't come back!", "Fine geez", "Never!"}, + {"Just cancel your plans.\nI'll wait.", "Good luck", "Sure"}, + {"You couldn't kill the lich anyway.", "You're right", "Oh yeah?"}, + {"The gnomes are laughing at you!\nAre you really gonna take that?", "Yeah :(", "No way!"}, + {"Don't go now! There's a\nboulder trap around the corner!", "Kill me", "Oh thanks"}, + {"I'll tell your parents\nyou said a bad word.", "Poop", "Please no"}, + {"Please don't leave!\nThere's more treasure to loot!", "Don't care", "More loot!"}, + {"Just be glad I can't summon\nthe minotaur in real life.", "Too bad", "Point taken"}, + {"I'd leave too.\nThis game looks just like Minecraft.", "lol", "Ouch"} + }; + constexpr int num_quit_messages = sizeof(quit_messages) / (sizeof(const char*) * 3); + + if (quit_motd >= num_quit_messages) { + quit_motd = 0; + } + if (quit_motd < 0) { + quit_motd = rand() % num_quit_messages; + } + + auto text = frame->addField("text", 128); + text->setSize(SDL_Rect{30, 28, 304, 46}); + text->setFont(smallfont_no_outline); + text->setText(quit_messages[quit_motd][0]); + text->setJustify(Field::justify_t::CENTER); + + auto okay = frame->addButton("okay"); + okay->setSize(SDL_Rect{58, 78, 130, 52}); + okay->setBackground("images/ui/Main Menus/Disconnect/UI_Disconnect_Button_Abandon00.png"); + okay->setColor(makeColor(127, 127, 127, 255)); + okay->setHighlightColor(makeColor(255, 255, 255, 255)); + okay->setTextColor(makeColor(127, 127, 127, 255)); + okay->setTextHighlightColor(makeColor(255, 255, 255, 255)); + okay->setFont(smallfont_outline); + okay->setText(quit_messages[quit_motd][1]); + okay->setWidgetRight("cancel"); + okay->setWidgetBack("cancel"); + okay->select(); + okay->setCallback([](Button&){mainloop = 0;}); + + auto cancel = frame->addButton("cancel"); + cancel->setSize(SDL_Rect{196, 78, 108, 52}); + cancel->setBackground("images/ui/Main Menus/Disconnect/UI_Disconnect_Button_GoBack00.png"); + cancel->setColor(makeColor(127, 127, 127, 255)); + cancel->setHighlightColor(makeColor(255, 255, 255, 255)); + cancel->setTextColor(makeColor(127, 127, 127, 255)); + cancel->setTextHighlightColor(makeColor(255, 255, 255, 255)); + cancel->setFont(smallfont_outline); + cancel->setText(quit_messages[quit_motd][2]); + cancel->setWidgetLeft("okay"); + cancel->setWidgetBack("cancel"); + cancel->setCallback([](Button&){ + soundCancel(); + assert(main_menu_frame); + auto buttons = main_menu_frame->findFrame("buttons"); assert(buttons); + auto quit_button = buttons->findButton("QUIT"); assert(quit_button); + quit_button->select(); + auto quit_confirm = main_menu_frame->findFrame("quit_confirm"); + if (quit_confirm) { + quit_confirm->removeSelf(); + } + }); + + ++quit_motd; } /******************************************************************************/ From f728dc0aa2633a986bcf8aeecc88b10549985d91 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Fri, 15 Oct 2021 20:26:50 -0700 Subject: [PATCH 16/39] fix visual bugs with sliders Signed-off-by: SheridanR --- src/ui/MainMenu.cpp | 11 +++++++---- src/ui/Slider.cpp | 26 ++++++++++++++++++-------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 20662f4a2..339e60448 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -831,7 +831,6 @@ namespace MainMenu { auto slider = window->addSlider((std::string("sort_slider") + std::to_string(c)).c_str()); slider->setMaxValue(6.f); slider->setMinValue(-6.f); - slider->setBorder(24); slider->setRailSize(SDL_Rect{158, 100 + 50 *c, 724, 24}); slider->setHandleSize(SDL_Rect{0, 0, 52, 54}); slider->setRailImage("images/ui/Main Menus/Settings/AutoSort/transparent.png"); @@ -892,9 +891,13 @@ namespace MainMenu { auto number = std::string(slider->getName()).substr(sizeof("sort_slider") - 1); int c = atoi(number.c_str()); auto icon = window->findImage((std::string("sort_slider_img") + std::to_string(c)).c_str()); assert(icon); - icon->disabled = false; - icon->pos.x = slider->getHandleSize().x; - icon->pos.y = slider->getHandleSize().y; + int x = slider->getHandleSize().x; + int y = slider->getHandleSize().y; + if (x || y) { + icon->disabled = false; + icon->pos.x = x; + icon->pos.y = y; + } if (slider->isSelected()) { slider->setHandleImage("images/ui/Main Menus/Settings/AutoSort/AutoSort_SliderBox_BackBrown00.png"); } else { diff --git a/src/ui/Slider.cpp b/src/ui/Slider.cpp index 434a1531e..bbaef61d5 100644 --- a/src/ui/Slider.cpp +++ b/src/ui/Slider.cpp @@ -66,6 +66,16 @@ void Slider::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vectorhandleSize; + if (handleSize.x == 0 && handleSize.y == 0) { + if (orientation == SLIDER_HORIZONTAL) { + handleSize.x = (railSize.x + border) - handleSize.w / 2 + ((float)(value - minValue) / (maxValue - minValue)) * (railSize.w - border * 2); + handleSize.y = railSize.y + railSize.h / 2 - handleSize.h / 2; + } else if (orientation == SLIDER_VERTICAL) { + handleSize.x = railSize.x + railSize.w / 2 - handleSize.w / 2; + handleSize.y = (railSize.y + border) - handleSize.h / 2 + ((float)(value - minValue) / (maxValue - minValue)) * (railSize.h - border * 2); + } + } _handleSize.x = _size.x + std::max(0, handleSize.x - _actualSize.x); _handleSize.y = _size.y + std::max(0, handleSize.y - _actualSize.y); _handleSize.w = std::min(handleSize.w, _size.w - handleSize.x + _actualSize.x) + std::min(0, handleSize.x - _actualSize.x); @@ -107,6 +117,14 @@ void Slider::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector Date: Fri, 15 Oct 2021 22:32:55 -0700 Subject: [PATCH 17/39] WIP generic settings window --- src/ui/MainMenu.cpp | 124 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 121 insertions(+), 3 deletions(-) diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 339e60448..3ff40dbf1 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -584,7 +584,7 @@ namespace MainMenu { allSettings.cheats_enabled = false; } - void settingsCustomizeInventorySorting(Button&); + static void settingsCustomizeInventorySorting(Button&); static void inventorySortingDefaults(Button& button) { soundActivate(); @@ -628,7 +628,7 @@ namespace MainMenu { allSettings.inventory_sorting.save(); } - void settingsCustomizeInventorySorting(Button& button) { + static void settingsCustomizeInventorySorting(Button& button) { soundActivate(); auto window = main_menu_frame->addFrame("inventory_sorting_window"); @@ -1067,6 +1067,124 @@ namespace MainMenu { }); } + static Frame* settingsGenericWindow(const char* name, const char* title) { + auto window = main_menu_frame->addFrame(name); + window->setSize(SDL_Rect{ + (Frame::virtualScreenX - 826) / 2, + (Frame::virtualScreenY - 718) / 2, + 826, + 718}); + window->setActualSize(SDL_Rect{0, 0, 826, 718}); + window->setBorder(0); + window->setColor(0); + + auto help_text = window->addField("help_text", 256); + help_text->setSize(SDL_Rect{30, 566, 766, 54}); + help_text->setFont(smallfont_no_outline); + help_text->setJustify(Field::justify_t::CENTER); + help_text->setText("Help text goes here"); + + auto background = window->addImage( + window->getActualSize(), + 0xffffffff, + "images/ui/Main Menus/Settings/GenericWindow/UI_MM14_Window00.png", + "background" + ); + + auto timber = window->addImage( + window->getActualSize(), + 0xffffffff, + "images/ui/Main Menus/Settings/GenericWindow/UI_MM14_Window01.png", + "timber" + ); + timber->ontop = true; + + auto banner = window->addField("title", 64); + banner->setSize(SDL_Rect{246, 22, 338, 24}); + banner->setFont(banner_font); + banner->setText(title); + banner->setJustify(Field::justify_t::CENTER); + + auto subwindow = window->addFrame("subwindow"); + subwindow->setSize(SDL_Rect{30, 64, 766, 502}); + subwindow->setActualSize(SDL_Rect{0, 0, 766, 502}); + subwindow->setBorder(0); + subwindow->setColor(0); + + auto rocks = subwindow->addImage( + subwindow->getActualSize(), + makeColor(127, 127, 127, 251), + "images/ui/Main Menus/Settings/GenericWindow/UI_MM14_Rocks00.png", + "rocks" + ); + rocks->tiled = true; + + auto defaults = window->addButton("defaults"); + defaults->setBackground("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonStandard00.png"); + defaults->setColor(makeColor(127, 127, 127, 255)); + defaults->setHighlightColor(makeColor(255, 255, 255, 255)); + defaults->setTextColor(makeColor(127, 127, 127, 255)); + defaults->setTextHighlightColor(makeColor(255, 255, 255, 255)); + defaults->setSize(SDL_Rect{156, 630, 164, 62}); + defaults->setText("Restore\nDefaults"); + defaults->setFont(smallfont_outline); + defaults->setWidgetBack("discard"); + defaults->addWidgetAction("MenuStart", "confirm"); + defaults->addWidgetAction("MenuAlt2", "defaults"); + defaults->setWidgetRight("discard"); + + auto discard = window->addButton("discard"); + discard->setBackground("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonStandard00.png"); + discard->setColor(makeColor(127, 127, 127, 255)); + discard->setHighlightColor(makeColor(255, 255, 255, 255)); + discard->setTextColor(makeColor(127, 127, 127, 255)); + discard->setTextHighlightColor(makeColor(255, 255, 255, 255)); + discard->setText("Discard\n& Exit"); + discard->setFont(smallfont_outline); + discard->setSize(SDL_Rect{ + (window->getActualSize().w - 164) / 2, + 630, + 164, + 62} + ); + discard->setCallback([](Button& button){ + soundCancel(); + auto parent = static_cast(button.getParent()); + parent->removeSelf(); + }); + discard->setWidgetBack("discard"); + discard->addWidgetAction("MenuStart", "confirm"); + discard->addWidgetAction("MenuAlt2", "defaults"); + discard->setWidgetLeft("defaults"); + discard->setWidgetRight("confirm"); + + auto confirm = window->addButton("confirm"); + confirm->setBackground("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonStandard00.png"); + confirm->setColor(makeColor(127, 127, 127, 255)); + confirm->setHighlightColor(makeColor(255, 255, 255, 255)); + confirm->setTextColor(makeColor(127, 127, 127, 255)); + confirm->setTextHighlightColor(makeColor(255, 255, 255, 255)); + confirm->setText("Confirm\n& Exit"); + confirm->setFont(smallfont_outline); + confirm->setSize(SDL_Rect{504, 630, 164, 62}); + confirm->setCallback([](Button& button){ + soundActivate(); + auto parent = static_cast(button.getParent()); + parent->removeSelf(); + }); + confirm->setWidgetBack("discard"); + confirm->addWidgetAction("MenuStart", "confirm"); + confirm->addWidgetAction("MenuAlt2", "defaults"); + confirm->setWidgetLeft("discard"); + confirm->select(); + + return window; + } + + static void settingsBindings(Button& button) { + auto bindings = settingsGenericWindow("bindings", "BINDINGS"); + } + static int settingsAddSubHeader(Frame& frame, int y, const char* name, const char* text) { std::string fullname = std::string("subheader_") + name; auto image = frame.addImage( @@ -1778,7 +1896,7 @@ namespace MainMenu { y += settingsAddSubHeader(*settings_subwindow, y, "general", "General Settings"); y += settingsAddCustomize(*settings_subwindow, y, "bindings", "Bindings", "Modify controls for mouse, keyboard, gamepads, and other peripherals.", - nullptr); + settingsBindings); y += settingsAddSubHeader(*settings_subwindow, y, "mouse_and_keyboard", "Mouse & Keyboard"); y += settingsAddBooleanOption(*settings_subwindow, y, "numkeys_in_inventory", "Number Keys in Inventory", From d2d8a81ebdfb3396986ba460997c89ea624518c5 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Tue, 19 Oct 2021 18:30:46 -0700 Subject: [PATCH 18/39] lots of binding menu stuff add dimmers to all existing windows Signed-off-by: SheridanR --- src/game.cpp | 1 + src/input.cpp | 29 +++ src/input.hpp | 3 + src/ui/MainMenu.cpp | 593 +++++++++++++++++++++++++++++++------------- 4 files changed, 451 insertions(+), 175 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 120b1c89e..8cfdad66e 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2850,6 +2850,7 @@ void handleEvents(void) inputs.updateAllMouse(); } + Input::lastInputOfAnyKind = ""; for (auto& input : Input::inputs) { input.updateReleasedBindings(); input.update(); diff --git a/src/input.cpp b/src/input.cpp index 44c2594a8..6c10f1d29 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -115,6 +115,34 @@ void Input::defaultBindings() { #endif } +void Input::clearDefaultBindings() { + // This is used to disable UI bindings while the player is rebinding keys + // in the game settings. Maybe it will be used for other things though... + for (int c = 0; c < MAXPLAYERS; ++c) { + inputs[c].bind("MenuTab", ""); + inputs[c].bind("MenuUp", ""); + inputs[c].bind("MenuLeft", ""); + inputs[c].bind("MenuRight", ""); + inputs[c].bind("MenuDown", ""); + inputs[c].bind("MenuConfirm", ""); + inputs[c].bind("MenuCancel", ""); + inputs[c].bind("MenuAlt1", ""); + inputs[c].bind("MenuAlt2", ""); + inputs[c].bind("MenuStart", ""); + inputs[c].bind("MenuSelect", ""); + inputs[c].bind("MenuPageLeft", ""); + inputs[c].bind("MenuPageRight", ""); + inputs[c].bind("AltMenuUp", ""); + inputs[c].bind("AltMenuLeft", ""); + inputs[c].bind("AltMenuRight", ""); + inputs[c].bind("AltMenuDown", ""); + inputs[c].bind("MenuScrollUp", ""); + inputs[c].bind("MenuScrollLeft", ""); + inputs[c].bind("MenuScrollRight", ""); + inputs[c].bind("MenuScrollDown", ""); + } +} + float Input::analog(const char* binding) const { auto b = bindings.find(binding); return b != bindings.end() ? (*b).second.analog : 0.f; @@ -382,6 +410,7 @@ void Input::bind(const char* binding, const char* input) { auto result = bindings.emplace(binding, binding_t()); b = result.first; } + (*b).second = binding_t(); (*b).second.input.assign(input); if (input == nullptr) { (*b).second.type = binding_t::INVALID; diff --git a/src/input.hpp b/src/input.hpp index 061ebb18f..f36d01970 100644 --- a/src/input.hpp +++ b/src/input.hpp @@ -35,6 +35,9 @@ class Input { //! set default bindings for all players static void defaultBindings(); + //! clear default bindings + static void clearDefaultBindings(); + //! input mapping struct binding_t { std::string input = ""; diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index 3ff40dbf1..ac1c72a61 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -169,6 +169,48 @@ namespace MainMenu { } } + static void updateSettingSelection(Frame& frame) { + auto& images = frame.getImages(); + for (auto image : images) { + if (image->path == "images/ui/Main Menus/Settings/Settings_Left_BackingSelect00.png") { + image->path = "images/ui/Main Menus/Settings/Settings_Left_Backing00.png"; + } + } + static Widget* current_selected_widget = nullptr; + auto selectedWidget = frame.findSelectedWidget(0); + if (selectedWidget && current_selected_widget != selectedWidget) { + current_selected_widget = selectedWidget; + std::string setting; + auto name = std::string(selectedWidget->getName()); + if (selectedWidget->getType() == Widget::WIDGET_SLIDER) { + setting = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_slider") - 1) - (sizeof("setting_") - 1)); + } else if (selectedWidget->getType() == Widget::WIDGET_BUTTON) { + auto button = static_cast(selectedWidget); + auto customize = "images/ui/Main Menus/Settings/Settings_Button_Customize00.png"; + auto binding = "images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonChoosing00.png"; + if (strcmp(button->getBackground(), customize) == 0) { + setting = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_customize_button") - 1) - (sizeof("setting_") - 1)); + } else if (strcmp(button->getBackground(), binding) == 0) { + setting = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_binding_button") - 1) - (sizeof("setting_") - 1)); + } else { + setting = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_button") - 1) - (sizeof("setting_") - 1)); + } + } + if (!setting.empty()) { + auto image = frame.findImage((std::string("setting_") + setting + std::string("_image")).c_str()); + if (image) { + image->path = "images/ui/Main Menus/Settings/Settings_Left_BackingSelect00.png"; + } + auto field = frame.findField((std::string("setting_") + setting + std::string("_field")).c_str()); + if (field) { + auto settings = static_cast(frame.getParent()); + auto tooltip = settings->findField("tooltip"); assert(tooltip); + tooltip->setText(field->getGuide()); + } + } + } + } + static Button* createBackWidget(Frame* parent, void (*callback)(Button&)) { auto back = parent->addFrame("back"); back->setSize(SDL_Rect{5, 5, 66, 36}); @@ -430,6 +472,7 @@ namespace MainMenu { Customize = 2, BooleanWithCustomize = 3, Dropdown = 4, + Binding = 5, }; Type type; const char* name; @@ -1067,124 +1110,6 @@ namespace MainMenu { }); } - static Frame* settingsGenericWindow(const char* name, const char* title) { - auto window = main_menu_frame->addFrame(name); - window->setSize(SDL_Rect{ - (Frame::virtualScreenX - 826) / 2, - (Frame::virtualScreenY - 718) / 2, - 826, - 718}); - window->setActualSize(SDL_Rect{0, 0, 826, 718}); - window->setBorder(0); - window->setColor(0); - - auto help_text = window->addField("help_text", 256); - help_text->setSize(SDL_Rect{30, 566, 766, 54}); - help_text->setFont(smallfont_no_outline); - help_text->setJustify(Field::justify_t::CENTER); - help_text->setText("Help text goes here"); - - auto background = window->addImage( - window->getActualSize(), - 0xffffffff, - "images/ui/Main Menus/Settings/GenericWindow/UI_MM14_Window00.png", - "background" - ); - - auto timber = window->addImage( - window->getActualSize(), - 0xffffffff, - "images/ui/Main Menus/Settings/GenericWindow/UI_MM14_Window01.png", - "timber" - ); - timber->ontop = true; - - auto banner = window->addField("title", 64); - banner->setSize(SDL_Rect{246, 22, 338, 24}); - banner->setFont(banner_font); - banner->setText(title); - banner->setJustify(Field::justify_t::CENTER); - - auto subwindow = window->addFrame("subwindow"); - subwindow->setSize(SDL_Rect{30, 64, 766, 502}); - subwindow->setActualSize(SDL_Rect{0, 0, 766, 502}); - subwindow->setBorder(0); - subwindow->setColor(0); - - auto rocks = subwindow->addImage( - subwindow->getActualSize(), - makeColor(127, 127, 127, 251), - "images/ui/Main Menus/Settings/GenericWindow/UI_MM14_Rocks00.png", - "rocks" - ); - rocks->tiled = true; - - auto defaults = window->addButton("defaults"); - defaults->setBackground("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonStandard00.png"); - defaults->setColor(makeColor(127, 127, 127, 255)); - defaults->setHighlightColor(makeColor(255, 255, 255, 255)); - defaults->setTextColor(makeColor(127, 127, 127, 255)); - defaults->setTextHighlightColor(makeColor(255, 255, 255, 255)); - defaults->setSize(SDL_Rect{156, 630, 164, 62}); - defaults->setText("Restore\nDefaults"); - defaults->setFont(smallfont_outline); - defaults->setWidgetBack("discard"); - defaults->addWidgetAction("MenuStart", "confirm"); - defaults->addWidgetAction("MenuAlt2", "defaults"); - defaults->setWidgetRight("discard"); - - auto discard = window->addButton("discard"); - discard->setBackground("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonStandard00.png"); - discard->setColor(makeColor(127, 127, 127, 255)); - discard->setHighlightColor(makeColor(255, 255, 255, 255)); - discard->setTextColor(makeColor(127, 127, 127, 255)); - discard->setTextHighlightColor(makeColor(255, 255, 255, 255)); - discard->setText("Discard\n& Exit"); - discard->setFont(smallfont_outline); - discard->setSize(SDL_Rect{ - (window->getActualSize().w - 164) / 2, - 630, - 164, - 62} - ); - discard->setCallback([](Button& button){ - soundCancel(); - auto parent = static_cast(button.getParent()); - parent->removeSelf(); - }); - discard->setWidgetBack("discard"); - discard->addWidgetAction("MenuStart", "confirm"); - discard->addWidgetAction("MenuAlt2", "defaults"); - discard->setWidgetLeft("defaults"); - discard->setWidgetRight("confirm"); - - auto confirm = window->addButton("confirm"); - confirm->setBackground("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonStandard00.png"); - confirm->setColor(makeColor(127, 127, 127, 255)); - confirm->setHighlightColor(makeColor(255, 255, 255, 255)); - confirm->setTextColor(makeColor(127, 127, 127, 255)); - confirm->setTextHighlightColor(makeColor(255, 255, 255, 255)); - confirm->setText("Confirm\n& Exit"); - confirm->setFont(smallfont_outline); - confirm->setSize(SDL_Rect{504, 630, 164, 62}); - confirm->setCallback([](Button& button){ - soundActivate(); - auto parent = static_cast(button.getParent()); - parent->removeSelf(); - }); - confirm->setWidgetBack("discard"); - confirm->addWidgetAction("MenuStart", "confirm"); - confirm->addWidgetAction("MenuAlt2", "defaults"); - confirm->setWidgetLeft("discard"); - confirm->select(); - - return window; - } - - static void settingsBindings(Button& button) { - auto bindings = settingsGenericWindow("bindings", "BINDINGS"); - } - static int settingsAddSubHeader(Frame& frame, int y, const char* name, const char* text) { std::string fullname = std::string("subheader_") + name; auto image = frame.addImage( @@ -1234,6 +1159,37 @@ namespace MainMenu { return size.h + 10; } + static int settingsAddBinding( + Frame& frame, + int y, + const char* binding, + const char* tip, + void (*callback)(Button&)) + { + std::string fullname = std::string("setting_") + binding; + int result = settingsAddOption(frame, y, binding, binding, tip); + auto button = frame.addButton((fullname + "_binding_button").c_str()); + button->setSize(SDL_Rect{ + 390, + y + 4, + 98, + 44}); + button->setFont(smallfont_outline); + button->setText(Input::inputs[0].binding(binding)); + button->setJustify(Button::justify_t::CENTER); + button->setCallback(callback); + button->setBackground("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonChoosing00.png"); + button->setHighlightColor(makeColor(255,255,255,255)); + button->setColor(makeColor(127,127,127,255)); + button->setTextHighlightColor(makeColor(255,255,255,255)); + button->setTextColor(makeColor(127,127,127,255)); + button->setWidgetSearchParent(frame.getParent()->getName()); + button->setWidgetBack("discard_and_exit"); + button->addWidgetAction("MenuAlt1", "restore_defaults"); + button->addWidgetAction("MenuStart", "confirm_and_exit"); + return result; + } + static int settingsAddBooleanOption( Frame& frame, int y, @@ -1263,6 +1219,7 @@ namespace MainMenu { button->setColor(makeColor(127,127,127,255)); button->setTextHighlightColor(makeColor(255,255,255,255)); button->setTextColor(makeColor(127,127,127,255)); + button->setWidgetSearchParent(frame.getParent()->getName()); button->setWidgetBack("discard_and_exit"); button->setWidgetPageLeft("tab_left"); button->setWidgetPageRight("tab_right"); @@ -1298,6 +1255,7 @@ namespace MainMenu { button->setColor(makeColor(127,127,127,255)); button->setTextHighlightColor(makeColor(255,255,255,255)); button->setTextColor(makeColor(127,127,127,255)); + button->setWidgetSearchParent(frame.getParent()->getName()); button->setWidgetLeft((fullname + "_button").c_str()); button->setWidgetBack("discard_and_exit"); button->setWidgetPageLeft("tab_left"); @@ -1305,6 +1263,7 @@ namespace MainMenu { button->addWidgetAction("MenuAlt1", "restore_defaults"); button->addWidgetAction("MenuStart", "confirm_and_exit"); auto boolean = frame.findButton((fullname + "_button").c_str()); assert(boolean); + boolean->setWidgetSearchParent(frame.getParent()->getName()); boolean->setWidgetRight((fullname + "_customize_button").c_str()); boolean->setWidgetBack("discard_and_exit"); boolean->setWidgetPageLeft("tab_left"); @@ -1339,6 +1298,7 @@ namespace MainMenu { button->setColor(makeColor(127,127,127,255)); button->setTextHighlightColor(makeColor(255,255,255,255)); button->setTextColor(makeColor(127,127,127,255)); + button->setWidgetSearchParent(frame.getParent()->getName()); button->setWidgetBack("discard_and_exit"); button->setWidgetPageLeft("tab_left"); button->setWidgetPageRight("tab_right"); @@ -1375,6 +1335,7 @@ namespace MainMenu { button->setColor(makeColor(127,127,127,255)); button->setTextHighlightColor(makeColor(255,255,255,255)); button->setTextColor(makeColor(127,127,127,255)); + button->setWidgetSearchParent(frame.getParent()->getName()); button->setWidgetBack("discard_and_exit"); button->setWidgetPageLeft("tab_left"); button->setWidgetPageRight("tab_right"); @@ -1444,6 +1405,7 @@ namespace MainMenu { slider->setHighlightColor(makeColor(255,255,255,255)); slider->setHandleImage("images/ui/Main Menus/Settings/Settings_ValueSlider_Slide00.png"); slider->setRailImage("images/ui/Main Menus/Settings/Settings_ValueSlider_Backing00.png"); + slider->setWidgetSearchParent(frame.getParent()->getName()); slider->setWidgetBack("discard_and_exit"); slider->setWidgetPageLeft("tab_left"); slider->setWidgetPageRight("tab_right"); @@ -1473,40 +1435,7 @@ namespace MainMenu { settings_subwindow->setBorder(0); settings_subwindow->setTickCallback([](Widget& widget){ auto frame = static_cast(&widget); - auto& images = frame->getImages(); - for (auto image : images) { - if (image->path == "images/ui/Main Menus/Settings/Settings_Left_BackingSelect00.png") { - image->path = "images/ui/Main Menus/Settings/Settings_Left_Backing00.png"; - } - } - auto selectedWidget = widget.findSelectedWidget(0); - if (selectedWidget) { - std::string setting; - auto name = std::string(selectedWidget->getName()); - if (selectedWidget->getType() == Widget::WIDGET_SLIDER) { - setting = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_slider") - 1) - (sizeof("setting_") - 1)); - } else if (selectedWidget->getType() == Widget::WIDGET_BUTTON) { - auto button = static_cast(selectedWidget); - auto customize = "images/ui/Main Menus/Settings/Settings_Button_Customize00.png"; - if (strcmp(button->getBackground(), customize) == 0) { - setting = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_customize_button") - 1) - (sizeof("setting_") - 1)); - } else { - setting = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_button") - 1) - (sizeof("setting_") - 1)); - } - } - if (!setting.empty()) { - auto image = frame->findImage((std::string("setting_") + setting + std::string("_image")).c_str()); - if (image) { - image->path = "images/ui/Main Menus/Settings/Settings_Left_BackingSelect00.png"; - } - auto field = frame->findField((std::string("setting_") + setting + std::string("_field")).c_str()); - if (field) { - auto settings = static_cast(frame->getParent()); - auto tooltip = settings->findField("tooltip"); assert(tooltip); - tooltip->setText(field->getGuide()); - } - } - } + updateSettingSelection(*frame); updateSliderArrows(*frame); }); auto rock_background = settings_subwindow->addImage( @@ -1578,10 +1507,16 @@ namespace MainMenu { return std::make_pair( std::string("setting_") + std::string(setting.name) + std::string("_button"), std::string("setting_") + std::string(setting.name) + std::string("_customize_button")); - default: + case Setting::Type::Dropdown: return std::make_pair( std::string("setting_") + std::string(setting.name) + std::string("_dropdown_button"), std::string("")); + case Setting::Type::Binding: + return std::make_pair( + std::string("setting_") + std::string(setting.name) + std::string("_binding_button"), + std::string("")); + default: + return std::make_pair(std::string(""), std::string("")); } } @@ -1592,14 +1527,15 @@ namespace MainMenu { } static void settingsSubwindowFinalize(Frame& frame, int y) { - const int height = std::max(224 * 2, y); - frame.setActualSize(SDL_Rect{0, 0, 547 * 2, height}); + auto size = frame.getActualSize(); + const int height = std::max(size.h, y); + frame.setActualSize(SDL_Rect{0, 0, size.w, height}); auto rock_background = frame.findImage("background"); assert(rock_background); rock_background->pos = frame.getActualSize(); auto slider = frame.findSlider("scroll_slider"); assert(slider); slider->setValue(0.f); slider->setMinValue(0.f); - slider->setMaxValue(height - 224 * 2); + slider->setMaxValue(height - size.h); } static void hookSettingToSetting(Frame& frame, const Setting& setting1, const Setting& setting2) { @@ -1633,6 +1569,275 @@ namespace MainMenu { } } + static Frame* settingsGenericWindow(const char* name, const char* title) { + auto dimmer = main_menu_frame->addFrame("dimmer"); + dimmer->setSize(SDL_Rect{0, 0, Frame::virtualScreenX, Frame::virtualScreenY}); + dimmer->setActualSize(dimmer->getSize()); + dimmer->setColor(makeColor(0, 0, 0, 63)); + dimmer->setBorder(0); + + auto window = dimmer->addFrame(name); + window->setSize(SDL_Rect{ + (Frame::virtualScreenX - 826) / 2, + (Frame::virtualScreenY - 718) / 2, + 826, + 718}); + window->setActualSize(SDL_Rect{0, 0, 826, 718}); + window->setBorder(0); + window->setColor(0); + + auto tooltip = window->addField("tooltip", 256); + tooltip->setSize(SDL_Rect{30, 566, 766, 54}); + tooltip->setFont(smallfont_no_outline); + tooltip->setJustify(Field::justify_t::CENTER); + tooltip->setText(""); + + auto background = window->addImage( + window->getActualSize(), + 0xffffffff, + "images/ui/Main Menus/Settings/GenericWindow/UI_MM14_Window00.png", + "background" + ); + + auto timber = window->addImage( + window->getActualSize(), + 0xffffffff, + "images/ui/Main Menus/Settings/GenericWindow/UI_MM14_Window01.png", + "timber" + ); + timber->ontop = true; + + auto banner = window->addField("title", 64); + banner->setSize(SDL_Rect{246, 22, 338, 24}); + banner->setFont(banner_font); + banner->setText(title); + banner->setJustify(Field::justify_t::CENTER); + + auto subwindow = window->addFrame("subwindow"); + subwindow->setSize(SDL_Rect{30, 64, 766, 502}); + subwindow->setActualSize(SDL_Rect{0, 0, 766, 502}); + subwindow->setScrollBarsEnabled(false); + subwindow->setBorder(0); + subwindow->setColor(0); + subwindow->setTickCallback([](Widget& widget){ + auto frame = static_cast(&widget); + updateSettingSelection(*frame); + }); + + auto rocks = subwindow->addImage( + subwindow->getActualSize(), + makeColor(127, 127, 127, 251), + "images/ui/Main Menus/Settings/GenericWindow/UI_MM14_Rocks00.png", + "background" + ); + rocks->tiled = true; + + auto slider = subwindow->addSlider("scroll_slider"); + slider->setBorder(24); + slider->setOrientation(Slider::SLIDER_VERTICAL); + slider->setRailSize(SDL_Rect{732, 8, 30, 486}); + slider->setRailImage("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ScrollBar00.png"); + slider->setHandleSize(SDL_Rect{0, 0, 34, 34}); + slider->setHandleImage("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ScrollBoulder00.png"); + slider->setCallback([](Slider& slider){ + Frame* frame = static_cast(slider.getParent()); + auto actualSize = frame->getActualSize(); + actualSize.y = slider.getValue(); + frame->setActualSize(actualSize); + auto railSize = slider.getRailSize(); + railSize.y = 8 + actualSize.y; + slider.setRailSize(railSize); + }); + slider->setTickCallback([](Widget& widget){ + Slider* slider = static_cast(&widget); + Frame* frame = static_cast(slider->getParent()); + auto actualSize = frame->getActualSize(); + slider->setValue(actualSize.y); + auto railSize = slider->getRailSize(); + railSize.y = 8 + actualSize.y; + slider->setRailSize(railSize); + }); + + auto defaults = window->addButton("restore_defaults"); + defaults->setBackground("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonStandard00.png"); + defaults->setColor(makeColor(127, 127, 127, 255)); + defaults->setHighlightColor(makeColor(255, 255, 255, 255)); + defaults->setTextColor(makeColor(127, 127, 127, 255)); + defaults->setTextHighlightColor(makeColor(255, 255, 255, 255)); + defaults->setSize(SDL_Rect{156, 630, 164, 62}); + defaults->setText("Restore\nDefaults"); + defaults->setFont(smallfont_outline); + defaults->setWidgetSearchParent(name); + defaults->setWidgetBack("discard_and_exit"); + defaults->addWidgetAction("MenuStart", "confirm_and_exit"); + defaults->addWidgetAction("MenuAlt1", "restore_defaults"); + defaults->setWidgetRight("discard_and_exit"); + + auto discard = window->addButton("discard_and_exit"); + discard->setBackground("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonStandard00.png"); + discard->setColor(makeColor(127, 127, 127, 255)); + discard->setHighlightColor(makeColor(255, 255, 255, 255)); + discard->setTextColor(makeColor(127, 127, 127, 255)); + discard->setTextHighlightColor(makeColor(255, 255, 255, 255)); + discard->setText("Discard\n& Exit"); + discard->setFont(smallfont_outline); + discard->setSize(SDL_Rect{ + (window->getActualSize().w - 164) / 2, + 630, + 164, + 62} + ); + discard->setCallback([](Button& button){ + soundCancel(); + auto parent = static_cast(button.getParent()); assert(parent); + auto parent_background = static_cast(parent->getParent()); assert(parent_background); + parent_background->removeSelf(); + }); + discard->setWidgetSearchParent(name); + discard->setWidgetBack("discard_and_exit"); + discard->addWidgetAction("MenuStart", "confirm_and_exit"); + discard->addWidgetAction("MenuAlt1", "restore_defaults"); + discard->setWidgetLeft("restore_defaults"); + discard->setWidgetRight("confirm_and_exit"); + + auto confirm = window->addButton("confirm_and_exit"); + confirm->setBackground("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonStandard00.png"); + confirm->setColor(makeColor(127, 127, 127, 255)); + confirm->setHighlightColor(makeColor(255, 255, 255, 255)); + confirm->setTextColor(makeColor(127, 127, 127, 255)); + confirm->setTextHighlightColor(makeColor(255, 255, 255, 255)); + confirm->setText("Confirm\n& Exit"); + confirm->setFont(smallfont_outline); + confirm->setSize(SDL_Rect{504, 630, 164, 62}); + confirm->setCallback([](Button& button){ + soundActivate(); + auto parent = static_cast(button.getParent()); assert(parent); + auto parent_background = static_cast(parent->getParent()); assert(parent_background); + parent_background->removeSelf(); + }); + confirm->setWidgetSearchParent(name); + confirm->setWidgetBack("discard_and_exit"); + confirm->addWidgetAction("MenuStart", "confirm_and_exit"); + confirm->addWidgetAction("MenuAlt1", "restore_defaults"); + confirm->setWidgetLeft("discard_and_exit"); + confirm->select(); + + return window; + } + + static void settingsBindings(Button& button) { + soundActivate(); + auto window = settingsGenericWindow("bindings", "BINDINGS"); assert(window); + auto subwindow = window->findFrame("subwindow"); assert(subwindow); + int y = 8; + + static const std::vector bindings = { + {Setting::Type::Binding, "Move Forward"}, + {Setting::Type::Binding, "Move Left"}, + {Setting::Type::Binding, "Move Backward"}, + {Setting::Type::Binding, "Move Right"}, + {Setting::Type::Binding, "Turn Left"}, + {Setting::Type::Binding, "Turn Right"}, + {Setting::Type::Binding, "Look Up"}, + {Setting::Type::Binding, "Look Down"}, + {Setting::Type::Binding, "Chat"}, + {Setting::Type::Binding, "Console Command"}, + {Setting::Type::Binding, "Character Status"}, + {Setting::Type::Binding, "Spell List"}, + {Setting::Type::Binding, "Cast Spell"}, + {Setting::Type::Binding, "Block"}, + {Setting::Type::Binding, "Sneak"}, + {Setting::Type::Binding, "Attack"}, + {Setting::Type::Binding, "Use"}, + {Setting::Type::Binding, "Autosort Inventory"}, + {Setting::Type::Binding, "Command NPC"}, + {Setting::Type::Binding, "Show NPC Commands"}, + {Setting::Type::Binding, "Cycle NPCs"}, + {Setting::Type::Binding, "Hotbar Scroll Left"}, + {Setting::Type::Binding, "Hotbar Scroll Right"}, + {Setting::Type::Binding, "Hotbar Select"}, + }; + + static bool bind_mode; + static Button* bound_button = nullptr; + static std::string bound_binding = ""; + static int bound_player = -1; + bind_mode = false; + + for (auto& binding : bindings) { + char tip[256]; + snprintf(tip, sizeof(tip), "Bind an input device to %s", binding.name); + y += settingsAddBinding(*subwindow, y, binding.name, tip, + [](Button& button){ + soundToggle(); + auto& name = std::string(button.getName()); + bind_mode = true; + bound_button = &button; + bound_binding = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_binding_button") - 1) - (sizeof("setting_") - 1));; + bound_player = 0; + button.setText(". . ."); + auto subwindow = static_cast(button.getParent()); assert(subwindow); + auto settings = static_cast(subwindow->getParent()); assert(settings); + auto tooltip = settings->findField("tooltip"); assert(tooltip); + char buf[256]; + snprintf(buf, sizeof(buf), + "Binding \"%s\". Press ESC to cancel.\n" + "The next input you activate will be bound to this action.", + bound_binding.c_str()); + tooltip->setText(buf); + Input::clearDefaultBindings(); + + auto bindings = main_menu_frame->findFrame("bindings"); assert(bindings); + auto confirm = bindings->findButton("confirm_and_exit"); assert(confirm); + auto discard = bindings->findButton("discard_and_exit"); assert(discard); + auto defaults = bindings->findButton("restore_defaults"); assert(defaults); + confirm->setDisabled(true); + discard->setDisabled(true); + defaults->setDisabled(true); + }); + } + + window->setTickCallback([](Widget&){ + if (bind_mode) { + if (bound_button) { + if (!Input::lastInputOfAnyKind.empty()) { + if (Input::lastInputOfAnyKind != "Escape") { + Input::inputs[bound_player].bind(bound_binding.c_str(), + Input::lastInputOfAnyKind.c_str()); + } + bound_button->setText(Input::inputs[bound_player].binding(bound_binding.c_str())); + bound_button = nullptr; + } + } else { + if (!Input::inputs[bound_player].binary(bound_binding.c_str())) { + auto bindings = main_menu_frame->findFrame("bindings"); assert(bindings); + auto confirm = bindings->findButton("confirm_and_exit"); assert(confirm); + auto discard = bindings->findButton("discard_and_exit"); assert(discard); + auto defaults = bindings->findButton("restore_defaults"); assert(defaults); + confirm->setDisabled(false); + discard->setDisabled(false); + defaults->setDisabled(false); + + auto binding = Input::inputs[bound_player].binding(bound_binding.c_str()); + auto tooltip = bindings->findField("tooltip"); assert(tooltip); + char buf[256]; + snprintf(buf, sizeof(buf), "Bound \"%s\" to \"%s\"", bound_binding.c_str(), binding); + tooltip->setText(buf); + + Input::defaultBindings(); + bound_binding = ""; + bound_player = -1; + bind_mode = false; + } + } + } + }); + + hookSettings(*subwindow, bindings); + settingsSubwindowFinalize(*subwindow, y); + settingsSelect(*subwindow, bindings.front()); + } + void settingsUI(Button& button) { Frame* settings_subwindow; if ((settings_subwindow = settingsSubwindowSetup(button)) == nullptr) { @@ -4109,7 +4314,13 @@ namespace MainMenu { /******************************************************************************/ static void createPlayWindow() { - auto window = main_menu_frame->addFrame("play_game_window"); + auto dimmer = main_menu_frame->addFrame("dimmer"); + dimmer->setSize(SDL_Rect{0, 0, Frame::virtualScreenX, Frame::virtualScreenY}); + dimmer->setActualSize(dimmer->getSize()); + dimmer->setColor(makeColor(0, 0, 0, 63)); + dimmer->setBorder(0); + + auto window = dimmer->addFrame("play_game_window"); window->setSize(SDL_Rect{ (Frame::virtualScreenX - 218 * 2) / 2, (Frame::virtualScreenY - 130 * 2) / 2, @@ -4160,6 +4371,7 @@ namespace MainMenu { soundCancel(); auto frame = static_cast(button.getParent()); frame = static_cast(frame->getParent()); + frame = static_cast(frame->getParent()); frame->removeSelf(); assert(main_menu_frame); auto buttons = main_menu_frame->findFrame("buttons"); assert(buttons); @@ -4225,10 +4437,17 @@ namespace MainMenu { // remove "Play Game" window auto frame = static_cast(button.getParent()); + frame = static_cast(frame->getParent()); frame->removeSelf(); + auto dimmer = main_menu_frame->addFrame("dimmer"); + dimmer->setSize(SDL_Rect{0, 0, Frame::virtualScreenX, Frame::virtualScreenY}); + dimmer->setActualSize(dimmer->getSize()); + dimmer->setColor(makeColor(0, 0, 0, 63)); + dimmer->setBorder(0); + // create "Local or Network" window - auto window = main_menu_frame->addFrame("local_or_network_window"); + auto window = dimmer->addFrame("local_or_network_window"); window->setSize(SDL_Rect{ (Frame::virtualScreenX - 436) / 2, (Frame::virtualScreenY - 240) / 2, @@ -4255,6 +4474,7 @@ namespace MainMenu { soundCancel(); auto frame = static_cast(button.getParent()); frame = static_cast(frame->getParent()); + frame = static_cast(frame->getParent()); frame->removeSelf(); createPlayWindow(); }); @@ -4467,7 +4687,13 @@ namespace MainMenu { allSettings.extra_life_enabled = svFlags & SV_FLAG_LIFESAVING; allSettings.cheats_enabled = svFlags & SV_FLAG_CHEATS; - auto settings = main_menu_frame->addFrame("settings"); + auto dimmer = main_menu_frame->addFrame("dimmer"); + dimmer->setSize(SDL_Rect{0, 0, Frame::virtualScreenX, Frame::virtualScreenY}); + dimmer->setActualSize(dimmer->getSize()); + dimmer->setColor(makeColor(0, 0, 0, 63)); + dimmer->setBorder(0); + + auto settings = dimmer->addFrame("settings"); settings->setSize(SDL_Rect{(Frame::virtualScreenX - 1126) / 2, (Frame::virtualScreenY - 718) / 2, 1126, 718}); settings->setActualSize(SDL_Rect{0, 0, settings->getSize().w, settings->getSize().h}); settings->setColor(0); @@ -4538,6 +4764,7 @@ namespace MainMenu { button->setSize(SDL_Rect{76 + (272 - 76) * c, 64, 184, 64}); button->setColor(makeColor(255, 255, 255, 191)); button->setHighlightColor(makeColor(255, 255, 255, 255)); + button->setWidgetSearchParent("settings"); button->setWidgetPageLeft("tab_left"); button->setWidgetPageRight("tab_right"); button->addWidgetAction("MenuAlt1", "restore_defaults"); @@ -4572,6 +4799,7 @@ namespace MainMenu { tab_left->setSize(SDL_Rect{32, 68, 38, 58}); tab_left->setColor(makeColor(255, 255, 255, 191)); tab_left->setHighlightColor(makeColor(255, 255, 255, 255)); + tab_left->setWidgetSearchParent("settings"); tab_left->setWidgetBack("discard_and_exit"); tab_left->setWidgetPageLeft("tab_left"); tab_left->setWidgetPageRight("tab_right"); @@ -4608,6 +4836,7 @@ namespace MainMenu { tab_right->setSize(SDL_Rect{1056, 68, 38, 58}); tab_right->setColor(makeColor(255, 255, 255, 191)); tab_right->setHighlightColor(makeColor(255, 255, 255, 255)); + tab_right->setWidgetSearchParent("settings"); tab_right->setWidgetBack("discard_and_exit"); tab_right->setWidgetPageLeft("tab_left"); tab_right->setWidgetPageRight("tab_right"); @@ -4652,6 +4881,7 @@ namespace MainMenu { restore_defaults->setFont(smallfont_outline); restore_defaults->setColor(makeColor(255, 255, 255, 191)); restore_defaults->setHighlightColor(makeColor(255, 255, 255, 255)); + restore_defaults->setWidgetSearchParent("settings"); restore_defaults->setWidgetBack("discard_and_exit"); restore_defaults->setWidgetPageLeft("tab_left"); restore_defaults->setWidgetPageRight("tab_right"); @@ -4672,17 +4902,20 @@ namespace MainMenu { discard_and_exit->setFont(smallfont_outline); discard_and_exit->setColor(makeColor(255, 255, 255, 191)); discard_and_exit->setHighlightColor(makeColor(255, 255, 255, 255)); - discard_and_exit->setCallback([](Button&){ + discard_and_exit->setCallback([](Button& button){ soundCancel(); - assert(main_menu_frame); - auto buttons = main_menu_frame->findFrame("buttons"); assert(buttons); - auto settings_button = buttons->findButton("SETTINGS"); assert(settings_button); - settings_button->select(); - auto settings = main_menu_frame->findFrame("settings"); + if (main_menu_frame) { + auto buttons = main_menu_frame->findFrame("buttons"); assert(buttons); + auto settings_button = buttons->findButton("SETTINGS"); assert(settings_button); + settings_button->select(); + } + auto settings = static_cast(button.getParent()); if (settings) { - settings->removeSelf(); + auto dimmer = static_cast(settings->getParent()); + dimmer->removeSelf(); } }); + discard_and_exit->setWidgetSearchParent("settings"); discard_and_exit->setWidgetBack("discard_and_exit"); discard_and_exit->setWidgetPageLeft("tab_left"); discard_and_exit->setWidgetPageRight("tab_right"); @@ -4700,18 +4933,21 @@ namespace MainMenu { confirm_and_exit->setFont(smallfont_outline); confirm_and_exit->setColor(makeColor(255, 255, 255, 191)); confirm_and_exit->setHighlightColor(makeColor(255, 255, 255, 255)); - confirm_and_exit->setCallback([](Button&){ + confirm_and_exit->setCallback([](Button& button){ soundActivate(); - assert(main_menu_frame); settingsSave(); - auto buttons = main_menu_frame->findFrame("buttons"); assert(buttons); - auto settings_button = buttons->findButton("SETTINGS"); assert(settings_button); - settings_button->select(); - auto settings = main_menu_frame->findFrame("settings"); + if (main_menu_frame) { + auto buttons = main_menu_frame->findFrame("buttons"); assert(buttons); + auto settings_button = buttons->findButton("SETTINGS"); assert(settings_button); + settings_button->select(); + } + auto settings = static_cast(button.getParent()); if (settings) { - settings->removeSelf(); + auto dimmer = static_cast(settings->getParent()); + dimmer->removeSelf(); } }); + confirm_and_exit->setWidgetSearchParent("settings"); confirm_and_exit->setWidgetBack("discard_and_exit"); confirm_and_exit->setWidgetPageLeft("tab_left"); confirm_and_exit->setWidgetPageRight("tab_right"); @@ -4729,8 +4965,14 @@ namespace MainMenu { } soundActivate(); + + auto dimmer = main_menu_frame->addFrame("dimmer"); + dimmer->setSize(SDL_Rect{0, 0, Frame::virtualScreenX, Frame::virtualScreenY}); + dimmer->setActualSize(dimmer->getSize()); + dimmer->setColor(makeColor(0, 0, 0, 63)); + dimmer->setBorder(0); - auto frame = main_menu_frame->addFrame("quit_confirm"); + auto frame = dimmer->addFrame("quit_confirm"); frame->setSize(SDL_Rect{(Frame::virtualScreenX - 364) / 2, (Frame::virtualScreenY - 176) / 2, 364, 176}); frame->setActualSize(SDL_Rect{0, 0, 364, 176}); frame->setColor(0); @@ -4801,7 +5043,8 @@ namespace MainMenu { quit_button->select(); auto quit_confirm = main_menu_frame->findFrame("quit_confirm"); if (quit_confirm) { - quit_confirm->removeSelf(); + auto dimmer = static_cast(quit_confirm->getParent()); assert(dimmer); + dimmer->removeSelf(); } }); From 73bc7d74581ab0f011db9aa8d40b7fb25eca1563 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Mon, 25 Oct 2021 23:02:40 -0700 Subject: [PATCH 19/39] Can now assign separate colors for outlines on Text + Fields Fixed sliders in main menu glitching out when dragging them MainMenu.cpp: dropdown items can be disabled / grayed out Signed-off-by: SheridanR --- src/book.cpp | 6 +- src/interface/bookgui.cpp | 12 ++- src/interface/playerinventory.cpp | 30 ++++--- src/opengl.cpp | 3 +- src/ui/Button.cpp | 3 +- src/ui/Field.cpp | 19 +++-- src/ui/Field.hpp | 12 ++- src/ui/Frame.cpp | 6 +- src/ui/GameUI.cpp | 24 ++++-- src/ui/MainMenu.cpp | 130 ++++++++++++++++++++++++------ src/ui/Slider.cpp | 10 ++- src/ui/Slider.hpp | 3 + src/ui/Text.cpp | 69 ++++++++++++---- src/ui/Text.hpp | 6 +- src/ui/Widget.cpp | 6 +- 15 files changed, 254 insertions(+), 85 deletions(-) diff --git a/src/book.cpp b/src/book.cpp index 6f92b27dd..e5034d608 100644 --- a/src/book.cpp +++ b/src/book.cpp @@ -620,7 +620,8 @@ void BookParser_t::createBook(std::string filename) firstIteration = false; pageText += token; tmpField->setText(pageText.c_str()); - if ( auto getText = Text::get(tmpField->getText(), tmpField->getFont()) ) + if ( auto getText = Text::get(tmpField->getText(), tmpField->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)) ) { int textHeight = getText->getHeight(); if ( textHeight > tmpField->getSize().h ) @@ -694,7 +695,8 @@ void BookParser_t::createBook(std::string filename) // } // } // tmpField.setText(pageText.c_str()); - // if ( auto getText = Text::get(tmpField.getText(), tmpField.getFont()) ) + // if ( auto getText = Text::get(tmpField.getText(), tmpField.getFont(), + // makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)) ) // { // if ( getText->getHeight() > tmpField.getSize().h - tmpField.getSize().y ) // { diff --git a/src/interface/bookgui.cpp b/src/interface/bookgui.cpp index 64c6e6bd1..8f920a22c 100644 --- a/src/interface/bookgui.cpp +++ b/src/interface/bookgui.cpp @@ -212,7 +212,8 @@ void Player::BookGUI_t::updateBookGUI() auto promptImg = innerFrame->findImage("prompt back img"); promptImg->disabled = !drawGlyphs; SDL_Rect glyphPos = promptImg->pos; - if ( auto textGet = Text::get(promptBack->getText(), promptBack->getFont()) ) + if ( auto textGet = Text::get(promptBack->getText(), promptBack->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)) ) { SDL_Rect textPos = promptBack->getSize(); textPos.w = textGet->getWidth(); @@ -239,7 +240,8 @@ void Player::BookGUI_t::updateBookGUI() promptImg->disabled = promptNext->isDisabled(); SDL_Rect glyphPos = promptImg->pos; SDL_Rect textPos = promptNext->getSize(); - if ( auto textGet = Text::get(promptNext->getText(), promptNext->getFont()) ) + if ( auto textGet = Text::get(promptNext->getText(), promptNext->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)) ) { textPos.w = textGet->getWidth(); } @@ -264,7 +266,8 @@ void Player::BookGUI_t::updateBookGUI() promptImg->disabled = promptPrev->isDisabled(); SDL_Rect glyphPos = promptImg->pos; SDL_Rect textPos = promptPrev->getSize(); - if ( auto textGet = Text::get(promptPrev->getText(), promptPrev->getFont()) ) + if ( auto textGet = Text::get(promptPrev->getText(), promptPrev->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)) ) { textPos.w = textGet->getWidth(); } @@ -432,7 +435,8 @@ void Player::BookGUI_t::updateBookGUI() // firstIteration = false; // pageText += token; // leftColumn->setText(pageText.c_str()); - // if ( auto getText = Text::get(leftColumn->getText(), leftColumn->getFont()) ) + // if ( auto getText = Text::get(leftColumn->getText(), leftColumn->getFont(), + // makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)) ) // { // int textHeight = getText->getHeight(); // if ( textHeight > leftColumn->getSize().h ) diff --git a/src/interface/playerinventory.cpp b/src/interface/playerinventory.cpp index fe062ec78..f8415a930 100644 --- a/src/interface/playerinventory.cpp +++ b/src/interface/playerinventory.cpp @@ -2257,7 +2257,8 @@ void Player::HUD_t::updateFrameTooltip(Item* item, const int x, const int y) snprintf(buf, sizeof(buf), "%s %s (%+d)", ItemTooltips.getItemStatusAdjective(item->type, item->status).c_str(), item->getName(), item->beatitude); } txtHeader->setText(buf); - Text* textGet = Text::get(txtHeader->getText(), txtHeader->getFont()); + Text* textGet = Text::get(txtHeader->getText(), txtHeader->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)); if ( textGet ) { textx = textGet->getWidth(); @@ -2324,13 +2325,15 @@ void Player::HUD_t::updateFrameTooltip(Item* item, const int x, const int y) unsigned int newWidth = 0; for ( auto& str : tokens ) { - if ( Text* textGet = Text::get(str.c_str(), txtHeader->getFont()) ) + if ( Text* textGet = Text::get(str.c_str(), txtHeader->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)) ) { newWidth = std::max(newWidth, textGet->getWidth()); } } - if ( Text* textGet = Text::get(txtHeader->getText(), txtHeader->getFont()) ) + if ( Text* textGet = Text::get(txtHeader->getText(), txtHeader->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)) ) { textx = newWidth; texty = textGet->getHeight(); @@ -2344,7 +2347,8 @@ void Player::HUD_t::updateFrameTooltip(Item* item, const int x, const int y) } } - //if ( Text* textGet = Text::get(txtHeader->getText(), txtHeader->getFont()) ) + //if ( Text* textGet = Text::get(txtHeader->getText(), txtHeader->getFont(), + // makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)) ) //{ //} @@ -3083,7 +3087,8 @@ void Player::HUD_t::updateFrameTooltip(Item* item, const int x, const int y) int numLines = txtPrimaryValue->getNumTextLines(); if ( numLines > 1 ) { - auto textGet = Text::get(txtPrimaryValue->getText(), txtPrimaryValue->getFont()); + auto textGet = Text::get(txtPrimaryValue->getText(), txtPrimaryValue->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)); if ( textGet ) { imgPrimaryIcon->pos.y += pady * (std::max(0, numLines - 1)); // for each line > 1 add padding @@ -3126,7 +3131,8 @@ void Player::HUD_t::updateFrameTooltip(Item* item, const int x, const int y) int numLines = txtSecondaryValue->getNumTextLines(); if ( numLines > 1 ) { - auto textGet = Text::get(txtSecondaryValue->getText(), txtSecondaryValue->getFont()); + auto textGet = Text::get(txtSecondaryValue->getText(), txtSecondaryValue->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)); if ( textGet ) { imgSecondaryIcon->pos.y += pady * (std::max(0, numLines - 1)); // for each line > 1 add padding @@ -3164,7 +3170,8 @@ void Player::HUD_t::updateFrameTooltip(Item* item, const int x, const int y) int numLines = txtThirdValue->getNumTextLines(); if ( numLines > 1 ) { - auto textGet = Text::get(txtThirdValue->getText(), txtThirdValue->getFont()); + auto textGet = Text::get(txtThirdValue->getText(), txtThirdValue->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)); if ( textGet ) { imgThirdIcon->pos.y += pady * (std::max(0, numLines - 1)); // for each line > 1 add padding @@ -3257,7 +3264,8 @@ void Player::HUD_t::updateFrameTooltip(Item* item, const int x, const int y) const int charHeight = 13; //Font::get(txtDescription->getFont())->sizeText("_", nullptr, &charHeight); -- this produces 13px @ 12 point font, used above - /*auto textGet = Text::get(txtDescription->getText(), txtDescription->getFont()); + /*auto textGet = Text::get(txtDescription->getText(), txtDescription->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)); if ( textGet ) { auto tmp = textGet->getWidth(); @@ -3614,7 +3622,8 @@ void Player::HUD_t::updateFrameTooltip(Item* item, const int x, const int y) auto& txt = pair.second; if ( !txt->isDisabled() ) { - if ( auto textGet = Text::get(txt->getText(), txt->getFont()) ) + if ( auto textGet = Text::get(txt->getText(), txt->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)) ) { if ( txt->getHJustify() == Field::justify_t::RIGHT ) { @@ -3646,7 +3655,8 @@ void Player::HUD_t::updateFrameTooltip(Item* item, const int x, const int y) auto& txt = pair.second; if ( !txt->isDisabled() ) { - if ( auto textGet = Text::get(txt->getText(), txt->getFont()) ) + if ( auto textGet = Text::get(txt->getText(), txt->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)) ) { if ( txt->getHJustify() == Field::justify_t::LEFT ) { diff --git a/src/opengl.cpp b/src/opengl.cpp index 9eeeec77b..df0fc4459 100644 --- a/src/opengl.cpp +++ b/src/opengl.cpp @@ -1589,7 +1589,8 @@ void glDrawSpriteFromImage(view_t* camera, Entity* entity, std::string text, int return; } - auto rendered_text = Text::get(text.c_str(), "fonts/pixel_maz.ttf#16#2"); + auto rendered_text = Text::get(text.c_str(), "fonts/pixel_maz.ttf#16#2", + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)); auto textureId = rendered_text->getTexID(); // setup projection diff --git a/src/ui/Button.cpp b/src/ui/Button.cpp index fbc328bba..7d77cfe6d 100644 --- a/src/ui/Button.cpp +++ b/src/ui/Button.cpp @@ -148,7 +148,8 @@ void Button::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector reflowTextLine(std::string& input, int w { // this is probably OK } - else if ( getText = Text::get(std::string(result[currentLine] + " " + token).c_str(), font) ) + else if ( getText = Text::get(std::string(result[currentLine] + " " + token).c_str(), font, + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)) ) { if ( getText->getWidth() > width ) { @@ -452,7 +459,7 @@ int Field::getLastLineThatFitsWithinHeight() if ( text == nullptr || textlen <= 1 ) { return -1; } - if ( auto getText = Text::get(text, font.c_str()) ) + if ( auto getText = Text::get(text, font.c_str(), textColor, outlineColor) ) { if ( getText->getHeight() <= getSize().h/* - getSize().y*/ ) { @@ -475,7 +482,7 @@ int Field::getLastLineThatFitsWithinHeight() allLines.push_back('\n'); } allLines += token[0]; - if ( auto getText = Text::get(allLines.c_str(), font.c_str()) ) + if ( auto getText = Text::get(allLines.c_str(), font.c_str(), textColor, outlineColor) ) { if ( getText->getHeight() > getSize().h ) { @@ -494,7 +501,7 @@ void Field::reflowTextToFit(const int characterOffset) { return; } - if ( auto getText = Text::get(text, font.c_str()) ) + if ( auto getText = Text::get(text, font.c_str(), textColor, outlineColor) ) { if ( getText->getWidth() <= (getSize().w) ) { @@ -541,7 +548,7 @@ void Field::reflowTextToFit(const int characterOffset) { int charWidth = 0; actualFont->sizeText("_", &charWidth, nullptr); - //if ( auto textGet = Text::get("_", font.c_str()) ) + //if ( auto textGet = Text::get("_", font.c_str(), textColor, outlineColor) ) //{ // charWidth = textGet->getWidth(); //} diff --git a/src/ui/Field.hpp b/src/ui/Field.hpp index 40ecb3c3c..b3043da89 100644 --- a/src/ui/Field.hpp +++ b/src/ui/Field.hpp @@ -65,10 +65,15 @@ class Field : public Widget { //! gets the physical screen-space x/y (not relative to current parent - but to the absolute root) SDL_Rect getAbsoluteSize() const; + //! TODO comment pls + int getLastLineThatFitsWithinHeight(); + virtual type_t getType() const override { return WIDGET_FIELD; } const char* getText() const { return text; } const char* getFont() const { return font.c_str(); } const Uint32 getColor() const { return color; } + const Uint32 getTextColor() const { return textColor; } + const Uint32 getOutlineColor() const { return outlineColor; } const SDL_Rect getSize() const { return size; } const int getHJustify() const { return static_cast(hjustify); } const int getVJustify() const { return static_cast(vjustify); } @@ -83,6 +88,8 @@ class Field : public Widget { void setPos(const int x, const int y) { size.x = x; size.y = y; } void setSize(const SDL_Rect _size) { size = _size; } void setColor(const Uint32 _color) { color = _color; } + void setTextColor(const Uint32 _color) { textColor = _color; } + void setOutlineColor(const Uint32 _color) { outlineColor = _color; } void setEditable(const bool _editable) { editable = _editable; } void setNumbersOnly(const bool _numbersOnly) { numbersOnly = _numbersOnly; } void setJustify(const int _justify) { hjustify = vjustify = static_cast(_justify); } @@ -93,7 +100,6 @@ class Field : public Widget { void setFont(const char* _font) { font = _font; } void setGuide(const char* _guide) { guide = _guide; } void reflowTextToFit(const int characterOffset); - int getLastLineThatFitsWithinHeight(); void setOntop(const bool _ontop) { ontop = _ontop; } static char* tokenize(char* str, const char* const delimiters); @@ -102,7 +108,9 @@ class Field : public Widget { std::string guide; //!< string to use as a descriptive guide for the field (eg "Enter character's name"); char* text = nullptr; //!< internal text buffer size_t textlen = 0; //!< length of internal text buffer - Uint32 color = 0xFFFFFFFF; //!< text color + Uint32 color; //!< color mixed w/ final rendered text + Uint32 textColor; //!< text color + Uint32 outlineColor; //!< outline color SDL_Rect size; //!< size of the field in pixels justify_t hjustify = LEFT; //!< horizontal text justification justify_t vjustify = TOP; //!< vertical text justification diff --git a/src/ui/Frame.cpp b/src/ui/Frame.cpp index 36beeb76c..160ee2ff1 100644 --- a/src/ui/Frame.cpp +++ b/src/ui/Frame.cpp @@ -371,7 +371,8 @@ void Frame::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vectorgetName()); + Text* text = Text::get(tooltip, font->getName(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)); SDL_Rect src; src.x = mousex + 20; src.y = mousey; diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index d9dfe2c3e..c4627725b 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -653,7 +653,8 @@ void Player::MessageZone_t::processChatbox() //current->requiresResize = false; } - Text* textGet = Text::get(entry->getText(), entry->getFont()); + Text* textGet = Text::get(entry->getText(), entry->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)); if ( !messageDrawDescending ) { currentY -= textGet->getHeight(); @@ -3617,7 +3618,8 @@ void Player::Inventory_t::updateItemContextMenu() if ( auto interactText = interactFrame->findField("interact text") ) { interactText->setColor(hudColors.itemContextMenuHeadingText); - if ( auto textGet = Text::get(interactText->getText(), interactText->getFont()) ) + if ( auto textGet = Text::get(interactText->getText(), interactText->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)) ) { maxWidth = textGet->getWidth(); } @@ -3666,7 +3668,8 @@ void Player::Inventory_t::updateItemContextMenu() }*/ txt->setText(getContextMenuLangEntry(player.playernum, promptType, *item)); - if ( auto textGet = Text::get(txt->getText(), txt->getFont()) ) + if ( auto textGet = Text::get(txt->getText(), txt->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)) ) { maxWidth = std::max(textGet->getWidth(), maxWidth); @@ -4839,7 +4842,8 @@ SDL_Surface* blitEnemyBar(const int player) } for ( auto& txt : frame->getFields() ) { - auto textGet = Text::get(txt->getText(), txt->getFont()); + auto textGet = Text::get(txt->getText(), txt->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)); SDL_Surface* txtSurf = const_cast(textGet->getSurf()); SDL_Rect pos; pos.w = textGet->getWidth(); @@ -5205,10 +5209,12 @@ void Player::HUD_t::updateEnemyBar2(Frame* whichFrame, void* enemyHPDetails) SDL_Rect barPos = foregroundFrame->getAbsoluteSize(); //txtPos.x = barPos.x + baseBg->pos.x/* + baseBg->pos.w*/; //txtPos.y = barPos.y - 30; + auto text = Text::get(dmgText->getText(), dmgText->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)); txtPos.x = 30 + player.camera_virtualWidth() / 2; txtPos.y = -50 + player.camera_virtualHeight() / 2; - txtPos.w = Text::get(dmgText->getText(), dmgText->getFont())->getWidth(); - txtPos.h = Text::get(dmgText->getText(), dmgText->getFont())->getHeight(); + txtPos.w = text->getWidth(); + txtPos.h = text->getHeight(); dmgText->setSize(txtPos); hudDamageTextVelocityX = 1.0; hudDamageTextVelocityY = 3.0; @@ -5574,10 +5580,12 @@ void Player::HUD_t::updateEnemyBar(Frame* whichFrame) SDL_Rect barPos = foregroundFrame->getAbsoluteSize(); //txtPos.x = barPos.x + baseBg->pos.x/* + baseBg->pos.w*/; //txtPos.y = barPos.y - 30; + auto text = Text::get(dmgText->getText(), dmgText->getFont(), + makeColor(255, 255, 255, 255), makeColor(0, 0, 0, 255)); txtPos.x = 30 + player.camera_virtualWidth() / 2; txtPos.y = -50 + player.camera_virtualHeight() / 2; - txtPos.w = Text::get(dmgText->getText(), dmgText->getFont())->getWidth(); - txtPos.h = Text::get(dmgText->getText(), dmgText->getFont())->getHeight(); + txtPos.w = text->getWidth(); + txtPos.h = text->getHeight(); dmgText->setSize(txtPos); hudDamageTextVelocityX = 1.0; hudDamageTextVelocityY = 3.0; diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index ac1c72a61..a27b4e041 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -176,10 +176,8 @@ namespace MainMenu { image->path = "images/ui/Main Menus/Settings/Settings_Left_Backing00.png"; } } - static Widget* current_selected_widget = nullptr; auto selectedWidget = frame.findSelectedWidget(0); - if (selectedWidget && current_selected_widget != selectedWidget) { - current_selected_widget = selectedWidget; + if (selectedWidget) { std::string setting; auto name = std::string(selectedWidget->getName()); if (selectedWidget->getType() == Widget::WIDGET_SLIDER) { @@ -188,10 +186,13 @@ namespace MainMenu { auto button = static_cast(selectedWidget); auto customize = "images/ui/Main Menus/Settings/Settings_Button_Customize00.png"; auto binding = "images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonChoosing00.png"; + auto dropdown = "images/ui/Main Menus/Settings/Settings_Drop_ScrollBG02.png"; if (strcmp(button->getBackground(), customize) == 0) { setting = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_customize_button") - 1) - (sizeof("setting_") - 1)); } else if (strcmp(button->getBackground(), binding) == 0) { setting = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_binding_button") - 1) - (sizeof("setting_") - 1)); + } else if (strcmp(button->getBackground(), dropdown) == 0) { + setting = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_dropdown_button") - 1) - (sizeof("setting_") - 1)); } else { setting = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_button") - 1) - (sizeof("setting_") - 1)); } @@ -203,9 +204,13 @@ namespace MainMenu { } auto field = frame.findField((std::string("setting_") + setting + std::string("_field")).c_str()); if (field) { - auto settings = static_cast(frame.getParent()); - auto tooltip = settings->findField("tooltip"); assert(tooltip); - tooltip->setText(field->getGuide()); + static Widget* current_selected_widget = nullptr; + if (current_selected_widget != selectedWidget) { + current_selected_widget = selectedWidget; + auto settings = static_cast(frame.getParent()); + auto tooltip = settings->findField("tooltip"); assert(tooltip); + tooltip->setText(field->getGuide()); + } } } } @@ -473,6 +478,7 @@ namespace MainMenu { BooleanWithCustomize = 3, Dropdown = 4, Binding = 5, + //Field = 6, }; Type type; const char* name; @@ -982,9 +988,8 @@ namespace MainMenu { static void settingsOpenDropdown(Button& button, const char* name, bool small_dropdown, void(*entry_func)(Frame::entry_t&)) { std::string dropdown_name = "setting_" + std::string(name) + "_dropdown"; - auto settings = main_menu_frame->findFrame("settings"); assert(settings); - auto settings_subwindow = settings->findFrame("settings_subwindow"); assert(settings_subwindow); - auto dropdown = settings_subwindow->addFrame(dropdown_name.c_str()); assert(dropdown); + auto frame = static_cast(button.getParent()); + auto dropdown = frame->addFrame(dropdown_name.c_str()); assert(dropdown); dropdown->setSize(SDL_Rect{ button.getSize().x, button.getSize().y, @@ -1036,7 +1041,25 @@ namespace MainMenu { } } else { - break; + auto str = std::string("~__") + std::to_string(i); + auto find = button.getWidgetActions().find(str); + if (find != button.getWidgetActions().end()) { + auto entry_name = find->second.c_str(); + auto entry = dropdown_list->addEntry(entry_name, false); + entry->text = entry_name; + entry->click = [](Frame::entry_t&){soundError();}; + entry->ctrlClick = entry->click; + entry->color = makeColor(127, 127, 127, 255); + dropdown_list->resizeForEntries(); + auto size = dropdown_list->getActualSize(); + size.h += 14; + dropdown_list->setActualSize(size); + if (strcmp(button.getText(), entry_name) == 0) { + dropdown_list->setSelection(i); + } + } else { + break; + } } } dropdown_list->scrollToSelection(true); @@ -1110,11 +1133,13 @@ namespace MainMenu { }); } - static int settingsAddSubHeader(Frame& frame, int y, const char* name, const char* text) { + static int settingsAddSubHeader(Frame& frame, int y, const char* name, const char* text, bool generic_window = false) { std::string fullname = std::string("subheader_") + name; auto image = frame.addImage( - SDL_Rect{0, y, 1080, 42}, + SDL_Rect{0, y, frame.getSize().w, 42}, 0xffffffff, + generic_window? + "images/ui/Main Menus/Settings/GenericWindow/UI_MM14_HeaderBacking00.png": "images/ui/Main Menus/Settings/Settings_SubHeading_Backing00.png", (fullname + "_image").c_str() ); @@ -1123,16 +1148,17 @@ namespace MainMenu { field->setFont(bigfont_outline); field->setText(text); field->setJustify(Field::justify_t::CENTER); - Text* text_image = Text::get(text, field->getFont()); + Text* text_image = Text::get(field->getText(), field->getFont(), + field->getTextColor(), field->getOutlineColor()); int w = text_image->getWidth(); auto fleur_left = frame.addImage( - SDL_Rect{ (1080 - w) / 2 - 26 - 8, y + 6, 26, 30 }, + SDL_Rect{ (image->pos.w - w) / 2 - 26 - 8, y + 6, 26, 30 }, 0xffffffff, "images/ui/Main Menus/Settings/Settings_SubHeading_Fleur00.png", (fullname + "_fleur_left").c_str() ); auto fleur_right = frame.addImage( - SDL_Rect{ (1080 + w) / 2 + 8, y + 6, 26, 30 }, + SDL_Rect{ (image->pos.w + w) / 2 + 8, y + 6, 26, 30 }, 0xffffffff, "images/ui/Main Menus/Settings/Settings_SubHeading_Fleur00.png", (fullname + "_fleur_right").c_str() @@ -1172,13 +1198,13 @@ namespace MainMenu { button->setSize(SDL_Rect{ 390, y + 4, - 98, + 158, 44}); button->setFont(smallfont_outline); button->setText(Input::inputs[0].binding(binding)); button->setJustify(Button::justify_t::CENTER); button->setCallback(callback); - button->setBackground("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonChoosing00.png"); + button->setBackground("images/ui/Main Menus/Settings/Settings_Button_Customize00.png"); button->setHighlightColor(makeColor(255,255,255,255)); button->setColor(makeColor(127,127,127,255)); button->setTextHighlightColor(makeColor(255,255,255,255)); @@ -1315,7 +1341,8 @@ namespace MainMenu { const char* tip, const std::vector& items, const char* selected, - void (*callback)(Button&)) + void (*callback)(Button&), + const std::set& grayed_items = {}) { std::string fullname = std::string("setting_") + name; int result = settingsAddOption(frame, y, name, text, tip); @@ -1342,7 +1369,11 @@ namespace MainMenu { button->addWidgetAction("MenuAlt1", "restore_defaults"); button->addWidgetAction("MenuStart", "confirm_and_exit"); for (int i = 0; i < items.size(); ++i) { - button->addWidgetAction((std::string("__") + std::to_string(i)).c_str(), items[i]); + if (grayed_items.find(i) == grayed_items.end()) { + button->addWidgetAction((std::string("__") + std::to_string(i)).c_str(), items[i]); + } else { + button->addWidgetAction((std::string("~__") + std::to_string(i)).c_str(), items[i]); + } } return result; } @@ -1448,7 +1479,7 @@ namespace MainMenu { auto slider = settings_subwindow->addSlider("scroll_slider"); slider->setBorder(24); slider->setOrientation(Slider::SLIDER_VERTICAL); - slider->setRailSize(SDL_Rect{1038, 16, 30, 440}); + slider->setRailSize(SDL_Rect{1040, 8, 30, 440}); slider->setRailImage("images/ui/Main Menus/Settings/Settings_Slider_Backing00.png"); slider->setHandleSize(SDL_Rect{0, 0, 34, 34}); slider->setHandleImage("images/ui/Main Menus/Settings/Settings_Slider_Boulder00.png"); @@ -1458,8 +1489,9 @@ namespace MainMenu { actualSize.y = slider.getValue(); frame->setActualSize(actualSize); auto railSize = slider.getRailSize(); - railSize.y = 16 + actualSize.y; + railSize.y = 8 + actualSize.y; slider.setRailSize(railSize); + slider.updateHandlePosition(); }); slider->setTickCallback([](Widget& widget){ Slider* slider = static_cast(&widget); @@ -1467,8 +1499,9 @@ namespace MainMenu { auto actualSize = frame->getActualSize(); slider->setValue(actualSize.y); auto railSize = slider->getRailSize(); - railSize.y = 16 + actualSize.y; + railSize.y = 8 + actualSize.y; slider->setRailSize(railSize); + slider->updateHandlePosition(); }); auto sliderLeft = settings_subwindow->addImage( SDL_Rect{0, 0, 30, 44}, @@ -1635,7 +1668,7 @@ namespace MainMenu { auto slider = subwindow->addSlider("scroll_slider"); slider->setBorder(24); slider->setOrientation(Slider::SLIDER_VERTICAL); - slider->setRailSize(SDL_Rect{732, 8, 30, 486}); + slider->setRailSize(SDL_Rect{724, 8, 30, 486}); slider->setRailImage("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ScrollBar00.png"); slider->setHandleSize(SDL_Rect{0, 0, 34, 34}); slider->setHandleImage("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ScrollBoulder00.png"); @@ -1647,6 +1680,7 @@ namespace MainMenu { auto railSize = slider.getRailSize(); railSize.y = 8 + actualSize.y; slider.setRailSize(railSize); + slider.updateHandlePosition(); }); slider->setTickCallback([](Widget& widget){ Slider* slider = static_cast(&widget); @@ -1656,6 +1690,7 @@ namespace MainMenu { auto railSize = slider->getRailSize(); railSize.y = 8 + actualSize.y; slider->setRailSize(railSize); + slider->updateHandlePosition(); }); auto defaults = window->addButton("restore_defaults"); @@ -1729,7 +1764,7 @@ namespace MainMenu { soundActivate(); auto window = settingsGenericWindow("bindings", "BINDINGS"); assert(window); auto subwindow = window->findFrame("subwindow"); assert(subwindow); - int y = 8; + int y = 0; static const std::vector bindings = { {Setting::Type::Binding, "Move Forward"}, @@ -1764,6 +1799,33 @@ namespace MainMenu { static int bound_player = -1; bind_mode = false; + y += settingsAddSubHeader(*subwindow, y, "bindings_header", "Profiles", true); + + y += settingsAddDropdown(*subwindow, y, "player_dropdown_button", "Player", + "Select the player whose controls you wish to customize.", + {"Player 1", "Player 2", "Player 3", "Player 4"}, + "Player 1", [](Button& button){ + soundActivate(); + settingsOpenDropdown(button, "player_dropdown", true, nullptr); + }); + + int num_controllers = (int)std::max(Input::gameControllers.size(), Input::joysticks.size()); + num_controllers = std::min(std::max(1, num_controllers + 1), 5); + std::set locked_controllers; + for (int i = num_controllers; i < 5; ++i) { + locked_controllers.emplace(i); + } + + y += settingsAddDropdown(*subwindow, y, "device_dropdown_button", "Device", + "Select a controller for the given player.", + {"KB & Mouse", "Gamepad 1", "Gamepad 2", "Gamepad 3", "Gamepad 4"}, + "KB & Mouse", [](Button& button){ + soundActivate(); + settingsOpenDropdown(button, "device_dropdown", true, nullptr); + }, locked_controllers); + + y += settingsAddSubHeader(*subwindow, y, "bindings_header", "Bindings", true); + for (auto& binding : bindings) { char tip[256]; snprintf(tip, sizeof(tip), "Bind an input device to %s", binding.name); @@ -1786,6 +1848,9 @@ namespace MainMenu { bound_binding.c_str()); tooltip->setText(buf); Input::clearDefaultBindings(); + for (auto button : subwindow->getButtons()) { + button->setDisabled(true); + } auto bindings = main_menu_frame->findFrame("bindings"); assert(bindings); auto confirm = bindings->findButton("confirm_and_exit"); assert(confirm); @@ -1804,9 +1869,15 @@ namespace MainMenu { if (Input::lastInputOfAnyKind != "Escape") { Input::inputs[bound_player].bind(bound_binding.c_str(), Input::lastInputOfAnyKind.c_str()); + } else { + Input::keys[SDL_SCANCODE_ESCAPE] = 0; } bound_button->setText(Input::inputs[bound_player].binding(bound_binding.c_str())); bound_button = nullptr; + + // fixes a bug where these inputs don't reset themselves + Input::mouseButtons[SDL_BUTTON_WHEELUP] = 0; + Input::mouseButtons[SDL_BUTTON_WHEELDOWN] = 0; } } else { if (!Input::inputs[bound_player].binary(bound_binding.c_str())) { @@ -1817,6 +1888,11 @@ namespace MainMenu { confirm->setDisabled(false); discard->setDisabled(false); defaults->setDisabled(false); + + auto subwindow = bindings->findFrame("subwindow"); assert(subwindow); + for (auto button : subwindow->getButtons()) { + button->setDisabled(false); + } auto binding = Input::inputs[bound_player].binding(bound_binding.c_str()); auto tooltip = bindings->findField("tooltip"); assert(tooltip); @@ -1833,6 +1909,12 @@ namespace MainMenu { } }); + hookSettings(*subwindow, + {{Setting::Type::Dropdown, "player_dropdown_button"}, + {Setting::Type::Dropdown, "device_dropdown_button"}, + //{Setting::Type::Field, "preset_entry"}, + bindings[0], + }); hookSettings(*subwindow, bindings); settingsSubwindowFinalize(*subwindow, y); settingsSelect(*subwindow, bindings.front()); diff --git a/src/ui/Slider.cpp b/src/ui/Slider.cpp index bbaef61d5..1999f2c88 100644 --- a/src/ui/Slider.cpp +++ b/src/ui/Slider.cpp @@ -114,9 +114,7 @@ void Slider::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vectorgetTTF(); - SDL_Color colorBlack = { 0, 0, 0, 255 }; - SDL_Color colorWhite = { 255, 255, 255, 255 }; + SDL_Color colorText; + SDL_GetRGBA(textColor, mainsurface->format, + &colorText.r, &colorText.g, &colorText.b, &colorText.a); + + SDL_Color colorOutline; + SDL_GetRGBA(outlineColor, mainsurface->format, + &colorOutline.r, &colorOutline.g, &colorOutline.b, &colorOutline.a); if (surf) { SDL_FreeSurface(surf); @@ -93,9 +119,9 @@ void Text::render() { int outlineSize = font->getOutline(); if ( outlineSize > 0 ) { TTF_SetFontOutline(ttf, outlineSize); - surf = TTF_RenderUTF8_Blended(ttf, strToRender.c_str(), colorBlack); + surf = TTF_RenderUTF8_Blended(ttf, strToRender.c_str(), colorOutline); TTF_SetFontOutline(ttf, 0); - SDL_Surface* text = TTF_RenderUTF8_Blended(ttf, strToRender.c_str(), colorWhite); + SDL_Surface* text = TTF_RenderUTF8_Blended(ttf, strToRender.c_str(), colorText); SDL_Rect rect; rect.x = outlineSize; rect.y = outlineSize; SDL_BlitSurface(text, NULL, surf, &rect); @@ -103,7 +129,7 @@ void Text::render() { } else { TTF_SetFontOutline(ttf, 0); - surf = TTF_RenderUTF8_Blended(ttf, strToRender.c_str(), colorWhite); + surf = TTF_RenderUTF8_Blended(ttf, strToRender.c_str(), colorText); } assert(surf); if ( texid == 0 ) { @@ -229,7 +255,7 @@ void Text::drawColor(const SDL_Rect _src, const SDL_Rect _dest, const SDL_Rect v static std::unordered_map hashed_text; static const int TEXT_BUDGET = 1000; -Text* Text::get(const char* str, const char* font) { +Text* Text::get(const char* str, const char* font, Uint32 textColor, Uint32 outlineColor) { if (!str) { return nullptr; } @@ -238,13 +264,22 @@ Text* Text::get(const char* str, const char* font) { } size_t len0 = strlen(str); size_t len1 = strlen(font); - char textAndFont[65536]; // better not try to render more than 64kb of text... - size_t totalLen = len0 + len1 + 2; + char textAndFont[65536] = { '\0' }; // better not try to render more than 64kb of text... + size_t totalLen = + len0 + sizeof(fontBreak) + + len1 + sizeof(fontBreak) + + 10 + sizeof(fontBreak) + + 10 + sizeof(fontBreak) + + sizeof('\0'); if (totalLen > sizeof(textAndFont)) { assert(0 && "Trying to render > 64kb of ttf text"); return nullptr; } - snprintf(textAndFont, totalLen, "%s%c%s", str, Text::fontBreak, font); + snprintf(textAndFont, sizeof(textAndFont), "%s%c%s%c%#010x%c%#010x", + str, Text::fontBreak, + font, Text::fontBreak, + textColor, Text::fontBreak, + outlineColor, Text::fontBreak); Text* text = nullptr; auto search = hashed_text.find(textAndFont); diff --git a/src/ui/Text.hpp b/src/ui/Text.hpp index a11e74032..16e9fcf4b 100644 --- a/src/ui/Text.hpp +++ b/src/ui/Text.hpp @@ -22,7 +22,7 @@ class Text { public: //! special char marks font to be used - static const char fontBreak = 8; + static const char fontBreak = '\b'; const char* getName() const { return name.c_str(); } const GLuint getTexID() const { return texid; } @@ -46,8 +46,10 @@ class Text { //! get a Text object from the engine //! @param str The Text's string //! @param font the Text's font + //! @param textColor the color of the rendered text + //! @param outlineColor the color of the rendered outline //! @return the Text or nullptr if it could not be retrieved - static Text* get(const char* str, const char* font); + static Text* get(const char* str, const char* font, Uint32 textColor, Uint32 outlineColor); //! dump engine's text cache static void dumpCache(); diff --git a/src/ui/Widget.cpp b/src/ui/Widget.cpp index 53cfcbf5f..f050a30a5 100644 --- a/src/ui/Widget.cpp +++ b/src/ui/Widget.cpp @@ -77,7 +77,7 @@ Widget* Widget::handleInput() { if (!move.second.empty()) { root = root ? root : findSearchRoot(); Widget* result = root->findWidget(move.second.c_str(), true); - if (result) { + if (result && !result->disabled && !result->invisible) { playSound(495, 64); result->scrollParent(); return result; @@ -92,7 +92,7 @@ Widget* Widget::handleInput() { if (!action.second.empty()) { root = root ? root : findSearchRoot(); Widget* result = root->findWidget(action.second.c_str(), true); - if (result) { + if (result && !result->disabled) { result->activate(); return nullptr; } @@ -101,7 +101,7 @@ Widget* Widget::handleInput() { } // activate current selection - if (input.consumeBinaryToggle("MenuConfirm")) { + if (input.consumeBinaryToggle("MenuConfirm") && !disabled) { activate(); return nullptr; } From 7df6c026499930c633d27e79a554f706832835c4 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Tue, 26 Oct 2021 00:09:25 -0700 Subject: [PATCH 20/39] rebind stuff Signed-off-by: SheridanR --- src/ui/Field.cpp | 2 +- src/ui/MainMenu.cpp | 83 ++++++++++++++++++++++++--------------------- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/src/ui/Field.cpp b/src/ui/Field.cpp index faac774b8..690d99cf4 100644 --- a/src/ui/Field.cpp +++ b/src/ui/Field.cpp @@ -146,7 +146,7 @@ void Field::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vectorheight(false) + actualFont->getOutline() * 2; + int fullH = lines * (actualFont->height(false) + actualFont->getOutline() * 2); char* buf = (char*)malloc(textlen + 1); memcpy(buf, text, textlen + 1); diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index a27b4e041..dbb255e10 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -1796,6 +1796,7 @@ namespace MainMenu { static bool bind_mode; static Button* bound_button = nullptr; static std::string bound_binding = ""; + static std::string bound_input = ""; static int bound_player = -1; bind_mode = false; @@ -1835,6 +1836,7 @@ namespace MainMenu { auto& name = std::string(button.getName()); bind_mode = true; bound_button = &button; + bound_input = button.getText(); bound_binding = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_binding_button") - 1) - (sizeof("setting_") - 1));; bound_player = 0; button.setText(". . ."); @@ -1843,7 +1845,7 @@ namespace MainMenu { auto tooltip = settings->findField("tooltip"); assert(tooltip); char buf[256]; snprintf(buf, sizeof(buf), - "Binding \"%s\". Press ESC to cancel.\n" + "Binding \"%s\". Press ESC to cancel or DEL to delete the binding.\n" "The next input you activate will be bound to this action.", bound_binding.c_str()); tooltip->setText(buf); @@ -1864,47 +1866,52 @@ namespace MainMenu { window->setTickCallback([](Widget&){ if (bind_mode) { - if (bound_button) { - if (!Input::lastInputOfAnyKind.empty()) { - if (Input::lastInputOfAnyKind != "Escape") { - Input::inputs[bound_player].bind(bound_binding.c_str(), - Input::lastInputOfAnyKind.c_str()); - } else { - Input::keys[SDL_SCANCODE_ESCAPE] = 0; - } - bound_button->setText(Input::inputs[bound_player].binding(bound_binding.c_str())); - bound_button = nullptr; - - // fixes a bug where these inputs don't reset themselves - Input::mouseButtons[SDL_BUTTON_WHEELUP] = 0; - Input::mouseButtons[SDL_BUTTON_WHEELDOWN] = 0; - } - } else { - if (!Input::inputs[bound_player].binary(bound_binding.c_str())) { - auto bindings = main_menu_frame->findFrame("bindings"); assert(bindings); - auto confirm = bindings->findButton("confirm_and_exit"); assert(confirm); - auto discard = bindings->findButton("discard_and_exit"); assert(discard); - auto defaults = bindings->findButton("restore_defaults"); assert(defaults); - confirm->setDisabled(false); - discard->setDisabled(false); - defaults->setDisabled(false); - - auto subwindow = bindings->findFrame("subwindow"); assert(subwindow); - for (auto button : subwindow->getButtons()) { - button->setDisabled(false); - } - - auto binding = Input::inputs[bound_player].binding(bound_binding.c_str()); - auto tooltip = bindings->findField("tooltip"); assert(tooltip); + if (bound_button && !Input::lastInputOfAnyKind.empty()) { + auto bindings = main_menu_frame->findFrame("bindings"); assert(bindings); + auto tooltip = bindings->findField("tooltip"); assert(tooltip); + if (Input::lastInputOfAnyKind == "Escape") { + bound_button->setText(bound_input.c_str()); + char buf[256]; + snprintf(buf, sizeof(buf), "Cancelled rebinding \"%s\"", bound_binding.c_str()); + tooltip->setText(buf); + Input::keys[SDL_SCANCODE_ESCAPE] = 0; + } else if (Input::lastInputOfAnyKind == "Delete") { + bound_button->setText(""); + char buf[256]; + snprintf(buf, sizeof(buf), "Deleted \"%s\" binding.", bound_binding.c_str()); + tooltip->setText(buf); + //Input::inputs[bound_player].bind(bound_binding.c_str(), nullptr); + } else { + bound_button->setText(Input::lastInputOfAnyKind.c_str()); char buf[256]; - snprintf(buf, sizeof(buf), "Bound \"%s\" to \"%s\"", bound_binding.c_str(), binding); + snprintf(buf, sizeof(buf), "Bound \"%s\" to \"%s\"", bound_binding.c_str(), Input::lastInputOfAnyKind.c_str()); tooltip->setText(buf); + //Input::inputs[bound_player].bind(bound_binding.c_str(), Input::lastInputOfAnyKind.c_str()); + } + bound_button = nullptr; + } + else if (!bound_button && + !Input::mouseButtons[SDL_BUTTON_LEFT] && + !Input::mouseButtons[SDL_BUTTON_WHEELDOWN] && + !Input::mouseButtons[SDL_BUTTON_WHEELUP] && + !Input::keys[SDL_SCANCODE_SPACE]) { + auto bindings = main_menu_frame->findFrame("bindings"); assert(bindings); + auto confirm = bindings->findButton("confirm_and_exit"); assert(confirm); + auto discard = bindings->findButton("discard_and_exit"); assert(discard); + auto defaults = bindings->findButton("restore_defaults"); assert(defaults); + confirm->setDisabled(false); + discard->setDisabled(false); + defaults->setDisabled(false); - Input::defaultBindings(); - bound_binding = ""; - bound_player = -1; - bind_mode = false; + auto subwindow = bindings->findFrame("subwindow"); assert(subwindow); + for (auto button : subwindow->getButtons()) { + button->setDisabled(false); } + + Input::defaultBindings(); + bound_binding = ""; + bound_player = -1; + bind_mode = false; } } }); From 5c79226c8dc2958bba3ab62cce38897b516307fa Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 26 Oct 2021 20:26:42 +1100 Subject: [PATCH 21/39] * skillsheet save work, mostly functional --- src/game.cpp | 5 + src/init_game.cpp | 1 + src/interface/bookgui.cpp | 4 +- src/interface/clickdescription.cpp | 5 + src/interface/consolecommand.cpp | 5 + src/player.cpp | 1 + src/player.hpp | 77 ++ src/ui/Field.cpp | 31 +- src/ui/Field.hpp | 1 + src/ui/GameUI.cpp | 2017 ++++++++++++++++++++++++++++ 10 files changed, 2144 insertions(+), 3 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 91eff26c2..b3c5ac8dd 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -4102,6 +4102,10 @@ void ingameHud() { players[player]->bookGUI.closeBookGUI(); } + if ( players[player]->skillSheet.bSkillSheetOpen ) + { + players[player]->skillSheet.closeSkillSheet(); + } gui_clickdrag[player] = false; //Just a catchall to make sure that any ongoing GUI dragging ends when the GUI is closed. @@ -4145,6 +4149,7 @@ void ingameHud() players[player]->characterSheet.processCharacterSheet(); players[player]->inventoryUI.updateSelectedItemAnimation(); players[player]->inventoryUI.updateInventoryItemTooltip(); + players[player]->skillSheet.processSkillSheet(); players[player]->hotbar.processHotbar(); players[player]->inventoryUI.processInventory(); players[player]->inventoryUI.updateCursor(); diff --git a/src/init_game.cpp b/src/init_game.cpp index 39485c2f9..2d48ff2ad 100644 --- a/src/init_game.cpp +++ b/src/init_game.cpp @@ -301,6 +301,7 @@ int initGame() ItemTooltips.readTooltipsFromFile(); loadHUDSettingsJSON(); + Player::SkillSheet_t::loadSkillSheetJSON(); updateLoadingScreen(94); diff --git a/src/interface/bookgui.cpp b/src/interface/bookgui.cpp index 64c6e6bd1..7d8309a7f 100644 --- a/src/interface/bookgui.cpp +++ b/src/interface/bookgui.cpp @@ -102,7 +102,7 @@ void Player::BookGUI_t::createBookGUI() "", "prompt prev img"); promptPrevPageImg->disabled = true; - std::string bookFont = "fonts/pixel_maz.ttf#16"; + std::string bookFont = "fonts/pixel_maz.ttf#14"; Field* bookLeftColumnText = bookBackground->addField("left column text", 1024); bookLeftColumnText->setText("Nothing"); const int pageWidth = BOOK_PAGE_WIDTH; @@ -114,7 +114,7 @@ void Player::BookGUI_t::createBookGUI() bookLeftColumnText->setHJustify(Field::justify_t::LEFT); bookLeftColumnText->setVJustify(Field::justify_t::TOP); bookLeftColumnText->setColor(SDL_MapRGBA(mainsurface->format, 0, 0, 0, 255)); - //bookLeftColumnText->setColor(SDL_MapRGBA(mainsurface->format, 67, 195, 157, 255)); + //bookLeftColumnText->setColor(makeColor(201, 162, 100, 255)); //bookBackground->addImage(bookLeftColumnText->getSize(), 0xFFFFFFFF, "images/system/white.png", "debug img"); diff --git a/src/interface/clickdescription.cpp b/src/interface/clickdescription.cpp index 4804ab450..36fb5eaa3 100644 --- a/src/interface/clickdescription.cpp +++ b/src/interface/clickdescription.cpp @@ -93,6 +93,11 @@ void clickDescription(int player, Entity* entity) { return; //Click falls inside the right sidebar. } + + if ( players[player]->skillSheet.bSkillSheetOpen ) + { + return; + } //int x = std::max(character_bmp->w, xres/2-inventory_bmp->w/2); //if (mouseInBounds(x,x+inventory_bmp->w,0,inventory_bmp->h)) //return NULL; diff --git a/src/interface/consolecommand.cpp b/src/interface/consolecommand.cpp index 61637334f..c6078d45b 100644 --- a/src/interface/consolecommand.cpp +++ b/src/interface/consolecommand.cpp @@ -3459,6 +3459,11 @@ void consoleCommand(char const * const command_str) loadHUDSettingsJSON(); messagePlayer(clientnum, "Reloaded HUD_settings.json"); } + else if ( !strncmp(command_str, "/loadskillsheet", 15) ) + { + Player::SkillSheet_t::loadSkillSheetJSON(); + messagePlayer(clientnum, "Reloaded skillsheet_entries.json"); + } else if ( !strncmp(command_str, "/usepaperdollmovement", 21) ) { restrictPaperDollMovement = !restrictPaperDollMovement; diff --git a/src/player.cpp b/src/player.cpp index a4866bb53..6587b4638 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1608,6 +1608,7 @@ Player::Player(int in_playernum, bool in_local_host) : hud(*this), magic(*this), characterSheet(*this), + skillSheet(*this), movement(*this), messageZone(*this), worldUI(*this), diff --git a/src/player.hpp b/src/player.hpp index 8c51b15f0..8badb8c23 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -963,6 +963,83 @@ class Player void updateCharacterInfo(); } characterSheet; + class SkillSheet_t + { + Player& player; + public: + SkillSheet_t(Player& p) : player(p) + {}; + ~SkillSheet_t() {}; + + Frame* skillFrame = nullptr; + int selectedSkill = 0; + int highlightedSkill = 0; + real_t skillsFadeInAnimationY = 0.0; + bool bSkillSheetOpen = false; + Uint32 openTick = 0; + bool bSkillSheetEntryLoaded = false; + real_t scrollPercent = 0.0; + real_t scrollInertia = 0.0; + + static struct SkillSheetData_t + { + Uint32 defaultTextColor = 0xFFFFFFFF; + Uint32 noviceTextColor = 0xFFFFFFFF; + Uint32 expertTextColor = 0xFFFFFFFF; + Uint32 legendTextColor = 0xFFFFFFFF; + struct SkillEntry_t + { + SkillEntry_t() {}; + ~SkillEntry_t() {}; + std::string name; + int skillId = -1; + std::string skillIconPath; + std::string skillIconPathLegend; + std::string statIconPath; + std::string description; + std::string legendaryDescription; + int effectStartOffsetX = 72; + int effectBackgroundOffsetX = 8; + int effectBackgroundWidth = 80; + struct SkillEffect_t + { + SkillEffect_t() {}; + ~SkillEffect_t() {}; + std::string tag; + std::string title; + std::string rawValue; + std::string value; + real_t marquee = 0.0; + Uint32 marqueeTicks = 0; + bool marqueeCompleted = false; + int effectUpdatedAtSkillLevel = -1; + }; + std::vector effects; + }; + std::vector skillEntries; + std::string iconBgPathDefault = ""; + std::string iconBgPathNovice = ""; + std::string iconBgPathExpert = ""; + std::string iconBgPathLegend = ""; + std::string iconBgSelectedPathDefault = ""; + std::string iconBgSelectedPathNovice = ""; + std::string iconBgSelectedPathExpert = ""; + std::string iconBgSelectedPathLegend = ""; + std::string highlightSkillImg = ""; + std::string selectSkillImg = ""; + std::string highlightSkillImg_Right = ""; + std::string selectSkillImg_Right = ""; + } skillSheetData; + + void selectSkill(int skill); + void createSkillSheet(); + void processSkillSheet(); + void closeSkillSheet(); + void openSkillSheet(); + void resetSkillDisplay(); + static void loadSkillSheetJSON(); + } skillSheet; + class HUD_t { Player& player; diff --git a/src/ui/Field.cpp b/src/ui/Field.cpp index 785b90865..c6f30d1e7 100644 --- a/src/ui/Field.cpp +++ b/src/ui/Field.cpp @@ -140,7 +140,7 @@ void Field::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vectorheight(false) + actualFont->getOutline() * 2; + int fullH = lines * (actualFont->height(false) + actualFont->getOutline() * 2); char* buf = (char*)malloc(textlen + 1); memcpy(buf, text, textlen + 1); @@ -447,6 +447,35 @@ std::unordered_map reflowTextLine(std::string& input, int w return result; } +std::string Field::getLongestLine() +{ + if ( text == nullptr || textlen <= 1 ) { + return ""; + } + if ( getNumTextLines() <= 1 ) + { + return text; + } + char* nexttoken; + char* token = text; + std::string originalText = text; + std::string longestLine = ""; + int longestLineWidth = 0; + do { + nexttoken = tokenize(token, "\n"); + if ( auto getText = Text::get(token, font.c_str()) ) + { + if ( getText->getWidth() > longestLineWidth ) + { + longestLineWidth = getText->getWidth(); + longestLine = token; + } + } + } while ( (token = nexttoken) != NULL ); + setText(originalText.c_str()); // make sure to replace the original text field, as tokenize will modify it + return longestLine; +} + int Field::getLastLineThatFitsWithinHeight() { if ( text == nullptr || textlen <= 1 ) { diff --git a/src/ui/Field.hpp b/src/ui/Field.hpp index 7466dc64c..e7776f27e 100644 --- a/src/ui/Field.hpp +++ b/src/ui/Field.hpp @@ -94,6 +94,7 @@ class Field : public Widget { void setGuide(const char* _guide) { guide = _guide; } void reflowTextToFit(const int characterOffset); int getLastLineThatFitsWithinHeight(); + std::string getLongestLine(); void setOntop(const bool _ontop) { ontop = _ontop; } static char* tokenize(char* str, const char* const delimiters); diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 642625f8e..2fe22365a 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -6,6 +6,7 @@ #include "Field.hpp" #include "Button.hpp" #include "Text.hpp" +#include "Slider.hpp" #include "../main.hpp" #include "../game.hpp" @@ -3112,6 +3113,204 @@ void drawCharacterPreview(const int player, SDL_Rect pos) fov = ofov; } +Player::SkillSheet_t::SkillSheetData_t Player::SkillSheet_t::skillSheetData; +void Player::SkillSheet_t::loadSkillSheetJSON() +{ + if ( !PHYSFS_getRealDir("/data/skillsheet_entries.json") ) + { + printlog("[JSON]: Error: Could not find file: data/skillsheet_entries.json"); + } + else + { + std::string inputPath = PHYSFS_getRealDir("/data/skillsheet_entries.json"); + inputPath.append("/data/skillsheet_entries.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("skills") ) + { + auto& allEntries = skillSheetData.skillEntries; + allEntries.clear(); + for ( rapidjson::Value::ConstValueIterator itr = d["skills"].Begin(); + itr != d["skills"].End(); ++itr ) + { + allEntries.push_back(SkillSheetData_t::SkillEntry_t()); + auto& entry = allEntries[allEntries.size() - 1]; + if ( (*itr).HasMember("name") ) + { + entry.name = (*itr)["name"].GetString(); + } + if ( (*itr).HasMember("id") ) + { + entry.skillId = (*itr)["id"].GetInt(); + } + if ( (*itr).HasMember("icon_base_path") ) + { + entry.skillIconPath = (*itr)["icon_base_path"].GetString(); + } + if ( (*itr).HasMember("icon_legend_path") ) + { + entry.skillIconPathLegend = (*itr)["icon_legend_path"].GetString(); + } + if ( (*itr).HasMember("icon_stat_path") ) + { + entry.statIconPath = (*itr)["icon_stat_path"].GetString(); + } + if ( (*itr).HasMember("description") ) + { + entry.description = (*itr)["description"].GetString(); + } + if ( (*itr).HasMember("legend_text") ) + { + entry.legendaryDescription = (*itr)["legend_text"].GetString(); + } + if ( (*itr).HasMember("effects") ) + { + for ( rapidjson::Value::ConstValueIterator eff_itr = (*itr)["effects"].Begin(); eff_itr != (*itr)["effects"].End(); ++eff_itr ) + { + entry.effects.push_back(SkillSheetData_t::SkillEntry_t::SkillEffect_t()); + auto& effect = entry.effects[entry.effects.size() - 1]; + effect.tag = (*eff_itr)["tag"].GetString(); + effect.title = (*eff_itr)["title"].GetString(); + effect.rawValue = (*eff_itr)["value"].GetString(); + } + } + if ( (*itr).HasMember("effects_position") ) + { + for ( rapidjson::Value::ConstMemberIterator effPos_itr = (*itr)["effects_position"].MemberBegin(); + effPos_itr != (*itr)["effects_position"].MemberEnd(); ++effPos_itr ) + { + std::string memberName = effPos_itr->name.GetString(); + if ( memberName == "effect_start_offset_x" ) + { + entry.effectStartOffsetX = effPos_itr->value.GetInt(); + } + else if ( memberName == "effect_background_offset_x" ) + { + entry.effectBackgroundOffsetX = effPos_itr->value.GetInt(); + } + else if ( memberName == "effect_background_width" ) + { + entry.effectBackgroundWidth = effPos_itr->value.GetInt(); + } + } + } + } + } + if ( d.HasMember("skill_select_images") ) + { + if ( d["skill_select_images"].HasMember("highlight_left") ) + { + skillSheetData.highlightSkillImg = d["skill_select_images"]["highlight_left"].GetString(); + } + if ( d["skill_select_images"].HasMember("selected_left") ) + { + skillSheetData.selectSkillImg = d["skill_select_images"]["selected_left"].GetString(); + } + if ( d["skill_select_images"].HasMember("highlight_right") ) + { + skillSheetData.highlightSkillImg_Right = d["skill_select_images"]["highlight_right"].GetString(); + } + if ( d["skill_select_images"].HasMember("selected_right") ) + { + skillSheetData.selectSkillImg_Right = d["skill_select_images"]["selected_right"].GetString(); + } + } + if ( d.HasMember("skill_background_icons") ) + { + if ( d["skill_background_icons"].HasMember("default") ) + { + skillSheetData.iconBgPathDefault = d["skill_background_icons"]["default"].GetString(); + } + if ( d["skill_background_icons"].HasMember("default_selected") ) + { + skillSheetData.iconBgSelectedPathDefault = d["skill_background_icons"]["default_selected"].GetString(); + } + if ( d["skill_background_icons"].HasMember("novice") ) + { + skillSheetData.iconBgPathNovice = d["skill_background_icons"]["novice"].GetString(); + } + if ( d["skill_background_icons"].HasMember("novice_selected") ) + { + skillSheetData.iconBgSelectedPathNovice = d["skill_background_icons"]["novice_selected"].GetString(); + } + if ( d["skill_background_icons"].HasMember("expert") ) + { + skillSheetData.iconBgPathExpert = d["skill_background_icons"]["expert"].GetString(); + } + if ( d["skill_background_icons"].HasMember("expert_selected") ) + { + skillSheetData.iconBgSelectedPathExpert = d["skill_background_icons"]["expert_selected"].GetString(); + } + if ( d["skill_background_icons"].HasMember("legend") ) + { + skillSheetData.iconBgPathLegend = d["skill_background_icons"]["legend"].GetString(); + } + if ( d["skill_background_icons"].HasMember("legend_selected") ) + { + skillSheetData.iconBgSelectedPathLegend = d["skill_background_icons"]["legend_selected"].GetString(); + } + } + if ( d.HasMember("colors") ) + { + if ( d["colors"].HasMember("default") ) + { + skillSheetData.defaultTextColor = SDL_MapRGBA(mainsurface->format, + d["colors"]["default"]["r"].GetInt(), + d["colors"]["default"]["g"].GetInt(), + d["colors"]["default"]["b"].GetInt(), + d["colors"]["default"]["a"].GetInt()); + } + if ( d["colors"].HasMember("novice") ) + { + skillSheetData.noviceTextColor = SDL_MapRGBA(mainsurface->format, + d["colors"]["novice"]["r"].GetInt(), + d["colors"]["novice"]["g"].GetInt(), + d["colors"]["novice"]["b"].GetInt(), + d["colors"]["novice"]["a"].GetInt()); + } + if ( d["colors"].HasMember("expert") ) + { + skillSheetData.expertTextColor = SDL_MapRGBA(mainsurface->format, + d["colors"]["expert"]["r"].GetInt(), + d["colors"]["expert"]["g"].GetInt(), + d["colors"]["expert"]["b"].GetInt(), + d["colors"]["expert"]["a"].GetInt()); + } + if ( d["colors"].HasMember("legend") ) + { + skillSheetData.legendTextColor = SDL_MapRGBA(mainsurface->format, + d["colors"]["legend"]["r"].GetInt(), + d["colors"]["legend"]["g"].GetInt(), + d["colors"]["legend"]["b"].GetInt(), + d["colors"]["legend"]["a"].GetInt()); + } + } + printlog("[JSON]: Successfully read json file %s", inputPath.c_str()); + } + } + } +} + void loadHUDSettingsJSON() { if ( !PHYSFS_getRealDir("/data/HUD_settings.json") ) @@ -6455,4 +6654,1822 @@ void doFrames() { (void)gui->process(); gui->draw(); } +} + +void Player::SkillSheet_t::createSkillSheet() +{ + if ( skillFrame ) + { + return; + } + + char name[32]; + snprintf(name, sizeof(name), "player skills %d", player.playernum); + Frame* frame = gui->addFrame(name); + skillFrame = frame; + frame->setSize(SDL_Rect{ players[player.playernum]->camera_virtualx1(), + players[player.playernum]->camera_virtualy1(), + players[player.playernum]->camera_virtualWidth(), + players[player.playernum]->camera_virtualHeight() }); + frame->setHollow(true); + frame->setBorder(0); + frame->setOwner(player.playernum); + frame->setInheritParentFrameOpacity(false); + + auto fade = skillFrame->addImage( + SDL_Rect{ 0, 0, skillFrame->getSize().w, skillFrame->getSize().h }, + 0, "images/system/white.png", "fade img"); + Uint8 r, g, b, a; + SDL_GetRGBA(fade->color, mainsurface->format, &r, &g, &b, &a); + a = 0; + fade->color = SDL_MapRGBA(mainsurface->format, r, g, b, a); + + Frame* skillBackground = frame->addFrame("skills frame"); + const int width = 684; + const int height = 404; + skillBackground->setSize(SDL_Rect{ frame->getSize().x, frame->getSize().y, width, height }); + auto bgImg = skillBackground->addImage(SDL_Rect{ 0, 0, width, height }, 0xFFFFFFFF, + "images/ui/SkillSheet/UI_Skills_Window_01.png", "skills img"); + + const int skillEntryStartY = 38; + SDL_Rect skillEntryPos{ 0, skillEntryStartY, 218, 40 }; + skillEntryPos.x = skillBackground->getSize().w - skillEntryPos.w; + SDL_Rect skillSelectorPos{ 6, 2, 176, 32 }; + Frame* allSkillEntries = skillBackground->addFrame("skill entries frame"); + allSkillEntries->setSize(SDL_Rect{ 0, 0, skillBackground->getSize().w, skillBackground->getSize().h }); + + const char* boldFont = "fonts/pixel_maz_multiline.ttf#16#2"; + const char* numberFont = "fonts/pixelmix.ttf#16"; + const char* titleFont = "fonts/pixel_maz_multiline.ttf#24#2"; + const char* descFont = "fonts/pixel_maz_multiline.ttf#16#2"; + for ( int i = 0; i < 8; ++i ) // left side + { + char skillname[32]; + snprintf(skillname, sizeof(skillname), "skill %d", i); + Frame* entry = allSkillEntries->addFrame(skillname); + entry->setSize(skillEntryPos); + entry->addImage(skillSelectorPos, 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_SkillSelector_00.png", "selector img"); + SDL_Rect imgBgPos{ skillEntryPos.w - 36, 0, 36, 36 }; + entry->addImage(imgBgPos, 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_Icons_BG00_00.png", "skill icon bg"); + SDL_Rect imgFgPos{ imgBgPos.x + 6, imgBgPos.y + 6, 24, 24 }; + entry->addImage(imgFgPos, 0xFFFFFFFF, "", "skill icon fg"); + SDL_Rect statPos{ 10, 6, 24, 24 }; + auto statIcon = entry->addImage(statPos, 0xFFFFFFFF, "", "stat icon"); + if ( i < skillSheetData.skillEntries.size() ) + { + statIcon->path = skillSheetData.skillEntries[i].statIconPath; + } + skillEntryPos.y += skillEntryPos.h; + + SDL_Rect profNamePos{ statPos.x + statPos.w + 4, 4, 98, 28 }; + Field* profName = entry->addField("skill name", 64); + profName->setSize(profNamePos); + profName->setHJustify(Field::justify_t::RIGHT); + profName->setVJustify(Field::justify_t::CENTER); + profName->setFont(boldFont); + profName->setColor(skillSheetData.defaultTextColor); + if ( i < skillSheetData.skillEntries.size() ) + { + profName->setText(skillSheetData.skillEntries[i].name.c_str()); + } + else + { + profName->setText("Default"); + } + + SDL_Rect profLevelPos{ profNamePos.x + profNamePos.w + 8, profNamePos.y, 34, 28 }; + Field* profLevel = entry->addField("skill level", 16); + profLevel->setSize(profLevelPos); + profLevel->setHJustify(Field::justify_t::RIGHT); + profLevel->setVJustify(Field::justify_t::CENTER); + profLevel->setFont(numberFont); + profLevel->setColor(skillSheetData.defaultTextColor); + profLevel->setText("0"); + } + + skillEntryPos.x = skillBackground->getSize().w - skillEntryPos.w; + skillEntryPos.x = 0; + skillEntryPos.y = skillEntryStartY; + skillSelectorPos.x = 36; + + for ( int i = 8; i < 16; ++i ) // right side + { + char skillname[32]; + snprintf(skillname, sizeof(skillname), "skill %d", i); + Frame* entry = allSkillEntries->addFrame(skillname); + entry->setSize(skillEntryPos); + entry->addImage(skillSelectorPos, 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_SkillSelectorR_00.png", "selector img"); + SDL_Rect imgBgPos{ 0, 0, 36, 36 }; + entry->addImage(imgBgPos, 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_Icons_BG00_00.png", "skill icon bg"); + SDL_Rect imgFgPos{ imgBgPos.x + 6, imgBgPos.y + 6, 24, 24 }; + entry->addImage(imgFgPos, 0xFFFFFFFF, "images/ui/SkillSheet/icons/Alchemy01.png", "skill icon fg"); + SDL_Rect statPos{ skillEntryPos.w - 10 - 24, 6, 24, 24 }; + auto statIcon = entry->addImage(statPos, 0xFFFFFFFF, "", "stat icon"); + if ( i < skillSheetData.skillEntries.size() ) + { + statIcon->path = skillSheetData.skillEntries[i].statIconPath; + } + skillEntryPos.y += skillEntryPos.h; + + SDL_Rect profNamePos{ statPos.x - 4 - 98, 4, 98, 28 }; + Field* profName = entry->addField("skill name", 64); + profName->setSize(profNamePos); + profName->setHJustify(Field::justify_t::LEFT); + profName->setVJustify(Field::justify_t::CENTER); + profName->setFont(boldFont); + profName->setColor(skillSheetData.defaultTextColor); + if ( i < skillSheetData.skillEntries.size() ) + { + profName->setText(skillSheetData.skillEntries[i].name.c_str()); + } + else + { + profName->setText("Default"); + } + + SDL_Rect profLevelPos{ profNamePos.x - 12 - 34, profNamePos.y, 34, 28 }; + Field* profLevel = entry->addField("skill level", 16); + profLevel->setSize(profLevelPos); + profLevel->setHJustify(Field::justify_t::RIGHT); + profLevel->setVJustify(Field::justify_t::CENTER); + profLevel->setFont(numberFont); + profLevel->setColor(skillSheetData.defaultTextColor); + profLevel->setText("0"); + } + + SDL_Rect skillTitlePos{ skillBackground->getSize().w / 2 - 320 / 2, 16, 320, 40 }; + auto skillTitleTxt = skillBackground->addField("skill title txt", 64); + skillTitleTxt->setHJustify(Field::justify_t::CENTER); + skillTitleTxt->setVJustify(Field::justify_t::CENTER); + skillTitleTxt->setText(""); + skillTitleTxt->setSize(skillTitlePos); + skillTitleTxt->setFont(titleFont); + skillTitleTxt->setColor(makeColor(201, 162, 100, 255)); + + SDL_Rect descPos{ 8, 64, 320, 324 }; + descPos.x = skillBackground->getSize().w / 2 - descPos.w / 2; + auto skillDescriptionFrame = skillBackground->addFrame("skill desc frame"); + skillDescriptionFrame->setSize(descPos); + + /*auto debugRect = skillDescriptionFrame->addImage(SDL_Rect{ 0, 0, descPos.w, descPos.h }, 0xFFFFFFFF, + "images/system/white.png", "")*/; + + auto slider = skillDescriptionFrame->addSlider("skill slider"); + slider->setMinValue(0); + slider->setMaxValue(100); + slider->setValue(0); + SDL_Rect sliderPos{ descPos.w - 4 - 30, 4, 30, descPos.h - 8 }; + slider->setRailSize(SDL_Rect{ sliderPos }); + slider->setHandleSize(SDL_Rect{ 0, 0, 34, 34 }); + slider->setOrientation(Slider::SLIDER_VERTICAL); + //slider->setCallback(callback); + slider->setColor(makeColor(127, 127, 127, 255)); + slider->setHighlightColor(makeColor(255, 255, 255, 255)); + slider->setHandleImage("images/ui/Main Menus/Settings/Settings_Slider_Boulder00.png"); + slider->setRailImage("images/ui/Main Menus/Settings/Settings_Slider_Backing00.png"); + + Font* actualFont = Font::get(descFont); + int fontHeight; + actualFont->sizeText("_", nullptr, &fontHeight); + + auto scrollAreaOuterFrame = skillDescriptionFrame->addFrame("scroll area outer frame"); + scrollAreaOuterFrame->setSize(SDL_Rect{ 16, 4, sliderPos.x - 4 - 16, descPos.h - 8 }); + auto scrollAreaFrame = scrollAreaOuterFrame->addFrame("skill scroll area"); + scrollAreaFrame->setSize(SDL_Rect{ 0, 0, scrollAreaOuterFrame->getSize().w, 1000 }); + + SDL_Rect txtPos{ 0, 0, scrollAreaFrame->getSize().w, scrollAreaFrame->getSize().h }; + { + auto skillLvlHeaderTxt = scrollAreaFrame->addField("skill lvl header txt", 128); + skillLvlHeaderTxt->setFont(descFont); + skillLvlHeaderTxt->setSize(txtPos); + skillLvlHeaderTxt->setText("Skill Level:"); + + auto skillLvlHeaderVal = scrollAreaFrame->addField("skill lvl header val", 128); + skillLvlHeaderVal->setFont(descFont); + skillLvlHeaderVal->setSize(txtPos); + skillLvlHeaderVal->setText(""); + skillLvlHeaderVal->setHJustify(Field::justify_t::RIGHT); + + txtPos.y += fontHeight; + } + { + const int iconSize = 24; + auto statIcon = scrollAreaFrame->addImage( + SDL_Rect{ txtPos.x + txtPos.w - iconSize, txtPos.y, iconSize, iconSize }, + 0xFFFFFFFF, "images/ui/CharSheet/HUD_CharSheet_DEX_00.png", "stat icon"); + + txtPos.y += std::max(0, iconSize - fontHeight); + auto statHeaderTxt = scrollAreaFrame->addField("stat header txt", 128); + statHeaderTxt->setFont(descFont); + statHeaderTxt->setSize(txtPos); + statHeaderTxt->setText("Associated Stat:"); + + auto statTypeTxt = scrollAreaFrame->addField("stat type txt", 128); + statTypeTxt->setFont(descFont); + txtPos.w -= iconSize + 4; + statTypeTxt->setSize(txtPos); + txtPos.w += iconSize + 4; + statTypeTxt->setText(""); + statTypeTxt->setHJustify(Field::justify_t::RIGHT); + txtPos.y += fontHeight; + txtPos.y += fontHeight / 2; + } + { + auto skillDescriptionTxt = scrollAreaFrame->addField("skill desc txt", 1024); + skillDescriptionTxt->setFont(descFont); + skillDescriptionTxt->setSize(txtPos); + skillDescriptionTxt->setText(""); + + int txtHeight = skillDescriptionTxt->getNumTextLines() * actualFont->height(true); + + txtPos.y += txtHeight; + txtPos.y += fontHeight / 2; + + } + { + char effectFrameName[64] = ""; + char effectFieldName[64] = ""; + char effectFieldVal[64] = ""; + char effectBgName[64]; + int effectXOffset = 72; // tmp paramters - configured in skillsheet json + int effectBackgroundXOffset = 8; + int effectBackgroundWidth = 80; + for ( int i = 0; i < 10; ++i ) + { + snprintf(effectFrameName, sizeof(effectFrameName), "effect %d frame", i); + auto effectFrame = scrollAreaFrame->addFrame(effectFrameName); + effectFrame->setSize(SDL_Rect{ txtPos.x, txtPos.y - 2, txtPos.w, fontHeight + 8 }); + effectFrame->addImage( + SDL_Rect{ 0, 0, effectFrame->getSize().w, effectFrame->getSize().h - 4 }, + makeColor(101, 87, 67, 255), "images/system/white.png", "effect frame bg highlight"); + effectFrame->addImage( + SDL_Rect{ 0, 0, effectFrame->getSize().w, effectFrame->getSize().h - 4 }, + makeColor(101, 33, 33, 28), "images/system/white.png", "effect frame bg tmp"); + int valueX = effectFrame->getSize().w - effectXOffset; + + auto valBgImgFrame = effectFrame->addFrame("effect val bg frame"); + valBgImgFrame->setSize(SDL_Rect{ valueX - effectBackgroundXOffset, 0, effectBackgroundWidth, effectFrame->getSize().h - 4}); + { + Frame::image_t* tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_TL00.png", "effect bg top left"); + if ( tmp ) + { + tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); + tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); + } + tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_TR00.png", "effect bg top right"); + if ( tmp ) + { + tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); + tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); + } + tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_T00.png", "effect bg top middle"); + if ( tmp ) + { + tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); + tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); + } + tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_L00.png", "effect bg left"); + if ( tmp ) + { + tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); + tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); + } + tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_R00.png", "effect bg right"); + if ( tmp ) + { + tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); + tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); + } + tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_M00.png", "effect bg middle"); + if ( tmp ) + { + tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); + tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); + } + tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_BL00.png", "effect bg bottom left"); + if ( tmp ) + { + tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); + tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); + } + tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_BR00.png", "effect bg bottom right"); + if ( tmp ) + { + tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); + tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); + } + tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_B00.png", "effect bg bottom middle"); + if ( tmp ) + { + tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); + tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); + } + } + + auto effectTxtFrame = effectFrame->addFrame("effect txt frame"); + effectTxtFrame->setSize(SDL_Rect{ 0, 0, effectFrame->getSize().w - effectXOffset - effectBackgroundXOffset, effectFrame->getSize().h - 4 }); + auto effectTxt = effectTxtFrame->addField("effect txt", 128); + effectTxt->setFont(descFont); + effectTxt->setSize(SDL_Rect{0, 0, 1000, effectTxtFrame->getSize().h}); // large 1000px to handle large text length marquee + effectTxt->setVJustify(Field::justify_t::CENTER); + effectTxt->setText(""); + effectTxt->setColor(makeColor(201, 162, 100, 255)); + + auto effectValFrame = effectFrame->addFrame("effect val frame"); + effectValFrame->setSize(SDL_Rect{ valueX, 0, effectXOffset, effectFrame->getSize().h - 4 }); + auto effectVal = effectValFrame->addField("effect val", 128); + effectVal->setFont(descFont); + effectVal->setSize(SDL_Rect{ 0, 0, 1000, effectValFrame->getSize().h }); // large 1000px to handle large text length marquee + effectVal->setVJustify(Field::justify_t::CENTER); + effectVal->setText(""); + effectVal->setColor(makeColor(201, 162, 100, 255)); + + //effectFrame->setDisabled(true); + + txtPos.y += effectFrame->getSize().h; + } + } + { + auto legendDivImg = scrollAreaFrame->addImage(SDL_Rect{ 0, txtPos.y, 212, 28 }, 0xFFFFFFFF, + "images/ui/SkillSheet/UI_Skills_Separator_00.png", "legend div"); + legendDivImg->pos.x = scrollAreaFrame->getSize().w / 2 - legendDivImg->pos.w / 2; + auto legendDivTxt = scrollAreaFrame->addField("legend div text", 128); + legendDivTxt->setText(language[4056]); + SDL_Rect legendDivTxtPos = legendDivImg->pos; + legendDivTxt->setSize(legendDivTxtPos); + legendDivTxt->setFont(descFont); + legendDivTxt->setHJustify(Field::justify_t::CENTER); + + auto legendFrame = scrollAreaFrame->addFrame("legend frame"); + SDL_Rect legendPos{ 0, txtPos.y, txtPos.w, 100 }; + legendFrame->setSize(legendPos); + auto tl = legendFrame->addImage(SDL_Rect{ 0, 0, 18, 18 }, 0xFFFFFFFF, + "images/ui/SkillSheet/UI_Skills_LegendBox_TL_00.png", "top left img"); + auto tm = legendFrame->addImage(SDL_Rect{ 18, 0, legendPos.w - 18 * 2, 18 }, 0xFFFFFFFF, + "images/ui/SkillSheet/UI_Skills_LegendBox_T_00.png", "top img"); + auto tr = legendFrame->addImage(SDL_Rect{ legendPos.w - 18, 0, 18, 18 }, 0xFFFFFFFF, + "images/ui/SkillSheet/UI_Skills_LegendBox_TR_00.png", "top right img"); + + auto ml = legendFrame->addImage(SDL_Rect{ 0, 18, 18, legendPos.h - 18 * 2 }, 0xFFFFFFFF, + "images/ui/SkillSheet/UI_Skills_LegendBox_ML_00.png", "middle left img"); + auto mm = legendFrame->addImage(SDL_Rect{ 18, 18, legendPos.w - 18 * 2, ml->pos.h }, 0xFFFFFFFF, + "images/ui/SkillSheet/UI_Skills_LegendBox_M_00.png", "middle img"); + auto mr = legendFrame->addImage(SDL_Rect{ legendPos.w - 18, 18, 18, ml->pos.h }, 0xFFFFFFFF, + "images/ui/SkillSheet/UI_Skills_LegendBox_MR_00.png", "middle right img"); + + auto bl = legendFrame->addImage(SDL_Rect{ 0, ml->pos.y + ml->pos.h, 18, 18 }, 0xFFFFFFFF, + "images/ui/SkillSheet/UI_Skills_LegendBox_BL_00.png", "bottom left img"); + auto bm = legendFrame->addImage(SDL_Rect{ 18, bl->pos.y, legendPos.w - 18 * 2, 18 }, 0xFFFFFFFF, + "images/ui/SkillSheet/UI_Skills_LegendBox_B_00.png", "bottom img"); + auto br = legendFrame->addImage(SDL_Rect{ legendPos.w - 18, bl->pos.y, 18, 18 }, 0xFFFFFFFF, + "images/ui/SkillSheet/UI_Skills_LegendBox_BR_00.png", "bottom right img"); + + auto legendText = legendFrame->addField("legend text", 256); + legendText->setText(""); + legendText->setSize(mm->pos); + legendText->setFont(descFont); + legendText->setHJustify(Field::justify_t::CENTER); + } + /*auto debugRect = skillDescriptionFrame->addImage(txtPos, 0xFFFFFFFF, + "images/system/white.png", "");*/ +} + +void Player::SkillSheet_t::resetSkillDisplay() +{ + bSkillSheetEntryLoaded = false; + scrollPercent = 0.0; + scrollInertia = 0.0; + + if ( skillFrame ) + { + auto innerFrame = skillFrame->findFrame("skills frame"); + auto skillDescriptionFrame = innerFrame->findFrame("skill desc frame"); + auto slider = skillDescriptionFrame->findSlider("skill slider"); + slider->setValue(0.0); + } + + for ( auto& skillEntry : skillSheetData.skillEntries ) + { + for ( auto& skillEffect : skillEntry.effects ) + { + skillEffect.marqueeTicks = 0; + skillEffect.marquee = 0.0; + } + } +} + +void Player::SkillSheet_t::closeSkillSheet() +{ + bSkillSheetOpen = false; + if ( skillFrame ) + { + skillFrame->setDisabled(true); + } + resetSkillDisplay(); +} + +void Player::SkillSheet_t::openSkillSheet() +{ + players[player.playernum]->openStatusScreen(GUI_MODE_INVENTORY, + INVENTORY_MODE_ITEM, player.GUI.MODULE_SKILLS_LIST); // Reset the GUI to the inventory. + bSkillSheetOpen = true; + openTick = ticks; + resetSkillDisplay(); + if ( selectedSkill < 0 ) + { + selectSkill(0); + } +} + +std::string formatSkillSheetEffects(int playernum, int proficiency, std::string& tag, std::string& rawValue) +{ + char buf[128] = ""; + if ( !players[playernum] ) { return ""; } + Entity* player = players[playernum]->entity; + real_t val = 0.0; + real_t val2 = 0.0; + if ( proficiency == PRO_STEALTH ) + { + if ( tag == "STEALTH_VIS_REDUCTION" ) + { + val = stats[playernum]->PROFICIENCIES[proficiency] * 2 * 100 / 512.f; // % visibility reduction + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "STEALTH_SNEAK_VIS" ) + { + val = (2 + (stats[playernum]->PROFICIENCIES[proficiency] / 40)); // night vision when sneaking + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "STEALTH_BACKSTAB" ) + { + val = (stats[playernum]->PROFICIENCIES[proficiency] / 20 + 2) * 2; // backstab dmg + if ( skillCapstoneUnlocked(playernum, proficiency) ) + { + val *= 2; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "STEALTH_CURRENT_VIS" ) + { + if ( player ) + { + val = player->entityLightAfterReductions(*stats[playernum], nullptr); + val = std::max(1, (static_cast(val / 32.0))); // general visibility + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + } + return buf; + } + else if ( proficiency == PRO_RANGED ) + { + if ( tag == "RANGED_DMG_RANGE" ) + { + val = 100 - (100 - stats[playernum]->PROFICIENCIES[proficiency]) / 2.f; // lowest damage roll + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "RANGED_DEGRADE_CHANCE" ) + { + val = 50 + static_cast(stats[playernum]->PROFICIENCIES[proficiency] / 20) * 10; + if ( stats[playernum]->type == GOBLIN ) + { + val += 20; + if ( stats[playernum]->PROFICIENCIES[proficiency] < SKILL_LEVEL_LEGENDARY ) + { + val = std::min(val, 90.0); + } + } + if ( skillCapstoneUnlocked(playernum, proficiency) ) + { + val = 0.0; + } + if ( val > 0.0001 ) + { + val = 100 / val; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "RANGED_THROWN_DMG" ) + { + int skillLVL = stats[playernum]->PROFICIENCIES[proficiency] / 20; // thrown dmg bonus + val = 100 * thrownDamageSkillMultipliers[std::min(skillLVL, 5)]; + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "RANGED_PIERCE_CHANCE" ) + { + if ( player ) + { + val = std::min(std::max(player->getPER() / 2, 0), 50); + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + } + return buf; + } + else if ( proficiency == PRO_SHIELD ) + { + if ( tag == "BLOCK_AC_INCREASE" ) + { + val = 5 + static_cast(stats[playernum]->PROFICIENCIES[proficiency] / 5); + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "BLOCK_DEGRADE_NORMAL_CHANCE" ) + { + val = 25 + (stats[playernum]->type == GOBLIN ? 10 : 0); // degrade > 0 dmg taken + val += (static_cast(stats[playernum]->PROFICIENCIES[proficiency] / 10)); + if ( skillCapstoneUnlocked(playernum, proficiency) ) + { + val = 0.0; + } + if ( val > 0.0001 ) + { + val = 100 / val; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), val); + } + else if ( tag == "BLOCK_DEGRADE_DEFENDING_CHANCE" ) + { + val = 10 + (stats[playernum]->type == GOBLIN ? 10 : 0); // degrade on 0 dmg + val += (static_cast(stats[playernum]->PROFICIENCIES[proficiency] / 10)); + if ( svFlags & SV_FLAG_HARDCORE ) + { + val = 40 + (stats[playernum]->type == GOBLIN ? 10 : 0); + } + if ( skillCapstoneUnlocked(playernum, proficiency) ) + { + val = 0.0; + } + if ( val > 0.0001 ) + { + val = 100 / val; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), val); + } + return buf; + } + else if ( proficiency == PRO_UNARMED ) + { + if ( tag == "UNARMED_DMG_RANGE" ) + { + val = 100 - (100 - stats[playernum]->PROFICIENCIES[proficiency]) / 2.f; // lowest damage roll + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "UNARMED_BONUS_DMG" ) + { + val = static_cast(stats[playernum]->PROFICIENCIES[proficiency] / 20); + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "GLOVE_DEGRADE_CHANCE" ) + { + val = 100 + (stats[playernum]->type == GOBLIN ? 20 : 0); // chance to degrade on > 0 dmg + if ( svFlags & SV_FLAG_HARDCORE ) + { + val *= 2; + } + if ( skillCapstoneUnlocked(playernum, proficiency) ) + { + val = 0.0; + } + if ( val > 0.0001 ) + { + val = 100 / val; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "GLOVE_DEGRADE0_CHANCE" ) + { + val = 8 + (stats[playernum]->type == GOBLIN ? 4 : 0); // chance to degrade on 0 dmg + if ( svFlags & SV_FLAG_HARDCORE ) + { + val *= 2; + } + if ( skillCapstoneUnlocked(playernum, proficiency) ) + { + val = 0.0; + } + if ( val > 0.0001 ) + { + val = 100 / val; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "UNARMED_KNOCKBACK_DIST" ) + { + val = static_cast(stats[playernum]->PROFICIENCIES[proficiency] / 20) * 20; + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + return buf; + } + else if ( proficiency == PRO_SWORD ) + { + if ( tag == "SWORD_DMG_RANGE" ) + { + if ( proficiency == PRO_POLEARM ) + { + val = 100 - (100 - stats[playernum]->PROFICIENCIES[proficiency]) / 3.f; // lowest damage roll + } + else + { + val = 100 - (100 - stats[playernum]->PROFICIENCIES[proficiency]) / 2.f; // lowest damage roll + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "SWORD_DEGRADE_CHANCE" ) + { + val = 50 + (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; + } + if ( skillCapstoneUnlocked(playernum, proficiency) ) + { + val = 0.0; + } + if ( val > 0.0001 ) + { + val = 100 / val; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "SWORD_DEGRADE0_CHANCE" ) + { + val = 4 + (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; + } + if ( skillCapstoneUnlocked(playernum, proficiency) ) + { + val = 0.0; + } + if ( val > 0.0001 ) + { + val = 100 / val; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + return buf; + } + else if ( proficiency == PRO_POLEARM ) + { + if ( tag == "POLEARM_DMG_RANGE" ) + { + if ( proficiency == PRO_POLEARM ) + { + val = 100 - (100 - stats[playernum]->PROFICIENCIES[proficiency]) / 3.f; // lowest damage roll + } + else + { + val = 100 - (100 - stats[playernum]->PROFICIENCIES[proficiency]) / 2.f; // lowest damage roll + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "POLEARM_DEGRADE_CHANCE" ) + { + val = 50 + (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; + } + if ( skillCapstoneUnlocked(playernum, proficiency) ) + { + val = 0.0; + } + if ( val > 0.0001 ) + { + val = 100 / val; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "POLEARM_DEGRADE0_CHANCE" ) + { + val = 4 + (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; + } + if ( skillCapstoneUnlocked(playernum, proficiency) ) + { + val = 0.0; + } + if ( val > 0.0001 ) + { + val = 100 / val; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + return buf; + } + else if ( proficiency == PRO_AXE ) + { + if ( tag == "AXE_DMG_RANGE" ) + { + if ( proficiency == PRO_POLEARM ) + { + val = 100 - (100 - stats[playernum]->PROFICIENCIES[proficiency]) / 3.f; // lowest damage roll + } + else + { + val = 100 - (100 - stats[playernum]->PROFICIENCIES[proficiency]) / 2.f; // lowest damage roll + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "AXE_DEGRADE_CHANCE" ) + { + val = 50 + (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; + } + if ( skillCapstoneUnlocked(playernum, proficiency) ) + { + val = 0.0; + } + if ( val > 0.0001 ) + { + val = 100 / val; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "AXE_DEGRADE0_CHANCE" ) + { + val = 4 + (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; + } + if ( skillCapstoneUnlocked(playernum, proficiency) ) + { + val = 0.0; + } + if ( val > 0.0001 ) + { + val = 100 / val; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + return buf; + } + else if ( proficiency == PRO_MACE ) + { + if ( tag == "MACE_DMG_RANGE" ) + { + if ( proficiency == PRO_POLEARM ) + { + val = 100 - (100 - stats[playernum]->PROFICIENCIES[proficiency]) / 3.f; // lowest damage roll + } + else + { + val = 100 - (100 - stats[playernum]->PROFICIENCIES[proficiency]) / 2.f; // lowest damage roll + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "MACE_DEGRADE_CHANCE" ) + { + val = 50 + (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; + } + if ( skillCapstoneUnlocked(playernum, proficiency) ) + { + val = 0.0; + } + if ( val > 0.0001 ) + { + val = 100 / val; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "MACE_DEGRADE0_CHANCE" ) + { + val = 4 + (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; + } + if ( skillCapstoneUnlocked(playernum, proficiency) ) + { + val = 0.0; + } + if ( val > 0.0001 ) + { + val = 100 / val; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + return buf; + } + else if ( proficiency == PRO_SWIMMING ) + { + if ( tag == "SWIM_SPEED" ) + { + val = (((stats[playernum]->PROFICIENCIES[proficiency] / 100.f) * 50.f) + 50); // water movement speed + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + return buf; + } + else if ( proficiency == PRO_LEADERSHIP ) + { + if ( tag == "LEADER_MAX_FOLLOWERS" ) + { + val = std::min(8, std::max(4, 2 * (stats[playernum]->PROFICIENCIES[proficiency] / 20))); // max followers + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "LEADER_FOLLOWER_SPEED" ) + { + val = 1 + (stats[playernum]->PROFICIENCIES[proficiency] / 20); + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "LEADER_CHARM_MONSTER" ) + { + val = 80 + ((statGetCHR(stats[playernum], player) + stats[playernum]->PROFICIENCIES[proficiency]) / 20) * 10; + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "LIST_LEADER_AVAILABLE_FOLLOWERS" ) + { + snprintf(buf, sizeof(buf), rawValue.c_str(), " - TODO\n - TODO\n - TODO"); + } + return buf; + } + else if ( proficiency == PRO_TRADING ) + { + if ( tag == "TRADING_BUY_PRICE" ) + { + val = 1 / ((50 + stats[playernum]->PROFICIENCIES[proficiency]) / 150.f); // buy value + snprintf(buf, sizeof(buf), rawValue.c_str(), val); + } + else if ( tag == "TRADING_SELL_PRICE" ) + { + val = (50 + stats[playernum]->PROFICIENCIES[proficiency]) / 150.f; // sell value + snprintf(buf, sizeof(buf), rawValue.c_str(), val); + } + return buf; + } + else if ( proficiency == PRO_APPRAISAL ) + { + if ( tag == "APPRAISE_GOLD_SPEED" ) + { + val = (60.f / (stats[playernum]->PROFICIENCIES[proficiency] + 1)) / (TICKS_PER_SECOND); // appraisal time per gold value + snprintf(buf, sizeof(buf), rawValue.c_str(), val); + } + else if ( tag == "APPRAISE_MAX_GOLD_VALUE" ) + { + val = 10 * (stats[playernum]->PROFICIENCIES[proficiency] + (statGetPER(stats[playernum], player) * 5)); // max gold value can appraise + if ( val < 0.1 ) + { + val = 9; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "APPRAISE_WORTHLESS_GLASS" ) + { + if ( (stats[playernum]->PROFICIENCIES[proficiency] + (statGetPER(stats[playernum], player) * 5)) >= 100 ) + { + snprintf(buf, sizeof(buf), rawValue.c_str(), language[1314]); // yes + } + else + { + snprintf(buf, sizeof(buf), rawValue.c_str(), language[1315]); // no + } + } + return buf; + } + else if ( proficiency == PRO_LOCKPICKING ) + { + Sint32 PER = statGetPER(stats[playernum], player); + if ( tag == "TINKERING_LOCKPICK_CHESTS_DOORS" ) + { + val = stats[playernum]->PROFICIENCIES[proficiency] / 2.f; // lockpick chests/doors + if ( stats[playernum]->PROFICIENCIES[proficiency] == SKILL_LEVEL_LEGENDARY ) + { + val = 100.f; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "TINKERING_SCRAP_CHESTS" ) + { + val = std::min(100.f, stats[playernum]->PROFICIENCIES[proficiency] + 50.f); + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "TINKERING_SCRAP_AUTOMATONS" ) + { + if ( stats[playernum]->PROFICIENCIES[proficiency] >= SKILL_LEVEL_EXPERT ) + { + val = 100.f; // lockpick automatons + } + else + { + val = (100 - 100 / (static_cast(stats[playernum]->PROFICIENCIES[proficiency] / 20 + 1))); // lockpick automatons + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "TINKERING_DISARM_ARROWS" ) + { + val = (100 - 100 / (std::max(1, static_cast(stats[playernum]->PROFICIENCIES[proficiency] / 10)))); // disarm arrow traps + if ( stats[playernum]->PROFICIENCIES[proficiency] < SKILL_LEVEL_BASIC ) + { + val = 0.f; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "TINKERING_KIT_SCRAP_BONUS" ) + { + // bonus scrapping chances. + int skillLVL = std::min(5, static_cast((stats[playernum]->PROFICIENCIES[proficiency] + PER) / 20)); + skillLVL = std::max(0, skillLVL); + switch ( skillLVL ) + { + case 5: + val = 150.f; + break; + case 4: + val = 125.f; + break; + case 3: + val = 50.f; + break; + case 2: + val = 25.f; + break; + case 1: + val = 12.5; + break; + default: + val = 0.f; + break; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "TINKERING_KIT_REPAIR_ITEM" ) + { + std::string canRepairItems = language[4057]; // none + if ( (stats[playernum]->PROFICIENCIES[proficiency] + PER + (stats[playernum]->type == AUTOMATON ? 20 : 0)) >= SKILL_LEVEL_LEGENDARY ) + { + canRepairItems = language[4058]; // all + } + else if ( (stats[playernum]->PROFICIENCIES[proficiency] + PER + (stats[playernum]->type == AUTOMATON ? 20 : 0)) >= SKILL_LEVEL_MASTER ) + { + canRepairItems = language[4059]; // 2/0 + } + else if ( (stats[playernum]->PROFICIENCIES[proficiency] + PER + (stats[playernum]->type == AUTOMATON ? 20 : 0)) >= SKILL_LEVEL_EXPERT ) + { + canRepairItems = language[4060]; // 1/0 + } + snprintf(buf, sizeof(buf), rawValue.c_str(), canRepairItems.c_str()); + } + else if ( tag == "TINKERING_MAX_ALLIES" ) + { + val = maximumTinkeringBotsCanBeDeployed(stats[playernum]); + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + return buf; + } + else if ( proficiency == PRO_ALCHEMY ) + { + if ( tag == "ALCHEMY_POTION_EFFECT_DMG" ) + { + int skillLVL = stats[playernum]->PROFICIENCIES[proficiency] / 20; + val = 100 * potionDamageSkillMultipliers[std::min(skillLVL, 5)]; + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "ALCHEMY_THROWN_IMPACT_DMG" ) + { + int skillLVL = stats[playernum]->PROFICIENCIES[proficiency] / 20; + val = 100 * potionDamageSkillMultipliers[std::min(skillLVL, 5)]; + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "ALCHEMY_DUPLICATION_CHANCE" ) + { + val = 50.f + static_cast(stats[playernum]->PROFICIENCIES[proficiency] / 20) * 10; + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "ALCHEMY_EMPTY_BOTTLE_CONSUME" ) + { + val = std::min(80, (60 + static_cast(stats[playernum]->PROFICIENCIES[proficiency] / 20) * 10)); + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "ALCHEMY_EMPTY_BOTTLE_BREW" ) + { + val = 50.f + static_cast(stats[playernum]->PROFICIENCIES[proficiency] / 20) * 5; + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "ALCHEMY_LEARNT_INGREDIENTS" ) + { + snprintf(buf, sizeof(buf), rawValue.c_str(), " - TODO\n - TODO\n - TODO"); + } + return buf; + } + else if ( proficiency == PRO_SPELLCASTING ) + { + if ( tag == "CASTING_MP_REGEN" ) + { + if ( player ) + { + val = player->getManaRegenInterval(*(stats[playernum])) / (TICKS_PER_SECOND * 1.f); + } + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "CASTING_BEGINNER" ) + { + if ( isSpellcasterBeginner(playernum, player) ) + { + snprintf(buf, sizeof(buf), rawValue.c_str(), language[1314]); // yes + } + else + { + snprintf(buf, sizeof(buf), rawValue.c_str(), language[1315]); // no + } + } + else if ( tag == "CASTING_SPELLBOOK_FUMBLE" ) + { + int skillLVL = std::min(std::max(0, stats[playernum]->PROFICIENCIES[proficiency] + statGetINT(stats[playernum], player)), 100); + skillLVL /= 20; + std::string tierName = language[4061]; + tierName += " "; + if ( skillLVL == 0 ) + { + tierName += "I"; + } + else if ( skillLVL == 1 ) + { + tierName += "II"; + } + else if ( skillLVL == 2 ) + { + tierName += "III"; + } + else if ( skillLVL == 3 ) + { + tierName += "IV"; + } + else if ( skillLVL == 4 ) + { + tierName += "V"; + } + else if ( skillLVL >= 5 ) + { + tierName += "VI"; + } + snprintf(buf, sizeof(buf), rawValue.c_str(), tierName.c_str()); + } + return buf; + } + else if ( proficiency == PRO_MAGIC ) + { + /*if ( tag == "CASTING_MP_REGEN" ) + { + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "CASTING_BEGINNER" ) + { + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + } + else if ( tag == "CASTING_SPELLBOOK_FUMBLE" ) + { + snprintf(buf, sizeof(buf), rawValue.c_str(), (int)val); + }*/ + } + + return ""; +} + +void Player::SkillSheet_t::selectSkill(int skill) +{ + selectedSkill = skill; + bSkillSheetEntryLoaded = false; + openTick = ticks; + resetSkillDisplay(); +} + +void Player::SkillSheet_t::processSkillSheet() +{ + if ( !skillFrame ) + { + createSkillSheet(); + } + + if ( keystatus[SDL_SCANCODE_K] ) + { + if ( !bSkillSheetOpen ) + { + openSkillSheet(); + } + else + { + closeSkillSheet(); + } + keystatus[SDL_SCANCODE_K] = 0; + } + + if ( !bSkillSheetOpen ) + { + skillFrame->setDisabled(true); + + auto innerFrame = skillFrame->findFrame("skills frame"); + SDL_Rect pos = innerFrame->getSize(); + skillsFadeInAnimationY = 0.0; + pos.y = -pos.h; + innerFrame->setSize(pos); + + auto fade = skillFrame->findImage("fade img"); + Uint8 r, g, b, a; + SDL_GetRGBA(fade->color, mainsurface->format, &r, &g, &b, &a); + a = 0; + fade->color = SDL_MapRGBA(mainsurface->format, r, g, b, a); + return; + } + + skillFrame->setDisabled(false); + skillFrame->setSize(SDL_Rect{ players[player.playernum]->camera_virtualx1(), + players[player.playernum]->camera_virtualy1(), + players[player.playernum]->camera_virtualWidth(), + players[player.playernum]->camera_virtualHeight() }); + + auto innerFrame = skillFrame->findFrame("skills frame"); + SDL_Rect sheetSize = innerFrame->getSize(); + sheetSize.x = skillFrame->getSize().w / 2 - sheetSize.w / 2; + + const real_t fpsScale = (50.f / std::max(1U, fpsLimit)); // ported from 50Hz + real_t setpointDiffX = fpsScale * std::max(.01, (1.0 - skillsFadeInAnimationY)) / 5.0; + skillsFadeInAnimationY += setpointDiffX; + skillsFadeInAnimationY = std::min(1.0, skillsFadeInAnimationY); + + auto fade = skillFrame->findImage("fade img"); + Uint8 r, g, b, a; + SDL_GetRGBA(fade->color, mainsurface->format, &r, &g, &b, &a); + a = 128 * skillsFadeInAnimationY; + fade->color = SDL_MapRGBA(mainsurface->format, r, g, b, a); + + int baseY = (skillFrame->getSize().h / 2 - sheetSize.h / 2); + sheetSize.y = -sheetSize.h + skillsFadeInAnimationY * (baseY + sheetSize.h); + innerFrame->setSize(sheetSize); + + auto skillDescriptionFrame = innerFrame->findFrame("skill desc frame"); + auto slider = skillDescriptionFrame->findSlider("skill slider"); + bool sliderDisabled = slider->isDisabled(); + + if ( stats[player.playernum] && skillSheetData.skillEntries.size() > 0 ) + { + if ( Frame* allSkillEntries = innerFrame->findFrame("skill entries frame") ) + { + for ( int i = 0; i < NUMPROFICIENCIES; ++i ) + { + char skillname[32]; + snprintf(skillname, sizeof(skillname), "skill %d", i); + auto entry = allSkillEntries->findFrame(skillname); + + if ( i >= skillSheetData.skillEntries.size() ) + { + entry->setDisabled(true); + break; + } + entry->setDisabled(false); + int proficiency = skillSheetData.skillEntries[i].skillId; + + auto skillLevel = entry->findField("skill level"); + char skillLevelText[32]; + snprintf(skillLevelText, sizeof(skillLevelText), "%d", stats[player.playernum]->PROFICIENCIES[proficiency]); + skillLevel->setText(skillLevelText); + + auto skillIconBg = entry->findImage("skill icon bg"); + auto skillIconFg = entry->findImage("skill icon fg"); + skillIconFg->path = skillSheetData.skillEntries[i].skillIconPath; + + auto statIcon = entry->findImage("stat icon"); + statIcon->disabled = true; + + auto selectorIcon = entry->findImage("selector img"); + selectorIcon->disabled = true; + + if ( entry->capturesMouse() ) + { + highlightedSkill = i; + if ( inputs.bMouseLeft(player.playernum) ) + { + selectSkill(i); + //inputs.mouseClearLeft(player.playernum); + } + } + + if ( selectedSkill == i || highlightedSkill == i ) + { + selectorIcon->disabled = false; + if ( selectedSkill == i && highlightedSkill == i ) + { + selectorIcon->path = (i < 8) ? "images/ui/SkillSheet/UI_Skills_SkillSelector100_00.png" : "images/ui/SkillSheet/UI_Skills_SkillSelector100R_00.png"; + } + else if ( highlightedSkill == i ) + { + selectorIcon->path = (i < 8) ? skillSheetData.highlightSkillImg : skillSheetData.highlightSkillImg_Right; + } + else if ( selectedSkill == i ) + { + selectorIcon->path = (i < 8) ? skillSheetData.selectSkillImg : skillSheetData.selectSkillImg_Right; + } + } + + if ( stats[player.playernum]->PROFICIENCIES[proficiency] >= SKILL_LEVEL_LEGENDARY ) + { + skillIconFg->path = skillSheetData.skillEntries[i].skillIconPathLegend; + skillLevel->setColor(skillSheetData.legendTextColor); + if ( selectedSkill == i ) + { + skillIconBg->path = skillSheetData.iconBgSelectedPathLegend; + } + else + { + skillIconBg->path = skillSheetData.iconBgPathLegend; + } + } + else if ( stats[player.playernum]->PROFICIENCIES[proficiency] >= SKILL_LEVEL_EXPERT ) + { + skillLevel->setColor(skillSheetData.expertTextColor); + if ( selectedSkill == i ) + { + skillIconBg->path = skillSheetData.iconBgSelectedPathExpert; + } + else + { + skillIconBg->path = skillSheetData.iconBgPathExpert; + } + } + else if ( stats[player.playernum]->PROFICIENCIES[proficiency] >= SKILL_LEVEL_BASIC ) + { + skillLevel->setColor(skillSheetData.noviceTextColor); + if ( selectedSkill == i ) + { + skillIconBg->path = skillSheetData.iconBgSelectedPathNovice; + } + else + { + skillIconBg->path = skillSheetData.iconBgPathNovice; + } + } + else + { + skillLevel->setColor(skillSheetData.defaultTextColor); + if ( selectedSkill == i ) + { + skillIconBg->path = skillSheetData.iconBgSelectedPathDefault; + } + else + { + skillIconBg->path = skillSheetData.iconBgPathDefault; + } + } + //statIcon->path = skillIconFg->path; + //skillIconFg->path = ""; + } + } + + int lowestY = 0; + auto scrollAreaOuterFrame = skillDescriptionFrame->findFrame("scroll area outer frame"); + auto scrollArea = scrollAreaOuterFrame->findFrame("skill scroll area"); + SDL_Rect scrollOuterFramePos = scrollAreaOuterFrame->getSize(); + SDL_Rect scrollAreaPos = scrollArea->getSize(); + + if ( keystatus[SDL_SCANCODE_J] ) + { + keystatus[SDL_SCANCODE_J] = 0; + slider->setDisabled(!slider->isDisabled()); + } + int sliderOffsetW = 0; + if ( slider->isDisabled() ) + { + int diff = (scrollOuterFramePos.w - (skillDescriptionFrame->getSize().w - 32)); + sliderOffsetW = -diff; + } + else + { + int diff = (scrollOuterFramePos.w - (slider->getRailSize().x - 4 - 16)); + sliderOffsetW = -diff; + } + if ( sliderOffsetW != 0 ) + { + bSkillSheetEntryLoaded = false; + } + + scrollOuterFramePos.w += sliderOffsetW; + scrollAreaOuterFrame->setSize(scrollOuterFramePos); + scrollAreaPos.w = scrollOuterFramePos.w; + scrollArea->setSize(scrollAreaPos); + + if ( slider->isDisabled() ) + { + scrollPercent = 0.0; + scrollInertia = 0.0; + } + + if ( selectedSkill >= 0 && selectedSkill < skillSheetData.skillEntries.size() ) + { + auto skillLvlHeaderVal = scrollArea->findField("skill lvl header val"); + char skillLvl[128] = ""; + int proficiency = skillSheetData.skillEntries[selectedSkill].skillId; + int proficiencyValue = stats[player.playernum]->PROFICIENCIES[proficiency]; + std::string skillLvlTitle = ""; + if ( proficiencyValue >= SKILL_LEVEL_LEGENDARY ) + { + skillLvlTitle = language[369]; + } + else if ( proficiencyValue >= SKILL_LEVEL_MASTER ) + { + skillLvlTitle = language[368]; + } + else if ( proficiencyValue >= SKILL_LEVEL_EXPERT ) + { + skillLvlTitle = language[367]; + } + else if ( proficiencyValue >= SKILL_LEVEL_SKILLED ) + { + skillLvlTitle = language[366]; + } + else if ( proficiencyValue >= SKILL_LEVEL_BASIC ) + { + skillLvlTitle = language[365]; + } + else if ( proficiencyValue >= SKILL_LEVEL_NOVICE ) + { + skillLvlTitle = language[364]; + } + else + { + skillLvlTitle = language[363]; + } + skillLvlTitle.erase(std::remove(skillLvlTitle.begin(), skillLvlTitle.end(), ' '), skillLvlTitle.end()); // trim whitespace + snprintf(skillLvl, sizeof(skillLvl), "%s (%d)", skillLvlTitle.c_str(), stats[player.playernum]->PROFICIENCIES[proficiency]); + skillLvlHeaderVal->setText(skillLvl); + + SDL_Rect skillLvlHeaderValPos = skillLvlHeaderVal->getSize(); + skillLvlHeaderValPos.x += sliderOffsetW; + skillLvlHeaderVal->setSize(skillLvlHeaderValPos); + + if ( !bSkillSheetEntryLoaded ) + { + auto skillTitleTxt = innerFrame->findField("skill title txt"); + skillTitleTxt->setText(skillSheetData.skillEntries[selectedSkill].name.c_str()); + + auto statTypeTxt = scrollArea->findField("stat type txt"); + auto statIcon = scrollArea->findImage("stat icon"); + statIcon->path = skillSheetData.skillEntries[selectedSkill].statIconPath; + switch ( getStatForProficiency(proficiency) ) + { + case STAT_STR: + statTypeTxt->setText("STR"); + break; + case STAT_DEX: + statTypeTxt->setText("DEX"); + break; + case STAT_CON: + statTypeTxt->setText("CON"); + break; + case STAT_INT: + statTypeTxt->setText("INT"); + break; + case STAT_PER: + statTypeTxt->setText("PER"); + break; + case STAT_CHR: + statTypeTxt->setText("CHR"); + break; + default: + break; + } + + SDL_Rect statTypeTxtPos = statTypeTxt->getSize(); + statTypeTxtPos.x += sliderOffsetW; + statTypeTxt->setSize(statTypeTxtPos); + statIcon->pos.x += sliderOffsetW; + } + + auto skillDescriptionTxt = scrollArea->findField("skill desc txt"); + int moveEffectsOffsetY = 0; + Font* actualFont = Font::get(skillDescriptionTxt->getFont()); + if ( !bSkillSheetEntryLoaded ) + { + SDL_Rect skillDescriptionTxtPos = skillDescriptionTxt->getSize(); + skillDescriptionTxtPos.w = scrollAreaPos.w; + skillDescriptionTxt->setSize(skillDescriptionTxtPos); + + int txtHeightOld = skillDescriptionTxt->getNumTextLines() * actualFont->height(true); + skillDescriptionTxt->setText(skillSheetData.skillEntries[selectedSkill].description.c_str()); + skillDescriptionTxt->reflowTextToFit(0); + int txtHeightNew = skillDescriptionTxt->getNumTextLines() * actualFont->height(true); + + if ( txtHeightNew != txtHeightOld ) + { + moveEffectsOffsetY = (txtHeightNew - txtHeightOld); + } + } + + lowestY = std::max(lowestY, skillDescriptionTxt->getSize().y + skillDescriptionTxt->getNumTextLines() * actualFont->height(true)); + + int previousEffectFrameHeight = 0; + for ( int eff = 0; eff < 10; ++eff ) + { + char effectFrameName[64] = ""; + snprintf(effectFrameName, sizeof(effectFrameName), "effect %d frame", eff); + auto effectFrame = scrollArea->findFrame(effectFrameName); + if ( !effectFrame ) { continue; } + + effectFrame->setDisabled(true); + SDL_Rect effectFramePos = effectFrame->getSize(); + effectFramePos.w = scrollAreaPos.w; + + if ( moveEffectsOffsetY != 0 ) + { + effectFramePos.y += moveEffectsOffsetY; + } + effectFrame->setSize(effectFramePos); + + if ( eff < skillSheetData.skillEntries[selectedSkill].effects.size() ) + { + effectFrame->setDisabled(false); + + auto& effect_t = skillSheetData.skillEntries[selectedSkill].effects[eff]; + auto effectTxtFrame = effectFrame->findFrame("effect txt frame"); + auto effectTxt = effectTxtFrame->findField("effect txt"); + auto effectValFrame = effectFrame->findFrame("effect val frame"); + auto effectVal = effectValFrame->findField("effect val"); + auto effectBgImgFrame = effectFrame->findFrame("effect val bg frame"); + { + // adjust position to match width of container + SDL_Rect effectTxtFramePos = effectTxtFrame->getSize(); + SDL_Rect effectValFramePos = effectValFrame->getSize(); + SDL_Rect effectBgImgFramePos = effectBgImgFrame->getSize(); + const auto& effectStartOffsetX = skillSheetData.skillEntries[selectedSkill].effectStartOffsetX; + const auto& effectBackgroundOffsetX = skillSheetData.skillEntries[selectedSkill].effectBackgroundOffsetX; + const auto& effectBackgroundWidth = skillSheetData.skillEntries[selectedSkill].effectBackgroundWidth; + effectTxtFramePos.w = effectFrame->getSize().w - effectStartOffsetX - effectBackgroundOffsetX; + effectValFramePos.x = effectFrame->getSize().w - effectStartOffsetX; + effectValFramePos.w = effectStartOffsetX; + effectBgImgFramePos.x = effectFrame->getSize().w - effectStartOffsetX - effectBackgroundOffsetX; + effectBgImgFramePos.w = effectBackgroundWidth; + effectTxtFrame->setSize(effectTxtFramePos); + effectValFrame->setSize(effectValFramePos); + effectBgImgFrame->setSize(effectBgImgFramePos); + } + + effectTxt->setText(effect_t.title.c_str()); + if ( effect_t.effectUpdatedAtSkillLevel != stats[player.playernum]->PROFICIENCIES[proficiency] + || effect_t.value == "" ) + { + effect_t.effectUpdatedAtSkillLevel = stats[player.playernum]->PROFICIENCIES[proficiency]; + effect_t.value = formatSkillSheetEffects(player.playernum, proficiency, effect_t.tag, effect_t.rawValue); + } + effectVal->setText(effect_t.value.c_str()); + + Font* effectTxtFont = Font::get(effectTxt->getFont()); + int fontHeight; + effectTxtFont->sizeText("_", nullptr, &fontHeight); + int numEffectLines = effectTxt->getNumTextLines(); + effectFramePos = effectFrame->getSize(); + if ( numEffectLines > 1 ) + { + effectFramePos.h = (fontHeight * numEffectLines) + 8; + } + else + { + effectFramePos.h = fontHeight + 8; + } + if ( eff > 0 ) + { + effectFramePos.y = previousEffectFrameHeight; // don't adjust first effect frame y pos + } + effectFrame->setSize(effectFramePos); + + { + // adjust position to match height of container + SDL_Rect effectTxtFramePos = effectTxtFrame->getSize(); + SDL_Rect effectValFramePos = effectValFrame->getSize(); + SDL_Rect effectBgImgFramePos = effectBgImgFrame->getSize(); + const int containerHeight = effectFramePos.h - 4; + effectTxtFramePos.h = containerHeight; + effectValFramePos.h = containerHeight; + effectTxtFrame->setSize(effectTxtFramePos); + effectValFrame->setSize(effectValFramePos); + + SDL_Rect effectTxtPos = effectTxt->getSize(); + effectTxtPos.h = containerHeight; + effectTxt->setSize(effectTxtPos); + SDL_Rect effectValPos = effectVal->getSize(); + effectValPos.h = containerHeight; + effectVal->setSize(effectValPos); + + if ( numEffectLines > 1 ) + { + effectBgImgFramePos.h = (fontHeight + 8) * effectVal->getNumTextLines(); + effectBgImgFramePos.y = (containerHeight / 2 - effectBgImgFramePos.h / 2); + } + else + { + effectBgImgFramePos.y = 0; + effectBgImgFramePos.h = containerHeight; + } + effectBgImgFrame->setSize(effectBgImgFramePos); + + auto effectFrameBgImg = effectFrame->findImage("effect frame bg highlight"); + effectFrameBgImg->pos = SDL_Rect{ 0, effectFrame->getSize().h - 2, effectFrame->getSize().w, 1 }; + + auto effectFrameBgImgTmp = effectFrame->findImage("effect frame bg tmp"); + effectFrameBgImgTmp->pos = SDL_Rect{ 0, 0, effectFrame->getSize().w, effectFrame->getSize().h }; + } + + { + // adjust inner background image elements + auto tl = effectBgImgFrame->findImage("effect bg top left"); + tl->pos.x = 0; + tl->pos.y = 0; + auto tr = effectBgImgFrame->findImage("effect bg top right"); + tr->pos.x = effectBgImgFrame->getSize().w - tr->pos.w; + tr->pos.y = 0; + auto tm = effectBgImgFrame->findImage("effect bg top middle"); + tm->pos.x = tl->pos.x + tl->pos.w; + tm->pos.w = effectBgImgFrame->getSize().w - tr->pos.w - tl->pos.w; + auto bl = effectBgImgFrame->findImage("effect bg bottom left"); + bl->pos.x = 0; + bl->pos.y = effectBgImgFrame->getSize().h - bl->pos.h; + auto br = effectBgImgFrame->findImage("effect bg bottom right"); + br->pos.x = effectBgImgFrame->getSize().w - br->pos.w; + br->pos.y = bl->pos.y; + auto bm = effectBgImgFrame->findImage("effect bg bottom middle"); + bm->pos.x = tm->pos.x; + bm->pos.w = tm->pos.w; + bm->pos.y = bl->pos.y; + auto ml = effectBgImgFrame->findImage("effect bg left"); + ml->pos.x = 0; + ml->pos.y = tl->pos.y + tl->pos.h; + ml->pos.h = effectBgImgFrame->getSize().h - bl->pos.h - tl->pos.h; + auto mr = effectBgImgFrame->findImage("effect bg right"); + mr->pos.x = effectBgImgFrame->getSize().w - mr->pos.w; + mr->pos.y = ml->pos.y; + mr->pos.h = ml->pos.h; + auto mm = effectBgImgFrame->findImage("effect bg middle"); + mm->pos.x = ml->pos.x + ml->pos.w; + mm->pos.y = ml->pos.y; + mm->pos.w = effectBgImgFrame->getSize().w - ml->pos.w - mr->pos.w; + mm->pos.h = ml->pos.h; + } + + lowestY = std::max(lowestY, effectFrame->getSize().y + effectFrame->getSize().h); + + auto textGetTitle = Text::get(effectTxt->getText(), effectTxt->getFont()); + int titleWidth = textGetTitle->getWidth(); + if ( numEffectLines > 1 ) + { + auto textGetTitle = Text::get(effectTxt->getLongestLine().c_str(), effectTxt->getFont()); + titleWidth = textGetTitle->getWidth(); + } + auto textGetValue = Text::get(effectVal->getText(), effectVal->getFont()); + int valueWidth = textGetValue->getWidth(); + + // check marquee if needed + if ( ticks - openTick > TICKS_PER_SECOND * 2 ) + { + bool doMarquee = false; + doMarquee = doMarquee || (titleWidth > (effectTxt->getSize().x + effectTxtFrame->getSize().w)); + doMarquee = doMarquee || (valueWidth > (effectVal->getSize().x + effectValFrame->getSize().w)); + + if ( doMarquee ) + { + const real_t fpsScale = (60.f / std::max(1U, fpsLimit)); // ported from 60Hz + effect_t.marquee += (.005 * fpsScale); + //effect_t.marquee = std::min(1.0, effect_t.marquee); + + /*if ( effect_t.marqueeTicks == 0 && effect_t.marquee >= 1.0 ) + { + effect_t.marqueeTicks = ticks; + }*/ + /*if ( effect_t.marqueeTicks > 0 && (ticks - effect_t.marqueeTicks > TICKS_PER_SECOND * 2) ) + { + effect_t.marqueeTicks = 0; + effect_t.marquee = 0.0; + }*/ + } + } + SDL_Rect posTitle = effectTxt->getSize(); + int scrollTitleLength = titleWidth - effectTxtFrame->getSize().w; + if ( titleWidth <= effectTxtFrame->getSize().w ) + { + scrollTitleLength = 0; + posTitle.x = 0; + effect_t.marqueeCompleted = false; + effect_t.marquee = 0.0; + effect_t.marqueeTicks = 0; + } + else + { + posTitle.x = std::max((int)(-effect_t.marquee * 100), -scrollTitleLength); + if ( posTitle.x == -scrollTitleLength ) + { + if ( !effect_t.marqueeCompleted ) + { + effect_t.marqueeTicks = ticks; + } + effect_t.marqueeCompleted = true; + } + else + { + effect_t.marqueeCompleted = false; + } + } + //posTitle.x = -scrollTitleLength * effect_t.marquee; + effectTxt->setSize(posTitle); + + SDL_Rect posValue = effectVal->getSize(); + int scrollValueLength = valueWidth - effectValFrame->getSize().w; + if ( valueWidth <= effectValFrame->getSize().w ) + { + scrollValueLength = 0; + } + posValue.x = -scrollValueLength * effect_t.marquee; + effectVal->setSize(posValue); + } + + previousEffectFrameHeight = effectFrame->getSize().y + effectFrame->getSize().h; + } + + Uint32 lastMarqueeTick = 0; + bool allMarqueeCompleted = true; + for ( auto& effect_t : skillSheetData.skillEntries[selectedSkill].effects ) + { + if ( effect_t.marquee > 0.0 ) + { + if ( !effect_t.marqueeCompleted ) + { + allMarqueeCompleted = false; + } + lastMarqueeTick = std::max(effect_t.marqueeTicks, lastMarqueeTick); + } + } + if ( allMarqueeCompleted && lastMarqueeTick > 0 && ((ticks - lastMarqueeTick) > 2 * TICKS_PER_SECOND) ) + { + for ( auto& effect_t : skillSheetData.skillEntries[selectedSkill].effects ) + { + effect_t.marquee = 0.0; + effect_t.marqueeCompleted = false; + effect_t.marqueeTicks = 0; + } + openTick = (ticks > TICKS_PER_SECOND) ? (ticks - TICKS_PER_SECOND) : ticks; + } + + // legend panel + auto legendDivImg = scrollArea->findImage("legend div"); + legendDivImg->pos.x = scrollArea->getSize().w / 2 - legendDivImg->pos.w / 2; + legendDivImg->pos.y = lowestY + 8; + auto legendDivTxt = scrollArea->findField("legend div text"); + SDL_Rect legendDivTxtPos = legendDivTxt->getSize(); + legendDivTxtPos.x = legendDivImg->pos.x; + legendDivTxtPos.w = legendDivImg->pos.w; + legendDivTxtPos.y = legendDivImg->pos.y + legendDivImg->pos.h - 16; + legendDivTxtPos.h = actualFont->height(true); + legendDivTxt->setSize(legendDivTxtPos); + lowestY = legendDivTxtPos.y + legendDivTxtPos.h; + + auto legendFrame = scrollArea->findFrame("legend frame"); + SDL_Rect legendPos = legendFrame->getSize(); + legendPos.y = lowestY; + legendPos.w = scrollArea->getSize().w; + legendFrame->setSize(legendPos); + + auto tl = legendFrame->findImage("top left img"); + auto tm = legendFrame->findImage("top img"); + auto tr = legendFrame->findImage("top right img"); + tm->pos.w = legendPos.w - tl->pos.w - tr->pos.w; + tr->pos.x = legendPos.w - tr->pos.w; + + auto legendText = legendFrame->findField("legend text"); + legendText->setText(skillSheetData.skillEntries[selectedSkill].legendaryDescription.c_str()); + SDL_Rect legendTextPos = legendText->getSize(); + legendTextPos.w = tm->pos.w; + legendText->setSize(legendTextPos); + legendText->reflowTextToFit(0); + legendTextPos.h = legendText->getNumTextLines() * actualFont->height(true); + legendTextPos.y = tm->pos.y + tm->pos.h / 2; + legendText->setSize(legendTextPos); + + + auto ml = legendFrame->findImage("middle left img"); + ml->pos.h = legendTextPos.h - tm->pos.h; + auto mm = legendFrame->findImage("middle img"); + mm->pos.h = ml->pos.h; + auto mr = legendFrame->findImage("middle right img"); + mr->pos.h = ml->pos.h; + mm->pos.w = legendPos.w - ml->pos.w - mr->pos.w; + mr->pos.x = legendPos.w - mr->pos.w; + + auto bl = legendFrame->findImage("bottom left img"); + bl->pos.y = ml->pos.y + ml->pos.h; + auto bm = legendFrame->findImage("bottom img"); + bm->pos.y = bl->pos.y; + auto br = legendFrame->findImage("bottom right img"); + br->pos.y = bl->pos.y; + bm->pos.w = legendPos.w - bl->pos.w - br->pos.w; + br->pos.x = legendPos.w - br->pos.w; + + if ( proficiencyValue < SKILL_LEVEL_LEGENDARY ) + { + tl->path = "images/ui/SkillSheet/UI_Skills_LegendBox_TL_00.png"; + tm->path = "images/ui/SkillSheet/UI_Skills_LegendBox_T_00.png"; + tr->path = "images/ui/SkillSheet/UI_Skills_LegendBox_TR_00.png"; + + ml->path = "images/ui/SkillSheet/UI_Skills_LegendBox_ML_00.png"; + mm->path = "images/ui/SkillSheet/UI_Skills_LegendBox_M_00.png"; + mr->path = "images/ui/SkillSheet/UI_Skills_LegendBox_MR_00.png"; + + bl->path = "images/ui/SkillSheet/UI_Skills_LegendBox_BL_00.png"; + bm->path = "images/ui/SkillSheet/UI_Skills_LegendBox_B_00.png"; + br->path = "images/ui/SkillSheet/UI_Skills_LegendBox_BR_00.png"; + } + else + { + tl->path = "images/ui/SkillSheet/UI_Skills_LegendBox100_TL_00.png"; + tm->path = "images/ui/SkillSheet/UI_Skills_LegendBox100_T_00.png"; + tr->path = "images/ui/SkillSheet/UI_Skills_LegendBox100_TR_00.png"; + + ml->path = "images/ui/SkillSheet/UI_Skills_LegendBox100_ML_00.png"; + mm->path = "images/ui/SkillSheet/UI_Skills_LegendBox100_M_00.png"; + mr->path = "images/ui/SkillSheet/UI_Skills_LegendBox100_MR_00.png"; + + bl->path = "images/ui/SkillSheet/UI_Skills_LegendBox100_BL_00.png"; + bm->path = "images/ui/SkillSheet/UI_Skills_LegendBox100_B_00.png"; + br->path = "images/ui/SkillSheet/UI_Skills_LegendBox100_BR_00.png"; + } + + legendPos.h = bl->pos.y + bl->pos.h; + legendFrame->setSize(legendPos); + + lowestY = legendPos.y + legendPos.h; + lowestY += 4; // small buffer after legend box + } + + if ( !slider->isDisabled() ) + { + if ( mousestatus[SDL_BUTTON_WHEELDOWN] ) + { + mousestatus[SDL_BUTTON_WHEELDOWN] = 0; + scrollInertia = std::min(scrollInertia + .05, .15); + } + if ( mousestatus[SDL_BUTTON_WHEELUP] ) + { + mousestatus[SDL_BUTTON_WHEELUP] = 0; + scrollInertia = std::max(scrollInertia - .05, -.15); + } + } + + if ( abs(scrollInertia) > 0.0 ) + { + scrollInertia *= .9; + if ( abs(scrollInertia) < .01 ) + { + scrollInertia = 0.0; + } + scrollPercent = std::min(1.0, std::max(scrollPercent + scrollInertia, 0.00)); + if ( scrollPercent >= 1.0 || scrollPercent <= 0.0 ) + { + scrollInertia = 0.0; + } + slider->setValue(scrollPercent * 100); + } + + scrollPercent = slider->getValue() / 100.0; + scrollAreaPos = scrollArea->getSize(); + if ( lowestY > scrollAreaOuterFrame->getSize().h ) + { + scrollAreaPos.y = -(lowestY - scrollAreaOuterFrame->getSize().h) * scrollPercent; + slider->setDisabled(false); + } + else + { + scrollAreaPos.y = 0; + slider->setDisabled(true); + } + scrollArea->setSize(scrollAreaPos); + } + + if ( sliderDisabled != slider->isDisabled() ) + { + // rerun this function + processSkillSheet(); + bSkillSheetEntryLoaded = false; + } + else + { + bSkillSheetEntryLoaded = true; + } } \ No newline at end of file From cf8bd970ba18adcb7b7f24f08afc2d99401c39fe Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 26 Oct 2021 20:27:02 +1100 Subject: [PATCH 22/39] * remove mono font hinting - doesn't look great? --- src/ui/Font.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/Font.cpp b/src/ui/Font.cpp index 86decee52..fb4b1e512 100644 --- a/src/ui/Font.cpp +++ b/src/ui/Font.cpp @@ -28,7 +28,7 @@ Font::Font(const char* _name) { printlog("failed to load '%s': %s", path.c_str(), TTF_GetError()); return; } - TTF_SetFontHinting(font, TTF_HINTING_MONO); + //TTF_SetFontHinting(font, TTF_HINTING_MONO); TTF_SetFontKerning(font, 1); } From 8b9b01e7900aa2f3051226a0db8e69048601c9f2 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 26 Oct 2021 20:27:52 +1100 Subject: [PATCH 23/39] * slider hidden if disabled * slider uses individual player mouse x/y instead of global mouse x/y --- src/ui/Slider.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ui/Slider.cpp b/src/ui/Slider.cpp index 47df40839..7fcfd2b72 100644 --- a/src/ui/Slider.cpp +++ b/src/ui/Slider.cpp @@ -14,7 +14,7 @@ Slider::Slider(Frame& _parent) { } void Slider::draw(SDL_Rect _size, SDL_Rect _actualSize, const std::vector& selectedWidgets) { - if (invisible) { + if ( invisible || isDisabled() ) { return; } if (maxValue == minValue) { @@ -167,10 +167,17 @@ Slider::result_t Slider::process(SDL_Rect _size, SDL_Rect _actualSize, const boo return result; } +#ifdef EDITOR Sint32 mousex = (::mousex / (float)xres) * (float)Frame::virtualScreenX; Sint32 mousey = (::mousey / (float)yres) * (float)Frame::virtualScreenY; Sint32 omousex = (::omousex / (float)xres) * (float)Frame::virtualScreenX; Sint32 omousey = (::omousey / (float)yres) * (float)Frame::virtualScreenY; +#else + Sint32 mousex = (inputs.getMouse(owner, Inputs::X) / (float)xres) * (float)Frame::virtualScreenX; + Sint32 mousey = (inputs.getMouse(owner, Inputs::Y) / (float)yres) * (float)Frame::virtualScreenY; + Sint32 omousex = (inputs.getMouse(owner, Inputs::OX) / (float)xres) * (float)Frame::virtualScreenX; + Sint32 omousey = (inputs.getMouse(owner, Inputs::OY) / (float)yres) * (float)Frame::virtualScreenY; +#endif #ifndef NINTENDO if (rectContainsPoint(_size, omousex, omousey)) { From c94f6cd2aa6aa9a9662f1d914ff6a68c00f337fb Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Tue, 26 Oct 2021 20:28:13 +1100 Subject: [PATCH 24/39] * add some commented out test for player velocity --- src/actplayer.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/actplayer.cpp b/src/actplayer.cpp index d4fc69c18..8c9ae26f3 100644 --- a/src/actplayer.cpp +++ b/src/actplayer.cpp @@ -994,10 +994,11 @@ void Player::PlayerMovement_t::handlePlayerMovement(bool useRefreshRateDelta) PLAYER_VELX *= pow(0.75, refreshRateDelta); PLAYER_VELY *= pow(0.75, refreshRateDelta); - /*if ( keystatus[SDL_SCANCODE_G] ) - { - messagePlayer(0, "X: %5.5f, Y: %5.5f", PLAYER_VELX, PLAYER_VELY); - }*/ + //if ( keystatus[SDL_SCANCODE_G] ) + //{ + // //messagePlayer(0, "X: %5.5f, Y: %5.5f", PLAYER_VELX, PLAYER_VELY); + // //messagePlayer(0, "Vel: %5.5f", sqrt(pow(PLAYER_VELX, 2) + pow(PLAYER_VELY, 2))); + //} for ( node_t* node = map.creatures->first; node != nullptr; node = node->next ) //Since looking for players only, don't search full entity list. Best idea would be to directly example players[] though. { From 4ed40760687afaeb24a72c77a7e7253d352bb4df Mon Sep 17 00:00:00 2001 From: SheridanR Date: Wed, 27 Oct 2021 01:16:36 -0700 Subject: [PATCH 25/39] Binding works Add minimap and messages windows Signed-off-by: SheridanR --- src/game.cpp | 3 +- src/input.cpp | 1 - src/ui/Frame.cpp | 106 ++++++---- src/ui/MainMenu.cpp | 490 +++++++++++++++++++++++++++++++++++++++----- src/ui/MainMenu.hpp | 104 +++++++++- 5 files changed, 606 insertions(+), 98 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 8cfdad66e..1d12e0bfa 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5113,7 +5113,8 @@ int main(int argc, char** argv) inputs.controllerClearInput(clientnum, INJOY_MENU_NEXT); inputs.controllerClearInput(clientnum, INJOY_MENU_CANCEL); fadealpha = 255; -#if (!defined STEAMWORKS && !defined USE_EOS && !defined NINTENDO) + // Yeah we're just not going to do the "Please don't pirate us" message anymore +#if (0) introstage = 0; fadeout = false; fadefinished = false; diff --git a/src/input.cpp b/src/input.cpp index 6c10f1d29..ad184cc4f 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -410,7 +410,6 @@ void Input::bind(const char* binding, const char* input) { auto result = bindings.emplace(binding, binding_t()); b = result.first; } - (*b).second = binding_t(); (*b).second.input.assign(input); if (input == nullptr) { (*b).second.type = binding_t::INVALID; diff --git a/src/ui/Frame.cpp b/src/ui/Frame.cpp index 160ee2ff1..53cd9a600 100644 --- a/src/ui/Frame.cpp +++ b/src/ui/Frame.cpp @@ -673,17 +673,36 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: } } + // process frames + { + for (int i = frames.size() - 1; i >= 0; --i) { + Frame* frame = frames[i]; + result_t frameResult = frame->process(_size, actualSize, selectedWidgets, usable); + usable = result.usable = frameResult.usable; + if (!frameResult.removed) { + if (frameResult.tooltip != nullptr) { + result = frameResult; + } + } else { + delete frame; + frames.erase(frames.begin() + i); + } + } + } + // scroll with right stick - if (allowScrolling && allowScrollBinds) { + if (usable && allowScrolling && allowScrollBinds) { Input& input = Input::inputs[owner]; // x scroll if (this->actualSize.w > size.w) { if (input.binary("MenuScrollRight")) { this->actualSize.x += std::min(this->actualSize.x + 5, this->actualSize.w - _size.w); + usable = result.usable = false; } else if (input.binary("MenuScrollLeft")) { this->actualSize.x -= std::max(this->actualSize.x - 5, 0); + usable = result.usable = false; } } @@ -691,63 +710,58 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: if (this->actualSize.h > size.h) { if (input.binary("MenuScrollDown")) { this->actualSize.y = std::min(this->actualSize.y + 5, this->actualSize.h - _size.h); + usable = result.usable = false; } else if (input.binary("MenuScrollUp")) { this->actualSize.y = std::max(this->actualSize.y - 5, 0); + usable = result.usable = false; } } } - // process frames - { - for (int i = frames.size() - 1; i >= 0; --i) { - Frame* frame = frames[i]; - result_t frameResult = frame->process(_size, actualSize, selectedWidgets, usable); - usable = result.usable = frameResult.usable; - if (!frameResult.removed) { - if (frameResult.tooltip != nullptr) { - result = frameResult; - } - } else { - delete frame; - frames.erase(frames.begin() + i); - } + // scroll with mouse wheel + if (parent != nullptr && !hollow && rectContainsPoint(fullSize, omousex, omousey) && usable) { + bool mwheeldown = false; + bool mwheelup = false; + if (mousestatus[SDL_BUTTON_WHEELDOWN]) { + mousestatus[SDL_BUTTON_WHEELDOWN] = 0; + mwheeldown = true; } - } + if (mousestatus[SDL_BUTTON_WHEELUP]) { + mousestatus[SDL_BUTTON_WHEELUP] = 0; + mwheelup = true; + } + if (allowScrolling && allowScrollBinds) { + if (mwheeldown || mwheelup) { + usable = result.usable = false; - // scroll with mouse wheel - if (parent != nullptr && !hollow && rectContainsPoint(fullSize, omousex, omousey) && usable && allowScrolling && allowScrollBinds) { - // x scroll with mouse wheel - if (this->actualSize.w > size.w) { - if (this->actualSize.h <= size.h) { - if (mousestatus[SDL_BUTTON_WHEELDOWN]) { - mousestatus[SDL_BUTTON_WHEELDOWN] = 0; - this->actualSize.x += std::min(entrySize, size.w); - usable = result.usable = false; - } else if (mousestatus[SDL_BUTTON_WHEELUP]) { - mousestatus[SDL_BUTTON_WHEELUP] = 0; - this->actualSize.x -= std::min(entrySize, size.w); - usable = result.usable = false; + // x scroll with mouse wheel + if (this->actualSize.w > size.w) { + if (this->actualSize.h <= size.h) { + if (mwheeldown) { + this->actualSize.x += std::min(entrySize, size.w); + } + if (mwheelup) { + this->actualSize.x -= std::min(entrySize, size.w); + } + this->actualSize.x = std::min(std::max(0, this->actualSize.x), + std::max(0, this->actualSize.w - size.w)); + } } - } - } - // y scroll with mouse wheel - if (this->actualSize.h > size.h) { - if (mousestatus[SDL_BUTTON_WHEELDOWN]) { - mousestatus[SDL_BUTTON_WHEELDOWN] = 0; - this->actualSize.y += std::min(entrySize, size.h); - usable = result.usable = false; - } else if (mousestatus[SDL_BUTTON_WHEELUP]) { - mousestatus[SDL_BUTTON_WHEELUP] = 0; - this->actualSize.y -= std::min(entrySize, size.h); - usable = result.usable = false; + // y scroll with mouse wheel + if (this->actualSize.h > size.h) { + if (mwheeldown) { + this->actualSize.y += std::min(entrySize, size.h); + } + if (mwheelup) { + this->actualSize.y -= std::min(entrySize, size.h); + } + this->actualSize.y = std::min(std::max(0, this->actualSize.y), + std::max(0, this->actualSize.h - size.h)); + } } } - - // bound - this->actualSize.x = std::min(std::max(0, this->actualSize.x), std::max(0, this->actualSize.w - size.w)); - this->actualSize.y = std::min(std::max(0, this->actualSize.y), std::max(0, this->actualSize.h - size.h)); } // process (frame view) sliders @@ -1476,9 +1490,11 @@ void Frame::scrollToSelection(bool scroll_to_top) { const int index = selection; if (scroll_to_top || actualSize.y > index * entrySize) { actualSize.y = index * entrySize; + actualSize.y = std::min(std::max(0, actualSize.y), std::max(0, actualSize.h - size.h)); } if (actualSize.y + size.h < (index + 1) * entrySize) { actualSize.y = (index + 1) * entrySize - size.h; + actualSize.y = std::min(std::max(0, actualSize.y), std::max(0, actualSize.h - size.h)); } } diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index dbb255e10..b8cac819d 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -317,6 +317,110 @@ namespace MainMenu { return InventorySorting(); } +/******************************************************************************/ + + inline void Bindings::save() { + FileHelper::writeObject("config/bindings.json", EFileFormat::Json, *this); + } + + inline Bindings Bindings::load() { + Bindings bindings; + bool result = FileHelper::readObject("config/bindings.json", bindings); + return result ? bindings : reset(); + } + + inline Bindings Bindings::reset() { + Bindings bindings; + for (int c = 0; c < 4; ++c) { + bindings.devices[c] = c; + + bindings.kb_mouse_bindings[c].emplace("Move Forward", "W"); + bindings.kb_mouse_bindings[c].emplace("Move Left", "A"); + bindings.kb_mouse_bindings[c].emplace("Move Backward", "S"); + bindings.kb_mouse_bindings[c].emplace("Move Right", "D"); + bindings.kb_mouse_bindings[c].emplace("Turn Left", "Left"); + bindings.kb_mouse_bindings[c].emplace("Turn Right", "Right"); + bindings.kb_mouse_bindings[c].emplace("Look Up", "Up"); + bindings.kb_mouse_bindings[c].emplace("Look Down", "Down"); + bindings.kb_mouse_bindings[c].emplace("Chat", "Return"); + bindings.kb_mouse_bindings[c].emplace("Console Command", "/"); + bindings.kb_mouse_bindings[c].emplace("Character Status", "Tab"); + bindings.kb_mouse_bindings[c].emplace("Spell List", "M"); + bindings.kb_mouse_bindings[c].emplace("Cast Spell", "F"); + bindings.kb_mouse_bindings[c].emplace("Block", "Space"); + bindings.kb_mouse_bindings[c].emplace("Sneak", "Shift"); + bindings.kb_mouse_bindings[c].emplace("Attack", "Mouse1"); + bindings.kb_mouse_bindings[c].emplace("Use", "Mouse3"); + bindings.kb_mouse_bindings[c].emplace("Autosort Inventory", "Y"); + bindings.kb_mouse_bindings[c].emplace("Command NPC", "C"); + bindings.kb_mouse_bindings[c].emplace("Show NPC Commands", "X"); + bindings.kb_mouse_bindings[c].emplace("Cycle NPCs", "Z"); + bindings.kb_mouse_bindings[c].emplace("Hotbar Scroll Left", "["); + bindings.kb_mouse_bindings[c].emplace("Hotbar Scroll Right", "]"); + bindings.kb_mouse_bindings[c].emplace("Hotbar Select", "\\"); + + bindings.gamepad_bindings[c].emplace("Move Forward", "StickLeftY-"); + bindings.gamepad_bindings[c].emplace("Move Left", "StickLeftX-"); + bindings.gamepad_bindings[c].emplace("Move Backward", "StickLeftY+"); + bindings.gamepad_bindings[c].emplace("Move Right", "StickLeftX+"); + bindings.gamepad_bindings[c].emplace("Turn Left", "StickRightX-"); + bindings.gamepad_bindings[c].emplace("Turn Right", "StickRightX+"); + bindings.gamepad_bindings[c].emplace("Look Up", "StickRightY-"); + bindings.gamepad_bindings[c].emplace("Look Down", "StickRightY+"); + bindings.gamepad_bindings[c].emplace("Character Status", "ButtonSelect"); + bindings.gamepad_bindings[c].emplace("Cast Spell", "RightBumper"); + bindings.gamepad_bindings[c].emplace("Block", "LeftTrigger"); + bindings.gamepad_bindings[c].emplace("Sneak", "LeftTrigger"); + bindings.gamepad_bindings[c].emplace("Attack", "RightTrigger"); + bindings.gamepad_bindings[c].emplace("Use", "ButtonA"); + bindings.gamepad_bindings[c].emplace("Command NPC", "DpadY-"); + bindings.gamepad_bindings[c].emplace("Show NPC Commands", "DpadX+"); + bindings.gamepad_bindings[c].emplace("Cycle NPCs", "DpadX-"); + bindings.gamepad_bindings[c].emplace("Hotbar Scroll Left", "ButtonLeftBumper"); + bindings.gamepad_bindings[c].emplace("Hotbar Scroll Right", "ButtonRightBumper"); + bindings.gamepad_bindings[c].emplace("Hotbar Select", "ButtonY"); + } + return bindings; + } + +/******************************************************************************/ + + inline void Minimap::save() { + minimapTransparencyForeground = 100 - foreground_opacity; + minimapTransparencyBackground = 100 - background_opacity; + minimapScale = map_scale; + minimapObjectZoom = icon_scale; + } + + inline Minimap Minimap::load() { + Minimap minimap; + minimap.foreground_opacity = 100 - minimapTransparencyForeground; + minimap.background_opacity = 100 - minimapTransparencyBackground; + minimap.map_scale = minimapScale; + minimap.icon_scale = minimapObjectZoom; + return minimap; + } + + inline Minimap Minimap::reset() { + return Minimap(); + } + +/******************************************************************************/ + + inline void Messages::save() { + FileHelper::writeObject("config/messages.json", EFileFormat::Json, *this); + } + + inline Messages Messages::load() { + Messages messages; + bool result = FileHelper::readObject("config/messages.json", messages); + return result ? messages : reset(); + } + + inline Messages Messages::reset() { + return Messages(); + } + /******************************************************************************/ static const char* intro_text = @@ -488,7 +592,9 @@ namespace MainMenu { auto_hotbar_new_items = allSettings.add_items_to_hotbar_enabled; allSettings.inventory_sorting.save(); right_click_protect = !allSettings.use_on_release_enabled; + allSettings.minimap.save(); disable_messages = !allSettings.show_messages_enabled; + allSettings.show_messages.save(); hide_playertags = !allSettings.show_player_nametags_enabled; nohud = !allSettings.show_hud_enabled; broadcast = !allSettings.show_ip_address_enabled; @@ -509,6 +615,7 @@ namespace MainMenu { minimapPingMute = !allSettings.minimap_pings_enabled; mute_player_monster_sounds = !allSettings.player_monster_sounds_enabled; mute_audio_on_focus_lost = !allSettings.out_of_focus_audio_enabled; + allSettings.bindings.save(); hotbar_numkey_quick_add = allSettings.numkeys_in_inventory_enabled; mousespeed = allSettings.mouse_sensitivity; reversemouse = allSettings.reverse_mouse_enabled; @@ -589,7 +696,9 @@ namespace MainMenu { allSettings.add_items_to_hotbar_enabled = true; allSettings.inventory_sorting = InventorySorting::reset(); allSettings.use_on_release_enabled = true; + allSettings.minimap.reset(); allSettings.show_messages_enabled = true; + allSettings.show_messages.reset(); allSettings.show_player_nametags_enabled = true; allSettings.show_hud_enabled = true; allSettings.show_ip_address_enabled = true; @@ -615,6 +724,7 @@ namespace MainMenu { allSettings.minimap_pings_enabled = true; allSettings.player_monster_sounds_enabled = true; allSettings.out_of_focus_audio_enabled = true; + allSettings.bindings.reset(); allSettings.numkeys_in_inventory_enabled = true; allSettings.mouse_sensitivity = 32.f; allSettings.reverse_mouse_enabled = false; @@ -1188,6 +1298,7 @@ namespace MainMenu { static int settingsAddBinding( Frame& frame, int y, + int player_index, const char* binding, const char* tip, void (*callback)(Button&)) @@ -1201,7 +1312,17 @@ namespace MainMenu { 158, 44}); button->setFont(smallfont_outline); - button->setText(Input::inputs[0].binding(binding)); + auto device = allSettings.bindings.devices[player_index]; + auto& bindings = + device == 0 ? allSettings.bindings.kb_mouse_bindings[player_index]: + device >= 1 && device <= 4 ? allSettings.bindings.gamepad_bindings[player_index]: + allSettings.bindings.joystick_bindings[player_index]; + auto find = bindings.find(binding); + if (find != bindings.end()) { + button->setText(find->second.c_str()); + } else { + button->setText("[unbound]"); + } button->setJustify(Button::justify_t::CENTER); button->setCallback(callback); button->setBackground("images/ui/Main Menus/Settings/Settings_Button_Customize00.png"); @@ -1602,7 +1723,13 @@ namespace MainMenu { } } - static Frame* settingsGenericWindow(const char* name, const char* title) { + static Frame* settingsGenericWindow( + const char* name, + const char* title, + void (*defaults_callback)(Button&), + void (*discard_callback)(Button&), + void (*confirm_callback)(Button&)) + { auto dimmer = main_menu_frame->addFrame("dimmer"); dimmer->setSize(SDL_Rect{0, 0, Frame::virtualScreenX, Frame::virtualScreenY}); dimmer->setActualSize(dimmer->getSize()); @@ -1655,6 +1782,7 @@ namespace MainMenu { subwindow->setTickCallback([](Widget& widget){ auto frame = static_cast(&widget); updateSettingSelection(*frame); + updateSliderArrows(*frame); }); auto rocks = subwindow->addImage( @@ -1693,6 +1821,24 @@ namespace MainMenu { slider->updateHandlePosition(); }); + auto sliderLeft = subwindow->addImage( + SDL_Rect{0, 0, 30, 44}, + 0xffffffff, + "images/ui/Main Menus/Settings/AutoSort/AutoSort_SliderBox_Left00.png", + "slider_left" + ); + sliderLeft->disabled = true; + sliderLeft->ontop = true; + + auto sliderRight = subwindow->addImage( + SDL_Rect{0, 0, 30, 44}, + 0xffffffff, + "images/ui/Main Menus/Settings/AutoSort/AutoSort_SliderBox_Right00.png", + "slider_right" + ); + sliderRight->disabled = true; + sliderRight->ontop = true; + auto defaults = window->addButton("restore_defaults"); defaults->setBackground("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonStandard00.png"); defaults->setColor(makeColor(127, 127, 127, 255)); @@ -1707,6 +1853,7 @@ namespace MainMenu { defaults->addWidgetAction("MenuStart", "confirm_and_exit"); defaults->addWidgetAction("MenuAlt1", "restore_defaults"); defaults->setWidgetRight("discard_and_exit"); + defaults->setCallback(defaults_callback); auto discard = window->addButton("discard_and_exit"); discard->setBackground("images/ui/Main Menus/Settings/GenericWindow/UI_MM14_ButtonStandard00.png"); @@ -1722,12 +1869,7 @@ namespace MainMenu { 164, 62} ); - discard->setCallback([](Button& button){ - soundCancel(); - auto parent = static_cast(button.getParent()); assert(parent); - auto parent_background = static_cast(parent->getParent()); assert(parent_background); - parent_background->removeSelf(); - }); + discard->setCallback(discard_callback); discard->setWidgetSearchParent(name); discard->setWidgetBack("discard_and_exit"); discard->addWidgetAction("MenuStart", "confirm_and_exit"); @@ -1744,12 +1886,7 @@ namespace MainMenu { confirm->setText("Confirm\n& Exit"); confirm->setFont(smallfont_outline); confirm->setSize(SDL_Rect{504, 630, 164, 62}); - confirm->setCallback([](Button& button){ - soundActivate(); - auto parent = static_cast(button.getParent()); assert(parent); - auto parent_background = static_cast(parent->getParent()); assert(parent_background); - parent_background->removeSelf(); - }); + confirm->setCallback(confirm_callback); confirm->setWidgetSearchParent(name); confirm->setWidgetBack("discard_and_exit"); confirm->addWidgetAction("MenuStart", "confirm_and_exit"); @@ -1760,9 +1897,229 @@ namespace MainMenu { return window; } - static void settingsBindings(Button& button) { + static void settingsMinimap(Button& button) { soundActivate(); - auto window = settingsGenericWindow("bindings", "BINDINGS"); assert(window); + auto window = settingsGenericWindow("minimap", "MINIMAP", + [](Button& button){ // restore defaults + auto parent = static_cast(button.getParent()); assert(parent); + auto parent_background = static_cast(parent->getParent()); assert(parent_background); + parent_background->removeSelf(); + allSettings.minimap = Minimap::reset(); + settingsMinimap(button); + }, + [](Button& button){ // discard & exit + soundCancel(); + auto parent = static_cast(button.getParent()); assert(parent); + auto parent_background = static_cast(parent->getParent()); assert(parent_background); + parent_background->removeSelf(); + allSettings.minimap.load(); + }, + [](Button& button){ // confirm & exit + soundActivate(); + auto parent = static_cast(button.getParent()); assert(parent); + auto parent_background = static_cast(parent->getParent()); assert(parent_background); + parent_background->removeSelf(); + allSettings.minimap.save(); + }); + assert(window); + auto subwindow = window->findFrame("subwindow"); assert(subwindow); + int y = 0; + + y += settingsAddSubHeader(*subwindow, y, "scale_header", "Scale", true); + + y += settingsAddSlider(*subwindow, y, "map_scale", "Map scale", + "Scale the map to be larger or smaller.", + 100, 100, 200, true, nullptr); + + y += settingsAddSlider(*subwindow, y, "icon_scale", "Icon scale", + "Scale the size of icons on the map (such as players and allies)", + 100, 50, 200, true, nullptr); + + y += settingsAddSubHeader(*subwindow, y, "transparency_header", "Transparency", true); + + y += settingsAddSlider(*subwindow, y, "foreground_opacity", "Foreground opacity", + "Set the opacity of the minimap's foreground.", + 100, 0, 100, true, nullptr); + + y += settingsAddSlider(*subwindow, y, "background_opacity", "Background opacity", + "Set the opacity of the minimap's background.", + 100, 0, 100, true, nullptr); + + hookSettings(*subwindow, + {{Setting::Type::Slider, "map_scale"}, + {Setting::Type::Slider, "icon_scale"}, + {Setting::Type::Slider, "foreground_opacity"}, + {Setting::Type::Slider, "background_opacity"}, + }); + settingsSubwindowFinalize(*subwindow, y); + settingsSelect(*subwindow, {Setting::Type::Slider, "map_scale"}); + } + + static void settingsMessages(Button& button) { + soundActivate(); + auto window = settingsGenericWindow("messages", "MESSAGES", + [](Button& button){ // restore defaults + auto parent = static_cast(button.getParent()); assert(parent); + auto parent_background = static_cast(parent->getParent()); assert(parent_background); + parent_background->removeSelf(); + allSettings.show_messages = Messages::reset(); + settingsMessages(button); + }, + [](Button& button){ // discard & exit + soundCancel(); + auto parent = static_cast(button.getParent()); assert(parent); + auto parent_background = static_cast(parent->getParent()); assert(parent_background); + parent_background->removeSelf(); + allSettings.show_messages.load(); + }, + [](Button& button){ // confirm & exit + soundActivate(); + auto parent = static_cast(button.getParent()); assert(parent); + auto parent_background = static_cast(parent->getParent()); assert(parent_background); + parent_background->removeSelf(); + allSettings.show_messages.save(); + }); + assert(window); + auto subwindow = window->findFrame("subwindow"); assert(subwindow); + int y = 0; + + y += settingsAddSubHeader(*subwindow, y, "categories_header", "Categories", true); + + // TODO bind these to actual settings + + y += settingsAddBooleanOption(*subwindow, y, "messages_combat", "Combat messages", + "Enable report of damage received or given in combat.", + true, nullptr); + + y += settingsAddBooleanOption(*subwindow, y, "messages_status", "Status messages", + "Enable report of player character status changes and other passive effects.", + true, nullptr); + + y += settingsAddBooleanOption(*subwindow, y, "messages_inventory", "Inventory messages", + "Enable report of inventory and item appraisal messages.", + true, nullptr); + + y += settingsAddBooleanOption(*subwindow, y, "messages_equipment", "Equipment messages", + "Enable report of player equipment changes.", + true, nullptr); + + y += settingsAddBooleanOption(*subwindow, y, "messages_world", "World messages", + "Enable report of diegetic messages, such as speech and text.", + true, nullptr); + + y += settingsAddBooleanOption(*subwindow, y, "messages_chat", "Player chat", + "Enable multiplayer chat.", + true, nullptr); + + y += settingsAddBooleanOption(*subwindow, y, "messages_progression", "Progression messages", + "Enable report of player character progression messages (ie level-ups).", + true, nullptr); + + y += settingsAddBooleanOption(*subwindow, y, "messages_interaction", "Interaction messages", + "Enable report of player interactions with the world.", + true, nullptr); + + y += settingsAddBooleanOption(*subwindow, y, "messages_inspection", "Inspection messages", + "Enable player inspections of world objects.", + true, nullptr); + + hookSettings(*subwindow, + {{Setting::Type::Boolean, "messages_combat"}, + {Setting::Type::Boolean, "messages_status"}, + {Setting::Type::Boolean, "messages_inventory"}, + {Setting::Type::Boolean, "messages_equipment"}, + {Setting::Type::Boolean, "messages_world"}, + {Setting::Type::Boolean, "messages_chat"}, + {Setting::Type::Boolean, "messages_progression"}, + {Setting::Type::Boolean, "messages_interaction"}, + {Setting::Type::Boolean, "messages_inspection"}, + }); + settingsSubwindowFinalize(*subwindow, y); + settingsSelect(*subwindow, {Setting::Type::Boolean, "messages_combat"}); + } + + static const char* getDeviceNameForIndex(int index) { + switch (index) { + case 0: return "KB & Mouse"; + case 1: return "Gamepad 1"; + case 2: return "Gamepad 2"; + case 3: return "Gamepad 3"; + case 4: return "Gamepad 4"; + case 5: return "Joystick 1"; + case 6: return "Joystick 2"; + case 7: return "Joystick 3"; + case 8: return "Joystick 4"; + default: return "Unknown"; + } + } + + static int getDeviceIndexForName(const char* name) { + if (strcmp(name, "KB & Mouse") == 0) { return 0; } + if (strcmp(name, "Gamepad 1") == 0) { return 1; } + if (strcmp(name, "Gamepad 2") == 0) { return 2; } + if (strcmp(name, "Gamepad 3") == 0) { return 3; } + if (strcmp(name, "Gamepad 4") == 0) { return 4; } + if (strcmp(name, "Joystick 1") == 0) { return 5; } + if (strcmp(name, "Joystick 2") == 0) { return 6; } + if (strcmp(name, "Joystick 3") == 0) { return 7; } + if (strcmp(name, "Joystick 4") == 0) { return 8; } + else { return -1; } + } + + static bool settingsBind(int player_index, int device_index, const char* binding, const char* input) { + assert(binding); + auto& bindings = + device_index == 0 ? allSettings.bindings.kb_mouse_bindings[player_index]: + device_index >= 1 && device_index <= 4 ? allSettings.bindings.gamepad_bindings[player_index]: + allSettings.bindings.joystick_bindings[player_index]; + if (input == nullptr) { + bindings.erase(binding); + return true; + } else { + std::string input_to_store; + if (device_index >= 1 && device_index <= 4 && strncmp(input, "Pad", 3) == 0) { + input_to_store = input + 4; + } + else if (device_index >= 5 && device_index <= 8 && strncmp(input, "Joy", 3) == 0) { + input_to_store = input + 4; + } + else if (device_index == 0 && strncmp(input, "Pad", 3) && strncmp(input, "Joy", 3)) { + input_to_store = input; + } + if (input_to_store.empty()) { + return false; + } else { + bindings.insert_or_assign(binding, input_to_store.c_str()); + return true; + } + } + } + + static void settingsBindings(int player_index, Setting setting_to_select) { + soundActivate(); + auto window = settingsGenericWindow("bindings", "BINDINGS", + [](Button& button){ // restore defaults + auto parent = static_cast(button.getParent()); assert(parent); + auto parent_background = static_cast(parent->getParent()); assert(parent_background); + parent_background->removeSelf(); + allSettings.bindings = Bindings::reset(); + settingsBindings(0, {Setting::Type::Dropdown, "player_dropdown_button"}); + }, + [](Button& button){ // discard & exit + soundCancel(); + auto parent = static_cast(button.getParent()); assert(parent); + auto parent_background = static_cast(parent->getParent()); assert(parent_background); + parent_background->removeSelf(); + allSettings.bindings.load(); + }, + [](Button& button){ // confirm & exit + soundActivate(); + auto parent = static_cast(button.getParent()); assert(parent); + auto parent_background = static_cast(parent->getParent()); assert(parent_background); + parent_background->removeSelf(); + allSettings.bindings.save(); + }); + assert(window); auto subwindow = window->findFrame("subwindow"); assert(subwindow); int y = 0; @@ -1797,32 +2154,59 @@ namespace MainMenu { static Button* bound_button = nullptr; static std::string bound_binding = ""; static std::string bound_input = ""; - static int bound_player = -1; + static int bound_player; + static int bound_device; + bound_player = player_index; + bound_device = allSettings.bindings.devices[bound_player]; bind_mode = false; y += settingsAddSubHeader(*subwindow, y, "bindings_header", "Profiles", true); + std::string player_str = "Player " + std::to_string(player_index + 1); y += settingsAddDropdown(*subwindow, y, "player_dropdown_button", "Player", "Select the player whose controls you wish to customize.", - {"Player 1", "Player 2", "Player 3", "Player 4"}, - "Player 1", [](Button& button){ + {"Player 1", "Player 2", "Player 3", "Player 4"}, player_str.c_str(), + [](Button& button){ soundActivate(); - settingsOpenDropdown(button, "player_dropdown", true, nullptr); + settingsOpenDropdown(button, "player_dropdown", true, + [](Frame::entry_t& entry){ + soundActivate(); + auto parent = main_menu_frame->findFrame("bindings"); + auto parent_background = static_cast(parent->getParent()); assert(parent_background); + parent_background->removeSelf(); + int player_index = (int)(entry.name.back() - '1'); + settingsBindings(player_index, {Setting::Type::Dropdown, "player_dropdown_button"}); + }); }); - int num_controllers = (int)std::max(Input::gameControllers.size(), Input::joysticks.size()); - num_controllers = std::min(std::max(1, num_controllers + 1), 5); std::set locked_controllers; - for (int i = num_controllers; i < 5; ++i) { + for (int i = 1 + Input::gameControllers.size(); i < 5; ++i) { + locked_controllers.emplace(i); + } + for (int i = 5 + Input::joysticks.size(); i < 9; ++i) { locked_controllers.emplace(i); } + std::vector devices; + devices.reserve(9); + for (int i = 0; i < 9; ++i) { + devices.push_back(getDeviceNameForIndex(i)); + } + y += settingsAddDropdown(*subwindow, y, "device_dropdown_button", "Device", - "Select a controller for the given player.", - {"KB & Mouse", "Gamepad 1", "Gamepad 2", "Gamepad 3", "Gamepad 4"}, - "KB & Mouse", [](Button& button){ + "Select a controller for the given player.", devices, getDeviceNameForIndex(allSettings.bindings.devices[player_index]), + [](Button& button){ soundActivate(); - settingsOpenDropdown(button, "device_dropdown", true, nullptr); + settingsOpenDropdown(button, "device_dropdown", false, + [](Frame::entry_t& entry){ + soundActivate(); + auto parent = main_menu_frame->findFrame("bindings"); + auto parent_background = static_cast(parent->getParent()); assert(parent_background); + parent_background->removeSelf(); + int device_index = getDeviceIndexForName(entry.text.c_str()); + allSettings.bindings.devices[bound_player] = device_index; + settingsBindings(bound_player, {Setting::Type::Dropdown, "device_dropdown_button"}); + }); }, locked_controllers); y += settingsAddSubHeader(*subwindow, y, "bindings_header", "Bindings", true); @@ -1830,15 +2214,14 @@ namespace MainMenu { for (auto& binding : bindings) { char tip[256]; snprintf(tip, sizeof(tip), "Bind an input device to %s", binding.name); - y += settingsAddBinding(*subwindow, y, binding.name, tip, + y += settingsAddBinding(*subwindow, y, player_index, binding.name, tip, [](Button& button){ soundToggle(); auto& name = std::string(button.getName()); bind_mode = true; bound_button = &button; bound_input = button.getText(); - bound_binding = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_binding_button") - 1) - (sizeof("setting_") - 1));; - bound_player = 0; + bound_binding = name.substr(sizeof("setting_") - 1, name.size() - (sizeof("_binding_button") - 1) - (sizeof("setting_") - 1)); button.setText(". . ."); auto subwindow = static_cast(button.getParent()); assert(subwindow); auto settings = static_cast(subwindow->getParent()); assert(settings); @@ -1876,19 +2259,29 @@ namespace MainMenu { tooltip->setText(buf); Input::keys[SDL_SCANCODE_ESCAPE] = 0; } else if (Input::lastInputOfAnyKind == "Delete") { - bound_button->setText(""); + (void)settingsBind(bound_player, bound_device, bound_binding.c_str(), nullptr); + bound_button->setText("[unbound]"); char buf[256]; snprintf(buf, sizeof(buf), "Deleted \"%s\" binding.", bound_binding.c_str()); tooltip->setText(buf); - //Input::inputs[bound_player].bind(bound_binding.c_str(), nullptr); } else { - bound_button->setText(Input::lastInputOfAnyKind.c_str()); + bool result = settingsBind(bound_player, bound_device, bound_binding.c_str(), Input::lastInputOfAnyKind.c_str()); + if (!result) { + goto bind_failed; + } + auto begin = Input::lastInputOfAnyKind.substr(0, 3); + std:: string newinput = begin == "Pad" || begin == "Joy" ? + Input::lastInputOfAnyKind.substr(4) : Input::lastInputOfAnyKind; + bound_button->setText(newinput.c_str()); char buf[256]; - snprintf(buf, sizeof(buf), "Bound \"%s\" to \"%s\"", bound_binding.c_str(), Input::lastInputOfAnyKind.c_str()); + snprintf(buf, sizeof(buf), "Bound \"%s\" to \"%s\"", bound_binding.c_str(), newinput.c_str()); tooltip->setText(buf); - //Input::inputs[bound_player].bind(bound_binding.c_str(), Input::lastInputOfAnyKind.c_str()); } bound_button = nullptr; + bind_failed: + // fixes a bug where these are not released after being used + Input::mouseButtons[SDL_BUTTON_WHEELDOWN] = 0; + Input::mouseButtons[SDL_BUTTON_WHEELUP] = 0; } else if (!bound_button && !Input::mouseButtons[SDL_BUTTON_LEFT] && @@ -1910,7 +2303,6 @@ namespace MainMenu { Input::defaultBindings(); bound_binding = ""; - bound_player = -1; bind_mode = false; } } @@ -1919,12 +2311,11 @@ namespace MainMenu { hookSettings(*subwindow, {{Setting::Type::Dropdown, "player_dropdown_button"}, {Setting::Type::Dropdown, "device_dropdown_button"}, - //{Setting::Type::Field, "preset_entry"}, bindings[0], }); hookSettings(*subwindow, bindings); settingsSubwindowFinalize(*subwindow, y); - settingsSelect(*subwindow, bindings.front()); + settingsSelect(*subwindow, setting_to_select); } void settingsUI(Button& button) { @@ -1932,16 +2323,15 @@ namespace MainMenu { if ((settings_subwindow = settingsSubwindowSetup(button)) == 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::BooleanWithCustomize, "add_items_to_hotbar"}); + settingsSelect(*settings_subwindow, {Setting::Type::Boolean, "add_items_to_hotbar"}); return; } int y = 0; y += settingsAddSubHeader(*settings_subwindow, y, "inventory", "Inventory Options"); - y += settingsAddBooleanWithCustomizeOption(*settings_subwindow, y, "add_items_to_hotbar", "Add Items to Hotbar", + y += settingsAddBooleanOption(*settings_subwindow, y, "add_items_to_hotbar", "Add Items to Hotbar", "Automatically fill the hotbar with recently collected items.", - allSettings.add_items_to_hotbar_enabled, [](Button& button){soundToggle(); allSettings.add_items_to_hotbar_enabled = button.isPressed();}, - settingsCustomizeInventorySorting); + allSettings.add_items_to_hotbar_enabled, [](Button& button){soundToggle(); allSettings.add_items_to_hotbar_enabled = button.isPressed();}); y += settingsAddCustomize(*settings_subwindow, y, "inventory_sorting", "Inventory Sorting", "Customize the way items are automatically sorted in your inventory.", settingsCustomizeInventorySorting); @@ -1954,11 +2344,11 @@ namespace MainMenu { y += settingsAddSubHeader(*settings_subwindow, y, "hud", "HUD Options"); y += settingsAddCustomize(*settings_subwindow, y, "minimap_settings", "Minimap Settings", "Customize the appearance of the in-game minimap.", - nullptr); + [](Button& button){allSettings.minimap = Minimap::load(); settingsMinimap(button);}); y += settingsAddBooleanWithCustomizeOption(*settings_subwindow, y, "show_messages", "Show Messages", "Customize which messages will be logged to the player, if any.", allSettings.show_messages_enabled, [](Button& button){soundToggle(); allSettings.show_messages_enabled = button.isPressed();}, - nullptr); + [](Button& button){allSettings.show_messages = Messages::load(); settingsMessages(button);}); y += settingsAddBooleanOption(*settings_subwindow, y, "show_player_nametags", "Show Player Nametags", "Display the name of each player character above their avatar.", allSettings.show_player_nametags_enabled, [](Button& button){soundToggle(); allSettings.show_player_nametags_enabled = button.isPressed();}); @@ -1973,7 +2363,7 @@ namespace MainMenu { #ifndef NINTENDO hookSettings(*settings_subwindow, - {{Setting::Type::BooleanWithCustomize, "add_items_to_hotbar"}, + {{Setting::Type::Boolean, "add_items_to_hotbar"}, {Setting::Type::Customize, "inventory_sorting"}, {Setting::Type::Boolean, "use_on_release"}, {Setting::Type::Customize, "minimap_settings"}, @@ -1983,7 +2373,7 @@ namespace MainMenu { {Setting::Type::Boolean, "show_ip_address"}}); #else hookSettings(*settings_subwindow, - {{Setting::Type::BooleanWithCustomize, "add_items_to_hotbar"}, + {{Setting::Type::Boolean, "add_items_to_hotbar"}, {Setting::Type::Customize, "inventory_sorting"}, {Setting::Type::Customize, "minimap_settings"}, {Setting::Type::BooleanWithCustomize, "show_messages"}, @@ -1992,7 +2382,7 @@ namespace MainMenu { #endif settingsSubwindowFinalize(*settings_subwindow, y); - settingsSelect(*settings_subwindow, {Setting::Type::BooleanWithCustomize, "add_items_to_hotbar"}); + settingsSelect(*settings_subwindow, {Setting::Type::Boolean, "add_items_to_hotbar"}); } void settingsVideo(Button& button) { @@ -2190,7 +2580,7 @@ namespace MainMenu { y += settingsAddSubHeader(*settings_subwindow, y, "general", "General Settings"); y += settingsAddCustomize(*settings_subwindow, y, "bindings", "Bindings", "Modify controls for mouse, keyboard, gamepads, and other peripherals.", - settingsBindings); + [](Button&){allSettings.bindings = Bindings::load(); settingsBindings(0, {Setting::Type::Dropdown, "player_dropdown_button"});}); y += settingsAddSubHeader(*settings_subwindow, y, "mouse_and_keyboard", "Mouse & Keyboard"); y += settingsAddBooleanOption(*settings_subwindow, y, "numkeys_in_inventory", "Number Keys in Inventory", @@ -2212,6 +2602,7 @@ namespace MainMenu { #ifdef NINTENDO y += settingsAddSubHeader(*settings_subwindow, y, "gamepad", "Controller Settings"); + // TODO y += settingsAddCustomize(*settings_subwindow, y, "bindings", "Bindings", "Modify controller bindings.", nullptr); @@ -4733,7 +5124,9 @@ namespace MainMenu { allSettings.add_items_to_hotbar_enabled = auto_hotbar_new_items; allSettings.inventory_sorting = InventorySorting::load(); allSettings.use_on_release_enabled = !right_click_protect; + allSettings.minimap = Minimap::load(); allSettings.show_messages_enabled = !disable_messages; + allSettings.show_messages = Messages::load(); allSettings.show_player_nametags_enabled = !hide_playertags; allSettings.show_hud_enabled = !nohud; allSettings.show_ip_address_enabled = !broadcast; @@ -4759,6 +5152,7 @@ namespace MainMenu { allSettings.minimap_pings_enabled = !minimapPingMute; allSettings.player_monster_sounds_enabled = !mute_player_monster_sounds; allSettings.out_of_focus_audio_enabled = !mute_audio_on_focus_lost; + allSettings.bindings = Bindings::load(); allSettings.numkeys_in_inventory_enabled = hotbar_numkey_quick_add; allSettings.mouse_sensitivity = mousespeed; allSettings.reverse_mouse_enabled = reversemouse; diff --git a/src/ui/MainMenu.hpp b/src/ui/MainMenu.hpp index 95276e005..c55b67d0d 100644 --- a/src/ui/MainMenu.hpp +++ b/src/ui/MainMenu.hpp @@ -1,5 +1,10 @@ #pragma once +#include +#include +#include "../main.hpp" +#include "../json.hpp" + class Button; namespace MainMenu { @@ -38,13 +43,106 @@ namespace MainMenu { static inline InventorySorting reset(); }; + struct Bindings { + int devices[4]; + std::unordered_map kb_mouse_bindings[4]; + std::unordered_map gamepad_bindings[4]; + std::unordered_map joystick_bindings[4]; + inline void save(); + static inline Bindings load(); + static inline Bindings reset(); + void serialize(FileInterface* file) { + Uint32 num_players = 4; + file->propertyName("players"); + file->beginArray(num_players); + for (int c = 0; c < std::min(num_players, (Uint32)4); ++c) { + file->beginObject(); + file->property("device", devices[c]); + for (int j = 0; j < 3; ++j) { + auto& bindings = + j == 0 ? kb_mouse_bindings[c]: + j == 1 ? gamepad_bindings[c]: + joystick_bindings[c]; + file->propertyName( + j == 0 ? "kb_mouse_bindings": + j == 1 ? "gamepad_bindings": + "joystick_bindings"); + if (file->isReading()) { + bindings.clear(); + } + Uint32 count = bindings.size(); + file->beginArray(count); + if (file->isReading()) { + for (Uint32 index = 0; index < count; ++index) { + file->beginObject(); + std::string binding; + file->property("binding", binding); + std::string input; + file->property("input", input); + bindings.emplace(binding, input); + file->endObject(); + } + } else { + for (auto& bind : bindings) { + file->beginObject(); + std::string binding = bind.first; + file->property("binding", binding); + std::string input = bind.second; + file->property("input", input); + file->endObject(); + } + } + file->endArray(); + } + file->endObject(); + } + file->endArray(); + } + }; + + struct Minimap { + int map_scale = 2; + int icon_scale = 2; + int foreground_opacity = 100; + int background_opacity = 100; + inline void save(); + static inline Minimap load(); + static inline Minimap reset(); + }; + + struct Messages { + bool combat = true; + bool status = true; + bool inventory = true; + bool equipment = true; + bool world = true; + bool chat = true; + bool progression = true; + bool interaction = true; + bool inspection = true; + inline void save(); + static inline Messages load(); + static inline Messages reset(); + void serialize(FileInterface* file) { + file->property("combat", combat); + file->property("status", status); + file->property("inventory", inventory); + file->property("equipment", equipment); + file->property("world", world); + file->property("chat", chat); + file->property("progression", progression); + file->property("interaction", interaction); + file->property("inspection", inspection); + } + }; + struct AllSettings { bool add_items_to_hotbar_enabled; InventorySorting inventory_sorting; bool use_on_release_enabled; - //Whatever minimap; + Minimap minimap; bool show_messages_enabled; - //Whatever show_messages; + Messages show_messages; bool show_player_nametags_enabled; bool show_hud_enabled; bool show_ip_address_enabled; @@ -70,7 +168,7 @@ namespace MainMenu { bool minimap_pings_enabled; bool player_monster_sounds_enabled; bool out_of_focus_audio_enabled; - //Whatever bindings; + Bindings bindings; bool numkeys_in_inventory_enabled; float mouse_sensitivity; bool reverse_mouse_enabled; From 58ace3e8daa4738919a88383ed0156d514172b78 Mon Sep 17 00:00:00 2001 From: WALL OF JUSTICE <-> Date: Sat, 30 Oct 2021 02:00:09 +1100 Subject: [PATCH 26/39] * move skillsheet in front of inventory * skillsheet allow resizing - add compact view WIP --- src/game.cpp | 2 +- src/ui/GameUI.cpp | 677 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 506 insertions(+), 173 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index b3c5ac8dd..da919ef50 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -4149,9 +4149,9 @@ void ingameHud() players[player]->characterSheet.processCharacterSheet(); players[player]->inventoryUI.updateSelectedItemAnimation(); players[player]->inventoryUI.updateInventoryItemTooltip(); - players[player]->skillSheet.processSkillSheet(); players[player]->hotbar.processHotbar(); players[player]->inventoryUI.processInventory(); + players[player]->skillSheet.processSkillSheet(); players[player]->inventoryUI.updateCursor(); players[player]->hotbar.updateCursor(); players[player]->hud.updateCursor(); diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 2fe22365a..2e29a4c1e 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -79,6 +79,87 @@ std::string EnemyBarSettings_t::getEnemyBarSpriteName(Entity* entity) return "default"; } +enum ImageIndexes9x9 : int +{ + TOP_LEFT, + TOP_RIGHT, + TOP, + MIDDLE_LEFT, + MIDDLE_RIGHT, + MIDDLE, + BOTTOM_LEFT, + BOTTOM_RIGHT, + BOTTOM +}; + +const std::vector skillsheetEffectBackgroundImages = +{ + "9x9 bg top left", + "9x9 bg top right", + "9x9 bg top middle", + "9x9 bg left", + "9x9 bg right", + "9x9 bg middle", + "9x9 bg bottom left", + "9x9 bg bottom right", + "9x9 bg bottom middle" +}; + +void imageSetWidthHeight9x9(Frame* container, const std::vector& imgNames) +{ + for ( auto& img : imgNames ) + { + if ( auto i = container->findImage(img.c_str()) ) + { + if ( auto imgGet = Image::get(i->path.c_str()) ) + { + i->pos.w = imgGet->getWidth(); + i->pos.h = imgGet->getHeight(); + } + } + } +} + +// for 9x9 images stretched to fit a container +void imageResizeToContainer9x9(Frame* container, SDL_Rect dimensionsToFill, const std::vector& imgNames) +{ + assert(imgNames.size() == 9); + // adjust inner background image elements + auto tl = container->findImage(imgNames[TOP_LEFT].c_str()); + tl->pos.x = dimensionsToFill.x; + tl->pos.y = dimensionsToFill.y; + auto tr = container->findImage(imgNames[TOP_RIGHT].c_str()); + tr->pos.x = dimensionsToFill.w - tr->pos.w; + tr->pos.y = dimensionsToFill.y; + auto tm = container->findImage(imgNames[TOP].c_str()); + tm->pos.x = tl->pos.x + tl->pos.w; + tm->pos.y = dimensionsToFill.y; + tm->pos.w = dimensionsToFill.w - tr->pos.w - tl->pos.w; + auto bl = container->findImage(imgNames[BOTTOM_LEFT].c_str()); + bl->pos.x = dimensionsToFill.x; + bl->pos.y = dimensionsToFill.h - bl->pos.h; + auto br = container->findImage(imgNames[BOTTOM_RIGHT].c_str()); + br->pos.x = dimensionsToFill.w - br->pos.w; + br->pos.y = bl->pos.y; + auto bm = container->findImage(imgNames[BOTTOM].c_str()); + bm->pos.x = bl->pos.x + bl->pos.w; + bm->pos.w = dimensionsToFill.w - bl->pos.w - br->pos.w; + bm->pos.y = bl->pos.y; + auto ml = container->findImage(imgNames[MIDDLE_LEFT].c_str()); + ml->pos.x = dimensionsToFill.x; + ml->pos.y = tl->pos.y + tl->pos.h; + ml->pos.h = dimensionsToFill.h - dimensionsToFill.y - bl->pos.h - tl->pos.h; + auto mr = container->findImage(imgNames[MIDDLE_RIGHT].c_str()); + mr->pos.x = dimensionsToFill.w - mr->pos.w; + mr->pos.y = ml->pos.y; + mr->pos.h = ml->pos.h; + auto mm = container->findImage(imgNames[MIDDLE].c_str()); + mm->pos.x = ml->pos.x + ml->pos.w; + mm->pos.y = ml->pos.y; + mm->pos.w = dimensionsToFill.w - ml->pos.w - mr->pos.w; + mm->pos.h = ml->pos.h; +} + void createHPMPBars(const int player) { auto& hud_t = players[player]->hud; @@ -6688,25 +6769,65 @@ void Player::SkillSheet_t::createSkillSheet() const int width = 684; const int height = 404; skillBackground->setSize(SDL_Rect{ frame->getSize().x, frame->getSize().y, width, height }); - auto bgImg = skillBackground->addImage(SDL_Rect{ 0, 0, width, height }, 0xFFFFFFFF, - "images/ui/SkillSheet/UI_Skills_Window_01.png", "skills img"); + /*auto bgImg = skillBackground->addImage(SDL_Rect{ 0, 0, width, height }, 0xFFFFFFFF, + "images/ui/SkillSheet/UI_Skills_Window_02.png", "skills img");*/ + + Frame* allSkillEntriesLeft = skillBackground->addFrame("skill entries frame left"); + Frame* allSkillEntriesRight = skillBackground->addFrame("skill entries frame right"); + //allSkillEntries->setSize(SDL_Rect{ 0, 0, skillBackground->getSize().w, skillBackground->getSize().h }); + + SDL_Rect allSkillEntriesPosLeft{ 0, 0, 182, skillBackground->getSize().h }; + SDL_Rect allSkillEntriesPosRight{ skillBackground->getSize().w - 182, 0, 182, skillBackground->getSize().h }; + allSkillEntriesLeft->setSize(allSkillEntriesPosLeft); + allSkillEntriesRight->setSize(allSkillEntriesPosRight); + + allSkillEntriesLeft->addImage(SDL_Rect{ 0, 12, 182, 376 }, + 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_Window_Left_03.png", "bg wing left"); + allSkillEntriesRight->addImage(SDL_Rect{ 0, 12, 182, 376 }, + 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_Window_Right_03.png", "bg wing right"); + + auto skillBackgroundImagesFrame = skillBackground->addFrame("skills bg images"); + skillBackgroundImagesFrame->setSize(SDL_Rect{ 0, 0, skillBackground->getSize().w, skillBackground->getSize().h }); + { + Uint32 color = makeColor(255, 255, 255, 255); + skillBackgroundImagesFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_Window_TL_03.png", skillsheetEffectBackgroundImages[TOP_LEFT].c_str()); + skillBackgroundImagesFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_Window_TR_03.png", skillsheetEffectBackgroundImages[TOP_RIGHT].c_str()); + skillBackgroundImagesFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_Window_T_03.png", skillsheetEffectBackgroundImages[TOP].c_str()); + skillBackgroundImagesFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_Window_L_03.png", skillsheetEffectBackgroundImages[MIDDLE_LEFT].c_str()); + skillBackgroundImagesFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_Window_R_03.png", skillsheetEffectBackgroundImages[MIDDLE_RIGHT].c_str()); + skillBackgroundImagesFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + makeColor(0, 0, 0, 255), "images/system/white.png", skillsheetEffectBackgroundImages[MIDDLE].c_str()); + skillBackgroundImagesFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_Window_BL_03.png", skillsheetEffectBackgroundImages[BOTTOM_LEFT].c_str()); + skillBackgroundImagesFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_Window_BR_03.png", skillsheetEffectBackgroundImages[BOTTOM_RIGHT].c_str()); + skillBackgroundImagesFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_Window_B_03.png", skillsheetEffectBackgroundImages[BOTTOM].c_str()); + imageSetWidthHeight9x9(skillBackgroundImagesFrame, skillsheetEffectBackgroundImages); + + skillBackgroundImagesFrame->addImage(SDL_Rect{ 0, 0, 78, 18 }, + color, "images/ui/SkillSheet/UI_Skills_Window_Flourish_T.png", "flourish top"); + skillBackgroundImagesFrame->addImage(SDL_Rect{ 0, 0, 34, 14 }, + color, "images/ui/SkillSheet/UI_Skills_Window_Flourish_B.png", "flourish bottom"); + } const int skillEntryStartY = 38; - SDL_Rect skillEntryPos{ 0, skillEntryStartY, 218, 40 }; - skillEntryPos.x = skillBackground->getSize().w - skillEntryPos.w; - SDL_Rect skillSelectorPos{ 6, 2, 176, 32 }; - Frame* allSkillEntries = skillBackground->addFrame("skill entries frame"); - allSkillEntries->setSize(SDL_Rect{ 0, 0, skillBackground->getSize().w, skillBackground->getSize().h }); - + SDL_Rect skillEntryPos{ 0, skillEntryStartY, 182, 40 }; + SDL_Rect skillSelectorPos{ 0, 2, 146, 32 }; const char* boldFont = "fonts/pixel_maz_multiline.ttf#16#2"; const char* numberFont = "fonts/pixelmix.ttf#16"; const char* titleFont = "fonts/pixel_maz_multiline.ttf#24#2"; const char* descFont = "fonts/pixel_maz_multiline.ttf#16#2"; - for ( int i = 0; i < 8; ++i ) // left side + for ( int i = 0; i < 8; ++i ) { char skillname[32]; snprintf(skillname, sizeof(skillname), "skill %d", i); - Frame* entry = allSkillEntries->addFrame(skillname); + Frame* entry = allSkillEntriesRight->addFrame(skillname); entry->setSize(skillEntryPos); entry->addImage(skillSelectorPos, 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_SkillSelector_00.png", "selector img"); SDL_Rect imgBgPos{ skillEntryPos.w - 36, 0, 36, 36 }; @@ -6721,7 +6842,7 @@ void Player::SkillSheet_t::createSkillSheet() } skillEntryPos.y += skillEntryPos.h; - SDL_Rect profNamePos{ statPos.x + statPos.w + 4, 4, 98, 28 }; + SDL_Rect profNamePos{ statPos.x + statPos.w - 32, 4, 98, 28 }; Field* profName = entry->addField("skill name", 64); profName->setSize(profNamePos); profName->setHJustify(Field::justify_t::RIGHT); @@ -6737,7 +6858,7 @@ void Player::SkillSheet_t::createSkillSheet() profName->setText("Default"); } - SDL_Rect profLevelPos{ profNamePos.x + profNamePos.w + 8, profNamePos.y, 34, 28 }; + SDL_Rect profLevelPos{ profNamePos.x + profNamePos.w + 8 - 2, profNamePos.y, 34, 28 }; Field* profLevel = entry->addField("skill level", 16); profLevel->setSize(profLevelPos); profLevel->setHJustify(Field::justify_t::RIGHT); @@ -6747,16 +6868,15 @@ void Player::SkillSheet_t::createSkillSheet() profLevel->setText("0"); } - skillEntryPos.x = skillBackground->getSize().w - skillEntryPos.w; skillEntryPos.x = 0; skillEntryPos.y = skillEntryStartY; skillSelectorPos.x = 36; - for ( int i = 8; i < 16; ++i ) // right side + for ( int i = 8; i < 16; ++i ) { char skillname[32]; snprintf(skillname, sizeof(skillname), "skill %d", i); - Frame* entry = allSkillEntries->addFrame(skillname); + Frame* entry = allSkillEntriesLeft->addFrame(skillname); entry->setSize(skillEntryPos); entry->addImage(skillSelectorPos, 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_SkillSelectorR_00.png", "selector img"); SDL_Rect imgBgPos{ 0, 0, 36, 36 }; @@ -6771,7 +6891,7 @@ void Player::SkillSheet_t::createSkillSheet() } skillEntryPos.y += skillEntryPos.h; - SDL_Rect profNamePos{ statPos.x - 4 - 98, 4, 98, 28 }; + SDL_Rect profNamePos{ statPos.x - 98 + 32, 4, 98, 28 }; Field* profName = entry->addField("skill name", 64); profName->setSize(profNamePos); profName->setHJustify(Field::justify_t::LEFT); @@ -6787,7 +6907,7 @@ void Player::SkillSheet_t::createSkillSheet() profName->setText("Default"); } - SDL_Rect profLevelPos{ profNamePos.x - 12 - 34, profNamePos.y, 34, 28 }; + SDL_Rect profLevelPos{ profNamePos.x - 12 - 32, profNamePos.y, 34, 28 }; Field* profLevel = entry->addField("skill level", 16); profLevel->setSize(profLevelPos); profLevel->setHJustify(Field::justify_t::RIGHT); @@ -6804,9 +6924,10 @@ void Player::SkillSheet_t::createSkillSheet() skillTitleTxt->setText(""); skillTitleTxt->setSize(skillTitlePos); skillTitleTxt->setFont(titleFont); + skillTitleTxt->setOntop(true); skillTitleTxt->setColor(makeColor(201, 162, 100, 255)); - SDL_Rect descPos{ 8, 64, 320, 324 }; + SDL_Rect descPos{ 0, 64, 320, 324 }; descPos.x = skillBackground->getSize().w / 2 - descPos.w / 2; auto skillDescriptionFrame = skillBackground->addFrame("skill desc frame"); skillDescriptionFrame->setSize(descPos); @@ -6818,7 +6939,7 @@ void Player::SkillSheet_t::createSkillSheet() slider->setMinValue(0); slider->setMaxValue(100); slider->setValue(0); - SDL_Rect sliderPos{ descPos.w - 4 - 30, 4, 30, descPos.h - 8 }; + SDL_Rect sliderPos{ descPos.w - 34, 4, 30, descPos.h - 8 }; slider->setRailSize(SDL_Rect{ sliderPos }); slider->setHandleSize(SDL_Rect{ 0, 0, 34, 34 }); slider->setOrientation(Slider::SLIDER_VERTICAL); @@ -6879,6 +7000,35 @@ void Player::SkillSheet_t::createSkillSheet() skillDescriptionTxt->setFont(descFont); skillDescriptionTxt->setSize(txtPos); skillDescriptionTxt->setText(""); + //skillDescriptionTxt->setColor(makeColor(201, 162, 100, 255)); + skillDescriptionTxt->setHJustify(Field::justify_t::CENTER); + skillDescriptionTxt->setOntop(true); + + auto skillDescriptionBgFrame = scrollAreaFrame->addFrame("skill desc bg frame"); + { + //Uint32 color = makeColor(22, 24, 29, 255); + Uint32 color = makeColor(255, 255, 255, 128); + skillDescriptionBgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_LegendBox_TL_00.png", skillsheetEffectBackgroundImages[TOP_LEFT].c_str()); + skillDescriptionBgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_LegendBox_TR_00.png", skillsheetEffectBackgroundImages[TOP_RIGHT].c_str()); + skillDescriptionBgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_LegendBox_T_00.png", skillsheetEffectBackgroundImages[TOP].c_str()); + skillDescriptionBgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_LegendBox_ML_00.png", skillsheetEffectBackgroundImages[MIDDLE_LEFT].c_str()); + skillDescriptionBgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_LegendBox_MR_00.png", skillsheetEffectBackgroundImages[MIDDLE_RIGHT].c_str()); + skillDescriptionBgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_LegendBox_M_00.png", skillsheetEffectBackgroundImages[MIDDLE].c_str()); + skillDescriptionBgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_LegendBox_BL_00.png", skillsheetEffectBackgroundImages[BOTTOM_LEFT].c_str()); + skillDescriptionBgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_LegendBox_BR_00.png", skillsheetEffectBackgroundImages[BOTTOM_RIGHT].c_str()); + skillDescriptionBgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_LegendBox_B_00.png", skillsheetEffectBackgroundImages[BOTTOM].c_str()); + imageSetWidthHeight9x9(skillDescriptionBgFrame, skillsheetEffectBackgroundImages); + } + int txtHeight = skillDescriptionTxt->getNumTextLines() * actualFont->height(true); @@ -6910,69 +7060,27 @@ void Player::SkillSheet_t::createSkillSheet() auto valBgImgFrame = effectFrame->addFrame("effect val bg frame"); valBgImgFrame->setSize(SDL_Rect{ valueX - effectBackgroundXOffset, 0, effectBackgroundWidth, effectFrame->getSize().h - 4}); { - Frame::image_t* tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, - 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_TL00.png", "effect bg top left"); - if ( tmp ) - { - tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); - tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); - } - tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, - 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_TR00.png", "effect bg top right"); - if ( tmp ) - { - tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); - tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); - } - tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, - 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_T00.png", "effect bg top middle"); - if ( tmp ) - { - tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); - tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); - } - tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, - 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_L00.png", "effect bg left"); - if ( tmp ) - { - tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); - tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); - } - tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, - 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_R00.png", "effect bg right"); - if ( tmp ) - { - tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); - tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); - } - tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, - 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_M00.png", "effect bg middle"); - if ( tmp ) - { - tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); - tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); - } - tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, - 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_BL00.png", "effect bg bottom left"); - if ( tmp ) - { - tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); - tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); - } - tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, - 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_BR00.png", "effect bg bottom right"); - if ( tmp ) - { - tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); - tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); - } - tmp = valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, - 0xFFFFFFFF, "images/ui/SkillSheet/UI_Skills_EffectBG_B00.png", "effect bg bottom middle"); - if ( tmp ) - { - tmp->pos.w = Image::get(tmp->path.c_str())->getWidth(); - tmp->pos.h = Image::get(tmp->path.c_str())->getHeight(); - } + Uint32 color = makeColor(51, 33, 26, 255); + valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_EffectBG_TL00.png", skillsheetEffectBackgroundImages[TOP_LEFT].c_str()); + valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_EffectBG_TR00.png", skillsheetEffectBackgroundImages[TOP_RIGHT].c_str()); + valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_EffectBG_T00.png", skillsheetEffectBackgroundImages[TOP].c_str()); + valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_EffectBG_L00.png", skillsheetEffectBackgroundImages[MIDDLE_LEFT].c_str()); + valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_EffectBG_R00.png", skillsheetEffectBackgroundImages[MIDDLE_RIGHT].c_str()); + valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_EffectBG_M00.png", skillsheetEffectBackgroundImages[MIDDLE].c_str()); + valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_EffectBG_BL00.png", skillsheetEffectBackgroundImages[BOTTOM_LEFT].c_str()); + valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_EffectBG_BR00.png", skillsheetEffectBackgroundImages[BOTTOM_RIGHT].c_str()); + valBgImgFrame->addImage(SDL_Rect{ 0, 0, 6, 6 }, + color, "images/ui/SkillSheet/UI_Skills_EffectBG_B00.png", skillsheetEffectBackgroundImages[BOTTOM].c_str()); + + imageSetWidthHeight9x9(valBgImgFrame, skillsheetEffectBackgroundImages); } auto effectTxtFrame = effectFrame->addFrame("effect txt frame"); @@ -7039,6 +7147,22 @@ void Player::SkillSheet_t::createSkillSheet() legendText->setFont(descFont); legendText->setHJustify(Field::justify_t::CENTER); } + + std::string promptFont = "fonts/pixel_maz.ttf#16#2"; + const int promptWidth = 60; + const int promptHeight = 27; + auto promptBack = skillBackground->addField("prompt back txt", 16); + promptBack->setSize(SDL_Rect{ skillBackground->getSize().w - promptWidth - 16, // lower right corner + 0, promptWidth, promptHeight }); + promptBack->setFont(promptFont.c_str()); + promptBack->setHJustify(Field::justify_t::RIGHT); + promptBack->setVJustify(Field::justify_t::CENTER); + promptBack->setText(language[4053]); + + auto promptBackImg = skillBackground->addImage(SDL_Rect{ 0, 0, 0, 0 }, 0xFFFFFFFF, + "", "prompt back img"); + promptBackImg->disabled = true; + /*auto debugRect = skillDescriptionFrame->addImage(txtPos, 0xFFFFFFFF, "images/system/white.png", "");*/ } @@ -7757,6 +7881,12 @@ void Player::SkillSheet_t::selectSkill(int skill) resetSkillDisplay(); } +real_t percentXTmp = 0.0; +real_t percentYTmp = 0.0; +int showHideSkillSheet = 0; +real_t percentShowHideSkillSheet = 0.0; +bool bUseCompactSkillsView = true; + void Player::SkillSheet_t::processSkillSheet() { if ( !skillFrame ) @@ -7803,6 +7933,85 @@ void Player::SkillSheet_t::processSkillSheet() auto innerFrame = skillFrame->findFrame("skills frame"); SDL_Rect sheetSize = innerFrame->getSize(); + Frame* allSkillEntriesLeft = innerFrame->findFrame("skill entries frame left"); + Frame* allSkillEntriesRight = innerFrame->findFrame("skill entries frame right"); + auto leftWingImg = allSkillEntriesLeft->findImage("bg wing left"); + auto rightWingImg = allSkillEntriesRight->findImage("bg wing right"); + auto skillDescriptionFrame = innerFrame->findFrame("skill desc frame"); + auto bgImgFrame = innerFrame->findFrame("skills bg images"); + auto scrollAreaOuterFrame = skillDescriptionFrame->findFrame("scroll area outer frame"); + auto scrollArea = scrollAreaOuterFrame->findFrame("skill scroll area"); + SDL_Rect scrollOuterFramePos = scrollAreaOuterFrame->getSize(); + SDL_Rect scrollAreaPos = scrollArea->getSize(); + + int compactViewWidthOffset = (showHideSkillSheet != 0 ? allSkillEntriesLeft->getSize().w / 2 : 0); + { + // dynamic width/height adjustments of outer containers + sheetSize.h = std::max(0, std::min(skillFrame->getSize().h, (int)(404 + percentYTmp * 80))); + sheetSize.w = std::max(0, std::min(skillFrame->getSize().w, + (int)(684 + percentXTmp * 80 - compactViewWidthOffset))); + innerFrame->setSize(sheetSize); + + SDL_Rect skillDescPos = skillDescriptionFrame->getSize(); + skillDescPos.h = innerFrame->getSize().h - skillDescPos.y - 16; + skillDescPos.w = innerFrame->getSize().w + compactViewWidthOffset - allSkillEntriesLeft->getSize().w - allSkillEntriesRight->getSize().w; + skillDescPos.x = innerFrame->getSize().w / 2 - skillDescPos.w / 2; + if ( showHideSkillSheet != 0 ) + { + skillDescPos.x += (percentShowHideSkillSheet) * allSkillEntriesLeft->getSize().w * .25; + } + skillDescriptionFrame->setSize(skillDescPos); + + scrollOuterFramePos.h = skillDescPos.h - 8; + + auto leftWingPos = allSkillEntriesLeft->getSize(); + auto rightWingPos = allSkillEntriesRight->getSize(); + + bool useHalfWidth = false;// showHideSkillSheet != 0; + leftWingPos.x = 0 + (useHalfWidth ? leftWingPos.w * .25 : 0); + rightWingPos.x = innerFrame->getSize().w - rightWingPos.w - (useHalfWidth ? rightWingPos.w * .25 : 0); + leftWingPos.y = std::max(0, innerFrame->getSize().h / 2 - leftWingPos.h / 2); + rightWingPos.y = std::max(0, innerFrame->getSize().h / 2 - rightWingPos.h / 2); + allSkillEntriesLeft->setSize(leftWingPos); + allSkillEntriesRight->setSize(rightWingPos); + + if ( bUseCompactSkillsView ) + { + leftWingImg->path = "images/ui/SkillSheet/UI_Skills_Window_LeftCompact_03.png"; + rightWingImg->path = "images/ui/SkillSheet/UI_Skills_Window_RightCompact_03.png"; + } + else + { + leftWingImg->path = "images/ui/SkillSheet/UI_Skills_Window_Left_03.png"; + rightWingImg->path = "images/ui/SkillSheet/UI_Skills_Window_Right_03.png"; + } + leftWingImg->pos.h = Image::get(leftWingImg->path.c_str())->getHeight(); + rightWingImg->pos.h = Image::get(rightWingImg->path.c_str())->getHeight(); + } + { + // dynamic main panel width/height background image adjustment + SDL_Rect bgImgFramePos = bgImgFrame->getSize(); + int backgroundWidth = skillDescriptionFrame->getSize().w; + int backgroundHeight = innerFrame->getSize().h; + + auto flourishTop = bgImgFrame->findImage("flourish top"); + flourishTop->pos.x = bgImgFramePos.w / 2 - flourishTop->pos.w / 2; + auto flourishBottom = bgImgFrame->findImage("flourish bottom"); + flourishBottom->pos.x = bgImgFramePos.w / 2 - flourishBottom->pos.w / 2; + flourishBottom->pos.y = backgroundHeight - flourishBottom->pos.h; + + bgImgFramePos.x = innerFrame->getSize().w / 2 - backgroundWidth / 2; + if ( showHideSkillSheet != 0 ) + { + bgImgFramePos.x += (percentShowHideSkillSheet) * allSkillEntriesLeft->getSize().w * .25; + } + bgImgFramePos.y = 0; + bgImgFramePos.w = backgroundWidth; + bgImgFramePos.h = backgroundHeight; + bgImgFrame->setSize(bgImgFramePos); + + imageResizeToContainer9x9(bgImgFrame, SDL_Rect{0, 12, backgroundWidth, backgroundHeight - 6 }, skillsheetEffectBackgroundImages); + } sheetSize.x = skillFrame->getSize().w / 2 - sheetSize.w / 2; const real_t fpsScale = (50.f / std::max(1U, fpsLimit)); // ported from 50Hz @@ -7820,135 +8029,223 @@ void Player::SkillSheet_t::processSkillSheet() sheetSize.y = -sheetSize.h + skillsFadeInAnimationY * (baseY + sheetSize.h); innerFrame->setSize(sheetSize); - auto skillDescriptionFrame = innerFrame->findFrame("skill desc frame"); auto slider = skillDescriptionFrame->findSlider("skill slider"); bool sliderDisabled = slider->isDisabled(); + auto titleText = innerFrame->findField("skill title txt"); + SDL_Rect titleTextPos = titleText->getSize(); + titleTextPos.x = skillDescriptionFrame->getSize().x + skillDescriptionFrame->getSize().w / 2 - titleTextPos.w / 2; + titleText->setSize(titleTextPos); + + if ( keystatus[SDL_SCANCODE_F1] ) + { + if ( keystatus[SDL_SCANCODE_LSHIFT] ) + { + percentXTmp = std::max(percentXTmp - .01, -1.0); + } + else + { + percentXTmp = std::min(percentXTmp + .01, 1.0); + } + } + if ( keystatus[SDL_SCANCODE_F2] ) + { + if ( keystatus[SDL_SCANCODE_LSHIFT] ) + { + percentYTmp = std::max(percentYTmp - .01, -1.0); + } + else + { + percentYTmp = std::min(percentYTmp + .01, 1.0); + } + } + if ( keystatus[SDL_SCANCODE_F3] ) + { + keystatus[SDL_SCANCODE_F3] = 0; + if ( keystatus[SDL_SCANCODE_LCTRL] ) + { + bUseCompactSkillsView = !bUseCompactSkillsView; + } + else if ( keystatus[SDL_SCANCODE_LSHIFT] ) + { + showHideSkillSheet = 0; + } + else + { + showHideSkillSheet = (showHideSkillSheet != 1) ? 1 : -1; + //percentShowHideSkillSheet = 0.0; + } + } + if ( showHideSkillSheet != 0 ) + { + const real_t fpsScale = (144.0 / std::max(1U, fpsLimit)); // ported from 144Hz + real_t setpointDiff = std::max(0.1, 1.0 - abs(percentShowHideSkillSheet)); + percentShowHideSkillSheet += fpsScale * (setpointDiff / 5.0) * showHideSkillSheet; + if ( percentShowHideSkillSheet < -1.0 ) + { + percentShowHideSkillSheet = -1.0; + } + if ( percentShowHideSkillSheet > 1.0 ) + { + percentShowHideSkillSheet = 1.0; + } + } + + int lowestSkillEntryY = 0; if ( stats[player.playernum] && skillSheetData.skillEntries.size() > 0 ) { - if ( Frame* allSkillEntries = innerFrame->findFrame("skill entries frame") ) + bool skillDescAreaCapturesMouse = bgImgFrame->capturesMouse(); + const int skillEntryStartY = bUseCompactSkillsView ? 28 : 38; + const int entryHeight = bUseCompactSkillsView ? 36 : 40; + SDL_Rect entryResizePos{ 0, skillEntryStartY, 0, entryHeight }; + for ( int i = 0; i < NUMPROFICIENCIES; ++i ) { - for ( int i = 0; i < NUMPROFICIENCIES; ++i ) + char skillname[32]; + snprintf(skillname, sizeof(skillname), "skill %d", i); + Frame* allSkillEntries = allSkillEntriesRight; + if ( i >= 8 ) { - char skillname[32]; - snprintf(skillname, sizeof(skillname), "skill %d", i); - auto entry = allSkillEntries->findFrame(skillname); - - if ( i >= skillSheetData.skillEntries.size() ) + allSkillEntries = allSkillEntriesLeft; + if ( i == 8 ) { - entry->setDisabled(true); - break; + entryResizePos.y = skillEntryStartY; } - entry->setDisabled(false); - int proficiency = skillSheetData.skillEntries[i].skillId; + } + auto entry = allSkillEntries->findFrame(skillname); - auto skillLevel = entry->findField("skill level"); - char skillLevelText[32]; - snprintf(skillLevelText, sizeof(skillLevelText), "%d", stats[player.playernum]->PROFICIENCIES[proficiency]); - skillLevel->setText(skillLevelText); + if ( i >= skillSheetData.skillEntries.size() ) + { + entry->setDisabled(true); + break; + } + entry->setDisabled(false); + SDL_Rect entryPos = entry->getSize(); + entryPos.y = entryResizePos.y; + entryPos.h = entryResizePos.h; + entry->setSize(entryPos); + entryResizePos.y += entryResizePos.h; + lowestSkillEntryY = std::max(lowestSkillEntryY, entryPos.y + entryPos.h); - auto skillIconBg = entry->findImage("skill icon bg"); - auto skillIconFg = entry->findImage("skill icon fg"); - skillIconFg->path = skillSheetData.skillEntries[i].skillIconPath; + int proficiency = skillSheetData.skillEntries[i].skillId; - auto statIcon = entry->findImage("stat icon"); - statIcon->disabled = true; + auto skillLevel = entry->findField("skill level"); + char skillLevelText[32]; + snprintf(skillLevelText, sizeof(skillLevelText), "%d", stats[player.playernum]->PROFICIENCIES[proficiency]); + skillLevel->setText(skillLevelText); - auto selectorIcon = entry->findImage("selector img"); - selectorIcon->disabled = true; + auto skillIconBg = entry->findImage("skill icon bg"); + auto skillIconFg = entry->findImage("skill icon fg"); + skillIconFg->path = skillSheetData.skillEntries[i].skillIconPath; - if ( entry->capturesMouse() ) - { - highlightedSkill = i; - if ( inputs.bMouseLeft(player.playernum) ) - { - selectSkill(i); - //inputs.mouseClearLeft(player.playernum); - } - } + auto statIcon = entry->findImage("stat icon"); + statIcon->disabled = true; + + auto selectorIcon = entry->findImage("selector img"); + selectorIcon->disabled = true; - if ( selectedSkill == i || highlightedSkill == i ) + if ( entry->capturesMouse() && !skillDescAreaCapturesMouse ) + { + highlightedSkill = i; + if ( showHideSkillSheet != 0 ) { - selectorIcon->disabled = false; - if ( selectedSkill == i && highlightedSkill == i ) - { - selectorIcon->path = (i < 8) ? "images/ui/SkillSheet/UI_Skills_SkillSelector100_00.png" : "images/ui/SkillSheet/UI_Skills_SkillSelector100R_00.png"; - } - else if ( highlightedSkill == i ) + if ( highlightedSkill >= 8 ) { - selectorIcon->path = (i < 8) ? skillSheetData.highlightSkillImg : skillSheetData.highlightSkillImg_Right; + showHideSkillSheet = 1; } - else if ( selectedSkill == i ) + else { - selectorIcon->path = (i < 8) ? skillSheetData.selectSkillImg : skillSheetData.selectSkillImg_Right; + showHideSkillSheet = -1; } } + if ( inputs.bMouseLeft(player.playernum) ) + { + selectSkill(i); + //inputs.mouseClearLeft(player.playernum); + } + } - if ( stats[player.playernum]->PROFICIENCIES[proficiency] >= SKILL_LEVEL_LEGENDARY ) + if ( selectedSkill == i || highlightedSkill == i ) + { + selectorIcon->disabled = false; + if ( selectedSkill == i && highlightedSkill == i ) { - skillIconFg->path = skillSheetData.skillEntries[i].skillIconPathLegend; - skillLevel->setColor(skillSheetData.legendTextColor); - if ( selectedSkill == i ) - { - skillIconBg->path = skillSheetData.iconBgSelectedPathLegend; - } - else - { - skillIconBg->path = skillSheetData.iconBgPathLegend; - } + selectorIcon->path = (i < 8) ? "images/ui/SkillSheet/UI_Skills_SkillSelector100_01.png" : "images/ui/SkillSheet/UI_Skills_SkillSelector100R_01.png"; } - else if ( stats[player.playernum]->PROFICIENCIES[proficiency] >= SKILL_LEVEL_EXPERT ) + else if ( highlightedSkill == i ) { - skillLevel->setColor(skillSheetData.expertTextColor); - if ( selectedSkill == i ) - { - skillIconBg->path = skillSheetData.iconBgSelectedPathExpert; - } - else - { - skillIconBg->path = skillSheetData.iconBgPathExpert; - } + selectorIcon->path = (i < 8) ? skillSheetData.highlightSkillImg : skillSheetData.highlightSkillImg_Right; } - else if ( stats[player.playernum]->PROFICIENCIES[proficiency] >= SKILL_LEVEL_BASIC ) + else if ( selectedSkill == i ) { - skillLevel->setColor(skillSheetData.noviceTextColor); - if ( selectedSkill == i ) - { - skillIconBg->path = skillSheetData.iconBgSelectedPathNovice; - } - else - { - skillIconBg->path = skillSheetData.iconBgPathNovice; - } + selectorIcon->path = (i < 8) ? skillSheetData.selectSkillImg : skillSheetData.selectSkillImg_Right; + } + } + + if ( stats[player.playernum]->PROFICIENCIES[proficiency] >= SKILL_LEVEL_LEGENDARY ) + { + skillIconFg->path = skillSheetData.skillEntries[i].skillIconPathLegend; + skillLevel->setColor(skillSheetData.legendTextColor); + if ( selectedSkill == i ) + { + skillIconBg->path = skillSheetData.iconBgSelectedPathLegend; } else { - skillLevel->setColor(skillSheetData.defaultTextColor); - if ( selectedSkill == i ) - { - skillIconBg->path = skillSheetData.iconBgSelectedPathDefault; - } - else - { - skillIconBg->path = skillSheetData.iconBgPathDefault; - } + skillIconBg->path = skillSheetData.iconBgPathLegend; } - //statIcon->path = skillIconFg->path; - //skillIconFg->path = ""; } + else if ( stats[player.playernum]->PROFICIENCIES[proficiency] >= SKILL_LEVEL_EXPERT ) + { + skillLevel->setColor(skillSheetData.expertTextColor); + if ( selectedSkill == i ) + { + skillIconBg->path = skillSheetData.iconBgSelectedPathExpert; + } + else + { + skillIconBg->path = skillSheetData.iconBgPathExpert; + } + } + else if ( stats[player.playernum]->PROFICIENCIES[proficiency] >= SKILL_LEVEL_BASIC ) + { + skillLevel->setColor(skillSheetData.noviceTextColor); + if ( selectedSkill == i ) + { + skillIconBg->path = skillSheetData.iconBgSelectedPathNovice; + } + else + { + skillIconBg->path = skillSheetData.iconBgPathNovice; + } + } + else + { + skillLevel->setColor(skillSheetData.defaultTextColor); + if ( selectedSkill == i ) + { + skillIconBg->path = skillSheetData.iconBgSelectedPathDefault; + } + else + { + skillIconBg->path = skillSheetData.iconBgPathDefault; + } + } + //statIcon->path = skillIconFg->path; + //skillIconFg->path = ""; } int lowestY = 0; - auto scrollAreaOuterFrame = skillDescriptionFrame->findFrame("scroll area outer frame"); - auto scrollArea = scrollAreaOuterFrame->findFrame("skill scroll area"); - SDL_Rect scrollOuterFramePos = scrollAreaOuterFrame->getSize(); - SDL_Rect scrollAreaPos = scrollArea->getSize(); if ( keystatus[SDL_SCANCODE_J] ) { keystatus[SDL_SCANCODE_J] = 0; slider->setDisabled(!slider->isDisabled()); } + SDL_Rect sliderPos = slider->getRailSize(); + sliderPos.x = skillDescriptionFrame->getSize().w - 34; + sliderPos.h = skillDescriptionFrame->getSize().h - 8; + slider->setRailSize(sliderPos); + int sliderOffsetW = 0; if ( slider->isDisabled() ) { @@ -8058,6 +8355,7 @@ void Player::SkillSheet_t::processSkillSheet() } auto skillDescriptionTxt = scrollArea->findField("skill desc txt"); + auto skillDescriptionBgFrame = scrollArea->findFrame("skill desc bg frame"); int moveEffectsOffsetY = 0; Font* actualFont = Font::get(skillDescriptionTxt->getFont()); if ( !bSkillSheetEntryLoaded ) @@ -8075,6 +8373,10 @@ void Player::SkillSheet_t::processSkillSheet() { moveEffectsOffsetY = (txtHeightNew - txtHeightOld); } + skillDescriptionBgFrame->setSize(SDL_Rect{skillDescriptionTxtPos.x, skillDescriptionTxtPos.y - 4, + skillDescriptionTxtPos.w, txtHeightNew + 8 }); + imageResizeToContainer9x9(skillDescriptionBgFrame, + SDL_Rect{ 0, 0, skillDescriptionBgFrame->getSize().w, skillDescriptionBgFrame->getSize().h }, skillsheetEffectBackgroundImages); } lowestY = std::max(lowestY, skillDescriptionTxt->getSize().y + skillDescriptionTxt->getNumTextLines() * actualFont->height(true)); @@ -8192,7 +8494,9 @@ void Player::SkillSheet_t::processSkillSheet() { // adjust inner background image elements - auto tl = effectBgImgFrame->findImage("effect bg top left"); + imageResizeToContainer9x9(effectBgImgFrame, + SDL_Rect{ 0, 0, effectBgImgFrame->getSize().w, effectBgImgFrame->getSize().h }, skillsheetEffectBackgroundImages); + /*auto tl = effectBgImgFrame->findImage("effect bg top left"); tl->pos.x = 0; tl->pos.y = 0; auto tr = effectBgImgFrame->findImage("effect bg top right"); @@ -8223,7 +8527,7 @@ void Player::SkillSheet_t::processSkillSheet() mm->pos.x = ml->pos.x + ml->pos.w; mm->pos.y = ml->pos.y; mm->pos.w = effectBgImgFrame->getSize().w - ml->pos.w - mr->pos.w; - mm->pos.h = ml->pos.h; + mm->pos.h = ml->pos.h;*/ } lowestY = std::max(lowestY, effectFrame->getSize().y + effectFrame->getSize().h); @@ -8462,6 +8766,35 @@ void Player::SkillSheet_t::processSkillSheet() scrollArea->setSize(scrollAreaPos); } + bool drawGlyphs = ::inputs.getVirtualMouse(player.playernum)->draw_cursor; + if ( auto promptBack = innerFrame->findField("prompt back txt") ) + { + promptBack->setDisabled(!drawGlyphs); + promptBack->setText(language[4053]); + auto promptImg = innerFrame->findImage("prompt back img"); + promptImg->disabled = !drawGlyphs; + SDL_Rect glyphPos = promptImg->pos; + if ( auto textGet = Text::get(promptBack->getText(), promptBack->getFont()) ) + { + SDL_Rect textPos = promptBack->getSize(); + textPos.w = textGet->getWidth(); + textPos.x = innerFrame->getSize().w - textPos.w - 16; + textPos.y = lowestSkillEntryY + 4; + promptBack->setSize(textPos); + glyphPos.x = promptBack->getSize().x + promptBack->getSize().w - textGet->getWidth() - 4; + } + promptImg->path = Input::inputs[player.playernum].getGlyphPathForInput("MenuCancel"); + Image* glyphImage = Image::get(promptImg->path.c_str()); + if ( glyphImage ) + { + glyphPos.w = glyphImage->getWidth(); + glyphPos.h = glyphImage->getHeight(); + glyphPos.x -= glyphPos.w; + glyphPos.y = promptBack->getSize().y + promptBack->getSize().h / 2 - glyphPos.h / 2; + promptImg->pos = glyphPos; + } + } + if ( sliderDisabled != slider->isDisabled() ) { // rerun this function From 69e61f969837899929f520f83e7cd5d2312b39a0 Mon Sep 17 00:00:00 2001 From: SheridanR Date: Mon, 1 Nov 2021 18:43:30 -0700 Subject: [PATCH 27/39] Pause menu, other stuff Signed-off-by: SheridanR --- src/game.cpp | 41 ++-- src/ui/Frame.cpp | 5 - src/ui/GameUI.cpp | 9 +- src/ui/MainMenu.cpp | 470 +++++++++++++++++++++++++++++--------------- src/ui/MainMenu.hpp | 13 +- 5 files changed, 344 insertions(+), 194 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 1d12e0bfa..3e19adef0 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -4146,12 +4146,15 @@ void ingameHud() continue; } //drawSkillsSheet(player); - if ( !nohud ) + if ( !gamePaused ) { - drawStatusNew(player); + if ( !nohud ) + { + drawStatusNew(player); + } + drawSustainedSpells(player); + updateAppraisalItemBox(player); } - drawSustainedSpells(player); - updateAppraisalItemBox(player); // inventory and stats if ( players[player]->shootmode == false ) @@ -5390,7 +5393,7 @@ int main(int argc, char** argv) if (newui) { - MainMenu::doMainMenu(); + MainMenu::doMainMenu(!intro); } else { @@ -5748,35 +5751,23 @@ int main(int argc, char** argv) DebugStats.t6Messages = std::chrono::high_resolution_clock::now(); - doFrames(); - - if ( !gamePaused ) + if ( /*newui*/ 0 ) { - if ( /*newui*/ 0 ) - { - newIngameHud(); - } - else - { - ingameHud(); - } + newIngameHud(); } - else if ( !multiplayer ) + else { - // darken the rest of the screen - src.x = 0; - src.y = 0; - src.w = mainsurface->w; - src.h = mainsurface->h; - drawRect(&src, SDL_MapRGB(mainsurface->format, 0, 0, 0), 127); + ingameHud(); } + doFrames(); + if ( gamePaused ) { // handle menu if (newui) { - MainMenu::doMainMenu(); + MainMenu::doMainMenu(!intro); } else { @@ -5785,6 +5776,8 @@ int main(int argc, char** argv) } else { + MainMenu::destroyMainMenu(); + // draw subwindow if ( !movie ) { diff --git a/src/ui/Frame.cpp b/src/ui/Frame.cpp index 53cd9a600..afdbebf7f 100644 --- a/src/ui/Frame.cpp +++ b/src/ui/Frame.cpp @@ -544,11 +544,6 @@ Frame::result_t Frame::process(SDL_Rect _size, SDL_Rect _actualSize, const std:: return result; } -#ifndef EDITOR // bad editor no cookie - if ( players[getOwner()]->shootmode ) { - return result; - } -#endif if ( parent && inheritParentFrameOpacity ) { setOpacity(static_cast(parent)->getOpacity()); } diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index c4627725b..551fcf54b 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -491,7 +491,7 @@ void Player::HUD_t::processHUD() players[player.playernum]->camera_virtualWidth(), players[player.playernum]->camera_virtualHeight() }); - if ( nohud || !players[player.playernum]->isLocalPlayer() ) + if ( gamePaused || nohud || !players[player.playernum]->isLocalPlayer() ) { // hide hudFrame->setDisabled(true); @@ -585,6 +585,11 @@ void Player::MessageZone_t::processChatbox() players[player.playernum]->camera_virtualHeight() }; Frame* messageBoxFrame = chatFrame->findFrame("message box"); + if (gamePaused) { + messageBoxFrame->setDisabled(true); + } else { + messageBoxFrame->setDisabled(false); + } SDL_Rect messageBoxSize = messageBoxFrame->getSize(); if ( player.shootmode && messageBoxSize.x == chatboxTopAlignedPos.x ) { @@ -1783,7 +1788,7 @@ void Player::Hotbar_t::processHotbar() players[player.playernum]->camera_virtualWidth(), players[player.playernum]->camera_virtualHeight() }); - if ( nohud || !players[player.playernum]->isLocalPlayer() ) + if ( gamePaused || nohud || !players[player.playernum]->isLocalPlayer() ) { // hide hotbarFrame->setDisabled(true); diff --git a/src/ui/MainMenu.cpp b/src/ui/MainMenu.cpp index b8cac819d..324145ae5 100644 --- a/src/ui/MainMenu.cpp +++ b/src/ui/MainMenu.cpp @@ -109,21 +109,23 @@ namespace MainMenu { } // update cursor position - auto cursor = main_menu_frame->findImage("cursor"); - if (cursor) { - cursor->disabled = !buttonSelected; - int diff = main_menu_cursor_y - cursor->pos.y; - if (diff > 0) { - diff = std::max(1, diff / 2); - } else if (diff < 0) { - diff = std::min(-1, diff / 2); + if (main_menu_frame) { + auto cursor = main_menu_frame->findImage("cursor"); + if (cursor) { + cursor->disabled = !buttonSelected; + int diff = main_menu_cursor_y - cursor->pos.y; + if (diff > 0) { + diff = std::max(1, diff / 2); + } else if (diff < 0) { + diff = std::min(-1, diff / 2); + } + cursor->pos = SDL_Rect{ + main_menu_cursor_x + (int)(sinf(main_menu_cursor_bob) * 16.f) - 16, + diff + cursor->pos.y, + 37 * 2, + 23 * 2 + }; } - cursor->pos = SDL_Rect{ - main_menu_cursor_x + (int)(sinf(main_menu_cursor_bob) * 16.f) - 16, - diff + cursor->pos.y, - 37 * 2, - 23 * 2 - }; } } @@ -175,6 +177,9 @@ namespace MainMenu { if (image->path == "images/ui/Main Menus/Settings/Settings_Left_BackingSelect00.png") { image->path = "images/ui/Main Menus/Settings/Settings_Left_Backing00.png"; } + if (image->path == "images/ui/Main Menus/Settings/GenericWindow/Settings_Left_BackingSelect_Short00.png") { + image->path = "images/ui/Main Menus/Settings/GenericWindow/Settings_Left_Backing_Short00.png"; + } } auto selectedWidget = frame.findSelectedWidget(0); if (selectedWidget) { @@ -199,9 +204,12 @@ namespace MainMenu { } if (!setting.empty()) { auto image = frame.findImage((std::string("setting_") + setting + std::string("_image")).c_str()); - if (image) { + if (image && image->path == "images/ui/Main Menus/Settings/Settings_Left_Backing00.png") { image->path = "images/ui/Main Menus/Settings/Settings_Left_BackingSelect00.png"; } + else if (image && image->path == "images/ui/Main Menus/Settings/GenericWindow/Settings_Left_Backing_Short00.png") { + image->path = "images/ui/Main Menus/Settings/GenericWindow/Settings_Left_BackingSelect_Short00.png"; + } auto field = frame.findField((std::string("setting_") + setting + std::string("_field")).c_str()); if (field) { static Widget* current_selected_widget = nullptr; @@ -1276,11 +1284,20 @@ namespace MainMenu { return image->pos.h + 6; } - static int settingsAddOption(Frame& frame, int y, const char* name, const char* text, const char* tip) { + static int settingsAddOption( + Frame& frame, + int y, + const char* name, + const char* text, + const char* tip, + bool _short = false + ) { std::string fullname = std::string("setting_") + name; auto image = frame.addImage( - SDL_Rect{0, y, 382, 52}, + SDL_Rect{0, y, _short ? 278 : 382, 52}, 0xffffffff, + _short ? + "images/ui/Main Menus/Settings/GenericWindow/Settings_Left_Backing_Short00.png": "images/ui/Main Menus/Settings/Settings_Left_Backing00.png", (fullname + "_image").c_str() ); @@ -1509,12 +1526,13 @@ namespace MainMenu { float minValue, float maxValue, bool percent, - void (*callback)(Slider&)) + void (*callback)(Slider&), + bool _short = false) { std::string fullname = std::string("setting_") + name; - int result = settingsAddOption(frame, y, name, text, tip); + int result = settingsAddOption(frame, y, name, text, tip, _short); auto box = frame.addImage( - SDL_Rect{402, y + 4, 132, 44}, + SDL_Rect{_short ? 298 : 402, y + 4, 132, 44}, 0xffffffff, "images/ui/Main Menus/Settings/Settings_Value_Backing00.png", (fullname + "_box").c_str() @@ -1550,13 +1568,17 @@ namespace MainMenu { slider->setMaxValue(maxValue); slider->setBorder(16); slider->setValue(value); - slider->setRailSize(SDL_Rect{field->getSize().x + field->getSize().w + 32, y + 14, 450, 24}); + slider->setRailSize(SDL_Rect{field->getSize().x + field->getSize().w + 32, y + 14, _short ? 282 : 450, 24}); slider->setHandleSize(SDL_Rect{0, 0, 52, 42}); slider->setCallback(callback); slider->setColor(makeColor(127,127,127,255)); slider->setHighlightColor(makeColor(255,255,255,255)); slider->setHandleImage("images/ui/Main Menus/Settings/Settings_ValueSlider_Slide00.png"); - slider->setRailImage("images/ui/Main Menus/Settings/Settings_ValueSlider_Backing00.png"); + if (_short) { + slider->setRailImage("images/ui/Main Menus/Settings/GenericWindow/Settings_ValueSlider_Backing_Short00.png"); + } else { + slider->setRailImage("images/ui/Main Menus/Settings/Settings_ValueSlider_Backing00.png"); + } slider->setWidgetSearchParent(frame.getParent()->getName()); slider->setWidgetBack("discard_and_exit"); slider->setWidgetPageLeft("tab_left"); @@ -1929,21 +1951,21 @@ namespace MainMenu { y += settingsAddSlider(*subwindow, y, "map_scale", "Map scale", "Scale the map to be larger or smaller.", - 100, 100, 200, true, nullptr); + 100, 100, 200, true, nullptr, true); y += settingsAddSlider(*subwindow, y, "icon_scale", "Icon scale", "Scale the size of icons on the map (such as players and allies)", - 100, 50, 200, true, nullptr); + 100, 50, 200, true, nullptr, true); y += settingsAddSubHeader(*subwindow, y, "transparency_header", "Transparency", true); y += settingsAddSlider(*subwindow, y, "foreground_opacity", "Foreground opacity", "Set the opacity of the minimap's foreground.", - 100, 0, 100, true, nullptr); + 100, 0, 100, true, nullptr, true); y += settingsAddSlider(*subwindow, y, "background_opacity", "Background opacity", "Set the opacity of the minimap's background.", - 100, 0, 100, true, nullptr); + 100, 0, 100, true, nullptr, true); hookSettings(*subwindow, {{Setting::Type::Slider, "map_scale"}, @@ -2748,7 +2770,7 @@ namespace MainMenu { back_button->setSize(SDL_Rect{Frame::virtualScreenX - 400, Frame::virtualScreenY - 70, 380, 50}); back_button->setCallback([](Button& b){ destroyMainMenu(); - createMainMenu(); + createMainMenu(false); mainHallOfRecords(b); auto buttons = main_menu_frame->findFrame("buttons"); assert(buttons); auto credits = buttons->findButton("CREDITS"); assert(credits); @@ -2933,7 +2955,7 @@ namespace MainMenu { {"PLAY MODDED GAME", mainPlayModdedGame}, {"HALL OF RECORDS", mainHallOfRecords}, {"SETTINGS", mainSettings}, - {"QUIT", mainQuit} + {"QUIT", mainQuitToDesktop} }; #endif const int num_options = sizeof(options) / sizeof(options[0]); @@ -4754,7 +4776,7 @@ namespace MainMenu { auto back_button = createBackWidget(lobby, [](Button&){ soundCancel(); destroyMainMenu(); - createMainMenu(); + createMainMenu(false); }); auto back_frame = back_button->getParent(); @@ -5440,9 +5462,13 @@ namespace MainMenu { confirm_and_exit->addWidgetAction("MenuStart", "confirm_and_exit"); } - static int quit_motd = -1; - - void mainQuit(Button& button) { + void quitConfirmWindow( + const char* window_text, + const char* okay_text, + const char* cancel_text, + void (*okay_callback)(Button&), + void (*cancel_callback)(Button&) + ) { if (main_menu_frame->findFrame("quit_confirm")) { return; } @@ -5454,7 +5480,7 @@ namespace MainMenu { dimmer->setActualSize(dimmer->getSize()); dimmer->setColor(makeColor(0, 0, 0, 63)); dimmer->setBorder(0); - + auto frame = dimmer->addFrame("quit_confirm"); frame->setSize(SDL_Rect{(Frame::virtualScreenX - 364) / 2, (Frame::virtualScreenY - 176) / 2, 364, 176}); frame->setActualSize(SDL_Rect{0, 0, 364, 176}); @@ -5467,30 +5493,10 @@ namespace MainMenu { "background" ); - static const char* quit_messages[][3] { - {"You want to leave, eh?\nThen get out and don't come back!", "Fine geez", "Never!"}, - {"Just cancel your plans.\nI'll wait.", "Good luck", "Sure"}, - {"You couldn't kill the lich anyway.", "You're right", "Oh yeah?"}, - {"The gnomes are laughing at you!\nAre you really gonna take that?", "Yeah :(", "No way!"}, - {"Don't go now! There's a\nboulder trap around the corner!", "Kill me", "Oh thanks"}, - {"I'll tell your parents\nyou said a bad word.", "Poop", "Please no"}, - {"Please don't leave!\nThere's more treasure to loot!", "Don't care", "More loot!"}, - {"Just be glad I can't summon\nthe minotaur in real life.", "Too bad", "Point taken"}, - {"I'd leave too.\nThis game looks just like Minecraft.", "lol", "Ouch"} - }; - constexpr int num_quit_messages = sizeof(quit_messages) / (sizeof(const char*) * 3); - - if (quit_motd >= num_quit_messages) { - quit_motd = 0; - } - if (quit_motd < 0) { - quit_motd = rand() % num_quit_messages; - } - auto text = frame->addField("text", 128); text->setSize(SDL_Rect{30, 28, 304, 46}); text->setFont(smallfont_no_outline); - text->setText(quit_messages[quit_motd][0]); + text->setText(window_text); text->setJustify(Field::justify_t::CENTER); auto okay = frame->addButton("okay"); @@ -5501,11 +5507,11 @@ namespace MainMenu { okay->setTextColor(makeColor(127, 127, 127, 255)); okay->setTextHighlightColor(makeColor(255, 255, 255, 255)); okay->setFont(smallfont_outline); - okay->setText(quit_messages[quit_motd][1]); + okay->setText(okay_text); okay->setWidgetRight("cancel"); okay->setWidgetBack("cancel"); okay->select(); - okay->setCallback([](Button&){mainloop = 0;}); + okay->setCallback(okay_callback); auto cancel = frame->addButton("cancel"); cancel->setSize(SDL_Rect{196, 78, 108, 52}); @@ -5515,30 +5521,147 @@ namespace MainMenu { cancel->setTextColor(makeColor(127, 127, 127, 255)); cancel->setTextHighlightColor(makeColor(255, 255, 255, 255)); cancel->setFont(smallfont_outline); - cancel->setText(quit_messages[quit_motd][2]); + cancel->setText(cancel_text); cancel->setWidgetLeft("okay"); cancel->setWidgetBack("cancel"); - cancel->setCallback([](Button&){ - soundCancel(); - assert(main_menu_frame); - auto buttons = main_menu_frame->findFrame("buttons"); assert(buttons); - auto quit_button = buttons->findButton("QUIT"); assert(quit_button); - quit_button->select(); - auto quit_confirm = main_menu_frame->findFrame("quit_confirm"); - if (quit_confirm) { - auto dimmer = static_cast(quit_confirm->getParent()); assert(dimmer); - dimmer->removeSelf(); - } + cancel->setCallback(cancel_callback); + } + + void mainEndLife(Button& button) { + quitConfirmWindow( + "Are you sure you want to die?\nThere is no return from this.", // window text + "End Life", // okay text + "Cancel", // cancel text + [](Button&){ // okay + soundActivate(); + closeMainMenu(); + stats[clientnum]->HP = 0; + }, + [](Button&){ // cancel + soundCancel(); + assert(main_menu_frame); + auto buttons = main_menu_frame->findFrame("buttons"); assert(buttons); + auto quit_button = buttons->findButton("QUIT TO MAIN MENU"); assert(quit_button); + quit_button->select(); + auto quit_confirm = main_menu_frame->findFrame("quit_confirm"); + if (quit_confirm) { + auto dimmer = static_cast(quit_confirm->getParent()); assert(dimmer); + dimmer->removeSelf(); + } + }); + } + + void mainRestartGame(Button& button) { + quitConfirmWindow( + "Are you sure you want to restart?\nThis adventure will be lost forever.", // window text + "Restart", // okay text + "Cancel", // cancel text + [](Button&){ // okay + soundActivate(); + destroyMainMenu(); + createDummyMainMenu(); + main_menu_fade_destination = FadeDestination::GameStart; + fadeout = true; + }, + [](Button&){ // cancel + soundCancel(); + assert(main_menu_frame); + auto buttons = main_menu_frame->findFrame("buttons"); assert(buttons); + auto quit_button = buttons->findButton("QUIT TO MAIN MENU"); assert(quit_button); + quit_button->select(); + auto quit_confirm = main_menu_frame->findFrame("quit_confirm"); + if (quit_confirm) { + auto dimmer = static_cast(quit_confirm->getParent()); assert(dimmer); + dimmer->removeSelf(); + } }); + } + + void mainQuitToMainMenu(Button& button) { + quitConfirmWindow( + "All progress before the current\ndungeon level will be saved.", // window text + "Quit to Menu", // okay text + "Cancel", // cancel text + [](Button&){ // okay + soundActivate(); + destroyMainMenu(); + createDummyMainMenu(); + main_menu_fade_destination = FadeDestination::RootMainMenu; + fadeout = true; + }, + [](Button&){ // cancel + soundCancel(); + assert(main_menu_frame); + auto buttons = main_menu_frame->findFrame("buttons"); assert(buttons); + auto quit_button = buttons->findButton("QUIT TO MAIN MENU"); assert(quit_button); + quit_button->select(); + auto quit_confirm = main_menu_frame->findFrame("quit_confirm"); + if (quit_confirm) { + auto dimmer = static_cast(quit_confirm->getParent()); assert(dimmer); + dimmer->removeSelf(); + } + }); + } + + void mainQuitToDesktop(Button& button) { + static const char* quit_messages[][3] { + {"You want to leave, eh?\nThen get out and don't come back!", "Fine geez", "Never!"}, + {"Just cancel your plans.\nI'll wait.", "Good luck", "Sure"}, + {"You couldn't kill the lich anyway.", "You're right", "Oh yeah?"}, + {"The gnomes are laughing at you!\nAre you really gonna take that?", "Yeah :(", "No way!"}, + {"Don't go now! There's a\nboulder trap around the corner!", "Kill me", "Oh thanks"}, + {"I'll tell your parents\nyou said a bad word.", "Poop", "Please no"}, + {"Please don't leave!\nThere's more treasure to loot!", "Don't care", "More loot!"}, + {"Just be glad I can't summon\nthe minotaur in real life.", "Too bad", "Point taken"}, + {"I'd leave too.\nThis game looks just like Minecraft.", "lol", "Ouch"} + }; + constexpr int num_quit_messages = sizeof(quit_messages) / (sizeof(const char*) * 3); + static int quit_motd = -2; ++quit_motd; + if (quit_motd >= num_quit_messages) { + quit_motd = 0; + } + if (quit_motd < 0) { + quit_motd = rand() % num_quit_messages; + } + + quitConfirmWindow( + quit_messages[quit_motd][0], // window text + quit_messages[quit_motd][1], // okay text + quit_messages[quit_motd][2], // cancel text + [](Button&){ // okay + soundActivate(); + mainloop = 0; + }, + [](Button&){ // cancel + soundCancel(); + assert(main_menu_frame); + auto buttons = main_menu_frame->findFrame("buttons"); assert(buttons); + auto quit_button = buttons->findButton("QUIT"); + if (!quit_button) { + quit_button = buttons->findButton("QUIT TO DESKTOP"); + } + assert(quit_button); + quit_button->select(); + auto quit_confirm = main_menu_frame->findFrame("quit_confirm"); + if (quit_confirm) { + auto dimmer = static_cast(quit_confirm->getParent()); assert(dimmer); + dimmer->removeSelf(); + } + }); + } + + void mainClose(Button& button) { + soundActivate(); + closeMainMenu(); } /******************************************************************************/ - void doMainMenu() { + void doMainMenu(bool ingame) { if (!main_menu_frame) { - createMainMenu(); + createMainMenu(ingame); } assert(main_menu_frame); @@ -5547,7 +5670,7 @@ namespace MainMenu { if (fadeout && fadealpha >= 255) { if (main_menu_fade_destination == FadeDestination::RootMainMenu) { destroyMainMenu(); - createMainMenu(); + createMainMenu(ingame); playMusic(intromusic[1], true, true, false); } if (main_menu_fade_destination == FadeDestination::IntroStoryScreen) { @@ -5555,6 +5678,7 @@ namespace MainMenu { playMusic(sounds[501], false, true, false); } if (main_menu_fade_destination == FadeDestination::HallOfTrials) { + destroyMainMenu(); multiplayer = SINGLE; numplayers = 0; gameModeManager.setMode(GameModeManager_t::GAME_MODE_TUTORIAL_INIT); @@ -5568,6 +5692,7 @@ namespace MainMenu { doNewGame(false); } if (main_menu_fade_destination == FadeDestination::GameStart) { + destroyMainMenu(); multiplayer = SINGLE; numplayers = 0; gameModeManager.setMode(GameModeManager_t::GAME_MODE_DEFAULT); @@ -5579,13 +5704,13 @@ namespace MainMenu { } } - void createMainMenu() { + void createMainMenu(bool ingame) { main_menu_frame = gui->addFrame("main_menu"); + main_menu_frame->setBorder(0); main_menu_frame->setSize(SDL_Rect{0, 0, Frame::virtualScreenX, Frame::virtualScreenY}); main_menu_frame->setActualSize(SDL_Rect{0, 0, main_menu_frame->getSize().w, main_menu_frame->getSize().h}); - main_menu_frame->setHollow(true); - main_menu_frame->setBorder(0); + main_menu_frame->setColor(ingame ? makeColor(0, 0, 0, 63) : 0); main_menu_frame->setTickCallback(tickMainMenu); int y = 16; @@ -5604,41 +5729,59 @@ namespace MainMenu { ); y += title->pos.h; - auto notification = main_menu_frame->addFrame("notification"); - notification->setSize(SDL_Rect{ - (Frame::virtualScreenX - 236 * 2) / 2, - y, - 236 * 2, - 49 * 2 - }); - notification->setActualSize(SDL_Rect{0, 0, notification->getSize().w, notification->getSize().h}); - notification->addImage(notification->getActualSize(), 0xffffffff, - "images/ui/Main Menus/Main/UI_MainMenu_EXNotification.png", "background"); - y += notification->getSize().h; - y += 16; + if (!ingame) { + auto notification = main_menu_frame->addFrame("notification"); + notification->setSize(SDL_Rect{ + (Frame::virtualScreenX - 236 * 2) / 2, + y, + 236 * 2, + 49 * 2 + }); + notification->setActualSize(SDL_Rect{0, 0, notification->getSize().w, notification->getSize().h}); + notification->addImage(notification->getActualSize(), 0xffffffff, + "images/ui/Main Menus/Main/UI_MainMenu_EXNotification.png", "background"); + y += notification->getSize().h; + y += 16; + } struct Option { const char* name; void (*callback)(Button&); }; + std::vector