Add Kue billiards game

Signed-off-by: Mavridis Philippe <mavridisf@gmail.com>
feat/kue
Mavridis Philippe 4 months ago
parent 83ba00b7e5
commit 04b5a62b8d
No known key found for this signature in database
GPG Key ID: F8D2D7E2F989A494

@ -95,6 +95,7 @@ option( BUILD_KTUBERLING "Build ktuberling" ${BUILD_ALL} )
option( BUILD_LSKAT "Build lskat" ${BUILD_ALL} )
option( BUILD_TWIN4 "Build twin4" ${BUILD_ALL} )
option( BUILD_TDEFIFTEEN "Build tdefifteen" ${BUILD_ALL} )
option( BUILD_KUE "Build kue" ${BUILD_ALL} )
##### configure checks
@ -186,6 +187,7 @@ tde_conditional_add_subdirectory( BUILD_KTUBERLING ktuberling )
tde_conditional_add_subdirectory( BUILD_LSKAT lskat )
tde_conditional_add_subdirectory( BUILD_TWIN4 twin4 )
tde_conditional_add_subdirectory( BUILD_TDEFIFTEEN tdefifteen )
tde_conditional_add_subdirectory( BUILD_KUE kue )
if( BUILD_KSIRTET OR BUILD_KFOULEGGS OR BUILD_KLICKETY )
add_subdirectory( libksirtet )

@ -66,6 +66,17 @@ if( WITH_ARTS )
endif( WITH_ARTS )
# Kue needs GL
if( BUILD_KUE )
check_include_file( "GL/gl.h" HAVE_GL_H )
check_include_file( "GL/glu.h" HAVE_GLU_H )
check_include_file( "GL/glx.h" HAVE_GLX_H )
if( NOT HAVE_GL_H OR NOT HAVE_GLU_H OR NOT HAVE_GLX_H)
tde_message_fatal("GL required for Kue, but not found on your system")
endif()
endif()
##### Import libtdegames
##### All these games require libtdegames
@ -100,6 +111,7 @@ if( BUILD_ATLANTIK OR
BUILD_KTRON OR
BUILD_KTUBERLING OR
BUILD_LSKAT OR
BUILD_TWIN4 )
BUILD_TWIN4 OR
BUILD_KUE )
tde_import ( libtdegames )
endif()

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 980 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1022 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

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
Loading…
Cancel
Save