1 /* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*-
2 *
3 * Quadra, an action puzzle game
4 * Copyright (C) 1998-2000 Ludus Design
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "quadra.h"
22
23 #include "config.h"
24 #include "SDL.h"
25 #ifdef UGS_LINUX
26 #include <pwd.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #endif
30 #ifdef WIN32
31 #include <shlobj.h>
32 #endif
33 #include <stdlib.h>
34 #include <exception>
35 #include "packets.h"
36 #include "types.h"
37 #include "net.h"
38 #include "video.h"
39 #include "cursor.h"
40 #include "image_png.h"
41 #include "palette.h"
42 #include "input.h"
43 #include "sound.h"
44 #include "sprite.h"
45 #include "bitmap.h"
46 #include "inter.h"
47 #include "random.h"
48 #include "game.h"
49 #include "bloc.h"
50 #include "color.h"
51 #include "menu.h"
52 #include "main.h"
53 #include "overmind.h"
54 #include "command.h"
55 #include "multi_player.h"
56 #include "stringtable.h"
57 #include "net_stuff.h"
58 #include "chat_text.h"
59 #include "recording.h"
60 #include "canvas.h"
61 #include "global.h"
62 #include "sons.h"
63 #include "cfgfile.h"
64 #include "fonts.h"
65 #include "res_compress.h"
66 #include "highscores.h"
67 #include "crypt.h"
68 #include "unicode.h"
69 #include "nglog.h"
70 #include "clock.h"
71 #include "net_server.h"
72 #include "update.h"
73
74 using std::max;
75 using std::min;
76
77 Color *color[9];
78 Font *fteam[8];
79 bool video_is_dumb = false;
80
set_fteam_color(const Palette & pal)81 void set_fteam_color(const Palette& pal) {
82 fteam[0]->colorize(pal, 255,125,0);
83 fteam[1]->colorize(pal, 0,225,255);
84 fteam[2]->colorize(pal, 255,0,0);
85 fteam[3]->colorize(pal, 255,0,255);
86 fteam[4]->colorize(pal, 255,255,0);
87 fteam[5]->colorize(pal, 0,255,0);
88 fteam[6]->colorize(pal, 40,40,255);
89 fteam[7]->colorize(pal, 170,170,170);
90 }
91
raw_draw_bloc_corner(const Video_bitmap & bit,int x,int y,Byte side,Color * col,Byte to[4])92 void raw_draw_bloc_corner(const Video_bitmap& bit, int x, int y, Byte side, Color* col, Byte to[4]) {
93 raw_draw_bloc(bit, x, y, side, col);
94 if(!(side&1) && !(side&2) && to[0]&2 && to[1]&1) {
95 bit.put_pel(x, y, col->shade(7));
96 bit.put_pel(x+1, y, col->shade(6));
97 bit.put_pel(x, y+1, col->shade(6));
98 bit.put_pel(x+2, y, col->shade(5));
99 bit.put_pel(x+1, y+1, col->shade(5));
100 bit.put_pel(x, y+2, col->shade(5));
101 }
102 if(!(side&4) && !(side&2) && to[2]&2 && to[1]&4) {
103 bit.put_pel(x+17, y, col->shade(1+2));
104 bit.put_pel(x+16, y, col->shade(2+1));
105 bit.put_pel(x+17, y+1, col->shade(2+2));
106 bit.put_pel(x+15, y, col->shade(3));
107 bit.put_pel(x+16, y+1, col->shade(3+1));
108 bit.put_pel(x+17, y+2, col->shade(3+2));
109 }
110 if(!(side&1) && !(side&8) && to[0]&8 && to[3]&1) {
111 bit.put_pel(x, y+17, col->shade(7));
112 bit.put_pel(x+1, y+17, col->shade(6));
113 bit.put_pel(x, y+16, col->shade(6));
114 bit.put_pel(x+2, y+17, col->shade(5));
115 bit.put_pel(x+1, y+16, col->shade(5));
116 bit.put_pel(x, y+15, col->shade(5));
117 }
118 if(!(side&4) && !(side&8) && to[2]&8 && to[3]&4) {
119 bit.put_pel(x+17, y+17, col->shade(1));
120 bit.put_pel(x+16, y+17, col->shade(2));
121 bit.put_pel(x+17, y+16, col->shade(2));
122 bit.put_pel(x+15, y+17, col->shade(3));
123 bit.put_pel(x+16, y+16, col->shade(3));
124 bit.put_pel(x+17, y+15, col->shade(3));
125 }
126 }
127
raw_draw_bloc(const Video_bitmap & bit,int x,int y,Byte side,Color * col)128 void raw_draw_bloc(const Video_bitmap& bit, int x, int y, Byte side, Color* col) {
129 int tx,tl,rx=0,ry=0,rw=18,rh=18;
130 if(side&1) {
131 bit.vline(x, y, 18, col->shade(7));
132 bit.vline(x+1, y, 18, col->shade(6));
133 bit.vline(x+2, y, 18, col->shade(5));
134 rx=3; rw-=3;
135 }
136 if(side&2) {
137 bit.hline(y, x, 18, col->shade(7));
138 if(side&1)
139 tx = x+1;
140 else
141 tx = x;
142 bit.hline(y+1, tx, x-tx+18, col->shade(6));
143 if(side&1)
144 tx = x+2;
145 else
146 tx = x;
147 bit.hline(y+2, tx, x-tx+18, col->shade(5));
148 ry=3; rh-=3;
149 }
150 if(side&4) {
151 bit.vline(x+17, y, 18, col->shade(1));
152 if(side&2)
153 tx = y+1;
154 else
155 tx = y;
156 bit.vline(x+16, tx, y-tx+18, col->shade(2));
157 if(side&2)
158 tx = y+2;
159 else
160 tx = y;
161 bit.vline(x+15, tx, y-tx+18, col->shade(3));
162 rw-=3;
163 }
164 if(side&8) {
165 bit.hline(y+17, x, 18, col->shade(1));
166 if(side&1)
167 tx = x+1;
168 else
169 tx = x;
170 tl = x-tx+18;
171 if(side&4)
172 tl--;
173 bit.hline(y+16, tx, tl, col->shade(2));
174 if(side&1)
175 tx = x+2;
176 else
177 tx = x;
178 tl = x-tx+18;
179 if(side&4)
180 tl -= 2;
181 bit.hline(y+15, tx, tl, col->shade(3));
182 rh-=3;
183 }
184 Byte main_color=col->shade(4);
185 for(int i=0; i<rh; i++)
186 bit.hline(y+ry+i, x+rx, rw, main_color);
187 }
188
raw_small_draw_bloc(const Video_bitmap & bit,int x,int y,Byte side,Color * col)189 void raw_small_draw_bloc(const Video_bitmap& bit, int x, int y, Byte side, Color* col) {
190 int i,rx=0,ry=0,rw=6,rh=6;
191 if(side&1) {
192 bit.vline(x, y, 6, col->shade(7));
193 rx++; rw--;
194 }
195 if(side&2) {
196 bit.hline(y, x, 6, col->shade(7));
197 ry++; rh--;
198 }
199 if(side&4) {
200 bit.vline(x+5, y, 6, col->shade(1));
201 rw--;
202 }
203 if(side&8) {
204 bit.hline(y+5, x, 6, col->shade(1));
205 rh--;
206 }
207 for(i=0; i<rh; i++)
208 bit.hline(y+ry+i, x+rx, rw, col->shade(4));
209 }
210
Player_check_link(Canvas * c)211 Player_check_link::Player_check_link(Canvas *c): Player_base(c) {
212 anim = 0;
213 tombe = 0;
214 //Reset Canvas::moved
215 int i, j;
216 for(j=0; j<36; j++)
217 for(i=0; i<18; i++)
218 canvas->moved[j][i]=false;
219 }
220
step()221 void Player_check_link::step() {
222 Player_base::step();
223 int i, j, k = 1;
224 if(anim == 0) {
225 while(k) { // rescan plusieurs pass
226 k = 0;
227 for(j = 31; j >= 0; j--) { // pour chaque rangee :
228 for(i = 4; i < 14; i++) // scan gauche a droite ->
229 if((canvas->block[j][i]&8) && (!canvas->tmp[j][i]) && canvas->tmp[j+1][i]) {
230 fill_bloc(i,j);
231 k = 1;
232 }
233 }
234 }
235 anim++;
236 } else {
237 anim = 0;
238 k = 0;
239 //Temp move array
240 bool moved[36][18];
241 for(j=0; j<36; j++)
242 for(i=0; i<18; i++)
243 moved[j][i]=false;
244 for(j = 31; j >= 0; j--)
245 for(i = 4; i < 14; i++)
246 if(canvas->occupied[j][i] && (!canvas->tmp[j][i])) {
247 k = 1;
248 canvas->block[j+1][i] = canvas->block[j][i];
249 canvas->occupied[j+1][i] = canvas->occupied[j][i];
250 moved[j+1][i] = true;
251 canvas->blinded[j+1][i] = canvas->blinded[j][i];
252 canvas->bflash[j+1][i] = canvas->bflash[j][i];
253 canvas->dirted[j+1][i] = 2;
254 canvas->block[j][i] = 0;
255 canvas->occupied[j][i] = false;
256 canvas->blinded[j][i] = 0;
257 canvas->bflash[j][i] = 0;
258 canvas->dirted[j][i] = 2;
259 }
260 if(k) {
261 tombe++;
262 //Some blocks moved, overwrite Canvas::moved
263 for(j=0; j<36; j++) {
264 bool movement_in_line=false;
265 for(i=0; i<18; i++)
266 if(moved[j][i])
267 movement_in_line=true;
268 if(movement_in_line)
269 for(i=0; i<18; i++)
270 canvas->moved[j][i]=moved[j][i];
271 }
272 }
273 if(!k) {
274 if(tombe) {
275 i = -500 + tombe * 50;
276 if(!canvas->islocal())
277 i -= 1000;
278 if(i>0)
279 i=0;
280 if(canvas->inter) {
281 play_sound(sons.depose4, i, -1, 11000+ugs_random.rnd(127) - (canvas->complexity<<8));
282 }
283 }
284 ret();//exec(new Player_check_line(canvas));
285 }
286 }
287 }
288
fill_bloc(Byte x,Byte y)289 void Player_check_link::fill_bloc(Byte x, Byte y) {
290 canvas->tmp[y][x] = 1;
291 if((!(canvas->block[y][x]&8)) && (!canvas->tmp[y+1][x]))
292 fill_bloc(x, y+1);
293 if((!(canvas->block[y][x]&1)) && (!canvas->tmp[y][x-1]))
294 fill_bloc(x-1, y);
295 if((!(canvas->block[y][x]&4)) && (!canvas->tmp[y][x+1]))
296 fill_bloc(x+1, y);
297 if((!(canvas->block[y][x]&2)) && (!canvas->tmp[y-1][x]))
298 fill_bloc(x, y-1);
299 }
300
Player_flash_lines(Canvas * c)301 Player_flash_lines::Player_flash_lines(Canvas *c): Player_base(c) {
302 anim = 0;
303 canvas->color_flash = 255;
304 if(canvas->inter) {
305 int vo = -300 - ugs_random.rnd(255);
306 if(!canvas->islocal())
307 vo -= 1000;
308 play_sound(sons.flash, vo, -1, 11000+ugs_random.rnd(127) - (canvas->complexity<<8));
309 }
310 }
311
step()312 void Player_flash_lines::step() {
313 Player_base::step();
314 int i;
315 if(anim < 16) {
316 if((anim>>1)&1)
317 canvas->color_flash = 200;
318 else
319 canvas->color_flash = 255;
320 anim++;
321 } else {
322 canvas->color_flash = 0;
323 canvas->clear_tmp();
324 anim = 0;
325 for(i = 0; i < 20; i++)
326 canvas->flash[i] = 0;
327 exec(new Player_check_link(canvas));
328 }
329 }
330
step()331 void Player_base::step() {
332 if(canvas->idle<3)
333 canvas->gone_time=0;
334 if(game && game->net_version()<23 && !game->paused && (canvas->idle==0 || canvas->idle==1))
335 canvas->stats[CS::PLAYING_TIME].add(1);
336 if(!((overmind.framecount+1)&127)) {
337 int pm=0;
338 int time=canvas->stats[CS::PLAYING_TIME].get_value();
339 if(time)
340 pm=(int)(canvas->stats[CS::SCORE].get_value()*6000.0/time);
341 canvas->stats[CS::PPM].set_value(pm);
342 pm=0;
343 if(time)
344 pm=(int)(canvas->stats[CS::COMPTETOT].get_value()*6000.0/time);
345 canvas->stats[CS::BPM].set_value(pm);
346 }
347 if(game && !game->paused && !(overmind.framecount&15)) {
348 int i, j;
349 for(j=0; j<36; j++)
350 for(i=0; i<18; i++)
351 if(canvas->blinded[j][i]) {
352 if(!--canvas->blinded[j][i]) {
353 canvas->bflash[j][i]=24;
354 canvas->dirted[j][i]=2;
355 }
356 }
357 }
358 }
359
calc_by(int py) const360 Byte Player_base::calc_by(int py) const {
361 return (((py+15+(12*18<<4))>>4)+17)/18;
362 }
363
check_gone()364 bool Player_base::check_gone() {
365 {
366 Packet_gone *p=(Packet_gone *) game->peekpacket(P_GONE);
367 if(p && p->player==canvas->num_player) {
368 bool chat_msg=p->chat_msg;
369 game->removepacket();
370 msgbox("Player_base::check_gone: Player %i is gone\n", canvas->num_player);
371 exec(new Player_gone(canvas, chat_msg));
372 ret();
373 return true;
374 }
375 }
376 if(game->abort || game->terminated) {
377 if(canvas->islocal()) {
378 msgbox("Player_base::check_gone: local player %i gone\n", canvas->num_player);
379 Player_base *pb;
380 if(game->terminated || game->server || canvas->idle==2 || canvas->dying || game->delay_start==500) {
381 Packet_clientgone p;
382 p.player=canvas->num_player;
383 p.chat_msg=!game->terminated;
384 net->sendtcp(&p);
385 pb = new Player_gone(canvas, !game->terminated);
386 }
387 else {
388 //Player_dead::Player_dead sends the packet
389 pb = new Player_dead(canvas, true);
390 }
391 exec(pb);
392 ret();
393 return true;
394 }
395 }
396 return false;
397 }
398
check_state()399 void Player_base::check_state() {
400 Packet_state *p=(Packet_state *) game->peekpacket(P_STATE);
401 if(p && p->player==canvas->num_player) {
402 msgbox("Player_base::check_state: player=%i, state=%i\n", p->player, p->state);
403 canvas->state=(Canvas::State) p->state;
404 game->removepacket();
405 }
406 }
407
remove_bonus()408 void Player_base::remove_bonus() {
409 canvas->should_remove_bonus=false;
410 canvas->bonus=0;
411 if(canvas->islocal()) {
412 Packet_clientremovebonus p;
413 p.player=canvas->num_player;
414 net->sendtcp(&p);
415 }
416 }
417
check_bonus()418 void Player_base::check_bonus() {
419 Packet_removebonus *p=(Packet_removebonus *) game->peekpacket(P_REMOVEBONUS);
420 if(p && p->player==canvas->num_player) {
421 remove_bonus();
422 game->removepacket();
423 }
424 }
425
play_sound(Sample * s,int vol,int pan,int freq)426 void Player_base::play_sound(Sample *s, int vol, int pan, int freq) {
427 if(time_control == TIME_SLOW)
428 freq = freq*2/3;
429 if(time_control == TIME_FAST)
430 freq = freq*3/2;
431 s->play(vol, pan, freq);
432 }
433
Player_text_scroll(Canvas * c,const char * texte,int xoffset,int yoffset)434 Player_text_scroll::Player_text_scroll(Canvas *c, const char *texte, int xoffset, int yoffset): Player_base(c) {
435 (void)new Zone_combo(&combo, canvas, texte, canvas->x + xoffset, canvas->y + 330 + yoffset);
436 if(game)
437 game->net_list.add_watch(this);
438 }
439
~Player_text_scroll()440 Player_text_scroll::~Player_text_scroll() {
441 if(game) // if ALT-F4: game is destroyed before this module... grr...
442 game->net_list.remove_watch(this);
443 }
444
notify()445 void Player_text_scroll::notify() {
446 if(!game->net_list.get(canvas->num_player)) {
447 stop();
448 }
449 }
450
step()451 void Player_text_scroll::step() {
452 if(combo) {
453 combo->y-=2;
454 if(combo->y < canvas->y || canvas->inter==NULL || canvas->small_watch) {
455 stop();
456 }
457 }
458 }
459
stop()460 void Player_text_scroll::stop() {
461 if(combo) {
462 delete combo;
463 combo=NULL;
464 }
465 ret();
466 }
467
Player_add_bonus(Canvas * c)468 Player_add_bonus::Player_add_bonus(Canvas *c): Player_base(c) {
469 anim = 0;
470 first_done=false;
471 if(canvas->inter) {
472 int vo = -600;
473 if(!canvas->islocal())
474 vo -= 1000;
475 play_sound(sons.bonus1, vo, -1, 11000+ugs_random.rnd(511));
476 }
477 nb=canvas->bonus;
478 }
479
~Player_add_bonus()480 Player_add_bonus::~Player_add_bonus() {
481 }
482
step()483 void Player_add_bonus::step() {
484 Player_base::step();
485 int y, i, j;
486 bool old_net_version=false;
487 if(game->net_version()<23)
488 old_net_version=true;
489 if(nb && canvas->bonus) {
490 if(anim == 0) {
491 for(j = 0; j < 31; j++)
492 for(i = 4; i < 14; i++) {
493 canvas->block[j][i] = canvas->block[j+1][i];
494 canvas->occupied[j][i] = canvas->occupied[j+1][i];
495 canvas->blinded[j][i] = canvas->blinded[j+1][i];
496 canvas->bflash[j][i] = canvas->bflash[j+1][i];
497 canvas->dirted[j+1][i] = 2;
498 }
499 Word hole_pos=canvas->bon[0].hole_pos;
500 if(old_net_version)
501 hole_pos=(1 << (9-(canvas->bon[0].x-4)));
502 Byte top_bottom_add=0;
503 if(!first_done || old_net_version)
504 top_bottom_add |= 2; //Add top
505 if(canvas->bon[0].final || old_net_version)
506 top_bottom_add |= 8; //Add bottom
507 for(i = 4; i < 14; i++) {
508 canvas->block[31][i] = top_bottom_add + (canvas->bon[0].color<<4);
509 canvas->occupied[31][i] = true;
510 }
511 for(i = 4; i < 14; i++) {
512 if(hole_pos&512) {
513 canvas->block[31][i] = 0;
514 canvas->occupied[31][i] = false;
515 if(first_done && canvas->occupied[30][i]) {
516 //First line done in this packet and corresponding
517 // unit block above is not a hole: add bottom
518 canvas->block[30][i] |= 8;
519 }
520 if(i > 4) {
521 if(canvas->occupied[31][i-1])
522 canvas->block[31][i-1] |= 4;
523 if(canvas->occupied[31][4])
524 canvas->block[31][4] |= 1;
525 }
526 if(i < 14) {
527 canvas->block[31][i+1] |= 1;
528 canvas->block[31][14] |= 4;
529 }
530 }
531 else {
532 if(first_done && !canvas->occupied[30][i]) {
533 //First line done in this packet and corresponding
534 // unit block above is a hole: add top
535 canvas->block[31][i] |= 2;
536 }
537 }
538 canvas->blinded[31][i] = 0;
539 canvas->bflash[31][i] = 0;
540 hole_pos <<= 1;
541 }
542 first_done=true;
543 if(canvas->bon[0].final)
544 first_done=false;
545 }
546 anim++;
547 if(anim == 7) {
548 for(y = 1; y < canvas->bonus; y++)
549 canvas->bon[y-1] = canvas->bon[y];
550 canvas->bonus--;
551 nb--;
552 anim=0;
553 }
554 } else {
555 for(i = 4; i < 14; i++)
556 if(canvas->occupied[0][i])
557 canvas->block[0][i] |= 2; // clip top
558 canvas->clear_tmp();
559 call(new Player_check_link(canvas));
560 ret();
561 }
562 }
563
Player_change_level(Canvas * c)564 Player_change_level::Player_change_level(Canvas *c): Player_base(c) {
565 canvas->myself->pause();
566 }
567
~Player_change_level()568 Player_change_level::~Player_change_level() {
569 canvas->myself->unpause();
570 }
571
init()572 void Player_change_level::init() {
573 Player_base::init();
574 canvas->change_level_single();
575 if(canvas->inter) {
576 canvas->inter->font->colorize(*canvas->pal, 255, 255, 255);
577 video->need_paint = 2;
578 }
579 int nframe=16;
580 if(time_control == TIME_SLOW)
581 nframe = nframe*3/2;
582 if(time_control == TIME_FAST)
583 nframe = 3;
584 call(new Fade_to(*canvas->pal, noir, nframe));
585 call(new Wait_time(14)); // so that the 'level up' doesn't jerk
586 ret();
587 }
588
Player_level_up(Canvas * c)589 Player_level_up::Player_level_up(Canvas *c): Player_base(c) {
590 canvas->level_up = 220;
591 if(canvas->islocal() || (canvas->inter && !canvas->small_watch))
592 Player_base::play_sound(sons.levelup, -200-ugs_random.rnd(127), 0, 11000);
593 }
594
init()595 void Player_level_up::init() {
596 Player_base::init();
597 if(game->single) { // && (canvas->level-1<=9 || game->net_version()<23)) {
598 call(new Player_change_level(canvas));
599 int nframe=16;
600 if(time_control == TIME_SLOW)
601 nframe = nframe*3/2;
602 if(time_control == TIME_FAST)
603 nframe = 3;
604 call(new Fade_to(noir, *canvas->pal, nframe));
605 }
606 }
607
step()608 void Player_level_up::step() {
609 Player_base::step();
610 canvas->level_up--;
611 if(canvas->level_up < 0) {
612 canvas->level_up = 0;
613 ret();
614 }
615 }
616
Player_check_line(Canvas * c)617 Player_check_line::Player_check_line(Canvas *c): Player_base(c) {
618 }
619
step()620 void Player_check_line::step() {
621 Player_base::step();
622 int co = check_nb_line();
623 if(co) {
624 canvas->depth += co;
625 canvas->complexity++;
626 check_clean();
627 call(new Player_flash_lines(canvas));
628 } else {
629 if(canvas->idle < 2) { // if alive
630 //Copy hole positions into moved array so that give_line
631 // and Net_list::send can use them
632 int i, j;
633 for(j=0; j<36; j++)
634 for(i=0; i<18; i++)
635 canvas->moved[j][i]=hole_pos[j][i];
636 canvas->give_line();
637 } else { // if dead, useless
638 canvas->depth = 0;
639 canvas->complexity=0;
640 canvas->send_for_clean=0;
641 }
642 ret();
643 }
644 }
645
check_nb_line()646 int Player_check_line::check_nb_line() {
647 int co = 0, i, j;
648 for(j = 12; j < 32; j++) {
649 for(i = 4; i < 14; i++)
650 if(!canvas->occupied[j][i])
651 break;
652 if(i == 14) {
653 if(co < 20)
654 canvas->flash[co] = j;
655 for(i = 4; i < 14; i++) {
656 if(canvas->occupied[j-1][i] && !(canvas->block[j-1][i]&8)) {
657 canvas->block[j-1][i] |= 8;
658 canvas->dirted[j-1][i] = 2;
659 }
660 hole_pos[canvas->depth+co][i]=canvas->moved[j][i];
661 canvas->block[j][i] = 0;
662 canvas->occupied[j][i] = false;
663 canvas->blinded[j][i] = 0;
664 canvas->bflash[j][i] = 0;
665 canvas->dirted[j][i] = 2;
666 if(canvas->occupied[j+1][i] && !(canvas->block[j+1][i]&2)) {
667 canvas->block[j+1][i] |= 2;
668 canvas->dirted[j+1][i] = 2;
669 }
670 }
671 co++;
672 }
673 }
674 return co;
675 }
676
check_clean()677 void Player_check_line::check_clean() {
678 int i, j;
679 for(j = 12; j < 32; j++)
680 for(i = 4; i < 14; i++)
681 if(canvas->occupied[j][i])
682 return;
683 canvas->add_text_scroller("Clean Canvas!!");
684 if(game->net_version()<23)
685 canvas->stats[CS::SCORE].add(5000);
686 canvas->send_for_clean=true;
687 }
688
Player_process_key(Canvas * c)689 Player_process_key::Player_process_key(Canvas *c): Player_base(c) {
690 hold_left = hold_right = 0;
691 last_video_frame = last_overmind_frame = 0;
692 canvas->idle = 1;
693 canvas->state = Canvas::PLAYING;
694 block_rotated=0;
695 time_held=0;
696 }
697
rotate_left()698 bool Player_base::rotate_left() {
699 bool ret=false;
700 int t, i;
701 t = (canvas->bloc->rot-1)&3;
702 if(!canvas->collide(canvas->bloc->bx, canvas->bloc->by, t)) {
703 ret=true;
704 canvas->bloc->rot = t;
705 if(canvas->inter) {
706 i = (canvas->bloc->bx-9) * 300;
707 play_sound(sons.drip, -200-ugs_random.rnd(127), i, 10500+ugs_random.rnd(1023));
708 }
709 }
710 canvas->set_bit(8);
711 return ret;
712 }
713
rotate_right(bool twice)714 bool Player_base::rotate_right(bool twice) {
715 bool ret=false;
716 int t, i;
717 int inc=1;
718 if(twice)
719 inc=2;
720 t = (canvas->bloc->rot+inc)&3;
721 if(!canvas->collide(canvas->bloc->bx, canvas->bloc->by, t)) {
722 ret=true;
723 canvas->bloc->rot = t;
724 if(canvas->inter) {
725 i = (canvas->bloc->bx-9) * 300;
726 play_sound(sons.drip, -200-ugs_random.rnd(127), i, 10500+ugs_random.rnd(1023));
727 }
728 }
729 if(twice)
730 canvas->set_bit(32);
731 else
732 canvas->set_bit(16);
733 return ret;
734 }
735
move_down()736 void Player_base::move_down() {
737 int t;
738 t = calc_by(canvas->bloc->y+canvas->down_speed);
739 if(!canvas->collide(canvas->bloc->bx, t, canvas->bloc->rot)) {
740 canvas->bloc->y += canvas->down_speed;
741 canvas->bloc->by = calc_by(canvas->bloc->y);
742 } else { // is used to "settle" the block so that it drops
743 canvas->bloc->y = ((canvas->bloc->by-12)*18<<4)-1;
744 }
745 canvas->set_bit(1);
746 }
747
drop_down()748 void Player_base::drop_down() {
749 while(!canvas->check_collide(canvas->bloc, canvas->bloc->bx, canvas->bloc->by+1, canvas->bloc->rot))
750 canvas->bloc->by++;
751 canvas->bloc->calc_xy();
752 canvas->set_bit(64);
753 }
754
move_left()755 bool Player_base::move_left() {
756 bool ret=false;
757 if(!canvas->collide(canvas->bloc->bx-1, canvas->bloc->by, canvas->bloc->rot)) {
758 ret=true;
759 canvas->bloc->bx--;
760 }
761 canvas->set_bit(2);
762 return ret;
763 }
764
move_right()765 bool Player_base::move_right() {
766 bool ret=false;
767 if(!canvas->collide(canvas->bloc->bx+1, canvas->bloc->by, canvas->bloc->rot)) {
768 ret=true;
769 canvas->bloc->bx++;
770 }
771 canvas->set_bit(4);
772 return ret;
773 }
774
playback_control()775 void Player_process_key::playback_control() {
776 Byte r = playback->get_byte();
777 if(r & 8)
778 rotate_left();
779 if(r & 16)
780 rotate_right();
781 if(r & 1)
782 move_down();
783 if(r & 2)
784 move_left();
785 if(r & 4)
786 move_right();
787 }
788
keyboard_control()789 void Player_process_key::keyboard_control() {
790 if(last_video_frame == video->framecount && overmind.framecount - last_overmind_frame > 3)
791 return;
792 if(last_video_frame != video->framecount) {
793 last_video_frame = video->framecount;
794 last_overmind_frame = overmind.framecount;
795 }
796
797 bool bouge_left = false;
798 bool bouge_right = false;
799 bool auto_bouge = false;
800 if(canvas->check_key(2) & RELEASED) {
801 if(rotate_left())
802 block_rotated++;
803 else {
804 if(canvas->collide_side_only) {
805 int inc;
806 if(canvas->bloc->bx<5)
807 inc=1;
808 else
809 inc = -1;
810 if(!canvas->collide(canvas->bloc->bx+inc, canvas->bloc->by, canvas->bloc->rot)) {
811 canvas->bloc->bx+=inc;
812 if(rotate_left()) {
813 auto_bouge=true;
814 block_rotated++;
815 }
816 else
817 canvas->bloc->bx-=inc;
818 }
819 }
820 }
821 canvas->clear_key(2);
822 }
823 if(canvas->check_key(4) & RELEASED) {
824 if(rotate_right())
825 block_rotated++;
826 else {
827 if(canvas->collide_side_only) {
828 int inc;
829 if(canvas->bloc->bx<5)
830 inc=1;
831 else
832 inc = -1;
833 if(!canvas->collide(canvas->bloc->bx+inc, canvas->bloc->by, canvas->bloc->rot)) {
834 canvas->bloc->bx+=inc;
835 if(rotate_right()) {
836 auto_bouge=true;
837 block_rotated++;
838 }
839 else
840 canvas->bloc->bx-=inc;
841 }
842 }
843 }
844 canvas->clear_key(4);
845 }
846 if(canvas->check_key(5) & RELEASED) {
847 if(rotate_right(true))
848 block_rotated++;
849 else {
850 if(canvas->collide_side_only) {
851 int inc;
852 if(canvas->bloc->bx<5)
853 inc=1;
854 else
855 inc = -1;
856 if(!canvas->collide(canvas->bloc->bx+inc, canvas->bloc->by, canvas->bloc->rot)) {
857 canvas->bloc->bx+=inc;
858 if(rotate_right(true)) {
859 auto_bouge=true;
860 block_rotated++;
861 }
862 else
863 canvas->bloc->bx-=inc;
864 }
865 }
866 }
867 canvas->clear_key(5);
868 }
869 if(canvas->check_key(3) & PRESSED || canvas->check_key(3) & RELEASED) {
870 move_down();
871 canvas->unrelease_key(3);
872 }
873 if(canvas->check_key(6) & PRESSED) {
874 drop_down();
875 canvas->clear_key(6);
876 }
877 if(canvas->check_key(0) & PRESSED || canvas->check_key(0) & RELEASED) {
878 if(!bouge_left) {
879 bouge_left = true;
880 canvas->unrelease_key(0);
881 }
882 }
883 if(canvas->check_key(1) & PRESSED || canvas->check_key(1) & RELEASED) {
884 if(!bouge_right) {
885 bouge_right = true;
886 canvas->unrelease_key(1);
887 }
888 }
889 if(bouge_left && !bouge_right && !auto_bouge) {
890 if(hold_left < 2) {
891 if(hold_left == 0)
892 hold_left = canvas->h_repeat_delay+10; // initial repeating delay
893 else
894 hold_left = canvas->h_repeat_delay;
895 if(!move_left())
896 hold_left=1;
897 } else
898 hold_left--;
899 } else
900 hold_left = 0;
901 if(bouge_right && !bouge_left && !auto_bouge) {
902 if(hold_right < 2) {
903 if(hold_right == 0)
904 hold_right = canvas->h_repeat_delay+10;
905 else
906 hold_right = canvas->h_repeat_delay;
907 if(!move_right())
908 hold_right=1;
909 } else
910 hold_right--;
911 } else
912 hold_right = 0;
913 }
914
init()915 void Player_process_key::init() {
916 Player_base::init();
917 // canvas->clear_key(2);
918 // canvas->clear_key(4);
919 // canvas->clear_key(5);
920 canvas->clear_key(6);
921 if(!canvas->continuous)
922 canvas->clear_key(3);
923 canvas->start_moves();
924 }
925
step()926 void Player_process_key::step() {
927 Player_base::step();
928 if(check_gone())
929 return;
930
931 if(canvas->should_remove_bonus)
932 remove_bonus();
933
934 int t, i;
935 if(game->survivor)
936 if(check_first_frag())
937 return;
938 if(game->paused)
939 return;
940
941 time_held++;
942 canvas->start_byte();
943
944 if(playback)
945 playback_control();
946 else {
947 if(canvas->inter) // if the canvas is hidden, don't listen to keys
948 keyboard_control();
949 }
950
951 canvas->write_byte();
952
953 t = ((canvas->bloc->bx-4)<<4)*18; // compute the position where the block should be
954 int nx = canvas->bloc->x; // new position 'x' to obtain
955 if(nx < t) {
956 nx += canvas->side_speed;
957 if(nx > t) // if past the position
958 nx = t;
959 }
960 if(nx > t) {
961 nx -= canvas->side_speed;
962 if(nx < t) // if past the position
963 nx = t;
964 }
965 t = calc_by(canvas->bloc->y-(17<<4)-15);
966 if(!canvas->collide(canvas->bloc->bx, t, canvas->bloc->rot))
967 canvas->bloc->x = nx; // accepte le deplacement 'smooth' seulement s'il ne passe pas par-dessus un bloc
968 t = calc_by(canvas->bloc->y+canvas->speed);
969 if(canvas->collide(canvas->bloc->bx, t, canvas->bloc->rot)) {
970 canvas->send_p_moves();
971 Packet_clientstampblock p;
972 p.rotate=canvas->bloc->rot;
973 p.x=canvas->bloc->bx;
974 p.y=canvas->bloc->by;
975 p.player=canvas->num_player;
976 if(game->net_version()<23) {
977 i = max(overmind.framecount - canvas->frame_start - 50, static_cast<unsigned int>(0));
978 p.score=max(0, 100 - i) >> 1;
979 }
980 else
981 p.score=0;
982 p.date = canvas->watch_date++;
983 if(block_rotated>255)
984 block_rotated=255;
985 p.block_rotated=block_rotated;
986 if(time_held>65535)
987 time_held=65535;
988 p.time_held=time_held;
989 net->sendtcp(&p);
990 exec(new Player_stamp(canvas, &p));
991 return;
992 }
993 else {
994 canvas->bloc->y+=canvas->speed;
995 canvas->bloc->by = calc_by(canvas->bloc->y);
996 if(canvas->shadow)
997 canvas->calc_shadow();
998 }
999 }
1000
check_first_frag()1001 bool Player_process_key::check_first_frag() {
1002 if(game->net_list.syncpoint==Canvas::PLAYING)
1003 return false;
1004 if(game->net_list.syncpoint==Canvas::LAST)
1005 return false;
1006 //If syncing toward WAITFORWINNER or WAITFORRESTART,
1007 // go to first_frag state
1008 canvas->send_p_moves();
1009 Packet_clientfirst_frag pf;
1010 pf.player = canvas->num_player;
1011 net->sendtcp(&pf);
1012 exec(new Player_first_frag(canvas));
1013 return true;
1014 }
1015
step()1016 void Player_get_next::step() {
1017 Player_base::step();
1018 while(!canvas->bloc)
1019 shift_next();
1020
1021 canvas->set_next();
1022 canvas->frame_start = overmind.framecount;
1023 ret();
1024 }
1025
shift_next()1026 void Player_get_next::shift_next() {
1027 canvas->bloc = canvas->next3;
1028 canvas->next3 = canvas->next2;
1029 canvas->next2 = canvas->next;
1030 Byte the_next;
1031 if(game->net_version()>=23)
1032 the_next=canvas->rnd.rnd()%7;
1033 else
1034 the_next=canvas->rnd.crap_rnd()%7;
1035 canvas->next = new Bloc(the_next, -1, 7, 10);
1036 }
1037
Player_normal(Canvas * c)1038 Player_normal::Player_normal(Canvas *c): Player_base(c) {
1039 canvas->idle = 1;
1040 canvas->state = Canvas::PLAYING;
1041 }
1042
step()1043 void Player_normal::step() {
1044 Player_base::step();
1045
1046 if(!canvas->bloc)
1047 call(new Player_get_next(canvas));
1048 else {
1049 //If this is a local player, check death condition
1050 if(canvas->islocal()) {
1051 if(canvas->collide(canvas->bloc->bx, canvas->bloc->by, canvas->bloc->rot)) {
1052 bool tg=false;
1053 if(game->single)
1054 tg=true;
1055 call(new Player_dead(canvas));
1056 }
1057 else {
1058 call(new Player_process_key(canvas));
1059 }
1060 }
1061 else {
1062 //Not local, wait for next block or death
1063 call(new Player_wait_block(canvas));
1064 }
1065 }
1066 }
1067
Player_dead(Canvas * c,bool tg)1068 Player_dead::Player_dead(Canvas *c, bool tg): Player_base(c), then_gone(tg) {
1069 canvas->dying=true;
1070 for(j=0; j<36; j++)
1071 for(i=0; i<18; i++) {
1072 if(canvas->blinded[j][i]) {
1073 canvas->bflash[j][i] = 24;
1074 canvas->dirted[j][i] = 2;
1075 }
1076 canvas->blinded[j][i] = 0;
1077 }
1078 if(canvas->islocal()) {
1079 //If local, tell server that we died
1080 Packet_clientdead p;
1081 p.player=canvas->num_player;
1082 p.then_gone=then_gone;
1083 net->sendtcp(&p);
1084 }
1085 Canvas* fragger = NULL;
1086 if(canvas->last_attacker != 255)
1087 fragger = game->net_list.get(canvas->last_attacker);
1088 if(fragger)
1089 msgbox("Player_dead::Player_dead: %s a ete fragger par %s. attacks[]=", canvas->name, fragger->name);
1090 else
1091 msgbox("Player_dead::Player_dead: %s est mort sans frag. attacks[]=", canvas->name);
1092 for(int i2=0; i2<MAXPLAYERS; i2++)
1093 msgbox("%i,", canvas->attacks[i2]);
1094 msgbox("\n");
1095 if(fragger) {
1096 fragger->stats[CS::FRAG].add(1);
1097 sprintf(st, "%s fragged %s!", fragger->name, c->name);
1098 message(fragger->color, st);
1099
1100 couleur = fragger->color<<4; // couleur du 'fragger'
1101 sprintf(st, "Fragged %s!", c->name);
1102 fragger->add_text_scroller(st);
1103 } else {
1104 sprintf(st, "%s died.", c->name);
1105 message(-1, st);
1106 couleur = 8<<4; // gris pour mort naturelle
1107 }
1108 canvas->stats[CS::DEATH].add(1);
1109 const char *death_type="normal";
1110 //Suicide: less than 9 unit blocks in the 2 lines right
1111 // below the top
1112 int num_units=0;
1113 for(i=14; i<16; i++)
1114 for(j=4; j<14; j++)
1115 if(canvas->occupied[i][j])
1116 num_units++;
1117 bool suicide=false;
1118 if(num_units<9)
1119 suicide=true;
1120 if(then_gone) {
1121 death_type="left";
1122 suicide=false;
1123 }
1124 //Overkill and maximum overkill
1125 for(i=0; i<12; i++) {
1126 num_units=0;
1127 for(j=4; j<14; j++)
1128 if(canvas->occupied[i][j])
1129 num_units++;
1130 if(num_units>=8)
1131 break;
1132 }
1133 if(i<10 && i>=7) {
1134 death_type="overkill";
1135 if(fragger)
1136 fragger->stats[CS::OVERKILLER].add(1);
1137 canvas->stats[CS::OVERKILLEE].add(1);
1138 suicide=false;
1139 }
1140 if(i<7) {
1141 death_type="maximum_overkill";
1142 if(fragger)
1143 fragger->stats[CS::MAXOVERKILLER].add(1);
1144 canvas->stats[CS::MAXOVERKILLEE].add(1);
1145 suicide=false;
1146 }
1147 if(suicide) {
1148 death_type="suicide";
1149 canvas->stats[CS::SUICIDES].add(1);
1150 }
1151 Packet_serverlog log("player_dead");
1152 log.add(Packet_serverlog::Var("id", canvas->id()));
1153 log.add(Packet_serverlog::Var("fragger_id", fragger? fragger->id():0));
1154 log.add(Packet_serverlog::Var("type", death_type));
1155 if(game && game->net_server)
1156 game->net_server->record_packet(&log);
1157 i = 11;
1158 j = 4;
1159 Player_dead::c = 0;
1160 canvas->delete_bloc();
1161 canvas->set_next();
1162 }
1163
step()1164 void Player_dead::step() {
1165 Player_base::step();
1166
1167 if(c++&1)
1168 return;
1169 if(j < 9) {
1170 if(canvas->occupied[i][j]) {
1171 canvas->block[i][j] = (canvas->block[i][j] & 15) + couleur;
1172 canvas->dirted[i][j] = 2;
1173 }
1174 if(canvas->occupied[42-i][j]) {
1175 canvas->block[42-i][j] = (canvas->block[42-i][j] & 15) + couleur;
1176 canvas->dirted[42-i][j] = 2;
1177 }
1178 if(canvas->occupied[i][17-j]) {
1179 canvas->block[i][17-j] = (canvas->block[i][17-j] & 15) + couleur;
1180 canvas->dirted[i][17-j] = 2;
1181 }
1182 if(canvas->occupied[42-i][17-j]) {
1183 canvas->block[42-i][17-j] = (canvas->block[42-i][17-j] & 15) + couleur;
1184 canvas->dirted[42-i][17-j] = 2;
1185 }
1186 int vo = -600;
1187 if(!canvas->islocal())
1188 vo -= 1000;
1189
1190 i++;
1191 if(i == 22) {
1192 j++;
1193 i = 11;
1194 if(canvas->inter)
1195 play_sound(sons.flash, vo, -1, 22500+ugs_random.rnd(1023));
1196 }
1197 }
1198 else {
1199 canvas->dying=false;
1200 if(then_gone)
1201 exec(new Player_gone(canvas, true));
1202 else
1203 if(canvas->islocal())
1204 exec(new Player_dead_wait(canvas));
1205 else
1206 exec(new Player_wait_respawn(canvas));
1207 }
1208 }
1209
Player_dead_wait(Canvas * c,bool ab)1210 Player_dead_wait::Player_dead_wait(Canvas *c, bool ab): Player_base(c), add_bonus(ab) {
1211 if(canvas->inter)
1212 canvas->clear_key_all();
1213 canvas->idle = 2;
1214 canvas->state = Canvas::WAITFORWINNER;
1215 }
1216
init()1217 void Player_dead_wait::init() {
1218 Player_base::init();
1219 if(game->single || game->delay_start)
1220 return;
1221 if(game->survivor)
1222 canvas->set_message("Waiting for the", " next round...");
1223 else
1224 canvas->set_message("Press a key to", " restart.");
1225 }
1226
step()1227 void Player_dead_wait::step() {
1228 Player_base::step();
1229 if(check_gone())
1230 return;
1231
1232 if(canvas->should_remove_bonus)
1233 remove_bonus();
1234
1235 bool restart=false;
1236
1237 if(playback && playback->old_mode) {
1238 if(playback->get_byte())
1239 restart = true;
1240 }
1241 else {
1242 if(canvas->inter) {// si canvas invisible, ecoute pas les touches!
1243 for(int i=0; i<7; i++)
1244 if(canvas->check_key(i))
1245 restart = true;
1246 }
1247 }
1248
1249 if(game->survivor) {
1250 restart = check_first_frag();
1251 }
1252 else
1253 if(game->hot_potato && canvas->color==game->potato_team) {
1254 //Sorry mister, your buddies are still juggling the hot
1255 // potato, you can't go help them yet! :)
1256 restart=false;
1257 }
1258 //Never restart in single-player games
1259 if(game->single)
1260 restart=false;
1261 if(restart) {
1262 Packet_clientrespawn p;
1263 p.player=canvas->num_player;
1264 net->sendtcp(&p);
1265 canvas->restart();
1266 ret();
1267 Packet_serverlog log("player_respawn");
1268 log.add(Packet_serverlog::Var("id", canvas->id()));
1269 if(game && game->net_server)
1270 game->net_server->record_packet(&log);
1271 }
1272 else {
1273 if(canvas->bonus && !canvas->bon[0].blind_time && add_bonus)
1274 call(new Player_add_bonus(canvas));
1275 }
1276 }
1277
check_first_frag()1278 bool Player_dead_wait::check_first_frag() {
1279 switch(canvas->state) {
1280 case Canvas::WAITFORWINNER:
1281 if(game->net_list.syncpoint==Canvas::WAITFORRESTART) {
1282 canvas->state = Canvas::WAITFORRESTART;
1283 Packet_clientstate p;
1284 p.player = canvas->num_player;
1285 p.state = canvas->state;
1286 net->sendtcp(&p);
1287 }
1288 break;
1289 case Canvas::WAITFORRESTART:
1290 if(game->net_list.syncpoint==Canvas::PLAYING)
1291 return true;
1292 case Canvas::PLAYING:
1293 case Canvas::LAST:
1294 break;
1295 }
1296 return false;
1297 }
1298
Player_first_frag(Canvas * c)1299 Player_first_frag::Player_first_frag(Canvas *c): Player_base(c) {
1300 canvas->dying=true;
1301 for(j=0; j<36; j++)
1302 for(i=0; i<18; i++) {
1303 if(canvas->blinded[j][i]) {
1304 canvas->bflash[j][i] = 24;
1305 canvas->dirted[j][i] = 2;
1306 }
1307 canvas->blinded[j][i] = 0;
1308 }
1309 couleur = 8<<4;
1310 i = 11;
1311 j = 4;
1312 this->c = 0;
1313 canvas->delete_bloc();
1314 canvas->set_next();
1315 canvas->stats[CS::ROUND_WINS].add(1);
1316 Packet_serverlog log("player_survived");
1317 log.add(Packet_serverlog::Var("id", canvas->id()));
1318 if(game && game->net_server)
1319 game->net_server->record_packet(&log);
1320 }
1321
step()1322 void Player_first_frag::step() {
1323 Player_base::step();
1324
1325 if(c++&1)
1326 return;
1327 if(j < 9) {
1328 canvas->block[i][j] = 15 + couleur;
1329 canvas->occupied[i][j] = true;
1330 canvas->dirted[i][j] = 2;
1331 canvas->block[42-i][j] = 15 + couleur;
1332 canvas->occupied[42-i][j] = true;
1333 canvas->dirted[42-i][j] = 2;
1334 canvas->block[i][17-j] = 15 + couleur;
1335 canvas->occupied[i][17-j] = true;
1336 canvas->dirted[i][17-j] = 2;
1337 canvas->block[42-i][17-j] = 15 + couleur;
1338 canvas->occupied[42-i][17-j] = true;
1339 canvas->dirted[42-i][17-j] = 2;
1340 int vo = -600;
1341 if(!canvas->islocal())
1342 vo -= 1000;
1343
1344 i++;
1345 if(i == 22) {
1346 j++;
1347 i = 11;
1348 if(canvas->inter)
1349 play_sound(sons.flash, vo, 1000, 32500+ugs_random.rnd(1023));
1350 }
1351 } else {
1352 canvas->dying=false;
1353 Player_base *pb;
1354 if(canvas->islocal()) {
1355 pb=new Player_dead_wait(canvas, false);
1356 }
1357 else {
1358 pb=new Player_wait_respawn(canvas, false);
1359 }
1360 exec(pb);
1361 }
1362 }
1363
Player_gone(Canvas * c,bool chat_msg)1364 Player_gone::Player_gone(Canvas *c, bool chat_msg): Player_base(c) {
1365 canvas->local_player = false; // enleve le statut de 'local' a un joueur gone
1366 canvas->idle = 3;
1367 canvas->state = Canvas::LAST;
1368
1369 canvas->delete_bloc();
1370 canvas->set_next();
1371 if(chat_msg) {
1372 sprintf(st, "%s has left the game.", canvas->name);
1373 message(-1, st);
1374 }
1375 Packet_serverlog log("player_gone");
1376 log.add(Packet_serverlog::Var("id", canvas->id()));
1377 if(game && game->net_server)
1378 game->net_server->record_packet(&log);
1379 }
1380
step()1381 void Player_gone::step() {
1382 Player_base::step();
1383 if(canvas->islocal()) {
1384 if(canvas->should_remove_bonus)
1385 remove_bonus();
1386 }
1387 else {
1388 check_bonus();
1389 }
1390 canvas->gone_time++;
1391
1392 {
1393 Packet_dead *p=(Packet_dead *) game->peekpacket(P_DEAD);
1394 if(p && p->player==canvas->num_player && p->then_gone)
1395 game->removepacket(); //Remove superfluous gone packet sent by server if client disconnects too fast
1396 }
1397
1398 {
1399 Packet_gone *p=(Packet_gone *) game->peekpacket(P_GONE);
1400 if(p && p->player==canvas->num_player)
1401 game->removepacket(); //Remove superfluous gone packet sent by server if client disconnects too fast
1402 }
1403
1404 Packet_rejoin *p=(Packet_rejoin *) game->peekpacket(P_REJOIN);
1405 if(p && p->player==canvas->num_player) {
1406 canvas->h_repeat = p->h_repeat;
1407 canvas->v_repeat = p->v_repeat;
1408 canvas->smooth = p->smooth? true:false;
1409 canvas->shadow = p->shadow? true:false;
1410 canvas->handicap = p->handicap;
1411 sprintf(st, "%s has joined back!", canvas->long_name(true, false));
1412 message(-1, st);
1413 msgbox("Player_gone::step: player %i is no longer a goner! Rejoining.\n", p->player);
1414 game->removepacket();
1415
1416 game->net_list.rejoin_player(canvas);
1417 canvas->delete_bloc();
1418 canvas->reinit();
1419 canvas->restart();
1420 game->net_list.notify_all();
1421 if(game->net_version()>=23 && game->survivor) {
1422 if(canvas->islocal())
1423 exec(new Player_dead_wait(canvas));
1424 else
1425 exec(new Player_wait_respawn(canvas));
1426 }
1427 ret();
1428 }
1429 }
1430
Player_wait_block(Canvas * c)1431 Player_wait_block::Player_wait_block(Canvas *c): Player_base(c) {
1432 canvas->idle = 1;
1433 canvas->state = Canvas::PLAYING;
1434 move_index=0;
1435 }
1436
~Player_wait_block()1437 Player_wait_block::~Player_wait_block() {
1438 int total_left=0;
1439 if(move_index<canvas->da_moves.size())
1440 total_left+=canvas->da_moves.size()-move_index;
1441 if(total_left)
1442 msgbox("Player_wait_block::deletemoves: moves not exhausted, %i left\n", total_left);
1443 canvas->da_moves.resize(0);
1444 }
1445
init()1446 void Player_wait_block::init() {
1447 Player_base::init();
1448 }
1449
step()1450 void Player_wait_block::step() {
1451 Player_base::step();
1452 bool wait_more=false;
1453 if(playback && !playback->old_mode) {
1454 int remain=canvas->da_moves.size()-move_index;
1455 if(remain && remain<=50)
1456 wait_more=true;
1457 }
1458
1459 if(!wait_more)
1460 if(check_gone())
1461 return;
1462
1463 check_bonus();
1464
1465 if(!wait_more)
1466 if(game->survivor)
1467 if(check_first_frag())
1468 return;
1469
1470 if(!(playback && playback->old_mode)) {
1471 {
1472 Packet_moves *p=(Packet_moves *) game->peekpacket(P_MOVES);
1473 if(p && p->player==canvas->num_player) {
1474 if(canvas->da_moves.size()+p->size<4000)
1475 canvas->da_moves.append(p->moves, p->size);
1476 game->removepacket();
1477 }
1478 }
1479 if(move_index<canvas->da_moves.size() && !game->paused) {
1480 Byte r = canvas->da_moves[move_index++];
1481 if(r & 8)
1482 rotate_left();
1483 if(r & 16)
1484 rotate_right();
1485 if(r & 32)
1486 rotate_right(true);
1487 if(r & 1)
1488 move_down();
1489 if(r & 64)
1490 drop_down();
1491 if(r & 2)
1492 move_left();
1493 if(r & 4)
1494 move_right();
1495
1496 int t;
1497 t = ((canvas->bloc->bx-4)<<4)*18; // calcul la position ou le bloc devrait etre
1498 int nx = canvas->bloc->x; // nouvelle position 'x' a obtenir
1499 if(nx < t) {
1500 nx += canvas->side_speed;
1501 if(nx > t) // si depasse la position
1502 nx = t;
1503 }
1504 if(nx > t) {
1505 nx -= canvas->side_speed;
1506 if(nx < t) // si depasse la position
1507 nx = t;
1508 }
1509 t = calc_by(canvas->bloc->y-(17<<4)-15);
1510 if(!canvas->collide(canvas->bloc->bx, t, canvas->bloc->rot))
1511 canvas->bloc->x = nx; // accepte le deplacement 'smooth' seulement s'il ne passe pas par-dessus un bloc
1512 t = calc_by(canvas->bloc->y+canvas->speed);
1513 if(!canvas->collide(canvas->bloc->bx, t, canvas->bloc->rot)) {
1514 canvas->bloc->y+=canvas->speed;
1515 canvas->bloc->by = calc_by(canvas->bloc->y);
1516 }
1517 if(canvas->shadow)
1518 canvas->calc_shadow();
1519 }
1520 }
1521
1522 if(wait_more)
1523 return;
1524
1525 //Check whether this player placed a block
1526 Packet_stampblock *p=(Packet_stampblock *) game->peekpacket(P_STAMPBLOCK);
1527 if(p && p->player==canvas->num_player) {
1528 exec(new Player_stamp(canvas, p));
1529 game->removepacket();
1530 return;
1531 }
1532
1533 //Check whether this player died
1534 Packet_dead *pd=(Packet_dead *) game->peekpacket(P_DEAD);
1535 if(pd && pd->player==canvas->num_player) {
1536 exec(new Player_dead(canvas, pd->then_gone));
1537 game->removepacket();
1538 return;
1539 }
1540 }
1541
check_first_frag()1542 bool Player_wait_block::check_first_frag() {
1543 Packet_first_frag *p=(Packet_first_frag *) game->peekpacket(P_FIRST_FRAG);
1544 if(p && p->player==canvas->num_player) {
1545 msgbox("Player_wait_block::check_first_frag: recu P_FIRST_FRAG: %s gagnant!\n", canvas->name);
1546 exec(new Player_first_frag(canvas));
1547 game->removepacket();
1548 return true;
1549 }
1550 return false;
1551 }
1552
Player_stamp(Canvas * c,Packet_stampblock * p)1553 Player_stamp::Player_stamp(Canvas *c, Packet_stampblock *p): Player_base(c) {
1554 /* Flash blinded blocks when stamping
1555 int i, j;
1556 for(j=0; j<36; j++)
1557 for(i=0; i<18; i++)
1558 if(canvas->blinded[j][i]) {
1559 canvas->bflash[j][i] = 8;
1560 }
1561 */
1562 int i, j;
1563 //Reset Canvas::moved
1564 for(j=0; j<36; j++)
1565 for(i=0; i<18; i++)
1566 canvas->moved[j][i]=false;
1567 canvas->idle = 0;
1568 canvas->potato_team_on_last_stamp=game->potato_team;
1569 canvas->bloc->rot=p->rotate;
1570 canvas->bloc->bx=p->x;
1571 canvas->bloc->by=p->y;
1572 if(canvas->collide(p->x, p->y, p->rotate) || !canvas->collide(p->x, p->y+1, p->rotate)) {
1573 char st[256];
1574 sprintf(st, "*** %s dropped an invalid block! ***", canvas->name);
1575 if(game->server) {
1576 message(-1, st, true, false, true);
1577 game->net_list.server_drop_player(canvas->num_player, DROP_INVALID_BLOCK);
1578 Net_connection *nc=canvas->remote_adr;
1579 send_msg(nc, "You have been dropped because you placed an invalid block. This may be due to a bug in the game or a network failure.");
1580 send_msg(nc, "Quitting and rejoining the game should fix the problem. If not, please contact support@ludusdesign.com.");
1581 }
1582 msgbox("%s\n", st);
1583 }
1584 canvas->stats[CS::COMPTE0+canvas->bloc->type].add(1);
1585 canvas->stats[CS::COMPTETOT].add(1);
1586 canvas->stats[CS::ROTATED0+canvas->bloc->type].add(p->block_rotated);
1587 canvas->stats[CS::ROTATEDTOT].add(p->block_rotated);
1588 canvas->stats[CS::SCORE].add(p->score);
1589 if(game->net_version()>=23)
1590 canvas->stats[CS::PLAYING_TIME].add(p->time_held);
1591
1592 Packet_serverlog log("player_stampblock");
1593 log.add(Packet_serverlog::Var("id", canvas->id()));
1594 log.add(Packet_serverlog::Var("block", canvas->bloc->type));
1595 log.add(Packet_serverlog::Var("times_rotated", p->block_rotated));
1596 log.add(Packet_serverlog::Var("time_held", p->time_held));
1597 log.add(Packet_serverlog::Var("points", p->score));
1598 if(game && game->net_server)
1599 game->net_server->record_packet(&log);
1600
1601 canvas->watch_date = p->date;
1602 stamp_bloc();
1603 if(canvas->bonus && !canvas->bon[0].blind_time)
1604 addbonus=new Player_add_bonus(canvas);
1605 else
1606 addbonus=NULL;
1607
1608 for(i=0; i<MAXPLAYERS; i++) {
1609 if(canvas->attacks[i] > 0) {
1610 canvas->attacks[i]--;
1611 if(canvas->attacks[i] == 0 && canvas->last_attacker == i) // si c'etait lui le last_attacker,
1612 if(!game->survivor)
1613 canvas->last_attacker = 255; // on l'oublie.
1614 }
1615
1616 // new handicap code for net_version >= 24
1617 Canvas* other_canvas = game->net_list.get(i);
1618 if(other_canvas) {
1619 int diff=0;
1620 if(canvas->handicap>other_canvas->handicap)
1621 diff=canvas->handicap-other_canvas->handicap;
1622 if(canvas->handicaps[i] < diff*Canvas::stamp_per_handicap)
1623 canvas->handicaps[i]++;
1624 }
1625 }
1626
1627 // adjust handicap_crowd considering crowdedness of the game (i.e. number of players alive)
1628 int max_handicap_crowd = max(0, int(game->net_list.count_alive())-4);
1629 max_handicap_crowd *= Canvas::stamp_per_handicap;
1630 if(canvas->handicap_crowd < max_handicap_crowd)
1631 ++canvas->handicap_crowd;
1632 else
1633 canvas->handicap_crowd = max_handicap_crowd;
1634 }
1635
init()1636 void Player_stamp::init() {
1637 if(addbonus) {
1638 call(new Player_check_line(canvas));
1639 call(addbonus);
1640 }
1641 call(new Player_check_line(canvas));
1642 ret();
1643 }
1644
stamp_bloc()1645 void Player_stamp::stamp_bloc() {
1646 int i,j;
1647 Byte t;
1648 Byte blindness=0;
1649 if(canvas->bonus && canvas->bon[0].blind_time) {
1650 blindness=canvas->bon[0].blind_time;
1651 int y;
1652 for(y = 1; y < canvas->bonus; y++)
1653 canvas->bon[y-1] = canvas->bon[y];
1654 canvas->bonus--;
1655 }
1656 for(j = 0; j < 4; j++)
1657 for(i = 0; i < 4; i++) {
1658 t = canvas->bloc->bloc[canvas->bloc->type][canvas->bloc->rot][j][i];
1659 if(t) {
1660 canvas->block[canvas->bloc->by+j][canvas->bloc->bx+i] = t + (canvas->bloc->type<<4);
1661 canvas->occupied[canvas->bloc->by+j][canvas->bloc->bx+i] = true;
1662 canvas->blinded[canvas->bloc->by+j][canvas->bloc->bx+i] = blindness;
1663 canvas->bflash[canvas->bloc->by+j][canvas->bloc->bx+i] = blindness? 16:0;
1664 canvas->dirted[canvas->bloc->by+j][canvas->bloc->bx+i] = 2;
1665 canvas->last_x = i;
1666 canvas->moved[canvas->bloc->by+j][canvas->bloc->bx+i] = true;
1667 }
1668 }
1669 canvas->last_x += canvas->bloc->bx;
1670 int startline=0;
1671 for(j = 0; j < 32; ++j)
1672 for(i = 4; i < 14; ++i)
1673 if(canvas->occupied[j][i]) {
1674 startline = j;
1675 // break out of both loops
1676 j=32;
1677 break;
1678 }
1679 canvas->snapshot[0]=0;
1680 for(j = startline; j < 32; j++)
1681 for(i = 4; i < 14; i++) {
1682 if(canvas->occupied[j][i]) {
1683 char bl[3];
1684 Byte color;
1685 if(canvas->moved[j][i])
1686 color=8;
1687 else
1688 color=canvas->block[j][i]>>4;
1689 Byte side=canvas->block[j][i]&15;
1690 sprintf(bl, "%c%c", '0'+color, 'a'+side);
1691 strcat(canvas->snapshot, bl);
1692 }
1693 else
1694 strcat(canvas->snapshot, "e0");
1695 }
1696 i = (canvas->bloc->bx-9) * 300;
1697 Sample *dep;
1698 switch(ugs_random.rnd()%3) {
1699 case 0: dep = sons.depose;
1700 break;
1701 case 1: dep = sons.depose2;
1702 break;
1703 default: dep = sons.depose3;
1704 break;
1705 }
1706 if(canvas->inter) {
1707 int vo = -200 - ugs_random.rnd(255);
1708 if(!canvas->islocal())
1709 vo -= 1000;
1710 play_sound(dep, vo, i, 10500+ugs_random.rnd(1023));
1711 }
1712
1713 delete canvas->bloc;
1714 canvas->bloc = NULL;
1715 if(canvas->bloc_shadow) {
1716 delete canvas->bloc_shadow;
1717 canvas->bloc_shadow = NULL;
1718 }
1719 }
1720
Player_wait_respawn(Canvas * c,bool ab)1721 Player_wait_respawn::Player_wait_respawn(Canvas *c, bool ab): Player_base(c), add_bonus(ab) {
1722 canvas->idle = 2;
1723 canvas->state = Canvas::WAITFORWINNER;
1724 }
1725
step()1726 void Player_wait_respawn::step() {
1727 Player_base::step();
1728 if(check_gone())
1729 return;
1730
1731 check_bonus();
1732
1733 check_state();
1734
1735 Packet_respawn *p=(Packet_respawn *) game->peekpacket(P_RESPAWN);
1736 if(p && p->player==canvas->num_player) {
1737 canvas->restart();
1738 ret();
1739 game->removepacket();
1740 Packet_serverlog log("player_respawn");
1741 log.add(Packet_serverlog::Var("id", canvas->id()));
1742 if(game && game->net_server)
1743 game->net_server->record_packet(&log);
1744 } else {
1745 if(canvas->bonus && !canvas->bon[0].blind_time && add_bonus)
1746 call(new Player_add_bonus(canvas));
1747 }
1748 }
1749
Player_init(Canvas * canvas)1750 Player_init::Player_init(Canvas *canvas): Player_base(canvas) {
1751 canvas->restart();
1752 if(game->net_version()>=23 && game->survivor)
1753 canvas->idle = 2;
1754 else
1755 canvas->idle = 1;
1756 canvas->state = Canvas::PLAYING;
1757 if(canvas->wait_download)
1758 net->addwatch(P_DOWNLOAD, this);
1759 }
1760
step()1761 void Player_init::step() {
1762 Player_base::step();
1763 //Don't start until we got our download packet
1764 if(canvas->wait_download)
1765 return;
1766 Byte idle=canvas->idle; //Cause Player_normal::Player_normal sets it
1767 exec(new Player_normal(canvas));
1768 if(idle == 2) {
1769 if(canvas->islocal())
1770 call(new Player_dead_wait(canvas));
1771 else
1772 call(new Player_wait_respawn(canvas));
1773 }
1774 if(idle == 3) {
1775 call(new Player_gone(canvas, false));
1776 canvas->remote_adr = NULL; // empeche de watcher un joueur qui est 'gone' lorsqu'on join
1777 }
1778 }
1779
net_call(Packet * p2)1780 void Player_init::net_call(Packet *p2) {
1781 Packet_download *p=(Packet_download *) p2;
1782 {
1783 Canvas *c = game->net_list.get(p->player);
1784 if(!c || c!=canvas) {
1785 msgbox("Player_init::net_call: got a P_DOWNLOAD but canvas does not exist or not even mine, something screwy is going on\n");
1786 delete p2;
1787 return;
1788 }
1789 }
1790 for(int y=0; y<32; y++)
1791 for(int x=0; x<10; x++) {
1792 canvas->block[y][x+4] = p->can[y][x];
1793 canvas->occupied[y][x+4] = p->occ[y][x];
1794 canvas->blinded[y][x+4] = p->blinded[y][x];
1795 }
1796 canvas->rnd.set_seed(p->seed);
1797 if(p->bloc != 255) {
1798 canvas->bloc = new Bloc(p->bloc, -1, 7, 10);
1799 canvas->next = new Bloc(p->next, -1, 7, 10);
1800 canvas->next2 = new Bloc(p->next2, -1, 7, 10);
1801 canvas->next3 = new Bloc(p->next3, -1, 7, 10);
1802 }
1803 canvas->bonus = p->bonus;
1804 int i;
1805 for(i=0; i<20; i++) {
1806 canvas->bon[i].x = p->bon[i].x;
1807 canvas->bon[i].color = p->bon[i].color;
1808 canvas->bon[i].blind_time = p->bon[i].blind_time;
1809 canvas->bon[i].hole_pos = p->bon[i].hole_pos;
1810 canvas->bon[i].final = p->bon[i].final;
1811 }
1812 for(i=0; i<MAXPLAYERS; i++)
1813 canvas->attacks[i] = p->attacks[i];
1814 canvas->last_attacker = p->last_attacker;
1815 canvas->idle = p->idle;
1816 canvas->state = (Canvas::State) p->state;
1817 msgbox("Player_init::net_call: name=%s, idle=%i, bonus=%i, bloc=%i\n", canvas->name, canvas->idle, canvas->bonus, p->bloc);
1818 delete p2;
1819 canvas->wait_download=false;
1820 net->removewatch(P_DOWNLOAD, this);
1821 }
1822
init_directory()1823 void init_directory() {
1824 strcpy(quadradir, exe_directory);
1825 #ifdef WIN32
1826 if(SHGetFolderPath(0, CSIDL_APPDATA|CSIDL_FLAG_CREATE, 0, SHGFP_TYPE_CURRENT, quadradir) < 0) {
1827 msgbox("SHGetFolderPath failed, using exe_directory");
1828 }
1829 else {
1830 strcat(quadradir, "\\Quadra");
1831 CreateDirectory(quadradir, 0);
1832 }
1833 #endif
1834 #ifdef UGS_LINUX
1835 struct passwd *pw = NULL;
1836
1837 pw = getpwuid(getuid());
1838 if(pw) {
1839 strcpy(quadradir, pw->pw_dir);
1840 strcat(quadradir, "/.quadra");
1841 mkdir(quadradir, 0777);
1842 chown(quadradir, pw->pw_uid, pw->pw_gid);
1843 }
1844 #endif
1845 }
1846
init_stuff(bool need_sound,bool need_video)1847 static void init_stuff(bool need_sound, bool need_video) {
1848 int i;
1849
1850 if (!need_video) {
1851 video_is_dumb = true;
1852 char value[] = "SDL_VIDEODRIVER=dummy";
1853 SDL_putenv(value);
1854 }
1855
1856 if(SDL_Init(SDL_INIT_VIDEO) == -1) {
1857 fprintf(stderr, "Could not initialize SDL: %s\n", SDL_GetError());
1858 exit(1);
1859 }
1860
1861 video = new Video;
1862
1863 if(!video)
1864 fatal_msgbox("Could not initialize video subsystem");
1865
1866 fonts.init();
1867 //If we init a dumb video, we need a dumb input too
1868 input = Input::New(!need_video);
1869 // don't need sound if no video
1870 if(need_sound && need_video)
1871 sound = Sound::New();
1872 else
1873 sound = NULL;
1874
1875 for(i=0; i<256; i++)
1876 noir.setcolor(i, 40, 40, 40);
1877
1878 chat_text = new Chat_text(fonts.normal, 212);
1879 net_starter = new Net_starter();
1880 sons.pause = new Sample(Res_doze("cuckoo.wav"));
1881 sons.start = new Sample(Res_doze("hooter03.wav"));
1882 sons.bonus1 = new Sample(Res_doze("Whizz1.wav"));
1883 sons.levelup = new Sample(Res_doze("glissup.wav"));
1884 // when the canvas 'flows'
1885 sons.depose4 = new Sample(Res_doze("Clang3.wav"));
1886 sons.flash = NULL;
1887 sons.depose3 = NULL;
1888 sons.depose2 = NULL;
1889 sons.depose = NULL;
1890 sons.drip = NULL;
1891 sons.glass = new Sample(Res_doze("Glass01.wav"));
1892 sons.enter = new Sample(Res_doze("Tapdrip.wav"));
1893 sons.fadein = new Sample(Res_doze("W_BAYO_0.wav"));
1894 sons.fadeout = new Sample(Res_doze("fadeout.wav"));
1895 sons.point = new Sample(Res_doze("click_1.wav"));
1896 sons.click = new Sample(Res_doze("Blip1.wav"));
1897 sons.msg = new Sample(Res_doze("handbell.wav"));
1898 sons.potato_get = new Sample(Res_doze("potato_get.wav"));
1899 sons.potato_rid = new Sample(Res_doze("zingle.wav"));
1900 sons.minute = new Sample(Res_doze("t1min.wav"));
1901 sons.thirty = new Sample(Res_doze("t30sec.wav"));
1902 sons.twenty = new Sample(Res_doze("t20sec.wav"));
1903 sons.ten = new Sample(Res_doze("t10sec.wav"));
1904 sons.five = new Sample(Res_doze("t5sec.wav"));
1905 sons.four = new Sample(Res_doze("t4sec.wav"));
1906 sons.three = new Sample(Res_doze("t3sec.wav"));
1907 sons.two = new Sample(Res_doze("t2sec.wav"));
1908 sons.one = new Sample(Res_doze("t1sec.wav"));
1909 cursor = new Cursor;
1910 for(i=0; i<8; i++)
1911 fteam[i] = new Font(*fonts.normal);
1912 }
1913
deinit_stuff()1914 void deinit_stuff() {
1915 for(int i=0; i<8; i++)
1916 if(fteam[i]) {
1917 delete fteam[i];
1918 fteam[i] = NULL;
1919 }
1920
1921 delete sons.click; sons.click = NULL;
1922 delete sons.point; sons.point = NULL;
1923 delete sons.fadeout; sons.fadeout = NULL;
1924 delete sons.fadein; sons.fadein = NULL;
1925 delete sons.drip; sons.drip = NULL;
1926 delete sons.glass; sons.glass = NULL;
1927 delete sons.depose; sons.depose = NULL;
1928 delete sons.depose2; sons.depose2 = NULL;
1929 delete sons.depose3; sons.depose3 = NULL;
1930 delete sons.depose4; sons.depose4 = NULL;
1931 delete sons.flash; sons.flash = NULL;
1932 delete sons.enter; sons.enter = NULL;
1933 delete sons.levelup; sons.levelup = NULL;
1934 delete sons.bonus1; sons.bonus1 = NULL;
1935 delete sons.start; sons.start = NULL;
1936 delete sons.pause; sons.pause = NULL;
1937 delete sons.msg; sons.msg = NULL;
1938 delete sons.potato_get; sons.potato_get = NULL;
1939 delete sons.potato_rid; sons.potato_rid = NULL;
1940 delete sons.minute; sons.minute = NULL;
1941 delete sons.thirty; sons.thirty = NULL;
1942 delete sons.twenty; sons.twenty = NULL;
1943 delete sons.ten; sons.ten = NULL;
1944 delete sons.five; sons.five = NULL;
1945 delete sons.four; sons.four = NULL;
1946 delete sons.three; sons.three = NULL;
1947 delete sons.two; sons.two = NULL;
1948 delete sons.one; sons.one = NULL;
1949
1950 delete net_starter; net_starter=NULL;
1951 delete chat_text; chat_text=NULL;
1952
1953 config.write();
1954 Highscores::freemem();
1955
1956 delete cursor; cursor = NULL;
1957
1958 fonts.deinit();
1959 }
1960
command_get_param(const char * t,const char * def=NULL)1961 const char *command_get_param(const char *t, const char *def=NULL) {
1962 const char *temp = command.get_param();
1963 if(!temp)
1964 temp=def;
1965 if(!temp)
1966 fatal_msgbox("Command line parameter not found for '%s'", t);
1967 return temp;
1968 }
1969
read_attack_param(const char * s)1970 Attack read_attack_param(const char *s) {
1971 Attack ret;
1972 char temp[1024];
1973 strncpy(temp, s, sizeof(temp));
1974 temp[sizeof(temp)-1]=0;
1975 char *param=strchr(temp, ' ');
1976 if(param) {
1977 *param=0;
1978 param++;
1979 }
1980 if(!strcmp(temp, "lines"))
1981 ret.type=ATTACK_LINES;
1982 if(!strcmp(temp, "none"))
1983 ret.type=ATTACK_NONE;
1984 if(!strcmp(temp, "blind") || !strcmp(temp, "fullblind")) {
1985 if(param) {
1986 int p=atoi(param);
1987 p=min(max(1, p), 255);
1988 ret.param=p;
1989 }
1990 }
1991 if(!strcmp(temp, "blind")) {
1992 ret.type=ATTACK_BLIND;
1993 if(!param)
1994 ret.param=30;
1995 }
1996 if(!strcmp(temp, "fullblind")) {
1997 ret.type=ATTACK_FULLBLIND;
1998 if(!param)
1999 ret.param=12;
2000 }
2001 return ret;
2002 }
2003
display_command_line_help()2004 void display_command_line_help() {
2005 char st[4096];
2006 Res_doze cmdline("help_en.txt");
2007 Dword size = min(static_cast<Dword>(sizeof(st)-1), cmdline.size());
2008 strncpy(st, (char *)cmdline.buf(), size);
2009 st[size] = 0;
2010 if(video)
2011 delete video;
2012 user_output("Quadra - Command line", st);
2013 }
2014
read_script(const char * fn,bool second=false)2015 void read_script(const char *fn, bool second=false) {
2016 char st[32768];
2017 Res_dos script(fn);
2018 if(script.exist) {
2019 //-2 because Stringtable is strange
2020 Dword size = min(static_cast<Dword>(sizeof(st)-2), script.size());
2021 strncpy(st, (char *)script.buf(), size);
2022 st[size] = 0;
2023 Stringtable str((Byte *)st, size);
2024 int i;
2025 for(i=0; i<str.size(); i++) {
2026 if(second) {
2027 if(game && str.get(i)[0]=='/')
2028 game->net_list.got_admin_line(str.get(i), NULL);
2029 }
2030 else {
2031 if(str.get(i)[0]=='-')
2032 command.add(str.get(i));
2033 }
2034 }
2035 }
2036 else
2037 msgbox("Can't find script %s, ignoring.\n", fn);
2038 }
2039
start_game()2040 void start_game() {
2041 if(command.token("113"))
2042 Config::net_version = 20;
2043 if(command.token("debug")) {
2044 _debug = true;
2045 msgbox("Debug mode enabled\n");
2046 }
2047
2048 bool no_video = false;
2049 bool no_sound = false;
2050 bool demo_play = false;
2051 bool demo_verif = false;
2052 bool demo_verified_and_valid = false;
2053 char buf[512];
2054 /* FIXME: rather than using 1024 MAXPATHLEN should be used. To do
2055 so requires all other filename lengths be MAXPATHLEN as well. */
2056 char fn[1024];
2057
2058 init_directory();
2059
2060 const char *dir=quadradir;
2061 #ifdef WIN32
2062 dir = exe_directory;
2063 #else
2064 #ifdef UGS_XCODE
2065 dir = ".";
2066 #else
2067 dir = getenv("QUADRADIR");
2068 if(!dir)
2069 dir = DATAGAMESDIR;
2070 #endif
2071 #endif
2072 resmanager=new Resmanager();
2073 snprintf(fn, sizeof(fn) - 1, "%s/quadra.res", dir);
2074 resmanager->loadresfile(fn);
2075 snprintf(fn, sizeof(fn) - 1, "%s/quadra%i%i%i.res", dir, Config::major, Config::minor, Config::patchlevel);
2076 resmanager->loadresfile(fn);
2077 if(command.token("patch") || command.token("theme")) {
2078 const char *temp=command_get_param("patch <filename>");
2079 if(temp[0] != '/' && temp[0] != '\\')
2080 snprintf(fn, sizeof(fn) - 1, "%s/%s", dir, temp);
2081 else
2082 snprintf(fn, sizeof(fn) - 1, "%s", temp);
2083 resmanager->loadresfile(fn);
2084 }
2085 msgbox("Reading config: ");
2086 config.read();
2087 msgbox("Ok\n");
2088 //Read script and add to command line options if applicable
2089 if(command.token("exec")) {
2090 const char *temp=command_get_param("exec <filename>");
2091 msgbox("Reading script %s: ", temp);
2092 read_script(temp);
2093 msgbox("Ok\n");
2094 }
2095 bool dedicated=command.token("dedicated");
2096 int i;
2097 for(i=0; i<MAXTEAMS; i++)
2098 set_team_name(i, NULL);
2099
2100 if(command.token("h help ?")) {
2101 display_command_line_help();
2102 delete resmanager;
2103 return;
2104 }
2105 if(_debug && command.token("verify")) {
2106 const char *temp = command_get_param("verify <filename>");
2107 strncpy(buf, temp, sizeof(buf) - 1);
2108 demo_play = true;
2109 demo_verif = true;
2110 demo_verified_and_valid = false;
2111 no_video = true;
2112 no_sound = true;
2113 }
2114 if(command.token("play")) {
2115 const char *temp = command_get_param("play <filename>");
2116 strncpy(buf, temp, sizeof(buf) - 1);
2117 demo_play = true;
2118 demo_verif = false;
2119 }
2120 if(!demo_play && dedicated)
2121 no_video=true;
2122 if(command.token("novideo")) {
2123 no_video=true;
2124 no_sound=true;
2125 }
2126 if(command.token("nosound")) {
2127 no_sound=true;
2128 }
2129 msgbox("Calling init_stuff: ");
2130 init_stuff(!no_sound, !no_video); //No sound when checking demos
2131 msgbox("Ok\n");
2132
2133 // Start auto-updater, but only if we have video enabled.
2134 if(!no_video)
2135 AutoUpdater::start();
2136
2137 Dword last=0;
2138 Dword acc=0;
2139 Executor *menu = new Executor();
2140 //Add Menu_intro so we get back there after -connect, -server or -play
2141 // unless -thenquit option si specified
2142 if(!command.token("thenquit") && !demo_verif)
2143 menu->add(new Menu_intro());
2144
2145 if(!demo_play) {
2146 if(command.token("server") || dedicated) {
2147 if(!net->active)
2148 fatal_msgbox("Network failed to initialize or not present\nCan't start server.\n");
2149 buf[0] = 0;
2150 if(command.token("port")) {
2151 const char *temp = command_get_param("port <TCP/IP port>");
2152 int port = atoi(temp);
2153 if(port<=0 || port>=65535)
2154 fatal_msgbox("Illegal port number.\n");
2155 config.info.port_number = port;
2156 }
2157 Game_params p;
2158 if(command.token("ffa"))
2159 p.set_preset(PRESET_FFA);
2160 if(command.token("survivor"))
2161 p.set_preset(PRESET_SURVIVOR);
2162 if(command.token("hotpotato"))
2163 p.set_preset(PRESET_HOT_POTATO);
2164 if(command.token("peace"))
2165 p.set_preset(PRESET_PEACE);
2166 if(command.token("blind")) {
2167 p.set_preset(PRESET_BLIND);
2168 const char *temp = command_get_param("blind <n>", "30");
2169 Dword time=atoi(temp);
2170 time=min(max(time, static_cast<Dword>(0)), static_cast<Dword>(255));
2171 p.normal_attack.param=time;
2172 p.clean_attack.param=time;
2173 }
2174 if(command.token("fullblind")) {
2175 p.set_preset(PRESET_FULLBLIND);
2176 const char *temp = command_get_param("fullblind <n>", "12");
2177 Dword time=atoi(temp);
2178 time=min(max(time, static_cast<Dword>(0)), static_cast<Dword>(255));
2179 p.normal_attack.param=time;
2180 p.clean_attack.param=time;
2181 }
2182 if(command.token("attack")) {
2183 const char *temp=command_get_param("attack <type> [strength]");
2184 p.normal_attack=read_attack_param(temp);
2185 }
2186 if(command.token("attackclean")) {
2187 const char *temp=command_get_param("attackclean <type> [strength]");
2188 p.clean_attack=read_attack_param(temp);
2189 }
2190 if(command.token("attack2")) {
2191 const char *temp=command_get_param("attack2 <type> [strength]");
2192 p.potato_normal_attack=read_attack_param(temp);
2193 }
2194 if(command.token("attack2clean")) {
2195 const char *temp=command_get_param("attack2clean <type> [strength]");
2196 p.potato_clean_attack=read_attack_param(temp);
2197 }
2198 if(command.token("boringrules"))
2199 p.boring_rules = true;
2200 if(command.token("nolevelup"))
2201 p.level_up = false;
2202 if(command.token("levelup"))
2203 p.level_up = true;
2204 if(command.token("level")) {
2205 const char *temp = command_get_param("level <level number>");
2206 p.level_start = atoi(temp);
2207 p.level_start = min(max(p.level_start, 1), 40);
2208 }
2209 if(command.token("name")) {
2210 const char *temp = command_get_param("name <game name>");
2211 strncpy(buf, temp, sizeof(buf) - 1);
2212 p.name=buf;
2213 }
2214 if(command.token("nohandicap"))
2215 p.allow_handicap=false;
2216 if(command.token("endfrag"))
2217 p.game_end = END_FRAG;
2218 if(command.token("endfrags"))
2219 p.game_end = END_FRAG;
2220 if(command.token("endtime"))
2221 p.game_end = END_TIME;
2222 if(command.token("endpoints"))
2223 p.game_end = END_POINTS;
2224 if(command.token("endscore"))
2225 p.game_end = END_POINTS;
2226 if(p.game_end != END_NEVER) {
2227 const char *temp = command_get_param("endfrag/endtime/endpoints <number>");
2228 p.game_end_value = atoi(temp);
2229 p.game_end_value = min(max(p.game_end_value, 1), p.game_end<=END_TIME? 9999:99999);
2230 }
2231 if(command.token("public"))
2232 p.game_public = true;
2233 menu->add(new Menu_startserver());
2234 (void)new Game(&p);
2235 if(dedicated && !command.token("once"))
2236 game->auto_restart = true;
2237 if(command.token("nomoves"))
2238 game->wants_moves = false;
2239 //Read script a second time, now that game is created
2240 if(command.token("exec")) {
2241 const char *temp=command_get_param("exec <filename>");
2242 read_script(temp, true);
2243 }
2244 if(command.token("admin")) {
2245 const char *temp = command_get_param("admin <password>");
2246 char line[1024];
2247 snprintf(line, sizeof(line) - 1, "/setpasswd %s", temp);
2248 game->net_list.got_admin_line(line, NULL);
2249 }
2250 if(command.token("record")) {
2251 const char *temp = command_get_param("record <filename>", Clock::absolute_time());
2252 game->prepare_recording(temp);
2253 game->prepare_logging();
2254 }
2255 }
2256 else {
2257 if(command.token("connectfile")) {
2258 const char *temp = command_get_param("connectfile <filename>");
2259 char st[1024];
2260 Res_dos file(temp);
2261 if(file.exist) {
2262 snprintf(st, sizeof(st), "-connect %*.*s", file.size(), file.size(), (char *)file.buf());
2263 st[sizeof(st) - 1] = 0;
2264 command.add(st);
2265 }
2266 else
2267 msgbox("Can't find connectfile %s, ignoring.\n", temp);
2268
2269 }
2270 if(command.token("connect")) {
2271 if(!net->active)
2272 fatal_msgbox("Network failed to initialize or not present\nCan't connect.\n");
2273 const char *temp = command_get_param("connect <TCP/IP address>");
2274 strncpy(buf, temp, sizeof(buf) - 1);
2275 buf[sizeof(buf)-1] = 0;
2276 menu->add(new Menu_startconnect(buf, false));
2277 if(config.warning)
2278 menu->add(new Menu_setup());
2279 }
2280 }
2281 }
2282 else {
2283 Res_compress *res = new Res_compress(buf, RES_TRY);
2284 if(res->exist) {
2285 menu->add(new Demo_multi_player(res));
2286 // le 'delete res' est fait par ~Demo_multi_player
2287 if(playback)
2288 playback->set_verification_flag(&demo_verified_and_valid);
2289 }
2290 else {
2291 msgbox("Unable to open demo '%s'\n", buf);
2292 delete res;
2293 }
2294 }
2295
2296 overmind.start(menu);
2297 bool reset_time=false;
2298 while(!menu->done) {
2299 last=getmsec();
2300 if(demo_verif) {
2301 acc=500;
2302 while(acc--)
2303 overmind.step();
2304 }
2305 else {
2306 while(acc>=10) {
2307 if(reset_time) { // remet 'normal' seulement si au moins 1 frame s'est ecoule
2308 time_control = TIME_NORMAL;
2309 reset_time = false;
2310 }
2311 acc-=10;
2312 try {
2313 overmind.step();
2314 }
2315 catch(std::exception *e) {
2316 msgbox("Exception caught from overmind.step(): %s\n", e->what());
2317 }
2318 reset_time=true;
2319 if(time_control == TIME_FREEZE)
2320 break;
2321 }
2322 }
2323 start_frame();
2324 if(ecran && !video_is_dumb) {
2325 try {
2326 ecran->draw_zone();
2327 }
2328 catch(std::exception *e) {
2329 msgbox("Exception caught from ecran->draw_zone(): %s\n", e->what());
2330 }
2331
2332 #ifdef FRAMECOUNTER
2333 static Dword lastvideoframe=0, lastoverframe=0;
2334 if(ecran->font) {
2335 if(overmind.framecount-lastoverframe > 500) {
2336 lastoverframe = overmind.framecount;
2337 lastvideoframe = video->framecount;
2338 }
2339 int up = 999;
2340 if(overmind.framecount-lastoverframe > 0)
2341 up = ((video->framecount-lastvideoframe) * 100) / (overmind.framecount-lastoverframe);
2342 video->vb->rect(0,0,50,20,0);
2343 char st[80];
2344 sprintf(st, "%i", up);
2345 ecran->font->draw(st, video->vb, 0, 0);
2346 }
2347 #endif /* FRAMECOUNTER */
2348 }
2349 end_frame();
2350
2351 #ifndef NDEBUG
2352 Uint8 *keystate = SDL_GetKeyState(NULL);
2353 if(keystate[SDLK_F8]) // F8 = buckage
2354 for(int j=0; j<8000000; j++)
2355 ;
2356 if(keystate[SDLK_F9]) // F8 = slow motion mode
2357 time_control = TIME_SLOW;
2358 if(keystate[SDLK_F10]) // F8 = turbo mode
2359 time_control = TIME_FAST;
2360 #endif
2361
2362 switch(time_control) {
2363 case TIME_FREEZE: acc = 10; break;
2364 case TIME_SLOW: acc += 1; break;
2365 case TIME_FAST: acc += 80; break;
2366 default: acc+=getmsec()-last;
2367 }
2368 if(acc > 300 && !video_is_dumb) {
2369 overmind.framecount+=acc-300;
2370 acc = 300; // pour eviter trop de depassement
2371 }
2372 if(acc > 10000) {
2373 msgbox("Not enough CPU time to be server!\n");
2374 overmind.framecount+=acc-10;
2375 acc=10;
2376 }
2377 }
2378 delete menu;
2379
2380 deinit_stuff();
2381 delete resmanager;
2382
2383 // FIXME: This is not the smoothest exit ever, a better way?
2384 if(demo_verif)
2385 exit(demo_verified_and_valid? 0 : 1);
2386 }
2387