/**
Copyright (C) 2014 by jabelar
This file is part of jabelar's Minecraft Forge modding examples; as such,
you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
For a copy of the GNU General Public License see <http://www.gnu.org/licenses/>.
*/
package com.blogspot.jabelarminecraft.wildanimals.utilities;
import java.util.List;
import java.util.UUID;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks;
import net.minecraft.server.MinecraftServer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.MathHelper;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import net.minecraftforge.common.util.BlockSnapshot;
import com.blogspot.jabelarminecraft.wildanimals.WildAnimals;
import com.blogspot.jabelarminecraft.wildanimals.entities.IModEntity;
import com.blogspot.jabelarminecraft.wildanimals.networking.MessageSyncEntityToClient;
import com.blogspot.jabelarminecraft.wildanimals.networking.MessageSyncEntityToServer;
/**
* @author jabelar
*
*/
public class Utilities
{
/*
* Text Utilities
*/
public static String stringToRainbow(String parString, boolean parReturnToBlack)
{
int stringLength = parString.length();
if (stringLength < 1)
{
return "";
}
String outputString = "";
EnumChatFormatting[] colorChar =
{
EnumChatFormatting.RED,
EnumChatFormatting.GOLD,
EnumChatFormatting.YELLOW,
EnumChatFormatting.GREEN,
EnumChatFormatting.AQUA,
EnumChatFormatting.BLUE,
EnumChatFormatting.LIGHT_PURPLE,
EnumChatFormatting.DARK_PURPLE
};
for (int i = 0; i < stringLength; i++)
{
outputString = outputString+colorChar[i%8]+parString.substring(i, i+1);
}
// return color to a common one after (most chat is white, but for other GUI might want black)
if (parReturnToBlack)
{
return outputString+EnumChatFormatting.BLACK;
}
return outputString+EnumChatFormatting.WHITE;
}
// by default return to white (for chat formatting).
public static String stringToRainbow(String parString)
{
return stringToRainbow(parString, false);
}
public static String stringToGolden(String parString, int parShineLocation, boolean parReturnToBlack)
{
int stringLength = parString.length();
if (stringLength < 1)
{
return "";
}
String outputString = "";
for (int i = 0; i < stringLength; i++)
{
if ((i+parShineLocation+Minecraft.getSystemTime()/20)%88==0)
{
outputString = outputString+EnumChatFormatting.WHITE+parString.substring(i, i+1);
}
else if ((i+parShineLocation+Minecraft.getSystemTime()/20)%88==1)
{
outputString = outputString+EnumChatFormatting.YELLOW+parString.substring(i, i+1);
}
else if ((i+parShineLocation+Minecraft.getSystemTime()/20)%88==87)
{
outputString = outputString+EnumChatFormatting.YELLOW+parString.substring(i, i+1);
}
else
{
outputString = outputString+EnumChatFormatting.GOLD+parString.substring(i, i+1);
}
}
// return color to a common one after (most chat is white, but for other GUI might want black)
if (parReturnToBlack)
{
return outputString+EnumChatFormatting.BLACK;
}
return outputString+EnumChatFormatting.WHITE;
}
// by default return to white (for chat formatting).
public static String stringToGolden(String parString, int parShineLocation)
{
return stringToGolden(parString, parShineLocation, false);
}
public static Entity getEntityByID(int entityID, World world)
{
for(Object o: world.getLoadedEntityList())
{
if(((Entity)o).getEntityId() == entityID)
{
// DEBUG
// System.out.println("Found the entity");
return ((Entity)o);
}
}
return null;
}
/**
* Based on code from http://pages.cs.wisc.edu/~ltorrey/cs302/examples/PigLatinTranslator.java
* Method to translate a sentence word by word.
* @param s The sentence in English
* @return The pig latin version
*/
public static String toPigLatin(String s)
{
String latin = "";
int i = 0;
while (i<s.length())
{
// Take care of punctuation and spaces
while (i<s.length() && !isLetter(s.charAt(i)))
{
latin = latin + s.charAt(i);
i++;
}
// If there aren't any words left, stop.
if (i>=s.length()) break;
// Otherwise we're at the beginning of a word.
int begin = i;
while (i<s.length() && isLetter(s.charAt(i)))
{
i++;
}
// Now we're at the end of a word, so translate it.
int end = i;
latin = latin + pigWord(s.substring(begin, end));
}
return latin;
}
/**
* Method to test whether a character is a letter or not.
* @param c The character to test
* @return True if it's a letter
*/
private static boolean isLetter(char c)
{
return ( (c >='A' && c <='Z') || (c >='a' && c <='z') );
}
/**
* Method to translate one word into pig latin.
* @param word The word in english
* @return The pig latin version
*/
private static String pigWord(String word)
{
int split = firstVowel(word);
return word.substring(split)+"-"+word.substring(0, split)+"ay";
}
/**
* Method to find the index of the first vowel in a word.
* @param word The word to search
* @return The index of the first vowel
*/
private static int firstVowel(String word)
{
word = word.toLowerCase();
for (int i=0; i<word.length(); i++)
{
if (word.charAt(i)=='a' || word.charAt(i)=='e' ||
word.charAt(i)=='i' || word.charAt(i)=='o' ||
word.charAt(i)=='u')
{
return i;
}
}
return 0;
}
/*
* Networking packet utilities
*/
public static void sendEntitySyncPacketToClient(IModEntity parEntity)
{
Entity theEntity = (Entity)parEntity;
if (!theEntity.worldObj.isRemote)
{
// // DEBUG
// System.out.println("sendEntitySyncPacket from server");
WildAnimals.network.sendToAll(new MessageSyncEntityToClient(theEntity.getEntityId(), parEntity.getSyncDataCompound()));
}
}
public static void sendEntitySyncPacketToServer(IModEntity parEntity)
{
Entity theEntity = (Entity)parEntity;
if (theEntity.worldObj.isRemote)
{
// DEBUG
System.out.println("sendEntitySyncPacket from client");
WildAnimals.network.sendToServer(new MessageSyncEntityToServer(theEntity.getEntityId(), parEntity.getSyncDataCompound()));
}
}
/**
* Sets the block ID and metadata at a given location. Args: X, Y, Z, new block ID, new metadata, flags. Flag 1 will
* cause a block update. Flag 2 will send the change to clients (you almost always want parChunk). Flag 4 prevents the
* block from being re-rendered, if parChunk is a client world. Flags can be added together.
*/
public static boolean setBlockFast(World parWorld, int parX, int parY, int parZ, Block parBlock, int parMetaData, int parFlag)
{
// Make sure position is within valid range
if (parX >= -30000000 && parZ >= -30000000 && parX < 30000000 && parZ < 30000000)
{
if (parY < 0)
{
return false;
}
else if (parY >= 256)
{
return false;
}
else
{
Chunk chunk = parWorld.getChunkFromChunkCoords(parX >> 4, parZ >> 4);
Block existingBlock = null;
BlockSnapshot blockSnapshot = null;
if ((parFlag & 1) != 0)
{
existingBlock = chunk.getBlock(parX & 15, parY, parZ & 15);
}
if (parWorld.captureBlockSnapshots && !parWorld.isRemote)
{
blockSnapshot = BlockSnapshot.getBlockSnapshot(parWorld, parX, parY, parZ, parFlag);
parWorld.capturedBlockSnapshots.add(blockSnapshot);
}
boolean setBlockSuceeded = setBlockInChunkFast(chunk, parX & 15, parY, parZ & 15, parBlock, parMetaData);
if (!setBlockSuceeded && blockSnapshot != null)
{
parWorld.capturedBlockSnapshots.remove(blockSnapshot);
blockSnapshot = null;
}
if (setBlockSuceeded && blockSnapshot == null) // Don't notify clients or update physics while capturing blockstates
{
// Modularize client and physic updates
parWorld.markAndNotifyBlock(parX, parY, parZ, chunk, existingBlock, parBlock, parFlag);
}
return setBlockSuceeded;
}
}
else
{
return false;
}
}
public static boolean setBlockInChunkFast(Chunk parChunk, int parX, int parY, int parZ, Block parBlock, int parMetaData)
{
int mapKey = parZ << 4 | parX;
if (parY >= parChunk.precipitationHeightMap[mapKey] - 1)
{
parChunk.precipitationHeightMap[mapKey] = -999;
}
Block existingBlock = parChunk.getBlock(parX, parY, parZ);
int existingMetaData = parChunk.getBlockMetadata(parX, parY, parZ);
if (existingBlock == parBlock && existingMetaData == parMetaData)
{
return false;
}
else
{
ExtendedBlockStorage extendedblockstorage = parChunk.getBlockStorageArray()[parY >> 4];
if (extendedblockstorage == null)
{
if (parBlock == Blocks.air)
{
return false;
}
extendedblockstorage = parChunk.getBlockStorageArray()[parY >> 4] = new ExtendedBlockStorage(parY >> 4 << 4, !parChunk.worldObj.provider.hasNoSky);
}
int worldPosX = parChunk.xPosition * 16 + parX;
int worldPosZ = parChunk.zPosition * 16 + parZ;
if (!parChunk.worldObj.isRemote)
{
existingBlock.onBlockPreDestroy(parChunk.worldObj, worldPosX, parY, worldPosZ, existingMetaData);
}
extendedblockstorage.setExtBlockID(parX, parY & 15, parZ, parBlock);
extendedblockstorage.setExtBlockMetadata(parX, parY & 15, parZ, parMetaData); // This line duplicates the one below, so breakBlock fires with valid worldstate
if (!parChunk.worldObj.isRemote)
{
existingBlock.breakBlock(parChunk.worldObj, worldPosX, parY, worldPosZ, existingBlock, existingMetaData);
// After breakBlock a phantom TE might have been created with incorrect meta. This attempts to kill that phantom TE so the normal one can be create properly later
TileEntity te = parChunk.getTileEntityUnsafe(parX & 0x0F, parY, parZ & 0x0F);
if (te != null && te.shouldRefresh(existingBlock, parChunk.getBlock(parX & 0x0F, parY, parZ & 0x0F), existingMetaData, parChunk.getBlockMetadata(parX & 0x0F, parY, parZ & 0x0F), parChunk.worldObj, worldPosX, parY, worldPosZ))
{
parChunk.removeTileEntity(parX & 0x0F, parY, parZ & 0x0F);
}
}
else if (existingBlock.hasTileEntity(existingMetaData))
{
TileEntity te = parChunk.getTileEntityUnsafe(parX & 0x0F, parY, parZ & 0x0F);
if (te != null && te.shouldRefresh(existingBlock, parBlock, existingMetaData, parMetaData, parChunk.worldObj, worldPosX, parY, worldPosZ))
{
parChunk.worldObj.removeTileEntity(worldPosX, parY, worldPosZ);
}
}
if (extendedblockstorage.getBlockByExtId(parX, parY & 15, parZ) != parBlock)
{
return false;
}
else
{
extendedblockstorage.setExtBlockMetadata(parX, parY & 15, parZ, parMetaData);
TileEntity tileentity;
if (!parChunk.worldObj.isRemote)
{
parBlock.onBlockAdded(parChunk.worldObj, worldPosX, parY, worldPosZ);
}
if (parBlock.hasTileEntity(parMetaData))
{
tileentity = parChunk.getBlockTileEntityInChunk(parX, parY, parZ);
if (tileentity != null)
{
tileentity.updateContainingBlockInfo();
tileentity.blockMetadata = parMetaData;
}
}
parChunk.isModified = true;
return true;
}
}
}
// this will work across all dimensions
// thanks to diesieben07 for this tip on http://www.minecraftforge.net/forum/index.php?topic=27715.0
public static EntityPlayer getPlayerOnServerFromUUID(UUID parUUID)
{
if (parUUID == null)
{
return null;
}
List<EntityPlayerMP> allPlayers = MinecraftServer.getServer().getConfigurationManager().playerEntityList;
for (EntityPlayerMP player : allPlayers)
{
if (player.getUniqueID().equals(parUUID))
{
return player;
}
}
return null;
}
/**
* A method used to see if an entity is a suitable target through a number of checks.
*/
public static boolean isSuitableTarget(EntityLivingBase theAttackerEntity,
EntityLivingBase parPossibleTargetEntity,
boolean parShouldCheckSight)
{
if (parPossibleTargetEntity == null)
{
// // DEBUG
// System.out.println("Target isn't suitable because it is null");
return false;
}
else if (parPossibleTargetEntity == theAttackerEntity)
{
// DEBUG
System.out.println("Target isn't suitable because it is itself");
return false;
}
else if (!parPossibleTargetEntity.isEntityAlive())
{
// DEBUG
System.out.println("Target isn't suitable because it is dead");
return false;
}
else if (theAttackerEntity.isOnSameTeam(parPossibleTargetEntity))
{
// DEBUG
System.out.println("Target isn't suitable because it is on same team");
return false;
}
// else if (parPossibleTargetEntity instanceof EntityPlayer && ((EntityPlayer)parPossibleTargetEntity).capabilities.disableDamage)
// {
// // DEBUG
// System.out.println("Target isn't suitable because player can't take damage");
// return false;
// }
else if (theAttackerEntity instanceof EntityLiving && parShouldCheckSight)
{
// DEBUG
System.out.println("The attacker can see target = "+((EntityLiving)theAttackerEntity).getEntitySenses().canSee(parPossibleTargetEntity));
return ((EntityLiving)theAttackerEntity).getEntitySenses().canSee(parPossibleTargetEntity);
}
else
{
return true;
}
}
// // This is mostly copied from the EntityRenderer#getMouseOver() method
// public static MovingObjectPosition getMouseOverExtended(float parDist)
// {
// double dist = parDist;
// Minecraft mc = FMLClientHandler.instance().getClient();
// EntityLivingBase theRenderViewEntity = mc.renderViewEntity;
// AxisAlignedBB theViewBoundingBox = AxisAlignedBB.getBoundingBox(
// theRenderViewEntity.posX-0.5D,
// theRenderViewEntity.posY-0.0D,
// theRenderViewEntity.posZ-0.5D,
// theRenderViewEntity.posX+0.5D,
// theRenderViewEntity.posY+1.5D,
// theRenderViewEntity.posZ+0.5D
// );
// MovingObjectPosition returnMOP = null;
// if (mc.theWorld != null)
// {
// returnMOP = theRenderViewEntity.rayTrace(parDist, 0);
// Vec3 pos = theRenderViewEntity.getPosition(0).addVector(0.0D, theRenderViewEntity.getEyeHeight(), 0.0D);
// if (returnMOP != null)
// {
// dist = returnMOP.hitVec.distanceTo(pos);
// }
//
// Vec3 lookvec = theRenderViewEntity.getLook(0);
// Vec3 var8 = pos.addVector(lookvec.xCoord * dist, lookvec.yCoord * dist, lookvec.zCoord * dist);
// Entity pointedEntity = null;
// float var9 = 1.0F;
// @SuppressWarnings("unchecked")
// List<Entity> list = mc.theWorld.getEntitiesWithinAABBExcludingEntity(theRenderViewEntity, theViewBoundingBox.addCoord(lookvec.xCoord * dist, lookvec.yCoord * dist, lookvec.zCoord * dist).expand(var9, var9, var9));
//
// for (Entity entity : list)
// {
// if (entity.canBeCollidedWith())
// {
// float bordersize = entity.getCollisionBorderSize();
// AxisAlignedBB aabb = AxisAlignedBB.getBoundingBox(entity.posX-entity.width/2, entity.posY, entity.posZ-entity.width/2, entity.posX+entity.width/2, entity.posY+entity.height, entity.posZ+entity.width/2);
// aabb.expand(bordersize, bordersize, bordersize);
// MovingObjectPosition mop0 = aabb.calculateIntercept(pos, var8);
//
// if (aabb.isVecInside(pos))
// {
// if (0.0D < dist || dist == 0.0D)
// {
// pointedEntity = entity;
// dist = 0.0D;
// }
// } else if (mop0 != null)
// {
// double d1 = pos.distanceTo(mop0.hitVec);
//
// if (d1 < d || d == 0.0D)
// {
// pointedEntity = entity;
// d = d1;
// }
// }
// }
// }
//
// if (pointedEntity != null && (d < calcdist || returnMOP == null))
// {
// returnMOP = new MovingObjectPosition(pointedEntity);
// }
//
// }
// return returnMOP;
// }
public static float getYawFromVec(Vec3 parVec)
{
// The coordinate system for Minecraft is a bit backwards as explained
// at https://github.com/chraft/c-raft/wiki/Vectors,-Location,-Yaw-and-Pitch-in-C%23raft
return (float) -Math.toDegrees(Math.atan2(parVec.xCoord, parVec.zCoord));
}
public static float getPitchFromVec(Vec3 parVec)
{
// The coordinate system for Minecraft is a bit backwards as explained
// at https://github.com/chraft/c-raft/wiki/Vectors,-Location,-Yaw-and-Pitch-in-C%23raft
Vec3 theVec = parVec.normalize();
return (float) Math.toDegrees(Math.asin(theVec.yCoord));
}
/**
* True if the entity has an unobstructed line of travel to the waypoint.
*/
public static boolean isCourseTraversable(Entity parEntity, double parX, double parY, double parZ)
{
double theDistance = MathHelper.sqrt_double(parX * parX + parY * parY + parZ * parZ);
double incrementX = (parX - parEntity.posX) / theDistance;
double incrementY = (parY - parEntity.posY) / theDistance;
double incrementZ = (parZ - parEntity.posZ) / theDistance;
AxisAlignedBB entityBoundingBox = parEntity.boundingBox.copy();
for (int i = 1; i < theDistance; ++i)
{
entityBoundingBox.offset(incrementX, incrementY, incrementZ);
if (!parEntity.worldObj.getCollidingBoundingBoxes(parEntity, entityBoundingBox).isEmpty())
{
return false;
}
}
return true;
}
}