You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegames/kolf/game.cpp

4303 lines
94 KiB

#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 <tqbrush.h>
#include <tqcanvas.h>
#include <tqcheckbox.h>
#include <tqcolor.h>
#include <tqcursor.h>
#include <tqevent.h>
#include <tqfont.h>
#include <tqfontmetrics.h>
#include <tqimage.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqmap.h>
#include <tqpainter.h>
#include <tqpaintdevicemetrics.h>
#include <tqpen.h>
#include <tqpixmap.h>
#include <tqpixmapcache.h>
#include <tqpoint.h>
#include <tqpointarray.h>
#include <tqrect.h>
#include <tqsimplerichtext.h>
#include <tqsize.h>
#include <tqslider.h>
#include <tqspinbox.h>
#include <tqstring.h>
#include <tqstringlist.h>
#include <tqtimer.h>
#include <tqtooltip.h>
#include <tqvaluelist.h>
#include <tqwhatsthis.h>
#include <math.h>
#include <stdlib.h>
#include <unistd.h>
#include "kcomboboxdialog.h"
#include "kvolumecontrol.h"
#include "vector.h"
#include "game.h"
inline TQString makeGroup(int id, int hole, TQString name, int x, int y)
{
return TQString("%1-%2@%3,%4|%5").arg(hole).arg(name).arg(x).arg(y).arg(id);
}
inline TQString makeStateGroup(int id, const TQString &name)
{
return TQString("%1|%2").arg(name).arg(id);
}
/////////////////////////
RectPoint::RectPoint(TQColor color, RectItem *rect, TQCanvas *canvas)
: TQCanvasEllipse(canvas)
{
setZ(9999);
setSize(10, 10);
this->rect = rect;
setBrush(TQBrush(color));
setSizeFactor(1.0);
dontmove = false;
}
void RectPoint::moveBy(double dx, double dy)
{
TQCanvasEllipse::moveBy(dx, dy);
if (dontmove)
{
dontmove = false;
return;
}
TQCanvasItem *qitem = dynamic_cast<TQCanvasItem *>(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(TQWidget *parent)
{
CanvasItem *citem = dynamic_cast<CanvasItem *>(rect);
if (citem)
return citem->config(parent);
else
return CanvasItem::config(parent);
}
/////////////////////////
Arrow::Arrow(TQCanvas *canvas)
: TQCanvasLine(canvas)
{
line1 = new TQCanvasLine(canvas);
line2 = new TQCanvasLine(canvas);
m_angle = 0;
m_length = 20;
m_reversed = false;
setPen(black);
updateSelf();
setVisible(false);
}
void Arrow::setPen(TQPen p)
{
TQCanvasLine::setPen(p);
line1->setPen(p);
line2->setPen(p);
}
void Arrow::setZ(double newz)
{
TQCanvasLine::setZ(newz);
line1->setZ(newz);
line2->setZ(newz);
}
void Arrow::setVisible(bool yes)
{
TQCanvasLine::setVisible(yes);
line1->setVisible(yes);
line2->setVisible(yes);
}
void Arrow::moveBy(double dx, double dy)
{
TQCanvasLine::moveBy(dx, dy);
line1->moveBy(dx, dy);
line2->moveBy(dx, dy);
}
void Arrow::aboutToDie()
{
delete line1;
delete line2;
}
void Arrow::updateSelf()
{
TQPoint start = startPoint();
TQPoint end(m_length * cos(m_angle), m_length * sin(m_angle));
if (m_reversed)
{
TQPoint 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 = TQPoint(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 = TQPoint(lineLen * cos(angle2), lineLen * sin(angle2));
line2->setPoints(0, 0, end.x(), end.y());
}
/////////////////////////
BridgeConfig::BridgeConfig(Bridge *bridge, TQWidget *parent)
: Config(parent)
{
this->bridge = bridge;
m_vlayout = new TQVBoxLayout(this, marginHint(), spacingHint());
TQGridLayout *layout = new TQGridLayout(m_vlayout, 2, 3, spacingHint());
layout->addWidget(new TQLabel(i18n("Walls on:"), this), 0, 0);
top = new TQCheckBox(i18n("&Top"), this);
layout->addWidget(top, 0, 1);
connect(top, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(topWallChanged(bool)));
top->setChecked(bridge->topWallVisible());
bot = new TQCheckBox(i18n("&Bottom"), this);
layout->addWidget(bot, 1, 1);
connect(bot, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(botWallChanged(bool)));
bot->setChecked(bridge->botWallVisible());
left = new TQCheckBox(i18n("&Left"), this);
layout->addWidget(left, 1, 0);
connect(left, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(leftWallChanged(bool)));
left->setChecked(bridge->leftWallVisible());
right = new TQCheckBox(i18n("&Right"), this);
layout->addWidget(right, 1, 2);
connect(right, TQT_SIGNAL(toggled(bool)), this, TQT_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(TQRect rect, TQCanvas *canvas)
: TQCanvasRectangle(rect, canvas)
{
TQColor color("#92772D");
setBrush(TQBrush(color));
setPen(Qt::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(TQColor color)
{
topWall->setPen(TQPen(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)
{
TQCanvasRectangle::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());
TQCanvasItemList list = collisions(true);
for (TQCanvasItemList::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());
}
TQPtrList<TQCanvasItem> Bridge::moveableItems() const
{
TQPtrList<TQCanvasItem> ret;
ret.append(point);
return ret;
}
void Bridge::newSize(int width, int height)
{
setSize(width, height);
}
void Bridge::setSize(int width, int height)
{
TQCanvasRectangle::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, TQWidget *parent)
: BridgeConfig(windmill, parent)
{
this->windmill = windmill;
m_vlayout->addStretch();
TQCheckBox *check = new TQCheckBox(i18n("Windmill on bottom"), this);
check->setChecked(windmill->bottom());
connect(check, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(endChanged(bool)));
m_vlayout->addWidget(check);
TQHBoxLayout *hlayout = new TQHBoxLayout(m_vlayout, spacingHint());
hlayout->addWidget(new TQLabel(i18n("Slow"), this));
TQSlider *slider = new TQSlider(1, 10, 1, windmill->curSpeed(), Qt::Horizontal, this);
hlayout->addWidget(slider);
hlayout->addWidget(new TQLabel(i18n("Fast"), this));
connect(slider, TQT_SIGNAL(valueChanged(int)), this, TQT_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(TQRect rect, TQCanvas *canvas)
: Bridge(rect, canvas), speedfactor(16), m_bottom(true)
{
guard = new WindmillGuard(canvas);
guard->setPen(TQPen(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(TQCanvas *canvas)
: Bridge(TQRect(0, 0, 110, 40), canvas)
{
setZ(998.8);
m_text = m_untranslatedText = i18n("New Text");
setBrush(TQBrush(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 TQString &text)
{
m_text = text;
m_untranslatedText = text;
update();
}
void Sign::draw(TQPainter &painter)
{
Bridge::draw(painter);
painter.setPen(TQPen(black, 1));
TQSimpleRichText txt(m_text, kapp->font());
const int indent = wallPen().width() + 3;
txt.setWidth(width() - 2*indent);
TQColorGroup colorGroup;
colorGroup.setColor(TQColorGroup::Foreground, black);
colorGroup.setColor(TQColorGroup::Text, black);
colorGroup.setColor(TQColorGroup::Background, black);
colorGroup.setColor(TQColorGroup::Base, black);
txt.draw(&painter, x() + indent, y(), TQRect(x() + indent, y(), width() - indent, height() - indent), colorGroup);
}
/////////////////////////
SignConfig::SignConfig(Sign *sign, TQWidget *parent)
: BridgeConfig(sign, parent)
{
this->sign = sign;
m_vlayout->addStretch();
m_vlayout->addWidget(new TQLabel(i18n("Sign HTML:"), this));
KLineEdit *name = new KLineEdit(sign->text(), this);
m_vlayout->addWidget(name);
connect(name, TQT_SIGNAL(textChanged(const TQString &)), this, TQT_SLOT(textChanged(const TQString &)));
}
void SignConfig::textChanged(const TQString &text)
{
sign->setText(text);
changed();
}
/////////////////////////
EllipseConfig::EllipseConfig(Ellipse *ellipse, TQWidget *parent)
: Config(parent), slow1(0), fast1(0), slow2(0), fast2(0), slider1(0), slider2(0)
{
this->ellipse = ellipse;
m_vlayout = new TQVBoxLayout(this, marginHint(), spacingHint());
TQCheckBox *check = new TQCheckBox(i18n("Enable show/hide"), this);
m_vlayout->addWidget(check);
connect(check, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(check1Changed(bool)));
check->setChecked(ellipse->changeEnabled());
TQHBoxLayout *hlayout = new TQHBoxLayout(m_vlayout, spacingHint());
slow1 = new TQLabel(i18n("Slow"), this);
hlayout->addWidget(slow1);
slider1 = new TQSlider(1, 100, 5, 100 - ellipse->changeEvery(), Qt::Horizontal, this);
hlayout->addWidget(slider1);
fast1 = new TQLabel(i18n("Fast"), this);
hlayout->addWidget(fast1);
connect(slider1, TQT_SIGNAL(valueChanged(int)), this, TQT_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(TQCanvas *canvas)
: TQCanvasEllipse(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);
}
TQPtrList<TQCanvasItem> Ellipse::moveableItems() const
{
TQPtrList<TQCanvasItem> ret;
ret.append(point);
return ret;
}
void Ellipse::newSize(int width, int height)
{
TQCanvasEllipse::setSize(width, height);
}
void Ellipse::moveBy(double dx, double dy)
{
TQCanvasEllipse::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)
{
TQCanvasEllipse::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(TQWidget *parent)
{
return new EllipseConfig(this, parent);
}
void Ellipse::aboutToSave()
{
setVisible(true);
dontHide = true;
}
void Ellipse::savingDone()
{
dontHide = false;
}
/////////////////////////
Puddle::Puddle(TQCanvas *canvas)
: Ellipse(canvas)
{
setSize(45, 30);
TQBrush brush;
TQPixmap pic;
if (!TQPixmapCache::find("puddle", pic))
{
pic.load(locate("appdata", "pics/puddle.png"));
TQPixmapCache::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())
{
TQCanvasRectangle i(TQRect(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(TQCanvas *canvas)
: Ellipse(canvas)
{
setSize(45, 40);
TQBrush brush;
TQPixmap pic;
if (!TQPixmapCache::find("sand", pic))
{
pic.load(locate("appdata", "pics/sand.png"));
TQPixmapCache::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*/)
{
TQCanvasRectangle i(TQRect(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(TQCanvas *canvas)
: TQCanvasLine(canvas)
{
m_showGuideLine = true;
oneDegree = M_PI / 180;
len = 9;
angle = 0;
guideLine = new TQCanvasLine(canvas);
guideLine->setPen(TQPen(white, 1, TQPen::DotLine));
guideLine->setZ(998.8);
setPen(TQPen(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)
{
TQCanvasLine::moveBy(dx, dy);
guideLine->move(x(), y());
}
void Putter::setShowGuideLine(bool yes)
{
m_showGuideLine = yes;
setVisible(isVisible());
}
void Putter::setVisible(bool yes)
{
TQCanvasLine::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);
TQPoint start;
TQPoint 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(TQCanvas *canvas)
: TQCanvasEllipse(20, 20, canvas)
{
setZ(-25);
firstColor = TQColor("#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)
{
TQCanvasEllipse::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)
{
TQCanvasEllipse::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 TQPoint start(x(), y());
const TQPoint 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(TQColor color, TQCanvas *canvas)
: TQCanvasEllipse(15, 15, canvas)
{
setZ(998.1);
setPen(black);
setBrush(color);
}
bool Hole::collision(Ball *ball, long int /*id*/)
{
bool wasCenter = false;
switch (result(TQPoint(ball->x(), ball->y()), ball->curVector().magnitude(), &wasCenter))
{
case Result_Holed:
place(ball, wasCenter);
return false;
default:
break;
}
return true;
}
HoleResult Hole::result(TQPoint p, double s, bool * /*wasCenter*/)
{
const double longestRadius = width() > height()? width() : height();
if (s > longestRadius / 5.0)
return Result_Miss;
TQCanvasRectangle i(TQRect(p, TQSize(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(TQCanvas *canvas)
: Hole(TQColor("#808080"), canvas)
{
if (!TQPixmapCache::find("cup", pixmap))
{
pixmap.load(locate("appdata", "pics/cup.png"));
TQPixmapCache::insert("cup", pixmap);
}
}
void Cup::draw(TQPainter &p)
{
p.drawPixmap(TQPoint(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(TQCanvas *canvas)
: Hole(black, canvas), exitDeg(0)
{
infoLine = 0;
m_minSpeed = 3.0;
m_maxSpeed = 5.0;
runs = 0;
const TQColor myColor((TQRgb)(kapp->random() % 0x01000000));
outside = new TQCanvasEllipse(canvas);
outside->setZ(z() - .001);
outside->setBrush(TQBrush(myColor));
setBrush(black);
exitItem = new BlackHoleExit(this, canvas);
exitItem->setPen(TQPen(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 TQCanvasLine(canvas());
infoLine->setVisible(true);
infoLine->setPen(TQPen(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)
{
TQCanvasEllipse::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();
}
TQPtrList<TQCanvasItem> BlackHole::moveableItems() const
{
TQPtrList<TQCanvasItem> ret;
ret.append(exitItem);
return ret;
}
BlackHoleTimer::BlackHoleTimer(Ball *ball, double speed, int msec)
: m_speed(speed), m_ball(ball)
{
TQTimer::singleShot(msec, this, TQT_SLOT(mySlot()));
TQTimer::singleShot(msec / 2, this, TQT_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(TQPoint(x(), y()), TQPoint(exitItem->x(), exitItem->y())).magnitude();
BlackHoleTimer *timer = new BlackHoleTimer(ball, speed, magnitude * 2.5 - speed * 35 + 500);
connect(timer, TQT_SIGNAL(eject(Ball *, double)), this, TQT_SLOT(eject(Ball *, double)));
connect(timer, TQT_SIGNAL(halfway()), this, TQT_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)
{
TQPoint 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);
TQPoint midPoint(0, 0);
TQPoint start;
TQPoint 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", TQPoint(exitItem->x(), exitItem->y()));
cfg->writeEntry("exitDeg", exitDeg);
cfg->writeEntry("minspeed", m_minSpeed);
cfg->writeEntry("maxspeed", m_maxSpeed);
}
/////////////////////////
BlackHoleExit::BlackHoleExit(BlackHole *blackHole, TQCanvas *canvas)
: TQCanvasLine(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)
{
TQCanvasLine::moveBy(dx, dy);
arrow->move(x(), y());
blackHole->updateInfo();
}
void BlackHoleExit::setPen(TQPen p)
{
TQCanvasLine::setPen(p);
arrow->setPen(TQPen(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(TQWidget *parent)
{
return blackHole->config(parent);
}
/////////////////////////
BlackHoleConfig::BlackHoleConfig(BlackHole *blackHole, TQWidget *parent)
: Config(parent)
{
this->blackHole = blackHole;
TQVBoxLayout *layout = new TQVBoxLayout(this, marginHint(), spacingHint());
layout->addWidget(new TQLabel(i18n("Exiting ball angle:"), this));
TQSpinBox *deg = new TQSpinBox(0, 359, 10, this);
deg->setSuffix(TQString(" ") + i18n("degrees"));
deg->setValue(blackHole->curExitDeg());
deg->setWrapping(true);
layout->addWidget(deg);
connect(deg, TQT_SIGNAL(valueChanged(int)), this, TQT_SLOT(degChanged(int)));
layout->addStretch();
TQHBoxLayout *hlayout = new TQHBoxLayout(layout, spacingHint());
hlayout->addWidget(new TQLabel(i18n("Minimum exit speed:"), this));
KDoubleNumInput *min = new KDoubleNumInput(this);
min->setRange(0, 8, 1, true);
hlayout->addWidget(min);
connect(min, TQT_SIGNAL(valueChanged(double)), this, TQT_SLOT(minChanged(double)));
min->setValue(blackHole->minSpeed());
hlayout = new TQHBoxLayout(layout, spacingHint());
hlayout->addWidget(new TQLabel(i18n("Maximum:"), this));
KDoubleNumInput *max = new KDoubleNumInput(this);
max->setRange(1, 10, 1, true);
hlayout->addWidget(max);
connect(max, TQT_SIGNAL(valueChanged(double)), this, TQT_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, TQCanvas *canvas)
: TQCanvasEllipse(canvas)
{
this->wall = wall;
this->start = start;
alwaysShow = false;
editing = false;
visible = true;
lastId = INT_MAX - 10;
dontmove = false;
move(0, 0);
TQPoint 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();
TQCanvasItem *onPoint = 0;
TQCanvasItemList l = collisions(true);
for (TQCanvasItemList::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)
{
TQCanvasEllipse::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;
TQCanvasItemList l = collisions(true);
for (TQCanvasItemList::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;
TQCanvasItemList l = collisions(true);
for (TQCanvasItemList::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;
TQPoint relStart(start? wall->startPoint() : wall->endPoint());
TQPoint 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(TQCanvas *canvas)
: TQCanvasLine(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(TQPen(darkRed, 3));
setPoints(-15, 10, 15, -5);
moveBy(0, 0);
editModeChanged(false);
}
void Wall::selectedItem(TQCanvasItem *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)
{
TQCanvasLine::setVisible(yes);
startItem->setVisible(yes);
endItem->setVisible(yes);
startItem->updateVisible();
endItem->updateVisible();
}
void Wall::setZ(double newz)
{
TQCanvasLine::setZ(newz);
if (startItem)
startItem->setZ(newz + .002);
if (endItem)
endItem->setZ(newz + .001);
}
void Wall::setPen(TQPen p)
{
TQCanvasLine::setPen(p);
if (startItem)
startItem->setBrush(TQBrush(p.color()));
if (endItem)
endItem->setBrush(TQBrush(p.color()));
}
void Wall::aboutToDie()
{
delete startItem;
delete endItem;
}
void Wall::setGame(KolfGame *game)
{
CanvasItem::setGame(game);
startItem->setGame(game);
endItem->setGame(game);
}
TQPtrList<TQCanvasItem> Wall::moveableItems() const
{
TQPtrList<TQCanvasItem> ret;
ret.append(startItem);
ret.append(endItem);
return ret;
}
void Wall::moveBy(double dx, double dy)
{
TQCanvasLine::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)
{
TQCanvasLine::setVelocity(vx, vy);
/*
startItem->setVelocity(vx, vy);
endItem->setVelocity(vx, vy);
*/
}
TQPointArray Wall::areaPoints() const
{
// editing we want full width for easy moving
if (editing)
return TQCanvasLine::areaPoints();
// lessen width, for TQCanvasLine::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!
TQPointArray p(4); const int xi = int(x()); const int yi = int(y()); const TQPoint start = startPoint(); const TQPoint 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 = TQABS(x1-x2); const int dy = TQABS(y1-y2); if ( dx > dy ) { p[0] = TQPoint(x1+xi,y1+yi-1); p[1] = TQPoint(x2+xi,y2+yi-1); p[2] = TQPoint(x2+xi,y2+yi+1); p[3] = TQPoint(x1+xi,y1+yi+1); } else { p[0] = TQPoint(x1+xi-1,y1+yi); p[1] = TQPoint(x2+xi-1,y2+yi); p[2] = TQPoint(x2+xi+1,y2+yi); p[3] = TQPoint(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)
{
TQPoint start(startPoint());
start = cfg->readPointEntry("startPoint", &start);
TQPoint 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", TQPoint(startItem->x(), startItem->y()));
cfg->writeEntry("endPoint", TQPoint(endItem->x(), endItem->y()));
}
/////////////////////////
HoleConfig::HoleConfig(HoleInfo *holeInfo, TQWidget *parent)
: Config(parent)
{
this->holeInfo = holeInfo;
TQVBoxLayout *layout = new TQVBoxLayout(this, marginHint(), spacingHint());
TQHBoxLayout *hlayout = new TQHBoxLayout(layout, spacingHint());
hlayout->addWidget(new TQLabel(i18n("Course name: "), this));
KLineEdit *nameEdit = new KLineEdit(holeInfo->untranslatedName(), this);
hlayout->addWidget(nameEdit);
connect(nameEdit, TQT_SIGNAL(textChanged(const TQString &)), this, TQT_SLOT(nameChanged(const TQString &)));
hlayout = new TQHBoxLayout(layout, spacingHint());
hlayout->addWidget(new TQLabel(i18n("Course author: "), this));
KLineEdit *authorEdit = new KLineEdit(holeInfo->author(), this);
hlayout->addWidget(authorEdit);
connect(authorEdit, TQT_SIGNAL(textChanged(const TQString &)), this, TQT_SLOT(authorChanged(const TQString &)));
layout->addStretch();
hlayout = new TQHBoxLayout(layout, spacingHint());
hlayout->addWidget(new TQLabel(i18n("Par:"), this));
TQSpinBox *par = new TQSpinBox(1, 15, 1, this);
par->setValue(holeInfo->par());
hlayout->addWidget(par);
connect(par, TQT_SIGNAL(valueChanged(int)), this, TQT_SLOT(parChanged(int)));
hlayout->addStretch();
hlayout->addWidget(new TQLabel(i18n("Maximum:"), this));
TQSpinBox *maxstrokes = new TQSpinBox(holeInfo->lowestMaxStrokes(), 30, 1, this);
TQWhatsThis::add(maxstrokes, i18n("Maximum number of strokes player can take on this hole."));
TQToolTip::add(maxstrokes, i18n("Maximum number of strokes"));
maxstrokes->setSpecialValueText(i18n("Unlimited"));
maxstrokes->setValue(holeInfo->maxStrokes());
hlayout->addWidget(maxstrokes);
connect(maxstrokes, TQT_SIGNAL(valueChanged(int)), this, TQT_SLOT(maxStrokesChanged(int)));
TQCheckBox *check = new TQCheckBox(i18n("Show border walls"), this);
check->setChecked(holeInfo->borderWalls());
layout->addWidget(check);
connect(check, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(borderWallsChanged(bool)));
}
void HoleConfig::authorChanged(const TQString &newauthor)
{
holeInfo->setAuthor(newauthor);
changed();
}
void HoleConfig::nameChanged(const TQString &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(TQCanvas *canvas)
: TQCanvasItem(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 TQCanvasItem*) const { return false; }
bool StrokeCircle::collidesWith(const TQCanvasSprite*, const TQCanvasPolygonalItem*, const TQCanvasRectangle*, const TQCanvasEllipse*, const TQCanvasText*) const { return false; }
TQRect StrokeCircle::boundingRect() const { return TQRect(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(TQPainter &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(TQBrush(black, TQt::NoBrush));
p.setPen(TQPen(white, ithickness / 2));
p.drawEllipse(x() + ithickness / 2, y() + ithickness / 2, iwidth - ithickness, iheight - ithickness);
p.setPen(TQPen(TQColor((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(TQPen(white, 1));
p.drawEllipse(x(), y(), iwidth, iheight);
p.drawEllipse(x() + ithickness, y() + ithickness, iwidth - ithickness * 2, iheight - ithickness * 2);
p.setPen(TQPen(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, TQString filename, TQWidget *parent, const char *name )
: TQCanvasView(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 = TQColor("#35760D");
margin = 10;
setFocusPolicy(TQ_StrongFocus);
setFixedSize(width + 2 * margin, height + 2 * margin);
setMargins(margin, margin, margin, margin);
course = new TQCanvas(TQT_TQOBJECT(this));
course->setBackgroundColor(white);
course->resize(width, height);
TQPixmap pic;
if (!TQPixmapCache::find("grass", pic))
{
pic.load(locate("appdata", "pics/grass.png"));
TQPixmapCache::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 TQCanvasRectangle(course);
highlighter->setPen(TQPen(yellow, 1));
highlighter->setBrush(TQBrush(NoBrush));
highlighter->setVisible(false);
highlighter->setZ(10000);
// shows some info about hole
infoText = new TQCanvasText(course);
infoText->setText("");
infoText->setColor(white);
TQFont 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(TQPoint(margin, margin), TQPoint(width - margin, margin));
addBorderWall(TQPoint(margin, height - margin - 1), TQPoint(width - margin, height - margin - 1));
// vert
addBorderWall(TQPoint(margin, margin), TQPoint(margin, height - margin));
addBorderWall(TQPoint(width - margin - 1, margin), TQPoint(width - margin - 1, height - margin));
timer = new TQTimer(this);
connect(timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(timeout()));
timerMsec = 300;
fastTimer = new TQTimer(this);
connect(fastTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(fastTimeout()));
fastTimerMsec = 11;
autoSaveTimer = new TQTimer(this);
connect(autoSaveTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(autoSaveTimeout()));
autoSaveMsec = 5 * 1000 * 60; // 5 min autosave
// setUseAdvancedPutting() sets maxStrength!
setUseAdvancedPutting(false);
putting = false;
putterTimer = new TQTimer(this);
connect(putterTimer, TQT_SIGNAL(timeout()), this, TQT_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(TQString("%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 TQString &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(TQPoint start, TQPoint 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;
TQRect rect = selectedItem->boundingRect();
highlighter->move(rect.x() + 1, rect.y() + 1);
highlighter->setSize(rect.width(), rect.height());
}
void KolfGame::handleMouseDoubleClickEvent(TQMouseEvent *e)
{
// allow two fast single clicks
handleMousePressEvent(e);
}
void KolfGame::handleMousePressEvent(TQMouseEvent *e)
{
if (m_ignoreEvents)
return;
if (editing)
{
if (inPlay)
return;
storedMousePos = e->pos();
TQCanvasItemList 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 Qt::LeftButton:
{
selectedItem = list.first();
movingItem = selectedItem;
moving = true;
if (citem->cornerResize())
setCursor(KCursor::sizeFDiagCursor());
else
setCursor(KCursor::sizeAllCursor());
emit newSelectedItem(citem);
highlighter->setVisible(true);
TQRect 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() == Qt::LeftButton)
puttPress();
else if (e->button() == Qt::RightButton)
toggleShowInfo();
}
}
setFocus();
}
TQPoint KolfGame::viewportToViewport(const TQPoint &p)
{
// for some reason viewportToContents doesn't work right
return p - TQPoint(margin, margin);
}
// the following four functions are needed to handle both
// border presses and regular in-course presses
void KolfGame::mouseReleaseEvent(TQMouseEvent * e)
{
TQMouseEvent fixedEvent (TQEvent::MouseButtonRelease, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state());
handleMouseReleaseEvent(&fixedEvent);
}
void KolfGame::mousePressEvent(TQMouseEvent * e)
{
TQMouseEvent fixedEvent (TQEvent::MouseButtonPress, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state());
handleMousePressEvent(&fixedEvent);
}
void KolfGame::mouseDoubleClickEvent(TQMouseEvent * e)
{
TQMouseEvent fixedEvent (TQEvent::MouseButtonDblClick, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state());
handleMouseDoubleClickEvent(&fixedEvent);
}
void KolfGame::mouseMoveEvent(TQMouseEvent * e)
{
TQMouseEvent fixedEvent (TQEvent::MouseMove, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state());
handleMouseMoveEvent(&fixedEvent);
}
void KolfGame::handleMouseMoveEvent(TQMouseEvent *e)
{
if (inPlay || !putter || m_ignoreEvents)
return;
TQPoint 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
TQCanvasItemList 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);
TQRect brect = movingItem->boundingRect();
emit newStatusText(TQString("%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 TQPoint cursor = viewportToViewport(viewportToContents(mapFromGlobal(TQCursor::pos())));
const TQPoint ball((*curPlayer).ball()->x(), (*curPlayer).ball()->y());
putter->setAngle(-Vector(cursor, ball).direction());
}
void KolfGame::handleMouseReleaseEvent(TQMouseEvent *e)
{
setCursor(KCursor::arrowCursor());
if (editing)
{
emit newStatusText(TQString());
moving = false;
}
if (m_ignoreEvents)
return;
if (!editing && m_useMouse)
{
if (!inPlay && e->button() == Qt::LeftButton)
puttRelease();
else if (e->button() == Qt::RightButton)
toggleShowInfo();
}
setFocus();
}
void KolfGame::keyPressEvent(TQKeyEvent *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)
{
TQCanvasItem *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
{
TQCanvasItem *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(TQKeyEvent *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;
TQCanvasItem *item = dynamic_cast<TQCanvasItem *>(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(TQPoint((*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;
TQTimer::singleShot(0, this, TQT_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));
}
TQTimer::singleShot(600, this, TQT_SLOT(holeDone()));
}
else
{
inPlay = false;
TQTimer::singleShot(0, this, TQT_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();
TQCanvasItem *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()
{
TQCanvasItem *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);
TQStringList options;
const TQString placeOutside = i18n("Drop Outside of Hazard");
const TQString rehit = i18n("Rehit From Last Location");
options << placeOutside << rehit;
const TQString 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)
{
TQCanvasItemList list = ball->collisions(true);
bool keepMoving = false;
while (!list.isEmpty())
{
TQCanvasItem *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();
TQTimer::singleShot(100, this, TQT_SLOT(emitMax()));
return;
}
TQTimer::singleShot(100, this, TQT_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);
TQCanvasItem *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(TQString("%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()
{
TQString text = i18n("Hole %1: par %2, maximum %3 strokes").arg(curHole).arg(holeInfo.par()).arg(holeInfo.maxStrokes());
infoText->move((width - TQFontMetrics(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()) + TQString("\n")
+ i18n("Created by %1").arg(holeInfo.author()) + TQString("\n")
+ i18n("%1 holes").arg(highestHole),
i18n("Course Information"),
addDontShowAgain? holeInfo.name() + TQString(" ") + holeInfo.author() : TQString());
}
void KolfGame::hideInfo()
{
infoText->setText("");
infoText->setVisible(false);
emit newStatusText(TQString());
}
void KolfGame::openFile()
{
Object *curObj = 0;
TQCanvasItem *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(TQString("%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);
TQStringList missingPlugins;
TQStringList groups = cfg->groupList();
int numItems = 0;
int _highestHole = 0;
for (TQStringList::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 TQString 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;
TQCanvasItem *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, TQString("<p>&lt;http://katzbrown.com/kolf/Plugins/&gt;</p><p>") + i18n("This hole uses the following plugins, which you do not have installed:") + TQString("</p>"), missingPlugins, TQString(), TQString("%1 warning").arg(holeInfo.untranslatedName() + TQString::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!
TQCanvasItem *qcanvasItem = 0;
TQPtrList<CanvasItem> todo;
TQPtrList<TQCanvasItem> 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
{
TQString 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(TQPtrList<TQCanvasItem> list)
{
TQCanvasItem *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)
{
TQCanvasItem *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;
TQCanvasItem *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()
{
TQCanvasItem *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 TQString &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())
{
TQString 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;
TQCanvasItem *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;
}
}
TQStringList groups = cfg->groupList();
// wipe out all groups from this hole
for (TQStringList::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(TQString("%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(TQString("%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
TQCanvasItem *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(TQString 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 + TQString::fromLatin1(".wav");
// not needed when all of the files are in the distribution
//if (!TQFile::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)
{
TQPainter p(&pr);
TQPaintDeviceMetrics metrics(&pr);
// translate to center
p.translate(metrics.width() / 2 - course->rect().width() / 2, metrics.height() / 2 - course->rect().height() / 2);
TQPixmap pix(width, height);
TQPainter pixp(&pix);
course->drawArea(course->rect(), &pixp);
p.drawPixmap(0, 0, pix);
p.setPen(TQPen(black, 2));
p.drawRect(course->rect());
p.resetXForm();
if (pr.option("kde-kolf-title") == "true")
{
TQString text = i18n("%1 - Hole %2; by %3").arg(holeInfo.name()).arg(curHole).arg(holeInfo.author());
TQFont font(kapp->font());
font.setPointSize(18);
TQRect rect = TQFontMetrics(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 TQString& 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)
{
TQString group = TQString("%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(TQString::number(i));
players.append(Player());
players.last().ball()->setColor(config->readEntry("Color", "#ffffff"));
players.last().setName(config->readEntry("Name"));
players.last().setId(i);
TQStringList scores(config->readListEntry("Scores"));
TQValueList<int> intscores;
for (TQStringList::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
TQStringList groups = config->groupList();
for (TQStringList::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(TQString::number((*it).id()));
config->writeEntry("Name", (*it).name());
config->writeEntry("Color", TQString((*it).ball()->color().name()));
TQStringList scores;
TQValueList<int> intscores = (*it).scores();
for (TQValueList<int>::Iterator it = intscores.begin(); it != intscores.end(); ++it)
scores.append(TQString::number(*it));
config->writeEntry("Scores", scores);
}
}
CourseInfo::CourseInfo()
: name(i18n("Course Name")), author(i18n("Course Author")), holes(0), par(0)
{
}
#include "game.moc"