From 1560ddacc221a2c24deff5d36eabcb9facfae026 Mon Sep 17 00:00:00 2001
From: jsalmon3 <>
Date: Wed, 10 Apr 2002 22:48:11 +0000
Subject: [PATCH] Added game stats screen

---
 src/action/action_harvest.cpp  |   1 +
 src/action/action_minegold.cpp |   6 +-
 src/action/action_resource.cpp |   2 +
 src/game/intro.cpp             | 336 +++++++++++++++++++++++++++++++--
 src/include/player.h           |  24 ++-
 src/include/settings.h         |   2 +
 src/stratagus/mainloop.cpp     |   5 +
 src/unit/unit.cpp              |  19 +-
 8 files changed, 377 insertions(+), 18 deletions(-)

diff --git a/src/action/action_harvest.cpp b/src/action/action_harvest.cpp
index 533446701..a937f4f28 100644
--- a/src/action/action_harvest.cpp
+++ b/src/action/action_harvest.cpp
@@ -357,6 +357,7 @@ local int ReturnWithWood(Unit* unit)
     //	Update wood.
     //
     unit->Player->Resources[WoodCost]+=unit->Player->Incomes[WoodCost];
+    unit->Player->TotalResources[WoodCost]+=unit->Player->Incomes[WoodCost];
     if( unit->Player==ThisPlayer ) {
 	MustRedraw|=RedrawResources;
     }
diff --git a/src/action/action_minegold.cpp b/src/action/action_minegold.cpp
index 02c057e82..d451216f3 100644
--- a/src/action/action_minegold.cpp
+++ b/src/action/action_minegold.cpp
@@ -367,11 +367,15 @@ local int MoveToGoldDeposit(Unit* unit)
     //	Update gold.
     //
     if ( OptionUseDepletedMines && unit->Rs == OptionUseDepletedMines ) {
-        unit->Player->Resources[GoldCost]+=
+	unit->Player->Resources[GoldCost]+=
+		(unit->Player->Incomes[GoldCost] * 100)
+			/ OptionUseDepletedMines;
+	unit->Player->TotalResources[GoldCost]+=
 		(unit->Player->Incomes[GoldCost] * 100)
 			/ OptionUseDepletedMines;
     } else {
         unit->Player->Resources[GoldCost]+=unit->Player->Incomes[GoldCost];
+	unit->Player->TotalResources[GoldCost]+=unit->Player->Incomes[GoldCost];
     }
     if( unit->Player==ThisPlayer ) {
 	MustRedraw|=RedrawResources;
diff --git a/src/action/action_resource.cpp b/src/action/action_resource.cpp
index 6855bb4bf..84769bc74 100644
--- a/src/action/action_resource.cpp
+++ b/src/action/action_resource.cpp
@@ -400,6 +400,8 @@ local int MoveToDepot(Unit* unit,const Resource* resource)
     //
     unit->Player->Resources[resource->Cost]
 	+=unit->Player->Incomes[resource->Cost];
+    unit->Player->TotalResources[resource->Cost]
+	+=unit->Player->Incomes[resource->Cost];
     if( unit->Player==ThisPlayer ) {
 	MustRedraw|=RedrawResources;
     }
diff --git a/src/game/intro.cpp b/src/game/intro.cpp
index 6849cc3e1..c4fa4c722 100644
--- a/src/game/intro.cpp
+++ b/src/game/intro.cpp
@@ -41,6 +41,10 @@
 #include "sound.h"
 #include "settings.h"
 #include "ccl.h"
+#include "campaign.h"
+#include "cursor.h"
+#include "menus.h"
+#include "interface.h"
 
 /*----------------------------------------------------------------------------
 --	Declarations
@@ -67,45 +71,89 @@ global Credits	GameCredits;		/// Game credits
 ----------------------------------------------------------------------------*/
 
 local int IntroNoEvent;			/// Flag got an event.
+local int UseContinueButton;
+local int ContinueButtonX;
+local int ContinueButtonY;
+local int ContinueButtonFlags;
 
 /**
 **	Callback for input.
 */
-local void IntroCallbackButton1(unsigned dummy __attribute__((unused)))
+local void IntroCallbackButton1(unsigned button)
 {
-    IntroNoEvent=0;
+    if( UseContinueButton ) {
+	if( (1<<button)==LeftButton &&
+	    ContinueButtonX<=CursorX && CursorX<=ContinueButtonX+106 &&
+	    ContinueButtonY<=CursorY && CursorY<=ContinueButtonY+27 ) {
+	    ContinueButtonFlags|=MenuButtonClicked;
+	}
+    }
+    else {
+	IntroNoEvent=0;
+    }
 }
 
 /**
 **	Callback for input.
 */
-local void IntroCallbackButton2(unsigned dummy __attribute__((unused)))
+local void IntroCallbackButton2(unsigned button)
 {
+    if( UseContinueButton ) {
+	if( (1<<button)==LeftButton &&
+	    ContinueButtonX<=CursorX && CursorX<=ContinueButtonX+106 &&
+	    ContinueButtonY<=CursorY && CursorY<=ContinueButtonY+27 &&
+	    (ContinueButtonFlags&MenuButtonClicked) ) {
+	    IntroNoEvent=0;
+	}
+	ContinueButtonFlags&=~MenuButtonClicked;
+    }
 }
 
 /**
 **	Callback for input.
 */
-local void IntroCallbackKey1(unsigned dummy1 __attribute__((unused)),
-	unsigned dummy2 __attribute__((unused)))
+local void IntroCallbackKey1(unsigned key, unsigned keychar)
 {
-    IntroNoEvent=0;
+    if( UseContinueButton ) {
+	if( keychar=='c' || keychar=='\r' ) {
+	    ContinueButtonFlags|=MenuButtonClicked;
+	}
+    }
+    else {
+	IntroNoEvent=0;
+    }
 }
 
 /**
 **	Callback for input.
 */
-local void IntroCallbackKey2(unsigned dummy1 __attribute__((unused)),
-	unsigned dummy2 __attribute__((unused)))
+local void IntroCallbackKey2(unsigned key, unsigned keychar)
 {
+    if( UseContinueButton ) {
+	if( keychar=='c' || keychar=='\r' ) {
+	    IntroNoEvent=0;
+	    ContinueButtonFlags&=~MenuButtonClicked;
+	}
+    }
 }
 
 /**
 **	Callback for input.
 */
-local void IntroCallbackMouse(int dummy_x __attribute__((unused)),
-	int dummy_y __attribute__((unused)))
+local void IntroCallbackMouse(int x, int y)
 {
+    CursorX=x;
+    CursorY=y;
+
+    if( UseContinueButton ) {
+	if( ContinueButtonX<=CursorX && CursorX<=ContinueButtonX+106 &&
+	    ContinueButtonY<=CursorY && CursorY<=ContinueButtonY+27 ) {
+	    ContinueButtonFlags|=MenuButtonActive;
+	}
+	else {
+	    ContinueButtonFlags&=~MenuButtonActive;
+	}
+    }
 }
 
 /**
@@ -116,6 +164,24 @@ local void IntroCallbackExit(void)
     DebugLevel3Fn("Exit\n");
 }
 
+/**
+**	Draws a continue button at x,y
+*/
+local void DrawContinueButton()
+{
+    DrawMenuButton(MBUTTON_GM_HALF,ContinueButtonFlags,
+	106,27,
+	ContinueButtonX,ContinueButtonY,
+	LargeFont,"~!Continue");
+}
+
+local void InitContinueButton(int x,int y)
+{
+    ContinueButtonX=x;
+    ContinueButtonY=y;
+    ContinueButtonFlags=MenuButtonSelected;
+}
+
 /**
 **	Splits text up into a linked list of lines less than a given width.
 **
@@ -259,6 +325,8 @@ global void ShowIntro(const Intro *intro)
     TextLines* ObjectivesText[MAX_OBJECTIVES];
     int OldVideoSyncSpeed;
 
+    UseContinueButton=0;
+
     VideoLockScreen();
     VideoClearScreen();
     VideoUnlockScreen();
@@ -356,8 +424,6 @@ global void ShowIntro(const Intro *intro)
 	    }
 	}
 
-	// FIXME: draw Continue button
-
 	VideoUnlockScreen();
 
 	// FIXME: update only the changed area!!!!
@@ -437,14 +503,19 @@ global void ShowCredits(Credits *credits)
 	SplitTextIntoLines(credits->Names,320,&ScrollingCredits);
     }
 
+    UseContinueButton=1;
+    InitContinueButton(455,480-40);
+    DestroyCursorBackground();
+
     x=(VideoWidth-640)/2;
     y=(VideoHeight-480)/2;
     IntroNoEvent=1;
     line=0;
     scrolling=1;
-    while( IntroNoEvent ) {
+    while( 1 ) {
 
 	VideoLockScreen();
+	HideAnyCursor();
 
 	//
 	//	Draw background
@@ -463,7 +534,8 @@ global void ShowCredits(Credits *credits)
 	    scrolling=ScrollText(x+140,y+80,320,275,line,ScrollingCredits);
 	}
 
-	// FIXME: draw Continue button
+	DrawContinueButton();
+	DrawAnyCursor();
 
 	VideoUnlockScreen();
 
@@ -472,6 +544,9 @@ global void ShowCredits(Credits *credits)
 	Invalidate();
 	RealizeVideoMemory();
 
+	if( !IntroNoEvent )
+	    break;
+
 	WaitEventsOneFrame(&callbacks);
 
 	++line;
@@ -532,6 +607,8 @@ global void ShowPicture(const char* act,const char* title,const char* picture)
     int maxi;
     int i;
 
+    UseContinueButton=0;
+
     OldVideoSyncSpeed=VideoSyncSpeed;
     VideoSyncSpeed=100;
     SetVideoSync();
@@ -641,6 +718,237 @@ global void ShowPicture(const char* act,const char* title,const char* picture)
     SetVideoSync();
 }
 
+enum {
+    STATS_OUTCOME,
+    STATS_RANK,
+    STATS_SCORE,
+    STATS_UNITS,
+    STATS_BUILDINGS,
+    STATS_GOLD,
+    STATS_WOOD,
+    STATS_OIL,
+    STATS_KILLS,
+    STATS_RAZINGS,
+    MAX_STATS_TEXT
+};
+
+local char* GameStatsText[MAX_STATS_TEXT];
+local int OldVideoSyncSpeed;
+local int GameStatsFrameCounter;
+
+local void GameStatsInit()
+{
+    Graphic* background;
+    char buf[20];
+
+    OldVideoSyncSpeed=VideoSyncSpeed;
+    VideoSyncSpeed=100;
+    SetVideoSync();
+    GameStatsFrameCounter=0;
+
+    background=LoadGraphic(MenuBackground);
+    VideoCreatePalette(GlobalPalette);
+
+    VideoLockScreen();
+    VideoClearScreen();
+    VideoDrawSubClip(background,0,0,background->Width,background->Height,
+	(VideoWidth-background->Width)/2,(VideoHeight-background->Height)/2);
+    VideoUnlockScreen();
+
+    if( GameResult==GameVictory )
+	GameStatsText[STATS_OUTCOME]="Victory!";
+    else
+	GameStatsText[STATS_OUTCOME]="Defeat!";
+
+    GameStatsText[STATS_RANK]="Overlord";
+
+    sprintf(buf,"%u",ThisPlayer->Score);
+    GameStatsText[STATS_SCORE]=strdup(buf);
+
+    sprintf(buf,"%u",ThisPlayer->TotalUnits);
+    GameStatsText[STATS_UNITS]=strdup(buf);
+
+    sprintf(buf,"%u",ThisPlayer->TotalBuildings);
+    GameStatsText[STATS_BUILDINGS]=strdup(buf);
+
+    sprintf(buf,"%u",ThisPlayer->TotalResources[GoldCost]);
+    GameStatsText[STATS_GOLD]=strdup(buf);
+
+    sprintf(buf,"%u",ThisPlayer->TotalResources[WoodCost]);
+    GameStatsText[STATS_WOOD]=strdup(buf);
+
+    sprintf(buf,"%u",ThisPlayer->TotalResources[OilCost]);
+    GameStatsText[STATS_OIL]=strdup(buf);
+
+    sprintf(buf,"%u",ThisPlayer->TotalKills);
+    GameStatsText[STATS_KILLS]=strdup(buf);
+
+    sprintf(buf,"%u",ThisPlayer->TotalRazings);
+    GameStatsText[STATS_RAZINGS]=strdup(buf);
+}
+
+local int GameStatsDrawFunc()
+{
+    int x;
+    int y;
+    int dodraw;
+    int done;
+    const int StatsPause=30;  // Wait one second between each stat
+
+    done=0;
+    GameStatsFrameCounter++;
+
+    if( (GameStatsFrameCounter%StatsPause)!=0 )
+	return done;
+
+    x=(VideoWidth-640)/2;
+    y=(VideoHeight-480)/2;
+    dodraw=GameStatsFrameCounter/StatsPause;
+
+
+    if( dodraw==1 ) {
+	VideoDrawTextCentered(x+106,y+57,LargeFont,"Outcome");
+	VideoDrawTextCentered(x+106,y+78,LargeTitleFont,
+	    GameStatsText[STATS_OUTCOME]);
+    }
+
+    if( dodraw==2 ) {
+	VideoDrawTextCentered(x+324,y+57,LargeFont,"Rank");
+	VideoDrawTextCentered(x+324,y+78,SmallTitleFont,
+	    GameStatsText[STATS_RANK]);
+    }
+
+    if( dodraw==3 ) {
+	VideoDrawTextCentered(x+540,y+57,LargeFont,"Score");
+	VideoDrawTextCentered(x+540,y+78,SmallTitleFont,
+	    GameStatsText[STATS_SCORE]);
+    }
+
+    if( dodraw==4 ) {
+	VideoDrawTextCentered(x+50,y+178,LargeFont,"Units");
+	VideoDrawRectangleClip(ColorBlack,x+10,y+208,80,24);
+	VideoDrawRectangleClip(ColorYellow,x+11,y+209,78,22);
+	VideoFillRectangleClip(ColorBlack,x+12,y+210,76,20);
+	VideoDrawTextCentered(x+50,y+213,LargeFont,
+	    GameStatsText[STATS_UNITS]);
+    }
+
+    if( dodraw==5 ) {
+	VideoDrawTextCentered(x+140,y+178,LargeFont,"Buildings");
+	VideoDrawRectangleClip(ColorBlack,x+100,y+208,80,24);
+	VideoDrawRectangleClip(ColorYellow,x+101,y+209,78,22);
+	VideoFillRectangleClip(ColorBlack,x+102,y+210,76,20);
+	VideoDrawTextCentered(x+140,y+213,LargeFont,
+	    GameStatsText[STATS_BUILDINGS]);
+    }
+
+    if( dodraw==6 ) {
+	VideoDrawTextCentered(x+230,y+178,LargeFont,"Gold");
+	VideoDrawRectangleClip(ColorBlack,x+190,y+208,80,24);
+	VideoDrawRectangleClip(ColorYellow,x+191,y+209,78,22);
+	VideoFillRectangleClip(ColorBlack,x+192,y+210,76,20);
+	VideoDrawTextCentered(x+230,y+213,LargeFont,
+	    GameStatsText[STATS_GOLD]);
+    }
+
+    if( dodraw==7 ) {
+	VideoDrawTextCentered(x+320,y+178,LargeFont,"Lumber");
+	VideoDrawRectangleClip(ColorBlack,x+280,y+208,80,24);
+	VideoDrawRectangleClip(ColorYellow,x+281,y+209,78,22);
+	VideoFillRectangleClip(ColorBlack,x+282,y+210,76,20);
+	VideoDrawTextCentered(x+320,y+213,LargeFont,
+	    GameStatsText[STATS_WOOD]);
+    }
+
+    if( dodraw==8 ) {
+	VideoDrawTextCentered(x+410,y+178,LargeFont,"Oil");
+	VideoDrawRectangleClip(ColorBlack,x+370,y+208,80,24);
+	VideoDrawRectangleClip(ColorYellow,x+371,y+209,78,22);
+	VideoFillRectangleClip(ColorBlack,x+372,y+210,76,20);
+	VideoDrawTextCentered(x+410,y+213,LargeFont,
+	    GameStatsText[STATS_OIL]);
+    }
+
+    if( dodraw==9 ) {
+	VideoDrawTextCentered(x+500,y+178,LargeFont,"Kills");
+	VideoDrawRectangleClip(ColorBlack,x+460,y+208,80,24);
+	VideoDrawRectangleClip(ColorYellow,x+461,y+209,78,22);
+	VideoFillRectangleClip(ColorBlack,x+462,y+210,76,20);
+	VideoDrawTextCentered(x+500,y+213,LargeFont,
+	    GameStatsText[STATS_KILLS]);
+    }
+
+    if( dodraw==10 ) {
+	VideoDrawTextCentered(x+590,y+178,LargeFont,"Razings");
+	VideoDrawRectangleClip(ColorBlack,x+550,y+208,80,24);
+	VideoDrawRectangleClip(ColorYellow,x+551,y+209,78,22);
+	VideoFillRectangleClip(ColorBlack,x+552,y+210,76,20);
+	VideoDrawTextCentered(x+590,y+213,LargeFont,
+	    GameStatsText[STATS_RAZINGS]);
+	done=1;
+    }
+
+    return done;
+}
+
+local void GameStatsEnd(void)
+{
+    int i;
+
+    for( i=0; i<MAX_STATS_TEXT; i++ ) {
+	free(GameStatsText[i]);
+    }
+
+    VideoSyncSpeed=OldVideoSyncSpeed;
+    SetVideoSync();
+}
+
+global void ShowStats()
+{
+    EventCallback callbacks;
+    int done;
+
+    callbacks.ButtonPressed=IntroCallbackButton1;
+    callbacks.ButtonReleased=IntroCallbackButton2;
+    callbacks.MouseMoved=IntroCallbackMouse;
+    callbacks.MouseExit=IntroCallbackExit;
+    callbacks.KeyPressed=IntroCallbackKey1;
+    callbacks.KeyReleased=IntroCallbackKey2;
+    callbacks.NetworkEvent=NetworkEvent;
+    callbacks.SoundReady=WriteSound;
+
+
+    GameStatsInit();
+
+    UseContinueButton=1;
+    InitContinueButton(455,480-40);
+    DestroyCursorBackground();
+
+    done=0;
+    IntroNoEvent=1;
+    // FIXME: Need continue button at 455,480-40
+    while( 1 ) {
+	VideoLockScreen();
+	HideAnyCursor();
+	if( !done ) {
+	    done=GameStatsDrawFunc();
+	}
+	DrawContinueButton();
+	DrawAnyCursor();
+	VideoUnlockScreen();
+
+	Invalidate();
+	RealizeVideoMemory();
+
+	if( !IntroNoEvent )
+	    break;
+
+	WaitEventsOneFrame(&callbacks);
+    }
+
+    GameStatsEnd();
+}
+
 
 /**
 **	Parse the credits configuration.
diff --git a/src/include/player.h b/src/include/player.h
index c8bc966d6..1733222a2 100644
--- a/src/include/player.h
+++ b/src/include/player.h
@@ -221,7 +221,23 @@
 **		Total number of points. You can get points for killing units,
 **		destroying buildings ...
 **
-**	Player::Kills
+**	Player::TotalUnits
+**
+**		Total number of units made.
+**
+**	Player::TotalBuildings
+**
+**		Total number of buildings made.
+**
+**	Player::TotalResources
+**
+**		Total number of resources collected.
+**
+**	Player::TotalRazings
+**
+**		Total number of buildings destroyed.
+**
+**	Player::TotalKills
 **
 **		Total number of kills.
 **
@@ -323,7 +339,11 @@ struct _player_ {
     unsigned	LastRepairResource;	/// last resource for repair cycles
 
     unsigned	Score;			/// Points for killing ...
-    unsigned	Kills;			/// How many unit killed
+    unsigned	TotalUnits;
+    unsigned	TotalBuildings;
+    unsigned	TotalResources[MaxCosts];
+    unsigned	TotalRazings;
+    unsigned	TotalKills;		/// How many unit killed
 
 // Display video
     unsigned	Color;			/// color of units on minimap
diff --git a/src/include/settings.h b/src/include/settings.h
index d4721bf9e..d6f85c60f 100644
--- a/src/include/settings.h
+++ b/src/include/settings.h
@@ -124,6 +124,8 @@ extern void ShowIntro(const Intro* intro);
 extern void ShowCredits();
     /// Show picture
 extern void ShowPicture(const char* act,const char* title,const char* picture);
+    /// Show stats
+extern void ShowStats();
     /// Register Ccl
 extern void CreditsCclRegister(void);
     /// Create a game
diff --git a/src/stratagus/mainloop.cpp b/src/stratagus/mainloop.cpp
index 1397bcd74..37296e145 100644
--- a/src/stratagus/mainloop.cpp
+++ b/src/stratagus/mainloop.cpp
@@ -634,6 +634,11 @@ global void GameMainLoop(void)
 	SetStatusLine("You have won!");
 	ProcessMenu(MENU_VICTORY, 1);
     }
+
+    if( GameResult==GameVictory || GameResult==GameDefeat ) {
+	ShowStats();
+    }
+
     GamePaused=0;
 }
 
diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp
index 10b09c57f..d0f48d9f6 100644
--- a/src/unit/unit.cpp
+++ b/src/unit/unit.cpp
@@ -265,6 +265,12 @@ global Unit* MakeUnit(UnitType* type,Player* player)
     //
     if( player ) {
 	unit->PlayerSlot=player->Units+player->TotalNumUnits++;
+	if( type->Building ) {
+	    player->TotalBuildings++;
+	}
+	else {
+	    player->TotalUnits++;
+	}
 	*unit->PlayerSlot=unit;
 
 	player->UnitTypesCount[type->Type]++;
@@ -1441,6 +1447,12 @@ global void ChangeUnitOwner(Unit* unit,Player* oldplayer,Player* newplayer)
     //	Insert into new player table.
 
     unit->PlayerSlot=newplayer->Units+newplayer->TotalNumUnits++;
+    if( unit->Type->Building ) {
+	newplayer->TotalBuildings++;
+    }
+    else {
+	newplayer->TotalUnits++;
+    }
     *unit->PlayerSlot=unit;
 
     unit->Player=newplayer;
@@ -3128,7 +3140,12 @@ global void HitUnit(Unit* attacker,Unit* target,int damage)
     if( target->HP<=damage ) {	// unit is killed or destroyed
 	if( attacker ) {
 	    attacker->Player->Score+=target->Type->Points;
-	    attacker->Player->Kills++;
+	    if( type->Building ) {
+		attacker->Player->TotalRazings++;
+	    }
+	    else {
+		attacker->Player->TotalKills++;
+	    }
 #ifdef USE_HP_FOR_XP
 	    attacker->XP+=target->HP;
 #else