]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/minigame/pong.qc
Make pong actually playable
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / minigames / minigame / pong.qc
1 // send flags
2 const int PONG_SF_PLAYERSCORE  = MINIG_SF_CUSTOM;   // sent when reporting scores
3 const int PONG_SF_SINGLEPLAYER = MINIG_SF_CUSTOM<<1;// send minigame.pong_ai
4
5 // fields
6 .int       pong_ai;    // (minigame) when non-zero, singleplayer vs AI
7 .int       pong_score; // (minigame_player) number of goals
8 .entity pong_paddles[2];// (minigame) paddles
9 .float     pong_length;// (pong_paddle) size (0,1)
10
11 #ifdef SVQC
12
13 float autocvar_sv_minigames_pong_paddlesize = 0.3;
14 float autocvar_sv_minigames_pong_paddlespeed= 1;
15 float autocvar_sv_minigames_pong_ballwait   = 1;
16 float autocvar_sv_minigames_pong_ballspeed  = 1;
17
18 void pong_ball_think();
19
20 void pong_ball_thinkthrow()
21 {
22         float angle;
23         do
24                 angle = random()*M_PI*2;
25         while ( (angle > 0.44*M_PI && angle < 0.55*M_PI) || 
26                 (angle > 1.44*M_PI && angle < 1.55*M_PI) );
27         self.velocity_x = cos(angle)*autocvar_sv_minigames_pong_ballspeed;
28         self.velocity_y = sin(angle)*autocvar_sv_minigames_pong_ballspeed;
29         self.SendFlags |= MINIG_SF_UPDATE;
30         self.think = pong_ball_think;
31         self.nextthink = time;
32 }
33
34 void pong_reset_ball(entity ball)
35 {
36         ball.velocity = '0 0 0';
37         ball.origin = '0.5 0.5 0';
38         ball.SendFlags |= MINIG_SF_UPDATE;
39         ball.think = pong_ball_thinkthrow;
40         ball.nextthink = time + autocvar_sv_minigames_pong_ballwait;
41 }
42
43 void pong_add_score(entity minigame, int pteam)
44 {
45         if ( !minigame )
46                 return;
47         entity paddle = minigame.pong_paddles[pteam-1];
48         if ( paddle.realowner )
49         {
50                 paddle.realowner.pong_score++;
51                 paddle.realowner.SendFlags |= PONG_SF_PLAYERSCORE;
52         }
53 }
54
55 bool pong_goal(entity ball, int pteam)
56 {
57         entity paddle = ball.owner.pong_paddles[pteam-1];
58         if (!paddle)
59                 return false;
60         if ( ball.origin_y < paddle.origin_y-paddle.pong_length/2 ||
61                 ball.origin_y > paddle.origin_y+paddle.pong_length/2 )
62         {
63                 pong_add_score(ball.owner,minigame_next_team(pteam, 2));
64                 pong_reset_ball(ball);
65                 return true;
66         }
67         return false;
68 }
69
70 void pong_ball_think()
71 {
72         float think_speed = autocvar_sys_ticrate;
73         self.nextthink = time + think_speed;
74         
75         self.origin_x += self.velocity_x * think_speed;
76         self.origin_y += self.velocity_y * think_speed;
77         if ( self.origin_y <= 0 )
78         {
79                 self.origin_y = 0;
80                 self.velocity_y *= -1;
81         }
82         else if ( self.origin_y >= 1 )
83         {
84                 self.origin_y = 1;
85                 self.velocity_y *= -1;
86         }
87         
88         if ( self.origin_x <= 0 )
89         {
90                 if ( !pong_goal(self,2) )
91                 {
92                          self.origin_x = 0;
93                          self.velocity_x *= -1;
94                 }
95         }
96         else if ( self.origin_x >= 1 )
97         {
98                 if ( !pong_goal(self,1) )
99                 {
100                          self.origin_x = 1;
101                          self.velocity_x *= -1;
102                 }
103         }
104         
105         self.SendFlags |= MINIG_SF_UPDATE;
106 }
107
108 void pong_paddle_think()
109 {
110         float think_speed = autocvar_sys_ticrate;
111         self.nextthink = time + think_speed;
112         
113         if ( self.realowner.movement.x > 0 && self.origin_y > self.pong_length/2 )
114         {
115                 self.origin_y -= autocvar_sv_minigames_pong_paddlespeed * think_speed;
116                 if ( self.origin_y < 0 )
117                         self.origin_y = 0;
118                 self.SendFlags |= MINIG_SF_UPDATE;
119         }
120         else if ( self.realowner.movement.x < 0 && self.origin_y < 1-self.pong_length/2 )
121         {
122                 self.origin_y += autocvar_sv_minigames_pong_paddlespeed * think_speed;
123                 if ( self.origin_y > 1 )
124                         self.origin_y = 1;
125                 self.SendFlags |= MINIG_SF_UPDATE;
126         }
127                 
128 }
129
130 // required function, handle server side events
131 int minigame_event_pong(entity minigame, string event, ...)
132 {
133         switch (event)
134         {
135                 case "start":
136                 {
137                         entity ball = msle_spawn(minigame,"pong_ball");
138                         pong_reset_ball(ball);
139                         return true;
140                 }
141                 case "end":
142                         // nothing to do
143                         return false;
144                 case "join":
145                 {
146                         int pl_num = minigame_count_players(minigame);
147                         
148                         // Don't allow joining a single player match
149                         if ( (minigame.pong_ai) && pl_num > 0 )
150                                 return false;
151
152                         // Don't allow more than 2 players
153                         if(pl_num >= 2) { return false; }
154
155                         int pl_team = 1;
156                         // Get the right team
157                         if(minigame.minigame_players)
158                                 pl_team = minigame_next_team(minigame.minigame_players.team, 2);
159                         
160                         
161                         entity player = ...(0,entity);
162                         entity paddle = msle_spawn(minigame,"pong_paddle");// Note puddle isn't a typo
163                         paddle.pong_length = autocvar_sv_minigames_pong_paddlesize;
164                         paddle.origin_y = 0.5;
165                         paddle.origin_x = pl_team == 1 ? 0.99 : 0.01;
166                         paddle.think = pong_paddle_think;
167                         paddle.nextthink = time;
168                         paddle.team = pl_team;
169                         paddle.realowner = player;
170                         minigame.pong_paddles[pl_team-1] = paddle;
171
172                         // Team 1 by default
173                         return pl_team;
174                 }
175                 case "part":
176                         // todo
177                         return false;
178                 case "cmd":
179                         // nothing to do
180                         return false;
181                 case "network_send":
182                 {
183                         entity sent = ...(0,entity);
184                         int sf = ...(1,int);
185                         if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
186                         {
187                                 WriteByte(MSG_ENTITY,sent.pong_score);
188                         }
189                         else if ( sent.classname == "minigame" && (sf & PONG_SF_SINGLEPLAYER) )
190                         {
191                                 WriteByte(MSG_ENTITY,sent.pong_ai);
192                         }
193                         return false;
194                 }
195         }
196         return false;
197 }
198
199
200 #elif defined(CSQC)
201
202
203 // Required function, draw the game board
204 void minigame_hud_board_pong(vector pos, vector mySize)
205 {
206         minigame_hud_fitsqare(pos, mySize);
207         minigame_hud_simpleboard(pos,mySize,minigame_texture("pong/board"));
208         
209         entity e;
210         vector obj_pos;
211         vector ball_size = minigame_hud_denormalize_size('1 1 0' / 16,pos,mySize);
212         vector paddle_size;
213         FOREACH_MINIGAME_ENTITY(e)
214         {
215                 if ( e.classname == "pong_ball" )
216                 {
217                         obj_pos = minigame_hud_denormalize(e.origin,pos,mySize);
218                         minigame_drawpic_centered( obj_pos, minigame_texture("pong/ball"),
219                                         ball_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
220                 }
221                 else if ( e.classname == "pong_paddle" )
222                 {
223                         obj_pos = minigame_hud_denormalize(e.origin,pos,mySize);
224                         paddle_size = minigame_hud_denormalize_size(eX / 32 + eY*e.pong_length,pos,mySize);
225                         minigame_drawpic_centered( obj_pos, minigame_texture("pong/paddle"),
226                                         paddle_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
227                 }
228         }
229 }
230
231
232 // Required function, draw the game status panel
233 void minigame_hud_status_pong(vector pos, vector mySize)
234 {
235         HUD_Panel_DrawBg(1);
236         vector ts;
237         ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
238                 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
239         
240         pos_y += ts_y;
241         mySize_y -= ts_y;
242         
243         vector player_fontsize = hud_fontsize * 1.75;
244         ts_y = ( mySize_y - 2*player_fontsize_y ) / 2;
245         ts_x = mySize_x;
246         vector mypos;
247         vector tile_size = '48 48 0';
248
249         entity e;
250         FOREACH_MINIGAME_ENTITY(e)
251         {
252                 if ( e.classname == "minigame_player" )
253                 {
254                         mypos = pos;
255                         if ( e.team == 2 )
256                                 mypos_y  += player_fontsize_y + ts_y;
257                         minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
258                                 (e.minigame_playerslot ? GetPlayerName(e.minigame_playerslot-1) : _("AI")),
259                                 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
260                         
261                         mypos_y += player_fontsize_y;
262                         
263                         drawstring(mypos,ftos(e.pong_score),tile_size,
264                                            '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
265                 }
266         }
267 }
268
269
270 // Required function, handle client events
271 int minigame_event_pong(entity minigame, string event, ...)
272 {
273         switch(event)
274         {
275                 case "activate":
276                         return false;
277                 case "key_pressed":
278                         switch ( ...(0,int) )
279                         {
280                                 case K_UPARROW:
281                                 case K_KP_UPARROW:
282                                         return true;
283                                 case K_DOWNARROW:
284                                 case K_KP_DOWNARROW:
285                                         return true;
286                         }
287                         return false;
288                 case "network_receive":
289                 {
290                         entity sent = ...(0,entity);
291                         int sf = ...(1,int);
292                         if ( sent.classname == "minigame_player" && (sf & PONG_SF_PLAYERSCORE ) )
293                         {
294                                 sent.pong_score = ReadByte();
295                         }
296                         else if ( sent.classname == "minigame" && (sf & PONG_SF_SINGLEPLAYER) )
297                         {
298                                 int ai = ReadByte();
299                                 bool spawnai = ai && !sent.pong_ai;
300                                 sent.pong_ai = ai;
301                                 
302                                 if ( spawnai )
303                                 {
304                                         entity aiplayer = spawn();
305                                         aiplayer.classname = "minigame_player";
306                                         aiplayer.owner = minigame;
307                                         aiplayer.team = ai;
308                                         aiplayer.minigame_playerslot = 0;
309                                         aiplayer.minigame_autoclean = 1;
310                                         // todo aiplayer.think
311                                 }
312                         }
313                         return false;
314                 }
315                 case "menu_show":
316                 {
317                         HUD_MinigameMenu_CustomEntry(...(0,entity),_("Single Player"),"singleplayer");
318                         return false;
319                 }
320                 case "menu_click":
321                 {
322                         if ( ...(0,string) == "singleplayer" && !minigame.pong_ai )
323                         {
324                                 if ( minigame_count_players(minigame) == 1 )
325                                         minigame_cmd("singleplayer");
326                         }
327                         return false;
328                 }
329         }
330
331         return false;
332 }
333 #endif