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
25 #include <qpixmapcache.h>
27 #include <QHBoxLayout>
28 #include <QVBoxLayout>
30 #include <QApplication>
32 #include <kcombobox.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"))
41 stuckOnGround
= 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");
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);
67 text
->setBrush(Qt::white
);
69 editModeChanged(false);
72 // this does updatePixmap
73 setGradient("Vertical");
76 bool Slope::terrainCollisions() const
78 // we are a terrain collision
82 void Slope::showInfo()
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()
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
));
109 arrowPenThickness
= baseArrowPenThickness
*resizeFactor
;
110 setPos(baseX
*resizeFactor
, baseY
*resizeFactor
);
111 setRect(0, 0, baseWidth
*resizeFactor
, baseHeight
*resizeFactor
);
115 void Slope::firstMove(int x
, int y
)
121 void Slope::aboutToDie()
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
;
147 void Slope::setGrade(double newGrade
)
149 if (newGrade
>= 0 && newGrade
< 11)
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
169 if (game
&& game
->isEditing())
170 game
->updateHighlighter();
173 QGraphicsRectItem::setRect(rect().x(), rect().y(), width
, height
);
179 void Slope::moveBy(double dx
, double dy
)
181 QGraphicsRectItem::moveBy(dx
, dy
);
184 point
->setPos(x() + width(), y() + height());
190 void Slope::moveArrow()
194 if(type
== Diagonal
) {
196 xavg
= boundingRect().width()*1/4 + x();
197 yavg
= boundingRect().height()*1/4 + y();
200 xavg
= boundingRect().width()*3/4 + x();
201 yavg
= boundingRect().height()*3/4 + y();
204 else if(type
== CrossDiagonal
) {
206 xavg
= boundingRect().width()*3/4 + x();
207 yavg
= boundingRect().height()*1/4 + y();
210 xavg
= boundingRect().width()*1/4 + x();
211 yavg
= boundingRect().height()*3/4 + y();
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
);
228 text
->setPos(xavg
- text
->boundingRect().width() / 2, yavg
- text
->boundingRect().height() / 2);
231 void Slope::editModeChanged(bool changed
)
233 point
->setVisible(changed
);
237 void Slope::updateZ(QGraphicsRectItem
*vStrut
)
239 const double area
= (height() * width());
240 const int defaultz
= -50;
244 QGraphicsRectItem
*rect
= 0;
246 rect
= vStrut
? vStrut
: onVStrut();
250 if (area
> (rect
->rect().width() * rect
->rect().height()))
253 newZ
= rect
->zValue();
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();
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
) {
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
);
303 else if(type
== Diagonal
) {
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
);
312 else if(type
== Elliptic
) {
314 path
.addEllipse(rect().x(), rect().y(), width(), height());
319 path
.addRect(rect().x(), rect().y(), width(), height());
324 bool Slope::collision(Ball
*ball
, long int /*id*/)
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;
339 slopeAngle
= atan((double)width() / (double)height());
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;
357 reversed
? vx
+= addto
: vx
-= addto
;
361 reversed
? vy
+= addto
: vy
-= addto
;
366 reversed
? vx
+= cos(slopeAngle
) * addto
: vx
-= cos(slopeAngle
) * addto
;
367 reversed
? vy
+= sin(slopeAngle
) * addto
: vy
-= sin(slopeAngle
) * addto
;
371 reversed
? vx
-= cos(slopeAngle
) * addto
: vx
+= cos(slopeAngle
) * addto
;
372 reversed
? vy
+= sin(slopeAngle
) * addto
: vy
-= sin(slopeAngle
) * addto
;
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
);
385 ball
->setState(Rolling
);
387 // do NOT do terrain collidingItems
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
)
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
)
413 void Slope::setType(GradientType type
)
417 if (type
== Elliptic
)
419 // calls updatePixmap
420 newSize(width(), height());
426 void Slope::updatePixmap() //this needs work so that the slope colour depends on angle again
436 slopeName
= "slope_e";
438 slopeName
= "slope_w";
443 slopeName
= "slope_s";
445 slopeName
= "slope_n";
450 slopeName
= "slope_se";
452 slopeName
= "slope_nw";
457 slopeName
= "slope_sw";
459 slopeName
= "slope_ne";
463 slopeName
= "slope_dip";
465 slopeName
= "slope_bump";
471 pixmap
=game
->renderer
->renderSvg(slopeName
, (int)width(), (int)height(), 0);
473 // we update the arrows in this function
476 const double length
= sqrt(double(width() * width() + height() * height())) / 4;
478 if (type
== Elliptic
)
481 for (int i
= 0; i
< 4; ++i
)
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
);
490 arrows
.append(arrow
);
495 Arrow
*arrow
= new Arrow(0, scene());
506 angle
= atan((double)width() / (double)height());
509 angle
= M_PI
- atan((double)width() / (double)height());
518 arrow
->setAngle(angle
);
519 arrow
->setLength(length
);
520 arrow
->setPen(QPen(Qt::black
, arrowPenThickness
));
523 arrows
.append(arrow
);
526 text
->setText(QString::number(grade
));
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
)
545 QVBoxLayout
*layout
= new QVBoxLayout(this);
546 layout
->setMargin( marginHint() );
547 layout
->setSpacing( spacingHint() );
549 KComboBox
*gradient
= new KComboBox(this);
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
);
593 void SlopeConfig::setReversed(bool yes
)
595 slope
->setReversed(yes
);
599 void SlopeConfig::setStuckOnGround(bool yes
)
601 slope
->setStuckOnGround(yes
);
605 void SlopeConfig::gradeChanged(double newgrade
)
607 slope
->setGrade(newgrade
);