1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
5 * Holger Schemel *
6 * Detmolder Strasse 189 *
7 * 33604 Bielefeld *
8 * Germany *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
11 * game.c *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
29
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
63
64 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
65
66 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
67
68 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
69
70 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
71
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y) \
74 GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbled(x, y) \
76 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
78 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y) \
80 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
81 #else
82 #define TEST_DrawLevelField(x, y) \
83 DrawLevelField(x, y)
84 #define TEST_DrawLevelFieldCrumbled(x, y) \
85 DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
87 DrawLevelFieldCrumbledNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y) \
89 DrawTwinkleOnField(x, y)
90 #endif
91
92
93 /* for DigField() */
94 #define DF_NO_PUSH 0
95 #define DF_DIG 1
96 #define DF_SNAP 2
97
98 /* for MovePlayer() */
99 #define MP_NO_ACTION 0
100 #define MP_MOVING 1
101 #define MP_ACTION 2
102 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
103
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT 0
106 #define SCROLL_GO_ON 1
107
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START 0
110 #define EX_TYPE_NONE 0
111 #define EX_TYPE_NORMAL (1 << 0)
112 #define EX_TYPE_CENTER (1 << 1)
113 #define EX_TYPE_BORDER (1 << 2)
114 #define EX_TYPE_CROSS (1 << 3)
115 #define EX_TYPE_DYNA (1 << 4)
116 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
117
118 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
122
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME (PANEL_XPOS(game.panel.time))
139 #define YY_TIME (PANEL_YPOS(game.panel.time))
140
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1 (DX + XX_LEVEL1)
143 #define DX_LEVEL2 (DX + XX_LEVEL2)
144 #define DX_LEVEL (DX + XX_LEVEL)
145 #define DY_LEVEL (DY + YY_LEVEL)
146 #define DX_EMERALDS (DX + XX_EMERALDS)
147 #define DY_EMERALDS (DY + YY_EMERALDS)
148 #define DX_DYNAMITE (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE (DY + YY_DYNAMITE)
150 #define DX_KEYS (DX + XX_KEYS)
151 #define DY_KEYS (DY + YY_KEYS)
152 #define DX_SCORE (DX + XX_SCORE)
153 #define DY_SCORE (DY + YY_SCORE)
154 #define DX_TIME1 (DX + XX_TIME1)
155 #define DX_TIME2 (DX + XX_TIME2)
156 #define DX_TIME (DX + XX_TIME)
157 #define DY_TIME (DY + YY_TIME)
158
159 #if 1
160 /* game panel display and control definitions */
161
162 #define GAME_PANEL_LEVEL_NUMBER 0
163 #define GAME_PANEL_GEMS 1
164 #define GAME_PANEL_INVENTORY_COUNT 2
165 #define GAME_PANEL_INVENTORY_FIRST_1 3
166 #define GAME_PANEL_INVENTORY_FIRST_2 4
167 #define GAME_PANEL_INVENTORY_FIRST_3 5
168 #define GAME_PANEL_INVENTORY_FIRST_4 6
169 #define GAME_PANEL_INVENTORY_FIRST_5 7
170 #define GAME_PANEL_INVENTORY_FIRST_6 8
171 #define GAME_PANEL_INVENTORY_FIRST_7 9
172 #define GAME_PANEL_INVENTORY_FIRST_8 10
173 #define GAME_PANEL_INVENTORY_LAST_1 11
174 #define GAME_PANEL_INVENTORY_LAST_2 12
175 #define GAME_PANEL_INVENTORY_LAST_3 13
176 #define GAME_PANEL_INVENTORY_LAST_4 14
177 #define GAME_PANEL_INVENTORY_LAST_5 15
178 #define GAME_PANEL_INVENTORY_LAST_6 16
179 #define GAME_PANEL_INVENTORY_LAST_7 17
180 #define GAME_PANEL_INVENTORY_LAST_8 18
181 #define GAME_PANEL_KEY_1 19
182 #define GAME_PANEL_KEY_2 20
183 #define GAME_PANEL_KEY_3 21
184 #define GAME_PANEL_KEY_4 22
185 #define GAME_PANEL_KEY_5 23
186 #define GAME_PANEL_KEY_6 24
187 #define GAME_PANEL_KEY_7 25
188 #define GAME_PANEL_KEY_8 26
189 #define GAME_PANEL_KEY_WHITE 27
190 #define GAME_PANEL_KEY_WHITE_COUNT 28
191 #define GAME_PANEL_SCORE 29
192 #define GAME_PANEL_HIGHSCORE 30
193 #define GAME_PANEL_TIME 31
194 #define GAME_PANEL_TIME_HH 32
195 #define GAME_PANEL_TIME_MM 33
196 #define GAME_PANEL_TIME_SS 34
197 #define GAME_PANEL_FRAME 35
198 #define GAME_PANEL_SHIELD_NORMAL 36
199 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
200 #define GAME_PANEL_SHIELD_DEADLY 38
201 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
202 #define GAME_PANEL_EXIT 40
203 #define GAME_PANEL_EMC_MAGIC_BALL 41
204 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
205 #define GAME_PANEL_LIGHT_SWITCH 43
206 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
207 #define GAME_PANEL_TIMEGATE_SWITCH 45
208 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
209 #define GAME_PANEL_SWITCHGATE_SWITCH 47
210 #define GAME_PANEL_EMC_LENSES 48
211 #define GAME_PANEL_EMC_LENSES_TIME 49
212 #define GAME_PANEL_EMC_MAGNIFIER 50
213 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
214 #define GAME_PANEL_BALLOON_SWITCH 52
215 #define GAME_PANEL_DYNABOMB_NUMBER 53
216 #define GAME_PANEL_DYNABOMB_SIZE 54
217 #define GAME_PANEL_DYNABOMB_POWER 55
218 #define GAME_PANEL_PENGUINS 56
219 #define GAME_PANEL_SOKOBAN_OBJECTS 57
220 #define GAME_PANEL_SOKOBAN_FIELDS 58
221 #define GAME_PANEL_ROBOT_WHEEL 59
222 #define GAME_PANEL_CONVEYOR_BELT_1 60
223 #define GAME_PANEL_CONVEYOR_BELT_2 61
224 #define GAME_PANEL_CONVEYOR_BELT_3 62
225 #define GAME_PANEL_CONVEYOR_BELT_4 63
226 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
227 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
228 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
229 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
230 #define GAME_PANEL_MAGIC_WALL 68
231 #define GAME_PANEL_MAGIC_WALL_TIME 69
232 #define GAME_PANEL_GRAVITY_STATE 70
233 #define GAME_PANEL_GRAPHIC_1 71
234 #define GAME_PANEL_GRAPHIC_2 72
235 #define GAME_PANEL_GRAPHIC_3 73
236 #define GAME_PANEL_GRAPHIC_4 74
237 #define GAME_PANEL_GRAPHIC_5 75
238 #define GAME_PANEL_GRAPHIC_6 76
239 #define GAME_PANEL_GRAPHIC_7 77
240 #define GAME_PANEL_GRAPHIC_8 78
241 #define GAME_PANEL_ELEMENT_1 79
242 #define GAME_PANEL_ELEMENT_2 80
243 #define GAME_PANEL_ELEMENT_3 81
244 #define GAME_PANEL_ELEMENT_4 82
245 #define GAME_PANEL_ELEMENT_5 83
246 #define GAME_PANEL_ELEMENT_6 84
247 #define GAME_PANEL_ELEMENT_7 85
248 #define GAME_PANEL_ELEMENT_8 86
249 #define GAME_PANEL_ELEMENT_COUNT_1 87
250 #define GAME_PANEL_ELEMENT_COUNT_2 88
251 #define GAME_PANEL_ELEMENT_COUNT_3 89
252 #define GAME_PANEL_ELEMENT_COUNT_4 90
253 #define GAME_PANEL_ELEMENT_COUNT_5 91
254 #define GAME_PANEL_ELEMENT_COUNT_6 92
255 #define GAME_PANEL_ELEMENT_COUNT_7 93
256 #define GAME_PANEL_ELEMENT_COUNT_8 94
257 #define GAME_PANEL_CE_SCORE_1 95
258 #define GAME_PANEL_CE_SCORE_2 96
259 #define GAME_PANEL_CE_SCORE_3 97
260 #define GAME_PANEL_CE_SCORE_4 98
261 #define GAME_PANEL_CE_SCORE_5 99
262 #define GAME_PANEL_CE_SCORE_6 100
263 #define GAME_PANEL_CE_SCORE_7 101
264 #define GAME_PANEL_CE_SCORE_8 102
265 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
266 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
267 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
268 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
269 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
270 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
271 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
272 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
273 #define GAME_PANEL_PLAYER_NAME 111
274 #define GAME_PANEL_LEVEL_NAME 112
275 #define GAME_PANEL_LEVEL_AUTHOR 113
276
277 #define NUM_GAME_PANEL_CONTROLS 114
278
279 struct GamePanelOrderInfo
280 {
281 int nr;
282 int sort_priority;
283 };
284
285 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
286
287 struct GamePanelControlInfo
288 {
289 int nr;
290
291 struct TextPosInfo *pos;
292 int type;
293
294 int value, last_value;
295 int frame, last_frame;
296 int gfx_frame;
297 int gfx_random;
298 };
299
300 static struct GamePanelControlInfo game_panel_controls[] =
301 {
302 {
303 GAME_PANEL_LEVEL_NUMBER,
304 &game.panel.level_number,
305 TYPE_INTEGER,
306 },
307 {
308 GAME_PANEL_GEMS,
309 &game.panel.gems,
310 TYPE_INTEGER,
311 },
312 {
313 GAME_PANEL_INVENTORY_COUNT,
314 &game.panel.inventory_count,
315 TYPE_INTEGER,
316 },
317 {
318 GAME_PANEL_INVENTORY_FIRST_1,
319 &game.panel.inventory_first[0],
320 TYPE_ELEMENT,
321 },
322 {
323 GAME_PANEL_INVENTORY_FIRST_2,
324 &game.panel.inventory_first[1],
325 TYPE_ELEMENT,
326 },
327 {
328 GAME_PANEL_INVENTORY_FIRST_3,
329 &game.panel.inventory_first[2],
330 TYPE_ELEMENT,
331 },
332 {
333 GAME_PANEL_INVENTORY_FIRST_4,
334 &game.panel.inventory_first[3],
335 TYPE_ELEMENT,
336 },
337 {
338 GAME_PANEL_INVENTORY_FIRST_5,
339 &game.panel.inventory_first[4],
340 TYPE_ELEMENT,
341 },
342 {
343 GAME_PANEL_INVENTORY_FIRST_6,
344 &game.panel.inventory_first[5],
345 TYPE_ELEMENT,
346 },
347 {
348 GAME_PANEL_INVENTORY_FIRST_7,
349 &game.panel.inventory_first[6],
350 TYPE_ELEMENT,
351 },
352 {
353 GAME_PANEL_INVENTORY_FIRST_8,
354 &game.panel.inventory_first[7],
355 TYPE_ELEMENT,
356 },
357 {
358 GAME_PANEL_INVENTORY_LAST_1,
359 &game.panel.inventory_last[0],
360 TYPE_ELEMENT,
361 },
362 {
363 GAME_PANEL_INVENTORY_LAST_2,
364 &game.panel.inventory_last[1],
365 TYPE_ELEMENT,
366 },
367 {
368 GAME_PANEL_INVENTORY_LAST_3,
369 &game.panel.inventory_last[2],
370 TYPE_ELEMENT,
371 },
372 {
373 GAME_PANEL_INVENTORY_LAST_4,
374 &game.panel.inventory_last[3],
375 TYPE_ELEMENT,
376 },
377 {
378 GAME_PANEL_INVENTORY_LAST_5,
379 &game.panel.inventory_last[4],
380 TYPE_ELEMENT,
381 },
382 {
383 GAME_PANEL_INVENTORY_LAST_6,
384 &game.panel.inventory_last[5],
385 TYPE_ELEMENT,
386 },
387 {
388 GAME_PANEL_INVENTORY_LAST_7,
389 &game.panel.inventory_last[6],
390 TYPE_ELEMENT,
391 },
392 {
393 GAME_PANEL_INVENTORY_LAST_8,
394 &game.panel.inventory_last[7],
395 TYPE_ELEMENT,
396 },
397 {
398 GAME_PANEL_KEY_1,
399 &game.panel.key[0],
400 TYPE_ELEMENT,
401 },
402 {
403 GAME_PANEL_KEY_2,
404 &game.panel.key[1],
405 TYPE_ELEMENT,
406 },
407 {
408 GAME_PANEL_KEY_3,
409 &game.panel.key[2],
410 TYPE_ELEMENT,
411 },
412 {
413 GAME_PANEL_KEY_4,
414 &game.panel.key[3],
415 TYPE_ELEMENT,
416 },
417 {
418 GAME_PANEL_KEY_5,
419 &game.panel.key[4],
420 TYPE_ELEMENT,
421 },
422 {
423 GAME_PANEL_KEY_6,
424 &game.panel.key[5],
425 TYPE_ELEMENT,
426 },
427 {
428 GAME_PANEL_KEY_7,
429 &game.panel.key[6],
430 TYPE_ELEMENT,
431 },
432 {
433 GAME_PANEL_KEY_8,
434 &game.panel.key[7],
435 TYPE_ELEMENT,
436 },
437 {
438 GAME_PANEL_KEY_WHITE,
439 &game.panel.key_white,
440 TYPE_ELEMENT,
441 },
442 {
443 GAME_PANEL_KEY_WHITE_COUNT,
444 &game.panel.key_white_count,
445 TYPE_INTEGER,
446 },
447 {
448 GAME_PANEL_SCORE,
449 &game.panel.score,
450 TYPE_INTEGER,
451 },
452 {
453 GAME_PANEL_HIGHSCORE,
454 &game.panel.highscore,
455 TYPE_INTEGER,
456 },
457 {
458 GAME_PANEL_TIME,
459 &game.panel.time,
460 TYPE_INTEGER,
461 },
462 {
463 GAME_PANEL_TIME_HH,
464 &game.panel.time_hh,
465 TYPE_INTEGER,
466 },
467 {
468 GAME_PANEL_TIME_MM,
469 &game.panel.time_mm,
470 TYPE_INTEGER,
471 },
472 {
473 GAME_PANEL_TIME_SS,
474 &game.panel.time_ss,
475 TYPE_INTEGER,
476 },
477 {
478 GAME_PANEL_FRAME,
479 &game.panel.frame,
480 TYPE_INTEGER,
481 },
482 {
483 GAME_PANEL_SHIELD_NORMAL,
484 &game.panel.shield_normal,
485 TYPE_ELEMENT,
486 },
487 {
488 GAME_PANEL_SHIELD_NORMAL_TIME,
489 &game.panel.shield_normal_time,
490 TYPE_INTEGER,
491 },
492 {
493 GAME_PANEL_SHIELD_DEADLY,
494 &game.panel.shield_deadly,
495 TYPE_ELEMENT,
496 },
497 {
498 GAME_PANEL_SHIELD_DEADLY_TIME,
499 &game.panel.shield_deadly_time,
500 TYPE_INTEGER,
501 },
502 {
503 GAME_PANEL_EXIT,
504 &game.panel.exit,
505 TYPE_ELEMENT,
506 },
507 {
508 GAME_PANEL_EMC_MAGIC_BALL,
509 &game.panel.emc_magic_ball,
510 TYPE_ELEMENT,
511 },
512 {
513 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
514 &game.panel.emc_magic_ball_switch,
515 TYPE_ELEMENT,
516 },
517 {
518 GAME_PANEL_LIGHT_SWITCH,
519 &game.panel.light_switch,
520 TYPE_ELEMENT,
521 },
522 {
523 GAME_PANEL_LIGHT_SWITCH_TIME,
524 &game.panel.light_switch_time,
525 TYPE_INTEGER,
526 },
527 {
528 GAME_PANEL_TIMEGATE_SWITCH,
529 &game.panel.timegate_switch,
530 TYPE_ELEMENT,
531 },
532 {
533 GAME_PANEL_TIMEGATE_SWITCH_TIME,
534 &game.panel.timegate_switch_time,
535 TYPE_INTEGER,
536 },
537 {
538 GAME_PANEL_SWITCHGATE_SWITCH,
539 &game.panel.switchgate_switch,
540 TYPE_ELEMENT,
541 },
542 {
543 GAME_PANEL_EMC_LENSES,
544 &game.panel.emc_lenses,
545 TYPE_ELEMENT,
546 },
547 {
548 GAME_PANEL_EMC_LENSES_TIME,
549 &game.panel.emc_lenses_time,
550 TYPE_INTEGER,
551 },
552 {
553 GAME_PANEL_EMC_MAGNIFIER,
554 &game.panel.emc_magnifier,
555 TYPE_ELEMENT,
556 },
557 {
558 GAME_PANEL_EMC_MAGNIFIER_TIME,
559 &game.panel.emc_magnifier_time,
560 TYPE_INTEGER,
561 },
562 {
563 GAME_PANEL_BALLOON_SWITCH,
564 &game.panel.balloon_switch,
565 TYPE_ELEMENT,
566 },
567 {
568 GAME_PANEL_DYNABOMB_NUMBER,
569 &game.panel.dynabomb_number,
570 TYPE_INTEGER,
571 },
572 {
573 GAME_PANEL_DYNABOMB_SIZE,
574 &game.panel.dynabomb_size,
575 TYPE_INTEGER,
576 },
577 {
578 GAME_PANEL_DYNABOMB_POWER,
579 &game.panel.dynabomb_power,
580 TYPE_ELEMENT,
581 },
582 {
583 GAME_PANEL_PENGUINS,
584 &game.panel.penguins,
585 TYPE_INTEGER,
586 },
587 {
588 GAME_PANEL_SOKOBAN_OBJECTS,
589 &game.panel.sokoban_objects,
590 TYPE_INTEGER,
591 },
592 {
593 GAME_PANEL_SOKOBAN_FIELDS,
594 &game.panel.sokoban_fields,
595 TYPE_INTEGER,
596 },
597 {
598 GAME_PANEL_ROBOT_WHEEL,
599 &game.panel.robot_wheel,
600 TYPE_ELEMENT,
601 },
602 {
603 GAME_PANEL_CONVEYOR_BELT_1,
604 &game.panel.conveyor_belt[0],
605 TYPE_ELEMENT,
606 },
607 {
608 GAME_PANEL_CONVEYOR_BELT_2,
609 &game.panel.conveyor_belt[1],
610 TYPE_ELEMENT,
611 },
612 {
613 GAME_PANEL_CONVEYOR_BELT_3,
614 &game.panel.conveyor_belt[2],
615 TYPE_ELEMENT,
616 },
617 {
618 GAME_PANEL_CONVEYOR_BELT_4,
619 &game.panel.conveyor_belt[3],
620 TYPE_ELEMENT,
621 },
622 {
623 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
624 &game.panel.conveyor_belt_switch[0],
625 TYPE_ELEMENT,
626 },
627 {
628 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
629 &game.panel.conveyor_belt_switch[1],
630 TYPE_ELEMENT,
631 },
632 {
633 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
634 &game.panel.conveyor_belt_switch[2],
635 TYPE_ELEMENT,
636 },
637 {
638 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
639 &game.panel.conveyor_belt_switch[3],
640 TYPE_ELEMENT,
641 },
642 {
643 GAME_PANEL_MAGIC_WALL,
644 &game.panel.magic_wall,
645 TYPE_ELEMENT,
646 },
647 {
648 GAME_PANEL_MAGIC_WALL_TIME,
649 &game.panel.magic_wall_time,
650 TYPE_INTEGER,
651 },
652 {
653 GAME_PANEL_GRAVITY_STATE,
654 &game.panel.gravity_state,
655 TYPE_STRING,
656 },
657 {
658 GAME_PANEL_GRAPHIC_1,
659 &game.panel.graphic[0],
660 TYPE_ELEMENT,
661 },
662 {
663 GAME_PANEL_GRAPHIC_2,
664 &game.panel.graphic[1],
665 TYPE_ELEMENT,
666 },
667 {
668 GAME_PANEL_GRAPHIC_3,
669 &game.panel.graphic[2],
670 TYPE_ELEMENT,
671 },
672 {
673 GAME_PANEL_GRAPHIC_4,
674 &game.panel.graphic[3],
675 TYPE_ELEMENT,
676 },
677 {
678 GAME_PANEL_GRAPHIC_5,
679 &game.panel.graphic[4],
680 TYPE_ELEMENT,
681 },
682 {
683 GAME_PANEL_GRAPHIC_6,
684 &game.panel.graphic[5],
685 TYPE_ELEMENT,
686 },
687 {
688 GAME_PANEL_GRAPHIC_7,
689 &game.panel.graphic[6],
690 TYPE_ELEMENT,
691 },
692 {
693 GAME_PANEL_GRAPHIC_8,
694 &game.panel.graphic[7],
695 TYPE_ELEMENT,
696 },
697 {
698 GAME_PANEL_ELEMENT_1,
699 &game.panel.element[0],
700 TYPE_ELEMENT,
701 },
702 {
703 GAME_PANEL_ELEMENT_2,
704 &game.panel.element[1],
705 TYPE_ELEMENT,
706 },
707 {
708 GAME_PANEL_ELEMENT_3,
709 &game.panel.element[2],
710 TYPE_ELEMENT,
711 },
712 {
713 GAME_PANEL_ELEMENT_4,
714 &game.panel.element[3],
715 TYPE_ELEMENT,
716 },
717 {
718 GAME_PANEL_ELEMENT_5,
719 &game.panel.element[4],
720 TYPE_ELEMENT,
721 },
722 {
723 GAME_PANEL_ELEMENT_6,
724 &game.panel.element[5],
725 TYPE_ELEMENT,
726 },
727 {
728 GAME_PANEL_ELEMENT_7,
729 &game.panel.element[6],
730 TYPE_ELEMENT,
731 },
732 {
733 GAME_PANEL_ELEMENT_8,
734 &game.panel.element[7],
735 TYPE_ELEMENT,
736 },
737 {
738 GAME_PANEL_ELEMENT_COUNT_1,
739 &game.panel.element_count[0],
740 TYPE_INTEGER,
741 },
742 {
743 GAME_PANEL_ELEMENT_COUNT_2,
744 &game.panel.element_count[1],
745 TYPE_INTEGER,
746 },
747 {
748 GAME_PANEL_ELEMENT_COUNT_3,
749 &game.panel.element_count[2],
750 TYPE_INTEGER,
751 },
752 {
753 GAME_PANEL_ELEMENT_COUNT_4,
754 &game.panel.element_count[3],
755 TYPE_INTEGER,
756 },
757 {
758 GAME_PANEL_ELEMENT_COUNT_5,
759 &game.panel.element_count[4],
760 TYPE_INTEGER,
761 },
762 {
763 GAME_PANEL_ELEMENT_COUNT_6,
764 &game.panel.element_count[5],
765 TYPE_INTEGER,
766 },
767 {
768 GAME_PANEL_ELEMENT_COUNT_7,
769 &game.panel.element_count[6],
770 TYPE_INTEGER,
771 },
772 {
773 GAME_PANEL_ELEMENT_COUNT_8,
774 &game.panel.element_count[7],
775 TYPE_INTEGER,
776 },
777 {
778 GAME_PANEL_CE_SCORE_1,
779 &game.panel.ce_score[0],
780 TYPE_INTEGER,
781 },
782 {
783 GAME_PANEL_CE_SCORE_2,
784 &game.panel.ce_score[1],
785 TYPE_INTEGER,
786 },
787 {
788 GAME_PANEL_CE_SCORE_3,
789 &game.panel.ce_score[2],
790 TYPE_INTEGER,
791 },
792 {
793 GAME_PANEL_CE_SCORE_4,
794 &game.panel.ce_score[3],
795 TYPE_INTEGER,
796 },
797 {
798 GAME_PANEL_CE_SCORE_5,
799 &game.panel.ce_score[4],
800 TYPE_INTEGER,
801 },
802 {
803 GAME_PANEL_CE_SCORE_6,
804 &game.panel.ce_score[5],
805 TYPE_INTEGER,
806 },
807 {
808 GAME_PANEL_CE_SCORE_7,
809 &game.panel.ce_score[6],
810 TYPE_INTEGER,
811 },
812 {
813 GAME_PANEL_CE_SCORE_8,
814 &game.panel.ce_score[7],
815 TYPE_INTEGER,
816 },
817 {
818 GAME_PANEL_CE_SCORE_1_ELEMENT,
819 &game.panel.ce_score_element[0],
820 TYPE_ELEMENT,
821 },
822 {
823 GAME_PANEL_CE_SCORE_2_ELEMENT,
824 &game.panel.ce_score_element[1],
825 TYPE_ELEMENT,
826 },
827 {
828 GAME_PANEL_CE_SCORE_3_ELEMENT,
829 &game.panel.ce_score_element[2],
830 TYPE_ELEMENT,
831 },
832 {
833 GAME_PANEL_CE_SCORE_4_ELEMENT,
834 &game.panel.ce_score_element[3],
835 TYPE_ELEMENT,
836 },
837 {
838 GAME_PANEL_CE_SCORE_5_ELEMENT,
839 &game.panel.ce_score_element[4],
840 TYPE_ELEMENT,
841 },
842 {
843 GAME_PANEL_CE_SCORE_6_ELEMENT,
844 &game.panel.ce_score_element[5],
845 TYPE_ELEMENT,
846 },
847 {
848 GAME_PANEL_CE_SCORE_7_ELEMENT,
849 &game.panel.ce_score_element[6],
850 TYPE_ELEMENT,
851 },
852 {
853 GAME_PANEL_CE_SCORE_8_ELEMENT,
854 &game.panel.ce_score_element[7],
855 TYPE_ELEMENT,
856 },
857 {
858 GAME_PANEL_PLAYER_NAME,
859 &game.panel.player_name,
860 TYPE_STRING,
861 },
862 {
863 GAME_PANEL_LEVEL_NAME,
864 &game.panel.level_name,
865 TYPE_STRING,
866 },
867 {
868 GAME_PANEL_LEVEL_AUTHOR,
869 &game.panel.level_author,
870 TYPE_STRING,
871 },
872
873 {
874 -1,
875 NULL,
876 -1,
877 }
878 };
879 #endif
880
881
882 /* values for delayed check of falling and moving elements and for collision */
883 #define CHECK_DELAY_MOVING 3
884 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
885 #define CHECK_DELAY_COLLISION 2
886 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
887
888 /* values for initial player move delay (initial delay counter value) */
889 #define INITIAL_MOVE_DELAY_OFF -1
890 #define INITIAL_MOVE_DELAY_ON 0
891
892 /* values for player movement speed (which is in fact a delay value) */
893 #define MOVE_DELAY_MIN_SPEED 32
894 #define MOVE_DELAY_NORMAL_SPEED 8
895 #define MOVE_DELAY_HIGH_SPEED 4
896 #define MOVE_DELAY_MAX_SPEED 1
897
898 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
899 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
900
901 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
902 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
903
904 /* values for other actions */
905 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
906 #define MOVE_STEPSIZE_MIN (1)
907 #define MOVE_STEPSIZE_MAX (TILEX)
908
909 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
910 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
911
912 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
913
914 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
915 RND(element_info[e].push_delay_random))
916 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
917 RND(element_info[e].drop_delay_random))
918 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
919 RND(element_info[e].move_delay_random))
920 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
921 (element_info[e].move_delay_random))
922 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
923 RND(element_info[e].ce_value_random_initial))
924 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
925 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
926 RND((c)->delay_random * (c)->delay_frames))
927 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
928 RND((c)->delay_random))
929
930
931 #define GET_VALID_RUNTIME_ELEMENT(e) \
932 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
933
934 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
935 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
936 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
937 (be) + (e) - EL_SELF)
938
939 #define GET_PLAYER_FROM_BITS(p) \
940 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
941
942 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
943 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
944 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
945 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
946 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
947 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
948 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
949 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
950 RESOLVED_REFERENCE_ELEMENT(be, e) : \
951 (e))
952
953 #define CAN_GROW_INTO(e) \
954 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
955
956 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
957 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
958 (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
961 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
962 (CAN_MOVE_INTO_ACID(e) && \
963 Feld[x][y] == EL_ACID) || \
964 (condition)))
965
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
967 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
968 (CAN_MOVE_INTO_ACID(e) && \
969 Feld[x][y] == EL_ACID) || \
970 (condition)))
971
972 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
973 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
974 (condition) || \
975 (CAN_MOVE_INTO_ACID(e) && \
976 Feld[x][y] == EL_ACID) || \
977 (DONT_COLLIDE_WITH(e) && \
978 IS_PLAYER(x, y) && \
979 !PLAYER_ENEMY_PROTECTED(x, y))))
980
981 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
983
984 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
985 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
986
987 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
988 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
989
990 #define ANDROID_CAN_CLONE_FIELD(x, y) \
991 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
992 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
993
994 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
996
997 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
998 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
999
1000 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
1001 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
1002
1003 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
1004 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1005
1006 #define PIG_CAN_ENTER_FIELD(e, x, y) \
1007 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1008
1009 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
1010 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1011 Feld[x][y] == EL_EM_EXIT_OPEN || \
1012 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1013 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1014 IS_FOOD_PENGUIN(Feld[x][y])))
1015 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
1016 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1017
1018 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
1019 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1020
1021 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
1022 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1023
1024 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
1025 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
1026 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1027
1028 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
1029
1030 #define CE_ENTER_FIELD_COND(e, x, y) \
1031 (!IS_PLAYER(x, y) && \
1032 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1033
1034 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1035 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1036
1037 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1038 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1039
1040 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1041 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1042 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1043 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1044
1045 /* game button identifiers */
1046 #define GAME_CTRL_ID_STOP 0
1047 #define GAME_CTRL_ID_PAUSE 1
1048 #define GAME_CTRL_ID_PLAY 2
1049 #define SOUND_CTRL_ID_MUSIC 3
1050 #define SOUND_CTRL_ID_LOOPS 4
1051 #define SOUND_CTRL_ID_SIMPLE 5
1052
1053 #define NUM_GAME_BUTTONS 6
1054
1055
1056 /* forward declaration for internal use */
1057
1058 static void CreateField(int, int, int);
1059
1060 static void ResetGfxAnimation(int, int);
1061
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1064
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1069
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1074
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1081
1082 static void TestIfPlayerTouchesCustomElement(int, int);
1083 static void TestIfElementTouchesCustomElement(int, int);
1084 static void TestIfElementHitsCustomElement(int, int, int);
1085 #if 0
1086 static void TestIfElementSmashesCustomElement(int, int, int);
1087 #endif
1088
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1092
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1094 #define CheckTriggeredElementChange(x, y, e, ev) \
1095 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1097 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1099 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1101 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1102
1103 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1104 #define CheckElementChange(x, y, e, te, ev) \
1105 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1106 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1107 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1108 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1109 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1110
1111 static void PlayLevelSound(int, int, int);
1112 static void PlayLevelSoundNearest(int, int, int);
1113 static void PlayLevelSoundAction(int, int, int);
1114 static void PlayLevelSoundElementAction(int, int, int, int);
1115 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1116 static void PlayLevelSoundActionIfLoop(int, int, int);
1117 static void StopLevelSoundActionIfLoop(int, int, int);
1118 static void PlayLevelMusic();
1119
1120 static void MapGameButtons();
1121 static void HandleGameButtons(struct GadgetInfo *);
1122
1123 int AmoebeNachbarNr(int, int);
1124 void AmoebeUmwandeln(int, int);
1125 void ContinueMoving(int, int);
1126 void Bang(int, int);
1127 void InitMovDir(int, int);
1128 void InitAmoebaNr(int, int);
1129 int NewHiScore(void);
1130
1131 void TestIfGoodThingHitsBadThing(int, int, int);
1132 void TestIfBadThingHitsGoodThing(int, int, int);
1133 void TestIfPlayerTouchesBadThing(int, int);
1134 void TestIfPlayerRunsIntoBadThing(int, int, int);
1135 void TestIfBadThingTouchesPlayer(int, int);
1136 void TestIfBadThingRunsIntoPlayer(int, int, int);
1137 void TestIfFriendTouchesBadThing(int, int);
1138 void TestIfBadThingTouchesFriend(int, int);
1139 void TestIfBadThingTouchesOtherBadThing(int, int);
1140 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1141
1142 void KillPlayer(struct PlayerInfo *);
1143 void BuryPlayer(struct PlayerInfo *);
1144 void RemovePlayer(struct PlayerInfo *);
1145
1146 static int getInvisibleActiveFromInvisibleElement(int);
1147 static int getInvisibleFromInvisibleActiveElement(int);
1148
1149 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1150
1151 /* for detection of endless loops, caused by custom element programming */
1152 /* (using maximal playfield width x 10 is just a rough approximation) */
1153 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1154
1155 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1156 { \
1157 if (recursion_loop_detected) \
1158 return (rc); \
1159 \
1160 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1161 { \
1162 recursion_loop_detected = TRUE; \
1163 recursion_loop_element = (e); \
1164 } \
1165 \
1166 recursion_loop_depth++; \
1167 }
1168
1169 #define RECURSION_LOOP_DETECTION_END() \
1170 { \
1171 recursion_loop_depth--; \
1172 }
1173
1174 static int recursion_loop_depth;
1175 static boolean recursion_loop_detected;
1176 static boolean recursion_loop_element;
1177
1178 static int map_player_action[MAX_PLAYERS];
1179
1180
1181 /* ------------------------------------------------------------------------- */
1182 /* definition of elements that automatically change to other elements after */
1183 /* a specified time, eventually calling a function when changing */
1184 /* ------------------------------------------------------------------------- */
1185
1186 /* forward declaration for changer functions */
1187 static void InitBuggyBase(int, int);
1188 static void WarnBuggyBase(int, int);
1189
1190 static void InitTrap(int, int);
1191 static void ActivateTrap(int, int);
1192 static void ChangeActiveTrap(int, int);
1193
1194 static void InitRobotWheel(int, int);
1195 static void RunRobotWheel(int, int);
1196 static void StopRobotWheel(int, int);
1197
1198 static void InitTimegateWheel(int, int);
1199 static void RunTimegateWheel(int, int);
1200
1201 static void InitMagicBallDelay(int, int);
1202 static void ActivateMagicBall(int, int);
1203
1204 struct ChangingElementInfo
1205 {
1206 int element;
1207 int target_element;
1208 int change_delay;
1209 void (*pre_change_function)(int x, int y);
1210 void (*change_function)(int x, int y);
1211 void (*post_change_function)(int x, int y);
1212 };
1213
1214 static struct ChangingElementInfo change_delay_list[] =
1215 {
1216 {
1217 EL_NUT_BREAKING,
1218 EL_EMERALD,
1219 6,
1220 NULL,
1221 NULL,
1222 NULL
1223 },
1224 {
1225 EL_PEARL_BREAKING,
1226 EL_EMPTY,
1227 8,
1228 NULL,
1229 NULL,
1230 NULL
1231 },
1232 {
1233 EL_EXIT_OPENING,
1234 EL_EXIT_OPEN,
1235 29,
1236 NULL,
1237 NULL,
1238 NULL
1239 },
1240 {
1241 EL_EXIT_CLOSING,
1242 EL_EXIT_CLOSED,
1243 29,
1244 NULL,
1245 NULL,
1246 NULL
1247 },
1248 {
1249 EL_STEEL_EXIT_OPENING,
1250 EL_STEEL_EXIT_OPEN,
1251 29,
1252 NULL,
1253 NULL,
1254 NULL
1255 },
1256 {
1257 EL_STEEL_EXIT_CLOSING,
1258 EL_STEEL_EXIT_CLOSED,
1259 29,
1260 NULL,
1261 NULL,
1262 NULL
1263 },
1264 {
1265 EL_EM_EXIT_OPENING,
1266 EL_EM_EXIT_OPEN,
1267 29,
1268 NULL,
1269 NULL,
1270 NULL
1271 },
1272 {
1273 EL_EM_EXIT_CLOSING,
1274 #if 1
1275 EL_EMPTY,
1276 #else
1277 EL_EM_EXIT_CLOSED,
1278 #endif
1279 29,
1280 NULL,
1281 NULL,
1282 NULL
1283 },
1284 {
1285 EL_EM_STEEL_EXIT_OPENING,
1286 EL_EM_STEEL_EXIT_OPEN,
1287 29,
1288 NULL,
1289 NULL,
1290 NULL
1291 },
1292 {
1293 EL_EM_STEEL_EXIT_CLOSING,
1294 #if 1
1295 EL_STEELWALL,
1296 #else
1297 EL_EM_STEEL_EXIT_CLOSED,
1298 #endif
1299 29,
1300 NULL,
1301 NULL,
1302 NULL
1303 },
1304 {
1305 EL_SP_EXIT_OPENING,
1306 EL_SP_EXIT_OPEN,
1307 29,
1308 NULL,
1309 NULL,
1310 NULL
1311 },
1312 {
1313 EL_SP_EXIT_CLOSING,
1314 EL_SP_EXIT_CLOSED,
1315 29,
1316 NULL,
1317 NULL,
1318 NULL
1319 },
1320 {
1321 EL_SWITCHGATE_OPENING,
1322 EL_SWITCHGATE_OPEN,
1323 29,
1324 NULL,
1325 NULL,
1326 NULL
1327 },
1328 {
1329 EL_SWITCHGATE_CLOSING,
1330 EL_SWITCHGATE_CLOSED,
1331 29,
1332 NULL,
1333 NULL,
1334 NULL
1335 },
1336 {
1337 EL_TIMEGATE_OPENING,
1338 EL_TIMEGATE_OPEN,
1339 29,
1340 NULL,
1341 NULL,
1342 NULL
1343 },
1344 {
1345 EL_TIMEGATE_CLOSING,
1346 EL_TIMEGATE_CLOSED,
1347 29,
1348 NULL,
1349 NULL,
1350 NULL
1351 },
1352
1353 {
1354 EL_ACID_SPLASH_LEFT,
1355 EL_EMPTY,
1356 8,
1357 NULL,
1358 NULL,
1359 NULL
1360 },
1361 {
1362 EL_ACID_SPLASH_RIGHT,
1363 EL_EMPTY,
1364 8,
1365 NULL,
1366 NULL,
1367 NULL
1368 },
1369 {
1370 EL_SP_BUGGY_BASE,
1371 EL_SP_BUGGY_BASE_ACTIVATING,
1372 0,
1373 InitBuggyBase,
1374 NULL,
1375 NULL
1376 },
1377 {
1378 EL_SP_BUGGY_BASE_ACTIVATING,
1379 EL_SP_BUGGY_BASE_ACTIVE,
1380 0,
1381 InitBuggyBase,
1382 NULL,
1383 NULL
1384 },
1385 {
1386 EL_SP_BUGGY_BASE_ACTIVE,
1387 EL_SP_BUGGY_BASE,
1388 0,
1389 InitBuggyBase,
1390 WarnBuggyBase,
1391 NULL
1392 },
1393 {
1394 EL_TRAP,
1395 EL_TRAP_ACTIVE,
1396 0,
1397 InitTrap,
1398 NULL,
1399 ActivateTrap
1400 },
1401 {
1402 EL_TRAP_ACTIVE,
1403 EL_TRAP,
1404 31,
1405 NULL,
1406 ChangeActiveTrap,
1407 NULL
1408 },
1409 {
1410 EL_ROBOT_WHEEL_ACTIVE,
1411 EL_ROBOT_WHEEL,
1412 0,
1413 InitRobotWheel,
1414 RunRobotWheel,
1415 StopRobotWheel
1416 },
1417 {
1418 EL_TIMEGATE_SWITCH_ACTIVE,
1419 EL_TIMEGATE_SWITCH,
1420 0,
1421 InitTimegateWheel,
1422 RunTimegateWheel,
1423 NULL
1424 },
1425 {
1426 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1427 EL_DC_TIMEGATE_SWITCH,
1428 0,
1429 InitTimegateWheel,
1430 RunTimegateWheel,
1431 NULL
1432 },
1433 {
1434 EL_EMC_MAGIC_BALL_ACTIVE,
1435 EL_EMC_MAGIC_BALL_ACTIVE,
1436 0,
1437 InitMagicBallDelay,
1438 NULL,
1439 ActivateMagicBall
1440 },
1441 {
1442 EL_EMC_SPRING_BUMPER_ACTIVE,
1443 EL_EMC_SPRING_BUMPER,
1444 8,
1445 NULL,
1446 NULL,
1447 NULL
1448 },
1449 {
1450 EL_DIAGONAL_SHRINKING,
1451 EL_UNDEFINED,
1452 0,
1453 NULL,
1454 NULL,
1455 NULL
1456 },
1457 {
1458 EL_DIAGONAL_GROWING,
1459 EL_UNDEFINED,
1460 0,
1461 NULL,
1462 NULL,
1463 NULL,
1464 },
1465
1466 {
1467 EL_UNDEFINED,
1468 EL_UNDEFINED,
1469 -1,
1470 NULL,
1471 NULL,
1472 NULL
1473 }
1474 };
1475
1476 struct
1477 {
1478 int element;
1479 int push_delay_fixed, push_delay_random;
1480 }
1481 push_delay_list[] =
1482 {
1483 { EL_SPRING, 0, 0 },
1484 { EL_BALLOON, 0, 0 },
1485
1486 { EL_SOKOBAN_OBJECT, 2, 0 },
1487 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1488 { EL_SATELLITE, 2, 0 },
1489 { EL_SP_DISK_YELLOW, 2, 0 },
1490
1491 { EL_UNDEFINED, 0, 0 },
1492 };
1493
1494 struct
1495 {
1496 int element;
1497 int move_stepsize;
1498 }
1499 move_stepsize_list[] =
1500 {
1501 { EL_AMOEBA_DROP, 2 },
1502 { EL_AMOEBA_DROPPING, 2 },
1503 { EL_QUICKSAND_FILLING, 1 },
1504 { EL_QUICKSAND_EMPTYING, 1 },
1505 { EL_QUICKSAND_FAST_FILLING, 2 },
1506 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1507 { EL_MAGIC_WALL_FILLING, 2 },
1508 { EL_MAGIC_WALL_EMPTYING, 2 },
1509 { EL_BD_MAGIC_WALL_FILLING, 2 },
1510 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1511 { EL_DC_MAGIC_WALL_FILLING, 2 },
1512 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1513
1514 { EL_UNDEFINED, 0 },
1515 };
1516
1517 struct
1518 {
1519 int element;
1520 int count;
1521 }
1522 collect_count_list[] =
1523 {
1524 { EL_EMERALD, 1 },
1525 { EL_BD_DIAMOND, 1 },
1526 { EL_EMERALD_YELLOW, 1 },
1527 { EL_EMERALD_RED, 1 },
1528 { EL_EMERALD_PURPLE, 1 },
1529 { EL_DIAMOND, 3 },
1530 { EL_SP_INFOTRON, 1 },
1531 { EL_PEARL, 5 },
1532 { EL_CRYSTAL, 8 },
1533
1534 { EL_UNDEFINED, 0 },
1535 };
1536
1537 struct
1538 {
1539 int element;
1540 int direction;
1541 }
1542 access_direction_list[] =
1543 {
1544 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1545 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1546 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1547 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1548 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1549 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1550 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1551 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1552 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1553 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1554 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1555
1556 { EL_SP_PORT_LEFT, MV_RIGHT },
1557 { EL_SP_PORT_RIGHT, MV_LEFT },
1558 { EL_SP_PORT_UP, MV_DOWN },
1559 { EL_SP_PORT_DOWN, MV_UP },
1560 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1561 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1562 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1563 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1564 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1565 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1566 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1567 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1568 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1569 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1570 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1571 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1572 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1573 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1574 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1575
1576 { EL_UNDEFINED, MV_NONE }
1577 };
1578
1579 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1580
1581 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1582 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1583 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1584 IS_JUST_CHANGING(x, y))
1585
1586 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1587
1588 /* static variables for playfield scan mode (scanning forward or backward) */
1589 static int playfield_scan_start_x = 0;
1590 static int playfield_scan_start_y = 0;
1591 static int playfield_scan_delta_x = 1;
1592 static int playfield_scan_delta_y = 1;
1593
1594 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1595 (y) >= 0 && (y) <= lev_fieldy - 1; \
1596 (y) += playfield_scan_delta_y) \
1597 for ((x) = playfield_scan_start_x; \
1598 (x) >= 0 && (x) <= lev_fieldx - 1; \
1599 (x) += playfield_scan_delta_x)
1600
1601 #ifdef DEBUG
DEBUG_SetMaximumDynamite()1602 void DEBUG_SetMaximumDynamite()
1603 {
1604 int i;
1605
1606 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1607 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1608 local_player->inventory_element[local_player->inventory_size++] =
1609 EL_DYNAMITE;
1610 }
1611 #endif
1612
InitPlayfieldScanModeVars()1613 static void InitPlayfieldScanModeVars()
1614 {
1615 if (game.use_reverse_scan_direction)
1616 {
1617 playfield_scan_start_x = lev_fieldx - 1;
1618 playfield_scan_start_y = lev_fieldy - 1;
1619
1620 playfield_scan_delta_x = -1;
1621 playfield_scan_delta_y = -1;
1622 }
1623 else
1624 {
1625 playfield_scan_start_x = 0;
1626 playfield_scan_start_y = 0;
1627
1628 playfield_scan_delta_x = 1;
1629 playfield_scan_delta_y = 1;
1630 }
1631 }
1632
InitPlayfieldScanMode(int mode)1633 static void InitPlayfieldScanMode(int mode)
1634 {
1635 game.use_reverse_scan_direction =
1636 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1637
1638 InitPlayfieldScanModeVars();
1639 }
1640
get_move_delay_from_stepsize(int move_stepsize)1641 static int get_move_delay_from_stepsize(int move_stepsize)
1642 {
1643 move_stepsize =
1644 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1645
1646 /* make sure that stepsize value is always a power of 2 */
1647 move_stepsize = (1 << log_2(move_stepsize));
1648
1649 return TILEX / move_stepsize;
1650 }
1651
SetPlayerMoveSpeed(struct PlayerInfo * player,int move_stepsize,boolean init_game)1652 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1653 boolean init_game)
1654 {
1655 int player_nr = player->index_nr;
1656 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1657 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1658
1659 /* do no immediately change move delay -- the player might just be moving */
1660 player->move_delay_value_next = move_delay;
1661
1662 /* information if player can move must be set separately */
1663 player->cannot_move = cannot_move;
1664
1665 if (init_game)
1666 {
1667 player->move_delay = game.initial_move_delay[player_nr];
1668 player->move_delay_value = game.initial_move_delay_value[player_nr];
1669
1670 player->move_delay_value_next = -1;
1671
1672 player->move_delay_reset_counter = 0;
1673 }
1674 }
1675
GetPlayerConfig()1676 void GetPlayerConfig()
1677 {
1678 GameFrameDelay = setup.game_frame_delay;
1679
1680 if (!audio.sound_available)
1681 setup.sound_simple = FALSE;
1682
1683 if (!audio.loops_available)
1684 setup.sound_loops = FALSE;
1685
1686 if (!audio.music_available)
1687 setup.sound_music = FALSE;
1688
1689 if (!video.fullscreen_available)
1690 setup.fullscreen = FALSE;
1691
1692 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1693
1694 SetAudioMode(setup.sound);
1695 InitJoysticks();
1696 }
1697
GetElementFromGroupElement(int element)1698 int GetElementFromGroupElement(int element)
1699 {
1700 if (IS_GROUP_ELEMENT(element))
1701 {
1702 struct ElementGroupInfo *group = element_info[element].group;
1703 int last_anim_random_frame = gfx.anim_random_frame;
1704 int element_pos;
1705
1706 if (group->choice_mode == ANIM_RANDOM)
1707 gfx.anim_random_frame = RND(group->num_elements_resolved);
1708
1709 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1710 group->choice_mode, 0,
1711 group->choice_pos);
1712
1713 if (group->choice_mode == ANIM_RANDOM)
1714 gfx.anim_random_frame = last_anim_random_frame;
1715
1716 group->choice_pos++;
1717
1718 element = group->element_resolved[element_pos];
1719 }
1720
1721 return element;
1722 }
1723
InitPlayerField(int x,int y,int element,boolean init_game)1724 static void InitPlayerField(int x, int y, int element, boolean init_game)
1725 {
1726 if (element == EL_SP_MURPHY)
1727 {
1728 if (init_game)
1729 {
1730 if (stored_player[0].present)
1731 {
1732 Feld[x][y] = EL_SP_MURPHY_CLONE;
1733
1734 return;
1735 }
1736 else
1737 {
1738 stored_player[0].initial_element = element;
1739 stored_player[0].use_murphy = TRUE;
1740
1741 if (!level.use_artwork_element[0])
1742 stored_player[0].artwork_element = EL_SP_MURPHY;
1743 }
1744
1745 Feld[x][y] = EL_PLAYER_1;
1746 }
1747 }
1748
1749 if (init_game)
1750 {
1751 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1752 int jx = player->jx, jy = player->jy;
1753
1754 player->present = TRUE;
1755
1756 player->block_last_field = (element == EL_SP_MURPHY ?
1757 level.sp_block_last_field :
1758 level.block_last_field);
1759
1760 /* ---------- initialize player's last field block delay --------------- */
1761
1762 /* always start with reliable default value (no adjustment needed) */
1763 player->block_delay_adjustment = 0;
1764
1765 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1766 if (player->block_last_field && element == EL_SP_MURPHY)
1767 player->block_delay_adjustment = 1;
1768
1769 /* special case 2: in game engines before 3.1.1, blocking was different */
1770 if (game.use_block_last_field_bug)
1771 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1772
1773 if (!options.network || player->connected)
1774 {
1775 player->active = TRUE;
1776
1777 /* remove potentially duplicate players */
1778 if (StorePlayer[jx][jy] == Feld[x][y])
1779 StorePlayer[jx][jy] = 0;
1780
1781 StorePlayer[x][y] = Feld[x][y];
1782
1783 if (options.debug)
1784 {
1785 printf("Player %d activated.\n", player->element_nr);
1786 printf("[Local player is %d and currently %s.]\n",
1787 local_player->element_nr,
1788 local_player->active ? "active" : "not active");
1789 }
1790 }
1791
1792 Feld[x][y] = EL_EMPTY;
1793
1794 player->jx = player->last_jx = x;
1795 player->jy = player->last_jy = y;
1796 }
1797
1798 #if USE_PLAYER_REANIMATION
1799 if (!init_game)
1800 {
1801 int player_nr = GET_PLAYER_NR(element);
1802 struct PlayerInfo *player = &stored_player[player_nr];
1803
1804 if (player->active && player->killed)
1805 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1806 }
1807 #endif
1808 }
1809
InitField(int x,int y,boolean init_game)1810 static void InitField(int x, int y, boolean init_game)
1811 {
1812 int element = Feld[x][y];
1813
1814 switch (element)
1815 {
1816 case EL_SP_MURPHY:
1817 case EL_PLAYER_1:
1818 case EL_PLAYER_2:
1819 case EL_PLAYER_3:
1820 case EL_PLAYER_4:
1821 InitPlayerField(x, y, element, init_game);
1822 break;
1823
1824 case EL_SOKOBAN_FIELD_PLAYER:
1825 element = Feld[x][y] = EL_PLAYER_1;
1826 InitField(x, y, init_game);
1827
1828 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1829 InitField(x, y, init_game);
1830 break;
1831
1832 case EL_SOKOBAN_FIELD_EMPTY:
1833 local_player->sokobanfields_still_needed++;
1834 break;
1835
1836 case EL_STONEBLOCK:
1837 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1838 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1839 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1840 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1841 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1844 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1845 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847 break;
1848
1849 case EL_BUG:
1850 case EL_BUG_RIGHT:
1851 case EL_BUG_UP:
1852 case EL_BUG_LEFT:
1853 case EL_BUG_DOWN:
1854 case EL_SPACESHIP:
1855 case EL_SPACESHIP_RIGHT:
1856 case EL_SPACESHIP_UP:
1857 case EL_SPACESHIP_LEFT:
1858 case EL_SPACESHIP_DOWN:
1859 case EL_BD_BUTTERFLY:
1860 case EL_BD_BUTTERFLY_RIGHT:
1861 case EL_BD_BUTTERFLY_UP:
1862 case EL_BD_BUTTERFLY_LEFT:
1863 case EL_BD_BUTTERFLY_DOWN:
1864 case EL_BD_FIREFLY:
1865 case EL_BD_FIREFLY_RIGHT:
1866 case EL_BD_FIREFLY_UP:
1867 case EL_BD_FIREFLY_LEFT:
1868 case EL_BD_FIREFLY_DOWN:
1869 case EL_PACMAN_RIGHT:
1870 case EL_PACMAN_UP:
1871 case EL_PACMAN_LEFT:
1872 case EL_PACMAN_DOWN:
1873 case EL_YAMYAM:
1874 case EL_YAMYAM_LEFT:
1875 case EL_YAMYAM_RIGHT:
1876 case EL_YAMYAM_UP:
1877 case EL_YAMYAM_DOWN:
1878 case EL_DARK_YAMYAM:
1879 case EL_ROBOT:
1880 case EL_PACMAN:
1881 case EL_SP_SNIKSNAK:
1882 case EL_SP_ELECTRON:
1883 case EL_MOLE:
1884 case EL_MOLE_LEFT:
1885 case EL_MOLE_RIGHT:
1886 case EL_MOLE_UP:
1887 case EL_MOLE_DOWN:
1888 InitMovDir(x, y);
1889 break;
1890
1891 case EL_AMOEBA_FULL:
1892 case EL_BD_AMOEBA:
1893 InitAmoebaNr(x, y);
1894 break;
1895
1896 case EL_AMOEBA_DROP:
1897 if (y == lev_fieldy - 1)
1898 {
1899 Feld[x][y] = EL_AMOEBA_GROWING;
1900 Store[x][y] = EL_AMOEBA_WET;
1901 }
1902 break;
1903
1904 case EL_DYNAMITE_ACTIVE:
1905 case EL_SP_DISK_RED_ACTIVE:
1906 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1907 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1908 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1909 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1910 MovDelay[x][y] = 96;
1911 break;
1912
1913 case EL_EM_DYNAMITE_ACTIVE:
1914 MovDelay[x][y] = 32;
1915 break;
1916
1917 case EL_LAMP:
1918 local_player->lights_still_needed++;
1919 break;
1920
1921 case EL_PENGUIN:
1922 local_player->friends_still_needed++;
1923 break;
1924
1925 case EL_PIG:
1926 case EL_DRAGON:
1927 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1928 break;
1929
1930 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1931 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1932 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1933 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1934 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1935 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1936 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1937 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1938 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1939 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1940 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1941 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1942 if (init_game)
1943 {
1944 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1945 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1946 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1947
1948 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1949 {
1950 game.belt_dir[belt_nr] = belt_dir;
1951 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1952 }
1953 else /* more than one switch -- set it like the first switch */
1954 {
1955 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1956 }
1957 }
1958 break;
1959
1960 #if !USE_BOTH_SWITCHGATE_SWITCHES
1961 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1962 if (init_game)
1963 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1964 break;
1965
1966 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1967 if (init_game)
1968 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1969 break;
1970 #endif
1971
1972 case EL_LIGHT_SWITCH_ACTIVE:
1973 if (init_game)
1974 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1975 break;
1976
1977 case EL_INVISIBLE_STEELWALL:
1978 case EL_INVISIBLE_WALL:
1979 case EL_INVISIBLE_SAND:
1980 if (game.light_time_left > 0 ||
1981 game.lenses_time_left > 0)
1982 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1983 break;
1984
1985 case EL_EMC_MAGIC_BALL:
1986 if (game.ball_state)
1987 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1988 break;
1989
1990 case EL_EMC_MAGIC_BALL_SWITCH:
1991 if (game.ball_state)
1992 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1993 break;
1994
1995 case EL_TRIGGER_PLAYER:
1996 case EL_TRIGGER_ELEMENT:
1997 case EL_TRIGGER_CE_VALUE:
1998 case EL_TRIGGER_CE_SCORE:
1999 case EL_SELF:
2000 case EL_ANY_ELEMENT:
2001 case EL_CURRENT_CE_VALUE:
2002 case EL_CURRENT_CE_SCORE:
2003 case EL_PREV_CE_1:
2004 case EL_PREV_CE_2:
2005 case EL_PREV_CE_3:
2006 case EL_PREV_CE_4:
2007 case EL_PREV_CE_5:
2008 case EL_PREV_CE_6:
2009 case EL_PREV_CE_7:
2010 case EL_PREV_CE_8:
2011 case EL_NEXT_CE_1:
2012 case EL_NEXT_CE_2:
2013 case EL_NEXT_CE_3:
2014 case EL_NEXT_CE_4:
2015 case EL_NEXT_CE_5:
2016 case EL_NEXT_CE_6:
2017 case EL_NEXT_CE_7:
2018 case EL_NEXT_CE_8:
2019 /* reference elements should not be used on the playfield */
2020 Feld[x][y] = EL_EMPTY;
2021 break;
2022
2023 default:
2024 if (IS_CUSTOM_ELEMENT(element))
2025 {
2026 if (CAN_MOVE(element))
2027 InitMovDir(x, y);
2028
2029 #if USE_NEW_CUSTOM_VALUE
2030 if (!element_info[element].use_last_ce_value || init_game)
2031 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2032 #endif
2033 }
2034 else if (IS_GROUP_ELEMENT(element))
2035 {
2036 Feld[x][y] = GetElementFromGroupElement(element);
2037
2038 InitField(x, y, init_game);
2039 }
2040
2041 break;
2042 }
2043
2044 if (!init_game)
2045 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2046 }
2047
InitField_WithBug1(int x,int y,boolean init_game)2048 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2049 {
2050 InitField(x, y, init_game);
2051
2052 /* not needed to call InitMovDir() -- already done by InitField()! */
2053 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054 CAN_MOVE(Feld[x][y]))
2055 InitMovDir(x, y);
2056 }
2057
InitField_WithBug2(int x,int y,boolean init_game)2058 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2059 {
2060 int old_element = Feld[x][y];
2061
2062 InitField(x, y, init_game);
2063
2064 /* not needed to call InitMovDir() -- already done by InitField()! */
2065 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2066 CAN_MOVE(old_element) &&
2067 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2068 InitMovDir(x, y);
2069
2070 /* this case is in fact a combination of not less than three bugs:
2071 first, it calls InitMovDir() for elements that can move, although this is
2072 already done by InitField(); then, it checks the element that was at this
2073 field _before_ the call to InitField() (which can change it); lastly, it
2074 was not called for "mole with direction" elements, which were treated as
2075 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2076 */
2077 }
2078
2079 #if 1
2080
get_key_element_from_nr(int key_nr)2081 static int get_key_element_from_nr(int key_nr)
2082 {
2083 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2084 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2085 EL_EM_KEY_1 : EL_KEY_1);
2086
2087 return key_base_element + key_nr;
2088 }
2089
get_next_dropped_element(struct PlayerInfo * player)2090 static int get_next_dropped_element(struct PlayerInfo *player)
2091 {
2092 return (player->inventory_size > 0 ?
2093 player->inventory_element[player->inventory_size - 1] :
2094 player->inventory_infinite_element != EL_UNDEFINED ?
2095 player->inventory_infinite_element :
2096 player->dynabombs_left > 0 ?
2097 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2098 EL_UNDEFINED);
2099 }
2100
get_inventory_element_from_pos(struct PlayerInfo * player,int pos)2101 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2102 {
2103 /* pos >= 0: get element from bottom of the stack;
2104 pos < 0: get element from top of the stack */
2105
2106 if (pos < 0)
2107 {
2108 int min_inventory_size = -pos;
2109 int inventory_pos = player->inventory_size - min_inventory_size;
2110 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2111
2112 return (player->inventory_size >= min_inventory_size ?
2113 player->inventory_element[inventory_pos] :
2114 player->inventory_infinite_element != EL_UNDEFINED ?
2115 player->inventory_infinite_element :
2116 player->dynabombs_left >= min_dynabombs_left ?
2117 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2118 EL_UNDEFINED);
2119 }
2120 else
2121 {
2122 int min_dynabombs_left = pos + 1;
2123 int min_inventory_size = pos + 1 - player->dynabombs_left;
2124 int inventory_pos = pos - player->dynabombs_left;
2125
2126 return (player->inventory_infinite_element != EL_UNDEFINED ?
2127 player->inventory_infinite_element :
2128 player->dynabombs_left >= min_dynabombs_left ?
2129 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2130 player->inventory_size >= min_inventory_size ?
2131 player->inventory_element[inventory_pos] :
2132 EL_UNDEFINED);
2133 }
2134 }
2135
compareGamePanelOrderInfo(const void * object1,const void * object2)2136 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2137 {
2138 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2139 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2140 int compare_result;
2141
2142 if (gpo1->sort_priority != gpo2->sort_priority)
2143 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2144 else
2145 compare_result = gpo1->nr - gpo2->nr;
2146
2147 return compare_result;
2148 }
2149
InitGameControlValues()2150 void InitGameControlValues()
2151 {
2152 int i;
2153
2154 for (i = 0; game_panel_controls[i].nr != -1; i++)
2155 {
2156 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2157 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2158 struct TextPosInfo *pos = gpc->pos;
2159 int nr = gpc->nr;
2160 int type = gpc->type;
2161
2162 if (nr != i)
2163 {
2164 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2165 Error(ERR_EXIT, "this should not happen -- please debug");
2166 }
2167
2168 /* force update of game controls after initialization */
2169 gpc->value = gpc->last_value = -1;
2170 gpc->frame = gpc->last_frame = -1;
2171 gpc->gfx_frame = -1;
2172
2173 /* determine panel value width for later calculation of alignment */
2174 if (type == TYPE_INTEGER || type == TYPE_STRING)
2175 {
2176 pos->width = pos->size * getFontWidth(pos->font);
2177 pos->height = getFontHeight(pos->font);
2178 }
2179 else if (type == TYPE_ELEMENT)
2180 {
2181 pos->width = pos->size;
2182 pos->height = pos->size;
2183 }
2184
2185 /* fill structure for game panel draw order */
2186 gpo->nr = gpc->nr;
2187 gpo->sort_priority = pos->sort_priority;
2188 }
2189
2190 /* sort game panel controls according to sort_priority and control number */
2191 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2193 }
2194
UpdatePlayfieldElementCount()2195 void UpdatePlayfieldElementCount()
2196 {
2197 boolean use_element_count = FALSE;
2198 int i, j, x, y;
2199
2200 /* first check if it is needed at all to calculate playfield element count */
2201 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203 use_element_count = TRUE;
2204
2205 if (!use_element_count)
2206 return;
2207
2208 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209 element_info[i].element_count = 0;
2210
2211 SCAN_PLAYFIELD(x, y)
2212 {
2213 element_info[Feld[x][y]].element_count++;
2214 }
2215
2216 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218 if (IS_IN_GROUP(j, i))
2219 element_info[EL_GROUP_START + i].element_count +=
2220 element_info[j].element_count;
2221 }
2222
UpdateGameControlValues()2223 void UpdateGameControlValues()
2224 {
2225 int i, k;
2226 int time = (local_player->LevelSolved ?
2227 local_player->LevelSolved_CountingTime :
2228 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229 level.native_em_level->lev->time :
2230 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231 level.native_sp_level->game_sp->time_played :
2232 level.time == 0 ? TimePlayed : TimeLeft);
2233 int score = (local_player->LevelSolved ?
2234 local_player->LevelSolved_CountingScore :
2235 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236 level.native_em_level->lev->score :
2237 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238 level.native_sp_level->game_sp->score :
2239 local_player->score);
2240 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241 level.native_em_level->lev->required :
2242 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243 level.native_sp_level->game_sp->infotrons_still_needed :
2244 local_player->gems_still_needed);
2245 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246 level.native_em_level->lev->required > 0 :
2247 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2249 local_player->gems_still_needed > 0 ||
2250 local_player->sokobanfields_still_needed > 0 ||
2251 local_player->lights_still_needed > 0);
2252
2253 UpdatePlayfieldElementCount();
2254
2255 /* update game panel control values */
2256
2257 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2258 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259
2260 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261 for (i = 0; i < MAX_NUM_KEYS; i++)
2262 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265
2266 if (game.centered_player_nr == -1)
2267 {
2268 for (i = 0; i < MAX_PLAYERS; i++)
2269 {
2270 /* only one player in Supaplex game engine */
2271 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272 break;
2273
2274 for (k = 0; k < MAX_NUM_KEYS; k++)
2275 {
2276 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277 {
2278 if (level.native_em_level->ply[i]->keys & (1 << k))
2279 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280 get_key_element_from_nr(k);
2281 }
2282 else if (stored_player[i].key[k])
2283 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284 get_key_element_from_nr(k);
2285 }
2286
2287 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2289 level.native_em_level->ply[i]->dynamite;
2290 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2291 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292 level.native_sp_level->game_sp->red_disk_count;
2293 else
2294 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2295 stored_player[i].inventory_size;
2296
2297 if (stored_player[i].num_white_keys > 0)
2298 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2299 EL_DC_KEY_WHITE;
2300
2301 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302 stored_player[i].num_white_keys;
2303 }
2304 }
2305 else
2306 {
2307 int player_nr = game.centered_player_nr;
2308
2309 for (k = 0; k < MAX_NUM_KEYS; k++)
2310 {
2311 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2312 {
2313 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2314 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315 get_key_element_from_nr(k);
2316 }
2317 else if (stored_player[player_nr].key[k])
2318 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2319 get_key_element_from_nr(k);
2320 }
2321
2322 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2323 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2324 level.native_em_level->ply[player_nr]->dynamite;
2325 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2326 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2327 level.native_sp_level->game_sp->red_disk_count;
2328 else
2329 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330 stored_player[player_nr].inventory_size;
2331
2332 if (stored_player[player_nr].num_white_keys > 0)
2333 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336 stored_player[player_nr].num_white_keys;
2337 }
2338
2339 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2340 {
2341 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2342 get_inventory_element_from_pos(local_player, i);
2343 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2344 get_inventory_element_from_pos(local_player, -i - 1);
2345 }
2346
2347 game_panel_controls[GAME_PANEL_SCORE].value = score;
2348 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2349
2350 game_panel_controls[GAME_PANEL_TIME].value = time;
2351
2352 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2353 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2354 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2355
2356 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2357
2358 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2359 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2360 EL_EMPTY);
2361 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2362 local_player->shield_normal_time_left;
2363 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2364 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2365 EL_EMPTY);
2366 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2367 local_player->shield_deadly_time_left;
2368
2369 game_panel_controls[GAME_PANEL_EXIT].value =
2370 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2371
2372 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2373 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2374 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2375 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2376 EL_EMC_MAGIC_BALL_SWITCH);
2377
2378 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2379 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2380 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2381 game.light_time_left;
2382
2383 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2384 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2385 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2386 game.timegate_time_left;
2387
2388 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2389 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2390
2391 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2392 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2393 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2394 game.lenses_time_left;
2395
2396 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2397 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2398 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2399 game.magnify_time_left;
2400
2401 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2402 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2403 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2404 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2405 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2406 EL_BALLOON_SWITCH_NONE);
2407
2408 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2409 local_player->dynabomb_count;
2410 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2411 local_player->dynabomb_size;
2412 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2413 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2414
2415 game_panel_controls[GAME_PANEL_PENGUINS].value =
2416 local_player->friends_still_needed;
2417
2418 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2419 local_player->sokobanfields_still_needed;
2420 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2421 local_player->sokobanfields_still_needed;
2422
2423 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2424 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2425
2426 for (i = 0; i < NUM_BELTS; i++)
2427 {
2428 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2429 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2430 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2431 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2432 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2433 }
2434
2435 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2436 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2437 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2438 game.magic_wall_time_left;
2439
2440 #if USE_PLAYER_GRAVITY
2441 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2442 local_player->gravity;
2443 #else
2444 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2445 #endif
2446
2447 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2448 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2449
2450 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2451 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2452 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2453 game.panel.element[i].id : EL_UNDEFINED);
2454
2455 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2456 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2457 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2458 element_info[game.panel.element_count[i].id].element_count : 0);
2459
2460 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2461 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2462 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2463 element_info[game.panel.ce_score[i].id].collect_score : 0);
2464
2465 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2466 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2467 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2468 element_info[game.panel.ce_score_element[i].id].collect_score :
2469 EL_UNDEFINED);
2470
2471 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2472 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2473 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2474
2475 /* update game panel control frames */
2476
2477 for (i = 0; game_panel_controls[i].nr != -1; i++)
2478 {
2479 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2480
2481 if (gpc->type == TYPE_ELEMENT)
2482 {
2483 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2484 {
2485 int last_anim_random_frame = gfx.anim_random_frame;
2486 int element = gpc->value;
2487 int graphic = el2panelimg(element);
2488
2489 if (gpc->value != gpc->last_value)
2490 {
2491 gpc->gfx_frame = 0;
2492 gpc->gfx_random = INIT_GFX_RANDOM();
2493 }
2494 else
2495 {
2496 gpc->gfx_frame++;
2497
2498 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2499 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2500 gpc->gfx_random = INIT_GFX_RANDOM();
2501 }
2502
2503 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504 gfx.anim_random_frame = gpc->gfx_random;
2505
2506 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2507 gpc->gfx_frame = element_info[element].collect_score;
2508
2509 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2510 gpc->gfx_frame);
2511
2512 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2513 gfx.anim_random_frame = last_anim_random_frame;
2514 }
2515 }
2516 }
2517 }
2518
DisplayGameControlValues()2519 void DisplayGameControlValues()
2520 {
2521 boolean redraw_panel = FALSE;
2522 int i;
2523
2524 for (i = 0; game_panel_controls[i].nr != -1; i++)
2525 {
2526 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2527
2528 if (PANEL_DEACTIVATED(gpc->pos))
2529 continue;
2530
2531 if (gpc->value == gpc->last_value &&
2532 gpc->frame == gpc->last_frame)
2533 continue;
2534
2535 redraw_panel = TRUE;
2536 }
2537
2538 if (!redraw_panel)
2539 return;
2540
2541 /* copy default game door content to main double buffer */
2542 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2543 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2544
2545 /* redraw game control buttons */
2546 #if 1
2547 RedrawGameButtons();
2548 #else
2549 UnmapGameButtons();
2550 MapGameButtons();
2551 #endif
2552
2553 game_status = GAME_MODE_PSEUDO_PANEL;
2554
2555 #if 1
2556 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2557 #else
2558 for (i = 0; game_panel_controls[i].nr != -1; i++)
2559 #endif
2560 {
2561 #if 1
2562 int nr = game_panel_order[i].nr;
2563 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2564 #else
2565 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2566 int nr = gpc->nr;
2567 #endif
2568 struct TextPosInfo *pos = gpc->pos;
2569 int type = gpc->type;
2570 int value = gpc->value;
2571 int frame = gpc->frame;
2572 #if 0
2573 int last_value = gpc->last_value;
2574 int last_frame = gpc->last_frame;
2575 #endif
2576 int size = pos->size;
2577 int font = pos->font;
2578 boolean draw_masked = pos->draw_masked;
2579 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2580
2581 if (PANEL_DEACTIVATED(pos))
2582 continue;
2583
2584 #if 0
2585 if (value == last_value && frame == last_frame)
2586 continue;
2587 #endif
2588
2589 gpc->last_value = value;
2590 gpc->last_frame = frame;
2591
2592 #if 0
2593 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2594 #endif
2595
2596 if (type == TYPE_INTEGER)
2597 {
2598 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2599 nr == GAME_PANEL_TIME)
2600 {
2601 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2602
2603 if (use_dynamic_size) /* use dynamic number of digits */
2604 {
2605 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2606 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2607 int size2 = size1 + 1;
2608 int font1 = pos->font;
2609 int font2 = pos->font_alt;
2610
2611 size = (value < value_change ? size1 : size2);
2612 font = (value < value_change ? font1 : font2);
2613
2614 #if 0
2615 /* clear background if value just changed its size (dynamic digits) */
2616 if ((last_value < value_change) != (value < value_change))
2617 {
2618 int width1 = size1 * getFontWidth(font1);
2619 int width2 = size2 * getFontWidth(font2);
2620 int max_width = MAX(width1, width2);
2621 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2622
2623 pos->width = max_width;
2624
2625 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2626 max_width, max_height);
2627 }
2628 #endif
2629 }
2630 }
2631
2632 #if 1
2633 /* correct text size if "digits" is zero or less */
2634 if (size <= 0)
2635 size = strlen(int2str(value, size));
2636
2637 /* dynamically correct text alignment */
2638 pos->width = size * getFontWidth(font);
2639 #endif
2640
2641 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2642 int2str(value, size), font, mask_mode);
2643 }
2644 else if (type == TYPE_ELEMENT)
2645 {
2646 int element, graphic;
2647 Bitmap *src_bitmap;
2648 int src_x, src_y;
2649 int width, height;
2650 int dst_x = PANEL_XPOS(pos);
2651 int dst_y = PANEL_YPOS(pos);
2652
2653 #if 1
2654 if (value != EL_UNDEFINED && value != EL_EMPTY)
2655 {
2656 element = value;
2657 graphic = el2panelimg(value);
2658
2659 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2660
2661 #if 1
2662 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2663 size = TILESIZE;
2664 #endif
2665
2666 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2667 &src_x, &src_y);
2668
2669 width = graphic_info[graphic].width * size / TILESIZE;
2670 height = graphic_info[graphic].height * size / TILESIZE;
2671
2672 if (draw_masked)
2673 {
2674 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2675 dst_x - src_x, dst_y - src_y);
2676 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2677 dst_x, dst_y);
2678 }
2679 else
2680 {
2681 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2682 dst_x, dst_y);
2683 }
2684 }
2685 #else
2686 if (value == EL_UNDEFINED || value == EL_EMPTY)
2687 {
2688 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2689 graphic = el2panelimg(element);
2690
2691 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2692 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2693 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2694 }
2695 else
2696 {
2697 element = value;
2698 graphic = el2panelimg(value);
2699
2700 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2701 }
2702
2703 width = graphic_info[graphic].width * size / TILESIZE;
2704 height = graphic_info[graphic].height * size / TILESIZE;
2705
2706 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2707 #endif
2708 }
2709 else if (type == TYPE_STRING)
2710 {
2711 boolean active = (value != 0);
2712 char *state_normal = "off";
2713 char *state_active = "on";
2714 char *state = (active ? state_active : state_normal);
2715 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2716 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2717 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2718 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2719
2720 if (nr == GAME_PANEL_GRAVITY_STATE)
2721 {
2722 int font1 = pos->font; /* (used for normal state) */
2723 int font2 = pos->font_alt; /* (used for active state) */
2724 #if 0
2725 int size1 = strlen(state_normal);
2726 int size2 = strlen(state_active);
2727 int width1 = size1 * getFontWidth(font1);
2728 int width2 = size2 * getFontWidth(font2);
2729 int max_width = MAX(width1, width2);
2730 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2731
2732 pos->width = max_width;
2733
2734 /* clear background for values that may have changed its size */
2735 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2736 max_width, max_height);
2737 #endif
2738
2739 font = (active ? font2 : font1);
2740 }
2741
2742 if (s != NULL)
2743 {
2744 char *s_cut;
2745
2746 #if 1
2747 if (size <= 0)
2748 {
2749 /* don't truncate output if "chars" is zero or less */
2750 size = strlen(s);
2751
2752 /* dynamically correct text alignment */
2753 pos->width = size * getFontWidth(font);
2754 }
2755 #endif
2756
2757 s_cut = getStringCopyN(s, size);
2758
2759 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2760 s_cut, font, mask_mode);
2761
2762 free(s_cut);
2763 }
2764 }
2765
2766 redraw_mask |= REDRAW_DOOR_1;
2767 }
2768
2769 game_status = GAME_MODE_PLAYING;
2770 }
2771
UpdateAndDisplayGameControlValues()2772 void UpdateAndDisplayGameControlValues()
2773 {
2774 if (tape.warp_forward)
2775 return;
2776
2777 UpdateGameControlValues();
2778 DisplayGameControlValues();
2779 }
2780
DrawGameValue_Emeralds(int value)2781 void DrawGameValue_Emeralds(int value)
2782 {
2783 struct TextPosInfo *pos = &game.panel.gems;
2784 #if 1
2785 int font_nr = pos->font;
2786 #else
2787 int font_nr = FONT_TEXT_2;
2788 #endif
2789 int font_width = getFontWidth(font_nr);
2790 int chars = pos->size;
2791
2792 #if 1
2793 return; /* !!! USE NEW STUFF !!! */
2794 #endif
2795
2796 if (PANEL_DEACTIVATED(pos))
2797 return;
2798
2799 pos->width = chars * font_width;
2800
2801 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2802 }
2803
DrawGameValue_Dynamite(int value)2804 void DrawGameValue_Dynamite(int value)
2805 {
2806 struct TextPosInfo *pos = &game.panel.inventory_count;
2807 #if 1
2808 int font_nr = pos->font;
2809 #else
2810 int font_nr = FONT_TEXT_2;
2811 #endif
2812 int font_width = getFontWidth(font_nr);
2813 int chars = pos->size;
2814
2815 #if 1
2816 return; /* !!! USE NEW STUFF !!! */
2817 #endif
2818
2819 if (PANEL_DEACTIVATED(pos))
2820 return;
2821
2822 pos->width = chars * font_width;
2823
2824 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2825 }
2826
DrawGameValue_Score(int value)2827 void DrawGameValue_Score(int value)
2828 {
2829 struct TextPosInfo *pos = &game.panel.score;
2830 #if 1
2831 int font_nr = pos->font;
2832 #else
2833 int font_nr = FONT_TEXT_2;
2834 #endif
2835 int font_width = getFontWidth(font_nr);
2836 int chars = pos->size;
2837
2838 #if 1
2839 return; /* !!! USE NEW STUFF !!! */
2840 #endif
2841
2842 if (PANEL_DEACTIVATED(pos))
2843 return;
2844
2845 pos->width = chars * font_width;
2846
2847 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2848 }
2849
DrawGameValue_Time(int value)2850 void DrawGameValue_Time(int value)
2851 {
2852 struct TextPosInfo *pos = &game.panel.time;
2853 static int last_value = -1;
2854 int chars1 = 3;
2855 int chars2 = 4;
2856 int chars = pos->size;
2857 #if 1
2858 int font1_nr = pos->font;
2859 int font2_nr = pos->font_alt;
2860 #else
2861 int font1_nr = FONT_TEXT_2;
2862 int font2_nr = FONT_TEXT_1;
2863 #endif
2864 int font_nr = font1_nr;
2865 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2866
2867 #if 1
2868 return; /* !!! USE NEW STUFF !!! */
2869 #endif
2870
2871 if (PANEL_DEACTIVATED(pos))
2872 return;
2873
2874 if (use_dynamic_chars) /* use dynamic number of chars */
2875 {
2876 chars = (value < 1000 ? chars1 : chars2);
2877 font_nr = (value < 1000 ? font1_nr : font2_nr);
2878 }
2879
2880 /* clear background if value just changed its size (dynamic chars only) */
2881 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2882 {
2883 int width1 = chars1 * getFontWidth(font1_nr);
2884 int width2 = chars2 * getFontWidth(font2_nr);
2885 int max_width = MAX(width1, width2);
2886 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2887
2888 pos->width = max_width;
2889
2890 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2891 max_width, max_height);
2892 }
2893
2894 pos->width = chars * getFontWidth(font_nr);
2895
2896 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2897
2898 last_value = value;
2899 }
2900
DrawGameValue_Level(int value)2901 void DrawGameValue_Level(int value)
2902 {
2903 struct TextPosInfo *pos = &game.panel.level_number;
2904 int chars1 = 2;
2905 int chars2 = 3;
2906 int chars = pos->size;
2907 #if 1
2908 int font1_nr = pos->font;
2909 int font2_nr = pos->font_alt;
2910 #else
2911 int font1_nr = FONT_TEXT_2;
2912 int font2_nr = FONT_TEXT_1;
2913 #endif
2914 int font_nr = font1_nr;
2915 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2916
2917 #if 1
2918 return; /* !!! USE NEW STUFF !!! */
2919 #endif
2920
2921 if (PANEL_DEACTIVATED(pos))
2922 return;
2923
2924 if (use_dynamic_chars) /* use dynamic number of chars */
2925 {
2926 chars = (level_nr < 100 ? chars1 : chars2);
2927 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2928 }
2929
2930 pos->width = chars * getFontWidth(font_nr);
2931
2932 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2933 }
2934
DrawGameValue_Keys(int key[MAX_NUM_KEYS])2935 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2936 {
2937 #if 0
2938 struct TextPosInfo *pos = &game.panel.keys;
2939 #endif
2940 #if 0
2941 int base_key_graphic = EL_KEY_1;
2942 #endif
2943 int i;
2944
2945 #if 1
2946 return; /* !!! USE NEW STUFF !!! */
2947 #endif
2948
2949 #if 0
2950 if (PANEL_DEACTIVATED(pos))
2951 return;
2952 #endif
2953
2954 #if 0
2955 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2956 base_key_graphic = EL_EM_KEY_1;
2957 #endif
2958
2959 #if 0
2960 pos->width = 4 * MINI_TILEX;
2961 #endif
2962
2963 #if 1
2964 for (i = 0; i < MAX_NUM_KEYS; i++)
2965 #else
2966 /* currently only 4 of 8 possible keys are displayed */
2967 for (i = 0; i < STD_NUM_KEYS; i++)
2968 #endif
2969 {
2970 #if 1
2971 struct TextPosInfo *pos = &game.panel.key[i];
2972 #endif
2973 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2974 int src_y = DOOR_GFX_PAGEY1 + 123;
2975 #if 1
2976 int dst_x = PANEL_XPOS(pos);
2977 int dst_y = PANEL_YPOS(pos);
2978 #else
2979 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2980 int dst_y = PANEL_YPOS(pos);
2981 #endif
2982
2983 #if 1
2984 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2985 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2986 EL_KEY_1) + i;
2987 int graphic = el2edimg(element);
2988 #endif
2989
2990 #if 1
2991 if (PANEL_DEACTIVATED(pos))
2992 continue;
2993 #endif
2994
2995 #if 0
2996 /* masked blit with tiles from half-size scaled bitmap does not work yet
2997 (no mask bitmap created for these sizes after loading and scaling) --
2998 solution: load without creating mask, scale, then create final mask */
2999
3000 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3001 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3002
3003 if (key[i])
3004 {
3005 #if 0
3006 int graphic = el2edimg(base_key_graphic + i);
3007 #endif
3008 Bitmap *src_bitmap;
3009 int src_x, src_y;
3010
3011 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3012
3013 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3014 dst_x - src_x, dst_y - src_y);
3015 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3016 dst_x, dst_y);
3017 }
3018 #else
3019 #if 1
3020 if (key[i])
3021 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3022 else
3023 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3024 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3025 #else
3026 if (key[i])
3027 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3028 else
3029 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3030 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3031 #endif
3032 #endif
3033 }
3034 }
3035
3036 #else
3037
DrawGameValue_Emeralds(int value)3038 void DrawGameValue_Emeralds(int value)
3039 {
3040 int font_nr = FONT_TEXT_2;
3041 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3042
3043 if (PANEL_DEACTIVATED(game.panel.gems))
3044 return;
3045
3046 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3047 }
3048
DrawGameValue_Dynamite(int value)3049 void DrawGameValue_Dynamite(int value)
3050 {
3051 int font_nr = FONT_TEXT_2;
3052 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3053
3054 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3055 return;
3056
3057 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3058 }
3059
DrawGameValue_Score(int value)3060 void DrawGameValue_Score(int value)
3061 {
3062 int font_nr = FONT_TEXT_2;
3063 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3064
3065 if (PANEL_DEACTIVATED(game.panel.score))
3066 return;
3067
3068 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3069 }
3070
DrawGameValue_Time(int value)3071 void DrawGameValue_Time(int value)
3072 {
3073 int font1_nr = FONT_TEXT_2;
3074 #if 1
3075 int font2_nr = FONT_TEXT_1;
3076 #else
3077 int font2_nr = FONT_LEVEL_NUMBER;
3078 #endif
3079 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3080 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3081
3082 if (PANEL_DEACTIVATED(game.panel.time))
3083 return;
3084
3085 /* clear background if value just changed its size */
3086 if (value == 999 || value == 1000)
3087 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3088
3089 if (value < 1000)
3090 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3091 else
3092 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3093 }
3094
DrawGameValue_Level(int value)3095 void DrawGameValue_Level(int value)
3096 {
3097 int font1_nr = FONT_TEXT_2;
3098 #if 1
3099 int font2_nr = FONT_TEXT_1;
3100 #else
3101 int font2_nr = FONT_LEVEL_NUMBER;
3102 #endif
3103
3104 if (PANEL_DEACTIVATED(game.panel.level))
3105 return;
3106
3107 if (level_nr < 100)
3108 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3109 else
3110 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3111 }
3112
DrawGameValue_Keys(int key[MAX_NUM_KEYS])3113 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3114 {
3115 int base_key_graphic = EL_KEY_1;
3116 int i;
3117
3118 if (PANEL_DEACTIVATED(game.panel.keys))
3119 return;
3120
3121 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3122 base_key_graphic = EL_EM_KEY_1;
3123
3124 /* currently only 4 of 8 possible keys are displayed */
3125 for (i = 0; i < STD_NUM_KEYS; i++)
3126 {
3127 int x = XX_KEYS + i * MINI_TILEX;
3128 int y = YY_KEYS;
3129
3130 if (key[i])
3131 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3132 else
3133 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3134 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3135 }
3136 }
3137
3138 #endif
3139
DrawAllGameValues(int emeralds,int dynamite,int score,int time,int key_bits)3140 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3141 int key_bits)
3142 {
3143 int key[MAX_NUM_KEYS];
3144 int i;
3145
3146 /* prevent EM engine from updating time/score values parallel to GameWon() */
3147 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3148 local_player->LevelSolved)
3149 return;
3150
3151 for (i = 0; i < MAX_NUM_KEYS; i++)
3152 key[i] = key_bits & (1 << i);
3153
3154 DrawGameValue_Level(level_nr);
3155
3156 DrawGameValue_Emeralds(emeralds);
3157 DrawGameValue_Dynamite(dynamite);
3158 DrawGameValue_Score(score);
3159 DrawGameValue_Time(time);
3160
3161 DrawGameValue_Keys(key);
3162 }
3163
UpdateGameDoorValues()3164 void UpdateGameDoorValues()
3165 {
3166 UpdateGameControlValues();
3167 }
3168
DrawGameDoorValues()3169 void DrawGameDoorValues()
3170 {
3171 DisplayGameControlValues();
3172 }
3173
DrawGameDoorValues_OLD()3174 void DrawGameDoorValues_OLD()
3175 {
3176 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3177 int dynamite_value = 0;
3178 int score_value = (local_player->LevelSolved ? local_player->score_final :
3179 local_player->score);
3180 int gems_value = local_player->gems_still_needed;
3181 int key_bits = 0;
3182 int i, j;
3183
3184 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3185 {
3186 DrawGameDoorValues_EM();
3187
3188 return;
3189 }
3190
3191 if (game.centered_player_nr == -1)
3192 {
3193 for (i = 0; i < MAX_PLAYERS; i++)
3194 {
3195 for (j = 0; j < MAX_NUM_KEYS; j++)
3196 if (stored_player[i].key[j])
3197 key_bits |= (1 << j);
3198
3199 dynamite_value += stored_player[i].inventory_size;
3200 }
3201 }
3202 else
3203 {
3204 int player_nr = game.centered_player_nr;
3205
3206 for (i = 0; i < MAX_NUM_KEYS; i++)
3207 if (stored_player[player_nr].key[i])
3208 key_bits |= (1 << i);
3209
3210 dynamite_value = stored_player[player_nr].inventory_size;
3211 }
3212
3213 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3214 key_bits);
3215 }
3216
3217
3218 /*
3219 =============================================================================
3220 InitGameEngine()
3221 -----------------------------------------------------------------------------
3222 initialize game engine due to level / tape version number
3223 =============================================================================
3224 */
3225
InitGameEngine()3226 static void InitGameEngine()
3227 {
3228 int i, j, k, l, x, y;
3229
3230 /* set game engine from tape file when re-playing, else from level file */
3231 game.engine_version = (tape.playing ? tape.engine_version :
3232 level.game_version);
3233
3234 /* ---------------------------------------------------------------------- */
3235 /* set flags for bugs and changes according to active game engine version */
3236 /* ---------------------------------------------------------------------- */
3237
3238 /*
3239 Summary of bugfix/change:
3240 Fixed handling for custom elements that change when pushed by the player.
3241
3242 Fixed/changed in version:
3243 3.1.0
3244
3245 Description:
3246 Before 3.1.0, custom elements that "change when pushing" changed directly
3247 after the player started pushing them (until then handled in "DigField()").
3248 Since 3.1.0, these custom elements are not changed until the "pushing"
3249 move of the element is finished (now handled in "ContinueMoving()").
3250
3251 Affected levels/tapes:
3252 The first condition is generally needed for all levels/tapes before version
3253 3.1.0, which might use the old behaviour before it was changed; known tapes
3254 that are affected are some tapes from the level set "Walpurgis Gardens" by
3255 Jamie Cullen.
3256 The second condition is an exception from the above case and is needed for
3257 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3258 above (including some development versions of 3.1.0), but before it was
3259 known that this change would break tapes like the above and was fixed in
3260 3.1.1, so that the changed behaviour was active although the engine version
3261 while recording maybe was before 3.1.0. There is at least one tape that is
3262 affected by this exception, which is the tape for the one-level set "Bug
3263 Machine" by Juergen Bonhagen.
3264 */
3265
3266 game.use_change_when_pushing_bug =
3267 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3268 !(tape.playing &&
3269 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3270 tape.game_version < VERSION_IDENT(3,1,1,0)));
3271
3272 /*
3273 Summary of bugfix/change:
3274 Fixed handling for blocking the field the player leaves when moving.
3275
3276 Fixed/changed in version:
3277 3.1.1
3278
3279 Description:
3280 Before 3.1.1, when "block last field when moving" was enabled, the field
3281 the player is leaving when moving was blocked for the time of the move,
3282 and was directly unblocked afterwards. This resulted in the last field
3283 being blocked for exactly one less than the number of frames of one player
3284 move. Additionally, even when blocking was disabled, the last field was
3285 blocked for exactly one frame.
3286 Since 3.1.1, due to changes in player movement handling, the last field
3287 is not blocked at all when blocking is disabled. When blocking is enabled,
3288 the last field is blocked for exactly the number of frames of one player
3289 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3290 last field is blocked for exactly one more than the number of frames of
3291 one player move.
3292
3293 Affected levels/tapes:
3294 (!!! yet to be determined -- probably many !!!)
3295 */
3296
3297 game.use_block_last_field_bug =
3298 (game.engine_version < VERSION_IDENT(3,1,1,0));
3299
3300 /*
3301 Summary of bugfix/change:
3302 Changed behaviour of CE changes with multiple changes per single frame.
3303
3304 Fixed/changed in version:
3305 3.2.0-6
3306
3307 Description:
3308 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3309 This resulted in race conditions where CEs seem to behave strange in some
3310 situations (where triggered CE changes were just skipped because there was
3311 already a CE change on that tile in the playfield in that engine frame).
3312 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3313 (The number of changes per frame must be limited in any case, because else
3314 it is easily possible to define CE changes that would result in an infinite
3315 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3316 should be set large enough so that it would only be reached in cases where
3317 the corresponding CE change conditions run into a loop. Therefore, it seems
3318 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3319 maximal number of change pages for custom elements.)
3320
3321 Affected levels/tapes:
3322 Probably many.
3323 */
3324
3325 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3326 game.max_num_changes_per_frame = 1;
3327 #else
3328 game.max_num_changes_per_frame =
3329 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3330 #endif
3331
3332 /* ---------------------------------------------------------------------- */
3333
3334 /* default scan direction: scan playfield from top/left to bottom/right */
3335 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3336
3337 /* dynamically adjust element properties according to game engine version */
3338 InitElementPropertiesEngine(game.engine_version);
3339
3340 #if 0
3341 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3342 printf(" tape version == %06d [%s] [file: %06d]\n",
3343 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3344 tape.file_version);
3345 printf(" => game.engine_version == %06d\n", game.engine_version);
3346 #endif
3347
3348 /* ---------- initialize player's initial move delay --------------------- */
3349
3350 /* dynamically adjust player properties according to level information */
3351 for (i = 0; i < MAX_PLAYERS; i++)
3352 game.initial_move_delay_value[i] =
3353 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3354
3355 /* dynamically adjust player properties according to game engine version */
3356 for (i = 0; i < MAX_PLAYERS; i++)
3357 game.initial_move_delay[i] =
3358 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3359 game.initial_move_delay_value[i] : 0);
3360
3361 /* ---------- initialize player's initial push delay --------------------- */
3362
3363 /* dynamically adjust player properties according to game engine version */
3364 game.initial_push_delay_value =
3365 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3366
3367 /* ---------- initialize changing elements ------------------------------- */
3368
3369 /* initialize changing elements information */
3370 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3371 {
3372 struct ElementInfo *ei = &element_info[i];
3373
3374 /* this pointer might have been changed in the level editor */
3375 ei->change = &ei->change_page[0];
3376
3377 if (!IS_CUSTOM_ELEMENT(i))
3378 {
3379 ei->change->target_element = EL_EMPTY_SPACE;
3380 ei->change->delay_fixed = 0;
3381 ei->change->delay_random = 0;
3382 ei->change->delay_frames = 1;
3383 }
3384
3385 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3386 {
3387 ei->has_change_event[j] = FALSE;
3388
3389 ei->event_page_nr[j] = 0;
3390 ei->event_page[j] = &ei->change_page[0];
3391 }
3392 }
3393
3394 /* add changing elements from pre-defined list */
3395 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3396 {
3397 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3398 struct ElementInfo *ei = &element_info[ch_delay->element];
3399
3400 ei->change->target_element = ch_delay->target_element;
3401 ei->change->delay_fixed = ch_delay->change_delay;
3402
3403 ei->change->pre_change_function = ch_delay->pre_change_function;
3404 ei->change->change_function = ch_delay->change_function;
3405 ei->change->post_change_function = ch_delay->post_change_function;
3406
3407 ei->change->can_change = TRUE;
3408 ei->change->can_change_or_has_action = TRUE;
3409
3410 ei->has_change_event[CE_DELAY] = TRUE;
3411
3412 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3413 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3414 }
3415
3416 /* ---------- initialize internal run-time variables --------------------- */
3417
3418 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3419 {
3420 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3421
3422 for (j = 0; j < ei->num_change_pages; j++)
3423 {
3424 ei->change_page[j].can_change_or_has_action =
3425 (ei->change_page[j].can_change |
3426 ei->change_page[j].has_action);
3427 }
3428 }
3429
3430 /* add change events from custom element configuration */
3431 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3432 {
3433 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3434
3435 for (j = 0; j < ei->num_change_pages; j++)
3436 {
3437 if (!ei->change_page[j].can_change_or_has_action)
3438 continue;
3439
3440 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3441 {
3442 /* only add event page for the first page found with this event */
3443 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3444 {
3445 ei->has_change_event[k] = TRUE;
3446
3447 ei->event_page_nr[k] = j;
3448 ei->event_page[k] = &ei->change_page[j];
3449 }
3450 }
3451 }
3452 }
3453
3454 #if 1
3455 /* ---------- initialize reference elements in change conditions --------- */
3456
3457 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3458 {
3459 int element = EL_CUSTOM_START + i;
3460 struct ElementInfo *ei = &element_info[element];
3461
3462 for (j = 0; j < ei->num_change_pages; j++)
3463 {
3464 int trigger_element = ei->change_page[j].initial_trigger_element;
3465
3466 if (trigger_element >= EL_PREV_CE_8 &&
3467 trigger_element <= EL_NEXT_CE_8)
3468 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3469
3470 ei->change_page[j].trigger_element = trigger_element;
3471 }
3472 }
3473 #endif
3474
3475 /* ---------- initialize run-time trigger player and element ------------- */
3476
3477 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3478 {
3479 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3480
3481 for (j = 0; j < ei->num_change_pages; j++)
3482 {
3483 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3484 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3485 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3486 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3487 ei->change_page[j].actual_trigger_ce_value = 0;
3488 ei->change_page[j].actual_trigger_ce_score = 0;
3489 }
3490 }
3491
3492 /* ---------- initialize trigger events ---------------------------------- */
3493
3494 /* initialize trigger events information */
3495 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3496 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3497 trigger_events[i][j] = FALSE;
3498
3499 /* add trigger events from element change event properties */
3500 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3501 {
3502 struct ElementInfo *ei = &element_info[i];
3503
3504 for (j = 0; j < ei->num_change_pages; j++)
3505 {
3506 if (!ei->change_page[j].can_change_or_has_action)
3507 continue;
3508
3509 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3510 {
3511 int trigger_element = ei->change_page[j].trigger_element;
3512
3513 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3514 {
3515 if (ei->change_page[j].has_event[k])
3516 {
3517 if (IS_GROUP_ELEMENT(trigger_element))
3518 {
3519 struct ElementGroupInfo *group =
3520 element_info[trigger_element].group;
3521
3522 for (l = 0; l < group->num_elements_resolved; l++)
3523 trigger_events[group->element_resolved[l]][k] = TRUE;
3524 }
3525 else if (trigger_element == EL_ANY_ELEMENT)
3526 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3527 trigger_events[l][k] = TRUE;
3528 else
3529 trigger_events[trigger_element][k] = TRUE;
3530 }
3531 }
3532 }
3533 }
3534 }
3535
3536 /* ---------- initialize push delay -------------------------------------- */
3537
3538 /* initialize push delay values to default */
3539 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3540 {
3541 if (!IS_CUSTOM_ELEMENT(i))
3542 {
3543 /* set default push delay values (corrected since version 3.0.7-1) */
3544 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3545 {
3546 element_info[i].push_delay_fixed = 2;
3547 element_info[i].push_delay_random = 8;
3548 }
3549 else
3550 {
3551 element_info[i].push_delay_fixed = 8;
3552 element_info[i].push_delay_random = 8;
3553 }
3554 }
3555 }
3556
3557 /* set push delay value for certain elements from pre-defined list */
3558 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3559 {
3560 int e = push_delay_list[i].element;
3561
3562 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3563 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3564 }
3565
3566 /* set push delay value for Supaplex elements for newer engine versions */
3567 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3568 {
3569 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3570 {
3571 if (IS_SP_ELEMENT(i))
3572 {
3573 /* set SP push delay to just enough to push under a falling zonk */
3574 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3575
3576 element_info[i].push_delay_fixed = delay;
3577 element_info[i].push_delay_random = 0;
3578 }
3579 }
3580 }
3581
3582 /* ---------- initialize move stepsize ----------------------------------- */
3583
3584 /* initialize move stepsize values to default */
3585 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3586 if (!IS_CUSTOM_ELEMENT(i))
3587 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3588
3589 /* set move stepsize value for certain elements from pre-defined list */
3590 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3591 {
3592 int e = move_stepsize_list[i].element;
3593
3594 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3595 }
3596
3597 /* ---------- initialize collect score ----------------------------------- */
3598
3599 /* initialize collect score values for custom elements from initial value */
3600 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3601 if (IS_CUSTOM_ELEMENT(i))
3602 element_info[i].collect_score = element_info[i].collect_score_initial;
3603
3604 /* ---------- initialize collect count ----------------------------------- */
3605
3606 /* initialize collect count values for non-custom elements */
3607 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3608 if (!IS_CUSTOM_ELEMENT(i))
3609 element_info[i].collect_count_initial = 0;
3610
3611 /* add collect count values for all elements from pre-defined list */
3612 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3613 element_info[collect_count_list[i].element].collect_count_initial =
3614 collect_count_list[i].count;
3615
3616 /* ---------- initialize access direction -------------------------------- */
3617
3618 /* initialize access direction values to default (access from every side) */
3619 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3620 if (!IS_CUSTOM_ELEMENT(i))
3621 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3622
3623 /* set access direction value for certain elements from pre-defined list */
3624 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3625 element_info[access_direction_list[i].element].access_direction =
3626 access_direction_list[i].direction;
3627
3628 /* ---------- initialize explosion content ------------------------------- */
3629 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3630 {
3631 if (IS_CUSTOM_ELEMENT(i))
3632 continue;
3633
3634 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3635 {
3636 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3637
3638 element_info[i].content.e[x][y] =
3639 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3640 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3641 i == EL_PLAYER_3 ? EL_EMERALD :
3642 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3643 i == EL_MOLE ? EL_EMERALD_RED :
3644 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3645 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3646 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3647 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3648 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3649 i == EL_WALL_EMERALD ? EL_EMERALD :
3650 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3651 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3652 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3653 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3654 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3655 i == EL_WALL_PEARL ? EL_PEARL :
3656 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3657 EL_EMPTY);
3658 }
3659 }
3660
3661 /* ---------- initialize recursion detection ------------------------------ */
3662 recursion_loop_depth = 0;
3663 recursion_loop_detected = FALSE;
3664 recursion_loop_element = EL_UNDEFINED;
3665
3666 /* ---------- initialize graphics engine ---------------------------------- */
3667 game.scroll_delay_value =
3668 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3669 setup.scroll_delay ? setup.scroll_delay_value : 0);
3670 game.scroll_delay_value =
3671 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3672 }
3673
get_num_special_action(int element,int action_first,int action_last)3674 int get_num_special_action(int element, int action_first, int action_last)
3675 {
3676 int num_special_action = 0;
3677 int i, j;
3678
3679 for (i = action_first; i <= action_last; i++)
3680 {
3681 boolean found = FALSE;
3682
3683 for (j = 0; j < NUM_DIRECTIONS; j++)
3684 if (el_act_dir2img(element, i, j) !=
3685 el_act_dir2img(element, ACTION_DEFAULT, j))
3686 found = TRUE;
3687
3688 if (found)
3689 num_special_action++;
3690 else
3691 break;
3692 }
3693
3694 return num_special_action;
3695 }
3696
3697
3698 /*
3699 =============================================================================
3700 InitGame()
3701 -----------------------------------------------------------------------------
3702 initialize and start new game
3703 =============================================================================
3704 */
3705
InitGame()3706 void InitGame()
3707 {
3708 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3709 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3710 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3711 #if 0
3712 boolean do_fading = (game_status == GAME_MODE_MAIN);
3713 #endif
3714 #if 1
3715 int initial_move_dir = MV_DOWN;
3716 #else
3717 int initial_move_dir = MV_NONE;
3718 #endif
3719 int i, j, x, y;
3720
3721 game_status = GAME_MODE_PLAYING;
3722
3723 #if 1
3724 /* needed if different viewport properties defined for playing */
3725 ChangeViewportPropertiesIfNeeded();
3726 #endif
3727
3728 #if 1
3729 DrawCompleteVideoDisplay();
3730 #endif
3731
3732 InitGameEngine();
3733 InitGameControlValues();
3734
3735 /* don't play tapes over network */
3736 network_playing = (options.network && !tape.playing);
3737
3738 for (i = 0; i < MAX_PLAYERS; i++)
3739 {
3740 struct PlayerInfo *player = &stored_player[i];
3741
3742 player->index_nr = i;
3743 player->index_bit = (1 << i);
3744 player->element_nr = EL_PLAYER_1 + i;
3745
3746 player->present = FALSE;
3747 player->active = FALSE;
3748 player->mapped = FALSE;
3749
3750 player->killed = FALSE;
3751 player->reanimated = FALSE;
3752
3753 player->action = 0;
3754 player->effective_action = 0;
3755 player->programmed_action = 0;
3756
3757 player->score = 0;
3758 player->score_final = 0;
3759
3760 player->gems_still_needed = level.gems_needed;
3761 player->sokobanfields_still_needed = 0;
3762 player->lights_still_needed = 0;
3763 player->friends_still_needed = 0;
3764
3765 for (j = 0; j < MAX_NUM_KEYS; j++)
3766 player->key[j] = FALSE;
3767
3768 player->num_white_keys = 0;
3769
3770 player->dynabomb_count = 0;
3771 player->dynabomb_size = 1;
3772 player->dynabombs_left = 0;
3773 player->dynabomb_xl = FALSE;
3774
3775 player->MovDir = initial_move_dir;
3776 player->MovPos = 0;
3777 player->GfxPos = 0;
3778 player->GfxDir = initial_move_dir;
3779 player->GfxAction = ACTION_DEFAULT;
3780 player->Frame = 0;
3781 player->StepFrame = 0;
3782
3783 player->initial_element = player->element_nr;
3784 player->artwork_element =
3785 (level.use_artwork_element[i] ? level.artwork_element[i] :
3786 player->element_nr);
3787 player->use_murphy = FALSE;
3788
3789 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3790 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3791
3792 player->gravity = level.initial_player_gravity[i];
3793
3794 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3795
3796 player->actual_frame_counter = 0;
3797
3798 player->step_counter = 0;
3799
3800 player->last_move_dir = initial_move_dir;
3801
3802 player->is_active = FALSE;
3803
3804 player->is_waiting = FALSE;
3805 player->is_moving = FALSE;
3806 player->is_auto_moving = FALSE;
3807 player->is_digging = FALSE;
3808 player->is_snapping = FALSE;
3809 player->is_collecting = FALSE;
3810 player->is_pushing = FALSE;
3811 player->is_switching = FALSE;
3812 player->is_dropping = FALSE;
3813 player->is_dropping_pressed = FALSE;
3814
3815 player->is_bored = FALSE;
3816 player->is_sleeping = FALSE;
3817
3818 player->frame_counter_bored = -1;
3819 player->frame_counter_sleeping = -1;
3820
3821 player->anim_delay_counter = 0;
3822 player->post_delay_counter = 0;
3823
3824 player->dir_waiting = initial_move_dir;
3825 player->action_waiting = ACTION_DEFAULT;
3826 player->last_action_waiting = ACTION_DEFAULT;
3827 player->special_action_bored = ACTION_DEFAULT;
3828 player->special_action_sleeping = ACTION_DEFAULT;
3829
3830 player->switch_x = -1;
3831 player->switch_y = -1;
3832
3833 player->drop_x = -1;
3834 player->drop_y = -1;
3835
3836 player->show_envelope = 0;
3837
3838 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3839
3840 player->push_delay = -1; /* initialized when pushing starts */
3841 player->push_delay_value = game.initial_push_delay_value;
3842
3843 player->drop_delay = 0;
3844 player->drop_pressed_delay = 0;
3845
3846 player->last_jx = -1;
3847 player->last_jy = -1;
3848 player->jx = -1;
3849 player->jy = -1;
3850
3851 player->shield_normal_time_left = 0;
3852 player->shield_deadly_time_left = 0;
3853
3854 player->inventory_infinite_element = EL_UNDEFINED;
3855 player->inventory_size = 0;
3856
3857 if (level.use_initial_inventory[i])
3858 {
3859 for (j = 0; j < level.initial_inventory_size[i]; j++)
3860 {
3861 int element = level.initial_inventory_content[i][j];
3862 int collect_count = element_info[element].collect_count_initial;
3863 int k;
3864
3865 if (!IS_CUSTOM_ELEMENT(element))
3866 collect_count = 1;
3867
3868 if (collect_count == 0)
3869 player->inventory_infinite_element = element;
3870 else
3871 for (k = 0; k < collect_count; k++)
3872 if (player->inventory_size < MAX_INVENTORY_SIZE)
3873 player->inventory_element[player->inventory_size++] = element;
3874 }
3875 }
3876
3877 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3878 SnapField(player, 0, 0);
3879
3880 player->LevelSolved = FALSE;
3881 player->GameOver = FALSE;
3882
3883 player->LevelSolved_GameWon = FALSE;
3884 player->LevelSolved_GameEnd = FALSE;
3885 player->LevelSolved_PanelOff = FALSE;
3886 player->LevelSolved_SaveTape = FALSE;
3887 player->LevelSolved_SaveScore = FALSE;
3888 player->LevelSolved_CountingTime = 0;
3889 player->LevelSolved_CountingScore = 0;
3890
3891 map_player_action[i] = i;
3892 }
3893
3894 network_player_action_received = FALSE;
3895
3896 #if defined(NETWORK_AVALIABLE)
3897 /* initial null action */
3898 if (network_playing)
3899 SendToServer_MovePlayer(MV_NONE);
3900 #endif
3901
3902 ZX = ZY = -1;
3903 ExitX = ExitY = -1;
3904
3905 FrameCounter = 0;
3906 TimeFrames = 0;
3907 TimePlayed = 0;
3908 TimeLeft = level.time;
3909 TapeTime = 0;
3910
3911 ScreenMovDir = MV_NONE;
3912 ScreenMovPos = 0;
3913 ScreenGfxPos = 0;
3914
3915 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3916
3917 AllPlayersGone = FALSE;
3918
3919 game.yamyam_content_nr = 0;
3920 game.robot_wheel_active = FALSE;
3921 game.magic_wall_active = FALSE;
3922 game.magic_wall_time_left = 0;
3923 game.light_time_left = 0;
3924 game.timegate_time_left = 0;
3925 game.switchgate_pos = 0;
3926 game.wind_direction = level.wind_direction_initial;
3927
3928 #if !USE_PLAYER_GRAVITY
3929 game.gravity = FALSE;
3930 game.explosions_delayed = TRUE;
3931 #endif
3932
3933 game.lenses_time_left = 0;
3934 game.magnify_time_left = 0;
3935
3936 game.ball_state = level.ball_state_initial;
3937 game.ball_content_nr = 0;
3938
3939 game.envelope_active = FALSE;
3940
3941 /* set focus to local player for network games, else to all players */
3942 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3943 game.centered_player_nr_next = game.centered_player_nr;
3944 game.set_centered_player = FALSE;
3945
3946 if (network_playing && tape.recording)
3947 {
3948 /* store client dependent player focus when recording network games */
3949 tape.centered_player_nr_next = game.centered_player_nr_next;
3950 tape.set_centered_player = TRUE;
3951 }
3952
3953 for (i = 0; i < NUM_BELTS; i++)
3954 {
3955 game.belt_dir[i] = MV_NONE;
3956 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3957 }
3958
3959 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3960 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3961
3962 SCAN_PLAYFIELD(x, y)
3963 {
3964 Feld[x][y] = level.field[x][y];
3965 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3966 ChangeDelay[x][y] = 0;
3967 ChangePage[x][y] = -1;
3968 #if USE_NEW_CUSTOM_VALUE
3969 CustomValue[x][y] = 0; /* initialized in InitField() */
3970 #endif
3971 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3972 AmoebaNr[x][y] = 0;
3973 WasJustMoving[x][y] = 0;
3974 WasJustFalling[x][y] = 0;
3975 CheckCollision[x][y] = 0;
3976 CheckImpact[x][y] = 0;
3977 Stop[x][y] = FALSE;
3978 Pushed[x][y] = FALSE;
3979
3980 ChangeCount[x][y] = 0;
3981 ChangeEvent[x][y] = -1;
3982
3983 ExplodePhase[x][y] = 0;
3984 ExplodeDelay[x][y] = 0;
3985 ExplodeField[x][y] = EX_TYPE_NONE;
3986
3987 RunnerVisit[x][y] = 0;
3988 PlayerVisit[x][y] = 0;
3989
3990 GfxFrame[x][y] = 0;
3991 GfxRandom[x][y] = INIT_GFX_RANDOM();
3992 GfxElement[x][y] = EL_UNDEFINED;
3993 GfxAction[x][y] = ACTION_DEFAULT;
3994 GfxDir[x][y] = MV_NONE;
3995 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3996 }
3997
3998 SCAN_PLAYFIELD(x, y)
3999 {
4000 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4001 emulate_bd = FALSE;
4002 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4003 emulate_sb = FALSE;
4004 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4005 emulate_sp = FALSE;
4006
4007 InitField(x, y, TRUE);
4008
4009 ResetGfxAnimation(x, y);
4010 }
4011
4012 InitBeltMovement();
4013
4014 for (i = 0; i < MAX_PLAYERS; i++)
4015 {
4016 struct PlayerInfo *player = &stored_player[i];
4017
4018 /* set number of special actions for bored and sleeping animation */
4019 player->num_special_action_bored =
4020 get_num_special_action(player->artwork_element,
4021 ACTION_BORING_1, ACTION_BORING_LAST);
4022 player->num_special_action_sleeping =
4023 get_num_special_action(player->artwork_element,
4024 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4025 }
4026
4027 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4028 emulate_sb ? EMU_SOKOBAN :
4029 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4030
4031 #if USE_NEW_ALL_SLIPPERY
4032 /* initialize type of slippery elements */
4033 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4034 {
4035 if (!IS_CUSTOM_ELEMENT(i))
4036 {
4037 /* default: elements slip down either to the left or right randomly */
4038 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4039
4040 /* SP style elements prefer to slip down on the left side */
4041 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4042 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4043
4044 /* BD style elements prefer to slip down on the left side */
4045 if (game.emulation == EMU_BOULDERDASH)
4046 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4047 }
4048 }
4049 #endif
4050
4051 /* initialize explosion and ignition delay */
4052 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4053 {
4054 if (!IS_CUSTOM_ELEMENT(i))
4055 {
4056 int num_phase = 8;
4057 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4058 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4059 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4060 int last_phase = (num_phase + 1) * delay;
4061 int half_phase = (num_phase / 2) * delay;
4062
4063 element_info[i].explosion_delay = last_phase - 1;
4064 element_info[i].ignition_delay = half_phase;
4065
4066 if (i == EL_BLACK_ORB)
4067 element_info[i].ignition_delay = 1;
4068 }
4069
4070 #if 0
4071 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4072 element_info[i].explosion_delay = 1;
4073
4074 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4075 element_info[i].ignition_delay = 1;
4076 #endif
4077 }
4078
4079 /* correct non-moving belts to start moving left */
4080 for (i = 0; i < NUM_BELTS; i++)
4081 if (game.belt_dir[i] == MV_NONE)
4082 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4083
4084 #if USE_NEW_PLAYER_ASSIGNMENTS
4085 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4086 /* choose default local player */
4087 local_player = &stored_player[0];
4088
4089 for (i = 0; i < MAX_PLAYERS; i++)
4090 stored_player[i].connected = FALSE;
4091
4092 local_player->connected = TRUE;
4093 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4094
4095 if (tape.playing)
4096 {
4097 /* try to guess locally connected team mode players (needed for correct
4098 assignment of player figures from level to locally playing players) */
4099
4100 for (i = 0; i < MAX_PLAYERS; i++)
4101 if (tape.player_participates[i])
4102 stored_player[i].connected = TRUE;
4103 }
4104 else if (setup.team_mode && !options.network)
4105 {
4106 /* try to guess locally connected team mode players (needed for correct
4107 assignment of player figures from level to locally playing players) */
4108
4109 for (i = 0; i < MAX_PLAYERS; i++)
4110 if (setup.input[i].use_joystick ||
4111 setup.input[i].key.left != KSYM_UNDEFINED)
4112 stored_player[i].connected = TRUE;
4113 }
4114
4115 #if 0
4116 for (i = 0; i < MAX_PLAYERS; i++)
4117 printf("::: player %d: %s\n", i,
4118 (stored_player[i].connected ? "connected" : "not connected"));
4119
4120 for (i = 0; i < MAX_PLAYERS; i++)
4121 printf("::: player %d: %s\n", i,
4122 (stored_player[i].present ? "present" : "not present"));
4123 #endif
4124
4125 /* check if any connected player was not found in playfield */
4126 for (i = 0; i < MAX_PLAYERS; i++)
4127 {
4128 struct PlayerInfo *player = &stored_player[i];
4129
4130 if (player->connected && !player->present)
4131 {
4132 struct PlayerInfo *field_player = NULL;
4133
4134 #if 0
4135 printf("::: looking for field player for player %d ...\n", i);
4136 #endif
4137
4138 /* assign first free player found that is present in the playfield */
4139
4140 /* first try: look for unmapped playfield player that is not connected */
4141 if (field_player == NULL)
4142 for (j = 0; j < MAX_PLAYERS; j++)
4143 if (stored_player[j].present &&
4144 !stored_player[j].mapped &&
4145 !stored_player[j].connected)
4146 field_player = &stored_player[j];
4147
4148 /* second try: look for *any* unmapped playfield player */
4149 if (field_player == NULL)
4150 for (j = 0; j < MAX_PLAYERS; j++)
4151 if (stored_player[j].present &&
4152 !stored_player[j].mapped)
4153 field_player = &stored_player[j];
4154
4155 if (field_player != NULL)
4156 {
4157 int jx = field_player->jx, jy = field_player->jy;
4158
4159 #if 0
4160 printf("::: found player figure %d\n", field_player->index_nr);
4161 #endif
4162
4163 player->present = FALSE;
4164 player->active = FALSE;
4165
4166 field_player->present = TRUE;
4167 field_player->active = TRUE;
4168
4169 /*
4170 player->initial_element = field_player->initial_element;
4171 player->artwork_element = field_player->artwork_element;
4172
4173 player->block_last_field = field_player->block_last_field;
4174 player->block_delay_adjustment = field_player->block_delay_adjustment;
4175 */
4176
4177 StorePlayer[jx][jy] = field_player->element_nr;
4178
4179 field_player->jx = field_player->last_jx = jx;
4180 field_player->jy = field_player->last_jy = jy;
4181
4182 if (local_player == player)
4183 local_player = field_player;
4184
4185 map_player_action[field_player->index_nr] = i;
4186
4187 field_player->mapped = TRUE;
4188
4189 #if 0
4190 printf("::: map_player_action[%d] == %d\n",
4191 field_player->index_nr, i);
4192 #endif
4193 }
4194 }
4195
4196 if (player->connected && player->present)
4197 player->mapped = TRUE;
4198 }
4199
4200 #else
4201
4202 /* check if any connected player was not found in playfield */
4203 for (i = 0; i < MAX_PLAYERS; i++)
4204 {
4205 struct PlayerInfo *player = &stored_player[i];
4206
4207 if (player->connected && !player->present)
4208 {
4209 for (j = 0; j < MAX_PLAYERS; j++)
4210 {
4211 struct PlayerInfo *field_player = &stored_player[j];
4212 int jx = field_player->jx, jy = field_player->jy;
4213
4214 /* assign first free player found that is present in the playfield */
4215 if (field_player->present && !field_player->connected)
4216 {
4217 player->present = TRUE;
4218 player->active = TRUE;
4219
4220 field_player->present = FALSE;
4221 field_player->active = FALSE;
4222
4223 player->initial_element = field_player->initial_element;
4224 player->artwork_element = field_player->artwork_element;
4225
4226 player->block_last_field = field_player->block_last_field;
4227 player->block_delay_adjustment = field_player->block_delay_adjustment;
4228
4229 StorePlayer[jx][jy] = player->element_nr;
4230
4231 player->jx = player->last_jx = jx;
4232 player->jy = player->last_jy = jy;
4233
4234 break;
4235 }
4236 }
4237 }
4238 }
4239 #endif
4240
4241 #if 0
4242 printf("::: local_player->present == %d\n", local_player->present);
4243 #endif
4244
4245 if (tape.playing)
4246 {
4247 /* when playing a tape, eliminate all players who do not participate */
4248
4249 #if USE_NEW_PLAYER_ASSIGNMENTS
4250 for (i = 0; i < MAX_PLAYERS; i++)
4251 {
4252 if (stored_player[i].active &&
4253 !tape.player_participates[map_player_action[i]])
4254 {
4255 struct PlayerInfo *player = &stored_player[i];
4256 int jx = player->jx, jy = player->jy;
4257
4258 player->active = FALSE;
4259 StorePlayer[jx][jy] = 0;
4260 Feld[jx][jy] = EL_EMPTY;
4261 }
4262 }
4263 #else
4264 for (i = 0; i < MAX_PLAYERS; i++)
4265 {
4266 if (stored_player[i].active &&
4267 !tape.player_participates[i])
4268 {
4269 struct PlayerInfo *player = &stored_player[i];
4270 int jx = player->jx, jy = player->jy;
4271
4272 player->active = FALSE;
4273 StorePlayer[jx][jy] = 0;
4274 Feld[jx][jy] = EL_EMPTY;
4275 }
4276 }
4277 #endif
4278 }
4279 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4280 {
4281 /* when in single player mode, eliminate all but the first active player */
4282
4283 for (i = 0; i < MAX_PLAYERS; i++)
4284 {
4285 if (stored_player[i].active)
4286 {
4287 for (j = i + 1; j < MAX_PLAYERS; j++)
4288 {
4289 if (stored_player[j].active)
4290 {
4291 struct PlayerInfo *player = &stored_player[j];
4292 int jx = player->jx, jy = player->jy;
4293
4294 player->active = FALSE;
4295 player->present = FALSE;
4296
4297 StorePlayer[jx][jy] = 0;
4298 Feld[jx][jy] = EL_EMPTY;
4299 }
4300 }
4301 }
4302 }
4303 }
4304
4305 /* when recording the game, store which players take part in the game */
4306 if (tape.recording)
4307 {
4308 #if USE_NEW_PLAYER_ASSIGNMENTS
4309 for (i = 0; i < MAX_PLAYERS; i++)
4310 if (stored_player[i].connected)
4311 tape.player_participates[i] = TRUE;
4312 #else
4313 for (i = 0; i < MAX_PLAYERS; i++)
4314 if (stored_player[i].active)
4315 tape.player_participates[i] = TRUE;
4316 #endif
4317 }
4318
4319 if (options.debug)
4320 {
4321 for (i = 0; i < MAX_PLAYERS; i++)
4322 {
4323 struct PlayerInfo *player = &stored_player[i];
4324
4325 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4326 i+1,
4327 player->present,
4328 player->connected,
4329 player->active);
4330 if (local_player == player)
4331 printf("Player %d is local player.\n", i+1);
4332 }
4333 }
4334
4335 if (BorderElement == EL_EMPTY)
4336 {
4337 SBX_Left = 0;
4338 SBX_Right = lev_fieldx - SCR_FIELDX;
4339 SBY_Upper = 0;
4340 SBY_Lower = lev_fieldy - SCR_FIELDY;
4341 }
4342 else
4343 {
4344 SBX_Left = -1;
4345 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4346 SBY_Upper = -1;
4347 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4348 }
4349
4350 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4351 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4352
4353 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4354 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4355
4356 /* if local player not found, look for custom element that might create
4357 the player (make some assumptions about the right custom element) */
4358 if (!local_player->present)
4359 {
4360 int start_x = 0, start_y = 0;
4361 int found_rating = 0;
4362 int found_element = EL_UNDEFINED;
4363 int player_nr = local_player->index_nr;
4364
4365 SCAN_PLAYFIELD(x, y)
4366 {
4367 int element = Feld[x][y];
4368 int content;
4369 int xx, yy;
4370 boolean is_player;
4371
4372 if (level.use_start_element[player_nr] &&
4373 level.start_element[player_nr] == element &&
4374 found_rating < 4)
4375 {
4376 start_x = x;
4377 start_y = y;
4378
4379 found_rating = 4;
4380 found_element = element;
4381 }
4382
4383 if (!IS_CUSTOM_ELEMENT(element))
4384 continue;
4385
4386 if (CAN_CHANGE(element))
4387 {
4388 for (i = 0; i < element_info[element].num_change_pages; i++)
4389 {
4390 /* check for player created from custom element as single target */
4391 content = element_info[element].change_page[i].target_element;
4392 is_player = ELEM_IS_PLAYER(content);
4393
4394 if (is_player && (found_rating < 3 ||
4395 (found_rating == 3 && element < found_element)))
4396 {
4397 start_x = x;
4398 start_y = y;
4399
4400 found_rating = 3;
4401 found_element = element;
4402 }
4403 }
4404 }
4405
4406 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4407 {
4408 /* check for player created from custom element as explosion content */
4409 content = element_info[element].content.e[xx][yy];
4410 is_player = ELEM_IS_PLAYER(content);
4411
4412 if (is_player && (found_rating < 2 ||
4413 (found_rating == 2 && element < found_element)))
4414 {
4415 start_x = x + xx - 1;
4416 start_y = y + yy - 1;
4417
4418 found_rating = 2;
4419 found_element = element;
4420 }
4421
4422 if (!CAN_CHANGE(element))
4423 continue;
4424
4425 for (i = 0; i < element_info[element].num_change_pages; i++)
4426 {
4427 /* check for player created from custom element as extended target */
4428 content =
4429 element_info[element].change_page[i].target_content.e[xx][yy];
4430
4431 is_player = ELEM_IS_PLAYER(content);
4432
4433 if (is_player && (found_rating < 1 ||
4434 (found_rating == 1 && element < found_element)))
4435 {
4436 start_x = x + xx - 1;
4437 start_y = y + yy - 1;
4438
4439 found_rating = 1;
4440 found_element = element;
4441 }
4442 }
4443 }
4444 }
4445
4446 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4447 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4448 start_x - MIDPOSX);
4449
4450 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4451 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4452 start_y - MIDPOSY);
4453 }
4454 else
4455 {
4456 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4457 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4458 local_player->jx - MIDPOSX);
4459
4460 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4461 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4462 local_player->jy - MIDPOSY);
4463 }
4464
4465 #if 0
4466 /* do not use PLAYING mask for fading out from main screen */
4467 game_status = GAME_MODE_MAIN;
4468 #endif
4469
4470 StopAnimation();
4471
4472 if (!game.restart_level)
4473 CloseDoor(DOOR_CLOSE_1);
4474
4475 #if 1
4476 if (level_editor_test_game)
4477 FadeSkipNextFadeIn();
4478 else
4479 FadeSetEnterScreen();
4480 #else
4481 if (level_editor_test_game)
4482 fading = fading_none;
4483 else
4484 fading = menu.destination;
4485 #endif
4486
4487 #if 1
4488 FadeOut(REDRAW_FIELD);
4489 #else
4490 if (do_fading)
4491 FadeOut(REDRAW_FIELD);
4492 #endif
4493
4494 #if 0
4495 game_status = GAME_MODE_PLAYING;
4496 #endif
4497
4498 /* !!! FIX THIS (START) !!! */
4499 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4500 {
4501 InitGameEngine_EM();
4502
4503 /* blit playfield from scroll buffer to normal back buffer for fading in */
4504 BlitScreenToBitmap_EM(backbuffer);
4505 }
4506 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4507 {
4508 InitGameEngine_SP();
4509
4510 /* blit playfield from scroll buffer to normal back buffer for fading in */
4511 BlitScreenToBitmap_SP(backbuffer);
4512 }
4513 else
4514 {
4515 DrawLevel();
4516 DrawAllPlayers();
4517
4518 /* after drawing the level, correct some elements */
4519 if (game.timegate_time_left == 0)
4520 CloseAllOpenTimegates();
4521
4522 /* blit playfield from scroll buffer to normal back buffer for fading in */
4523 if (setup.soft_scrolling)
4524 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4525
4526 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4527 }
4528 /* !!! FIX THIS (END) !!! */
4529
4530 #if 1
4531 FadeIn(REDRAW_FIELD);
4532 #else
4533 if (do_fading)
4534 FadeIn(REDRAW_FIELD);
4535
4536 BackToFront();
4537 #endif
4538
4539 if (!game.restart_level)
4540 {
4541 /* copy default game door content to main double buffer */
4542 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4543 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4544 }
4545
4546 SetPanelBackground();
4547 SetDrawBackgroundMask(REDRAW_DOOR_1);
4548
4549 #if 1
4550 UpdateAndDisplayGameControlValues();
4551 #else
4552 UpdateGameDoorValues();
4553 DrawGameDoorValues();
4554 #endif
4555
4556 if (!game.restart_level)
4557 {
4558 UnmapGameButtons();
4559 UnmapTapeButtons();
4560 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4561 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4562 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4563 MapGameButtons();
4564 MapTapeButtons();
4565
4566 /* copy actual game door content to door double buffer for OpenDoor() */
4567 BlitBitmap(drawto, bitmap_db_door,
4568 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4569
4570 OpenDoor(DOOR_OPEN_ALL);
4571
4572 PlaySound(SND_GAME_STARTING);
4573
4574 if (setup.sound_music)
4575 PlayLevelMusic();
4576
4577 KeyboardAutoRepeatOffUnlessAutoplay();
4578
4579 if (options.debug)
4580 {
4581 for (i = 0; i < MAX_PLAYERS; i++)
4582 printf("Player %d %sactive.\n",
4583 i + 1, (stored_player[i].active ? "" : "not "));
4584 }
4585 }
4586
4587 #if 1
4588 UnmapAllGadgets();
4589
4590 MapGameButtons();
4591 MapTapeButtons();
4592 #endif
4593
4594 game.restart_level = FALSE;
4595 }
4596
UpdateEngineValues(int actual_scroll_x,int actual_scroll_y)4597 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4598 {
4599 /* this is used for non-R'n'D game engines to update certain engine values */
4600
4601 /* needed to determine if sounds are played within the visible screen area */
4602 scroll_x = actual_scroll_x;
4603 scroll_y = actual_scroll_y;
4604 }
4605
InitMovDir(int x,int y)4606 void InitMovDir(int x, int y)
4607 {
4608 int i, element = Feld[x][y];
4609 static int xy[4][2] =
4610 {
4611 { 0, +1 },
4612 { +1, 0 },
4613 { 0, -1 },
4614 { -1, 0 }
4615 };
4616 static int direction[3][4] =
4617 {
4618 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4619 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4620 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4621 };
4622
4623 switch (element)
4624 {
4625 case EL_BUG_RIGHT:
4626 case EL_BUG_UP:
4627 case EL_BUG_LEFT:
4628 case EL_BUG_DOWN:
4629 Feld[x][y] = EL_BUG;
4630 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4631 break;
4632
4633 case EL_SPACESHIP_RIGHT:
4634 case EL_SPACESHIP_UP:
4635 case EL_SPACESHIP_LEFT:
4636 case EL_SPACESHIP_DOWN:
4637 Feld[x][y] = EL_SPACESHIP;
4638 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4639 break;
4640
4641 case EL_BD_BUTTERFLY_RIGHT:
4642 case EL_BD_BUTTERFLY_UP:
4643 case EL_BD_BUTTERFLY_LEFT:
4644 case EL_BD_BUTTERFLY_DOWN:
4645 Feld[x][y] = EL_BD_BUTTERFLY;
4646 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4647 break;
4648
4649 case EL_BD_FIREFLY_RIGHT:
4650 case EL_BD_FIREFLY_UP:
4651 case EL_BD_FIREFLY_LEFT:
4652 case EL_BD_FIREFLY_DOWN:
4653 Feld[x][y] = EL_BD_FIREFLY;
4654 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4655 break;
4656
4657 case EL_PACMAN_RIGHT:
4658 case EL_PACMAN_UP:
4659 case EL_PACMAN_LEFT:
4660 case EL_PACMAN_DOWN:
4661 Feld[x][y] = EL_PACMAN;
4662 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4663 break;
4664
4665 case EL_YAMYAM_LEFT:
4666 case EL_YAMYAM_RIGHT:
4667 case EL_YAMYAM_UP:
4668 case EL_YAMYAM_DOWN:
4669 Feld[x][y] = EL_YAMYAM;
4670 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4671 break;
4672
4673 case EL_SP_SNIKSNAK:
4674 MovDir[x][y] = MV_UP;
4675 break;
4676
4677 case EL_SP_ELECTRON:
4678 MovDir[x][y] = MV_LEFT;
4679 break;
4680
4681 case EL_MOLE_LEFT:
4682 case EL_MOLE_RIGHT:
4683 case EL_MOLE_UP:
4684 case EL_MOLE_DOWN:
4685 Feld[x][y] = EL_MOLE;
4686 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4687 break;
4688
4689 default:
4690 if (IS_CUSTOM_ELEMENT(element))
4691 {
4692 struct ElementInfo *ei = &element_info[element];
4693 int move_direction_initial = ei->move_direction_initial;
4694 int move_pattern = ei->move_pattern;
4695
4696 if (move_direction_initial == MV_START_PREVIOUS)
4697 {
4698 if (MovDir[x][y] != MV_NONE)
4699 return;
4700
4701 move_direction_initial = MV_START_AUTOMATIC;
4702 }
4703
4704 if (move_direction_initial == MV_START_RANDOM)
4705 MovDir[x][y] = 1 << RND(4);
4706 else if (move_direction_initial & MV_ANY_DIRECTION)
4707 MovDir[x][y] = move_direction_initial;
4708 else if (move_pattern == MV_ALL_DIRECTIONS ||
4709 move_pattern == MV_TURNING_LEFT ||
4710 move_pattern == MV_TURNING_RIGHT ||
4711 move_pattern == MV_TURNING_LEFT_RIGHT ||
4712 move_pattern == MV_TURNING_RIGHT_LEFT ||
4713 move_pattern == MV_TURNING_RANDOM)
4714 MovDir[x][y] = 1 << RND(4);
4715 else if (move_pattern == MV_HORIZONTAL)
4716 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4717 else if (move_pattern == MV_VERTICAL)
4718 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4719 else if (move_pattern & MV_ANY_DIRECTION)
4720 MovDir[x][y] = element_info[element].move_pattern;
4721 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4722 move_pattern == MV_ALONG_RIGHT_SIDE)
4723 {
4724 /* use random direction as default start direction */
4725 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4726 MovDir[x][y] = 1 << RND(4);
4727
4728 for (i = 0; i < NUM_DIRECTIONS; i++)
4729 {
4730 int x1 = x + xy[i][0];
4731 int y1 = y + xy[i][1];
4732
4733 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4734 {
4735 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4736 MovDir[x][y] = direction[0][i];
4737 else
4738 MovDir[x][y] = direction[1][i];
4739
4740 break;
4741 }
4742 }
4743 }
4744 }
4745 else
4746 {
4747 MovDir[x][y] = 1 << RND(4);
4748
4749 if (element != EL_BUG &&
4750 element != EL_SPACESHIP &&
4751 element != EL_BD_BUTTERFLY &&
4752 element != EL_BD_FIREFLY)
4753 break;
4754
4755 for (i = 0; i < NUM_DIRECTIONS; i++)
4756 {
4757 int x1 = x + xy[i][0];
4758 int y1 = y + xy[i][1];
4759
4760 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4761 {
4762 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4763 {
4764 MovDir[x][y] = direction[0][i];
4765 break;
4766 }
4767 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4768 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4769 {
4770 MovDir[x][y] = direction[1][i];
4771 break;
4772 }
4773 }
4774 }
4775 }
4776 break;
4777 }
4778
4779 GfxDir[x][y] = MovDir[x][y];
4780 }
4781
InitAmoebaNr(int x,int y)4782 void InitAmoebaNr(int x, int y)
4783 {
4784 int i;
4785 int group_nr = AmoebeNachbarNr(x, y);
4786
4787 if (group_nr == 0)
4788 {
4789 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4790 {
4791 if (AmoebaCnt[i] == 0)
4792 {
4793 group_nr = i;
4794 break;
4795 }
4796 }
4797 }
4798
4799 AmoebaNr[x][y] = group_nr;
4800 AmoebaCnt[group_nr]++;
4801 AmoebaCnt2[group_nr]++;
4802 }
4803
PlayerWins(struct PlayerInfo * player)4804 static void PlayerWins(struct PlayerInfo *player)
4805 {
4806 player->LevelSolved = TRUE;
4807 player->GameOver = TRUE;
4808
4809 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4810 level.native_em_level->lev->score : player->score);
4811
4812 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4813 player->LevelSolved_CountingScore = player->score_final;
4814 }
4815
GameWon()4816 void GameWon()
4817 {
4818 static int time, time_final;
4819 static int score, score_final;
4820 static int game_over_delay_1 = 0;
4821 static int game_over_delay_2 = 0;
4822 int game_over_delay_value_1 = 50;
4823 int game_over_delay_value_2 = 50;
4824
4825 if (!local_player->LevelSolved_GameWon)
4826 {
4827 int i;
4828
4829 /* do not start end game actions before the player stops moving (to exit) */
4830 if (local_player->MovPos)
4831 return;
4832
4833 local_player->LevelSolved_GameWon = TRUE;
4834 local_player->LevelSolved_SaveTape = tape.recording;
4835 local_player->LevelSolved_SaveScore = !tape.playing;
4836
4837 if (tape.auto_play) /* tape might already be stopped here */
4838 tape.auto_play_level_solved = TRUE;
4839
4840 #if 1
4841 TapeStop();
4842 #endif
4843
4844 game_over_delay_1 = game_over_delay_value_1;
4845 game_over_delay_2 = game_over_delay_value_2;
4846
4847 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4848 score = score_final = local_player->score_final;
4849
4850 if (TimeLeft > 0)
4851 {
4852 time_final = 0;
4853 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4854 }
4855 else if (level.time == 0 && TimePlayed < 999)
4856 {
4857 time_final = 999;
4858 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4859 }
4860
4861 local_player->score_final = score_final;
4862
4863 if (level_editor_test_game)
4864 {
4865 time = time_final;
4866 score = score_final;
4867
4868 #if 1
4869 local_player->LevelSolved_CountingTime = time;
4870 local_player->LevelSolved_CountingScore = score;
4871
4872 game_panel_controls[GAME_PANEL_TIME].value = time;
4873 game_panel_controls[GAME_PANEL_SCORE].value = score;
4874
4875 DisplayGameControlValues();
4876 #else
4877 DrawGameValue_Time(time);
4878 DrawGameValue_Score(score);
4879 #endif
4880 }
4881
4882 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4883 {
4884 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4885 {
4886 /* close exit door after last player */
4887 if ((AllPlayersGone &&
4888 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4889 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4890 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4891 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4892 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4893 {
4894 int element = Feld[ExitX][ExitY];
4895
4896 #if 0
4897 if (element == EL_EM_EXIT_OPEN ||
4898 element == EL_EM_STEEL_EXIT_OPEN)
4899 {
4900 Bang(ExitX, ExitY);
4901 }
4902 else
4903 #endif
4904 {
4905 Feld[ExitX][ExitY] =
4906 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4907 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4908 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4909 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4910 EL_EM_STEEL_EXIT_CLOSING);
4911
4912 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4913 }
4914 }
4915
4916 /* player disappears */
4917 DrawLevelField(ExitX, ExitY);
4918 }
4919
4920 for (i = 0; i < MAX_PLAYERS; i++)
4921 {
4922 struct PlayerInfo *player = &stored_player[i];
4923
4924 if (player->present)
4925 {
4926 RemovePlayer(player);
4927
4928 /* player disappears */
4929 DrawLevelField(player->jx, player->jy);
4930 }
4931 }
4932 }
4933
4934 PlaySound(SND_GAME_WINNING);
4935 }
4936
4937 if (game_over_delay_1 > 0)
4938 {
4939 game_over_delay_1--;
4940
4941 return;
4942 }
4943
4944 if (time != time_final)
4945 {
4946 int time_to_go = ABS(time_final - time);
4947 int time_count_dir = (time < time_final ? +1 : -1);
4948 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4949
4950 time += time_count_steps * time_count_dir;
4951 score += time_count_steps * level.score[SC_TIME_BONUS];
4952
4953 #if 1
4954 local_player->LevelSolved_CountingTime = time;
4955 local_player->LevelSolved_CountingScore = score;
4956
4957 game_panel_controls[GAME_PANEL_TIME].value = time;
4958 game_panel_controls[GAME_PANEL_SCORE].value = score;
4959
4960 DisplayGameControlValues();
4961 #else
4962 DrawGameValue_Time(time);
4963 DrawGameValue_Score(score);
4964 #endif
4965
4966 if (time == time_final)
4967 StopSound(SND_GAME_LEVELTIME_BONUS);
4968 else if (setup.sound_loops)
4969 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4970 else
4971 PlaySound(SND_GAME_LEVELTIME_BONUS);
4972
4973 return;
4974 }
4975
4976 local_player->LevelSolved_PanelOff = TRUE;
4977
4978 if (game_over_delay_2 > 0)
4979 {
4980 game_over_delay_2--;
4981
4982 return;
4983 }
4984
4985 #if 1
4986 GameEnd();
4987 #endif
4988 }
4989
GameEnd()4990 void GameEnd()
4991 {
4992 int hi_pos;
4993 boolean raise_level = FALSE;
4994
4995 local_player->LevelSolved_GameEnd = TRUE;
4996
4997 CloseDoor(DOOR_CLOSE_1);
4998
4999 if (local_player->LevelSolved_SaveTape)
5000 {
5001 #if 0
5002 TapeStop();
5003 #endif
5004
5005 #if 1
5006 SaveTapeChecked(tape.level_nr); /* ask to save tape */
5007 #else
5008 SaveTape(tape.level_nr); /* ask to save tape */
5009 #endif
5010 }
5011
5012 if (level_editor_test_game)
5013 {
5014 game_status = GAME_MODE_MAIN;
5015
5016 #if 1
5017 DrawAndFadeInMainMenu(REDRAW_FIELD);
5018 #else
5019 DrawMainMenu();
5020 #endif
5021
5022 return;
5023 }
5024
5025 if (!local_player->LevelSolved_SaveScore)
5026 {
5027 #if 1
5028 FadeOut(REDRAW_FIELD);
5029 #endif
5030
5031 game_status = GAME_MODE_MAIN;
5032
5033 DrawAndFadeInMainMenu(REDRAW_FIELD);
5034
5035 return;
5036 }
5037
5038 if (level_nr == leveldir_current->handicap_level)
5039 {
5040 leveldir_current->handicap_level++;
5041 SaveLevelSetup_SeriesInfo();
5042 }
5043
5044 if (level_nr < leveldir_current->last_level)
5045 raise_level = TRUE; /* advance to next level */
5046
5047 if ((hi_pos = NewHiScore()) >= 0)
5048 {
5049 game_status = GAME_MODE_SCORES;
5050
5051 DrawHallOfFame(hi_pos);
5052
5053 if (raise_level)
5054 {
5055 level_nr++;
5056 TapeErase();
5057 }
5058 }
5059 else
5060 {
5061 #if 1
5062 FadeOut(REDRAW_FIELD);
5063 #endif
5064
5065 game_status = GAME_MODE_MAIN;
5066
5067 if (raise_level)
5068 {
5069 level_nr++;
5070 TapeErase();
5071 }
5072
5073 DrawAndFadeInMainMenu(REDRAW_FIELD);
5074 }
5075 }
5076
NewHiScore()5077 int NewHiScore()
5078 {
5079 int k, l;
5080 int position = -1;
5081
5082 LoadScore(level_nr);
5083
5084 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5085 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5086 return -1;
5087
5088 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5089 {
5090 if (local_player->score_final > highscore[k].Score)
5091 {
5092 /* player has made it to the hall of fame */
5093
5094 if (k < MAX_SCORE_ENTRIES - 1)
5095 {
5096 int m = MAX_SCORE_ENTRIES - 1;
5097
5098 #ifdef ONE_PER_NAME
5099 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5100 if (strEqual(setup.player_name, highscore[l].Name))
5101 m = l;
5102 if (m == k) /* player's new highscore overwrites his old one */
5103 goto put_into_list;
5104 #endif
5105
5106 for (l = m; l > k; l--)
5107 {
5108 strcpy(highscore[l].Name, highscore[l - 1].Name);
5109 highscore[l].Score = highscore[l - 1].Score;
5110 }
5111 }
5112
5113 #ifdef ONE_PER_NAME
5114 put_into_list:
5115 #endif
5116 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5117 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5118 highscore[k].Score = local_player->score_final;
5119 position = k;
5120 break;
5121 }
5122
5123 #ifdef ONE_PER_NAME
5124 else if (!strncmp(setup.player_name, highscore[k].Name,
5125 MAX_PLAYER_NAME_LEN))
5126 break; /* player already there with a higher score */
5127 #endif
5128
5129 }
5130
5131 if (position >= 0)
5132 SaveScore(level_nr);
5133
5134 return position;
5135 }
5136
getElementMoveStepsizeExt(int x,int y,int direction)5137 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5138 {
5139 int element = Feld[x][y];
5140 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5141 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5142 int horiz_move = (dx != 0);
5143 int sign = (horiz_move ? dx : dy);
5144 int step = sign * element_info[element].move_stepsize;
5145
5146 /* special values for move stepsize for spring and things on conveyor belt */
5147 if (horiz_move)
5148 {
5149 if (CAN_FALL(element) &&
5150 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5151 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5152 else if (element == EL_SPRING)
5153 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5154 }
5155
5156 return step;
5157 }
5158
getElementMoveStepsize(int x,int y)5159 inline static int getElementMoveStepsize(int x, int y)
5160 {
5161 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5162 }
5163
InitPlayerGfxAnimation(struct PlayerInfo * player,int action,int dir)5164 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5165 {
5166 if (player->GfxAction != action || player->GfxDir != dir)
5167 {
5168 #if 0
5169 printf("Player frame reset! (%d => %d, %d => %d)\n",
5170 player->GfxAction, action, player->GfxDir, dir);
5171 #endif
5172
5173 player->GfxAction = action;
5174 player->GfxDir = dir;
5175 player->Frame = 0;
5176 player->StepFrame = 0;
5177 }
5178 }
5179
5180 #if USE_GFX_RESET_GFX_ANIMATION
ResetGfxFrame(int x,int y,boolean redraw)5181 static void ResetGfxFrame(int x, int y, boolean redraw)
5182 {
5183 int element = Feld[x][y];
5184 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5185 int last_gfx_frame = GfxFrame[x][y];
5186
5187 if (graphic_info[graphic].anim_global_sync)
5188 GfxFrame[x][y] = FrameCounter;
5189 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5190 GfxFrame[x][y] = CustomValue[x][y];
5191 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5192 GfxFrame[x][y] = element_info[element].collect_score;
5193 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5194 GfxFrame[x][y] = ChangeDelay[x][y];
5195
5196 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5197 DrawLevelGraphicAnimation(x, y, graphic);
5198 }
5199 #endif
5200
ResetGfxAnimation(int x,int y)5201 static void ResetGfxAnimation(int x, int y)
5202 {
5203 GfxAction[x][y] = ACTION_DEFAULT;
5204 GfxDir[x][y] = MovDir[x][y];
5205 GfxFrame[x][y] = 0;
5206
5207 #if USE_GFX_RESET_GFX_ANIMATION
5208 ResetGfxFrame(x, y, FALSE);
5209 #endif
5210 }
5211
ResetRandomAnimationValue(int x,int y)5212 static void ResetRandomAnimationValue(int x, int y)
5213 {
5214 GfxRandom[x][y] = INIT_GFX_RANDOM();
5215 }
5216
InitMovingField(int x,int y,int direction)5217 void InitMovingField(int x, int y, int direction)
5218 {
5219 int element = Feld[x][y];
5220 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5221 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5222 int newx = x + dx;
5223 int newy = y + dy;
5224 boolean is_moving_before, is_moving_after;
5225 #if 0
5226 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5227 #endif
5228
5229 /* check if element was/is moving or being moved before/after mode change */
5230 #if 1
5231 #if 1
5232 is_moving_before = (WasJustMoving[x][y] != 0);
5233 #else
5234 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5235 is_moving_before = WasJustMoving[x][y];
5236 #endif
5237 #else
5238 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5239 #endif
5240 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5241
5242 /* reset animation only for moving elements which change direction of moving
5243 or which just started or stopped moving
5244 (else CEs with property "can move" / "not moving" are reset each frame) */
5245 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5246 #if 1
5247 if (is_moving_before != is_moving_after ||
5248 direction != MovDir[x][y])
5249 ResetGfxAnimation(x, y);
5250 #else
5251 if ((is_moving_before || is_moving_after) && !continues_moving)
5252 ResetGfxAnimation(x, y);
5253 #endif
5254 #else
5255 if (!continues_moving)
5256 ResetGfxAnimation(x, y);
5257 #endif
5258
5259 MovDir[x][y] = direction;
5260 GfxDir[x][y] = direction;
5261
5262 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5263 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5264 direction == MV_DOWN && CAN_FALL(element) ?
5265 ACTION_FALLING : ACTION_MOVING);
5266 #else
5267 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5268 ACTION_FALLING : ACTION_MOVING);
5269 #endif
5270
5271 /* this is needed for CEs with property "can move" / "not moving" */
5272
5273 if (is_moving_after)
5274 {
5275 if (Feld[newx][newy] == EL_EMPTY)
5276 Feld[newx][newy] = EL_BLOCKED;
5277
5278 MovDir[newx][newy] = MovDir[x][y];
5279
5280 #if USE_NEW_CUSTOM_VALUE
5281 CustomValue[newx][newy] = CustomValue[x][y];
5282 #endif
5283
5284 GfxFrame[newx][newy] = GfxFrame[x][y];
5285 GfxRandom[newx][newy] = GfxRandom[x][y];
5286 GfxAction[newx][newy] = GfxAction[x][y];
5287 GfxDir[newx][newy] = GfxDir[x][y];
5288 }
5289 }
5290
Moving2Blocked(int x,int y,int * goes_to_x,int * goes_to_y)5291 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5292 {
5293 int direction = MovDir[x][y];
5294 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5295 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5296
5297 *goes_to_x = newx;
5298 *goes_to_y = newy;
5299 }
5300
Blocked2Moving(int x,int y,int * comes_from_x,int * comes_from_y)5301 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5302 {
5303 int oldx = x, oldy = y;
5304 int direction = MovDir[x][y];
5305
5306 if (direction == MV_LEFT)
5307 oldx++;
5308 else if (direction == MV_RIGHT)
5309 oldx--;
5310 else if (direction == MV_UP)
5311 oldy++;
5312 else if (direction == MV_DOWN)
5313 oldy--;
5314
5315 *comes_from_x = oldx;
5316 *comes_from_y = oldy;
5317 }
5318
MovingOrBlocked2Element(int x,int y)5319 int MovingOrBlocked2Element(int x, int y)
5320 {
5321 int element = Feld[x][y];
5322
5323 if (element == EL_BLOCKED)
5324 {
5325 int oldx, oldy;
5326
5327 Blocked2Moving(x, y, &oldx, &oldy);
5328 return Feld[oldx][oldy];
5329 }
5330 else
5331 return element;
5332 }
5333
MovingOrBlocked2ElementIfNotLeaving(int x,int y)5334 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5335 {
5336 /* like MovingOrBlocked2Element(), but if element is moving
5337 and (x,y) is the field the moving element is just leaving,
5338 return EL_BLOCKED instead of the element value */
5339 int element = Feld[x][y];
5340
5341 if (IS_MOVING(x, y))
5342 {
5343 if (element == EL_BLOCKED)
5344 {
5345 int oldx, oldy;
5346
5347 Blocked2Moving(x, y, &oldx, &oldy);
5348 return Feld[oldx][oldy];
5349 }
5350 else
5351 return EL_BLOCKED;
5352 }
5353 else
5354 return element;
5355 }
5356
RemoveField(int x,int y)5357 static void RemoveField(int x, int y)
5358 {
5359 Feld[x][y] = EL_EMPTY;
5360
5361 MovPos[x][y] = 0;
5362 MovDir[x][y] = 0;
5363 MovDelay[x][y] = 0;
5364
5365 #if USE_NEW_CUSTOM_VALUE
5366 CustomValue[x][y] = 0;
5367 #endif
5368
5369 AmoebaNr[x][y] = 0;
5370 ChangeDelay[x][y] = 0;
5371 ChangePage[x][y] = -1;
5372 Pushed[x][y] = FALSE;
5373
5374 #if 0
5375 ExplodeField[x][y] = EX_TYPE_NONE;
5376 #endif
5377
5378 GfxElement[x][y] = EL_UNDEFINED;
5379 GfxAction[x][y] = ACTION_DEFAULT;
5380 GfxDir[x][y] = MV_NONE;
5381 #if 0
5382 /* !!! this would prevent the removed tile from being redrawn !!! */
5383 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5384 #endif
5385 }
5386
RemoveMovingField(int x,int y)5387 void RemoveMovingField(int x, int y)
5388 {
5389 int oldx = x, oldy = y, newx = x, newy = y;
5390 int element = Feld[x][y];
5391 int next_element = EL_UNDEFINED;
5392
5393 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5394 return;
5395
5396 if (IS_MOVING(x, y))
5397 {
5398 Moving2Blocked(x, y, &newx, &newy);
5399
5400 if (Feld[newx][newy] != EL_BLOCKED)
5401 {
5402 /* element is moving, but target field is not free (blocked), but
5403 already occupied by something different (example: acid pool);
5404 in this case, only remove the moving field, but not the target */
5405
5406 RemoveField(oldx, oldy);
5407
5408 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5409
5410 TEST_DrawLevelField(oldx, oldy);
5411
5412 return;
5413 }
5414 }
5415 else if (element == EL_BLOCKED)
5416 {
5417 Blocked2Moving(x, y, &oldx, &oldy);
5418 if (!IS_MOVING(oldx, oldy))
5419 return;
5420 }
5421
5422 if (element == EL_BLOCKED &&
5423 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5424 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5425 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5426 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5427 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5428 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5429 next_element = get_next_element(Feld[oldx][oldy]);
5430
5431 RemoveField(oldx, oldy);
5432 RemoveField(newx, newy);
5433
5434 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5435
5436 if (next_element != EL_UNDEFINED)
5437 Feld[oldx][oldy] = next_element;
5438
5439 TEST_DrawLevelField(oldx, oldy);
5440 TEST_DrawLevelField(newx, newy);
5441 }
5442
DrawDynamite(int x,int y)5443 void DrawDynamite(int x, int y)
5444 {
5445 int sx = SCREENX(x), sy = SCREENY(y);
5446 int graphic = el2img(Feld[x][y]);
5447 int frame;
5448
5449 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5450 return;
5451
5452 if (IS_WALKABLE_INSIDE(Back[x][y]))
5453 return;
5454
5455 if (Back[x][y])
5456 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5457 else if (Store[x][y])
5458 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5459
5460 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5461
5462 if (Back[x][y] || Store[x][y])
5463 DrawGraphicThruMask(sx, sy, graphic, frame);
5464 else
5465 DrawGraphic(sx, sy, graphic, frame);
5466 }
5467
CheckDynamite(int x,int y)5468 void CheckDynamite(int x, int y)
5469 {
5470 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5471 {
5472 MovDelay[x][y]--;
5473
5474 if (MovDelay[x][y] != 0)
5475 {
5476 DrawDynamite(x, y);
5477 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5478
5479 return;
5480 }
5481 }
5482
5483 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5484
5485 Bang(x, y);
5486 }
5487
setMinimalPlayerBoundaries(int * sx1,int * sy1,int * sx2,int * sy2)5488 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5489 {
5490 boolean num_checked_players = 0;
5491 int i;
5492
5493 for (i = 0; i < MAX_PLAYERS; i++)
5494 {
5495 if (stored_player[i].active)
5496 {
5497 int sx = stored_player[i].jx;
5498 int sy = stored_player[i].jy;
5499
5500 if (num_checked_players == 0)
5501 {
5502 *sx1 = *sx2 = sx;
5503 *sy1 = *sy2 = sy;
5504 }
5505 else
5506 {
5507 *sx1 = MIN(*sx1, sx);
5508 *sy1 = MIN(*sy1, sy);
5509 *sx2 = MAX(*sx2, sx);
5510 *sy2 = MAX(*sy2, sy);
5511 }
5512
5513 num_checked_players++;
5514 }
5515 }
5516 }
5517
checkIfAllPlayersFitToScreen_RND()5518 static boolean checkIfAllPlayersFitToScreen_RND()
5519 {
5520 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5521
5522 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5523
5524 return (sx2 - sx1 < SCR_FIELDX &&
5525 sy2 - sy1 < SCR_FIELDY);
5526 }
5527
setScreenCenteredToAllPlayers(int * sx,int * sy)5528 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5529 {
5530 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5531
5532 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5533
5534 *sx = (sx1 + sx2) / 2;
5535 *sy = (sy1 + sy2) / 2;
5536 }
5537
DrawRelocateScreen(int old_x,int old_y,int x,int y,int move_dir,boolean center_screen,boolean quick_relocation)5538 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5539 boolean center_screen, boolean quick_relocation)
5540 {
5541 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5542 boolean no_delay = (tape.warp_forward);
5543 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5544 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5545
5546 if (quick_relocation)
5547 {
5548 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5549 {
5550 if (!level.shifted_relocation || center_screen)
5551 {
5552 /* quick relocation (without scrolling), with centering of screen */
5553
5554 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5555 x > SBX_Right + MIDPOSX ? SBX_Right :
5556 x - MIDPOSX);
5557
5558 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5559 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5560 y - MIDPOSY);
5561 }
5562 else
5563 {
5564 /* quick relocation (without scrolling), but do not center screen */
5565
5566 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5567 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5568 old_x - MIDPOSX);
5569
5570 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5571 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5572 old_y - MIDPOSY);
5573
5574 int offset_x = x + (scroll_x - center_scroll_x);
5575 int offset_y = y + (scroll_y - center_scroll_y);
5576
5577 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5578 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5579 offset_x - MIDPOSX);
5580
5581 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5582 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5583 offset_y - MIDPOSY);
5584 }
5585 }
5586 else
5587 {
5588 #if 1
5589 if (!level.shifted_relocation || center_screen)
5590 {
5591 /* quick relocation (without scrolling), with centering of screen */
5592
5593 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5594 x > SBX_Right + MIDPOSX ? SBX_Right :
5595 x - MIDPOSX);
5596
5597 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5598 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5599 y - MIDPOSY);
5600 }
5601 else
5602 {
5603 /* quick relocation (without scrolling), but do not center screen */
5604
5605 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5606 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5607 old_x - MIDPOSX);
5608
5609 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5610 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5611 old_y - MIDPOSY);
5612
5613 int offset_x = x + (scroll_x - center_scroll_x);
5614 int offset_y = y + (scroll_y - center_scroll_y);
5615
5616 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5617 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5618 offset_x - MIDPOSX);
5619
5620 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5621 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5622 offset_y - MIDPOSY);
5623 }
5624 #else
5625 /* quick relocation (without scrolling), inside visible screen area */
5626
5627 int offset = game.scroll_delay_value;
5628
5629 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5630 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5631 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5632
5633 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5634 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5635 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5636
5637 /* don't scroll over playfield boundaries */
5638 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5639 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5640
5641 /* don't scroll over playfield boundaries */
5642 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5643 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5644 #endif
5645 }
5646
5647 RedrawPlayfield(TRUE, 0,0,0,0);
5648 }
5649 else
5650 {
5651 #if 1
5652 int scroll_xx, scroll_yy;
5653
5654 if (!level.shifted_relocation || center_screen)
5655 {
5656 /* visible relocation (with scrolling), with centering of screen */
5657
5658 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5659 x > SBX_Right + MIDPOSX ? SBX_Right :
5660 x - MIDPOSX);
5661
5662 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5663 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5664 y - MIDPOSY);
5665 }
5666 else
5667 {
5668 /* visible relocation (with scrolling), but do not center screen */
5669
5670 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5671 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5672 old_x - MIDPOSX);
5673
5674 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5675 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5676 old_y - MIDPOSY);
5677
5678 int offset_x = x + (scroll_x - center_scroll_x);
5679 int offset_y = y + (scroll_y - center_scroll_y);
5680
5681 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5682 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5683 offset_x - MIDPOSX);
5684
5685 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5686 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5687 offset_y - MIDPOSY);
5688 }
5689
5690 #else
5691
5692 /* visible relocation (with scrolling), with centering of screen */
5693
5694 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5695 x > SBX_Right + MIDPOSX ? SBX_Right :
5696 x - MIDPOSX);
5697
5698 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5699 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5700 y - MIDPOSY);
5701 #endif
5702
5703 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5704
5705 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5706 {
5707 int dx = 0, dy = 0;
5708 int fx = FX, fy = FY;
5709
5710 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5711 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5712
5713 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5714 break;
5715
5716 scroll_x -= dx;
5717 scroll_y -= dy;
5718
5719 fx += dx * TILEX / 2;
5720 fy += dy * TILEY / 2;
5721
5722 ScrollLevel(dx, dy);
5723 DrawAllPlayers();
5724
5725 /* scroll in two steps of half tile size to make things smoother */
5726 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5727 FlushDisplay();
5728 Delay(wait_delay_value);
5729
5730 /* scroll second step to align at full tile size */
5731 BackToFront();
5732 Delay(wait_delay_value);
5733 }
5734
5735 DrawAllPlayers();
5736 BackToFront();
5737 Delay(wait_delay_value);
5738 }
5739 }
5740
RelocatePlayer(int jx,int jy,int el_player_raw)5741 void RelocatePlayer(int jx, int jy, int el_player_raw)
5742 {
5743 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5744 int player_nr = GET_PLAYER_NR(el_player);
5745 struct PlayerInfo *player = &stored_player[player_nr];
5746 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5747 boolean no_delay = (tape.warp_forward);
5748 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5749 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5750 int old_jx = player->jx;
5751 int old_jy = player->jy;
5752 int old_element = Feld[old_jx][old_jy];
5753 int element = Feld[jx][jy];
5754 boolean player_relocated = (old_jx != jx || old_jy != jy);
5755
5756 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5757 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5758 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5759 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5760 int leave_side_horiz = move_dir_horiz;
5761 int leave_side_vert = move_dir_vert;
5762 int enter_side = enter_side_horiz | enter_side_vert;
5763 int leave_side = leave_side_horiz | leave_side_vert;
5764
5765 if (player->GameOver) /* do not reanimate dead player */
5766 return;
5767
5768 if (!player_relocated) /* no need to relocate the player */
5769 return;
5770
5771 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5772 {
5773 RemoveField(jx, jy); /* temporarily remove newly placed player */
5774 DrawLevelField(jx, jy);
5775 }
5776
5777 if (player->present)
5778 {
5779 while (player->MovPos)
5780 {
5781 ScrollPlayer(player, SCROLL_GO_ON);
5782 ScrollScreen(NULL, SCROLL_GO_ON);
5783
5784 AdvanceFrameAndPlayerCounters(player->index_nr);
5785
5786 DrawPlayer(player);
5787
5788 BackToFront();
5789 Delay(wait_delay_value);
5790 }
5791
5792 DrawPlayer(player); /* needed here only to cleanup last field */
5793 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5794
5795 player->is_moving = FALSE;
5796 }
5797
5798 if (IS_CUSTOM_ELEMENT(old_element))
5799 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5800 CE_LEFT_BY_PLAYER,
5801 player->index_bit, leave_side);
5802
5803 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5804 CE_PLAYER_LEAVES_X,
5805 player->index_bit, leave_side);
5806
5807 Feld[jx][jy] = el_player;
5808 InitPlayerField(jx, jy, el_player, TRUE);
5809
5810 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5811 possible that the relocation target field did not contain a player element,
5812 but a walkable element, to which the new player was relocated -- in this
5813 case, restore that (already initialized!) element on the player field */
5814 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5815 {
5816 Feld[jx][jy] = element; /* restore previously existing element */
5817 #if 0
5818 /* !!! do not initialize already initialized element a second time !!! */
5819 /* (this causes at least problems with "element creation" CE trigger for
5820 already existing elements, and existing Sokoban fields counted twice) */
5821 InitField(jx, jy, FALSE);
5822 #endif
5823 }
5824
5825 /* only visually relocate centered player */
5826 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5827 FALSE, level.instant_relocation);
5828
5829 TestIfPlayerTouchesBadThing(jx, jy);
5830 TestIfPlayerTouchesCustomElement(jx, jy);
5831
5832 if (IS_CUSTOM_ELEMENT(element))
5833 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5834 player->index_bit, enter_side);
5835
5836 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5837 player->index_bit, enter_side);
5838
5839 #if 1
5840 if (player->is_switching)
5841 {
5842 /* ensure that relocation while still switching an element does not cause
5843 a new element to be treated as also switched directly after relocation
5844 (this is important for teleporter switches that teleport the player to
5845 a place where another teleporter switch is in the same direction, which
5846 would then incorrectly be treated as immediately switched before the
5847 direction key that caused the switch was released) */
5848
5849 player->switch_x += jx - old_jx;
5850 player->switch_y += jy - old_jy;
5851 }
5852 #endif
5853 }
5854
Explode(int ex,int ey,int phase,int mode)5855 void Explode(int ex, int ey, int phase, int mode)
5856 {
5857 int x, y;
5858 int last_phase;
5859 int border_element;
5860
5861 /* !!! eliminate this variable !!! */
5862 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5863
5864 if (game.explosions_delayed)
5865 {
5866 ExplodeField[ex][ey] = mode;
5867 return;
5868 }
5869
5870 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5871 {
5872 int center_element = Feld[ex][ey];
5873 int artwork_element, explosion_element; /* set these values later */
5874
5875 #if 0
5876 /* --- This is only really needed (and now handled) in "Impact()". --- */
5877 /* do not explode moving elements that left the explode field in time */
5878 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5879 center_element == EL_EMPTY &&
5880 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5881 return;
5882 #endif
5883
5884 #if 0
5885 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5886 if (mode == EX_TYPE_NORMAL ||
5887 mode == EX_TYPE_CENTER ||
5888 mode == EX_TYPE_CROSS)
5889 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5890 #endif
5891
5892 /* remove things displayed in background while burning dynamite */
5893 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5894 Back[ex][ey] = 0;
5895
5896 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5897 {
5898 /* put moving element to center field (and let it explode there) */
5899 center_element = MovingOrBlocked2Element(ex, ey);
5900 RemoveMovingField(ex, ey);
5901 Feld[ex][ey] = center_element;
5902 }
5903
5904 /* now "center_element" is finally determined -- set related values now */
5905 artwork_element = center_element; /* for custom player artwork */
5906 explosion_element = center_element; /* for custom player artwork */
5907
5908 if (IS_PLAYER(ex, ey))
5909 {
5910 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5911
5912 artwork_element = stored_player[player_nr].artwork_element;
5913
5914 if (level.use_explosion_element[player_nr])
5915 {
5916 explosion_element = level.explosion_element[player_nr];
5917 artwork_element = explosion_element;
5918 }
5919 }
5920
5921 #if 1
5922 if (mode == EX_TYPE_NORMAL ||
5923 mode == EX_TYPE_CENTER ||
5924 mode == EX_TYPE_CROSS)
5925 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5926 #endif
5927
5928 last_phase = element_info[explosion_element].explosion_delay + 1;
5929
5930 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5931 {
5932 int xx = x - ex + 1;
5933 int yy = y - ey + 1;
5934 int element;
5935
5936 if (!IN_LEV_FIELD(x, y) ||
5937 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5938 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5939 continue;
5940
5941 element = Feld[x][y];
5942
5943 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5944 {
5945 element = MovingOrBlocked2Element(x, y);
5946
5947 if (!IS_EXPLOSION_PROOF(element))
5948 RemoveMovingField(x, y);
5949 }
5950
5951 /* indestructible elements can only explode in center (but not flames) */
5952 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5953 mode == EX_TYPE_BORDER)) ||
5954 element == EL_FLAMES)
5955 continue;
5956
5957 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5958 behaviour, for example when touching a yamyam that explodes to rocks
5959 with active deadly shield, a rock is created under the player !!! */
5960 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5961 #if 0
5962 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5963 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5964 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5965 #else
5966 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5967 #endif
5968 {
5969 if (IS_ACTIVE_BOMB(element))
5970 {
5971 /* re-activate things under the bomb like gate or penguin */
5972 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5973 Back[x][y] = 0;
5974 }
5975
5976 continue;
5977 }
5978
5979 /* save walkable background elements while explosion on same tile */
5980 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5981 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5982 Back[x][y] = element;
5983
5984 /* ignite explodable elements reached by other explosion */
5985 if (element == EL_EXPLOSION)
5986 element = Store2[x][y];
5987
5988 if (AmoebaNr[x][y] &&
5989 (element == EL_AMOEBA_FULL ||
5990 element == EL_BD_AMOEBA ||
5991 element == EL_AMOEBA_GROWING))
5992 {
5993 AmoebaCnt[AmoebaNr[x][y]]--;
5994 AmoebaCnt2[AmoebaNr[x][y]]--;
5995 }
5996
5997 RemoveField(x, y);
5998
5999 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6000 {
6001 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6002
6003 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6004
6005 if (PLAYERINFO(ex, ey)->use_murphy)
6006 Store[x][y] = EL_EMPTY;
6007 }
6008
6009 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6010 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6011 else if (ELEM_IS_PLAYER(center_element))
6012 Store[x][y] = EL_EMPTY;
6013 else if (center_element == EL_YAMYAM)
6014 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6015 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6016 Store[x][y] = element_info[center_element].content.e[xx][yy];
6017 #if 1
6018 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6019 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6020 otherwise) -- FIX THIS !!! */
6021 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6022 Store[x][y] = element_info[element].content.e[1][1];
6023 #else
6024 else if (!CAN_EXPLODE(element))
6025 Store[x][y] = element_info[element].content.e[1][1];
6026 #endif
6027 else
6028 Store[x][y] = EL_EMPTY;
6029
6030 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6031 center_element == EL_AMOEBA_TO_DIAMOND)
6032 Store2[x][y] = element;
6033
6034 Feld[x][y] = EL_EXPLOSION;
6035 GfxElement[x][y] = artwork_element;
6036
6037 ExplodePhase[x][y] = 1;
6038 ExplodeDelay[x][y] = last_phase;
6039
6040 Stop[x][y] = TRUE;
6041 }
6042
6043 if (center_element == EL_YAMYAM)
6044 game.yamyam_content_nr =
6045 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6046
6047 return;
6048 }
6049
6050 if (Stop[ex][ey])
6051 return;
6052
6053 x = ex;
6054 y = ey;
6055
6056 if (phase == 1)
6057 GfxFrame[x][y] = 0; /* restart explosion animation */
6058
6059 last_phase = ExplodeDelay[x][y];
6060
6061 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6062
6063 #ifdef DEBUG
6064
6065 /* activate this even in non-DEBUG version until cause for crash in
6066 getGraphicAnimationFrame() (see below) is found and eliminated */
6067
6068 #endif
6069 #if 1
6070
6071 #if 1
6072 /* this can happen if the player leaves an explosion just in time */
6073 if (GfxElement[x][y] == EL_UNDEFINED)
6074 GfxElement[x][y] = EL_EMPTY;
6075 #else
6076 if (GfxElement[x][y] == EL_UNDEFINED)
6077 {
6078 printf("\n\n");
6079 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6080 printf("Explode(): This should never happen!\n");
6081 printf("\n\n");
6082
6083 GfxElement[x][y] = EL_EMPTY;
6084 }
6085 #endif
6086
6087 #endif
6088
6089 border_element = Store2[x][y];
6090 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6091 border_element = StorePlayer[x][y];
6092
6093 if (phase == element_info[border_element].ignition_delay ||
6094 phase == last_phase)
6095 {
6096 boolean border_explosion = FALSE;
6097
6098 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6099 !PLAYER_EXPLOSION_PROTECTED(x, y))
6100 {
6101 KillPlayerUnlessExplosionProtected(x, y);
6102 border_explosion = TRUE;
6103 }
6104 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6105 {
6106 Feld[x][y] = Store2[x][y];
6107 Store2[x][y] = 0;
6108 Bang(x, y);
6109 border_explosion = TRUE;
6110 }
6111 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6112 {
6113 AmoebeUmwandeln(x, y);
6114 Store2[x][y] = 0;
6115 border_explosion = TRUE;
6116 }
6117
6118 /* if an element just explodes due to another explosion (chain-reaction),
6119 do not immediately end the new explosion when it was the last frame of
6120 the explosion (as it would be done in the following "if"-statement!) */
6121 if (border_explosion && phase == last_phase)
6122 return;
6123 }
6124
6125 if (phase == last_phase)
6126 {
6127 int element;
6128
6129 element = Feld[x][y] = Store[x][y];
6130 Store[x][y] = Store2[x][y] = 0;
6131 GfxElement[x][y] = EL_UNDEFINED;
6132
6133 /* player can escape from explosions and might therefore be still alive */
6134 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6135 element <= EL_PLAYER_IS_EXPLODING_4)
6136 {
6137 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6138 int explosion_element = EL_PLAYER_1 + player_nr;
6139 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6140 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6141
6142 if (level.use_explosion_element[player_nr])
6143 explosion_element = level.explosion_element[player_nr];
6144
6145 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6146 element_info[explosion_element].content.e[xx][yy]);
6147 }
6148
6149 /* restore probably existing indestructible background element */
6150 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6151 element = Feld[x][y] = Back[x][y];
6152 Back[x][y] = 0;
6153
6154 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6155 GfxDir[x][y] = MV_NONE;
6156 ChangeDelay[x][y] = 0;
6157 ChangePage[x][y] = -1;
6158
6159 #if USE_NEW_CUSTOM_VALUE
6160 CustomValue[x][y] = 0;
6161 #endif
6162
6163 InitField_WithBug2(x, y, FALSE);
6164
6165 TEST_DrawLevelField(x, y);
6166
6167 TestIfElementTouchesCustomElement(x, y);
6168
6169 if (GFX_CRUMBLED(element))
6170 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6171
6172 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6173 StorePlayer[x][y] = 0;
6174
6175 if (ELEM_IS_PLAYER(element))
6176 RelocatePlayer(x, y, element);
6177 }
6178 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6179 {
6180 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6181 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6182
6183 if (phase == delay)
6184 TEST_DrawLevelFieldCrumbled(x, y);
6185
6186 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6187 {
6188 DrawLevelElement(x, y, Back[x][y]);
6189 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6190 }
6191 else if (IS_WALKABLE_UNDER(Back[x][y]))
6192 {
6193 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6194 DrawLevelElementThruMask(x, y, Back[x][y]);
6195 }
6196 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6197 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6198 }
6199 }
6200
DynaExplode(int ex,int ey)6201 void DynaExplode(int ex, int ey)
6202 {
6203 int i, j;
6204 int dynabomb_element = Feld[ex][ey];
6205 int dynabomb_size = 1;
6206 boolean dynabomb_xl = FALSE;
6207 struct PlayerInfo *player;
6208 static int xy[4][2] =
6209 {
6210 { 0, -1 },
6211 { -1, 0 },
6212 { +1, 0 },
6213 { 0, +1 }
6214 };
6215
6216 if (IS_ACTIVE_BOMB(dynabomb_element))
6217 {
6218 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6219 dynabomb_size = player->dynabomb_size;
6220 dynabomb_xl = player->dynabomb_xl;
6221 player->dynabombs_left++;
6222 }
6223
6224 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6225
6226 for (i = 0; i < NUM_DIRECTIONS; i++)
6227 {
6228 for (j = 1; j <= dynabomb_size; j++)
6229 {
6230 int x = ex + j * xy[i][0];
6231 int y = ey + j * xy[i][1];
6232 int element;
6233
6234 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6235 break;
6236
6237 element = Feld[x][y];
6238
6239 /* do not restart explosions of fields with active bombs */
6240 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6241 continue;
6242
6243 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6244
6245 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6246 !IS_DIGGABLE(element) && !dynabomb_xl)
6247 break;
6248 }
6249 }
6250 }
6251
Bang(int x,int y)6252 void Bang(int x, int y)
6253 {
6254 int element = MovingOrBlocked2Element(x, y);
6255 int explosion_type = EX_TYPE_NORMAL;
6256
6257 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6258 {
6259 struct PlayerInfo *player = PLAYERINFO(x, y);
6260
6261 #if USE_FIX_CE_ACTION_WITH_PLAYER
6262 element = Feld[x][y] = player->initial_element;
6263 #else
6264 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6265 player->element_nr);
6266 #endif
6267
6268 if (level.use_explosion_element[player->index_nr])
6269 {
6270 int explosion_element = level.explosion_element[player->index_nr];
6271
6272 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6273 explosion_type = EX_TYPE_CROSS;
6274 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6275 explosion_type = EX_TYPE_CENTER;
6276 }
6277 }
6278
6279 switch (element)
6280 {
6281 case EL_BUG:
6282 case EL_SPACESHIP:
6283 case EL_BD_BUTTERFLY:
6284 case EL_BD_FIREFLY:
6285 case EL_YAMYAM:
6286 case EL_DARK_YAMYAM:
6287 case EL_ROBOT:
6288 case EL_PACMAN:
6289 case EL_MOLE:
6290 RaiseScoreElement(element);
6291 break;
6292
6293 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6294 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6295 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6296 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6297 case EL_DYNABOMB_INCREASE_NUMBER:
6298 case EL_DYNABOMB_INCREASE_SIZE:
6299 case EL_DYNABOMB_INCREASE_POWER:
6300 explosion_type = EX_TYPE_DYNA;
6301 break;
6302
6303 case EL_DC_LANDMINE:
6304 #if 0
6305 case EL_EM_EXIT_OPEN:
6306 case EL_EM_STEEL_EXIT_OPEN:
6307 #endif
6308 explosion_type = EX_TYPE_CENTER;
6309 break;
6310
6311 case EL_PENGUIN:
6312 case EL_LAMP:
6313 case EL_LAMP_ACTIVE:
6314 case EL_AMOEBA_TO_DIAMOND:
6315 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6316 explosion_type = EX_TYPE_CENTER;
6317 break;
6318
6319 default:
6320 if (element_info[element].explosion_type == EXPLODES_CROSS)
6321 explosion_type = EX_TYPE_CROSS;
6322 else if (element_info[element].explosion_type == EXPLODES_1X1)
6323 explosion_type = EX_TYPE_CENTER;
6324 break;
6325 }
6326
6327 if (explosion_type == EX_TYPE_DYNA)
6328 DynaExplode(x, y);
6329 else
6330 Explode(x, y, EX_PHASE_START, explosion_type);
6331
6332 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6333 }
6334
SplashAcid(int x,int y)6335 void SplashAcid(int x, int y)
6336 {
6337 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6338 (!IN_LEV_FIELD(x - 1, y - 2) ||
6339 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6340 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6341
6342 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6343 (!IN_LEV_FIELD(x + 1, y - 2) ||
6344 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6345 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6346
6347 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6348 }
6349
InitBeltMovement()6350 static void InitBeltMovement()
6351 {
6352 static int belt_base_element[4] =
6353 {
6354 EL_CONVEYOR_BELT_1_LEFT,
6355 EL_CONVEYOR_BELT_2_LEFT,
6356 EL_CONVEYOR_BELT_3_LEFT,
6357 EL_CONVEYOR_BELT_4_LEFT
6358 };
6359 static int belt_base_active_element[4] =
6360 {
6361 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6362 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6363 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6364 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6365 };
6366
6367 int x, y, i, j;
6368
6369 /* set frame order for belt animation graphic according to belt direction */
6370 for (i = 0; i < NUM_BELTS; i++)
6371 {
6372 int belt_nr = i;
6373
6374 for (j = 0; j < NUM_BELT_PARTS; j++)
6375 {
6376 int element = belt_base_active_element[belt_nr] + j;
6377 int graphic_1 = el2img(element);
6378 int graphic_2 = el2panelimg(element);
6379
6380 if (game.belt_dir[i] == MV_LEFT)
6381 {
6382 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6383 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6384 }
6385 else
6386 {
6387 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6388 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6389 }
6390 }
6391 }
6392
6393 SCAN_PLAYFIELD(x, y)
6394 {
6395 int element = Feld[x][y];
6396
6397 for (i = 0; i < NUM_BELTS; i++)
6398 {
6399 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6400 {
6401 int e_belt_nr = getBeltNrFromBeltElement(element);
6402 int belt_nr = i;
6403
6404 if (e_belt_nr == belt_nr)
6405 {
6406 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6407
6408 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6409 }
6410 }
6411 }
6412 }
6413 }
6414
ToggleBeltSwitch(int x,int y)6415 static void ToggleBeltSwitch(int x, int y)
6416 {
6417 static int belt_base_element[4] =
6418 {
6419 EL_CONVEYOR_BELT_1_LEFT,
6420 EL_CONVEYOR_BELT_2_LEFT,
6421 EL_CONVEYOR_BELT_3_LEFT,
6422 EL_CONVEYOR_BELT_4_LEFT
6423 };
6424 static int belt_base_active_element[4] =
6425 {
6426 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6427 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6428 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6429 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6430 };
6431 static int belt_base_switch_element[4] =
6432 {
6433 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6434 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6435 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6436 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6437 };
6438 static int belt_move_dir[4] =
6439 {
6440 MV_LEFT,
6441 MV_NONE,
6442 MV_RIGHT,
6443 MV_NONE,
6444 };
6445
6446 int element = Feld[x][y];
6447 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6448 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6449 int belt_dir = belt_move_dir[belt_dir_nr];
6450 int xx, yy, i;
6451
6452 if (!IS_BELT_SWITCH(element))
6453 return;
6454
6455 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6456 game.belt_dir[belt_nr] = belt_dir;
6457
6458 if (belt_dir_nr == 3)
6459 belt_dir_nr = 1;
6460
6461 /* set frame order for belt animation graphic according to belt direction */
6462 for (i = 0; i < NUM_BELT_PARTS; i++)
6463 {
6464 int element = belt_base_active_element[belt_nr] + i;
6465 int graphic_1 = el2img(element);
6466 int graphic_2 = el2panelimg(element);
6467
6468 if (belt_dir == MV_LEFT)
6469 {
6470 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6471 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6472 }
6473 else
6474 {
6475 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6476 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6477 }
6478 }
6479
6480 SCAN_PLAYFIELD(xx, yy)
6481 {
6482 int element = Feld[xx][yy];
6483
6484 if (IS_BELT_SWITCH(element))
6485 {
6486 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6487
6488 if (e_belt_nr == belt_nr)
6489 {
6490 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6491 TEST_DrawLevelField(xx, yy);
6492 }
6493 }
6494 else if (IS_BELT(element) && belt_dir != MV_NONE)
6495 {
6496 int e_belt_nr = getBeltNrFromBeltElement(element);
6497
6498 if (e_belt_nr == belt_nr)
6499 {
6500 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6501
6502 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6503 TEST_DrawLevelField(xx, yy);
6504 }
6505 }
6506 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6507 {
6508 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6509
6510 if (e_belt_nr == belt_nr)
6511 {
6512 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6513
6514 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6515 TEST_DrawLevelField(xx, yy);
6516 }
6517 }
6518 }
6519 }
6520
ToggleSwitchgateSwitch(int x,int y)6521 static void ToggleSwitchgateSwitch(int x, int y)
6522 {
6523 int xx, yy;
6524
6525 game.switchgate_pos = !game.switchgate_pos;
6526
6527 SCAN_PLAYFIELD(xx, yy)
6528 {
6529 int element = Feld[xx][yy];
6530
6531 #if !USE_BOTH_SWITCHGATE_SWITCHES
6532 if (element == EL_SWITCHGATE_SWITCH_UP ||
6533 element == EL_SWITCHGATE_SWITCH_DOWN)
6534 {
6535 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6536 TEST_DrawLevelField(xx, yy);
6537 }
6538 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6539 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6540 {
6541 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6542 TEST_DrawLevelField(xx, yy);
6543 }
6544 #else
6545 if (element == EL_SWITCHGATE_SWITCH_UP)
6546 {
6547 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6548 TEST_DrawLevelField(xx, yy);
6549 }
6550 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6551 {
6552 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6553 TEST_DrawLevelField(xx, yy);
6554 }
6555 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6556 {
6557 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6558 TEST_DrawLevelField(xx, yy);
6559 }
6560 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6561 {
6562 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6563 TEST_DrawLevelField(xx, yy);
6564 }
6565 #endif
6566 else if (element == EL_SWITCHGATE_OPEN ||
6567 element == EL_SWITCHGATE_OPENING)
6568 {
6569 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6570
6571 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6572 }
6573 else if (element == EL_SWITCHGATE_CLOSED ||
6574 element == EL_SWITCHGATE_CLOSING)
6575 {
6576 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6577
6578 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6579 }
6580 }
6581 }
6582
getInvisibleActiveFromInvisibleElement(int element)6583 static int getInvisibleActiveFromInvisibleElement(int element)
6584 {
6585 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6586 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6587 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6588 element);
6589 }
6590
getInvisibleFromInvisibleActiveElement(int element)6591 static int getInvisibleFromInvisibleActiveElement(int element)
6592 {
6593 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6594 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6595 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6596 element);
6597 }
6598
RedrawAllLightSwitchesAndInvisibleElements()6599 static void RedrawAllLightSwitchesAndInvisibleElements()
6600 {
6601 int x, y;
6602
6603 SCAN_PLAYFIELD(x, y)
6604 {
6605 int element = Feld[x][y];
6606
6607 if (element == EL_LIGHT_SWITCH &&
6608 game.light_time_left > 0)
6609 {
6610 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6611 TEST_DrawLevelField(x, y);
6612 }
6613 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6614 game.light_time_left == 0)
6615 {
6616 Feld[x][y] = EL_LIGHT_SWITCH;
6617 TEST_DrawLevelField(x, y);
6618 }
6619 else if (element == EL_EMC_DRIPPER &&
6620 game.light_time_left > 0)
6621 {
6622 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6623 TEST_DrawLevelField(x, y);
6624 }
6625 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6626 game.light_time_left == 0)
6627 {
6628 Feld[x][y] = EL_EMC_DRIPPER;
6629 TEST_DrawLevelField(x, y);
6630 }
6631 else if (element == EL_INVISIBLE_STEELWALL ||
6632 element == EL_INVISIBLE_WALL ||
6633 element == EL_INVISIBLE_SAND)
6634 {
6635 if (game.light_time_left > 0)
6636 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6637
6638 TEST_DrawLevelField(x, y);
6639
6640 /* uncrumble neighbour fields, if needed */
6641 if (element == EL_INVISIBLE_SAND)
6642 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6643 }
6644 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6645 element == EL_INVISIBLE_WALL_ACTIVE ||
6646 element == EL_INVISIBLE_SAND_ACTIVE)
6647 {
6648 if (game.light_time_left == 0)
6649 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6650
6651 TEST_DrawLevelField(x, y);
6652
6653 /* re-crumble neighbour fields, if needed */
6654 if (element == EL_INVISIBLE_SAND)
6655 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6656 }
6657 }
6658 }
6659
RedrawAllInvisibleElementsForLenses()6660 static void RedrawAllInvisibleElementsForLenses()
6661 {
6662 int x, y;
6663
6664 SCAN_PLAYFIELD(x, y)
6665 {
6666 int element = Feld[x][y];
6667
6668 if (element == EL_EMC_DRIPPER &&
6669 game.lenses_time_left > 0)
6670 {
6671 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6672 TEST_DrawLevelField(x, y);
6673 }
6674 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6675 game.lenses_time_left == 0)
6676 {
6677 Feld[x][y] = EL_EMC_DRIPPER;
6678 TEST_DrawLevelField(x, y);
6679 }
6680 else if (element == EL_INVISIBLE_STEELWALL ||
6681 element == EL_INVISIBLE_WALL ||
6682 element == EL_INVISIBLE_SAND)
6683 {
6684 if (game.lenses_time_left > 0)
6685 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6686
6687 TEST_DrawLevelField(x, y);
6688
6689 /* uncrumble neighbour fields, if needed */
6690 if (element == EL_INVISIBLE_SAND)
6691 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6692 }
6693 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6694 element == EL_INVISIBLE_WALL_ACTIVE ||
6695 element == EL_INVISIBLE_SAND_ACTIVE)
6696 {
6697 if (game.lenses_time_left == 0)
6698 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6699
6700 TEST_DrawLevelField(x, y);
6701
6702 /* re-crumble neighbour fields, if needed */
6703 if (element == EL_INVISIBLE_SAND)
6704 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6705 }
6706 }
6707 }
6708
RedrawAllInvisibleElementsForMagnifier()6709 static void RedrawAllInvisibleElementsForMagnifier()
6710 {
6711 int x, y;
6712
6713 SCAN_PLAYFIELD(x, y)
6714 {
6715 int element = Feld[x][y];
6716
6717 if (element == EL_EMC_FAKE_GRASS &&
6718 game.magnify_time_left > 0)
6719 {
6720 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6721 TEST_DrawLevelField(x, y);
6722 }
6723 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6724 game.magnify_time_left == 0)
6725 {
6726 Feld[x][y] = EL_EMC_FAKE_GRASS;
6727 TEST_DrawLevelField(x, y);
6728 }
6729 else if (IS_GATE_GRAY(element) &&
6730 game.magnify_time_left > 0)
6731 {
6732 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6733 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6734 IS_EM_GATE_GRAY(element) ?
6735 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6736 IS_EMC_GATE_GRAY(element) ?
6737 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6738 IS_DC_GATE_GRAY(element) ?
6739 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6740 element);
6741 TEST_DrawLevelField(x, y);
6742 }
6743 else if (IS_GATE_GRAY_ACTIVE(element) &&
6744 game.magnify_time_left == 0)
6745 {
6746 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6747 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6748 IS_EM_GATE_GRAY_ACTIVE(element) ?
6749 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6750 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6751 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6752 IS_DC_GATE_GRAY_ACTIVE(element) ?
6753 EL_DC_GATE_WHITE_GRAY :
6754 element);
6755 TEST_DrawLevelField(x, y);
6756 }
6757 }
6758 }
6759
ToggleLightSwitch(int x,int y)6760 static void ToggleLightSwitch(int x, int y)
6761 {
6762 int element = Feld[x][y];
6763
6764 game.light_time_left =
6765 (element == EL_LIGHT_SWITCH ?
6766 level.time_light * FRAMES_PER_SECOND : 0);
6767
6768 RedrawAllLightSwitchesAndInvisibleElements();
6769 }
6770
ActivateTimegateSwitch(int x,int y)6771 static void ActivateTimegateSwitch(int x, int y)
6772 {
6773 int xx, yy;
6774
6775 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6776
6777 SCAN_PLAYFIELD(xx, yy)
6778 {
6779 int element = Feld[xx][yy];
6780
6781 if (element == EL_TIMEGATE_CLOSED ||
6782 element == EL_TIMEGATE_CLOSING)
6783 {
6784 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6785 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6786 }
6787
6788 /*
6789 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6790 {
6791 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6792 TEST_DrawLevelField(xx, yy);
6793 }
6794 */
6795
6796 }
6797
6798 #if 1
6799 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6800 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6801 #else
6802 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6803 #endif
6804 }
6805
Impact(int x,int y)6806 void Impact(int x, int y)
6807 {
6808 boolean last_line = (y == lev_fieldy - 1);
6809 boolean object_hit = FALSE;
6810 boolean impact = (last_line || object_hit);
6811 int element = Feld[x][y];
6812 int smashed = EL_STEELWALL;
6813
6814 if (!last_line) /* check if element below was hit */
6815 {
6816 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6817 return;
6818
6819 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6820 MovDir[x][y + 1] != MV_DOWN ||
6821 MovPos[x][y + 1] <= TILEY / 2));
6822
6823 /* do not smash moving elements that left the smashed field in time */
6824 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6825 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6826 object_hit = FALSE;
6827
6828 #if USE_QUICKSAND_IMPACT_BUGFIX
6829 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6830 {
6831 RemoveMovingField(x, y + 1);
6832 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6833 Feld[x][y + 2] = EL_ROCK;
6834 TEST_DrawLevelField(x, y + 2);
6835
6836 object_hit = TRUE;
6837 }
6838
6839 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6840 {
6841 RemoveMovingField(x, y + 1);
6842 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6843 Feld[x][y + 2] = EL_ROCK;
6844 TEST_DrawLevelField(x, y + 2);
6845
6846 object_hit = TRUE;
6847 }
6848 #endif
6849
6850 if (object_hit)
6851 smashed = MovingOrBlocked2Element(x, y + 1);
6852
6853 impact = (last_line || object_hit);
6854 }
6855
6856 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6857 {
6858 SplashAcid(x, y + 1);
6859 return;
6860 }
6861
6862 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6863 /* only reset graphic animation if graphic really changes after impact */
6864 if (impact &&
6865 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6866 {
6867 ResetGfxAnimation(x, y);
6868 TEST_DrawLevelField(x, y);
6869 }
6870
6871 if (impact && CAN_EXPLODE_IMPACT(element))
6872 {
6873 Bang(x, y);
6874 return;
6875 }
6876 else if (impact && element == EL_PEARL &&
6877 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6878 {
6879 ResetGfxAnimation(x, y);
6880
6881 Feld[x][y] = EL_PEARL_BREAKING;
6882 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6883 return;
6884 }
6885 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6886 {
6887 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6888
6889 return;
6890 }
6891
6892 if (impact && element == EL_AMOEBA_DROP)
6893 {
6894 if (object_hit && IS_PLAYER(x, y + 1))
6895 KillPlayerUnlessEnemyProtected(x, y + 1);
6896 else if (object_hit && smashed == EL_PENGUIN)
6897 Bang(x, y + 1);
6898 else
6899 {
6900 Feld[x][y] = EL_AMOEBA_GROWING;
6901 Store[x][y] = EL_AMOEBA_WET;
6902
6903 ResetRandomAnimationValue(x, y);
6904 }
6905 return;
6906 }
6907
6908 if (object_hit) /* check which object was hit */
6909 {
6910 if ((CAN_PASS_MAGIC_WALL(element) &&
6911 (smashed == EL_MAGIC_WALL ||
6912 smashed == EL_BD_MAGIC_WALL)) ||
6913 (CAN_PASS_DC_MAGIC_WALL(element) &&
6914 smashed == EL_DC_MAGIC_WALL))
6915 {
6916 int xx, yy;
6917 int activated_magic_wall =
6918 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6919 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6920 EL_DC_MAGIC_WALL_ACTIVE);
6921
6922 /* activate magic wall / mill */
6923 SCAN_PLAYFIELD(xx, yy)
6924 {
6925 if (Feld[xx][yy] == smashed)
6926 Feld[xx][yy] = activated_magic_wall;
6927 }
6928
6929 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6930 game.magic_wall_active = TRUE;
6931
6932 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6933 SND_MAGIC_WALL_ACTIVATING :
6934 smashed == EL_BD_MAGIC_WALL ?
6935 SND_BD_MAGIC_WALL_ACTIVATING :
6936 SND_DC_MAGIC_WALL_ACTIVATING));
6937 }
6938
6939 if (IS_PLAYER(x, y + 1))
6940 {
6941 if (CAN_SMASH_PLAYER(element))
6942 {
6943 KillPlayerUnlessEnemyProtected(x, y + 1);
6944 return;
6945 }
6946 }
6947 else if (smashed == EL_PENGUIN)
6948 {
6949 if (CAN_SMASH_PLAYER(element))
6950 {
6951 Bang(x, y + 1);
6952 return;
6953 }
6954 }
6955 else if (element == EL_BD_DIAMOND)
6956 {
6957 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6958 {
6959 Bang(x, y + 1);
6960 return;
6961 }
6962 }
6963 else if (((element == EL_SP_INFOTRON ||
6964 element == EL_SP_ZONK) &&
6965 (smashed == EL_SP_SNIKSNAK ||
6966 smashed == EL_SP_ELECTRON ||
6967 smashed == EL_SP_DISK_ORANGE)) ||
6968 (element == EL_SP_INFOTRON &&
6969 smashed == EL_SP_DISK_YELLOW))
6970 {
6971 Bang(x, y + 1);
6972 return;
6973 }
6974 else if (CAN_SMASH_EVERYTHING(element))
6975 {
6976 if (IS_CLASSIC_ENEMY(smashed) ||
6977 CAN_EXPLODE_SMASHED(smashed))
6978 {
6979 Bang(x, y + 1);
6980 return;
6981 }
6982 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6983 {
6984 if (smashed == EL_LAMP ||
6985 smashed == EL_LAMP_ACTIVE)
6986 {
6987 Bang(x, y + 1);
6988 return;
6989 }
6990 else if (smashed == EL_NUT)
6991 {
6992 Feld[x][y + 1] = EL_NUT_BREAKING;
6993 PlayLevelSound(x, y, SND_NUT_BREAKING);
6994 RaiseScoreElement(EL_NUT);
6995 return;
6996 }
6997 else if (smashed == EL_PEARL)
6998 {
6999 ResetGfxAnimation(x, y);
7000
7001 Feld[x][y + 1] = EL_PEARL_BREAKING;
7002 PlayLevelSound(x, y, SND_PEARL_BREAKING);
7003 return;
7004 }
7005 else if (smashed == EL_DIAMOND)
7006 {
7007 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7008 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7009 return;
7010 }
7011 else if (IS_BELT_SWITCH(smashed))
7012 {
7013 ToggleBeltSwitch(x, y + 1);
7014 }
7015 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7016 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7017 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7018 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7019 {
7020 ToggleSwitchgateSwitch(x, y + 1);
7021 }
7022 else if (smashed == EL_LIGHT_SWITCH ||
7023 smashed == EL_LIGHT_SWITCH_ACTIVE)
7024 {
7025 ToggleLightSwitch(x, y + 1);
7026 }
7027 else
7028 {
7029 #if 0
7030 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7031 #endif
7032
7033 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7034
7035 CheckElementChangeBySide(x, y + 1, smashed, element,
7036 CE_SWITCHED, CH_SIDE_TOP);
7037 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7038 CH_SIDE_TOP);
7039 }
7040 }
7041 else
7042 {
7043 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7044 }
7045 }
7046 }
7047
7048 /* play sound of magic wall / mill */
7049 if (!last_line &&
7050 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7051 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7052 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7053 {
7054 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7055 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7056 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7057 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7058 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7059 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7060
7061 return;
7062 }
7063
7064 /* play sound of object that hits the ground */
7065 if (last_line || object_hit)
7066 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7067 }
7068
TurnRoundExt(int x,int y)7069 inline static void TurnRoundExt(int x, int y)
7070 {
7071 static struct
7072 {
7073 int dx, dy;
7074 } move_xy[] =
7075 {
7076 { 0, 0 },
7077 { -1, 0 },
7078 { +1, 0 },
7079 { 0, 0 },
7080 { 0, -1 },
7081 { 0, 0 }, { 0, 0 }, { 0, 0 },
7082 { 0, +1 }
7083 };
7084 static struct
7085 {
7086 int left, right, back;
7087 } turn[] =
7088 {
7089 { 0, 0, 0 },
7090 { MV_DOWN, MV_UP, MV_RIGHT },
7091 { MV_UP, MV_DOWN, MV_LEFT },
7092 { 0, 0, 0 },
7093 { MV_LEFT, MV_RIGHT, MV_DOWN },
7094 { 0, 0, 0 },
7095 { 0, 0, 0 },
7096 { 0, 0, 0 },
7097 { MV_RIGHT, MV_LEFT, MV_UP }
7098 };
7099
7100 int element = Feld[x][y];
7101 int move_pattern = element_info[element].move_pattern;
7102
7103 int old_move_dir = MovDir[x][y];
7104 int left_dir = turn[old_move_dir].left;
7105 int right_dir = turn[old_move_dir].right;
7106 int back_dir = turn[old_move_dir].back;
7107
7108 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7109 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7110 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7111 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7112
7113 int left_x = x + left_dx, left_y = y + left_dy;
7114 int right_x = x + right_dx, right_y = y + right_dy;
7115 int move_x = x + move_dx, move_y = y + move_dy;
7116
7117 int xx, yy;
7118
7119 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7120 {
7121 TestIfBadThingTouchesOtherBadThing(x, y);
7122
7123 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7124 MovDir[x][y] = right_dir;
7125 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7126 MovDir[x][y] = left_dir;
7127
7128 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7129 MovDelay[x][y] = 9;
7130 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7131 MovDelay[x][y] = 1;
7132 }
7133 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7134 {
7135 TestIfBadThingTouchesOtherBadThing(x, y);
7136
7137 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7138 MovDir[x][y] = left_dir;
7139 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7140 MovDir[x][y] = right_dir;
7141
7142 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7143 MovDelay[x][y] = 9;
7144 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7145 MovDelay[x][y] = 1;
7146 }
7147 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7148 {
7149 TestIfBadThingTouchesOtherBadThing(x, y);
7150
7151 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7152 MovDir[x][y] = left_dir;
7153 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7154 MovDir[x][y] = right_dir;
7155
7156 if (MovDir[x][y] != old_move_dir)
7157 MovDelay[x][y] = 9;
7158 }
7159 else if (element == EL_YAMYAM)
7160 {
7161 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7162 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7163
7164 if (can_turn_left && can_turn_right)
7165 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7166 else if (can_turn_left)
7167 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7168 else if (can_turn_right)
7169 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7170 else
7171 MovDir[x][y] = back_dir;
7172
7173 MovDelay[x][y] = 16 + 16 * RND(3);
7174 }
7175 else if (element == EL_DARK_YAMYAM)
7176 {
7177 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7178 left_x, left_y);
7179 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7180 right_x, right_y);
7181
7182 if (can_turn_left && can_turn_right)
7183 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7184 else if (can_turn_left)
7185 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7186 else if (can_turn_right)
7187 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7188 else
7189 MovDir[x][y] = back_dir;
7190
7191 MovDelay[x][y] = 16 + 16 * RND(3);
7192 }
7193 else if (element == EL_PACMAN)
7194 {
7195 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7196 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7197
7198 if (can_turn_left && can_turn_right)
7199 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7200 else if (can_turn_left)
7201 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7202 else if (can_turn_right)
7203 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7204 else
7205 MovDir[x][y] = back_dir;
7206
7207 MovDelay[x][y] = 6 + RND(40);
7208 }
7209 else if (element == EL_PIG)
7210 {
7211 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7212 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7213 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7214 boolean should_turn_left, should_turn_right, should_move_on;
7215 int rnd_value = 24;
7216 int rnd = RND(rnd_value);
7217
7218 should_turn_left = (can_turn_left &&
7219 (!can_move_on ||
7220 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7221 y + back_dy + left_dy)));
7222 should_turn_right = (can_turn_right &&
7223 (!can_move_on ||
7224 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7225 y + back_dy + right_dy)));
7226 should_move_on = (can_move_on &&
7227 (!can_turn_left ||
7228 !can_turn_right ||
7229 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7230 y + move_dy + left_dy) ||
7231 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7232 y + move_dy + right_dy)));
7233
7234 if (should_turn_left || should_turn_right || should_move_on)
7235 {
7236 if (should_turn_left && should_turn_right && should_move_on)
7237 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7238 rnd < 2 * rnd_value / 3 ? right_dir :
7239 old_move_dir);
7240 else if (should_turn_left && should_turn_right)
7241 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7242 else if (should_turn_left && should_move_on)
7243 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7244 else if (should_turn_right && should_move_on)
7245 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7246 else if (should_turn_left)
7247 MovDir[x][y] = left_dir;
7248 else if (should_turn_right)
7249 MovDir[x][y] = right_dir;
7250 else if (should_move_on)
7251 MovDir[x][y] = old_move_dir;
7252 }
7253 else if (can_move_on && rnd > rnd_value / 8)
7254 MovDir[x][y] = old_move_dir;
7255 else if (can_turn_left && can_turn_right)
7256 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7257 else if (can_turn_left && rnd > rnd_value / 8)
7258 MovDir[x][y] = left_dir;
7259 else if (can_turn_right && rnd > rnd_value/8)
7260 MovDir[x][y] = right_dir;
7261 else
7262 MovDir[x][y] = back_dir;
7263
7264 xx = x + move_xy[MovDir[x][y]].dx;
7265 yy = y + move_xy[MovDir[x][y]].dy;
7266
7267 if (!IN_LEV_FIELD(xx, yy) ||
7268 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7269 MovDir[x][y] = old_move_dir;
7270
7271 MovDelay[x][y] = 0;
7272 }
7273 else if (element == EL_DRAGON)
7274 {
7275 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7276 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7277 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7278 int rnd_value = 24;
7279 int rnd = RND(rnd_value);
7280
7281 if (can_move_on && rnd > rnd_value / 8)
7282 MovDir[x][y] = old_move_dir;
7283 else if (can_turn_left && can_turn_right)
7284 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7285 else if (can_turn_left && rnd > rnd_value / 8)
7286 MovDir[x][y] = left_dir;
7287 else if (can_turn_right && rnd > rnd_value / 8)
7288 MovDir[x][y] = right_dir;
7289 else
7290 MovDir[x][y] = back_dir;
7291
7292 xx = x + move_xy[MovDir[x][y]].dx;
7293 yy = y + move_xy[MovDir[x][y]].dy;
7294
7295 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7296 MovDir[x][y] = old_move_dir;
7297
7298 MovDelay[x][y] = 0;
7299 }
7300 else if (element == EL_MOLE)
7301 {
7302 boolean can_move_on =
7303 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7304 IS_AMOEBOID(Feld[move_x][move_y]) ||
7305 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7306 if (!can_move_on)
7307 {
7308 boolean can_turn_left =
7309 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7310 IS_AMOEBOID(Feld[left_x][left_y])));
7311
7312 boolean can_turn_right =
7313 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7314 IS_AMOEBOID(Feld[right_x][right_y])));
7315
7316 if (can_turn_left && can_turn_right)
7317 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7318 else if (can_turn_left)
7319 MovDir[x][y] = left_dir;
7320 else
7321 MovDir[x][y] = right_dir;
7322 }
7323
7324 if (MovDir[x][y] != old_move_dir)
7325 MovDelay[x][y] = 9;
7326 }
7327 else if (element == EL_BALLOON)
7328 {
7329 MovDir[x][y] = game.wind_direction;
7330 MovDelay[x][y] = 0;
7331 }
7332 else if (element == EL_SPRING)
7333 {
7334 #if USE_NEW_SPRING_BUMPER
7335 if (MovDir[x][y] & MV_HORIZONTAL)
7336 {
7337 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7338 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7339 {
7340 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7341 ResetGfxAnimation(move_x, move_y);
7342 TEST_DrawLevelField(move_x, move_y);
7343
7344 MovDir[x][y] = back_dir;
7345 }
7346 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7347 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7348 MovDir[x][y] = MV_NONE;
7349 }
7350 #else
7351 if (MovDir[x][y] & MV_HORIZONTAL &&
7352 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7353 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7354 MovDir[x][y] = MV_NONE;
7355 #endif
7356
7357 MovDelay[x][y] = 0;
7358 }
7359 else if (element == EL_ROBOT ||
7360 element == EL_SATELLITE ||
7361 element == EL_PENGUIN ||
7362 element == EL_EMC_ANDROID)
7363 {
7364 int attr_x = -1, attr_y = -1;
7365
7366 if (AllPlayersGone)
7367 {
7368 attr_x = ExitX;
7369 attr_y = ExitY;
7370 }
7371 else
7372 {
7373 int i;
7374
7375 for (i = 0; i < MAX_PLAYERS; i++)
7376 {
7377 struct PlayerInfo *player = &stored_player[i];
7378 int jx = player->jx, jy = player->jy;
7379
7380 if (!player->active)
7381 continue;
7382
7383 if (attr_x == -1 ||
7384 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7385 {
7386 attr_x = jx;
7387 attr_y = jy;
7388 }
7389 }
7390 }
7391
7392 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7393 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7394 game.engine_version < VERSION_IDENT(3,1,0,0)))
7395 {
7396 attr_x = ZX;
7397 attr_y = ZY;
7398 }
7399
7400 if (element == EL_PENGUIN)
7401 {
7402 int i;
7403 static int xy[4][2] =
7404 {
7405 { 0, -1 },
7406 { -1, 0 },
7407 { +1, 0 },
7408 { 0, +1 }
7409 };
7410
7411 for (i = 0; i < NUM_DIRECTIONS; i++)
7412 {
7413 int ex = x + xy[i][0];
7414 int ey = y + xy[i][1];
7415
7416 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7417 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7418 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7419 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7420 {
7421 attr_x = ex;
7422 attr_y = ey;
7423 break;
7424 }
7425 }
7426 }
7427
7428 MovDir[x][y] = MV_NONE;
7429 if (attr_x < x)
7430 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7431 else if (attr_x > x)
7432 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7433 if (attr_y < y)
7434 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7435 else if (attr_y > y)
7436 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7437
7438 if (element == EL_ROBOT)
7439 {
7440 int newx, newy;
7441
7442 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7443 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7444 Moving2Blocked(x, y, &newx, &newy);
7445
7446 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7447 MovDelay[x][y] = 8 + 8 * !RND(3);
7448 else
7449 MovDelay[x][y] = 16;
7450 }
7451 else if (element == EL_PENGUIN)
7452 {
7453 int newx, newy;
7454
7455 MovDelay[x][y] = 1;
7456
7457 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7458 {
7459 boolean first_horiz = RND(2);
7460 int new_move_dir = MovDir[x][y];
7461
7462 MovDir[x][y] =
7463 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7464 Moving2Blocked(x, y, &newx, &newy);
7465
7466 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7467 return;
7468
7469 MovDir[x][y] =
7470 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7471 Moving2Blocked(x, y, &newx, &newy);
7472
7473 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7474 return;
7475
7476 MovDir[x][y] = old_move_dir;
7477 return;
7478 }
7479 }
7480 else if (element == EL_SATELLITE)
7481 {
7482 int newx, newy;
7483
7484 MovDelay[x][y] = 1;
7485
7486 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7487 {
7488 boolean first_horiz = RND(2);
7489 int new_move_dir = MovDir[x][y];
7490
7491 MovDir[x][y] =
7492 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7493 Moving2Blocked(x, y, &newx, &newy);
7494
7495 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7496 return;
7497
7498 MovDir[x][y] =
7499 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7500 Moving2Blocked(x, y, &newx, &newy);
7501
7502 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7503 return;
7504
7505 MovDir[x][y] = old_move_dir;
7506 return;
7507 }
7508 }
7509 else if (element == EL_EMC_ANDROID)
7510 {
7511 static int check_pos[16] =
7512 {
7513 -1, /* 0 => (invalid) */
7514 7, /* 1 => MV_LEFT */
7515 3, /* 2 => MV_RIGHT */
7516 -1, /* 3 => (invalid) */
7517 1, /* 4 => MV_UP */
7518 0, /* 5 => MV_LEFT | MV_UP */
7519 2, /* 6 => MV_RIGHT | MV_UP */
7520 -1, /* 7 => (invalid) */
7521 5, /* 8 => MV_DOWN */
7522 6, /* 9 => MV_LEFT | MV_DOWN */
7523 4, /* 10 => MV_RIGHT | MV_DOWN */
7524 -1, /* 11 => (invalid) */
7525 -1, /* 12 => (invalid) */
7526 -1, /* 13 => (invalid) */
7527 -1, /* 14 => (invalid) */
7528 -1, /* 15 => (invalid) */
7529 };
7530 static struct
7531 {
7532 int dx, dy;
7533 int dir;
7534 } check_xy[8] =
7535 {
7536 { -1, -1, MV_LEFT | MV_UP },
7537 { 0, -1, MV_UP },
7538 { +1, -1, MV_RIGHT | MV_UP },
7539 { +1, 0, MV_RIGHT },
7540 { +1, +1, MV_RIGHT | MV_DOWN },
7541 { 0, +1, MV_DOWN },
7542 { -1, +1, MV_LEFT | MV_DOWN },
7543 { -1, 0, MV_LEFT },
7544 };
7545 int start_pos, check_order;
7546 boolean can_clone = FALSE;
7547 int i;
7548
7549 /* check if there is any free field around current position */
7550 for (i = 0; i < 8; i++)
7551 {
7552 int newx = x + check_xy[i].dx;
7553 int newy = y + check_xy[i].dy;
7554
7555 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7556 {
7557 can_clone = TRUE;
7558
7559 break;
7560 }
7561 }
7562
7563 if (can_clone) /* randomly find an element to clone */
7564 {
7565 can_clone = FALSE;
7566
7567 start_pos = check_pos[RND(8)];
7568 check_order = (RND(2) ? -1 : +1);
7569
7570 for (i = 0; i < 8; i++)
7571 {
7572 int pos_raw = start_pos + i * check_order;
7573 int pos = (pos_raw + 8) % 8;
7574 int newx = x + check_xy[pos].dx;
7575 int newy = y + check_xy[pos].dy;
7576
7577 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7578 {
7579 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7580 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7581
7582 Store[x][y] = Feld[newx][newy];
7583
7584 can_clone = TRUE;
7585
7586 break;
7587 }
7588 }
7589 }
7590
7591 if (can_clone) /* randomly find a direction to move */
7592 {
7593 can_clone = FALSE;
7594
7595 start_pos = check_pos[RND(8)];
7596 check_order = (RND(2) ? -1 : +1);
7597
7598 for (i = 0; i < 8; i++)
7599 {
7600 int pos_raw = start_pos + i * check_order;
7601 int pos = (pos_raw + 8) % 8;
7602 int newx = x + check_xy[pos].dx;
7603 int newy = y + check_xy[pos].dy;
7604 int new_move_dir = check_xy[pos].dir;
7605
7606 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7607 {
7608 MovDir[x][y] = new_move_dir;
7609 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7610
7611 can_clone = TRUE;
7612
7613 break;
7614 }
7615 }
7616 }
7617
7618 if (can_clone) /* cloning and moving successful */
7619 return;
7620
7621 /* cannot clone -- try to move towards player */
7622
7623 start_pos = check_pos[MovDir[x][y] & 0x0f];
7624 check_order = (RND(2) ? -1 : +1);
7625
7626 for (i = 0; i < 3; i++)
7627 {
7628 /* first check start_pos, then previous/next or (next/previous) pos */
7629 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7630 int pos = (pos_raw + 8) % 8;
7631 int newx = x + check_xy[pos].dx;
7632 int newy = y + check_xy[pos].dy;
7633 int new_move_dir = check_xy[pos].dir;
7634
7635 if (IS_PLAYER(newx, newy))
7636 break;
7637
7638 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7639 {
7640 MovDir[x][y] = new_move_dir;
7641 MovDelay[x][y] = level.android_move_time * 8 + 1;
7642
7643 break;
7644 }
7645 }
7646 }
7647 }
7648 else if (move_pattern == MV_TURNING_LEFT ||
7649 move_pattern == MV_TURNING_RIGHT ||
7650 move_pattern == MV_TURNING_LEFT_RIGHT ||
7651 move_pattern == MV_TURNING_RIGHT_LEFT ||
7652 move_pattern == MV_TURNING_RANDOM ||
7653 move_pattern == MV_ALL_DIRECTIONS)
7654 {
7655 boolean can_turn_left =
7656 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7657 boolean can_turn_right =
7658 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7659
7660 if (element_info[element].move_stepsize == 0) /* "not moving" */
7661 return;
7662
7663 if (move_pattern == MV_TURNING_LEFT)
7664 MovDir[x][y] = left_dir;
7665 else if (move_pattern == MV_TURNING_RIGHT)
7666 MovDir[x][y] = right_dir;
7667 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7668 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7669 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7670 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7671 else if (move_pattern == MV_TURNING_RANDOM)
7672 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7673 can_turn_right && !can_turn_left ? right_dir :
7674 RND(2) ? left_dir : right_dir);
7675 else if (can_turn_left && can_turn_right)
7676 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7677 else if (can_turn_left)
7678 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7679 else if (can_turn_right)
7680 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7681 else
7682 MovDir[x][y] = back_dir;
7683
7684 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7685 }
7686 else if (move_pattern == MV_HORIZONTAL ||
7687 move_pattern == MV_VERTICAL)
7688 {
7689 if (move_pattern & old_move_dir)
7690 MovDir[x][y] = back_dir;
7691 else if (move_pattern == MV_HORIZONTAL)
7692 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7693 else if (move_pattern == MV_VERTICAL)
7694 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7695
7696 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7697 }
7698 else if (move_pattern & MV_ANY_DIRECTION)
7699 {
7700 MovDir[x][y] = move_pattern;
7701 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7702 }
7703 else if (move_pattern & MV_WIND_DIRECTION)
7704 {
7705 MovDir[x][y] = game.wind_direction;
7706 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7707 }
7708 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7709 {
7710 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7711 MovDir[x][y] = left_dir;
7712 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7713 MovDir[x][y] = right_dir;
7714
7715 if (MovDir[x][y] != old_move_dir)
7716 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7717 }
7718 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7719 {
7720 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7721 MovDir[x][y] = right_dir;
7722 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7723 MovDir[x][y] = left_dir;
7724
7725 if (MovDir[x][y] != old_move_dir)
7726 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7727 }
7728 else if (move_pattern == MV_TOWARDS_PLAYER ||
7729 move_pattern == MV_AWAY_FROM_PLAYER)
7730 {
7731 int attr_x = -1, attr_y = -1;
7732 int newx, newy;
7733 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7734
7735 if (AllPlayersGone)
7736 {
7737 attr_x = ExitX;
7738 attr_y = ExitY;
7739 }
7740 else
7741 {
7742 int i;
7743
7744 for (i = 0; i < MAX_PLAYERS; i++)
7745 {
7746 struct PlayerInfo *player = &stored_player[i];
7747 int jx = player->jx, jy = player->jy;
7748
7749 if (!player->active)
7750 continue;
7751
7752 if (attr_x == -1 ||
7753 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7754 {
7755 attr_x = jx;
7756 attr_y = jy;
7757 }
7758 }
7759 }
7760
7761 MovDir[x][y] = MV_NONE;
7762 if (attr_x < x)
7763 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7764 else if (attr_x > x)
7765 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7766 if (attr_y < y)
7767 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7768 else if (attr_y > y)
7769 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7770
7771 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7772
7773 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7774 {
7775 boolean first_horiz = RND(2);
7776 int new_move_dir = MovDir[x][y];
7777
7778 if (element_info[element].move_stepsize == 0) /* "not moving" */
7779 {
7780 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7781 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7782
7783 return;
7784 }
7785
7786 MovDir[x][y] =
7787 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7788 Moving2Blocked(x, y, &newx, &newy);
7789
7790 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7791 return;
7792
7793 MovDir[x][y] =
7794 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7795 Moving2Blocked(x, y, &newx, &newy);
7796
7797 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7798 return;
7799
7800 MovDir[x][y] = old_move_dir;
7801 }
7802 }
7803 else if (move_pattern == MV_WHEN_PUSHED ||
7804 move_pattern == MV_WHEN_DROPPED)
7805 {
7806 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7807 MovDir[x][y] = MV_NONE;
7808
7809 MovDelay[x][y] = 0;
7810 }
7811 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7812 {
7813 static int test_xy[7][2] =
7814 {
7815 { 0, -1 },
7816 { -1, 0 },
7817 { +1, 0 },
7818 { 0, +1 },
7819 { 0, -1 },
7820 { -1, 0 },
7821 { +1, 0 },
7822 };
7823 static int test_dir[7] =
7824 {
7825 MV_UP,
7826 MV_LEFT,
7827 MV_RIGHT,
7828 MV_DOWN,
7829 MV_UP,
7830 MV_LEFT,
7831 MV_RIGHT,
7832 };
7833 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7834 int move_preference = -1000000; /* start with very low preference */
7835 int new_move_dir = MV_NONE;
7836 int start_test = RND(4);
7837 int i;
7838
7839 for (i = 0; i < NUM_DIRECTIONS; i++)
7840 {
7841 int move_dir = test_dir[start_test + i];
7842 int move_dir_preference;
7843
7844 xx = x + test_xy[start_test + i][0];
7845 yy = y + test_xy[start_test + i][1];
7846
7847 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7848 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7849 {
7850 new_move_dir = move_dir;
7851
7852 break;
7853 }
7854
7855 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7856 continue;
7857
7858 move_dir_preference = -1 * RunnerVisit[xx][yy];
7859 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7860 move_dir_preference = PlayerVisit[xx][yy];
7861
7862 if (move_dir_preference > move_preference)
7863 {
7864 /* prefer field that has not been visited for the longest time */
7865 move_preference = move_dir_preference;
7866 new_move_dir = move_dir;
7867 }
7868 else if (move_dir_preference == move_preference &&
7869 move_dir == old_move_dir)
7870 {
7871 /* prefer last direction when all directions are preferred equally */
7872 move_preference = move_dir_preference;
7873 new_move_dir = move_dir;
7874 }
7875 }
7876
7877 MovDir[x][y] = new_move_dir;
7878 if (old_move_dir != new_move_dir)
7879 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7880 }
7881 }
7882
TurnRound(int x,int y)7883 static void TurnRound(int x, int y)
7884 {
7885 int direction = MovDir[x][y];
7886
7887 TurnRoundExt(x, y);
7888
7889 GfxDir[x][y] = MovDir[x][y];
7890
7891 if (direction != MovDir[x][y])
7892 GfxFrame[x][y] = 0;
7893
7894 if (MovDelay[x][y])
7895 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7896
7897 ResetGfxFrame(x, y, FALSE);
7898 }
7899
JustBeingPushed(int x,int y)7900 static boolean JustBeingPushed(int x, int y)
7901 {
7902 int i;
7903
7904 for (i = 0; i < MAX_PLAYERS; i++)
7905 {
7906 struct PlayerInfo *player = &stored_player[i];
7907
7908 if (player->active && player->is_pushing && player->MovPos)
7909 {
7910 int next_jx = player->jx + (player->jx - player->last_jx);
7911 int next_jy = player->jy + (player->jy - player->last_jy);
7912
7913 if (x == next_jx && y == next_jy)
7914 return TRUE;
7915 }
7916 }
7917
7918 return FALSE;
7919 }
7920
StartMoving(int x,int y)7921 void StartMoving(int x, int y)
7922 {
7923 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7924 int element = Feld[x][y];
7925
7926 if (Stop[x][y])
7927 return;
7928
7929 if (MovDelay[x][y] == 0)
7930 GfxAction[x][y] = ACTION_DEFAULT;
7931
7932 if (CAN_FALL(element) && y < lev_fieldy - 1)
7933 {
7934 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7935 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7936 if (JustBeingPushed(x, y))
7937 return;
7938
7939 if (element == EL_QUICKSAND_FULL)
7940 {
7941 if (IS_FREE(x, y + 1))
7942 {
7943 InitMovingField(x, y, MV_DOWN);
7944 started_moving = TRUE;
7945
7946 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7947 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7948 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7949 Store[x][y] = EL_ROCK;
7950 #else
7951 Store[x][y] = EL_ROCK;
7952 #endif
7953
7954 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7955 }
7956 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7957 {
7958 if (!MovDelay[x][y])
7959 {
7960 MovDelay[x][y] = TILEY + 1;
7961
7962 ResetGfxAnimation(x, y);
7963 ResetGfxAnimation(x, y + 1);
7964 }
7965
7966 if (MovDelay[x][y])
7967 {
7968 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7969 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7970
7971 MovDelay[x][y]--;
7972 if (MovDelay[x][y])
7973 return;
7974 }
7975
7976 Feld[x][y] = EL_QUICKSAND_EMPTY;
7977 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7978 Store[x][y + 1] = Store[x][y];
7979 Store[x][y] = 0;
7980
7981 PlayLevelSoundAction(x, y, ACTION_FILLING);
7982 }
7983 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7984 {
7985 if (!MovDelay[x][y])
7986 {
7987 MovDelay[x][y] = TILEY + 1;
7988
7989 ResetGfxAnimation(x, y);
7990 ResetGfxAnimation(x, y + 1);
7991 }
7992
7993 if (MovDelay[x][y])
7994 {
7995 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7996 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7997
7998 MovDelay[x][y]--;
7999 if (MovDelay[x][y])
8000 return;
8001 }
8002
8003 Feld[x][y] = EL_QUICKSAND_EMPTY;
8004 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8005 Store[x][y + 1] = Store[x][y];
8006 Store[x][y] = 0;
8007
8008 PlayLevelSoundAction(x, y, ACTION_FILLING);
8009 }
8010 }
8011 else if (element == EL_QUICKSAND_FAST_FULL)
8012 {
8013 if (IS_FREE(x, y + 1))
8014 {
8015 InitMovingField(x, y, MV_DOWN);
8016 started_moving = TRUE;
8017
8018 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8019 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8020 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8021 Store[x][y] = EL_ROCK;
8022 #else
8023 Store[x][y] = EL_ROCK;
8024 #endif
8025
8026 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8027 }
8028 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8029 {
8030 if (!MovDelay[x][y])
8031 {
8032 MovDelay[x][y] = TILEY + 1;
8033
8034 ResetGfxAnimation(x, y);
8035 ResetGfxAnimation(x, y + 1);
8036 }
8037
8038 if (MovDelay[x][y])
8039 {
8040 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8041 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8042
8043 MovDelay[x][y]--;
8044 if (MovDelay[x][y])
8045 return;
8046 }
8047
8048 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8049 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8050 Store[x][y + 1] = Store[x][y];
8051 Store[x][y] = 0;
8052
8053 PlayLevelSoundAction(x, y, ACTION_FILLING);
8054 }
8055 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8056 {
8057 if (!MovDelay[x][y])
8058 {
8059 MovDelay[x][y] = TILEY + 1;
8060
8061 ResetGfxAnimation(x, y);
8062 ResetGfxAnimation(x, y + 1);
8063 }
8064
8065 if (MovDelay[x][y])
8066 {
8067 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8068 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8069
8070 MovDelay[x][y]--;
8071 if (MovDelay[x][y])
8072 return;
8073 }
8074
8075 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8076 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8077 Store[x][y + 1] = Store[x][y];
8078 Store[x][y] = 0;
8079
8080 PlayLevelSoundAction(x, y, ACTION_FILLING);
8081 }
8082 }
8083 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8084 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8085 {
8086 InitMovingField(x, y, MV_DOWN);
8087 started_moving = TRUE;
8088
8089 Feld[x][y] = EL_QUICKSAND_FILLING;
8090 Store[x][y] = element;
8091
8092 PlayLevelSoundAction(x, y, ACTION_FILLING);
8093 }
8094 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8095 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8096 {
8097 InitMovingField(x, y, MV_DOWN);
8098 started_moving = TRUE;
8099
8100 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8101 Store[x][y] = element;
8102
8103 PlayLevelSoundAction(x, y, ACTION_FILLING);
8104 }
8105 else if (element == EL_MAGIC_WALL_FULL)
8106 {
8107 if (IS_FREE(x, y + 1))
8108 {
8109 InitMovingField(x, y, MV_DOWN);
8110 started_moving = TRUE;
8111
8112 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8113 Store[x][y] = EL_CHANGED(Store[x][y]);
8114 }
8115 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8116 {
8117 if (!MovDelay[x][y])
8118 MovDelay[x][y] = TILEY/4 + 1;
8119
8120 if (MovDelay[x][y])
8121 {
8122 MovDelay[x][y]--;
8123 if (MovDelay[x][y])
8124 return;
8125 }
8126
8127 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8128 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8129 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8130 Store[x][y] = 0;
8131 }
8132 }
8133 else if (element == EL_BD_MAGIC_WALL_FULL)
8134 {
8135 if (IS_FREE(x, y + 1))
8136 {
8137 InitMovingField(x, y, MV_DOWN);
8138 started_moving = TRUE;
8139
8140 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8141 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8142 }
8143 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8144 {
8145 if (!MovDelay[x][y])
8146 MovDelay[x][y] = TILEY/4 + 1;
8147
8148 if (MovDelay[x][y])
8149 {
8150 MovDelay[x][y]--;
8151 if (MovDelay[x][y])
8152 return;
8153 }
8154
8155 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8156 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8157 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8158 Store[x][y] = 0;
8159 }
8160 }
8161 else if (element == EL_DC_MAGIC_WALL_FULL)
8162 {
8163 if (IS_FREE(x, y + 1))
8164 {
8165 InitMovingField(x, y, MV_DOWN);
8166 started_moving = TRUE;
8167
8168 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8169 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8170 }
8171 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8172 {
8173 if (!MovDelay[x][y])
8174 MovDelay[x][y] = TILEY/4 + 1;
8175
8176 if (MovDelay[x][y])
8177 {
8178 MovDelay[x][y]--;
8179 if (MovDelay[x][y])
8180 return;
8181 }
8182
8183 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8184 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8185 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8186 Store[x][y] = 0;
8187 }
8188 }
8189 else if ((CAN_PASS_MAGIC_WALL(element) &&
8190 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8191 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8192 (CAN_PASS_DC_MAGIC_WALL(element) &&
8193 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8194
8195 {
8196 InitMovingField(x, y, MV_DOWN);
8197 started_moving = TRUE;
8198
8199 Feld[x][y] =
8200 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8201 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8202 EL_DC_MAGIC_WALL_FILLING);
8203 Store[x][y] = element;
8204 }
8205 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8206 {
8207 SplashAcid(x, y + 1);
8208
8209 InitMovingField(x, y, MV_DOWN);
8210 started_moving = TRUE;
8211
8212 Store[x][y] = EL_ACID;
8213 }
8214 else if (
8215 #if USE_FIX_IMPACT_COLLISION
8216 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8217 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8218 #else
8219 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8220 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8221 #endif
8222 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8223 CAN_FALL(element) && WasJustFalling[x][y] &&
8224 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8225
8226 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8227 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8228 (Feld[x][y + 1] == EL_BLOCKED)))
8229 {
8230 /* this is needed for a special case not covered by calling "Impact()"
8231 from "ContinueMoving()": if an element moves to a tile directly below
8232 another element which was just falling on that tile (which was empty
8233 in the previous frame), the falling element above would just stop
8234 instead of smashing the element below (in previous version, the above
8235 element was just checked for "moving" instead of "falling", resulting
8236 in incorrect smashes caused by horizontal movement of the above
8237 element; also, the case of the player being the element to smash was
8238 simply not covered here... :-/ ) */
8239
8240 CheckCollision[x][y] = 0;
8241 CheckImpact[x][y] = 0;
8242
8243 Impact(x, y);
8244 }
8245 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8246 {
8247 if (MovDir[x][y] == MV_NONE)
8248 {
8249 InitMovingField(x, y, MV_DOWN);
8250 started_moving = TRUE;
8251 }
8252 }
8253 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8254 {
8255 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8256 MovDir[x][y] = MV_DOWN;
8257
8258 InitMovingField(x, y, MV_DOWN);
8259 started_moving = TRUE;
8260 }
8261 else if (element == EL_AMOEBA_DROP)
8262 {
8263 Feld[x][y] = EL_AMOEBA_GROWING;
8264 Store[x][y] = EL_AMOEBA_WET;
8265 }
8266 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8267 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8268 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8269 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8270 {
8271 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8272 (IS_FREE(x - 1, y + 1) ||
8273 Feld[x - 1][y + 1] == EL_ACID));
8274 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8275 (IS_FREE(x + 1, y + 1) ||
8276 Feld[x + 1][y + 1] == EL_ACID));
8277 boolean can_fall_any = (can_fall_left || can_fall_right);
8278 boolean can_fall_both = (can_fall_left && can_fall_right);
8279 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8280
8281 #if USE_NEW_ALL_SLIPPERY
8282 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8283 {
8284 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8285 can_fall_right = FALSE;
8286 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8287 can_fall_left = FALSE;
8288 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8289 can_fall_right = FALSE;
8290 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8291 can_fall_left = FALSE;
8292
8293 can_fall_any = (can_fall_left || can_fall_right);
8294 can_fall_both = FALSE;
8295 }
8296 #else
8297 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8298 {
8299 if (slippery_type == SLIPPERY_ONLY_LEFT)
8300 can_fall_right = FALSE;
8301 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8302 can_fall_left = FALSE;
8303 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8304 can_fall_right = FALSE;
8305 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8306 can_fall_left = FALSE;
8307
8308 can_fall_any = (can_fall_left || can_fall_right);
8309 can_fall_both = (can_fall_left && can_fall_right);
8310 }
8311 #endif
8312
8313 #if USE_NEW_ALL_SLIPPERY
8314 #else
8315 #if USE_NEW_SP_SLIPPERY
8316 /* !!! better use the same properties as for custom elements here !!! */
8317 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8318 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8319 {
8320 can_fall_right = FALSE; /* slip down on left side */
8321 can_fall_both = FALSE;
8322 }
8323 #endif
8324 #endif
8325
8326 #if USE_NEW_ALL_SLIPPERY
8327 if (can_fall_both)
8328 {
8329 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8330 can_fall_right = FALSE; /* slip down on left side */
8331 else
8332 can_fall_left = !(can_fall_right = RND(2));
8333
8334 can_fall_both = FALSE;
8335 }
8336 #else
8337 if (can_fall_both)
8338 {
8339 if (game.emulation == EMU_BOULDERDASH ||
8340 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8341 can_fall_right = FALSE; /* slip down on left side */
8342 else
8343 can_fall_left = !(can_fall_right = RND(2));
8344
8345 can_fall_both = FALSE;
8346 }
8347 #endif
8348
8349 if (can_fall_any)
8350 {
8351 /* if not determined otherwise, prefer left side for slipping down */
8352 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8353 started_moving = TRUE;
8354 }
8355 }
8356 #if 0
8357 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8358 #else
8359 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8360 #endif
8361 {
8362 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8363 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8364 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8365 int belt_dir = game.belt_dir[belt_nr];
8366
8367 if ((belt_dir == MV_LEFT && left_is_free) ||
8368 (belt_dir == MV_RIGHT && right_is_free))
8369 {
8370 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8371
8372 InitMovingField(x, y, belt_dir);
8373 started_moving = TRUE;
8374
8375 Pushed[x][y] = TRUE;
8376 Pushed[nextx][y] = TRUE;
8377
8378 GfxAction[x][y] = ACTION_DEFAULT;
8379 }
8380 else
8381 {
8382 MovDir[x][y] = 0; /* if element was moving, stop it */
8383 }
8384 }
8385 }
8386
8387 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8388 #if 0
8389 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8390 #else
8391 if (CAN_MOVE(element) && !started_moving)
8392 #endif
8393 {
8394 int move_pattern = element_info[element].move_pattern;
8395 int newx, newy;
8396
8397 #if 0
8398 #if DEBUG
8399 if (MovDir[x][y] == MV_NONE)
8400 {
8401 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8402 x, y, element, element_info[element].token_name);
8403 printf("StartMoving(): This should never happen!\n");
8404 }
8405 #endif
8406 #endif
8407
8408 Moving2Blocked(x, y, &newx, &newy);
8409
8410 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8411 return;
8412
8413 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8414 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8415 {
8416 WasJustMoving[x][y] = 0;
8417 CheckCollision[x][y] = 0;
8418
8419 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8420
8421 if (Feld[x][y] != element) /* element has changed */
8422 return;
8423 }
8424
8425 if (!MovDelay[x][y]) /* start new movement phase */
8426 {
8427 /* all objects that can change their move direction after each step
8428 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8429
8430 if (element != EL_YAMYAM &&
8431 element != EL_DARK_YAMYAM &&
8432 element != EL_PACMAN &&
8433 !(move_pattern & MV_ANY_DIRECTION) &&
8434 move_pattern != MV_TURNING_LEFT &&
8435 move_pattern != MV_TURNING_RIGHT &&
8436 move_pattern != MV_TURNING_LEFT_RIGHT &&
8437 move_pattern != MV_TURNING_RIGHT_LEFT &&
8438 move_pattern != MV_TURNING_RANDOM)
8439 {
8440 TurnRound(x, y);
8441
8442 if (MovDelay[x][y] && (element == EL_BUG ||
8443 element == EL_SPACESHIP ||
8444 element == EL_SP_SNIKSNAK ||
8445 element == EL_SP_ELECTRON ||
8446 element == EL_MOLE))
8447 TEST_DrawLevelField(x, y);
8448 }
8449 }
8450
8451 if (MovDelay[x][y]) /* wait some time before next movement */
8452 {
8453 MovDelay[x][y]--;
8454
8455 if (element == EL_ROBOT ||
8456 element == EL_YAMYAM ||
8457 element == EL_DARK_YAMYAM)
8458 {
8459 DrawLevelElementAnimationIfNeeded(x, y, element);
8460 PlayLevelSoundAction(x, y, ACTION_WAITING);
8461 }
8462 else if (element == EL_SP_ELECTRON)
8463 DrawLevelElementAnimationIfNeeded(x, y, element);
8464 else if (element == EL_DRAGON)
8465 {
8466 int i;
8467 int dir = MovDir[x][y];
8468 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8469 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8470 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8471 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8472 dir == MV_UP ? IMG_FLAMES_1_UP :
8473 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8474 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8475
8476 GfxAction[x][y] = ACTION_ATTACKING;
8477
8478 if (IS_PLAYER(x, y))
8479 DrawPlayerField(x, y);
8480 else
8481 TEST_DrawLevelField(x, y);
8482
8483 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8484
8485 for (i = 1; i <= 3; i++)
8486 {
8487 int xx = x + i * dx;
8488 int yy = y + i * dy;
8489 int sx = SCREENX(xx);
8490 int sy = SCREENY(yy);
8491 int flame_graphic = graphic + (i - 1);
8492
8493 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8494 break;
8495
8496 if (MovDelay[x][y])
8497 {
8498 int flamed = MovingOrBlocked2Element(xx, yy);
8499
8500 /* !!! */
8501 #if 0
8502 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8503 Bang(xx, yy);
8504 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8505 RemoveMovingField(xx, yy);
8506 else
8507 RemoveField(xx, yy);
8508 #else
8509 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8510 Bang(xx, yy);
8511 else
8512 RemoveMovingField(xx, yy);
8513 #endif
8514
8515 ChangeDelay[xx][yy] = 0;
8516
8517 Feld[xx][yy] = EL_FLAMES;
8518
8519 if (IN_SCR_FIELD(sx, sy))
8520 {
8521 TEST_DrawLevelFieldCrumbled(xx, yy);
8522 DrawGraphic(sx, sy, flame_graphic, frame);
8523 }
8524 }
8525 else
8526 {
8527 if (Feld[xx][yy] == EL_FLAMES)
8528 Feld[xx][yy] = EL_EMPTY;
8529 TEST_DrawLevelField(xx, yy);
8530 }
8531 }
8532 }
8533
8534 if (MovDelay[x][y]) /* element still has to wait some time */
8535 {
8536 PlayLevelSoundAction(x, y, ACTION_WAITING);
8537
8538 return;
8539 }
8540 }
8541
8542 /* now make next step */
8543
8544 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8545
8546 if (DONT_COLLIDE_WITH(element) &&
8547 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8548 !PLAYER_ENEMY_PROTECTED(newx, newy))
8549 {
8550 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8551
8552 return;
8553 }
8554
8555 else if (CAN_MOVE_INTO_ACID(element) &&
8556 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8557 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8558 (MovDir[x][y] == MV_DOWN ||
8559 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8560 {
8561 SplashAcid(newx, newy);
8562 Store[x][y] = EL_ACID;
8563 }
8564 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8565 {
8566 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8567 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8568 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8569 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8570 {
8571 RemoveField(x, y);
8572 TEST_DrawLevelField(x, y);
8573
8574 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8575 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8576 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8577
8578 local_player->friends_still_needed--;
8579 if (!local_player->friends_still_needed &&
8580 !local_player->GameOver && AllPlayersGone)
8581 PlayerWins(local_player);
8582
8583 return;
8584 }
8585 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8586 {
8587 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8588 TEST_DrawLevelField(newx, newy);
8589 else
8590 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8591 }
8592 else if (!IS_FREE(newx, newy))
8593 {
8594 GfxAction[x][y] = ACTION_WAITING;
8595
8596 if (IS_PLAYER(x, y))
8597 DrawPlayerField(x, y);
8598 else
8599 TEST_DrawLevelField(x, y);
8600
8601 return;
8602 }
8603 }
8604 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8605 {
8606 if (IS_FOOD_PIG(Feld[newx][newy]))
8607 {
8608 if (IS_MOVING(newx, newy))
8609 RemoveMovingField(newx, newy);
8610 else
8611 {
8612 Feld[newx][newy] = EL_EMPTY;
8613 TEST_DrawLevelField(newx, newy);
8614 }
8615
8616 PlayLevelSound(x, y, SND_PIG_DIGGING);
8617 }
8618 else if (!IS_FREE(newx, newy))
8619 {
8620 if (IS_PLAYER(x, y))
8621 DrawPlayerField(x, y);
8622 else
8623 TEST_DrawLevelField(x, y);
8624
8625 return;
8626 }
8627 }
8628 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8629 {
8630 if (Store[x][y] != EL_EMPTY)
8631 {
8632 boolean can_clone = FALSE;
8633 int xx, yy;
8634
8635 /* check if element to clone is still there */
8636 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8637 {
8638 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8639 {
8640 can_clone = TRUE;
8641
8642 break;
8643 }
8644 }
8645
8646 /* cannot clone or target field not free anymore -- do not clone */
8647 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8648 Store[x][y] = EL_EMPTY;
8649 }
8650
8651 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8652 {
8653 if (IS_MV_DIAGONAL(MovDir[x][y]))
8654 {
8655 int diagonal_move_dir = MovDir[x][y];
8656 int stored = Store[x][y];
8657 int change_delay = 8;
8658 int graphic;
8659
8660 /* android is moving diagonally */
8661
8662 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8663
8664 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8665 GfxElement[x][y] = EL_EMC_ANDROID;
8666 GfxAction[x][y] = ACTION_SHRINKING;
8667 GfxDir[x][y] = diagonal_move_dir;
8668 ChangeDelay[x][y] = change_delay;
8669
8670 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8671 GfxDir[x][y]);
8672
8673 DrawLevelGraphicAnimation(x, y, graphic);
8674 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8675
8676 if (Feld[newx][newy] == EL_ACID)
8677 {
8678 SplashAcid(newx, newy);
8679
8680 return;
8681 }
8682
8683 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8684
8685 Store[newx][newy] = EL_EMC_ANDROID;
8686 GfxElement[newx][newy] = EL_EMC_ANDROID;
8687 GfxAction[newx][newy] = ACTION_GROWING;
8688 GfxDir[newx][newy] = diagonal_move_dir;
8689 ChangeDelay[newx][newy] = change_delay;
8690
8691 graphic = el_act_dir2img(GfxElement[newx][newy],
8692 GfxAction[newx][newy], GfxDir[newx][newy]);
8693
8694 DrawLevelGraphicAnimation(newx, newy, graphic);
8695 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8696
8697 return;
8698 }
8699 else
8700 {
8701 Feld[newx][newy] = EL_EMPTY;
8702 TEST_DrawLevelField(newx, newy);
8703
8704 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8705 }
8706 }
8707 else if (!IS_FREE(newx, newy))
8708 {
8709 #if 0
8710 if (IS_PLAYER(x, y))
8711 DrawPlayerField(x, y);
8712 else
8713 TEST_DrawLevelField(x, y);
8714 #endif
8715
8716 return;
8717 }
8718 }
8719 else if (IS_CUSTOM_ELEMENT(element) &&
8720 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8721 {
8722 #if 1
8723 if (!DigFieldByCE(newx, newy, element))
8724 return;
8725 #else
8726 int new_element = Feld[newx][newy];
8727
8728 if (!IS_FREE(newx, newy))
8729 {
8730 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8731 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8732 ACTION_BREAKING);
8733
8734 /* no element can dig solid indestructible elements */
8735 if (IS_INDESTRUCTIBLE(new_element) &&
8736 !IS_DIGGABLE(new_element) &&
8737 !IS_COLLECTIBLE(new_element))
8738 return;
8739
8740 if (AmoebaNr[newx][newy] &&
8741 (new_element == EL_AMOEBA_FULL ||
8742 new_element == EL_BD_AMOEBA ||
8743 new_element == EL_AMOEBA_GROWING))
8744 {
8745 AmoebaCnt[AmoebaNr[newx][newy]]--;
8746 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8747 }
8748
8749 if (IS_MOVING(newx, newy))
8750 RemoveMovingField(newx, newy);
8751 else
8752 {
8753 RemoveField(newx, newy);
8754 TEST_DrawLevelField(newx, newy);
8755 }
8756
8757 /* if digged element was about to explode, prevent the explosion */
8758 ExplodeField[newx][newy] = EX_TYPE_NONE;
8759
8760 PlayLevelSoundAction(x, y, action);
8761 }
8762
8763 Store[newx][newy] = EL_EMPTY;
8764
8765 #if 1
8766 /* this makes it possible to leave the removed element again */
8767 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8768 Store[newx][newy] = new_element;
8769 #else
8770 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8771 {
8772 int move_leave_element = element_info[element].move_leave_element;
8773
8774 /* this makes it possible to leave the removed element again */
8775 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8776 new_element : move_leave_element);
8777 }
8778 #endif
8779
8780 #endif
8781
8782 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8783 {
8784 RunnerVisit[x][y] = FrameCounter;
8785 PlayerVisit[x][y] /= 8; /* expire player visit path */
8786 }
8787 }
8788 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8789 {
8790 if (!IS_FREE(newx, newy))
8791 {
8792 if (IS_PLAYER(x, y))
8793 DrawPlayerField(x, y);
8794 else
8795 TEST_DrawLevelField(x, y);
8796
8797 return;
8798 }
8799 else
8800 {
8801 boolean wanna_flame = !RND(10);
8802 int dx = newx - x, dy = newy - y;
8803 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8804 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8805 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8806 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8807 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8808 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8809
8810 if ((wanna_flame ||
8811 IS_CLASSIC_ENEMY(element1) ||
8812 IS_CLASSIC_ENEMY(element2)) &&
8813 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8814 element1 != EL_FLAMES && element2 != EL_FLAMES)
8815 {
8816 ResetGfxAnimation(x, y);
8817 GfxAction[x][y] = ACTION_ATTACKING;
8818
8819 if (IS_PLAYER(x, y))
8820 DrawPlayerField(x, y);
8821 else
8822 TEST_DrawLevelField(x, y);
8823
8824 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8825
8826 MovDelay[x][y] = 50;
8827
8828 /* !!! */
8829 #if 0
8830 RemoveField(newx, newy);
8831 #endif
8832 Feld[newx][newy] = EL_FLAMES;
8833 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8834 {
8835 #if 0
8836 RemoveField(newx1, newy1);
8837 #endif
8838 Feld[newx1][newy1] = EL_FLAMES;
8839 }
8840 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8841 {
8842 #if 0
8843 RemoveField(newx2, newy2);
8844 #endif
8845 Feld[newx2][newy2] = EL_FLAMES;
8846 }
8847
8848 return;
8849 }
8850 }
8851 }
8852 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8853 Feld[newx][newy] == EL_DIAMOND)
8854 {
8855 if (IS_MOVING(newx, newy))
8856 RemoveMovingField(newx, newy);
8857 else
8858 {
8859 Feld[newx][newy] = EL_EMPTY;
8860 TEST_DrawLevelField(newx, newy);
8861 }
8862
8863 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8864 }
8865 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8866 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8867 {
8868 if (AmoebaNr[newx][newy])
8869 {
8870 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8871 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8872 Feld[newx][newy] == EL_BD_AMOEBA)
8873 AmoebaCnt[AmoebaNr[newx][newy]]--;
8874 }
8875
8876 #if 0
8877 /* !!! test !!! */
8878 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8879 {
8880 RemoveMovingField(newx, newy);
8881 }
8882 #else
8883 if (IS_MOVING(newx, newy))
8884 {
8885 RemoveMovingField(newx, newy);
8886 }
8887 #endif
8888 else
8889 {
8890 Feld[newx][newy] = EL_EMPTY;
8891 TEST_DrawLevelField(newx, newy);
8892 }
8893
8894 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8895 }
8896 else if ((element == EL_PACMAN || element == EL_MOLE)
8897 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8898 {
8899 if (AmoebaNr[newx][newy])
8900 {
8901 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8902 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8903 Feld[newx][newy] == EL_BD_AMOEBA)
8904 AmoebaCnt[AmoebaNr[newx][newy]]--;
8905 }
8906
8907 if (element == EL_MOLE)
8908 {
8909 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8910 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8911
8912 ResetGfxAnimation(x, y);
8913 GfxAction[x][y] = ACTION_DIGGING;
8914 TEST_DrawLevelField(x, y);
8915
8916 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8917
8918 return; /* wait for shrinking amoeba */
8919 }
8920 else /* element == EL_PACMAN */
8921 {
8922 Feld[newx][newy] = EL_EMPTY;
8923 TEST_DrawLevelField(newx, newy);
8924 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8925 }
8926 }
8927 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8928 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8929 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8930 {
8931 /* wait for shrinking amoeba to completely disappear */
8932 return;
8933 }
8934 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8935 {
8936 /* object was running against a wall */
8937
8938 TurnRound(x, y);
8939
8940 #if 0
8941 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8942 if (move_pattern & MV_ANY_DIRECTION &&
8943 move_pattern == MovDir[x][y])
8944 {
8945 int blocking_element =
8946 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8947
8948 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8949 MovDir[x][y]);
8950
8951 element = Feld[x][y]; /* element might have changed */
8952 }
8953 #endif
8954
8955 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8956 DrawLevelElementAnimation(x, y, element);
8957
8958 if (DONT_TOUCH(element))
8959 TestIfBadThingTouchesPlayer(x, y);
8960
8961 return;
8962 }
8963
8964 InitMovingField(x, y, MovDir[x][y]);
8965
8966 PlayLevelSoundAction(x, y, ACTION_MOVING);
8967 }
8968
8969 if (MovDir[x][y])
8970 ContinueMoving(x, y);
8971 }
8972
ContinueMoving(int x,int y)8973 void ContinueMoving(int x, int y)
8974 {
8975 int element = Feld[x][y];
8976 struct ElementInfo *ei = &element_info[element];
8977 int direction = MovDir[x][y];
8978 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8979 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8980 int newx = x + dx, newy = y + dy;
8981 int stored = Store[x][y];
8982 int stored_new = Store[newx][newy];
8983 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8984 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8985 boolean last_line = (newy == lev_fieldy - 1);
8986
8987 MovPos[x][y] += getElementMoveStepsize(x, y);
8988
8989 if (pushed_by_player) /* special case: moving object pushed by player */
8990 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8991
8992 if (ABS(MovPos[x][y]) < TILEX)
8993 {
8994 #if 0
8995 int ee = Feld[x][y];
8996 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8997 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8998
8999 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9000 x, y, ABS(MovPos[x][y]),
9001 ee, gg, ff,
9002 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9003 #endif
9004
9005 TEST_DrawLevelField(x, y);
9006
9007 return; /* element is still moving */
9008 }
9009
9010 /* element reached destination field */
9011
9012 Feld[x][y] = EL_EMPTY;
9013 Feld[newx][newy] = element;
9014 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9015
9016 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9017 {
9018 element = Feld[newx][newy] = EL_ACID;
9019 }
9020 else if (element == EL_MOLE)
9021 {
9022 Feld[x][y] = EL_SAND;
9023
9024 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9025 }
9026 else if (element == EL_QUICKSAND_FILLING)
9027 {
9028 element = Feld[newx][newy] = get_next_element(element);
9029 Store[newx][newy] = Store[x][y];
9030 }
9031 else if (element == EL_QUICKSAND_EMPTYING)
9032 {
9033 Feld[x][y] = get_next_element(element);
9034 element = Feld[newx][newy] = Store[x][y];
9035 }
9036 else if (element == EL_QUICKSAND_FAST_FILLING)
9037 {
9038 element = Feld[newx][newy] = get_next_element(element);
9039 Store[newx][newy] = Store[x][y];
9040 }
9041 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9042 {
9043 Feld[x][y] = get_next_element(element);
9044 element = Feld[newx][newy] = Store[x][y];
9045 }
9046 else if (element == EL_MAGIC_WALL_FILLING)
9047 {
9048 element = Feld[newx][newy] = get_next_element(element);
9049 if (!game.magic_wall_active)
9050 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9051 Store[newx][newy] = Store[x][y];
9052 }
9053 else if (element == EL_MAGIC_WALL_EMPTYING)
9054 {
9055 Feld[x][y] = get_next_element(element);
9056 if (!game.magic_wall_active)
9057 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9058 element = Feld[newx][newy] = Store[x][y];
9059
9060 #if USE_NEW_CUSTOM_VALUE
9061 InitField(newx, newy, FALSE);
9062 #endif
9063 }
9064 else if (element == EL_BD_MAGIC_WALL_FILLING)
9065 {
9066 element = Feld[newx][newy] = get_next_element(element);
9067 if (!game.magic_wall_active)
9068 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9069 Store[newx][newy] = Store[x][y];
9070 }
9071 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9072 {
9073 Feld[x][y] = get_next_element(element);
9074 if (!game.magic_wall_active)
9075 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9076 element = Feld[newx][newy] = Store[x][y];
9077
9078 #if USE_NEW_CUSTOM_VALUE
9079 InitField(newx, newy, FALSE);
9080 #endif
9081 }
9082 else if (element == EL_DC_MAGIC_WALL_FILLING)
9083 {
9084 element = Feld[newx][newy] = get_next_element(element);
9085 if (!game.magic_wall_active)
9086 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9087 Store[newx][newy] = Store[x][y];
9088 }
9089 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9090 {
9091 Feld[x][y] = get_next_element(element);
9092 if (!game.magic_wall_active)
9093 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9094 element = Feld[newx][newy] = Store[x][y];
9095
9096 #if USE_NEW_CUSTOM_VALUE
9097 InitField(newx, newy, FALSE);
9098 #endif
9099 }
9100 else if (element == EL_AMOEBA_DROPPING)
9101 {
9102 Feld[x][y] = get_next_element(element);
9103 element = Feld[newx][newy] = Store[x][y];
9104 }
9105 else if (element == EL_SOKOBAN_OBJECT)
9106 {
9107 if (Back[x][y])
9108 Feld[x][y] = Back[x][y];
9109
9110 if (Back[newx][newy])
9111 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9112
9113 Back[x][y] = Back[newx][newy] = 0;
9114 }
9115
9116 Store[x][y] = EL_EMPTY;
9117 MovPos[x][y] = 0;
9118 MovDir[x][y] = 0;
9119 MovDelay[x][y] = 0;
9120
9121 MovDelay[newx][newy] = 0;
9122
9123 if (CAN_CHANGE_OR_HAS_ACTION(element))
9124 {
9125 /* copy element change control values to new field */
9126 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9127 ChangePage[newx][newy] = ChangePage[x][y];
9128 ChangeCount[newx][newy] = ChangeCount[x][y];
9129 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9130 }
9131
9132 #if USE_NEW_CUSTOM_VALUE
9133 CustomValue[newx][newy] = CustomValue[x][y];
9134 #endif
9135
9136 ChangeDelay[x][y] = 0;
9137 ChangePage[x][y] = -1;
9138 ChangeCount[x][y] = 0;
9139 ChangeEvent[x][y] = -1;
9140
9141 #if USE_NEW_CUSTOM_VALUE
9142 CustomValue[x][y] = 0;
9143 #endif
9144
9145 /* copy animation control values to new field */
9146 GfxFrame[newx][newy] = GfxFrame[x][y];
9147 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9148 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9149 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9150
9151 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9152
9153 /* some elements can leave other elements behind after moving */
9154 #if 1
9155 if (ei->move_leave_element != EL_EMPTY &&
9156 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9157 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9158 #else
9159 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9160 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9161 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9162 #endif
9163 {
9164 int move_leave_element = ei->move_leave_element;
9165
9166 #if 1
9167 #if 1
9168 /* this makes it possible to leave the removed element again */
9169 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9170 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9171 #else
9172 /* this makes it possible to leave the removed element again */
9173 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9174 move_leave_element = stored;
9175 #endif
9176 #else
9177 /* this makes it possible to leave the removed element again */
9178 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9179 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9180 move_leave_element = stored;
9181 #endif
9182
9183 Feld[x][y] = move_leave_element;
9184
9185 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9186 MovDir[x][y] = direction;
9187
9188 InitField(x, y, FALSE);
9189
9190 if (GFX_CRUMBLED(Feld[x][y]))
9191 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9192
9193 if (ELEM_IS_PLAYER(move_leave_element))
9194 RelocatePlayer(x, y, move_leave_element);
9195 }
9196
9197 /* do this after checking for left-behind element */
9198 ResetGfxAnimation(x, y); /* reset animation values for old field */
9199
9200 if (!CAN_MOVE(element) ||
9201 (CAN_FALL(element) && direction == MV_DOWN &&
9202 (element == EL_SPRING ||
9203 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9204 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9205 GfxDir[x][y] = MovDir[newx][newy] = 0;
9206
9207 TEST_DrawLevelField(x, y);
9208 TEST_DrawLevelField(newx, newy);
9209
9210 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9211
9212 /* prevent pushed element from moving on in pushed direction */
9213 if (pushed_by_player && CAN_MOVE(element) &&
9214 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9215 !(element_info[element].move_pattern & direction))
9216 TurnRound(newx, newy);
9217
9218 /* prevent elements on conveyor belt from moving on in last direction */
9219 if (pushed_by_conveyor && CAN_FALL(element) &&
9220 direction & MV_HORIZONTAL)
9221 MovDir[newx][newy] = 0;
9222
9223 if (!pushed_by_player)
9224 {
9225 int nextx = newx + dx, nexty = newy + dy;
9226 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9227
9228 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9229
9230 if (CAN_FALL(element) && direction == MV_DOWN)
9231 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9232
9233 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9234 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9235
9236 #if USE_FIX_IMPACT_COLLISION
9237 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9238 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9239 #endif
9240 }
9241
9242 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9243 {
9244 TestIfBadThingTouchesPlayer(newx, newy);
9245 TestIfBadThingTouchesFriend(newx, newy);
9246
9247 if (!IS_CUSTOM_ELEMENT(element))
9248 TestIfBadThingTouchesOtherBadThing(newx, newy);
9249 }
9250 else if (element == EL_PENGUIN)
9251 TestIfFriendTouchesBadThing(newx, newy);
9252
9253 if (DONT_GET_HIT_BY(element))
9254 {
9255 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9256 }
9257
9258 /* give the player one last chance (one more frame) to move away */
9259 if (CAN_FALL(element) && direction == MV_DOWN &&
9260 (last_line || (!IS_FREE(x, newy + 1) &&
9261 (!IS_PLAYER(x, newy + 1) ||
9262 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9263 Impact(x, newy);
9264
9265 if (pushed_by_player && !game.use_change_when_pushing_bug)
9266 {
9267 int push_side = MV_DIR_OPPOSITE(direction);
9268 struct PlayerInfo *player = PLAYERINFO(x, y);
9269
9270 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9271 player->index_bit, push_side);
9272 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9273 player->index_bit, push_side);
9274 }
9275
9276 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9277 MovDelay[newx][newy] = 1;
9278
9279 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9280
9281 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9282
9283 #if 0
9284 if (ChangePage[newx][newy] != -1) /* delayed change */
9285 {
9286 int page = ChangePage[newx][newy];
9287 struct ElementChangeInfo *change = &ei->change_page[page];
9288
9289 ChangePage[newx][newy] = -1;
9290
9291 if (change->can_change)
9292 {
9293 if (ChangeElement(newx, newy, element, page))
9294 {
9295 if (change->post_change_function)
9296 change->post_change_function(newx, newy);
9297 }
9298 }
9299
9300 if (change->has_action)
9301 ExecuteCustomElementAction(newx, newy, element, page);
9302 }
9303 #endif
9304
9305 TestIfElementHitsCustomElement(newx, newy, direction);
9306 TestIfPlayerTouchesCustomElement(newx, newy);
9307 TestIfElementTouchesCustomElement(newx, newy);
9308
9309 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9310 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9311 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9312 MV_DIR_OPPOSITE(direction));
9313 }
9314
AmoebeNachbarNr(int ax,int ay)9315 int AmoebeNachbarNr(int ax, int ay)
9316 {
9317 int i;
9318 int element = Feld[ax][ay];
9319 int group_nr = 0;
9320 static int xy[4][2] =
9321 {
9322 { 0, -1 },
9323 { -1, 0 },
9324 { +1, 0 },
9325 { 0, +1 }
9326 };
9327
9328 for (i = 0; i < NUM_DIRECTIONS; i++)
9329 {
9330 int x = ax + xy[i][0];
9331 int y = ay + xy[i][1];
9332
9333 if (!IN_LEV_FIELD(x, y))
9334 continue;
9335
9336 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9337 group_nr = AmoebaNr[x][y];
9338 }
9339
9340 return group_nr;
9341 }
9342
AmoebenVereinigen(int ax,int ay)9343 void AmoebenVereinigen(int ax, int ay)
9344 {
9345 int i, x, y, xx, yy;
9346 int new_group_nr = AmoebaNr[ax][ay];
9347 static int xy[4][2] =
9348 {
9349 { 0, -1 },
9350 { -1, 0 },
9351 { +1, 0 },
9352 { 0, +1 }
9353 };
9354
9355 if (new_group_nr == 0)
9356 return;
9357
9358 for (i = 0; i < NUM_DIRECTIONS; i++)
9359 {
9360 x = ax + xy[i][0];
9361 y = ay + xy[i][1];
9362
9363 if (!IN_LEV_FIELD(x, y))
9364 continue;
9365
9366 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9367 Feld[x][y] == EL_BD_AMOEBA ||
9368 Feld[x][y] == EL_AMOEBA_DEAD) &&
9369 AmoebaNr[x][y] != new_group_nr)
9370 {
9371 int old_group_nr = AmoebaNr[x][y];
9372
9373 if (old_group_nr == 0)
9374 return;
9375
9376 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9377 AmoebaCnt[old_group_nr] = 0;
9378 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9379 AmoebaCnt2[old_group_nr] = 0;
9380
9381 SCAN_PLAYFIELD(xx, yy)
9382 {
9383 if (AmoebaNr[xx][yy] == old_group_nr)
9384 AmoebaNr[xx][yy] = new_group_nr;
9385 }
9386 }
9387 }
9388 }
9389
AmoebeUmwandeln(int ax,int ay)9390 void AmoebeUmwandeln(int ax, int ay)
9391 {
9392 int i, x, y;
9393
9394 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9395 {
9396 int group_nr = AmoebaNr[ax][ay];
9397
9398 #ifdef DEBUG
9399 if (group_nr == 0)
9400 {
9401 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9402 printf("AmoebeUmwandeln(): This should never happen!\n");
9403 return;
9404 }
9405 #endif
9406
9407 SCAN_PLAYFIELD(x, y)
9408 {
9409 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9410 {
9411 AmoebaNr[x][y] = 0;
9412 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9413 }
9414 }
9415
9416 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9417 SND_AMOEBA_TURNING_TO_GEM :
9418 SND_AMOEBA_TURNING_TO_ROCK));
9419 Bang(ax, ay);
9420 }
9421 else
9422 {
9423 static int xy[4][2] =
9424 {
9425 { 0, -1 },
9426 { -1, 0 },
9427 { +1, 0 },
9428 { 0, +1 }
9429 };
9430
9431 for (i = 0; i < NUM_DIRECTIONS; i++)
9432 {
9433 x = ax + xy[i][0];
9434 y = ay + xy[i][1];
9435
9436 if (!IN_LEV_FIELD(x, y))
9437 continue;
9438
9439 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9440 {
9441 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9442 SND_AMOEBA_TURNING_TO_GEM :
9443 SND_AMOEBA_TURNING_TO_ROCK));
9444 Bang(x, y);
9445 }
9446 }
9447 }
9448 }
9449
AmoebeUmwandelnBD(int ax,int ay,int new_element)9450 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9451 {
9452 int x, y;
9453 int group_nr = AmoebaNr[ax][ay];
9454 boolean done = FALSE;
9455
9456 #ifdef DEBUG
9457 if (group_nr == 0)
9458 {
9459 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9460 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9461 return;
9462 }
9463 #endif
9464
9465 SCAN_PLAYFIELD(x, y)
9466 {
9467 if (AmoebaNr[x][y] == group_nr &&
9468 (Feld[x][y] == EL_AMOEBA_DEAD ||
9469 Feld[x][y] == EL_BD_AMOEBA ||
9470 Feld[x][y] == EL_AMOEBA_GROWING))
9471 {
9472 AmoebaNr[x][y] = 0;
9473 Feld[x][y] = new_element;
9474 InitField(x, y, FALSE);
9475 TEST_DrawLevelField(x, y);
9476 done = TRUE;
9477 }
9478 }
9479
9480 if (done)
9481 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9482 SND_BD_AMOEBA_TURNING_TO_ROCK :
9483 SND_BD_AMOEBA_TURNING_TO_GEM));
9484 }
9485
AmoebeWaechst(int x,int y)9486 void AmoebeWaechst(int x, int y)
9487 {
9488 static unsigned long sound_delay = 0;
9489 static unsigned long sound_delay_value = 0;
9490
9491 if (!MovDelay[x][y]) /* start new growing cycle */
9492 {
9493 MovDelay[x][y] = 7;
9494
9495 if (DelayReached(&sound_delay, sound_delay_value))
9496 {
9497 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9498 sound_delay_value = 30;
9499 }
9500 }
9501
9502 if (MovDelay[x][y]) /* wait some time before growing bigger */
9503 {
9504 MovDelay[x][y]--;
9505 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9506 {
9507 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9508 6 - MovDelay[x][y]);
9509
9510 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9511 }
9512
9513 if (!MovDelay[x][y])
9514 {
9515 Feld[x][y] = Store[x][y];
9516 Store[x][y] = 0;
9517 TEST_DrawLevelField(x, y);
9518 }
9519 }
9520 }
9521
AmoebaDisappearing(int x,int y)9522 void AmoebaDisappearing(int x, int y)
9523 {
9524 static unsigned long sound_delay = 0;
9525 static unsigned long sound_delay_value = 0;
9526
9527 if (!MovDelay[x][y]) /* start new shrinking cycle */
9528 {
9529 MovDelay[x][y] = 7;
9530
9531 if (DelayReached(&sound_delay, sound_delay_value))
9532 sound_delay_value = 30;
9533 }
9534
9535 if (MovDelay[x][y]) /* wait some time before shrinking */
9536 {
9537 MovDelay[x][y]--;
9538 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9539 {
9540 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9541 6 - MovDelay[x][y]);
9542
9543 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9544 }
9545
9546 if (!MovDelay[x][y])
9547 {
9548 Feld[x][y] = EL_EMPTY;
9549 TEST_DrawLevelField(x, y);
9550
9551 /* don't let mole enter this field in this cycle;
9552 (give priority to objects falling to this field from above) */
9553 Stop[x][y] = TRUE;
9554 }
9555 }
9556 }
9557
AmoebeAbleger(int ax,int ay)9558 void AmoebeAbleger(int ax, int ay)
9559 {
9560 int i;
9561 int element = Feld[ax][ay];
9562 int graphic = el2img(element);
9563 int newax = ax, neway = ay;
9564 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9565 static int xy[4][2] =
9566 {
9567 { 0, -1 },
9568 { -1, 0 },
9569 { +1, 0 },
9570 { 0, +1 }
9571 };
9572
9573 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9574 {
9575 Feld[ax][ay] = EL_AMOEBA_DEAD;
9576 TEST_DrawLevelField(ax, ay);
9577 return;
9578 }
9579
9580 if (IS_ANIMATED(graphic))
9581 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9582
9583 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9584 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9585
9586 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9587 {
9588 MovDelay[ax][ay]--;
9589 if (MovDelay[ax][ay])
9590 return;
9591 }
9592
9593 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9594 {
9595 int start = RND(4);
9596 int x = ax + xy[start][0];
9597 int y = ay + xy[start][1];
9598
9599 if (!IN_LEV_FIELD(x, y))
9600 return;
9601
9602 if (IS_FREE(x, y) ||
9603 CAN_GROW_INTO(Feld[x][y]) ||
9604 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9605 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9606 {
9607 newax = x;
9608 neway = y;
9609 }
9610
9611 if (newax == ax && neway == ay)
9612 return;
9613 }
9614 else /* normal or "filled" (BD style) amoeba */
9615 {
9616 int start = RND(4);
9617 boolean waiting_for_player = FALSE;
9618
9619 for (i = 0; i < NUM_DIRECTIONS; i++)
9620 {
9621 int j = (start + i) % 4;
9622 int x = ax + xy[j][0];
9623 int y = ay + xy[j][1];
9624
9625 if (!IN_LEV_FIELD(x, y))
9626 continue;
9627
9628 if (IS_FREE(x, y) ||
9629 CAN_GROW_INTO(Feld[x][y]) ||
9630 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9631 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9632 {
9633 newax = x;
9634 neway = y;
9635 break;
9636 }
9637 else if (IS_PLAYER(x, y))
9638 waiting_for_player = TRUE;
9639 }
9640
9641 if (newax == ax && neway == ay) /* amoeba cannot grow */
9642 {
9643 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9644 {
9645 Feld[ax][ay] = EL_AMOEBA_DEAD;
9646 TEST_DrawLevelField(ax, ay);
9647 AmoebaCnt[AmoebaNr[ax][ay]]--;
9648
9649 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9650 {
9651 if (element == EL_AMOEBA_FULL)
9652 AmoebeUmwandeln(ax, ay);
9653 else if (element == EL_BD_AMOEBA)
9654 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9655 }
9656 }
9657 return;
9658 }
9659 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9660 {
9661 /* amoeba gets larger by growing in some direction */
9662
9663 int new_group_nr = AmoebaNr[ax][ay];
9664
9665 #ifdef DEBUG
9666 if (new_group_nr == 0)
9667 {
9668 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9669 printf("AmoebeAbleger(): This should never happen!\n");
9670 return;
9671 }
9672 #endif
9673
9674 AmoebaNr[newax][neway] = new_group_nr;
9675 AmoebaCnt[new_group_nr]++;
9676 AmoebaCnt2[new_group_nr]++;
9677
9678 /* if amoeba touches other amoeba(s) after growing, unify them */
9679 AmoebenVereinigen(newax, neway);
9680
9681 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9682 {
9683 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9684 return;
9685 }
9686 }
9687 }
9688
9689 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9690 (neway == lev_fieldy - 1 && newax != ax))
9691 {
9692 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9693 Store[newax][neway] = element;
9694 }
9695 else if (neway == ay || element == EL_EMC_DRIPPER)
9696 {
9697 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9698
9699 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9700 }
9701 else
9702 {
9703 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9704 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9705 Store[ax][ay] = EL_AMOEBA_DROP;
9706 ContinueMoving(ax, ay);
9707 return;
9708 }
9709
9710 TEST_DrawLevelField(newax, neway);
9711 }
9712
Life(int ax,int ay)9713 void Life(int ax, int ay)
9714 {
9715 int x1, y1, x2, y2;
9716 int life_time = 40;
9717 int element = Feld[ax][ay];
9718 int graphic = el2img(element);
9719 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9720 level.biomaze);
9721 boolean changed = FALSE;
9722
9723 if (IS_ANIMATED(graphic))
9724 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9725
9726 if (Stop[ax][ay])
9727 return;
9728
9729 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9730 MovDelay[ax][ay] = life_time;
9731
9732 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9733 {
9734 MovDelay[ax][ay]--;
9735 if (MovDelay[ax][ay])
9736 return;
9737 }
9738
9739 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9740 {
9741 int xx = ax+x1, yy = ay+y1;
9742 int nachbarn = 0;
9743
9744 if (!IN_LEV_FIELD(xx, yy))
9745 continue;
9746
9747 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9748 {
9749 int x = xx+x2, y = yy+y2;
9750
9751 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9752 continue;
9753
9754 if (((Feld[x][y] == element ||
9755 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9756 !Stop[x][y]) ||
9757 (IS_FREE(x, y) && Stop[x][y]))
9758 nachbarn++;
9759 }
9760
9761 if (xx == ax && yy == ay) /* field in the middle */
9762 {
9763 if (nachbarn < life_parameter[0] ||
9764 nachbarn > life_parameter[1])
9765 {
9766 Feld[xx][yy] = EL_EMPTY;
9767 if (!Stop[xx][yy])
9768 TEST_DrawLevelField(xx, yy);
9769 Stop[xx][yy] = TRUE;
9770 changed = TRUE;
9771 }
9772 }
9773 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9774 { /* free border field */
9775 if (nachbarn >= life_parameter[2] &&
9776 nachbarn <= life_parameter[3])
9777 {
9778 Feld[xx][yy] = element;
9779 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9780 if (!Stop[xx][yy])
9781 TEST_DrawLevelField(xx, yy);
9782 Stop[xx][yy] = TRUE;
9783 changed = TRUE;
9784 }
9785 }
9786 }
9787
9788 if (changed)
9789 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9790 SND_GAME_OF_LIFE_GROWING);
9791 }
9792
InitRobotWheel(int x,int y)9793 static void InitRobotWheel(int x, int y)
9794 {
9795 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9796 }
9797
RunRobotWheel(int x,int y)9798 static void RunRobotWheel(int x, int y)
9799 {
9800 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9801 }
9802
StopRobotWheel(int x,int y)9803 static void StopRobotWheel(int x, int y)
9804 {
9805 if (ZX == x && ZY == y)
9806 {
9807 ZX = ZY = -1;
9808
9809 game.robot_wheel_active = FALSE;
9810 }
9811 }
9812
InitTimegateWheel(int x,int y)9813 static void InitTimegateWheel(int x, int y)
9814 {
9815 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9816 }
9817
RunTimegateWheel(int x,int y)9818 static void RunTimegateWheel(int x, int y)
9819 {
9820 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9821 }
9822
InitMagicBallDelay(int x,int y)9823 static void InitMagicBallDelay(int x, int y)
9824 {
9825 #if 1
9826 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9827 #else
9828 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9829 #endif
9830 }
9831
ActivateMagicBall(int bx,int by)9832 static void ActivateMagicBall(int bx, int by)
9833 {
9834 int x, y;
9835
9836 if (level.ball_random)
9837 {
9838 int pos_border = RND(8); /* select one of the eight border elements */
9839 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9840 int xx = pos_content % 3;
9841 int yy = pos_content / 3;
9842
9843 x = bx - 1 + xx;
9844 y = by - 1 + yy;
9845
9846 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9847 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9848 }
9849 else
9850 {
9851 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9852 {
9853 int xx = x - bx + 1;
9854 int yy = y - by + 1;
9855
9856 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9857 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9858 }
9859 }
9860
9861 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9862 }
9863
CheckExit(int x,int y)9864 void CheckExit(int x, int y)
9865 {
9866 if (local_player->gems_still_needed > 0 ||
9867 local_player->sokobanfields_still_needed > 0 ||
9868 local_player->lights_still_needed > 0)
9869 {
9870 int element = Feld[x][y];
9871 int graphic = el2img(element);
9872
9873 if (IS_ANIMATED(graphic))
9874 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9875
9876 return;
9877 }
9878
9879 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9880 return;
9881
9882 Feld[x][y] = EL_EXIT_OPENING;
9883
9884 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9885 }
9886
CheckExitEM(int x,int y)9887 void CheckExitEM(int x, int y)
9888 {
9889 if (local_player->gems_still_needed > 0 ||
9890 local_player->sokobanfields_still_needed > 0 ||
9891 local_player->lights_still_needed > 0)
9892 {
9893 int element = Feld[x][y];
9894 int graphic = el2img(element);
9895
9896 if (IS_ANIMATED(graphic))
9897 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9898
9899 return;
9900 }
9901
9902 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9903 return;
9904
9905 Feld[x][y] = EL_EM_EXIT_OPENING;
9906
9907 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9908 }
9909
CheckExitSteel(int x,int y)9910 void CheckExitSteel(int x, int y)
9911 {
9912 if (local_player->gems_still_needed > 0 ||
9913 local_player->sokobanfields_still_needed > 0 ||
9914 local_player->lights_still_needed > 0)
9915 {
9916 int element = Feld[x][y];
9917 int graphic = el2img(element);
9918
9919 if (IS_ANIMATED(graphic))
9920 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9921
9922 return;
9923 }
9924
9925 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9926 return;
9927
9928 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9929
9930 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9931 }
9932
CheckExitSteelEM(int x,int y)9933 void CheckExitSteelEM(int x, int y)
9934 {
9935 if (local_player->gems_still_needed > 0 ||
9936 local_player->sokobanfields_still_needed > 0 ||
9937 local_player->lights_still_needed > 0)
9938 {
9939 int element = Feld[x][y];
9940 int graphic = el2img(element);
9941
9942 if (IS_ANIMATED(graphic))
9943 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9944
9945 return;
9946 }
9947
9948 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9949 return;
9950
9951 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9952
9953 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9954 }
9955
CheckExitSP(int x,int y)9956 void CheckExitSP(int x, int y)
9957 {
9958 if (local_player->gems_still_needed > 0)
9959 {
9960 int element = Feld[x][y];
9961 int graphic = el2img(element);
9962
9963 if (IS_ANIMATED(graphic))
9964 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9965
9966 return;
9967 }
9968
9969 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9970 return;
9971
9972 Feld[x][y] = EL_SP_EXIT_OPENING;
9973
9974 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9975 }
9976
CloseAllOpenTimegates()9977 static void CloseAllOpenTimegates()
9978 {
9979 int x, y;
9980
9981 SCAN_PLAYFIELD(x, y)
9982 {
9983 int element = Feld[x][y];
9984
9985 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9986 {
9987 Feld[x][y] = EL_TIMEGATE_CLOSING;
9988
9989 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9990 }
9991 }
9992 }
9993
DrawTwinkleOnField(int x,int y)9994 void DrawTwinkleOnField(int x, int y)
9995 {
9996 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9997 return;
9998
9999 if (Feld[x][y] == EL_BD_DIAMOND)
10000 return;
10001
10002 if (MovDelay[x][y] == 0) /* next animation frame */
10003 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10004
10005 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10006 {
10007 MovDelay[x][y]--;
10008
10009 DrawLevelElementAnimation(x, y, Feld[x][y]);
10010
10011 if (MovDelay[x][y] != 0)
10012 {
10013 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10014 10 - MovDelay[x][y]);
10015
10016 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10017 }
10018 }
10019 }
10020
MauerWaechst(int x,int y)10021 void MauerWaechst(int x, int y)
10022 {
10023 int delay = 6;
10024
10025 if (!MovDelay[x][y]) /* next animation frame */
10026 MovDelay[x][y] = 3 * delay;
10027
10028 if (MovDelay[x][y]) /* wait some time before next frame */
10029 {
10030 MovDelay[x][y]--;
10031
10032 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10033 {
10034 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10035 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10036
10037 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10038 }
10039
10040 if (!MovDelay[x][y])
10041 {
10042 if (MovDir[x][y] == MV_LEFT)
10043 {
10044 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10045 TEST_DrawLevelField(x - 1, y);
10046 }
10047 else if (MovDir[x][y] == MV_RIGHT)
10048 {
10049 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10050 TEST_DrawLevelField(x + 1, y);
10051 }
10052 else if (MovDir[x][y] == MV_UP)
10053 {
10054 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10055 TEST_DrawLevelField(x, y - 1);
10056 }
10057 else
10058 {
10059 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10060 TEST_DrawLevelField(x, y + 1);
10061 }
10062
10063 Feld[x][y] = Store[x][y];
10064 Store[x][y] = 0;
10065 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10066 TEST_DrawLevelField(x, y);
10067 }
10068 }
10069 }
10070
MauerAbleger(int ax,int ay)10071 void MauerAbleger(int ax, int ay)
10072 {
10073 int element = Feld[ax][ay];
10074 int graphic = el2img(element);
10075 boolean oben_frei = FALSE, unten_frei = FALSE;
10076 boolean links_frei = FALSE, rechts_frei = FALSE;
10077 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10078 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10079 boolean new_wall = FALSE;
10080
10081 if (IS_ANIMATED(graphic))
10082 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10083
10084 if (!MovDelay[ax][ay]) /* start building new wall */
10085 MovDelay[ax][ay] = 6;
10086
10087 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10088 {
10089 MovDelay[ax][ay]--;
10090 if (MovDelay[ax][ay])
10091 return;
10092 }
10093
10094 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10095 oben_frei = TRUE;
10096 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10097 unten_frei = TRUE;
10098 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10099 links_frei = TRUE;
10100 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10101 rechts_frei = TRUE;
10102
10103 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10104 element == EL_EXPANDABLE_WALL_ANY)
10105 {
10106 if (oben_frei)
10107 {
10108 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10109 Store[ax][ay-1] = element;
10110 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10111 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10112 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10113 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10114 new_wall = TRUE;
10115 }
10116 if (unten_frei)
10117 {
10118 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10119 Store[ax][ay+1] = element;
10120 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10121 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10122 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10123 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10124 new_wall = TRUE;
10125 }
10126 }
10127
10128 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10129 element == EL_EXPANDABLE_WALL_ANY ||
10130 element == EL_EXPANDABLE_WALL ||
10131 element == EL_BD_EXPANDABLE_WALL)
10132 {
10133 if (links_frei)
10134 {
10135 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10136 Store[ax-1][ay] = element;
10137 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10138 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10139 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10140 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10141 new_wall = TRUE;
10142 }
10143
10144 if (rechts_frei)
10145 {
10146 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10147 Store[ax+1][ay] = element;
10148 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10149 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10150 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10151 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10152 new_wall = TRUE;
10153 }
10154 }
10155
10156 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10157 TEST_DrawLevelField(ax, ay);
10158
10159 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10160 oben_massiv = TRUE;
10161 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10162 unten_massiv = TRUE;
10163 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10164 links_massiv = TRUE;
10165 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10166 rechts_massiv = TRUE;
10167
10168 if (((oben_massiv && unten_massiv) ||
10169 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10170 element == EL_EXPANDABLE_WALL) &&
10171 ((links_massiv && rechts_massiv) ||
10172 element == EL_EXPANDABLE_WALL_VERTICAL))
10173 Feld[ax][ay] = EL_WALL;
10174
10175 if (new_wall)
10176 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10177 }
10178
MauerAblegerStahl(int ax,int ay)10179 void MauerAblegerStahl(int ax, int ay)
10180 {
10181 int element = Feld[ax][ay];
10182 int graphic = el2img(element);
10183 boolean oben_frei = FALSE, unten_frei = FALSE;
10184 boolean links_frei = FALSE, rechts_frei = FALSE;
10185 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10186 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10187 boolean new_wall = FALSE;
10188
10189 if (IS_ANIMATED(graphic))
10190 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10191
10192 if (!MovDelay[ax][ay]) /* start building new wall */
10193 MovDelay[ax][ay] = 6;
10194
10195 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10196 {
10197 MovDelay[ax][ay]--;
10198 if (MovDelay[ax][ay])
10199 return;
10200 }
10201
10202 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10203 oben_frei = TRUE;
10204 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10205 unten_frei = TRUE;
10206 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10207 links_frei = TRUE;
10208 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10209 rechts_frei = TRUE;
10210
10211 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10212 element == EL_EXPANDABLE_STEELWALL_ANY)
10213 {
10214 if (oben_frei)
10215 {
10216 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10217 Store[ax][ay-1] = element;
10218 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10219 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10220 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10221 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10222 new_wall = TRUE;
10223 }
10224 if (unten_frei)
10225 {
10226 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10227 Store[ax][ay+1] = element;
10228 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10229 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10230 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10231 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10232 new_wall = TRUE;
10233 }
10234 }
10235
10236 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10237 element == EL_EXPANDABLE_STEELWALL_ANY)
10238 {
10239 if (links_frei)
10240 {
10241 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10242 Store[ax-1][ay] = element;
10243 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10244 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10245 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10246 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10247 new_wall = TRUE;
10248 }
10249
10250 if (rechts_frei)
10251 {
10252 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10253 Store[ax+1][ay] = element;
10254 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10255 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10256 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10257 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10258 new_wall = TRUE;
10259 }
10260 }
10261
10262 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10263 oben_massiv = TRUE;
10264 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10265 unten_massiv = TRUE;
10266 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10267 links_massiv = TRUE;
10268 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10269 rechts_massiv = TRUE;
10270
10271 if (((oben_massiv && unten_massiv) ||
10272 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10273 ((links_massiv && rechts_massiv) ||
10274 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10275 Feld[ax][ay] = EL_STEELWALL;
10276
10277 if (new_wall)
10278 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10279 }
10280
CheckForDragon(int x,int y)10281 void CheckForDragon(int x, int y)
10282 {
10283 int i, j;
10284 boolean dragon_found = FALSE;
10285 static int xy[4][2] =
10286 {
10287 { 0, -1 },
10288 { -1, 0 },
10289 { +1, 0 },
10290 { 0, +1 }
10291 };
10292
10293 for (i = 0; i < NUM_DIRECTIONS; i++)
10294 {
10295 for (j = 0; j < 4; j++)
10296 {
10297 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10298
10299 if (IN_LEV_FIELD(xx, yy) &&
10300 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10301 {
10302 if (Feld[xx][yy] == EL_DRAGON)
10303 dragon_found = TRUE;
10304 }
10305 else
10306 break;
10307 }
10308 }
10309
10310 if (!dragon_found)
10311 {
10312 for (i = 0; i < NUM_DIRECTIONS; i++)
10313 {
10314 for (j = 0; j < 3; j++)
10315 {
10316 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10317
10318 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10319 {
10320 Feld[xx][yy] = EL_EMPTY;
10321 TEST_DrawLevelField(xx, yy);
10322 }
10323 else
10324 break;
10325 }
10326 }
10327 }
10328 }
10329
InitBuggyBase(int x,int y)10330 static void InitBuggyBase(int x, int y)
10331 {
10332 int element = Feld[x][y];
10333 int activating_delay = FRAMES_PER_SECOND / 4;
10334
10335 ChangeDelay[x][y] =
10336 (element == EL_SP_BUGGY_BASE ?
10337 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10338 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10339 activating_delay :
10340 element == EL_SP_BUGGY_BASE_ACTIVE ?
10341 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10342 }
10343
WarnBuggyBase(int x,int y)10344 static void WarnBuggyBase(int x, int y)
10345 {
10346 int i;
10347 static int xy[4][2] =
10348 {
10349 { 0, -1 },
10350 { -1, 0 },
10351 { +1, 0 },
10352 { 0, +1 }
10353 };
10354
10355 for (i = 0; i < NUM_DIRECTIONS; i++)
10356 {
10357 int xx = x + xy[i][0];
10358 int yy = y + xy[i][1];
10359
10360 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10361 {
10362 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10363
10364 break;
10365 }
10366 }
10367 }
10368
InitTrap(int x,int y)10369 static void InitTrap(int x, int y)
10370 {
10371 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10372 }
10373
ActivateTrap(int x,int y)10374 static void ActivateTrap(int x, int y)
10375 {
10376 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10377 }
10378
ChangeActiveTrap(int x,int y)10379 static void ChangeActiveTrap(int x, int y)
10380 {
10381 int graphic = IMG_TRAP_ACTIVE;
10382
10383 /* if new animation frame was drawn, correct crumbled sand border */
10384 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10385 TEST_DrawLevelFieldCrumbled(x, y);
10386 }
10387
getSpecialActionElement(int element,int number,int base_element)10388 static int getSpecialActionElement(int element, int number, int base_element)
10389 {
10390 return (element != EL_EMPTY ? element :
10391 number != -1 ? base_element + number - 1 :
10392 EL_EMPTY);
10393 }
10394
getModifiedActionNumber(int value_old,int operator,int operand,int value_min,int value_max)10395 static int getModifiedActionNumber(int value_old, int operator, int operand,
10396 int value_min, int value_max)
10397 {
10398 int value_new = (operator == CA_MODE_SET ? operand :
10399 operator == CA_MODE_ADD ? value_old + operand :
10400 operator == CA_MODE_SUBTRACT ? value_old - operand :
10401 operator == CA_MODE_MULTIPLY ? value_old * operand :
10402 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10403 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10404 value_old);
10405
10406 return (value_new < value_min ? value_min :
10407 value_new > value_max ? value_max :
10408 value_new);
10409 }
10410
ExecuteCustomElementAction(int x,int y,int element,int page)10411 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10412 {
10413 struct ElementInfo *ei = &element_info[element];
10414 struct ElementChangeInfo *change = &ei->change_page[page];
10415 int target_element = change->target_element;
10416 int action_type = change->action_type;
10417 int action_mode = change->action_mode;
10418 int action_arg = change->action_arg;
10419 int action_element = change->action_element;
10420 int i;
10421
10422 if (!change->has_action)
10423 return;
10424
10425 /* ---------- determine action paramater values -------------------------- */
10426
10427 int level_time_value =
10428 (level.time > 0 ? TimeLeft :
10429 TimePlayed);
10430
10431 int action_arg_element_raw =
10432 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10433 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10434 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10435 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10436 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10437 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10438 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10439 EL_EMPTY);
10440 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10441
10442 #if 0
10443 if (action_arg_element_raw == EL_GROUP_START)
10444 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10445 #endif
10446
10447 int action_arg_direction =
10448 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10449 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10450 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10451 change->actual_trigger_side :
10452 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10453 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10454 MV_NONE);
10455
10456 int action_arg_number_min =
10457 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10458 CA_ARG_MIN);
10459
10460 int action_arg_number_max =
10461 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10462 action_type == CA_SET_LEVEL_GEMS ? 999 :
10463 action_type == CA_SET_LEVEL_TIME ? 9999 :
10464 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10465 action_type == CA_SET_CE_VALUE ? 9999 :
10466 action_type == CA_SET_CE_SCORE ? 9999 :
10467 CA_ARG_MAX);
10468
10469 int action_arg_number_reset =
10470 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10471 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10472 action_type == CA_SET_LEVEL_TIME ? level.time :
10473 action_type == CA_SET_LEVEL_SCORE ? 0 :
10474 #if USE_NEW_CUSTOM_VALUE
10475 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10476 #else
10477 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10478 #endif
10479 action_type == CA_SET_CE_SCORE ? 0 :
10480 0);
10481
10482 int action_arg_number =
10483 (action_arg <= CA_ARG_MAX ? action_arg :
10484 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10485 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10486 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10487 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10488 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10489 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10490 #if USE_NEW_CUSTOM_VALUE
10491 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10492 #else
10493 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10494 #endif
10495 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10496 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10497 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10498 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10499 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10500 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10501 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10502 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10503 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10504 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10505 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10506 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10507 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10508 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10509 -1);
10510
10511 int action_arg_number_old =
10512 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10513 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10514 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10515 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10516 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10517 0);
10518
10519 int action_arg_number_new =
10520 getModifiedActionNumber(action_arg_number_old,
10521 action_mode, action_arg_number,
10522 action_arg_number_min, action_arg_number_max);
10523
10524 #if 1
10525 int trigger_player_bits =
10526 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10527 change->actual_trigger_player_bits : change->trigger_player);
10528 #else
10529 int trigger_player_bits =
10530 (change->actual_trigger_player >= EL_PLAYER_1 &&
10531 change->actual_trigger_player <= EL_PLAYER_4 ?
10532 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10533 PLAYER_BITS_ANY);
10534 #endif
10535
10536 int action_arg_player_bits =
10537 (action_arg >= CA_ARG_PLAYER_1 &&
10538 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10539 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10540 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10541 PLAYER_BITS_ANY);
10542
10543 /* ---------- execute action -------------------------------------------- */
10544
10545 switch (action_type)
10546 {
10547 case CA_NO_ACTION:
10548 {
10549 return;
10550 }
10551
10552 /* ---------- level actions ------------------------------------------- */
10553
10554 case CA_RESTART_LEVEL:
10555 {
10556 game.restart_level = TRUE;
10557
10558 break;
10559 }
10560
10561 case CA_SHOW_ENVELOPE:
10562 {
10563 int element = getSpecialActionElement(action_arg_element,
10564 action_arg_number, EL_ENVELOPE_1);
10565
10566 if (IS_ENVELOPE(element))
10567 local_player->show_envelope = element;
10568
10569 break;
10570 }
10571
10572 case CA_SET_LEVEL_TIME:
10573 {
10574 if (level.time > 0) /* only modify limited time value */
10575 {
10576 TimeLeft = action_arg_number_new;
10577
10578 #if 1
10579 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10580
10581 DisplayGameControlValues();
10582 #else
10583 DrawGameValue_Time(TimeLeft);
10584 #endif
10585
10586 if (!TimeLeft && setup.time_limit)
10587 for (i = 0; i < MAX_PLAYERS; i++)
10588 KillPlayer(&stored_player[i]);
10589 }
10590
10591 break;
10592 }
10593
10594 case CA_SET_LEVEL_SCORE:
10595 {
10596 local_player->score = action_arg_number_new;
10597
10598 #if 1
10599 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10600
10601 DisplayGameControlValues();
10602 #else
10603 DrawGameValue_Score(local_player->score);
10604 #endif
10605
10606 break;
10607 }
10608
10609 case CA_SET_LEVEL_GEMS:
10610 {
10611 local_player->gems_still_needed = action_arg_number_new;
10612
10613 #if 1
10614 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10615
10616 DisplayGameControlValues();
10617 #else
10618 DrawGameValue_Emeralds(local_player->gems_still_needed);
10619 #endif
10620
10621 break;
10622 }
10623
10624 #if !USE_PLAYER_GRAVITY
10625 case CA_SET_LEVEL_GRAVITY:
10626 {
10627 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10628 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10629 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10630 game.gravity);
10631 break;
10632 }
10633 #endif
10634
10635 case CA_SET_LEVEL_WIND:
10636 {
10637 game.wind_direction = action_arg_direction;
10638
10639 break;
10640 }
10641
10642 case CA_SET_LEVEL_RANDOM_SEED:
10643 {
10644 #if 1
10645 /* ensure that setting a new random seed while playing is predictable */
10646 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10647 #else
10648 InitRND(action_arg_number_new);
10649 #endif
10650
10651 #if 0
10652 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10653 #endif
10654
10655 #if 0
10656 {
10657 int i;
10658
10659 printf("::: ");
10660 for (i = 0; i < 9; i++)
10661 printf("%d, ", RND(2));
10662 printf("\n");
10663 }
10664 #endif
10665
10666 break;
10667 }
10668
10669 /* ---------- player actions ------------------------------------------ */
10670
10671 case CA_MOVE_PLAYER:
10672 {
10673 /* automatically move to the next field in specified direction */
10674 for (i = 0; i < MAX_PLAYERS; i++)
10675 if (trigger_player_bits & (1 << i))
10676 stored_player[i].programmed_action = action_arg_direction;
10677
10678 break;
10679 }
10680
10681 case CA_EXIT_PLAYER:
10682 {
10683 for (i = 0; i < MAX_PLAYERS; i++)
10684 if (action_arg_player_bits & (1 << i))
10685 PlayerWins(&stored_player[i]);
10686
10687 break;
10688 }
10689
10690 case CA_KILL_PLAYER:
10691 {
10692 for (i = 0; i < MAX_PLAYERS; i++)
10693 if (action_arg_player_bits & (1 << i))
10694 KillPlayer(&stored_player[i]);
10695
10696 break;
10697 }
10698
10699 case CA_SET_PLAYER_KEYS:
10700 {
10701 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10702 int element = getSpecialActionElement(action_arg_element,
10703 action_arg_number, EL_KEY_1);
10704
10705 if (IS_KEY(element))
10706 {
10707 for (i = 0; i < MAX_PLAYERS; i++)
10708 {
10709 if (trigger_player_bits & (1 << i))
10710 {
10711 stored_player[i].key[KEY_NR(element)] = key_state;
10712
10713 DrawGameDoorValues();
10714 }
10715 }
10716 }
10717
10718 break;
10719 }
10720
10721 case CA_SET_PLAYER_SPEED:
10722 {
10723 #if 0
10724 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10725 #endif
10726
10727 for (i = 0; i < MAX_PLAYERS; i++)
10728 {
10729 if (trigger_player_bits & (1 << i))
10730 {
10731 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10732
10733 if (action_arg == CA_ARG_SPEED_FASTER &&
10734 stored_player[i].cannot_move)
10735 {
10736 action_arg_number = STEPSIZE_VERY_SLOW;
10737 }
10738 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10739 action_arg == CA_ARG_SPEED_FASTER)
10740 {
10741 action_arg_number = 2;
10742 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10743 CA_MODE_MULTIPLY);
10744 }
10745 else if (action_arg == CA_ARG_NUMBER_RESET)
10746 {
10747 action_arg_number = level.initial_player_stepsize[i];
10748 }
10749
10750 move_stepsize =
10751 getModifiedActionNumber(move_stepsize,
10752 action_mode,
10753 action_arg_number,
10754 action_arg_number_min,
10755 action_arg_number_max);
10756
10757 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10758 }
10759 }
10760
10761 break;
10762 }
10763
10764 case CA_SET_PLAYER_SHIELD:
10765 {
10766 for (i = 0; i < MAX_PLAYERS; i++)
10767 {
10768 if (trigger_player_bits & (1 << i))
10769 {
10770 if (action_arg == CA_ARG_SHIELD_OFF)
10771 {
10772 stored_player[i].shield_normal_time_left = 0;
10773 stored_player[i].shield_deadly_time_left = 0;
10774 }
10775 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10776 {
10777 stored_player[i].shield_normal_time_left = 999999;
10778 }
10779 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10780 {
10781 stored_player[i].shield_normal_time_left = 999999;
10782 stored_player[i].shield_deadly_time_left = 999999;
10783 }
10784 }
10785 }
10786
10787 break;
10788 }
10789
10790 #if USE_PLAYER_GRAVITY
10791 case CA_SET_PLAYER_GRAVITY:
10792 {
10793 for (i = 0; i < MAX_PLAYERS; i++)
10794 {
10795 if (trigger_player_bits & (1 << i))
10796 {
10797 stored_player[i].gravity =
10798 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10799 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10800 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10801 stored_player[i].gravity);
10802 }
10803 }
10804
10805 break;
10806 }
10807 #endif
10808
10809 case CA_SET_PLAYER_ARTWORK:
10810 {
10811 for (i = 0; i < MAX_PLAYERS; i++)
10812 {
10813 if (trigger_player_bits & (1 << i))
10814 {
10815 int artwork_element = action_arg_element;
10816
10817 if (action_arg == CA_ARG_ELEMENT_RESET)
10818 artwork_element =
10819 (level.use_artwork_element[i] ? level.artwork_element[i] :
10820 stored_player[i].element_nr);
10821
10822 #if USE_GFX_RESET_PLAYER_ARTWORK
10823 if (stored_player[i].artwork_element != artwork_element)
10824 stored_player[i].Frame = 0;
10825 #endif
10826
10827 stored_player[i].artwork_element = artwork_element;
10828
10829 SetPlayerWaiting(&stored_player[i], FALSE);
10830
10831 /* set number of special actions for bored and sleeping animation */
10832 stored_player[i].num_special_action_bored =
10833 get_num_special_action(artwork_element,
10834 ACTION_BORING_1, ACTION_BORING_LAST);
10835 stored_player[i].num_special_action_sleeping =
10836 get_num_special_action(artwork_element,
10837 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10838 }
10839 }
10840
10841 break;
10842 }
10843
10844 case CA_SET_PLAYER_INVENTORY:
10845 {
10846 for (i = 0; i < MAX_PLAYERS; i++)
10847 {
10848 struct PlayerInfo *player = &stored_player[i];
10849 int j, k;
10850
10851 if (trigger_player_bits & (1 << i))
10852 {
10853 int inventory_element = action_arg_element;
10854
10855 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10856 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10857 action_arg == CA_ARG_ELEMENT_ACTION)
10858 {
10859 int element = inventory_element;
10860 int collect_count = element_info[element].collect_count_initial;
10861
10862 if (!IS_CUSTOM_ELEMENT(element))
10863 collect_count = 1;
10864
10865 if (collect_count == 0)
10866 player->inventory_infinite_element = element;
10867 else
10868 for (k = 0; k < collect_count; k++)
10869 if (player->inventory_size < MAX_INVENTORY_SIZE)
10870 player->inventory_element[player->inventory_size++] =
10871 element;
10872 }
10873 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10874 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10875 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10876 {
10877 if (player->inventory_infinite_element != EL_UNDEFINED &&
10878 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10879 action_arg_element_raw))
10880 player->inventory_infinite_element = EL_UNDEFINED;
10881
10882 for (k = 0, j = 0; j < player->inventory_size; j++)
10883 {
10884 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10885 action_arg_element_raw))
10886 player->inventory_element[k++] = player->inventory_element[j];
10887 }
10888
10889 player->inventory_size = k;
10890 }
10891 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10892 {
10893 if (player->inventory_size > 0)
10894 {
10895 for (j = 0; j < player->inventory_size - 1; j++)
10896 player->inventory_element[j] = player->inventory_element[j + 1];
10897
10898 player->inventory_size--;
10899 }
10900 }
10901 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10902 {
10903 if (player->inventory_size > 0)
10904 player->inventory_size--;
10905 }
10906 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10907 {
10908 player->inventory_infinite_element = EL_UNDEFINED;
10909 player->inventory_size = 0;
10910 }
10911 else if (action_arg == CA_ARG_INVENTORY_RESET)
10912 {
10913 player->inventory_infinite_element = EL_UNDEFINED;
10914 player->inventory_size = 0;
10915
10916 if (level.use_initial_inventory[i])
10917 {
10918 for (j = 0; j < level.initial_inventory_size[i]; j++)
10919 {
10920 int element = level.initial_inventory_content[i][j];
10921 int collect_count = element_info[element].collect_count_initial;
10922
10923 if (!IS_CUSTOM_ELEMENT(element))
10924 collect_count = 1;
10925
10926 if (collect_count == 0)
10927 player->inventory_infinite_element = element;
10928 else
10929 for (k = 0; k < collect_count; k++)
10930 if (player->inventory_size < MAX_INVENTORY_SIZE)
10931 player->inventory_element[player->inventory_size++] =
10932 element;
10933 }
10934 }
10935 }
10936 }
10937 }
10938
10939 break;
10940 }
10941
10942 /* ---------- CE actions ---------------------------------------------- */
10943
10944 case CA_SET_CE_VALUE:
10945 {
10946 #if USE_NEW_CUSTOM_VALUE
10947 int last_ce_value = CustomValue[x][y];
10948
10949 CustomValue[x][y] = action_arg_number_new;
10950
10951 if (CustomValue[x][y] != last_ce_value)
10952 {
10953 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10954 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10955
10956 if (CustomValue[x][y] == 0)
10957 {
10958 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10959 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10960 }
10961 }
10962 #endif
10963
10964 break;
10965 }
10966
10967 case CA_SET_CE_SCORE:
10968 {
10969 #if USE_NEW_CUSTOM_VALUE
10970 int last_ce_score = ei->collect_score;
10971
10972 ei->collect_score = action_arg_number_new;
10973
10974 if (ei->collect_score != last_ce_score)
10975 {
10976 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10977 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10978
10979 if (ei->collect_score == 0)
10980 {
10981 int xx, yy;
10982
10983 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10984 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10985
10986 /*
10987 This is a very special case that seems to be a mixture between
10988 CheckElementChange() and CheckTriggeredElementChange(): while
10989 the first one only affects single elements that are triggered
10990 directly, the second one affects multiple elements in the playfield
10991 that are triggered indirectly by another element. This is a third
10992 case: Changing the CE score always affects multiple identical CEs,
10993 so every affected CE must be checked, not only the single CE for
10994 which the CE score was changed in the first place (as every instance
10995 of that CE shares the same CE score, and therefore also can change)!
10996 */
10997 SCAN_PLAYFIELD(xx, yy)
10998 {
10999 if (Feld[xx][yy] == element)
11000 CheckElementChange(xx, yy, element, EL_UNDEFINED,
11001 CE_SCORE_GETS_ZERO);
11002 }
11003 }
11004 }
11005 #endif
11006
11007 break;
11008 }
11009
11010 case CA_SET_CE_ARTWORK:
11011 {
11012 int artwork_element = action_arg_element;
11013 boolean reset_frame = FALSE;
11014 int xx, yy;
11015
11016 if (action_arg == CA_ARG_ELEMENT_RESET)
11017 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11018 element);
11019
11020 if (ei->gfx_element != artwork_element)
11021 reset_frame = TRUE;
11022
11023 ei->gfx_element = artwork_element;
11024
11025 SCAN_PLAYFIELD(xx, yy)
11026 {
11027 if (Feld[xx][yy] == element)
11028 {
11029 if (reset_frame)
11030 {
11031 ResetGfxAnimation(xx, yy);
11032 ResetRandomAnimationValue(xx, yy);
11033 }
11034
11035 TEST_DrawLevelField(xx, yy);
11036 }
11037 }
11038
11039 break;
11040 }
11041
11042 /* ---------- engine actions ------------------------------------------ */
11043
11044 case CA_SET_ENGINE_SCAN_MODE:
11045 {
11046 InitPlayfieldScanMode(action_arg);
11047
11048 break;
11049 }
11050
11051 default:
11052 break;
11053 }
11054 }
11055
CreateFieldExt(int x,int y,int element,boolean is_change)11056 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11057 {
11058 int old_element = Feld[x][y];
11059 int new_element = GetElementFromGroupElement(element);
11060 int previous_move_direction = MovDir[x][y];
11061 #if USE_NEW_CUSTOM_VALUE
11062 int last_ce_value = CustomValue[x][y];
11063 #endif
11064 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11065 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11066 boolean add_player_onto_element = (new_element_is_player &&
11067 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11068 /* this breaks SnakeBite when a snake is
11069 halfway through a door that closes */
11070 /* NOW FIXED AT LEVEL INIT IN files.c */
11071 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11072 #endif
11073 IS_WALKABLE(old_element));
11074
11075 #if 0
11076 /* check if element under the player changes from accessible to unaccessible
11077 (needed for special case of dropping element which then changes) */
11078 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11079 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11080 {
11081 Bang(x, y);
11082
11083 return;
11084 }
11085 #endif
11086
11087 if (!add_player_onto_element)
11088 {
11089 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11090 RemoveMovingField(x, y);
11091 else
11092 RemoveField(x, y);
11093
11094 Feld[x][y] = new_element;
11095
11096 #if !USE_GFX_RESET_GFX_ANIMATION
11097 ResetGfxAnimation(x, y);
11098 ResetRandomAnimationValue(x, y);
11099 #endif
11100
11101 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11102 MovDir[x][y] = previous_move_direction;
11103
11104 #if USE_NEW_CUSTOM_VALUE
11105 if (element_info[new_element].use_last_ce_value)
11106 CustomValue[x][y] = last_ce_value;
11107 #endif
11108
11109 InitField_WithBug1(x, y, FALSE);
11110
11111 new_element = Feld[x][y]; /* element may have changed */
11112
11113 #if USE_GFX_RESET_GFX_ANIMATION
11114 ResetGfxAnimation(x, y);
11115 ResetRandomAnimationValue(x, y);
11116 #endif
11117
11118 TEST_DrawLevelField(x, y);
11119
11120 if (GFX_CRUMBLED(new_element))
11121 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11122 }
11123
11124 #if 1
11125 /* check if element under the player changes from accessible to unaccessible
11126 (needed for special case of dropping element which then changes) */
11127 /* (must be checked after creating new element for walkable group elements) */
11128 #if USE_FIX_KILLED_BY_NON_WALKABLE
11129 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11130 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11131 {
11132 Bang(x, y);
11133
11134 return;
11135 }
11136 #else
11137 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11138 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11139 {
11140 Bang(x, y);
11141
11142 return;
11143 }
11144 #endif
11145 #endif
11146
11147 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11148 if (new_element_is_player)
11149 RelocatePlayer(x, y, new_element);
11150
11151 if (is_change)
11152 ChangeCount[x][y]++; /* count number of changes in the same frame */
11153
11154 TestIfBadThingTouchesPlayer(x, y);
11155 TestIfPlayerTouchesCustomElement(x, y);
11156 TestIfElementTouchesCustomElement(x, y);
11157 }
11158
CreateField(int x,int y,int element)11159 static void CreateField(int x, int y, int element)
11160 {
11161 CreateFieldExt(x, y, element, FALSE);
11162 }
11163
CreateElementFromChange(int x,int y,int element)11164 static void CreateElementFromChange(int x, int y, int element)
11165 {
11166 element = GET_VALID_RUNTIME_ELEMENT(element);
11167
11168 #if USE_STOP_CHANGED_ELEMENTS
11169 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11170 {
11171 int old_element = Feld[x][y];
11172
11173 /* prevent changed element from moving in same engine frame
11174 unless both old and new element can either fall or move */
11175 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11176 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11177 Stop[x][y] = TRUE;
11178 }
11179 #endif
11180
11181 CreateFieldExt(x, y, element, TRUE);
11182 }
11183
ChangeElement(int x,int y,int element,int page)11184 static boolean ChangeElement(int x, int y, int element, int page)
11185 {
11186 struct ElementInfo *ei = &element_info[element];
11187 struct ElementChangeInfo *change = &ei->change_page[page];
11188 int ce_value = CustomValue[x][y];
11189 int ce_score = ei->collect_score;
11190 int target_element;
11191 int old_element = Feld[x][y];
11192
11193 /* always use default change event to prevent running into a loop */
11194 if (ChangeEvent[x][y] == -1)
11195 ChangeEvent[x][y] = CE_DELAY;
11196
11197 if (ChangeEvent[x][y] == CE_DELAY)
11198 {
11199 /* reset actual trigger element, trigger player and action element */
11200 change->actual_trigger_element = EL_EMPTY;
11201 change->actual_trigger_player = EL_EMPTY;
11202 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11203 change->actual_trigger_side = CH_SIDE_NONE;
11204 change->actual_trigger_ce_value = 0;
11205 change->actual_trigger_ce_score = 0;
11206 }
11207
11208 /* do not change elements more than a specified maximum number of changes */
11209 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11210 return FALSE;
11211
11212 ChangeCount[x][y]++; /* count number of changes in the same frame */
11213
11214 if (change->explode)
11215 {
11216 Bang(x, y);
11217
11218 return TRUE;
11219 }
11220
11221 if (change->use_target_content)
11222 {
11223 boolean complete_replace = TRUE;
11224 boolean can_replace[3][3];
11225 int xx, yy;
11226
11227 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11228 {
11229 boolean is_empty;
11230 boolean is_walkable;
11231 boolean is_diggable;
11232 boolean is_collectible;
11233 boolean is_removable;
11234 boolean is_destructible;
11235 int ex = x + xx - 1;
11236 int ey = y + yy - 1;
11237 int content_element = change->target_content.e[xx][yy];
11238 int e;
11239
11240 can_replace[xx][yy] = TRUE;
11241
11242 if (ex == x && ey == y) /* do not check changing element itself */
11243 continue;
11244
11245 if (content_element == EL_EMPTY_SPACE)
11246 {
11247 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11248
11249 continue;
11250 }
11251
11252 if (!IN_LEV_FIELD(ex, ey))
11253 {
11254 can_replace[xx][yy] = FALSE;
11255 complete_replace = FALSE;
11256
11257 continue;
11258 }
11259
11260 e = Feld[ex][ey];
11261
11262 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11263 e = MovingOrBlocked2Element(ex, ey);
11264
11265 is_empty = (IS_FREE(ex, ey) ||
11266 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11267
11268 is_walkable = (is_empty || IS_WALKABLE(e));
11269 is_diggable = (is_empty || IS_DIGGABLE(e));
11270 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11271 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11272 is_removable = (is_diggable || is_collectible);
11273
11274 can_replace[xx][yy] =
11275 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11276 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11277 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11278 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11279 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11280 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11281 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11282
11283 if (!can_replace[xx][yy])
11284 complete_replace = FALSE;
11285 }
11286
11287 if (!change->only_if_complete || complete_replace)
11288 {
11289 boolean something_has_changed = FALSE;
11290
11291 if (change->only_if_complete && change->use_random_replace &&
11292 RND(100) < change->random_percentage)
11293 return FALSE;
11294
11295 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11296 {
11297 int ex = x + xx - 1;
11298 int ey = y + yy - 1;
11299 int content_element;
11300
11301 if (can_replace[xx][yy] && (!change->use_random_replace ||
11302 RND(100) < change->random_percentage))
11303 {
11304 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11305 RemoveMovingField(ex, ey);
11306
11307 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11308
11309 content_element = change->target_content.e[xx][yy];
11310 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11311 ce_value, ce_score);
11312
11313 CreateElementFromChange(ex, ey, target_element);
11314
11315 something_has_changed = TRUE;
11316
11317 /* for symmetry reasons, freeze newly created border elements */
11318 if (ex != x || ey != y)
11319 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11320 }
11321 }
11322
11323 if (something_has_changed)
11324 {
11325 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11326 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11327 }
11328 }
11329 }
11330 else
11331 {
11332 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11333 ce_value, ce_score);
11334
11335 if (element == EL_DIAGONAL_GROWING ||
11336 element == EL_DIAGONAL_SHRINKING)
11337 {
11338 target_element = Store[x][y];
11339
11340 Store[x][y] = EL_EMPTY;
11341 }
11342
11343 CreateElementFromChange(x, y, target_element);
11344
11345 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11346 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11347 }
11348
11349 /* this uses direct change before indirect change */
11350 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11351
11352 return TRUE;
11353 }
11354
11355 #if USE_NEW_DELAYED_ACTION
11356
HandleElementChange(int x,int y,int page)11357 static void HandleElementChange(int x, int y, int page)
11358 {
11359 int element = MovingOrBlocked2Element(x, y);
11360 struct ElementInfo *ei = &element_info[element];
11361 struct ElementChangeInfo *change = &ei->change_page[page];
11362 boolean handle_action_before_change = FALSE;
11363
11364 #ifdef DEBUG
11365 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11366 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11367 {
11368 printf("\n\n");
11369 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11370 x, y, element, element_info[element].token_name);
11371 printf("HandleElementChange(): This should never happen!\n");
11372 printf("\n\n");
11373 }
11374 #endif
11375
11376 /* this can happen with classic bombs on walkable, changing elements */
11377 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11378 {
11379 #if 0
11380 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11381 ChangeDelay[x][y] = 0;
11382 #endif
11383
11384 return;
11385 }
11386
11387 if (ChangeDelay[x][y] == 0) /* initialize element change */
11388 {
11389 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11390
11391 if (change->can_change)
11392 {
11393 #if 1
11394 /* !!! not clear why graphic animation should be reset at all here !!! */
11395 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11396 #if USE_GFX_RESET_WHEN_NOT_MOVING
11397 /* when a custom element is about to change (for example by change delay),
11398 do not reset graphic animation when the custom element is moving */
11399 if (!IS_MOVING(x, y))
11400 #endif
11401 {
11402 ResetGfxAnimation(x, y);
11403 ResetRandomAnimationValue(x, y);
11404 }
11405 #endif
11406
11407 if (change->pre_change_function)
11408 change->pre_change_function(x, y);
11409 }
11410 }
11411
11412 ChangeDelay[x][y]--;
11413
11414 if (ChangeDelay[x][y] != 0) /* continue element change */
11415 {
11416 if (change->can_change)
11417 {
11418 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11419
11420 if (IS_ANIMATED(graphic))
11421 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11422
11423 if (change->change_function)
11424 change->change_function(x, y);
11425 }
11426 }
11427 else /* finish element change */
11428 {
11429 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11430 {
11431 page = ChangePage[x][y];
11432 ChangePage[x][y] = -1;
11433
11434 change = &ei->change_page[page];
11435 }
11436
11437 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11438 {
11439 ChangeDelay[x][y] = 1; /* try change after next move step */
11440 ChangePage[x][y] = page; /* remember page to use for change */
11441
11442 return;
11443 }
11444
11445 #if 1
11446 /* special case: set new level random seed before changing element */
11447 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11448 handle_action_before_change = TRUE;
11449
11450 if (change->has_action && handle_action_before_change)
11451 ExecuteCustomElementAction(x, y, element, page);
11452 #endif
11453
11454 if (change->can_change)
11455 {
11456 if (ChangeElement(x, y, element, page))
11457 {
11458 if (change->post_change_function)
11459 change->post_change_function(x, y);
11460 }
11461 }
11462
11463 if (change->has_action && !handle_action_before_change)
11464 ExecuteCustomElementAction(x, y, element, page);
11465 }
11466 }
11467
11468 #else
11469
HandleElementChange(int x,int y,int page)11470 static void HandleElementChange(int x, int y, int page)
11471 {
11472 int element = MovingOrBlocked2Element(x, y);
11473 struct ElementInfo *ei = &element_info[element];
11474 struct ElementChangeInfo *change = &ei->change_page[page];
11475
11476 #ifdef DEBUG
11477 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11478 {
11479 printf("\n\n");
11480 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11481 x, y, element, element_info[element].token_name);
11482 printf("HandleElementChange(): This should never happen!\n");
11483 printf("\n\n");
11484 }
11485 #endif
11486
11487 /* this can happen with classic bombs on walkable, changing elements */
11488 if (!CAN_CHANGE(element))
11489 {
11490 #if 0
11491 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11492 ChangeDelay[x][y] = 0;
11493 #endif
11494
11495 return;
11496 }
11497
11498 if (ChangeDelay[x][y] == 0) /* initialize element change */
11499 {
11500 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11501
11502 ResetGfxAnimation(x, y);
11503 ResetRandomAnimationValue(x, y);
11504
11505 if (change->pre_change_function)
11506 change->pre_change_function(x, y);
11507 }
11508
11509 ChangeDelay[x][y]--;
11510
11511 if (ChangeDelay[x][y] != 0) /* continue element change */
11512 {
11513 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11514
11515 if (IS_ANIMATED(graphic))
11516 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11517
11518 if (change->change_function)
11519 change->change_function(x, y);
11520 }
11521 else /* finish element change */
11522 {
11523 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11524 {
11525 page = ChangePage[x][y];
11526 ChangePage[x][y] = -1;
11527
11528 change = &ei->change_page[page];
11529 }
11530
11531 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11532 {
11533 ChangeDelay[x][y] = 1; /* try change after next move step */
11534 ChangePage[x][y] = page; /* remember page to use for change */
11535
11536 return;
11537 }
11538
11539 if (ChangeElement(x, y, element, page))
11540 {
11541 if (change->post_change_function)
11542 change->post_change_function(x, y);
11543 }
11544 }
11545 }
11546
11547 #endif
11548
CheckTriggeredElementChangeExt(int trigger_x,int trigger_y,int trigger_element,int trigger_event,int trigger_player,int trigger_side,int trigger_page)11549 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11550 int trigger_element,
11551 int trigger_event,
11552 int trigger_player,
11553 int trigger_side,
11554 int trigger_page)
11555 {
11556 boolean change_done_any = FALSE;
11557 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11558 int i;
11559
11560 if (!(trigger_events[trigger_element][trigger_event]))
11561 return FALSE;
11562
11563 #if 0
11564 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11565 trigger_event, recursion_loop_depth, recursion_loop_detected,
11566 recursion_loop_element, EL_NAME(recursion_loop_element));
11567 #endif
11568
11569 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11570
11571 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11572 {
11573 int element = EL_CUSTOM_START + i;
11574 boolean change_done = FALSE;
11575 int p;
11576
11577 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11578 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11579 continue;
11580
11581 for (p = 0; p < element_info[element].num_change_pages; p++)
11582 {
11583 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11584
11585 if (change->can_change_or_has_action &&
11586 change->has_event[trigger_event] &&
11587 change->trigger_side & trigger_side &&
11588 change->trigger_player & trigger_player &&
11589 change->trigger_page & trigger_page_bits &&
11590 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11591 {
11592 change->actual_trigger_element = trigger_element;
11593 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11594 change->actual_trigger_player_bits = trigger_player;
11595 change->actual_trigger_side = trigger_side;
11596 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11597 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11598
11599 #if 0
11600 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11601 element, EL_NAME(element), p);
11602 #endif
11603
11604 if ((change->can_change && !change_done) || change->has_action)
11605 {
11606 int x, y;
11607
11608 SCAN_PLAYFIELD(x, y)
11609 {
11610 if (Feld[x][y] == element)
11611 {
11612 if (change->can_change && !change_done)
11613 {
11614 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11615 /* if element already changed in this frame, not only prevent
11616 another element change (checked in ChangeElement()), but
11617 also prevent additional element actions for this element */
11618
11619 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11620 !level.use_action_after_change_bug)
11621 continue;
11622 #endif
11623
11624 #if 0
11625 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11626 element, EL_NAME(element), p);
11627 #endif
11628
11629 ChangeDelay[x][y] = 1;
11630 ChangeEvent[x][y] = trigger_event;
11631
11632 HandleElementChange(x, y, p);
11633 }
11634 #if USE_NEW_DELAYED_ACTION
11635 else if (change->has_action)
11636 {
11637 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11638 /* if element already changed in this frame, not only prevent
11639 another element change (checked in ChangeElement()), but
11640 also prevent additional element actions for this element */
11641
11642 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11643 !level.use_action_after_change_bug)
11644 continue;
11645 #endif
11646
11647
11648 #if 0
11649 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11650 element, EL_NAME(element), p);
11651 #endif
11652
11653 ExecuteCustomElementAction(x, y, element, p);
11654 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11655 }
11656 #else
11657 if (change->has_action)
11658 {
11659 ExecuteCustomElementAction(x, y, element, p);
11660 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11661 }
11662 #endif
11663 }
11664 }
11665
11666 if (change->can_change)
11667 {
11668 change_done = TRUE;
11669 change_done_any = TRUE;
11670
11671 #if 0
11672 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11673 element, EL_NAME(element), p);
11674 #endif
11675
11676 }
11677 }
11678 }
11679 }
11680 }
11681
11682 RECURSION_LOOP_DETECTION_END();
11683
11684 return change_done_any;
11685 }
11686
CheckElementChangeExt(int x,int y,int element,int trigger_element,int trigger_event,int trigger_player,int trigger_side)11687 static boolean CheckElementChangeExt(int x, int y,
11688 int element,
11689 int trigger_element,
11690 int trigger_event,
11691 int trigger_player,
11692 int trigger_side)
11693 {
11694 boolean change_done = FALSE;
11695 int p;
11696
11697 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11698 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11699 return FALSE;
11700
11701 if (Feld[x][y] == EL_BLOCKED)
11702 {
11703 Blocked2Moving(x, y, &x, &y);
11704 element = Feld[x][y];
11705 }
11706
11707 #if 0
11708 /* check if element has already changed */
11709 if (Feld[x][y] != element)
11710 return FALSE;
11711 #else
11712 /* check if element has already changed or is about to change after moving */
11713 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11714 Feld[x][y] != element) ||
11715
11716 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11717 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11718 ChangePage[x][y] != -1)))
11719 return FALSE;
11720 #endif
11721
11722 #if 0
11723 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11724 trigger_event, recursion_loop_depth, recursion_loop_detected,
11725 recursion_loop_element, EL_NAME(recursion_loop_element));
11726 #endif
11727
11728 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11729
11730 #if 0
11731 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11732 #endif
11733
11734 for (p = 0; p < element_info[element].num_change_pages; p++)
11735 {
11736 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11737
11738 /* check trigger element for all events where the element that is checked
11739 for changing interacts with a directly adjacent element -- this is
11740 different to element changes that affect other elements to change on the
11741 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11742 boolean check_trigger_element =
11743 (trigger_event == CE_TOUCHING_X ||
11744 trigger_event == CE_HITTING_X ||
11745 trigger_event == CE_HIT_BY_X ||
11746 #if 1
11747 /* this one was forgotten until 3.2.3 */
11748 trigger_event == CE_DIGGING_X);
11749 #endif
11750
11751 if (change->can_change_or_has_action &&
11752 change->has_event[trigger_event] &&
11753 change->trigger_side & trigger_side &&
11754 change->trigger_player & trigger_player &&
11755 (!check_trigger_element ||
11756 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11757 {
11758 change->actual_trigger_element = trigger_element;
11759 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11760 change->actual_trigger_player_bits = trigger_player;
11761 change->actual_trigger_side = trigger_side;
11762 change->actual_trigger_ce_value = CustomValue[x][y];
11763 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11764
11765 /* special case: trigger element not at (x,y) position for some events */
11766 if (check_trigger_element)
11767 {
11768 static struct
11769 {
11770 int dx, dy;
11771 } move_xy[] =
11772 {
11773 { 0, 0 },
11774 { -1, 0 },
11775 { +1, 0 },
11776 { 0, 0 },
11777 { 0, -1 },
11778 { 0, 0 }, { 0, 0 }, { 0, 0 },
11779 { 0, +1 }
11780 };
11781
11782 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11783 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11784
11785 change->actual_trigger_ce_value = CustomValue[xx][yy];
11786 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11787 }
11788
11789 if (change->can_change && !change_done)
11790 {
11791 ChangeDelay[x][y] = 1;
11792 ChangeEvent[x][y] = trigger_event;
11793
11794 HandleElementChange(x, y, p);
11795
11796 change_done = TRUE;
11797 }
11798 #if USE_NEW_DELAYED_ACTION
11799 else if (change->has_action)
11800 {
11801 ExecuteCustomElementAction(x, y, element, p);
11802 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11803 }
11804 #else
11805 if (change->has_action)
11806 {
11807 ExecuteCustomElementAction(x, y, element, p);
11808 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11809 }
11810 #endif
11811 }
11812 }
11813
11814 RECURSION_LOOP_DETECTION_END();
11815
11816 return change_done;
11817 }
11818
PlayPlayerSound(struct PlayerInfo * player)11819 static void PlayPlayerSound(struct PlayerInfo *player)
11820 {
11821 int jx = player->jx, jy = player->jy;
11822 int sound_element = player->artwork_element;
11823 int last_action = player->last_action_waiting;
11824 int action = player->action_waiting;
11825
11826 if (player->is_waiting)
11827 {
11828 if (action != last_action)
11829 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11830 else
11831 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11832 }
11833 else
11834 {
11835 if (action != last_action)
11836 StopSound(element_info[sound_element].sound[last_action]);
11837
11838 if (last_action == ACTION_SLEEPING)
11839 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11840 }
11841 }
11842
PlayAllPlayersSound()11843 static void PlayAllPlayersSound()
11844 {
11845 int i;
11846
11847 for (i = 0; i < MAX_PLAYERS; i++)
11848 if (stored_player[i].active)
11849 PlayPlayerSound(&stored_player[i]);
11850 }
11851
SetPlayerWaiting(struct PlayerInfo * player,boolean is_waiting)11852 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11853 {
11854 boolean last_waiting = player->is_waiting;
11855 int move_dir = player->MovDir;
11856
11857 player->dir_waiting = move_dir;
11858 player->last_action_waiting = player->action_waiting;
11859
11860 if (is_waiting)
11861 {
11862 if (!last_waiting) /* not waiting -> waiting */
11863 {
11864 player->is_waiting = TRUE;
11865
11866 player->frame_counter_bored =
11867 FrameCounter +
11868 game.player_boring_delay_fixed +
11869 GetSimpleRandom(game.player_boring_delay_random);
11870 player->frame_counter_sleeping =
11871 FrameCounter +
11872 game.player_sleeping_delay_fixed +
11873 GetSimpleRandom(game.player_sleeping_delay_random);
11874
11875 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11876 }
11877
11878 if (game.player_sleeping_delay_fixed +
11879 game.player_sleeping_delay_random > 0 &&
11880 player->anim_delay_counter == 0 &&
11881 player->post_delay_counter == 0 &&
11882 FrameCounter >= player->frame_counter_sleeping)
11883 player->is_sleeping = TRUE;
11884 else if (game.player_boring_delay_fixed +
11885 game.player_boring_delay_random > 0 &&
11886 FrameCounter >= player->frame_counter_bored)
11887 player->is_bored = TRUE;
11888
11889 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11890 player->is_bored ? ACTION_BORING :
11891 ACTION_WAITING);
11892
11893 if (player->is_sleeping && player->use_murphy)
11894 {
11895 /* special case for sleeping Murphy when leaning against non-free tile */
11896
11897 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11898 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11899 !IS_MOVING(player->jx - 1, player->jy)))
11900 move_dir = MV_LEFT;
11901 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11902 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11903 !IS_MOVING(player->jx + 1, player->jy)))
11904 move_dir = MV_RIGHT;
11905 else
11906 player->is_sleeping = FALSE;
11907
11908 player->dir_waiting = move_dir;
11909 }
11910
11911 if (player->is_sleeping)
11912 {
11913 if (player->num_special_action_sleeping > 0)
11914 {
11915 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11916 {
11917 int last_special_action = player->special_action_sleeping;
11918 int num_special_action = player->num_special_action_sleeping;
11919 int special_action =
11920 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11921 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11922 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11923 last_special_action + 1 : ACTION_SLEEPING);
11924 int special_graphic =
11925 el_act_dir2img(player->artwork_element, special_action, move_dir);
11926
11927 player->anim_delay_counter =
11928 graphic_info[special_graphic].anim_delay_fixed +
11929 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11930 player->post_delay_counter =
11931 graphic_info[special_graphic].post_delay_fixed +
11932 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11933
11934 player->special_action_sleeping = special_action;
11935 }
11936
11937 if (player->anim_delay_counter > 0)
11938 {
11939 player->action_waiting = player->special_action_sleeping;
11940 player->anim_delay_counter--;
11941 }
11942 else if (player->post_delay_counter > 0)
11943 {
11944 player->post_delay_counter--;
11945 }
11946 }
11947 }
11948 else if (player->is_bored)
11949 {
11950 if (player->num_special_action_bored > 0)
11951 {
11952 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11953 {
11954 int special_action =
11955 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11956 int special_graphic =
11957 el_act_dir2img(player->artwork_element, special_action, move_dir);
11958
11959 player->anim_delay_counter =
11960 graphic_info[special_graphic].anim_delay_fixed +
11961 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11962 player->post_delay_counter =
11963 graphic_info[special_graphic].post_delay_fixed +
11964 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11965
11966 player->special_action_bored = special_action;
11967 }
11968
11969 if (player->anim_delay_counter > 0)
11970 {
11971 player->action_waiting = player->special_action_bored;
11972 player->anim_delay_counter--;
11973 }
11974 else if (player->post_delay_counter > 0)
11975 {
11976 player->post_delay_counter--;
11977 }
11978 }
11979 }
11980 }
11981 else if (last_waiting) /* waiting -> not waiting */
11982 {
11983 player->is_waiting = FALSE;
11984 player->is_bored = FALSE;
11985 player->is_sleeping = FALSE;
11986
11987 player->frame_counter_bored = -1;
11988 player->frame_counter_sleeping = -1;
11989
11990 player->anim_delay_counter = 0;
11991 player->post_delay_counter = 0;
11992
11993 player->dir_waiting = player->MovDir;
11994 player->action_waiting = ACTION_DEFAULT;
11995
11996 player->special_action_bored = ACTION_DEFAULT;
11997 player->special_action_sleeping = ACTION_DEFAULT;
11998 }
11999 }
12000
PlayerActions(struct PlayerInfo * player,byte player_action)12001 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12002 {
12003 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12004 int left = player_action & JOY_LEFT;
12005 int right = player_action & JOY_RIGHT;
12006 int up = player_action & JOY_UP;
12007 int down = player_action & JOY_DOWN;
12008 int button1 = player_action & JOY_BUTTON_1;
12009 int button2 = player_action & JOY_BUTTON_2;
12010 int dx = (left ? -1 : right ? 1 : 0);
12011 int dy = (up ? -1 : down ? 1 : 0);
12012
12013 if (!player->active || tape.pausing)
12014 return 0;
12015
12016 if (player_action)
12017 {
12018 if (button1)
12019 snapped = SnapField(player, dx, dy);
12020 else
12021 {
12022 if (button2)
12023 dropped = DropElement(player);
12024
12025 moved = MovePlayer(player, dx, dy);
12026 }
12027
12028 if (tape.single_step && tape.recording && !tape.pausing)
12029 {
12030 #if 1
12031 /* as it is called "single step mode", just return to pause mode when the
12032 player stopped moving after one tile (or never starts moving at all) */
12033 if (!player->is_moving)
12034 #else
12035 /* this is buggy: there are quite some cases where the single step mode
12036 does not return to pause mode (like pushing things that don't move
12037 or simply by trying to run against a wall) */
12038 if (button1 || (dropped && !moved))
12039 #endif
12040 {
12041 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12042 SnapField(player, 0, 0); /* stop snapping */
12043 }
12044 }
12045
12046 SetPlayerWaiting(player, FALSE);
12047
12048 return player_action;
12049 }
12050 else
12051 {
12052 /* no actions for this player (no input at player's configured device) */
12053
12054 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12055 SnapField(player, 0, 0);
12056 CheckGravityMovementWhenNotMoving(player);
12057
12058 if (player->MovPos == 0)
12059 SetPlayerWaiting(player, TRUE);
12060
12061 if (player->MovPos == 0) /* needed for tape.playing */
12062 player->is_moving = FALSE;
12063
12064 player->is_dropping = FALSE;
12065 player->is_dropping_pressed = FALSE;
12066 player->drop_pressed_delay = 0;
12067
12068 return 0;
12069 }
12070 }
12071
CheckLevelTime()12072 static void CheckLevelTime()
12073 {
12074 int i;
12075
12076 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12077 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12078 {
12079 if (level.native_em_level->lev->home == 0) /* all players at home */
12080 {
12081 PlayerWins(local_player);
12082
12083 AllPlayersGone = TRUE;
12084
12085 level.native_em_level->lev->home = -1;
12086 }
12087
12088 if (level.native_em_level->ply[0]->alive == 0 &&
12089 level.native_em_level->ply[1]->alive == 0 &&
12090 level.native_em_level->ply[2]->alive == 0 &&
12091 level.native_em_level->ply[3]->alive == 0) /* all dead */
12092 AllPlayersGone = TRUE;
12093 }
12094 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12095 {
12096 if (game_sp.LevelSolved &&
12097 !game_sp.GameOver) /* game won */
12098 {
12099 PlayerWins(local_player);
12100
12101 game_sp.GameOver = TRUE;
12102
12103 AllPlayersGone = TRUE;
12104 }
12105
12106 if (game_sp.GameOver) /* game lost */
12107 AllPlayersGone = TRUE;
12108 }
12109
12110 if (TimeFrames >= FRAMES_PER_SECOND)
12111 {
12112 TimeFrames = 0;
12113 TapeTime++;
12114
12115 for (i = 0; i < MAX_PLAYERS; i++)
12116 {
12117 struct PlayerInfo *player = &stored_player[i];
12118
12119 if (SHIELD_ON(player))
12120 {
12121 player->shield_normal_time_left--;
12122
12123 if (player->shield_deadly_time_left > 0)
12124 player->shield_deadly_time_left--;
12125 }
12126 }
12127
12128 if (!local_player->LevelSolved && !level.use_step_counter)
12129 {
12130 TimePlayed++;
12131
12132 if (TimeLeft > 0)
12133 {
12134 TimeLeft--;
12135
12136 if (TimeLeft <= 10 && setup.time_limit)
12137 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12138
12139 #if 1
12140 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12141
12142 DisplayGameControlValues();
12143 #else
12144 DrawGameValue_Time(TimeLeft);
12145 #endif
12146
12147 if (!TimeLeft && setup.time_limit)
12148 {
12149 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12150 level.native_em_level->lev->killed_out_of_time = TRUE;
12151 else
12152 for (i = 0; i < MAX_PLAYERS; i++)
12153 KillPlayer(&stored_player[i]);
12154 }
12155 }
12156 #if 1
12157 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12158 {
12159 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12160
12161 DisplayGameControlValues();
12162 }
12163 #else
12164 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12165 DrawGameValue_Time(TimePlayed);
12166 #endif
12167
12168 level.native_em_level->lev->time =
12169 (level.time == 0 ? TimePlayed : TimeLeft);
12170 }
12171
12172 if (tape.recording || tape.playing)
12173 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12174 }
12175
12176 #if 1
12177 UpdateAndDisplayGameControlValues();
12178 #else
12179 UpdateGameDoorValues();
12180 DrawGameDoorValues();
12181 #endif
12182 }
12183
AdvanceFrameAndPlayerCounters(int player_nr)12184 void AdvanceFrameAndPlayerCounters(int player_nr)
12185 {
12186 int i;
12187
12188 /* advance frame counters (global frame counter and time frame counter) */
12189 FrameCounter++;
12190 TimeFrames++;
12191
12192 /* advance player counters (counters for move delay, move animation etc.) */
12193 for (i = 0; i < MAX_PLAYERS; i++)
12194 {
12195 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12196 int move_delay_value = stored_player[i].move_delay_value;
12197 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12198
12199 if (!advance_player_counters) /* not all players may be affected */
12200 continue;
12201
12202 #if USE_NEW_PLAYER_ANIM
12203 if (move_frames == 0) /* less than one move per game frame */
12204 {
12205 int stepsize = TILEX / move_delay_value;
12206 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12207 int count = (stored_player[i].is_moving ?
12208 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12209
12210 if (count % delay == 0)
12211 move_frames = 1;
12212 }
12213 #endif
12214
12215 stored_player[i].Frame += move_frames;
12216
12217 if (stored_player[i].MovPos != 0)
12218 stored_player[i].StepFrame += move_frames;
12219
12220 if (stored_player[i].move_delay > 0)
12221 stored_player[i].move_delay--;
12222
12223 /* due to bugs in previous versions, counter must count up, not down */
12224 if (stored_player[i].push_delay != -1)
12225 stored_player[i].push_delay++;
12226
12227 if (stored_player[i].drop_delay > 0)
12228 stored_player[i].drop_delay--;
12229
12230 if (stored_player[i].is_dropping_pressed)
12231 stored_player[i].drop_pressed_delay++;
12232 }
12233 }
12234
StartGameActions(boolean init_network_game,boolean record_tape,long random_seed)12235 void StartGameActions(boolean init_network_game, boolean record_tape,
12236 long random_seed)
12237 {
12238 unsigned long new_random_seed = InitRND(random_seed);
12239
12240 if (record_tape)
12241 TapeStartRecording(new_random_seed);
12242
12243 #if defined(NETWORK_AVALIABLE)
12244 if (init_network_game)
12245 {
12246 SendToServer_StartPlaying();
12247
12248 return;
12249 }
12250 #endif
12251
12252 InitGame();
12253 }
12254
GameActions()12255 void GameActions()
12256 {
12257 static unsigned long game_frame_delay = 0;
12258 unsigned long game_frame_delay_value;
12259 byte *recorded_player_action;
12260 byte summarized_player_action = 0;
12261 byte tape_action[MAX_PLAYERS];
12262 int i;
12263
12264 /* detect endless loops, caused by custom element programming */
12265 if (recursion_loop_detected && recursion_loop_depth == 0)
12266 {
12267 char *message = getStringCat3("Internal Error ! Element ",
12268 EL_NAME(recursion_loop_element),
12269 " caused endless loop ! Quit the game ?");
12270
12271 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12272 EL_NAME(recursion_loop_element));
12273
12274 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12275
12276 recursion_loop_detected = FALSE; /* if game should be continued */
12277
12278 free(message);
12279
12280 return;
12281 }
12282
12283 if (game.restart_level)
12284 StartGameActions(options.network, setup.autorecord, level.random_seed);
12285
12286 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12287 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12288 {
12289 if (level.native_em_level->lev->home == 0) /* all players at home */
12290 {
12291 PlayerWins(local_player);
12292
12293 AllPlayersGone = TRUE;
12294
12295 level.native_em_level->lev->home = -1;
12296 }
12297
12298 if (level.native_em_level->ply[0]->alive == 0 &&
12299 level.native_em_level->ply[1]->alive == 0 &&
12300 level.native_em_level->ply[2]->alive == 0 &&
12301 level.native_em_level->ply[3]->alive == 0) /* all dead */
12302 AllPlayersGone = TRUE;
12303 }
12304 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12305 {
12306 if (game_sp.LevelSolved &&
12307 !game_sp.GameOver) /* game won */
12308 {
12309 PlayerWins(local_player);
12310
12311 game_sp.GameOver = TRUE;
12312
12313 AllPlayersGone = TRUE;
12314 }
12315
12316 if (game_sp.GameOver) /* game lost */
12317 AllPlayersGone = TRUE;
12318 }
12319
12320 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12321 GameWon();
12322
12323 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12324 TapeStop();
12325
12326 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12327 return;
12328
12329 game_frame_delay_value =
12330 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12331
12332 if (tape.playing && tape.warp_forward && !tape.pausing)
12333 game_frame_delay_value = 0;
12334
12335 /* ---------- main game synchronization point ---------- */
12336
12337 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12338
12339 if (network_playing && !network_player_action_received)
12340 {
12341 /* try to get network player actions in time */
12342
12343 #if defined(NETWORK_AVALIABLE)
12344 /* last chance to get network player actions without main loop delay */
12345 HandleNetworking();
12346 #endif
12347
12348 /* game was quit by network peer */
12349 if (game_status != GAME_MODE_PLAYING)
12350 return;
12351
12352 if (!network_player_action_received)
12353 return; /* failed to get network player actions in time */
12354
12355 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12356 }
12357
12358 if (tape.pausing)
12359 return;
12360
12361 /* at this point we know that we really continue executing the game */
12362
12363 network_player_action_received = FALSE;
12364
12365 /* when playing tape, read previously recorded player input from tape data */
12366 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12367
12368 #if 1
12369 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12370 if (tape.pausing)
12371 return;
12372 #endif
12373
12374 if (tape.set_centered_player)
12375 {
12376 game.centered_player_nr_next = tape.centered_player_nr_next;
12377 game.set_centered_player = TRUE;
12378 }
12379
12380 for (i = 0; i < MAX_PLAYERS; i++)
12381 {
12382 summarized_player_action |= stored_player[i].action;
12383
12384 if (!network_playing)
12385 stored_player[i].effective_action = stored_player[i].action;
12386 }
12387
12388 #if defined(NETWORK_AVALIABLE)
12389 if (network_playing)
12390 SendToServer_MovePlayer(summarized_player_action);
12391 #endif
12392
12393 if (!options.network && !setup.team_mode)
12394 local_player->effective_action = summarized_player_action;
12395
12396 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12397 {
12398 for (i = 0; i < MAX_PLAYERS; i++)
12399 stored_player[i].effective_action =
12400 (i == game.centered_player_nr ? summarized_player_action : 0);
12401 }
12402
12403 if (recorded_player_action != NULL)
12404 for (i = 0; i < MAX_PLAYERS; i++)
12405 stored_player[i].effective_action = recorded_player_action[i];
12406
12407 for (i = 0; i < MAX_PLAYERS; i++)
12408 {
12409 tape_action[i] = stored_player[i].effective_action;
12410
12411 /* (this can only happen in the R'n'D game engine) */
12412 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12413 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12414 }
12415
12416 /* only record actions from input devices, but not programmed actions */
12417 if (tape.recording)
12418 TapeRecordAction(tape_action);
12419
12420 #if USE_NEW_PLAYER_ASSIGNMENTS
12421 {
12422 byte mapped_action[MAX_PLAYERS];
12423
12424 for (i = 0; i < MAX_PLAYERS; i++)
12425 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12426
12427 for (i = 0; i < MAX_PLAYERS; i++)
12428 stored_player[i].effective_action = mapped_action[i];
12429 }
12430 #endif
12431
12432 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12433 {
12434 GameActions_EM_Main();
12435 }
12436 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12437 {
12438 GameActions_SP_Main();
12439 }
12440 else
12441 {
12442 GameActions_RND();
12443 }
12444 }
12445
GameActions_EM_Main()12446 void GameActions_EM_Main()
12447 {
12448 byte effective_action[MAX_PLAYERS];
12449 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12450 int i;
12451
12452 for (i = 0; i < MAX_PLAYERS; i++)
12453 effective_action[i] = stored_player[i].effective_action;
12454
12455 GameActions_EM(effective_action, warp_mode);
12456
12457 CheckLevelTime();
12458
12459 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12460 }
12461
GameActions_SP_Main()12462 void GameActions_SP_Main()
12463 {
12464 byte effective_action[MAX_PLAYERS];
12465 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12466 int i;
12467
12468 for (i = 0; i < MAX_PLAYERS; i++)
12469 effective_action[i] = stored_player[i].effective_action;
12470
12471 GameActions_SP(effective_action, warp_mode);
12472
12473 CheckLevelTime();
12474
12475 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12476 }
12477
GameActions_RND()12478 void GameActions_RND()
12479 {
12480 int magic_wall_x = 0, magic_wall_y = 0;
12481 int i, x, y, element, graphic;
12482
12483 InitPlayfieldScanModeVars();
12484
12485 #if USE_ONE_MORE_CHANGE_PER_FRAME
12486 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12487 {
12488 SCAN_PLAYFIELD(x, y)
12489 {
12490 ChangeCount[x][y] = 0;
12491 ChangeEvent[x][y] = -1;
12492 }
12493 }
12494 #endif
12495
12496 if (game.set_centered_player)
12497 {
12498 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12499
12500 /* switching to "all players" only possible if all players fit to screen */
12501 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12502 {
12503 game.centered_player_nr_next = game.centered_player_nr;
12504 game.set_centered_player = FALSE;
12505 }
12506
12507 /* do not switch focus to non-existing (or non-active) player */
12508 if (game.centered_player_nr_next >= 0 &&
12509 !stored_player[game.centered_player_nr_next].active)
12510 {
12511 game.centered_player_nr_next = game.centered_player_nr;
12512 game.set_centered_player = FALSE;
12513 }
12514 }
12515
12516 if (game.set_centered_player &&
12517 ScreenMovPos == 0) /* screen currently aligned at tile position */
12518 {
12519 int sx, sy;
12520
12521 if (game.centered_player_nr_next == -1)
12522 {
12523 setScreenCenteredToAllPlayers(&sx, &sy);
12524 }
12525 else
12526 {
12527 sx = stored_player[game.centered_player_nr_next].jx;
12528 sy = stored_player[game.centered_player_nr_next].jy;
12529 }
12530
12531 game.centered_player_nr = game.centered_player_nr_next;
12532 game.set_centered_player = FALSE;
12533
12534 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12535 DrawGameDoorValues();
12536 }
12537
12538 for (i = 0; i < MAX_PLAYERS; i++)
12539 {
12540 int actual_player_action = stored_player[i].effective_action;
12541
12542 #if 1
12543 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12544 - rnd_equinox_tetrachloride 048
12545 - rnd_equinox_tetrachloride_ii 096
12546 - rnd_emanuel_schmieg 002
12547 - doctor_sloan_ww 001, 020
12548 */
12549 if (stored_player[i].MovPos == 0)
12550 CheckGravityMovement(&stored_player[i]);
12551 #endif
12552
12553 /* overwrite programmed action with tape action */
12554 if (stored_player[i].programmed_action)
12555 actual_player_action = stored_player[i].programmed_action;
12556
12557 PlayerActions(&stored_player[i], actual_player_action);
12558
12559 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12560 }
12561
12562 ScrollScreen(NULL, SCROLL_GO_ON);
12563
12564 /* for backwards compatibility, the following code emulates a fixed bug that
12565 occured when pushing elements (causing elements that just made their last
12566 pushing step to already (if possible) make their first falling step in the
12567 same game frame, which is bad); this code is also needed to use the famous
12568 "spring push bug" which is used in older levels and might be wanted to be
12569 used also in newer levels, but in this case the buggy pushing code is only
12570 affecting the "spring" element and no other elements */
12571
12572 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12573 {
12574 for (i = 0; i < MAX_PLAYERS; i++)
12575 {
12576 struct PlayerInfo *player = &stored_player[i];
12577 int x = player->jx;
12578 int y = player->jy;
12579
12580 if (player->active && player->is_pushing && player->is_moving &&
12581 IS_MOVING(x, y) &&
12582 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12583 Feld[x][y] == EL_SPRING))
12584 {
12585 ContinueMoving(x, y);
12586
12587 /* continue moving after pushing (this is actually a bug) */
12588 if (!IS_MOVING(x, y))
12589 Stop[x][y] = FALSE;
12590 }
12591 }
12592 }
12593
12594 #if 0
12595 debug_print_timestamp(0, "start main loop profiling");
12596 #endif
12597
12598 SCAN_PLAYFIELD(x, y)
12599 {
12600 ChangeCount[x][y] = 0;
12601 ChangeEvent[x][y] = -1;
12602
12603 /* this must be handled before main playfield loop */
12604 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12605 {
12606 MovDelay[x][y]--;
12607 if (MovDelay[x][y] <= 0)
12608 RemoveField(x, y);
12609 }
12610
12611 #if USE_NEW_SNAP_DELAY
12612 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12613 {
12614 MovDelay[x][y]--;
12615 if (MovDelay[x][y] <= 0)
12616 {
12617 RemoveField(x, y);
12618 TEST_DrawLevelField(x, y);
12619
12620 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12621 }
12622 }
12623 #endif
12624
12625 #if DEBUG
12626 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12627 {
12628 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12629 printf("GameActions(): This should never happen!\n");
12630
12631 ChangePage[x][y] = -1;
12632 }
12633 #endif
12634
12635 Stop[x][y] = FALSE;
12636 if (WasJustMoving[x][y] > 0)
12637 WasJustMoving[x][y]--;
12638 if (WasJustFalling[x][y] > 0)
12639 WasJustFalling[x][y]--;
12640 if (CheckCollision[x][y] > 0)
12641 CheckCollision[x][y]--;
12642 if (CheckImpact[x][y] > 0)
12643 CheckImpact[x][y]--;
12644
12645 GfxFrame[x][y]++;
12646
12647 /* reset finished pushing action (not done in ContinueMoving() to allow
12648 continuous pushing animation for elements with zero push delay) */
12649 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12650 {
12651 ResetGfxAnimation(x, y);
12652 TEST_DrawLevelField(x, y);
12653 }
12654
12655 #if DEBUG
12656 if (IS_BLOCKED(x, y))
12657 {
12658 int oldx, oldy;
12659
12660 Blocked2Moving(x, y, &oldx, &oldy);
12661 if (!IS_MOVING(oldx, oldy))
12662 {
12663 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12664 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12665 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12666 printf("GameActions(): This should never happen!\n");
12667 }
12668 }
12669 #endif
12670 }
12671
12672 #if 0
12673 debug_print_timestamp(0, "- time for pre-main loop:");
12674 #endif
12675
12676 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12677 SCAN_PLAYFIELD(x, y)
12678 {
12679 element = Feld[x][y];
12680 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12681
12682 #if 1
12683 {
12684 #if 1
12685 int element2 = element;
12686 int graphic2 = graphic;
12687 #else
12688 int element2 = Feld[x][y];
12689 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12690 #endif
12691 int last_gfx_frame = GfxFrame[x][y];
12692
12693 if (graphic_info[graphic2].anim_global_sync)
12694 GfxFrame[x][y] = FrameCounter;
12695 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12696 GfxFrame[x][y] = CustomValue[x][y];
12697 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12698 GfxFrame[x][y] = element_info[element2].collect_score;
12699 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12700 GfxFrame[x][y] = ChangeDelay[x][y];
12701
12702 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12703 DrawLevelGraphicAnimation(x, y, graphic2);
12704 }
12705 #else
12706 ResetGfxFrame(x, y, TRUE);
12707 #endif
12708
12709 #if 1
12710 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12711 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12712 ResetRandomAnimationValue(x, y);
12713 #endif
12714
12715 #if 1
12716 SetRandomAnimationValue(x, y);
12717 #endif
12718
12719 #if 1
12720 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12721 #endif
12722 }
12723 #endif // -------------------- !!! TEST ONLY !!! --------------------
12724
12725 #if 0
12726 debug_print_timestamp(0, "- time for TEST loop: -->");
12727 #endif
12728
12729 SCAN_PLAYFIELD(x, y)
12730 {
12731 element = Feld[x][y];
12732 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12733
12734 ResetGfxFrame(x, y, TRUE);
12735
12736 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12737 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12738 ResetRandomAnimationValue(x, y);
12739
12740 SetRandomAnimationValue(x, y);
12741
12742 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12743
12744 if (IS_INACTIVE(element))
12745 {
12746 if (IS_ANIMATED(graphic))
12747 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12748
12749 continue;
12750 }
12751
12752 /* this may take place after moving, so 'element' may have changed */
12753 if (IS_CHANGING(x, y) &&
12754 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12755 {
12756 int page = element_info[element].event_page_nr[CE_DELAY];
12757
12758 #if 1
12759 HandleElementChange(x, y, page);
12760 #else
12761 if (CAN_CHANGE(element))
12762 HandleElementChange(x, y, page);
12763
12764 if (HAS_ACTION(element))
12765 ExecuteCustomElementAction(x, y, element, page);
12766 #endif
12767
12768 element = Feld[x][y];
12769 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12770 }
12771
12772 #if 0 // ---------------------------------------------------------------------
12773
12774 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12775 {
12776 StartMoving(x, y);
12777
12778 element = Feld[x][y];
12779 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12780
12781 if (IS_ANIMATED(graphic) &&
12782 !IS_MOVING(x, y) &&
12783 !Stop[x][y])
12784 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12785
12786 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12787 TEST_DrawTwinkleOnField(x, y);
12788 }
12789 else if (IS_MOVING(x, y))
12790 ContinueMoving(x, y);
12791 else
12792 {
12793 switch (element)
12794 {
12795 case EL_ACID:
12796 case EL_EXIT_OPEN:
12797 case EL_EM_EXIT_OPEN:
12798 case EL_SP_EXIT_OPEN:
12799 case EL_STEEL_EXIT_OPEN:
12800 case EL_EM_STEEL_EXIT_OPEN:
12801 case EL_SP_TERMINAL:
12802 case EL_SP_TERMINAL_ACTIVE:
12803 case EL_EXTRA_TIME:
12804 case EL_SHIELD_NORMAL:
12805 case EL_SHIELD_DEADLY:
12806 if (IS_ANIMATED(graphic))
12807 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12808 break;
12809
12810 case EL_DYNAMITE_ACTIVE:
12811 case EL_EM_DYNAMITE_ACTIVE:
12812 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12813 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12814 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12815 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12816 case EL_SP_DISK_RED_ACTIVE:
12817 CheckDynamite(x, y);
12818 break;
12819
12820 case EL_AMOEBA_GROWING:
12821 AmoebeWaechst(x, y);
12822 break;
12823
12824 case EL_AMOEBA_SHRINKING:
12825 AmoebaDisappearing(x, y);
12826 break;
12827
12828 #if !USE_NEW_AMOEBA_CODE
12829 case EL_AMOEBA_WET:
12830 case EL_AMOEBA_DRY:
12831 case EL_AMOEBA_FULL:
12832 case EL_BD_AMOEBA:
12833 case EL_EMC_DRIPPER:
12834 AmoebeAbleger(x, y);
12835 break;
12836 #endif
12837
12838 case EL_GAME_OF_LIFE:
12839 case EL_BIOMAZE:
12840 Life(x, y);
12841 break;
12842
12843 case EL_EXIT_CLOSED:
12844 CheckExit(x, y);
12845 break;
12846
12847 case EL_EM_EXIT_CLOSED:
12848 CheckExitEM(x, y);
12849 break;
12850
12851 case EL_STEEL_EXIT_CLOSED:
12852 CheckExitSteel(x, y);
12853 break;
12854
12855 case EL_EM_STEEL_EXIT_CLOSED:
12856 CheckExitSteelEM(x, y);
12857 break;
12858
12859 case EL_SP_EXIT_CLOSED:
12860 CheckExitSP(x, y);
12861 break;
12862
12863 case EL_EXPANDABLE_WALL_GROWING:
12864 case EL_EXPANDABLE_STEELWALL_GROWING:
12865 MauerWaechst(x, y);
12866 break;
12867
12868 case EL_EXPANDABLE_WALL:
12869 case EL_EXPANDABLE_WALL_HORIZONTAL:
12870 case EL_EXPANDABLE_WALL_VERTICAL:
12871 case EL_EXPANDABLE_WALL_ANY:
12872 case EL_BD_EXPANDABLE_WALL:
12873 MauerAbleger(x, y);
12874 break;
12875
12876 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12877 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12878 case EL_EXPANDABLE_STEELWALL_ANY:
12879 MauerAblegerStahl(x, y);
12880 break;
12881
12882 case EL_FLAMES:
12883 CheckForDragon(x, y);
12884 break;
12885
12886 case EL_EXPLOSION:
12887 break;
12888
12889 case EL_ELEMENT_SNAPPING:
12890 case EL_DIAGONAL_SHRINKING:
12891 case EL_DIAGONAL_GROWING:
12892 {
12893 graphic =
12894 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12895
12896 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12897 break;
12898 }
12899
12900 default:
12901 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12902 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12903 break;
12904 }
12905 }
12906
12907 #else // ---------------------------------------------------------------------
12908
12909 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12910 {
12911 StartMoving(x, y);
12912
12913 element = Feld[x][y];
12914 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12915
12916 if (IS_ANIMATED(graphic) &&
12917 !IS_MOVING(x, y) &&
12918 !Stop[x][y])
12919 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12920
12921 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12922 TEST_DrawTwinkleOnField(x, y);
12923 }
12924 else if ((element == EL_ACID ||
12925 element == EL_EXIT_OPEN ||
12926 element == EL_EM_EXIT_OPEN ||
12927 element == EL_SP_EXIT_OPEN ||
12928 element == EL_STEEL_EXIT_OPEN ||
12929 element == EL_EM_STEEL_EXIT_OPEN ||
12930 element == EL_SP_TERMINAL ||
12931 element == EL_SP_TERMINAL_ACTIVE ||
12932 element == EL_EXTRA_TIME ||
12933 element == EL_SHIELD_NORMAL ||
12934 element == EL_SHIELD_DEADLY) &&
12935 IS_ANIMATED(graphic))
12936 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12937 else if (IS_MOVING(x, y))
12938 ContinueMoving(x, y);
12939 else if (IS_ACTIVE_BOMB(element))
12940 CheckDynamite(x, y);
12941 else if (element == EL_AMOEBA_GROWING)
12942 AmoebeWaechst(x, y);
12943 else if (element == EL_AMOEBA_SHRINKING)
12944 AmoebaDisappearing(x, y);
12945
12946 #if !USE_NEW_AMOEBA_CODE
12947 else if (IS_AMOEBALIVE(element))
12948 AmoebeAbleger(x, y);
12949 #endif
12950
12951 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12952 Life(x, y);
12953 else if (element == EL_EXIT_CLOSED)
12954 CheckExit(x, y);
12955 else if (element == EL_EM_EXIT_CLOSED)
12956 CheckExitEM(x, y);
12957 else if (element == EL_STEEL_EXIT_CLOSED)
12958 CheckExitSteel(x, y);
12959 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12960 CheckExitSteelEM(x, y);
12961 else if (element == EL_SP_EXIT_CLOSED)
12962 CheckExitSP(x, y);
12963 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12964 element == EL_EXPANDABLE_STEELWALL_GROWING)
12965 MauerWaechst(x, y);
12966 else if (element == EL_EXPANDABLE_WALL ||
12967 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12968 element == EL_EXPANDABLE_WALL_VERTICAL ||
12969 element == EL_EXPANDABLE_WALL_ANY ||
12970 element == EL_BD_EXPANDABLE_WALL)
12971 MauerAbleger(x, y);
12972 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12973 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12974 element == EL_EXPANDABLE_STEELWALL_ANY)
12975 MauerAblegerStahl(x, y);
12976 else if (element == EL_FLAMES)
12977 CheckForDragon(x, y);
12978 else if (element == EL_EXPLOSION)
12979 ; /* drawing of correct explosion animation is handled separately */
12980 else if (element == EL_ELEMENT_SNAPPING ||
12981 element == EL_DIAGONAL_SHRINKING ||
12982 element == EL_DIAGONAL_GROWING)
12983 {
12984 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12985
12986 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12987 }
12988 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12989 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12990
12991 #endif // ---------------------------------------------------------------------
12992
12993 if (IS_BELT_ACTIVE(element))
12994 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12995
12996 if (game.magic_wall_active)
12997 {
12998 int jx = local_player->jx, jy = local_player->jy;
12999
13000 /* play the element sound at the position nearest to the player */
13001 if ((element == EL_MAGIC_WALL_FULL ||
13002 element == EL_MAGIC_WALL_ACTIVE ||
13003 element == EL_MAGIC_WALL_EMPTYING ||
13004 element == EL_BD_MAGIC_WALL_FULL ||
13005 element == EL_BD_MAGIC_WALL_ACTIVE ||
13006 element == EL_BD_MAGIC_WALL_EMPTYING ||
13007 element == EL_DC_MAGIC_WALL_FULL ||
13008 element == EL_DC_MAGIC_WALL_ACTIVE ||
13009 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13010 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13011 {
13012 magic_wall_x = x;
13013 magic_wall_y = y;
13014 }
13015 }
13016 }
13017
13018 #if 0
13019 debug_print_timestamp(0, "- time for MAIN loop: -->");
13020 #endif
13021
13022 #if USE_NEW_AMOEBA_CODE
13023 /* new experimental amoeba growth stuff */
13024 if (!(FrameCounter % 8))
13025 {
13026 static unsigned long random = 1684108901;
13027
13028 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13029 {
13030 x = RND(lev_fieldx);
13031 y = RND(lev_fieldy);
13032 element = Feld[x][y];
13033
13034 if (!IS_PLAYER(x,y) &&
13035 (element == EL_EMPTY ||
13036 CAN_GROW_INTO(element) ||
13037 element == EL_QUICKSAND_EMPTY ||
13038 element == EL_QUICKSAND_FAST_EMPTY ||
13039 element == EL_ACID_SPLASH_LEFT ||
13040 element == EL_ACID_SPLASH_RIGHT))
13041 {
13042 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13043 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13044 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13045 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13046 Feld[x][y] = EL_AMOEBA_DROP;
13047 }
13048
13049 random = random * 129 + 1;
13050 }
13051 }
13052 #endif
13053
13054 #if 0
13055 if (game.explosions_delayed)
13056 #endif
13057 {
13058 game.explosions_delayed = FALSE;
13059
13060 SCAN_PLAYFIELD(x, y)
13061 {
13062 element = Feld[x][y];
13063
13064 if (ExplodeField[x][y])
13065 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13066 else if (element == EL_EXPLOSION)
13067 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13068
13069 ExplodeField[x][y] = EX_TYPE_NONE;
13070 }
13071
13072 game.explosions_delayed = TRUE;
13073 }
13074
13075 if (game.magic_wall_active)
13076 {
13077 if (!(game.magic_wall_time_left % 4))
13078 {
13079 int element = Feld[magic_wall_x][magic_wall_y];
13080
13081 if (element == EL_BD_MAGIC_WALL_FULL ||
13082 element == EL_BD_MAGIC_WALL_ACTIVE ||
13083 element == EL_BD_MAGIC_WALL_EMPTYING)
13084 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13085 else if (element == EL_DC_MAGIC_WALL_FULL ||
13086 element == EL_DC_MAGIC_WALL_ACTIVE ||
13087 element == EL_DC_MAGIC_WALL_EMPTYING)
13088 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13089 else
13090 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13091 }
13092
13093 if (game.magic_wall_time_left > 0)
13094 {
13095 game.magic_wall_time_left--;
13096
13097 if (!game.magic_wall_time_left)
13098 {
13099 SCAN_PLAYFIELD(x, y)
13100 {
13101 element = Feld[x][y];
13102
13103 if (element == EL_MAGIC_WALL_ACTIVE ||
13104 element == EL_MAGIC_WALL_FULL)
13105 {
13106 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13107 TEST_DrawLevelField(x, y);
13108 }
13109 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13110 element == EL_BD_MAGIC_WALL_FULL)
13111 {
13112 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13113 TEST_DrawLevelField(x, y);
13114 }
13115 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13116 element == EL_DC_MAGIC_WALL_FULL)
13117 {
13118 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13119 TEST_DrawLevelField(x, y);
13120 }
13121 }
13122
13123 game.magic_wall_active = FALSE;
13124 }
13125 }
13126 }
13127
13128 if (game.light_time_left > 0)
13129 {
13130 game.light_time_left--;
13131
13132 if (game.light_time_left == 0)
13133 RedrawAllLightSwitchesAndInvisibleElements();
13134 }
13135
13136 if (game.timegate_time_left > 0)
13137 {
13138 game.timegate_time_left--;
13139
13140 if (game.timegate_time_left == 0)
13141 CloseAllOpenTimegates();
13142 }
13143
13144 if (game.lenses_time_left > 0)
13145 {
13146 game.lenses_time_left--;
13147
13148 if (game.lenses_time_left == 0)
13149 RedrawAllInvisibleElementsForLenses();
13150 }
13151
13152 if (game.magnify_time_left > 0)
13153 {
13154 game.magnify_time_left--;
13155
13156 if (game.magnify_time_left == 0)
13157 RedrawAllInvisibleElementsForMagnifier();
13158 }
13159
13160 for (i = 0; i < MAX_PLAYERS; i++)
13161 {
13162 struct PlayerInfo *player = &stored_player[i];
13163
13164 if (SHIELD_ON(player))
13165 {
13166 if (player->shield_deadly_time_left)
13167 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13168 else if (player->shield_normal_time_left)
13169 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13170 }
13171 }
13172
13173 #if USE_DELAYED_GFX_REDRAW
13174 SCAN_PLAYFIELD(x, y)
13175 {
13176 #if 1
13177 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13178 #else
13179 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13180 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13181 #endif
13182 {
13183 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13184 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13185
13186 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13187 DrawLevelField(x, y);
13188
13189 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13190 DrawLevelFieldCrumbled(x, y);
13191
13192 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13193 DrawLevelFieldCrumbledNeighbours(x, y);
13194
13195 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13196 DrawTwinkleOnField(x, y);
13197 }
13198
13199 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13200 }
13201 #endif
13202
13203 CheckLevelTime();
13204
13205 DrawAllPlayers();
13206 PlayAllPlayersSound();
13207
13208 if (options.debug) /* calculate frames per second */
13209 {
13210 static unsigned long fps_counter = 0;
13211 static int fps_frames = 0;
13212 unsigned long fps_delay_ms = Counter() - fps_counter;
13213
13214 fps_frames++;
13215
13216 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13217 {
13218 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13219
13220 fps_frames = 0;
13221 fps_counter = Counter();
13222 }
13223
13224 redraw_mask |= REDRAW_FPS;
13225 }
13226
13227 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13228
13229 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13230 {
13231 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13232
13233 local_player->show_envelope = 0;
13234 }
13235
13236 #if 0
13237 debug_print_timestamp(0, "stop main loop profiling ");
13238 printf("----------------------------------------------------------\n");
13239 #endif
13240
13241 /* use random number generator in every frame to make it less predictable */
13242 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13243 RND(1);
13244 }
13245
AllPlayersInSight(struct PlayerInfo * player,int x,int y)13246 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13247 {
13248 int min_x = x, min_y = y, max_x = x, max_y = y;
13249 int i;
13250
13251 for (i = 0; i < MAX_PLAYERS; i++)
13252 {
13253 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13254
13255 if (!stored_player[i].active || &stored_player[i] == player)
13256 continue;
13257
13258 min_x = MIN(min_x, jx);
13259 min_y = MIN(min_y, jy);
13260 max_x = MAX(max_x, jx);
13261 max_y = MAX(max_y, jy);
13262 }
13263
13264 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13265 }
13266
AllPlayersInVisibleScreen()13267 static boolean AllPlayersInVisibleScreen()
13268 {
13269 int i;
13270
13271 for (i = 0; i < MAX_PLAYERS; i++)
13272 {
13273 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13274
13275 if (!stored_player[i].active)
13276 continue;
13277
13278 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13279 return FALSE;
13280 }
13281
13282 return TRUE;
13283 }
13284
ScrollLevel(int dx,int dy)13285 void ScrollLevel(int dx, int dy)
13286 {
13287 #if 0
13288 /* (directly solved in BlitBitmap() now) */
13289 static Bitmap *bitmap_db_field2 = NULL;
13290 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13291 int x, y;
13292 #else
13293 int x, y;
13294 #endif
13295
13296 #if 0
13297 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13298 /* only horizontal XOR vertical scroll direction allowed */
13299 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13300 return;
13301 #endif
13302
13303 #if 0
13304 /* (directly solved in BlitBitmap() now) */
13305 if (bitmap_db_field2 == NULL)
13306 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13307
13308 /* needed when blitting directly to same bitmap -- should not be needed with
13309 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13310 BlitBitmap(drawto_field, bitmap_db_field2,
13311 FX + TILEX * (dx == -1) - softscroll_offset,
13312 FY + TILEY * (dy == -1) - softscroll_offset,
13313 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13314 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13315 FX + TILEX * (dx == 1) - softscroll_offset,
13316 FY + TILEY * (dy == 1) - softscroll_offset);
13317 BlitBitmap(bitmap_db_field2, drawto_field,
13318 FX + TILEX * (dx == 1) - softscroll_offset,
13319 FY + TILEY * (dy == 1) - softscroll_offset,
13320 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13321 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13322 FX + TILEX * (dx == 1) - softscroll_offset,
13323 FY + TILEY * (dy == 1) - softscroll_offset);
13324
13325 #else
13326
13327 #if 0
13328 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13329 int xsize = (BX2 - BX1 + 1);
13330 int ysize = (BY2 - BY1 + 1);
13331 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13332 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13333 int step = (start < end ? +1 : -1);
13334
13335 for (i = start; i != end; i += step)
13336 {
13337 BlitBitmap(drawto_field, drawto_field,
13338 FX + TILEX * (dx != 0 ? i + step : 0),
13339 FY + TILEY * (dy != 0 ? i + step : 0),
13340 TILEX * (dx != 0 ? 1 : xsize),
13341 TILEY * (dy != 0 ? 1 : ysize),
13342 FX + TILEX * (dx != 0 ? i : 0),
13343 FY + TILEY * (dy != 0 ? i : 0));
13344 }
13345
13346 #else
13347
13348 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13349
13350 BlitBitmap(drawto_field, drawto_field,
13351 FX + TILEX * (dx == -1) - softscroll_offset,
13352 FY + TILEY * (dy == -1) - softscroll_offset,
13353 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13354 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13355 FX + TILEX * (dx == 1) - softscroll_offset,
13356 FY + TILEY * (dy == 1) - softscroll_offset);
13357 #endif
13358 #endif
13359
13360 if (dx != 0)
13361 {
13362 x = (dx == 1 ? BX1 : BX2);
13363 for (y = BY1; y <= BY2; y++)
13364 DrawScreenField(x, y);
13365 }
13366
13367 if (dy != 0)
13368 {
13369 y = (dy == 1 ? BY1 : BY2);
13370 for (x = BX1; x <= BX2; x++)
13371 DrawScreenField(x, y);
13372 }
13373
13374 redraw_mask |= REDRAW_FIELD;
13375 }
13376
canFallDown(struct PlayerInfo * player)13377 static boolean canFallDown(struct PlayerInfo *player)
13378 {
13379 int jx = player->jx, jy = player->jy;
13380
13381 return (IN_LEV_FIELD(jx, jy + 1) &&
13382 (IS_FREE(jx, jy + 1) ||
13383 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13384 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13385 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13386 }
13387
canPassField(int x,int y,int move_dir)13388 static boolean canPassField(int x, int y, int move_dir)
13389 {
13390 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13391 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13392 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13393 int nextx = x + dx;
13394 int nexty = y + dy;
13395 int element = Feld[x][y];
13396
13397 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13398 !CAN_MOVE(element) &&
13399 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13400 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13401 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13402 }
13403
canMoveToValidFieldWithGravity(int x,int y,int move_dir)13404 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13405 {
13406 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13407 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13408 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13409 int newx = x + dx;
13410 int newy = y + dy;
13411
13412 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13413 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13414 (IS_DIGGABLE(Feld[newx][newy]) ||
13415 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13416 canPassField(newx, newy, move_dir)));
13417 }
13418
CheckGravityMovement(struct PlayerInfo * player)13419 static void CheckGravityMovement(struct PlayerInfo *player)
13420 {
13421 #if USE_PLAYER_GRAVITY
13422 if (player->gravity && !player->programmed_action)
13423 #else
13424 if (game.gravity && !player->programmed_action)
13425 #endif
13426 {
13427 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13428 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13429 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13430 int jx = player->jx, jy = player->jy;
13431 boolean player_is_moving_to_valid_field =
13432 (!player_is_snapping &&
13433 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13434 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13435 boolean player_can_fall_down = canFallDown(player);
13436
13437 if (player_can_fall_down &&
13438 !player_is_moving_to_valid_field)
13439 player->programmed_action = MV_DOWN;
13440 }
13441 }
13442
CheckGravityMovementWhenNotMoving(struct PlayerInfo * player)13443 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13444 {
13445 return CheckGravityMovement(player);
13446
13447 #if USE_PLAYER_GRAVITY
13448 if (player->gravity && !player->programmed_action)
13449 #else
13450 if (game.gravity && !player->programmed_action)
13451 #endif
13452 {
13453 int jx = player->jx, jy = player->jy;
13454 boolean field_under_player_is_free =
13455 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13456 boolean player_is_standing_on_valid_field =
13457 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13458 (IS_WALKABLE(Feld[jx][jy]) &&
13459 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13460
13461 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13462 player->programmed_action = MV_DOWN;
13463 }
13464 }
13465
13466 /*
13467 MovePlayerOneStep()
13468 -----------------------------------------------------------------------------
13469 dx, dy: direction (non-diagonal) to try to move the player to
13470 real_dx, real_dy: direction as read from input device (can be diagonal)
13471 */
13472
MovePlayerOneStep(struct PlayerInfo * player,int dx,int dy,int real_dx,int real_dy)13473 boolean MovePlayerOneStep(struct PlayerInfo *player,
13474 int dx, int dy, int real_dx, int real_dy)
13475 {
13476 int jx = player->jx, jy = player->jy;
13477 int new_jx = jx + dx, new_jy = jy + dy;
13478 #if !USE_FIXED_DONT_RUN_INTO
13479 int element;
13480 #endif
13481 int can_move;
13482 boolean player_can_move = !player->cannot_move;
13483
13484 if (!player->active || (!dx && !dy))
13485 return MP_NO_ACTION;
13486
13487 player->MovDir = (dx < 0 ? MV_LEFT :
13488 dx > 0 ? MV_RIGHT :
13489 dy < 0 ? MV_UP :
13490 dy > 0 ? MV_DOWN : MV_NONE);
13491
13492 if (!IN_LEV_FIELD(new_jx, new_jy))
13493 return MP_NO_ACTION;
13494
13495 if (!player_can_move)
13496 {
13497 if (player->MovPos == 0)
13498 {
13499 player->is_moving = FALSE;
13500 player->is_digging = FALSE;
13501 player->is_collecting = FALSE;
13502 player->is_snapping = FALSE;
13503 player->is_pushing = FALSE;
13504 }
13505 }
13506
13507 #if 1
13508 if (!options.network && game.centered_player_nr == -1 &&
13509 !AllPlayersInSight(player, new_jx, new_jy))
13510 return MP_NO_ACTION;
13511 #else
13512 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13513 return MP_NO_ACTION;
13514 #endif
13515
13516 #if !USE_FIXED_DONT_RUN_INTO
13517 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13518
13519 /* (moved to DigField()) */
13520 if (player_can_move && DONT_RUN_INTO(element))
13521 {
13522 if (element == EL_ACID && dx == 0 && dy == 1)
13523 {
13524 SplashAcid(new_jx, new_jy);
13525 Feld[jx][jy] = EL_PLAYER_1;
13526 InitMovingField(jx, jy, MV_DOWN);
13527 Store[jx][jy] = EL_ACID;
13528 ContinueMoving(jx, jy);
13529 BuryPlayer(player);
13530 }
13531 else
13532 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13533
13534 return MP_MOVING;
13535 }
13536 #endif
13537
13538 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13539 if (can_move != MP_MOVING)
13540 return can_move;
13541
13542 /* check if DigField() has caused relocation of the player */
13543 if (player->jx != jx || player->jy != jy)
13544 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13545
13546 StorePlayer[jx][jy] = 0;
13547 player->last_jx = jx;
13548 player->last_jy = jy;
13549 player->jx = new_jx;
13550 player->jy = new_jy;
13551 StorePlayer[new_jx][new_jy] = player->element_nr;
13552
13553 if (player->move_delay_value_next != -1)
13554 {
13555 player->move_delay_value = player->move_delay_value_next;
13556 player->move_delay_value_next = -1;
13557 }
13558
13559 player->MovPos =
13560 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13561
13562 player->step_counter++;
13563
13564 PlayerVisit[jx][jy] = FrameCounter;
13565
13566 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13567 player->is_moving = TRUE;
13568 #endif
13569
13570 #if 1
13571 /* should better be called in MovePlayer(), but this breaks some tapes */
13572 ScrollPlayer(player, SCROLL_INIT);
13573 #endif
13574
13575 return MP_MOVING;
13576 }
13577
MovePlayer(struct PlayerInfo * player,int dx,int dy)13578 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13579 {
13580 int jx = player->jx, jy = player->jy;
13581 int old_jx = jx, old_jy = jy;
13582 int moved = MP_NO_ACTION;
13583
13584 if (!player->active)
13585 return FALSE;
13586
13587 if (!dx && !dy)
13588 {
13589 if (player->MovPos == 0)
13590 {
13591 player->is_moving = FALSE;
13592 player->is_digging = FALSE;
13593 player->is_collecting = FALSE;
13594 player->is_snapping = FALSE;
13595 player->is_pushing = FALSE;
13596 }
13597
13598 return FALSE;
13599 }
13600
13601 if (player->move_delay > 0)
13602 return FALSE;
13603
13604 player->move_delay = -1; /* set to "uninitialized" value */
13605
13606 /* store if player is automatically moved to next field */
13607 player->is_auto_moving = (player->programmed_action != MV_NONE);
13608
13609 /* remove the last programmed player action */
13610 player->programmed_action = 0;
13611
13612 if (player->MovPos)
13613 {
13614 /* should only happen if pre-1.2 tape recordings are played */
13615 /* this is only for backward compatibility */
13616
13617 int original_move_delay_value = player->move_delay_value;
13618
13619 #if DEBUG
13620 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13621 tape.counter);
13622 #endif
13623
13624 /* scroll remaining steps with finest movement resolution */
13625 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13626
13627 while (player->MovPos)
13628 {
13629 ScrollPlayer(player, SCROLL_GO_ON);
13630 ScrollScreen(NULL, SCROLL_GO_ON);
13631
13632 AdvanceFrameAndPlayerCounters(player->index_nr);
13633
13634 DrawAllPlayers();
13635 BackToFront();
13636 }
13637
13638 player->move_delay_value = original_move_delay_value;
13639 }
13640
13641 player->is_active = FALSE;
13642
13643 if (player->last_move_dir & MV_HORIZONTAL)
13644 {
13645 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13646 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13647 }
13648 else
13649 {
13650 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13651 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13652 }
13653
13654 #if USE_FIXED_BORDER_RUNNING_GFX
13655 if (!moved && !player->is_active)
13656 {
13657 player->is_moving = FALSE;
13658 player->is_digging = FALSE;
13659 player->is_collecting = FALSE;
13660 player->is_snapping = FALSE;
13661 player->is_pushing = FALSE;
13662 }
13663 #endif
13664
13665 jx = player->jx;
13666 jy = player->jy;
13667
13668 #if 1
13669 if (moved & MP_MOVING && !ScreenMovPos &&
13670 (player->index_nr == game.centered_player_nr ||
13671 game.centered_player_nr == -1))
13672 #else
13673 if (moved & MP_MOVING && !ScreenMovPos &&
13674 (player == local_player || !options.network))
13675 #endif
13676 {
13677 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13678 int offset = game.scroll_delay_value;
13679
13680 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13681 {
13682 /* actual player has left the screen -- scroll in that direction */
13683 if (jx != old_jx) /* player has moved horizontally */
13684 scroll_x += (jx - old_jx);
13685 else /* player has moved vertically */
13686 scroll_y += (jy - old_jy);
13687 }
13688 else
13689 {
13690 if (jx != old_jx) /* player has moved horizontally */
13691 {
13692 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13693 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13694 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13695
13696 /* don't scroll over playfield boundaries */
13697 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13698 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13699
13700 /* don't scroll more than one field at a time */
13701 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13702
13703 /* don't scroll against the player's moving direction */
13704 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13705 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13706 scroll_x = old_scroll_x;
13707 }
13708 else /* player has moved vertically */
13709 {
13710 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13711 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13712 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13713
13714 /* don't scroll over playfield boundaries */
13715 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13716 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13717
13718 /* don't scroll more than one field at a time */
13719 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13720
13721 /* don't scroll against the player's moving direction */
13722 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13723 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13724 scroll_y = old_scroll_y;
13725 }
13726 }
13727
13728 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13729 {
13730 #if 1
13731 if (!options.network && game.centered_player_nr == -1 &&
13732 !AllPlayersInVisibleScreen())
13733 {
13734 scroll_x = old_scroll_x;
13735 scroll_y = old_scroll_y;
13736 }
13737 else
13738 #else
13739 if (!options.network && !AllPlayersInVisibleScreen())
13740 {
13741 scroll_x = old_scroll_x;
13742 scroll_y = old_scroll_y;
13743 }
13744 else
13745 #endif
13746 {
13747 ScrollScreen(player, SCROLL_INIT);
13748 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13749 }
13750 }
13751 }
13752
13753 player->StepFrame = 0;
13754
13755 if (moved & MP_MOVING)
13756 {
13757 if (old_jx != jx && old_jy == jy)
13758 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13759 else if (old_jx == jx && old_jy != jy)
13760 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13761
13762 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13763
13764 player->last_move_dir = player->MovDir;
13765 player->is_moving = TRUE;
13766 player->is_snapping = FALSE;
13767 player->is_switching = FALSE;
13768 player->is_dropping = FALSE;
13769 player->is_dropping_pressed = FALSE;
13770 player->drop_pressed_delay = 0;
13771
13772 #if 0
13773 /* should better be called here than above, but this breaks some tapes */
13774 ScrollPlayer(player, SCROLL_INIT);
13775 #endif
13776 }
13777 else
13778 {
13779 CheckGravityMovementWhenNotMoving(player);
13780
13781 player->is_moving = FALSE;
13782
13783 /* at this point, the player is allowed to move, but cannot move right now
13784 (e.g. because of something blocking the way) -- ensure that the player
13785 is also allowed to move in the next frame (in old versions before 3.1.1,
13786 the player was forced to wait again for eight frames before next try) */
13787
13788 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13789 player->move_delay = 0; /* allow direct movement in the next frame */
13790 }
13791
13792 if (player->move_delay == -1) /* not yet initialized by DigField() */
13793 player->move_delay = player->move_delay_value;
13794
13795 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13796 {
13797 TestIfPlayerTouchesBadThing(jx, jy);
13798 TestIfPlayerTouchesCustomElement(jx, jy);
13799 }
13800
13801 if (!player->active)
13802 RemovePlayer(player);
13803
13804 return moved;
13805 }
13806
ScrollPlayer(struct PlayerInfo * player,int mode)13807 void ScrollPlayer(struct PlayerInfo *player, int mode)
13808 {
13809 int jx = player->jx, jy = player->jy;
13810 int last_jx = player->last_jx, last_jy = player->last_jy;
13811 int move_stepsize = TILEX / player->move_delay_value;
13812
13813 #if USE_NEW_PLAYER_SPEED
13814 if (!player->active)
13815 return;
13816
13817 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13818 return;
13819 #else
13820 if (!player->active || player->MovPos == 0)
13821 return;
13822 #endif
13823
13824 if (mode == SCROLL_INIT)
13825 {
13826 player->actual_frame_counter = FrameCounter;
13827 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13828
13829 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13830 Feld[last_jx][last_jy] == EL_EMPTY)
13831 {
13832 int last_field_block_delay = 0; /* start with no blocking at all */
13833 int block_delay_adjustment = player->block_delay_adjustment;
13834
13835 /* if player blocks last field, add delay for exactly one move */
13836 if (player->block_last_field)
13837 {
13838 last_field_block_delay += player->move_delay_value;
13839
13840 /* when blocking enabled, prevent moving up despite gravity */
13841 #if USE_PLAYER_GRAVITY
13842 if (player->gravity && player->MovDir == MV_UP)
13843 block_delay_adjustment = -1;
13844 #else
13845 if (game.gravity && player->MovDir == MV_UP)
13846 block_delay_adjustment = -1;
13847 #endif
13848 }
13849
13850 /* add block delay adjustment (also possible when not blocking) */
13851 last_field_block_delay += block_delay_adjustment;
13852
13853 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13854 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13855 }
13856
13857 #if USE_NEW_PLAYER_SPEED
13858 if (player->MovPos != 0) /* player has not yet reached destination */
13859 return;
13860 #else
13861 return;
13862 #endif
13863 }
13864 else if (!FrameReached(&player->actual_frame_counter, 1))
13865 return;
13866
13867 #if USE_NEW_PLAYER_SPEED
13868 if (player->MovPos != 0)
13869 {
13870 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13871 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13872
13873 /* before DrawPlayer() to draw correct player graphic for this case */
13874 if (player->MovPos == 0)
13875 CheckGravityMovement(player);
13876 }
13877 #else
13878 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13879 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13880
13881 /* before DrawPlayer() to draw correct player graphic for this case */
13882 if (player->MovPos == 0)
13883 CheckGravityMovement(player);
13884 #endif
13885
13886 if (player->MovPos == 0) /* player reached destination field */
13887 {
13888 if (player->move_delay_reset_counter > 0)
13889 {
13890 player->move_delay_reset_counter--;
13891
13892 if (player->move_delay_reset_counter == 0)
13893 {
13894 /* continue with normal speed after quickly moving through gate */
13895 HALVE_PLAYER_SPEED(player);
13896
13897 /* be able to make the next move without delay */
13898 player->move_delay = 0;
13899 }
13900 }
13901
13902 player->last_jx = jx;
13903 player->last_jy = jy;
13904
13905 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13906 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13907 #if 1
13908 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13909 #endif
13910 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13911 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13912 #if 1
13913 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13914 #endif
13915 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13916 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13917 {
13918 DrawPlayer(player); /* needed here only to cleanup last field */
13919 RemovePlayer(player);
13920
13921 if (local_player->friends_still_needed == 0 ||
13922 IS_SP_ELEMENT(Feld[jx][jy]))
13923 PlayerWins(player);
13924 }
13925
13926 /* this breaks one level: "machine", level 000 */
13927 {
13928 int move_direction = player->MovDir;
13929 int enter_side = MV_DIR_OPPOSITE(move_direction);
13930 int leave_side = move_direction;
13931 int old_jx = last_jx;
13932 int old_jy = last_jy;
13933 int old_element = Feld[old_jx][old_jy];
13934 int new_element = Feld[jx][jy];
13935
13936 if (IS_CUSTOM_ELEMENT(old_element))
13937 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13938 CE_LEFT_BY_PLAYER,
13939 player->index_bit, leave_side);
13940
13941 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13942 CE_PLAYER_LEAVES_X,
13943 player->index_bit, leave_side);
13944
13945 if (IS_CUSTOM_ELEMENT(new_element))
13946 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13947 player->index_bit, enter_side);
13948
13949 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13950 CE_PLAYER_ENTERS_X,
13951 player->index_bit, enter_side);
13952
13953 #if USE_FIX_CE_ACTION_WITH_PLAYER
13954 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13955 CE_MOVE_OF_X, move_direction);
13956 #else
13957 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13958 CE_MOVE_OF_X, move_direction);
13959 #endif
13960 }
13961
13962 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13963 {
13964 TestIfPlayerTouchesBadThing(jx, jy);
13965 TestIfPlayerTouchesCustomElement(jx, jy);
13966
13967 /* needed because pushed element has not yet reached its destination,
13968 so it would trigger a change event at its previous field location */
13969 if (!player->is_pushing)
13970 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13971
13972 if (!player->active)
13973 RemovePlayer(player);
13974 }
13975
13976 if (!local_player->LevelSolved && level.use_step_counter)
13977 {
13978 int i;
13979
13980 TimePlayed++;
13981
13982 if (TimeLeft > 0)
13983 {
13984 TimeLeft--;
13985
13986 if (TimeLeft <= 10 && setup.time_limit)
13987 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13988
13989 #if 1
13990 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13991
13992 DisplayGameControlValues();
13993 #else
13994 DrawGameValue_Time(TimeLeft);
13995 #endif
13996
13997 if (!TimeLeft && setup.time_limit)
13998 for (i = 0; i < MAX_PLAYERS; i++)
13999 KillPlayer(&stored_player[i]);
14000 }
14001 #if 1
14002 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14003 {
14004 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14005
14006 DisplayGameControlValues();
14007 }
14008 #else
14009 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14010 DrawGameValue_Time(TimePlayed);
14011 #endif
14012 }
14013
14014 if (tape.single_step && tape.recording && !tape.pausing &&
14015 !player->programmed_action)
14016 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14017 }
14018 }
14019
ScrollScreen(struct PlayerInfo * player,int mode)14020 void ScrollScreen(struct PlayerInfo *player, int mode)
14021 {
14022 static unsigned long screen_frame_counter = 0;
14023
14024 if (mode == SCROLL_INIT)
14025 {
14026 /* set scrolling step size according to actual player's moving speed */
14027 ScrollStepSize = TILEX / player->move_delay_value;
14028
14029 screen_frame_counter = FrameCounter;
14030 ScreenMovDir = player->MovDir;
14031 ScreenMovPos = player->MovPos;
14032 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14033 return;
14034 }
14035 else if (!FrameReached(&screen_frame_counter, 1))
14036 return;
14037
14038 if (ScreenMovPos)
14039 {
14040 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14041 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14042 redraw_mask |= REDRAW_FIELD;
14043 }
14044 else
14045 ScreenMovDir = MV_NONE;
14046 }
14047
TestIfPlayerTouchesCustomElement(int x,int y)14048 void TestIfPlayerTouchesCustomElement(int x, int y)
14049 {
14050 static int xy[4][2] =
14051 {
14052 { 0, -1 },
14053 { -1, 0 },
14054 { +1, 0 },
14055 { 0, +1 }
14056 };
14057 static int trigger_sides[4][2] =
14058 {
14059 /* center side border side */
14060 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14061 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14062 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14063 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14064 };
14065 static int touch_dir[4] =
14066 {
14067 MV_LEFT | MV_RIGHT,
14068 MV_UP | MV_DOWN,
14069 MV_UP | MV_DOWN,
14070 MV_LEFT | MV_RIGHT
14071 };
14072 int center_element = Feld[x][y]; /* should always be non-moving! */
14073 int i;
14074
14075 for (i = 0; i < NUM_DIRECTIONS; i++)
14076 {
14077 int xx = x + xy[i][0];
14078 int yy = y + xy[i][1];
14079 int center_side = trigger_sides[i][0];
14080 int border_side = trigger_sides[i][1];
14081 int border_element;
14082
14083 if (!IN_LEV_FIELD(xx, yy))
14084 continue;
14085
14086 if (IS_PLAYER(x, y)) /* player found at center element */
14087 {
14088 struct PlayerInfo *player = PLAYERINFO(x, y);
14089
14090 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14091 border_element = Feld[xx][yy]; /* may be moving! */
14092 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14093 border_element = Feld[xx][yy];
14094 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14095 border_element = MovingOrBlocked2Element(xx, yy);
14096 else
14097 continue; /* center and border element do not touch */
14098
14099 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14100 player->index_bit, border_side);
14101 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14102 CE_PLAYER_TOUCHES_X,
14103 player->index_bit, border_side);
14104
14105 #if USE_FIX_CE_ACTION_WITH_PLAYER
14106 {
14107 /* use player element that is initially defined in the level playfield,
14108 not the player element that corresponds to the runtime player number
14109 (example: a level that contains EL_PLAYER_3 as the only player would
14110 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14111 int player_element = PLAYERINFO(x, y)->initial_element;
14112
14113 CheckElementChangeBySide(xx, yy, border_element, player_element,
14114 CE_TOUCHING_X, border_side);
14115 }
14116 #endif
14117 }
14118 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14119 {
14120 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14121
14122 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14123 {
14124 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14125 continue; /* center and border element do not touch */
14126 }
14127
14128 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14129 player->index_bit, center_side);
14130 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14131 CE_PLAYER_TOUCHES_X,
14132 player->index_bit, center_side);
14133
14134 #if USE_FIX_CE_ACTION_WITH_PLAYER
14135 {
14136 /* use player element that is initially defined in the level playfield,
14137 not the player element that corresponds to the runtime player number
14138 (example: a level that contains EL_PLAYER_3 as the only player would
14139 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14140 int player_element = PLAYERINFO(xx, yy)->initial_element;
14141
14142 CheckElementChangeBySide(x, y, center_element, player_element,
14143 CE_TOUCHING_X, center_side);
14144 }
14145 #endif
14146
14147 break;
14148 }
14149 }
14150 }
14151
14152 #if USE_ELEMENT_TOUCHING_BUGFIX
14153
TestIfElementTouchesCustomElement(int x,int y)14154 void TestIfElementTouchesCustomElement(int x, int y)
14155 {
14156 static int xy[4][2] =
14157 {
14158 { 0, -1 },
14159 { -1, 0 },
14160 { +1, 0 },
14161 { 0, +1 }
14162 };
14163 static int trigger_sides[4][2] =
14164 {
14165 /* center side border side */
14166 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14167 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14168 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14169 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14170 };
14171 static int touch_dir[4] =
14172 {
14173 MV_LEFT | MV_RIGHT,
14174 MV_UP | MV_DOWN,
14175 MV_UP | MV_DOWN,
14176 MV_LEFT | MV_RIGHT
14177 };
14178 boolean change_center_element = FALSE;
14179 int center_element = Feld[x][y]; /* should always be non-moving! */
14180 int border_element_old[NUM_DIRECTIONS];
14181 int i;
14182
14183 for (i = 0; i < NUM_DIRECTIONS; i++)
14184 {
14185 int xx = x + xy[i][0];
14186 int yy = y + xy[i][1];
14187 int border_element;
14188
14189 border_element_old[i] = -1;
14190
14191 if (!IN_LEV_FIELD(xx, yy))
14192 continue;
14193
14194 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14195 border_element = Feld[xx][yy]; /* may be moving! */
14196 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14197 border_element = Feld[xx][yy];
14198 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14199 border_element = MovingOrBlocked2Element(xx, yy);
14200 else
14201 continue; /* center and border element do not touch */
14202
14203 border_element_old[i] = border_element;
14204 }
14205
14206 for (i = 0; i < NUM_DIRECTIONS; i++)
14207 {
14208 int xx = x + xy[i][0];
14209 int yy = y + xy[i][1];
14210 int center_side = trigger_sides[i][0];
14211 int border_element = border_element_old[i];
14212
14213 if (border_element == -1)
14214 continue;
14215
14216 /* check for change of border element */
14217 CheckElementChangeBySide(xx, yy, border_element, center_element,
14218 CE_TOUCHING_X, center_side);
14219
14220 /* (center element cannot be player, so we dont have to check this here) */
14221 }
14222
14223 for (i = 0; i < NUM_DIRECTIONS; i++)
14224 {
14225 int xx = x + xy[i][0];
14226 int yy = y + xy[i][1];
14227 int border_side = trigger_sides[i][1];
14228 int border_element = border_element_old[i];
14229
14230 if (border_element == -1)
14231 continue;
14232
14233 /* check for change of center element (but change it only once) */
14234 if (!change_center_element)
14235 change_center_element =
14236 CheckElementChangeBySide(x, y, center_element, border_element,
14237 CE_TOUCHING_X, border_side);
14238
14239 #if USE_FIX_CE_ACTION_WITH_PLAYER
14240 if (IS_PLAYER(xx, yy))
14241 {
14242 /* use player element that is initially defined in the level playfield,
14243 not the player element that corresponds to the runtime player number
14244 (example: a level that contains EL_PLAYER_3 as the only player would
14245 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14246 int player_element = PLAYERINFO(xx, yy)->initial_element;
14247
14248 CheckElementChangeBySide(x, y, center_element, player_element,
14249 CE_TOUCHING_X, border_side);
14250 }
14251 #endif
14252 }
14253 }
14254
14255 #else
14256
TestIfElementTouchesCustomElement_OLD(int x,int y)14257 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14258 {
14259 static int xy[4][2] =
14260 {
14261 { 0, -1 },
14262 { -1, 0 },
14263 { +1, 0 },
14264 { 0, +1 }
14265 };
14266 static int trigger_sides[4][2] =
14267 {
14268 /* center side border side */
14269 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14270 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14271 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14272 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14273 };
14274 static int touch_dir[4] =
14275 {
14276 MV_LEFT | MV_RIGHT,
14277 MV_UP | MV_DOWN,
14278 MV_UP | MV_DOWN,
14279 MV_LEFT | MV_RIGHT
14280 };
14281 boolean change_center_element = FALSE;
14282 int center_element = Feld[x][y]; /* should always be non-moving! */
14283 int i;
14284
14285 for (i = 0; i < NUM_DIRECTIONS; i++)
14286 {
14287 int xx = x + xy[i][0];
14288 int yy = y + xy[i][1];
14289 int center_side = trigger_sides[i][0];
14290 int border_side = trigger_sides[i][1];
14291 int border_element;
14292
14293 if (!IN_LEV_FIELD(xx, yy))
14294 continue;
14295
14296 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14297 border_element = Feld[xx][yy]; /* may be moving! */
14298 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14299 border_element = Feld[xx][yy];
14300 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14301 border_element = MovingOrBlocked2Element(xx, yy);
14302 else
14303 continue; /* center and border element do not touch */
14304
14305 /* check for change of center element (but change it only once) */
14306 if (!change_center_element)
14307 change_center_element =
14308 CheckElementChangeBySide(x, y, center_element, border_element,
14309 CE_TOUCHING_X, border_side);
14310
14311 /* check for change of border element */
14312 CheckElementChangeBySide(xx, yy, border_element, center_element,
14313 CE_TOUCHING_X, center_side);
14314 }
14315 }
14316
14317 #endif
14318
TestIfElementHitsCustomElement(int x,int y,int direction)14319 void TestIfElementHitsCustomElement(int x, int y, int direction)
14320 {
14321 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14322 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14323 int hitx = x + dx, hity = y + dy;
14324 int hitting_element = Feld[x][y];
14325 int touched_element;
14326
14327 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14328 return;
14329
14330 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14331 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14332
14333 if (IN_LEV_FIELD(hitx, hity))
14334 {
14335 int opposite_direction = MV_DIR_OPPOSITE(direction);
14336 int hitting_side = direction;
14337 int touched_side = opposite_direction;
14338 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14339 MovDir[hitx][hity] != direction ||
14340 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14341
14342 object_hit = TRUE;
14343
14344 if (object_hit)
14345 {
14346 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14347 CE_HITTING_X, touched_side);
14348
14349 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14350 CE_HIT_BY_X, hitting_side);
14351
14352 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14353 CE_HIT_BY_SOMETHING, opposite_direction);
14354
14355 #if USE_FIX_CE_ACTION_WITH_PLAYER
14356 if (IS_PLAYER(hitx, hity))
14357 {
14358 /* use player element that is initially defined in the level playfield,
14359 not the player element that corresponds to the runtime player number
14360 (example: a level that contains EL_PLAYER_3 as the only player would
14361 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14362 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14363
14364 CheckElementChangeBySide(x, y, hitting_element, player_element,
14365 CE_HITTING_X, touched_side);
14366 }
14367 #endif
14368 }
14369 }
14370
14371 /* "hitting something" is also true when hitting the playfield border */
14372 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14373 CE_HITTING_SOMETHING, direction);
14374 }
14375
14376 #if 0
14377 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14378 {
14379 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14380 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14381 int hitx = x + dx, hity = y + dy;
14382 int hitting_element = Feld[x][y];
14383 int touched_element;
14384 #if 0
14385 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14386 !IS_FREE(hitx, hity) &&
14387 (!IS_MOVING(hitx, hity) ||
14388 MovDir[hitx][hity] != direction ||
14389 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14390 #endif
14391
14392 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14393 return;
14394
14395 #if 0
14396 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14397 return;
14398 #endif
14399
14400 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14401 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14402
14403 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14404 EP_CAN_SMASH_EVERYTHING, direction);
14405
14406 if (IN_LEV_FIELD(hitx, hity))
14407 {
14408 int opposite_direction = MV_DIR_OPPOSITE(direction);
14409 int hitting_side = direction;
14410 int touched_side = opposite_direction;
14411 #if 0
14412 int touched_element = MovingOrBlocked2Element(hitx, hity);
14413 #endif
14414 #if 1
14415 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14416 MovDir[hitx][hity] != direction ||
14417 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14418
14419 object_hit = TRUE;
14420 #endif
14421
14422 if (object_hit)
14423 {
14424 int i;
14425
14426 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14427 CE_SMASHED_BY_SOMETHING, opposite_direction);
14428
14429 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14430 CE_OTHER_IS_SMASHING, touched_side);
14431
14432 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14433 CE_OTHER_GETS_SMASHED, hitting_side);
14434 }
14435 }
14436 }
14437 #endif
14438
TestIfGoodThingHitsBadThing(int good_x,int good_y,int good_move_dir)14439 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14440 {
14441 int i, kill_x = -1, kill_y = -1;
14442
14443 int bad_element = -1;
14444 static int test_xy[4][2] =
14445 {
14446 { 0, -1 },
14447 { -1, 0 },
14448 { +1, 0 },
14449 { 0, +1 }
14450 };
14451 static int test_dir[4] =
14452 {
14453 MV_UP,
14454 MV_LEFT,
14455 MV_RIGHT,
14456 MV_DOWN
14457 };
14458
14459 for (i = 0; i < NUM_DIRECTIONS; i++)
14460 {
14461 int test_x, test_y, test_move_dir, test_element;
14462
14463 test_x = good_x + test_xy[i][0];
14464 test_y = good_y + test_xy[i][1];
14465
14466 if (!IN_LEV_FIELD(test_x, test_y))
14467 continue;
14468
14469 test_move_dir =
14470 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14471
14472 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14473
14474 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14475 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14476 */
14477 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14478 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14479 {
14480 kill_x = test_x;
14481 kill_y = test_y;
14482 bad_element = test_element;
14483
14484 break;
14485 }
14486 }
14487
14488 if (kill_x != -1 || kill_y != -1)
14489 {
14490 if (IS_PLAYER(good_x, good_y))
14491 {
14492 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14493
14494 if (player->shield_deadly_time_left > 0 &&
14495 !IS_INDESTRUCTIBLE(bad_element))
14496 Bang(kill_x, kill_y);
14497 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14498 KillPlayer(player);
14499 }
14500 else
14501 Bang(good_x, good_y);
14502 }
14503 }
14504
TestIfBadThingHitsGoodThing(int bad_x,int bad_y,int bad_move_dir)14505 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14506 {
14507 int i, kill_x = -1, kill_y = -1;
14508 int bad_element = Feld[bad_x][bad_y];
14509 static int test_xy[4][2] =
14510 {
14511 { 0, -1 },
14512 { -1, 0 },
14513 { +1, 0 },
14514 { 0, +1 }
14515 };
14516 static int touch_dir[4] =
14517 {
14518 MV_LEFT | MV_RIGHT,
14519 MV_UP | MV_DOWN,
14520 MV_UP | MV_DOWN,
14521 MV_LEFT | MV_RIGHT
14522 };
14523 static int test_dir[4] =
14524 {
14525 MV_UP,
14526 MV_LEFT,
14527 MV_RIGHT,
14528 MV_DOWN
14529 };
14530
14531 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14532 return;
14533
14534 for (i = 0; i < NUM_DIRECTIONS; i++)
14535 {
14536 int test_x, test_y, test_move_dir, test_element;
14537
14538 test_x = bad_x + test_xy[i][0];
14539 test_y = bad_y + test_xy[i][1];
14540
14541 if (!IN_LEV_FIELD(test_x, test_y))
14542 continue;
14543
14544 test_move_dir =
14545 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14546
14547 test_element = Feld[test_x][test_y];
14548
14549 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14550 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14551 */
14552 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14553 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14554 {
14555 /* good thing is player or penguin that does not move away */
14556 if (IS_PLAYER(test_x, test_y))
14557 {
14558 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14559
14560 if (bad_element == EL_ROBOT && player->is_moving)
14561 continue; /* robot does not kill player if he is moving */
14562
14563 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14564 {
14565 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14566 continue; /* center and border element do not touch */
14567 }
14568
14569 kill_x = test_x;
14570 kill_y = test_y;
14571
14572 break;
14573 }
14574 else if (test_element == EL_PENGUIN)
14575 {
14576 kill_x = test_x;
14577 kill_y = test_y;
14578
14579 break;
14580 }
14581 }
14582 }
14583
14584 if (kill_x != -1 || kill_y != -1)
14585 {
14586 if (IS_PLAYER(kill_x, kill_y))
14587 {
14588 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14589
14590 if (player->shield_deadly_time_left > 0 &&
14591 !IS_INDESTRUCTIBLE(bad_element))
14592 Bang(bad_x, bad_y);
14593 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14594 KillPlayer(player);
14595 }
14596 else
14597 Bang(kill_x, kill_y);
14598 }
14599 }
14600
TestIfGoodThingGetsHitByBadThing(int bad_x,int bad_y,int bad_move_dir)14601 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14602 {
14603 int bad_element = Feld[bad_x][bad_y];
14604 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14605 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14606 int test_x = bad_x + dx, test_y = bad_y + dy;
14607 int test_move_dir, test_element;
14608 int kill_x = -1, kill_y = -1;
14609
14610 if (!IN_LEV_FIELD(test_x, test_y))
14611 return;
14612
14613 test_move_dir =
14614 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14615
14616 test_element = Feld[test_x][test_y];
14617
14618 if (test_move_dir != bad_move_dir)
14619 {
14620 /* good thing can be player or penguin that does not move away */
14621 if (IS_PLAYER(test_x, test_y))
14622 {
14623 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14624
14625 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14626 player as being hit when he is moving towards the bad thing, because
14627 the "get hit by" condition would be lost after the player stops) */
14628 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14629 return; /* player moves away from bad thing */
14630
14631 kill_x = test_x;
14632 kill_y = test_y;
14633 }
14634 else if (test_element == EL_PENGUIN)
14635 {
14636 kill_x = test_x;
14637 kill_y = test_y;
14638 }
14639 }
14640
14641 if (kill_x != -1 || kill_y != -1)
14642 {
14643 if (IS_PLAYER(kill_x, kill_y))
14644 {
14645 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14646
14647 if (player->shield_deadly_time_left > 0 &&
14648 !IS_INDESTRUCTIBLE(bad_element))
14649 Bang(bad_x, bad_y);
14650 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14651 KillPlayer(player);
14652 }
14653 else
14654 Bang(kill_x, kill_y);
14655 }
14656 }
14657
TestIfPlayerTouchesBadThing(int x,int y)14658 void TestIfPlayerTouchesBadThing(int x, int y)
14659 {
14660 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14661 }
14662
TestIfPlayerRunsIntoBadThing(int x,int y,int move_dir)14663 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14664 {
14665 TestIfGoodThingHitsBadThing(x, y, move_dir);
14666 }
14667
TestIfBadThingTouchesPlayer(int x,int y)14668 void TestIfBadThingTouchesPlayer(int x, int y)
14669 {
14670 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14671 }
14672
TestIfBadThingRunsIntoPlayer(int x,int y,int move_dir)14673 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14674 {
14675 TestIfBadThingHitsGoodThing(x, y, move_dir);
14676 }
14677
TestIfFriendTouchesBadThing(int x,int y)14678 void TestIfFriendTouchesBadThing(int x, int y)
14679 {
14680 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14681 }
14682
TestIfBadThingTouchesFriend(int x,int y)14683 void TestIfBadThingTouchesFriend(int x, int y)
14684 {
14685 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14686 }
14687
TestIfBadThingTouchesOtherBadThing(int bad_x,int bad_y)14688 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14689 {
14690 int i, kill_x = bad_x, kill_y = bad_y;
14691 static int xy[4][2] =
14692 {
14693 { 0, -1 },
14694 { -1, 0 },
14695 { +1, 0 },
14696 { 0, +1 }
14697 };
14698
14699 for (i = 0; i < NUM_DIRECTIONS; i++)
14700 {
14701 int x, y, element;
14702
14703 x = bad_x + xy[i][0];
14704 y = bad_y + xy[i][1];
14705 if (!IN_LEV_FIELD(x, y))
14706 continue;
14707
14708 element = Feld[x][y];
14709 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14710 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14711 {
14712 kill_x = x;
14713 kill_y = y;
14714 break;
14715 }
14716 }
14717
14718 if (kill_x != bad_x || kill_y != bad_y)
14719 Bang(bad_x, bad_y);
14720 }
14721
KillPlayer(struct PlayerInfo * player)14722 void KillPlayer(struct PlayerInfo *player)
14723 {
14724 int jx = player->jx, jy = player->jy;
14725
14726 if (!player->active)
14727 return;
14728
14729 #if 0
14730 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14731 player->killed, player->active, player->reanimated);
14732 #endif
14733
14734 /* the following code was introduced to prevent an infinite loop when calling
14735 -> Bang()
14736 -> CheckTriggeredElementChangeExt()
14737 -> ExecuteCustomElementAction()
14738 -> KillPlayer()
14739 -> (infinitely repeating the above sequence of function calls)
14740 which occurs when killing the player while having a CE with the setting
14741 "kill player X when explosion of <player X>"; the solution using a new
14742 field "player->killed" was chosen for backwards compatibility, although
14743 clever use of the fields "player->active" etc. would probably also work */
14744 #if 1
14745 if (player->killed)
14746 return;
14747 #endif
14748
14749 player->killed = TRUE;
14750
14751 /* remove accessible field at the player's position */
14752 Feld[jx][jy] = EL_EMPTY;
14753
14754 /* deactivate shield (else Bang()/Explode() would not work right) */
14755 player->shield_normal_time_left = 0;
14756 player->shield_deadly_time_left = 0;
14757
14758 #if 0
14759 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14760 player->killed, player->active, player->reanimated);
14761 #endif
14762
14763 Bang(jx, jy);
14764
14765 #if 0
14766 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14767 player->killed, player->active, player->reanimated);
14768 #endif
14769
14770 #if USE_PLAYER_REANIMATION
14771 #if 1
14772 if (player->reanimated) /* killed player may have been reanimated */
14773 player->killed = player->reanimated = FALSE;
14774 else
14775 BuryPlayer(player);
14776 #else
14777 if (player->killed) /* player may have been reanimated */
14778 BuryPlayer(player);
14779 #endif
14780 #else
14781 BuryPlayer(player);
14782 #endif
14783 }
14784
KillPlayerUnlessEnemyProtected(int x,int y)14785 static void KillPlayerUnlessEnemyProtected(int x, int y)
14786 {
14787 if (!PLAYER_ENEMY_PROTECTED(x, y))
14788 KillPlayer(PLAYERINFO(x, y));
14789 }
14790
KillPlayerUnlessExplosionProtected(int x,int y)14791 static void KillPlayerUnlessExplosionProtected(int x, int y)
14792 {
14793 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14794 KillPlayer(PLAYERINFO(x, y));
14795 }
14796
BuryPlayer(struct PlayerInfo * player)14797 void BuryPlayer(struct PlayerInfo *player)
14798 {
14799 int jx = player->jx, jy = player->jy;
14800
14801 if (!player->active)
14802 return;
14803
14804 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14805 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14806
14807 player->GameOver = TRUE;
14808 RemovePlayer(player);
14809 }
14810
RemovePlayer(struct PlayerInfo * player)14811 void RemovePlayer(struct PlayerInfo *player)
14812 {
14813 int jx = player->jx, jy = player->jy;
14814 int i, found = FALSE;
14815
14816 player->present = FALSE;
14817 player->active = FALSE;
14818
14819 if (!ExplodeField[jx][jy])
14820 StorePlayer[jx][jy] = 0;
14821
14822 if (player->is_moving)
14823 TEST_DrawLevelField(player->last_jx, player->last_jy);
14824
14825 for (i = 0; i < MAX_PLAYERS; i++)
14826 if (stored_player[i].active)
14827 found = TRUE;
14828
14829 if (!found)
14830 AllPlayersGone = TRUE;
14831
14832 ExitX = ZX = jx;
14833 ExitY = ZY = jy;
14834 }
14835
14836 #if USE_NEW_SNAP_DELAY
setFieldForSnapping(int x,int y,int element,int direction)14837 static void setFieldForSnapping(int x, int y, int element, int direction)
14838 {
14839 struct ElementInfo *ei = &element_info[element];
14840 int direction_bit = MV_DIR_TO_BIT(direction);
14841 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14842 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14843 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14844
14845 Feld[x][y] = EL_ELEMENT_SNAPPING;
14846 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14847
14848 ResetGfxAnimation(x, y);
14849
14850 GfxElement[x][y] = element;
14851 GfxAction[x][y] = action;
14852 GfxDir[x][y] = direction;
14853 GfxFrame[x][y] = -1;
14854 }
14855 #endif
14856
14857 /*
14858 =============================================================================
14859 checkDiagonalPushing()
14860 -----------------------------------------------------------------------------
14861 check if diagonal input device direction results in pushing of object
14862 (by checking if the alternative direction is walkable, diggable, ...)
14863 =============================================================================
14864 */
14865
checkDiagonalPushing(struct PlayerInfo * player,int x,int y,int real_dx,int real_dy)14866 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14867 int x, int y, int real_dx, int real_dy)
14868 {
14869 int jx, jy, dx, dy, xx, yy;
14870
14871 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14872 return TRUE;
14873
14874 /* diagonal direction: check alternative direction */
14875 jx = player->jx;
14876 jy = player->jy;
14877 dx = x - jx;
14878 dy = y - jy;
14879 xx = jx + (dx == 0 ? real_dx : 0);
14880 yy = jy + (dy == 0 ? real_dy : 0);
14881
14882 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14883 }
14884
14885 /*
14886 =============================================================================
14887 DigField()
14888 -----------------------------------------------------------------------------
14889 x, y: field next to player (non-diagonal) to try to dig to
14890 real_dx, real_dy: direction as read from input device (can be diagonal)
14891 =============================================================================
14892 */
14893
DigField(struct PlayerInfo * player,int oldx,int oldy,int x,int y,int real_dx,int real_dy,int mode)14894 static int DigField(struct PlayerInfo *player,
14895 int oldx, int oldy, int x, int y,
14896 int real_dx, int real_dy, int mode)
14897 {
14898 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14899 boolean player_was_pushing = player->is_pushing;
14900 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14901 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14902 int jx = oldx, jy = oldy;
14903 int dx = x - jx, dy = y - jy;
14904 int nextx = x + dx, nexty = y + dy;
14905 int move_direction = (dx == -1 ? MV_LEFT :
14906 dx == +1 ? MV_RIGHT :
14907 dy == -1 ? MV_UP :
14908 dy == +1 ? MV_DOWN : MV_NONE);
14909 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14910 int dig_side = MV_DIR_OPPOSITE(move_direction);
14911 int old_element = Feld[jx][jy];
14912 #if USE_FIXED_DONT_RUN_INTO
14913 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14914 #else
14915 int element;
14916 #endif
14917 int collect_count;
14918
14919 if (is_player) /* function can also be called by EL_PENGUIN */
14920 {
14921 if (player->MovPos == 0)
14922 {
14923 player->is_digging = FALSE;
14924 player->is_collecting = FALSE;
14925 }
14926
14927 if (player->MovPos == 0) /* last pushing move finished */
14928 player->is_pushing = FALSE;
14929
14930 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14931 {
14932 player->is_switching = FALSE;
14933 player->push_delay = -1;
14934
14935 return MP_NO_ACTION;
14936 }
14937 }
14938
14939 #if !USE_FIXED_DONT_RUN_INTO
14940 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14941 return MP_NO_ACTION;
14942 #endif
14943
14944 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14945 old_element = Back[jx][jy];
14946
14947 /* in case of element dropped at player position, check background */
14948 else if (Back[jx][jy] != EL_EMPTY &&
14949 game.engine_version >= VERSION_IDENT(2,2,0,0))
14950 old_element = Back[jx][jy];
14951
14952 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14953 return MP_NO_ACTION; /* field has no opening in this direction */
14954
14955 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14956 return MP_NO_ACTION; /* field has no opening in this direction */
14957
14958 #if USE_FIXED_DONT_RUN_INTO
14959 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14960 {
14961 SplashAcid(x, y);
14962
14963 Feld[jx][jy] = player->artwork_element;
14964 InitMovingField(jx, jy, MV_DOWN);
14965 Store[jx][jy] = EL_ACID;
14966 ContinueMoving(jx, jy);
14967 BuryPlayer(player);
14968
14969 return MP_DONT_RUN_INTO;
14970 }
14971 #endif
14972
14973 #if USE_FIXED_DONT_RUN_INTO
14974 if (player_can_move && DONT_RUN_INTO(element))
14975 {
14976 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14977
14978 return MP_DONT_RUN_INTO;
14979 }
14980 #endif
14981
14982 #if USE_FIXED_DONT_RUN_INTO
14983 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14984 return MP_NO_ACTION;
14985 #endif
14986
14987 #if !USE_FIXED_DONT_RUN_INTO
14988 element = Feld[x][y];
14989 #endif
14990
14991 collect_count = element_info[element].collect_count_initial;
14992
14993 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14994 return MP_NO_ACTION;
14995
14996 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14997 player_can_move = player_can_move_or_snap;
14998
14999 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15000 game.engine_version >= VERSION_IDENT(2,2,0,0))
15001 {
15002 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15003 player->index_bit, dig_side);
15004 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15005 player->index_bit, dig_side);
15006
15007 if (element == EL_DC_LANDMINE)
15008 Bang(x, y);
15009
15010 if (Feld[x][y] != element) /* field changed by snapping */
15011 return MP_ACTION;
15012
15013 return MP_NO_ACTION;
15014 }
15015
15016 #if USE_PLAYER_GRAVITY
15017 if (player->gravity && is_player && !player->is_auto_moving &&
15018 canFallDown(player) && move_direction != MV_DOWN &&
15019 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15020 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15021 #else
15022 if (game.gravity && is_player && !player->is_auto_moving &&
15023 canFallDown(player) && move_direction != MV_DOWN &&
15024 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15025 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15026 #endif
15027
15028 if (player_can_move &&
15029 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15030 {
15031 int sound_element = SND_ELEMENT(element);
15032 int sound_action = ACTION_WALKING;
15033
15034 if (IS_RND_GATE(element))
15035 {
15036 if (!player->key[RND_GATE_NR(element)])
15037 return MP_NO_ACTION;
15038 }
15039 else if (IS_RND_GATE_GRAY(element))
15040 {
15041 if (!player->key[RND_GATE_GRAY_NR(element)])
15042 return MP_NO_ACTION;
15043 }
15044 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15045 {
15046 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15047 return MP_NO_ACTION;
15048 }
15049 else if (element == EL_EXIT_OPEN ||
15050 element == EL_EM_EXIT_OPEN ||
15051 #if 1
15052 element == EL_EM_EXIT_OPENING ||
15053 #endif
15054 element == EL_STEEL_EXIT_OPEN ||
15055 element == EL_EM_STEEL_EXIT_OPEN ||
15056 #if 1
15057 element == EL_EM_STEEL_EXIT_OPENING ||
15058 #endif
15059 element == EL_SP_EXIT_OPEN ||
15060 element == EL_SP_EXIT_OPENING)
15061 {
15062 sound_action = ACTION_PASSING; /* player is passing exit */
15063 }
15064 else if (element == EL_EMPTY)
15065 {
15066 sound_action = ACTION_MOVING; /* nothing to walk on */
15067 }
15068
15069 /* play sound from background or player, whatever is available */
15070 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15071 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15072 else
15073 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15074 }
15075 else if (player_can_move &&
15076 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15077 {
15078 if (!ACCESS_FROM(element, opposite_direction))
15079 return MP_NO_ACTION; /* field not accessible from this direction */
15080
15081 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15082 return MP_NO_ACTION;
15083
15084 if (IS_EM_GATE(element))
15085 {
15086 if (!player->key[EM_GATE_NR(element)])
15087 return MP_NO_ACTION;
15088 }
15089 else if (IS_EM_GATE_GRAY(element))
15090 {
15091 if (!player->key[EM_GATE_GRAY_NR(element)])
15092 return MP_NO_ACTION;
15093 }
15094 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15095 {
15096 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15097 return MP_NO_ACTION;
15098 }
15099 else if (IS_EMC_GATE(element))
15100 {
15101 if (!player->key[EMC_GATE_NR(element)])
15102 return MP_NO_ACTION;
15103 }
15104 else if (IS_EMC_GATE_GRAY(element))
15105 {
15106 if (!player->key[EMC_GATE_GRAY_NR(element)])
15107 return MP_NO_ACTION;
15108 }
15109 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15110 {
15111 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15112 return MP_NO_ACTION;
15113 }
15114 else if (element == EL_DC_GATE_WHITE ||
15115 element == EL_DC_GATE_WHITE_GRAY ||
15116 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15117 {
15118 if (player->num_white_keys == 0)
15119 return MP_NO_ACTION;
15120
15121 player->num_white_keys--;
15122 }
15123 else if (IS_SP_PORT(element))
15124 {
15125 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15126 element == EL_SP_GRAVITY_PORT_RIGHT ||
15127 element == EL_SP_GRAVITY_PORT_UP ||
15128 element == EL_SP_GRAVITY_PORT_DOWN)
15129 #if USE_PLAYER_GRAVITY
15130 player->gravity = !player->gravity;
15131 #else
15132 game.gravity = !game.gravity;
15133 #endif
15134 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15135 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15136 element == EL_SP_GRAVITY_ON_PORT_UP ||
15137 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15138 #if USE_PLAYER_GRAVITY
15139 player->gravity = TRUE;
15140 #else
15141 game.gravity = TRUE;
15142 #endif
15143 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15144 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15145 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15146 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15147 #if USE_PLAYER_GRAVITY
15148 player->gravity = FALSE;
15149 #else
15150 game.gravity = FALSE;
15151 #endif
15152 }
15153
15154 /* automatically move to the next field with double speed */
15155 player->programmed_action = move_direction;
15156
15157 if (player->move_delay_reset_counter == 0)
15158 {
15159 player->move_delay_reset_counter = 2; /* two double speed steps */
15160
15161 DOUBLE_PLAYER_SPEED(player);
15162 }
15163
15164 PlayLevelSoundAction(x, y, ACTION_PASSING);
15165 }
15166 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15167 {
15168 RemoveField(x, y);
15169
15170 if (mode != DF_SNAP)
15171 {
15172 GfxElement[x][y] = GFX_ELEMENT(element);
15173 player->is_digging = TRUE;
15174 }
15175
15176 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15177
15178 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15179 player->index_bit, dig_side);
15180
15181 if (mode == DF_SNAP)
15182 {
15183 #if USE_NEW_SNAP_DELAY
15184 if (level.block_snap_field)
15185 setFieldForSnapping(x, y, element, move_direction);
15186 else
15187 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15188 #else
15189 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15190 #endif
15191
15192 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15193 player->index_bit, dig_side);
15194 }
15195 }
15196 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15197 {
15198 RemoveField(x, y);
15199
15200 if (is_player && mode != DF_SNAP)
15201 {
15202 GfxElement[x][y] = element;
15203 player->is_collecting = TRUE;
15204 }
15205
15206 if (element == EL_SPEED_PILL)
15207 {
15208 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15209 }
15210 else if (element == EL_EXTRA_TIME && level.time > 0)
15211 {
15212 TimeLeft += level.extra_time;
15213
15214 #if 1
15215 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15216
15217 DisplayGameControlValues();
15218 #else
15219 DrawGameValue_Time(TimeLeft);
15220 #endif
15221 }
15222 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15223 {
15224 player->shield_normal_time_left += level.shield_normal_time;
15225 if (element == EL_SHIELD_DEADLY)
15226 player->shield_deadly_time_left += level.shield_deadly_time;
15227 }
15228 else if (element == EL_DYNAMITE ||
15229 element == EL_EM_DYNAMITE ||
15230 element == EL_SP_DISK_RED)
15231 {
15232 if (player->inventory_size < MAX_INVENTORY_SIZE)
15233 player->inventory_element[player->inventory_size++] = element;
15234
15235 DrawGameDoorValues();
15236 }
15237 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15238 {
15239 player->dynabomb_count++;
15240 player->dynabombs_left++;
15241 }
15242 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15243 {
15244 player->dynabomb_size++;
15245 }
15246 else if (element == EL_DYNABOMB_INCREASE_POWER)
15247 {
15248 player->dynabomb_xl = TRUE;
15249 }
15250 else if (IS_KEY(element))
15251 {
15252 player->key[KEY_NR(element)] = TRUE;
15253
15254 DrawGameDoorValues();
15255 }
15256 else if (element == EL_DC_KEY_WHITE)
15257 {
15258 player->num_white_keys++;
15259
15260 /* display white keys? */
15261 /* DrawGameDoorValues(); */
15262 }
15263 else if (IS_ENVELOPE(element))
15264 {
15265 player->show_envelope = element;
15266 }
15267 else if (element == EL_EMC_LENSES)
15268 {
15269 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15270
15271 RedrawAllInvisibleElementsForLenses();
15272 }
15273 else if (element == EL_EMC_MAGNIFIER)
15274 {
15275 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15276
15277 RedrawAllInvisibleElementsForMagnifier();
15278 }
15279 else if (IS_DROPPABLE(element) ||
15280 IS_THROWABLE(element)) /* can be collected and dropped */
15281 {
15282 int i;
15283
15284 if (collect_count == 0)
15285 player->inventory_infinite_element = element;
15286 else
15287 for (i = 0; i < collect_count; i++)
15288 if (player->inventory_size < MAX_INVENTORY_SIZE)
15289 player->inventory_element[player->inventory_size++] = element;
15290
15291 DrawGameDoorValues();
15292 }
15293 else if (collect_count > 0)
15294 {
15295 local_player->gems_still_needed -= collect_count;
15296 if (local_player->gems_still_needed < 0)
15297 local_player->gems_still_needed = 0;
15298
15299 #if 1
15300 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15301
15302 DisplayGameControlValues();
15303 #else
15304 DrawGameValue_Emeralds(local_player->gems_still_needed);
15305 #endif
15306 }
15307
15308 RaiseScoreElement(element);
15309 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15310
15311 if (is_player)
15312 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15313 player->index_bit, dig_side);
15314
15315 if (mode == DF_SNAP)
15316 {
15317 #if USE_NEW_SNAP_DELAY
15318 if (level.block_snap_field)
15319 setFieldForSnapping(x, y, element, move_direction);
15320 else
15321 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15322 #else
15323 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15324 #endif
15325
15326 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15327 player->index_bit, dig_side);
15328 }
15329 }
15330 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15331 {
15332 if (mode == DF_SNAP && element != EL_BD_ROCK)
15333 return MP_NO_ACTION;
15334
15335 if (CAN_FALL(element) && dy)
15336 return MP_NO_ACTION;
15337
15338 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15339 !(element == EL_SPRING && level.use_spring_bug))
15340 return MP_NO_ACTION;
15341
15342 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15343 ((move_direction & MV_VERTICAL &&
15344 ((element_info[element].move_pattern & MV_LEFT &&
15345 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15346 (element_info[element].move_pattern & MV_RIGHT &&
15347 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15348 (move_direction & MV_HORIZONTAL &&
15349 ((element_info[element].move_pattern & MV_UP &&
15350 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15351 (element_info[element].move_pattern & MV_DOWN &&
15352 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15353 return MP_NO_ACTION;
15354
15355 /* do not push elements already moving away faster than player */
15356 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15357 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15358 return MP_NO_ACTION;
15359
15360 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15361 {
15362 if (player->push_delay_value == -1 || !player_was_pushing)
15363 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15364 }
15365 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15366 {
15367 if (player->push_delay_value == -1)
15368 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15369 }
15370 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15371 {
15372 if (!player->is_pushing)
15373 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15374 }
15375
15376 player->is_pushing = TRUE;
15377 player->is_active = TRUE;
15378
15379 if (!(IN_LEV_FIELD(nextx, nexty) &&
15380 (IS_FREE(nextx, nexty) ||
15381 (IS_SB_ELEMENT(element) &&
15382 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15383 (IS_CUSTOM_ELEMENT(element) &&
15384 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15385 return MP_NO_ACTION;
15386
15387 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15388 return MP_NO_ACTION;
15389
15390 if (player->push_delay == -1) /* new pushing; restart delay */
15391 player->push_delay = 0;
15392
15393 if (player->push_delay < player->push_delay_value &&
15394 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15395 element != EL_SPRING && element != EL_BALLOON)
15396 {
15397 /* make sure that there is no move delay before next try to push */
15398 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15399 player->move_delay = 0;
15400
15401 return MP_NO_ACTION;
15402 }
15403
15404 if (IS_CUSTOM_ELEMENT(element) &&
15405 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15406 {
15407 if (!DigFieldByCE(nextx, nexty, element))
15408 return MP_NO_ACTION;
15409 }
15410
15411 if (IS_SB_ELEMENT(element))
15412 {
15413 if (element == EL_SOKOBAN_FIELD_FULL)
15414 {
15415 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15416 local_player->sokobanfields_still_needed++;
15417 }
15418
15419 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15420 {
15421 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15422 local_player->sokobanfields_still_needed--;
15423 }
15424
15425 Feld[x][y] = EL_SOKOBAN_OBJECT;
15426
15427 if (Back[x][y] == Back[nextx][nexty])
15428 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15429 else if (Back[x][y] != 0)
15430 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15431 ACTION_EMPTYING);
15432 else
15433 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15434 ACTION_FILLING);
15435
15436 #if 1
15437 if (local_player->sokobanfields_still_needed == 0 &&
15438 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15439 #else
15440 if (local_player->sokobanfields_still_needed == 0 &&
15441 game.emulation == EMU_SOKOBAN)
15442 #endif
15443 {
15444 PlayerWins(player);
15445
15446 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15447 }
15448 }
15449 else
15450 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15451
15452 InitMovingField(x, y, move_direction);
15453 GfxAction[x][y] = ACTION_PUSHING;
15454
15455 if (mode == DF_SNAP)
15456 ContinueMoving(x, y);
15457 else
15458 MovPos[x][y] = (dx != 0 ? dx : dy);
15459
15460 Pushed[x][y] = TRUE;
15461 Pushed[nextx][nexty] = TRUE;
15462
15463 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15464 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15465 else
15466 player->push_delay_value = -1; /* get new value later */
15467
15468 /* check for element change _after_ element has been pushed */
15469 if (game.use_change_when_pushing_bug)
15470 {
15471 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15472 player->index_bit, dig_side);
15473 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15474 player->index_bit, dig_side);
15475 }
15476 }
15477 else if (IS_SWITCHABLE(element))
15478 {
15479 if (PLAYER_SWITCHING(player, x, y))
15480 {
15481 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15482 player->index_bit, dig_side);
15483
15484 return MP_ACTION;
15485 }
15486
15487 player->is_switching = TRUE;
15488 player->switch_x = x;
15489 player->switch_y = y;
15490
15491 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15492
15493 if (element == EL_ROBOT_WHEEL)
15494 {
15495 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15496 ZX = x;
15497 ZY = y;
15498
15499 game.robot_wheel_active = TRUE;
15500
15501 TEST_DrawLevelField(x, y);
15502 }
15503 else if (element == EL_SP_TERMINAL)
15504 {
15505 int xx, yy;
15506
15507 SCAN_PLAYFIELD(xx, yy)
15508 {
15509 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15510 Bang(xx, yy);
15511 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15512 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15513 }
15514 }
15515 else if (IS_BELT_SWITCH(element))
15516 {
15517 ToggleBeltSwitch(x, y);
15518 }
15519 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15520 element == EL_SWITCHGATE_SWITCH_DOWN ||
15521 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15522 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15523 {
15524 ToggleSwitchgateSwitch(x, y);
15525 }
15526 else if (element == EL_LIGHT_SWITCH ||
15527 element == EL_LIGHT_SWITCH_ACTIVE)
15528 {
15529 ToggleLightSwitch(x, y);
15530 }
15531 else if (element == EL_TIMEGATE_SWITCH ||
15532 element == EL_DC_TIMEGATE_SWITCH)
15533 {
15534 ActivateTimegateSwitch(x, y);
15535 }
15536 else if (element == EL_BALLOON_SWITCH_LEFT ||
15537 element == EL_BALLOON_SWITCH_RIGHT ||
15538 element == EL_BALLOON_SWITCH_UP ||
15539 element == EL_BALLOON_SWITCH_DOWN ||
15540 element == EL_BALLOON_SWITCH_NONE ||
15541 element == EL_BALLOON_SWITCH_ANY)
15542 {
15543 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15544 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15545 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15546 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15547 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15548 move_direction);
15549 }
15550 else if (element == EL_LAMP)
15551 {
15552 Feld[x][y] = EL_LAMP_ACTIVE;
15553 local_player->lights_still_needed--;
15554
15555 ResetGfxAnimation(x, y);
15556 TEST_DrawLevelField(x, y);
15557 }
15558 else if (element == EL_TIME_ORB_FULL)
15559 {
15560 Feld[x][y] = EL_TIME_ORB_EMPTY;
15561
15562 if (level.time > 0 || level.use_time_orb_bug)
15563 {
15564 TimeLeft += level.time_orb_time;
15565
15566 #if 1
15567 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15568
15569 DisplayGameControlValues();
15570 #else
15571 DrawGameValue_Time(TimeLeft);
15572 #endif
15573 }
15574
15575 ResetGfxAnimation(x, y);
15576 TEST_DrawLevelField(x, y);
15577 }
15578 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15579 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15580 {
15581 int xx, yy;
15582
15583 game.ball_state = !game.ball_state;
15584
15585 SCAN_PLAYFIELD(xx, yy)
15586 {
15587 int e = Feld[xx][yy];
15588
15589 if (game.ball_state)
15590 {
15591 if (e == EL_EMC_MAGIC_BALL)
15592 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15593 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15594 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15595 }
15596 else
15597 {
15598 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15599 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15600 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15601 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15602 }
15603 }
15604 }
15605
15606 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15607 player->index_bit, dig_side);
15608
15609 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15610 player->index_bit, dig_side);
15611
15612 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15613 player->index_bit, dig_side);
15614
15615 return MP_ACTION;
15616 }
15617 else
15618 {
15619 if (!PLAYER_SWITCHING(player, x, y))
15620 {
15621 player->is_switching = TRUE;
15622 player->switch_x = x;
15623 player->switch_y = y;
15624
15625 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15626 player->index_bit, dig_side);
15627 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15628 player->index_bit, dig_side);
15629
15630 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15631 player->index_bit, dig_side);
15632 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15633 player->index_bit, dig_side);
15634 }
15635
15636 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15637 player->index_bit, dig_side);
15638 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15639 player->index_bit, dig_side);
15640
15641 return MP_NO_ACTION;
15642 }
15643
15644 player->push_delay = -1;
15645
15646 if (is_player) /* function can also be called by EL_PENGUIN */
15647 {
15648 if (Feld[x][y] != element) /* really digged/collected something */
15649 {
15650 player->is_collecting = !player->is_digging;
15651 player->is_active = TRUE;
15652 }
15653 }
15654
15655 return MP_MOVING;
15656 }
15657
DigFieldByCE(int x,int y,int digging_element)15658 static boolean DigFieldByCE(int x, int y, int digging_element)
15659 {
15660 int element = Feld[x][y];
15661
15662 if (!IS_FREE(x, y))
15663 {
15664 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15665 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15666 ACTION_BREAKING);
15667
15668 /* no element can dig solid indestructible elements */
15669 if (IS_INDESTRUCTIBLE(element) &&
15670 !IS_DIGGABLE(element) &&
15671 !IS_COLLECTIBLE(element))
15672 return FALSE;
15673
15674 if (AmoebaNr[x][y] &&
15675 (element == EL_AMOEBA_FULL ||
15676 element == EL_BD_AMOEBA ||
15677 element == EL_AMOEBA_GROWING))
15678 {
15679 AmoebaCnt[AmoebaNr[x][y]]--;
15680 AmoebaCnt2[AmoebaNr[x][y]]--;
15681 }
15682
15683 if (IS_MOVING(x, y))
15684 RemoveMovingField(x, y);
15685 else
15686 {
15687 RemoveField(x, y);
15688 TEST_DrawLevelField(x, y);
15689 }
15690
15691 /* if digged element was about to explode, prevent the explosion */
15692 ExplodeField[x][y] = EX_TYPE_NONE;
15693
15694 PlayLevelSoundAction(x, y, action);
15695 }
15696
15697 Store[x][y] = EL_EMPTY;
15698
15699 #if 1
15700 /* this makes it possible to leave the removed element again */
15701 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15702 Store[x][y] = element;
15703 #else
15704 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15705 {
15706 int move_leave_element = element_info[digging_element].move_leave_element;
15707
15708 /* this makes it possible to leave the removed element again */
15709 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15710 element : move_leave_element);
15711 }
15712 #endif
15713
15714 return TRUE;
15715 }
15716
SnapField(struct PlayerInfo * player,int dx,int dy)15717 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15718 {
15719 int jx = player->jx, jy = player->jy;
15720 int x = jx + dx, y = jy + dy;
15721 int snap_direction = (dx == -1 ? MV_LEFT :
15722 dx == +1 ? MV_RIGHT :
15723 dy == -1 ? MV_UP :
15724 dy == +1 ? MV_DOWN : MV_NONE);
15725 boolean can_continue_snapping = (level.continuous_snapping &&
15726 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15727
15728 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15729 return FALSE;
15730
15731 if (!player->active || !IN_LEV_FIELD(x, y))
15732 return FALSE;
15733
15734 if (dx && dy)
15735 return FALSE;
15736
15737 if (!dx && !dy)
15738 {
15739 if (player->MovPos == 0)
15740 player->is_pushing = FALSE;
15741
15742 player->is_snapping = FALSE;
15743
15744 if (player->MovPos == 0)
15745 {
15746 player->is_moving = FALSE;
15747 player->is_digging = FALSE;
15748 player->is_collecting = FALSE;
15749 }
15750
15751 return FALSE;
15752 }
15753
15754 #if USE_NEW_CONTINUOUS_SNAPPING
15755 /* prevent snapping with already pressed snap key when not allowed */
15756 if (player->is_snapping && !can_continue_snapping)
15757 return FALSE;
15758 #else
15759 if (player->is_snapping)
15760 return FALSE;
15761 #endif
15762
15763 player->MovDir = snap_direction;
15764
15765 if (player->MovPos == 0)
15766 {
15767 player->is_moving = FALSE;
15768 player->is_digging = FALSE;
15769 player->is_collecting = FALSE;
15770 }
15771
15772 player->is_dropping = FALSE;
15773 player->is_dropping_pressed = FALSE;
15774 player->drop_pressed_delay = 0;
15775
15776 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15777 return FALSE;
15778
15779 player->is_snapping = TRUE;
15780 player->is_active = TRUE;
15781
15782 if (player->MovPos == 0)
15783 {
15784 player->is_moving = FALSE;
15785 player->is_digging = FALSE;
15786 player->is_collecting = FALSE;
15787 }
15788
15789 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15790 TEST_DrawLevelField(player->last_jx, player->last_jy);
15791
15792 TEST_DrawLevelField(x, y);
15793
15794 return TRUE;
15795 }
15796
DropElement(struct PlayerInfo * player)15797 static boolean DropElement(struct PlayerInfo *player)
15798 {
15799 int old_element, new_element;
15800 int dropx = player->jx, dropy = player->jy;
15801 int drop_direction = player->MovDir;
15802 int drop_side = drop_direction;
15803 #if 1
15804 int drop_element = get_next_dropped_element(player);
15805 #else
15806 int drop_element = (player->inventory_size > 0 ?
15807 player->inventory_element[player->inventory_size - 1] :
15808 player->inventory_infinite_element != EL_UNDEFINED ?
15809 player->inventory_infinite_element :
15810 player->dynabombs_left > 0 ?
15811 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15812 EL_UNDEFINED);
15813 #endif
15814
15815 player->is_dropping_pressed = TRUE;
15816
15817 /* do not drop an element on top of another element; when holding drop key
15818 pressed without moving, dropped element must move away before the next
15819 element can be dropped (this is especially important if the next element
15820 is dynamite, which can be placed on background for historical reasons) */
15821 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15822 return MP_ACTION;
15823
15824 if (IS_THROWABLE(drop_element))
15825 {
15826 dropx += GET_DX_FROM_DIR(drop_direction);
15827 dropy += GET_DY_FROM_DIR(drop_direction);
15828
15829 if (!IN_LEV_FIELD(dropx, dropy))
15830 return FALSE;
15831 }
15832
15833 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15834 new_element = drop_element; /* default: no change when dropping */
15835
15836 /* check if player is active, not moving and ready to drop */
15837 if (!player->active || player->MovPos || player->drop_delay > 0)
15838 return FALSE;
15839
15840 /* check if player has anything that can be dropped */
15841 if (new_element == EL_UNDEFINED)
15842 return FALSE;
15843
15844 /* check if drop key was pressed long enough for EM style dynamite */
15845 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15846 return FALSE;
15847
15848 /* check if anything can be dropped at the current position */
15849 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15850 return FALSE;
15851
15852 /* collected custom elements can only be dropped on empty fields */
15853 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15854 return FALSE;
15855
15856 if (old_element != EL_EMPTY)
15857 Back[dropx][dropy] = old_element; /* store old element on this field */
15858
15859 ResetGfxAnimation(dropx, dropy);
15860 ResetRandomAnimationValue(dropx, dropy);
15861
15862 if (player->inventory_size > 0 ||
15863 player->inventory_infinite_element != EL_UNDEFINED)
15864 {
15865 if (player->inventory_size > 0)
15866 {
15867 player->inventory_size--;
15868
15869 DrawGameDoorValues();
15870
15871 if (new_element == EL_DYNAMITE)
15872 new_element = EL_DYNAMITE_ACTIVE;
15873 else if (new_element == EL_EM_DYNAMITE)
15874 new_element = EL_EM_DYNAMITE_ACTIVE;
15875 else if (new_element == EL_SP_DISK_RED)
15876 new_element = EL_SP_DISK_RED_ACTIVE;
15877 }
15878
15879 Feld[dropx][dropy] = new_element;
15880
15881 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15882 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15883 el2img(Feld[dropx][dropy]), 0);
15884
15885 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15886
15887 /* needed if previous element just changed to "empty" in the last frame */
15888 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15889
15890 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15891 player->index_bit, drop_side);
15892 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15893 CE_PLAYER_DROPS_X,
15894 player->index_bit, drop_side);
15895
15896 TestIfElementTouchesCustomElement(dropx, dropy);
15897 }
15898 else /* player is dropping a dyna bomb */
15899 {
15900 player->dynabombs_left--;
15901
15902 Feld[dropx][dropy] = new_element;
15903
15904 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15905 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15906 el2img(Feld[dropx][dropy]), 0);
15907
15908 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15909 }
15910
15911 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15912 InitField_WithBug1(dropx, dropy, FALSE);
15913
15914 new_element = Feld[dropx][dropy]; /* element might have changed */
15915
15916 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15917 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15918 {
15919 int move_direction, nextx, nexty;
15920
15921 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15922 MovDir[dropx][dropy] = drop_direction;
15923
15924 move_direction = MovDir[dropx][dropy];
15925 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15926 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15927
15928 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15929
15930 #if USE_FIX_IMPACT_COLLISION
15931 /* do not cause impact style collision by dropping elements that can fall */
15932 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15933 #else
15934 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15935 #endif
15936 }
15937
15938 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15939 player->is_dropping = TRUE;
15940
15941 player->drop_pressed_delay = 0;
15942 player->is_dropping_pressed = FALSE;
15943
15944 player->drop_x = dropx;
15945 player->drop_y = dropy;
15946
15947 return TRUE;
15948 }
15949
15950 /* ------------------------------------------------------------------------- */
15951 /* game sound playing functions */
15952 /* ------------------------------------------------------------------------- */
15953
15954 static int *loop_sound_frame = NULL;
15955 static int *loop_sound_volume = NULL;
15956
InitPlayLevelSound()15957 void InitPlayLevelSound()
15958 {
15959 int num_sounds = getSoundListSize();
15960
15961 checked_free(loop_sound_frame);
15962 checked_free(loop_sound_volume);
15963
15964 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15965 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15966 }
15967
PlayLevelSound(int x,int y,int nr)15968 static void PlayLevelSound(int x, int y, int nr)
15969 {
15970 int sx = SCREENX(x), sy = SCREENY(y);
15971 int volume, stereo_position;
15972 int max_distance = 8;
15973 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15974
15975 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15976 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15977 return;
15978
15979 if (!IN_LEV_FIELD(x, y) ||
15980 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15981 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15982 return;
15983
15984 volume = SOUND_MAX_VOLUME;
15985
15986 if (!IN_SCR_FIELD(sx, sy))
15987 {
15988 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15989 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15990
15991 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15992 }
15993
15994 stereo_position = (SOUND_MAX_LEFT +
15995 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15996 (SCR_FIELDX + 2 * max_distance));
15997
15998 if (IS_LOOP_SOUND(nr))
15999 {
16000 /* This assures that quieter loop sounds do not overwrite louder ones,
16001 while restarting sound volume comparison with each new game frame. */
16002
16003 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16004 return;
16005
16006 loop_sound_volume[nr] = volume;
16007 loop_sound_frame[nr] = FrameCounter;
16008 }
16009
16010 PlaySoundExt(nr, volume, stereo_position, type);
16011 }
16012
PlayLevelSoundNearest(int x,int y,int sound_action)16013 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16014 {
16015 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16016 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16017 y < LEVELY(BY1) ? LEVELY(BY1) :
16018 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16019 sound_action);
16020 }
16021
PlayLevelSoundAction(int x,int y,int action)16022 static void PlayLevelSoundAction(int x, int y, int action)
16023 {
16024 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16025 }
16026
PlayLevelSoundElementAction(int x,int y,int element,int action)16027 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16028 {
16029 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16030
16031 if (sound_effect != SND_UNDEFINED)
16032 PlayLevelSound(x, y, sound_effect);
16033 }
16034
PlayLevelSoundElementActionIfLoop(int x,int y,int element,int action)16035 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16036 int action)
16037 {
16038 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16039
16040 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16041 PlayLevelSound(x, y, sound_effect);
16042 }
16043
PlayLevelSoundActionIfLoop(int x,int y,int action)16044 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16045 {
16046 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16047
16048 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16049 PlayLevelSound(x, y, sound_effect);
16050 }
16051
StopLevelSoundActionIfLoop(int x,int y,int action)16052 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16053 {
16054 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16055
16056 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16057 StopSound(sound_effect);
16058 }
16059
PlayLevelMusic()16060 static void PlayLevelMusic()
16061 {
16062 if (levelset.music[level_nr] != MUS_UNDEFINED)
16063 PlayMusic(levelset.music[level_nr]); /* from config file */
16064 else
16065 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16066 }
16067
PlayLevelSound_EM(int xx,int yy,int element_em,int sample)16068 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16069 {
16070 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16071 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16072 int x = xx - 1 - offset;
16073 int y = yy - 1 - offset;
16074
16075 switch (sample)
16076 {
16077 case SAMPLE_blank:
16078 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16079 break;
16080
16081 case SAMPLE_roll:
16082 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16083 break;
16084
16085 case SAMPLE_stone:
16086 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16087 break;
16088
16089 case SAMPLE_nut:
16090 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16091 break;
16092
16093 case SAMPLE_crack:
16094 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16095 break;
16096
16097 case SAMPLE_bug:
16098 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16099 break;
16100
16101 case SAMPLE_tank:
16102 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16103 break;
16104
16105 case SAMPLE_android_clone:
16106 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16107 break;
16108
16109 case SAMPLE_android_move:
16110 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16111 break;
16112
16113 case SAMPLE_spring:
16114 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16115 break;
16116
16117 case SAMPLE_slurp:
16118 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16119 break;
16120
16121 case SAMPLE_eater:
16122 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16123 break;
16124
16125 case SAMPLE_eater_eat:
16126 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16127 break;
16128
16129 case SAMPLE_alien:
16130 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16131 break;
16132
16133 case SAMPLE_collect:
16134 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16135 break;
16136
16137 case SAMPLE_diamond:
16138 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16139 break;
16140
16141 case SAMPLE_squash:
16142 /* !!! CHECK THIS !!! */
16143 #if 1
16144 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16145 #else
16146 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16147 #endif
16148 break;
16149
16150 case SAMPLE_wonderfall:
16151 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16152 break;
16153
16154 case SAMPLE_drip:
16155 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16156 break;
16157
16158 case SAMPLE_push:
16159 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16160 break;
16161
16162 case SAMPLE_dirt:
16163 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16164 break;
16165
16166 case SAMPLE_acid:
16167 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16168 break;
16169
16170 case SAMPLE_ball:
16171 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16172 break;
16173
16174 case SAMPLE_grow:
16175 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16176 break;
16177
16178 case SAMPLE_wonder:
16179 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16180 break;
16181
16182 case SAMPLE_door:
16183 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16184 break;
16185
16186 case SAMPLE_exit_open:
16187 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16188 break;
16189
16190 case SAMPLE_exit_leave:
16191 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16192 break;
16193
16194 case SAMPLE_dynamite:
16195 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16196 break;
16197
16198 case SAMPLE_tick:
16199 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16200 break;
16201
16202 case SAMPLE_press:
16203 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16204 break;
16205
16206 case SAMPLE_wheel:
16207 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16208 break;
16209
16210 case SAMPLE_boom:
16211 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16212 break;
16213
16214 case SAMPLE_die:
16215 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16216 break;
16217
16218 case SAMPLE_time:
16219 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16220 break;
16221
16222 default:
16223 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16224 break;
16225 }
16226 }
16227
PlayLevelSound_SP(int xx,int yy,int element_sp,int action_sp)16228 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16229 {
16230 int element = map_element_SP_to_RND(element_sp);
16231 int action = map_action_SP_to_RND(action_sp);
16232 int offset = (setup.sp_show_border_elements ? 0 : 1);
16233 int x = xx - offset;
16234 int y = yy - offset;
16235
16236 #if 0
16237 printf("::: %d -> %d\n", element_sp, action_sp);
16238 #endif
16239
16240 PlayLevelSoundElementAction(x, y, element, action);
16241 }
16242
16243 #if 0
16244 void ChangeTime(int value)
16245 {
16246 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16247
16248 *time += value;
16249
16250 /* EMC game engine uses value from time counter of RND game engine */
16251 level.native_em_level->lev->time = *time;
16252
16253 DrawGameValue_Time(*time);
16254 }
16255
16256 void RaiseScore(int value)
16257 {
16258 /* EMC game engine and RND game engine have separate score counters */
16259 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16260 &level.native_em_level->lev->score : &local_player->score);
16261
16262 *score += value;
16263
16264 DrawGameValue_Score(*score);
16265 }
16266 #endif
16267
RaiseScore(int value)16268 void RaiseScore(int value)
16269 {
16270 local_player->score += value;
16271
16272 #if 1
16273 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16274
16275 DisplayGameControlValues();
16276 #else
16277 DrawGameValue_Score(local_player->score);
16278 #endif
16279 }
16280
RaiseScoreElement(int element)16281 void RaiseScoreElement(int element)
16282 {
16283 switch (element)
16284 {
16285 case EL_EMERALD:
16286 case EL_BD_DIAMOND:
16287 case EL_EMERALD_YELLOW:
16288 case EL_EMERALD_RED:
16289 case EL_EMERALD_PURPLE:
16290 case EL_SP_INFOTRON:
16291 RaiseScore(level.score[SC_EMERALD]);
16292 break;
16293 case EL_DIAMOND:
16294 RaiseScore(level.score[SC_DIAMOND]);
16295 break;
16296 case EL_CRYSTAL:
16297 RaiseScore(level.score[SC_CRYSTAL]);
16298 break;
16299 case EL_PEARL:
16300 RaiseScore(level.score[SC_PEARL]);
16301 break;
16302 case EL_BUG:
16303 case EL_BD_BUTTERFLY:
16304 case EL_SP_ELECTRON:
16305 RaiseScore(level.score[SC_BUG]);
16306 break;
16307 case EL_SPACESHIP:
16308 case EL_BD_FIREFLY:
16309 case EL_SP_SNIKSNAK:
16310 RaiseScore(level.score[SC_SPACESHIP]);
16311 break;
16312 case EL_YAMYAM:
16313 case EL_DARK_YAMYAM:
16314 RaiseScore(level.score[SC_YAMYAM]);
16315 break;
16316 case EL_ROBOT:
16317 RaiseScore(level.score[SC_ROBOT]);
16318 break;
16319 case EL_PACMAN:
16320 RaiseScore(level.score[SC_PACMAN]);
16321 break;
16322 case EL_NUT:
16323 RaiseScore(level.score[SC_NUT]);
16324 break;
16325 case EL_DYNAMITE:
16326 case EL_EM_DYNAMITE:
16327 case EL_SP_DISK_RED:
16328 case EL_DYNABOMB_INCREASE_NUMBER:
16329 case EL_DYNABOMB_INCREASE_SIZE:
16330 case EL_DYNABOMB_INCREASE_POWER:
16331 RaiseScore(level.score[SC_DYNAMITE]);
16332 break;
16333 case EL_SHIELD_NORMAL:
16334 case EL_SHIELD_DEADLY:
16335 RaiseScore(level.score[SC_SHIELD]);
16336 break;
16337 case EL_EXTRA_TIME:
16338 RaiseScore(level.extra_time_score);
16339 break;
16340 case EL_KEY_1:
16341 case EL_KEY_2:
16342 case EL_KEY_3:
16343 case EL_KEY_4:
16344 case EL_EM_KEY_1:
16345 case EL_EM_KEY_2:
16346 case EL_EM_KEY_3:
16347 case EL_EM_KEY_4:
16348 case EL_EMC_KEY_5:
16349 case EL_EMC_KEY_6:
16350 case EL_EMC_KEY_7:
16351 case EL_EMC_KEY_8:
16352 case EL_DC_KEY_WHITE:
16353 RaiseScore(level.score[SC_KEY]);
16354 break;
16355 default:
16356 RaiseScore(element_info[element].collect_score);
16357 break;
16358 }
16359 }
16360
RequestQuitGameExt(boolean skip_request,boolean quick_quit,char * message)16361 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16362 {
16363 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16364 {
16365 #if defined(NETWORK_AVALIABLE)
16366 if (options.network)
16367 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16368 else
16369 #endif
16370 {
16371 if (quick_quit)
16372 {
16373 #if 1
16374
16375 #if 1
16376 FadeSkipNextFadeIn();
16377 #else
16378 fading = fading_none;
16379 #endif
16380
16381 #else
16382 OpenDoor(DOOR_CLOSE_1);
16383 #endif
16384
16385 game_status = GAME_MODE_MAIN;
16386
16387 #if 1
16388 DrawAndFadeInMainMenu(REDRAW_FIELD);
16389 #else
16390 DrawMainMenu();
16391 #endif
16392 }
16393 else
16394 {
16395 #if 0
16396 FadeOut(REDRAW_FIELD);
16397 #endif
16398
16399 game_status = GAME_MODE_MAIN;
16400
16401 DrawAndFadeInMainMenu(REDRAW_FIELD);
16402 }
16403 }
16404 }
16405 else /* continue playing the game */
16406 {
16407 if (tape.playing && tape.deactivate_display)
16408 TapeDeactivateDisplayOff(TRUE);
16409
16410 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16411
16412 if (tape.playing && tape.deactivate_display)
16413 TapeDeactivateDisplayOn();
16414 }
16415 }
16416
RequestQuitGame(boolean ask_if_really_quit)16417 void RequestQuitGame(boolean ask_if_really_quit)
16418 {
16419 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16420 boolean skip_request = AllPlayersGone || quick_quit;
16421
16422 RequestQuitGameExt(skip_request, quick_quit,
16423 "Do you really want to quit the game ?");
16424 }
16425
16426
16427 /* ------------------------------------------------------------------------- */
16428 /* random generator functions */
16429 /* ------------------------------------------------------------------------- */
16430
InitEngineRandom_RND(long seed)16431 unsigned int InitEngineRandom_RND(long seed)
16432 {
16433 game.num_random_calls = 0;
16434
16435 #if 0
16436 unsigned int rnd_seed = InitEngineRandom(seed);
16437
16438 printf("::: START RND: %d\n", rnd_seed);
16439
16440 return rnd_seed;
16441 #else
16442
16443 return InitEngineRandom(seed);
16444
16445 #endif
16446
16447 }
16448
RND(int max)16449 unsigned int RND(int max)
16450 {
16451 if (max > 0)
16452 {
16453 game.num_random_calls++;
16454
16455 return GetEngineRandom(max);
16456 }
16457
16458 return 0;
16459 }
16460
16461
16462 /* ------------------------------------------------------------------------- */
16463 /* game engine snapshot handling functions */
16464 /* ------------------------------------------------------------------------- */
16465
16466 struct EngineSnapshotInfo
16467 {
16468 /* runtime values for custom element collect score */
16469 int collect_score[NUM_CUSTOM_ELEMENTS];
16470
16471 /* runtime values for group element choice position */
16472 int choice_pos[NUM_GROUP_ELEMENTS];
16473
16474 /* runtime values for belt position animations */
16475 int belt_graphic[4][NUM_BELT_PARTS];
16476 int belt_anim_mode[4][NUM_BELT_PARTS];
16477 };
16478
16479 static struct EngineSnapshotInfo engine_snapshot_rnd;
16480 static char *snapshot_level_identifier = NULL;
16481 static int snapshot_level_nr = -1;
16482
SaveEngineSnapshotValues_RND()16483 static void SaveEngineSnapshotValues_RND()
16484 {
16485 static int belt_base_active_element[4] =
16486 {
16487 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16488 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16489 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16490 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16491 };
16492 int i, j;
16493
16494 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16495 {
16496 int element = EL_CUSTOM_START + i;
16497
16498 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16499 }
16500
16501 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16502 {
16503 int element = EL_GROUP_START + i;
16504
16505 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16506 }
16507
16508 for (i = 0; i < 4; i++)
16509 {
16510 for (j = 0; j < NUM_BELT_PARTS; j++)
16511 {
16512 int element = belt_base_active_element[i] + j;
16513 int graphic = el2img(element);
16514 int anim_mode = graphic_info[graphic].anim_mode;
16515
16516 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16517 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16518 }
16519 }
16520 }
16521
LoadEngineSnapshotValues_RND()16522 static void LoadEngineSnapshotValues_RND()
16523 {
16524 unsigned long num_random_calls = game.num_random_calls;
16525 int i, j;
16526
16527 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16528 {
16529 int element = EL_CUSTOM_START + i;
16530
16531 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16532 }
16533
16534 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16535 {
16536 int element = EL_GROUP_START + i;
16537
16538 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16539 }
16540
16541 for (i = 0; i < 4; i++)
16542 {
16543 for (j = 0; j < NUM_BELT_PARTS; j++)
16544 {
16545 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16546 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16547
16548 graphic_info[graphic].anim_mode = anim_mode;
16549 }
16550 }
16551
16552 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16553 {
16554 InitRND(tape.random_seed);
16555 for (i = 0; i < num_random_calls; i++)
16556 RND(1);
16557 }
16558
16559 if (game.num_random_calls != num_random_calls)
16560 {
16561 Error(ERR_INFO, "number of random calls out of sync");
16562 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16563 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16564 Error(ERR_EXIT, "this should not happen -- please debug");
16565 }
16566 }
16567
SaveEngineSnapshot()16568 void SaveEngineSnapshot()
16569 {
16570 /* do not save snapshots from editor */
16571 if (level_editor_test_game)
16572 return;
16573
16574 /* free previous snapshot buffers, if needed */
16575 FreeEngineSnapshotBuffers();
16576
16577 /* copy some special values to a structure better suited for the snapshot */
16578
16579 SaveEngineSnapshotValues_RND();
16580 SaveEngineSnapshotValues_EM();
16581 SaveEngineSnapshotValues_SP();
16582
16583 /* save values stored in special snapshot structure */
16584
16585 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16587 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16588
16589 /* save further RND engine values */
16590
16591 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16592 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16593 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16594
16595 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16596 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16597 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16598 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16599
16600 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16601 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16602 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16603 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16604 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16605
16606 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16607 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16608 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16609
16610 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16611
16612 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16613
16614 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16615 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16616
16617 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16618 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16619 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16620 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16621 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16622 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16623 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16624 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16625 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16626 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16627 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16628 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16629 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16630 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16631 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16632 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16633 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16634 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16635
16636 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16637 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16638
16639 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16640 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16641 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16642
16643 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16644 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16645
16646 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16647 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16648 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16649 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16650 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16651
16652 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16653 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16654
16655 /* save level identification information */
16656
16657 setString(&snapshot_level_identifier, leveldir_current->identifier);
16658 snapshot_level_nr = level_nr;
16659
16660 #if 0
16661 ListNode *node = engine_snapshot_list_rnd;
16662 int num_bytes = 0;
16663
16664 while (node != NULL)
16665 {
16666 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16667
16668 node = node->next;
16669 }
16670
16671 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16672 #endif
16673 }
16674
LoadEngineSnapshot()16675 void LoadEngineSnapshot()
16676 {
16677 /* restore generically stored snapshot buffers */
16678
16679 LoadEngineSnapshotBuffers();
16680
16681 /* restore special values from snapshot structure */
16682
16683 LoadEngineSnapshotValues_RND();
16684 LoadEngineSnapshotValues_EM();
16685 LoadEngineSnapshotValues_SP();
16686 }
16687
CheckEngineSnapshot()16688 boolean CheckEngineSnapshot()
16689 {
16690 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16691 snapshot_level_nr == level_nr);
16692 }
16693
16694
16695 /* ---------- new game button stuff ---------------------------------------- */
16696
16697 /* graphic position values for game buttons */
16698 #define GAME_BUTTON_XSIZE 30
16699 #define GAME_BUTTON_YSIZE 30
16700 #define GAME_BUTTON_XPOS 5
16701 #define GAME_BUTTON_YPOS 215
16702 #define SOUND_BUTTON_XPOS 5
16703 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16704
16705 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16706 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16707 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16708 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16709 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16710 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16711
16712 static struct
16713 {
16714 int *x, *y;
16715 int gd_x, gd_y;
16716 int gadget_id;
16717 char *infotext;
16718 } gamebutton_info[NUM_GAME_BUTTONS] =
16719 {
16720 #if 1
16721 {
16722 &game.button.stop.x, &game.button.stop.y,
16723 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16724 GAME_CTRL_ID_STOP,
16725 "stop game"
16726 },
16727 {
16728 &game.button.pause.x, &game.button.pause.y,
16729 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16730 GAME_CTRL_ID_PAUSE,
16731 "pause game"
16732 },
16733 {
16734 &game.button.play.x, &game.button.play.y,
16735 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16736 GAME_CTRL_ID_PLAY,
16737 "play game"
16738 },
16739 {
16740 &game.button.sound_music.x, &game.button.sound_music.y,
16741 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16742 SOUND_CTRL_ID_MUSIC,
16743 "background music on/off"
16744 },
16745 {
16746 &game.button.sound_loops.x, &game.button.sound_loops.y,
16747 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16748 SOUND_CTRL_ID_LOOPS,
16749 "sound loops on/off"
16750 },
16751 {
16752 &game.button.sound_simple.x,&game.button.sound_simple.y,
16753 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16754 SOUND_CTRL_ID_SIMPLE,
16755 "normal sounds on/off"
16756 }
16757 #else
16758 {
16759 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16760 GAME_CTRL_ID_STOP,
16761 "stop game"
16762 },
16763 {
16764 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16765 GAME_CTRL_ID_PAUSE,
16766 "pause game"
16767 },
16768 {
16769 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16770 GAME_CTRL_ID_PLAY,
16771 "play game"
16772 },
16773 {
16774 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16775 SOUND_CTRL_ID_MUSIC,
16776 "background music on/off"
16777 },
16778 {
16779 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16780 SOUND_CTRL_ID_LOOPS,
16781 "sound loops on/off"
16782 },
16783 {
16784 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16785 SOUND_CTRL_ID_SIMPLE,
16786 "normal sounds on/off"
16787 }
16788 #endif
16789 };
16790
CreateGameButtons()16791 void CreateGameButtons()
16792 {
16793 int i;
16794
16795 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16796 {
16797 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16798 struct GadgetInfo *gi;
16799 int button_type;
16800 boolean checked;
16801 unsigned long event_mask;
16802 int x, y;
16803 int gd_xoffset, gd_yoffset;
16804 int gd_x1, gd_x2, gd_y1, gd_y2;
16805 int id = i;
16806
16807 x = DX + *gamebutton_info[i].x;
16808 y = DY + *gamebutton_info[i].y;
16809 gd_xoffset = gamebutton_info[i].gd_x;
16810 gd_yoffset = gamebutton_info[i].gd_y;
16811 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16812 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16813
16814 if (id == GAME_CTRL_ID_STOP ||
16815 id == GAME_CTRL_ID_PAUSE ||
16816 id == GAME_CTRL_ID_PLAY)
16817 {
16818 button_type = GD_TYPE_NORMAL_BUTTON;
16819 checked = FALSE;
16820 event_mask = GD_EVENT_RELEASED;
16821 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16822 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16823 }
16824 else
16825 {
16826 button_type = GD_TYPE_CHECK_BUTTON;
16827 checked =
16828 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16829 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16830 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16831 event_mask = GD_EVENT_PRESSED;
16832 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16833 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16834 }
16835
16836 gi = CreateGadget(GDI_CUSTOM_ID, id,
16837 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16838 #if 1
16839 GDI_X, x,
16840 GDI_Y, y,
16841 #else
16842 GDI_X, DX + gd_xoffset,
16843 GDI_Y, DY + gd_yoffset,
16844 #endif
16845 GDI_WIDTH, GAME_BUTTON_XSIZE,
16846 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16847 GDI_TYPE, button_type,
16848 GDI_STATE, GD_BUTTON_UNPRESSED,
16849 GDI_CHECKED, checked,
16850 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16851 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16852 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16853 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16854 GDI_DIRECT_DRAW, FALSE,
16855 GDI_EVENT_MASK, event_mask,
16856 GDI_CALLBACK_ACTION, HandleGameButtons,
16857 GDI_END);
16858
16859 if (gi == NULL)
16860 Error(ERR_EXIT, "cannot create gadget");
16861
16862 game_gadget[id] = gi;
16863 }
16864 }
16865
FreeGameButtons()16866 void FreeGameButtons()
16867 {
16868 int i;
16869
16870 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16871 FreeGadget(game_gadget[i]);
16872 }
16873
MapGameButtons()16874 static void MapGameButtons()
16875 {
16876 int i;
16877
16878 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16879 MapGadget(game_gadget[i]);
16880 }
16881
UnmapGameButtons()16882 void UnmapGameButtons()
16883 {
16884 int i;
16885
16886 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16887 UnmapGadget(game_gadget[i]);
16888 }
16889
RedrawGameButtons()16890 void RedrawGameButtons()
16891 {
16892 int i;
16893
16894 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16895 RedrawGadget(game_gadget[i]);
16896 }
16897
HandleGameButtonsExt(int id)16898 static void HandleGameButtonsExt(int id)
16899 {
16900 if (game_status != GAME_MODE_PLAYING)
16901 return;
16902
16903 switch (id)
16904 {
16905 case GAME_CTRL_ID_STOP:
16906 if (tape.playing)
16907 TapeStop();
16908 else
16909 RequestQuitGame(TRUE);
16910 break;
16911
16912 case GAME_CTRL_ID_PAUSE:
16913 if (options.network)
16914 {
16915 #if defined(NETWORK_AVALIABLE)
16916 if (tape.pausing)
16917 SendToServer_ContinuePlaying();
16918 else
16919 SendToServer_PausePlaying();
16920 #endif
16921 }
16922 else
16923 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16924 break;
16925
16926 case GAME_CTRL_ID_PLAY:
16927 if (tape.pausing)
16928 {
16929 #if defined(NETWORK_AVALIABLE)
16930 if (options.network)
16931 SendToServer_ContinuePlaying();
16932 else
16933 #endif
16934 {
16935 tape.pausing = FALSE;
16936 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16937 }
16938 }
16939 break;
16940
16941 case SOUND_CTRL_ID_MUSIC:
16942 if (setup.sound_music)
16943 {
16944 setup.sound_music = FALSE;
16945
16946 FadeMusic();
16947 }
16948 else if (audio.music_available)
16949 {
16950 setup.sound = setup.sound_music = TRUE;
16951
16952 SetAudioMode(setup.sound);
16953
16954 PlayLevelMusic();
16955 }
16956 break;
16957
16958 case SOUND_CTRL_ID_LOOPS:
16959 if (setup.sound_loops)
16960 setup.sound_loops = FALSE;
16961 else if (audio.loops_available)
16962 {
16963 setup.sound = setup.sound_loops = TRUE;
16964
16965 SetAudioMode(setup.sound);
16966 }
16967 break;
16968
16969 case SOUND_CTRL_ID_SIMPLE:
16970 if (setup.sound_simple)
16971 setup.sound_simple = FALSE;
16972 else if (audio.sound_available)
16973 {
16974 setup.sound = setup.sound_simple = TRUE;
16975
16976 SetAudioMode(setup.sound);
16977 }
16978 break;
16979
16980 default:
16981 break;
16982 }
16983 }
16984
HandleGameButtons(struct GadgetInfo * gi)16985 static void HandleGameButtons(struct GadgetInfo *gi)
16986 {
16987 HandleGameButtonsExt(gi->custom_id);
16988 }
16989
HandleSoundButtonKeys(Key key)16990 void HandleSoundButtonKeys(Key key)
16991 {
16992 #if 1
16993 if (key == setup.shortcut.sound_simple)
16994 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16995 else if (key == setup.shortcut.sound_loops)
16996 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16997 else if (key == setup.shortcut.sound_music)
16998 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16999 #else
17000 if (key == setup.shortcut.sound_simple)
17001 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17002 else if (key == setup.shortcut.sound_loops)
17003 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17004 else if (key == setup.shortcut.sound_music)
17005 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17006 #endif
17007 }
17008