]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/minigame/pong.qc
Pong movement keys
[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 // keys
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
15
16 // fields
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)
22
23 #ifdef SVQC
24
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;
29
30 void pong_ball_think();
31
32 // Throws a ball in a random direction and sets the think function
33 void pong_ball_throw(entity ball)
34 {
35         float angle;
36         do
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;
43         ball.team = 0;
44         ball.SendFlags |= MINIG_SF_UPDATE|PONG_SF_BALLTEAM;
45 }
46
47 // Think equivalent of pong_ball_throw, used to delay throws
48 void pong_ball_throwthink()
49 {
50         pong_ball_throw(self);
51 }
52
53 // Moves ball to the center and stops its motion
54 void pong_ball_reset(entity ball)
55 {
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;
60         ball.team = 0;
61         ball.SendFlags |= PONG_SF_BALLTEAM;
62 }
63
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)
66 {
67         if ( !minigame )
68                 return;
69         
70         if ( team_thrower == 0 )
71                 team_thrower = team_receiver;
72         
73         if ( team_thrower == team_receiver )
74                 delta *= -1;
75         
76         entity paddle_thrower = minigame.pong_paddles[team_thrower-1];
77         if ( paddle_thrower.realowner )
78         {
79                 paddle_thrower.realowner.minigame_players.pong_score += delta;
80                 paddle_thrower.realowner.minigame_players.SendFlags |= PONG_SF_PLAYERSCORE;
81         }
82 }
83
84 bool pong_paddlemiss(float ball_coord, float pad_coord, float pad_len)
85 {
86         return ball_coord < pad_coord - pad_len/2 || ball_coord > pad_coord + pad_len/2;
87 }
88
89 // Checks for a goal, when that happes adds scores and resets the ball
90 bool pong_goal(entity ball, int pteam)
91 {
92         entity paddle = ball.owner.pong_paddles[pteam-1];
93         if (!paddle)
94                 return false;
95         
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) )
98         {
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;
103                 return true;
104         }
105         else
106         {
107                 ball.team = pteam;
108                 ball.SendFlags |= PONG_SF_BALLTEAM;
109         }
110         
111         return false;
112 }
113
114 // Moves the ball around
115 void pong_ball_think()
116 {
117         float think_speed = autocvar_sys_ticrate;
118         self.nextthink = time + think_speed;
119         
120         self.origin_x += self.velocity_x * think_speed;
121         self.origin_y += self.velocity_y * think_speed;
122         
123         // TODO consider the ball's radius (and add cvar for that)
124         if ( self.origin_y <= 0 )
125         {
126                 if ( !pong_goal(self,3) )
127                 {
128                         self.origin_y = 0;
129                         self.velocity_y *= -1;
130                 }
131         }
132         else if ( self.origin_y >= 1 )
133         {
134                 if ( !pong_goal(self,4) )
135                 {
136                         self.origin_y = 1;
137                         self.velocity_y *= -1;
138                 }
139         }
140         
141         if ( self.origin_x <= 0 )
142         {
143                 if ( !pong_goal(self,2) )
144                 {
145                          self.origin_x = 0;
146                          self.velocity_x *= -1;
147                 }
148         }
149         else if ( self.origin_x >= 1 )
150         {
151                 if ( !pong_goal(self,1) )
152                 {
153                          self.origin_x = 1;
154                          self.velocity_x *= -1;
155                 }
156         }
157         
158         self.SendFlags |= MINIG_SF_UPDATE;
159 }
160
161 // Moves the paddle
162 void pong_paddle_think()
163 {
164         float think_speed = autocvar_sys_ticrate;
165         self.nextthink = time + think_speed;
166         
167         if ( self.realowner.minigame_players.pong_keys &&
168                 self.realowner.minigame_players.pong_keys != PONG_KEY_BOTH )
169         {
170                 float movement = autocvar_sv_minigames_pong_paddlespeed * think_speed;
171                 float halflen = self.pong_length/2;
172                 
173                 if ( self.realowner.minigame_players.pong_keys == PONG_KEY_DECREASE )
174                         movement *= -1;
175                 
176                 if ( self.team > 2 )
177                         self.origin_x = bound(halflen, self.origin_x+movement, 1-halflen);
178                 else
179                         self.origin_y = bound(halflen, self.origin_y+movement, 1-halflen);
180                 
181                 self.SendFlags |= MINIG_SF_UPDATE;
182         }
183 }
184
185 vector pong_team_to_paddlepos(int nteam)
186 {
187         switch(nteam)
188         {
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';
194         }
195 }
196
197 // required function, handle server side events
198 int pong_server_event(entity minigame, string event, ...)
199 {
200         switch (event)
201         {
202                 case "start":
203                 {
204                         minigame.minigame_flags |= PONG_STATUS_WAIT;
205                         return true;
206                 }
207                 case "end":
208                         // nothing to do
209                         return false;
210                 case "join":
211                 {
212                         int pl_num = minigame_count_players(minigame);
213                         
214                         // Don't allow joining a match that is already running
215                         if ( minigame.minigame_flags & PONG_STATUS_PLAY )
216                                 return false;
217
218                         // Don't allow any more players
219                         if(pl_num >= PONG_MAX_PLAYERS) 
220                                 return false;
221
222                         int pl_team = 1;
223                         // Get the right team
224                         if(minigame.minigame_players)
225                                 pl_team = minigame_next_team(minigame.minigame_players.team, PONG_MAX_PLAYERS);
226                         
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;
236
237                         // Team 1 by default
238                         return pl_team;
239                 }
240                 case "part":
241                         // TODO remove paddle or switch to AI
242                         return false;
243                 case "cmd":
244                 {
245                         entity player = ...(0,entity);
246                         switch(argv(0))
247                         {
248                                 case "throw":
249                                         if ( minigame.minigame_flags & PONG_STATUS_WAIT )
250                                         {
251                                                 minigame.minigame_flags = PONG_STATUS_PLAY |
252                                                         (minigame.minigame_flags & ~PONG_STATUS_WAIT);
253                                                 minigame.SendFlags |= MINIG_SF_UPDATE;
254                                                 
255                                                 entity ball = msle_spawn(minigame,"pong_ball");
256                                                 pong_ball_reset(ball);
257                                                 pong_ball_throw(ball);
258                                         }
259                                         return true;
260                                 case "+movei":
261                                         player.pong_keys |= PONG_KEY_INCREASE;
262                                         return true;
263                                 case "+moved":
264                                         player.pong_keys |= PONG_KEY_DECREASE;
265                                         return true;
266                                 case "-movei":
267                                         player.pong_keys &= ~PONG_KEY_INCREASE;
268                                         return true;
269                                 case "-moved":
270                                         player.pong_keys &= ~PONG_KEY_DECREASE;
271                                         return true;
272                                                 
273                         }
274                         return false;
275                 }
276                 case "network_send":
277                 {
278                         entity sent = ...(0,entity);
279                         int sf = ...(1,int);
280                         if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
281                         {
282                                 WriteLong(MSG_ENTITY,sent.pong_score);
283                         }
284                         return false;
285                 }
286         }
287         return false;
288 }
289
290
291 #elif defined(CSQC)
292
293 #include "waypointsprites.qh" // drawrotpic
294
295 float pong_team_to_angle(int nteam)
296 {
297         switch(nteam)
298         {
299                 default:
300                 case 1: return 0;
301                 case 2: return M_PI;
302                 case 3: return M_PI*3/2;
303                 case 4: return M_PI/2;
304         }
305 }
306
307 vector pong_team_to_color(int nteam)
308 {
309         switch(nteam)
310         {
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';
316         }
317 }
318
319 // Required function, draw the game board
320 void pong_hud_board(vector pos, vector mySize)
321 {
322         minigame_hud_fitsqare(pos, mySize);
323         minigame_hud_simpleboard(pos,mySize,minigame_texture("pong/board"));
324         
325         entity e;
326         vector obj_pos;
327         vector ball_size = minigame_hud_denormalize_size('1 1 0' / 16,pos,mySize);
328         vector paddle_size;
329         FOREACH_MINIGAME_ENTITY(e)
330         {
331                 if ( e.classname == "pong_ball" )
332                 {
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 );
336                         
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 );
340                 }
341                 else if ( e.classname == "pong_paddle" )
342                 {
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);
345                         
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 );
349                         
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 );
353                         
354                 }
355         }
356 }
357
358
359 // Required function, draw the game status panel
360 void pong_hud_status(vector pos, vector mySize)
361 {
362         HUD_Panel_DrawBg(1);
363         vector ts;
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);
366         
367         pos_y += ts_y;
368         mySize_y -= ts_y;
369         
370         vector player_fontsize = hud_fontsize * 1.75;
371         ts_y = ( mySize_y - PONG_MAX_PLAYERS*player_fontsize_y ) / PONG_MAX_PLAYERS;
372         ts_x = mySize_x;
373         vector mypos;
374         vector tile_size = '48 48 0';
375
376         entity e;
377         FOREACH_MINIGAME_ENTITY(e)
378         {
379                 if ( e.classname == "minigame_player" )
380                 {
381                         mypos = pos;
382                         mypos_y  += (e.team-1) * (player_fontsize_y + ts_y);
383                         
384                         drawfill(mypos, eX*mySize_x+eY*ts_y, pong_team_to_color(e.team),
385                                          0.25, DRAWFLAG_ADDITIVE);
386                         
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);
390                         
391                         drawstring(mypos+eY*player_fontsize_y,ftos(e.pong_score),tile_size,
392                                            '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
393                         
394                         if ( e == minigame_self )
395                         {
396                                 drawborderlines(1, mypos, eX*mySize_x+eY*ts_y, 
397                                         pong_team_to_color(e.team), 1, DRAWFLAG_NORMAL);
398                         }
399                 }
400         }
401 }
402
403 // convert minigame flags to a message
404 string pong_message(int mgflags)
405 {
406         string rmessage = "";
407         if (mgflags & PONG_STATUS_WAIT)
408                 rmessage = _("Press \"Throw Ball\" to start the match with the current players");
409         return rmessage;
410 }
411
412 // Required function, handle client events
413 int pong_client_event(entity minigame, string event, ...)
414 {
415         switch(event)
416         {
417                 case "activate":
418                         return false;
419                 case "key_pressed":
420                         switch ( ...(0,int) )
421                         {
422                                 case K_UPARROW:
423                                 case K_KP_UPARROW:
424                                 case K_LEFTARROW:
425                                 case K_KP_LEFTARROW:
426                                         minigame_cmd("+moved");
427                                         return true;
428                                 case K_DOWNARROW:
429                                 case K_KP_DOWNARROW:
430                                 case K_RIGHTARROW:
431                                 case K_KP_RIGHTARROW:
432                                         minigame_cmd("+movei");
433                                         return true;
434                         }
435                         return false;
436                 case "key_released":
437                         switch ( ...(0,int) )
438                         {
439                                 case K_UPARROW:
440                                 case K_KP_UPARROW:
441                                 case K_LEFTARROW:
442                                 case K_KP_LEFTARROW:
443                                         minigame_cmd("-moved");
444                                         return true;
445                                 case K_DOWNARROW:
446                                 case K_KP_DOWNARROW:
447                                 case K_RIGHTARROW:
448                                 case K_KP_RIGHTARROW:
449                                         minigame_cmd("-movei");
450                                         return true;
451                         }
452                         return false;
453                 case "network_receive":
454                 {
455                         entity sent = ...(0,entity);
456                         int sf = ...(1,int);
457                         if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
458                         {
459                                 sent.pong_score = ReadLong();
460                         }
461                         else if ( sent.classname == "minigame" )
462                         {
463                                 if ( sf & MINIG_SF_UPDATE )
464                                 {
465                                         sent.message = pong_message(sent.minigame_flags);
466                                 }
467                         }
468                         return false;
469                 }
470                 case "menu_show":
471                 {
472                         HUD_MinigameMenu_CustomEntry(...(0,entity),_("Throw Ball"),"pong_throw");
473                         return false;
474                 }
475                 case "menu_click":
476                 {
477                         string cmd = ...(0,string);
478                         if( cmd == "pong_throw" && minigame.minigame_flags & PONG_STATUS_WAIT )
479                         {
480                                 minigame_cmd("throw");
481                         }
482                         return false;
483                 }
484         }
485
486         return false;
487 }
488 #endif