init
This commit is contained in:
commit
87bff164f1
416 files changed
+279512
No files matched your search
+940
@@ -0,0 +1,940 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../noita_random.cpp"
|
||||
#include "stb_hbwang.h"
|
||||
#include "coalmine_hax.cpp"
|
||||
#include "jps.hh"
|
||||
#include "pix.cpp"
|
||||
#include "search.cpp"
|
||||
#include "png.cpp"
|
||||
#include "MurmurHash3.cpp"
|
||||
|
||||
// TODO: Look into optimizing with <https://github.com/google/highway>
|
||||
|
||||
const bool DEBUG = false;
|
||||
|
||||
// MODIFIED (noita_gpu): see MapGen::hasPath. Default is the engine's value.
|
||||
static int g_path_check_max_height = 205;
|
||||
|
||||
NollaPrng GetRNG(int width)
|
||||
{
|
||||
NollaPrng rng;
|
||||
rng.SetRandomFromWorldSeed(world_seed);
|
||||
rng.Next();
|
||||
|
||||
int iters = width + world_seed + 11 * (width / -11) - 12 * (world_seed / 12);
|
||||
|
||||
if (iters > 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
rng.Next();
|
||||
iters -= 1;
|
||||
} while (iters != 0);
|
||||
}
|
||||
return rng;
|
||||
}
|
||||
|
||||
class ToFrom
|
||||
{
|
||||
public:
|
||||
unsigned int from;
|
||||
unsigned int *to;
|
||||
int to_count;
|
||||
NollaPrng rng;
|
||||
};
|
||||
|
||||
// This needs to be sorted for binary search
|
||||
static const unsigned long blockedColors[] = {
|
||||
0x00ac6e, // load_pixel_scene4_alt
|
||||
0x70d79e, // load_gunpowderpool_01
|
||||
0x70d79f, //???
|
||||
0x70d7a1, // load_gunpowderpool_04
|
||||
0x7868ff, // load_gunpowderpool_02
|
||||
0xc35700, // load_oiltank
|
||||
0xff0080, // load_pixel_scene2
|
||||
0xff00ff, //???
|
||||
0xff0aff, // load_pixel_scene
|
||||
};
|
||||
|
||||
bool isBlockedColor(unsigned long color)
|
||||
{
|
||||
for (uint i = 0; i < sizeof(blockedColors) / sizeof(blockedColors[0]); i++)
|
||||
{
|
||||
if (blockedColors[i] == color)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const int BIOME_PATH_FIND_WORLD_POS_MIN_X = 159;
|
||||
const int BIOME_PATH_FIND_WORLD_POS_MAX_X = 223;
|
||||
|
||||
const int WORLD_OFFSET_X = 35;
|
||||
const int WORLD_OFFSET_Y = 14;
|
||||
|
||||
int GetWidthFromPix(const int a, const int b, const int offset)
|
||||
{
|
||||
return (((b - offset) * 512) / 10 - ((a - offset) * 512) / 10);
|
||||
}
|
||||
int GetWidthFromPix(const int a, const int b)
|
||||
{
|
||||
return GetWidthFromPix(a, b, 0);
|
||||
}
|
||||
|
||||
static int floorDiv512(int value)
|
||||
{
|
||||
if (value >= 0)
|
||||
{
|
||||
return value / 512;
|
||||
}
|
||||
return -(((-value) + 511) / 512);
|
||||
}
|
||||
|
||||
int GetTilePosXValue(const int gx, const int gy)
|
||||
{
|
||||
return floorDiv512(gx) + WORLD_OFFSET_X;
|
||||
}
|
||||
|
||||
int GetTilePosYValue(const int gx, const int gy)
|
||||
{
|
||||
return floorDiv512(gy) + WORLD_OFFSET_Y;
|
||||
}
|
||||
|
||||
int GetGlobalPosXValue(const int x, const int y)
|
||||
{
|
||||
return 512 * (x - WORLD_OFFSET_X);
|
||||
}
|
||||
|
||||
int GetGlobalPosYValue(const int x, const int y)
|
||||
{
|
||||
return 512 * (y - WORLD_OFFSET_Y);
|
||||
}
|
||||
|
||||
double GetBiomeOffsetX()
|
||||
{
|
||||
NollaPrng rng = NewNollaPrng(world_seed);
|
||||
double someRand = rng.Next() * 9999.0 + 1.0;
|
||||
double someRand2 = rng.Next() * 200000.0 - 100000.0;
|
||||
double offset = rng.Next() * 200000.0 - 100000.0;
|
||||
return offset;
|
||||
}
|
||||
|
||||
void rgbaToRgb(const unsigned char *src, unsigned char *dest, uint w, uint h)
|
||||
{
|
||||
unsigned long j = 0;
|
||||
unsigned long src_size = w * h;
|
||||
for (unsigned long i = 0; i < src_size; i++)
|
||||
{
|
||||
dest[j++] = src[i * 4];
|
||||
dest[j++] = src[i * 4 + 1];
|
||||
dest[j++] = src[i * 4 + 2];
|
||||
}
|
||||
}
|
||||
|
||||
void rgbToRgba(const unsigned char *src, unsigned char *dest, uint w, uint h)
|
||||
{
|
||||
unsigned long j = 0;
|
||||
unsigned long src_size = w * h;
|
||||
for (unsigned long i = 0; i < src_size; i++)
|
||||
{
|
||||
dest[j++] = src[i * 3];
|
||||
dest[j++] = src[i * 3 + 1];
|
||||
dest[j++] = src[i * 3 + 2];
|
||||
dest[j++] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
void whiteout(unsigned char *src, uint w, uint h)
|
||||
{
|
||||
unsigned long src_size = w * h;
|
||||
for (unsigned long i = 0; i < src_size; i++)
|
||||
{
|
||||
src[i * 4] = 255;
|
||||
src[i * 4 + 1] = 255;
|
||||
src[i * 4 + 2] = 255;
|
||||
src[i * 4 + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
enum mapToUse
|
||||
{
|
||||
out,
|
||||
path
|
||||
};
|
||||
|
||||
struct Block
|
||||
{
|
||||
int startX;
|
||||
int startY;
|
||||
int endX;
|
||||
int endY;
|
||||
};
|
||||
|
||||
struct BlockList
|
||||
{
|
||||
Block *items;
|
||||
uint len;
|
||||
uint cap;
|
||||
};
|
||||
|
||||
void blockListClear(BlockList *list)
|
||||
{
|
||||
list->len = 0;
|
||||
}
|
||||
|
||||
void blockListFree(BlockList *list)
|
||||
{
|
||||
free(list->items);
|
||||
list->items = NULL;
|
||||
list->len = 0;
|
||||
list->cap = 0;
|
||||
}
|
||||
|
||||
void blockListPush(BlockList *list, Block block)
|
||||
{
|
||||
if (list->len == list->cap)
|
||||
{
|
||||
list->cap = list->cap == 0 ? 32 : list->cap * 2;
|
||||
list->items = (Block *)realloc(list->items, list->cap * sizeof(Block));
|
||||
}
|
||||
list->items[list->len++] = block;
|
||||
}
|
||||
|
||||
class MapGen
|
||||
{
|
||||
public:
|
||||
unsigned char *out_map;
|
||||
unsigned char *path_map;
|
||||
uint width;
|
||||
uint height;
|
||||
bool isCoalMine;
|
||||
bool shouldBlockOutRooms;
|
||||
unsigned int *randomMaterials;
|
||||
int worldX;
|
||||
int worldY;
|
||||
|
||||
mapToUse usemap = out;
|
||||
|
||||
unsigned long endNode;
|
||||
unsigned long *parents;
|
||||
|
||||
BlockList blockedOutRooms = {};
|
||||
|
||||
MapGen(
|
||||
unsigned char *_map,
|
||||
uint _width,
|
||||
uint _height,
|
||||
bool _isCoalMine,
|
||||
bool _shouldBlockOutRooms,
|
||||
unsigned int *_randomMaterials,
|
||||
int _worldX,
|
||||
int _worldY) : width(_width),
|
||||
height(_height),
|
||||
out_map(_map),
|
||||
isCoalMine(_isCoalMine),
|
||||
shouldBlockOutRooms(_shouldBlockOutRooms),
|
||||
randomMaterials(_randomMaterials),
|
||||
worldX(_worldX),
|
||||
worldY(_worldY)
|
||||
{
|
||||
path_map = (unsigned char *)malloc(3 * width * height);
|
||||
memcpy(path_map, out_map, 3 * width * height);
|
||||
parents = (unsigned long *)malloc(width * height * sizeof(unsigned long));
|
||||
}
|
||||
|
||||
~MapGen()
|
||||
{
|
||||
blockListFree(&blockedOutRooms);
|
||||
free(parents);
|
||||
free(path_map);
|
||||
}
|
||||
|
||||
unsigned char *GetMap()
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
return out_map;
|
||||
}
|
||||
switch (usemap)
|
||||
{
|
||||
case out:
|
||||
return out_map;
|
||||
break;
|
||||
case path:
|
||||
return path_map;
|
||||
break;
|
||||
|
||||
default:
|
||||
return out_map;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
blockListClear(&blockedOutRooms);
|
||||
}
|
||||
|
||||
void resetParents()
|
||||
{
|
||||
unsigned long count = width * height;
|
||||
for (unsigned long i = 0; i < count; i++)
|
||||
{
|
||||
parents[i] = NO_PARENT;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasPath()
|
||||
{
|
||||
usemap = out;
|
||||
if (shouldBlockOutRooms)
|
||||
{
|
||||
blockOutRooms(COLOR_BLACK, COLOR_WHITE);
|
||||
}
|
||||
|
||||
if (isCoalMine)
|
||||
{
|
||||
doCoalMineHax();
|
||||
}
|
||||
|
||||
memcpy(path_map, out_map, 3 * width * height);
|
||||
|
||||
usemap = path;
|
||||
bool valid = isValid();
|
||||
// MODIFIED (noita_gpu): the engine only requires the path check to pass for maps whose height
|
||||
// is <= 205 wang pixels (noita.exe FUN_00879bf0: `local_90 = height <= DAT_01152478`, value 205);
|
||||
// taller maps accept the first generated image.
|
||||
if ((int)height > g_path_check_max_height)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
void finalize()
|
||||
{
|
||||
usemap = out;
|
||||
// Generated, now to fix up the map
|
||||
bool mainPath = isMainPath();
|
||||
if (!isCoalMine && mainPath)
|
||||
{
|
||||
fillMainPath();
|
||||
}
|
||||
if (isCoalMine)
|
||||
{
|
||||
undoCoalMineHax();
|
||||
}
|
||||
if (shouldBlockOutRooms)
|
||||
{
|
||||
fillBlockedRooms(COLOR_BLACK);
|
||||
}
|
||||
ClearPath();
|
||||
fillC0ffee();
|
||||
fillRandomMaterials();
|
||||
}
|
||||
|
||||
void undoCoalMineHax()
|
||||
{
|
||||
unsigned char *map = GetMap();
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
long overlayPos = getPos(256, 3, x, y);
|
||||
long i = getPos(width, 3, x, y);
|
||||
long pix = createRGB(coalmine_overlay[overlayPos], coalmine_overlay[overlayPos + 1], coalmine_overlay[overlayPos + 2]);
|
||||
if (pix == 0x4000)
|
||||
{
|
||||
// pudy248 note: is not actually air, this is the main rock portion of the overlay
|
||||
map[i] = 0x00;
|
||||
map[i + 1] = 0x00;
|
||||
map[i + 2] = 0x00;
|
||||
}
|
||||
if (pix == 0x0040)
|
||||
{ // blue. Looks like air?
|
||||
map[i] = 0x00;
|
||||
map[i + 1] = 0x00;
|
||||
map[i + 2] = 0x00;
|
||||
}
|
||||
if (pix == 0xFEFEFE)
|
||||
{ // white. Stairs. rock_static_intro
|
||||
// In the debug it's not shown, but used in path finding.
|
||||
map[i] = 0x00;
|
||||
map[i + 1] = 0x00;
|
||||
map[i + 2] = 0x00;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void doCoalMineHax()
|
||||
{
|
||||
unsigned char *map = GetMap();
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
long overlayPos = getPos(256, 3, x, y);
|
||||
long i = getPos(width, 3, x, y);
|
||||
long pix = createRGB(coalmine_overlay[overlayPos], coalmine_overlay[overlayPos + 1], coalmine_overlay[overlayPos + 2]);
|
||||
if (pix == 0x4000)
|
||||
{
|
||||
// pudy248 note: is not actually air, this is the main rock portion of the overlay
|
||||
map[i] = 0xFF;
|
||||
map[i + 1] = 0xFF;
|
||||
map[i + 2] = 0xFF;
|
||||
}
|
||||
if (pix == 0x0040)
|
||||
{ // blue. Looks like air?
|
||||
map[i] = 0x00;
|
||||
map[i + 1] = 0x00;
|
||||
map[i + 2] = 0x00;
|
||||
}
|
||||
if (pix == 0xFEFEFE)
|
||||
{ // white. Stairs. rock_static_intro
|
||||
// In the debug it's not shown, but used in path finding.
|
||||
map[i] = 0x0a;
|
||||
map[i + 1] = 0x33;
|
||||
map[i + 2] = 0x44;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClearPath()
|
||||
{
|
||||
unsigned char *map = GetMap();
|
||||
unsigned long current = endNode;
|
||||
unsigned long max = width * height;
|
||||
while (current != NO_PARENT && current < max)
|
||||
{
|
||||
int x = current % width;
|
||||
int y = current / width;
|
||||
long c = getPixelColor(map, width, x, y);
|
||||
if (c == COLOR_COFFEE)
|
||||
{
|
||||
floodFill(map, width, height, x, y, COLOR_COFFEE, COLOR_BLACK);
|
||||
}
|
||||
current = parents[current];
|
||||
}
|
||||
}
|
||||
|
||||
bool HasPathToBottom(
|
||||
uint path_start_x,
|
||||
bool fixed_x)
|
||||
{
|
||||
unsigned char *map = GetMap();
|
||||
resetParents();
|
||||
Search s = Search(map, width, height, 0, height - 1, parents);
|
||||
|
||||
if (fixed_x)
|
||||
{
|
||||
bool hasPath = s.findPath(path_start_x, 0);
|
||||
if (hasPath)
|
||||
{
|
||||
endNode = s.endPos;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int x = path_start_x;
|
||||
|
||||
while (x < width)
|
||||
{
|
||||
long c = getPixelColor(map, width, x, 0);
|
||||
if (c != COLOR_BLACK && c != COLOR_COFFEE)
|
||||
{
|
||||
x++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == COLOR_COFFEE)
|
||||
{
|
||||
floodFill(map, width, height, x, 0, COLOR_COFFEE, COLOR_BLACK);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool hasPath = s.findPath(x, 0);
|
||||
if (hasPath)
|
||||
{
|
||||
endNode = s.endPos;
|
||||
return true;
|
||||
}
|
||||
x++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isMainPath()
|
||||
{
|
||||
int fill_x_from = (BIOME_PATH_FIND_WORLD_POS_MIN_X - worldX * 512.0) / 10;
|
||||
int fill_x_to = fill_x_from + (BIOME_PATH_FIND_WORLD_POS_MAX_X - BIOME_PATH_FIND_WORLD_POS_MIN_X) / 10;
|
||||
return worldY < 29 && fill_x_to > 0 && fill_x_from > 0 && width > fill_x_from && fill_x_to < width + fill_x_from;
|
||||
}
|
||||
|
||||
int fillMainPath()
|
||||
{
|
||||
unsigned char *map = GetMap();
|
||||
int fill_x_from = (BIOME_PATH_FIND_WORLD_POS_MIN_X - worldX * 512.0) / 10;
|
||||
int fill_x_to = fill_x_from + (BIOME_PATH_FIND_WORLD_POS_MAX_X - BIOME_PATH_FIND_WORLD_POS_MIN_X) / 10;
|
||||
fill(map, width, fill_x_from, fill_x_to, 0, 6, COLOR_BLACK);
|
||||
return fill_x_from;
|
||||
}
|
||||
|
||||
void clearStartingArea()
|
||||
{
|
||||
unsigned char *map = GetMap();
|
||||
uint x = 0;
|
||||
|
||||
for (uint x = 0; x < width; x++)
|
||||
{
|
||||
long c = getPixelColor(map, width, x, 0);
|
||||
if (c == COLOR_COFFEE)
|
||||
{
|
||||
floodFill(map, width, height, x, 0, COLOR_COFFEE, COLOR_BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isValid()
|
||||
{
|
||||
uint path_start_x = 0;
|
||||
bool mainPath = isMainPath();
|
||||
if (isCoalMine)
|
||||
{
|
||||
path_start_x = 0x8e;
|
||||
}
|
||||
else if (mainPath)
|
||||
{
|
||||
path_start_x = fillMainPath();
|
||||
}
|
||||
|
||||
bool hasPath = HasPathToBottom(path_start_x, mainPath);
|
||||
|
||||
return hasPath;
|
||||
}
|
||||
|
||||
void blockOutRooms(unsigned long passableColor, unsigned long targetColor)
|
||||
{
|
||||
unsigned char *map = GetMap();
|
||||
unsigned long posMax = width * height;
|
||||
|
||||
for (unsigned long pos = 0; pos < posMax; pos++)
|
||||
{
|
||||
long color = getPixelColor(map, pos * 3);
|
||||
// ~70% of pixels are black or white, so skip them
|
||||
if (color == COLOR_BLACK)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (color == COLOR_WHITE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!isBlockedColor(color))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
uint x = pos % width;
|
||||
uint y = pos / width;
|
||||
|
||||
int startX = x + 1;
|
||||
int endX = x + 1;
|
||||
int startY = y + 1;
|
||||
int endY = y + 1;
|
||||
bool foundEnd = false;
|
||||
while (!foundEnd && endX < width)
|
||||
{
|
||||
long c = getPixelColor(map, width, endX, startY);
|
||||
if (c == passableColor || ShadeOfGrey(c, 0x32))
|
||||
{
|
||||
endX += 1;
|
||||
continue;
|
||||
};
|
||||
endX -= 1;
|
||||
foundEnd = true;
|
||||
}
|
||||
if (endX >= width)
|
||||
{
|
||||
endX = width - 1;
|
||||
}
|
||||
foundEnd = false;
|
||||
while (!foundEnd && endY < height)
|
||||
{
|
||||
long c = getPixelColor(map, width, startX, endY);
|
||||
if (c == passableColor || ShadeOfGrey(c, 0x32))
|
||||
{
|
||||
endY += 1;
|
||||
continue;
|
||||
};
|
||||
endY -= 1;
|
||||
foundEnd = true;
|
||||
}
|
||||
if (endY >= height)
|
||||
{
|
||||
endY = height - 1;
|
||||
}
|
||||
blockListPush(&blockedOutRooms, Block{startX, startY, endX, endY});
|
||||
fill(map, width, startX, endX, startY, endY, targetColor);
|
||||
}
|
||||
}
|
||||
|
||||
void fillBlockedRooms(unsigned long color)
|
||||
{
|
||||
unsigned char *map = GetMap();
|
||||
for (uint i = 0; i < blockedOutRooms.len; i++)
|
||||
{
|
||||
Block block = blockedOutRooms.items[i];
|
||||
fill(map, width, block.startX, block.endX, block.startY, block.endY, color);
|
||||
}
|
||||
}
|
||||
|
||||
void fillC0ffee()
|
||||
{
|
||||
unsigned char *map = GetMap();
|
||||
NollaPrng rng = NewNollaPrng(0);
|
||||
rng.SetRandomFromWorldSeed(world_seed);
|
||||
rng.Next();
|
||||
|
||||
unsigned long posMax = width * height;
|
||||
|
||||
for (unsigned long pos = 0; pos < posMax; pos++)
|
||||
{
|
||||
long c = getPixelColor(map, pos * 3);
|
||||
if (c != COLOR_COFFEE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
long to = COLOR_BLACK;
|
||||
if (0.5f > (float)rng.Next()) // BIOME_RANDOM_BLOCK_CHANCE
|
||||
{
|
||||
to = COLOR_WHITE;
|
||||
}
|
||||
|
||||
int x = pos % width;
|
||||
int y = pos / width;
|
||||
floodFill(map, width, height, x, y, COLOR_COFFEE, to);
|
||||
}
|
||||
}
|
||||
|
||||
void fillRandomMaterials()
|
||||
{
|
||||
unsigned char *map = GetMap();
|
||||
int numberOfRandoms = randomMaterials[0];
|
||||
if (numberOfRandoms == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ToFrom *replacements = (ToFrom *)malloc(numberOfRandoms * sizeof(ToFrom));
|
||||
int pos = 1;
|
||||
|
||||
for (int i = 0; i < numberOfRandoms; i++)
|
||||
{
|
||||
replacements[i].from = randomMaterials[pos];
|
||||
replacements[i].to_count = randomMaterials[pos + 1];
|
||||
replacements[i].to = &randomMaterials[pos + 2];
|
||||
replacements[i].rng = GetRNG(width);
|
||||
replacements[i].rng.Next();
|
||||
pos += 2 + replacements[i].to_count;
|
||||
}
|
||||
|
||||
for (int i = 0; i < numberOfRandoms; i++)
|
||||
{
|
||||
ToFrom *rr = &replacements[i];
|
||||
unsigned long posMax = width * height;
|
||||
for (unsigned long pos = 0; pos < posMax; pos++)
|
||||
{
|
||||
long c = getPixelColor(map, pos * 3);
|
||||
if (c != rr->from)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int rand = rr->rng.Random(0, rr->to_count - 1);
|
||||
int x = pos % width;
|
||||
int y = pos / width;
|
||||
floodFill(map, width, height, x, y, rr->from, rr->to[rand]);
|
||||
}
|
||||
}
|
||||
free(replacements);
|
||||
}
|
||||
};
|
||||
|
||||
struct TilesetCacheEntry
|
||||
{
|
||||
unsigned long color;
|
||||
stbhw_tileset *tileset;
|
||||
};
|
||||
|
||||
static TilesetCacheEntry *tilesetCache = NULL;
|
||||
static uint tilesetCacheLen = 0;
|
||||
static uint tilesetCacheCap = 0;
|
||||
|
||||
static uint nextWangRandom(void *user)
|
||||
{
|
||||
return ((NollaPrng *)user)->NextU();
|
||||
}
|
||||
|
||||
stbhw_tileset *get_tileset(unsigned long color, const unsigned char *rgba_tiles_data, uint tiles_width, uint tiles_height)
|
||||
{
|
||||
for (uint i = 0; i < tilesetCacheLen; i++)
|
||||
{
|
||||
if (tilesetCache[i].color == color)
|
||||
{
|
||||
return tilesetCache[i].tileset;
|
||||
}
|
||||
}
|
||||
|
||||
stbhw_tileset *ts = (stbhw_tileset *)malloc(sizeof(stbhw_tileset));
|
||||
memset(ts, 0, sizeof(stbhw_tileset));
|
||||
unsigned char *rgb_tiles_data = (unsigned char *)malloc(3 * tiles_width * tiles_height);
|
||||
rgbaToRgb(rgba_tiles_data, rgb_tiles_data, tiles_width, tiles_height);
|
||||
int success = stbhw_build_tileset_from_image(ts, rgb_tiles_data, tiles_width * 3, tiles_width, tiles_height);
|
||||
free(rgb_tiles_data);
|
||||
if (!success)
|
||||
{
|
||||
free(ts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (tilesetCacheLen == tilesetCacheCap)
|
||||
{
|
||||
tilesetCacheCap = tilesetCacheCap == 0 ? 16 : tilesetCacheCap * 2;
|
||||
tilesetCache = (TilesetCacheEntry *)realloc(tilesetCache, tilesetCacheCap * sizeof(TilesetCacheEntry));
|
||||
}
|
||||
tilesetCache[tilesetCacheLen++] = TilesetCacheEntry{color, ts};
|
||||
return ts;
|
||||
}
|
||||
|
||||
void generate_map_from_rgba_tiles(
|
||||
unsigned char *rgba_tiles_data,
|
||||
uint tiles_width,
|
||||
uint tiles_height,
|
||||
unsigned char *result,
|
||||
uint width,
|
||||
uint height,
|
||||
unsigned long color,
|
||||
bool isCoalMine,
|
||||
bool shouldBlockOutRooms,
|
||||
unsigned int *randomMaterials,
|
||||
int worldX,
|
||||
int worldY)
|
||||
{
|
||||
if (rgba_tiles_data == NULL || tiles_width == 0 || tiles_height == 0)
|
||||
{
|
||||
whiteout(result, width, height);
|
||||
return;
|
||||
}
|
||||
|
||||
stbhw_tileset *ts = get_tileset(color, rgba_tiles_data, tiles_width, tiles_height);
|
||||
if (ts == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NollaPrng rng = GetRNG(width);
|
||||
|
||||
int tries = 0;
|
||||
long mod_malloc_amount = 3 * width * (height + 4);
|
||||
unsigned char *res = (unsigned char *)malloc(mod_malloc_amount);
|
||||
long malloc_amount = 3 * width * height;
|
||||
|
||||
unsigned char *rgb_result = (unsigned char *)malloc(malloc_amount);
|
||||
|
||||
bool hasPath = false;
|
||||
do
|
||||
{
|
||||
if (tries >= 100)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
NollaPrng rng2 = NewNollaPrng(rng.NextU());
|
||||
|
||||
int success = stbhw_generate_image(ts, NULL, res, width * 3, width, height + 4, nextWangRandom, &rng2);
|
||||
if (!success)
|
||||
{
|
||||
free(res);
|
||||
free(rgb_result);
|
||||
return;
|
||||
}
|
||||
|
||||
int num_bytes = 4 * 3 * width;
|
||||
memcpy(rgb_result, &res[num_bytes], mod_malloc_amount - num_bytes);
|
||||
|
||||
MapGen mg = MapGen(rgb_result, width, height, isCoalMine, shouldBlockOutRooms, randomMaterials, worldX, worldY);
|
||||
|
||||
hasPath = mg.hasPath();
|
||||
if (hasPath)
|
||||
{
|
||||
mg.finalize();
|
||||
}
|
||||
mg.reset();
|
||||
tries++;
|
||||
} while (hasPath == false);
|
||||
|
||||
free(res);
|
||||
rgbToRgba(rgb_result, result, width, height);
|
||||
free(rgb_result);
|
||||
}
|
||||
|
||||
class MapHandler
|
||||
{
|
||||
public:
|
||||
uint width;
|
||||
uint height;
|
||||
unsigned long color;
|
||||
bool isCoalMine;
|
||||
bool shouldBlockOutRooms;
|
||||
unsigned int *randomMaterials;
|
||||
int worldX;
|
||||
int worldY;
|
||||
|
||||
unsigned char *map;
|
||||
unsigned char *bigMap;
|
||||
|
||||
void init(
|
||||
uint _width,
|
||||
uint _height,
|
||||
unsigned long _color,
|
||||
bool _isCoalMine,
|
||||
bool _shouldBlockOutRooms,
|
||||
uintptr_t _randomMaterials,
|
||||
int _worldX,
|
||||
int _worldY)
|
||||
{
|
||||
width = _width;
|
||||
height = _height;
|
||||
color = _color;
|
||||
isCoalMine = _isCoalMine;
|
||||
shouldBlockOutRooms = _shouldBlockOutRooms;
|
||||
randomMaterials = reinterpret_cast<unsigned int *>(_randomMaterials);
|
||||
worldX = _worldX - WORLD_OFFSET_X;
|
||||
worldY = _worldY - WORLD_OFFSET_Y;
|
||||
map = (unsigned char *)malloc(_width * _height * 4);
|
||||
bigMap = (unsigned char *)malloc(_width * 10 * _height * 10 * 4);
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
free(map);
|
||||
free(bigMap);
|
||||
}
|
||||
|
||||
void generate_map(const unsigned char *png_data, uint32_t png_len)
|
||||
{
|
||||
if (png_data == NULL || png_len == 0)
|
||||
{
|
||||
whiteout(map, width, height);
|
||||
return;
|
||||
}
|
||||
image tiles = load_png_bytes(png_data, png_len);
|
||||
generate_map_from_rgba_tiles(
|
||||
tiles.image,
|
||||
tiles.width,
|
||||
tiles.height,
|
||||
map,
|
||||
width,
|
||||
height,
|
||||
color,
|
||||
isCoalMine,
|
||||
shouldBlockOutRooms,
|
||||
randomMaterials,
|
||||
worldX,
|
||||
worldY);
|
||||
free_png_image(&tiles);
|
||||
}
|
||||
|
||||
void toBig()
|
||||
{
|
||||
scaleImage(map, width, height, bigMap, 10);
|
||||
}
|
||||
|
||||
void drawImageData(image *img, int gx, int gy, uintptr_t _colorToMaterialTable)
|
||||
{
|
||||
if (img == NULL || img->image == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// int tx = (gx) / 512;
|
||||
int tx = (gx + 5) / 512;
|
||||
// int rx = (gx) % 512;
|
||||
int rx = (gx + 5) % 512;
|
||||
|
||||
// int ty = (gy) / 512;
|
||||
int ty = (gy + 13) / 512;
|
||||
// int ry = (gy) % 512;
|
||||
int ry = (gy + 13) % 512;
|
||||
|
||||
int old_px = (tx - worldX) * 512 + rx;
|
||||
int old_py = (ty - worldY) * 512 + ry;
|
||||
|
||||
int px = gx - worldX * 512;
|
||||
int py = gy - worldY * 512;
|
||||
|
||||
// printf("drawImageData path: %s, gx: %i, gy: %i, px: %i, py: %i, old_px: %i, old_py: %i\n", path.c_str(), gx, gy, px, py, old_px, old_py);
|
||||
|
||||
const unsigned int *colorToMaterialTable = reinterpret_cast<unsigned int *>(_colorToMaterialTable);
|
||||
|
||||
// Check that the image won't go out of bounds
|
||||
// if (px + img.width > width * 10 || py + img.height > height * 10)
|
||||
// {
|
||||
// printf("drawImageData out of bounds %i %i %i %i\n", px, py, img.width, img.height);
|
||||
// return;
|
||||
// }
|
||||
// if (px < 0 || py < 0)
|
||||
// {
|
||||
// printf("drawImageData out of bounds %i %i %i %i\n", px, py, img.width, img.height);
|
||||
// return;
|
||||
// }
|
||||
|
||||
for (int y = 0; y < img->height; y++)
|
||||
{
|
||||
for (int x = 0; x < img->width; x++)
|
||||
{
|
||||
unsigned long offset = getPos(img->width, img->channels, x, y);
|
||||
unsigned long pixel = getRGBAPixelColor(img->image, offset);
|
||||
bool isTransparent = isRGBATransparent(pixel);
|
||||
if (pixel == 0xFF || isTransparent) // 0x000000FF (black)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned long toColor = pixel;
|
||||
for (int i = 0; i < colorToMaterialTable[0]; i++)
|
||||
{
|
||||
int offset = 1 + i * 2;
|
||||
if (colorToMaterialTable[offset] == pixel)
|
||||
{
|
||||
toColor = colorToMaterialTable[offset + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
setRGBAPixelColor(bigMap, width * 10, px + x, py + y, toColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// map and result are allocated and freed in js.
|
||||
// Only used in coalmines?
|
||||
STBHW_EXTERN void generate_path_map(
|
||||
unsigned char map[],
|
||||
uint map_w,
|
||||
uint map_h,
|
||||
unsigned char result[],
|
||||
int worldX,
|
||||
int worldY)
|
||||
{
|
||||
long malloc_amount = 3 * map_w * map_h;
|
||||
memcpy(result, map, malloc_amount);
|
||||
uint path_start_x = 0x8e;
|
||||
floodFill(result, map_w, map_h, path_start_x, 1, COLOR_BLACK, COLOR_PURPLE);
|
||||
}
|
||||
Reference in new issue
Block a user