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;
10 const int PONG_MAX_PLAYERS = 2; // TODO 4
11 .int pong_score; // (minigame_player) number of goals
12 .entity pong_paddles[PONG_MAX_PLAYERS];// (minigame) paddles
13 .float pong_length; // (pong_paddle) size (0,1)
17 float autocvar_sv_minigames_pong_paddlesize = 0.3;
18 float autocvar_sv_minigames_pong_paddlespeed= 1;
19 float autocvar_sv_minigames_pong_ballwait = 1;
20 float autocvar_sv_minigames_pong_ballspeed = 1;
22 void pong_ball_think();
24 // Throws a ball in a random direction and sets the think function
25 void pong_ball_throw(entity ball)
29 angle = random()*M_PI*2;
30 while ( (angle > 0.44*M_PI && angle < 0.55*M_PI) ||
31 (angle > 1.44*M_PI && angle < 1.55*M_PI) );
32 ball.velocity_x = cos(angle)*autocvar_sv_minigames_pong_ballspeed;
33 ball.velocity_y = sin(angle)*autocvar_sv_minigames_pong_ballspeed;
34 ball.SendFlags |= MINIG_SF_UPDATE;
35 ball.think = pong_ball_think;
36 ball.nextthink = time;
40 // Think equivalent of pong_ball_throw, used to delay throws
41 void pong_ball_throwthink()
43 pong_ball_throw(self);
46 // Moves ball to the center and stops its motion
47 void pong_ball_reset(entity ball)
49 ball.velocity = '0 0 0';
50 ball.origin = '0.5 0.5 0';
51 ball.SendFlags |= MINIG_SF_UPDATE;
52 ball.think = SUB_NullThink;
56 // Add the score to the given team in the minigame
57 void pong_add_score(entity minigame, int team_thrower, int team_receiver, int delta)
62 if ( team_thrower == 0 )
63 team_thrower = team_receiver;
65 if ( team_thrower == team_receiver )
68 entity paddle_thrower = minigame.pong_paddles[team_thrower-1];
69 if ( paddle_thrower.realowner )
71 paddle_thrower.realowner.minigame_players.pong_score += delta;
72 paddle_thrower.realowner.minigame_players.SendFlags |= PONG_SF_PLAYERSCORE;
76 // Checks for a goal, when that happes adds scores and resets the ball
77 bool pong_goal(entity ball, int pteam)
79 entity paddle = ball.owner.pong_paddles[pteam-1];
82 if ( ball.origin_y < paddle.origin_y-paddle.pong_length/2 ||
83 ball.origin_y > paddle.origin_y+paddle.pong_length/2 )
85 pong_add_score(ball.owner ,ball.team, pteam, 1);
86 pong_ball_reset(ball);
87 ball.think = pong_ball_throwthink;
88 ball.nextthink = time + autocvar_sv_minigames_pong_ballwait;
95 // Moves the ball around
96 void pong_ball_think()
98 float think_speed = autocvar_sys_ticrate;
99 self.nextthink = time + think_speed;
101 self.origin_x += self.velocity_x * think_speed;
102 self.origin_y += self.velocity_y * think_speed;
104 // TODO consider the ball's radius (and add cvar for that)
105 if ( self.origin_y <= 0 )
107 if ( !pong_goal(self,3) )
110 self.velocity_y *= -1;
114 else if ( self.origin_y >= 1 )
116 if ( !pong_goal(self,4) )
119 self.velocity_y *= -1;
124 if ( self.origin_x <= 0 )
126 if ( !pong_goal(self,2) )
129 self.velocity_x *= -1;
133 else if ( self.origin_x >= 1 )
135 if ( !pong_goal(self,1) )
138 self.velocity_x *= -1;
143 self.SendFlags |= MINIG_SF_UPDATE;
147 void pong_paddle_think()
149 float think_speed = autocvar_sys_ticrate;
150 self.nextthink = time + think_speed;
152 if ( self.realowner.movement.x > 0 && self.origin_y > self.pong_length/2 )
154 self.origin_y -= autocvar_sv_minigames_pong_paddlespeed * think_speed;
155 if ( self.origin_y < 0 )
157 self.SendFlags |= MINIG_SF_UPDATE;
159 else if ( self.realowner.movement.x < 0 && self.origin_y < 1-self.pong_length/2 )
161 self.origin_y += autocvar_sv_minigames_pong_paddlespeed * think_speed;
162 if ( self.origin_y > 1 )
164 self.SendFlags |= MINIG_SF_UPDATE;
168 vector pong_team_to_paddlepos(int nteam)
172 case 1: return '0.99 0.5 0';
173 case 2: return '0.01 0.5 0';
174 case 3: return '0.5 0.01 0';
175 case 4: return '0.5 0.99 0';
176 default:return '0 0 0';
180 // required function, handle server side events
181 int minigame_event_pong(entity minigame, string event, ...)
187 minigame.minigame_flags |= PONG_STATUS_WAIT;
195 int pl_num = minigame_count_players(minigame);
197 // Don't allow joining a match that is already running
198 if ( minigame.minigame_flags & PONG_STATUS_PLAY )
201 // Don't allow any more players
202 if(pl_num >= PONG_MAX_PLAYERS)
206 // Get the right team
207 if(minigame.minigame_players)
208 pl_team = minigame_next_team(minigame.minigame_players.team, PONG_MAX_PLAYERS);
211 entity player = ...(0,entity);
212 entity paddle = msle_spawn(minigame,"pong_paddle");// Note puddle isn't a typo
213 paddle.pong_length = autocvar_sv_minigames_pong_paddlesize;
214 paddle.origin = pong_team_to_paddlepos(pl_team);
215 paddle.think = pong_paddle_think;
216 paddle.nextthink = time;
217 paddle.team = pl_team;
218 paddle.realowner = player;
219 minigame.pong_paddles[pl_team-1] = paddle;
225 // TODO remove paddle or switch to AI
231 if ( minigame.minigame_flags & PONG_STATUS_WAIT )
233 minigame.minigame_flags = PONG_STATUS_PLAY |
234 (minigame.minigame_flags & ~PONG_STATUS_WAIT);
235 minigame.SendFlags |= MINIG_SF_UPDATE;
237 entity ball = msle_spawn(minigame,"pong_ball");
238 pong_ball_reset(ball);
239 pong_ball_throw(ball);
248 entity sent = ...(0,entity);
250 if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
252 WriteLong(MSG_ENTITY,sent.pong_score);
264 // Required function, draw the game board
265 void minigame_hud_board_pong(vector pos, vector mySize)
267 minigame_hud_fitsqare(pos, mySize);
268 minigame_hud_simpleboard(pos,mySize,minigame_texture("pong/board"));
272 vector ball_size = minigame_hud_denormalize_size('1 1 0' / 16,pos,mySize);
274 FOREACH_MINIGAME_ENTITY(e)
276 if ( e.classname == "pong_ball" )
278 obj_pos = minigame_hud_denormalize(e.origin,pos,mySize);
279 minigame_drawpic_centered( obj_pos, minigame_texture("pong/ball"),
280 ball_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
282 else if ( e.classname == "pong_paddle" )
284 obj_pos = minigame_hud_denormalize(e.origin,pos,mySize);
285 paddle_size = minigame_hud_denormalize_size(eX / 32 + eY*e.pong_length,pos,mySize);
287 minigame_drawpic_centered( obj_pos, minigame_texture("pong/paddle"),
288 paddle_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
294 // Required function, draw the game status panel
295 void minigame_hud_status_pong(vector pos, vector mySize)
299 ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
300 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
305 vector player_fontsize = hud_fontsize * 1.75;
306 ts_y = ( mySize_y - PONG_MAX_PLAYERS*player_fontsize_y ) / PONG_MAX_PLAYERS;
309 vector tile_size = '48 48 0';
312 FOREACH_MINIGAME_ENTITY(e)
314 if ( e.classname == "minigame_player" )
316 // TODO show the team color
318 mypos_y += (e.team-1) * (player_fontsize_y + ts_y);
319 minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
320 (e.minigame_playerslot ? GetPlayerName(e.minigame_playerslot-1) : _("AI")),
321 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
323 mypos_y += player_fontsize_y;
325 drawstring(mypos,ftos(e.pong_score),tile_size,
326 '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
331 // convert minigame flags to a message
332 string pong_message(int mgflags)
334 string rmessage = "";
335 if (mgflags & PONG_STATUS_WAIT)
336 rmessage = _("Press \"Throw Ball\" to start the match with the current players");
340 // Required function, handle client events
341 int minigame_event_pong(entity minigame, string event, ...)
348 switch ( ...(0,int) )
358 case "network_receive":
360 entity sent = ...(0,entity);
362 if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
364 sent.pong_score = ReadLong();
366 else if ( sent.classname == "minigame" )
368 if ( sf & MINIG_SF_UPDATE )
370 sent.message = pong_message(sent.minigame_flags);
377 HUD_MinigameMenu_CustomEntry(...(0,entity),_("Throw Ball"),"pong_throw");
382 string cmd = ...(0,string);
383 if( cmd == "pong_throw" && minigame.minigame_flags & PONG_STATUS_WAIT )
385 minigame_cmd("throw");