]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/minigame/pong.qc
Complete pong goal logic with other teams
[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         
104         // TODO consider the ball's radius (and add cvar for that)
105         if ( self.origin_y <= 0 )
106         {
107                 if ( !pong_goal(self,3) )
108                 {
109                         self.origin_y = 0;
110                         self.velocity_y *= -1;
111                         self.team = 3;
112                 }
113         }
114         else if ( self.origin_y >= 1 )
115         {
116                 if ( !pong_goal(self,4) )
117                 {
118                         self.origin_y = 1;
119                         self.velocity_y *= -1;
120                         self.team = 4;
121                 }
122         }
123         
124         if ( self.origin_x <= 0 )
125         {
126                 if ( !pong_goal(self,2) )
127                 {
128                          self.origin_x = 0;
129                          self.velocity_x *= -1;
130                          self.team = 2;
131                 }
132         }
133         else if ( self.origin_x >= 1 )
134         {
135                 if ( !pong_goal(self,1) )
136                 {
137                          self.origin_x = 1;
138                          self.velocity_x *= -1;
139                          self.team = 1;
140                 }
141         }
142         
143         self.SendFlags |= MINIG_SF_UPDATE;
144 }
145
146 // Moves the paddle
147 void pong_paddle_think()
148 {
149         float think_speed = autocvar_sys_ticrate;
150         self.nextthink = time + think_speed;
151         
152         if ( self.realowner.movement.x > 0 && self.origin_y > self.pong_length/2 )
153         {
154                 self.origin_y -= autocvar_sv_minigames_pong_paddlespeed * think_speed;
155                 if ( self.origin_y < 0 )
156                         self.origin_y = 0;
157                 self.SendFlags |= MINIG_SF_UPDATE;
158         }
159         else if ( self.realowner.movement.x < 0 && self.origin_y < 1-self.pong_length/2 )
160         {
161                 self.origin_y += autocvar_sv_minigames_pong_paddlespeed * think_speed;
162                 if ( self.origin_y > 1 )
163                         self.origin_y = 1;
164                 self.SendFlags |= MINIG_SF_UPDATE;
165         }
166 }
167
168 vector pong_team_to_paddlepos(int nteam)
169 {
170         switch(nteam)
171         {
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';
177         }
178 }
179
180 // required function, handle server side events
181 int minigame_event_pong(entity minigame, string event, ...)
182 {
183         switch (event)
184         {
185                 case "start":
186                 {
187                         minigame.minigame_flags |= PONG_STATUS_WAIT;
188                         return true;
189                 }
190                 case "end":
191                         // nothing to do
192                         return false;
193                 case "join":
194                 {
195                         int pl_num = minigame_count_players(minigame);
196                         
197                         // Don't allow joining a match that is already running
198                         if ( minigame.minigame_flags & PONG_STATUS_PLAY )
199                                 return false;
200
201                         // Don't allow any more players
202                         if(pl_num >= PONG_MAX_PLAYERS) 
203                                 return false;
204
205                         int pl_team = 1;
206                         // Get the right team
207                         if(minigame.minigame_players)
208                                 pl_team = minigame_next_team(minigame.minigame_players.team, PONG_MAX_PLAYERS);
209                         
210                         
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;
220
221                         // Team 1 by default
222                         return pl_team;
223                 }
224                 case "part":
225                         // TODO remove paddle or switch to AI
226                         return false;
227                 case "cmd":
228                         switch(argv(0))
229                         {
230                                 case "throw":
231                                         if ( minigame.minigame_flags & PONG_STATUS_WAIT )
232                                         {
233                                                 minigame.minigame_flags = PONG_STATUS_PLAY |
234                                                         (minigame.minigame_flags & ~PONG_STATUS_WAIT);
235                                                 minigame.SendFlags |= MINIG_SF_UPDATE;
236                                                 
237                                                 entity ball = msle_spawn(minigame,"pong_ball");
238                                                 pong_ball_reset(ball);
239                                                 pong_ball_throw(ball);
240                                         }
241                                         return true;
242                                                 
243                         }
244                         // nothing to do
245                         return false;
246                 case "network_send":
247                 {
248                         entity sent = ...(0,entity);
249                         int sf = ...(1,int);
250                         if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
251                         {
252                                 WriteLong(MSG_ENTITY,sent.pong_score);
253                         }
254                         return false;
255                 }
256         }
257         return false;
258 }
259
260
261 #elif defined(CSQC)
262
263
264 // Required function, draw the game board
265 void minigame_hud_board_pong(vector pos, vector mySize)
266 {
267         minigame_hud_fitsqare(pos, mySize);
268         minigame_hud_simpleboard(pos,mySize,minigame_texture("pong/board"));
269         
270         entity e;
271         vector obj_pos;
272         vector ball_size = minigame_hud_denormalize_size('1 1 0' / 16,pos,mySize);
273         vector paddle_size;
274         FOREACH_MINIGAME_ENTITY(e)
275         {
276                 if ( e.classname == "pong_ball" )
277                 {
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 );
281                 }
282                 else if ( e.classname == "pong_paddle" )
283                 {
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);
286                         // TODO team paddles
287                         minigame_drawpic_centered( obj_pos, minigame_texture("pong/paddle"),
288                                         paddle_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
289                 }
290         }
291 }
292
293
294 // Required function, draw the game status panel
295 void minigame_hud_status_pong(vector pos, vector mySize)
296 {
297         HUD_Panel_DrawBg(1);
298         vector ts;
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);
301         
302         pos_y += ts_y;
303         mySize_y -= ts_y;
304         
305         vector player_fontsize = hud_fontsize * 1.75;
306         ts_y = ( mySize_y - PONG_MAX_PLAYERS*player_fontsize_y ) / PONG_MAX_PLAYERS;
307         ts_x = mySize_x;
308         vector mypos;
309         vector tile_size = '48 48 0';
310
311         entity e;
312         FOREACH_MINIGAME_ENTITY(e)
313         {
314                 if ( e.classname == "minigame_player" )
315                 {
316                         // TODO show the team color
317                         mypos = pos;
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);
322                         
323                         mypos_y += player_fontsize_y;
324                         
325                         drawstring(mypos,ftos(e.pong_score),tile_size,
326                                            '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
327                 }
328         }
329 }
330
331 // convert minigame flags to a message
332 string pong_message(int mgflags)
333 {
334         string rmessage = "";
335         if (mgflags & PONG_STATUS_WAIT)
336                 rmessage = _("Press \"Throw Ball\" to start the match with the current players");
337         return rmessage;
338 }
339
340 // Required function, handle client events
341 int minigame_event_pong(entity minigame, string event, ...)
342 {
343         switch(event)
344         {
345                 case "activate":
346                         return false;
347                 case "key_pressed":
348                         switch ( ...(0,int) )
349                         {
350                                 case K_UPARROW:
351                                 case K_KP_UPARROW:
352                                         return true;
353                                 case K_DOWNARROW:
354                                 case K_KP_DOWNARROW:
355                                         return true;
356                         }
357                         return false;
358                 case "network_receive":
359                 {
360                         entity sent = ...(0,entity);
361                         int sf = ...(1,int);
362                         if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
363                         {
364                                 sent.pong_score = ReadLong();
365                         }
366                         else if ( sent.classname == "minigame" )
367                         {
368                                 if ( sf & MINIG_SF_UPDATE )
369                                 {
370                                         sent.message = pong_message(sent.minigame_flags);
371                                 }
372                         }
373                         return false;
374                 }
375                 case "menu_show":
376                 {
377                         HUD_MinigameMenu_CustomEntry(...(0,entity),_("Throw Ball"),"pong_throw");
378                         return false;
379                 }
380                 case "menu_click":
381                 {
382                         string cmd = ...(0,string);
383                         if( cmd == "pong_throw" && minigame.minigame_flags & PONG_STATUS_WAIT )
384                         {
385                                 minigame_cmd("throw");
386                         }
387                         return false;
388                 }
389         }
390
391         return false;
392 }
393 #endif