There are many modders interested in extending the reach of weapons. In fact I think this is a valid method for creating a gun weapon (instead of creating an actual projectile like some people do).
Although there is a Entity#theItemInWorldManager.setBlockReachDistance() method, it actually doesn't seem to do what we want -- apparently it doesn't affect the client. So we have to do something more involved to get the extended reach working as explained below.
Thanks to the Balkon's Weapon Mod source for giving me the idea for this.
The steps are:
- Create an IExtendedReach interface that will allow you to identify weapons with extended reach, and provide method to get value of the reach.
- Make your custom weapons implement the IExtendedReach interface.
- Create and register a custom IMessage packet for telling server about a successful attack.
- Handle the MouseEvent where you:
- Check whether the mouse left button was clicked.
- Check whether an IExtendedReach item is equipped.
- Calculate a MovingObjectPosition with the reach of the item.
- If the MovingObjectPosition returns an entity, send a custom packet to server to call the attack method on that entity.
Note: This tutorial has code for 1.8. For earlier versions, the same technique works but some method names may have changed.
Create An IExtendedReach Interface
This interface will help us identify items with extended reach and provide a method called getReach() to get the reach value. Something simply like this:
Implement the IExtendedReach Interface In Your Custom Weapon Classes
Take your custom weapons and implement the interface (add implements IExtendedReach to your class declaration). This also means you need to add the getReach() method to return the amount of reach. Something like this:
All I'm doing here is implementing the interface we made in the previous step.
Key Point: The value returned in the getReach() method is the distance of your weapons reach. In this case I've set it to 4.
Reminder: Don't forget to do the other stuff to make your weapon item: register your item (and in 1.8 also register your item renderer), properly locate a texture file, put an entry into your .lang file, etc.
Create An IMessage Custom Packet
In vanilla Minecraft, just like in this example, the detection of what can be reached is calculated on the client but you need the effect to occur on the server. Therefore Minecraft sends a packet called C02PacketUseEntity when there is an attack. We can't use that packet for extended reach though, because to prevent hacking the server double-checks the distance to the attacked entity to ensure it is within reach.
So we need to make a custom packet that essentially does the same thing, but checks against the extended reach instead of the default amount.
This should be fairly understandable if you're familiar with packets. Basically it provides methods to pack and unpack information into byte buffers which are the payloads for the packets, and then the onMessage() method is called when it is received.
For the toBytes() and fromBytes() methods I'm using ByteBufUtils methods. You can also use ByteBuf methods to read and write integers, but I like to make people aware of ByteBufUtils because it provides methods for more complicated data (especially useful for NBT). The value of 4 in the read and write methods is indicating that I think I only need up to 4 bytes to represent the entity ID since I expect it to be less than 4,294,967,296. In fact you could probably use less bytes, but ultimately there isn't much need to save bytes here -- this packet is only sent occasionally.
In this case, in the onMessage() method I'm preventing hacking by confirming that the server agrees that the player has an extended reach weapon equipped and also that the entity hit is within the reach. If so, I call the method for processing the attack, and note that because it is 1.8 (which does networking in separate thread) I have made the method thread-safe.
Register The Custom Packet
In the init handling method of your common proxy, you should register the packet like this:
BlockSmith is the main class of my mod, so you should rename it to the main class of your mod. You'll also need to make a public, static field in your main class for the network.
Handle the MouseEvent
If you're not familiar with handling events, check out Jabelar's Event Tutorial. Remember to register your event handler!
For the actual handling method, you'll want something like this:
The code should be fairly self explanatory, I check that the button is the left one (id = 0) and buttonstate is true if pressed, check that the equipped item implements the IExtendedReach interface, find what the mouse is over (using a ray trace from the render view entity) and attacks it if it is an entity within the reach distance.
The mouseOverExtended() method is copied a lot from the EntityRenderer#mouseOver() method but it returns the MovingObjectPosition and it adjusts the distance based on the reach.
The way it works is that it essentially is taking a ray trace along the look vector (which only detects blocks) and then creates a series of bounding boxes along that vector and checks for entities inside where the vector to them intercepts the look vector. I think...
Test It Out
Run the game and confirm that you can hit entities from distance of 20 blocks away. Change the value of the getReach() return value in your item to change its reach to other amounts.
That's it. Pretty simple really. As always, feel free to comment if you need clarifications or have corrections. Happy modding!