Пример #1
0
void ai_ballos_skull(Object *o)
{
	ANIMATE(8, 0, 3);
	
	switch(o->state)
	{
		case 0:
		{
			o->state = 100;
			o->frame = random(0, 16) & 3;
		}
		case 100:
		{
			o->yinertia += 0x40;
			LIMITY(0x700);
			
			if (o->timer++ & 2)
			{
				(SmokePuff(o->x, o->y))->PushBehind(o);
			}
			
			if (o->y > 0x10000)
			{
				o->flags &= ~FLAG_IGNORE_SOLID;
				
				if (o->blockd)
				{
					o->yinertia = -0x200;
					o->state = 110;
					o->flags |= FLAG_IGNORE_SOLID;
					
					quake(10, SND_BLOCK_DESTROY);
					
					for(int i=0;i<4;i++)
					{
						Object *s = SmokePuff(o->x + random(-12<<CSF, 12<<CSF), \
											  o->y + 0x2000);
						
						s->xinertia = random(-0x155, 0x155);
						s->yinertia = random(-0x600, 0);
						s->PushBehind(o);
					}
				}
			}
		}
		break;
		
		case 110:
		{
			o->yinertia += 0x40;
			
			if (o->Top() >= (map.ysize * TILE_H) << CSF)
			{
				o->Delete();
			}
		}
		break;
	}
}
Пример #2
0
void ai_proximity_press_vert(Object *o)
{
	switch(o->state)
	{
		case 0:
		{
			if (pdistlx(8<<CSF) && pdistly2(8<<CSF, 128<<CSF) && \
				!o->blockd)
			{
				o->state = 10;
				o->animtimer = 0;
				o->frame = 1;
			}
		}
		break;
		
		case 10:
		{
			if (o->frame < 2)
				ANIMATE_FWD(2);
			
			if (o->blockd)
			{
				if (o->frame >= 2)	// make sure eye fully open
				{
					SmokeSide(o, 4, DOWN);
					quake(10);
				}
				
				o->flags |= FLAG_SOLID_BRICK;
				o->damage = 0;
				
				o->state = 11;
				o->frame = 0;
			}
			else
			{
				if (player->Top() > o->CenterY())
				{
					o->flags &= ~FLAG_SOLID_BRICK;
					o->damage = 127;
				}
				else
				{
					o->flags |= FLAG_SOLID_BRICK;
					o->damage = 0;
				}
			}
		}
		break;
	}
	
	if (o->state >= 5)
	{
		o->yinertia += 0x80;
		LIMITY(0x5ff);
	}
}
Пример #3
0
void ai_block_movev(Object *o)
{
	int py = player->CenterY();
	int objy = o->CenterY();
	
	switch(o->state)
	{
		case 0:
         NX_LOG("ai_block_movev - state 0.\n");
			o->flags |= FLAG_SOLID_BRICK;
			o->smushdamage = 100;
			o->dir = (o->dir == LEFT) ? UP : DOWN;
			o->state = (o->dir == DOWN) ? 10 : 20;
		break;
		
		case 10:	// at top edge, ready to travel down
         NX_LOG("ai_block_movev - state 10.\n");
			if (((py > objy) && (py - objy) < 0x32000) || \
				((py < objy) && (objy - py) < 0x3200))
			{
				if (pdistlx(0x3200))
				{
					o->state = 30;
					o->timer = 0;
				}
			}
		break;
		
		case 20:	// at bottom edge, ready to travel up
         NX_LOG("ai_block_movev - state 20.\n");
			if (((py > objy) && (py - objy) < 0x3200) || \
				((py < objy) && (objy - py) < 0x32000))
			{
				if (pdistlx(0x3200))
				{
					o->state = 30;
					o->timer = 0;
				}
			}
		break;
		
		case 30:	// traveling
         NX_LOG("ai_block_movev - state 30.\n");
		{
			YACCEL(0x20);
			LIMITY(0x200);
			
			// hit edge
			if ((o->dir == DOWN && o->blockd) || (o->dir == UP && o->blocku))
			{
				SmokeSide(o, 4, o->dir);
				quake(10);
				
				o->yinertia = 0;
				o->dir ^= 1;
				o->state = (o->dir==DOWN) ? 10 : 20;
			}
			
			if ((++o->timer % 10) == 6)
				sound(SND_BLOCK_MOVE);
		}
		break;
	}
}
Пример #4
0
void ai_block_moveh(Object *o)
{
	int px = player->CenterX();
	int objx = o->CenterX();
	
	switch(o->state)
	{
		case 0:
         NX_LOG("ai_block_moveh - state 0.\n");
			o->flags |= FLAG_SOLID_BRICK;
			o->smushdamage = 100;
			o->state = (o->dir == LEFT) ? 10:20;
		break;
		
		case 10:	// at right edge, ready to travel left
         NX_LOG("ai_block_moveh - state 10.\n");
			if (((px > objx) && (px - objx) < 0x3200) || \
				((px < objx) && (objx - px) < 0x32000))
			{
				if (pdistly(0x3200))
				{
					o->state = 30;
					o->timer = 0;
				}
			}
		break;
		
		case 20:	// at left edge, ready to travel right
         NX_LOG("ai_block_moveh - state 20.\n");
			if (((px > objx) && (px - objx) < 0x32000) || \
				((px < objx) && (objx - px) < 0x3200))
			{
				if (pdistly(0x3200))
				{
					o->state = 30;
					o->timer = 0;
				}
			}
		break;
		
		case 30:	// traveling
		{
         NX_LOG("ai_block_moveh - state 30.\n");
			XACCEL(0x20);
			LIMITX(0x200);
			
			// hit edge
			if ((o->dir == RIGHT && o->blockr) || (o->dir == LEFT && o->blockl))
			{
				SmokeSide(o, 4, o->dir);
				quake(10);
				
				o->xinertia = 0;
				o->dir ^= 1;
				o->state = (o->dir==LEFT) ? 10 : 20;
			}
			
			if ((++o->timer % 10) == 6)
				sound(SND_BLOCK_MOVE);
		}
		break;
	}
}
Пример #5
0
void ai_ma_pignon(Object *o)
{
	/*debug("state: %d", o->state);
	debug("timer: %d", o->timer);
	debug("timer2: %d", o->timer2);
	debug("timer3: %d", o->timer3);
	debug("xinertia: %d", o->xinertia);
	debug("yinertia: %d", o->yinertia);
	debug("frame: %d", o->frame);
	*/
	
	switch(o->state)
	{
		case 0:
		{
			o->SnapToGround();
			o->state = 1;
		}
		case 1:
		{
			FACEPLAYER;
			o->frame = 0;
			randblink(o);
		}
		break;
		
		case MP_Fight_Start:		// set by script
		{
			o->state = MP_BaseState;
			o->timer = 0;
			o->timer2 = 0;
			o->flags |= FLAG_SHOOTABLE;
		}
		case MP_BaseState:
		{
			FACEPLAYER;
			o->damage = 1;
			o->frame = 0;
			
			if (++o->timer > 4)
			{
				o->timer = 0;
				o->state = MP_Jump;
				
				if (++o->timer3 > 12)
				{
					o->timer3 = 0;
					o->state = MP_CloneAttack;
				}
			}
		}
		break;
		
		case MP_Jump:		// pause a moment and jump
		{
			o->frame = 2;
			if (++o->timer > 4)
			{
				o->state = MP_In_Air;
				o->frame = 3;
				
				o->xinertia = random(-0x400, 0x400);
				o->yinertia = -0x800;
				
				sound(SND_ENEMY_JUMP);
				o->timer2++;
			}
		}
		break;
		
		case MP_In_Air:		// jumping or falling after clone attack
		{
			o->yinertia += 0x80;
			
			// for when falling back onscreen after clone attack
			if (o->y > (8 * TILE_H) << CSF)
				o->flags &= ~FLAG_IGNORE_SOLID;
			else
				o->blockd = false;
			
			// bounce off walls
			if ((o->blockl && o->xinertia < 0) || \
				(o->blockr && o->xinertia > 0))
			{
				o->xinertia = -o->xinertia;
			}
			
			FACEPLAYER;
			
			// select frame
			if (o->yinertia < -0x200)
			{
				o->frame = 3;
			}
			else if (o->yinertia > 0x200)
			{
				o->frame = 4;
			}
			else
			{
				o->frame = 0;
			}
			
			if (o->blockd && o->yinertia > 0)
			{
				o->state = MP_Landed;
				o->timer = 0;
				o->frame = 2;
				o->xinertia = 0;
			}
			
			if (o->timer2 > 4)
			{
				if (player->y < (o->y + 0x800))
				{
					o->state = MP_ChargeAttack;
					o->timer = 0;
					o->xinertia = 0;
					o->yinertia = 0;
				}
			}
		}
		break;
		
		case MP_Landed:
		{
			o->frame = 2;
			if (++o->timer > 4)
			{
				o->state = MP_BaseState;
			}
		}
		break;
		
		
		case MP_ChargeAttack:		// charge attack
		{
			o->frame = 5;
			if (++o->timer > 10)
			{
				o->state = MP_ChargeAttack+1;
				o->frame = 6;
				
				XMOVE(0x5ff);
				sound(SND_FUNNY_EXPLODE);
				
				o->flags &= ~FLAG_SHOOTABLE;
				o->flags |= FLAG_INVULNERABLE;
				o->damage = 10;
			}
		}
		break;
		case MP_ChargeAttack+1:		// in-air during charge attack
		{
			ANIMATE(0, 6, 7);
			
			if ((o->xinertia < 0 && o->blockl) || \
				(o->xinertia > 0 && o->blockr))
			{
				o->state = MP_Hit_Wall;
			}
		}
		break;
		
		case MP_Hit_Wall:		// hit wall
		{
			o->state++;
			o->timer = 0;
			quake(16);
		}
		case MP_Hit_Wall+1:
		{
			o->damage = 4;
			ANIMATE(0, 6, 7);
			
			if ((++o->timer % 6) == 0)
			{
				int x = (random(4, 16) * TILE_W) << CSF;
				CreateObject(x, (16 << CSF), OBJ_MA_PIGNON_ROCK);
			}
			
			if (o->timer > 30)
			{
				o->timer2 = 0;
				o->state = MP_In_Air;
				
				o->flags |= FLAG_SHOOTABLE;
				o->flags &= ~FLAG_INVULNERABLE;
				
				o->damage = 3;
			}
		}
		break;
		
		
		case MP_CloneAttack:	// begin clone-attack sequence
		{
			o->state++;
			o->frame = 9;
			FACEPLAYER;
		}
		case MP_CloneAttack+1:	// walk at player before attack
		{
			ANIMATE(0, 9, 11);
			
			XMOVE(0x400);
			if (pdistlx(3 << CSF))
			{
				o->state = MP_Fly_Up;
				o->timer = 0;
				o->frame = 2;
				o->xinertia = 0;
			}
		}
		break;
		
		case MP_Fly_Up:		// jump and fly up for clone attack
		{
			o->frame = 2;
			if (++o->timer > 4)
			{
				o->state++;
				o->frame = 12;
				o->yinertia = -0x800;
				sound(SND_FUNNY_EXPLODE);
				
				o->flags |= FLAG_IGNORE_SOLID;
				o->flags &= ~FLAG_SHOOTABLE;
				o->flags |= FLAG_INVULNERABLE;
				
				o->damage = 10;
			}
		}
		break;
		case MP_Fly_Up+1:		// flying up
		{
			ANIMATE(0, 12, 13);
			
			if (o->y < (16<<CSF))
				o->state = MP_Spawn_Clones;
		}
		break;
		
		case MP_Spawn_Clones:	// offscreen, spawning clones
		{
			o->yinertia = 0;
			o->state++;
			o->timer = 0;
			
			quake(10);
		}
		case MP_Spawn_Clones+1:
		{
			ANIMATE(0, 12, 13);
			
			if ((++o->timer % 6) == 0)
			{
				int x = (random(4, 16) * TILE_W) << CSF;
				CreateObject(x, (16 << CSF), OBJ_MA_PIGNON_CLONE);
			}
			
			if (o->timer > 30)
			{
				o->timer2 = 0;
				o->state = MP_In_Air;	// fall back down to ground
				
				o->flags |= FLAG_SHOOTABLE;
				o->flags &= ~FLAG_INVULNERABLE;
			}
		}
		break;
		
		case MP_Defeated:			// defeated -- set by script
		{
			KillObjectsOfType(OBJ_MA_PIGNON_CLONE);
			o->flags &= ~FLAG_SHOOTABLE;
			o->state++;
			o->timer = 0;
			o->frame = 8;
			o->damage = 0;
		}
		case MP_Defeated+1:
		{
			o->yinertia += 0x20;
			if (o->blockd)
			{
				o->xinertia *= 7;
				o->xinertia /= 8;
			}
			
			o->display_xoff = (++o->timer & 1);
		}
		break;
	}
	
	// ma pignon is invulnerable to missiles and Blade.
	if (o->state >= MP_Fight_Start && o->state < MP_Defeated)
	{
		// ....he's invulnerable anyway during these two states so don't mess with that.
		if (o->state != MP_ChargeAttack+1 && o->state != MP_Fly_Up+1)
		{
			bool found_weapons = false;
			if (o->type != OBJ_MA_PIGNON_CLONE)
			{
				Object *c;
				FOREACH_OBJECT(c)
				{
					if (c->type == OBJ_MISSILE_SHOT || \
						c->type == OBJ_SUPERMISSILE_SHOT || \
						c->type == OBJ_MISSILE_BOOM_SPAWNER || \
						c->type == OBJ_BLADE12_SHOT || \
						c->type == OBJ_BLADE3_SHOT || \
						c->type == OBJ_BLADE_SLASH)
					{
						found_weapons = true;
						break;
					}
				}
			}
			
			if (found_weapons)
			{
				o->flags &= ~FLAG_SHOOTABLE;
				o->flags |= FLAG_INVULNERABLE;
			}
			else
			{
				o->flags |= FLAG_SHOOTABLE;
				o->flags &= ~FLAG_INVULNERABLE;
			}
		}
Пример #6
0
// This object is responsible for collapsing the walls in the final best-ending sequence.
// All the original object does is collapse one tile further every 101 frames.
// However, since it's triggered at the beginning of the cinematic and then is let to run
// through almost the entire thing it needs to be sync'd really-really perfect with a
// number of other systems; the textboxes, etc.
//
// I spent several hours trying to get my events to run in perfect frame-by-frame
// exactness with the original engine, and found several things that were slightly off.
// However, I've decided that even if I got it absolutely perfect, it's too liable to
// get broken by some minor innocent change in the future, and requires too much of
// the engine to be tuned just so.
//
// So, I've added some event-based triggers to the object, that are NOT technically supposed
// to be there. These will make extra sure that nothing embarrassing happens during this great
// finale, such as the walls being one tile too far at one point, or even worse, having
// them collapse onto Balrog before he makes it to the exit. Because there are no triggers
// in the script and I can't change the script, I had to do a bit of sneaky spying on program
// state to implement them.
void ai_wall_collapser(Object *o)
{
int y;
	
	switch(o->state)
	{
		case 0:
		{
			o->invisible = true;
			o->timer = 0;
			o->state = 1;
		}
		break;
		
		case 10:	// trigger
		{
			if (++o->timer > 100)
			{
				o->timer2++;
				o->timer = 0;
				
				int xa = (o->x >> CSF) / TILE_W;
				int ya = (o->y >> CSF) / TILE_H;
				for(y=0;y<20;y++)
				{
					// pushing the smoke behind all objects prevents it from covering
					// up the NPC's on the collapse just before takeoff.
					map_ChangeTileWithSmoke(xa, ya+y, 109, 4, false, lowestobject);
				}
				
				sound(SND_BLOCK_DESTROY);
				quake(20);
				
				if (o->dir == LEFT) o->x -= (TILE_W << CSF);
							   else o->x += (TILE_W << CSF);
				
				// reached the solid tile in the center of the throne.
				// it isn't supposed to cover this tile until after Curly
				// says we're gonna get crushed.
				if (o->timer2 == 6)
					o->state = 20;
				
				// balrog is about to take off/rescue you.
				if (o->timer2 == 9)
					o->state = 30;
			}
		}
		break;
		
		// "gonna get crushed" event
		case 20:
		{
			// wait for text to come up
			if (textbox.IsVisible())
				o->state = 21;
		}
		break;
		case 21:
		{
			// wait for text to dismiss, then tile immediately collapses
			if (!textbox.IsVisible())
			{
				o->state = 10;
				o->timer = 1000;
			}
		}
		break;
		
		// balrog is about to take off. the video I took shows that
		// the walls are supposed to collapse into your space on the
		// exact same frame that he breaks the first ceiling tile.
		case 30:
		{
			o->linkedobject = Objects::FindByType(OBJ_BALROG_DROP_IN);
			if (o->linkedobject)
				o->state = 31;
		}
		break;
		case 31:
		{
			//debug("%x", o->linkedobject->y);
			if (o->linkedobject && o->linkedobject->y <= 0x45800)
			{
				o->state = 10;
				o->timer = 1000;
			}
		}
		break;
	}
Пример #7
0
void ai_balrog(Object *o)
{
bool fall = true;

	// he is greenish when he first appears in Gum Room
	if (DoesCurrentStageUseSpriteset(NPCSET_FROG))
		o->sprite = SPR_BALROG_GREEN;
	
	switch(o->state)
	{
		case 0:
		{
			o->flags &= ~FLAG_IGNORE_SOLID;
			o->xinertia = 0;
			o->balrog.smoking = false;
			
			o->frame = 0;
			randblink(o, 4, 8);
		}
		break;
		
		case 10:		// he jumps and flys away
			o->xinertia = 0;
			o->frame = 2;
			o->timer = 0;
			o->state++;
		case 11:
		{
			if (++o->timer <= 20) break;
			
			o->state++;
			o->yinertia = -0x800;
			o->flags |= FLAG_IGNORE_SOLID;
		}
		case 12:
		{
			fall = false;
			o->frame = 3;
			o->yinertia -= 0x10;
			if (o->y < 0)
			{
				o->Delete();
				sound(SND_QUAKE);
				game.quaketime = 30;
			}
		}
		break;
		
		// he looks shocked and shakes, then flys away
		// used when he is "hit by something"
		case 20:
		{
			o->state = 21;
			o->frame = 5;
			o->xinertia = 0;
			o->timer = o->timer2 = 0;
			SmokeClouds(o, 4, 8, 8);
			sound(SND_BIG_CRASH);
			o->balrog.smoking = 1;
		}
		case 21:
		{
			o->timer2++;
			o->x += ((o->timer2 >> 1) & 1) ? (1<<CSF) : -(1<<CSF);
			
			if (++o->timer > 100)
				o->state = 10;
			
			o->yinertia += 0x20;
			LIMITY(0x5ff);
		}
		break;
		
		case 30:	// he smiles for a moment
			o->frame = 6;
			o->timer = 0;
			o->state = 31;
		case 31: if (++o->timer > 100) o->state = o->frame = 0;
		break;
		
		// flashing white (spell casted on him)
		// this only works in Gum Room before balfrog fight, as the normal
		// non-greenish spritesheet doesn't include the required frame.
		case 40:
			o->state = 41;
			o->animtimer = 0;
			o->animframe = 0;
		case 41:
		{
			static const int flash_seq[] = { 5, 7 };
			o->animate_seq(1, flash_seq, 2);
		}
		break;
		case 42:
			o->timer = 0;
			o->state = 43;
		case 43:
			// flashing visibility
			// (transforming into Balfrog stage boss;
			//	our flashing is interlaced with his)
			o->timer++;
			o->invisible = (o->timer & 2) ? false : true;
		break;
		
		case 50:	// he faces away
			o->frame = 8;
			o->xinertia = 0;
		break;
		
		case 60:	// he walks
			o->state = 61;
			balrog_walk_init(o);
		case 61:
		{
			balrog_walk_animation(o);
			XMOVE(0x200);
		}
		break;
		
		// he is teleported away (looking distressed)
		// this is when he is sent to Labyrinth at end of Sand Zone
		case 70:
			o->xinertia = 0;
			o->timer = 0;
			o->frame = 7;
			o->state++;
		case 71:
			if (DoTeleportOut(o, 2))
				o->Delete();
		break;
		
		case 80:	// hands up and shakes
			o->frame = 5;
			o->state = 81;
		case 81:
		{
			if (++o->timer & 2)
				o->x += (1 << CSF);
			else
				o->x -= (1 << CSF);
		}
		break;
		
		// fly up and lift Curly & PNPC
		// (post-Ballos ending scene)
		case 100:
		{
			o->state = 101;
			o->timer = 0;
			o->frame = 2;	// prepare for jump
		}
		case 101:
		{
			if (++o->timer > 20)
			{
				o->state = 102;
				o->timer = 0;
				o->frame = 3;	// fly up
				
				DeleteObjectsOfType(OBJ_NPC_PLAYER);
				DeleteObjectsOfType(OBJ_CURLY);
				
				CreateObject(0, 0, OBJ_BALROG_PASSENGER, 0, 0, LEFT)->linkedobject = o;
				CreateObject(0, 0, OBJ_BALROG_PASSENGER, 0, 0, RIGHT)->linkedobject = o;
				
				o->yinertia = -0x800;
				o->flags |= FLAG_IGNORE_SOLID;	// so can fly through ceiling
				fall = false;
			}
		}
		break;
		case 102:	// flying up during escape seq
		{
			fall = false;
			
			// bust through ceiling
			int y = ((o->y + (4<<CSF)) >> CSF) / TILE_H;
			if (y < 35 && y >= 0)
			{
				int x = (o->CenterX() >> CSF) / TILE_W;
				
				if (map.tiles[x][y] != 0)
				{
					// smoke needs to go at the bottom of z-order or you can't
					// see any of the characters through all the smoke.
					map_ChangeTileWithSmoke(x, y, 0, 4, false, lowestobject);
					map_ChangeTileWithSmoke(x-1, y, 0, 4, false, lowestobject);
					map_ChangeTileWithSmoke(x+1, y, 0, 4, false, lowestobject);
					
					megaquake(10, 0);
					sound(SND_MISSILE_HIT);
				}
			}
			
			if (o->Bottom() < -(20<<CSF))
			{
				quake(30, 0);
				o->Delete();
			}
		}
		break;
		
		case 500:	// used during Balfrog death scene
		{
			fall = false;
		}
		break;
	}
Пример #8
0
void ai_counter_bomb(Object *o)
{
	/*debug("state: %d", o->state);
	debug("timer: %d", o->timer);
	debug("timer2: %d", o->timer2);*/
	
	switch(o->state)
	{
		case 0:
		{
			o->state = 1;
			o->ymark = o->y;
			
			o->timer = random(0, 50);
			o->timer2 = 0;
		}
		case 1:
		{	// desync if multiple enemies
			if (--o->timer < 0)
			{
				o->timer = 0;
				o->state = 2;
				o->yinertia = 0x300;
			}
		}
		break;
		
		case 2:		// ready
		{
			if (pdistlx(80 << CSF) || o->shaketime)
			{
				o->state = 3;
				o->timer = 0;
			}
		}
		break;
		
		case 3:		// counting down...
		{
			if (--o->timer < 0)
			{
				if (o->timer2 < 5)
				{
					Object *number = CreateObject(o->CenterX() + (8 << CSF), \
												  o->y + (16 << CSF), \
												  OBJ_COUNTER_BOMB_NUMBER);
					
					number->frame = o->timer2++;
					o->timer = 60;
				}
				else
				{
					// expand bounding box to cover explosion area
					o->x = o->CenterX();
					o->y = o->CenterY();
					o->invisible = true;
					o->sprite = SPR_BBOX_PUPPET_1;
					sprites[o->sprite].bbox.x1 = -128;
					sprites[o->sprite].bbox.y1 = -100;
					sprites[o->sprite].bbox.x2 = 128;
					sprites[o->sprite].bbox.y2 = 100;
					o->damage = 30;
					
					o->yinertia = 0;
					o->state = 4;
					
					// make kaboom
					sound(SND_EXPLOSION1);
					quake(20);
					SmokeXY(o->CenterX(), o->CenterY(), 100, 128, 100);
					
					return;
				}
			}
		}
		break;
		
		case 4:		// exploding (one frame only to give time for bbox to damage player)
			o->Delete();
			return;
	}
	
	ANIMATE(4, 0, 2);
	
	if (o->state == 2 || o->state == 3)
	{
		o->yinertia += (o->y > o->ymark) ? -0x10 : 0x10;
		LIMITY(0x100);
	}
}
Пример #9
0
void ai_deleet(Object *o)
{
	// trigger counter
	if (o->hp < (1000 - DELEET_HP) && o->state < 2)
	{
		o->state = 2;
		o->timer = 0;
		o->frame = 2;
		
		o->flags |= FLAG_INVULNERABLE;
		sound(SND_CHEST_OPEN);
	}
	
	switch(o->state)
	{
		case 0:
		{
			o->state = 1;
			o->x += (TILE_W / 2) << CSF;
			o->y += (TILE_H / 2) << CSF;
			
			if (o->dir == LEFT)
				o->y += (8<<CSF);
			else
				o->x += (8<<CSF);
		}
		case 1:
		{
			if (o->shaketime)
				o->timer2++;
			else
				o->timer2 = 0;
			
			o->frame = (o->timer2 & 2) ? 1 : 0;
		}
		break;
		
		case 2:
		{
			int counter = -1;
			
			switch(o->timer)
			{
				case 0:		counter = 0; break;	// 5
				case 50:	counter = 1; break;	// 4
				case 100:	counter = 2; break;	// 3
				case 150:	counter = 3; break;	// 2
				case 200:	counter = 4; break;	// 1
				
				case 250:
				{
					o->state = 3;
					o->sprite = SPR_BBOX_PUPPET_1;
					o->invisible = true;
					
					sprites[o->sprite].bbox.x1 = -48;
					sprites[o->sprite].bbox.x2 = 48;
					sprites[o->sprite].bbox.y1 = -48;
					sprites[o->sprite].bbox.y2 = 48;
					o->damage = 12;
					
					quake(10);
					SmokeXY(o->x, o->y, 40, 48, 48);
					
					o->flags &= ~FLAG_SHOOTABLE;
					o->flags &= ~FLAG_INVULNERABLE;
					
					if (o->dir == LEFT)
					{
						int x = (o->x >> CSF) / TILE_W;
						int y = ((o->y >> CSF) - 8) / TILE_H;
						
						map.tiles[x][y] = 0;
						map.tiles[x][y+1] = 0;
					}
					else
					{
						int x = ((o->x >> CSF) - 8) / TILE_W;
						int y = (o->y >> CSF) / TILE_H;
						
						map.tiles[x][y] = 0;
						map.tiles[x+1][y] = 0;
					}
					
				}
				break;
			}
			
			if (counter != -1)
			{
				CreateObject(o->x, o->y - (8<<CSF), \
						OBJ_COUNTER_BOMB_NUMBER)->frame = counter;
			}
			
			o->timer++;
		}
Пример #10
0
void CoreBoss::Run()
{
bool do_thrust = false;
int i;

	if (!o) return;
	//stat("state = %d", o->state);
	
	switch(o->state)
	{
		case CORE_SLEEP:	break;			// core is asleep
		
		// Core's mouth is closed.
		// Core targets player point but does not update it during the state.
		// This is also the state set via BOA to awaken the core.
		case CORE_CLOSED:
		{
			o->state = CORE_CLOSED+1;
			o->timer = 0;
			
			StopWaterStream();
			o->xmark = player->x;
			o->ymark = player->y;
		}
		case CORE_CLOSED+1:
		{
			// open mouth after 400 ticks
			if (o->timer > 400)
			{
				if (++o->timer2 > 3)
				{	// every 3rd time do gusting left and big core blasts
					o->timer2 = 0;
					o->state = CORE_GUST;
				}
				else
				{
					o->state = CORE_OPEN;
				}
				
				do_thrust = true;
			}
		}
		break;
		
		// Core's mouth is open.
		// Core moves towards player, and updates the position throughout
		// the state (is "aggressive" about seeking him).
		// Core fires ghosties, and curly targets it.
		case CORE_OPEN:
		{
			o->state = CORE_OPEN+1;
			o->timer = 0;
			// gonna open mouth, so save the current HP so we'll
			// know how much damage we've taken this time.
			o->savedhp = o->hp;
		}
		case CORE_OPEN+1:
		{
			o->xmark = player->x;
			o->ymark = player->y;
			
			// must call constantly for red-flashing when hit
			OPEN_MOUTH;
			
			// hint curly to target us
			if ((o->timer % 64) == 1)
			{
				o->CurlyTargetHere();
			}
			
			// spawn ghosties
			if (o->timer < 200)
			{
				if ((o->timer % 20)==0)
				{
					CreateObject(o->x + (random(-48, -16) << CSF), \
						     	 o->y + (random(-64, 64) << CSF), \
							 	 OBJ_CORE_GHOSTIE);
				}
			}
			
			// close mouth when 400 ticks have passed or we've taken more than 200 damage
			if (o->timer > 400 || (o->savedhp - o->hp) >= 200)
			{
				o->state = CORE_CLOSED;
				CLOSE_MOUTH;
				do_thrust = true;
			}
		}
		break;
		
		
		case CORE_GUST:
		{
			o->state = CORE_GUST+1;
			o->timer = 0;
			
			StartWaterStream();
		}
		case CORE_GUST+1:
		{
			// spawn water droplet effects and push player
			Object *droplet = CreateObject(player->x + ((random(-50, 150)<<CSF)*2), \
								   		   player->y + (random(-160, 160)<<CSF),
								   		   OBJ_FAN_DROPLET);
			droplet->dir = LEFT;
			player->xinertia -= 0x20;
			
			OPEN_MOUTH;
			
			// spawn the big white blasts
			if (o->timer==300 || o->timer==350 || o->timer==400)
			{
				EmFireAngledShot(pieces[CFRONT], OBJ_CORE_BLAST, 0, 3<<CSF);
				sound(SND_LIGHTNING_STRIKE);
			}
			
			if (o->timer > 400)
			{
				o->state = CORE_CLOSED;
				CLOSE_MOUTH;
				do_thrust = true;
			}
		}
		break;
		
		
		case 500:		// defeated!!
		{
			StopWaterStream();
			map.wlforcestate = WL_CALM;
			
			o->state = 501;
			o->timer = 0;
			o->xinertia = o->yinertia = 0;
			game.curlytarget.timeleft = 0;
			
			CLOSE_MOUTH;
			
			game.quaketime = 20;
			SmokeXY(pieces[CBACK]->x, pieces[CBACK]->CenterY(), 20, 128, 64);
			
			// tell all the MC's to retreat
			for(i=0;i<5;i++)
			{
				pieces[i]->flags &= ~(FLAG_SHOOTABLE & FLAG_INVULNERABLE);
				pieces[i]->state = MC_RETREAT;
			}
		}
		case 501:
		{
			o->timer++;
			if ((o->timer & 0x0f) != 0)
			{
				SmokeXY(pieces[CBACK]->x, pieces[CBACK]->CenterY(), 1, 64, 32);
			}
			
			if (o->timer & 2)
				o->x -= (1 << CSF);
			else
				o->x += (1 << CSF);
			
			#define CORE_DEATH_TARGET_X		0x7a000
			#define CORE_DEATH_TARGET_Y		0x16000
			o->xinertia += (o->x > CORE_DEATH_TARGET_X) ? -0x80 : 0x80;
			o->yinertia += (o->y > CORE_DEATH_TARGET_Y) ? -0x80 : 0x80;
		}
		break;
		
		case 600:			// teleported away by Misery
		{
			o->xinertia = 0;
			o->yinertia = 0;
			o->state++;
			//sound(SND_TELEPORT);
			
			pieces[CFRONT]->clip_enable = pieces[CBACK]->clip_enable = 1;
			o->timer = sprites[pieces[CFRONT]->sprite].h;
		}
		case 601:
		{
			pieces[CFRONT]->display_xoff = pieces[CBACK]->display_xoff = random(-8, 8);
			
			pieces[CFRONT]->clipy2 = o->timer;
			pieces[CBACK]->clipy2 = o->timer;
			
			if (--o->timer < 0)
			{
				pieces[CFRONT]->invisible = true;
				pieces[CBACK]->invisible = true;
				
				// restore status bars
				game.stageboss.object = NULL;
				game.bossbar.object = NULL;
				o->Delete(); o = NULL;
				return;
			}
		}
		break;
	}
	
	
	if (do_thrust)
	{
		// tell all the minicores to jump to a new position
		for(i=0;i<5;i++)
		{
			pieces[i]->state = MC_THRUST;
		}
		
		quake(20);
		sound(SND_CORE_THRUST);
	}
	
	
	// fire the minicores in any awake non-dead state
	if (o->state >= CORE_CLOSED && o->state < 500)
	{
		o->timer++;
		
		// fire off each minicore sequentially...
		switch(o->timer)
		{
			case 80+0:   pieces[0]->state = MC_CHARGE_FIRE; break;
			case 80+30:  pieces[1]->state = MC_CHARGE_FIRE; break;
			case 80+60:  pieces[2]->state = MC_CHARGE_FIRE; break;
			case 80+90:  pieces[3]->state = MC_CHARGE_FIRE; break;
			case 80+120: pieces[4]->state = MC_CHARGE_FIRE; break;
		}
		
		// move main core towards a spot in front of target
		o->xinertia += (o->x > (o->xmark + (160<<CSF))) ? -4 : 4;
		o->yinertia += (o->y > o->ymark - (o->Height() / 2)) ? -4 : 4;
	}
	
	// set up our shootable status--you never actually hit the core (CFRONT),
	// but if it's mouth is open, make us, the invisible controller object, shootable.
	if (pieces[CFRONT]->frame==2)
	{
		o->flags &= ~FLAG_SHOOTABLE;
		pieces[CFRONT]->flags |= FLAG_INVULNERABLE;
	}
	else
	{
		o->flags |= FLAG_SHOOTABLE;
		pieces[CFRONT]->flags &= ~FLAG_INVULNERABLE;
	}
	
	LIMITX(0x80);
	LIMITY(0x80);
}
Пример #11
0
void ai_red_demon(Object *o)
{
	switch(o->state)
	{
		case 0:
		{
			o->xmark = o->x;
			o->xinertia = 0;
			o->frame = 0;
			o->state = 1;
		}
		case 1:
		{
			ANIMATE(20, 0, 1);
			FACEPLAYER;
		}
		break;
		
		case 10:	// prepare to jump
		{
			o->flags |= FLAG_SHOOTABLE;
			o->state = 11;
			o->frame = 3;
			o->timer = 0;
		}
		case 11:
		{
			switch(++o->timer)
			{
				case 30:
				case 40:
				case 50:
				{
					o->frame = 4;
					EmFireAngledShot(o, OBJ_RED_DEMON_SHOT, 0, 0x800);
					sound(SND_EM_FIRE);
				}
				break;
				
				case 34:
				case 44:
				case 54:
				{
					o->frame = 3;
				}
				break;
				
				case 61:
				{
					o->state = 20;
					o->timer = 0;
					o->frame = 2;
				}
				break;
			}
		}
		break;
		
		case 20:	// pause before jump
		{
			if (++o->timer > 20)
			{
				o->state = 21;
				o->timer = 0;
				o->frame = 5;
				
				o->yinertia = -0x5ff;
				o->xinertia = (o->CenterX() < player->CenterX()) ? 0x100 : -0x100;
			}
		}
		break;
		
		case 21:	// in air
		{
			switch(++o->timer)
			{
				case 30:
				case 40:
				case 50:
				{
					o->frame = 6;
					EmFireAngledShot(o, OBJ_RED_DEMON_SHOT, 0, 0x800);
					sound(SND_EM_FIRE);
				}
				break;
				
				case 34:
				case 44:
				{
					o->frame = 5;
				}
				break;
				
				case 54:
				{
					o->frame = 7;
				}
				break;
			}
			
			if (o->blockd && o->yinertia >= 0)
			{
				quake(10);
				o->state = 22;
				o->timer = 0;
				o->frame = 2;
			}
		}
		break;
		
		case 22:	// landed
		{
			o->xinertia /= 2;
			
			if (++o->timer > 22)
			{
				o->state = 10;
			}
		}
		break;
		
		// defeated/turned to stone (set by script)
		case 50:
		{
			o->flags &= ~FLAG_SHOOTABLE;
			o->damage = 0;
			
			if (o->blockd)
			{
				o->state = 51;
				o->frame = 2;
				
				game.quaketime = 10;
				SmokeClouds(o, 12, 4, 4);
				o->SpawnXP(19);
				
				sound(SND_BIG_CRASH);
				
				// needed to prevent status bars from not disappearing
				game.bossbar.object = NULL;
			}
		}
		break;
		
		case 51:
		{
			o->xinertia *= 7;
			o->xinertia /= 8;
			
			o->frame = 8;
		}
		break;
	}
	
	if (o->state < 50)
	{
		FACEPLAYER;
	}
	
	o->yinertia += 0x20;
	LIMITY(0x5ff);
}
Пример #12
0
// Kulala
void ai_giant_jelly(Object *o)
{
	switch(o->state)
	{
		case 0:		// frozen/in stasis. waiting for player to shoot.
			o->frame = 4;
			if (o->shaketime)
			{
				quake(30);
				o->state = 10;
				o->frame = 0;
				o->timer = 0;
			}
		break;
		
		case 10:	// falling
		{
			o->flags |= FLAG_SHOOTABLE;
			o->flags &= ~FLAG_INVULNERABLE;
			
			if (++o->timer > 40)
			{
				o->timer = 0;
				o->animtimer = 0;
				o->state = 11;
			}
		}
		break;
		
		case 11:	// animate thrust
		{
			ANIMATE_FWD(5);
			if (o->frame >= 3)
			{
				o->frame = 3;
				o->state = 12;
			}
		}
		break;
		
		case 12:	// thrusting upwards
		{
			o->yinertia = -0x155;
			if (++o->timer > 20)
			{
				o->state = 10;
				o->frame = 0;
				o->timer = 0;
			}
		}
		break;
		
		case 20:	// shot/freeze over/go invulnerable
		{
			o->frame = 4;
			o->xinertia >>= 1;
			o->yinertia += 0x20;
			
			if (!o->shaketime)
			{
				o->state = 10;
				o->frame = 0;
				o->timer = 30;
			}
		}
		break;
	}
	
	if (o->shaketime)
	{
		if (++o->timer3 > 12)
		{
			o->state = 20;
			o->frame = 4;
			o->flags &= ~FLAG_SHOOTABLE;
			o->flags |= FLAG_INVULNERABLE;
		}
	}
	else
	{
		o->timer3 = 0;
	}
	
	if (o->state >= 10)
	{
		if (o->blockl) { o->timer2 = 50; o->dir = RIGHT; }
		if (o->blockr) { o->timer2 = 50; o->dir = LEFT; }
		
		if (o->timer2 > 0)
		{
			o->timer2--;
			XACCEL(0x80);
		}
		else
		{
			o->timer2 = 50;
			FACEPLAYER;
		}
		
		o->yinertia += 0x10;
		if (o->blockd) o->yinertia = -0x300;
	}
	
	LIMITX(0x100);
	LIMITY(0x300);
}
Пример #13
0
void ai_balrog_boss_running(Object *o)
{
	// try to catch player
	if (o->state == STATE_CHARGE+1 || o->state == STATE_JUMP)
	{
		if ((pdistlx(12 << CSF) && pdistly(8 << CSF)) && o->timer > 8)
		{
			hurtplayer(2);
			balrog_grab_player(o);
			o->state = STATE_CAUGHT_PLAYER;
		}
	}
	
	switch(o->state)
	{
		case 0:
		{
			FACEPLAYER;
			o->flags |= FLAG_SHOOTABLE;
			o->frame = 0;
			o->state = 1;
		}
		case 1:
		{
			if (++o->timer > 30)
			{
				o->state = STATE_CHARGE;
				o->timer2++;
			}
		}
		break;
		
		// running towards player
		case STATE_CHARGE:
		{
			o->state++;
			o->timer = 0;
			o->frame = 9;
			o->animtimer = 0;
		}
		case STATE_CHARGE+1:
		{
			XACCEL(0x10);
			walking_animation(o);
			
			if (++o->timer > 75 || \
				(o->dir == LEFT && o->blockl) ||
				(o->dir == RIGHT && o->blockr))
			{
				o->frame = 0;
				o->state = STATE_SLOW_DOWN;
				break;
			}
			
			// can jump every 3rd time, but if he catches the player
			// before he gets a chance to he does NOT jump on the next charge.
			if ((o->timer2 & 3) == 0)
			{
				if (o->timer > 25)
				{	// initiate jump
					o->frame = 3;
					o->yinertia = -0x400;
					o->state = STATE_JUMP;
				}
			}
		}
		break;
		
		// jumping
		case STATE_JUMP:
		{
			if (o->blockd && o->yinertia >= 0)
			{
				o->frame = 2;		// <-- Landed frame.
				quake(30);
				o->state = STATE_SLOW_DOWN;
			}
		}
		break;
		
		// slowing down after charging or jumping
		case STATE_SLOW_DOWN:
		{
			o->xinertia *= 4;
			o->xinertia /= 5;
			
			if (o->xinertia == 0)
				o->state = 0;
		}
		break;
		
		// caught player
		case STATE_CAUGHT_PLAYER:
		{
			if (balrog_toss_player_away(o))
				o->state = 0;
		}
		break;
	}
	
	o->yinertia += 0x20;
	LIMITX(0x400);
	LIMITY(0x5FF);
}