--- /dev/null
+
+#define DEBUG_ENABLE 0
+#define FPS 30
+#define COLOR_BG 50 50 50 255
+#define COLOR_FG 222 222 222 255
+#define PAD_SIZE 60
+#define PAD_DISTANCE 20
+#define BALL_SPEED 5.0
+#define BALL_SPEEDUP 0.1
+#define BALL_SIZE 10
+
+#if DEBUG_ENABLE
+#define DBG(...) { \
+ new _buf[128]; \
+ snprintf(_buf, sizeof(_buf), __VA_ARGS__); \
+ set_text(PART:"dbg", _buf); }
+#else
+#define DBG(...)
+#endif
+
+collections {
+ group { name: "main";
+ min: 500 300;
+ script {
+ public ballx, bally;
+ public ballspeedx, ballspeedy;
+ public player_score, ai_score;
+
+ public game_reset() {
+ set_int(ai_score, 0);
+ set_int(player_score, 0);
+ set_text(PART:"score1", "0");
+ set_text(PART:"score2", "0");
+ game_init();
+ }
+
+ public game_init() {
+ set_float(ballspeedx, 0.0);
+ set_float(ballspeedy, 0.0);
+ set_float(ballx, 100);
+ set_float(bally, 100);
+ }
+
+ public start_game() {
+ set_float(ballspeedx, BALL_SPEED);
+ set_float(ballspeedy, BALL_SPEED * randf());
+ }
+
+ public player_wins() {
+ new buf[16]
+ set_int(player_score, get_int(player_score) + 1);
+ snprintf(buf, sizeof(buf), "%d", get_int(player_score));
+ set_text(PART:"score1", buf);
+ game_init();
+ }
+
+ public ai_wins() {
+ new buf[16]
+ set_int(ai_score, get_int(ai_score) + 1);
+ snprintf(buf, sizeof(buf), "%d", get_int(ai_score));
+ set_text(PART:"score2", buf);
+ game_init();
+ }
+
+ public game_loop(count) {
+ count++;
+
+ /* get field geometry */
+ new fx, fy, fw, fh;
+ get_geometry(PART:"bg", fx, fy, fw, fh);
+
+ /* get mouse coords */
+ new mx, my;
+ get_mouse(mx, my);
+
+ /* get the ball info */
+ new Float:bx = get_float(ballx);
+ new Float:by = get_float(bally);
+ new Float:speedx = get_float(ballspeedx);
+ new Float:speedy = get_float(ballspeedy);
+
+ /* move the player pad */
+ new pady = my - PAD_SIZE / 2;
+ if (pady < 0) pady = 0;
+ else if (pady + PAD_SIZE > fh) pady = fh - PAD_SIZE;
+ custom_state(PART:"pad1", "default", 0.0);
+ set_state_val(PART:"pad1", STATE_REL1_OFFSET, 20, pady);
+ set_state(PART:"pad1", "custom", 0.0);
+
+ /* move the AI pad */
+ new pad2y = round(by) - PAD_SIZE / 2 - BALL_SIZE / 2;
+ if (pad2y < 0) pad2y = 0;
+ else if (pad2y + PAD_SIZE > fh) pad2y = fh - PAD_SIZE;
+ custom_state(PART:"pad2", "default", 0.0);
+ set_state_val(PART:"pad2", STATE_REL1_OFFSET, 20, pad2y);
+ set_state(PART:"pad2", "custom", 0.0);
+
+ /* calc new ball position */
+ bx += speedx;
+ by += speedy;
+
+ /* check walls collision */
+ if (by < 0)
+ {
+ speedy = -speedy;
+ by = 0;
+ }
+ else if (by + BALL_SIZE > fh)
+ {
+ speedy = -speedy;
+ by = fh - BALL_SIZE - 1;
+ }
+
+ /* check player pad collision */
+ if ((speedx < 0) &&
+ (bx < PAD_DISTANCE + 10) && (bx > 0) &&
+ (by + BALL_SIZE > pady) && (by < pady + PAD_SIZE))
+ {
+ new Float:dy = by - pady - PAD_SIZE / 2;
+ speedy += dy / 10;
+ speedx = -speedx + BALL_SPEEDUP;
+ }
+
+ /* check AI pad collision */
+ else if ((bx + BALL_SIZE > fw - PAD_DISTANCE - 10) &&
+ (bx + BALL_SIZE < fw) &&
+ (by + BALL_SIZE > pad2y) && (by < pad2y + PAD_SIZE))
+ {
+ new Float:dy = by - pad2y - PAD_SIZE / 2;
+ speedy += dy / 10;
+ speedx = -speedx - BALL_SPEEDUP;
+ }
+
+ /* apply the new ball position */
+ custom_state(PART:"ball", "default", 0.0);
+ set_state_val(PART:"ball", STATE_REL1_OFFSET, round(bx), round(by));
+ set_state(PART:"ball", "custom", 0.0);
+
+ /* update global vars */
+ set_float(ballx, bx);
+ set_float(bally, by);
+ set_float(ballspeedx, speedx);
+ set_float(ballspeedy, speedy);
+
+ /* AI score a point */
+ if (bx < 0) ai_wins();
+ /* player score a point */
+ if (bx + BALL_SIZE > fw) player_wins();
+
+ /* show debug info (if debug enabled) */
+ DBG("loop:%d [speed %f %f] [mouse: %d %d] [ball: %f %f]",
+ count, speedx, speedy, mx, my, bx, by)
+
+ /* recall the loop in n seconds */
+ timer(1.0 / FPS, "game_loop", count);
+ }
+ }
+ parts {
+ part { name: "bg";
+ type: RECT;
+ description { state: "default" 0.0;
+ color: COLOR_BG;
+ }
+ }
+ part { name: "net";
+ type: RECT;
+ description { state: "default" 0.0;
+ color: COLOR_FG;
+ max: 10 9999;
+ }
+ }
+ part { name: "score1";
+ type: TEXT;
+ description { state: "default" 0.0;
+ color: COLOR_FG;
+ rel2.relative: 0.5 0.5;
+ rel2.offset: -20 0;
+ text {
+ text: "0";
+ font: "Sans";
+ size: 50;
+ align: 1.0 0.0;
+ }
+ }
+ }
+ part { name: "score2";
+ type: TEXT;
+ description { state: "default" 0.0;
+ color: COLOR_FG;
+ rel1.relative: 0.5 0.0;
+ rel1.offset: 20 0;
+ text {
+ text: "0";
+ font: "Sans";
+ size: 50;
+ align: 0.0 0.0;
+ }
+ }
+ }
+ part { name: "pad1";
+ type: RECT;
+ description { state: "default" 0.0;
+ color: COLOR_FG;
+ max: 10 PAD_SIZE;
+ fixed: 1 1;
+ align: 0.0 0.0;
+ rel1.offset: PAD_DISTANCE 0;
+ }
+ }
+ part { name: "pad2";
+ type: RECT;
+ description { state: "default" 0.0;
+ color: COLOR_FG;
+ max: 10 PAD_SIZE;
+ fixed: 1 1;
+ align: 1.0 0.0;
+ rel2.offset: -PAD_DISTANCE 0;
+ }
+ }
+ part { name: "ball";
+ type: RECT;
+ description { state: "default" 0.0;
+ color: COLOR_FG;
+ max: BALL_SIZE BALL_SIZE;
+ fixed: 1 1;
+ align: 0.0 0.0;
+ rel1.offset: 100 100;
+ }
+ }
+ #if DEBUG_ENABLE
+ part { name: "dbg";
+ type: TEXT;
+ description { state: "default" 0.0;
+ color: 255 255 255 200;
+ text {
+ font: "Sans";
+ size: 12;
+ align: 1.0 1.0;
+ }
+ }
+ }
+ #endif
+ }
+ programs {
+ /* on load: reset the game and start the game loop */
+ program {
+ signal: "load";
+ source: "";
+ script {
+ game_reset();
+ timer(0.1 , "game_loop", 0);
+ }
+ }
+ /* mouse left click: start the game, if not yet started */
+ program {
+ signal: "mouse,down,1";
+ source: "bg";
+ script {
+ if (get_float(ballspeedx) == 0.0)
+ start_game();
+ }
+ }
+ /* mouse right click: restart the game*/
+ program {
+ signal: "mouse,down,3";
+ source: "bg";
+ script {
+ game_reset();
+ }
+ }
+ }
+ }
+}