Renamed AnimationSettings -> AnimationManager.
[tagua/yd.git] / src / chessboard.cpp
blob61dfc1bb07007c82db9e1697d5796b39250c8929
1 /*
2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@gmail.com>
3 (c) 2006 Maurizio Monge <maurizio.monge@kdemail.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 */
11 #include <math.h>
12 #include <iostream>
13 #include <QPainter>
14 #include <QApplication>
15 #include <QMouseEvent>
17 #include <core/namer.h>
19 #include "animation.h"
20 #include "chessboard.h"
21 #include "components.h"
22 #include "constrainedtext.h"
23 #include "entities/userentity.h"
24 #include "mainanimation.h"
25 #include "mastersettings.h"
26 #include "pointconverter.h"
27 #include "sprite.h"
29 using namespace boost;
31 /** inherit instead of typedef to ease forward declaration :) */
32 class BoardTags : public std::map<QString, std::map<Point, boost::shared_ptr<KGameCanvasPixmap> > > {
35 ChessBoard::ChessBoard(Components* components,
36 const AnimationManager& animManager,
37 KGameCanvasAbstract* parent)
38 : ClickableCanvas(parent)
39 , m_flipped(false)
40 , m_square_size(0)
41 , m_border_size(0)
42 , m_sprites(0,0)
43 , m_hinting_pos(Point::invalid())
44 , selection(Point::invalid())
45 , lastSelection(Point::invalid())
46 , m_dropped_pool(0)
47 , m_dropped_index(-1)
48 , m_anim_manager(animManager) {
50 m_main_animation = new MainAnimation( 1.0 );
52 m_tags = BoardTagsPtr(new BoardTags);
54 m_canvas_background = new KGameCanvasGroup(this);
55 m_canvas_background->show();
57 m_canvas_border = new KGameCanvasGroup(this);
58 m_canvas_border->show();
60 m_canvas_border_text = new KGameCanvasGroup(this);
61 m_canvas_border_text->show();
63 m_pieces_group = new KGameCanvasGroup(this);
64 m_pieces_group->show();
66 m_namer = components->namer();
68 settingsChanged();
71 ChessBoard::~ChessBoard() {
72 delete m_pieces_group;
74 while(!m_canvas_background->items()->isEmpty())
75 delete m_canvas_background->items()->first();
76 delete m_canvas_background;
78 while(!m_canvas_border->items()->isEmpty())
79 delete m_canvas_border->items()->first();
80 delete m_canvas_border;
82 delete m_main_animation;
85 void ChessBoard::settingsChanged() {
86 Settings s_anim = settings().group("animations");
87 int speed = (s_anim["speed"] | 16);
88 int smoothness = (s_anim["smoothness"] | 16);
89 m_main_animation->setSpeed( 0.4*pow(10.0, speed/32.0) );
90 m_main_animation->setDelay( int(70.0*pow(10.0, -smoothness/32.0)) );
92 m_border_text_color = m_controls_loader.getStaticValue<QColor>("border_color");
93 m_border_font = m_controls_loader.getStaticValue<QFont>("border_font");
95 recreateBorder();
98 void ChessBoard::updateBackground() {
99 while(!m_canvas_background->items()->isEmpty())
100 delete m_canvas_background->items()->first();
102 if(!m_square_size)
103 return;
105 Loader::PixmapOrMap bg = m_tags_loader.getValue<Loader::PixmapOrMap>("background");
106 if(const QPixmap* p = boost::get<QPixmap>(&bg)) {
107 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(*p, boardRect().size(), QPoint(),
108 true, m_canvas_background);
109 t->show();
111 else if(const Loader::PixmapMap* p = boost::get<Loader::PixmapMap>(&bg)) {
112 for(Loader::PixmapMap::const_iterator it = p->begin(); it != p->end(); ++it) {
113 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(it->second, it->first.size(),
114 QPoint(), true, m_canvas_background);
115 t->moveTo(it->first.topLeft());
116 t->show();
121 void ChessBoard::enqueue(const shared_ptr<Animation>& anim) {
122 m_main_animation->addAnimation(anim);
125 void ChessBoard::adjustSprite(const Point& p, bool immediate) {
126 SpritePtr sprite = m_sprites[p].sprite();
128 if(!sprite)
129 return;
131 enqueue(
132 1 /*BROKEN m_anim_movement*/ && !immediate
133 ? AnimationPtr(new MovementAnimation(sprite, converter()->toReal(p), 1.0))
134 : AnimationPtr(new InstantAnimation(sprite, converter()->toReal(p)))
138 boost::shared_ptr<KGameCanvasPixmap> ChessBoard::addTag(const QString& name, Point pt, bool over) {
139 if(!m_sprites.valid(pt))
140 return boost::shared_ptr<KGameCanvasPixmap>();
142 QPixmap p = m_tags_loader.getPixmap(name);
143 boost::shared_ptr<KGameCanvasPixmap> item =
144 boost::shared_ptr<KGameCanvasPixmap>(new KGameCanvasPixmap(p, this));
145 item->moveTo(converter()->toReal(pt));
146 if(over)
147 item->stackOver(m_pieces_group);
148 else
149 item->stackUnder(m_pieces_group);
150 item->show();
152 (*m_tags)[name][pt] = item;
153 return item;
156 void ChessBoard::clearTags(const QString& name) {
157 m_tags->erase(name);
160 void ChessBoard::clearTags() {
161 m_tags->clear();
164 void ChessBoard::setTags(const QString& name, Point p1, Point p2, Point p3,
165 Point p4, Point p5, Point p6 ) {
166 //TODO: maybe this could be optimized a bit
167 clearTags(name);
168 addTag(name, p1);
169 addTag(name, p2);
170 addTag(name, p3);
171 addTag(name, p4);
172 addTag(name, p5);
173 addTag(name, p6);
176 void ChessBoard::recreateBorder() {
177 m_border_text.clear();
178 while(!m_canvas_border_text->items()->isEmpty())
179 delete m_canvas_border_text->items()->first();
181 if(m_border_coords.size() == 0)
182 return;
184 Point s = m_sprites.getSize();
185 for(int w = 0; w<2; w++)
186 for(int i = 0;i<s.x;i++) {
187 int c = w ? i : i+s.x+s.y;
188 QString l = m_border_coords.size()>c ? m_border_coords[c] : QString();
189 ConstrainedText *item = new ConstrainedText(m_canvas_border_text);
190 item->setColor(m_border_text_color);
191 item->setText(l);
192 item->setFont(m_border_font);
193 item->setColor(m_border_text_color);
194 item->show();
195 m_border_text.push_back(item);
198 for(int w = 0; w<2; w++)
199 for(int i = 0;i<s.y;i++) {
200 int c = w ? i+s.x : i+2*s.x+s.y;
201 QString n = m_border_coords.size()>c ? m_border_coords[c] : QString();
202 ConstrainedText *item = new ConstrainedText(m_canvas_border_text);
203 item->setColor(m_border_text_color);
204 item->setText(n);
205 item->setFont(m_border_font);
206 item->setColor(m_border_text_color);
207 item->show();
208 m_border_text.push_back(item);
211 m_pieces_group->raise();
213 updateBorder();
216 void ChessBoard::updateBorder() {
217 while(!m_canvas_border->items()->isEmpty())
218 delete m_canvas_border->items()->first();
220 if(!m_square_size)
221 return;
223 int at = 0;
224 for(int w = 0; w<2; w++)
225 for(int i = 0;i<m_sprites.getSize().x;i++) {
226 int x = (m_flipped ? (m_sprites.getSize().x-1-i) : i)*m_square_size;
227 int y = w ? -m_border_text_far : m_square_size*m_sprites.getSize().y+m_border_text_near;
229 m_border_text[at]->setVisible(m_border_text_near != m_border_text_far);
230 m_border_text[at]->setConstrainRect(QRect(x,y,m_square_size,m_border_text_far-m_border_text_near));
231 at++;
234 for(int w = 0; w<2; w++)
235 for(int i = 0;i<m_sprites.getSize().y;i++) {
236 int x = w ? (-m_border_text_far-m_border_text_near)/2
237 : m_square_size*m_sprites.getSize().x + (m_border_text_far+m_border_text_near)/2;
238 int y = (!m_flipped ? (m_sprites.getSize().y-1-i) : i)*m_square_size
239 + (m_square_size-m_border_text_far-m_border_text_near)/2;
241 m_border_text[at]->setVisible(m_border_text_near != m_border_text_far);
242 m_border_text[at]->setConstrainRect(QRect(x-m_square_size/2,y,m_square_size,m_border_text_far-m_border_text_near));
243 at++;
246 ::LuaApi::LuaValueMap params;
247 params["width"] = m_square_size*m_sprites.getSize().x;
248 params["height"] = m_square_size*m_sprites.getSize().y;
249 Loader::PixmapOrMap bord = m_controls_loader.getValue<Loader::PixmapOrMap>("border", &params);
250 if(const QPixmap* p = boost::get<QPixmap>(&bord)) {
251 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(*p, boardRect().size(), QPoint(),
252 true, m_canvas_border);
253 t->show();
255 else if(const Loader::PixmapMap* p = boost::get<Loader::PixmapMap>(&bord)) {
256 for(Loader::PixmapMap::const_iterator it = p->begin(); it != p->end(); ++it) {
257 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(it->second, it->first.size(),
258 QPoint(), true, m_canvas_border);
259 t->moveTo(it->first.topLeft());
260 t->show();
265 void ChessBoard::createGrid(Point p, const QStringList& border_coords) {
266 m_border_coords = border_coords;
267 m_sprites = PieceGrid(p.x,p.y);
268 recreateBorder();
271 QPixmap ChessBoard::loadSprite(const QString& id) {
272 return m_loader.piecePixmap(id, m_flipped);
275 QRect ChessBoard::computeRect(Point p) const {
276 QPoint realPoint = converter()->toReal(p);
277 return squareRect(realPoint.x(), realPoint.y());
280 QRect ChessBoard::squareRect(int x, int y) const {
281 return QRect(x, y, m_square_size, m_square_size);
284 QRegion ChessBoard::computeRegion(Point p) const {
285 return QRegion(computeRect(p));
288 // selection
289 void ChessBoard::setSelection(const Point& p) {
290 lastSelection = selection;
291 selection = p;
292 setTags("selection", p);
295 void ChessBoard::cancelSelection() {
296 lastSelection = selection;
297 selection = Point::invalid();
298 clearTags("selection");
301 // premove
303 void ChessBoard::setPremove(const Move& premove) {
304 m_premove_from = premove.src();
305 m_premove_to = premove.dst();
306 setTags("premove", m_premove_from, m_premove_to);
309 void ChessBoard::cancelPremove() {
310 m_premove_from = Point::invalid();
311 m_premove_to = Point::invalid();
312 clearTags("premove");
315 void ChessBoard::updateSprites() {
316 // adjust piece positions
317 for (Point i = m_sprites.first(); i <= m_sprites.last(); i = m_sprites.next(i)) {
318 boost::shared_ptr<Sprite> p = m_sprites[i].sprite();
320 if (p) {
321 // drawing sprite
322 p->setPixmap(loadSprite(m_sprites[i].name()));
323 adjustSprite(i, true);
328 void ChessBoard::updateTags() {
329 if(!m_square_size)
330 return;
332 for(BoardTags::iterator tit = m_tags->begin(); tit != m_tags->end(); ++tit)
333 for(std::map<Point, boost::shared_ptr<KGameCanvasPixmap> >::iterator pt =
334 tit->second.begin(); pt != tit->second.end(); ++pt) {
335 pt->second->moveTo(converter()->toReal(pt->first));
336 pt->second->setPixmap(m_tags_loader.getPixmap(tit->first));
341 bool ChessBoard::doMove(Move& m) {
342 if (m_entity.lock()->oneClickMoves() || m_entity.lock()->validTurn(m.src()) == Moving) {
343 if (m_entity.lock()->testMove(m)) {
344 m_entity.lock()->executeMove(m);
345 return true;
349 std::cout << "invalid move" << std::endl;
350 error(InvalidMove);
352 return false;
356 void ChessBoard::onResize(int new_size, int border_size, int border_text_near,
357 int border_text_far, bool force_reload) {
358 if(m_square_size == new_size && !force_reload)
359 return;
361 m_square_size = new_size;
362 m_border_size = border_size;
363 m_border_text_near = border_text_near;
364 m_border_text_far = border_text_far;
366 // update the size of the piece loader
367 m_loader.setSize(m_square_size);
369 // update the size of the tag loader
370 m_tags_loader.setSize(m_square_size);
372 // update the size of the controls loader
373 m_controls_loader.setSize(m_border_size);
375 // update canvas background
376 updateBackground();
378 // update the sprites
379 updateSprites();
381 // update piece tags
382 updateTags();
384 // update border
385 updateBorder();
388 void ChessBoard::onMousePress(const QPoint& pos, int button) {
389 Point point = converter()->toLogical(pos);
390 if (!m_sprites.valid(point))
391 return;
393 //BEGIN left click
395 if (button == Qt::LeftButton) {
396 if (m_entity.lock()->oneClickMoves()) {
397 Move m(Point::invalid(), point);
398 doMove(m);
400 else {
401 shared_ptr<Sprite> piece = m_sprites[point].sprite();
403 if (piece && m_entity.lock()->movable(point)) {
404 cancelSelection();
405 m_drag_info = DragInfoPtr(new DragInfo(point, pos, piece,
406 m_entity.lock()->validTurn(point)) );
407 piece->raise();
410 // if selection is valid, (pre)move to point
411 else if (selection != Point::invalid()) {
412 piece = m_sprites[selection].sprite();
413 Q_ASSERT(piece);
414 Move m(selection, point);
416 switch(m_entity.lock()->validTurn(selection)) {
418 case Moving:
419 doMove(m);
420 cancelSelection();
421 break;
423 case Premoving:
424 if (m_entity.lock()->testPremove(m)) {
425 m_entity.lock()->addPremove(m);
426 setPremove(m);
427 cancelSelection();
429 break;
431 default:
432 break;
438 //END left click
440 //BEGIN right click
442 else if (button == Qt::RightButton) {
443 cancelSelection();
444 if (point == m_premove_from || point == m_premove_to)
445 cancelPremove();
446 m_entity.lock()->handleRightClick(point);
449 //END right click
452 void ChessBoard::onMouseRelease(const QPoint& pos, int button) {
453 Point point = converter()->toLogical(pos);
455 //BEGIN left click
457 if (button == Qt::LeftButton) {
459 if (m_drag_info) {
460 // Q_ASSERT(m_drag_info->piece);
461 Q_ASSERT(m_drag_info->sprite);
462 bool moved = false;
464 // remove valid move highlighting
465 clearTags("validmove");
467 // toggle selection if the piece didn't move
468 if (m_drag_info->from == point) {
469 if (lastSelection == point)
470 cancelSelection();
471 else
472 setSelection(point);
475 else {
476 Point dst = point;
477 if (!m_sprites.valid(point))
478 dst = Point::invalid();
479 Move m(m_drag_info->from, dst);
481 switch (m_entity.lock()->validTurn(m.src())) {
482 case Moving:
483 if (doMove(m))
484 moved = true;
485 break;
487 case Premoving:
488 if (m_entity.lock()->testPremove(m)) {
489 m_entity.lock()->addPremove(m);
490 setPremove(m);
492 break;
494 default:
495 break;
499 shared_ptr<Sprite> s = m_sprites[m_drag_info->from].sprite();
500 if (!moved && s && s->pos() != converter()->toReal(m_drag_info->from)) {
501 Q_ASSERT(s);
502 QPoint real = converter()->toReal(m_drag_info->from);
503 if( (point == m_drag_info->from) ? 0/* !m_anim_movement*/ : 0 /* !m_anim_fade*/) //BROKEN
504 enqueue(shared_ptr<Animation>(new InstantAnimation(s, real)));
505 else if (point == m_drag_info->from)
506 enqueue(shared_ptr<Animation>(new MovementAnimation(s, real)));
507 else
508 enqueue(shared_ptr<Animation>(new TeleportAnimation(s, s->pos(), real)));
511 m_drag_info = DragInfoPtr();
515 //END left button
518 void ChessBoard::onMouseMove(const QPoint& pos, int /*button*/) {
519 Point point = converter()->toLogical(pos);
521 if (m_drag_info) {
522 Q_ASSERT(m_drag_info->sprite);
523 // check drag threshold
524 if (!m_drag_info->dragStarted) {
525 QPoint delta = pos - m_drag_info->real;
526 if (delta.x() * delta.x() + delta.y() * delta.y() > DragInfo::DRAG_THRESHOLD) {
527 m_drag_info->dragStarted = true;
530 if (m_drag_info->dragStarted)
531 m_drag_info->sprite->moveTo(pos - QPoint(m_square_size / 2, m_square_size / 2) );
533 // highlight valid moves
534 Move move(m_drag_info->from, point);
535 bool valid = m_sprites.valid(point);
536 if (valid) {
537 InteractionType action = m_entity.lock()->validTurn(move.src());
538 if (action == Moving)
539 valid = m_entity.lock()->testMove(move);
542 if (valid)
543 setTags("validmove", point);
544 else
545 clearTags("validmove");
547 else if (m_entity.lock()->oneClickMoves()) {
548 if(point == m_hinting_pos)
549 return;
551 Piece hint;
553 if (m_sprites.valid(point)) {
554 Move move(Point::invalid(), point);
555 if (m_entity.lock()->testMove(move)) {
556 // set move hint
557 hint = m_entity.lock()->moveHint(move);
561 updateHinting(point, hint);
565 void ChessBoard::onPositionChanged() {
566 if (m_entity.lock() && m_entity.lock()->oneClickMoves() && m_sprites.valid(m_hinting_pos)) {
567 Piece hint;
569 Move move(Point::invalid(), m_hinting_pos);
570 if (m_entity.lock()->testMove(move)) {
571 // set move hint
572 hint = m_entity.lock()->moveHint(move);
575 updateHinting(move.dst(), hint);
579 void ChessBoard::onMouseLeave() {
580 updateHinting(Point::invalid(), Piece());
583 void ChessBoard::updateHinting(Point pt, Piece piece) {
584 if(!m_sprites.valid(pt))
585 piece = Piece();
588 QString piece_name = m_namer->name(piece);
589 if(piece != Piece() || !m_sprites.valid(pt)) {
590 if(m_hinting.sprite()) {
591 if(1 /*BROKEN m_anim_fade*/)
592 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
593 m_hinting.sprite()->pos(), 160, 0)) );
594 else
595 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
598 m_hinting_pos = Point::invalid();
599 m_hinting = NamedSprite();
601 else {
602 if(pt == m_hinting_pos) {
603 if (piece_name != m_hinting.name()) {
604 m_hinting = NamedSprite(piece_name, m_hinting.sprite());
605 m_hinting.sprite()->setPixmap(loadSprite(piece_name));
608 else {
609 if(m_hinting.sprite()) {
610 if(1 /*BROKEN m_anim_fade*/)
611 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
612 m_hinting.sprite()->pos(), 160, 0)) );
613 else
614 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
617 QPixmap pix = loadSprite(piece_name);
618 SpritePtr sprite(new Sprite(pix, piecesGroup(), converter()->toReal(pt)));
619 sprite->setOpacity(160);
620 sprite->raise();
621 sprite->show();
623 m_hinting_pos = pt;
624 m_hinting = NamedSprite(piece_name, sprite);
626 /*if(m_anim_fade)
627 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
628 m_hinting.sprite()->pos(), 0, 160)) );
629 else {
630 m_hinting.sprite()->setOpacity(160);
631 enqueue(boost::shared_ptr<Animation>(new DropAnimation(m_hinting.sprite())) );
637 void ChessBoard::reset() {
638 clearTags();
639 cancelSelection();
640 cancelPremove();
641 m_main_animation->stop();
644 void ChessBoard::flip(bool flipped)
646 if (m_flipped != flipped) {
647 m_flipped = flipped;
649 // update sprite positions
650 for (Point i = m_sprites.first(); i <= m_sprites.last(); i = m_sprites.next(i)) {
651 SpritePtr p = m_sprites[i].sprite();
652 if (p) {
653 p->setPixmap(loadSprite(m_sprites[i].name()));
654 adjustSprite(i, true);
658 updateTags();
659 updateBorder();
663 void ChessBoard::draggingOn(const IColor* pool, int index, const QPoint& point) {
664 Point to = converter()->toLogical(point);
666 if (m_sprites.valid(to))
667 switch (m_entity.lock()->validTurn(pool)) {
668 case Moving: {
669 Move m(pool, index, to);
670 if (m_entity.lock()->testMove(m)) {
671 setTags("validmove", to);
672 return;
674 break;
677 case Premoving:
678 setTags("validmove", to);
679 return;
681 default:
682 break;
685 clearTags("validmove");
688 bool ChessBoard::dropOn(const IColor* pool, int index, const QPoint& point) {
690 Point to = converter()->toLogical(point);
691 if (!m_sprites.valid(to))
692 return false;
694 clearTags("validmove");
696 switch (m_entity.lock()->validTurn(pool)) {
698 case Moving: {
699 Move m(pool, index, to);
700 if (m_entity.lock()->testMove(m)) {
701 m_entity.lock()->executeMove(m);
702 return true;
704 break;
707 case Premoving: {
708 Move m(pool, index, to);
709 if (m_entity.lock()->testPremove(m)) {
710 m_entity.lock()->addPremove(m);
711 setPremove(m);
713 break;
716 default:
717 break;
720 std::cout << "invalid move" << std::endl;
721 error(InvalidMove);
722 return false;