1 /***************************************************************************
2 * Brutal Chess
3 * http://brutalchess.sf.net
4 *
5 * File : gamecore.cpp
6 * Authors : Mike Cook, Joe Flint, Neil Pankey
7 **************************************************************************/
8
9 #include "boardtheme.h"
10 #include "chessgame.h"
11 #include "chessplayer.h"
12 #include "config.h"
13 #include "fontloader.h"
14 #include "gamecore.h"
15 #include "menu.h"
16 #include "menuitem.h"
17 #include "objfile.h"
18 #include "options.h"
19 #include "SDL.h"
20 #include "SDL_opengl.h"
21 #include "SDL_thread.h"
22 #include "texture.h"
23
24 #include <cmath>
25 #include <iostream>
26
27 using std::cout;
28 using std::cerr;
29 using std::endl;
30
31 // Menu string constants
32 static const string AI_DIFFICULTY_EASY = "Easy";
33 static const string AI_DIFFICULTY_MEDIUM = "Medium";
34 static const string AI_DIFFICULTY_HARD = "Hard";
35
36 static const string PLAYER_BRUTAL = "Brutal";
37 static const string PLAYER_HUMAN = "Human";
38 static const string PLAYER_RANDOM = "Random";
39
40 GameCore * GameCore::m_instance = 0;
41
get_Instance()42 GameCore * GameCore::get_Instance()
43 {
44 if (m_instance == 0)
45 m_instance = new GameCore;
46 return m_instance;
47 }
48
destroy()49 void GameCore::destroy()
50 {
51 delete m_theme;
52 m_theme = 0;
53 delete m_set;
54 m_set;
55 delete this;
56 }
57
init(const ChessGame & cg,BoardTheme * bt,PieceSet * ps)58 void GameCore::init(const ChessGame & cg, BoardTheme * bt, PieceSet * ps)
59 {
60 m_game = cg;
61 m_theme = bt;
62 m_set = ps;
63 }
64
load()65 bool GameCore::load()
66 {
67 m_options = Options::getInstance();
68
69 m_set->load();
70 m_theme->load();
71 m_game.newGame();
72 m_game.startGame();
73 m_rotatey = 0.0;
74 m_isWaitingForPromotion = false;
75 m_rotate = false;
76 m_loaded = true;
77 m_suggestedwhiteplayer = m_options->getPlayer1String();
78 m_suggestedblackplayer = m_options->getPlayer2String();
79 return true;
80 }
81
loadGL()82 bool GameCore::loadGL()
83 {
84 if(m_loadthread) {
85 SDL_WaitThread(m_loadthread, NULL);
86 m_loadthread = 0;
87 }
88
89 m_set->loadGL();
90 m_theme->loadGL();
91
92 m_defaultcur = SDL_GetCursor();
93 char blank[64];
94 for(int i = 0; i < 64; i++) {
95 blank[i] = 0;
96 }
97
98 m_blankcur = SDL_CreateCursor((Uint8*)blank, (Uint8*)blank, 8, 8, 0, 0);
99 m_glloaded = true;
100
101 if(!m_thinkthread) {
102 spawnThinkThread();
103 }
104
105 return true;
106 }
107
unloadGL()108 void GameCore::unloadGL()
109 {
110 if(!m_glloaded) {
111 return;
112 }
113
114 m_set->unloadGL();
115 m_theme->unloadGL();
116
117 SDL_FreeCursor(m_blankcur);
118
119 m_glloaded = false;
120 }
121
draw()122 void GameCore::draw()
123 {
124 if(m_loaded) {
125 if(!m_glloaded) {
126 loadGL();
127 }
128
129 toBoardSpace();
130
131 // Fix the lighting so it rotates with the board (same as shadow light)
132 GLfloat light_position[4];
133 light_position[0] = 10;
134 light_position[1] = 12;
135 light_position[2] = 6;
136 light_position[3] = 1;
137 //glLightfv(GL_LIGHT0, GL_POSITION, light_position);
138
139 if(m_options->reflections) {
140 drawReflections();
141 }
142
143 // Blend the draw reflections with the board.
144 glEnable( GL_BLEND );
145 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
146 m_theme->draw(m_game.getState());
147 glDisable( GL_BLEND );
148
149 if(m_options->shadows) {
150 projectShadows();
151 }
152
153 m_set->draw(m_game.getState());
154
155
156 // Draw a spinning pawn (like the loading screen) if we are waiting on the player
157 if(!m_game.getCurrentPlayer()->isHuman()) {
158 glLoadIdentity();
159
160 double height = static_cast<double>(m_options->getResolutionHeight());
161 double width = static_cast<double>(m_options->getResolutionWidth());
162 double aspectRatioDiff = (height/width) - 0.75;
163 glTranslated(33.5 - 50.0*aspectRatioDiff, -23.5, -106);
164
165 glRotatef(SDL_GetTicks()/3.5,0,1,0);
166 glRotatef(30,0,0,1);
167 glTranslatef(0, -3.5, 0);
168
169 // Make sure we can see this above the board
170 glDisable(GL_DEPTH_TEST);
171 Piece p(m_game.getCurrentPlayer()->getColor(), Piece::PAWN);
172 glScalef(7.0, 7.0, 7.0);
173 // Slightly transparent
174 m_set->drawPiece(&p, 0.9, false);
175 glScalef(1/7.0, 1/7.0, 1/7.0);
176 glEnable(GL_DEPTH_TEST);
177 }
178
179 if (m_menu.isActive()) {
180 m_menu.draw();
181 }
182
183 // See if the game has ended, if it has increment a timer that runs
184 // to allow the player to see what happened before displaying the
185 // end game menu.
186 if (m_endgametimer.started()) {
187 m_endgametimer++;
188 } else if (m_endgametimer.done()) {
189 buildMenu();
190 m_menu.pushOptionsSet("Game Over");
191 string condition;
192 if (m_game.getBoard().isCheckMate(Piece::BLACK)) {
193 condition = "Checkmate - White Wins";
194 }
195 else if (m_game.getBoard().isCheckMate(Piece::WHITE)) {
196 condition = "Checkmate - Black Wins";
197 }
198 else {
199 condition = "Stalemate";
200 }
201 m_menu.setHeader(condition);
202 m_menu.activate();
203 m_endgametimer.resetDone();
204 }
205
206 } else {
207 glLoadIdentity();
208 drawLoadingScreen();
209 // Extra delay during the loading screen to avoid choking the CPU
210 SDL_Delay(25);
211 }
212 }
213
handleEvent(SDL_Event & e)214 bool GameCore::handleEvent(SDL_Event& e)
215 {
216 if(!m_loaded)
217 return false;
218
219 // Needs to handle mouse, keyboard, and some application events
220 // Most window level tasks will be handled in main
221 if(m_menu.handleEvent(e)) {
222 return true;
223 }
224
225 if (e.type == SDL_MOUSEMOTION) {
226 if(m_rotate) {
227 GLfloat viewport[4];
228 glGetFloatv(GL_VIEWPORT, viewport);
229 m_rotatey += 360*(e.motion.xrel / viewport[2]);
230 m_rotatex += 360*(e.motion.yrel / viewport[3]);
231 if(m_rotatex < -42)
232 m_rotatex = -42;
233 if(m_rotatex > 48)
234 m_rotatex = 48;
235 return true;
236 }
237 m_mousex = e.motion.x;
238 m_mousey = e.motion.y;
239 updateMouseBoardPos();
240 m_theme->hoverPosition(m_mousepos);
241 m_set->mousePosition(m_mouseboardx, m_mouseboardy);
242 m_set->hoverPosition(m_game.getState(), m_mousepos);
243 return true;
244 }
245 else if (e.type == SDL_MOUSEBUTTONDOWN) {
246 updateMouseBoardPos();
247 bool needMove = m_game.getCurrentPlayer()->needMove();
248 if (e.button.button == SDL_BUTTON_RIGHT) {
249 // If a piece has already been picked up, release the piece.
250 if(m_firstclick.isValid() && needMove) {
251 SDL_SetCursor(m_defaultcur);
252 m_set->deselectPosition();
253 m_firstclick.invalidate();
254 } else {
255 m_rotate = true;
256 m_mousex = e.button.x;
257 m_mousey = e.button.y;
258 m_theme->hoverPosition(BoardPosition());
259 m_set->hoverPosition(m_game.getState(), BoardPosition());
260 SDL_ShowCursor(SDL_DISABLE);
261 SDL_WM_GrabInput(SDL_GRAB_ON);
262 }
263 }
264 else if (!needMove) {
265 } else if (e.button.button == SDL_BUTTON_LEFT) {
266 if(m_rotate)
267 return false;
268
269 // Check to see if we're waiting for the player to select a piece to
270 // promote his pawn to.
271 if (this->isWaitingForPromotion()) {
272 Piece::Type t = getPromotionSelection(m_mousepos);
273 if (t == Piece::PAWN) {
274 return false;
275 }
276 BoardMove bm(m_firstclick, m_secondclick, m_game.getBoard().getPiece(m_firstclick));
277 bm.setPromotion(t);
278 if (m_game.getCurrentPlayer()->needMove()) {
279 m_game.getCurrentPlayer()->sendMove(bm);
280 }
281 m_set->drawPromotionSelector(false);
282 this->setIsWaitingForPromotion(false);
283 return true;
284 }
285
286 // Check to see if we've clicked yet
287 if (m_firstclick.isValid()) {
288 if(m_firstclick == m_mousepos) {
289 // Clicked on the start position, unselect
290 SDL_SetCursor(m_defaultcur);
291 m_set->deselectPosition();
292 m_firstclick.invalidate();
293 }
294 else {
295 // We've already clicked once, so save this new click position and
296 // make the move from m_firstclick to the new position
297 BoardMove bm(m_firstclick, m_mousepos, m_game.getBoard().getPiece(m_firstclick));
298 if (bm.needPromotion()) {
299 m_secondclick = m_mousepos;
300 if (m_game.getCurrentPlayer()->isHuman()) {
301 m_set->drawPromotionSelector(true);
302 this->setIsWaitingForPromotion(true);
303 SDL_SetCursor(m_defaultcur);
304 }
305 else {
306 // TODO - Get promotion from AI
307 }
308 }
309 else if (m_game.getCurrentPlayer()->needMove()) {
310 // The move doesn't need promotion, so simply send it off
311 // to the current player.
312 m_game.getCurrentPlayer()->sendMove(bm);
313 }
314 }
315 }
316 else {
317 // We haven't clicked yet so set m_firstclick to this click's position
318 if(m_mousepos.isValid() && m_game.getBoard().isOccupied(m_mousepos)) {
319 // Make sure player is selecting his own piece
320 if(m_game.getTurn() == m_game.getBoard().getPiece(m_mousepos)->color()) {
321 SDL_SetCursor(m_blankcur);
322 m_firstclick = m_mousepos;
323 m_set->selectPosition(m_mousepos);
324 }
325 }
326 }
327 }
328 }
329 else if (e.type == SDL_MOUSEBUTTONUP) {
330 if (e.button.button == SDL_BUTTON_RIGHT && m_rotate) {
331 m_rotate = false;
332 SDL_ShowCursor(SDL_ENABLE);
333 SDL_WM_GrabInput(SDL_GRAB_OFF);
334 SDL_WarpMouse(m_mousex, m_mousey);
335 }
336 }
337 else if (e.type == SDL_KEYDOWN) {
338 if (e.key.keysym.sym == SDLK_ESCAPE) {
339 buildMenu();
340 if (m_menu.isActive())
341 m_menu.deactivate();
342 else {
343 // If a piece is picked up, drop it before showing the menu
344 if(m_firstclick.isValid()) {
345 SDL_SetCursor(m_defaultcur);
346 m_set->deselectPosition();
347 m_firstclick.invalidate();
348 }
349 m_menu.activate();
350 }
351 }
352 }
353 else if (e.type == SDL_USEREVENT) {
354 if(e.user.code == 0) {
355 // Received notification that the player is done thinking
356 ChessPlayer *player = (ChessPlayer*)e.user.data1;
357 //m_set->animateMove(player->getMove());
358 if(m_game.tryMove(player->getMove())) {
359 m_game.getCurrentPlayer()->opponentMove(player->getMove(), m_game.getState());
360 SDL_SetCursor(m_defaultcur);
361 m_set->deselectPosition();
362 m_firstclick.invalidate();
363 }
364 SDL_WaitThread(m_thinkthread, NULL);
365 m_thinkthread = NULL;
366
367 // Only temporary, really want to do this after animation is done
368 if (!(m_game.getBoard().containsCheckMate() || m_game.getState().isDraw())) {
369 spawnThinkThread();
370 } else {
371 m_endgametimer = Timer(Timer::LINEAR);
372 m_endgametimer.setDuration(1.0);
373 m_endgametimer.start();
374 }
375 }
376 else if (e.user.code == Menu::eQUIT) {
377 SDL_Event quitevent;
378 quitevent.type = SDL_QUIT;
379 SDL_PushEvent(&quitevent);
380 }
381 else if (e.user.code == Menu::eREFLECTTOG) {
382 m_options->reflections = !m_options->reflections;
383 }
384 else if (e.user.code == Menu::eSHADOWTOG) {
385 m_options->shadows = !m_options->shadows;
386 }
387 else if (e.user.code == Menu::eHISTORYARROWSTOG) {
388 m_theme->toggleHistoryArrows();
389 }
390 else if (e.user.code == Menu::eBBRUTALPLYCHANGED) {
391 // Set the AI ply depth based on the ai difficulty string
392 string difficulty = m_blackbrutalplychoices->getCurrentChoice();
393 if (AI_DIFFICULTY_EASY == difficulty) {
394 m_options->brutalplayer2ply = EASY;
395 }
396 else if (AI_DIFFICULTY_MEDIUM == difficulty) {
397 m_options->brutalplayer2ply = MEDIUM;
398 }
399 else if (AI_DIFFICULTY_HARD == difficulty) {
400 m_options->brutalplayer2ply = HARD;
401 }
402 BrutalPlayer* player = dynamic_cast<BrutalPlayer*>(m_game.getPlayer2());
403 if (player) {
404 player->setPly(m_options->brutalplayer2ply);
405 }
406 }
407 else if (e.user.code == Menu::eBLACKPLAYERCHANGED) {
408 m_suggestedblackplayer = m_blackplayerchoices->getCurrentChoice();
409 m_blackbrutalplychoices->setCollapsed(PLAYER_BRUTAL != m_suggestedblackplayer);
410 }
411 else if (e.user.code == Menu::eWBRUTALPLYCHANGED) {
412 // Set the AI ply depth based on the ai difficulty string
413 string difficulty = m_whitebrutalplychoices->getCurrentChoice();
414 if (AI_DIFFICULTY_EASY == difficulty) {
415 m_options->brutalplayer1ply = EASY;
416 }
417 else if (AI_DIFFICULTY_MEDIUM == difficulty) {
418 m_options->brutalplayer1ply = MEDIUM;
419 }
420 else if (AI_DIFFICULTY_HARD == difficulty) {
421 m_options->brutalplayer1ply = HARD;
422 }
423 BrutalPlayer* player = dynamic_cast<BrutalPlayer*>(m_game.getPlayer1());
424 if (player) {
425 player->setPly(m_options->brutalplayer1ply);
426 }
427 }
428 else if (e.user.code == Menu::eWHITEPLAYERCHANGED) {
429 m_suggestedwhiteplayer = m_whiteplayerchoices->getCurrentChoice();
430 m_whitebrutalplychoices->setCollapsed(PLAYER_BRUTAL != m_suggestedwhiteplayer);
431 }
432 else if (e.user.code == Menu::eSTARTNEWGAME) {
433 ChessPlayer * whiteplayer = PlayerFactory(m_suggestedwhiteplayer);
434 BrutalPlayer* brutalplayer = dynamic_cast<BrutalPlayer*>(whiteplayer);
435 if (brutalplayer) {
436 brutalplayer->setPly(m_options->brutalplayer1ply);
437 }
438 whiteplayer->setIsWhite(true);
439 ChessPlayer * blackplayer = PlayerFactory(m_suggestedblackplayer);
440 brutalplayer = dynamic_cast<BrutalPlayer*>(blackplayer);
441 if (brutalplayer) {
442 brutalplayer->setPly(m_options->brutalplayer2ply);
443 }
444 blackplayer->setIsWhite(false);
445 m_game.setPlayer1(whiteplayer);
446 m_game.setPlayer2(blackplayer);
447 m_game.newGame();
448 if (m_thinkthread) {
449 SDL_KillThread(m_thinkthread);
450 }
451 spawnThinkThread();
452 SDL_Event backEvent;
453 backEvent.type = SDL_USEREVENT;
454 backEvent.user.code = Menu::eBACK;
455 SDL_PushEvent(&backEvent);
456 SDL_Event backEvent2;
457 backEvent2.type = SDL_USEREVENT;
458 backEvent2.user.code = Menu::eBACK;
459 SDL_PushEvent(&backEvent2);
460 SDL_Event backEvent3;
461 backEvent3.type = SDL_USEREVENT;
462 backEvent3.user.code = Menu::eBACK;
463 SDL_PushEvent(&backEvent3);
464 }
465 }
466 return false;
467 }
468
preload()469 bool GameCore::preload()
470 {
471 if(m_loadpawn.load("../models/pawn.obj")) {
472 // add debugging
473 } else if(m_loadpawn.load(MODELS_DIR + string("pawn.obj"))) {
474 // add debugging
475 } else {
476 cerr << "Failed to load pawn." << endl;
477 return false;
478 }
479
480 m_loadpawn.findNorms();
481 m_loadpawn.setScale(m_loadpawn.scale()*12);
482
483 if(FontLoader::loadFont("sans", "../fonts/ZEROES__.TTF", 32)) {
484 // add debugging
485 } else if(FontLoader::loadFont("sans", FONTS_DIR + string("ZEROES__.TTF"), 32)) {
486 // add debugging
487 } else {
488 cerr << "Failed to load fonts." << endl;
489 return false;
490 }
491
492 if(m_logotexture.load("../art/brutalchesslogo.png")) {
493 // add debugging
494 } else if(m_logotexture.load(ART_DIR + string("brutalchesslogo.png"))) {
495 // add debugging
496 } else {
497 cerr << "Failed to load logo" << endl;
498 return false;
499 }
500
501 m_logotexture.loadGL();
502 m_preloaded = true;
503 return true;
504 }
505
drawLoadingScreen()506 void GameCore::drawLoadingScreen()
507 {
508 if(!m_preloaded)
509 preload();
510 glTranslated(0,0,-106);
511
512 // Drawing the Brutal Chess Logo
513 m_logotexture.use();
514 glEnable(GL_TEXTURE_2D);
515 glDisable(GL_LIGHTING);
516 glEnable(GL_BLEND);
517 glColor3f(1.0, 1.0, 1.0);
518 glBegin(GL_QUADS);
519 glNormal3d(0,0,-1);
520 glTexCoord2d(0.0,0.0);
521 glVertex3f(-35.0,25.0,0.0);
522 glTexCoord2d(0.0,1.0);
523 glVertex3f(-35.0,-10.0,0.0);
524 glTexCoord2d(1.0,1.0);
525 glVertex3f(35.0,-10.0,0.0);
526 glTexCoord2d(1.0,0.0);
527 glVertex3f(35.0,25.0,0.0);
528 glEnd();
529 glDisable(GL_BLEND);
530 glEnable(GL_LIGHTING);
531 glDisable(GL_TEXTURE_2D);
532
533 // Alpha value to create a pulsing effect
534 double c = 0.6+0.4*cos(0.15*SDL_GetTicks()*3.1415/180.0);
535
536 // Drawing the Loading text
537 glEnable(GL_BLEND);
538 glColor4f(1.0, 1.0, 1.0, c);
539 glTranslated(28, -18, 0);
540 glScaled(1/10.0, 1/10.0, 1/10.0);
541 FontLoader::print(-28,-7,"Loading...");
542 glScaled(10, 10, 10);
543 glDisable(GL_BLEND);
544
545 // Spinning pawn
546 glRotatef(SDL_GetTicks()/2.5,0,1,0);
547 glRotatef(30,0,0,1);
548
549 m_loadpawn.draw();
550 }
551
projectShadows()552 void GameCore::projectShadows()
553 {
554 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
555
556 // Generate project matrix for planar shadows
557 GLfloat shadowMat[4][4];
558 GLfloat groundplane[4];
559
560 // Specified in board space, y is up
561 groundplane[0] = 0;
562 groundplane[1] = 1;
563 groundplane[2] = 0;
564 groundplane[3] = 0;
565 GLfloat lightpos[4];
566 lightpos[0] = 6;
567 lightpos[1] = 12;
568 lightpos[2] = 10;
569 // 1, since this is a point light
570 lightpos[3] = 1;
571
572 GLfloat mat[16];
573
574 for(int i = 0; i < 16; i++)
575 mat[i] = 0;
576
577 mat[0] = lightpos[0];
578 mat[4] = lightpos[1];
579 mat[8] = lightpos[2];
580 mat[12] = lightpos[3];
581
582 glPushMatrix();
583 glLoadIdentity();
584 glMultMatrixf(mat);
585 glRotated(m_rotatex, 1, 0, 0);
586 glRotated(m_rotatey, 0, 1, 0);
587 glGetFloatv(GL_MODELVIEW_MATRIX, mat);
588 glPopMatrix();
589
590 lightpos[0] = mat[0] + 4;
591 lightpos[2] = mat[8] - 4;
592
593 GLfloat dot;
594
595 dot = groundplane[0] * lightpos[0] +
596 groundplane[1] * lightpos[1] +
597 groundplane[2] * lightpos[2] +
598 groundplane[3] * lightpos[3];
599
600 shadowMat[0][0] = dot - lightpos[0] * groundplane[0];
601 shadowMat[1][0] = 0.f - lightpos[0] * groundplane[1];
602 shadowMat[2][0] = 0.f - lightpos[0] * groundplane[2];
603 shadowMat[3][0] = 0.f - lightpos[0] * groundplane[3];
604
605 shadowMat[0][1] = 0.f - lightpos[1] * groundplane[0];
606 shadowMat[1][1] = dot - lightpos[1] * groundplane[1];
607 shadowMat[2][1] = 0.f - lightpos[1] * groundplane[2];
608 shadowMat[3][1] = 0.f - lightpos[1] * groundplane[3];
609
610 shadowMat[0][2] = 0.f - lightpos[2] * groundplane[0];
611 shadowMat[1][2] = 0.f - lightpos[2] * groundplane[1];
612 shadowMat[2][2] = dot - lightpos[2] * groundplane[2];
613 shadowMat[3][2] = 0.f - lightpos[2] * groundplane[3];
614
615 shadowMat[0][3] = 0.f - lightpos[3] * groundplane[0];
616 shadowMat[1][3] = 0.f - lightpos[3] * groundplane[1];
617 shadowMat[2][3] = 0.f - lightpos[3] * groundplane[2];
618 shadowMat[3][3] = dot - lightpos[3] * groundplane[3];
619
620 glPushMatrix();
621
622 // Apply the project matrix
623 glMultMatrixf((GLfloat*)shadowMat);
624
625 glDisable(GL_LIGHTING);
626
627 // Only want to write to the stencil buffer
628 glDisable(GL_DEPTH_TEST);
629 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
630
631 // Write a 1 to where the shadows should appear
632 glEnable(GL_STENCIL_TEST);
633 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
634 glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
635 m_set->draw(m_game.getState());
636
637 // Only want to draw where the stencil buffer is 1
638 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
639 glStencilFunc(GL_EQUAL, 1, 0xffffffff);
640 glPopMatrix();
641
642 // Prevent depth buffer glitch
643 glTranslatef(0, 0.01, 0);
644
645 // Draw the actual shadow, a black 50% alpha polygon
646 glEnable(GL_BLEND);
647 glDisable(GL_TEXTURE_2D);
648 glColor4f(0, 0, 0, 0.5);
649 glBegin( GL_QUADS );
650 glNormal3d( 0, 7, 0 );
651 glVertex3d( -0.1, 0, 0.1 );
652 glVertex3d( 8.1, 0, 0.1 );
653 glVertex3d( 8.1, 0, -8.1 );
654 glVertex3d( -0.1, 0, -8.1 );
655 glEnd();
656
657 // Restore OpenGL state
658 glTranslatef(0, -0.01, 0);
659 glPopAttrib();
660 }
661
drawReflections()662 void GameCore::drawReflections()
663 {
664 // This plane is drawn and then erased. The purpose is to obtain the mouse
665 // coordinates, and set the stencil buffer for the reflections
666 // It is exactly the same size as the board, and is in exactly the same spot
667 glDisable( GL_DEPTH_TEST );
668 glEnable( GL_STENCIL_TEST );
669 glStencilOp( GL_REPLACE, GL_REPLACE, GL_REPLACE );
670 glStencilFunc( GL_ALWAYS, 1, 0xffffffff );
671
672 glBegin( GL_QUADS );
673 glNormal3d( 0, 7, 0 );
674 glVertex3d( 0.0, 0.0, 0.0 );
675 glVertex3d( 8.0, 0.0, 0.0 );
676 glVertex3d( 8.0, 0.0, -8.0 );
677 glVertex3d( 0.0, 0.0, -8.0 );
678 glEnd();
679
680 glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
681 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
682
683 // Only draw if the stencil buffer has a 1 here
684 glEnable( GL_DEPTH_TEST );
685 glStencilFunc( GL_EQUAL, 1, 0xffffffff );
686 glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
687
688 GLfloat light_position[4];
689 glGetLightfv(GL_LIGHT0, GL_POSITION, light_position);
690
691 // Invert over the board plane
692 glScalef( 1, -1, 1 );
693 glCullFace(GL_FRONT);
694
695 glLightfv(GL_LIGHT1, GL_POSITION, light_position);
696 glEnable(GL_LIGHT1);
697 glDisable(GL_LIGHT0);
698
699 // Draw the reflected pieces
700 m_set->draw(m_game.getState());
701
702 // Return to normal (not inverted)
703 glCullFace(GL_BACK);
704 glScalef( 1, -1, 1 );
705 glEnable(GL_LIGHT0);
706 glDisable(GL_LIGHT1);
707 glDisable( GL_STENCIL_TEST );
708 glClear( GL_STENCIL_BUFFER_BIT );
709 }
710
buildMenu()711 void GameCore::buildMenu()
712 {
713 static bool built = false;
714 if(!built) {
715 /* Main
716 New game
717 Options ->
718 Quit
719 */
720 m_menu.addMenuItem("Brutal Chess", new ChangeMenuItem("New Game", "New Game"));
721 m_menu.addMenuItem("Brutal Chess", new ChangeMenuItem("Game Options", "Game Options"));
722 m_menu.addMenuItem("Brutal Chess", new ChangeMenuItem("Graphics", "Graphics"));
723 m_menu.addMenuItem("Brutal Chess", new ActionItem("Quit", Menu::eQUIT));
724 m_menu.addMenuItem("Brutal Chess", new SeparatorItem());
725 m_menu.addMenuItem("Brutal Chess", new ActionItem("Back To Game", Menu::eBACK));
726
727 // New Game Menu
728 m_whiteplayerchoices = new ChoicesItem("White Player");
729 m_whiteplayerchoices->addChoice(PLAYER_BRUTAL, Menu::eWHITEPLAYERCHANGED);
730 m_whiteplayerchoices->addChoice(PLAYER_HUMAN, Menu::eWHITEPLAYERCHANGED);
731 m_whiteplayerchoices->addChoice(PLAYER_RANDOM, Menu::eWHITEPLAYERCHANGED);
732 m_whiteplayerchoices->setChoice(PLAYER_HUMAN);
733 m_whitebrutalplychoices = new ChoicesItem(" Difficulty");
734 m_whitebrutalplychoices->addChoice(AI_DIFFICULTY_EASY, Menu::eWBRUTALPLYCHANGED);
735 m_whitebrutalplychoices->addChoice(AI_DIFFICULTY_MEDIUM, Menu::eWBRUTALPLYCHANGED);
736 m_whitebrutalplychoices->addChoice(AI_DIFFICULTY_HARD, Menu::eWBRUTALPLYCHANGED);
737 m_whitebrutalplychoices->setChoice(AI_DIFFICULTY_MEDIUM);
738 m_blackplayerchoices = new ChoicesItem("Black Player");
739 m_blackplayerchoices->addChoice(PLAYER_BRUTAL, Menu::eBLACKPLAYERCHANGED);
740 m_blackplayerchoices->addChoice(PLAYER_HUMAN, Menu::eBLACKPLAYERCHANGED);
741 m_blackplayerchoices->addChoice(PLAYER_RANDOM, Menu::eBLACKPLAYERCHANGED);
742 m_blackplayerchoices->setChoice(PLAYER_BRUTAL);
743 m_blackbrutalplychoices = new ChoicesItem(" Difficulty");
744 m_blackbrutalplychoices->addChoice(AI_DIFFICULTY_EASY, Menu::eBBRUTALPLYCHANGED);
745 m_blackbrutalplychoices->addChoice(AI_DIFFICULTY_MEDIUM, Menu::eBBRUTALPLYCHANGED);
746 m_blackbrutalplychoices->addChoice(AI_DIFFICULTY_HARD, Menu::eBBRUTALPLYCHANGED);
747 m_blackbrutalplychoices->setChoice(AI_DIFFICULTY_MEDIUM);
748 m_menu.addMenuItem("New Game", m_whiteplayerchoices);
749 m_menu.addMenuItem("New Game", m_whitebrutalplychoices);
750 m_menu.addMenuItem("New Game", m_blackplayerchoices);
751 m_menu.addMenuItem("New Game", m_blackbrutalplychoices);
752 m_menu.addMenuItem("New Game", new ActionItem("Start New Game", Menu::eSTARTNEWGAME));
753 m_menu.addMenuItem("New Game", new SeparatorItem());
754 m_menu.addMenuItem("New Game", new ActionItem("Back", Menu::eBACK));
755
756 // Game Options
757 m_menu.addMenuItem("Game Options", new ToggleItem("History Arrows", Menu::eHISTORYARROWSTOG, m_options->historyarrows));
758 m_menu.addMenuItem("Game Options", new SeparatorItem());
759 m_menu.addMenuItem("Game Options", new ActionItem("Back", Menu::eBACK));
760
761 /* Graphics
762 Resolution 640x480 800x600 1024x768 1280x1024 1400x1050 1600x1200
763 Fullscreen
764 Apply resolution
765 -
766 Reflections On Off
767 Shadows On Off
768 */
769 m_resolutionchoices = new ChoicesItem("Resolution");
770 m_resolutionchoices->addChoice("640x480", Menu::e640X480);
771 m_resolutionchoices->addChoice("800x600", Menu::e800X600);
772 m_resolutionchoices->addChoice("1024x768", Menu::e1024X768);
773 m_resolutionchoices->addChoice("1280x1024", Menu::e1280X1024);
774 m_resolutionchoices->addChoice("1400x1050", Menu::e1400X1050);
775 m_resolutionchoices->addChoice("1600x1200", Menu::e1600X1200);
776 m_resolutionchoices->setChoice("800x600");
777 m_menu.addMenuItem("Graphics", m_resolutionchoices);
778 m_fullscreentoggle = new ToggleItem("Fullscreen", Menu::eFULLSCREENTOG, m_options->fullscreen);
779 m_menu.addMenuItem("Graphics", m_fullscreentoggle);
780 m_menu.addMenuItem("Graphics", new ActionItem("Apply resolution", Menu::eAPPLYRES));
781 m_menu.addMenuItem("Graphics", new SeparatorItem());
782 m_menu.addMenuItem("Graphics", new ToggleItem("Reflections", Menu::eREFLECTTOG, m_options->reflections));
783 m_menu.addMenuItem("Graphics", new ToggleItem("Shadows", Menu::eSHADOWTOG, m_options->shadows));
784 m_menu.addMenuItem("Graphics", new SeparatorItem());
785 m_menu.addMenuItem("Graphics", new ActionItem("Back", Menu::eBACK));
786
787 m_menu.addMenuItem("Game Over", new ChangeMenuItem("New Game", "New Game"));
788 m_menu.addMenuItem("Game Over", new ActionItem("Repeat Game", Menu::eSTARTNEWGAME));
789 m_menu.addMenuItem("Game Over", new SeparatorItem());
790 m_menu.addMenuItem("Game Over", new ActionItem("Main Menu", Menu::eBACK));
791 m_menu.addMenuItem("Game Over", new ActionItem("Quit", Menu::eQUIT));
792
793 m_menu.pushOptionsSet("Brutal Chess");
794 built = true;
795 }
796 }
797
setResolution(std::string resolution)798 void GameCore::setResolution(std::string resolution)
799 {
800 m_resolutionchoices->setChoice(resolution);
801 }
802
setFullscreen(bool fullscreen)803 void GameCore::setFullscreen(bool fullscreen)
804 {
805 m_fullscreentoggle->setOn(fullscreen);
806 }
807
808
toBoardSpace()809 void GameCore::toBoardSpace()
810 {
811 // Setup board space coordinates
812 glLoadIdentity();
813 glTranslated(0, 0, -106);
814 glRotated(42, 1, 0, 0);
815
816 glRotated(m_rotatex, 1, 0, 0);
817 glRotated(m_rotatey, 0, 1, 0);
818
819 glScaled(7, 7, 7);
820 glTranslated(-4.0, 0.0, 4.0);
821 }
updateMouseBoardPos()822 void GameCore::updateMouseBoardPos()
823 {
824 // Make sure the screen is cleared
825 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
826
827 toBoardSpace();
828
829 // Draw a large quad to project the mouse position onto
830 glBegin(GL_QUADS);
831 glVertex3d(-13, 0, 9.5);
832 glVertex3d( 20, 0, 9.5);
833 glVertex3d( 20, 0, -13);
834 glVertex3d(-13, 0, -13);
835 glEnd();
836
837 GLdouble mvmatrix[16];
838 GLdouble pjmatrix[16];
839 GLint viewport[4];
840
841 // Get the current window size, modelview and projection matrices
842 glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
843 glGetDoublev(GL_PROJECTION_MATRIX, pjmatrix);
844 glGetIntegerv(GL_VIEWPORT, viewport);
845
846 // Measure mouse y coordinate from the bottom of the window
847 int mousey = viewport[3] - m_mousey;
848 GLfloat mousedepth;
849
850 // Get the depth of the single pixel under the mouse
851 glReadPixels(m_mousex, mousey, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT,
852 &mousedepth);
853
854 GLdouble x, y, z;
855 // Project the mouse position into board coordinates
856 gluUnProject(m_mousex, mousey, mousedepth, mvmatrix, pjmatrix, viewport,
857 &x, &y, &z);
858
859 m_mouseboardx = x;
860 m_mouseboardy = z;
861
862 m_mousepos = BoardPosition((int)floor(x), -(int)ceil(z));
863 }
864
callThink(void * pt)865 int callThink(void *pt)
866 {
867 ChessGame* game = (ChessGame*)pt;
868 // Give the player time to think
869 game->getCurrentPlayer()->think(game->getState());
870
871 // Finished thinking, let the main thread know
872 SDL_Event thinkevent;
873 thinkevent.type = SDL_USEREVENT;
874 thinkevent.user.code = 0;
875 thinkevent.user.data1 = game->getCurrentPlayer();
876 thinkevent.user.data2 = NULL;
877 SDL_PushEvent(&thinkevent);
878 return 0;
879 }
880
spawnThinkThread()881 void GameCore::spawnThinkThread()
882 {
883 m_thinkthread = SDL_CreateThread(callThink, &m_game);
884 if(m_thinkthread == NULL) {
885 cerr << "Unable to create think thread: " << SDL_GetError() << endl;
886 }
887
888 /*
889 if(!m_game.getCurrentPlayer()->isHuman()) {
890 SDL_WaitThread(m_thinkthread, NULL);
891 }
892 */
893 }
894
getPromotionSelection(const BoardPosition & bp)895 Piece::Type GameCore::getPromotionSelection(const BoardPosition & bp)
896 {
897 if (m_game.getCurrentPlayer()->isWhite()) {
898 if (bp == BoardPosition('c', 9))
899 return Piece::QUEEN;
900 else if (bp == BoardPosition('d', 9))
901 return Piece::KNIGHT;
902 else if (bp == BoardPosition('e', 9))
903 return Piece::BISHOP;
904 else if (bp == BoardPosition('f', 9))
905 return Piece::ROOK;
906 }
907 else {
908 if (bp == BoardPosition('c', 0))
909 return Piece::ROOK;
910 else if (bp == BoardPosition('d', 0))
911 return Piece::BISHOP;
912 else if (bp == BoardPosition('e', 0))
913 return Piece::KNIGHT;
914 else if (bp == BoardPosition('f', 0))
915 return Piece::QUEEN;
916 }
917 return Piece::PAWN;
918 }
919
920 // End of file gamecore.cpp
921