]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/minigame/pong.qc
Polish pong logic
[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
9 // fields
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)
14
15 #ifdef SVQC
16
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;
21
22 void pong_ball_think();
23
24 // Throws a ball in a random direction and sets the think function
25 void pong_ball_throw(entity ball)
26 {
27         float angle;
28         do
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;
37         ball.team = 0;
38 }
39
40 // Think equivalent of pong_ball_throw, used to delay throws
41 void pong_ball_throwthink()
42 {
43         pong_ball_throw(self);
44 }
45
46 // Moves ball to the center and stops its motion
47 void pong_ball_reset(entity ball)
48 {
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;
53         ball.team = 0;
54 }
55
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)
58 {
59         if ( !minigame )
60                 return;
61         
62         if ( team_thrower == 0 )
63                 team_thrower = team_receiver;
64         
65         if ( team_thrower == team_receiver )
66                 delta *= -1;
67         
68         entity paddle_thrower = minigame.pong_paddles[team_thrower-1];
69         if ( paddle_thrower.realowner )
70         {
71                 paddle_thrower.realowner.minigame_players.pong_score += delta;
72                 paddle_thrower.realowner.minigame_players.SendFlags |= PONG_SF_PLAYERSCORE;
73         }
74 }
75
76 // Checks for a goal, when that happes adds scores and resets the ball
77 bool pong_goal(entity ball, int pteam)
78 {
79         entity paddle = ball.owner.pong_paddles[pteam-1];
80         if (!paddle)
81                 return false;
82         if ( ball.origin_y < paddle.origin_y-paddle.pong_length/2 ||
83                 ball.origin_y > paddle.origin_y+paddle.pong_length/2 )
84         {
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;
89                 return true;
90         }
91         
92         return false;
93 }
94
95 // Moves the ball around
96 void pong_ball_think()
97 {
98         float think_speed = autocvar_sys_ticrate;
99         self.nextthink = time + think_speed;
100         
101         self.origin_x += self.velocity_x * think_speed;
102         self.origin_y += self.velocity_y * think_speed;
103         if ( self.origin_y <= 0 )
104         {
105                 self.origin_y = 0;
106                 self.velocity_y *= -1;
107         }
108         else if ( self.origin_y >= 1 )
109         {
110                 self.origin_y = 1;
111                 self.velocity_y *= -1;
112         }
113         
114         if ( self.origin_x <= 0 )
115         {
116                 if ( !pong_goal(self,2) )
117                 {
118                          self.origin_x = 0;
119                          self.velocity_x *= -1;
120                          self.team = 2;
121                 }
122         }
123         else if ( self.origin_x >= 1 )
124         {
125                 if ( !pong_goal(self,1) )
126                 {
127                          self.origin_x = 1;
128                          self.velocity_x *= -1;
129                          self.team = 1;
130                 }
131         }
132         // TODO team 3 4 goal check
133         
134         self.SendFlags |= MINIG_SF_UPDATE;
135 }
136
137 // Moves the paddle
138 void pong_paddle_think()
139 {
140         float think_speed = autocvar_sys_ticrate;
141         self.nextthink = time + think_speed;
142         
143         if ( self.realowner.movement.x > 0 && self.origin_y > self.pong_length/2 )
144         {
145                 self.origin_y -= autocvar_sv_minigames_pong_paddlespeed * think_speed;
146                 if ( self.origin_y < 0 )
147                         self.origin_y = 0;
148                 self.SendFlags |= MINIG_SF_UPDATE;
149         }
150         else if ( self.realowner.movement.x < 0 && self.origin_y < 1-self.pong_length/2 )
151         {
152                 self.origin_y += autocvar_sv_minigames_pong_paddlespeed * think_speed;
153                 if ( self.origin_y > 1 )
154                         self.origin_y = 1;
155                 self.SendFlags |= MINIG_SF_UPDATE;
156         }
157 }
158
159 vector pong_team_to_paddlepos(int nteam)
160 {
161         switch(nteam)
162         {
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';
168         }
169 }
170
171 // required function, handle server side events
172 int minigame_event_pong(entity minigame, string event, ...)
173 {
174         switch (event)
175         {
176                 case "start":
177                 {
178                         minigame.minigame_flags |= PONG_STATUS_WAIT;
179                         return true;
180                 }
181                 case "end":
182                         // nothing to do
183                         return false;
184                 case "join":
185                 {
186                         int pl_num = minigame_count_players(minigame);
187                         
188                         // Don't allow joining a match that is already running
189                         if ( minigame.minigame_flags & PONG_STATUS_PLAY )
190                                 return false;
191
192                         // Don't allow any more players
193                         if(pl_num >= PONG_MAX_PLAYERS) 
194                                 return false;
195
196                         int pl_team = 1;
197                         // Get the right team
198                         if(minigame.minigame_players)
199                                 pl_team = minigame_next_team(minigame.minigame_players.team, PONG_MAX_PLAYERS);
200                         
201                         
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;
211
212                         // Team 1 by default
213                         return pl_team;
214                 }
215                 case "part":
216                         // TODO remove paddle or switch to AI
217                         return false;
218                 case "cmd":
219                         switch(argv(0))
220                         {
221                                 case "throw":
222                                         if ( minigame.minigame_flags & PONG_STATUS_WAIT )
223                                         {
224                                                 minigame.minigame_flags = PONG_STATUS_PLAY |
225                                                         (minigame.minigame_flags & ~PONG_STATUS_WAIT);
226                                                 minigame.SendFlags |= MINIG_SF_UPDATE;
227                                                 
228                                                 entity ball = msle_spawn(minigame,"pong_ball");
229                                                 pong_ball_reset(ball);
230                                                 pong_ball_throw(ball);
231                                         }
232                                         return true;
233                                                 
234                         }
235                         // nothing to do
236                         return false;
237                 case "network_send":
238                 {
239                         entity sent = ...(0,entity);
240                         int sf = ...(1,int);
241                         if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
242                         {
243                                 WriteLong(MSG_ENTITY,sent.pong_score);
244                         }
245                         return false;
246                 }
247         }
248         return false;
249 }
250
251
252 #elif defined(CSQC)
253
254
255 // Required function, draw the game board
256 void minigame_hud_board_pong(vector pos, vector mySize)
257 {
258         minigame_hud_fitsqare(pos, mySize);
259         minigame_hud_simpleboard(pos,mySize,minigame_texture("pong/board"));
260         
261         entity e;
262         vector obj_pos;
263         vector ball_size = minigame_hud_denormalize_size('1 1 0' / 16,pos,mySize);
264         vector paddle_size;
265         FOREACH_MINIGAME_ENTITY(e)
266         {
267                 if ( e.classname == "pong_ball" )
268                 {
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 );
272                 }
273                 else if ( e.classname == "pong_paddle" )
274                 {
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);
277                         // TODO team paddles
278                         minigame_drawpic_centered( obj_pos, minigame_texture("pong/paddle"),
279                                         paddle_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
280                 }
281         }
282 }
283
284
285 // Required function, draw the game status panel
286 void minigame_hud_status_pong(vector pos, vector mySize)
287 {
288         HUD_Panel_DrawBg(1);
289         vector ts;
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);
292         
293         pos_y += ts_y;
294         mySize_y -= ts_y;
295         
296         vector player_fontsize = hud_fontsize * 1.75;
297         ts_y = ( mySize_y - PONG_MAX_PLAYERS*player_fontsize_y ) / PONG_MAX_PLAYERS;
298         ts_x = mySize_x;
299         vector mypos;
300         vector tile_size = '48 48 0';
301
302         entity e;
303         FOREACH_MINIGAME_ENTITY(e)
304         {
305                 if ( e.classname == "minigame_player" )
306                 {
307                         // TODO show the team color
308                         mypos = pos;
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);
313                         
314                         mypos_y += player_fontsize_y;
315                         
316                         drawstring(mypos,ftos(e.pong_score),tile_size,
317                                            '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
318                 }
319         }
320 }
321
322 // convert minigame flags to a message
323 string pong_message(int mgflags)
324 {
325         string rmessage = "";
326         if (mgflags & PONG_STATUS_WAIT)
327                 rmessage = _("Press \"Throw Ball\" to start the match with the current players");
328         return rmessage;
329 }
330
331 // Required function, handle client events
332 int minigame_event_pong(entity minigame, string event, ...)
333 {
334         switch(event)
335         {
336                 case "activate":
337                         return false;
338                 case "key_pressed":
339                         switch ( ...(0,int) )
340                         {
341                                 case K_UPARROW:
342                                 case K_KP_UPARROW:
343                                         return true;
344                                 case K_DOWNARROW:
345                                 case K_KP_DOWNARROW:
346                                         return true;
347                         }
348                         return false;
349                 case "network_receive":
350                 {
351                         entity sent = ...(0,entity);
352                         int sf = ...(1,int);
353                         if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
354                         {
355                                 sent.pong_score = ReadLong();
356                         }
357                         else if ( sent.classname == "minigame" )
358                         {
359                                 if ( sf & MINIG_SF_UPDATE )
360                                 {
361                                         sent.message = pong_message(sent.minigame_flags);
362                                 }
363                         }
364                         return false;
365                 }
366                 case "menu_show":
367                 {
368                         HUD_MinigameMenu_CustomEntry(...(0,entity),_("Throw Ball"),"pong_throw");
369                         return false;
370                 }
371                 case "menu_click":
372                 {
373                         string cmd = ...(0,string);
374                         if( cmd == "pong_throw" && minigame.minigame_flags & PONG_STATUS_WAIT )
375                         {
376                                 minigame_cmd("throw");
377                         }
378                         return false;
379                 }
380         }
381
382         return false;
383 }
384 #endif