Jump to content

Photo

Delayed arrow shot script not working.


  • Please log in to reply
11 replies to this topic

#1 hideous_kojima

hideous_kojima

    Newbie

  • Members
  • Location:Somewhere in hyperbolic space...

Posted 16 June 2018 - 06:56 PM

So I've been working on a FSA-style bow script, breaking the development down into multiple phases - phase 1 being a bow that simply shoots an arrow whenever its used, and phase 2 (the current phase) being a bow that adds a slight delay (nocking) before firing. I pseudo-coded the logic and it's rock-solid, but due to my inexperience with ZScript the implementation isn't doing what I want... though I feel like I'm this close. Here's what I have so far:

import "std.zh"
import "ffcscript.zh"

const int NOCK_TIMER_END = 30;
const int IC_FAUXBOW = IC_CUSTOM1;

// BEGIN BOW SCRIPTS
item script FauxBow {
    void run() {
	int scriptName[] = "FauxBow2_FFC";
	int ffcScript = Game->GetFFCScript(scriptName);
	if (CountFFCsRunning(ffcScript) == 0)
            RunFFCScript(ffcScript, NULL);
    }
}

ffc script FauxBow_FFC {
    void run() {
	int bowID = GetHighestLevelItem(IC_FAUXBOW);
	int nockTimer = 0;
	bool nockingArrow = false;
	while (true) {                               // Phase 2 Bow Logic:
	    if (UsingItem(bowID) && !nockingArrow) { // If trying to use bow, and no arrow being nocked:
		nockingArrow = true;		     //   Start nocking an arrow.
       	    } else if (nockingArrow) {		     // Otherwise:
		if (nockTimer >= NOCK_TIMER_END) {   //   If nocking is done:
		    ShootArrow();		     //     Shoot the arrow.
		    nockTimer = 0;                   //     Reset the nocking timer.
		    nockingArrow = false;            //     No longer nocking an arrow.
		} else {                             //   Otherwise:
		    nockTimer++;                     //     Continue nocking, incrementing the nocking timer.
		}
	    }
	    Waitframe();
	}
    }
    void ShootArrow() {
        lweapon arrow = CreateLWeaponAt(LW_ARROW, Link->X, Link->Y);
	arrow->Dir = Link->Dir;
	arrow->Step = 300;
	if (Link->Dir == DIR_UP)
  	    arrow->Flip = 0;
	else if (Link->Dir == DIR_DOWN)
	    arrow->Flip = 3;
	else if (Link->Dir == DIR_LEFT)
	    arrow->Flip = 7;
	else if (Link->Dir == DIR_RIGHT)
	    arrow->Flip = 4;
    }
}
// END BOW SCRIPTS

I'm pretty sure the only issue is in how I implemented the "trying to use bow" piece of the commented logic. I don't think I'm using UsingItem() correctly, but due to my lack of knowledge of the language I can't think of another way to put the idea into code. Can anyone fix or give guidance here? Thanks!



#2 Deedee

Deedee

    Bug Frog Dragon Girl

  • Moderators
  • Real Name:Deedee
  • Pronouns:She / Her, They / Them
  • Location:Canada

Posted 16 June 2018 - 07:08 PM

It's calling "FauxBow2_FFC" when the ffc script is called "FauxBow_FFC".



#3 hideous_kojima

hideous_kojima

    Newbie

  • Members
  • Location:Somewhere in hyperbolic space...

Posted 16 June 2018 - 07:23 PM

Ah that is true! What a silly mistake!

 

I fixed that aspect of it, plus I replaced "IC_CUSTOM1" with 67 at line 5 because of a compiler error... but still this isn't working.

 

On top of that, if you replace UsingItem(bowID) with "true" the script functions as expected in that case, firing a continuous stream of arrows separated by none other than the nocking delay... so I know the problem is with how I'm interpreting the "trying to use bow" pseudoinstruction.

 

Thus the big question is: how do I know the player/Link is trying to use the custom bow item during the current frame?



#4 Russ

Russ

    Caelan, the Encouraging

  • Administrators
  • Location:Washington

Posted 16 June 2018 - 08:19 PM

Thus the big question is: how do I know the player/Link is trying to use the custom bow item during the current frame?

You could check if...
(GetEquipmentA() == *your item* and Link->InputA) || (GetEquipmentB() == *your item* and Link->InputB)


#5 Deedee

Deedee

    Bug Frog Dragon Girl

  • Moderators
  • Real Name:Deedee
  • Pronouns:She / Her, They / Them
  • Location:Canada

Posted 16 June 2018 - 08:20 PM

Generally, you pass the item id through an argument in the item editor, then pass that down to the FFC script via ffc->InitD[];. It's a pain, but it's more reliable.



#6 hideous_kojima

hideous_kojima

    Newbie

  • Members
  • Location:Somewhere in hyperbolic space...

Posted 16 June 2018 - 08:43 PM

You could check if...

(GetEquipmentA() == *your item* and Link->InputA) || (GetEquipmentB() == *your item* and Link->InputB)

 

Funny enough that's exactly how UsingItem() works haha. And it seems like it would work for my purposes, if only I could fill in the *your item* part... which brings me to Dimentio's contribution...

 

 

Generally, you pass the item id through an argument in the item editor, then pass that down to the FFC script via ffc->InitD[];. It's a pain, but it's more reliable.

 

Sorry I'm a dumb, but I don't know how to do that, so could you maybe elaborate if you don't mind terribly?

 

 

UPDATE: Solved the problem with the help of a global variable, a la Saffith (https://www.purezc.n...showtopic=73932)...

import "std.zh"
import "ffcscript.zh"

const int NOCK_TIMER_END = 30;
const int IC_FAUXBOW = 67;

bool g_BowUsed = false;

// BEGIN BOW SCRIPTS
item script FauxBow {
    void run() {
        g_BowUsed = true;
	int scriptName[] = "FauxBow_FFC";
	int ffcScript = Game->GetFFCScript(scriptName);
	if (CountFFCsRunning(ffcScript) == 0)
            RunFFCScript(ffcScript, NULL);
    }
}

ffc script FauxBow_FFC {
    void run() {
	int bowID = GetHighestLevelItem(IC_FAUXBOW);
	int nockTimer = 0;
	bool nockingArrow = false;
	while (true) {                               // Phase 2 Bow Logic:
	    if (g_BowUsed && !nockingArrow) { // If trying to use bow, and no arrow being nocked:
		nockingArrow = true;		     //   Start nocking an arrow.
                g_BowUsed = false;
       	    } else if (nockingArrow) {		     // Otherwise:
		if (nockTimer >= NOCK_TIMER_END) {   //   If nocking is done:
		    ShootArrow();		     //     Shoot the arrow.
		    nockTimer = 0;                   //     Reset the nocking timer.
		    nockingArrow = false;            //     No longer nocking an arrow.
		} else {                             //   Otherwise:
		    nockTimer++;                     //     Continue nocking, incrementing the nocking timer.
		}
	    }
	    Waitframe();
	}
    }
    void ShootArrow() {
        lweapon arrow = CreateLWeaponAt(LW_ARROW, Link->X, Link->Y);
	arrow->Dir = Link->Dir;
	arrow->Step = 300;
	if (Link->Dir == DIR_UP)
  	    arrow->Flip = 0;
	else if (Link->Dir == DIR_DOWN)
	    arrow->Flip = 3;
	else if (Link->Dir == DIR_LEFT)
	    arrow->Flip = 7;
	else if (Link->Dir == DIR_RIGHT)
	    arrow->Flip = 4;
    }
}
// END BOW SCRIPTS

Still, I would like to learn alternative ways of accomplishing this besides global vars (I just don't trust them...)


Edited by hideous_kojima, 16 June 2018 - 09:27 PM.


#7 Deedee

Deedee

    Bug Frog Dragon Girl

  • Moderators
  • Real Name:Deedee
  • Pronouns:She / Her, They / Them
  • Location:Canada

Posted 16 June 2018 - 09:28 PM

Inside the void run() of your item script, put: "int itemid" so it's:

void run (int itemid)

Then, create a new array of size 8 and set index 0 of that array to itemid (see here:)

int args[8];
args[0] = itemid;

Then change "RunFFCScript(ffcScript, NULL);" to "RunFFCScript(ffcScript, args);"

Then add "int itemid" to the brackets of voice run() of the ffc script, same as you did the item script.

Finally, you can use itemid inside the ffc script as you please.


  • coolgamer012345 likes this

#8 hideous_kojima

hideous_kojima

    Newbie

  • Members
  • Location:Somewhere in hyperbolic space...

Posted 16 June 2018 - 10:14 PM

Inside the void run() of your item script, put: "int itemid" so it's:

void run (int itemid)

Then, create a new array of size 8 and set index 0 of that array to itemid (see here:)

int args[8];
args[0] = itemid;

Then change "RunFFCScript(ffcScript, NULL);" to "RunFFCScript(ffcScript, args);"

Then add "int itemid" to the brackets of voice run() of the ffc script, same as you did the item script.

Finally, you can use itemid inside the ffc script as you please.

 

Gotcha! It's just... how do I even pass an item id into the run() function? I assume you set that up in the Item Editor, like you said? But where would I do that there, and what item ID would I even pass?



#9 Deedee

Deedee

    Bug Frog Dragon Girl

  • Moderators
  • Real Name:Deedee
  • Pronouns:She / Her, They / Them
  • Location:Canada

Posted 16 June 2018 - 10:16 PM

Oh, it's D0 in the script tab of the item editor. You can have up to 8 variables in the void run() of an item script, and each one corresponds to a respective D# value.



#10 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 17 June 2018 - 04:02 PM

Gotcha! It's just... how do I even pass an item id into the run() function? I assume you set that up in the Item Editor, like you said? But where would I do that there, and what item ID would I even pass?

 
The Item Editor D* values (D0, through D7) correspond to the first eight parameters of the run() functions in item scripts. Because there are two script slots for items (action, and collect), the values are shared. That means that if you enter a value for D0, that if both the item action, and the item collect scripts use parameter 1 in their run function, that both will pull the same value:
 
int items_owned[256];

/* Here, we have a problem, becaue the Item Editor D1 value
   would needc to be different for both scripts, as the collect
   script expects it to be a ZQ String, and the action script
   expects it to be a tile ID.

   To remedy this, the parameters in run must either be staggered,
   or they must split their value between decimal and integer
   components.
*/

item script collect
{
    void run(int id, int message)
    {
        items_owned[id] = 1;
        Screen->message(message);
    }
}

item script action
{
    void run(int id, int tile)
    {
        this->Tile = tile;
    }
}

/* These correct this issue by adding unused parameters to run(),
   so that the action script, and the collect script use different
   D* values for their args (d0 is shared, D1 us used by the
   collect script, and D2 by the action script:
*/

item script collect
{
    void run(int id, int message)
    {
        items_owned[id] = 1;
        Screen->message(message);
    }
}

item script action
{
    void run(int id, int m, int tile) //m is unused
    {
        this->Tile = tile;
    }
}

 
For the function RunFFCScript(), you are passing an array pointer as the second parameter.
 
int bar[4] = { 16, 3, 12.5, 9 };
int ffname[]="TheScriptIdentifier"; //A string, for GetFFCScript().
RunFFCScript(Game->GetFFCScript(ffname), bar); //'bar[]'
 
/* It is not mandatory to name the array 'ars[]', and this array may be of any size, however
   the maximum number of values that you can pass into the FFC D* args is *8*.
   This is why the defult array s a size of [8].
 
   You may pass NULL to parameter 2, if you do not need to forward values to the FFC InitD[].
 
   Look at the function RunFFCScript(int id, int array_pointer), in ffscript.zh for more details.
*/
 
 
As you are new to this, in ZScript, array pointers aren't declared in function params as *ptr, as they would be in C: Instead, they are typed by their datatype solely.
 
Here is a legal function that accepts an array pointer:
 
// SetArrayIndex(int ptr[], int index, int value)
void SetArrayValue(int arr, int indx, int x)
{
    int sz = SizeOfArray(arr) -1;
    if ( indx < 0 || indx > sz ) return; //Sanity check.
    arr[indx] = x;
}
   
 
You'd use it as follows:
 
int my_array[26];
SetArrayIndex(my_array, 4, 12.7612); //Would set index 4 of my_array[] to a value of 12.7612
 
Some Notes on InitD[8]
Spoiler


P.S.

UsingItem() should check Link->PressA/PressB, not Link->InputA/InputB.



#11 hideous_kojima

hideous_kojima

    Newbie

  • Members
  • Location:Somewhere in hyperbolic space...

Posted 17 June 2018 - 08:07 PM

Thanks for all the help! Has been very informative!

 

Last thing: can I give my custom item Fauxbow any ID I want, and use it like any other item ID, so long as it doesn't conflict with the I_ constants? And where can I actually tell ZC/ZQ that the ID I want goes with the Fauxbow custom item?

 

I actually got my bow script working perfectly now assuming the item is equipped to B, which I assured via Init Data for testing purposes... but obviously I wouldn't want to assume that much for a real quest! So, essentially, what I want is a way to do the following:

 

1.  Check if the Fauxbow is currently equipped, and if its button was pressed;

2.  Check if the Fauxbow is currently equipped, and if its button is down.

 

I could obviously use UsingItem() and Russ's suggestion, but I just don't know what item ID to use for those, hence the above questions...

 

 

UPDATE: I figured out that the ID number for a custom item can be seen beneath the tile display in the Item Editor.. so I added a constant I_FAUXBOW equal to that number to my script and voila things work like magic! Thanks again everyone for your help! It has been pivotal :D


Edited by hideous_kojima, 17 June 2018 - 08:50 PM.

  • Deedee likes this

#12 Saffith

Saffith

    IPv7 user

  • ZC Developers

Posted 17 June 2018 - 09:04 PM

Last thing: can I give my custom item Fauxbow any ID I want, and use it like any other item ID, so long as it doesn't conflict with the I_ constants?

It doesn't matter even if it does conflict. The I_ constants in std.zh are just the default item numbers. Those specific values aren't important at all, although messing with item 0 (1 rupee) might cause problems in some cases. (Or maybe that's been fixed? Not sure.)


1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users