From 9a7ac49b7e3b7cd4a09b016ceec116830a3458f9 Mon Sep 17 00:00:00 2001
From: Daniel Karbach <daniel.karbach@localhorst.tv>
Date: Tue, 19 Feb 2013 18:59:02 +0100
Subject: [PATCH] added basic defeat state

refs #4
---
 src/battle/BattleState.cpp        |   8 +-
 src/battle/Resources.cpp          |   2 +
 src/battle/Resources.h            |   1 +
 src/battle/states/DefeatState.cpp | 111 ++++++++++++++++++++++++++
 src/battle/states/DefeatState.h   |  54 +++++++++++++
 src/sdl/Desaturate.cpp            | 127 ++++++++++++++++++++++++++++++
 src/sdl/Desaturate.h              |  16 ++++
 test-data/test.l2s                |   1 +
 8 files changed, 319 insertions(+), 1 deletion(-)
 create mode 100644 src/battle/states/DefeatState.cpp
 create mode 100644 src/battle/states/DefeatState.h
 create mode 100644 src/sdl/Desaturate.cpp
 create mode 100644 src/sdl/Desaturate.h

diff --git a/src/battle/BattleState.cpp b/src/battle/BattleState.cpp
index 1f68acf..c6f07cd 100644
--- a/src/battle/BattleState.cpp
+++ b/src/battle/BattleState.cpp
@@ -1,6 +1,7 @@
 #include "BattleState.h"
 
 #include "PartyLayout.h"
+#include "states/DefeatState.h"
 #include "states/SelectMoveAction.h"
 #include "states/PerformAttacks.h"
 #include "states/VictoryState.h"
@@ -129,7 +130,12 @@ void BattleState::OnResumeState(SDL_Surface *screen) {
 		return;
 	}
 	if (battle.Defeat()) {
-		Ctrl().PopState();
+		if (alreadyPushed) {
+			Ctrl().PopState();
+		} else {
+			Ctrl().PushState(new DefeatState(&battle, this));
+			alreadyPushed = true;
+		}
 		return;
 	}
 	// TODO: this should not push a state while quitting
diff --git a/src/battle/Resources.cpp b/src/battle/Resources.cpp
index 9db29e1..3e61cf5 100644
--- a/src/battle/Resources.cpp
+++ b/src/battle/Resources.cpp
@@ -61,6 +61,7 @@ Resources::Resources()
 , noEquipmentText("MISSING TRANSLATION")
 
 , escapeText("MISSING TRANSLATION")
+, defeatText("MISSING TRANSLATION")
 
 , numberAnimationPrototype(0)
 , bigNumberSprite(0)
@@ -152,6 +153,7 @@ void Resources::CreateTypeDescription() {
 	td.AddField("noEquipmentText", FieldDescription(((char *)&r.noEquipmentText) - ((char *)&r), Interpreter::STRING_ID).SetReferenced().SetDescription("text to show for missing equipment"));
 
 	td.AddField("escapeText", FieldDescription(((char *)&r.escapeText) - ((char *)&r), Interpreter::STRING_ID).SetReferenced().SetDescription("displayed when the party escapes"));
+	td.AddField("defeatText", FieldDescription(((char *)&r.defeatText) - ((char *)&r), Interpreter::STRING_ID).SetReferenced().SetDescription("displayed when the party was defeated"));
 
 	td.AddField("numberAnimationPrototype", FieldDescription(((char *)&r.numberAnimationPrototype) - ((char *)&r), Animation::TYPE_ID).SetReferenced().SetDescription("animation of an attack's result digit"));
 
diff --git a/src/battle/Resources.h b/src/battle/Resources.h
index 0a8a1e8..6624910 100644
--- a/src/battle/Resources.h
+++ b/src/battle/Resources.h
@@ -62,6 +62,7 @@ struct Resources {
 	const char *noEquipmentText;
 
 	const char *escapeText;
+	const char *defeatText;
 
 	const graphics::Animation *numberAnimationPrototype;
 
diff --git a/src/battle/states/DefeatState.cpp b/src/battle/states/DefeatState.cpp
new file mode 100644
index 0000000..77b3f8e
--- /dev/null
+++ b/src/battle/states/DefeatState.cpp
@@ -0,0 +1,111 @@
+#include "DefeatState.h"
+
+#include "../Battle.h"
+#include "../BattleState.h"
+#include "../Resources.h"
+#include "../../app/Application.h"
+#include "../../app/Input.h"
+#include "../../math/Vector.h"
+#include "../../graphics/ColorFade.h"
+#include "../../graphics/Font.h"
+#include "../../graphics/Frame.h"
+#include "../../sdl/Desaturate.h"
+
+using app::Application;
+using app::Input;
+using graphics::ColorFade;
+using graphics::Font;
+using graphics::Frame;
+using math::Vector;
+using std::vector;
+
+namespace battle {
+
+DefeatState::DefeatState(
+		Battle *battle,
+		BattleState *parent)
+: battle(battle)
+, parent(parent)
+, cache(0)
+, format(0) {
+
+}
+
+
+void DefeatState::OnEnterState(SDL_Surface *screen) {
+	timer = GraphicsTimers().StartCountdown(1500);
+	format = screen->format;
+	OnResize(screen->w, screen->h);
+}
+
+void DefeatState::OnExitState(SDL_Surface *screen) {
+	if (cache) {
+		SDL_FreeSurface(cache);
+		cache = 0;
+	}
+}
+
+void DefeatState::OnResumeState(SDL_Surface *screen) {
+
+}
+
+void DefeatState::OnPauseState(SDL_Surface *screen) {
+
+}
+
+
+void DefeatState::OnResize(int width, int height) {
+	const Resources &res = parent->Res();
+	const Frame &frame = *res.titleFrame;
+	const Font &font = *res.titleFont;
+
+	framePosition = parent->ScreenOffset();
+	frameSize = Vector<int> (
+			parent->Width(),
+			frame.BorderHeight() * 2 + font.CharHeight());
+
+	textPosition = Vector<int>(
+			(parent->Width() - strlen(res.defeatText) * font.CharWidth()) / 2,
+			frame.BorderHeight());
+
+	parent->Resize(width, height);
+	if (cache) {
+		SDL_FreeSurface(cache);
+	}
+	cache = SDL_CreateRGBSurface(0, width, height, format->BitsPerPixel,
+			format->Rmask, format->Gmask, format->Bmask, format->Amask);
+	parent->Render(cache);
+	parent->RenderHeroes(cache);
+	RenderTitleBar(cache);
+}
+
+
+void DefeatState::HandleEvents(const Input &input) {
+	if (timer.Finished()) {
+		Ctrl().PopState();
+	}
+}
+
+
+void DefeatState::UpdateWorld(Uint32 deltaT) {
+
+}
+
+void DefeatState::Render(SDL_Surface *screen) {
+	if (screen->format->palette
+			|| cache->format->palette) {
+		// I refuse to desaturate an indexed color urface
+		SDL_BlitSurface(cache, 0, screen, 0);
+		return;
+	}
+
+	sdl::Desaturate(cache, screen, timer.Remaining() * 255 / 1500);
+}
+
+void DefeatState::RenderTitleBar(SDL_Surface *screen) {
+	const Resources &res = parent->Res();
+	res.titleFrame->Draw(screen, framePosition, frameSize.X(), frameSize.Y());
+	res.titleFont->DrawString(res.defeatText, screen, textPosition);
+}
+
+}
diff --git a/src/battle/states/DefeatState.h b/src/battle/states/DefeatState.h
new file mode 100644
index 0000000..998bff4
--- /dev/null
+++ b/src/battle/states/DefeatState.h
@@ -0,0 +1,54 @@
+#ifndef BATTLE_DEFEATSTATE_H_
+#define BATTLE_DEFEATSTATE_H_
+
+namespace battle {
+	class Battle;
+	class BattleState;
+}
+
+#include "../../app/State.h"
+#include "../../math/Vector.h"
+
+#include <SDL.h>
+
+namespace battle {
+
+class DefeatState
+: public app::State {
+
+public:
+	DefeatState(
+			Battle *battle,
+			BattleState *parent);
+
+public:
+	virtual void HandleEvents(const app::Input &);
+	virtual void UpdateWorld(Uint32 deltaT);
+	virtual void Render(SDL_Surface *);
+
+private:
+	virtual void OnEnterState(SDL_Surface *screen);
+	virtual void OnExitState(SDL_Surface *screen);
+	virtual void OnResumeState(SDL_Surface *screen);
+	virtual void OnPauseState(SDL_Surface *screen);
+
+	virtual void OnResize(int width, int height);
+
+private:
+	void RenderTitleBar(SDL_Surface *screen);
+
+private:
+	Battle *battle;
+	BattleState *parent;
+	SDL_Surface *cache;
+	SDL_PixelFormat *format;
+	app::Timer<Uint32> timer;
+	math::Vector<int> framePosition;
+	math::Vector<int> frameSize;
+	math::Vector<int> textPosition;
+
+};
+
+}
+
+#endif
diff --git a/src/sdl/Desaturate.cpp b/src/sdl/Desaturate.cpp
new file mode 100644
index 0000000..175e1b9
--- /dev/null
+++ b/src/sdl/Desaturate.cpp
@@ -0,0 +1,127 @@
+#include "Desaturate.h"
+
+#include <stdexcept>
+
+using std::runtime_error;
+
+
+namespace sdl {
+
+template<typename SrcType, typename DestType>
+void DesaturatePixel(
+		SrcType *src, SDL_PixelFormat *srcFmt,
+		DestType *dest, SDL_PixelFormat *destFmt,
+		Uint8 amount) {
+
+	Uint8 srcRed = (((*src) & srcFmt->Rmask)
+			>> srcFmt->Rshift) << srcFmt->Rloss;
+	Uint8 srcGreen = (((*src) & srcFmt->Gmask)
+			>> srcFmt->Gshift) << srcFmt->Gloss;
+	Uint8 srcBlue = (((*src) & srcFmt->Bmask)
+			>> srcFmt->Bshift) << srcFmt->Bloss;
+
+	Uint8 srcGrey = (srcRed * 76 + srcGreen * 150 + srcBlue * 29) / 255;
+
+	Uint8 destRed = (srcRed * amount + srcGrey * (255 - amount)) / 255;
+	Uint8 destGreen = (srcGreen * amount + srcGrey * (255 - amount)) / 255;
+	Uint8 destBlue = (srcBlue * amount + srcGrey * (255 - amount)) / 255;
+
+	*dest
+		= ((destRed >> destFmt->Rloss) << destFmt->Rshift)
+		| ((destGreen >> destFmt->Gloss) << destFmt->Gshift)
+		| ((destBlue >> destFmt->Bloss) << destFmt->Bshift)
+		| (*dest & destFmt->Amask);
+}
+
+void Desaturate(
+		SDL_Surface *src,
+		SDL_Surface *dest,
+		Uint8 amount) {
+	Uint32 size = src->w * src->h;
+	SDL_LockSurface(src);
+	SDL_LockSurface(dest);
+	SDL_PixelFormat *srcFmt = src->format;
+	SDL_PixelFormat *destFmt = dest->format;
+	Uint8 srcBPP = srcFmt->BytesPerPixel;
+	Uint8 destBPP = destFmt->BytesPerPixel;
+	switch (srcBPP) {
+	default:
+		SDL_UnlockSurface(dest);
+		SDL_UnlockSurface(src);
+		throw runtime_error("unable to read src format");
+	case 2: {
+		Uint16 *srcIter = reinterpret_cast<Uint16 *>(
+				src->pixels);
+		Uint16 *srcEnd = srcIter + size;
+		switch (destBPP) {
+		default:
+			SDL_UnlockSurface(dest);
+			SDL_UnlockSurface(src);
+			throw runtime_error("unable to read src format");
+		case 2: {
+			Uint16 *destIter = reinterpret_cast<Uint16 *>(
+					dest->pixels);
+			Uint16 *destEnd = destIter + size;
+			for (;srcIter < srcEnd && destIter < destEnd;
+					++srcIter, ++destIter) {
+				DesaturatePixel(
+						srcIter, srcFmt,
+						destIter, destFmt,
+						amount);
+			}
+			break; }
+		case 4: {
+			Uint32 *destIter = reinterpret_cast<Uint32 *>(
+					dest->pixels);
+			Uint32 *destEnd = destIter + size;
+			for (;srcIter < srcEnd && destIter < destEnd;
+					++srcIter, ++destIter) {
+				DesaturatePixel(
+						srcIter, srcFmt,
+						destIter, destFmt,
+						amount);
+			}
+			break; }
+		}
+		break; }
+	case 4: {
+		Uint32 *srcIter = reinterpret_cast<Uint32 *>(
+				src->pixels);
+		Uint32 *srcEnd = srcIter + size;
+		switch (destBPP) {
+		default:
+			SDL_UnlockSurface(dest);
+			SDL_UnlockSurface(src);
+			throw runtime_error("unable to read src format");
+		case 2: {
+			Uint16 *destIter = reinterpret_cast<Uint16 *>(
+					dest->pixels);
+			Uint16 *destEnd = destIter + size;
+			for (;srcIter < srcEnd && destIter < destEnd;
+					++srcIter, ++destIter) {
+				DesaturatePixel(
+						srcIter, srcFmt,
+						destIter, destFmt,
+						amount);
+			}
+			break; }
+		case 4: {
+			Uint32 *destIter = reinterpret_cast<Uint32 *>(
+					dest->pixels);
+			Uint32 *destEnd = destIter + size;
+			for (;srcIter < srcEnd && destIter < destEnd;
+					++srcIter, ++destIter) {
+				DesaturatePixel(
+						srcIter, srcFmt,
+						destIter, destFmt,
+						amount);
+			}
+			break; }
+		}
+		break; }
+	}
+	SDL_UnlockSurface(dest);
+	SDL_UnlockSurface(src);
+}
+
+}
diff --git a/src/sdl/Desaturate.h b/src/sdl/Desaturate.h
new file mode 100644
index 0000000..45dff7c
--- /dev/null
+++ b/src/sdl/Desaturate.h
@@ -0,0 +1,16 @@
+#ifndef SDL_DESATURATE_H_
+#define SDL_DESATURATE_H_
+
+#include <SDL.h>
+
+namespace sdl {
+
+/// Copy src to dest, desaturated by amount/255.
+void Desaturate(
+		SDL_Surface *src,
+		SDL_Surface *dest,
+		Uint8 amount);
+
+}
+
+#endif
diff --git a/test-data/test.l2s b/test-data/test.l2s
index 500039f..59de524 100644
--- a/test-data/test.l2s
+++ b/test-data/test.l2s
@@ -651,6 +651,7 @@ export BattleResources battleResources {
 	noEquipmentText: "No equip",
 
 	escapeText: "Escapes.",
+	defeatText: "Total Defeat.",
 
 	victoryGetsText: "Gets",
 	victoryExpText: "EXP",
-- 
2.39.5