2 const int PONG_STATUS_WAIT = 0x0010; // waiting for players to join
3 const int PONG_STATUS_PLAY = 0x0020; // playing
6 // (minigame_player) sent when reporting scores
7 const int PONG_SF_PLAYERSCORE = MINIG_SF_CUSTOM;
8 // (pong_ball) sent when changing team
9 const int PONG_SF_BALLTEAM = MINIG_SF_CUSTOM;
12 const int PONG_KEY_INCREASE = 0x01; // Move down/right
13 const int PONG_KEY_DECREASE = 0x02; // Move up/left
14 const int PONG_KEY_BOTH = 0x03; // Player jamming keys at ramdom
17 const int PONG_MAX_PLAYERS = 4;
18 .int pong_score; // (minigame_player) number of goals
19 .int pong_keys; // (client) pressed keys
20 .entity pong_paddles[PONG_MAX_PLAYERS];// (minigame) paddles
21 .float pong_length; // (pong_paddle) size (0,1)
25 float autocvar_sv_minigames_pong_paddlesize = 0.3;
26 float autocvar_sv_minigames_pong_paddlespeed= 1;
27 float autocvar_sv_minigames_pong_ballwait = 1;
28 float autocvar_sv_minigames_pong_ballspeed = 1;
30 void pong_ball_think();
32 // Throws a ball in a random direction and sets the think function
33 void pong_ball_throw(entity ball)
37 angle = random()*M_PI*2;
38 while ( fabs(sin(angle)) < 0.17 || fabs(cos(angle)) < 0.17 );
39 ball.velocity_x = cos(angle)*autocvar_sv_minigames_pong_ballspeed;
40 ball.velocity_y = sin(angle)*autocvar_sv_minigames_pong_ballspeed;
41 ball.think = pong_ball_think;
42 ball.nextthink = time;
44 ball.SendFlags |= MINIG_SF_UPDATE|PONG_SF_BALLTEAM;
47 // Think equivalent of pong_ball_throw, used to delay throws
48 void pong_ball_throwthink()
50 pong_ball_throw(self);
53 // Moves ball to the center and stops its motion
54 void pong_ball_reset(entity ball)
56 ball.velocity = '0 0 0';
57 ball.origin = '0.5 0.5 0';
58 ball.SendFlags |= MINIG_SF_UPDATE;
59 ball.think = SUB_NullThink;
61 ball.SendFlags |= PONG_SF_BALLTEAM;
64 // Add the score to the given team in the minigame
65 void pong_add_score(entity minigame, int team_thrower, int team_receiver, int delta)
70 if ( team_thrower == 0 )
71 team_thrower = team_receiver;
73 if ( team_thrower == team_receiver )
76 entity paddle_thrower = minigame.pong_paddles[team_thrower-1];
77 if ( paddle_thrower.realowner )
79 paddle_thrower.realowner.minigame_players.pong_score += delta;
80 paddle_thrower.realowner.minigame_players.SendFlags |= PONG_SF_PLAYERSCORE;
84 bool pong_paddlemiss(float ball_coord, float pad_coord, float pad_len)
86 return ball_coord < pad_coord - pad_len/2 || ball_coord > pad_coord + pad_len/2;
89 // Checks for a goal, when that happes adds scores and resets the ball
90 bool pong_goal(entity ball, int pteam)
92 entity paddle = ball.owner.pong_paddles[pteam-1];
96 if ( (pteam > 2 && pong_paddlemiss(ball.origin_x,paddle.origin_x,paddle.pong_length)) ||
97 pong_paddlemiss(ball.origin_y,paddle.origin_y,paddle.pong_length) )
99 pong_add_score(ball.owner ,ball.team, pteam, 1);
100 pong_ball_reset(ball);
101 ball.think = pong_ball_throwthink;
102 ball.nextthink = time + autocvar_sv_minigames_pong_ballwait;
108 ball.SendFlags |= PONG_SF_BALLTEAM;
114 // Moves the ball around
115 void pong_ball_think()
117 float think_speed = autocvar_sys_ticrate;
118 self.nextthink = time + think_speed;
120 self.origin_x += self.velocity_x * think_speed;
121 self.origin_y += self.velocity_y * think_speed;
123 // TODO consider the ball's radius (and add cvar for that)
124 if ( self.origin_y <= 0 )
126 if ( !pong_goal(self,3) )
129 self.velocity_y *= -1;
132 else if ( self.origin_y >= 1 )
134 if ( !pong_goal(self,4) )
137 self.velocity_y *= -1;
141 if ( self.origin_x <= 0 )
143 if ( !pong_goal(self,2) )
146 self.velocity_x *= -1;
149 else if ( self.origin_x >= 1 )
151 if ( !pong_goal(self,1) )
154 self.velocity_x *= -1;
158 self.SendFlags |= MINIG_SF_UPDATE;
162 void pong_paddle_think()
164 float think_speed = autocvar_sys_ticrate;
165 self.nextthink = time + think_speed;
167 if ( self.realowner.minigame_players.pong_keys &&
168 self.realowner.minigame_players.pong_keys != PONG_KEY_BOTH )
170 float movement = autocvar_sv_minigames_pong_paddlespeed * think_speed;
171 float halflen = self.pong_length/2;
173 if ( self.realowner.minigame_players.pong_keys == PONG_KEY_DECREASE )
177 self.origin_x = bound(halflen, self.origin_x+movement, 1-halflen);
179 self.origin_y = bound(halflen, self.origin_y+movement, 1-halflen);
181 self.SendFlags |= MINIG_SF_UPDATE;
185 vector pong_team_to_paddlepos(int nteam)
189 case 1: return '0.99 0.5 0';
190 case 2: return '0.01 0.5 0';
191 case 3: return '0.5 0.01 0';
192 case 4: return '0.5 0.99 0';
193 default:return '0 0 0';
197 // required function, handle server side events
198 int pong_server_event(entity minigame, string event, ...)
204 minigame.minigame_flags |= PONG_STATUS_WAIT;
212 int pl_num = minigame_count_players(minigame);
214 // Don't allow joining a match that is already running
215 if ( minigame.minigame_flags & PONG_STATUS_PLAY )
218 // Don't allow any more players
219 if(pl_num >= PONG_MAX_PLAYERS)
223 // Get the right team
224 if(minigame.minigame_players)
225 pl_team = minigame_next_team(minigame.minigame_players.team, PONG_MAX_PLAYERS);
227 entity player = ...(0,entity);
228 entity paddle = msle_spawn(minigame,"pong_paddle");// Note puddle isn't a typo
229 paddle.pong_length = autocvar_sv_minigames_pong_paddlesize;
230 paddle.origin = pong_team_to_paddlepos(pl_team);
231 paddle.think = pong_paddle_think;
232 paddle.nextthink = time;
233 paddle.team = pl_team;
234 paddle.realowner = player;
235 minigame.pong_paddles[pl_team-1] = paddle;
241 // TODO remove paddle or switch to AI
245 entity player = ...(0,entity);
249 if ( minigame.minigame_flags & PONG_STATUS_WAIT )
251 minigame.minigame_flags = PONG_STATUS_PLAY |
252 (minigame.minigame_flags & ~PONG_STATUS_WAIT);
253 minigame.SendFlags |= MINIG_SF_UPDATE;
255 entity ball = msle_spawn(minigame,"pong_ball");
256 pong_ball_reset(ball);
257 pong_ball_throw(ball);
261 player.pong_keys |= PONG_KEY_INCREASE;
264 player.pong_keys |= PONG_KEY_DECREASE;
267 player.pong_keys &= ~PONG_KEY_INCREASE;
270 player.pong_keys &= ~PONG_KEY_DECREASE;
278 entity sent = ...(0,entity);
280 if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
282 WriteLong(MSG_ENTITY,sent.pong_score);
293 #include "waypointsprites.qh" // drawrotpic
295 float pong_team_to_angle(int nteam)
302 case 3: return M_PI*3/2;
303 case 4: return M_PI/2;
307 vector pong_team_to_color(int nteam)
311 case 1: return '1 0 0';
312 case 2: return '0 0 1';
313 case 3: return '1 1 0';
314 case 4: return '1 0 1';
315 default:return '1 1 1';
319 // Required function, draw the game board
320 void pong_hud_board(vector pos, vector mySize)
322 minigame_hud_fitsqare(pos, mySize);
323 minigame_hud_simpleboard(pos,mySize,minigame_texture("pong/board"));
327 vector ball_size = minigame_hud_denormalize_size('1 1 0' / 16,pos,mySize);
329 FOREACH_MINIGAME_ENTITY(e)
331 if ( e.classname == "pong_ball" )
333 obj_pos = minigame_hud_denormalize(e.origin,pos,mySize);
334 minigame_drawpic_centered( obj_pos, minigame_texture("pong/ball"),
335 ball_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
337 minigame_drawpic_centered( obj_pos, minigame_texture("pong/ball"),
338 ball_size, pong_team_to_color(e.team),
339 panel_fg_alpha, DRAWFLAG_ADDITIVE );
341 else if ( e.classname == "pong_paddle" )
343 obj_pos = minigame_hud_denormalize(e.origin,pos,mySize);
344 paddle_size = minigame_hud_denormalize_size(eX / 16 + eY*e.pong_length,pos,mySize);
346 drawrotpic(obj_pos, pong_team_to_angle(e.team), minigame_texture("pong/paddle-glow"),
347 paddle_size, paddle_size/2, pong_team_to_color(e.team),
348 panel_fg_alpha, DRAWFLAG_ADDITIVE );
350 drawrotpic(obj_pos, pong_team_to_angle(e.team), minigame_texture("pong/paddle"),
351 paddle_size, paddle_size/2, '1 1 1',
352 panel_fg_alpha, DRAWFLAG_NORMAL );
359 // Required function, draw the game status panel
360 void pong_hud_status(vector pos, vector mySize)
364 ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
365 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
370 vector player_fontsize = hud_fontsize * 1.75;
371 ts_y = ( mySize_y - PONG_MAX_PLAYERS*player_fontsize_y ) / PONG_MAX_PLAYERS;
374 vector tile_size = '48 48 0';
377 FOREACH_MINIGAME_ENTITY(e)
379 if ( e.classname == "minigame_player" )
382 mypos_y += (e.team-1) * (player_fontsize_y + ts_y);
384 drawfill(mypos, eX*mySize_x+eY*ts_y, pong_team_to_color(e.team),
385 0.25, DRAWFLAG_ADDITIVE);
387 minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
388 (e.minigame_playerslot ? GetPlayerName(e.minigame_playerslot-1) : _("AI")),
389 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
391 drawstring(mypos+eY*player_fontsize_y,ftos(e.pong_score),tile_size,
392 '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
394 if ( e == minigame_self )
396 drawborderlines(1, mypos, eX*mySize_x+eY*ts_y,
397 pong_team_to_color(e.team), 1, DRAWFLAG_NORMAL);
403 // convert minigame flags to a message
404 string pong_message(int mgflags)
406 string rmessage = "";
407 if (mgflags & PONG_STATUS_WAIT)
408 rmessage = _("Press \"Throw Ball\" to start the match with the current players");
412 // Required function, handle client events
413 int pong_client_event(entity minigame, string event, ...)
420 switch ( ...(0,int) )
426 minigame_cmd("+moved");
431 case K_KP_RIGHTARROW:
432 minigame_cmd("+movei");
437 switch ( ...(0,int) )
443 minigame_cmd("-moved");
448 case K_KP_RIGHTARROW:
449 minigame_cmd("-movei");
453 case "network_receive":
455 entity sent = ...(0,entity);
457 if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
459 sent.pong_score = ReadLong();
461 else if ( sent.classname == "minigame" )
463 if ( sf & MINIG_SF_UPDATE )
465 sent.message = pong_message(sent.minigame_flags);
472 HUD_MinigameMenu_CustomEntry(...(0,entity),_("Throw Ball"),"pong_throw");
477 string cmd = ...(0,string);
478 if( cmd == "pong_throw" && minigame.minigame_flags & PONG_STATUS_WAIT )
480 minigame_cmd("throw");