Add implementation for variety of attribute and base attribute hierarchies, add in JSON encoding for itemstack-item pairing to hold stats and information about items without unintuitive print & parse methods. Temporarily disable donation related commands in DonationManager for testing, as it was presenting compilation issues. Introduce further implementation of LegendaryItems. Add various utility methods for JSON encoding (utilizes GSON). Fix various bugs seen in internal testing.
This commit is contained in:
parent
a8a8d4961a
commit
167549c9e8
@ -40,9 +40,10 @@ public class DonationManager extends MiniDbClientPlugin<Donor>
|
|||||||
@Override
|
@Override
|
||||||
public void addCommands()
|
public void addCommands()
|
||||||
{
|
{
|
||||||
addCommand(new GemCommand(this));
|
// TODO: Re-add commands? Where are command implementations, seen as missing at the moment.
|
||||||
addCommand(new CoinCommand(this));
|
//addCommand(new GemCommand(this));
|
||||||
addCommand(new GoldCommand(this));
|
//addCommand(new CoinCommand(this));
|
||||||
|
//addCommand(new GoldCommand(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
|
@ -8,5 +8,6 @@
|
|||||||
<classpathentry combineaccessrules="false" kind="src" path="/Mineplex.Minecraft.Game.ClassCombat"/>
|
<classpathentry combineaccessrules="false" kind="src" path="/Mineplex.Minecraft.Game.ClassCombat"/>
|
||||||
<classpathentry combineaccessrules="false" kind="src" path="/Mineplex.Minecraft.Game.Core"/>
|
<classpathentry combineaccessrules="false" kind="src" path="/Mineplex.Minecraft.Game.Core"/>
|
||||||
<classpathentry kind="var" path="REPO_DIR/Plugins/Libraries/craftbukkit.jar" sourcepath="/REPO_DIR/GitHubLibraries/CraftBukkit/src"/>
|
<classpathentry kind="var" path="REPO_DIR/Plugins/Libraries/craftbukkit.jar" sourcepath="/REPO_DIR/GitHubLibraries/CraftBukkit/src"/>
|
||||||
|
<classpathentry kind="var" path="REPO_DIR/Plugins/Libraries/gson-2.2.1.jar"/>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
@ -17,6 +17,7 @@ import mineplex.core.itemstack.ItemStackFactory;
|
|||||||
import mineplex.core.memory.MemoryFix;
|
import mineplex.core.memory.MemoryFix;
|
||||||
import mineplex.core.message.MessageManager;
|
import mineplex.core.message.MessageManager;
|
||||||
import mineplex.core.monitor.LagMeter;
|
import mineplex.core.monitor.LagMeter;
|
||||||
|
import mineplex.core.packethandler.PacketHandler;
|
||||||
import mineplex.core.portal.Portal;
|
import mineplex.core.portal.Portal;
|
||||||
import mineplex.core.preferences.PreferencesManager;
|
import mineplex.core.preferences.PreferencesManager;
|
||||||
import mineplex.core.punish.Punish;
|
import mineplex.core.punish.Punish;
|
||||||
@ -94,7 +95,9 @@ public class Clans extends JavaPlugin
|
|||||||
new BuildingShop(clans, _clientManager, _donationManager);
|
new BuildingShop(clans, _clientManager, _donationManager);
|
||||||
new PvpShop(clans, _clientManager, _donationManager);
|
new PvpShop(clans, _clientManager, _donationManager);
|
||||||
|
|
||||||
GearManager customGear = new GearManager(this);
|
// Enable custom-gear related managers
|
||||||
|
PacketHandler packetHandler = new PacketHandler(this);
|
||||||
|
GearManager customGear = new GearManager(this, packetHandler);
|
||||||
|
|
||||||
//Updates
|
//Updates
|
||||||
getServer().getScheduler().scheduleSyncRepeatingTask(this, new Updater(this), 1, 1);
|
getServer().getScheduler().scheduleSyncRepeatingTask(this, new Updater(this), 1, 1);
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package mineplex.game.clans.clans;
|
||||||
|
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.Color;
|
||||||
|
|
||||||
|
public class ChunkData
|
||||||
|
{
|
||||||
|
|
||||||
|
private int _x;
|
||||||
|
public int getX() { return _x; }
|
||||||
|
|
||||||
|
private int _z;
|
||||||
|
public int getZ() { return _z; }
|
||||||
|
|
||||||
|
private ChatColor _color;
|
||||||
|
public ChatColor getColor() { return _color; }
|
||||||
|
|
||||||
|
private String _clanName;
|
||||||
|
public String getClanName() { return _clanName; }
|
||||||
|
|
||||||
|
public ChunkData(int x, int z, ChatColor color, String clanName)
|
||||||
|
{
|
||||||
|
_x = x;
|
||||||
|
_z = z;
|
||||||
|
_color = color;
|
||||||
|
_clanName = clanName;
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,15 @@
|
|||||||
package mineplex.game.clans.clans;
|
package mineplex.game.clans.clans;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.Chunk;
|
import org.bukkit.Chunk;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import mineplex.core.common.util.C;
|
import mineplex.core.common.util.C;
|
||||||
@ -33,7 +37,49 @@ public class ClansUtility
|
|||||||
ADMIN,
|
ADMIN,
|
||||||
SAFE
|
SAFE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param location
|
||||||
|
* @param radius
|
||||||
|
* @return a 2D array of {@link ClanTerritory} with uniform dimension of ({@code radius} * 2 + 1). The region represented by
|
||||||
|
* the array of territories is centered on {@code location} chunk with a given chunk {@code radius}.
|
||||||
|
*/
|
||||||
|
public List<ChunkData> getTerritory(Location location, int radius, ClanInfo surveyorClan)
|
||||||
|
{
|
||||||
|
World world = location.getWorld();
|
||||||
|
Chunk chunk = location.getChunk();
|
||||||
|
int chunkX = chunk.getX();
|
||||||
|
int chunkZ = chunk.getZ();
|
||||||
|
int width = radius*2 + 1;
|
||||||
|
|
||||||
|
List<ChunkData> chunks = new ArrayList<ChunkData>();
|
||||||
|
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
for (int z = 0; z < width; z++)
|
||||||
|
{
|
||||||
|
int territoryX = chunkX - radius + x;
|
||||||
|
int territoryZ = chunkZ - radius + z;
|
||||||
|
ClanTerritory territory = getClaim(world.getChunkAt(territoryX, territoryZ));
|
||||||
|
|
||||||
|
if (territory != null)
|
||||||
|
{
|
||||||
|
ClanInfo clan = getOwner(territory);
|
||||||
|
String clanName = territory.Owner;
|
||||||
|
ClanRelation relationship = rel(surveyorClan, clan);
|
||||||
|
ChatColor color = relChatColor(relationship, false);
|
||||||
|
|
||||||
|
ChunkData data = new ChunkData(territoryX, territoryZ, color, clanName);
|
||||||
|
|
||||||
|
chunks.add(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunks;
|
||||||
|
}
|
||||||
|
|
||||||
public ClanInfo searchClanPlayer(Player caller, String name, boolean inform)
|
public ClanInfo searchClanPlayer(Player caller, String name, boolean inform)
|
||||||
{
|
{
|
||||||
//CLAN
|
//CLAN
|
||||||
@ -197,11 +243,16 @@ public class ClansUtility
|
|||||||
|
|
||||||
return clan.getHome().getChunk().equals(chunk);
|
return clan.getHome().getChunk().equals(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ClanTerritory getClaim(Chunk chunk)
|
||||||
|
{
|
||||||
|
String chunkTag = UtilWorld.chunkToStr(chunk);
|
||||||
|
return Clans.getClaimMap().get(chunkTag);
|
||||||
|
}
|
||||||
|
|
||||||
public ClanTerritory getClaim(Location loc)
|
public ClanTerritory getClaim(Location loc)
|
||||||
{
|
{
|
||||||
String chunk = UtilWorld.chunkToStr(loc.getChunk());
|
return getClaim(loc.getChunk());
|
||||||
return Clans.getClaimMap().get(chunk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClanTerritory getClaim(String chunk)
|
public ClanTerritory getClaim(String chunk)
|
||||||
|
@ -4,12 +4,15 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import mineplex.game.clans.items.attributes.ItemAttribute;
|
import mineplex.game.clans.items.attributes.ItemAttribute;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a customizable wrapper for an {@link ItemStack}, enabling the possession
|
* Represents a customizable wrapper for an {@link ItemStack}, enabling the possession
|
||||||
@ -20,11 +23,34 @@ import org.bukkit.inventory.ItemStack;
|
|||||||
public class CustomItem
|
public class CustomItem
|
||||||
{
|
{
|
||||||
|
|
||||||
private ItemAttribute superPrefix;
|
private ItemAttribute _superPrefix;
|
||||||
private ItemAttribute prefix;
|
public void setSuperPrefix(ItemAttribute attribute) { _superPrefix = attribute; }
|
||||||
private ItemAttribute suffix;
|
|
||||||
|
private ItemAttribute _prefix;
|
||||||
|
public void setPrefix(ItemAttribute attribute) { _prefix = attribute; }
|
||||||
|
|
||||||
|
private ItemAttribute _suffix;
|
||||||
|
public void setSuffix(ItemAttribute attribute) { _suffix = attribute; }
|
||||||
|
|
||||||
private String _displayName;
|
private String _displayName;
|
||||||
|
private String _description;
|
||||||
|
private Material _material;
|
||||||
|
|
||||||
|
private String _uuid;
|
||||||
|
public String getUuid() { return _uuid; }
|
||||||
|
|
||||||
|
public CustomItem(String displayName, String description, Material material)
|
||||||
|
{
|
||||||
|
_displayName = displayName;
|
||||||
|
_description = description;
|
||||||
|
_material = material;
|
||||||
|
_uuid = UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomItem(Material material)
|
||||||
|
{
|
||||||
|
this(material.toString(), null, material); // TODO: Prettify item materal name
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the name displayed to players for the item.
|
* @return the name displayed to players for the item.
|
||||||
@ -34,19 +60,19 @@ public class CustomItem
|
|||||||
// Concatenate attribute prefixes/suffixes to display name.
|
// Concatenate attribute prefixes/suffixes to display name.
|
||||||
String display = _displayName;
|
String display = _displayName;
|
||||||
|
|
||||||
if (prefix != null)
|
if (_prefix != null)
|
||||||
{
|
{
|
||||||
display = prefix.getDisplayName() + " " + display;
|
display = _prefix.getDisplayName() + " " + display;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (superPrefix != null)
|
if (_superPrefix != null)
|
||||||
{
|
{
|
||||||
display = superPrefix.getDisplayName() + " " + display;
|
display = _superPrefix.getDisplayName() + " " + display;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (suffix != null)
|
if (_suffix != null)
|
||||||
{
|
{
|
||||||
display += " of " + suffix.getDisplayName();
|
display += " of " + _suffix.getDisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
return display;
|
return display;
|
||||||
@ -54,22 +80,46 @@ public class CustomItem
|
|||||||
|
|
||||||
public List<String> getLore()
|
public List<String> getLore()
|
||||||
{
|
{
|
||||||
|
String serialization = GearManager.getItemSerialization(this);
|
||||||
|
|
||||||
List<String> lore = new ArrayList<String>();
|
List<String> lore = new ArrayList<String>();
|
||||||
|
|
||||||
// TODO: Add decorative stat lore (and hidden json-encoding/uuid info)
|
if (_description != null)
|
||||||
lore.add("Custom stat lore string!");
|
{
|
||||||
|
lore.add(_description);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display attribute descriptions and stats in lore
|
||||||
|
for (ItemAttribute attribute : getAttributes())
|
||||||
|
{
|
||||||
|
String attributeLine = attribute.getDisplayName() + " - " + attribute.getDescription();
|
||||||
|
lore.add(attributeLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tack on serialized JSON encoded line for utility purposes. (Not seen by user)
|
||||||
|
List<String> serializedLines = new ArrayList<String>();
|
||||||
|
String[] seri = serialization.split("\n");
|
||||||
|
for (String line : seri)
|
||||||
|
{
|
||||||
|
serializedLines.add(line);
|
||||||
|
}
|
||||||
|
lore.addAll(serializedLines);
|
||||||
|
|
||||||
return lore;
|
return lore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemStack toItemStack()
|
public ItemStack toItemStack(int amount)
|
||||||
{
|
{
|
||||||
// TODO: Generate an item stack representing this CustomItem
|
ItemStack item = new ItemStack(_material, amount);
|
||||||
return null;
|
update(item);
|
||||||
|
|
||||||
|
// TODO: Add non-descript enchantment for glowing efect?
|
||||||
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onInteract(PlayerInteractEvent event)
|
public void onInteract(PlayerInteractEvent event)
|
||||||
{
|
{
|
||||||
|
System.out.println("Triggered interact!");
|
||||||
for (ItemAttribute attribute : getAttributes())
|
for (ItemAttribute attribute : getAttributes())
|
||||||
{
|
{
|
||||||
attribute.onInteract(event);
|
attribute.onInteract(event);
|
||||||
@ -78,6 +128,7 @@ public class CustomItem
|
|||||||
|
|
||||||
public void onAttack(EntityDamageByEntityEvent event)
|
public void onAttack(EntityDamageByEntityEvent event)
|
||||||
{
|
{
|
||||||
|
System.out.println("Triggered attack!");
|
||||||
for (ItemAttribute attribute : getAttributes())
|
for (ItemAttribute attribute : getAttributes())
|
||||||
{
|
{
|
||||||
attribute.onAttack(event);
|
attribute.onAttack(event);
|
||||||
@ -86,6 +137,7 @@ public class CustomItem
|
|||||||
|
|
||||||
public void onAttacked(EntityDamageByEntityEvent event)
|
public void onAttacked(EntityDamageByEntityEvent event)
|
||||||
{
|
{
|
||||||
|
System.out.println("Triggered damage!");
|
||||||
for (ItemAttribute attribute : getAttributes())
|
for (ItemAttribute attribute : getAttributes())
|
||||||
{
|
{
|
||||||
attribute.onAttacked(event);
|
attribute.onAttacked(event);
|
||||||
@ -98,9 +150,36 @@ public class CustomItem
|
|||||||
public Set<ItemAttribute> getAttributes()
|
public Set<ItemAttribute> getAttributes()
|
||||||
{
|
{
|
||||||
Set<ItemAttribute> attributes = new HashSet<ItemAttribute>();
|
Set<ItemAttribute> attributes = new HashSet<ItemAttribute>();
|
||||||
attributes.add(superPrefix);
|
if (_superPrefix != null) attributes.add(_superPrefix);
|
||||||
attributes.add(prefix);
|
if (_prefix != null) attributes.add(_prefix);
|
||||||
attributes.add(suffix);
|
if (_suffix != null) attributes.add(_suffix);
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param item - the item to check for a matching link
|
||||||
|
* @return true, if {@code item} matches this CustomItem via UUID, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean matches(CustomItem item)
|
||||||
|
{
|
||||||
|
return item.getUuid().equals(_uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update {@code item} with the proper meta properties suited for this
|
||||||
|
* {@link CustomItem}.
|
||||||
|
* @param item - the item whose meta properties are being updated to become a version of this updated custom item.
|
||||||
|
*/
|
||||||
|
public void update(ItemStack item)
|
||||||
|
{
|
||||||
|
ItemMeta meta = item.getItemMeta();
|
||||||
|
|
||||||
|
String displayName = getDisplayName();
|
||||||
|
List<String> lore = getLore();
|
||||||
|
|
||||||
|
meta.setDisplayName(displayName);
|
||||||
|
meta.setLore(lore);
|
||||||
|
|
||||||
|
item.setItemMeta(meta);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,56 +1,101 @@
|
|||||||
package mineplex.game.clans.items;
|
package mineplex.game.clans.items;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import mineplex.core.MiniPlugin;
|
import mineplex.core.MiniPlugin;
|
||||||
import mineplex.core.account.CoreClientManager;
|
import mineplex.core.account.CoreClientManager;
|
||||||
|
import mineplex.core.common.util.UtilServer;
|
||||||
|
import mineplex.core.packethandler.IPacketHandler;
|
||||||
|
import mineplex.core.packethandler.PacketHandler;
|
||||||
|
import mineplex.core.packethandler.PacketInfo;
|
||||||
import mineplex.core.portal.TransferHandler;
|
import mineplex.core.portal.TransferHandler;
|
||||||
import mineplex.core.portal.Commands.SendCommand;
|
import mineplex.core.portal.Commands.SendCommand;
|
||||||
import mineplex.core.portal.Commands.ServerCommand;
|
import mineplex.core.portal.Commands.ServerCommand;
|
||||||
|
import mineplex.game.clans.items.attributes.ItemAttribute;
|
||||||
|
import mineplex.game.clans.items.attributes.weapon.FlamingAttribute;
|
||||||
|
import mineplex.game.clans.items.attributes.weapon.FrostedAttribute;
|
||||||
|
import mineplex.game.clans.items.attributes.weapon.SharpAttribute;
|
||||||
import mineplex.game.clans.items.commands.GearCommand;
|
import mineplex.game.clans.items.commands.GearCommand;
|
||||||
import mineplex.game.clans.items.generation.Weight;
|
import mineplex.game.clans.items.generation.Weight;
|
||||||
import mineplex.game.clans.items.generation.WeightSet;
|
import mineplex.game.clans.items.generation.WeightSet;
|
||||||
|
import mineplex.game.clans.items.legendaries.AlligatorsTooth;
|
||||||
|
import mineplex.game.clans.items.legendaries.WindBlade;
|
||||||
|
import mineplex.game.clans.items.smelting.SmeltingListener;
|
||||||
import mineplex.serverdata.Region;
|
import mineplex.serverdata.Region;
|
||||||
import mineplex.serverdata.Utility;
|
import mineplex.serverdata.Utility;
|
||||||
import mineplex.serverdata.commands.ServerCommandManager;
|
import mineplex.serverdata.commands.ServerCommandManager;
|
||||||
import mineplex.serverdata.commands.TransferCommand;
|
import mineplex.serverdata.commands.TransferCommand;
|
||||||
|
import mineplex.serverdata.serialization.RuntimeTypeAdapterFactory;
|
||||||
import mineplex.serverdata.servers.ServerManager;
|
import mineplex.serverdata.servers.ServerManager;
|
||||||
|
import net.minecraft.server.v1_7_R4.Packet;
|
||||||
|
import net.minecraft.server.v1_7_R4.PacketPlayOutSetSlot;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages creation and retrieval of associated {@link PlayerGear}s with online players, as well
|
* Manages creation and retrieval of associated {@link PlayerGear}s with online players, as well
|
||||||
* as offering methods for parsing and handling {@link CustomItem}s.
|
* as offering methods for parsing and handling {@link CustomItem}s.
|
||||||
* @author MrTwiggy
|
* @author MrTwiggy
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class GearManager extends MiniPlugin
|
public class GearManager extends MiniPlugin implements IPacketHandler
|
||||||
{
|
{
|
||||||
|
private static final String ITEM_SERIALIZATION_TAG = "-JSON-";
|
||||||
private static GearManager _instance; // Singleton instance
|
private static GearManager _instance; // Singleton instance
|
||||||
|
|
||||||
|
|
||||||
private Map<String, PlayerGear> playerGears; // Mapping of player names (key) to cached gear set (value).
|
private Map<String, PlayerGear> playerGears; // Mapping of player names (key) to cached gear set (value).
|
||||||
private WeightSet<Integer> _attributeWeights; // Weightings for randomly selecting number of attributes (1, 2, 3)
|
private WeightSet<Integer> _attributeWeights; // Weightings for randomly selecting number of attributes (1, 2, 3)
|
||||||
private WeightSet<Boolean> _itemWeights; // Weightings for randomly selecting item type (legendary/rare)
|
private WeightSet<Boolean> _itemWeights; // Weightings for randomly selecting item type (legendary/rare)
|
||||||
private WeightSet<Boolean> _gearWeights; // Weightings for randomly selecting gear type (armour/weapon)
|
private WeightSet<Boolean> _gearWeights; // Weightings for randomly selecting gear type (armour/weapon)
|
||||||
|
private static Gson _gson;
|
||||||
|
|
||||||
public GearManager(JavaPlugin plugin)
|
public GearManager(JavaPlugin plugin, PacketHandler packetHandler)
|
||||||
{
|
{
|
||||||
super("CustomGear", plugin);
|
super("CustomGear", plugin);
|
||||||
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
|
|
||||||
playerGears = new HashMap<String, PlayerGear>();
|
playerGears = new HashMap<String, PlayerGear>();
|
||||||
|
// TODO: Introduce configurable non-hardcoded values for generation weights?
|
||||||
_attributeWeights = new WeightSet<Integer>(new Weight<Integer>(3, 3), new Weight<Integer>(20, 2), new Weight<Integer>(77, 1));
|
_attributeWeights = new WeightSet<Integer>(new Weight<Integer>(3, 3), new Weight<Integer>(20, 2), new Weight<Integer>(77, 1));
|
||||||
_itemWeights = new WeightSet<Boolean>(new Weight<Boolean>(90, true), new Weight<Boolean>(10, false));
|
_itemWeights = new WeightSet<Boolean>(new Weight<Boolean>(90, true), new Weight<Boolean>(10, false));
|
||||||
_itemWeights = new WeightSet<Boolean>(new Weight<Boolean>(50, true), new Weight<Boolean>(50, false));
|
_itemWeights = new WeightSet<Boolean>(new Weight<Boolean>(50, true), new Weight<Boolean>(50, false));
|
||||||
|
|
||||||
System.out.println("-Testting-testing");
|
System.out.println("-Testting-testing");
|
||||||
System.out.println(Utility.currentTimeSeconds());
|
System.out.println(Utility.currentTimeSeconds());
|
||||||
|
|
||||||
|
// Register listeners
|
||||||
|
UtilServer.getServer().getPluginManager().registerEvents(new ItemListener(), getPlugin());
|
||||||
|
UtilServer.getServer().getPluginManager().registerEvents(new SmeltingListener(), getPlugin());
|
||||||
|
|
||||||
|
// adding all different container classes with their flag
|
||||||
|
RuntimeTypeAdapterFactory<ItemAttribute> typeFactory = RuntimeTypeAdapterFactory
|
||||||
|
.of(ItemAttribute.class, "AttributeType")
|
||||||
|
.registerSubtype(SharpAttribute.class)
|
||||||
|
.registerSubtype(FrostedAttribute.class)
|
||||||
|
.registerSubtype(FlamingAttribute.class); // TODO: Register all item attributes automatically
|
||||||
|
|
||||||
|
RuntimeTypeAdapterFactory<CustomItem> customItemType = RuntimeTypeAdapterFactory
|
||||||
|
.of(CustomItem.class, "ItemType")
|
||||||
|
.registerSubtype(AlligatorsTooth.class)
|
||||||
|
.registerSubtype(WindBlade.class); // TODO: Register all legendary weapons automatically
|
||||||
|
|
||||||
|
GsonBuilder builder = new GsonBuilder();
|
||||||
|
builder.registerTypeAdapterFactory(typeFactory);
|
||||||
|
builder.registerTypeAdapterFactory(customItemType);
|
||||||
|
_gson = builder.create();
|
||||||
|
|
||||||
|
packetHandler.addPacketHandler(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -88,12 +133,94 @@ public class GearManager extends MiniPlugin
|
|||||||
|
|
||||||
public static CustomItem parseItem(ItemStack item)
|
public static CustomItem parseItem(ItemStack item)
|
||||||
{
|
{
|
||||||
return null; // TODO: Parse CustomItem from hidden JSON-encoded lore string in item passed in
|
String serialization = getItemSerialization(item);
|
||||||
|
|
||||||
|
if (serialization != null)
|
||||||
|
{
|
||||||
|
CustomItem customItem = deserialize(serialization);
|
||||||
|
return customItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // No serialization found in item's lore, not a custom item!
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isCustomItem(ItemStack item)
|
public static boolean isCustomItem(ItemStack item)
|
||||||
{
|
{
|
||||||
return parseItem(item) != null; // TODO: Check for JSON-encoded lore string instead of deserializing?
|
return getItemSerialization(item) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getItemSerialization(CustomItem customItem)
|
||||||
|
{
|
||||||
|
String tempSeri = serialize(customItem);
|
||||||
|
String serialization = ITEM_SERIALIZATION_TAG;
|
||||||
|
|
||||||
|
for (int i = 0; i < tempSeri.length(); i++)
|
||||||
|
{
|
||||||
|
if (i % 40 == 39)
|
||||||
|
{
|
||||||
|
serialization += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
serialization += tempSeri.charAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return serialization;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getItemSerialization(ItemStack item)
|
||||||
|
{
|
||||||
|
if (item == null || item.getItemMeta() == null
|
||||||
|
|| item.getItemMeta().getLore() == null) return null;
|
||||||
|
|
||||||
|
ItemMeta meta = item.getItemMeta();
|
||||||
|
|
||||||
|
/*for (String lore : meta.getLore())
|
||||||
|
{
|
||||||
|
if (lore.startsWith(ITEM_SERIALIZATION_TAG)) // Found serialization lore-line
|
||||||
|
{
|
||||||
|
int tagLength = ITEM_SERIALIZATION_TAG.length();
|
||||||
|
String serialization = lore.substring(tagLength);
|
||||||
|
|
||||||
|
return serialization;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// TODO: Implement packet intercepting to hide json encoded lore
|
||||||
|
List<String> lore = meta.getLore();
|
||||||
|
boolean serialized = false;
|
||||||
|
String serialization = "";
|
||||||
|
for (int i = 0; i < lore.size(); i++)
|
||||||
|
{
|
||||||
|
String line = lore.get(i);
|
||||||
|
|
||||||
|
if (line.startsWith(ITEM_SERIALIZATION_TAG))
|
||||||
|
{
|
||||||
|
serialized = true;
|
||||||
|
serialization += line.substring(ITEM_SERIALIZATION_TAG.length());
|
||||||
|
}
|
||||||
|
else if (serialized)
|
||||||
|
{
|
||||||
|
serialization += line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialized)
|
||||||
|
{
|
||||||
|
return serialization;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // Unable to find any serialized lore lines, hence not a CustomItem.
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String serialize(CustomItem customItem)
|
||||||
|
{
|
||||||
|
return _gson.toJson(customItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CustomItem deserialize(String serialization)
|
||||||
|
{
|
||||||
|
System.out.println("Serialization: " + serialization);
|
||||||
|
return _gson.fromJson(serialization, CustomItem.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,4 +230,18 @@ public class GearManager extends MiniPlugin
|
|||||||
{
|
{
|
||||||
return _instance;
|
return _instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(PacketInfo packetInfo)
|
||||||
|
{
|
||||||
|
Packet packet = packetInfo.getPacket();
|
||||||
|
|
||||||
|
if (packet instanceof PacketPlayOutSetSlot)
|
||||||
|
{
|
||||||
|
System.out.println("Item slot packet!");
|
||||||
|
PacketPlayOutSetSlot slotPacket = (PacketPlayOutSetSlot) packet;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,9 @@ import org.bukkit.event.Listener;
|
|||||||
import org.bukkit.event.block.Action;
|
import org.bukkit.event.block.Action;
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
|
import org.bukkit.event.player.PlayerItemHeldEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listens for item-related trigger events and accordingly triggers appropriate
|
* Listens for item-related trigger events and accordingly triggers appropriate
|
||||||
@ -17,6 +19,31 @@ import org.bukkit.event.player.PlayerInteractEvent;
|
|||||||
public class ItemListener implements Listener
|
public class ItemListener implements Listener
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle players shuffling CustomItems around by properly updating
|
||||||
|
* and managing their movement.
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
@EventHandler
|
||||||
|
public void onInventoryClick(InventoryClickEvent event)
|
||||||
|
{
|
||||||
|
// TODO: Update any custom-items that are selected/moved to save proper stats if they
|
||||||
|
// TODO: are active. (IE: PlayerGear possesses it as armor slot or weapon)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle updated CustomItem stats and lore upon player
|
||||||
|
* switching items.
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
@EventHandler
|
||||||
|
public void onItemHeldChanged(PlayerItemHeldEvent event)
|
||||||
|
{
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
PlayerGear gear = getGear(player);
|
||||||
|
|
||||||
|
gear.onItemHeldChanged(event);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the trigger of custom gear related effects and abilities.
|
* Handle the trigger of custom gear related effects and abilities.
|
||||||
|
@ -7,7 +7,9 @@ import org.bukkit.Bukkit;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
|
import org.bukkit.event.player.PlayerItemHeldEvent;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PlayerGear caches and manages a players set of {@link CustomItem}s that
|
* PlayerGear caches and manages a players set of {@link CustomItem}s that
|
||||||
@ -17,7 +19,6 @@ import org.bukkit.inventory.ItemStack;
|
|||||||
*/
|
*/
|
||||||
public class PlayerGear
|
public class PlayerGear
|
||||||
{
|
{
|
||||||
|
|
||||||
private String _playerName; // Name of player who owns the gear
|
private String _playerName; // Name of player who owns the gear
|
||||||
|
|
||||||
// Cached custom item information for player's gear
|
// Cached custom item information for player's gear
|
||||||
@ -44,6 +45,14 @@ public class PlayerGear
|
|||||||
return Bukkit.getPlayer(_playerName);
|
return Bukkit.getPlayer(_playerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the {@link PlayerInventory} associated with the owner of this {@link PlayerGear}.
|
||||||
|
*/
|
||||||
|
public PlayerInventory getInventory()
|
||||||
|
{
|
||||||
|
return getPlayer().getInventory();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger interact events for the set of equipped {@link CustomItem}s in gear set.
|
* Trigger interact events for the set of equipped {@link CustomItem}s in gear set.
|
||||||
* @param event - the triggering interact event
|
* @param event - the triggering interact event
|
||||||
@ -80,6 +89,21 @@ public class PlayerGear
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update appropriate gear status and item lores.
|
||||||
|
* @param event - the triggering item held change event.
|
||||||
|
*/
|
||||||
|
public void onItemHeldChanged(PlayerItemHeldEvent event)
|
||||||
|
{
|
||||||
|
ItemStack item = getPlayer().getItemInHand();
|
||||||
|
CustomItem weapon = getWeapon();
|
||||||
|
|
||||||
|
if (weapon != null)
|
||||||
|
{
|
||||||
|
weapon.update(item); // Update held-item's stats.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public CustomItem getWeapon()
|
public CustomItem getWeapon()
|
||||||
{
|
{
|
||||||
ItemStack weaponItem = getPlayer().getInventory().getItemInHand();
|
ItemStack weaponItem = getPlayer().getInventory().getItemInHand();
|
||||||
@ -146,11 +170,11 @@ public class PlayerGear
|
|||||||
public Set<CustomItem> getGear()
|
public Set<CustomItem> getGear()
|
||||||
{
|
{
|
||||||
Set<CustomItem> items = new HashSet<CustomItem>();
|
Set<CustomItem> items = new HashSet<CustomItem>();
|
||||||
items.add(getWeapon());
|
if (getWeapon() != null) items.add(getWeapon());
|
||||||
items.add(getHelmet());
|
if (getHelmet() != null) items.add(getHelmet());
|
||||||
items.add(getChestplate());
|
if (getChestplate() != null) items.add(getChestplate());
|
||||||
items.add(getLeggings());
|
if (getLeggings() != null) items.add(getLeggings());
|
||||||
items.add(getBoots());
|
if (getBoots() != null) items.add(getBoots());
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,8 +182,13 @@ public class PlayerGear
|
|||||||
{
|
{
|
||||||
if (customItem == null || item == null) return false;
|
if (customItem == null || item == null) return false;
|
||||||
|
|
||||||
// TODO: Implement more sophisticated match-checking that checks hidden UUID
|
if (GearManager.isCustomItem(item))
|
||||||
return customItem.getDisplayName().equals(item.getItemMeta().getDisplayName());
|
{
|
||||||
|
CustomItem customItem2 = GearManager.parseItem(item);
|
||||||
|
return customItem2.matches(customItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CustomItem parseItem(ItemStack item)
|
private CustomItem parseItem(ItemStack item)
|
||||||
|
@ -18,6 +18,12 @@ public abstract class ItemAttribute
|
|||||||
* @return the attribute name display to players.
|
* @return the attribute name display to players.
|
||||||
*/
|
*/
|
||||||
public abstract String getDisplayName();
|
public abstract String getDisplayName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a user-friendly description of this attribute, entailing it's effects
|
||||||
|
* and current associated values.
|
||||||
|
*/
|
||||||
|
public String getDescription() { return "???IMPLEMENT"; }
|
||||||
|
|
||||||
public void onInteract(PlayerInteractEvent event)
|
public void onInteract(PlayerInteractEvent event)
|
||||||
{
|
{
|
||||||
|
@ -6,6 +6,7 @@ public abstract class FlatReductionAttribute extends ReductionAttribute
|
|||||||
{
|
{
|
||||||
|
|
||||||
private double _reduction;
|
private double _reduction;
|
||||||
|
public double getFlatReduction() { return _reduction; }
|
||||||
|
|
||||||
public FlatReductionAttribute(ValueDistribution reductionGen, ReductionConfig config)
|
public FlatReductionAttribute(ValueDistribution reductionGen, ReductionConfig config)
|
||||||
{
|
{
|
||||||
|
@ -23,4 +23,9 @@ public class LavaAttribute extends PercentReductionAttribute
|
|||||||
return "Lava Forged";
|
return "Lava Forged";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return String.format("Reduce fire-related damage by %.2f percent.", getReductionPercent());
|
||||||
|
}
|
||||||
}
|
}
|
@ -21,5 +21,10 @@ public class PaddedAttribute extends FlatReductionAttribute
|
|||||||
{
|
{
|
||||||
return "Padded";
|
return "Padded";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return String.format("Reduce fall damage by %.2f half-hearts.", getFlatReduction());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ import mineplex.game.clans.items.generation.ValueDistribution;
|
|||||||
|
|
||||||
public abstract class PercentReductionAttribute extends ReductionAttribute
|
public abstract class PercentReductionAttribute extends ReductionAttribute
|
||||||
{
|
{
|
||||||
|
|
||||||
private double _reductionPercent;
|
private double _reductionPercent;
|
||||||
|
public double getReductionPercent() { return _reductionPercent; }
|
||||||
|
|
||||||
public PercentReductionAttribute(ValueDistribution reductionGen, ReductionConfig config)
|
public PercentReductionAttribute(ValueDistribution reductionGen, ReductionConfig config)
|
||||||
{
|
{
|
||||||
|
@ -24,4 +24,9 @@ public class ProtectionAttribute extends FlatReductionAttribute
|
|||||||
return "Protection";
|
return "Protection";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return String.format("Reduce incoming attack damage by %.2f half-hearts.", getFlatReduction());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,4 +21,9 @@ public class ReinforcedAttribute extends FlatReductionAttribute
|
|||||||
return "Reinforced";
|
return "Reinforced";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return String.format("Reduce incoming enemy attacks by %.2f half-hearts.", getFlatReduction());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,4 +22,9 @@ public class SlantedAttribute extends FlatReductionAttribute
|
|||||||
return "Slanted";
|
return "Slanted";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return String.format("Reduce arrow damage by %.2f half-hearts.", getFlatReduction());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ public abstract class AttackAttribute extends ItemAttribute
|
|||||||
{
|
{
|
||||||
|
|
||||||
private int _attackLimit;
|
private int _attackLimit;
|
||||||
|
public int getAttackLimit() { return _attackLimit; }
|
||||||
|
|
||||||
private int _attackCount;
|
private int _attackCount;
|
||||||
|
|
||||||
public AttackAttribute(int attackLimit)
|
public AttackAttribute(int attackLimit)
|
||||||
|
@ -21,6 +21,12 @@ public class ConqueringAttribute extends DamageAttribute
|
|||||||
return "Conquering"; // TODO: Fill in name
|
return "Conquering"; // TODO: Fill in name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return String.format("Deal an extra %.2f hearts of damage to mobs.", getBonusDamage());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean grantBonusDamage(Entity entity)
|
public boolean grantBonusDamage(Entity entity)
|
||||||
{
|
{
|
||||||
|
@ -9,6 +9,7 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
|||||||
public abstract class DamageAttribute extends ItemAttribute
|
public abstract class DamageAttribute extends ItemAttribute
|
||||||
{
|
{
|
||||||
private double _bonusDamage;
|
private double _bonusDamage;
|
||||||
|
public double getBonusDamage() { return _bonusDamage; }
|
||||||
|
|
||||||
public DamageAttribute(ValueDistribution damageGen)
|
public DamageAttribute(ValueDistribution damageGen)
|
||||||
{
|
{
|
||||||
|
@ -24,6 +24,12 @@ public class FlamingAttribute extends AttackAttribute
|
|||||||
return "Flaming"; // TODO: Fill in name
|
return "Flaming"; // TODO: Fill in name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return String.format("Enemies catch fire for %d ticks every %d attacks.", _fireDuration, getAttackLimit());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void triggerAttack(Entity attacker, Entity defender)
|
public void triggerAttack(Entity attacker, Entity defender)
|
||||||
{
|
{
|
||||||
|
@ -16,12 +16,11 @@ import org.bukkit.potion.PotionEffectType;
|
|||||||
*/
|
*/
|
||||||
public class FrostedAttribute extends ItemAttribute
|
public class FrostedAttribute extends ItemAttribute
|
||||||
{
|
{
|
||||||
public final int TICKS_PER_SECOND = 20; // Number of ticks per second for in-game logic.
|
|
||||||
private static ValueDistribution amountGen = generateDistribution(0, 3); // Value generator for slow amount range
|
private static ValueDistribution amountGen = generateDistribution(0, 3); // Value generator for slow amount range
|
||||||
private static ValueDistribution durationGen = generateDistribution(1, 3); // Value generator for slow duration range
|
private static ValueDistribution durationGen = generateDistribution(20, 60); // Value generator for slow duration range
|
||||||
|
|
||||||
private int _slowAmount; // The slowness level/amplifier
|
private int _slowAmount; // The slowness level/amplifier
|
||||||
private double _slowDuration; // The duration (in seconds) of slow effect
|
private int _slowDuration; // The duration (in ticks) of slow effect
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor
|
* Class constructor
|
||||||
@ -29,7 +28,7 @@ public class FrostedAttribute extends ItemAttribute
|
|||||||
public FrostedAttribute()
|
public FrostedAttribute()
|
||||||
{
|
{
|
||||||
_slowAmount = amountGen.generateIntValue();
|
_slowAmount = amountGen.generateIntValue();
|
||||||
_slowDuration = durationGen.generateValue();
|
_slowDuration = durationGen.generateIntValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -38,6 +37,12 @@ public class FrostedAttribute extends ItemAttribute
|
|||||||
return "Frosted";
|
return "Frosted";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return String.format("Apply slowness %d for %d ticks to enemies.", _slowAmount, _slowDuration);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttacked(EntityDamageByEntityEvent event)
|
public void onAttacked(EntityDamageByEntityEvent event)
|
||||||
{
|
{
|
||||||
@ -51,7 +56,6 @@ public class FrostedAttribute extends ItemAttribute
|
|||||||
|
|
||||||
private PotionEffect generateSlowEffect()
|
private PotionEffect generateSlowEffect()
|
||||||
{
|
{
|
||||||
int duration = (int) (_slowDuration / TICKS_PER_SECOND);
|
return new PotionEffect(PotionEffectType.SLOW, _slowDuration, _slowAmount);
|
||||||
return new PotionEffect(PotionEffectType.SLOW, duration, _slowAmount);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,12 @@ public class HasteAttribute extends AttackAttribute
|
|||||||
return "Haste"; // TODO: Fill in name
|
return "Haste"; // TODO: Fill in name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return String.format("Gain speed %d for %d ticks every %d attacks.", _speedAmount, _speedDuration, getAttackLimit());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void triggerAttack(Entity attacker, Entity defender)
|
public void triggerAttack(Entity attacker, Entity defender)
|
||||||
{
|
{
|
||||||
|
@ -21,4 +21,5 @@ public class HeavyAttribute extends ItemAttribute
|
|||||||
return ""; // TODO: Fill in name
|
return ""; // TODO: Fill in name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,12 @@ public class JaggedAttribute extends AttackAttribute
|
|||||||
return "Jagged"; // TODO: Fill in name
|
return "Jagged"; // TODO: Fill in name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return String.format("Temporarily halt enemies every %d attacks.", getAttackLimit());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void triggerAttack(Entity attacker, Entity defender)
|
public void triggerAttack(Entity attacker, Entity defender)
|
||||||
{
|
{
|
||||||
|
@ -20,6 +20,12 @@ public class SharpAttribute extends DamageAttribute
|
|||||||
return "Sharp"; // TODO: Fill in name
|
return "Sharp"; // TODO: Fill in name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return String.format("Deal an extra %.2f damage.", getBonusDamage());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean grantBonusDamage(Entity defender)
|
public boolean grantBonusDamage(Entity defender)
|
||||||
{
|
{
|
||||||
|
@ -24,6 +24,12 @@ public class VampiricAttribute extends ItemAttribute
|
|||||||
return "Vampiric"; // TODO: Fill in name
|
return "Vampiric"; // TODO: Fill in name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return String.format("Heal yourself for %d percentage of damage dealt to enemy players.", _healPercent);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttack(EntityDamageByEntityEvent event)
|
public void onAttack(EntityDamageByEntityEvent event)
|
||||||
{
|
{
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
package mineplex.game.clans.items.commands;
|
package mineplex.game.clans.items.commands;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.bukkit.Chunk;
|
import org.bukkit.Chunk;
|
||||||
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
|
||||||
import mineplex.core.command.CommandBase;
|
import mineplex.core.command.CommandBase;
|
||||||
import mineplex.core.common.Rank;
|
import mineplex.core.common.Rank;
|
||||||
@ -22,7 +26,13 @@ import mineplex.game.clans.clans.ClanRole;
|
|||||||
import mineplex.game.clans.clans.ClansManager;
|
import mineplex.game.clans.clans.ClansManager;
|
||||||
import mineplex.game.clans.clans.ClansUtility.ClanRelation;
|
import mineplex.game.clans.clans.ClansUtility.ClanRelation;
|
||||||
import mineplex.game.clans.clans.ClientClan;
|
import mineplex.game.clans.clans.ClientClan;
|
||||||
|
import mineplex.game.clans.items.CustomItem;
|
||||||
import mineplex.game.clans.items.GearManager;
|
import mineplex.game.clans.items.GearManager;
|
||||||
|
import mineplex.game.clans.items.attributes.weapon.FlamingAttribute;
|
||||||
|
import mineplex.game.clans.items.attributes.weapon.FrostedAttribute;
|
||||||
|
import mineplex.game.clans.items.attributes.weapon.SharpAttribute;
|
||||||
|
import mineplex.game.clans.items.legendaries.LegendaryItem;
|
||||||
|
import mineplex.game.clans.items.legendaries.WindBlade;
|
||||||
|
|
||||||
public class GearCommand extends CommandBase<GearManager>
|
public class GearCommand extends CommandBase<GearManager>
|
||||||
{
|
{
|
||||||
@ -36,6 +46,30 @@ public class GearCommand extends CommandBase<GearManager>
|
|||||||
{
|
{
|
||||||
UtilPlayer.message(caller, F.main("Gear", "Opening custom gear GUI!"));
|
UtilPlayer.message(caller, F.main("Gear", "Opening custom gear GUI!"));
|
||||||
|
|
||||||
|
if (caller.getItemInHand() != null && caller.getItemInHand().getType() != Material.AIR)
|
||||||
|
{
|
||||||
|
ItemStack item = caller.getItemInHand();
|
||||||
|
ItemMeta meta = item.getItemMeta();
|
||||||
|
List<String> lore = meta.getLore();
|
||||||
|
lore.set(0, "Test");
|
||||||
|
meta.setLore(lore);
|
||||||
|
item.setItemMeta(meta);
|
||||||
|
System.out.println("aaaaaaa");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LegendaryItem legendary = new WindBlade();
|
||||||
|
caller.setItemInHand(legendary.toItemStack(1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*CustomItem customItem = new CustomItem();
|
||||||
|
customItem.setPrefix(new FrostedAttribute());
|
||||||
|
customItem.setSuperPrefix(new SharpAttribute());
|
||||||
|
customItem.setSuffix(new FlamingAttribute());
|
||||||
|
ItemStack sword = customItem.toItemStack(1);
|
||||||
|
caller.setItemInHand(sword);*/
|
||||||
// TODO: Open custom gear GUI here
|
// TODO: Open custom gear GUI here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,19 +3,26 @@ package mineplex.game.clans.items.legendaries;
|
|||||||
import mineplex.game.clans.items.attributes.ItemAttribute;
|
import mineplex.game.clans.items.attributes.ItemAttribute;
|
||||||
import mineplex.game.clans.items.generation.ValueDistribution;
|
import mineplex.game.clans.items.generation.ValueDistribution;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
public class AlligatorsTooth extends LegendaryItem
|
public class AlligatorsTooth extends LegendaryItem
|
||||||
{
|
{
|
||||||
|
|
||||||
private static ValueDistribution damageGen = generateDistribution(1.0d, 5.0d);
|
private static ValueDistribution boostGen = generateDistribution(0.6d, 1.2d);
|
||||||
|
private static ValueDistribution damageGen = generateDistribution(1.0d, 6.0d);
|
||||||
|
|
||||||
private double _damageBonus;
|
private double _damageBonus;
|
||||||
|
private double _swimSpeed;
|
||||||
|
|
||||||
public AlligatorsTooth()
|
public AlligatorsTooth()
|
||||||
{
|
{
|
||||||
|
super("Alligators Tooth", "Grants bonus damage in water and special ability to swim fast!", Material.RAW_FISH);
|
||||||
|
|
||||||
_damageBonus = damageGen.generateValue();
|
_damageBonus = damageGen.generateValue();
|
||||||
|
_swimSpeed = boostGen.generateValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -41,7 +48,9 @@ public class AlligatorsTooth extends LegendaryItem
|
|||||||
|
|
||||||
private void propelPlayer(Player player)
|
private void propelPlayer(Player player)
|
||||||
{
|
{
|
||||||
// TODO: Propel player forward with 0.6 to 1.2 velocity
|
Vector direction = player.getLocation().getDirection().normalize();
|
||||||
|
direction.multiply(_swimSpeed);
|
||||||
|
player.setVelocity(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isInWater(Player player)
|
private boolean isInWater(Player player)
|
||||||
|
@ -3,6 +3,7 @@ package mineplex.game.clans.items.legendaries;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
import org.bukkit.potion.PotionEffect;
|
import org.bukkit.potion.PotionEffect;
|
||||||
@ -11,6 +12,11 @@ import org.bukkit.potion.PotionEffectType;
|
|||||||
public class GiantsBroadsword extends LegendaryItem
|
public class GiantsBroadsword extends LegendaryItem
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public GiantsBroadsword()
|
||||||
|
{
|
||||||
|
super("Giants Broadsword", "Deal huge damage and block to gain defensive abilities!", Material.PAPER);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(Player wielder)
|
public void update(Player wielder)
|
||||||
{
|
{
|
||||||
@ -24,6 +30,12 @@ public class GiantsBroadsword extends LegendaryItem
|
|||||||
public void onAttack(EntityDamageByEntityEvent event, Player wielder)
|
public void onAttack(EntityDamageByEntityEvent event, Player wielder)
|
||||||
{
|
{
|
||||||
// TODO: Buff knockback and damage values (What are specific values?)
|
// TODO: Buff knockback and damage values (What are specific values?)
|
||||||
|
// Chiss: Make 'em up
|
||||||
|
|
||||||
|
double bonusDamage = 2.0d; // Too much?
|
||||||
|
event.setDamage(event.getDamage() + bonusDamage);
|
||||||
|
|
||||||
|
// TODO: Apply knockback bonus using previous Clans Custom combat events
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buffPlayer(Player player)
|
private void buffPlayer(Player player)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package mineplex.game.clans.items.legendaries;
|
package mineplex.game.clans.items.legendaries;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.block.Action;
|
import org.bukkit.event.block.Action;
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
@ -19,6 +20,13 @@ public class LegendaryItem extends CustomItem
|
|||||||
|
|
||||||
public LegendaryItem()
|
public LegendaryItem()
|
||||||
{
|
{
|
||||||
|
this(null, null, null); // TODO: Never used, just to skip base implementation for testing.
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegendaryItem(String name, String description, Material material)
|
||||||
|
{
|
||||||
|
super(name, description, material);
|
||||||
|
|
||||||
_lastBlock = 0l;
|
_lastBlock = 0l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,34 +3,52 @@ package mineplex.game.clans.items.legendaries;
|
|||||||
import mineplex.game.clans.items.attributes.ItemAttribute;
|
import mineplex.game.clans.items.attributes.ItemAttribute;
|
||||||
import mineplex.game.clans.items.generation.ValueDistribution;
|
import mineplex.game.clans.items.generation.ValueDistribution;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
public class WindBlade extends LegendaryItem
|
public class WindBlade extends LegendaryItem
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public static final double FLIGHT_VELOCITY = 0.25d;
|
||||||
public static final int MAX_FLIGHT_TIME = 80; // Max flight of 80 ticks
|
public static final int MAX_FLIGHT_TIME = 80; // Max flight of 80 ticks
|
||||||
|
|
||||||
private long _flightTime; // Time (in ticks) since last touching ground and flying
|
private long _flightTime; // Time (in ticks) since last touching ground and flying
|
||||||
|
|
||||||
public WindBlade()
|
public WindBlade()
|
||||||
{
|
{
|
||||||
|
super("Wind Blade", "Activate flying ability to take flight for 80 ticks before landing!", Material.STICK); // TODO: Configurable?
|
||||||
|
|
||||||
_flightTime = 0;
|
_flightTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(Player wielder)
|
public void update(Player wielder)
|
||||||
{
|
{
|
||||||
|
Entity entity = (Entity) wielder;
|
||||||
|
|
||||||
|
// Check if player is attempting to fly and activate
|
||||||
if (isHoldingRightClick() && canPropel())
|
if (isHoldingRightClick() && canPropel())
|
||||||
{
|
{
|
||||||
propelPlayer(wielder);
|
propelPlayer(wielder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if player has touched down
|
||||||
|
if (entity.isOnGround())
|
||||||
|
{
|
||||||
|
_flightTime = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void propelPlayer(Player player)
|
private void propelPlayer(Player player)
|
||||||
{
|
{
|
||||||
_flightTime++;
|
_flightTime++;
|
||||||
// TODO: Propel player forward with ??? velocity
|
Vector direction = player.getLocation().getDirection().normalize();
|
||||||
|
direction.multiply(FLIGHT_VELOCITY); // Set velocity magnitude
|
||||||
|
|
||||||
|
player.setVelocity(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canPropel()
|
private boolean canPropel()
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package mineplex.game.clans.items.smelting;
|
package mineplex.game.clans.items.smelting;
|
||||||
|
|
||||||
|
import mineplex.game.clans.items.GearManager;
|
||||||
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
@ -9,21 +11,32 @@ public class Smelter
|
|||||||
|
|
||||||
public static void smeltItemInHand(Player player)
|
public static void smeltItemInHand(Player player)
|
||||||
{
|
{
|
||||||
// Smelt item in hand for player
|
|
||||||
ItemStack item = player.getInventory().getItemInHand();
|
ItemStack item = player.getInventory().getItemInHand();
|
||||||
|
|
||||||
if (item != null)
|
if (isSmeltable(item))
|
||||||
{
|
{
|
||||||
ItemStack returns = smeltItem(item);
|
ItemStack returns = smeltItem(item);
|
||||||
player.getInventory().setItemInHand(returns);
|
player.getInventory().setItemInHand(returns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Notify player of smelt success/failure?
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ItemStack smeltItem(ItemStack item)
|
public static ItemStack smeltItem(ItemStack item)
|
||||||
{
|
{
|
||||||
Material material = getSmeltedType(item.getType());
|
Material material = getSmeltedType(item.getType());
|
||||||
int maxAmount = getSmeltAmount(item.getType());
|
int maxAmount = getSmeltAmount(item.getType());
|
||||||
int amount = maxAmount; // TODO: Determine proportional return on smelt depending on type/durability
|
int amount = maxAmount;
|
||||||
|
|
||||||
|
if (!GearManager.isCustomItem(item))
|
||||||
|
{
|
||||||
|
short maxDurability = item.getType().getMaxDurability();
|
||||||
|
int durability = maxDurability - item.getDurability();
|
||||||
|
double percent = durability / (double) maxDurability;
|
||||||
|
System.out.println("Durability: " + item.getDurability() + " -- max: " + item.getType().getMaxDurability() + " --- percent: " + percent);
|
||||||
|
amount = Math.max(1, (int) (maxAmount * percent));
|
||||||
|
}
|
||||||
|
|
||||||
return new ItemStack(material, amount);
|
return new ItemStack(material, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,6 +73,13 @@ public class Smelter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isSmeltable(ItemStack item)
|
||||||
|
{
|
||||||
|
if (item == null) return false;
|
||||||
|
|
||||||
|
return getSmeltedType(item.getType()) != null;
|
||||||
|
}
|
||||||
|
|
||||||
private static Material getSmeltedType(Material itemType)
|
private static Material getSmeltedType(Material itemType)
|
||||||
{
|
{
|
||||||
switch (itemType)
|
switch (itemType)
|
||||||
|
@ -41,6 +41,8 @@ public class Utility
|
|||||||
*/
|
*/
|
||||||
public static <T> T deserialize(String serializedData, Class<T> type)
|
public static <T> T deserialize(String serializedData, Class<T> type)
|
||||||
{
|
{
|
||||||
|
if (serializedData == null) return null;
|
||||||
|
|
||||||
return _gson.fromJson(serializedData, type);
|
return _gson.fromJson(serializedData, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,240 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Google Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package mineplex.serverdata.serialization;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.google.gson.TypeAdapter;
|
||||||
|
import com.google.gson.TypeAdapterFactory;
|
||||||
|
import com.google.gson.internal.Streams;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapts values whose runtime type may differ from their declaration type. This
|
||||||
|
* is necessary when a field's type is not the same type that GSON should create
|
||||||
|
* when deserializing that field. For example, consider these types:
|
||||||
|
* <pre> {@code
|
||||||
|
* abstract class Shape {
|
||||||
|
* int x;
|
||||||
|
* int y;
|
||||||
|
* }
|
||||||
|
* class Circle extends Shape {
|
||||||
|
* int radius;
|
||||||
|
* }
|
||||||
|
* class Rectangle extends Shape {
|
||||||
|
* int width;
|
||||||
|
* int height;
|
||||||
|
* }
|
||||||
|
* class Diamond extends Shape {
|
||||||
|
* int width;
|
||||||
|
* int height;
|
||||||
|
* }
|
||||||
|
* class Drawing {
|
||||||
|
* Shape bottomShape;
|
||||||
|
* Shape topShape;
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
* <p>Without additional type information, the serialized JSON is ambiguous. Is
|
||||||
|
* the bottom shape in this drawing a rectangle or a diamond? <pre> {@code
|
||||||
|
* {
|
||||||
|
* "bottomShape": {
|
||||||
|
* "width": 10,
|
||||||
|
* "height": 5,
|
||||||
|
* "x": 0,
|
||||||
|
* "y": 0
|
||||||
|
* },
|
||||||
|
* "topShape": {
|
||||||
|
* "radius": 2,
|
||||||
|
* "x": 4,
|
||||||
|
* "y": 1
|
||||||
|
* }
|
||||||
|
* }}</pre>
|
||||||
|
* This class addresses this problem by adding type information to the
|
||||||
|
* serialized JSON and honoring that type information when the JSON is
|
||||||
|
* deserialized: <pre> {@code
|
||||||
|
* {
|
||||||
|
* "bottomShape": {
|
||||||
|
* "type": "Diamond",
|
||||||
|
* "width": 10,
|
||||||
|
* "height": 5,
|
||||||
|
* "x": 0,
|
||||||
|
* "y": 0
|
||||||
|
* },
|
||||||
|
* "topShape": {
|
||||||
|
* "type": "Circle",
|
||||||
|
* "radius": 2,
|
||||||
|
* "x": 4,
|
||||||
|
* "y": 1
|
||||||
|
* }
|
||||||
|
* }}</pre>
|
||||||
|
* Both the type field name ({@code "type"}) and the type labels ({@code
|
||||||
|
* "Rectangle"}) are configurable.
|
||||||
|
*
|
||||||
|
* <h3>Registering Types</h3>
|
||||||
|
* Create a {@code RuntimeTypeAdapter} by passing the base type and type field
|
||||||
|
* name to the {@link #of} factory method. If you don't supply an explicit type
|
||||||
|
* field name, {@code "type"} will be used. <pre> {@code
|
||||||
|
* RuntimeTypeAdapter<Shape> shapeAdapter
|
||||||
|
* = RuntimeTypeAdapter.of(Shape.class, "type");
|
||||||
|
* }</pre>
|
||||||
|
* Next register all of your subtypes. Every subtype must be explicitly
|
||||||
|
* registered. This protects your application from injection attacks. If you
|
||||||
|
* don't supply an explicit type label, the type's simple name will be used.
|
||||||
|
* <pre> {@code
|
||||||
|
* shapeAdapter.registerSubtype(Rectangle.class, "Rectangle");
|
||||||
|
* shapeAdapter.registerSubtype(Circle.class, "Circle");
|
||||||
|
* shapeAdapter.registerSubtype(Diamond.class, "Diamond");
|
||||||
|
* }</pre>
|
||||||
|
* Finally, register the type adapter in your application's GSON builder:
|
||||||
|
* <pre> {@code
|
||||||
|
* Gson gson = new GsonBuilder()
|
||||||
|
* .registerTypeAdapter(Shape.class, shapeAdapter)
|
||||||
|
* .create();
|
||||||
|
* }</pre>
|
||||||
|
* Like {@code GsonBuilder}, this API supports chaining: <pre> {@code
|
||||||
|
* RuntimeTypeAdapter<Shape> shapeAdapter = RuntimeTypeAdapterFactory.of(Shape.class)
|
||||||
|
* .registerSubtype(Rectangle.class)
|
||||||
|
* .registerSubtype(Circle.class)
|
||||||
|
* .registerSubtype(Diamond.class);
|
||||||
|
* }</pre>
|
||||||
|
*/
|
||||||
|
public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
|
||||||
|
private final Class<?> baseType;
|
||||||
|
private final String typeFieldName;
|
||||||
|
private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<String, Class<?>>();
|
||||||
|
private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<Class<?>, String>();
|
||||||
|
|
||||||
|
private RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName) {
|
||||||
|
if (typeFieldName == null || baseType == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
this.baseType = baseType;
|
||||||
|
this.typeFieldName = typeFieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new runtime type adapter using for {@code baseType} using {@code
|
||||||
|
* typeFieldName} as the type field name. Type field names are case sensitive.
|
||||||
|
*/
|
||||||
|
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) {
|
||||||
|
return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
|
||||||
|
* the type field name.
|
||||||
|
*/
|
||||||
|
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) {
|
||||||
|
return new RuntimeTypeAdapterFactory<T>(baseType, "type");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers {@code type} identified by {@code label}. Labels are case
|
||||||
|
* sensitive.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if either {@code type} or {@code label}
|
||||||
|
* have already been registered on this type adapter.
|
||||||
|
*/
|
||||||
|
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) {
|
||||||
|
if (type == null || label == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
|
||||||
|
throw new IllegalArgumentException("types and labels must be unique");
|
||||||
|
}
|
||||||
|
labelToSubtype.put(label, type);
|
||||||
|
subtypeToLabel.put(type, label);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers {@code type} identified by its {@link Class#getSimpleName simple
|
||||||
|
* name}. Labels are case sensitive.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if either {@code type} or its simple name
|
||||||
|
* have already been registered on this type adapter.
|
||||||
|
*/
|
||||||
|
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {
|
||||||
|
return registerSubtype(type, type.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) {
|
||||||
|
if (type.getRawType() != baseType) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, TypeAdapter<?>> labelToDelegate
|
||||||
|
= new LinkedHashMap<String, TypeAdapter<?>>();
|
||||||
|
final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate
|
||||||
|
= new LinkedHashMap<Class<?>, TypeAdapter<?>>();
|
||||||
|
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
|
||||||
|
TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
|
||||||
|
labelToDelegate.put(entry.getKey(), delegate);
|
||||||
|
subtypeToDelegate.put(entry.getValue(), delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TypeAdapter<R>() {
|
||||||
|
@Override public R read(JsonReader in) throws IOException {
|
||||||
|
JsonElement jsonElement = Streams.parse(in);
|
||||||
|
JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
|
||||||
|
if (labelJsonElement == null) {
|
||||||
|
throw new JsonParseException("cannot deserialize " + baseType
|
||||||
|
+ " because it does not define a field named " + typeFieldName);
|
||||||
|
}
|
||||||
|
String label = labelJsonElement.getAsString();
|
||||||
|
@SuppressWarnings("unchecked") // registration requires that subtype extends T
|
||||||
|
TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);
|
||||||
|
if (delegate == null) {
|
||||||
|
throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
|
||||||
|
+ label + "; did you forget to register a subtype?");
|
||||||
|
}
|
||||||
|
return delegate.fromJsonTree(jsonElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void write(JsonWriter out, R value) throws IOException {
|
||||||
|
Class<?> srcType = value.getClass();
|
||||||
|
String label = subtypeToLabel.get(srcType);
|
||||||
|
@SuppressWarnings("unchecked") // registration requires that subtype extends T
|
||||||
|
TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);
|
||||||
|
if (delegate == null) {
|
||||||
|
throw new JsonParseException("cannot serialize " + srcType.getName()
|
||||||
|
+ "; did you forget to register a subtype?");
|
||||||
|
}
|
||||||
|
JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
|
||||||
|
if (jsonObject.has(typeFieldName)) {
|
||||||
|
throw new JsonParseException("cannot serialize " + srcType.getName()
|
||||||
|
+ " because it already defines a field named " + typeFieldName);
|
||||||
|
}
|
||||||
|
JsonObject clone = new JsonObject();
|
||||||
|
clone.add(typeFieldName, new JsonPrimitive(label));
|
||||||
|
for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
|
||||||
|
clone.add(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
Streams.write(clone, out);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user