· 8 years ago · Aug 21, 2018, 12:10 PM
1-- Laser sentry based mob farm
2-- by SukaiPoppuGo
3--
4-- Aim and kill mobs inside a darkroom surrounding mob_spawner
5-- Manage lighting to prevent spawn
6-- Laser-proof materials : obsidian
7-- Laser-proof up to 4.9 potency : mob_spawner, iron_bars, iron_block, emerald_block, diamond_block...
8-- Blocks by hardness source : https://minecraft.gamepedia.com/Breaking#Blocks_by_hardness
9--
10--------------------------------------------------
11-- Setup caution:
12-- Laser sentry require mob_spawner relative position (from the laser position).
13-- Install block scanner module, or create a settings file "mob_spawnerPosition" with each relative coordinates:
14-- {
15-- mob_spawner.x = number,
16-- mob_spawner.y = number,
17-- mob_spawner.z = number,
18-- }
19-- Example:
20-- You place the laser in front of mob_spawner with 4 blocks space between.
21-- Take place behind the laser, look in direction of mob_spawner and press F3
22-- Note the axis indicator. For example :
23-- "Facing: south (Towards positive Z)" your setup is {x = 0, y = 0, z = 5}
24-- "Facing: west (Towards negative X)" your setup is {x = -5, y = 0, z = 0}
25-- If your are not confortable with this procedure, add a block scanner module
26-- for an automatic setup. When this settings is saved, you can remove it.
27--
28-- The author decline all responsability about injuries or grieffing caused by
29-- laser beam. You should test your system before in creative mode, then config
30-- and setup carefully.
31--
32--------------------------------------------------
33-- inspired/based : https://squiddev-cc.github.io/plethora/examples/laser-sentry.html
34-- This program finds hostile mobs and fires lasers at them, acting like a sentry tower.
35-- Credit: SquidDev-CC
36--
37
38
39--------------------------------------------------
40--
41local SETTINGS_FILE = "mob_spawnerPosition"
42
43--------------------------------------------------
44-- Display manager
45os.loadAPI("api/LaserDisplay.lua")
46assert(LaserDisplay, "Require LaserDisplay API\npastebin get zYhzrKEg api/LaserDisplay.lua")
47
48--------------------------------------------------
49-- init modules
50local modules = peripheral.find("manipulator")
51if not modules then
52 error("Cannot find manipulator", 0)
53end
54-- We require an entity sensor to find mobs and a laser to shoot at them. We error if neither exists.
55if not modules.hasModule("plethora:laser") then error("Cannot find laser", 0) end
56if not modules.hasModule("plethora:sensor") then error("Cannot find entity sensor", 0) end
57
58--------------------------------------------------
59-- Laser potency
60-- deal 4 damage / potency
61-- mine 1 hardness / potency
62local MAX_POTENCY = 4.9 -- Maximum potency (mob_spawner hardeness = 5)
63local MIN_POTENCY = 0.5 -- Minimum potency
64local MIN_POTENCY_RECOVERY = 0.2 --delay in seconds
65
66local DEBUG_MODE = false
67
68--------------------------------------------------
69-- Config mob_spawner relative position
70os.loadAPI("api/LaserConfig.lua")
71assert(LaserConfig, "Require LaserConfig API\npastebin get GJhqHVcX api/LaserConfig.lua")
72LaserConfig.file.set(SETTINGS_FILE)
73local mob_spawner = LaserConfig.init()
74
75--------------------------------------------------
76-- Avoid to shoot outside this range
77-- Example for mob_spawner = {x = 0, y = 0, z = 5}
78--local RANGE_RESTRICT = {
79-- x1 = 5, y1 = 2, z1 = 10,
80-- x2 = -4, y2 = -4, z2 = 1,
81--}
82--
83local RANGE_RESTRICT = {
84 x1 = mob_spawner.x+5, y1 = mob_spawner.y+2, z1 = mob_spawner.z+5,
85 x2 = mob_spawner.x-4, y2 = mob_spawner.y-4, z2 = mob_spawner.z-4,
86}
87
88
89--------------------------------------------------
90-- Init Entity whitelist
91local entityWhitelist = settings.get("mob_spawner.whiteList", false)
92if not entityWhitelist then
93 entityWhitelist = LaserConfig.getWhiteList()
94end
95entityWhitelist["SukaiPoppuGo"] = false --safety first
96
97
98--------------------------------------------------
99-- Max/Min range of mob positions
100local stats = {
101 maxX = 0, minX = math.huge,
102 maxY = 0, minY = math.huge,
103 maxZ = 0, minZ = math.huge,
104}
105
106--------------------------------------------------
107-- function stats.update
108-- Update Max/Min range of mob positions
109--
110-- param number x Current mob X position
111-- param number y Current mob Y position
112-- param number z Current mob Z position
113function stats.update(x,y,z)
114 LaserDisplay.status("stats.update")
115 stats.maxX = math.max(x, stats.maxX)
116 stats.minX = math.min(x, stats.minX)
117 stats.maxY = math.max(y, stats.maxY)
118 stats.minY = math.min(y, stats.minY)
119 stats.maxZ = math.max(z, stats.maxZ)
120 stats.minZ = math.min(z, stats.minZ)
121end
122
123--------------------------------------------------
124-- function lights
125-- Redstone Mgr
126--
127-- param boolean state (opt) True set lights On, False set lights Off, nil toggle lights
128local function lights(state)
129 LaserDisplay.status("lights")
130 state = state or not rs.getOutput("bottom")
131 rs.setOutput("left", state)
132 rs.setOutput("right", state)
133 rs.setOutput("bottom", state)
134 rs.setOutput("top", true)
135end
136-- init
137lights(true)
138
139
140--------------------------------------------------
141-- Display group
142-- window
143--
144
145function update()
146 LaserDisplay.status("update")
147 LaserDisplay.range(stats.maxX, stats.minX, stats.maxY, stats.minY, stats.maxZ, stats.minZ)
148 LaserDisplay.update()
149end
150
151
152--------------------------------------------------
153-- function keyTip
154-- Formate tips about keys functionality
155--
156-- param number k Key code from keys table
157-- param string tip Information for users
158-- return string Formated information
159local function keyTip(k, tip)
160 local sK = string.len(keys.getName(k)) > 1 and keys.getName(k) or string.upper(keys.getName(k))
161 return string.format("press [%s] %s", sK, tip)
162end
163
164--------------------------------------------------
165-- Action group for user
166local action = {}
167
168
169--------------------------------------------------
170-- function action.toggleLights
171-- turn On / shutdown lights
172function action.toggleLights()
173 LaserDisplay.status("toggleLights")
174 lights()
175end
176
177
178--------------------------------------------------
179-- function action.toggleLaser
180-- enable/disable Laser
181local laserState = false
182function action.toggleLaser()
183 LaserDisplay.status("toggleLaser")
184 laserState = not laserState
185end
186
187--------------------------------------------------
188-- function control
189-- Restore control inputs between delay
190--
191-- param: duration number delay length in seconds
192local function control(duration)
193 LaserDisplay.status("control")
194 local delay = os.startTimer(duration)
195 repeat
196 local e = {os.pullEvent()}
197 if e[1] == "mouse_click" and e[2] == 1 and e[4] == 1 then
198 lights()
199 LaserDisplay.header("Toggle lights")
200 elseif not term.isColor() and e[1] == "key" then
201 local _,k = os.pullEvent("key_up")
202 if e[2] == k then
203
204 end
205 end
206 until e[1] == "timer" and e[2] == delay
207end
208
209--------------------------------------------------
210-- function fire
211-- Perform a shoot
212--
213-- We define a function which fires a laser towards an entity. This is a very naive implementation
214-- as it does not account for the entity moving between firing and impact.
215-- You could use the motionX, motionY and motionZ fields if you wish to add such functionality.
216-- CFG: damage=4 The damage done to an entity for each potency.
217--
218-- x, y, z number Relative position
219-- param: potency number
220local function fire(x, y, z, potency)
221 LaserDisplay.status("fire")
222 local pitch = -math.atan2(y, math.sqrt(x * x + z * z))
223 local yaw = math.atan2(-x, z)
224 modules.fire(math.deg(yaw), math.deg(pitch), potency)
225end
226
227--------------------------------------------------
228-- function inRange
229-- Range restrictions
230--
231-- param x number Relative position X
232-- param y number Relative position Y
233-- param z number Relative position Z
234-- return boolean Coordinate are inside LaserDisplay.range
235local function inRange(x,y,z)
236 LaserDisplay.status("inRange")
237 local r = RANGE_RESTRICT
238 local _x = (x >= math.min(r.x1, r.x2) and x <= math.max(r.x1, r.x2))
239 local _y = (y >= math.min(r.y1, r.y2) and y <= math.max(r.y1, r.y2))
240 local _z = (z >= math.min(r.z1, r.z2) and z <= math.max(r.z1, r.z2))
241 if DEBUG_MODE and not(_x and _y and _z) then --debug
242 LaserDisplay.output.print(tostring(_x), ":", x, ">=", math.min(r.x1, r.x2), "and <=", math.max(r.x1, r.x2))
243 LaserDisplay.output.print(tostring(_y), ":", y, ">=", math.min(r.y1, r.y2), "and <=", math.max(r.y1, r.y2))
244 LaserDisplay.output.print(tostring(_z), ":", z, ">=", math.min(r.z1, r.z2), "and <=", math.max(r.z1, r.z2))
245 LaserDisplay.output.print("-Press any key-")
246 os.pullEvent("key") os.pullEvent("key_up")
247 end
248 return _x and _y and _z
249end
250
251--------------------------------------------------
252-- function fireInRange
253-- Manage Firing in good conditions
254-- Perform cooldown delay and LaserDisplay.output informations
255--
256-- param entity table Item result from modules.sense()
257-- return boolean fired A shoot sequence were done
258local function fireInRange(entity)
259 LaserDisplay.status("fireInRange")
260 --init
261 local fired = false
262 local mob = modules.getMetaByID(entity.id)
263
264 -- Mob is alive
265 if mob and mob.health and mob.health > 0 then
266 --Fire sequence to death
267 while mob and mob.health and mob.health > 0 do
268 -- Precise coordinates
269 local x = mob.x + (mob.motionX or 0)
270 local y = mob.y + (mob.motionY or 0)
271 local z = mob.z + (mob.motionZ or 0)
272 -- Max range statistics
273 stats.update(x,y,z)
274
275 -- Target in range
276 if inRange(x,y,z) then
277
278 local potency = math.min(MAX_POTENCY, math.max(MIN_POTENCY, math.ceil(mob.health*10/4)/10))
279 local delay = math.max(potency * .1 + .1, MIN_POTENCY_RECOVERY)
280 -- Display
281 LaserDisplay.output.print(
282 "--\nFire mob at X:", math.floor(x*10+5)/10,
283 "Y:", math.floor(y*10+5)/10,
284 "Z:",math.floor(z*10+5)/10,
285 "\n",
286 "heatlh:", math.floor(mob.health*10+5)/10,
287 "potency:", potency,
288 "delay:", delay
289 )
290 --FIRE!
291 fire(x, y, z, potency)
292 fired = true
293 --Delay
294 control(delay)
295
296 -- Out of range
297 else
298 -- Display
299 LaserDisplay.output.print(
300 "--\nMob at X:", math.floor(x*10+5)/10,
301 "Y:", math.floor(y*10+5)/10,
302 "Z:",math.floor(z*10+5)/10
303 )
304 LaserDisplay.output.printError("Out of range")
305 break
306 end
307
308 -- refresh mob datas
309 mob = modules.getMetaByID(entity.id)
310 end
311
312 -- Mob is dead
313 else
314 -- Display
315 LaserDisplay.output.print(
316 "--\nMob at X:", math.floor(x*10+5)/10,
317 "Y:", math.floor(y*10+5)/10,
318 "Z:",math.floor(z*10+5)/10
319 )
320 LaserDisplay.output.printError("Is already dead")
321 end
322 return fired
323end
324
325--------------------------------------------------
326-- Main
327term.setCursorPos(1,2)
328term.clear()
329LaserDisplay.init()
330LaserDisplay.status("ready")
331while true do
332 --Update display
333 update()
334 --Check lighting
335 if rs.getOutput("bottom") then
336 LaserDisplay.header("Laser off")
337 else
338 LaserDisplay.header("Laser on")
339 -- List entities
340 local mobs = modules.sense()
341
342 -- Fire first target
343 local fired = false
344 for i = 1, #mobs do
345 local mob = mobs[i]
346 if mob and entityWhitelist[mob.name] and mob.id then
347 fired = fireInRange(mob)
348 if fired then break end
349 end
350 end
351 end
352 control(1)
353end