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.
16 #include <boost/random/uniform_smallint.hpp>
19 #include <core/point.h>
23 using namespace boost
;
25 struct ExplosionFragment
{
29 ExplosionFragment() : m_pcount(0) {}
32 /* inherit instead of typedef to ease forward declaration :) */
33 class SpriteExplosion
: public std::vector
<ExplosionFragment
> {
37 Sprite::Sprite(const QPixmap
& pix
, KGameCanvasAbstract
* canvas
,
38 const QPoint
& location
)
39 : KGameCanvasPixmap(pix
, canvas
)
46 , m_dummy_opacity(255)
47 , m_dummy_visible(false)
63 Sprite
* Sprite::duplicate() const {
64 return new Sprite(pixmap(), canvas(), pos() );
67 void Sprite::setThumb(const QImage
& thumb
) {
68 kDebug() << "setting thumb";
69 QPixmap pix
= m_pixmap
;
70 int width
= pix
.width() / 2;
71 int height
= pix
.height() / 2;
72 QPixmap thumb_pix
= QPixmap::fromImage(
73 thumb
.scaled(width
, height
,
74 Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
));
77 QPainter
painter(&pix
);
78 painter
.drawPixmap(pix
.width() - width
, 0, thumb_pix
);
81 KGameCanvasPixmap::setPixmap(pix
);
84 void Sprite::removeThumb() {
85 KGameCanvasPixmap::setPixmap(m_pixmap
);
88 void Sprite::setPixmap(const QPixmap
& pix
) {
90 KGameCanvasPixmap::setPixmap(pix
);
93 void Sprite::setMovementAnimation(const shared_ptr
<Animation
>& animation
) {
94 m_movement_animation
= animation
;
97 weak_ptr
<Animation
> Sprite::movementAnimation() const {
98 return m_movement_animation
;
101 void Sprite::setFadeAnimation(const shared_ptr<FadeAnimation>& animation) {
102 m_fade_animation = animation;
105 weak_ptr<FadeAnimation> Sprite::fadeAnimation() const {
106 return m_fade_animation;
109 void Sprite::setExplosionStep(float f
) {
114 void Sprite::setupExplosion(Random
& random
) {
119 m_explosion
= createExplosion(random
);
123 * this function generate many polygons that fill the image, + their random speeds
125 * Split points are some random point on the border (ordered accordin to the angle).
126 * The generated polygons are created cutting from each split point to (according
127 * to random's will) the center -or- a point in the mid way from the center to the
128 * previous or next split point.
130 SpriteExplosion
* Sprite::createExplosion(Random
& random
) {
131 SpriteExplosion
* retv
= new SpriteExplosion
;
132 int w
= pixmap().width();
133 int h
= pixmap().height();
137 int split_side
[40]; // the side of the image, 0 to 3
138 int split_start
[40]; // -1 to 1
139 QPoint split_point
[40];
140 QPoint split_start_point
[40];
146 /* generate few subsequent angles */
147 Random::RealGenerator random_float
= random
.rand(0.4, 0.7);
148 for(float s
= 0; s
< 2*M_PI
-0.3; s
+= random_float()) {
149 splits
[num_splits
] = s
;
150 splits_r
[num_splits
] = cos(s
);
151 splits_i
[num_splits
] = sin(s
);
155 /* generate the random center and calculate the relative corners */
156 random_float
= random
.rand(0.4, 0.6);
157 QPoint
center(int(w
* random_float()), int(h
* random_float()));
158 QPoint corners
[4] = { -center
, QPoint(w
,0)-center
,
159 QPoint(w
,h
)-center
, QPoint(0,h
)-center
};
161 /* for each split angle, find the size of the rect that contains the split point */
162 for(int i
=0;i
<num_splits
;i
++) {
165 for(int j
=0;j
<4;j
++) {
168 /* the imaginary part of the products (as complex numbers) must be >0 and <0 */
169 if(corners
[j
].x()*splits_i
[i
]-corners
[j
].y()*splits_r
[i
] >= 0
170 && corners
[j1
].x()*splits_i
[i
]-corners
[j1
].y()*splits_r
[i
] < 0 ) {
173 float fact
= (corners
[j
].x()*corners
[j1
].y()-corners
[j
].y()*corners
[j1
].x())/
174 (splits_r
[i
]*(corners
[j1
].y()-corners
[j
].y())-
175 splits_i
[i
]*(corners
[j1
].x()-corners
[j
].x()));
176 float x
= splits_r
[i
]*fact
;
177 float y
= splits_i
[i
]*fact
;
178 split_point
[i
] = QPoint(int(x
+0.5), int(y
+0.5));
183 /* there should really be one :) */
184 Q_ASSERT(split_side
[i
] != -1);
187 /* generate the split points connections. Some will be connected with the center (0),
188 others will be connected to a mid-way point of the previous or next points (-1/+1) */
189 Random::IntegerGenerator coin
= random
.rand(0, 1);
190 Random::IntegerGenerator die3
= random
.rand(0, 2);
192 for(int i
=1;i
<num_splits
;i
++) {
193 if(split_start
[i
-1]==1)
195 else if(split_start
[i
-1]==-1)
196 split_start
[i
] = coin();
198 split_start
[i
] = die3() - 1;
201 /* calculate such mid-way points */
202 random_float
= random
.rand(0.1, 0.3);
203 for(int i
=0;i
<num_splits
;i
++) {
204 int r
= (i
+split_start
[i
]);
205 r
= r
<0 ? num_splits
-1 : r
>=num_splits
? 0 : r
;
207 split_start_point
[i
] = QPoint();
209 split_start_point
[i
] = split_point
[r
] * random_float();
212 random_float
= random
.rand(0.5, 1.2);
213 for(int i
=0;i
<num_splits
;i
++) {
215 int i1
= (i
+1 == num_splits
) ? 0 : i
+1;
217 /* generate polygons, they have at most 6 sides */
218 f
.m_polygon
[f
.m_pcount
++] = center
+ split_point
[i
];
219 if(split_side
[i
] != split_side
[i1
])
220 f
.m_polygon
[f
.m_pcount
++] = center
+ corners
[split_side
[i1
]];
221 f
.m_polygon
[f
.m_pcount
++] = center
+ split_point
[i1
];
223 if(split_start
[i1
]==+1)
224 f
.m_polygon
[f
.m_pcount
++] = center
+ split_start_point
[i1
];
226 if(split_start
[i1
]==-1)
227 f
.m_polygon
[f
.m_pcount
++] = center
+ split_start_point
[i1
];
228 else if(split_start
[i
]==+1)
229 f
.m_polygon
[f
.m_pcount
++] = center
+ split_start_point
[i
];
231 f
.m_polygon
[f
.m_pcount
++] = center
;
233 if(split_start
[i
]==-1)
234 f
.m_polygon
[f
.m_pcount
++] = center
+ split_start_point
[i
];
236 /* generate a random speed */
237 f
.m_speed
= (split_point
[i
]+split_point
[i1
]) * random_float();
245 void Sprite::setRotation(float f
) {
250 void Sprite::setScale(float f
) {
255 void Sprite::paint(QPainter
* p
) {
258 /* if scale/rotate change the painter matrix */
259 if(m_rotation
!= 0.0 || m_scale
!= 1.0) {
260 QRectF rect
= pixmap().rect();
261 QPointF
center(rect
.width()*0.5, rect
.height()*0.5);
263 p
->translate(center
+pos());
264 p
->rotate(m_rotation
*180.0/M_PI
);
265 p
->scale(m_scale
, m_scale
);
266 p
->translate(-center
-pos());
270 p
->setPen(Qt::NoPen
);
271 p
->setBrush(pixmap());
274 for(int i
=0;i
<int(m_explosion
->size());i
++) {
275 ExplosionFragment
& f
= (*m_explosion
)[i
];
276 QPoint delta
= f
.m_speed
*m_explode_step
;
279 p
->drawConvexPolygon(f
.m_polygon
, f
.m_pcount
);
280 p
->translate(-delta
);
282 p
->translate(-pos());
285 KGameCanvasPixmap::paint(p
);
287 if(m_rotation
!= 0.0 || m_scale
!= 1.0)
291 QRect
Sprite::rect() const {
294 /* if exploding, set the rect to the bounding rect of the pieces */
296 int x1
=99999, x2
=-99999, y1
=99999, y2
=-99999;
298 for(int i
=0;i
<int(m_explosion
->size());i
++) {
299 ExplosionFragment
& f
= (*m_explosion
)[i
];
300 QPoint delta
= f
.m_speed
*m_explode_step
;
302 for(int j
=0;j
<f
.m_pcount
;j
++) {
303 x1
= std::min(x1
, f
.m_polygon
[j
].x()+delta
.x());
304 y1
= std::min(y1
, f
.m_polygon
[j
].y()+delta
.y());
305 x2
= std::max(x2
, f
.m_polygon
[j
].x()+delta
.x());
306 y2
= std::max(y2
, f
.m_polygon
[j
].y()+delta
.y());
310 retv
= QRect(x1
,y1
,x2
-x1
,y2
-y1
).translated(pos());
313 retv
= KGameCanvasPixmap::rect();
315 /* transform the rectangle as needed */
316 if(m_rotation
!= 0.0 || m_scale
!= 1.0) {
317 QRectF rect
= pixmap().rect();
318 QPointF
center(rect
.width()*0.5, rect
.height()*0.5);
320 transf
.translate(+center
.x()+pos().x(), +center
.y()+pos().y());
321 transf
.rotate(m_rotation
*180.0/M_PI
);
322 transf
.scale(m_scale
, m_scale
);
323 transf
.translate(-center
.x()-pos().x(), -center
.y()-pos().y());
324 return transf
.mapRect( retv
);