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_MAX_PLAYERS = 4;
13 .int pong_score; // (minigame_player) number of goals
14 .entity pong_paddles[PONG_MAX_PLAYERS];// (minigame) paddles
15 .float pong_length; // (pong_paddle) size (0,1)
19 float autocvar_sv_minigames_pong_paddlesize = 0.3;
20 float autocvar_sv_minigames_pong_paddlespeed= 1;
21 float autocvar_sv_minigames_pong_ballwait = 1;
22 float autocvar_sv_minigames_pong_ballspeed = 1;
24 void pong_ball_think();
26 // Throws a ball in a random direction and sets the think function
27 void pong_ball_throw(entity ball)
31 angle = random()*M_PI*2;
32 while ( (angle > 0.44*M_PI && angle < 0.55*M_PI) ||
33 (angle > 1.44*M_PI && angle < 1.55*M_PI) );
34 ball.velocity_x = cos(angle)*autocvar_sv_minigames_pong_ballspeed;
35 ball.velocity_y = sin(angle)*autocvar_sv_minigames_pong_ballspeed;
36 ball.think = pong_ball_think;
37 ball.nextthink = time;
39 ball.SendFlags |= MINIG_SF_UPDATE|PONG_SF_BALLTEAM;
42 // Think equivalent of pong_ball_throw, used to delay throws
43 void pong_ball_throwthink()
45 pong_ball_throw(self);
48 // Moves ball to the center and stops its motion
49 void pong_ball_reset(entity ball)
51 ball.velocity = '0 0 0';
52 ball.origin = '0.5 0.5 0';
53 ball.SendFlags |= MINIG_SF_UPDATE;
54 ball.think = SUB_NullThink;
56 ball.SendFlags |= PONG_SF_BALLTEAM;
59 // Add the score to the given team in the minigame
60 void pong_add_score(entity minigame, int team_thrower, int team_receiver, int delta)
65 if ( team_thrower == 0 )
66 team_thrower = team_receiver;
68 if ( team_thrower == team_receiver )
71 entity paddle_thrower = minigame.pong_paddles[team_thrower-1];
72 if ( paddle_thrower.realowner )
74 paddle_thrower.realowner.minigame_players.pong_score += delta;
75 paddle_thrower.realowner.minigame_players.SendFlags |= PONG_SF_PLAYERSCORE;
79 bool pong_paddlemiss(float ball_coord, float pad_coord, float pad_len)
81 return ball_coord < pad_coord - pad_len/2 || ball_coord > pad_coord + pad_len/2;
84 // Checks for a goal, when that happes adds scores and resets the ball
85 bool pong_goal(entity ball, int pteam)
87 entity paddle = ball.owner.pong_paddles[pteam-1];
91 if ( (pteam > 2 && pong_paddlemiss(ball.origin_x,paddle.origin_x,paddle.pong_length)) ||
92 pong_paddlemiss(ball.origin_y,paddle.origin_y,paddle.pong_length) )
94 pong_add_score(ball.owner ,ball.team, pteam, 1);
95 pong_ball_reset(ball);
96 ball.think = pong_ball_throwthink;
97 ball.nextthink = time + autocvar_sv_minigames_pong_ballwait;
103 ball.SendFlags |= PONG_SF_BALLTEAM;
109 // Moves the ball around
110 void pong_ball_think()
112 float think_speed = autocvar_sys_ticrate;
113 self.nextthink = time + think_speed;
115 self.origin_x += self.velocity_x * think_speed;
116 self.origin_y += self.velocity_y * think_speed;
118 // TODO consider the ball's radius (and add cvar for that)
119 if ( self.origin_y <= 0 )
121 if ( !pong_goal(self,3) )
124 self.velocity_y *= -1;
127 else if ( self.origin_y >= 1 )
129 if ( !pong_goal(self,4) )
132 self.velocity_y *= -1;
136 if ( self.origin_x <= 0 )
138 if ( !pong_goal(self,2) )
141 self.velocity_x *= -1;
144 else if ( self.origin_x >= 1 )
146 if ( !pong_goal(self,1) )
149 self.velocity_x *= -1;
153 self.SendFlags |= MINIG_SF_UPDATE;
157 void pong_paddle_think()
159 float think_speed = autocvar_sys_ticrate;
160 self.nextthink = time + think_speed;
162 if ( self.realowner.movement.x > 0 && self.origin_y > self.pong_length/2 )
164 self.origin_y -= autocvar_sv_minigames_pong_paddlespeed * think_speed;
165 if ( self.origin_y < 0 )
167 self.SendFlags |= MINIG_SF_UPDATE;
169 else if ( self.realowner.movement.x < 0 && self.origin_y < 1-self.pong_length/2 )
171 self.origin_y += autocvar_sv_minigames_pong_paddlespeed * think_speed;
172 if ( self.origin_y > 1 )
174 self.SendFlags |= MINIG_SF_UPDATE;
178 vector pong_team_to_paddlepos(int nteam)
182 case 1: return '0.99 0.5 0';
183 case 2: return '0.01 0.5 0';
184 case 3: return '0.5 0.01 0';
185 case 4: return '0.5 0.99 0';
186 default:return '0 0 0';
190 // required function, handle server side events
191 int pong_server_event(entity minigame, string event, ...)
197 minigame.minigame_flags |= PONG_STATUS_WAIT;
205 int pl_num = minigame_count_players(minigame);
207 // Don't allow joining a match that is already running
208 if ( minigame.minigame_flags & PONG_STATUS_PLAY )
211 // Don't allow any more players
212 if(pl_num >= PONG_MAX_PLAYERS)
216 // Get the right team
217 if(minigame.minigame_players)
218 pl_team = minigame_next_team(minigame.minigame_players.team, PONG_MAX_PLAYERS);
221 entity player = ...(0,entity);
222 entity paddle = msle_spawn(minigame,"pong_paddle");// Note puddle isn't a typo
223 paddle.pong_length = autocvar_sv_minigames_pong_paddlesize;
224 paddle.origin = pong_team_to_paddlepos(pl_team);
225 paddle.think = pong_paddle_think;
226 paddle.nextthink = time;
227 paddle.team = pl_team;
228 paddle.realowner = player;
229 minigame.pong_paddles[pl_team-1] = paddle;
235 // TODO remove paddle or switch to AI
241 if ( minigame.minigame_flags & PONG_STATUS_WAIT )
243 minigame.minigame_flags = PONG_STATUS_PLAY |
244 (minigame.minigame_flags & ~PONG_STATUS_WAIT);
245 minigame.SendFlags |= MINIG_SF_UPDATE;
247 entity ball = msle_spawn(minigame,"pong_ball");
248 pong_ball_reset(ball);
249 pong_ball_throw(ball);
258 entity sent = ...(0,entity);
260 if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
262 WriteLong(MSG_ENTITY,sent.pong_score);
273 #include "waypointsprites.qh" // drawrotpic
275 float pong_team_to_angle(int nteam)
282 case 3: return M_PI*3/2;
283 case 4: return M_PI/2;
287 vector pong_team_to_color(int nteam)
291 case 1: return '1 0 0';
292 case 2: return '0 0 1';
293 case 3: return '1 1 0';
294 case 4: return '1 0 1';
295 default:return '1 1 1';
299 // Required function, draw the game board
300 void pong_hud_board(vector pos, vector mySize)
302 minigame_hud_fitsqare(pos, mySize);
303 minigame_hud_simpleboard(pos,mySize,minigame_texture("pong/board"));
307 vector ball_size = minigame_hud_denormalize_size('1 1 0' / 16,pos,mySize);
309 FOREACH_MINIGAME_ENTITY(e)
311 if ( e.classname == "pong_ball" )
313 obj_pos = minigame_hud_denormalize(e.origin,pos,mySize);
314 minigame_drawpic_centered( obj_pos, minigame_texture("pong/ball"),
315 ball_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
317 minigame_drawpic_centered( obj_pos, minigame_texture("pong/ball"),
318 ball_size, pong_team_to_color(e.team),
319 panel_fg_alpha, DRAWFLAG_ADDITIVE );
321 else if ( e.classname == "pong_paddle" )
323 obj_pos = minigame_hud_denormalize(e.origin,pos,mySize);
324 paddle_size = minigame_hud_denormalize_size(eX / 16 + eY*e.pong_length,pos,mySize);
326 drawrotpic(obj_pos, pong_team_to_angle(e.team), minigame_texture("pong/paddle-glow"),
327 paddle_size, paddle_size/2, pong_team_to_color(e.team),
328 panel_fg_alpha, DRAWFLAG_ADDITIVE );
330 drawrotpic(obj_pos, pong_team_to_angle(e.team), minigame_texture("pong/paddle"),
331 paddle_size, paddle_size/2, '1 1 1',
332 panel_fg_alpha, DRAWFLAG_NORMAL );
339 // Required function, draw the game status panel
340 void pong_hud_status(vector pos, vector mySize)
344 ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
345 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
350 vector player_fontsize = hud_fontsize * 1.75;
351 ts_y = ( mySize_y - PONG_MAX_PLAYERS*player_fontsize_y ) / PONG_MAX_PLAYERS;
354 vector tile_size = '48 48 0';
357 FOREACH_MINIGAME_ENTITY(e)
359 if ( e.classname == "minigame_player" )
361 // TODO show the team color
363 mypos_y += (e.team-1) * (player_fontsize_y + ts_y);
365 drawfill(mypos, eX*mySize_x+eY*ts_y, pong_team_to_color(e.team),
366 0.25, DRAWFLAG_ADDITIVE);
368 minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
369 (e.minigame_playerslot ? GetPlayerName(e.minigame_playerslot-1) : _("AI")),
370 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
372 mypos_y += player_fontsize_y;
374 drawstring(mypos,ftos(e.pong_score),tile_size,
375 '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
380 // convert minigame flags to a message
381 string pong_message(int mgflags)
383 string rmessage = "";
384 if (mgflags & PONG_STATUS_WAIT)
385 rmessage = _("Press \"Throw Ball\" to start the match with the current players");
389 // Required function, handle client events
390 int pong_client_event(entity minigame, string event, ...)
397 switch ( ...(0,int) )
407 case "network_receive":
409 entity sent = ...(0,entity);
411 if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
413 sent.pong_score = ReadLong();
415 else if ( sent.classname == "minigame" )
417 if ( sf & MINIG_SF_UPDATE )
419 sent.message = pong_message(sent.minigame_flags);
426 HUD_MinigameMenu_CustomEntry(...(0,entity),_("Throw Ball"),"pong_throw");
431 string cmd = ...(0,string);
432 if( cmd == "pong_throw" && minigame.minigame_flags & PONG_STATUS_WAIT )
434 minigame_cmd("throw");