ClassiCube/src/World.c

327 lines
10 KiB
C

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