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;
103 if ( self.origin_y <= 0 )
106 self.velocity_y *= -1;
108 else if ( self.origin_y >= 1 )
111 self.velocity_y *= -1;
114 if ( self.origin_x <= 0 )
116 if ( !pong_goal(self,2) )
119 self.velocity_x *= -1;
123 else if ( self.origin_x >= 1 )
125 if ( !pong_goal(self,1) )
128 self.velocity_x *= -1;
132 // TODO team 3 4 goal check
134 self.SendFlags |= MINIG_SF_UPDATE;
138 void pong_paddle_think()
140 float think_speed = autocvar_sys_ticrate;
141 self.nextthink = time + think_speed;
143 if ( self.realowner.movement.x > 0 && self.origin_y > self.pong_length/2 )
145 self.origin_y -= autocvar_sv_minigames_pong_paddlespeed * think_speed;
146 if ( self.origin_y < 0 )
148 self.SendFlags |= MINIG_SF_UPDATE;
150 else if ( self.realowner.movement.x < 0 && self.origin_y < 1-self.pong_length/2 )
152 self.origin_y += autocvar_sv_minigames_pong_paddlespeed * think_speed;
153 if ( self.origin_y > 1 )
155 self.SendFlags |= MINIG_SF_UPDATE;
159 vector pong_team_to_paddlepos(int nteam)
163 case 1: return '0.99 0.5 0';
164 case 2: return '0.01 0.5 0';
165 case 3: return '0.5 0.01 0';
166 case 4: return '0.5 0.99 0';
167 default:return '0 0 0';
171 // required function, handle server side events
172 int minigame_event_pong(entity minigame, string event, ...)
178 minigame.minigame_flags |= PONG_STATUS_WAIT;
186 int pl_num = minigame_count_players(minigame);
188 // Don't allow joining a match that is already running
189 if ( minigame.minigame_flags & PONG_STATUS_PLAY )
192 // Don't allow any more players
193 if(pl_num >= PONG_MAX_PLAYERS)
197 // Get the right team
198 if(minigame.minigame_players)
199 pl_team = minigame_next_team(minigame.minigame_players.team, PONG_MAX_PLAYERS);
202 entity player = ...(0,entity);
203 entity paddle = msle_spawn(minigame,"pong_paddle");// Note puddle isn't a typo
204 paddle.pong_length = autocvar_sv_minigames_pong_paddlesize;
205 paddle.origin = pong_team_to_paddlepos(pl_team);
206 paddle.think = pong_paddle_think;
207 paddle.nextthink = time;
208 paddle.team = pl_team;
209 paddle.realowner = player;
210 minigame.pong_paddles[pl_team-1] = paddle;
216 // TODO remove paddle or switch to AI
222 if ( minigame.minigame_flags & PONG_STATUS_WAIT )
224 minigame.minigame_flags = PONG_STATUS_PLAY |
225 (minigame.minigame_flags & ~PONG_STATUS_WAIT);
226 minigame.SendFlags |= MINIG_SF_UPDATE;
228 entity ball = msle_spawn(minigame,"pong_ball");
229 pong_ball_reset(ball);
230 pong_ball_throw(ball);
239 entity sent = ...(0,entity);
241 if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
243 WriteLong(MSG_ENTITY,sent.pong_score);
255 // Required function, draw the game board
256 void minigame_hud_board_pong(vector pos, vector mySize)
258 minigame_hud_fitsqare(pos, mySize);
259 minigame_hud_simpleboard(pos,mySize,minigame_texture("pong/board"));
263 vector ball_size = minigame_hud_denormalize_size('1 1 0' / 16,pos,mySize);
265 FOREACH_MINIGAME_ENTITY(e)
267 if ( e.classname == "pong_ball" )
269 obj_pos = minigame_hud_denormalize(e.origin,pos,mySize);
270 minigame_drawpic_centered( obj_pos, minigame_texture("pong/ball"),
271 ball_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
273 else if ( e.classname == "pong_paddle" )
275 obj_pos = minigame_hud_denormalize(e.origin,pos,mySize);
276 paddle_size = minigame_hud_denormalize_size(eX / 32 + eY*e.pong_length,pos,mySize);
278 minigame_drawpic_centered( obj_pos, minigame_texture("pong/paddle"),
279 paddle_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
285 // Required function, draw the game status panel
286 void minigame_hud_status_pong(vector pos, vector mySize)
290 ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
291 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
296 vector player_fontsize = hud_fontsize * 1.75;
297 ts_y = ( mySize_y - PONG_MAX_PLAYERS*player_fontsize_y ) / PONG_MAX_PLAYERS;
300 vector tile_size = '48 48 0';
303 FOREACH_MINIGAME_ENTITY(e)
305 if ( e.classname == "minigame_player" )
307 // TODO show the team color
309 mypos_y += (e.team-1) * (player_fontsize_y + ts_y);
310 minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
311 (e.minigame_playerslot ? GetPlayerName(e.minigame_playerslot-1) : _("AI")),
312 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
314 mypos_y += player_fontsize_y;
316 drawstring(mypos,ftos(e.pong_score),tile_size,
317 '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
322 // convert minigame flags to a message
323 string pong_message(int mgflags)
325 string rmessage = "";
326 if (mgflags & PONG_STATUS_WAIT)
327 rmessage = _("Press \"Throw Ball\" to start the match with the current players");
331 // Required function, handle client events
332 int minigame_event_pong(entity minigame, string event, ...)
339 switch ( ...(0,int) )
349 case "network_receive":
351 entity sent = ...(0,entity);
353 if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
355 sent.pong_score = ReadLong();
357 else if ( sent.classname == "minigame" )
359 if ( sf & MINIG_SF_UPDATE )
361 sent.message = pong_message(sent.minigame_flags);
368 HUD_MinigameMenu_CustomEntry(...(0,entity),_("Throw Ball"),"pong_throw");
373 string cmd = ...(0,string);
374 if( cmd == "pong_throw" && minigame.minigame_flags & PONG_STATUS_WAIT )
376 minigame_cmd("throw");