Last active
October 6, 2017 19:29
-
-
Save wallabra/b21a7f6640c3cfb116efbb0d00a39a04 to your computer and use it in GitHub Desktop.
Leaf Sickle plugin prototype for Sponge API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package usr.gustavo6046.sponge.sickle; | |
import java.io.File; | |
import java.io.IOException; | |
import java.nio.charset.Charset; | |
import java.nio.file.Files; | |
import java.nio.file.NoSuchFileException; | |
import java.nio.file.Paths; | |
import java.util.LinkedList; | |
import java.util.Map; | |
import java.util.Optional; | |
import org.spongepowered.api.Sponge; | |
import org.spongepowered.api.block.BlockSnapshot; | |
import org.spongepowered.api.block.BlockState; | |
import org.spongepowered.api.block.BlockTypes; | |
import org.spongepowered.api.data.key.Keys; | |
import org.spongepowered.api.data.persistence.DataFormats; | |
import org.spongepowered.api.data.type.HandTypes; | |
import org.spongepowered.api.entity.Entity; | |
import org.spongepowered.api.entity.EntityTypes; | |
import org.spongepowered.api.entity.living.player.Player; | |
import org.spongepowered.api.event.Listener; | |
import org.spongepowered.api.event.block.ChangeBlockEvent; | |
import org.spongepowered.api.event.block.TargetBlockEvent; | |
import org.spongepowered.api.event.cause.Cause; | |
import org.spongepowered.api.event.cause.EventContext; | |
import org.spongepowered.api.event.cause.EventContextKeys; | |
import org.spongepowered.api.event.game.state.GamePreInitializationEvent; | |
import org.spongepowered.api.event.item.inventory.ClickInventoryEvent; | |
import org.spongepowered.api.item.ItemTypes; | |
import org.spongepowered.api.item.inventory.Inventory; | |
import org.spongepowered.api.item.inventory.ItemStack; | |
import org.spongepowered.api.item.inventory.ItemStackSnapshot; | |
import org.spongepowered.api.item.inventory.crafting.CraftingOutput; | |
import org.spongepowered.api.item.recipe.crafting.Ingredient; | |
import org.spongepowered.api.item.recipe.crafting.ShapedCraftingRecipe; | |
import org.spongepowered.api.plugin.Plugin; | |
import org.spongepowered.api.text.Text; | |
import org.spongepowered.api.text.format.TextColors; | |
import org.spongepowered.api.util.Direction; | |
import org.spongepowered.api.world.Location; | |
import org.spongepowered.api.world.World; | |
import org.spongepowered.api.world.extent.Extent; | |
import usr.gustavo6046.sponge.lib.Capsule; | |
import usr.gustavo6046.sponge.lib.SpongeLinkedList; | |
/** | |
* The official Leaf Sickle plugin for Sponge API. | |
* | |
* @author Gustavo6046 | |
*/ | |
@Plugin(id = "leaf_sickle", name = "Leaf Sickle") | |
public class LeafSicklePlugin | |
{ | |
static class SickleBreakEvent implements TargetBlockEvent | |
{ | |
static class Builder | |
{ | |
Player rootPlayer; | |
BlockSnapshot sourceBlock; | |
LinkedList<Location<World>> destinationBlocks; | |
protected Builder(Player breaker, BlockSnapshot sourceBlock) | |
{ | |
rootPlayer = breaker; | |
this.sourceBlock = sourceBlock; | |
} | |
public Builder addAffectedBlock(Location<World> loc) | |
{ | |
destinationBlocks.push(loc); | |
return this; | |
} | |
public Builder addAllAffectedblocks(LinkedList<Location<World>> locs) | |
{ | |
destinationBlocks.addAll(locs); | |
return this; | |
} | |
public SickleBreakEvent build(boolean breakDestinationBlocks) | |
{ | |
if ( breakDestinationBlocks ) | |
for ( Location<World> loc : destinationBlocks ) | |
loc.setBlock(BlockState.builder() | |
.blockType(BlockTypes.AIR) | |
.build() | |
); | |
return new SickleBreakEvent((Cause.builder() | |
.append(rootPlayer) | |
.append(sourceBlock) | |
.build(EventContext.builder() | |
.add(EventContextKeys.BLOCK_HIT, sourceBlock) | |
.add(EventContextKeys.PLAYER, rootPlayer) | |
.build() | |
) | |
), sourceBlock, destinationBlocks); | |
} | |
} | |
LinkedList<Location<World>> destinationBlocks; | |
BlockSnapshot sourceBlock; | |
Cause reason; | |
protected SickleBreakEvent(Cause builderReason, BlockSnapshot source, LinkedList<Location<World>> dest) | |
{ | |
sourceBlock = source; | |
destinationBlocks = dest; | |
reason = builderReason; | |
} | |
@Override | |
public Cause getCause() { | |
return reason; | |
} | |
@Override | |
public BlockSnapshot getTargetBlock() { | |
return sourceBlock; | |
} | |
} | |
public static SickleBreakEvent.Builder eventBuilder(Player breaker, BlockSnapshot sourceBlock) | |
{ | |
return new SickleBreakEvent.Builder(breaker, sourceBlock); | |
} | |
/** | |
* List of sickles. To keep track of which | |
* Golden Hoes are Sickles ;) | |
*/ | |
public SpongeLinkedList< Capsule<ItemStackSnapshot> > sickles; | |
private ShapedCraftingRecipe sickleRec; | |
private int lastSickleId; | |
protected Map<Player, Inventory> lastCGrids; | |
/** | |
* All the Directions that neighbors a block | |
* orthogonally (i.e. by its 6 faces). | |
*/ | |
private static Direction[] neighDirs = new Direction[] { | |
Direction.DOWN, | |
Direction.UP, | |
Direction.NORTH, | |
Direction.SOUTH, | |
Direction.EAST, | |
Direction.WEST | |
}; | |
/** | |
* Checks if an ItemStack is a | |
* Leaf Sickle™. | |
* | |
* @param other The stack to check for sickles. | |
* @return Whether this stack is a Sickle™ or not. | |
* If it is then also files a takedown | |
* request via DMCA for copyright | |
* infringement :P | |
*/ | |
public boolean isLeafSickle(ItemStack other) | |
{ | |
if ( sickles == null ) | |
{ | |
defaultSickles(); | |
return false; | |
} | |
for ( Capsule<ItemStackSnapshot> capsule : sickles ) | |
if ( capsule.getValue().equals(other.createSnapshot()) ) | |
return true; | |
return false; | |
} | |
/** | |
* Iterates on a block and breaks all blocks around it. | |
* | |
* @param loc The location on which to iterate. | |
* @param depth The current depth of iteration (usually one when starting). | |
* @param maxDepth The max depth of iteration. | |
* @param player The causing player. | |
* @param breaked The LinkedList to which append broken blocks. | |
*/ | |
public void iterateBlockBreak( | |
Location<World> loc, | |
int depth, | |
int maxDepth, | |
Player player, | |
LinkedList<Location<World>> breaked | |
) | |
{ | |
if ( !loc.getBlock().getType().equals(BlockTypes.LEAVES) ) | |
return; | |
ItemStack item = ItemStack.builder().fromBlockState(loc.getBlock()).build(); | |
Extent extent = loc.getExtent(); | |
Entity itemEntity = extent.createEntity(EntityTypes.ITEM, loc.getPosition()); | |
if ( itemEntity == null ) | |
return; | |
itemEntity.offer(Keys.REPRESENTED_ITEM, item.createSnapshot()); | |
extent.spawnEntity(itemEntity); | |
breaked.push(loc); | |
loc.setBlock(BlockState.builder() | |
.blockType(BlockTypes.AIR) | |
.build() | |
); | |
// ...and now the fun part! | |
if ( depth <= maxDepth ) | |
for ( Direction dir : neighDirs ) | |
iterateBlockBreak(loc.getBlockRelative(dir), depth + 1, maxDepth, player, breaked); | |
} | |
/** | |
* Checks for crafted Special Items and | |
* replaces them by a functioning version. | |
* | |
* Explanation: recipes usually generate | |
* a copy of the items, but this plugin | |
* requires these items to be stored in | |
* a LinkedList using a function. | |
* | |
* Of course, usually this function | |
* is not called with Recipes, so we try | |
* and catch when the items are crafted | |
* so as to check if they are Sickles and | |
* add them to the linked list. | |
* | |
* On block break, the item that broke blocks | |
* is compared with the list; if it is in the | |
* list, then the special power of Leaf Sickles™ | |
* activates :) | |
* | |
* | |
* @param event The inventory event to trigger | |
* the crafting. | |
*/ | |
@Listener | |
public void onCraft(ClickInventoryEvent event) | |
{ | |
Optional<Player> _player = event.getCause().first(Player.class); | |
if ( !_player.isPresent() ) | |
return; | |
Player player = _player.get(); | |
Inventory inventory = event.getTargetInventory(); | |
Inventory output = inventory.query(CraftingOutput.class); | |
if ( nonEmptyInv(output) && output.peek().isPresent() && output.peek().get().equalTo(generateExampleSickle()) ) | |
{ | |
ItemStack sickle = generateSickle(null); | |
output.offer(sickle); | |
System.out.println("Made Leaf Sickle "+sickle+" for player "+player.getIdentifier()); | |
} | |
} | |
private boolean nonEmptyInv(Inventory output) { | |
return output.capacity() > 0; | |
} | |
/** | |
* Listens on block breaking events. | |
* | |
* @param event The event on which to listen. | |
*/ | |
@Listener | |
public void onBlockBreak(ChangeBlockEvent.Break event) | |
{ | |
Optional<Player> _player = event.getCause().first(Player.class); | |
if ( !_player.isPresent() ) | |
return; | |
Player player = _player.get(); | |
Optional<ItemStack> _stack; | |
if ( !(_stack = player.getItemInHand(HandTypes.MAIN_HAND)).isPresent() || !isLeafSickle(_stack.get())) | |
{ | |
if ( _stack.isPresent() ) | |
System.out.println("tool="+_stack.get()); | |
else | |
System.out.println("tool=BARE_HANDS"); | |
return; | |
} | |
event.getTransactions().stream().forEach((trans) -> | |
{ | |
Location<World> block; | |
if ( ( block = trans.getOriginal().getLocation().get() ).getBlock().getType().equals(BlockTypes.LEAVES) ) | |
{ | |
LinkedList<Location<World>> breaked = new LinkedList<Location<World>>(); | |
System.out.println("Breaking all blocks in a radius around: " + block.getPosition()); | |
iterateBlockBreak(block, 1, 7, player, breaked); | |
Sponge.getEventManager().post(eventBuilder(player, trans.getOriginal()) | |
.addAllAffectedblocks(breaked) | |
.build(false) | |
); | |
} | |
}); | |
} | |
private void defaultSickles() | |
{ | |
sickles = new SpongeLinkedList< Capsule<ItemStackSnapshot> >(); | |
saveSickles(); | |
} | |
public void saveSickles() | |
{ | |
File f; | |
(f = new File("config/LeafSickle/data/sickles.json")).getParentFile().mkdirs(); | |
try | |
{ | |
Files.write(f.toPath(), | |
Charset | |
.forName("UTF-8") | |
.encode(DataFormats.JSON.write(sickles.toContainer())) | |
.array() | |
); | |
} | |
catch ( IOException e1 ) | |
{ | |
e1.printStackTrace(); | |
return; | |
} | |
} | |
/** | |
* Ran when the plugin is called (equivalent to | |
* PreBeginPlay in Unreal). | |
* | |
* @param evt The event on which to listen | |
* @throws IOException | |
*/ | |
@SuppressWarnings("unchecked") | |
@Listener | |
public void onPreInit(GamePreInitializationEvent evt) throws IOException | |
{ | |
// ============= | |
// Configuration Stuff | |
String json; | |
try | |
{ | |
json = new String( | |
Files.readAllBytes(Paths.get("config/leafsickle/data/sickles.json")), | |
"utf-8" | |
); | |
} | |
catch ( NoSuchFileException err ) | |
{ | |
defaultSickles(); | |
json = ""; | |
} | |
Optional<SpongeLinkedList<Capsule<ItemStackSnapshot>>> _sickles; | |
if ( json.equals("") ) | |
_sickles = Optional.empty(); | |
else | |
_sickles = Sponge.getDataManager().deserialize(( Class< SpongeLinkedList< Capsule<ItemStackSnapshot> > > )(Object)SpongeLinkedList.class, DataFormats.JSON.read(json)); | |
if ( _sickles.isPresent() ) | |
sickles = _sickles.get(); | |
else | |
defaultSickles(); | |
// ============= | |
Ingredient stick = Ingredient.of(ItemTypes.STICK); | |
Ingredient goldBar = Ingredient.of(ItemTypes.GOLD_INGOT); | |
sickleRec = ShapedCraftingRecipe.builder() | |
.aisle( | |
"gg ", | |
"s g", | |
"s " | |
) | |
.where('s', stick) | |
.where('g', goldBar) | |
.aisle( | |
" gg", | |
"g s", | |
" s" | |
) | |
.where('s', stick) | |
.where('g', goldBar) | |
.result(generateExampleSickle()) | |
.build("leaf-sickle", this); | |
Sponge | |
.getRegistry() | |
.getCraftingRecipeRegistry() | |
.register(sickleRec); | |
} | |
/** | |
* Generates an example (item data only) | |
* Leaf Sickle™ and returns it. | |
* | |
* @return Duh. | |
*/ | |
public ItemStack generateExampleSickle() | |
{ | |
ItemStack sickle = ItemStack.builder() | |
.itemType(ItemTypes.GOLDEN_HOE) | |
.build(); | |
sickle.offer(Keys.DISPLAY_NAME, Text.of(TextColors.LIGHT_PURPLE, "Leaf Sickle")); | |
return sickle; | |
} | |
/** | |
* This function generates a Leaf Sickle item | |
* and registers it. | |
* | |
* @param id A Capsule that holds an Integer object, | |
* which is set to the SickleID of the new | |
* Sickle. | |
* @return The item. | |
*/ | |
public ItemStack generateSickle(Capsule<Integer> id) | |
{ | |
ItemStack sickle = generateExampleSickle(); | |
if ( id != null ) | |
id.set(lastSickleId); | |
lastSickleId++; | |
Capsule<ItemStackSnapshot> resSnap = new Capsule<ItemStackSnapshot>(sickle.createSnapshot()); | |
sickles.push(resSnap); | |
saveSickles(); | |
return sickle; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment