Jump to content

Photo

[Request] A better Aquamentus

Enemy Boss Aquamentus Request

  • Please log in to reply
17 replies to this topic

#1 FireSeraphim

FireSeraphim

    Behold the might of legend!

  • Members
  • Real Name:Patrick Casey Spurlock

Posted 08 February 2016 - 10:16 AM

I find myself in need of a better Aquamentus for Level 1 of Hyrule Fantasy and I even have some very specific ideas in mind, something more among the lines of a hybrid between the BS Zelda Level 7 Aquamentus and the Oracle of Seasons Aquamentus in terms of AI with some of my own touches, allow me to get specific for this request:

 

  • Like in OOS he should always be walking towards you in all eight directions (despite not having eight directional frames) but should back off occasionally if he gets too close to a wall
  • I'm content to use the default Aquamentus sprites (I'm using the Revenge 2 tileset as you can see in the screenshots for Hyrule Fantasy), no need for fancy ripping or anything.
  • also like in OOS he should occasionally ram you, with the added caveat that he should simply jump back rather than fully flying back, since the sprites I'm using don't have fancy flying frames
  • at 50% percent of it's HP, it should swap to CSET 10, and change from firing three fireballs at you to four fireballs and ram you slightly more often
  • at 25% percent of it's HP, it should swap to CSET 8 and ram you more often.

Since this is a common problem of sorts, I am very much open to allowing the resulting script to be shared and used in other quests to remedy the ol' "Boring Aquamentus" Syndrome.


Edited by FireSeraphim, 08 February 2016 - 12:43 PM.


#2 Mani Kanina

Mani Kanina

    Rabbits!

  • Members

Posted 08 February 2016 - 10:20 AM

not really script related, but this sounds like a very hard boss for level 1.

#3 Bill Nye the Russian Spy

Bill Nye the Russian Spy

    БИЛЛ БИЛЛ БИЛЛ БИЛЛ

  • Members
  • Real Name:Guess.
  • Location:Not your first guess

Posted 08 February 2016 - 11:02 AM

not really script related, but this sounds like a very hard boss for level 1.

Not really, assuming it's possible to get the L2 Shield before this. (If not, though, you have a point.)

#4 FireSeraphim

FireSeraphim

    Behold the might of legend!

  • Members
  • Real Name:Patrick Casey Spurlock

Posted 08 February 2016 - 11:36 AM

Well I had to shake it up somehow. My quest has eight directional movement enabled for link, so technically this variant of the Aquamentus shouldn't give anyone too much grief, especially in light of Link's increased mobility due to the aforementioned quest rule. And yeah I intend to have one of the shops in the area near level 1 sell magic shields.


Edited by FireSeraphim, 08 February 2016 - 11:40 AM.


#5 Mani Kanina

Mani Kanina

    Rabbits!

  • Members

Posted 08 February 2016 - 12:02 PM

In general, you can't assume that the player has an optional item though, you'd have to either balance by the player not having the magic shield, or provide one inside the dungeon.

Also, Link has eight directional movement in the GBC games, and you're basically proposing the level 1 boss from Seasons, but on crack. (Given that it rages and gets more aggressive twice.)

Given the fact that the boss rages twice and the low HP pools the player will have early game, it's not out of the blue to argue that many players will be caught off-guard and, potentially, get killed. A 7 fire ball pattern can also be very hard to avoid, so I think players are very likely to just tank it instead, (which is in general not something desirable). I mean, if you're trying to improve on zelda 1 and be more like alttp, then having reasonable difficulty and difficulty curves should be one of the goals. Think of the first boss in alttp, not only did it move in a very predictable pattern, but Link would come in, with a minimum of 4 hearts. (This can be reasonably assumed since the chest in the sanctuary is pretty hard to miss unless you do it on purpose.) And at a maximum of five hearts, two bottles (with potions of fairies), and the ice rod. It's also fairly reasonable to assume that the average player would probably have one bottle and the bug catching net, given how much the player is thrown around exploring the village the first time through the game, (And they don't know where they have to go).


The first boss in alttp is not hard, it's very easy to mechanically grasp how it operates, and you'll probably have the resources to hang on through the fight for quite some time, even if you're not good at fighting back. But, here's the important part, it feels like a tough fight the first time through it.

You should strive to make bosses that are interesting to fight, but you should probably plan your difficulty curve in such a way that the intended skill level of the players are unlikely to die on the first couple of bosses.


Also, on a personal note, I'd say have only on level of rage and have it trigger at 50% of the life, and have that for all the bosses. It would be a very neat twist, and it'd make for some cool fights. Especially given how easy and uninteresting all boss fights were in zelda 1.

Edited by Lunaria, 08 February 2016 - 12:03 PM.


#6 FireSeraphim

FireSeraphim

    Behold the might of legend!

  • Members
  • Real Name:Patrick Casey Spurlock

Posted 08 February 2016 - 12:41 PM

Fair enough, I'll be editing the OP now.

Edit: The Aquamentus proposal has been nerfed.


Edited by FireSeraphim, 08 February 2016 - 12:44 PM.


#7 Bill Nye the Russian Spy

Bill Nye the Russian Spy

    БИЛЛ БИЛЛ БИЛЛ БИЛЛ

  • Members
  • Real Name:Guess.
  • Location:Not your first guess

Posted 08 February 2016 - 01:26 PM

Honestly, the 7-fireball thing was my only concern about the difficulty, hence the mention of the shield.

Also, if you find Aquamentus trivial without tanking or the L2 Shield in the original Zelda 1 (or full health), then I must really suck at dodging fireballs with 4-directional movement. But I already knew that. :P

On topic: I personally don't think you had to remove the stomping, but other than that, this looks more reasonable by the "no optional upgrades" standard.

Edited by Bill Nye the Russian Spy, 08 February 2016 - 01:32 PM.


#8 Lejes

Lejes

    Seeker of Runes

  • Members
  • Location:Flying High Above Monsteropolis

Posted 13 February 2016 - 02:48 AM

const int TILE_AQUA1 = 7530; // The first tile of Aquamentus's default animation.
const int TILE_AQUA2 = 7570; // The first tile of Aquamentus's attacking animation.
const int SFX_AQUA_WALLCRASH = 3;
const int SFX_AQUA_STOMP = 3;

ffc script BetterAquamentus
{
	void run(int enemyID)
	{
		npc ghost = Ghost_InitAutoGhost(this, enemyID);
		Ghost_SetFlag(GHF_IGNORE_ALL_TERRAIN);
		int maxHP = Ghost_HP;
		int timer_attack = 180;
		bool y_greater;
		bool y_done;
		bool turn_cset10;
		bool turn_cset8;
		float angle;
		int choice;
		
		ghost->Extend = 3;
		Ghost_TileWidth = 2;
		Ghost_TileHeight = 2;
		int aspeed = ghost->ASpeed;
		int original_x = Ghost_X;
		int original_y = Ghost_Y;
		ghost->ASpeed = 0;
		Trace(aspeed);
		
		Ghost_Waitframes(this, ghost, true, true, 30);
		ghost->OriginalTile = TILE_AQUA2;
		Game->PlaySound(SFX_ROAR);
		Ghost_Waitframes(this, ghost, true, true, 60);
		ghost->OriginalTile = TILE_AQUA1;
		ghost->ASpeed = aspeed;
		
		while (true)
		{
			if (Abs((Ghost_X + (0.5 * ghost->HitWidth)) - (Link->X + 8)) > 12 && Abs((Ghost_Y + (0.5 * ghost->HitHeight)) - (Link->Y + 8)) > 12);
			{
				Ghost_MoveTowardLink(ghost->Step * 0.01, 2);
			}
			if (Ghost_X <= 32 || Ghost_Y <= 16 || Ghost_Y + 32 >= 144)
			{
				angle = Angle(Ghost_X, Ghost_Y, original_x, original_y);
				y_greater = false;
				if (Ghost_Y >= original_y)
				{
					y_greater = true;
				}
				y_done = false;
				while (Ghost_X < original_x || !y_done)
				{
					Ghost_MoveAtAngle(angle, ghost->Step * 0.04, 2);
					if (y_greater && Ghost_Y <= original_y)
					{
						y_done = true;
					}
					else if (!y_greater && Ghost_Y >= original_y)
					{
						y_done = true;
					}
					BetterAqua_Ghost_Waitframe(this, ghost);
				}
			}
			if (timer_attack <= 0)
			{
				if (Ghost_HP > maxHP * 0.5)
				{
					choice = Rand(6);
					if (choice == 5)
					{
						Aqua_RamAttack(this, ghost, original_x, original_y, aspeed);
					}
					else
					{
						Aqua_FireballAttack(this, ghost, maxHP);
					}
				}
				else if (Ghost_HP <= maxHP * 0.5 && Ghost_HP > maxHP * 0.25)
				{
					choice = Rand(6);
					if (choice == 5 || choice == 4)
					{
						Aqua_RamAttack(this, ghost, original_x, original_y, aspeed);
					}
					else
					{
						Aqua_FireballAttack(this, ghost, maxHP);
					}
				}
				else if (Ghost_HP <= maxHP * 0.25)
				{
					choice = Rand(6);
					if (choice == 5 || choice == 4 || choice == 3)
					{
						Aqua_RamAttack(this, ghost, original_x, original_y, aspeed);
					}
					else
					{
						Aqua_FireballAttack(this, ghost, maxHP);
					}
				}
				timer_attack = Rand(120, 240);
			}
			
			if (Ghost_HP <= maxHP * 0.5 && !turn_cset10)
			{
				Ghost_CSet = 10;
				turn_cset10 = true;
			}
			if (Ghost_HP <= maxHP * 0.25 && !turn_cset8)
			{
				Ghost_CSet = 8;
				turn_cset8 = true;
			}
			if (timer_attack > 0)
			{
				timer_attack--;
			}
			BetterAqua_Ghost_Waitframe(this, ghost);
		}
	}
}

void Aqua_FireballAttack(ffc this, npc ghost, int maxHP)
{
	float angle = Angle(Ghost_X, Ghost_Y, Link->X, Link->Y);
	float initangle = angle - 30;
	int numshots = 3;
	
	ghost->OriginalTile = TILE_AQUA2;
	for (int i = 0; i < 30; i++)
	{
		BetterAqua_Ghost_Waitframe(this, ghost);
	}
	
	if (Ghost_HP <= maxHP * 0.5)
	{
		initangle = Choose(angle - 30, angle - 60);
		numshots = 4;
	}
	for (int i = 0; i < numshots; i++)
	{
		FireEWeapon(EW_FIREBALL, Ghost_X, Ghost_Y, DegtoRad(initangle + (30 * i)), 150, ghost->WeaponDamage, -1, -1, 0);
	}
	for (int i = 0; i < 10; i++)
	{
		BetterAqua_Ghost_Waitframe(this, ghost);
	}
	ghost->OriginalTile = TILE_AQUA1;
}

void Aqua_RamAttack(ffc this, npc ghost, int original_x, int original_y, int aspeed)
{
	int time;
	float angle;
	int timer_collide;
	int storedDefense[18];
	
	float jump = 4;
	int t;
	int t0;
	int t1;
	int t2;
	float v;
	
	ghost->ASpeed = aspeed * 0.5;
	Ghost_StoreDefenses(ghost, storedDefense);
	Ghost_SetAllDefenses(ghost, NPCDT_BLOCK);
	for (int i = 0; i < 45; i++)
	{
		BetterAqua_Ghost_Waitframe(this, ghost);
	}
	angle = Angle(Ghost_X + (0.5 * ghost->HitWidth), Ghost_Y + (0.5 * ghost->HitHeight), Link->X + 8, Link->Y + 8);
	//while (Ghost_CanMove(DIR_UP, ghost->Step * 0.08, 64) && Ghost_CanMove(DIR_DOWN, ghost->Step * 0.08, 64) && Ghost_CanMove(DIR_LEFT, ghost->Step * 0.08, 64))
	while (!Screen->isSolid(Ghost_X + 16, Ghost_Y + 11) && !Screen->isSolid(Ghost_X + 3, Ghost_Y + 16) && !Screen->isSolid(Ghost_X + 29, Ghost_Y + 16) && !Screen->isSolid(Ghost_X + 16, Ghost_Y + 29))
	{
		Ghost_MoveAtAngle(angle, ghost->Step * 0.08, 4);
		timer_collide++;
		BetterAqua_Ghost_Waitframe(this, ghost);
	}
	Game->PlaySound(SFX_AQUA_WALLCRASH);
	angle = Angle(Ghost_X, Ghost_Y, original_x, original_y);
	t0 = (2 * jump) / GRAVITY;
	t1 = (jump + TERMINAL_VELOCITY) / GRAVITY;
	t2 = ((-0.5 * GRAVITY * Pow(t1, 2)) + (jump * t1)) / TERMINAL_VELOCITY;
	if (t0 > t1)
	{
		t = t1 + t2;
	}
	else
	{
		t = t0;
	}
	v = Distance(Ghost_X, Ghost_Y, original_x, original_y) / t;
	ghost->ASpeed = aspeed;
	Ghost_Jump = jump;
	for (int i = 0; i < t; i++)
	{
		Ghost_MoveAtAngle(angle, v, 2);
		BetterAqua_Ghost_Waitframe(this, ghost);
	}
	Ghost_SetDefenses(ghost, storedDefense);
}

void BetterAqua_Ghost_Waitframe(ffc this, npc ghost)
{
	if (ghost->Misc[0] % ghost->ASpeed == 0)
	{
		Game->PlaySound(SFX_AQUA_STOMP);
	}
	ghost->Misc[0]++;
	
	Ghost_Waitframe(this, ghost, 1, true);
}

 
I wasn't entirely sure what "should back off occasionally if he gets too close to a wall" meant, and I didn't feel like playing Oracle of Seasons intro and dungeon 1 to find out. You have two tiles and two sound effects to set as constants. SFX_AQUA_STOMP can be 0 if you don't want it to make stomping noises. It needs to use built in animation, also, because the script assumes built in animation. It also uses the enemy's Step and Weapon Damage stats, so don't leave those at 0.


Edited by Lejes, 13 February 2016 - 02:50 AM.


#9 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 13 February 2016 - 04:19 AM

Something to note, the reason that I do this:

ffc script ff{
    void run(int sfx){
        if ( sfx ) Game->PlaySound(sfx); 
    }
}

In 2.50.0, and probably 2.50.1, the 0-Sound produced a soft click, not silence. I don't recall if this was fixed for 2.50.2, but this way, with a statement before the PlaySound() instruction, you never play sound 0.

 

I do the same for Screen->Message() instructions, and for many values that rely on an argument set by the user.

 

 

 

[...]

 
I wasn't entirely sure what "should back off occasionally if he gets too close to a wall" meant, [...]

 

 

He wants the Aquamentus to move a few paces forward, if it is cornered against the wall, like a small charge.

 


Edited by ZoriaRPG, 13 February 2016 - 04:30 AM.


#10 FireSeraphim

FireSeraphim

    Behold the might of legend!

  • Members
  • Real Name:Patrick Casey Spurlock

Posted 13 February 2016 - 07:08 AM

He wants the Aquamentus to move a few paces forward, if it is cornered against the wall, like a small charge.

 yeah, something like that.

 

@Lejes: Thank you again, but you didn't leave any setup instructions, is this script meant to be directly applied to the pre-existing aquamentuses or is it meant be used via a custom enemy with the misc. animation attribute?


Edited by FireSeraphim, 13 February 2016 - 07:12 AM.


#11 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 13 February 2016 - 12:17 PM

 yeah, something like that.

 

@Lejes: Thank you again, but you didn't leave any setup instructions, is this script meant to be directly applied to the pre-existing aquamentuses or is it meant be used via a custom enemy with the misc. animation attribute?

 

Normally, scripts with heavy custom movement are applied to the enemy type 'other', as the custom movement would otherwise conflict with the normal enemy. Try setting it to a normal aquamentus though, first, and see if the overrides work. I don't see anything that sets an initial tile, so it probably is supposed to be on the Aquamentus enemy type. The tile is only changed during the special Waitframe instructions here.

 

You may need to change the constants for TILE_AQUA* to whatever tiles you are using for the enemy in your tileset. That is, what would correspond to the normal Aquamentus enemy. If you encounter any glitches, compare them to what tiles are used in Classic, and EZGB. They should match an Aquamentus in one of the two, if not both. Then, if they don't match the tiles in the tileset you are using, work out what tiles they'd be using the tiles from Classic/EZGB, find the similar tiles in your tileset, and adjust the assigned values of those two constants.

 

Last, there is a Trace() instructions that you should comment out, unless you want the aspeed variable to write to allegro.log whenever you load this enemy. Lejes much have forgotten to remove it.

 

I might want to borrow some of this, if you don't mind. Not the shot patterns though, just the movement patterns, so if I implement it, it won;t be a complete clone.



#12 FireSeraphim

FireSeraphim

    Behold the might of legend!

  • Members
  • Real Name:Patrick Casey Spurlock

Posted 13 February 2016 - 02:52 PM

Still waiting for the setup instructions from Lejes.



#13 Lejes

Lejes

    Seeker of Runes

  • Members
  • Location:Flying High Above Monsteropolis

Posted 13 February 2016 - 03:25 PM

No, this isn't meant to go over a default Aquamentus. Trying to alter default bosses is a fool's game. To set it up, copy this into your main script file, change the constants to match your tileset, and import it like you would any other FFC script. In the enemy editor, set the enemy's tile to Aquamentus's face. You can use an extra sprite palette if you want, the script shouldn't interfere with that. The enemy will be set to 2 by 2 automatically, so keep that in mind when you lay out the tiles for the animation. Enemy should be Other type, "W.Damage" and "Step Speed" should be set to whatever numbers you want (I recommend 2 and 25 respectively, but you can use whatever). "F.Rate" should be set to a non-zero number if you want it to animate correctly, just like any other enemy. "E.Anim" should be set to...whatever. I used basic "2-Frame" animation when testing. The script does not set Aquamentus's direction property at all, so the directional animations probably won't work. The enemy name should be "whatever@BetterAquamentus". The "whatever" part can be anything you like, but "@BetterAquamentus" needs to be that exactly. On the Data 2 tab, Misc. Attr. 11 and 12 should both be -1. There's also a global component to ghosted enemy scripts, but I think you're already using some, so that should already be set up.

 

And yes, the Trace(aspeed) shouldn't be there. That line can be deleted. About the retreating from the wall behavior. Currently I have it set to go all the way back to its original position when it gets close to a wall, but that can be changed to walk only a few steps back fairly easily.



#14 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 13 February 2016 - 04:04 PM

No, this isn't meant to go over a default Aquamentus. Trying to alter default bosses is a fool's game. To set it up, copy this into your main script file, change the constants to match your tileset,[...]


That's what I originally thought, mostly because of the npc->Extend settings. What is throwing me off here, is that the initial tile isn't set until the Waitframe(). Is there a reason for that, or did I miss it?

He's also going to need an explanation on what tile values he should assign to the constants. I can't even deduce that, merely at a glance. Particularly as I don't know the reasoning behind using two, and calling tile2 first.

 

As an anecdote, I've been able to add complex movement to a gleeok with autoghost, using its normal enemy type, so this does work, but that's likely because gleeok enemies don't move by default. It does require some tricky things, though.


Edited by ZoriaRPG, 13 February 2016 - 04:08 PM.


#15 Lejes

Lejes

    Seeker of Runes

  • Members
  • Location:Flying High Above Monsteropolis

Posted 13 February 2016 - 04:30 PM

The initial tile is set in the enemy editor. The reason it changes to TILE_AQUA2 early on is for the roar animation. It stays with TILE_AQUA1 most of the time after that, except when it's using the fireball attack. You're free to borrow this if you want, Zoria.





Also tagged with one or more of these keywords: Enemy, Boss, Aquamentus, Request

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users