Jump to content

Photo

Disable item per screen


  • Please log in to reply
7 replies to this topic

#1 Lüt

Lüt

    Germanize

  • Members
  • Real Name:Steve
  • Location:Chicago

Posted 09 November 2018 - 03:16 PM

Is there a way to disable a specific inventory item on a specific screen?

Ideally, an FFC in which D0 (or D1+) could be the itemID(s) to disable.

This would effectively be like the "Disable Item" DMap setting, but for only one screen. (I don't care whether or not the item disappears from the subscreen / button selection box. Whichever's easier.)

I came across this code posted by idontknow8 in a similar thread earlier this year...

FFC Script ItemJinxes{
    void run (int item1, int item2, int item3, int item4, int item5, int item6, int item7 ){
        while(true){
            if(GetEquipmentB( ) == item1 || GetEquipmentB( ) == item2 || GetEquipmentB( ) == item3 || GetEquipmentB( ) == item4 || GetEquipmentB( ) == item5 || GetEquipmentB( ) == item6 || GetEquipmentB( ) == item7){
            Link->ItemJinx = 2;
            }
        Waitframe( );
        }
    }
}

...but it has two problems. First, it only applies to active (selectable) items, and I want to disable a passive item. Second, it only targets active items in the B-button slot, meaning a quest with A+B select can simply swap the disabled item to the A-button slot to enable it. (Possibly third, the jinx function might not even apply to passive items?)

What I'm trying to do is disable the Whimsical Ring (itemID 122) on Map 15 (DMap 37) Screen 03 to keep it from undermining a boss fight. For my purposes, a "hard-coded" global would be fine if necessary. But the FFC would be far more versatile, and useful to more people.

(Also before anybody says it, scroll-warping to an identical DMap is causing other issues, hence the script request.)

#2 jsm116

jsm116

    Script kludger

  • Members
  • Location:The 301

Posted 09 November 2018 - 08:45 PM

Outside your global script:

int itemscreen;
bool hasitem = false;
const int I_TAKEAWAY = 122;	//Whimsical ring

Inside your global while() loop, above Waitframe():

if(Game->GetCurScreen() != itemscreen)
{
	itemscreen = Game->GetCurScreen();
				
	if((Screen->Flags[9] & 100b) == 100b) Link->Item[I_TAKEAWAY] = false;
	else if(hasitem) Link->Item[I_TAKEAWAY] = true;
}

Finally an item script:

item script RingPickup
{
	void run()
	{
		hasitem = true;
	}
}

Setup: Put the item script in the pickup slot for the Whimsical Ring. On any screen where that ring should be deactivated, check the General Use 1 (scripts) box on the second page of the screen flags. The code in the global will check on screen load if that flag is active. If it is checked and you already have the ring, the ring will be deactivated. If you go to another screen and you had the ring previously, the ring will come back. If you've never picked up the ring, nothing will happen either way.


  • Lüt likes this

#3 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 09 November 2018 - 09:49 PM

Is there a way to disable a specific inventory item on a specific screen?

Ideally, an FFC in which D0 (or D1+) could be the itemID(s) to disable.

This would effectively be like the "Disable Item" DMap setting, but for only one screen. (I don't care whether or not the item disappears from the subscreen / button selection box. Whichever's easier.)

I came across this code posted by idontknow8 in a similar thread earlier this year...
 

FFC Script ItemJinxes{
    void run (int item1, int item2, int item3, int item4, int item5, int item6, int item7 ){
        while(true){
            if(GetEquipmentB( ) == item1 || GetEquipmentB( ) == item2 || GetEquipmentB( ) == item3 || GetEquipmentB( ) == item4 || GetEquipmentB( ) == item5 || GetEquipmentB( ) == item6 || GetEquipmentB( ) == item7){
            Link->ItemJinx = 2;
            }
        Waitframe( );
        }
    }
}
...but it has two problems. First, it only applies to active (selectable) items, and I want to disable a passive item. Second, it only targets active items in the B-button slot, meaning a quest with A+B select can simply swap the disabled item to the A-button slot to enable it. (Possibly third, the jinx function might not even apply to passive items?)

What I'm trying to do is disable the Whimsical Ring (itemID 122) on Map 15 (DMap 37) Screen 03 to keep it from undermining a boss fight. For my purposes, a "hard-coded" global would be fine if necessary. But the FFC would be far more versatile, and useful to more people.

(Also before anybody says it, scroll-warping to an identical DMap is causing other issues, hence the script request.)


Are you using 2.55?


 
int OwnsItems[256];

item script generalPickup
{
    void run()
    {
        OwnsItems[this->ID] = 1;
    }
}

You want a system like that, for inventory management.

For this specific application,. though:
 
ffc script noWhimRing
{
	void run()
	{
		Link->Item[I_WHIMSICALRING] = false;
		while(EnemiesAlive())
		{
			if ( Link->HP <= 0 ) 
			{
				if ( OwnsItems[I_WHIMSICALRING] ) Link->Item[I_WHIMSICALRING] = true;
			}
			Waitframe();
		}
		if ( OwnsItems[I_WHIMSICALRING] ) Link->Item[I_WHIMSICALRING] = true;
	}
}
That'd work on a boss fight screen.
  • Lüt likes this

#4 jsm116

jsm116

    Script kludger

  • Members
  • Location:The 301

Posted 09 November 2018 - 09:59 PM

Zoria, if the boss killed you in that fight, how would you get the ring back?



#5 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 09 November 2018 - 10:32 PM

Zoria, if the boss killed you in that fight, how would you get the ring back?


Ah, good point. Fixed.

If the boss is scripted, it'd probably be better to use the enemy script for this logic. You could just insert something similar to what I posted above in that.

You can also put that fight on its own DMap, and disable the ring on that DMap, to avoid all of this fuss.

#6 Lüt

Lüt

    Germanize

  • Members
  • Real Name:Steve
  • Location:Chicago

Posted 11 November 2018 - 08:30 PM

Setup: Put the item script in the pickup slot for the Whimsical Ring. On any screen where that ring should be deactivated, check the General Use 1 (scripts) box on the second page of the screen flags. The code in the global will check on screen load if that flag is active. If it is checked and you already have the ring, the ring will be deactivated. If you go to another screen and you had the ring previously, the ring will come back. If you've never picked up the ring, nothing will happen either way.

Thanks, this was clear and functional. I've tested it through a number of warp/death/continue situations, and it works in each one.

I have a question about "General Use # (Scripts)" flags though. I already have Moosh Pits using General Use 1, but his file has me enter "2" to use that flag, presumably because it's the third checkbox (0, 1, 2) under that specific screen flag section. But your script identifies that flag with a "9" and a "100b." It's not the 9th flag in any listing, and the only references I can find to 100b in any of the std* files are for usage with Link->Dir (that being up-left). If I wanted to change your script to use General Use 2 - or any other # really - how would I do that?

Are you using 2.55?

Unfortunately, no. This is a 2.53 update to a 2.50 project.

You want a system like that, for inventory management.

So after discussing this on Discord, this was the final solution reached for this method:

// Restores ring if player dies or uses F6 in item-disabled room
global script onExit
{
    void run()
    {
        if ( OwnsItems[130] ) Link->Item[130] = true;
    }
}

// Returns if there are enemy NPCs alive on the current screen
// Borrowed from ZC 2.53's std_functions.zh for compiling with ZC 2.50.2 - can be removed if using 2.53+ to compile
bool EnemiesAlive()
{
	bool alive = false; 
	for ( int q = Screen->NumNPCs(); q > 0; --q )
	{
		npc n = Screen->LoadNPC(q); 
		if ( n->Type != NPCT_PROJECTILE )
		{
			if ( !(n->MiscFlags&NPCMF_NOT_BEATABLE) )
			{
				if ( n->Type != NPCT_FAIRY )
				{
					if ( n->Type != NPCT_GUY )
					{
						alive = true;
					}
				}
			}
		}
	}
	return alive;
}

// Inventory management system
int OwnsItems[256];

// Use as item pickup script to enter item into system
item script generalPickup
{
    void run(int a, int id)
    {
        Screen->Message(a);
        OwnsItems[id] = 1;
    }
}

// Place on screen to disable item
ffc script noWhimRing
{
    void run()
    {
        Link->Item[130] = false;
        Waitframes(5);
        while(EnemiesAlive())
        {
            if(Link->Action==LA_SCROLLING && OwnsItems[130]) Link->Item[130] = true;
            Waitframe();
        }
        if ( OwnsItems[130] ) Link->Item[130] = true;
    }
}

// Place on adjacent screens to restore item
ffc script restoreWhimRing
{
    void run()
    {
        if ( OwnsItems[130] ) Link->Item[130] = true;
    }
}

It's a bit more complicated than jsm's solution, but works equally well.

The first FFC disables the item in whatever room it's placed in, and the second FFC restores the item when you move to an adjacent room. The item script enters it into the inventory management system upon pickup so that the FFCs can identify it in the first place, and the global restores it in case the player dies or uses F6 in a room where it's disabled.

A potential problem, though, is that onExit scripts are broken in 2.50.2 (and earlier?), so this would require 2.53.0 to function properly when dying or saving/continuing.

Either way, both solutions have their benefits and drawbacks. jsm's is the easiest to configure and most simple to use, but costs an extremely limited resource (a "General Use #" flag), while Zoria's has more components to work with, but is a bit more flexible and spares that resource for more script-heavy quests. (And it caught the EnemiessAlive typo in 2.53+'s std_functions.zh.)

You can also put that fight on its own DMap, and disable the ring on that DMap, to avoid all of this fuss.

That was the problem, though. Scrolling warps were messing with continue points in unresolvable ways, so a scripting solution was the last solution.

And now we're set :D

#7 jsm116

jsm116

    Script kludger

  • Members
  • Location:The 301

Posted 11 November 2018 - 09:22 PM

Glad you have a solution that works for you.

 

To your question:

 

Screen->Flags[] is an array of 10 values that correspond to the 10 different sections of screen flags. Each of those 10 values is a composite of bits that are OR'ed together, starting with the least significant bit. So since I was checking for the third flag in the 10th section (arrays start counting from 0), that was 100b. To switch it to the General Use 2 just change both values to 1000b, for General Use 3 change both to 10000b etc.

 

Longer explanation of bitwise operations:

 

Spoiler

  • Lüt likes this

#8 Lüt

Lüt

    Germanize

  • Members
  • Real Name:Steve
  • Location:Chicago

Posted 24 November 2018 - 07:16 AM

Screen->Flags[] is an array of 10 values that correspond to the 10 different sections of screen flags. Each of those 10 values is a composite of bits that are OR'ed together, starting with the least significant bit. So since I was checking for the third flag in the 10th section (arrays start counting from 0), that was 100b. To switch it to the General Use 2 just change both values to 1000b, for General Use 3 change both to 10000b etc.

OK, that worked great, thanks.

 

Sorry for the delayed response, I was in the middle of trying to select, learn and accessibly summarize like 15 new scripts for my set when I asked this. It was a bigger learning curve than I anticipated.

 

The extended explanation makes sense, and now I know what those bitwise numeric charts are that I've seen scattered around other scripts before. The comparison process reminds me of truth tables (hey MTH101 proves useful again). I think I see how the Moosh code plugs the flag number (2) into the variables to convert it to the bitwise number as well, but I wouldn't bet a class grade on it.

 

And just for reference, here's an update to the code I originally referenced in the first post:

bool isEquipped(int itm)
{
    return GetEquipmentB() == itm || GetEquipmentA() == itm;
}

ffc script DisableSelectableItems
{
    void run (int item1, int item2, int item3, int item4, int item5, int item6, int item7 )
    {
        while(true)
        {
            if(isEquipped(item1) || isEquipped(item2) || isEquipped(item3) || isEquipped(item4) || isEquipped(item5) || isEquipped(item6) || isEquipped(item7))
            {
            Link->ItemJinx = 2;
            }
        Waitframe( );
        }
    }
}

As before, this FFC allows the quest designer to disable selectable inventory items per room using D0-D6 as the item IDs, but by adding the bool to combine GetEquipmentA and GetEquipmentB, it now works for quests that have A+B select enabled rather than just quests with B-only select.




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users