From e3badbe07482abcaf37ef42490a8aacc39bafdb6 Mon Sep 17 00:00:00 2001
From: Kalle Olavi Niemitalo <kon@iki.fi>
Date: Sun, 27 Jun 2010 18:13:30 +0300
Subject: [PATCH] Morel spores preview

This code is not of good quality.
---
 engine/action/action_still.cpp     |   38 +++++++++++++
 engine/include/particle.h          |   12 ++++
 engine/particle/smokeparticle.cpp  |  104 ++++++++++++++++++++++++++++++++++++
 graphics/neutral/white-spore-4.png |  Bin 0 -> 144 bytes
 scripts/boswars.lua                |    2 +-
 units/morels/unit-morels.lua       |    7 +++
 6 files changed, 162 insertions(+), 1 deletions(-)
 create mode 100644 graphics/neutral/white-spore-4.png

diff --git a/engine/action/action_still.cpp b/engine/action/action_still.cpp
index 334ac60..0e15db3 100644
--- a/engine/action/action_still.cpp
+++ b/engine/action/action_still.cpp
@@ -34,6 +34,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <memory>
 
 #include "stratagus.h"
 #include "missile.h"
@@ -43,6 +44,7 @@
 #include "unit.h"
 #include "unit_cache.h"
 #include "map.h"
+#include "particle.h"
 #include "pathfinder.h"
 #include "spells.h"
 #include "player.h"
@@ -184,6 +186,38 @@ static void AutoAttack(CUnit *unit, bool stand_ground)
 	Assert(!unit->Orders[0]->Goal);
 }
 
+static void ReleaseSpore(CUnit *unit)
+{
+	const int cost = 10;
+	if (unit->Variable[MANA_INDEX].Value < cost)
+		return;
+
+	static CGraphic *sporeGraphic;
+	CPosition pos(unit->X * TileSizeX + unit->IX + TileSizeX / 2 + (MyRand() % 9) - 4,
+		      unit->Y * TileSizeY + unit->IY + (MyRand() % 9) - 4);
+	double dx, dy;
+
+	CWindyParticle::GetWindSpeed(pos, &dx, &dy);
+	if ((dx * dx + dy * dy) * unit->Variable[MANA_INDEX].Value < 1.0
+	    && unit->Variable[MANA_INDEX].Value < unit->Variable[MANA_INDEX].Max)
+		return;
+
+	if (sporeGraphic == NULL) {
+		sporeGraphic = CGraphic::New("graphics/neutral/white-spore-4.png", 4, 4);
+		sporeGraphic->Load();
+	}
+
+	unit->Variable[MANA_INDEX].Value -= cost;
+	std::auto_ptr<Animation> particleAnimation(
+		new GraphicAnimation(sporeGraphic, 1500));
+	std::auto_ptr<CParticle> particle(
+		new CWindyParticle(pos, particleAnimation.get()));
+	particleAnimation.release();
+	ParticleManager.add(particle.get());
+	particle.release();
+}
+
+
 /**
 **  Unit stands still or stand ground.
 **
@@ -212,6 +246,10 @@ void ActionStillGeneric(CUnit *unit, bool stand_ground)
 		return;
 	}
 
+	if (strstr(unit->Type->Ident.c_str(), "morel")) {
+		ReleaseSpore(unit);
+	}
+
 	if (AutoCast(unit) || AutoRepair(unit)) {
 		return;
 	}
diff --git a/engine/include/particle.h b/engine/include/particle.h
index f6c7c38..ff53cd0 100644
--- a/engine/include/particle.h
+++ b/engine/include/particle.h
@@ -165,6 +165,18 @@ protected:
 };
 
 
+class CWindyParticle : public CSmokeParticle
+{
+public:
+	CWindyParticle(CPosition position, Animation *animation);
+
+	virtual void update(int ticks);
+	virtual CParticle * clone();
+
+	static void GetWindSpeed(CPosition position, double *dx, double *dy);
+};
+
+
 class CParticleManager
 {
 public:
diff --git a/engine/particle/smokeparticle.cpp b/engine/particle/smokeparticle.cpp
index 586f312..f4b5e4d 100644
--- a/engine/particle/smokeparticle.cpp
+++ b/engine/particle/smokeparticle.cpp
@@ -30,6 +30,7 @@
 #include "stratagus.h"
 #include "particle.h"
 #include "video.h"
+#include <cmath>
 
 
 
@@ -70,3 +71,106 @@ CParticle* CSmokeParticle::clone()
 }
 
 //@}
+
+CWindyParticle::CWindyParticle(CPosition position, Animation *flake) :
+	CSmokeParticle(position, flake)
+{
+}
+
+void CWindyParticle::update(int ticks)
+{
+	puff->update(ticks);
+	if (puff->isFinished()) {
+		destroy();
+		return;
+	}
+
+	double dx, dy;
+	GetWindSpeed(this->pos, &dx, &dy);
+
+	this->pos.x += (dx * ticks + (MyRand() % 201 - 100) / 200.0);
+	this->pos.y += (dy * ticks + (MyRand() % 201 - 100) / 200.0);
+}
+
+CParticle* CWindyParticle::clone()
+{
+	return new CWindyParticle(pos, puff);
+}
+
+void CWindyParticle::GetWindSpeed(CPosition position,
+				  double *dx, double *dy)
+{
+	using std::sin;
+	using std::cos;
+
+	static struct {
+		unsigned long GameCycle;
+		double uOffset;
+		double vOffset;
+		double a, b, c, d;
+	} cache = { -1UL };
+
+	if (cache.GameCycle != GameCycle) {
+		cache.GameCycle = GameCycle;
+		cache.uOffset
+			= sin(GameCycle / 15.0 + 5) * 0.09
+			- sin(GameCycle / 72.0 + 4) * 0.2
+			+ sin(GameCycle / 120.0 + 7) * 0.8
+			- sin(GameCycle / 321.0 + 3) * 1.7
+			+ sin(GameCycle / 937.0 + 11) * 2.5;
+		cache.vOffset
+			= cos(GameCycle / 15.0 + 5) * 0.09
+			- cos(GameCycle / 72.0 + 4) * 0.2
+			+ cos(GameCycle / 120.0 + 7) * 0.8
+			- cos(GameCycle / 321.0 + 3) * 1.7
+			+ cos(GameCycle / 937.0 + 11) * 2.5;
+		cache.a = sin(GameCycle / 23.0 + 9) * 0.3
+			- sin(GameCycle / 79.0 + 22) * 0.5
+			+ sin(GameCycle / 251.0 + 6) * 0.8
+			- sin(GameCycle / 614.0 + 8) * 1.3;
+		cache.b = cos(GameCycle / 23.0 + 9) * 0.3
+			- cos(GameCycle / 79.0 + 22) * 0.5
+			+ cos(GameCycle / 251.0 + 6) * 0.8
+			- cos(GameCycle / 614.0 + 8) * 1.3;
+		cache.c = sin(GameCycle / 129.0 + 12) * 0.009
+			- sin(GameCycle / 367.0 + 2) * 0.011
+			+ sin(GameCycle / 598.0 + 14) * 0.013
+			- sin(GameCycle / 1156.0 + 18) * 0.015
+			+ sin(GameCycle / 3384.0 + 1) * 0.017
+			- sin(GameCycle / 8913.0 + 13) * 0.019;
+		cache.d = cos(GameCycle / 129.0 + 12) * 0.009
+			- cos(GameCycle / 367.0 + 2) * 0.011
+			+ cos(GameCycle / 598.0 + 14) * 0.013
+			- cos(GameCycle / 1156.0 + 18) * 0.015
+			+ cos(GameCycle / 3384.0 + 1) * 0.017
+			- cos(GameCycle / 8913.0 + 13) * 0.019;
+	}
+
+	double u = position.x / 400.0 + cache.uOffset;
+	double v = position.y / 400.0 + cache.vOffset;
+
+	// To get a divergence-free vector field on a plane: first
+	// construct a scalar field, then take its gradient, and
+	// finally rotate the gradient 90 degrees at each point.
+	//
+	// If you think of the scalar field as a height map, start
+	// from some point, and follow the vectors, then you traverse
+	// through a path of constant height and either return where
+	// you started or continue to infinity.  In this time-step
+	// simulation though, errors accumulate into a centrifugal
+	// force of sorts, and you won't stay on the path.
+	//
+	// Here, the scalar field H is pretty simple, but still
+	// complex enough to result in wind patterns that don't look
+	// entirely artificial.
+	//
+	// H = sin(u + a * cos(v)) + cos(v + b * sin(u))
+	// H' = cos(u + a * cos(v)) * (u' - a * sin(v) * v')
+	//    - sin(v + b * sin(u)) * (v' + b * cos(u) * u')
+	// grad(H) = (cos(u + a * cos(v)) - sin(v + b * sin(u)) * (b * cos(u)),
+	//            cos(u + a * cos(v)) * (-a * sin(v)) - sin(v + b * sin(u)))
+
+	*dx = cache.d + (cos(u + cache.a * cos(v)) * (-cache.a * sin(v)) - sin(v + cache.b * sin(u))) / 16;
+	*dy = cache.c - (cos(u + cache.a * cos(v)) - sin(v + cache.b * sin(u)) * (cache.b * cos(u))) / 16;
+}
+
diff --git a/graphics/neutral/white-spore-4.png b/graphics/neutral/white-spore-4.png
new file mode 100644
index 0000000000000000000000000000000000000000..7c37130fac009cb397a8a1f6031c819e3f619d5c
GIT binary patch
diff --git a/scripts/boswars.lua b/scripts/boswars.lua
index ca45694..39a6543 100644
--- a/scripts/boswars.lua
+++ b/scripts/boswars.lua
@@ -78,7 +78,7 @@ DefineDecorations({Index = "HitPoints", HideNeutral = true, CenterX = true,
   ShowWhenMax = false})
 
 -- ManaDot
-DefineDecorations({Index = "Mana", HideNeutral = true, CenterX = true,
+DefineDecorations({Index = "Mana", HideNeutral = false, CenterX = true,
   OffsetPercent = {50, 100},Method = {"sprite", {"sprite-mana"}},
   ShowWhenMax = false})
 DefineDecorations({Index = "Transport", HideNeutral = true, CenterX = true,
diff --git a/units/morels/unit-morels.lua b/units/morels/unit-morels.lua
index 357648c..b414f6f 100644
--- a/units/morels/unit-morels.lua
+++ b/units/morels/unit-morels.lua
@@ -51,6 +51,7 @@ DefineUnitType("unit-morel-1", {
     Icon = "icon-morel-1",
     Construction = "construction-morels",
     HitPoints = 50,
+    MaxMana = 100,
     DrawLevel = 25,
     VisibleUnderFog = true,
     NeutralMinimapColor = {196, 196, 196},
@@ -90,6 +91,7 @@ DefineUnitType("unit-morel-2", {
     Icon = "icon-morel-2",
     Construction = "construction-morels",
     HitPoints = 50,
+    MaxMana = 100,
     DrawLevel = 25,
     VisibleUnderFog = true,
     NeutralMinimapColor = {196, 196, 196},
@@ -129,6 +131,7 @@ DefineUnitType("unit-morel-3", {
     Icon = "icon-morel-3",
     Construction = "construction-morels",
     HitPoints = 50,
+    MaxMana = 100,
     DrawLevel = 25,
     VisibleUnderFog = true,
     NeutralMinimapColor = {196, 196, 196},
@@ -168,6 +171,7 @@ DefineUnitType("unit-morel-4", {
     Icon = "icon-morel-4",
     Construction = "construction-morels",
     HitPoints = 50,
+    MaxMana = 100,
     DrawLevel = 25,
     VisibleUnderFog = true,
     NeutralMinimapColor = {196, 196, 196},
@@ -207,6 +211,7 @@ DefineUnitType("unit-morel-5", {
     Icon = "icon-morel-5",
     Construction = "construction-morels",
     HitPoints = 50,
+    MaxMana = 100,
     DrawLevel = 25,
     VisibleUnderFog = true,
     NeutralMinimapColor = {196, 196, 196},
@@ -246,6 +251,7 @@ DefineUnitType("unit-morel-6", {
     Icon = "icon-morel-6",
     Construction = "construction-morels",
     HitPoints = 50,
+    MaxMana = 100,
     DrawLevel = 25,
     VisibleUnderFog = true,
     NeutralMinimapColor = {196, 196, 196},
@@ -285,6 +291,7 @@ DefineUnitType("unit-morel-7", {
     Icon = "icon-morel-7",
     Construction = "construction-morels",
     HitPoints = 50,
+    MaxMana = 100,
     DrawLevel = 25,
     VisibleUnderFog = true,
     NeutralMinimapColor = {196, 196, 196},
-- 
1.7.1

