summaryrefslogtreecommitdiff
path: root/src/main/java/com/redstoner/modules/signalstrength/SignalStrength.java
blob: 2480bfc880f890b09e14ee19f7d670b3172da9a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package com.redstoner.modules.signalstrength;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;

import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.Nameable;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;

import com.nemez.cmdmgr.Command;
import com.redstoner.annotations.Commands;
import com.redstoner.annotations.Version;
import com.redstoner.misc.CommandHolderType;
import com.redstoner.modules.Module;

@Commands(CommandHolderType.File)
@Version(major = 5, minor = 0, revision = 0, compatible = 4)
public class SignalStrength implements Module
{
	
	private static final String namePrefix = ChatColor.DARK_PURPLE + "Signal Strength: " + ChatColor.RED + ChatColor.BOLD;
	
	private static String nameForSignalStrength(int strength)
	{
		return namePrefix + strength;
	}
	
	private static boolean isSignalStrengthNameOrEmpty(String name)
	{
		return name == null || name.isEmpty() || name.startsWith(namePrefix);
	}
	
	@Command(hook = "ss")
	public boolean ss(CommandSender sender, int strength)
	{
		return ssm(sender, strength, Material.REDSTONE.toString());
	}
	
	@Command(hook = "ssm")
	public boolean ssm(CommandSender sender, int strength, String material)
	{
		if (strength < 0 || strength > 15)
		{
			getLogger().message(sender, true, "The strength must be between 0 and 15!");
			return true;
		}
		
		Player player = (Player) sender;
		if (player.getGameMode() != GameMode.CREATIVE)
		{
			getLogger().message(sender, true, "You must be in creative mode to do that");
			return true;
		}
		
		Material itemType = Material.matchMaterial(material);
		if (itemType == null)
		{
			getLogger().message(sender, true, "The material " + material + " could not be recognized");
			return true;
		}
		
		// Empty set in the first argument would make it always return the first block, because no block types are
		// considered to be transparent. Only a value of null is treated as "air only".
		Block targetBlock = player.getTargetBlock((Set<Material>) null, 5);
		if (targetBlock == null)
		{
			getLogger().message(sender, true, "That command can only be used if a container is targeted!");
			return true;
		}
		Inventory inventory = getInventory(targetBlock);
		if (inventory == null)
		{
			getLogger().message(sender, true, "That command can only be used if a container is targeted!");
			return true;
		}
		
		// --------Get the stack size and required amount of items to achieve the desired signal strength---------
		int stackSize = itemType.getMaxStackSize();
		int slotCount = inventory.getSize();
		int itemCount = computeRequiredItemCount(strength, stackSize, slotCount);
		if (itemCount == -1)
		{
			getLogger().message(sender, true,
					"The desired signal strength could not be achieved with the requested item type");
			return true;
		}
		// #--------Add the other side of the chest if target is a double chest and check if player can build---------
		ArrayList<Block> containerBlocks = new ArrayList<>();
		containerBlocks.add(targetBlock);
		
		Material blockType = targetBlock.getType();
		if (inventory.getType() == InventoryType.CHEST)
		{
			Arrays.stream(new BlockFace[] {BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST, BlockFace.NORTH})
					.map(targetBlock::getRelative).filter(b -> b.getType() == blockType).forEach(containerBlocks::add);
		}
		
		for (Block containerBlock : containerBlocks)
		{
			if (!canBuild(player, containerBlock))
			{
				getLogger().message(sender, true, "You can not build here!");
				return true;
			}
		}
		// #----------------Insert items-------------
		int fullStackCount = itemCount / stackSize;
		int remaining = itemCount % stackSize;
		for (Block containerBlock : containerBlocks)
		{
			// Below checks should evaluate to false, but let's be safe.
			BlockState blockState = containerBlock.getState();
			if (!(blockState instanceof InventoryHolder))
				continue;
			
			if (blockState instanceof Nameable && isSignalStrengthNameOrEmpty(((Nameable) blockState).getCustomName()))
			{
				((Nameable) blockState).setCustomName(nameForSignalStrength(strength));
				blockState.update();
			}
			
			Inventory inv = ((InventoryHolder) blockState).getInventory();
			if (inv == null)
				continue;
			
			inv.clear();
			for (int i = 0; i < fullStackCount; i++)
				inv.setItem(i, new ItemStack(itemType, stackSize));
			if (remaining > 0)
				inv.setItem(fullStackCount, new ItemStack(itemType, remaining));
			
		}
		getLogger().message(sender, "Comparators attached to this " + enumNameToHumanName(blockType.name())
				+ " will now put out a signal strength of " + strength);
		return true;
	}
	
	private static Inventory getInventory(Block b)
	{
		BlockState state = b.getState();
		if (state instanceof InventoryHolder)
			return ((InventoryHolder) state).getInventory();
		return null;
	}
	
	private static int computeRequiredItemCount(int strength, int stackSize, int slotCount)
	{
		int itemCount = -1;
		if (strength == 0)
			itemCount = 0;
		else if (strength == 1)
			itemCount = 1;
		else
			itemCount = (int) Math.ceil(slotCount * stackSize / 14.0 * (strength - 1));
		
		// Reverse engineer the calculation to verify
		int resultingStrength = itemCount == 0 ? 0 : (int) Math.floor(1 + 14.0 * itemCount / stackSize / slotCount);
		if (resultingStrength != strength)
		{
			return -1;
		}
		// Clarification on these formulas at https://minecraft.gamepedia.com/Redstone_Comparator#Containers
		return itemCount;
	}
	
	private static boolean canBuild(Player p, Block b)
	{
		BlockPlaceEvent event = new BlockPlaceEvent(b, b.getState(), b.getRelative(BlockFace.DOWN),
				p.getInventory().getItemInMainHand(), p, true, EquipmentSlot.HAND);
		Bukkit.getPluginManager().callEvent(event);
		return !event.isCancelled();
	}
	
	private static String enumNameToHumanName(String enumName)
	{
		return enumName.toLowerCase().replace('_', ' ');
	}
	
}