2013-03-01 06:52:01 +01:00
package com.gmail.nossr50.skills.herbalism ;
2018-07-24 04:13:57 +02:00
import com.gmail.nossr50.config.Config ;
import com.gmail.nossr50.config.experience.ExperienceConfig ;
import com.gmail.nossr50.config.treasure.TreasureConfig ;
2019-07-07 10:42:57 +02:00
import com.gmail.nossr50.datatypes.BlockSnapshot ;
2019-01-28 03:11:51 +01:00
import com.gmail.nossr50.datatypes.experience.XPGainReason ;
2019-07-07 10:42:57 +02:00
import com.gmail.nossr50.datatypes.experience.XPGainSource ;
2019-01-14 08:23:48 +01:00
import com.gmail.nossr50.datatypes.interactions.NotificationType ;
2018-07-24 04:13:57 +02:00
import com.gmail.nossr50.datatypes.player.McMMOPlayer ;
2019-01-28 03:11:51 +01:00
import com.gmail.nossr50.datatypes.skills.PrimarySkillType ;
import com.gmail.nossr50.datatypes.skills.SubSkillType ;
import com.gmail.nossr50.datatypes.skills.SuperAbilityType ;
import com.gmail.nossr50.datatypes.skills.ToolType ;
2018-07-24 04:13:57 +02:00
import com.gmail.nossr50.datatypes.treasure.HylianTreasure ;
import com.gmail.nossr50.mcMMO ;
2019-07-07 10:42:57 +02:00
import com.gmail.nossr50.runnables.skills.DelayedHerbalismXPCheckTask ;
2018-07-24 04:13:57 +02:00
import com.gmail.nossr50.runnables.skills.HerbalismBlockUpdaterTask ;
import com.gmail.nossr50.skills.SkillManager ;
import com.gmail.nossr50.util.* ;
2019-01-14 08:23:48 +01:00
import com.gmail.nossr50.util.player.NotificationManager ;
2019-01-25 05:45:26 +01:00
import com.gmail.nossr50.util.random.RandomChanceSkillStatic ;
import com.gmail.nossr50.util.random.RandomChanceUtil ;
2019-01-15 14:35:41 +01:00
import com.gmail.nossr50.util.skills.RankUtils ;
2019-01-09 04:52:52 +01:00
import com.gmail.nossr50.util.skills.SkillActivationType ;
2018-07-24 04:13:57 +02:00
import com.gmail.nossr50.util.skills.SkillUtils ;
2018-10-10 03:48:47 +02:00
import org.bukkit.Location ;
import org.bukkit.Material ;
2019-07-07 10:42:57 +02:00
import org.bukkit.block.Block ;
import org.bukkit.block.BlockFace ;
2013-03-01 06:52:01 +01:00
import org.bukkit.block.BlockState ;
2018-08-01 03:35:45 +02:00
import org.bukkit.block.data.Ageable ;
2019-07-07 10:42:57 +02:00
import org.bukkit.block.data.BlockData ;
2013-03-01 06:52:01 +01:00
import org.bukkit.entity.Player ;
2019-07-07 10:42:57 +02:00
import org.bukkit.event.block.BlockBreakEvent ;
2013-03-01 06:52:01 +01:00
import org.bukkit.inventory.ItemStack ;
import org.bukkit.inventory.PlayerInventory ;
2014-04-06 14:41:40 +02:00
import org.bukkit.metadata.FixedMetadataValue ;
2013-03-01 06:52:01 +01:00
2019-07-07 10:42:57 +02:00
import java.util.ArrayList ;
import java.util.Collection ;
import java.util.HashSet ;
2018-07-24 04:13:57 +02:00
import java.util.List ;
2013-03-01 06:52:01 +01:00
public class HerbalismManager extends SkillManager {
public HerbalismManager ( McMMOPlayer mcMMOPlayer ) {
2019-01-13 08:54:53 +01:00
super ( mcMMOPlayer , PrimarySkillType . HERBALISM ) ;
2013-03-01 06:52:01 +01:00
}
public boolean canGreenThumbBlock ( BlockState blockState ) {
2019-01-26 20:21:25 +01:00
if ( ! RankUtils . hasUnlockedSubskill ( getPlayer ( ) , SubSkillType . HERBALISM_GREEN_THUMB ) )
return false ;
2013-03-01 06:52:01 +01:00
Player player = getPlayer ( ) ;
2016-03-05 11:39:16 +01:00
ItemStack item = player . getInventory ( ) . getItemInMainHand ( ) ;
2018-07-24 04:13:57 +02:00
return item . getAmount ( ) > 0 & & item . getType ( ) = = Material . WHEAT_SEEDS & & BlockUtils . canMakeMossy ( blockState ) & & Permissions . greenThumbBlock ( player , blockState . getType ( ) ) ;
2013-03-01 06:52:01 +01:00
}
public boolean canUseShroomThumb ( BlockState blockState ) {
2019-01-26 20:21:25 +01:00
if ( ! RankUtils . hasUnlockedSubskill ( getPlayer ( ) , SubSkillType . HERBALISM_SHROOM_THUMB ) )
return false ;
2013-03-01 06:52:01 +01:00
Player player = getPlayer ( ) ;
2016-03-12 19:35:19 +01:00
PlayerInventory inventory = player . getInventory ( ) ;
Material itemType = inventory . getItemInMainHand ( ) . getType ( ) ;
2013-03-01 06:52:01 +01:00
2019-01-09 04:52:52 +01:00
return ( itemType = = Material . BROWN_MUSHROOM | | itemType = = Material . RED_MUSHROOM ) & & inventory . contains ( Material . BROWN_MUSHROOM , 1 ) & & inventory . contains ( Material . RED_MUSHROOM , 1 ) & & BlockUtils . canMakeShroomy ( blockState ) & & Permissions . isSubSkillEnabled ( player , SubSkillType . HERBALISM_SHROOM_THUMB ) ;
2013-03-01 06:52:01 +01:00
}
public boolean canUseHylianLuck ( ) {
2019-01-26 20:21:25 +01:00
if ( ! RankUtils . hasUnlockedSubskill ( getPlayer ( ) , SubSkillType . HERBALISM_HYLIAN_LUCK ) )
return false ;
2019-01-09 04:52:52 +01:00
return Permissions . isSubSkillEnabled ( getPlayer ( ) , SubSkillType . HERBALISM_HYLIAN_LUCK ) ;
2013-03-01 06:52:01 +01:00
}
public boolean canGreenTerraBlock ( BlockState blockState ) {
2019-01-13 04:56:54 +01:00
return mcMMOPlayer . getAbilityMode ( SuperAbilityType . GREEN_TERRA ) & & BlockUtils . canMakeMossy ( blockState ) ;
2013-03-01 06:52:01 +01:00
}
public boolean canActivateAbility ( ) {
2013-03-03 17:06:05 +01:00
return mcMMOPlayer . getToolPreparationMode ( ToolType . HOE ) & & Permissions . greenTerra ( getPlayer ( ) ) ;
2013-03-01 06:52:01 +01:00
}
2019-07-07 10:42:57 +02:00
public boolean isGreenTerraActive ( ) {
2019-01-13 04:56:54 +01:00
return mcMMOPlayer . getAbilityMode ( SuperAbilityType . GREEN_TERRA ) ;
2013-03-01 06:52:01 +01:00
}
/ * *
* Handle the Farmer ' s Diet ability
*
* @param eventFoodLevel The initial change in hunger from the event
* @return the modified change in hunger for the event
* /
2019-01-15 14:39:25 +01:00
public int farmersDiet ( int eventFoodLevel ) {
2019-01-15 14:35:41 +01:00
return SkillUtils . handleFoodSkills ( getPlayer ( ) , eventFoodLevel , SubSkillType . HERBALISM_FARMERS_DIET ) ;
2013-03-01 06:52:01 +01:00
}
/ * *
* Process the Green Terra ability .
*
* @param blockState The { @link BlockState } to check ability activation for
* @return true if the ability was successful , false otherwise
* /
2019-07-07 10:42:57 +02:00
public boolean processGreenTerraBlockConversion ( BlockState blockState ) {
2013-03-01 06:52:01 +01:00
Player player = getPlayer ( ) ;
if ( ! Permissions . greenThumbBlock ( player , blockState . getType ( ) ) ) {
return false ;
}
PlayerInventory playerInventory = player . getInventory ( ) ;
2018-07-24 04:13:57 +02:00
ItemStack seed = new ItemStack ( Material . WHEAT_SEEDS ) ;
2013-03-01 06:52:01 +01:00
if ( ! playerInventory . containsAtLeast ( seed , 1 ) ) {
2019-01-14 08:23:48 +01:00
NotificationManager . sendPlayerInformation ( player , NotificationType . REQUIREMENTS_NOT_MET , " Herbalism.Ability.GTe.NeedMore " ) ;
2013-03-01 06:52:01 +01:00
return false ;
}
playerInventory . removeItem ( seed ) ;
player . updateInventory ( ) ; // Needed until replacement available
return Herbalism . convertGreenTerraBlocks ( blockState ) ;
}
/ * *
2019-07-07 10:42:57 +02:00
* Handles herbalism abilities and XP rewards from a BlockBreakEvent
* @param blockBreakEvent The Block Break Event to process
2013-03-01 06:52:01 +01:00
* /
2019-07-07 10:42:57 +02:00
public void processHerbalismBlockBreakEvent ( BlockBreakEvent blockBreakEvent ) {
2013-10-09 16:27:06 +02:00
Player player = getPlayer ( ) ;
2013-03-08 14:53:54 +01:00
2019-07-07 10:42:57 +02:00
if ( Config . getInstance ( ) . getHerbalismPreventAFK ( ) & & player . isInsideVehicle ( ) ) {
2013-03-08 10:11:33 +01:00
return ;
}
2018-07-27 02:47:45 +02:00
2019-07-07 10:42:57 +02:00
/ *
* There are single - block plants and multi - block plants in Minecraft
* In order to give out proper rewards , we need to collect all blocks that would be broken from this event
* /
//Grab all broken blocks
HashSet < Block > brokenBlocks = getBrokenHerbalismBlocks ( blockBreakEvent ) ;
//Handle rewards, xp, ability interactions, etc
processHerbalismOnBlocksBroken ( blockBreakEvent , brokenBlocks ) ;
}
/ * *
* Process rewards for a set of plant blocks for Herbalism
* @param blockBreakEvent the block break event
* @param brokenPlants plant blocks to process
* /
private void processHerbalismOnBlocksBroken ( BlockBreakEvent blockBreakEvent , HashSet < Block > brokenPlants ) {
BlockState originalBreak = blockBreakEvent . getBlock ( ) . getState ( ) ;
//TODO: The design of Green Terra needs to change, this is a mess
if ( Permissions . greenThumbPlant ( getPlayer ( ) , originalBreak . getType ( ) ) ) {
processGreenThumbPlants ( originalBreak , isGreenTerraActive ( ) ) ;
2013-10-05 01:18:51 +02:00
}
2019-07-07 10:42:57 +02:00
/ *
* Mark blocks for double drops
* Be aware of the hacky interactions we are doing with Chorus Plants
* /
2019-08-05 16:39:33 +02:00
checkDoubleDropsOnBrokenPlants ( blockBreakEvent . getPlayer ( ) , brokenPlants ) ;
2019-07-07 10:42:57 +02:00
//It would take an expensive algorithm to predict which parts of a Chorus Tree will break as a result of root break
//So this hacky method is used instead
ArrayList < BlockSnapshot > delayedChorusBlocks = new ArrayList < > ( ) ; //Blocks that will be checked in future ticks
HashSet < Block > noDelayPlantBlocks = new HashSet < > ( ) ; //Blocks that will be checked immediately
for ( Block brokenPlant : brokenPlants ) {
/ *
* This check is to make XP bars appear to work properly with Chorus Trees by giving XP for the originalBreak immediately instead of later
* /
if ( brokenPlant . getLocation ( ) . equals ( originalBreak . getBlock ( ) . getLocation ( ) ) ) {
//If its the same block as the original, we are going to directly check it for being a valid XP gain and add it to the nonChorusBlocks list even if its a chorus block
//This stops a delay from happening when bringing up the XP bar for chorus trees
if ( ! mcMMO . getPlaceStore ( ) . isTrue ( originalBreak ) ) {
//Even if its a chorus block, the original break will be moved to nonChorusBlocks for immediate XP rewards
noDelayPlantBlocks . add ( brokenPlant ) ;
} else {
if ( isChorusTree ( brokenPlant . getType ( ) ) ) {
//If its a chorus tree AND it was marked as true in the placestore then we add this block to the list of chorus blocks
delayedChorusBlocks . add ( new BlockSnapshot ( brokenPlant . getType ( ) , brokenPlant ) ) ;
} else {
noDelayPlantBlocks . add ( brokenPlant ) ; //If its not a chorus plant that was marked as unnatural but it was marked unnatural, put it in the nodelay list to be handled
}
}
} else if ( isChorusTree ( brokenPlant . getType ( ) ) ) {
//Chorus Blocks get checked for XP several ticks later to avoid expensive calculations
delayedChorusBlocks . add ( new BlockSnapshot ( brokenPlant . getType ( ) , brokenPlant ) ) ;
} else {
noDelayPlantBlocks . add ( brokenPlant ) ;
}
}
//Give out XP to the non-chorus blocks
if ( noDelayPlantBlocks . size ( ) > 0 ) {
//Note: Will contain 1 chorus block if the original block was a chorus block, this is to prevent delays for the XP bar
awardXPForPlantBlocks ( noDelayPlantBlocks ) ;
}
2013-03-01 06:52:01 +01:00
2019-07-07 10:42:57 +02:00
if ( delayedChorusBlocks . size ( ) > 0 ) {
//Check XP for chorus blocks
DelayedHerbalismXPCheckTask delayedHerbalismXPCheckTask = new DelayedHerbalismXPCheckTask ( mcMMOPlayer , delayedChorusBlocks ) ;
//Large delay because the tree takes a while to break
delayedHerbalismXPCheckTask . runTaskLater ( mcMMO . p , 20 ) ; //Calculate Chorus XP + Bonus Drops 1 tick later
}
}
2019-08-05 16:39:33 +02:00
/ * *
* Check for double drops on a collection of broken blocks
* If a double drop has occurred , it will be marked here for bonus drops
* @param player player who broke the blocks
* @param brokenPlants the collection of broken plants
* /
public void checkDoubleDropsOnBrokenPlants ( Player player , Collection < Block > brokenPlants ) {
//Only proceed if skill unlocked and permission enabled
if ( ! RankUtils . hasUnlockedSubskill ( player , SubSkillType . HERBALISM_DOUBLE_DROPS )
| | ! Permissions . isSubSkillEnabled ( player , SubSkillType . HERBALISM_DOUBLE_DROPS ) ) {
return ;
}
2019-07-07 10:42:57 +02:00
for ( Block brokenPlant : brokenPlants ) {
BlockState brokenPlantState = brokenPlant . getState ( ) ;
BlockData plantData = brokenPlantState . getBlockData ( ) ;
//Check for double drops
if ( ! mcMMO . getPlaceStore ( ) . isTrue ( brokenPlant ) ) {
/ *
*
* Natural Blocks
*
*
*
* /
//Not all things that are natural should give double drops, make sure its fully mature as well
if ( plantData instanceof Ageable ) {
Ageable ageable = ( Ageable ) plantData ;
if ( isAgeableMature ( ageable ) | | isBizarreAgeable ( plantData ) ) {
2019-08-05 16:39:33 +02:00
if ( checkDoubleDrop ( brokenPlantState ) ) {
markForBonusDrops ( brokenPlantState ) ;
}
2019-07-07 10:42:57 +02:00
}
} else if ( checkDoubleDrop ( brokenPlantState ) ) {
//Add metadata to mark this block for double or triple drops
markForBonusDrops ( brokenPlantState ) ;
}
} else {
/ *
*
* Unnatural Blocks
*
* /
//If its a Crop we need to reward XP when its fully grown
if ( isAgeableAndFullyMature ( plantData ) & & ! isBizarreAgeable ( plantData ) ) {
//Add metadata to mark this block for double or triple drops
markForBonusDrops ( brokenPlantState ) ;
}
}
}
}
2013-10-09 16:27:06 +02:00
2019-07-07 10:42:57 +02:00
/ * *
* Checks if BlockData is ageable and we can trust that age for Herbalism rewards / XP reasons
* @param blockData target BlockData
* @return returns true if the ageable is trustworthy for Herbalism XP / Rewards
* /
public boolean isBizarreAgeable ( BlockData blockData ) {
if ( blockData instanceof Ageable ) {
//Catcus and Sugar Canes cannot be trusted
switch ( blockData . getMaterial ( ) ) {
case CACTUS :
case SUGAR_CANE :
return true ;
default :
return false ;
2013-10-09 16:27:06 +02:00
}
}
2013-03-08 10:11:33 +01:00
2019-07-07 10:42:57 +02:00
return false ;
}
public void markForBonusDrops ( BlockState brokenPlantState ) {
//Add metadata to mark this block for double or triple drops
boolean awardTriple = mcMMOPlayer . getAbilityMode ( SuperAbilityType . GREEN_TERRA ) ;
BlockUtils . markDropsAsBonus ( brokenPlantState , awardTriple ) ;
}
/ * *
* Checks if a block is an ageable and if that ageable is fully mature
* @param plantData target plant
* @return returns true if the block is both an ageable and fully mature
* /
public boolean isAgeableAndFullyMature ( BlockData plantData ) {
return plantData instanceof Ageable & & isAgeableMature ( ( Ageable ) plantData ) ;
}
public void awardXPForPlantBlocks ( HashSet < Block > brokenPlants ) {
int xpToReward = 0 ;
for ( Block brokenPlantBlock : brokenPlants ) {
BlockState brokenBlockNewState = brokenPlantBlock . getState ( ) ;
BlockData plantData = brokenBlockNewState . getBlockData ( ) ;
if ( mcMMO . getPlaceStore ( ) . isTrue ( brokenBlockNewState ) ) {
/ *
*
* Unnatural Blocks
*
*
* /
//If its a Crop we need to reward XP when its fully grown
if ( isAgeableAndFullyMature ( plantData ) & & ! isBizarreAgeable ( plantData ) ) {
xpToReward + = ExperienceConfig . getInstance ( ) . getXp ( PrimarySkillType . HERBALISM , brokenBlockNewState . getType ( ) ) ;
2019-03-17 04:26:26 +01:00
}
2019-07-07 10:42:57 +02:00
//Mark it as natural again as it is being broken
mcMMO . getPlaceStore ( ) . setFalse ( brokenBlockNewState ) ;
2019-03-24 00:21:25 +01:00
} else {
2019-07-07 10:42:57 +02:00
/ *
*
* Natural Blocks
*
*
* /
//Calculate XP
if ( plantData instanceof Ageable ) {
Ageable plantAgeable = ( Ageable ) plantData ;
if ( isAgeableMature ( plantAgeable ) | | isBizarreAgeable ( plantData ) ) {
xpToReward + = ExperienceConfig . getInstance ( ) . getXp ( PrimarySkillType . HERBALISM , brokenBlockNewState . getType ( ) ) ;
}
} else {
xpToReward + = ExperienceConfig . getInstance ( ) . getXp ( PrimarySkillType . HERBALISM , brokenPlantBlock . getType ( ) ) ;
}
2013-03-01 06:52:01 +01:00
}
2019-07-07 10:42:57 +02:00
}
if ( mcMMOPlayer . isDebugMode ( ) ) {
mcMMOPlayer . getPlayer ( ) . sendMessage ( " Plants processed: " + brokenPlants . size ( ) ) ;
}
//Reward XP
if ( xpToReward > 0 ) {
applyXpGain ( xpToReward , XPGainReason . PVE , XPGainSource . SELF ) ;
}
}
2019-03-24 00:21:25 +01:00
2019-07-07 10:42:57 +02:00
public boolean isAgeableMature ( Ageable ageable ) {
return ageable . getAge ( ) = = ageable . getMaximumAge ( )
& & ageable . getAge ( ) ! = 0 ;
}
/ * *
* Award XP for any blocks that used to be something else but are now AIR
* @param brokenPlants snapshot of broken blocks
* /
public void awardXPForBlockSnapshots ( ArrayList < BlockSnapshot > brokenPlants ) {
/ *
* This handles XP for blocks that we need to check are broken after the fact
* This only applies to chorus trees right now
* /
int xpToReward = 0 ;
int blocksGivingXP = 0 ;
for ( BlockSnapshot blockSnapshot : brokenPlants ) {
BlockState brokenBlockNewState = blockSnapshot . getBlockRef ( ) . getState ( ) ;
//Remove metadata from the snapshot of blocks
if ( brokenBlockNewState . hasMetadata ( mcMMO . BONUS_DROPS_METAKEY ) ) {
brokenBlockNewState . removeMetadata ( mcMMO . BONUS_DROPS_METAKEY , mcMMO . p ) ;
}
//If the block is not AIR that means it wasn't broken
if ( brokenBlockNewState . getType ( ) ! = Material . AIR ) {
continue ;
}
if ( mcMMO . getPlaceStore ( ) . isTrue ( brokenBlockNewState ) ) {
//Mark it as natural again as it is being broken
mcMMO . getPlaceStore ( ) . setFalse ( brokenBlockNewState ) ;
} else {
//TODO: Do we care about chorus flower age?
//Calculate XP for the old type
xpToReward + = ExperienceConfig . getInstance ( ) . getXp ( PrimarySkillType . HERBALISM , blockSnapshot . getOldType ( ) ) ;
blocksGivingXP + + ;
2018-01-15 22:47:41 +01:00
}
2013-03-01 06:52:01 +01:00
}
2019-07-07 10:42:57 +02:00
if ( mcMMOPlayer . isDebugMode ( ) ) {
mcMMOPlayer . getPlayer ( ) . sendMessage ( " Chorus Plants checked for XP: " + brokenPlants . size ( ) ) ;
mcMMOPlayer . getPlayer ( ) . sendMessage ( " Valid Chorus Plant XP Gains: " + blocksGivingXP ) ;
}
//Reward XP
if ( xpToReward > 0 ) {
applyXpGain ( xpToReward , XPGainReason . PVE , XPGainSource . SELF ) ;
}
}
/ * *
* Process and return plant blocks from a BlockBreakEvent
* @param blockBreakEvent target event
* @return a set of plant - blocks that were broken as a result of this event
* /
private HashSet < Block > getBrokenHerbalismBlocks ( BlockBreakEvent blockBreakEvent ) {
//Get an updated capture of this block
BlockState originalBlockBlockState = blockBreakEvent . getBlock ( ) . getState ( ) ;
Material originalBlockMaterial = originalBlockBlockState . getType ( ) ;
HashSet < Block > blocksBroken = new HashSet < > ( ) ; //Blocks broken
//Check if this block is a one block plant or not
boolean oneBlockPlant = isOneBlockPlant ( originalBlockMaterial ) ;
if ( oneBlockPlant ) {
//If the block is a one-block plant return only that
blocksBroken . add ( originalBlockBlockState . getBlock ( ) ) ;
} else {
//If the block is a multi-block structure, capture a set of all blocks broken and return that
blocksBroken = getBrokenBlocksMultiBlockPlants ( originalBlockBlockState , blockBreakEvent ) ;
}
//Return all broken plant-blocks
return blocksBroken ;
}
private HashSet < Block > getBrokenChorusBlocks ( BlockState originalBreak ) {
HashSet < Block > traversedBlocks = grabChorusTreeBrokenBlocksRecursive ( originalBreak . getBlock ( ) , new HashSet < > ( ) ) ;
return traversedBlocks ;
}
private HashSet < Block > grabChorusTreeBrokenBlocksRecursive ( Block currentBlock , HashSet < Block > traversed ) {
if ( ! isChorusTree ( currentBlock . getType ( ) ) )
return traversed ;
// Prevent any infinite loops, who needs more than 256 chorus anyways
if ( traversed . size ( ) > 256 )
return traversed ;
if ( ! traversed . add ( currentBlock ) )
return traversed ;
//Grab all Blocks in the Tree
for ( BlockFace blockFace : new BlockFace [ ] { BlockFace . UP , BlockFace . NORTH , BlockFace . SOUTH , BlockFace . EAST , BlockFace . WEST } )
grabChorusTreeBrokenBlocksRecursive ( currentBlock . getRelative ( blockFace , 1 ) , traversed ) ;
traversed . add ( currentBlock ) ;
return traversed ;
2019-03-24 00:21:25 +01:00
}
2013-03-08 10:11:33 +01:00
2019-07-07 10:42:57 +02:00
/ * *
* Grab a set of all plant blocks that are broken as a result of this event
* The method to grab these blocks is a bit hacky and does not hook into the API
* Basically we expect the blocks to be broken if this event is not cancelled and we determine which block are broken on our end rather than any event state captures
*
* @param blockBreakEvent target event
* @return a set of plant - blocks broken from this event
* /
protected HashSet < Block > getBrokenBlocksMultiBlockPlants ( BlockState originalBlockBroken , BlockBreakEvent blockBreakEvent ) {
//Track the broken blocks
HashSet < Block > brokenBlocks ;
if ( isChorusBranch ( originalBlockBroken . getType ( ) ) ) {
brokenBlocks = getBrokenChorusBlocks ( originalBlockBroken ) ;
} else {
brokenBlocks = getBlocksBrokenAbove ( originalBlockBroken ) ;
}
return brokenBlocks ;
}
private boolean isChorusBranch ( Material blockType ) {
return blockType = = Material . CHORUS_PLANT ;
}
private boolean isChorusTree ( Material blockType ) {
return blockType = = Material . CHORUS_PLANT | | blockType = = Material . CHORUS_FLOWER ;
}
/ * *
* Grabs blocks upwards from a target block
* A lot of Plants / Crops in Herbalism only break vertically from a broken block
* The vertical search returns early if it runs into anything that is not a multi - block plant
* Multi - block plants are hard - coded and kept in { @link MaterialMapStore }
*
* @param breakPointBlockState The point of the " break "
* @return A set of blocks above the target block which can be assumed to be broken
* /
private HashSet < Block > getBlocksBrokenAbove ( BlockState breakPointBlockState ) {
HashSet < Block > brokenBlocks = new HashSet < > ( ) ;
Block block = breakPointBlockState . getBlock ( ) ;
//Add the initial block to the set
brokenBlocks . add ( block ) ;
//Limit our search
int maxHeight = 255 ;
// Search vertically for multi-block plants, exit early if any non-multi block plants
for ( int y = 1 ; y < maxHeight ; y + + ) {
//TODO: Should this grab state? It would be more expensive..
Block relativeUpBlock = block . getRelative ( BlockFace . UP , y ) ;
//Abandon our search if the block isn't multi
if ( ! mcMMO . getMaterialMapStore ( ) . isMultiBlockPlant ( relativeUpBlock . getType ( ) ) )
break ;
brokenBlocks . add ( relativeUpBlock ) ;
}
return brokenBlocks ;
}
/ * *
* If the plant is considered a one block plant
* This is determined by seeing if it exists in a hard - coded collection of Multi - Block plants
* @param material target plant material
* @return true if the block is not contained in the collection of multi - block plants
* /
private boolean isOneBlockPlant ( Material material ) {
return ! mcMMO . getMaterialMapStore ( ) . isMultiBlockPlant ( material ) ;
2019-04-10 01:32:42 +02:00
}
2019-03-24 00:21:25 +01:00
/ * *
* Check for success on herbalism double drops
* @param blockState target block state
* @return true if double drop succeeds
* /
2019-07-07 10:42:57 +02:00
private boolean checkDoubleDrop ( BlockState blockState )
2019-03-24 00:21:25 +01:00
{
return BlockUtils . checkDoubleDrops ( getPlayer ( ) , blockState , skill , SubSkillType . HERBALISM_DOUBLE_DROPS ) ;
2013-03-01 06:52:01 +01:00
}
/ * *
* Process the Green Thumb ability for blocks .
*
* @param blockState The { @link BlockState } to check ability activation for
* @return true if the ability was successful , false otherwise
* /
public boolean processGreenThumbBlocks ( BlockState blockState ) {
2019-01-25 05:45:26 +01:00
if ( ! RandomChanceUtil . isActivationSuccessful ( SkillActivationType . RANDOM_LINEAR_100_SCALE_WITH_CAP , SubSkillType . HERBALISM_GREEN_THUMB , getPlayer ( ) ) ) {
2019-02-01 19:34:13 +01:00
NotificationManager . sendPlayerInformation ( getPlayer ( ) , NotificationType . SUBSKILL_MESSAGE_FAILED , " Herbalism.Ability.GTh.Fail " ) ;
2013-03-01 06:52:01 +01:00
return false ;
}
return Herbalism . convertGreenTerraBlocks ( blockState ) ;
}
/ * *
* Process the Hylian Luck ability .
*
* @param blockState The { @link BlockState } to check ability activation for
* @return true if the ability was successful , false otherwise
* /
public boolean processHylianLuck ( BlockState blockState ) {
2019-01-25 05:45:26 +01:00
if ( ! RandomChanceUtil . isActivationSuccessful ( SkillActivationType . RANDOM_LINEAR_100_SCALE_WITH_CAP , SubSkillType . HERBALISM_HYLIAN_LUCK , getPlayer ( ) ) ) {
2013-03-01 06:52:01 +01:00
return false ;
}
2018-07-27 01:53:29 +02:00
String friendly = StringUtils . getFriendlyConfigBlockDataString ( blockState . getBlockData ( ) ) ;
2017-06-10 20:16:31 +02:00
if ( ! TreasureConfig . getInstance ( ) . hylianMap . containsKey ( friendly ) )
return false ;
List < HylianTreasure > treasures = TreasureConfig . getInstance ( ) . hylianMap . get ( friendly ) ;
2013-03-01 06:52:01 +01:00
2013-10-18 14:31:00 +02:00
Player player = getPlayer ( ) ;
2013-10-11 23:47:42 +02:00
2015-11-04 19:35:25 +01:00
if ( treasures . isEmpty ( ) ) {
2013-10-11 23:47:42 +02:00
return false ;
}
2015-11-04 19:35:25 +01:00
int skillLevel = getSkillLevel ( ) ;
2016-03-16 17:47:40 +01:00
Location location = Misc . getBlockCenter ( blockState ) ;
2013-10-11 23:47:42 +02:00
2015-11-04 19:35:25 +01:00
for ( HylianTreasure treasure : treasures ) {
2019-01-25 05:45:26 +01:00
if ( skillLevel > = treasure . getDropLevel ( )
& & RandomChanceUtil . checkRandomChanceExecutionSuccess ( new RandomChanceSkillStatic ( treasure . getDropChance ( ) , getPlayer ( ) , SubSkillType . HERBALISM_HYLIAN_LUCK ) ) ) {
2015-11-04 19:35:25 +01:00
if ( ! EventUtils . simulateBlockBreak ( blockState . getBlock ( ) , player , false ) ) {
return false ;
}
blockState . setType ( Material . AIR ) ;
Misc . dropItem ( location , treasure . getDrop ( ) ) ;
2019-01-14 08:23:48 +01:00
NotificationManager . sendPlayerInformation ( player , NotificationType . SUBSKILL_MESSAGE , " Herbalism.HylianLuck " ) ;
2015-11-04 19:35:25 +01:00
return true ;
}
}
return false ;
2013-03-01 06:52:01 +01:00
}
/ * *
* Process the Shroom Thumb ability .
*
* @param blockState The { @link BlockState } to check ability activation for
* @return true if the ability was successful , false otherwise
* /
public boolean processShroomThumb ( BlockState blockState ) {
Player player = getPlayer ( ) ;
PlayerInventory playerInventory = player . getInventory ( ) ;
2016-03-12 19:35:19 +01:00
if ( ! playerInventory . contains ( Material . BROWN_MUSHROOM , 1 ) ) {
2019-01-14 08:23:48 +01:00
NotificationManager . sendPlayerInformation ( player , NotificationType . REQUIREMENTS_NOT_MET , " Skills.NeedMore " , StringUtils . getPrettyItemString ( Material . BROWN_MUSHROOM ) ) ;
2013-03-01 06:52:01 +01:00
return false ;
}
2016-03-12 19:35:19 +01:00
if ( ! playerInventory . contains ( Material . RED_MUSHROOM , 1 ) ) {
2019-01-14 08:23:48 +01:00
NotificationManager . sendPlayerInformation ( player , NotificationType . REQUIREMENTS_NOT_MET , " Skills.NeedMore " , StringUtils . getPrettyItemString ( Material . RED_MUSHROOM ) ) ;
2013-03-01 06:52:01 +01:00
return false ;
}
playerInventory . removeItem ( new ItemStack ( Material . BROWN_MUSHROOM ) ) ;
playerInventory . removeItem ( new ItemStack ( Material . RED_MUSHROOM ) ) ;
player . updateInventory ( ) ;
2019-01-25 05:45:26 +01:00
if ( ! RandomChanceUtil . isActivationSuccessful ( SkillActivationType . RANDOM_LINEAR_100_SCALE_WITH_CAP , SubSkillType . HERBALISM_SHROOM_THUMB , player ) ) {
2019-02-01 19:34:13 +01:00
NotificationManager . sendPlayerInformation ( player , NotificationType . SUBSKILL_MESSAGE_FAILED , " Herbalism.Ability.ShroomThumb.Fail " ) ;
2013-03-01 06:52:01 +01:00
return false ;
}
return Herbalism . convertShroomThumb ( blockState ) ;
}
/ * *
* Process the Green Thumb ability for plants .
*
* @param blockState The { @link BlockState } to check ability activation for
2013-08-10 20:10:45 +02:00
* @param greenTerra boolean to determine if greenTerra is active or not
2013-03-01 06:52:01 +01:00
* /
2013-03-08 10:11:33 +01:00
private void processGreenThumbPlants ( BlockState blockState , boolean greenTerra ) {
2018-10-13 23:09:08 +02:00
if ( ! BlockUtils . isFullyGrown ( blockState ) )
return ;
2013-03-01 06:52:01 +01:00
Player player = getPlayer ( ) ;
PlayerInventory playerInventory = player . getInventory ( ) ;
2016-03-18 01:18:40 +01:00
Material seed = null ;
2013-10-09 16:27:06 +02:00
switch ( blockState . getType ( ) ) {
2018-07-24 04:13:57 +02:00
case CARROTS :
seed = Material . CARROT ;
2013-10-09 16:27:06 +02:00
break ;
2018-07-24 04:13:57 +02:00
case WHEAT :
seed = Material . WHEAT_SEEDS ;
2013-10-09 16:27:06 +02:00
break ;
2018-08-05 16:15:12 +02:00
case NETHER_WART :
2018-07-24 04:13:57 +02:00
seed = Material . NETHER_WART ;
2013-10-09 16:27:06 +02:00
break ;
2018-07-24 04:13:57 +02:00
case POTATOES :
seed = Material . POTATO ;
2013-10-09 16:27:06 +02:00
break ;
2016-06-26 23:14:19 +02:00
2018-07-24 04:13:57 +02:00
case BEETROOTS :
2016-06-19 23:33:43 +02:00
seed = Material . BEETROOT_SEEDS ;
break ;
2013-10-09 16:27:06 +02:00
2018-09-15 04:44:21 +02:00
case COCOA :
seed = Material . COCOA_BEANS ;
break ;
2013-10-09 16:27:06 +02:00
default :
2016-03-20 18:42:16 +01:00
return ;
2013-10-09 16:27:06 +02:00
}
2013-03-01 06:52:01 +01:00
2017-09-01 05:47:28 +02:00
ItemStack seedStack = new ItemStack ( seed ) ;
2019-03-24 00:21:25 +01:00
if ( ! greenTerra & & ! RandomChanceUtil . checkRandomChanceExecutionSuccess ( player , SubSkillType . HERBALISM_GREEN_THUMB , true ) ) {
2013-03-01 06:52:01 +01:00
return ;
}
2019-07-07 10:42:57 +02:00
if ( ! processGrowingPlants ( blockState , greenTerra ) ) {
2013-03-01 06:52:01 +01:00
return ;
}
2019-03-24 00:21:25 +01:00
if ( ! ItemUtils . isHoe ( getPlayer ( ) . getInventory ( ) . getItemInMainHand ( ) ) )
{
if ( ! playerInventory . containsAtLeast ( seedStack , 1 ) ) {
return ;
}
playerInventory . removeItem ( seedStack ) ;
player . updateInventory ( ) ; // Needed until replacement available
2013-03-01 06:52:01 +01:00
}
2013-03-08 10:11:33 +01:00
2013-03-20 08:11:16 +01:00
new HerbalismBlockUpdaterTask ( blockState ) . runTaskLater ( mcMMO . p , 0 ) ;
2013-03-08 10:11:33 +01:00
}
2019-07-07 10:42:57 +02:00
private boolean processGrowingPlants ( BlockState blockState , boolean greenTerra ) {
2019-01-15 14:35:41 +01:00
int greenThumbStage = getGreenThumbStage ( ) ;
2013-09-05 22:36:02 +02:00
2014-04-06 14:41:40 +02:00
blockState . setMetadata ( mcMMO . greenThumbDataKey , new FixedMetadataValue ( mcMMO . p , ( int ) ( System . currentTimeMillis ( ) / Misc . TIME_CONVERSION_FACTOR ) ) ) ;
2018-08-05 16:15:12 +02:00
Ageable crops = ( Ageable ) blockState . getBlockData ( ) ;
2014-04-06 14:41:40 +02:00
2013-03-08 10:11:33 +01:00
switch ( blockState . getType ( ) ) {
2016-06-26 23:14:19 +02:00
2018-07-27 01:53:29 +02:00
case POTATOES :
case CARROTS :
2018-07-24 04:13:57 +02:00
case WHEAT :
2013-09-05 22:36:02 +02:00
if ( greenTerra ) {
2018-08-01 03:35:45 +02:00
crops . setAge ( 3 ) ;
2013-09-05 22:36:02 +02:00
}
else {
2018-08-01 03:35:45 +02:00
crops . setAge ( greenThumbStage ) ;
2013-09-05 22:36:02 +02:00
}
2018-08-05 16:15:12 +02:00
break ;
2013-09-05 22:36:02 +02:00
2018-08-05 16:15:12 +02:00
case BEETROOTS :
case NETHER_WART :
2013-04-05 04:24:02 +02:00
2013-09-05 22:36:02 +02:00
if ( greenTerra | | greenThumbStage > 2 ) {
2018-08-05 16:15:12 +02:00
crops . setAge ( 2 ) ;
2013-03-08 10:11:33 +01:00
}
2013-09-05 22:36:02 +02:00
else if ( greenThumbStage = = 2 ) {
2018-08-05 16:15:12 +02:00
crops . setAge ( 1 ) ;
2013-09-05 22:36:02 +02:00
}
2013-03-08 10:11:33 +01:00
else {
2018-08-05 16:15:12 +02:00
crops . setAge ( 0 ) ;
2013-03-08 10:11:33 +01:00
}
2018-08-05 16:15:12 +02:00
break ;
2013-03-08 10:11:33 +01:00
case COCOA :
if ( greenTerra | | getGreenThumbStage ( ) > 1 ) {
2018-08-05 16:15:12 +02:00
crops . setAge ( 1 ) ;
2013-03-08 10:11:33 +01:00
}
else {
2018-08-05 16:15:12 +02:00
crops . setAge ( 0 ) ;
2013-03-08 10:11:33 +01:00
}
2018-08-05 16:15:12 +02:00
break ;
2013-03-08 10:11:33 +01:00
default :
return false ;
}
2018-08-05 16:15:12 +02:00
blockState . setBlockData ( crops ) ;
return true ;
2013-03-08 10:11:33 +01:00
}
2019-01-15 14:35:41 +01:00
private int getGreenThumbStage ( ) {
return RankUtils . getRank ( getPlayer ( ) , SubSkillType . HERBALISM_GREEN_THUMB ) ;
2013-03-01 06:52:01 +01:00
}
}