Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's C++ page.

 

 

 

 

 

(C++) Boenken source code (version 3.1)

 

Boenken version 3.1 source code.

 

Operating system: Ubuntu 10.04 LTS Lucid Lynx

IDE: Qt Creator 2.0.0

Project type: GUI application

Compiler: G++ 4.4.1

Libraries used:

 

 

 

 

 

Qt project file

 

#-------------------------------------------------
#
# Project created by QtCreator 2010-08-13T10:40:55
#
#-------------------------------------------------

QT += core gui

TARGET = Boenken
TEMPLATE = app


SOURCES += main.cpp\
dialogmain.cpp \
sprite.cpp \
dialogmenu.cpp \
dialogcontrols.cpp \
dialogplayers.cpp \
dialogabout.cpp \
dialogarena.cpp \
spritemoving.cpp \
spritenonmoving.cpp \
spriteplayer.cpp \
spriteball.cpp \
boenken.cpp \
dialogpresskey.cpp \
dialogwhatsnew.cpp

HEADERS += dialogmain.h \
sprite.h \
dialogmenu.h \
dialogcontrols.h \
dialogplayers.h \
dialogabout.h \
dialogarena.h \
arenasettings.h \
spritemoving.h \
spritenonmoving.h \
spriteplayer.h \
spriteball.h \
boenken.h \
controls.h \
dialogpresskey.h \
dialogwhatsnew.h

FORMS += dialogmain.ui \
dialogmenu.ui \
dialogcontrols.ui \
dialogplayers.ui \
dialogabout.ui \
dialogarena.ui \
dialogpresskey.ui \
dialogwhatsnew.ui

RESOURCES += \
resources.qrc

 

 

 

 

 

arenasettings.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef ARENASETTINGS_H
#define ARENASETTINGS_H

#include <utility>

enum Formation
{
  straight_line,
  circle_inward,
  circle_outward
};

///ArenaSettings holds the parameters set in DialogArena
struct ArenaSettings
{
  std::pair<int,int> screen_size;
  int width() const { return screen_size.first; }
  int height() const { return screen_size.second; }
  int n_balls;
  int n_obstacles;
  Formation formation;
  double friction;
};

#endif // ARENASETTINGS_H

 

 

 

 

 

boenken.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>

#include <boost/foreach.hpp>

#include <QPainter>

#include "boenken.h"
#include "sprite.h"
#include "spriteball.h"
#include "spritemoving.h"
#include "spritenonmoving.h"
#include "spriteplayer.h"

Boenken::Boenken(
  const ArenaSettings& arena_settings,
  const Controls& controls,
  std::vector<boost::shared_ptr<SpritePlayer   > > players,
  std::vector<boost::shared_ptr<SpriteBall     > > balls,
  std::vector<boost::shared_ptr<SpriteNonMoving> > obstacles)
  : m_players(players),
    m_balls(balls),
    m_obstacles(obstacles),
    m_moving_sprites(CollectMovingSprites(players,balls)),
    m_sprites(CollectSprites(players,balls,obstacles)),
    m_arena_settings(arena_settings),
    m_controls(controls)
{
  ///Reset the score to 0-0
  SpriteBall::ResetScore();

  ///Set the friction
  SpriteMoving::SetFriction(m_arena_settings.friction);

  ///Set the arena size for the Sprites
  Sprite::setArenaSize(this->getWidth(),this->getHeight());

  ///Keep the right goal posts at the right side
  ///Assume this is done!
  assert(m_obstacles.size() >= 4
    && "There must be at least four goal posts");
  //m_obstacles[2]->setX(width - m_obstacles[2]->m_size);
  //m_obstacles[3]->setX(width - m_obstacles[3]->m_size);

  ///Set the goal area
  assert(m_obstacles.size() >= 4
    && "There must be at least four goal posts");
  const double goal_y_top    = m_obstacles[0]->getY();
  const double goal_y_bottom = m_obstacles[1]->getY();
  assert(goal_y_top <= goal_y_bottom);
  SpriteBall::SetGoalPoles(goal_y_top,goal_y_bottom);
}

void Boenken::drawPlayers(QPainter& painter) const
{
  BOOST_FOREACH(const Sprite * const s,m_sprites)
  {
    s->Draw(painter);
  }
}

int Boenken::getHeight() const
{
  return m_arena_settings.height();
}

const std::pair<int,int> Boenken::GetScore() const
{
  return SpriteBall::GetScore();
}

int Boenken::getWidth() const
{
  return m_arena_settings.width();
}


void Boenken::pressKey(const int key)
{
  const std::size_t n_players = m_controls.m_names.size();
  for (std::size_t i=0; i!=n_players; ++i)
  {
    if (key == m_controls.m_keys_accel[i]) m_players[i]->Accelerate();
    if (key == m_controls.m_keys_turn[i] ) m_players[i]->TurnRight();
  }
  switch (key)
  {
    ///F1 is the debugging key
    ///F2 is the debugging key, after which there is a quit
    case Qt::Key_F1:
    case Qt::Key_F2:
    {
      std::clog << "Player coordinats:\n";
      BOOST_FOREACH(boost::shared_ptr<SpritePlayer>& s,m_players)
      {
        std::clog << s.get() << ": (" << s->getX() << "," << s->getY() << ")\n";
      }
      std::clog << "Ball coordinats:\n";
      BOOST_FOREACH(boost::shared_ptr<SpriteBall>& s,m_balls)
      {
        std::clog << s.get() << ": (" << s->getX() << "," << s->getY() << ")\n";
      }
      std::clog << "Moving sprite coordinats (must match above):\n";
      BOOST_FOREACH(SpriteMoving * s,m_moving_sprites)
      {
        std::clog << s << ": (" << s->getX() << "," << s->getY() << ")\n";
      }
    }
  }
  if (key == Qt::Key_F2) std::exit(1);
}



///Moves all sprites
void Boenken::tick()
{
  ///Move all moving sprites
  BOOST_FOREACH(SpriteMoving* const s,m_moving_sprites)
  {
    assert(s);
    s->Move();
  }

  ///Check all moving sprite collisions with moving sprites
  {
    BOOST_FOREACH(SpriteMoving* const s1,m_moving_sprites)
    {
      BOOST_FOREACH(SpriteMoving* const s2,m_moving_sprites)
      {
        assert(s1);
        assert(s2);
        if (s1 <= s2) continue;
        SpritePlayer::Collision(s1,s2);
      }
    }
  }

  ///Check all moving sprite collisions with non-moving sprites
  {
    BOOST_FOREACH(boost::shared_ptr<SpriteNonMoving>& s1,m_obstacles)
    {
      BOOST_FOREACH(SpriteMoving* const s2,m_moving_sprites)
      {
        SpriteNonMoving::Collision(s1.get(),s2);
      }
    }
  }
}









///Collect all moving sprites
const std::vector<SpriteMoving*> CollectMovingSprites(
  std::vector<boost::shared_ptr<SpritePlayer> > players,
  std::vector<boost::shared_ptr<SpriteBall  > > balls)
{
  std::vector<SpriteMoving*> v;
  BOOST_FOREACH(boost::shared_ptr<SpritePlayer> i,players)
  {
    assert(i);
    SpriteMoving * const s = i.get();
    assert(s);
    v.push_back(s);
  }
  BOOST_FOREACH(boost::shared_ptr<SpriteBall> i,balls)
  {
    assert(i);
    SpriteMoving * const s = i.get();
    assert(s);
    v.push_back(s);
  }
  return v;
}

const std::vector<Sprite*> CollectSprites(
  std::vector<boost::shared_ptr<SpritePlayer   > > players,
  std::vector<boost::shared_ptr<SpriteBall     > > balls,
  std::vector<boost::shared_ptr<SpriteNonMoving> > obstacles)
{
  std::vector<Sprite*> v;
  BOOST_FOREACH(boost::shared_ptr<SpritePlayer> i,players)
  {
    assert(i);
    Sprite * const s = i.get();
    assert(s);
    v.push_back(s);
  }
  BOOST_FOREACH(boost::shared_ptr<SpriteBall> i,balls)
  {
    assert(i);
    Sprite * const s = i.get();
    assert(s);
    v.push_back(s);
  }
  BOOST_FOREACH(boost::shared_ptr<SpriteNonMoving> i,obstacles)
  {
    assert(i);
    Sprite * const s = i.get();
    assert(s);
    v.push_back(s);
  }
  return v;
}

 

 

 

 

 

boenken.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef BOENKEN_H
#define BOENKEN_H

#include <vector>
#include <boost/shared_ptr.hpp>

#include "arenasettings.h"
#include "controls.h"

struct QPainter;
struct Sprite;
struct SpriteBall;
struct SpriteMoving;
struct SpriteNonMoving;
struct SpritePlayer;

///Boenken contains the game.
///Boenken can be displayed by DialogMain
struct Boenken
{
  Boenken(
    const ArenaSettings& arena_settings,
    const Controls& controls,
    std::vector<boost::shared_ptr<SpritePlayer   > > players,
    std::vector<boost::shared_ptr<SpriteBall     > > balls,
    std::vector<boost::shared_ptr<SpriteNonMoving> > obstacles);

  void drawPlayers(QPainter& painter) const;
  int getWidth()  const;
  int getHeight() const;
  const std::pair<int,int> GetScore() const;
  void pressKey(const int key);
  //void resize(const int width, const int height);
  void tick();

  private:
  ///'Real' sprites
  std::vector<boost::shared_ptr<SpritePlayer   > > m_players;
  std::vector<boost::shared_ptr<SpriteBall     > > m_balls;
  std::vector<boost::shared_ptr<SpriteNonMoving> > m_obstacles;
  ///Sprite copies for group-specific routines
  const std::vector<SpriteMoving*> m_moving_sprites;
  const std::vector<Sprite      *> m_sprites;
  ///
  const ArenaSettings m_arena_settings;
  const Controls m_controls;
};



const std::vector<SpriteMoving*> CollectMovingSprites(
  std::vector<boost::shared_ptr<SpritePlayer> > players,
  std::vector<boost::shared_ptr<SpriteBall  > > balls);

const std::vector<Sprite*> CollectSprites(
  std::vector<boost::shared_ptr<SpritePlayer   > > players,
  std::vector<boost::shared_ptr<SpriteBall     > > balls,
  std::vector<boost::shared_ptr<SpriteNonMoving> > obstacles);



#endif // BOENKEN_H

 

 

 

 

 

controls.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef CONTROLS_H
#define CONTROLS_H

#include <string>
#include <vector>

///Controls contains the control keys
///and the players' names
///Controls is created by DialogControls
struct Controls
{
  std::vector<int> m_keys_accel;
  std::vector<int> m_keys_turn;
  std::vector<std::string> m_names;
};

#endif // CONTROLS_H

 

 

 

 

 

dialogabout.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "dialogabout.h"
#include "dialogwhatsnew.h"
#include "ui_dialogabout.h"

DialogAbout::DialogAbout(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DialogAbout)
{
  ui->setupUi(this);
  QObject::connect(ui->button_whats_new,SIGNAL(clicked()),this,SLOT(OnWhatsNewClick()));
  QObject::connect(ui->button_about_qt,SIGNAL(clicked()),this,SLOT(OnAboutQtClick()));
  QObject::connect(ui->button_done,SIGNAL(clicked()),this,SLOT(close()));
}

DialogAbout::~DialogAbout()
{
  delete ui;
}

void DialogAbout::changeEvent(QEvent *e)
{
  QDialog::changeEvent(e);
  switch (e->type()) {
  case QEvent::LanguageChange:
    ui->retranslateUi(this);
    break;
  default:
    break;
  }
}

void DialogAbout::OnWhatsNewClick()
{
  DialogWhatsNew d;
  d.exec();
}

void DialogAbout::OnAboutQtClick()
{
  QApplication::aboutQt();
}

 

 

 

 

 

dialogabout.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef DIALOGABOUT_H
#define DIALOGABOUT_H

#include <QDialog>

namespace Ui {
  class DialogAbout;
}

class DialogAbout : public QDialog
{
  Q_OBJECT

public:
  explicit DialogAbout(QWidget *parent = 0);
  ~DialogAbout();

protected:
  void changeEvent(QEvent *e);

private:
  Ui::DialogAbout *ui;
private slots:
  void OnAboutQtClick();
  void OnWhatsNewClick();
};

#endif // DIALOGABOUT_H

 

 

 

 

 

dialogarena.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <cassert>

#include <QDesktopWidget>

#include "dialogarena.h"
#include "ui_dialogarena.h"

DialogArena::DialogArena(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DialogArena)
{
  ui->setupUi(this);
  QObject::connect(ui->button_done,SIGNAL(clicked()),this,SLOT(close()));
}

DialogArena::~DialogArena()
{
  delete ui;
}

void DialogArena::changeEvent(QEvent *e)
{
  QDialog::changeEvent(e);
  switch (e->type()) {
  case QEvent::LanguageChange:
    ui->retranslateUi(this);
    break;
  default:
    break;
  }
}

///The purpose of DialogArena is to create
///an ArenaSettings
const ArenaSettings DialogArena::GetSettings() const
{
  ArenaSettings s;
  s.formation = this->GetFormation();
  s.friction = this->GetFriction();
  s.n_balls = this->GetNballs();
  s.n_obstacles = this->GetNobstacles();
  s.screen_size = this->GetScreenSize();
  return s;
}

const std::pair<int,int> DialogArena::GetScreenSize() const
{
  //Makes the code shorter and copy-pastable
  const QComboBox * const b = ui->box_screen_size;
  assert(b->count()==2);
  switch(b->currentIndex())
  {
    case 0:
      assert(b->currentText() == "Full screen");
      {
        // -8, because of the application fame
        const int width
          = QApplication::desktop()->width()
          - 8;
        // -50, because of the app's title bar and those Ubuntu panels
        const int height
          = QApplication::desktop()->height() - 96;
        return std::make_pair(width,height);
      }
    case 1:
      assert(b->currentText() == "640x400");
      return std::make_pair(640,400);
    default:
      assert(!"Should not get here");
  }
}

int DialogArena::GetNballs() const
{
  //Makes the code shorter and copy-pastable
  const QComboBox * const b = ui->box_nballs;
  assert(b->count()==2);
  switch(b->currentIndex())
  {
    case 0:
      assert(b->currentText() == "One");
      return 1;
    case 1:
      assert(b->currentText() == "Two");
      return 2;
    default:
      assert(!"Should not get here");
  }
}

int DialogArena::GetNobstacles() const
{
  //Makes the code shorter and copy-pastable
  const QComboBox * const b = ui->box_obstacles;
  assert(b->count()==2);
  switch(b->currentIndex())
  {
    case 0:
      assert(b->currentText() == "None");
      return 0;
    case 1:
      assert(b->currentText() == "Two");
      return 2;
    default:
      assert(!"Should not get here");
  }
}

Formation DialogArena::GetFormation() const
{
  //Makes the code shorter and copy-pastable
  const QComboBox * const b = ui->box_formation;
  assert(b->count()==3);
  switch(b->currentIndex())
  {
    case 0:
      assert(b->currentText() == "Line");
      return straight_line;
    case 1:
      assert(b->currentText() == "Circle, inward");
      return circle_inward;
    case 2:
      assert(b->currentText() == "Circle, outward");
      return circle_outward;
    default:
      assert(!"Should not get here");
  }
}

double DialogArena::GetFriction() const
{
  //Makes the code shorter and copy-pastable
  const QComboBox * const b = ui->box_friction;
  assert(b->count()==2);
  switch(b->currentIndex())
  {
    case 0:
      assert(b->currentText() == "None");
      return 1.0;
    case 1:
      assert(b->currentText() == "Low");
      return 0.999;
    default:
      assert(!"Should not get here");
  }
}


 

 

 

 

 

dialogarena.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef DIALOGARENA_H
#define DIALOGARENA_H

#include <QDialog>
#include "arenasettings.h"

namespace Ui {
  class DialogArena;
}

///DialogArena
///
///Dialog to setup up the arena
class DialogArena : public QDialog
{
  Q_OBJECT

public:
  explicit DialogArena(QWidget *parent = 0);
  ~DialogArena();
  const std::pair<int,int> GetScreenSize() const;
  int GetNballs() const;
  int GetNobstacles() const;
  Formation GetFormation() const;
  double GetFriction() const;
  const ArenaSettings GetSettings() const;

protected:
  void changeEvent(QEvent *e);

private:
  Ui::DialogArena *ui;
};

#endif // DIALOGARENA_H

 

 

 

 

 

dialogcontrols.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <boost/lexical_cast.hpp>

#include "dialogcontrols.h"
#include "dialogpresskey.h"

#include "ui_dialogcontrols.h"

DialogControls::DialogControls(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DialogControls)
{
  ui->setupUi(this);
  QObject::connect(ui->button_done,SIGNAL(clicked()),this,SLOT(close()));
  m_keys_accel.push_back(Qt::Key_W);
  m_keys_accel.push_back(Qt::Key_Up);
  m_keys_turn.push_back(Qt::Key_D);
  m_keys_turn.push_back(Qt::Key_Right);

  QObject::connect(ui->button_accelerate_1,SIGNAL(clicked()),this,SLOT(onAccelerate1()));
  QObject::connect(ui->button_accelerate_2,SIGNAL(clicked()),this,SLOT(onAccelerate2()));
  QObject::connect(ui->button_turn_1,SIGNAL(clicked()),this,SLOT(onTurn1()));
  QObject::connect(ui->button_turn_2,SIGNAL(clicked()),this,SLOT(onTurn2()));

  showKeys();
}

DialogControls::~DialogControls()
{
  delete ui;
}

void DialogControls::changeEvent(QEvent *e)
{
  QDialog::changeEvent(e);
  switch (e->type()) {
  case QEvent::LanguageChange:
    ui->retranslateUi(this);
    break;
  default:
    break;
  }
}

void DialogControls::showKeys()
{
  ui->button_accelerate_1->setText(
    boost::lexical_cast<std::string>(m_keys_accel[0]).c_str() );
  ui->button_accelerate_2->setText(
    boost::lexical_cast<std::string>(m_keys_accel[1]).c_str() );
  ui->button_turn_1->setText(
    boost::lexical_cast<std::string>(m_keys_turn[0]).c_str() );
  ui->button_turn_2->setText(
    boost::lexical_cast<std::string>(m_keys_turn[1]).c_str() );

}

void DialogControls::onAccelerate1()
{
  DialogPressKey d;
  d.exec();
  const int key = d.GetKey();
  m_keys_accel[0] = key;
  showKeys();
}

void DialogControls::onAccelerate2()
{
  DialogPressKey d;
  d.exec();
  const int key = d.GetKey();
  m_keys_accel[1] = key;
  showKeys();
}

void DialogControls::onTurn1()
{
  DialogPressKey d;
  d.exec();
  const int key = d.GetKey();
  m_keys_turn[0] = key;
  showKeys();
}

void DialogControls::onTurn2()
{
  DialogPressKey d;
  d.exec();
  const int key = d.GetKey();
  m_keys_turn[1] = key;
  showKeys();
}

std::vector<int> DialogControls::getKeysAccel() const
{
  return m_keys_accel;
}

std::vector<int> DialogControls::getKeysTurn() const
{
  return m_keys_turn;
}

std::vector<std::string> DialogControls::getNames() const
{
  std::vector<std::string> v;
  v.push_back(ui->edit_name1->text().toStdString());
  v.push_back(ui->edit_name2->text().toStdString());
  return v;
}

Controls DialogControls::GetControls() const
{
  Controls c;
  c.m_keys_accel = this->getKeysAccel();
  c.m_keys_turn = this->getKeysTurn();
  c.m_names = this->getNames();
  return c;
}

 

 

 

 

 

dialogcontrols.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef DIALOGCONTROLS_H
#define DIALOGCONTROLS_H

#include <string>
#include <vector>

#include <QDialog>

#include "controls.h"

namespace Ui {
  class DialogControls;
}

///DialogControls
///
///This dialog connects player names to their controls
///(but it does not connect sprites to their controllers,
///this is what DialogPlayers is for)
class DialogControls : public QDialog
{
  Q_OBJECT

public:
  explicit DialogControls(QWidget *parent = 0);
  ~DialogControls();
  Controls GetControls() const;

protected:
  void changeEvent(QEvent *e);

private:
  Ui::DialogControls *ui;
  std::vector<int> m_keys_accel;
  std::vector<int> m_keys_turn;

private slots:
  void onAccelerate1();
  void onAccelerate2();
  void onTurn1();
  void onTurn2();
  void showKeys();

  std::vector<int> getKeysAccel() const;
  std::vector<int> getKeysTurn() const;
  std::vector<std::string> getNames() const;
};

#endif // DIALOGCONTROLS_H

 

 

 

 

 

dialogmain.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <cassert>
#include <cstdlib>
#include <iostream>

#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/numeric/conversion/cast.hpp>

#include <QDesktopWidget>
#include <QKeyEvent>
#include <QPainter>

#include "boenken.h"
#include "dialogmain.h"
#include "ui_dialogmain.h"

///All parameters are fed into the contructor
DialogMain::DialogMain(
  QWidget *parent,
  boost::shared_ptr<Boenken> boenken)
  : QDialog(parent),
    ui(new Ui::DialogMain),
    m_background(new QPixmap),
    m_timer(new QTimer),
    m_timer_countdown(new QTimer),
    m_boenken(boenken)
{
  ui->setupUi(this);


  ///Set GUI size to Boenken size
  this->setGeometry(0,0,m_boenken->getWidth(),m_boenken->getHeight());
  ///Size the black background
  m_background.reset(new QPixmap(m_boenken->getWidth(),m_boenken->getHeight()));
  Paint(*m_background,1,1,1); //Black
  ///Put the dialog in the screen center
  const QRect screen = QApplication::desktop()->screenGeometry();
  this->move( screen.center() - this->rect().center() );
  ///Disallow resize from now on
  this->setFixedWidth(m_boenken->getWidth());
  this->setFixedHeight(m_boenken->getHeight());

  ///Start the timer
  QObject::connect( m_timer.get(),SIGNAL(timeout()),this,SLOT(onTimer()));
  QObject::connect( m_timer_countdown.get(),SIGNAL(timeout()),this,SLOT(onCountdownTimer()));
  m_timer_countdown->start(1000);
}

DialogMain::~DialogMain()
{
  m_timer->stop();
  m_timer_countdown->stop();
  delete ui;
}

void DialogMain::changeEvent(QEvent *e)
{
  QDialog::changeEvent(e);
  switch (e->type()) {
  case QEvent::LanguageChange:
    ui->retranslateUi(this);
    break;
  default:
    break;
  }
}


void DialogMain::paintEvent(QPaintEvent*)
{
  QPainter painter(this);

  ///Draw background on painter
  painter.drawPixmap(rect(),*m_background);

  ///Draw all sprites on painter
  {
    //Set the pen to white and 3 pixels wide
    QPen pen = painter.pen();
    pen.setWidth(3);
    pen.setColor(QColor(255,255,255));
    painter.setPen(pen);
  }
  ///Call Boenken to draw the sprites
  m_boenken->drawPlayers(painter);
  ///Draw score on painter
  {
    QFont font = painter.font();
    font.setFamily("Courier");
    painter.setFont(font);
    const std::pair<int,int> scores = m_boenken->GetScore();
    const QString score_left
      = QString(boost::lexical_cast<std::string>(scores.first).c_str());
    const QString score_right
      = boost::lexical_cast<std::string>(scores.second).c_str();
    painter.drawText(120,0,80,20,0,score_left);
    painter.drawText(440,0,80,20,0,score_right);
  }
}

void DialogMain::keyPressEvent(QKeyEvent * e)
{
  //Assume that a key is pressed
  //(which according to the Qt doc is always true)
  assert(e->type() == QEvent::KeyPress);
  m_boenken->pressKey(e->key());
}

void DialogMain::onCountdownTimer()
{

  m_timer_countdown->stop();
  m_timer->start(20);
}

///The main method: every 50msec the Sprites
///move and are drawn to the screen
void DialogMain::onTimer()
{
  ///Boenken moves all sprites
  m_boenken->tick();
  ///Repaint the screen
  this->repaint();
}

boost::shared_ptr<Boenken> CreateNoBoenken()
{
  boost::shared_ptr<Boenken> p;
  return p;
}

/// paint a Pixmap to a single color fast,
/// from http://www.richelbilderbeek.nl/CppPaint.htm
void Paint(
  QPixmap& pixmap,
  const unsigned char r,
  const unsigned char g,
  const unsigned char b,
  const unsigned char a)
{
  const int width = pixmap.width();
  const int height = pixmap.height();

  QImage image = pixmap.toImage();

  assert(image.bytesPerLine() / width == 4
    && "Assume there are 4 bytes per pixel");

  for (int y=0; y!=height; ++y)
  {
    unsigned char * const line
      = static_cast<unsigned char *>(image.scanLine(y));
    for (int x=0; x!=width; ++x)
    {
      line[x*4+3] = a; //Alpha value
      line[x*4+2] = r; //Red
      line[x*4+1] = g; //Green
      line[x*4+0] = b; //Blue
    }
  }
  pixmap = pixmap.fromImage(image);
}

 

 

 

 

 

dialogmain.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef DIALOGMAIN_H
#define DIALOGMAIN_H

#include <vector>

#include <boost/shared_ptr.hpp>

#include <QDialog>
#include <QPixmap>
#include <QTimer>

struct Boenken;
boost::shared_ptr<Boenken> CreateNoBoenken();

namespace Ui {
  class DialogMain;
}

///DialogMain displays Boenken and handles user events
class DialogMain : public QDialog
{
  Q_OBJECT

public:
  explicit DialogMain(
    QWidget *parent = 0,
    boost::shared_ptr<Boenken> boenken = CreateNoBoenken()
  );
  ~DialogMain();

protected:
  void changeEvent(QEvent *e);

private:
  //UI
  Ui::DialogMain *ui;
  //Graphics
  boost::shared_ptr<QPixmap> m_background;
  //Other Qthings
  ///The main game timer
  boost::shared_ptr<QTimer> m_timer;
  ///The timer that does the countdown
  boost::shared_ptr<QTimer> m_timer_countdown;
  //Other member variables
  boost::shared_ptr<Boenken> m_boenken;
  //Overloaded methods
  void paintEvent(QPaintEvent*);
  void keyPressEvent(QKeyEvent*);

private slots:
  void onTimer();
  void onCountdownTimer();

};

/// paint a Pixmap to a single color fast,
/// from http://www.richelbilderbeek.nl/CppPaint.htm
void Paint(
  QPixmap& pixmap,
  const unsigned char r,
  const unsigned char g,
  const unsigned char b,
  const unsigned char a = 255);

#endif // DIALOGMAIN_H

 

 

 

 

 

dialogmenu.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <boost/numeric/conversion/cast.hpp>

#include "arenasettings.h"
#include "boenken.h"
#include "dialogabout.h"
#include "dialogarena.h"
#include "dialogcontrols.h"
#include "dialogmain.h"
#include "dialogmenu.h"
#include "dialogplayers.h"
#include "spriteball.h"
#include "spritemoving.h"
#include "spritenonmoving.h"
#include "spriteplayer.h"
#include "ui_dialogmenu.h"

DialogMenu::DialogMenu(QWidget *parent) :
  QDialog(parent),
  ui(new Ui::DialogMenu),
  m_controls(new DialogControls),
  m_players( new DialogPlayers),
  m_arena(new DialogArena)

{
  ui->setupUi(this);
  QObject::connect(ui->button_set_controls,SIGNAL(clicked()),this,SLOT(onControlsClick()));
  QObject::connect(ui->button_set_players,SIGNAL(clicked()),this,SLOT(onPlayersClick()));
  QObject::connect(ui->button_set_arena,SIGNAL(clicked()),this,SLOT(onArenaClick()));
  QObject::connect(ui->button_start,SIGNAL(clicked()),this,SLOT(onStartClick()));
  QObject::connect(ui->button_about,SIGNAL(clicked()),this,SLOT(onAboutClick()));
  QObject::connect(ui->button_quit,SIGNAL(clicked()),this,SLOT(close()));
}

DialogMenu::~DialogMenu()
{
  delete ui;
}

void DialogMenu::changeEvent(QEvent *e)
{
  QDialog::changeEvent(e);
  switch (e->type()) {
  case QEvent::LanguageChange:
    ui->retranslateUi(this);
    break;
  default:
    break;
  }
}


void DialogMenu::onControlsClick()
{
  this->hide();
  this->m_controls->exec();
  this->show();
}

void DialogMenu::onPlayersClick()
{
  this->hide();
  this->m_players->exec();
  this->show();
}

void DialogMenu::onArenaClick()
{
  this->hide();
  this->m_arena->exec();
  this->show();
}

void DialogMenu::onStartClick()
{
  const ArenaSettings a = this->m_arena->GetSettings();
  const Controls c = this->m_controls->GetControls();
  const std::vector<boost::shared_ptr<SpritePlayer   > > players   = CreatePlayers(a);
  const std::vector<boost::shared_ptr<SpriteBall     > > balls     = CreateBalls(a);
  const std::vector<boost::shared_ptr<SpriteNonMoving> > obstacles = CreateObstacles(a);
  boost::shared_ptr<Boenken> b(new Boenken(a,c,players,balls,obstacles));
  assert(b);
  DialogMain d(0,b);

  this->hide();
  d.exec();
  this->show();
}

void DialogMenu::onAboutClick()
{
  DialogAbout d;
  this->hide();
  d.exec();
  this->show();
}

const std::vector<boost::shared_ptr<SpritePlayer> > CreatePlayers(
  const ArenaSettings& a)
{
  //Create two
  std::vector<boost::shared_ptr<SpritePlayer> > v;
  const int n_players = 2;
  const int size = 32;

  const double mid_x
    = boost::numeric_cast<double>(a.screen_size.first) / 2.0;
  const double mid_y
    = boost::numeric_cast<double>(a.screen_size.second) / 2.0;

  if (a.formation==straight_line)
  {
    const int x_left  = (1 * (a.width() / 4) ) - (size / 2);
    const int x_right = (3 * (a.width() / 4) ) - (size / 2);
    const int players_height = size * (n_players / 2);
    int y = (a.height()) / 2 - (players_height / 2);
    for (int i=0; i!=n_players; ++i,y+=(size/2))
    {
      const int x = (i % 2 ? x_right : x_left);
      const double face_angle = (i % 2 ? 1.5 * M_PI : 0.5 * M_PI);
      const unsigned char r = (i % 2 ?   0 : 255);
      const unsigned char g = (i % 2 ?   0 :   0);
      const unsigned char b = (i % 2 ? 255 :   0);
      boost::shared_ptr<SpritePlayer> p(
        new SpritePlayer(x,y,face_angle,size,r,g,b));
      assert(p);
      v.push_back(p);
    }

  }
  else
  {
    assert(a.formation==circle_inward || a.formation==circle_outward);
    const double ray_x = mid_x / 2.0;
    const double ray_y = mid_y / 2.0;
    const double d_angle = (2.0 * M_PI) / boost::numeric_cast<double>(n_players);
    double angle = 1.5 * M_PI;

    for (int i=0; i!=n_players; ++i,angle+=d_angle)
    {
      const double x
       = mid_x + (std::sin(angle) * ray_x)
       - (boost::numeric_cast<double>(size) / 2.0);
      const double y
        = mid_y - (std::cos(angle) * ray_y)
        - (boost::numeric_cast<double>(size) / 2.0);
      const unsigned char r = (i % 2 ?   0 : 255);
      const unsigned char g = (i % 2 ?   0 :   0);
      const unsigned char b = (i % 2 ? 255 :   0);
      const double face_angle = angle
        + (a.formation==circle_inward ? M_PI : 0.0);

      boost::shared_ptr<SpritePlayer> p(
        new SpritePlayer(x,y,face_angle,size,r,g,b));
      assert(p);
      v.push_back(p);
    }
  }

  return v;
}

const std::vector<boost::shared_ptr<SpriteBall> > CreateBalls(
  const ArenaSettings& a)
{
  const int size = 32;
  //A ball is always green
  const unsigned char r =   0;
  const unsigned char g = 255;
  const unsigned char b =   0;
  std::vector<boost::shared_ptr<SpriteBall> > v;
  switch (a.n_balls)
  {
    case 1:
    {
      const int x
        = a.screen_size.first / 2 - (size / 2);
      const int y
        = a.screen_size.second / 2 - (size / 2);
      boost::shared_ptr<SpriteBall> s(
        new SpriteBall(x,y,size,r,g,b));
      assert(s);
      v.push_back(s);
    }
    break;
    case 2:
    {
      const int x1
        = (a.screen_size.first / 2)
        - size;
      const int x2 = x1 + size + 1;
      const int y
        = (a.screen_size.second / 2)
        - (size / 2);
      boost::shared_ptr<SpriteBall> s1(
        new SpriteBall(x1,y,size,r,g,b));
      boost::shared_ptr<SpriteBall> s2(
        new SpriteBall(x2,y,size,r,g,b));
      assert(s1);
      assert(s2);
      v.push_back(s1);
      v.push_back(s2);
    }
    break;
    default:
      assert(!"Should not get here");
  }
  return v;
}

///Always creates the four goalposts
const std::vector<boost::shared_ptr<SpriteNonMoving> > CreateObstacles(
  const ArenaSettings& a)
{
  const int size = 32;
  //A obstacle is always grey
  const unsigned char r = 255;
  const unsigned char g = 255;
  const unsigned char b = 255;

  std::vector<boost::shared_ptr<SpriteNonMoving> > v;
  {
    //Top-left goalpost
    const int x = 0;
    const int y = a.screen_size.second / 4;
    boost::shared_ptr<SpriteNonMoving> s(
      new SpriteNonMoving(x,y,size,r,g,b));
    v.push_back(s);
  }
  {
    //Bottom-left goalpost
    const int x = 0;
    const int y = 3 * a.screen_size.second / 4;
    boost::shared_ptr<SpriteNonMoving> s(
      new SpriteNonMoving(x,y,size,r,g,b));
    assert(s);
    v.push_back(s);
  }
  {
    //Top-left goalpost
    const int x = a.screen_size.first - size;
    const int y = a.screen_size.second / 4;
    boost::shared_ptr<SpriteNonMoving> s(
      new SpriteNonMoving(x,y,size,r,g,b));
    assert(s);
    v.push_back(s);
  }
  {
    //Bottom-left goalpost
    const int x = a.screen_size.first - size;
    const int y = 3 * a.screen_size.second / 4;
    boost::shared_ptr<SpriteNonMoving> s(
      new SpriteNonMoving(x,y,size,r,g,b));
    assert(s);
    v.push_back(s);
  }
  if (a.n_obstacles == 2)
  {
    {
      //Left obstacle
      const int x = ( (1 * a.width() ) / 8) - (size / 2);
      const int y = ( (1 * a.height()) / 2) - (size / 2);
      boost::shared_ptr<SpriteNonMoving> s(
        new SpriteNonMoving(x,y,size,r,g,b));
      assert(s);
      v.push_back(s);
    }
    {
      //Right obstacle
      const int x = ( (7 * a.width() ) / 8) - (size / 2);
      const int y = ( (1 * a.height()) / 2) - (size / 2);
      boost::shared_ptr<SpriteNonMoving> s(
        new SpriteNonMoving(x,y,size,r,g,b));
      assert(s);
      v.push_back(s);
    }

  }
  return v;
}

 

 

 

 

 

dialogmenu.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef DIALOGMENU_H
#define DIALOGMENU_H

#include <vector>
#include <boost/shared_ptr.hpp>
#include <QDialog>


namespace Ui {
  class DialogMenu;
}

struct ArenaSettings;
struct DialogControls;
struct DialogPlayers;
struct DialogArena;
struct SpriteBall;
struct SpriteNonMoving;
struct SpritePlayer;

///DialogMenu
///
///Gathers all options to start a game
class DialogMenu : public QDialog
{
  Q_OBJECT

public:
  explicit DialogMenu(QWidget *parent = 0);
  ~DialogMenu();

protected:
  void changeEvent(QEvent *e);

private:
  Ui::DialogMenu *ui;
  boost::shared_ptr<DialogControls> m_controls;
  boost::shared_ptr<DialogPlayers> m_players;
  boost::shared_ptr<DialogArena> m_arena;

private slots:
  void onControlsClick();
  void onPlayersClick();
  void onArenaClick();
  void onStartClick();
  void onAboutClick();
};

const std::vector<boost::shared_ptr<SpritePlayer> > CreatePlayers(
  const ArenaSettings& a);

const std::vector<boost::shared_ptr<SpriteBall> > CreateBalls(
  const ArenaSettings& a);

const std::vector<boost::shared_ptr<SpriteNonMoving> > CreateObstacles(
  const ArenaSettings& a);

#endif // DIALOGMENU_H

 

 

 

 

 

dialogplayers.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "dialogplayers.h"
#include "ui_dialogplayers.h"

DialogPlayers::DialogPlayers(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DialogPlayers)
{
  ui->setupUi(this);
  QObject::connect(ui->button_done,SIGNAL(clicked()),this,SLOT(close()));
}

DialogPlayers::~DialogPlayers()
{
  delete ui;
}

void DialogPlayers::changeEvent(QEvent *e)
{
  QDialog::changeEvent(e);
  switch (e->type()) {
  case QEvent::LanguageChange:
    ui->retranslateUi(this);
    break;
  default:
    break;
  }
}

 

 

 

 

 

dialogplayers.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef DIALOGPLAYERS_H
#define DIALOGPLAYERS_H

#include <QDialog>

namespace Ui {
  class DialogPlayers;
}

///Dialog to connect sprites to their controllers\n
///A controller can be\n
///- a human\n
///- a computer\n
class DialogPlayers : public QDialog
{
  Q_OBJECT

public:
  explicit DialogPlayers(QWidget *parent = 0);
  ~DialogPlayers();

protected:
  void changeEvent(QEvent *e);

private:
  Ui::DialogPlayers *ui;
};

#endif // DIALOGPLAYERS_H

 

 

 

 

 

dialogpresskey.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <cassert>

#include <QKeyEvent>

#include "dialogpresskey.h"
#include "ui_dialogpresskey.h"

DialogPressKey::DialogPressKey(QWidget *parent) :
  QDialog(parent),
  ui(new Ui::DialogPressKey),
  m_key(0)
{
  ui->setupUi(this);
}

DialogPressKey::~DialogPressKey()
{
  delete ui;
}

void DialogPressKey::changeEvent(QEvent *e)
{
  QDialog::changeEvent(e);
  switch (e->type()) {
  case QEvent::LanguageChange:
    ui->retranslateUi(this);
    break;
  default:
    break;
  }
}

void DialogPressKey::keyPressEvent(QKeyEvent * e)
{
  //According to the Qt doc, the assert below is always true
  assert(e->type() == QEvent::KeyPress);
  m_key = e->key();
  close();
}

 

 

 

 

 

dialogpresskey.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef DIALOGPRESSKEY_H
#define DIALOGPRESSKEY_H

#include <QDialog>

namespace Ui {
  class DialogPressKey;
}

///DialogPressKey asks the user to press
///any key. The key pressed can be requested
///later, by DialogControls.
class DialogPressKey : public QDialog
{
  Q_OBJECT

public:
  explicit DialogPressKey(QWidget *parent = 0);
  ~DialogPressKey();
  int GetKey() const { return m_key; }

protected:
  void changeEvent(QEvent *e);
  void keyPressEvent(QKeyEvent * e);

private:
  Ui::DialogPressKey *ui;
  int m_key;
};

#endif // DIALOGPRESSKEY_H

 

 

 

 

 

dialogwhatsnew.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "dialogwhatsnew.h"
#include "ui_dialogwhatsnew.h"

DialogWhatsNew::DialogWhatsNew(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DialogWhatsNew)
{
    ui->setupUi(this);
}

DialogWhatsNew::~DialogWhatsNew()
{
    delete ui;
}

void DialogWhatsNew::changeEvent(QEvent *e)
{
    QDialog::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

 

 

 

 

 

dialogwhatsnew.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef DIALOGWHATSNEW_H
#define DIALOGWHATSNEW_H

#include <QDialog>

namespace Ui {
    class DialogWhatsNew;
}

class DialogWhatsNew : public QDialog
{
    Q_OBJECT

public:
    explicit DialogWhatsNew(QWidget *parent = 0);
    ~DialogWhatsNew();

protected:
    void changeEvent(QEvent *e);

private:
    Ui::DialogWhatsNew *ui;
};

#endif // DIALOGWHATSNEW_H

 

 

 

 

 

main.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <QtGui/QApplication>
#include "dialogmenu.h"

/// \mainpage Boenken documentation
///
/// Boenken is a simple game
///
/// Boenken version 3.1\n
/// (C) 2005 Richel Bilderbeek\n
/// programmed at the 14th of August of 2010\n
/// From http://www.richelbilderbeek.nl/GameBoenken.htm\n
/// Licenced under GPL 3.0\n
///
/// \author  Richel Bilderbeek
/// \version 3.1
/// \date    2010-08-14

///Only shows the menu
int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  DialogMenu w;
  w.show();
  return a.exec();
}

 

 

 

 

 

sprite.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <cassert>
#include <cmath>
#include <iostream>

#include <boost/numeric/conversion/cast.hpp>

#include <QBitmap>
#include <QImage>
#include <QPainter>
#include <QPixmap>

#include "sprite.h"

///The maximum x coordinat a Sprite can have
int Sprite::m_maxx = 320;

///The maximum y coordinat a Sprite can have
int Sprite::m_maxy = 200;

Sprite::Sprite(
  const double x,
  const double y,
  const int size,
  const unsigned char r,
  const unsigned char g,
  const unsigned char b)
  : m_size(size),
    m_pixmap(DrawGlobe(size,size,r,g,b)),
    m_x(x),
    m_y(y)
{

}

QRect Sprite::rect() const
{
  return QRect(m_x,m_y,m_size,m_size);
}

const QPixmap& Sprite::pixmap() const
{
  return m_pixmap;
}

void Sprite::Draw(QPainter& painter) const
{
  //Draw the globe
  painter.drawPixmap(this->rect(),this->pixmap());
}

///Sets the arena size,
///that is Sprite::m_maxx and Sprite::m_maxy.
///SpriteBall::SetGoalPoles defines the vertical
///range of the goal posts
void Sprite::setArenaSize(const int width, const int height)
{
  m_maxx = width;
  m_maxy = height;
}

//From http://www.richelbilderbeek.nl/CppDrawGlobe.htm
QPixmap DrawGlobe(
  const int width,
  const int height,
  const unsigned char r,
  const unsigned char g,
  const unsigned char b)
{
  QPixmap pixmap(width,height);
  QImage image = pixmap.toImage();

  assert(image.bytesPerLine() / width == 4
    && "Assume there are 4 bytes per pixel");

  const double r_max = boost::numeric_cast<double>(r);
  const double g_max = boost::numeric_cast<double>(g);
  const double b_max = boost::numeric_cast<double>(b);
  const double midX = boost::numeric_cast<double>(width ) / 2.0;
  const double midY = boost::numeric_cast<double>(height) / 2.0;
  const double max_dist = std::min(midX,midY);

  for (int y=0; y!=height; ++y)
  {

    unsigned char * const line
      = static_cast<unsigned char *>(image.scanLine(y));
    const double y_d = boost::numeric_cast<double>(y);
    for (int x=0; x!=width; ++x)
    {
      const double x_d = boost::numeric_cast<double>(x);
      const double dist
        = std::sqrt(
            ((x_d - midX) * (x_d - midX))
          + ((y_d - midY) * (y_d - midY)) );
      if (dist <= max_dist)
      {
        const double rel_dist = dist / max_dist;
        const int r_here = rel_dist * r_max;
        const int g_here = rel_dist * g_max;
        const int b_here = rel_dist * b_max;
        assert( r_here >= 0);
        assert( r_here < 256);
        assert( g_here >= 0);
        assert( g_here < 256);
        assert( b_here >= 0);
        assert( b_here < 256);
        line[x*4+3] = 255; //Alpha value
        line[x*4+2] = r_here; //Red
        line[x*4+1] = g_here; //Green
        line[x*4+0] = b_here; //Blue
      }
      else
      {
        line[x*4+3] = 0; //Alpha value
        line[x*4+2] = 0; //Red
        line[x*4+1] = 0; //Green
        line[x*4+0] = 0; //Blue
      }
    }
  }
  pixmap = pixmap.fromImage(image);

  //Add transparency
  const QBitmap mask = pixmap.createMaskFromColor(QColor(0,0,0,0).rgb());
  pixmap.setMask(mask);

  return pixmap;
}

//From www.richelbilderbeek.nl/CppGetAngle.htm
double GetAngle(const double dX, const double dY)
{
  //In which quadrant are we?
  if (dX > 0.0)
  {
    if (dY > 0.0)
    {
      //dX > 0.0 && dY > 0.0
      //Quadrant IV
      assert(dX > 0.0 && dY > 0.0);
      const double angle = (1.0 * M_PI) - std::atan(dX / dY);
      assert(angle >= 0.5 * M_PI && angle <= 1.0 * M_PI);
      return angle;
    }
    else if (dY < 0.0)
    {
      //dX > 0.0 && dY <= 0.0
      //Quadrant I
      assert(dX > 0.0 && dY < 0.0);
      const double angle = (0.0 * M_PI) - std::atan(dX / dY);
      assert(angle >= 0.0 * M_PI && angle <= 0.5 * M_PI);
      return angle;
    }
    else
    {
      //dX > 0.0 && dY == 0.0
      //On Y-axis, right side
      assert(dX > 0.0 && dY == 0.0);
      const double angle = 0.5 * M_PI;
      return angle;
    }
  }
  else if (dX < 0.0)
  {
    if (dY > 0.0)
    {
      //dX < 0.0 && dY > 0.0
      //Quadrant III
      assert(dX < 0.0 && dY > 0.0);
      const double angle = (1.0 * M_PI) - std::atan(dX / dY);
      assert(angle >= 1.0 * M_PI && angle <= 1.5 * M_PI);
      return angle;
    }
    else if (dY < 0.0)
    {
      //dX < 0.0 && dY < 0.0
      //Quadrant II
      assert(dX < 0.0 && dY < 0.0);
      const double angle = (2.0 * M_PI) - std::atan(dX / dY);
      assert(angle >= 1.5 * M_PI && angle <= 2.0 * M_PI);
      return angle;
    }
    else
    {
      //dX < 0.0 && dY == 0.0
      //On X-axis
      assert(dX < 0.0 && dY == 0.0);
      const double angle = 1.5 * M_PI;
      return angle;
    }
  }
  else
  {
    if (dY > 0.0)
    {
      //dX == 0 && dY > 0.0)
      //On Y-axis, right side of origin
      assert(dX==0.0 && dY > 0.0);
      const double angle = 1.0 * M_PI;
      return angle;
    }
    else if (dY < 0.0)
    {
      //dX == 0 && dY < 0.0)
      //On Y-axis, left side of origin
      assert(dX==0.0 && dY < 0.0);
      const double angle = 0.0 * M_PI;
      return angle;
    }
    else
    {
      //dX == 0.0 && dY == 0.0)
      //On origin
      assert(dX==0.0 && dY == 0.0);
      const double angle = 0.0 * M_PI;
      return angle;
    }
  }
}

//From http://www.richelbilderbeek.nl/CppDoPerfectElasticCollision.htm
void DoPerfectElasticCollision(
  const double angleCollision,
  double& angle1,
  double& speed1,
  double& angle2,
  double& speed2)
{
  //The length of the impulse of player 1 (assumes both players have equal mass!)
  const double A = speed1;
  //The length of the impulse of player 2 (assumes both players have equal mass!)
  const double E = speed2;
  //The angles between the two globes
  const double c = angleCollision;
  //The angle between c and the impulse direction of player 1
  const double a = c - angle1;
  //The angle between c and the impulse direction of player 2
  const double b = c + M_PI - angle2;

  //Seperate the impulses to their impulses paralel and othoganal the angle of collision
  //The length of the impulse of player 1 parallel to the collision
  const double B = A * std::cos(a);
  //The length of the impulse of player 1 orthogonal to the collision
  const double C = A * std::sin(a);
  //The length of the impulse of player 2 parallel to the collision
  const double F = E * std::cos(b);
  //The length of the impulse of player 2 orthogonal to the collision
  const double G = E * std::sin(b);

  //Seperate the impulses in X and Y directions
  const double BdX = B *  std::sin(c + (0.0 * M_PI));
  const double BdY = B * -std::cos(c + (0.0 * M_PI));
  const double CdX = C *  std::sin(c + (1.5 * M_PI));
  const double CdY = C * -std::cos(c + (1.5 * M_PI));
  const double FdX = F *  std::sin(c + (1.0 * M_PI));
  const double FdY = F * -std::cos(c + (1.0 * M_PI));
  const double GdX = G *  std::sin(c + (0.5 * M_PI));
  const double GdY = G * -std::cos(c + (0.5 * M_PI));

  //The resulting impulses
  //The resulting impulse of player 1 in the X direction
  const double DdX = CdX + FdX;
  //The resulting impulse of player 1 in the Y direction
  const double DdY = CdY + FdY;
  //The resulting impulse of player 2 in the X direction
  const double HdX = BdX + GdX;
  //The resulting impulse of player 2 in the Y direction
  const double HdY = BdY + GdY;

  //Write the final results
  angle1 = GetAngle(DdX, DdY);
  angle2 = GetAngle(HdX, HdY);
  speed1 = std::sqrt( (DdX * DdX) + (DdY * DdY) ); //Pythagoras
  speed2 = std::sqrt( (HdX * HdX) + (HdY * HdY) ); //Pythagoras
}

 

 

 

 

 

sprite.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef SPRITE_H
#define SPRITE_H

#include <cassert>
#include <boost/noncopyable.hpp>

#include <QPixmap>

///Sprite is the ABC of anything that must be drawn on screen
///Sprite is noncopyable
struct Sprite : public boost::noncopyable
{
  Sprite(
    const double x,
    const double y,
    const int size,
    const unsigned char r,
    const unsigned char g,
    const unsigned char b);

  ///The size (width==height) in pixels
  const int m_size;

  ///The globe part of the Sprite
  const QPixmap m_pixmap;

  double getX() const { return m_x; }
  double getY() const { return m_y; }

  ///The x,y,w,h of the sprite
  QRect rect() const;

  const QPixmap& pixmap() const;

  ///Every sprite must be drawn to the screen
  virtual void Draw(QPainter& painter) const;

  ///Every sprite must be within the arena
  static void setArenaSize(const int width, const int height);

  ///dummy_make_me_abstract ensures that
  ///Sprite and SpriteMoving
  ///are abstract base classes
  virtual void dummy_make_me_abstract() const = 0;

  protected:
  double m_x;
  double m_y;
  static int m_maxx;
  static int m_maxy;

  //private:
  //Ensure Sprite can only be deleted by boost::checked_delete
  virtual ~Sprite() {}
  //friend void boost::checked_delete<>(Sprite* x);

};


///Draws a globe with a nice 3D effect\n
///From http://www.richelbilderbeek.nl/CppDrawGlobe.htm
QPixmap DrawGlobe(
  const int width,
  const int height,
  const unsigned char r,
  const unsigned char g,
  const unsigned char b);

///Obtain the angle for delta-x and delta-y,\n
///where these deltas can be seen as the relative
///coordinats of the end of the dial of a clock\n
///dx - dy - angle\n
///0  - -1 - 0.0 * M_PI\n
///+1 -  0 - 0.5 * M_PI\n
///0  - +1 - 1.0 * M_PI\n
///-1 -  0 - 1.5 * M_PI\n
///From www.richelbilderbeek.nl/CppGetAngle.htm
double GetAngle(const double dX, const double dY);

///DoPerfectElasticCollision calculates the impulses after a
///collision.
///From http://www.richelbilderbeek.nl/CppDoPerfectElasticCollision.htm
void DoPerfectElasticCollision(
  const double angleCollision,
  double& angle1,
  double& speed1,
  double& angle2,
  double& speed2);

#endif // SPRITE_H

 

 

 

 

 

spriteball.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <cmath>
#include <iostream>

#include "spriteball.h"

///The y-coordinat of the top goal post
double SpriteBall::m_goal_y_top = 100.0;

///The y-coordinat of the bottom goal post
double SpriteBall::m_goal_y_bottom = 300.0;

///The current score for the left team
int SpriteBall::m_score_left = 0;

///The current score for the right team
int SpriteBall::m_score_right = 0;

//The number of balls, for debugging purposes
int SpriteBall::sm_n_balls = 0;

SpriteBall::SpriteBall(
  const double x,
  const double y,
  const int size,
  const unsigned char r,
  const unsigned char g,
  const unsigned char b)
: SpriteMoving(x,y,size,r,g,b)
{
  ++sm_n_balls;
  std::clog << "A ball is created at ("
    << this->getX() << ","
    << this->getY() << ")\n";
}

SpriteBall::~SpriteBall()
{
  --sm_n_balls;
  assert(sm_n_balls >= 0);
}


void SpriteBall::SetGoalPoles(
  const double goal_y_top,
  const double goal_y_bottom)
{
  m_goal_y_top    = goal_y_top;
  m_goal_y_bottom = goal_y_bottom;
  assert(m_goal_y_top < m_goal_y_bottom
    && "Assume the top pole is on top");
}

void SpriteBall::Move()
{
  const double maxx = this->m_maxx - this->m_size;
  const double maxy = this->m_maxy - this->m_size;
  m_x += m_dx;
  m_y += m_dy;

  ///Move moving sprite into the arena at all costs
  if (m_x <  0.0)
  {
    if (m_y  > m_goal_y_top && m_y < m_goal_y_bottom)
    {
      //GOAL!
      ++m_score_right;
      std::clog << "Ball at ("<< m_x << "," << m_y << ")"
        << " hit the left goal, defined in height range <"
        << m_goal_y_top << "," << m_goal_y_bottom << ">\n";
    }
    m_x = 0.0;
    m_dx = std::fabs(m_dx);
    std::clog << "Ball moved to the right to (" << m_x << "," << m_y << ")\n";
  }
  else
  {
    if (m_x > maxx)
    {
      if (m_y  > m_goal_y_top && m_y < m_goal_y_bottom)
      {
        ///GOAL!
        ++m_score_left;
        std::clog << "Ball at ("<< m_x << "," << m_y << ")"
          << " hit the right goal, defined in height range <"
          << m_goal_y_top << "," << m_goal_y_bottom << ">\n";
      }
      m_x = maxx;
      m_dx = -std::fabs(m_dx);
      std::clog << "Ball moved to the left to (" << m_x << "," << m_y << ")\n";
    }
  }
  if (m_y <  0.0)
  {
    m_y  =  0.0;
    m_dy = std::fabs(m_dy);
    std::clog << "Ball moved downwards to (" << m_x << "," << m_y << ")\n";

  }
  else
  {
    if (m_y > maxy)
    {
      m_y  = maxy;
      m_dy = -std::fabs(m_dy);
      std::clog << "Ball moved upwards to (" << m_x << "," << m_y << ")\n";
    }
  }
  m_dx *= m_friction;
  m_dy *= m_friction;
}

std::pair<int,int> SpriteBall::GetScore()
{
  return std::make_pair(m_score_left,m_score_right);
}

 

 

 

 

 

spriteball.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef SPRITEBALL_H
#define SPRITEBALL_H

#include <boost/checked_delete.hpp>

#include "spritemoving.h"

///SpriteBall is green per default
///and keeps track of the score
///and can only be deleted by boost::checked_delete
struct SpriteBall : public SpriteMoving
{
  SpriteBall(
    const double x,
    const double y,
    const int size = 32,
    const unsigned char r =   0,
    const unsigned char g = 255,
    const unsigned char b =   0);
  void Move();
  static void SetGoalPoles(const double goal_y_top,const double goal_y_bottom);
  static std::pair<int,int> GetScore();
  static void ResetScore() { m_score_left = 0; m_score_right = 0; }
  static int CountBalls() { return sm_n_balls; }

  private:
  ///Ensure SpriteBall can only be deleted by boost::checked_delete
  ~SpriteBall();
  friend void boost::checked_delete<>(SpriteBall* x);

  static double m_goal_y_top;
  static double m_goal_y_bottom;
  static int m_score_left;
  static int m_score_right;

  ///SpriteBall is no base class
  void dummy_make_me_abstract() const {}

  //The number of balls, for debugging purposes
  static int sm_n_balls;


};



#endif // SPRITEBALL_H

 

 

 

 

 

spritemoving.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <cassert>
#include <cmath>
#include <iostream>

#include <boost/numeric/conversion/cast.hpp>

#include "spritemoving.h"

int SpriteMoving::sm_n_moving_sprites = 0;

///The fraction of impulse that is conserved \n
///1.0: no friction
///0.9: some friction
///0.1: much friction
double SpriteMoving::m_friction = 0.995;

SpriteMoving::SpriteMoving(
  const double x,
  const double y,
  const int size,
  const unsigned char r,
  const unsigned char g,
  const unsigned char b)
  : Sprite(x,y,size,r,g,b)
{
  ++sm_n_moving_sprites;
}

double SpriteMoving::CalcImpulseAngle() const
{
  return GetAngle(m_dx,m_dy);
}

double SpriteMoving::CalcImpulseSpeed() const
{
  return std::sqrt( (m_dx * m_dx) + (m_dy * m_dy) );
}

void SpriteMoving::SetFriction(const double friction)
{
  assert(friction  > 0.0);
  assert(friction <= 1.0);
  m_friction = friction;
}

void SpriteMoving::Collision(SpriteMoving * const p1, SpriteMoving * const p2)
{
  assert(p1!=p2);
  assert(p1 > p2);
  const double dx = p2->getX() - p1->getX();
  const double dy = p2->getY() - p1->getY();
  const double distance = std::sqrt((dy * dy) + (dx * dx));
  const double collision_distance
    = boost::numeric_cast<double>(p1->m_size + p2->m_size) / 2.0;
  if (distance < collision_distance)
  {
    //A collision!
    //Obtain the relative angle between the players
    const double a = GetAngle(dx,dy);
    //Obtain the players' current impulses
    double p1_a = p1->CalcImpulseAngle();
    double p1_s = p1->CalcImpulseSpeed();
    double p2_a = p2->CalcImpulseAngle();
    double p2_s = p2->CalcImpulseSpeed();
    //Obtain the new impulses
    DoPerfectElasticCollision(a, p1_a,p1_s,p2_a,p2_s);
    //Set the players' new impulses
    const double dx1 =  std::sin(p1_a) * p1_s;
    const double dy1 = -std::cos(p1_a) * p1_s;
    const double dx2 =  std::sin(p2_a) * p2_s;
    const double dy2 = -std::cos(p2_a) * p2_s;
    p1->SetSpeed(dx1,dy1);
    p2->SetSpeed(dx2,dy2);
    std::clog << "New impulses: (" << dx1 << "," << dy1 << ")"
      << " (" << dx2 << "," << dy2 << ")\n";
    //Let them move away from each perpendicalar to the collision axis
    {
      const double go_away_distance = collision_distance - distance;
      assert(go_away_distance > 0);
      const double go_away_dx1 =  std::sin(a + M_PI) * (go_away_distance / 2.0);
      const double go_away_dy1 = -std::cos(a + M_PI) * (go_away_distance / 2.0);
      const double go_away_dx2 =  std::sin(a +  0.0) * (go_away_distance / 2.0);
      const double go_away_dy2 = -std::cos(a +  0.0) * (go_away_distance / 2.0);
      p1->Move(go_away_dx1,go_away_dy1);
      p2->Move(go_away_dx2,go_away_dy2);
    }
    //Let the players move again
    p1->Move();
    p2->Move();
    #ifndef NDEBUG
    {
      const double new_dx = p2->getX() - p1->getX();
      const double new_dy = p2->getY() - p1->getY();
      const double new_distance = std::sqrt((new_dy * new_dy) + (new_dx * new_dx));
      if (new_distance < distance)
        std::clog << "Players should in general move away after a collision\n";
      //assert(new_distance > distance && "Players should move away after a collision");
    }
    #endif
  }
}

void SpriteMoving::Draw(QPainter& painter) const
{
  //Draw the globe
  Sprite::Draw(painter);
  #ifdef DRAW_MOVING_SPRITE_IMPULSES
  //Determine the player center
  const double half_size = boost::numeric_cast<double>(m_size) / 2.0;
  const double x_mid = m_x + half_size;
  const double y_mid = m_y + half_size;
  //Draw the moving sprite's impulse
  const double x3 = x_mid + (std::sin(CalcImpulseAngle()) * CalcImpulseSpeed() * 10.0);
  const double y3 = y_mid - (std::cos(CalcImpulseAngle()) * CalcImpulseSpeed() * 10.0);
  painter.drawLine(x_mid,y_mid,x3,y3);
  #endif
}


 

 

 

 

 

spritemoving.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef SPRITEMOVING_H
#define SPRITEMOVING_H

#include "sprite.h"

///SpriteMoving is an abstract base class,
///thanks to dummy_make_me_abstract
struct SpriteMoving : public Sprite
{
  SpriteMoving(
    const double x,
    const double y,
    const int size,
    const unsigned char r,
    const unsigned char g,
    const unsigned char b);

  double CalcImpulseAngle() const;
  double CalcImpulseSpeed() const;
  virtual void Draw(QPainter& painter) const;
  virtual void Move() = 0;
  void Move(const double dx, const double dy) { m_dx += dx; m_dy += dy; }
  void SetSpeed(const double dx, const double dy) { m_dx = dx; m_dy = dy; }

  static void SetFriction(const double friction);
  static void Collision(SpriteMoving * const p1, SpriteMoving * const p2);

  ///Debugging purposes
  static int CountMovingSprites() { return sm_n_moving_sprites; }

  protected:
  double m_dx;
  double m_dy;
  static double m_friction;
  static int sm_n_moving_sprites;

  //private:
  //Ensure SpriteMoving can only be deleted by boost::checked_delete
  virtual ~SpriteMoving()
  {
    --sm_n_moving_sprites;
  }
  //friend void boost::checked_delete<>(SpriteMoving* x);

};

#endif // SPRITEMOVING_H

 

 

 

 

 

spritenonmoving.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <cmath>
#include <iostream>

#include <boost/numeric/conversion/cast.hpp>

#include "spritemoving.h"
#include "spritenonmoving.h"

void SpriteNonMoving::Collision(SpriteNonMoving * const p1, SpriteMoving * const p2)
{
  const double dx = p2->getX() - p1->getX();
  const double dy = p2->getY() - p1->getY();
  const double distance = std::sqrt((dy * dy) + (dx * dx));
  const double collision_distance
    = boost::numeric_cast<double>(p1->m_size + p2->m_size) / 2.0;
  if (distance < collision_distance)
  {
    //A collision!
    //Obtain the relative angle between the players
    const double a = GetAngle(dx,dy);
    //Obtain the moving sprite's current impulse
    double p2_a = p2->CalcImpulseAngle();
    double p2_s = p2->CalcImpulseSpeed();
    //Obstacles have opposite impulse
    double p1_a = p2_a + M_PI;
    double p1_s = p1_s;
    //Obtain the new impulses
    DoPerfectElasticCollision(a, p1_a,p1_s,p2_a,p2_s);
    //Set the player's new impulse
    const double dx2 =  std::sin(p2_a) * p2_s;
    const double dy2 = -std::cos(p2_a) * p2_s;
    p2->SetSpeed(dx2,dy2);
    //Let the player move away from each perpendicalar to the collision axis
    {
      const double go_away_distance = collision_distance - distance;
      assert(go_away_distance > 0);
      const double go_away_dx2 =  std::sin(a +  0.0) * (go_away_distance / 2.0);
      const double go_away_dy2 = -std::cos(a +  0.0) * (go_away_distance / 2.0);
      p2->Move(go_away_dx2,go_away_dy2);
    }
    //Let the player move again
    p2->Move();
    #ifndef NDEBUG
    {
      const double new_dx = p2->getX() - p1->getX();
      const double new_dy = p2->getY() - p1->getY();
      const double new_distance = std::sqrt((new_dy * new_dy) + (new_dx * new_dx));
      if (new_distance < distance)
        std::clog << "Players should in general move away after a collision\n";
      //assert(new_distance > distance && "Players should move away after a collision");
    }
    #endif
  }
}



 

 

 

 

 

spritenonmoving.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef SPRITENONMOVING_H
#define SPRITENONMOVING_H

#include "sprite.h"
#include <boost/checked_delete.hpp>

struct SpriteMoving;

///SpriteNonMoving are obstacles.
///Obstacles are grey per default
///and can only be deleted by boost::checked_delete
struct SpriteNonMoving : public Sprite
{
  SpriteNonMoving(
    const double x,
    const double y,
    const int size = 32,
    const unsigned char r = 255,
    const unsigned char g = 255,
    const unsigned char b = 255)
    : Sprite(x,y,size,r,g,b)
  {

  }

  public:
  void setX(const double x) { m_x = x; }
  void setY(const double y) { m_y = y; }
  static void Collision(SpriteNonMoving * const p1, SpriteMoving * const p2);

  ///SpriteNonMoving is no base class
  void dummy_make_me_abstract() const {}

  private:
  ///Ensure SpriteNonMoving can only be deleted by boost::checked_delete
  virtual ~SpriteNonMoving() {}
  friend void boost::checked_delete<>(SpriteNonMoving* x);
};

#endif // SPRITENONMOVING_H

 

 

 

 

 

spriteplayer.cpp

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <cmath>
#include <iostream>

#include <boost/numeric/conversion/cast.hpp>

#include <QPainter>

#include "spriteplayer.h"

///The acceleration (i.e. change of impulse)
///if a player presses accelerate
const double SpritePlayer::m_acceleration = 1.0;

///The speed of turning around
///if a player presses turn
const double SpritePlayer::m_turnspeed = M_PI / 10.0;

///The number of SpritePlayers.
///for debugging purposes
int SpritePlayer::ms_n_players = 0;

SpritePlayer::SpritePlayer(
  const double x,
  const double y,
  const double angle,
  const int size,
  const unsigned char r,
  const unsigned char g,
  const unsigned char b)
  : SpriteMoving(x,y,size,r,g,b),
    m_id(ms_n_players),
    m_angle(angle)

{
  ++ms_n_players;
  std::clog << "The " << ms_n_players
    << "th player is created at ("
    << this->getX() << ","
    << this->getY() << ")\n";
}

SpritePlayer::~SpritePlayer()
{
  std::clog << "The " << ms_n_players
    << "th player is destroyed\n";
  --ms_n_players;
  assert(ms_n_players >= 0);
}

void SpritePlayer::Move()
{
  ///Move the sprite
  m_x += m_dx;
  m_y += m_dy;

  ///Calculate the sprites maximal coordinats
  const double maxx = this->m_maxx - this->m_size;
  const double maxy = this->m_maxy - this->m_size;
  assert(maxx > 0);
  assert(maxy > 0);

  ///Move moving sprite into the arena
  if (m_x <  0.0)
  {
    m_x =  0.0; m_dx =  std::fabs(m_dx);
    std::clog << "Player #" << m_id << " moved to right to ("
      << m_x << "," << m_y << ")\n";
  }
  if (m_y <  0.0)
  {
    m_y =  0.0; m_dy =  std::fabs(m_dy);
    std::clog << "Player #" << m_id << " moved downwards to ("
      << m_x << "," << m_y << ")\n";
  }
  if (m_x > maxx)
  {
    m_x = maxx; m_dx = -std::fabs(m_dx);
    std::clog << "Player #" << m_id << " moved to left to ("
      << m_x << "," << m_y << ")\n";
  }
  if (m_y > maxy)
  {
    m_y = maxy; m_dy = -std::fabs(m_dy);
    std::clog << "Player #" << m_id << " moved upwards to ("
      << m_x << "," << m_y << ")\n";
  }

  ///Decrease the speed due to friction
  m_dx *= m_friction;
  m_dy *= m_friction;
}


void SpritePlayer::Accelerate()
{
  m_dx += (std::sin(m_angle) * m_acceleration);
  m_dy -= (std::cos(m_angle) * m_acceleration);
}

void SpritePlayer::TurnRight()
{
  m_angle+=m_turnspeed;
}

void SpritePlayer::Draw(QPainter& painter) const
{
  //Draw the globe and possibly impulse
  SpriteMoving::Draw(painter);
  //Determine the player center
  const double half_size = boost::numeric_cast<double>(m_size) / 2.0;
  const double x_mid = m_x + half_size;
  const double y_mid = m_y + half_size;
  //Draw the line the player looks at
  const double x2 = x_mid + (std::sin(m_angle) * half_size);
  const double y2 = y_mid - (std::cos(m_angle) * half_size);
  painter.drawLine(x_mid,y_mid,x2,y2);
}

 

 

 

 

 

spriteplayer.h

 

/*
  Boenken. A multiplayer soccer/billiards game.
  Copyright (C) 2007 Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef SPRITEPLAYER_H
#define SPRITEPLAYER_H

#include <boost/checked_delete.hpp>
#include "spritemoving.h"

///SpritePlayer is a MovingSprite that can respond to input
///and can only be deleted by boost::checked_delete
struct SpritePlayer : public SpriteMoving
{
  SpritePlayer(
    const double x,
    const double y,
    const double angle,
    const int size,
    const unsigned char r,
    const unsigned char g,
    const unsigned char b);

  void Accelerate();
  void Draw(QPainter& painter) const;
  void Move();
  void TurnRight();

  ///The SpritePlayer's ID
  const int m_id;

  private:
  ///Ensure SpritePlayer can only be deleted by boost::checked_delete
  ~SpritePlayer();
  friend void boost::checked_delete<>(SpritePlayer* x);

  double m_angle;

  ///SpritePlayer is no base class
  void dummy_make_me_abstract() const {}
  static const double m_acceleration;
  static const double m_turnspeed;
  static int ms_n_players;
};


#endif // SPRITEPLAYER_H

 

 

 

 

 

 

 

 

 

 

Go back to Richel Bilderbeek's C++ page.

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict