Added borderCoord.
[tagua.git] / src / board.cpp
blobf9c910afe8402d7d939c32cf6a9734cb62afa3a5
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(const AnimationSettings& animSettings, 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)
44 , m_anim_settings(animSettings) {
46 m_main_animation = new MainAnimation( 1.0 );
48 m_tags = BoardTagsPtr(new BoardTags);
50 m_canvas_background = new KGameCanvasGroup(this);
51 m_canvas_background->show();
53 m_canvas_border = new KGameCanvasGroup(this);
54 m_canvas_border->show();
56 m_canvas_border_text = new KGameCanvasGroup(this);
57 m_canvas_border_text->show();
59 m_pieces_group = new KGameCanvasGroup(this);
60 m_pieces_group->show();
62 settingsChanged();
65 Board::~Board() {
66 delete m_pieces_group;
68 while(!m_canvas_background->items()->isEmpty())
69 delete m_canvas_background->items()->first();
70 delete m_canvas_background;
72 while(!m_canvas_border->items()->isEmpty())
73 delete m_canvas_border->items()->first();
74 delete m_canvas_border;
76 delete m_main_animation;
79 void Board::settingsChanged() {
80 Settings s_anim = settings().group("animations");
81 int speed = (s_anim["speed"] | 16);
82 int smoothness = (s_anim["smoothness"] | 16);
83 m_main_animation->setSpeed( 0.4*pow(10.0, speed/32.0) );
84 m_main_animation->setDelay( int(70.0*pow(10.0, -smoothness/32.0)) );
86 m_border_text_color = m_controls_loader.getStaticValue<QColor>("border_color");
87 m_border_font = m_controls_loader.getStaticValue<QFont>("border_font");
89 recreateBorder();
92 void Board::updateBackground() {
93 while(!m_canvas_background->items()->isEmpty())
94 delete m_canvas_background->items()->first();
96 if(!m_square_size)
97 return;
99 Loader::PixmapOrMap bg = m_tags_loader.getValue<Loader::PixmapOrMap>("background");
100 if(const QPixmap* p = boost::get<QPixmap>(&bg)) {
101 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(*p, boardRect().size(), QPoint(),
102 true, m_canvas_background);
103 t->show();
105 else if(const Loader::PixmapMap* p = boost::get<Loader::PixmapMap>(&bg)) {
106 for(Loader::PixmapMap::const_iterator it = p->begin(); it != p->end(); ++it) {
107 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(it->second, it->first.size(),
108 QPoint(), true, m_canvas_background);
109 t->moveTo(it->first.topLeft());
110 t->show();
115 void Board::enqueue(const shared_ptr<Animation>& anim) {
116 m_main_animation->addAnimation(anim);
119 void Board::adjustSprite(const Point& p, bool immediate) {
120 SpritePtr sprite = m_sprites[p].sprite();
122 if(!sprite)
123 return;
125 enqueue(
126 1 /*BROKEN m_anim_movement*/ && !immediate
127 ? AnimationPtr(new MovementAnimation(sprite, converter()->toReal(p), 1.0))
128 : AnimationPtr(new InstantAnimation(sprite, converter()->toReal(p)))
132 boost::shared_ptr<KGameCanvasPixmap> Board::addTag(const QString& name, Point pt, bool over) {
133 if(!m_sprites.valid(pt))
134 return boost::shared_ptr<KGameCanvasPixmap>();
136 QPixmap p = m_tags_loader(name);
137 boost::shared_ptr<KGameCanvasPixmap> item =
138 boost::shared_ptr<KGameCanvasPixmap>(new KGameCanvasPixmap(p, this));
139 item->moveTo(converter()->toReal(pt));
140 if(over)
141 item->stackOver(m_pieces_group);
142 else
143 item->stackUnder(m_pieces_group);
144 item->show();
146 (*m_tags)[name][pt] = item;
147 return item;
150 void Board::clearTags(const QString& name) {
151 m_tags->erase(name);
154 void Board::clearTags() {
155 m_tags->clear();
158 void Board::setTags(const QString& name, Point p1, Point p2, Point p3,
159 Point p4, Point p5, Point p6 ) {
160 //TODO: maybe this could be optimized a bit
161 clearTags(name);
162 addTag(name, p1);
163 addTag(name, p2);
164 addTag(name, p3);
165 addTag(name, p4);
166 addTag(name, p5);
167 addTag(name, p6);
170 void Board::recreateBorder() {
171 m_border_text.clear();
172 while(!m_canvas_border_text->items()->isEmpty())
173 delete m_canvas_border_text->items()->first();
175 if(m_border_coords.size() == 0)
176 return;
178 Point s = m_sprites.getSize();
179 for(int w = 0; w<2; w++)
180 for(int i = 0;i<s.x;i++) {
181 int c = w ? i : i+s.x+s.y;
182 QString l = m_border_coords.size()>c ? m_border_coords[c] : QString();
183 ConstrainedText *item = new ConstrainedText(m_canvas_border_text);
184 item->setColor(m_border_text_color);
185 item->setText(l);
186 item->setFont(m_border_font);
187 item->setColor(m_border_text_color);
188 item->show();
189 m_border_text.push_back(item);
192 for(int w = 0; w<2; w++)
193 for(int i = 0;i<s.y;i++) {
194 int c = w ? i+s.x : i+2*s.x+s.y;
195 QString n = m_border_coords.size()>c ? m_border_coords[c] : QString();
196 ConstrainedText *item = new ConstrainedText(m_canvas_border_text);
197 item->setColor(m_border_text_color);
198 item->setText(n);
199 item->setFont(m_border_font);
200 item->setColor(m_border_text_color);
201 item->show();
202 m_border_text.push_back(item);
205 m_pieces_group->raise();
207 updateBorder();
210 void Board::updateBorder() {
211 while(!m_canvas_border->items()->isEmpty())
212 delete m_canvas_border->items()->first();
214 if(!m_square_size)
215 return;
217 int at = 0;
218 for(int w = 0; w<2; w++)
219 for(int i = 0;i<m_sprites.getSize().x;i++) {
220 int x = (m_flipped ? (m_sprites.getSize().x-1-i) : i)*m_square_size;
221 int y = w ? -m_border_text_far : m_square_size*m_sprites.getSize().y+m_border_text_near;
223 m_border_text[at]->setVisible(m_border_text_near != m_border_text_far);
224 m_border_text[at]->setConstrainRect(QRect(x,y,m_square_size,m_border_text_far-m_border_text_near));
225 at++;
228 for(int w = 0; w<2; w++)
229 for(int i = 0;i<m_sprites.getSize().y;i++) {
230 int x = w ? (-m_border_text_far-m_border_text_near)/2
231 : m_square_size*m_sprites.getSize().x + (m_border_text_far+m_border_text_near)/2;
232 int y = (!m_flipped ? (m_sprites.getSize().y-1-i) : i)*m_square_size
233 + (m_square_size-m_border_text_far-m_border_text_near)/2;
235 m_border_text[at]->setVisible(m_border_text_near != m_border_text_far);
236 m_border_text[at]->setConstrainRect(QRect(x-m_square_size/2,y,m_square_size,m_border_text_far-m_border_text_near));
237 at++;
240 ::LuaApi::LuaValueMap params;
241 params["width"] = m_square_size*m_sprites.getSize().x;
242 params["height"] = m_square_size*m_sprites.getSize().y;
243 Loader::PixmapOrMap bord = m_controls_loader.getValue<Loader::PixmapOrMap>("border", &params);
244 if(const QPixmap* p = boost::get<QPixmap>(&bord)) {
245 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(*p, boardRect().size(), QPoint(),
246 true, m_canvas_border);
247 t->show();
249 else if(const Loader::PixmapMap* p = boost::get<Loader::PixmapMap>(&bord)) {
250 for(Loader::PixmapMap::const_iterator it = p->begin(); it != p->end(); ++it) {
251 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(it->second, it->first.size(),
252 QPoint(), true, m_canvas_border);
253 t->moveTo(it->first.topLeft());
254 t->show();
259 void Board::createGrid(Point p, const QStringList& border_coords) {
260 m_border_coords = border_coords;
261 m_sprites = PieceGrid(p.x,p.y);
262 recreateBorder();
265 QRect Board::computeRect(Point p) const {
266 QPoint realPoint = converter()->toReal(p);
267 return squareRect(realPoint.x(), realPoint.y());
270 QRect Board::squareRect(int x, int y) const {
271 return QRect(x, y, m_square_size, m_square_size);
274 QRegion Board::computeRegion(Point p) const {
275 return QRegion(computeRect(p));
278 // selection
279 void Board::setSelection(const Point& p) {
280 lastSelection = selection;
281 selection = p;
282 setTags("selection", p);
285 void Board::cancelSelection() {
286 lastSelection = selection;
287 selection = Point::invalid();
288 clearTags("selection");
291 // premove
293 void Board::setPremove(const NormalUserMove& premove) {
294 m_premove_from = premove.from;
295 m_premove_to = premove.to;
296 setTags("premove", m_premove_from, m_premove_to);
299 void Board::setPremove(const DropUserMove& premove) {
300 m_premove_from = Point::invalid();
301 m_premove_to = premove.m_to;
302 setTags("premove", m_premove_to);
305 void Board::setPremove(const Premove& premove) {
306 setPremove(premove.toUserMove());
309 void Board::cancelPremove() {
310 m_premove_from = Point::invalid();
311 m_premove_to = Point::invalid();
312 clearTags("premove");
315 void Board::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( m_loader( m_sprites[i].name() ) );
323 adjustSprite(i, true);
328 void Board::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(tit->first));
341 bool Board::doMove(const NormalUserMove& m) {
342 if (m_entity.lock()->oneClickMoves() || m_entity.lock()->validTurn(m.from) == Moving) {
343 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
344 if (mv) {
345 m_entity.lock()->executeMove(mv);
346 return true;
350 std::cout << "invalid move" << std::endl;
351 error(InvalidMove);
353 return false;
357 void Board::onResize(int new_size, int border_size, int border_text_near,
358 int border_text_far, bool force_reload) {
359 if(m_square_size == new_size && !force_reload)
360 return;
362 m_square_size = new_size;
363 m_border_size = border_size;
364 m_border_text_near = border_text_near;
365 m_border_text_far = border_text_far;
367 // update the size of the piece loader
368 m_loader.setSize(m_square_size);
370 // update the size of the tag loader
371 m_tags_loader.setSize(m_square_size);
373 // update the size of the controls loader
374 m_controls_loader.setSize(m_border_size);
376 // update canvas background
377 updateBackground();
379 // update the sprites
380 updateSprites();
382 // update piece tags
383 updateTags();
385 // update border
386 updateBorder();
389 void Board::onMousePress(const QPoint& pos, int button) {
390 Point point = converter()->toLogical(pos);
391 if (!m_sprites.valid(point))
392 return;
394 //BEGIN left click
396 if (button == Qt::LeftButton) {
397 if (m_entity.lock()->oneClickMoves()) {
398 NormalUserMove m(Point::invalid(), point);
399 m_entity.lock()->setPromotion(m);
400 doMove(m);
402 else {
403 shared_ptr<Sprite> piece = m_sprites[point].sprite();
405 if (piece && m_entity.lock()->movable(point)) {
406 cancelSelection();
407 m_drag_info = DragInfoPtr(new DragInfo(point, pos, piece,
408 m_entity.lock()->validTurn(point)) );
409 piece->raise();
412 // if selection is valid, (pre)move to point
413 else if (selection != Point::invalid()) {
414 piece = m_sprites[selection].sprite();
415 Q_ASSERT(piece);
416 NormalUserMove m(selection, point);
417 m_entity.lock()->setPromotion(m);
419 switch(m_entity.lock()->validTurn(selection)) {
421 case Moving:
422 doMove(m);
423 cancelSelection();
424 break;
426 case Premoving:
427 if (m_entity.lock()->testPremove(m)) {
428 m_entity.lock()->addPremove(m);
429 setPremove(m);
430 cancelSelection();
432 break;
434 default:
435 break;
441 //END left click
443 //BEGIN right click
445 else if (button == Qt::RightButton) {
446 cancelSelection();
447 if (point == m_premove_from || point == m_premove_to)
448 cancelPremove();
449 m_entity.lock()->handleRightClick(point);
452 //END right click
455 void Board::onMouseRelease(const QPoint& pos, int button) {
456 Point point = converter()->toLogical(pos);
458 //BEGIN left click
460 if (button == Qt::LeftButton) {
462 if (m_drag_info) {
463 // Q_ASSERT(m_drag_info->piece);
464 Q_ASSERT(m_drag_info->sprite);
465 bool moved = false;
467 // remove valid move highlighting
468 clearTags("validmove");
470 // toggle selection if the piece didn't move
471 if (m_drag_info->from == point) {
472 if (lastSelection == point)
473 cancelSelection();
474 else
475 setSelection(point);
478 else {
479 NormalUserMove m(m_drag_info->from, point, true);
480 if (!m_sprites.valid(point))
481 m.to = Point::invalid();
483 m_entity.lock()->setPromotion(m);
485 switch(m_entity.lock()->validTurn(m_drag_info->from)) {
487 case Moving:
488 if (doMove(m))
489 moved = true;
490 break;
492 case Premoving:
493 if (m_entity.lock()->testPremove(m)) {
494 m_entity.lock()->addPremove(m);
495 setPremove(m);
497 break;
499 default:
500 break;
504 shared_ptr<Sprite> s = m_sprites[m_drag_info->from].sprite();
505 if (!moved && s && s->pos() != converter()->toReal(m_drag_info->from)) {
506 Q_ASSERT(s);
507 QPoint real = converter()->toReal(m_drag_info->from);
508 if( (point == m_drag_info->from) ? 0/* !m_anim_movement*/ : 0 /* !m_anim_fade*/) //BROKEN
509 enqueue(shared_ptr<Animation>(new InstantAnimation(s, real)));
510 else if (point == m_drag_info->from)
511 enqueue(shared_ptr<Animation>(new MovementAnimation(s, real)));
512 else
513 enqueue(shared_ptr<Animation>(new TeleportAnimation(s, s->pos(), real)));
516 m_drag_info = DragInfoPtr();
520 //END left button
523 void Board::onMouseMove(const QPoint& pos, int /*button*/) {
524 Point point = converter()->toLogical(pos);
526 if (m_drag_info) {
527 Q_ASSERT(m_drag_info->sprite);
528 // check drag threshold
529 if (!m_drag_info->dragStarted) {
530 QPoint delta = pos - m_drag_info->real;
531 if (delta.x() * delta.x() + delta.y() * delta.y() > DragInfo::DRAG_THRESHOLD) {
532 m_drag_info->dragStarted = true;
535 if (m_drag_info->dragStarted)
536 m_drag_info->sprite->moveTo(pos - QPoint(m_square_size / 2, m_square_size / 2) );
538 // highlight valid moves
539 NormalUserMove move(m_drag_info->from, point);
540 bool valid = m_sprites.valid(point);
541 if (valid) {
542 InteractionType action = m_entity.lock()->validTurn(m_drag_info->from);
543 if (action == Moving)
544 valid = m_entity.lock()->testMove(move);
547 if (valid)
548 setTags("validmove", point);
549 else
550 clearTags("validmove");
552 else if (m_entity.lock()->oneClickMoves()) {
553 if(point == m_hinting_pos)
554 return;
556 AbstractPiece::Ptr hint;
558 if (m_sprites.valid(point)) {
559 if (AbstractMove::Ptr move = m_entity.lock()->testMove(
560 NormalUserMove(Point::invalid(), point))) {
561 // set move hint
562 hint = m_entity.lock()->moveHint(move);
566 updateHinting(point, hint);
570 void Board::onPositionChanged() {
571 if (m_entity.lock() && m_entity.lock()->oneClickMoves() && m_sprites.valid(m_hinting_pos)) {
572 AbstractPiece::Ptr hint;
574 if (AbstractMove::Ptr move = m_entity.lock()->testMove(
575 NormalUserMove(Point::invalid(), m_hinting_pos)) ) {
576 // set move hint
577 hint = m_entity.lock()->moveHint(move);
580 updateHinting(m_hinting_pos, hint);
584 void Board::onMouseLeave() {
585 updateHinting(Point::invalid(), AbstractPiece::Ptr());
588 void Board::updateHinting(Point pt, AbstractPiece::Ptr piece) {
589 if(!m_sprites.valid(pt))
590 piece = AbstractPiece::Ptr();
592 if(!piece || !m_sprites.valid(pt)) {
593 if(m_hinting.sprite()) {
594 if(1 /*BROKEN m_anim_fade*/)
595 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
596 m_hinting.sprite()->pos(), 160, 0)) );
597 else
598 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
601 m_hinting_pos = Point::invalid();
602 m_hinting = NamedSprite();
604 else {
605 if(pt == m_hinting_pos) {
606 if(!(piece->name() == m_hinting.name())) {
607 m_hinting = NamedSprite(piece->name(), m_hinting.sprite());
608 m_hinting.sprite()->setPixmap(m_loader(piece->name()));
611 else {
612 if(m_hinting.sprite()) {
613 if(1 /*BROKEN m_anim_fade*/)
614 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
615 m_hinting.sprite()->pos(), 160, 0)) );
616 else
617 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
620 QPixmap pix = m_loader(piece->name());
621 SpritePtr sprite(new Sprite(pix, piecesGroup(), converter()->toReal(pt)));
622 sprite->setOpacity(160);
623 sprite->raise();
624 sprite->show();
626 m_hinting_pos = pt;
627 m_hinting = NamedSprite(piece->name(), sprite);
629 /*if(m_anim_fade)
630 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
631 m_hinting.sprite()->pos(), 0, 160)) );
632 else {
633 m_hinting.sprite()->setOpacity(160);
634 enqueue(boost::shared_ptr<Animation>(new DropAnimation(m_hinting.sprite())) );
640 void Board::reset() {
641 clearTags();
642 cancelSelection();
643 cancelPremove();
644 m_main_animation->stop();
647 void Board::flip(bool flipped)
649 if (m_flipped != flipped) {
650 m_flipped = flipped;
652 // update sprite positions
653 for (Point i = m_sprites.first(); i <= m_sprites.last(); i = m_sprites.next(i))
654 if (m_sprites[i].sprite())
655 adjustSprite(i);
657 updateTags();
658 updateBorder();
662 void Board::draggingOn(int pool, int index, const QPoint& point) {
663 Point to = converter()->toLogical(point);
665 if (m_sprites.valid(to))
666 switch(m_entity.lock()->validTurn(pool)) {
667 case Moving: {
668 DropUserMove m(pool, index, to);
669 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
670 if (mv) {
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 Board::dropOn(int 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 DropUserMove m(pool, index, to);
700 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
701 if (mv) {
702 m_entity.lock()->executeMove(mv);
703 return true;
705 break;
708 case Premoving: {
709 DropUserMove m(pool, index, to);
710 if (m_entity.lock()->testPremove(m)) {
711 m_entity.lock()->addPremove(m);
712 setPremove(m);
714 break;
717 default:
718 break;
721 std::cout << "invalid move" << std::endl;
722 error(InvalidMove);
723 return false;