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.
14 #include <QApplication>
15 #include <QMouseEvent>
18 #include "mastersettings.h"
21 #include "animation.h"
22 #include "pointconverter.h"
23 #include "entities/userentity.h"
24 #include "mainanimation.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
)
40 , m_hinting_pos(Point::invalid())
41 , selection(Point::invalid())
42 , lastSelection(Point::invalid())
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();
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");
93 void Board::updateBackground() {
94 while(!m_canvas_background
->items()->isEmpty())
95 delete m_canvas_background
->items()->first();
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
);
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());
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();
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
));
144 item
->stackOver(m_pieces_group
);
146 item
->stackUnder(m_pieces_group
);
149 (*m_tags
)[name
][pt
] = item
;
153 void Board::clearTags(const QString
& name
) {
157 void Board::clearTags() {
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
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)
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
);
189 item
->setFont(m_border_font
);
190 item
->setColor(m_border_text_color
);
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
);
202 item
->setFont(m_border_font
);
203 item
->setColor(m_border_text_color
);
205 m_border_text
.push_back(item
);
208 m_pieces_group
->raise();
213 void Board::updateBorder() {
214 while(!m_canvas_border
->items()->isEmpty())
215 delete m_canvas_border
->items()->first();
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
));
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
));
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", ¶ms
);
247 if(const QPixmap
* p
= boost::get
<QPixmap
>(&bord
)) {
248 KGameCanvasTiledPixmap
*t
= new KGameCanvasTiledPixmap(*p
, boardRect().size(), QPoint(),
249 true, m_canvas_border
);
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());
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
);
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
));
288 void Board::setSelection(const Point
& p
) {
289 lastSelection
= selection
;
291 setTags("selection", p
);
294 void Board::cancelSelection() {
295 lastSelection
= selection
;
296 selection
= Point::invalid();
297 clearTags("selection");
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();
331 p
->setPixmap(loadSprite(m_sprites
[i
].name()));
332 adjustSprite(i
, true);
337 void Board::updateTags() {
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
);
354 m_entity
.lock()->executeMove(mv
);
359 kDebug() << "invalid move";
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
)
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
388 // update the sprites
398 void Board::onMousePress(const QPoint
& pos
, int button
) {
399 Point point
= converter()->toLogical(pos
);
400 if (!m_sprites
.valid(point
))
405 if (button
== Qt::LeftButton
) {
406 if (m_entity
.lock()->oneClickMoves()) {
407 NormalUserMove m
= m_entity
.lock()->createMove(Point::invalid(), point
);
411 shared_ptr
<Sprite
> piece
= m_sprites
[point
].sprite();
413 if (piece
&& m_entity
.lock()->movable(point
)) {
415 m_drag_info
= DragInfoPtr(new DragInfo(point
, pos
, piece
,
416 m_entity
.lock()->validTurn(point
)) );
420 // if selection is valid, (pre)move to point
421 else if (selection
!= Point::invalid()) {
422 piece
= m_sprites
[selection
].sprite();
424 NormalUserMove m
= m_entity
.lock()->createMove(selection
, point
);
426 switch(m_entity
.lock()->validTurn(selection
)) {
434 if (m_entity
.lock()->testPremove(m
)) {
435 m_entity
.lock()->addPremove(m
);
452 else if (button
== Qt::RightButton
) {
454 if (point
== m_premove_from
|| point
== m_premove_to
)
456 m_entity
.lock()->handleRightClick(point
);
462 void Board::onMouseRelease(const QPoint
& pos
, int button
) {
463 Point point
= converter()->toLogical(pos
);
467 if (button
== Qt::LeftButton
) {
470 // Q_ASSERT(m_drag_info->piece);
471 Q_ASSERT(m_drag_info
->sprite
);
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
)
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
)) {
498 if (m_entity
.lock()->testPremove(m
)) {
499 m_entity
.lock()->addPremove(m
);
509 shared_ptr
<Sprite
> s
= m_sprites
[m_drag_info
->from
].sprite();
510 if (!moved
&& s
&& s
->pos() != converter()->toReal(m_drag_info
->from
)) {
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
)));
518 enqueue(shared_ptr
<Animation
>(new TeleportAnimation(s
, s
->pos(), real
)));
521 m_drag_info
= DragInfoPtr();
528 void Board::onMouseMove(const QPoint
& pos
, int /*button*/) {
529 Point point
= converter()->toLogical(pos
);
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
);
547 InteractionType action
= m_entity
.lock()->validTurn(m_drag_info
->from
);
548 if (action
== Moving
)
549 valid
= m_entity
.lock()->testMove(move
);
553 setTags("validmove", point
);
555 clearTags("validmove");
557 else if (m_entity
.lock()->oneClickMoves()) {
558 if(point
== m_hinting_pos
)
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
))) {
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
))) {
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)) );
603 enqueue( boost::shared_ptr
<Animation
>(new CaptureAnimation(m_hinting
.sprite())) );
606 m_hinting_pos
= Point::invalid();
607 m_hinting
= NamedSprite();
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()));
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)) );
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);
632 m_hinting
= NamedSprite(piece
->name(), sprite
);
635 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
636 m_hinting.sprite()->pos(), 0, 160)) );
638 m_hinting.sprite()->setOpacity(160);
639 enqueue(boost::shared_ptr<Animation>(new DropAnimation(m_hinting.sprite())) );
645 void Board::reset() {
649 m_main_animation
->stop();
652 void Board::flip(bool flipped
)
654 if (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();
661 p
->setPixmap(loadSprite(m_sprites
[i
].name()));
662 adjustSprite(i
, true);
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
)) {
677 DropUserMove m
= m_entity
.lock()->createDrop(pool
, index
, to
);
678 AbstractMove::Ptr mv
= m_entity
.lock()->testMove(m
);
680 setTags("validmove", to
);
687 setTags("validmove", to
);
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
))
703 clearTags("validmove");
705 switch(m_entity
.lock()->validTurn(pool
)) {
708 DropUserMove m
= m_entity
.lock()->createDrop(pool
, index
, to
);
709 AbstractMove::Ptr mv
= m_entity
.lock()->testMove(m
);
711 m_entity
.lock()->executeMove(mv
);
718 DropUserMove m
= m_entity
.lock()->createDrop(pool
, index
, to
);
719 if (m_entity
.lock()->testPremove(m
)) {
720 m_entity
.lock()->addPremove(m
);
730 kDebug() << "invalid move";