Tagua now compiles with QT_NO_KEYWORDS. Boost 1.34 is required.
[tagua/yd.git] / src / board.cpp
blob7939c89f8d9baee79a8bcbb55fc91fd245fc0faf
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 <math.h>
12 #include <iostream>
13 #include <QPainter>
14 #include <QApplication>
15 #include <QMouseEvent>
17 #include "mastersettings.h"
18 #include "board.h"
19 #include "sprite.h"
20 #include "animation.h"
21 #include "pointconverter.h"
22 #include "entities/userentity.h"
23 #include "mainanimation.h"
24 #include "premove.h"
25 #include "constrainedtext.h"
27 using namespace boost;
29 /** inherit instead of typedef to ease forward declaration :) */
30 class BoardTags : public std::map<QString, std::map<Point, boost::shared_ptr<KGameCanvasPixmap> > > {
33 Board::Board(KGameCanvasAbstract* parent)
34 : ClickableCanvas(parent)
35 , m_flipped(false)
36 , m_square_size(0)
37 , m_border_size(0)
38 , m_sprites(0,0)
39 , m_hinting_pos(Point::invalid())
40 , selection(Point::invalid())
41 , lastSelection(Point::invalid())
42 , m_dropped_pool(-1)
43 , m_dropped_index(-1) {
45 m_main_animation = new MainAnimation( 1.0 );
47 m_tags = BoardTagsPtr(new BoardTags);
49 m_canvas_background = new KGameCanvasGroup(this);
50 m_canvas_background->show();
52 m_canvas_border = new KGameCanvasGroup(this);
53 m_canvas_border->show();
55 m_canvas_border_text = new KGameCanvasGroup(this);
56 m_canvas_border_text->show();
58 m_pieces_group = new KGameCanvasGroup(this);
59 m_pieces_group->show();
61 settingsChanged();
64 Board::~Board() {
65 delete m_pieces_group;
67 while(!m_canvas_background->items()->isEmpty())
68 delete m_canvas_background->items()->first();
69 delete m_canvas_background;
71 while(!m_canvas_border->items()->isEmpty())
72 delete m_canvas_border->items()->first();
73 delete m_canvas_border;
75 delete m_main_animation;
78 void Board::settingsChanged() {
79 Settings s_anim = settings().group("animations");
80 int speed = (s_anim["speed"] | 16);
81 int smoothness = (s_anim["smoothness"] | 16);
82 m_main_animation->setSpeed( 0.4*pow(10.0, speed/32.0) );
83 m_main_animation->setDelay( int(70.0*pow(10.0, -smoothness/32.0)) );
85 m_border_text_color = m_controls_loader.getStaticValue<QColor>("border_color");
86 m_border_font = m_controls_loader.getStaticValue<QFont>("border_font");
88 recreateBorder();
91 void Board::updateBackground() {
92 while(!m_canvas_background->items()->isEmpty())
93 delete m_canvas_background->items()->first();
95 if(!m_square_size)
96 return;
98 Loader::PixmapOrMap bg = m_tags_loader.getValue<Loader::PixmapOrMap>("background");
99 if(const QPixmap* p = boost::get<QPixmap>(&bg)) {
100 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(*p, boardRect().size(), QPoint(),
101 true, m_canvas_background);
102 t->show();
104 else if(const Loader::PixmapMap* p = boost::get<Loader::PixmapMap>(&bg)) {
105 for(Loader::PixmapMap::const_iterator it = p->begin(); it != p->end(); ++it) {
106 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(it->second, it->first.size(),
107 QPoint(), true, m_canvas_background);
108 t->moveTo(it->first.topLeft());
109 t->show();
114 void Board::enqueue(const shared_ptr<Animation>& anim) {
115 m_main_animation->addAnimation(anim);
118 void Board::adjustSprite(const Point& p, bool immediate) {
119 SpritePtr sprite = m_sprites[p].sprite();
121 if(!sprite)
122 return;
124 enqueue(
125 1 /*BROKEN m_anim_movement*/ && !immediate
126 ? AnimationPtr(new MovementAnimation(sprite, converter()->toReal(p), 1.0))
127 : AnimationPtr(new InstantAnimation(sprite, converter()->toReal(p)))
131 boost::shared_ptr<KGameCanvasPixmap> Board::addTag(const QString& name, Point pt, bool over) {
132 if(!m_sprites.valid(pt))
133 return boost::shared_ptr<KGameCanvasPixmap>();
135 QPixmap p = m_tags_loader(name);
136 boost::shared_ptr<KGameCanvasPixmap> item =
137 boost::shared_ptr<KGameCanvasPixmap>(new KGameCanvasPixmap(p, this));
138 item->moveTo(converter()->toReal(pt));
139 if(over)
140 item->stackOver(m_pieces_group);
141 else
142 item->stackUnder(m_pieces_group);
143 item->show();
145 (*m_tags)[name][pt] = item;
146 return item;
149 void Board::clearTags(const QString& name) {
150 m_tags->erase(name);
153 void Board::clearTags() {
154 m_tags->clear();
157 void Board::setTags(const QString& name, Point p1, Point p2, Point p3,
158 Point p4, Point p5, Point p6 ) {
159 //TODO: maybe this could be optimized a bit
160 clearTags(name);
161 addTag(name, p1);
162 addTag(name, p2);
163 addTag(name, p3);
164 addTag(name, p4);
165 addTag(name, p5);
166 addTag(name, p6);
169 void Board::recreateBorder() {
170 m_border_text.clear();
171 while(!m_canvas_border_text->items()->isEmpty())
172 delete m_canvas_border_text->items()->first();
174 if(m_border_coords.size() == 0)
175 return;
177 Point s = m_sprites.getSize();
178 for(int w = 0; w<2; w++)
179 for(int i = 0;i<s.x;i++) {
180 int c = w ? i : i+s.x+s.y;
181 QString l = m_border_coords.size()>c ? m_border_coords[c] : QString();
182 ConstrainedText *item = new ConstrainedText(m_canvas_border_text);
183 item->setColor(m_border_text_color);
184 item->setText(l);
185 item->setFont(m_border_font);
186 item->setColor(m_border_text_color);
187 item->show();
188 m_border_text.push_back(item);
191 for(int w = 0; w<2; w++)
192 for(int i = 0;i<s.y;i++) {
193 int c = w ? i+s.x : i+2*s.x+s.y;
194 QString n = m_border_coords.size()>c ? m_border_coords[c] : QString();
195 ConstrainedText *item = new ConstrainedText(m_canvas_border_text);
196 item->setColor(m_border_text_color);
197 item->setText(n);
198 item->setFont(m_border_font);
199 item->setColor(m_border_text_color);
200 item->show();
201 m_border_text.push_back(item);
204 m_pieces_group->raise();
206 updateBorder();
209 void Board::updateBorder() {
210 while(!m_canvas_border->items()->isEmpty())
211 delete m_canvas_border->items()->first();
213 if(!m_square_size)
214 return;
216 int at = 0;
217 for(int w = 0; w<2; w++)
218 for(int i = 0;i<m_sprites.getSize().x;i++) {
219 int x = (m_flipped ? (m_sprites.getSize().x-1-i) : i)*m_square_size;
220 int y = w ? -m_border_text_far : m_square_size*m_sprites.getSize().y+m_border_text_near;
222 m_border_text[at]->setVisible(m_border_text_near != m_border_text_far);
223 m_border_text[at]->setConstrainRect(QRect(x,y,m_square_size,m_border_text_far-m_border_text_near));
224 at++;
227 for(int w = 0; w<2; w++)
228 for(int i = 0;i<m_sprites.getSize().y;i++) {
229 int x = w ? (-m_border_text_far-m_border_text_near)/2
230 : m_square_size*m_sprites.getSize().x + (m_border_text_far+m_border_text_near)/2;
231 int y = (!m_flipped ? (m_sprites.getSize().y-1-i) : i)*m_square_size
232 + (m_square_size-m_border_text_far-m_border_text_near)/2;
234 m_border_text[at]->setVisible(m_border_text_near != m_border_text_far);
235 m_border_text[at]->setConstrainRect(QRect(x-m_square_size/2,y,m_square_size,m_border_text_far-m_border_text_near));
236 at++;
239 ::LuaApi::LuaValueMap params;
240 params["width"] = m_square_size*m_sprites.getSize().x;
241 params["height"] = m_square_size*m_sprites.getSize().y;
242 Loader::PixmapOrMap bord = m_controls_loader.getValue<Loader::PixmapOrMap>("border", &params);
243 if(const QPixmap* p = boost::get<QPixmap>(&bord)) {
244 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(*p, boardRect().size(), QPoint(),
245 true, m_canvas_border);
246 t->show();
248 else if(const Loader::PixmapMap* p = boost::get<Loader::PixmapMap>(&bord)) {
249 for(Loader::PixmapMap::const_iterator it = p->begin(); it != p->end(); ++it) {
250 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(it->second, it->first.size(),
251 QPoint(), true, m_canvas_border);
252 t->moveTo(it->first.topLeft());
253 t->show();
258 void Board::createGrid(Point p, const QStringList& border_coords) {
259 m_border_coords = border_coords;
260 m_sprites = PieceGrid(p.x,p.y);
261 recreateBorder();
264 QRect Board::computeRect(Point p) const {
265 QPoint realPoint = converter()->toReal(p);
266 return squareRect(realPoint.x(), realPoint.y());
269 QRect Board::squareRect(int x, int y) const {
270 return QRect(x, y, m_square_size, m_square_size);
273 QRegion Board::computeRegion(Point p) const {
274 return QRegion(computeRect(p));
277 // selection
278 void Board::setSelection(const Point& p) {
279 lastSelection = selection;
280 selection = p;
281 setTags("selection", p);
284 void Board::cancelSelection() {
285 lastSelection = selection;
286 selection = Point::invalid();
287 clearTags("selection");
290 // premove
292 void Board::setPremove(const NormalUserMove& premove) {
293 m_premove_from = premove.from;
294 m_premove_to = premove.to;
295 setTags("premove", m_premove_from, m_premove_to);
298 void Board::setPremove(const DropUserMove& premove) {
299 m_premove_from = Point::invalid();
300 m_premove_to = premove.m_to;
301 setTags("premove", m_premove_to);
304 void Board::setPremove(const Premove& premove) {
305 setPremove(premove.toUserMove());
308 void Board::cancelPremove() {
309 m_premove_from = Point::invalid();
310 m_premove_to = Point::invalid();
311 clearTags("premove");
314 void Board::updateSprites() {
315 // adjust piece positions
316 for (Point i = m_sprites.first(); i <= m_sprites.last(); i = m_sprites.next(i)) {
317 boost::shared_ptr<Sprite> p = m_sprites[i].sprite();
319 if (p) {
320 // drawing sprite
321 p->setPixmap( m_loader( m_sprites[i].name() ) );
322 adjustSprite(i, true);
327 void Board::updateTags() {
328 if(!m_square_size)
329 return;
331 for(BoardTags::iterator tit = m_tags->begin(); tit != m_tags->end(); ++tit)
332 for(std::map<Point, boost::shared_ptr<KGameCanvasPixmap> >::iterator pt =
333 tit->second.begin(); pt != tit->second.end(); ++pt) {
334 pt->second->moveTo(converter()->toReal(pt->first));
335 pt->second->setPixmap(m_tags_loader(tit->first));
340 bool Board::doMove(const NormalUserMove& m) {
341 if (m_entity.lock()->oneClickMoves() || m_entity.lock()->validTurn(m.from) == Moving) {
342 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
343 if (mv) {
344 m_entity.lock()->executeMove(mv);
345 return true;
349 std::cout << "invalid move" << std::endl;
350 error(InvalidMove);
352 return false;
356 void Board::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 Board::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 NormalUserMove m(Point::invalid(), point);
398 m_entity.lock()->setPromotion(m);
399 doMove(m);
401 else {
402 shared_ptr<Sprite> piece = m_sprites[point].sprite();
404 if (piece && m_entity.lock()->movable(point)) {
405 cancelSelection();
406 m_drag_info = DragInfoPtr(new DragInfo(point, pos, piece,
407 m_entity.lock()->validTurn(point)) );
408 piece->raise();
411 // if selection is valid, (pre)move to point
412 else if (selection != Point::invalid()) {
413 piece = m_sprites[selection].sprite();
414 Q_ASSERT(piece);
415 NormalUserMove m(selection, point);
416 m_entity.lock()->setPromotion(m);
418 switch(m_entity.lock()->validTurn(selection)) {
420 case Moving:
421 doMove(m);
422 cancelSelection();
423 break;
425 case Premoving:
426 if (m_entity.lock()->testPremove(m)) {
427 m_entity.lock()->addPremove(m);
428 setPremove(m);
429 cancelSelection();
431 break;
433 default:
434 break;
440 //END left click
442 //BEGIN right click
444 else if (button == Qt::RightButton) {
445 cancelSelection();
446 if (point == m_premove_from || point == m_premove_to)
447 cancelPremove();
448 m_entity.lock()->handleRightClick(point);
451 //END right click
454 void Board::onMouseRelease(const QPoint& pos, int button) {
455 Point point = converter()->toLogical(pos);
457 //BEGIN left click
459 if (button == Qt::LeftButton) {
461 if (m_drag_info) {
462 // Q_ASSERT(m_drag_info->piece);
463 Q_ASSERT(m_drag_info->sprite);
464 bool moved = false;
466 // remove valid move highlighting
467 clearTags("validmove");
469 // toggle selection if the piece didn't move
470 if (m_drag_info->from == point) {
471 if (lastSelection == point)
472 cancelSelection();
473 else
474 setSelection(point);
477 else {
478 NormalUserMove m(m_drag_info->from, point, true);
479 if (!m_sprites.valid(point))
480 m.to = Point::invalid();
482 m_entity.lock()->setPromotion(m);
484 switch(m_entity.lock()->validTurn(m_drag_info->from)) {
486 case Moving:
487 if (doMove(m))
488 moved = true;
489 break;
491 case Premoving:
492 if (m_entity.lock()->testPremove(m)) {
493 m_entity.lock()->addPremove(m);
494 setPremove(m);
496 break;
498 default:
499 break;
503 shared_ptr<Sprite> s = m_sprites[m_drag_info->from].sprite();
504 if (!moved && s && s->pos() != converter()->toReal(m_drag_info->from)) {
505 Q_ASSERT(s);
506 QPoint real = converter()->toReal(m_drag_info->from);
507 if( (point == m_drag_info->from) ? 0/* !m_anim_movement*/ : 0 /* !m_anim_fade*/) //BROKEN
508 enqueue(shared_ptr<Animation>(new InstantAnimation(s, real)));
509 else if (point == m_drag_info->from)
510 enqueue(shared_ptr<Animation>(new MovementAnimation(s, real)));
511 else
512 enqueue(shared_ptr<Animation>(new TeleportAnimation(s, s->pos(), real)));
515 m_drag_info = DragInfoPtr();
519 //END left button
522 void Board::onMouseMove(const QPoint& pos, int /*button*/) {
523 Point point = converter()->toLogical(pos);
525 if (m_drag_info) {
526 Q_ASSERT(m_drag_info->sprite);
527 // check drag threshold
528 if (!m_drag_info->dragStarted) {
529 QPoint delta = pos - m_drag_info->real;
530 if (delta.x() * delta.x() + delta.y() * delta.y() > DragInfo::DRAG_THRESHOLD) {
531 m_drag_info->dragStarted = true;
534 if (m_drag_info->dragStarted)
535 m_drag_info->sprite->moveTo(pos - QPoint(m_square_size / 2, m_square_size / 2) );
537 // highlight valid moves
538 NormalUserMove move(m_drag_info->from, point);
539 bool valid = m_sprites.valid(point);
540 if (valid) {
541 InteractionType action = m_entity.lock()->validTurn(m_drag_info->from);
542 if (action == Moving)
543 valid = m_entity.lock()->testMove(move);
546 if (valid)
547 setTags("validmove", point);
548 else
549 clearTags("validmove");
551 else if (m_entity.lock()->oneClickMoves()) {
552 if(point == m_hinting_pos)
553 return;
555 AbstractPiece::Ptr hint;
557 if (m_sprites.valid(point)) {
558 if (AbstractMove::Ptr move = m_entity.lock()->testMove(
559 NormalUserMove(Point::invalid(), point))) {
560 // set move hint
561 hint = m_entity.lock()->moveHint(move);
565 updateHinting(point, hint);
569 void Board::onPositionChanged() {
570 if (m_entity.lock() && m_entity.lock()->oneClickMoves() && m_sprites.valid(m_hinting_pos)) {
571 AbstractPiece::Ptr hint;
573 if (AbstractMove::Ptr move = m_entity.lock()->testMove(
574 NormalUserMove(Point::invalid(), m_hinting_pos)) ) {
575 // set move hint
576 hint = m_entity.lock()->moveHint(move);
579 updateHinting(m_hinting_pos, hint);
583 void Board::onMouseLeave() {
584 updateHinting(Point::invalid(), AbstractPiece::Ptr());
587 void Board::updateHinting(Point pt, AbstractPiece::Ptr piece) {
588 if(!m_sprites.valid(pt))
589 piece = AbstractPiece::Ptr();
591 if(!piece || !m_sprites.valid(pt)) {
592 if(m_hinting.sprite()) {
593 if(1 /*BROKEN m_anim_fade*/)
594 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
595 m_hinting.sprite()->pos(), 160, 0)) );
596 else
597 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
600 m_hinting_pos = Point::invalid();
601 m_hinting = NamedSprite();
603 else {
604 if(pt == m_hinting_pos) {
605 if(!(piece->name() == m_hinting.name())) {
606 m_hinting = NamedSprite(piece->name(), m_hinting.sprite());
607 m_hinting.sprite()->setPixmap(m_loader(piece->name()));
610 else {
611 if(m_hinting.sprite()) {
612 if(1 /*BROKEN m_anim_fade*/)
613 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
614 m_hinting.sprite()->pos(), 160, 0)) );
615 else
616 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
619 QPixmap pix = m_loader(piece->name());
620 SpritePtr sprite(new Sprite(pix, piecesGroup(), converter()->toReal(pt)));
621 sprite->setOpacity(160);
622 sprite->raise();
623 sprite->show();
625 m_hinting_pos = pt;
626 m_hinting = NamedSprite(piece->name(), sprite);
628 /*if(m_anim_fade)
629 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
630 m_hinting.sprite()->pos(), 0, 160)) );
631 else {
632 m_hinting.sprite()->setOpacity(160);
633 enqueue(boost::shared_ptr<Animation>(new DropAnimation(m_hinting.sprite())) );
639 void Board::reset() {
640 clearTags();
641 cancelSelection();
642 cancelPremove();
643 m_main_animation->stop();
646 void Board::flip(bool flipped)
648 if (m_flipped != flipped) {
649 m_flipped = flipped;
651 // update sprite positions
652 for (Point i = m_sprites.first(); i <= m_sprites.last(); i = m_sprites.next(i))
653 if (m_sprites[i].sprite())
654 adjustSprite(i);
656 updateTags();
657 updateBorder();
661 void Board::draggingOn(int pool, int index, const QPoint& point) {
662 Point to = converter()->toLogical(point);
664 if (m_sprites.valid(to))
665 switch(m_entity.lock()->validTurn(pool)) {
666 case Moving: {
667 DropUserMove m(pool, index, to);
668 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
669 if (mv) {
670 setTags("validmove", to);
671 return;
673 break;
676 case Premoving:
677 setTags("validmove", to);
678 return;
680 default:
681 break;
684 clearTags("validmove");
687 bool Board::dropOn(int pool, int index, const QPoint& point) {
689 Point to = converter()->toLogical(point);
690 if (!m_sprites.valid(to))
691 return false;
693 clearTags("validmove");
695 switch(m_entity.lock()->validTurn(pool)) {
697 case Moving: {
698 DropUserMove m(pool, index, to);
699 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
700 if (mv) {
701 m_entity.lock()->executeMove(mv);
702 return true;
704 break;
707 case Premoving: {
708 DropUserMove 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;