Maelstrom: Turned the game screen into a UIPanel

https://github.com/libsdl-org/Maelstrom/commit/a7bf340a67ac6e9de469bc7e84c6cbcd29d462fc

From a7bf340a67ac6e9de469bc7e84c6cbcd29d462fc Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Wed, 26 Oct 2011 19:40:06 -0400
Subject: [PATCH] Turned the game screen into a UIPanel

---
 UI/game.xml             |   2 +
 UIPanels.cpp            |   3 +
 main.cpp                |   8 +-
 main.h                  |   1 +
 netlogic/Makefile.am    |   2 +-
 netlogic/about.cpp      |  10 --
 netlogic/blit.cpp       | 156 -----------------------------
 netlogic/game.cpp       | 215 ++++++++++++++++++++++++++++++++--------
 netlogic/game.h         |  17 ++++
 screenlib/UIManager.cpp |  17 ++--
 screenlib/UIPanel.cpp   |   8 ++
 screenlib/UIPanel.h     |   4 +
 12 files changed, 222 insertions(+), 221 deletions(-)
 create mode 100644 UI/game.xml
 delete mode 100644 netlogic/blit.cpp
 create mode 100644 netlogic/game.h

diff --git a/UI/game.xml b/UI/game.xml
new file mode 100644
index 00000000..98cb3218
--- /dev/null
+++ b/UI/game.xml
@@ -0,0 +1,2 @@
+<UIPanel cursor="false" delegate="GamePanel">
+</UIPanel>
diff --git a/UIPanels.cpp b/UIPanels.cpp
index 07bd71cb..986d6277 100644
--- a/UIPanels.cpp
+++ b/UIPanels.cpp
@@ -3,6 +3,7 @@
 #include "UIPanels.h"
 #include "main.h"
 #include "netlogic/about.h"
+#include "netlogic/game.h"
 
 
 static UIPanelDelegate *
@@ -12,6 +13,8 @@ CreateMaelstromUIDelegate(UIPanel *panel, const char *delegate)
 		return new MainPanelDelegate(panel);
 	} else if (strcasecmp(delegate, "AboutPanel") == 0) {
 		return new AboutPanelDelegate(panel);
+	} else if (strcasecmp(delegate, "GamePanel") == 0) {
+		return new GamePanelDelegate(panel);
 	} else {
 		fprintf(stderr, "Warning: Couldn't find delegate '%s'\n", delegate);
 		return NULL;
diff --git a/main.cpp b/main.cpp
index 8d726313..b943afb9 100644
--- a/main.cpp
+++ b/main.cpp
@@ -461,11 +461,15 @@ MainPanelDelegate::OnLoad()
 		button->SetButtonDelegate(new SetVolumeDelegate(8));
 	}
 
-	gUpdateBuffer = true;
-
 	return true;
 }
 
+void
+MainPanelDelegate::OnShow()
+{
+	gUpdateBuffer = true;
+}
+
 void
 MainPanelDelegate::OnTick()
 {
diff --git a/main.h b/main.h
index d36f987b..2869527a 100644
--- a/main.h
+++ b/main.h
@@ -8,6 +8,7 @@ class MainPanelDelegate : public UIPanelDelegate
 	MainPanelDelegate(UIPanel *panel) : UIPanelDelegate(panel) { }
 
 	virtual bool OnLoad();
+	virtual void OnShow();
 	virtual void OnTick();
 };
 
diff --git a/netlogic/Makefile.am b/netlogic/Makefile.am
index 1ddb28bc..831206dc 100644
--- a/netlogic/Makefile.am
+++ b/netlogic/Makefile.am
@@ -3,8 +3,8 @@ noinst_LIBRARIES = liblogic.a
 
 liblogic_a_SOURCES =		\
 	about.cpp		\
-	blit.cpp		\
 	game.cpp		\
+	game.h			\
 	globals.h		\
 	logic.cpp		\
 	make.cpp		\
diff --git a/netlogic/about.cpp b/netlogic/about.cpp
index a0dcb855..149b544b 100644
--- a/netlogic/about.cpp
+++ b/netlogic/about.cpp
@@ -4,16 +4,6 @@
 #include "about.h"
 
 
-AboutPanelDelegate::AboutPanelDelegate(UIPanel *panel) : UIPanelDelegate(panel)
-{
-	numsprites = 0;
-}
-
-AboutPanelDelegate::~AboutPanelDelegate()
-{
-	assert(numsprites == 0);
-}
-
 void
 AboutPanelDelegate::OnShow()
 {
diff --git a/netlogic/blit.cpp b/netlogic/blit.cpp
deleted file mode 100644
index 0b6bcce6..00000000
--- a/netlogic/blit.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-
-/* Note well:  The order that things are done is very important to prevent
-               bugs.  Several optimizing assumptions are done in player.cc
-               and object.cc that require this order.  For example, I assume
-               that all sprites are erased when object->Move() is called,
-               that object->HitBy() is called before the move, and that
-               object->Move() is called EVERY timestep!
-*/
-
-#include "Maelstrom_Globals.h"
-#include "netplay.h"
-#include "object.h"
-#include "player.h"
-#include "globals.h"
-
-/* Returns the number of players left in the game */
-int RunFrame(void)
-{
-	int i, j, PlayersLeft;
-
-	/* Read in keyboard input for our ship */
-	HandleEvents(0);
-
-	/* Send Sync! signal to all players, and handle keyboard. */
-	if ( SyncNetwork() < 0 ) {
-		error("Game aborted!\n");
-		return(0);
-	}
-	OBJ_LOOP(i, gNumPlayers)
-		gPlayers[i]->HandleKeys();
-
-	if ( gPaused > 0 )
-		return(1);
-
-	/* Play the boom sounds */
-	if ( --gNextBoom == 0 ) {
-		if ( gBoomPhase ) {
-			sound->PlaySound(gBoom1, 0);
-			gBoomPhase = 0;
-		} else {
-			sound->PlaySound(gBoom2, 0);
-			gBoomPhase = 1;
-		}
-		gNextBoom = gBoomDelay;
-	}
-
-	/* Do all hit detection */
-	OBJ_LOOP(j, gNumPlayers) {
-		if ( ! gPlayers[j]->Alive() )
-			continue;
-
-		/* This loop looks funny because gNumSprites can change 
-		   dynamically during the loop as sprites are killed/created.
-		   This same logic is used whenever looping where sprites
-		   might be destroyed.
-		*/
-		OBJ_LOOP(i, gNumSprites) {
-			if ( gSprites[i]->HitBy(gPlayers[j]) < 0 ) {
-				delete gSprites[i];
-				gSprites[i] = gSprites[gNumSprites];
-			}
-		}
-		OBJ_LOOP(i, gNumPlayers) {
-			if ( i == j )	// Don't shoot ourselves. :)
-				continue;
-			(void) gPlayers[i]->HitBy(gPlayers[j]);
-		}
-	}
-	if ( gEnemySprite ) {
-		OBJ_LOOP(i, gNumPlayers) {
-			if ( ! gPlayers[i]->Alive() )
-				continue;
-			(void) gPlayers[i]->HitBy(gEnemySprite);
-		}
-		OBJ_LOOP(i, gNumSprites) {
-			if ( gSprites[i] == gEnemySprite )
-				continue;
-			if ( gSprites[i]->HitBy(gEnemySprite) < 0 ) {
-				delete gSprites[i];
-				gSprites[i] = gSprites[gNumSprites];
-			}
-		}
-	}
-
-	/* Handle all the shimmy and the shake. :-) */
-	if ( gShakeTime && (gShakeTime-- > 0) ) {
-		int shakeV;
-
-		OBJ_LOOP(i, gNumPlayers) {
-			shakeV = FastRandom(SHAKE_FACTOR);
-			if ( ! gPlayers[i]->Alive() )
-				continue;
-			gPlayers[i]->Shake(FastRandom(SHAKE_FACTOR));
-		}
-		OBJ_LOOP(i, gNumSprites) {
-			shakeV = FastRandom(SHAKE_FACTOR);
-			gSprites[i]->Shake(FastRandom(SHAKE_FACTOR));
-		}
-	}
-
-	/* Move all of the sprites */
-	OBJ_LOOP(i, gNumPlayers)
-		gPlayers[i]->Move(0);
-	OBJ_LOOP(i, gNumSprites) {
-		if ( gSprites[i]->Move(gFreezeTime) < 0 ) {
-			delete gSprites[i];
-			gSprites[i] = gSprites[gNumSprites];
-		}
-	}
-	if ( gFreezeTime )
-		--gFreezeTime;
-
-	/* Now Blit them all again */
-	OBJ_LOOP(i, gNumSprites)
-		gSprites[i]->BlitSprite();
-	OBJ_LOOP(i, gNumPlayers)
-		gPlayers[i]->BlitSprite();
-
-	/* Make sure someone is still playing... */
-	for ( i=0, PlayersLeft=0; i < gNumPlayers; ++i ) {
-		if ( gPlayers[i]->Kicking() )
-			++PlayersLeft;
-	}
-	if ( gNumPlayers > 1 ) {
-		OBJ_LOOP(i, gNumPlayers)
-			gPlayers[i]->ShowDot();
-	}
-
-#ifdef SERIOUS_DEBUG
-printf("Player listing: ");
-OBJ_LOOP(i, gNumPlayers) {
-	int x, y;
-	gPlayers[i]->GetPos(&x, &y);
-	printf("  %d = (%d,%d)", i, x, y);
-}
-printf("\n");
-
-printf("Object listing: ");
-OBJ_LOOP(i, gNumSprites) {
-	int x, y;
-	gSprites[i]->GetPos(&x, &y);
-	printf("  %d = (%d,%d)", i, x, y);
-}
-printf("\n");
-#endif /* SERIOUS_DEBUG */
-
-	/* Timing handling -- Delay the FRAME_DELAY */
-	if ( ! gNoDelay ) {
-		Uint32 ticks;
-		while ( ((ticks=Ticks)-gLastDrawn) < FRAME_DELAY ) {
-			SDL_Delay(1);
-		}
-		gLastDrawn = ticks;
-	}
-	return(PlayersLeft);
-}
diff --git a/netlogic/game.cpp b/netlogic/game.cpp
index ed4af9a2..b3421fe9 100644
--- a/netlogic/game.cpp
+++ b/netlogic/game.cpp
@@ -5,6 +5,7 @@
 #include "netplay.h"
 #include "make.h"
 #include "load.h"
+#include "game.h"
 
 
 #ifdef MOVIE_SUPPORT
@@ -89,7 +90,6 @@ int SelectMovieRect(void)
 
 }
 #endif
-extern int RunFrame(void);	/* The heart of blit.cpp */
 
 // Global variables set in this file...
 int	gGameOn;
@@ -277,15 +277,49 @@ void NewGame(void)
 		}
 	}
 
+	ui->ShowPanel(PANEL_GAME);
+
+	/* Play the game, dammit! */
+	while (gGameOn) {
+		ui->Draw();
+
+		/* Timing handling -- Delay the FRAME_DELAY */
+		if ( ! gNoDelay ) {
+			Uint32 ticks;
+			while ( ((ticks=Ticks)-gLastDrawn) < FRAME_DELAY ) {
+				SDL_Delay(1);
+			}
+			gLastDrawn = ticks;
+		}
+	}
+	
+	ui->HidePanel(PANEL_GAME);
+
+/* -- Do the game over stuff */
+
+	screen->HideCursor();
+	DoGameOver();
+	screen->ShowCursor();
+
+	ui->ShowPanel(PANEL_MAIN);
+}	/* -- NewGame */
+
+bool
+GamePanelDelegate::OnLoad()
+{
 	/* Load the font and colors we use everywhere */
 	geneva = fonts[GENEVA_9];
 	text_height = fontserv->TextHeight(geneva);
 	ourGrey = screen->MapRGB(30000>>8, 30000>>8, 0xFF);
 	ourWhite = screen->MapRGB(0xFF, 0xFF, 0xFF);
 
-	/* Fade into game mode */
-	screen->Fade();
-	screen->HideCursor();
+	return true;
+}
+
+void
+GamePanelDelegate::OnShow()
+{
+	int i;
 
 	/* Initialize some game variables */
 	gGameOn = 1;
@@ -298,50 +332,24 @@ void NewGame(void)
 	gNumSprites = 0;
 
 	NextWave();
+}
 
-	/* Play the game, dammit! */
-	while (gGameOn) {
-		screen->Clear();
-
-		/* -- Draw the star field */
-		for ( i=0; i<MAX_STARS; ++i ) {
-			screen->DrawPoint(gTheStars[i]->xCoord, 
-				gTheStars[i]->yCoord, gTheStars[i]->color);
-		}
-
-		/* Draw the status frame */
-		DrawStatus(false);
-
-		if (!RunFrame()) {
-			gGameOn = 0;
-		}
-		screen->Update();
-
-		DoHouseKeeping();
-	}
-	
-/* -- Do the game over stuff */
-
-	DoGameOver();
-	screen->ShowCursor();
-}	/* -- NewGame */
-
-
-/* ----------------------------------------------------------------- */
-/* -- Do some housekeeping! */
+void
+GamePanelDelegate::OnHide()
+{
+}
 
-static void DoHouseKeeping(void)
+void
+GamePanelDelegate::OnTick()
 {
 	/* Don't do anything if we're paused */
 	if ( gPaused ) {
-		/* Give up the CPU for a frame duration */
-		Delay(FRAME_DELAY);
 		return;
 	}
 
 #ifdef MOVIE_SUPPORT
 	if ( gMovie )
-		win->ScreenDump("MovieFrame", &gMovieRect);
+		screen->ScreenDump("MovieFrame", &gMovieRect);
 #endif
 	/* -- Maybe throw a multiplier up on the screen */
 	if (gMultiplierShown && (--gMultiplierShown == 0) )
@@ -388,9 +396,134 @@ static void DoHouseKeeping(void)
 		else if ( --gWhenDone == 0 )
 			NextWave();
 	}
-	
-}	/* -- DoHouseKeeping */
+}
+
+void
+GamePanelDelegate::OnDraw()
+{
+	int i, j, PlayersLeft;
+
+	/* -- Draw the star field */
+	for ( i=0; i<MAX_STARS; ++i ) {
+		screen->DrawPoint(gTheStars[i]->xCoord, 
+			gTheStars[i]->yCoord, gTheStars[i]->color);
+	}
+
+	/* Draw the status frame */
+	DrawStatus(false);
+
+	/* Read in keyboard input for our ship */
+	HandleEvents(0);
+
+	/* Send Sync! signal to all players, and handle keyboard. */
+	if ( SyncNetwork() < 0 ) {
+		error("Game aborted!\n");
+		gGameOn = 0;
+		return;
+	}
+	OBJ_LOOP(i, gNumPlayers)
+		gPlayers[i]->HandleKeys();
+
+	if ( gPaused > 0 )
+		return;
+
+	/* Play the boom sounds */
+	if ( --gNextBoom == 0 ) {
+		if ( gBoomPhase ) {
+			sound->PlaySound(gBoom1, 0);
+			gBoomPhase = 0;
+		} else {
+			sound->PlaySound(gBoom2, 0);
+			gBoomPhase = 1;
+		}
+		gNextBoom = gBoomDelay;
+	}
+
+	/* Do all hit detection */
+	OBJ_LOOP(j, gNumPlayers) {
+		if ( ! gPlayers[j]->Alive() )
+			continue;
+
+		/* This loop looks funny because gNumSprites can change 
+		   dynamically during the loop as sprites are killed/created.
+		   This same logic is used whenever looping where sprites
+		   might be destroyed.
+		*/
+		OBJ_LOOP(i, gNumSprites) {
+			if ( gSprites[i]->HitBy(gPlayers[j]) < 0 ) {
+				delete gSprites[i];
+				gSprites[i] = gSprites[gNumSprites];
+			}
+		}
+		OBJ_LOOP(i, gNumPlayers) {
+			if ( i == j )	// Don't shoot ourselves. :)
+				continue;
+			(void) gPlayers[i]->HitBy(gPlayers[j]);
+		}
+	}
+	if ( gEnemySprite ) {
+		OBJ_LOOP(i, gNumPlayers) {
+			if ( ! gPlayers[i]->Alive() )
+				continue;
+			(void) gPlayers[i]->HitBy(gEnemySprite);
+		}
+		OBJ_LOOP(i, gNumSprites) {
+			if ( gSprites[i] == gEnemySprite )
+				continue;
+			if ( gSprites[i]->HitBy(gEnemySprite) < 0 ) {
+				delete gSprites[i];
+				gSprites[i] = gSprites[gNumSprites];
+			}
+		}
+	}
 
+	/* Handle all the shimmy and the shake. :-) */
+	if ( gShakeTime && (gShakeTime-- > 0) ) {
+		int shakeV;
+
+		OBJ_LOOP(i, gNumPlayers) {
+			shakeV = FastRandom(SHAKE_FACTOR);
+			if ( ! gPlayers[i]->Alive() )
+				continue;
+			gPlayers[i]->Shake(FastRandom(SHAKE_FACTOR));
+		}
+		OBJ_LOOP(i, gNumSprites) {
+			shakeV = FastRandom(SHAKE_FACTOR);
+			gSprites[i]->Shake(FastRandom(SHAKE_FACTOR));
+		}
+	}
+
+	/* Move all of the sprites */
+	OBJ_LOOP(i, gNumPlayers)
+		gPlayers[i]->Move(0);
+	OBJ_LOOP(i, gNumSprites) {
+		if ( gSprites[i]->Move(gFreezeTime) < 0 ) {
+			delete gSprites[i];
+			gSprites[i] = gSprites[gNumSprites];
+		}
+	}
+	if ( gFreezeTime )
+		--gFreezeTime;
+
+	/* Now Blit them all again */
+	OBJ_LOOP(i, gNumSprites)
+		gSprites[i]->BlitSprite();
+	OBJ_LOOP(i, gNumPlayers)
+		gPlayers[i]->BlitSprite();
+
+	/* Make sure someone is still playing... */
+	for ( i=0, PlayersLeft=0; i < gNumPlayers; ++i ) {
+		if ( gPlayers[i]->Kicking() )
+			++PlayersLeft;
+	}
+	if ( gNumPlayers > 1 ) {
+		OBJ_LOOP(i, gNumPlayers)
+			gPlayers[i]->ShowDot();
+	}
+	if ( !PlayersLeft ) {
+		gGameOn = 0;
+	}
+}
 
 /* ----------------------------------------------------------------- */
 /* -- Start the next wave! */
@@ -544,12 +677,10 @@ static void DoGameOver(void)
 		final[i].Score = gPlayers[i]->GetScore();
 		final[i].Frags = gPlayers[i]->GetFrags();
 	}
-#ifndef macintosh
 	if ( gDeathMatch )
 		qsort(final,gNumPlayers,sizeof(struct FinalScore),cmp_byfrags);
 	else
 		qsort(final,gNumPlayers,sizeof(struct FinalScore),cmp_byscore);
-#endif
 
 	screen->Fade();
 	sound->HaltSound();
diff --git a/netlogic/game.h b/netlogic/game.h
new file mode 100644
index 00000000..adcdd6c8
--- /dev/null
+++ b/netlogic/game.h
@@ -0,0 +1,17 @@
+
+#ifndef _game_h
+#define _game_h
+
+class GamePanelDelegate : public UIPanelDelegate
+{
+public:
+	GamePanelDelegate(UIPanel *panel) : UIPanelDelegate(panel) { }
+
+	virtual bool OnLoad();
+	virtual void OnShow();
+	virtual void OnHide();
+	virtual void OnTick();
+	virtual void OnDraw();
+};
+
+#endif // _game_h
diff --git a/screenlib/UIManager.cpp b/screenlib/UIManager.cpp
index 3f2768b8..d5b8a62d 100644
--- a/screenlib/UIManager.cpp
+++ b/screenlib/UIManager.cpp
@@ -184,6 +184,9 @@ UIManager::ShowPanel(UIPanel *panel)
 			Draw();
 			m_screen->FadeIn();
 		}
+		if (!panel->IsCursorVisible()) {
+			m_screen->HideCursor();
+		}
 	}
 }
 
@@ -192,19 +195,13 @@ UIManager::HidePanel(UIPanel *panel)
 {
 	if (panel && m_visible.remove(panel)) {
 		panel->Hide();
+
 		if (panel->IsFullscreen()) {
 			m_screen->FadeOut();
-
-			for (unsigned int i = m_visible.length(); i--; ) {
-				if (m_visible[i]->IsFullscreen()) {
-					m_visible[i]->Show();
-					Draw();
-					m_screen->FadeIn();
-					break;
-				}
-			}
 		}
-
+		if (!panel->IsCursorVisible()) {
+			m_screen->ShowCursor();
+		}
 	}
 }
 
diff --git a/screenlib/UIPanel.cpp b/screenlib/UIPanel.cpp
index adbf6500..0f97a5c0 100644
--- a/screenlib/UIPanel.cpp
+++ b/screenlib/UIPanel.cpp
@@ -36,6 +36,7 @@ UIPanel::UIPanel(UIManager *ui, const char *name) : UIArea(ui->GetScreen())
 	m_rect.h = m_screen->Height();
 	m_shown = false;
 	m_fullscreen = true;
+	m_cursorVisible = true;
 	m_enterSound = 0;
 	m_leaveSound = 0;
 	m_delegate = NULL;
@@ -70,6 +71,13 @@ UIPanel::Load(rapidxml::xml_node<> *node)
 			m_fullscreen = false;
 		}
 	}
+	attr = node->first_attribute("cursor", 0, false);
+	if (attr) {
+		const char *value = attr->value();
+		if (*value == '0' || *value == 'f' || *value == 'F') {
+			m_cursorVisible = false;
+		}
+	}
 	attr = node->first_attribute("enterSound", 0, false);
 	if (attr) {
 		m_enterSound = atoi(attr->value());
diff --git a/screenlib/UIPanel.h b/screenlib/UIPanel.h
index d21cf9bc..5b398930 100644
--- a/screenlib/UIPanel.h
+++ b/screenlib/UIPanel.h
@@ -65,6 +65,9 @@ class UIPanel : public UIArea
 	bool IsFullscreen() const {
 		return m_fullscreen;
 	}
+	bool IsCursorVisible() const {
+		return m_cursorVisible;
+	}
 
 	bool Load(rapidxml::xml_node<> *node);
 
@@ -97,6 +100,7 @@ class UIPanel : public UIArea
 	UIManager *m_ui;
 	char *m_name;
 	bool m_fullscreen;
+	bool m_cursorVisible;
 	int m_enterSound;
 	int m_leaveSound;
 	UIPanelDelegate *m_delegate;