Jump to content

Photo

Poison Sword Script Help

Script Help 2.50

  • Please log in to reply
15 replies to this topic

#1 linkinsphere

linkinsphere

    Junior

  • Members
  • Location:US

Posted 01 September 2016 - 06:10 PM

Hello. I've recently picked up on Zelda Classic programming. I've been coding in Java (relevance) for the last couple years. Since I really enjoy coding and Zelda Classic, I thought I'd combine the two and test myself. However, I'm an extreme novice in the Zelda Classic (zscript) library and I really need help learning this. But in this case, I need help with this script.

If anyone can help me with this matter, please show me what to do and explain why mine isn't working. Thanks!

Basically, after every second which has passed after the poison sword makes contact with the mob, I want it to receive a quarter heart of damage for 3 seconds. Problem is: the enemy won't take constant damage after contact has been made.
Basically, after every second which has passed after the poison sword makes contact with the mob, I want it to receive a quarter heart of damage for 3 seconds. Problem is: the enemy won't take constant damage after contact has been made. If anyone can help me with this matter, please show me what to do and explain why mine isn't working.

import "std.zh"

item script PoisonSword
{
	void run()
	{
		lweapon sword;
		npc n;

		int PoisonEffectTime = 3;
		int time = 0;

		for (int w = Screen->NumNPCs(); w<=0; w++)
		{
			n = Screen->LoadNPC(w);

			if (Collision(sword,n))
			{	
				while (time < PoisonEffectTime)
				{
					Waitframe();
					n->HP = 0;
					time++;
				}
			}
		}
	}
}


#2 Nimono

Nimono

    Ultra Miyoa Extraordinaire!

  • Members
  • Real Name:Matthew
  • Location:Static Void Kingdom

Posted 01 September 2016 - 06:45 PM

Unfortunately, item scripts can't loop. However, what you CAN do is change your looping stuff into an ffc script. You can call upon an FFC to start running a script from within your item script. It's pretty simple! Just do this:

for(int i = 1; i <= 32; i++)
{
	ffc scriptRunner = Screen->LoadFFC(i);
	if(scriptRunner->Script == 0 && scriptRunner->Data == 0)
	{
		scriptRunner->Data = 1; // Should be a completely transparent combo that is not combo 0
		scriptRunner->Script = scriptnum; // The number for the FFC script you wish to run
		break;
	}
}

This code calls upon an FFC that is currently running no script and has no set combo. You can also set InitD[0-7] when calling the FFC if that script has extra features you'd like to have, such as, for example, changing how long the enemy stays poisoned or how much damage it takes each second.

 

EDIT: Few other things I noticed. First off, your for loop has "w<=0". This will make it not actually run, you want w<10, or whatever the max NPCs is, I completely forget. Right now, it starts as equal to 0, therefore the condition is met, so it stops. Second, you set an lweapon variable, but you don't actually create anything. Make sure to do that! I'd recommend using CreateLWeaponAt so you can automatically set its position to Link's position; you normally have to manually set their position as they spawn at 0,0. Lastly, you're setting the NPC's HP to 0, which instantly kills it. Although i suppose you're doing that right now to test if your code works... When you have it working, keep in mind that HP is in 16ths of a heart, so 4 is 1/4 heart. At least, I think that's how it is in ZScript. Never done that myself before.



#3 cavthena

cavthena

    Apprentice

  • Members
  • Real Name:Clayton
  • Location:I wish I knew

Posted 01 September 2016 - 07:17 PM

I'd go with a global script to handle this just as it sounds like something that will happen often and could get messy with FFCs.

 

Something like this.

const int POISONMISC = 14; //Use NPC->Misc[14] to track poison ticker
const int POISONTIME = 180; //Time poison lasts. 180 frames, 3 seconds
const int POISONDAMAGE = 4; //NPCs dont use hearts. 1/4 heart = 4HP. 1 heart = 16HP.
const int POISONSWORD = 0; //Type of LWeapon the poison sword is.

void PoisonSword()
{
	lweapon sword;
	npc temp;
	
	//Tickers
	int t1;
	
	if(NumLWeaponsOf(POISONSWORD) > 0) //Only if poison sword is on screen.
	{
		sword = LoadLWeaponOf(POISONSWORD); //Assumes there can only be 1 lweapon of poison sword.
		for(t1 = Screen->NumNPCs(); t1 > 0; t1--) //NPCs count down but dont include 0 or log an error.
		{
			temp = Screen->LoadNPC(t1);
			//Make sure sword and npc are colliding. Check is sword and NPC are valid. Check for sword defenses.
			if(	 Collision(sword, temp) && 
				 sword->isValid() && 
				 temp->isValid() && 
				(!temp->Defense[NPCD_SWORD] == NPCDT_BLOCK ||
				 !temp->Defense[NPCD_SWORD] == NPCDT_IGNORE))
			{
				temp->Misc[POISONMISC] = POISONTIME;
			}
		}
	}
	
	//Check each NPC for poison.
	for(t1 = Screen->NumNPCs(); t1 > 0; t1--)
	{
		temp->Screen->LoadNPC(t1);
		if(temp->isValid() && temp->Misc[POISONMISC] > 0 && temp->Misc[POISONMISC] % 60 == 0)
		{
			temp->HP -= POISONDAMAGE;
			//PlaySound? Add it here.
                        temp->Misc[POISONMISC]--;
		}
                else if(temp->isValid() && temp->Misc[POISONMISC] > 0)
                {
                       temp->Misc[POISONMISC]--; //Reduce remaining time by 1 frame.
                }
	}
}

Edited by cavthena, 01 September 2016 - 09:07 PM.


#4 linkinsphere

linkinsphere

    Junior

  • Members
  • Location:US

Posted 01 September 2016 - 08:54 PM

Thanks for the help guys, but the sword still doesn't  do damage over time... I'm not experienced enough to figure out why. It has no errors. (After I fixed one)



#5 cavthena

cavthena

    Apprentice

  • Members
  • Real Name:Clayton
  • Location:I wish I knew

Posted 01 September 2016 - 09:08 PM

In order to deal damage over time to an NPC you need to track the NPC and the amount of time left that he's poisoned. Then deal the damage you want after each tick.

 

Please view the code above, it deals 4HP damage each second over 3 seconds. I've used NPC->Misc[] to track the remaining frames that the poison should last.



#6 linkinsphere

linkinsphere

    Junior

  • Members
  • Location:US

Posted 01 September 2016 - 09:59 PM

In order to deal damage over time to an NPC you need to track the NPC and the amount of time left that he's poisoned. Then deal the damage you want after each tick.

 

Please view the code above, it deals 4HP damage each second over 3 seconds. I've used NPC->Misc[] to track the remaining frames that the poison should last.

I kept getting an error telling me that "temp->Screen->LoadNPC(t1)" doesn't work, so I changed it to "temp = Screen->LoadNPC(t1)". I also took the "temp->isValid" out of the conditional because that gave an error as well. Also, how can I see the data through the Misc as an output?


Edited by linkinsphere, 01 September 2016 - 10:03 PM.


#7 cavthena

cavthena

    Apprentice

  • Members
  • Real Name:Clayton
  • Location:I wish I knew

Posted 01 September 2016 - 10:11 PM

oops my bad that should be "temp = Screen->LoadNPC(t1);" not "temp->Screen->LoadNPC(t1);"

 

All the valid checks are to make sure the weapon and NPC are actually on screen. If you attempt to do anything with an weapon or NPC that is not valid then you'll log an error. This can cause lag.

 

You can use the Trace functions found in zscript.txt to wright it in allegro.log.

as an example:

Trace(temp->Misc[POISONMISC]); will log whatever is in Temp->Misc[POISONMISC].



#8 linkinsphere

linkinsphere

    Junior

  • Members
  • Location:US

Posted 01 September 2016 - 10:16 PM

Okay, so I ran a few tests, but the enemies don't take any damage aside from the contact damage. Is there anything I'm doing wrong?



#9 cavthena

cavthena

    Apprentice

  • Members
  • Real Name:Clayton
  • Location:I wish I knew

Posted 01 September 2016 - 11:19 PM

Right finally tracked the problem down. The Defense checks were getting in the way so I removed them for now. Also found a problem with the collision and damage timing, simply added 1 to the counter to prevent instant death.

 

Tested in my own quest with the wooden sword. As before this is a global script, place this in your global loop and set the constants approprately.

const int POISONMISC = 14; //Use NPC->Misc[14] to track poison ticker
const int POISONTIME = 180; //Time poison lasts. 180 frames, 3 seconds
const int POISONDAMAGE = 4; //NPCs dont use hearts. 1/4 heart = 4HP. 1 heart = 16HP.
const int POISONSWORD = 1; //Type of LWeapon the poison sword is. See LW_* in std_constants for weapon types.

void PoisonSword()
{
	lweapon sword;
	npc temp;
	
	//Tickers
	int t1;
	
	if(NumLWeaponsOf(POISONSWORD) > 0) //Only if poison sword is on screen.
	{
		sword = LoadLWeaponOf(POISONSWORD); //Assumes there can only be 1 lweapon of poison sword.
		for(t1 = Screen->NumNPCs(); t1 > 0; t1--) //NPCs count down but dont include 0 or log an error.
		{
			temp = Screen->LoadNPC(t1);
			//Make sure sword and npc are colliding. Check is sword and NPC are valid. Check for sword defenses.
			if(Collision(sword, temp) && sword->isValid() && temp->isValid())
			{
				temp->Misc[POISONMISC] = POISONTIME + 1;
			}
		}
	}
	
	//Check each NPC for poison.
	for(t1 = Screen->NumNPCs(); t1 > 0; t1--)
	{
		temp = Screen->LoadNPC(t1);
		if(temp->isValid() && temp->Misc[POISONMISC] > 0 && temp->Misc[POISONMISC] % 60 == 0)
		{
			temp->HP -= POISONDAMAGE;
			Game->PlaySound(SFX_EHIT);
            temp->Misc[POISONMISC]--;
		}
                else if(temp->isValid() && temp->Misc[POISONMISC] > 0)
                {
                       temp->Misc[POISONMISC]--; //Reduce remaining time by 1 frame.
                }
	}
} 

P.S. I would set the damage to 1 or 2. 4 seems to kill 20HP enemies pretty quick.


Edited by cavthena, 01 September 2016 - 11:22 PM.


#10 linkinsphere

linkinsphere

    Junior

  • Members
  • Location:US

Posted 02 September 2016 - 11:26 AM

Right finally tracked the problem down. The Defense checks were getting in the way so I removed them for now. Also found a problem with the collision and damage timing, simply added 1 to the counter to prevent instant death.

 

Tested in my own quest with the wooden sword. As before this is a global script, place this in your global loop and set the constants approprately.

const int POISONMISC = 14; //Use NPC->Misc[14] to track poison ticker
const int POISONTIME = 180; //Time poison lasts. 180 frames, 3 seconds
const int POISONDAMAGE = 4; //NPCs dont use hearts. 1/4 heart = 4HP. 1 heart = 16HP.
const int POISONSWORD = 1; //Type of LWeapon the poison sword is. See LW_* in std_constants for weapon types.

void PoisonSword()
{
	lweapon sword;
	npc temp;
	
	//Tickers
	int t1;
	
	if(NumLWeaponsOf(POISONSWORD) > 0) //Only if poison sword is on screen.
	{
		sword = LoadLWeaponOf(POISONSWORD); //Assumes there can only be 1 lweapon of poison sword.
		for(t1 = Screen->NumNPCs(); t1 > 0; t1--) //NPCs count down but dont include 0 or log an error.
		{
			temp = Screen->LoadNPC(t1);
			//Make sure sword and npc are colliding. Check is sword and NPC are valid. Check for sword defenses.
			if(Collision(sword, temp) && sword->isValid() && temp->isValid())
			{
				temp->Misc[POISONMISC] = POISONTIME + 1;
			}
		}
	}
	
	//Check each NPC for poison.
	for(t1 = Screen->NumNPCs(); t1 > 0; t1--)
	{
		temp = Screen->LoadNPC(t1);
		if(temp->isValid() && temp->Misc[POISONMISC] > 0 && temp->Misc[POISONMISC] % 60 == 0)
		{
			temp->HP -= POISONDAMAGE;
			Game->PlaySound(SFX_EHIT);
            temp->Misc[POISONMISC]--;
		}
                else if(temp->isValid() && temp->Misc[POISONMISC] > 0)
                {
                       temp->Misc[POISONMISC]--; //Reduce remaining time by 1 frame.
                }
	}
} 

P.S. I would set the damage to 1 or 2. 4 seems to kill 20HP enemies pretty quick.

I really appreciate all this help, man. Another thing though, do I import and  compile the global script into global "active" or somewhere else? It doesn't seem to work. I could just be placing the script in the wrong area.



#11 linkinsphere

linkinsphere

    Junior

  • Members
  • Location:US

Posted 02 September 2016 - 10:18 PM

Bump

#12 cavthena

cavthena

    Apprentice

  • Members
  • Real Name:Clayton
  • Location:I wish I knew

Posted 02 September 2016 - 10:37 PM

Yeah, it's a global active script. I don't have access to my computer so this will be messy. Phone coding...

Global script setup, very basic you should know what to do with the function above.

global script GlobalActive{
void run(){
while(true){
PoisonSword();
Waitframe();
}
}
}

#13 linkinsphere

linkinsphere

    Junior

  • Members
  • Location:US

Posted 03 September 2016 - 12:05 AM

Thanks so much for helping me with this, bro! One more thing though... Is it possible to have multiple "elemental" swords on the same script? I could just change and copy some variables around, right?



#14 cavthena

cavthena

    Apprentice

  • Members
  • Real Name:Clayton
  • Location:I wish I knew

Posted 03 September 2016 - 12:39 AM

It depends on what you want each sword to do. This script only had poison or more precisely damage over time in mind. So in this case I won't say that simply altering the arguments or variables will get you what you want. As an example you wouldn't be able to alter this function to handle an Ice Sword that freezes enemies and/or the environment.

The concept would be roughly the same though. The trick with ZScript is finding the best method to do what you want it to do. I would be willing to help you with the thought process but I wouldn't have the time to code it. With big codes like that, First I would come up with a list of all the swords you want, their effects and if Link carries all of them at once or just one at a time, and if they can affect each other. Eg the ice sword can freeze enemies but the fire sword would thaw them.
From there we can figure out how to best handle them with item, ffc, global or a mix. After that it's on to pseudo code and finally real code.

Almost seems like a lot of work for something like ZC but it's how you get these things to work.

#15 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 08 September 2016 - 05:10 AM

Elemental damage... The easiest way to do this without making huge engine modifications, is to put a flag on every weapon that you create, using a ->Misc[index].

Let's say, this:
 

WFLG_ICE = 01b;
WFLG_FIRE = 10b;
WFLG_SONIC = 100b;
WFLG_ELECT = 1000b;
WFLG_LIGHT = 10000b;
WFLG_DARK = 100000b;
WFLG_WATER = 10000000b;
WFLG_EARTH = 10000000b;

MISC_WFLAGS = 15;

void SetWeaponElement(lweapon l, int flag) { l->Misc[MISC_WFLAGS] |= flag; }
void SetWeaponElement(eweapon l, int flag) { l->Misc[MISC_WFLAGS] |= flag; }

bool GetWeaponElement(lweapon l, int element) { return l->Misc[MISC_FLAGS]&element != 0; }
bool GetWeaponElement(eweapon l, int element) { return l->Misc[MISC_FLAGS]&element != 0; }

You can handle weapon elemental damage with Link easily enough, but handling it with enemies is a right pain.

The easy option is to assign a ghost script to every enemy that gives enemies custom defence properties, but that can become problematic.

I do it globally, by changing the 'Script' defence based on a huge array that holds extended defs for every enemy in the game.

If you aren't modifying damage, but adding special properties (stun, freeze, burn, and so forth) to what weapons do, you can handle those with the global script by checking for collision, reading the weapon flag, and producing an effect.

Really, it's damage modification, and the inability to set every one of the ten script types as a unique weapon defence property, that make this tedious.

 

If you want any of the files from me, that work as an elemental damage system for enemies, using that list, let me know.


Edited by ZoriaRPG, 08 September 2016 - 06:11 AM.

  • linkinsphere likes this



Also tagged with one or more of these keywords: Script, Help, 2.50

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users