const int STATUS_TIME_PERMANENT = 200000; //Pass this into duration argument for permanent status effect.
const int STATUS_STATE_OFF = 0; //Link is free from this status effect.
const int STATUS_STATE_EXPIRED = 1; //The status effect timer just recently hit 0.
const int STATUS_STATE_CLEARED = 2; //The status effect has been recently cured.
const int STATUS_STATE_ACTIVE = 3;
const int STATUS_STATE_UNCURABLE_ACTIVE = 4;
const int STATUS_STATE_PERMANENT_ACTIVE = 5;
const int STATUS_STATE_PERMANENT_UNCURABLE_ACTIVE = 6;
const int STATUS_POISON = 0; // Poison. Damages Link over time.
const int STATUS_DRUNK = 1; // Good old "Drunk" status effect from ZC.
const int STATUS_CONFUSION = 2; // Reverses Left and Right + Up and Down.
const int STATUS_ANTIMAGIC = 3; // Makes Link lose all MP and disables gaining any MP. Also known as CURSE.
const int STATUS_JINX_A = 4; // Jinxes corresponding action button.
const int STATUS_JINX_B = 5; // Best used with "Can select A button on subscreen" quest rule turned on.
const int STATUS_BLIND = 6; // Fills the screen white for simulating being blinded.
const int STATUS_STONE = 7; // Turns Link into a stone statue.
const int STATUS_SCRIPT1 = 8; // Any other curses and blesses that can be bestowed upon poor hero.
const int STATUS_SCRIPT2 = 9;
const int STATUS_SCRIPT3 = 10;
//Constants used by poison status effect.
const int POISON_DAMAGE = 4; // 1/4 heart every 1/2 second is lost when poisoned.
const int POISON_TIMER = 30; // Feel free to change these two constants prior to compiling.
const int CSET_STONE = 11; //CSet used for drawing pertified Link. Change to 7 for Frozen Link or to 5
//for Midas curse, which means turned into golden statue.
const int UPDATE_TIMERS_WHILE_SCROLLING = 0; //Set this constant to any number other than 0,
//and status timers will count down even when the screen is scrolling.
const int PERSIST_STATUS_ON_CONTINUE = 0; //Set constant to 0 to prevent status effects from persisting through F6/Continue, or reloading save file.
int STATUSTIMERS[32];//I have set these arrays quite big so invalidation of saves is uncommon.
int STATUS_STATE[32];//Feel free to change number of timers if global variable number limit prevents scripts from compiling.
//Init global script.
global script Init {
void run() {
InitStatusEffects();
}
}
//Status timers initializing function.
void InitStatusEffects(){
int numtimers = SizeOfArray(STATUSTIMERS);
for (int i=0; i<numtimers; i++){
STATUSTIMERS[i] = 0;
STATUS_STATE[i] = STATUS_STATE_OFF;
}
}
//Example of global script setup.
global script StatusActive {
void run() {
ClearStatusEffectsOnReload();//If PERSIST_STATUS_ON_CONTINUE is set to 0, clear all status effects on restart.
while(true){
StatusEffectsUpdate1(); //Checks status effects and runs code for active ones.
UpdateStatusTimers(); //Updates status effect timers.
Waitdraw(); // Performs Game Logic.
Waitframe(); // Wait for the screen to draw. (You wouldn't think it from the name, right?)
}
}
}
void ClearStatusEffectsOnReload(){
if (PERSIST_STATUS_ON_CONTINUE>0) return;
InitStatusEffects();
}
//Main status effect updating function. Run in main loop of global script
//prior to performing game logic (Waitdraw()).
void StatusEffectsUpdate1(){
if (STATUS_STATE[STATUS_POISON]>0){
if (IsActive(STATUS_POISON)){
int poisontimer = STATUSTIMERS[STATUS_POISON];
if ((poisontimer%POISON_TIMER)==0){
Game->PlaySound(SFX_OUCH);
Link->HP -= POISON_DAMAGE;
}
}
}
if (STATUS_STATE[STATUS_DRUNK]>0){
if (HasStarted(STATUS_DRUNK)){
Link->Drunk = Abs(STATUSTIMERS[STATUS_DRUNK]);//Timer value is negative the moment the status is induced.
}
if (HasExpired(STATUS_DRUNK))Link->Drunk=0;
if (HasRemoved(STATUS_DRUNK))Link->Drunk=0;
}
if (STATUS_STATE[STATUS_CONFUSION]>0){ //Credit goes to Mero for original code.
if (IsActive(STATUS_CONFUSION)){
if(Link->InputUp != Link->InputDown)
{
Link->InputUp = !Link->InputUp;
Link->InputDown = !Link->InputDown;
}
if(Link->InputLeft != Link->InputRight)
{
Link->InputLeft = !Link->InputLeft;
Link->InputRight = !Link->InputRight;
}
}
}
if (STATUS_STATE[STATUS_ANTIMAGIC]>0){
if (IsActive(STATUS_ANTIMAGIC)){
Link->MP=0;
}
}
if (STATUS_STATE[STATUS_JINX_A]>0){
if (IsActive(STATUS_JINX_A)){
Link->InputA = false;
Link->PressA = false;
}
}
if (STATUS_STATE[STATUS_JINX_B]>0){
if (IsActive(STATUS_JINX_B)){
Link->InputB = false;
Link->PressB = false;
}
}
if (STATUS_STATE[STATUS_BLIND]>0){
if (IsActive(STATUS_BLIND)){
Screen->Rectangle(6, 0, 0, 256, 176, 1, 1, 1, 1, 0, true, OP_OPAQUE);
}
}
if (STATUS_STATE[STATUS_STONE]>0){
if (IsActive(STATUS_STONE)){
NoAction();
int LTile = Link->Tile;
Screen->FastTile(3, Link->X, Link->Y, LTile, CSET_STONE, OP_OPAQUE);
}
}
if (STATUS_STATE[STATUS_SCRIPT1]>0){
if (IsActive(STATUS_SCRIPT1)){
//Your status effect code runs HERE. Once every frame.
}
}
if (STATUS_STATE[STATUS_SCRIPT2]>0){
if (IsActive(STATUS_SCRIPT2)){
//Your status effect code runs HERE. Once every frame.
}
}
if (STATUS_STATE[STATUS_SCRIPT3]>0){//A more advanced version of status effect template.
if (HasStarted(STATUS_SCRIPT3)){
//Run code the moment the moment the status effect has been induced.
}
if (IsActive(STATUS_SCRIPT3)){
//This event runs once every frame.
}
if (HasExpired(STATUS_SCRIPT3)){
//Use this event if you need to run some more code right before expiration of status effect.
//Do you remember "Doom" status effect from Final Fantasy RPG`s?
}
if (HasRemoved(STATUS_SCRIPT3)){
//You could also run code here, if you want to execute some nasty surprises
//when player attempts to cure this status effect.
}
}
if (STATUS_STATE[10]>0){
//If you want to add more slots for status effects, just duplicate this piece of code as many times as you want.
//Just make sure to increment index to status timers array every time to avoid conflicts.
}
}
//Updates timers for non-permanent status effects. Used in global script right after StatusEffectsUpdate().
void UpdateStatusTimers(){
if ((Link->Action == LA_SCROLLING)&&(UPDATE_TIMERS_WHILE_SCROLLING == 0)) return;
int numtimers = SizeOfArray(STATUS_STATE);
for (int i=0; i<numtimers; i++){
if(STATUSTIMERS[i]<0) STATUSTIMERS[i] = Abs(STATUSTIMERS[i]);
if ((STATUS_STATE[i] == STATUS_STATE_EXPIRED)||(STATUS_STATE[i] == STATUS_STATE_CLEARED)) STATUS_STATE[i] = STATUS_STATE_OFF;
if (IsPermanent(i)) continue;
if (STATUSTIMERS[i] > 0) STATUSTIMERS[i] --;
else if (STATUS_STATE[i]>1) STATUS_STATE[i] = STATUS_STATE_EXPIRED;
}
}
//Induces given status effect with set time.
//Set "nullifier" to allow specific item to prevent specific status effect/s from being applied to Link.
//"Uncurable" boolean controls whether the status effect can be removed by various means or not.
//Pass STATUS_TIME_PERMANENT into "duration" for permanent status effect.
// /!\ WARNING! Current Status effects and their remaining timers are recorded in player`s save file!
void InduceStatusEffect (int status, int duration, int nullifier, bool uncurable){
if (Link->Item[nullifier]) return;
if (uncurable){
if (duration == STATUS_TIME_PERMANENT) STATUS_STATE[status] = STATUS_STATE_PERMANENT_UNCURABLE_ACTIVE;
else STATUS_STATE[status] = STATUS_STATE_UNCURABLE_ACTIVE;
}
else {
if (duration == STATUS_TIME_PERMANENT) STATUS_STATE[status] = STATUS_STATE_PERMANENT_ACTIVE;
else STATUS_STATE[status] = STATUS_STATE_ACTIVE;
}
STATUSTIMERS[status] = -duration;
}
//Returns TRUE, if the status effect has been recently bestowed upon Link.
bool HasStarted(int status){
return STATUSTIMERS[status]<0;
}
//Returns TRUE, if the status effect is currently active.
bool IsActive(int status){
return STATUS_STATE[status]>2;
}
//Returns TRUE, if the status effect has recently expired.
bool HasExpired(int status){
return STATUS_STATE[status]==STATUS_STATE_EXPIRED;
}
//Returns TRUE, if the status effect has been recently removed, such as by using item.
bool HasRemoved(int status){
return STATUS_STATE[status]==STATUS_STATE_CLEARED;
}
//Removes the given status effect.
// "uncurableforceremove" when set to TRUE bypasses default inability to remove status effect
// by default means and removes this status effect no matter what.
void RemoveStatusEffect(int status, bool uncurableforceremove){
if (!IsCurable(status)&&(!uncurableforceremove)) return;
STATUSTIMERS[status] = 0;
STATUS_STATE[status] = STATUS_STATE_CLEARED;
}
//Returns TRUE if the given status effect is permanent and therefore does not expire on it`s own.
bool IsPermanent (int status){
if (STATUS_STATE[status] == STATUS_STATE_PERMANENT_UNCURABLE_ACTIVE) return true;
if (STATUS_STATE[status] == STATUS_STATE_PERMANENT_ACTIVE) return true;
return false;
}
//Returns TRUE if the given status effect cannot be cured by normal ways.
bool IsCurable(int status){
if (STATUS_STATE[status] == STATUS_STATE_PERMANENT_UNCURABLE_ACTIVE) return false;
if (STATUS_STATE[status] == STATUS_STATE_UNCURABLE_ACTIVE) return false;
return true;
}
//Item that removes specific status effect when used/picked up.
//Can be used as "On Use" item script, like various antidotes,
//or "On Pickup", for item that prevents given status effect.
//D0: Status to remove.
//D1: Clear normally uncurable status effect. 0 - false, 1 - true.
item script CureStatusEffect{
void run (int status, int uncurableforceremove){
bool forceremove = false;
if (!IsActive(status)) Quit();
if (uncurableforceremove > 0) forceremove = true;
RemoveStatusEffect(status, forceremove);
}
}
//Item that applies status effect (usually positive) on activation.
//Best used on potions.
//D0: Status to apply.
//D1: Effect duration.
//D2: Sound to play on usage.
item script SetStatusEffect{
void run(int status, int duration, int sound){
Game->PlaySound(sound);
InduceStatusEffect (status, duration, 0, false);
}
}
//Removes ALL curable status effects when used/picked up. Like the milk in Minecraft.
//Best used as "On Use" item script, for universal "Cure All" potion.
item script CureALL{
void run (){
int numtimers = SizeOfArray(STATUSTIMERS);
for (int i=0; i<numtimers; i++){
if (IsActive(i))RemoveStatusEffect(i, false);
}
}
}
//This is a FFC script for testing and debugging status effects. Any time Link gets hurt by anything
//on the screen with this FFC he will instantly cursed by given status effect for the given time.
//Use this script to debug custom status effects.
//Controls: L/R to cycle trough status effects to apply to Link.
//Place FFC anywhere in the screen.
// D0 - Default index to status effect.
// D1 - Duration, in frames.
// Set D1 to 200000 to render status effect permanent.
ffc script StatusDebug{
void run(int status, int time){
int oldaction = Link->Action;
while(true){
if (Link->Action == LA_GOTHURTLAND){
if (Link->Action != oldaction) InduceStatusEffect (status, time, 0, false);
}
oldaction = Link->Action;
if (Link->PressR){
if (status < 32) status++;
}
if (Link->PressL){
if (status > 0) status--;
}
debugValue( 1, status);
debugValue( 2, STATUSTIMERS[status]);
debugValue( 3, Link->Drunk);
Waitframe();
}
}
}
//Step on FFC with this screen and all curable status effect will be removed instantly.
//Place FFC on the same place as Fairy Ring combo flags.
//D0: sound to play on curing status effects.
ffc script FairyStatusRemover{
void run(int sound){
int count = 0;
int numtimers = SizeOfArray(STATUSTIMERS);
while(true){
if (LinkCollision(this)){
for (int i=0; i<numtimers; i++){
if (IsActive(i)){
RemoveStatusEffect(i, false);
count++;
}
}
if (count>0)Game->PlaySound(sound);
// Waitframe();
// Quit();
}
count=0;
Waitframe();
}
}
}
//This FFC script curses any character with given status effect as long as he is on the given screen
//(or area, if FFC carryover is enabled).
//Place FFC anywhere in the screen.
//D0: Index to status array. Set it to define which status effect is induced.
//D1: Item that prevents this curse as long as it`s in character`s inventory.
ffc script CursedZone{
void run(int status, int nullifier){
int timer = 0;
if (IsActive(status)) timer = STATUSTIMERS[status];
while (true){
if (!IsActive(status))InduceStatusEffect (status, 2, nullifier, false);
else if (!Link->Item[nullifier]) STATUSTIMERS[status] = timer;
Waitframe();
}
}
}