init
This commit is contained in:
commit
87bff164f1
416 files changed
+279512
No files matched your search
+334
@@ -0,0 +1,334 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// MurmurHash3 was written by Austin Appleby, and is placed in the public
|
||||
// domain. The author hereby disclaims copyright to this source code.
|
||||
|
||||
// Note - The x86 and x64 versions do _not_ produce the same results, as the
|
||||
// algorithms are optimized for their respective platforms. You can still
|
||||
// compile and run any of them on any platform, but your performance with the
|
||||
// non-native version will be less than optimal.
|
||||
|
||||
#include "MurmurHash3.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Platform-specific functions and macros
|
||||
|
||||
// Microsoft Visual Studio
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
#define FORCE_INLINE __forceinline
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ROTL32(x,y) _rotl(x,y)
|
||||
#define ROTL64(x,y) _rotl64(x,y)
|
||||
|
||||
#define BIG_CONSTANT(x) (x)
|
||||
|
||||
// Other compilers
|
||||
|
||||
#else // defined(_MSC_VER)
|
||||
|
||||
#define FORCE_INLINE inline __attribute__((always_inline))
|
||||
|
||||
inline uint32_t rotl32 ( uint32_t x, int8_t r )
|
||||
{
|
||||
return (x << r) | (x >> (32 - r));
|
||||
}
|
||||
|
||||
inline uint64_t rotl64 ( uint64_t x, int8_t r )
|
||||
{
|
||||
return (x << r) | (x >> (64 - r));
|
||||
}
|
||||
|
||||
#define ROTL32(x,y) rotl32(x,y)
|
||||
#define ROTL64(x,y) rotl64(x,y)
|
||||
|
||||
#define BIG_CONSTANT(x) (x##LLU)
|
||||
|
||||
#endif // !defined(_MSC_VER)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Block read - if your platform needs to do endian-swapping or can only
|
||||
// handle aligned reads, do the conversion here
|
||||
|
||||
FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i )
|
||||
{
|
||||
return p[i];
|
||||
}
|
||||
|
||||
FORCE_INLINE uint64_t getblock64 ( const uint64_t * p, int i )
|
||||
{
|
||||
return p[i];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Finalization mix - force all bits of a hash block to avalanche
|
||||
|
||||
FORCE_INLINE uint32_t fmix32 ( uint32_t h )
|
||||
{
|
||||
h ^= h >> 16;
|
||||
h *= 0x85ebca6b;
|
||||
h ^= h >> 13;
|
||||
h *= 0xc2b2ae35;
|
||||
h ^= h >> 16;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
//----------
|
||||
|
||||
FORCE_INLINE uint64_t fmix64 ( uint64_t k )
|
||||
{
|
||||
k ^= k >> 33;
|
||||
k *= BIG_CONSTANT(0xff51afd7ed558ccd);
|
||||
k ^= k >> 33;
|
||||
k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
|
||||
k ^= k >> 33;
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MurmurHash3_x86_32 ( const void * key, int len,
|
||||
uint32_t seed, void * out )
|
||||
{
|
||||
const uint8_t * data = (const uint8_t*)key;
|
||||
const int nblocks = len / 4;
|
||||
|
||||
uint32_t h1 = seed;
|
||||
|
||||
const uint32_t c1 = 0xcc9e2d51;
|
||||
const uint32_t c2 = 0x1b873593;
|
||||
|
||||
//----------
|
||||
// body
|
||||
|
||||
const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
|
||||
|
||||
for(int i = -nblocks; i; i++)
|
||||
{
|
||||
uint32_t k1 = getblock32(blocks,i);
|
||||
|
||||
k1 *= c1;
|
||||
k1 = ROTL32(k1,15);
|
||||
k1 *= c2;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = ROTL32(h1,13);
|
||||
h1 = h1*5+0xe6546b64;
|
||||
}
|
||||
|
||||
//----------
|
||||
// tail
|
||||
|
||||
const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
|
||||
|
||||
uint32_t k1 = 0;
|
||||
|
||||
switch(len & 3)
|
||||
{
|
||||
case 3: k1 ^= tail[2] << 16;
|
||||
case 2: k1 ^= tail[1] << 8;
|
||||
case 1: k1 ^= tail[0];
|
||||
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
||||
};
|
||||
|
||||
//----------
|
||||
// finalization
|
||||
|
||||
h1 ^= len;
|
||||
|
||||
h1 = fmix32(h1);
|
||||
|
||||
*(uint32_t*)out = h1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MurmurHash3_x86_128 ( const void * key, const int len,
|
||||
uint32_t seed, void * out )
|
||||
{
|
||||
const uint8_t * data = (const uint8_t*)key;
|
||||
const int nblocks = len / 16;
|
||||
|
||||
uint32_t h1 = seed;
|
||||
uint32_t h2 = seed;
|
||||
uint32_t h3 = seed;
|
||||
uint32_t h4 = seed;
|
||||
|
||||
const uint32_t c1 = 0x239b961b;
|
||||
const uint32_t c2 = 0xab0e9789;
|
||||
const uint32_t c3 = 0x38b34ae5;
|
||||
const uint32_t c4 = 0xa1e38b93;
|
||||
|
||||
//----------
|
||||
// body
|
||||
|
||||
const uint32_t * blocks = (const uint32_t *)(data + nblocks*16);
|
||||
|
||||
for(int i = -nblocks; i; i++)
|
||||
{
|
||||
uint32_t k1 = getblock32(blocks,i*4+0);
|
||||
uint32_t k2 = getblock32(blocks,i*4+1);
|
||||
uint32_t k3 = getblock32(blocks,i*4+2);
|
||||
uint32_t k4 = getblock32(blocks,i*4+3);
|
||||
|
||||
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
||||
|
||||
h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b;
|
||||
|
||||
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
|
||||
|
||||
h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747;
|
||||
|
||||
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
|
||||
|
||||
h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35;
|
||||
|
||||
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
|
||||
|
||||
h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17;
|
||||
}
|
||||
|
||||
//----------
|
||||
// tail
|
||||
|
||||
const uint8_t * tail = (const uint8_t*)(data + nblocks*16);
|
||||
|
||||
uint32_t k1 = 0;
|
||||
uint32_t k2 = 0;
|
||||
uint32_t k3 = 0;
|
||||
uint32_t k4 = 0;
|
||||
|
||||
switch(len & 15)
|
||||
{
|
||||
case 15: k4 ^= tail[14] << 16;
|
||||
case 14: k4 ^= tail[13] << 8;
|
||||
case 13: k4 ^= tail[12] << 0;
|
||||
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
|
||||
|
||||
case 12: k3 ^= tail[11] << 24;
|
||||
case 11: k3 ^= tail[10] << 16;
|
||||
case 10: k3 ^= tail[ 9] << 8;
|
||||
case 9: k3 ^= tail[ 8] << 0;
|
||||
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
|
||||
|
||||
case 8: k2 ^= tail[ 7] << 24;
|
||||
case 7: k2 ^= tail[ 6] << 16;
|
||||
case 6: k2 ^= tail[ 5] << 8;
|
||||
case 5: k2 ^= tail[ 4] << 0;
|
||||
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
|
||||
|
||||
case 4: k1 ^= tail[ 3] << 24;
|
||||
case 3: k1 ^= tail[ 2] << 16;
|
||||
case 2: k1 ^= tail[ 1] << 8;
|
||||
case 1: k1 ^= tail[ 0] << 0;
|
||||
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
||||
};
|
||||
|
||||
//----------
|
||||
// finalization
|
||||
|
||||
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
|
||||
h1 = fmix32(h1);
|
||||
h2 = fmix32(h2);
|
||||
h3 = fmix32(h3);
|
||||
h4 = fmix32(h4);
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
|
||||
((uint32_t*)out)[0] = h1;
|
||||
((uint32_t*)out)[1] = h2;
|
||||
((uint32_t*)out)[2] = h3;
|
||||
((uint32_t*)out)[3] = h4;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MurmurHash3_x64_128 ( const void * key, const int len,
|
||||
const uint32_t seed, void * out )
|
||||
{
|
||||
const uint8_t * data = (const uint8_t*)key;
|
||||
const int nblocks = len / 16;
|
||||
|
||||
uint64_t h1 = seed;
|
||||
uint64_t h2 = seed;
|
||||
|
||||
const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5);
|
||||
const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f);
|
||||
|
||||
//----------
|
||||
// body
|
||||
|
||||
const uint64_t * blocks = (const uint64_t *)(data);
|
||||
|
||||
for(int i = 0; i < nblocks; i++)
|
||||
{
|
||||
uint64_t k1 = getblock64(blocks,i*2+0);
|
||||
uint64_t k2 = getblock64(blocks,i*2+1);
|
||||
|
||||
k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
|
||||
|
||||
h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729;
|
||||
|
||||
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
|
||||
|
||||
h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5;
|
||||
}
|
||||
|
||||
//----------
|
||||
// tail
|
||||
|
||||
const uint8_t * tail = (const uint8_t*)(data + nblocks*16);
|
||||
|
||||
uint64_t k1 = 0;
|
||||
uint64_t k2 = 0;
|
||||
|
||||
switch(len & 15)
|
||||
{
|
||||
case 15: k2 ^= ((uint64_t)tail[14]) << 48;
|
||||
case 14: k2 ^= ((uint64_t)tail[13]) << 40;
|
||||
case 13: k2 ^= ((uint64_t)tail[12]) << 32;
|
||||
case 12: k2 ^= ((uint64_t)tail[11]) << 24;
|
||||
case 11: k2 ^= ((uint64_t)tail[10]) << 16;
|
||||
case 10: k2 ^= ((uint64_t)tail[ 9]) << 8;
|
||||
case 9: k2 ^= ((uint64_t)tail[ 8]) << 0;
|
||||
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
|
||||
|
||||
case 8: k1 ^= ((uint64_t)tail[ 7]) << 56;
|
||||
case 7: k1 ^= ((uint64_t)tail[ 6]) << 48;
|
||||
case 6: k1 ^= ((uint64_t)tail[ 5]) << 40;
|
||||
case 5: k1 ^= ((uint64_t)tail[ 4]) << 32;
|
||||
case 4: k1 ^= ((uint64_t)tail[ 3]) << 24;
|
||||
case 3: k1 ^= ((uint64_t)tail[ 2]) << 16;
|
||||
case 2: k1 ^= ((uint64_t)tail[ 1]) << 8;
|
||||
case 1: k1 ^= ((uint64_t)tail[ 0]) << 0;
|
||||
k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
|
||||
};
|
||||
|
||||
//----------
|
||||
// finalization
|
||||
|
||||
h1 ^= len; h2 ^= len;
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
h1 = fmix64(h1);
|
||||
h2 = fmix64(h2);
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
((uint64_t*)out)[0] = h1;
|
||||
((uint64_t*)out)[1] = h2;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// MurmurHash3 was written by Austin Appleby, and is placed in the public
|
||||
// domain. The author hereby disclaims copyright to this source code.
|
||||
|
||||
#ifndef _MURMURHASH3_H_
|
||||
#define _MURMURHASH3_H_
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Platform-specific functions and macros
|
||||
|
||||
// Microsoft Visual Studio
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1600)
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
// Other compilers
|
||||
|
||||
#else // defined(_MSC_VER)
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#endif // !defined(_MSC_VER)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MurmurHash3_x86_32 ( const void * key, int len, uint32_t seed, void * out );
|
||||
|
||||
void MurmurHash3_x86_128 ( const void * key, int len, uint32_t seed, void * out );
|
||||
|
||||
void MurmurHash3_x64_128 ( const void * key, int len, uint32_t seed, void * out );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#endif // _MURMURHASH3_H_
|
||||
+79106
File diff suppressed because it is too large.
Load diff
Vendored
+1539
File diff suppressed because it is too large.
Load diff
+105
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#include "../noita_random.cpp"
|
||||
|
||||
const unsigned long COLOR_PURPLE = 0x7f007f;
|
||||
const unsigned long COLOR_RED = 0xff0000;
|
||||
const unsigned long COLOR_BLACK = 0x000000;
|
||||
const unsigned long COLOR_WHITE = 0xffffff;
|
||||
const unsigned long COLOR_YELLOW = 0xffff00;
|
||||
|
||||
// Extra traversable colors
|
||||
const unsigned long COLOR_COFFEE = 0xc0ffee;
|
||||
const unsigned long COLOR_FROZEN_VAULT_MINT = 0xcff7c8;
|
||||
const unsigned long COLOR_HELL_GREEN = 0x8aff80;
|
||||
|
||||
unsigned long createRGB(const unsigned char r, const unsigned char g, const unsigned char b)
|
||||
{
|
||||
return ((r & 0xff) << 16) + ((g & 0xff) << 8) + (b & 0xff);
|
||||
}
|
||||
|
||||
unsigned long createRGBA(const unsigned char r, const unsigned char g, const unsigned char b, const unsigned char a)
|
||||
{
|
||||
return ((r & 0xff) << 24) | ((g & 0xff) << 16) | ((b & 0xff) << 8) | (a & 0xff);
|
||||
}
|
||||
|
||||
unsigned long getPos(const uint w, const uint s, const uint x, const uint y)
|
||||
{
|
||||
return s * (w * y + x);
|
||||
}
|
||||
|
||||
unsigned long getPixelColor(const unsigned char *map, unsigned long pos)
|
||||
{
|
||||
unsigned char r = map[pos];
|
||||
unsigned char g = map[pos + 1];
|
||||
unsigned char b = map[pos + 2];
|
||||
return createRGB(r, g, b);
|
||||
}
|
||||
|
||||
unsigned long getPixelColor(const unsigned char *map, const uint w, const uint x, const uint y)
|
||||
{
|
||||
unsigned long pos = getPos(w, 3, x, y);
|
||||
return getPixelColor(map, pos);
|
||||
}
|
||||
|
||||
bool ShadeOfGrey(const unsigned long color, const unsigned char limit)
|
||||
{
|
||||
unsigned char r = (color >> 16) & 0xFF;
|
||||
unsigned char g = (color >> 8) & 0xFF;
|
||||
unsigned char b = color & 0xFF;
|
||||
|
||||
return r <= limit && (r == g) && (g == b) && color != COLOR_BLACK && color != COLOR_WHITE;
|
||||
}
|
||||
|
||||
bool isRGBATransparent(const unsigned long color)
|
||||
{
|
||||
unsigned char a = color & 0xFF;
|
||||
return a == 0;
|
||||
}
|
||||
|
||||
unsigned long getRGBAPixelColor(const unsigned char *map, unsigned long pos)
|
||||
{
|
||||
unsigned char r = map[pos];
|
||||
unsigned char g = map[pos + 1];
|
||||
unsigned char b = map[pos + 2];
|
||||
unsigned char a = map[pos + 3];
|
||||
return createRGBA(r, g, b, a);
|
||||
}
|
||||
|
||||
unsigned long getRGBAPixelColor(const unsigned char *map, const uint w, const uint x, const uint y)
|
||||
{
|
||||
unsigned long pos = getPos(w, 4, x, y);
|
||||
return getRGBAPixelColor(map, pos);
|
||||
}
|
||||
|
||||
void setPixelColor(unsigned char *map, unsigned long pos, unsigned long color)
|
||||
{
|
||||
unsigned char r = ((color >> 16) & 0xff);
|
||||
unsigned char g = ((color >> 8) & 0xff);
|
||||
unsigned char b = ((color) & 0xff);
|
||||
map[pos] = r;
|
||||
map[pos + 1] = g;
|
||||
map[pos + 2] = b;
|
||||
}
|
||||
void setRGBAPixelColor(unsigned char *map, unsigned long pos, unsigned long color)
|
||||
{
|
||||
unsigned char r = ((color >> 24) & 0xff);
|
||||
unsigned char g = ((color >> 16) & 0xff);
|
||||
unsigned char b = ((color >> 8) & 0xff);
|
||||
unsigned char a = ((color) & 0xff);
|
||||
map[pos] = r;
|
||||
map[pos + 1] = g;
|
||||
map[pos + 2] = b;
|
||||
map[pos + 3] = a;
|
||||
}
|
||||
|
||||
void setPixelColor(unsigned char *map, uint w, uint x, uint y, unsigned long color)
|
||||
{
|
||||
unsigned long pos = getPos(w, 3, x, y);
|
||||
setPixelColor(map, pos, color);
|
||||
}
|
||||
void setRGBAPixelColor(unsigned char *map, uint w, uint x, uint y, unsigned long color)
|
||||
{
|
||||
unsigned long pos = getPos(w, 4, x, y);
|
||||
setRGBAPixelColor(map, pos, color);
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void zero_out(uint8_t *data, uint l)
|
||||
{
|
||||
if (data == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (uint i = 0; i < l; i += 4)
|
||||
{
|
||||
uint8_t r = data[i + 0];
|
||||
uint8_t g = data[i + 1];
|
||||
uint8_t b = data[i + 2];
|
||||
uint8_t a = data[i + 3];
|
||||
if (a == 0 && r == 255 && g == 255 && b == 255)
|
||||
{
|
||||
data[i + 0] = 0;
|
||||
data[i + 1] = 0;
|
||||
data[i + 2] = 0;
|
||||
data[i + 3] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct image
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int channels;
|
||||
stbi_uc *image;
|
||||
};
|
||||
|
||||
image load_png_bytes(const unsigned char *data, int len)
|
||||
{
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int channels = 0;
|
||||
stbi_uc *img = stbi_load_from_memory(data, len, &width, &height, &channels, 4);
|
||||
if (img == NULL)
|
||||
{
|
||||
return image{0, 0, 0, NULL};
|
||||
}
|
||||
|
||||
channels = 4;
|
||||
zero_out(img, width * height * channels);
|
||||
return image{width, height, channels, img};
|
||||
}
|
||||
|
||||
void free_png_image(image *img)
|
||||
{
|
||||
if (img == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
stbi_image_free(img->image);
|
||||
img->image = NULL;
|
||||
img->width = 0;
|
||||
img->height = 0;
|
||||
img->channels = 0;
|
||||
}
|
||||
|
||||
void scaleImage(unsigned char *src, uint src_width, uint src_height, unsigned char *dest, unsigned char scale)
|
||||
{
|
||||
int dest_width = src_width * scale;
|
||||
int dest_height = src_height * scale;
|
||||
|
||||
for (int y = 0; y < dest_height; y++)
|
||||
{
|
||||
int src_y = y / scale;
|
||||
|
||||
for (int x = 0; x < dest_width; x++)
|
||||
{
|
||||
int src_x = x / scale;
|
||||
|
||||
int src_index = (src_y * src_width + src_x) * 4;
|
||||
int dest_index = (y * dest_width + x) * 4;
|
||||
|
||||
dest[dest_index + 0] = src[src_index + 0];
|
||||
dest[dest_index + 1] = src[src_index + 1];
|
||||
dest[dest_index + 2] = src[src_index + 2];
|
||||
dest[dest_index + 3] = src[src_index + 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
+328
@@ -0,0 +1,328 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pix.cpp"
|
||||
|
||||
const unsigned long NO_PARENT = (unsigned long)-1;
|
||||
|
||||
void fill(unsigned char *map,
|
||||
int w,
|
||||
int x1,
|
||||
int x2,
|
||||
int y1,
|
||||
int y2,
|
||||
long color)
|
||||
{
|
||||
for (int x = x1; x <= x2; x++)
|
||||
{
|
||||
for (int y = y1; y <= y2; y++)
|
||||
{
|
||||
setPixelColor(map, w, x, y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FloodPoint
|
||||
{
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
};
|
||||
|
||||
struct FloodStack
|
||||
{
|
||||
FloodPoint *items;
|
||||
unsigned long len;
|
||||
unsigned long cap;
|
||||
};
|
||||
|
||||
static void flood_stack_push(FloodStack *stack, unsigned int x, unsigned int y)
|
||||
{
|
||||
if (stack->len == stack->cap)
|
||||
{
|
||||
stack->cap = stack->cap == 0 ? 256 : stack->cap * 2;
|
||||
stack->items = (FloodPoint *)realloc(stack->items, stack->cap * sizeof(FloodPoint));
|
||||
}
|
||||
stack->items[stack->len++] = FloodPoint{x, y};
|
||||
}
|
||||
|
||||
static FloodPoint flood_stack_pop(FloodStack *stack)
|
||||
{
|
||||
return stack->items[--stack->len];
|
||||
}
|
||||
|
||||
void floodFill(unsigned char *map,
|
||||
uint width,
|
||||
uint height,
|
||||
uint initialX,
|
||||
uint initialY,
|
||||
unsigned long fromColor,
|
||||
unsigned long toColor)
|
||||
{
|
||||
if (initialX >= width || initialY >= height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char *visited = (unsigned char *)calloc(width * height, 1);
|
||||
FloodStack stack = {};
|
||||
|
||||
flood_stack_push(&stack, initialX, initialY);
|
||||
visited[getPos(width, 1, initialX, initialY)] = true;
|
||||
|
||||
while (stack.len > 0)
|
||||
{
|
||||
FloodPoint pos = flood_stack_pop(&stack);
|
||||
const int x = pos.x;
|
||||
const int y = pos.y;
|
||||
|
||||
setPixelColor(map, width, x, y, toColor);
|
||||
|
||||
const int next[4][2] = {
|
||||
{x - 1, y},
|
||||
{x + 1, y},
|
||||
{x, y - 1},
|
||||
{x, y + 1},
|
||||
};
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int nx = next[i][0];
|
||||
int ny = next[i][1];
|
||||
if (nx < 0 || ny < 0 || (uint)nx >= width || (uint)ny >= height)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned long p = getPos(width, 1, nx, ny);
|
||||
if (visited[p])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned long nc = getPixelColor(map, p * 3);
|
||||
if (nc != fromColor || nc == toColor)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
visited[p] = true;
|
||||
flood_stack_push(&stack, nx, ny);
|
||||
}
|
||||
}
|
||||
|
||||
free(stack.items);
|
||||
free(visited);
|
||||
}
|
||||
|
||||
struct SearchNode
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
unsigned long parent;
|
||||
unsigned long order;
|
||||
};
|
||||
|
||||
struct NodeHeap
|
||||
{
|
||||
SearchNode *items;
|
||||
unsigned long len;
|
||||
unsigned long cap;
|
||||
int targetY;
|
||||
};
|
||||
|
||||
static int node_priority(const NodeHeap *heap, const SearchNode *node)
|
||||
{
|
||||
int dy = node->y - heap->targetY;
|
||||
return dy < 0 ? -dy : dy;
|
||||
}
|
||||
|
||||
static bool node_less(const NodeHeap *heap, const SearchNode *a, const SearchNode *b)
|
||||
{
|
||||
int ap = node_priority(heap, a);
|
||||
int bp = node_priority(heap, b);
|
||||
if (ap != bp)
|
||||
{
|
||||
return ap < bp;
|
||||
}
|
||||
return a->order < b->order;
|
||||
}
|
||||
|
||||
static void heap_swap(SearchNode *a, SearchNode *b)
|
||||
{
|
||||
SearchNode tmp = *a;
|
||||
*a = *b;
|
||||
*b = tmp;
|
||||
}
|
||||
|
||||
static void heap_push(NodeHeap *heap, SearchNode node)
|
||||
{
|
||||
if (heap->len == heap->cap)
|
||||
{
|
||||
heap->cap = heap->cap == 0 ? 256 : heap->cap * 2;
|
||||
heap->items = (SearchNode *)realloc(heap->items, heap->cap * sizeof(SearchNode));
|
||||
}
|
||||
|
||||
unsigned long i = heap->len++;
|
||||
heap->items[i] = node;
|
||||
while (i > 0)
|
||||
{
|
||||
unsigned long parent = (i - 1) / 2;
|
||||
if (!node_less(heap, &heap->items[i], &heap->items[parent]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
heap_swap(&heap->items[i], &heap->items[parent]);
|
||||
i = parent;
|
||||
}
|
||||
}
|
||||
|
||||
static SearchNode heap_pop(NodeHeap *heap)
|
||||
{
|
||||
SearchNode result = heap->items[0];
|
||||
heap->items[0] = heap->items[--heap->len];
|
||||
|
||||
unsigned long i = 0;
|
||||
while (true)
|
||||
{
|
||||
unsigned long left = i * 2 + 1;
|
||||
unsigned long right = left + 1;
|
||||
unsigned long best = i;
|
||||
|
||||
if (left < heap->len && node_less(heap, &heap->items[left], &heap->items[best]))
|
||||
{
|
||||
best = left;
|
||||
}
|
||||
if (right < heap->len && node_less(heap, &heap->items[right], &heap->items[best]))
|
||||
{
|
||||
best = right;
|
||||
}
|
||||
if (best == i)
|
||||
{
|
||||
break;
|
||||
}
|
||||
heap_swap(&heap->items[i], &heap->items[best]);
|
||||
i = best;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
class Search
|
||||
{
|
||||
public:
|
||||
unsigned char *map;
|
||||
int width;
|
||||
int height;
|
||||
int targetX;
|
||||
int targetY;
|
||||
char *visited;
|
||||
unsigned long *parents;
|
||||
unsigned long endPos = NO_PARENT;
|
||||
|
||||
NodeHeap heap = {};
|
||||
unsigned long order = 0;
|
||||
|
||||
Search(unsigned char _map[], int _width, int _height, int _targetX, int _targetY, unsigned long *_parents)
|
||||
{
|
||||
map = _map;
|
||||
width = _width;
|
||||
height = _height;
|
||||
targetX = _targetX;
|
||||
targetY = _targetY;
|
||||
parents = _parents;
|
||||
visited = (char *)calloc(width * height, 1);
|
||||
heap.targetY = targetY;
|
||||
}
|
||||
|
||||
~Search()
|
||||
{
|
||||
free(heap.items);
|
||||
free(visited);
|
||||
}
|
||||
|
||||
bool findPath(int x, int y)
|
||||
{
|
||||
unsigned long start = pos(x, y);
|
||||
setVisited(x, y);
|
||||
parents[start] = NO_PARENT;
|
||||
heap_push(&heap, SearchNode{x, y, NO_PARENT, order++});
|
||||
|
||||
while (heap.len > 0)
|
||||
{
|
||||
SearchNode n = heap_pop(&heap);
|
||||
if (atTarget(&n))
|
||||
{
|
||||
endPos = pos(n.x, n.y);
|
||||
return true;
|
||||
}
|
||||
|
||||
tryNext(n.x, n.y - 1, &n);
|
||||
tryNext(n.x - 1, n.y, &n);
|
||||
tryNext(n.x + 1, n.y, &n);
|
||||
tryNext(n.x, n.y + 1, &n);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long pos(int x, int y) const
|
||||
{
|
||||
return getPos(width, 1, x, y);
|
||||
}
|
||||
|
||||
private:
|
||||
bool tryNext(int x, int y, SearchNode *n)
|
||||
{
|
||||
if (!valid(x, y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (isVisited(x, y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!traversable(x, y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long p = pos(x, y);
|
||||
unsigned long parent = pos(n->x, n->y);
|
||||
setVisited(x, y);
|
||||
parents[p] = parent;
|
||||
heap_push(&heap, SearchNode{x, y, parent, order++});
|
||||
return true;
|
||||
}
|
||||
|
||||
void setVisited(int x, int y)
|
||||
{
|
||||
unsigned long p = pos(x, y);
|
||||
|
||||
setPixelColor(map, p * 3, COLOR_PURPLE);
|
||||
visited[p] = true;
|
||||
}
|
||||
|
||||
bool isVisited(int x, int y)
|
||||
{
|
||||
return visited[pos(x, y)];
|
||||
}
|
||||
|
||||
bool valid(int x, int y) const
|
||||
{
|
||||
return x >= 0 && y >= 0 && x < width && y < height;
|
||||
}
|
||||
|
||||
bool traversable(int x, int y) const
|
||||
{
|
||||
unsigned long p = pos(x, y);
|
||||
long c = getPixelColor(map, p * 3);
|
||||
|
||||
return c == COLOR_BLACK || c == COLOR_COFFEE || c == COLOR_FROZEN_VAULT_MINT || c == COLOR_HELL_GREEN;
|
||||
}
|
||||
|
||||
bool atTarget(SearchNode *n) const
|
||||
{
|
||||
return targetY == n->y;
|
||||
}
|
||||
};
|
||||
+1396
File diff suppressed because it is too large.
Load diff
+7987
File diff suppressed because it is too large.
Load diff
+1724
File diff suppressed because it is too large.
Load diff
+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