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 "canvas.h"
22
23 #include <stdio.h>
24 #include "input.h"
25 #include "random.h"
26 #include "bloc.h"
27 #include "quadra.h"
28 #include "image_png.h"
29 #include "cfgfile.h"
30 #include "res.h"
31 #include "zone.h"
32 #include "game.h"
33 #include "global.h"
34 #include "sons.h"
35 #include "recording.h"
36 #include "chat_text.h"
37 #include "nglog.h"
38 #include "net_server.h"
39 #include "packets.h"
40
41 using std::max;
42 using std::min;
43 using std::vector;
44
Canvas(int qplayer,int game_seed,Palette * p)45 Canvas::Canvas(int qplayer, int game_seed, Palette *p):
46 bit(NULL), //Somebody somewhere will set that. Sucks.
47 rnd(game_seed),
48 sprlevel_up(NULL) {
49 // constructs a local Canvas
50 snapshot[0]=0;
51 best_move=best_clean=best_recurse=0;
52 memset(player_hash, 0, sizeof(player_hash));
53 memset(team_hash, 0, sizeof(team_hash));
54 collide_side_only=false;
55 should_remove_bonus=false;
56 wait_download=false;
57 z_lines=z_potatolines=z_linestot=z_potatolinestot=NULL;
58 team_potato_lines=team_potato_linestot=0;
59 send_for_clean=false;
60 handicap_crowd=0;
61 potato_team_on_last_stamp=255;
62 potato_lines=0;
63 gone_time=0;
64 inter=NULL;
65 h_repeat = v_repeat = 0;
66 smooth = shadow = false;
67 moves=NULL;
68 pal = p;
69 player = qplayer;
70 if(playback) {
71 color = playback->player[player].color;
72 strcpy(name, playback->player[player].name);
73 continuous=1;
74 } else {
75 handicap = config.player2[player].handicap;
76 color = config.player[player].color;
77 strcpy(name, config.player[player].name);
78 strcpy(team_name, config.player2[player].ngTeam);
79 continuous=config.player2[player].continuous;
80 }
81 remote_adr=NULL;
82 local_player = true;
83 hide();
84 init();
85 }
86
Canvas(int game_seed,Byte team,const char * nam,int ph_repeat,int pv_repeat,bool psmooth,bool pshadow,int phandicap,Net_connection * adr,int qplayer,bool wait_down)87 Canvas::Canvas(int game_seed, Byte team, const char *nam, int ph_repeat,
88 int pv_repeat, bool psmooth, bool pshadow, int phandicap,
89 Net_connection *adr, int qplayer, bool wait_down):
90 bit(NULL),
91 rnd(game_seed),
92 sprlevel_up(NULL) {
93 // constructs a remote Canvas
94 snapshot[0]=0;
95 best_move=best_clean=best_recurse=0;
96 memset(player_hash, 0, sizeof(player_hash));
97 memset(team_hash, 0, sizeof(team_hash));
98 collide_side_only=false;
99 should_remove_bonus=false;
100 wait_download=wait_down;
101 z_lines=z_potatolines=z_linestot=z_potatolinestot=NULL;
102 team_potato_lines=team_potato_linestot=0;
103 send_for_clean=false;
104 handicap_crowd=0;
105 potato_team_on_last_stamp=255;
106 potato_lines=0;
107 gone_time=0;
108 inter=NULL;
109 handicap = phandicap;
110 h_repeat = ph_repeat;
111 v_repeat = pv_repeat;
112 continuous=1; //Doesn't matter for remote canvas
113 smooth = psmooth;
114 shadow = pshadow;
115 moves=NULL;
116 pal = NULL;
117 player = qplayer;
118 color = team;
119 strncpy(name, nam, sizeof(name));
120 name[sizeof(name)-1]=0;
121 remote_adr=adr;
122 local_player = false;
123 hide();
124 init();
125 }
126
~Canvas()127 Canvas::~Canvas() {
128 delete over;
129 delete myself;
130 if(moves)
131 delete moves;
132 delete_bloc();
133 if (sprlevel_up)
134 SDL_FreeSurface(sprlevel_up);
135 while (!watchers.empty()) {
136 delete watchers.back();
137 watchers.pop_back();
138 }
139 }
140
long_name(bool handi,bool gone)141 char *Canvas::long_name(bool handi, bool gone) {
142 static char ret[64];
143 strcpy(ret, name);
144 if(handi) {
145 const char *h="";
146 switch(handicap) {
147 case 0: h=" (-)"; break;
148 case 1: h=" (A)"; break;
149 case 3: h=" (M)"; break;
150 case 4: h=" (+)"; break;
151 }
152 strcat(ret, h);
153 }
154 if(gone)
155 if(idle==3)
156 strcat(ret, " *");
157 return ret;
158 }
159
delete_bloc()160 void Canvas::delete_bloc() {
161 if(bloc)
162 delete bloc;
163 if(next)
164 delete next;
165 if(next2)
166 delete next2;
167 if(next3)
168 delete next3;
169 if(bloc_shadow)
170 delete bloc_shadow;
171 bloc = next = next2 = next3 = bloc_shadow = NULL;
172 }
173
init()174 void Canvas::init() {
175 trying_to_drop=false;
176 {
177 Res_doze res("gamelvup.png");
178 Png raw(res);
179 sprlevel_up = raw.new_surface();
180 SDL_SetColorKey(sprlevel_up, SDL_SRCCOLORKEY, 0);
181 }
182 over = new Overmind();
183 bloc = next = next2 = next3 = bloc_shadow = NULL;
184 reinit();
185 myself = new Executor();
186 myself->add(new Player_init(this));
187 over->start(myself);
188 }
189
reinit()190 void Canvas::reinit() {
191 set_message("", "");
192 int i,j;
193 watch_date = 0;
194 for(j=0; j<36; j++)
195 for(i=0; i<18; i++) {
196 occupied[j][i] = false;
197 block[j][i] = 0;
198 blinded[j][i] = 0;
199 bflash[j][i] = 0;
200 }
201 for(i=0; i<20; i++)
202 flash[i] = 0;
203 if(islocal()) {
204 if(playback) {
205 //playback->multi_mode is certainly false since this canvas
206 // is local. Thus, we don't care about handicap
207 h_repeat = v_repeat = playback->player[player].repeat;
208 continuous = 1;
209 smooth = playback->player[player].smooth ? true:false;
210 shadow = playback->player[player].shadow ? true:false;
211 } else {
212 handicap = config.player2[player].handicap;
213 h_repeat = config.player2[player].h_repeat;
214 v_repeat = config.player2[player].v_repeat;
215 continuous = config.player2[player].continuous;
216 smooth = config.player[player].smooth ? true:false;
217 shadow = config.player[player].shadow ? true:false;
218 }
219 }
220 if(game->normal_attack.type==ATTACK_BLIND || game->normal_attack.type==ATTACK_FULLBLIND)
221 shadow=true;
222 if(game->potato_normal_attack.type==ATTACK_BLIND || game->potato_normal_attack.type==ATTACK_FULLBLIND)
223 shadow=true;
224 switch(h_repeat) {
225 case 0:
226 h_repeat_delay = 11;
227 break;
228 case 1:
229 h_repeat_delay = 6;
230 break;
231 case 2:
232 h_repeat_delay = 3;
233 break;
234 case 3:
235 h_repeat_delay = 1;
236 break;
237 }
238 side_speed = (18<<4)/h_repeat_delay;
239 switch(v_repeat) {
240 case 0:
241 v_repeat_delay = 11;
242 break;
243 case 1:
244 v_repeat_delay = 6;
245 break;
246 case 2:
247 v_repeat_delay = 3;
248 break;
249 case 3:
250 v_repeat_delay = 1;
251 break;
252 }
253 down_speed = (340)/v_repeat_delay;
254 if(down_speed > 180)
255 down_speed = 180;
256 level = game->level_start;
257 depth = complexity = bonus = 0;
258 stats[LINESCUR].set_value(0);
259 level_up = color_flash = 0;
260 over->framecount = 0; // initialize the counter when the player starts
261 if(game->net_version()>=23 && game->survivor)
262 idle = 2;
263 else
264 idle = 1;
265 dying = false;
266 state = PLAYING;
267 handicap_crowd=0;
268 potato_team_on_last_stamp=255;
269 send_for_clean=false;
270 should_remove_bonus=false;
271 }
272
restart()273 void Canvas::restart() {
274 if(islocal())
275 clear_key_all();
276 init_block();
277 clear_tmp();
278 delete_bloc();
279 set_next();
280 last_attacker = 255;
281 for(int i=0; i<MAXPLAYERS; i++) {
282 attacks[i] = 0;
283 handicaps[i] = 0;
284 }
285 level = game->level_start;
286 depth = complexity = bonus = 0;
287 stats[LINESCUR].set_value(0);
288 level_up = color_flash = 0;
289 calc_speed();
290 idle = 1; // starts 'idle' to allow joins if the game is on pause and the player just started
291 state = PLAYING;
292 dying=false;
293 handicap_crowd=0;
294 potato_team_on_last_stamp=255;
295 send_for_clean=false;
296 should_remove_bonus=false;
297 set_message("", "");
298 }
299
clear_key_all()300 void Canvas::clear_key_all() {
301 for(int i=0; i<7; i++) // clears the key states of the player
302 clear_key(i);
303 }
304
calc_shadow()305 void Canvas::calc_shadow() {
306 if(!bloc_shadow)
307 bloc_shadow = new Bloc(bloc->type, 8, 0, 0);
308 bloc_shadow->rot = bloc->rot;
309 bloc_shadow->bx = bloc->bx;
310 bloc_shadow->by = bloc->by;
311 while(!check_collide(bloc_shadow, bloc_shadow->bx, bloc_shadow->by+1, bloc_shadow->rot))
312 bloc_shadow->by++;
313 bloc_shadow->calc_xy();
314 //bloc_shadow->x = bloc->x;
315 }
316
init_block()317 void Canvas::init_block() {
318 int x, y;
319 for(y=32; y < 36; y++)
320 for(x = 0; x < 18; x++)
321 occupied[y][x] = true;
322 for(y = 0; y < 32; y++) {
323 for(x = 0; x < 4; x++) {
324 occupied[y][x] = true;
325 occupied[y][x+14] = true;
326 }
327 for(x = 4; x < 14; x++) {
328 block[y][x] = 0;
329 occupied[y][x] = false;
330 }
331 }
332 for(x = 0; x < 18; x++)
333 tmp[32][x] = 1;
334 for(y = 0; y < 32; y++) {
335 for(x = 0; x < 4; x++)
336 tmp[y][x] = 1;
337 for(x = 14; x < 18; x++)
338 tmp[y][x] = 1;
339 }
340 for(y = 0; y < 36; y++)
341 for(x = 0; x < 14; x++)
342 dirted[y][x]=2;
343 for(y = 0; y < 36; y++)
344 for(x = 0; x < 18; x++) {
345 blinded[y][x] = 0;
346 bflash[y][x] = 0;
347 }
348 }
349
draw_block(int j,int i) const350 void Canvas::draw_block(int j, int i) const {
351 Byte side, col, to[4];
352 side = block[j][i]&15;
353 col = block[j][i]>>4;
354 to[0] = block[j][i-1];
355 to[1] = block[j-1][i];
356 to[2] = block[j][i+1];
357 to[3] = block[j+1][i];
358 raw_draw_bloc_corner(*screen, (i-4)*18, (j-12)*18, side, ::color[col],to);
359 }
360
calc_speed()361 void Canvas::calc_speed() {
362 //speed = level * 4;
363 if(level<=10)
364 speed = 4 + (level-1)*5;
365 else
366 speed = 50 + (level-10)*3;
367 }
368
set_next()369 void Canvas::set_next() {
370 if(znext) {
371 znext->set_next(next);
372 znext2->set_next(next2);
373 znext3->set_next(next3);
374 }
375 }
376
set_message(const char * m1,const char * m2)377 void Canvas::set_message(const char *m1, const char *m2) {
378 strncpy(msg1, m1, sizeof(msg1));
379 msg1[sizeof(msg1)-1]=0;
380 strncpy(msg2, m2, sizeof(msg2));
381 msg2[sizeof(msg2)-1]=0;
382 }
383
add_text_scroller(const char * st,int xoffset,int yoffset)384 void Canvas::add_text_scroller(const char *st, int xoffset, int yoffset) {
385 if(inter && !small_watch) { // if the canvas is currently visible
386 Executor *tmp2 = new Executor(true);
387 tmp2->add(new Player_text_scroll(this, st, xoffset, yoffset));
388 over->start(tmp2);
389 }
390 }
391
blind_all(Byte time)392 void Canvas::blind_all(Byte time) {
393 if(idle<2 && !dying) {
394 int x, y;
395 for(y = 0; y < 36; y++)
396 for(x = 0; x < 18; x++) {
397 int tmp = blinded[y][x];
398 if(occupied[y][x]) {
399 if(!tmp && time) {
400 bflash[y][x]=32;
401 dirted[y][x]=2;
402 }
403 tmp=min(255, tmp+time);
404 blinded[y][x] = tmp;
405 }
406 }
407 }
408 }
409
add_packet(Canvas * sender,Byte nb,Byte nc,Byte lx,Attack attack,Word hole_pos[])410 void Canvas::add_packet(Canvas *sender, Byte nb, Byte nc, Byte lx, Attack attack, Word hole_pos[]) {
411 int x, qui;
412 if(!sender)
413 return;
414
415 Packet_serverlog log("player_attacked");
416 log.add(Packet_serverlog::Var("id", id()));
417 log.add(Packet_serverlog::Var("attacker_id", sender->id()));
418 log.add(Packet_serverlog::Var("type", attack.log_type()));
419 log.add(Packet_serverlog::Var("size", attack.type==ATTACK_FULLBLIND? nb*nc:nb));
420 if(game->net_server)
421 game->net_server->record_packet(&log);
422
423 //Nothing further to do if attack is none.
424 if(attack.type==ATTACK_NONE)
425 return;
426
427 //Update last_attacker and attacks array
428 qui = game->net_list.canvas2player(sender);
429 int temp = attacks[qui] + nb*2;
430 if(temp > 255)
431 temp = 255;
432 attacks[qui] = temp;
433 if(last_attacker != 255) { // if there is a last attacker
434 if(attacks[qui] >= attacks[last_attacker]) // if larger or equal to the last best
435 last_attacker = qui; // it's the one that becomes the best
436 } else
437 last_attacker = qui;
438
439 //If full-blind attack, blind canvas and return
440 if(attack.type==ATTACK_FULLBLIND) {
441 blind_all(nb*nc*attack.param);
442 return;
443 }
444
445 //Attack type is either blind or lines, add bonuses
446 if(bonus < 20) {
447 if(nb+bonus > 20)
448 nb = 20-bonus;
449 nc--;
450 int normal = max(nb - nc, 0);
451 int fucked = nb - normal;
452 if(game->net_version()>=23) {
453 for(x=0; x<nb; x++) {
454 bon[x+bonus].x = 127;
455 bon[x+bonus].color = sender->color;
456 bon[x+bonus].blind_time = attack.type==ATTACK_BLIND? attack.param:0;
457 bon[x+bonus].hole_pos=hole_pos[x];
458 if(x==nb-1)
459 bon[x+bonus].final=true;
460 else
461 bon[x+bonus].final=false;
462 }
463 bonus += nb;
464 }
465
466 if(game->net_version()<23) {
467 for(x=0; x<normal; x++) {
468 bon[x+bonus].x = lx;
469 bon[x+bonus].color = sender->color;
470 bon[x+bonus].blind_time = attack.type==ATTACK_BLIND? attack.param:0;
471 }
472 bonus += normal;
473
474 for(x=0; x<fucked; x++) {
475 lx += nc;
476 while(lx > 13)
477 lx -= 10;
478 bon[x+bonus].x = lx;
479 bon[x+bonus].color = sender->color;
480 bon[x+bonus].blind_time = attack.type==ATTACK_BLIND? attack.param:0;
481 }
482 bonus += fucked;
483 }
484 }
485 }
486
give_line()487 void Canvas::give_line() {
488 if(!depth)
489 return;
490 int i, score_add;
491 int clean_bonus=0;
492 bool log_it=false;
493 Word move_value=(depth<<8)+complexity;
494 if(send_for_clean) {
495 clean_bonus=(1+depth)/2;
496 if(move_value>best_clean) {
497 log_it=true;
498 best_clean=move_value;
499 }
500 }
501 if(move_value>best_move) {
502 log_it=true;
503 best_move=move_value;
504 }
505 move_value=(complexity<<8)+depth;
506 if(move_value>best_recurse) {
507 log_it=true;
508 best_recurse=move_value;
509 }
510 if(log_it) {
511 Packet_serverlog log("player_snapshot");
512 log.add(Packet_serverlog::Var("id", id()));
513 log.add(Packet_serverlog::Var("lines", depth));
514 log.add(Packet_serverlog::Var("clean", send_for_clean? "true":"false"));
515 log.add(Packet_serverlog::Var("combo", complexity));
516 log.add(Packet_serverlog::Var("snapshot", snapshot));
517 if(game->net_server)
518 game->net_server->record_packet(&log);
519 }
520 switch(depth) {
521 case 1: score_add = 250; break;
522 case 2: score_add = 500; break;
523 case 3: score_add = 1000; break;
524 case 4: score_add = 2000; break;
525 default: score_add = 200 * depth * depth; break;
526 }
527 int complexity_points=1000*(complexity-1);
528 if(game->net_version()>=23)
529 complexity_points=200*(complexity-1)*(complexity-1);
530 score_add += complexity_points;
531 //0 clean_points for net_version<23 cause it was added in check_clean
532 if(send_for_clean && game->net_version()>=23) {
533 int clean_points;
534 if(depth<=4)
535 clean_points=depth*1250;
536 else
537 clean_points=depth*depth*500;
538 score_add += clean_points;
539 }
540 score_add += (score_add/10)*level;
541 stats[SCORE].add(score_add);
542 i = depth-1;
543 bool enough=(depth >= game->combo_min);
544 if(game->net_version()==23) {
545 int alive_count=0;
546 for(i=0; i<MAXPLAYERS; i++) {
547 Canvas *c=game->net_list.get(i);
548 if(c && c->idle<2)
549 alive_count++;
550 }
551 //alive_count adjustment only if there's at least 5 alive
552 if(alive_count>4)
553 alive_count-=4;
554 else
555 alive_count=0;
556 i = max(0, depth-1-alive_count);
557 // this is a bug, it should have been done like net_version >= 24 (below)
558 // but it must remain as is for network compatibility
559 enough=i? true:false;
560 }
561 if(game->net_version()>=24) {
562 if(!send_for_clean && !game->boring_rules)
563 while(i && handicap_crowd >= stamp_per_handicap) {
564 handicap_crowd -= stamp_per_handicap;
565 --i;
566 }
567 if(!i)
568 enough = false;
569 }
570
571 Packet_serverlog log("player_lines_cleared");
572 log.add(Packet_serverlog::Var("id", id()));
573 log.add(Packet_serverlog::Var("lines", depth));
574 log.add(Packet_serverlog::Var("clean", send_for_clean? "true":"false"));
575 log.add(Packet_serverlog::Var("attack_size", clean_bonus+(enough? i:0)));
576 log.add(Packet_serverlog::Var("combo", complexity));
577 log.add(Packet_serverlog::Var("points", score_add));
578 if(game->net_server)
579 game->net_server->record_packet(&log);
580
581 Attack clean_att, normal_att;
582 normal_att=game->normal_attack;
583 clean_att=game->clean_attack;
584 if(game->hot_potato && color==game->potato_team) {
585 normal_att=game->potato_normal_attack;
586 clean_att=game->potato_clean_attack;
587 }
588 if(color==game->potato_team && enough)
589 potato_lines += (i+clean_bonus);
590 if(send_for_clean) {
591 game->net_list.send(this, clean_bonus, complexity, last_x, clean_att, true);
592 if(chat_text) {
593 char st[256];
594 int num;
595 if(normal_att.type==ATTACK_NONE) {
596 num=depth;
597 if(num>1)
598 sprintf(st, "Clean canvas: %s clears %i lines!", name, num);
599 else
600 sprintf(st, "Clean canvas: %s clears 1 line!", name);
601 }
602 else {
603 num=clean_bonus;
604 if(enough)
605 num+=i;
606 if(num>1)
607 sprintf(st, "Clean canvas: %s sends %i lines!", name, num);
608 else
609 sprintf(st, "Clean canvas: %s sends 1 line!", name);
610 }
611 message(color, st);
612 }
613 }
614 if(i && enough) { // sends nothing if depth < combo_min
615 if(i>=3 && chat_text && !send_for_clean) {
616 // if does a 'quad' minimally and not clean
617 char st[256];
618 if(normal_att.type == ATTACK_NONE)
619 sprintf(st, "%s clears %i line%s.", name, depth, depth!=1? "s":"");
620 else
621 sprintf(st, "%s sends %i line%s.", name, i, i != 1 ? "s" : "");
622 message(color, st);
623 }
624 game->net_list.send(this, i, complexity, last_x, normal_att, false);
625 if(inter && !small_watch) { // if the canvas is currently visible
626 char st[256];
627 if(depth == 2)
628 sprintf(st, "Double! %i pts", score_add);
629 if(depth == 3)
630 sprintf(st, "Triple! %i pts", score_add);
631 if(depth == 4)
632 sprintf(st, "Quad! %i pts", score_add);
633 if(depth > 4)
634 sprintf(st, "%i-lines! %i pts", depth, score_add);
635 add_text_scroller(st, 20);
636 }
637 }
638 i=depth-1; //For stats accounting, use old numbers
639 if(i >= 14) {
640 stats[CLEAR14+i-14].add(1);
641 i = 14; //Add in CLEARMORE
642 }
643 stats[CLEAR00+i].add(1);
644 stats[COMBO00+complexity-1].add(1);
645
646 stats[LINESCUR].add(depth);
647 stats[LINESTOT].add(depth);
648 if(game->level_up && stats[LINESCUR].get_value() >= level*15) {
649 level++;
650 calc_speed();
651 Executor *tmp = new Executor(true);
652 tmp->add(new Player_level_up(this));
653 over->start(tmp);
654 }
655 depth = 0;
656 complexity=0;
657 send_for_clean=false;
658 }
659
change_level_single()660 void Canvas::change_level_single() {
661 change_level(level, pal, bit);
662 //video->setpal(*pal);
663 if(level <= 10 && (level-1) > config.info.unlock_theme && !playback) {
664 config.info.unlock_theme = level-1;
665 config.write();
666 }
667 }
668
change_level(const int level,Palette * pal,Bitmap * bit)669 void Canvas::change_level(const int level, Palette *pal, Bitmap *bit) {
670 int num, i;
671 num = (level-1)%10;
672 // if(level>5)
673 // num=config.info.multi_level-1;
674 sprintf(st, "fond%i.png", num);
675 if(level==-1)
676 strcpy(st, "black.png");
677 Res_doze *res = new Res_doze(st);
678 Png img(*res);
679 bit->reload(img);
680 Palette pal2(img);
681 for(i=0; i<256; i++) // was 184
682 pal->setcolor(i, pal2.r(i), pal2.g(i), pal2.b(i));
683
684 for(i=0; i<MAXTEAMS-1; i++)
685 fteam[i]->colorize(*pal, pal2.r(i*8+184), pal2.g(i*8+184), pal2.b(i*8+184));
686 fteam[MAXTEAMS-1]->colorize(*pal, 170, 170, 170);
687
688 video->need_paint = 2;
689 delete res;
690
691 delete sons.flash;
692 delete sons.depose3;
693 delete sons.depose2;
694 delete sons.depose;
695 delete sons.drip;
696
697 sons.flash = sons.depose3 = sons.depose2 = sons.depose = sons.drip = NULL;
698 const char *foo0, *foo1, *foo2, *foo3, *foo4;
699 switch(num) {
700 case 1:
701 foo0="Pwap2.wav";
702 foo1=foo2=foo3="Knock.wav";
703 foo4="click_3.wav";
704 break;
705 case 2:
706 foo0="Blip1.wav";
707 foo1="metal3.wav";
708 foo2="Metal1.wav";
709 foo3="Metal6.wav";
710 foo4="click_1.wav";
711 break;
712 case 3:
713 foo0="Whistle1.wav";
714 foo1="Tapdrip.wav";
715 foo2="Click01.wav";
716 foo3="click_3.wav";
717 foo4="Click01.wav";
718 break;
719 case 4:
720 foo0="Spring.wav";
721 foo1="Pop1.wav";
722 foo2="bloop.wav";
723 foo3="Pwap2.wav";
724 foo4="corkpop.wav";
725 break;
726 case 5:
727 foo0="Whistle2.wav";
728 foo1="Knock.wav";
729 foo2="Splodge.wav";
730 foo3="Pop1.wav";
731 foo4="Tapdrip.wav";
732 break;
733 case 6:
734 foo0="Glass04.wav";
735 foo1="Glass01.wav";
736 foo2="Glass03.wav";
737 foo3="Glass03.wav";
738 foo4="Click01.wav";
739 break;
740 case 7:
741 foo0="Bubble2.wav";
742 foo1="Water05_1.wav";
743 foo2="water05_2.wav";
744 foo3="water05_3.wav";
745 foo4="Click01.wav";
746 break;
747 case 8:
748 foo0="Ceramic3.wav";
749 foo1="Explod03.wav";
750 foo2="Explod05.wav";
751 foo3="Explod06.wav";
752 foo4="Tapdrip.wav";
753 break;
754 case 9:
755 foo0="Smash2.wav";
756 foo1="Knock.wav";
757 foo2="bloop.wav";
758 foo3="click_1.wav";
759 foo4="Pop1.wav";
760 break;
761 default:
762 foo0="Pwap2.wav";
763 foo1="Hitwood1.wav";
764 foo2="Chop2.wav";
765 foo3="metal3.wav";
766 foo4="Tapdrip.wav";
767 break;
768 }
769 sons.flash = new Sample(Res_doze(foo0)); // when we do a ligne (flash)
770 sons.depose3 = new Sample(Res_doze(foo1)); // drop
771 sons.depose2 = new Sample(Res_doze(foo2)); // drop
772 sons.depose = new Sample(Res_doze(foo3)); // drop
773 sons.drip = new Sample(Res_doze(foo4)); // rotate
774 }
775
clear_tmp()776 void Canvas::clear_tmp() {
777 int i, j;
778 for(j = 0; j < 32; j++)
779 for(i = 4; i < 14; i++)
780 tmp[j][i] = 0;
781 for(j=0; j<36; j++)
782 for(i=0; i<18; i++)
783 moved[j][i]=false;
784 }
785
set_canvas_pos(int px,int py,Bitmap * fo,Video_bitmap * s,Zone_next * z,Zone_next * z2,Zone_next * z3,Inter * in)786 void Canvas::set_canvas_pos(int px, int py, Bitmap *fo, Video_bitmap *s, Zone_next *z, Zone_next *z2, Zone_next *z3, Inter *in) {
787 x = px;
788 y = py;
789 fond = fo;
790 screen = s;
791 znext = z;
792 znext2 = z2;
793 znext3 = z3;
794 set_next();
795 inter = in;
796 small_watch = false;
797 }
798
hide()799 void Canvas::hide() {
800 x = 0;
801 y = 0;
802 fond = NULL;
803 screen = NULL;
804 znext = NULL;
805 znext2 = NULL;
806 znext3 = NULL;
807 inter = NULL;
808 z_lines=z_potatolines=z_linestot=z_potatolinestot=NULL;
809 }
810
check_key(int i)811 Byte Canvas::check_key(int i) {
812 if(ecran && ecran->focus) { // prevents controlling while inputting
813 // into a zone_text_input that has the
814 // focus
815 clear_key(i); // prevents rotating from happening after an input
816 // (because bit 'was released!')
817 input->allow_key_repeat(true);
818 return 0;
819 } else {
820 input->allow_key_repeat(false);
821 if(i < 5)
822 return input->keys[config.player[player].key[i]];
823 else
824 return input->keys[config.player2[player].key[i - 5]];
825 }
826 }
827
clear_key(int i)828 void Canvas::clear_key(int i) {
829 if(i < 5)
830 input->keys[config.player[player].key[i]] = 0;
831 else
832 input->keys[config.player2[player].key[i - 5]] = 0;
833 }
834
unrelease_key(int i)835 void Canvas::unrelease_key(int i) {
836 if(i<5)
837 input->keys[config.player[player].key[i]] &= ~RELEASED;
838 else
839 input->keys[config.player2[player].key[i-5]] &= ~RELEASED;
840 }
841
blit_level_up()842 void Canvas::blit_level_up() {
843 screen->put_surface(sprlevel_up, 10, level_up - 30);
844 dirt_rect(10, level_up - 30, sprlevel_up->w, sprlevel_up->h);
845 }
846
blit_flash()847 void Canvas::blit_flash() {
848 int i, j, pj;
849 for(j = 0; j < 20; j++) {
850 pj = flash[j];
851 if(pj) {
852 for(i=0; i<18; i++)
853 screen->hline((pj-12)*18+i, 0, 10*18-1, color_flash);
854 dirt_rect(0, (pj-12)*18, 10*18, 18);
855 }
856 }
857 }
858
dirt_rect(int x1,int y1,int w1,int h1)859 void Canvas::dirt_rect(int x1, int y1, int w1, int h1) {
860 int i,j;
861 w1 = (x1+w1+17)/18;
862 h1 = (y1+h1+17)/18;
863 x1 = x1/18;
864 y1 = y1/18;
865 x1 = max(0,x1);
866 y1 = max(0,y1);
867 w1 = min(10,w1);
868 h1 = min(20,h1);
869 for(j=y1; j<h1; j++)
870 for(i=x1; i<w1; i++)
871 dirted[j+12][i+4]=2;
872 }
873
collide(Byte px,Byte py,Byte rot)874 bool Canvas::collide(Byte px, Byte py, Byte rot) {
875 return check_collide(bloc, px, py, rot);
876 }
877
check_collide(Bloc * blo,Byte px,Byte py,Byte rot)878 bool Canvas::check_collide(Bloc *blo, Byte px, Byte py, Byte rot) {
879 collide_side_only=true;
880 bool ret=false;
881 int i,j;
882 for(j = 0; j < 4; j++)
883 for(i = 0; i < 4; i++) {
884 if(blo->bloc[bloc->type][rot][j][i])
885 if(occupied[py + j][px + i]) {
886 if(px+i>=4 && px+i<14)
887 collide_side_only=false;
888 ret=true;
889 }
890 }
891 if(ret) {
892 return ret;
893 }
894 else {
895 collide_side_only=false;
896 return false;
897 }
898 }
899
step_bflash()900 void Canvas::step_bflash() {
901 int j, i;
902 for(j = 12; j < 32; j++)
903 for(i = 4; i < 14; i++) {
904 if(occupied[j][i] && bflash[j][i]) {
905 if(!(bflash[j][i]&3))
906 dirted[j][i] = 2;
907 bflash[j][i]--;
908 if(!bflash[j][i])
909 dirted[j][i] = 2;
910 }
911 }
912 }
913
blit_back()914 void Canvas::blit_back() {
915 step_bflash();
916 video->clone_palette(fond->surface);
917 int j, i, x2, y2;
918 for(j = 12; j < 32; j++)
919 for(i = 4; i < 14; i++) {
920 if(dirted[j][i]) {
921 bool blitbloc=true;
922 if(!occupied[j][i])
923 blitbloc=false;
924 if(bflash[j][i])
925 if(bflash[j][i]&4)
926 blitbloc=false;
927 if(!bflash[j][i] && blinded[j][i])
928 blitbloc=false;
929 if(blitbloc) {
930 draw_block(j,i);
931 } else {
932 x2=(i-4)*18;
933 y2=(j-12)*18;
934 SDL_Rect rect;
935 rect.x = x2;
936 rect.y = y2;
937 rect.w = rect.h = 18;
938 screen->put_surface(fond->surface, rect, x2, y2);
939 }
940 dirted[j][i]--;
941 }
942 }
943 if(msg1[0] && inter)
944 inter->font->draw(msg1, video->vb, x+5, y+40);
945 if(msg2[0] && inter)
946 inter->font->draw(msg2, video->vb, x+5, y+60);
947 }
948
blit_bloc(Bloc * blo)949 void Canvas::blit_bloc(Bloc *blo) {
950 int j,i,bx,by,tx,ty;
951 if(!blo)
952 return;
953 if(smooth) {
954 blo->draw(*screen);
955 } else {
956 blo->draw(*screen, (blo->bx-4)*18, (blo->by-12)*18);
957 }
958 for(j=0; j<4; j++)
959 for(i=0; i<4; i++) {
960 if(blo->bloc[blo->type][blo->rot][j][i]) {
961 if(smooth) {
962 tx=(blo->x>>4)+4*18;
963 ty=(blo->y>>4)+12*18;
964 } else {
965 tx=blo->bx*18;
966 ty=blo->by*18;
967 }
968 tx += i*18;
969 ty += j*18;
970 bx=tx/18;
971 by=ty/18;
972 if(by>=0)
973 dirted[by][bx]=2;
974 bx=(tx+17)/18;
975 if(by>=0)
976 dirted[by][bx]=2;
977 by=(ty+17)/18;
978 if(by>=0)
979 dirted[by][bx]=2;
980 bx=tx/18;
981 if(by>=0)
982 dirted[by][bx]=2;
983 }
984 }
985 }
986
small_draw_block(int j,int i) const987 void Canvas::small_draw_block(int j, int i) const {
988 Byte side, col;
989 side = block[j][i]&15;
990 col = block[j][i]>>4;
991 raw_small_draw_bloc(*screen, (i-4)*6, (j-12)*6, side, ::color[col]);
992 }
993
small_blit_back()994 void Canvas::small_blit_back() {
995 step_bflash();
996 video->clone_palette(fond->surface);
997 int j, i, x2, y2;
998 for(j = 12; j < 32; j++)
999 for(i = 4; i < 14; i++)
1000 if(dirted[j][i]) {
1001 bool blitbloc=true;
1002 if(!occupied[j][i])
1003 blitbloc=false;
1004 if(bflash[j][i])
1005 if(bflash[j][i]&4)
1006 blitbloc=false;
1007 if(!bflash[j][i] && blinded[j][i])
1008 blitbloc=false;
1009 if(blitbloc) {
1010 small_draw_block(j,i);
1011 } else {
1012 x2=(i-4)*6;
1013 y2=(j-12)*6;
1014 SDL_Rect rect;
1015 rect.x = x2;
1016 rect.y = y2;
1017 rect.w = rect.h = 6;
1018 screen->put_surface(fond->surface, rect, x2, y2);
1019 }
1020 dirted[j][i]--;
1021 }
1022 }
1023
small_blit_bloc(Bloc * blo)1024 void Canvas::small_blit_bloc(Bloc *blo) {
1025 int j,i,bx,by,tx,ty;
1026 if(!blo)
1027 return;
1028 blo->small_draw(*screen, (blo->bx-4)*6, (blo->by-12)*6);
1029 for(j=0; j<4; j++)
1030 for(i=0; i<4; i++) {
1031 if(blo->bloc[blo->type][blo->rot][j][i]) {
1032 tx=blo->bx*6;
1033 ty=blo->by*6;
1034 tx += i*6;
1035 ty += j*6;
1036 bx=tx/6;
1037 by=ty/6;
1038 if(by>=0)
1039 dirted[by][bx]=2;
1040 bx=(tx+5)/6;
1041 if(by>=0)
1042 dirted[by][bx]=2;
1043 by=(ty+5)/6;
1044 if(by>=0)
1045 dirted[by][bx]=2;
1046 bx=tx/6;
1047 if(by>=0)
1048 dirted[by][bx]=2;
1049 }
1050 }
1051 }
1052
small_blit_flash()1053 void Canvas::small_blit_flash() {
1054 int i, j, pj;
1055 for(j = 0; j < 20; j++) {
1056 pj = flash[j];
1057 if(pj) {
1058 for(i=0; i<6; i++)
1059 screen->hline((pj-12)*6+i, 0, 10*6-1, color_flash);
1060 dirt_rect(0, (pj-12)*18, 10*18, 18);
1061 }
1062 }
1063 }
1064
add_watcher(Watcher * w)1065 void Canvas::add_watcher(Watcher *w) {
1066 watchers.push_back(w);
1067 }
1068
remove_watcher(Net_connection * nc)1069 void Canvas::remove_watcher(Net_connection *nc) {
1070 vector<Watcher*>::iterator it = watchers.begin();
1071
1072 while (it != watchers.end()) {
1073 if ((*it)->nc == nc) {
1074 delete *it;
1075 watchers.erase(it);
1076 it = watchers.begin();
1077 } else
1078 ++it;
1079 }
1080 }
1081
islocal() const1082 bool Canvas::islocal() const {
1083 return local_player;
1084 }
1085
start_moves()1086 void Canvas::start_moves() {
1087 if(game->wants_moves) {
1088 if(moves)
1089 delete moves;
1090 moves=new Packet_clientmoves();
1091 moves->player = num_player;
1092 }
1093 }
1094
send_p_moves()1095 void Canvas::send_p_moves() {
1096 if(game->wants_moves) {
1097 if(moves) {
1098 net->sendtcp(moves);
1099 delete moves;
1100 moves=NULL;
1101 }
1102 }
1103 }
1104
start_byte()1105 void Canvas::start_byte() {
1106 if(game->wants_moves) {
1107 if(!moves)
1108 start_moves();
1109 if(moves->size>=50) {
1110 send_p_moves();
1111 start_moves();
1112 }
1113 moves->start_byte();
1114 }
1115 }
1116
set_bit(int v)1117 void Canvas::set_bit(int v) {
1118 if(moves)
1119 moves->set_bit(v);
1120 }
1121
write_byte()1122 void Canvas::write_byte() {
1123 if(moves)
1124 moves->write_byte();
1125 }
1126