Update 20161106

This commit is contained in:
maikerumine 2016-11-05 21:14:05 -04:00
parent 6fa78d5be1
commit 4bbccec3c1
219 changed files with 1902 additions and 9373 deletions

View File

@ -1,37 +0,0 @@
-- ANTI CHEAT by rnd
-- Copyright 2016 rnd
-- includes spectator mod by jp, modified/bugfixed by rnd
-------------------------------------------------------------------------
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------
features:
0. what it does:
- succesffuly detect noclip/fly. Its just a matter of time when someone noclipping/flying is detected.
- players cant know when they are being watch since intervals are randomized
- lag resistant (see CHECK_AGAIN in settings)
1. moderators can:
-see full reports with coordinates of location as cheats occur
-use /cstats to see latest detected cheater
-use /cdebug to see even suspected cheats to be verified later
-use /watch NAME to spectate suspect/detected cheater, /unwatch to return to normal
managing moderators:
-edit names inside anticheatsettings.moderators in settings.lua
-Any player with kick privileges is moderator and is additionaly ignored by cheat checks. Use this for admin only - cheaters can then see who moderators are.
2. this mod works well with basic_vote mod. After cheater has been positively detected anyone can use /vote to kick, remove interact or kill cheater. Vote in this case is cast anonymously, under the name #anticheat.

Binary file not shown.

View File

@ -1,2 +0,0 @@
default
boneworld?

View File

@ -1,556 +0,0 @@
-- ANTI CHEAT by rnd
-- Copyright 2016 rnd
-- includes modified/bugfixed spectator mod by jp
-------------------------------------------------------------------------
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------
local cheat = {};
local version = "10/27/2016";
anticheatsettings = {};
dofile(minetest.get_modpath("anticheat").."/settings.lua")
local CHEAT_TIMESTEP = anticheatsettings.CHEAT_TIMESTEP;
local CHECK_AGAIN = anticheatsettings.CHECK_AGAIN;
cheat.moderators = anticheatsettings.moderators;
anticheatdb = {}; -- data about detected cheaters
cheat.suspect = "";
cheat.players = {}; -- temporary cheat detection db
cheat.message = "";
cheat.debuglist = {}; -- [name]=true -- who gets to see debug msgs
cheat.scan_timer = 0; -- global scan of players
cheat.stat_timer = 0; -- used to collect stats
cheat.nodelist = {};
cheat.watcher = {}; -- list of watchers
cheat.timestep = CHEAT_TIMESTEP;
-- list of forbidden nodes
cheat.nodelist = {["default:stone"] = false, ["default:cobble"]= false, ["default:dirt"] = false, ["default:sand"]=false,["default:tree"]= false};
local punish_cheat = function(name)
local player = minetest.get_player_by_name(name);
local ip = tostring(minetest.get_player_ip(name));
if not player then return end
local text=""; local logtext = "";
if cheat.players[name].cheattype == 1 then
text = "#anticheat: ".. name .. " was caught walking inside wall";
logtext = os.date("%H:%M.%S").." #anticheat: ".. name .. " was caught walking inside wall at " .. minetest.pos_to_string(cheat.players[name].cheatpos);
player:set_hp(0);
elseif cheat.players[name].cheattype == 2 then
logtext= os.date("%H:%M.%S").." #anticheat: ".. name .. " was caught flying at " .. minetest.pos_to_string(cheat.players[name].cheatpos);
if cheat.players[name].cheatpos.y>5 then -- only above height 5 it directly damages flyer
text = "#anticheat: ".. name .. " was caught flying";
--player:set_hp(0);
end
end
if text~="" then
minetest.chat_send_all(text);
end
if logtext~="" then
minetest.log("action", logtext);
cheat.message = logtext;
anticheatdb[ip] = {name = name, msg = logtext};
cheat.players[name].count=0; -- reset counter
cheat.players[name].cheattype = 0;
for name,_ in pairs(cheat.moderators) do -- display full message to moderators
minetest.chat_send_player(name,logtext);
end
end
end
-- check position more closely
-- uncomment when obfuscating:
--dofile(minetest.get_modpath("anticheat").."/anticheat_source.lua")
local ie = minetest.request_insecure_environment() or _G;
local anticheat_routines = ie.loadfile(minetest.get_modpath("anticheat").."/anticheat_routines.bin")
check_noclip, check_fly, check_player = anticheat_routines(minetest,cheat,CHECK_AGAIN,punish_cheat);
minetest.register_globalstep(function(dtime)
cheat.scan_timer = cheat.scan_timer + dtime
-- GENERAL SCAN OF ALL PLAYERS
if cheat.scan_timer>cheat.timestep then
cheat.stat_timer = cheat.stat_timer + cheat.timestep;
-- dig xp stats every 2 minutes
if boneworld and cheat.stat_timer>120 then
cheat.stat_timer = 0;
local players = minetest.get_connected_players();
for _,player in pairs(players) do
local pname = player:get_player_name();
if cheat.players[pname].stats.state == 1 then -- only if dig xp loaded to prevent anomalous stats
if boneworld.digxp[pname] then
local deltadig = cheat.players[pname].stats.digxp;
cheat.players[pname].stats.digxp = boneworld.digxp[pname];
deltadig = boneworld.digxp[pname]-deltadig;
cheat.players[pname].stats.deltadig = deltadig;
if deltadig>cheat.players[pname].stats.maxdeltadig then
cheat.players[pname].stats.maxdeltadig = deltadig;
end
if deltadig>2 then -- unnaturally high deltadig
local ip = tostring(minetest.get_player_ip(pname));
local logtext = os.date("%H:%M.%S") .. " #anticheat: " .. pname .. " (ip " .. ip .. ") is mining resources too fast, deltadig " .. deltadig;
anticheatdb[ip] = {name = pname, msg = logtext};
minetest.log("action", logtext);
end
end
end
end
end
cheat.timestep = CHEAT_TIMESTEP + (2*math.random()-1)*2; -- randomize step so its unpredictable
cheat.scan_timer=0;
--local t = minetest.get_gametime();
local players = minetest.get_connected_players();
for _,player in pairs(players) do
check_player(player);
end
for name,_ in pairs(cheat.debuglist) do -- show suspects in debug
for _,player in pairs(players) do
local pname = player:get_player_name();
if cheat.players[pname].count>0 then
minetest.chat_send_player(name, "name " .. pname .. ", cheat pos " .. minetest.pos_to_string(cheat.players[pname].cheatpos) .. " last clear pos " .. minetest.pos_to_string(cheat.players[pname].clearpos) .. " cheat type " .. cheat.players[pname].cheattype .. " cheatcount " .. cheat.players[pname].count );
end
end
end
end
end)
-- long range dig check
local check_can_dig = function(pos, digger)
local p = digger:getpos();
if p.y<0 then p.y=p.y+2 else p.y=p.y+1 end -- head position
local dist = math.max(math.abs(p.x-pos.x),math.abs(p.y-pos.y),math.abs(p.z-pos.z));
if dist>6 then -- here 5
dist = math.floor(dist*100)/100;
local pname = digger:get_player_name();
local logtext = os.date("%H:%M.%S") .. "#anticheat: long range dig " .. pname ..", distance " .. dist .. ", pos " .. minetest.pos_to_string(pos);
for name,_ in pairs(cheat.debuglist) do -- show to all watchers
minetest.chat_send_player(name,logtext)
end
local ip = tostring(minetest.get_player_ip(pname));
anticheatdb[ip] = {name = pname, msg = logtext};
return false
end
return true
end
local set_check_can_dig = function(name)
local tabl = minetest.registered_nodes[name];
if not tabl then return end
tabl.can_dig = check_can_dig;
minetest.override_item(name, {can_dig = check_can_dig})
--minetest.register_node(":"..name, tabl);
end
minetest.after(0,
function()
set_check_can_dig("default:stone");
set_check_can_dig("default:stone_with_iron");
set_check_can_dig("default:stone_with_copper");
set_check_can_dig("default:stone_with_coal");
set_check_can_dig("default:stone_with_gold");
set_check_can_dig("default:stone_with_mese");
set_check_can_dig("default:stone_with_diamond");
end
)
-- DISABLED: lot of false positives
-- collects misc stats on players
-- minetest.register_on_cheat(
-- function(player, c)
-- local name = player:get_player_name(); if name == nil then return end
-- local stats = cheat.players[name].stats;
-- if not stats[c.type] then stats[c.type] = 0 end
-- stats[c.type]=stats[c.type]+1;
-- end
-- )
local watchers = {}; -- for each player a list of watchers
minetest.register_on_joinplayer(function(player) -- init stuff on player join
local name = player:get_player_name(); if name == nil then return end
local pos = player:getpos();
if cheat.players[name] == nil then
cheat.players[name]={count=0,cheatpos = pos, clearpos = pos, lastpos = pos, cheattype = 0}; -- type 0: none, 1 noclip, 2 fly
end
if cheat.players[name] and cheat.players[name].stats == nil then
cheat.players[name].stats = {maxdeltadig=0,deltadig = 0,digxp = 0, state = 0}; -- various statistics about player: max dig xp increase in 2 minutes, current dig xp increase
minetest.after(5, -- load digxp after boneworld loads it
function()
if boneworld and boneworld.xp then
cheat.players[name].stats.digxp = boneworld.digxp[name] or 0;
cheat.players[name].stats.state = 1;
end
end
)
end
--state 0 = stats not loaded yet
watchers[name] = {}; -- for spectator mod
local ip = tostring(minetest.get_player_ip(name));
local msg = "";
-- check anticheat db
--check ip
if anticheatdb[ip] then
msg = "#anticheat: welcome back detected cheater, ip = " .. ip .. ", name " .. anticheatdb[ip].name .. ", new name = " .. name;
end;
--check names
for ip,v in pairs(anticheatdb) do
if v.name == name then
msg = "#anticheat: welcome back detected cheater, ip = " .. ip .. ", name = newname = " .. v.name;
break;
end
end
if msg~="" then
for name,_ in pairs(cheat.moderators) do
minetest.chat_send_player(name,msg);
end
end
end)
minetest.register_chatcommand("cchk", { -- see cheat report
privs = {
interact = true
},
func = function(name, param)
local privs = minetest.get_player_privs(name).privs;
if not cheat.moderators[name] and not privs then return end
local player = minetest.get_player_by_name(param);
if not player then return end
check_player(player);
local players = minetest.get_connected_players();
for name,_ in pairs(cheat.debuglist) do -- show suspects in debug
for _,player in pairs(players) do
local pname = player:get_player_name();
if cheat.players[pname].count>0 then
minetest.chat_send_player(name, "name " .. pname .. ", cheat pos " .. minetest.pos_to_string(cheat.players[pname].cheatpos) .. " last clear pos " .. minetest.pos_to_string(cheat.players[pname].clearpos) .. " cheat type " .. cheat.players[pname].cheattype .. " cheatcount " .. cheat.players[pname].count );
end
end
end
end
})
minetest.register_chatcommand("crep", { -- see cheat report
privs = {
interact = true
},
func = function(name, param)
local privs = minetest.get_player_privs(name).privs;
if not cheat.moderators[name] and not privs then return end
if param == "" then
minetest.chat_send_player(name,"use: crep type, types: 0(default) cheat report, 1 connected player stats (".. version ..")");
end
param = tonumber(param) or 0;
if param == 0 then -- show cheat report
local text = "";
for ip,v in pairs(anticheatdb) do
if v and v.name and v.msg then
text = text .. "ip " .. ip .. " ".. v.msg .. "\n";
end
end
if text ~= "" then
local form = "size [6,7] textarea[0,0;6.5,8.5;creport;CHEAT REPORT;".. text.."]"
minetest.show_formspec(name, "anticheatreport", form)
end
elseif param == 1 then -- show cheat stats
local text = "";
local players = minetest.get_connected_players();
for _,player in pairs(players) do
local pname = player:get_player_name();
local ip = tostring(minetest.get_player_ip(pname));
text = text .. "\nname " .. pname .. ", digxp " .. math.floor(1000*cheat.players[pname].stats.digxp)/1000 ..
", deltadigxp(2min) " .. math.floor(1000*cheat.players[pname].stats.deltadig)/1000 .. ", maxdeltadigxp " .. math.floor(1000*cheat.players[pname].stats.maxdeltadig)/1000; -- .. " ".. string.gsub(dump(cheat.players[pname].stats), "\n", " ");
if anticheatdb[ip] then text = text .. " (DETECTED) ip ".. ip .. ", name " .. anticheatdb[ip].name end
end
if text ~= "" then
local form = "size [10,8] textarea[0,0;10.5,9.;creport;CHEAT STATISTICS;".. text.."]"
minetest.show_formspec(name, "anticheatreport", form)
end
end
-- suspects info
local players = minetest.get_connected_players();
for _,player in pairs(players) do
local pname = player:get_player_name();
if cheat.players[pname].count>0 then
minetest.chat_send_player(name, "name " .. pname .. ", cheat pos " .. minetest.pos_to_string(cheat.players[pname].cheatpos) .. " last clear pos " .. minetest.pos_to_string(cheat.players[pname].lastpos) .. " cheat type " .. cheat.players[pname].cheattype .. " cheatcount " .. cheat.players[pname].count );
end
end
end
})
minetest.register_chatcommand("cdebug", { -- toggle cdebug= display of stats on/off for this player
privs = {
interact = true
},
func = function(name, param)
local privs = minetest.get_player_privs(name).privs;
if not cheat.moderators[name] and not privs then return end
if cheat.debuglist[name] == nil then cheat.debuglist[name] = true else cheat.debuglist[name] = nil end;
minetest.chat_send_player(name,"#anticheat: " .. version);
if cheat.debuglist[name]==true then
minetest.chat_send_player(name,"#anticheat: display of debug messages is ON");
else
minetest.chat_send_player(name,"#anticheat: display of debug messages is OFF");
end
end
})
------------------------------------------------------
-- [Mod] Spectator Mode [git] [spectator_mode]
-- https://github.com/minetest-mods/spectator_mode
-- by jp » Tue Dec 08, 2015 15:34
-- modified/bugfixes by rnd
------------------------------------------------------
local original_pos = {}
local function unwatching(name)
local watcher = minetest.get_player_by_name(name)
local privs = minetest.get_player_privs(name)
if watcher and default.player_attached[name] == true then
watcher:set_detach()
local pos = original_pos[watcher]
if pos then
-- setpos seems to be very unreliable
-- this workaround helps though
minetest.after(0.1, function()
watcher:setpos(pos)
end)
original_pos[watcher] = nil
end
cheat.watcher[name]=nil;
minetest.after(5,
function()
default.player_attached[name] = false
watcher:set_eye_offset({x=0, y=0, z=0}, {x=0, y=0, z=0})
watcher:set_nametag_attributes({color = {a=255, r=255, g=255, b=255}})
watcher:hud_set_flags({
healthbar = true,
minimap = true,
breathbar = true,
hotbar = true,
wielditem = true,
crosshair = true
})
watcher:set_properties({
visual_size = {x=1, y=1},
makes_footstep_sound = true,
collisionbox = {-0.3, -1, -0.3, 0.3, 1, 0.3}
})
end
)
-- if not privs.interact and cheat.moderators[name] == true then
-- privs.interact = true
-- minetest.set_player_privs(name, privs)
-- end
end
end
minetest.register_chatcommand("watch", {
params = "<to_name>",
description = "",
privs = {interact=true},
func = function(name, param)
local privs = minetest.get_player_privs(name)
if not cheat.moderators[name] and not privs.kick then return end
local watcher = minetest.get_player_by_name(name)
if param == "" then -- no name given - select a suspect automatically
local players = minetest.get_connected_players();
for _,player in pairs(players) do
local pname = player:get_player_name();
if cheat.players[pname].count>0 then
param = pname;
cheat.suspect = param;
break;
end
end
if param == "" and cheat.suspect~="" then param = cheat.suspect end -- if none found watch last suspect
end
local target = minetest.get_player_by_name(param);
if not target then return end
if not cheat.players[param] then return end
local canwatch = false;
for ip,v in pairs(anticheatdb) do
if v.name == param then
canwatch = true;
break;
end
end
local ip = tostring(minetest.get_player_ip(param));
if anticheatdb[ip] then canwatch = true end -- can watch since this ip was detected before
if canwatch or cheat.players[param].count>0 or param == cheat.suspect or privs.kick then
else
minetest.chat_send_player(name, "ordinary watchers can only watch cheat suspects of detected cheaters");
return
end
if target and watcher ~= target then
if default.player_attached[name] == true then
unwatching(param)
else
original_pos[watcher] = watcher:getpos()
end
-- show inventory
local tinv = target:get_inventory():get_list("main");
for i,v in pairs(tinv) do tinv[i] = v:to_string(); end
tinv = dump(tinv);
local form = "size [6,7] textarea[0,0;6.5,8.5;creport;INVENTORY LIST;".. tinv.."]"
minetest.show_formspec(name, "watch_inventory", form)
default.player_attached[name] = true
watcher:set_attach(target, "", {x=0, y=-5, z=-20}, {x=0, y=0, z=0})
watcher:set_eye_offset({x=0, y=-5, z=-20}, {x=0, y=0, z=0})
watcher:set_nametag_attributes({color = {a=0}})
watcher:hud_set_flags({
healthbar = false,
minimap = false,
breathbar = false,
hotbar = false,
wielditem = false,
crosshair = false
})
watcher:set_properties({
visual_size = {x=0, y=0},
makes_footstep_sound = false,
collisionbox = {0}
})
-- privs.interact = nil
-- minetest.set_player_privs(name, privs)
cheat.watcher[name]=true;
watchers[param][name] = true; -- register name as watcher of param
return true, "Watching '"..param.."' at "..minetest.pos_to_string(vector.round(target:getpos()))
end
return false, "Invalid parameter ('"..param.."')."
end
})
minetest.register_chatcommand("unwatch", {
description = "",
privs = {interact=true},
func = function(name, param)
unwatching(name)
-- unregister name as watcher
for pname,val in pairs (watchers) do
if val[name] then watchers[pname][name] = nil; end
end
end
})
minetest.register_on_leaveplayer(function(player)
local name = player:get_player_name()
for pname,_ in pairs (watchers[name]) do
unwatching(pname); -- all watchers do /unwatch
end
watchers[name] = nil;
end)

View File

@ -1,36 +0,0 @@
-- ANTI CHEAT by rnd
-- Copyright 2016 rnd
-- includes modified/bugfixed spectator mod by jp
-------------------------------------------------------------------------
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------
-- SETTINGS --------------------------------------------------------------
anticheatsettings.CHEAT_TIMESTEP = 15; -- check all players
anticheatsettings.CHECK_AGAIN = 15; -- after player found in bad position check again after this to make sure its not lag, this should be larger than expected lag in seconds
-- moderators list, those players can use cheat debug and will see full cheat message
anticheatsettings.moderators = {
["rnd"]=true,
["sasha2"]=true,
["maikerumine"]=true,
["sorcerykid"]=true,
["Zorg"]=true,
["AspireMint"]=true,
["843jdc"]=true,
["Fixer"] = true,
}
-- END OF SETTINGS --------------------------------------------------------

View File

@ -1 +0,0 @@
bones

View File

@ -1,377 +0,0 @@
-- boneworld by rnd, 2016
-- for more interesting bone gameplay:
-- you no longer get extra bones if you pick bones from player with same ip address (no suicide bone farming)
-- each player has experience points (xp)
-- when you die you loose 20% of your xp, half of that is stored in bones
-- (when you kill other player you get 10% of his xp)
-- if you pick up bones you get xp stored in bones
-- if you pick up other player bones you get 20% of average of your and bone owner xp award in extra bones (for example if you have 10 xp and you pick noob bone will get 2 bones instead of normally 1)
local version = "10/22/16"
local worldpath = minetest.get_worldpath();
--os.execute( "mkdir "..worldpath.. "\\boneworld")
minetest.mkdir(worldpath .. "\\boneworld") -- directory used to save xp data
boneworld = {};
boneworld.xp = {}; -- [name] = bonexp, bone collect xp
boneworld.digxp = {}; -- [name] = xp, mining xp
boneworld.protect = {}; -- [name] = {timer, position}: time of last dig in unprotected area, position
-- those players get special dig xp when they join
boneworld.vipdig = {["abba"]=1000000}
--boneworld.killxp = {}; -- xp obtained through kills
boneworld.wastedxp = 0; -- xp thats stored in bones and not yet reclaimed
local share_bones_time = tonumber(minetest.setting_get("share_bones_time")) or 1200
--local share_bones_time = tonumber(minetest.setting_get("share_bones_time")) or 20;
local share_bones_time_early = tonumber(minetest.setting_get("share_bones_time_early")) or share_bones_time / 4
local function is_owner(pos, name)
local owner = minetest.get_meta(pos):get_string("owner")
if owner == "" or owner == name or minetest.check_player_privs(name, "protection_bypass") then
return true
end
return false
end
local on_timer = function(pos, elapsed)
local meta = minetest.get_meta(pos)
local time = meta:get_int("time")+elapsed;
if time >= share_bones_time then
meta:set_string("infotext", meta:get_string("owner").."'s old bones (died ".. meta:get_string("date") .."), bone xp " ..math.floor(meta:get_float("xp")*100)/100);
meta:set_string("owner", "")
else
if meta:get_int("active") == 0 then -- store data in bones, 1x
meta:set_int("active",1);
local owner = meta:get_string("owner");
meta:set_string("date",os.date("%x"));
meta:set_string("owner_orig",owner);
meta:set_string("ip", tostring(minetest.get_player_ip(owner)));
if not minetest.get_player_by_name(owner) then -- mob bones
boneworld.xp[owner] = 0.1 -- 0.1th of noob player xp in mobs bone
time=0.9*share_bones_time; -- 10x shorter old bone time
else
boneworld.xp[owner] = boneworld.xp[owner] or 1;
time = 0;
end
if boneworld.xp[owner]<1 then
meta:set_float("xp", 0.01) -- mobs or bones with 0 xp
else
meta:set_float("xp", 0.1); -- player bones give 0.1 xp, same as 10 mob bones
end
boneworld.wastedxp = boneworld.wastedxp + meta:get_float("xp");
meta:set_string("infotext"," Here lies " .. owner .. ", bone xp " .. math.floor(meta:get_float("xp")*100)/100);
end
meta:set_int("time", time)
return true
end
end
local on_punch = function(pos, node, player)
if(not is_owner(pos, player:get_player_name())) then
return
end
if(minetest.get_meta(pos):get_string("infotext") == "") then
return
end
local inv = minetest.get_meta(pos):get_inventory()
local player_inv = player:get_inventory()
local has_space = true
for i=1,inv:get_size("main") do
local stk = inv:get_stack("main", i)
if player_inv:room_for_item("main", stk) then
inv:set_stack("main", i, nil)
player_inv:add_item("main", stk)
else
has_space = false
break
end
end
-- remove bones if player emptied them
if has_space then
local meta = minetest.get_meta(pos);
local active = meta:get_int("active") == 1;
local puncher = player:get_player_name();
-- award extra bones/xp if you collect bones from different ip player
--debug
if active and meta:get_string("ip")~= tostring(minetest.get_player_ip(puncher)) then
local xp = meta:get_float("xp");if xp==0 then xp = 0.01 end
-- average of owners xp (at time of death) and puncher xp will be awarded as extra bones
-- with every 10 more xp one bone
local count;
if boneworld.xp[puncher]>100 then -- dont give more bones when bone xp exceeds 100
count = 1 + 0.1*100;
else
count = 1+0.1*boneworld.xp[puncher];
end
count = math.floor(count);
minetest.chat_send_player(puncher, "you find " .. count .. " bones in the corpse.");
if player_inv:room_for_item("main", ItemStack("bones:bones "..count)) then
player_inv:add_item("main", ItemStack("bones:bones "..count))
else
minetest.add_item(pos,ItemStack("bones:bones "..count))
end
-- add xp from bones to player who retrieved bones;
boneworld.xp[puncher] = boneworld.xp[puncher] + meta:get_float("xp");
boneworld.wastedxp = boneworld.wastedxp - meta:get_float("xp");
end
minetest.remove_node(pos)
end
end
-- load xp
minetest.register_on_joinplayer(
function(player)
local name = player:get_player_name();
if not boneworld.xp[name] then -- load xp
local filename = worldpath .. "\\boneworld\\" .. name..".xp";
local f = io.open(filename, "r");
if not f then -- file does not yet exist
boneworld.xp[name] = 1;
boneworld.digxp[name] = 0;
else
local str = f:read("*a") or 1;
local words = {};
for w in str:gmatch("%S+") do
words[#words+1]=w
end
boneworld.xp[name] = tonumber(words[1] or 1);
boneworld.digxp[name] = tonumber(words[2] or 0);
f:close();
end
end
if boneworld.vipdig[name] then
if boneworld.digxp[name]<boneworld.vipdig[name] then
boneworld.digxp[name] = boneworld.vipdig[name];
end
end
end
)
-- save xp
minetest.register_on_leaveplayer(
function(player)
local name = player:get_player_name();
local bonexp = boneworld.xp[name] or 1;
local digxp = boneworld.digxp[name] or 0;
--debug
if bonexp > 2 or digxp>1 then -- save xp for serious players only
local filename = worldpath .. "\\boneworld\\" .. name..".xp";
local f = io.open(filename, "w");
if not f then return end
f:write(bonexp .. " " .. digxp);
f:close();
else
-- dont save, player didnt do anything
end
end
)
minetest.register_on_dieplayer( -- -1 bone xp each time you die; otherwise no motivation to be careful
function(player)
local name = player:get_player_name();
local xp = boneworld.xp[name] or 1;
if xp>2 then
xp=xp-1
else
xp = 1;
end
boneworld.xp[name]=xp;
end
)
local tweak_bones = function()
local name = "bones:bones";
local table = minetest.registered_nodes[name];
--table.on_construct = on_construct;
table.on_punch = on_punch;
table.on_timer = on_timer;
minetest.register_node(":"..name, table);
end
minetest.after(0,tweak_bones);
minetest.register_chatcommand("xp", {
description = "xp name - show bone collecting experience of target player " .. version,
privs = {
interact = true
},
func = function(name, param)
local msg;
if param == "" then
local xp = math.floor((boneworld.xp[name])*100)/100;
local digxp = math.floor((boneworld.digxp[name])*100)/100;
--local killxp = math.floor((boneworld.killxp[name])*100)/100;
msg = "xp name - show experience of target player"
.."\n# "..name .. " has " .. xp .. " bone collecting experience, ".. digxp .. " digging experience"
.. " (can dig to ".. math.floor(200+50*math.sqrt(digxp)) .. ")"
.. "\nTotal xp stored in bones in world is " .. math.floor(boneworld.wastedxp*100)/100;
else
local xp = math.floor((boneworld.xp[param] or 1)*100)/100;
local digxp = math.floor((boneworld.digxp[param] or 0)*100)/100;
--local killxp = math.floor((boneworld.killxp[name])*100)/100;
msg = "xp name - show experience of target player (10.04.16)"
.."\n# "..param .. " has " .. xp .. " bone collecting experience, ".. digxp .. " digging experience";
end
minetest.chat_send_player(name, msg);
end
})
-- limit digging to above -(200+xp*5)
local old_is_protected = minetest.is_protected
function minetest.is_protected(pos, name)
local is_protected_new = old_is_protected(pos, name);
if pos.y>-200 or name == "" then
else
--to do : digxp here!!
local digxp = boneworld.digxp[name] or 0;
local maxdepth = 200+50*math.sqrt(digxp);
if pos.y<-maxdepth then
minetest.chat_send_player(name, "You can only dig above -"..math.floor(maxdepth) .. ". Get more dig experience to dig deeper");
local player = minetest.get_player_by_name(name); if not player then return true end
if pos.y<-maxdepth-5 then player:setpos({x=0,y=1,z=0}) end
is_protected_new = true
end
end
if not is_protected_new then -- remember time, pos of last dig in unprotected area
local t1 = minetest.get_gametime();
local t0;
local protect_data = boneworld.protect[name];
if not protect_data then
boneworld.protect[name] = {t=t1, pos=pos};
t0 = t1;
else
t0 = boneworld.protect[name].t;
end
if t1-t0>10 then -- "time" to remember new time, pos
boneworld.protect[name].t = t1;
boneworld.protect[name].pos = pos;
end
else -- tried to dig in protected area, teleport to last good position
local player = minetest.get_player_by_name(name); if not player then return true end
local protect_data = boneworld.protect[name];
local tpos;
if not protect_data then -- safety check
boneworld.protect[name] = {t=minetest.get_gametime(), pos=pos};
tpos = pos
else
tpos = boneworld.protect[name].pos;
end
player:setpos({x=tpos.x,y=tpos.y+1,z=tpos.z});
end
return is_protected_new;
end
-- mining xp
-- how much mining xp digging minerals yields
boneworld.mineralxp = {
["default:stone"] = 0.01,
["default:stone_with_coal"] = 0.03,
["default:stone_with_iron"] = 0.1,
["default:stone_with_copper"] = 0.1,
["default:stone_with_gold"] = 0.2,
["default:stone_with_mese"] = 0.5,
["default:stone_with_diamond"] = 1,
}
local after_dig_node = function(pos, oldnode, oldmetadata, digger)
local nodename = oldnode.name;
local name = digger:get_player_name();
local digxp = boneworld.mineralxp[nodename] or 0; digxp = digxp*0.1; -- bonus xp
local xp = boneworld.digxp[name] or 0;
xp = xp + digxp;
boneworld.digxp[name] = xp;
-- extra reward with small probability
if xp<100 or nodename == "default:stone" or digxp == 0 then return end
local P;
if xp>10000 then
P=0.5
else
P = (xp/10000+0.0001)*0.5;
end
if math.random(1/P) == 1 then
P=1;
end
if P==1 then
local player_inv = digger:get_inventory()
local stk = ItemStack(nodename);
if player_inv:room_for_item("main", stk) then
--minetest.chat_send_player(name, "Congratulations! You found extra " .. nodename)
player_inv:add_item("main", stk)
end
end
--minetest.chat_send_all(name .. " digged " .. nodename .. " for " .. digxp .. " mining xp ")
end
local set_after_dig_node = function(name)
local tabl = minetest.registered_nodes[name];
if not tabl then return end
minetest.override_item(name, {after_dig_node = after_dig_node})
end
minetest.after(0,
function()
set_after_dig_node("default:stone");
set_after_dig_node("default:stone_with_iron");
set_after_dig_node("default:stone_with_copper");
set_after_dig_node("default:stone_with_coal");
set_after_dig_node("default:stone_with_gold");
set_after_dig_node("default:stone_with_mese");
set_after_dig_node("default:stone_with_diamond");
end
)

View File

@ -1,20 +0,0 @@
Copyright (c) 2015-2016 BlockMen <blockmen2015@gmail.com>
This software is provided 'as-is', without any express or implied warranty. In no
event will the authors be held liable for any damages arising from the use of
this software.
Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to the
following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation is required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View File

@ -1,26 +0,0 @@
Creatures MOB-Engine
====================
Copyright (c) 2015-2016 BlockMen <blockmen2015@gmail.com>
Version: 2.0.2
This mod provides an engine, that handles the base function for MOB in Minetest.
It offers an easy way to register MOB and allows to custom handling for the needs
of each mob. This engine aims to be a solid base, that has a good balance between
performance and functionality.
See API.txt for more informations on how to use this engine for mobs.
License:
~~~~~~~~
Code:
(c) Copyright 2015-2016 BlockMen; modified zlib-License
see "LICENSE.txt" for details.
Media(textures and other media):
(c) Copyright (2014-2016) BlockMen; CC-BY-SA 3.0
Github:
~~~~~~~
https://github.com/BlockMen/cme/creatures

View File

@ -1,148 +0,0 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015-2016 BlockMen <blockmen2015@gmail.com>
--
-- common.lua
--
-- This software is provided 'as-is', without any express or implied warranty. In no
-- event will the authors be held liable for any damages arising from the use of
-- this software.
--
-- Permission is granted to anyone to use this software for any purpose, including
-- commercial applications, and to alter it and redistribute it freely, subject to the
-- following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
-- claim that you wrote the original software. If you use this software in a
-- product, an acknowledgment in the product documentation is required.
-- 2. Altered source versions must be plainly marked as such, and must not
-- be misrepresented as being the original software.
-- 3. This notice may not be removed or altered from any source distribution.
--
-- constants
nullVec = {x = 0, y = 0, z = 0}
DEGTORAD = math.pi / 180.0
-- common functions
function creatures.rnd(table, errval)
if not errval then
errval = false
end
local res = 1000000000
local rn = math.random(0, res - 1)
local retval = nil
local psum = 0
for s,w in pairs(table) do
psum = psum + ((tonumber(w) or w.chance or 0) * res)
if psum > rn then
retval = s
break
end
end
return retval
end
function throw_error(msg)
core.log("error", "#Creatures: ERROR: " .. msg)
end
function creatures.compare_pos(pos1, pos2)
if not pos1 or not pos2 then
return
end
if pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z then
return false
end
return true
end
function creatures.findTarget(search_obj, pos, radius, search_type, ignore_mob, xray, no_count)
local player_near = false
local mobs = {}
for _,obj in ipairs(core.get_objects_inside_radius(pos, radius)) do
if obj ~= search_obj then
if xray or core.line_of_sight(pos, obj:getpos()) == true then
local is_player = obj:is_player()
if is_player then
player_near = true
if no_count == true then
return {}, true
end
end
local entity = obj:get_luaentity()
local isItem = (entity and entity.name == "__builtin:item") or false
local ignore = (entity and entity.mob_name == ignore_mob and search_type ~= "mates") or false
if search_type == "all" then
if not isItem and not ignore then
table.insert(mobs, obj)
end
elseif search_type == "hostile" then
if not ignore and (entity and entity.hostile == true) or is_player then
table.insert(mobs, obj)
end
elseif search_type == "nonhostile" then
if entity and not entity.hostile and not isItem and not ignore then
table.insert(mobs, obj)
end
elseif search_type == "player" then
if is_player then
table.insert(mobs, obj)
end
elseif search_type == "mate" then
if not isItem and (entity and entity.mob_name == ignore_mob) then
table.insert(mobs, obj)
end
end
end
end --for
end
return mobs,player_near
end
function creatures.dropItems(pos, drops)
if not pos or not drops then
return
end
-- convert drops table
local tab = {}
for _,elem in pairs(drops) do
local name = tostring(elem[1])
local v = elem[2]
local chance = elem.chance
local amount = ""
-- check if drops depending on defined chance
if name and chance then
local ct = {}
ct[name] = chance
ct["_fake"] = 1 - chance
local res = creatures.rnd(ct)
if res == "_fake" then
name = nil
end
end
-- get amount
if name and v then
if type(v) == "table" then
amount = math.random(v.min or 1, v.max or 1) or 1
elseif type(v) == "number" then
amount = v
end
if amount > 0 then
amount = " " .. amount
end
end
if name then
local obj = core.add_item(pos, name .. amount)
if not obj then
throw_error("Could not drop item '" .. name .. amount .. "'")
end
end
end
end

View File

@ -1,2 +0,0 @@
default
wool

View File

@ -1 +0,0 @@
A Mod(pack) for Minetest that provides a MOB-Engine and adds several creatures to the game.

View File

@ -1,674 +0,0 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015-2016 BlockMen <blockmen2015@gmail.com>
--
-- functions.lua
--
-- This software is provided 'as-is', without any express or implied warranty. In no
-- event will the authors be held liable for any damages arising from the use of
-- this software.
--
-- Permission is granted to anyone to use this software for any purpose, including
-- commercial applications, and to alter it and redistribute it freely, subject to the
-- following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
-- claim that you wrote the original software. If you use this software in a
-- product, an acknowledgment in the product documentation is required.
-- 2. Altered source versions must be plainly marked as such, and must not
-- be misrepresented as being the original software.
-- 3. This notice may not be removed or altered from any source distribution.
--
-- Localizations
local rnd = math.random
local function knockback(selfOrObject, dir, old_dir, strengh)
local object = selfOrObject
if selfOrObject.mob_name then
object = selfOrObject.object
end
local current_fmd = object:get_properties().automatic_face_movement_dir or 0
object:set_properties({automatic_face_movement_dir = false})
object:setvelocity(vector.add(old_dir, {x = dir.x * strengh, y = 3.5, z = dir.z * strengh}))
old_dir.y = 0
core.after(0.4, function()
object:set_properties({automatic_face_movement_dir = current_fmd})
object:setvelocity(old_dir)
selfOrObject.falltimer = nil
if selfOrObject.stunned == true then
selfOrObject.stunned = false
if selfOrObject.can_panic == true then
selfOrObject.target = nil
selfOrObject.mode = "_run"
selfOrObject.modetimer = 0
end
end
end)
end
local function on_hit(me)
core.after(0.1, function()
me:settexturemod("^[colorize:#c4000099")
end)
core.after(0.5, function()
me:settexturemod("")
end)
end
local hasMoved = creatures.compare_pos
local function getDir(pos1, pos2)
local retval
if pos1 and pos2 then
retval = {x = pos2.x - pos1.x, y = pos2.y - pos1.y, z = pos2.z - pos1.z}
end
return retval
end
local function getDistance(vec, fly_offset)
if not vec then
return -1
end
if fly_offset then
vec.y = vec.y + fly_offset
end
return math.sqrt((vec.x)^2 + (vec.y)^2 + (vec.z)^2)
end
local findTarget = creatures.findTarget
local function update_animation(obj_ref, mode, anim_def)
if anim_def and obj_ref then
obj_ref:set_animation({x = anim_def.start, y = anim_def.stop}, anim_def.speed, 0, anim_def.loop)
end
end
local function update_velocity(obj_ref, dir, speed, add)
local velo = obj_ref:getvelocity()
if not dir.y then
dir.y = velo.y/speed
end
local new_velo = {x = dir.x * speed, y = dir.y * speed or velo.y , z = dir.z * speed}
if add then
new_velo = vector.add(velo, new_velo)
end
obj_ref:setvelocity(new_velo)
end
local function getYaw(dirOrYaw)
local yaw = 360 * rnd()
if dirOrYaw and type(dirOrYaw) == "table" then
yaw = math.atan(dirOrYaw.z / dirOrYaw.x) + math.pi^2 - 2
if dirOrYaw.x > 0 then
yaw = yaw + math.pi
end
elseif dirOrYaw and type(dirOrYaw) == "number" then
-- here could be a value based on given yaw
end
return yaw
end
local dropItems = creatures.dropItems
local function killMob(me, def)
if not def then
if me then
me:remove()
end
end
local pos = me:getpos()
me:setvelocity(nullVec)
me:set_properties({collisionbox = nullVec})
me:set_hp(0)
if def.sounds and def.sounds.on_death then
local death_snd = def.sounds.on_death
core.sound_play(death_snd.name, {pos = pos, max_hear_distance = death_snd.distance or 5, gain = death_snd.gain or 1})
end
if def.model.animations.death then
local dur = def.model.animations.death.duration or 0.5
update_animation(me, "death", def.model.animations["death"])
core.after(dur, function()
me:remove()
end)
else
me:remove()
end
if def.drops then
if type(def.drops) == "function" then
def.drops(me:get_luaentity())
else
dropItems(pos, def.drops)
end
end
end
local function limit(value, min, max)
if value < min then
return min
end
if value > max then
return max
end
return value
end
local function calcPunchDamage(obj, actual_interval, tool_caps)
local damage = 0
if not tool_caps or not actual_interval then
return 0
end
local my_armor = obj:get_armor_groups() or {}
for group,_ in pairs(tool_caps.damage_groups) do
damage = damage + (tool_caps.damage_groups[group] or 0) * limit(actual_interval / tool_caps.full_punch_interval, 0.0, 1.0) * ((my_armor[group] or 0) / 100.0)
end
return damage or 0
end
local function onDamage(self, hp)
local me = self.object
local def = core.registered_entities[self.mob_name]
hp = hp or me:get_hp()
if hp <= 0 then
self.stunned = true
killMob(me, def)
else
on_hit(me) -- red flashing
if def.sounds and def.sounds.on_damage then
local dmg_snd = def.sounds.on_damage
core.sound_play(dmg_snd.name, {pos = me:getpos(), max_hear_distance = dmg_snd.distance or 5, gain = dmg_snd.gain or 1})
end
end
end
local function changeHP(self, value)
local me = self.object
local hp = me:get_hp()
hp = hp + math.floor(value)
me:set_hp(hp)
if value < 0 then
onDamage(self, hp)
end
end
local function checkWielded(wielded, itemList)
for s,w in pairs(itemList) do
if w == wielded then
return true
end
end
return false
end
local tool_uses = {0, 30, 110, 150, 280, 300, 500, 1000}
local function addWearout(player, tool_def)
if not core.setting_getbool("creative_mode") then
local item = player:get_wielded_item()
if tool_def and tool_def.damage_groups and tool_def.damage_groups.fleshy then
local uses = tool_uses[tool_def.damage_groups.fleshy] or 0
if uses > 0 then
local wear = 65535/uses
item:add_wear(wear)
player:set_wielded_item(item)
end
end
end
end
local function spawnParticles(...)
end
if core.setting_getbool("creatures_enable_particles") == true then
spawnParticles = function(pos, velocity, texture_str)
local vel = vector.multiply(velocity, 0.5)
vel.y = 0
core.add_particlespawner({
amount = 8,
time = 1,
minpos = vector.add(pos, -0.7),
maxpos = vector.add(pos, 0.7),
minvel = vector.add(vel, {x = -0.1, y = -0.01, z = -0.1}),
maxvel = vector.add(vel, {x = 0.1, y = 0, z = 0.1}),
minacc = vector.new(),
maxacc = vector.new(),
minexptime = 0.8,
maxexptime = 1,
minsize = 1,
maxsize = 2.5,
texture = texture_str,
})
end
end
-- --
-- Default entity functions
-- --
creatures.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
if self.stunned == true then
return
end
local me = self.object
local mypos = me:getpos()
changeHP(self, calcPunchDamage(me, time_from_last_punch, tool_capabilities) * -1)
if puncher then
if self.hostile then
self.mode = "attack"
self.target = puncher
end
if time_from_last_punch >= 0.45 and self.stunned == false then
if self.has_kockback == true then
local v = me:getvelocity()
v.y = 0
if not self.can_fly then
me:setacceleration({x = 0, y = -15, z = 0})
end
knockback(self, dir, v, 5)
self.stunned = true
end
-- add wearout to weapons/tools
addWearout(puncher, tool_capabilities)
end
end
end
creatures.on_rightclick = function(self, clicker)
end
creatures.on_step = function(self, dtime)
-- first get the relevant specs; exit if we don't know anything (1-3ms)
local def = core.registered_entities[self.mob_name]
if not def then
throw_error("Can't load creature-definition")
return
end
-- timer updates
self.lifetimer = self.lifetimer + dtime
self.modetimer = self.modetimer + dtime
self.soundtimer = self.soundtimer + dtime
self.yawtimer = self.yawtimer + dtime
self.nodetimer = self.nodetimer + dtime
self.followtimer = self.followtimer + dtime
if self.envtimer then
self.envtimer = self.envtimer + dtime
end
if self.falltimer then
self.falltimer = self.falltimer + dtime
end
if self.searchtimer then
self.searchtimer = self.searchtimer + dtime
end
if self.attacktimer then
self.attacktimer = self.attacktimer + dtime
end
if self.swimtimer then
self.swimtimer = self.swimtimer + dtime
end
-- main
if self.stunned == true then
return
end
if self.lifetimer > def.stats.lifetime and not (self.mode == "attack" and self.target) then
self.lifetimer = 0
if not self.tamed or (self.tamed and def.stats.dies_when_tamed) then
killMob(self.object, def)
end
end
-- localize some things
local modes = def.modes
local current_mode = self.mode
local me = self.object
local current_pos = me:getpos()
current_pos.y = current_pos.y + 0.5
local moved = hasMoved(current_pos, self.last_pos) or false
local fallen = false
-- Update pos and current node if necessary
if moved == true or not self.last_pos then
-- for falldamage
if self.has_falldamage and self.last_pos and not self.in_water then
local dist = math.abs(current_pos.y - self.last_pos.y)
if dist > 0 then
self.fall_dist = self.fall_dist - dist
if not self.falltimer then
self.falltimer = 0
end
end
end
self.last_pos = current_pos
if self.nodetimer > 0.2 then
self.nodetimer = 0
local current_node = core.get_node_or_nil(current_pos)
self.last_node = current_node
if def.stats.light then
local wtime = core.get_timeofday()
local llvl = core.get_node_light({x = current_pos.x, y = current_pos.y + 0.5, z = current_pos.z}) or 0
self.last_llvl = llvl
end
end
else
if (modes[current_mode].moving_speed or 0) > 0 then
update_velocity(me, nullVec, 0)
if modes["idle"] and not (current_mode == "attack" or current_mode == "follow") then
current_mode = "idle"
self.modetimer = 0
end
end
if self.fall_dist < 0 then
fallen = true
end
end
if fallen then
local falltime = tonumber(self.falltimer) or 0
local dist = math.abs(self.fall_dist) or 0
self.falltimer = 0
self.fall_dist = 0
fallen = false
local damage = 0
if dist > 3 and not self.in_water and falltime/dist < 0.2 then
damage = dist - 3
end
-- damage by calced value
if damage > 0 then
changeHP(self, damage * -1)
end
end
-- special mode handling
-- check distance to target
if self.target and self.followtimer > 0.6 then
self.followtimer = 0
local p2 = self.target:getpos()
local dir = getDir(current_pos, p2)
local offset
if self.can_fly then
offset = modes["fly"].target_offset
end
local dist = getDistance(dir, offset)
local radius
if self.hostile and def.combat then
radius = def.combat.search_radius
elseif modes["follow"] then
radius = modes["follow"].radius
end
if dist == -1 or dist > (radius or 5) then
self.target = nil
current_mode = ""
elseif dist > -1 and self.hostile and dist < def.combat.attack_radius then
-- attack
if self.attacktimer > def.combat.attack_speed then
self.attacktimer = 0
if core.line_of_sight(current_pos, p2) == true then
self.target:punch(me, 1.0, {
full_punch_interval = def.combat.attack_speed,
damage_groups = {fleshy = def.combat.attack_damage}
})
end
update_velocity(me, self.dir, 0)
end
else
if current_mode == "attack" or current_mode == "follow" then
self.dir = vector.normalize(dir)
me:setyaw(getYaw(dir))
if self.in_water then
self.dir.y = me:getvelocity().y
end
update_velocity(me, self.dir, modes[current_mode].moving_speed or 0)
end
end
end
-- search a target (1-2ms)
if not self.target and ((self.hostile and def.combat.search_enemy) or modes["follow"]) and current_mode ~= "_run" then
local timer
if self.hostile then
timer = def.combat.search_timer or 2
elseif modes["follow"] then
timer = modes["follow"].timer
end
if self.searchtimer > (timer or 4) then
self.searchtimer = 0
local targets = {}
if self.hostile then
targets = findTarget(me, current_pos, def.combat.search_radius, def.combat.search_type, def.combat.search_xray)
else
targets = findTarget(me, current_pos, modes["follow"].radius or 5, "player")
end
if #targets > 1 then
self.target = targets[rnd(1, #targets)]
elseif #targets == 1 then
self.target = targets[1]
end
if self.target then
if self.hostile and modes["attack"] then
current_mode = "attack"
else
local name = self.target:get_wielded_item():get_name()
if name and checkWielded(name, modes["follow"].items) == true then
current_mode = "follow"
self.modetimer = 0
else
self.target = nil
end
end
end
end
end
if current_mode == "eat" and not self.eat_node then
local nodes = modes[current_mode].nodes
local p = {x = current_pos.x, y = current_pos.y - 1, z = current_pos.z}
local sn = core.get_node_or_nil(p)
local eat_node
for _,name in pairs(nodes) do
if name == self.last_node.name then
eat_node = current_pos
break
elseif sn and sn.name == name then
eat_node = p
break
end
end
if not eat_node then
current_mode = "idle"
else
self.eat_node = eat_node
end
end
-- further mode handling
-- update mode
if current_mode ~= "attack" and
(current_mode == "" or self.modetimer > (modes[current_mode].duration or 4)) then
self.modetimer = 0
local new_mode = creatures.rnd(modes) or "idle"
if new_mode == "eat" and self.in_water == true then
new_mode = "idle"
end
if current_mode == "follow" and rnd(1, 10) < 3 then
new_mode = current_mode
elseif current_mode == "follow" then
-- "lock" searching a little bit
self.searchtimer = rnd(5, 8) * -1
self.target = nil
end
current_mode = new_mode
-- change eaten node when mode changes
if self.eat_node then
local n = core.get_node_or_nil(self.eat_node)
local nnn = n.name
local def = core.registered_nodes[n.name]
local sounds
if def then
if def.drop and type(def.drop) == "string" then
nnn = def.drop
elseif not def.walkable then
nnn = "air"
end
end
if nnn and nnn ~= n.name and core.registered_nodes[nnn] then
core.set_node(self.eat_node, {name = nnn})
if not sounds then
sounds = def.sounds
end
if sounds and sounds.dug then
core.sound_play(sounds.dug, {pos = self.eat_node, max_hear_distance = 5, gain = 1})
end
end
self.eat_node = nil
end
end
-- mode has changes, do things
if current_mode ~= self.last_mode then
self.last_mode = current_mode
local moving_speed = modes[current_mode].moving_speed or 0
if moving_speed > 0 then
local yaw = (getYaw(me:getyaw()) + 90.0) * DEGTORAD
me:setyaw(yaw + 4.73)
self.dir = {x = math.cos(yaw), y = 0, z = math.sin(yaw)}
if self.can_fly then
if current_pos.y >= (modes["fly"].max_height or 50) and not self.target then
self.dir.y = -0.5
else
self.dir.y = (rnd() - 0.5)
end
end
-- reduce speed in water
if self.in_water == true then
moving_speed = moving_speed * 0.7
end
else
self.dir = nullVec
end
update_velocity(me, self.dir, moving_speed)
local anim_def = def.model.animations[current_mode]
if self.in_water and def.model.animations["swim"] then
anim_def = def.model.animations["swim"]
end
update_animation(me, current_mode, anim_def)
end
-- update yaw
if current_mode ~= "attack" and current_mode ~= "follow" and
(modes[current_mode].update_yaw or 0) > 0 and
self.yawtimer > (modes[current_mode].update_yaw or 4) then
self.yawtimer = 0
local mod = nil
if current_mode == "_run" then
mod = me:getyaw()
end
local yaw = (getYaw(mod) + 90.0) * DEGTORAD
me:setyaw(yaw + 4.73)
local moving_speed = modes[current_mode].moving_speed or 0
if moving_speed > 0 then
self.dir = {x = math.cos(yaw), y = nil, z = math.sin(yaw)}
update_velocity(me, self.dir, moving_speed)
end
end
--swim
if self.can_swim and self.swimtimer > 0.8 and self.last_node then
self.swimtimer = 0
local name = self.last_node.name
if name then
if name == "default:water_source" then
self.air_cnt = 0
local vel = me:getvelocity()
update_velocity(me, {x = vel.x, y = 0.9, z = vel.z}, 1)
me:setacceleration({x = 0, y = -1.2, z = 0})
self.in_water = true
-- play swimming sounds
if def.sounds and def.sounds.swim then
local swim_snd = def.sounds.swim
core.sound_play(swim_snd.name, {pos = current_pos, gain = swim_snd.gain or 1, max_hear_distance = swim_snd.distance or 10})
end
spawnParticles(current_pos, vel, "bubble.png")
else
self.air_cnt = self.air_cnt + 1
if self.in_water == true and self.air_cnt > 5 then
self.in_water = false
if not self.can_fly then
me:setacceleration({x = 0, y = -15, z = 0})
end
end
end
end
end
-- Add damage when drowning or in lava
if self.env_damage and self.envtimer > 1 and self.last_node then
self.envtimer = 0
local name = self.last_node.name
if not self.can_swim and name == "default:water_source" then
changeHP(self, -1)
elseif self.can_burn then
if name == "fire:basic_flame" or name == "default:lava_source" then
changeHP(self, -4)
end
end
-- add damage when light is too bright or too dark
local tod = core.get_timeofday() * 24000
if self.last_llvl and self.can_burn and self.last_llvl > (def.stats.light.max or 15) and tod < 18000 then
changeHP(self, -1)
elseif self.last_llvl and self.last_llvl < (def.stats.light.min or 0) then
changeHP(self, -2)
end
end
-- Random sounds
if def.sounds and def.sounds.random[current_mode] then
local rnd_sound = def.sounds.random[current_mode]
if not self.snd_rnd_time then
self.snd_rnd_time = rnd((rnd_sound.time_min or 5), (rnd_sound.time_max or 35))
end
if rnd_sound and self.soundtimer > self.snd_rnd_time + rnd() then
self.soundtimer = 0
self.snd_rnd_time = nil
core.sound_play(rnd_sound.name, {pos = current_pos, gain = rnd_sound.gain or 1, max_hear_distance = rnd_sound.distance or 30})
end
end
self.mode = current_mode
end
creatures.get_staticdata = function(self)
return {
hp = self.object:get_hp(),
mode = self.mode,
tamed = self.tamed,
modetimer = self.modetimer,
lifetimer = self.lifetimer,
soundtimer = self.soundtimer,
fall_dist = self.fall_dist,
in_water = self.in_water,
}
end

View File

@ -1,33 +0,0 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015-2016 BlockMen <blockmen2015@gmail.com>
--
-- init.lua
--
-- This software is provided 'as-is', without any express or implied warranty. In no
-- event will the authors be held liable for any damages arising from the use of
-- this software.
--
-- Permission is granted to anyone to use this software for any purpose, including
-- commercial applications, and to alter it and redistribute it freely, subject to the
-- following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
-- claim that you wrote the original software. If you use this software in a
-- product, an acknowledgment in the product documentation is required.
-- 2. Altered source versions must be plainly marked as such, and must not
-- be misrepresented as being the original software.
-- 3. This notice may not be removed or altered from any source distribution.
--
creatures = {}
local modpath = core.get_modpath("creatures")
-- API and common functions
dofile(modpath .."/common.lua")
dofile(modpath .."/functions.lua")
dofile(modpath .."/register.lua")
-- Common items
--dofile(modpath .."/items.lua")

View File

@ -1,39 +0,0 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015-2016 BlockMen <blockmen2015@gmail.com>
--
-- items.lua
--
-- This software is provided 'as-is', without any express or implied warranty. In no
-- event will the authors be held liable for any damages arising from the use of
-- this software.
--
-- Permission is granted to anyone to use this software for any purpose, including
-- commercial applications, and to alter it and redistribute it freely, subject to the
-- following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
-- claim that you wrote the original software. If you use this software in a
-- product, an acknowledgment in the product documentation is required.
-- 2. Altered source versions must be plainly marked as such, and must not
-- be misrepresented as being the original software.
-- 3. This notice may not be removed or altered from any source distribution.
--
core.register_craftitem("creatures:flesh", {
description = "Flesh",
inventory_image = "creatures_flesh.png",
on_use = core.item_eat(2),
})
core.register_craftitem("creatures:meat", {
description = "Cooked Meat",
inventory_image = "creatures_meat.png",
on_use = core.item_eat(4),
})
core.register_craft({
type = "cooking",
output = "creatures:meat",
recipe = "creatures:flesh",
})

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -1,580 +0,0 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015-2016 BlockMen <blockmen2015@gmail.com>
--
-- register.lua
--
-- This software is provided 'as-is', without any express or implied warranty. In no
-- event will the authors be held liable for any damages arising from the use of
-- this software.
--
-- Permission is granted to anyone to use this software for any purpose, including
-- commercial applications, and to alter it and redistribute it freely, subject to the
-- following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
-- claim that you wrote the original software. If you use this software in a
-- product, an acknowledgment in the product documentation is required.
-- 2. Altered source versions must be plainly marked as such, and must not
-- be misrepresented as being the original software.
-- 3. This notice may not be removed or altered from any source distribution.
--
local allow_hostile = core.setting_getbool("only_peaceful_mobs") ~= true
local function translate_def(def)
local new_def = {
physical = true,
visual = "mesh",
stepheight = 0.6, -- ensure we get over slabs/stairs
automatic_face_movement_dir = def.model.rotation or 0.0,
mesh = def.model.mesh,
textures = def.model.textures,
collisionbox = def.model.collisionbox or {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
visual_size = def.model.scale or {x = 1, y = 1},
backface_culling = def.model.backface_culling or false,
collide_with_objects = def.model.collide_with_objects or true,
makes_footstep_sound = true,
stats = def.stats,
model = def.model,
sounds = def.sounds,
combat = def.combat,
modes = {},
drops = def.drops,
}
-- Tanslate modes to better accessable format
for mn,def in pairs(def.modes) do
local name = tostring(mn)
if name ~= "update_time" then
new_def.modes[name] = def
--if name == "attack" then new_def.modes[name].chance = 0 end
end
end
-- insert special mode "_run" which is used when in panic
if def.stats.can_panic then
if def.modes.walk then
local new = table.copy(new_def.modes["walk"])
new.chance = 0
new.duration = 3
new.moving_speed = new.moving_speed * 2
if def.modes.panic and def.modes.panic.moving_speed then
new.moving_speed = def.modes.panic.moving_speed
end
new.update_yaw = 0.7
new_def.modes["_run"] = new
local new_anim = def.model.animations.panic
if not new_anim then
new_anim = table.copy(def.model.animations.walk)
new_anim.speed = new_anim.speed * 2
end
new_def.model.animations._run = new_anim
end
end
if def.stats.can_jump and type(def.stats.can_jump) == "number" then
if def.stats.can_jump > 0 then
new_def.stepheight = def.stats.can_jump + 0.1
end
end
if def.stats.sneaky or def.stats.can_fly then
new_def.makes_footstep_sound = false
end
new_def.get_staticdata = function(self)
local main_tab = creatures.get_staticdata(self)
-- is own staticdata function defined? If so, merge results
if def.get_staticdata then
local data = def.get_staticdata(self)
if data and type(data) == "table" then
for s,w in pairs(data) do
main_tab[s] = w
end
end
end
-- return data serialized
return core.serialize(main_tab)
end
new_def.on_activate = function(self, staticdata)
-- Add everything we need as basis for the engine
self.mob_name = def.name
self.hp = def.stats.hp
self.hostile = def.stats.hostile
self.mode = ""
self.stunned = false -- if knocked back or hit do nothing else
self.has_kockback = def.stats.has_kockback
self.has_falldamage = def.stats.has_falldamage
self.can_swim = def.stats.can_swim
self.can_fly = def.stats.can_fly
self.can_burn = def.stats.can_burn
self.can_panic = def.stats.can_panic == true and def.modes.walk ~= nil
--self.is_tamed = nil
--self.target = nil
self.dir = {x = 0, z = 0}
--self.last_pos = nil (was nullVec)
--self.last_node = nil
--self.last_llvl = 0
self.fall_dist = 0
self.air_cnt = 0
-- Timers
self.lifetimer = 0
self.modetimer = math.random()--0
self.soundtimer = math.random()
self.nodetimer = 2 -- ensure we get the first step
self.yawtimer = math.random() * 2--0
self.followtimer = 0
if self.can_swim then
self.swimtimer = 2 -- ensure we get the first step
-- self.in_water = nil
end
if self.hostile then
self.attacktimer = 0
end
if self.hostile or def.modes.follow then
self.searchtimer = 0
end
if self.can_burn or not def.stats.can_swim or self.has_falldamage then
self.env_damage = true
self.envtimer = 0
end
-- Other things
if staticdata then
local tab = core.deserialize(staticdata)
if tab and type(tab) == "table" then
for s,w in pairs(tab) do
self[tostring(s)] = w
end
end
end
-- check we got a valid mode
if not new_def.modes[self.mode] or (new_def.modes[self.mode].chance or 0) <= 0 then
self.mode = "idle"
end
if not self.can_fly then
if not self.in_water then
self.object:setacceleration({x = 0, y = -15, z = 0})
end
end
-- check if falling and set velocity only 0 when not falling
if self.fall_dist == 0 then
self.object:setvelocity(nullVec)
end
self.object:set_hp(self.hp)
if not core.setting_getbool("enable_damage") then
self.hostile = false
end
-- immortal is needed to disable clientside smokepuff shit
self.object:set_armor_groups({fleshy = 100, immortal = 1})
-- call custom on_activate if defined
if def.on_activate then
def.on_activate(self, staticdata)
end
end
new_def.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
if def.on_punch and def.on_punch(self, puncher, time_from_last_punch, tool_capabilities, dir) == true then
return
end
creatures.on_punch(self, puncher, time_from_last_punch, tool_capabilities, dir)
end
new_def.on_rightclick = function(self, clicker)
if def.on_rightclick and def.on_rightclick(self, clicker) == true then
return
end
creatures.on_rightclick(self, clicker)
end
new_def.on_step = function(self, dtime)
if def.on_step and def.on_step(self, dtime) == true then
return
end
creatures.on_step(self, dtime)
end
return new_def
end
function creatures.register_mob(def) -- returns true if sucessfull
if not def or not def.name then
throw_error("Can't register mob. No name or Definition given.")
return false
end
local mob_def = translate_def(def)
core.register_entity(":" .. def.name, mob_def)
-- register spawn
if def.spawning and not (def.stats.hostile and not allow_hostile) then
local spawn_def = def.spawning
spawn_def.mob_name = def.name
spawn_def.mob_size = def.model.collisionbox
if creatures.register_spawn(spawn_def) ~= true then
throw_error("Couldn't register spawning for '" .. def.name .. "'")
end
if spawn_def.spawn_egg then
local egg_def = def.spawning.spawn_egg
egg_def.mob_name = def.name
egg_def.box = def.model.collisionbox
creatures.register_egg(egg_def)
end
if spawn_def.spawner then
local spawner_def = def.spawning.spawner
spawner_def.mob_name = def.name
spawner_def.range = spawner_def.range or 4
spawner_def.number = spawner_def.number or 6
spawner_def.model = def.model
creatures.register_spawner(spawner_def)
end
end
return true
end
local function inRange(min_max, value)
if not value or not min_max or not min_max.min or not min_max.max then
return false
end
if (value >= min_max.min and value <= min_max.max) then
return true
end
return false
end
local function checkSpace(pos, height)
for i = 0, height do
local n = core.get_node_or_nil({x = pos.x, y = pos.y + i, z = pos.z})
if not n or n.name ~= "air" then
return false
end
end
return true
end
local time_taker = 0
local function step(tick)
core.after(tick, step, tick)
time_taker = time_taker + tick
end
step(0.5)
local function stopABMFlood()
if time_taker == 0 then
return true
end
time_taker = 0
end
local function groupSpawn(pos, mob, group, nodes, range, max_loops)
local cnt = 0
local cnt2 = 0
local nodes = core.find_nodes_in_area({x = pos.x - range, y = pos.y - range, z = pos.z - range},
{x = pos.x + range, y = pos.y, z = pos.z + range}, nodes)
local number = #nodes - 1
if max_loops and type(max_loops) == "number" then
number = max_loops
end
while cnt < group and cnt2 < number do
cnt2 = cnt2 + 1
local p = nodes[math.random(1, number)]
p.y = p.y + 1
if checkSpace(p, mob.size) == true then
cnt = cnt + 1
core.add_entity(p, mob.name)
end
end
if cnt < group then
return false
end
end
function creatures.register_spawn(spawn_def)
if not spawn_def or not spawn_def.abm_nodes then
throw_error("No valid definition for given.")
return false
end
if not spawn_def.abm_nodes.neighbors then
spawn_def.abm_nodes.neighbors = {}
end
table.insert(spawn_def.abm_nodes.neighbors, "air")
core.register_abm({
nodenames = spawn_def.abm_nodes.spawn_on,
neighbors = spawn_def.abm_nodes.neighbors,
interval = spawn_def.abm_interval or 44,
chance = spawn_def.abm_chance or 7000,
catch_up = false,
action = function(pos, node, active_object_count, active_object_count_wider)
-- prevent abm-"feature"
if stopABMFlood() == true then
return
end
-- time check
local tod = core.get_timeofday() * 24000
if spawn_def.time_range then
local wanted_res = false
local range = table.copy(spawn_def.time_range)
if range.min > range.max and range.min <= tod then
wanted_res = true
end
if inRange(range, tod) == wanted_res then
return
end
end
-- position check
if spawn_def.height_limit and not inRange(spawn_def.height_limit, pos.y) then
return
end
-- light check
pos.y = pos.y + 1
local llvl = core.get_node_light(pos)
if spawn_def.light and not inRange(spawn_def.light, llvl) then
return
end
-- creature count check
local max
if active_object_count_wider > (spawn_def.max_number or 1) then
local mates_num = #creatures.findTarget(nil, pos, 16, "mate", spawn_def.mob_name, true)
if (mates_num or 0) >= spawn_def.max_number then
return
else
max = spawn_def.max_number - mates_num
end
end
-- ok everything seems fine, spawn creature
local height_min = (spawn_def.mob_size[5] or 2) - (spawn_def.mob_size[2] or 0)
height_min = math.ceil(height_min)
local number = 0
if type(spawn_def.number) == "table" then
number = math.random(spawn_def.number.min, spawn_def.number.max)
else
number = spawn_def.number or 1
end
if max and number > max then
number = max
end
if number > 1 then
groupSpawn(pos, {name = spawn_def.mob_name, size = height_min}, number, spawn_def.abm_nodes.spawn_on, 5)
else
-- space check
if not checkSpace(pos, height_min) then
return
end
core.add_entity(pos, spawn_def.mob_name)
end
end,
})
return true
end
local function eggSpawn(itemstack, placer, pointed_thing, egg_def)
if pointed_thing.type == "node" then
local pos = pointed_thing.above
pos.y = pos.y + 0.5
local height = (egg_def.box[5] or 2) - (egg_def.box[2] or 0)
if checkSpace(pos, height) == true then
core.add_entity(pos, egg_def.mob_name)
if core.setting_getbool("creative_mode") ~= true then
itemstack:take_item()
end
end
return itemstack
end
end
function creatures.register_egg(egg_def)
if not egg_def or not egg_def.mob_name or not egg_def.box then
throw_error("Can't register Spawn-Egg. Not enough parameters given.")
return false
end
core.register_craftitem(":" .. egg_def.mob_name .. "_spawn_egg", {
description = egg_def.description or egg_def.mob_name .. " spawn egg",
inventory_image = egg_def.texture or "creatures_spawn_egg.png",
liquids_pointable = false,
on_place = function(itemstack, placer, pointed_thing)
return eggSpawn(itemstack, placer, pointed_thing, egg_def)
end,
})
return true
end
local function makeSpawnerEntiy(mob_name, model)
core.register_entity(":" .. mob_name .. "_spawner_dummy", {
hp_max = 1,
physical = false,
collide_with_objects = false,
collisionbox = nullVec,
visual = "mesh",
visual_size = {x = 0.42, y = 0.42},
mesh = model.mesh,
textures = model.textures,
makes_footstep_sound = false,
automatic_rotate = math.pi * -2.9,
mob_name = "_" .. mob_name .. "_dummy",
on_activate = function(self)
self.timer = 0
self.object:setvelocity(nullVec)
self.object:setacceleration(nullVec)
self.object:set_armor_groups({immortal = 1})
--self.object:set_bone_position("Root", nullVec, {x=45,y=0,z=0})
end,
on_step = function(self, dtime)
self.timer = self.timer + dtime
if self.timer > 30 then
self.timer = 0
local n = core.get_node_or_nil(self.object:getpos())
if n and n.name and n.name ~= mob_name .. "_spawner" then
self.object:remove()
end
end
end
})
end
local function spawnerSpawn(pos, spawner_def)
local mates = creatures.findTarget(nil, pos, spawner_def.range, "mate", spawner_def.mob_name, true) or {}
if #mates >= spawner_def.number then
return false
end
local number_max = spawner_def.number - #mates
local rh = math.floor(spawner_def.range/2)
local area = {
min = {x = pos.x - rh, y=pos.y - rh, z = pos.z - rh},
max = {x = pos.x + rh, y=pos.y + rh - spawner_def.height - 1, z = pos.z + rh}
}
local height = area.max.y - area.min.y
local cnt = 0
for i = 0, height do
if cnt >= number_max then
break
end
local p = {x = math.random(area.min.x, area.max.x), y = area.min.y + i, z = math.random(area.min.z, area.max.z)}
local n = core.get_node_or_nil(p)
if n and n.name then
local walkable = core.registered_nodes[n.name].walkable or false
p.y = p.y + 1
if walkable and checkSpace(p, spawner_def.height) == true then
local llvl = core.get_node_light(p)
if not spawner_def.light or (spawner_def.light and inRange(spawner_def.light, llvl)) then
cnt = cnt + 1
core.add_entity(p, spawner_def.mob_name)
end
end
end
end
end
local spawner_timers = {}
function creatures.register_spawner(spawner_def)
if not spawner_def or not spawner_def.mob_name or not spawner_def.model then
throw_error("Can't register Spawn-Egg. Not enough parameters given.")
return false
end
makeSpawnerEntiy(spawner_def.mob_name, spawner_def.model)
core.register_node(":" .. spawner_def.mob_name .. "_spawner", {
description = spawner_def.description or spawner_def.mob_name .. " spawner",
paramtype = "light",
tiles = {"creatures_spawner.png"},
is_ground_content = true,
drawtype = "glasslike",
groups = {cracky = 1, level = 1},
drop = "",
on_construct = function(pos)
pos.y = pos.y - 0.3
core.add_entity(pos, spawner_def.mob_name .. "_spawner_dummy")
end,
on_destruct = function(pos)
for _,obj in ipairs(core.get_objects_inside_radius(pos, 1)) do
local entity = obj:get_luaentity()
if obj and entity and entity.mob_name == "_" .. spawner_def.mob_name .. "_dummy" then
obj:remove()
end
end
end
})
local box = spawner_def.model.collisionbox
local height = (box[5] or 2) - (box[2] or 0)
spawner_def.height = height
if spawner_def.player_range and type(spawner_def.player_range) == "number" then
core.register_abm({
nodenames = {spawner_def.mob_name .. "_spawner"},
interval = 2,
chance = 1,
catch_up = false,
action = function(pos)
local id = core.pos_to_string(pos)
if not spawner_timers[id] then
spawner_timers[id] = os.time()
end
local time_from_last_call = os.time() - spawner_timers[id]
local mobs,player_near = creatures.findTarget(nil, pos, spawner_def.player_range, "player", nil, true, true)
if player_near == true and time_from_last_call > 10 and (math.random(1, 5) == 1 or (time_from_last_call ) > 27) then
spawner_timers[id] = os.time()
spawnerSpawn(pos, spawner_def)
end
end
})
else
core.register_abm({
nodenames = {spawner_def.mob_name .. "_spawner"},
interval = 10,
chance = 3,
action = function(pos)
spawnerSpawn(pos, spawner_def)
end
})
end
return true
end

View File

@ -1,61 +0,0 @@
-------------------------------------------------------------------------
-- Wasteland
-- Copyright (C) 2015 BlockMen <blockmen2015@gmail.de>
--
-- This file is part of Wasteland
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------
local max_mobs_sum = creatures.zombie_max
-- hostile mobs
if not minetest.setting_getbool("only_peaceful_mobs") then
-- zombie
minetest.register_abm({
nodenames = creatures.z_spawn_nodes,
neighbors = {"air"},
interval = 40.0,
chance = 7600,
action = function(pos, node, active_object_count, active_object_count_wider)
-- check per mapblock max (max per creature is done by .spawn())
if active_object_count_wider > max_mobs_sum then
return
end
local n = minetest.get_node_or_nil(pos)
--if n and n.name and n.name ~= "default:stone" and math.random(1,4)>3 then return end
pos.y = pos.y + 1
local ll = minetest.get_node_light(pos)
if not ll then
return
end
if ll >= creatures.z_ll then
return
end
if ll < -1 then
return
end
if minetest.get_node(pos).name ~= "air" then
return
end
pos.y = pos.y + 1
if minetest.get_node(pos).name ~= "air" then
return
end
pos.y = pos.y - 1
creatures.spawn(pos, math.random(1, 3), "creatures:zombie", 2, 20)
end})
end

View File

@ -1,115 +0,0 @@
-------------------------------------------------------------------------
-- Wasteland
-- Copyright (C) 2015 BlockMen <blockmen2015@gmail.de>
--
-- This file is part of Wasteland
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------
function creatures.register_spawner(mob,size,offset,mesh,texture,range,max,max_ll,min_ll,day_only)
local DUMMY = {
hp_max = 1,
physical = true,
collisionbox = {0,0,0,0,0,0},
visual = "mesh",
visual_size = size,
mesh = mesh,
textures = texture,
makes_footstep_sound = false,
timer = 0,
automatic_rotate = math.pi * -2.9,
m_name = "dummy"
}
DUMMY.on_activate = function(self)
self.object:setvelocity({x=0, y=0, z=0})
self.object:setacceleration({x=0, y=0, z=0})
self.object:set_armor_groups({immortal=1})
end
DUMMY.on_step = function(self, dtime)
self.timer = self.timer + 0.01
local n = minetest.get_node_or_nil(self.object:getpos())
if self.timer > 1 then
if n and n.name and n.name ~= "creatures:"..mob.."_spawner" then
self.object:remove()
end
end
end
DUMMY.on_punch = function(self, hitter)
end
minetest.register_entity("creatures:dummy_"..mob, DUMMY)
-- node
minetest.register_node("creatures:"..mob.."_spawner", {
description = mob.." spawner",
paramtype = "light",
tiles = {"creatures_spawner.png"},
is_ground_content = true,
drawtype = "allfaces",
groups = {cracky=1,level=1},
drop = "",
on_construct = function(pos)
pos.y = pos.y + offset
minetest.env:add_entity(pos,"creatures:dummy_"..mob)
end,
on_destruct = function(pos)
for _,obj in ipairs(minetest.env:get_objects_inside_radius(pos, 1)) do
if not obj:is_player() then
if obj ~= nil and obj:get_luaentity().m_name == "dummy" then
obj:remove()
end
end
end
end,
stack_max = 40,
})
--abm
minetest.register_abm({
nodenames = {"creatures:"..mob.."_spawner"},
interval = 2.0,
chance = 20,
action = function(pos, node, active_object_count, active_object_count_wider)
local res,player_near = false
local mobs = 0
res,mobs,player_near = creatures.find_mates(pos, mob, range)
if player_near then
if mobs < max then
pos.x = pos.x+1
local p = minetest.find_node_near(pos, 5, {"air"})
local ll = minetest.env:get_node_light(p)
local wtime = minetest.env:get_timeofday()
if not ll then return end
if ll > max_ll then return end
if ll < min_ll then return end
if minetest.env:get_node(p).name ~= "air" then return end
p.y = p.y+1
if minetest.env:get_node(p).name ~= "air" then return end
if not day_only then
if (wtime > 0.2 and wtime < 0.805) and pos.y > 0 then return end
end
p.y = p.y-1
creatures.spawn(p, 1, "creatures:"..mob,range,max)
end
end
end })
end
-- spawner
creatures.register_spawner("zombie",{x=0.42,y=0.42},0.08,"creatures_mob.x",{"creatures_zombie.png"},17,6,7,-1,false)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 686 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 453 B

View File

@ -1,364 +0,0 @@
-------------------------------------------------------------------------
-- Wasteland
-- Copyright (C) 2015 BlockMen <blockmen2015@gmail.de>
--
-- This file is part of Wasteland
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------
local z_chillaxin_speed = 3
local z_animation_speed = 15
local z_mesh = "creatures_mob.x"
local z_texture = {"creatures_zombie.png"}
local z_hp = 20
local z_drop = "jt_mods:griefer_soul"
local z_life_max = 80 --~5min
local z_player_radius = 24
local z_hit_radius = 1.4
creatures.z_ll = 7
local z_sound_normal = "creatures_zombie"
local z_sound_hit = "creatures_zombie_hit"
local z_sound_dead = "creatures_zombie_death"
creatures.z_spawn_nodes = {"default:dry_dirt","default:dirt","default:mossycobble", "default:stone","default:dirt","default:desert_sand"}
creatures.z_spawner_range = 17
creatures.z_spawner_max_mobs = 6
local function z_get_animations()
return {
stand_START = 0,
stand_END = 79,
lay_START = 162,
lay_END = 166,
walk_START = 168,
walk_END = 188,
-- not used
sit_START = 81,
sit_END = 160
}
end
function z_hit(self)
local sound = z_sound_hit
if self.object:get_hp() < 1 then sound = z_sound_dead end
minetest.sound_play(sound, {pos = self.object:getpos(), max_hear_distance = 18, loop = false, gain = 0.4})
self.object:settexturemod("^[colorize:#c4000099")
self.can_punch = false
self.object:set_animation({x = self.anim.walk_START, y = self.anim.walk_END}, 35, 0)
self.npc_anim = creatures.ANIM_WALK
minetest.after(0.4, function()
self.can_punch = true
self.object:settexturemod("")
self.npc_anim = ""
end)
end
function z_init_visuals(self)
local prop = {
mesh = z_mesh,
textures = z_texture,
}
self.object:set_properties(prop)
end
ZOMBIE_DEF = {
physical = true,
collisionbox = {-0.25, -1, -0.3, 0.25, 0.75, 0.3},
visual = "mesh",
visual_size = {x=1, y=1},
mesh = z_mesh,
textures = z_texture,
makes_footstep_sound = true,
npc_anim = 0,
lifetime = 0,
timer = 0,
turn_timer = 0,
vec = 0,
yaw = 0,
yawwer = 0,
state = 1,
can_punch = true,
dead = false,
jump_timer = 0,
last_pos = {x=0,y=0,z=0},
punch_timer = 0,
sound_timer = 0,
attacker = "",
attacking_timer = 0,
mob_name = "zombie"
}
ZOMBIE_DEF.get_staticdata = function(self)
return minetest.serialize({
itemstring = self.itemstring,
timer = self.timer,
lifetime = self.lifetime,
})
end
ZOMBIE_DEF.on_activate = function(self, staticdata, dtime_s)
z_init_visuals(self)
self.anim = z_get_animations()
self.object:set_animation({x=self.anim.stand_START,y=self.anim.stand_END}, z_animation_speed, 0)
self.npc_anim = creatures.ANIM_STAND
self.object:setacceleration({x=0,y=-20,z=0})
self.state = 1
self.object:set_hp(z_hp)
self.object:set_armor_groups({fleshy=130})
self.last_pos = {x=0,y=0,z=0}
self.can_punch = true
self.dead = false
self.lifetime = 0
if staticdata then
local tmp = minetest.deserialize(staticdata)
if tmp and tmp.timer then
self.timer = tmp.timer
end
if tmp and tmp.lifetime ~= nil then
self.lifetime = tmp.lifetime
end
end
end
ZOMBIE_DEF.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
if not self.can_punch then return end
local my_pos = self.object:getpos()
if puncher ~= nil then
self.attacker = puncher
if time_from_last_punch >= 0.45 then
z_hit(self)
local v = self.object:getvelocity()
self.direction = {x=v.x, y=v.y, z=v.z}
self.punch_timer = 0
self.object:setvelocity({x=dir.x*z_chillaxin_speed,y=5,z=dir.z*z_chillaxin_speed})
if self.state == 1 then
self.state = 8
elseif self.state >= 2 then
self.state = 9
end
-- add wear to sword/tool
creatures.add_wear(puncher, tool_capabilities)
end
end
if self.object:get_hp() < 1 then
creatures.drop(my_pos, {{name=z_drop, count=math.random(0,2)}}, dir)
end
end
ZOMBIE_DEF.on_step = function(self, dtime)
if self.dead then return end
self.timer = self.timer + 0.01
self.lifetime = self.lifetime + 0.01
self.turn_timer = self.turn_timer + 0.01
self.jump_timer = self.jump_timer + 0.01
self.punch_timer = self.punch_timer + 0.01
self.attacking_timer = self.attacking_timer + 0.01
self.sound_timer = self.sound_timer + 0.01
local current_pos = self.object:getpos()
local current_node = minetest.env:get_node_or_nil(current_pos)
if self.time_passed == nil then
self.time_passed = 0
end
-- death
if self.object:get_hp() < 1 then
self.object:setvelocity({x=0,y=-20,z=0})
self.object:set_hp(0)
self.attacker = ""
self.state = 0
self.dead = true
minetest.sound_play(z_sound_dead, {pos = current_pos, max_hear_distance = 10, gain = 0.9})
self.object:set_animation({x=self.anim.lay_START,y=self.anim.lay_END}, z_animation_speed, 0)
minetest.after(1, function()
self.object:remove()
if self.object:get_hp() < 1 and creatures.drop_on_death then
creatures.drop(current_pos, {{name=z_drop, count=math.random(0,2)}})
end
end)
end
-- die if old
if self.lifetime > z_life_max then
self.object:set_hp(0)
self.state = 0
self.dead = true
self.object:remove()
return
end
-- die when in water, lava or sunlight
local wtime = minetest.env:get_timeofday()
local ll = minetest.env:get_node_light({x=current_pos.x,y=current_pos.y+1,z=current_pos.z}) or 0
local nn = nil
if current_node ~= nil then nn = current_node.name end
if nn ~= nil and nn == "default:water_source" or
nn == "default:water_flowing" or
nn == "default:lava_source" or
nn == "default:lava_flowing" or
(wtime > 0.2 and wtime < 0.805 and current_pos.y > 0 and ll > 11) then
self.sound_timer = self.sound_timer + dtime
if self.sound_timer >= 0.8 then
local damage = 5
if ll > 11 then damage = 2 end
self.sound_timer = 0
self.object:set_hp(self.object:get_hp()-damage)
z_hit(self)
end
else
self.time_passed = 0
end
-- update moving state every 0.5 or 1 second
if self.state < 3 then
if self.timer > 0.2 then
if self.attacker == "" then
self.state = math.random(1,2)
else self.state = 5 end
self.timer = 0
end
end
-- play random sound
if self.sound_timer > math.random(5,35) then
minetest.sound_play(z_sound_normal, {pos = current_pos, max_hear_distance = 18, gain = 0.7})
self.sound_timer = 0
end
-- after knocked back
if self.state >= 8 then
if self.punch_timer > 0.15 then
if self.state == 9 then
self.object:setvelocity({x=self.direction.x*z_chillaxin_speed,y=-20,z=self.direction.z*z_chillaxin_speed})
self.state = 2
elseif self.state == 8 then
self.object:setvelocity({x=0,y=-20,z=0})
self.state = 1
end
end
end
--STANDING
if self.state == 1 then
self.yawwer = true
self.attacker = ""
-- seach for players
for _,object in ipairs(minetest.env:get_objects_inside_radius(current_pos, z_player_radius)) do
if object:is_player() then
self.yawwer = false
NPC = current_pos
PLAYER = object:getpos()
self.vec = {x=PLAYER.x-NPC.x, y=PLAYER.y-NPC.y, z=PLAYER.z-NPC.z}
self.yaw = math.atan(self.vec.z/self.vec.x)+math.pi^2
if PLAYER.x > NPC.x then
self.yaw = self.yaw + math.pi
end
self.yaw = self.yaw - 2
self.object:setyaw(self.yaw)
self.attacker = object
end
end
if self.attacker == "" and self.turn_timer > math.random(1,4) then
self.yaw = 360 * math.random()
self.object:setyaw(self.yaw)
self.turn_timer = 0
self.direction = {x = math.sin(self.yaw)*-1, y = -20, z = math.cos(self.yaw)}
end
self.object:setvelocity({x=0,y=self.object:getvelocity().y,z=0})
if self.npc_anim ~= creatures.ANIM_STAND then
self.anim = z_get_animations()
self.object:set_animation({x=self.anim.stand_START,y=self.anim.stand_END}, z_animation_speed, 0)
self.npc_anim = creatures.ANIM_STAND
end
if self.attacker ~= "" then
self.direction = {x = math.sin(self.yaw)*-1, y = -20, z = math.cos(self.yaw)}
self.state = 2
end
end
--UPDATE DIR
if self.state == 5 then
self.yawwer = true
self.attacker = ""
-- seach for players
for _,object in ipairs(minetest.env:get_objects_inside_radius(current_pos, z_player_radius)) do
if object:is_player() then
self.yawwer = false
NPC = current_pos
PLAYER = object:getpos()
self.vec = {x=PLAYER.x-NPC.x, y=PLAYER.y-NPC.y, z=PLAYER.z-NPC.z}
self.yaw = math.atan(self.vec.z/self.vec.x)+math.pi^2
if PLAYER.x > NPC.x then
self.yaw = self.yaw + math.pi
end
self.yaw = self.yaw - 2
self.object:setyaw(self.yaw)
self.attacker = object
end
end
if self.attacker ~= "" then
self.direction = {x = math.sin(self.yaw)*-1, y = -20, z = math.cos(self.yaw)}
self.state = 2
else
self.state =1
end
end
-- WALKING
if self.state == 2 then
if self.attacker ~= "" then
self.direction = {x=math.sin(self.yaw)*-1, y=-20, z=math.cos(self.yaw)}
end
if self.direction ~= nil then
self.object:setvelocity({x=self.direction.x*z_chillaxin_speed,y=self.object:getvelocity().y,z=self.direction.z*z_chillaxin_speed})
end
if self.turn_timer > math.random(1,4) and not self.attacker then
self.yaw = 360 * math.random()
self.object:setyaw(self.yaw)
self.turn_timer = 0
self.direction = {x=math.sin(self.yaw)*-1, y=-20, z=math.cos(self.yaw)}
end
if self.npc_anim ~= creatures.ANIM_WALK then
self.npc_anim = creatures.ANIM_WALK
self.object:set_animation({x=self.anim.walk_START,y=self.anim.walk_END}, z_animation_speed, 0)
end
--jump
local p = current_pos
p.y = p.y-0.5
creatures.jump(self, p, 7.4, 0.25)
if self.attacker ~= "" and minetest.setting_getbool("enable_damage") then
local s = current_pos
local attacker_pos = self.attacker:getpos() or nil
if attacker_pos == nil then return end
local p = attacker_pos
if (s ~= nil and p ~= nil) then
local dist = ((p.x-s.x)^2 + (p.y-s.y)^2 + (p.z-s.z)^2)^0.5
creatures.attack(self, current_pos, attacker_pos, dist, z_hit_radius)
end
end
end
end
minetest.register_entity("creatures:zombie", ZOMBIE_DEF)

View File

@ -53,7 +53,7 @@ function default.register_ores()
ore_type = "blob",
ore = "default:clay",
wherein = {"default:sand"},
clust_scarcity = 16 * 16 * 16,
clust_scarcity = 26 * 26 * 26,
clust_size = 5,
y_min = -15,
y_max = 0,
@ -75,7 +75,7 @@ function default.register_ores()
ore = "default:sand",
wherein = {"default:stone", "default:sandstone",
"default:desert_stone"},
clust_scarcity = 16 * 16 * 16,
clust_scarcity = 26 * 26 * 26,
clust_size = 5,
y_min = -31,
y_max = 0,
@ -96,7 +96,7 @@ function default.register_ores()
ore_type = "blob",
ore = "default:dirt",
wherein = {"default:stone"},
clust_scarcity = 16 * 16 * 16,
clust_scarcity = 36 * 36 * 36,
clust_size = 5,
y_min = -31,
y_max = 31000,
@ -117,7 +117,7 @@ function default.register_ores()
ore_type = "blob",
ore = "default:gravel",
wherein = {"default:stone"},
clust_scarcity = 16 * 16 * 16,
clust_scarcity = 36 * 36 * 36,
clust_size = 5,
y_min = -31000,
y_max = 31000,
@ -178,7 +178,7 @@ function default.register_ores()
clust_scarcity = 9 * 9 * 9,
clust_num_ores = 12,
clust_size = 3,
y_min = 1025,
y_min = 125,
y_max = 31000,
})
@ -213,7 +213,7 @@ function default.register_ores()
clust_scarcity = 9 * 9 * 9,
clust_num_ores = 5,
clust_size = 3,
y_min = 1025,
y_min = 125,
y_max = 31000,
})
@ -283,7 +283,7 @@ function default.register_ores()
clust_scarcity = 14 * 14 * 14,
clust_num_ores = 5,
clust_size = 3,
y_min = 1025,
y_min = 125,
y_max = 31000,
})
@ -318,7 +318,7 @@ function default.register_ores()
clust_scarcity = 15 * 15 * 15,
clust_num_ores = 4,
clust_size = 3,
y_min = 1025,
y_min = 155,
y_max = 31000,
})
@ -353,7 +353,7 @@ function default.register_ores()
clust_scarcity = 36 * 36 * 36,
clust_num_ores = 3,
clust_size = 2,
y_min = 1025,
y_min = 125,
y_max = 31000,
})

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 459 B

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 348 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 251 B

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 B

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 471 B

After

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 B

After

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 B

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 B

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 B

After

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 B

After

Width:  |  Height:  |  Size: 607 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 423 B

After

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 B

After

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 375 B

After

Width:  |  Height:  |  Size: 709 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 B

After

Width:  |  Height:  |  Size: 627 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 B

After

Width:  |  Height:  |  Size: 496 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 B

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 B

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 B

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 B

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 B

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 359 B

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 B

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 B

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 B

After

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 B

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 552 B

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 B

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 B

After

Width:  |  Height:  |  Size: 913 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 B

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 B

After

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 B

After

Width:  |  Height:  |  Size: 803 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 826 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 B

After

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 B

After

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 483 B

After

Width:  |  Height:  |  Size: 906 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 B

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 B

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 794 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 779 B

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 B

After

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 B

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 B

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 417 B

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 B

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 736 B

After

Width:  |  Height:  |  Size: 758 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 823 B

After

Width:  |  Height:  |  Size: 834 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 B

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

Some files were not shown because too many files have changed in this diff Show More