SVN_SILENT made messages (.desktop file)
[kdegames.git] / kolf / slope.cpp
blob4b90d2d37d5cdd546b83bd6ec4a5a514d716b314
1 /*
2 Copyright (C) 2002-2005, Jason Katz-Brown <jasonkb@mit.edu>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 #include "slope.h"
21 #include <qbitmap.h>
22 #include <QCheckBox>
23 #include <QLabel>
24 #include <qimage.h>
25 #include <qpixmapcache.h>
27 #include <QHBoxLayout>
28 #include <QVBoxLayout>
29 #include <QPainter>
30 #include <QApplication>
32 #include <kcombobox.h>
33 #include <kconfig.h>
34 #include <knuminput.h>
35 #include <kstandarddirs.h>
37 Slope::Slope(const QRect &rect, QGraphicsItem * parent, QGraphicsScene *scene)
38 : QGraphicsRectItem(rect, parent, scene), type(Vertical), grade(4), reversed(false), color(QColor("#327501"))
40 setData(0, 1031);
41 stuckOnGround = false;
42 showingInfo = false;
43 baseArrowPenThickness = arrowPenThickness = 1;
45 gradientKeys[Vertical] = "Vertical";
46 gradientKeys[Horizontal] = "Horizontal";
47 gradientKeys[Diagonal] = "Diagonal";
48 gradientKeys[CrossDiagonal] = "Opposite Diagonal";
49 gradientKeys[Elliptic] = "Elliptic";
51 gradientI18nKeys[Vertical] = i18n("Vertical");
52 gradientI18nKeys[Horizontal] = i18n("Horizontal");
53 gradientI18nKeys[Diagonal] = i18n("Diagonal");
54 gradientI18nKeys[CrossDiagonal] = i18n("Opposite Diagonal");
55 gradientI18nKeys[Elliptic] = i18n("Circular");
57 setZValue(-50);
59 point = new RectPoint(color.light(), this, parent, scene);
61 QFont font(QApplication::font());
62 baseFontPixelSize = 18;
63 font.setPixelSize(baseFontPixelSize);
64 text = new QGraphicsSimpleTextItem(0, scene);
65 text->setZValue(99999.99);
66 text->setFont(font);
67 text->setBrush(Qt::white);
69 editModeChanged(false);
70 hideInfo();
72 // this does updatePixmap
73 setGradient("Vertical");
76 bool Slope::terrainCollisions() const
78 // we are a terrain collision
79 return true;
82 void Slope::showInfo()
84 showingInfo = true;
85 QList<Arrow *>::const_iterator arrow;
86 for (arrow = arrows.constBegin(); arrow != arrows.constEnd(); ++arrow)
88 (*arrow)->setZValue(zValue() + .01);
89 (*arrow)->setVisible(true);
91 text->setVisible(true);
94 void Slope::hideInfo()
96 showingInfo = false;
97 QList<Arrow *>::const_iterator arrow;
98 for (arrow = arrows.constBegin(); arrow != arrows.constEnd(); ++arrow)
99 (*arrow)->setVisible(false);
100 text->setVisible(false);
103 void Slope::resize(double resizeFactor)
105 this->resizeFactor = resizeFactor;
106 QFont font = text->font();
107 font.setPixelSize((int)(baseFontPixelSize*resizeFactor));
108 text->setFont(font);
109 arrowPenThickness = baseArrowPenThickness*resizeFactor;
110 setPos(baseX*resizeFactor, baseY*resizeFactor);
111 setRect(0, 0, baseWidth*resizeFactor, baseHeight*resizeFactor);
112 updatePixmap();
115 void Slope::firstMove(int x, int y)
117 baseX = (double)x;
118 baseY = (double)y;
121 void Slope::aboutToDie()
123 delete point;
124 clearArrows();
125 delete text;
128 void Slope::clearArrows()
130 QList<Arrow *>::const_iterator arrow;
131 for (arrow = arrows.constBegin(); arrow != arrows.constEnd(); ++arrow)
133 (*arrow)->setVisible(false);
134 (*arrow)->aboutToDie();
136 while (!arrows.isEmpty())
137 delete arrows.takeFirst();
140 QList<QGraphicsItem *> Slope::moveableItems() const
142 QList<QGraphicsItem *> ret;
143 ret.append(point);
144 return ret;
147 void Slope::setGrade(double newGrade)
149 if (newGrade >= 0 && newGrade < 11)
151 grade = newGrade;
152 updatePixmap();
156 void Slope::setSize(double width, double height)
158 newSize(width, height);
161 void Slope::newSize(double width, double height)
163 if (type == Elliptic)
165 QGraphicsRectItem::setRect(rect().x(), rect().y(), width, width);
166 // move point back to good spot
167 moveBy(0, 0);
169 if (game && game->isEditing())
170 game->updateHighlighter();
172 else
173 QGraphicsRectItem::setRect(rect().x(), rect().y(), width, height);
175 updatePixmap();
176 updateZ();
179 void Slope::moveBy(double dx, double dy)
181 QGraphicsRectItem::moveBy(dx, dy);
183 point->dontMove();
184 point->setPos(x() + width(), y() + height());
186 moveArrow();
187 updateZ();
190 void Slope::moveArrow()
192 double xavg, yavg;
194 if(type == Diagonal) {
195 if(reversed) {
196 xavg = boundingRect().width()*1/4 + x();
197 yavg = boundingRect().height()*1/4 + y();
199 else {
200 xavg = boundingRect().width()*3/4 + x();
201 yavg = boundingRect().height()*3/4 + y();
204 else if(type == CrossDiagonal) {
205 if(reversed) {
206 xavg = boundingRect().width()*3/4 + x();
207 yavg = boundingRect().height()*1/4 + y();
209 else {
210 xavg = boundingRect().width()*1/4 + x();
211 yavg = boundingRect().height()*3/4 + y();
214 else {
215 xavg = boundingRect().width()/2 + x();
216 yavg = boundingRect().height()/2 + y();
219 QList<Arrow *>::const_iterator arrow;
220 for (arrow = arrows.constBegin(); arrow != arrows.constEnd(); ++arrow)
221 (*arrow)->setPos(xavg, yavg);
223 if (showingInfo)
224 showInfo();
225 else
226 hideInfo();
228 text->setPos(xavg - text->boundingRect().width() / 2, yavg - text->boundingRect().height() / 2);
231 void Slope::editModeChanged(bool changed)
233 point->setVisible(changed);
234 moveBy(0, 0);
237 void Slope::updateZ(QGraphicsRectItem *vStrut)
239 const double area = (height() * width());
240 const int defaultz = -50;
242 double newZ = 0;
244 QGraphicsRectItem *rect = 0;
245 if (!stuckOnGround)
246 rect = vStrut? vStrut : onVStrut();
248 if (rect)
250 if (area > (rect->rect().width() * rect->rect().height()))
251 newZ = defaultz;
252 else
253 newZ = rect->zValue();
255 else
256 newZ = defaultz;
258 setZValue(((double)1 / (area == 0? 1 : area)) + newZ);
261 void Slope::load(KConfigGroup *cfgGroup)
263 stuckOnGround = cfgGroup->readEntry("stuckOnGround", stuckOnGround);
264 grade = cfgGroup->readEntry("grade", grade);
265 reversed = cfgGroup->readEntry("reversed", reversed);
267 // bypass updatePixmap which newSize normally does
268 QGraphicsRectItem::setRect(rect().x(), rect().y(), cfgGroup->readEntry("width", width()), cfgGroup->readEntry("height", height()));
269 baseWidth = rect().width();
270 baseHeight = rect().height();
271 updateZ();
273 QString gradientType = cfgGroup->readEntry("gradient", gradientKeys[type]);
274 setGradient(gradientType);
277 void Slope::save(KConfigGroup *cfgGroup)
279 cfgGroup->writeEntry("reversed", reversed);
280 cfgGroup->writeEntry("width", width());
281 cfgGroup->writeEntry("height", height());
282 cfgGroup->writeEntry("gradient", gradientKeys[type]);
283 cfgGroup->writeEntry("grade", grade);
284 cfgGroup->writeEntry("stuckOnGround", stuckOnGround);
287 void Slope::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/ )
289 painter->drawPixmap(0, 0, pixmap);
292 QPainterPath Slope::shape() const
294 if(type == CrossDiagonal) {
295 QPainterPath path;
296 QPolygonF polygon(3);
297 polygon[0] = QPointF(rect().x(), rect().y());
298 polygon[1] = QPointF(rect().x() + width(), rect().y() + height());
299 polygon[2] = reversed? QPointF(rect().x() + width(), rect().y()) : QPointF(rect().x(), rect().y() + height());
300 path.addPolygon(polygon);
301 return path;
303 else if(type == Diagonal) {
304 QPainterPath path;
305 QPolygonF polygon(3);
306 polygon[0] = QPointF(rect().x() + width(), rect().y());
307 polygon[1] = QPointF(rect().x(), rect().y() + height());
308 polygon[2] = !reversed? QPointF(rect().x() + width(), rect().y() + height()) : QPointF(rect().x(), rect().y());
309 path.addPolygon(polygon);
310 return path;
312 else if(type == Elliptic) {
313 QPainterPath path;
314 path.addEllipse(rect().x(), rect().y(), width(), height());
315 return path;
317 else {
318 QPainterPath path;
319 path.addRect(rect().x(), rect().y(), width(), height());
320 return path;
324 bool Slope::collision(Ball *ball, long int /*id*/)
326 if (grade <= 0)
327 return false;
329 double vx = ball->getXVelocity();
330 double vy = ball->getYVelocity();
331 double addto = 0.013 * grade;
333 const bool diag = type == Diagonal || type == CrossDiagonal;
334 const bool circle = type == Elliptic;
336 double slopeAngle = 0;
338 if (diag)
339 slopeAngle = atan((double)width() / (double)height());
340 else if (circle)
342 const QPointF start((x() + width() / 2.0), y() + height() / 2.0);
343 const QPointF end(ball->x(), ball->y());
345 Vector betweenVector(start, end);
346 const double factor = betweenVector.magnitude() / ((double)width() / 2.0);
347 slopeAngle = betweenVector.direction();
349 // this little bit by Daniel
350 addto *= factor * M_PI / 2;
351 addto = sin(addto);
354 switch (type)
356 case Horizontal:
357 reversed? vx += addto : vx -= addto;
358 break;
360 case Vertical:
361 reversed? vy += addto : vy -= addto;
362 break;
364 case Diagonal:
365 case Elliptic:
366 reversed? vx += cos(slopeAngle) * addto : vx -= cos(slopeAngle) * addto;
367 reversed? vy += sin(slopeAngle) * addto : vy -= sin(slopeAngle) * addto;
368 break;
370 case CrossDiagonal:
371 reversed? vx -= cos(slopeAngle) * addto : vx += cos(slopeAngle) * addto;
372 reversed? vy += sin(slopeAngle) * addto : vy -= sin(slopeAngle) * addto;
373 break;
375 default:
376 break;
379 ball->setVelocity(vx, vy);
380 // check if the ball is at the center of a pit or mound
381 // or has otherwise stopped.
382 if (vx == 0 && vy ==0)
383 ball->setState(Stopped);
384 else
385 ball->setState(Rolling);
387 // do NOT do terrain collidingItems
388 return false;
391 void Slope::setGradient(const QString &text)
393 for (QMap<GradientType, QString>::Iterator it = gradientKeys.begin(); it != gradientKeys.end(); ++it)
395 if (it.value() == text)
397 setType(it.key());
398 return;
402 // extra forgiveness ;-) (note it's i18n keys)
403 for (QMap<GradientType, QString>::Iterator it = gradientI18nKeys.begin(); it != gradientI18nKeys.end(); ++it)
405 if (it.value() == text)
407 setType(it.key());
408 return;
413 void Slope::setType(GradientType type)
415 this->type = type;
417 if (type == Elliptic)
419 // calls updatePixmap
420 newSize(width(), height());
422 else
423 updatePixmap();
426 void Slope::updatePixmap() //this needs work so that the slope colour depends on angle again
428 if(game == 0)
429 return;
431 QString slopeName;
433 switch(type) {
434 case Horizontal:
435 if(reversed)
436 slopeName = "slope_e";
437 else
438 slopeName = "slope_w";
439 break;
441 case Vertical:
442 if(reversed)
443 slopeName = "slope_s";
444 else
445 slopeName = "slope_n";
446 break;
448 case Diagonal:
449 if(reversed)
450 slopeName = "slope_se";
451 else
452 slopeName = "slope_nw";
453 break;
455 case CrossDiagonal:
456 if(reversed)
457 slopeName = "slope_sw";
458 else
459 slopeName = "slope_ne";
460 break;
461 case Elliptic:
462 if(reversed)
463 slopeName = "slope_dip";
464 else
465 slopeName = "slope_bump";
466 break;
467 default:
468 break;
471 pixmap=game->renderer->renderSvg(slopeName, (int)width(), (int)height(), 0);
473 // we update the arrows in this function
474 clearArrows();
476 const double length = sqrt(double(width() * width() + height() * height())) / 4;
478 if (type == Elliptic)
480 double angle = 0;
481 for (int i = 0; i < 4; ++i)
483 angle += M_PI / 2;
484 Arrow *arrow = new Arrow(0, scene());
485 arrow->setLength(length);
486 arrow->setAngle(angle);
487 arrow->setPen(QPen(Qt::black, arrowPenThickness));
488 arrow->setReversed(reversed);
489 arrow->updateSelf();
490 arrows.append(arrow);
493 else
495 Arrow *arrow = new Arrow(0, scene());
496 double angle = 0;
498 switch(type) {
499 case Horizontal:
500 angle = 0;
501 break;
502 case Vertical:
503 angle = M_PI / 2;
504 break;
505 case Diagonal:
506 angle = atan((double)width() / (double)height());
507 break;
508 case CrossDiagonal:
509 angle = M_PI - atan((double)width() / (double)height());
510 break;
511 default:
512 break;
515 if (!reversed)
516 angle += M_PI;
518 arrow->setAngle(angle);
519 arrow->setLength(length);
520 arrow->setPen(QPen(Qt::black, arrowPenThickness));
521 arrow->updateSelf();
523 arrows.append(arrow);
526 text->setText(QString::number(grade));
528 moveArrow();
531 void Slope::updateBaseResizeInfo()
533 baseX = x() / resizeFactor;
534 baseY = y() / resizeFactor;
535 baseWidth = width() / resizeFactor;
536 baseHeight = height() / resizeFactor;
539 /////////////////////////
541 SlopeConfig::SlopeConfig(Slope *slope, QWidget *parent)
542 : Config(parent)
544 this->slope = slope;
545 QVBoxLayout *layout = new QVBoxLayout(this);
546 layout->setMargin( marginHint() );
547 layout->setSpacing( spacingHint() );
549 KComboBox *gradient = new KComboBox(this);
550 QStringList items;
551 QString curText;
552 for (QMap<GradientType, QString>::Iterator it = slope->gradientI18nKeys.begin(); it != slope->gradientI18nKeys.end(); ++it)
554 if (it.key() == slope->curType())
555 curText = it.value();
556 items.append(it.value());
558 gradient->addItems(items);
559 gradient->setCurrentItem(curText);
560 layout->addWidget(gradient);
561 connect(gradient, SIGNAL(activated(const QString &)), this, SLOT(setGradient(const QString &)));
563 layout->addStretch();
565 QCheckBox *reversed = new QCheckBox(i18n("Reverse direction"), this);
566 reversed->setChecked(slope->isReversed());
567 layout->addWidget(reversed);
568 connect(reversed, SIGNAL(toggled(bool)), this, SLOT(setReversed(bool)));
570 QHBoxLayout *hlayout = new QHBoxLayout;
571 hlayout->setSpacing( spacingHint() );
572 layout->addLayout( hlayout );
573 hlayout->addWidget(new QLabel(i18n("Grade:"), this));
574 KDoubleNumInput *grade = new KDoubleNumInput(this);
575 grade->setRange(0, 8, 1, true);
576 grade->setValue(slope->curGrade());
577 hlayout->addWidget(grade);
578 connect(grade, SIGNAL(valueChanged(double)), this, SLOT(gradeChanged(double)));
580 QCheckBox *stuck = new QCheckBox(i18n("Unmovable"), this);
581 stuck->setWhatsThis( i18n("Whether or not this slope can be moved by other objects, like floaters."));
582 stuck->setChecked(slope->isStuckOnGround());
583 layout->addWidget(stuck);
584 connect(stuck, SIGNAL(toggled(bool)), this, SLOT(setStuckOnGround(bool)));
587 void SlopeConfig::setGradient(const QString &text)
589 slope->setGradient(text);
590 changed();
593 void SlopeConfig::setReversed(bool yes)
595 slope->setReversed(yes);
596 changed();
599 void SlopeConfig::setStuckOnGround(bool yes)
601 slope->setStuckOnGround(yes);
602 changed();
605 void SlopeConfig::gradeChanged(double newgrade)
607 slope->setGrade(newgrade);
608 changed();
612 #include "slope.moc"