local v_new = vector.new local v_rotate_around_axis = vector.rotate_around_axis local v_multiply = vector.multiply local v_dot = vector.dot local modstorage = core.get_mod_storage() yl_snowball.modstorage = modstorage local get_player_participation = yl_snowball.get_player_participation yl_snowball.COOLDOWN = 0.4 -- TODO make configurable yl_snowball.SNOWBALL_SPEED = 17 -- 17 was good yl_snowball.SNOWBALL_GRAVITY = -8 -- -8 was good local player_last_throw_time = {} core.register_on_leaveplayer( function(player, _timed_out) local player_name = player:get_player_name() if player_name ~= "" then player_last_throw_time[player_name] = nil end end ) local function spawn_snow_puff(pos, normal) local shift = normal * 0.1 local center = pos + shift local def = { pos = center, amount = 20, time = 0.01, radius = { min = 0.01, max = 0.1, bias = -1 }, drag = 0.1, attract = { kind = "point", strength = -14, origin = center - shift, }, acc = v_new(0, -8, 0), exptime = 0.5, size = 2, collisiondetection = true, collision_removal = true, texture = { name = "yl_snowball_snowflake.png", --blend = "clip", -- does not work in 5.9?!? }, --glow = 2, } core.add_particlespawner(def) end local function get_start_params(player) local look = player:get_look_dir() local eye_height = v_new(0, player:get_properties().eye_height, 0) local eye_offset = player:get_eye_offset() * 0.1 -- Eye offset is in scaled coords local yaw = player:get_look_horizontal() local dir = v_multiply(look, 0.4) -- forward shift. TODO tweak constant local p_pos = player:get_pos() local up = v_new(0,1,0) -- Initial projectile position: in front of camera, shifted little forward local start = p_pos + eye_height + v_rotate_around_axis(eye_offset, up, yaw) + dir local player_vel = futil.get_velocity(player) -- vector pointing right local right = v_rotate_around_axis(v_new(1,0,0), up, yaw) -- horizonal velocity component local horiz_vel = v_multiply(right, v_dot(player_vel, right)) -- Make projectile velocity influenced only by forward/back and up/down movement. -- This makes you always shoot "straight" local vel = player_vel - horiz_vel -- vizlib.draw_line(p_pos, p_pos + v_multiply(player_vel,1.0)) -- velocity -- vizlib.draw_line(p_pos, p_pos + v_multiply(look,3), {color = "green"}) -- look dir -- vizlib.draw_line(p_pos, p_pos + v_multiply(vel,1.0), {color = "yellow"}) -- projectile vel return start, look, vel end -- FIXME make this local yl_snowball.on_use = function(itemstack, user, pointed_thing) if (not user) or (not user:is_player()) then return itemstack end local player = user local player_name = player:get_player_name() local current_time = core.get_us_time() --core.get_gametime() -- gametime seems to "lag"? local diff = current_time - (player_last_throw_time[player_name] or 0) if diff < yl_snowball.COOLDOWN * 1000000 then -- too soon, don't shot, don't take items return nil end player_last_throw_time[player_name] = current_time local speed = yl_snowball.SNOWBALL_SPEED local gravity = yl_snowball.SNOWBALL_GRAVITY local start, look, p_vel = get_start_params(player) -- projectile velocity points where we look + affected by player movement local vel = (look * speed) + (p_vel * 0.3) ballistics.shoot( "yl_snowball:snowball_ent", start, vel, v_new(0, gravity, 0), player, nil ) yl_snowball.snowgame.register_shot(player_name) if not core.check_player_privs(player, "creative") then itemstack:take_item(1) end return itemstack end core.register_craftitem( "yl_snowball:snowball", { description = "Throwable Snowball", stack_max= 99, inventory_image = "yl_snowball.png", on_use = function(...) -- FIXME remove wrapper return yl_snowball.on_use(...) end, } ) local SIZE = 0.4 local SNOWBALL_DEF = { parameters = { punch = { tool_capabilities = { damage_groups = { fleshy = 1.0 }, }, }, particles = { amount = 1, time = 0.1, --time = 4, texture = "yl_snowball_snowflake.png", glow = 1, minvel = { x = 0, y = -0.1, z = 0 }, maxvel = { x = 0, y = -0.1, z = 0 }, minacc = { x = 0, y = -0.1, z = 0 }, maxacc = { x = 0, y = -0.1, z = 0 }, minexptime = 0.5, maxexptime = 0.5, minsize = 2, maxsize = 2, --_period = 0.09, _period = 0.2, }, hit_sound = { spec = { name = "default_place_node" }, parameters = { gain = 0.8, pitch = 2.0 }, }, }, physical = false, -- FIXME I think enabling this will allow jumping on them? visual = "sprite", visual_size = {x=SIZE, y=SIZE}, textures = {'yl_snowball.png'}, collisionbox = {-SIZE/2, -SIZE/2, -SIZE/2, SIZE/2, SIZE/2, SIZE/2}, on_step = function(self, dtime, moveresult) -- draw a snowflake trail ballistics.on_step_particles(self, dtime, moveresult) end, on_hit_node = function(self, node_pos, node, above_pos, intersection_point, intersection_normal, box_id) ballistics.on_hit_node_hit_sound_play(self, node_pos, node, above_pos, intersection_point, intersection_normal, box_id) self.object:remove() spawn_snow_puff(intersection_point, intersection_normal) if y_bows and y_bows.util.bullseye_was_hit(self, node_pos, node, above_pos, intersection_point, intersection_normal, box_id) then y_bows.on_bullseye_hit(node_pos, self._source_obj) end return true end, on_hit_object = function(self, target, intersection_point, intersection_normal, box_id) spawn_snow_puff(intersection_point, intersection_normal) ballistics.on_hit_object_hit_sound_play(self, target, intersection_point, intersection_normal, box_id) self.object:remove() if futil.is_player(target) then local target_name = target:get_player_name() local source = self._source_obj if futil.is_player(source) then local source_name = source:get_player_name() local source_participates = get_player_participation(source_name) local target_participates = get_player_participation(target_name) -- register hit and play sounds only if both participate if source_participates and target_participates then yl_snowball.snowgame.register_hit(source_name, target_name) local sound_name = "y_bows_arrow_successful_hit" core.sound_play({ name = sound_name }, { gain = 0.7, to_player = source_name, }) local take_hit_sound_name = "default_snow_footstep" core.sound_play({ name = take_hit_sound_name }, { gain = 0.7, to_player = target_name, }) end end else ballistics.on_hit_object_punch(self, target, intersection_point, intersection_normal, box_id) end return true end } ballistics.register_projectile("yl_snowball:snowball_ent", SNOWBALL_DEF) core.register_craft({ type = "shapeless", output = 'yl_snowball:snowball 3', recipe = {'default:snow'}, })