#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 */ };