Color mode sampling on tiles to determine minimap colors. Release Build 8703.

mac-build
sigonasr2 8 months ago
parent 914b55e4c1
commit a81169cd7f
  1. 78
      Adventures in Lestoria/AdventuresInLestoria.cpp
  2. 3
      Adventures in Lestoria/AdventuresInLestoria.h
  3. 2
      Adventures in Lestoria/Map.h
  4. 19
      Adventures in Lestoria/Minimap.cpp
  5. 3
      Adventures in Lestoria/TODO.txt
  6. 2
      Adventures in Lestoria/Version.h
  7. 2
      Adventures in Lestoria/assets/Campaigns/1_2.tmx
  8. 2
      Adventures in Lestoria/assets/Campaigns/1_3.tmx
  9. 2
      Adventures in Lestoria/assets/Campaigns/Boss_1_B.tmx
  10. 3
      Adventures in Lestoria/assets/Campaigns/World_Map.tmx
  11. BIN
      x64/Release/Adventures in Lestoria.exe

@ -1993,6 +1993,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;
@ -2053,6 +2055,8 @@ void AiL::InitializeLevel(std::string mapFile,MapName map){
SetDrawTarget(nullptr);
r->Decal()->Update();
}
ComputeModeColors(MAP_TILESETS["assets/maps/"+baseSourceDir]);
}
}
@ -2509,18 +2513,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;i<tileData.size();i++){
int firstgid=stoi(tileData[i-1].data["firstgid"]);
if(tileID%1000000<stoi(tileData[i].data["firstgid"])-1){
size_t slashMarkerSourceDir = tileData[size_t(i-1)].data["source"].find_last_of('/');
std::string baseSourceDir=tileData[size_t(i-1)].data["source"].substr(slashMarkerSourceDir+1);
return {&MAP_TILESETS["assets/maps/"+baseSourceDir],stoi(tileData[i-1].data["firstgid"])};
if(tileID!=-1){
return {&MAP_TILESETS["assets/maps/"+baseSourceDir],firstgid,MAP_TILESETS["assets/maps/"+baseSourceDir].tilecols[tileID-(firstgid-1)]};
}else{
return {&MAP_TILESETS["assets/maps/"+baseSourceDir],firstgid,BLANK};
}
}
}
size_t slashMarkerSourceDir = tileData[tileData.size()-1].data["source"].find_last_of('/');
std::string baseSourceDir=tileData[tileData.size()-1].data["source"].substr(slashMarkerSourceDir+1);
return {&MAP_TILESETS["assets/maps/"+baseSourceDir],stoi(tileData[tileData.size()-1].data["firstgid"])};
int firstgid=stoi(tileData[tileData.size()-1].data["firstgid"]);
return {&MAP_TILESETS["assets/maps/"+baseSourceDir],firstgid,MAP_TILESETS["assets/maps/"+baseSourceDir].tilecols[tileID-firstgid]};
}
}
@ -2540,35 +2550,36 @@ bool AiL::IsOverlayLayer(LayerTag&layer){
}
geom2d::rect<float>AiL::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<float>({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<float>({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;
@ -2581,7 +2592,7 @@ geom2d::rect<float>AiL::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::rect<float>collisionRect=GetTileSheet(map,tileID%1000000).tileset->collision[tileID%1000000-GetTileSheet(map,tileID%1000000).firstgid+1].collision;
if(foundRect==NO_COLLISION){
@ -2601,6 +2612,25 @@ geom2d::rect<float>AiL::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;
@ -3995,3 +4025,27 @@ void AiL::SetCompletedStageFlag(){
void AiL::ResetCompletedStageFlag(){
prevStageCompleted=false;
}
void AiL::ComputeModeColors(TilesetData&tileset){
for(int y=0;y<tileset.tileset->Sprite()->height/tileset.tileheight;y++){
for(int x=0;x<tileset.tileset->Sprite()->width/tileset.tilewidth;x++){
#pragma region Individual tile iteration
std::unordered_map<uint32_t,int>pixelCounts;
Pixel modeCol=BLANK;
int maxCount=0;
vi2d pixelOffset=vi2d{x,y}*tileset.tilewidth;
for(int pixelY=0;pixelY<tileset.tileheight;pixelY++){
for(int pixelX=0;pixelX<tileset.tilewidth;pixelX++){
vi2d targetPixel=vi2d{pixelX,pixelY}+pixelOffset;
Pixel tileCol=tileset.tileset->Sprite()->GetPixel(targetPixel);
pixelCounts[tileCol.n]++;
if(pixelCounts[tileCol.n]>maxCount){
modeCol=tileCol;
pixelCounts[tileCol.n]=maxCount;
}
}
}
tileset.tilecols.push_back(modeCol);
#pragma endregion
}
}
}

@ -190,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
@ -252,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::rect<float>GetTileCollision(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;

@ -64,11 +64,13 @@ struct TilesetData{
std::map<int,XMLTag>staircaseTiles;
std::map<int,std::vector<int>>animationData;
std::set<int>reflectiveData;
std::vector<Pixel>tilecols;
};
struct TilesheetData{
TilesetData*tileset;
int firstgid;
Pixel tilecol;
bool operator==(const TilesheetData&rhs){
return tileset==rhs.tileset&&firstgid==rhs.firstgid;
}

@ -60,18 +60,27 @@ void Minimap::Initialize(){
for(int x=0;x<game->GetCurrentMapData().width;x++){
for(int y=0;y<game->GetCurrentMapData().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){
minimap.Sprite()->SetPixel({x,y},BLACK);
game->SetPixelMode(Pixel::ALPHA);
minimap.Sprite()->SetPixel({x,y},BLANK);
game->SetPixelMode(Pixel::NORMAL);
collision=true;
}
break;
tileCol=game->GetTileColor(game->GetCurrentMapName(),vf2d{float(x),float(y)}*game->GetCurrentMapData().tilewidth);
}
}
}
if(tileFound&&!collision){
if(tileCol==BLANK)ERR("WARNING! A tile should not be blank as we should have handled all conditions that would've caused that in a different color! THIS SHOULD NOT BE HAPPENING!")
minimap.Sprite()->SetPixel({x,y},tileCol);
}else
if(!tileFound){
game->SetPixelMode(Pixel::ALPHA);
minimap.Sprite()->SetPixel({x,y},BLANK);
@ -88,5 +97,11 @@ void Minimap::Update(){
}
void Minimap::Draw(){
for(int y=-1;y<=1;y++){
for(int x=-1;x<=1;x++){
if(x==0&&y==0)continue;
else game->DrawDecal(vf2d{float(game->ScreenWidth()-minimap.Sprite()->width),0}+vf2d{float(x),float(y)},minimap.Decal(),{1.f,1.f},BLACK);
}
}
game->DrawDecal(vf2d{float(game->ScreenWidth()-minimap.Sprite()->width),0},minimap.Decal());
}

@ -19,9 +19,6 @@ 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.
>Start with a completely white image.
>Iterate through all tiles, no tile means set to transparent. Collision tile means set to black.
>Generate a blank map cover image.
>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).

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

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" class="Map" orientation="orthogonal" renderorder="right-down" width="182" height="150" tilewidth="24" tileheight="24" infinite="0" nextlayerid="7" nextobjectid="109">
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="182" height="150" tilewidth="24" tileheight="24" infinite="0" nextlayerid="7" nextobjectid="109">
<properties>
<property name="Backdrop" propertytype="Backdrop" value="forest"/>
<property name="Background Music" propertytype="BGM" value="foresty1_1"/>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" class="Map" orientation="orthogonal" renderorder="right-down" width="240" height="120" tilewidth="24" tileheight="24" infinite="0" nextlayerid="7" nextobjectid="85">
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="240" height="120" tilewidth="24" tileheight="24" infinite="0" nextlayerid="7" nextobjectid="85">
<properties>
<property name="Backdrop" propertytype="Backdrop" value="forest"/>
<property name="Background Music" propertytype="BGM" value="foresty1_1"/>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" class="Map" orientation="orthogonal" renderorder="right-down" width="72" height="80" tilewidth="24" tileheight="24" infinite="0" nextlayerid="5" nextobjectid="7">
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="72" height="80" tilewidth="24" tileheight="24" infinite="0" nextlayerid="5" nextobjectid="7">
<properties>
<property name="Backdrop" propertytype="Backdrop" value="forest"/>
<property name="Background Music" propertytype="BGM" value="foresty_boss"/>

@ -9,8 +9,7 @@
<tileset firstgid="8138" source="../maps/Minifantasy_TinyOverworldConstructions.tsx"/>
<tileset firstgid="9286" source="../maps/Minifantasy_TinyOverworldAllProps.tsx"/>
<tileset firstgid="10806" source="../maps/Stage_Plate.tsx"/>
<tileset firstgid="11066" source=":/automap-tiles.tsx"/>
<tileset firstgid="11071" source="../maps/Tilesheet_No_Shadow24x24.tsx"/>
<tileset firstgid="11066" source="../maps/Tilesheet_No_Shadow24x24.tsx"/>
<layer id="1" name="Layer 1" width="250" height="177">
<data encoding="csv">
713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,713,2147,82,82,82,239,256,256,256,256,256,256,

Loading…
Cancel
Save