From 148fc8a037c0ae224bbe2cc1917dab1c12e89606 Mon Sep 17 00:00:00 2001
From: Tim Felgentreff <timfelgentreff@gmail.com>
Date: Mon, 25 Apr 2022 21:54:03 +0200
Subject: [PATCH] apply a shearing effect to generated shadows

---
 src/include/video.h          |  2 +-
 src/stratagus/construct.cpp  |  2 +-
 src/unit/script_unittype.cpp | 14 +++++++
 src/unit/unittype.cpp        |  2 +-
 src/video/graphic.cpp        | 81 +++++++++++++++++++++++++++++-------
 5 files changed, 83 insertions(+), 18 deletions(-)

diff --git a/src/include/video.h b/src/include/video.h
index 8be1aae60..9d081f401 100644
--- a/src/include/video.h
+++ b/src/include/video.h
@@ -169,7 +169,7 @@ public:
 	void SetOriginalSize();
 	bool TransparentPixel(int x, int y);
 	void SetPaletteColor(int idx, int r, int g, int b);
-	void MakeShadow();
+	void MakeShadow(int xOffset, int yOffset);
 
 	// minor programmatic editing features
 	void OverlayGraphic(CGraphic *other, bool mask = false);
diff --git a/src/stratagus/construct.cpp b/src/stratagus/construct.cpp
index e1b72f759..219545680 100644
--- a/src/stratagus/construct.cpp
+++ b/src/stratagus/construct.cpp
@@ -108,7 +108,7 @@ void CConstruction::Load()
 		this->ShadowSprite = CGraphic::ForceNew(file, this->ShadowWidth, this->ShadowHeight);
 		this->ShadowSprite->Load();
 		this->ShadowSprite->Flip();
-		this->ShadowSprite->MakeShadow();
+		this->ShadowSprite->MakeShadow(0, 0); // FIXME: TODO: (timfel): construction shadows don't have X offset, so no shear?
 	}
 }
 
diff --git a/src/unit/script_unittype.cpp b/src/unit/script_unittype.cpp
index d79dfdcfa..5788bec17 100644
--- a/src/unit/script_unittype.cpp
+++ b/src/unit/script_unittype.cpp
@@ -485,6 +485,9 @@ static int CclDefineUnitType(lua_State *l)
 	// Slot identifier
 	const char *str = LuaToString(l, 1);
 	CUnitType *type = UnitTypeByIdent(str);
+
+	constexpr int redefineSprite = 2;
+
 	int redefine;
 	if (type) {
 		redefine = 1;
@@ -524,13 +527,20 @@ static int CclDefineUnitType(lua_State *l)
 			}
 			if (redefine) {
 				if (type->Sprite && type->Sprite->File != type->File) {
+					redefine |= redefineSprite;
 					CGraphic::Free(type->Sprite);
 					type->Sprite = NULL;
 				}
 				if (type->AltSprite && type->AltSprite->File != type->AltFile) {
+					redefine |= redefineSprite;
 					CGraphic::Free(type->AltSprite);
 					type->AltSprite = NULL;
 				}
+				if (redefine && type->ShadowSprite) {
+					redefine |= redefineSprite;
+					CGraphic::Free(type->ShadowSprite);
+					type->ShadowSprite = NULL;
+				}
 			}
 			if (type->ShadowFile == shadowMarker) {
 				type->ShadowFile = type->File;
@@ -575,6 +585,7 @@ static int CclDefineUnitType(lua_State *l)
 				}
 			}
 			if (redefine && type->ShadowSprite) {
+				redefine |= redefineSprite;
 				CGraphic::Free(type->ShadowSprite);
 				type->ShadowSprite = NULL;
 			}
@@ -1210,6 +1221,9 @@ static int CclDefineUnitType(lua_State *l)
 	}
 	UpdateDefaultBoolFlags(*type);
 	if (!CclInConfigFile) {
+		if (redefine & redefineSprite) {
+			LoadUnitTypeSprite(*type);
+		}
 		UpdateUnitStats(*type, 1);
 	}
 	return 0;
diff --git a/src/unit/unittype.cpp b/src/unit/unittype.cpp
index 1282cdcb7..c2eea6277 100644
--- a/src/unit/unittype.cpp
+++ b/src/unit/unittype.cpp
@@ -994,7 +994,7 @@ void LoadUnitTypeSprite(CUnitType &type)
 			if (type.Flip) {
 				type.ShadowSprite->Flip();
 			}
-			type.ShadowSprite->MakeShadow();
+			type.ShadowSprite->MakeShadow(type.ShadowOffsetX, type.ShadowOffsetY);
 		}
 	}
 
diff --git a/src/video/graphic.cpp b/src/video/graphic.cpp
index 426b4a6b2..7c0404cf2 100644
--- a/src/video/graphic.cpp
+++ b/src/video/graphic.cpp
@@ -1154,24 +1154,75 @@ static inline void dither(SDL_Surface *Surface) {
 **
 **  @todo FIXME: 32bpp
 */
-void CGraphic::MakeShadow()
+void CGraphic::MakeShadow(int xOffset, int yOffset)
 {
-	if (Surface->format->BytesPerPixel == 1) {
-		SDL_Surface *alphaSurface = SDL_CreateRGBSurface(0, Surface->w, Surface->h, 32, RMASK, GMASK, BMASK, AMASK);
-		SDL_BlitSurface(Surface, NULL, alphaSurface, NULL);
-		SDL_SetSurfaceAlphaMod(alphaSurface, 80);
-		SDL_SetSurfaceColorMod(alphaSurface, 0, 0, 0);
-		SDL_FreeSurface(Surface);
-		Surface = alphaSurface;
+	VideoPaletteListRemove(Surface);
+	SDL_Surface *alphaSurface = SDL_CreateRGBSurface(0, Surface->w, Surface->h, 32, RMASK, GMASK, BMASK, AMASK);
+	SDL_BlitSurface(Surface, NULL, alphaSurface, NULL);
+	SDL_SetSurfaceAlphaMod(alphaSurface, 80);
+	SDL_SetSurfaceColorMod(alphaSurface, 0, 0, 0);
+	SDL_FreeSurface(Surface);
+	Surface = alphaSurface;
+	if (SurfaceFlip) {
+		VideoPaletteListRemove(SurfaceFlip);
+		SDL_Surface *alphaSurfaceFlip = SDL_CreateRGBSurface(0, SurfaceFlip->w, SurfaceFlip->h, 32, RMASK, GMASK, BMASK, AMASK);
+		SDL_BlitSurface(SurfaceFlip, NULL, alphaSurfaceFlip, NULL);
+		SDL_SetSurfaceAlphaMod(alphaSurfaceFlip, 80);
+		SDL_SetSurfaceColorMod(alphaSurfaceFlip, 0, 0, 0);
+		SDL_FreeSurface(SurfaceFlip);
+		SurfaceFlip = alphaSurfaceFlip;
+	}
 
-		if (SurfaceFlip) {
-			SDL_Surface *alphaSurfaceFlip = SDL_CreateRGBSurface(0, SurfaceFlip->w, SurfaceFlip->h, 32, RMASK, GMASK, BMASK, AMASK);
-			SDL_BlitSurface(SurfaceFlip, NULL, alphaSurfaceFlip, NULL);
-			SDL_SetSurfaceAlphaMod(alphaSurfaceFlip, 80);
-			SDL_SetSurfaceColorMod(alphaSurfaceFlip, 0, 0, 0);
-			SDL_FreeSurface(SurfaceFlip);
-			SurfaceFlip = alphaSurfaceFlip;
+	// Apply shearing effect. same angle for Surface and SurfaceFlip!
+	// The sun shines from the same angle on to both normal and flipped sprites :)
+
+	// BEGIN HACK: XXX: FIXME: positive yOffset is used for fliers for now, these should not get shearing.
+	// We need to find a better way to communicate that. The rest of the code already supports shearing
+	// in both directions for y.
+	yOffset = std::min(0, yOffset);
+	// END HACK
+
+	if (yOffset || xOffset) {
+		SDL_LockSurface(Surface);
+		uint32_t* pixels = (uint32_t *)Surface->pixels;
+		int pitch = Surface->pitch / sizeof(uint32_t);
+		for (int f = 0; f < NumFrames; f++) {
+			int frameX = frame_map[f].x;
+			int frameY = frame_map[f].y;
+			for (int x = xOffset > 0 ? 0 : Width - 1; xOffset > 0 ? x < Width : x >= 0; xOffset > 0 ? x++ : x--) {
+				for (int y = yOffset > 0 ? 0 : Height - 1; yOffset > 0 ? y < Height : y >= 0; yOffset > 0 ? y++ : y--) {
+					int xNew = x + xOffset * y / Height;
+					int yNew = y + yOffset * x / Width;
+					if (xNew < 0 || yNew < 0 || xNew >= Width || yNew >= Height) {
+						pixels[x + frameX + (y + frameY) * pitch] = 0;
+					} else {
+						pixels[x + frameX + (y + frameY) * pitch] = pixels[xNew + frameX + (yNew + frameY) * pitch];
+					}
+				}
+			}
 		}
+		SDL_UnlockSurface(Surface);
+	}
+	if ((yOffset || xOffset) && SurfaceFlip) {
+		SDL_LockSurface(SurfaceFlip);
+		uint32_t* pixels = (uint32_t *)SurfaceFlip->pixels;
+		int pitch = SurfaceFlip->pitch / sizeof(uint32_t);
+		for (int f = 0; f < NumFrames; f++) {
+			int frameX = frameFlip_map[f].x;
+			int frameY = frameFlip_map[f].y;
+			for (int x = xOffset > 0 ? 0 : Width - 1; xOffset > 0 ? x < Width : x >= 0; xOffset > 0 ? x++ : x--) {
+				for (int y = yOffset > 0 ? 0 : Height - 1; yOffset > 0 ? y < Height : y >= 0; yOffset > 0 ? y++ : y--) {
+					int xNew = x + xOffset * y / Height;
+					int yNew = y + yOffset * x / Width;
+					if (xNew < 0 || yNew < 0 || xNew >= Width || yNew >= Height) {
+						pixels[x + frameX + (y + frameY) * pitch] = 0;
+					} else {
+						pixels[x + frameX + (y + frameY) * pitch] = pixels[xNew + frameX + (yNew + frameY) * pitch];
+					}
+				}
+			}
+		}
+		SDL_UnlockSurface(SurfaceFlip);
 	}
 }