summary refs log tree commit diff
path: root/src/World.c
diff options
context:
space:
mode:
authorWlodekM <[email protected]>2024-06-16 10:35:45 +0300
committerWlodekM <[email protected]>2024-06-16 10:35:45 +0300
commitabef6da56913f1c55528103e60a50451a39628b1 (patch)
treeb3c8092471ecbb73e568cd0d336efa0e7871ee8d /src/World.c
initial commit
Diffstat (limited to 'src/World.c')
-rw-r--r--src/World.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/src/World.c b/src/World.c
new file mode 100644
index 0000000..b01563d
--- /dev/null
+++ b/src/World.c
@@ -0,0 +1,326 @@
+#include "World.h"
+#include "Logger.h"
+#include "String.h"
+#include "Platform.h"
+#include "Event.h"
+#include "Block.h"
+#include "Entity.h"
+#include "ExtMath.h"
+#include "Physics.h"
+#include "Game.h"
+#include "TexturePack.h"
+#include "Window.h"
+
+struct _WorldData World;
+static char nameBuffer[STRING_SIZE];
+/*########################################################################################################################*
+*----------------------------------------------------------World----------------------------------------------------------*
+*#########################################################################################################################*/
+static void GenerateNewUuid(void) {
+	RNGState rnd;
+	int i;
+	Random_SeedFromCurrentTime(&rnd);
+
+	/* seed a bit more randomness for uuid */
+	for (i = 0; i < Game_Username.length; i++) {
+		Random_Next(&rnd, Game_Username.buffer[i] + 3);
+	}
+
+	for (i = 0; i < WORLD_UUID_LEN; i++) {
+		World.Uuid[i] = Random_Next(&rnd, 256);
+	}
+
+	/* Set version and variant bits */
+	World.Uuid[6] &= 0x0F;
+	World.Uuid[6] |= 0x40; /* version 4*/
+	World.Uuid[8] &= 0x3F;
+	World.Uuid[8] |= 0x80; /* variant 2*/
+}
+
+void World_Reset(void) {
+#ifdef EXTENDED_BLOCKS
+	if (World.Blocks != World.Blocks2) Mem_Free(World.Blocks2);
+	World.Blocks2 = NULL;
+	World.IDMask  = 0xFF;
+#endif
+	Mem_Free(World.Blocks);
+	World.Blocks = NULL;
+	String_InitArray(World.Name, nameBuffer);
+
+	World_SetDimensions(0, 0, 0);
+	World.Loaded   = false;
+	World.LastSave = -200;
+	World.Seed     = 0;
+	Env_Reset();
+}
+
+void World_NewMap(void) {
+	World_Reset();
+	Event_RaiseVoid(&WorldEvents.NewMap);
+}
+
+void World_SetNewMap(BlockRaw* blocks, int width, int height, int length) {
+	/* TODO: TEMP HACK */
+	if (!blocks) { width = 0; height = 0; length = 0; }
+
+	World_SetDimensions(width, height, length);
+	World.Blocks      = blocks;
+	World.Name.length = 0;
+
+	if (!World.Volume) World.Blocks = NULL;
+#ifdef EXTENDED_BLOCKS
+	/* .cw maps may have set this to a non-NULL when importing */
+	if (!World.Blocks2) {
+		World.Blocks2 = World.Blocks;
+		World.IDMask  = 0xFF;
+	}
+#endif
+
+	if (Env.EdgeHeight == -1)   { Env.EdgeHeight   = height / 2; }
+	if (Env.CloudsHeight == -1) { Env.CloudsHeight = height + 2; }
+
+	GenerateNewUuid();
+	World.Loaded = true;
+	Event_RaiseVoid(&WorldEvents.MapLoaded);
+}
+
+CC_NOINLINE void World_SetDimensions(int width, int height, int length) {
+	World.Width  = width; World.Height = height; World.Length = length;
+	World.Volume = width * height * length;
+
+	World.OneY = width * length;
+	World.MaxX = width  - 1;
+	World.MaxY = height - 1;
+	World.MaxZ = length - 1;
+
+	World.ChunksX = (width  + CHUNK_MAX) >> CHUNK_SHIFT;
+	World.ChunksY = (height + CHUNK_MAX) >> CHUNK_SHIFT;
+	World.ChunksZ = (length + CHUNK_MAX) >> CHUNK_SHIFT;
+
+	World.ChunksCount = World.ChunksX * World.ChunksY * World.ChunksZ;
+}
+
+#ifdef EXTENDED_BLOCKS
+void World_SetMapUpper(BlockRaw* blocks) {
+	World.Blocks2 = blocks;
+	World.IDMask  = 0x3FF;
+}
+#endif
+
+void World_OutOfMemory(void) {
+	Window_ShowDialog("Out of memory", "Not enough free memory to load the map.\nTry joining a different map.");
+	World_Reset();
+}
+
+
+#ifdef EXTENDED_BLOCKS
+static CC_NOINLINE void LazyInitUpper(int i, BlockID block) {
+	BlockRaw* data = (BlockRaw*)Mem_TryAllocCleared(World.Volume, 1);
+	if (!data) { World_OutOfMemory(); return; }
+
+	World_SetMapUpper(data);
+	World.Blocks2[i] = (BlockRaw)(block >> 8);
+}
+
+void World_SetBlock(int x, int y, int z, BlockID block) {
+	int i = World_Pack(x, y, z);
+	World.Blocks[i] = (BlockRaw)block;
+
+	/* defer allocation of second map array if possible */
+	if (World.Blocks == World.Blocks2) {
+		if (block < 256) return;
+		LazyInitUpper(i, block);
+		return;
+	}
+	World.Blocks2[i] = (BlockRaw)(block >> 8);
+}
+#else
+void World_SetBlock(int x, int y, int z, BlockID block) {
+	World.Blocks[World_Pack(x, y, z)] = block; 
+}
+#endif
+
+BlockID World_GetPhysicsBlock(int x, int y, int z) {
+	if (y < 0 || !World_ContainsXZ(x, z)) return BLOCK_BEDROCK;
+	if (y >= World.Height) return BLOCK_AIR;
+
+	return World_GetBlock(x, y, z);
+}
+
+BlockID World_SafeGetBlock(int x, int y, int z) {
+	return World_Contains(x, y, z) ? World_GetBlock(x, y, z) : BLOCK_AIR;
+}
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------Environment-------------------------------------------------------*
+*#########################################################################################################################*/
+#define Env_Set(src, dst, var) \
+if (src != dst) { dst = src; Event_RaiseInt(&WorldEvents.EnvVarChanged, var); }
+
+struct _EnvData Env;
+const char* const Weather_Names[3] = { "Sunny", "Rainy", "Snowy" };
+
+void Env_Reset(void) {
+	Env.EdgeHeight   = -1;
+	Env.SidesOffset  = -2;
+	Env.CloudsHeight = -1;
+
+	Env.EdgeBlock  = BLOCK_STILL_WATER;
+	Env.SidesBlock = BLOCK_BEDROCK;
+
+	Env.CloudsSpeed    = 1.0f;
+	Env.WeatherSpeed   = 1.0f;
+	Env.WeatherFade    = 1.0f;
+	Env.SkyboxHorSpeed = 0.0f;
+	Env.SkyboxVerSpeed = 0.0f;
+
+	Env.ShadowCol = ENV_DEFAULT_SHADOW_COLOR;
+	PackedCol_GetShaded(Env.ShadowCol, &Env.ShadowXSide,
+		&Env.ShadowZSide, &Env.ShadowYMin);
+
+	Env.SunCol = ENV_DEFAULT_SUN_COLOR;
+	PackedCol_GetShaded(Env.SunCol, &Env.SunXSide,
+		&Env.SunZSide, &Env.SunYMin);
+
+	Env.SkyCol       = ENV_DEFAULT_SKY_COLOR;
+	Env.FogCol       = ENV_DEFAULT_FOG_COLOR;
+	Env.CloudsCol    = ENV_DEFAULT_CLOUDS_COLOR;
+	Env.SkyboxCol    = ENV_DEFAULT_SKYBOX_COLOR;
+	Env.LavaLightCol = ENV_DEFAULT_LAVALIGHT_COLOR;
+	Env.LampLightCol = ENV_DEFAULT_LAMPLIGHT_COLOR;
+	Env.Weather   = WEATHER_SUNNY;
+	Env.ExpFog    = false;
+}
+
+
+void Env_SetEdgeBlock(BlockID block) {
+	/* some server software wrongly uses this value */
+	if (block == 255 && !Block_IsCustomDefined(255)) block = BLOCK_STILL_WATER; 
+	Env_Set(block, Env.EdgeBlock, ENV_VAR_EDGE_BLOCK);
+}
+void Env_SetSidesBlock(BlockID block) {
+	/* some server software wrongly uses this value */
+	if (block == 255 && !Block_IsCustomDefined(255)) block = BLOCK_BEDROCK;
+	Env_Set(block, Env.SidesBlock, ENV_VAR_SIDES_BLOCK);
+}
+
+void Env_SetEdgeHeight(int height) {
+	Env_Set(height, Env.EdgeHeight, ENV_VAR_EDGE_HEIGHT);
+}
+void Env_SetSidesOffset(int offset) {
+	Env_Set(offset, Env.SidesOffset, ENV_VAR_SIDES_OFFSET);
+}
+void Env_SetCloudsHeight(int height) {
+	Env_Set(height, Env.CloudsHeight, ENV_VAR_CLOUDS_HEIGHT);
+}
+void Env_SetCloudsSpeed(float speed) {
+	Env_Set(speed, Env.CloudsSpeed, ENV_VAR_CLOUDS_SPEED);
+}
+
+void Env_SetWeatherSpeed(float speed) {
+	Env_Set(speed, Env.WeatherSpeed, ENV_VAR_WEATHER_SPEED);
+}
+void Env_SetWeatherFade(float rate) {
+	Env_Set(rate, Env.WeatherFade, ENV_VAR_WEATHER_FADE);
+}
+void Env_SetWeather(int weather) {
+	Env_Set(weather, Env.Weather, ENV_VAR_WEATHER);
+}
+void Env_SetExpFog(cc_bool expFog) {
+	Env_Set(expFog, Env.ExpFog, ENV_VAR_EXP_FOG);
+}
+void Env_SetSkyboxHorSpeed(float speed) {
+	Env_Set(speed, Env.SkyboxHorSpeed, ENV_VAR_SKYBOX_HOR_SPEED);
+}
+void Env_SetSkyboxVerSpeed(float speed) {
+	Env_Set(speed, Env.SkyboxVerSpeed, ENV_VAR_SKYBOX_VER_SPEED);
+}
+
+void Env_SetSkyCol(PackedCol color) {
+	Env_Set(color, Env.SkyCol, ENV_VAR_SKY_COLOR);
+}
+void Env_SetFogCol(PackedCol color) {
+	Env_Set(color, Env.FogCol, ENV_VAR_FOG_COLOR);
+}
+void Env_SetCloudsCol(PackedCol color) {
+	Env_Set(color, Env.CloudsCol, ENV_VAR_CLOUDS_COLOR);
+}
+void Env_SetSkyboxCol(PackedCol color) {
+	Env_Set(color, Env.SkyboxCol, ENV_VAR_SKYBOX_COLOR);
+}
+void Env_SetLavaLightCol(PackedCol color) {
+	Env_Set(color, Env.LavaLightCol, ENV_VAR_LAVALIGHT_COLOR);
+}
+void Env_SetLampLightCol(PackedCol color) {
+	Env_Set(color, Env.LampLightCol, ENV_VAR_LAMPLIGHT_COLOR);
+}
+void Env_SetSunCol(PackedCol color) {
+	PackedCol_GetShaded(color, &Env.SunXSide, &Env.SunZSide, &Env.SunYMin);
+	Env_Set(color, Env.SunCol, ENV_VAR_SUN_COLOR);
+}
+void Env_SetShadowCol(PackedCol color) {
+	PackedCol_GetShaded(color, &Env.ShadowXSide, &Env.ShadowZSide, &Env.ShadowYMin);
+	Env_Set(color, Env.ShadowCol, ENV_VAR_SHADOW_COLOR);
+}
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------Respawning--------------------------------------------------------*
+*#########################################################################################################################*/
+float Respawn_HighestSolidY(struct AABB* bb) {
+	int minX = Math_Floor(bb->Min.x), maxX = Math_Floor(bb->Max.x);
+	int minY = Math_Floor(bb->Min.y), maxY = Math_Floor(bb->Max.y);
+	int minZ = Math_Floor(bb->Min.z), maxZ = Math_Floor(bb->Max.z);
+	float highestY = RESPAWN_NOT_FOUND;
+
+	BlockID block;
+	struct AABB blockBB;
+	Vec3 v;
+	int x, y, z;	
+
+	for (y = minY; y <= maxY; y++) { v.y = (float)y;
+		for (z = minZ; z <= maxZ; z++) { v.z = (float)z;
+			for (x = minX; x <= maxX; x++) { v.x = (float)x;
+
+				/* TODO: Maybe use how picking gets blocks, so the bedrock */
+				/* just below and just on borders of the map is treated as such */
+				/* Not sure if this is really necessary though, it seems to work */
+				/* just fine already when you're standing on the bottom of the map. */
+				block = World_SafeGetBlock(x, y, z);
+				Vec3_Add(&blockBB.Min, &v, &Blocks.MinBB[block]);
+				Vec3_Add(&blockBB.Max, &v, &Blocks.MaxBB[block]);
+
+				if (Blocks.Collide[block] != COLLIDE_SOLID) continue;
+				if (!AABB_Intersects(bb, &blockBB)) continue;
+				if (blockBB.Max.y > highestY) highestY = blockBB.Max.y;
+			}
+		}
+	}
+	return highestY;
+}
+
+Vec3 Respawn_FindSpawnPosition(float x, float z, Vec3 modelSize) {
+	Vec3 spawn;
+	struct AABB bb;
+	float highestY;
+	int y;
+
+	Vec3_Set(spawn, x, World.Height + ENTITY_ADJUSTMENT, z);
+	AABB_Make(&bb, &spawn, &modelSize);
+	spawn.y = 0.0f;
+	
+	for (y = World.Height; y >= 0; y--) {
+		highestY = Respawn_HighestSolidY(&bb);
+		if (highestY != RESPAWN_NOT_FOUND) {
+			spawn.y = highestY; break;
+		}
+		bb.Min.y -= 1.0f; bb.Max.y -= 1.0f;
+	}
+	return spawn;
+}
+
+struct IGameComponent World_Component = {
+	World_Reset, /* Init  */
+	World_Reset  /* Free  */
+};