forked from Emojigit/technic_grass_clean
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
267 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Changelog | ||
All time are in UTC+8 (HKT) | ||
## 2020/08/03 | ||
17:48 Edited it to make it cut grass, not tree. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
-- Configuration | ||
|
||
local grassc_max_charge = 30000 -- Maximum charge of the saw | ||
-- Gives 2500 nodes on a single charge (about 50 complete normal trees) | ||
local chainsaw_charge_per_node = 12 | ||
-- Cut down tree leaves. Leaf decay may cause slowness on large trees | ||
-- if this is disabled. | ||
local chainsaw_leaves = true | ||
|
||
-- First value is node name; second is whether the node is considered even if chainsaw_leaves is false. | ||
local nodes = { | ||
-- The default grasses | ||
{"default:grass_1",true}, | ||
{"default:grass_2",true}, | ||
{"default:grass_3",true}, | ||
{"default:grass_4",true}, | ||
{"default:grass_5",true}, | ||
{"default:dry_grass_1",true}, | ||
{"default:dry_grass_2",true}, | ||
{"default:dry_grass_3",true}, | ||
{"default:dry_grass_4",true}, | ||
{"default:dry_grass_5",true}, | ||
{"default:junglegrass",true}, | ||
{"default:dry_shrub",true}, | ||
-- ethereal | ||
{"ethereal:fern",true}, | ||
{"ethereal:onion_1",true}, | ||
{"ethereal:onion_2",true}, | ||
{"ethereal:onion_3",true}, | ||
{"ethereal:onion_4",true}, | ||
{"ethereal:onion_5",true}, | ||
{"ethereal:crystalgrass",true}, | ||
{"ethereal:crystal_spike",true}, | ||
{"ethereal:snowygrass",true}, | ||
} | ||
|
||
local timber_nodenames = {} | ||
for _, node in pairs(nodes) do | ||
if chainsaw_leaves or node[2] then | ||
timber_nodenames[node[1]] = true | ||
end | ||
end | ||
|
||
local S = technic.getter | ||
|
||
technic.register_power_tool("technic_grass_clean:grass_cleaner", grassc_max_charge) | ||
|
||
-- This function checks if the specified node should be sawed | ||
local function check_if_node_sawed(pos) | ||
local node_name = minetest.get_node(pos).name | ||
if timber_nodenames[node_name] then | ||
return true | ||
end | ||
|
||
return false | ||
end | ||
|
||
-- Table for saving what was sawed down | ||
local produced = {} | ||
|
||
-- Save the items sawed down so that we can drop them in a nice single stack | ||
local function handle_drops(drops) | ||
for _, item in ipairs(drops) do | ||
local stack = ItemStack(item) | ||
local name = stack:get_name() | ||
local p = produced[name] | ||
if not p then | ||
produced[name] = stack | ||
else | ||
p:set_count(p:get_count() + stack:get_count()) | ||
end | ||
end | ||
end | ||
|
||
--- Iterator over positions to try to saw around a sawed node. | ||
-- This returns positions in a 3x1x3 area around the position, plus the | ||
-- position above it. This does not return the bottom position to prevent | ||
-- the chainsaw from cutting down nodes below the cutting position. | ||
-- @param pos Sawing position. | ||
local function iterSawTries(pos) | ||
-- Copy position to prevent mangling it | ||
local pos = vector.new(pos) | ||
local i = 0 | ||
|
||
return function() | ||
i = i + 1 | ||
-- Given a (top view) area like so (where 5 is the starting position): | ||
-- X --> | ||
-- Z 123 | ||
-- | 456 | ||
-- V 789 | ||
-- This will return positions 1, 4, 7, 2, 8 (skip 5), 3, 6, 9, | ||
-- and the position above 5. | ||
if i == 1 then | ||
-- Move to starting position | ||
pos.x = pos.x - 1 | ||
pos.z = pos.z - 1 | ||
elseif i == 4 or i == 7 then | ||
-- Move to next X and back to start of Z when we reach | ||
-- the end of a Z line. | ||
pos.x = pos.x + 1 | ||
pos.z = pos.z - 2 | ||
elseif i == 5 then | ||
-- Skip the middle position (we've already run on it) | ||
-- and double-increment the counter. | ||
pos.z = pos.z + 2 | ||
i = i + 1 | ||
elseif i <= 9 then | ||
-- Go to next Z. | ||
pos.z = pos.z + 1 | ||
elseif i == 10 then | ||
-- Move back to center and up. | ||
-- The Y+ position must be last so that we don't dig | ||
-- straight upward and not come down (since the Y- | ||
-- position isn't checked). | ||
pos.x = pos.x - 1 | ||
pos.z = pos.z - 1 | ||
pos.y = pos.y + 1 | ||
else | ||
return nil | ||
end | ||
return pos | ||
end | ||
end | ||
|
||
-- This function does all the hard work. Recursively we dig the node at hand | ||
-- if it is in the table and then search the surroundings for more stuff to dig. | ||
local function recursive_dig(pos, remaining_charge) | ||
if remaining_charge < chainsaw_charge_per_node then | ||
return remaining_charge | ||
end | ||
local node = minetest.get_node(pos) | ||
|
||
if not check_if_node_sawed(pos) then | ||
return remaining_charge | ||
end | ||
|
||
-- Wood found - cut it | ||
handle_drops(minetest.get_node_drops(node.name, "")) | ||
minetest.remove_node(pos) | ||
remaining_charge = remaining_charge - chainsaw_charge_per_node | ||
|
||
-- Check surroundings and run recursively if any charge left | ||
for npos in iterSawTries(pos) do | ||
if remaining_charge < chainsaw_charge_per_node then | ||
break | ||
end | ||
if check_if_node_sawed(npos) then | ||
remaining_charge = recursive_dig(npos, remaining_charge) | ||
else | ||
minetest.check_for_falling(npos) | ||
end | ||
end | ||
return remaining_charge | ||
end | ||
|
||
-- Function to randomize positions for new node drops | ||
local function get_drop_pos(pos) | ||
local drop_pos = {} | ||
|
||
for i = 0, 8 do | ||
-- Randomize position for a new drop | ||
drop_pos.x = pos.x + math.random(-3, 3) | ||
drop_pos.y = pos.y - 1 | ||
drop_pos.z = pos.z + math.random(-3, 3) | ||
|
||
-- Move the randomized position upwards until | ||
-- the node is air or unloaded. | ||
for y = drop_pos.y, drop_pos.y + 5 do | ||
drop_pos.y = y | ||
local node = minetest.get_node_or_nil(drop_pos) | ||
|
||
if not node then | ||
-- If the node is not loaded yet simply drop | ||
-- the item at the original digging position. | ||
return pos | ||
elseif node.name == "air" then | ||
-- Add variation to the entity drop position, | ||
-- but don't let drops get too close to the edge | ||
drop_pos.x = drop_pos.x + (math.random() * 0.8) - 0.5 | ||
drop_pos.z = drop_pos.z + (math.random() * 0.8) - 0.5 | ||
return drop_pos | ||
end | ||
end | ||
end | ||
|
||
-- Return the original position if this takes too long | ||
return pos | ||
end | ||
|
||
-- Chainsaw entry point | ||
local function chainsaw_dig(pos, current_charge) | ||
-- Start sawing things down | ||
local remaining_charge = recursive_dig(pos, current_charge) | ||
minetest.sound_play("chainsaw", {pos = pos, gain = 1.0, | ||
max_hear_distance = 10}) | ||
|
||
-- Now drop items for the player | ||
for name, stack in pairs(produced) do | ||
-- Drop stacks of stack max or less | ||
local count, max = stack:get_count(), stack:get_stack_max() | ||
stack:set_count(max) | ||
while count > max do | ||
minetest.add_item(get_drop_pos(pos), stack) | ||
count = count - max | ||
end | ||
stack:set_count(count) | ||
minetest.add_item(get_drop_pos(pos), stack) | ||
end | ||
|
||
-- Clean up | ||
produced = {} | ||
|
||
return remaining_charge | ||
end | ||
|
||
|
||
minetest.register_tool("technic_grass_clean:grass_cleaner", { | ||
description = "Grass Cleaner", | ||
inventory_image = "technic_chainsaw.png", | ||
stack_max = 1, | ||
wear_represents = "technic_RE_charge", | ||
on_refill = technic.refill_RE_charge, | ||
on_use = function(itemstack, user, pointed_thing) | ||
if pointed_thing.type ~= "node" then | ||
return itemstack | ||
end | ||
|
||
local meta = minetest.deserialize(itemstack:get_metadata()) | ||
if not meta or not meta.charge or | ||
meta.charge < chainsaw_charge_per_node then | ||
return | ||
end | ||
|
||
local name = user:get_player_name() | ||
if minetest.is_protected(pointed_thing.under, name) then | ||
minetest.record_protection_violation(pointed_thing.under, name) | ||
return | ||
end | ||
|
||
-- Send current charge to digging function so that the | ||
-- chainsaw will stop after digging a number of nodes | ||
meta.charge = chainsaw_dig(pointed_thing.under, meta.charge) | ||
if not technic.creative_mode then | ||
technic.set_RE_wear(itemstack, meta.charge, chainsaw_max_charge) | ||
itemstack:set_metadata(minetest.serialize(meta)) | ||
end | ||
return itemstack | ||
end, | ||
}) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# License | ||
Copyright (C) 2020 Cato Yiu (Emoji) | ||
|
||
Technic chests code is licensed under the GNU LGPLv2+. | ||
|
||
Note: I am call Emoji(Service) on most all place, for example, my name is Emojigit on git hosting website like github. | ||
Except but not only: | ||
- Emojiwiki on wikimedia | ||
- Emojinotabug on NotABug |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
name = technic_grass_clean | ||
depends = technic, default | ||
description = A tool like chainsaw, but cut grass, not tree. |