Update the board after the animator has done its work.
[tagua/yd.git] / src / board.cpp
blob4ec4b0fc839a5fe41080a14d34164c1585e3388d
1 /*
2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@sns.it>
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 <iostream>
12 #include <QPainter>
13 #include <QApplication>
14 #include <QMouseEvent>
16 #include "global.h"
17 #include "board.h"
18 #include "sprite.h"
19 #include "animation.h"
20 #include "pointconverter.h"
21 #include "entities/userentity.h"
22 #include "mainanimation.h"
23 #include "premove.h"
25 using namespace boost;
27 /** inherit instead of typedef to ease forward declaration :) */
28 class BoardTags : public std::map<QString, std::map<Point, boost::shared_ptr<Canvas::Pixmap> > > {
31 Board::Board(Canvas::Abstract* parent)
32 : PieceGroup(parent)
33 , m_sprites(0,0)
34 , m_hinting_pos(Point::invalid())
35 , selection(Point::invalid())
36 , lastSelection(Point::invalid()) {
38 m_tags = BoardTagsPtr(new BoardTags);
40 m_canvas_background = new Canvas::Group(this);
41 m_canvas_background->lower();
42 m_canvas_background->show();
44 m_pieces_group = new Canvas::Group(this);
45 m_pieces_group->show();
47 mySettingsChanged();
50 Board::~Board() {
51 delete m_pieces_group;
53 while(!m_canvas_background->items()->isEmpty())
54 delete m_canvas_background->items()->first();
55 delete m_canvas_background;
58 void Board::mySettingsChanged() {
59 Settings s_border = settings.group("board-border");
60 m_show_border = s_border.flag("visible", true);
61 m_border_color = (s_border["color"] |= QColor(Qt::white));
62 m_border_text_color = (s_border["text-color"] |= QColor(Qt::black));
63 m_border_font = (s_border["font"] |= QApplication::font());
65 recreateBorder();
68 void Board::settingsChanged() {
69 PieceGroup::settingsChanged();
70 mySettingsChanged();
73 void Board::updateBackground() {
74 while(!m_canvas_background->items()->isEmpty())
75 delete m_canvas_background->items()->first();
77 Loader::PixmapOrMap bg = m_tags_loader.getPixmapMap("background");
78 if(const QPixmap* p = boost::get<QPixmap>(&bg)) {
79 Canvas::TiledPixmap *t = new Canvas::TiledPixmap(*p, boardRect().size(), QPoint(),
80 true, m_canvas_background);
81 t->show();
83 else if(const Loader::PixmapMap* p = boost::get<Loader::PixmapMap>(&bg)) {
84 for(Loader::PixmapMap::const_iterator it = p->begin(); it != p->end(); ++it) {
85 Canvas::TiledPixmap *t = new Canvas::TiledPixmap(it->second, it->first.size(),
86 QPoint(), true, m_canvas_background);
87 t->moveTo(it->first.topLeft());
88 t->show();
93 boost::shared_ptr<Canvas::Pixmap> Board::addTag(const QString& name, Point pt, bool over) {
94 if(!m_sprites.valid(pt))
95 return boost::shared_ptr<Canvas::Pixmap>();
97 QPixmap p = m_tags_loader(name);
98 boost::shared_ptr<Canvas::Pixmap> item =
99 boost::shared_ptr<Canvas::Pixmap>(new Canvas::Pixmap(p, this));
100 item->moveTo(converter()->toReal(pt));
101 if(over)
102 item->stackOver(m_pieces_group);
103 else
104 item->stackUnder(m_pieces_group);
105 item->show();
107 (*m_tags)[name][pt] = item;
108 return item;
111 void Board::clearTags(const QString& name) {
112 m_tags->erase(name);
115 void Board::clearTags() {
116 m_tags->clear();
119 void Board::setTags(const QString& name, Point p1, Point p2, Point p3,
120 Point p4, Point p5, Point p6 ) {
121 //TODO: maybe this could be optimized a bit
122 clearTags(name);
123 addTag(name, p1);
124 addTag(name, p2);
125 addTag(name, p3);
126 addTag(name, p4);
127 addTag(name, p5);
128 addTag(name, p6);
131 void Board::recreateBorder() {
132 m_border_margins.clear();
133 m_border_items.clear();
135 if(!m_show_border || m_border_coords.size() == 0) {
136 m_border_size = 0;
137 return;
140 QFontMetrics fm(m_border_font);
141 m_border_size = std::max( fm.height(), fm.boundingRect("H").width() );
142 m_border_asc = fm.ascent() + (m_border_size-fm.height())/2;
144 for(int w = 0; w<2; w++)
145 for(int i=0;i<4;i++) {
146 Canvas::Rectangle *item = new Canvas::Rectangle(
147 w ? m_border_text_color : m_border_color, QSize(), this);
148 item->show();
149 m_border_margins.push_back( boost::shared_ptr<Canvas::Rectangle>( item ));
152 Point s = m_sprites.getSize();
153 for(int w = 0; w<2; w++)
154 for(int i = 0;i<s.x;i++) {
155 int c = w ? i : i+s.x+s.y;
156 QString l = m_border_coords.size()>c ? m_border_coords[c] : QString();
157 Canvas::Item *item = new Canvas::Text( l, m_border_text_color, m_border_font,
158 Canvas::Text::HCenter, Canvas::Text::VBaseline, this);
159 item->show();
160 m_border_items.push_back( boost::shared_ptr<Canvas::Item>( item ));
163 for(int w = 0; w<2; w++)
164 for(int i = 0;i<s.y;i++) {
165 int c = w ? i+s.x : i+2*s.x+s.y;
166 QString n = m_border_coords.size()>c ? m_border_coords[c] : QString();
167 Canvas::Item *item = new Canvas::Text( n, m_border_text_color, m_border_font,
168 Canvas::Text::HCenter, Canvas::Text::VBaseline, this);
169 item->show();
170 m_border_items.push_back( boost::shared_ptr<Canvas::Item>( item ));
173 m_pieces_group->raise();
175 updateBorder();
178 void Board::updateBorder() {
179 if(!m_show_border)
180 return;
182 int at = 0;
183 for(int w = 0; w<2; w++)
184 for(int i = 0;i<m_sprites.getSize().x;i++) {
185 int x = (m_flipped ? (m_sprites.getSize().x-1-i):i)*m_square_size+m_square_size/2;
186 int y = m_border_asc+(w?0:m_border_size+m_square_size*m_sprites.getSize().y)-m_border_size;
187 m_border_items[at++]->moveTo(x, y);
190 for(int w = 0; w<2; w++)
191 for(int i = 0;i<m_sprites.getSize().y;i++) {
192 int x = -m_border_size/2+(w?0:m_border_size+m_square_size*m_sprites.getSize().x);
193 int y = (!m_flipped ? (m_sprites.getSize().y-1-i):i)*m_square_size
194 +m_square_size/2-m_border_size/2+m_border_asc;
195 m_border_items[at++]->moveTo(x, y);
198 m_border_margins[0]->moveTo(-m_border_size,-m_border_size);
199 m_border_margins[0]->setSize(QSize(2*m_border_size+m_square_size*m_sprites.getSize().x,
200 m_border_size));
201 m_border_margins[4]->moveTo(-m_border_size-1,-m_border_size-1);
202 m_border_margins[4]->setSize(QSize(2*m_border_size+m_square_size*m_sprites.getSize().x+2, 1));
204 m_border_margins[1]->moveTo(-m_border_size,m_square_size*m_sprites.getSize().y);
205 m_border_margins[1]->setSize(QSize(2*m_border_size+m_square_size*m_sprites.getSize().x,
206 m_border_size));
207 m_border_margins[5]->moveTo(-m_border_size-1,m_square_size*m_sprites.getSize().y+m_border_size);
208 m_border_margins[5]->setSize(QSize(2*m_border_size+m_square_size*m_sprites.getSize().x+2, 1));
210 m_border_margins[2]->moveTo(-m_border_size,0);
211 m_border_margins[2]->setSize(QSize(m_border_size, m_square_size*m_sprites.getSize().y));
212 m_border_margins[6]->moveTo(-m_border_size-1,-m_border_size);
213 m_border_margins[6]->setSize(QSize(1, m_square_size*m_sprites.getSize().y+2*m_border_size));
215 m_border_margins[3]->moveTo(m_square_size*m_sprites.getSize().x, 0);
216 m_border_margins[3]->setSize(QSize(m_border_size, m_square_size*m_sprites.getSize().y));
217 m_border_margins[7]->moveTo(m_square_size*m_sprites.getSize().x+m_border_size, -m_border_size);
218 m_border_margins[7]->setSize(QSize(1, m_square_size*m_sprites.getSize().y+2*m_border_size));
221 void Board::createGrid(Point p, const QStringList& border_coords) {
222 m_border_coords = border_coords;
223 m_sprites = PieceGrid(p.x,p.y);
224 recreateBorder();
227 QRect Board::computeRect(Point p) const {
228 QPoint realPoint = converter()->toReal(p);
229 return squareRect(realPoint.x(), realPoint.y());
232 QRect Board::squareRect(int x, int y) const {
233 return QRect(x, y, m_square_size, m_square_size);
236 QRegion Board::computeRegion(Point p) const {
237 return QRegion(computeRect(p));
240 // selection
241 void Board::setSelection(const Point& p) {
242 lastSelection = selection;
243 selection = p;
244 setTags("selection", p);
247 void Board::cancelSelection() {
248 lastSelection = selection;
249 selection = Point::invalid();
250 clearTags("selection");
253 // premove
255 void Board::setPremove(const NormalUserMove& premove) {
256 m_premove_from = premove.from;
257 m_premove_to = premove.to;
258 setTags("premove", m_premove_from, m_premove_to);
261 void Board::setPremove(const DropUserMove& premove) {
262 m_premove_from = Point::invalid();
263 m_premove_to = premove.m_to;
264 setTags("premove", m_premove_to);
267 void Board::setPremove(const Premove& premove) {
268 setPremove(premove.toUserMove());
271 void Board::cancelPremove() {
272 m_premove_from = Point::invalid();
273 m_premove_to = Point::invalid();
274 clearTags("premove");
277 void Board::updateSprites() {
278 // adjust piece positions
279 for (Point i = m_sprites.first(); i <= m_sprites.last(); i = m_sprites.next(i)) {
280 boost::shared_ptr<Sprite> p = m_sprites[i].sprite();
282 if (p) {
283 // drawing sprite
284 p->setPixmap( m_loader( m_sprites[i].name() ) );
285 adjustSprite(i);
290 void Board::updateTags() {
291 for(BoardTags::iterator tit = m_tags->begin(); tit != m_tags->end(); ++tit)
292 for(std::map<Point, boost::shared_ptr<Canvas::Pixmap> >::iterator pt =
293 tit->second.begin(); pt != tit->second.end(); ++pt) {
294 pt->second->moveTo(converter()->toReal(pt->first));
295 pt->second->setPixmap(m_tags_loader(tit->first));
300 bool Board::doMove(const NormalUserMove& m) {
301 if (m_entity.lock()->oneClickMoves() || m_entity.lock()->validTurn(m.from) == Moving) {
302 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
303 if (mv) {
304 m_entity.lock()->executeMove(mv);
305 return true;
309 std::cout << "invalid move" << std::endl;
310 emit error(InvalidMove);
312 return false;
316 void Board::onResize(int new_size, bool force_reload) {
317 if(m_square_size == new_size && !force_reload)
318 return;
320 PieceGroup::onResize(new_size);
322 // update the size of the tag loader
323 m_tags_loader.setSize(m_square_size);
325 // update canvas background
326 updateBackground();
328 // update the sprites
329 updateSprites();
331 // update piece tags
332 updateTags();
334 // update border
335 updateBorder();
338 void Board::onMousePress(const QPoint& pos, int button) {
339 Point point = converter()->toLogical(pos);
340 if (!m_sprites.valid(point))
341 return;
343 //BEGIN left click
345 if (button == Qt::LeftButton) {
346 if (m_entity.lock()->oneClickMoves()) {
347 NormalUserMove m(Point::invalid(), point);
348 m_entity.lock()->setPromotion(m);
349 doMove(m);
351 else {
352 shared_ptr<Sprite> piece = m_sprites[point].sprite();
354 if (piece && m_entity.lock()->movable(point)) {
355 cancelSelection();
356 m_drag_info = DragInfoPtr(new DragInfo(point, pos, piece,
357 m_entity.lock()->validTurn(point)) );
358 piece->raise();
361 // if selection is valid, (pre)move to point
362 else if (selection != Point::invalid()) {
363 piece = m_sprites[selection].sprite();
364 Q_ASSERT(piece);
365 NormalUserMove m(selection, point);
366 m_entity.lock()->setPromotion(m);
368 switch(m_entity.lock()->validTurn(selection)) {
370 case Moving:
371 doMove(m);
372 cancelSelection();
373 break;
375 case Premoving:
376 if (m_entity.lock()->testPremove(m)) {
377 m_entity.lock()->addPremove(m);
378 setPremove(m);
379 cancelSelection();
381 break;
383 default:
384 break;
390 //END left click
392 //BEGIN right click
394 else if (button == Qt::RightButton) {
395 cancelSelection();
396 if (point == m_premove_from || point == m_premove_to)
397 cancelPremove();
398 m_entity.lock()->handleRightClick(point);
401 //END right click
404 void Board::onMouseRelease(const QPoint& pos, int button) {
405 Point point = converter()->toLogical(pos);
407 //BEGIN left click
409 if (button == Qt::LeftButton) {
411 if (m_drag_info) {
412 // Q_ASSERT(m_drag_info->piece);
413 Q_ASSERT(m_drag_info->sprite);
414 bool moved = false;
416 // remove valid move highlighting
417 clearTags("validmove");
419 // toggle selection if the piece didn't move
420 if (m_drag_info->from == point) {
421 if (lastSelection == point)
422 cancelSelection();
423 else
424 setSelection(point);
427 else {
428 NormalUserMove m(m_drag_info->from, point, true);
429 if (!m_sprites.valid(point))
430 m.to = Point::invalid();
432 m_entity.lock()->setPromotion(m);
434 switch(m_entity.lock()->validTurn(m_drag_info->from)) {
436 case Moving:
437 if (doMove(m))
438 moved = true;
439 break;
441 case Premoving:
442 if (m_entity.lock()->testPremove(m)) {
443 m_entity.lock()->addPremove(m);
444 setPremove(m);
446 break;
448 default:
449 break;
453 shared_ptr<Sprite> s = m_sprites[m_drag_info->from].sprite();
454 if (!moved && s && s->pos() != converter()->toReal(m_drag_info->from)) {
455 Q_ASSERT(s);
456 QPoint real = converter()->toReal(m_drag_info->from);
457 if( (point == m_drag_info->from) ? !m_anim_movement : !m_anim_fade)
458 enqueue(shared_ptr<Animation>(new InstantAnimation(s, real)));
459 else if (point == m_drag_info->from)
460 enqueue(shared_ptr<Animation>(new MovementAnimation(s, real)));
461 else
462 enqueue(shared_ptr<Animation>(new TeleportAnimation(s, s->pos(), real)));
465 m_drag_info = DragInfoPtr();
469 //END left button
472 void Board::onMouseMove(const QPoint& pos, int /*button*/) {
473 Point point = converter()->toLogical(pos);
475 if (m_drag_info) {
476 Q_ASSERT(m_drag_info->sprite);
477 // check drag threshold
478 if (!m_drag_info->dragStarted) {
479 QPoint delta = pos - m_drag_info->real;
480 if (delta.x() * delta.x() + delta.y() * delta.y() > DragInfo::DRAG_THRESHOLD) {
481 m_drag_info->dragStarted = true;
484 if (m_drag_info->dragStarted)
485 m_drag_info->sprite->moveTo(pos - QPoint(m_square_size / 2, m_square_size / 2) );
487 // highlight valid moves
488 NormalUserMove move(m_drag_info->from, point);
489 bool valid = m_sprites.valid(point);
490 if (valid) {
491 InteractionType action = m_entity.lock()->validTurn(m_drag_info->from);
492 if (action == Moving)
493 valid = m_entity.lock()->testMove(move);
496 if (valid)
497 setTags("validmove", point);
498 else
499 clearTags("validmove");
501 else if (m_entity.lock()->oneClickMoves()) {
502 if(point == m_hinting_pos)
503 return;
505 AbstractPiece::Ptr hint;
507 if (m_sprites.valid(point)) {
508 if (AbstractMove::Ptr move = m_entity.lock()->testMove(
509 NormalUserMove(Point::invalid(), point))) {
510 // set move hint
511 hint = m_entity.lock()->moveHint(move);
515 updateHinting(point, hint);
519 void Board::onPositionChanged() {
520 if (m_entity.lock() && m_entity.lock()->oneClickMoves() && m_sprites.valid(m_hinting_pos)) {
521 AbstractPiece::Ptr hint;
523 if (AbstractMove::Ptr move = m_entity.lock()->testMove(
524 NormalUserMove(Point::invalid(), m_hinting_pos)) ) {
525 // set move hint
526 hint = m_entity.lock()->moveHint(move);
529 updateHinting(m_hinting_pos, hint);
533 void Board::onMouseLeave() {
534 updateHinting(Point::invalid(), AbstractPiece::Ptr());
537 void Board::updateHinting(Point pt, AbstractPiece::Ptr piece) {
538 if(!m_sprites.valid(pt))
539 piece = AbstractPiece::Ptr();
541 if(!piece || !m_sprites.valid(pt)) {
542 if(m_hinting.sprite()) {
543 if(m_anim_fade)
544 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
545 m_hinting.sprite()->pos(), 160, 0)) );
546 else
547 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
550 m_hinting_pos = Point::invalid();
551 m_hinting = NamedSprite();
553 else {
554 if(pt == m_hinting_pos) {
555 if(!(piece->name() == m_hinting.name())) {
556 m_hinting = NamedSprite(piece->name(), m_hinting.sprite());
557 m_hinting.sprite()->setPixmap(m_loader(piece->name()));
560 else {
561 if(m_hinting.sprite()) {
562 if(m_anim_fade)
563 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
564 m_hinting.sprite()->pos(), 160, 0)) );
565 else
566 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
569 QPixmap pix = m_loader(piece->name());
570 boost::shared_ptr<Sprite> sprite = createSprite(pix, pt);
571 sprite->setOpacity(160);
572 sprite->raise();
573 sprite->show();
575 m_hinting_pos = pt;
576 m_hinting = NamedSprite(piece->name(), sprite);
578 /*if(m_anim_fade)
579 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
580 m_hinting.sprite()->pos(), 0, 160)) );
581 else {
582 m_hinting.sprite()->setOpacity(160);
583 enqueue(boost::shared_ptr<Animation>(new DropAnimation(m_hinting.sprite())) );
589 void Board::reset() {
590 clearTags();
591 cancelSelection();
592 cancelPremove();
593 m_main_animation->stop();
596 void Board::flip(bool flipped)
598 if (m_flipped != flipped) {
599 m_flipped = flipped;
601 // update sprite positions
602 for (Point i = m_sprites.first(); i <= m_sprites.last(); i = m_sprites.next(i))
603 if (m_sprites[i].sprite())
604 animatePiece(m_sprites[i].sprite(), i, 1.0);
606 updateTags();
607 updateBorder();
611 void Board::draggingOn(int pool, int index, const QPoint& point) {
612 Point to = converter()->toLogical(point);
614 #if 0
615 //BROKEN
616 if (m_sprites.valid(to))
617 switch(m_entity.lock()->validTurn(piece->color())) {
618 case Moving: {
619 DropUserMove m(piece, to);
620 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
621 if (mv) {
622 setTags("validmove", to);
623 return;
625 break;
628 case Premoving:
629 setTags("validmove", to);
630 return;
632 default:
633 break;
635 #endif
637 clearTags("validmove");
640 bool Board::dropOn(int pool, int index, const QPoint& point) {
642 Point to = converter()->toLogical(point);
643 if (!m_sprites.valid(to))
644 return false;
646 clearTags("validmove");
648 #if 0
649 //BROKEN
650 switch(m_entity.lock()->validTurn(piece->color())) {
652 case Moving: {
653 DropUserMove m(piece, to);
654 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
655 if (mv) {
656 m_entity.lock()->executeMove(mv);
657 return true;
659 break;
662 case Premoving: {
663 DropUserMove m(piece, to);
664 if (m_entity.lock()->testPremove(m)) {
665 m_entity.lock()->addPremove(m);
666 setPremove(m);
668 break;
671 default:
672 break;
674 #endif
675 std::cout << "invalid move" << std::endl;
676 emit error(InvalidMove);
677 return false;
680 #include "board.moc"