~melchizedek6809/WolkenWelten

3892642742089337b09de3c789d4c7039b526b83 — Ben (Win10) 9 days ago 81b743f
Completely rewrote the fire system
M client/src/game/blockMining.c => client/src/game/blockMining.c +7 -0
@@ 48,6 48,13 @@ float blockMiningGetProgress(blockMining *bm){
	return ((float)bm->damage) / ((float)blockTypeGetHealth(bm->b));
}

void blockMiningBurnBlock(int x, int y, int z, blockId b){
	(void)x;
	(void)y;
	(void)z;
	(void)b;
}

int blockMiningUpdateMesh(int d){
	blockMining *bm = &blockMiningList[d];
	const int frame = floorf(blockMiningGetProgress(bm)*8);

M client/src/game/fire.c => client/src/game/fire.c +70 -65
@@ 21,95 21,100 @@
#include "../gfx/gfx.h"
#include "../gfx/particle.h"
#include "../gui/overlay.h"
#include "../network/chat.h"
#include "../sdl/sdl.h"
#include "../sfx/sfx.h"
#include "../../../common/src/network/messages.h"
#include "../voxel/chungus.h"
#include "../../../common/src/game/chunkOverlay.h"
#include "../../../common/src/game/fire.h"
#include "../../../common/src/misc/colors.h"
#include "../../../common/src/misc/profiling.h"

#include <string.h>

void fireNew(u16 x, u16 y, u16 z, i16 strength){
	msgFireUpdate(-1,0,0,x,y,z,strength);
}

static void fireDraw(const fire *f){
	if(abs(f->x-(int)player->pos.x) > renderDistance){return;}
	if(abs(f->y-(int)player->pos.y) > renderDistance){return;}
	if(abs(f->z-(int)player->pos.z) > renderDistance){return;}
	const vec spos = vecNew(f->x,f->y,f->z);
	const float size = (float)(f->strength * 0.01f);

static void fireGenerateParticles(int x, int y, int z, u8 strength){
	const int bx = rngValA(0xFF);
	const int by = rngValA(0xFF);
	const int bz = rngValA(0xFF);
	const float px = x + (bx / 256.0);
	const float py = y + (by / 256.0);
	const float pz = z + (bz / 256.0);

	const vec spos = vecNew(px,py,pz);
	const float size = MAX(3.f,(float)(strength * 0.03f));

	newParticleV(vecAdd(spos,vecRngAbs()), vecAdd(vecNew(0,0.01f,0),vecMulS(vecRng(),0.0001f)), size, size*0.5f,0xFF60C8FF, 96);
	if(f->strength <  64){return;}
	if(strength <  16){return;}
	newParticleV(vecAdd(spos,vecRngAbs()), vecAdd(vecNew(0,0.01f,0),vecMulS(vecRng(),0.0001f)), size*0.7f, size*0.65f,0xFF5098FF, 128);
	if(f->strength < 128){return;}
	if(strength < 48){return;}
	newParticleV(vecAdd(spos,vecRngAbs()), vecAdd(vecNew(0,0.01f,0),vecMulS(vecRng(),0.0001f)), size*0.6f, size*0.75f,0xFF1F38EF, 156);
	if(f->strength < 256){return;}
	if(strength < 128){return;}
	newParticleV(vecAdd(spos,vecRngAbs()), vecAdd(vecNew(0,0.01f,0),vecMulS(vecRng(),0.0001f)), size*0.5f, size*0.75f,0xFF1F38EF, 178);
}

static void fireDrawSmoke(const fire *f){
	if(f->strength < 256){return;}
	if(abs(f->x-(int)player->pos.x) > renderDistance){return;}
	if(abs(f->y-(int)player->pos.y) > renderDistance){return;}
	if(abs(f->z-(int)player->pos.z) > renderDistance){return;}
	const vec spos = vecNew(f->x,f->y,f->z);
	const float size = (float)(f->strength * 0.01f);
	if(strength < 160){return;}
	u32 c = 0x00101820 | (rngValR()&0x0003070F);
	newSparticleV(vecAdd(spos,vecRngAbs()), vecAdd(vecNew(0,0.001f,0),vecMulS(vecRng(),0.0001f)), size*0.01f, size*0.2f,c,2048);
}

static void fireGenerateChunkParticles(const chunkOverlay *f, int cx, int cy, int cz){
	if(player){
		if(abs(cx-(int)player->pos.x) > renderDistance){return;}
		if(abs(cy-(int)player->pos.y) > renderDistance){return;}
		if(abs(cz-(int)player->pos.z) > renderDistance){return;}
	}

	const u8 *d = &f->data[0][0][0];
	for(int x = 0; x < CHUNK_SIZE; x++){
	for(int y = 0; y < CHUNK_SIZE; y++){
	for(int z = 0; z < CHUNK_SIZE; z++){
		const u8 strength = *d++;
		if(!strength){continue;}
		fireGenerateParticles(cx+x, cy+y, cz+z, strength);
	}
	}
	}
}

void fireDrawAll(){
	static uint calls = 0;
	static  int lastTick = 0;
	int curTick;
	static int calls = 0;
	PROFILE_START();

	if(lastTick == 0){lastTick = getTicks();}
	curTick = getTicks();
	for(;lastTick < curTick;lastTick+=msPerTick){
		for(uint i=calls&0xF;i<fireCount;i+=0x10){
			fireDraw(&fireList[i]);
	for(uint i = calls&0x7; i < chungusCount; i+=8){
		chungus *cng = &chungusList[i];
		for(int x=0;x<16;x++){
		for(int y=0;y<16;y++){
		for(int z=0;z<16;z++){
			const chunk *c = &cng->chunks[x][y][z];
			if(c->fire == NULL){continue;}
			fireGenerateChunkParticles(c->fire, c->x, c->y, c->z);
		}
		}
		for(uint i=calls&0x3F;i<fireCount;i+=0x40){
			fireDrawSmoke(&fireList[i]);
		}
		calls++;
	}
	++calls;
	PROFILE_STOP();
}

void fireRecvUpdate(uint c, const packet *p){
	(void)c;
	const uint i     = p->v.u16[0];
	const uint count = p->v.u16[1];
	if(count > fireCount){
		memset(&fireList[fireCount],0,sizeof(fire) * (count-fireCount));
	}
	fireCount = count;
	fire *f = &fireList[i];
	f->x = p->v.u16[2];
	f->y = p->v.u16[3];
	f->z = p->v.u16[4];
	f->strength = p->v.i16[5];
}
void fireUpdateAll(){
	static int calls = 0;
	PROFILE_START();

void fireCheckPlayerBurn(uint off){
	for(uint i=off&0x7F;i<fireCount;i+=0x80){
		const fire *f   = &fireList[i];
		const vec fpos  = vecNew(f->x,f->y,f->z);
		const vec  dist = vecSub(player->pos,fpos);
		const float  dd = vecDot(dist,dist);
		const float fdd = MIN(9.f,(fireList[i].strength * 0.01f) * (fireList[i].strength * 0.01f));
		if(dd < fdd){
			sfxPlay(sfxUngh,1.f);
			setOverlayColor(0xA03020F0,0);
			if(characterHP(player,-1)){
				characterDyingMessage(characterGetBeing(player),0,deathCauseFire);
				setOverlayColor(0xFF000000,0);
				commitOverlayColor();
	for(uint i = calls&0xF; i < chungusCount; i+=0x10){
		chungus *cng = &chungusList[i];
		for(int x=0;x<16;x++){
		for(int y=0;y<16;y++){
		for(int z=0;z<16;z++){
			chunk *c = &cng->chunks[x][y][z];
			if(c->fire == NULL){continue;}
			if(!fireTick(c->fire, c->fluid, c->block, c->x, c->y, c->z)){
				chunkOverlayFree(c->fire);
				c->fire = NULL;
			}
		}
		}
		}
	}
}
	++calls;

void fireBoxExtinguish(u16 x, u16 y, u16 z, u16 w, u16 h, u16 d, int strength){
	(void)x;(void)y;(void)z;(void)w;(void)h;(void)d;(void)strength;
	PROFILE_STOP();
}

M client/src/game/fire.h => client/src/game/fire.h +2 -3
@@ 1,6 1,5 @@
#pragma once
#include "../../../common/src/game/fire.h"

void fireDrawAll        ();
void fireRecvUpdate     (uint c, const packet *p);
void fireCheckPlayerBurn(uint off);
void fireDrawAll   ();
void fireUpdateAll ();

M client/src/gfx/effects.c => client/src/gfx/effects.c +15 -0
@@ 234,3 234,18 @@ void fxSnowDrop(const vec pos){
		newParticleV(pos,v,16.f,6.f,color,48);
	}
}

void fxFluidVapor(int cx, int cy, int cz, int fluidType, int amountLost){
	(void)fluidType;
	(void)amountLost;
	const vec pos = vecNew(cx,cy,cz);
	//sfxPlayPos(sfxTock,0.2f,pos);
	for(int i=0;i<128;i++){
		const vec p = vecAdd(pos,vecRngAbs());
		newParticleS(p.x,p.y,p.z,0xF0F0E8E0,.7f,96);
	}
	for(int i=0;i<16;i++){
		const u32 c = 0x00C0D0E0 + (rngValR()&0x003F2F1F);
		newSparticleV(vecAdd(pos,vecRngAbs()), vecAdd(vecNew(0,0.001f+(rngValf()*0.001f),0),vecMulS(vecRng(),0.0001f)), 0.01f, 0.2f,c,768);
	}
}

M client/src/gfx/effects.h => client/src/gfx/effects.h +1 -0
@@ 12,3 12,4 @@ void fxAnimalDiedPacket (const packet *p);
void fxProjectileHit    (const packet *p);
void fxRainDrop         (const vec pos);
void fxSnowDrop         (const vec pos);
void fxFluidVapor       (int cx, int cy, int cz, int fluidType, int amountLost);

M client/src/main.c => client/src/main.c +1 -1
@@ 174,13 174,13 @@ void worldUpdate(){
		ropeUpdateAll();
		projectileUpdateAll();
		gtimeUpdate();
		fireCheckPlayerBurn(gameTicks);
		weatherUpdateAll();
		throwableUpdateAll();
		throwableCheckPickup();
		entityUpdateAll();
		lispEvents();
		fluidPhysicsTick();
		fireUpdateAll();

		gameTicks++;
	}

M client/src/misc/lisp.c => client/src/misc/lisp.c +0 -10
@@ 504,15 504,6 @@ static lVal *wwlnfProjectile(lClosure *c, lVal *v){
	return NULL;
}

static lVal *wwlnfFireNew(lClosure *c, lVal *v){
	(void)c;
	const vec pos = castToVec(lCar(v),vecNOne()); v = lCdr(v);
	const int str = castToInt(lCar(v),8);

	if(vecInWorld(pos)){fireNew(pos.x,pos.y,pos.z,str);}
	return NULL;
}

static lVal *wwlnfRaycast(lClosure *c, lVal *v){
	(void)c;
	bool before = castToBool(lCar(v));


@@ 808,7 799,6 @@ static void lispAddClientNFuncs(lClosure *c){
	lAddNativeFunc(c,"try-to-shoot",   "(cd ammo)",         "Try to shoot and cooldown for CD and use AMMO bullets",          wwlnfTryToShoot);
	lAddNativeFunc(c,"beamblast",      "(size damage hits-left)", "Calls cFunc beamblast",                                    wwlnfBeamblast);
	lAddNativeFunc(c,"projectile",     "(&type &num)",      "Fire &NUM=1 projectiles of &TYPE=0",                             wwlnfProjectile);
	lAddNativeFunc(c,"fire-new",       "(pos &strength)",   "Create/Grow a fire at POS with &STRENGTH=8",                     wwlnfFireNew);
	lAddNativeFunc(c,"toggle-inventory!","()",              "Toggle the inveotory",                                           wwlnfToggleInventory);
	lAddNativeFunc(c,"widget-focus-on-game?","()",          "Return #t if the game is focused and not some menu",             wwlnfGuiFocusOnGame);
	lAddNativeFunc(c,"draw-boundaries", "()",                "Return the current boundary drawing style",                     wwlnfDrawBoundariesGet);

M client/src/network/client.c => client/src/network/client.c +0 -3
@@ 316,9 316,6 @@ void clientParsePacket(const packet *p){
	case msgtFxProjectileHit:
		fxProjectileHit(p);
		break;
	case msgtFireRecvUpdate:
		fireRecvUpdate(-1,p);
		break;
	case msgtLispRecvSExpr:
		lispRecvSExpr(p);
		break;

M client/src/voxel/bigchungus.c => client/src/voxel/bigchungus.c +23 -1
@@ 50,7 50,6 @@ void worldInit(){
	chunkInit();
	animalInit();
	itemDropInit();
	fireInit();
	projectileInit();
	throwableInit();
}


@@ 379,3 378,26 @@ bool worldShouldBeLoaded(const vec cpos){
	(void)cpos;
	return true;
}

u8 worldGetFire(int x,int y,int z) {
	if((x|y|z)&(~0xFFFF)){return 0;}
	chungus *chng = world[x>>8][(y>>8)&0x7F][z>>8];
	if(chng == NULL){ return 0; }
	chunk *chnk = &chng->chunks[(x>>4)&0xF][(y>>4)&0xF][(z>>4)&0xF];
	if(chnk->fire == NULL){ return 0; }
	return chnk->fire->data[x&0xF][y&0xF][z&0xF];
}

u8 worldTryFire(int x,int y,int z) {
	return worldGetFire(x,y,z);
}

bool worldSetFire(int x,int y,int z, u8 level){
	if((x|y|z)&(~0xFFFF)){return NULL;}
	chungus *chng = world[x>>8][(y>>8)&0x7F][z>>8];
	if(chng == NULL){return false;}
	chunk *chnk = &chng->chunks[(x>>4)&0xF][(y>>4)&0xF][(z>>4)&0xF];
	if(chnk->fire == NULL){chnk->fire = chunkOverlayAllocate();}
	chnk->fire->data[x&0xF][y&0xF][z&0xF] = level;
	return true;
}

M client/src/voxel/bigchungus.h => client/src/voxel/bigchungus.h +8 -3
@@ 11,9 11,6 @@ void     worldBoxSphereDirty   (int x, int y, int z, int r);
blockId  worldTryB             (int x, int y, int z);
blockId  worldGetB             (int x, int y, int z);
bool     worldSetB             (int x, int y, int z, blockId block);
u8       worldTryFluid         (int x, int y, int z);
u8       worldGetFluid         (int x, int y, int z);
bool     worldSetFluid         (int x, int y, int z, blockId block);
chungus *worldTryChungus       (int x, int y, int z);
chungus *worldGetChungus       (int x, int y, int z);
chunk   *worldGetChunk         (int x, int y, int z);


@@ 26,3 23,11 @@ int      checkCollision        (int x, int y, int z);
bool     worldIsLoaded         (int x, int y, int z);
void     worldSetChunkUpdated  (int x, int y, int z);
bool     worldShouldBeLoaded   (const vec cpos);

u8       worldTryFluid         (int x, int y, int z);
u8       worldGetFluid         (int x, int y, int z);
bool     worldSetFluid         (int x, int y, int z, u8 level);

u8       worldTryFire          (int x, int y, int z);
u8       worldGetFire          (int x, int y, int z);
bool     worldSetFire          (int x, int y, int z, u8 strength);

M client/src/voxel/chungus.c => client/src/voxel/chungus.c +0 -1
@@ 104,7 104,6 @@ void chungusFree(chungus *c){
	if(c == NULL){return;}
	animalDelChungus(c);
	itemDropDelChungus(c);
	fireDelChungus(c);
	throwableDelChungus(c);
	for(int x=0;x<16;x++){
	for(int y=0;y<16;y++){

M client/src/voxel/chunk.c => client/src/voxel/chunk.c +4 -0
@@ 482,6 482,10 @@ void chunkRecvUpdate(const packet *p){
		if(chnk->fluid == NULL){chnk->fluid = chunkOverlayAllocate();}
		dest = &chnk->fluid->data[0][0][0];
		break;
	case chunkOverlayFire:
		if(chnk->fire == NULL){chnk->fire = chunkOverlayAllocate();}
		dest = &chnk->fire->data[0][0][0];
		break;
	}
	memcpy(dest,p->v.u8,sizeof(chnk->block->data));
	chnk->flags |= CHUNK_FLAG_DIRTY;

M client/src/voxel/chunk.h => client/src/voxel/chunk.h +1 -1
@@ 14,7 14,7 @@ struct chunk {
	struct chunkvertbuf *vertbuf;
	beingList bl;

	chunkOverlay *fluid, *block;
	chunkOverlay *fluid, *block, *fire;
};

void    chunkInit                  ();

M common/src/game/animal.c => common/src/game/animal.c +2 -3
@@ 561,9 561,8 @@ void animalCheckBurnAll(){
	static uint calls = 0;
	for(uint i=(calls&0x7F);i<animalListMax;i+=0x80){
		animal *a = &animalList[i];
		fire *f = fireGetAtPos(a->pos.x,a->pos.y,a->pos.z);
		if(f == NULL)       {continue;}
		if(f->strength < 64){continue;}
		const u8 f = worldGetFire(a->pos.x,a->pos.y,a->pos.z);
		if(f < 16){continue;}
		a->health--;
		animalRBurn(a);
	}

M common/src/game/being.c => common/src/game/being.c +0 -26
@@ 21,7 21,6 @@
#include "../game/being.h"
#include "../game/character.h"
#include "../game/entity.h"
#include "../game/fire.h"
#include "../game/grenade.h"
#include "../game/hook.h"
#include "../game/itemDrop.h"


@@ 71,10 70,6 @@ being beingItemDrop  (u32 id){
	return beingNew(BEING_ITEMDROP,  id);
}

being beingFire      (u32 id){
	return beingNew(BEING_FIRE,      id);
}

being beingThrowable (u32 id){
	return beingNew(BEING_THROWABLE, id);
}


@@ 104,10 99,6 @@ vec beingGetPos(being b){
		if(c == NULL)     {return vecNOne();}
		if(c->ent == NULL){return vecNOne();}
		return c->ent->pos; }
	case BEING_FIRE: {
		fire *c = fireGetByBeing(b);
		if(c == NULL)     {return vecNOne();}
		return vecNew(c->x,c->y,c->z); }
	default:
		return vecNOne();
	}


@@ 141,13 132,6 @@ void beingSetPos(being b, const vec pos){
		if(c == NULL){return;}
		c->ent->pos = pos;
		return; }
	case BEING_FIRE: {
		fire *c = fireGetByBeing(b);
		if(c == NULL){return;}
		c->x = pos.x;
		c->y = pos.y;
		c->z = pos.z;
		return; }
	default:
		return;
	}


@@ 180,13 164,6 @@ void beingAddPos(being b, const vec pos){
		if(c == NULL){return;}
		c->ent->pos = vecAdd(c->ent->pos,pos);
		return; }
	case BEING_FIRE: {
		fire *c = fireGetByBeing(b);
		if(c == NULL){return;}
		c->x += pos.x;
		c->y += pos.y;
		c->z += pos.z;
		return; }
	default:
		return;
	}


@@ 214,7 191,6 @@ vec beingGetVel(being b){
		itemDrop *c = itemDropGetByBeing(b);
		if(c == NULL){return vecZero();}
		return c->ent->vel; }
	case BEING_FIRE:
	default:
		return vecZero();
	}


@@ 247,7 223,6 @@ void beingSetVel(being b, const vec vel){
		if(c == NULL){return;}
		c->ent->vel = vel;
		return; }
	case BEING_FIRE:
	default:
		return;
	}


@@ 280,7 255,6 @@ void beingAddVel(being b, const vec vel){
		if(c == NULL){return;}
		c->ent->vel = vecAdd(c->ent->vel,vel);
		return; }
	case BEING_FIRE:
	default:
		return;
	}

M common/src/game/being.h => common/src/game/being.h +0 -1
@@ 10,7 10,6 @@ being beingHook      (u32 id);
being beingGrenade   (u32 id);
being beingProjectile(u32 id);
being beingItemDrop  (u32 id);
being beingFire      (u32 id);
being beingThrowable (u32 id);

vec   beingGetPos    (being b);

A common/src/game/blockMining.h => common/src/game/blockMining.h +4 -0
@@ 0,0 1,4 @@
#pragma once
#include "../common.h"

void blockMiningBurnBlock(int x, int y, int z, blockId b);

M common/src/game/blockType.c => common/src/game/blockType.c +38 -38
@@ 61,16 61,16 @@ int blockTypeGetFireHealth(blockId b){
	return blocks[b].firehp;
}
int blockTypeGetFireDamage(blockId b){
	if(b == I_Grass){return 4;}
	if(b == I_Coal) {return 4;}
	if(b == I_Grass){return 2;}
	if(b == I_Coal) {return 2;}

	switch(blockTypeGetCat(b)){
	default:
		return 1;
		return 0;
	case WOOD:
		return 4;
		return 2;
	case LEAVES:
		return 6;
		return 3;
	}
}



@@ 100,42 100,42 @@ bool blockTypeValid(blockId b){
}

void blockTypeInit(){
	blockTypeInitBlock    ( 1, 1, DIRT,  "Dirt", 500, 1000, 1.5f, 0xFF0A234F,0xFF051B45, 0xF, 0x3FF);
	blockTypeInitBlock    ( 2, 0, DIRT,  "Grass", 240,  400, 1.7f, 0xFF004227,0xFF051B45, 0x1F, 0x3FF);
	blockTypeInitBlock    ( 3, 2, STONE, "Stone", 1200, 2000, 5.f, 0xFF5E5E5E,0xFF484848, 0xFFFF, 0xFFF);
	blockTypeInitBlock    ( 4, 3, STONE, "Coal", 800, 3000, 4.f, 0xFF262626,0xFF101010, 0xFFFF, 0xFFF);
	blockTypeInitBlock    ( 5, 4, WOOD,  "Spruce Log", 500, 800, 3.f, 0xFF051B25,0xFF07161D, 0xFFFF, 0xFFFF);
	blockTypeInitBlock    ( 6, 5, LEAVES,"Spruce Leafes",  60,  400, 0.2f, 0xFF012C12,0xFF01250F, 0, 0x1F);
	blockTypeInitBlock    ( 7, 7, WOOD,  "Roots", 480,  480, 1.5f, 0xFF14323E,0xFF0D2029, 0x7, 0xFFFF);
	blockTypeInitBlock    ( 8, 6, LEAVES,"Dry Grass", 240, 1000, 1.6f, 0xFF11644B,0xFF007552, 0x1F, 0x3FF);
	blockTypeInitBlock    ( 9, 8, STONE, "Obsidian",      2000, 8000, 10.f, 0xFF222222,0xFF171717, 0xFFFF, 0xFFF);
	blockTypeInitBlock    (10, 9, WOOD,  "Oak Log",        600,  800, 3.f, 0xFF082C3C,0xFF08242E, 0xFFFF, 0xFFFF);
	blockTypeInitBlock    (11,10, LEAVES,"Oak Leaves",     70,  440, 0.2f, 0xFF004227,0xFF003318, 0, 0x1F);
	blockTypeInitBlock    (12,12, STONE, "Marble Block",  1600, 8000, 8.f, 0xFFF0F0F0,0xFFEBEBEB, 0xFFFF, 0xFFF);
	blockTypeInitBlock    (13,11, STONE, "Hematite Ore",  1100, 4000, 6.f, 0xFF5B5B72,0xFF5E5E5E, 0xFFFF, 0xFFF);

	blockTypeInitBlock    (14,13, STONE, "Marble Pillar", 1600, 8000, 8.f, 0xFFF0F0F0,0xFFEBEBEB, 0xFFFF, 0xFFF);
	blockTypeInitBlock    ( 1, 1, DIRT,  "Dirt", 500, 100, 1.5f, 0xFF0A234F,0xFF051B45, 0xF, 0x3FF);
	blockTypeInitBlock    ( 2, 0, DIRT,  "Grass", 240,  40, 1.7f, 0xFF004227,0xFF051B45, 0x1F, 0x3FF);
	blockTypeInitBlock    ( 3, 2, STONE, "Stone", 1200, 255, 5.f, 0xFF5E5E5E,0xFF484848, 0xFFFF, 0xFFF);
	blockTypeInitBlock    ( 4, 3, STONE, "Coal", 800, 250, 4.f, 0xFF262626,0xFF101010, 0xFFFF, 0xFFF);
	blockTypeInitBlock    ( 5, 4, WOOD,  "Spruce Log", 500, 80, 3.f, 0xFF051B25,0xFF07161D, 0xFFFF, 0xFFFF);
	blockTypeInitBlock    ( 6, 5, LEAVES,"Spruce Leafes",  80,  40, 0.2f, 0xFF012C12,0xFF01250F, 0, 0x1F);
	blockTypeInitBlock    ( 7, 7, WOOD,  "Roots", 480,  40, 1.5f, 0xFF14323E,0xFF0D2029, 0x7, 0xFFFF);
	blockTypeInitBlock    ( 8, 6, LEAVES,"Dry Grass", 240, 80, 1.6f, 0xFF11644B,0xFF007552, 0x1F, 0x3FF);
	blockTypeInitBlock    ( 9, 8, STONE, "Obsidian",      2000, 250, 10.f, 0xFF222222,0xFF171717, 0xFFFF, 0xFFF);
	blockTypeInitBlock    (10, 9, WOOD,  "Oak Log",        600,  80, 3.f, 0xFF082C3C,0xFF08242E, 0xFFFF, 0xFFFF);
	blockTypeInitBlock    (11,10, LEAVES,"Oak Leaves",     70,  44, 0.2f, 0xFF004227,0xFF003318, 0, 0x1F);
	blockTypeInitBlock    (12,12, STONE, "Marble Block",  1600, 255, 8.f, 0xFFF0F0F0,0xFFEBEBEB, 0xFFFF, 0xFFF);
	blockTypeInitBlock    (13,11, STONE, "Hematite Ore",  1100, 160, 6.f, 0xFF5B5B72,0xFF5E5E5E, 0xFFFF, 0xFFF);

	blockTypeInitBlock    (14,13, STONE, "Marble Pillar", 1600, 250, 8.f, 0xFFF0F0F0,0xFFEBEBEB, 0xFFFF, 0xFFF);
	blockTypeSetTex       (14,sideTop,12);
	blockTypeSetTex       (14,sideBottom,12);

	blockTypeInitBlock    (15,14, STONE, "Marble Blocks",1600, 8000, 8.f,   0xFFF0F0F0,0xFFEBEBEB, 0xFFFF, 0xFFF);
	blockTypeInitBlock    (16,24, LEAVES,"Acacia Leafes",  70,  600, 0.2f,  0xFF003002,0xFF1c6f32, 0, 0x1F);
	blockTypeInitBlock    (17,17, WOOD,  "Boards",        400,  600, 2.5f,  0xFF09678f,0xFF1380af, 0x1F, 0xFFF);
	blockTypeInitBlock    (18,18, STONE, "Crystals",     2500, 8000, 2.f,   0xFF997CE8,0xFF4D25B5, 0xFFFF, 0xFFF);
	blockTypeInitBlock    (19,19, LEAVES,"Sakura Leafes",  70,  420, 0.2f,  0xFF997CE8,0xFF4D25B5, 0, 0x1F);
	blockTypeInitBlock    (20,20, WOOD,  "Birch Log",     500,  800, 3.f,   0xFF525255,0xFF525555, 0xFFFF, 0xFFFF);
	blockTypeInitBlock    (21,21, LEAVES,"Flower Bush",    90,  640, 0.3f,  0xFF004227,0xFF003318, 0, 0x1F);
	blockTypeInitBlock    (22,23, LEAVES,"Date Bush",      80,  840, 0.25f, 0xFF00334f,0xFF128394, 0, 0x1F);

	blockTypeInitBlock    (23,26, DIRT,  "Snow Dirt",  500, 2000, 1.5f,           0xFF0A234F,0xFF051B45, 0xF, 0x3FF);
	blockTypeInitBlock    (24,25, DIRT,  "Snow Grass", 240, 1000, 1.7f,           0xFF004227,0xFF051B45, 0x1F, 0x3FF);
	blockTypeInitBlock    (25,27, DIRT,  "Snowy Spruce Leafes",  60, 800, 0.3f,   0xFF012C12,0xFF01250F, 0, 0x1F);
	blockTypeInitBlock    (26,28, DIRT,  "Snowy Oak Leaves",     70, 880, 0.3f,   0xFF004227,0xFF003318, 0, 0x1F);
	blockTypeInitBlock    (27,29, DIRT,  "Snowy Flower Bush",    90, 1240, 0.4f,  0xFF004227,0xFF003318, 0, 0x1F);
	blockTypeInitBlock    (28,30, DIRT,  "Snowy Date Bush",      80, 1640, 0.35f, 0xFF00334f,0xFF128394, 0, 0x1F);
	blockTypeInitBlock    (29,31, DIRT,  "Snowy Acacia Leafes",  70, 1200, 0.3f,  0xFF003002,0xFF1c6f32, 0, 0x1F);
	blockTypeInitBlock    (30,32, WOOD,  "Snowy Roots", 480,  680, 1.8f,          0xFF14323E,0xFF0D2029, 7, 0xFFFF);
	blockTypeInitBlock    (31,33, DIRT,  "Snowy Sakura Leafes", 70,  820, 0.2f,   0xFF997CE8,0xFF4D25B5, 0, 0x1F);
	blockTypeInitBlock    (15,14, STONE, "Marble Blocks",1600, 250, 8.f,   0xFFF0F0F0,0xFFEBEBEB, 0xFFFF, 0xFFF);
	blockTypeInitBlock    (16,24, LEAVES,"Acacia Leafes",  70,  60, 0.2f,  0xFF003002,0xFF1c6f32, 0, 0x1F);
	blockTypeInitBlock    (17,17, WOOD,  "Boards",        400, 100, 2.5f,  0xFF09678f,0xFF1380af, 0x1F, 0xFFF);
	blockTypeInitBlock    (18,18, STONE, "Crystals",     2500, 250, 2.f,   0xFF997CE8,0xFF4D25B5, 0xFFFF, 0xFFF);
	blockTypeInitBlock    (19,19, LEAVES,"Sakura Leafes",  70,  42, 0.2f,  0xFF997CE8,0xFF4D25B5, 0, 0x1F);
	blockTypeInitBlock    (20,20, WOOD,  "Birch Log",     500,  80, 3.f,   0xFF525255,0xFF525555, 0xFFFF, 0xFFFF);
	blockTypeInitBlock    (21,21, LEAVES,"Flower Bush",    90,  64, 0.3f,  0xFF004227,0xFF003318, 0, 0x1F);
	blockTypeInitBlock    (22,23, LEAVES,"Date Bush",      80,  84, 0.25f, 0xFF00334f,0xFF128394, 0, 0x1F);

	blockTypeInitBlock    (23,26, DIRT,  "Snow Dirt",  500, 200, 1.5f,           0xFF0A234F,0xFF051B45, 0xF, 0x3FF);
	blockTypeInitBlock    (24,25, DIRT,  "Snow Grass", 240, 100, 1.7f,           0xFF004227,0xFF051B45, 0x1F, 0x3FF);
	blockTypeInitBlock    (25,27, DIRT,  "Snowy Spruce Leafes",  60, 80, 0.3f,   0xFF012C12,0xFF01250F, 0, 0x1F);
	blockTypeInitBlock    (26,28, DIRT,  "Snowy Oak Leaves",     70, 80, 0.3f,   0xFF004227,0xFF003318, 0, 0x1F);
	blockTypeInitBlock    (27,29, DIRT,  "Snowy Flower Bush",    90, 124, 0.4f,  0xFF004227,0xFF003318, 0, 0x1F);
	blockTypeInitBlock    (28,30, DIRT,  "Snowy Date Bush",      80, 164, 0.35f, 0xFF00334f,0xFF128394, 0, 0x1F);
	blockTypeInitBlock    (29,31, DIRT,  "Snowy Acacia Leafes",  70, 120, 0.3f,  0xFF003002,0xFF1c6f32, 0, 0x1F);
	blockTypeInitBlock    (30,32, WOOD,  "Snowy Roots", 480,  68, 1.8f,          0xFF14323E,0xFF0D2029, 7, 0xFFFF);
	blockTypeInitBlock    (31,33, DIRT,  "Snowy Sakura Leafes", 70,  82, 0.2f,   0xFF997CE8,0xFF4D25B5, 0, 0x1F);

	blockTypeGenMeshes();
}

M common/src/game/fire.c => common/src/game/fire.c +88 -84
@@ 15,107 15,111 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "fire.h"

#include "../game/being.h"
#include "../game/item.h"
#include "../network/messages.h"
#include "./blockType.h"
#include "./blockMining.h"
#include "./item.h"
#include "../misc/effects.h"
#include "../world/world.h"

#include <stdlib.h>

fire fireList[1<<14];
uint fireCount = 0;

void fireInit(){
	fireCount = 0;
int fireSpreadTo(int f, int x, int y, int z){
	if(f <= 0){return f;}
	const u8 db = worldGetB(x,y,z);
	if(!db){return f;}
	const int df = worldGetFire(x,y,z);
	if(f <= df){return f;}
	if(rngValA(0xFF) > (uint)f){return f;}
	const int ndf = MIN(255,df + f / 16);
	const int diff = ndf - df;
	worldSetFire(x,y,z,ndf);
	return f - diff;
}

void fireEmptyUpdate(uint c){
	msgFireUpdate(c,0,0,0,0,0,0);
}
int fireTick(chunkOverlay *f, chunkOverlay *fluid, chunkOverlay *block, int cx, int cy, int cz){
	int blocksLit = 0;
	for(int x=0;x<CHUNK_SIZE;x++){
	for(int y=0;y<CHUNK_SIZE;y++){
	for(int z=0;z<CHUNK_SIZE;z++){
		int curLevel = f->data[x][y][z];
		if(!curLevel){continue;}
		blocksLit++;
		curLevel--;

		if(block){
			const u8 b = block->data[x][y][z];
			if(b){
				curLevel += blockTypeGetFireDamage(b);
				if(curLevel >= blockTypeGetFireHealth(b)){
					blockMiningBurnBlock(cx+x,cy+y,cz+z,b);
				}
			}
		}
		if(fluid){
			const u8 fluidLevel = fluid->data[x][y][z] & 0xF0;
			if(fluidLevel){
				const int newLevel = MAX(0,curLevel - fluidLevel);
				const int diff     = curLevel - newLevel;
				const int newFluid = (fluidLevel - diff) | fluid->data[x][y][z];
				fluid->data[x][y][z] = newFluid > 0xF ? newFluid : 0;
				fxFluidVapor(cx+x,cy+y,cz+z,fluid->data[x][y][z] & 0xF, diff);
				curLevel = newLevel;
			}
		}
		if(curLevel > 16){
			curLevel = fireSpreadTo(curLevel,cx+x,cy+y+1,cz+z);
		}
		if(curLevel > 32){
			switch(rngValA(3)){
			case 0:
				curLevel = fireSpreadTo(curLevel,cx+x-1,cy+y,cz+z);
				break;
			case 1:
				curLevel = fireSpreadTo(curLevel,cx+x+1,cy+y,cz+z);
				break;
			case 2:
				curLevel = fireSpreadTo(curLevel,cx+x,cy+y,cz+z-1);
				break;
			case 3:
				curLevel = fireSpreadTo(curLevel,cx+x,cy+y,cz+z+1);
				break;
			}
		}
		if(curLevel > 48){
			curLevel = fireSpreadTo(curLevel,cx+x,cy+y-1,cz+z);
		}

void fireSendUpdate(uint c, uint i){
	fire *f = &fireList[i];
	msgFireUpdate(c,i,fireCount,f->x,f->y,f->z,f->strength);
		f->data[x][y][z] = curLevel;
	}
	}
	}
	return blocksLit;
}

void fireBox(u16 x, u16 y, u16 z, u16 w, u16 h, u16 d, int strength){
void fireBox(u16 x, u16 y, u16 z, u16 w, u16 h, u16 d, u8 strength){
	for(int cx = x;cx < x+w;cx++){
	for(int cy = y;cy < y+h;cy++){
	for(int cz = z;cz < z+d;cz++){
		const blockId b = worldGetB(cx,cy,cz);
		if(b == 0){continue;}
		fireNew(cx,cy,cz,strength);
	}
		worldSetFire(cx,cy,cz,strength);
	}
	}
}

void fireDel(uint i){
	if(i >= fireCount){return;}
	const int  m   = fireCount - 1;
	beingList *ibl = fireList[i].bl;
	beingList *mbl = fireList[m].bl;
	being      ib  = fireGetBeing(&fireList[i]);
	being      mb  = fireGetBeing(&fireList[m]);

	beingListDel(ibl,ib);
	beingListDel(mbl,mb);
	fireList[i] = fireList[m];
	fireList[i].bl = beingListUpdate(NULL,ib);
	fireCount--;
}

void fireDelChungus(const chungus *c){
	if(c == NULL){return;}
	const vec cp = chungusGetPos(c);
	for(uint i=fireCount-1;i<fireCount;i--){
		const fire *f = &fireList[i];
		if((f->x >> 8) != cp.x){continue;}
		if((f->y >> 8) != cp.y){continue;}
		if((f->z >> 8) != cp.z){continue;}
		fireDel(i);
	}
}

fire *fireGetAtPos(u16 x, u16 y, u16 z){
	beingList *bl = beingListGet(x,y,z);
	if(bl == NULL){return NULL;}
	for(beingListEntry *ble = bl->first; ble != NULL; ble = ble->next){
		for(uint i=0;i<countof(ble->v);i++){
			if(beingType(ble->v[i]) != BEING_FIRE){continue;}
			fire *t = fireGetByBeing(ble->v[i]);
			if(t == NULL){continue;}
			if(t->x != x){continue;}
			if(t->y != y){continue;}
			if(t->z != z){continue;}
			return t;
void fireBoxExtinguish(u16 x, u16 y, u16 z, u16 w, u16 h, u16 d, u8 strength){
	(void)strength;
	for(int cx = x;cx < x+w;cx++){
	for(int cy = y;cy < y+h;cy++){
	for(int cz = z;cz < z+d;cz++){
		if(!isClient){
			const blockId b = worldTryB(cx,cy,cz);
			if((b == I_Dry_Grass) && (rngValA(1) == 0)){
				worldSetB(cx,cy,cz,I_Grass);
			}
			if(!b){
				worldSetFluid(cx,cy,cz,0xF0);
			}
		}
	}
	return NULL;
}

fire *fireGetByBeing(being b){
	if(beingType(b) != BEING_FIRE){return NULL;}
	uint i = beingID(b);
	if(i >= fireCount){return NULL;}
	return &fireList[i];
}

being fireGetBeing(const fire *f){
	if(f == NULL){return 0;}
	return beingFire(f - fireList);
}

int fireHitCheck(const vec pos, float mdd, int dmg, int cause, u16 iteration, being source){
	(void)mdd;
	(void)cause;
	(void)iteration;
	(void)source;
	fire *f = fireGetAtPos(pos.x,pos.y,pos.z);
	if(f != NULL){
		f->strength -= dmg*4;
		return 1;
	}
	return 0;
	}
}

M common/src/game/fire.h => common/src/game/fire.h +4 -17
@@ 1,19 1,6 @@
#pragma once
#include "../../../common/src/common.h"
#include "../common.h"

extern fire fireList[1<<14];
extern uint fireCount;

void fireInit          ();
void fireNew           (u16 x, u16 y, u16 z, i16 strength);
void fireDel           (uint i);
void fireDelChungus    (const chungus *c);
fire *fireGetAtPos     (u16 x,u16 y, u16 z);
void fireBox           (u16 x, u16 y, u16 z, u16 w, u16 h, u16 d, int strength);
void fireBoxExtinguish (u16 x, u16 y, u16 z, u16 w, u16 h, u16 d, int strength);
void fireSendUpdate    (uint c, uint i);
void fireEmptyUpdate   (uint c);

fire *fireGetByBeing   (being b);
being fireGetBeing     (const fire *f);
int fireHitCheck       (const vec pos, float mdd, int dmg, int cause, u16 iteration, being source);
int  fireTick          (chunkOverlay *fire, chunkOverlay *fluid, chunkOverlay *block, int x, int y, int z);
void fireBox           (u16 x, u16 y, u16 z, u16 w, u16 h, u16 d, u8 strength);
void fireBoxExtinguish (u16 x, u16 y, u16 z, u16 w, u16 h, u16 d, u8 strength);

M common/src/game/projectile.c => common/src/game/projectile.c +1 -6
@@ 171,12 171,7 @@ static inline int projectileUpdate(projectile *p){
	if(characterHitCheck (p->pos, mdd, 1, 3, iteration, p->source)){return 1;}
	if(animalHitCheck    (p->pos, mdd, 1, 3, iteration, p->source)){return 1;}
	const blockId b = worldGetB(p->pos.x,p->pos.y,p->pos.z);
	if(p->style == 6){
		if(b){return 1;}
		if(fireHitCheck(p->pos, mdd, 1, 3, iteration, p->source)){return 1;}
	}else if(b){
		return projectileBounce(p,b);
	}
	if(b){return projectileBounce(p,b);}
	if(p->target != 0){return projectileHomeIn(p);}
	return 0;
}

M common/src/game/weather/rain.c => common/src/game/weather/rain.c +1 -1
@@ 102,7 102,7 @@ void rainUpdateAll(){
				if(isClient){
					fxRainDrop(vecNew(glrd->x,glrd->y,glrd->z));
				}else{
					fireBoxExtinguish(glrd->x, glrd->y, glrd->z, 1, 1, 1, 256);
					fireBoxExtinguish(glrd->x, glrd->y, glrd->z, 1, 1, 1, 255);
				}
				rainDel(i);
				continue;

M common/src/game_structs.h => common/src/game_structs.h +2 -9
@@ 51,7 51,8 @@ typedef u8 blockId;

typedef enum chunkOverlayType {
	chunkOverlayBlock = 0,
	chunkOverlayFluid
	chunkOverlayFluid,
	chunkOverlayFire
} chunkOverlayType;

typedef struct {


@@ 292,14 293,6 @@ typedef struct {
} projectile;

typedef struct {
	u16 x,y,z;
	i16 strength;
	i16 blockDmg;
	i16 oxygen;
	beingList *bl;
} fire;

typedef struct {
	entity *ent;
	int ticksLeft,cluster;
	float pwr,clusterPwr;

M common/src/misc/effects.h => common/src/misc/effects.h +1 -0
@@ 2,3 2,4 @@
#include "../common.h"

void fxBlockBreak (const vec pos, u8 b, u8 cause);
void fxFluidVapor (int cx, int cy, int cz, int fluidType, int amountLost);

M common/src/network/messages.c => common/src/network/messages.c +0 -16
@@ 445,20 445,6 @@ void msgFxBeamBlastHit(int c, const vec pos, u16 size, u16 style){
	packetQueue(p,msgtFxProjectileHit,4*4,c);
}

void msgFireUpdate(int c, u16 i, u16 count, u16 x, u16 y, u16 z, i16 strength){
	packet *p = &packetBuffer;

	p->v.u16[0] = i;
	p->v.u16[1] = count;

	p->v.u16[2] = x;
	p->v.u16[3] = y;
	p->v.u16[4] = z;
	p->v.i16[5] = strength;

	packetQueue(p,msgtFireRecvUpdate,6*2,c);
}

void msgLispSExpr(int c, const char *str){
	packet *p  = &packetBuffer;
	int len    = strnlen(str,4192);


@@ 568,8 554,6 @@ const char *networkGetMessageName(uint i){
		return "projectileUpdate";
	case msgtFxProjectileHit:
		return "fxProjectileHit";
	case msgtFireRecvUpdate:
		return "fireRecvUpdate";
	case msgtLispRecvSExpr:
		return "lispRecvSExpr";
	case msgtLightningStrike:

M common/src/network/messages.h => common/src/network/messages.h +0 -1
@@ 45,7 45,6 @@ void msgPlayerSetEquipment       (int c, const item *itm, size_t itemCount);
void msgItemDropPickup           (int c, uint i);
void msgItemDropBounce           (int c, uint i);
void msgRopeUpdate               (int c, uint i, rope *r);
void msgFireUpdate               (int c, u16 i, u16 count, u16 x, u16 y, u16 z, i16 strength);
void msgWaterUpdate              (int c, u16 i, u16 count, u16 x, u16 y, u16 z, i16 strength);
void msgLispSExpr                (int c, const char *str);
void msgLightningStrike          (int c, u16 lx, u16 ly, u16 lz, u16 tx, u16 ty, u16 tz, u16 seed);

M common/src/network/packet.h => common/src/network/packet.h +0 -1
@@ 98,7 98,6 @@ typedef enum {
	msgtProjectileUpdate,
	msgtFxProjectileHit,
	msgtLightningStrike,
	msgtFireRecvUpdate,
	msgtLispRecvSExpr,
	msgtWeatherRecvUpdate,
	msgtRainRecvUpdate,

M common/src/nuj/items.nuj => common/src/nuj/items.nuj +2 -1
@@ 84,7 84,8 @@
]]

[def item-burn-up [λ [pos id amount] "Calls the specific burn up handler that AMOUNT ID items have burned up at POS"
        [cond [[procedure? [[items [- id 256]] burn-up]] [[[items [- id 256]] burn-up] pos id amount]]
     [cond [[procedure? [items [- id 256]] burn-up] [[[items [- id 256]] burn-up]]
                                                    [[[items [- id 256]] burn-up] pos id amount]]
               [#t #f]
        ]
]]

M common/src/world/world.h => common/src/world/world.h +8 -3
@@ 6,9 6,6 @@ void     worldBoxSphere     (int x, int y, int z, int r, u8 block);
blockId  worldTryB          (int x, int y, int z);
blockId  worldGetB          (int x, int y, int z);
bool     worldSetB          (int x, int y, int z, blockId block);
u8       worldTryFluid      (int x, int y, int z);
u8       worldGetFluid      (int x, int y, int z);
bool     worldSetFluid      (int x, int y, int z, u8 level);
int      checkCollision     (int x, int y, int z);
chungus *worldTryChungus    (int x, int y, int z);
chungus *worldGetChungus    (int x, int y, int z);


@@ 21,3 18,11 @@ void     worldBreak         (int x, int y, int z);
void     worldBoxMine       (int x, int y, int z, int w,int h,int d);
void     worldBoxMineSphere (int x, int y, int z, int r);
bool     worldShouldBeLoaded(const vec cpos);

u8       worldTryFluid      (int x, int y, int z);
u8       worldGetFluid      (int x, int y, int z);
bool     worldSetFluid      (int x, int y, int z, u8 level);

u8       worldTryFire       (int x, int y, int z);
u8       worldGetFire       (int x, int y, int z);
bool     worldSetFire       (int x, int y, int z, u8 strength);

M server/src/game/being.c => server/src/game/being.c +0 -3
@@ 45,9 45,6 @@ void beingSync(u8 c, being b){
	case BEING_ITEMDROP:
		itemDropUpdateMsg(c,beingID(b));
		return;
	case BEING_FIRE:
		fireSendUpdate(c,beingID(b));
		return;
	}
}


M server/src/game/blockMining.h => server/src/game/blockMining.h +0 -1
@@ 1,7 1,6 @@
#pragma once
#include "../../../common/src/common.h"

void blockMiningBurnBlock    (int x, int y, int z, blockId b);
void blockMiningDropItemsPos (int x, int y, int z, blockId b);
void blockMiningMineBlock    (int x, int y, int z, u8 cause);
int  blockMiningMinePos      (int dmg, int x, int y, int z);

M server/src/game/fire.c => server/src/game/fire.c +17 -198
@@ 25,215 25,34 @@
#include "../voxel/bigchungus.h"
#include "../voxel/chungus.h"
#include "../../../common/src/game/blockType.h"
#include "../../../common/src/game/chunkOverlay.h"
#include "../../../common/src/game/item.h"
#include "../../../common/src/misc/misc.h"
#include "../../../common/src/misc/profiling.h"

#include <stdio.h>

int windFireOffX = 0;
int windFireOffY = 0;
int windFireOffZ = 0;

void fireNewF(u16 x, u16 y, u16 z, i16 strength, i16 blockDmg, i16 oxygen){
	fire *f = NULL;
	if(fireCount < countof(fireList)){
		f = &fireList[fireCount++];
	}else{
		f = &fireList[rngValM(countof(fireList))];
	}

	f->x = x;
	f->y = y;
	f->z = z;
	f->strength = strength;
	f->blockDmg = blockDmg;
	f->oxygen   = oxygen;
	fireSendUpdate(-1,(int)(f - fireList));
	f->bl = beingListUpdate(f->bl,fireGetBeing(f));
}

void fireNew(u16 x, u16 y, u16 z, i16 strength){
	if(!inWorld(x,y,z)){return;}
	fire *f = fireGetAtPos(x,y,z);
	if(f == NULL){
		if(fireCount < countof(fireList)){
			f = &fireList[fireCount++];
		}else{
			f = &fireList[rngValM(countof(fireList))];
		}
		f->x = x;
		f->y = y;
		f->z = z;
		f->strength = 0;
		f->blockDmg = 0;
		f->oxygen   = 8;
		f->bl = beingListUpdate(NULL,fireGetBeing(f));
	}
	f->strength = MAX(f->strength,MIN(1024,f->strength+strength));
	fireSendUpdate(-1,(int)(f - fireList));
}

void fireRecvUpdate(uint c, const packet *p){
	(void)c;
	const u16 x        = p->v.u16[2];
	const u16 y        = p->v.u16[3];
	const u16 z        = p->v.u16[4];
	const i16 strength = p->v.i16[5];
	fireNew(x,y,z,strength);
}

static inline void fireSpreadToBlock(fire *f, int r,blockCategory ccat){
	u16 fx,fy,fz;
	fx = (f->x - r) + rngValM(r*2+1) + windFireOffX;
	fy = (f->y - r) + rngValM(r*2+1) + windFireOffY;
	fz = (f->z - r) + rngValM(r*2+1) + windFireOffZ;

	if((fy < f->y) && (rngValR() & 1)){
		fy = fy + (f->y-fy);
	}
	const blockId nb = worldGetB(fx,fy,fz);
	if(ccat != 0){
		blockCategory nbt = blockTypeGetCat(nb);
		if(nbt != ccat){return;}
	}
	if(nb == 0){return;}
	fireNew(fx,fy,fz,8);
	f->strength -= 8;
}

static inline void fireSpread(fire *f){
	int count = f->strength >> 7;
	for(int i=0;i<count;i++){
		if(rngValA(0x1F)){continue;}
		int r = 1;
		if((f->strength > 0xFF) && ((rngValR() & 0x07) == 0)){r = 2;}
		fireSpreadToBlock(f,r,0);
	}
	count = f->strength >> 6;
	for(int i=0;i<count+1;i++){
		if(rngValA(0x1F)){continue;}
		int r = 1;
		const uint rv = (rngValR() & 0x07);
		if(     (f->strength > 0x1F) && (rv != 0)){r = 2;}
		else if((f->strength > 0xFF) && (rv == 0)){r = 3;}
		fireSpreadToBlock(f,r,LEAVES);
	}
}

void fireUpdate(fire *f){
	if(f == NULL){return;}
	f->strength -= 2;
	if(f->strength <= 0){
		fireDel(f-fireList);
		fireSendUpdate(-1,fireCount);
		fireSendUpdate(-1,f-fireList);
		return;
	}

	int airB = 0;
	for(int x = -1;x < 2;x++){
	for(int y = -1;y < 2;y++){
	for(int z = -1;z < 2;z++){
		if(worldGetB(f->x+x,f->y+y,f->z+z) == 0){airB++;}
	}
	}
	}

	const blockId b = worldGetB(f->x,f->y,f->z);
	const int   dmg = MIN(f->oxygen,blockTypeGetFireDamage(b));

	f->strength = MIN(30000,f->strength+dmg-1);
	f->oxygen  -= dmg;
	f->oxygen  += MIN(airB,64-f->oxygen);

	if(f->strength <= 0){
		fireDel(f-fireList);
		fireSendUpdate(-1,fireCount);
		fireSendUpdate(-1,f-fireList);
		return;
	}

	if(b == 0){
		f->blockDmg = 0;
		fireSpread(f);
	}else{
		const int maxhp = blockTypeGetFireHealth(b);
		f->blockDmg = MIN(maxhp,f->blockDmg + dmg);
		if(f->blockDmg >= maxhp){
			blockMiningBurnBlock(f->x,f->y,f->z,b);
			f->blockDmg = 0;
		}
	}
	if(f->strength > 8192){
		f->strength -= 1024;
		explode(vecAdd(vecNew(f->x,f->y,f->z),vecMulS(vecRng(),4.f)), 0.5f, 0);
	}else if(f->strength > 2048){
		f->strength -= 512;
		explode(vecAdd(vecNew(f->x,f->y,f->z),vecMulS(vecRng(),3.f)), 0.3f, 0);
	}

	fireSpread(f);
}

static void fireCalcWindOff(){
	windFireOffX = 0;
	windFireOffY = 0;
	windFireOffZ = 0;
	if(windVel.x >  0.0001){windFireOffX++;}
	if(windVel.y >  0.0001){windFireOffY++;}
	if(windVel.z >  0.0001){windFireOffZ++;}
	if(windVel.x < -0.0001){windFireOffX--;}
	if(windVel.y < -0.0001){windFireOffY--;}
	if(windVel.z < -0.0001){windFireOffZ--;}
}

void fireUpdateAll(){
	static uint calls = 0;
	static int calls = 0;
	PROFILE_START();
	fireCalcWindOff();

	for(uint i=(calls&0x1F);i<fireCount;i+=0x20){
		fireUpdate(&fireList[i]);
	}
	calls++;

	PROFILE_STOP();
}

void fireSyncPlayer(uint c){
	if(fireCount == 0){
		fireEmptyUpdate(c);
		return;
	}

	const int count = 16;
	const uint max = MIN(fireCount,clients[c].fireUpdateOffset+count);
	for(;clients[c].fireUpdateOffset<max;clients[c].fireUpdateOffset++){
		fireSendUpdate(c,clients[c].fireUpdateOffset);
	}
	if(clients[c].fireUpdateOffset >= fireCount){clients[c].fireUpdateOffset = 0;}
}

void fireBoxExtinguish(u16 x, u16 y, u16 z, u16 w, u16 h, u16 d, int strength){
	for(int cx = x;cx < x+w;cx++){
	for(int cy = y;cy < y+h;cy++){
	for(int cz = z;cz < z+d;cz++){
		fire *f = fireGetAtPos(cx,cy,cz);
		if(f != NULL){
			f->strength = MAX(-strength,f->strength - strength);
			fireSendUpdate(-1, f - fireList);
		}
		if(!isClient){
			const blockId b = worldTryB(cx,cy,cz);
			if((b == I_Dry_Grass) && (rngValA(1) == 0)){
				worldSetB(cx,cy,cz,I_Grass);
			}
			if(!b){
				worldSetFluid(cx,cy,cz,0xF0);
	for(uint i = calls&0xF; i < chungusCount; i+=0x10){
		chungus *cng = &chungusList[i];
		for(int x=0;x<16;x++){
		for(int y=0;y<16;y++){
		for(int z=0;z<16;z++){
			chunk *c = &cng->chunks[x][y][z];
			if(c->fire == NULL){continue;}
			if(!fireTick(c->fire, c->fluid, c->block, c->x, c->y, c->z)){
				chunkOverlayFree(c->fire);
				c->fire = NULL;
			}
		}
		}
		}
	}
	}
	}
	++calls;

	PROFILE_STOP();
}

M server/src/game/fire.h => server/src/game/fire.h +0 -4
@@ 1,8 1,4 @@
#pragma once
#include "../../../common/src/game/fire.h"

void  fireNewF         (u16 x, u16 y, u16 z, i16 strength, i16 blockDmg, i16 oxygen);
void  fireNew          (u16 x, u16 y, u16 z, i16 strength);
void  fireRecvUpdate   (uint c, const packet *p);
void  fireUpdateAll    ();
void  fireSyncPlayer   (uint c);

M server/src/game/itemDrop.c => server/src/game/itemDrop.c +8 -10
@@ 240,27 240,25 @@ void itemDropUpdateFire(uint i){
	const uint cx = e->pos.x;
	const uint cy = e->pos.y;
	const uint cz = e->pos.z;
	fire *f = &fireList[id->lastFire];
	if((id->lastFire >= fireCount) || (f->x != cx) || (f->y != cy) || (f->z != cz)){
		f = fireGetAtPos(cx,cy,cz);
		id->lastFire = (u16)(f - fireList);
	}
	if((f != NULL) && (f->x == cx) && (f->y == cy) && (f->z == cz)){
		const int dmg = MIN(f->oxygen,itemGetFireDamage(&id->itm) * id->itm.amount);
	const u8 f = worldGetFire(cx,cy,cz);
	if(f){
		const int dmg = f;
		id->fireDmg += dmg;
		f->strength += dmg - id->itm.amount;
		f->oxygen   -= dmg;
		//f->strength += dmg - id->itm.amount;
		//f->oxygen   -= dmg;
	}else if(id->fireDmg > 0){
		const int dmg = MIN(id->fireDmg,itemGetFireDamage(&id->itm) * id->itm.amount);
		id->fireDmg -= dmg;
		fireNew(cx,cy,cz,dmg);
		//fireNew(cx,cy,cz,dmg);
	}

	int maxhp = itemGetFireHealth(&id->itm) * id->itm.amount;
	if(id->fireDmg >= maxhp){
		/*
		if(id->ent != NULL){
			lispCallFuncVII("item-burn-up",id->ent->pos, id->itm.ID , id->itm.amount);
		}
		*/
		itemDropDel(i);
		addPriorityItemDrop(i);
		id->fireDmg = 0;

M server/src/game/weather/lightning.c => server/src/game/weather/lightning.c +1 -1
@@ 31,7 31,7 @@
int fireBeamSize = 0;
void lightningFireBeamCB(int x, int y, int z){
	if(worldTryB(x,y,z)){
		fireNew(x,y,z,fireBeamSize);
		worldSetFire(x,y,z,fireBeamSize);
	}
}


M server/src/misc/client_stubs.c => server/src/misc/client_stubs.c +8 -0
@@ 96,3 96,11 @@ void lWidgetMarkI(uint i){
}

void lightningDrawOverlay(){}

void fxFluidVapor (int cx, int cy, int cz, int fluidType, int amountLost){
	(void)cx;
	(void)cy;
	(void)cz;
	(void)fluidType;
	(void)amountLost;
}

M server/src/misc/lisp.c => server/src/misc/lisp.c +0 -5
@@ 81,10 81,6 @@ static lVal *wwlnfACount(lClosure *c, lVal *v){
	(void)c;(void)v;
	return lValInt(animalCount);
}
static lVal *wwlnfFCount(lClosure *c, lVal *v){
	(void)c;(void)v;
	return lValInt(fireCount);
}
static lVal *wwlnfBMCount(lClosure *c, lVal *v){
	(void)c;(void)v;
	return lValInt(blockMiningGetActive());


@@ 468,7 464,6 @@ void addServerNativeFuncs(lClosure *c){
	lAddNativeFunc(c,"player-pos",     "()",                                           "Returns player pos vector",                                  wwlnfPlayerPos);
	lAddNativeFunc(c,"animal-count",   "()",                                           "Returns animal count",                                       wwlnfACount);
	lAddNativeFunc(c,"animal-kill-all","()",                                           "Returns animal count",                                       wwlnfAnimalKillAll);
	lAddNativeFunc(c,"fire-count",     "()",                                           "Returns fire count",                                         wwlnfFCount);
	lAddNativeFunc(c,"mining-count",   "()",                                           "Returns block mining count",                                 wwlnfBMCount);
	lAddNativeFunc(c,"item-drop-count","()",                                           "Returns item drop count",                                    wwlnfIDCount);
	lAddNativeFunc(c,"item-drop-slow", "()",                                           "Returns amount of itemDrops that have the slow update bit",  wwlnfIDSlowCount);

M server/src/network/server.c => server/src/network/server.c +1 -4
@@ 227,7 227,6 @@ void msgUpdatePlayer(uint c){
	blockMiningUpdatePlayer(c);
	animalSyncPlayer(c);
	projectileSyncPlayer(c);
	fireSyncPlayer(c);
	throwableSyncPlayer(c);
	addQueuedChunks(c);
	clients[c].flags &= ~(CONNECTION_DO_UPDATE);


@@ 279,6 278,7 @@ void msgSendChunkOverlay(uint c, chunkOverlay *o, int x, int y, int z, chunkOver
void msgSendChunk(uint c, const chunk *chnk){
	msgSendChunkOverlay(c,chnk->block, chnk->x, chnk->y, chnk->z, chunkOverlayBlock);
	msgSendChunkOverlay(c,chnk->fluid, chnk->x, chnk->y, chnk->z, chunkOverlayFluid);
	msgSendChunkOverlay(c,chnk->fire,  chnk->x, chnk->y, chnk->z, chunkOverlayFire);
}

void dispatchBeingDmg(uint c, const packet *p){


@@ 410,9 410,6 @@ void serverParseSinglePacket(uint c, packet *p){
	case msgtFxProjectileHit:
		packetEchoExcept(c,p);
		break;
	case msgtFireRecvUpdate:
		fireRecvUpdate(c,p);
		break;
	case msgtLispRecvSExpr:
		lispRecvSExpr(c,p);
		break;

M server/src/persistence/chunk.c => server/src/persistence/chunk.c +7 -0
@@ 42,6 42,9 @@ void *chunkSave(chunk *c, void *rbuf, saveType t){
	case saveTypeChunkFluidData:
		src = c->fluid ? c->fluid->data : NULL;
		break;
	case saveTypeChunkFireData:
		src = c->fire ? c->fire->data : NULL;
		break;
	}
	if(src == NULL){return buf;}
	memcpy(buf+4, src, 16*16*16);


@@ 70,6 73,10 @@ const void *chunkLoad(chungus *c, const void *rbuf){
		if(chnk->fluid == NULL){chnk->fluid = chunkOverlayAllocate();}
		dest = chnk->fluid->data;
		break;
	case saveTypeChunkFireData:
		if(chnk->fire == NULL){chnk->fire = chunkOverlayAllocate();}
		dest = chnk->fire->data;
		break;
	}
	memcpy(dest, &buf[4], 4096);


D server/src/persistence/fire.c => server/src/persistence/fire.c +0 -62
@@ 1,62 0,0 @@
/*
 * Wolkenwelten - Copyright (C) 2020-2021 - Benjamin Vincent Schulenburg
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "fire.h"

#include "savegame.h"
#include "../game/fire.h"
#include "../voxel/chungus.h"

static void *fireSave(const fire *f, void *buf){
	u8    *b = (u8  *)buf;
	u16   *u = (u16 *)buf;
	i16   *s = (i16 *)buf;

	if(f == NULL){return b;}

	b[0] = saveTypeFire;
	b[1] = 0;

	u[1] = f->x;
	u[2] = f->y;
	u[3] = f->z;

	s[4] = f->strength;
	s[5] = f->blockDmg;
	s[6] = f->oxygen;

	return b+14;
}

const void *fireLoad(const void *buf){
	u8    *b = (u8  *)buf;
	u16   *u = (u16 *)buf;
	i16   *s = (i16 *)buf;

	fireNewF(u[1],u[2],u[3],s[4],s[5],s[6]);
	return b+14;
}

void *fireSaveChungus(const chungus *c,void *buf){
	if(c == NULL){return buf;}
	for(uint i=0;i<fireCount;i++){
		if(c->x != (fireList[i].x >> 8)){continue;}
		if(c->y != (fireList[i].y >> 8)){continue;}
		if(c->z != (fireList[i].z >> 8)){continue;}
		buf = fireSave(&fireList[i],buf);
	}
	return buf;
}

D server/src/persistence/fire.h => server/src/persistence/fire.h +0 -5
@@ 1,5 0,0 @@
#pragma once
#include "../../../common/src/common.h"

const void *fireLoad       (const void *buf);
void       *fireSaveChungus(const chungus *c,void *buf);

M server/src/persistence/savegame.c => server/src/persistence/savegame.c +2 -5
@@ 19,7 19,6 @@
#include "animal.h"
#include "character.h"
#include "chunk.h"
#include "fire.h"
#include "itemDrop.h"
#include "throwable.h"



@@ 151,6 150,7 @@ void chungusLoad(chungus *c){
	for(b=saveLoadBuffer;b<end;){
		saveType cType = *b;
		switch(cType){
		case saveTypeChunkFireData:
		case saveTypeChunkFluidData:
		case saveTypeChunkBlockData:
			b = chunkLoad(c,b);


@@ 161,9 161,6 @@ void chungusLoad(chungus *c){
		case saveTypeAnimal:
			b = animalLoad(b);
			break;
		case saveTypeFire:
			b = fireLoad(b);
			break;
		case saveTypeThrowable:
			b = throwableLoad(b);
			break;


@@ 193,12 190,12 @@ void chungusSave(chungus *c){
	for(int z=0;z<16;z++){
		cbuf = chunkSave(&c->chunks[x][y][z],cbuf, saveTypeChunkBlockData);
		cbuf = chunkSave(&c->chunks[x][y][z],cbuf, saveTypeChunkFluidData);
		cbuf = chunkSave(&c->chunks[x][y][z],cbuf, saveTypeChunkFireData);
	}
	}
	}
	cbuf = itemDropSaveChungus (c,cbuf);
	cbuf = animalSaveChungus   (c,cbuf);
	cbuf = fireSaveChungus     (c,cbuf);
	cbuf = throwableSaveChungus(c,cbuf);
	const size_t uncompressedLen = cbuf - saveLoadBuffer;
	if(uncompressedLen == 0){

M server/src/persistence/savegame.h => server/src/persistence/savegame.h +2 -1
@@ 8,7 8,8 @@ typedef enum {
	saveTypeAnimal,
	saveTypeFire,
	saveTypeThrowable,
	saveTypeChunkFluidData
	saveTypeChunkFluidData,
	saveTypeChunkFireData
} saveType;

void playerSafeSave        ();

M server/src/voxel/bigchungus.c => server/src/voxel/bigchungus.c +34 -0
@@ 480,3 480,37 @@ u8 worldGetFluid(int x, int y, int z){
bool worldSetFluid(int x, int y, int z, int level){
	return bigchungusSetFluid(&world,x,y,z, level);
}


bool bigchungusSetFire(bigchungus *c, int x, int y, int z, int strength){
	chungus *chng;
	int cx = (x / CHUNGUS_SIZE) & 0xFF;
	int cy = (y / CHUNGUS_SIZE) & 0x7F;
	int cz = (z / CHUNGUS_SIZE) & 0xFF;
	chng = c->chungi[cx][cy][cz];
	if(chng == NULL){return false;}
	chungusSetFire(chng, x&0xFF, y&0xFF, z&0xFF, strength);
	return true;
}

u8 bigchungusTryFire(bigchungus *c, int x,int y,int z) {
	chungus *chng;
	if(!inWorld(x,y,z)){return 0;}
	chng = bigchungusTryChungus(c,x>>8,y>>8,z>>8);
	if(chng == NULL){ return 0; }
	return chungusGetFire(chng,x&0xFF,y&0xFF,z&0xFF);
}

u8 bigchungusGetFire(bigchungus *c, int x,int y,int z) {
	return bigchungusTryFire(c,x,y,z);
}

u8 worldTryFire(int x, int y, int z){
	return bigchungusTryFluid(&world,x,y,z);
}
u8 worldGetFire(int x, int y, int z){
	return bigchungusGetFire(&world,x,y,z);
}
bool worldSetFire(int x, int y, int z, int strength){
	return bigchungusSetFire(&world,x,y,z, strength);
}

M server/src/voxel/bigchungus.h => server/src/voxel/bigchungus.h +7 -0
@@ 66,3 66,10 @@ u8       bigchungusGetFluid(bigchungus *c, int x, int y, int z);
u8       worldTryFluid(int x, int y, int z);
u8       worldGetFluid(int x, int y, int z);
bool     worldSetFluid(int x, int y, int z, int level);

bool     bigchungusSetFire(bigchungus *c, int x, int y, int z, int strength);
u8       bigchungusTryFire(bigchungus *c, int x, int y, int z);
u8       bigchungusGetFire(bigchungus *c, int x, int y, int z);
u8       worldTryFire(int x, int y, int z);
u8       worldGetFire(int x, int y, int z);
bool     worldSetFire(int x, int y, int z, int level);

M server/src/voxel/chungus.c => server/src/voxel/chungus.c +19 -1
@@ 125,7 125,6 @@ void chungusFree(chungus *c){
	chungusSave(c);
	animalDelChungus(c);
	itemDropDelChungus(c);
	fireDelChungus(c);
	throwableDelChungus(c);
	for(int x=0;x<CHUNGUS_COORDS;x++){
	for(int y=0;y<CHUNGUS_COORDS;y++){


@@ 497,6 496,25 @@ void chungusSetFluid(chungus *c, int x, int y, int z, int level){
	chnk->clientsUpdated = c->clientsUpdated = 0;
}

u8 chungusGetFire(chungus *c, int x, int y, int z){
	c->freeTimer = freeTime;
	chunk *chnk = &c->chunks[(x>>4)&0xF][(y>>4)&0xF][(z>>4)&0xF];
	if(chnk->fire == NULL){ return 0;}
	return chnk->fire->data[x&0xF][y&0xF][z&0xF];
}

void chungusSetFire(chungus *c, int x, int y, int z, int strength){
	if((x&(~0xFF)) || (y&(~0xFF)) || (z&(~0xFF))){return;}
	c->freeTimer = freeTime;
	int cx = x >> 4;
	int cy = y >> 4;
	int cz = z >> 4;
	chunk *chnk = &c->chunks[cx][cy][cz];
	if(chnk->fire == NULL){chnk->fire = chunkOverlayAllocate();}
	chnk->fire->data[x&0xF][y&0xF][z&0xF] = strength;
	chnk->clientsUpdated = c->clientsUpdated = 0;
}

uint chungusGetLinearMax(){
	return chungusCount * 16 * 16 * 16;
}

M server/src/voxel/chungus.h => server/src/voxel/chungus.h +2 -0
@@ 38,6 38,8 @@ void         chungusSetB             (chungus *c, int x, int y, int z, blockId b
blockId      chungusGetB             (chungus *c, int x, int y, int z);
void         chungusSetFluid         (chungus *c, int x, int y, int z, int level);
u8           chungusGetFluid         (chungus *c, int x, int y, int z);
void         chungusSetFire          (chungus *c, int x, int y, int z, int strength);
u8           chungusGetFire          (chungus *c, int x, int y, int z);
vec          chungusGetPos           (const chungus *c);
chunk       *chungusGetChunk         (chungus *c, int x, int y, int z);
void         chungusSetSpawn         (chungus *c, const vec spawn);

M server/src/voxel/chunk.h => server/src/voxel/chunk.h +1 -1
@@ 10,7 10,7 @@ struct chunk {
	void *nextFree;
	beingList bl;

	chunkOverlay *fluid, *block;
	chunkOverlay *fluid, *block, *fire;
};

void   chunkFree        (chunk *c);