Initial implementation of the new abstraction architecture.
[tagua.git] / src / board.cpp
blob3307e95a97e78d51ca7d1da8288d8a9b86a29362
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 "boardsprite.h"
21 #include "pointconverter.h"
22 #include "entities/userentity.h"
23 #include "mainanimation.h"
24 #include "premove.h"
26 using namespace boost;
28 /** inherit instead of typedef to ease forward declaration :) */
29 class BoardTags : public std::map<QString, std::map<Point, boost::shared_ptr<Canvas::Pixmap> > > {
32 Board::Board(Canvas::Abstract* parent)
33 : PieceGroup(parent)
34 , m_sprites(0,0)
35 , m_hinting_pos(Point::invalid())
36 , selection(Point::invalid())
37 , lastSelection(Point::invalid()) {
39 m_tags = BoardTagsPtr(new BoardTags);
41 m_canvas_background = new Canvas::Group(this);
42 m_canvas_background->lower();
43 m_canvas_background->show();
45 m_pieces_group = new Canvas::Group(this);
46 m_pieces_group->show();
48 mySettingsChanged();
51 Board::~Board() {
52 delete m_pieces_group;
54 while(!m_canvas_background->items()->isEmpty())
55 delete m_canvas_background->items()->first();
56 delete m_canvas_background;
59 void Board::mySettingsChanged() {
60 Settings s_border = settings.group("board-border");
61 m_show_border = s_border.flag("visible", true);
62 m_border_color = (s_border["color"] |= QColor(Qt::white));
63 m_border_text_color = (s_border["text-color"] |= QColor(Qt::black));
64 m_border_font = (s_border["font"] |= QApplication::font());
66 recreateBorder();
69 void Board::settingsChanged() {
70 PieceGroup::settingsChanged();
71 mySettingsChanged();
74 void Board::updateBackground() {
75 while(!m_canvas_background->items()->isEmpty())
76 delete m_canvas_background->items()->first();
78 Loader::PixmapOrMap bg = m_tags_loader.getPixmapMap("background");
79 if(const QPixmap* p = boost::get<QPixmap>(&bg)) {
80 Canvas::TiledPixmap *t = new Canvas::TiledPixmap(*p, boardRect().size(), QPoint(),
81 true, m_canvas_background);
82 t->show();
84 else if(const Loader::PixmapMap* p = boost::get<Loader::PixmapMap>(&bg)) {
85 for(Loader::PixmapMap::const_iterator it = p->begin(); it != p->end(); ++it) {
86 Canvas::TiledPixmap *t = new Canvas::TiledPixmap(it->second, it->first.size(),
87 QPoint(), true, m_canvas_background);
88 t->moveTo(it->first.topLeft());
89 t->show();
94 boost::shared_ptr<Canvas::Pixmap> Board::addTag(const QString& name, Point pt, bool over) {
95 if(!m_sprites.valid(pt))
96 return boost::shared_ptr<Canvas::Pixmap>();
98 QPixmap p = m_tags_loader(name);
99 boost::shared_ptr<Canvas::Pixmap> item =
100 boost::shared_ptr<Canvas::Pixmap>(new Canvas::Pixmap(p, this));
101 item->moveTo(converter()->toReal(pt));
102 if(over)
103 item->stackOver(m_pieces_group);
104 else
105 item->stackUnder(m_pieces_group);
106 item->show();
108 (*m_tags)[name][pt] = item;
109 return item;
112 void Board::clearTags(const QString& name) {
113 m_tags->erase(name);
116 void Board::clearTags() {
117 m_tags->clear();
120 void Board::setTags(const QString& name, Point p1, Point p2, Point p3,
121 Point p4, Point p5, Point p6 ) {
122 //TODO: maybe this could be optimized a bit
123 clearTags(name);
124 addTag(name, p1);
125 addTag(name, p2);
126 addTag(name, p3);
127 addTag(name, p4);
128 addTag(name, p5);
129 addTag(name, p6);
132 void Board::recreateBorder() {
133 m_border_margins.clear();
134 m_border_items.clear();
136 if(!m_show_border || m_border_coords.size() == 0) {
137 m_border_size = 0;
138 return;
141 QFontMetrics fm(m_border_font);
142 m_border_size = std::max( fm.height(), fm.boundingRect("H").width() );
143 m_border_asc = fm.ascent() + (m_border_size-fm.height())/2;
145 for(int w = 0; w<2; w++)
146 for(int i=0;i<4;i++) {
147 Canvas::Rectangle *item = new Canvas::Rectangle(
148 w ? m_border_text_color : m_border_color, QSize(), this);
149 item->show();
150 m_border_margins.push_back( boost::shared_ptr<Canvas::Rectangle>( item ));
153 Point s = m_sprites.getSize();
154 for(int w = 0; w<2; w++)
155 for(int i = 0;i<s.x;i++) {
156 int c = w ? i : i+s.x+s.y;
157 QString l = m_border_coords.size()>c ? m_border_coords[c] : QString();
158 Canvas::Item *item = new Canvas::Text( l, m_border_text_color, m_border_font,
159 Canvas::Text::HCenter, Canvas::Text::VBaseline, this);
160 item->show();
161 m_border_items.push_back( boost::shared_ptr<Canvas::Item>( item ));
164 for(int w = 0; w<2; w++)
165 for(int i = 0;i<s.y;i++) {
166 int c = w ? i+s.x : i+2*s.x+s.y;
167 QString n = m_border_coords.size()>c ? m_border_coords[c] : QString();
168 Canvas::Item *item = new Canvas::Text( n, m_border_text_color, m_border_font,
169 Canvas::Text::HCenter, Canvas::Text::VBaseline, this);
170 item->show();
171 m_border_items.push_back( boost::shared_ptr<Canvas::Item>( item ));
174 m_pieces_group->raise();
176 updateBorder();
179 void Board::updateBorder() {
180 if(!m_show_border)
181 return;
183 int at = 0;
184 for(int w = 0; w<2; w++)
185 for(int i = 0;i<m_sprites.getSize().x;i++) {
186 int x = (m_flipped ? (m_sprites.getSize().x-1-i):i)*m_square_size+m_square_size/2;
187 int y = m_border_asc+(w?0:m_border_size+m_square_size*m_sprites.getSize().y)-m_border_size;
188 m_border_items[at++]->moveTo(x, y);
191 for(int w = 0; w<2; w++)
192 for(int i = 0;i<m_sprites.getSize().y;i++) {
193 int x = -m_border_size/2+(w?0:m_border_size+m_square_size*m_sprites.getSize().x);
194 int y = (!m_flipped ? (m_sprites.getSize().y-1-i):i)*m_square_size
195 +m_square_size/2-m_border_size/2+m_border_asc;
196 m_border_items[at++]->moveTo(x, y);
199 m_border_margins[0]->moveTo(-m_border_size,-m_border_size);
200 m_border_margins[0]->setSize(QSize(2*m_border_size+m_square_size*m_sprites.getSize().x,
201 m_border_size));
202 m_border_margins[4]->moveTo(-m_border_size-1,-m_border_size-1);
203 m_border_margins[4]->setSize(QSize(2*m_border_size+m_square_size*m_sprites.getSize().x+2, 1));
205 m_border_margins[1]->moveTo(-m_border_size,m_square_size*m_sprites.getSize().y);
206 m_border_margins[1]->setSize(QSize(2*m_border_size+m_square_size*m_sprites.getSize().x,
207 m_border_size));
208 m_border_margins[5]->moveTo(-m_border_size-1,m_square_size*m_sprites.getSize().y+m_border_size);
209 m_border_margins[5]->setSize(QSize(2*m_border_size+m_square_size*m_sprites.getSize().x+2, 1));
211 m_border_margins[2]->moveTo(-m_border_size,0);
212 m_border_margins[2]->setSize(QSize(m_border_size, m_square_size*m_sprites.getSize().y));
213 m_border_margins[6]->moveTo(-m_border_size-1,-m_border_size);
214 m_border_margins[6]->setSize(QSize(1, m_square_size*m_sprites.getSize().y+2*m_border_size));
216 m_border_margins[3]->moveTo(m_square_size*m_sprites.getSize().x, 0);
217 m_border_margins[3]->setSize(QSize(m_border_size, m_square_size*m_sprites.getSize().y));
218 m_border_margins[7]->moveTo(m_square_size*m_sprites.getSize().x+m_border_size, -m_border_size);
219 m_border_margins[7]->setSize(QSize(1, m_square_size*m_sprites.getSize().y+2*m_border_size));
222 void Board::createGrid(Point p, const QStringList& border_coords) {
223 m_border_coords = border_coords;
224 m_sprites = PieceGrid(p.x,p.y);
225 recreateBorder();
228 QRect Board::computeRect(Point p) const {
229 QPoint realPoint = converter()->toReal(p);
230 return squareRect(realPoint.x(), realPoint.y());
233 QRect Board::squareRect(int x, int y) const {
234 return QRect(x, y, m_square_size, m_square_size);
237 QRegion Board::computeRegion(Point p) const {
238 return QRegion(computeRect(p));
241 // selection
242 void Board::setSelection(const Point& p) {
243 lastSelection = selection;
244 selection = p;
245 setTags("selection", p);
248 void Board::cancelSelection() {
249 lastSelection = selection;
250 selection = Point::invalid();
251 clearTags("selection");
254 // premove
256 void Board::setPremove(const NormalUserMove& premove) {
257 m_premove_from = premove.from;
258 m_premove_to = premove.to;
259 setTags("premove", m_premove_from, m_premove_to);
262 void Board::setPremove(const DropUserMove& premove) {
263 m_premove_from = Point::invalid();
264 m_premove_to = premove.m_to;
265 setTags("premove", m_premove_to);
268 void Board::setPremove(const Premove& premove) {
269 setPremove(premove.toUserMove());
272 void Board::cancelPremove() {
273 m_premove_from = Point::invalid();
274 m_premove_to = Point::invalid();
275 clearTags("premove");
278 void Board::updateSprites() {
279 // adjust piece positions
280 for (Point i = m_sprites.first(); i <= m_sprites.last(); i = m_sprites.next(i)) {
281 boost::shared_ptr<Sprite> p = m_sprites[i].sprite();
283 if (p) {
284 // drawing sprite
285 p->setPixmap( m_loader( m_sprites[i].name() ) );
286 adjustSprite(i);
291 void Board::updateTags() {
292 for(BoardTags::iterator tit = m_tags->begin(); tit != m_tags->end(); ++tit)
293 for(std::map<Point, boost::shared_ptr<Canvas::Pixmap> >::iterator pt =
294 tit->second.begin(); pt != tit->second.end(); ++pt) {
295 pt->second->moveTo(converter()->toReal(pt->first));
296 pt->second->setPixmap(m_tags_loader(tit->first));
301 bool Board::doMove(const NormalUserMove& m) {
302 if (m_entity.lock()->oneClickMoves() || m_entity.lock()->validTurn(m.from) == Entity::Moving) {
303 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
304 if (mv) {
305 m_entity.lock()->executeMove(mv);
306 return true;
310 std::cout << "invalid move" << std::endl;
311 emit error(InvalidMove);
313 return false;
317 void Board::onResize(int new_size, bool force_reload) {
318 if(m_square_size == new_size && !force_reload)
319 return;
321 PieceGroup::onResize(new_size);
323 // update the size of the tag loader
324 m_tags_loader.setSize(m_square_size);
326 // update canvas background
327 updateBackground();
329 // update the sprites
330 updateSprites();
332 // update piece tags
333 updateTags();
335 // update border
336 updateBorder();
339 void Board::onMousePress(const QPoint& pos, int button) {
340 Point point = converter()->toLogical(pos);
341 if (!m_sprites.valid(point))
342 return;
344 //BEGIN left click
346 if (button == Qt::LeftButton) {
347 if (m_entity.lock()->oneClickMoves()) {
348 NormalUserMove m(Point::invalid(), point);
349 m_entity.lock()->setPromotion(m);
350 doMove(m);
352 else {
353 shared_ptr<Sprite> piece = m_sprites[point].sprite();
355 if (piece && m_entity.lock()->movable(point)) {
356 cancelSelection();
357 m_drag_info = DragInfoPtr(new DragInfo(point, pos, piece,
358 m_entity.lock()->validTurn(point)) );
359 piece->raise();
362 // if selection is valid, (pre)move to point
363 else if (selection != Point::invalid()) {
364 piece = m_sprites[selection].sprite();
365 Q_ASSERT(piece);
366 NormalUserMove m(selection, point);
367 m_entity.lock()->setPromotion(m);
369 switch(m_entity.lock()->validTurn(selection)) {
371 case UserEntity::Moving:
372 doMove(m);
373 cancelSelection();
374 break;
376 case UserEntity::Premoving:
377 if (m_entity.lock()->testPremove(m)) {
378 m_entity.lock()->addPremove(m);
379 setPremove(m);
380 cancelSelection();
382 break;
384 default:
385 break;
391 //END left click
393 //BEGIN right click
395 else if (button == Qt::RightButton) {
396 cancelSelection();
397 if (point == m_premove_from || point == m_premove_to)
398 cancelPremove();
399 m_entity.lock()->handleRightClick(point);
402 //END right click
405 void Board::onMouseRelease(const QPoint& pos, int button) {
406 Point point = converter()->toLogical(pos);
408 //BEGIN left click
410 if (button == Qt::LeftButton) {
412 if (m_drag_info) {
413 // Q_ASSERT(m_drag_info->piece);
414 Q_ASSERT(m_drag_info->sprite);
415 bool moved = false;
417 // remove valid move highlighting
418 clearTags("validmove");
420 // toggle selection if the piece didn't move
421 if (m_drag_info->from == point) {
422 if (lastSelection == point)
423 cancelSelection();
424 else
425 setSelection(point);
428 else {
429 NormalUserMove m(m_drag_info->from, point, true);
430 if (!m_sprites.valid(point))
431 m.to = Point::invalid();
433 m_entity.lock()->setPromotion(m);
435 switch(m_entity.lock()->validTurn(m_drag_info->from)) {
437 case UserEntity::Moving:
438 if (doMove(m))
439 moved = true;
440 break;
442 case UserEntity::Premoving:
443 if (m_entity.lock()->testPremove(m)) {
444 m_entity.lock()->addPremove(m);
445 setPremove(m);
447 break;
449 default:
450 break;
454 shared_ptr<Sprite> s = m_sprites[m_drag_info->from].sprite();
455 if (!moved && s && s->pos() != converter()->toReal(m_drag_info->from)) {
456 Q_ASSERT(s);
457 QPoint real = converter()->toReal(m_drag_info->from);
458 if( (point == m_drag_info->from) ? !m_anim_movement : !m_anim_fade)
459 enqueue(shared_ptr<Animation>(new InstantAnimation(s, real)));
460 else if (point == m_drag_info->from)
461 enqueue(shared_ptr<Animation>(new MovementAnimation(s, real)));
462 else
463 enqueue(shared_ptr<Animation>(new TeleportAnimation(s, s->pos(), real)));
466 m_drag_info = DragInfoPtr();
470 //END left button
473 void Board::onMouseMove(const QPoint& pos, int /*button*/) {
474 Point point = converter()->toLogical(pos);
476 if (m_drag_info) {
477 Q_ASSERT(m_drag_info->sprite);
478 // check drag threshold
479 if (!m_drag_info->dragStarted) {
480 QPoint delta = pos - m_drag_info->real;
481 if (delta.x() * delta.x() + delta.y() * delta.y() > DragInfo::DRAG_THRESHOLD) {
482 m_drag_info->dragStarted = true;
485 if (m_drag_info->dragStarted)
486 m_drag_info->sprite->moveTo(pos - QPoint(m_square_size / 2, m_square_size / 2) );
488 // highlight valid moves
489 NormalUserMove move(m_drag_info->from, point);
490 bool valid = m_sprites.valid(point);
491 if (valid) {
492 UserEntity::Action action = m_entity.lock()->validTurn(m_drag_info->from);
493 if (action == UserEntity::Moving)
494 valid = m_entity.lock()->testMove(move);
497 if (valid)
498 setTags("validmove", point);
499 else
500 clearTags("validmove");
502 else if (m_entity.lock()->oneClickMoves()) {
503 if(point == m_hinting_pos)
504 return;
506 AbstractPiece::Ptr hint;
508 if (m_sprites.valid(point)) {
509 if (AbstractMove::Ptr move = m_entity.lock()->testMove(
510 NormalUserMove(Point::invalid(), point))) {
511 // set move hint
512 hint = m_entity.lock()->moveHint(move);
516 updateHinting(point, hint);
520 void Board::onPositionChanged() {
521 if (m_entity.lock() && m_entity.lock()->oneClickMoves() && m_sprites.valid(m_hinting_pos)) {
522 AbstractPiece::Ptr hint;
524 if (AbstractMove::Ptr move = m_entity.lock()->testMove(
525 NormalUserMove(Point::invalid(), m_hinting_pos)) ) {
526 // set move hint
527 hint = m_entity.lock()->moveHint(move);
530 updateHinting(m_hinting_pos, hint);
534 void Board::onMouseLeave() {
535 updateHinting(Point::invalid(), AbstractPiece::Ptr());
538 void Board::updateHinting(Point pt, AbstractPiece::Ptr piece) {
539 if(!m_sprites.valid(pt))
540 piece = AbstractPiece::Ptr();
542 if(!piece || !m_sprites.valid(pt)) {
543 if(m_hinting.sprite()) {
544 if(m_anim_fade)
545 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
546 m_hinting.sprite()->pos(), 160, 0)) );
547 else
548 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
551 m_hinting_pos = Point::invalid();
552 m_hinting = Element();
554 else {
555 if(pt == m_hinting_pos) {
556 if(!piece->equals(m_hinting.piece())) {
557 m_hinting = Element(piece, m_hinting.sprite());
558 m_hinting.sprite()->setPixmap(m_loader(piece->name()));
561 else {
562 if(m_hinting.sprite()) {
563 if(m_anim_fade)
564 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
565 m_hinting.sprite()->pos(), 160, 0)) );
566 else
567 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
570 QPixmap pix = m_loader(piece->name());
571 boost::shared_ptr<Sprite> sprite = createSprite(pix, pt);
572 sprite->setOpacity(160);
573 sprite->raise();
574 sprite->show();
576 m_hinting_pos = pt;
577 m_hinting = Element(piece, sprite);
579 /*if(m_anim_fade)
580 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
581 m_hinting.sprite()->pos(), 0, 160)) );
582 else {
583 m_hinting.sprite()->setOpacity(160);
584 enqueue(boost::shared_ptr<Animation>(new DropAnimation(m_hinting.sprite())) );
590 void Board::reset() {
591 clearTags();
592 cancelSelection();
593 cancelPremove();
594 m_main_animation->stop();
597 void Board::flip(bool flipped)
599 if (m_flipped != flipped) {
600 m_flipped = flipped;
602 // update sprite positions
603 for (Point i = m_sprites.first(); i <= m_sprites.last(); i = m_sprites.next(i))
604 if (m_sprites[i].sprite())
605 animatePiece(m_sprites[i].sprite(), i, 1.0);
607 updateTags();
608 updateBorder();
612 void Board::draggingOn(AbstractPiece::Ptr piece, const QPoint& point) {
613 Point to = converter()->toLogical(point);
615 if (m_sprites.valid(to))
616 switch(m_entity.lock()->validTurn(piece->color())) {
617 case UserEntity::Moving: {
618 DropUserMove m(piece, to);
619 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
620 if (mv) {
621 setTags("validmove", to);
622 return;
624 break;
627 case UserEntity::Premoving:
628 setTags("validmove", to);
629 return;
631 default:
632 break;
635 clearTags("validmove");
638 bool Board::dropOn(AbstractPiece::Ptr piece, const QPoint& point) {
640 Point to = converter()->toLogical(point);
641 if (!m_sprites.valid(to))
642 return false;
644 clearTags("validmove");
646 switch(m_entity.lock()->validTurn(piece->color())) {
648 case UserEntity::Moving: {
649 DropUserMove m(piece, to);
650 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
651 if (mv) {
652 m_entity.lock()->executeMove(mv);
653 return true;
655 break;
658 case UserEntity::Premoving: {
659 DropUserMove m(piece, to);
660 if (m_entity.lock()->testPremove(m)) {
661 m_entity.lock()->addPremove(m);
662 setPremove(m);
664 break;
667 default:
668 break;
670 std::cout << "invalid move" << std::endl;
671 emit error(InvalidMove);
672 return false;
675 #include "board.moc"