@ -0,0 +1,64 @@
|
||||
################################################################################
|
||||
# Improvements and feedback are welcome! #
|
||||
# This software is licensed under the terms of the GNU GPL v3 license. #
|
||||
################################################################################
|
||||
|
||||
include_directories(
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/libtdegames
|
||||
${TDE_INCLUDE_DIR}
|
||||
${TQT_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
link_directories(
|
||||
${TQT_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
### kue (library) ############################################################
|
||||
tde_add_library(
|
||||
kue SHARED AUTOMOC
|
||||
|
||||
SOURCES
|
||||
billiard.cpp disc.cpp interface.cpp physics.cpp table.cpp global.cpp
|
||||
vector.cpp cue.cpp graphics.cpp point.cpp sphere.cpp texture.cpp
|
||||
utility.cpp newgame.cpp pluginloader.cpp rules.cpp localplayer.cpp
|
||||
|
||||
LINK
|
||||
tdegames-shared
|
||||
GL
|
||||
GLU
|
||||
|
||||
DESTINATION ${LIB_INSTALL_DIR}
|
||||
)
|
||||
|
||||
### kue (executable) #########################################################
|
||||
tde_add_executable(
|
||||
kue AUTOMOC
|
||||
|
||||
SOURCES
|
||||
main.cpp
|
||||
|
||||
LINK
|
||||
kue-shared
|
||||
|
||||
DESTINATION ${BIN_INSTALL_DIR}
|
||||
)
|
||||
|
||||
### modules ####################################################################
|
||||
add_subdirectory(modules)
|
||||
|
||||
### data #######################################################################
|
||||
tde_install_icons()
|
||||
|
||||
install(
|
||||
DIRECTORY textures/
|
||||
DESTINATION ${DATA_INSTALL_DIR}/kue/textures
|
||||
FILES_MATCHING PATTERN *.png
|
||||
)
|
||||
|
||||
### translated desktop files ###################################################
|
||||
tde_create_translated_desktop(kue.desktop)
|
||||
|
||||
# kate: replace-tabs true; tab-width 2;
|
@ -0,0 +1,22 @@
|
||||
################################################################################
|
||||
# kue - Simple billiards game #
|
||||
# Copyright (C) 2023 Mavridis Philippe <mavridisf@gmail.com> #
|
||||
# #
|
||||
# Improvements and feedback are welcome! #
|
||||
# This software is licensed under the terms of the GNU GPL v3 license. #
|
||||
################################################################################
|
||||
|
||||
find_package( TQt )
|
||||
find_package( TDE )
|
||||
|
||||
tde_setup_architecture_flags( )
|
||||
|
||||
include( TestBigEndian )
|
||||
test_big_endian( WORDS_BIGENDIAN )
|
||||
|
||||
tde_setup_largefiles( )
|
||||
|
||||
|
||||
if( WITH_GCC_VISIBILITY )
|
||||
tde_setup_gcc_visibility( )
|
||||
endif( WITH_GCC_VISIBILITY )
|
@ -0,0 +1,87 @@
|
||||
#include "billiard.h"
|
||||
#include "graphics.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <tqstring.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
const double FRICTIONAL_FORCE = 0.025;
|
||||
|
||||
KueBilliard::KueBilliard(double x, double y, double r, const KueTexture &texture) : circle(x, y, r), _texture(texture)
|
||||
{
|
||||
}
|
||||
|
||||
KueBilliard::~KueBilliard()
|
||||
{
|
||||
}
|
||||
|
||||
void KueBilliard::step(double seconds)
|
||||
{
|
||||
double delta_x;
|
||||
double delta_y;
|
||||
|
||||
if (isStopped())
|
||||
return;
|
||||
|
||||
vector new_velocity = _velocity - (FRICTIONAL_FORCE * seconds);
|
||||
|
||||
if (new_velocity.magnitude() < 0.0)
|
||||
new_velocity.setMagnitude(0.0);
|
||||
|
||||
delta_x = (_velocity.componentX() + new_velocity.componentX()) / 2.0 * seconds;
|
||||
delta_y = (_velocity.componentY() + new_velocity.componentY()) / 2.0 * seconds;
|
||||
|
||||
_pos_x += delta_x;
|
||||
_pos_y += delta_y;
|
||||
|
||||
_velocity = new_velocity;
|
||||
}
|
||||
|
||||
bool KueBilliard::isStopped()
|
||||
{
|
||||
return (_velocity.magnitude() == 0.0);
|
||||
}
|
||||
|
||||
void KueBilliard::reflect(double normal)
|
||||
{
|
||||
_velocity.setDirection(M_PI - (_velocity.direction()) + (normal * 2.0));
|
||||
}
|
||||
|
||||
void KueBilliard::collide(KueBilliard &b) {
|
||||
_velocity -= b._velocity;
|
||||
|
||||
double mv = _velocity.magnitude();
|
||||
|
||||
vector unit1 = vector(*this, b);
|
||||
unit1 = unit1.unit();
|
||||
|
||||
vector unit2 = _velocity.unit();
|
||||
|
||||
double cos = unit1 * unit2;
|
||||
|
||||
unit1 *= mv * cos;
|
||||
_velocity -= unit1;
|
||||
_velocity += b._velocity;
|
||||
|
||||
b._velocity += unit1;
|
||||
}
|
||||
|
||||
vector& KueBilliard::velocity()
|
||||
{
|
||||
return _velocity;
|
||||
}
|
||||
|
||||
void KueBilliard::setVelocity(const vector &velocity)
|
||||
{
|
||||
_velocity = velocity;
|
||||
}
|
||||
|
||||
KueTexture& KueBilliard::texture()
|
||||
{
|
||||
return _texture;
|
||||
}
|
||||
|
||||
void KueBilliard::setTexture(const KueTexture &texture)
|
||||
{
|
||||
_texture = texture;
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
#ifndef _BILLIARD_H
|
||||
#define _BILLIARD_H
|
||||
|
||||
#include <tqstring.h>
|
||||
#include "texture.h"
|
||||
#include "vector.h"
|
||||
#include "circle.h"
|
||||
|
||||
class KueBilliard : public circle
|
||||
{
|
||||
public:
|
||||
KueBilliard(double x, double y, double r, const KueTexture &texure = KueTexture::null());
|
||||
~KueBilliard();
|
||||
|
||||
void step(double seconds);
|
||||
|
||||
bool isStopped();
|
||||
void reflect(double normal);
|
||||
void collide(KueBilliard &other_billiard);
|
||||
|
||||
vector& velocity();
|
||||
void setVelocity(const vector &velocity);
|
||||
|
||||
KueTexture& texture();
|
||||
void setTexture(const KueTexture &);
|
||||
|
||||
protected:
|
||||
KueTexture _texture;
|
||||
|
||||
vector _velocity;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,27 @@
|
||||
#ifndef _CIRCLE_H
|
||||
#define _CIRCLE_H
|
||||
|
||||
#include "point.h"
|
||||
|
||||
// Billards and pockets are both circles
|
||||
// The collison detection code likes to deal with circles, not billards and pockets
|
||||
|
||||
class circle : public point {
|
||||
public:
|
||||
circle(double x, double y, double r) : point(x, y) { _radius = r; }
|
||||
~circle() {}
|
||||
|
||||
// Accessors
|
||||
double radius() { return _radius; }
|
||||
void setRadius(double radius) { _radius = radius; }
|
||||
|
||||
// Returns true if we intersect the other circle
|
||||
double intersects(const circle &other_circle) { return (distance(other_circle) - other_circle._radius) <= _radius; }
|
||||
// Returns the distance from the edge of this circle to the edge of the other
|
||||
double edgeDistance(const circle &other_circle) { return distance(other_circle) - _radius - other_circle._radius; }
|
||||
|
||||
protected:
|
||||
double _radius;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
#define VERSION "@VERSION@"
|
||||
|
||||
// Defined if you have fvisibility and fvisibility-inlines-hidden support.
|
||||
#cmakedefine __KDE_HAVE_GCC_VISIBILITY 1
|
||||
|
||||
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
|
||||
significant byte first (like Motorola and SPARC, unlike Intel). */
|
||||
#cmakedefine WORDS_BIGENDIAN @WORDS_BIGENDIAN@
|
@ -0,0 +1,74 @@
|
||||
#include <math.h>
|
||||
#include <tqcolor.h>
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
|
||||
#include "cue.h"
|
||||
#include "texture.h"
|
||||
|
||||
const int CUE_DISPLAY_LIST = 4;
|
||||
const int TIP_DISPLAY_LIST = 5;
|
||||
|
||||
const double RADIUS = 0.00286;
|
||||
|
||||
void cue::draw(double x, double y, double angle, KueTexture &texture, const TQColor &tip_color)
|
||||
{
|
||||
glPushMatrix();
|
||||
|
||||
// Go to the specified location
|
||||
glTranslated(x, y, RADIUS);
|
||||
|
||||
// Rotate to the specificed angle
|
||||
glRotated(angle, 0.0, 0.0, 1.0);
|
||||
// And tip the cue upwards
|
||||
glRotated(80, 0.0, 1.0, 0.0);
|
||||
|
||||
// Have we built the cue display list?
|
||||
if (!glIsList(CUE_DISPLAY_LIST) == GL_TRUE)
|
||||
{
|
||||
// Make a new quadtic object
|
||||
GLUquadricObj *qobj = gluNewQuadric();
|
||||
|
||||
// We need normals and texturing for lighting and textures
|
||||
gluQuadricNormals(qobj, GLU_SMOOTH);
|
||||
gluQuadricTexture(qobj, GL_TRUE);
|
||||
|
||||
// Make a new display list
|
||||
glNewList(TIP_DISPLAY_LIST, GL_COMPILE);
|
||||
|
||||
// Draw the tip
|
||||
gluCylinder(qobj, RADIUS / 2.5, RADIUS / 2.5, 0.003, 10, 10);
|
||||
|
||||
// End the tip list
|
||||
glEndList();
|
||||
|
||||
// Make a new display list
|
||||
glNewList(CUE_DISPLAY_LIST, GL_COMPILE);
|
||||
|
||||
// Draw the main part of the cue
|
||||
glTranslated(0.0, 0.0, 0.003);
|
||||
gluCylinder(qobj, RADIUS / 2.5, RADIUS / 1.5, 0.047, 10, 10);
|
||||
|
||||
// End the cue list
|
||||
glEndList();
|
||||
|
||||
// Draw the quadric
|
||||
gluDeleteQuadric(qobj);
|
||||
}
|
||||
|
||||
|
||||
KueTexture::null().makeCurrent();
|
||||
glColor3d(
|
||||
tip_color.red() / 255.0,
|
||||
tip_color.green() / 255.0,
|
||||
tip_color.blue() / 255.0
|
||||
);
|
||||
glCallList(TIP_DISPLAY_LIST);
|
||||
|
||||
texture.makeCurrent();
|
||||
glColor3d(1.0, 1.0, 1.0);
|
||||
glCallList(CUE_DISPLAY_LIST);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
#ifndef _CUE_H
|
||||
#define _CUE_H
|
||||
|
||||
class TQColor;
|
||||
class KueTexture;
|
||||
|
||||
// Draws a pool cue
|
||||
class cue {
|
||||
public:
|
||||
static void draw(double x, double y, double angle, KueTexture &texture, const TQColor &tip_color);
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,46 @@
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "disc.h"
|
||||
|
||||
// The pre-allocated number of our display list
|
||||
const int DISC_DISPLAY_LIST = 3;
|
||||
// The disc radius currently cached in our display list
|
||||
double disc_list_radius = 0.0;
|
||||
|
||||
void disc::draw(double x, double y, double r)
|
||||
{
|
||||
glPushMatrix();
|
||||
|
||||
// Move to the specified location
|
||||
glTranslatef(x, y, 0.0001);
|
||||
|
||||
// Have we cached this radius
|
||||
if ((r == disc_list_radius) && (glIsList(DISC_DISPLAY_LIST) == GL_TRUE))
|
||||
{
|
||||
// Yes, just call the display list
|
||||
glCallList(DISC_DISPLAY_LIST);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nope, make a new quadric object
|
||||
GLUquadricObj *quad = gluNewQuadric();
|
||||
|
||||
// Make a new display list
|
||||
glNewList(DISC_DISPLAY_LIST, GL_COMPILE_AND_EXECUTE);
|
||||
// Draw the disc
|
||||
gluDisk(quad, 0.0, r, 10, 1);
|
||||
// End the display list
|
||||
glEndList();
|
||||
|
||||
// Update the cached value
|
||||
disc_list_radius = r;
|
||||
|
||||
// Delete the quadric object
|
||||
gluDeleteQuadric(quad);
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
#ifndef _DISC_H
|
||||
#define _DISC_H
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159
|
||||
#endif
|
||||
|
||||
class disc {
|
||||
public:
|
||||
static void draw(double x, double y, double radius);
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,39 @@
|
||||
#include "rules.h"
|
||||
#include "physics.h"
|
||||
#include "global.h"
|
||||
#include "main.h"
|
||||
|
||||
KueRulesEngine* KueGlobal::_rules = nullptr;
|
||||
KuePhysics* KueGlobal::_physics = nullptr;
|
||||
GLUserInterface* KueGlobal::_glWidget = nullptr;
|
||||
MainWindow* KueGlobal::_mainWindow = nullptr;
|
||||
TQPtrVector<KueTeam>* KueGlobal::_teams = nullptr;
|
||||
|
||||
KueRulesEngine* KueGlobal::rules()
|
||||
{
|
||||
return _rules;
|
||||
}
|
||||
|
||||
KuePhysics* KueGlobal::physics()
|
||||
{
|
||||
return _physics;
|
||||
}
|
||||
|
||||
GLUserInterface *KueGlobal::glWidget()
|
||||
{
|
||||
return _glWidget;
|
||||
}
|
||||
|
||||
MainWindow *KueGlobal::mainWindow()
|
||||
{
|
||||
return _mainWindow;
|
||||
}
|
||||
|
||||
TQPtrVector<KueTeam> *KueGlobal::teams()
|
||||
{
|
||||
if (!_teams)
|
||||
{
|
||||
_teams = new TQPtrVector<KueTeam>;
|
||||
}
|
||||
return _teams;
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
#ifndef __GLOBAL_H_
|
||||
#define __GLOBAL_H_
|
||||
|
||||
#include <tqgl.h>
|
||||
#include <tqptrvector.h>
|
||||
#include "team.h"
|
||||
|
||||
class MainWindow;
|
||||
class GLUserInterface;
|
||||
class KueRulesEngine;
|
||||
class KuePhysics;
|
||||
|
||||
class KueGlobal {
|
||||
public:
|
||||
// Get our version string
|
||||
static const char *version() { return "1.0"; }
|
||||
|
||||
// Exit menu, start the game
|
||||
static void stopMenu();
|
||||
|
||||
// Member instances
|
||||
static KueRulesEngine *rules();
|
||||
static KuePhysics *physics();
|
||||
|
||||
static GLUserInterface *glWidget();
|
||||
static MainWindow *mainWindow();
|
||||
static TQPtrVector<KueTeam> *teams();
|
||||
|
||||
private:
|
||||
static KueRulesEngine *_rules;
|
||||
static KuePhysics *_physics;
|
||||
|
||||
static GLUserInterface *_glWidget;
|
||||
static MainWindow *_mainWindow;
|
||||
|
||||
static TQPtrVector<KueTeam> *_teams;
|
||||
|
||||
friend class MainWindow;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,176 @@
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
#include <tdeglobal.h>
|
||||
#include <tdeconfig.h>
|
||||
#include <tqptrvector.h>
|
||||
|
||||
#include "physics.h"
|
||||
#include "global.h"
|
||||
#include "pocket.h"
|
||||
#include "billiard.h"
|
||||
#include "config.h"
|
||||
#include "graphics.h"
|
||||
|
||||
// Constants
|
||||
|
||||
const double FIELD_WIDTH = 0.127;
|
||||
const double FIELD_LENGTH = 0.254;
|
||||
|
||||
GLfloat LightAmbient[] = { 0.2, 0.2, 0.2, 1.0 };
|
||||
GLfloat LightDiffuse[] = { 0.8, 0.8, 0.8, 1.0 };
|
||||
GLfloat LightPosition[] = { FIELD_LENGTH / 2.0, FIELD_WIDTH / 2.0, 0.0, 1.0 };
|
||||
|
||||
GLdouble eyex = FIELD_LENGTH / 2.0;
|
||||
GLdouble eyey = FIELD_WIDTH / 2.0;
|
||||
GLdouble eyez = 0.1;
|
||||
|
||||
GLdouble centerx = FIELD_LENGTH / 2.0;
|
||||
GLdouble centery = FIELD_WIDTH / 2.0;
|
||||
GLdouble centerz = -FIELD_LENGTH / 2.0;
|
||||
|
||||
void graphics::resize(int width, int height)
|
||||
{
|
||||
// We want to use the whole window
|
||||
glViewport(0, 0, width, height);
|
||||
|
||||
// 3D-specific OpenGL setup
|
||||
|
||||
// Modify the projection matrix
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
|
||||
// Set up a perspective view based on our window's aspect ratio
|
||||
gluPerspective(45.0f, (GLdouble)width / (GLdouble)height, 0.1f, 100.0f);
|
||||
|
||||
// Go back to modifying the modelview matrix (the default)
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
bool graphics::init()
|
||||
{
|
||||
// Clear the window to black when we start to draw
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
// Clear the depth buffer to 1.0
|
||||
glClearDepth(1.0);
|
||||
glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading
|
||||
|
||||
// Enable texturing
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
// Initialize our light
|
||||
glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
|
||||
|
||||
// Enable our light
|
||||
glEnable(GL_LIGHT0);
|
||||
|
||||
// Don't draw polygons that aren't facing us (big speedup on cheap 486s ;)
|
||||
// glEnable(GL_CULL_FACE);
|
||||
|
||||
// Have we enabled lighting?
|
||||
TDEGlobal::config()->setGroup("Graphics");
|
||||
if (TDEGlobal::config()->readBoolEntry("Lighting", true)) {
|
||||
// Turn on lighting
|
||||
glEnable(GL_LIGHTING);
|
||||
|
||||
// Whenever we use glColor(), set the lighting system automatically know what
|
||||
// the new color is.
|
||||
glEnable(GL_COLOR_MATERIAL);
|
||||
}
|
||||
|
||||
glDepthFunc(GL_LESS); // The Type Of Depth Test To Do
|
||||
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
|
||||
|
||||
// Makes texturing drawing slight more accurate
|
||||
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void graphics::startDraw()
|
||||
{
|
||||
// Reset our origin, rotation, and scaling
|
||||
glLoadIdentity();
|
||||
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
|
||||
|
||||
glTranslatef(0.0, 0.0, -FIELD_LENGTH / 2.0);
|
||||
|
||||
// Set the camera position and angle
|
||||
gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, 0.0, 0.0, 1.0);
|
||||
|
||||
// Clear our window to the default color set by glClearColor (i.e. black)
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void graphics::endDraw()
|
||||
{
|
||||
// Execute any backlogged commands right now
|
||||
glFlush();;
|
||||
}
|
||||
|
||||
void graphics::lookAt(GLdouble _eyex, GLdouble _eyey, GLdouble _eyez, GLdouble _centerx, GLdouble _centery, GLdouble _centerz)
|
||||
{
|
||||
// Update our internal viewpoint variables
|
||||
// These values are used in startDraw() to update the view
|
||||
|
||||
// The eye coordinates are where we're looking from
|
||||
eyex = _eyex;
|
||||
eyey = _eyey;
|
||||
eyez = _eyez;
|
||||
|
||||
// And the center coordinates are what we're looing at
|
||||
centerx = _centerx;
|
||||
centery = _centery;
|
||||
centerz = _centerz;
|
||||
}
|
||||
|
||||
// Simple OpenGL wrapper
|
||||
void graphics::setColor(double r, double g, double b)
|
||||
{
|
||||
glColor3d(r, g, b);
|
||||
}
|
||||
|
||||
void graphics::drawScene()
|
||||
{
|
||||
const TQPtrVector<KuePocket>& pockets = KueGlobal::physics()->pockets();
|
||||
const TQPtrVector<KueBilliard>& billiards = KueGlobal::physics()->billiards();
|
||||
|
||||
// Display the billiards and pockets
|
||||
|
||||
// The pockets are black without a texture
|
||||
graphics::setColor(0.0, 0.0, 0.0);
|
||||
KueTexture::null().makeCurrent();
|
||||
|
||||
// Draw all of the textures
|
||||
for (unsigned int i = 0;i < pockets.size();i++)
|
||||
{
|
||||
if (pockets[i])
|
||||
disc::draw(pockets[i]->positionX(), pockets[i]->positionY(), pockets[i]->radius());
|
||||
}
|
||||
|
||||
// Now we need to turn the color to white,
|
||||
// so we don't interfere with the texture
|
||||
// color
|
||||
graphics::setColor(1.0, 1.0, 1.0);
|
||||
|
||||
for (unsigned int i = 0;i < billiards.size();i++)
|
||||
{
|
||||
if (billiards[i])
|
||||
{
|
||||
double circ = billiards[i]->radius() * 2.0 * M_PI;
|
||||
double pos_x = billiards[i]->positionX();
|
||||
double pos_y = billiards[i]->positionY();
|
||||
double rot_x = fmod(pos_x, circ) / circ * 360.0;
|
||||
double rot_y = fmod(pos_y, circ) / circ * 360.0;
|
||||
|
||||
billiards[i]->texture().makeCurrent();
|
||||
|
||||
sphere::draw(pos_x, pos_y, billiards[i]->radius(), rot_x, rot_y);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
// Main header for the graphics subsystem
|
||||
|
||||
#ifndef _GRAPHICS_H
|
||||
#define _GRAPHICS_H
|
||||
|
||||
#include "cue.h"
|
||||
#include "disc.h"
|
||||
#include "sphere.h"
|
||||
#include "table.h"
|
||||
#include "texture.h"
|
||||
|
||||
const int GRAPHICS_NOTEXTURE = 0;
|
||||
|
||||
class graphics {
|
||||
public:
|
||||
// Setup OpenGL
|
||||
static bool init();
|
||||
// Shut down OpenGL
|
||||
static bool deinit();
|
||||
|
||||
static void resize(int width, int height);
|
||||
|
||||
// Start and end drawing mode
|
||||
static void startDraw();
|
||||
static void endDraw();
|
||||
|
||||
// Draw the basic scene
|
||||
static void drawScene();
|
||||
|
||||
// Set the pen color
|
||||
static void setColor(double r, double g, double b);
|
||||
|
||||
// Sets the viewing angle
|
||||
static void lookAt(double eyex, double eyey, double eyez, double centerx, double centery, double centerz);
|
||||
};
|
||||
|
||||
#endif
|
After Width: | Height: | Size: 328 B |
After Width: | Height: | Size: 980 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,319 @@
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <GL/gl.h>
|
||||
#include <tdemainwindow.h>
|
||||
#include <tdeapplication.h>
|
||||
#include <kstatusbar.h>
|
||||
#include "physics.h"
|
||||
#include <kdebug.h>
|
||||
#include <tqptrvector.h>
|
||||
|
||||
#include "interface.h"
|
||||
#include "graphics.h"
|
||||
#include "vector.h"
|
||||
#include "global.h"
|
||||
|
||||
// Radius of the billiards
|
||||
const double RADIUS = 0.00286;
|
||||
|
||||
// How long the user can hold down the mouse button to increase shot power, it levels off if this value is exceeded
|
||||
const int MAX_WINDUP_TIME = 2500;
|
||||
// What's the value in meters/second of that maximum shot
|
||||
const double MAX_SHOT_SPEED = 0.45;
|
||||
|
||||
const int UPDATE_TIME = 15;
|
||||
|
||||
GLUserInterface::GLUserInterface(TQWidget *parent) :
|
||||
TQGLWidget(parent),
|
||||
_cue_location(0.0, 0.0, 0.0),
|
||||
_mouse_location(0.5, 0.5),
|
||||
_cue_texture("cue-player1")
|
||||
{
|
||||
// Set all our variables to known safe values
|
||||
_placing_cue = false;
|
||||
_shooting = false;
|
||||
_forward_only = false;
|
||||
|
||||
setMouseTracking(true);
|
||||
_table = new table;
|
||||
}
|
||||
|
||||
|
||||
GLUserInterface::~GLUserInterface()
|
||||
{
|
||||
delete _table;
|
||||
}
|
||||
|
||||
double GLUserInterface::windowToInternalX(int x)
|
||||
{
|
||||
// Make sure the value isn't outside the window (yes, we
|
||||
// used to get invalid values from GLUT sometimes)
|
||||
if (x < 0)
|
||||
return 0.0;
|
||||
|
||||
if (x > width())
|
||||
return 1.0;
|
||||
|
||||
// Now divide the x value by the windows width, so the left edge
|
||||
// has a value of 0.0, the middle has 0.5, and the right edge 1.0
|
||||
return (x / (double)width());
|
||||
}
|
||||
|
||||
double GLUserInterface::windowToInternalY(int y)
|
||||
{
|
||||
if (y < 0)
|
||||
return 0.0;
|
||||
|
||||
if (y > height())
|
||||
return 1.0;
|
||||
|
||||
// Now divide the y value by the window's height, so the left edge
|
||||
// has a value of 0.0, the middle has 0.5, and the right edge 1.0
|
||||
return (y / (double)height());
|
||||
}
|
||||
|
||||
void GLUserInterface::initializeGL()
|
||||
{
|
||||
// Initialize our graphics subsystem
|
||||
if (!graphics::init())
|
||||
kdWarning() << "Unable to initialize graphics" << endl;
|
||||
}
|
||||
|
||||
void GLUserInterface::resizeGL(int width, int height)
|
||||
{
|
||||
graphics::resize(width, height);
|
||||
}
|
||||
|
||||
void GLUserInterface::paintGL()
|
||||
{
|
||||
// Tell the graphics code to enter drawing mode
|
||||
graphics::startDraw();
|
||||
|
||||
// Draw the table
|
||||
_table->draw(KueGlobal::physics()->fieldWidth(), KueGlobal::physics()->fieldHeight());
|
||||
|
||||
|
||||
// Draw the basic physics scene
|
||||
graphics::drawScene();
|
||||
|
||||
// Are we shooting?
|
||||
if (_shooting) {
|
||||
double angle_rad;
|
||||
double angle_deg;
|
||||
|
||||
// Calculate the current view angle
|
||||
angle_rad = positionToAngle(_mouse_location.positionX());
|
||||
// Convert it to degrees for OpenGL's benefit
|
||||
angle_deg = angle_rad / M_PI * 180;
|
||||
|
||||
// Calculate the 'focus' (where the cue is pointing)
|
||||
double focusx = _cue_location.positionX() + (cos(angle_rad) / 150.0 * ((shotPower() * 2.0) + 0.7));
|
||||
double focusy = _cue_location.positionY() + (sin(angle_rad) / 150.0 * ((shotPower() * 2.0) + 0.7));
|
||||
|
||||
// Draw
|
||||
cue::draw(focusx, focusy, angle_deg, _cue_texture, _player_color);
|
||||
}
|
||||
|
||||
// Now we're done with drawing
|
||||
graphics::endDraw();
|
||||
}
|
||||
|
||||
void GLUserInterface::mouseMoveEvent(TQMouseEvent *e)
|
||||
{
|
||||
double x = windowToInternalX(e->x());
|
||||
double y = windowToInternalY(e->y());
|
||||
|
||||
// Invert the mouse along the X-axis
|
||||
x = 1.0 - x;
|
||||
|
||||
// Update the mouse location variable
|
||||
_mouse_location = point(x, y);
|
||||
// Update our 3D view
|
||||
updateView();
|
||||
|
||||
if (_placing_cue)
|
||||
{
|
||||
// If we're placing a cue ball, the mouse location affects its position on the table
|
||||
point cue_ball_point(_cue_line, positionToCuePlacement(x));
|
||||
emit(previewBilliardPlace(cue_ball_point));
|
||||
}
|
||||
}
|
||||
|
||||
void GLUserInterface::mousePressEvent(TQMouseEvent *e)
|
||||
{
|
||||
mouseClicked(windowToInternalX(e->x()), windowToInternalY(e->y()), e->button(), true);
|
||||
}
|
||||
|
||||
void GLUserInterface::mouseReleaseEvent(TQMouseEvent *e)
|
||||
{
|
||||
mouseClicked(windowToInternalX(e->x()), windowToInternalY(e->y()), e->button(), false);
|
||||
}
|
||||
|
||||
void GLUserInterface::mouseClicked(double x, double y, int button, int state) {
|
||||
// Invert the mouse along the X-axis
|
||||
x = 1.0 - x;
|
||||
|
||||
// Regardless of the button press event, we'll take a free mouse position update
|
||||
_mouse_location = point(x, y);
|
||||
updateView();
|
||||
|
||||
// But no non-left buttons past this point;
|
||||
if (button != TQt::LeftButton)
|
||||
return;
|
||||
|
||||
// Are we placing the cue ball?
|
||||
// The "mouse down only" check is so we don't catch a mouse button
|
||||
// coming up that was pressed down before we started placing
|
||||
// the cue ball. It can be very confusing otherwise, and makes
|
||||
// the game seem "glitchy"
|
||||
if (_placing_cue && (state))
|
||||
{
|
||||
// Calculate the cues new position
|
||||
point cue_ball_point(_cue_line, positionToCuePlacement(x));
|
||||
// The the rules engine what we've decided
|
||||
emit(billiardPlaced(cue_ball_point));
|
||||
|
||||
// We're done placing the cue
|
||||
_placing_cue = false;
|
||||
// We calculate the view differently depending on if we're
|
||||
// placing the cue or taking a shot, so update the view
|
||||
// with both the new cue position and with the "taking a shot"
|
||||
// view mode.
|
||||
updateView();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (_shooting)
|
||||
{
|
||||
if (state)
|
||||
{
|
||||
if (!_shot_started)
|
||||
{
|
||||
_shot_started = true;
|
||||
_shot_time.start();
|
||||
startTimer(UPDATE_TIME);
|
||||
}
|
||||
}
|
||||
else if (_shot_started)
|
||||
{
|
||||
// Calculate the angle
|
||||
double angle = positionToAngle(_mouse_location.positionX()) + M_PI;
|
||||
|
||||
// Take the shot
|
||||
vector velocity(shotPower() * MAX_SHOT_SPEED, angle);
|
||||
emit(shotTaken(velocity));
|
||||
|
||||
// We're no longer shooting
|
||||
_shooting = false;
|
||||
_shot_started = false;
|
||||
killTimers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLUserInterface::placeBilliard(double cue_line)
|
||||
{
|
||||
// We've been asked to place the cue ball
|
||||
|
||||
// Enter 'cue placing' mode
|
||||
_placing_cue = true;
|
||||
_cue_line = cue_line;
|
||||
|
||||
// Show it in the position that is associated with our current mouse position
|
||||
point cue_ball_point(_cue_line, positionToCuePlacement(_mouse_location.positionX()));
|
||||
emit(previewBilliardPlace(cue_ball_point));
|
||||
|
||||
// Set up our stupid placing-cue-specific view
|
||||
updateView();
|
||||
}
|
||||
|
||||
void GLUserInterface::startShot(circle cue_location, TQColor player_color, bool forward_only) {
|
||||
// Enter 'shooting' mode
|
||||
_shot_started = false;
|
||||
_shooting = true;
|
||||
|
||||
// Copy over our parameters
|
||||
_forward_only = forward_only;
|
||||
_cue_location = cue_location;
|
||||
_player_color = player_color;
|
||||
|
||||
// Set up our new view
|
||||
updateView();
|
||||
}
|
||||
|
||||
void GLUserInterface::updateView() {
|
||||
if (_placing_cue)
|
||||
{
|
||||
// Our eye is slightly behind the cue line
|
||||
double eyex = _cue_line - (1 / 200.0);
|
||||
// And right in the middle of the table horizontally
|
||||
double eyey = KueGlobal::physics()->fieldHeight() / 2.0;
|
||||
|
||||
// Look at the cue line from our eye position
|
||||
graphics::lookAt(eyex, eyey, (_cue_location.radius() * 4.0 * _mouse_location.positionY()) + _cue_location.radius(), _cue_line, KueGlobal::physics()->fieldHeight() / 2.0, RADIUS);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Figure out our view angle
|
||||
double angle = positionToAngle(_mouse_location.positionX());
|
||||
// Use that to calculate the position of our eye
|
||||
double eyex = _cue_location.positionX() + (cos(angle) / 200.0);
|
||||
double eyey = _cue_location.positionY() + (sin(angle) / 200.0);
|
||||
|
||||
// Look at the cue ball
|
||||
graphics::lookAt(eyex, eyey, (RADIUS * 4.0 * _mouse_location.positionY()) + RADIUS, _cue_location.positionX(), _cue_location.positionY(), RADIUS);
|
||||
}
|
||||
|
||||
// We most certainly need to redraw, unless the physics engine is runnung
|
||||
if (!KueGlobal::physics()->running())
|
||||
KueGlobal::glWidget()->updateGL();
|
||||
}
|
||||
|
||||
double GLUserInterface::shotPower()
|
||||
{
|
||||
if (!_shot_started)
|
||||
return 0.0;
|
||||
|
||||
int difference = _shot_time.elapsed();
|
||||
|
||||
if (difference > MAX_WINDUP_TIME)
|
||||
return 1.0;
|
||||
|
||||
return (double(difference) / double(MAX_WINDUP_TIME));
|
||||
}
|
||||
|
||||
double GLUserInterface::positionToAngle(double position)
|
||||
{
|
||||
// Convert the mouse x-position to a view angle, depending if we're only allow to shoot forward or not
|
||||
if (_forward_only)
|
||||
return (position * M_PI) + (M_PI / 2.0);
|
||||
else
|
||||
return (((position - 0.5) * 1.1) + 0.5) * M_PI * 2.0;
|
||||
}
|
||||
|
||||
double GLUserInterface::positionToCuePlacement(double position)
|
||||
{
|
||||
// Convert the mouse x-position to a cue x-location
|
||||
|
||||
// Direct linear mapping to the table
|
||||
double y_pos = position * KueGlobal::physics()->fieldHeight();
|
||||
|
||||
// Except we must be careful not to go off the table
|
||||
if (y_pos < RADIUS)
|
||||
y_pos = RADIUS;
|
||||
|
||||
if ((y_pos + RADIUS) > KueGlobal::physics()->fieldHeight())
|
||||
y_pos = KueGlobal::physics()->fieldHeight() - RADIUS;
|
||||
|
||||
return y_pos;
|
||||
}
|
||||
|
||||
void GLUserInterface::timerEvent( TQTimerEvent * )
|
||||
{
|
||||
KueGlobal::glWidget()->updateGL();
|
||||
}
|
||||
|
||||
#include "interface.moc"
|
||||
|
@ -0,0 +1,86 @@
|
||||
#ifndef _INTERFACE_H
|
||||
#define _INTERFACE_H
|
||||
|
||||
#include <tqdatetime.h>
|
||||
#include <tqobject.h>
|
||||
#include <tqcolor.h>
|
||||
#include <tqgl.h>
|
||||
|
||||
#include "circle.h"
|
||||
#include "vector.h"
|
||||
#include "texture.h"
|
||||
|
||||
class table;
|
||||
class rules;
|
||||
class TQString;
|
||||
|
||||
// Handles user interface functionality
|
||||
class GLUserInterface : public TQGLWidget {
|
||||
TQ_OBJECT
|
||||
public:
|
||||
GLUserInterface(TQWidget *parent);
|
||||
~GLUserInterface();
|
||||
|
||||
// Rules' functions
|
||||
void placeBilliard(double cue_line);
|
||||
void startShot(circle cue_location, TQColor player_color, bool forward_only = false);
|
||||
|
||||
void update();
|
||||
|
||||
signals:
|
||||
// Called by the interface when the user has decided on a cue location
|
||||
void billiardPlaced(point &location);
|
||||
// Called by the interface when the user is deciding on a cue location
|
||||
void previewBilliardPlace(point &location);
|
||||
void shotTaken(vector &velocity);
|
||||
|
||||
private:
|
||||
void initializeGL();
|
||||
void resizeGL(int widget, int height);
|
||||
void paintGL();
|
||||
|
||||
void mouseMoveEvent(TQMouseEvent *e);
|
||||
void mousePressEvent(TQMouseEvent *e);
|
||||
void mouseReleaseEvent(TQMouseEvent *e);
|
||||
|
||||
double windowToInternalX(int x);
|
||||
double windowToInternalY(int y);
|
||||
|
||||
void timerEvent( TQTimerEvent * );
|
||||
void updateView();
|
||||
double shotPower();
|
||||
|
||||
void mouseClicked(double x, double y, int button, int state);
|
||||
|
||||
double positionToAngle(double position);
|
||||
double positionToCuePlacement(double position);
|
||||
|
||||
// Are we placing the cue ball?
|
||||
bool _placing_cue;
|
||||
double _cue_line;
|
||||
|
||||
// Are we shooting?
|
||||
bool _shooting;
|
||||
// Can we shoot only in front of the start line?
|
||||
bool _forward_only;
|
||||
|
||||
|
||||
// Where is the cue? And how big is it?
|
||||
circle _cue_location;
|
||||
// Where is the mouse?
|
||||
point _mouse_location;
|
||||
|
||||
// When did the user begin the 'shot' by pressing down the mouse button
|
||||
bool _shot_started;
|
||||
TQTime _shot_time;
|
||||
|
||||
// Cue texture
|
||||
KueTexture _cue_texture;
|
||||
|
||||
// The current player's color
|
||||
TQColor _player_color;
|
||||
|
||||
table *_table;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,52 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Exec=kue -caption "%c" %i %m
|
||||
Icon=kue
|
||||
X-DocPath=ktron/index.html
|
||||
GenericName=Simple billiards game
|
||||
GenericName[bs]=Jednostavna igra bilijara
|
||||
GenericName[ca]=Un senzill joc de billar
|
||||
GenericName[cs]=Jednoduchá kulečníková hra
|
||||
GenericName[da]=Simpelt billardspil
|
||||
GenericName[de]=Einfaches Billiardgames
|
||||
GenericName[el]=Απλό παιχνίδι μπιλιάρδου
|
||||
GenericName[es]=Sencillo juego de billar
|
||||
GenericName[et]=Lihtne piljardimäng
|
||||
GenericName[eu]=Billar txiki bat
|
||||
GenericName[fi]=Yksinkertainen biljardipeli
|
||||
GenericName[fr]=Jeu de billard simple
|
||||
GenericName[gl]=Xogo de Billar
|
||||
GenericName[he]=משחק ביליארד פשוט
|
||||
GenericName[hr]=Jednostavna igra bilijara
|
||||
GenericName[hu]=Billiárd
|
||||
GenericName[it]=Semplice gioco di biliardo
|
||||
GenericName[ja]=シンプルなビリヤードゲーム
|
||||
GenericName[lv]=Vienkārša biljarda spēle
|
||||
GenericName[nl]=Eenvoudig biljartspel
|
||||
GenericName[nn]=Enkelt biljardspel
|
||||
GenericName[pl]=Prosta gra w bilard
|
||||
GenericName[pt]=Jogo de Bilhar
|
||||
GenericName[pt_BR]=Simples jogo de bilhar
|
||||
GenericName[ro]=Un joc simplu de biliard
|
||||
GenericName[ru]=Простой бильярд
|
||||
GenericName[sk]=Jednoduchý billiard
|
||||
GenericName[sl]=Preprosta igra biljarda
|
||||
GenericName[sr]=Једноставна игра билијара
|
||||
GenericName[sr@Latn]=Jednostavna igra bilijara
|
||||
GenericName[sv]=Enkelt biljardspel
|
||||
GenericName[th]=เกมบิลเลียดยอดนิยม
|
||||
GenericName[tr]=Basit bilardo oyunu
|
||||
GenericName[uk]=Простий більярд
|
||||
GenericName[ven]=Mitambo ya billiards isa kondi
|
||||
GenericName[wa]=On djeu di biyård simpe
|
||||
GenericName[xx]=xxSimple billiards gamexx
|
||||
GenericName[zh_CN]=简单的弹子游戏
|
||||
GenericName[zh_TW]=簡單的撞球遊戲
|
||||
GenericName[zu]=Alula amabhodi omdlalo
|
||||
Terminal=false
|
||||
Name=Kue
|
||||
Name[th]=สอยคิว - K
|
||||
Name[xx]=xxKuexx
|
||||
X-TDE-StartupNotify=true
|
||||
X-DCOP-ServiceType=Multi
|
||||
Encoding=UTF-8
|
@ -0,0 +1,82 @@
|
||||
#include <tdeapplication.h>
|
||||
#include <kdebug.h>
|
||||
#include <tqtimer.h>
|
||||
|
||||
#include "localplayer.h"
|
||||
#include "global.h"
|
||||
#include "interface.h"
|
||||
#include "physics.h"
|
||||
|
||||
|
||||
KueLocalPlayer::KueLocalPlayer(TQString n, TQColor color) : KuePlayer(n), _dest(0)
|
||||
{
|
||||
_color = color;
|
||||
}
|
||||
|
||||
// Rules' functions
|
||||
void KueLocalPlayer::placeBilliard(int index, const KueBilliard &b, double line, TQObject *dest, const char *dest_slot)
|
||||
{
|
||||
_dest = dest;
|
||||
_dest_slot = dest_slot;
|
||||
|
||||
connect(KueGlobal::glWidget(), TQ_SIGNAL(billiardPlaced(point &)), this, TQ_SLOT(billiardPlaced(point &)));
|
||||
connect(KueGlobal::glWidget(), TQ_SIGNAL(previewBilliardPlace(point &)), this, TQ_SLOT(previewBilliardPlace(point &)));
|
||||
|
||||
_index = index;
|
||||
// We use the passed billiard as a template, and just move it around
|
||||
KueGlobal::physics()->insertBilliard(index, b);
|
||||
|
||||
KueGlobal::glWidget()->placeBilliard(line);
|
||||
}
|
||||
|
||||
void KueLocalPlayer::takeShot(int index, bool forward_only, TQObject *dest, const char *dest_slot)
|
||||
{
|
||||
_dest = dest;
|
||||
_dest_slot = dest_slot;
|
||||
|
||||
connect(KueGlobal::glWidget(), TQ_SIGNAL(shotTaken(vector &)), this, TQ_SLOT(shotTaken(vector &)));
|
||||
|
||||
_index = index;
|
||||
KueGlobal::glWidget()->startShot(*KueGlobal::physics()->billiards()[index], _color, forward_only);
|
||||
}
|
||||
|
||||
// Called by the interface when the user has decided on a cue location
|
||||
void KueLocalPlayer::billiardPlaced(point &location)
|
||||
{
|
||||
KueGlobal::physics()->billiards()[_index]->setPosition(location);
|
||||
KueGlobal::physics()->run(0);
|
||||
|
||||
disconnect();
|
||||
callBack();
|
||||
}
|
||||
|
||||
// Called by the interface when the user is deciding on a cue location
|
||||
void KueLocalPlayer::previewBilliardPlace(point &location)
|
||||
{
|
||||
// Inform the physics engine
|
||||
KueGlobal::physics()->billiards()[_index]->setPosition(location);
|
||||
|
||||
// Redraw
|
||||
KueGlobal::glWidget()->updateGL();
|
||||
}
|
||||
|
||||
void KueLocalPlayer::shotTaken(vector &velocity)
|
||||
{
|
||||
KueGlobal::physics()->billiards()[_index]->setVelocity(velocity);
|
||||
|
||||
disconnect();
|
||||
callBack();
|
||||
}
|
||||
|
||||
void KueLocalPlayer::callBack()
|
||||
{
|
||||
// Call the completion callback
|
||||
if (_dest)
|
||||
{
|
||||
TQTimer::singleShot(0, _dest, _dest_slot);
|
||||
_dest = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#include "localplayer.moc"
|
@ -0,0 +1,40 @@
|
||||
#ifndef _LOCALPLAYER_H
|
||||
#define _LOCALPLAYER_H
|
||||
|
||||
#include <tqstring.h>
|
||||
#include <tqcolor.h>
|
||||
|
||||
#include "player.h"
|
||||
#include "interface.h"
|
||||
#include "main.h"
|
||||
|
||||
|
||||
class KueLocalPlayer : public KuePlayer {
|
||||
TQ_OBJECT
|
||||
public:
|
||||
KueLocalPlayer(TQString name = TQString::null, TQColor = TQt::white);
|
||||
~KueLocalPlayer() {}
|
||||
|
||||
// Rules' functions
|
||||
void placeBilliard(int index, const KueBilliard &b, double line, TQObject *dest = 0, const char *slot = 0);
|
||||
void takeShot(int billiard, bool forward_only = false, TQObject *dest = 0, const char *slot = 0);
|
||||
|
||||
protected slots:
|
||||
// Called by the interface when the user has decided on a cue location
|
||||
void billiardPlaced(point &location);
|
||||
// Called by the interface when the user is deciding on a cue location
|
||||
void previewBilliardPlace(point &location);
|
||||
void shotTaken(vector &velocity);
|
||||
|
||||
protected:
|
||||
void callBack();
|
||||
|
||||
TQObject *_dest;
|
||||
const char *_dest_slot;
|
||||
TQColor _color;
|
||||
|
||||
double _radius;
|
||||
int _index;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,218 @@
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <tdeapplication.h>
|
||||
#include <tqtimer.h>
|
||||
#include <tdelocale.h>
|
||||
#include <tdecmdlineargs.h>
|
||||
#include <tdeaboutdata.h>
|
||||
#include <tdeconfig.h>
|
||||
#include <tdeglobal.h>
|
||||
#include <tdemenubar.h>
|
||||
#include <tdemessagebox.h>
|
||||
#include <tdepopupmenu.h>
|
||||
#include <kstdaction.h>
|
||||
#include <tdeaction.h>
|
||||
#include <kstatusbar.h>
|
||||
#include <kstdgameaction.h>
|
||||
#include <klibloader.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
#include "newgame.h"
|
||||
#include "main.h"
|
||||
#include "graphics.h"
|
||||
#include "physics.h"
|
||||
#include "rules.h"
|
||||
#include "interface.h"
|
||||
#include "localplayer.h"
|
||||
#include "global.h"
|
||||
#include "config.h"
|
||||
|
||||
MainWindow::MainWindow()
|
||||
{
|
||||
// We have a statusbar, show it now
|
||||
statusBar();
|
||||
|
||||
// Make a game menu
|
||||
TDEPopupMenu *game_menu = new TDEPopupMenu(this);
|
||||
_new_action = KStdGameAction::gameNew(this, TQ_SLOT(newGame()), actionCollection());
|
||||
_new_action->plug(game_menu);
|
||||
_end_action = KStdGameAction::end(this, TQ_SLOT(endGame()), actionCollection());
|
||||
_end_action->plug(game_menu);
|
||||
TDEAction *quit_action = KStdGameAction::quit(this, TQ_SLOT(close()), actionCollection());
|
||||
quit_action->plug(game_menu);
|
||||
menuBar()->insertItem(i18n("&Game"), game_menu);
|
||||
|
||||
// Make a settings menu
|
||||
TDEPopupMenu *settings_menu = new TDEPopupMenu(this);
|
||||
TDEAction *menubar_action = KStdAction::showMenubar(this, TQ_SLOT(toggleMenubar()), actionCollection());
|
||||
menubar_action->plug(settings_menu);
|
||||
TDEAction *statusbar_action = KStdAction::showStatusbar(this, TQ_SLOT(toggleStatusbar()), actionCollection());
|
||||
statusbar_action->plug(settings_menu);
|
||||
menuBar()->insertItem(i18n("&Settings"), settings_menu);
|
||||
|
||||
// Make a help menu
|
||||
TDEPopupMenu *help_menu = helpMenu();
|
||||
menuBar()->insertItem(i18n("&Help"), help_menu);
|
||||
|
||||
// Restore our window size
|
||||
TDEGlobal::config()->setGroup("Window Settings");
|
||||
restoreWindowSize(TDEGlobal::config());
|
||||
|
||||
_in_game = false;
|
||||
_end_action->setEnabled(false);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
TDEGlobal::config()->setGroup("Window Settings");
|
||||
saveWindowSize(TDEGlobal::config());
|
||||
}
|
||||
|
||||
void MainWindow::toggleMenubar()
|
||||
{
|
||||
if (!menuBar()->isHidden())
|
||||
menuBar()->hide();
|
||||
else
|
||||
menuBar()->show();
|
||||
}
|
||||
|
||||
void MainWindow::toggleStatusbar()
|
||||
{
|
||||
if (!statusBar()->isHidden())
|
||||
statusBar()->hide();
|
||||
else
|
||||
statusBar()->show();
|
||||
}
|
||||
|
||||
void MainWindow::newGame()
|
||||
{
|
||||
// Show the "New Game" dialog
|
||||
newGameDialog *new_dialog = new newGameDialog(this, "newgame");
|
||||
if (new_dialog->exec() == TQDialog::Accepted) {
|
||||
// Reset our state
|
||||
if (_in_game)
|
||||
endGame();
|
||||
|
||||
// Recreate our basic objects
|
||||
KueGlobal::_physics = new KuePhysics;
|
||||
KueGlobal::_glWidget = new GLUserInterface(this);
|
||||
setCentralWidget(KueGlobal::_glWidget);
|
||||
|
||||
// Set up the teams
|
||||
TQValueList<KueTeam*> selectedTeams = new_dialog->selectedTeams();
|
||||
|
||||
KueGlobal::teams()->resize(selectedTeams.count());
|
||||
for (unsigned int i = 0;i < KueGlobal::teams()->size(); i++)
|
||||
{
|
||||
KueGlobal::teams()->insert(i, selectedTeams.first());
|
||||
selectedTeams.pop_front();
|
||||
}
|
||||
|
||||
// Load the plugin the user requested
|
||||
KLibFactory *factory = KLibLoader::self()->factory(new_dialog->selectedPlugin().filename.local8Bit());
|
||||
|
||||
// Do we even have an object factory?
|
||||
if (!factory) {
|
||||
kdWarning() << "Unable to retrieve KLibFactory for " << new_dialog->selectedPlugin().filename << endl;
|
||||
delete new_dialog;
|
||||
return;
|
||||
}
|
||||
|
||||
// Actually request an object of type "KueKueGlobal::rules()"
|
||||
TQObject *rules_object = factory->create(this, "KueGlobal::rules()", "KueRulesEngine");
|
||||
|
||||
// Did they return something at all?
|
||||
if (!rules_object) {
|
||||
kdWarning() << "Plugin unable to provide a KueRulesEngine" << endl;
|
||||
delete new_dialog;
|
||||
return;
|
||||
}
|
||||
|
||||
// Is the object -actually- a KueRulesEngine?
|
||||
// Some broken plugins may not check their object type parameter, so this is a sanity check
|
||||
if (!rules_object->inherits("KueRulesEngine")) {
|
||||
kdWarning() << "Plugin returned an object of an unexpected type" << endl;
|
||||
|
||||
delete rules_object;
|
||||
delete new_dialog;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// It checked out, set our KueGlobal::rules() to this object
|
||||
KueGlobal::_rules = (KueRulesEngine*)rules_object;
|
||||
|
||||
connect(KueGlobal::physics(), TQ_SIGNAL(billiardHit(unsigned int, unsigned int)), KueGlobal::rules(), TQ_SLOT(billiardHit(unsigned int, unsigned int)));
|
||||
connect(KueGlobal::physics(), TQ_SIGNAL(billiardSunk(unsigned int, unsigned int)), KueGlobal::rules(), TQ_SLOT(billiardSunk(unsigned int, unsigned int)));
|
||||
connect(KueGlobal::physics(), TQ_SIGNAL(motionStopped()), KueGlobal::rules(), TQ_SLOT(motionStopped()));
|
||||
|
||||
connect(KueGlobal::rules(), TQ_SIGNAL(showMessage(const TQString &)), KueGlobal::mainWindow()->statusBar(), TQ_SLOT(message(const TQString &)));
|
||||
connect(KueGlobal::rules(), TQ_SIGNAL(gameOver(const TQString &)), this, TQ_SLOT(endGame(const TQString &)));
|
||||
|
||||
_in_game = true;
|
||||
_end_action->setEnabled(true);
|
||||
|
||||
KueGlobal::rules()->start();
|
||||
KueGlobal::glWidget()->show();
|
||||
}
|
||||
|
||||
delete new_dialog;
|
||||
}
|
||||
|
||||
void MainWindow::endGame()
|
||||
{
|
||||
delete KueGlobal::_rules;
|
||||
delete KueGlobal::_physics;
|
||||
delete KueGlobal::_glWidget;
|
||||
|
||||
KueGlobal::_rules = nullptr;
|
||||
KueGlobal::_physics = nullptr;
|
||||
KueGlobal::_glWidget = nullptr;
|
||||
|
||||
statusBar()->message(TQString::null);
|
||||
_in_game = false;
|
||||
_end_action->setEnabled(false);
|
||||
}
|
||||
|
||||
void MainWindow::endGame(const TQString &reason)
|
||||
{
|
||||
// Stop the physics engine
|
||||
KueGlobal::physics()->stop();
|
||||
|
||||
// Notify the user
|
||||
KMessageBox::information(this, reason, i18n("Game Over"));
|
||||
|
||||
// We do this delayed, because most modules don't (and can't) call this
|
||||
// at a place where they are ready to have the entire game state
|
||||
// destroyed
|
||||
TQTimer::singleShot(0, this, TQ_SLOT(endGame()));
|
||||
}
|
||||
|
||||
MainWindow* MainWindow::the()
|
||||
{
|
||||
if (!KueGlobal::_mainWindow)
|
||||
KueGlobal::_mainWindow = new MainWindow;
|
||||
return KueGlobal::_mainWindow;
|
||||
}
|
||||
|
||||
// Program starts here
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
TDEAboutData aboutData("kue", I18N_NOOP("Kue"), VERSION,
|
||||
I18N_NOOP("A simple billiards game"),
|
||||
TDEAboutData::License_GPL,
|
||||
I18N_NOOP("(c) 2002 Ryan Cumming"));
|
||||
|
||||
aboutData.addAuthor("Ryan Cumming", 0, "<bodnar42@phalynx.dhs.org>");
|
||||
TDECmdLineArgs::init(argc, argv, &aboutData);
|
||||
|
||||
TDEApplication a;
|
||||
TDEGlobal::locale()->insertCatalogue("libtdegames");
|
||||
TDEGlobal::locale()->insertCatalogue("libkdehighscores");
|
||||
|
||||
MainWindow::the()->show();
|
||||
return a.exec();
|
||||
}
|
||||
|
||||
#include "main.moc"
|
@ -0,0 +1,34 @@
|
||||
#ifndef __MAIN_H_
|
||||
#define __MAIN_H_
|
||||
|
||||
#include <tdemainwindow.h>
|
||||
|
||||
class TDEAction;
|
||||
|
||||
class MainWindow : public TDEMainWindow
|
||||
{
|
||||
TQ_OBJECT
|
||||
public:
|
||||
static MainWindow* the();
|
||||
|
||||
public slots:
|
||||
void toggleMenubar();
|
||||
void toggleStatusbar();
|
||||
void newGame();
|
||||
void endGame();
|
||||
|
||||
protected slots:
|
||||
// Called by plugins
|
||||
void endGame(const TQString &reason);
|
||||
|
||||
protected:
|
||||
MainWindow();
|
||||
~MainWindow();
|
||||
|
||||
private:
|
||||
bool _in_game;
|
||||
TDEAction *_new_action;
|
||||
TDEAction *_end_action;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,337 @@
|
||||
#include <kdebug.h>
|
||||
#include <tdelocale.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "8ball.h"
|
||||
#include "interface.h"
|
||||
#include "physics.h"
|
||||
#include "utility.h"
|
||||
#include "team.h"
|
||||
#include "player.h"
|
||||
#include "global.h"
|
||||
|
||||
const unsigned int BILLIARDS_COUNT = 15;
|
||||
|
||||
K_EXPORT_COMPONENT_FACTORY( libkue8ball, EightBallFactory )
|
||||
|
||||
TQObject *EightBallFactory::createObject (TQObject *parent, const char* name, const char* classname, const TQStringList &args = TQStringList() )
|
||||
{
|
||||
Q_UNUSED(args);
|
||||
|
||||
if (classname != TQString("KueRulesEngine"))
|
||||
return 0;
|
||||
|
||||
return new EightBall(parent, name);
|
||||
}
|
||||
|
||||
EightBall::EightBall(TQObject *parent, const char *name) : KueRulesEngine(parent, name)
|
||||
{
|
||||
KueUtility::layoutTable();
|
||||
KueUtility::layoutPockets();
|
||||
KueUtility::layoutBilliards(KueUtility::Triangle);
|
||||
|
||||
// Reset our state (NOTE: anyone see anything missing?)
|
||||
_game_called = GAME_UNCALLED;
|
||||
_current_team = 0;
|
||||
_first_hit = -1;
|
||||
_first_sunk = -1;
|
||||
_scratch = false;
|
||||
_broke = false;
|
||||
|
||||
_current_player = KueGlobal::teams()->at(_current_team)->nextPlayer();
|
||||
}
|
||||
|
||||
EightBall::~EightBall()
|
||||
{
|
||||
}
|
||||
|
||||
void EightBall::start()
|
||||
{
|
||||
cuePlaced();
|
||||
}
|
||||
|
||||
void EightBall::billiardSunk(unsigned int ball, unsigned int /* pocket */)
|
||||
{
|
||||
// Called when the physics engine sinks a billiard
|
||||
|
||||
// Somebody must win the game if the 8ball is sunk, the question is who
|
||||
if (ballIsMagic(ball))
|
||||
{
|
||||
if (onlyMagicLeft(_current_team))
|
||||
{
|
||||
// It was our only ball left, we win
|
||||
playerWins(_current_team);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We messed up real bad
|
||||
playerWins(!_current_team);
|
||||
}
|
||||
}
|
||||
|
||||
// Have we sunk nothing yet? Or ist the cue ball?
|
||||
if ((ownsBall(!_current_team, ball)) || ballIsCue(ball))
|
||||
{
|
||||
// Oops, we shouldn't have sunk that... scratch!
|
||||
_scratch = true;
|
||||
}
|
||||
else if (_first_sunk == -1)
|
||||
{
|
||||
// Ah, it's all good...
|
||||
_first_sunk = ball;
|
||||
}
|
||||
}
|
||||
|
||||
void EightBall::billiardHit(unsigned int ball1, unsigned int ball2) {
|
||||
// Is this our first hit?
|
||||
if (_first_hit == -1)
|
||||
{
|
||||
// Count the one that isn't the cue ball ;)
|
||||
if (ballIsCue(ball1))
|
||||
{
|
||||
_first_hit = ball2;
|
||||
_broke = true;
|
||||
}
|
||||
else if (ballIsCue(ball2))
|
||||
{
|
||||
_first_hit = ball1;
|
||||
_broke = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EightBall::motionStopped()
|
||||
{
|
||||
// The physics engine has finished its job, turn it off to save CPU time
|
||||
KueGlobal::physics()->stop();
|
||||
|
||||
// Did we hit a ball? And did we own that ball?
|
||||
if ((_first_hit == -1) || ownsBall(!_current_team, _first_hit))
|
||||
{
|
||||
// Nope, scratch
|
||||
_scratch = true;
|
||||
}
|
||||
|
||||
// Did we hit a magic ball first when there are other balls left?
|
||||
if ((!onlyMagicLeft(_current_team)) && ballIsMagic(_first_hit))
|
||||
{
|
||||
// Scratch!
|
||||
_scratch = true;
|
||||
}
|
||||
|
||||
// We downright lose if we scratch on the 8-ball (HA!)
|
||||
if (onlyMagicLeft(_current_team) && _scratch)
|
||||
{
|
||||
playerWins(!_current_team);
|
||||
return;
|
||||
}
|
||||
|
||||
// We lose our turn if the shot was a scratch, or we sunk nothing
|
||||
if ((_scratch) || (_first_sunk == -1))
|
||||
{
|
||||
if (_current_team == 0)
|
||||
_current_team = 1;
|
||||
else
|
||||
_current_team = 0;
|
||||
|
||||
_current_player = KueGlobal::teams()->at(_current_team)->nextPlayer();
|
||||
}
|
||||
|
||||
if (_first_sunk != -1)
|
||||
{
|
||||
if (_game_called == GAME_UNCALLED)
|
||||
{
|
||||
if (_current_team)
|
||||
{
|
||||
_game_called = (ballIsSolid(_first_sunk) ? GAME_PLAYER1_STRIPES : GAME_PLAYER1_SOLIDS);
|
||||
}
|
||||
else
|
||||
{
|
||||
_game_called = (ballIsSolid(_first_sunk) ? GAME_PLAYER1_SOLIDS : GAME_PLAYER1_STRIPES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset our shot state
|
||||
_first_hit = -1;
|
||||
_first_sunk = -1;
|
||||
|
||||
// Did we scratch?
|
||||
if (_scratch)
|
||||
{
|
||||
// Create the cue ball again
|
||||
KueBilliard cue(
|
||||
KueGlobal::physics()->fieldWidth() / 4.0,
|
||||
KueGlobal::physics()->fieldHeight() / 2.0,
|
||||
KueUtility::defaultBilliardRadius()
|
||||
);
|
||||
|
||||
|
||||
if (_broke)
|
||||
{
|
||||
// We scratched, the cue ball goes back home
|
||||
emit(showMessage(placeCueBallMessage()));
|
||||
_current_player->placeBilliard(
|
||||
0,
|
||||
cue,
|
||||
KueGlobal::physics()->fieldWidth() / 4.0,
|
||||
this,
|
||||
TQ_SLOT(cuePlaced())
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
KueGlobal::physics()->insertBilliard(0, cue);
|
||||
cuePlaced();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
emit(showMessage(startShotMessage()));
|
||||
// The cue ball stays where it is, go right to the shot
|
||||
_current_player->takeShot(0, false, this, TQ_SLOT(shotTaken()));
|
||||
}
|
||||
}
|
||||
|
||||
// Is a ball solid?
|
||||
bool EightBall::ballIsSolid(unsigned int number)
|
||||
{
|
||||
return ((number > 0) && (number < 8));
|
||||
}
|
||||
|
||||
// Is a ball 'magic' (8 ball)?
|
||||
bool EightBall::ballIsMagic(unsigned int number)
|
||||
{
|
||||
return (number == 8);
|
||||
}
|
||||
|
||||
// Is a ball striped?
|
||||
bool EightBall::ballIsStripe(unsigned int number)
|
||||
{
|
||||
return (number > 8);
|
||||
}
|
||||
|
||||
// Is a ball the cue ball (ball 0)
|
||||
bool EightBall::ballIsCue(unsigned int number)
|
||||
{
|
||||
return (number == 0);
|
||||
}
|
||||
|
||||
// Does the given player only have the 8 ball left to sink?
|
||||
bool EightBall::onlyMagicLeft(int player)
|
||||
{
|
||||
// It's impossible if the game is uncalled (any game with a sunk ball is called)
|
||||
if (_game_called == GAME_UNCALLED)
|
||||
return false;
|
||||
|
||||
// Check all the billiards belonging to the player
|
||||
for (unsigned int x = 1;x < BILLIARDS_COUNT;x++)
|
||||
{
|
||||
// Does the player own it, and does it still exist
|
||||
if (ownsBall(player, x) && KueGlobal::physics()->billiards()[x])
|
||||
{
|
||||
// So apparently there is more than magic left
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Nope, only magic
|
||||
return true;
|
||||
}
|
||||
|
||||
void EightBall::playerWins(int player)
|
||||
{
|
||||
Q_UNUSED(player);
|
||||
TQString message;
|
||||
|
||||
// Announce the winner
|
||||
message = i18n("%1 wins!").arg(_current_player->name());
|
||||
|
||||
// Show the message
|
||||
emit(showMessage(message));
|
||||
|
||||
// Tell the rest of the game about the stunning victory
|
||||
emit(gameOver(message));
|
||||
}
|
||||
|
||||
// Does the player own the given ball
|
||||
bool EightBall::ownsBall(int player, unsigned int ball)
|
||||
{
|
||||
// Until the game is called, nobody owns anything
|
||||
if (_game_called == GAME_UNCALLED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// And nobody ever owns the 8 ball
|
||||
if (ballIsMagic(ball))
|
||||
return false;
|
||||
|
||||
if (player)
|
||||
{
|
||||
return (_game_called == GAME_PLAYER1_STRIPES) ? ballIsSolid(ball) : ballIsStripe(ball);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (_game_called == GAME_PLAYER1_STRIPES) ? ballIsStripe(ball) : ballIsSolid(ball);
|
||||
}
|
||||
}
|
||||
|
||||
TQString EightBall::startShotMessage()
|
||||
{
|
||||
TQString message;
|
||||
// What type of shot is this?
|
||||
if (_broke)
|
||||
message = i18n("%1's shot").arg(_current_player->name());
|
||||
else
|
||||
message = i18n("%1's break shot").arg(_current_player->name());
|
||||
|
||||
message = message + " " + sideString();
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
TQString EightBall::placeCueBallMessage()
|
||||
{
|
||||
TQString message;
|
||||
|
||||
// Tell the user what is going on
|
||||
message = i18n("%1 placing cue ball").arg(_current_player->name());
|
||||
message = message + " " + sideString();
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
TQString EightBall::sideString()
|
||||
{
|
||||
if (_game_called == GAME_UNCALLED)
|
||||
return "";
|
||||
|
||||
if (_current_team)
|
||||
return (_game_called == GAME_PLAYER1_STRIPES) ? i18n("(solids)") : i18n("(stripes)");
|
||||
else
|
||||
return (_game_called == GAME_PLAYER1_STRIPES) ? i18n("(stripes)") : i18n("(solids)");
|
||||
}
|
||||
|
||||
void EightBall::cuePlaced()
|
||||
{
|
||||
// Tell the interface code to start the shot
|
||||
emit(showMessage(startShotMessage()));
|
||||
_current_player->takeShot(0, true, this, TQ_SLOT(shotTaken()));
|
||||
}
|
||||
|
||||
void EightBall::shotTaken()
|
||||
{
|
||||
// Start the physics engine
|
||||
KueGlobal::physics()->start();
|
||||
|
||||
// Reset the shot-related variables
|
||||
_scratch = false;
|
||||
_first_hit = -1;
|
||||
_first_sunk = -1;
|
||||
}
|
||||
|
||||
|
||||
#include "8ball.moc"
|
@ -0,0 +1,88 @@
|
||||
#ifndef _EIGHTBALL_H
|
||||
#define _EIGHTBALL_H
|
||||
|
||||
#include <tqobject.h>
|
||||
#include <klibloader.h>
|
||||
|
||||
#include "vector.h"
|
||||
#include "point.h"
|
||||
#include "rules.h"
|
||||
|
||||
|
||||
// Forward declarations of our helper classes
|
||||
class TQString;
|
||||
class KuePlayer;
|
||||
|
||||
// Possible values of _game_called
|
||||
enum gameCallType {GAME_UNCALLED, GAME_PLAYER1_STRIPES, GAME_PLAYER1_SOLIDS};
|
||||
|
||||
|
||||
class EightBallFactory : KLibFactory {
|
||||
public:
|
||||
TQObject* createObject(TQObject*, const char*, const char*, const TQStringList &);
|
||||
};
|
||||
|
||||
class EightBall : public KueRulesEngine {
|
||||
TQ_OBJECT
|
||||
public:
|
||||
EightBall(TQObject *parent, const char *name);
|
||||
~EightBall();
|
||||
|
||||
void start();
|
||||
|
||||
protected slots:
|
||||
// Called by physics engine when a billiard is sunk
|
||||
void billiardSunk(unsigned int ball, unsigned int pocket);
|
||||
// Called by physics engine when a billiard is struck (by the cue ball or another billiard)
|
||||
void billiardHit(unsigned int ball1, unsigned int ball2);
|
||||
// Called by the physics engine when all billiards have stopped moving
|
||||
void motionStopped();
|
||||
|
||||
void cuePlaced();
|
||||
void shotTaken();
|
||||
|
||||
private:
|
||||
// Ask the interface to start the shot
|
||||
TQString startShotMessage();
|
||||
// Ask the interface to place the cue ball
|
||||
TQString placeCueBallMessage();
|
||||
|
||||
// Does a player only have an 8 ball left to shoot at?
|
||||
bool onlyMagicLeft(int player);
|
||||
// Does a player own a given ball?
|
||||
bool ownsBall(int player, unsigned int ball);
|
||||
|
||||
// Is a ball solid?
|
||||
bool ballIsSolid(unsigned int number);
|
||||
// Is a ball stripped?
|
||||
bool ballIsStripe(unsigned int number);
|
||||
// Is a ball the cue ball?
|
||||
bool ballIsCue(unsigned int number);
|
||||
// Is a ball the magic ball (8)?
|
||||
bool ballIsMagic(unsigned int number);
|
||||
|
||||
// Handle a player's victory
|
||||
void playerWins(int player);
|
||||
|
||||
// Is this shot a scratch?
|
||||
bool _scratch;
|
||||
// First ball sunk
|
||||
int _first_sunk;
|
||||
// First ball hit
|
||||
int _first_hit;
|
||||
|
||||
// The current player
|
||||
KuePlayer *_current_player;
|
||||
// The current team
|
||||
int _current_team;
|
||||
// Who's stripes? And who's solids?
|
||||
gameCallType _game_called;
|
||||
|
||||
// Have we had a successful break?
|
||||
bool _broke;
|
||||
|
||||
TQString sideString();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,6 @@
|
||||
Filename=libkue8ball
|
||||
Type=rulesengine
|
||||
Name=8-Ball
|
||||
Description=A very simple version of 8-ball pool
|
||||
MinTeams=2
|
||||
MaxTeams=2
|
@ -0,0 +1,42 @@
|
||||
################################################################################
|
||||
# Improvements and feedback are welcome! #
|
||||
# This software is licensed under the terms of the GNU GPL v3 license. #
|
||||
################################################################################
|
||||
|
||||
include_directories(
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${TDE_INCLUDE_DIR}
|
||||
${TQT_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
link_directories(
|
||||
${TQT_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
### kue8ball (library) #########################################################
|
||||
tde_add_library(
|
||||
kue8ball SHARED AUTOMOC
|
||||
|
||||
SOURCES
|
||||
8ball.cpp
|
||||
|
||||
LINK
|
||||
tdeio-shared
|
||||
kue-shared
|
||||
|
||||
DESTINATION
|
||||
${LIB_INSTALL_DIR}
|
||||
)
|
||||
|
||||
### data #######################################################################
|
||||
install(
|
||||
FILES
|
||||
8ball.plugin
|
||||
|
||||
DESTINATION
|
||||
${DATA_INSTALL_DIR}/kue
|
||||
)
|
||||
|
||||
# kate: replace-tabs true; tab-width 2;
|
@ -0,0 +1,217 @@
|
||||
#include <kdebug.h>
|
||||
#include <tdelocale.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "9ball.h"
|
||||
#include "interface.h"
|
||||
#include "physics.h"
|
||||
#include "utility.h"
|
||||
#include "team.h"
|
||||
#include "player.h"
|
||||
#include "global.h"
|
||||
|
||||
const unsigned int BILLIARDS_COUNT = 15;
|
||||
|
||||
K_EXPORT_COMPONENT_FACTORY( libkue9ball, NineBallFactory )
|
||||
|
||||
TQObject *NineBallFactory::createObject (TQObject *parent, const char* name, const char* classname, const TQStringList &args = TQStringList() )
|
||||
{
|
||||
Q_UNUSED(args);
|
||||
|
||||
if (classname != TQString("KueRulesEngine"))
|
||||
return 0;
|
||||
|
||||
return new NineBall(parent, name);
|
||||
}
|
||||
|
||||
NineBall::NineBall(TQObject *parent, const char *name) : KueRulesEngine(parent, name)
|
||||
{
|
||||
KueUtility::layoutTable();
|
||||
KueUtility::layoutPockets();
|
||||
KueUtility::layoutBilliards(KueUtility::Diamond);
|
||||
|
||||
// Reset our state (NOTE: anyone see anything missing?)
|
||||
_current_team = 0;
|
||||
_first_hit = -1;
|
||||
_first_sunk = -1;
|
||||
_foul = false;
|
||||
_broke = false;
|
||||
|
||||
_current_player = KueGlobal::teams()->at(_current_team)->nextPlayer();
|
||||
}
|
||||
|
||||
NineBall::~NineBall()
|
||||
{
|
||||
}
|
||||
|
||||
void NineBall::start()
|
||||
{
|
||||
cuePlaced();
|
||||
}
|
||||
|
||||
void NineBall::billiardSunk(unsigned int ball, unsigned int pocket)
|
||||
{
|
||||
Q_UNUSED(pocket);
|
||||
|
||||
// Ah, it's all good...
|
||||
if (_first_sunk == -1)
|
||||
_first_sunk = ball;
|
||||
|
||||
if (ball == 0)
|
||||
_foul = true;
|
||||
}
|
||||
|
||||
void NineBall::billiardHit(unsigned int ball1, unsigned int ball2)
|
||||
{
|
||||
// Is this our first hit?
|
||||
if (_first_hit == -1)
|
||||
{
|
||||
// Select the ball involved which isn't the cue ball
|
||||
_first_hit = ball1 ? ball1 : ball2;
|
||||
|
||||
if (!ballIsLowest(_first_hit))
|
||||
_foul = true;
|
||||
|
||||
_broke = true;
|
||||
}
|
||||
}
|
||||
|
||||
void NineBall::motionStopped()
|
||||
{
|
||||
// The physics engine has finished its job, turn it off to save CPU time
|
||||
KueGlobal::physics()->stop();
|
||||
|
||||
// Are all the balls 1-9 gone?
|
||||
if (ballIsLowest(10))
|
||||
{
|
||||
playerWins();
|
||||
return;
|
||||
}
|
||||
|
||||
// We lose our turn if the shot was a scratch, or we sunk nothing
|
||||
if ((_foul) || (_first_sunk == -1))
|
||||
{
|
||||
if (_current_team == 0)
|
||||
_current_team = 1;
|
||||
else
|
||||
_current_team = 0;
|
||||
|
||||
_current_player = KueGlobal::teams()->at(_current_team)->nextPlayer();
|
||||
}
|
||||
|
||||
// Reset our shot state
|
||||
_first_hit = -1;
|
||||
_first_sunk = -1;
|
||||
|
||||
// Did we scratch?
|
||||
if (_foul)
|
||||
{
|
||||
// Recreate the cue call
|
||||
KueBilliard cue(
|
||||
KueGlobal::physics()->fieldWidth() / 4.0,
|
||||
KueGlobal::physics()->fieldHeight() / 2.0,
|
||||
KueUtility::defaultBilliardRadius()
|
||||
);
|
||||
|
||||
if (_broke)
|
||||
{
|
||||
// Ask the user where to place the billiard
|
||||
emit(showMessage(placeCueBallMessage()));
|
||||
_current_player->placeBilliard(
|
||||
0,
|
||||
cue,
|
||||
KueGlobal::physics()->fieldWidth() / 4.0,
|
||||
this,
|
||||
TQ_SLOT(cuePlaced())
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We scratched, the cue ball goes back home
|
||||
KueGlobal::physics()->insertBilliard(0, cue);
|
||||
cuePlaced();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
emit(showMessage(startShotMessage()));
|
||||
// The cue ball stays where it is, go right to the shot
|
||||
_current_player->takeShot(0, false, this, TQ_SLOT(shotTaken()));
|
||||
}
|
||||
}
|
||||
|
||||
// Is a ball 'magic' (8 ball)?
|
||||
bool NineBall::ballIsLowest(unsigned int number)
|
||||
{
|
||||
for (unsigned int x = 1;x < number;x++)
|
||||
if (KueGlobal::physics()->billiards()[x])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Is a ball the cue ball (ball 0)
|
||||
bool NineBall::ballIsCue(unsigned int number)
|
||||
{
|
||||
return (number == 0);
|
||||
}
|
||||
|
||||
void NineBall::playerWins()
|
||||
{
|
||||
TQString message;
|
||||
|
||||
// Announce the winner
|
||||
message = i18n("%1 wins!").arg(_current_player->name());
|
||||
|
||||
// Show the message
|
||||
emit(showMessage(message));
|
||||
|
||||
// Tell the rest of the game about the stunning victory
|
||||
emit(gameOver(message));
|
||||
}
|
||||
|
||||
TQString NineBall::startShotMessage()
|
||||
{
|
||||
TQString message;
|
||||
// What type of shot is this?
|
||||
if (_broke)
|
||||
message = i18n("%1's shot").arg(_current_player->name());
|
||||
else
|
||||
message = i18n("%1's break shot").arg(_current_player->name());
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
TQString NineBall::placeCueBallMessage()
|
||||
{
|
||||
TQString message;
|
||||
|
||||
// Tell the user what is going on
|
||||
message = i18n("%1 placing cue ball").arg(_current_player->name());
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void NineBall::cuePlaced()
|
||||
{
|
||||
// Tell the interface code to start the shot
|
||||
emit(showMessage(startShotMessage()));
|
||||
_current_player->takeShot(0, true, this, TQ_SLOT(shotTaken()));
|
||||
}
|
||||
|
||||
void NineBall::shotTaken()
|
||||
{
|
||||
// Start the physics engine
|
||||
KueGlobal::physics()->start();
|
||||
|
||||
// Reset the shot-related variables
|
||||
_foul = false;
|
||||
_first_hit = -1;
|
||||
_first_sunk = -1;
|
||||
}
|
||||
|
||||
|
||||
#include "9ball.moc"
|
@ -0,0 +1,71 @@
|
||||
#ifndef _EIGHTBALL_H
|
||||
#define _EIGHTBALL_H
|
||||
|
||||
#include <tqobject.h>
|
||||
#include <klibloader.h>
|
||||
|
||||
#include "vector.h"
|
||||
#include "point.h"
|
||||
#include "rules.h"
|
||||
|
||||
|
||||
// Forward declarations of our helper classes
|
||||
class TQString;
|
||||
class KuePlayer;
|
||||
|
||||
class NineBallFactory : KLibFactory {
|
||||
public:
|
||||
TQObject* createObject(TQObject*, const char*, const char*, const TQStringList &);
|
||||
};
|
||||
|
||||
class NineBall : public KueRulesEngine {
|
||||
TQ_OBJECT
|
||||
public:
|
||||
NineBall(TQObject *parent, const char *name);
|
||||
~NineBall();
|
||||
|
||||
void start();
|
||||
|
||||
protected slots:
|
||||
// Called by physics engine when a billiard is sunk
|
||||
void billiardSunk(unsigned int ball, unsigned int pocket);
|
||||
// Called by physics engine when a billiard is struck (by the cue ball or another billiard)
|
||||
void billiardHit(unsigned int ball1, unsigned int ball2);
|
||||
// Called by the physics engine when all billiards have stopped moving
|
||||
void motionStopped();
|
||||
|
||||
void cuePlaced();
|
||||
void shotTaken();
|
||||
|
||||
private:
|
||||
// Ask the interface to start the shot
|
||||
TQString startShotMessage();
|
||||
// Ask the interface to place the cue ball
|
||||
TQString placeCueBallMessage();
|
||||
|
||||
// Is a ball the cue ball?
|
||||
bool ballIsCue(unsigned int number);
|
||||
// Is a ball the magic ball (8)?
|
||||
bool ballIsLowest(unsigned int number);
|
||||
|
||||
// Handle a player's victory
|
||||
void playerWins();
|
||||
|
||||
// Is this shot a scratch?
|
||||
bool _foul;
|
||||
// First ball sunk
|
||||
int _first_sunk;
|
||||
// First ball hit
|
||||
int _first_hit;
|
||||
|
||||
// The current player
|
||||
KuePlayer *_current_player;
|
||||
// The current team
|
||||
int _current_team;
|
||||
|
||||
// Have we had a successful break?
|
||||
bool _broke;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,6 @@
|
||||
Filename=libkue9ball
|
||||
Type=rulesengine
|
||||
Name=9-Ball
|
||||
Description=A very simple version of 9-ball pool
|
||||
MinTeams=2
|
||||
MaxTeams=2
|
@ -0,0 +1,42 @@
|
||||
################################################################################
|
||||
# Improvements and feedback are welcome! #
|
||||
# This software is licensed under the terms of the GNU GPL v3 license. #
|
||||
################################################################################
|
||||
|
||||
include_directories(
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${TDE_INCLUDE_DIR}
|
||||
${TQT_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
link_directories(
|
||||
${TQT_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
### kue9ball (library) #########################################################
|
||||
tde_add_library(
|
||||
kue9ball SHARED AUTOMOC
|
||||
|
||||
SOURCES
|
||||
9ball.cpp
|
||||
|
||||
LINK
|
||||
tdeio-shared
|
||||
kue-shared
|
||||
|
||||
DESTINATION
|
||||
${LIB_INSTALL_DIR}
|
||||
)
|
||||
|
||||
### data #######################################################################
|
||||
install(
|
||||
FILES
|
||||
9ball.plugin
|
||||
|
||||
DESTINATION
|
||||
${DATA_INSTALL_DIR}/kue
|
||||
)
|
||||
|
||||
# kate: replace-tabs true; tab-width 2;
|
@ -0,0 +1,8 @@
|
||||
################################################################################
|
||||
# Improvements and feedback are welcome! #
|
||||
# This software is licensed under the terms of the GNU GPL v3 license. #
|
||||
################################################################################
|
||||
|
||||
add_subdirectory(8ball)
|
||||
add_subdirectory(9ball)
|
||||
add_subdirectory(freeplay)
|
@ -0,0 +1,42 @@
|
||||
################################################################################
|
||||
# Improvements and feedback are welcome! #
|
||||
# This software is licensed under the terms of the GNU GPL v3 license. #
|
||||
################################################################################
|
||||
|
||||
include_directories(
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${TDE_INCLUDE_DIR}
|
||||
${TQT_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
link_directories(
|
||||
${TQT_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
### kuefreeplay (library) #######################################################
|
||||
tde_add_library(
|
||||
kuefreeplay SHARED AUTOMOC
|
||||
|
||||
SOURCES
|
||||
freeplay.cpp
|
||||
|
||||
LINK
|
||||
tdeio-shared
|
||||
kue-shared
|
||||
|
||||
DESTINATION
|
||||
${LIB_INSTALL_DIR}
|
||||
)
|
||||
|
||||
### data #######################################################################
|
||||
install(
|
||||
FILES
|
||||
freeplay.plugin
|
||||
|
||||
DESTINATION
|
||||
${DATA_INSTALL_DIR}/kue
|
||||
)
|
||||
|
||||
# kate: replace-tabs true; tab-width 2;
|
@ -0,0 +1,93 @@
|
||||
#include "freeplay.h"
|
||||
#include "interface.h"
|
||||
#include "physics.h"
|
||||
#include "utility.h"
|
||||
#include "player.h"
|
||||
#include "global.h"
|
||||
#include "team.h"
|
||||
|
||||
#include <tdelocale.h>
|
||||
#include <tdemessagebox.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <krandomsequence.h>
|
||||
|
||||
const int BILLIARDS_COUNT = 15;
|
||||
|
||||
K_EXPORT_COMPONENT_FACTORY( libkuefreeplay, FreePlayFactory )
|
||||
|
||||
TQObject *FreePlayFactory::createObject (TQObject *parent, const char* name, const char* classname, const TQStringList &args = TQStringList() )
|
||||
{
|
||||
Q_UNUSED(args);
|
||||
|
||||
if (classname != TQString("KueRulesEngine"))
|
||||
return 0;
|
||||
|
||||
return new FreePlay(parent, name);
|
||||
}
|
||||
|
||||
FreePlay::FreePlay(TQObject *parent, const char *name) : KueRulesEngine(parent, name)
|
||||
{
|
||||
KueUtility::layoutTable();
|
||||
KueUtility::layoutPockets();
|
||||
KueUtility::layoutBilliards(KueUtility::Triangle);
|
||||
|
||||
_current_team = 0;
|
||||
}
|
||||
|
||||
FreePlay::~FreePlay()
|
||||
{
|
||||
}
|
||||
|
||||
void FreePlay::start()
|
||||
{
|
||||
startShot();
|
||||
}
|
||||
|
||||
void FreePlay::motionStopped()
|
||||
{
|
||||
// The physics engine has finished its job, turn it off to save CPU time
|
||||
KueGlobal::physics()->stop();
|
||||
|
||||
_current_team++;
|
||||
|
||||
if (_current_team == KueGlobal::teams()->count())
|
||||
_current_team = 0;
|
||||
|
||||
startShot();
|
||||
}
|
||||
|
||||
void FreePlay::shotTaken()
|
||||
{
|
||||
// Start the physics engine
|
||||
KueGlobal::physics()->start();
|
||||
}
|
||||
|
||||
void FreePlay::startShot()
|
||||
{
|
||||
TQString message;
|
||||
KuePlayer *current_player = KueGlobal::teams()->at(_current_team)->nextPlayer();
|
||||
|
||||
// Did the cue ball get sunk? Replace it.
|
||||
if (!KueGlobal::physics()->billiards()[0])
|
||||
{
|
||||
KueBilliard cue(
|
||||
KueGlobal::physics()->fieldWidth() / 4.0,
|
||||
KueGlobal::physics()->fieldHeight() / 2.0,
|
||||
KueUtility::defaultBilliardRadius()
|
||||
);
|
||||
|
||||
KueGlobal::physics()->insertBilliard(0, cue);
|
||||
}
|
||||
|
||||
// What type of shot is this?
|
||||
message = i18n("%1's shot").arg(current_player->name());
|
||||
|
||||
// Show the generated message
|
||||
emit(showMessage(message));
|
||||
|
||||
// Tell the interface to start the shot UI.
|
||||
current_player->takeShot(0, false, this, TQ_SLOT(shotTaken()));
|
||||
}
|
||||
|
||||
#include "freeplay.moc"
|
@ -0,0 +1,46 @@
|
||||
#ifndef _FREEPLAY_H
|
||||
#define _FREEPLAY_H
|
||||
|
||||
#include <tqobject.h>
|
||||
#include <klibloader.h>
|
||||
|
||||
#include "vector.h"
|
||||
#include "point.h"
|
||||
#include "rules.h"
|
||||
|
||||
|
||||
// Forward declarations of our helper classes
|
||||
class TQString;
|
||||
|
||||
// Possible values of _game_called
|
||||
enum gameCallType {GAME_UNCALLED, GAME_PLAYER1_STRIPES, GAME_PLAYER1_SOLIDS};
|
||||
|
||||
|
||||
class FreePlayFactory : KLibFactory {
|
||||
public:
|
||||
TQObject* createObject(TQObject*, const char*, const char*, const TQStringList &);
|
||||
};
|
||||
|
||||
class FreePlay : public KueRulesEngine {
|
||||
TQ_OBJECT
|
||||
public:
|
||||
FreePlay(TQObject *parent, const char *name);
|
||||
~FreePlay();
|
||||
|
||||
void start();
|
||||
|
||||
protected slots:
|
||||
// Called by the physics engine when all billiards have stopped moving
|
||||
void motionStopped();
|
||||
// Called by the interface after the user has decided on a shot
|
||||
void shotTaken();
|
||||
|
||||
private:
|
||||
// Ask the interface to start the shot
|
||||
void startShot();
|
||||
|
||||
unsigned int _current_team;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,85 @@
|
||||
#ifndef _EIGHTBALL_H
|
||||
#define _EIGHTBALL_H
|
||||
|
||||
#include <ntqobject.h>
|
||||
#include <klibloader.h>
|
||||
|
||||
#include "vector.h"
|
||||
#include "point.h"
|
||||
#include "rules.h"
|
||||
|
||||
|
||||
// Forward declarations of our helper classes
|
||||
class TQString;
|
||||
|
||||
// Possible values of _game_called
|
||||
enum gameCallType {GAME_UNCALLED, GAME_PLAYER1_STRIPES, GAME_PLAYER1_SOLIDS};
|
||||
|
||||
|
||||
class FreePlayFactory : KLibFactory {
|
||||
public:
|
||||
TQObject* createObject(TQObject*, const char*, const char*, const TQStringList &);
|
||||
};
|
||||
|
||||
class FreePlay : public TQObject, public KueRulesEngine {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FreePlay();
|
||||
~FreePlay();
|
||||
|
||||
// Is the game over?
|
||||
bool gameIsOver() { return false; }
|
||||
|
||||
protected slots:
|
||||
// Called by physics engine when a billiard is sunk
|
||||
void billiardSunk(unsigned int ball, unsigned int pocket);
|
||||
// Called by physics engine when a billiard is struck (by the cue ball or another billiard)
|
||||
void billiardHit(unsigned int ball1, unsigned int ball2);
|
||||
// Called by the physics engine when all billiards have stopped moving
|
||||
void motionStopped();
|
||||
|
||||
// Called by the interface when the user has decided on a cue location
|
||||
void cuePlaced(point &location);
|
||||
// Called by the interface when the user is deciding on a cue location
|
||||
void previewCuePlace(point &location);
|
||||
// Called by the interface after the user has decided on a hot
|
||||
void shotTaken(vector &velocity);
|
||||
|
||||
private:
|
||||
// Ask the interface to start the shot
|
||||
void startShot(bool forward_only);
|
||||
// Ask the interface to place the cue ball
|
||||
void placeCueBall();
|
||||
|
||||
// Does a player only have an 8 ball left to shoot at?
|
||||
bool onlyMagicLeft(int player);
|
||||
// Does a player own a given ball?
|
||||
bool ownsBall(int player, unsigned int ball);
|
||||
|
||||
// Is a ball solid?
|
||||
bool ballIsSolid(unsigned int number);
|
||||
// Is a ball stripped?
|
||||
bool ballIsStripe(unsigned int number);
|
||||
// Is a ball the cue ball?
|
||||
bool ballIsCue(unsigned int number);
|
||||
// Is a ball the magic ball (8)?
|
||||
bool ballIsMagic(unsigned int number);
|
||||
|
||||
// Handle a player's victory
|
||||
void playerWins(int player);
|
||||
|
||||
// Is this shot a scratch?
|
||||
bool _scratch;
|
||||
// First ball sunk
|
||||
int _first_sunk;
|
||||
// First ball hit
|
||||
int _first_hit;
|
||||
|
||||
// The current player
|
||||
int _current_player;
|
||||
// Who's stripes? And who's solids?
|
||||
gameCallType _game_called;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,5 @@
|
||||
Filename=libkuefreeplay
|
||||
Type=rulesengine
|
||||
Name=Free Play
|
||||
Description=A very simple version of 8-ball pool
|
||||
# There are no minimum or maximum number of teams
|
@ -0,0 +1,242 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include <tqhbox.h>
|
||||
#include <tqvbox.h>
|
||||
#include <tqlayout.h>
|
||||
#include <tqlabel.h>
|
||||
|
||||
#include <tdelocale.h>
|
||||
#include <kcombobox.h>
|
||||
#include <klineedit.h>
|
||||
#include <kseparator.h>
|
||||
#include <tqpushbutton.h>
|
||||
#include <kdebug.h>
|
||||
#include <kcolorbutton.h>
|
||||
|
||||
#include "team.h"
|
||||
#include "localplayer.h"
|
||||
#include "newgame.h"
|
||||
|
||||
newGameDialog::newGameDialog(TQWidget *parent, const char *name) :
|
||||
KDialogBase(parent, name, true, i18n("New Game"), Ok|Cancel)
|
||||
{
|
||||
_page = new TQWidget( this );
|
||||
// Just makes it look nicer
|
||||
setMinimumSize(400, 300);
|
||||
|
||||
setMainWidget(_page);
|
||||
_top_layout = new TQVBoxLayout(_page, 0, KDialogBase::spacingHint());
|
||||
|
||||
_game_type = new KComboBox(_page);
|
||||
|
||||
// Get a list of available rules plugins
|
||||
_plugins_list = KuePluginLoader::available();
|
||||
TQValueList<KuePluginInfo>::const_iterator plugin = _plugins_list.begin();
|
||||
|
||||
// Go over the plugins that KuePluginLoader::available claims we have
|
||||
for(;plugin != _plugins_list.end();plugin++)
|
||||
{
|
||||
// We only want plugins of the type "rulesengine"
|
||||
if ((*plugin).type == "rulesengine")
|
||||
_game_type->insertItem((*plugin).name);
|
||||
}
|
||||
|
||||
connect(_game_type, TQ_SIGNAL(activated(int)), this, TQ_SLOT(slotNewPluginSelection()));
|
||||
|
||||
// Set up an hbox to put the combo and its label in
|
||||
TQHBoxLayout *hbox = new TQHBoxLayout(_top_layout, KDialogBase::spacingHint());
|
||||
hbox->addWidget(new TQLabel(_game_type, i18n("Game &rules:"), _page));
|
||||
hbox->addWidget(_game_type);
|
||||
hbox->setStretchFactor(_game_type, 2);
|
||||
|
||||
// Add a separator
|
||||
_top_layout->addWidget(new KSeparator(KSeparator::HLine, _page));
|
||||
|
||||
// Add the players widget
|
||||
_player_widget = new TQWidget(_page);
|
||||
_player_layout = new TQGridLayout(_player_widget);
|
||||
_player_layout->setSpacing(KDialogBase::spacingHint());
|
||||
_top_layout->addWidget(_player_widget);
|
||||
|
||||
// Buttons to add/remove players
|
||||
_add_player_button = new TQPushButton(i18n("&Add Player"), _page);
|
||||
connect(_add_player_button, TQ_SIGNAL(clicked()), this, TQ_SLOT(addPlayerRow()));
|
||||
_remove_player_button = new TQPushButton(i18n("Remove &Player"), _page);
|
||||
connect(_remove_player_button, TQ_SIGNAL(clicked()), this, TQ_SLOT(removePlayerRow()));
|
||||
|
||||
// Set up a second hbox for the add/remove player buttons
|
||||
hbox = new TQHBoxLayout(_top_layout, KDialogBase::spacingHint());
|
||||
hbox->addStretch(2);
|
||||
hbox->addWidget(_add_player_button);
|
||||
hbox->addWidget(_remove_player_button);
|
||||
|
||||
// This dialog is slightly less confusing with separator
|
||||
enableButtonSeparator(true);
|
||||
|
||||
// Populates the player widget initially, and sets up the player
|
||||
// buttons' enabled state
|
||||
slotNewPluginSelection();
|
||||
}
|
||||
|
||||
newGameDialog::~newGameDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void newGameDialog::slotOk()
|
||||
{
|
||||
KDialogBase::slotOk();
|
||||
}
|
||||
|
||||
KuePluginInfo newGameDialog::selectedPlugin()
|
||||
{
|
||||
// Find the plugin the user requested
|
||||
TQValueList<KuePluginInfo>::const_iterator plugin = _plugins_list.begin();
|
||||
|
||||
for(;plugin != _plugins_list.end();plugin++)
|
||||
{
|
||||
if (((*plugin).name == _game_type->currentText()) && ((*plugin).type == "rulesengine"))
|
||||
return *plugin;
|
||||
}
|
||||
|
||||
kdWarning() << "Unable to find KuePluginInfo matching user selection" << endl;
|
||||
|
||||
// Oops
|
||||
return KuePluginInfo();
|
||||
}
|
||||
|
||||
TQValueList<KueTeam*> newGameDialog::selectedTeams()
|
||||
{
|
||||
TQValueList<KueTeam *> ret;
|
||||
|
||||
while (_players.count())
|
||||
{
|
||||
KueTeam *temp = new KueTeam;
|
||||
playerRow *row = _players.first();
|
||||
|
||||
// Add the player
|
||||
temp->addPlayer(new KueLocalPlayer(row->nameEdit->text(), row->colorButton->color()));
|
||||
ret.append(temp);
|
||||
|
||||
// Remove the row
|
||||
removePlayerRow(row);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void newGameDialog::addPlayerRow()
|
||||
{
|
||||
playerRow *row = new playerRow;
|
||||
int new_row = _players.count();
|
||||
|
||||
row->label = new TQLabel(i18n("Player %1:").arg(new_row + 1), _player_widget);
|
||||
row->nameEdit = new KLineEdit(i18n("Player %1").arg(new_row + 1), _player_widget);
|
||||
row->colorButton = new KColorButton(defaultPlayerColor(new_row), _player_widget);
|
||||
|
||||
_player_layout->addWidget(row->label, new_row, 0);
|
||||
_player_layout->addWidget(row->nameEdit, new_row, 1);
|
||||
_player_layout->addWidget(row->colorButton, new_row, 2);
|
||||
_player_layout->setRowStretch(new_row, 0);
|
||||
_player_layout->setRowStretch(new_row + 1, 1);
|
||||
|
||||
row->label->show();
|
||||
row->nameEdit->show();
|
||||
row->colorButton->show();
|
||||
|
||||
|
||||
_players.append(row);
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
void newGameDialog::removePlayerRow(playerRow *row)
|
||||
{
|
||||
delete row->label;
|
||||
delete row->nameEdit;
|
||||
delete row->colorButton;
|
||||
delete row;
|
||||
|
||||
_players.remove(row);
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
void newGameDialog::removePlayerRow()
|
||||
{
|
||||
playerRow *row = _players.last();
|
||||
removePlayerRow(row);
|
||||
}
|
||||
|
||||
void newGameDialog::slotNewPluginSelection()
|
||||
{
|
||||
KuePluginInfo info = selectedPlugin();
|
||||
|
||||
// Cache the result so we don't have to call the (relatively) expensive
|
||||
// selectedPlugin() every time we need these values
|
||||
_min_players = info.minTeams;
|
||||
_max_players = info.maxTeams;
|
||||
|
||||
assert(_min_players <= _max_players);
|
||||
|
||||
while (_players.count() < _min_players)
|
||||
{
|
||||
addPlayerRow();
|
||||
}
|
||||
|
||||
while (_players.count() > _max_players)
|
||||
{
|
||||
removePlayerRow();
|
||||
}
|
||||
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
|
||||
void newGameDialog::updateButtons()
|
||||
{
|
||||
_remove_player_button->setEnabled(_players.count() > _min_players);
|
||||
_add_player_button->setEnabled(_players.count() < _max_players);
|
||||
}
|
||||
|
||||
TQColor newGameDialog::defaultPlayerColor(unsigned int player)
|
||||
{
|
||||
// We only have 16 colors :(
|
||||
player = player % 16;
|
||||
|
||||
switch (player)
|
||||
{
|
||||
case 0:
|
||||
return TQt::white;
|
||||
case 1:
|
||||
return TQt::black;
|
||||
case 2:
|
||||
return TQt::red;
|
||||
case 3:
|
||||
return TQt::green;
|
||||
case 4:
|
||||
return TQt::blue;
|
||||
case 5:
|
||||
return TQt::cyan;
|
||||
case 6:
|
||||
return TQt::magenta;
|
||||
case 7:
|
||||
return TQt::yellow;
|
||||
case 8:
|
||||
return TQt::gray;
|
||||
case 9:
|
||||
return TQt::darkRed;
|
||||
case 10:
|
||||
return TQt::darkGreen;
|
||||
case 11:
|
||||
return TQt::darkBlue;
|
||||
case 12:
|
||||
return TQt::darkCyan;
|
||||
case 13:
|
||||
return TQt::darkMagenta;
|
||||
case 14:
|
||||
return TQt::darkYellow;
|
||||
default:
|
||||
return TQt::darkGray;
|
||||
}
|
||||
}
|
||||
|
||||
#include "newgame.moc"
|
||||
|
@ -0,0 +1,64 @@
|
||||
#ifndef _NEWGAME_H
|
||||
#define _NEWGAME_H
|
||||
|
||||
#include <kdialogbase.h>
|
||||
#include "pluginloader.h"
|
||||
#include <ntqvaluelist.h>
|
||||
|
||||
class KColorButton;
|
||||
class KComboBox;
|
||||
class KueTeam;
|
||||
class TQGridLayout;
|
||||
class TQVBoxLayout;
|
||||
class TQLabel;
|
||||
class KLineEdit;
|
||||
class TQPushButton;
|
||||
|
||||
struct playerRow
|
||||
{
|
||||
TQLabel *label;
|
||||
KLineEdit *nameEdit;
|
||||
KColorButton *colorButton;
|
||||
};
|
||||
|
||||
class newGameDialog : public KDialogBase
|
||||
{
|
||||
TQ_OBJECT
|
||||
public:
|
||||
newGameDialog(TQWidget *parent, const char *name = 0);
|
||||
~newGameDialog();
|
||||
|
||||
KuePluginInfo selectedPlugin();
|
||||
TQValueList<KueTeam*> selectedTeams();
|
||||
|
||||
protected slots:
|
||||
void slotOk();
|
||||
void slotNewPluginSelection();
|
||||
|
||||
void addPlayerRow();
|
||||
void removePlayerRow();
|
||||
|
||||
protected:
|
||||
void removePlayerRow(playerRow *);
|
||||
void updateButtons();
|
||||
TQColor defaultPlayerColor(unsigned int player);
|
||||
|
||||
TQWidget *_page;
|
||||
TQVBoxLayout *_top_layout;
|
||||
|
||||
TQPushButton *_add_player_button;
|
||||
TQPushButton *_remove_player_button;
|
||||
|
||||
TQWidget *_player_widget;
|
||||
TQGridLayout *_player_layout;
|
||||
TQPtrList<playerRow> _players;
|
||||
|
||||
KComboBox *_game_type;
|
||||
TQValueList<KuePluginInfo> _plugins_list;
|
||||
|
||||
unsigned int _max_players;
|
||||
unsigned int _min_players;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,189 @@
|
||||
#include "physics.h"
|
||||
#include "rules.h"
|
||||
#include "interface.h"
|
||||
#include "global.h"
|
||||
|
||||
#include <GL/gl.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ntqsize.h>
|
||||
#include <stdio.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
const double COLLISION_DRAG = 0.95;
|
||||
const double BUMPER_DRAG = 0.8;
|
||||
|
||||
KuePhysics::KuePhysics() : _running(false), _field_width(0.0), _field_height(0.0)
|
||||
{
|
||||
_billiards.setAutoDelete(true);
|
||||
_pockets.setAutoDelete(true);
|
||||
}
|
||||
|
||||
KuePhysics::~KuePhysics()
|
||||
{
|
||||
}
|
||||
|
||||
void KuePhysics::timerEvent ( TQTimerEvent * )
|
||||
{
|
||||
// Have all the balls stopped?
|
||||
if (!run(TIME_CHUNK))
|
||||
{
|
||||
// Tell the rules manager
|
||||
emit(motionStopped());
|
||||
return;
|
||||
}
|
||||
|
||||
// We need a redisplay
|
||||
KueGlobal::glWidget()->updateGL();
|
||||
}
|
||||
|
||||
void KuePhysics::seperate(KueBilliard *a, KueBilliard *b)
|
||||
{
|
||||
double distance = a->distance(*b);
|
||||
double delta = (a->radius() + b->radius() - distance) / 2.0;
|
||||
double angle = a->angle(*b);
|
||||
double delta_x = cos(angle) * delta;
|
||||
double delta_y = sin(angle) * delta;
|
||||
|
||||
a->setPositionX(a->positionX() - delta_x);
|
||||
a->setPositionY(a->positionY() - delta_y);
|
||||
|
||||
b->setPositionX(b->positionX() + delta_x);
|
||||
b->setPositionY(b->positionY() + delta_y);
|
||||
}
|
||||
|
||||
bool KuePhysics::allStop()
|
||||
{
|
||||
for (unsigned int i = 0;i < _billiards.size();i++)
|
||||
if (_billiards[i])
|
||||
if (!_billiards[i]->isStopped())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KuePhysics::doPocketing()
|
||||
{
|
||||
for (unsigned int b = 0;b < _billiards.size();b++)
|
||||
{
|
||||
if (!_billiards[b])
|
||||
continue;
|
||||
|
||||
for (unsigned int p = 0;p < _pockets.size();p++)
|
||||
{
|
||||
if (!_pockets[p])
|
||||
continue;
|
||||
|
||||
if (_billiards[b]->distance(*_pockets[p]) <= _pockets[p]->radius())
|
||||
{
|
||||
emit(billiardSunk(b, p));
|
||||
|
||||
_billiards.remove(b);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KuePhysics::insertBilliard(unsigned int index, const KueBilliard &b)
|
||||
{
|
||||
// Do we need to grow the vector?
|
||||
if (index >= _billiards.size())
|
||||
_billiards.resize(index + 1);
|
||||
|
||||
// Insert the new billiard
|
||||
_billiards.insert(index, new KueBilliard(b));
|
||||
}
|
||||
|
||||
void KuePhysics::insertPocket(unsigned int index, const KuePocket &p)
|
||||
{
|
||||
// Grow the vector as needed
|
||||
if (index >= _pockets.size())
|
||||
_pockets.resize(index + 1);
|
||||
|
||||
// Insert the new pocket
|
||||
_pockets.insert(index, new KuePocket(p));
|
||||
}
|
||||
|
||||
bool KuePhysics::run(int milliseconds)
|
||||
{
|
||||
// The collison code accepts values in seconds, not milliseconds
|
||||
double seconds = milliseconds / 1000.0;
|
||||
|
||||
for (int i = _billiards.size() - 1;i >= 0;i--) {
|
||||
if (!_billiards[i])
|
||||
continue;
|
||||
|
||||
// Move the billiards forwards along their velocity vectors
|
||||
_billiards[i]->step(seconds);
|
||||
|
||||
// Save the x, and radius y values so we don't waste a bunch of
|
||||
// function calls
|
||||
double x = _billiards[i]->positionX();
|
||||
double y = _billiards[i]->positionY();
|
||||
double radius = _billiards[i]->radius();
|
||||
|
||||
// Check if the billiard intersects with any edge, and if it does
|
||||
// reflect it along the side it hit, and then move the billiard
|
||||
// back on the playing field.
|
||||
// Pretty terse code, but it really needs to be fast
|
||||
if ((x + radius) > _field_width)
|
||||
{
|
||||
_billiards[i]->reflect(0.0);
|
||||
_billiards[i]->velocity() *= BUMPER_DRAG;
|
||||
_billiards[i]->setPositionX(_field_width - radius);
|
||||
}
|
||||
else if (x < radius)
|
||||
{
|
||||
_billiards[i]->reflect(0.0);
|
||||
_billiards[i]->velocity() *= BUMPER_DRAG;
|
||||
_billiards[i]->setPositionX(radius);
|
||||
}
|
||||
|
||||
if ((y + radius) > _field_height)
|
||||
{
|
||||
_billiards[i]->reflect(M_PI / 2.0);
|
||||
_billiards[i]->velocity() *= BUMPER_DRAG;
|
||||
_billiards[i]->setPositionY(_field_height - radius);
|
||||
}
|
||||
else if (y < radius)
|
||||
{
|
||||
_billiards[i]->reflect(M_PI / 2.0);
|
||||
_billiards[i]->velocity() *= BUMPER_DRAG;
|
||||
_billiards[i]->setPositionY(radius);
|
||||
}
|
||||
|
||||
for (unsigned int j = i + 1;j <= _billiards.size() - 1;j++) {
|
||||
// If this isn't a billiard anymore, skip it
|
||||
if (!_billiards[j])
|
||||
continue;
|
||||
|
||||
// Are these billiards intersecting (colliding)?
|
||||
if (_billiards[i]->intersects(*_billiards[j]))
|
||||
{
|
||||
// Update their velocity vectors
|
||||
_billiards[i]->collide(*_billiards[j]);
|
||||
// Physically seperate the two balls so we don't
|
||||
// call the collision code again
|
||||
seperate(_billiards[i], _billiards[j]);
|
||||
|
||||
// Factor in collision drag
|
||||
_billiards[i]->velocity() *= COLLISION_DRAG;
|
||||
_billiards[j]->velocity() *= COLLISION_DRAG;
|
||||
|
||||
// Tell the rules engine that we hit a ball
|
||||
emit(billiardHit(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doPocketing();
|
||||
|
||||
// Return true if we aren't done yet
|
||||
return (!allStop());
|
||||
}
|
||||
|
||||
#include "physics.moc"
|
@ -0,0 +1,76 @@
|
||||
#ifndef _PHYSICS_H
|
||||
#define _PHYSICS_H
|
||||
|
||||
#include "main.h"
|
||||
#include "billiard.h"
|
||||
#include "pocket.h"
|
||||
#include "circle.h"
|
||||
#include "point.h"
|
||||
#include "vector.h"
|
||||
|
||||
#include <ntqptrvector.h>
|
||||
|
||||
const int TIME_CHUNK = 10;
|
||||
|
||||
class KuePhysics : public TQObject {
|
||||
TQ_OBJECT
|
||||
|
||||
public:
|
||||
KuePhysics();
|
||||
~KuePhysics();
|
||||
|
||||
// Start the physics engine
|
||||
void start() { _running = true; startTimer(TIME_CHUNK); }
|
||||
// Full stop
|
||||
void stop() { _running = false; killTimers(); }
|
||||
|
||||
bool running() { return _running; }
|
||||
|
||||
// Run the physics state for a set number of milliseconds
|
||||
// You should really only pass this 0, to seperate billiards on demand
|
||||
// If you can think of a clever use for it, go right ahead, though
|
||||
bool run(int milliseconds);
|
||||
|
||||
// Inserts a billard at a specific location
|
||||
void insertBilliard(unsigned int billiard, const KueBilliard &b);
|
||||
|
||||
// Removes a given billiard
|
||||
void removeBilliard(unsigned int billiard) { _billiards.remove(billiard); }
|
||||
|
||||
// Inserts pocket at a specific location
|
||||
void insertPocket(unsigned int pocket, const KuePocket &p);
|
||||
|
||||
// Removes a given pocket
|
||||
void removePocket(unsigned int pocket) { _pockets.remove(pocket); }
|
||||
|
||||
TQPtrVector<KueBilliard> & billiards() { return _billiards; }
|
||||
TQPtrVector<KuePocket> & pockets() { return _pockets; }
|
||||
|
||||
double fieldWidth() { return _field_width; }
|
||||
double fieldHeight() { return _field_height; }
|
||||
|
||||
void setFieldWidth(double field_width) { _field_width = field_width; }
|
||||
void setFieldHeight(double field_height) { _field_height = field_height; }
|
||||
|
||||
signals:
|
||||
void billiardHit(unsigned int b1, unsigned int b2);
|
||||
void billiardSunk(unsigned int b, unsigned int p);
|
||||
void motionStopped();
|
||||
|
||||
protected:
|
||||
void doPocketing();
|
||||
void timerEvent ( TQTimerEvent * );
|
||||
|
||||
bool _running;
|
||||
void seperate(KueBilliard *a, KueBilliard *b);
|
||||
|
||||
bool allStop();
|
||||
|
||||
TQPtrVector<KueBilliard> _billiards;
|
||||
TQPtrVector<KuePocket> _pockets;
|
||||
|
||||
double _field_width;
|
||||
double _field_height;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,32 @@
|
||||
#ifndef _PLAYER_H
|
||||
#define _PLAYER_H
|
||||
|
||||
#include <tqstring.h>
|
||||
#include <tqobject.h>
|
||||
|
||||
#include "circle.h"
|
||||
#include "point.h"
|
||||
#include "vector.h"
|
||||
#include "billiard.h"
|
||||
|
||||
class KuePlayer : public TQObject {
|
||||
public:
|
||||
KuePlayer(TQString name = TQString::null) { _name = name; }
|
||||
~KuePlayer() {}
|
||||
|
||||
virtual const TQString &name() { return _name; }
|
||||
void setName(const TQString &name) { _name = name; }
|
||||
|
||||
// Rules' functions
|
||||
|
||||
// Place the billiard 'b' at a user defined location
|
||||
virtual void placeBilliard(int index, const KueBilliard &b, double line, TQObject *_dest = 0, const char *slot = 0) = 0;
|
||||
|
||||
// Take a shot on billiard with index 'billiard_index'
|
||||
virtual void takeShot(int billiard_index, bool forward_only = false, TQObject *_dest = 0, const char *slot = 0) = 0;
|
||||
|
||||
protected:
|
||||
TQString _name;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,40 @@
|
||||
#include <tqstring.h>
|
||||
#include <tqvaluelist.h>
|
||||
#include <tdeglobal.h>
|
||||
#include <kstandarddirs.h>
|
||||
#include <tqfile.h>
|
||||
#include <ksimpleconfig.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "pluginloader.h"
|
||||
|
||||
TQValueList<KuePluginInfo> KuePluginLoader::available() {
|
||||
TQValueList<KuePluginInfo> items;
|
||||
|
||||
TQStringList files=TDEGlobal::dirs()->findAllResources("appdata", "*.plugin", false, true);
|
||||
for (TQStringList::Iterator i=files.begin(); i!=files.end(); ++i) {
|
||||
items.append(getInformation(*i));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
KuePluginInfo KuePluginLoader::getInformation(const TQString &filename) {
|
||||
KuePluginInfo info;
|
||||
|
||||
if (!TQFile::exists(filename))
|
||||
return info;
|
||||
|
||||
KSimpleConfig file(filename);
|
||||
|
||||
info.filename = file.readPathEntry("Filename");
|
||||
info.type = file.readEntry("Type");
|
||||
info.name = file.readEntry("Name");
|
||||
info.description = file.readEntry("Description");
|
||||
|
||||
info.minTeams = TQMAX(file.readUnsignedNumEntry("MinTeams", 1), 1);
|
||||
info.maxTeams = TQMAX(file.readUnsignedNumEntry("MaxTeams", UINT_MAX), 1);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
#ifndef _PLUGINLOADER_H
|
||||
#define _PLUGINLOADER_H
|
||||
|
||||
#include <tqvaluelist.h>
|
||||
|
||||
class TQString;
|
||||
|
||||
class KuePluginInfo {
|
||||
public:
|
||||
TQString name;
|
||||
TQString description;
|
||||
TQString filename;
|
||||
TQString type;
|
||||
|
||||
// Minimum and maximum number of teams
|
||||
unsigned int minTeams;
|
||||
unsigned int maxTeams;
|
||||
};
|
||||
|
||||
namespace KuePluginLoader {
|
||||
TQValueList<KuePluginInfo> available();
|
||||
KuePluginInfo getInformation(const TQString &filename);
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,10 @@
|
||||
#ifndef _POCKET_H
|
||||
#define _POCKET_H
|
||||
|
||||
// Pockets are just circles with a set radius
|
||||
class KuePocket : public circle {
|
||||
public:
|
||||
KuePocket(double x, double y, double r) : circle(x, y, r) {}
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,19 @@
|
||||
#include "point.h"
|
||||
#include <math.h>
|
||||
|
||||
point::point(double x, double y) {
|
||||
_pos_x = x;
|
||||
_pos_y = y;
|
||||
}
|
||||
|
||||
point::~point() {
|
||||
}
|
||||
|
||||
double point::distance(const point &other_point) const {
|
||||
// Finds the distance between two points, using:
|
||||
// d^2 = (x1 - x2)^2 + (y1 - y2)^2
|
||||
double delta_x = _pos_x - other_point._pos_x;
|
||||
double delta_y = _pos_y - other_point._pos_y;
|
||||
|
||||
return sqrt((delta_x * delta_x) + (delta_y * delta_y));
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
#ifndef _POINT_H
|
||||
#define _POINT_H
|
||||
|
||||
#include <math.h>
|
||||
|
||||
// Point is just a point on a 2D plane
|
||||
class point {
|
||||
public:
|
||||
point(double x = 0.0, double y = 0.0);
|
||||
~point();
|
||||
|
||||
// Gets our position
|
||||
double positionX() const { return _pos_x; }
|
||||
double positionY() const { return _pos_y; }
|
||||
|
||||
// Sets our position
|
||||
void setPositionX(double new_pos) {_pos_x = new_pos;}
|
||||
void setPositionY(double new_pos) {_pos_y = new_pos;}
|
||||
void setPosition(double new_x, double new_y) {_pos_x = new_x; _pos_y = new_y; }
|
||||
void setPosition(const point &p) {_pos_x = p._pos_x; _pos_y = p._pos_y; }
|
||||
|
||||
// Finds the distance between us and another point
|
||||
double distance(const point &other_point) const;
|
||||
// Finds the angle between us and another point
|
||||
double angle(const point &other_point) const { return atan2(other_point._pos_y - _pos_y, other_point._pos_x - _pos_x); }
|
||||
|
||||
protected:
|
||||
double _pos_x;
|
||||
double _pos_y;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,22 @@
|
||||
#include "rules.h"
|
||||
|
||||
// Stub implementations
|
||||
// These functions exist purely to define an interface to inherit from
|
||||
|
||||
void KueRulesEngine::billiardSunk(unsigned int ball, unsigned int pocket)
|
||||
{
|
||||
Q_UNUSED(ball);
|
||||
Q_UNUSED(pocket);
|
||||
}
|
||||
|
||||
void KueRulesEngine::billiardHit(unsigned int ball1, unsigned int ball2)
|
||||
{
|
||||
Q_UNUSED(ball1);
|
||||
Q_UNUSED(ball2);
|
||||
}
|
||||
|
||||
void KueRulesEngine::motionStopped()
|
||||
{
|
||||
}
|
||||
|
||||
#include "rules.moc"
|
@ -0,0 +1,34 @@
|
||||
#ifndef _RULES_H
|
||||
#define _RULES_H
|
||||
|
||||
#include <tqobject.h>
|
||||
|
||||
#include "vector.h"
|
||||
#include "point.h"
|
||||
#include "rules.h"
|
||||
|
||||
// Temple for rules engine plugins
|
||||
class KueRulesEngine : public TQObject {
|
||||
TQ_OBJECT
|
||||
public:
|
||||
KueRulesEngine(TQObject *parent = 0, const char *name = 0) : TQObject(parent, name) {}
|
||||
virtual ~KueRulesEngine() {}
|
||||
|
||||
virtual void start() = 0;
|
||||
|
||||
signals:
|
||||
// Emitting gameOver notifies the user of the result, stops the physics
|
||||
// engine, and schedules us for deletion
|
||||
void gameOver(const TQString &result);
|
||||
void showMessage(const TQString &text);
|
||||
|
||||
protected slots:
|
||||
// Called by physics engine when a billiard is sunk
|
||||
virtual void billiardSunk(unsigned int ball, unsigned int pocket);
|
||||
// Called by physics engine when a billiard is struck (by the cue ball or another billiard)
|
||||
virtual void billiardHit(unsigned int ball1, unsigned int ball2);
|
||||
// Called by the physics engine when all billiards have stopped moving
|
||||
virtual void motionStopped();
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,61 @@
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <tdeconfig.h>
|
||||
#include <tdeglobal.h>
|
||||
#include "sphere.h"
|
||||
|
||||
const int SPHERE_DISPLAY_LIST = 2;
|
||||
double sphere_list_radius = 0.0;
|
||||
|
||||
void sphere::draw(double x, double y, double r, double rot_x, double rot_y)
|
||||
{
|
||||
glPushMatrix();
|
||||
glTranslated(x, y, r);
|
||||
|
||||
// Rotate the balls the specified the amount
|
||||
glRotated(rot_x, 0.0, 1.0, 0.0);
|
||||
glRotated(rot_y, 1.0, 0.0, 0.0);
|
||||
|
||||
// Do we have this sphere cached?
|
||||
if ((r == sphere_list_radius) && (glIsList(SPHERE_DISPLAY_LIST) == GL_TRUE))
|
||||
{
|
||||
// It was cached, call the list
|
||||
glCallList(SPHERE_DISPLAY_LIST);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Figure out the number of sphere divisons we need
|
||||
TDEGlobal::config()->setGroup("Graphics");
|
||||
int sphere_divisions = TDEGlobal::config()->readNumEntry("Sphere Divisions", 8);
|
||||
|
||||
// sphere_divisions < 3 causes OpenGL to draw nothing at all,
|
||||
// so clamp to value to 3.
|
||||
if (sphere_divisions < 3)
|
||||
sphere_divisions = 3;
|
||||
|
||||
// Make the quadratic object
|
||||
GLUquadricObj *quad = gluNewQuadric();
|
||||
|
||||
// We need normals for lighting to work
|
||||
gluQuadricNormals(quad, GLU_SMOOTH);
|
||||
// We also need texture points
|
||||
gluQuadricTexture(quad, GL_TRUE);
|
||||
|
||||
// Create the display list
|
||||
glNewList(SPHERE_DISPLAY_LIST, GL_COMPILE_AND_EXECUTE);
|
||||
// Draw the sphere
|
||||
gluSphere(quad, r, sphere_divisions, sphere_divisions);
|
||||
// End the display list
|
||||
glEndList();
|
||||
|
||||
// Update the cached radius
|
||||
sphere_list_radius = r;
|
||||
|
||||
// Delete quadatric object
|
||||
gluDeleteQuadric(quad);
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
#ifndef _SPHERE_H
|
||||
#define _SPHERE_H
|
||||
|
||||
|
||||
// Draws a sphere
|
||||
class sphere {
|
||||
public:
|
||||
static void draw(double x, double y, double radius, double rot_x, double rot_y);
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,119 @@
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
#include <tqstring.h>
|
||||
|
||||
#include "table.h"
|
||||
#include "graphics.h"
|
||||
|
||||
|
||||
table::table() : _texture("table") {
|
||||
}
|
||||
|
||||
table::~table() {
|
||||
}
|
||||
|
||||
|
||||
void table::draw(double play_area_width, double play_area_height)
|
||||
{ // The location of the head string
|
||||
double head_string = play_area_width / 4.0;
|
||||
|
||||
// Set the texture to the felt texture
|
||||
_texture.makeCurrent();
|
||||
|
||||
glColor3d(0.1, 1.0, 0.1);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
// Draw the table as a green textured square
|
||||
glNormal3d(0.0, 0.0, 1.0);
|
||||
|
||||
glTexCoord2d(0.0, 0.0);
|
||||
glVertex2d(0.0, 0.0);
|
||||
|
||||
glTexCoord2d(16.0, 0.0);
|
||||
glVertex2d(play_area_width, 0.0);
|
||||
|
||||
glTexCoord2d(16.0, 8.0);
|
||||
glVertex2d(play_area_width, play_area_height);
|
||||
|
||||
glTexCoord2d(0.0, 8.0);
|
||||
glVertex2d(0.0, play_area_height);
|
||||
|
||||
|
||||
// Draw the table as a green textured square
|
||||
glNormal3d(0.0, 1.0, 0.0);
|
||||
|
||||
glTexCoord2d(0.0, 1.0);
|
||||
glVertex3d(0.0, 0.0, 0.0);
|
||||
|
||||
glTexCoord2d(0.0, 0.0);
|
||||
glVertex3d(0.0, 0.0, 0.005);
|
||||
|
||||
glTexCoord2d(16.0, 1.0);
|
||||
glVertex3d(play_area_width, 0.0, 0.005);
|
||||
|
||||
glTexCoord2d(16.0, 0.0);
|
||||
glVertex3d(play_area_width, 0.0, 0.0);
|
||||
|
||||
|
||||
glNormal3d(0.0, -1.0, 0.0);
|
||||
|
||||
glTexCoord2d(0.0, 0.0);
|
||||
glVertex3d(0.0, play_area_height, 0.0);
|
||||
|
||||
glTexCoord2d(0.0, 1.0);
|
||||
glVertex3d(0.0, play_area_height, 0.005);
|
||||
|
||||
glTexCoord2d(16.0, 0.0);
|
||||
glVertex3d(play_area_width, play_area_height, 0.005);
|
||||
|
||||
glTexCoord2d(16.0, 1.0);
|
||||
glVertex3d(play_area_width, play_area_height, 0.0);
|
||||
|
||||
glNormal3d(1.0, 0.0, 0.0);
|
||||
|
||||
glTexCoord2d(0.0, 0.0);
|
||||
glVertex3d(0.0, 0.0, 0.0);
|
||||
|
||||
glTexCoord2d(0.0, 1.0);
|
||||
glVertex3d(0.0, 0.0, 0.005);
|
||||
|
||||
glTexCoord2d(16.0, 0.0);
|
||||
glVertex3d(0.0, play_area_height, 0.005);
|
||||
|
||||
glTexCoord2d(16.0, 1.0);
|
||||
glVertex3d(0.0, play_area_height, 0.0);
|
||||
|
||||
glNormal3d(-1.0, 0.0, 0.0);
|
||||
|
||||
glTexCoord2d(0.0, 0.0);
|
||||
glVertex3d(play_area_width, 0.0, 0.0);
|
||||
|
||||
glTexCoord2d(0.0, 1.0);
|
||||
glVertex3d(play_area_width, 0.0, 0.005);
|
||||
|
||||
glTexCoord2d(16.0, 0.0);
|
||||
glVertex3d(play_area_width, play_area_height, 0.005);
|
||||
|
||||
glTexCoord2d(16.0, 1.0);
|
||||
glVertex3d(play_area_width, play_area_height, 0.0);
|
||||
|
||||
|
||||
glEnd();
|
||||
|
||||
// Draw the head string line on in a color slightly lighter than the table
|
||||
glColor3d(0.2, 1.0, 0.2);
|
||||
|
||||
glNormal3d(0.0, 0.0, 1.0);
|
||||
|
||||
// Make the line two pixels thick
|
||||
glLineWidth(2.0);
|
||||
|
||||
// Actually draw the line
|
||||
glBegin(GL_LINES);
|
||||
|
||||
glVertex2d(head_string, 0.0);
|
||||
glVertex2d(head_string, play_area_height);
|
||||
|
||||
glEnd();
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
#ifndef _TABLE_H
|
||||
#define _TABLE_H
|
||||
|
||||
#include "texture.h"
|
||||
|
||||
// Draws a table
|
||||
class table {
|
||||
public:
|
||||
table();
|
||||
~table();
|
||||
|
||||
void draw(double width, double height);
|
||||
|
||||
private:
|
||||
KueTexture _texture;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,26 @@
|
||||
#ifndef _TEAM_H
|
||||
#define _TEAM_H
|
||||
|
||||
#include <tqptrlist.h>
|
||||
#include "player.h"
|
||||
|
||||
class KueTeam {
|
||||
public:
|
||||
KueTeam() {}
|
||||
~KueTeam() {}
|
||||
|
||||
// Moves currentPlayer forward to the next player, and returns its new value
|
||||
KuePlayer *nextPlayer() { if (!_players.next()) return _players.first(); else return _players.current();}
|
||||
// Returns the current player
|
||||
KuePlayer *currentPlayer() { return _players.current(); }
|
||||
|
||||
// Adds a new player
|
||||
void addPlayer(KuePlayer *p) { _players.append(p); }
|
||||
// Removes an existing player
|
||||
void removePlayer(KuePlayer *p) { _players.remove(p); }
|
||||
|
||||
private:
|
||||
TQPtrList<KuePlayer> _players;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,140 @@
|
||||
#include "texture.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include <tqimage.h>
|
||||
#include <tqgl.h>
|
||||
#include <tdeglobal.h>
|
||||
#include <tdeconfig.h>
|
||||
#include <tqstring.h>
|
||||
#include <kstandarddirs.h>
|
||||
|
||||
KueTexture::KueTexture(const TQString &filename)
|
||||
{
|
||||
_filename = filename;
|
||||
_texture_id = 0;
|
||||
|
||||
if (filename.isNull())
|
||||
{
|
||||
// filename == TQString::null is an alias for the null texture
|
||||
_loaded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_loaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
KueTexture::KueTexture(unsigned int texture_id)
|
||||
{
|
||||
_filename = TQString::null;
|
||||
_texture_id = texture_id;
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
KueTexture::KueTexture(const KueTexture &t)
|
||||
{
|
||||
// Is the texture file backed?
|
||||
if (t._filename.isNull())
|
||||
{
|
||||
// This is easy, copy over the texture id
|
||||
_texture_id = t._texture_id;
|
||||
_loaded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Yes, copy over the filename
|
||||
_filename = t._filename;
|
||||
_loaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
KueTexture KueTexture::null() {
|
||||
return KueTexture(0);
|
||||
}
|
||||
|
||||
KueTexture::~KueTexture()
|
||||
{
|
||||
// We only "own" the texture ID if we were created from a filename
|
||||
// Also check that we've allocated a valid texture ID. That means
|
||||
// that the texture is loaded, and it's non-NULL.
|
||||
// We don't use isNull(), because that forces a file load
|
||||
if (_loaded && _texture_id && (!_filename.isNull()))
|
||||
{
|
||||
// Free a texture ID and its associated texture
|
||||
glDeleteTextures(1, &_texture_id);
|
||||
}
|
||||
}
|
||||
|
||||
bool KueTexture::isNull()
|
||||
{
|
||||
load();
|
||||
|
||||
return (_texture_id == 0);
|
||||
}
|
||||
|
||||
void KueTexture::load()
|
||||
{
|
||||
if (_loaded)
|
||||
{
|
||||
// The texture is already loaded, nothing to do here
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the full pathname for the texture
|
||||
TQImage raw_image, gl_image;
|
||||
TQString fullname;
|
||||
|
||||
// Find the real filename
|
||||
fullname = TDEGlobal::dirs()->findResource("appdata", "textures/" + _filename + ".png");
|
||||
|
||||
// Try to load the file
|
||||
if (raw_image.load(fullname))
|
||||
{
|
||||
gl_image = TQGLWidget::convertToGLFormat(raw_image);
|
||||
|
||||
// Ask OpenGL for a new texture ID
|
||||
glGenTextures(1, &_texture_id);
|
||||
|
||||
// Make it the current texture (blank right now)
|
||||
glBindTexture(GL_TEXTURE_2D, _texture_id);
|
||||
|
||||
// Should we filter textures?
|
||||
TDEGlobal::config()->setGroup("Graphics");
|
||||
if (TDEGlobal::config()->readBoolEntry("Filter Textures", false))
|
||||
{
|
||||
// Yes, enable smooth scaling
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, enable fast scaling
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
}
|
||||
|
||||
// Load the image data in to the texture
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, 3, gl_image.width(), gl_image.height(), 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, gl_image.bits());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unable to load image, use null texture
|
||||
_texture_id = 0;
|
||||
}
|
||||
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
bool KueTexture::makeCurrent()
|
||||
{
|
||||
load();
|
||||
|
||||
// Sets the current 2D texture, where 0 means no texture
|
||||
glBindTexture(GL_TEXTURE_2D, _texture_id);
|
||||
|
||||
return true;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
#ifndef _TEXTURE_H
|
||||
#define _TEXTURE_H
|
||||
|
||||
#include <tqstring.h>
|
||||
|
||||
class KueTexture {
|
||||
public:
|
||||
KueTexture(const TQString &filename);
|
||||
KueTexture(unsigned int texture_id);
|
||||
KueTexture(const KueTexture &);
|
||||
~KueTexture();
|
||||
|
||||
bool makeCurrent();
|
||||
|
||||
// Is this a null texture?
|
||||
bool isNull();
|
||||
// The null texture
|
||||
static KueTexture null();
|
||||
|
||||
protected:
|
||||
// Loads the texture immediately
|
||||
void load();
|
||||
|
||||
// The filename of the texture
|
||||
// Will be a null string for textures created using the texture_id
|
||||
// version of the constructor
|
||||
TQString _filename;
|
||||
|
||||
// The texture ID for the texture
|
||||
// Undefined until a texture is loaded, 0 for the null texture
|
||||
unsigned int _texture_id;
|
||||
|
||||
// Stores if the texture is currently loaded or not
|
||||
// This is required to support loading file-backed textures on demand
|
||||
bool _loaded;
|
||||
};
|
||||
|
||||
#endif // _TEXTURE_H
|
After Width: | Height: | Size: 958 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1022 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 527 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 825 B |
@ -0,0 +1,190 @@
|
||||
#include <kdebug.h>
|
||||
#include <krandomsequence.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "physics.h"
|
||||
#include "main.h"
|
||||
#include "utility.h"
|
||||
#include "global.h"
|
||||
|
||||
const double PLAY_AREA_WIDTH = 0.254;
|
||||
const double PLAY_AREA_HEIGHT = 0.127;
|
||||
|
||||
void rackTriangle()
|
||||
{
|
||||
int next_ball = 0;
|
||||
unsigned int rack_order[] = { 1, 2, 3, 4, 8, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15 };
|
||||
|
||||
double field_width = KueGlobal::physics()->fieldWidth();
|
||||
double field_height = KueGlobal::physics()->fieldHeight();
|
||||
|
||||
// The initial spacing of the billiards
|
||||
const double initial_spacing = (0.00286 * 2.0);
|
||||
|
||||
// The location of the cue and rack lines
|
||||
double rack_line = (field_width * 3.0) / 4.0;
|
||||
|
||||
// The location of the mid line
|
||||
double mid_line = field_height / 2.0;
|
||||
|
||||
KRandomSequence r;
|
||||
|
||||
for (int x = 0;x < 15;x++)
|
||||
{
|
||||
// The eight-ball must stay where it is
|
||||
if (rack_order[x] != 8)
|
||||
{
|
||||
int swap_index;
|
||||
|
||||
swap_index = r.getLong(14);
|
||||
|
||||
if (rack_order[swap_index] != 8) {
|
||||
int temp = rack_order[x];
|
||||
|
||||
rack_order[x] = rack_order[swap_index];
|
||||
rack_order[swap_index] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// These loops build a triangle out of the billiards
|
||||
for (int row = 0;row < 5;row++)
|
||||
{
|
||||
double row_start = mid_line - ((row / 2.0) * initial_spacing);
|
||||
|
||||
for (int pos = 0;pos <= row;pos++)
|
||||
{
|
||||
// Calculate its position
|
||||
double x = rack_line + (row * initial_spacing);
|
||||
double y = row_start + (pos * initial_spacing);
|
||||
|
||||
// Create the billiard
|
||||
KueBilliard billiard(x, y, KueUtility::defaultBilliardRadius(), KueUtility::textureForBilliard(rack_order[next_ball]));
|
||||
|
||||
// Actually place it
|
||||
KueGlobal::physics()->insertBilliard(rack_order[next_ball], billiard);
|
||||
next_ball++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rackDiamond()
|
||||
{
|
||||
int next_ball = 0;
|
||||
unsigned int rack_order[] = { 1, 2, 3, 4, 8, 5, 6, 7, 9 };
|
||||
|
||||
double field_width = KueGlobal::physics()->fieldWidth();
|
||||
double field_height = KueGlobal::physics()->fieldHeight();
|
||||
|
||||
// The initial spacing of the billiards
|
||||
const double initial_spacing = (0.00286 * 2.0);
|
||||
// The location of the cue and rack lines
|
||||
double rack_line = (field_width * 3.0) / 4.0;
|
||||
// The location of the mid line
|
||||
double mid_line = field_height / 2.0;
|
||||
|
||||
KRandomSequence r;
|
||||
|
||||
// Randomize the billiard order of billiards [1] -> [7]
|
||||
for (int x = 1;x < 8;x++)
|
||||
{
|
||||
// The the value of another billiard in the same range
|
||||
int swap_index = r.getLong(6) + 1;
|
||||
|
||||
int temp = rack_order[x];
|
||||
rack_order[x] = rack_order[swap_index];
|
||||
rack_order[swap_index] = temp;
|
||||
}
|
||||
|
||||
// These loops build a triangle out of the billiards
|
||||
for (int row = 0;row < 5;row++) {
|
||||
// Number of billiards on this row
|
||||
int row_count = 3 - abs(row - 2);
|
||||
double row_start = mid_line - ((row_count / 2.0) * initial_spacing);
|
||||
|
||||
for (int pos = 0;pos < row_count;pos++)
|
||||
{
|
||||
// Calculate its position
|
||||
double x = rack_line + (row * initial_spacing);
|
||||
double y = row_start + (pos * initial_spacing);
|
||||
|
||||
// Create the billiard
|
||||
KueBilliard billiard(x, y, KueUtility::defaultBilliardRadius(), TQString::number(rack_order[next_ball]));
|
||||
|
||||
// Actually place it
|
||||
KueGlobal::physics()->insertBilliard(rack_order[next_ball], billiard);
|
||||
next_ball++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KueUtility::layoutTable() {
|
||||
KueGlobal::physics()->setFieldWidth(PLAY_AREA_WIDTH);
|
||||
KueGlobal::physics()->setFieldHeight(PLAY_AREA_HEIGHT);
|
||||
}
|
||||
|
||||
void KueUtility::layoutPockets()
|
||||
{
|
||||
double field_width = KueGlobal::physics()->fieldWidth();
|
||||
double field_height = KueGlobal::physics()->fieldHeight();
|
||||
double radius = KueUtility::defaultPocketRadius();
|
||||
|
||||
// Place the pockets in the four corners
|
||||
KueGlobal::physics()->insertPocket(0, KuePocket(0.0, 0.0, radius));
|
||||
KueGlobal::physics()->insertPocket(1, KuePocket(field_width / 2.0, 0.0, radius));
|
||||
KueGlobal::physics()->insertPocket(2, KuePocket(field_width, 0.0, radius));
|
||||
|
||||
KueGlobal::physics()->insertPocket(3, KuePocket(0.0, field_height, radius));
|
||||
KueGlobal::physics()->insertPocket(4, KuePocket(field_width / 2.0, field_height, radius));
|
||||
KueGlobal::physics()->insertPocket(5, KuePocket(field_width, field_height, radius));
|
||||
}
|
||||
|
||||
void KueUtility::layoutBilliards(rackType rack_type)
|
||||
{
|
||||
if (rack_type == Triangle)
|
||||
{
|
||||
rackTriangle();
|
||||
}
|
||||
else if (rack_type == Diamond)
|
||||
{
|
||||
rackDiamond();
|
||||
}
|
||||
else if (rack_type == None)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
kdWarning() << "Unknown rack type, no racking done" << endl;
|
||||
}
|
||||
|
||||
// Place the cue ball
|
||||
KueBilliard cue(KueGlobal::physics()->fieldWidth() / 4.0, KueGlobal::physics()->fieldHeight() / 2.0, KueUtility::defaultBilliardRadius());
|
||||
KueGlobal::physics()->insertBilliard(0, cue);
|
||||
}
|
||||
|
||||
KueTexture KueUtility::textureForBilliard(unsigned int index)
|
||||
{
|
||||
if (index)
|
||||
{
|
||||
return KueTexture(TQString::number(index));
|
||||
}
|
||||
else
|
||||
{
|
||||
return KueTexture::null();
|
||||
}
|
||||
}
|
||||
|
||||
// Regulation radius of billiards, in meters
|
||||
double KueUtility::defaultBilliardRadius()
|
||||
{
|
||||
return 0.00286;
|
||||
}
|
||||
|
||||
// Regulation radius of pockets, in meters
|
||||
double KueUtility::defaultPocketRadius()
|
||||
{
|
||||
return 0.006;
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
#ifndef _UTILITY_H
|
||||
#define _UTILITY_H
|
||||
|
||||
#include "texture.h"
|
||||
|
||||
// Helper functions for rules implementations
|
||||
namespace KueUtility {
|
||||
enum rackType {None, Triangle, Diamond};
|
||||
|
||||
// Regulation table layout
|
||||
void layoutTable();
|
||||
// Regulation pocket layout
|
||||
void layoutPockets();
|
||||
|
||||
// Lays out the billiard in either 8-ball or 9-ball style, or just
|
||||
// place the cue ball (with rack_type = None)
|
||||
// Billiard 0 becomes the cue ball, and the rest of the billiards
|
||||
// are indexed according to their face number
|
||||
void layoutBilliards(rackType rack_type = None);
|
||||
|
||||
// The texture for a given billiard index
|
||||
KueTexture textureForBilliard(unsigned int index);
|
||||
|
||||
// Regulation radius of billiards, in meters
|
||||
double defaultBilliardRadius();
|
||||
|
||||
// Regulation radius of pockets, in meters
|
||||
double defaultPocketRadius();
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,85 @@
|
||||
#include "vector.h"
|
||||
|
||||
// Creates a vector with between two points
|
||||
vector::vector(const point &source, const point &dest) {
|
||||
_magnitude = source.distance(dest);
|
||||
_direction = source.angle(dest);
|
||||
}
|
||||
|
||||
// Creates an empty vector
|
||||
vector::vector() {
|
||||
_magnitude = 0.0;
|
||||
_direction = 0.0;
|
||||
}
|
||||
|
||||
// Copy another vector object
|
||||
vector::vector(const vector& v) {
|
||||
_magnitude = v._magnitude;
|
||||
_direction = v._direction;
|
||||
}
|
||||
|
||||
// Set the X component
|
||||
void vector::setComponentX(double x) {
|
||||
setComponents(x, componentY());
|
||||
}
|
||||
|
||||
// Set the Y component
|
||||
void vector::setComponentY(double y) {
|
||||
setComponents(componentX(), y);
|
||||
}
|
||||
|
||||
// Operations with another vector performs vector math
|
||||
vector vector::operator+(const vector& v) {
|
||||
double x = componentX() + v.componentX();
|
||||
double y = componentY() + v.componentY();
|
||||
|
||||
return vector(sqrt((x * x) + (y * y)), atan2(y, x));
|
||||
}
|
||||
|
||||
vector vector::operator-(const vector& v) {
|
||||
double x = componentX() - v.componentX();
|
||||
double y = componentY() - v.componentY();
|
||||
|
||||
return vector(sqrt((x * x) + (y * y)), atan2(y, x));
|
||||
}
|
||||
|
||||
vector& vector::operator+=(const vector& v) {
|
||||
setComponents(componentX() + v.componentX(), componentY() + v.componentY());
|
||||
return *this;
|
||||
}
|
||||
|
||||
vector& vector::operator-=(const vector& v) {
|
||||
setComponents(componentX() - v.componentX(), componentY() - v.componentY());
|
||||
return *this;
|
||||
}
|
||||
|
||||
double vector::operator*(const vector& v) {
|
||||
return ((componentX() * v.componentX()) + (componentY() * v.componentY()));
|
||||
}
|
||||
|
||||
// Operations with a single double value affects the magnitude
|
||||
vector& vector::operator+= (double m) {
|
||||
_magnitude += m;
|
||||
return *this;
|
||||
}
|
||||
|
||||
vector& vector::operator-= (double m) {
|
||||
_magnitude -= m;
|
||||
return *this;
|
||||
}
|
||||
|
||||
vector& vector::operator*= (double m) {
|
||||
_magnitude *= m;
|
||||
return *this;
|
||||
}
|
||||
|
||||
vector& vector::operator/= (double m) {
|
||||
_magnitude /= m;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Sets both components at once (the only way to do it efficently)
|
||||
void vector::setComponents(double x, double y) {
|
||||
_direction = atan2(y, x);
|
||||
_magnitude = sqrt((x * x) + (y * y));
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
#ifndef _VECTOR_H
|
||||
#define _VECTOR_H
|
||||
|
||||
#include <math.h>
|
||||
#include "point.h"
|
||||
|
||||
// Implements a vector in 2D
|
||||
class vector {
|
||||
public:
|
||||
// Normal constructors
|
||||
vector(double magnitude, double direction) { _magnitude = magnitude; _direction = direction; }
|
||||
vector(const point& source, const point& dest);
|
||||
vector();
|
||||
|
||||
// Copy constructor
|
||||
vector(const vector&);
|
||||
|
||||
// Accessors, sorta
|
||||
double componentX() const { return (_magnitude * cos(_direction)); };
|
||||
double componentY() const { return (_magnitude * sin(_direction)); };
|
||||
|
||||
// Sets individual components
|
||||
// Wrappers around setComponents(double, double) - below
|
||||
void setComponentX(double x);
|
||||
void setComponentY(double y);
|
||||
|
||||
// Sets both components at once
|
||||
void setComponents(double x, double y);
|
||||
|
||||
// Accessors
|
||||
double magnitude() const { return _magnitude; }
|
||||
double direction() const { return _direction; }
|
||||
void setMagnitude(double m) { _magnitude = m; }
|
||||
void setDirection(double d) { _direction = d; }
|
||||
|
||||
// Vector math
|
||||
vector operator+(const vector&);
|
||||
vector operator-(const vector&);
|
||||
|
||||
vector& operator+=(const vector&);
|
||||
vector& operator-=(const vector&);
|
||||
|
||||
// Dot product
|
||||
double operator*(const vector&);
|
||||
|
||||
// Magnitude math
|
||||
vector operator+(double m) { return vector(_magnitude + m, _direction); }
|
||||
vector operator-(double m) { return vector(_magnitude - m, _direction); }
|
||||
vector operator*(double m) { return vector(_magnitude * m, _direction); }
|
||||
vector operator/(double m) { return vector(_magnitude / m, _direction); }
|
||||
|
||||
vector& operator+=(double m);
|
||||
vector& operator-=(double m);
|
||||
vector& operator*=(double m);
|
||||
vector& operator/=(double m);
|
||||
|
||||
// Return the vector's equalivent on the unit circle
|
||||
vector unit() const { return vector(1.0, _direction); }
|
||||
|
||||
protected:
|
||||
double _magnitude;
|
||||
double _direction;
|
||||
};
|
||||
|
||||
#endif
|