1 
2 #include "nx.h"
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"
9 #include "inventory.h"
10 #include "map_system.h"
11 #include "game.h"
12 #include "profile.h"
13 #include "game.fdh"
14 
15 static struct TickFunctions
16 {
17 	void (*OnTick)(void);
18 	bool (*OnEnter)(int param);
19 	void (*OnExit)(void);
20 }
21 tickfunctions[] =
22 {
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
34 };
35 
36 Object *onscreen_objects[MAX_OBJECTS];
37 int nOnscreenObjects;
38 
39 Game game;
40 TextBox textbox;
41 ObjProp objprop[OBJ_LAST];
42 
43 // init Game object: only called once during startup
init()44 bool Game::init()
45 {
46    int i;
47 
48 	memset(&game, 0, sizeof(game));
49 
50 	// set default properties
51 	memset(objprop, 0, sizeof(objprop));
52 	for(i=0;i<OBJ_LAST;i++)
53 	{
54 		objprop[i].shaketime = 16;
55       objprop[i].sprite = SPR_NULL;
56 	}
57 
58 	AssignSprites();		// auto-generated function to assign sprites to objects
59 	AssignExtraSprites();	// assign rest of sprites (to be replaced at some point)
60 
61 	if (ai_init()) return 1;			// setup function pointers to AI routines
62 
63 	if (initslopetable()) return 1;
64 	if (initmapfirsttime()) return 1;
65 
66 	// create the player object--note that the player is NOT destroyed on map change
67 	if (game.createplayer()) return 1;
68 
69 	return 0;
70 }
71 
72 
73 // reset things to prepare for entry to the next stage
initlevel()74 bool Game::initlevel()
75 {
76 	Carets::DestroyAll();	// delete smoke clouds, ZZzz's etc...
77 	ScreenEffects::Stop();	// prevents white flash after island scene when ballos defeated
78 
79 	game.frozen = false;
80 	game.bossbar.object = NULL;
81 	nOnscreenObjects = 0;
82 
83 	if (statusbar_init()) return 1;					// reset his displayed health value
84 	InitPlayer();
85 	initmap();
86 
87 	game.stageboss.SetType(stages[game.curmap].bossNo);
88 	game.stageboss.OnMapEntry();
89 
90 	map_scroll_jump(player->CenterX(), player->CenterY());
91 
92 	if (game.switchstage.eventonentry)
93 	{
94 		// this prevents a glitch otherwise caused by entry script to Last Cave.
95 		// i.e. the script immediately <PRI's then fades in while the game is still
96 		// frozen, thus the player code never has a chance to set the initial frame.
97 		PHandleAttributes();
98 		PSelectFrame();
99 
100 		NX_LOG("-- Starting on-entry script %d\n", game.switchstage.eventonentry);
101 		StartScript(game.switchstage.eventonentry);
102 		game.switchstage.eventonentry = 0;
103 	}
104 
105 	return 0;
106 }
107 
createplayer()108 bool Game::createplayer()
109 {
110 	if (player)
111 	{
112 		NX_ERR("game.createplayer: player already exists!\n");
113 		return 1;
114 	}
115 
116 	player = (Player *)CreateObject(0, 0, OBJ_PLAYER);
117 	PInitFirstTime();
118 
119 	return 0;
120 }
121 
122 
close(void)123 void Game::close(void)
124 {
125 	// call any onexit/cleanup function for the current mode
126 	setmode(GM_NONE);
127 
128 	Objects::DestroyAll(true);	// destroy all objects and player
129 	FloatText::DeleteAll();
130 }
131 
132 /*
133 void c------------------------------() {}
134 */
135 
setmode(int newmode,int param,bool force)136 bool Game::setmode(int newmode, int param, bool force)
137 {
138 	if (newmode == 0)
139 		newmode = GM_NORMAL;
140 
141 	if (game.mode == newmode && !force)
142 		return 0;
143 
144 	NX_LOG("Setting tick function to type %d param %d\n", newmode, param);
145 
146 	if (tickfunctions[game.mode].OnExit)
147 		tickfunctions[game.mode].OnExit();
148 
149 	game.mode = newmode;
150 
151 	if (tickfunctions[game.mode].OnEnter)
152 	{
153 		if (tickfunctions[game.mode].OnEnter(param))
154 		{
155 			NX_ERR("game.setmode: initilization failed for mode %d\n", newmode);
156 			game.mode = GM_NONE;
157 			return 1;
158 		}
159 	}
160 
161 	return 0;
162 }
163 
pause(int pausemode,int param)164 bool Game::pause(int pausemode, int param)
165 {
166 	if (game.paused == pausemode)
167 		return 0;
168 
169 	NX_LOG("Setting pause: type %d param %d\n", pausemode, param);
170 
171 	if (tickfunctions[game.paused].OnExit)
172 		tickfunctions[game.paused].OnExit();
173 
174 	game.paused = pausemode;
175 
176 	if (tickfunctions[game.paused].OnEnter)
177 	{
178 		if (tickfunctions[game.paused].OnEnter(param))
179 		{
180 			NX_ERR("game.pause: initilization failed for mode %d\n", pausemode);
181 			game.paused = 0;
182 			return 1;
183 		}
184 	}
185 
186 	if (!game.paused)
187 		memset(inputs, 0, sizeof(inputs));
188 
189 	return 0;
190 }
191 
tick(void)192 void Game::tick(void)
193 {
194 	if (game.paused)
195 	{
196 		tickfunctions[game.paused].OnTick();
197 	}
198 	else
199 	{
200 		// run scripts
201 		RunScripts();
202 
203 		// call the tick function for the current game mode
204 		tickfunctions[game.mode].OnTick();
205 	}
206 }
207 
208 
switchmap(int mapno,int scriptno,int px,int py)209 void Game::switchmap(int mapno, int scriptno, int px, int py)
210 {
211 	game.switchstage.mapno = mapno;
212 	game.switchstage.playerx = px;
213 	game.switchstage.playery = py;
214 	game.switchstage.eventonentry = scriptno;
215 }
216 
217 
reset()218 void Game::reset()
219 {
220 	memset(inputs, 0, sizeof(inputs));
221 	StopLoopSounds();
222 	StopScripts();
223 
224 	game.pause(false);
225 	game.setmode(GM_INTRO, 0, true);
226 }
227 
228 /*
229 void c------------------------------() {}
230 */
231 
232 // standard in-game tick (as opposed to title-screen, inventory etc)
game_tick_normal(void)233 void game_tick_normal(void)
234 {
235 Object *o;
236 
237 	player->riding = NULL;
238 	player->bopped_object = NULL;
239 	Objects::UpdateBlockStates();
240 
241 	if (!game.frozen)
242 	{
243 		// run AI for player and stageboss first
244 		HandlePlayer();
245 		game.stageboss.Run();
246 
247 		// now objects AI and move all objects to their new positions
248 		Objects::RunAI();
249 		Objects::PhysicsSim();
250 
251 		// run the "aftermove" AI routines
252 		HandlePlayer_am();
253 		game.stageboss.RunAftermove();
254 
255 		FOREACH_OBJECT(o)
256 		{
257 			if (!o->deleted)
258 				o->OnAftermove();
259 		}
260 	}
261 
262 	// important to put this before and not after DrawScene(), or non-existant objects
263 	// can wind up in the onscreen_objects[] array, and blow up the program on the next tick.
264 	Objects::CullDeleted();
265 
266 	map_scroll_do();
267 
268 	DrawScene();
269 	DrawStatusBar();
270 	fade.Draw();
271 
272 	niku_run();
273 	if (player->equipmask & EQUIP_NIKUMARU)
274 		niku_draw(game.counter);
275 
276 	textbox.Draw();
277 
278 	ScreenEffects::Draw();
279 	map_draw_map_name();	// stage name overlay as on entry
280 }
281 
282 
283 // shake screen.
quake(int quaketime,int snd)284 void quake(int quaketime, int snd)
285 {
286 	if (game.quaketime < quaketime)
287 		game.quaketime = quaketime;
288 
289 	if (snd)
290 		sound((snd != -1) ? snd : SND_QUAKE);
291 }
292 
293 // during Ballos fight, since there's already a perpetual quake,
294 // we need to be able to make an even BIGGER quake effect.
megaquake(int quaketime,int snd)295 void megaquake(int quaketime, int snd)
296 {
297 	if (game.megaquaketime < quaketime)
298 	{
299 		game.megaquaketime = quaketime;
300 		if (game.quaketime < game.megaquaketime)
301 			game.quaketime = game.megaquaketime;
302 	}
303 
304 	if (snd)
305 		sound((snd != -1) ? snd : SND_QUAKE);
306 }
307 
308 
DrawScene(void)309 void DrawScene(void)
310 {
311 int scr_x, scr_y;
312 
313    ClearScreen(BLACK);
314 	// sporidically-used animated tile feature,
315 	// e.g. water currents in Waterway
316 	if (map.nmotiontiles)
317 		AnimateMotionTiles();
318 
319 	// draw background map tiles
320 		map_draw_backdrop();
321 		map_draw(false);
322 
323 	// draw all objects following their z-order
324 	nOnscreenObjects = 0;
325 
326 	for(Object *o = lowestobject;
327 		o != NULL;
328 		o = o->higher)
329 	{
330 		if (o == player) continue;	// player drawn specially in DrawPlayer
331 
332 		// keep it's floattext linked with it's position
333 		o->DamageText->UpdatePos(o);
334 
335 		// shake enemies that were just hit. when they stop shaking,
336 		// start rising up how many damage they took.
337 		if (o->shaketime)
338 		{
339 			o->display_xoff = (o->shaketime & 2) ? 1 : -1;
340 			if (!--o->shaketime) o->display_xoff = 0;
341 		}
342 		else if (o->DamageWaiting > 0)
343 		{
344 			o->DamageText->AddQty(o->DamageWaiting);
345 			o->DamageWaiting = 0;
346 		}
347 
348 		// get object's onscreen position
349 		scr_x = (o->x >> CSF) - (map.displayed_xscroll >> CSF);
350 		scr_y = (o->y >> CSF) - (map.displayed_yscroll >> CSF);
351 		scr_x -= sprites[o->sprite].frame[o->frame].dir[o->dir].drawpoint.x;
352 		scr_y -= sprites[o->sprite].frame[o->frame].dir[o->dir].drawpoint.y;
353 
354 		// don't draw objects that are completely offscreen
355 		// (+26 so floattext won't suddenly disappear on object near bottom of screen)
356 		if (scr_x <= SCREEN_WIDTH && scr_y <= SCREEN_HEIGHT+26 && \
357 			scr_x >= -sprites[o->sprite].w && scr_y >= -sprites[o->sprite].h)
358 		{
359 			if (nOnscreenObjects < MAX_OBJECTS-1)
360 			{
361 				onscreen_objects[nOnscreenObjects++] = o;
362 				o->onscreen = true;
363 			}
364 			else
365 			{
366 				NX_ERR("%s:%d: Max Objects Overflow\n", __FILE__, __LINE__);
367 				return;
368 			}
369 
370 			if (!o->invisible && o->sprite != SPR_NULL)
371 			{
372 				scr_x += o->display_xoff;
373 
374 				if (o->clip_enable)
375 				{
376 					draw_sprite_clipped(scr_x, scr_y, o->sprite, o->frame, o->dir, o->clipx1, o->clipx2, o->clipy1, o->clipy2);
377 				}
378 				else
379 				{
380 					draw_sprite(scr_x, scr_y, o->sprite, o->frame, o->dir);
381 				}
382 			}
383 		}
384 		else
385 		{
386 			o->onscreen = false;
387 		}
388 	}
389 
390 	// draw the player
391 	DrawPlayer();
392 
393 	// draw foreground map tiles
394    map_draw(TA_FOREGROUND);
395 
396 	// draw carets (always-on-top effects such as boomflash)
397 	Carets::DrawAll();
398 
399 	// draw rising/falling water in maps like Almond
400 	map_drawwaterlevel();
401 
402 	// draw all floattext (rising damage and XP amounts)
403 	FloatText::DrawAll();
404 
405 	//if (game.debug.DrawBoundingBoxes) DrawBoundingBoxes();
406 	//if (game.debug.debugmode) DrawAttrPoints();
407 }
408 
409 /*
410 void c------------------------------() {}
411 */
412 
game_load(int num)413 bool game_load(int num)
414 {
415 Profile p;
416 
417 	NX_LOG("game_load: loading savefile %d\n", num);
418 
419 	if (profile_load(GetProfileName(num), &p))
420 		return 1;
421 
422 	return game_load(&p);
423 }
424 
game_load(Profile * p)425 bool game_load(Profile *p)
426 {
427 int i;
428 
429 	player->hp = p->hp;
430 	player->maxHealth = p->maxhp;
431 
432 	player->whimstar.nstars = p->num_whimstars;
433 	player->equipmask = p->equipmask;
434 
435 	// load weapons
436 	for(i=0;i<WPN_COUNT;i++)
437 	{
438 		player->weapons[i].hasWeapon = p->weapons[i].hasWeapon;
439 		player->weapons[i].level = p->weapons[i].level;
440 		player->weapons[i].xp = p->weapons[i].xp;
441 		player->weapons[i].ammo = p->weapons[i].ammo;
442 		player->weapons[i].maxammo = p->weapons[i].maxammo;
443 	}
444 
445 	player->curWeapon = p->curWeapon;
446 
447 	// load inventory
448 	memcpy(player->inventory, p->inventory, sizeof(player->inventory));
449 	player->ninventory = p->ninventory;
450 
451 	// load flags
452 	memcpy(game.flags, p->flags, sizeof(game.flags));
453 
454 	// load teleporter slots
455 	textbox.StageSelect.ClearSlots();
456 	for(i=0;i<p->num_teleslots;i++)
457 	{
458 		int slotno = p->teleslots[i].slotno;
459 		int scriptno = p->teleslots[i].scriptno;
460 
461 		textbox.StageSelect.SetSlot(slotno, scriptno);
462 		NX_LOG(" - Read Teleporter Slot %d: slotno=%d scriptno=%d\n", i, slotno, scriptno);
463 	}
464 
465 	// have to load the stage last AFTER the flags are loaded because
466 	// of the options to appear and disappear objects based on flags.
467 	if (load_stage(p->stage)) return 1;
468 	music(p->songno);
469 
470 	player->x = p->px;
471 	player->y = p->py;
472 	player->dir = p->pdir;
473 	player->hide = false;
474 	game.showmapnametime = 0;
475 
476 	return 0;
477 }
478 
479 
game_save(int num)480 bool game_save(int num)
481 {
482 Profile p;
483 
484 	NX_LOG("game_save: writing savefile %d\n", num);
485 
486 	if (game_save(&p))
487 		return 1;
488 
489 	if (profile_save(GetProfileName(num), &p))
490 		return 1;
491 
492 	return 0;
493 }
494 
game_save(Profile * p)495 bool game_save(Profile *p)
496 {
497 int i;
498 
499 	memset(p, 0, sizeof(Profile));
500 
501 	p->stage = game.curmap;
502 	p->songno = music_cursong();
503 
504 	p->px = player->x;
505 	p->py = player->y;
506 	p->pdir = player->dir;
507 
508 	p->hp = player->hp;
509 	p->maxhp = player->maxHealth;
510 
511 	p->num_whimstars = player->whimstar.nstars;
512 	p->equipmask = player->equipmask;
513 
514 	// save weapons
515 	p->curWeapon = player->curWeapon;
516 
517 	for(i=0;i<WPN_COUNT;i++)
518 	{
519 		p->weapons[i].hasWeapon = player->weapons[i].hasWeapon;
520 		p->weapons[i].level = player->weapons[i].level;
521 		p->weapons[i].xp = player->weapons[i].xp;
522 		p->weapons[i].ammo = player->weapons[i].ammo;
523 		p->weapons[i].maxammo = player->weapons[i].maxammo;
524 	}
525 
526 	// save inventory
527 	p->ninventory = player->ninventory;
528 	memcpy(p->inventory, player->inventory, sizeof(p->inventory));
529 
530 	// save flags
531 	memcpy(p->flags, game.flags, sizeof(p->flags));
532 
533 	// save teleporter slots
534 	for(i=0;i<NUM_TELEPORTER_SLOTS;i++)
535 	{
536 		int slotno, scriptno;
537 		if (!textbox.StageSelect.GetSlotByIndex(i, &slotno, &scriptno))
538 		{
539 			p->teleslots[p->num_teleslots].slotno = slotno;
540 			p->teleslots[p->num_teleslots].scriptno = scriptno;
541 			p->num_teleslots++;
542 		}
543 	}
544 
545 	return 0;
546 }
547 
548 /*
549 void c------------------------------() {}
550 */
551 
552 // assign sprites for the objects that didn't get covered by the
553 // auto-generated spritesetup->cpp, and set some properties on the objects.
554 // This is mostly for objects where the sprite is not named the same as
555 // the object it is assigned to.
AssignExtraSprites(void)556 void AssignExtraSprites(void)
557 {
558 	objprop[OBJ_PLAYER].sprite = SPR_MYCHAR;
559 	objprop[OBJ_NPC_PLAYER].sprite = SPR_MYCHAR;
560 	objprop[OBJ_PTELIN].sprite = SPR_MYCHAR;
561 	objprop[OBJ_PTELOUT].sprite = SPR_MYCHAR;
562 
563 	objprop[OBJ_NULL].sprite = SPR_NULL;
564 	objprop[OBJ_HVTRIGGER].sprite = SPR_NULL;
565 	objprop[OBJ_BUBBLE_SPAWNER].sprite = SPR_NULL;
566 	objprop[OBJ_DROPLET_SPAWNER].sprite = SPR_NULL;
567 	objprop[OBJ_HEY_SPAWNER].sprite = SPR_NULL;
568 	objprop[OBJ_WATERLEVEL].sprite = SPR_NULL;
569 	objprop[OBJ_LAVA_DRIP_SPAWNER].sprite = SPR_NULL;
570 	objprop[OBJ_RED_BAT_SPAWNER].sprite = SPR_NULL;
571 	objprop[OBJ_SCROLL_CONTROLLER].sprite = SPR_NULL;
572 	objprop[OBJ_DOCTOR_GHOST].sprite = SPR_NULL;
573 	objprop[OBJ_FALLING_BLOCK].sprite = SPR_NULL;	// set at runtime based on current map
574 	objprop[OBJ_FALLING_BLOCK_SPAWNER].sprite = SPR_NULL;
575 	objprop[OBJ_QUAKE].sprite = SPR_NULL;
576 	objprop[OBJ_BUTE_SPAWNER].sprite = SPR_NULL;
577 	objprop[OBJ_SMOKE_DROPPER].sprite = SPR_NULL;
578 
579 
580 	objprop[OBJ_BUTE_ARROW].sprite = SPR_BUTE_ARROW_LEFT;	// so spawn point is applied
581 
582 	objprop[OBJ_POLISHBABY].defaultnxflags |= NXFLAG_SLOW_WHEN_HURT;
583 
584 	objprop[OBJ_MIMIGAC1].sprite = SPR_MIMIGAC;
585 	objprop[OBJ_MIMIGAC2].sprite = SPR_MIMIGAC;
586 	objprop[OBJ_MIMIGAC_ENEMY].sprite = SPR_MIMIGAC;
587 	objprop[OBJ_MIMIGAC_ENEMY].shaketime = 0;
588 
589 	objprop[OBJ_MISERY_FLOAT].sprite = SPR_MISERY;
590 	objprop[OBJ_MISERY_FLOAT].damage = 1;
591 	objprop[OBJ_MISERY_STAND].sprite = SPR_MISERY;
592 
593 	objprop[OBJ_PUPPY_WAG].sprite = SPR_PUPPY;
594 	objprop[OBJ_PUPPY_BARK].sprite = SPR_PUPPY;
595 	objprop[OBJ_PUPPY_CARRY].sprite = SPR_PUPPY;
596 	objprop[OBJ_PUPPY_SLEEP].sprite = SPR_PUPPY_ASLEEP;
597 	objprop[OBJ_PUPPY_RUN].sprite = SPR_PUPPY;
598 	objprop[OBJ_PUPPY_ITEMS].sprite = SPR_PUPPY;
599 
600 	objprop[OBJ_BALROG_DROP_IN].sprite = SPR_BALROG;
601 	objprop[OBJ_BALROG_BUST_IN].sprite = SPR_BALROG;
602 
603 	objprop[OBJ_CROWWITHSKULL].sprite = SPR_CROW;
604 	objprop[OBJ_ARMADILLO].defaultnxflags |= (NXFLAG_FOLLOW_SLOPE | NXFLAG_SLOW_WHEN_HURT);
605 	objprop[OBJ_SKULLHEAD_CARRIED].sprite = SPR_SKULLHEAD;
606 
607 	objprop[OBJ_TOROKO].defaultnxflags |= NXFLAG_FOLLOW_SLOPE;
608 	objprop[OBJ_TOROKO_TELEPORT_IN].sprite = SPR_TOROKO;
609 
610 	objprop[OBJ_KING].defaultnxflags |= NXFLAG_FOLLOW_SLOPE;
611 
612 	objprop[OBJ_FAN_DROPLET].sprite = SPR_WATER_DROPLET;
613 
614 	objprop[OBJ_MGUN_TRAIL].defaultflags |= FLAG_IGNORE_SOLID;
615 
616 	objprop[OBJ_BLOCK_MOVEH].sprite = SPR_MOVING_BLOCK;
617 	objprop[OBJ_BLOCK_MOVEV].sprite = SPR_MOVING_BLOCK;
618 
619 	objprop[OBJ_IRONH].shaketime = 8;
620 
621 	objprop[OBJ_OMEGA_BODY].shaketime = 0;		// omega handles his own shaketime
622 	objprop[OBJ_OMEGA_BODY].hurt_sound = SND_ENEMY_HURT_BIG;
623 
624 	objprop[OBJ_OMEGA_LEG].sprite = SPR_OMG_LEG_INAIR;
625 	objprop[OBJ_OMEGA_STRUT].sprite = SPR_OMG_STRUT;
626 
627 	objprop[OBJ_OMEGA_SHOT].death_smoke_amt = 4;
628 	objprop[OBJ_OMEGA_SHOT].death_sound = SND_EXPL_SMALL;
629 	objprop[OBJ_OMEGA_SHOT].initial_hp = 1;
630 	objprop[OBJ_OMEGA_SHOT].xponkill = 1;
631 
632 	objprop[OBJ_BAT_HANG].sprite = SPR_BAT;
633 	objprop[OBJ_BAT_CIRCLE].sprite = SPR_BAT;
634 
635 	objprop[OBJ_FIREBALL1].defaultnxflags |= NXFLAG_FOLLOW_SLOPE;
636 	objprop[OBJ_FIREBALL23].defaultnxflags |= NXFLAG_FOLLOW_SLOPE;
637 
638 	objprop[OBJ_CURLY_AI].sprite = SPR_CURLY;
639 	objprop[OBJ_CURLY_AI].defaultnxflags |= NXFLAG_FOLLOW_SLOPE;
640 
641 	objprop[OBJ_CURLY].defaultnxflags |= NXFLAG_FOLLOW_SLOPE;
642 
643 	objprop[OBJ_MINICORE].hurt_sound = SND_ENEMY_HURT_COOL;
644 	objprop[OBJ_CORE_CONTROLLER].hurt_sound = SND_CORE_HURT;
645 
646 	objprop[OBJ_CURLY_CARRIED].sprite = SPR_CURLY;
647 
648 	objprop[OBJ_BALROG_BOSS_RUNNING].sprite = SPR_BALROG;
649 	objprop[OBJ_BALROG_BOSS_FLYING].sprite = SPR_BALROG;
650 	objprop[OBJ_BALROG_BOSS_MISSILES].sprite = SPR_BALROG;
651 
652 	objprop[OBJ_XP].sprite = SPR_XP_SMALL;
653 
654 	objprop[OBJ_NPC_IGOR].sprite = SPR_IGOR;
655 	objprop[OBJ_BOSS_IGOR].sprite = SPR_IGOR;
656 	objprop[OBJ_BOSS_IGOR_DEFEATED].sprite = SPR_IGOR;
657 	objprop[OBJ_IGOR_BALCONY].sprite = SPR_IGOR;
658 
659 	objprop[OBJ_X_TARGET].hurt_sound = SND_ENEMY_HURT_COOL;
660 	objprop[OBJ_X_INTERNALS].shaketime = 9;
661 	objprop[OBJ_X_MAINOBJECT].xponkill = 1;
662 
663 	objprop[OBJ_POOH_BLACK_BUBBLE].xponkill = 0;
664 	objprop[OBJ_POOH_BLACK_DYING].sprite = SPR_POOH_BLACK;
665 
666 	objprop[OBJ_BOOSTER_FALLING].sprite = SPR_PROFESSOR_BOOSTER;
667 
668 	objprop[OBJ_MIMIGA_FARMER_STANDING].sprite = SPR_MIMIGA_FARMER;
669 	objprop[OBJ_MIMIGA_FARMER_WALKING].sprite = SPR_MIMIGA_FARMER;
670 	objprop[OBJ_DROLL_GUARD].sprite = SPR_DROLL;
671 
672 	objprop[OBJ_MA_PIGNON_CLONE].sprite = SPR_MA_PIGNON;
673 
674 	objprop[OBJ_DOCTOR_SHOT_TRAIL].sprite = SPR_DOCTOR_SHOT;
675 
676 	// they're still able to detect when they touch floor; etc,
677 	// but we don't want say a falling one to get blocked by the ceiling.
678 	objprop[OBJ_RED_ENERGY].defaultflags |= FLAG_IGNORE_SOLID;
679 
680 	objprop[OBJ_SUE_TELEPORT_IN].sprite = SPR_SUE;
681 
682 	objprop[OBJ_MISERY_BAT].sprite = SPR_ORANGE_BAT_FINAL;
683 	objprop[OBJ_UD_MINICORE_IDLE].sprite = SPR_UD_MINICORE;
684 
685 	objprop[OBJ_WHIMSICAL_STAR].sprite = SPR_WHIMSICAL_STAR;	// for bbox only, object is invisible
686 }
687 
688