Fiinished implementation of stack-based Effects system. Only remaining issue is edge-case when list is full (missing return path). Release Build 13190
All checks were successful
Emscripten Build / Build_and_Deploy_Web_Build (push) Successful in 8m36s

This commit is contained in:
AMay 2026-03-28 08:06:55 -05:00
parent 1089f00d26
commit 6d15c427a5
46 changed files with 411 additions and 437 deletions

View File

@ -231,7 +231,7 @@
<AdditionalIncludeDirectories>C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;$(ProjectDir)steam;$(ProjectDir)discord-files;C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\LabUser\Documents\include;C:\Users\niconiconii\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\niconiconii\OneDrive\Documents\include;C:\Users\niconiconii\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;C:\Users\sigon\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;C:\Users\sigon\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\sigon\OneDrive\Documents\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatSpecificWarningsAsErrors>4099;5030;4715;4172;4834</TreatSpecificWarningsAsErrors>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<AdditionalOptions>/w14099 /w15030 /w14715 /w14172 /w14834 %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions>/w14099 /w15030 /w14715 /w14172 /w14834 /bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -400,10 +400,6 @@
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="BlackHole.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="BlizzardSnowEmitter.h" />
<ClInclude Include="BombBoom.h">
<SubType>
@ -424,7 +420,6 @@
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="FallEffect.h" />
<ClInclude Include="FriendlyType.h" />
<ClInclude Include="HurtDamageInfo.h">
<SubType>
@ -489,7 +484,6 @@
</SubType>
</ClInclude>
<ClInclude Include="Error.h" />
<ClInclude Include="ExpandingRing.h" />
<ClInclude Include="FloatingMenuComponent.h">
<SubType>
</SubType>
@ -768,10 +762,6 @@
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="TrailEffect.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="Tutorial.h" />
<ClInclude Include="VisualNovel.h" />
<ClInclude Include="Test.h" />
@ -832,6 +822,7 @@
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="BlackHole.cpp" />
<ClCompile Include="BlacksmithCraftingWindow.cpp">
<SubType>
</SubType>
@ -872,6 +863,7 @@
</SubType>
</ClCompile>
<ClCompile Include="Error.cpp" />
<ClCompile Include="ExpandingRing.cpp" />
<ClCompile Include="ExplosiveTrap.cpp">
<SubType>
</SubType>
@ -880,6 +872,7 @@
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="FallEffect.cpp" />
<ClCompile Include="FlipCoinEffect.cpp">
<SubType>
</SubType>
@ -988,7 +981,6 @@
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="FallingDebris.h" />
<ClCompile Include="Feather.cpp">
<SubType>
</SubType>
@ -1300,6 +1292,7 @@
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="TrailEffect.cpp" />
<ClCompile Include="Trapper.cpp" />
<ClCompile Include="Turret.cpp" />
<ClCompile Include="Tutorial.cpp" />

View File

@ -666,9 +666,6 @@
<ClInclude Include="Pixel.h">
<Filter>Header Files\Engine</Filter>
</ClInclude>
<ClInclude Include="ExpandingRing.h">
<Filter>Source Files\Effects</Filter>
</ClInclude>
<ClInclude Include="TEST_DEFINES.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -693,12 +690,6 @@
<ClInclude Include="Timer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="BlackHole.h">
<Filter>Source Files\Effects</Filter>
</ClInclude>
<ClInclude Include="TrailEffect.h">
<Filter>Source Files\Effects</Filter>
</ClInclude>
<ClInclude Include="Oscillator.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -732,9 +723,6 @@
<ClInclude Include="BlizzardSnowEmitter.h">
<Filter>Source Files\Emitters</Filter>
</ClInclude>
<ClInclude Include="FallEffect.h">
<Filter>Source Files\Effects</Filter>
</ClInclude>
<ClInclude Include="TileGroupDataFile.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -842,9 +830,6 @@
<ClCompile Include="SlimeKing.cpp">
<Filter>Source Files\Monster Strategies</Filter>
</ClCompile>
<ClCompile Include="FallingDebris.h">
<Filter>Source Files\Effects</Filter>
</ClCompile>
<ClCompile Include="RunAway.cpp">
<Filter>Source Files\Monster Strategies</Filter>
</ClCompile>
@ -1382,6 +1367,18 @@
<ClCompile Include="ThunderOrb.cpp">
<Filter>Source Files\Bullet Types</Filter>
</ClCompile>
<ClCompile Include="TrailEffect.cpp">
<Filter>Source Files\Effects</Filter>
</ClCompile>
<ClCompile Include="BlackHole.cpp">
<Filter>Source Files\Effects</Filter>
</ClCompile>
<ClCompile Include="FallEffect.cpp">
<Filter>Source Files\Effects</Filter>
</ClCompile>
<ClCompile Include="ExpandingRing.cpp">
<Filter>Source Files\Effects</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />

View File

@ -809,22 +809,20 @@ void AiL::UpdateCamera(float fElapsedTime){
view.SetWorldOffset(camera.GetViewPosition());
}
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
void AiL::UpdateEffects(float fElapsedTime){
std::ranges::for_each(EMITTER_LIST,[&fElapsedTime](const std::unique_ptr<IEmitter>&emitter){
emitter->Update(fElapsedTime);
});
const auto RunEffectUpdate=[&](const std::array<EffectData,EFFECT_LIMIT>&arr,size_t&effCount){
const auto RunEffectUpdate=[&](std::array<EffectData,EFFECT_LIMIT>&arr,size_t&effCount){
size_t effectSlot{};
std::ranges::for_each(EffectManager::backgroundEffectList,[&fElapsedTime,&effectSlot](EffectData&effect){
std::ranges::for_each(arr,[&fElapsedTime,&effectSlot,&effCount](EffectData&effect){
enum class AliveState:bool{ALIVE,DEAD};
AliveState previousAliveState{*effect.lifetime<=0.f?AliveState::DEAD:AliveState::ALIVE};
std::visit(EffectOverloads{},*effect.effect,std::variant<float>{fElapsedTime});
AliveState newAliveState{*effect.lifetime<=0.f?AliveState::DEAD:AliveState::ALIVE};
if(previousAliveState==AliveState::ALIVE&&newAliveState==AliveState::DEAD)EffectManager::activeBackgroundEffectCount--;
AliveState previousAliveState{!effect.effect?AliveState::DEAD:AliveState::ALIVE};\
if(previousAliveState==AliveState::DEAD)return;
if(!std::visit(EffectOverloads{},*effect.effect,std::variant<float>{fElapsedTime})){effect.effect.reset();}
AliveState newAliveState{!effect.effect?AliveState::DEAD:AliveState::ALIVE};
if(previousAliveState==AliveState::ALIVE&&newAliveState==AliveState::DEAD)effCount--;
[[unlikely]]if(previousAliveState==AliveState::DEAD&&newAliveState==AliveState::ALIVE)ERR(std::format("WARNING! Effect in slot {} has resurrected! THIS SHOULD NOT BE HAPPENING!",effectSlot));
[[unlikely]]if(EffectManager::GetForegroundEffectCount()>=EFFECT_LIMIT||EffectManager::GetBackgroundEffectCount()>=EFFECT_LIMIT)ERR(std::format("WARNING! Effect count limits out-of-bounds! Foreground Count: {}, Background Count: {}, Limit: {} THIS SHOULD NOT BE HAPPENING!",EffectManager::GetForegroundEffectCount(),EffectManager::GetBackgroundEffectCount(),EFFECT_LIMIT));
effectSlot++;
@ -1064,15 +1062,15 @@ void AiL::RenderWorld(float fElapsedTime){
auto dropsAfterLower{ItemDrop::drops|std::views::filter([&pl](const ItemDrop&drop){return drop.GetPos().y>=pl->GetPos().y&&!drop.OnUpperLevel();})};
auto bulletsUpper{BULLET_LIST|std::views::filter([](const std::unique_ptr<IBullet>&bullet){return bullet->OnUpperLevel();})};
auto bulletsLower{BULLET_LIST|std::views::filter([](const std::unique_ptr<IBullet>&bullet){return !bullet->OnUpperLevel();})};
std::vector<std::reference_wrapper<EffectData>>activeForegroundEffects,activeBackgroundEffects;
std::ranges::for_each(EffectManager::foregroundEffectList,[&activeForegroundEffects=activeForegroundEffects](const EffectData&eff){if((*eff.lifetime)>=0)activeForegroundEffects.emplace_back(eff.effect);});
std::ranges::for_each(EffectManager::backgroundEffectList,[&activeBackgroundEffects=activeBackgroundEffects](const EffectData&eff){if((*eff.lifetime)>=0)activeBackgroundEffects.emplace_back(eff.effect);});
std::ranges::sort(activeForegroundEffects,[](const std::reference_wrapper<EffectData>&eff1,const std::reference_wrapper<EffectData>&eff2){return EFF(*eff1.get().effect).pos<EFF(*eff2.get().effect).pos;});
std::ranges::sort(activeBackgroundEffects,[](const std::reference_wrapper<EffectData>&eff1,const std::reference_wrapper<EffectData>&eff2){return EFF(*eff1.get().effect).pos<EFF(*eff2.get().effect).pos;});
auto foregroundEffectsUpper{activeForegroundEffects|std::views::filter([](const std::reference_wrapper<EffectData>&effect){return EFF(*effect.get().effect).OnUpperLevel();})};
auto foregroundEffectsLower{activeForegroundEffects|std::views::filter([](const std::reference_wrapper<EffectData>&effect){return !EFF(*effect.get().effect).OnUpperLevel();})};
auto backgroundEffectsUpper{activeBackgroundEffects|std::views::filter([](const std::reference_wrapper<EffectData>&effect){return EFF(*effect.get().effect).OnUpperLevel();})};
auto backgroundEffectsLower{activeBackgroundEffects|std::views::filter([](const std::reference_wrapper<EffectData>&effect){return !EFF(*effect.get().effect).OnUpperLevel();})};
std::vector<std::reference_wrapper<EffectT>>activeForegroundEffects,activeBackgroundEffects;
std::ranges::for_each(EffectManager::foregroundEffectList,[&activeForegroundEffects=activeForegroundEffects](EffectData&eff){if(eff.effect)activeForegroundEffects.emplace_back(std::ref(*eff.effect));});
std::ranges::for_each(EffectManager::backgroundEffectList,[&activeBackgroundEffects=activeBackgroundEffects](EffectData&eff){if(eff.effect)activeBackgroundEffects.emplace_back(std::ref(*eff.effect));});
std::ranges::sort(activeForegroundEffects,[](const std::reference_wrapper<EffectT>&eff1,const std::reference_wrapper<EffectT>&eff2){return EFF(eff1.get()).pos<EFF(eff2.get()).pos;});
std::ranges::sort(activeBackgroundEffects,[](const std::reference_wrapper<EffectT>&eff1,const std::reference_wrapper<EffectT>&eff2){return EFF(eff1.get()).pos<EFF(eff2.get()).pos;});
auto foregroundEffectsUpper{activeForegroundEffects|std::views::filter([](const std::reference_wrapper<EffectT>&effect){return EFF(effect.get()).OnUpperLevel();})};
auto foregroundEffectsLower{activeForegroundEffects|std::views::filter([](const std::reference_wrapper<EffectT>&effect){return !EFF(effect.get()).OnUpperLevel();})};
auto backgroundEffectsUpper{activeBackgroundEffects|std::views::filter([](const std::reference_wrapper<EffectT>&effect){return EFF(effect.get()).OnUpperLevel();})};
auto backgroundEffectsLower{activeBackgroundEffects|std::views::filter([](const std::reference_wrapper<EffectT>&effect){return !EFF(effect.get()).OnUpperLevel();})};
auto upperEndZones{GetActiveZonesForCurrentMap()["EndZone"]|std::views::filter([](const ZoneData&zone){return zone.isUpper;})};
auto endZones{GetActiveZonesForCurrentMap()["EndZone"]|std::views::filter([](const ZoneData&zone){return !zone.isUpper;})};
#pragma endregion
@ -1119,8 +1117,8 @@ void AiL::RenderWorld(float fElapsedTime){
if(player->PoisonArrowAutoAttackReady()&&player->poisonArrowLastParticleTimer==0.f){
const float particleSize{fmod(game->GetRunTime(),0.4f)<0.2f?1.f:1.5f};
game->AddEffect(Effect{player->GetPos()-vf2d{0,4.f}-player->GetFacingDirVector()*6.f,0.2f,"energy_particle.png",player->OnUpperLevel(),vf2d{particleSize,particleSize}*1.2f,0.05f,vf2d{},Pixel{0,255,255,120},util::random(2*PI),0.f,true});
game->AddEffect(Effect{player->GetPos()-vf2d{0,4.f}-player->GetFacingDirVector()*6.f,0.2f,"energy_particle.png",player->OnUpperLevel(),vf2d{particleSize,particleSize},0.05f,vf2d{},DARK_GREEN,util::random(2*PI)});
game->AddEffect(Effect{player->GetPos()-vf2d{0,4.f}-player->GetFacingDirVector()*6.f,0.2f,"energy_particle.png",player->OnUpperLevel(),vf2d{particleSize,particleSize}*1.2f,0.05f,vf2d{},Pixel{0,255,255,120},util::random(2*PI),0.f,true},false);
game->AddEffect(Effect{player->GetPos()-vf2d{0,4.f}-player->GetFacingDirVector()*6.f,0.2f,"energy_particle.png",player->OnUpperLevel(),vf2d{particleSize,particleSize},0.05f,vf2d{},DARK_GREEN,util::random(2*PI)},false);
player->poisonArrowLastParticleTimer=0.15f;
}
@ -1410,7 +1408,7 @@ void AiL::RenderWorld(float fElapsedTime){
{
auto it=backgroundEffectsLower.begin();
while(it!=backgroundEffectsLower.end()){
const Effect&e=EFF(*(*it).get().effect);
const Effect&e=EFF((*it).get());
e._Draw();
++it;
}
@ -1551,8 +1549,8 @@ void AiL::RenderWorld(float fElapsedTime){
m.Draw();
++monstersBeforeLowerIt;
}
for(std::reference_wrapper<EffectData>e:backgroundEffectsLower){
EFF(*e.get().effect)._Draw();
for(std::reference_wrapper<EffectT>e:backgroundEffectsLower){
EFF(e.get())._Draw();
}
while(dropsBeforeLowerIt!=dropsBeforeLower.end()){
const ItemDrop&drop{*dropsBeforeLowerIt};
@ -1582,8 +1580,8 @@ void AiL::RenderWorld(float fElapsedTime){
for(std::unique_ptr<IBullet>&b:bulletsLower){
b->_Draw();
}
for(std::reference_wrapper<EffectData>e:foregroundEffectsLower){
EFF(*e.get().effect)._Draw();
for(std::reference_wrapper<EffectT>e:foregroundEffectsLower){
EFF(e.get())._Draw();
}
#pragma endregion
#pragma region Permanent Foreground Rendering
@ -1705,7 +1703,7 @@ void AiL::RenderWorld(float fElapsedTime){
{
auto it=backgroundEffectsUpper.begin();
while(it!=backgroundEffectsUpper.end()){
const Effect&e=EFF(*(*it).get().effect);
const Effect&e=EFF((*it).get());
e._Draw();
++it;
}
@ -1858,8 +1856,8 @@ void AiL::RenderWorld(float fElapsedTime){
m.Draw();
++monstersBeforeUpperIt;
}
for(std::reference_wrapper<EffectData>e:backgroundEffectsUpper){
EFF(*e.get().effect)._Draw();
for(std::reference_wrapper<EffectT>e:backgroundEffectsUpper){
EFF(e.get())._Draw();
}
while(dropsBeforeUpperIt!=dropsBeforeUpper.end()){
const ItemDrop&drop{*dropsBeforeUpperIt};
@ -1887,8 +1885,8 @@ void AiL::RenderWorld(float fElapsedTime){
for(std::unique_ptr<IBullet>&b:bulletsUpper){
b->_Draw();
}
for(std::reference_wrapper<EffectData>e:foregroundEffectsUpper){
EFF(*e.get().effect)._Draw();
for(std::reference_wrapper<EffectT>e:foregroundEffectsUpper){
EFF(e.get())._Draw();
}
#pragma endregion
#pragma region Permanent Upper Foreground Rendering
@ -4534,10 +4532,8 @@ void AiL::AddToSpecialMarkedTargetList(std::tuple<std::weak_ptr<Monster>,StackCo
lockOnSpecialTargets.emplace_back(markData);
}
[[nodiscard]]std::vector<std::shared_ptr<Effect>>AiL::GetAllEffects()const{
std::vector<std::shared_ptr<Effect>>allEffects{};
std::ranges::merge(GetForegroundEffects(),GetBackgroundEffects(),std::back_inserter(allEffects));
return allEffects;
[[nodiscard]]std::pair<ForegroundEffectsArr,BackgroundEffectsArr>AiL::GetAllEffects()const{
return {EffectManager::GetForegroundEffects(),EffectManager::GetBackgroundEffects()};
}
void AiL::InitializeCamera(){
@ -4623,8 +4619,11 @@ void AiL::AddNotification(const Notification&n){
AiL::Notification::Notification(const std::string_view message,const float time,const Pixel col)
:message(message),time(time),col(col){}
const std::vector<std::shared_ptr<Effect>>AiL::GetEffect(EffectType type){
return GetAllEffects()|std::views::filter([&type](const std::shared_ptr<Effect>effect){return effect->GetType()==type;})|std::ranges::to<std::vector>();
const std::vector<std::reference_wrapper<EffectData>>AiL::GetEffect(EffectType type){
std::vector<std::reference_wrapper<EffectData>>foundEffects;
std::ranges::copy(GetAllEffects().first|std::views::filter([&type](const EffectData&effect){return EFF(*effect.effect).GetType()==type;})|std::ranges::to<std::vector>(),std::back_inserter(foundEffects));
std::ranges::copy(GetAllEffects().second|std::views::filter([&type](const EffectData&effect){return EFF(*effect.effect).GetType()==type;})|std::ranges::to<std::vector>(),std::back_inserter(foundEffects));
return foundEffects;
}
void AiL::PrecacheNewLevels(){

View File

@ -82,8 +82,9 @@ using HurtListItem=std::pair<std::variant<Monster*,Player*>,HurtReturnValue>;
using HurtList=std::vector<HurtListItem>;
using StackCount=uint8_t;
using MarkTime=float;
using ForegroundWrapper=EffectRef;
using BackgroundWrapper=EffectRef;
using ForegroundEffectsArr=std::array<EffectData,EFFECT_LIMIT>;
using BackgroundEffectsArr=std::array<EffectData,EFFECT_LIMIT>;
enum class KnockbackCondition{
KNOCKBACK_HURT_TARGETS, //Knockback only targets that took damage.
@ -200,7 +201,7 @@ public:
void RenderHud();
void RenderMenu();
bool MenuClicksDeactivated()const;
const std::vector<std::shared_ptr<Effect>>GetEffect(EffectType type);
const std::vector<std::reference_wrapper<EffectData>>GetEffect(EffectType type);
std::vector<Entity>GetTargetsInRange(vf2d pos,float radius,bool upperLevel,float z,const HurtType hurtTargets)const;
const HurtList Hurt(vf2d pos,float radius,int damage,bool upperLevel,float z,const HurtType hurtTargets,HurtFlag::HurtFlag hurtFlags=HurtFlag::NONE)const;
const HurtList HurtMonsterType(vf2d pos,float radius,int damage,bool upperLevel,float z,const std::string_view monsterName,HurtFlag::HurtFlag hurtFlags=HurtFlag::NONE)const;
@ -337,9 +338,7 @@ public:
void ResetLevelStates();
const std::vector<std::shared_ptr<Effect>>&GetForegroundEffects()const;
const std::vector<std::shared_ptr<Effect>>&GetBackgroundEffects()const;
std::vector<std::shared_ptr<Effect>>GetAllEffects()const; //Foreground and background effects in one vector.
std::pair<ForegroundEffectsArr,BackgroundEffectsArr>GetAllEffects()const; //Foreground and background effects in one vector. (Foreground,Background)
struct TileGroupData{
vi2d tilePos;
@ -352,12 +351,16 @@ public:
void PrecacheNewLevels();
bool savingFile=false;
template<typename T,typename U>
inline std::pair<EffectRef<T>,EffectRef<U>>AddEffect(Effect&&foreground,Effect&&background){
return std::pair<EffectRef<T>,EffectRef<U>>{AddEffect(static_cast<T>(foreground),false),AddEffect(static_cast<T>(background),false)};
}
//If back is true, places the effect in the background.
template<typename T>
inline EffectRef AddEffect(T&&effect,bool back=false){
inline EffectRef<T>AddEffect(const T&effect,bool back=false){
std::array<EffectData,EFFECT_LIMIT>&effectList{back?EffectManager::backgroundEffectList:EffectManager::foregroundEffectList};
for(size_t index{};const EffectData&data:effectList|std::views::drop_while([&index=index](const EffectData&data){
if(*data.lifetime>0.f)return true;
if(!data.effect)return true;
index++;
return false;
})|std::views::take(1)){
@ -366,12 +369,11 @@ public:
if(back)EffectManager::activeBackgroundEffectCount++;
else EffectManager::activeForegroundEffectCount++;
[[unlikely]]if(EffectManager::GetForegroundEffectCount()>EFFECT_LIMIT||EffectManager::GetBackgroundEffectCount()>EFFECT_LIMIT)ERR(std::format("WARNING! Effect count has exceeded the limit of {}! THIS SHOULD NOT BE HAPPENING",EFFECT_LIMIT))
return EffectRef{int(index),newEffectId};
return EffectRef<T>{int(index),newEffectId};
}
effectListIsFull:{
//Find oldest effect, overwrite that slot.
}
}
template<typename T>
inline std::pair<ForegroundWrapper,BackgroundWrapper>AddEffect(T&&foreground,T&&background){
return{AddEffect(foreground,false),AddEffect(background,false)};
}
private:
std::vector<TileRenderData*>tilesWithCollision,tilesWithoutCollision;

View File

@ -0,0 +1,19 @@
#include"EffectTypes.h"
#include"DEFINES.h"
#include"config.h"
INCLUDE_MONSTER_LIST
BlackHole::BlackHole(Oscillator<vf2d>pos,const std::string&img,float lifetime,bool onUpperLevel,Oscillator<vf2d>size,vf2d spd,Oscillator<Pixel>col,float rotation,float rotationSpd,bool additiveBlending,float particleSpawnFreq,const std::function<Effect(const Effect&self)>&particleGenerator)
:FadeInOutEffect(pos,img,lifetime,onUpperLevel,size,spd,col,rotation,rotationSpd,additiveBlending,particleSpawnFreq,particleGenerator){}
bool BlackHole::Update(float fElapsedTime){
for(std::shared_ptr<Monster>&m:MONSTER_LIST){
float distToMonster{util::distance(pos,m->GetPos())};
if(!m->IsSolid()&&m->OnUpperLevel()==OnUpperLevel()&&m->GetZ()<1.f&&distToMonster<="Black Hole"_ENC["PULL IN RADIUS"]/100.f*24){
float pullInForce{util::map_range<float>(distToMonster,0,"Black Hole"_ENC["PULL IN RADIUS"]/100.f*24,"Black Hole"_ENC["PULL IN FORCE MAX"],"Black Hole"_ENC["PULL IN FORCE MIN"])};
m->AddAddedVelocity(util::pointTo(m->GetPos(),pos)*pullInForce);
}
}
return FadeInOutEffect::Update(fElapsedTime);
}

View File

@ -1,59 +0,0 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2026 Amy Sigona <sigonasr2@gmail.com>
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions or derivations of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice. This list of conditions and the following disclaimer must be
reproduced in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Portions of this software are copyright © 2024 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#pragma once
#include "AdventuresInLestoria.h"
#include "util.h"
#include "Effect.h"
INCLUDE_MONSTER_LIST
struct BlackHole:FadeInOutEffect{
inline BlackHole(Oscillator<vf2d>pos,const std::string&img,float lifetime,bool onUpperLevel,Oscillator<vf2d>size,vf2d spd,Oscillator<Pixel>col,float rotation,float rotationSpd,bool additiveBlending=false,float particleSpawnFreq=0.f,const std::function<Effect(const Effect&self)>&particleGenerator={})
:FadeInOutEffect(pos,img,lifetime,onUpperLevel,size,spd,col,rotation,rotationSpd,additiveBlending,particleSpawnFreq,particleGenerator){}
inline bool Update(float fElapsedTime){
for(std::shared_ptr<Monster>&m:MONSTER_LIST){
float distToMonster{util::distance(pos,m->GetPos())};
if(!m->IsSolid()&&m->OnUpperLevel()==OnUpperLevel()&&m->GetZ()<1.f&&distToMonster<="Black Hole"_ENC["PULL IN RADIUS"]/100.f*24){
float pullInForce{util::map_range<float>(distToMonster,0,"Black Hole"_ENC["PULL IN RADIUS"]/100.f*24,"Black Hole"_ENC["PULL IN FORCE MAX"],"Black Hole"_ENC["PULL IN FORCE MIN"])};
m->AddAddedVelocity(util::pointTo(m->GetPos(),pos)*pullInForce);
}
}
return FadeInOutEffect::Update(fElapsedTime);
}
};

View File

@ -48,6 +48,36 @@ Blizzard::Blizzard(const vf2d pos,const float radius, const float lifetime,const
,snow(std::make_unique<BlizzardSnowEmitter>(pos,"circle.png",radius*1.25f,snowSizeRange,emitterFreq,lifetime,upperLevel)){
for(int i:std::ranges::iota_view(0,200))snow->Emit();
}
Blizzard&Blizzard::operator=(const Blizzard&newEff){
Effect::operator=(newEff);
this->damage=newEff.damage;
this->tickRate=newEff.tickRate;
this->tickTimer=newEff.tickTimer;
this->radius=newEff.radius;
this->friendly=newEff.friendly;
this->snow.reset(newEff.snow.get());
this->blizzardSFX=newEff.blizzardSFX;
return *this;
}
Blizzard&Blizzard::operator=(Blizzard&&newEff){
Effect::operator=(newEff);
this->damage=newEff.damage;
this->tickRate=newEff.tickRate;
this->tickTimer=newEff.tickTimer;
this->radius=newEff.radius;
this->friendly=newEff.friendly;
this->snow.reset(newEff.snow.get());
this->blizzardSFX=newEff.blizzardSFX;
return *this;
}
Blizzard::Blizzard(const Blizzard&newEff)
:Effect(dynamic_cast<Effect&>(const_cast<Blizzard&>(newEff))){
*this=newEff;
}
Blizzard::Blizzard(Blizzard&&newEff)
:Effect(dynamic_cast<Effect&>(const_cast<Blizzard&>(newEff))){
*this=newEff;
}
bool Blizzard::Update(float fElapsedTime){
if(!IsDead()){
blizzardSFX.Update();
@ -58,7 +88,7 @@ bool Blizzard::Update(float fElapsedTime){
tickTimer+=tickRate;
game->Hurt(pos,radius*0.9f,damage,OnUpperLevel(),z,friendly?HurtType::MONSTER:HurtType::PLAYER);
}
snow->Update(fElapsedTime);
}
snow->Update(fElapsedTime);
return Effect::Update(fElapsedTime);
}

View File

@ -39,7 +39,6 @@ All rights reserved.
#include"BlizzardSnowEmitter.h"
#include"AdventuresInLestoria.h"
#include"FallEffect.h"
INCLUDE_game
@ -57,5 +56,5 @@ void BlizzardSnowEmitter::Emit(){
const float colorInterpolation{util::random(1)};
const Pixel baseCol{104,215,239};
const uint8_t alpha{uint8_t(util::random_range(96,255))};
game->AddEffect(FallEffect{pos+vf2d{xSpd*timeToFall,0.f}+vf2d{dist,angle}.cart(),startingZ,gravity,img_filename,upperLevel,scale*vf2d{1.f,1.f},0.5f,vf2d{xSpd,0.f},Pixel{uint8_t(util::lerp(float(baseCol.r),255.f,colorInterpolation)),uint8_t(util::lerp(float(baseCol.g),255.f,colorInterpolation)),uint8_t(util::lerp(float(baseCol.b),255.f,colorInterpolation)),alpha},util::degToRad(util::random_range(0,360)),util::degToRad(util::random_range(-1.f,1.f)),true,EffectType::BLIZZARD_SNOW});
game->AddEffect(FallWShadowEffect{pos+vf2d{xSpd*timeToFall,0.f}+vf2d{dist,angle}.cart(),startingZ,gravity,img_filename,upperLevel,scale*vf2d{1.f,1.f},0.5f,vf2d{xSpd,0.f},Pixel{uint8_t(util::lerp(float(baseCol.r),255.f,colorInterpolation)),uint8_t(util::lerp(float(baseCol.g),255.f,colorInterpolation)),uint8_t(util::lerp(float(baseCol.b),255.f,colorInterpolation)),alpha},util::degToRad(util::random_range(0,360)),util::degToRad(util::random_range(-1.f,1.f)),true,EffectType::BLIZZARD_SNOW});
}

View File

@ -39,7 +39,6 @@ All rights reserved.
#include "Bullet.h"
#include "Direction.h"
#include "Effect.h"
#include "TrailEffect.h"
#include "Entity.h"
#include <variant>
#include <memory>
@ -61,7 +60,7 @@ struct FireBolt:public Bullet{
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
private:
std::weak_ptr<TrailEffect>flameTrail;
std::optional<EffectRef<TrailEffect>>flameTrail;
};
struct LightningBolt:public Bullet{

View File

@ -41,7 +41,6 @@ All rights reserved.
#include "DEFINES.h"
#include "util.h"
#include "SoundEffect.h"
#include "TrailEffect.h"
#include <ranges>
INCLUDE_game

View File

@ -42,7 +42,6 @@ All rights reserved.
#include "util.h"
INCLUDE_game
CollectCoinEffect::CollectCoinEffect(const Entity attachedTarget,float coinArcRiseZAmt,float lifetime,const std::string&imgFile,bool upperLevel,float size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
:Effect(attachedTarget.GetPos(),lifetime,imgFile,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending),attachedTarget(attachedTarget),sizeFlipper(-1.f,1.f,lifetime*2.f),zRiser(0.f,coinArcRiseZAmt,1/lifetime){
}

View File

@ -44,6 +44,7 @@ using BackdropName=std::string;
using MonsterSpawnerID=int;
#define EFFECT_LIMIT 1000
#define mutableconst mutable /*This type indicates our intention was for it to be const but the class requires move assignability.*/
#define INCLUDE_ANIMATION_DATA extern safemap<std::string,Animate2D::FrameSequence>ANIMATION_DATA;
#define INCLUDE_MONSTER_LIST extern std::vector<std::shared_ptr<Monster>>MONSTER_LIST;

View File

@ -37,7 +37,13 @@ All rights reserved.
#pragma endregion
#include "Monster.h"
#include"Effect.h"
DEFINE_STRATEGY(DONOTHING)
Effect a{vf2d{},0.f,"",true,1.f,2.f,vf2d{},BLACK};
Effect b{vf2d{},0.f,"",true,1.f,2.f,vf2d{},BLACK};
EffectT eff{a};
EffectT eff2=b;
eff=eff2;
END_STRATEGY

View File

@ -143,26 +143,15 @@ const bool Effect::IsDead()const{
return lifetime<=0.f;
}
EffectData::EffectData(){}
EffectData::EffectData(int effectId,Effect&&effect):effectId(effectId),effect(effect),lifetime(&effect.lifetime){}
EffectRef::EffectRef(int slotId,int effectId):slotId(slotId),effectId(effectId){}
bool EffectRef::expired()const{
const std::array<EffectManager::EffectData,EFFECT_LIMIT>&effectList{foregroundEffect?EffectManager::GetForegroundEffects():EffectManager::GetBackgroundEffects()};
return effectList.at(slotId).effectId!=effectId||*(effectList.at(slotId).lifetime)<=0.f;
};
bool EffectRef::operator==(const EffectRef&ref){
return !expired();
};
EffectData::EffectData(int effectId,const Effect&effect):effectId(effectId),effect(effect){}
void EffectManager::Reset(){
foregroundEffectList.fill({});
backgroundEffectList.fill({});
activeForegroundEffectCount=activeBackgroundEffectCount=0;
for(const EffectData&effData:foregroundEffectList|std::views::drop_while([](const EffectData&effData){return *effData.lifetime<=0.f;}))ERR(std::format("WARNING! The active effect count !=0! (Reported:{})! IT SHOULD BE CLEARED!!!",GetForegroundEffectCount()));
for(const EffectData&effData:backgroundEffectList|std::views::drop_while([](const EffectData&effData){return *effData.lifetime<=0.f;}))ERR(std::format("WARNING! The active effect count !=0! (Reported:{})! IT SHOULD BE CLEARED!!!",GetBackgroundEffectCount()));
for(const EffectData&effData:foregroundEffectList|std::views::drop_while([](const EffectData&effData){return !effData.effect.has_value();}))ERR(std::format("WARNING! The active effect count !=0! (Reported:{})! IT SHOULD BE CLEARED!!!",GetForegroundEffectCount()));
for(const EffectData&effData:backgroundEffectList|std::views::drop_while([](const EffectData&effData){return !effData.effect.has_value();}))ERR(std::format("WARNING! The active effect count !=0! (Reported:{})! IT SHOULD BE CLEARED!!!",GetBackgroundEffectCount()));
}
size_t EffectManager::GetForegroundEffectCount(){
@ -171,9 +160,9 @@ size_t EffectManager::GetForegroundEffectCount(){
size_t EffectManager::GetBackgroundEffectCount(){
return activeBackgroundEffectCount;
}
const std::array<EffectData,EFFECT_LIMIT>&EffectManager::GetForegroundEffects(){
std::array<EffectData,EFFECT_LIMIT>&EffectManager::GetForegroundEffects(){
return foregroundEffectList;
}
const std::array<EffectData,EFFECT_LIMIT>&EffectManager::GetBackgroundEffects(){
std::array<EffectData,EFFECT_LIMIT>&EffectManager::GetBackgroundEffects(){
return backgroundEffectList;
}

View File

@ -1,6 +1,8 @@
#pragma once
#include"EffectTypes.h"
#define EFF(variant) EffectManager::GetEffect(variant)
using EffectT=std::variant<
Effect,
Meteor,
@ -17,7 +19,12 @@ using EffectT=std::variant<
FlipCoinEffect,
CollectCoinEffect,
Blizzard,
FreezeGround
FreezeGround,
TrailEffect,
BlackHole,
ExpandingRing,
FallEffect,
FallWShadowEffect
>;
struct EffectOverloads{
@ -31,40 +38,56 @@ struct EffectManager{
friend class AiL;
struct EffectData{
EffectData();
EffectData(int effectId,Effect&&effect);
EffectData(int effectId,const Effect&effect);
int effectId{};
std::optional<EffectT>effect{};
float*lifetime{nullptr};
};
static void Reset();
static const std::array<EffectData,EFFECT_LIMIT>&GetForegroundEffects();
static const std::array<EffectData,EFFECT_LIMIT>&GetBackgroundEffects();
static std::array<EffectData,EFFECT_LIMIT>&GetForegroundEffects();
static std::array<EffectData,EFFECT_LIMIT>&GetBackgroundEffects();
static size_t GetForegroundEffectCount();
static size_t GetBackgroundEffectCount();
struct funcName_Overloads {
template<typename T>
const Effect& operator()(T&obj) {
return obj;
};
};
template<typename T>
static Effect& GetEffect(T& eff) {
return const_cast<Effect&>(std::visit(funcName_Overloads{},eff));
};
private:
inline static int GLOBAL_EFFECT_ID{0};
static std::array<EffectData,EFFECT_LIMIT>foregroundEffectList;
static size_t activeForegroundEffectCount;
static std::array<EffectData,EFFECT_LIMIT>backgroundEffectList;
std::vector<std::shared_ptr<Effect>>d;
static size_t activeBackgroundEffectCount;
inline static std::array<EffectData,EFFECT_LIMIT>foregroundEffectList{};
inline static size_t activeForegroundEffectCount{};
inline static std::array<EffectData,EFFECT_LIMIT>backgroundEffectList{};
inline static size_t activeBackgroundEffectCount{};
};
using EffectData=EffectManager::EffectData;
template<typename T>
struct EffectRef{
public:
EffectRef(int slotId,int effectId);
bool expired()const;
bool operator==(const EffectRef&ref);
template<typename T>
T&&get(){
std::array<EffectManager::EffectData,1000>&effectList{foregroundEffect?EffectManager::foregroundEffectList:EffectManager::backgroundEffectList};
inline EffectRef(int slotId,int effectId):slotId(slotId),effectId(effectId){}
inline bool expired()const{
const std::array<EffectManager::EffectData,EFFECT_LIMIT>&effectList{foregroundEffect?EffectManager::GetForegroundEffects():EffectManager::GetBackgroundEffects()};
return effectList.at(slotId).effectId!=effectId||effectList.at(slotId).effect;
};
inline bool operator==(const EffectRef&ref){
return !expired();
};
T&get(){
std::array<EffectManager::EffectData,1000>&effectList{foregroundEffect?EffectManager::GetForegroundEffects():EffectManager::GetBackgroundEffects()};
if(expired())ERR("WARNING! This effect was not properly checked for expiration! THIS IS NOT ALLOWED!");
return std::any_cast<T>(effectList.at(slotId).effect);
return std::get<T>(*effectList.at(slotId).effect);
};
private:
int slotId;
int effectId;
bool foregroundEffect{false};
};
};

View File

@ -58,18 +58,6 @@ enum class EffectType{
BLIZZARD_SNOW,
};
#define CREATE_VARIANT(funcName,funcDeclaration,returnFunc)\
struct funcName_Overloads{\
template<typename T>\
returnFunc\
};\
template<typename T>\
static funcDeclaration{\
return std::visit(Effect::funcName_Overloads{},eff);\
}
#define EFF(variant) Effect::GetEffect(variant)
struct Effect{
friend class AiL;
friend struct FallingBullet;
@ -100,7 +88,6 @@ public:
const bool&OnUpperLevel()const;
void SetType(const EffectType type);
const bool IsDead()const;
CREATE_VARIANT(GetEffect,Effect&GetEffect(T&eff),Effect&operator()(T&obj)const{return obj;});
protected:
float original_fadeOutTime;
@ -139,8 +126,8 @@ private:
float damageMult{1.f};
float fireRingLifetime{};
std::string meteorCrashSFX{};
const std::pair<MeteorDamage,PulsatingFireDamage>damage{};
const MeteorSetting setting;
mutableconst std::pair<MeteorDamage,PulsatingFireDamage>damage{};
mutableconst MeteorSetting setting;
};
struct PulsatingFire:Effect{
@ -154,7 +141,7 @@ struct PulsatingFire:Effect{
float lastDamageTimer=0;
float radius;
int damage;
const PulsatingFireSetting setting;
mutableconst PulsatingFireSetting setting;
bool Update(float fElapsedTime)override; //NOTE: In most cases, call Effect::Update() in your overwritten function!
void Draw(const Pixel blendCol)const override; //NOTE: In most cases, call Effect::Update() in your overwritten function!
};
@ -164,8 +151,8 @@ struct SwordSlash:Effect{
bool Update(float fElapsedTime)override; //NOTE: In most cases, call Effect::Update() in your overwritten function!
private:
HitList hitList;
const float damageMult;
const float swordSweepAngle;
mutableconst float damageMult;
mutableconst float swordSweepAngle;
};
//This draws effects using screen coordinates instead of world coordinates, useful for applying effects directly on the screen.
@ -198,7 +185,7 @@ struct ShineEffect:Effect{
virtual bool Update(float fElapsedTime)override final; //NOTE: In most cases, call Effect::Update() in your overwritten function!
private:
float fadeinTime;
const Pixel originalCol;
mutableconst Pixel originalCol;
};
//Spawns and moves towards the player after some amount of time.
@ -226,23 +213,23 @@ struct FadeInOutEffect:Effect{
virtual bool Update(float fElapsedTime)override; //NOTE: In most cases, call Effect::Update() in your overwritten function!
virtual void Draw(const Pixel blendCol)const override; //NOTE: In most cases, call Effect::Update() in your overwritten function!
std::function<Effect(const Effect&self)>particleGenerator;
const float particleSpawnFreq;
mutableconst float particleSpawnFreq;
Oscillator<vf2d>posOscillator;
Oscillator<vf2d>sizeOscillator;
Oscillator<Pixel>colOscillator;
float particleSpawnTimer{};
const float originalParticleSpawnTimer{};
mutableconst float originalParticleSpawnTimer{};
};
struct LingeringEffect:FadeInOutEffect{
LingeringEffect(vf2d pos,const std::string&img,const std::string&soundEffect,float radius,int damage,float damageFreq,const HurtType friendly,float lifetime,float cycleSpd,bool onUpperLevel,float size,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending=false,float particleSpawnFreq=0.f,const std::function<Effect(const Effect&self)>&particleGenerator={});
virtual bool Update(float fElapsedTime)override final; //NOTE: In most cases, call Effect::Update() in your overwritten function!
const int damage;
mutableconst int damage;
float damageTimer{};
const float originalDamageTimer{};
const float radius;
mutableconst float originalDamageTimer{};
mutableconst float radius;
HurtType friendly;
const size_t sfxID{};
mutableconst size_t sfxID{};
};
struct Ink:Effect{
@ -251,9 +238,9 @@ struct Ink:Effect{
Ink(vf2d pos,float radius,float lifetime,float inkSlowdownDuration,float inkSlowdownPct,float fadeinTime,float fadeoutTime,vf2d size,Pixel col,bool onUpperLevel,float rotation);
virtual bool Update(float fElapsedTime)override final; //NOTE: In most cases, call Effect::Update() in your overwritten function!
private:
const float radius;
const float inkSlowdownDuration;
const float inkSlowdownAmount;
mutableconst float radius;
mutableconst float inkSlowdownDuration;
mutableconst float inkSlowdownAmount;
};
struct FlipCoinEffect:Effect{
@ -271,7 +258,7 @@ struct CollectCoinEffect:Effect{
CollectCoinEffect(const Entity attachedTarget,float coinArcRiseZAmt,float lifetime,const std::string&imgFile,bool upperLevel,float size=1.0f,float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
virtual bool Update(float fElapsedTime)override; //NOTE: In most cases, call Effect::Update() in your overwritten function!
virtual void Draw(const Pixel blendCol)const override; //NOTE: In most cases, call Effect::Update() in your overwritten function!
const Entity attachedTarget;
mutableconst Entity attachedTarget;
Oscillator<float>sizeFlipper;
Oscillator<float>zRiser;
};
@ -279,6 +266,10 @@ struct CollectCoinEffect:Effect{
struct Blizzard:Effect{
public:
Blizzard(const vf2d pos,const float radius,const float lifetime,const int damage,const float tickRate,const std::pair<MinScale,MaxScale>snowSizeRange,const float emitterFreq,const bool upperLevel,const FriendlyType friendly);
Blizzard(const Blizzard&newEff);
Blizzard(Blizzard&&newEff);
Blizzard&operator=(const Blizzard&newEff);
Blizzard&operator=(Blizzard&&newEff);
bool Update(float fElapsedTime)override; //NOTE: In most cases, call Effect::Update() in your overwritten function!
private:
int damage;
@ -306,9 +297,53 @@ public:
FreezeGround(const vf2d pos,const float lifetime,const int damage,const FreezeGroundSettings settings,const bool upperLevel,const FriendlyType friendly);
bool Update(float fElapsedTime)override; //NOTE: In most cases, call Effect::Update() in your overwritten function!
private:
const int damage;
mutableconst int damage;
float tickTimer{};
std::unordered_set<Entity>entitiesInside;
const FriendlyType friendly;
const FreezeGroundSettings settings;
mutableconst FriendlyType friendly;
mutableconst FreezeGroundSettings settings;
};
struct TrailEffect:Effect{
TrailEffect(vf2d startPos,float lifetime,const std::string&imgFile,int damage,float damageTickFrequency,bool upperLevel,float fadeout,vf2d scale,float imgXOffsetSpd,Oscillator<Pixel>col,EffectType type=EffectType::NONE,bool additiveBlending=false);
void SetEndPos(const vf2d pos);
bool Update(float fElapsedTime);
void Draw(const Pixel blendCol)const;
private:
Oscillator<Pixel>col;
vf2d endPos{};
float imageXSpd;
float imageXOffset{};
int damage;
float damageTickFrequency;
float hitTimer{};
};
struct BlackHole:FadeInOutEffect{
BlackHole(Oscillator<vf2d>pos,const std::string&img,float lifetime,bool onUpperLevel,Oscillator<vf2d>size,vf2d spd,Oscillator<Pixel>col,float rotation,float rotationSpd,bool additiveBlending=false,float particleSpawnFreq=0.f,const std::function<Effect(const Effect&self)>&particleGenerator={});
bool Update(float fElapsedTime);
};
struct ExpandingRing:Effect{
ExpandingRing(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,vf2d expandingSize,vf2d size={1.f,1.f},float fadeout=0.f,vf2d spd={},Pixel col=WHITE,float rotation=0.f,float rotationSpd=0.f,bool additiveBlending=false);
bool Update(float fElapsedTime);
private:
mutableconst vf2d expandingSize;
};
class FallEffect:public Effect{
public:
FallEffect(vf2d pos, float lifetime,const std::string&imgFile, bool upperLevel,vf2d size={1,1},float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
bool Update(float fElapsedTime)override;
private:
mutableconst float gravity{20};
};
class FallWShadowEffect:public Effect{
public:
FallWShadowEffect(vf2d pos,const float z,const float gravity,const std::string&imgFile, bool upperLevel,vf2d size={1,1},float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false,const EffectType type=EffectType::NONE);
bool Update(float fElapsedTime)override;
private:
mutableconst float gravity;
};

View File

@ -42,6 +42,7 @@ All rights reserved.
#include"Buff.h"
#include"HurtDamageInfo.h"
#include"FriendlyType.h"
#include"DEFINES.h"
class Player;
class Monster;
@ -71,7 +72,7 @@ public:
const int GetAttack()const;
const FriendlyType IsFriendly()const;
private:
const std::variant<Monster*,Player*>entity;
mutableconst std::variant<Monster*,Player*>entity;
inline bool operator==(const Entity&rhs){return entity==rhs.entity;}
};

View File

@ -35,22 +35,13 @@ Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#include "Effect.h"
#include "DEFINES.h"
#include "safemap.h"
#pragma once
INCLUDE_GFX
#include "EffectTypes.h"
class FallEffect:public Effect{
const float gravity=20;
public:
inline FallEffect(vf2d pos, float lifetime,const std::string&imgFile, bool upperLevel,vf2d size={1,1},float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false)
:Effect(pos,lifetime,imgFile,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending){
}
inline bool Update(float fElapsedTime)override{
spd.y+=gravity*fElapsedTime;
return Effect::Update(fElapsedTime);
}
};
ExpandingRing::ExpandingRing(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,vf2d expandingSize,vf2d size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
:expandingSize(expandingSize),Effect(pos,lifetime,imgFile,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending){}
bool ExpandingRing::Update(float fElapsedTime){
size+=expandingSize*fElapsedTime;
return true;
}

View File

@ -103,8 +103,8 @@ void ExplosiveTrap::Detonate(){
}
}
std::weak_ptr<Effect>explodeEffect{game->AddEffect(Effect{pos,ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration()-0.2f,"explosionframes.png",OnUpperLevel(),scale*2.f,0.2f,vf2d{0,-6.f},WHITE,util::random(2*PI),0.f})};
explodeEffect.lock()->scaleSpd={0.125f,0.125f};
auto explodeEffect{game->AddEffect(Effect{pos,ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration()-0.2f,"explosionframes.png",OnUpperLevel(),scale*2.f,0.2f,vf2d{0,-6.f},WHITE,util::random(2*PI),0.f})};
explodeEffect.get().scaleSpd={0.125f,0.125f};
SoundEffect::PlaySFX("Explosion",pos);
explosionCount--;
if(explosionCount>0)rearmTime=0.6f;

View File

@ -30,23 +30,29 @@ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Portions of this software are copyright © 2024 The FreeType
Portions of this software are copyright © 2024 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#pragma once
#include "EffectTypes.h"
#include "Effect.h"
FallEffect::FallEffect(vf2d pos, float lifetime,const std::string&imgFile, bool upperLevel,vf2d size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
:Effect(pos,lifetime,imgFile,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending){}
bool FallEffect::Update(float fElapsedTime){
spd.y+=gravity*fElapsedTime;
return Effect::Update(fElapsedTime);
}
struct ExpandingRing:Effect{
inline ExpandingRing(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,vf2d expandingSize,vf2d size={1.f,1.f},float fadeout=0.f,vf2d spd={},Pixel col=WHITE,float rotation=0.f,float rotationSpd=0.f,bool additiveBlending=false)
:expandingSize(expandingSize),Effect(pos,lifetime,imgFile,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending){}
inline bool Update(float fElapsedTime){
size+=expandingSize*fElapsedTime;
return true;
}
private:
const vf2d expandingSize;
};
FallWShadowEffect::FallWShadowEffect(vf2d pos,const float z,const float gravity,const std::string&imgFile, bool upperLevel,vf2d size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending,const EffectType type)
:gravity(gravity),Effect(pos,INFINITY,imgFile,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending){
this->z=z;
this->SetType(type);
}
bool FallWShadowEffect::Update(float fElapsedTime){
z-=gravity*fElapsedTime;
if(z<=0.f)lifetime=0.f;
return Effect::Update(fElapsedTime);
}

View File

@ -1,60 +0,0 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2026 Amy Sigona <sigonasr2@gmail.com>
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions or derivations of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice. This list of conditions and the following disclaimer must be
reproduced in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Portions of this software are copyright © 2024 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#include "Effect.h"
#include "DEFINES.h"
#include "safemap.h"
INCLUDE_GFX
//This class is used when you want an effect to start in mid-air and fall down. It disappears upon hitting the ground.
class FallEffect:public Effect{
public:
inline FallEffect(vf2d pos,const float z,const float gravity,const std::string&imgFile, bool upperLevel,vf2d size={1,1},float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false,const EffectType type=EffectType::NONE)
:gravity(gravity),Effect(pos,INFINITY,imgFile,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending){
this->z=z;
this->SetType(type);
}
inline bool Update(float fElapsedTime)override{
z-=gravity*fElapsedTime;
if(z<=0.f)lifetime=0.f;
return Effect::Update(fElapsedTime);
}
private:
const float gravity;
};

View File

@ -41,7 +41,7 @@ All rights reserved.
#include "DEFINES.h"
#include "util.h"
#include "SoundEffect.h"
#include "TrailEffect.h"
INCLUDE_game
INCLUDE_MONSTER_LIST
@ -49,7 +49,7 @@ INCLUDE_MONSTER_LIST
FireBolt::FireBolt(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,FriendlyType friendly,Pixel col)
:Bullet(pos,vel,radius,damage,
"energy_bolt.png",upperLevel,false,INFINITE,true,friendly,col){
if(game->GetPlayer()->HasEnchant("Trail of Fire")&&friendly)flameTrail=DYNAMIC_POINTER_CAST<TrailEffect>(game->AddEffect(TrailEffect{pos,"Trail of Fire"_ENC["TRAIL DURATION"],"FlamesTexture.png",int("Trail of Fire"_ENC["TRAIL DAMAGE"]/100.f*game->GetPlayer()->GetAttack()),"Trail of Fire"_ENC["TRAIL TICK FREQUENCY"],upperLevel,1.f,vf2d{1.f,2.f},30000.f,Oscillator<Pixel>{{255,0,0,128},Pixel(0xE74F30),2.f},EffectType::TRAIL_OF_FIRE,true},true));
if(game->GetPlayer()->HasEnchant("Trail of Fire")&&friendly)flameTrail=game->AddEffect(TrailEffect{pos,"Trail of Fire"_ENC["TRAIL DURATION"],"FlamesTexture.png",int("Trail of Fire"_ENC["TRAIL DAMAGE"]/100.f*game->GetPlayer()->GetAttack()),"Trail of Fire"_ENC["TRAIL TICK FREQUENCY"],upperLevel,1.f,vf2d{1.f,2.f},30000.f,Oscillator<Pixel>{{255,0,0,128},Pixel(0xE74F30),2.f},EffectType::TRAIL_OF_FIRE,true},true);
SoundEffect::PlaySFX("Wizard Fire Bolt Shoot",SoundEffect::CENTERED);
}
@ -73,7 +73,7 @@ void FireBolt::Update(float fElapsedTime){
SoundEffect::PlaySFX("Wizard Fire Bolt Hit",pos);
}
if(!flameTrail.expired())flameTrail.lock()->SetEndPos(pos);
if(flameTrail&&!(*flameTrail).expired())(*flameTrail).get().SetEndPos(pos);
}
BulletDestroyState FireBolt::PlayerHit(Player*player)

View File

@ -5,6 +5,7 @@
INCLUDE_GFX
INCLUDE_game
FreezeGround::FreezeGround(const vf2d pos,const float lifetime,const int damage,const FreezeGroundSettings settings,const bool upperLevel,const FriendlyType friendly)
:damage(damage),friendly(friendly),settings(settings),Effect(pos,lifetime,"freezeground.png",upperLevel,settings.radius/100.f/(GFX["freezeground.png"].Sprite()->Size().x/24.f)*2.35f,0.5f,{},{199,247,239,32},0.f,0.f,true)
{

View File

@ -1,4 +1,4 @@
#pragma region License
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~

View File

@ -38,6 +38,9 @@ All rights reserved.
#include "BulletTypes.h"
#include "Attributable.h"
#include"AdventuresInLestoria.h"
INCLUDE_game
GhostSaber::GhostSaber(const vf2d pos,const std::weak_ptr<Monster>target,const float lifetime,const float distFromTarget,const float knockbackAmt,const float initialRot,const float radius,const float expandSpd,const int damage,const bool upperLevel,const float rotSpd,const FriendlyType friendly,const Pixel col,const vf2d scale,const float image_angle)
:Bullet(target.lock()->GetPos()+vf2d{distFromTarget,initialRot}.cart(),{},radius,damage,"ghost_dagger.png",upperLevel,true,INFINITE,false,friendly,col,scale,image_angle),attachedMonster(target),rotSpd(rotSpd),distFromTarget(distFromTarget),rot(initialRot),aliveTime(lifetime),knockbackAmt(knockbackAmt),expandSpd(expandSpd){}

View File

@ -46,6 +46,7 @@ using A=Attribute;
INCLUDE_game
INCLUDE_MONSTER_LIST
INCLUDE_GFX
DEFINE_STRATEGY(GIANT_OCTOPUS)
enum PhaseName{

View File

@ -37,6 +37,9 @@ All rights reserved.
#pragma endregion
#include "BulletTypes.h"
#include"AdventuresInLestoria.h"
INCLUDE_game
HomingBullet::HomingBullet(const vf2d pos,const Entity target,const float rotateTowardsSpeed,const float rotateTowardsSpeedCoveredInInk,const float lifetime,const float speed,const float radius,const int damage,const bool upperLevel,const FriendlyType friendly,const Pixel col,const vf2d scale,const float image_angle)
:Bullet(pos,util::pointTo(pos,target.GetPos())*speed,radius,damage,upperLevel,friendly,col,scale,image_angle),target(target),rotateTowardsSpeed(rotateTowardsSpeed),rotateTowardsSpeedCoveredInInk(rotateTowardsSpeedCoveredInInk){

View File

@ -37,8 +37,10 @@ All rights reserved.
#pragma endregion
#include "BulletTypes.h"
#include"AdventuresInLestoria.h"
INCLUDE_ANIMATION_DATA
INCLUDE_game
InkBullet::InkBullet(const vf2d pos,const vf2d targetPos,const vf2d vel,const float inkExplosionRadius,const float inkPuddleLifetime,const float inkSlowdownDuration,const float inkSlowdownPct,const float inkPuddleRadius,const bool upperLevel,const FriendlyType friendly)
:Bullet(pos,vel,inkExplosionRadius,0,"inkbullet.png",upperLevel,friendly),targetPos(targetPos),inkExplosionRadius(inkExplosionRadius),inkSlowdownPct(inkSlowdownPct),inkSlowdownDuration(inkSlowdownDuration),inkPuddleLifetime(inkPuddleLifetime),inkPuddleRadius(inkPuddleRadius){

View File

@ -38,7 +38,6 @@ All rights reserved.
#include "BulletTypes.h"
#include "AdventuresInLestoria.h"
#include "FallingDebris.h"
#include "util.h"
#include "SoundEffect.h"

View File

@ -66,6 +66,7 @@ INCLUDE_BULLET_LIST
INCLUDE_DATA
INCLUDE_SPAWNER_CONTROLLER
INCLUDE_SPAWNER_LIST
INCLUDE_GFX
safemap<std::string,std::function<void(Monster&,float,std::string)>>STRATEGY_DATA;
std::unordered_map<std::string,Renderable*>MonsterData::imgs;
@ -1245,8 +1246,8 @@ void Monster::OnDeath(){
float targetScale{radius/24};
float startingScale{GetCollisionRadius()/24};
std::weak_ptr<Effect>eff{game->AddEffect(Effect{pos,ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration()-0.2f,"explosionframes.png",OnUpperLevel(),startingScale,0.2f,vf2d{0,-6.f},WHITE,util::random(2*PI),0.f})};
eff.lock()->scaleSpd={(targetScale-startingScale)/ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration(),(targetScale-startingScale)/ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration()};
auto eff{game->AddEffect(Effect{pos,ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration()-0.2f,"explosionframes.png",OnUpperLevel(),startingScale,0.2f,vf2d{0,-6.f},WHITE,util::random(2*PI),0.f})};
eff.get().scaleSpd={(targetScale-startingScale)/ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration(),(targetScale-startingScale)/ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration()};
SoundEffect::PlaySFX("Explosion",pos);
#pragma endregion
}

View File

@ -55,8 +55,11 @@ public:
inline const T&get()const{
return currentVal;
};
const T&first;
const T&second;
mutableconst std::reference_wrapper<T>first;
std::reference_wrapper<T>second;
private:
T val1,val2;
T currentVal;

View File

@ -43,6 +43,7 @@ All rights reserved.
#include "BulletTypes.h"
INCLUDE_game
INCLUDE_GFX
DEFINE_STRATEGY(PIRATE_BUCCANEER)
#pragma region Phase, Animation, and Helper function setup

View File

@ -45,6 +45,7 @@ All rights reserved.
using A=Attribute;
INCLUDE_game
INCLUDE_GFX
DEFINE_STRATEGY(PIRATE_MARAUDER)
enum PhaseName{

View File

@ -496,7 +496,7 @@ void Player::Update(float fElapsedTime){
monsterPtr->Knockback(vf2d{knockbackAmt,knockbackDir}.cart());
}
#pragma endregion
game->AddEffect(Effect{GetPos(),"Warrior.Ability 2.EffectLifetime"_F,"ground-slam-attack-front.png",upperLevel,groundSlamVisualRange,"Warrior.Ability 2.EffectFadetime"_F},Effect{GetPos(),"Warrior.Ability 2.EffectLifetime"_F,"ground-slam-attack-back.png",upperLevel,groundSlamVisualRange,"Warrior.Ability 2.EffectFadetime"_F});
game->AddEffect<Effect,Effect>({GetPos(),"Warrior.Ability 2.EffectLifetime"_F,"ground-slam-attack-front.png",upperLevel,groundSlamVisualRange,"Warrior.Ability 2.EffectFadetime"_F},{GetPos(),"Warrior.Ability 2.EffectLifetime"_F,"ground-slam-attack-back.png",upperLevel,groundSlamVisualRange,"Warrior.Ability 2.EffectFadetime"_F});
SoundEffect::PlaySFX("Warrior Ground Slam",SoundEffect::CENTERED);
}
if(lastAnimationFlip>0){

View File

@ -38,6 +38,10 @@ All rights reserved.
#include "BulletTypes.h"
#include <ranges>
#include"SoundEffect.h"
#include"AdventuresInLestoria.h"
INCLUDE_game
PoisonBottle::PoisonBottle(vf2d pos,vf2d targetPos,const std::string&img,float explodeRadius,float bounceExplodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,int additionalBounceCount,bool upperLevel,bool hitsMultiple,float lifetime,FriendlyType friendly,Pixel col,vf2d scale,float image_angle)
:ThrownProjectile(pos,targetPos,img,explodeRadius,z,totalFallTime,totalRiseZAmt,damage,upperLevel,hitsMultiple,lifetime,friendly,col,scale,image_angle),bounceExplodeRadius(bounceExplodeRadius),additionalBounceCount(additionalBounceCount){

View File

@ -93,8 +93,8 @@ BulletDestroyState PurpleEnergyBall::PlayerHit(Player*player)
if(bounceCount<=0)fadeOutTime="Witch.Auto Attack.BulletHitFadeoutTime"_F;
else lastHitTimer=0.1f;
Deactivate();
std::weak_ptr<Effect>hitEffect{game->AddEffect(Effect{player->GetPos(),0,"purpleenergyball_hit.png",upperLevel,player->GetSizeMult(),"Witch.Auto Attack.SplashEffectFadeoutTime"_F,vf2d{},WHITE,util::random(2*PI),5*PI})};
hitEffect.lock()->scaleSpd={-0.3f,-0.3f};
auto hitEffect{game->AddEffect(Effect{player->GetPos(),0,"purpleenergyball_hit.png",upperLevel,player->GetSizeMult(),"Witch.Auto Attack.SplashEffectFadeoutTime"_F,vf2d{},WHITE,util::random(2*PI),5*PI})};
hitEffect.get().scaleSpd={-0.3f,-0.3f};
return BulletDestroyState::KEEP_ALIVE;
}
@ -107,7 +107,7 @@ BulletDestroyState PurpleEnergyBall::MonsterHit(Monster&monster,const uint8_t ma
if(bounceCount<=0)fadeOutTime="Witch.Auto Attack.BulletHitFadeoutTime"_F;
else lastHitTimer=0.1f;
Deactivate();
std::weak_ptr<Effect>hitEffect{game->AddEffect(Effect{monster.GetPos(),0,"purpleenergyball_hit.png",upperLevel,monster.GetSizeMult(),"Witch.Auto Attack.SplashEffectFadeoutTime"_F,vf2d{},WHITE,util::random(2*PI),5*PI})};
hitEffect.lock()->scaleSpd={-0.3f,-0.3f};
auto hitEffect{game->AddEffect(Effect{monster.GetPos(),0,"purpleenergyball_hit.png",upperLevel,monster.GetSizeMult(),"Witch.Auto Attack.SplashEffectFadeoutTime"_F,vf2d{},WHITE,util::random(2*PI),5*PI})};
hitEffect.get().scaleSpd={-0.3f,-0.3f};
return BulletDestroyState::KEEP_ALIVE;
}

View File

@ -37,6 +37,7 @@ All rights reserved.
#pragma endregion
#include "BulletTypes.h"
#include"Player.h"
RotateBullet::RotateBullet(vf2d pos,float startingAng,float startSpeed,float acc,float headingAngleChange,float radius,int damage,bool upperLevel,FriendlyType friendly,Pixel col)

View File

@ -40,7 +40,6 @@ All rights reserved.
#include "AdventuresInLestoria.h"
#include "MonsterStrategyHelpers.h"
#include "util.h"
#include "FallingDebris.h"
#include "SoundEffect.h"
INCLUDE_game
@ -68,7 +67,7 @@ DEFINE_STRATEGY(RUN_TOWARDS)
if(desiredTargetLine.length()>=ConfigInt("MaxDistance")/100.f*24){
//Trim to max distance desired.
m.target=desiredTargetLine.rpoint(ConfigInt("MaxDistance")/100.f*24);
} else {
}else{
m.target=desiredTargetLine.upoint(1.2f);
}
m.SetState(State::MOVE_TOWARDS);

View File

@ -42,7 +42,6 @@ All rights reserved.
#include "util.h"
#include "safemap.h"
#include "Effect.h"
#include "FallingDebris.h"
#include "MonsterAttribute.h"
#include "SoundEffect.h"

View File

@ -44,7 +44,6 @@ All rights reserved.
#include "BulletTypes.h"
#include "SoundEffect.h"
#include "StageMaskPolygon.h"
#include "ExpandingRing.h"
#include <ranges>
INCLUDE_game

View File

@ -53,15 +53,15 @@ ThrownProjectile::ThrownProjectile(vf2d pos,vf2d targetPos,const std::string&img
void ThrownProjectile::OnGroundLand(){
const HurtList hurtList{game->Hurt(pos,explodeRadius,damage,OnUpperLevel(),z,HurtType::MONSTER,HurtFlag::PLAYER_ABILITY)};
if(explodeEffect){
std::weak_ptr<Effect>explosionEffect{game->AddEffect(Effect{explodeEffect.value()})};
explosionEffect.lock()->pos+=pos;
auto explosionEffect{game->AddEffect(Effect{explodeEffect.value()})};
explosionEffect.get().pos+=pos;
}
if(explodeSoundEffect)SoundEffect::PlaySFX(explodeSoundEffect.value(),pos);
if(lingeringEffect){
std::weak_ptr<LingeringEffect>lingerEffect{DYNAMIC_POINTER_CAST<LingeringEffect>(game->AddEffect(LingeringEffect{lingeringEffect.value()}).lock())};
lingerEffect.lock()->posOscillator.val1+=pos;
lingerEffect.lock()->posOscillator.val2+=pos;
lingerEffect.lock()->pos+=pos;
auto lingerEffect{game->AddEffect(LingeringEffect{lingeringEffect.value()})};
lingerEffect.get().posOscillator.val1+=pos;
lingerEffect.get().posOscillator.val2+=pos;
lingerEffect.get().pos+=pos;
}
vel={};

View File

@ -0,0 +1,93 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2026 Amy Sigona <sigonasr2@gmail.com>
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions or derivations of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice. This list of conditions and the following disclaimer must be
reproduced in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Portions of this software are copyright © 2024 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#include"EffectTypes.h"
#include"DEFINES.h"
#include"SoundEffect.h"
#include"AdventuresInLestoria.h"
INCLUDE_game
INCLUDE_MONSTER_LIST
TrailEffect::TrailEffect(vf2d startPos,float lifetime,const std::string&imgFile,int damage,float damageTickFrequency,bool upperLevel,float fadeout,vf2d scale,float imgXOffsetSpd,Oscillator<Pixel>col,EffectType type,bool additiveBlending)
:endPos(startPos),imageXSpd(imgXOffsetSpd),damage(damage),col(col),damageTickFrequency(damageTickFrequency),Effect(startPos,lifetime,imgFile,upperLevel,0.f,fadeout,scale,{},type,WHITE,0.f,0.f,additiveBlending){}
void TrailEffect::SetEndPos(const vf2d pos){
this->endPos=pos;
}
bool TrailEffect::Update(float fElapsedTime){
col.Update(fElapsedTime);
imageXOffset+=imageXSpd*fElapsedTime;
rotation=util::angleTo(pos,endPos);
hitTimer-=fElapsedTime;
if(hitTimer<=0.f){
for(std::shared_ptr<Monster>&monster:MONSTER_LIST){
vf2d closestPointToFlameTrail{geom2d::closest(geom2d::line<float>{pos,endPos},monster->GetPos())};
float distToTrail{util::distance(monster->GetPos(),closestPointToFlameTrail)};
if(!monster->InUndamageableState(OnUpperLevel(),GetZ())&&distToTrail<=24*size.y){
monster->Hurt(damage,OnUpperLevel(),GetZ(),HurtFlag::DOT);
SoundEffect::PlaySFX("Burn",monster->GetPos());
}
}
hitTimer+=damageTickFrequency;
}
return Effect::Update(fElapsedTime);
}
void TrailEffect::Draw(const Pixel blendCol)const{
geom2d::line<float>flameTrailLine{pos,endPos};
geom2d::line<float>flameTrailLineRectLine1{pos+vf2d{GetFrame().GetSourceImage()->Sprite()->height/2.f*size.y,rotation-PI/2}.cart(),endPos+vf2d{GetFrame().GetSourceImage()->Sprite()->height/2.f*size.y,rotation-PI/2}.cart()};
geom2d::line<float>flameTrailLineRectLine2{pos+vf2d{GetFrame().GetSourceImage()->Sprite()->height/2.f*size.y,rotation+PI/2}.cart(),endPos+vf2d{GetFrame().GetSourceImage()->Sprite()->height/2.f*size.y,rotation+PI/2}.cart()};
vf2d flameTrailSize{flameTrailLine.length(),float(GetFrame().GetSourceImage()->Sprite()->height)};
flameTrailSize*=size;
const auto PixelToUVSpace=[this](vf2d pixelPos){
pixelPos.x+=imageXOffset;
return pixelPos/GetFrame().GetSourceImage()->Sprite()->Size()/size;
};
game->view.DrawPolygonDecal(GetFrame().GetSourceImage()->Decal(),std::vector<vf2d>{
flameTrailLine.upoint(0.5f),flameTrailLineRectLine1.start,flameTrailLineRectLine1.upoint(0.5f),flameTrailLineRectLine1.end,
flameTrailLineRectLine2.end,flameTrailLineRectLine2.upoint(0.5f),flameTrailLineRectLine2.start,flameTrailLineRectLine1.start,
},std::vector<vf2d>{
PixelToUVSpace(flameTrailSize/2),PixelToUVSpace({}),PixelToUVSpace({flameTrailSize.x/2.f,0}),PixelToUVSpace({flameTrailSize.x,0.f}),
PixelToUVSpace(flameTrailSize),PixelToUVSpace({flameTrailSize.x/2.f,flameTrailSize.y}),PixelToUVSpace({0.f,flameTrailSize.y}),PixelToUVSpace({}),
},std::vector<Pixel>{
col.get(),{0,0,0,0},col.get(),{0,0,0,0},
{0,0,0,0},col.get(),{0,0,0,0},{0,0,0,0},
},blendCol);
}

View File

@ -1,104 +0,0 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2026 Amy Sigona <sigonasr2@gmail.com>
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions or derivations of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice. This list of conditions and the following disclaimer must be
reproduced in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Portions of this software are copyright © 2024 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#pragma once
#include "AdventuresInLestoria.h"
#include "SoundEffect.h"
INCLUDE_game
INCLUDE_GFX
INCLUDE_MONSTER_LIST
struct TrailEffect:Effect{
inline TrailEffect(vf2d startPos,float lifetime,const std::string&imgFile,int damage,float damageTickFrequency,bool upperLevel,float fadeout,vf2d scale,float imgXOffsetSpd,Oscillator<Pixel>col,EffectType type=EffectType::NONE,bool additiveBlending=false)
:endPos(startPos),imageXSpd(imgXOffsetSpd),damage(damage),col(col),damageTickFrequency(damageTickFrequency),Effect(startPos,lifetime,imgFile,upperLevel,0.f,fadeout,scale,{},type,WHITE,0.f,0.f,additiveBlending){}
inline void SetEndPos(const vf2d pos){
this->endPos=pos;
}
inline bool Update(float fElapsedTime){
col.Update(fElapsedTime);
imageXOffset+=imageXSpd*fElapsedTime;
rotation=util::angleTo(pos,endPos);
hitTimer-=fElapsedTime;
if(hitTimer<=0.f){
for(std::shared_ptr<Monster>&monster:MONSTER_LIST){
vf2d closestPointToFlameTrail{geom2d::closest(geom2d::line<float>{pos,endPos},monster->GetPos())};
float distToTrail{util::distance(monster->GetPos(),closestPointToFlameTrail)};
if(!monster->InUndamageableState(OnUpperLevel(),GetZ())&&distToTrail<=24*size.y){
monster->Hurt(damage,OnUpperLevel(),GetZ(),HurtFlag::DOT);
SoundEffect::PlaySFX("Burn",monster->GetPos());
}
}
hitTimer+=damageTickFrequency;
}
return Effect::Update(fElapsedTime);
}
inline void Draw(const Pixel blendCol)const{
geom2d::line<float>flameTrailLine{pos,endPos};
geom2d::line<float>flameTrailLineRectLine1{pos+vf2d{GetFrame().GetSourceImage()->Sprite()->height/2.f*size.y,rotation-PI/2}.cart(),endPos+vf2d{GetFrame().GetSourceImage()->Sprite()->height/2.f*size.y,rotation-PI/2}.cart()};
geom2d::line<float>flameTrailLineRectLine2{pos+vf2d{GetFrame().GetSourceImage()->Sprite()->height/2.f*size.y,rotation+PI/2}.cart(),endPos+vf2d{GetFrame().GetSourceImage()->Sprite()->height/2.f*size.y,rotation+PI/2}.cart()};
vf2d flameTrailSize{flameTrailLine.length(),float(GetFrame().GetSourceImage()->Sprite()->height)};
flameTrailSize*=size;
const auto PixelToUVSpace=[this](vf2d pixelPos){
pixelPos.x+=imageXOffset;
return pixelPos/GetFrame().GetSourceImage()->Sprite()->Size()/size;
};
game->view.DrawPolygonDecal(GetFrame().GetSourceImage()->Decal(),std::vector<vf2d>{
flameTrailLine.upoint(0.5f),flameTrailLineRectLine1.start,flameTrailLineRectLine1.upoint(0.5f),flameTrailLineRectLine1.end,
flameTrailLineRectLine2.end,flameTrailLineRectLine2.upoint(0.5f),flameTrailLineRectLine2.start,flameTrailLineRectLine1.start,
},std::vector<vf2d>{
PixelToUVSpace(flameTrailSize/2),PixelToUVSpace({}),PixelToUVSpace({flameTrailSize.x/2.f,0}),PixelToUVSpace({flameTrailSize.x,0.f}),
PixelToUVSpace(flameTrailSize),PixelToUVSpace({flameTrailSize.x/2.f,flameTrailSize.y}),PixelToUVSpace({0.f,flameTrailSize.y}),PixelToUVSpace({}),
},std::vector<Pixel>{
col.get(),{0,0,0,0},col.get(),{0,0,0,0},
{0,0,0,0},col.get(),{0,0,0,0},{0,0,0,0},
},blendCol);
}
private:
Oscillator<Pixel>col;
vf2d endPos{};
float imageXSpd;
float imageXOffset{};
int damage;
float damageTickFrequency;
float hitTimer{};
};

View File

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 3
#define VERSION_PATCH 0
#define VERSION_BUILD 13079
#define VERSION_BUILD 13190
#define stringify(a) stringify_(a)
#define stringify_(a) #a

View File

@ -44,7 +44,6 @@ All rights reserved.
#include "config.h"
#include "util.h"
#include "SoundEffect.h"
#include "BlackHole.h"
INCLUDE_MONSTER_LIST
INCLUDE_BULLET_LIST
@ -146,18 +145,18 @@ void Wizard::InitializeClassAbilities(){
SoundEffect::PlaySFX("Wizard Teleport",SoundEffect::CENTERED);
};
std::vector<std::shared_ptr<Effect>>portals{game->GetEffect(EffectType::BLINK_PORTAL)};
std::vector<std::reference_wrapper<EffectData>>portals{game->GetEffect(EffectType::BLINK_PORTAL)};
if(portals.size()>0){
TeleportTo(portals[0]->pos);
portals[0]->lifetime=0.f;
portals[0]->fadeout=0.25f;
TeleportTo(EFF(*portals[0].get().effect).pos);
EFF(*portals[0].get().effect).lifetime=0.f;
EFF(*portals[0].get().effect).fadeout=0.25f;
return true;
}else if(p->lastPathfindingCooldown==0.f){
if(game->TestingModeEnabled()||dist>0&&p->CanPathfindTo(p->GetPos(),teleportPoint,float("Wizard.Right Click Ability.TilesMax"_I))
&&(NoTileCollisionExistsHere()||NoPlayerCollisionWithTile())){
if(p->HasEnchant("Blink Portal")){
std::weak_ptr<Effect>eff{game->AddEffect(FadeInOutEffect{Oscillator<vf2d>{p->GetPos(),p->GetPos()+vf2d{0,-6.f},0.5f},"portal.png","Blink Portal"_ENC["REACTIVATION TIME"],p->OnUpperLevel(),Oscillator<vf2d>{{0.9f,0.9f},{1.1f,1.1f},0.5f},vf2d{},Oscillator<Pixel>{WHITE,Pixel(0x50196f),0.5f},0.f,0.f},true)};
eff.lock()->SetType(EffectType::BLINK_PORTAL);
auto eff{game->AddEffect(FadeInOutEffect{Oscillator<vf2d>{p->GetPos(),p->GetPos()+vf2d{0,-6.f},0.5f},"portal.png","Blink Portal"_ENC["REACTIVATION TIME"],p->OnUpperLevel(),Oscillator<vf2d>{{0.9f,0.9f},{1.1f,1.1f},0.5f},vf2d{},Oscillator<Pixel>{WHITE,Pixel(0x50196f),0.5f},0.f,0.f},true)};
eff.get().SetType(EffectType::BLINK_PORTAL);
}
TeleportTo(teleportPoint);
p->lastPathfindingCooldown=0.1f;