From 57577aa4c163b01572b21914defe34e7ee24104b Mon Sep 17 00:00:00 2001
From: Treeki <treeki@gmail.com>
Date: Tue, 24 Jul 2012 15:55:13 +0200
Subject: parts of the new unlock system, still got to wrap up some stuff

---
 src/koopatlas/mapdata.cpp     |  10 ++--
 src/koopatlas/mapdata.h       |  10 +++-
 src/koopatlas/pathmanager.cpp | 108 +++++++++++++++++++++++++++++++++++++++++-
 src/koopatlas/pathmanager.h   |   7 +++
 4 files changed, 128 insertions(+), 7 deletions(-)

(limited to 'src/koopatlas')

diff --git a/src/koopatlas/mapdata.cpp b/src/koopatlas/mapdata.cpp
index 9c84c4a..dd4646b 100644
--- a/src/koopatlas/mapdata.cpp
+++ b/src/koopatlas/mapdata.cpp
@@ -173,6 +173,7 @@ void dKPMapData_c::fixup() {
 
 	fixRef(data->layers);
 	fixRef(data->tilesets);
+	fixRef(data->unlockData);
 
 	for (int iLayer = 0; iLayer < data->layerCount; iLayer++) {
 		dKPLayer_s *layer = fixRef(data->layers[iLayer]);
@@ -225,10 +226,7 @@ void dKPMapData_c::fixup() {
 	}
 
 	
-	// before we finish here, create the Node Extra classes
-
-	// before the first off, do the unlocking.
-	SaveBlock *save = GetSaveFile()->GetBlock(-1);
+	/*SaveBlock *save = GetSaveFile()->GetBlock(-1);
 	for (int i = 0; i < pathLayer->pathCount; i++) {
 		dKPPath_s *path = pathLayer->paths[i];
 
@@ -242,8 +240,10 @@ void dKPMapData_c::fixup() {
 			else
 				path->isAvailable = false;
 		}
-	}
+	}*/
+
 
+	// before we finish here, create the Node Extra classes
 
 	// first off, count how many we need...
 	int count = 0;
diff --git a/src/koopatlas/mapdata.h b/src/koopatlas/mapdata.h
index 18dd9ee..b9a32f3 100644
--- a/src/koopatlas/mapdata.h
+++ b/src/koopatlas/mapdata.h
@@ -95,12 +95,18 @@ struct dKPNode_s {
 };
 
 struct dKPPath_s {
+	enum Availability {
+		NOT_AVAILABLE = 0,
+		AVAILABLE = 1,
+		NEWLY_AVAILABLE = 2
+	};
+
 	dKPNode_s *start, *end;
 	dKPLayer_s *tileLayer, *doodadLayer;
 
 	u8 unlockType; // 0 = always, 1 = normal, 2 = secret
 	u8 unlockLevelNumber[2];
-	bool isAvailable; // computed on-the-fly - default from Koopatlas is true
+	u8 isAvailable; // computed on-the-fly - default from Koopatlas is 1
 	float speed;
 	int animation;
 };
@@ -164,6 +170,8 @@ struct dKPMapFile_s {
 	int tilesetCount;
 	GXTexObj *tilesets;
 
+	u8 *unlockData;
+
 	dKPLayer_s::sector_s sectors[1]; // variable size
 };
 
diff --git a/src/koopatlas/pathmanager.cpp b/src/koopatlas/pathmanager.cpp
index 41515a6..444684c 100644
--- a/src/koopatlas/pathmanager.cpp
+++ b/src/koopatlas/pathmanager.cpp
@@ -61,11 +61,115 @@ void dWMPathManager_c::setup() {
 		}
 	}
 
-	// unlock all needed paths
 	SpammyReport("Unlocking paths\n");
+	isEnteringLevel = false;
+	unlockPaths();
 	SpammyReport("done\n");
 }
 
+static u8 *PathAvailabilityData = 0;
+
+dWMPathManager_c::~dWMPathManager_c() {
+	if (PathAvailabilityData && !isEnteringLevel) {
+		delete[] PathAvailabilityData;
+		PathAvailabilityData = 0;
+	}
+}
+
+void dWMPathManager_c::unlockPaths() {
+	u8 *oldAvData = PathAvailabilityData;
+	PathAvailabilityData = new u8[pathLayer->pathCount];
+
+	// unlock all needed paths
+	for (int i = 0; i < pathLayer->pathCount; i++)
+		PathAvailabilityData[i] = dKPPath_s::NOT_AVAILABLE;
+
+	SaveBlock *save = GetSaveFile()->GetBlock(-1);
+
+	u8 *in = (u8*)dScKoopatlas_c::instance->mapData.data->unlockData;
+
+	while (*in != 0) {
+		// begin processing a block
+		bool value = evaluateUnlockCondition(in, save);
+
+		// , get what it's supposed to affect
+		// for now we'll assume that it affects one or more paths
+		u8 affectedCount = *(in++);
+
+		for (int i = 0; i < affectedCount; i++) {
+			u8 one = *(in++);
+			u8 two = *(in++);
+			u16 pathID = (one << 8) | two;
+
+			dKPPath_s *path = pathLayer->paths[pathID];
+			path->isAvailable = dKPPath_s::AVAILABLE;
+			PathAvailabilityData[pathID] = dKPPath_s::AVAILABLE;
+			// NEWLY_AVAILABLE is set later, when that stuff is figured out
+		}
+
+		// now evaluate a condition. simple!
+	}
+
+	// did anything become newly available?!
+	int whatsNew = 0;
+
+	if (oldAvData) {
+		for (int i = 0; i < pathLayer->pathCount; i++) {
+			if ((PathAvailabilityData[i] > 0) && (oldAvData[i] == 0)) {
+				dKPPath_s *path = pathLayer->paths[i];
+				path->isAvailable = dKPPath_s::NEWLY_AVAILABLE;
+				whatsNew++;
+			}
+		}
+		delete[] oldAvData;
+	}
+
+	// todo: set a flag on Map to do the path fade/sound, with whatsNew
+}
+
+bool dWMPathManager_c::evaluateUnlockCondition(u8 *&in, SaveBlock *save) {
+	u8 controlByte = *(in++);
+
+	u8 conditionType = (controlByte >> 6);
+
+	if (conditionType == 0)
+		return true;
+
+	if (conditionType == 1) {
+		// Simple level
+
+		bool isSecret = (controlByte & 0x10);
+		u8 worldNumber = controlByte & 0xF;
+		u8 levelNumber = *(in++);
+
+		u32 conds = save->GetLevelCondition(worldNumber, levelNumber);
+
+		if (isSecret)
+			return (conds & COND_SECRET) != 0;
+		else
+			return (conds & COND_NORMAL) != 0;
+	}
+
+	// Type: 2 = AND, 3 = OR
+	bool isAnd = (conditionType == 2);
+	bool isOr = (conditionType == 3);
+
+	bool value = isOr ? false : true;
+
+	u8 termCount = (controlByte & 0x3F) + 1;
+
+	for (int i = 0; i < termCount; i++) {
+		bool what = evaluateUnlockCondition(in, save);
+
+		if (isOr)
+			value |= what;
+		else
+			value &= what;
+	}
+
+	return value;
+}
+
 
 void dWMPathManager_c::execute() {
 	int nowPressed = Remocon_GetPressed(GetActiveRemocon());
@@ -354,6 +458,8 @@ void dWMPathManager_c::activatePoint() {
 		MapSoundPlayer(SoundRelatedClass, SE_SYS_GAME_START, 1);
 		daWMPlayer_c::instance->startAnimation(170, 1.2, 10.0, 0.0);
 
+		isEnteringLevel = true;
+
 		dLevelInfo_c::entry_s *level = dScKoopatlas_c::instance->levelInfo.search(w, l);
 		dScKoopatlas_c::instance->startLevel(level);
 	}
diff --git a/src/koopatlas/pathmanager.h b/src/koopatlas/pathmanager.h
index 969bdb2..5d35f4c 100644
--- a/src/koopatlas/pathmanager.h
+++ b/src/koopatlas/pathmanager.h
@@ -10,6 +10,7 @@ extern "C" bool SpawnEffect(const char*, int, Vec*, S16Vec*, Vec*);
 class dWMPathManager_c {
 	public:
 		void setup();
+		~dWMPathManager_c();
 		void execute();
 
 		bool canUseExit(dKPPath_s *path) {
@@ -36,6 +37,12 @@ class dWMPathManager_c {
 		bool reverseThroughPath; // direction we are going through the path
 
 		bool mustComplainToMapCreator;
+
+	private:
+		void unlockPaths();
+		bool evaluateUnlockCondition(u8 *&in, SaveBlock *save);
+
+		bool isEnteringLevel;
 };
 
 #endif
-- 
cgit v1.2.3


From 0d20c172706178e5df2d426fcf5cb1b7ae85c225 Mon Sep 17 00:00:00 2001
From: Treeki <treeki@gmail.com>
Date: Wed, 25 Jul 2012 00:36:07 +0200
Subject: moving to desktop

---
 src/koopatlas/mapdata.cpp     | 17 -----------------
 src/koopatlas/mapdata.h       |  6 ++----
 src/koopatlas/pathmanager.cpp |  4 +---
 3 files changed, 3 insertions(+), 24 deletions(-)

(limited to 'src/koopatlas')

diff --git a/src/koopatlas/mapdata.cpp b/src/koopatlas/mapdata.cpp
index dd4646b..650f1e3 100644
--- a/src/koopatlas/mapdata.cpp
+++ b/src/koopatlas/mapdata.cpp
@@ -226,23 +226,6 @@ void dKPMapData_c::fixup() {
 	}
 
 	
-	/*SaveBlock *save = GetSaveFile()->GetBlock(-1);
-	for (int i = 0; i < pathLayer->pathCount; i++) {
-		dKPPath_s *path = pathLayer->paths[i];
-
-		if (path->unlockType > 0) {
-			u32 conds = save->GetLevelCondition(path->unlockLevelNumber[0] - 1, path->unlockLevelNumber[1] - 1);
-
-			if (path->unlockType == 1 && (conds & COND_NORMAL))
-				path->isAvailable = true;
-			else if (path->unlockType == 2 && (conds & COND_SECRET))
-				path->isAvailable = true;
-			else
-				path->isAvailable = false;
-		}
-	}*/
-
-
 	// before we finish here, create the Node Extra classes
 
 	// first off, count how many we need...
diff --git a/src/koopatlas/mapdata.h b/src/koopatlas/mapdata.h
index b9a32f3..48a1720 100644
--- a/src/koopatlas/mapdata.h
+++ b/src/koopatlas/mapdata.h
@@ -75,8 +75,6 @@ struct dKPNode_s {
 	// The union is placed at the very end so we can leave out padding in the
 	// kpbin
 	union {
-		// struct { u8 levelNumber[2]; };
-		// FORWARDS COMPATIBILITY:  
 		struct { u8 levelNumber[2]; bool hasSecret; };
 		struct { const char *destMap; u8 thisID, foreignID, transition, _; };
 	};
@@ -104,9 +102,9 @@ struct dKPPath_s {
 	dKPNode_s *start, *end;
 	dKPLayer_s *tileLayer, *doodadLayer;
 
-	u8 unlockType; // 0 = always, 1 = normal, 2 = secret
-	u8 unlockLevelNumber[2];
 	u8 isAvailable; // computed on-the-fly - default from Koopatlas is 1
+	u8 hasSecret;
+	u8 _padding[2];
 	float speed;
 	int animation;
 };
diff --git a/src/koopatlas/pathmanager.cpp b/src/koopatlas/pathmanager.cpp
index 444684c..4eb6c2d 100644
--- a/src/koopatlas/pathmanager.cpp
+++ b/src/koopatlas/pathmanager.cpp
@@ -92,7 +92,7 @@ void dWMPathManager_c::unlockPaths() {
 		// begin processing a block
 		bool value = evaluateUnlockCondition(in, save);
 
-		// , get what it's supposed to affect
+		// get what it's supposed to affect
 		// for now we'll assume that it affects one or more paths
 		u8 affectedCount = *(in++);
 
@@ -106,8 +106,6 @@ void dWMPathManager_c::unlockPaths() {
 			PathAvailabilityData[pathID] = dKPPath_s::AVAILABLE;
 			// NEWLY_AVAILABLE is set later, when that stuff is figured out
 		}
-
-		// now evaluate a condition. simple!
 	}
 
 	// did anything become newly available?!
-- 
cgit v1.2.3


From 6895c831ad320c14b01ccabe1c8adcec354e3f9f Mon Sep 17 00:00:00 2001
From: Treeki <treeki@gmail.com>
Date: Wed, 25 Jul 2012 14:10:39 +0200
Subject: lots more work completed, basic unlocks working

---
 src/koopatlas/core.cpp        |   5 ++
 src/koopatlas/map.cpp         |  35 ++++---------
 src/koopatlas/map.h           |   5 +-
 src/koopatlas/mapdata.cpp     |  32 +++++++++++-
 src/koopatlas/mapdata.h       |  29 ++++++++---
 src/koopatlas/pathmanager.cpp | 114 +++++++++++++++++++++++++++++++++++++-----
 src/koopatlas/pathmanager.h   |   8 +++
 7 files changed, 178 insertions(+), 50 deletions(-)

(limited to 'src/koopatlas')

diff --git a/src/koopatlas/core.cpp b/src/koopatlas/core.cpp
index 42bf7c1..2317d00 100644
--- a/src/koopatlas/core.cpp
+++ b/src/koopatlas/core.cpp
@@ -973,6 +973,11 @@ void NewerMapDrawFunc() {
 	DrawOpa();
 	DrawXlu();
 	UnlinkScene(1);
+	SetCurrentCameraID(0);
+	for (int i = 0; i < 4; i++)
+		RenderEffects(0, 0xB+i);
+	for (int i = 0; i < 4; i++)
+		RenderEffects(0, 7+i);
 	GXDrawDone();
 	// Leaving out some stuff here
 	DrawAllLayoutsAfterX(0x80);
diff --git a/src/koopatlas/map.cpp b/src/koopatlas/map.cpp
index 4d2bc1d..2486d34 100644
--- a/src/koopatlas/map.cpp
+++ b/src/koopatlas/map.cpp
@@ -45,7 +45,6 @@ int dWMMap_c::onCreate() {
 	renderer.allocator.setup(GameHeaps[0], 0x20);
 	bool result = renderer.setup(&renderer.allocator);
 
-	bgMatrix.identity();
 	bgMatrix.translation(0.0f, 0.0f, -500.0f);
 
 	allocator.link(-1, GameHeaps[0], 0, 0x20);
@@ -112,34 +111,21 @@ void dWMMap_c::renderer_c::drawLayers() {
 
 	beginRendering();
 
-	bool cont = false;
 	for (int iLayer = data->layerCount - 1; iLayer >= 0; iLayer--) {
 		dKPLayer_s *layer = data->layers[iLayer];
 		renderMtx[2][3] += 2.0f;
 
-		cont = false;
-		for (int iNodes = dataCls->pathLayer->nodeCount - 1; iNodes >= 0; iNodes--) {
-			dKPNode_s *node = dataCls->pathLayer->nodes[iNodes];
-			bool unlock = node->isUnlocked();
-			if (!unlock) { 
-				if (node->tileLayer == layer) { cont = true; break; }
-				if (node->doodadLayer == layer) { cont = true; break; }
-			}
-		}
-		for (int iPaths = dataCls->pathLayer->pathCount - 1; iPaths >= 0; iPaths--) {
-			dKPPath_s *path = dataCls->pathLayer->paths[iPaths];
-			if (!path->isAvailable) {
-				if (path->tileLayer == layer) { cont = true; break; }
-				if (path->doodadLayer == layer) { cont = true; break; }
-			}
-		}
+		if (layer->alpha == 0)
+			continue; // invisible
 
-		if (cont) { continue; }
 		TileReport("Checking layer %d with type %d\n", iLayer, layer->type);
+
+		GXSetTevColor(GX_TEVREG0, (GXColor){255,255,255,layer->alpha});
+
 		if (layer->type == dKPLayer_s::OBJECTS) 	
-			renderTileLayer(layer, data->sectors, 255);
+			renderTileLayer(layer, data->sectors);
 		else if (layer->type == dKPLayer_s::DOODADS)
-			renderDoodadLayer(layer, 255);
+			renderDoodadLayer(layer);
 	}
 
 	endRendering();
@@ -209,7 +195,7 @@ void dWMMap_c::renderer_c::beginRendering() {
 	GXSetTevColor(GX_TEVREG1, (GXColor){0,0,0,255});
 }
 
-void dWMMap_c::renderer_c::renderTileLayer(dKPLayer_s *layer, dKPLayer_s::sector_s *sectors, int alpha) {
+void dWMMap_c::renderer_c::renderTileLayer(dKPLayer_s *layer, dKPLayer_s::sector_s *sectors) {
 	//TileReport("Rendering layer %p\n", layer);
 
 	// don't render it if we don't need to
@@ -288,8 +274,6 @@ void dWMMap_c::renderer_c::renderTileLayer(dKPLayer_s *layer, dKPLayer_s::sector
 					float coordY2 = yMult * (tileY + 26.0f);
 
 
-					GXSetTevColor(GX_TEVREG0, (GXColor){255,255,255,alpha});
-
 					GXBegin(GX_QUADS, GX_VTXFMT0, 4);
 					GXPosition2s16(worldX + 24, worldY - 24);
 					GXTexCoord2f32(coordX2, coordY2);
@@ -310,7 +294,7 @@ void dWMMap_c::renderer_c::renderTileLayer(dKPLayer_s *layer, dKPLayer_s::sector
 	//TileReport("Layer complete\n");
 }
 
-void dWMMap_c::renderer_c::renderDoodadLayer(dKPLayer_s *layer, int alpha) {
+void dWMMap_c::renderer_c::renderDoodadLayer(dKPLayer_s *layer) {
 	for (int i = 0; i < layer->doodadCount; i++) {
 		dKPDoodad_s *doodad = layer->doodads[i];
 		DoodadReport("Doodad @ %f,%f sized %f,%f with angle %f\n", doodad->x, doodad->y, doodad->width, doodad->height, doodad->angle);
@@ -430,7 +414,6 @@ void dWMMap_c::renderer_c::renderDoodadLayer(dKPLayer_s *layer, int alpha) {
 		loadCamera(doodadMtx);
 		loadTexture(doodad->texObj);
 
-		GXSetTevColor(GX_TEVREG0, (GXColor){255,255,255,alpha});
 		GXBegin(GX_QUADS, GX_VTXFMT1, 4);
 		GXPosition2f32(halfW, -halfH);
 		GXTexCoord2u8(255, 255);
diff --git a/src/koopatlas/map.h b/src/koopatlas/map.h
index 14c2b87..8d3e2ae 100644
--- a/src/koopatlas/map.h
+++ b/src/koopatlas/map.h
@@ -42,8 +42,8 @@ class dWMMap_c : public dBase_c {
 				void beginRendering();
 				void endRendering();
 
-				void renderTileLayer(dKPLayer_s *layer, dKPLayer_s::sector_s *sector, int alpha);
-				void renderDoodadLayer(dKPLayer_s *layer, int alpha);
+				void renderTileLayer(dKPLayer_s *layer, dKPLayer_s::sector_s *sector);
+				void renderDoodadLayer(dKPLayer_s *layer);
 
 				void loadTexture(GXTexObj *obj);
 
@@ -55,7 +55,6 @@ class dWMMap_c : public dBase_c {
 				float baseZ;
 
 				int minX, minY, maxX, maxY;
-				int unlockingAlpha;
 		};
 		
 		renderer_c renderer;
diff --git a/src/koopatlas/mapdata.cpp b/src/koopatlas/mapdata.cpp
index 650f1e3..63fd0b7 100644
--- a/src/koopatlas/mapdata.cpp
+++ b/src/koopatlas/mapdata.cpp
@@ -2,17 +2,31 @@
 
 
 // HELPER FUNCTIONS
-dKPPath_s *dKPNode_s::getOppositeExitTo(dKPPath_s *path) {
+dKPPath_s *dKPNode_s::getOppositeExitTo(dKPPath_s *path, bool mustBeAvailable) {
 	for (int i = 0; i < 4; i++) {
 		dKPPath_s *check = exits[i];
 
-		if (check != 0 && check != path)
+		if (check != 0 && check != path) {
+			if (mustBeAvailable && !check->isAvailable)
+				continue;
+
 			return check;
+		}
 	}
 
 	return 0;
 }
 
+int dKPNode_s::getExitCount(bool mustBeAvailable) {
+	int ct = 0;
+
+	for (int i = 0; i < 4; i++)
+		if (exits[i] && (mustBeAvailable ? exits[i]->isAvailable : true))
+			ct++;
+
+	return ct;
+}
+
 bool dKPNode_s::isUnlocked() {
 	for (int i = 0; i < 4; i++)
 		if (exits[i]) 
@@ -89,6 +103,20 @@ void dKPNode_s::setupNodeExtra() {
 	this->extra->mallocator.unlink();
 }
 
+void dKPNode_s::setLayerAlpha(u8 alpha) {
+	if (tileLayer)
+		tileLayer->alpha = alpha;
+	if (doodadLayer)
+		doodadLayer->alpha = alpha;
+}
+
+void dKPPath_s::setLayerAlpha(u8 alpha) {
+	if (tileLayer)
+		tileLayer->alpha = alpha;
+	if (doodadLayer)
+		doodadLayer->alpha = alpha;
+}
+
 int dKPLayer_s::findNodeID(dKPNode_s *node) {
 	for (int i = 0; i < nodeCount; i++)
 		if (nodes[i] == node)
diff --git a/src/koopatlas/mapdata.h b/src/koopatlas/mapdata.h
index 48a1720..40e1a43 100644
--- a/src/koopatlas/mapdata.h
+++ b/src/koopatlas/mapdata.h
@@ -70,6 +70,8 @@ struct dKPNode_s {
 
 	NodeTypes type;
 
+	bool isNew;
+
 	dKPNodeExtra_c *extra;
 
 	// The union is placed at the very end so we can leave out padding in the
@@ -89,24 +91,37 @@ struct dKPNode_s {
 	bool isUnlocked();
 	void setupNodeExtra();
 
-	dKPPath_s *getOppositeExitTo(dKPPath_s *path);
+	dKPPath_s *getOppositeExitTo(dKPPath_s *path, bool mustBeAvailable=false);
+	dKPPath_s *getOppositeAvailableExitTo(dKPPath_s *path) {
+		return getOppositeExitTo(path, true);
+	}
+
+	int getExitCount(bool mustBeAvailable=false);
+	int getAvailableExitCount() {
+		return getExitCount(true);
+	}
+
+	void setLayerAlpha(u8 alpha);
 };
 
 struct dKPPath_s {
 	enum Availability {
 		NOT_AVAILABLE = 0,
 		AVAILABLE = 1,
-		NEWLY_AVAILABLE = 2
+		NEWLY_AVAILABLE = 2,
+		ALWAYS_AVAILABLE = 3
 	};
 
 	dKPNode_s *start, *end;
 	dKPLayer_s *tileLayer, *doodadLayer;
 
-	u8 isAvailable; // computed on-the-fly - default from Koopatlas is 1
-	u8 hasSecret;
+	u8 isAvailable; // computed on-the-fly - default from Koopatlas is NOT or ALWAYS
+	u8 isSecret;
 	u8 _padding[2];
 	float speed;
 	int animation;
+
+	void setLayerAlpha(u8 alpha);
 };
 
 /******************************************************************************
@@ -118,6 +133,8 @@ struct dKPLayer_s {
 	};
 
 	LayerTypes type;
+	u8 alpha;
+	u8 _padding[3];
 
 	typedef u16 sector_s[16][16];
 
@@ -180,7 +197,7 @@ class dKPMapData_c {
 		template <typename T>
 			inline T* fixRef(T*& indexAsPtr) {
 				unsigned int index = (unsigned int)indexAsPtr;
-				if (index == 0xFFFFFFFF)
+				if (index == 0xFFFFFFFF || index == 0)
 					indexAsPtr = 0;
 				else
 					indexAsPtr = (T*)(((char*)data) + index);
@@ -190,7 +207,7 @@ class dKPMapData_c {
 		template <typename T>
 			inline T* fixRefSafe(T*& indexAsPtr) {
 				unsigned int index = (unsigned int)indexAsPtr;
-				if (index == 0xFFFFFFFF)
+				if (index == 0xFFFFFFFF || index == 0)
 					indexAsPtr = 0;
 				else if (index < 0x80000000)
 					indexAsPtr = (T*)(((char*)data) + index);
diff --git a/src/koopatlas/pathmanager.cpp b/src/koopatlas/pathmanager.cpp
index 4eb6c2d..ee7b596 100644
--- a/src/koopatlas/pathmanager.cpp
+++ b/src/koopatlas/pathmanager.cpp
@@ -68,21 +68,40 @@ void dWMPathManager_c::setup() {
 }
 
 static u8 *PathAvailabilityData = 0;
+static u8 *NodeAvailabilityData = 0;
 
 dWMPathManager_c::~dWMPathManager_c() {
 	if (PathAvailabilityData && !isEnteringLevel) {
 		delete[] PathAvailabilityData;
 		PathAvailabilityData = 0;
+
+		delete[] NodeAvailabilityData;
+		NodeAvailabilityData = 0;
 	}
 }
 
 void dWMPathManager_c::unlockPaths() {
-	u8 *oldAvData = PathAvailabilityData;
+	u8 *oldPathAvData = PathAvailabilityData;
 	PathAvailabilityData = new u8[pathLayer->pathCount];
 
+	u8 *oldNodeAvData = NodeAvailabilityData;
+	NodeAvailabilityData = new u8[pathLayer->nodeCount];
+
+	OSReport("Unlocking paths\n");
+
 	// unlock all needed paths
-	for (int i = 0; i < pathLayer->pathCount; i++)
-		PathAvailabilityData[i] = dKPPath_s::NOT_AVAILABLE;
+	for (int i = 0; i < pathLayer->pathCount; i++) {
+		dKPPath_s *path = pathLayer->paths[i];
+
+		PathAvailabilityData[i] = path->isAvailable;
+
+		//OSReport("Path %d: %d\n", i, path->isAvailable);
+		// if this path is not "always available", then nuke its alpha
+		path->setLayerAlpha((path->isAvailable == dKPPath_s::ALWAYS_AVAILABLE) ? 255 : 0);
+	}
+
+	for (int i = 0; i < pathLayer->nodeCount; i++)
+		NodeAvailabilityData[i] = pathLayer->nodes[i]->isUnlocked();
 
 	SaveBlock *save = GetSaveFile()->GetBlock(-1);
 
@@ -91,6 +110,7 @@ void dWMPathManager_c::unlockPaths() {
 	while (*in != 0) {
 		// begin processing a block
 		bool value = evaluateUnlockCondition(in, save);
+		//OSReport("Unlock condition: %d\n", value);
 
 		// get what it's supposed to affect
 		// for now we'll assume that it affects one or more paths
@@ -102,27 +122,55 @@ void dWMPathManager_c::unlockPaths() {
 			u16 pathID = (one << 8) | two;
 
 			dKPPath_s *path = pathLayer->paths[pathID];
-			path->isAvailable = dKPPath_s::AVAILABLE;
-			PathAvailabilityData[pathID] = dKPPath_s::AVAILABLE;
+			path->isAvailable = value ? dKPPath_s::AVAILABLE : dKPPath_s::NOT_AVAILABLE;
+			PathAvailabilityData[pathID] = value ? dKPPath_s::AVAILABLE : dKPPath_s::NOT_AVAILABLE;
+			//OSReport("Applied to path %p[%d]\n", path, pathID);
 			// NEWLY_AVAILABLE is set later, when that stuff is figured out
+
+			path->setLayerAlpha(value ? 255 : 0);
 		}
 	}
 
 	// did anything become newly available?!
-	int whatsNew = 0;
+	newlyAvailablePaths = 0;
+	newlyAvailableNodes = 0;
 
-	if (oldAvData) {
+	if (oldPathAvData) {
 		for (int i = 0; i < pathLayer->pathCount; i++) {
-			if ((PathAvailabilityData[i] > 0) && (oldAvData[i] == 0)) {
+			if ((PathAvailabilityData[i] > 0) && (oldPathAvData[i] == 0)) {
 				dKPPath_s *path = pathLayer->paths[i];
 				path->isAvailable = dKPPath_s::NEWLY_AVAILABLE;
-				whatsNew++;
+				newlyAvailablePaths++;
+
+				// set this path's alpha to 0, we'll fade it in later
+				path->setLayerAlpha(0);
 			}
 		}
-		delete[] oldAvData;
+		delete[] oldPathAvData;
+
+		// check nodes too
+		for (int i = 0; i < pathLayer->nodeCount; i++) {
+			if ((NodeAvailabilityData[i] > 0) && (oldNodeAvData[i] == 0)) {
+				dKPNode_s *node = pathLayer->nodes[i];
+				node->isNew = true;
+				newlyAvailableNodes++;
+			}
+		}
+		delete[] oldNodeAvData;
+	}
+
+	// now set all node alphas
+	for (int i = 0; i < pathLayer->nodeCount; i++) {
+		dKPNode_s *node = pathLayer->nodes[i];
+		
+		node->setLayerAlpha((node->isUnlocked() & !node->isNew) ? 255 : 0);
 	}
 
-	// todo: set a flag on Map to do the path fade/sound, with whatsNew
+	// if anything was new, set it as such
+	if (newlyAvailablePaths || newlyAvailableNodes) {
+		countdownToFadeIn = 30;
+	}
+	unlockingAlpha = -1;
 }
 
 bool dWMPathManager_c::evaluateUnlockCondition(u8 *&in, SaveBlock *save) {
@@ -186,6 +234,36 @@ void dWMPathManager_c::execute() {
 		else if (nowPressed & WPAD_TWO)
 			activatePoint();
 	}
+
+	// handle path fading
+	if (countdownToFadeIn > 0) {
+		countdownToFadeIn--;
+		if (countdownToFadeIn <= 0)
+			unlockingAlpha = 0;
+	}
+
+	if (unlockingAlpha != -1) {
+		unlockingAlpha += 3;
+
+		for (int i = 0; i < pathLayer->pathCount; i++) {
+			dKPPath_s *path = pathLayer->paths[i];
+
+			if (path->isAvailable == dKPPath_s::NEWLY_AVAILABLE)
+				path->setLayerAlpha(unlockingAlpha);
+		}
+
+		for (int i = 0; i < pathLayer->nodeCount; i++) {
+			dKPNode_s *node = pathLayer->nodes[i];
+			
+			if (node->isNew)
+				node->setLayerAlpha(unlockingAlpha);
+		}
+
+		if (unlockingAlpha == 255) {
+			// we've reached the end
+			unlockingAlpha = -1;
+		}
+	}
 }
 
 
@@ -404,6 +482,16 @@ void dWMPathManager_c::moveThroughPath() {
 
    		SpammyReport("reached path end (%p)\n", to);
 
+   		// Quick check: do we *actually* need to stop on this node?
+   		// If it's a junction with more than two exits, but only two are open,
+   		// take the opposite open one
+   		bool stopOverride = false;
+
+   		if (to->type == dKPNode_s::STOP) {
+   			if (to->getExitCount() > 2 && to->getAvailableExitCount() == 2)
+   				stopOverride = true;
+		}
+
 		if (to->type == dKPNode_s::CHANGE) {
 			// Go to another map
 			isMoving = false;
@@ -415,7 +503,7 @@ void dWMPathManager_c::moveThroughPath() {
 
 			DoSceneChange(WORLD_MAP, 0x10000000 | to->foreignID, 0);
 
-		} else if (to->type != dKPNode_s::PASS_THROUGH) {
+		} else if (to->type != dKPNode_s::PASS_THROUGH && !stopOverride) {
 	   		// Stop here
 	   		player->startAnimation(0, 1.2, 10.0, 0.0);
 	   		player->hasEffect = false;
@@ -431,7 +519,7 @@ void dWMPathManager_c::moveThroughPath() {
 			dWMHud_c::instance->showPointBar();
 			SpammyReport("Point bar shown\n");
 		} else {
-			startMovementTo(to->getOppositeExitTo(currentPath));
+			startMovementTo(to->getOppositeAvailableExitTo(currentPath));
 			SpammyReport("passthrough node, continuing to next path\n");
 		}
 	}
diff --git a/src/koopatlas/pathmanager.h b/src/koopatlas/pathmanager.h
index 5d35f4c..2ce0e9c 100644
--- a/src/koopatlas/pathmanager.h
+++ b/src/koopatlas/pathmanager.h
@@ -14,6 +14,8 @@ class dWMPathManager_c {
 		void execute();
 
 		bool canUseExit(dKPPath_s *path) {
+			OSReport("Checking usability of path %p\n", path);
+			if (path) OSReport("Availability: %d\n", path->isAvailable);
 			return (path != 0) && (path->isAvailable);
 		}
 
@@ -38,6 +40,12 @@ class dWMPathManager_c {
 
 		bool mustComplainToMapCreator;
 
+		int newlyAvailablePaths;
+		int newlyAvailableNodes;
+
+		int unlockingAlpha; // -1 if not used
+		int countdownToFadeIn;
+
 	private:
 		void unlockPaths();
 		bool evaluateUnlockCondition(u8 *&in, SaveBlock *save);
-- 
cgit v1.2.3