Replaced all std::cout with kDebug.
[tagua/yd.git] / src / board.cpp
blob1ee0afda3e7c66979af7145e8ea759ac65ec239b
1 /*
2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@gmail.com>
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>
16 #include <KDebug>
18 #include "mastersettings.h"
19 #include "board.h"
20 #include "sprite.h"
21 #include "animation.h"
22 #include "pointconverter.h"
23 #include "entities/userentity.h"
24 #include "mainanimation.h"
25 #include "premove.h"
26 #include "constrainedtext.h"
28 using namespace boost;
30 /** inherit instead of typedef to ease forward declaration :) */
31 class BoardTags : public std::map<QString, std::map<Point, boost::shared_ptr<KGameCanvasPixmap> > > {
34 Board::Board(const AnimationSettings& animSettings, KGameCanvasAbstract* parent)
35 : ClickableCanvas(parent)
36 , m_flipped(false)
37 , m_square_size(0)
38 , m_border_size(0)
39 , m_sprites(0,0)
40 , m_hinting_pos(Point::invalid())
41 , selection(Point::invalid())
42 , lastSelection(Point::invalid())
43 , m_dropped_pool(-1)
44 , m_dropped_index(-1)
45 , m_anim_settings(animSettings) {
47 m_main_animation = new MainAnimation( 1.0 );
49 m_tags = BoardTagsPtr(new BoardTags);
51 m_canvas_background = new KGameCanvasGroup(this);
52 m_canvas_background->show();
54 m_canvas_border = new KGameCanvasGroup(this);
55 m_canvas_border->show();
57 m_canvas_border_text = new KGameCanvasGroup(this);
58 m_canvas_border_text->show();
60 m_pieces_group = new KGameCanvasGroup(this);
61 m_pieces_group->show();
63 settingsChanged();
66 Board::~Board() {
67 delete m_pieces_group;
69 while(!m_canvas_background->items()->isEmpty())
70 delete m_canvas_background->items()->first();
71 delete m_canvas_background;
73 while(!m_canvas_border->items()->isEmpty())
74 delete m_canvas_border->items()->first();
75 delete m_canvas_border;
77 delete m_main_animation;
80 void Board::settingsChanged() {
81 Settings s_anim = settings().group("animations");
82 int speed = (s_anim["speed"] | 16);
83 int smoothness = (s_anim["smoothness"] | 16);
84 m_main_animation->setSpeed( 0.4*pow(10.0, speed/32.0) );
85 m_main_animation->setDelay( int(70.0*pow(10.0, -smoothness/32.0)) );
87 m_border_text_color = m_controls_loader.getStaticValue<QColor>("border_color");
88 m_border_font = m_controls_loader.getStaticValue<QFont>("border_font");
90 recreateBorder();
93 void Board::updateBackground() {
94 while(!m_canvas_background->items()->isEmpty())
95 delete m_canvas_background->items()->first();
97 if(!m_square_size)
98 return;
100 Loader::PixmapOrMap bg = m_tags_loader.getValue<Loader::PixmapOrMap>("background");
101 if(const QPixmap* p = boost::get<QPixmap>(&bg)) {
102 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(*p, boardRect().size(), QPoint(),
103 true, m_canvas_background);
104 t->show();
106 else if(const Loader::PixmapMap* p = boost::get<Loader::PixmapMap>(&bg)) {
107 for(Loader::PixmapMap::const_iterator it = p->begin(); it != p->end(); ++it) {
108 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(it->second, it->first.size(),
109 QPoint(), true, m_canvas_background);
110 t->moveTo(it->first.topLeft());
111 t->show();
114 else
115 kError() << "Board::updateBackground(): unexpected type in boost::variant!";
118 void Board::enqueue(const shared_ptr<Animation>& anim) {
119 m_main_animation->addAnimation(anim);
122 void Board::adjustSprite(const Point& p, bool immediate) {
123 SpritePtr sprite = m_sprites[p].sprite();
125 if(!sprite)
126 return;
128 enqueue(
129 1 /*BROKEN m_anim_movement*/ && !immediate
130 ? AnimationPtr(new MovementAnimation(sprite, converter()->toReal(p), 1.0))
131 : AnimationPtr(new InstantAnimation(sprite, converter()->toReal(p)))
135 boost::shared_ptr<KGameCanvasPixmap> Board::addTag(const QString& name, Point pt, bool over) {
136 if(!m_sprites.valid(pt))
137 return boost::shared_ptr<KGameCanvasPixmap>();
139 QPixmap p = m_tags_loader.getPixmap(name);
140 boost::shared_ptr<KGameCanvasPixmap> item =
141 boost::shared_ptr<KGameCanvasPixmap>(new KGameCanvasPixmap(p, this));
142 item->moveTo(converter()->toReal(pt));
143 if(over)
144 item->stackOver(m_pieces_group);
145 else
146 item->stackUnder(m_pieces_group);
147 item->show();
149 (*m_tags)[name][pt] = item;
150 return item;
153 void Board::clearTags(const QString& name) {
154 m_tags->erase(name);
157 void Board::clearTags() {
158 m_tags->clear();
161 void Board::setTags(const QString& name, Point p1, Point p2, Point p3,
162 Point p4, Point p5, Point p6 ) {
163 //TODO: maybe this could be optimized a bit
164 clearTags(name);
165 addTag(name, p1);
166 addTag(name, p2);
167 addTag(name, p3);
168 addTag(name, p4);
169 addTag(name, p5);
170 addTag(name, p6);
173 void Board::recreateBorder() {
174 m_border_text.clear();
175 while(!m_canvas_border_text->items()->isEmpty())
176 delete m_canvas_border_text->items()->first();
178 if(m_border_coords.size() == 0)
179 return;
181 Point s = m_sprites.getSize();
182 for(int w = 0; w<2; w++)
183 for(int i = 0;i<s.x;i++) {
184 int c = w ? i : i+s.x+s.y;
185 QString l = m_border_coords.size()>c ? m_border_coords[c] : QString();
186 ConstrainedText *item = new ConstrainedText(m_canvas_border_text);
187 item->setColor(m_border_text_color);
188 item->setText(l);
189 item->setFont(m_border_font);
190 item->setColor(m_border_text_color);
191 item->show();
192 m_border_text.push_back(item);
195 for(int w = 0; w<2; w++)
196 for(int i = 0;i<s.y;i++) {
197 int c = w ? i+s.x : i+2*s.x+s.y;
198 QString n = m_border_coords.size()>c ? m_border_coords[c] : QString();
199 ConstrainedText *item = new ConstrainedText(m_canvas_border_text);
200 item->setColor(m_border_text_color);
201 item->setText(n);
202 item->setFont(m_border_font);
203 item->setColor(m_border_text_color);
204 item->show();
205 m_border_text.push_back(item);
208 m_pieces_group->raise();
210 updateBorder();
213 void Board::updateBorder() {
214 while(!m_canvas_border->items()->isEmpty())
215 delete m_canvas_border->items()->first();
217 if(!m_square_size)
218 return;
220 int at = 0;
221 for(int w = 0; w<2; w++)
222 for(int i = 0;i<m_sprites.getSize().x;i++) {
223 int x = (m_flipped ? (m_sprites.getSize().x-1-i) : i)*m_square_size;
224 int y = w ? -m_border_text_far : m_square_size*m_sprites.getSize().y+m_border_text_near;
226 m_border_text[at]->setVisible(m_border_text_near != m_border_text_far);
227 m_border_text[at]->setConstrainRect(QRect(x,y,m_square_size,m_border_text_far-m_border_text_near));
228 at++;
231 for(int w = 0; w<2; w++)
232 for(int i = 0;i<m_sprites.getSize().y;i++) {
233 int x = w ? (-m_border_text_far-m_border_text_near)/2
234 : m_square_size*m_sprites.getSize().x + (m_border_text_far+m_border_text_near)/2;
235 int y = (!m_flipped ? (m_sprites.getSize().y-1-i) : i)*m_square_size
236 + (m_square_size-m_border_text_far-m_border_text_near)/2;
238 m_border_text[at]->setVisible(m_border_text_near != m_border_text_far);
239 m_border_text[at]->setConstrainRect(QRect(x-m_square_size/2,y,m_square_size,m_border_text_far-m_border_text_near));
240 at++;
243 ::LuaApi::LuaValueMap params;
244 params["width"] = m_square_size*m_sprites.getSize().x;
245 params["height"] = m_square_size*m_sprites.getSize().y;
246 Loader::PixmapOrMap bord = m_controls_loader.getValue<Loader::PixmapOrMap>("border", &params);
247 if(const QPixmap* p = boost::get<QPixmap>(&bord)) {
248 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(*p, boardRect().size(), QPoint(),
249 true, m_canvas_border);
250 t->show();
252 else if(const Loader::PixmapMap* p = boost::get<Loader::PixmapMap>(&bord)) {
253 for(Loader::PixmapMap::const_iterator it = p->begin(); it != p->end(); ++it) {
254 KGameCanvasTiledPixmap *t = new KGameCanvasTiledPixmap(it->second, it->first.size(),
255 QPoint(), true, m_canvas_border);
256 t->moveTo(it->first.topLeft());
257 t->show();
260 else
261 kError() << "Board::updateBorder(): unexpected type in boost::variant!";
264 void Board::createGrid(Point p, const QStringList& border_coords) {
265 m_border_coords = border_coords;
266 m_sprites = PieceGrid(p.x,p.y);
267 recreateBorder();
270 QPixmap Board::loadSprite(const QString& id) {
271 return m_loader.piecePixmap(id, m_flipped);
274 QRect Board::computeRect(Point p) const {
275 QPoint realPoint = converter()->toReal(p);
276 return squareRect(realPoint.x(), realPoint.y());
279 QRect Board::squareRect(int x, int y) const {
280 return QRect(x, y, m_square_size, m_square_size);
283 QRegion Board::computeRegion(Point p) const {
284 return QRegion(computeRect(p));
287 // selection
288 void Board::setSelection(const Point& p) {
289 lastSelection = selection;
290 selection = p;
291 setTags("selection", p);
294 void Board::cancelSelection() {
295 lastSelection = selection;
296 selection = Point::invalid();
297 clearTags("selection");
300 // premove
302 void Board::setPremove(const NormalUserMove& premove) {
303 m_premove_from = premove.from;
304 m_premove_to = premove.to;
305 setTags("premove", m_premove_from, m_premove_to);
308 void Board::setPremove(const DropUserMove& premove) {
309 m_premove_from = Point::invalid();
310 m_premove_to = premove.to;
311 setTags("premove", m_premove_to);
314 void Board::setPremove(const Premove& premove) {
315 setPremove(premove.toUserMove());
318 void Board::cancelPremove() {
319 m_premove_from = Point::invalid();
320 m_premove_to = Point::invalid();
321 clearTags("premove");
324 void Board::updateSprites() {
325 // adjust piece positions
326 for (Point i = m_sprites.first(); i <= m_sprites.last(); i = m_sprites.next(i)) {
327 boost::shared_ptr<Sprite> p = m_sprites[i].sprite();
329 if (p) {
330 // drawing sprite
331 p->setPixmap(loadSprite(m_sprites[i].name()));
332 adjustSprite(i, true);
337 void Board::updateTags() {
338 if(!m_square_size)
339 return;
341 for(BoardTags::iterator tit = m_tags->begin(); tit != m_tags->end(); ++tit)
342 for(std::map<Point, boost::shared_ptr<KGameCanvasPixmap> >::iterator pt =
343 tit->second.begin(); pt != tit->second.end(); ++pt) {
344 pt->second->moveTo(converter()->toReal(pt->first));
345 pt->second->setPixmap(m_tags_loader.getPixmap(tit->first));
350 bool Board::doMove(const NormalUserMove& m) {
351 if (m_entity.lock()->oneClickMoves() || m_entity.lock()->validTurn(m.from) == Moving) {
352 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
353 if (mv) {
354 m_entity.lock()->executeMove(mv);
355 return true;
359 kDebug() << "invalid move";
360 error(InvalidMove);
362 return false;
366 void Board::onResize(int new_size, int border_size, int border_text_near,
367 int border_text_far, bool force_reload) {
368 if(m_square_size == new_size && !force_reload)
369 return;
371 m_square_size = new_size;
372 m_border_size = border_size;
373 m_border_text_near = border_text_near;
374 m_border_text_far = border_text_far;
376 // update the size of the piece loader
377 m_loader.setSize(m_square_size);
379 // update the size of the tag loader
380 m_tags_loader.setSize(m_square_size);
382 // update the size of the controls loader
383 m_controls_loader.setSize(m_border_size);
385 // update canvas background
386 updateBackground();
388 // update the sprites
389 updateSprites();
391 // update piece tags
392 updateTags();
394 // update border
395 updateBorder();
398 void Board::onMousePress(const QPoint& pos, int button) {
399 Point point = converter()->toLogical(pos);
400 if (!m_sprites.valid(point))
401 return;
403 //BEGIN left click
405 if (button == Qt::LeftButton) {
406 if (m_entity.lock()->oneClickMoves()) {
407 NormalUserMove m = m_entity.lock()->createMove(Point::invalid(), point);
408 doMove(m);
410 else {
411 shared_ptr<Sprite> piece = m_sprites[point].sprite();
413 if (piece && m_entity.lock()->movable(point)) {
414 cancelSelection();
415 m_drag_info = DragInfoPtr(new DragInfo(point, pos, piece,
416 m_entity.lock()->validTurn(point)) );
417 piece->raise();
420 // if selection is valid, (pre)move to point
421 else if (selection != Point::invalid()) {
422 piece = m_sprites[selection].sprite();
423 Q_ASSERT(piece);
424 NormalUserMove m = m_entity.lock()->createMove(selection, point);
426 switch(m_entity.lock()->validTurn(selection)) {
428 case Moving:
429 doMove(m);
430 cancelSelection();
431 break;
433 case Premoving:
434 if (m_entity.lock()->testPremove(m)) {
435 m_entity.lock()->addPremove(m);
436 setPremove(m);
437 cancelSelection();
439 break;
441 default:
442 break;
448 //END left click
450 //BEGIN right click
452 else if (button == Qt::RightButton) {
453 cancelSelection();
454 if (point == m_premove_from || point == m_premove_to)
455 cancelPremove();
456 m_entity.lock()->handleRightClick(point);
459 //END right click
462 void Board::onMouseRelease(const QPoint& pos, int button) {
463 Point point = converter()->toLogical(pos);
465 //BEGIN left click
467 if (button == Qt::LeftButton) {
469 if (m_drag_info) {
470 // Q_ASSERT(m_drag_info->piece);
471 Q_ASSERT(m_drag_info->sprite);
472 bool moved = false;
474 // remove valid move highlighting
475 clearTags("validmove");
477 // toggle selection if the piece didn't move
478 if (m_drag_info->from == point) {
479 if (lastSelection == point)
480 cancelSelection();
481 else
482 setSelection(point);
485 else {
486 NormalUserMove m = m_entity.lock()->createMove(m_drag_info->from, point);
487 if (!m_sprites.valid(point))
488 m.to = Point::invalid();
490 switch(m_entity.lock()->validTurn(m_drag_info->from)) {
492 case Moving:
493 if (doMove(m))
494 moved = true;
495 break;
497 case Premoving:
498 if (m_entity.lock()->testPremove(m)) {
499 m_entity.lock()->addPremove(m);
500 setPremove(m);
502 break;
504 default:
505 break;
509 shared_ptr<Sprite> s = m_sprites[m_drag_info->from].sprite();
510 if (!moved && s && s->pos() != converter()->toReal(m_drag_info->from)) {
511 Q_ASSERT(s);
512 QPoint real = converter()->toReal(m_drag_info->from);
513 if( (point == m_drag_info->from) ? 0/* !m_anim_movement*/ : 0 /* !m_anim_fade*/) //BROKEN
514 enqueue(shared_ptr<Animation>(new InstantAnimation(s, real)));
515 else if (point == m_drag_info->from)
516 enqueue(shared_ptr<Animation>(new MovementAnimation(s, real)));
517 else
518 enqueue(shared_ptr<Animation>(new TeleportAnimation(s, s->pos(), real)));
521 m_drag_info = DragInfoPtr();
525 //END left button
528 void Board::onMouseMove(const QPoint& pos, int /*button*/) {
529 Point point = converter()->toLogical(pos);
531 if (m_drag_info) {
532 Q_ASSERT(m_drag_info->sprite);
533 // check drag threshold
534 if (!m_drag_info->dragStarted) {
535 QPoint delta = pos - m_drag_info->real;
536 if (delta.x() * delta.x() + delta.y() * delta.y() > DragInfo::DRAG_THRESHOLD) {
537 m_drag_info->dragStarted = true;
540 if (m_drag_info->dragStarted)
541 m_drag_info->sprite->moveTo(pos - QPoint(m_square_size / 2, m_square_size / 2) );
543 // highlight valid moves
544 NormalUserMove move = m_entity.lock()->createMove(m_drag_info->from, point);
545 bool valid = m_sprites.valid(point);
546 if (valid) {
547 InteractionType action = m_entity.lock()->validTurn(m_drag_info->from);
548 if (action == Moving)
549 valid = m_entity.lock()->testMove(move);
552 if (valid)
553 setTags("validmove", point);
554 else
555 clearTags("validmove");
557 else if (m_entity.lock()->oneClickMoves()) {
558 if(point == m_hinting_pos)
559 return;
561 AbstractPiece::Ptr hint;
563 if (m_sprites.valid(point)) {
564 if (AbstractMove::Ptr move = m_entity.lock()->testMove(
565 m_entity.lock()->createMove(Point::invalid(), point))) {
566 // set move hint
567 hint = m_entity.lock()->moveHint(move);
571 updateHinting(point, hint);
575 void Board::onPositionChanged() {
576 if (m_entity.lock() && m_entity.lock()->oneClickMoves() && m_sprites.valid(m_hinting_pos)) {
577 AbstractPiece::Ptr hint;
579 if (AbstractMove::Ptr move = m_entity.lock()->testMove(
580 m_entity.lock()->createMove(Point::invalid(), m_hinting_pos))) {
581 // set move hint
582 hint = m_entity.lock()->moveHint(move);
585 updateHinting(m_hinting_pos, hint);
589 void Board::onMouseLeave() {
590 updateHinting(Point::invalid(), AbstractPiece::Ptr());
593 void Board::updateHinting(Point pt, AbstractPiece::Ptr piece) {
594 if(!m_sprites.valid(pt))
595 piece = AbstractPiece::Ptr();
597 if(!piece || !m_sprites.valid(pt)) {
598 if(m_hinting.sprite()) {
599 if(1 /*BROKEN m_anim_fade*/)
600 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
601 m_hinting.sprite()->pos(), 160, 0)) );
602 else
603 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
606 m_hinting_pos = Point::invalid();
607 m_hinting = NamedSprite();
609 else {
610 if(pt == m_hinting_pos) {
611 if(!(piece->name() == m_hinting.name())) {
612 m_hinting = NamedSprite(piece->name(), m_hinting.sprite());
613 m_hinting.sprite()->setPixmap(loadSprite(piece->name()));
616 else {
617 if(m_hinting.sprite()) {
618 if(1 /*BROKEN m_anim_fade*/)
619 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
620 m_hinting.sprite()->pos(), 160, 0)) );
621 else
622 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
625 QPixmap pix = loadSprite(piece->name());
626 SpritePtr sprite(new Sprite(pix, piecesGroup(), converter()->toReal(pt)));
627 sprite->setOpacity(160);
628 sprite->raise();
629 sprite->show();
631 m_hinting_pos = pt;
632 m_hinting = NamedSprite(piece->name(), sprite);
634 /*if(m_anim_fade)
635 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
636 m_hinting.sprite()->pos(), 0, 160)) );
637 else {
638 m_hinting.sprite()->setOpacity(160);
639 enqueue(boost::shared_ptr<Animation>(new DropAnimation(m_hinting.sprite())) );
645 void Board::reset() {
646 clearTags();
647 cancelSelection();
648 cancelPremove();
649 m_main_animation->stop();
652 void Board::flip(bool flipped)
654 if (m_flipped != flipped) {
655 m_flipped = flipped;
657 // update sprite positions
658 for (Point i = m_sprites.first(); i <= m_sprites.last(); i = m_sprites.next(i)) {
659 SpritePtr p = m_sprites[i].sprite();
660 if (p) {
661 p->setPixmap(loadSprite(m_sprites[i].name()));
662 adjustSprite(i, true);
666 updateTags();
667 updateBorder();
671 void Board::draggingOn(int pool, int index, const QPoint& point) {
672 Point to = converter()->toLogical(point);
674 if (m_sprites.valid(to))
675 switch(m_entity.lock()->validTurn(pool)) {
676 case Moving: {
677 DropUserMove m = m_entity.lock()->createDrop(pool, index, to);
678 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
679 if (mv) {
680 setTags("validmove", to);
681 return;
683 break;
686 case Premoving:
687 setTags("validmove", to);
688 return;
690 default:
691 break;
694 clearTags("validmove");
697 bool Board::dropOn(int pool, int index, const QPoint& point) {
699 Point to = converter()->toLogical(point);
700 if (!m_sprites.valid(to))
701 return false;
703 clearTags("validmove");
705 switch(m_entity.lock()->validTurn(pool)) {
707 case Moving: {
708 DropUserMove m = m_entity.lock()->createDrop(pool, index, to);
709 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
710 if (mv) {
711 m_entity.lock()->executeMove(mv);
712 return true;
714 break;
717 case Premoving: {
718 DropUserMove m = m_entity.lock()->createDrop(pool, index, to);
719 if (m_entity.lock()->testPremove(m)) {
720 m_entity.lock()->addPremove(m);
721 setPremove(m);
723 break;
726 default:
727 break;
730 kDebug() << "invalid move";
731 error(InvalidMove);
732 return false;