Jump to content

coolAlias

Members
  • Posts

    2805
  • Joined

  • Last visited

Posts posted by coolAlias

  1. Item#getMovingObjectPositionFromPlayer still uses a ray trace, so if the block is not there, you will find the wrong block. However, you will probably be looking at the same face on the returned block as on the one you just broke, since that block is 'behind' it. Still, it won't be 100% accurate, just as my original solution is not 100% accurate - merely an approximation for when it's not application-critical.

     

    If you want 100% accuracy, I suggest you use events like I mentioned earlier.

  2. The only way to be completely 100% accurate is to use raytracing, but that is probably not an option because the block is already broken, so the best you can is get a rough approximation which is what that code does.

     

    In your case, you are looking down at the block (meaning you hit the top, which is why I suggested using #opposite [or don't use opposite if you are]). Try breaking a block not below your feet and you should get a cardinal direction.

     

    If it absolutely MUST be accurate, you are going to have to do some extra work BEFORE the block breaks. Subscribe to PlayerInteractEvent and listen for LEFT_CLICK_BLOCK; from here, figure out the face clicked and store it along with the block position; when the block is broken, check if its position is the last one the player clicked and fetch the side hit from there.

  3. @Draco No, it is not immediately overwritten. Look at the code more carefully - it only assigns TOP or BOTTOM if the pitch angle is greater than 45 or less than 45, so that whole 90 degrees in between is given to the cardinal facings.

     

    Again, it is what vanilla uses somewhere for placing blocks, and I've used it in my own code with much success. If you want to go with a ray trace or something else, that's probably fine, too, provided the block is still in the world to be traced against.

  4. I did something very similar, but used Reflection to auto-register all my variants, and custom resource locations. (Note that the following code was tested in 1.8, so may not work perfectly anymore - I'll be finding that out soon enough, I expect).

     

    Base item interface:

     

    /**
    * 
    * Interface to improve encapsulation and allow automated registration of variants and renderers
    *
    */
    public interface IModItem {
    
    /**
     * Returns an array of variant names to be used in {@link #registerVariants()} and possible {@link #registerRenderers(ItemModelMesher) registerRenderer()} 
     * Typical variant name is "mod_id:" plus the item's unlocalized name, minus any leading prefixes (e.g. 'item.')
     * @return Return null if there are no variants (e.g. standard generic item)
     */
    String[] getVariants();
    
    /**
     * Register any item variant names here using {@link ModelBakery#addVariantName}
     * This MUST be called during {@code FMLPreInitializationEvent}
     * 
     * Typical implementation taking advantage of {@link #getVariants()}:
     * 
     *	String[] variants = getVariants();
     *	if (variants != null) { // allows for alternate single variants, such as "minecraft:spawn_egg"
     *		ModelBakery.addVariantName(this, variants);
     *	}
     */
    @SideOnly(Side.CLIENT)
    void registerVariants();
    
    /**
     * Register all of the Item's renderers here, including for any subtypes.
     * This MUST be called during {@code FMLInitializationEvent}
     * 
     * A default implementation to register an item with modid:itemname would be:
     *
     *	String name = getUnlocalizedName();
     *	name = YourMod.MODID + ":" + name.substring(name.lastIndexOf(".") + 1);
     *	mesher.register(this, 0, new ModelResourceLocation(name, "inventory"));
     */
    @SideOnly(Side.CLIENT)
    void registerRenderers(ItemModelMesher mesher);
    
    }
    

     

     

    Typical implementation of that interface:

     

    /**
    * 
    * Provides generic implementations of IModItem methods that should work for most items.
    *
    */
    public class BaseModItem extends Item implements IModItem
    {
    public BaseModItem() {
    	super();
    }
    
    /**
     * Default behavior returns NULL to not register any variants
     */
    @Override
    public String[] getVariants() {
    	return null;
    }
    
    /**
     * Default implementation suggested by {@link IModItem#registerVariants()}
     */
    @Override
    @SideOnly(Side.CLIENT)
    public void registerVariants() {
    	String[] variants = getVariants();
    	if (variants != null) {
    		ModelBakery.addVariantName(this, variants);
    	}
    }
    
    /**
     * Register all of this Item's renderers here, including for any subtypes.
     * Default behavior registers a single inventory-based mesher for each variant
     * returned by {@link #getVariants() getVariants}.
     * If no variants are available, "mod_id:" plus the item's unlocalized name is used.
     */
    @Override
    @SideOnly(Side.CLIENT)
    public void registerRenderers(ItemModelMesher mesher) {
    	String[] variants = getVariants();
    	if (variants == null || variants.length < 1) {
    		String name = getUnlocalizedName();
    		variants = new String[]{ModInfo.ID + ":" + name.substring(name.lastIndexOf(".") + 1)};
    	}
    	for (int i = 0; i < variants.length; ++i) {
    		mesher.register(this, i, new ModelResourceLocation(variants[i], "inventory"));
    	}
    }
    }

     

     

    My model swapper interface:

     

    /**
    * 
    * Allows automating the {@link ModelBakeEvent} for both Blocks and Items
    *
    */
    @SuppressWarnings("deprecation")
    public interface ISwapModel {
    
    /**
     * Return the default resource locations used to retrieve this object from the model registry
     */
    @SideOnly(Side.CLIENT)
    Collection<ModelResourceLocation> getDefaultResources();
    
    /**
     * Return the class used for the new model
     * The class must have a constructor that takes a single IBakedModel argument
     */
    @SideOnly(Side.CLIENT)
    Class<? extends IBakedModel> getNewModel();
    }
    

     

     

    ModelBakeEvent:

     

    @SubscribeEvent
    public void onBakeModel(ModelBakeEvent event) {
    	for (ModelResourceLocation resource : ClientProxy.smartModels.keySet()) {
    		Object object =  event.modelRegistry.getObject(resource);
    		if (object instanceof IBakedModel) {
    			Class<? extends IBakedModel> clazz = ClientProxy.smartModels.get(resource);
    			try {
    				IBakedModel customRender = clazz.getConstructor(IBakedModel.class).newInstance((IBakedModel) object);
    				event.modelRegistry.putObject(resource, customRender);
    				ZSSMain.logger.debug("Registered new renderer for resource " + resource + ": " + customRender.getClass().getSimpleName());
    			} catch (NoSuchMethodException e) {
    				ZSSMain.logger.warn("Failed to swap model: class " + clazz.getSimpleName() + " is missing a constructor that takes an IBakedModel");
    			} catch (Exception e) {
    				ZSSMain.logger.warn("Failed to swap model with exception: " + e.getMessage());
    			}
    		} else {
    			ZSSMain.logger.warn("Resource is not a baked model! Failed resource: " + resource.toString());
    		}
    	}
    }
    

     

     

    In my ClientProxy:

     

    /** Stores all models which need to be replaced during {@link ModelBakeEvent} */
    @SuppressWarnings("deprecation")
    public static final Map<ModelResourceLocation, Class<? extends net.minecraft.client.resources.model.IBakedModel>> smartModels = Maps.newHashMap();
    
    /**
     * Automated variant and custom state mapper registration for blocks and items
     * Utilizes {@link IModItem#registerVariants()} and {@link ICustomStateMapper#getCustomStateMap()}
     * Call during FMLPreInitializationEvent after all Blocks and Items have been initialized
     */
    private void registerVariants() {
    	try {
    		for (Field f: ZSSBlocks.class.getFields()) {
    			if (Block.class.isAssignableFrom(f.getType())) {
    				Block block = (Block) f.get(null);
    				if (block != null) {
    					if (block instanceof ICustomStateMapper) {
    						ZSSMain.logger.debug("Setting custom state mapper for " + block.getUnlocalizedName());
    						ModelLoader.setCustomStateMapper(block, ((ICustomStateMapper) block).getCustomStateMap());
    					}
    					String name = block.getUnlocalizedName();
    					Item item = GameRegistry.findItem(ModInfo.ID, name.substring(name.lastIndexOf(".") + 1));
    					if (item instanceof IModItem) {
    						((IModItem) item).registerVariants();
    					}
    				}
    			}
    		}
    	} catch(Exception e) {
    		ZSSMain.logger.warn("Caught exception while registering block variants: " + e.toString());
    		e.printStackTrace();
    	}
    	try {
    		for (Field f: ZSSItems.class.getFields()) {
    			if (Item.class.isAssignableFrom(f.getType())) {
    				Item item = (Item) f.get(null);
    				if (item instanceof IModItem) {
    					((IModItem) item).registerVariants();
    				}
    			}
    		}
    	} catch(Exception e) {
    		ZSSMain.logger.warn("Caught exception while registering item variants: " + e.toString());
    		e.printStackTrace();
    	}
    }
    
    /**
     * Automated block and item renderer registration using {@link IModItem#registerRenderers}
     */
    private void registerRenderers() {
    	try {
    		for (Field f: ZSSBlocks.class.getFields()) {
    			if (Block.class.isAssignableFrom(f.getType())) {
    				Block block = (Block) f.get(null);
    				if (block != null) {
    					if (block instanceof ISpecialRenderer) {
    						((ISpecialRenderer) block).registerSpecialRenderer();
    					}
    					if (block instanceof ISwapModel) {
    						addModelToSwap((ISwapModel) block);
    					}
    					String name = block.getUnlocalizedName();
    					Item item = GameRegistry.findItem(ModInfo.ID, name.substring(name.lastIndexOf(".") + 1));
    					if (item instanceof IModItem) {
    						((IModItem) item).registerRenderers(mc.getRenderItem().getItemModelMesher());
    					}
    					if (item instanceof ISwapModel) {
    						addModelToSwap((ISwapModel) item);
    					}
    				}
    			}
    		}
    	} catch(Exception e) {
    		ZSSMain.logger.warn("Caught exception while registering block renderers: " + e.toString());
    		e.printStackTrace();
    	}
    	try {
    		for (Field f: ZSSItems.class.getFields()) {
    			if (Item.class.isAssignableFrom(f.getType())) {
    				Item item = (Item) f.get(null);
    				if (item instanceof IModItem) {
    					((IModItem) item).registerRenderers(mc.getRenderItem().getItemModelMesher());
    				}
    				if (item instanceof ISwapModel) {
    					addModelToSwap((ISwapModel) item);
    				}
    			}
    		}
    	} catch(Exception e) {
    		ZSSMain.logger.warn("Caught exception while registering item renderers: " + e.toString());
    		e.printStackTrace();
    	}
    }
    
    /**
     * Adds the model swap information to the map
     */
    private void addModelToSwap(ISwapModel swap) {
    	for (ModelResourceLocation resource : swap.getDefaultResources()) {
    		if (smartModels.containsKey(resource)) {
    			if (smartModels.get(resource) != swap.getNewModel()) {
    				ZSSMain.logger.warn("Conflicting models for resource " + resource.toString() + ": models=[old: " + smartModels.get(resource).getSimpleName() + ", new: " + swap.getNewModel().getSimpleName());
    			}
    		} else {
    			ZSSMain.logger.debug("Swapping model for " + resource.toString() + " to class " + swap.getNewModel().getSimpleName());
    			smartModels.put(resource, swap.getNewModel());
    		}
    	}
    }
    

     

     

    I think that's about it. Makes the code really nice, imo, when a class is almost entirely self-descriptive; i.e. each class is responsible for itself, rather than expecting random lines of 'support' code all over the place. Here's an example of an Item actually using some of that stuff, and here's a Block.

     

    P.S. There are some who disagree with using getUnlocalizedName in the ways that I have above; if you're one of those, feel free to use something else in your own code :P

  5. This also happens with the recommended 1.8.9 Forge build 1722, even after removing the offending class from the build path. Deleting that package entirely allows the project to build.

     

    Has the Forge team decided to explicitly disallow creating a class with a vanilla Minecraft package declaration, or is this a bug? I hope it's a bug, as it's really handy to access protected methods and fields and has worked great up until now.

     

    Yes, I know I can 'just use Reflection', but it's much more fun this way ;)

     

    EDIT: Btw, the code still works fine while running in Eclipse - it just doesn't want to play nice at compile time.

  6. Unfortunately there isn't a direct way, at least none that I am aware of, but you can use the current rotation and pitch of the entity that broke the block to approximate which direction they broke it from:

    EnumFacing face = EnumFacing.fromAngle(entity.rotationYaw);
    if (entity.rotationPitch < -45.0F) {
    face = EnumFacing.UP;
    } else if (entity.rotationPitch > 45.0F) {
    face = EnumFacing.DOWN;
    }
    

    That's the same code (or essentially the same) that is used in the placement logic for directional blocks, so you probably need to use the opposite facing instead.

  7. Ah, so they are like different blades or something to that effect that the player carries around in a pouch and swaps at will? Or should the upgrades be more like player abilities that they gain when finding / using special items? E.g. after consuming some kind of power-up the player can then channel that power through their scythe?

     

    The reason I ask is because the implementation can be very different depending on exactly how you want it to play out. If they really are items that can be swapped in and out of the scythe on the fly, then sure, your idea of the implementation is fine, but if you only decided to use a pouch because you weren't sure how to implement your first idea (whatever that may have been), then I think we can do better.

     

    Not knocking on the pouch idea, it just doesn't strike me as very intuitive from an in-game lore point of view (possibly due to not having the full back-story).

     

    Anyway, regardless of how you decide to implement it, changing state (server-side) based on a key press (client-side) will require packets - do you have any experience with those?

  8. Right, I understood that's how you intend to implement it, but what exactly are you trying to do in terms of the game? I mean, why does the scythe move an item into the pouch? How are they related?

     

    For example, in my mod I have several projectile weapons that can each use different ammo which can be selected from the player's current inventory, but instead of storing that item in the projectile weapon, I just store the selected ammo type's index and retrieve it when needed.

     

    So, depending on what the relationship of the scythe and pouch is, you may not need to move things, is all I'm trying to say.

  9. #getCollisionBoundingBox is the box used to detect collisions with entities. This is the part Fences set to 1.5F.

     

    #setBlockBoundsBasedOnState sets the block's 'hit box', i.e. the portion of the block that can be clicked, which is the part you see when hovering over the block with your mouse. For fences, this is still maxed out at 1.0F on the y-axis.

  10. You CAN make bounding boxes bigger than 1x1x1, but, in general, you should not. Fences make the collision bounding box taller than 1 block so that players and mobs cannot jump over them, but the fence, as a block, still restricts itself basically to the 1x1x1 cube, which is why you can place blocks on top of the fence and the top block is still only 1 meter rather than 1.5 meters from the ground.

  11. Thank you coolAlias for the quick, descriptive reply.  i will change the forge directory now and post my results when i check it. To clarify though, i have had success crafting other items such as blocks, but not the swords. Thank you choonster, i have registered all of my items i have added in my ModItems Class

    If your other crafting recipes work, it is very unlikely to be an issue with Forge - that's just something to check when all else fails, and I didn't see anything in your recipe that looked off. ;)

     

    Post your sword class, if possible, and any custom recipe handler involved in those recipes.

  12. So to be clear, the crash happens using the vanilla crafting bench with the recipe you showed? If that's the case, try rolling back to an earlier version of Forge such as 1448 and testing if the crash still occurs there.

     

    Some Forge releases occasionally have issues such as this, and testing on a known stable version can at least rule out Forge as the culprit. Personally, I've had the best experience with the 1448 release for 1.7.10, which is why I recommend testing against that.

     

    If you still have issues on 1448, post your custom Item class, if there is one, and any other code that may relate to your issue.

  13. You're already trying to do a hacky solution by making 'impassable air' - why insist on doing it both the hard and wrong way, when it is much easier to simply make real blocks at the positions you want to be real blocks?

     

    Sorry, but making a single Block bigger than 1x1x1, in the context of Minecraft, is a terrible idea. The entire Minecraft code is built around the idea that Blocks take up at most a 1x1x1 cube and that within that cube will only be 1 block - messing with that is just asking for trouble.

     

    You can make multi-block structures such as a 3x3 door, however, by placing and breaking all the blocks together, as I already mentioned. Vanilla doors and beds already do this - you just need to extend the concept to encompass a slightly larger scale.

  14. That happens because you probably didn't do your renderer registration via your proxy, but did it in your main class or somewhere like that which is loaded on both sides; as a result, the server tries to load the renderer classes, which are all marked @SideOnly(Side.CLIENT), and cannot find them.

     

    Any such classes or methods should generally be called through your proxy.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.