From 6ce04e79c3aabb92123b2709a0a5eba39c5d597e Mon Sep 17 00:00:00 2001 From: Ihar Hubchyk Date: Fri, 4 Jun 2021 12:04:38 +0800 Subject: [PATCH] Version 0.9.4 (#3596) going better --- CMakeLists.txt | 2 +- appveyor.yml | 2 +- changelog.txt | 85 ++++++ script/windows/fheroes2.iss | 2 +- sonar-project.properties | 2 +- src/fheroes2/game/game_credits.cpp | 415 +++++++++++++++++++++++------ src/fheroes2/game/game_logo.cpp | 5 +- src/fheroes2/system/version.h | 2 +- 8 files changed, 423 insertions(+), 92 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 758d9d081f2..47caeab1be3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ # For file(GLOB_RECOURSE CONFIGURE_DEPENDS ...) cmake_minimum_required(VERSION 3.12) -project(fheroes2 VERSION 0.9.3 LANGUAGES C CXX) +project(fheroes2 VERSION 0.9.4 LANGUAGES C CXX) set (CMAKE_CXX_STANDARD 11) include(GNUInstallDirs) diff --git a/appveyor.yml b/appveyor.yml index ebc2ad06d82..12e69600cb3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,7 +10,7 @@ skip_commits: skip_tags: true # version format -version: 0.9.3.{build} +version: 0.9.4.{build} # Build worker image (VM template) image: Visual Studio 2015 diff --git a/changelog.txt b/changelog.txt index e1952c3b8e9..12d3efd82b1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,88 @@ +version 0.9.4 (04 June 2021) +- add Windows executable installers +- use user profile directories to store files to allow multi-user access to the game +- fix incorrect text splitting in some rare cases +- fix crash in Roland campaign, scenario 10 +- add missing popup dialogs for system options and buttons +- fix inaccurate object sorting by distance +- fix multiple issues with AI and spell logic using distance evaluation +- read campaign files in non-case sensitive manner +- make fog uncovering task for AI more efficient +- fix AI obsession over Observation Towers +- fix missing path drawing while a hero moving through Whirlpool +- mark resource generator objects as visited for allies +- fix incorrect game ending during time loss condition +- show extended shadow for 2-hex monsters in battle +- do not show gray flag over Haunted mine +- add configuration option for first game run +- support PoL heroes and artifacts in "battle only" mode +- fix inaccessible beach tiles +- add hiding and showing cursor logic for SDL2 +- fix shadows for random resources and artifacts +- draw correct castle icon in popup window +- fix incorrect hero position facing left +- remove unneeded AI hero animation under fog +- do not clear morale modifiers too early for Tavern case +- highlight the door in Main Menu for mouse over event +- speed up and fix many places with cursor rendering +- recruit an AI hero if none exists +- fix cheating AI behavior for dwellings with defenders +- add an option to replay Intro video for campaign +- force display rendering on app activation +- fix Stables, Alchemist's Tower and Water Wheel passabilities +- fix puzzle drawings +- avoid using useless spell during battle for AI +- return to load screen after cancelling loaded campaign scenario +- play hero's vanishing sound after AI vs human battle +- make Ultimate Crown as artifact for campaign scenarios +- fix incorrect hero receiving a bonus spell for campaign scenario +- fix monster recruitment logic when a hero present in a castle +- fix well's max button hiring logic +- add an option to open Hero Screen within battle +- fix move points and spell points replenishment logic for heroes from hero pool +- set proper difficulty level for campaign scenarios +- directly show next campaign scenario for end of scenario save +- modernize and secure demo version installation scripts +- add video playback for campaign scenarios +- do not reset hero mana points to maximum in the beginning of a new week if they are above maximum +- fix Roland, chapter 9 map conditions +- fix Tower shooting logic during castle's siege +- build Mage Guild before Special building for AI +- do not count monsters under fog for Visions spell +- mute sound by setting music and effects volume to zero instead of pausing them +- speed up AI hero movement +- add an option to hide AI movements +- do not draw objects under the fog if they are far from a revealed area +- add base code for the Price of Loyalty campaign support +- add dismiss and upgrade hotkeys in unit dialog info +- add shadow for Spell Book +- unveil the fog at the start of hero's movement +- fix extra place with hero's double shadow at adventure world +- add extended cursor icons from 4+ to 7+ +- fix UI defect of selected creature appearing after closing meeting dialog +- fix incorrect rendering of View World image +- add Lose Sorceress Village Condition for The Succession Wars campaign +- fix flags in Oracle/Thieves' Guild dialog +- fix sound mute in background on MacOS with SDL1 +- add Home and End button support for save naming +- fix elements layout for Oracle/Thieves dialogs +- generate obstacles on battlefield based on the battle tile index +- use non-plural name for 1 creature while viewing with Crystal Ball +- add correct support of The Price of Loyalty add-on maps +- fix double clicking between dialog in castle's window +- speed up MIDI loading +- fix bug with mouse cursor not visible after left-clicking on hero's secondary skill in Kingdom Overview +- fix the "world: use unique artifacts for resource affecting" option +- reduce CPU usage for video playback and puzzle revealing +- fix Mix-up in Roland's Campaign Scenarios +- fix issue when troop info window was not showing after moving a single unit between army bars using drag & drop method +- simulate level-ups for campaign-specific heroes that start at a higher level +- reset current music from the previous turn in the beginning of a new human turn +- upgrade Rendering engine +- fix broken behavior of scrollbars +- do not offer to exchange artifact with spell book +- disable controllers on non-console platforms + version 0.9.3 (04 May 2021) - add additional info to oracle dialog - check that unit is (im)movable after each unit action diff --git a/script/windows/fheroes2.iss b/script/windows/fheroes2.iss index 97562e639a3..f58d6eaf900 100644 --- a/script/windows/fheroes2.iss +++ b/script/windows/fheroes2.iss @@ -1,6 +1,6 @@ #define AppName "fheroes2" #define AppId "fheroes2" -#define AppVersion "0.9.3" +#define AppVersion "0.9.4" [Setup] AppName={#AppName} diff --git a/sonar-project.properties b/sonar-project.properties index d70a547030f..4313f67d5b2 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -3,7 +3,7 @@ sonar.organization=ihhub # This is the name and version displayed in the SonarCloud UI. sonar.projectName=fheroes2 -sonar.projectVersion=0.9.3 +sonar.projectVersion=0.9.4 # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. sonar.sources=. diff --git a/src/fheroes2/game/game_credits.cpp b/src/fheroes2/game/game_credits.cpp index a15bf4b1787..239b2563d78 100644 --- a/src/fheroes2/game/game_credits.cpp +++ b/src/fheroes2/game/game_credits.cpp @@ -22,6 +22,7 @@ #include "agg.h" #include "agg_image.h" #include "cursor.h" +#include "game_delays.h" #include "icn.h" #include "localevent.h" #include "mus.h" @@ -30,124 +31,368 @@ #include "text.h" #include "ui_window.h" -void Game::ShowCredits() +#include + +namespace { - fheroes2::Display & display = fheroes2::Display::instance(); - const fheroes2::StandardWindow border( display.DEFAULT_WIDTH, display.DEFAULT_HEIGHT ); + void transformToBlack( fheroes2::Image & out ) + { + if ( out.empty() ) + return; - // setup cursor - const CursorRestorer cursorRestorer( true, Cursor::POINTER ); + assert( !out.singleLayer() ); + + uint8_t * image = out.image(); + const uint8_t * transform = out.transform(); + const uint8_t * imageEnd = image + out.width() * out.height(); + + for ( ; image != imageEnd; ++image, ++transform ) { + if ( *transform == 0 ) { + *image = 0; + } + } + } + + enum class AnimationState : int + { + FADING_IN, + NO_ACTION, + FADING_OUT + }; + + class AnimationSequence + { + public: + AnimationSequence() = delete; + explicit AnimationSequence( const int32_t imageCount ) + : _imageCount( imageCount ) + {} + + int32_t pageId() const + { + return _pageId; + } + + uint8_t alpha() const + { + return static_cast( _alphaValue ); + } + + AnimationState state() const + { + return _animationState; + } + + void increment() + { + switch ( _animationState ) { + case AnimationState::FADING_IN: + _alphaValue += ALPHA_VALUE_STEP; + if ( _alphaValue >= 255 ) { + _alphaValue = 255; + _animationState = AnimationState::NO_ACTION; + _noActionCounter = 0; + } + break; + case AnimationState::NO_ACTION: + ++_noActionCounter; + if ( _noActionCounter > NO_ACTION_COUNTER_LIMIT ) { + _animationState = AnimationState::FADING_OUT; + } + break; + case AnimationState::FADING_OUT: + _alphaValue -= ALPHA_VALUE_STEP; + if ( _alphaValue < 0 ) { + _alphaValue = 0; + _animationState = AnimationState::FADING_IN; + + ++_pageId; + if ( _pageId >= _imageCount ) + _pageId = 0; + } + + break; + default: + assert( 0 ); + break; + } + } + + private: + const int32_t _imageCount; + int32_t _alphaValue = 0; + int32_t _noActionCounter = 0; + int32_t _pageId = 0; + + AnimationState _animationState = AnimationState::FADING_IN; + + enum : int32_t + { + ALPHA_VALUE_STEP = 5, + NO_ACTION_COUNTER_LIMIT = 80 + }; + }; + + fheroes2::Sprite generateHeader() + { + const fheroes2::Sprite & background = fheroes2::AGG::GetICN( ICN::CBKGLAVA, 0 ); + assert( background.height() < fheroes2::Display::DEFAULT_HEIGHT ); + + fheroes2::Sprite output( fheroes2::Display::DEFAULT_WIDTH, fheroes2::Display::DEFAULT_HEIGHT - background.height() ); + output.fill( 0 ); + output._disableTransformLayer(); + + Text caption( "Free Heroes of Might and Magic II (" + Settings::GetVersion() + ")", Font::YELLOW_BIG ); + caption.Blit( output.width() / 2 - caption.w() / 2, 15, output ); + + return output; + } + + fheroes2::Sprite generateFirstPage() + { + fheroes2::Sprite output = fheroes2::AGG::GetICN( ICN::CBKGLAVA, 0 ); + output._disableTransformLayer(); + + const int32_t columnStep = 210; + const int32_t textInitialOffsetY = 3; + const int32_t textWidth = 200; + + int32_t offsetY = textInitialOffsetY; + + TextBox title( "Project Coordination and Core Development", Font::YELLOW_BIG, textWidth ); + TextBox name( "Ihar Hubchyk", Font::BIG, textWidth ); + title.Blit( ( columnStep - title.w() ) / 2, offsetY, output ); + name.Blit( ( columnStep - name.w() ) / 2, offsetY + title.h(), output ); + offsetY += title.h() + name.h() + 10; + + const fheroes2::Sprite & blackDragon = fheroes2::AGG::GetICN( ICN::DRAGBLAK, 5 ); + fheroes2::Blit( blackDragon, output, ( columnStep - blackDragon.width() ) / 2, offsetY ); + offsetY += blackDragon.height(); + + title.Set( "QA and Support", Font::YELLOW_BIG, textWidth ); + name.Set( "Igor Tsivilko", Font::BIG, textWidth ); + title.Blit( ( columnStep - title.w() ) / 2, offsetY, output ); + name.Blit( ( columnStep - name.w() ) / 2, offsetY + title.h(), output ); + offsetY += title.h() + name.h() + 10; + + const fheroes2::Sprite & cyclop = fheroes2::AGG::GetICN( ICN::CYCLOPS, 38 ); + fheroes2::Blit( cyclop, output, ( columnStep - cyclop.width() ) / 2, offsetY ); + offsetY += cyclop.height(); + + title.Set( "Development", Font::YELLOW_BIG, textWidth ); + name.Set( "Ivan Shibanov", Font::BIG, textWidth ); + title.Blit( ( columnStep - title.w() ) / 2, offsetY, output ); + name.Blit( ( columnStep - name.w() ) / 2, offsetY + title.h(), output ); + offsetY += title.h() + name.h() + 10; - const fheroes2::Point screenOffset( ( display.width() - display.DEFAULT_WIDTH ) / 2, ( display.height() - display.DEFAULT_HEIGHT ) / 2 ); + const fheroes2::Sprite & crusader = fheroes2::AGG::GetICN( ICN::PALADIN2, 23 ); + fheroes2::Blit( crusader, output, ( columnStep - crusader.width() ) / 2, offsetY ); + offsetY += crusader.height(); - const fheroes2::Sprite & background = fheroes2::AGG::GetICN( ICN::CBKGLAVA, 0 ); - fheroes2::Blit( background, display, screenOffset.x, screenOffset.y + display.DEFAULT_HEIGHT - background.height() ); + const int32_t bottomOffset = offsetY; - fheroes2::Fill( display, screenOffset.x, screenOffset.y, display.DEFAULT_WIDTH, display.DEFAULT_HEIGHT - background.height(), 0 ); + const fheroes2::Sprite & mage = fheroes2::AGG::GetICN( ICN::MAGE1, 24 ); + offsetY -= crusader.height(); + fheroes2::Blit( mage, output, columnStep + ( columnStep - mage.width() ) / 2, offsetY ); - Text caption( "Free Heroes of Might and Magic II (" + Settings::GetVersion() + ")", Font::YELLOW_BIG ); - caption.Blit( screenOffset.x + display.DEFAULT_WIDTH / 2 - caption.w() / 2, screenOffset.y + 15 ); + name.Set( "Oleg Derevenetz", Font::BIG, textWidth ); + offsetY -= 10 + name.h(); + name.Blit( columnStep + ( columnStep - name.w() ) / 2, offsetY, output ); + offsetY -= title.h(); + title.Blit( columnStep + ( columnStep - title.w() ) / 2, offsetY, output ); - const int32_t columnStep = 210; - const int32_t textInitialOffsetY = 40; - const int32_t textWidth = 200; + offsetY = bottomOffset + 10; - int32_t offsetY = screenOffset.y + textInitialOffsetY; + const Text websiteInto( "Visit us at ", Font::BIG ); + const Text website( "https://github.com/ihhub/fheroes2", Font::YELLOW_BIG ); + const int32_t websiteOffsetX = ( output.width() - websiteInto.w() - website.w() ) / 2; + websiteInto.Blit( websiteOffsetX, offsetY, output ); + website.Blit( websiteOffsetX + websiteInto.w(), offsetY, output ); - TextBox title( "Project Coordination and Core Development", Font::YELLOW_BIG, textWidth ); - TextBox name( "Ihar Hubchyk", Font::BIG, textWidth ); - title.Blit( screenOffset.x + ( columnStep - title.w() ) / 2, offsetY ); - name.Blit( screenOffset.x + ( columnStep - name.w() ) / 2, offsetY + title.h() ); - offsetY += title.h() + name.h() + 10; + const fheroes2::Sprite & missile = fheroes2::AGG::GetICN( ICN::ARCH_MSL, 4 ); + fheroes2::Blit( missile, output, websiteOffsetX - 10 - missile.width(), offsetY + website.h() / 2 - missile.height() / 2 ); + fheroes2::Blit( missile, output, websiteOffsetX + websiteInto.w() + website.w() + 10, offsetY + website.h() / 2 - missile.height() / 2, true ); - const fheroes2::Sprite & blackDragon = fheroes2::AGG::GetICN( ICN::DRAGBLAK, 5 ); - fheroes2::Blit( blackDragon, display, screenOffset.x + ( columnStep - blackDragon.width() ) / 2, offsetY ); - offsetY += blackDragon.height(); + offsetY = textInitialOffsetY; - title.Set( "QA and Support", Font::YELLOW_BIG, textWidth ); - name.Set( "Igor Tsivilko", Font::BIG, textWidth ); - title.Blit( screenOffset.x + ( columnStep - title.w() ) / 2, offsetY ); - name.Blit( screenOffset.x + ( columnStep - name.w() ) / 2, offsetY + title.h() ); - offsetY += title.h() + name.h() + 10; + title.Set( "Special Thanks to", Font::YELLOW_BIG, textWidth ); + title.Blit( 2 * columnStep + ( columnStep - title.w() ) / 2, offsetY, output ); + offsetY += title.h(); - const fheroes2::Sprite & cyclop = fheroes2::AGG::GetICN( ICN::CYCLOPS, 38 ); - fheroes2::Blit( cyclop, display, screenOffset.x + ( columnStep - cyclop.width() ) / 2, offsetY ); - offsetY += cyclop.height(); + const std::string contributors( "LeHerosInconnu\n" + "undef21\n" + "shprotru\n" + "eos428\n" + "vincent-grosbois\n" + "Andrii Kurdiumov\n" + "Vasilenko Alexey\n" + "Andrey Starodubtsev\n" + "tau3\n" + "and many other contributors!" ); - title.Set( "Development", Font::YELLOW_BIG, textWidth ); - name.Set( "Ivan Shibanov", Font::BIG, textWidth ); - title.Blit( screenOffset.x + ( columnStep - title.w() ) / 2, offsetY ); - name.Blit( screenOffset.x + ( columnStep - name.w() ) / 2, offsetY + title.h() ); - offsetY += title.h() + name.h() + 10; + name.Set( contributors, Font::BIG, textWidth ); + name.Blit( 2 * columnStep + ( columnStep - name.w() ) / 2, offsetY, output ); + offsetY += name.h() + 10; - const fheroes2::Sprite & crusader = fheroes2::AGG::GetICN( ICN::PALADIN2, 23 ); - fheroes2::Blit( crusader, display, screenOffset.x + ( columnStep - crusader.width() ) / 2, offsetY ); - offsetY += crusader.height(); + const fheroes2::Sprite & hydra = fheroes2::AGG::GetICN( ICN::HYDRA, 11 ); + fheroes2::Blit( hydra, output, 2 * columnStep + ( columnStep - hydra.width() ) / 2, offsetY ); + offsetY += hydra.height(); - const int32_t bottomOffset = offsetY; + title.Set( "Original project before 0.7", Font::YELLOW_SMALL, textWidth ); + title.Blit( 2 * columnStep + ( columnStep - title.w() ) / 2, offsetY, output ); + offsetY += title.h(); - const fheroes2::Sprite & mage = fheroes2::AGG::GetICN( ICN::MAGE1, 24 ); - offsetY -= crusader.height(); - fheroes2::Blit( mage, display, screenOffset.x + columnStep + ( columnStep - mage.width() ) / 2, offsetY ); + name.Set( "Andrey Afletdinov\nhttps://sourceforge.net/\nprojects/fheroes2/", Font::SMALL, textWidth ); + name.Blit( 2 * columnStep + ( columnStep - name.w() ) / 2, offsetY, output ); - name.Set( "Oleg Derevenetz", Font::BIG, textWidth ); - offsetY -= 10 + name.h(); - name.Blit( screenOffset.x + columnStep + ( columnStep - name.w() ) / 2, offsetY ); - offsetY -= title.h(); - title.Blit( screenOffset.x + columnStep + ( columnStep - title.w() ) / 2, offsetY ); + const fheroes2::Sprite & goblin = fheroes2::AGG::GetICN( ICN::GOBLIN, 27 ); + fheroes2::Blit( goblin, output, ( output.width() - goblin.width() ) / 2, ( output.height() - goblin.height() ) / 2 ); - offsetY = bottomOffset + 10; + return output; + } + + fheroes2::Sprite generateSecondPage() + { + fheroes2::Sprite output = fheroes2::AGG::GetICN( ICN::CBKGSWMP, 0 ); + output._disableTransformLayer(); + + const int32_t columnStep = 425; + const int32_t textInitialOffsetY = 50; + const int32_t textWidth = 300; + + int32_t offsetY = textInitialOffsetY; + + TextBox title( "Support us at", Font::BIG, textWidth ); + TextBox name( "https://www.patreon.com/fheroes2", Font::YELLOW_BIG, textWidth ); + title.Blit( ( columnStep - title.w() ) / 2, offsetY, output ); + name.Blit( ( columnStep - name.w() ) / 2, offsetY + title.h(), output ); + offsetY += title.h() + name.h() + 10; + + const fheroes2::Sprite & wizard = fheroes2::AGG::GetICN( ICN::CMBTCAPZ, 4 ); + fheroes2::Blit( wizard, output, ( columnStep - wizard.width() ) / 2, offsetY ); + offsetY += wizard.height(); + + title.Set( "Connect with us at", Font::BIG, textWidth ); + name.Set( "https://www.facebook.com/groups/fheroes2", Font::YELLOW_BIG, textWidth - 10 ); // special case to properly split the string + title.Blit( ( columnStep - title.w() ) / 2, offsetY, output ); + name.Blit( ( columnStep - name.w() ) / 2, offsetY + title.h(), output ); + offsetY += title.h() + name.h() + 10; + + const fheroes2::Sprite & vampireLord = fheroes2::AGG::GetICN( ICN::VAMPIRE2, 22 ); + fheroes2::Blit( vampireLord, output, ( columnStep - vampireLord.width() ) / 2, offsetY ); + offsetY += vampireLord.height(); - const Text websiteInto( "Visit us at ", Font::BIG ); - const Text website( "https://github.com/ihhub/fheroes2", Font::YELLOW_BIG ); - const int32_t websiteOffsetX = screenOffset.x + ( display.DEFAULT_WIDTH - websiteInto.w() - website.w() ) / 2; - websiteInto.Blit( websiteOffsetX, offsetY ); - website.Blit( websiteOffsetX + websiteInto.w(), offsetY ); + title.Set( "Need help with the game?", Font::BIG, textWidth ); + name.Set( "https://discord.gg/xF85vbZ", Font::YELLOW_BIG, textWidth ); + title.Blit( ( columnStep - title.w() ) / 2, offsetY, output ); + name.Blit( ( columnStep - name.w() ) / 2, offsetY + title.h(), output ); + offsetY += title.h() + name.h() + 10; - const fheroes2::Sprite & missile = fheroes2::AGG::GetICN( ICN::ARCH_MSL, 4 ); - fheroes2::Blit( missile, display, websiteOffsetX - 10 - missile.width(), offsetY + website.h() / 2 - missile.height() / 2 ); - fheroes2::Blit( missile, display, websiteOffsetX + websiteInto.w() + website.w() + 10, offsetY + website.h() / 2 - missile.height() / 2, true ); + fheroes2::Sprite labyrinth = fheroes2::AGG::GetICN( ICN::TWNWUP_3, 0 ); + fheroes2::ApplyPalette( labyrinth, 2 ); + fheroes2::Blit( labyrinth, output, ( columnStep - labyrinth.width() ) / 2, offsetY ); - offsetY = screenOffset.y + textInitialOffsetY; + offsetY = textInitialOffsetY + 70; - title.Set( "Special Thanks to", Font::YELLOW_BIG, textWidth ); - title.Blit( screenOffset.x + 2 * columnStep + ( columnStep - title.w() ) / 2, offsetY ); - offsetY += title.h(); + title.Set( "Special Thanks to", Font::YELLOW_BIG, output.width() - columnStep ); + title.Blit( columnStep + ( output.width() - columnStep - title.w() ) / 2, offsetY, output ); + offsetY += title.h(); - const std::string contributors( "LeHerosInconnu\n" - "shprotru\n" - "undef21\n" - "vincent-grosbois\n" - "eos428\n" - "Andrii Kurdiumov\n" - "Vasilenko Alexey\n" - "Andrey Starodubtsev\n" - "dimag0g\n" - "and many other contributors!" ); + const std::string contributors( "Matt Taylor\n" + "Lipatov Kiril\n" + "Aleksei Mazur\n" + "William Hoskinson\n" + "and many-many other supporters!" ); - name.Set( contributors, Font::BIG, textWidth ); - name.Blit( screenOffset.x + 2 * columnStep + ( columnStep - name.w() ) / 2, offsetY ); - offsetY += name.h() + 10; + name.Set( contributors, Font::BIG, output.width() - columnStep ); + name.Blit( columnStep + ( output.width() - columnStep - title.w() ) / 2, offsetY, output ); + offsetY += name.h() + 10; - const fheroes2::Sprite & hydra = fheroes2::AGG::GetICN( ICN::HYDRA, 11 ); - fheroes2::Blit( hydra, display, screenOffset.x + 2 * columnStep + ( columnStep - hydra.width() ) / 2, offsetY ); - offsetY += hydra.height(); + const fheroes2::Sprite & miniPeasant = fheroes2::AGG::GetICN( ICN::MONS32, 0 ); + const fheroes2::Sprite & miniSwordsman = fheroes2::AGG::GetICN( ICN::MONS32, 5 ); + const fheroes2::Sprite & miniCrusader = fheroes2::AGG::GetICN( ICN::MONS32, 10 ); + const fheroes2::Sprite & miniTitan = fheroes2::AGG::GetICN( ICN::MONS32, 46 ); - title.Set( "Original project before 0.7", Font::YELLOW_SMALL, textWidth ); - title.Blit( screenOffset.x + 2 * columnStep + ( columnStep - title.w() ) / 2, offsetY ); - offsetY += title.h(); + const int32_t miniMonsterXOffset = columnStep + ( output.width() - columnStep ) / 2; + offsetY += miniTitan.height(); - name.Set( "Andrey Afletdinov\nhttps://sourceforge.net/\nprojects/fheroes2/", Font::SMALL, textWidth ); - name.Blit( screenOffset.x + 2 * columnStep + ( columnStep - name.w() ) / 2, offsetY ); + fheroes2::Blit( miniPeasant, 0, 0, output, miniMonsterXOffset - miniPeasant.width() - miniSwordsman.width(), offsetY - miniPeasant.height(), miniPeasant.width(), + miniPeasant.height() ); + fheroes2::Blit( miniSwordsman, 0, 0, output, miniMonsterXOffset - miniSwordsman.width(), offsetY - miniSwordsman.height(), miniSwordsman.width(), + miniSwordsman.height() ); + fheroes2::Blit( miniCrusader, 0, 0, output, miniMonsterXOffset, offsetY - miniCrusader.height(), miniCrusader.width(), miniCrusader.height() ); + fheroes2::Blit( miniTitan, 0, 0, output, miniMonsterXOffset + miniCrusader.width(), offsetY - miniTitan.height(), miniTitan.width(), miniTitan.height() ); + + fheroes2::Sprite creature = fheroes2::AGG::GetICN( ICN::MAGE2, 4 ); + transformToBlack( creature ); + + const int32_t creatureOffsetY = output.height() - 100; + fheroes2::Blit( creature, 0, 0, output, miniMonsterXOffset - creature.width() / 2, output.height() - 100, creature.width(), creature.height() ); + title.Set( "?", Font::YELLOW_BIG, 30 ); + title.Blit( miniMonsterXOffset - title.w() / 2, creatureOffsetY + creature.height() / 2, output ); + + return output; + } +} + +void Game::ShowCredits() +{ + // setup cursor + const CursorRestorer cursorRestorer( true, Cursor::POINTER ); - const fheroes2::Sprite & goblin = fheroes2::AGG::GetICN( ICN::GOBLIN, 27 ); - fheroes2::Blit( goblin, display, screenOffset.x + ( display.DEFAULT_WIDTH - goblin.width() ) / 2, screenOffset.y + ( display.DEFAULT_HEIGHT - goblin.height() ) / 2 ); + fheroes2::Display & display = fheroes2::Display::instance(); AGG::PlayMusic( MUS::VICTORY, true, true ); LocalEvent & le = LocalEvent::Get(); - while ( le.HandleEvents() ) { + + fheroes2::Image blackScreen( fheroes2::Display::DEFAULT_WIDTH, fheroes2::Display::DEFAULT_HEIGHT ); + blackScreen.fill( 0 ); + blackScreen._disableTransformLayer(); + + fheroes2::Image output( fheroes2::Display::DEFAULT_WIDTH, fheroes2::Display::DEFAULT_HEIGHT ); + output.reset(); + output._disableTransformLayer(); + + const uint64_t animationDelay = 50; + + std::vector pages; + pages.emplace_back( generateFirstPage() ); + pages.emplace_back( generateSecondPage() ); + + const fheroes2::Sprite header = generateHeader(); + + AnimationSequence sequence( static_cast( pages.size() ) ); + + bool fadeInHeader = true; + + while ( le.HandleEvents( Game::isCustomDelayNeeded( animationDelay ) ) ) { if ( le.KeyPress() || le.MouseClickLeft() || le.MouseClickMiddle() || le.MouseClickRight() ) break; + + if ( Game::validateCustomAnimationDelay( animationDelay ) ) { + if ( fadeInHeader && sequence.state() != AnimationState::FADING_IN ) { + fadeInHeader = false; + fheroes2::Copy( header, 0, 0, output, 0, 0, header.width(), header.height() ); + } + + if ( fadeInHeader ) { + output = blackScreen; + fheroes2::AlphaBlit( header, 0, 0, output, 0, 0, header.width(), header.height(), sequence.alpha() ); + + const fheroes2::Image & page = pages[sequence.pageId()]; + fheroes2::AlphaBlit( page, 0, 0, output, 0, header.height(), page.width(), page.height(), sequence.alpha() ); + } + else { + const fheroes2::Image & page = pages[sequence.pageId()]; + fheroes2::Copy( blackScreen, 0, 0, output, 0, header.height(), page.width(), page.height() ); + fheroes2::AlphaBlit( page, 0, 0, output, 0, header.height(), page.width(), page.height(), sequence.alpha() ); + } + + fheroes2::Resize( output, display ); + display.render(); + + sequence.increment(); + } } } diff --git a/src/fheroes2/game/game_logo.cpp b/src/fheroes2/game/game_logo.cpp index 9a2e63b5c01..01c08681529 100644 --- a/src/fheroes2/game/game_logo.cpp +++ b/src/fheroes2/game/game_logo.cpp @@ -44,13 +44,14 @@ void fheroes2::showTeamInfo() display.render(); uint8_t alpha = 250; + const uint64_t animationDelay = 40; - while ( le.HandleEvents() && alpha > 20 ) { + while ( le.HandleEvents( Game::isCustomDelayNeeded( animationDelay ) ) && alpha > 20 ) { if ( le.KeyPress() || le.MouseClickLeft() || le.MouseClickMiddle() || le.MouseClickRight() ) break; // Subsequent frames must update only the area within the text. - if ( Game::validateCustomAnimationDelay( 40 ) ) { + if ( Game::validateCustomAnimationDelay( animationDelay ) ) { Copy( textImage, 0, 0, display, roi.x, roi.y, roi.width, roi.height ); fheroes2::ApplyAlpha( display, roi.x, roi.y, display, roi.x, roi.y, roi.width, roi.height, alpha ); display.render( roi ); diff --git a/src/fheroes2/system/version.h b/src/fheroes2/system/version.h index df0fc6a2cf6..3c548800458 100644 --- a/src/fheroes2/system/version.h +++ b/src/fheroes2/system/version.h @@ -22,4 +22,4 @@ #define MAJOR_VERSION 0 #define MINOR_VERSION 9 -#define INTERMEDIATE_VERSION 3 +#define INTERMEDIATE_VERSION 4