summaryrefslogtreecommitdiffstats
path: root/kolf/game.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kolf/game.cpp')
-rw-r--r--kolf/game.cpp4302
1 files changed, 4302 insertions, 0 deletions
diff --git a/kolf/game.cpp b/kolf/game.cpp
new file mode 100644
index 00000000..55b324ce
--- /dev/null
+++ b/kolf/game.cpp
@@ -0,0 +1,4302 @@
+#include <arts/kmedia2.h>
+#include <arts/kplayobject.h>
+#include <arts/kplayobjectfactory.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kcursor.h>
+#include <kdebug.h>
+#include <knuminput.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <klineedit.h>
+#include <kmessagebox.h>
+#include <kpixmapeffect.h>
+#include <kprinter.h>
+#include <kstandarddirs.h>
+
+#include <qbrush.h>
+#include <qcanvas.h>
+#include <qcheckbox.h>
+#include <qcolor.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qimage.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qmap.h>
+#include <qpainter.h>
+#include <qpaintdevicemetrics.h>
+#include <qpen.h>
+#include <qpixmap.h>
+#include <qpixmapcache.h>
+#include <qpoint.h>
+#include <qpointarray.h>
+#include <qrect.h>
+#include <qsimplerichtext.h>
+#include <qsize.h>
+#include <qslider.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+#include <qvaluelist.h>
+#include <qwhatsthis.h>
+
+#include <math.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "kcomboboxdialog.h"
+#include "kvolumecontrol.h"
+#include "vector.h"
+#include "game.h"
+
+
+inline QString makeGroup(int id, int hole, QString name, int x, int y)
+{
+ return QString("%1-%2@%3,%4|%5").arg(hole).arg(name).arg(x).arg(y).arg(id);
+}
+
+inline QString makeStateGroup(int id, const QString &name)
+{
+ return QString("%1|%2").arg(name).arg(id);
+}
+
+/////////////////////////
+
+RectPoint::RectPoint(QColor color, RectItem *rect, QCanvas *canvas)
+ : QCanvasEllipse(canvas)
+{
+ setZ(9999);
+ setSize(10, 10);
+ this->rect = rect;
+ setBrush(QBrush(color));
+ setSizeFactor(1.0);
+ dontmove = false;
+}
+
+void RectPoint::moveBy(double dx, double dy)
+{
+ QCanvasEllipse::moveBy(dx, dy);
+
+ if (dontmove)
+ {
+ dontmove = false;
+ return;
+ }
+
+ QCanvasItem *qitem = dynamic_cast<QCanvasItem *>(rect);
+ if (!qitem)
+ return;
+
+ double nw = m_sizeFactor * fabs(x() - qitem->x());
+ double nh = m_sizeFactor * fabs(y() - qitem->y());
+ if (nw <= 0 || nh <= 0)
+ return;
+
+ rect->newSize(nw, nh);
+}
+
+Config *RectPoint::config(QWidget *parent)
+{
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(rect);
+ if (citem)
+ return citem->config(parent);
+ else
+ return CanvasItem::config(parent);
+}
+
+/////////////////////////
+
+Arrow::Arrow(QCanvas *canvas)
+ : QCanvasLine(canvas)
+{
+ line1 = new QCanvasLine(canvas);
+ line2 = new QCanvasLine(canvas);
+
+ m_angle = 0;
+ m_length = 20;
+ m_reversed = false;
+
+ setPen(black);
+
+ updateSelf();
+ setVisible(false);
+}
+
+void Arrow::setPen(QPen p)
+{
+ QCanvasLine::setPen(p);
+ line1->setPen(p);
+ line2->setPen(p);
+}
+
+void Arrow::setZ(double newz)
+{
+ QCanvasLine::setZ(newz);
+ line1->setZ(newz);
+ line2->setZ(newz);
+}
+
+void Arrow::setVisible(bool yes)
+{
+ QCanvasLine::setVisible(yes);
+ line1->setVisible(yes);
+ line2->setVisible(yes);
+}
+
+void Arrow::moveBy(double dx, double dy)
+{
+ QCanvasLine::moveBy(dx, dy);
+ line1->moveBy(dx, dy);
+ line2->moveBy(dx, dy);
+}
+
+void Arrow::aboutToDie()
+{
+ delete line1;
+ delete line2;
+}
+
+void Arrow::updateSelf()
+{
+ QPoint start = startPoint();
+ QPoint end(m_length * cos(m_angle), m_length * sin(m_angle));
+
+ if (m_reversed)
+ {
+ QPoint tmp(start);
+ start = end;
+ end = tmp;
+ }
+
+ setPoints(start.x(), start.y(), end.x(), end.y());
+
+ const double lineLen = m_length / 2;
+
+ const double angle1 = m_angle - M_PI / 2 - 1;
+ line1->move(end.x() + x(), end.y() + y());
+ start = end;
+ end = QPoint(lineLen * cos(angle1), lineLen * sin(angle1));
+ line1->setPoints(0, 0, end.x(), end.y());
+
+ const double angle2 = m_angle + M_PI / 2 + 1;
+ line2->move(start.x() + x(), start.y() + y());
+ end = QPoint(lineLen * cos(angle2), lineLen * sin(angle2));
+ line2->setPoints(0, 0, end.x(), end.y());
+}
+
+/////////////////////////
+
+BridgeConfig::BridgeConfig(Bridge *bridge, QWidget *parent)
+ : Config(parent)
+{
+ this->bridge = bridge;
+
+ m_vlayout = new QVBoxLayout(this, marginHint(), spacingHint());
+ QGridLayout *layout = new QGridLayout(m_vlayout, 2, 3, spacingHint());
+ layout->addWidget(new QLabel(i18n("Walls on:"), this), 0, 0);
+ top = new QCheckBox(i18n("&Top"), this);
+ layout->addWidget(top, 0, 1);
+ connect(top, SIGNAL(toggled(bool)), this, SLOT(topWallChanged(bool)));
+ top->setChecked(bridge->topWallVisible());
+ bot = new QCheckBox(i18n("&Bottom"), this);
+ layout->addWidget(bot, 1, 1);
+ connect(bot, SIGNAL(toggled(bool)), this, SLOT(botWallChanged(bool)));
+ bot->setChecked(bridge->botWallVisible());
+ left = new QCheckBox(i18n("&Left"), this);
+ layout->addWidget(left, 1, 0);
+ connect(left, SIGNAL(toggled(bool)), this, SLOT(leftWallChanged(bool)));
+ left->setChecked(bridge->leftWallVisible());
+ right = new QCheckBox(i18n("&Right"), this);
+ layout->addWidget(right, 1, 2);
+ connect(right, SIGNAL(toggled(bool)), this, SLOT(rightWallChanged(bool)));
+ right->setChecked(bridge->rightWallVisible());
+}
+
+void BridgeConfig::topWallChanged(bool yes)
+{
+ bridge->setTopWallVisible(yes);
+ changed();
+}
+
+void BridgeConfig::botWallChanged(bool yes)
+{
+ bridge->setBotWallVisible(yes);
+ changed();
+}
+
+void BridgeConfig::leftWallChanged(bool yes)
+{
+ bridge->setLeftWallVisible(yes);
+ changed();
+}
+
+void BridgeConfig::rightWallChanged(bool yes)
+{
+ bridge->setRightWallVisible(yes);
+ changed();
+}
+
+/////////////////////////
+
+Bridge::Bridge(QRect rect, QCanvas *canvas)
+ : QCanvasRectangle(rect, canvas)
+{
+ QColor color("#92772D");
+ setBrush(QBrush(color));
+ setPen(NoPen);
+ setZ(998);
+
+ topWall = new Wall(canvas);
+ topWall->setAlwaysShow(true);
+ botWall = new Wall(canvas);
+ botWall->setAlwaysShow(true);
+ leftWall = new Wall(canvas);
+ leftWall->setAlwaysShow(true);
+ rightWall = new Wall(canvas);
+ rightWall->setAlwaysShow(true);
+
+ setWallZ(z() + 0.01);
+ setWallColor(color);
+
+ topWall->setVisible(false);
+ botWall->setVisible(false);
+ leftWall->setVisible(false);
+ rightWall->setVisible(false);
+
+ point = new RectPoint(color, this, canvas);
+ editModeChanged(false);
+
+ newSize(width(), height());
+}
+
+bool Bridge::collision(Ball *ball, long int /*id*/)
+{
+ ball->setFrictionMultiplier(.63);
+ return false;
+}
+
+void Bridge::setWallZ(double newz)
+{
+ topWall->setZ(newz);
+ botWall->setZ(newz);
+ leftWall->setZ(newz);
+ rightWall->setZ(newz);
+}
+
+void Bridge::setGame(KolfGame *game)
+{
+ CanvasItem::setGame(game);
+ topWall->setGame(game);
+ botWall->setGame(game);
+ leftWall->setGame(game);
+ rightWall->setGame(game);
+}
+
+void Bridge::setWallColor(QColor color)
+{
+ topWall->setPen(QPen(color.dark(), 3));
+ botWall->setPen(topWall->pen());
+ leftWall->setPen(topWall->pen());
+ rightWall->setPen(topWall->pen());
+}
+
+void Bridge::aboutToDie()
+{
+ delete point;
+ topWall->aboutToDie();
+ delete topWall;
+ botWall->aboutToDie();
+ delete botWall;
+ leftWall->aboutToDie();
+ delete leftWall;
+ rightWall->aboutToDie();
+ delete rightWall;
+}
+
+void Bridge::editModeChanged(bool changed)
+{
+ point->setVisible(changed);
+ moveBy(0, 0);
+}
+
+void Bridge::moveBy(double dx, double dy)
+{
+ QCanvasRectangle::moveBy(dx, dy);
+
+ point->dontMove();
+ point->move(x() + width(), y() + height());
+
+ topWall->move(x(), y());
+ botWall->move(x(), y() - 1);
+ leftWall->move(x(), y());
+ rightWall->move(x(), y());
+
+ QCanvasItemList list = collisions(true);
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it)
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(*it);
+ if (citem)
+ citem->updateZ();
+ }
+}
+
+void Bridge::load(KConfig *cfg)
+{
+ doLoad(cfg);
+}
+
+void Bridge::doLoad(KConfig *cfg)
+{
+ newSize(cfg->readNumEntry("width", width()), cfg->readNumEntry("height", height()));
+ setTopWallVisible(cfg->readBoolEntry("topWallVisible", topWallVisible()));
+ setBotWallVisible(cfg->readBoolEntry("botWallVisible", botWallVisible()));
+ setLeftWallVisible(cfg->readBoolEntry("leftWallVisible", leftWallVisible()));
+ setRightWallVisible(cfg->readBoolEntry("rightWallVisible", rightWallVisible()));
+}
+
+void Bridge::save(KConfig *cfg)
+{
+ doSave(cfg);
+}
+
+void Bridge::doSave(KConfig *cfg)
+{
+ cfg->writeEntry("width", width());
+ cfg->writeEntry("height", height());
+ cfg->writeEntry("topWallVisible", topWallVisible());
+ cfg->writeEntry("botWallVisible", botWallVisible());
+ cfg->writeEntry("leftWallVisible", leftWallVisible());
+ cfg->writeEntry("rightWallVisible", rightWallVisible());
+}
+
+QPtrList<QCanvasItem> Bridge::moveableItems() const
+{
+ QPtrList<QCanvasItem> ret;
+ ret.append(point);
+ return ret;
+}
+
+void Bridge::newSize(int width, int height)
+{
+ setSize(width, height);
+}
+
+void Bridge::setSize(int width, int height)
+{
+ QCanvasRectangle::setSize(width, height);
+
+ topWall->setPoints(0, 0, width, 0);
+ botWall->setPoints(0, height, width, height);
+ leftWall->setPoints(0, 0, 0, height);
+ rightWall->setPoints(width, 0, width, height);
+
+ moveBy(0, 0);
+}
+
+/////////////////////////
+
+WindmillConfig::WindmillConfig(Windmill *windmill, QWidget *parent)
+ : BridgeConfig(windmill, parent)
+{
+ this->windmill = windmill;
+ m_vlayout->addStretch();
+
+ QCheckBox *check = new QCheckBox(i18n("Windmill on bottom"), this);
+ check->setChecked(windmill->bottom());
+ connect(check, SIGNAL(toggled(bool)), this, SLOT(endChanged(bool)));
+ m_vlayout->addWidget(check);
+
+ QHBoxLayout *hlayout = new QHBoxLayout(m_vlayout, spacingHint());
+ hlayout->addWidget(new QLabel(i18n("Slow"), this));
+ QSlider *slider = new QSlider(1, 10, 1, windmill->curSpeed(), Qt::Horizontal, this);
+ hlayout->addWidget(slider);
+ hlayout->addWidget(new QLabel(i18n("Fast"), this));
+ connect(slider, SIGNAL(valueChanged(int)), this, SLOT(speedChanged(int)));
+
+ endChanged(check->isChecked());
+}
+
+void WindmillConfig::speedChanged(int news)
+{
+ windmill->setSpeed(news);
+ changed();
+}
+
+void WindmillConfig::endChanged(bool bottom)
+{
+ windmill->setBottom(bottom);
+ changed();
+
+ bot->setEnabled(!bottom);
+ if (startedUp)
+ {
+ bot->setChecked(!bottom);
+ botWallChanged(bot->isChecked());
+ }
+ top->setEnabled(bottom);
+ if (startedUp)
+ {
+ top->setChecked(bottom);
+ topWallChanged(top->isChecked());
+ }
+}
+
+/////////////////////////
+
+Windmill::Windmill(QRect rect, QCanvas *canvas)
+ : Bridge(rect, canvas), speedfactor(16), m_bottom(true)
+{
+ guard = new WindmillGuard(canvas);
+ guard->setPen(QPen(black, 5));
+ guard->setVisible(true);
+ guard->setAlwaysShow(true);
+ setSpeed(5);
+ guard->setZ(wallZ() + .1);
+
+ left = new Wall(canvas);
+ left->setPen(wallPen());
+ left->setAlwaysShow(true);
+ right = new Wall(canvas);
+ right->setPen(wallPen());
+ right->setAlwaysShow(true);
+ left->setZ(wallZ());
+ right->setZ(wallZ());
+ left->setVisible(true);
+ right->setVisible(true);
+
+ setTopWallVisible(false);
+ setBotWallVisible(false);
+ setLeftWallVisible(true);
+ setRightWallVisible(true);
+
+ newSize(width(), height());
+ moveBy(0, 0);
+}
+
+void Windmill::aboutToDie()
+{
+ Bridge::aboutToDie();
+ guard->aboutToDie();
+ delete guard;
+ left->aboutToDie();
+ delete left;
+ right->aboutToDie();
+ delete right;
+}
+
+void Windmill::setSpeed(int news)
+{
+ if (news < 0)
+ return;
+ speed = news;
+ guard->setXVelocity(((double)news / (double)3) * (guard->xVelocity() > 0? 1 : -1));
+}
+
+void Windmill::setGame(KolfGame *game)
+{
+ Bridge::setGame(game);
+ guard->setGame(game);
+ left->setGame(game);
+ right->setGame(game);
+}
+
+void Windmill::save(KConfig *cfg)
+{
+ cfg->writeEntry("speed", speed);
+ cfg->writeEntry("bottom", m_bottom);
+
+ doSave(cfg);
+}
+
+void Windmill::load(KConfig *cfg)
+{
+ setSpeed(cfg->readNumEntry("speed", -1));
+
+ doLoad(cfg);
+
+ left->editModeChanged(false);
+ right->editModeChanged(false);
+ guard->editModeChanged(false);
+
+ setBottom(cfg->readBoolEntry("bottom", true));
+}
+
+void Windmill::moveBy(double dx, double dy)
+{
+ Bridge::moveBy(dx, dy);
+
+ left->move(x(), y());
+ right->move(x(), y());
+
+ guard->moveBy(dx, dy);
+ guard->setBetween(x(), x() + width());
+
+ update();
+}
+
+void Windmill::setSize(int width, int height)
+{
+ newSize(width, height);
+}
+
+void Windmill::setBottom(bool yes)
+{
+ m_bottom = yes;
+ newSize(width(), height());
+}
+
+void Windmill::newSize(int width, int height)
+{
+ Bridge::newSize(width, height);
+
+ const int indent = width / 4;
+
+ double indentY = m_bottom? height : 0;
+ left->setPoints(0, indentY, indent, indentY);
+ right->setPoints(width - indent, indentY, width, indentY);
+
+ guard->setBetween(x(), x() + width);
+ double guardY = m_bottom? height + 4 : -4;
+ guard->setPoints(0, guardY, (double)indent / (double)1.07 - 2, guardY);
+}
+
+/////////////////////////
+
+void WindmillGuard::advance(int phase)
+{
+ Wall::advance(phase);
+
+ if (phase == 1)
+ {
+ if (x() + startPoint().x() <= min)
+ setXVelocity(fabs(xVelocity()));
+ else if (x() + endPoint().x() >= max)
+ setXVelocity(-fabs(xVelocity()));
+ }
+}
+
+/////////////////////////
+
+Sign::Sign(QCanvas *canvas)
+ : Bridge(QRect(0, 0, 110, 40), canvas)
+{
+ setZ(998.8);
+ m_text = m_untranslatedText = i18n("New Text");
+ setBrush(QBrush(white));
+ setWallColor(black);
+ setWallZ(z() + .01);
+
+ setTopWallVisible(true);
+ setBotWallVisible(true);
+ setLeftWallVisible(true);
+ setRightWallVisible(true);
+}
+
+void Sign::load(KConfig *cfg)
+{
+ m_text = cfg->readEntry("Comment", m_text);
+ m_untranslatedText = cfg->readEntryUntranslated("Comment", m_untranslatedText);
+
+ doLoad(cfg);
+}
+
+void Sign::save(KConfig *cfg)
+{
+ cfg->writeEntry("Comment", m_untranslatedText);
+
+ doSave(cfg);
+}
+
+void Sign::setText(const QString &text)
+{
+ m_text = text;
+ m_untranslatedText = text;
+
+ update();
+}
+
+void Sign::draw(QPainter &painter)
+{
+ Bridge::draw(painter);
+
+ painter.setPen(QPen(black, 1));
+ QSimpleRichText txt(m_text, kapp->font());
+ const int indent = wallPen().width() + 3;
+ txt.setWidth(width() - 2*indent);
+ QColorGroup colorGroup;
+ colorGroup.setColor(QColorGroup::Foreground, black);
+ colorGroup.setColor(QColorGroup::Text, black);
+ colorGroup.setColor(QColorGroup::Background, black);
+ colorGroup.setColor(QColorGroup::Base, black);
+ txt.draw(&painter, x() + indent, y(), QRect(x() + indent, y(), width() - indent, height() - indent), colorGroup);
+}
+
+/////////////////////////
+
+SignConfig::SignConfig(Sign *sign, QWidget *parent)
+ : BridgeConfig(sign, parent)
+{
+ this->sign = sign;
+ m_vlayout->addStretch();
+ m_vlayout->addWidget(new QLabel(i18n("Sign HTML:"), this));
+ KLineEdit *name = new KLineEdit(sign->text(), this);
+ m_vlayout->addWidget(name);
+ connect(name, SIGNAL(textChanged(const QString &)), this, SLOT(textChanged(const QString &)));
+}
+
+void SignConfig::textChanged(const QString &text)
+{
+ sign->setText(text);
+ changed();
+}
+
+/////////////////////////
+
+EllipseConfig::EllipseConfig(Ellipse *ellipse, QWidget *parent)
+ : Config(parent), slow1(0), fast1(0), slow2(0), fast2(0), slider1(0), slider2(0)
+{
+ this->ellipse = ellipse;
+
+ m_vlayout = new QVBoxLayout(this, marginHint(), spacingHint());
+
+ QCheckBox *check = new QCheckBox(i18n("Enable show/hide"), this);
+ m_vlayout->addWidget(check);
+ connect(check, SIGNAL(toggled(bool)), this, SLOT(check1Changed(bool)));
+ check->setChecked(ellipse->changeEnabled());
+
+ QHBoxLayout *hlayout = new QHBoxLayout(m_vlayout, spacingHint());
+ slow1 = new QLabel(i18n("Slow"), this);
+ hlayout->addWidget(slow1);
+ slider1 = new QSlider(1, 100, 5, 100 - ellipse->changeEvery(), Qt::Horizontal, this);
+ hlayout->addWidget(slider1);
+ fast1 = new QLabel(i18n("Fast"), this);
+ hlayout->addWidget(fast1);
+
+ connect(slider1, SIGNAL(valueChanged(int)), this, SLOT(value1Changed(int)));
+
+ check1Changed(ellipse->changeEnabled());
+
+ // TODO add slider2 and friends and make it possible for ellipses to grow and contract
+
+ m_vlayout->addStretch();
+}
+
+void EllipseConfig::value1Changed(int news)
+{
+ ellipse->setChangeEvery(100 - news);
+ changed();
+}
+
+void EllipseConfig::value2Changed(int /*news*/)
+{
+ changed();
+}
+
+void EllipseConfig::check1Changed(bool on)
+{
+ ellipse->setChangeEnabled(on);
+ if (slider1)
+ slider1->setEnabled(on);
+ if (slow1)
+ slow1->setEnabled(on);
+ if (fast1)
+ fast1->setEnabled(on);
+
+ changed();
+}
+
+void EllipseConfig::check2Changed(bool on)
+{
+ //ellipse->setChangeEnabled(on);
+ if (slider2)
+ slider2->setEnabled(on);
+ if (slow2)
+ slow2->setEnabled(on);
+ if (fast2)
+ fast2->setEnabled(on);
+
+ changed();
+}
+
+/////////////////////////
+
+Ellipse::Ellipse(QCanvas *canvas)
+ : QCanvasEllipse(canvas)
+{
+ savingDone();
+ setChangeEnabled(false);
+ setChangeEvery(50);
+ count = 0;
+ setVisible(true);
+
+ point = new RectPoint(black, this, canvas);
+ point->setSizeFactor(2.0);
+}
+
+void Ellipse::aboutToDie()
+{
+ delete point;
+}
+
+void Ellipse::setChangeEnabled(bool changeEnabled)
+{
+ m_changeEnabled = changeEnabled;
+ setAnimated(m_changeEnabled);
+
+ if (!m_changeEnabled)
+ setVisible(true);
+}
+
+QPtrList<QCanvasItem> Ellipse::moveableItems() const
+{
+ QPtrList<QCanvasItem> ret;
+ ret.append(point);
+ return ret;
+}
+
+void Ellipse::newSize(int width, int height)
+{
+ QCanvasEllipse::setSize(width, height);
+}
+
+void Ellipse::moveBy(double dx, double dy)
+{
+ QCanvasEllipse::moveBy(dx, dy);
+
+ point->dontMove();
+ point->move(x() + width() / 2, y() + height() / 2);
+}
+
+void Ellipse::editModeChanged(bool changed)
+{
+ point->setVisible(changed);
+ moveBy(0, 0);
+}
+
+void Ellipse::advance(int phase)
+{
+ QCanvasEllipse::advance(phase);
+
+ if (phase == 1 && m_changeEnabled && !dontHide)
+ {
+ if (count > (m_changeEvery + 10) * 1.8)
+ count = 0;
+ if (count == 0)
+ setVisible(!isVisible());
+
+ count++;
+ }
+}
+
+void Ellipse::load(KConfig *cfg)
+{
+ setChangeEnabled(cfg->readBoolEntry("changeEnabled", changeEnabled()));
+ setChangeEvery(cfg->readNumEntry("changeEvery", changeEvery()));
+ double newWidth = width(), newHeight = height();
+ newWidth = cfg->readNumEntry("width", newWidth);
+ newHeight = cfg->readNumEntry("height", newHeight);
+ newSize(newWidth, newHeight);
+}
+
+void Ellipse::save(KConfig *cfg)
+{
+ cfg->writeEntry("changeEvery", changeEvery());
+ cfg->writeEntry("changeEnabled", changeEnabled());
+ cfg->writeEntry("width", width());
+ cfg->writeEntry("height", height());
+}
+
+Config *Ellipse::config(QWidget *parent)
+{
+ return new EllipseConfig(this, parent);
+}
+
+void Ellipse::aboutToSave()
+{
+ setVisible(true);
+ dontHide = true;
+}
+
+void Ellipse::savingDone()
+{
+ dontHide = false;
+}
+
+/////////////////////////
+
+Puddle::Puddle(QCanvas *canvas)
+ : Ellipse(canvas)
+{
+ setSize(45, 30);
+
+ QBrush brush;
+ QPixmap pic;
+
+ if (!QPixmapCache::find("puddle", pic))
+ {
+ pic.load(locate("appdata", "pics/puddle.png"));
+ QPixmapCache::insert("puddle", pic);
+ }
+
+ brush.setPixmap(pic);
+ setBrush(brush);
+
+ KPixmap pointPic(pic);
+ KPixmapEffect::intensity(pointPic, .45);
+ brush.setPixmap(pointPic);
+ point->setBrush(brush);
+
+ setZ(-25);
+}
+
+bool Puddle::collision(Ball *ball, long int /*id*/)
+{
+ if (ball->isVisible())
+ {
+ QCanvasRectangle i(QRect(ball->x(), ball->y(), 1, 1), canvas());
+ i.setVisible(true);
+
+ // is center of ball in?
+ if (i.collidesWith(this)/* && ball->curVector().magnitude() < 4*/)
+ {
+ playSound("puddle");
+ ball->setAddStroke(ball->addStroke() + 1);
+ ball->setPlaceOnGround(true);
+ ball->setVisible(false);
+ ball->setState(Stopped);
+ ball->setVelocity(0, 0);
+ if (game && game->curBall() == ball)
+ game->stoppedBall();
+ }
+ else
+ return true;
+ }
+
+ return false;
+}
+
+/////////////////////////
+
+Sand::Sand(QCanvas *canvas)
+ : Ellipse(canvas)
+{
+ setSize(45, 40);
+
+ QBrush brush;
+ QPixmap pic;
+
+ if (!QPixmapCache::find("sand", pic))
+ {
+ pic.load(locate("appdata", "pics/sand.png"));
+ QPixmapCache::insert("sand", pic);
+ }
+
+ brush.setPixmap(pic);
+ setBrush(brush);
+
+ KPixmap pointPic(pic);
+ KPixmapEffect::intensity(pointPic, .45);
+ brush.setPixmap(pointPic);
+ point->setBrush(brush);
+
+ setZ(-26);
+}
+
+bool Sand::collision(Ball *ball, long int /*id*/)
+{
+ QCanvasRectangle i(QRect(ball->x(), ball->y(), 1, 1), canvas());
+ i.setVisible(true);
+
+ // is center of ball in?
+ if (i.collidesWith(this)/* && ball->curVector().magnitude() < 4*/)
+ {
+ if (ball->curVector().magnitude() > 0)
+ ball->setFrictionMultiplier(7);
+ else
+ {
+ ball->setVelocity(0, 0);
+ ball->setState(Stopped);
+ }
+ }
+
+ return true;
+}
+
+/////////////////////////
+
+Putter::Putter(QCanvas *canvas)
+ : QCanvasLine(canvas)
+{
+ m_showGuideLine = true;
+ oneDegree = M_PI / 180;
+ len = 9;
+ angle = 0;
+
+ guideLine = new QCanvasLine(canvas);
+ guideLine->setPen(QPen(white, 1, QPen::DotLine));
+ guideLine->setZ(998.8);
+
+ setPen(QPen(black, 4));
+ putterWidth = 11;
+ maxAngle = 2 * M_PI;
+
+ hideInfo();
+
+ // this also sets Z
+ resetAngles();
+}
+
+void Putter::showInfo()
+{
+ guideLine->setVisible(isVisible());
+}
+
+void Putter::hideInfo()
+{
+ guideLine->setVisible(m_showGuideLine? isVisible() : false);
+}
+
+void Putter::moveBy(double dx, double dy)
+{
+ QCanvasLine::moveBy(dx, dy);
+ guideLine->move(x(), y());
+}
+
+void Putter::setShowGuideLine(bool yes)
+{
+ m_showGuideLine = yes;
+ setVisible(isVisible());
+}
+
+void Putter::setVisible(bool yes)
+{
+ QCanvasLine::setVisible(yes);
+ guideLine->setVisible(m_showGuideLine? yes : false);
+}
+
+void Putter::setOrigin(int _x, int _y)
+{
+ setVisible(true);
+ move(_x, _y);
+ len = 9;
+ finishMe();
+}
+
+void Putter::setAngle(Ball *ball)
+{
+ angle = angleMap.contains(ball)? angleMap[ball] : 0;
+ finishMe();
+}
+
+void Putter::go(Direction d, Amount amount)
+{
+ double addition = (amount == Amount_More? 6 * oneDegree : amount == Amount_Less? .5 * oneDegree : 2 * oneDegree);
+
+ switch (d)
+ {
+ case Forwards:
+ len -= 1;
+ guideLine->setVisible(false);
+ break;
+ case Backwards:
+ len += 1;
+ guideLine->setVisible(false);
+ break;
+ case D_Left:
+ angle += addition;
+ if (angle > maxAngle)
+ angle -= maxAngle;
+ break;
+ case D_Right:
+ angle -= addition;
+ if (angle < 0)
+ angle = maxAngle - fabs(angle);
+ break;
+ }
+
+ finishMe();
+}
+
+void Putter::finishMe()
+{
+ midPoint.setX(cos(angle) * len);
+ midPoint.setY(-sin(angle) * len);
+
+ QPoint start;
+ QPoint end;
+
+ if (midPoint.y() || !midPoint.x())
+ {
+ start.setX(midPoint.x() - putterWidth * sin(angle));
+ start.setY(midPoint.y() - putterWidth * cos(angle));
+ end.setX(midPoint.x() + putterWidth * sin(angle));
+ end.setY(midPoint.y() + putterWidth * cos(angle));
+ }
+ else
+ {
+ start.setX(midPoint.x());
+ start.setY(midPoint.y() + putterWidth);
+ end.setY(midPoint.y() - putterWidth);
+ end.setX(midPoint.x());
+ }
+
+ guideLine->setPoints(midPoint.x(), midPoint.y(), -cos(angle) * len * 4, sin(angle) * len * 4);
+
+ setPoints(start.x(), start.y(), end.x(), end.y());
+}
+
+/////////////////////////
+
+Bumper::Bumper(QCanvas *canvas)
+ : QCanvasEllipse(20, 20, canvas)
+{
+ setZ(-25);
+
+ firstColor = QColor("#E74804");
+ secondColor = firstColor.light();
+
+ count = 0;
+ setBrush(firstColor);
+ setAnimated(false);
+
+ inside = new Inside(this, canvas);
+ inside->setBrush(firstColor.light(109));
+ inside->setSize(width() / 2.6, height() / 2.6);
+ inside->show();
+}
+
+void Bumper::aboutToDie()
+{
+ delete inside;
+}
+
+void Bumper::moveBy(double dx, double dy)
+{
+ QCanvasEllipse::moveBy(dx, dy);
+ //const double insideLen = (double)(width() - inside->width()) / 2.0;
+ inside->move(x(), y());
+}
+
+void Bumper::editModeChanged(bool changed)
+{
+ inside->setVisible(!changed);
+}
+
+void Bumper::advance(int phase)
+{
+ QCanvasEllipse::advance(phase);
+
+ if (phase == 1)
+ {
+ count++;
+ if (count > 2)
+ {
+ count = 0;
+ setBrush(firstColor);
+ update();
+ setAnimated(false);
+ }
+ }
+}
+
+bool Bumper::collision(Ball *ball, long int /*id*/)
+{
+ setBrush(secondColor);
+
+ double speed = 1.8 + ball->curVector().magnitude() * .9;
+ if (speed > 8)
+ speed = 8;
+
+ const QPoint start(x(), y());
+ const QPoint end(ball->x(), ball->y());
+
+ Vector betweenVector(start, end);
+ betweenVector.setMagnitude(speed);
+
+ // add some randomness so we don't go indefinetely
+ betweenVector.setDirection(betweenVector.direction() + deg2rad((kapp->random() % 3) - 1));
+
+ ball->setVector(betweenVector);
+ // for some reason, x is always switched...
+ ball->setXVelocity(-ball->xVelocity());
+ ball->setState(Rolling);
+
+ setAnimated(true);
+
+ return true;
+}
+
+/////////////////////////
+
+Hole::Hole(QColor color, QCanvas *canvas)
+ : QCanvasEllipse(15, 15, canvas)
+{
+ setZ(998.1);
+ setPen(black);
+ setBrush(color);
+}
+
+bool Hole::collision(Ball *ball, long int /*id*/)
+{
+ bool wasCenter = false;
+
+ switch (result(QPoint(ball->x(), ball->y()), ball->curVector().magnitude(), &wasCenter))
+ {
+ case Result_Holed:
+ place(ball, wasCenter);
+ return false;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+HoleResult Hole::result(QPoint p, double s, bool * /*wasCenter*/)
+{
+ const double longestRadius = width() > height()? width() : height();
+ if (s > longestRadius / 5.0)
+ return Result_Miss;
+
+ QCanvasRectangle i(QRect(p, QSize(1, 1)), canvas());
+ i.setVisible(true);
+
+ // is center of ball in cup?
+ if (i.collidesWith(this))
+ {
+ return Result_Holed;
+ }
+ else
+ return Result_Miss;
+}
+
+/////////////////////////
+
+Cup::Cup(QCanvas *canvas)
+ : Hole(QColor("#808080"), canvas)
+{
+ if (!QPixmapCache::find("cup", pixmap))
+ {
+ pixmap.load(locate("appdata", "pics/cup.png"));
+ QPixmapCache::insert("cup", pixmap);
+ }
+}
+
+void Cup::draw(QPainter &p)
+{
+ p.drawPixmap(QPoint(x() - width() / 2, y() - height() / 2), pixmap);
+}
+
+bool Cup::place(Ball *ball, bool /*wasCenter*/)
+{
+ ball->setState(Holed);
+ playSound("holed");
+
+ // the picture's center is a little different
+ ball->move(x() - 1, y());
+ ball->setVelocity(0, 0);
+ if (game && game->curBall() == ball)
+ game->stoppedBall();
+ return true;
+}
+
+void Cup::save(KConfig *cfg)
+{
+ cfg->writeEntry("dummykey", true);
+}
+
+/////////////////////////
+
+BlackHole::BlackHole(QCanvas *canvas)
+ : Hole(black, canvas), exitDeg(0)
+{
+ infoLine = 0;
+ m_minSpeed = 3.0;
+ m_maxSpeed = 5.0;
+ runs = 0;
+
+ const QColor myColor((QRgb)(kapp->random() % 0x01000000));
+
+ outside = new QCanvasEllipse(canvas);
+ outside->setZ(z() - .001);
+
+ outside->setBrush(QBrush(myColor));
+ setBrush(black);
+
+ exitItem = new BlackHoleExit(this, canvas);
+ exitItem->setPen(QPen(myColor, 6));
+ exitItem->setX(300);
+ exitItem->setY(100);
+
+ setSize(width(), width() / .8);
+ const float factor = 1.3;
+ outside->setSize(width() * factor, height() * factor);
+ outside->setVisible(true);
+
+ moveBy(0, 0);
+
+ finishMe();
+}
+
+void BlackHole::showInfo()
+{
+ delete infoLine;
+ infoLine = new QCanvasLine(canvas());
+ infoLine->setVisible(true);
+ infoLine->setPen(QPen(exitItem->pen().color(), 2));
+ infoLine->setZ(10000);
+ infoLine->setPoints(x(), y(), exitItem->x(), exitItem->y());
+
+ exitItem->showInfo();
+}
+
+void BlackHole::hideInfo()
+{
+ delete infoLine;
+ infoLine = 0;
+
+ exitItem->hideInfo();
+}
+
+void BlackHole::aboutToDie()
+{
+ Hole::aboutToDie();
+ delete outside;
+ exitItem->aboutToDie();
+ delete exitItem;
+}
+
+void BlackHole::updateInfo()
+{
+ if (infoLine)
+ {
+ infoLine->setVisible(true);
+ infoLine->setPoints(x(), y(), exitItem->x(), exitItem->y());
+ exitItem->showInfo();
+ }
+}
+
+void BlackHole::moveBy(double dx, double dy)
+{
+ QCanvasEllipse::moveBy(dx, dy);
+ outside->move(x(), y());
+ updateInfo();
+}
+
+void BlackHole::setExitDeg(int newdeg)
+{
+ exitDeg = newdeg;
+ if (game && game->isEditing() && game->curSelectedItem() == exitItem)
+ game->updateHighlighter();
+
+ exitItem->updateArrowAngle();
+ finishMe();
+}
+
+QPtrList<QCanvasItem> BlackHole::moveableItems() const
+{
+ QPtrList<QCanvasItem> ret;
+ ret.append(exitItem);
+ return ret;
+}
+
+BlackHoleTimer::BlackHoleTimer(Ball *ball, double speed, int msec)
+ : m_speed(speed), m_ball(ball)
+{
+ QTimer::singleShot(msec, this, SLOT(mySlot()));
+ QTimer::singleShot(msec / 2, this, SLOT(myMidSlot()));
+}
+
+void BlackHoleTimer::mySlot()
+{
+ emit eject(m_ball, m_speed);
+ delete this;
+}
+
+void BlackHoleTimer::myMidSlot()
+{
+ emit halfway();
+}
+
+bool BlackHole::place(Ball *ball, bool /*wasCenter*/)
+{
+ // most number is 10
+ if (runs > 10 && game && game->isInPlay())
+ return false;
+
+ playSound("blackholeputin");
+
+ const double diff = (m_maxSpeed - m_minSpeed);
+ const double speed = m_minSpeed + ball->curVector().magnitude() * (diff / 3.75);
+
+ ball->setVelocity(0, 0);
+ ball->setState(Stopped);
+ ball->setVisible(false);
+ ball->setForceStillGoing(true);
+
+ double magnitude = Vector(QPoint(x(), y()), QPoint(exitItem->x(), exitItem->y())).magnitude();
+ BlackHoleTimer *timer = new BlackHoleTimer(ball, speed, magnitude * 2.5 - speed * 35 + 500);
+
+ connect(timer, SIGNAL(eject(Ball *, double)), this, SLOT(eject(Ball *, double)));
+ connect(timer, SIGNAL(halfway()), this, SLOT(halfway()));
+
+ playSound("blackhole");
+ return false;
+}
+
+void BlackHole::eject(Ball *ball, double speed)
+{
+ ball->move(exitItem->x(), exitItem->y());
+
+ Vector v;
+ v.setMagnitude(10);
+ v.setDirection(deg2rad(exitDeg));
+ ball->setVector(v);
+
+ // advance ball 10
+ ball->doAdvance();
+
+ v.setMagnitude(speed);
+ ball->setVector(v);
+
+ ball->setForceStillGoing(false);
+ ball->setVisible(true);
+ ball->setState(Rolling);
+
+ runs++;
+
+ playSound("blackholeeject");
+}
+
+void BlackHole::halfway()
+{
+ playSound("blackhole");
+}
+
+void BlackHole::load(KConfig *cfg)
+{
+ QPoint exit = cfg->readPointEntry("exit", &exit);
+ exitItem->setX(exit.x());
+ exitItem->setY(exit.y());
+ exitDeg = cfg->readNumEntry("exitDeg", exitDeg);
+ m_minSpeed = cfg->readDoubleNumEntry("minspeed", m_minSpeed);
+ m_maxSpeed = cfg->readDoubleNumEntry("maxspeed", m_maxSpeed);
+ exitItem->updateArrowAngle();
+ exitItem->updateArrowLength();
+
+ finishMe();
+}
+
+void BlackHole::finishMe()
+{
+ double radians = deg2rad(exitDeg);
+ QPoint midPoint(0, 0);
+ QPoint start;
+ QPoint end;
+ const int width = 15;
+
+ if (midPoint.y() || !midPoint.x())
+ {
+ start.setX(midPoint.x() - width*sin(radians));
+ start.setY(midPoint.y() - width*cos(radians));
+ end.setX(midPoint.x() + width*sin(radians));
+ end.setY(midPoint.y() + width*cos(radians));
+ }
+ else
+ {
+ start.setX(midPoint.x());
+ start.setY(midPoint.y() + width);
+ end.setY(midPoint.y() - width);
+ end.setX(midPoint.x());
+ }
+
+ exitItem->setPoints(start.x(), start.y(), end.x(), end.y());
+ exitItem->setVisible(true);
+}
+
+void BlackHole::save(KConfig *cfg)
+{
+ cfg->writeEntry("exit", QPoint(exitItem->x(), exitItem->y()));
+ cfg->writeEntry("exitDeg", exitDeg);
+ cfg->writeEntry("minspeed", m_minSpeed);
+ cfg->writeEntry("maxspeed", m_maxSpeed);
+}
+
+/////////////////////////
+
+BlackHoleExit::BlackHoleExit(BlackHole *blackHole, QCanvas *canvas)
+ : QCanvasLine(canvas)
+{
+ this->blackHole = blackHole;
+ arrow = new Arrow(canvas);
+ setZ(blackHole->z());
+ arrow->setZ(z() - .00001);
+ updateArrowLength();
+ arrow->setVisible(false);
+}
+
+void BlackHoleExit::aboutToDie()
+{
+ arrow->aboutToDie();
+ delete arrow;
+}
+
+void BlackHoleExit::moveBy(double dx, double dy)
+{
+ QCanvasLine::moveBy(dx, dy);
+ arrow->move(x(), y());
+ blackHole->updateInfo();
+}
+
+void BlackHoleExit::setPen(QPen p)
+{
+ QCanvasLine::setPen(p);
+ arrow->setPen(QPen(p.color(), 1));
+}
+
+void BlackHoleExit::updateArrowAngle()
+{
+ // arrows work in a different angle system
+ arrow->setAngle(-deg2rad(blackHole->curExitDeg()));
+ arrow->updateSelf();
+}
+
+void BlackHoleExit::updateArrowLength()
+{
+ arrow->setLength(10.0 + 5.0 * (double)(blackHole->minSpeed() + blackHole->maxSpeed()) / 2.0);
+ arrow->updateSelf();
+}
+
+void BlackHoleExit::editModeChanged(bool editing)
+{
+ if (editing)
+ showInfo();
+ else
+ hideInfo();
+}
+
+void BlackHoleExit::showInfo()
+{
+ arrow->setVisible(true);
+}
+
+void BlackHoleExit::hideInfo()
+{
+ arrow->setVisible(false);
+}
+
+Config *BlackHoleExit::config(QWidget *parent)
+{
+ return blackHole->config(parent);
+}
+
+/////////////////////////
+
+BlackHoleConfig::BlackHoleConfig(BlackHole *blackHole, QWidget *parent)
+ : Config(parent)
+{
+ this->blackHole = blackHole;
+ QVBoxLayout *layout = new QVBoxLayout(this, marginHint(), spacingHint());
+ layout->addWidget(new QLabel(i18n("Exiting ball angle:"), this));
+ QSpinBox *deg = new QSpinBox(0, 359, 10, this);
+ deg->setSuffix(QString(" ") + i18n("degrees"));
+ deg->setValue(blackHole->curExitDeg());
+ deg->setWrapping(true);
+ layout->addWidget(deg);
+ connect(deg, SIGNAL(valueChanged(int)), this, SLOT(degChanged(int)));
+
+ layout->addStretch();
+
+ QHBoxLayout *hlayout = new QHBoxLayout(layout, spacingHint());
+ hlayout->addWidget(new QLabel(i18n("Minimum exit speed:"), this));
+ KDoubleNumInput *min = new KDoubleNumInput(this);
+ min->setRange(0, 8, 1, true);
+ hlayout->addWidget(min);
+ connect(min, SIGNAL(valueChanged(double)), this, SLOT(minChanged(double)));
+ min->setValue(blackHole->minSpeed());
+
+ hlayout = new QHBoxLayout(layout, spacingHint());
+ hlayout->addWidget(new QLabel(i18n("Maximum:"), this));
+ KDoubleNumInput *max = new KDoubleNumInput(this);
+ max->setRange(1, 10, 1, true);
+ hlayout->addWidget(max);
+ connect(max, SIGNAL(valueChanged(double)), this, SLOT(maxChanged(double)));
+ max->setValue(blackHole->maxSpeed());
+}
+
+void BlackHoleConfig::degChanged(int newdeg)
+{
+ blackHole->setExitDeg(newdeg);
+ changed();
+}
+
+void BlackHoleConfig::minChanged(double news)
+{
+ blackHole->setMinSpeed(news);
+ changed();
+}
+
+void BlackHoleConfig::maxChanged(double news)
+{
+ blackHole->setMaxSpeed(news);
+ changed();
+}
+
+/////////////////////////
+
+WallPoint::WallPoint(bool start, Wall *wall, QCanvas *canvas)
+ : QCanvasEllipse(canvas)
+{
+ this->wall = wall;
+ this->start = start;
+ alwaysShow = false;
+ editing = false;
+ visible = true;
+ lastId = INT_MAX - 10;
+ dontmove = false;
+
+ move(0, 0);
+ QPoint p;
+ if (start)
+ p = wall->startPoint();
+ else
+ p = wall->endPoint();
+ setX(p.x());
+ setY(p.y());
+}
+
+void WallPoint::clean()
+{
+ int oldWidth = width();
+ setSize(7, 7);
+ update();
+
+ QCanvasItem *onPoint = 0;
+ QCanvasItemList l = collisions(true);
+ for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it)
+ if ((*it)->rtti() == rtti())
+ onPoint = (*it);
+
+ if (onPoint)
+ move(onPoint->x(), onPoint->y());
+
+ setSize(oldWidth, oldWidth);
+}
+
+void WallPoint::moveBy(double dx, double dy)
+{
+ QCanvasEllipse::moveBy(dx, dy);
+ if (!editing)
+ updateVisible();
+
+ if (dontmove)
+ {
+ dontmove = false;
+ return;
+ }
+
+ if (!wall)
+ return;
+
+ if (start)
+ {
+ wall->setPoints(x(), y(), wall->endPoint().x() + wall->x(), wall->endPoint().y() + wall->y());
+ }
+ else
+ {
+ wall->setPoints(wall->startPoint().x() + wall->x(), wall->startPoint().y() + wall->y(), x(), y());
+ }
+ wall->move(0, 0);
+}
+
+void WallPoint::updateVisible()
+{
+ if (!wall->isVisible())
+ {
+ visible = false;
+ return;
+ }
+
+ if (alwaysShow)
+ visible = true;
+ else
+ {
+ visible = true;
+ QCanvasItemList l = collisions(true);
+ for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it)
+ if ((*it)->rtti() == rtti())
+ visible = false;
+ }
+}
+
+void WallPoint::editModeChanged(bool changed)
+{
+ editing = changed;
+ setVisible(true);
+ if (!editing)
+ updateVisible();
+}
+
+bool WallPoint::collision(Ball *ball, long int id)
+{
+ if (ball->curVector().magnitude() <= 0)
+ return false;
+
+ long int tempLastId = lastId;
+ lastId = id;
+ QCanvasItemList l = collisions(true);
+ for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it)
+ {
+ if ((*it)->rtti() == rtti())
+ {
+ WallPoint *point = (WallPoint *)(*it);
+ point->lastId = id;
+ }
+ }
+
+ //kdDebug(12007) << "WallPoint::collision id: " << id << ", tempLastId: " << tempLastId << endl;
+ Vector ballVector(ball->curVector());
+
+ //kdDebug(12007) << "Wall::collision ball speed: " << ball->curVector().magnitude() << endl;
+ int allowableDifference = 1;
+ if (ballVector.magnitude() < .30)
+ allowableDifference = 8;
+ else if (ballVector.magnitude() < .50)
+ allowableDifference = 6;
+ else if (ballVector.magnitude() < .65)
+ allowableDifference = 4;
+ else if (ballVector.magnitude() < .95)
+ allowableDifference = 2;
+
+ if (abs(id - tempLastId) <= allowableDifference)
+ {
+ //kdDebug(12007) << "WallPoint::collision - SKIP\n";
+ }
+ else
+ {
+ bool weirdBounce = visible;
+
+ QPoint relStart(start? wall->startPoint() : wall->endPoint());
+ QPoint relEnd(start? wall->endPoint() : wall->startPoint());
+ Vector wallVector(relStart, relEnd);
+ wallVector.setDirection(-wallVector.direction());
+
+ // find the angle between vectors, between 0 and PI
+ {
+ double difference = fabs(wallVector.direction() - ballVector.direction());
+ while (difference > 2 * M_PI)
+ difference -= 2 * M_PI;
+
+ if (difference < M_PI / 2 || difference > 3 * M_PI / 2)
+ weirdBounce = false;
+ }
+
+ playSound("wall", ball->curVector().magnitude() / 10.0);
+
+ ballVector /= wall->dampening;
+ const double ballAngle = ballVector.direction();
+
+ double wallAngle = wallVector.direction();
+
+ // opposite bounce, because we're the endpoint
+ if (weirdBounce)
+ wallAngle += M_PI / 2;
+
+ const double collisionAngle = ballAngle - wallAngle;
+ const double leavingAngle = wallAngle - collisionAngle;
+
+ ballVector.setDirection(leavingAngle);
+ ball->setVector(ballVector);
+ wall->lastId = id;
+
+ //kdDebug(12007) << "WallPoint::collision - NOT skip, weirdBounce is " << weirdBounce << endl;
+ } // end if that skips
+
+ wall->lastId = id;
+ return false;
+}
+
+/////////////////////////
+
+Wall::Wall(QCanvas *canvas)
+ : QCanvasLine(canvas)
+{
+ editing = false;
+ lastId = INT_MAX - 10;
+
+ dampening = 1.2;
+
+ startItem = 0;
+ endItem = 0;
+
+ moveBy(0, 0);
+ setZ(50);
+
+ startItem = new WallPoint(true, this, canvas);
+ endItem = new WallPoint(false, this, canvas);
+ startItem->setVisible(true);
+ endItem->setVisible(true);
+ setPen(QPen(darkRed, 3));
+
+ setPoints(-15, 10, 15, -5);
+
+ moveBy(0, 0);
+
+ editModeChanged(false);
+}
+
+void Wall::selectedItem(QCanvasItem *item)
+{
+ if (item->rtti() == Rtti_WallPoint)
+ {
+ WallPoint *wallPoint = dynamic_cast<WallPoint *>(item);
+ if (wallPoint) {
+ setPoints(startPoint().x(), startPoint().y(), wallPoint->x() - x(), wallPoint->y() - y());
+ }
+ }
+}
+
+void Wall::clean()
+{
+ startItem->clean();
+ endItem->clean();
+}
+
+void Wall::setAlwaysShow(bool yes)
+{
+ startItem->setAlwaysShow(yes);
+ endItem->setAlwaysShow(yes);
+}
+
+void Wall::setVisible(bool yes)
+{
+ QCanvasLine::setVisible(yes);
+
+ startItem->setVisible(yes);
+ endItem->setVisible(yes);
+ startItem->updateVisible();
+ endItem->updateVisible();
+}
+
+void Wall::setZ(double newz)
+{
+ QCanvasLine::setZ(newz);
+ if (startItem)
+ startItem->setZ(newz + .002);
+ if (endItem)
+ endItem->setZ(newz + .001);
+}
+
+void Wall::setPen(QPen p)
+{
+ QCanvasLine::setPen(p);
+
+ if (startItem)
+ startItem->setBrush(QBrush(p.color()));
+ if (endItem)
+ endItem->setBrush(QBrush(p.color()));
+}
+
+void Wall::aboutToDie()
+{
+ delete startItem;
+ delete endItem;
+}
+
+void Wall::setGame(KolfGame *game)
+{
+ CanvasItem::setGame(game);
+ startItem->setGame(game);
+ endItem->setGame(game);
+}
+
+QPtrList<QCanvasItem> Wall::moveableItems() const
+{
+ QPtrList<QCanvasItem> ret;
+ ret.append(startItem);
+ ret.append(endItem);
+ return ret;
+}
+
+void Wall::moveBy(double dx, double dy)
+{
+ QCanvasLine::moveBy(dx, dy);
+
+ if (!startItem || !endItem)
+ return;
+
+ startItem->dontMove();
+ endItem->dontMove();
+ startItem->move(startPoint().x() + x(), startPoint().y() + y());
+ endItem->move(endPoint().x() + x(), endPoint().y() + y());
+}
+
+void Wall::setVelocity(double vx, double vy)
+{
+ QCanvasLine::setVelocity(vx, vy);
+ /*
+ startItem->setVelocity(vx, vy);
+ endItem->setVelocity(vx, vy);
+ */
+}
+
+QPointArray Wall::areaPoints() const
+{
+ // editing we want full width for easy moving
+ if (editing)
+ return QCanvasLine::areaPoints();
+
+ // lessen width, for QCanvasLine::areaPoints() likes
+ // to make lines _very_ fat :(
+ // from qcanvas.cpp, only the stuff for a line width of 1 taken
+
+ // it's all squished because I don't want my
+ // line counts to count code I didn't write!
+ QPointArray p(4); const int xi = int(x()); const int yi = int(y()); const QPoint start = startPoint(); const QPoint end = endPoint(); const int x1 = start.x(); const int x2 = end.x(); const int y1 = start.y(); const int y2 = end.y(); const int dx = QABS(x1-x2); const int dy = QABS(y1-y2); if ( dx > dy ) { p[0] = QPoint(x1+xi,y1+yi-1); p[1] = QPoint(x2+xi,y2+yi-1); p[2] = QPoint(x2+xi,y2+yi+1); p[3] = QPoint(x1+xi,y1+yi+1); } else { p[0] = QPoint(x1+xi-1,y1+yi); p[1] = QPoint(x2+xi-1,y2+yi); p[2] = QPoint(x2+xi+1,y2+yi); p[3] = QPoint(x1+xi+1,y1+yi); } return p;
+}
+
+void Wall::editModeChanged(bool changed)
+{
+ // make big for debugging?
+ const bool debugPoints = false;
+
+ editing = changed;
+
+ startItem->setZ(z() + .002);
+ endItem->setZ(z() + .001);
+ startItem->editModeChanged(editing);
+ endItem->editModeChanged(editing);
+
+ int neww = 0;
+ if (changed || debugPoints)
+ neww = 10;
+ else
+ neww = pen().width();
+
+ startItem->setSize(neww, neww);
+ endItem->setSize(neww, neww);
+
+ moveBy(0, 0);
+}
+
+bool Wall::collision(Ball *ball, long int id)
+{
+ if (ball->curVector().magnitude() <= 0)
+ return false;
+
+ long int tempLastId = lastId;
+ lastId = id;
+ startItem->lastId = id;
+ endItem->lastId = id;
+
+ //kdDebug(12007) << "Wall::collision id: " << id << ", tempLastId: " << tempLastId << endl;
+ Vector ballVector(ball->curVector());
+
+ //kdDebug(12007) << "Wall::collision ball speed: " << ball->curVector().magnitude() << endl;
+ int allowableDifference = 1;
+ if (ballVector.magnitude() < .30)
+ allowableDifference = 8;
+ else if (ballVector.magnitude() < .50)
+ allowableDifference = 6;
+ else if (ballVector.magnitude() < .75)
+ allowableDifference = 4;
+ else if (ballVector.magnitude() < .95)
+ allowableDifference = 2;
+ //kdDebug(12007) << "Wall::collision allowableDifference is " << allowableDifference << endl;
+ if (abs(id - tempLastId) <= allowableDifference)
+ {
+ //kdDebug(12007) << "Wall::collision - SKIP\n";
+ return false;
+ }
+
+ playSound("wall", ball->curVector().magnitude() / 10.0);
+
+ ballVector /= dampening;
+ const double ballAngle = ballVector.direction();
+
+ const double wallAngle = -Vector(startPoint(), endPoint()).direction();
+ const double collisionAngle = ballAngle - wallAngle;
+ const double leavingAngle = wallAngle - collisionAngle;
+
+ ballVector.setDirection(leavingAngle);
+ ball->setVector(ballVector);
+
+ //kdDebug(12007) << "Wall::collision - NOT skip\n";
+ return false;
+}
+
+void Wall::load(KConfig *cfg)
+{
+ QPoint start(startPoint());
+ start = cfg->readPointEntry("startPoint", &start);
+ QPoint end(endPoint());
+ end = cfg->readPointEntry("endPoint", &end);
+
+ setPoints(start.x(), start.y(), end.x(), end.y());
+
+ moveBy(0, 0);
+ startItem->move(start.x(), start.y());
+ endItem->move(end.x(), end.y());
+}
+
+void Wall::save(KConfig *cfg)
+{
+ cfg->writeEntry("startPoint", QPoint(startItem->x(), startItem->y()));
+ cfg->writeEntry("endPoint", QPoint(endItem->x(), endItem->y()));
+}
+
+/////////////////////////
+
+HoleConfig::HoleConfig(HoleInfo *holeInfo, QWidget *parent)
+ : Config(parent)
+{
+ this->holeInfo = holeInfo;
+
+ QVBoxLayout *layout = new QVBoxLayout(this, marginHint(), spacingHint());
+
+ QHBoxLayout *hlayout = new QHBoxLayout(layout, spacingHint());
+ hlayout->addWidget(new QLabel(i18n("Course name: "), this));
+ KLineEdit *nameEdit = new KLineEdit(holeInfo->untranslatedName(), this);
+ hlayout->addWidget(nameEdit);
+ connect(nameEdit, SIGNAL(textChanged(const QString &)), this, SLOT(nameChanged(const QString &)));
+
+ hlayout = new QHBoxLayout(layout, spacingHint());
+ hlayout->addWidget(new QLabel(i18n("Course author: "), this));
+ KLineEdit *authorEdit = new KLineEdit(holeInfo->author(), this);
+ hlayout->addWidget(authorEdit);
+ connect(authorEdit, SIGNAL(textChanged(const QString &)), this, SLOT(authorChanged(const QString &)));
+
+ layout->addStretch();
+
+ hlayout = new QHBoxLayout(layout, spacingHint());
+ hlayout->addWidget(new QLabel(i18n("Par:"), this));
+ QSpinBox *par = new QSpinBox(1, 15, 1, this);
+ par->setValue(holeInfo->par());
+ hlayout->addWidget(par);
+ connect(par, SIGNAL(valueChanged(int)), this, SLOT(parChanged(int)));
+ hlayout->addStretch();
+
+ hlayout->addWidget(new QLabel(i18n("Maximum:"), this));
+ QSpinBox *maxstrokes = new QSpinBox(holeInfo->lowestMaxStrokes(), 30, 1, this);
+ QWhatsThis::add(maxstrokes, i18n("Maximum number of strokes player can take on this hole."));
+ QToolTip::add(maxstrokes, i18n("Maximum number of strokes"));
+ maxstrokes->setSpecialValueText(i18n("Unlimited"));
+ maxstrokes->setValue(holeInfo->maxStrokes());
+ hlayout->addWidget(maxstrokes);
+ connect(maxstrokes, SIGNAL(valueChanged(int)), this, SLOT(maxStrokesChanged(int)));
+
+ QCheckBox *check = new QCheckBox(i18n("Show border walls"), this);
+ check->setChecked(holeInfo->borderWalls());
+ layout->addWidget(check);
+ connect(check, SIGNAL(toggled(bool)), this, SLOT(borderWallsChanged(bool)));
+}
+
+void HoleConfig::authorChanged(const QString &newauthor)
+{
+ holeInfo->setAuthor(newauthor);
+ changed();
+}
+
+void HoleConfig::nameChanged(const QString &newname)
+{
+ holeInfo->setName(newname);
+ holeInfo->setUntranslatedName(newname);
+ changed();
+}
+
+void HoleConfig::parChanged(int newpar)
+{
+ holeInfo->setPar(newpar);
+ changed();
+}
+
+void HoleConfig::maxStrokesChanged(int newms)
+{
+ holeInfo->setMaxStrokes(newms);
+ changed();
+}
+
+void HoleConfig::borderWallsChanged(bool yes)
+{
+ holeInfo->borderWallsChanged(yes);
+ changed();
+}
+
+/////////////////////////
+
+StrokeCircle::StrokeCircle(QCanvas *canvas)
+ : QCanvasItem(canvas)
+{
+ dvalue = 0;
+ dmax = 360;
+ iwidth = 100;
+ iheight = 100;
+ ithickness = 8;
+ setZ(10000);
+}
+
+void StrokeCircle::setValue(double v)
+{
+ dvalue = v;
+ if (dvalue > dmax)
+ dvalue = dmax;
+
+ update();
+}
+
+double StrokeCircle::value()
+{
+ return dvalue;
+}
+
+bool StrokeCircle::collidesWith(const QCanvasItem*) const { return false; }
+
+bool StrokeCircle::collidesWith(const QCanvasSprite*, const QCanvasPolygonalItem*, const QCanvasRectangle*, const QCanvasEllipse*, const QCanvasText*) const { return false; }
+
+QRect StrokeCircle::boundingRect() const { return QRect(x(), y(), iwidth, iheight); }
+
+void StrokeCircle::setMaxValue(double m)
+{
+ dmax = m;
+ if (dvalue > dmax)
+ dvalue = dmax;
+
+ update();
+}
+void StrokeCircle::setSize(int w, int h)
+{
+ if (w > 0)
+ iwidth = w;
+ if (h > 0)
+ iheight = h;
+
+ update();
+}
+void StrokeCircle::setThickness(int t)
+{
+ if (t > 0)
+ ithickness = t;
+
+ update();
+}
+
+int StrokeCircle::thickness() const
+{
+ return ithickness;
+}
+
+int StrokeCircle::width() const
+{
+ return iwidth;
+}
+
+int StrokeCircle::height() const
+{
+ return iheight;
+}
+
+void StrokeCircle::draw(QPainter &p)
+{
+ int al = (int)((dvalue * 360 * 16) / dmax);
+ int length, deg;
+ if (al < 0)
+ {
+ deg = 270 * 16;
+ length = -al;
+ }
+ else if (al <= (270 * 16))
+ {
+ deg = 270 * 16 - al;
+ length = al;
+ }
+ else
+ {
+ deg = (360 * 16) - (al - (270 * 16));
+ length = al;
+ }
+
+ p.setBrush(QBrush(black, Qt::NoBrush));
+ p.setPen(QPen(white, ithickness / 2));
+ p.drawEllipse(x() + ithickness / 2, y() + ithickness / 2, iwidth - ithickness, iheight - ithickness);
+ p.setPen(QPen(QColor((int)(0xff * dvalue) / dmax, 0, 0xff - (int)(0xff * dvalue) / dmax), ithickness));
+ p.drawArc(x() + ithickness / 2, y() + ithickness / 2, iwidth - ithickness, iheight - ithickness, deg, length);
+
+ p.setPen(QPen(white, 1));
+ p.drawEllipse(x(), y(), iwidth, iheight);
+ p.drawEllipse(x() + ithickness, y() + ithickness, iwidth - ithickness * 2, iheight - ithickness * 2);
+ p.setPen(QPen(white, 3));
+ p.drawLine(x() + iwidth / 2, y() + iheight - ithickness * 1.5, x() + iwidth / 2, y() + iheight);
+ p.drawLine(x() + iwidth / 4 - iwidth / 20, y() + iheight - iheight / 4 + iheight / 20, x() + iwidth / 4 + iwidth / 20, y() + iheight - iheight / 4 - iheight / 20);
+ p.drawLine(x() + iwidth - iwidth / 4 + iwidth / 20, y() + iheight - iheight / 4 + iheight / 20, x() + iwidth - iwidth / 4 - iwidth / 20, y() + iheight - iheight / 4 - iheight / 20);
+}
+
+/////////////////////////////////////////
+
+KolfGame::KolfGame(ObjectList *obj, PlayerList *players, QString filename, QWidget *parent, const char *name )
+ : QCanvasView(parent, name)
+{
+ // for mouse control
+ setMouseTracking(true);
+ viewport()->setMouseTracking(true);
+ setFrameShape(NoFrame);
+
+ regAdv = false;
+ curHole = 0; // will get ++'d
+ cfg = 0;
+ setFilename(filename);
+ this->players = players;
+ this->obj = obj;
+ curPlayer = players->end();
+ curPlayer--; // will get ++'d to end and sent back
+ // to beginning
+ paused = false;
+ modified = false;
+ inPlay = false;
+ putting = false;
+ stroking = false;
+ editing = false;
+ strict = false;
+ lastDelId = -1;
+ m_showInfo = false;
+ ballStateList.canUndo = false;
+ fastAdvancedExist = false;
+ soundDir = locate("appdata", "sounds/");
+ dontAddStroke = false;
+ addingNewHole = false;
+ scoreboardHoles = 0;
+ infoShown = false;
+ m_useMouse = true;
+ m_useAdvancedPutting = false;
+ m_useAdvancedPutting = true;
+ m_sound = true;
+ m_ignoreEvents = false;
+ soundedOnce = false;
+ oldPlayObjects.setAutoDelete(true);
+ highestHole = 0;
+ recalcHighestHole = false;
+
+ holeInfo.setGame(this);
+ holeInfo.setAuthor(i18n("Course Author"));
+ holeInfo.setName(i18n("Course Name"));
+ holeInfo.setUntranslatedName(i18n("Course Name"));
+ holeInfo.setMaxStrokes(10);
+ holeInfo.borderWallsChanged(true);
+
+ // width and height are the width and height of the canvas
+ // in easy storage
+ width = 400;
+ height = 400;
+ grass = QColor("#35760D");
+
+ margin = 10;
+
+ setFocusPolicy(QWidget::StrongFocus);
+ setFixedSize(width + 2 * margin, height + 2 * margin);
+
+ setMargins(margin, margin, margin, margin);
+
+ course = new QCanvas(this);
+ course->setBackgroundColor(white);
+ course->resize(width, height);
+
+ QPixmap pic;
+ if (!QPixmapCache::find("grass", pic))
+ {
+ pic.load(locate("appdata", "pics/grass.png"));
+ QPixmapCache::insert("grass", pic);
+ }
+ course->setBackgroundPixmap(pic);
+
+ setCanvas(course);
+ move(0, 0);
+ adjustSize();
+
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ (*it).ball()->setCanvas(course);
+
+ // highlighter shows current item
+ highlighter = new QCanvasRectangle(course);
+ highlighter->setPen(QPen(yellow, 1));
+ highlighter->setBrush(QBrush(NoBrush));
+ highlighter->setVisible(false);
+ highlighter->setZ(10000);
+
+ // shows some info about hole
+ infoText = new QCanvasText(course);
+ infoText->setText("");
+ infoText->setColor(white);
+ QFont font = kapp->font();
+ font.setPixelSize(12);
+ infoText->move(15, width/2);
+ infoText->setZ(10001);
+ infoText->setFont(font);
+ infoText->setVisible(false);
+
+ // create the advanced putting indicator
+ strokeCircle = new StrokeCircle(course);
+ strokeCircle->move(width - 90, height - 90);
+ strokeCircle->setSize(80, 80);
+ strokeCircle->setThickness(8);
+ strokeCircle->setVisible(false);
+ strokeCircle->setValue(0);
+ strokeCircle->setMaxValue(360);
+
+ // whiteBall marks the spot of the whole whilst editing
+ whiteBall = new Ball(course);
+ whiteBall->setGame(this);
+ whiteBall->setColor(white);
+ whiteBall->setVisible(false);
+ whiteBall->setDoDetect(false);
+
+ int highestLog = 0;
+
+ // if players have scores from loaded game, move to last hole
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ {
+ if ((int)(*it).scores().count() > highestLog)
+ highestLog = (*it).scores().count();
+
+ (*it).ball()->setGame(this);
+ (*it).ball()->setAnimated(true);
+ }
+
+ // here only for saved games
+ if (highestLog)
+ curHole = highestLog;
+
+ putter = new Putter(course);
+
+ // border walls:
+
+ // horiz
+ addBorderWall(QPoint(margin, margin), QPoint(width - margin, margin));
+ addBorderWall(QPoint(margin, height - margin - 1), QPoint(width - margin, height - margin - 1));
+
+ // vert
+ addBorderWall(QPoint(margin, margin), QPoint(margin, height - margin));
+ addBorderWall(QPoint(width - margin - 1, margin), QPoint(width - margin - 1, height - margin));
+
+ timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()), this, SLOT(timeout()));
+ timerMsec = 300;
+
+ fastTimer = new QTimer(this);
+ connect(fastTimer, SIGNAL(timeout()), this, SLOT(fastTimeout()));
+ fastTimerMsec = 11;
+
+ autoSaveTimer = new QTimer(this);
+ connect(autoSaveTimer, SIGNAL(timeout()), this, SLOT(autoSaveTimeout()));
+ autoSaveMsec = 5 * 1000 * 60; // 5 min autosave
+
+ // setUseAdvancedPutting() sets maxStrength!
+ setUseAdvancedPutting(false);
+
+ putting = false;
+ putterTimer = new QTimer(this);
+ connect(putterTimer, SIGNAL(timeout()), this, SLOT(putterTimeout()));
+ putterTimerMsec = 20;
+}
+
+void KolfGame::startFirstHole(int hole)
+{
+ if (curHole > 0) // if there was saved game, sync scoreboard
+ // with number of holes
+ {
+ for (; scoreboardHoles < curHole; ++scoreboardHoles)
+ {
+ cfg->setGroup(QString("%1-hole@-50,-50|0").arg(scoreboardHoles + 1));
+ emit newHole(cfg->readNumEntry("par", 3));
+ }
+
+ // lets load all of the scores from saved game if there are any
+ for (int hole = 1; hole <= curHole; ++hole)
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ emit scoreChanged((*it).id(), hole, (*it).score(hole));
+ }
+
+ curHole = hole - 1;
+
+ // this increments curHole, etc
+ recalcHighestHole = true;
+ startNextHole();
+ paused = true;
+ unPause();
+}
+
+void KolfGame::setFilename(const QString &filename)
+{
+ this->filename = filename;
+ delete cfg;
+ cfg = new KConfig(filename, false, false);
+}
+
+KolfGame::~KolfGame()
+{
+ oldPlayObjects.clear();
+ delete cfg;
+}
+
+void KolfGame::setModified(bool mod)
+{
+ modified = mod;
+ emit modifiedChanged(mod);
+}
+
+void KolfGame::pause()
+{
+ if (paused)
+ {
+ // play along with people who call pause() again, instead of unPause()
+ unPause();
+ return;
+ }
+
+ paused = true;
+ timer->stop();
+ fastTimer->stop();
+ putterTimer->stop();
+}
+
+void KolfGame::unPause()
+{
+ if (!paused)
+ return;
+
+ paused = false;
+
+ timer->start(timerMsec);
+ fastTimer->start(fastTimerMsec);
+
+ if (putting || stroking)
+ putterTimer->start(putterTimerMsec);
+}
+
+void KolfGame::addBorderWall(QPoint start, QPoint end)
+{
+ Wall *wall = new Wall(course);
+ wall->setPoints(start.x(), start.y(), end.x(), end.y());
+ wall->setVisible(true);
+ wall->setGame(this);
+ wall->setZ(998.7);
+ borderWalls.append(wall);
+}
+
+void KolfGame::updateHighlighter()
+{
+ if (!selectedItem)
+ return;
+ QRect rect = selectedItem->boundingRect();
+ highlighter->move(rect.x() + 1, rect.y() + 1);
+ highlighter->setSize(rect.width(), rect.height());
+}
+
+void KolfGame::handleMouseDoubleClickEvent(QMouseEvent *e)
+{
+ // allow two fast single clicks
+ handleMousePressEvent(e);
+}
+
+void KolfGame::handleMousePressEvent(QMouseEvent *e)
+{
+ if (m_ignoreEvents)
+ return;
+
+ if (editing)
+ {
+ if (inPlay)
+ return;
+
+ storedMousePos = e->pos();
+
+ QCanvasItemList list = course->collisions(e->pos());
+ if (list.first() == highlighter)
+ list.pop_front();
+
+ moving = false;
+ highlighter->setVisible(false);
+ selectedItem = 0;
+ movingItem = 0;
+
+ if (list.count() < 1)
+ {
+ emit newSelectedItem(&holeInfo);
+ return;
+ }
+ // only items we keep track of
+ if ((!(items.containsRef(list.first()) || list.first() == whiteBall || extraMoveable.containsRef(list.first()))))
+ {
+ emit newSelectedItem(&holeInfo);
+ return;
+ }
+
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(list.first());
+ if (!citem || !citem->moveable())
+ {
+ emit newSelectedItem(&holeInfo);
+ return;
+ }
+
+ switch (e->button())
+ {
+ // select AND move now :)
+ case LeftButton:
+ {
+ selectedItem = list.first();
+ movingItem = selectedItem;
+ moving = true;
+
+ if (citem->cornerResize())
+ setCursor(KCursor::sizeFDiagCursor());
+ else
+ setCursor(KCursor::sizeAllCursor());
+
+ emit newSelectedItem(citem);
+ highlighter->setVisible(true);
+ QRect rect = selectedItem->boundingRect();
+ highlighter->move(rect.x() + 1, rect.y() + 1);
+ highlighter->setSize(rect.width(), rect.height());
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ if (m_useMouse)
+ {
+ if (!inPlay && e->button() == LeftButton)
+ puttPress();
+ else if (e->button() == RightButton)
+ toggleShowInfo();
+ }
+ }
+
+ setFocus();
+}
+
+QPoint KolfGame::viewportToViewport(const QPoint &p)
+{
+ // for some reason viewportToContents doesn't work right
+ return p - QPoint(margin, margin);
+}
+
+// the following four functions are needed to handle both
+// border presses and regular in-course presses
+
+void KolfGame::mouseReleaseEvent(QMouseEvent * e)
+{
+ QMouseEvent fixedEvent (QEvent::MouseButtonRelease, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state());
+ handleMouseReleaseEvent(&fixedEvent);
+}
+
+void KolfGame::mousePressEvent(QMouseEvent * e)
+{
+ QMouseEvent fixedEvent (QEvent::MouseButtonPress, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state());
+ handleMousePressEvent(&fixedEvent);
+}
+
+void KolfGame::mouseDoubleClickEvent(QMouseEvent * e)
+{
+ QMouseEvent fixedEvent (QEvent::MouseButtonDblClick, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state());
+ handleMouseDoubleClickEvent(&fixedEvent);
+}
+
+void KolfGame::mouseMoveEvent(QMouseEvent * e)
+{
+ QMouseEvent fixedEvent (QEvent::MouseMove, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state());
+ handleMouseMoveEvent(&fixedEvent);
+}
+
+void KolfGame::handleMouseMoveEvent(QMouseEvent *e)
+{
+ if (inPlay || !putter || m_ignoreEvents)
+ return;
+
+ QPoint mouse = e->pos();
+
+ // mouse moving of putter
+ if (!editing)
+ {
+ updateMouse();
+ return;
+ }
+
+ if (!moving)
+ {
+ // lets change the cursor to a hand
+ // if we're hovering over something
+
+ QCanvasItemList list = course->collisions(e->pos());
+ if (list.count() > 0)
+ setCursor(KCursor::handCursor());
+ else
+ setCursor(KCursor::arrowCursor());
+ return;
+ }
+
+ int moveX = storedMousePos.x() - mouse.x();
+ int moveY = storedMousePos.y() - mouse.y();
+
+ // moving counts as modifying
+ if (moveX || moveY)
+ setModified(true);
+
+ highlighter->moveBy(-(double)moveX, -(double)moveY);
+ movingItem->moveBy(-(double)moveX, -(double)moveY);
+ QRect brect = movingItem->boundingRect();
+ emit newStatusText(QString("%1x%2").arg(brect.x()).arg(brect.y()));
+ storedMousePos = mouse;
+}
+
+void KolfGame::updateMouse()
+{
+ // don't move putter if in advanced putting sequence
+ if (!m_useMouse || ((stroking || putting) && m_useAdvancedPutting))
+ return;
+
+ const QPoint cursor = viewportToViewport(viewportToContents(mapFromGlobal(QCursor::pos())));
+ const QPoint ball((*curPlayer).ball()->x(), (*curPlayer).ball()->y());
+ putter->setAngle(-Vector(cursor, ball).direction());
+}
+
+void KolfGame::handleMouseReleaseEvent(QMouseEvent *e)
+{
+ setCursor(KCursor::arrowCursor());
+
+ if (editing)
+ {
+ emit newStatusText(QString::null);
+ moving = false;
+ }
+
+ if (m_ignoreEvents)
+ return;
+
+ if (!editing && m_useMouse)
+ {
+ if (!inPlay && e->button() == LeftButton)
+ puttRelease();
+ else if (e->button() == RightButton)
+ toggleShowInfo();
+ }
+
+ setFocus();
+}
+
+void KolfGame::keyPressEvent(QKeyEvent *e)
+{
+ if (inPlay || editing || m_ignoreEvents)
+ return;
+
+ switch (e->key())
+ {
+ case Key_Up:
+ if (!e->isAutoRepeat())
+ toggleShowInfo();
+ break;
+
+ case Key_Escape:
+ putting = false;
+ stroking = false;
+ finishStroking = false;
+ strokeCircle->setVisible(false);
+ putterTimer->stop();
+ putter->setOrigin((*curPlayer).ball()->x(), (*curPlayer).ball()->y());
+ break;
+
+ case Key_Left:
+ case Key_Right:
+ // don't move putter if in advanced putting sequence
+ if ((!stroking && !putting) || !m_useAdvancedPutting)
+ putter->go(e->key() == Key_Left? D_Left : D_Right, e->state() & ShiftButton? Amount_More : e->state() & ControlButton? Amount_Less : Amount_Normal);
+ break;
+
+ case Key_Space: case Key_Down:
+ puttPress();
+ break;
+
+ default:
+ break;
+ }
+}
+
+void KolfGame::toggleShowInfo()
+{
+ setShowInfo(!m_showInfo);
+}
+
+void KolfGame::updateShowInfo()
+{
+ setShowInfo(m_showInfo);
+}
+
+void KolfGame::setShowInfo(bool yes)
+{
+ m_showInfo = yes;
+
+ if (m_showInfo)
+ {
+ QCanvasItem *item = 0;
+ for (item = items.first(); item; item = items.next())
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(item);
+ if (citem)
+ citem->showInfo();
+ }
+
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ (*it).ball()->showInfo();
+
+ showInfo();
+ }
+ else
+ {
+ QCanvasItem *item = 0;
+ for (item = items.first(); item; item = items.next())
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(item);
+ if (citem)
+ citem->hideInfo();
+ }
+
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ (*it).ball()->hideInfo();
+
+ hideInfo();
+ }
+}
+
+void KolfGame::puttPress()
+{
+ // Advanced putting: 1st click start putting sequence, 2nd determine strength, 3rd determine precision
+
+ if (!putting && !stroking && !inPlay)
+ {
+ puttCount = 0;
+ puttReverse = false;
+ putting = true;
+ stroking = false;
+ strength = 0;
+ if (m_useAdvancedPutting)
+ {
+ strokeCircle->setValue(0);
+ int pw = putter->endPoint().x() - putter->startPoint().x();
+ if (pw < 0) pw = -pw;
+ int px = (int)putter->x() + pw / 2;
+ int py = (int)putter->y();
+ if (px > width / 2 && py < height / 2)
+ strokeCircle->move(px - pw / 2 - 10 - strokeCircle->width(), py + 10);
+ else if (px > width / 2)
+ strokeCircle->move(px - pw / 2 - 10 - strokeCircle->width(), py - 10 - strokeCircle->height());
+ else if (py < height / 2)
+ strokeCircle->move(px + pw / 2 + 10, py + 10);
+ else
+ strokeCircle->move(px + pw / 2 + 10, py - 10 - strokeCircle->height());
+ strokeCircle->setVisible(true);
+ }
+ putterTimer->start(putterTimerMsec);
+ }
+ else if (m_useAdvancedPutting && putting && !editing)
+ {
+ putting = false;
+ stroking = true;
+ puttReverse = false;
+ finishStroking = false;
+ }
+ else if (m_useAdvancedPutting && stroking)
+ {
+ finishStroking = true;
+ putterTimeout();
+ }
+}
+
+void KolfGame::keyReleaseEvent(QKeyEvent *e)
+{
+ if (e->isAutoRepeat() || m_ignoreEvents)
+ return;
+
+ if (e->key() == Key_Space || e->key() == Key_Down)
+ puttRelease();
+ else if ((e->key() == Key_Backspace || e->key() == Key_Delete) && !(e->state() & ControlButton))
+ {
+ if (editing && !moving && selectedItem)
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(selectedItem);
+ if (!citem)
+ return;
+ citem = citem->itemToDelete();
+ if (!citem)
+ return;
+ QCanvasItem *item = dynamic_cast<QCanvasItem *>(citem);
+ if (citem && citem->deleteable())
+ {
+ lastDelId = citem->curId();
+
+ highlighter->setVisible(false);
+ items.removeRef(item);
+ citem->hideInfo();
+ citem->aboutToDelete();
+ citem->aboutToDie();
+ delete citem;
+ selectedItem = 0;
+ emit newSelectedItem(&holeInfo);
+
+ setModified(true);
+ }
+ }
+ }
+ else if (e->key() == Key_I || e->key() == Key_Up)
+ toggleShowInfo();
+}
+
+void KolfGame::puttRelease()
+{
+ if (!m_useAdvancedPutting && putting && !editing)
+ {
+ putting = false;
+ stroking = true;
+ }
+}
+
+void KolfGame::stoppedBall()
+{
+ if (!inPlay)
+ {
+ inPlay = true;
+ dontAddStroke = true;
+ }
+}
+
+void KolfGame::timeout()
+{
+ Ball *curBall = (*curPlayer).ball();
+
+ // test if the ball is gone
+ // in this case we want to stop the ball and
+ // later undo the shot
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ {
+ if (!course->rect().contains(QPoint((*it).ball()->x(), (*it).ball()->y())))
+ {
+ (*it).ball()->setState(Stopped);
+
+ // don't do it if he's past maxStrokes
+ if ((*it).score(curHole) < holeInfo.maxStrokes() - 1 || !holeInfo.hasMaxStrokes())
+ {
+ loadStateList();
+ }
+ shotDone();
+
+ return;
+ }
+ }
+
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ if ((*it).ball()->forceStillGoing() || ((*it).ball()->curState() == Rolling && (*it).ball()->curVector().magnitude() > 0 && (*it).ball()->isVisible()))
+ return;
+
+ int curState = curBall->curState();
+ if (curState == Stopped && inPlay)
+ {
+ inPlay = false;
+ QTimer::singleShot(0, this, SLOT(shotDone()));
+ }
+
+ if (curState == Holed && inPlay)
+ {
+ emit inPlayEnd();
+ emit playerHoled(&(*curPlayer));
+
+ int curScore = (*curPlayer).score(curHole);
+ if (!dontAddStroke)
+ curScore++;
+
+ if (curScore == 1)
+ {
+ playSound("holeinone");
+ }
+ else if (curScore <= holeInfo.par())
+ {
+ // I don't have a sound!!
+ // *sob*
+ // playSound("woohoo");
+ }
+
+ (*curPlayer).ball()->setZ((*curPlayer).ball()->z() + .1 - (.1)/(curScore));
+
+ if (allPlayersDone())
+ {
+ inPlay = false;
+
+ if (curHole > 0 && !dontAddStroke)
+ {
+ (*curPlayer).addStrokeToHole(curHole);
+ emit scoreChanged((*curPlayer).id(), curHole, (*curPlayer).score(curHole));
+ }
+ QTimer::singleShot(600, this, SLOT(holeDone()));
+ }
+ else
+ {
+ inPlay = false;
+ QTimer::singleShot(0, this, SLOT(shotDone()));
+ }
+ }
+}
+
+void KolfGame::fastTimeout()
+{
+ // do regular advance every other time
+ if (regAdv)
+ course->advance();
+ regAdv = !regAdv;
+
+ if (!editing)
+ {
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ (*it).ball()->doAdvance();
+
+ if (fastAdvancedExist)
+ {
+ CanvasItem *citem = 0;
+ for (citem = fastAdvancers.first(); citem; citem = fastAdvancers.next())
+ citem->doAdvance();
+ }
+
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ (*it).ball()->fastAdvanceDone();
+
+ if (fastAdvancedExist)
+ {
+ CanvasItem *citem = 0;
+ for (citem = fastAdvancers.first(); citem; citem = fastAdvancers.next())
+ citem->fastAdvanceDone();
+ }
+ }
+}
+
+void KolfGame::ballMoved()
+{
+ if (putter->isVisible())
+ {
+ putter->move((*curPlayer).ball()->x(), (*curPlayer).ball()->y());
+ updateMouse();
+ }
+}
+
+void KolfGame::putterTimeout()
+{
+ if (inPlay || editing)
+ return;
+
+ if (m_useAdvancedPutting)
+ {
+ if (putting)
+ {
+ const float base = 2.0;
+
+ if (puttReverse && strength <= 0)
+ {
+ // aborted
+ putting = false;
+ strokeCircle->setVisible(false);
+ }
+ else if (strength > maxStrength || puttReverse)
+ {
+ // decreasing strength as we've reached the top
+ puttReverse = true;
+ strength -= pow(base, strength / maxStrength) - 1.8;
+ if ((int)strength < puttCount * 2)
+ {
+ puttCount--;
+ if (puttCount >= 0)
+ putter->go(Forwards);
+ }
+ }
+ else
+ {
+ // make the increase at high strength faster
+ strength += pow(base, strength / maxStrength) - .3;
+ if ((int)strength > puttCount * 2)
+ {
+ putter->go(Backwards);
+ puttCount++;
+ }
+ }
+ // make the visible steps at high strength smaller
+ strokeCircle->setValue(pow(strength / maxStrength, 0.8) * 360);
+ }
+ else if (stroking)
+ {
+ double al = strokeCircle->value();
+ if (al >= 45)
+ al -= 0.2 + strength / 50 + al / 100;
+ else
+ al -= 0.2 + strength / 50;
+
+ if (puttReverse)
+ {
+ // show the stroke
+ puttCount--;
+ if (puttCount >= 0)
+ putter->go(Forwards);
+ else
+ {
+ strokeCircle->setVisible(false);
+ finishStroking = false;
+ putterTimer->stop();
+ putting = false;
+ stroking = false;
+ shotStart();
+ }
+ }
+ else if (al < -45 || finishStroking)
+ {
+ strokeCircle->setValue(al);
+ int deg;
+ // if > 45 or < -45 then bad stroke
+ if (al > 45)
+ {
+ deg = putter->curDeg() - 45 + rand() % 90;
+ strength -= rand() % (int)strength;
+ }
+ else if (!finishStroking)
+ {
+ deg = putter->curDeg() - 45 + rand() % 90;
+ strength -= rand() % (int)strength;
+ }
+ else
+ deg = putter->curDeg() + (int)(strokeCircle->value() / 3);
+
+ if (deg < 0)
+ deg += 360;
+ else if (deg > 360)
+ deg -= 360;
+
+ putter->setDeg(deg);
+ puttReverse = true;
+ }
+ else
+ {
+ strokeCircle->setValue(al);
+ putterTimer->changeInterval(putterTimerMsec/10);
+ }
+ }
+
+ }
+ else
+ {
+ if (putting)
+ {
+ putter->go(Backwards);
+ puttCount++;
+ strength += 1.5;
+ if (strength > maxStrength)
+ {
+ putting = false;
+ stroking = true;
+ }
+ }
+ else if (stroking)
+ {
+ if (putter->curLen() < (*curPlayer).ball()->height() + 2)
+ {
+ stroking = false;
+ putterTimer->stop();
+ putting = false;
+ stroking = false;
+ shotStart();
+ }
+
+ putter->go(Forwards);
+ putterTimer->changeInterval(putterTimerMsec/10);
+ }
+ }
+}
+
+void KolfGame::autoSaveTimeout()
+{
+ // this should be a config option
+ // until it is i'll disable it
+ if (editing)
+ {
+ //save();
+ }
+}
+
+void KolfGame::recreateStateList()
+{
+ stateDB.clear();
+
+ QCanvasItem *item = 0;
+
+ for (item = items.first(); item; item = items.next())
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(item);
+ if (citem)
+ {
+ stateDB.setName(makeStateGroup(citem->curId(), citem->name()));
+ citem->saveState(&stateDB);
+ }
+ }
+
+ ballStateList.clear();
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ ballStateList.append((*it).stateInfo(curHole));
+
+ ballStateList.canUndo = true;
+}
+
+void KolfGame::undoShot()
+{
+ if (ballStateList.canUndo)
+ loadStateList();
+}
+
+void KolfGame::loadStateList()
+{
+ QCanvasItem *item = 0;
+
+ for (item = items.first(); item; item = items.next())
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(item);
+ if (citem)
+ {
+ stateDB.setName(makeStateGroup(citem->curId(), citem->name()));
+ citem->loadState(&stateDB);
+ }
+ }
+
+ for (BallStateList::Iterator it = ballStateList.begin(); it != ballStateList.end(); ++it)
+ {
+ BallStateInfo info = (*it);
+ Player &player = (*players->at(info.id - 1));
+ player.ball()->move(info.spot.x(), info.spot.y());
+ player.ball()->setBeginningOfHole(info.beginningOfHole);
+ if ((*curPlayer).id() == info.id)
+ ballMoved();
+ else
+ player.ball()->setVisible(!info.beginningOfHole);
+ player.setScoreForHole(info.score, curHole);
+ player.ball()->setState(info.state);
+ emit scoreChanged(info.id, curHole, info.score);
+ }
+}
+
+void KolfGame::shotDone()
+{
+ inPlay = false;
+ emit inPlayEnd();
+ setFocus();
+
+ Ball *ball = (*curPlayer).ball();
+ double oldx = ball->x(), oldy = ball->y();
+
+ if (!dontAddStroke && (*curPlayer).numHoles())
+ (*curPlayer).addStrokeToHole(curHole);
+
+ dontAddStroke = false;
+
+ // do hack stuff, shouldn't be done here
+
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ {
+ if ((*it).ball()->addStroke())
+ {
+ for (int i = 1; i <= (*it).ball()->addStroke(); ++i)
+ (*it).addStrokeToHole(curHole);
+
+ // emit that we have a new stroke count
+ emit scoreChanged((*it).id(), curHole, (*it).score(curHole));
+ }
+ (*it).ball()->setAddStroke(0);
+ }
+
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ {
+ Ball *ball = (*it).ball();
+
+ if (ball->curState() == Holed)
+ continue;
+
+ Vector v;
+ if (ball->placeOnGround(v))
+ {
+ ball->setPlaceOnGround(false);
+
+ QStringList options;
+ const QString placeOutside = i18n("Drop Outside of Hazard");
+ const QString rehit = i18n("Rehit From Last Location");
+ options << placeOutside << rehit;
+ const QString choice = KComboBoxDialog::getItem(i18n("What would you like to do for your next shot?"), i18n("%1 is in a Hazard").arg((*it).name()), options, placeOutside, "hazardOptions");
+
+ if (choice == placeOutside)
+ {
+ (*it).ball()->setDoDetect(false);
+
+ double x = ball->x(), y = ball->y();
+
+ while (1)
+ {
+ QCanvasItemList list = ball->collisions(true);
+ bool keepMoving = false;
+ while (!list.isEmpty())
+ {
+ QCanvasItem *item = list.first();
+ if (item->rtti() == Rtti_DontPlaceOn)
+ keepMoving = true;
+
+ list.pop_front();
+ }
+ if (!keepMoving)
+ break;
+
+ const float movePixel = 3.0;
+ x -= cos(v.direction()) * movePixel;
+ y += sin(v.direction()) * movePixel;
+
+ ball->move(x, y);
+ }
+
+ // move another two pixels away
+ x -= cos(v.direction()) * 2;
+ y += sin(v.direction()) * 2;
+ }
+ else if (choice == rehit)
+ {
+ for (BallStateList::Iterator it = ballStateList.begin(); it != ballStateList.end(); ++it)
+ {
+ if ((*it).id == (*curPlayer).id())
+ {
+ if ((*it).beginningOfHole)
+ ball->move(whiteBall->x(), whiteBall->y());
+ else
+ ball->move((*it).spot.x(), (*it).spot.y());
+
+ break;
+ }
+ }
+ }
+
+ ball->setVisible(true);
+ ball->setState(Stopped);
+
+ (*it).ball()->setDoDetect(true);
+ ball->collisionDetect(oldx, oldy);
+ }
+ }
+
+ // emit again
+ emit scoreChanged((*curPlayer).id(), curHole, (*curPlayer).score(curHole));
+
+ ball->setVelocity(0, 0);
+
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ {
+ Ball *ball = (*it).ball();
+
+ int curStrokes = (*it).score(curHole);
+ if (curStrokes >= holeInfo.maxStrokes() && holeInfo.hasMaxStrokes())
+ {
+ ball->setState(Holed);
+ ball->setVisible(false);
+
+ // move to center in case he/she hit out
+ ball->move(width / 2, height / 2);
+ playerWhoMaxed = (*it).name();
+
+ if (allPlayersDone())
+ {
+ startNextHole();
+ QTimer::singleShot(100, this, SLOT(emitMax()));
+ return;
+ }
+
+ QTimer::singleShot(100, this, SLOT(emitMax()));
+ }
+ }
+
+ // change player to next player
+ // skip player if he's Holed
+ do
+ {
+ curPlayer++;
+ if (curPlayer == players->end())
+ curPlayer = players->begin();
+ }
+ while ((*curPlayer).ball()->curState() == Holed);
+
+ emit newPlayersTurn(&(*curPlayer));
+
+ (*curPlayer).ball()->setVisible(true);
+
+ putter->setAngle((*curPlayer).ball());
+ putter->setOrigin((*curPlayer).ball()->x(), (*curPlayer).ball()->y());
+ updateMouse();
+
+ inPlay = false;
+ (*curPlayer).ball()->collisionDetect(oldx, oldy);
+}
+
+void KolfGame::emitMax()
+{
+ emit maxStrokesReached(playerWhoMaxed);
+}
+
+void KolfGame::startBall(const Vector &vector)
+{
+ playSound("hit");
+
+ emit inPlayStart();
+ putter->setVisible(false);
+
+ (*curPlayer).ball()->setState(Rolling);
+ (*curPlayer).ball()->setVector(vector);
+
+ QCanvasItem *item = 0;
+ for (item = items.first(); item; item = items.next())
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(item);
+ if (citem)
+ citem->shotStarted();
+ }
+
+ inPlay = true;
+}
+
+void KolfGame::shotStart()
+{
+ // ensure we never hit the ball back into the hole which
+ // can cause hole skippage
+ if ((*curPlayer).ball()->curState() == Holed)
+ return;
+
+ // save state
+ recreateStateList();
+
+ putter->saveAngle((*curPlayer).ball());
+ strength /= 8;
+ if (!strength)
+ strength = 1;
+
+ startBall(Vector(strength, putter->curAngle() + M_PI));
+
+ addHoleInfo(ballStateList);
+}
+
+void KolfGame::addHoleInfo(BallStateList &list)
+{
+ list.player = (*curPlayer).id();
+ list.vector = (*curPlayer).ball()->curVector();
+ list.hole = curHole;
+}
+
+void KolfGame::sayWhosGoing()
+{
+ if (players->count() >= 2)
+ {
+ KMessageBox::information(this, i18n("%1 will start off.").arg((*curPlayer).name()), i18n("New Hole"), "newHole");
+ }
+}
+
+void KolfGame::holeDone()
+{
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ (*it).ball()->setVisible(false);
+ startNextHole();
+ sayWhosGoing();
+}
+
+// this function is WAY too smart for it's own good
+// ie, bad design :-(
+void KolfGame::startNextHole()
+{
+ setFocus();
+
+ bool reset = true;
+ if (askSave(true))
+ {
+ if (allPlayersDone())
+ {
+ // we'll reload this hole, but not reset
+ curHole--;
+ reset = false;
+ }
+ else
+ return;
+ }
+ else
+ setModified(false);
+
+ pause();
+
+ dontAddStroke = false;
+
+ inPlay = false;
+ timer->stop();
+ putter->resetAngles();
+
+ int oldCurHole = curHole;
+ curHole++;
+ emit currentHole(curHole);
+
+ if (reset)
+ {
+ whiteBall->move(width/2, height/2);
+ holeInfo.borderWallsChanged(true);
+ }
+
+ int leastScore = INT_MAX;
+
+ // to get the first player to go first on every hole,
+ // don't do the score stuff below
+ curPlayer = players->begin();
+ double oldx=(*curPlayer).ball()->x(), oldy=(*curPlayer).ball()->y();
+
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ {
+ if (curHole > 1)
+ {
+ bool ahead = false;
+ if ((*it).lastScore() != 0)
+ {
+ if ((*it).lastScore() < leastScore)
+ ahead = true;
+ else if ((*it).lastScore() == leastScore)
+ {
+ for (int i = curHole - 1; i > 0; --i)
+ {
+ const int thisScore = (*it).score(i);
+ const int thatScore = (*curPlayer).score(i);
+ if (thisScore < thatScore)
+ {
+ ahead = true;
+ break;
+ }
+ else if (thisScore > thatScore)
+ break;
+ }
+ }
+ }
+
+ if (ahead)
+ {
+ curPlayer = it;
+ leastScore = (*it).lastScore();
+ }
+ }
+
+ if (reset)
+ (*it).ball()->move(width / 2, height / 2);
+ else
+ (*it).ball()->move(whiteBall->x(), whiteBall->y());
+
+ (*it).ball()->setState(Stopped);
+
+ // this gets set to false when the ball starts
+ // to move by the Mr. Ball himself.
+ (*it).ball()->setBeginningOfHole(true);
+ if ((int)(*it).scores().count() < curHole)
+ (*it).addHole();
+ (*it).ball()->setVelocity(0, 0);
+ (*it).ball()->setVisible(false);
+ }
+
+ emit newPlayersTurn(&(*curPlayer));
+
+ if (reset)
+ openFile();
+
+ inPlay = false;
+ timer->start(timerMsec);
+
+ // if (false) { we're done with the round! }
+ if (oldCurHole != curHole)
+ {
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ (*it).ball()->setPlaceOnGround(false);
+
+ // here we have to make sure the scoreboard shows
+ // all of the holes up until now;
+
+ for (; scoreboardHoles < curHole; ++scoreboardHoles)
+ {
+ cfg->setGroup(QString("%1-hole@-50,-50|0").arg(scoreboardHoles + 1));
+ emit newHole(cfg->readNumEntry("par", 3));
+ }
+
+ resetHoleScores();
+ updateShowInfo();
+
+ // this is from shotDone()
+ (*curPlayer).ball()->setVisible(true);
+ putter->setOrigin((*curPlayer).ball()->x(), (*curPlayer).ball()->y());
+ updateMouse();
+
+ ballStateList.canUndo = false;
+
+ (*curPlayer).ball()->collisionDetect(oldx, oldy);
+ }
+
+ unPause();
+}
+
+void KolfGame::showInfo()
+{
+ QString text = i18n("Hole %1: par %2, maximum %3 strokes").arg(curHole).arg(holeInfo.par()).arg(holeInfo.maxStrokes());
+ infoText->move((width - QFontMetrics(infoText->font()).width(text)) / 2, infoText->y());
+ infoText->setText(text);
+ // I hate this text! Let's not show it
+ //infoText->setVisible(true);
+
+ emit newStatusText(text);
+}
+
+void KolfGame::showInfoDlg(bool addDontShowAgain)
+{
+ KMessageBox::information(parentWidget(),
+ i18n("Course name: %1").arg(holeInfo.name()) + QString("\n")
+ + i18n("Created by %1").arg(holeInfo.author()) + QString("\n")
+ + i18n("%1 holes").arg(highestHole),
+ i18n("Course Information"),
+ addDontShowAgain? holeInfo.name() + QString(" ") + holeInfo.author() : QString::null);
+}
+
+void KolfGame::hideInfo()
+{
+ infoText->setText("");
+ infoText->setVisible(false);
+
+ emit newStatusText(QString::null);
+}
+
+void KolfGame::openFile()
+{
+ Object *curObj = 0;
+
+ QCanvasItem *item = 0;
+ for (item = items.first(); item; item = items.next())
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(item);
+ if (citem)
+ {
+ // sometimes info is still showing
+ citem->hideInfo();
+ citem->aboutToDie();
+ }
+ }
+
+ items.setAutoDelete(true);
+ items.clear();
+ items.setAutoDelete(false);
+
+ extraMoveable.setAutoDelete(false);
+ extraMoveable.clear();
+ fastAdvancers.setAutoDelete(false);
+ fastAdvancers.clear();
+ selectedItem = 0;
+
+ // will tell basic course info
+ // we do this here for the hell of it.
+ // there is no fake id, by the way,
+ // because it's old and when i added ids i forgot to change it.
+ cfg->setGroup("0-course@-50,-50");
+ holeInfo.setAuthor(cfg->readEntry("author", holeInfo.author()));
+ holeInfo.setName(cfg->readEntry("Name", holeInfo.name()));
+ holeInfo.setUntranslatedName(cfg->readEntryUntranslated("Name", holeInfo.untranslatedName()));
+ emit titleChanged(holeInfo.name());
+
+ cfg->setGroup(QString("%1-hole@-50,-50|0").arg(curHole));
+ curPar = cfg->readNumEntry("par", 3);
+ holeInfo.setPar(curPar);
+ holeInfo.borderWallsChanged(cfg->readBoolEntry("borderWalls", holeInfo.borderWalls()));
+ holeInfo.setMaxStrokes(cfg->readNumEntry("maxstrokes", 10));
+ bool hasFinalLoad = cfg->readBoolEntry("hasFinalLoad", true);
+
+ QStringList missingPlugins;
+ QStringList groups = cfg->groupList();
+
+ int numItems = 0;
+ int _highestHole = 0;
+
+ for (QStringList::Iterator it = groups.begin(); it != groups.end(); ++it)
+ {
+ // [<holeNum>-<name>@<x>,<y>|<id>]
+ cfg->setGroup(*it);
+
+ const int len = (*it).length();
+ const int dashIndex = (*it).find("-");
+ const int holeNum = (*it).left(dashIndex).toInt();
+ if (holeNum > _highestHole)
+ _highestHole = holeNum;
+
+ const int atIndex = (*it).find("@");
+ const QString name = (*it).mid(dashIndex + 1, atIndex - (dashIndex + 1));
+
+ if (holeNum != curHole)
+ {
+ // if we've had one, break, cause list is sorted
+ // erps, no, cause we need to know highest hole!
+ if (numItems && !recalcHighestHole)
+ break;
+ continue;
+ }
+ numItems++;
+
+
+ const int commaIndex = (*it).find(",");
+ const int pipeIndex = (*it).find("|");
+ const int x = (*it).mid(atIndex + 1, commaIndex - (atIndex + 1)).toInt();
+ const int y = (*it).mid(commaIndex + 1, pipeIndex - (commaIndex + 1)).toInt();
+
+ // will tell where ball is
+ if (name == "ball")
+ {
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ (*it).ball()->move(x, y);
+ whiteBall->move(x, y);
+ continue;
+ }
+
+ const int id = (*it).right(len - (pipeIndex + 1)).toInt();
+
+ bool loaded = false;
+
+ for (curObj = obj->first(); curObj; curObj = obj->next())
+ {
+ if (name != curObj->_name())
+ continue;
+
+ QCanvasItem *newItem = curObj->newObject(course);
+ items.append(newItem);
+
+ CanvasItem *canvasItem = dynamic_cast<CanvasItem *>(newItem);
+ if (!canvasItem)
+ continue;
+
+ canvasItem->setId(id);
+ canvasItem->setGame(this);
+ canvasItem->editModeChanged(editing);
+ canvasItem->setName(curObj->_name());
+ addItemsToMoveableList(canvasItem->moveableItems());
+ if (canvasItem->fastAdvance())
+ addItemToFastAdvancersList(canvasItem);
+
+ newItem->move(x, y);
+ canvasItem->firstMove(x, y);
+
+ newItem->setVisible(true);
+
+ // make things actually show
+ if (!hasFinalLoad)
+ {
+ cfg->setGroup(makeGroup(id, curHole, canvasItem->name(), x, y));
+ canvasItem->load(cfg);
+ course->update();
+ }
+
+ // we don't allow multiple items for the same thing in
+ // the file!
+
+ loaded = true;
+ break;
+ }
+
+ if (!loaded && name != "hole" && missingPlugins.contains(name) <= 0)
+ missingPlugins.append(name);
+ }
+
+ if (!missingPlugins.empty())
+ {
+ KMessageBox::informationList(this, QString("<p>&lt;http://katzbrown.com/kolf/Plugins/&gt;</p><p>") + i18n("This hole uses the following plugins, which you do not have installed:") + QString("</p>"), missingPlugins, QString::null, QString("%1 warning").arg(holeInfo.untranslatedName() + QString::number(curHole)));
+ }
+
+ lastDelId = -1;
+
+ // if it's the first hole let's not
+ if (!numItems && curHole > 1 && !addingNewHole && curHole >= _highestHole)
+ {
+ // we're done, let's quit
+ curHole--;
+ pause();
+ emit holesDone();
+
+ // tidy things up
+ setBorderWalls(false);
+ clearHole();
+ setModified(false);
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ (*it).ball()->setVisible(false);
+
+ return;
+ }
+
+ // do it down here; if !hasFinalLoad, do it up there!
+ QCanvasItem *qcanvasItem = 0;
+ QPtrList<CanvasItem> todo;
+ QPtrList<QCanvasItem> qtodo;
+ if (hasFinalLoad)
+ {
+ for (qcanvasItem = items.first(); qcanvasItem; qcanvasItem = items.next())
+ {
+ CanvasItem *item = dynamic_cast<CanvasItem *>(qcanvasItem);
+ if (item)
+ {
+ if (item->loadLast())
+ {
+ qtodo.append(qcanvasItem);
+ todo.append(item);
+ }
+ else
+ {
+ QString group = makeGroup(item->curId(), curHole, item->name(), (int)qcanvasItem->x(), (int)qcanvasItem->y());
+ cfg->setGroup(group);
+ item->load(cfg);
+ }
+ }
+ }
+
+ CanvasItem *citem = 0;
+ qcanvasItem = qtodo.first();
+ for (citem = todo.first(); citem; citem = todo.next())
+ {
+ cfg->setGroup(makeGroup(citem->curId(), curHole, citem->name(), (int)qcanvasItem->x(), (int)qcanvasItem->y()));
+ citem->load(cfg);
+
+ qcanvasItem = qtodo.next();
+ }
+ }
+
+ for (qcanvasItem = items.first(); qcanvasItem; qcanvasItem = items.next())
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(qcanvasItem);
+ if (citem)
+ citem->updateZ();
+ }
+
+ if (curHole > _highestHole)
+ _highestHole = curHole;
+
+ if (recalcHighestHole)
+ {
+ highestHole = _highestHole;
+ recalcHighestHole = false;
+ emit largestHole(highestHole);
+ }
+
+ if (curHole == 1 && !filename.isNull() && !infoShown)
+ {
+ // let's not now, because they see it when they choose course
+ //showInfoDlg(true);
+ infoShown = true;
+ }
+
+ setModified(false);
+}
+
+void KolfGame::addItemsToMoveableList(QPtrList<QCanvasItem> list)
+{
+ QCanvasItem *item = 0;
+ for (item = list.first(); item; item = list.next())
+ extraMoveable.append(item);
+}
+
+void KolfGame::addItemToFastAdvancersList(CanvasItem *item)
+{
+ fastAdvancers.append(item);
+ fastAdvancedExist = fastAdvancers.count() > 0;
+}
+
+void KolfGame::addNewObject(Object *newObj)
+{
+ QCanvasItem *newItem = newObj->newObject(course);
+ items.append(newItem);
+ newItem->setVisible(true);
+
+ CanvasItem *canvasItem = dynamic_cast<CanvasItem *>(newItem);
+ if (!canvasItem)
+ return;
+
+ // we need to find a number that isn't taken
+ int i = lastDelId > 0? lastDelId : items.count() - 30;
+ if (i <= 0)
+ i = 0;
+
+ for (;; ++i)
+ {
+ bool found = false;
+ QCanvasItem *item = 0;
+ for (item = items.first(); item; item = items.next())
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(item);
+ if (citem)
+ {
+ if (citem->curId() == i)
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+
+
+ if (!found)
+ break;
+ }
+ canvasItem->setId(i);
+
+ canvasItem->setGame(this);
+
+ if (m_showInfo)
+ canvasItem->showInfo();
+ else
+ canvasItem->hideInfo();
+
+ canvasItem->editModeChanged(editing);
+
+ canvasItem->setName(newObj->_name());
+ addItemsToMoveableList(canvasItem->moveableItems());
+
+ if (canvasItem->fastAdvance())
+ addItemToFastAdvancersList(canvasItem);
+
+ newItem->move(width/2 - 18, height / 2 - 18);
+
+ if (selectedItem)
+ canvasItem->selectedItem(selectedItem);
+
+ setModified(true);
+}
+
+bool KolfGame::askSave(bool noMoreChances)
+{
+ if (!modified)
+ // not cancel, don't save
+ return false;
+
+ int result = KMessageBox::warningYesNoCancel(this, i18n("There are unsaved changes to current hole. Save them?"), i18n("Unsaved Changes"), KStdGuiItem::save(), noMoreChances? KStdGuiItem::discard() : i18n("Save &Later"), noMoreChances? "DiscardAsk" : "SaveAsk", true);
+ switch (result)
+ {
+ case KMessageBox::Yes:
+ save();
+ // fallthrough
+
+ case KMessageBox::No:
+ return false;
+ break;
+
+ case KMessageBox::Cancel:
+ return true;
+ break;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+void KolfGame::addNewHole()
+{
+ if (askSave(true))
+ return;
+
+ // either it's already false
+ // because it was saved by askSave(),
+ // or the user pressed the 'discard' button
+ setModified(false);
+
+ // find highest hole num, and create new hole
+ // now openFile makes highest hole for us
+
+ addingNewHole = true;
+ curHole = highestHole;
+ recalcHighestHole = true;
+ startNextHole();
+ addingNewHole = false;
+ emit currentHole(curHole);
+
+ // make sure even the current player isn't showing
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ (*it).ball()->setVisible(false);
+
+ whiteBall->setVisible(editing);
+ highlighter->setVisible(false);
+ putter->setVisible(!editing);
+ inPlay = false;
+
+ // add default objects
+ Object *curObj = 0;
+ for (curObj = obj->first(); curObj; curObj = obj->next())
+ if (curObj->addOnNewHole())
+ addNewObject(curObj);
+
+ save();
+}
+
+// kantan deshou ;-)
+void KolfGame::resetHole()
+{
+ if (askSave(true))
+ return;
+ setModified(false);
+ curHole--;
+ startNextHole();
+ resetHoleScores();
+}
+
+void KolfGame::resetHoleScores()
+{
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ {
+ (*it).resetScore(curHole);
+ emit scoreChanged((*it).id(), curHole, 0);
+ }
+}
+
+void KolfGame::clearHole()
+{
+ QCanvasItem *qcanvasItem = 0;
+ for (qcanvasItem = items.first(); qcanvasItem; qcanvasItem = items.next())
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(qcanvasItem);
+ if (citem)
+ citem->aboutToDie();
+ }
+ items.setAutoDelete(true);
+ items.clear();
+ items.setAutoDelete(false);
+ emit newSelectedItem(&holeInfo);
+
+ // add default objects
+ Object *curObj = 0;
+ for (curObj = obj->first(); curObj; curObj = obj->next())
+ if (curObj->addOnNewHole())
+ addNewObject(curObj);
+
+ setModified(true);
+}
+
+void KolfGame::switchHole(int hole)
+{
+ if (inPlay)
+ return;
+ if (hole < 1 || hole > highestHole)
+ return;
+
+ bool wasEditing = editing;
+ if (editing)
+ toggleEditMode();
+
+ if (askSave(true))
+ return;
+ setModified(false);
+
+ curHole = hole;
+ resetHole();
+
+ if (wasEditing)
+ toggleEditMode();
+}
+
+void KolfGame::switchHole(const QString &holestring)
+{
+ bool ok;
+ int hole = holestring.toInt(&ok);
+ if (!ok)
+ return;
+ switchHole(hole);
+}
+
+void KolfGame::nextHole()
+{
+ switchHole(curHole + 1);
+}
+
+void KolfGame::prevHole()
+{
+ switchHole(curHole - 1);
+}
+
+void KolfGame::firstHole()
+{
+ switchHole(1);
+}
+
+void KolfGame::lastHole()
+{
+ switchHole(highestHole);
+}
+
+void KolfGame::randHole()
+{
+ int newHole = 1 + (int)((double)kapp->random() * ((double)(highestHole - 1) / (double)RAND_MAX));
+ switchHole(newHole);
+}
+
+void KolfGame::save()
+{
+ if (filename.isNull())
+ {
+ QString newfilename = KFileDialog::getSaveFileName(":kourses", "application/x-kourse", this, i18n("Pick Kolf Course to Save To"));
+ if (newfilename.isNull())
+ return;
+
+ setFilename(newfilename);
+ }
+
+ emit parChanged(curHole, holeInfo.par());
+ emit titleChanged(holeInfo.name());
+
+ // we use this bool for optimization
+ // in openFile().
+ bool hasFinalLoad = false;
+ fastAdvancedExist = false;
+
+ QCanvasItem *item = 0;
+ for (item = items.first(); item; item = items.next())
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(item);
+ if (citem)
+ {
+ citem->aboutToSave();
+ if (citem->loadLast())
+ hasFinalLoad = true;
+ }
+ }
+
+ QStringList groups = cfg->groupList();
+
+ // wipe out all groups from this hole
+ for (QStringList::Iterator it = groups.begin(); it != groups.end(); ++it)
+ {
+ int holeNum = (*it).left((*it).find("-")).toInt();
+ if (holeNum == curHole)
+ cfg->deleteGroup(*it);
+ }
+ for (item = items.first(); item; item = items.next())
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(item);
+ if (citem)
+ {
+ citem->clean();
+
+ cfg->setGroup(makeGroup(citem->curId(), curHole, citem->name(), (int)item->x(), (int)item->y()));
+ citem->save(cfg);
+ }
+ }
+
+ // save where ball starts (whiteBall tells all)
+ cfg->setGroup(QString("%1-ball@%2,%3").arg(curHole).arg((int)whiteBall->x()).arg((int)whiteBall->y()));
+ cfg->writeEntry("dummykey", true);
+
+ cfg->setGroup("0-course@-50,-50");
+ cfg->writeEntry("author", holeInfo.author());
+ cfg->writeEntry("Name", holeInfo.untranslatedName());
+
+ // save hole info
+ cfg->setGroup(QString("%1-hole@-50,-50|0").arg(curHole));
+ cfg->writeEntry("par", holeInfo.par());
+ cfg->writeEntry("maxstrokes", holeInfo.maxStrokes());
+ cfg->writeEntry("borderWalls", holeInfo.borderWalls());
+ cfg->writeEntry("hasFinalLoad", hasFinalLoad);
+
+ cfg->sync();
+
+ for (item = items.first(); item; item = items.next())
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(item);
+ if (citem)
+ citem->savingDone();
+ }
+
+ setModified(false);
+}
+
+void KolfGame::toggleEditMode()
+{
+ // won't be editing anymore, and user wants to cancel, we return
+ // this is pretty useless. when the person leaves the hole,
+ // he gets asked again
+ /*
+ if (editing && modified)
+ {
+ if (askSave(false))
+ {
+ emit checkEditing();
+ return;
+ }
+ }
+ */
+
+ moving = false;
+ selectedItem = 0;
+
+ editing = !editing;
+
+ if (editing)
+ {
+ emit editingStarted();
+ emit newSelectedItem(&holeInfo);
+ }
+ else
+ {
+ emit editingEnded();
+ setCursor(KCursor::arrowCursor());
+ }
+
+ // alert our items
+ QCanvasItem *item = 0;
+ for (item = items.first(); item; item = items.next())
+ {
+ CanvasItem *citem = dynamic_cast<CanvasItem *>(item);
+ if (citem)
+ citem->editModeChanged(editing);
+ }
+
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ {
+ // curplayer shouldn't be hidden no matter what
+ if ((*it).ball()->beginningOfHole() && it != curPlayer)
+ (*it).ball()->setVisible(false);
+ else
+ (*it).ball()->setVisible(!editing);
+ }
+
+ whiteBall->setVisible(editing);
+ highlighter->setVisible(false);
+
+ // shouldn't see putter whilst editing
+ putter->setVisible(!editing);
+
+ if (editing)
+ autoSaveTimer->start(autoSaveMsec);
+ else
+ autoSaveTimer->stop();
+
+ inPlay = false;
+}
+
+void KolfGame::playSound(QString file, double vol)
+{
+ if (m_sound)
+ {
+ KPlayObject *oldPlayObject = 0;
+ for (oldPlayObject = oldPlayObjects.first(); oldPlayObject; oldPlayObject = oldPlayObjects.next())
+ {
+ if (oldPlayObject && oldPlayObject->state() != Arts::posPlaying)
+ {
+ oldPlayObjects.remove();
+
+ // because we will go to next() next time
+ // and after remove current item is one after
+ // removed item
+ (void) oldPlayObjects.prev();
+ }
+ }
+
+ file = soundDir + file + QString::fromLatin1(".wav");
+
+ // not needed when all of the files are in the distribution
+ //if (!QFile::exists(file))
+ //return;
+
+ KPlayObjectFactory factory(artsServer.server());
+ KPlayObject *playObject = factory.createPlayObject(KURL(file), true);
+
+ if (playObject && !playObject->isNull())
+ {
+ if (vol > 1)
+ vol = 1;
+ else if (vol <= .01)
+ {
+ delete playObject;
+ return;
+ }
+
+ if (vol < .99)
+ {
+ //new KVolumeControl(vol, artsServer.server(), playObject);
+ }
+
+ playObject->play();
+ oldPlayObjects.append(playObject);
+ }
+ }
+}
+
+void HoleInfo::borderWallsChanged(bool yes)
+{
+ m_borderWalls = yes;
+ game->setBorderWalls(yes);
+}
+
+void KolfGame::print(KPrinter &pr)
+{
+ QPainter p(&pr);
+
+ QPaintDeviceMetrics metrics(&pr);
+
+ // translate to center
+ p.translate(metrics.width() / 2 - course->rect().width() / 2, metrics.height() / 2 - course->rect().height() / 2);
+
+ QPixmap pix(width, height);
+ QPainter pixp(&pix);
+ course->drawArea(course->rect(), &pixp);
+ p.drawPixmap(0, 0, pix);
+
+ p.setPen(QPen(black, 2));
+ p.drawRect(course->rect());
+
+ p.resetXForm();
+
+ if (pr.option("kde-kolf-title") == "true")
+ {
+ QString text = i18n("%1 - Hole %2; by %3").arg(holeInfo.name()).arg(curHole).arg(holeInfo.author());
+ QFont font(kapp->font());
+ font.setPointSize(18);
+ QRect rect = QFontMetrics(font).boundingRect(text);
+ p.setFont(font);
+
+ p.drawText(metrics.width() / 2 - rect.width() / 2, metrics.height() / 2 - course->rect().height() / 2 -20 - rect.height(), text);
+ }
+}
+
+bool KolfGame::allPlayersDone()
+{
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ if ((*it).ball()->curState() != Holed)
+ return false;
+
+ return true;
+}
+
+void KolfGame::setBorderWalls(bool showing)
+{
+ Wall *wall = 0;
+ for (wall = borderWalls.first(); wall; wall = borderWalls.next())
+ wall->setVisible(showing);
+}
+
+void KolfGame::setUseAdvancedPutting(bool yes)
+{
+ m_useAdvancedPutting = yes;
+
+ // increase maxStrength in advanced putting mode
+ if (yes)
+ maxStrength = 65;
+ else
+ maxStrength = 55;
+}
+
+void KolfGame::setShowGuideLine(bool yes)
+{
+ putter->setShowGuideLine(yes);
+}
+
+void KolfGame::setSound(bool yes)
+{
+ m_sound = yes;
+}
+
+void KolfGame::courseInfo(CourseInfo &info, const QString& filename)
+{
+ KConfig cfg(filename);
+ cfg.setGroup("0-course@-50,-50");
+ info.author = cfg.readEntry("author", info.author);
+ info.name = cfg.readEntry("Name", cfg.readEntry("name", info.name));
+ info.untranslatedName = cfg.readEntryUntranslated("Name", cfg.readEntryUntranslated("name", info.name));
+
+ unsigned int hole = 1;
+ unsigned int par= 0;
+ while (1)
+ {
+ QString group = QString("%1-hole@-50,-50|0").arg(hole);
+ if (!cfg.hasGroup(group))
+ {
+ hole--;
+ break;
+ }
+
+ cfg.setGroup(group);
+ par += cfg.readNumEntry("par", 3);
+
+ hole++;
+ }
+
+ info.par = par;
+ info.holes = hole;
+}
+
+void KolfGame::scoresFromSaved(KConfig *config, PlayerList &players)
+{
+ config->setGroup("0 Saved Game");
+ int numPlayers = config->readNumEntry("Players", 0);
+ if (numPlayers <= 0)
+ return;
+
+ for (int i = 1; i <= numPlayers; ++i)
+ {
+ // this is same as in kolf.cpp, but we use saved game values
+ config->setGroup(QString::number(i));
+ players.append(Player());
+ players.last().ball()->setColor(config->readEntry("Color", "#ffffff"));
+ players.last().setName(config->readEntry("Name"));
+ players.last().setId(i);
+
+ QStringList scores(config->readListEntry("Scores"));
+ QValueList<int> intscores;
+ for (QStringList::Iterator it = scores.begin(); it != scores.end(); ++it)
+ intscores.append((*it).toInt());
+
+ players.last().setScores(intscores);
+ }
+}
+
+void KolfGame::saveScores(KConfig *config)
+{
+ // wipe out old player info
+ QStringList groups = config->groupList();
+ for (QStringList::Iterator it = groups.begin(); it != groups.end(); ++it)
+ {
+ // this deletes all int groups, ie, the player info groups
+ bool ok = false;
+ (*it).toInt(&ok);
+ if (ok)
+ config->deleteGroup(*it);
+ }
+
+ config->setGroup("0 Saved Game");
+ config->writeEntry("Players", players->count());
+ config->writeEntry("Course", filename);
+ config->writeEntry("Current Hole", curHole);
+
+ for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it)
+ {
+ config->setGroup(QString::number((*it).id()));
+ config->writeEntry("Name", (*it).name());
+ config->writeEntry("Color", (*it).ball()->color().name());
+
+ QStringList scores;
+ QValueList<int> intscores = (*it).scores();
+ for (QValueList<int>::Iterator it = intscores.begin(); it != intscores.end(); ++it)
+ scores.append(QString::number(*it));
+
+ config->writeEntry("Scores", scores);
+ }
+}
+
+CourseInfo::CourseInfo()
+: name(i18n("Course Name")), author(i18n("Course Author")), holes(0), par(0)
+{
+}
+
+#include "game.moc"