import "std.zh"
const int SHADOW = 1300; //combo ID on the tiles page
const int SHADOW_CSET = -1; //set shadow cset globally if >= 0 else use d0
const int SFX_JMP = 61; //SFX_JUMP is taken
const int SFX_LAND = 0;
//jump settings
const int JUMP_FORCE = 4; //any lower and you may or may-not break the script
const int MAX_HEIGHT = 32;
const int SENSITIVITY = 32; //how close stalfos get before they jump
const int JUMP_BACK = 1; //distance that stalfos jump away per-frame
//jump states
const int ON_GROUND = 0;
const int GOING_UP = 1;
const int GOING_DOWN = 2;
//npc->Misc indexes
const int JUMP_DIR = 0;
const int JUMP_STATE = 1;
ffc script Stalfos_Jump{
void run(int cset){
int shadow_cset;
if(SHADOW_CSET >= 0)
shadow_cset = SHADOW_CSET;
else
shadow_cset = cset;
npc e;
while(1){
Waitframe();
for(int i = Screen->NumNPCs(); i > 0 ; i--) {
e = Screen->LoadNPC(i);
if(e->ID != NPC_STALFOS1 && e->ID != NPC_STALFOS2 && e->ID != NPC_STALFOS3)
continue;
if(Link->InputA == true //assumes that the A button is always the sword
&& e->Misc[JUMP_STATE] == ON_GROUND
&& Distance(Link->X, Link->Y, e->X, e->Y) <= SENSITIVITY
&& !e->Stun){
e->Misc[JUMP_STATE] = GOING_UP;
e->Misc[JUMP_DIR] = get_jump_direction(e);
Game->PlaySound(SFX_JMP);
}
if(e->Misc[JUMP_STATE] != ON_GROUND)
do_jump(e, shadow_cset);
}
}
}
}
int get_jump_direction(npc e){
if(Link->Dir == DIR_UP || Link->Dir == DIR_DOWN){
if(e->Y < Link->Y) return DIR_UP;
return DIR_DOWN;
}else{
if(e->X < Link->X) return DIR_LEFT;
else return DIR_RIGHT;
}
return DIR_UP; //should never get here
}
void do_jump(npc e, int shadow_cset){
int dir = e->Misc[JUMP_DIR];
Screen->FastTile(0, e->X, e->Y, SHADOW, shadow_cset, 255);
if(CanWalk(e->X, e->Y, dir, 1, 0)){ //might not check water, maybe fix that later if needed
if(dir == DIR_LEFT) e->X -= (JUMP_BACK + 1); //stalfos are more stubbron to push left and up for some reason
else if(dir == DIR_RIGHT) e->X += JUMP_BACK;
else if(dir == DIR_DOWN) e->Y += JUMP_BACK;
else e->Y -= (JUMP_BACK + 1);
}
if(e->Misc[JUMP_STATE] == GOING_UP){
e->Z += JUMP_FORCE;
if(e->Z >= MAX_HEIGHT) e->Misc[JUMP_STATE] = GOING_DOWN;
}else{
//no need to subtract from Z, the game enforces gravity on top-down view
if(e->Z <= 0) e->Misc[JUMP_STATE] = ON_GROUND;
Game->PlaySound(SFX_LAND);
}
}
This script modifies the level 1, 2, and 3 Stalfoses and assumes that the sword is always equipped to the A button.
Set the shadow ID and the sounds with the constants.
The shadow cset can be set using D0 or globally using the constant.
Enemies won't jump while stunned.
Bugs:
-Doesn't check if Link can actually use a sword (eg cursed).
-Stalfoses can jump onto walkable water and pits.
You can check for NumLWeaponsOf(LW_SWORD) instead of button presses, as well as proximity* to Link. This would make whether the sword is on A, or B irrelevent, and would also ensure that jinxes work. Likewise, checking proximity will prevent them all jumping for no reason. I'd also check that Link is facing the enemy, before making it jump.
This would work better as a global component, rather than an ffc. I'd say that the bugs preclude its use, but it's fixable.
Either that, or an AutoGhost script, so that it's tied specifically to a desired npc, rather than affecting all Stalfos in a game.
Last, reading the tile on which the enemy would land (you already know the coordinates) to see if it's a bad type, and cancelling the jump if it is, would solve the other major bugs.
*I made functions specifically to do this, ProximityX() and ProximityY(), if you want them.
---
The reason that I advise posting the script request in the 'Script Requests' board, is that some of us don't read this board frequently, but we check the scripting boards daily. It increases your chances of fast results; but don't expect anything 'fast' from me. I have enough backlog for two years.
import "std.zh"
const int SHADOW = 1300; //combo ID on the tiles page
const int SHADOW_CSET = 7;
const int SFX_JMP = 61;
const int SFX_LAND = 0;
//jump settings
const int JUMP_FORCE = 4; //any lower and you may or may-not break the script
const int MAX_HEIGHT = 32;
const int PROXIMITY = 32; //how close stalfos get before they jump
const int JUMP_BACK = 1; //distance that stalfos jump away per-frame
//jump states
const int ON_GROUND = 0;
const int GOING_UP = 1;
const int GOING_DOWN = 2;
//npc->Misc indexes
const int JUMP_DIR = 0;
const int JUMP_STATE = 1;
global script Main_Loop{
void run(){
while(1){
Waitframe();
Stalfos_Jump();
}
}
}
void Stalfos_Jump(){
npc e;
for(int i = Screen->NumNPCs(); i > 0 ; i--) {
e = Screen->LoadNPC(i);
if(e->ID != NPC_STALFOS1 && e->ID != NPC_STALFOS2 && e->ID != NPC_STALFOS3)
continue;
if(
Link->Action == LA_ATTACKING &&
((Sword_EquippedA() && Link->InputA) || (Sword_EquippedB() && Link->InputB)) &&
e->Misc[JUMP_STATE] == ON_GROUND &&
Is_Within_Proximity(e) &&
!e->Stun &&
Link_Is_Facing(e) &&
!Link->SwordJinx
){
e->Misc[JUMP_STATE] = GOING_UP;
e->Misc[JUMP_DIR] = Link->Dir;
Game->PlaySound(SFX_JMP);
}
if(e->Misc[JUMP_STATE] != ON_GROUND)
do_jump(e);
}
}
bool E_Can_Move(npc e, int dir, int step){
int x = e->X; int y = e->Y;
int c = 8;
int xx = x + 15;
int yy = y + 15;
if(dir==DIR_UP) return !(y-step<0 || Screen->isSolid(x,y+c-step) || Screen->isSolid(x+8,y+c-step)
|| Screen->isSolid(xx,y+c-step) || IsWater_Pit_OrNoEnemy(ComboAt(x,y-step)));
else if(dir==DIR_DOWN) return !(yy+step>=176 || Screen->isSolid(x,yy+step) || Screen->isSolid(x+8,yy+step)
|| Screen->isSolid(xx,yy+step) || IsWater_Pit_OrNoEnemy(ComboAt(x,yy+step)));
else if(dir==DIR_LEFT) return !(x-step<0 || Screen->isSolid(x-step,y+c) || Screen->isSolid(x-step,y+c+7)
|| Screen->isSolid(x-step,yy) || IsWater_Pit_OrNoEnemy(ComboAt(x-step,y)));
else if(dir==DIR_RIGHT) return !(xx+step>=256 || Screen->isSolid(xx+step,y+c) || Screen->isSolid(xx+step,y+c+7)
|| Screen->isSolid(xx+step,yy) || IsWater_Pit_OrNoEnemy(ComboAt(xx+step,y)));
return false; //invalid direction
}
bool Link_Is_Facing(npc e){
if(e->X < Link->X && Link->Dir == DIR_LEFT)
return true;
else if(e->X > Link->X && Link->Dir == DIR_RIGHT)
return true;
else if(e->Y > Link->Y && Link->Dir == DIR_DOWN)
return true;
else if(e->Y < Link->Y && Link->Dir == DIR_UP)
return true;
return false;
}
void do_jump(npc e){
int dir = e->Misc[JUMP_DIR];
Screen->FastTile(0, e->X, e->Y, SHADOW, SHADOW_CSET, 255);
if(E_Can_Move(e, dir, 1)){
if(dir == DIR_LEFT) e->X -= (JUMP_BACK + 1); //stalfos are more stubbron to push left and up for some reason
else if(dir == DIR_RIGHT) e->X += JUMP_BACK;
else if(dir == DIR_DOWN) e->Y += JUMP_BACK;
else e->Y -= (JUMP_BACK + 1);
}
if(e->Misc[JUMP_STATE] == GOING_UP){
e->Z += JUMP_FORCE;
if(e->Z >= MAX_HEIGHT) e->Misc[JUMP_STATE] = GOING_DOWN;
}else{
//no need to subtract from Z, the game enforces gravity on top-down view
if(e->Z <= 0) e->Misc[JUMP_STATE] = ON_GROUND;
Game->PlaySound(SFX_LAND);
}
}
bool IsWater_Pit_OrNoEnemy(int position){
int combo=Screen->ComboT[position];
int flag = Screen->ComboF[position];
if(combo==CT_WATER || combo==CT_SWIMWARP || combo==CT_DIVEWARP || (combo>=CT_SWIMWARPB && combo<=CT_DIVEWARPD)
|| combo==CT_PIT || combo==CT_PITR || (combo>=CT_PITB && combo<=CT_PITD)
|| flag==CF_NOENEMY || flag==CF_NOGROUNDENEMY)
return true;
else
return false;
}
bool Sword_EquippedA(){
int ID = GetEquipmentA();
return (ID == I_SWORD1 || ID == I_SWORD2 || ID == I_SWORD3 || ID == I_SWORD4);
}
bool Sword_EquippedB(){
int ID = GetEquipmentB();
return (ID == I_SWORD1 || ID == I_SWORD2 || ID == I_SWORD3 || ID == I_SWORD4);
}
int av(int a){
if(a >= 0) return a;
else return (-1 * a);
}
bool Is_Within_Proximity(npc e){
int offset;
if(Game->Generic[GEN_CANSLASH]) offset = 16;
else offset = 0;
if(Link->Dir == DIR_LEFT || Link->Dir == DIR_RIGHT)
return (av(e->X - Link->X) <= PROXIMITY && Link->Y - e->Y < 16 + offset && e->Y - Link->Y < 16);
else if(Link->Dir == DIR_UP)
return (av(e->Y - Link->Y) <= PROXIMITY && Link->X - e->X < 16 && e->X - Link->X < 16 + offset);
else //DIR_DOWN
return (av(e->Y - Link->Y) <= PROXIMITY && Link->X - e->X < 16 + offset && e->X - Link->X < 16);
}
Added a direction check.
Improved proximity check. And it adjusts depending on whether or not the player has slash.
Added check for water, pits, and no-enemy tiles.
Made the script global.
Jinxes work properly.
Sword can be used in either A or B.
As elegant as checking for LW_SWORD sounds, it doesn't really work because its too late to dodge by the time the lweapon is on screen. Detecting the player's attack at button press gives the enemy an extra frame or 2 to react, making it much harder to hit
That, and I think sword beams count as swords?
I think the coding is solid and all but comes a rather problematic issue. How do you go about adding it in if you're using ghost.zh? I still can't quite wrap my head around the initial instructions in Lejes's tutorial.
Did you write import "ghost.zh" at the top of your script file? Good. Doing that automatically imports a bunch of stuff, including the ghost.zh global script.
If it's your only global script, you just need to put "GhostZHActiveScript" in your Active global slot. If you're using others, use this handy guide to combine them!
import "std.zh"
import "string.zh"
import "ghost.zh"
global script GhostAndOtherStuff
{
void run()
{
int dumbvariable;
float someotherdumbvariable;
bool dumboolean;
int dumbarray[10];
StartGhostZH();
while (true)
{
UpdateGhostZH1();
DumbFunction();
Waitdraw();
Z3Scrolling();
UpdateGhostZH2();
Waitframe();
}
}
}
The important things to look at here are the start of the while loop, Waitdraw(), and Waitframe(). The three global ghost functions need to be placed in relation to these.
As long as they're in the proper order, everything should work fine.
What does all that mean exactly? especially in context of all the global variables you placed at the start of your script and the fact it's a global script?
Oh and I really would appreciate it if any of this site's fine musicians would be up for covering the two requests I stated in my opening post.
Edited by FireSeraphim, 08 February 2016 - 03:41 AM.
So what they're saying is that Ghost comes with a global script for your convenience. But, since ZQuest allows you to put only one script into the active slot, you'll need to make your own global script, use that instead, and call the functions yourself.
Do something like this:
global script GhostAndOtherStuff{
void run(){
StartGhostZH();
while(true){
UpdateGhostZH1();
Waitdraw();
UpdateGhostZH2();
Waitframe();
Stalfos_Jump();
//followed by other global "scripts"
}
}
}
The only "script" here is "GhostAndOtherStuff". The rest are just functions that get called every frame to actually do the work, well besides StartGhostZH(); which gets called only once because it's outside of the while loop.
As for the global constants.. Just put them near the top of the file, outside of any script or function.
Also tagged with one or more of these keywords: Request, Midis, Composers Plz, Scripts, Enemies, Programmers Plz, Etc, custom tiles, Sprite Artists Plz