This commit is contained in:
Gaël committed 2026-10-19 20:59:15 +02:00
commit 87bff164f1
416 files changed
+279512

No files matched your search

+334
View File
@@ -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
View File
@@ -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_
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+105
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
};
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+940
View File
@@ -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);
}