Removed unimplemented GUI actions.
[tagua/yd.git] / src / board.cpp
blobe66997c415c96f08ca47db388cb319e398aa54ce
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>
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.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 = m_entity.lock()->createMove(Point::invalid(), point);
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 = m_entity.lock()->createMove(selection, point);
417 switch(m_entity.lock()->validTurn(selection)) {
419 case Moving:
420 doMove(m);
421 cancelSelection();
422 break;
424 case Premoving:
425 if (m_entity.lock()->testPremove(m)) {
426 m_entity.lock()->addPremove(m);
427 setPremove(m);
428 cancelSelection();
430 break;
432 default:
433 break;
439 //END left click
441 //BEGIN right click
443 else if (button == Qt::RightButton) {
444 cancelSelection();
445 if (point == m_premove_from || point == m_premove_to)
446 cancelPremove();
447 m_entity.lock()->handleRightClick(point);
450 //END right click
453 void Board::onMouseRelease(const QPoint& pos, int button) {
454 Point point = converter()->toLogical(pos);
456 //BEGIN left click
458 if (button == Qt::LeftButton) {
460 if (m_drag_info) {
461 // Q_ASSERT(m_drag_info->piece);
462 Q_ASSERT(m_drag_info->sprite);
463 bool moved = false;
465 // remove valid move highlighting
466 clearTags("validmove");
468 // toggle selection if the piece didn't move
469 if (m_drag_info->from == point) {
470 if (lastSelection == point)
471 cancelSelection();
472 else
473 setSelection(point);
476 else {
477 NormalUserMove m = m_entity.lock()->createMove(m_drag_info->from, point);
478 if (!m_sprites.valid(point))
479 m.to = Point::invalid();
481 switch(m_entity.lock()->validTurn(m_drag_info->from)) {
483 case Moving:
484 if (doMove(m))
485 moved = true;
486 break;
488 case Premoving:
489 if (m_entity.lock()->testPremove(m)) {
490 m_entity.lock()->addPremove(m);
491 setPremove(m);
493 break;
495 default:
496 break;
500 shared_ptr<Sprite> s = m_sprites[m_drag_info->from].sprite();
501 if (!moved && s && s->pos() != converter()->toReal(m_drag_info->from)) {
502 Q_ASSERT(s);
503 QPoint real = converter()->toReal(m_drag_info->from);
504 if( (point == m_drag_info->from) ? 0/* !m_anim_movement*/ : 0 /* !m_anim_fade*/) //BROKEN
505 enqueue(shared_ptr<Animation>(new InstantAnimation(s, real)));
506 else if (point == m_drag_info->from)
507 enqueue(shared_ptr<Animation>(new MovementAnimation(s, real)));
508 else
509 enqueue(shared_ptr<Animation>(new TeleportAnimation(s, s->pos(), real)));
512 m_drag_info = DragInfoPtr();
516 //END left button
519 void Board::onMouseMove(const QPoint& pos, int /*button*/) {
520 Point point = converter()->toLogical(pos);
522 if (m_drag_info) {
523 Q_ASSERT(m_drag_info->sprite);
524 // check drag threshold
525 if (!m_drag_info->dragStarted) {
526 QPoint delta = pos - m_drag_info->real;
527 if (delta.x() * delta.x() + delta.y() * delta.y() > DragInfo::DRAG_THRESHOLD) {
528 m_drag_info->dragStarted = true;
531 if (m_drag_info->dragStarted)
532 m_drag_info->sprite->moveTo(pos - QPoint(m_square_size / 2, m_square_size / 2) );
534 // highlight valid moves
535 NormalUserMove move = m_entity.lock()->createMove(m_drag_info->from, point);
536 bool valid = m_sprites.valid(point);
537 if (valid) {
538 InteractionType action = m_entity.lock()->validTurn(m_drag_info->from);
539 if (action == Moving)
540 valid = m_entity.lock()->testMove(move);
543 if (valid)
544 setTags("validmove", point);
545 else
546 clearTags("validmove");
548 else if (m_entity.lock()->oneClickMoves()) {
549 if(point == m_hinting_pos)
550 return;
552 AbstractPiece::Ptr hint;
554 if (m_sprites.valid(point)) {
555 if (AbstractMove::Ptr move = m_entity.lock()->testMove(
556 m_entity.lock()->createMove(Point::invalid(), point))) {
557 // set move hint
558 hint = m_entity.lock()->moveHint(move);
562 updateHinting(point, hint);
566 void Board::onPositionChanged() {
567 if (m_entity.lock() && m_entity.lock()->oneClickMoves() && m_sprites.valid(m_hinting_pos)) {
568 AbstractPiece::Ptr hint;
570 if (AbstractMove::Ptr move = m_entity.lock()->testMove(
571 m_entity.lock()->createMove(Point::invalid(), m_hinting_pos))) {
572 // set move hint
573 hint = m_entity.lock()->moveHint(move);
576 updateHinting(m_hinting_pos, hint);
580 void Board::onMouseLeave() {
581 updateHinting(Point::invalid(), AbstractPiece::Ptr());
584 void Board::updateHinting(Point pt, AbstractPiece::Ptr piece) {
585 if(!m_sprites.valid(pt))
586 piece = AbstractPiece::Ptr();
588 if(!piece || !m_sprites.valid(pt)) {
589 if(m_hinting.sprite()) {
590 if(1 /*BROKEN m_anim_fade*/)
591 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
592 m_hinting.sprite()->pos(), 160, 0)) );
593 else
594 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
597 m_hinting_pos = Point::invalid();
598 m_hinting = NamedSprite();
600 else {
601 if(pt == m_hinting_pos) {
602 if(!(piece->name() == m_hinting.name())) {
603 m_hinting = NamedSprite(piece->name(), m_hinting.sprite());
604 m_hinting.sprite()->setPixmap(m_loader(piece->name()));
607 else {
608 if(m_hinting.sprite()) {
609 if(1 /*BROKEN m_anim_fade*/)
610 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
611 m_hinting.sprite()->pos(), 160, 0)) );
612 else
613 enqueue( boost::shared_ptr<Animation>(new CaptureAnimation(m_hinting.sprite())) );
616 QPixmap pix = m_loader(piece->name());
617 SpritePtr sprite(new Sprite(pix, piecesGroup(), converter()->toReal(pt)));
618 sprite->setOpacity(160);
619 sprite->raise();
620 sprite->show();
622 m_hinting_pos = pt;
623 m_hinting = NamedSprite(piece->name(), sprite);
625 /*if(m_anim_fade)
626 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
627 m_hinting.sprite()->pos(), 0, 160)) );
628 else {
629 m_hinting.sprite()->setOpacity(160);
630 enqueue(boost::shared_ptr<Animation>(new DropAnimation(m_hinting.sprite())) );
636 void Board::reset() {
637 clearTags();
638 cancelSelection();
639 cancelPremove();
640 m_main_animation->stop();
643 void Board::flip(bool flipped)
645 if (m_flipped != flipped) {
646 m_flipped = flipped;
648 // update sprite positions
649 for (Point i = m_sprites.first(); i <= m_sprites.last(); i = m_sprites.next(i))
650 if (m_sprites[i].sprite())
651 adjustSprite(i);
653 updateTags();
654 updateBorder();
658 void Board::draggingOn(int pool, int index, const QPoint& point) {
659 Point to = converter()->toLogical(point);
661 if (m_sprites.valid(to))
662 switch(m_entity.lock()->validTurn(pool)) {
663 case Moving: {
664 DropUserMove m = m_entity.lock()->createDrop(pool, index, to);
665 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
666 if (mv) {
667 setTags("validmove", to);
668 return;
670 break;
673 case Premoving:
674 setTags("validmove", to);
675 return;
677 default:
678 break;
681 clearTags("validmove");
684 bool Board::dropOn(int pool, int index, const QPoint& point) {
686 Point to = converter()->toLogical(point);
687 if (!m_sprites.valid(to))
688 return false;
690 clearTags("validmove");
692 switch(m_entity.lock()->validTurn(pool)) {
694 case Moving: {
695 DropUserMove m = m_entity.lock()->createDrop(pool, index, to);
696 AbstractMove::Ptr mv = m_entity.lock()->testMove(m);
697 if (mv) {
698 m_entity.lock()->executeMove(mv);
699 return true;
701 break;
704 case Premoving: {
705 DropUserMove m = m_entity.lock()->createDrop(pool, index, to);
706 if (m_entity.lock()->testPremove(m)) {
707 m_entity.lock()->addPremove(m);
708 setPremove(m);
710 break;
713 default:
714 break;
717 std::cout << "invalid move" << std::endl;
718 error(InvalidMove);
719 return false;