3 #include "endgame/island.h"
4 #include "endgame/credits.h"
5 #include "intro/intro.h"
6 #include "intro/title.h"
7 #include "pause/pause.h"
8 #include "pause/options.h"
10 #include "map_system.h"
15 static struct TickFunctions
18 bool (*OnEnter
)(int param
);
23 NULL
, NULL
, NULL
, // GM_NONE
24 game_tick_normal
, NULL
, NULL
, // GM_NORMAL
25 inventory_tick
, inventory_init
, NULL
, // GM_INVENTORY
26 ms_tick
, ms_init
, ms_close
, // GM_MAP_SYSTEM
27 island_tick
, island_init
, NULL
, // GM_ISLAND
28 credit_tick
, credit_init
, credit_close
, // GM_CREDITS
29 intro_tick
, intro_init
, NULL
, // GM_INTRO
30 title_tick
, title_init
, NULL
, // GM_TITLE
31 pause_tick
, pause_init
, NULL
, // GP_PAUSED
32 options_tick
, options_init
, options_close
// GP_OPTIONS
33 //old_options_tick, old_options_init, old_options_close // GP_OPTIONS
36 Object
*onscreen_objects
[MAX_OBJECTS
];
42 ObjProp objprop
[OBJ_LAST
];
44 // init Game object: only called once during startup
49 memset(&game
, 0, sizeof(game
));
51 // set default properties
52 memset(objprop
, 0, sizeof(objprop
));
53 for(i
=0;i
<OBJ_LAST
;i
++)
55 objprop
[i
].shaketime
= 16;
56 #ifdef DEBUG // big red "NO" sprite points out unimplemented objects
57 objprop
[i
].sprite
= SPR_UNIMPLEMENTED_OBJECT
;
59 objprop
[i
].sprite
= SPR_NULL
;
63 AssignSprites(); // auto-generated function to assign sprites to objects
64 AssignExtraSprites(); // assign rest of sprites (to be replaced at some point)
66 if (ai_init()) return 1; // setup function pointers to AI routines
68 if (initslopetable()) return 1;
69 if (initmapfirsttime()) return 1;
71 // create the player object--note that the player is NOT destroyed on map change
72 if (game
.createplayer()) return 1;
78 // reset things to prepare for entry to the next stage
79 bool Game::initlevel()
81 Carets::DestroyAll(); // delete smoke clouds, ZZzz's etc...
82 ScreenEffects::Stop(); // prevents white flash after island scene when ballos defeated
85 game
.bossbar
.object
= NULL
;
88 if (statusbar_init()) return 1; // reset his displayed health value
92 game
.stageboss
.SetType(stages
[game
.curmap
].bossNo
);
93 game
.stageboss
.OnMapEntry();
95 map_scroll_jump(player
->CenterX(), player
->CenterY());
97 if (game
.switchstage
.eventonentry
)
99 // this prevents a glitch otherwise caused by entry script to Last Cave.
100 // i.e. the script immediately <PRI's then fades in while the game is still
101 // frozen, thus the player code never has a chance to set the initial frame.
105 stat("-- Starting on-entry script %d", game
.switchstage
.eventonentry
);
106 StartScript(game
.switchstage
.eventonentry
);
107 game
.switchstage
.eventonentry
= 0;
113 bool Game::createplayer()
117 staterr("game.createplayer: player already exists!");
121 player
= (Player
*)CreateObject(0, 0, OBJ_PLAYER
);
128 void Game::close(void)
130 // call any onexit/cleanup function for the current mode
133 Objects::DestroyAll(true); // destroy all objects and player
134 FloatText::DeleteAll();
138 void c------------------------------() {}
141 bool Game::setmode(int newmode
, int param
, bool force
)
146 if (game
.mode
== newmode
&& !force
)
149 stat("Setting tick function to type %d param %d", newmode
, param
);
151 if (tickfunctions
[game
.mode
].OnExit
)
152 tickfunctions
[game
.mode
].OnExit();
156 if (tickfunctions
[game
.mode
].OnEnter
)
158 if (tickfunctions
[game
.mode
].OnEnter(param
))
160 staterr("game.setmode: initilization failed for mode %d", newmode
);
169 bool Game::pause(int pausemode
, int param
)
171 if (game
.paused
== pausemode
)
174 stat("Setting pause: type %d param %d", pausemode
, param
);
176 if (tickfunctions
[game
.paused
].OnExit
)
177 tickfunctions
[game
.paused
].OnExit();
179 game
.paused
= pausemode
;
181 if (tickfunctions
[game
.paused
].OnEnter
)
183 if (tickfunctions
[game
.paused
].OnEnter(param
))
185 staterr("game.pause: initilization failed for mode %d", pausemode
);
192 memset(inputs
, 0, sizeof(inputs
));
197 void Game::tick(void)
203 tickfunctions
[game
.paused
].OnTick();
207 // record/playback replays
213 // call the tick function for the current game mode
214 tickfunctions
[game
.mode
].OnTick();
222 void Game::switchmap(int mapno
, int scriptno
, int px
, int py
)
224 game
.switchstage
.mapno
= mapno
;
225 game
.switchstage
.playerx
= px
;
226 game
.switchstage
.playery
= py
;
227 game
.switchstage
.eventonentry
= scriptno
;
233 memset(inputs
, 0, sizeof(inputs
));
237 Replay::end_record();
238 Replay::end_playback();
241 game
.setmode(GM_INTRO
, 0, true);
242 console
.SetVisible(false);
246 void c------------------------------() {}
249 // standard in-game tick (as opposed to title-screen, inventory etc)
250 void game_tick_normal(void)
254 player
->riding
= NULL
;
255 player
->bopped_object
= NULL
;
256 Objects::UpdateBlockStates();
260 // run AI for player and stageboss first
262 game
.stageboss
.Run();
264 // now objects AI and move all objects to their new positions
266 Objects::PhysicsSim();
268 // run the "aftermove" AI routines
270 game
.stageboss
.RunAftermove();
279 // important to put this before and not after DrawScene(), or non-existant objects
280 // can wind up in the onscreen_objects[] array, and blow up the program on the next tick.
281 Objects::CullDeleted();
290 if (player
->equipmask
& EQUIP_NIKUMARU
)
291 niku_draw(game
.counter
);
295 ScreenEffects::Draw();
296 map_draw_map_name(); // stage name overlay as on entry
301 void quake(int quaketime
, int snd
)
303 if (game
.quaketime
< quaketime
)
304 game
.quaketime
= quaketime
;
307 sound((snd
!= -1) ? snd
: SND_QUAKE
);
310 // during Ballos fight, since there's already a perpetual quake,
311 // we need to be able to make an even BIGGER quake effect.
312 void megaquake(int quaketime
, int snd
)
314 if (game
.megaquaketime
< quaketime
)
316 game
.megaquaketime
= quaketime
;
317 if (game
.quaketime
< game
.megaquaketime
)
318 game
.quaketime
= game
.megaquaketime
;
322 sound((snd
!= -1) ? snd
: SND_QUAKE
);
329 extern int flipacceltime
;
331 // sporidically-used animated tile feature,
332 // e.g. water currents in Waterway
333 if (map
.nmotiontiles
)
334 AnimateMotionTiles();
336 // draw background map tiles
343 // draw all objects following their z-order
344 nOnscreenObjects
= 0;
346 for(Object
*o
= lowestobject
;
350 if (o
== player
) continue; // player drawn specially in DrawPlayer
352 // keep it's floattext linked with it's position
353 o
->DamageText
->UpdatePos(o
);
355 // shake enemies that were just hit. when they stop shaking,
356 // start rising up how many damage they took.
359 o
->display_xoff
= (o
->shaketime
& 2) ? 1 : -1;
360 if (!--o
->shaketime
) o
->display_xoff
= 0;
362 else if (o
->DamageWaiting
> 0)
364 o
->DamageText
->AddQty(o
->DamageWaiting
);
365 o
->DamageWaiting
= 0;
368 // get object's onscreen position
369 scr_x
= (o
->x
>> CSF
) - (map
.displayed_xscroll
>> CSF
);
370 scr_y
= (o
->y
>> CSF
) - (map
.displayed_yscroll
>> CSF
);
371 scr_x
-= sprites
[o
->sprite
].frame
[o
->frame
].dir
[o
->dir
].drawpoint
.x
;
372 scr_y
-= sprites
[o
->sprite
].frame
[o
->frame
].dir
[o
->dir
].drawpoint
.y
;
374 // don't draw objects that are completely offscreen
375 // (+26 so floattext won't suddenly disappear on object near bottom of screen)
376 if (scr_x
<= SCREEN_WIDTH
&& scr_y
<= SCREEN_HEIGHT
+26 && \
377 scr_x
>= -sprites
[o
->sprite
].w
&& scr_y
>= -sprites
[o
->sprite
].h
)
379 if (nOnscreenObjects
< MAX_OBJECTS
-1)
381 onscreen_objects
[nOnscreenObjects
++] = o
;
386 staterr("%s:%d: Max Objects Overflow", __FILE__
, __LINE__
);
390 if (!o
->invisible
&& o
->sprite
!= SPR_NULL
)
392 scr_x
+= o
->display_xoff
;
396 draw_sprite_clipped(scr_x
, scr_y
, o
->sprite
, o
->frame
, o
->dir
, o
->clipx1
, o
->clipx2
, o
->clipy1
, o
->clipy2
);
400 draw_sprite(scr_x
, scr_y
, o
->sprite
, o
->frame
, o
->dir
);
413 // draw foreground map tiles
415 map_draw(TA_FOREGROUND
);
417 // draw carets (always-on-top effects such as boomflash)
420 // draw rising/falling water in maps like Almond
421 map_drawwaterlevel();
423 // draw all floattext (rising damage and XP amounts)
424 FloatText::DrawAll();
426 if (game
.debug
.DrawBoundingBoxes
) DrawBoundingBoxes();
427 //if (game.debug.debugmode) DrawAttrPoints();
431 void c------------------------------() {}
434 bool game_load(int num
)
438 stat("game_load: loading savefile %d", num
);
440 if (profile_load(GetProfileName(num
), &p
))
443 return game_load(&p
);
446 bool game_load(Profile
*p
)
451 player
->maxHealth
= p
->maxhp
;
453 player
->whimstar
.nstars
= p
->num_whimstars
;
454 player
->equipmask
= p
->equipmask
;
457 for(i
=0;i
<WPN_COUNT
;i
++)
459 player
->weapons
[i
].hasWeapon
= p
->weapons
[i
].hasWeapon
;
460 player
->weapons
[i
].level
= p
->weapons
[i
].level
;
461 player
->weapons
[i
].xp
= p
->weapons
[i
].xp
;
462 player
->weapons
[i
].ammo
= p
->weapons
[i
].ammo
;
463 player
->weapons
[i
].maxammo
= p
->weapons
[i
].maxammo
;
466 player
->curWeapon
= p
->curWeapon
;
469 memcpy(player
->inventory
, p
->inventory
, sizeof(player
->inventory
));
470 player
->ninventory
= p
->ninventory
;
473 memcpy(game
.flags
, p
->flags
, sizeof(game
.flags
));
475 // load teleporter slots
476 textbox
.StageSelect
.ClearSlots();
477 for(i
=0;i
<p
->num_teleslots
;i
++)
479 int slotno
= p
->teleslots
[i
].slotno
;
480 int scriptno
= p
->teleslots
[i
].scriptno
;
482 textbox
.StageSelect
.SetSlot(slotno
, scriptno
);
483 stat(" - Read Teleporter Slot %d: slotno=%d scriptno=%d", i
, slotno
, scriptno
);
486 // have to load the stage last AFTER the flags are loaded because
487 // of the options to appear and disappear objects based on flags.
488 if (load_stage(p
->stage
)) return 1;
493 player
->dir
= p
->pdir
;
494 player
->hide
= false;
495 game
.showmapnametime
= 0;
501 bool game_save(int num
)
505 stat("game_save: writing savefile %d", num
);
510 if (profile_save(GetProfileName(num
), &p
))
516 bool game_save(Profile
*p
)
520 memset(p
, 0, sizeof(Profile
));
522 p
->stage
= game
.curmap
;
523 p
->songno
= music_cursong();
527 p
->pdir
= player
->dir
;
530 p
->maxhp
= player
->maxHealth
;
532 p
->num_whimstars
= player
->whimstar
.nstars
;
533 p
->equipmask
= player
->equipmask
;
536 p
->curWeapon
= player
->curWeapon
;
538 for(i
=0;i
<WPN_COUNT
;i
++)
540 p
->weapons
[i
].hasWeapon
= player
->weapons
[i
].hasWeapon
;
541 p
->weapons
[i
].level
= player
->weapons
[i
].level
;
542 p
->weapons
[i
].xp
= player
->weapons
[i
].xp
;
543 p
->weapons
[i
].ammo
= player
->weapons
[i
].ammo
;
544 p
->weapons
[i
].maxammo
= player
->weapons
[i
].maxammo
;
548 p
->ninventory
= player
->ninventory
;
549 memcpy(p
->inventory
, player
->inventory
, sizeof(p
->inventory
));
552 memcpy(p
->flags
, game
.flags
, sizeof(p
->flags
));
554 // save teleporter slots
555 for(i
=0;i
<NUM_TELEPORTER_SLOTS
;i
++)
557 int slotno
, scriptno
;
558 if (!textbox
.StageSelect
.GetSlotByIndex(i
, &slotno
, &scriptno
))
560 p
->teleslots
[p
->num_teleslots
].slotno
= slotno
;
561 p
->teleslots
[p
->num_teleslots
].scriptno
= scriptno
;
570 void c------------------------------() {}
573 // assign sprites for the objects that didn't get covered by the
574 // auto-generated spritesetup->cpp, and set some properties on the objects.
575 // This is mostly for objects where the sprite is not named the same as
576 // the object it is assigned to.
577 void AssignExtraSprites(void)
579 objprop
[OBJ_PLAYER
].sprite
= SPR_MYCHAR
;
580 objprop
[OBJ_NPC_PLAYER
].sprite
= SPR_MYCHAR
;
581 objprop
[OBJ_PTELIN
].sprite
= SPR_MYCHAR
;
582 objprop
[OBJ_PTELOUT
].sprite
= SPR_MYCHAR
;
584 objprop
[OBJ_NULL
].sprite
= SPR_NULL
;
585 objprop
[OBJ_HVTRIGGER
].sprite
= SPR_NULL
;
586 objprop
[OBJ_BUBBLE_SPAWNER
].sprite
= SPR_NULL
;
587 objprop
[OBJ_DROPLET_SPAWNER
].sprite
= SPR_NULL
;
588 objprop
[OBJ_HEY_SPAWNER
].sprite
= SPR_NULL
;
589 objprop
[OBJ_WATERLEVEL
].sprite
= SPR_NULL
;
590 objprop
[OBJ_LAVA_DRIP_SPAWNER
].sprite
= SPR_NULL
;
591 objprop
[OBJ_RED_BAT_SPAWNER
].sprite
= SPR_NULL
;
592 objprop
[OBJ_SCROLL_CONTROLLER
].sprite
= SPR_NULL
;
593 objprop
[OBJ_DOCTOR_GHOST
].sprite
= SPR_NULL
;
594 objprop
[OBJ_FALLING_BLOCK
].sprite
= SPR_NULL
; // set at runtime based on current map
595 objprop
[OBJ_FALLING_BLOCK_SPAWNER
].sprite
= SPR_NULL
;
596 objprop
[OBJ_QUAKE
].sprite
= SPR_NULL
;
597 objprop
[OBJ_BUTE_SPAWNER
].sprite
= SPR_NULL
;
598 objprop
[OBJ_SMOKE_DROPPER
].sprite
= SPR_NULL
;
601 objprop
[OBJ_BUTE_ARROW
].sprite
= SPR_BUTE_ARROW_LEFT
; // so spawn point is applied
603 objprop
[OBJ_POLISHBABY
].defaultnxflags
|= NXFLAG_SLOW_WHEN_HURT
;
605 objprop
[OBJ_MIMIGAC1
].sprite
= SPR_MIMIGAC
;
606 objprop
[OBJ_MIMIGAC2
].sprite
= SPR_MIMIGAC
;
607 objprop
[OBJ_MIMIGAC_ENEMY
].sprite
= SPR_MIMIGAC
;
608 objprop
[OBJ_MIMIGAC_ENEMY
].shaketime
= 0;
610 objprop
[OBJ_MISERY_FLOAT
].sprite
= SPR_MISERY
;
611 objprop
[OBJ_MISERY_FLOAT
].damage
= 1;
612 objprop
[OBJ_MISERY_STAND
].sprite
= SPR_MISERY
;
614 objprop
[OBJ_PUPPY_WAG
].sprite
= SPR_PUPPY
;
615 objprop
[OBJ_PUPPY_BARK
].sprite
= SPR_PUPPY
;
616 objprop
[OBJ_PUPPY_CARRY
].sprite
= SPR_PUPPY
;
617 objprop
[OBJ_PUPPY_SLEEP
].sprite
= SPR_PUPPY_ASLEEP
;
618 objprop
[OBJ_PUPPY_RUN
].sprite
= SPR_PUPPY
;
619 objprop
[OBJ_PUPPY_ITEMS
].sprite
= SPR_PUPPY
;
621 objprop
[OBJ_BALROG_DROP_IN
].sprite
= SPR_BALROG
;
622 objprop
[OBJ_BALROG_BUST_IN
].sprite
= SPR_BALROG
;
624 objprop
[OBJ_CROWWITHSKULL
].sprite
= SPR_CROW
;
625 objprop
[OBJ_ARMADILLO
].defaultnxflags
|= (NXFLAG_FOLLOW_SLOPE
| NXFLAG_SLOW_WHEN_HURT
);
626 objprop
[OBJ_SKULLHEAD_CARRIED
].sprite
= SPR_SKULLHEAD
;
628 objprop
[OBJ_TOROKO
].defaultnxflags
|= NXFLAG_FOLLOW_SLOPE
;
629 objprop
[OBJ_TOROKO_TELEPORT_IN
].sprite
= SPR_TOROKO
;
631 objprop
[OBJ_KING
].defaultnxflags
|= NXFLAG_FOLLOW_SLOPE
;
633 objprop
[OBJ_FAN_DROPLET
].sprite
= SPR_WATER_DROPLET
;
635 objprop
[OBJ_MGUN_TRAIL
].defaultflags
|= FLAG_IGNORE_SOLID
;
637 objprop
[OBJ_BLOCK_MOVEH
].sprite
= SPR_MOVING_BLOCK
;
638 objprop
[OBJ_BLOCK_MOVEV
].sprite
= SPR_MOVING_BLOCK
;
640 objprop
[OBJ_IRONH
].shaketime
= 8;
642 objprop
[OBJ_OMEGA_BODY
].shaketime
= 0; // omega handles his own shaketime
643 objprop
[OBJ_OMEGA_BODY
].hurt_sound
= SND_ENEMY_HURT_BIG
;
645 objprop
[OBJ_OMEGA_LEG
].sprite
= SPR_OMG_LEG_INAIR
;
646 objprop
[OBJ_OMEGA_STRUT
].sprite
= SPR_OMG_STRUT
;
648 objprop
[OBJ_OMEGA_SHOT
].death_smoke_amt
= 4;
649 objprop
[OBJ_OMEGA_SHOT
].death_sound
= SND_EXPL_SMALL
;
650 objprop
[OBJ_OMEGA_SHOT
].initial_hp
= 1;
651 objprop
[OBJ_OMEGA_SHOT
].xponkill
= 1;
653 objprop
[OBJ_BAT_HANG
].sprite
= SPR_BAT
;
654 objprop
[OBJ_BAT_CIRCLE
].sprite
= SPR_BAT
;
656 objprop
[OBJ_FIREBALL1
].defaultnxflags
|= NXFLAG_FOLLOW_SLOPE
;
657 objprop
[OBJ_FIREBALL23
].defaultnxflags
|= NXFLAG_FOLLOW_SLOPE
;
659 objprop
[OBJ_CURLY_AI
].sprite
= SPR_CURLY
;
660 objprop
[OBJ_CURLY_AI
].defaultnxflags
|= NXFLAG_FOLLOW_SLOPE
;
662 objprop
[OBJ_CURLY
].defaultnxflags
|= NXFLAG_FOLLOW_SLOPE
;
664 objprop
[OBJ_MINICORE
].hurt_sound
= SND_ENEMY_HURT_COOL
;
665 objprop
[OBJ_CORE_CONTROLLER
].hurt_sound
= SND_CORE_HURT
;
667 objprop
[OBJ_CURLY_CARRIED
].sprite
= SPR_CURLY
;
669 objprop
[OBJ_BALROG_BOSS_RUNNING
].sprite
= SPR_BALROG
;
670 objprop
[OBJ_BALROG_BOSS_FLYING
].sprite
= SPR_BALROG
;
671 objprop
[OBJ_BALROG_BOSS_MISSILES
].sprite
= SPR_BALROG
;
673 objprop
[OBJ_XP
].sprite
= SPR_XP_SMALL
;
675 objprop
[OBJ_NPC_IGOR
].sprite
= SPR_IGOR
;
676 objprop
[OBJ_BOSS_IGOR
].sprite
= SPR_IGOR
;
677 objprop
[OBJ_BOSS_IGOR_DEFEATED
].sprite
= SPR_IGOR
;
678 objprop
[OBJ_IGOR_BALCONY
].sprite
= SPR_IGOR
;
680 objprop
[OBJ_X_TARGET
].hurt_sound
= SND_ENEMY_HURT_COOL
;
681 objprop
[OBJ_X_INTERNALS
].shaketime
= 9;
682 objprop
[OBJ_X_MAINOBJECT
].xponkill
= 1;
684 objprop
[OBJ_POOH_BLACK_BUBBLE
].xponkill
= 0;
685 objprop
[OBJ_POOH_BLACK_DYING
].sprite
= SPR_POOH_BLACK
;
687 objprop
[OBJ_BOOSTER_FALLING
].sprite
= SPR_PROFESSOR_BOOSTER
;
689 objprop
[OBJ_MIMIGA_FARMER_STANDING
].sprite
= SPR_MIMIGA_FARMER
;
690 objprop
[OBJ_MIMIGA_FARMER_WALKING
].sprite
= SPR_MIMIGA_FARMER
;
691 objprop
[OBJ_DROLL_GUARD
].sprite
= SPR_DROLL
;
693 objprop
[OBJ_MA_PIGNON_CLONE
].sprite
= SPR_MA_PIGNON
;
695 objprop
[OBJ_DOCTOR_SHOT_TRAIL
].sprite
= SPR_DOCTOR_SHOT
;
697 // they're still able to detect when they touch floor; etc,
698 // but we don't want say a falling one to get blocked by the ceiling.
699 objprop
[OBJ_RED_ENERGY
].defaultflags
|= FLAG_IGNORE_SOLID
;
701 objprop
[OBJ_SUE_TELEPORT_IN
].sprite
= SPR_SUE
;
703 objprop
[OBJ_MISERY_BAT
].sprite
= SPR_ORANGE_BAT_FINAL
;
704 objprop
[OBJ_UD_MINICORE_IDLE
].sprite
= SPR_UD_MINICORE
;
706 objprop
[OBJ_WHIMSICAL_STAR
].sprite
= SPR_WHIMSICAL_STAR
; // for bbox only, object is invisible
708 // these are set by AI; this is just to silence unimplemented object warnings
710 objprop
[OBJ_CRITTER_FLYING
].sprite
= SPR_CRITTER_FLYING_CYAN
;
711 for(int i
=OBJ_SHOTS_START
;i
<=OBJ_SHOTS_END
;i
++)
712 if (objprop
[i
].sprite
==SPR_UNIMPLEMENTED_OBJECT
) objprop
[i
].sprite
= SPR_NULL
;