155 lines
5.1 KiB
Lua
155 lines
5.1 KiB
Lua
gunslinger.penetrate = {} --table for putting in items that bullets can penetrate, and how much speed the bullet will be reduced by. usage: gunslinger.penetrate["default:glass"] = 3
|
|
gunslinger.penetrate["default:glass"] = 4
|
|
gunslinger.penetrate["default:wood"] = 12
|
|
local bullets = {}
|
|
local timer = 0
|
|
local TICK = .05 --NOTE: I think my math is wrong somewhere, changing this value changes how the bullet behaves, so change only if you know what you are doing
|
|
local DEBUG = false
|
|
local force = false
|
|
minetest.register_entity("gunslinger:bullet", {
|
|
hp_max = 1,
|
|
bullet = {},
|
|
physical = false,
|
|
pointable = false,
|
|
collide_with_objects = false,
|
|
-- weight = 5,
|
|
collisionbox = {0,0,0, 0,0,0},
|
|
visual = "sprite",
|
|
visual_size = {x=.1, y=.1},
|
|
textures = {"gunslinger_bullet.png"}, -- number of required textures depends on visual
|
|
spritediv = {x=1, y=1},
|
|
initial_sprite_basepos = {x=0, y=0},
|
|
is_visible = true,
|
|
makes_footstep_sound = false,
|
|
automatic_rotate = false,
|
|
on_activate = function(self, staticdata, dtime_s)
|
|
minetest.after(TICK, function()
|
|
if not self.bullet then
|
|
self.object:remove()
|
|
end
|
|
end)
|
|
end,
|
|
})
|
|
function gunslinger.firebullet(player, pos, velocity, drag, def)
|
|
if not pos or not velocity or not drag or not def then return end
|
|
local bullet = {}
|
|
bullet.player = player
|
|
bullet.pos = pos
|
|
bullet.v = velocity
|
|
bullet.drag = drag
|
|
bullet.time = 0
|
|
bullet.start = os.clock()
|
|
bullet.force = true
|
|
bullet.def = def
|
|
bullet.entity = minetest.add_entity(pos, "gunslinger:bullet")
|
|
bullet.entity:set_properties({bullet = bullet})
|
|
table.insert(bullets, bullet)
|
|
force = true
|
|
end
|
|
minetest.register_globalstep(function(dtime)
|
|
timer = timer + dtime;
|
|
if timer >= TICK or force then
|
|
for id, bullet in pairs(bullets) do
|
|
--local t1 = os.clock()
|
|
if force and not bullet.force then goto continue else bullet.force = false end
|
|
local time = timer
|
|
if bullet.start then
|
|
bullet.time = os.clock() - bullet.start
|
|
time = bullet.time
|
|
else
|
|
bullet.time = bullet.time + timer
|
|
end
|
|
bullet.start = nil
|
|
local speed = vector.length(bullet.v)
|
|
local resistance = {}
|
|
resistance.y = -.001*bullet.drag * (bullet.v.y * speed);
|
|
resistance.x = -.001*bullet.drag * (bullet.v.x * speed);
|
|
resistance.z = -.001*bullet.drag * (bullet.v.z * speed);
|
|
bullet.v = vector.add(bullet.v, resistance)
|
|
bullet.v.y = bullet.v.y - (9.81*time)
|
|
local dir = vector.normalize(bullet.v)
|
|
if bullet.entity then
|
|
bullet.entity:set_pos(bullet.pos)
|
|
else
|
|
bullet.entity = minetest.add_entity(pos, "gunslinger:bullet")
|
|
bullet.entity.bullet = bullet
|
|
end
|
|
if bullet.entity then
|
|
bullet.entity:setvelocity(bullet.v)
|
|
end
|
|
local pos2 = vector.add(bullet.pos, vector.multiply(bullet.v, time))
|
|
local ray = minetest.raycast(bullet.pos, pos2, true, true)
|
|
if DEBUG then
|
|
minetest.add_particle({
|
|
pos = bullet.pos,
|
|
expirationtime = 10,
|
|
size = 4,
|
|
texture = "gunslinger_debug.png"
|
|
})
|
|
end
|
|
for pointed in ray do
|
|
if pointed and pointed.ref and pointed.ref == bullet.player then
|
|
goto next
|
|
end
|
|
local speed = vector.length(bullet.v)
|
|
if pointed.intersection_point and pointed.type == "node" then
|
|
if bullet.lastunder and vector.equals(bullet.lastunder, pointed.under) then goto next end
|
|
if DEBUG then
|
|
minetest.add_particle({
|
|
pos = vector.subtract(pointed.intersection_point, vector.divide(dir, 50)),
|
|
expirationtime = 10,
|
|
size = 6,
|
|
texture = "gunslinger_debug.png",
|
|
vertical = true
|
|
})
|
|
else
|
|
minetest.add_particle({
|
|
pos = vector.subtract(pointed.intersection_point, vector.divide(dir, 50)),
|
|
expirationtime = 10,
|
|
size = 2,
|
|
texture = "gunslinger_decal.png",
|
|
vertical = true
|
|
})
|
|
end
|
|
local name = minetest.get_node(pointed.under).name
|
|
if name and gunslinger.penetrate[name] and speed/gunslinger.penetrate[name] > 10 then
|
|
local factor = gunslinger.penetrate[name]
|
|
bullet.lastunder = pointed.under
|
|
pos2 = pointed.intersection_point
|
|
force = true
|
|
bullet.force = true
|
|
bullet.v = vector.add(bullet.v, {x=math.random(-1*factor,factor), y=math.random(-1*factor,factor), z=math.random(-1*factor,factor)})
|
|
bullet.v = vector.divide(bullet.v, factor)
|
|
else
|
|
bullet.entity:remove()
|
|
table.remove(bullets, id)
|
|
goto continue
|
|
end
|
|
end
|
|
if pointed.type == "object" then
|
|
local target = pointed.ref
|
|
local point = pointed.intersection_point
|
|
local speedmod = (speed+100)/((bullet.def.speed or 600)+100)
|
|
local dmg = bullet.def.base_dmg * bullet.def.dmg_mult * speedmod
|
|
|
|
-- Add 50% damage if headshot
|
|
if point.y > target:get_pos().y + 1.5 then
|
|
dmg = dmg * 1.5
|
|
end
|
|
|
|
target:punch(bullet.player, nil, {damage_groups={fleshy=dmg}})
|
|
end
|
|
::next::
|
|
end
|
|
if bullet.time > 10 or vector.length(bullet.v)<10 then bullet.entity:remove() table.remove(bullets, id) end
|
|
--minetest.log("error", (string.format("elapsed time: %.2fms", (os.clock() - t1) * 1000)))
|
|
bullet.pos = pos2
|
|
::continue::
|
|
end
|
|
if not force then
|
|
timer = 0
|
|
else
|
|
force = false
|
|
end
|
|
end
|
|
end) |