001    /**
002     * HitBlox.java - Class for getting blocks along line of sight
003     * 
004     * NOTES: This class is designed to handle the annoying parts of the seemingly
005     * simple task of getting the coordinates of the block a player is currently
006     * aimed at. This class abstracts the simpler tasks of finding the current
007     * target block and the adjacent unoccupied block to their own methods, but it
008     * also provides a public getNextBlock method for processing the entire
009     * line-of-sight from the player for more specialized tasks. This method can be
010     * used exactly as it is in getTargetBlock, for instance.
011     * 
012     * WARNING: Servers with map coordinate bugs may experience a one or more block
013     * inaccuracy when in affected parts of the world. A good way to test areas for
014     * the offset bug is to use Chrisinajar's Magic Carpet plugin.
015     * 
016     * Contact: For questions, contact Ho0ber@gmail.com or channel #hey0 on
017     * irc.esper.net
018     * 
019     * @author Ho0ber
020     */
021    public class HitBlox {
022    
023        private Location player_loc;
024        private double rot_x, rot_y, view_height;
025        private double length, h_length, step;
026        private int range;
027        private double x_offset, y_offset, z_offset;
028        private int last_x, last_y, last_z;
029        private int target_x, target_y, target_z, target_type;
030    
031        /**
032         * Constructor requiring player, uses default values
033         * 
034         * @param in_player
035         */
036        public HitBlox(Player in_player) {
037            init(in_player.getLocation(), 200, 0.2, 1.65); // Reasonable default
038            // values
039        }
040    
041        /**
042         * Constructor requiring location, uses default values
043         * 
044         * @param in_location
045         */
046        public HitBlox(Location in_location) {
047            init(in_location, 200, 0.2, 0);
048        }
049    
050        /**
051         * Constructor requiring player, max range, and a stepping value
052         * 
053         * @param in_player
054         * @param in_range
055         * @param in_step
056         */
057        public HitBlox(Player in_player, int in_range, double in_step) {
058            init(in_player.getLocation(), in_range, in_step, 1.65);
059        }
060    
061        /**
062         * Constructor requiring location, max range, and a stepping value
063         * 
064         * @param in_location
065         * @param in_range
066         * @param in_step
067         */
068        public HitBlox(Location in_location, int in_range, double in_step) {
069            init(in_location, in_range, in_step, 0);
070        }
071    
072        /**
073         * Initialization method
074         * 
075         * @param in_location
076         * @param in_range
077         * @param in_step
078         * @param in_view_height
079         */
080        public void init(Location in_location, int in_range, double in_step, double in_view_height) {
081            player_loc = in_location;
082            view_height = in_view_height;
083            range = in_range;
084            step = in_step;
085            length = 0;
086            rot_x = (player_loc.rotX + 90) % 360;
087            rot_y = player_loc.rotY * -1;
088    
089            target_x = (int) Math.floor(player_loc.x);
090            target_y = (int) Math.floor(player_loc.y + view_height);
091            target_z = (int) Math.floor(player_loc.z);
092            last_x = target_x;
093            last_y = target_y;
094            last_z = target_z;
095        }
096    
097        /**
098         * Returns the block at the cursor, or null if out of range
099         * 
100         * @return Block
101         */
102        public Block getTargetBlock() {
103            while ((getNextBlock() != null) && (getCurBlock().getType() == 0));
104            return getCurBlock();
105        }
106    
107        /**
108         * Sets the type of the block at the cursor
109         * 
110         * @param type
111         */
112        public void setTargetBlock(int type) {
113            while ((getNextBlock() != null) && (getCurBlock().getType() == 0));
114            if (getCurBlock() != null) {
115                etc.getServer().setBlockAt(type, target_x, target_y, target_z);
116            }
117        }
118    
119        /**
120         * Returns the block attached to the face at the cursor, or null if out of
121         * range
122         * 
123         * @return Block
124         */
125        public Block getFaceBlock() {
126            while ((getNextBlock() != null) && (getCurBlock().getType() == 0));
127            if (getCurBlock() != null) {
128                return getLastBlock();
129            } else {
130                return null;
131            }
132        }
133    
134        /**
135         * Sets the type of the block attached to the face at the cursor
136         * 
137         * @param type
138         */
139        public void setFaceBlock(int type) {
140            while ((getNextBlock() != null) && (getCurBlock().getType() == 0));
141            if (getCurBlock() != null) {
142                etc.getServer().setBlockAt(type, last_x, last_y, last_z);
143            }
144        }
145    
146        /**
147         * Returns STEPS forward along line of vision and returns block
148         * 
149         * @return Block
150         */
151        public Block getNextBlock() {
152            last_x = target_x;
153            last_y = target_y;
154            last_z = target_z;
155    
156            do {
157                length += step;
158    
159                h_length = (length * Math.cos(Math.toRadians(rot_y)));
160                y_offset = (length * Math.sin(Math.toRadians(rot_y)));
161                x_offset = (h_length * Math.cos(Math.toRadians(rot_x)));
162                z_offset = (h_length * Math.sin(Math.toRadians(rot_x)));
163    
164                target_x = (int) Math.floor(x_offset + player_loc.x);
165                target_y = (int) Math.floor(y_offset + player_loc.y + view_height);
166                target_z = (int) Math.floor(z_offset + player_loc.z);
167    
168            } while ((length <= range) && ((target_x == last_x) && (target_y == last_y) && (target_z == last_z)));
169    
170            if (length > range) {
171                return null;
172            }
173    
174            return etc.getServer().getBlockAt(target_x, target_y, target_z);
175        }
176    
177        /**
178         * Returns the current block along the line of vision
179         * 
180         * @return Block
181         */
182        public Block getCurBlock() {
183            if (length > range) {
184                return null;
185            } else {
186                return etc.getServer().getBlockAt(target_x, target_y, target_z);
187            }
188        }
189    
190        /**
191         * Sets current block type id
192         * 
193         * @param type
194         */
195        public void setCurBlock(int type) {
196            if (getCurBlock() != null) {
197                etc.getServer().setBlockAt(type, target_x, target_y, target_z);
198            }
199        }
200    
201        /**
202         * Returns the previous block along the line of vision
203         * 
204         * @return Block
205         */
206        public Block getLastBlock() {
207            return etc.getServer().getBlockAt(last_x, last_y, last_z);
208        }
209    
210        /**
211         * Sets previous block type id
212         * 
213         * @param type
214         */
215        public void setLastBlock(int type) {
216            if (getLastBlock() != null) {
217                etc.getServer().setBlockAt(type, last_x, last_y, last_z);
218            }
219        }
220    }