1 /***************************************************************************
2                           blatt.cpp  -  description
3                              -------------------
4     begin                : Mit Jul 12 22:54:51 MEST 2000
5     copyright            : (C) 2006 by Immi
6     email                : cuyo@pcpool.mathematik.uni-freiburg.de
7 
8 Modified 2006-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 #include <cstdio>
24 #include <cmath>
25 
26 #include "../config.h"
27 
28 #include "sdltools.h"
29 
30 #include "font.h"
31 
32 
33 #include "cuyointl.h"
34 #include "ui.h"
35 #include "ui2cuyo.h"
36 #include "cuyo.h"
37 #include "punktefeld.h"
38 #include "fehler.h"
39 #include "global.h"
40 #include "sound.h"
41 
42 #include "prefsdaten.h"
43 
44 #include "layout.h"
45 #include "blatt.h"
46 #include "menueintrag.h"
47 
48 
49 #define border_pic_laenge 640
50 
51 
52 
53 /********************************************************************************/
54 /* Ein paar Malfunktionen und so */
55 
56 
chooseX(int x10,int x20,int x21,int spz,int sp)57 int chooseX(int x10, int x20, int x21, int spz, int sp) {
58   if (spz == 1) return x10;
59   else if (sp == 0) return x20;
60   else return x21;
61 }
62 
63 
malVertRand(int x,int end_icon=2)64 void malVertRand(int x, int end_icon = 2) {
65   Blatt::gBlattPics[blattpic_borderv]->malStreifenV(
66     x, 0, L_fenster_hoehe, SDLTools::rect(0, 0, L_rand, border_pic_laenge));
67   Blatt::gBlattPics[blattpic_border]->malBildAusschnitt(
68     x, -L_rand, SDLTools::rect(end_icon * L_rand, L_rand, L_rand, L_rand));
69   Blatt::gBlattPics[blattpic_border]->malBildAusschnitt(
70     x, L_fenster_hoehe, SDLTools::rect(end_icon * L_rand, 3 * L_rand, L_rand, L_rand));
71 }
72 
malInfosRand(int x,int y)73 void malInfosRand(int x, int y) {
74   Blatt::gBlattPics[blattpic_borderh]->malStreifenH(
75     x, y, L_infos_breite, SDLTools::rect(0, 0, border_pic_laenge, L_rand));
76   Blatt::gBlattPics[blattpic_border]->malBildAusschnitt(
77     x - L_rand, y, SDLTools::rect(L_rand, 2 * L_rand, L_rand, L_rand));
78   Blatt::gBlattPics[blattpic_border]->malBildAusschnitt(
79     x + L_infos_breite, y, SDLTools::rect(3 * L_rand, 2 * L_rand, L_rand, L_rand));
80 }
81 
82 
malRahmen(int w)83 void malRahmen(int w) {
84   /* Ober- und Unterkante */
85   Blatt::gBlattPics[blattpic_borderh]->malStreifenH(
86     0, -L_rand, w, SDLTools::rect(0, 0, border_pic_laenge, L_rand));
87   Blatt::gBlattPics[blattpic_borderh]->malStreifenH(
88     0, L_fenster_hoehe, w, SDLTools::rect(0, 0, border_pic_laenge, L_rand));
89 
90   /* Linke und rechte Kante */
91   malVertRand(-L_rand, 1);
92   malVertRand(w, 3);
93 }
94 
95 
96 
97 /********************************************************************************/
98 
99 
doEvent(const SDL_Event & evt)100 void Blatt::doEvent(const SDL_Event & evt) {
101   switch (evt.type) {
102     case SDL_KEYDOWN:
103       keyEvent(evt.key.keysym);
104       break;
105     case SDL_MOUSEMOTION:
106       mouseMotionEvent(evt.motion.state & SDL_BUTTON(SDL_BUTTON_LEFT),
107                        evt.motion.x, evt.motion.y,
108                        evt.motion.x - evt.motion.xrel, evt.motion.y - evt.motion.yrel);
109       break;
110     case SDL_MOUSEBUTTONUP:
111     case SDL_MOUSEBUTTONDOWN:
112       if (evt.button.button == SDL_BUTTON_LEFT) {
113         mouseButtonEvent(evt.button.state == SDL_PRESSED, evt.button.x, evt.button.y);
114       }
115       break;
116     case SDL_VIDEORESIZE:
117       /* Bildschirminhalt neu malen */
118       resizeEvent();
119       break;
120     default:
121       break;
122   }
123 }
124 
125 
126 
127 Bilddatei * Blatt::gBlattPics[anz_blattpics];
128 
initBlaetter()129 void Blatt::initBlaetter() {
130 
131   for (int i=0; i<anz_blattpics; i++)
132     gBlattPics[i] = new Bilddatei();
133 
134   gBlattPics[blattpic_pfeile]->laden("menupics.xpm");
135   gBlattPics[blattpic_highlight]->laden("highlight.xpm");
136   gBlattPics[blattpic_titel]->laden("titel.xpm");
137   gBlattPics[blattpic_scroll]->laden("scroll.xpm");
138   gBlattPics[blattpic_scrollbright]->klonen(*gBlattPics[blattpic_scroll]);
139   gBlattPics[blattpic_scrolldimmed]->klonen(*gBlattPics[blattpic_scroll]);
140 
141   gBlattPics[blattpic_scroll]->setFaerbung(Color(130,130,220));
142   gBlattPics[blattpic_scrollbright]->setFaerbung(Color(255,255,128));
143   gBlattPics[blattpic_scrolldimmed]->setFaerbung(Color(80,80,145));
144 
145   gBlattPics[blattpic_border]->laden("border.xpm");
146   gBlattPics[blattpic_borderh]->laden("borderh.xpm");
147   gBlattPics[blattpic_borderv]->laden("borderv.xpm");
148   AutoColor::gGame.addUser(gBlattPics[blattpic_border]);
149   AutoColor::gGame.addUser(gBlattPics[blattpic_borderh]);
150   AutoColor::gGame.addUser(gBlattPics[blattpic_borderv]);
151 
152   gBlattPics[blattpic_infoicons]->laden("infoicons.xpm");
153 
154 }
155 
destroyBlaetter()156 void Blatt::destroyBlaetter() {
157   for (int i=0; i<anz_blattpics; i++)
158     delete gBlattPics[i];
159 }
160 
161 
162 
163 /********************************************************************************/
164 
165 
BlattSpiel()166 BlattSpiel::BlattSpiel() {
167   mPunktefeld[0] = new Punktefeld();
168   mPunktefeld[1] = new Punktefeld();
169   mDekoUpdaten = false;
170 }
171 
172 
173 
~BlattSpiel()174 BlattSpiel::~BlattSpiel() {
175   delete mPunktefeld[0];
176   delete mPunktefeld[1];
177 }
178 
179 
oeffnen(int lnr)180 void BlattSpiel::oeffnen(int lnr) {
181   UI::setBlatt(this);
182   SDLTools::setVirtualWindowSize(
183      Cuyo::getSpielerZahl() == 1 ? L_fenster_breite_1sp : L_fenster_breite_2sp,
184      L_fenster_hoehe);
185   Cuyo::startSpiel(lnr);
186 }
187 
188 
189 
190 
191 
keyEvent(const SDL_keysym & taste)192 void BlattSpiel::keyEvent(const SDL_keysym & taste) {
193   Cuyo::keyEvent(taste);
194 }
195 
196 
resizeEvent()197 void BlattSpiel::resizeEvent() {
198   mDekoUpdaten = true;
199   Cuyo::setUpdateAlles();
200   UI::nachEventAllesAnzeigen();
201 }
202 
203 
204 
205 
malInfos(int sp,int x)206 void BlattSpiel::malInfos(int sp, int x) {
207   Color c = ld->mHintergrundFarbe;
208 
209   malInfosRand(x, L_naechstesfall_hoehe); /*------------*/
210 
211 
212   Area::fillRect(x, L_naechstesfall_hoehe + L_rand, L_infos_breite, L_punkte_y - 2 * L_rand - L_naechstesfall_hoehe, c);
213   Str p =
214        Cuyo::getSpielerModus() == spielermodus_computer && sp == 1 ?
215          _("Computer") : _sprintf(_("Player %d"), sp + 1);
216   Font::gGame->drawText(p, x + L_infos_breite / 2, L_player_y + gric / 2, AlignCenter);
217 
218   malInfosRand(x, L_punkte_y - L_rand); /*------------*/
219 
220   Area::enter(SDLTools::rect(x, L_punkte_y,
221                              L_infos_breite, L_punkte_hoehe));
222   /* Wenn sich die Randfarbe ge�ndert hat, dann Punkte auf
223      jeden Fall neu malen */
224   mPunktefeld[sp]->updateGraphik(mDekoUpdaten);
225   Area::leave();
226 
227   malInfosRand(x, L_punkte_y + L_punkte_hoehe); /*------------*/
228 
229   int pu = L_punkte_y + L_punkte_hoehe + L_rand;
230   Area::fillRect(x, pu, L_infos_breite, L_infoblobs_y - L_rand - pu, c);
231 
232   malInfosRand(x, L_infoblobs_y - L_rand); /*------------*/
233 
234   /* Level name does not really fit in */
235 //   malInfosRand(x, L_levelname_y - L_rand); /*------------*/
236 //   /* Put text in an area so that it gets clipped if it is too long */
237 //   Area::enter(SDLTools::rect(x, L_levelname_y,
238 //                              L_infos_breite, gric));
239 //   Font::gGame->drawText(ld->mLevelName, L_infos_breite / 2, gric / 2, AlignCenter);
240 //   Area::leave();
241 
242   Area::fillRect(x + L_infoblobs_breite, L_infoblobs_y, L_infos_breite - L_infoblobs_breite, L_fenster_hoehe - L_infoblobs_y, c);
243 
244   if (ld->mLevelGeladen) {
245     /* To be cleaned: Instead of being the full height, the following area should rather
246      * go from L_infoblobs_y to the bottom */
247     Area::enter(SDLTools::rect(x, 0, L_infos_breite, L_fenster_hoehe));
248     Spielfeld* spielfeld = Cuyo::getSpielfeld(sp);
249     if (ld->mGreysAtAll)
250       Font::gGame->drawText(_sprintf("%d", spielfeld->getGrauAnz()),
251         L_greygrass_x2,L_grey_y+gric/2,AlignLeft);
252     Font::gGame->drawText(_sprintf("%d", spielfeld->getGrasAnz()),
253       L_greygrass_x2,L_grass_y+gric/2,AlignLeft);
254 
255     Str PlatzAnzahlFormat;
256     if (ld->mPlatzAnzahlMin == ld->mPlatzAnzahlMax) {
257       /* TRANSLATORS: During game: short version of "%d blops explode" */
258       PlatzAnzahlFormat=_sprintf(_("%d"), ld->mPlatzAnzahlMin);
259     } else {
260       if (ld->mPlatzAnzahlAndere) {
261         /* TRANSLATORS: During game: short version of
262            "between %d and %d blops explode" */
263         PlatzAnzahlFormat=_sprintf(_("%d-%d"),
264 				  ld->mPlatzAnzahlMin,ld->mPlatzAnzahlMax);
265       } else {
266         /* TRANSLATORS: During game: short version of
267            "%d or %d blops explode" */
268         PlatzAnzahlFormat=_sprintf(_("%d/%d"),
269 				  ld->mPlatzAnzahlMin,ld->mPlatzAnzahlMax);
270       }
271     }
272     Font::gGame->drawText(PlatzAnzahlFormat, L_levelexplode_x2, L_levelexplode_y + gric / 2, AlignLeft);
273     Area::updateRect(L_greygrass_x2, L_infoblobs_y,
274                      L_infos_breite-L_greygrass_x2, L_infoblobs_hoehe);
275     Area::leave();
276   }
277 }
278 
279 
anzeigen()280 void BlattSpiel::anzeigen() {
281   Color c = Color(150, 150, 150);
282 
283   Area::fillBorder(c);
284 
285   int spz = Cuyo::getSpielerZahl();
286 
287   malRahmen(spz == 1 ? L_fenster_breite_1sp : L_fenster_breite_2sp);
288 
289   if (spz == 1) {
290     malVertRand(chooseX(L_spielfeld_x, 1, 0) - L_rand);
291   } else {
292     malVertRand(chooseX(L_spielfeld_x, 2, 0) - L_rand);
293     malVertRand(chooseX(L_spielfeld_x, 2, 1) - L_rand);
294     malVertRand(chooseX(L_infos_x, 2, 1) - L_rand);
295   }
296 
297   for (int i = 0; i < spz; i++) {
298 
299     Area::enter(SDLTools::rect(chooseX(L_spielfeld_x, spz, i), L_spielfeld_y,
300                                L_spielfeld_breite, L_spielfeld_hoehe));
301     Cuyo::malSpielfeld(i);
302     Area::leave();
303 
304     malInfos(i, chooseX(L_infos_x, spz, i));
305 
306     Area::enter(SDLTools::rect(chooseX(L_infos_x, spz, i), 0,
307                                L_infos_breite, L_infos_hoehe));
308     Cuyo::malNaechstesFall(i);
309     Area::leave();
310   }
311 
312   /* Neue Randfarbe? Dann alles an den xserver schicken */
313   if (mDekoUpdaten) {
314     Area::updateAll();
315   }
316   mDekoUpdaten = false;
317 }
318 
319 
320 
zeitSchritt()321 void BlattSpiel::zeitSchritt() {
322   Cuyo::zeitSchritt();
323 
324   for (int i = 0; i < max_spielerzahl; i++)
325     mPunktefeld[i]->zwinkerSchritt();
326 
327   /* Den folgenden Aufruf sollte man eigentlich
328      Cuyo und den Punktefeldern ueberlassen; dann
329      wuerd's nur passieren, wenn's muss */
330   UI::nachEventAllesAnzeigen();
331 }
332 
333 
randNeuMalen()334 void BlattSpiel::randNeuMalen() {
335   mDekoUpdaten = true;
336 }
337 
setPunkte(int sp,int pt)338 void BlattSpiel::setPunkte(int sp, int pt) {
339   mPunktefeld[sp]->setPunkte(pt);
340 }
341 
342 
343 
344 /*****************************************************************************/
345 
346 
BlattMenu(bool immerscrollleiste)347 BlattMenu::BlattMenu(bool immerscrollleiste /* = false */) :
348     mImmerScrollleiste(immerscrollleiste),
349     mEintraege(), mEintraegeY(1,0),
350     mObermenu(NULL), mObereintrag(NULL), mWahl(eintrag_keiner), mHyperaktiv(eintrag_keiner),
351     mInfoText(""), mInfoW(-1),
352     mRahmenUpdaten(false), mRaenderUpdaten(false), mInfozeileUpdaten(false)
353 {
354 }
355 
356 
~BlattMenu()357 BlattMenu::~BlattMenu() {
358   menuLoeschen();
359 }
360 
361 
362 /* Gleich nach dem Konstruktor aufrufen, wenn das Obermenu nicht NULL
363    sein soll. Schoener waers, obermenu direkt dem Konstruktor zu
364    uebergeben. Das wuerde aber bedeuten, dass man gezwungen ist, in
365    *jeder* Klasse, die von BlattMenu erbt, einen Konstruktor zu
366    schreiben. Liefert einfach this zurueck, damit man
367      (new BlattMenuXXX())->setObermenu(...)
368    schreiben kann */
setObermenu(BlattMenu * obermenu)369 BlattMenu * BlattMenu::setObermenu(BlattMenu * obermenu) {
370   mObermenu = obermenu;
371   return this;
372 }
373 
374 
setObereintrag(MenuEintragSubmenu * obereintrag)375 void BlattMenu::setObereintrag(MenuEintragSubmenu * obereintrag) {
376   mObereintrag = obereintrag;
377 }
378 
379 
380 
neuerEintrag(MenuEintrag * eintrag)381 void BlattMenu::neuerEintrag(MenuEintrag* eintrag) {
382   mEintraegeY.push_back(mEintraegeY[mEintraege.size()]+eintrag->mHoehe);
383   mEintraege.push_back(eintrag);
384   /* Check for duplicate hotkeys */
385   int hot=eintrag->getAccel();
386   for (size_t i=mEintraege.size()-1; i-->0;)
387     if (mEintraege[i]->getAccel()==hot)
388       eintrag->deactivateAccel();
389 }
390 
391 
oeffnen(bool,int wahl)392 void BlattMenu::oeffnen(bool /*durchMaus*/, int wahl /*= eintrag_keiner*/) {
393 
394   SDLTools::setVirtualWindowSize(L_fenster_breite_menus, L_fenster_hoehe);
395 
396   mHyperaktiv = eintrag_keiner;
397   mWahl = MausBereich(wahl);
398   /* Nicht die nachfolgenden Setzroutinen verwenden, weil sonst
399      evtl. ein nicht-mehr existenter voriger gew�hlter Men�punkt neu
400      gemalt wird. */
401   //setHyperaktiv(eintrag_keiner);
402   //setWahl(wahl);
403 
404   /* Jetzt manuell alles updaten */
405   for (size_t i = 0; i < mEintraege.size(); i++)
406     updateEintrag(i);
407 
408   /* Daf�r manuell der Infozeile sagen, dass sie geupdatet werden muss */
409   updateInfo();
410 
411   /* Zentrierlinien berechnen */
412   int xsum[zl_anzahl];
413   for (int i = 0; i < zl_anzahl; i++) xsum[i] = 0;
414   for (size_t i = 0; i < mEintraege.size(); i++) {
415     int zl = mEintraege[i]->getZentrierLinie();
416     xsum[zl] += mEintraege[i]->getX0() + mEintraege[i]->getX1();
417   }
418   CASSERT(mEintraege.size());
419   for (int i = 0; i < zl_anzahl; i++)
420     mX0[i] = L_fenster_breite_menus/2 - xsum[i]/((int) mEintraege.size());
421 
422 
423   setScrollZielHigh(ynw_mitte, true);
424   //if (durchMaus)
425   //  setWahl(eintrag_keiner);
426 
427   /* Chaning AutoColor::gGame automatically recolors the
428      border pictures */
429   AutoColor::gGame = Color(0, 0, 70);
430 
431 
432   UI::setBlatt(this);
433   sichtbaresUpdaten();
434   mRahmenUpdaten = true;
435 }
436 
437 
438 
439 
440 int keypadersatz[10] = {
441   SDLK_INSERT, SDLK_END, SDLK_DOWN, SDLK_PAGEDOWN, SDLK_LEFT,
442   SDLK_KP5, SDLK_RIGHT, SDLK_HOME, SDLK_UP, SDLK_PAGEUP};
443 
keyEvent(const SDL_keysym & taste)444 void BlattMenu::keyEvent(const SDL_keysym & taste) {
445 
446   /* Wenn die Taste einen Ascii-Code hat, dann wollen wir mit
447      dem weiterarbeiten. (d.h. shift-& auf franzoesischer Tastatur
448      ist 1) */
449   int t = taste.unicode;
450   /* Fuer sonstige Tasten (Pfeile, etc.) nehmen wir den SDL-Code */
451   if (t <= 0 || t > 255)
452     t = taste.sym;
453   /* Aber Buchstaben haetten wir gerne als Grossbuchstaben.
454      (Wenn wir auch Umlaute als Tastenkuerzel haetten, muessten
455      wir uns auch darum kuemmern) */
456   if (t >= 'a' && t <= 'z')
457     t = t-'a'+'A';
458   /* Wenn eine Ziffernblock-Taste gedrueckt wurde aber kein
459      Uni-Code mitgeliefert wurde, dann soll das auf die uebliche
460      Art wie Pfeiltasten wirken.
461      (Manchmal macht SDL das automatisch; das scheint aber buggy
462      zu sein.) */
463   if (t>=SDLK_KP0 && t<=SDLK_KP9)
464     t = keypadersatz[t-SDLK_KP0];
465 
466 
467 //   /* Keycode normalisieren */
468 //   int t = taste.sym;
469 //   if (t >= 'a' && t <= 'z')
470 //     t = t-'a'+'A';
471 //   if (t>=SDLK_KP0 && t<=SDLK_KP9) {
472 //     if (taste.mod & KMOD_NUM)
473 //       t = t-SDLK_KP0+'0';
474 //     else
475 //       t = keypadersatz[t-SDLK_KP0];
476 //   }
477 //   if (t==SDLK_KP_ENTER || t==SDLK_SPACE)
478 //     t=SDLK_RETURN;
479 //
480 //   if (t == SDLK_ESCAPE) {
481 //     doEscape();
482 //     return;
483 //   }
484 
485   //print_to_stderr(_sprintf("sym %d = '%c'     ", taste.sym, taste.sym));
486   //print_to_stderr(_sprintf("unicode %d = '%c'     ", taste.unicode, taste.unicode));
487   //print_to_stderr(_sprintf("taste %d = '%c'\n", t, t));
488 
489   /* Jeder Tastendruck stellt den Subbereich auf default;
490      selbst solche, die nix bewirken */
491   setWahl(mWahl.mEintrag);
492 
493   if (mHyperaktiv >= 0) {
494     mEintraege[mHyperaktiv]->doHyperaktiv(taste,t);
495     setWahl(mHyperaktiv);
496     setHyperaktiv(eintrag_keiner);
497     return;
498   }
499 
500   switch (t) {
501     case SDLK_UP:       navigiere(-1); break;
502     case SDLK_DOWN:     navigiere(1); break;
503     case SDLK_PAGEUP:   navigiere(-1,mZeigVon); break;
504     case SDLK_PAGEDOWN: navigiere(1,mZeigBis-1); break;
505     case SDLK_HOME:     navigiere(1,0); break;
506     case SDLK_END:      navigiere(-1,mEintraege.size()-1); break;
507     case SDLK_ESCAPE:
508       doEscape();
509       break;
510     case SDLK_RETURN:
511     case SDLK_KP_ENTER:
512     case SDLK_SPACE:
513       doReturn(false);
514       break;
515     default:
516       /* Accelerator-Taste gedr�ckt? */
517       for (int i = 0; i < (int) mEintraege.size(); i++) {
518         if (t == mEintraege[i]->getAccel() && mEintraege[i]->getWaehlbar()) {
519 
520           setWahl(i);
521 	  doReturn(false);
522 	  return;
523         }
524       }
525       /* Vielleicht ist der gew�hlte Eintrag aktiv? */
526       if (mWahl.mEintrag >= 0)
527 	if (mEintraege[mWahl.mEintrag]->getAktiv()) {
528 	  mEintraege[mWahl.mEintrag]->doHyperaktiv(taste,t);
529           /* Tastatur-User wollen nix von Subbereichen wissen */
530 	  UI::nachEventAllesAnzeigen();
531 	}
532       break;
533   }
534 
535 }
536 
537 
538 
resizeEvent()539 void BlattMenu::resizeEvent() {
540   sichtbaresUpdaten();
541   mRahmenUpdaten = true;
542   UI::nachEventAllesAnzeigen();
543 }
544 
545 
546 
getMausPos(int x,int y)547 MausBereich BlattMenu::getMausPos(int x, int y) {
548   /* Befindet sich die Maus in der Scrollbar? */
549   bool sc = istScrollbar();
550   if (sc || mImmerScrollleiste) {
551     if (x >= L_scrollleiste_x && x < L_scrollleiste_x + gric) {
552       /* Das mit dem 1000 ist weil negative Zahlen in die falsche Richtung gerundet werden */
553       int yy = (y - L_scrollleiste_y + 1000 * gric) / gric - 1000;
554       if (yy >= 0 && yy < L_scrollleiste_buttonzahl) {
555         if (!sc && yy < 4) {
556 	  /* Auf grauem Scrollbarbutton */
557 	  return MausBereich();
558 	} else
559           return MausBereich(eintrag_scrollleiste, yy);
560       }
561     }
562   }
563 
564   int e=mEintraege.size();
565   for (y-=mY0; e>=0 && y<mEintraegeY[e]; e--) {}
566   /*                ~~ C garantiert, dass die rechte Seite nur
567      ausgef�hrt wird, wenn's n�tig ist */
568   if (e < mAnimZeigVon || e >= mAnimZeigBis)
569     return MausBereich();
570   if (!mEintraege[e]->getWaehlbar())
571     return MausBereich();
572   int sb = mEintraege[e]->getMausPos(x - mX0[mEintraege[e]->getZentrierLinie()],
573                                      y - mEintraegeY[e]);
574   if (sb == subbereich_keiner)
575     return MausBereich();
576   else
577     return MausBereich(e, sb);
578 }
579 
580 
581 /*  befindet sich der Cursor aus Sicht von Tastatur-Usern? */
getTastenCursorPos()582 int BlattMenu::getTastenCursorPos() {
583   if (mHyperaktiv >= 0)
584     return mHyperaktiv;
585   else
586     return mWahl.mEintrag;
587 }
588 
589 
590 
mouseMotionEvent(bool press,int x,int y,int x_alt,int y_alt)591 void BlattMenu::mouseMotionEvent(bool press, int x, int y, int x_alt, int y_alt) {
592 
593   /* Bei gedr�ckter Maustaste rumfahren
594      => Dort, wo der Knopf runter ging bleibt's ausgew�hlt */
595   if (press) {
596     return;
597   }
598 
599   MausBereich mausAlt = getMausPos(x_alt, y_alt);
600   MausBereich mausNeu = getMausPos(x, y);
601 
602   if (mausNeu == mausAlt)
603     return;
604 
605   setWahl(mausNeu);
606   UI::nachEventAllesAnzeigen();
607 }
608 
609 
mouseButtonEvent(bool press,int x,int y)610 void BlattMenu::mouseButtonEvent(bool press, int x, int y) {
611 
612   mPress = press;
613 
614   if (!press)
615     return;
616 
617   MausBereich mausNeu = getMausPos(x, y);
618 
619   /* Wenn was hyperaktiv war, dann kann man's durch Mausklick
620      enthyperaktivieren (selbst wenn man irgendwo anders ins
621      Fenster klickt) */
622   setHyperaktiv(eintrag_keiner);
623   setWahl(mausNeu);
624 
625   if (mWahl.mEintrag >= 0)
626     doReturn(true);
627   else if (mWahl.mEintrag == eintrag_scrollleiste) {
628     /* Um ganz hoch oder runter zu scrollen, k�nnen wir einfach auf
629        viel zu weit setzen; setScrollZielLow macht das dann schon richtig */
630     switch (mWahl.mSubBereich) {
631       case 0: setScrollZielLow(99999); break;
632       case 1: scrollleisteScroll(1); break;
633       case 2: scrollleisteScroll(-1); break;
634       case 3: setScrollZielLow(-99999); break;
635       case 4: doEscape();
636     }
637   }
638 }
639 
640 
641 
642 
643 
anzeigen()644 void BlattMenu::anzeigen() {
645 
646   //Area::fillRect(0, 0, L_fenster_breite_menus, L_fenster_hoehe, Color(30, 30, 70));
647 
648   if (mRahmenUpdaten) {
649     Area::fillBorder(Color(30, 30, 70));
650     //malRahmen(L_fenster_breite_menus);
651     Area::updateAll();
652     mRahmenUpdaten = false;
653   }
654 
655   /* Oberer und unterer Rand */
656   if (mRaenderUpdaten) {
657     /* Rand oberhalb des Men�s */
658     Area::fillRect(0, 0, L_fenster_breite_menus, mAnimY0+mEintraegeY[mAnimZeigVon],
659 		   Color(30, 30, 70));
660     Area::updateRect(0, 0,
661 		     L_fenster_breite_menus, mAnimY0+mEintraegeY[mAnimZeigVon]);
662 
663     /* Zwischen Men� und Infozeile */
664     /* Mark: H�h, ist das nicht das gleiche wie
665        mAnimY0+mEintraegeY[mAnimZeigBis] ? */
666     /* Immi: Wei� auch nicht mehr. Vielleicht wusste ich nicht, dass
667        mEintraegeY eins weiter geht als Gesamtzahl der Eintr�ge */
668     int y_u = mAnimY0+mEintraegeY[mAnimZeigBis - 1]
669       + mEintraege[mAnimZeigBis - 1]->mHoehe;
670     /* Provisorisch: Das Hauptmen� ist zu lang, wenn "debug-mode" drunter steht;
671        das produziert graphik-Fehler... */
672     if (L_fenster_hoehe - L_info_hoehe - y_u > 0) {
673       Area::fillRect(0, y_u, L_fenster_breite_menus, L_fenster_hoehe - L_info_hoehe - y_u,
674   		     Color(30, 30, 70));
675       Area::updateRect(0, y_u, L_fenster_breite_menus, L_fenster_hoehe - L_info_hoehe - y_u);
676     }
677 
678     /* Wenn diese Farbe ge�ndert wird, mu� das auch in
679        some_pic_sources/highlight.pov und some_pic_sources/Makefile
680        geschehen. Und in menueintrag.cpp */
681 
682     mRaenderUpdaten = false;
683   }
684 
685   int tc = getTastenCursorPos();
686 
687   for (int i = mAnimZeigVon; i < mAnimZeigBis; i++) {
688     mEintraege[i]->anzeigen(mX0[mEintraege[i]->getZentrierLinie()],
689                             mAnimY0+mEintraegeY[i], tc == i);
690   }
691 
692   if (mInfozeileUpdaten) {
693     Area::enter(SDLTools::rect
694 		(0, L_fenster_hoehe-L_info_hoehe,
695 		 L_fenster_breite_menus,L_info_hoehe));
696     Area::fillRect(0,0,L_fenster_breite_menus,L_info_hoehe, Color(30, 30, 70));
697     if (mInfoW>=0) {
698       Font::gMenu->drawText(mInfoText,
699 			    mInfoX+L_info_hspace,
700 			    L_info_hoehe-L_infosep,
701 			    AlignBottomLeft);
702       Font::gMenu->drawText(mInfoText,
703 			    mInfoX+L_info_hspace+mInfoW,
704 			    L_info_hoehe-L_infosep,
705 			    AlignBottomLeft);
706     } else
707       Font::gMenu->drawText(mInfoText, L_infosep, L_info_hoehe-L_infosep,
708 			    AlignBottomLeft);
709     Area::updateRect(0,0,L_fenster_breite_menus,L_info_hoehe);
710     Area::leave();
711     mInfozeileUpdaten = false;
712   }
713 
714   bool sc = istScrollbar();
715   if (sc || mImmerScrollleiste) {
716 
717     /* Provisorisch: Graphik wird noch jedes Mal neu gemalt */
718 
719     int scrollwahl = mWahl.mEintrag == eintrag_scrollleiste ? mWahl.mSubBereich : -1;
720     for (int i = 0; i < L_scrollleiste_buttonzahl; i++) {
721       int scrollbildchen[] = {2, 0, 1, 3, 6};
722       int helligkeit = scrollwahl == i ? blattpic_scrollbright : blattpic_scroll;
723       if (!sc && i < 4) helligkeit = blattpic_scrolldimmed;
724       gBlattPics[helligkeit]->malBildchen(L_scrollleiste_x,
725                         L_scrollleiste_y + i * gric,
726 			scrollbildchen[i]);
727     }
728 
729     Area::updateRect(L_scrollleiste_x, L_scrollleiste_y,
730                      gric, L_scrollleiste_buttonzahl * gric);
731   }
732 }
733 
doEscape()734 void BlattMenu::doEscape() {
735   if (mObermenu) {
736     Sound::playSample(sample_menuclick,so_fenster);
737     UI::setBlatt(mObermenu);
738     mObermenu->sichtbaresUpdaten();
739     if (mObereintrag)
740       mObereintrag->doUntermenuSchliessen();
741   }
742 }
743 
doReturn(bool durchMaus)744 void BlattMenu::doReturn(bool durchMaus) {
745   if (mWahl.mEintrag == eintrag_keiner)
746     return;
747   if (mEintraege[mWahl.mEintrag]->getHyper()) {
748     setHyperaktiv(mWahl.mEintrag);
749   } else {
750     mEintraege[mWahl.mEintrag]->doReturn(durchMaus);
751   }
752 }
753 
754 
zeitSchritt()755 void BlattMenu::zeitSchritt() {
756   /* Infozeile */
757   if (mInfoW>=0) {
758     mInfoX -= L_info_scrollspeed;
759     if (mInfoX<=-mInfoW)
760       mInfoX += mInfoW;
761     mInfozeileUpdaten = true;
762     UI::nachEventAllesAnzeigen();
763   }
764 
765   /* Men�eintr�ge d�rfen ihre Privat-Animation haben */
766   for (size_t i=0; i<mEintraege.size(); i++)
767     mEintraege[i]->zeitSchritt();
768 
769   /* Maus auf Scrollpfeil gedr�ckt? */
770   if (mWahl.mEintrag == eintrag_scrollleiste && mPress) {
771     if (mWahl.mSubBereich == 1) scrollleisteScroll(1); // hoch
772     else if (mWahl.mSubBereich == 2) scrollleisteScroll(-1); // runter
773   }
774 
775   /* Echte Scrollpos an gew�nschte anpassen */
776   scrollZeitSchritt();
777 }
778 
779 
780 
781 
navigiere(int d,int vorschlag)782 void BlattMenu::navigiere(int d, int vorschlag /*=-1*/) {
783   int sgn = d > 0 ? 1 : -1;
784 
785   int neu_wahl = mWahl.mEintrag;
786 
787   if (vorschlag >= 0) neu_wahl = vorschlag;
788   else if (neu_wahl >= 0) neu_wahl += d;
789   else if (sgn == 1) neu_wahl = -1 + d;
790   else neu_wahl = mEintraege.size() + d;
791 
792   sgn = -sgn; /* Erstmal auf den alten Wert zulaufen */
793   int angestossen = 0; /* Flags: 1=oben, 2=unten, 4=mitte */
794   while (1) {
795     if ((angestossen & 3) == 3) {
796       /* Wenn wir beim Suchen eines passenden Menupunkts oben *und*
797          unten anstossen, dann aufgeben */
798       neu_wahl = -1;
799       break;
800     } else if (neu_wahl < 0) {
801       neu_wahl = 0; sgn = 1; angestossen |= 5;
802     } else if (neu_wahl >= (int) mEintraege.size()) {
803       neu_wahl = mEintraege.size() - 1; sgn = -1; angestossen |= 6;
804     } else if (neu_wahl==mWahl.mEintrag && !(angestossen & 4)) {
805       sgn = -sgn; neu_wahl += sgn; angestossen |= 4;
806     } else if (!mEintraege[neu_wahl]->getWaehlbar())
807       neu_wahl += sgn;
808     else
809       break;
810   }
811 
812   if (neu_wahl == mWahl.mEintrag) return;
813   setWahl(neu_wahl);
814   setScrollZielHigh(d>0 ? ynw_oben : ynw_unten);
815   sichtbaresUpdaten();
816   UI::nachEventAllesAnzeigen();
817 }
818 
819 
820 
menuLoeschen()821 void BlattMenu::menuLoeschen() {
822   for (int i = 0; i < (int) mEintraege.size(); i++)
823     delete(mEintraege[i]);
824   mEintraege.clear();
825   mEintraegeY.clear();
826   mEintraegeY.push_back(0);
827 }
828 
829 
830 
831 /* �ndert wahl und k�mmert sich drum, dass Graphik geupdatet wird */
setWahl(MausBereich wahl)832 void BlattMenu::setWahl(MausBereich wahl) {
833   int walt = mWahl.mEintrag;
834   mWahl = wahl;
835   updateEintrag(walt);
836   if (walt != wahl.mEintrag)
837     updateEintrag(wahl.mEintrag);
838   updateInfo();
839 }
840 
setWahl(int eintrag,int subBereich)841 void BlattMenu::setWahl(int eintrag, int subBereich /*= subbereich_default*/) {
842   setWahl(MausBereich(eintrag, subBereich));
843 }
844 
845 
setHyperaktiv(int ha)846 void BlattMenu::setHyperaktiv(int ha) {
847   int halt = mHyperaktiv;
848   mHyperaktiv = ha;
849   updateEintrag(halt);
850   if (halt != ha)
851     updateEintrag(ha);
852   updateInfo();
853 }
854 
855 
updateInfo()856 void BlattMenu::updateInfo() {
857   Str neu = (mHyperaktiv >= 0
858     ? mEintraege[mHyperaktiv]->getInfo()
859     : (mWahl.mEintrag >= 0
860       ? mEintraege[mWahl.mEintrag]->getInfo()
861       : ""));
862   if (neu != mInfoText) {
863     mInfoText = neu;
864     mInfoW = Font::gMenu->getLineWidth(mInfoText.data());
865     if (mInfoW > L_fenster_breite_menus-2*L_infosep) {
866       mInfoW+=L_info_hspace;
867       mInfoX=0;
868     } else
869       mInfoW=-1;
870     mInfozeileUpdaten = true;
871   }
872 }
873 
874 
875 
876 /* Teilt dem Eintrag seinen neuen Subbereich mit. Der Eintrag k�mmert
877    sich dann um sein Graphik-Update */
updateEintrag(int e)878 void BlattMenu::updateEintrag(int e) {
879   if (e < 0) return;
880   mEintraege[e]->setSubBereich(
881            mHyperaktiv == e ? subbereich_hyperaktiv :
882            mWahl.mEintrag == e ? mWahl.mSubBereich :
883            subbereich_keiner
884          );
885 }
886 
887 
sichtbaresUpdaten()888 void BlattMenu::sichtbaresUpdaten() {
889   for (int i = mAnimZeigVon; i < mAnimZeigBis; i++)
890     mEintraege[i]->setUpdateFlag();
891   mRaenderUpdaten = true;
892   mInfozeileUpdaten = true;
893 }
894 
895 
istScrollbar() const896 bool BlattMenu::istScrollbar() const {
897   return mEintraegeY[mEintraege.size()] > L_menu_hoehe;
898 }
899 
900 
901 
scrollleisteScroll(int sgn)902 void BlattMenu::scrollleisteScroll(int sgn) {
903   setScrollZielLow(mY0 + sgn * L_maus_scroll_geschwindigkeit);
904 }
905 
906 
907 
908 /* Gleicht mY0, mZeigVon und mZeigBis an mWahl an.
909    sprung = true => keine Animation, sondern direkt dort hin. */
setScrollZielHigh(yneuwahl ynw,bool sprung)910 void BlattMenu::setScrollZielHigh(yneuwahl ynw /*=ynw_mitte*/, bool sprung /* = false */) {
911 
912   /* Kein Men�punkt gew�hlt? Dann nach oben */
913   int w = mWahl.mEintrag < 0 ? 0 : mWahl.mEintrag;
914 
915   /* Erstmal nehmen wir an, da� wir mWahl gem�� ynw ausrichten wollen */
916   int neuy = (L_menu_hoehe - mEintraege[w]->mHoehe)/2 - mEintraegeY[w];
917   switch (ynw) {
918     case ynw_mitte: break;
919     case ynw_oben: neuy-=L_menu_scroll_vorsprung; break;
920     case ynw_unten: neuy+=L_menu_scroll_vorsprung; break;
921   }
922 
923   setScrollZielLow(neuy, sprung);
924 }
925 
926 
927 
928 
929 /* Setzt mY0 und gleicht mZeigVon und mZeigBis an.
930    sprung = true => keine Animation, sondern direkt dort hin. */
setScrollZielLow(int neuy,bool sprung)931 void BlattMenu::setScrollZielLow(int neuy, bool sprung /* = false */) {
932 
933   if (!istScrollbar()) {
934     /* Das Men� passt komplett auf den Bildschirm. Dann wird der Scrollwunsch ignoriert. */
935     neuy = (L_menu_hoehe-mEintraegeY[mEintraege.size()])/2;
936 
937   } else {
938     /* Wenn scrollbar, dann aber nicht weiter rausscrollen als gesund w�re */
939     if (neuy>L_menu_scroll_freiraum) {
940       neuy = L_menu_scroll_freiraum;
941     } else if (neuy+mEintraegeY[mEintraege.size()]+L_menu_scroll_freiraum<L_menu_hoehe) {
942       neuy = L_menu_hoehe-L_menu_scroll_freiraum-mEintraegeY[mEintraege.size()];
943     }
944   }
945 
946   mY0 = neuy;
947 
948   calcZeigVonBis(mY0, mZeigVon, mZeigBis);
949 
950   if (sprung) {
951     mAnimY0 = mY0;
952     mAnimZeigVon = mZeigVon;
953     mAnimZeigBis = mZeigBis;
954     mScrollGesch = 0;
955     sichtbaresUpdaten();
956   } else {
957     scrollZeitSchritt();
958   }
959 }
960 
961 
962 
963 /* Berechnet zeigVon und zeigBis aus Y0... mit oder ohne anim */
calcZeigVonBis(int y0,int & von,int & bis)964 void BlattMenu::calcZeigVonBis(int y0, int & von, int & bis) {
965   for (von = mEintraege.size();
966        von>0 && y0+mEintraegeY[von-1]>=0;
967        von--) {}
968   for (bis = 0;
969        bis<(int)mEintraege.size() && y0+mEintraegeY[bis+1]<=L_menu_hoehe;
970        bis++) {}
971 }
972 
973 
974 
scrollZeitSchritt()975 void BlattMenu::scrollZeitSchritt() {
976   if (mAnimY0 == mY0) {
977     mScrollGesch = 0;
978     return;
979   }
980 
981   /* Wie schnell w�ren wir am liebsten, unter Ber�cksichtigung des Bremswegs?
982      Die Formel mit der Wurzel und dem Abrunden sollte aufs Pixel genau stimmen. */
983   int diff = mY0 - mAnimY0;
984   int sgn = diff > 0 ? 1 : -1;
985   int ziel_gesch = (int) floor((-0.5 + sqrt(0.25 + 2 * diff * sgn / L_scroll_beschleunigung))
986              * L_scroll_beschleunigung) * sgn;
987 
988   /* Versuchen, diese Geschwindigkeit zu erreichen. */
989   if (mScrollGesch + L_scroll_beschleunigung < ziel_gesch)
990     mScrollGesch += L_scroll_beschleunigung;
991   else if (mScrollGesch - L_scroll_beschleunigung > ziel_gesch)
992     mScrollGesch -= L_scroll_beschleunigung;
993   else
994     mScrollGesch = ziel_gesch;
995 
996   mAnimY0 += mScrollGesch;
997 
998   calcZeigVonBis(mAnimY0, mAnimZeigVon, mAnimZeigBis);
999   sichtbaresUpdaten();
1000   UI::nachEventAllesAnzeigen();
1001 }
1002 
1003 
1004 
1005 
1006 /*****************************************************************************/
1007 
oeffnen(bool durchMaus,int)1008 void BlattStartAt::oeffnen(bool durchMaus, int) {
1009   /* Zu umstaendlich:
1010      Im Moment liefert getMoeglicheLevel einen Vector von Strings zur�ck,
1011      der hier in einen Vector von Menueintraegen konvertiert werden muss.
1012      Besser waere, wenn direkt das Menu erzeugt werden koennte. */
1013   //std::vector<Str> levNamen;
1014   //int lev, nichtGewonnen;
1015   //Cuyo::getMoeglicheLevel(levNamen, lev, nichtGewonnen);
1016 
1017   ld->ladLevelSummary(false,Cuyo::berechneVersion());
1018   menuLoeschen();
1019   bool waehlbar = true;
1020   int spz = Cuyo::getSpielerZahl();
1021   int lanz = ld->getLevelAnz();
1022   int wanz = 0;
1023   bool ungeordnet = gDebug || !ld->getAngeordnet();
1024   for (int l = 1; l <= lanz; l++) {
1025     bool gew = PrefsDaten::getLevelGewonnen(spz == 2, l);
1026     if (waehlbar || gew)
1027       neuerEintrag(new MenuEintragLevel(this, ld->getLevelName(l),
1028                    gew, waehlbar));
1029     if (waehlbar) wanz++;
1030     waehlbar &= gew || ungeordnet;
1031   }
1032   CASSERT(wanz > 0);
1033   int gewaehlt = (ungeordnet ? 1 : wanz);
1034   /* MenuEintragAuswahl kann hier nicht verwendet werden: MenuEintragAuswahl()
1035      setzt das Blatt nach Return-Dr�cken auf Obermen� zur�ck; hier muss das
1036      Blatt aber auf Spiel gesetzt werden.
1037      (=> Im Moment kommt MenuEintragAuswahl nur noch an einer Stelle vor. Daf�r
1038      lohnt sich eigentlich keine eigene Klasse.) */
1039 
1040   /* Darf erst jetzt aufgerufen werden; das Men� muss vorher schon aufgebaut
1041      worden sein */
1042 
1043   int lnr = Cuyo::getLetzterLevel();
1044   if (lnr != 0 && lnr<=wanz) gewaehlt = lnr;
1045   /* Wahl z�hlt bei 0 los, level bei 1 */
1046   BlattMenu::oeffnen(durchMaus, gewaehlt - 1);
1047 }
1048 
1049 
doReturn(bool)1050 void BlattStartAt::doReturn(bool) {
1051   Sound::playSample(sample_menuclick,so_fenster);
1052   /* Wahl z�hlt bei 0 los, level bei 1 */
1053   UI::startSpiel(mWahl.mEintrag + 1);
1054 }
1055 
1056 
1057 
1058 
1059 /*****************************************************************************/
1060 
doNewGame()1061 void doNewGame() { UI::startSpiel(1); }
doRestartLastLevel()1062 void doRestartLastLevel() {
1063   int lnr = Cuyo::getLetzterLevel();
1064   if (lnr != 0) {
1065     UI::startSpiel(lnr);
1066   }
1067 }
doQuit()1068 void doQuit() { UI::quit(); }
1069 
1070 
stromRestartLastLevel()1071 bool stromRestartLastLevel() {
1072   int lastLevel = Cuyo::getLetzterLevel();
1073   return lastLevel != 0 && ld->levelAccessible(lastLevel);
1074 }
1075 
1076 
1077 static MenuEintrag * gRestartLastLevel;
1078 
updateRestartLastLevel()1079 void updateRestartLastLevel() {
1080   ld->ladLevelSummary(false,Cuyo::berechneVersion());
1081   gRestartLastLevel->updateStrom();
1082 }
1083 
setSchwierig(int i)1084 void setSchwierig(int i) {
1085   Cuyo::setSchwierig(i);
1086   updateRestartLastLevel();
1087 }
1088 
setLevelpack(int p)1089 void setLevelpack(int p) {
1090   Cuyo::setLevelpack(p);
1091   updateRestartLastLevel();
1092 }
1093 
getVersionString()1094 Str getVersionString() {
1095   /* The following should be done at compile time. */
1096   Str ret;
1097   for (const char * v = VERSION; *v; v++) {// VERSION is defined in config.h
1098     if (*v != '~')
1099       ret += *v;
1100   }
1101   return ret;
1102 }
1103 
1104 
BlattHauptmenu()1105 BlattHauptmenu::BlattHauptmenu() {
1106   neuerEintrag(new MenuEintragBild(this, blattpic_titel));
1107   if (gDebug) {
1108     neuerEintrag(new MenuEintrag(this,
1109          _sprintf(_("%s  -  Debug mode; Press alt-h for help."), getVersionString().data()),
1110 				 MenuEintrag::Art_deko));
1111   } else {
1112     neuerEintrag(new MenuEintrag(this, getVersionString().data(),
1113          		      MenuEintrag::Art_deko));
1114   }
1115   neuerEintrag(new MenuEintrag(this, "", MenuEintrag::Art_deko, L_medskip));
1116 
1117   /* TRANSLATORS: Now follows the main menu. The characters preceded by a tilde
1118      are hotkeys. Please choose them disjoint within the menu. If this
1119      cannot be achieved in a sensible manner, feel free to leave some out. */
1120   neuerEintrag(new MenuEintrag(this,_("~New Game"), doNewGame));
1121   /* We need to make the "Restart last level" menu entry known
1122      to other entries which may change its "electricity" status. */
1123   gRestartLastLevel = new MenuEintrag(this,_("~Restart last level"),
1124                                       doRestartLastLevel);
1125   gRestartLastLevel->setGetStrom(stromRestartLastLevel);
1126   neuerEintrag(gRestartLastLevel);
1127 
1128   neuerEintrag(new MenuEintragSubmenu(this,_("Start ~at level..."),
1129 				      (new BlattStartAt())->setObermenu(this)));
1130   neuerEintrag(new MenuEintrag(this,"", MenuEintrag::Art_deko, L_medskip));
1131   neuerEintrag(new MenuEintragSpielerModus(this,_("~1 Player"),
1132                                            &updateRestartLastLevel, 1));
1133   neuerEintrag(new MenuEintragSpielerModus(this,_("~2 Players"),
1134                                            &updateRestartLastLevel, 2));
1135   neuerEintrag(new MenuEintragSpielerModus(this,_("Player vs. ~Computer"),
1136                                            &updateRestartLastLevel,
1137 					   spielermodus_computer));
1138   neuerEintrag(new MenuEintrag(this,"", MenuEintrag::Art_deko, L_medskip));
1139   neuerEintrag(new MenuEintragAuswahlmenu(this,
1140 					  _("~Level track"),
1141 					  &Version::gLevelpack.mProsaNamen,
1142 					  &Version::gLevelpack.mErklaerungen,
1143 					  &Cuyo::getLevelpack,
1144 					  &setLevelpack));
1145   neuerEintrag(new MenuEintragAuswahlmenu(this,
1146 					  _("~Difficulty"),
1147 					  &Version::gSchwierig.mProsaNamen,
1148 					  &Version::gSchwierig.mErklaerungen,
1149 					  &Cuyo::getSchwierig,
1150 					  &setSchwierig,
1151     _("Setting the difficulty affects some, but not all levels.\n"
1152       "On difficulty \"Easy\", the \"Standard\" level track\n"
1153       "consists of levels which are affected by that difficulty,\n"
1154       "and of levels which are easy in themselves.\n"
1155       "The same holds for \"Hard\".")));
1156   neuerEintrag(new MenuEintrag(this,"", MenuEintrag::Art_deko, L_medskip));
1157   neuerEintrag(new MenuEintragSubmenu(this,_("~Preferences..."),
1158 				      (new BlattPrefs())->setObermenu(this)));
1159   neuerEintrag(new MenuEintrag(this, "", MenuEintrag::Art_deko, L_medskip));
1160 
1161 
1162 
1163   // The empty lines before this are to prevent us from adding menu entries
1164   // at the end, without moving the following comment to the new end.
1165   /* TRANSLATORS: This is the last entry of the main menu. */
1166   neuerEintrag(new MenuEintrag(this, _("~Quit"), doQuit));
1167 }
1168 
1169 
1170 /********************************************************************************/
1171 
1172 
BlattPrefs()1173 BlattPrefs::BlattPrefs(): BlattMenu() {
1174 
1175   for (int i = 0; i < 2; i++) {
1176     Str s = _sprintf(_("Keys Player %d:"), i + 1);
1177     neuerEintrag(new MenuEintrag(this, s, MenuEintrag::Art_deko));
1178     neuerEintrag(new MenuEintragTaste(this, _("Left"), i, 0));
1179     neuerEintrag(new MenuEintragTaste(this, _("Right"), i, 1));
1180     neuerEintrag(new MenuEintragTaste(this, _("Turn"), i, 2));
1181     neuerEintrag(new MenuEintragTaste(this, _("Down"), i, 3));
1182     neuerEintrag(new MenuEintrag(this, "", MenuEintrag::Art_deko, L_medskip));
1183   }
1184 
1185   neuerEintrag(new MenuEintragAI(this, _("AI Speed")));
1186   neuerEintrag(new MenuEintrag(this, "", MenuEintrag::Art_deko, L_medskip));
1187   neuerEintrag(new MenuEintragSound(this, _("Sound")));
1188   neuerEintrag(new MenuEintrag(this, "", MenuEintrag::Art_deko, L_medskip));
1189   neuerEintrag(new MenuEintragEscape(this));
1190 }
1191 
1192 
1193 
1194 
doEscape()1195 void BlattPrefs::doEscape() {
1196   PrefsDaten::schreibPreferences();
1197   BlattMenu::doEscape();
1198 }
1199 
1200 
1201