1 /***************************************************************************
2                           cuyo.cpp  -  description
3                              -------------------
4     begin                : Mit Jul 12 22:54:51 MEST 2000
5     copyright            : (C) 2000 by Immi
6     email                : cuyo@pcpool.mathematik.uni-freiburg.de
7 
8 Modified 2001-2008,2010,2011,2014 by the cuyo developers
9 Maintenance modifications 2012 by Bernhard R. Link
10 Maintenance modifications 2012 by the cuyo developers
11 
12  ***************************************************************************/
13 
14 /***************************************************************************
15  *                                                                         *
16  *   This program is free software; you can redistribute it and/or modify  *
17  *   it under the terms of the GNU General Public License as published by  *
18  *   the Free Software Foundation; either version 2 of the License, or     *
19  *   (at your option) any later version.                                   *
20  *                                                                         *
21  ***************************************************************************/
22 
23 /* 1 bedeutet, dass manche Signale abgefangen werden, um die Log-Datei
24    abzuspeichern. */
25 #define signale_fangen 0
26 
27 
28 
29 #include <cstdlib>
30 #include <cstdio>
31 
32 #if signale_fangen
33 #include <csignal>
34 #endif
35 
36 #include <SDL.h>
37 
38 #include "cuyointl.h"
39 #include "sound.h"
40 #include "aufnahme.h"
41 #include "prefsdaten.h"
42 #include "kiplayer.h"
43 #include "spielfeld.h"
44 #include "fehler.h"
45 #include "global.h"
46 #include "sdltools.h"
47 
48 #include "ui.h"
49 
50 #include "ui2cuyo.h"
51 #include "cuyo.h"
52 
53 /* # Zeitschritte, die nach der Zeitbonus-Animation noch gewartet wird */
54 #define nachbonus_wartezeit 50
55 
ngettext(char * msgid,const char * msgid_plural,unsigned long int n)56 char * ngettext (char * msgid, const char * msgid_plural, unsigned long int n) {return msgid;}
57 
58 
59 
60 namespace Cuyo {
61 
62 /*************************** Private Variablen **************************/
63 /* (stehen nicht in der .h-Datei) */
64 
65 /***** Debug-Variablen *****/
66 /* Es gibt au�erdem noch die globale Variable gDebug; */
67 bool mEinzelschritt;
68 bool mZeitlupe;
69 int mZaehlerZeitlupe;
70 bool mRueberReihenTest;
71 bool mAbspielen;
72 bool mSchnellvorlauf;
73 
74 
75 
76 /** Das Bild, das angezeigt wird, w�hrend das Spiel auf Pause steht. */
77 Bilddatei * mPauseBild;
78 
79 
80 /** Die beiden Spielfelder... */
81 Spielfeld* mSpielfeld[max_spielerzahl];
82 
83 /** Computer-Spieler. */
84 KIPlayer * mKI;
85 
86 
87 
88 /***** Zustandsvariablen *****/
89 
90 /** global-Modus (Spieler�bergreifend). */
91 enum global_modus {
92   gmodus_kein_spiel,
93   gmodus_spiel_start, // wie gmodus_spiel, aber Text muss noch gel�scht werden
94   gmodus_spiel,
95   /* die folgenden beiden Modi bedeuten beide, dass das
96      Spiel (der Level) zu Ende geht, aber dass wir noch warten, bis
97      die letzten Animationen fertig abgelaufen sind */
98   gmodus_warte_verloren,   // ... weil ein Spieler tot ist
99   gmodus_warte_gewonnen, // ... weil der Level fertig ist
100   gmodus_bonus_animation, // Zeit-Bonus bekommen...
101   gmodus_bonus_warte, 	  // Nach dem Zeit-Bonus noch ein bisschen warten
102   gmodus_ende_warte  // Kurz vor gmodus_kein_spiel: Benutzer darf noch
103               // seine Punkte bewundern. Wenn er eine Taste drueckt, geht's
104 	      // Zurueck ins Menue.
105 } mGModus;
106 
107 
108 /** True, wenn das Spiel auf Pause steht */
109 bool mPause;
110 
111 
112 /** true, wenn das Spiel grad nicht weitergehen soll, sondern auf einen
113     Tastendruck gewartet wird. */
114 bool mWarteAufTaste;
115 /* Wenn > 0, wird h�chstens so lange auf Taste gewartet */
116 int mWarteTimeout;
117 
118 /** Hier wird reingespeichert,
119     welche Version (zuletzt) auf der Kommandozeile stand.
120     Achtung! Das passiert schon vor Cuyo::init(), also vor der
121     Lebenszeit von Cuyo, wenn Cuyo eine h�tte. */
122 Version mKommandoZeilenVersion;
123 
124 /** Die Versionen, die �ber die Kommandozeile �bergeben wurden,
125     und die wir nicht anders verwalten. */
126 Version mZusatzVersionen;
127 int mSchwierig;
128 int mLevelpack;
129 /** Ein oder zwei Spieler? (Hat auch beim Spielen gegen die KI den Wert 2,
130     und *nicht* spz_ki) */
131 int mSpielerZahl;
132 /** Falls zwei Spieler: Gegen Computer? */
133 bool mGegenKI;
134 
135 /** Aktuelle Level-Nummer, wenn ein Spiel l�uft. Sonst undefiniert. */
136 int mLevelNr;
137 /** (Interner) Name des aktuellen Levels. Enth�lt, wenn grade kein Spiel
138     l�uft, den Namen vom Level zum weiterspielen. Ist (wenn kein Spiel
139     l�uft) "", wenn es keinen gibt. */
140 Str mIntLevelName;
141 int mPunkte[max_spielerzahl];
142 /** Wird nur w�hrend der Zeitbonus-Animation gebraucht... */
143 int mZeitBonus;
144 
145 
146 
147 
148 /*************************** Private Methoden **************************/
149 /* (stehen nicht in der .h-Datei) */
150 
151 
152 /** tut alles, was beim Starten eines Levels
153     getan werden muss; liefert false, wenn es
154     den Level gar nicht mehr gibt. Throwt bei Fehler. */
155 bool startLevel();
156 /** tut alles, was beim Stoppen eines Levels
157     getan werden muss (ohne Animation, d. h. entweder
158     ist die Animation schon vorbei oder es gibt halt keine). */
159 void stopLevel();
160 /** Setzt die Punktzahl f�r Spieler sp */
161 void setPunkte(int sp, int pu);
162 /** Gibt die Fehlermeldung bestehend aus t und fe aus:
163     Sowohl als Text im Cuyo-Fenster
164     (bei anz_sp vielen Spielern) als auch auf stderr.
165     mitLog wird an fe.getText() weitergegeben. (D. h.: soll
166     ggf. die Send-Log-Meldung ausgegeben werden?) */
167 void printFehler(int anz_sp, Str t, const Fehler & fe,
168                        bool mitLog = false);
169 
170 
171 /** Macht einen Schritt der Hetzrand-kommt-am-Ende-runter-Animation. */
172 void bonusAnimationSchritt();
173 /** Unterfunktion von zeitSchritt();
174     Hier passiert die eigentliche Arbeit. */
175 void zeitSchrittIntern();
176 
177 /** stoppt das Spiel sofort (egal, ob grad ein Level l�uft oder nicht) */
178 void stopSpiel(bool noch_anzeigen = false);
179 /** Die Haupt-Spielschritt-Routine, w�hrend das Spiel l�uft.
180     Ruft alle anderen spielschritt()-Routinen auf. */
181 void spielSchritt();
182 /** Wird von startLevel() und von spielSchritt() aufgerufen. L�sst
183     s�mtliche Blops animieren. */
184 void animiere();
185 
186 Version berechneVersion();
187 
188 /* �bernimmt die Dinge, die durch version spezifiziert werden.
189    Alles andere bleibt beim alten,
190    bis auf da� der alte Wert von mZusatzVersionen verlorengeht.  */
191 void setzeVersion(const Version & version);
192 
193 
194 
195 /** Liefert den Namen und Pfad der Prefs-Datei zur�ck
196     ($HOME/.cuyo) */
197 Str getPrefsName();
198 /** Liefert den Namen und Pfad der Log-Datei zur�ck
199     ($HOME/cuyo.log) */
200 Str getLogName();
201 
202 
203 void signaleAn();
204 void signaleAus();
205 
206 
207 
208 /** Startet das Spiel f�r die eingestellte Spielerzahl und mit dem
209     angegebenen Level */
startSpiel(int level)210 void startSpiel(int level) {
211   CASSERT(mGModus == gmodus_kein_spiel);
212 
213   ld->ladLevelSummary(false,berechneVersion());
214 
215   mLevelNr = level;
216   mIntLevelName =
217     (mLevelNr>ld->getLevelAnz() ? "" : ld->getIntLevelName(level));
218   PrefsDaten::setLastLevel(mIntLevelName);
219 
220   /* Damit der Level nicht schon beim Start angehalten ist oder schnell
221      l�uft */
222   mEinzelschritt = false;
223   mSchnellvorlauf = false;
224 
225   for (int i = 0; i < max_spielerzahl; i++)
226     setPunkte(i, 0);
227 
228   try {
229     if (!startLevel())
230       throw Fehler("%s","There are no (more?) (working?) levels.");
231 
232   } catch (Fehler f) {
233     print_to_stderr(f.getText()+"\n");
234     for (int i = 0; i < mSpielerZahl; i++)
235       mSpielfeld[i]->setText(f.getText(), true);
236 
237     /* Sicherheitshalber...: */
238     stopSpiel();
239 
240     return;
241   }
242 }
243 
244 
245 
246 
247 /** tut alles, was beim Starten eines Levels
248     getan werden muss; liefert false, wenn es
249     den Level gar nicht mehr gibt. Throwt bei Fehler. */
startLevel()250 bool startLevel() {
251 
252   /* So viele Level gibt's doch gar nicht */
253   if (mLevelNr > ld->getLevelAnz())
254     return false;
255 
256   /* Der Levelname sollte schon bekannt sein. Wir geben ihn gleich mal im
257      Fenstertitel aus. */
258   SDLTools::setLevelTitle(ld->getLevelName(mLevelNr));
259 
260   /* Muss der Level neu geladen werden, oder ist er noch von vorher
261      im Speicher? */
262   if (!ld->mLevelGeladen || ld->mLevelNummer != mLevelNr) {
263 
264     /* Ok, neu laden. Erst mal alten Level entladen, damit w�hrend
265        der "loading level"-Anzeige nicht noch falsche Informationen
266        �ber den alten Level angezeigt werden. */
267     ld->entladLevel();
268 
269     /* F�r "Loading level" */
270     ld->setSchriftFarbe(Color(80, 80, 80));
271 
272     /* Level-Daten laden */
273     for (int i = 0; i < mSpielerZahl; i++) {
274       mSpielfeld[i]->ladeLevelModus();
275       mSpielfeld[i]->setText(_sprintf(_("Score: %d\n\nLoading Level %d...\n\n"),
276 	        mPunkte[i], mLevelNr));
277     }
278     /* "Loading Level" gleich anzeigen. Au�erdem den Rand neu malen, da
279        wir die Schriftfarbe ge�ndert haben und das Numexplode vom alten
280        level ung�ltig ist und so. */
281     UI::randNeuMalen();
282     UI::sofortAllesAnzeigen();
283 
284     /* So, und jetzt den neuen Level laden. (Braucht etwas Zeit) */
285     ld->ladLevel(mLevelNr);
286 
287   }
288   else ld->erneuerLevel(mLevelNr);
289 
290   //for (int i = 0; i < max_spielerzahl; i++) {
291   //  /* Punkte-Anzeige muss geupdatet werden: Zu Level passende Farbe.
292   //     (Etwas gepfuscht) */
293   //  setPunkte(i, mPunkte[i]);
294   //}
295   /* Einige Farben haben sich durch das level-laden ge�ndert. Also
296      nach dem Level alles neu malen. Und da die mal-Routine nicht auf
297      die Idee kommt, dass die R�nder um den Level neu an den X-Server
298      geschickt werden m�ssen, selbst Area::updateAll() aufrufen. */
299   UI::randNeuMalen();
300 
301   /* Aufnehmen / Abspielen starten */
302   Aufnahme::init(mAbspielen, getSpielerModus());
303 
304   /***** Allen Leuten erz�hlen, dass jetzt ein Level anf�ngt. *****/
305 
306   /* F�r den Global-Blop... */
307   ld->startLevel();
308 
309   /* Hier ist die Stelle, wo wir darauf gucken, wie viele Spieler mitspielen;
310      nur f�r so viele Spieler wird das Spiel wirklich gestartet */
311   for (int i = 0; i < mSpielerZahl; i++) {
312 
313     mSpielfeld[i]->startLevel();
314     Str PlatzAnzahlFormat;
315     if (ld->mPlatzAnzahlMin == ld->mPlatzAnzahlMax)
316       PlatzAnzahlFormat=_sprintf(ngettext("1 blop explodes",
317 					  "%d blops explode",
318 					  ld->mPlatzAnzahlMin),
319 				 ld->mPlatzAnzahlMin);
320     else
321       if (ld->mPlatzAnzahlAndere)
322 	PlatzAnzahlFormat=_sprintf(_("between %d and %d blops explode"),
323 				  ld->mPlatzAnzahlMin,ld->mPlatzAnzahlMax);
324       else
325 	PlatzAnzahlFormat=_sprintf(_("%d or %d blops explode"),
326 				  ld->mPlatzAnzahlMin,ld->mPlatzAnzahlMax);
327     // TRANSLATORS: The second %s (after "by ") is the level's author's name
328     mSpielfeld[i]->setText(_sprintf(_("Score: %d\n\n"
329                 "Level %d\n%s\nby %s\n\n"
330                 "%s\n%s\n"
331 		"%s\n\n"
332 		"Space = Start"),
333 	    mPunkte[i],
334 	    mLevelNr, ld->mLevelName.data(), ld->mLevelAutor.data(),
335 	    PlatzAnzahlFormat.data(),
336 	    ld->mGrasBeiKettenreaktion ? _("Chain reaction necessary\n") : "",
337 	    ld->mBeschreibung.data()));
338   }
339 
340   /* Init-Events veschicken */
341   Blop::sendeGeschedulteEvents();
342 
343   /* Alle Blops einmal animieren, damit sie wissen, wie sie aussehen.
344      Dabei werden auch die ganzen Init-Events verschickt. */
345   animiere();
346 
347   /* Auch die KI m�chte wissen, wenn ein neuer Level anf�ngt. */
348   if (mGegenKI)
349     mKI->startLevel();
350 
351 
352   Sound::setMusic(ld->mMusik);
353 
354   mGModus = gmodus_spiel_start;
355   mPause = false;
356   mWarteAufTaste = true;
357   mWarteTimeout = 0;
358   return true;
359 }
360 
361 
362 
363 /** tut alles, was beim Stoppen eines Levels
364     getan werden muss (ohne Animation, d. h. entweder
365     ist die Animation schon vorbei oder es gibt halt keine). */
stopLevel()366 void stopLevel() {
367   SDLTools::setMainTitle();
368 
369   for (int i = 0; i < mSpielerZahl; i++)
370     mSpielfeld[i]->stopLevel();
371 }
372 
373 
374 
375 
376 /** Setzt die Punktzahl f�r Spieler sp */
setPunkte(int sp,int pu)377 void setPunkte(int sp, int pu){
378   mPunkte[sp] = pu;
379   UI::setPunkte(sp, pu);
380 }
381 
382 
383 
384 
385 
386 
387 
388 
389 
390 
391 
392 
393 /** stoppt das Spiel sofort (egal, ob grad ein Level l�uft oder nicht) */
stopSpiel(bool noch_anzeigen)394 void stopSpiel(bool noch_anzeigen/* = false*/) {
395   /* Evtl. wird stopSpiel() aufgerufen, wenn das Spiel noch gar nicht
396      richtig fertig gestartet wurde; dann wurde mGModus vielleicht noch
397      nicht gesetzt, aber es soll trotzdem was gestoppt werden... */
398   /*if (mGModus == gmodus_kein_spiel)
399     return;*/
400 
401   stopLevel();
402 
403   if (noch_anzeigen) {
404     mGModus = gmodus_ende_warte;
405     mWarteAufTaste = true;
406   } else {
407     mGModus = gmodus_kein_spiel;
408     UI::stopSpiel();
409   }
410 }
411 
412 
413 
414 
415 
416 
417 
418 
419 
420 
421 /** Die Haupt-Spielschritt-Routine, w�hrend das Spiel l�uft.
422     Ruft alle anderen spielschritt()-Routinen auf. */
spielSchritt()423 void spielSchritt() {
424 
425   /* Den Schritt aufnehmen bzw. abspielen. Beim Abspielen werden
426      hier auch ggf. Tastendr�cke abgespielt. */
427   Aufnahme::recSchritt(mSpielfeld);
428 
429   /* Ggf. einen Spielschritt f�r die KI */
430   if (mGegenKI)
431     mKI->spielSchritt();
432 
433 
434   /*** Die einzelnen Teile eines Spielschritts ausf�hren: ***/
435 
436   /* Who will be the one killing me for this #define? */
437   /* Answer: The first one to try
438        for (int i=1; i<=10; i++)
439          ALLE_SPIELER->do_stuff(i);
440   */
441 #define ALLE_SPIELER for (int i = 0; i < mSpielerZahl; i++) mSpielfeld[i]
442 
443   /* ggf. Message blinken lassen */
444   ALLE_SPIELER->blinkeMessage();
445 
446   /* Hetzrand runterbewegen; evtl. sterben */
447   ALLE_SPIELER->bewegeHetzrand();
448 
449   /* Evtl. zuf�llige Graue an die Spieler senden. (Blops werden nicht
450      erzeugt; es wird nur gespeichert, dass das noch Graue auf das
451      ankommen warten. */
452   ALLE_SPIELER->zufallsGraue();
453 
454   ALLE_SPIELER->fallSchritt();
455   Blop::sendeGeschedulteEvents();
456 
457   /* Reihen hin/her. */
458   ALLE_SPIELER->rueberReihenSchritt();
459   Blop::sendeGeschedulteEvents();
460 
461   /* Neue Explosionen testen. Da dabei keine Blops manipuliert werden, k�nnen
462      wir um alles eine gro�e gleichzeit machen. */
463   Blop::beginGleichzeitig();
464   ALLE_SPIELER->testeFlopp();
465   Blop::endGleichzeitig();
466 
467   /* Rest vom normalen Spielschritt */
468   ALLE_SPIELER->spielSchritt();
469   Blop::sendeGeschedulteEvents();
470 
471 #undef ALLE_SPIELER
472 
473   /* Animationen und Grafik-Ausgabe */
474   animiere();
475 
476 
477 
478 
479   if (mGModus == gmodus_spiel) {
480     /* Nur wenn das Spiel wirklich noch l�uft (und nicht nur darauf gewartet
481        wird, das wir beenden k�nnen: Testen, ob wir gewonnen haben */
482 
483     /* Wurde grade das restliche Gras vernichtet? */
484     for (int i = 0; i < mSpielerZahl; i++)
485       if (mSpielfeld[i]->getGrasAnz()>0)
486         goto noch_gras_da;
487 
488     /* Dann warten wir jetzt nur noch auf einen guten Moment zum beenden. */
489     mGModus = gmodus_warte_gewonnen;
490 
491 noch_gras_da:;
492   } else {
493     /* Nur wenn das Spiel kurz davor ist, beendet zu werden:
494        Checken, obs jetzt wirklich beendet werden kann. */
495 
496     CASSERT(mGModus == gmodus_warte_gewonnen ||
497             mGModus == gmodus_warte_verloren);
498     /* Spiel (Level) soll beendet werden; aber sind auch beide
499        Spieler bereit? */
500 
501     bool bereit = true;
502     for (int i = 0; i < mSpielerZahl; i++)
503       if (!mSpielfeld[i]->bereitZumStoppen())
504 	bereit = false;
505 
506     if (bereit) {
507       /* OK, alle bereit. */
508 
509       /* Gewonnen oder verloren? */
510       if (mGModus == gmodus_warte_gewonnen) {
511 
512         Sound::playSample(sample_levelwin,so_global);
513 
514 	/* Hier darf noch nicht stopLevel() aufgerufen werden, weil sonst
515 	   das Spielfeld nicht mehr angezeigt wird */
516 
517 	/* Gewonnen. Level als gewonnen abspeichern. Aber nicht im
518 	   Debug-Modus. */
519 	if (!gDebug)
520 	  PrefsDaten::schreibGewonnenenLevel(mSpielerZahl > 1, mIntLevelName);
521 
522 	/* Jetzt kommt die Zeitbonus-Animation */
523 	mGModus = gmodus_bonus_animation;
524 
525 	/* Noch keine Bonus-Punkte */
526 	mZeitBonus = 0;
527       } else {
528         Sound::playSample(sample_levelloose, so_global);
529 
530 	/* Spiel zu Ende weil verloren;
531            true = Spieler darf sich noch seine Punkte anschauen */
532 	stopSpiel(true);
533 
534 	for (int i = 0; i < mSpielerZahl; i++)
535 	  mSpielfeld[i]->setText(_sprintf(
536             _("Game over\n\nScore: %d\n\n"), mPunkte[i]));
537       }
538 
539     }	// Ende: if bereit
540   } // Ende: if warten, bis alle f�r's Spielende bereit sind
541 
542 
543 }  // spielSchritt()
544 
545 
546 
547 
548 /** Macht einen Schritt der Hetzrand-kommt-am-Ende-runter-Animation. */
bonusAnimationSchritt()549 void bonusAnimationSchritt() {
550   /* Hetzrand runterrutschen lassen */
551   bool ba_fertig = true; // "= true" nur um eine Warnung zu ersparen
552 
553   mZeitBonus += punkte_fuer_zeitbonus;
554 
555 #define bonusMessage _("Level %s complete!\n\nTime Bonus: %d\nScore: %d\n\n%s")
556 
557   for (int i = 0; i < mSpielerZahl; i++) {
558 
559     /* Neuen Text (mit neuer Punkt-Zahl) schreiben */
560     mSpielfeld[i]->setText(_sprintf(bonusMessage,
561 	      ld->mLevelName.data(), mZeitBonus, mPunkte[i], " "));
562 
563     /* Rand runterrutschen lassen */
564     ba_fertig = mSpielfeld[i]->bonusSchritt();
565 
566     /* Punkte bekommen */
567     setPunkte(i, mPunkte[i] + punkte_fuer_zeitbonus);
568   }
569 
570   /* Unten angekommen? (Sollte bei beiden Spielern gleichzeitig passieren) */
571   if (ba_fertig) {
572     mGModus = gmodus_bonus_warte;
573 
574     for (int i = 0; i < mSpielerZahl; i++) {
575       mSpielfeld[i]->setText(_sprintf(bonusMessage,
576 	      ld->mLevelName.data(), mZeitBonus, mPunkte[i],
577 	      _("Space = Continue")));
578     }
579 
580     mWarteAufTaste = true;
581     /* Kein Warte-Timeout mehr. Einfach Leertaste dr�cken */
582     //mWarteTimeout = nachbonus_wartezeit;
583   } // Ende: if Bonus-Animation fertig
584 
585 #undef bonusMessage
586 
587 }
588 
589 
590 /** Unterfunktion von zeitSchritt();
591     Hier passiert die eigentliche Arbeit. */
zeitSchrittIntern()592 void zeitSchrittIntern() {
593 
594   /* W�hrend Pause l�uft das Spiel nicht weiter */
595   if (mPause)
596     return;
597 
598   /* Warten wir grad drauf, dass der Benutzer eine Taste dr�ckt? */
599   if (mWarteAufTaste) {
600 
601     if (mWarteTimeout) {
602       /* Wir warten nicht beliebig lang */
603       mWarteTimeout--;
604       if (mWarteTimeout)
605 	return;
606 
607       /* Zu lange gewartet. Jetzt reicht's! */
608       mWarteAufTaste = false;
609     } else
610       return;
611   }
612 
613   /* Einzelschritt-Modus. Nach diesem Schritt gleich wieder auf
614      Tastendruck warten. */
615   if (mEinzelschritt) {
616     mWarteAufTaste = true;
617   }
618 
619 
620   if (mGModus == gmodus_ende_warte) {
621     /* Spieler hat sich seine Punkte (oder was auch immer sont)
622        fertig angeschaut. Jetzt koennen wir zurueck ins Menue */
623     mGModus = gmodus_kein_spiel;
624     UI::stopSpiel();
625     return;
626   }
627 
628   if (mGModus == gmodus_bonus_warte) {
629     /* Wir haben grade ein bisschen gewartet, damit der Benutzer seine
630        Level-Endpunkte bewundern kann. Jetzt geht's weiter mit dem n�chsten
631        Level... */
632 
633     /* Alten Level stoppen (ist vermutlich nicht wirklich n�tig) */
634     stopLevel();
635 
636     /* neuer Level */
637 
638     mLevelNr++;
639     mIntLevelName =
640       (mLevelNr>ld->getLevelAnz() ? "" : ld->getIntLevelName(mLevelNr));
641     try {
642       if (!startLevel()) {
643         /* Es gibt gar keine Level mehr; also Spiel beenden */
644         stopSpiel(true);
645         for (int i = 0; i < mSpielerZahl; i++) {
646           mSpielfeld[i]->setText(
647             _sprintf(mSpielerZahl == 2 && mPunkte[i] > mPunkte[1-i] ?
648                 _("***\nYou won even a bit more!!!\n\nScore: %d\n***\n\n") :
649                 _("***\nYou won!!!\n\nScore: %d\n***\n\n")  ,
650 	      mPunkte[i]));
651         }
652         mLevelNr = 0; // Jetzt kann man nicht mehr Restart last level
653         mIntLevelName = "";
654       }
655     } catch (Fehler fe) {
656       printFehler(mSpielerZahl, "Could not start level:\n", fe);
657       stopSpiel();
658     }
659     PrefsDaten::setLastLevel(mIntLevelName);
660     return;
661   }
662 
663   /* Spiel grade erst gestartet? Dann Texte l�schen */
664   if (mGModus == gmodus_spiel_start) {
665     for (int i = 0; i < mSpielerZahl; i++)
666       mSpielfeld[i]->setText("");
667     mGModus = gmodus_spiel;
668   }
669 
670   CASSERT(mGModus == gmodus_bonus_animation || mGModus == gmodus_spiel ||
671           mGModus == gmodus_warte_gewonnen || mGModus == gmodus_warte_verloren);
672 
673 
674   /* Ok, hier kommt der eigentliche Spielschritt... */
675   if (mGModus == gmodus_bonus_animation) {
676     /* ... Spielschritt Bonusanimation */
677     bonusAnimationSchritt() ;
678 
679   } else {
680     /* ... normaler Spielschritt */
681 
682 
683     /* Evtl. Signale abfangen, um die Logdatei abspeichern zu k�nnen. */
684     signaleAn();
685 
686     try {
687       /* Hier findet das eigentliches Spiel statt */
688       spielSchritt();
689 
690     } catch (Fehler fe) {
691       signaleAus();
692       bool log_gespeichert = false;
693 
694       try {
695         Aufnahme::speichern(getLogName());
696 	log_gespeichert = true;
697       } catch (Fehler) {}
698 
699       /* Im Debug-Modus soll das Spielfeld weiter angezeigt werden,
700          wenn ein Fehler w�hrend des Spiels passiert. */
701       stopSpiel(gDebug);
702 
703       /* Wenn das Speichern der log-Datei geklappt hat, soll ggf.
704          die "send log to..."-Meldung ausgegeben werden. */
705       printFehler(mSpielerZahl, "Error during the game:\n", fe,
706                   log_gespeichert);
707       return;
708     }
709 
710     signaleAus();
711   }
712 
713 
714 
715 }
716 
717 
718 /** Wird von startLevel() und von zeitSchritt() aufgerufen. L�sst
719     s�mtliche Blops animieren. */
animiere()720 void animiere() {
721 
722   Blop::beginGleichzeitig();
723 
724   /* Alle Grafiken l�schen */
725   Blop::lazyLeereStapel();
726 
727   /* Erst mal das globale Blop ausf�hren. */
728   ld->spielSchritt();
729 
730   /* Die eigentliche Animation */
731   for (int i = 0; i < mSpielerZahl; i++)
732     mSpielfeld[i]->animiere();
733 
734   Blop::endGleichzeitig();
735 }
736 
737 
738 
739 /**  */
neuePunkte(bool reSp,int pt)740 void neuePunkte(bool reSp, int pt){
741   int sp = (reSp ? 1 : 0);
742   int punkte = mPunkte[sp] + pt;
743   if (punkte<0) punkte=0;
744   setPunkte(sp,punkte);
745 }
746 
747 
748 
749 /** wird aufgerufen, wenn ein Spieler tot ist */
spielerTot()750 void spielerTot() {
751   mGModus = gmodus_warte_verloren;
752 }
753 
754 
755 
756 
757 
758 /* reSp sendet g Graue an den anderen Spieler */
sendeGraue(bool reSp,int g)759 void sendeGraue(bool reSp, int g) {
760   mSpielfeld[!reSp]->empfangeGraue(g);
761 }
762 
763 
764 /** reSp bittet den anderen Spieler um eine Reihe. Er selbst
765     hat H�he h. Antwort ist eine der Konstanten bewege_reihe_xxx */
bitteUmReihe(bool reSp,int h)766 int bitteUmReihe(bool reSp, int h) {
767   return mSpielfeld[!reSp]->bitteUmReihe(h);
768 }
769 
770 
771 /** reSp will einen Stein vom anderen Spieler (r�berreihe) */
willStein(bool reSp,Blop & s)772 void willStein(bool reSp, Blop & s) {
773   mSpielfeld[!reSp]->gebStein(s);
774 }
775 
776 
777 
778 
779 
780 /** Liefert den Namen und Pfad der Log-Datei zur�ck
781     ($HOME/cuyo.log) */
getLogName()782 Str getLogName() {
783   char * ho = getenv("HOME");
784   if (!ho) {
785     /* Unter Windows zum Beispiel... */
786     print_to_stderr("Warning: Env-Variable $HOME not found. Using the current directory for cuyo.log");
787     return "cuyo.log";
788   }
789   if (ho[strlen(ho) - 1] == '/')
790     return Str(ho) + "cuyo.log";
791   else
792     return Str(ho) + "/cuyo.log";
793 }
794 
795 
796 
797 /** Gibt die Fehlermeldung bestehend aus t und fe aus:
798     Sowohl als Text im Cuyo-Fenster
799     (bei anz_sp vielen Spielern) als auch auf stderr.
800     mitLog wird an fe.getText() weitergegeben. (D. h.: soll
801     ggf. die Send-Log-Meldung ausgegeben werden?) */
printFehler(int anz_sp,Str t,const Fehler & fe,bool mitLog)802 void printFehler(int anz_sp, Str t, const Fehler & fe,
803                        bool mitLog /*= false*/) {
804   t += fe.getText(mitLog);
805   print_to_stderr(t+"\n");
806   for (int i = 0; i < anz_sp; i++) {
807     /* zweites true = Kleine Schrift... */
808     mSpielfeld[i]->setText(t, true);
809   }
810 }
811 
812 
813 
814 
815 
816 
817 #if signale_fangen
818 
signalHandler(int s)819 void signalHandler(int s) {
820   try {
821     Aufnahme::speichern(getLogName());
822     /* send_log_string ist in fehler.h definiert. */
823     print_to_stderr(send_log_string+"\n");
824   } catch (Fehler) {}
825 
826   /* Braucht man das? Geht das? */
827   raise(s);
828 }
829 
830 
signaleAn()831 void signaleAn() {
832   /*{
833     struct sigaction act;
834     act.sa_handler = speicherFehler;
835     act.sa_mask = 0;
836     act.sa_flags = SA_ONESHOT;
837     sigaction(SIGSEGV, &act, 0);
838   }*/
839   signal(SIGILL, signalHandler);  // illegal instruction
840   signal(SIGSEGV, signalHandler); // segmentation fault
841   signal(SIGFPE, signalHandler);  // floating point exception
842 }
843 
signaleAus()844 void signaleAus() {
845   signal(SIGILL, SIG_DFL);  // illegal instruction
846   signal(SIGSEGV, SIG_DFL); // segmentation fault
847   signal(SIGFPE, SIG_DFL);  // floating point exception
848 }
849 
850 #else
signaleAn()851 void signaleAn() {
852 }
853 
signaleAus()854 void signaleAus() {
855 }
856 #endif
857 
858 
859 
860 /********************** public Methoden (f�r das Spiel) **********************/
861 /* (stehen in cuyo.h) */
862 
863 
864 
865 
866 
867 
868 
869 
870 
871 /** liefert true, wenn das Spiel normal l�uft, false
872 		wenn das Spiel am zuende gehen ist */
getSpielLaeuft()873 bool getSpielLaeuft() {
874   CASSERT(mGModus != gmodus_kein_spiel);
875   return mGModus == gmodus_spiel;
876 }
877 
878 
879 /** liefert true, wenn das Spiel gepaust ist. */
getSpielPause()880 bool getSpielPause() {
881   return mPause;
882 }
883 
884 /** Liefert die Anzahl der Mitspieler zur�ck. */
getSpielerZahl()885 int getSpielerZahl() {
886   return mSpielerZahl;
887 }
888 
889 
890 /** Liefert das Pause-Bildchen zur�ck */
getPauseBild()891 Bilddatei * getPauseBild() {
892   return mPauseBild;
893 }
894 
895 
896 /** Liefert true, wenn debug-R�berreihen-Test aktiv ist */
getRueberReihenTest()897 bool getRueberReihenTest() {
898   return mRueberReihenTest;
899 }
900 
901 
902 /** Liefert ein Spielfeld zur�ck. */
getSpielfeld(bool reSp)903 Spielfeld * getSpielfeld(bool reSp) {
904   return mSpielfeld[reSp];
905 }
906 
907 
908 
909 
910 /*********************** public Methoden f�rs ui **************************/
911 /* (stehen in ui2cuyo.h) */
912 
913 
init()914 void init() {
915   mEinzelschritt = false;
916   mZeitlupe = false;
917   mZaehlerZeitlupe = 0;
918   mRueberReihenTest = 0;
919   mAbspielen = 0;
920   mSchnellvorlauf = 0;
921   mPauseBild = 0;
922   mZusatzVersionen = Version();
923   mSchwierig = PrefsDaten::getDifficulty();
924 
925   int spielermodus = PrefsDaten::getPlayers();
926   mGegenKI = spielermodus==spielermodus_computer;
927   mSpielerZahl = (mGegenKI ? 2 : spielermodus);
928 
929   mLevelpack = PrefsDaten::getLevelTrack();
930   CASSERT(mLevelpack>=0);
931 
932   /* Jetzt gehen auf der Kommandozeile spezifizierte Versionen ein. */
933   setzeVersion(mKommandoZeilenVersion);
934 
935   /* Hier findet das parsen statt. (Fehler werden im try-catch-Block
936      der main()-Routine ausgegeben.) Das LevelDaten-Objekt weist
937      sich selber der globalen Variable ld zu. */
938   new LevelDaten(berechneVersion());
939 
940 
941   for (int i = 0; i < max_spielerzahl; i++) {
942     /* Spielfeld erzeugen */
943     mSpielfeld[i] = new Spielfeld(i > 0); // (i > 0) = rechter Spieler
944   }
945 
946   /* Pause-Bild laden */
947   mPauseBild = new Bilddatei();
948   mPauseBild->laden("pause.xpm");
949 
950   /* KI-Spieler erzeugen */
951   mKI = new KIPlayer(mSpielfeld[1]);
952 
953   setSpielerModus(spielermodus);
954     // Sollte erst nach Erzeugen der Men�s aufgerufen werden...
955 
956   mIntLevelName = PrefsDaten::getLastLevel();
957   mLevelNr = ld->getLevelNr(mIntLevelName);
958 
959   mGModus = gmodus_kein_spiel;
960 
961   mWarteAufTaste = false;
962 }
963 
destroy()964 void destroy(){
965   delete ld;
966   delete mKI;
967 }
968 
969 
970 
setPause(bool pause)971 void setPause(bool pause) {
972   mPause = pause;
973   for (int i = 0; i < mSpielerZahl; i++)
974     mSpielfeld[i]->setUpdateAlles();
975   UI::nachEventAllesAnzeigen();
976 }
977 
978 
979 /** Ein key-Event halt... (K�mmert sich um alle Tasten,
980     die w�hrend des Spiels so gedr�ckt werden...). */
keyEvent(const SDL_keysym & taste)981 void keyEvent(const SDL_keysym & taste) {
982 
983   if (mPause) {
984     switch (taste.sym) {
985       case SDLK_ESCAPE:
986         stopSpiel();
987         UI::nachEventAllesAnzeigen();
988 	break;
989       case ' ':
990       case SDLK_RETURN:
991       case SDLK_KP_ENTER:
992         setPause(false);
993 	break;
994       default:
995         break;
996     }
997     return;
998   }
999 
1000 
1001   if (taste.sym == SDLK_ESCAPE) {
1002     /* Auf Pause schalten */
1003     setPause(true);
1004     return;
1005   }
1006 
1007   if (mWarteAufTaste) {
1008     if (taste.sym == ' ' || taste.sym == SDLK_RETURN ||
1009         taste.sym == SDLK_KP_ENTER) {
1010       mWarteAufTaste = false;
1011       mWarteTimeout = 0;
1012       return;
1013     }
1014 
1015   } else if (mGModus == gmodus_spiel) {
1016     int sp, t;
1017     if (PrefsDaten::getTaste(taste.sym, sp, t)) {
1018       /* Im 1-Spieler-Modus und im KI-Modus alle Tastendr�cke an
1019 	 Spieler 1 senden: */
1020       if (mSpielerZahl == 1 || mGegenKI) sp = 0;
1021 
1022       Aufnahme::recTaste(sp, t);
1023       mSpielfeld[sp]->taste(t);
1024       return;
1025     }
1026   } // if spiel l�uft
1027 
1028 
1029   /* Event noch nicht verarbeitet. Dann vielleicht debug? */
1030 }
1031 
1032 
1033 /** Eine Taste wurde gedrueckt, von der das ui befunden hat, dass
1034     es sich um eine debug-Taste handeln koennte.
1035     Liefert zurueck, ob die Taste tats�chlich erkannt werden konnte. */
debugKeyEvent(const SDL_keysym & taste)1036 bool debugKeyEvent(const SDL_keysym & taste) {
1037 
1038   char buch = taste.sym;
1039   if ((taste.mod & KMOD_SHIFT) && buch >= 'a' && buch <= 'z')
1040     buch = buch - 'a' + 'A';
1041 
1042   if (buch == 'S') {
1043     print_to_stderr("Debug: Save log file\n");
1044     Aufnahme::speichern(getLogName());
1045     return true;
1046   }
1047 
1048   /* Handle other keys only in debug mode. */
1049 
1050   if (!gDebug)
1051     return false;
1052 
1053   if (buch == 'b') {
1054     for (int i = 0; i < mSpielerZahl; i++)
1055       mSpielfeld[i]->empfangeGraue(1);
1056     print_to_stderr("Debug: Receive greys\n");
1057 
1058   } else if (buch == 'e') {
1059     mEinzelschritt = !mEinzelschritt;
1060     print_to_stderr(_sprintf("Debug: Single step mode = %d\n",
1061 			     mEinzelschritt));
1062     if (!mEinzelschritt) {
1063       /* Nicht noch ein letztes Mal auf Taste warten... */
1064       mWarteAufTaste = false;
1065     }
1066 
1067   } else if (buch == 'f') {
1068     if (mGModus==gmodus_spiel) {
1069       for (int i=0; i<mSpielerZahl; i++)
1070         mSpielfeld[i]->resetFall();
1071       print_to_stderr("Debug: Replace fall\n");
1072     }
1073 
1074   } else if (buch == 'L') {
1075     if (mGModus == gmodus_kein_spiel) {
1076       print_to_stderr("Debug: Load log file\n");
1077       Aufnahme::laden(getLogName());
1078       setSpielerModus(Aufnahme::getSpielerModus());
1079       mIntLevelName = Aufnahme::getLevelName();
1080       mLevelNr = ld->getLevelNr(mIntLevelName);
1081       if (mLevelNr==0) mIntLevelName="";
1082       PrefsDaten::setLastLevel(mIntLevelName);
1083 
1084       // TRANSLATORS: The %d is the level number
1085       print_to_stderr(_sprintf("Level %s (%d)\nFor replay start level.\n",
1086 			       mIntLevelName.data(), mLevelNr));
1087       mAbspielen = true;
1088     } else
1089       print_to_stderr("Debug: Do *not* load log file during the game.\n");
1090 
1091   } else if (buch == 'l') {
1092     mAbspielen = !mAbspielen;
1093     print_to_stderr(_sprintf("Debug: Replay log file = %d\n", mAbspielen));
1094 
1095   } else if (buch == 'g') {
1096     /* Hoffentlich darf man gModus einfach so �ndern... */
1097 
1098     if (mGModus == gmodus_spiel) {
1099       mGModus = gmodus_warte_gewonnen;
1100       print_to_stderr("Debug: Win instantly\n");
1101     }
1102 
1103   } else if (buch == 'h') {
1104     print_to_stderr("Debug: Help (all keys to be combined with Alt)\n"
1105 		    "  b: Receive greys\n"
1106 		    "  e: Single step mode on/off\n"
1107                     "  f: Replace fall\n"
1108 		    "  g: Win instantly\n"
1109 		    "  h: Help (this one)\n"
1110 		    "  L: Load log file\n"
1111 		    "  l: Replay log file on/off\n"
1112 		    "  r: Reload level\n"
1113 		    "  S: Save log file\n"
1114 		    "  t: Test of row-exchange on/off\n"
1115 		    "  v: Fast forward on/off\n"
1116 		    "  z: Slow motion on/off\n");
1117 
1118   } else if (buch == 'r') {
1119     if (mGModus == gmodus_kein_spiel) {
1120       print_to_stderr("Debug: Reload levelconf\n");
1121       try {
1122    	ld->ladLevelSummary(true,berechneVersion());
1123       } catch (Fehler fe) {
1124 	printFehler(2, "Could not reload the level description file because of the following error:\n",
1125 	            fe);
1126       }
1127     } else
1128       print_to_stderr("Debug: Do *not* load log file during the game.\n");
1129 
1130   } else if (buch == 't') {
1131     mRueberReihenTest = !mRueberReihenTest;
1132     print_to_stderr(_sprintf("Debug: Test of row-exchange = %d\n",
1133 			     mRueberReihenTest));
1134 
1135   } else if (buch == 'v') {
1136     mSchnellvorlauf = !mSchnellvorlauf;
1137     print_to_stderr(_sprintf("Debug: Fast forward = %d\n", mSchnellvorlauf));
1138 
1139   } else if (buch == 'z') {
1140     mZeitlupe = !mZeitlupe;
1141     print_to_stderr(_sprintf("Debug: Slow motion = %d\n", mZeitlupe));
1142 
1143   } else return false;
1144 
1145   return true;
1146 }
1147 
1148 
1149 
1150 /*
1151 f�r xtrace:
1152 extern "C" {
1153 int XInternAtom(Display *display, char *atom_name, int only_if_exists);
1154 }
1155 */
1156 
1157 
1158 
1159 /** Die Haupt-Zeitschritt-Routine. Wird direkt
1160     vom ui aufgerufen. Ruft alle spielschritt()-Routinen u.�. auf. */
zeitSchritt()1161 void zeitSchritt() {
1162   CASSERT(mGModus != gmodus_kein_spiel);
1163 
1164 /*
1165 f�r xtrace:
1166  XInternAtom(qt_xdisplay(),"zeitschritt A",1);
1167 */
1168 
1169   /* Zeitlupen-Debug-Modus? */
1170   if (mZeitlupe) {
1171     mZaehlerZeitlupe++;
1172     if (mZaehlerZeitlupe == 5)
1173       mZaehlerZeitlupe = 0;
1174     else
1175       return;
1176   }
1177 
1178   if (mSchnellvorlauf) {
1179     for (int i = 0; i < 3; i++)
1180       if (mGModus != gmodus_kein_spiel) zeitSchrittIntern();
1181   } else {
1182     zeitSchrittIntern();
1183   }
1184 
1185   /* Brauche kein UI::allesAnzeigen(); darum kuemmert sich das spiel-Blatt selbst */
1186 }  // zeitSchritt()
1187 
1188 
1189 /** Markiert alle Graphik auf upzudaten; danach muss noch malSpielfeld()
1190     aufgerufen werden, um das Update wirklich zu machen */
setUpdateAlles()1191 void setUpdateAlles() {
1192   for (int i = 0; i < mSpielerZahl; i++)
1193     mSpielfeld[i]->setUpdateAlles();
1194 }
1195 
1196 
1197 /** Spielfeld neu malen. Wird vom ui aufgerufen. */
malSpielfeld(int sp)1198 void malSpielfeld(int sp) {
1199   mSpielfeld[sp]->malUpdateAlles();
1200 }
1201 
1202 /** NaechstesFall neu malen. Wird vom ui aufgerufen. */
malNaechstesFall(int sp)1203 void malNaechstesFall(int sp) {
1204   mSpielfeld[sp]->malUpdateNaechstesFall();
1205 }
1206 
1207 
1208 
getSpielerModus()1209 int getSpielerModus() {
1210   if (mGegenKI)
1211     return spielermodus_computer;
1212   else
1213     return mSpielerZahl;
1214 }
1215 
1216 /** Setzt #Spieler, KI-Modus; gemerkte Level-Nummer wird auf 0
1217     zurueckgesetzt. Vorbedingung: Es l�uft grad kein Spiel. */
setSpielerModus(int spm)1218 void setSpielerModus(int spm) {
1219   CASSERT(mGModus==gmodus_kein_spiel);
1220   if (spm == spielermodus_computer) {
1221     mGegenKI = true;
1222     mSpielerZahl = 2;
1223   } else {
1224     mGegenKI = false;
1225     mSpielerZahl = spm;
1226   }
1227   PrefsDaten::setPlayers(spm);
1228 }
1229 
1230 
1231 /** Der int ist der Index in Version::mLevelpack bzw Version::mSchwierig */
setLevelpack(int i)1232 void setLevelpack(int i) {
1233   mLevelpack = i;
1234   PrefsDaten::setLevelTrack(i);
1235 }
1236 
getLevelpack()1237 int getLevelpack() {return mLevelpack;}
1238 
setSchwierig(int i)1239 void setSchwierig(int i) {
1240   mSchwierig = i;
1241   PrefsDaten::setDifficulty(i);
1242 }
1243 
getSchwierig()1244 int getSchwierig() {return mSchwierig;}
1245 
1246 
1247 /* Liefert Nr. des zuletzt gespielten Levels (oder 0) */
getLetzterLevel()1248 int getLetzterLevel() {
1249   return ld->getLevelNr(mIntLevelName);
1250 }
1251 
1252 
1253 
berechneVersion()1254 Version berechneVersion() {
1255   Version ret = mZusatzVersionen;
1256   ret.nochEinMerkmal(mSpielerZahl==1 ? "1" : "2");
1257   if (Version::gSchwierig.mMerkmale[mSchwierig] != "")
1258     ret.nochEinMerkmal(Version::gSchwierig.mMerkmale[mSchwierig]);
1259   ret.nochEinMerkmal(Version::gLevelpack.mMerkmale[mLevelpack]);
1260   return ret;
1261 }
1262 
1263 
1264 /* �bernimmt die Dinge, die durch version spezifiziert werden.
1265    Alles andere bleibt beim alten,
1266    bis auf da� der alte Wert von mZusatzVersionen verlorengeht.  */
setzeVersion(const Version & version)1267 void setzeVersion(const Version & version) {
1268   mZusatzVersionen = version;
1269 
1270   /* Achtung! Jetzt enth�lt mZusatzVersionen erstmal zu viel.
1271      Aber der �berschu� wird gleich rausgel�scht. */
1272 
1273   try {
1274     mSpielerZahl = (
1275       mZusatzVersionen.extractMerkmal(dimaa_numspieler,
1276 				      (mSpielerZahl==1 ? "1" : "2"))
1277         == "1"
1278       ? 1 : 2);
1279     mSchwierig = mZusatzVersionen.extractMerkmal(Version::gSchwierig,
1280 						 mSchwierig);
1281     mLevelpack = mZusatzVersionen.extractMerkmal(Version::gLevelpack,
1282 						 mLevelpack);
1283   }
1284   catch(Str konflikt) {throw Fehler("Conflicting versions: %s",
1285 					 konflikt.data());}
1286 }
1287 
1288 
1289 }
1290 
1291