summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMavridis Philippe <mavridisf@gmail.com>2024-08-07 19:13:02 +0300
committerMavridis Philippe <mavridisf@gmail.com>2024-08-07 19:23:24 +0300
commit04b5a62b8d9f5ff8240f25361046f2a5d58e8262 (patch)
tree98b126454cdf68d544e138d7e8b31d5fd45b72c2
parent83ba00b7e569587d50383ff06a70148042ca780e (diff)
downloadtdegames-feat/kue.tar.gz
tdegames-feat/kue.zip
Add Kue billiards gamefeat/kue
Signed-off-by: Mavridis Philippe <mavridisf@gmail.com>
-rw-r--r--CMakeLists.txt2
-rw-r--r--ConfigureChecks.cmake14
-rw-r--r--kue/CMakeLists.txt64
-rw-r--r--kue/ConfigureChecks.cmake22
-rw-r--r--kue/billiard.cpp87
-rw-r--r--kue/billiard.h33
-rw-r--r--kue/circle.h27
-rw-r--r--kue/config.h.cmake8
-rw-r--r--kue/cue.cpp74
-rw-r--r--kue/cue.h13
-rw-r--r--kue/disc.cpp46
-rw-r--r--kue/disc.h15
-rw-r--r--kue/global.cpp39
-rw-r--r--kue/global.h41
-rw-r--r--kue/graphics.cpp176
-rw-r--r--kue/graphics.h37
-rw-r--r--kue/hi16-app-kue.pngbin0 -> 328 bytes
-rw-r--r--kue/hi32-app-kue.pngbin0 -> 980 bytes
-rw-r--r--kue/hi48-app-kue.pngbin0 -> 1053 bytes
-rw-r--r--kue/hi64-app-kue.pngbin0 -> 1507 bytes
-rw-r--r--kue/interface.cpp319
-rw-r--r--kue/interface.h86
-rw-r--r--kue/kue.desktop52
-rw-r--r--kue/localplayer.cpp82
-rw-r--r--kue/localplayer.h40
-rw-r--r--kue/main.cpp218
-rw-r--r--kue/main.h34
-rw-r--r--kue/modules/8ball/8ball.cpp337
-rw-r--r--kue/modules/8ball/8ball.h88
-rw-r--r--kue/modules/8ball/8ball.plugin6
-rw-r--r--kue/modules/8ball/CMakeLists.txt42
-rw-r--r--kue/modules/9ball/9ball.cpp217
-rw-r--r--kue/modules/9ball/9ball.h71
-rw-r--r--kue/modules/9ball/9ball.plugin6
-rw-r--r--kue/modules/9ball/CMakeLists.txt42
-rw-r--r--kue/modules/CMakeLists.txt8
-rw-r--r--kue/modules/freeplay/CMakeLists.txt42
-rw-r--r--kue/modules/freeplay/freeplay.cpp93
-rw-r--r--kue/modules/freeplay/freeplay.h46
-rw-r--r--kue/modules/freeplay/freeplay.h.save85
-rw-r--r--kue/modules/freeplay/freeplay.plugin5
-rw-r--r--kue/newgame.cpp242
-rw-r--r--kue/newgame.h64
-rw-r--r--kue/physics.cpp189
-rw-r--r--kue/physics.h76
-rw-r--r--kue/player.h32
-rw-r--r--kue/pluginloader.cpp40
-rw-r--r--kue/pluginloader.h25
-rw-r--r--kue/pocket.h10
-rw-r--r--kue/point.cpp19
-rw-r--r--kue/point.h32
-rw-r--r--kue/rules.cpp22
-rw-r--r--kue/rules.h34
-rw-r--r--kue/sphere.cpp61
-rw-r--r--kue/sphere.h11
-rw-r--r--kue/table.cpp119
-rw-r--r--kue/table.h18
-rw-r--r--kue/team.h26
-rw-r--r--kue/texture.cpp140
-rw-r--r--kue/texture.h38
-rw-r--r--kue/textures/1.pngbin0 -> 958 bytes
-rw-r--r--kue/textures/10.pngbin0 -> 1193 bytes
-rw-r--r--kue/textures/11.pngbin0 -> 1022 bytes
-rw-r--r--kue/textures/12.pngbin0 -> 1136 bytes
-rw-r--r--kue/textures/13.pngbin0 -> 1249 bytes
-rw-r--r--kue/textures/14.pngbin0 -> 1296 bytes
-rw-r--r--kue/textures/15.pngbin0 -> 1366 bytes
-rw-r--r--kue/textures/2.pngbin0 -> 1106 bytes
-rw-r--r--kue/textures/3.pngbin0 -> 1156 bytes
-rw-r--r--kue/textures/4.pngbin0 -> 1044 bytes
-rw-r--r--kue/textures/5.pngbin0 -> 1238 bytes
-rw-r--r--kue/textures/6.pngbin0 -> 1365 bytes
-rw-r--r--kue/textures/7.pngbin0 -> 1200 bytes
-rw-r--r--kue/textures/8.pngbin0 -> 527 bytes
-rw-r--r--kue/textures/9.pngbin0 -> 1170 bytes
-rw-r--r--kue/textures/cue-player1.pngbin0 -> 26201 bytes
-rw-r--r--kue/textures/cue-player2.pngbin0 -> 25940 bytes
-rw-r--r--kue/textures/table.pngbin0 -> 825 bytes
-rw-r--r--kue/utility.cpp190
-rw-r--r--kue/utility.h31
-rw-r--r--kue/vector.cpp85
-rw-r--r--kue/vector.h65
82 files changed, 4185 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b73f9797..e7fa34de 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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 )
diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
index 765e7393..f10fb006 100644
--- a/ConfigureChecks.cmake
+++ b/ConfigureChecks.cmake
@@ -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()
diff --git a/kue/CMakeLists.txt b/kue/CMakeLists.txt
new file mode 100644
index 00000000..1033bb7d
--- /dev/null
+++ b/kue/CMakeLists.txt
@@ -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; \ No newline at end of file
diff --git a/kue/ConfigureChecks.cmake b/kue/ConfigureChecks.cmake
new file mode 100644
index 00000000..3ce5420c
--- /dev/null
+++ b/kue/ConfigureChecks.cmake
@@ -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 ) \ No newline at end of file
diff --git a/kue/billiard.cpp b/kue/billiard.cpp
new file mode 100644
index 00000000..7237d7f8
--- /dev/null
+++ b/kue/billiard.cpp
@@ -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;
+}
diff --git a/kue/billiard.h b/kue/billiard.h
new file mode 100644
index 00000000..ea345201
--- /dev/null
+++ b/kue/billiard.h
@@ -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
diff --git a/kue/circle.h b/kue/circle.h
new file mode 100644
index 00000000..d412b41d
--- /dev/null
+++ b/kue/circle.h
@@ -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
diff --git a/kue/config.h.cmake b/kue/config.h.cmake
new file mode 100644
index 00000000..1370df9e
--- /dev/null
+++ b/kue/config.h.cmake
@@ -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@ \ No newline at end of file
diff --git a/kue/cue.cpp b/kue/cue.cpp
new file mode 100644
index 00000000..561ad89a
--- /dev/null
+++ b/kue/cue.cpp
@@ -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();
+}
diff --git a/kue/cue.h b/kue/cue.h
new file mode 100644
index 00000000..d92bc7a2
--- /dev/null
+++ b/kue/cue.h
@@ -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
diff --git a/kue/disc.cpp b/kue/disc.cpp
new file mode 100644
index 00000000..2b6946e5
--- /dev/null
+++ b/kue/disc.cpp
@@ -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();
+}
diff --git a/kue/disc.h b/kue/disc.h
new file mode 100644
index 00000000..9c937d81
--- /dev/null
+++ b/kue/disc.h
@@ -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
diff --git a/kue/global.cpp b/kue/global.cpp
new file mode 100644
index 00000000..99c90eea
--- /dev/null
+++ b/kue/global.cpp
@@ -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;
+} \ No newline at end of file
diff --git a/kue/global.h b/kue/global.h
new file mode 100644
index 00000000..0c0188c2
--- /dev/null
+++ b/kue/global.h
@@ -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 \ No newline at end of file
diff --git a/kue/graphics.cpp b/kue/graphics.cpp
new file mode 100644
index 00000000..a0568aff
--- /dev/null
+++ b/kue/graphics.cpp
@@ -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);
+ }
+ }
+
+}
diff --git a/kue/graphics.h b/kue/graphics.h
new file mode 100644
index 00000000..810fe8d8
--- /dev/null
+++ b/kue/graphics.h
@@ -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
diff --git a/kue/hi16-app-kue.png b/kue/hi16-app-kue.png
new file mode 100644
index 00000000..f8f1e1c7
--- /dev/null
+++ b/kue/hi16-app-kue.png
Binary files differ
diff --git a/kue/hi32-app-kue.png b/kue/hi32-app-kue.png
new file mode 100644
index 00000000..d3638685
--- /dev/null
+++ b/kue/hi32-app-kue.png
Binary files differ
diff --git a/kue/hi48-app-kue.png b/kue/hi48-app-kue.png
new file mode 100644
index 00000000..813151a2
--- /dev/null
+++ b/kue/hi48-app-kue.png
Binary files differ
diff --git a/kue/hi64-app-kue.png b/kue/hi64-app-kue.png
new file mode 100644
index 00000000..3f9b2b03
--- /dev/null
+++ b/kue/hi64-app-kue.png
Binary files differ
diff --git a/kue/interface.cpp b/kue/interface.cpp
new file mode 100644
index 00000000..5e99a8ff
--- /dev/null
+++ b/kue/interface.cpp
@@ -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"
+
diff --git a/kue/interface.h b/kue/interface.h
new file mode 100644
index 00000000..72565d31
--- /dev/null
+++ b/kue/interface.h
@@ -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
diff --git a/kue/kue.desktop b/kue/kue.desktop
new file mode 100644
index 00000000..a4d7767b
--- /dev/null
+++ b/kue/kue.desktop
@@ -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
diff --git a/kue/localplayer.cpp b/kue/localplayer.cpp
new file mode 100644
index 00000000..51b157b3
--- /dev/null
+++ b/kue/localplayer.cpp
@@ -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"
diff --git a/kue/localplayer.h b/kue/localplayer.h
new file mode 100644
index 00000000..fa307625
--- /dev/null
+++ b/kue/localplayer.h
@@ -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
diff --git a/kue/main.cpp b/kue/main.cpp
new file mode 100644
index 00000000..f6779afe
--- /dev/null
+++ b/kue/main.cpp
@@ -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"
diff --git a/kue/main.h b/kue/main.h
new file mode 100644
index 00000000..22eea472
--- /dev/null
+++ b/kue/main.h
@@ -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
diff --git a/kue/modules/8ball/8ball.cpp b/kue/modules/8ball/8ball.cpp
new file mode 100644
index 00000000..2ac2cff8
--- /dev/null
+++ b/kue/modules/8ball/8ball.cpp
@@ -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"
diff --git a/kue/modules/8ball/8ball.h b/kue/modules/8ball/8ball.h
new file mode 100644
index 00000000..73ee3864
--- /dev/null
+++ b/kue/modules/8ball/8ball.h
@@ -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
diff --git a/kue/modules/8ball/8ball.plugin b/kue/modules/8ball/8ball.plugin
new file mode 100644
index 00000000..eb2e0669
--- /dev/null
+++ b/kue/modules/8ball/8ball.plugin
@@ -0,0 +1,6 @@
+Filename=libkue8ball
+Type=rulesengine
+Name=8-Ball
+Description=A very simple version of 8-ball pool
+MinTeams=2
+MaxTeams=2
diff --git a/kue/modules/8ball/CMakeLists.txt b/kue/modules/8ball/CMakeLists.txt
new file mode 100644
index 00000000..5009d05c
--- /dev/null
+++ b/kue/modules/8ball/CMakeLists.txt
@@ -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; \ No newline at end of file
diff --git a/kue/modules/9ball/9ball.cpp b/kue/modules/9ball/9ball.cpp
new file mode 100644
index 00000000..eafaa2a8
--- /dev/null
+++ b/kue/modules/9ball/9ball.cpp
@@ -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"
diff --git a/kue/modules/9ball/9ball.h b/kue/modules/9ball/9ball.h
new file mode 100644
index 00000000..25a1774e
--- /dev/null
+++ b/kue/modules/9ball/9ball.h
@@ -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
diff --git a/kue/modules/9ball/9ball.plugin b/kue/modules/9ball/9ball.plugin
new file mode 100644
index 00000000..8c6e0292
--- /dev/null
+++ b/kue/modules/9ball/9ball.plugin
@@ -0,0 +1,6 @@
+Filename=libkue9ball
+Type=rulesengine
+Name=9-Ball
+Description=A very simple version of 9-ball pool
+MinTeams=2
+MaxTeams=2
diff --git a/kue/modules/9ball/CMakeLists.txt b/kue/modules/9ball/CMakeLists.txt
new file mode 100644
index 00000000..8e046cdc
--- /dev/null
+++ b/kue/modules/9ball/CMakeLists.txt
@@ -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; \ No newline at end of file
diff --git a/kue/modules/CMakeLists.txt b/kue/modules/CMakeLists.txt
new file mode 100644
index 00000000..bf9d6f43
--- /dev/null
+++ b/kue/modules/CMakeLists.txt
@@ -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) \ No newline at end of file
diff --git a/kue/modules/freeplay/CMakeLists.txt b/kue/modules/freeplay/CMakeLists.txt
new file mode 100644
index 00000000..0f935a3c
--- /dev/null
+++ b/kue/modules/freeplay/CMakeLists.txt
@@ -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; \ No newline at end of file
diff --git a/kue/modules/freeplay/freeplay.cpp b/kue/modules/freeplay/freeplay.cpp
new file mode 100644
index 00000000..022c0ec9
--- /dev/null
+++ b/kue/modules/freeplay/freeplay.cpp
@@ -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"
diff --git a/kue/modules/freeplay/freeplay.h b/kue/modules/freeplay/freeplay.h
new file mode 100644
index 00000000..f51ba950
--- /dev/null
+++ b/kue/modules/freeplay/freeplay.h
@@ -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
diff --git a/kue/modules/freeplay/freeplay.h.save b/kue/modules/freeplay/freeplay.h.save
new file mode 100644
index 00000000..36b9404e
--- /dev/null
+++ b/kue/modules/freeplay/freeplay.h.save
@@ -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
diff --git a/kue/modules/freeplay/freeplay.plugin b/kue/modules/freeplay/freeplay.plugin
new file mode 100644
index 00000000..9e559e15
--- /dev/null
+++ b/kue/modules/freeplay/freeplay.plugin
@@ -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
diff --git a/kue/newgame.cpp b/kue/newgame.cpp
new file mode 100644
index 00000000..9ddf8f1b
--- /dev/null
+++ b/kue/newgame.cpp
@@ -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"
+
diff --git a/kue/newgame.h b/kue/newgame.h
new file mode 100644
index 00000000..6cc0da5b
--- /dev/null
+++ b/kue/newgame.h
@@ -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
+
diff --git a/kue/physics.cpp b/kue/physics.cpp
new file mode 100644
index 00000000..f9822f2e
--- /dev/null
+++ b/kue/physics.cpp
@@ -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"
diff --git a/kue/physics.h b/kue/physics.h
new file mode 100644
index 00000000..e1affc8b
--- /dev/null
+++ b/kue/physics.h
@@ -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
diff --git a/kue/player.h b/kue/player.h
new file mode 100644
index 00000000..ad324db1
--- /dev/null
+++ b/kue/player.h
@@ -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
diff --git a/kue/pluginloader.cpp b/kue/pluginloader.cpp
new file mode 100644
index 00000000..39a64d63
--- /dev/null
+++ b/kue/pluginloader.cpp
@@ -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;
+}
+
diff --git a/kue/pluginloader.h b/kue/pluginloader.h
new file mode 100644
index 00000000..de5e1aa0
--- /dev/null
+++ b/kue/pluginloader.h
@@ -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
diff --git a/kue/pocket.h b/kue/pocket.h
new file mode 100644
index 00000000..4e05ca6a
--- /dev/null
+++ b/kue/pocket.h
@@ -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
diff --git a/kue/point.cpp b/kue/point.cpp
new file mode 100644
index 00000000..3ee12888
--- /dev/null
+++ b/kue/point.cpp
@@ -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));
+}
diff --git a/kue/point.h b/kue/point.h
new file mode 100644
index 00000000..c748e7d3
--- /dev/null
+++ b/kue/point.h
@@ -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
diff --git a/kue/rules.cpp b/kue/rules.cpp
new file mode 100644
index 00000000..4a15ac48
--- /dev/null
+++ b/kue/rules.cpp
@@ -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"
diff --git a/kue/rules.h b/kue/rules.h
new file mode 100644
index 00000000..8b6e66aa
--- /dev/null
+++ b/kue/rules.h
@@ -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
diff --git a/kue/sphere.cpp b/kue/sphere.cpp
new file mode 100644
index 00000000..321b8bad
--- /dev/null
+++ b/kue/sphere.cpp
@@ -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();
+}
diff --git a/kue/sphere.h b/kue/sphere.h
new file mode 100644
index 00000000..f106142e
--- /dev/null
+++ b/kue/sphere.h
@@ -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
diff --git a/kue/table.cpp b/kue/table.cpp
new file mode 100644
index 00000000..5f2f44ec
--- /dev/null
+++ b/kue/table.cpp
@@ -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();
+}
diff --git a/kue/table.h b/kue/table.h
new file mode 100644
index 00000000..4604f72c
--- /dev/null
+++ b/kue/table.h
@@ -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
diff --git a/kue/team.h b/kue/team.h
new file mode 100644
index 00000000..f2b39816
--- /dev/null
+++ b/kue/team.h
@@ -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
diff --git a/kue/texture.cpp b/kue/texture.cpp
new file mode 100644
index 00000000..cf753766
--- /dev/null
+++ b/kue/texture.cpp
@@ -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;
+}
diff --git a/kue/texture.h b/kue/texture.h
new file mode 100644
index 00000000..cac39d87
--- /dev/null
+++ b/kue/texture.h
@@ -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
diff --git a/kue/textures/1.png b/kue/textures/1.png
new file mode 100644
index 00000000..154ac8ce
--- /dev/null
+++ b/kue/textures/1.png
Binary files differ
diff --git a/kue/textures/10.png b/kue/textures/10.png
new file mode 100644
index 00000000..c16340aa
--- /dev/null
+++ b/kue/textures/10.png
Binary files differ
diff --git a/kue/textures/11.png b/kue/textures/11.png
new file mode 100644
index 00000000..14feb101
--- /dev/null
+++ b/kue/textures/11.png
Binary files differ
diff --git a/kue/textures/12.png b/kue/textures/12.png
new file mode 100644
index 00000000..adc6474c
--- /dev/null
+++ b/kue/textures/12.png
Binary files differ
diff --git a/kue/textures/13.png b/kue/textures/13.png
new file mode 100644
index 00000000..a9dd9302
--- /dev/null
+++ b/kue/textures/13.png
Binary files differ
diff --git a/kue/textures/14.png b/kue/textures/14.png
new file mode 100644
index 00000000..5d2b0691
--- /dev/null
+++ b/kue/textures/14.png
Binary files differ
diff --git a/kue/textures/15.png b/kue/textures/15.png
new file mode 100644
index 00000000..50d5db95
--- /dev/null
+++ b/kue/textures/15.png
Binary files differ
diff --git a/kue/textures/2.png b/kue/textures/2.png
new file mode 100644
index 00000000..e9daf2a8
--- /dev/null
+++ b/kue/textures/2.png
Binary files differ
diff --git a/kue/textures/3.png b/kue/textures/3.png
new file mode 100644
index 00000000..6ab15fd4
--- /dev/null
+++ b/kue/textures/3.png
Binary files differ
diff --git a/kue/textures/4.png b/kue/textures/4.png
new file mode 100644
index 00000000..3a7d37b7
--- /dev/null
+++ b/kue/textures/4.png
Binary files differ
diff --git a/kue/textures/5.png b/kue/textures/5.png
new file mode 100644
index 00000000..dafba177
--- /dev/null
+++ b/kue/textures/5.png
Binary files differ
diff --git a/kue/textures/6.png b/kue/textures/6.png
new file mode 100644
index 00000000..6847b742
--- /dev/null
+++ b/kue/textures/6.png
Binary files differ
diff --git a/kue/textures/7.png b/kue/textures/7.png
new file mode 100644
index 00000000..4fb73b04
--- /dev/null
+++ b/kue/textures/7.png
Binary files differ
diff --git a/kue/textures/8.png b/kue/textures/8.png
new file mode 100644
index 00000000..1af757fe
--- /dev/null
+++ b/kue/textures/8.png
Binary files differ
diff --git a/kue/textures/9.png b/kue/textures/9.png
new file mode 100644
index 00000000..fb7f4af3
--- /dev/null
+++ b/kue/textures/9.png
Binary files differ
diff --git a/kue/textures/cue-player1.png b/kue/textures/cue-player1.png
new file mode 100644
index 00000000..df6b04c4
--- /dev/null
+++ b/kue/textures/cue-player1.png
Binary files differ
diff --git a/kue/textures/cue-player2.png b/kue/textures/cue-player2.png
new file mode 100644
index 00000000..374cf88b
--- /dev/null
+++ b/kue/textures/cue-player2.png
Binary files differ
diff --git a/kue/textures/table.png b/kue/textures/table.png
new file mode 100644
index 00000000..b878017d
--- /dev/null
+++ b/kue/textures/table.png
Binary files differ
diff --git a/kue/utility.cpp b/kue/utility.cpp
new file mode 100644
index 00000000..7565228a
--- /dev/null
+++ b/kue/utility.cpp
@@ -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;
+}
+
diff --git a/kue/utility.h b/kue/utility.h
new file mode 100644
index 00000000..56343acb
--- /dev/null
+++ b/kue/utility.h
@@ -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
diff --git a/kue/vector.cpp b/kue/vector.cpp
new file mode 100644
index 00000000..40aed057
--- /dev/null
+++ b/kue/vector.cpp
@@ -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));
+}
diff --git a/kue/vector.h b/kue/vector.h
new file mode 100644
index 00000000..f1a0947a
--- /dev/null
+++ b/kue/vector.h
@@ -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