diff --git a/src/Animations.c b/src/Animations.c
new file mode 100644
index 000000000..b77674fc9
--- /dev/null
+++ b/src/Animations.c
@@ -0,0 +1,400 @@
+#include "TexturePack.h"
+#include "Constants.h"
+#include "Stream.h"
+#include "World.h"
+#include "Graphics.h"
+#include "Event.h"
+#include "Game.h"
+#include "Funcs.h"
+#include "Errors.h"
+#include "Chat.h"
+#include "ExtMath.h"
+#include "Options.h"
+#include "Logger.h"
+
+#define LIQUID_ANIM_MAX 64
+#define WATER_TEX_LOC 14
+#define LAVA_TEX_LOC 30
+
+#ifndef CC_BUILD_WEB
+/* Based off the incredible work from https://dl.dropboxusercontent.com/u/12694594/lava.txt
+ mirrored at https://github.com/UnknownShadow200/ClassicalSharp/wiki/Minecraft-Classic-lava-animation-algorithm
+ Water animation originally written by cybertoon, big thanks!
+*/
+/*########################################################################################################################*
+*-----------------------------------------------------Lava animation------------------------------------------------------*
+*#########################################################################################################################*/
+static float L_soupHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
+static float L_potHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
+static float L_flameHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
+static RNGState L_rnd;
+static cc_bool L_rndInited;
+
+static void LavaAnimation_Tick(BitmapCol* ptr, int size) {
+ int mask = size - 1, shift = Math_Log2(size);
+ float soupHeat, potHeat, col;
+ int x, y, i = 0;
+
+ if (!L_rndInited) {
+ Random_SeedFromCurrentTime(&L_rnd);
+ L_rndInited = true;
+ }
+
+ for (y = 0; y < size; y++) {
+ for (x = 0; x < size; x++) {
+ /* Calculate the colour at this coordinate in the heatmap */
+
+ /* Lookup table for (int)(1.2 * sin([ANGLE] * 22.5 * MATH_DEG2RAD)); */
+ /* [ANGLE] is integer x/y, so repeats every 16 intervals */
+ static cc_int8 sin_adj_table[16] = { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, -1, -1, -1, 0, 0 };
+ int xx = x + sin_adj_table[y & 0xF], yy = y + sin_adj_table[x & 0xF];
+
+ soupHeat =
+ L_soupHeat[((yy - 1) & mask) << shift | ((xx - 1) & mask)] +
+ L_soupHeat[((yy - 1) & mask) << shift | (xx & mask)] +
+ L_soupHeat[((yy - 1) & mask) << shift | ((xx + 1) & mask)] +
+
+ L_soupHeat[(yy & mask) << shift | ((xx - 1) & mask)] +
+ L_soupHeat[(yy & mask) << shift | (xx & mask)] +
+ L_soupHeat[(yy & mask) << shift | ((xx + 1) & mask)] +
+
+ L_soupHeat[((yy + 1) & mask) << shift | ((xx - 1) & mask)] +
+ L_soupHeat[((yy + 1) & mask) << shift | (xx & mask)] +
+ L_soupHeat[((yy + 1) & mask) << shift | ((xx + 1) & mask)];
+
+ potHeat =
+ L_potHeat[i] + /* x , y */
+ L_potHeat[y << shift | ((x + 1) & mask)] + /* x + 1, y */
+ L_potHeat[((y + 1) & mask) << shift | x] + /* x , y + 1 */
+ L_potHeat[((y + 1) & mask) << shift | ((x + 1) & mask)];/* x + 1, y + 1 */
+
+ L_soupHeat[i] = soupHeat * 0.1f + potHeat * 0.2f;
+
+ L_potHeat[i] += L_flameHeat[i];
+ if (L_potHeat[i] < 0.0f) L_potHeat[i] = 0.0f;
+
+ L_flameHeat[i] -= 0.06f * 0.01f;
+ if (Random_Float(&L_rnd) <= 0.005f) L_flameHeat[i] = 1.5f * 0.01f;
+
+ /* Output the pixel */
+ col = 2.0f * L_soupHeat[i];
+ Math_Clamp(col, 0.0f, 1.0f);
+
+ *ptr = BitmapCol_Make(
+ col * 100.0f + 155.0f,
+ col * col * 255.0f,
+ col * col * col * col * 128.0f,
+ 255);
+
+ ptr++; i++;
+ }
+ }
+}
+
+
+/*########################################################################################################################*
+*----------------------------------------------------Water animation------------------------------------------------------*
+*#########################################################################################################################*/
+static float W_soupHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
+static float W_potHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
+static float W_flameHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
+static RNGState W_rnd;
+static cc_bool W_rndInited;
+
+static void WaterAnimation_Tick(BitmapCol* ptr, int size) {
+ int mask = size - 1, shift = Math_Log2(size);
+ float soupHeat, col;
+ int x, y, i = 0;
+
+ if (!W_rndInited) {
+ Random_SeedFromCurrentTime(&W_rnd);
+ W_rndInited = true;
+ }
+
+ for (y = 0; y < size; y++) {
+ for (x = 0; x < size; x++) {
+ /* Calculate the colour at this coordinate in the heatmap */
+ soupHeat =
+ W_soupHeat[y << shift | ((x - 1) & mask)] +
+ W_soupHeat[y << shift | x ] +
+ W_soupHeat[y << shift | ((x + 1) & mask)];
+
+ W_soupHeat[i] = soupHeat / 3.3f + W_potHeat[i] * 0.8f;
+
+ W_potHeat[i] += W_flameHeat[i];
+ if (W_potHeat[i] < 0.0f) W_potHeat[i] = 0.0f;
+
+ W_flameHeat[i] -= 0.1f * 0.05f;
+ if (Random_Float(&W_rnd) <= 0.05f) W_flameHeat[i] = 0.5f * 0.05f;
+
+ /* Output the pixel */
+ col = W_soupHeat[i];
+ Math_Clamp(col, 0.0f, 1.0f);
+ col = col * col;
+
+ *ptr = BitmapCol_Make(
+ 32.0f + col * 32.0f,
+ 50.0f + col * 64.0f,
+ 255,
+ 146.0f + col * 50.0f);
+
+ ptr++; i++;
+ }
+ }
+}
+#endif
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------Animations--------------------------------------------------------*
+*#########################################################################################################################*/
+struct AnimationData {
+ TextureLoc texLoc; /* Tile (not pixel) coordinates in terrain.png */
+ cc_uint16 frameX, frameY; /* Top left pixel coordinates of start frame in animatons.png */
+ cc_uint16 frameSize; /* Size of each frame in pixel coordinates */
+ cc_uint16 state; /* Current animation frame index */
+ cc_uint16 statesCount; /* Total number of animation frames */
+ cc_uint16 delay; /* Delay in ticks until next frame is drawn */
+ cc_uint16 frameDelay; /* Delay between each frame */
+};
+
+static Bitmap anims_bmp;
+static struct AnimationData anims_list[ATLAS1D_MAX_ATLASES];
+static int anims_count;
+static cc_bool anims_validated, useLavaAnim, useWaterAnim, alwaysLavaAnim, alwaysWaterAnim;
+#define ANIM_MIN_ARGS 7
+
+static void Animations_ReadDescription(struct Stream* stream, const String* path) {
+ String line; char lineBuffer[STRING_SIZE * 2];
+ String parts[ANIM_MIN_ARGS];
+ int count;
+ struct AnimationData data = { 0 };
+ cc_uint8 tileX, tileY;
+
+ cc_uint8 buffer[2048];
+ struct Stream buffered;
+ cc_result res;
+
+ String_InitArray(line, lineBuffer);
+ /* ReadLine reads single byte at a time */
+ Stream_ReadonlyBuffered(&buffered, stream, buffer, sizeof(buffer));
+
+ for (;;) {
+ res = Stream_ReadLine(&buffered, &line);
+ if (res == ERR_END_OF_STREAM) break;
+ if (res) { Logger_Warn2(res, "reading from", path); break; }
+
+ if (!line.length || line.buffer[0] == '#') continue;
+ count = String_UNSAFE_Split(&line, ' ', parts, ANIM_MIN_ARGS);
+ if (count < ANIM_MIN_ARGS) {
+ Chat_Add1("&cNot enough arguments for anim: %s", &line); continue;
+ }
+
+ if (!Convert_ParseUInt8(&parts[0], &tileX) || tileX >= ATLAS2D_TILES_PER_ROW) {
+ Chat_Add1("&cInvalid anim tile X coord: %s", &parts[0]); continue;
+ }
+ if (!Convert_ParseUInt8(&parts[1], &tileY) || tileY >= ATLAS2D_MAX_ROWS_COUNT) {
+ Chat_Add1("&cInvalid anim tile Y coord: %s", &parts[1]); continue;
+ }
+ if (!Convert_ParseUInt16(&parts[2], &data.frameX)) {
+ Chat_Add1("&cInvalid anim frame X coord: %s", &parts[2]); continue;
+ }
+ if (!Convert_ParseUInt16(&parts[3], &data.frameY)) {
+ Chat_Add1("&cInvalid anim frame Y coord: %s", &parts[3]); continue;
+ }
+ if (!Convert_ParseUInt16(&parts[4], &data.frameSize) || !data.frameSize) {
+ Chat_Add1("&cInvalid anim frame size: %s", &parts[4]); continue;
+ }
+ if (!Convert_ParseUInt16(&parts[5], &data.statesCount)) {
+ Chat_Add1("&cInvalid anim states count: %s", &parts[5]); continue;
+ }
+ if (!Convert_ParseUInt16(&parts[6], &data.frameDelay)) {
+ Chat_Add1("&cInvalid anim frame delay: %s", &parts[6]); continue;
+ }
+
+ if (anims_count == Array_Elems(anims_list)) {
+ Chat_AddRaw("&cCannot show over 512 animations"); return;
+ }
+
+ data.texLoc = tileX + (tileY * ATLAS2D_TILES_PER_ROW);
+ anims_list[anims_count++] = data;
+ }
+}
+
+#define ANIMS_FAST_SIZE 64
+static void Animations_Draw(struct AnimationData* data, TextureLoc texLoc, int size) {
+ int dstX = Atlas1D_Index(texLoc), srcX;
+ int dstY = Atlas1D_RowId(texLoc) * Atlas2D.TileSize;
+ GfxResourceID tex;
+
+ cc_uint8 buffer[Bitmap_DataSize(ANIMS_FAST_SIZE, ANIMS_FAST_SIZE)];
+ cc_uint8* ptr = buffer;
+ Bitmap frame;
+
+ /* cannot allocate memory on the stack for very big animation.png frames */
+ if (size > ANIMS_FAST_SIZE) {
+ ptr = (cc_uint8*)Mem_Alloc(size * size, 4, "anim frame");
+ }
+ Bitmap_Init(frame, size, size, ptr);
+
+ if (!data) {
+#ifndef CC_BUILD_WEB
+ if (texLoc == LAVA_TEX_LOC) {
+ LavaAnimation_Tick((BitmapCol*)frame.Scan0, size);
+ } else if (texLoc == WATER_TEX_LOC) {
+ WaterAnimation_Tick((BitmapCol*)frame.Scan0, size);
+ }
+#endif
+ } else {
+ srcX = data->frameX + data->state * size;
+ Bitmap_UNSAFE_CopyBlock(srcX, data->frameY, 0, 0, &anims_bmp, &frame, size);
+ }
+
+ tex = Atlas1D.TexIds[dstX];
+ if (tex) { Gfx_UpdateTexturePart(tex, 0, dstY, &frame, Gfx.Mipmaps); }
+ if (size > ANIMS_FAST_SIZE) Mem_Free(ptr);
+}
+
+static void Animations_Apply(struct AnimationData* data) {
+ TextureLoc loc;
+ if (data->delay) { data->delay--; return; }
+
+ data->state++;
+ data->state %= data->statesCount;
+ data->delay = data->frameDelay;
+
+ loc = data->texLoc;
+#ifndef CC_BUILD_WEB
+ if (loc == LAVA_TEX_LOC && useLavaAnim) return;
+ if (loc == WATER_TEX_LOC && useWaterAnim) return;
+#endif
+ Animations_Draw(data, loc, data->frameSize);
+}
+
+static cc_bool Animations_IsDefaultZip(void) {
+ String texPack;
+ cc_bool optExists;
+ if (World_TextureUrl.length) return false;
+
+ optExists = Options_UNSAFE_Get(OPT_DEFAULT_TEX_PACK, &texPack);
+ return !optExists || String_CaselessEqualsConst(&texPack, "default.zip");
+}
+
+static void Animations_Clear(void) {
+ Mem_Free(anims_bmp.Scan0);
+ anims_count = 0;
+ anims_bmp.Scan0 = NULL;
+ anims_validated = false;
+}
+
+static void Animations_Validate(void) {
+ struct AnimationData data;
+ int maxX, maxY, tileX, tileY;
+ int i, j;
+
+ anims_validated = true;
+ for (i = 0; i < anims_count; i++) {
+ data = anims_list[i];
+
+ maxX = data.frameX + data.frameSize * data.statesCount;
+ maxY = data.frameY + data.frameSize;
+ tileX = Atlas2D_TileX(data.texLoc);
+ tileY = Atlas2D_TileY(data.texLoc);
+
+ if (data.frameSize > Atlas2D.TileSize || tileY >= Atlas2D.RowsCount) {
+ Chat_Add2("&cAnimation frames for tile (%i, %i) are bigger than the size of a tile in terrain.png", &tileX, &tileY);
+ } else if (maxX > anims_bmp.Width || maxY > anims_bmp.Height) {
+ Chat_Add2("&cSome of the animation frames for tile (%i, %i) are at coordinates outside animations.png", &tileX, &tileY);
+ } else {
+ /* if user has water/lava animations in their default.zip, disable built-in */
+ /* However, 'usewateranim' and 'uselavaanim' files should always disable use */
+ /* of custom water/lava animations, even when they exist in animations.png */
+ if (data.texLoc == LAVA_TEX_LOC && !alwaysLavaAnim) useLavaAnim = false;
+ if (data.texLoc == WATER_TEX_LOC && !alwaysWaterAnim) useWaterAnim = false;
+ continue;
+ }
+
+ /* Remove this animation from the list */
+ for (j = i; j < anims_count - 1; j++) {
+ anims_list[j] = anims_list[j + 1];
+ }
+ i--; anims_count--;
+ }
+}
+
+static void Animations_Tick(struct ScheduledTask* task) {
+ int i, size;
+
+#ifndef CC_BUILD_WEB
+ if (useLavaAnim) {
+ size = min(Atlas2D.TileSize, 64);
+ Animations_Draw(NULL, LAVA_TEX_LOC, size);
+ }
+ if (useWaterAnim) {
+ size = min(Atlas2D.TileSize, 64);
+ Animations_Draw(NULL, WATER_TEX_LOC, size);
+ }
+#endif
+
+ if (!anims_count) return;
+ if (!anims_bmp.Scan0) {
+ Chat_AddRaw("&cCurrent texture pack specifies it uses animations,");
+ Chat_AddRaw("&cbut is missing animations.png");
+ anims_count = 0; return;
+ }
+
+ /* deferred, because when reading animations.txt, might not have read animations.png yet */
+ if (!anims_validated) Animations_Validate();
+ for (i = 0; i < anims_count; i++) {
+ Animations_Apply(&anims_list[i]);
+ }
+}
+
+
+/*########################################################################################################################*
+*--------------------------------------------------Animations component---------------------------------------------------*
+*#########################################################################################################################*/
+static void OnPackChanged(void* obj) {
+ Animations_Clear();
+ useLavaAnim = Animations_IsDefaultZip();
+ useWaterAnim = useLavaAnim;
+ alwaysLavaAnim = false;
+ alwaysWaterAnim = false;
+}
+
+static void OnFileChanged(void* obj, struct Stream* stream, const String* name) {
+ cc_result res;
+ if (String_CaselessEqualsConst(name, "animations.png")) {
+ res = Png_Decode(&anims_bmp, stream);
+ if (!res) return;
+
+ Logger_Warn2(res, "decoding", name);
+ Mem_Free(anims_bmp.Scan0);
+ anims_bmp.Scan0 = NULL;
+ } else if (String_CaselessEqualsConst(name, "animations.txt")) {
+ Animations_ReadDescription(stream, name);
+ } else if (String_CaselessEqualsConst(name, "uselavaanim")) {
+ useLavaAnim = true;
+ alwaysLavaAnim = true;
+ } else if (String_CaselessEqualsConst(name, "usewateranim")) {
+ useWaterAnim = true;
+ alwaysWaterAnim = true;
+ }
+}
+
+static void Animations_Init(void) {
+ ScheduledTask_Add(GAME_DEF_TICKS, Animations_Tick);
+ Event_RegisterVoid(&TextureEvents.PackChanged, NULL, OnPackChanged);
+ Event_RegisterEntry(&TextureEvents.FileChanged, NULL, OnFileChanged);
+}
+
+static void Animations_Free(void) {
+ Animations_Clear();
+ Event_UnregisterVoid(&TextureEvents.PackChanged, NULL, OnPackChanged);
+ Event_UnregisterEntry(&TextureEvents.FileChanged, NULL, OnFileChanged);
+}
+
+struct IGameComponent Animations_Component = {
+ Animations_Init, /* Init */
+ Animations_Free /* Free */
+};
diff --git a/src/Animations.h b/src/Animations.h
new file mode 100644
index 000000000..71e5413fc
--- /dev/null
+++ b/src/Animations.h
@@ -0,0 +1,9 @@
+#ifndef CC_ANIMATIONS_H
+#define CC_ANIMATIONS_H
+/* Contains everything relating to texture animations (including default water/lava ones)
+ Copyright 2014-2020 ClassiCube | Licensed under BSD-3
+ */
+
+struct IGameComponent;
+extern struct IGameComponent Animations_Component;
+#endif
diff --git a/src/ClassiCube.vcxproj b/src/ClassiCube.vcxproj
index 5f706b873..5b819ca8b 100644
--- a/src/ClassiCube.vcxproj
+++ b/src/ClassiCube.vcxproj
@@ -187,6 +187,7 @@
+
@@ -250,6 +251,7 @@
+
diff --git a/src/ClassiCube.vcxproj.filters b/src/ClassiCube.vcxproj.filters
index 627e60248..7a8c44c58 100644
--- a/src/ClassiCube.vcxproj.filters
+++ b/src/ClassiCube.vcxproj.filters
@@ -309,6 +309,9 @@
Header Files\Graphics
+
+ Header Files\TexturePack
+
@@ -530,5 +533,8 @@
Source Files\Graphics
+
+ Source Files\TexturePack
+
\ No newline at end of file
diff --git a/src/Game.c b/src/Game.c
index 6b3675f72..4407b0117 100644
--- a/src/Game.c
+++ b/src/Game.c
@@ -35,6 +35,7 @@
#include "Builder.h"
#include "Protocol.h"
#include "Picking.h"
+#include "Animations.h"
struct _GameData Game;
int Game_Port;
diff --git a/src/TexturePack.c b/src/TexturePack.c
index d45ba791b..60d456fa9 100644
--- a/src/TexturePack.c
+++ b/src/TexturePack.c
@@ -1,6 +1,5 @@
#include "TexturePack.h"
#include "Constants.h"
-#include "Platform.h"
#include "Stream.h"
#include "World.h"
#include "Graphics.h"
@@ -9,403 +8,11 @@
#include "Http.h"
#include "Platform.h"
#include "Deflate.h"
-#include "Stream.h"
#include "Funcs.h"
-#include "Errors.h"
-#include "Chat.h"
#include "ExtMath.h"
-#include "Chat.h"
#include "Options.h"
#include "Logger.h"
-#define LIQUID_ANIM_MAX 64
-#define WATER_TEX_LOC 14
-#define LAVA_TEX_LOC 30
-
-#ifndef CC_BUILD_WEB
-/* Based off the incredible work from https://dl.dropboxusercontent.com/u/12694594/lava.txt
- mirrored at https://github.com/UnknownShadow200/ClassicalSharp/wiki/Minecraft-Classic-lava-animation-algorithm
- Water animation originally written by cybertoon, big thanks!
-*/
-/*########################################################################################################################*
-*-----------------------------------------------------Lava animation------------------------------------------------------*
-*#########################################################################################################################*/
-static float L_soupHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
-static float L_potHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
-static float L_flameHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
-static RNGState L_rnd;
-static cc_bool L_rndInited;
-
-static void LavaAnimation_Tick(BitmapCol* ptr, int size) {
- int mask = size - 1, shift = Math_Log2(size);
- float soupHeat, potHeat, col;
- int x, y, i = 0;
-
- if (!L_rndInited) {
- Random_SeedFromCurrentTime(&L_rnd);
- L_rndInited = true;
- }
-
- for (y = 0; y < size; y++) {
- for (x = 0; x < size; x++) {
- /* Calculate the colour at this coordinate in the heatmap */
-
- /* Lookup table for (int)(1.2 * sin([ANGLE] * 22.5 * MATH_DEG2RAD)); */
- /* [ANGLE] is integer x/y, so repeats every 16 intervals */
- static cc_int8 sin_adj_table[16] = { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, -1, -1, -1, 0, 0 };
- int xx = x + sin_adj_table[y & 0xF], yy = y + sin_adj_table[x & 0xF];
-
- soupHeat =
- L_soupHeat[((yy - 1) & mask) << shift | ((xx - 1) & mask)] +
- L_soupHeat[((yy - 1) & mask) << shift | (xx & mask)] +
- L_soupHeat[((yy - 1) & mask) << shift | ((xx + 1) & mask)] +
-
- L_soupHeat[(yy & mask) << shift | ((xx - 1) & mask)] +
- L_soupHeat[(yy & mask) << shift | (xx & mask)] +
- L_soupHeat[(yy & mask) << shift | ((xx + 1) & mask)] +
-
- L_soupHeat[((yy + 1) & mask) << shift | ((xx - 1) & mask)] +
- L_soupHeat[((yy + 1) & mask) << shift | (xx & mask)] +
- L_soupHeat[((yy + 1) & mask) << shift | ((xx + 1) & mask)];
-
- potHeat =
- L_potHeat[i] + /* x , y */
- L_potHeat[y << shift | ((x + 1) & mask)] + /* x + 1, y */
- L_potHeat[((y + 1) & mask) << shift | x] + /* x , y + 1 */
- L_potHeat[((y + 1) & mask) << shift | ((x + 1) & mask)];/* x + 1, y + 1 */
-
- L_soupHeat[i] = soupHeat * 0.1f + potHeat * 0.2f;
-
- L_potHeat[i] += L_flameHeat[i];
- if (L_potHeat[i] < 0.0f) L_potHeat[i] = 0.0f;
-
- L_flameHeat[i] -= 0.06f * 0.01f;
- if (Random_Float(&L_rnd) <= 0.005f) L_flameHeat[i] = 1.5f * 0.01f;
-
- /* Output the pixel */
- col = 2.0f * L_soupHeat[i];
- Math_Clamp(col, 0.0f, 1.0f);
-
- *ptr = BitmapCol_Make(
- col * 100.0f + 155.0f,
- col * col * 255.0f,
- col * col * col * col * 128.0f,
- 255);
-
- ptr++; i++;
- }
- }
-}
-
-
-/*########################################################################################################################*
-*----------------------------------------------------Water animation------------------------------------------------------*
-*#########################################################################################################################*/
-static float W_soupHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
-static float W_potHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
-static float W_flameHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
-static RNGState W_rnd;
-static cc_bool W_rndInited;
-
-static void WaterAnimation_Tick(BitmapCol* ptr, int size) {
- int mask = size - 1, shift = Math_Log2(size);
- float soupHeat, col;
- int x, y, i = 0;
-
- if (!W_rndInited) {
- Random_SeedFromCurrentTime(&W_rnd);
- W_rndInited = true;
- }
-
- for (y = 0; y < size; y++) {
- for (x = 0; x < size; x++) {
- /* Calculate the colour at this coordinate in the heatmap */
- soupHeat =
- W_soupHeat[y << shift | ((x - 1) & mask)] +
- W_soupHeat[y << shift | x ] +
- W_soupHeat[y << shift | ((x + 1) & mask)];
-
- W_soupHeat[i] = soupHeat / 3.3f + W_potHeat[i] * 0.8f;
-
- W_potHeat[i] += W_flameHeat[i];
- if (W_potHeat[i] < 0.0f) W_potHeat[i] = 0.0f;
-
- W_flameHeat[i] -= 0.1f * 0.05f;
- if (Random_Float(&W_rnd) <= 0.05f) W_flameHeat[i] = 0.5f * 0.05f;
-
- /* Output the pixel */
- col = W_soupHeat[i];
- Math_Clamp(col, 0.0f, 1.0f);
- col = col * col;
-
- *ptr = BitmapCol_Make(
- 32.0f + col * 32.0f,
- 50.0f + col * 64.0f,
- 255,
- 146.0f + col * 50.0f);
-
- ptr++; i++;
- }
- }
-}
-#endif
-
-
-/*########################################################################################################################*
-*-------------------------------------------------------Animations--------------------------------------------------------*
-*#########################################################################################################################*/
-struct AnimationData {
- TextureLoc texLoc; /* Tile (not pixel) coordinates in terrain.png */
- cc_uint16 frameX, frameY; /* Top left pixel coordinates of start frame in animatons.png */
- cc_uint16 frameSize; /* Size of each frame in pixel coordinates */
- cc_uint16 state; /* Current animation frame index */
- cc_uint16 statesCount; /* Total number of animation frames */
- cc_uint16 delay; /* Delay in ticks until next frame is drawn */
- cc_uint16 frameDelay; /* Delay between each frame */
-};
-
-static Bitmap anims_bmp;
-static struct AnimationData anims_list[ATLAS1D_MAX_ATLASES];
-static int anims_count;
-static cc_bool anims_validated, useLavaAnim, useWaterAnim, alwaysLavaAnim, alwaysWaterAnim;
-#define ANIM_MIN_ARGS 7
-
-static void Animations_ReadDescription(struct Stream* stream, const String* path) {
- String line; char lineBuffer[STRING_SIZE * 2];
- String parts[ANIM_MIN_ARGS];
- int count;
- struct AnimationData data = { 0 };
- cc_uint8 tileX, tileY;
-
- cc_uint8 buffer[2048];
- struct Stream buffered;
- cc_result res;
-
- String_InitArray(line, lineBuffer);
- /* ReadLine reads single byte at a time */
- Stream_ReadonlyBuffered(&buffered, stream, buffer, sizeof(buffer));
-
- for (;;) {
- res = Stream_ReadLine(&buffered, &line);
- if (res == ERR_END_OF_STREAM) break;
- if (res) { Logger_Warn2(res, "reading from", path); break; }
-
- if (!line.length || line.buffer[0] == '#') continue;
- count = String_UNSAFE_Split(&line, ' ', parts, ANIM_MIN_ARGS);
- if (count < ANIM_MIN_ARGS) {
- Chat_Add1("&cNot enough arguments for anim: %s", &line); continue;
- }
-
- if (!Convert_ParseUInt8(&parts[0], &tileX) || tileX >= ATLAS2D_TILES_PER_ROW) {
- Chat_Add1("&cInvalid anim tile X coord: %s", &parts[0]); continue;
- }
- if (!Convert_ParseUInt8(&parts[1], &tileY) || tileY >= ATLAS2D_MAX_ROWS_COUNT) {
- Chat_Add1("&cInvalid anim tile Y coord: %s", &parts[1]); continue;
- }
- if (!Convert_ParseUInt16(&parts[2], &data.frameX)) {
- Chat_Add1("&cInvalid anim frame X coord: %s", &parts[2]); continue;
- }
- if (!Convert_ParseUInt16(&parts[3], &data.frameY)) {
- Chat_Add1("&cInvalid anim frame Y coord: %s", &parts[3]); continue;
- }
- if (!Convert_ParseUInt16(&parts[4], &data.frameSize) || !data.frameSize) {
- Chat_Add1("&cInvalid anim frame size: %s", &parts[4]); continue;
- }
- if (!Convert_ParseUInt16(&parts[5], &data.statesCount)) {
- Chat_Add1("&cInvalid anim states count: %s", &parts[5]); continue;
- }
- if (!Convert_ParseUInt16(&parts[6], &data.frameDelay)) {
- Chat_Add1("&cInvalid anim frame delay: %s", &parts[6]); continue;
- }
-
- if (anims_count == Array_Elems(anims_list)) {
- Chat_AddRaw("&cCannot show over 512 animations"); return;
- }
-
- data.texLoc = tileX + (tileY * ATLAS2D_TILES_PER_ROW);
- anims_list[anims_count++] = data;
- }
-}
-
-#define ANIMS_FAST_SIZE 64
-static void Animations_Draw(struct AnimationData* data, TextureLoc texLoc, int size) {
- int dstX = Atlas1D_Index(texLoc), srcX;
- int dstY = Atlas1D_RowId(texLoc) * Atlas2D.TileSize;
- GfxResourceID tex;
-
- cc_uint8 buffer[Bitmap_DataSize(ANIMS_FAST_SIZE, ANIMS_FAST_SIZE)];
- cc_uint8* ptr = buffer;
- Bitmap frame;
-
- /* cannot allocate memory on the stack for very big animation.png frames */
- if (size > ANIMS_FAST_SIZE) {
- ptr = (cc_uint8*)Mem_Alloc(size * size, 4, "anim frame");
- }
- Bitmap_Init(frame, size, size, ptr);
-
- if (!data) {
-#ifndef CC_BUILD_WEB
- if (texLoc == LAVA_TEX_LOC) {
- LavaAnimation_Tick((BitmapCol*)frame.Scan0, size);
- } else if (texLoc == WATER_TEX_LOC) {
- WaterAnimation_Tick((BitmapCol*)frame.Scan0, size);
- }
-#endif
- } else {
- srcX = data->frameX + data->state * size;
- Bitmap_UNSAFE_CopyBlock(srcX, data->frameY, 0, 0, &anims_bmp, &frame, size);
- }
-
- tex = Atlas1D.TexIds[dstX];
- if (tex) { Gfx_UpdateTexturePart(tex, 0, dstY, &frame, Gfx.Mipmaps); }
- if (size > ANIMS_FAST_SIZE) Mem_Free(ptr);
-}
-
-static void Animations_Apply(struct AnimationData* data) {
- TextureLoc loc;
- if (data->delay) { data->delay--; return; }
-
- data->state++;
- data->state %= data->statesCount;
- data->delay = data->frameDelay;
-
- loc = data->texLoc;
-#ifndef CC_BUILD_WEB
- if (loc == LAVA_TEX_LOC && useLavaAnim) return;
- if (loc == WATER_TEX_LOC && useWaterAnim) return;
-#endif
- Animations_Draw(data, loc, data->frameSize);
-}
-
-static cc_bool Animations_IsDefaultZip(void) {
- String texPack;
- cc_bool optExists;
- if (World_TextureUrl.length) return false;
-
- optExists = Options_UNSAFE_Get(OPT_DEFAULT_TEX_PACK, &texPack);
- return !optExists || String_CaselessEqualsConst(&texPack, "default.zip");
-}
-
-static void Animations_Clear(void) {
- Mem_Free(anims_bmp.Scan0);
- anims_count = 0;
- anims_bmp.Scan0 = NULL;
- anims_validated = false;
-}
-
-static void Animations_Validate(void) {
- struct AnimationData data;
- int maxX, maxY, tileX, tileY;
- int i, j;
-
- anims_validated = true;
- for (i = 0; i < anims_count; i++) {
- data = anims_list[i];
-
- maxX = data.frameX + data.frameSize * data.statesCount;
- maxY = data.frameY + data.frameSize;
- tileX = Atlas2D_TileX(data.texLoc);
- tileY = Atlas2D_TileY(data.texLoc);
-
- if (data.frameSize > Atlas2D.TileSize || tileY >= Atlas2D.RowsCount) {
- Chat_Add2("&cAnimation frames for tile (%i, %i) are bigger than the size of a tile in terrain.png", &tileX, &tileY);
- } else if (maxX > anims_bmp.Width || maxY > anims_bmp.Height) {
- Chat_Add2("&cSome of the animation frames for tile (%i, %i) are at coordinates outside animations.png", &tileX, &tileY);
- } else {
- /* if user has water/lava animations in their default.zip, disable built-in */
- /* However, 'usewateranim' and 'uselavaanim' files should always disable use */
- /* of custom water/lava animations, even when they exist in animations.png */
- if (data.texLoc == LAVA_TEX_LOC && !alwaysLavaAnim) useLavaAnim = false;
- if (data.texLoc == WATER_TEX_LOC && !alwaysWaterAnim) useWaterAnim = false;
- continue;
- }
-
- /* Remove this animation from the list */
- for (j = i; j < anims_count - 1; j++) {
- anims_list[j] = anims_list[j + 1];
- }
- i--; anims_count--;
- }
-}
-
-static void Animations_Tick(struct ScheduledTask* task) {
- int i, size;
-
-#ifndef CC_BUILD_WEB
- if (useLavaAnim) {
- size = min(Atlas2D.TileSize, 64);
- Animations_Draw(NULL, LAVA_TEX_LOC, size);
- }
- if (useWaterAnim) {
- size = min(Atlas2D.TileSize, 64);
- Animations_Draw(NULL, WATER_TEX_LOC, size);
- }
-#endif
-
- if (!anims_count) return;
- if (!anims_bmp.Scan0) {
- Chat_AddRaw("&cCurrent texture pack specifies it uses animations,");
- Chat_AddRaw("&cbut is missing animations.png");
- anims_count = 0; return;
- }
-
- /* deferred, because when reading animations.txt, might not have read animations.png yet */
- if (!anims_validated) Animations_Validate();
- for (i = 0; i < anims_count; i++) {
- Animations_Apply(&anims_list[i]);
- }
-}
-
-
-/*########################################################################################################################*
-*--------------------------------------------------Animations component---------------------------------------------------*
-*#########################################################################################################################*/
-static void OnPackChanged(void* obj) {
- Animations_Clear();
- useLavaAnim = Animations_IsDefaultZip();
- useWaterAnim = useLavaAnim;
- alwaysLavaAnim = false;
- alwaysWaterAnim = false;
-}
-
-static void OnFileChanged(void* obj, struct Stream* stream, const String* name) {
- cc_result res;
- if (String_CaselessEqualsConst(name, "animations.png")) {
- res = Png_Decode(&anims_bmp, stream);
- if (!res) return;
-
- Logger_Warn2(res, "decoding", name);
- Mem_Free(anims_bmp.Scan0);
- anims_bmp.Scan0 = NULL;
- } else if (String_CaselessEqualsConst(name, "animations.txt")) {
- Animations_ReadDescription(stream, name);
- } else if (String_CaselessEqualsConst(name, "uselavaanim")) {
- useLavaAnim = true;
- alwaysLavaAnim = true;
- } else if (String_CaselessEqualsConst(name, "usewateranim")) {
- useWaterAnim = true;
- alwaysWaterAnim = true;
- }
-}
-
-static void Animations_Init(void) {
- ScheduledTask_Add(GAME_DEF_TICKS, Animations_Tick);
- Event_RegisterVoid(&TextureEvents.PackChanged, NULL, OnPackChanged);
- Event_RegisterEntry(&TextureEvents.FileChanged, NULL, OnFileChanged);
-}
-
-static void Animations_Free(void) {
- Animations_Clear();
- Event_UnregisterVoid(&TextureEvents.PackChanged, NULL, OnPackChanged);
- Event_UnregisterEntry(&TextureEvents.FileChanged, NULL, OnFileChanged);
-}
-
-struct IGameComponent Animations_Component = {
- Animations_Init, /* Init */
- Animations_Free /* Free */
-};
-
-
/*########################################################################################################################*
*------------------------------------------------------TerrainAtlas-------------------------------------------------------*
*#########################################################################################################################*/
diff --git a/src/TexturePack.h b/src/TexturePack.h
index f5a94cda9..7f0ca7e48 100644
--- a/src/TexturePack.h
+++ b/src/TexturePack.h
@@ -6,13 +6,11 @@
- Extracting the textures from a .zip archive
- Caching terrain atlases and texture packs to avoid redundant downloads
- Terrain atlas (including breaking it down into multiple 1D atlases)
- - Texture animations (including default water/lava ones)
- Copyright 2014-2020 ClassiCube | Licensed under BSD-3 */
+ Copyright 2014-2020 ClassiCube | Licensed under BSD-3
+*/
struct Stream;
struct HttpRequest;
-struct IGameComponent;
-extern struct IGameComponent Animations_Component;
/* Number of tiles in each row */
#define ATLAS2D_TILES_PER_ROW 16
diff --git a/src/freetype/afmodule.c b/src/freetype/afmodule.c
index ee36b5fcf..d7d8c3cf6 100644
--- a/src/freetype/afmodule.c
+++ b/src/freetype/afmodule.c
@@ -36,40 +36,6 @@
#define FT_COMPONENT trace_afmodule
- static FT_Error
- af_property_get_face_globals( FT_Face face,
- AF_FaceGlobals* aglobals,
- AF_Module module )
- {
- FT_Error error = FT_Err_Ok;
- AF_FaceGlobals globals;
-
-
- if ( !face )
- return FT_THROW( Invalid_Face_Handle );
-
- globals = (AF_FaceGlobals)face->autohint.data;
- if ( !globals )
- {
- /* trigger computation of the global style data */
- /* in case it hasn't been done yet */
- error = af_face_globals_new( face, &globals, module );
- if ( !error )
- {
- face->autohint.data =
- (FT_Pointer)globals;
- face->autohint.finalizer =
- (FT_Generic_Finalizer)af_face_globals_free;
- }
- }
-
- if ( !error )
- *aglobals = globals;
-
- return error;
- }
-
-
static const FT_ServiceDescRec af_services[] =
{
{ NULL, NULL }
diff --git a/src/freetype/ftstream.c b/src/freetype/ftstream.c
index d6202f4aa..50bbe51f9 100644
--- a/src/freetype/ftstream.c
+++ b/src/freetype/ftstream.c
@@ -374,24 +374,6 @@
}
- FT_BASE_DEF( FT_ULong )
- FT_Stream_GetUOffset( FT_Stream stream )
- {
- FT_Byte* p;
- FT_ULong result;
-
-
- FT_ASSERT( stream && stream->cursor );
-
- result = 0;
- p = stream->cursor;
- if ( p + 2 < stream->limit )
- result = FT_NEXT_UOFF3( p );
- stream->cursor = p;
- return result;
- }
-
-
FT_BASE_DEF( FT_ULong )
FT_Stream_GetULong( FT_Stream stream )
{
@@ -410,24 +392,6 @@
}
- FT_BASE_DEF( FT_ULong )
- FT_Stream_GetULongLE( FT_Stream stream )
- {
- FT_Byte* p;
- FT_ULong result;
-
-
- FT_ASSERT( stream && stream->cursor );
-
- result = 0;
- p = stream->cursor;
- if ( p + 3 < stream->limit )
- result = FT_NEXT_ULONG_LE( p );
- stream->cursor = p;
- return result;
- }
-
-
FT_BASE_DEF( FT_Char )
FT_Stream_ReadChar( FT_Stream stream,
FT_Error* error )
diff --git a/src/freetype/ftstream.h b/src/freetype/ftstream.h
index 6a5f6144e..06381437a 100644
--- a/src/freetype/ftstream.h
+++ b/src/freetype/ftstream.h
@@ -205,10 +205,6 @@ FT_BEGIN_HEADER
FT_BYTE_U32( p, 1, 8 ) | \
FT_BYTE_U32( p, 0, 0 ) )
-#define FT_PEEK_OFF3_LE( p ) FT_INT32( FT_BYTE_U32( p, 2, 16 ) | \
- FT_BYTE_U32( p, 1, 8 ) | \
- FT_BYTE_U32( p, 0, 0 ) )
-
#define FT_PEEK_UOFF3_LE( p ) FT_UINT32( FT_BYTE_U32( p, 2, 16 ) | \
FT_BYTE_U32( p, 1, 8 ) | \
FT_BYTE_U32( p, 0, 0 ) )
@@ -245,9 +241,6 @@ FT_BEGIN_HEADER
#define FT_NEXT_USHORT_LE( buffer ) \
( (unsigned short)( buffer += 2, FT_PEEK_USHORT_LE( buffer - 2 ) ) )
-#define FT_NEXT_OFF3_LE( buffer ) \
- ( (long)( buffer += 3, FT_PEEK_OFF3_LE( buffer - 3 ) ) )
-
#define FT_NEXT_UOFF3_LE( buffer ) \
( (unsigned long)( buffer += 3, FT_PEEK_UOFF3_LE( buffer - 3 ) ) )
@@ -269,16 +262,12 @@ FT_BEGIN_HEADER
#define FT_GET_BYTE() FT_GET_MACRO( BYTE )
#define FT_GET_SHORT() FT_GET_MACRO( SHORT )
#define FT_GET_USHORT() FT_GET_MACRO( USHORT )
-#define FT_GET_OFF3() FT_GET_MACRO( OFF3 )
-#define FT_GET_UOFF3() FT_GET_MACRO( UOFF3 )
#define FT_GET_LONG() FT_GET_MACRO( LONG )
#define FT_GET_ULONG() FT_GET_MACRO( ULONG )
#define FT_GET_TAG4() FT_GET_MACRO( ULONG )
#define FT_GET_SHORT_LE() FT_GET_MACRO( SHORT_LE )
#define FT_GET_USHORT_LE() FT_GET_MACRO( USHORT_LE )
-#define FT_GET_LONG_LE() FT_GET_MACRO( LONG_LE )
-#define FT_GET_ULONG_LE() FT_GET_MACRO( ULONG_LE )
#else
#define FT_GET_MACRO( func, type ) ( (type)func( stream ) )
@@ -287,16 +276,12 @@ FT_BEGIN_HEADER
#define FT_GET_BYTE() FT_GET_MACRO( FT_Stream_GetChar, FT_Byte )
#define FT_GET_SHORT() FT_GET_MACRO( FT_Stream_GetUShort, FT_Short )
#define FT_GET_USHORT() FT_GET_MACRO( FT_Stream_GetUShort, FT_UShort )
-#define FT_GET_OFF3() FT_GET_MACRO( FT_Stream_GetUOffset, FT_Long )
-#define FT_GET_UOFF3() FT_GET_MACRO( FT_Stream_GetUOffset, FT_ULong )
#define FT_GET_LONG() FT_GET_MACRO( FT_Stream_GetULong, FT_Long )
#define FT_GET_ULONG() FT_GET_MACRO( FT_Stream_GetULong, FT_ULong )
#define FT_GET_TAG4() FT_GET_MACRO( FT_Stream_GetULong, FT_ULong )
#define FT_GET_SHORT_LE() FT_GET_MACRO( FT_Stream_GetUShortLE, FT_Short )
#define FT_GET_USHORT_LE() FT_GET_MACRO( FT_Stream_GetUShortLE, FT_UShort )
-#define FT_GET_LONG_LE() FT_GET_MACRO( FT_Stream_GetULongLE, FT_Long )
-#define FT_GET_ULONG_LE() FT_GET_MACRO( FT_Stream_GetULongLE, FT_ULong )
#endif
#define FT_READ_MACRO( func, type, var ) \
@@ -418,10 +403,6 @@ FT_BEGIN_HEADER
FT_BASE( FT_UShort )
FT_Stream_GetUShort( FT_Stream stream );
- /* read a 24-bit big-endian unsigned integer from an entered frame */
- FT_BASE( FT_ULong )
- FT_Stream_GetUOffset( FT_Stream stream );
-
/* read a 32-bit big-endian unsigned integer from an entered frame */
FT_BASE( FT_ULong )
FT_Stream_GetULong( FT_Stream stream );
@@ -430,10 +411,6 @@ FT_BEGIN_HEADER
FT_BASE( FT_UShort )
FT_Stream_GetUShortLE( FT_Stream stream );
- /* read a 32-bit little-endian unsigned integer from an entered frame */
- FT_BASE( FT_ULong )
- FT_Stream_GetULongLE( FT_Stream stream );
-
/* read a byte from a stream */
FT_BASE( FT_Char )