#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 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) */ #if DEBUG_ENABLE new _buf[128]; snprintf(_buf, sizeof(_buf), "loop:%d [speed %f %f] [mouse: %d %d] [ball: %f %f]", count, speedx, speedy, mx, my, bx, by); set_text(PART:"dbg", _buf); #endif /* 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(); } } } } }