/** Runes of Wizardry Mod for Minecraft
* Licensed under the GNU GPL version 3
*
* this file was created by Xilef11 on 2016-03-05
*/
package com.zpig333.runesofwizardry.command;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommand;
import net.minecraft.command.ICommandSender;
import net.minecraft.command.WrongUsageException;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.SoundEvents;
import net.minecraft.item.ItemStack;
import net.minecraft.server.MinecraftServer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3i;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Level;
import com.zpig333.runesofwizardry.api.DustRegistry;
import com.zpig333.runesofwizardry.api.IRune;
import com.zpig333.runesofwizardry.core.ConfigHandler;
import com.zpig333.runesofwizardry.core.WizardryLogger;
import com.zpig333.runesofwizardry.core.WizardryRegistry;
import com.zpig333.runesofwizardry.core.rune.PatternUtils;
import com.zpig333.runesofwizardry.item.ItemDustPouch;
import com.zpig333.runesofwizardry.item.dust.DustPlaceholder;
import com.zpig333.runesofwizardry.tileentity.TileEntityDustPlaced;
import com.zpig333.runesofwizardry.util.RayTracer;
import com.zpig333.runesofwizardry.util.json.JsonUtils;
/**
* @author Xilef11
*
*/
public class CommandImportPattern implements ICommand {
private static CommandImportPattern instance;
private final List<String> aliases;
private static final String locKey="runesofwizardry.command.import";
public CommandImportPattern() {
//define aliases here
aliases = new LinkedList<String>();
//aliases.add(I18n.translateToLocal("runesofwizardry.command.export"));
}
/* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(ICommand arg0) {
//for sorting commands I guess?
return 0;
}
/* (non-Javadoc)
* @see net.minecraft.command.ICommand#getName()
*/
@Override
public String getName() {
return "rw_import";
}
/* (non-Javadoc)
* @see net.minecraft.command.ICommand#getUsage(net.minecraft.command.ICommandSender)
*/
@Override
public String getUsage(ICommandSender sender) {
//it gets partially translated - OK
return getName()+" "+locKey+".usage";
}
/* (non-Javadoc)
* @see net.minecraft.command.ICommand#getAliases()
*/
@Override
public List<String> getAliases() {
return aliases;
}
/* (non-Javadoc)
* @see net.minecraft.command.ICommand#processCommand(net.minecraft.command.ICommandSender, java.lang.String[])
*/
//Note: server is null when called from runic dictionary
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args)throws CommandException {
World world = sender.getEntityWorld();
//server-side only... for now
if(!world.isRemote){
if(sender instanceof EntityPlayer){
EntityPlayer player = (EntityPlayer) sender;
if(args.length<1 || args.length>2)throw new WrongUsageException(getUsage(sender));
//get the pattern from args
ItemStack[][] pattern = null;
IRune rune=null;
if(args[0].contains(":")){//if its a rune
rune = DustRegistry.getRuneByID(args[0]);
//translation happens automagically
if(rune==null)throw new CommandException(locKey+".nosuchrune",args[0]);
ItemStack[][] runepattern = rune.getPattern();
//COPY the pattern to avoid changing it by mistake (everything is mutable...)
pattern = new ItemStack[runepattern.length][runepattern[0].length];
for(int r=0;r<pattern.length;r++){
for(int c=0;c<pattern[r].length;c++){
pattern[r][c]=runepattern[r][c].copy();
}
}
}else{
//Check for JSON on the server
try {
pattern = PatternUtils.importFromJson(args[0]);
JsonUtils.clearItemStackJson();
} catch (FileNotFoundException e) {
sender.sendMessage(new TextComponentTranslation(locKey+".serverfilenotfound", args[0], args[0], args[0]));
return;
} catch (IOException e) {
WizardryLogger.logException(Level.ERROR, e, "Error while importing pattern from JSON");
}
}
//Find the block looked at + facing
RayTraceResult look = RayTracer.retrace(player);
BlockPos lookPos = look.getBlockPos();
Block block = world.getBlockState(lookPos).getBlock();
EnumFacing playerFacing = player.getHorizontalFacing();
WizardryLogger.logInfo("Import Pattern: Looking at block: "+block.getUnlocalizedName()+" at "+lookPos+" facing: "+playerFacing);
//allows to replace placed dust
if(world.getBlockState(lookPos).getBlock()==WizardryRegistry.dust_placed)lookPos=lookPos.down();
//place the pattern depending on arg
if(args.length==2){
String centering = args[1];
if(centering.equalsIgnoreCase("natural")){
if(rune!=null){//if we're placing a rune
placeRuneAtEntity(rune,pattern, world, lookPos, playerFacing, player);
}else{
placePatternCentered(pattern, world, lookPos, playerFacing, player);
}
}else if(centering.equalsIgnoreCase("topleft")){
PlacePatternTopLeft(pattern, world, lookPos, playerFacing, player);
}else if(centering.equalsIgnoreCase("center")){
placePatternCentered(pattern,world,lookPos,playerFacing,player);
}
}else{
if(rune!=null){//if we're placing a rune
placeRuneAtEntity(rune,pattern, world, lookPos, playerFacing, player);
}else{
placePatternCentered(pattern, world, lookPos, playerFacing, player);
}
}
//PlacePatternTopLeft(pattern, world, lookPos, playerFacing, player);
}
}else{
//TODO JSON on the client. we will need a packet of some sort.
}
}
private void placeRuneAtEntity(IRune rune, ItemStack[][] pattern,World world, BlockPos lookPos, EnumFacing playerFacing,EntityPlayer player) {
Vec3i pos = rune.getEntityPosition();
int dX = pos.getX(),dY = pos.getY();
EnumFacing left = playerFacing.rotateYCCW();
BlockPos newTL = lookPos.offset(left, dX).offset(playerFacing, dY);
PlacePatternTopLeft(pattern, world, newTL, playerFacing, player);
}
private void placePatternCentered(ItemStack[][] pattern, World world,BlockPos lookPos, EnumFacing playerFacing, EntityPlayer player) {
int height = pattern.length/TileEntityDustPlaced.ROWS;
int width = pattern[0].length/TileEntityDustPlaced.COLS;
int centerX = width/2, centerY = height/2;
EnumFacing left = playerFacing.rotateYCCW();
BlockPos newTL = lookPos.offset(left, centerX).offset(playerFacing, centerY);
PlacePatternTopLeft(pattern, world, newTL, playerFacing, player);
}
private void PlacePatternTopLeft(ItemStack[][] pattern, World world, BlockPos topLeft, EnumFacing playerFacing,EntityPlayer player){
//get the pattern in the right direction
ItemStack[][] rotatedPattern = PatternUtils.rotateAgainstFacing(pattern, playerFacing);
//convert to contents array
ItemStack[][][][] contents = PatternUtils.toContentsArray(rotatedPattern);
//get the pos for the NW block (an offset from the look/"top-left" block)
BlockPos nw=topLeft.up();
switch(playerFacing){
case EAST: nw = nw.offset(EnumFacing.WEST,contents[0].length-1);
break;
case NORTH: //no change needed
break;
case SOUTH: nw=nw.offset(EnumFacing.WEST,contents[0].length-1).offset(EnumFacing.NORTH,contents.length-1);
break;
case WEST:nw = nw.offset(EnumFacing.NORTH,contents.length-1);
break;
default: throw new IllegalStateException("Import command: Facing is not horizontal");
}
boolean missing=false;//did we miss some dusts or blocks?
//contents[0][0] is always NW most block
for(int r=0;r<contents.length;r++){
for(int c=0;c<contents[r].length;c++){
BlockPos current = nw.offset(EnumFacing.EAST, c).offset(EnumFacing.SOUTH, r);
IBlockState state=world.getBlockState(current);
Block block = state.getBlock();
boolean emptyBlock = PatternUtils.isEmpty(contents[r][c]);
if((block==Blocks.AIR||block==WizardryRegistry.dust_placed) && world.isSideSolid(current.down(), EnumFacing.UP) && !emptyBlock){
if(block==WizardryRegistry.dust_placed && !player.capabilities.isCreativeMode)block.breakBlock(world, current, state);
world.setBlockState(current, WizardryRegistry.dust_placed.getDefaultState());
TileEntity ent = world.getTileEntity(current);
if(ent instanceof TileEntityDustPlaced){//no reason why it isn't
TileEntityDustPlaced ted = (TileEntityDustPlaced)ent;
//ItemStack[][] pat = PatternUtils.rotateAgainstFacing(contents[r][c], playerFacing);
//ted.setContents(pat);
//remove from player's inventory if not creative
if(!player.capabilities.isCreativeMode){
ItemStack[][] itemStacks = contents[r][c];
//check one item at a time...
for (int row = 0; row < itemStacks.length; row++) {
ItemStack[] stacks = itemStacks[row];
for (int col = 0; col < stacks.length; col++) {
ItemStack s = stacks[col];
int n=0;
if(!s.isEmpty()){
n=s.getCount();
//n is the number of wanted items
//n=player.inventory.clearMatchingItems(s.getItem(), s.getMetadata(), s.getCount(), s.getTagCompound());
for(int i=0;i<player.inventory.getSizeInventory()&&n>0;i++){
ItemStack playerStack = player.inventory.getStackInSlot(i);
if(playerStack.isEmpty())continue;
//if the item matches
if(ItemStack.areItemsEqual(s, playerStack)&&ItemStack.areItemStackTagsEqual(s, playerStack)){
int originalSize = playerStack.getCount();
int remainder = originalSize-n;
playerStack.setCount(remainder>0? remainder : 0);
if(playerStack.getCount()==0)player.inventory.removeStackFromSlot(i);
n-= remainder>0? n : originalSize;
}else if(playerStack.getItem() instanceof ItemDustPouch){ ItemDustPouch pouch = (ItemDustPouch)playerStack.getItem();
ItemStack dust = pouch.getDustStack(playerStack, n);
if(ItemStack.areItemsEqual(s,dust)&&ItemStack.areItemStackTagsEqual(s, dust)){
int originalSize = dust.getCount();
n-= originalSize;
}else{
//re-add the dust if it didn't match
pouch.addDust(playerStack, dust);
}
}
}
}
if(n>0)missing=true;
//XXX this will update rendering all the time so it might be slow
if(n==0||s.isEmpty()||s.getItem() instanceof DustPlaceholder)ted.setInventorySlotContents(TileEntityDustPlaced.getSlotIDfromPosition(row, col), s.copy());
}
}
if(ted.isEmpty()){//remove the TE if we couldn't place any dust in it
world.removeTileEntity(current);
world.setBlockToAir(current);
}
}else{//in creative mode, just set the contents.
ted.setContents(contents[r][c]);
}
//world.markBlockForUpdate(current);
world.notifyBlockUpdate(current, state, state, 3);
}else{
throw new IllegalStateException("import command: TE was not placed dust");
}
}else if(!emptyBlock){
//we missed a block
missing=true;
}
}
}
if(missing){
player.sendMessage(new TextComponentTranslation(locKey+".incomplete"));
}
world.playSound(null, topLeft, SoundEvents.BLOCK_SAND_PLACE, SoundCategory.BLOCKS, 0.7f, 1.5f);
}
/* (non-Javadoc)
* @see net.minecraft.command.ICommand#canCommandSenderUseCommand(net.minecraft.command.ICommandSender)
*/
@Override
public boolean checkPermission(MinecraftServer server,ICommandSender sender) {
if(!(sender instanceof EntityPlayer))return false;
if(ConfigHandler.commandImportPermission.equals(ConfigHandler.PERMISSIONS_NONE))return false;
if(ConfigHandler.commandImportPermission.equals(ConfigHandler.PERMISSIONS_ALL))return true;
if(ConfigHandler.commandImportPermission.equals(ConfigHandler.PERMISSIONS_OP)){
String[] ops = server.getPlayerList().getOppedPlayerNames();
for(String name:ops){
if(name.equals(sender.getName()))return true;
}
//TODO check if cheats enabled if single player
}
return false;
}
/* (non-Javadoc)
* @see net.minecraft.command.ICommand#addTabCompletionOptions(net.minecraft.command.ICommandSender, java.lang.String[], net.minecraft.util.BlockPos)
*/
@Override
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender,String[] args, BlockPos pos) {
LinkedList<String> options = new LinkedList<String>();
for(String id:DustRegistry.getRuneIDs()){
if(StringUtils.containsIgnoreCase(id, args[0]))options.add(id);
}
return options;
}
/* (non-Javadoc)
* @see net.minecraft.command.ICommand#isUsernameIndex(java.lang.String[], int)
*/
@Override
public boolean isUsernameIndex(String[] args, int index) {
return false;
}
public static CommandImportPattern instance() {
if(instance==null){
instance = new CommandImportPattern();
}
return instance;
}
}