diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj
index e296c594..1b8da299 100644
--- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj
+++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj
@@ -411,6 +411,10 @@
+
+
+
+
@@ -749,6 +753,10 @@
+
+
+
+
@@ -893,6 +901,7 @@
+
diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
index 2aa2f719..9d7625bf 100644
--- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
+++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
@@ -627,6 +627,12 @@
Header Files\steam
+
+ Header Files
+
+
+ Header Files
+
@@ -1001,6 +1007,9 @@
Source Files
+
+ Source Files
+
@@ -1166,6 +1175,9 @@
Configurations
+
+ Configurations
+
diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp
index 57c4fb43..2e6b63d9 100644
--- a/Adventures in Lestoria/AdventuresInLestoria.cpp
+++ b/Adventures in Lestoria/AdventuresInLestoria.cpp
@@ -92,7 +92,7 @@ bool _DEBUG_MAP_LOAD_INFO = false;
//360x240
vi2d WINDOW_SIZE={24*15,24*10};
safemapANIMATION_DATA;
-std::vectorMONSTER_LIST;
+std::vector>MONSTER_LIST;
std::vectorSPAWNER_LIST;
std::vector>DAMAGENUMBER_LIST;
std::vector>BULLET_LIST;
@@ -236,6 +236,9 @@ AiL::AiL()
std::string ACHIEVEMENT_CONFIG = CONFIG_PATH + "achievement_config"_S;
utils::datafile::Read(DATA,ACHIEVEMENT_CONFIG);
+ std::string MINIMAP_CONFIG = CONFIG_PATH + "minimap_config"_S;
+ utils::datafile::Read(DATA,MINIMAP_CONFIG);
+
utils::datafile::DEBUG_ACCESS_OPTIONS="debug_access_options"_I;
sAppName = "GAME_NAME"_S;
@@ -350,6 +353,8 @@ bool AiL::OnUserCreate(){
SetupDiscord();
#endif
+ minimap.Initialize();
+
gameInitialized=true;
if(!gamepack.Loaded()&&"GENERATE_GAMEPACK"_B){
@@ -734,23 +739,23 @@ void AiL::UpdateBullets(float fElapsedTime){
b->distanceTraveled+=totalDistance/24.f*100.f;
const auto CollisionCheck=[&](){
if(b->friendly){
- for(Monster&m:MONSTER_LIST){
- if(geom2d::overlaps(m.Hitbox(),geom2d::circle(b->pos,b->radius))){
- if(b->hitList.find(&m)==b->hitList.end()&&m.Hurt(b->damage,b->OnUpperLevel(),0)){
+ for(std::unique_ptr&m:MONSTER_LIST){
+ if(geom2d::overlaps(m->Hitbox(),geom2d::circle(b->pos,b->radius))){
+ if(b->hitList.find(&*m)==b->hitList.end()&&m->Hurt(b->damage,b->OnUpperLevel(),0)){
if(!b->hitsMultiple){
- if(b->MonsterHit(m)){
+ if(b->MonsterHit(*m)){
b->dead=true;
}
return false;
}
- b->hitList.insert(&m);
+ b->hitList.insert(&*m);
}
}
}
} else {
if(geom2d::overlaps(player->Hitbox(),geom2d::circle(b->pos,b->radius))){
if(player->Hurt(b->damage,b->OnUpperLevel(),0)){
- if(b->PlayerHit(player.get())){
+ if(b->PlayerHit(&*player)){
b->dead=true;
}
return false;
@@ -790,10 +795,10 @@ void AiL::UpdateBullets(float fElapsedTime){
}
const MonsterHurtList AiL::HurtEnemies(vf2d pos,float radius,int damage,bool upperLevel,float z)const{
MonsterHurtList hitList;
- for(Monster&m:MONSTER_LIST){
- if(geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(m.GetPos(),12*m.GetSizeMult()))){
- HurtReturnValue returnVal=m.Hurt(damage,upperLevel,z);
- hitList.push_back({&m,returnVal});
+ for(std::unique_ptr&m:MONSTER_LIST){
+ if(geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(m->GetPos(),12*m->GetSizeMult()))){
+ HurtReturnValue returnVal=m->Hurt(damage,upperLevel,z);
+ hitList.push_back({&*m,returnVal});
}
}
return hitList;
@@ -801,11 +806,11 @@ const MonsterHurtList AiL::HurtEnemies(vf2d pos,float radius,int damage,bool upp
const MonsterHurtList AiL::HurtEnemiesNotHit(vf2d pos,float radius,int damage,HitList&hitList,bool upperLevel,float z){
MonsterHurtList affectedList;
- for(Monster&m:MONSTER_LIST){
- if(!hitList.count(&m)&&geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(m.GetPos(),12*m.GetSizeMult()))){
- HurtReturnValue returnVal=m.Hurt(damage,upperLevel,z);
- affectedList.push_back({&m,returnVal});
- hitList.insert(&m);
+ for(std::unique_ptr&m:MONSTER_LIST){
+ if(!hitList.count(&*m)&&geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(m->GetPos(),12*m->GetSizeMult()))){
+ HurtReturnValue returnVal=m->Hurt(damage,upperLevel,z);
+ affectedList.push_back({&*m,returnVal});
+ hitList.insert(&*m);
}
}
return affectedList;
@@ -813,14 +818,14 @@ const MonsterHurtList AiL::HurtEnemiesNotHit(vf2d pos,float radius,int damage,Hi
const MonsterHurtList AiL::HurtEnemiesConeNotHit(vf2d pos,float radius,float angle,float sweepAngle,int damage,HitList&hitList,bool upperLevel,float z){
MonsterHurtList affectedList;
- for(Monster&m:MONSTER_LIST){
- if(!hitList.count(&m)&&geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(m.GetPos(),12*m.GetSizeMult()))){
- float angleToMonster=geom2d::line{pos,m.GetPos()}.vector().polar().y;
+ for(std::unique_ptr&m:MONSTER_LIST){
+ if(!hitList.count(&*m)&&geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(m->GetPos(),12*m->GetSizeMult()))){
+ float angleToMonster=geom2d::line{pos,m->GetPos()}.vector().polar().y;
float angleDiff=util::angle_difference(angleToMonster,angle);
if(abs(angleDiff)<=sweepAngle){
- HurtReturnValue returnVal=m.Hurt(damage,upperLevel,z);
- affectedList.push_back({&m,returnVal});
- hitList.insert(&m);
+ HurtReturnValue returnVal=m->Hurt(damage,upperLevel,z);
+ affectedList.push_back({&*m,returnVal});
+ hitList.insert(&*m);
}
}
}
@@ -851,14 +856,14 @@ void AiL::PopulateRenderLists(){
Player*pl=GetPlayer();
pl->rendered=false;
- std::sort(MONSTER_LIST.begin(),MONSTER_LIST.end(),[](Monster&m1,Monster&m2){return m1.GetPos().y&m1,std::unique_ptr&m2){return m1->GetPos().yGetPos().y;});
std::sort(ItemDrop::drops.begin(),ItemDrop::drops.end(),[](ItemDrop&id1,ItemDrop&id2){return id1.GetPos().y&b1,std::unique_ptr&b2){return b1->pos.ypos.y;});
std::sort(foregroundEffects.begin(),foregroundEffects.end(),[](std::unique_ptr&e1,std::unique_ptr&e2){return e1->pos.ypos.y;});
std::sort(backgroundEffects.begin(),backgroundEffects.end(),[](std::unique_ptr&e1,std::unique_ptr&e2){return e1->pos.ypos.y;});
for(auto it=MONSTER_LIST.begin();it!=MONSTER_LIST.end();++it){
- Monster&m=*it;
+ Monster&m=**it;
if(m.GetPos().yGetPos().y){//This monster renders before the player does (behind the player)
if(m.OnUpperLevel()){
monstersBeforeUpper.push_back(&m);
@@ -1021,8 +1026,8 @@ void AiL::RenderWorld(float fElapsedTime){
multiplierX*=(1-abs(cos(1.5f*reflectionStepTime))*"water_reflection_scale_factor"_F);
float reflectionRatioX=abs(sin(reflectionStepTime))*"water_reflection_scale_factor"_F;
RenderPlayer(player->GetPos()+vf2d{reflectionRatioX*player->GetFrame().GetSourceRect().size.x,float(player->GetFrame().GetSourceRect().size.y)-8}*player->GetSizeMult(),{multiplierX,-1});
- for(Monster&m:MONSTER_LIST){
- m.DrawReflection(reflectionRatioX,multiplierX);
+ for(std::unique_ptr&m:MONSTER_LIST){
+ m->DrawReflection(reflectionRatioX,multiplierX);
}
SetDecalMode(DecalMode::NORMAL);
}
@@ -1689,8 +1694,8 @@ void AiL::RenderWorld(float fElapsedTime){
}
}
- for(Monster&m:MONSTER_LIST){
- m.strategyDrawOverlay(this,m,MONSTER_DATA[m.GetName()].GetAIStrategy());
+ for(std::unique_ptr&m:MONSTER_LIST){
+ m->strategyDrawOverlay(this,*m,MONSTER_DATA[m->GetName()].GetAIStrategy());
}
#ifdef _DEBUG
@@ -1805,6 +1810,10 @@ void AiL::RenderHud(){
std::string displayText=player->notificationDisplay.first;
DrawShadowStringPropDecal(vf2d{float(ScreenWidth()/2),float(ScreenHeight()/4)-24}-GetTextSizeProp(displayText)/2,displayText,BLUE,VERY_DARK_BLUE);
}
+
+ minimap.Update();
+ minimap.Draw();
+
DisplayBossEncounterInfo();
#ifdef _DEBUG
if("debug_player_info"_I){
@@ -1989,6 +1998,8 @@ void AiL::InitializeLevel(std::string mapFile,MapName map){
if(MAP_TILESETS.find("assets/maps/"+baseSourceDir)==MAP_TILESETS.end()){
TSXParser tileset(baseDir+tag.data["source"]);
Renderable*r=NEW Renderable();
+
+ if(tileset.GetData().tilewidth==0||tileset.GetData().tileheight==0)ERR(std::format("WARNING! Failed to load map {}! Found a tileset {} with a width of {} and height of {}. Zero values are not allowed!",map,"assets/maps/"+baseSourceDir,tileset.GetData().tilewidth,tileset.GetData().tileheight));
MAP_TILESETS["assets/maps/"+baseSourceDir].tilewidth=tileset.GetData().tilewidth;
MAP_TILESETS["assets/maps/"+baseSourceDir].tileheight=tileset.GetData().tileheight;
MAP_TILESETS["assets/maps/"+baseSourceDir].tileset=r;
@@ -2049,6 +2060,8 @@ void AiL::InitializeLevel(std::string mapFile,MapName map){
SetDrawTarget(nullptr);
r->Decal()->Update();
}
+
+ ComputeModeColors(MAP_TILESETS["assets/maps/"+baseSourceDir]);
}
}
@@ -2215,6 +2228,13 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
});
#pragma endregion
+ #pragma region Foreground and Upper Foreground Tile Fade Group Setup (Loading phase 3.5)
+ LoadingScreen::AddPhase([&](){
+ minimap.Reset();
+ return true;
+ });
+ #pragma endregion
+
#pragma region Foreground and Upper Foreground Tile Fade Group Setup (Loading phase 4)
LoadingScreen::AddPhase([&](){
std::setforegroundTilesAdded,upperForegroundTilesAdded;
@@ -2400,9 +2420,9 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
LoadingScreen::AddPhase([&](){
for(NPCData data:game->MAP_DATA[game->GetCurrentLevel()].npcs){
if(Unlock::IsUnlocked(data.unlockCondition)){
- MONSTER_LIST.push_back(Monster{data.spawnPos,MONSTER_DATA[data.name]});
- MONSTER_LIST.back().iframe_timer=INFINITE;
- MONSTER_LIST.back().npcData=data;
+ MONSTER_LIST.push_back(std::make_unique(data.spawnPos,MONSTER_DATA[data.name]));
+ MONSTER_LIST.back()->iframe_timer=INFINITE;
+ MONSTER_LIST.back()->npcData=data;
}
}
return true;
@@ -2498,18 +2518,24 @@ TilesheetData AiL::GetTileSheet(MapName map,int tileID){
if(tileData.size()==1){
size_t slashMarkerSourceDir = tileData[0].data["source"].find_last_of('/');
std::string baseSourceDir=tileData[0].data["source"].substr(slashMarkerSourceDir+1);
- return {&MAP_TILESETS["assets/maps/"+baseSourceDir],1};
+ return {&MAP_TILESETS["assets/maps/"+baseSourceDir],1,MAP_TILESETS["assets/maps/"+baseSourceDir].tilecols[tileID]};
} else {
for (int i=1;iAiL::GetTileCollision(MapName map,vf2d pos,bool upperLevel){
- if(pos.x<0||pos.y<0||pos.x>=GetCurrentMapData().width*game->GetCurrentMapData().tilewidth||pos.y>=GetCurrentMapData().height*game->GetCurrentMapData().tilewidth)return NO_COLLISION;
- if(GetCurrentMap().optimizedTile)return NO_COLLISION; //Overworld map has no collision.
+ MapTag&mapData=MAP_DATA[map].MapData;
+ if(pos.x<0||pos.y<0||pos.x>=mapData.width*mapData.tilewidth||pos.y>=mapData.height*mapData.tilewidth)return NO_COLLISION;
+ if(MAP_DATA[map].optimizedTile)return NO_COLLISION; //Overworld map has no collision.
bool hasTerrain=false;
- for(const LayerTag&layer:GetCurrentMap().LayerData){ //Figure out if any tile at this position is terrain. If so, we have a collision box to check.
+ for(const LayerTag&layer:MAP_DATA[map].LayerData){ //Figure out if any tile at this position is terrain. If so, we have a collision box to check.
if(Unlock::IsUnlocked(layer.unlockCondition)){
- int tileID=layer.tiles[pos.y/GetCurrentMapData().tilewidth][pos.x/GetCurrentMapData().tilewidth]-1;
+ int tileID=layer.tiles[pos.y/mapData.tilewidth][pos.x/mapData.tilewidth]-1;
if(tileID==-1)continue;
- const TilesheetData&data=GetTileSheet(GetCurrentLevel(),tileID);
+ const TilesheetData&data=GetTileSheet(map,tileID);
if(data.tileset->isTerrain){
hasTerrain=true;
break;
}
}
}
- if(!hasTerrain)return geom2d::rect({0.f,0.f},{float(GetCurrentMapData().tilewidth),float(GetCurrentMapData().tilewidth)}); //We assume no terrain means we can't walk on this.
+ if(!hasTerrain)return geom2d::rect({0.f,0.f},{float(mapData.tilewidth),float(mapData.tilewidth)}); //We assume no terrain means we can't walk on this.
#pragma region Lower Bridge Collision Check
if(!upperLevel){ //We are looking for lower bridge collisions.
for(ZoneData&zone:MAP_DATA[map].ZoneData["LowerBridgeCollision"]){
if(geom2d::contains(zone.zone,pos)){
- return {{0,0},{float(game->GetCurrentMapData().tilewidth),float(game->GetCurrentMapData().tilewidth)}};
+ return {{0,0},{float(mapData.tilewidth),float(mapData.tilewidth)}};
}
}
}
#pragma endregion
//The logic here is, if there's a tile on the bridge, we respect that tile instead if we're on the upper level. So we don't check other layers when we are on the upper level and there is a tile below us.
if(upperLevel&&bridgeLayerIndex!=-1){
- int tileID=MAP_DATA[map].LayerData[bridgeLayerIndex].tiles[int(pos.y)/GetCurrentMapData().tilewidth][int(pos.x)/GetCurrentMapData().tilewidth]-1;
+ int tileID=MAP_DATA[map].LayerData[bridgeLayerIndex].tiles[int(pos.y)/mapData.tilewidth][int(pos.x)/mapData.tilewidth]-1;
if(tileID!=-1){
if (GetTileSheet(map,tileID%1000000).tileset->collision.find(tileID%1000000-GetTileSheet(map,tileID%1000000).firstgid+1)!=GetTileSheet(map,tileID%1000000).tileset->collision.end()){
return GetTileSheet(map,tileID%1000000).tileset->collision[tileID%1000000-GetTileSheet(map,tileID%1000000).firstgid+1].collision;
@@ -2570,7 +2597,7 @@ geom2d::rectAiL::GetTileCollision(MapName map,vf2d pos,bool upperLevel){
if(Unlock::IsUnlocked(layer.unlockCondition)){
//auto HasNoClass=[&](){return layer.tag.data.find("class")==layer.tag.data.end();};
if(counter!=bridgeLayerIndex){
- int tileID=layer.tiles[int(pos.y)/GetCurrentMapData().tilewidth][int(pos.x)/GetCurrentMapData().tilewidth]-1;
+ int tileID=layer.tiles[int(pos.y)/mapData.tilewidth][int(pos.x)/mapData.tilewidth]-1;
if(tileID!=-1&&GetTileSheet(map,tileID%1000000).tileset->collision.find(tileID%1000000-GetTileSheet(map,tileID%1000000).firstgid+1)!=GetTileSheet(map,tileID%1000000).tileset->collision.end()){
geom2d::rectcollisionRect=GetTileSheet(map,tileID%1000000).tileset->collision[tileID%1000000-GetTileSheet(map,tileID%1000000).firstgid+1].collision;
if(foundRect==NO_COLLISION){
@@ -2590,6 +2617,25 @@ geom2d::rectAiL::GetTileCollision(MapName map,vf2d pos,bool upperLevel){
return foundRect;
}
+Pixel AiL::GetTileColor(MapName map,vf2d pos,bool upperLevel){
+ MapTag&mapData=MAP_DATA[map].MapData;
+ if(pos.x<0||pos.y<0||pos.x>=mapData.width*mapData.tilewidth||pos.y>=mapData.height*mapData.tilewidth)return BLANK;
+ if(MAP_DATA[map].optimizedTile)return BLANK; //Overworld map has no collision.
+
+
+ bool hasTerrain=false;
+ for(const LayerTag&layer:MAP_DATA[map].LayerData){ //Figure out if any tile at this position is terrain. If so, we have a collision box to check.
+ if(Unlock::IsUnlocked(layer.unlockCondition)){
+ int tileID=layer.tiles[pos.y/mapData.tilewidth][pos.x/mapData.tilewidth]-1;
+ if(tileID==-1)continue;
+ const TilesheetData&data=GetTileSheet(map,tileID);
+ return data.tilecol;
+ }
+ }
+
+ return BLANK;
+}
+
const MapName&AiL::GetCurrentLevel()const{
if(GameState::STATE!=nullptr&&GameState::STATE==GameState::states[States::STORY]){ //If we're inside a story, we expect the map/level name to be from the visual novel itself. Right now currentLevel would be WORLD_MAP which is not useful for a function like this.
return VisualNovel::novel.storyLevel;
@@ -3889,12 +3935,12 @@ rcode AiL::LoadResource(Renderable&renderable,std::string_view imgPath,bool filt
}
void AiL::UpdateMonsters(){
- for(Monster&m:MONSTER_LIST){
- m.Update(game->GetElapsedTime());
+ for(std::unique_ptr&m:MONSTER_LIST){
+ m->Update(game->GetElapsedTime());
}
for(Monster&m:game->monstersToBeSpawned){
size_t prevCapacity=MONSTER_LIST.capacity();
- MONSTER_LIST.push_back(m);
+ MONSTER_LIST.push_back(std::make_unique(m));
if(MONSTER_LIST.capacity()>prevCapacity)LOG(std::format("WARNING! The monster list has automatically reserved more space and resized to {}! This caused one potential frame where bullet/effect hitlists that stored information on what monsters were hit to potentially be hit a second time or cause monsters that should've been hit to never be hit. Consider starting with a larger default reserved size for MONSTER_LIST if your intention was to have this many monsters!",MONSTER_LIST.capacity()));
}
game->monstersToBeSpawned.clear();
@@ -3983,4 +4029,28 @@ void AiL::SetCompletedStageFlag(){
}
void AiL::ResetCompletedStageFlag(){
prevStageCompleted=false;
+}
+void AiL::ComputeModeColors(TilesetData&tileset){
+ for(int y=0;ySprite()->height/tileset.tileheight;y++){
+ for(int x=0;xSprite()->width/tileset.tilewidth;x++){
+ #pragma region Individual tile iteration
+ std::unordered_mappixelCounts;
+ Pixel modeCol=BLANK;
+ int maxCount=0;
+ vi2d pixelOffset=vi2d{x,y}*tileset.tilewidth;
+ for(int pixelY=0;pixelYSprite()->GetPixel(targetPixel);
+ pixelCounts[tileCol.n]++;
+ if(pixelCounts[tileCol.n]>maxCount){
+ modeCol=tileCol;
+ pixelCounts[tileCol.n]=maxCount;
+ }
+ }
+ }
+ tileset.tilecols.push_back(modeCol);
+ #pragma endregion
+ }
+ }
}
\ No newline at end of file
diff --git a/Adventures in Lestoria/AdventuresInLestoria.h b/Adventures in Lestoria/AdventuresInLestoria.h
index d066687a..5add30c5 100644
--- a/Adventures in Lestoria/AdventuresInLestoria.h
+++ b/Adventures in Lestoria/AdventuresInLestoria.h
@@ -56,6 +56,7 @@ All rights reserved.
#include "olcPixelGameEngine.h"
#include "DynamicCounter.h"
#include "UndefKeys.h"
+#include "Minimap.h"
class SteamKeyboardCallbackHandler;
@@ -74,6 +75,7 @@ class AiL : public olc::PixelGameEngine
friend class SaveFile;
friend class sig::Animation;
friend class Audio;
+ friend class Minimap;
std::unique_ptrplayer;
SplashScreen splash;
public:
@@ -188,6 +190,8 @@ private:
void ValidateGameStatus();
void _PrepareLevel(MapName map,MusicChange changeMusic);
+ //This function assigns the mode tile colors of each loaded tileset.
+ void ComputeModeColors(TilesetData&tileset);
#ifndef __EMSCRIPTEN__
::discord::Result SetupDiscord();
#endif
@@ -250,6 +254,7 @@ public:
TilesheetData GetTileSheet(MapName map,int tileID);
//Gets the rectangle of the tile collision at this tile. If upperLevel is set to true, the collision tile must be in a Bridge class layer for the tile to hit. Also, zones containing LowerBridgeCollision will apply when upperLevel is set to false.
geom2d::rectGetTileCollision(MapName map,vf2d pos,bool upperLevel=false);
+ Pixel GetTileColor(MapName map,vf2d pos,bool upperLevel=false);
//Checks if the point resides inside of a collision tile.
bool HasTileCollision(MapName map,vf2d pos,bool upperLevel=false);
const MapName&GetCurrentLevel()const;
@@ -329,6 +334,7 @@ public:
const bool PreviousStageCompleted()const;
void SetCompletedStageFlag();
void ResetCompletedStageFlag();
+ Minimap minimap;
struct TileGroupData{
vi2d tilePos;
diff --git a/Adventures in Lestoria/Bear.cpp b/Adventures in Lestoria/Bear.cpp
index e27a54ad..46303ec4 100644
--- a/Adventures in Lestoria/Bear.cpp
+++ b/Adventures in Lestoria/Bear.cpp
@@ -93,10 +93,10 @@ void Monster::STRATEGY::BEAR(Monster&m,float fElapsedTime,std::string strategy){
game->GetPlayer()->Knockback(playerDirVecNorm*ConfigFloat("Attack Knockback Amount"));
}
}
- for(Monster&otherM:MONSTER_LIST){
- if(!otherM.AttackAvoided(m.GetZ())&&&m!=&otherM&&geom2d::overlaps(attackCircle,otherM.Hitbox())){
- otherM.Knockup(ConfigFloat("Attack Knockup Duration"));
- vf2d monsterDirVecNorm=geom2d::line(m.GetPos(),otherM.GetPos()).vector().norm();
+ for(std::unique_ptr&otherM:MONSTER_LIST){
+ if(!otherM->AttackAvoided(m.GetZ())&&&m!=otherM.get()&&geom2d::overlaps(attackCircle,otherM->Hitbox())){
+ otherM->Knockup(ConfigFloat("Attack Knockup Duration"));
+ vf2d monsterDirVecNorm=geom2d::line(m.GetPos(),otherM->GetPos()).vector().norm();
game->GetPlayer()->Knockback(monsterDirVecNorm*ConfigFloat("Attack Knockback Amount"));
}
}
diff --git a/Adventures in Lestoria/BulletTypes.h b/Adventures in Lestoria/BulletTypes.h
index d5c795b2..f748d374 100644
--- a/Adventures in Lestoria/BulletTypes.h
+++ b/Adventures in Lestoria/BulletTypes.h
@@ -85,7 +85,8 @@ struct FrogTongue:public Bullet{
float tongueLength;
float duration;
float knockbackStrength;
- FrogTongue(vf2d pos,vf2d targetPos,float lifetime,int damage,bool upperLevel,float knockbackStrength=1.0f,bool friendly=false,Pixel col=WHITE);
+ Monster&sourceMonster;
+ FrogTongue(Monster&sourceMonster,vf2d targetPos,float lifetime,int damage,bool upperLevel,float knockbackStrength=1.0f,bool friendly=false,Pixel col=WHITE);
void Update(float fElapsedTime)override;
bool PlayerHit(Player*player)override;
bool MonsterHit(Monster&monster)override;
diff --git a/Adventures in Lestoria/DEFINES.h b/Adventures in Lestoria/DEFINES.h
index 04df454b..92cf1f32 100644
--- a/Adventures in Lestoria/DEFINES.h
+++ b/Adventures in Lestoria/DEFINES.h
@@ -42,7 +42,7 @@ All rights reserved.
using BackdropName=std::string;
#define INCLUDE_ANIMATION_DATA extern safemapANIMATION_DATA;
-#define INCLUDE_MONSTER_LIST extern std::vectorMONSTER_LIST;
+#define INCLUDE_MONSTER_LIST extern std::vector>MONSTER_LIST;
#define INCLUDE_SPAWNER_LIST extern std::vectorSPAWNER_LIST;
#define INCLUDE_DAMAGENUMBER_LIST extern std::vector>DAMAGENUMBER_LIST;
#define INCLUDE_game extern AiL*game;
diff --git a/Adventures in Lestoria/Frog.cpp b/Adventures in Lestoria/Frog.cpp
index fa76fa65..c733a77a 100644
--- a/Adventures in Lestoria/Frog.cpp
+++ b/Adventures in Lestoria/Frog.cpp
@@ -70,7 +70,7 @@ void Monster::STRATEGY::FROG(Monster&m,float fElapsedTime,std::string strategy){
m.F(A::LOCKON_WAITTIME)=ConfigFloat("Attack Duration");
vf2d tongueMaxRangePos=geom2d::line(m.GetPos(),m.V(A::LOCKON_POS)).upoint(ConfigFloat("Tongue Max Range")/ConfigFloat("Range"));
SoundEffect::PlaySFX("Slime Shoot",m.pos);
- CreateBullet(FrogTongue)(m.pos,tongueMaxRangePos,ConfigFloat("Attack Duration"),m.GetAttack(),m.OnUpperLevel(),ConfigFloat("Tongue Knockback Strength"),false,ConfigPixel("Tongue Color"))EndBullet;
+ CreateBullet(FrogTongue)(m,tongueMaxRangePos,ConfigFloat("Attack Duration"),m.GetAttack(),m.OnUpperLevel(),ConfigFloat("Tongue Knockback Strength"),false,ConfigPixel("Tongue Color"))EndBullet;
m.PerformShootAnimation();
m.I(A::PHASE)=2;
}
diff --git a/Adventures in Lestoria/FrogTongue.cpp b/Adventures in Lestoria/FrogTongue.cpp
index 7a942c13..ecffac39 100644
--- a/Adventures in Lestoria/FrogTongue.cpp
+++ b/Adventures in Lestoria/FrogTongue.cpp
@@ -44,28 +44,32 @@ INCLUDE_game
INCLUDE_MONSTER_LIST
INCLUDE_GFX
-FrogTongue::FrogTongue(vf2d pos,vf2d targetPos,float lifetime,int damage,bool upperLevel,float knockbackStrength,bool friendly,Pixel col)
- :Bullet(pos,{},0,damage,upperLevel,friendly,col),targetPos(targetPos),tongueLength(0.f),knockbackStrength(knockbackStrength){
+FrogTongue::FrogTongue(Monster&sourceMonster,vf2d targetPos,float lifetime,int damage,bool upperLevel,float knockbackStrength,bool friendly,Pixel col)
+ :Bullet(sourceMonster.GetPos(),{},0,damage,upperLevel,friendly,col),targetPos(targetPos),tongueLength(0.f),knockbackStrength(knockbackStrength),sourceMonster(sourceMonster){
this->lifetime=lifetime;
duration=lifetime;
}
void FrogTongue::Update(float fElapsedTime){
- geom2d::linelineToTarget(pos,targetPos);
- vf2d drawVec=lineToTarget.vector().norm()*3;
+ pos=sourceMonster.GetPos();
+
+ if(sourceMonster.IsAlive()){
+ geom2d::linelineToTarget(pos,targetPos);
+ vf2d drawVec=lineToTarget.vector().norm()*3;
- tongueLength=util::lerp(0,lineToTarget.length(),pow(sin((lifetime*PI)/duration),20.f));
+ tongueLength=util::lerp(0,lineToTarget.length(),pow(sin((lifetime*PI)/duration),20.f));
- vf2d tongueEndPos=geom2d::line(pos+drawVec,targetPos).upoint(pow(sin((lifetime*PI)/duration),20.f));
- geom2d::linetongueLine(pos+drawVec,tongueEndPos);
+ vf2d tongueEndPos=geom2d::line(pos+drawVec,targetPos).upoint(pow(sin((lifetime*PI)/duration),20.f));
+ geom2d::linetongueLine(pos+drawVec,tongueEndPos);
- if(!friendly&&geom2d::overlaps(game->GetPlayer()->Hitbox(),tongueLine)){
- PlayerHit(game->GetPlayer());
- }
- if(friendly){
- for(Monster&m:MONSTER_LIST){
- if(hitList.find(&m)==hitList.end()&&geom2d::overlaps(m.Hitbox(),tongueLine)){
- MonsterHit(m);
- hitList.insert(&m);
+ if(!friendly&&geom2d::overlaps(game->GetPlayer()->Hitbox(),tongueLine)){
+ PlayerHit(game->GetPlayer());
+ }
+ if(friendly){
+ for(std::unique_ptr&m:MONSTER_LIST){
+ if(hitList.find(&*m)==hitList.end()&&geom2d::overlaps(m->Hitbox(),tongueLine)){
+ MonsterHit(*m);
+ hitList.insert(&*m);
+ }
}
}
}
diff --git a/Adventures in Lestoria/LightningBolt.cpp b/Adventures in Lestoria/LightningBolt.cpp
index 39e777f3..d838ca8b 100644
--- a/Adventures in Lestoria/LightningBolt.cpp
+++ b/Adventures in Lestoria/LightningBolt.cpp
@@ -94,14 +94,14 @@ bool LightningBolt::MonsterHit(Monster& monster)
fadeOutTime="Wizard.Ability 2.BulletFadeoutTime"_F;
game->AddEffect(std::make_unique(monster.GetPos(),"Wizard.Ability 2.SplashLifetime"_F,"lightning_splash_effect.png",upperLevel,monster.GetSizeMult(),"Wizard.Ability 2.SplashFadeoutTime"_F,vf2d{},WHITE,"Wizard.Ability 2.SplashRotationRange"_FRange));
int targetsHit=0;
- for(Monster&m:MONSTER_LIST){
- if(&m==&monster||monster.OnUpperLevel()!=m.OnUpperLevel())continue;
- geom2d::linelineToTarget=geom2d::line(monster.GetPos(),m.GetPos());
+ for(std::unique_ptr&m:MONSTER_LIST){
+ if(&*m==&monster||monster.OnUpperLevel()!=m->OnUpperLevel())continue;
+ geom2d::linelineToTarget=geom2d::line(monster.GetPos(),m->GetPos());
float dist=lineToTarget.length();
if(dist<="Wizard.Ability 2.LightningChainRadius"_F/100*24){
- if(m.Hurt(int(game->GetPlayer()->GetAttack()*"Wizard.Ability 2.LightningChainDamageMult"_F),OnUpperLevel(),0)){
- EMITTER_LIST.push_back(std::make_unique(LightningBoltEmitter(monster.GetPos(),m.GetPos(),"Wizard.Ability 2.LightningChainFrequency"_F,"Wizard.Ability 2.LightningChainLifetime"_F,upperLevel)));
- game->AddEffect(std::make_unique(m.GetPos(),"Wizard.Ability 2.LightningChainSplashLifetime"_F,"lightning_splash_effect.png",upperLevel,monster.GetSizeMult(),"Wizard.Ability 2.LightningChainSplashFadeoutTime"_F,vf2d{},WHITE,"Wizard.Ability 2.LightningChainSplashRotationRange"_FRange));
+ if(m->Hurt(int(game->GetPlayer()->GetAttack()*"Wizard.Ability 2.LightningChainDamageMult"_F),OnUpperLevel(),0)){
+ EMITTER_LIST.push_back(std::make_unique(LightningBoltEmitter(monster.GetPos(),m->GetPos(),"Wizard.Ability 2.LightningChainFrequency"_F,"Wizard.Ability 2.LightningChainLifetime"_F,upperLevel)));
+ game->AddEffect(std::make_unique(m->GetPos(),"Wizard.Ability 2.LightningChainSplashLifetime"_F,"lightning_splash_effect.png",upperLevel,monster.GetSizeMult(),"Wizard.Ability 2.LightningChainSplashFadeoutTime"_F,vf2d{},WHITE,"Wizard.Ability 2.LightningChainSplashRotationRange"_FRange));
targetsHit++;
}
}
diff --git a/Adventures in Lestoria/Map.h b/Adventures in Lestoria/Map.h
index 3f1535a0..339940a0 100644
--- a/Adventures in Lestoria/Map.h
+++ b/Adventures in Lestoria/Map.h
@@ -64,11 +64,13 @@ struct TilesetData{
std::mapstaircaseTiles;
std::map>animationData;
std::setreflectiveData;
+ std::vectortilecols;
};
struct TilesheetData{
TilesetData*tileset;
int firstgid;
+ Pixel tilecol;
bool operator==(const TilesheetData&rhs){
return tileset==rhs.tileset&&firstgid==rhs.firstgid;
}
diff --git a/Adventures in Lestoria/Minimap.cpp b/Adventures in Lestoria/Minimap.cpp
new file mode 100644
index 00000000..eb7fb147
--- /dev/null
+++ b/Adventures in Lestoria/Minimap.cpp
@@ -0,0 +1,177 @@
+#pragma region License
+/*
+License (OLC-3)
+~~~~~~~~~~~~~~~
+
+Copyright 2024 Joshua Sigona
+
+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 "AdventuresInLestoria.h"
+#include "DEFINES.h"
+#include "Unlock.h"
+#include "util.h"
+
+INCLUDE_game
+
+void Minimap::Initialize(){
+ std::vectorenlargedCircle;
+ for(int i=360;i>=0;i-=4){
+ float angle=util::degToRad(float(i))-PI/2;
+ if(i==360){enlargedCircle.push_back(vf2d{cos(angle),sin(angle)}*"Minimap.Minimap HUD Size"_I+"Minimap.Minimap HUD Size"_I);}
+ enlargedCircle.push_back(vf2d{cos(angle),sin(angle)}*"Minimap.Minimap HUD Size"_I+"Minimap.Minimap HUD Size"_I);
+ }
+ mapCircleHud=ViewPort{enlargedCircle};
+
+}
+
+void Minimap::Reset(){
+ loadedChunks.clear();
+
+ if(minimap.Sprite()==nullptr)minimap.Create(1,1);
+ if(cover.Sprite()==nullptr)cover.Create(1,1);
+
+ Sprite baseMinimap;
+
+ #pragma region Cleanup minimap and cover images
+ minimap.Sprite()->Resize(game->GetCurrentMapData().width,game->GetCurrentMapData().height);
+ cover.Sprite()->Resize(game->GetCurrentMapData().width,game->GetCurrentMapData().height);
+ baseMinimap.Resize(game->GetCurrentMapData().width,game->GetCurrentMapData().height);
+ game->SetDrawTarget(minimap.Sprite());
+ game->SetPixelMode(Pixel::ALPHA);
+ game->Clear(BLANK);
+ game->SetDrawTarget(&baseMinimap);
+ game->Clear(BLANK);
+ game->SetDrawTarget(cover.Sprite());
+ game->Clear(BLANK);
+ game->SetPixelMode(Pixel::NORMAL);
+ //Will update the minimap decal at the end, since we are about to change it anyways.
+ cover.Decal()->Update();
+ #pragma endregion
+
+
+ for(int x=0;xGetCurrentMapData().width;x++){
+ for(int y=0;yGetCurrentMapData().height;y++){
+ bool tileFound=false;
+ bool collision=false;
+ Pixel tileCol;
+ for(const LayerTag&layer:game->MAP_DATA[game->GetCurrentLevel()].GetLayers()){
+ if(Unlock::IsUnlocked(layer.unlockCondition)){
+ int tileID=layer.tiles[y][x]-1;
+ if(tileID!=-1){
+ tileFound=true;
+ if(game->GetTileCollision(game->GetCurrentMapName(),vf2d{float(x),float(y)}*game->GetCurrentMapData().tilewidth)!=game->NO_COLLISION){
+ baseMinimap.SetPixel({x,y},BLANK);
+ collision=true;
+ }
+ tileCol=game->GetTileColor(game->GetCurrentMapName(),vf2d{float(x),float(y)}*game->GetCurrentMapData().tilewidth);
+ }
+ }
+ }
+ if(tileFound&&!collision){
+ baseMinimap.SetPixel({x,y},{uint8_t(std::min(255.f,tileCol.r*1.5f)),uint8_t(std::min(255.f,tileCol.g*1.5f)),uint8_t(std::min(255.f,tileCol.b*1.5f))});
+ }else
+ if(!tileFound){
+ baseMinimap.SetPixel({x,y},BLANK);
+ }
+ }
+ }
+
+ for(int sy=0;sy0){
+ for(int y=-1;y<=1;y++){
+ for(int x=-1;x<=1;x++){
+ if(x==0&&y==0)continue;
+ minimap.Sprite()->SetPixel(sx+x,sy+y,BLACK);
+ }
+ }
+ }
+ }
+ }
+ for(int sy=0;sy0){
+ minimap.Sprite()->SetPixel(sx,sy,baseMinimap.GetPixel(sx,sy));
+ }
+ }
+ }
+ game->SetDrawTarget(nullptr);
+
+ minimap.Decal()->Update();
+}
+
+void Minimap::Update(){
+
+}
+
+void Minimap::UpdateChunk(const vi2d chunkPos){
+ if(!loadedChunks.count(std::format("{}_{}",chunkPos.x,chunkPos.y))){
+ loadedChunks.insert(std::format("{}_{}",chunkPos.x,chunkPos.y));
+
+ vi2d centerChunkPos=chunkPos*"Minimap.Chunk Size"_I;
+
+ vi2d pixelPos=centerChunkPos-"Minimap.Chunk Size"_I*2;
+ vi2d chunkEndPixelPos=centerChunkPos+"Minimap.Chunk Size"_I*4;
+
+ //We start twice the distance we are supposed to outwards.
+ for(int y=pixelPos.y;yGetPixel(x,y).a==255||minimap.Sprite()->GetPixel(x,y).a==0)continue; //Already revealed or invisible anyways.
+
+ vi2d chunk=vi2d{x,y}/"Minimap.Chunk Size"_I;
+ if(chunk==chunkPos){
+ cover.Sprite()->SetPixel(x,y,minimap.Sprite()->GetPixel(x,y));
+ }else{
+ const vi2d chunkOffset={"Minimap.Chunk Size"_I/2,"Minimap.Chunk Size"_I/2};
+
+ const float distance=geom2d::line(centerChunkPos+chunkOffset,vf2d{float(x),float(y)}).length();
+ const int alpha=std::clamp(util::lerp(255,0,(distance-"Minimap.Chunk Size"_I)/"Minimap.Chunk Size"_I),0.f,255.f);
+
+ if(cover.Sprite()->GetPixel(x,y).a>alpha)continue; //The distance was uncovered by another closer chunk, don't need to reveal it here.
+
+ Pixel sourceCol=minimap.Sprite()->GetPixel(x,y);
+ sourceCol.a=alpha;
+ cover.Sprite()->SetPixel(x,y,sourceCol);
+ }
+ }
+ }
+
+ cover.Decal()->Update();
+ }
+}
+
+void Minimap::Draw(){
+ mapCircleHud.DrawRotatedDecal(vf2d{"Minimap.Minimap HUD Size"_I/2.f,"Minimap.Minimap HUD Size"_I/2.f},cover.Decal(),0.f,game->GetPlayer()->GetPos()/24);
+ mapCircleHud.DrawStringDecal({0,0},"Hello World! Hello World! Hello World! Hello World! \nHello World! Hello World! Hello World! Hello World! \nHello World! Hello World! Hello World! ");
+ mapCircleHud.drawEdges();
+}
\ No newline at end of file
diff --git a/Adventures in Lestoria/Minimap.h b/Adventures in Lestoria/Minimap.h
new file mode 100644
index 00000000..e17e387b
--- /dev/null
+++ b/Adventures in Lestoria/Minimap.h
@@ -0,0 +1,55 @@
+#pragma region License
+/*
+License (OLC-3)
+~~~~~~~~~~~~~~~
+
+Copyright 2024 Joshua Sigona
+
+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 "olcPGEX_ViewPort.h"
+
+class Minimap{
+public:
+ void Initialize();
+ void Reset();
+ void Update();
+ void Draw();
+
+ void UpdateChunk(const vi2d chunkPos);
+private:
+ ViewPort mapCircleHud;
+ Renderable minimap;
+ Renderable cover;
+ std::unordered_setloadedChunks;
+};
\ No newline at end of file
diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp
index 396cd13c..78b67796 100644
--- a/Adventures in Lestoria/Monster.cpp
+++ b/Adventures in Lestoria/Monster.cpp
@@ -269,14 +269,14 @@ bool Monster::Update(float fElapsedTime){
}
}
if(!HasIframes()){
- for(Monster&m:MONSTER_LIST){
- if(&m==this)continue;
- if(!m.HasIframes()&&OnUpperLevel()==m.OnUpperLevel()&&abs(m.GetZ()-GetZ())<=1&&geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(m.GetPos(),12*m.GetSizeMult()/2))){
- m.Collision(*this);
- geom2d::line line(pos,m.GetPos());
+ for(std::unique_ptr&m:MONSTER_LIST){
+ if(&*m==this)continue;
+ if(!m->HasIframes()&&OnUpperLevel()==m->OnUpperLevel()&&abs(m->GetZ()-GetZ())<=1&&geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(m->GetPos(),12*m->GetSizeMult()/2))){
+ m->Collision(*this);
+ geom2d::line line(pos,m->GetPos());
float dist = line.length();
- m.SetPos(line.rpoint(dist*1.1f));
- if(m.IsAlive()){
+ m->SetPos(line.rpoint(dist*1.1f));
+ if(m->IsAlive()){
vel=line.vector().norm()*-128;
}
}
diff --git a/Adventures in Lestoria/Pathfinding.cpp b/Adventures in Lestoria/Pathfinding.cpp
index 62c5c7f3..19d9bf5f 100644
--- a/Adventures in Lestoria/Pathfinding.cpp
+++ b/Adventures in Lestoria/Pathfinding.cpp
@@ -224,7 +224,7 @@ Pathfinding::sPoint2D Pathfinding::sSpline::GetSplinePoint(float t, bool bLooped
}
else
{
- p1 = (int)t1;
+ p1 = std::clamp(size_t(t1),size_t(0),points.size()-1);
p2 = (p1 + 1) % points.size();
p3 = (p2 + 1) % points.size();
p0 = p1 >= 1 ? p1 - 1 : points.size() - 1;
diff --git a/Adventures in Lestoria/Player.cpp b/Adventures in Lestoria/Player.cpp
index fa18c91f..deb2d954 100644
--- a/Adventures in Lestoria/Player.cpp
+++ b/Adventures in Lestoria/Player.cpp
@@ -482,19 +482,19 @@ void Player::Update(float fElapsedTime){
if(item3.cooldown<0){
item3.cooldown=0;
}
- for(Monster&m:MONSTER_LIST){
- if(!HasIframes()&&abs(m.GetZ()-GetZ())<=1&&OnUpperLevel()==m.OnUpperLevel()&&geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(m.GetPos(),12*m.GetSizeMult()/2))){
- if(m.IsAlive()){
- m.Collision(this);
+ for(std::unique_ptr&m:MONSTER_LIST){
+ if(!HasIframes()&&abs(m->GetZ()-GetZ())<=1&&OnUpperLevel()==m->OnUpperLevel()&&geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(m->GetPos(),12*m->GetSizeMult()/2))){
+ if(m->IsAlive()){
+ m->Collision(this);
}
- geom2d::line line(pos,m.GetPos());
+ geom2d::line line(pos,m->GetPos());
float dist = line.length();
if(dist<=0.001){
- m.SetPos(m.GetPos()+vf2d{util::random(2)-1,util::random(2)-1});
+ m->SetPos(m->GetPos()+vf2d{util::random(2)-1,util::random(2)-1});
}else{
- m.SetPos(line.rpoint(dist*1.1f));
+ m->SetPos(line.rpoint(dist*1.1f));
}
- if(m.IsAlive()&&!m.IsNPC()){ //Don't set the knockback if this monster is actually an NPC. Let's just push them around.
+ if(m->IsAlive()&&!m->IsNPC()){ //Don't set the knockback if this monster is actually an NPC. Let's just push them around.
vel=line.vector().norm()*-128;
}
}
@@ -865,6 +865,8 @@ void Player::Moved(){
ERR(std::format("WARNING! Player Y position is {}...Trying to recover. THIS SHOULD NOT BE HAPPENING!",pos.y));
ForceSetPos({pos.x,float(game->GetCurrentMapData().playerSpawnLocation.y)});
}
+
+ game->minimap.UpdateChunk(GetPos()/24/"Minimap.Chunk Size"_I);
}
void Player::Spin(float duration,float spinSpd){
@@ -1445,13 +1447,13 @@ const vf2d Player::GetAimingLocation(bool useWalkDir,bool invert){
}else{
//Find the closest monster target.
vf2d closestPoint={std::numeric_limits::max(),std::numeric_limits::max()};
- for(Monster&m:MONSTER_LIST){
- if(m.IsAlive()){
- geom2d::lineaimingLine=geom2d::line(GetPos(),m.GetPos());
+ for(std::unique_ptr&m:MONSTER_LIST){
+ if(m->IsAlive()){
+ geom2d::lineaimingLine=geom2d::line(GetPos(),m->GetPos());
float distToMonster=aimingLine.length();
float distToClosestPoint=geom2d::line(GetPos(),closestPoint).length();
if(distToClosestPoint>distToMonster&&distToMonster<=operator""_Pixels("Player.Auto Aim Detection Distance"_F)){
- closestPoint=m.GetPos();
+ closestPoint=m->GetPos();
}
}
}
diff --git a/Adventures in Lestoria/TODO.txt b/Adventures in Lestoria/TODO.txt
index ec5c4c36..15d219c0 100644
--- a/Adventures in Lestoria/TODO.txt
+++ b/Adventures in Lestoria/TODO.txt
@@ -19,6 +19,11 @@ Steel Weapons appear in the demo for Chapter 2.
Update display counters on the overworld map.
Pressing movement keys that negate each other shouldn't cause a walking animation to occur.
+>As the player navigates around the map, the blank map canvas gets updated based on distance.
+>If a chunk has not been explored yet, it gets flagged as explored (probably unlock the chunks around the player as well).
+
+>When a map is visited later, all visited chunks are revealed again.
+
============================================
Consider a "killed by player" / "marked by player" flag for monsters to determine if a player gets credit for a monster kill (for achievements)
Make another actions config file for the main build (The app # is different)
diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h
index 5ceb76c1..d2006157 100644
--- a/Adventures in Lestoria/Version.h
+++ b/Adventures in Lestoria/Version.h
@@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 1
#define VERSION_PATCH 0
-#define VERSION_BUILD 8667
+#define VERSION_BUILD 8815
#define stringify(a) stringify_(a)
#define stringify_(a) #a
diff --git a/Adventures in Lestoria/Warrior.cpp b/Adventures in Lestoria/Warrior.cpp
index f6aff201..bc96d8b5 100644
--- a/Adventures in Lestoria/Warrior.cpp
+++ b/Adventures in Lestoria/Warrior.cpp
@@ -70,12 +70,12 @@ bool Warrior::AutoAttack(){
bool attack=false;
Monster*closest=nullptr;
float closest_dist=999999;
- for(Monster&m:MONSTER_LIST){
- if(m.IsAlive()
- &&geom2d::overlaps(geom2d::circle(GetPos(),attack_range*GetSizeMult()*12),geom2d::circle(m.GetPos(),m.GetSizeMult()*12))
- &&geom2d::line(GetWorldAimingLocation(),m.GetPos()).length()(GetWorldAimingLocation(),m.GetPos()).length();
- closest=&m;
+ for(std::unique_ptr&m:MONSTER_LIST){
+ if(m->IsAlive()&&
+ geom2d::overlaps(geom2d::circle(GetPos(),attack_range*GetSizeMult()*12),geom2d::circle(m->GetPos(),m->GetSizeMult()*12))&&
+ geom2d::line(GetWorldAimingLocation(),m->GetPos()).length()(GetWorldAimingLocation(),m->GetPos()).length();
+ closest=&*m;
}
}
@@ -121,9 +121,9 @@ void Warrior::InitializeClassAbilities(){
game->AddEffect(std::make_unique(p->GetPos(),"Warrior.Ability 1.EffectLifetime"_F,"battlecry_effect.png",p->upperLevel,"Warrior.Ability 1.Range"_F/350,"Warrior.Ability 1.EffectFadetime"_F));
p->AddBuff(BuffType::STAT_UP,"Warrior.Ability 1.AttackUpDuration"_F,"Warrior.Ability 1.AttackIncrease"_F,{"Attack %"});
p->AddBuff(BuffType::DAMAGE_REDUCTION,"Warrior.Ability 1.DamageReductionDuration"_F,"Warrior.Ability 1.DamageReduction"_F);
- for(Monster&m:MONSTER_LIST){
- if(m.GetSizeMult()>="Warrior.Ability 1.AffectedSizeRange"_f[0]&&m.GetSizeMult()<="Warrior.Ability 1.AffectedSizeRange"_f[1]&&geom2d::overlaps(geom2d::circle(p->GetPos(),12*"Warrior.Ability 1.Range"_I/100.f),geom2d::circle(m.GetPos(),m.GetSizeMult()*12))){
- m.AddBuff(BuffType::SLOWDOWN,"Warrior.Ability 1.SlowdownDuration"_F,"Warrior.Ability 1.SlowdownAmt"_F);
+ for(std::unique_ptr&m:MONSTER_LIST){
+ if(m->GetSizeMult()>="Warrior.Ability 1.AffectedSizeRange"_f[0]&&m->GetSizeMult()<="Warrior.Ability 1.AffectedSizeRange"_f[1]&&geom2d::overlaps(geom2d::circle(p->GetPos(),12*"Warrior.Ability 1.Range"_I/100.f),geom2d::circle(m->GetPos(),m->GetSizeMult()*12))){
+ m->AddBuff(BuffType::SLOWDOWN,"Warrior.Ability 1.SlowdownDuration"_F,"Warrior.Ability 1.SlowdownAmt"_F);
}
}
SoundEffect::PlaySFX("Warrior Battlecry",SoundEffect::CENTERED);
diff --git a/Adventures in Lestoria/assets/Campaigns/1_2.tmx b/Adventures in Lestoria/assets/Campaigns/1_2.tmx
index 4ce4dec1..5687bf86 100644
--- a/Adventures in Lestoria/assets/Campaigns/1_2.tmx
+++ b/Adventures in Lestoria/assets/Campaigns/1_2.tmx
@@ -1,5 +1,5 @@
-