]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/minigame/pong.qc
Rename the minigame definition functions
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / minigames / minigame / pong.qc
1 // minigame flags
2 const int PONG_STATUS_WAIT = 0x0010; // waiting for players to join
3 const int PONG_STATUS_PLAY = 0x0020; // playing
4
5 // send flags
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;
10
11 // fields
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)
16
17 #ifdef SVQC
18
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;
23
24 void pong_ball_think();
25
26 // Throws a ball in a random direction and sets the think function
27 void pong_ball_throw(entity ball)
28 {
29         float angle;
30         do
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;
38         ball.team = 0;
39         ball.SendFlags |= MINIG_SF_UPDATE|PONG_SF_BALLTEAM;
40 }
41
42 // Think equivalent of pong_ball_throw, used to delay throws
43 void pong_ball_throwthink()
44 {
45         pong_ball_throw(self);
46 }
47
48 // Moves ball to the center and stops its motion
49 void pong_ball_reset(entity ball)
50 {
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;
55         ball.team = 0;
56         ball.SendFlags |= PONG_SF_BALLTEAM;
57 }
58
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)
61 {
62         if ( !minigame )
63                 return;
64         
65         if ( team_thrower == 0 )
66                 team_thrower = team_receiver;
67         
68         if ( team_thrower == team_receiver )
69                 delta *= -1;
70         
71         entity paddle_thrower = minigame.pong_paddles[team_thrower-1];
72         if ( paddle_thrower.realowner )
73         {
74                 paddle_thrower.realowner.minigame_players.pong_score += delta;
75                 paddle_thrower.realowner.minigame_players.SendFlags |= PONG_SF_PLAYERSCORE;
76         }
77 }
78
79 bool pong_paddlemiss(float ball_coord, float pad_coord, float pad_len)
80 {
81         return ball_coord < pad_coord - pad_len/2 || ball_coord > pad_coord + pad_len/2;
82 }
83
84 // Checks for a goal, when that happes adds scores and resets the ball
85 bool pong_goal(entity ball, int pteam)
86 {
87         entity paddle = ball.owner.pong_paddles[pteam-1];
88         if (!paddle)
89                 return false;
90         
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) )
93         {
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;
98                 return true;
99         }
100         else
101         {
102                 ball.team = pteam;
103                 ball.SendFlags |= PONG_SF_BALLTEAM;
104         }
105         
106         return false;
107 }
108
109 // Moves the ball around
110 void pong_ball_think()
111 {
112         float think_speed = autocvar_sys_ticrate;
113         self.nextthink = time + think_speed;
114         
115         self.origin_x += self.velocity_x * think_speed;
116         self.origin_y += self.velocity_y * think_speed;
117         
118         // TODO consider the ball's radius (and add cvar for that)
119         if ( self.origin_y <= 0 )
120         {
121                 if ( !pong_goal(self,3) )
122                 {
123                         self.origin_y = 0;
124                         self.velocity_y *= -1;
125                 }
126         }
127         else if ( self.origin_y >= 1 )
128         {
129                 if ( !pong_goal(self,4) )
130                 {
131                         self.origin_y = 1;
132                         self.velocity_y *= -1;
133                 }
134         }
135         
136         if ( self.origin_x <= 0 )
137         {
138                 if ( !pong_goal(self,2) )
139                 {
140                          self.origin_x = 0;
141                          self.velocity_x *= -1;
142                 }
143         }
144         else if ( self.origin_x >= 1 )
145         {
146                 if ( !pong_goal(self,1) )
147                 {
148                          self.origin_x = 1;
149                          self.velocity_x *= -1;
150                 }
151         }
152         
153         self.SendFlags |= MINIG_SF_UPDATE;
154 }
155
156 // Moves the paddle
157 void pong_paddle_think()
158 {
159         float think_speed = autocvar_sys_ticrate;
160         self.nextthink = time + think_speed;
161         
162         if ( self.realowner.movement.x > 0 && self.origin_y > self.pong_length/2 )
163         {
164                 self.origin_y -= autocvar_sv_minigames_pong_paddlespeed * think_speed;
165                 if ( self.origin_y < 0 )
166                         self.origin_y = 0;
167                 self.SendFlags |= MINIG_SF_UPDATE;
168         }
169         else if ( self.realowner.movement.x < 0 && self.origin_y < 1-self.pong_length/2 )
170         {
171                 self.origin_y += autocvar_sv_minigames_pong_paddlespeed * think_speed;
172                 if ( self.origin_y > 1 )
173                         self.origin_y = 1;
174                 self.SendFlags |= MINIG_SF_UPDATE;
175         }
176 }
177
178 vector pong_team_to_paddlepos(int nteam)
179 {
180         switch(nteam)
181         {
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';
187         }
188 }
189
190 // required function, handle server side events
191 int pong_server_event(entity minigame, string event, ...)
192 {
193         switch (event)
194         {
195                 case "start":
196                 {
197                         minigame.minigame_flags |= PONG_STATUS_WAIT;
198                         return true;
199                 }
200                 case "end":
201                         // nothing to do
202                         return false;
203                 case "join":
204                 {
205                         int pl_num = minigame_count_players(minigame);
206                         
207                         // Don't allow joining a match that is already running
208                         if ( minigame.minigame_flags & PONG_STATUS_PLAY )
209                                 return false;
210
211                         // Don't allow any more players
212                         if(pl_num >= PONG_MAX_PLAYERS) 
213                                 return false;
214
215                         int pl_team = 1;
216                         // Get the right team
217                         if(minigame.minigame_players)
218                                 pl_team = minigame_next_team(minigame.minigame_players.team, PONG_MAX_PLAYERS);
219                         
220                         
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;
230
231                         // Team 1 by default
232                         return pl_team;
233                 }
234                 case "part":
235                         // TODO remove paddle or switch to AI
236                         return false;
237                 case "cmd":
238                         switch(argv(0))
239                         {
240                                 case "throw":
241                                         if ( minigame.minigame_flags & PONG_STATUS_WAIT )
242                                         {
243                                                 minigame.minigame_flags = PONG_STATUS_PLAY |
244                                                         (minigame.minigame_flags & ~PONG_STATUS_WAIT);
245                                                 minigame.SendFlags |= MINIG_SF_UPDATE;
246                                                 
247                                                 entity ball = msle_spawn(minigame,"pong_ball");
248                                                 pong_ball_reset(ball);
249                                                 pong_ball_throw(ball);
250                                         }
251                                         return true;
252                                                 
253                         }
254                         // nothing to do
255                         return false;
256                 case "network_send":
257                 {
258                         entity sent = ...(0,entity);
259                         int sf = ...(1,int);
260                         if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
261                         {
262                                 WriteLong(MSG_ENTITY,sent.pong_score);
263                         }
264                         return false;
265                 }
266         }
267         return false;
268 }
269
270
271 #elif defined(CSQC)
272
273 #include "waypointsprites.qh" // drawrotpic
274
275 float pong_team_to_angle(int nteam)
276 {
277         switch(nteam)
278         {
279                 default:
280                 case 1: return 0;
281                 case 2: return M_PI;
282                 case 3: return M_PI*3/2;
283                 case 4: return M_PI/2;
284         }
285 }
286
287 vector pong_team_to_color(int nteam)
288 {
289         switch(nteam)
290         {
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';
296         }
297 }
298
299 // Required function, draw the game board
300 void pong_hud_board(vector pos, vector mySize)
301 {
302         minigame_hud_fitsqare(pos, mySize);
303         minigame_hud_simpleboard(pos,mySize,minigame_texture("pong/board"));
304         
305         entity e;
306         vector obj_pos;
307         vector ball_size = minigame_hud_denormalize_size('1 1 0' / 16,pos,mySize);
308         vector paddle_size;
309         FOREACH_MINIGAME_ENTITY(e)
310         {
311                 if ( e.classname == "pong_ball" )
312                 {
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 );
316                         
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 );
320                 }
321                 else if ( e.classname == "pong_paddle" )
322                 {
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);
325                         
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 );
329                         
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 );
333                         
334                 }
335         }
336 }
337
338
339 // Required function, draw the game status panel
340 void pong_hud_status(vector pos, vector mySize)
341 {
342         HUD_Panel_DrawBg(1);
343         vector ts;
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);
346         
347         pos_y += ts_y;
348         mySize_y -= ts_y;
349         
350         vector player_fontsize = hud_fontsize * 1.75;
351         ts_y = ( mySize_y - PONG_MAX_PLAYERS*player_fontsize_y ) / PONG_MAX_PLAYERS;
352         ts_x = mySize_x;
353         vector mypos;
354         vector tile_size = '48 48 0';
355
356         entity e;
357         FOREACH_MINIGAME_ENTITY(e)
358         {
359                 if ( e.classname == "minigame_player" )
360                 {
361                         // TODO show the team color
362                         mypos = pos;
363                         mypos_y  += (e.team-1) * (player_fontsize_y + ts_y);
364                         
365                         drawfill(mypos, eX*mySize_x+eY*ts_y, pong_team_to_color(e.team),
366                                          0.25, DRAWFLAG_ADDITIVE);
367                         
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);
371                         
372                         mypos_y += player_fontsize_y;
373                         
374                         drawstring(mypos,ftos(e.pong_score),tile_size,
375                                            '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
376                 }
377         }
378 }
379
380 // convert minigame flags to a message
381 string pong_message(int mgflags)
382 {
383         string rmessage = "";
384         if (mgflags & PONG_STATUS_WAIT)
385                 rmessage = _("Press \"Throw Ball\" to start the match with the current players");
386         return rmessage;
387 }
388
389 // Required function, handle client events
390 int pong_client_event(entity minigame, string event, ...)
391 {
392         switch(event)
393         {
394                 case "activate":
395                         return false;
396                 case "key_pressed":
397                         switch ( ...(0,int) )
398                         {
399                                 case K_UPARROW:
400                                 case K_KP_UPARROW:
401                                         return true;
402                                 case K_DOWNARROW:
403                                 case K_KP_DOWNARROW:
404                                         return true;
405                         }
406                         return false;
407                 case "network_receive":
408                 {
409                         entity sent = ...(0,entity);
410                         int sf = ...(1,int);
411                         if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
412                         {
413                                 sent.pong_score = ReadLong();
414                         }
415                         else if ( sent.classname == "minigame" )
416                         {
417                                 if ( sf & MINIG_SF_UPDATE )
418                                 {
419                                         sent.message = pong_message(sent.minigame_flags);
420                                 }
421                         }
422                         return false;
423                 }
424                 case "menu_show":
425                 {
426                         HUD_MinigameMenu_CustomEntry(...(0,entity),_("Throw Ball"),"pong_throw");
427                         return false;
428                 }
429                 case "menu_click":
430                 {
431                         string cmd = ...(0,string);
432                         if( cmd == "pong_throw" && minigame.minigame_flags & PONG_STATUS_WAIT )
433                         {
434                                 minigame_cmd("throw");
435                         }
436                         return false;
437                 }
438         }
439
440         return false;
441 }
442 #endif