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.
13 #include <QApplication>
14 #include <QMouseEvent>
19 #include "animation.h"
20 #include "pointconverter.h"
21 #include "entities/userentity.h"
22 #include "mainanimation.h"
25 using namespace boost
;
27 /** inherit instead of typedef to ease forward declaration :) */
28 class BoardTags
: public std::map
<QString
, std::map
<Point
, boost::shared_ptr
<KGameCanvasPixmap
> > > {
31 Board::Board(KGameCanvasAbstract
* parent
)
34 , m_hinting_pos(Point::invalid())
35 , selection(Point::invalid())
36 , lastSelection(Point::invalid())
38 , m_dropped_index(-1) {
40 m_tags
= BoardTagsPtr(new BoardTags
);
42 m_canvas_background
= new KGameCanvasGroup(this);
43 m_canvas_background
->lower();
44 m_canvas_background
->show();
46 m_pieces_group
= new KGameCanvasGroup(this);
47 m_pieces_group
->show();
53 delete m_pieces_group
;
55 while(!m_canvas_background
->items()->isEmpty())
56 delete m_canvas_background
->items()->first();
57 delete m_canvas_background
;
60 void Board::mySettingsChanged() {
61 Settings s_border
= settings
.group("board-border");
62 m_show_border
= s_border
.flag("visible", true);
63 m_border_color
= (s_border
["color"] |= QColor(Qt::white
));
64 m_border_text_color
= (s_border
["text-color"] |= QColor(Qt::black
));
65 m_border_font
= (s_border
["font"] |= QApplication::font());
70 void Board::settingsChanged() {
71 PieceGroup::settingsChanged();
75 void Board::updateBackground() {
76 while(!m_canvas_background
->items()->isEmpty())
77 delete m_canvas_background
->items()->first();
79 Loader::PixmapOrMap bg
= m_tags_loader
.getPixmapMap("background");
80 if(const QPixmap
* p
= boost::get
<QPixmap
>(&bg
)) {
81 KGameCanvasTiledPixmap
*t
= new KGameCanvasTiledPixmap(*p
, boardRect().size(), QPoint(),
82 true, m_canvas_background
);
85 else if(const Loader::PixmapMap
* p
= boost::get
<Loader::PixmapMap
>(&bg
)) {
86 for(Loader::PixmapMap::const_iterator it
= p
->begin(); it
!= p
->end(); ++it
) {
87 KGameCanvasTiledPixmap
*t
= new KGameCanvasTiledPixmap(it
->second
, it
->first
.size(),
88 QPoint(), true, m_canvas_background
);
89 t
->moveTo(it
->first
.topLeft());
95 boost::shared_ptr
<KGameCanvasPixmap
> Board::addTag(const QString
& name
, Point pt
, bool over
) {
96 if(!m_sprites
.valid(pt
))
97 return boost::shared_ptr
<KGameCanvasPixmap
>();
99 QPixmap p
= m_tags_loader(name
);
100 boost::shared_ptr
<KGameCanvasPixmap
> item
=
101 boost::shared_ptr
<KGameCanvasPixmap
>(new KGameCanvasPixmap(p
, this));
102 item
->moveTo(converter()->toReal(pt
));
104 item
->stackOver(m_pieces_group
);
106 item
->stackUnder(m_pieces_group
);
109 (*m_tags
)[name
][pt
] = item
;
113 void Board::clearTags(const QString
& name
) {
117 void Board::clearTags() {
121 void Board::setTags(const QString
& name
, Point p1
, Point p2
, Point p3
,
122 Point p4
, Point p5
, Point p6
) {
123 //TODO: maybe this could be optimized a bit
133 void Board::recreateBorder() {
134 m_border_margins
.clear();
135 m_border_items
.clear();
137 if(!m_show_border
|| m_border_coords
.size() == 0) {
142 QFontMetrics
fm(m_border_font
);
143 m_border_size
= std::max( fm
.height(), fm
.boundingRect("H").width() );
144 m_border_asc
= fm
.ascent() + (m_border_size
-fm
.height())/2;
146 for(int w
= 0; w
<2; w
++)
147 for(int i
=0;i
<4;i
++) {
148 KGameCanvasRectangle
*item
= new KGameCanvasRectangle(
149 w
? m_border_text_color
: m_border_color
, QSize(), this);
151 m_border_margins
.push_back( boost::shared_ptr
<KGameCanvasRectangle
>( item
));
154 Point s
= m_sprites
.getSize();
155 for(int w
= 0; w
<2; w
++)
156 for(int i
= 0;i
<s
.x
;i
++) {
157 int c
= w
? i
: i
+s
.x
+s
.y
;
158 QString l
= m_border_coords
.size()>c
? m_border_coords
[c
] : QString();
159 KGameCanvasItem
*item
= new KGameCanvasText( l
, m_border_text_color
, m_border_font
,
160 KGameCanvasText::HCenter
, KGameCanvasText::VBaseline
, this);
162 m_border_items
.push_back( boost::shared_ptr
<KGameCanvasItem
>( item
));
165 for(int w
= 0; w
<2; w
++)
166 for(int i
= 0;i
<s
.y
;i
++) {
167 int c
= w
? i
+s
.x
: i
+2*s
.x
+s
.y
;
168 QString n
= m_border_coords
.size()>c
? m_border_coords
[c
] : QString();
169 KGameCanvasItem
*item
= new KGameCanvasText( n
, m_border_text_color
, m_border_font
,
170 KGameCanvasText::HCenter
, KGameCanvasText::VBaseline
, this);
172 m_border_items
.push_back( boost::shared_ptr
<KGameCanvasItem
>( item
));
175 m_pieces_group
->raise();
180 void Board::updateBorder() {
185 for(int w
= 0; w
<2; w
++)
186 for(int i
= 0;i
<m_sprites
.getSize().x
;i
++) {
187 int x
= (m_flipped
? (m_sprites
.getSize().x
-1-i
):i
)*m_square_size
+m_square_size
/2;
188 int y
= m_border_asc
+(w
?0:m_border_size
+m_square_size
*m_sprites
.getSize().y
)-m_border_size
;
189 m_border_items
[at
++]->moveTo(x
, y
);
192 for(int w
= 0; w
<2; w
++)
193 for(int i
= 0;i
<m_sprites
.getSize().y
;i
++) {
194 int x
= -m_border_size
/2+(w
?0:m_border_size
+m_square_size
*m_sprites
.getSize().x
);
195 int y
= (!m_flipped
? (m_sprites
.getSize().y
-1-i
):i
)*m_square_size
196 +m_square_size
/2-m_border_size
/2+m_border_asc
;
197 m_border_items
[at
++]->moveTo(x
, y
);
200 m_border_margins
[0]->moveTo(-m_border_size
,-m_border_size
);
201 m_border_margins
[0]->setSize(QSize(2*m_border_size
+m_square_size
*m_sprites
.getSize().x
,
203 m_border_margins
[4]->moveTo(-m_border_size
-1,-m_border_size
-1);
204 m_border_margins
[4]->setSize(QSize(2*m_border_size
+m_square_size
*m_sprites
.getSize().x
+2, 1));
206 m_border_margins
[1]->moveTo(-m_border_size
,m_square_size
*m_sprites
.getSize().y
);
207 m_border_margins
[1]->setSize(QSize(2*m_border_size
+m_square_size
*m_sprites
.getSize().x
,
209 m_border_margins
[5]->moveTo(-m_border_size
-1,m_square_size
*m_sprites
.getSize().y
+m_border_size
);
210 m_border_margins
[5]->setSize(QSize(2*m_border_size
+m_square_size
*m_sprites
.getSize().x
+2, 1));
212 m_border_margins
[2]->moveTo(-m_border_size
,0);
213 m_border_margins
[2]->setSize(QSize(m_border_size
, m_square_size
*m_sprites
.getSize().y
));
214 m_border_margins
[6]->moveTo(-m_border_size
-1,-m_border_size
);
215 m_border_margins
[6]->setSize(QSize(1, m_square_size
*m_sprites
.getSize().y
+2*m_border_size
));
217 m_border_margins
[3]->moveTo(m_square_size
*m_sprites
.getSize().x
, 0);
218 m_border_margins
[3]->setSize(QSize(m_border_size
, m_square_size
*m_sprites
.getSize().y
));
219 m_border_margins
[7]->moveTo(m_square_size
*m_sprites
.getSize().x
+m_border_size
, -m_border_size
);
220 m_border_margins
[7]->setSize(QSize(1, m_square_size
*m_sprites
.getSize().y
+2*m_border_size
));
223 void Board::createGrid(Point p
, const QStringList
& border_coords
) {
224 m_border_coords
= border_coords
;
225 m_sprites
= PieceGrid(p
.x
,p
.y
);
229 QRect
Board::computeRect(Point p
) const {
230 QPoint realPoint
= converter()->toReal(p
);
231 return squareRect(realPoint
.x(), realPoint
.y());
234 QRect
Board::squareRect(int x
, int y
) const {
235 return QRect(x
, y
, m_square_size
, m_square_size
);
238 QRegion
Board::computeRegion(Point p
) const {
239 return QRegion(computeRect(p
));
243 void Board::setSelection(const Point
& p
) {
244 lastSelection
= selection
;
246 setTags("selection", p
);
249 void Board::cancelSelection() {
250 lastSelection
= selection
;
251 selection
= Point::invalid();
252 clearTags("selection");
257 void Board::setPremove(const NormalUserMove
& premove
) {
258 m_premove_from
= premove
.from
;
259 m_premove_to
= premove
.to
;
260 setTags("premove", m_premove_from
, m_premove_to
);
263 void Board::setPremove(const DropUserMove
& premove
) {
264 m_premove_from
= Point::invalid();
265 m_premove_to
= premove
.m_to
;
266 setTags("premove", m_premove_to
);
269 void Board::setPremove(const Premove
& premove
) {
270 setPremove(premove
.toUserMove());
273 void Board::cancelPremove() {
274 m_premove_from
= Point::invalid();
275 m_premove_to
= Point::invalid();
276 clearTags("premove");
279 void Board::updateSprites() {
280 // adjust piece positions
281 for (Point i
= m_sprites
.first(); i
<= m_sprites
.last(); i
= m_sprites
.next(i
)) {
282 boost::shared_ptr
<Sprite
> p
= m_sprites
[i
].sprite();
286 p
->setPixmap( m_loader( m_sprites
[i
].name() ) );
292 void Board::updateTags() {
293 for(BoardTags::iterator tit
= m_tags
->begin(); tit
!= m_tags
->end(); ++tit
)
294 for(std::map
<Point
, boost::shared_ptr
<KGameCanvasPixmap
> >::iterator pt
=
295 tit
->second
.begin(); pt
!= tit
->second
.end(); ++pt
) {
296 pt
->second
->moveTo(converter()->toReal(pt
->first
));
297 pt
->second
->setPixmap(m_tags_loader(tit
->first
));
302 bool Board::doMove(const NormalUserMove
& m
) {
303 if (m_entity
.lock()->oneClickMoves() || m_entity
.lock()->validTurn(m
.from
) == Moving
) {
304 AbstractMove::Ptr mv
= m_entity
.lock()->testMove(m
);
306 m_entity
.lock()->executeMove(mv
);
311 std::cout
<< "invalid move" << std::endl
;
312 emit
error(InvalidMove
);
318 void Board::onResize(int new_size
, bool force_reload
) {
319 if(m_square_size
== new_size
&& !force_reload
)
322 PieceGroup::onResize(new_size
);
324 // update the size of the tag loader
325 m_tags_loader
.setSize(m_square_size
);
327 // update canvas background
330 // update the sprites
340 void Board::onMousePress(const QPoint
& pos
, int button
) {
341 Point point
= converter()->toLogical(pos
);
342 if (!m_sprites
.valid(point
))
347 if (button
== Qt::LeftButton
) {
348 if (m_entity
.lock()->oneClickMoves()) {
349 NormalUserMove
m(Point::invalid(), point
);
350 m_entity
.lock()->setPromotion(m
);
354 shared_ptr
<Sprite
> piece
= m_sprites
[point
].sprite();
356 if (piece
&& m_entity
.lock()->movable(point
)) {
358 m_drag_info
= DragInfoPtr(new DragInfo(point
, pos
, piece
,
359 m_entity
.lock()->validTurn(point
)) );
363 // if selection is valid, (pre)move to point
364 else if (selection
!= Point::invalid()) {
365 piece
= m_sprites
[selection
].sprite();
367 NormalUserMove
m(selection
, point
);
368 m_entity
.lock()->setPromotion(m
);
370 switch(m_entity
.lock()->validTurn(selection
)) {
378 if (m_entity
.lock()->testPremove(m
)) {
379 m_entity
.lock()->addPremove(m
);
396 else if (button
== Qt::RightButton
) {
398 if (point
== m_premove_from
|| point
== m_premove_to
)
400 m_entity
.lock()->handleRightClick(point
);
406 void Board::onMouseRelease(const QPoint
& pos
, int button
) {
407 Point point
= converter()->toLogical(pos
);
411 if (button
== Qt::LeftButton
) {
414 // Q_ASSERT(m_drag_info->piece);
415 Q_ASSERT(m_drag_info
->sprite
);
418 // remove valid move highlighting
419 clearTags("validmove");
421 // toggle selection if the piece didn't move
422 if (m_drag_info
->from
== point
) {
423 if (lastSelection
== point
)
430 NormalUserMove
m(m_drag_info
->from
, point
, true);
431 if (!m_sprites
.valid(point
))
432 m
.to
= Point::invalid();
434 m_entity
.lock()->setPromotion(m
);
436 switch(m_entity
.lock()->validTurn(m_drag_info
->from
)) {
444 if (m_entity
.lock()->testPremove(m
)) {
445 m_entity
.lock()->addPremove(m
);
455 shared_ptr
<Sprite
> s
= m_sprites
[m_drag_info
->from
].sprite();
456 if (!moved
&& s
&& s
->pos() != converter()->toReal(m_drag_info
->from
)) {
458 QPoint real
= converter()->toReal(m_drag_info
->from
);
459 if( (point
== m_drag_info
->from
) ? !m_anim_movement
: !m_anim_fade
)
460 enqueue(shared_ptr
<Animation
>(new InstantAnimation(s
, real
)));
461 else if (point
== m_drag_info
->from
)
462 enqueue(shared_ptr
<Animation
>(new MovementAnimation(s
, real
)));
464 enqueue(shared_ptr
<Animation
>(new TeleportAnimation(s
, s
->pos(), real
)));
467 m_drag_info
= DragInfoPtr();
474 void Board::onMouseMove(const QPoint
& pos
, int /*button*/) {
475 Point point
= converter()->toLogical(pos
);
478 Q_ASSERT(m_drag_info
->sprite
);
479 // check drag threshold
480 if (!m_drag_info
->dragStarted
) {
481 QPoint delta
= pos
- m_drag_info
->real
;
482 if (delta
.x() * delta
.x() + delta
.y() * delta
.y() > DragInfo::DRAG_THRESHOLD
) {
483 m_drag_info
->dragStarted
= true;
486 if (m_drag_info
->dragStarted
)
487 m_drag_info
->sprite
->moveTo(pos
- QPoint(m_square_size
/ 2, m_square_size
/ 2) );
489 // highlight valid moves
490 NormalUserMove
move(m_drag_info
->from
, point
);
491 bool valid
= m_sprites
.valid(point
);
493 InteractionType action
= m_entity
.lock()->validTurn(m_drag_info
->from
);
494 if (action
== Moving
)
495 valid
= m_entity
.lock()->testMove(move
);
499 setTags("validmove", point
);
501 clearTags("validmove");
503 else if (m_entity
.lock()->oneClickMoves()) {
504 if(point
== m_hinting_pos
)
507 AbstractPiece::Ptr hint
;
509 if (m_sprites
.valid(point
)) {
510 if (AbstractMove::Ptr move
= m_entity
.lock()->testMove(
511 NormalUserMove(Point::invalid(), point
))) {
513 hint
= m_entity
.lock()->moveHint(move
);
517 updateHinting(point
, hint
);
521 void Board::onPositionChanged() {
522 if (m_entity
.lock() && m_entity
.lock()->oneClickMoves() && m_sprites
.valid(m_hinting_pos
)) {
523 AbstractPiece::Ptr hint
;
525 if (AbstractMove::Ptr move
= m_entity
.lock()->testMove(
526 NormalUserMove(Point::invalid(), m_hinting_pos
)) ) {
528 hint
= m_entity
.lock()->moveHint(move
);
531 updateHinting(m_hinting_pos
, hint
);
535 void Board::onMouseLeave() {
536 updateHinting(Point::invalid(), AbstractPiece::Ptr());
539 void Board::updateHinting(Point pt
, AbstractPiece::Ptr piece
) {
540 if(!m_sprites
.valid(pt
))
541 piece
= AbstractPiece::Ptr();
543 if(!piece
|| !m_sprites
.valid(pt
)) {
544 if(m_hinting
.sprite()) {
546 enqueue( boost::shared_ptr
<Animation
>(new FadeAnimation(m_hinting
.sprite(),
547 m_hinting
.sprite()->pos(), 160, 0)) );
549 enqueue( boost::shared_ptr
<Animation
>(new CaptureAnimation(m_hinting
.sprite())) );
552 m_hinting_pos
= Point::invalid();
553 m_hinting
= NamedSprite();
556 if(pt
== m_hinting_pos
) {
557 if(!(piece
->name() == m_hinting
.name())) {
558 m_hinting
= NamedSprite(piece
->name(), m_hinting
.sprite());
559 m_hinting
.sprite()->setPixmap(m_loader(piece
->name()));
563 if(m_hinting
.sprite()) {
565 enqueue( boost::shared_ptr
<Animation
>(new FadeAnimation(m_hinting
.sprite(),
566 m_hinting
.sprite()->pos(), 160, 0)) );
568 enqueue( boost::shared_ptr
<Animation
>(new CaptureAnimation(m_hinting
.sprite())) );
571 QPixmap pix
= m_loader(piece
->name());
572 boost::shared_ptr
<Sprite
> sprite
= createSprite(pix
, pt
);
573 sprite
->setOpacity(160);
578 m_hinting
= NamedSprite(piece
->name(), sprite
);
581 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
582 m_hinting.sprite()->pos(), 0, 160)) );
584 m_hinting.sprite()->setOpacity(160);
585 enqueue(boost::shared_ptr<Animation>(new DropAnimation(m_hinting.sprite())) );
591 void Board::reset() {
595 m_main_animation
->stop();
598 void Board::flip(bool flipped
)
600 if (m_flipped
!= flipped
) {
603 // update sprite positions
604 for (Point i
= m_sprites
.first(); i
<= m_sprites
.last(); i
= m_sprites
.next(i
))
605 if (m_sprites
[i
].sprite())
606 animatePiece(m_sprites
[i
].sprite(), i
, 1.0);
613 void Board::draggingOn(int pool
, int index
, const QPoint
& point
) {
614 Point to
= converter()->toLogical(point
);
618 if (m_sprites
.valid(to
))
619 switch(m_entity
.lock()->validTurn(piece
->color())) {
621 DropUserMove
m(piece
, to
);
622 AbstractMove::Ptr mv
= m_entity
.lock()->testMove(m
);
624 setTags("validmove", to
);
631 setTags("validmove", to
);
639 clearTags("validmove");
642 bool Board::dropOn(int pool
, int index
, const QPoint
& point
) {
644 Point to
= converter()->toLogical(point
);
645 if (!m_sprites
.valid(to
))
648 clearTags("validmove");
650 switch(m_entity
.lock()->validTurn(pool
)) {
653 DropUserMove
m(pool
, index
, to
);
654 AbstractMove::Ptr mv
= m_entity
.lock()->testMove(m
);
656 m_entity
.lock()->executeMove(mv
);
663 DropUserMove
m(pool
, index
, to
);
664 if (m_entity
.lock()->testPremove(m
)) {
665 m_entity
.lock()->addPremove(m
);
675 std::cout
<< "invalid move" << std::endl
;
676 emit
error(InvalidMove
);