1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
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 */
};
|