mjshi

Jack of Most Trades
Regular
Joined
Feb 16, 2013
Messages
998
Reaction score
885
First Language
English
Primarily Uses
N/A
Conditional Branch+ v2.0 (MV)
by mjshi- OK for use in all projects with credit
Get it here! (direct link)
VX Ace version also available.

In the past, if you wanted to see if the player has more than 10 potions in their inventory, you would have to open up a new event and do the following:
Control Variables > Variable 1 > Set > Game Data > Item "Potion" In Inventory > OK > OK > New Line > Conditional Branch > Variable 1 is > Greater than or equal to > "10" > OK

How tedious! If only there was a simpler, more efficient way...

Well. Look no further. Lazy- er, efficient people, rejoice! For the future is now, and with Conditional Branch+, you can replace that lengthy process with one that looks like this:
Conditional Branch > Script > "Check.has_more(1, 10)" > OK

Features
- No longer do you need a bazillion nested conditional branches to check if the player has multiple items!
Check.has(list of item ids) does that for you.
- No more must you abuse the else branch to see if the player has either one item or another!
Check.has_any(list of item ids) does that for you.
- And the above apply for switches and variables as well, along with much, much more. A full list of functions can be found below and in the plugin's help file.


update 1.1 added functions to check for weapons and equipment
update 1.1a fixed some help file description errors and added shorthands Check.true and Check.false for Check.all_true and Check.all_false respectively
update 1.2 fixed Check.greater and Check.lesser, also added Check.is_not (see help for more details)


* ------------------------------------------------------------------------------
* Conditional Branch+ v2
* Extends the functionality of what a conditional branch can check.
* By mjshi, OK for use in all projects with credit.
* ------------------------------------------------------------------------------
* How to use:
* On a conditional branch, go to the fourth tab and select the "Script" option.
* Type in desired thing to check. See below...
* ------------------------------------------------------------------------------
* Update 2.0!
* Added support for checking all ids or items in a given range.
* Rather than Check.has(1, 2, 3, 4, 10), you can type Check.has("1-4", 10)
* Simply type "1-4" or "1 to 4" or "1...4" or any variant you want, so long
* as the separator between the start and stop is a non-digit character.
* ==============================================================================
* Asterisk (*) means multiple inputs are accepted.
* Tilde (~) means that string ranges can be used for that parameter.
* ==============================================================================
* Combining Checks
* Use "&&", "||", and "()" to combine several checks in a conditional branch.
* && = this AND that are true
* || = this OR that are true
* () = order of operations, check innermost parentheses first
* ! = translates to NOT. EX. !Check.has(1) checks if player DOESN'T have
* item id 1 in their inventory.
*
* -- EX: (Check.has(1) && Check.greater(1 , 0)) || Check.has(2)
* -- Checks if player has item 1 and variable 1 > 0, or player has item 2.
* ==============================================================================
* Possible Checks
* ------------------------------------------------------------------------------
* Items
* ------------------------------------------------------------------------------
* Check.has(~*items)
* -- EX: Check.has(1, 3, 4)
* -- checks if player has items 1, 3, and 4 in inventory.
* -- EX: Check.has("1-4")
* -- checks if player has items 1 through 4 in inventory.
*
* Check.has_more(~*items, number)
* -- EX: Check.has_more(1, 2, 3, 4, 5)
* -- checks if player has at least five (includes 5) of items 1, 2, 3, 4.
* -- EX: Check.has_more("1-4", 5)
* -- checks if player has at least five of items 1 through 4 in inventory.
*
* Check.has_less(~*items, number)
* -- EX: Check.has_less(1, 2, 3, 4, 5)
* -- checks if player has at most five (includes 5) of items 1, 2, 3, 4.
* -- EX: Check.has_less("1-4", 5)
* -- checks if player has at most five of items 1 through 4 in inventory.
*
* Check.has_any(~*items)
* -- EX: Check.has_any(1, 3, 4)
* -- checks if player has either item 1, 3, or 4 in inventory.
* -- EX: Check.has_any("1-4")
* -- checks if player has any of the item IDS from 1 to 4.
*
* Check.each_more(*[item, number])
* -- EX: Check.each_more([1, 2], [2, 4])
* -- checks if there are at least 2 of item 1 and at least 4 of item 2.
*
* Check.each_less(*[item, number])
* -- EX: Check.each_less([1, 2], [2, 4])
* -- checks if there are at most 2 of item 1 and at most 4 of item 2.
* ------------------------------------------------------------------------------
* Equipment
* ------------------------------------------------------------------------------
* Check.equipped(who, "weapon", *ids, "armor", *ids)
* who can be omitted to check ALL members of the party or "active" to check all
* party members active in battle, or it can be a list of actor ids.
* additionally: stores the ID of the first person it finds with the equips
* in Check._whoHad, and if nobody had them equipped, Check._whoHad is 0.
* This can be used in Control Variables to set a variable to the ID of the
* person who had everything equipped.
*
* -- EX: Check.equipped("weapon", 1, "armor", 1, 3, 5)
* -- this works too: Check.equipped("armor", 1, 3, 5, "weapon", 1)
* -- checks if armors 1, 3 AND 5 as well as weapon 1 are equipped by anyone in
* the party. As you can see, weapon and armor can be anywhere.
* -- EX: Check.equipped(1, "weapon", 1)
* -- checks if weapon 1 is equipped by actor id 1
* -- EX: Check.equipped(1, 2, "armor", 1)
* -- checks if armor 1 is equipped by actor id 1 or two
*
* Check.any_equipped(who, "weapon", *ids, "armor", *ids)
* Similar setup to Check.equipped, but is lazier. Also stores the ID of the
* first person it finds in Check._whoHad. Check.equipped_any works as well.
* -- EX: Check.any_equipped(1, "weapon", 3, "armor", 1, 2, 3)
* -- checks if actor 1 equipped either weapon id 3 or armor id 1 or 2 or 3
*
* **The following commands check the inventory ONLY**
*
* Check.has_weapon(~*ids)
* Check.has_armor(~*ids)
* Check.weapon_any(~*ids)
* Check.armor_any(~*ids)
* -- EX: Check.has_weapon(1, 3, 4)
* -- checks if player has weapons 1, 3, and 4 in their inventory
* -- EX: Check.armor_any(1, 3, 4)
* -- checks if player has either armor 1, 3, or 4 in their inventory
* -- EX: Check.weapon_any("1-4")
* -- checks if player has either of the weapons 1 through 4 in inventory
*
* Check.weapon_more(~*ids, number)
* Check.weapon_less(~*ids, number)
* Check.armor_more(~*ids, number)
* Check.armor_less(~*ids, number)
* -- EX: Check.weapon_more(2, 10)
* -- checks if there are at least 10 of weapon id 2
* -- EX: Check.armor_less(2, 10)
* -- checks if there are at most 10 of armor id 2
* ------------------------------------------------------------------------------
* Variables
* ------------------------------------------------------------------------------
* Check.is_any(variable, ~*values)
* -- EX: Check.is_any(1, 3, 4, 5)
* -- checks if variable 1 is either 3, 4, or 5.
*
* Check.is_not(variable, ~*values)
* -- EX: Check.is_not(1, 3, 4, 5)
* -- checks if variable 1 is neither 3, 4, nor 5.
*
* Check.greater(~*variables, value)
* -- EX: Check.greater(1, 2, 3, 5)
* -- checks if variables 1, 2, and 3 are at least 5.
*
* Check.lesser(~*variables, value)
* -- EX: Check.lesser(1, 2, 3, 5)
* -- checks if variables 1, 2, and 3 are at most 5.
*
* Check.in_range(~*variables, start, stop)
* -- EX: Check.in_range(1, 3, 4, 5)
* -- checks if variable 1 AND 3 are between 4 and 5, including 4 and 5.
*
* Check.any_is(~*variables, value)
* -- EX: Check.any_is(1, 3, 4, 5)
* -- checks if variable 1 or 3 or 4 are equal to 5
*
* Check.any_inrange(~*variables, start, stop)
* -- EX: Check.any_inrange(1, 3, 4, 5)
* -- checks if variable 1 OR 3 are between 4 and 5, including 4 and 5.
*
* Check.each_is(*[variable, value])
* -- EX: Check.each_is([1, 3], [4, 5])
* -- checks if variable 1 is 3, and variable 4 is 5.
*
* Check.each_greater(*[variable, value])
* -- EX: Check.each_greater([1, 3], [4, 5])
* -- checks if variable 1 is at least 3 and variable 4 is at least 5.
*
* Check.each_lesser(*[variable, value])
* -- EX: Check.each_lesser([1, 3], [4, 5])
* -- checks if variable 1 is at most 3 and variable 4 is at most 5.
*
* Check.each_inrange(*[variable, start, stop])
* -- EX: Check.in_range([1, 3, 5], [3, 1, 4])
* -- checks if variable 1 is between 3 and 5, and variable 3 is between 1 and 4.
* ------------------------------------------------------------------------------
* Switches
* ------------------------------------------------------------------------------
* Check.all_true(~*switches)
* -- EX: Check.all_true(1, 2, 3)
* -- checks if switches 1, 2, 3 are true.
* -- EX: Check.all_true("1-4")
* -- checks if switches 1 through 4 are true.
*
* Check.true(~*switches)
* -- Same behavior as above
*
* Check.any_true(~*switches)
* -- EX: Check.any_true(1, 2, 3)
* -- checks if either of switches 1, 2, 3 are true.
*
* Check.all_false(~*switches)
* -- EX: Check.all_false(1, 2, 3)
* -- checks if switches 1, 2, 3 are false.
* -- EX: Check.all_false("1-4")
* -- checks if switches 1 through 4 are false.
*
* Check.false(~*switches)
* -- Same behavior as above
*
* Check.any_false(~*switches)
* -- EX: Check.any_false(1, 2, 3)
* -- checks if either of switches 1, 2, 3 are false.
*
* Check.each_switch(*[switch, on/off])
* If on, put 1. If off, put 0.
* ==============================================================================

Installation
Drag into plugin folder, enable in plugin manager. It's just a collection of logic bits, so read the help to see which functions exist.

Credit
mjshi
 
Last edited:

firestalker

Regular
Regular
Joined
Nov 18, 2015
Messages
422
Reaction score
62
First Language
English
Primarily Uses
RMMV
This looks like it'll be a huge help... 
 

Shaz

Keeper of the Nuts
Global Mod
Joined
Mar 2, 2012
Messages
46,153
Reaction score
16,959
First Language
English
Primarily Uses
RMMV
This only looks at items?  Not weapons and/or armor?
 

mjshi

Jack of Most Trades
Regular
Joined
Feb 16, 2013
Messages
998
Reaction score
885
First Language
English
Primarily Uses
N/A
@Shaz Yes, it only looks at items. I was thinking of it being used for things such as quests. I am always open to suggestions, though, so what would one use weapons/armor checks for? Equipment sets?
 

Shaz

Keeper of the Nuts
Global Mod
Joined
Mar 2, 2012
Messages
46,153
Reaction score
16,959
First Language
English
Primarily Uses
RMMV
Yes, possibly.  In my game I give the player the option to give away some of their weapons, but only those that they have multiples of.  


Because Control Variables lets you set a variable to the number of items, weapons OR armor in inventory, KDKW must have thought of some good reasons for needing to know how many of those you might have as well.
 

mjshi

Jack of Most Trades
Regular
Joined
Feb 16, 2013
Messages
998
Reaction score
885
First Language
English
Primarily Uses
N/A
@Shaz Does something like being able to write Check.has(1, 2, 3, "armor", "equipped") look appealing? For seeing if the player has equipped the armors id 1, 2, 3. Of course, you can also write Check.has(1, 2, 3, "weapon", "inventory") or something else of the sort.


JS is super annoying with the way it insists upon handling arguments. Adding this functionality really might take awhile, as something like this:


Check.has = function () {
var items = Array.prototype.slice.call(arguments);
for (var i = 0; i < items.length; i++) {if (!$gameParty.hasItem($dataItems[items])) return false}
return true;
};


turns into this, and it's still not accounting for the things that aren't in the inventory.


Check.has = function () {
var items = Array.prototype.slice.call(arguments);
var type = "items"
if (typeof items[items.length - 1] === "string") {
if (items[items.length - 2] && typeof items[items.length - 2] === "string") {
var equipOnly = (items[items.length - 2] === "equipped")
items.pop();
}
type = items[items.length - 1];
items.pop();
}
for (var i = 0; i < items.length; i++) {if (!$gameParty.hasItem(eval("$data" + type.charAt(0).toUpperCase() + string.slice(1) + "s" + "[items]"))) return false}
return true;
};


Of course, instead of doing something weird like abstracting everything in Check.has, I could simply just do Check.has_armor/Check.has_weapon as separate functions. What are your thoughts on this?
 

Shaz

Keeper of the Nuts
Global Mod
Joined
Mar 2, 2012
Messages
46,153
Reaction score
16,959
First Language
English
Primarily Uses
RMMV
I would much prefer a has_armor, has_weapon, has_item option :)   And the armor/weapon versions could have an option to include anything that's equipped.


Before you go to all the trouble though, only do it if you want to extend the functionality or if others want it.  I'm finishing my game in Ace and it will probably be quite a while before I use MV for anything serious.
 
Last edited by a moderator:

mjshi

Jack of Most Trades
Regular
Joined
Feb 16, 2013
Messages
998
Reaction score
885
First Language
English
Primarily Uses
N/A
Well, it certainly does have its uses, if only for checking set equipment, or excessive items.


I mean, I have nothing better to do, so it's no problem at all xD It's not going to take up much more space, nor can it slow anything down even if its utility is low, thus, the more functions the better. Thank you for your suggestion.
 

Moe_Lester13

Regular
Regular
Joined
Nov 22, 2015
Messages
72
Reaction score
11
First Language
English
I agree that more functionality is better, and this Plugin is definitely a gem. This will save so much time, so I wholeheartedly thank you for this Plugin :)


Keep up the awesome work!!
 

mjshi

Jack of Most Trades
Regular
Joined
Feb 16, 2013
Messages
998
Reaction score
885
First Language
English
Primarily Uses
N/A
Update 1.1


It took me way too long to get certain functions implemented. At this point, I've yet to playtest very much, but I'll tentatively put up update 1.1.


New features:

 * ------------------------------------------------------------------------------ *    Equipment * ------------------------------------------------------------------------------ * Check.equipped(who, "weapon", *ids, "armor", *ids) *  who can be omitted to check ALL members of the party or "active" to check all *  party members active in battle, or it can be a list of actor ids. *   additionally: stores the ID of the first person it finds with the equips *   in Check._whoHad, and if nobody had them equipped, Check._whoHad is 0. *   This can be used in Control Variables to set a variable to the ID of the *   person who had everything equipped.
 *
 * -- EX: Check.equipped("weapon", 1, "armor", 1, 3, 5)
 * -- this works too: Check.equipped("armor", 1, 3, 5, "weapon", 1)
 * -- checks if armors 1, 3 AND 5 as well as weapon 1 are equipped by anyone in
 *    the party. As you can see, weapon and armor can be anywhere.
 * -- EX: Check.equipped(1, "weapon", 1)
 * -- checks if weapon 1 is equipped by actor id 1
 * -- EX: Check.equipped(1, 2, "armor", 1)
 * -- checks if armor 1 is equipped by actor id 1 or two
 *
 * Check.any_equipped(who, "weapon", *ids, "armor", *ids)
 *  Similar setup to Check.equipped, but is lazier. Also stores the ID of the
 *  first person it finds in Check._whoHad. Check.equipped_any works as well.
 * -- EX: Check.any_equipped(1, "weapon", 3, "armor", 1, 2, 3)
 * -- checks if actor 1 equipped either weapon id 3 or armor id 1 or 2 or 3
 *
 *              **The following commands check the inventory ONLY**
 *
 * Check.has_weapon(*ids)
 * Check.has_armor(*ids)
 * Check.weapon_any(*ids)
 * Check.armor_any(*ids)
 * -- EX: Check.has_weapon(1, 3, 4)
 * -- checks if player has weapons 1, 3, and 4 in their inventory
 * -- EX: Check.armor_any(1, 3, 4)
 * -- checks if player has either armor 1, 3, or 4 in their inventory
 *
 * Check.weapon_more(*ids, number)
 * Check.weapon_less(*ids, number)
 * Check.armor_more(*ids, number)
 * Check.armor_less(*ids, number)
 * -- EX: Check.weapon_more(2, 10)
 * -- checks if there are at least 10 of weapon id 2
 * -- EX: Check.armor_less(2, 10)
 * -- checks if there are at most 10 of armor id 2
 
Last edited by a moderator:

DShou

Warper
Member
Joined
Nov 25, 2012
Messages
1
Reaction score
0
First Language
English
Primarily Uses
Heyo, here to report a small typo on line 204.


Check.each_more was repeated for what should have been Check.each_less.
 

mjshi

Jack of Most Trades
Regular
Joined
Feb 16, 2013
Messages
998
Reaction score
885
First Language
English
Primarily Uses
N/A
Ah, shoot. Thanks for telling me, I have no idea how that went undetected for so long (probably because not that many people use that function). I'll fix that as soon as I'm able. Thanks, again.


Update: Bug has been squashed! 
 
Last edited by a moderator:

deathsia

Pegisister
Regular
Joined
Feb 26, 2014
Messages
659
Reaction score
59
First Language
English
Primarily Uses
Okay I'm not sure if this thread is even monitored anymore but I'm having a very frustrating issue.


When I have the plugin check for all three items, it works without a hitch but lets say I want to check for two items in an out of order fashion...I'm horrible at explaining this so I'll just screen cap and explain what happens.

19bdbcc520.png





So...what I originally set up was after the intial check.has_any(51, 52, 53) I had it do an addition check(nested) by typing Check.has_any(52, 53) this resulted in something annoyingly unexpected IF I only picked up item 53, instead of detecting either item like the plugin says its supposed to, it instead FAILS the check and if an else branch is up, will go to that. Or even if I have items 53 and 52 in my inventory and not 51, it still somehow FAILS the check. It's almost as if the plugin is incapable of detecting the item unless it runs in a neatly fashioned 1.,2, 3 by numbers playout and if "1" isn't present, it doesn't bother to check if 2 or 3 are present.


Even more frustrating is that as long as 51 is in the players inventory, it will detect all the proper checks and show shop menus accordingly. Then again, I may be just missing some critical setup and being a bloody half-witted idiot so any help on this is appreciated. 


How I wanted this set up was like this:


It checks for any of the three items, if it detects at least one of them, it then checks for two of the three items(52 and 53), if it doesn't detect those, I assume that the player only has item 51 and thus an else branch is made. If it detects either item 52, or 53 it then checks to see if all three are present and if so, lists the shop BUT if not all three items are in the players inventory, it then checks for either of the two items being present in one of two combinations. If by this point it STILL hasn't detected one of the two combos, it goes to an else branch which checks for each item individually since by this point, its obvious the player only posses one of the three items.
 
Last edited by a moderator:

mjshi

Jack of Most Trades
Regular
Joined
Feb 16, 2013
Messages
998
Reaction score
885
First Language
English
Primarily Uses
N/A
If I ever don't respond to something posted on my own scripts/plugins, assume I am either hospitalized or dead ;P


I feel like your logic might be wrong somewhere-- the way you're using the conditional branch checks is overly complicated and results in tons of nested conditionals, the very thing this plugin seeks to prevent. With all those conditional branches, it is very easy to accidentally make a mistake somewhere. I've double-checked my code, all functions should work as they're described. I think.


Some of your checks are a bit inefficient. For example, if you wanted to check if the player didn't have a particular item, simply doing !Check.has(id) would work. Add that in combination with another Check.has and you can check if the player has only those two other items and not that item (so "!Check.has(id) && Check.has(id1, id2)"). I believe all that is explained in the help file.


Also, Check.has(x) || Check.has(y) can be replaced with Check.has_any(x, y). There's a couple more things like that in what you have there.


Just to clarify:


Check.has checks if the players has all items in that list, and doesn't bother checking 2 and 3 if the player doesn't have 1.


Check.has_any checks if the player has any of the items in that list, and doesn't bother checking 2 and 3 if the player does have 1.


Could you list all the cases please? That is, the different conditions that would lead to each result? (ex. "Case 1: player has item 51 but not 52 or 53, Case 2: player has either 51 or 52 but not item 53") Currently it's a bit much and a rather large wall of text to comprehend xD


After that, could you either send me a screenshot of the entire event or send over a test project, so I can check your work and see if you've set it up as you intended?
 

deathsia

Pegisister
Regular
Joined
Feb 26, 2014
Messages
659
Reaction score
59
First Language
English
Primarily Uses
Thanks for the quick reply! Give me about 2 mins and I'll edit this message with the proper screencaps!(I'm grabbing the whole event page which is gonna take multiple screen caps.


Part 1:

e6f9a0c098.png



Part 2:

02b6eda550.png



Part 3:

82212776e9.png



Part 4:

9db2782ba1.png



Part 5:

937687cb79.png



Part 6:

b845333fb5.png



And that's the whole event page screen capped, some images overlap each other slightly to pay attention to that. If you can give me an idea of how to clean up this mess, i'd love to hear it!
 
Last edited by a moderator:

mjshi

Jack of Most Trades
Regular
Joined
Feb 16, 2013
Messages
998
Reaction score
885
First Language
English
Primarily Uses
N/A
@deathsia Could you list the different "cases" in which the shop processings ought to occur?


So for example:


First shop segment will only occur if player has all the following items: 51, 52, 53
 

deathsia

Pegisister
Regular
Joined
Feb 26, 2014
Messages
659
Reaction score
59
First Language
English
Primarily Uses
Oh, sure.


So here goes:


If player has 51, 52, 53


then "shop 1" is shown


If the player has 52, 53 but NOT 51


"Shop two" is shown


If the player has 51, 53 but NOT 52


Shop 3 is shown


If the player only has one of the three items


It checks each individual item and displays a shop based on that item.


I may have forgotten something so I made a demo with just the scene.


https://dl.dropboxusercontent.com/u/59396144/Project9.zip


I threw this together in a hurry so hopfully it runs right
 

mjshi

Jack of Most Trades
Regular
Joined
Feb 16, 2013
Messages
998
Reaction score
885
First Language
English
Primarily Uses
N/A
@deathsia Thanks! That's much easier to understand.


Well, the demo you sent me is the deployed project-- I can't see the actual conditional branches that way xD


No problem, though, if your cases are correct I don't need it.


Here's some pseudo-code on an efficient way to set up these if/else branches--

Code:
If Check.has(51, 52, 53)
	Shop 1
	Exit event processing
End

If Check.has(52, 53) && !Check.has(51)
	Shop 2
	Exit event processing
End

If Check.has(51, 53) && !Check.has(52)
	Shop 3
	Exit event processing
End

If Check.has(51)
	Shop 4
End

If Check.has(52)
	Shop 5
End

If Check.has(53)
	Shop 6
End

No nesting whatsoever! However, if you don't want to use "exit event processing"...


This setup is less intuitive but still ought to work.

Code:
If Check.has(51, 52, 53)
	Shop 1
End

If Check.has(52, 53) && !Check.has(51)
	Shop 2
End

If Check.has(51, 53) && !Check.has(52)
	Shop 3
End

If Check.has(51) && !Check.has_any(52, 53)
	Shop 4
End
If Check.has(52) && !Check.has_any(51, 53)
	Shop 5
End
If Check.has(53) && !Check.has_any(51, 52)
	Shop 6
End
 

deathsia

Pegisister
Regular
Joined
Feb 26, 2014
Messages
659
Reaction score
59
First Language
English
Primarily Uses
Problem:


aae07fc4b7.png



It still seemingly ignores this if I only have 52 and 53 but NOT 51


Wait...hold the phone... I JUST realized something....its displaying the shop list as if the player has item 51 but NOT 52,53...wth?
 
Last edited by a moderator:

mjshi

Jack of Most Trades
Regular
Joined
Feb 16, 2013
Messages
998
Reaction score
885
First Language
English
Primarily Uses
N/A
@deathsia Strange-- I don't seem to have that problem. Double-check to make sure the event page is formatted as I described, or try the setup without the exit event processing.


See, I don't have that issue (ignore that giant sprite this is some random project from awhile ago).
 

Latest Threads

Latest Posts

Latest Profile Posts

I'm curious, how many hours in RPG Maker do you have? I'm clocked in at 2100 hours on MV and the vast majority of it is for one game. I wish I could track how much I used XP back in the day, I was on it a lot.
Why is it so early? Can I have twenty more minutes of sleep, please?
DK
Do you like this design?

GIF.gif
Im watching Depp vs Heard and keep thinking: "Wouldnt it be fun if her middle name was Lisa? Then she'd be Amber L. Heard. Sounds like Amber Alert."

Then I found out her middle name is Laura.
Yeaah, pallarax mapping

mTQFUv1QrOI.jpg

Forum statistics

Threads
134,807
Messages
1,250,817
Members
177,604
Latest member
hhhgf
Top