diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp index 4378df8d..f750f62d 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.cpp +++ b/Adventures in Lestoria/AdventuresInLestoria.cpp @@ -2200,7 +2200,7 @@ datafiledoubledata AiL::GetDoubleList(std::string key){ } int main() -{ +{ { AiL demo; if (demo.Construct(WINDOW_SIZE.x, WINDOW_SIZE.y, 4, 4)) diff --git a/Adventures in Lestoria/SaveFile.cpp b/Adventures in Lestoria/SaveFile.cpp index cf13b2bc..901bbc16 100644 --- a/Adventures in Lestoria/SaveFile.cpp +++ b/Adventures in Lestoria/SaveFile.cpp @@ -123,6 +123,22 @@ const void SaveFile::SaveGame(){ utils::datafile::INITIAL_SETUP_COMPLETE=true; #ifdef __EMSCRIPTEN__ + std::stringstream fileContents; + std::ifstream file("save_file_path"_S+std::format("save.{:04}",saveFileID)); + while(file.good()){ + int val=file.get(); + if(val!=-1){ + fileContents<RetryResponse; RetryResponse=[&](std::string_view response){ if(response!="ERR"){ @@ -339,6 +355,12 @@ const void SaveFile::Server_SaveMetadataFile(std::functionSendRequest("save_server"_S,CreateServerRequest(SaveFileOperation::SAVE_METADATA_FILE,fileContents.str())); game->responseCallback=respCallbackFunc; } diff --git a/Adventures in Lestoria/Ursule.cpp b/Adventures in Lestoria/Ursule.cpp index 1877fefd..fc7169d4 100644 --- a/Adventures in Lestoria/Ursule.cpp +++ b/Adventures in Lestoria/Ursule.cpp @@ -53,6 +53,7 @@ INCLUDE_DATA using A=Attribute; void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy){ + switch(m.phase){ case 0:{ m.phase=ConfigInt("StartPhase"); @@ -74,7 +75,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy if(m.GetRemainingHPPct()<=ConfigFloat("Phase 2.Change")/100.f){ //before moving to Phase 2, we need to make sure we're in Phase 0 of the bear AI. if(m.overlaySpriteTransparency<210U){ - if(m.I(A::PHASE)!=0.f)goto bear; + if(m.I(A::PHASE)!=0)goto bear; else{ if(m.F(A::RUN_AWAY_TIMER)==0.f)m.F(A::RUN_AWAY_TIMER)=ConfigFloat("Phase 1.Fur Change Color Time"); else{ @@ -226,6 +227,39 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy m.F(A::CHARGE_COOLDOWN)=std::max(0.f,m.F(A::CHARGE_COOLDOWN)-fElapsedTime); if(m.I(A::PHASE)!=0)goto bear2; //Prevent doing anything else if a part of bear AI is still running. + + if(m.GetRemainingHPPct()<=ConfigFloat("Phase 4.Change")/100.f){ + + auto TransitionToPhase5=[&](){ + m.phase=5; + m.PerformOtherAnimation(1); + m.I(A::PHASE_REPEAT_COUNT)=ConfigInt("Phase 4.Wisp Pattern Spawn Count"); + SoundEffect::PlaySFX("Ursule Phase Transition",SoundEffect::CENTERED); + m.F(A::ENVIRONMENT_TIMER)=ConfigFloat("Phase 4.Environment Fade-out Time"); + m.I(A::ENVIRONMENT_PHASE)=0; + }; + + //We also need to move to the center of the map. + if(m.F(A::RUN_AWAY_TIMER)==0.f)m.F(A::RUN_AWAY_TIMER)=ConfigFloat("Phase 4.Run to Center Time"); + else{ + m.F(A::RUN_AWAY_TIMER)=std::max(0.f,m.F(A::RUN_AWAY_TIMER)-fElapsedTime); + if(m.F(A::RUN_AWAY_TIMER)==0.f){ + TransitionToPhase5(); + break; + } + } + vf2d mapCenter=game->GetCurrentMap().MapData.MapSize*vf2d{float(game->GetCurrentMap().MapData.tilewidth),float(game->GetCurrentMap().MapData.tileheight)}/2.0f; + float distToCenter=geom2d::line(m.GetPos(),mapCenter).length(); + if(distToCenter>4.0f){ + m.targetAcquireTimer=20.f; + m.target=mapCenter; + RUN_TOWARDS(m,fElapsedTime,"Run Towards"); + break; + }else{ //Now we're finally good for phase 2. + TransitionToPhase5(); + break; + } + } if(distToPlayer>=ConfigFloat("Phase 3.Charge Range")/100.f*24.f&&m.F(A::CHARGE_COOLDOWN)==0.f){ if(ConfigFloat("Phase 3.Charge Cast Time")!=0.f){ @@ -250,6 +284,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy BEAR(m,fElapsedTime,"Bear"); }break; case 4:{ //A charging phase. + if(geom2d::line(m.pos,m.target).length()>100*fElapsedTime*m.GetMoveSpdMult()){ vf2d newPos=m.pos+geom2d::line(m.pos,m.target).vector().norm()*100*fElapsedTime*m.GetMoveSpdMult(); m.SetPos(newPos); @@ -265,6 +300,66 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy m.PerformIdleAnimation(); } }break; + case 5:{ //Final boss phase. + #pragma region Environment Color Change Handling + m.F(A::ENVIRONMENT_TIMER)=std::max(0.f,m.F(A::ENVIRONMENT_TIMER)-fElapsedTime); + switch(m.I(A::ENVIRONMENT_PHASE)){ + case 0:{ //Fade out. Use the phase 3 environment fade-in color as the previous color to lerp out from. + game->SetWorldColor(ConfigPixel("Phase 3.Environment Fade-In Color")*util::lerp(0.f,1.0f,m.F(A::ENVIRONMENT_TIMER)/ConfigFloat("Phase 4.Environment Fade-out Time"))); + if(m.F(A::ENVIRONMENT_TIMER)==0.f){ + game->SetWorldColor({0,0,0,255}); + m.F(A::ENVIRONMENT_TIMER)=ConfigFloat("Phase 4.Environment Fade-in Time"); + m.I(A::ENVIRONMENT_PHASE)++; + game->SetWorldColorFunc([&](vi2d pos){ + float fadeInRange=DATA["MonsterStrategy"]["Ursule"]["Phase 4"]["Environment Fade-In Range"].GetReal()/100.f*24.f; + float distToPlayer=geom2d::line(game->GetPlayer()->GetPos(),pos).length(); + return game->GetWorldColor()*std::min(1.0f,fadeInRange/distToPlayer); + }); + } + }break; + case 1:{ //Fade in. + Pixel fadeInCol=ConfigPixel("Phase 4.Environment Fade-In Color"); + game->SetWorldColor(fadeInCol*util::lerp(1.f,0.f,m.F(A::ENVIRONMENT_TIMER)/ConfigFloat("Phase 4.Environment Fade-in Time"))); + if(m.F(A::ENVIRONMENT_TIMER)==0.f){ + game->SetWorldColor(fadeInCol); + } + }break; + } + #pragma endregion + + if(m.F(A::SHOOT_TIMER)==0.f){ + uint8_t wispPatternCount=ConfigInt("Wisp Pattern Count"); + + uint8_t wispPattern=util::random()%wispPatternCount; + + if(ConfigString("Phase 4.Wisp Pattern Random Selection")=="Bag"){ + if(m.VEC(A::WISP_PATTERN_LIST).size()==0){ + for(uint8_t numb=0;numb(m.VEC(A::WISP_PATTERN_LIST).back()); + m.VEC(A::WISP_PATTERN_LIST).pop_back(); + } + + m.F(A::SHOOT_TIMER)=ConfigFloat("Phase 4.Wisp Pattern Spawn Wait Time"); + + vi2d wispSize={ConfigIntArr("Phase 4.Wisp Size",0),ConfigIntArr("Phase 4.Wisp Size",1)}; + + float rowWidth=ConfigString(std::format("Wisp Pattern {}.Row[0]",wispPattern)).length()*wispSize.x; // Width of a wisp set in pixels. + float mapWidth=game->GetCurrentMap().MapData.MapSize.x*float(game->GetCurrentMap().MapData.tilewidth); + int rowCount=Config(std::format("Wisp Pattern {}",wispPattern)).GetKeys().size(); + for(float x=0;x(vf2d{x,ySpawn},vf2d{0,ConfigFloat("Phase 4.Wisp Speed")},wispSize.x/3.f,m.GetAttack(),m.OnUpperLevel(),false,ConfigPixel("Phase 4.Wisp Color"))); + } + } + } + } + }break; default:{ ERR(std::format("WARNING! Unknown phase {} for {} reached!",m.phase,m.GetName())); } diff --git a/Adventures in Lestoria/assets/config/MonsterStrategies.txt b/Adventures in Lestoria/assets/config/MonsterStrategies.txt index 71505565..5445ff7c 100644 --- a/Adventures in Lestoria/assets/config/MonsterStrategies.txt +++ b/Adventures in Lestoria/assets/config/MonsterStrategies.txt @@ -411,14 +411,29 @@ MonsterStrategy } Phase 4 { + # Maximum amount of time the boss takes to run towards the center before giving up and continuing through Phase 2. + Run to Center Time = 10.0s + # Percentage of health to transition to Phase 4 Change = 50% - # Percentage of damage reduced on the bear while the barrier is active. - Barrier Damage Reduction = 100% + # Amount of time the environment fades out to pitch black. + Environment Fade-out Time = 0.4s + + # Amount of time for the environment to fade in with the new color. + Environment Fade-in Time = 2.0s + + # New fade-in environment color. + Environment Fade-In Color = 255, 82, 82, 255 + + # The amount of range sight the player has with the new environment. + Environment Fade-In Range = 400 + + # Wisp size in pixels. + Wisp Size = 24,24 # Speed at which the wisp moves downwards. - Wisp Speed = 130% + Wisp Speed = 90 # RGBA value of the wisp. Wisp Color = 247, 95, 0, 255 @@ -426,9 +441,12 @@ MonsterStrategy # There are 6 rows of wisps and we want them to spawn outside the arena Wisp Pattern Spawn Y = -144 + # Amount of time the wisp fades out after hitting the player. + Wisp Fadeout Time = 0.3s + # How much time (in seconds) to wait between each pattern spawn. - # 130% speed means it takes 4.61 seconds for all the wisps to move entirely down. - Wisp Pattern Spawn Wait Time = 4.61s + # 100% speed means it takes 6 seconds for all the wisps to move entirely down. + Wisp Pattern Spawn Wait Time = 1.6s # This value is either Bag or Random. Bag means every pattern gets selected once before re-cycling. Random is truly random with potential repeats. Wisp Pattern Random Selection = Random diff --git a/Adventures in Lestoria/assets/config/Monsters.txt b/Adventures in Lestoria/assets/config/Monsters.txt index 38cfa03f..bdf2c934 100644 --- a/Adventures in Lestoria/assets/config/Monsters.txt +++ b/Adventures in Lestoria/assets/config/Monsters.txt @@ -386,6 +386,6 @@ Monsters ANIMATION[0] = 4, 0.1, OneShot ANIMATION[1] = 6, 0.15, OneShot ANIMATION[2] = 2, 0.2, Reverse - ANIMATION[3] = 5, 0.2, Repeat + ANIMATION[3] = 5, 0.1, Repeat } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 593278ce..9b09f0d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -326,6 +326,7 @@ if (EMSCRIPTEN) -sEXPORTED_RUNTIME_METHODS=stringToNewUTF8 -std=c++20 --proxy-to-worker + -lidbfs.js -O2 --preload-file ${SOURCE_DATA_DIR}@assets) else() @@ -341,6 +342,7 @@ if (EMSCRIPTEN) -sEXPORTED_RUNTIME_METHODS=stringToNewUTF8 -std=c++20 --proxy-to-worker + -lidbfs.js -O2 -sLLD_REPORT_UNDEFINED) endif()