Some functions for ghosted enemy scripts. Requires at least RC2.
ghost.zh and demo
AutoGhost setup demo video
In the demo quest, the cheat codes are just 1, 2, 3, and 4.
Any bugs or suggestions, let me know.
Printable View
Some functions for ghosted enemy scripts. Requires at least RC2.
ghost.zh and demo
AutoGhost setup demo video
In the demo quest, the cheat codes are just 1, 2, 3, and 4.
Any bugs or suggestions, let me know.
Oooh, very neat. This should make boss writing slightly easier!
Edit: Hmm, if you do this:
The explosion on death travels slowly to the left... No idea why, since it clearly sets the explosion to step 0!Code:eweapon bomb = FireAimedEWeapon(EW_BOMB, this->X + 8, this->Y + 8, 0, 100, 4, false, 7, false, 38);
SetEWeaponMovement(bomb, EWM_THROW, 5);
SetEWeaponDeathEffect(bomb, EWD_EXPLODE, 4);
bomb->CollDetection = false; //so that the bomb itself doesn't hit Link
I think that one's a bug in ZC. Seems to do it no matter what.
Updated for builds 1174 and up. Handles stunning better now, and a couple of small bugs are fixed.
Another update. Added a new init method, improvements to stunning and thrown eweapons.
Does anyone besides me actually use this, or have any interest in doing so? I could write up a tutorial, if it'd be helpful.
(Edit: Small update: fixed a minor issue in the demo and a significant mistake in the documentation.)
(Another edit: Gah, I broke knockback last time. Fixed now. Added a couple more functions while I was at it.)
This header seems pretty useful..
Here's something I made with it
And the up/down one for good measure.Code://This script creates an enemy that walks left to right between solid combos,
//similar to a Goomba from Super Mario Bros.
//Set D0 to 0 to start right and 1 to start left.
//D1 = Enemy type to use for ghosting
ffc script goomba_left_right
{
void run(bool startleft, int enemtype)
{
npc goomba;
int timer;
bool left;
bool right;
if(startleft){left = true; right = false;} else {right = true; left = false;}
goomba=GhostInitCreate(this, enemtype);
SetFlags(this, GHF_NORMAL);
timer = 2;
for(int i=0; true; i++)
{
if(i==timer)
{
i=0;
if(!right && left && CanMove(this,goomba,2,1,0)) Move(this,goomba,-1,0,0); else right = true;
if(right && CanMove(this,goomba,3,1,0))Move(this,goomba,1,0,0); else { left = true; right = false;}
}
GhostWaitframeF(this, goomba, true, true);
}
}
}
If you're going to use these, be sure to import "ghost.zh"!Code://This script creates an enemy that walks down and up between solid combos,
//Set D0 to 0 to start down and 1 to start up.
//D1 = Enemy type to use for ghosting
ffc script goomba_up_down
{
void run(bool startup, int enemtype)
{
npc goomba;
int timer;
bool up;
bool down;
if(startup){up = true; down = false;} else {down = true; up = false;}
goomba=GhostInitCreate(this, enemtype);
SetFlags(this, GHF_NORMAL);
timer = 2;
for(int i=0; true; i++)
{
if(i==timer)
{
i=0;
if(!down && up && CanMove(this,goomba,0,1,0)) Move(this,goomba,0,-1,0); else down = true;
if(down && CanMove(this,goomba,1,1,0))Move(this,goomba,0,1,0); else { up = true; down = false;}
}
GhostWaitframeF(this, goomba, true, true);
}
}
}
EDIT: I am a very poor scripter, so a tutorial on how to put together a custom boss with this would be very nice
First boss I made with it:
Code:// Zora King
// Will work best with a Freeform Combo set to Combo W: 32, Combo H: 32, Tile W: 2, Tile H: 2, with provided graphics.
// NOTE: Using a SkipX setting of 1 on the combo will allow
// you to animate using 2x2 tile chunks.
// D0 - The number pointing to another Freeform Combo
// Used to display the diving, as well as house more variables.
// (Check below for details)
// D1 - The total duration the zora will stay on the screen.
// D2 - Enemy Type to use for ghosting
// D3 - Total hit points
// D4 - Fire every this amount of frames when above water;
// D5 - Damage of projectile;
// D6 - Fireballs Per Shot (set to an odd number up to 9. Even numbers may function but are not recommended)
// D7 - Sprite to use for fireball
// As mentioned above, further customization is allowed by
// By using the D variables stored in the FFC called by D0
// D0 = Projectile movement pattern(0 for normal shot)
// D1 = Projectile movement argument (0 for none)
// See ghost.zh for its EWM constants for what to use as patterns
// See ghost_zh_readme.txt for what to use as argument.
// Use ZRB_CHECKPOINT Floats to specify in percentages when the Zora will be submerged, rise, appear, and sink.
const float ZRB_CHECKPOINT1 = 0.30;// Time From Phase 1 to 2. Decimal indicates a percentage.
const float ZRB_CHECKPOINT2 = 0.45;// Time From Phase 2 to 3. Decimal indicates a percentage.
const float ZRB_CHECKPOINT3 = 0.60;// Time from Phase 3 to 4. Decimal indicates a percentage.
const float ZRB_CHECKPOINT4 = 0.75;// Time from Phase 4 to end(staytime). Decimal indicates a percentage.
const int ZRB_PRJ_X_OFFSET = 8; // Offset of projectile from origin in X
const int ZRB_PRJ_Y_OFFSET = 16; // Offset of projectile from origin in Y
ffc script zora_king
{
//Running part of script
void run(int diveffc_s, int staytime, int enemtype, int enemhp, int fireat, int firedmg, int fireperarc, int firesprite)
{
npc zoraboss;
eweapon zora_fire;
//Loads an FFC
ffc diveffc = Screen->LoadFFC(diveffc_s);
int fire_mtype = diveffc->InitD[0];
int fire_arg = diveffc->InitD[1];
TraceNL();
Trace(fire_mtype);
TraceNL();
Trace(fire_arg);
int time_check = 0;
int water_check = 1;
int checkpoint_1 = (staytime*ZRB_CHECKPOINT1);
int checkpoint_2 = (staytime*ZRB_CHECKPOINT2);
int checkpoint_3 = (staytime*ZRB_CHECKPOINT3);
int checkpoint_4 = (staytime*ZRB_CHECKPOINT4);
int pop_up_x = 16 * Rand(15);
int pop_up_y = 16 * Rand(10);
zoraboss = GhostInitCreate(this,enemtype);
SetHP(this,zoraboss,enemhp);
while(true){
if(water_check == 1){pop_up_x = 16 * Rand(15);pop_up_y = 16 * Rand(10); water_check = 0;}
if(IsWater(ComboAt(pop_up_x,pop_up_y)) && IsWater(ComboAt(pop_up_x + 16,pop_up_y)) && IsWater(ComboAt(pop_up_x,pop_up_y + 16)) && IsWater(ComboAt(pop_up_x + 16,pop_up_y + 16))){} else {pop_up_x = 16 * Rand(15);pop_up_y = 16 * Rand(10);}
if(time_check < staytime) time_check++;
if(time_check <= checkpoint_1){
change_x_y_2(this, 256, 176, diveffc, 256, 176);
}
if(time_check > checkpoint_1 && time_check <= checkpoint_2){
change_x_y_2(this, 256, 176, diveffc, pop_up_x, pop_up_y);
}
if(time_check > checkpoint_2 && time_check <= checkpoint_3){
change_x_y_2(this, pop_up_x, pop_up_y, diveffc, 256, 176);
if(time_check % fireat == 0){
if (fireperarc >= 1){
zora_fire=FireAimedEWeapon(129, this->X + ZRB_PRJ_X_OFFSET, this->Y + ZRB_PRJ_Y_OFFSET, 0, 200, firedmg,false, firesprite, false, 0);
if(fire_mtype > 0) SetEWeaponMovement(zora_fire, fire_mtype, fire_arg);
}
if (fireperarc >= 3){
zora_fire=FireAimedEWeapon(129, this->X + ZRB_PRJ_X_OFFSET, this->Y + ZRB_PRJ_Y_OFFSET, -0.1, 200, firedmg,false, firesprite, false, 0);
if(fire_mtype > 0)SetEWeaponMovement(zora_fire, fire_mtype, fire_arg);
zora_fire=FireAimedEWeapon(129, this->X + ZRB_PRJ_X_OFFSET, this->Y + ZRB_PRJ_Y_OFFSET, 0.1, 200, firedmg,false, firesprite, false, 0);
if(fire_mtype > 0)SetEWeaponMovement(zora_fire, fire_mtype, fire_arg);
}
if (fireperarc >= 5){
zora_fire=FireAimedEWeapon(129, this->X + ZRB_PRJ_X_OFFSET, this->Y + ZRB_PRJ_Y_OFFSET, -0.2, 200, firedmg,false, firesprite, false, 0);
if(fire_mtype > 0)SetEWeaponMovement(zora_fire, fire_mtype, fire_arg);
zora_fire=FireAimedEWeapon(129, this->X + ZRB_PRJ_X_OFFSET, this->Y + ZRB_PRJ_Y_OFFSET, 0.2, 200, firedmg,false, firesprite, false, 0);
if(fire_mtype > 0)SetEWeaponMovement(zora_fire, fire_mtype, fire_arg);
}
if (fireperarc >= 7){
zora_fire=FireAimedEWeapon(129, this->X + ZRB_PRJ_X_OFFSET, this->Y + ZRB_PRJ_Y_OFFSET, -0.3, 200, firedmg,false, firesprite, false, 0);
if(fire_mtype > 0)SetEWeaponMovement(zora_fire, fire_mtype, fire_arg);
zora_fire=FireAimedEWeapon(129, this->X + ZRB_PRJ_X_OFFSET, this->Y + ZRB_PRJ_Y_OFFSET, 0.3, 200, firedmg,false, firesprite, false, 0);
if(fire_mtype > 0)SetEWeaponMovement(zora_fire, fire_mtype, fire_arg);
}
if (fireperarc >= 9){
zora_fire=FireAimedEWeapon(129, this->X + ZRB_PRJ_X_OFFSET, this->Y + ZRB_PRJ_Y_OFFSET, -0.4, 200, firedmg,false, firesprite, false, 0);
if(fire_mtype > 0)SetEWeaponMovement(zora_fire, fire_mtype, fire_arg);
zora_fire=FireAimedEWeapon(129, this->X + ZRB_PRJ_X_OFFSET, this->Y + ZRB_PRJ_Y_OFFSET, 0.4, 200, firedmg,false, firesprite, false, 0);
if(fire_mtype > 0)SetEWeaponMovement(zora_fire, fire_mtype, fire_arg);
}
if (fireperarc >= 11){
zora_fire=FireAimedEWeapon(129, this->X + ZRB_PRJ_X_OFFSET, this->Y + ZRB_PRJ_Y_OFFSET, -0.5, 200, firedmg,false, firesprite, false, 0);
if(fire_mtype > 0)SetEWeaponMovement(zora_fire, fire_mtype, fire_arg);
zora_fire=FireAimedEWeapon(129, this->X + ZRB_PRJ_X_OFFSET, this->Y + ZRB_PRJ_Y_OFFSET, 0.5, 200, firedmg,false, firesprite, false, 0);
if(fire_mtype > 0)SetEWeaponMovement(zora_fire, fire_mtype, fire_arg);
}
}
}
if(time_check > checkpoint_3 && time_check <= checkpoint_4){
change_x_y_2(this, pop_up_x, pop_up_y, diveffc, 256, 176);
}
if(time_check > checkpoint_4 && time_check <= staytime){
change_x_y_2(this, 256, 176, diveffc, pop_up_x, pop_up_y);
}
if(time_check >= staytime){water_check = 1; time_check = 0;}
GhostWaitframeF(this, zoraboss, true, true);
}
}
void change_x_y_2(ffc f1, int x1, int y1, ffc f2, int x2, int y2)
{
f1->X = x1;
f1->Y = y1;
f2->X = x2;
f2->Y = y2;
}
}
Updated. It needs build 1326+ now, so it's only for Linux and the debug build at the moment.
- Fixed a couple of small bugs.
- Enemies can now have combos set automatically based on direction.
- Added a function to adjust the enemy's hitbox.
- Eweapons can now flicker and cast shadows, and there's now a falling movement type.
- Added functions to create eweapons larger than 1x1.
- The SetFlags() and all three FireEWeapon functions have changed; the old versions are deprecated.
I've also added on to the demo. Do try it, I'm rather proud of it. If you can't/won't play it, I also made a video of it.
Also, bigjoe, you'd suggested adding functions for lweapons and items? Not a bad idea, but I think that'd be a different file. I probably will do that in the future.
So, I kind of screwed up the last update. The link was pointing to an older version. Sorry. Fixed now.
I have a suggestion for a function:
GhostWaitframeR
This would be the same as GhostWaitframeN but it would allow you to specify a location relative to the NPC. (Negative or positive integers.)
The reason I suggest this is that when I try to do that manually using GhostWaitframeM, both npc and ffc fly diagonally until they disappear. I guess its because they both have to be the same in the function.