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.
12 #include <QApplication>
13 #include <QMouseEvent>
16 #include <core/namer.h>
18 #include "animation.h"
19 #include "animationmanager.h"
20 #include "chessboard.h"
21 #include "components.h"
22 #include "constrainedtext.h"
23 #include "entities/userentity.h"
24 #include "mastersettings.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 ChessBoard::ChessBoard(Components
* components
,
34 AnimationManager
* animManager
,
35 KGameCanvasAbstract
* parent
)
36 : ClickableCanvas(parent
)
41 , m_hinting_pos(Point::invalid())
42 , selection(Point::invalid())
43 , lastSelection(Point::invalid())
46 , m_anim_manager(animManager
) {
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 m_namer
= components
->namer();
67 ChessBoard::~ChessBoard() {
68 delete m_pieces_group
;
70 while(!m_canvas_background
->items()->isEmpty())
71 delete m_canvas_background
->items()->first();
72 delete m_canvas_background
;
74 while(!m_canvas_border
->items()->isEmpty())
75 delete m_canvas_border
->items()->first();
76 delete m_canvas_border
;
78 while(!m_canvas_border_text
->items()->isEmpty())
79 delete m_canvas_border_text
->items()->first();
80 delete m_canvas_border_text
;
83 void ChessBoard::settingsChanged() {
84 m_anim_manager
->reload();
85 m_border_text_color
= m_controls_loader
.getStaticValue
<QColor
>("border_color");
86 m_border_font
= m_controls_loader
.getStaticValue
<QFont
>("border_font");
91 void ChessBoard::updateBackground() {
92 while(!m_canvas_background
->items()->isEmpty())
93 delete m_canvas_background
->items()->first();
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
);
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());
113 kError() << "Unexpected type in boost::variant";
116 void ChessBoard::adjustSprite(const Point
& p
, bool immediate
) {
117 SpritePtr sprite
= m_sprites
[p
].sprite();
122 m_anim_manager
->enqueue(
123 1 /*BROKEN m_anim_movement*/ && !immediate
124 ? AnimationPtr(new MovementAnimation(sprite
, converter()->toReal(p
), 1.0))
125 : AnimationPtr(new InstantAnimation(sprite
, converter()->toReal(p
)))
129 boost::shared_ptr
<KGameCanvasPixmap
> ChessBoard::addTag(const QString
& name
, Point pt
, bool over
) {
130 if(!m_sprites
.valid(pt
))
131 return boost::shared_ptr
<KGameCanvasPixmap
>();
133 QPixmap p
= m_tags_loader
.getPixmap(name
);
134 boost::shared_ptr
<KGameCanvasPixmap
> item
=
135 boost::shared_ptr
<KGameCanvasPixmap
>(new KGameCanvasPixmap(p
, this));
136 item
->moveTo(converter()->toReal(pt
));
138 item
->stackOver(m_pieces_group
);
140 item
->stackUnder(m_pieces_group
);
143 (*m_tags
)[name
][pt
] = item
;
147 void ChessBoard::clearTags(const QString
& name
) {
151 void ChessBoard::clearTags() {
155 void ChessBoard::setTags(const QString
& name
, Point p1
, Point p2
, Point p3
,
156 Point p4
, Point p5
, Point p6
) {
157 //TODO: maybe this could be optimized a bit
167 void ChessBoard::recreateBorder() {
168 m_border_text
.clear();
169 while(!m_canvas_border_text
->items()->isEmpty())
170 delete m_canvas_border_text
->items()->first();
172 if(m_border_coords
.size() == 0)
175 Point s
= m_sprites
.getSize();
176 for(int w
= 0; w
<2; w
++)
177 for(int i
= 0;i
<s
.x
;i
++) {
178 int c
= w
? i
: i
+s
.x
+s
.y
;
179 QString l
= m_border_coords
.size()>c
? m_border_coords
[c
] : QString();
180 ConstrainedText
*item
= new ConstrainedText(m_canvas_border_text
); // FIXME[vg]: leaked
181 item
->setColor(m_border_text_color
);
183 item
->setFont(m_border_font
);
184 item
->setColor(m_border_text_color
);
186 m_border_text
.push_back(item
);
189 for(int w
= 0; w
<2; w
++)
190 for(int i
= 0;i
<s
.y
;i
++) {
191 int c
= w
? i
+s
.x
: i
+2*s
.x
+s
.y
;
192 QString n
= m_border_coords
.size()>c
? m_border_coords
[c
] : QString();
193 ConstrainedText
*item
= new ConstrainedText(m_canvas_border_text
); // FIXME[vg]: leaked
194 item
->setColor(m_border_text_color
);
196 item
->setFont(m_border_font
);
197 item
->setColor(m_border_text_color
);
199 m_border_text
.push_back(item
);
202 m_pieces_group
->raise();
207 void ChessBoard::updateBorder() {
208 while(!m_canvas_border
->items()->isEmpty())
209 delete m_canvas_border
->items()->first();
215 for(int w
= 0; w
<2; w
++)
216 for(int i
= 0;i
<m_sprites
.getSize().x
;i
++) {
217 int x
= (m_flipped
? (m_sprites
.getSize().x
-1-i
) : i
)*m_square_size
;
218 int y
= w
? -m_border_text_far
: m_square_size
*m_sprites
.getSize().y
+m_border_text_near
;
220 m_border_text
[at
]->setVisible(m_border_text_near
!= m_border_text_far
);
221 m_border_text
[at
]->setConstrainRect(QRect(x
,y
,m_square_size
,m_border_text_far
-m_border_text_near
));
225 for(int w
= 0; w
<2; w
++)
226 for(int i
= 0;i
<m_sprites
.getSize().y
;i
++) {
227 int x
= w
? (-m_border_text_far
-m_border_text_near
)/2
228 : m_square_size
*m_sprites
.getSize().x
+ (m_border_text_far
+m_border_text_near
)/2;
229 int y
= (!m_flipped
? (m_sprites
.getSize().y
-1-i
) : i
)*m_square_size
230 + (m_square_size
-m_border_text_far
-m_border_text_near
)/2;
232 m_border_text
[at
]->setVisible(m_border_text_near
!= m_border_text_far
);
233 m_border_text
[at
]->setConstrainRect(QRect(x
-m_square_size
/2,y
,m_square_size
,m_border_text_far
-m_border_text_near
));
237 ::LuaApi::LuaValueMap params
;
238 params
["width"] = m_square_size
*m_sprites
.getSize().x
;
239 params
["height"] = m_square_size
*m_sprites
.getSize().y
;
240 Loader::PixmapOrMap bord
= m_controls_loader
.getValue
<Loader::PixmapOrMap
>("border", ¶ms
);
241 if(const QPixmap
* p
= boost::get
<QPixmap
>(&bord
)) {
242 KGameCanvasTiledPixmap
*t
= new KGameCanvasTiledPixmap(*p
, boardRect().size(), QPoint(),
243 true, m_canvas_border
);
246 else if(const Loader::PixmapMap
* p
= boost::get
<Loader::PixmapMap
>(&bord
)) {
247 for(Loader::PixmapMap::const_iterator it
= p
->begin(); it
!= p
->end(); ++it
) {
248 KGameCanvasTiledPixmap
*t
= new KGameCanvasTiledPixmap(it
->second
, it
->first
.size(),
249 QPoint(), true, m_canvas_border
);
250 t
->moveTo(it
->first
.topLeft());
255 kError() << "Unexpected type in boost::variant";
258 void ChessBoard::createGrid(Point p
, const QStringList
& border_coords
) {
259 m_border_coords
= border_coords
;
260 m_sprites
= PieceGrid(p
.x
,p
.y
);
264 QPixmap
ChessBoard::loadSprite(const QString
& id
) {
265 return m_loader
.piecePixmap(id
, m_flipped
);
268 QRect
ChessBoard::computeRect(Point p
) const {
269 QPoint realPoint
= converter()->toReal(p
);
270 return squareRect(realPoint
.x(), realPoint
.y());
273 QRect
ChessBoard::squareRect(int x
, int y
) const {
274 return QRect(x
, y
, m_square_size
, m_square_size
);
277 QRegion
ChessBoard::computeRegion(Point p
) const {
278 return QRegion(computeRect(p
));
282 void ChessBoard::setSelection(const Point
& p
) {
283 lastSelection
= selection
;
285 setTags("selection", p
);
288 void ChessBoard::cancelSelection() {
289 lastSelection
= selection
;
290 selection
= Point::invalid();
291 clearTags("selection");
296 void ChessBoard::setPremove(const Move
& premove
) {
297 m_premove_from
= premove
.src();
298 m_premove_to
= premove
.dst();
299 setTags("premove", m_premove_from
, m_premove_to
);
302 void ChessBoard::cancelPremove() {
303 m_premove_from
= Point::invalid();
304 m_premove_to
= Point::invalid();
305 clearTags("premove");
308 void ChessBoard::updateSprites() {
309 // adjust piece positions
310 for (Point i
= m_sprites
.first(); i
<= m_sprites
.last(); i
= m_sprites
.next(i
)) {
311 boost::shared_ptr
<Sprite
> p
= m_sprites
[i
].sprite();
315 p
->setPixmap(loadSprite(m_sprites
[i
].name()));
316 adjustSprite(i
, true);
321 void ChessBoard::updateTags() {
325 for(BoardTags::iterator tit
= m_tags
->begin(); tit
!= m_tags
->end(); ++tit
)
326 for(std::map
<Point
, boost::shared_ptr
<KGameCanvasPixmap
> >::iterator pt
=
327 tit
->second
.begin(); pt
!= tit
->second
.end(); ++pt
) {
328 pt
->second
->moveTo(converter()->toReal(pt
->first
));
329 pt
->second
->setPixmap(m_tags_loader
.getPixmap(tit
->first
));
334 bool ChessBoard::doMove(Move
& m
) {
335 if (m_entity
.lock()->oneClickMoves() || m_entity
.lock()->validTurn(m
.src()) == Moving
) {
336 if (m_entity
.lock()->testMove(m
)) {
337 m_entity
.lock()->executeMove(m
);
342 kDebug() << "invalid move";
349 void ChessBoard::onResize(int new_size
, int border_size
, int border_text_near
,
350 int border_text_far
, bool force_reload
) {
351 if(m_square_size
== new_size
&& !force_reload
)
354 m_square_size
= new_size
;
355 m_border_size
= border_size
;
356 m_border_text_near
= border_text_near
;
357 m_border_text_far
= border_text_far
;
359 // update the size of the piece loader
360 m_loader
.setSize(m_square_size
);
362 // update the size of the tag loader
363 m_tags_loader
.setSize(m_square_size
);
365 // update the size of the controls loader
366 m_controls_loader
.setSize(m_border_size
);
368 // update canvas background
371 // update the sprites
381 void ChessBoard::onMousePress(const QPoint
& pos
, int button
) {
382 Point point
= converter()->toLogical(pos
);
383 if (!m_sprites
.valid(point
))
388 if (button
== Qt::LeftButton
) {
389 if (m_entity
.lock()->oneClickMoves()) {
390 Move
m(Point::invalid(), point
);
394 shared_ptr
<Sprite
> piece
= m_sprites
[point
].sprite();
396 if (piece
&& m_entity
.lock()->movable(point
)) {
398 m_drag_info
= DragInfoPtr(new DragInfo(point
, pos
, piece
,
399 m_entity
.lock()->validTurn(point
)) );
403 // if selection is valid, (pre)move to point
404 else if (selection
!= Point::invalid()) {
405 piece
= m_sprites
[selection
].sprite();
407 Move
m(selection
, point
);
409 switch(m_entity
.lock()->validTurn(selection
)) {
417 if (m_entity
.lock()->testPremove(m
)) {
418 m_entity
.lock()->addPremove(m
);
435 else if (button
== Qt::RightButton
) {
437 if (point
== m_premove_from
|| point
== m_premove_to
)
439 m_entity
.lock()->handleRightClick(point
);
445 void ChessBoard::onMouseRelease(const QPoint
& pos
, int button
) {
446 Point point
= converter()->toLogical(pos
);
450 if (button
== Qt::LeftButton
) {
453 // Q_ASSERT(m_drag_info->piece);
454 Q_ASSERT(m_drag_info
->sprite
);
457 // remove valid move highlighting
458 clearTags("validmove");
460 // toggle selection if the piece didn't move
461 if (m_drag_info
->from
== point
) {
462 if (lastSelection
== point
)
470 if (!m_sprites
.valid(point
))
471 dst
= Point::invalid();
472 Move
m(m_drag_info
->from
, dst
);
474 switch (m_entity
.lock()->validTurn(m
.src())) {
481 if (m_entity
.lock()->testPremove(m
)) {
482 m_entity
.lock()->addPremove(m
);
492 shared_ptr
<Sprite
> s
= m_sprites
[m_drag_info
->from
].sprite();
493 if (!moved
&& s
&& s
->pos() != converter()->toReal(m_drag_info
->from
)) {
495 QPoint real
= converter()->toReal(m_drag_info
->from
);
496 if( (point
== m_drag_info
->from
) ? 0/* !m_anim_movement*/ : 0 /* !m_anim_fade*/) //BROKEN
497 m_anim_manager
->enqueue(shared_ptr
<Animation
>(new InstantAnimation(s
, real
)));
498 else if (point
== m_drag_info
->from
)
499 m_anim_manager
->enqueue(shared_ptr
<Animation
>(new MovementAnimation(s
, real
)));
501 m_anim_manager
->enqueue(shared_ptr
<Animation
>(new TeleportAnimation(s
, s
->pos(), real
)));
504 m_drag_info
= DragInfoPtr();
511 void ChessBoard::onMouseMove(const QPoint
& pos
, int /*button*/) {
512 Point point
= converter()->toLogical(pos
);
513 static Point cachedPoint
= Point();
514 static bool cachedValid
;
517 Q_ASSERT(m_drag_info
->sprite
);
518 // check drag threshold
519 if (!m_drag_info
->dragStarted
) {
520 QPoint delta
= pos
- m_drag_info
->real
;
521 if (delta
.x() * delta
.x() + delta
.y() * delta
.y() > DragInfo::DRAG_THRESHOLD
) {
522 m_drag_info
->dragStarted
= true;
525 if (m_drag_info
->dragStarted
)
526 m_drag_info
->sprite
->moveTo(pos
- QPoint(m_square_size
/ 2, m_square_size
/ 2) );
528 // highlight valid moves
529 Move
move(m_drag_info
->from
, point
);
530 bool valid
= m_sprites
.valid(point
);
532 InteractionType action
= m_entity
.lock()->validTurn(move
.src());
533 if (action
== Moving
)
534 if (point
== cachedPoint
)
537 valid
= m_entity
.lock()->testMove(move
);
544 setTags("validmove", point
);
546 clearTags("validmove");
548 else if (m_entity
.lock()->oneClickMoves()) {
549 if(point
== m_hinting_pos
)
554 if (m_sprites
.valid(point
)) {
555 Move
move(Point::invalid(), point
);
556 if (m_entity
.lock()->testMove(move
)) {
558 hint
= m_entity
.lock()->moveHint(move
);
562 updateHinting(point
, hint
);
566 void ChessBoard::onPositionChanged() {
567 if (m_entity
.lock() && m_entity
.lock()->oneClickMoves() && m_sprites
.valid(m_hinting_pos
)) {
570 Move
move(Point::invalid(), m_hinting_pos
);
571 if (m_entity
.lock()->testMove(move
)) {
573 hint
= m_entity
.lock()->moveHint(move
);
576 updateHinting(move
.dst(), hint
);
580 void ChessBoard::onMouseLeave() {
581 updateHinting(Point::invalid(), Piece());
584 void ChessBoard::updateHinting(Point pt
, Piece piece
) {
585 if(!m_sprites
.valid(pt
))
589 QString piece_name
= m_namer
->name(piece
);
590 if(piece
!= Piece() || !m_sprites
.valid(pt
)) {
591 if(m_hinting
.sprite()) {
592 if(1 /*BROKEN m_anim_fade*/)
593 m_anim_manager
->enqueue( boost::shared_ptr
<Animation
>(new FadeAnimation(m_hinting
.sprite(),
594 m_hinting
.sprite()->pos(), 160, 0)) );
596 m_anim_manager
->enqueue( boost::shared_ptr
<Animation
>(new CaptureAnimation(m_hinting
.sprite())) );
599 m_hinting_pos
= Point::invalid();
600 m_hinting
= NamedSprite();
603 if(pt
== m_hinting_pos
) {
604 if (piece_name
!= m_hinting
.name()) {
605 m_hinting
= NamedSprite(piece_name
, m_hinting
.sprite());
606 m_hinting
.sprite()->setPixmap(loadSprite(piece_name
));
610 if(m_hinting
.sprite()) {
611 if(1 /*BROKEN m_anim_fade*/)
612 m_anim_manager
->enqueue( boost::shared_ptr
<Animation
>(new FadeAnimation(m_hinting
.sprite(),
613 m_hinting
.sprite()->pos(), 160, 0)) );
615 m_anim_manager
->enqueue( boost::shared_ptr
<Animation
>(new CaptureAnimation(m_hinting
.sprite())) );
618 QPixmap pix
= loadSprite(piece_name
);
619 SpritePtr
sprite(new Sprite(pix
, piecesGroup(), converter()->toReal(pt
)));
620 sprite
->setOpacity(160);
625 m_hinting
= NamedSprite(piece_name
, sprite
);
628 m_anim_manager->enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
629 m_hinting.sprite()->pos(), 0, 160)) );
631 m_hinting.sprite()->setOpacity(160);
632 m_anim_manager->enqueue(boost::shared_ptr<Animation>(new DropAnimation(m_hinting.sprite())) );
638 void ChessBoard::reset() {
642 m_anim_manager
->stop();
645 void ChessBoard::flip(bool flipped
)
647 if (m_flipped
!= flipped
) {
650 // update sprite positions
651 for (Point i
= m_sprites
.first(); i
<= m_sprites
.last(); i
= m_sprites
.next(i
)) {
652 SpritePtr p
= m_sprites
[i
].sprite();
654 p
->setPixmap(loadSprite(m_sprites
[i
].name()));
655 adjustSprite(i
, true);
664 void ChessBoard::draggingOn(const IColor
* pool
, int index
, const QPoint
& point
) {
665 Point to
= converter()->toLogical(point
);
667 if (m_sprites
.valid(to
))
668 switch (m_entity
.lock()->validTurn(pool
)) {
670 Move
m(pool
, index
, to
);
671 if (m_entity
.lock()->testMove(m
)) {
672 setTags("validmove", to
);
679 setTags("validmove", to
);
686 clearTags("validmove");
689 bool ChessBoard::dropOn(const IColor
* pool
, int index
, const QPoint
& point
) {
691 Point to
= converter()->toLogical(point
);
692 if (!m_sprites
.valid(to
))
695 clearTags("validmove");
697 switch (m_entity
.lock()->validTurn(pool
)) {
700 Move
m(pool
, index
, to
);
701 if (m_entity
.lock()->testMove(m
)) {
702 m_entity
.lock()->executeMove(m
);
709 Move
m(pool
, index
, to
);
710 if (m_entity
.lock()->testPremove(m
)) {
711 m_entity
.lock()->addPremove(m
);
721 kDebug() << "invalid move";