Browse Source

Weltweiter Bestscore und Spielwiederholung ansehen ist jetzt verfügbar

Kolja Strohm 6 năm trước cách đây
mục cha
commit
8f5b2be624
6 tập tin đã thay đổi với 594 bổ sung142 xóa
  1. 13 2
      Snake/Game.cpp
  2. 24 22
      Snake/Game.h
  3. 184 98
      Snake/Map.cpp
  4. 10 3
      Snake/Map.h
  5. 323 14
      Snake/Menü.cpp
  6. 40 3
      Snake/Menü.h

+ 13 - 2
Snake/Game.cpp

@@ -1,5 +1,6 @@
 #include "Game.h"
 #include <Bild.h>
+#include "../../../Include/KSGKlientV.h"
 
 // Inhalt der Game Klasse aus Game.h
 // Konstruktor
@@ -9,12 +10,15 @@ Game::Game()
 	screen = 0;
 	alpha = 0;
 	menü = 0;
+    client = 0;
 	ref = 1;
 }
 
 // Destruktor
 Game::~Game()
 {
+    if( client )
+        client->release();
 	if( schrift )
 		schrift->release();
 	if( screen )
@@ -29,6 +33,13 @@ bool Game::laden()
 	return 1;
 }
 
+void Game::setMinigameClientZ( MinigameKlientV *client )
+{
+    if( this->client )
+        this->client->release();
+    this->client = client;
+}
+
 void Game::doMausEreignis( MausEreignis &me )
 {
 	if( menü )
@@ -74,14 +85,14 @@ void Game::setSchriftZ( Schrift *schrift )
 {
 	this->schrift = schrift;
 	if( !menü && this->schrift && screen )
-		menü = new Menü( schrift, screen );
+		menü = new Menü( schrift, screen, client->getThis() );
 }
 
 void Game::setBildschirmZ( Bildschirm *screen )
 {
 	this->screen = screen;
 	if( !menü && schrift && screen )
-		menü = new Menü( schrift, screen );
+		menü = new Menü( schrift, screen, client->getThis() );
 }
 
 // constant

+ 24 - 22
Snake/Game.h

@@ -7,30 +7,32 @@
 class Game : public MiniGameV
 {
 private:
-	Schrift *schrift;
-	Bildschirm *screen;
-	Menü *menü;
-	int alpha;
-	int ref;
+    Schrift *schrift;
+    Bildschirm *screen;
+    Menü *menü;
+    MinigameKlientV *client;
+    int alpha;
+    int ref;
 
 public:
-	// Konstruktor
-	Game();
-	// Destruktor
-	~Game();
-	// nicht constant
-	virtual bool laden() override;
-	virtual void doMausEreignis( MausEreignis &me ) override;
-	virtual void doTastaturEreignis( TastaturEreignis &te ) override;
-	virtual bool tick( double zeit ) override;
-	virtual void render( Bild &zRObj ) override;
-	virtual void setSchriftZ( Schrift *schrift ) override;
-	virtual void setBildschirmZ( Bildschirm * screen ) override;
-	// constant
-	virtual bool istEnde() const override;
-	// Reference Counting
-	virtual MiniGameV *getThis() override;
-	virtual MiniGameV *release() override;
+    // Konstruktor
+    Game();
+    // Destruktor
+    ~Game();
+    // nicht constant
+    virtual bool laden() override;
+    virtual void setMinigameClientZ( MinigameKlientV *client ) override;
+    virtual void doMausEreignis( MausEreignis &me ) override;
+    virtual void doTastaturEreignis( TastaturEreignis &te ) override;
+    virtual bool tick( double zeit ) override;
+    virtual void render( Bild &zRObj ) override;
+    virtual void setSchriftZ( Schrift *schrift ) override;
+    virtual void setBildschirmZ( Bildschirm * screen ) override;
+    // constant
+    virtual bool istEnde() const override;
+    // Reference Counting
+    virtual MiniGameV *getThis() override;
+    virtual MiniGameV *release() override;
 };
 
 #endif

+ 184 - 98
Snake/Map.cpp

@@ -6,11 +6,13 @@
 #include <TastaturEreignis.h>
 #include <Zeit.h>
 #include <Text.h>
+#include <AsynchronCall.h>
 
 // Inhalt der Map Klasse aus Map.h
 // Konstruktor
-Map::Map()
+Map::Map( MinigameKlientV *klient )
 {
+    this->klient = klient;
 	schlange = new Array< Pos >();
 	ziele = new Array< Pos >();
 	feld = new LRahmen();
@@ -22,6 +24,8 @@ Map::Map()
 	map->setPosition( 10, 10 );
 	map->setSize( 200, 200 );
 	beendet = 1;
+    gameTime = 0;
+    rGen = 0;
 	move = 1;
 	sr = 1;
 	rend = 0;
@@ -37,11 +41,18 @@ Map::~Map()
 	feld->release();
 	kam->release();
 	map->release();
+    if( rGen )
+        rGen->release();
+    if( klient )
+        klient->release();
 }
 
 // nicht constant
-void Map::reset()
+void Map::reset( Text *zOptionen )
 {
+    gameTime = 0;
+    if( rGen )
+        rGen = rGen->release();
 	next = 0;
 	beendet = 0;
 	richtung = 0;
@@ -49,54 +60,46 @@ void Map::reset()
 	score = 0;
 	schlange->leeren();
 	ziele->leeren();
-	InitDatei *opd = new InitDatei( "data/Minigames/Snake/data/optionen.ini" );
-	if( !opd->laden() )
-		DateiPfadErstellen( "data/Minigames/Snake/data/optionen.ini" );
-	breite = 80;
-	if( opd->wertExistiert( "Breite" ) )
-		breite = (int)*opd->zWert( "Breite" );
-	else
-		opd->addWert( "Breite", "80" );
-	höhe = 50;
-	if( opd->wertExistiert( "Höhe" ) )
-		höhe = (int)*opd->zWert( "Höhe" );
-	else
-		opd->addWert( "Höhe", "50" );
-	zAnzahl = 1;
-	if( opd->wertExistiert( "Ziele" ) )
-		zAnzahl = (int)*opd->zWert( "Ziele" );
-	else
-		opd->addWert( "Ziele", "1" );
-	neuAnzahl = 10;
-	if( opd->wertExistiert( "Anhängen" ) )
-		neuAnzahl = (int)*opd->zWert( "Anhängen" );
-	else
-		opd->addWert( "Anhängen", "10" );
-	geschwindigkeit = 10;
-	if( opd->wertExistiert( "Geschwindigkeit" ) )
-		geschwindigkeit = (int)*opd->zWert( "Geschwindigkeit" );
-	else
-		opd->addWert( "Geschwindigkeit", "10" );
-	bool fortsetzen = 0;
-	if( opd->wertExistiert( "Fortsetzen" ) )
-		fortsetzen = (int)*opd->zWert( "Fortsetzen" ) != 0;
-	else
-		opd->addWert( "Fortsetzen", "0" );
-	opd->speichern();
-	opd->release();
+    Text *tmp = zOptionen->getTeilText( zOptionen->positionVon( '=' ) + 1, zOptionen->positionVon( ',' ) );
+    breite = *tmp;
+    tmp->release();
+    tmp = zOptionen->getTeilText( zOptionen->positionVon( '=', 1 ) + 1, zOptionen->positionVon( ',', 1 ) );
+    höhe = *tmp;
+    tmp->release();
+    tmp = zOptionen->getTeilText( zOptionen->positionVon( '=', 2 ) + 1, zOptionen->positionVon( ',', 2 ) );
+    zAnzahl = *tmp;
+    tmp->release();
+    tmp = zOptionen->getTeilText( zOptionen->positionVon( '=', 3 ) + 1, zOptionen->positionVon( ',', 3 ) );
+    neuAnzahl = *tmp;
+    tmp->release();
+    tmp = zOptionen->getTeilText( zOptionen->positionVon( '=', 4 ) + 1, zOptionen->positionVon( ',', 4 ) );
+    geschwindigkeit = *tmp;
+    tmp->release();
+    tmp = zOptionen->getTeilText( zOptionen->positionVon( '=', 5 ) + 1, zOptionen->positionVon( ',', 5 ) );
+    bool fortsetzen = (int)*tmp != 0;
+    tmp->release();
 	kamPos.x = 0;
 	kamPos.y = 0;
 	if( breite > 80 )
 		kamPos.x = breite / 2 - 40;
 	if( höhe > 50 )
 		kamPos.y = höhe / 2 - 25;
-	if( fortsetzen && DateiExistiert( "data/Minigames/Snake/data/game.save" ) )
+	if( fortsetzen && DateiExistiert( "data/Minigames/Snake/data/game.save" ) && klient )
 	{
+        if( capture.istOffen() )
+            capture.close();
+        capture.setDatei( "data/Minigames/Snake/data/game.mgc" );
+        capture.open( Datei::Style::schreiben | Datei::Style::ende | Datei::Style::lesen );
 		Datei *save = new Datei();
 		save->setDatei( "data/Minigames/Snake/data/game.save" );
 		save->open( Datei::Style::lesen );
 		int br = 0;
 		int hö = 0;
+        __int64 seed;
+        save->lese( (char*)&seed, 8 );
+        rGen = new RandomGenerator();
+        rGen->setSeed( seed );
+        save->lese( (char*)&gameTime, 8 );
 		save->lese( (char*)&br, 4 );
 		save->lese( (char*)&hö, 4 );
 		if( br == breite && hö == höhe )
@@ -125,6 +128,27 @@ void Map::reset()
 		save->close();
 		save->release();
 	}
+    else
+    {
+        rGen = new RandomGenerator();
+        if( klient )
+        {
+            if( capture.istOffen() )
+                capture.close();
+            DateiRemove( "data/Minigames/Snake/data/game.mgc" );
+            capture.setDatei( "data/Minigames/Snake/data/game.mgc" );
+            capture.erstellen();
+            capture.open( Datei::Style::schreiben );
+            __int64 seed = rGen->getSeed();
+            capture.schreibe( (char*)&seed, 8 );
+        }
+        else
+        {
+            tmp = zOptionen->getTeilText( zOptionen->positionVon( '=', 6 ) + 1 );
+            rGen->setSeed( (__int64)*tmp );
+            tmp->release();
+        }
+    }
 	if( !schlange->getEintragAnzahl() )
 		schlange->add( Pos{ (short)( breite / 2 ), (short)( höhe / 2 ) } );
 }
@@ -138,36 +162,62 @@ void Map::doTastaturEreignis( TastaturEreignis &te )
 {
 	if( !move )
 		return;
-	if( te.taste == T_Oben && richtung != 2 )
-	{
-		move = 0;
-		richtung = 0;
-	}
-	if( te.taste == T_Rechts && richtung != 3 )
-	{
-		move = 0;
-		richtung = 1;
-	}
-	if( te.taste == T_Unten && richtung != 0 )
-	{
-		move = 0;
-		richtung = 2;
-	}
-	if( te.taste == T_Links && richtung != 1 )
-	{
-		move = 0;
-		richtung = 3;
-	}
+    cs.lock();
+    if( !beendet )
+    {
+        bool save = 0;
+        if( te.taste == 'w' || te.taste == 'W' || te.taste == T_Oben )
+        {
+            if( te.id == TE_Press && richtung != 2 )
+            {
+                save = 1;
+                move = 0;
+                richtung = 0;
+            }
+        }
+        if( te.taste == 'd' || te.taste == 'D' || te.taste == T_Rechts )
+        {
+            if( te.id == TE_Press && richtung != 3 )
+            {
+                save = 1;
+                move = 0;
+                richtung = 1;
+            }
+        }
+        if( te.taste == 'a' || te.taste == 'A' || te.taste == T_Links )
+        {
+            if( te.id == TE_Press && richtung != 1 )
+            {
+                save = 1;
+                move = 0;
+                richtung = 3;
+            }
+        }
+        if( te.taste == 's' || te.taste == 'S' || te.taste == T_Unten )
+        {
+            if( te.id == TE_Press && richtung != 0 )
+            {
+                save = 1;
+                move = 0;
+                richtung = 2;
+            }
+        }
+        if( klient && save )
+        {
+            capture.schreibe( (char*)&gameTime, 8 );
+            capture.schreibe( (char*)&te.taste, 1 );
+        }
+    }
+    cs.unlock();
 }
 
 bool Map::tick( double tickVal )
 {
 	if( beendet )
 		return 0;
-	if( sr )
-		srand( (unsigned int)time( 0 ) );
-	sr = 0;
 	bool ret = rend;
+    cs.lock();
+    gameTime += tickVal;
 	rend = 0;
 	next -= tickVal;
 	while( next < 0 && !beendet )
@@ -215,7 +265,7 @@ bool Map::tick( double tickVal )
 			addAnzahl += neuAnzahl;
 			ziele->leeren();
 			for( int i = 0; i < zAnzahl; i++ )
-				ziele->add( Pos{ (short)( rand() % breite ), (short)( rand() % höhe ) } );
+				ziele->add( Pos{ (short)( rGen->rand() * breite ), (short)( rGen->rand() * höhe ) } );
 			if( zAnz )
 				score++;
 		}
@@ -227,8 +277,38 @@ bool Map::tick( double tickVal )
 			else
 				addAnzahl--;
 		}
-		else
+		else if( klient )
 		{
+            capture.close();
+            DateiRemove( "data/Minigames/Snake/data/upload.mgc" );
+            DateiUmbenennen( "data/Minigames/Snake/data/game.mgc", "data/Minigames/Snake/data/upload.mgc" );
+            int tmpScore = score;
+            MinigameKlientV *tmpKlient = klient->getThis();
+            new AsynchronCall( [ tmpScore, tmpKlient ]()
+            {
+                InitDatei *opd = new InitDatei( "data/Minigames/Snake/data/optionen.ini" );
+                opd->laden();
+                Text optionen = "Width=";
+                optionen += opd->zWert( "Breite" )->getText();
+                optionen += ",Height=";
+                optionen += opd->zWert( "Höhe" )->getText();
+                optionen += ",Ziele=";
+                optionen += opd->zWert( "Ziele" )->getText();
+                optionen += ",Anhang=";
+                optionen += opd->zWert( "Anhängen" )->getText();
+                optionen += ",Speed=";
+                optionen += opd->zWert( "Geschwindigkeit" )->getText();
+                opd->release();
+                int status = tmpKlient->reportEndOfGame( "Snake", optionen, tmpScore );
+                if( status == 2 )
+                {
+                    Datei d;
+                    d.setDatei( "data/Minigames/Snake/data/upload.mgc" );
+                    tmpKlient->uploadGameCapture( &d );
+                }
+                DateiRemove( "data/Minigames/Snake/data/upload.mgc" );
+                tmpKlient->release();
+            } );
 			KSGTDatei *stb = new KSGTDatei( "data/Minigames/Snake/data/score.ksgt" );
 			if( !stb->laden() )
 				DateiPfadErstellen( "data/Minigames/Snake/data/score.ksgt" );
@@ -261,6 +341,7 @@ bool Map::tick( double tickVal )
             DateiRemove( "data/Minigames/Snake/data/game.save" );
 		}
 	}
+    cs.unlock();
 	return ret;
 }
 
@@ -304,44 +385,49 @@ void Map::render( Bild &zRObj )
 	}
 }
 
-// constant
-void Map::speichern() const
+void Map::speichern()
 {
-	if( !beendet )
-	{
-		Datei *d = new Datei();
-		d->setDatei( "data/Minigames/Snake/data/game.save" );
-		d->erstellen();
-		d->open( Datei::Style::schreiben );
-		d->schreibe( (char*)&breite, 4 );
-		d->schreibe( (char*)&höhe, 4 );
-		d->schreibe( (char*)&score, 4 );
-		d->schreibe( (char*)&richtung, 4 );
-		int anz = schlange->getEintragAnzahl();
-		d->schreibe( (char*)&anz, 4 );
-		for( int i = 0; i < anz; i++ )
-		{
-			short p = schlange->get( i ).x;
-			d->schreibe( (char*)&p, 2 );
-			p = schlange->get( i ).y;
-			d->schreibe( (char*)&p, 2 );
-		}
-		anz = ziele->getEintragAnzahl();
-		d->schreibe( (char*)&anz, 4 );
-		for( int i = 0; i < anz; i++ )
-		{
-			short p = ziele->get( i ).x;
-			d->schreibe( (char*)&p, 2 );
-			p = ziele->get( i ).y;
-			d->schreibe( (char*)&p, 2 );
-		}
-		d->close();
-		d->release();
-	}
-	else
-		DateiRemove( "data/Minigames/Snake/data/game.save" );
+    if( !beendet )
+    {
+        if( capture.istOffen() )
+            capture.close();
+        Datei *d = new Datei();
+        d->setDatei( "data/Minigames/Snake/data/game.save" );
+        d->erstellen();
+        d->open( Datei::Style::schreiben );
+        __int64 seed = rGen->getSeed();
+        d->schreibe( (char*)&seed, 8 );
+        d->schreibe( (char*)&gameTime, 8 );
+        d->schreibe( (char*)&breite, 4 );
+        d->schreibe( (char*)&höhe, 4 );
+        d->schreibe( (char*)&score, 4 );
+        d->schreibe( (char*)&richtung, 4 );
+        int anz = schlange->getEintragAnzahl();
+        d->schreibe( (char*)&anz, 4 );
+        for( int i = 0; i < anz; i++ )
+        {
+            short p = schlange->get( i ).x;
+            d->schreibe( (char*)&p, 2 );
+            p = schlange->get( i ).y;
+            d->schreibe( (char*)&p, 2 );
+        }
+        anz = ziele->getEintragAnzahl();
+        d->schreibe( (char*)&anz, 4 );
+        for( int i = 0; i < anz; i++ )
+        {
+            short p = ziele->get( i ).x;
+            d->schreibe( (char*)&p, 2 );
+            p = ziele->get( i ).y;
+            d->schreibe( (char*)&p, 2 );
+        }
+        d->close();
+        d->release();
+    }
+    else if( klient )
+        DateiRemove( "data/Minigames/Snake/data/game.save" );
 }
 
+// constant
 int Map::getScore() const
 {
 	return score;

+ 10 - 3
Snake/Map.h

@@ -2,6 +2,8 @@
 #define Map_H
 
 #include <Bild.h>
+#include <Random.h>
+#include "../../../Include/KSGKlientV.h"
 
 using namespace Framework;
 
@@ -33,21 +35,26 @@ private:
 	int neuAnzahl;
 	bool sr;
 	bool rend;
+    double gameTime;
+    Datei capture;
+    Critical cs;
+    MinigameKlientV *klient;
+    RandomGenerator *rGen;
 	int ref;
 
 public:
 	// Konstruktor
-	Map();
+	Map( MinigameKlientV *klient );
 	// Destruktor
 	~Map();
 	// nicht constant
-	void reset();
+	void reset( Text *zOptionen );
 	void doMausEreignis( MausEreignis &me );
 	void doTastaturEreignis( TastaturEreignis &te );
 	bool tick( double tickVal );
 	void render( Bild &zRObj );
+    void speichern();
 	// constant
-	void speichern() const;
 	int getScore() const;
 	bool istBeendet() const;
 	// Reference Counting

+ 323 - 14
Snake/Menü.cpp

@@ -10,6 +10,8 @@
 #include <Schrift.h>
 #include <DateiSystem.h>
 #include <Rahmen.h>
+#include <AsynchronCall.h>
+#include <Bildschirm.h>
 
 // Inhalt der MenüVerloren Klasse aus Menü.h
 // Kontruktor
@@ -83,13 +85,14 @@ Men
 
 // Inhalt der MenüSpiel Klasse aus Menü.h
 // Konstruktor
-MenüSpiel::MenüSpiel( Schrift *zSchrift, Bildschirm *zScreen )
+MenüSpiel::MenüSpiel( Schrift *zSchrift, Bildschirm *zScreen, MinigameKlientV *klient )
 {
 	scoreT = initTextFeld( 690, 10, 100, 20, zSchrift, TextFeld::Style::Text, "Score: 0" );
 	beenden = initKnopf( 690, 40, 100, 20, zSchrift, Knopf::Style::Sichtbar, "Beenden" );
-	map = new Map();
+	map = new Map( klient );
 	verloren = new MenüVerloren( zSchrift );
 	beendet = 0;
+    timePuffer = 0;
 	ref = 1;
 }
 
@@ -105,7 +108,61 @@ Men
 // nicht constant
 void MenüSpiel::reset()
 {
-	map->reset();
+    InitDatei *opd = new InitDatei( "data/Minigames/Snake/data/optionen.ini" );
+    if( !opd->laden() )
+        DateiPfadErstellen( "data/Minigames/Snake/data/optionen.ini" );
+    Text optionen = "Width=";
+    if( opd->wertExistiert( "Breite" ) )
+        optionen += (int)*opd->zWert( "Breite" );
+    else
+    {
+        opd->addWert( "Breite", "80" );
+        optionen += 80;
+    }
+    optionen += ",Height=";
+    if( opd->wertExistiert( "Höhe" ) )
+        optionen += (int)*opd->zWert( "Höhe" );
+    else
+    {
+        opd->addWert( "Höhe", "50" );
+        optionen += 50;
+    }
+    optionen += ",Ziele=";
+    if( opd->wertExistiert( "Ziele" ) )
+        optionen += (int)*opd->zWert( "Ziele" );
+    else
+    {
+        opd->addWert( "Ziele", "1" );
+        optionen += 1;
+    }
+    optionen += ",Anhang=";
+    if( opd->wertExistiert( "Anhängen" ) )
+        optionen += (int)*opd->zWert( "Anhängen" );
+    else
+    {
+        opd->addWert( "Anhängen", "10" );
+        optionen += 10;
+    }
+    optionen += ",Speed=";
+    if( opd->wertExistiert( "Geschwindigkeit" ) )
+        optionen += (int)*opd->zWert( "Geschwindigkeit" );
+    else
+    {
+        opd->addWert( "Geschwindigkeit", "10" );
+        optionen += 10;
+    }
+    optionen += ",Fortsetzen=";
+    if( opd->wertExistiert( "Fortsetzen" ) )
+        optionen += (int)*opd->zWert( "Fortsetzen" ) != 0;
+    else
+    {
+        opd->addWert( "Fortsetzen", "0" );
+        optionen += 0;
+    }
+    opd->speichern();
+    opd->release();
+    optionen += ",Seed=0";
+	map->reset( &optionen );
 	beendet = 0;
 	scoreT->setText( "Score: " );
 	scoreT->zText()->append( map->getScore() );
@@ -146,8 +203,13 @@ void Men
 
 bool MenüSpiel::tick( double tickVal )
 {
+    timePuffer += tickVal;
 	bool ret = beenden->tick( tickVal );
-	ret |= map->tick( tickVal );
+    while( timePuffer >= 0.01 )
+    {
+        ret |= map->tick( 0.01 );
+        timePuffer -= 0.01;
+    }
 	scoreT->setText( "Score: " );
 	scoreT->zText()->append( map->getScore() );
 	if( map->istBeendet() )
@@ -186,17 +248,132 @@ Men
 }
 
 
+// Inhalt der MenüWiederhohlung Klasse
+// Konstruktor
+MenüWiederhohlung::MenüWiederhohlung( Schrift *zSchrift, Bildschirm *zScreen, Datei *datei, Text *zOptionen )
+{
+    scoreT = initTextFeld( 690, 10, 100, 20, zSchrift, TextFeld::Style::Text, "Score: 0" );
+    beenden = initKnopf( 690, 40, 100, 20, zSchrift, Knopf::Style::Sichtbar, "Beenden" );
+    this->datei = datei;
+    if( !datei->istOffen() )
+        datei->open( Datei::Style::lesen );
+    __int64 seed;
+    datei->lese( (char*)&seed, 8 );
+    zOptionen->append( ",Fortsetzen=0,Seed=" );
+    zOptionen->append( seed );
+    map = new Map( 0 );
+    map->reset( zOptionen );
+    beendet = 0;
+    nowTime = 0;
+    nextTime = 0;
+    datei->lese( (char*)&nextTime, 8 );
+    timePuffer = 0;
+    ref = 1;
+}
+
+// Destruktor
+MenüWiederhohlung::~MenüWiederhohlung()
+{
+    scoreT->release();
+    beenden->release();
+    map->release();
+    datei->close();
+    datei->release();
+}
+
+// nicht constant
+void MenüWiederhohlung::doMausEreignis( MausEreignis &me )
+{
+    bool vera = me.verarbeitet;
+    beenden->doMausEreignis( me );
+    if( !vera && me.verarbeitet && me.id == ME_RLinks )
+        beendet = 1;
+    if( map->istBeendet() )
+        beendet = 1;
+}
+
+void MenüWiederhohlung::doTastaturEreignis( TastaturEreignis &te )
+{}
+
+bool MenüWiederhohlung::tick( double tickVal )
+{
+    bool ret = beenden->tick( tickVal );
+    // TODO Replay Activity
+    while( nextTime >= 0 && nowTime + tickVal >= nextTime )
+    {
+        while( nowTime < nextTime )
+        {
+            ret |= map->tick( 0.01 );
+            tickVal -= 0.01;
+            nowTime += 0.01;
+        }
+        char aktion;
+        datei->lese( &aktion, 1 );
+        if( datei->istEnde() )
+            nextTime = -1;
+        else
+            datei->lese( (char*)&nextTime, 8 );
+        TastaturEreignis te;
+        te.taste = aktion;
+        te.verarbeitet = 0;
+        te.id = TE_Press;
+        map->doTastaturEreignis( te );
+    }
+    timePuffer += tickVal;
+    while( timePuffer > 0 )
+    {
+        ret |= map->tick( 0.01 );
+        nowTime += 0.01;
+        timePuffer -= 0.01;
+    }
+    scoreT->setText( "Score: " );
+    scoreT->zText()->append( map->getScore() );
+    return ret;
+}
+
+void MenüWiederhohlung::render( Bild &zRObj )
+{
+    map->render( zRObj );
+    scoreT->render( zRObj );
+    beenden->render( zRObj );
+}
+
+// constant
+bool MenüWiederhohlung::istBeendet() const
+{
+    return beendet;
+}
+
+// Reference Counting
+MenüWiederhohlung *MenüWiederhohlung::getThis()
+{
+    ref++;
+    return this;
+}
+
+MenüWiederhohlung *MenüWiederhohlung::release()
+{
+    if( !--ref )
+        delete this;
+    return 0;
+}
+
+
 // Inhalt der MenüStatistik Klasse aus Menü.h
 // Konstruktor
-MenüStatistik::MenüStatistik( Schrift *zSchrift, Bildschirm *zScreen )
+MenüStatistik::MenüStatistik( Schrift *zSchrift, Bildschirm *zScreen, MinigameKlientV *klient )
 {
+    this->klient = klient;
+    wiederH = 0;
+    screen = zScreen->getThis();
 	ansichtT = initTextFeld( 245, 10, 50, 20, zSchrift, TextFeld::Style::Text | TextFeld::Style::VCenter, "Ansicht:" );
 	ansicht = initAuswahlBox( 295, 10, 120, 20, zSchrift, AuswahlBox::Style::Normal | AuswahlBox::Style::Hintergrund, { "Tabelle", "Diagramm" } );
 	optionen = initKontrollKnopf( 425, 10, 130, 20, zSchrift, KontrollKnopf::Style::Normal, "Optionen beachten" );
-	gesammtT = initObjTabelle( 115, 40, 570, 420, zSchrift, ObjTabelle::Style::normal, { { "Score", 70, 0, 0 }, { "Datum", 145, 0, 0 }, { "Breite", 70, 0, 0 }, { "Höhe", 70, 0, 0 }, { "Ziele", 70, 0, 0 }, { "Anhängen", 70, 0, 0 }, { "Tempo", 70, 0, 0 } }, 20 );
-	optionenT = initObjTabelle( 115, 40, 570, 420, zSchrift, ObjTabelle::Style::normal, { { "Score", 70, 0, 0 }, { "Datum", 145, 0, 0 }, { "Breite", 70, 0, 0 }, { "Höhe", 70, 0, 0 }, { "Ziele", 70, 0, 0 }, { "Anhängen", 70, 0, 0 }, { "Tempo", 70, 0, 0 } }, 20 );
-	gesammtD = initLinienDiagramm( 100, 40, 600, 420, zSchrift, LDiag::Style::DatenRahmen, 0 );
-	optionenD = initLinienDiagramm( 100, 40, 600, 420, zSchrift, LDiag::Style::DatenRahmen, 0 );
+    worldBestT = initObjTabelle( 115, 40, 570, 120, zSchrift, ObjTabelle::Style::normal | ObjTabelle::Style::VScroll, { { "Score", 60, 0, 0 },{ "Spieler", 100, 0, 0 },{ "Ansehen", 55, 0, 0 },{ "Breite", 70, 0, 0 },{ "Höhe", 70, 0, 0 },{ "Ziele", 70, 0, 0 },{ "Anhängen", 60, 0, 0 },{ "Tempo", 65, 0, 0 } }, 20 );
+	gesammtT = initObjTabelle( 115, 170, 570, 290, zSchrift, ObjTabelle::Style::normal | ObjTabelle::Style::VScroll, { { "Score", 70, 0, 0 }, { "Datum", 145, 0, 0 }, { "Breite", 70, 0, 0 }, { "Höhe", 70, 0, 0 }, { "Ziele", 70, 0, 0 }, { "Anhängen", 65, 0, 0 }, { "Tempo", 60, 0, 0 } }, 20 );
+	optionenT = initObjTabelle( 115, 170, 570, 290, zSchrift, ObjTabelle::Style::normal | ObjTabelle::Style::VScroll, { { "Score", 70, 0, 0 }, { "Datum", 145, 0, 0 }, { "Breite", 70, 0, 0 }, { "Höhe", 70, 0, 0 }, { "Ziele", 70, 0, 0 }, { "Anhängen", 65, 0, 0 }, { "Tempo", 60, 0, 0 } }, 20 );
+	gesammtD = initLinienDiagramm( 100, 170, 600, 290, zSchrift, LDiag::Style::DatenRahmen, 0 );
+	optionenD = initLinienDiagramm( 100, 170, 600, 290, zSchrift, LDiag::Style::DatenRahmen, 0 );
 	releasen = initKnopf( 10, 470, 100, 20, zSchrift, Knopf::Style::Sichtbar, "Zurücksetzen" );
 	zurück = initKnopf( 350, 470, 100, 20, zSchrift, Knopf::Style::Sichtbar, "Zurück" );
 	schrift = zSchrift->getThis();
@@ -214,6 +391,23 @@ Men
 	}
 	for( int j = 0; j < 7; j++ )
 		( (TextFeld*)optionenT->zZeichnung( j, 0 ) )->release();
+    while( !asyncFinished )
+    {
+        Sleep( 100 );
+    }
+    anz = worldBestT->getZeilenAnzahl();
+    for( int i = 0; i < anz; i++ )
+    {
+        for( int j = 0; j < 8; j++ )
+        {
+            if( j == 2 )
+                ( (Knopf*)worldBestT->zZeichnung( j, 0 ) )->release();
+            else
+                ( (TextFeld*)worldBestT->zZeichnung( j, 0 ) )->release();
+        }
+        worldBestT->removeZeile( 0 );
+    }
+    worldBestT->release();
 	ansichtT->release();
 	ansicht->release();
 	optionen->release();
@@ -224,11 +418,17 @@ Men
 	releasen->release();
 	zurück->release();
 	schrift->release();
+    klient->release();
+    if( wiederH )
+        wiederH->release();
+    screen->release();
 }
 
 // nicht constant
 void MenüStatistik::reset()
 {
+    if( wiederH )
+        wiederH = wiederH->release();
 	int anz = gesammtT->getZeilenAnzahl();
 	for( int i = 1; i < anz; i++ )
 	{
@@ -239,6 +439,81 @@ void Men
 	anz = optionenT->getZeilenAnzahl();
 	for( int i = 1; i < anz; i++ )
 		optionenT->removeZeile( 1 );
+    anz = worldBestT->getZeilenAnzahl();
+    for( int i = 1; i < anz; i++ )
+    {
+        for( int j = 0; j < 8; j++ )
+        {
+            if( j == 2 )
+                ( (Knopf*)worldBestT->zZeichnung( j, 1 ) )->release();
+            else
+                ( (TextFeld*)worldBestT->zZeichnung( j, 1 ) )->release();
+        }
+        worldBestT->removeZeile( 1 );
+    }
+    ObjTabelle *tmpWBT = worldBestT->getThis();
+    MinigameKlientV *tmpKlient = klient->getThis();
+    Schrift *tmpSchrift = schrift->getThis();
+    new AsynchronCall( [ this, tmpWBT, tmpKlient, tmpSchrift ]()
+    {
+        Array< int > score;
+        RCArray< Text > player;
+        RCArray< Text > option;
+        int anz = tmpKlient->getMinigameBestscoreList( "Snake", &score, &player, &option );
+        for( int i = 0; i < anz; i++ )
+        {
+            TextFeld *scoreT = initTextFeld( 0, 0, 0, 0, tmpSchrift, TextFeld::Style::Text | TextFeld::Style::VCenter, Text( score.get( i ) ) );
+            TextFeld *spielerT = initTextFeld( 0, 0, 0, 0, tmpSchrift, TextFeld::Style::Text | TextFeld::Style::VCenter, player.z( i )->getText() );
+            Text optionen = option.z( i )->getText();
+            Text *breite = optionen.getTeilText( optionen.positionVon( '=' ) + 1, optionen.positionVon( ',' ) );
+            Text *höhe = optionen.getTeilText( optionen.positionVon( '=', 1 ) + 1, optionen.positionVon( ',', 1 ) );
+            Text *ziele = optionen.getTeilText( optionen.positionVon( '=', 2 ) + 1, optionen.positionVon( ',', 2 ) );
+            Text *anhang = optionen.getTeilText( optionen.positionVon( '=', 3 ) + 1, optionen.positionVon( ',', 3 ) );
+            Text *tempo = optionen.getTeilText( optionen.positionVon( '=', 4 ) + 1 );
+            TextFeld *breiteT = initTextFeld( 0, 0, 0, 0, tmpSchrift, TextFeld::Style::Text | TextFeld::Style::VCenter, breite->getText() );
+            breite->release();
+            TextFeld *höheT = initTextFeld( 0, 0, 0, 0, tmpSchrift, TextFeld::Style::Text | TextFeld::Style::VCenter, höhe->getText() );
+            höhe->release();
+            TextFeld *zieleT = initTextFeld( 0, 0, 0, 0, tmpSchrift, TextFeld::Style::Text | TextFeld::Style::VCenter, ziele->getText() );
+            ziele->release();
+            TextFeld *anhangT = initTextFeld( 0, 0, 0, 0, tmpSchrift, TextFeld::Style::Text | TextFeld::Style::VCenter, anhang->getText() );
+            anhang->release();
+            TextFeld *tempoT = initTextFeld( 0, 0, 0, 0, tmpSchrift, TextFeld::Style::Text | TextFeld::Style::VCenter, tempo->getText() );
+            tempo->release();
+            Knopf *ansehenK = initKnopf( 0, 0, 0, 0, tmpSchrift, Knopf::Style::Normal, "Ansehen" );
+            ansehenK->setMausEreignis( [ this, optionen ]( void *p, void *obj, MausEreignis me ) -> bool
+            {
+                if( me.id == ME_RLinks && !me.verarbeitet )
+                {
+                    ( (Knopf*)obj )->removeStyle( Knopf::Style::Erlaubt );
+                    getThis();
+                    new AsynchronCall( [ this, optionen, obj ]()
+                    {
+                        Datei *d = klient->downloadGameCapture( "Snake", optionen );
+                        ( (Knopf*)obj )->addStyle( Knopf::Style::Erlaubt );
+                        if( wiederH )
+                            wiederH = wiederH->release();
+                        Text options = optionen.getText();
+                        wiederH = new MenüWiederhohlung( schrift, screen, d, &options );
+                        release();
+                    } );
+                }
+                return 1;
+            } );
+            tmpWBT->addZeile( optionen.getText() );
+            tmpWBT->setZeichnungZ( 0, i + 1, scoreT );
+            tmpWBT->setZeichnungZ( 1, i + 1, spielerT );
+            tmpWBT->setZeichnungZ( 2, i + 1, ansehenK );
+            tmpWBT->setZeichnungZ( 3, i + 1, breiteT );
+            tmpWBT->setZeichnungZ( 4, i + 1, höheT );
+            tmpWBT->setZeichnungZ( 5, i + 1, zieleT );
+            tmpWBT->setZeichnungZ( 6, i + 1, anhangT );
+            tmpWBT->setZeichnungZ( 7, i + 1, tempoT );
+        }
+        tmpKlient->release();
+        tmpWBT->release();
+        tmpSchrift->release();
+    }, &asyncFinished );
 	InitDatei *opd = new InitDatei( "data/Minigames/Snake/data/optionen.ini" );
 	opd->laden();
 	if( !opd->wertExistiert( "Breite" ) )
@@ -373,8 +648,16 @@ void Men
 
 void MenüStatistik::doMausEreignis( MausEreignis &me )
 {
+    if( wiederH )
+    {
+        wiederH->doMausEreignis( me );
+        return;
+    }
 	ansicht->doMausEreignis( me );
 	optionen->doMausEreignis( me );
+    gesammtT->doMausEreignis( me );
+    optionenT->doMausEreignis( me );
+    worldBestT->doMausEreignis( me );
 	bool vera = me.verarbeitet;
 	releasen->doMausEreignis( me );
 	if( !vera && me.verarbeitet && me.id == ME_RLinks )
@@ -415,6 +698,13 @@ void Men
 
 bool MenüStatistik::tick( double tickVal )
 {
+    if( wiederH )
+    {
+        bool ret = wiederH->tick( tickVal );
+        if( wiederH->istBeendet() )
+            wiederH = wiederH->release();
+        return ret;
+    }
 	bool ret = ansicht->tick( tickVal );
 	ret |= optionen->tick( tickVal );
 	ret |= releasen->tick( tickVal );
@@ -423,11 +713,25 @@ bool Men
 	ret |= optionenT->tick( tickVal );
 	ret |= gesammtD->tick( tickVal );
 	ret |= optionenD->tick( tickVal );
+    ret |= worldBestT->tick( tickVal );
 	return ret;
 }
 
 void MenüStatistik::render( Bild &zRObj )
 {
+    if( wiederH )
+    {
+        wiederH->render( zRObj );
+        return;
+    }
+    schrift->setSchriftSize( 12 );
+    Text weltScore = "Welt Score:";
+    schrift->setDrawPosition( 10, 40 );
+    schrift->renderText( &weltScore, zRObj, 0xFFFFFFFF );
+    Text localScore = "Dein Score:";
+    schrift->setDrawPosition( 10, 170 );
+    schrift->renderText( &localScore, zRObj, 0xFFFFFFFF );
+    worldBestT->render( zRObj );
 	if( !ansicht->getAuswahl() )
 	{
 		if( optionen->hatStyle( KontrollKnopf::Style::Selected ) )
@@ -455,6 +759,11 @@ bool Men
 	return beendet;
 }
 
+bool MenüStatistik::istWiederhohlung() const
+{
+    return wiederH != 0;
+}
+
 // Reference Counting
 MenüStatistik *MenüStatistik::getThis()
 {
@@ -689,14 +998,14 @@ Men
 
 // Inhalt der Menü Klasse aus Menü.h
 // Konstruktor
-Menü::Menü( Schrift *zSchrift, Bildschirm *zScreen )
+Menü::Menü( Schrift *zSchrift, Bildschirm *zScreen, MinigameKlientV *klient )
 {
 	spielen = initKnopf( 350, 180, 100, 20, zSchrift, Knopf::Style::Sichtbar, "Spielen" );
-	spielenF = new MenüSpiel( zSchrift, zScreen );
+	spielenF = new MenüSpiel( zSchrift, zScreen, klient->getThis() );
 	optionen = initKnopf( 350, 220, 100, 20, zSchrift, Knopf::Style::Sichtbar, "Optionen" );
 	optionenF = new MenüOptionen( zSchrift, zScreen );
 	statistik = initKnopf( 350, 260, 100, 20, zSchrift, Knopf::Style::Sichtbar, "Statistik" );
-	statistikF = new MenüStatistik( zSchrift, zScreen );
+	statistikF = new MenüStatistik( zSchrift, zScreen, klient );
 	beenden = initKnopf( 350, 300, 100, 20, zSchrift, Knopf::Style::Sichtbar, "Beenden" );
 	ram = new LRahmen();
 	ram->setFarbe( 0xFFFFFFFF );
@@ -825,7 +1134,7 @@ bool Men
 
 void Menü::render( Bild &zRObj )
 {
-	if( status != 3 && hintergrund )
+	if( status != 3 && hintergrund && ( status != 2 || !statistikF->istWiederhohlung() ) )
 		zRObj.drawBild( 0, 0, hintergrund->getBreite(), hintergrund->getHeight(), *hintergrund );
 	switch( status )
 	{
@@ -847,7 +1156,7 @@ void Men
 		optionenF->render( zRObj );
 		break;
 	case 2:
-		if( hintergrund )
+		if( hintergrund && !statistikF->istWiederhohlung() )
 			zRObj.alphaRegion( 0, 0, hintergrund->getBreite(), hintergrund->getHeight(), 0xD0000000 );
 		statistikF->render( zRObj );
 		break;

+ 40 - 3
Snake/Menü.h

@@ -39,12 +39,13 @@ private:
 	Knopf *beenden;
 	Map *map;
 	MenüVerloren *verloren;
+    double timePuffer;
 	bool beendet;
 	int ref;
 
 public:
 	// Konstruktor
-	MenüSpiel( Schrift *zSchrift, Bildschirm *zScreen );
+	MenüSpiel( Schrift *zSchrift, Bildschirm *zScreen, MinigameKlientV *klient );
 	// Destruktor
 	~MenüSpiel();
 	// nicht constant
@@ -60,6 +61,36 @@ public:
 	MenüSpiel *release();
 };
 
+class MenüWiederhohlung
+{
+private:
+    TextFeld *scoreT;
+    Knopf *beenden;
+    Map *map;
+    Datei *datei;
+    double nowTime;
+    double nextTime;
+    bool beendet;
+    double timePuffer;
+    int ref;
+
+public:
+    // Konstruktor
+    MenüWiederhohlung( Schrift *zSchrift, Bildschirm *zScreen, Datei *datei, Text *zOptionen );
+    // Destruktor
+    ~MenüWiederhohlung();
+    // nicht constant
+    void doMausEreignis( MausEreignis &me );
+    void doTastaturEreignis( TastaturEreignis &te );
+    bool tick( double tickVal );
+    void render( Bild &zRObj );
+    // constant
+    bool istBeendet() const;
+    // Reference Counting
+    MenüWiederhohlung *getThis();
+    MenüWiederhohlung *release();
+};
+
 class MenüStatistik
 {
 private:
@@ -68,17 +99,22 @@ private:
 	KontrollKnopf *optionen;
 	ObjTabelle *gesammtT;
 	ObjTabelle *optionenT;
+    ObjTabelle *worldBestT;
 	LDiag *gesammtD;
 	LDiag *optionenD;
 	Knopf *releasen;
 	Knopf *zurück;
 	Schrift *schrift;
+    MinigameKlientV *klient;
+    MenüWiederhohlung *wiederH;
+    Bildschirm *screen;
+    bool asyncFinished;
 	bool beendet;
 	int ref = 1;
 
 public:
 	// Konstruktor
-	MenüStatistik( Schrift *zSchrift, Bildschirm *zScreen );
+	MenüStatistik( Schrift *zSchrift, Bildschirm *zScreen, MinigameKlientV *klient );
 	// Destruktor
 	~MenüStatistik();
 	// nicht constant
@@ -88,6 +124,7 @@ public:
 	void render( Bild &zRObj );
 	// constant
 	bool istBeendet() const;
+    bool istWiederhohlung() const;
 	// Reference Counting
 	MenüStatistik *getThis();
 	MenüStatistik *release();
@@ -148,7 +185,7 @@ private:
 
 public:
 	// Konstruktor
-	Menü( Schrift *zSchrift, Bildschirm *zScreen );
+	Menü( Schrift *zSchrift, Bildschirm *zScreen, MinigameKlientV *klient );
 	// Destruktor
 	~Menü();
 	// nicht constant#