Added real animations to the Shogi animator.
[tagua.git] / src / board.cpp
blob66469610b9c57986df24745cee6d0e81c3e6806f
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<KGameCanvasPixmap> > > {
31 Board::Board(KGameCanvasAbstract* parent)
32 : PieceGroup(parent)
33 , m_sprites(0,0)
34 , m_hinting_pos(Point::invalid())
35 , selection(Point::invalid())
36 , lastSelection(Point::invalid())
37 , m_dropped_pool(-1)
38 , m_dropped_index(-1) {
40 m_tags = BoardTagsPtr(new BoardTags);
42 m_canvas_background = new KGameCanvasGroup(this);
43 m_canvas_background->lower();
44 m_canvas_background->show();
46 m_pieces_group = new KGameCanvasGroup(this);
47 m_pieces_group->show();
49 mySettingsChanged();
52 Board::~Board() {
53 delete m_pieces_group;
55 while(!m_canvas_background->items()->isEmpty())
56 delete m_canvas_background->items()->first();
57 delete m_canvas_background;
60 void Board::mySettingsChanged() {
61 Settings s_border = settings.group("board-border");
62 m_show_border = s_border.flag("visible", true);
63 m_border_color = (s_border["color"] |= QColor(Qt::white));
64 m_border_text_color = (s_border["text-color"] |= QColor(Qt::black));
65 m_border_font = (s_border["font"] |= QApplication::font());
67 recreateBorder();
70 void Board::settingsChanged() {
71 PieceGroup::settingsChanged();
72 mySettingsChanged();
75 void Board::updateBackground() {
76 while(!m_canvas_background->items()->isEmpty())
77 delete m_canvas_background->items()->first();
79 Loader::PixmapOrMap bg = m_tags_loader.getPixmapMap("background");
80 if(const QPixmap* p = boost::get<QPixmap>(&bg)) {
81 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(*p, boardRect().size(), QPoint(),
82 true, m_canvas_background);
83 t->show();
85 else if(const Loader::PixmapMap* p = boost::get<Loader::PixmapMap>(&bg)) {
86 for(Loader::PixmapMap::const_iterator it = p->begin(); it != p->end(); ++it) {
87 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(it->second, it->first.size(),
88 QPoint(), true, m_canvas_background);
89 t->moveTo(it->first.topLeft());
90 t->show();
95 boost::shared_ptr<KGameCanvasPixmap> Board::addTag(const QString& name, Point pt, bool over) {
96 if(!m_sprites.valid(pt))
97 return boost::shared_ptr<KGameCanvasPixmap>();
99 QPixmap p = m_tags_loader(name);
100 boost::shared_ptr<KGameCanvasPixmap> item =
101 boost::shared_ptr<KGameCanvasPixmap>(new KGameCanvasPixmap(p, this));
102 item->moveTo(converter()->toReal(pt));
103 if(over)
104 item->stackOver(m_pieces_group);
105 else
106 item->stackUnder(m_pieces_group);
107 item->show();
109 (*m_tags)[name][pt] = item;
110 return item;
113 void Board::clearTags(const QString& name) {
114 m_tags->erase(name);
117 void Board::clearTags() {
118 m_tags->clear();
121 void Board::setTags(const QString& name, Point p1, Point p2, Point p3,
122 Point p4, Point p5, Point p6 ) {
123 //TODO: maybe this could be optimized a bit
124 clearTags(name);
125 addTag(name, p1);
126 addTag(name, p2);
127 addTag(name, p3);
128 addTag(name, p4);
129 addTag(name, p5);
130 addTag(name, p6);
133 void Board::recreateBorder() {
134 m_border_margins.clear();
135 m_border_items.clear();
137 if(!m_show_border || m_border_coords.size() == 0) {
138 m_border_size = 0;
139 return;
142 QFontMetrics fm(m_border_font);
143 m_border_size = std::max( fm.height(), fm.boundingRect("H").width() );
144 m_border_asc = fm.ascent() + (m_border_size-fm.height())/2;
146 for(int w = 0; w<2; w++)
147 for(int i=0;i<4;i++) {
148 KGameCanvasRectangle *item = new KGameCanvasRectangle(
149 w ? m_border_text_color : m_border_color, QSize(), this);
150 item->show();
151 m_border_margins.push_back( boost::shared_ptr<KGameCanvasRectangle>( item ));
154 Point s = m_sprites.getSize();
155 for(int w = 0; w<2; w++)
156 for(int i = 0;i<s.x;i++) {
157 int c = w ? i : i+s.x+s.y;
158 QString l = m_border_coords.size()>c ? m_border_coords[c] : QString();
159 KGameCanvasItem *item = new KGameCanvasText( l, m_border_text_color, m_border_font,
160 KGameCanvasText::HCenter, KGameCanvasText::VBaseline, this);
161 item->show();
162 m_border_items.push_back( boost::shared_ptr<KGameCanvasItem>( item ));
165 for(int w = 0; w<2; w++)
166 for(int i = 0;i<s.y;i++) {
167 int c = w ? i+s.x : i+2*s.x+s.y;
168 QString n = m_border_coords.size()>c ? m_border_coords[c] : QString();
169 KGameCanvasItem *item = new KGameCanvasText( n, m_border_text_color, m_border_font,
170 KGameCanvasText::HCenter, KGameCanvasText::VBaseline, this);
171 item->show();
172 m_border_items.push_back( boost::shared_ptr<KGameCanvasItem>( item ));
175 m_pieces_group->raise();
177 updateBorder();
180 void Board::updateBorder() {
181 if(!m_show_border)
182 return;
184 int at = 0;
185 for(int w = 0; w<2; w++)
186 for(int i = 0;i<m_sprites.getSize().x;i++) {
187 int x = (m_flipped ? (m_sprites.getSize().x-1-i):i)*m_square_size+m_square_size/2;
188 int y = m_border_asc+(w?0:m_border_size+m_square_size*m_sprites.getSize().y)-m_border_size;
189 m_border_items[at++]->moveTo(x, y);
192 for(int w = 0; w<2; w++)
193 for(int i = 0;i<m_sprites.getSize().y;i++) {
194 int x = -m_border_size/2+(w?0:m_border_size+m_square_size*m_sprites.getSize().x);
195 int y = (!m_flipped ? (m_sprites.getSize().y-1-i):i)*m_square_size
196 +m_square_size/2-m_border_size/2+m_border_asc;
197 m_border_items[at++]->moveTo(x, y);
200 m_border_margins[0]->moveTo(-m_border_size,-m_border_size);
201 m_border_margins[0]->setSize(QSize(2*m_border_size+m_square_size*m_sprites.getSize().x,
202 m_border_size));
203 m_border_margins[4]->moveTo(-m_border_size-1,-m_border_size-1);
204 m_border_margins[4]->setSize(QSize(2*m_border_size+m_square_size*m_sprites.getSize().x+2, 1));
206 m_border_margins[1]->moveTo(-m_border_size,m_square_size*m_sprites.getSize().y);
207 m_border_margins[1]->setSize(QSize(2*m_border_size+m_square_size*m_sprites.getSize().x,
208 m_border_size));
209 m_border_margins[5]->moveTo(-m_border_size-1,m_square_size*m_sprites.getSize().y+m_border_size);
210 m_border_margins[5]->setSize(QSize(2*m_border_size+m_square_size*m_sprites.getSize().x+2, 1));
212 m_border_margins[2]->moveTo(-m_border_size,0);
213 m_border_margins[2]->setSize(QSize(m_border_size, m_square_size*m_sprites.getSize().y));
214 m_border_margins[6]->moveTo(-m_border_size-1,-m_border_size);
215 m_border_margins[6]->setSize(QSize(1, m_square_size*m_sprites.getSize().y+2*m_border_size));
217 m_border_margins[3]->moveTo(m_square_size*m_sprites.getSize().x, 0);
218 m_border_margins[3]->setSize(QSize(m_border_size, m_square_size*m_sprites.getSize().y));
219 m_border_margins[7]->moveTo(m_square_size*m_sprites.getSize().x+m_border_size, -m_border_size);
220 m_border_margins[7]->setSize(QSize(1, m_square_size*m_sprites.getSize().y+2*m_border_size));
223 void Board::createGrid(Point p, const QStringList& border_coords) {
224 m_border_coords = border_coords;
225 m_sprites = PieceGrid(p.x,p.y);
226 recreateBorder();
229 QRect Board::computeRect(Point p) const {
230 QPoint realPoint = converter()->toReal(p);
231 return squareRect(realPoint.x(), realPoint.y());
234 QRect Board::squareRect(int x, int y) const {
235 return QRect(x, y, m_square_size, m_square_size);
238 QRegion Board::computeRegion(Point p) const {
239 return QRegion(computeRect(p));
242 // selection
243 void Board::setSelection(const Point& p) {
244 lastSelection = selection;
245 selection = p;
246 setTags("selection", p);
249 void Board::cancelSelection() {
250 lastSelection = selection;
251 selection = Point::invalid();
252 clearTags("selection");
255 // premove
257 void Board::setPremove(const NormalUserMove& premove) {
258 m_premove_from = premove.from;
259 m_premove_to = premove.to;
260 setTags("premove", m_premove_from, m_premove_to);
263 void Board::setPremove(const DropUserMove& premove) {
264 m_premove_from = Point::invalid();
265 m_premove_to = premove.m_to;
266 setTags("premove", m_premove_to);
269 void Board::setPremove(const Premove& premove) {
270 setPremove(premove.toUserMove());
273 void Board::cancelPremove() {
274 m_premove_from = Point::invalid();
275 m_premove_to = Point::invalid();
276 clearTags("premove");
279 void Board::updateSprites() {
280 // adjust piece positions
281 for (Point i = m_sprites.first(); i <= m_sprites.last(); i = m_sprites.next(i)) {
282 boost::shared_ptr<Sprite> p = m_sprites[i].sprite();
284 if (p) {
285 // drawing sprite
286 p->setPixmap( m_loader( m_sprites[i].name() ) );
287 adjustSprite(i);
292 void Board::updateTags() {
293 for(BoardTags::iterator tit = m_tags->begin(); tit != m_tags->end(); ++tit)
294 for(std::map<Point, boost::shared_ptr<KGameCanvasPixmap> >::iterator pt =
295 tit->second.begin(); pt != tit->second.end(); ++pt) {
296 pt->second->moveTo(converter()->toReal(pt->first));
297 pt->second->setPixmap(m_tags_loader(tit->first));
302 bool Board::doMove(const NormalUserMove& m) {
303 if (m_entity.lock()->oneClickMoves() || m_entity.lock()->validTurn(m.from) == Moving) {
304 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
305 if (mv) {
306 m_entity.lock()->executeMove(mv);
307 return true;
311 std::cout << "invalid move" << std::endl;
312 emit error(InvalidMove);
314 return false;
318 void Board::onResize(int new_size, bool force_reload) {
319 if(m_square_size == new_size && !force_reload)
320 return;
322 PieceGroup::onResize(new_size);
324 // update the size of the tag loader
325 m_tags_loader.setSize(m_square_size);
327 // update canvas background
328 updateBackground();
330 // update the sprites
331 updateSprites();
333 // update piece tags
334 updateTags();
336 // update border
337 updateBorder();
340 void Board::onMousePress(const QPoint& pos, int button) {
341 Point point = converter()->toLogical(pos);
342 if (!m_sprites.valid(point))
343 return;
345 //BEGIN left click
347 if (button == Qt::LeftButton) {
348 if (m_entity.lock()->oneClickMoves()) {
349 NormalUserMove m(Point::invalid(), point);
350 m_entity.lock()->setPromotion(m);
351 doMove(m);
353 else {
354 shared_ptr<Sprite> piece = m_sprites[point].sprite();
356 if (piece && m_entity.lock()->movable(point)) {
357 cancelSelection();
358 m_drag_info = DragInfoPtr(new DragInfo(point, pos, piece,
359 m_entity.lock()->validTurn(point)) );
360 piece->raise();
363 // if selection is valid, (pre)move to point
364 else if (selection != Point::invalid()) {
365 piece = m_sprites[selection].sprite();
366 Q_ASSERT(piece);
367 NormalUserMove m(selection, point);
368 m_entity.lock()->setPromotion(m);
370 switch(m_entity.lock()->validTurn(selection)) {
372 case Moving:
373 doMove(m);
374 cancelSelection();
375 break;
377 case Premoving:
378 if (m_entity.lock()->testPremove(m)) {
379 m_entity.lock()->addPremove(m);
380 setPremove(m);
381 cancelSelection();
383 break;
385 default:
386 break;
392 //END left click
394 //BEGIN right click
396 else if (button == Qt::RightButton) {
397 cancelSelection();
398 if (point == m_premove_from || point == m_premove_to)
399 cancelPremove();
400 m_entity.lock()->handleRightClick(point);
403 //END right click
406 void Board::onMouseRelease(const QPoint& pos, int button) {
407 Point point = converter()->toLogical(pos);
409 //BEGIN left click
411 if (button == Qt::LeftButton) {
413 if (m_drag_info) {
414 // Q_ASSERT(m_drag_info->piece);
415 Q_ASSERT(m_drag_info->sprite);
416 bool moved = false;
418 // remove valid move highlighting
419 clearTags("validmove");
421 // toggle selection if the piece didn't move
422 if (m_drag_info->from == point) {
423 if (lastSelection == point)
424 cancelSelection();
425 else
426 setSelection(point);
429 else {
430 NormalUserMove m(m_drag_info->from, point, true);
431 if (!m_sprites.valid(point))
432 m.to = Point::invalid();
434 m_entity.lock()->setPromotion(m);
436 switch(m_entity.lock()->validTurn(m_drag_info->from)) {
438 case Moving:
439 if (doMove(m))
440 moved = true;
441 break;
443 case Premoving:
444 if (m_entity.lock()->testPremove(m)) {
445 m_entity.lock()->addPremove(m);
446 setPremove(m);
448 break;
450 default:
451 break;
455 shared_ptr<Sprite> s = m_sprites[m_drag_info->from].sprite();
456 if (!moved && s && s->pos() != converter()->toReal(m_drag_info->from)) {
457 Q_ASSERT(s);
458 QPoint real = converter()->toReal(m_drag_info->from);
459 if( (point == m_drag_info->from) ? !m_anim_movement : !m_anim_fade)
460 enqueue(shared_ptr<Animation>(new InstantAnimation(s, real)));
461 else if (point == m_drag_info->from)
462 enqueue(shared_ptr<Animation>(new MovementAnimation(s, real)));
463 else
464 enqueue(shared_ptr<Animation>(new TeleportAnimation(s, s->pos(), real)));
467 m_drag_info = DragInfoPtr();
471 //END left button
474 void Board::onMouseMove(const QPoint& pos, int /*button*/) {
475 Point point = converter()->toLogical(pos);
477 if (m_drag_info) {
478 Q_ASSERT(m_drag_info->sprite);
479 // check drag threshold
480 if (!m_drag_info->dragStarted) {
481 QPoint delta = pos - m_drag_info->real;
482 if (delta.x() * delta.x() + delta.y() * delta.y() > DragInfo::DRAG_THRESHOLD) {
483 m_drag_info->dragStarted = true;
486 if (m_drag_info->dragStarted)
487 m_drag_info->sprite->moveTo(pos - QPoint(m_square_size / 2, m_square_size / 2) );
489 // highlight valid moves
490 NormalUserMove move(m_drag_info->from, point);
491 bool valid = m_sprites.valid(point);
492 if (valid) {
493 InteractionType action = m_entity.lock()->validTurn(m_drag_info->from);
494 if (action == Moving)
495 valid = m_entity.lock()->testMove(move);
498 if (valid)
499 setTags("validmove", point);
500 else
501 clearTags("validmove");
503 else if (m_entity.lock()->oneClickMoves()) {
504 if(point == m_hinting_pos)
505 return;
507 AbstractPiece::Ptr hint;
509 if (m_sprites.valid(point)) {
510 if (AbstractMove::Ptr move = m_entity.lock()->testMove(
511 NormalUserMove(Point::invalid(), point))) {
512 // set move hint
513 hint = m_entity.lock()->moveHint(move);
517 updateHinting(point, hint);
521 void Board::onPositionChanged() {
522 if (m_entity.lock() && m_entity.lock()->oneClickMoves() && m_sprites.valid(m_hinting_pos)) {
523 AbstractPiece::Ptr hint;
525 if (AbstractMove::Ptr move = m_entity.lock()->testMove(
526 NormalUserMove(Point::invalid(), m_hinting_pos)) ) {
527 // set move hint
528 hint = m_entity.lock()->moveHint(move);
531 updateHinting(m_hinting_pos, hint);
535 void Board::onMouseLeave() {
536 updateHinting(Point::invalid(), AbstractPiece::Ptr());
539 void Board::updateHinting(Point pt, AbstractPiece::Ptr piece) {
540 if(!m_sprites.valid(pt))
541 piece = AbstractPiece::Ptr();
543 if(!piece || !m_sprites.valid(pt)) {
544 if(m_hinting.sprite()) {
545 if(m_anim_fade)
546 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
547 m_hinting.sprite()->pos(), 160, 0)) );
548 else
549 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
552 m_hinting_pos = Point::invalid();
553 m_hinting = NamedSprite();
555 else {
556 if(pt == m_hinting_pos) {
557 if(!(piece->name() == m_hinting.name())) {
558 m_hinting = NamedSprite(piece->name(), m_hinting.sprite());
559 m_hinting.sprite()->setPixmap(m_loader(piece->name()));
562 else {
563 if(m_hinting.sprite()) {
564 if(m_anim_fade)
565 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
566 m_hinting.sprite()->pos(), 160, 0)) );
567 else
568 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
571 QPixmap pix = m_loader(piece->name());
572 boost::shared_ptr<Sprite> sprite = createSprite(pix, pt);
573 sprite->setOpacity(160);
574 sprite->raise();
575 sprite->show();
577 m_hinting_pos = pt;
578 m_hinting = NamedSprite(piece->name(), sprite);
580 /*if(m_anim_fade)
581 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
582 m_hinting.sprite()->pos(), 0, 160)) );
583 else {
584 m_hinting.sprite()->setOpacity(160);
585 enqueue(boost::shared_ptr<Animation>(new DropAnimation(m_hinting.sprite())) );
591 void Board::reset() {
592 clearTags();
593 cancelSelection();
594 cancelPremove();
595 m_main_animation->stop();
598 void Board::flip(bool flipped)
600 if (m_flipped != flipped) {
601 m_flipped = flipped;
603 // update sprite positions
604 for (Point i = m_sprites.first(); i <= m_sprites.last(); i = m_sprites.next(i))
605 if (m_sprites[i].sprite())
606 animatePiece(m_sprites[i].sprite(), i, 1.0);
608 updateTags();
609 updateBorder();
613 void Board::draggingOn(int pool, int index, const QPoint& point) {
614 Point to = converter()->toLogical(point);
616 #if 0
617 //BROKEN
618 if (m_sprites.valid(to))
619 switch(m_entity.lock()->validTurn(piece->color())) {
620 case Moving: {
621 DropUserMove m(piece, to);
622 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
623 if (mv) {
624 setTags("validmove", to);
625 return;
627 break;
630 case Premoving:
631 setTags("validmove", to);
632 return;
634 default:
635 break;
637 #endif
639 clearTags("validmove");
642 bool Board::dropOn(int pool, int index, const QPoint& point) {
644 Point to = converter()->toLogical(point);
645 if (!m_sprites.valid(to))
646 return false;
648 clearTags("validmove");
650 switch(m_entity.lock()->validTurn(pool)) {
652 case Moving: {
653 DropUserMove m(pool, index, 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(pool, index, to);
664 if (m_entity.lock()->testPremove(m)) {
665 m_entity.lock()->addPremove(m);
666 setPremove(m);
668 break;
671 default:
672 break;
675 std::cout << "invalid move" << std::endl;
676 emit error(InvalidMove);
677 return false;