Simple kits with bukkit API - api

I am trying to make some simple kitPvP with bukkit API (mainly for learning purposes), but I'm struggling with backing up a player's inventory before they choose a kit.
My code right now: (File: Commands.java, it's getting called from main with onCommand)
public boolean testkit(CommandSender sender, String[] args) {
if(sender instanceof Player) {
String kit = args[0]; // I know, may throw exception
Player player = (Player) sender;
PlayerInventory inventory = player.getInventory();
// Backup inventory into HashMap(?)
if(kit.equalsIgnoreCase("basic")) {
// Clear inventory then give items to player (or replace)
} else if(kit.equalsIgnoreCase("out")) {
// Clear inventory then give backup to player (or replace)
} else {
sender.sendMessage("No such kit.");
return false;
}
return true; // Returns if a good kit selected
} else {
sender.sendMessage("Only players can select kits!");
return false;
}
}
Now, I have a problem with the following parts:
Backing up the inventory of a player
Replacing player's inventory with another inventory
I have no idea how to do these things, because you cannot create a new PlayerInventory instance (it's an interface), and I have no idea what could hold the player's items. (Also I know that a HashMap will be wiped if the server closes, but that's not the point)
Also, I imagine there is some way to replace the player's inventory with another one, but I have absolutely no idea how.
EDIT: Found a rather unelegant solution. Over here,found how to make a new inventory, and made a function to just iterate over the player's inventory and copy the items into the backup.
private void overwrite(Inventory source, Inventory dest) {
for(int i = 0; i < source.getSize(); i++) {
dest.setItem(i, source.getItem(i));
}
}
private Inventory copy(Inventory inventory) {
Inventory copy = Bukkit.createInventory(inventory.getHolder(), inventory.getSize(), inventory.getName());
overwrite(inventory, copy);
return copy;
}
One question though: Will the ItemStack update to the new inventory if changed at old inventory? (Not very crucial here, but important to know IMO)
If it will, any way to prevent that?

What you should be doing is calling getContents() on the Player's inventory, then saving the array that that returns into a HashMap. You then clear() the player's inventory, and individually set the contents of each slot (or, for a more elegant solution, have a ItemStack[] ready for the items of each kit, that you can push into the inventory using setContents()).
Once the player is done with the kit and you want to restore their original Inventory, you just setContents() with the copy of their items that you have stored in the HashMap.
Do note that getContents() and setContents() don't deal with armor slots, so to do that, you'll want to also getArmorContents() and setArmorContents().

Related

Minecraft Forge - Place block onItemUse

I'm trying to make my custom item place water when used:
public class Beaker extends Item implements IHasModel {
public Beaker(String name, CreativeTabs tab, int maxStackSize) {
setUnlocalizedName(name);
setRegistryName(name);
setCreativeTab(tab);
setMaxStackSize(maxStackSize);
ItemInit.ITEMS.add(this);
}
#Override
public void registerModels() {
Main.proxy.registerItemRenderer(this, 0, "inventory");
}
#Override
public EnumActionResult onItemUse(EntityPlayer player, World worldIn, BlockPos pos, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) {
BlockPos clickedBlock = new BlockPos(hitX, hitY, hitZ);
worldIn.setBlockState(clickedBlock, Blocks.WATER.getDefaultState());
return EnumActionResult.SUCCESS;
}
}
However, when I right click, the item gets used (animation plays) but the water is not placed, I'm using Minecraft 1.12.2 and Forge 14.23.2.2613.
hitX hitY and hitZ have no relation to the world location where the action was performed. They are partial values within the clicked block for performing actions based on where the block was clicked (e.g. which button on a number pad).
If we look at ItemBlockSpecial (which handles items like Cake, Repeaters, Brewing Stands, and Cauldrons) we find this code:
public EnumActionResult onItemUse(EntityPlayer player, World worldIn, BlockPos pos, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
IBlockState iblockstate = worldIn.getBlockState(pos);
Block block = iblockstate.getBlock();
// snow layer stuff we don't care about
if (!block.isReplaceable(worldIn, pos))
{
pos = pos.offset(facing);
}
// placement code
}
The important thing to note here is that the pos parameter is used directly, while being modified by the facing parameter in the event that the block clicked is not replaceable (e.g. Tall Grass is replaceable, Stone is not).
If ItemBlockSpecial isn't sufficient for you (i.e. you can't make your item an instance of ItemBlockSpecial), then the code it uses to do things probably still will be.

Render glitch with custom block boundaries minecraft

I'm creating a mod for Minecraft. Recently, I've tried to make a custom block, and I'm having two issues with it.
My main issue is that the block is rendering incorrectly. I want the block to be smaller in size than a full block. I successfully changed the block boundaries with setBlockBounds(), and while that did make the block render smaller and use the smaller boundaries, it causes other rendering issues. When I place the block, the floor below is becomes invisible and I can see through it, either to caves below, blocks behind it, or the void if there is nothing there. How do I fix that block not rendering? Here's a screenshot:
Additionally, my goal for this block is to emit an "aura" that gives players around it speed or some other potion effect. I have the basic code for finding players around the block and giving them speed, but I can't find a way to activate this method every tick or every X amount of ticks to ensure that it gives players within the box speed in a reliable manner. There are already some blocks in the normal game that do this, so it must be possible. How can I do this?
For your first issue, you need to override isOpaqueCube to return false. You'll also want to override isFullCube for other parts of the code, but that isn't as important for rendering. Example:
public class YourBlock {
// ... existing code ...
/**
* Used to determine ambient occlusion and culling when rebuilding chunks for render
*/
#Override
public boolean isOpaqueCube(IBlockState state) {
return false;
}
#Override
public boolean isFullCube(IBlockState state) {
return false;
}
}
Here's some info on rendering that mentions this.
Regarding your second problem, that's more complicated. It's generally achieved via a tile entity, though you can also use block updates (which is much slower). Good examples of this are BlockBeacon and TileEntityBeacon (for using tile entities) and BlockFrostedIce (for block updates). Here's some (potentially out of date) info on tile entities.
Here's an (untested) example of getting an update each tick this with tile entities:
public class YourBlock {
// ... existing code ...
/**
* Returns a new instance of a block's tile entity class. Called on placing the block.
*/
#Override
public TileEntity createNewTileEntity(World worldIn, int meta) {
return new TileEntityYourBlock();
}
}
/**
* Tile entity for your block.
*
* Tile entities normally store data, but they can also receive an update each
* tick, but to do so they must implement ITickable. So, don't forget the
* "implements ITickable".
*/
public class TileEntityYourBlock extends TileEntity implements ITickable {
#Override
public void update() {
// Your code to give potion effects to nearby players would go here
// If you only want to do it every so often, you can check like this:
if (this.worldObj.getTotalWorldTime() % 80 == 0) {
// Only runs every 80 ticks (4 seconds)
}
}
// The following code isn't required to make a tile entity that gets ticked,
// but you'll want it if you want (EG) to be able to set the effect.
/**
* Example potion effect.
* May be null.
*/
private Potion effect;
public void setEffect(Potion potionEffect) {
this.effect = potionEffect;
}
public Potion getEffect() {
return this.effect;
}
#Override
public void readFromNBT(NBTTagCompound compound) {
super.readFromNBT(compound);
int effectID = compound.getInteger("Effect")
this.effect = Potion.getPotionById(effectID);
}
public void writeToNBT(NBTTagCompound compound) {
super.writeToNBT(compound);
int effectID = Potion.getIdFromPotion(this.effect);
compound.setInteger("Effect", effectID);
}
}
// This line needs to go in the main registration.
// The ID can be anything so long as it isn't used by another mod.
GameRegistry.registerTileEntity(TileEntityYourBlock.class, "YourBlock");

Bukkit: Change EXP_Bottle item material to consume instead of throwable

I'm trying to build a bukkit plugin to store XP Levels in a EXP_Bottle.
EXP_Bottle is throwable and releases EXP orbs.
I wanted to make it consumable instead of throwable.
Also, I wanted to get the right event on crafting to remove the EXP after the player grabs the new flask instead of when he places all stuff in the crafting table.
Can anyone help me out?
I do not believe it is possible to change the exp bottle to a consumable as the minecraft client will still think it is a exp bottle. However, you could listening for PlayerInteractEvent and achieve a similar functionality. Example:
#EventHandler
public void interact(PlayerInteractEvent e) {
ItemStack itemStack = e.getItem();
// Check to see if the item is a exp bottle
if (itemStack != null && e.getItem().getType().equals(Material.EXP_BOTTLE)) {
// Cancel the event so it will not be thrown
e.setCancelled(true);
Player player = e.getPlayer();
// Add exp to the player
player.giveExp(1);
// Remove the bottle from the players hand
int newAmount = e.getItem().getAmount() - 1;
if (newAmount > 0)
player.getItemInHand().setAmount(newAmount);
else
player.setItemInHand(null);
}
}
Also, if you want to listen for when an item is crafted you can use CraftItemEvent.
Cheers!

Controller selection

In my title screen, i have a code saying that the first controller using A is the PlayerIndex.one.
Here is the code:
public override void HandleInput(InputState input)
{
for (int anyPlayer = 0; anyPlayer <4; anyPlayer++)
{
if (GamePad.GetState((PlayerIndex)anyPlayer).Buttons.A == ButtonState.Pressed)
{
FirstPlayer = (PlayerIndex)anyPlayer;
this.ExitScreen();
AddScreen(new Background());
}
}
}
My question is: How can i use the "FirstPlayer" in other classes? (without this, there is no interest in this code)
I tried the Get Set thing but i can't make it work. Does i need to put my code in another class? Do you use other code to make this?
Thanks.
You can make a static variable say : SelectedPlayer,
and assign first player to it!
then you can call the first player through this class,
for example
class GameManager
{
public static PlayerIndex SelectedPlayer{get;set;}
..
..
..
}
and right after the loop in your code, you can say:
GameManager.SelectedPlayer = FirstPlayer;
I hope this helps, if your code cold be clearer that would be easier to help :)
Ok, so to do this properly you're going to have to redesign a little.
First off, you should be checking for a new gamepad input (i.e. you should be exiting the screen only when 'A' has been newly pressed). To do this you should be storing previous and current gamepad states:
private GamePadState currentGamePadState;
private GamePadState lastGamePadState;
// in your constructor
currentGamePadState = new GamePadState();
lastGamePadState = new GamePadState();
// in your update
lastGamePadState = currentGamePadState;
currentGamePadState = GamePad.GetState(PlayerIndex.One);
Really what you need to do is modify your class that deals with input. The basic functionality from your HandleInput function should be moved into your input class. Input should have a collection of functions that test for new/current input. For example, for the case you posted:
public Bool IsNewButtonPress(Buttons buton)
{
return (currentGamePadState.IsButtonDown(button) && lastGamePadState.IsButtonUp(button));
}
Then you can write:
public override void HandleInput(InputState input)
{
if (input.IsNewButtonPress(Buttons.A)
{
this.ExitScreen();
AddScreen(new Background());
}
}
Note: this will only work for one controller. To extend the implementation, you'll need to do something like this:
private GamePadState[] currentGamePadStates;
private GamePadState[] lastGamePadStates;
// in your constructor
currentGamePadStates = new GamePadState[4];
currentGamePadStates[0] = new GamePadState(PlayerIndex.One);
currentGamePadStates[1] = new GamePadController(PlayerIndex.Two);
// etc.
lastGamePadStates[0] = new GamePadState(PlayerIndex.One);
// etc.
// in your update
foreach (GamePadState s in currentGamePadStates)
{
// update all of this as before...
}
// etc.
Now, you want to test every controller for input, so you'll need to generalise by writing a function that returns a Bool after checking each GamePadState in the arrays for a button press.
Check out the MSDN Game State Management Sample for a well developed implementation. I can't remember if it supports multiple controllers, but the structure is clear and can easily be adapted if not.

DADiskEject causing problems with error code 12 (kDAReturnUnsupported)

I try to eject external USB drives and Disk Images after being unmounted in the following callback function:
void __unmountCallback(DADiskRef disk, DADissenterRef dissenter, void *context )
{
...
if (!dissenter)
{
DADiskEject(disk,
kDADiskEjectOptionDefault,
__ejectCallback,
NULL);
}
}
Unfortunately I get an error in __ejectCallback...
void __ejectCallback(DADiskRef disk, DADissenterRef dissenter, void * context)
{
if(dissenter)
{
DAReturn status = DADissenterGetStatus(dissenter);
if(unix_err(status))
{
int code = err_get_code(status);
...
}
}
}
The error code is 12 meaning kDAReturnUnsupported. I don't really know what is going wrong. Can anyone please comment on this? Does this mean disk images can not be ejected???
Many thanks in advance!!
The documentation is pretty unclear on this. Therefore, it's a good idea to look into the actual source code of the DARequest class to find out what causes the kDAReturnUnsupported response.
It reveals the following conditions that return a kDAReturnUnsupported response:
Does your DADisk instance represent the entire volume or not?
if ( DADiskGetDescription(disk, kDADiskDescriptionMediaWholeKey) == NULL )
{
status = kDAReturnUnsupported;
}
if ( DADiskGetDescription(disk, kDADiskDescriptionMediaWholeKey) == kCFBooleanFalse )
{
status = kDAReturnUnsupported;
}
Looking into the IO Kit documentation (for which DiscArbitation.framework is a wrapper for), we find that kDADiskDescriptionMediaWholeKey describes whether the media is whole or not (that is, it represents the whole disk or a partition on it), so check that you're ejecting the entire disc and not a partition. Remember, you can unmount a partition, but you can't eject it. (that wouldn't make sense)
Is the disc mountable?
Another condition in DARequest.c is whether the volume is mountable or not, so make sure it is:
if (DADiskGetDescription(disk, kDADiskDescriptionVolumeMountableKey) == kCFBooleanFalse )
{
status = kDAReturnUnsupported;
}
Is the DADisk instance's name valid?
A third check validates the volume's name. Some system provided (internal) volumes don't have a name and can't be ejected. The check is very simple and simply looks for any name, so this shouldn't be a big deal.
if (DARequestGetArgument2(request) == NULL)
{
status = kDAReturnUnsupported;
}
Go through these three checks and see if they apply to you. This way you're bound to find out what's wrong.