diff --git a/api.lua b/api.lua index b515e51..8a924a0 100644 --- a/api.lua +++ b/api.lua @@ -14,9 +14,9 @@ vehicles.air_rfac = 0.3 -- air resistance factor --------------------------------------------------------------------------- --- Nested Dynamic Tables +-- Dynamic Tables --------------------------------------------------------------------------- -vehicles.showed_seats_fspecs = {} -- pair: ["playername"] and objectref +vehicles.showed_fspecs = {} -- pair: ["playername"] and {self, formname, [invname, invlist]} vehicles.ngroups_friction_coefs = { -- friction coefficients of some groups of nodes. ["sand"] = 1.5, @@ -42,191 +42,204 @@ end base_props and wheel_props should contain def props for entity ('obj') and its item ('item') ]] vehicles.register_vehicle = function(name, base_props, wheel_props) - minetest.register_entity(MOD_NAME .. ":" .. name .. "_base", { - vehicle_type = base_props.obj.vehicle_type, - physical = true, - mass = base_props.obj.mass or 2000, -- in kgs - collide_with_objects = true, - collisionbox = base_props.obj.bounding_box or {-0.5, 0.0, -0.5, 0.5, 1.0, 0.5}, - selectionbox = base_props.obj.bounding_box or {-0.5, 0.0, -0.5, 0.5, 1.0, 0.5}, - visual = base_props.obj.visual, - visual_size = base_props.obj.visual_size or {x=1, y=1, z=1}, - mesh = (base_props.obj.visual == "mesh" and base_props.obj.mesh), - textures = base_props.obj.textures or {""}, - use_texture_alpha = base_props.obj.use_texture_alpha, - seats = base_props.obj.seats, -- table fields: {["is_busy"] = playername, [dplayer_obj] = ObjectRef, ["pos"] = position, ["rot"] = rotation, ["getout_coords"] = coords from seat, ["type"] = ("driver" or "passenger"), ["model"] = } - --ctrl_vals = base_props.ctrl_vals, -- table fields: {["move"] = float (acc_len), ["turn"] = float (degs)} - traction_force = base_props.obj.traction_force or 5000, -- in Neutons - wheels = base_props.obj.wheels, -- table fields: {["type"] = ("front" or "rear"), ["pos"] = position, ["rot"] = rotation, ["radius"] = wheel radius} - max_speed = base_props.obj.max_speed or 25000, -- in m/s - stepheight = base_props.obj.stepheight or 0.5, - on_activate = function(self, staticdata, dtime_s) - self.seats = table.copy(base_props.obj.seats) - local vehpos = self.object:get_pos() - for i, seat in ipairs(self.seats) do - seat.rot = seat.rot or {x=0, y=0, z=0} - seat.radius = seat.radius or 0.5 - end - self.wheels = {} - self.move_dir = 0 -- specifies direction of the vehicle movement (1 is forward, -1 is backward) - self.tracf = 0 -- specifies direction of the traction force and contains its value (> 0 is forward, < 0 is backward, =0 is stay) - - --[[if base_props.obj.player_model_def then - self.player_models = {} - if base_props.obj.player_model_def.driver then - player_api.register_model(base_props.obj.player_model_def.driver.name, table.derive_elems(base_props.obj.player_model_def.driver, 2, #base_props.obj.player_model_def.driver)) - self.player_models.driver = base_props.obj.player_model_def.driver.name + minetest.register_entity(MOD_NAME .. ":" .. name .. "_base", { + vehicle_type = base_props.obj.vehicle_type, + physical = true, + mass = base_props.obj.mass or 2000, -- in kgs + collide_with_objects = true, + collisionbox = base_props.obj.bounding_box or {-0.5, 0.0, -0.5, 0.5, 1.0, 0.5}, + selectionbox = base_props.obj.bounding_box or {-0.5, 0.0, -0.5, 0.5, 1.0, 0.5}, + visual = base_props.obj.visual, + visual_size = base_props.obj.visual_size or {x=1, y=1, z=1}, + mesh = (base_props.obj.visual == "mesh" and base_props.obj.mesh), + textures = base_props.obj.textures or {""}, + use_texture_alpha = base_props.obj.use_texture_alpha, + seats = base_props.obj.seats, -- table fields: {["is_busy"] = playername, [dplayer_obj] = ObjectRef, ["pos"] = position, ["rot"] = rotation, ["getout_coords"] = coords from seat, ["type"] = ("driver" or "passenger"), ["model"] = } + --ctrl_vals = base_props.ctrl_vals, -- table fields: {["move"] = float (acc_len), ["turn"] = float (degs)} + traction_force = base_props.obj.traction_force or 5000, -- in Neutons + trunk_inv = base_props.obj.trunk_inv + wheels = base_props.obj.wheels, -- table fields: {["type"] = ("front" or "rear"), ["pos"] = position, ["rot"] = rotation, ["radius"] = wheel radius} + max_speed = base_props.obj.max_speed or 25000, -- in m/s + compartments_edge = base_props.obj.compartments_edge -- rough edge between the base body and the trunk along local Z-axis (num) + stepheight = base_props.obj.stepheight or 0.5, + on_activate = function(self, staticdata, dtime_s) + self.seats = table.copy(base_props.obj.seats) + local vehpos = self.object:get_pos() + for i, seat in ipairs(self.seats) do + seat.rot = seat.rot or {x=0, y=0, z=0} + seat.radius = seat.radius or 0.5 end - if base_props.obj.player_model_def.passenger then - player_api.register_model(base_props.obj.player_model_def.passenger.name, table.derive_elems(base_props.obj.player_model_def.passenger, 2, #base_props.obj.player_model_def.passenger)) - self.player_models.passenger = base_props.obj.player_model_def.passenger.name - end - end]] - local vehrot = self.object:get_rotation() - for i, whl in ipairs(base_props.obj.wheels) do - local whl_obj = minetest.add_entity({x=vehpos.x+whl.pos.x, y=vehpos.y+whl.pos.y, z=vehpos.z+whl.pos.z}, MOD_NAME .. ":" .. name .. "_wheel") - whl_obj:set_attach(self.object, "", whl.pos, whl.rot or {0, 0, 0}) - self.wheels[i] = {object=whl_obj, type=whl.type, pos=whl.pos, rot=whl.rot or {0, 0, 0}, radius=whl.radius} - end - vehicles.set_gravity(self) - --self.acc_v2d = vehicles.v2d_coords(base_props.traction_force/base_props.mass, vehrot.y) -- keep own vehicle 2d acceleration along horizontal plane that doesn`t depend to external impacts - end, - on_step = function(self, dtime) - local drv_name = self.seats[vehicles.get_driver_i(self)].is_busy - if drv_name then - local player = minetest.get_player_by_name(drv_name) - local ctrls = player:get_player_control() - if ctrls.up then - minetest.debug("Going forward...") - self.move_dir = 1 - self.tracf = 1 - end - if ctrls.down then - minetest.debug("Going backward...") - self.move_dir = -1 - self.tracf = -1 - end - if not ctrls.up and not ctrls.down then - minetest.debug("Stopping...") - self.tracf = 0 - end - --[[if not ctrls.up or not ctrls.down then - self.tracf = 0 - end]] - end - - local v3d_v = self.object:get_velocity() - local v2d_v = {x=v3d_v.x, y=0, z=v3d_v.z} - --minetest.debug("yaw: " .. self.object:get_yaw()) - --minetest.debug("velocity: " .. dump(v3d_v)) - local v2d_vl = vehicles.v2d_length(v2d_v) - if v2d_vl < 0.4 and v2d_vl > 0 and self.tracf == 0 then - minetest.debug("Stopped!") - self.move_dir = 0 - end + self.wheels = {} + self.move_dir = 0 -- specifies direction of the vehicle movement (1 is forward, -1 is backward) + self.tracf = 0 -- specifies direction of the traction force and contains its value (> 0 is forward, < 0 is backward, =0 is stay) + + -- Create detached inventory of the vehicle trunk + if base_props.obj.trunk_inv then + local tr_inv = base_props.obj.trunk_inv + self.trunk_inv = #self.trunk_inv > 0 and self.trunk_inv or {} + minetest.create_detached_inventory(tr_inv.name, { + allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) + return count + end, + allow_put = function(inv, listname, index, stack, player) + return stack:get_count() + end, + allow_take = function(inv, listname, index, stack, player) + return stack:get_count() + end + }) - local yaw = self.object:get_yaw() - local entity_def = minetest.registered_entities[self.name] - local tdir_v = vehicles.v2d_coords(self.tracf*entity_def.traction_force, yaw) - tdir_v.y = 0 - local max_fcoef = vehicles.max_fric_coef(self) - local unit_v2d_v = vector.normalize(v2d_v) - unit_v2d_v = vehicles.check_for_nan(unit_v2d_v) - - --local tdir_sign = vehicles.get_sign(self.tracf_dir) - local sf_fforce = (math.abs(vehicles.gravity)*entity_def.mass)*max_fcoef*(-self.move_dir) -- surface friction force - local sf_fforce_v = vector.multiply(unit_v2d_v, sf_fforce) - sf_fforce_v = vehicles.check_for_nan(sf_fforce_v) - local air_rforce = v2d_vl^2 * vehicles.air_rfac * (-self.move_dir) -- air resistance force - local air_rforce_v = vector.multiply(unit_v2d_v, air_rforce) - air_rforce_v = vehicles.check_for_nan(air_rforce_v) - if self.move_dir == 0 then - minetest.debug("tdir_v: " .. dump(tdir_v)) - minetest.debug("sf_fforce_v: " .. dump(sf_fforce_v)) - minetest.debug("air_rforce_v: " .. dump(air_rforce_v)) - end - --[[local sf_fforce_sign = vehicles.get_sign(sf_fforce) - if tdir_sign == sf_fforce_sign then - sf_fforce = -sf_fforce - air_rforce = -air_rforce - end]] - --[[if self.tracf_dir ~= 0 then - minetest.debug("self.tracf_dir: " .. self.tracf_dir) - minetest.debug("sf_fforce: " .. sf_fforce) - minetest.debug("air_rforce: " .. air_rforce) - end]] - --[[minetest.debug("tdir_v: " .. dump(tdir_v)) - minetest.debug("sf_fforce: " .. sf_fforce) - minetest.debug("sf_fforce_v: " .. dump(sf_fforce_v)) - minetest.debug("air_rforce: " .. dump(air_rforce)) - minetest.debug("air_rforce_v: " .. dump(air_rforce_v)) - minetest.debug("tdir_v+sf_fforce_v: " .. dump(vector.add(tdir_v, sf_fforce_v))) - minetest.debug("(tdir_v+sf_fforce_v)+air_rforce_v: " .. dump(vector.add(vector.add(tdir_v, sf_fforce_v), air_rforce_v))) - minetest.debug("((tdir_v+sf_fforce_v)+air_rforce_v)/edef.mass: " .. dump(vector.divide(vector.add(vector.add(tdir_v, sf_fforce_v), air_rforce_v), edef.mass)))]] - local acc = vector.divide(vector.add(vector.add(tdir_v, sf_fforce_v), air_rforce_v), entity_def.mass) - acc.y = vehicles.gravity - --local acc_sum = (self.tracf_dir + sf_fforce + air_rforce)/edef.mass - --minetest.debug(acc_sum) - --local acc2d = vehicles.v2d_coords(acc_sum, self.object:get_yaw()) - --minetest.debug(dump(acc2d)) - --[[v2d_vl = (sf_fforce_sign < 0 and -v2d_vl or v2d_vl) - local new_vel = vehicles.v2d_coords(v2d_vl, self.object:get_yaw()) - new_vel.y = v2d_v.y - self.object:set_velocity(new_vel)]] - self.object:set_acceleration(acc) - --self.object:set_acceleration({x=acc2d.x, y=self.object:get_acceleration().y, z=acc2d.z}) - - for i, d in ipairs(self.wheels) do - d.object:set_rotation({x=vehicles.calc_angle_vel(vehicles.v2d_length(self.object:get_velocity()), d.radius)*dtime, y=0, z=0}) - --minetest.debug("wheels rotation: " .. dump(d.object:get_rotation())) - end - - end, - on_death = function(self, killer) - for i, sdata in ipairs(self.seats) do - if sdata.is_busy then - vehicles.get_out(self, sdata.dplayer_obj:get_luaentity(), i) + local inv = minetest.get_inventory({type="detached", name=tr_inv.name}) + inv:set_size(tr_inv.listname, tr_inv.listsize.w*tr_inv.listsize.h) + inv:set_list(tr_inv.listname, self.trunk_inv) end - end - for n, obj in pairs(vehicles.showed_seats_fspecs) do - if obj == self.object then - vehicles.close_seats_formspec(self, n, MOD_NAME .. ":vehicle_seats") - end - end - for i, whl in ipairs(self.wheels) do - whl.object:remove() - end - end, - on_rightclick = function(self, clicker) - vehicles.show_seats_formspec(self, MOD_NAME .. ":vehicle_seats", clicker:get_player_name()) - end + + local vehrot = self.object:get_rotation() + for i, whl in ipairs(base_props.obj.wheels) do + local whl_obj = minetest.add_entity({x=vehpos.x+whl.pos.x, y=vehpos.y+whl.pos.y, z=vehpos.z+whl.pos.z}, MOD_NAME .. ":" .. name .. "_wheel") + whl_obj:set_attach(self.object, "", whl.pos, whl.rot or {0, 0, 0}) + self.wheels[i] = {object=whl_obj, type=whl.type, pos=whl.pos, rot=whl.rot or {0, 0, 0}, radius=whl.radius} + end + vehicles.set_gravity(self) + end, + on_step = function(self, dtime) + local drv_name = self.seats[vehicles.get_driver_i(self)].is_busy + if drv_name then + local player = minetest.get_player_by_name(drv_name) + local ctrls = player:get_player_control() + if ctrls.up then + minetest.debug("Going forward...") + self.move_dir = 1 + self.tracf = 1 + end + if ctrls.down then + minetest.debug("Going backward...") + self.move_dir = -1 + self.tracf = -1 + end + if not ctrls.up and not ctrls.down then + minetest.debug("Stopping...") + self.tracf = 0 + end + end + + local v3d_v = self.object:get_velocity() + local v2d_v = {x=v3d_v.x, y=0, z=v3d_v.z} + --minetest.debug("yaw: " .. self.object:get_yaw()) + --minetest.debug("velocity: " .. dump(v3d_v)) + local v2d_vl = vehicles.v2d_length(v2d_v) + if v2d_vl < 0.4 and v2d_vl > 0 and self.tracf == 0 then + minetest.debug("Stopped!") + self.move_dir = 0 + end + + local yaw = self.object:get_yaw() + local entity_def = minetest.registered_entities[self.name] + local tdir_v = vehicles.v2d_coords(self.tracf*entity_def.traction_force, yaw) + tdir_v.y = 0 + local max_fcoef = vehicles.max_fric_coef(self) + local unit_v2d_v = vector.normalize(v2d_v) + unit_v2d_v = vehicles.check_for_nan(unit_v2d_v) + + local sf_fforce = (math.abs(vehicles.gravity)*entity_def.mass)*max_fcoef*(-self.move_dir) -- surface friction force + local sf_fforce_v = vector.multiply(unit_v2d_v, sf_fforce) + sf_fforce_v = vehicles.check_for_nan(sf_fforce_v) + local air_rforce = v2d_vl^2 * vehicles.air_rfac * (-self.move_dir) -- air resistance force + local air_rforce_v = vector.multiply(unit_v2d_v, air_rforce) + air_rforce_v = vehicles.check_for_nan(air_rforce_v) + if self.move_dir == 0 then + minetest.debug("tdir_v: " .. dump(tdir_v)) + minetest.debug("sf_fforce_v: " .. dump(sf_fforce_v)) + minetest.debug("air_rforce_v: " .. dump(air_rforce_v)) + end + + --[[if self.tracf_dir ~= 0 then + minetest.debug("self.tracf_dir: " .. self.tracf_dir) + minetest.debug("sf_fforce: " .. sf_fforce) + minetest.debug("air_rforce: " .. air_rforce) + end]] + --[[minetest.debug("tdir_v: " .. dump(tdir_v)) + minetest.debug("sf_fforce: " .. sf_fforce) + minetest.debug("sf_fforce_v: " .. dump(sf_fforce_v)) + minetest.debug("air_rforce: " .. dump(air_rforce)) + minetest.debug("air_rforce_v: " .. dump(air_rforce_v)) + minetest.debug("tdir_v+sf_fforce_v: " .. dump(vector.add(tdir_v, sf_fforce_v))) + minetest.debug("(tdir_v+sf_fforce_v)+air_rforce_v: " .. dump(vector.add(vector.add(tdir_v, sf_fforce_v), air_rforce_v))) + minetest.debug("((tdir_v+sf_fforce_v)+air_rforce_v)/edef.mass: " .. dump(vector.divide(vector.add(vector.add(tdir_v, sf_fforce_v), air_rforce_v), edef.mass)))]] + local acc = vector.divide(vector.add(vector.add(tdir_v, sf_fforce_v), air_rforce_v), entity_def.mass) + acc.y = vehicles.gravity + + self.object:set_acceleration(acc}) + + for i, d in ipairs(self.wheels) do + d.object:set_rotation({x=vehicles.calc_angle_vel(vehicles.v2d_length(self.object:get_velocity()), d.radius)*dtime, y=0, z=0}) + --minetest.debug("wheels rotation: " .. dump(d.object:get_rotation())) + end + + end, + on_death = function(self, killer) + for pn, f in pairs(self.showed_fspecs) do + vehicles.close_compartments_menu(self, pn) + end + for i, sdata in ipairs(self.seats) do + if sdata.is_busy then + vehicles.get_out(self, sdata.dplayer_obj:get_luaentity(), i) + end + end + for i, whl in ipairs(self.wheels) do + whl.object:remove() + end + end, + on_rightclick = function(self, clicker) + local vel = self.object:get_velocity() + if vector.length(vel) ~= 0 then + minetest.chat_send_player(clicker:get_player_name(), "The vehicle needs to be stopped at first!") + return + end + + local is_player_sit = vehicles.is_player_sit(clicker) + if is_player_sit then + vehicles.show_cabin_formspec(self, clicker:get_player_name()) + else + vehicles.show_compartments_menu(self, clicker:get_player_name()) + end + --[[local pos = self.object:get_pos() + local range = minetest.registered_items[""].range + local ray_vec = vector.new(0, 0, range) + ray_vec = vector.rotate(ray_vec, {x=clicker:get_look_vertical(), y=clicker:get_look_horizontal(), z=0}) + local ray = minetest.raycast(pos, vehicles.convert_pos_to_absolute(pos, ray_vec), true) + + for pointed_thing in ray do + if pointed_thing.ref == self.object then + local compart_edge = minetest.registered_entities[self.name].compartments_edge + vehicles.show_seats_formspec(self, MOD_NAME .. ":vehicle_seats", clicker:get_player_name())]] + end - }) + }) minetest.register_craftitem(MOD_NAME .. ":" .. name .. "_base", { - description = base_props.item.description or "", - inventory_image = base_props.item.inv_image or "", - on_place = function(itemstack, placer, pointed_thing) - if pointed_thing.type =="node" and pointed_thing.above.y >= pointed_thing.under.y then - minetest.add_entity(pointed_thing.above, itemstack:get_name()) - end + description = base_props.item.description or "", + inventory_image = base_props.item.inv_image or "", + on_place = function(itemstack, placer, pointed_thing) + if pointed_thing.type =="node" and pointed_thing.above.y >= pointed_thing.under.y then + minetest.add_entity(pointed_thing.above, itemstack:get_name()) end + end }) minetest.register_entity(MOD_NAME .. ":" .. name .. "_wheel", { - visual_size = wheel_props.obj.visual_size or {x=1, y=1, z=1}, - pointable = false, - visual = wheel_props.obj.visual, - mesh = (wheel_props.obj.visual == "mesh" and wheel_props.obj.mesh), - textures = wheel_props.obj.textures or {""}, - use_texture_alpha = wheel_props.obj.use_texture_alpha - + visual_size = wheel_props.obj.visual_size or {x=1, y=1, z=1}, + pointable = false, + visual = wheel_props.obj.visual, + mesh = (wheel_props.obj.visual == "mesh" and wheel_props.obj.mesh), + textures = wheel_props.obj.textures or {""}, + use_texture_alpha = wheel_props.obj.use_texture_alpha }) minetest.register_craftitem(MOD_NAME .. ":" .. name .. "_wheel", { - description = wheel_props.item.description or "", - inventory_image = wheel_props.item.inventory_image or "" + description = wheel_props.item.description or "", + inventory_image = wheel_props.item.inventory_image or "" }) if base_props.obj.player_mdefs then @@ -238,7 +251,18 @@ vehicles.register_vehicle = function(name, base_props, wheel_props) end end -vehicles.show_seats_formspec = function(self, formspec_name, playername) +vehicles.show_compartments_menu = function(self, playername) + local formspec = "formspec_version[3]size[6,7.5]label[2.5,0.5;Open:]button[1,1.5;4,2;seats_cabin;Seats Cabin]" + local trunk = minetest.registered_entities[self.name].trunk_inv + if trunk then + formspec = formspec .. "button[1,4.5;4,2;trunk;Trunk]" + end + + vehicles.showed_fspecs[playername] = {self=self, formname=MOD_NAME .. ":compartments_menu"} + minetest.show_formspec(playername, MOD_NAME .. ":compartments_menu", formspec) +end + +vehicles.show_cabin_formspec = function(self, playername) local seats_n = #self.seats local sbut_size = {x=4, y=1.5} local pad = 0.5 @@ -269,105 +293,74 @@ vehicles.show_seats_formspec = function(self, formspec_name, playername) (is_player_sit and "button[" .. wsize_x/2 - sbut_size.x/2 .. "," .. (wsize_y - (2.5/4*3)) .. ";4,1.5;get_up;" .. minetest.formspec_escape("Get up") .. "]" or "") - vehicles.showed_seats_fspecs[playername] = self.object - minetest.show_formspec(playername, formspec_name, formspec) + vehicles.showed_fspecs[playername] = {self=self, formname=MOD_NAME .. ":seats_cabin"} + minetest.show_formspec(playername, MOD_NAME .. ":seats_cabin", formspec) end ---[[vehicles.show_seats_formspec = function(self, formspec_name, playername) - local seats_n = #self.seats - local min_sbut_s = {x=1, y=0.5} -- min seat button size - local max_sbut_s = {x=4, y=2} - local buts_space_w = 0.3 -- space button size doesn`t depend on buttons` sizes - local buts_space_h = 0.2 - local w_sbut, h_sbut = 0.5, 0.5 -- distance between window edge and button doesn`t depend on buttons` sizes +vehicles.show_trunk_formspec = function(self, playername, invname, listname, inv_size) + local pad = 0.5 + local slots_sp = 0.3 + local wsize_x = pad*2 + slots_sp*(inv_size.w-1) + inv_size.w + local wsize_y = pad*2 + slots_sp*(inv_size.h-1) + inv_size.h + 0.7 + 4 + 3*slots_sp + local formspec = "formspec_version[3]size[" .. wsize_x .. "," .. wsize_y .. "]" .. + "list[detached:" .. listname .. ";" .. pad .. "," .. pad .. ";" .. inv_size.w .. "," .. inv_size.h .. "]" .. + "list[current_player;main;" .. pad .. "," .. (wsize_y - (4+3+slots_sp+pad)) .. ";8,4]" - local sizedowns_n = math.ceil(math.sqrt(seats_n)) -- number of button sizedowns - local but_new_x_s = max_sbut_s.x-0.5*sizedowns_n - local but_new_y_s = max_sbut_s.y-0.5*sizedowns_n - - local wsize_chx = 0 - local wsize_chy = 0 - if but_new_x_s < min_sbut_s.x or but_new_y_s < min_sbut_s.y then - but_new_x_s = min_sbut_s.x - but_new_y_s = min_sbut_s.y - - wsize_chx = 1.5*sizedowns_n-(max_sbut_s.x-min_sbut_s.x) - wsize_chy = 1.5*sizedowns_n-(max_sbut_s.y-min_sbut_s.y) + vehicles.showed_fspecs[playername] = {self=self, formname=MOD_NAME .. ":trunk", invname=invname, listname=listname} + minetest.show_formspec(playername, MOD_NAME .. ":trunk", formspec) +end + +vehicles.close_compartments_menu = function(self, playername, cabin_action) + local fd = vehicles.showed_fspecs[playername] + if fd.formname == MOD_NAME .. ":seats_cabin" then + vehicles.close_seats_formspec(self, playername, fd.formname, cabin_action, vehicles.is_player_sit(minetest.get_player_by_name(playername))) + elseif fd.formname == MOD_NAME .. ":trunk" then + vehicles.close_trunk_formspec(self, playername, fd.formname, fd.invname, fd.listname) end - local wsize_x = sizedowns_n*but_new_x_s + 2*w_sbut + (sizedowns_n-1)*buts_space_w + wsize_chx - local wsize_y = sizedowns_n*but_new_y_s + 2*h_sbut + (sizedowns_n-1)*buts_space_h + wsize_chy + 2 -- '+ 2' is a space for label text - local is_player_sit = vehicles.is_player_sit(minetest.get_player_by_name(playername)) - wsize_y = is_player_sit and wsize_y + 2 or wsize_y - - local formspec = "formspec_version[3]size[" .. wsize_x .. ", " .. wsize_y .. "]label[" .. wsize_x/4 .. ",1;" .. minetest.formspec_escape("Select a seat to sit down or press to get up:") .. "]" - - local bpos_x = w_sbut - local bpos_y = 2 - - for i = 1, seats_n do - minetest.debug(i) - bpos_x = ((i <= sizedowns_n or i > (math.pow(sizedowns_n, 2)-sizedowns_n)) and bpos_x + w_sbut) or bpos_x + buts_space_w - bpos_y = ((i%sizedowns_n == 0) and bpos_y + h_sbut) or (((i-1)%sizedowns_n == 0) and 2 + h_sbut) or bpos_y + buts_space_h + minetest.close_formspec(playername, MOD_NAME .. ":compartments_menu") + vehicles.showed_fspecs[playername] = nil +end + +-- 'seat_id' is optional +-- 'action' is supposed what to do with the player after closing (sit or go out off the vehicle) [optional] +vehicles.close_seats_formspec = function(self, playername, formname, action, seat_id) + local player = minetest.get_player_by_name(playername) + if action == "sit" then + minetest.debug("close_seats_formspec") + local is_busy = self.seats[seat_id].is_busy + if is_busy then return end + vehicles.sit(self, player, seat_id) + elseif action == "get_up" then + minetest.debug(dump(self.seats[seat_id].dplayer_obj)) + vehicles.get_out(self, self.seats[seat_id].dplayer_obj:get_luaentity(), seat_id) + local getout_coords = self.seats[seat_id].getout_coords + local yaw = self.object:get_yaw() + local pos = self.object:get_pos() + local player = minetest.get_player_by_name(playername) + local rel_getout_pos = vector.rotate(vector.add(self.seats[seat_id].pos, getout_coords), {x=0, y=self.object:get_yaw(), z=0}) + minetest.debug(dump(pos)) + minetest.debug(dump(rel_getout_pos)) + player:set_pos(vehicles.convert_pos_to_absolute(pos, rel_getout_pos)) + local rand_look = {-90, 90} + player:set_look_horizontal(yaw+rand_look[math.random(1, 2)]) - local i_busy = self.seats[i].is_busy - formspec = formspec .. ((i_busy and "style[seat_" .. i .. ";bgcolor=#FF0000]") or "") - formspec = formspec .. "button[" .. - bpos_x .. "," .. bpos_y .. ";" .. - but_new_x_s .. "," .. but_new_y_s .. - ";seat_" .. i .. ";" .. minetest.formspec_escape("#" .. i .. ((i_busy and "\n" .. playername) or "")) .. "]" - - bpos_x = (i ~= 1 and (i-1)%sizedowns_n == 0 and bpos_x + but_new_x_s) or bpos_x - bpos_y = bpos_y + but_new_y_s - - bpos_x = i == (math.pow(sizedowns_n, 2)-sizedowns_n+1) and bpos_x + w_sbut or (i-1)%sizedowns_n == 0 and bpos_x + buts_space_w - bpos_y = i%sizedowns_n == 0 and 2 or bpos_y + buts_space_h + end + + minetest.close_formspec(playername, formname) + vehicles.showed_fspecs[playername] = nil +end + +vehicles.close_trunk_formspec = function(self, playername, formname, inv_name, list_name) + local self = self.object:get_luaentity() + if self then + local inv = minetest.get_inventory(type="detached", name=inv_name) + self.trunk_inv = inv:get_list(list_name) end - formspec = (is_player_sit and (formspec .. "button[" .. - wsize_x/2 - ((but_new_x_s+2)/2) .. "," .. bpos_y .. ";" .. - but_new_x_s+2 .. "," .. wsize_y - bpos_y - (buts_space_h + 0.5 + h_sbut) .. - ";get_up;" .. minetest.formspec_escape("Get up") .. "]")) or formspec - - - showed_seats_fspecs[playername] = self.object - minetest.show_formspec(playername, formspec_name, formspec) - - - -end]] ---[[vehicles.show_seats_formspec = function(self, formspec_name, playername) - local seats_n = #self.seats - --minetest.debug(seats_n) - local buts_space_w = 0.2 - local buts_space_h = 0.3 -- space between buttons along width and height - local int = math.modf(seats_n/8) - local columns_n = (seats_n % 8 == 0 and int) or int + 1 - local max_col_cells_n = (seats_n >= 8 and 8) or seats_n - local w_sbut, h_sbut = 2, 0.5 - local fspec_size_w = columns_n * w_sbut + (columns_n + 1) * buts_space_w - local fspec_size_h = max_col_cells_n * h_sbut + (max_col_cells_n+1) * buts_space_h + 2 -- + 2 is a space for label text - local is_player_sit = vehicles.is_player_sit(minetest.get_player_by_name(playername)) - fspec_size_h = (is_player_sit and fspec_size_h + 2) or fspec_size_h -- if player sits the vehicle, heighten the formspec window still at 2 units - local formspec = "size[" .. fspec_size_w .. ", " .. fspec_size_h .. "]label[2,1;" .. minetest.formspec_escape("Select a vacant seat inside the vehicle below:") .. "]" - - local bpos_x = buts_space_w - minetest.debug(bpos_x) - local bpos_y = buts_space_h+2 - for i = 1, seats_n do - formspec = formspec .. "button[" .. - tostring(bpos_x) .. "," .. bpos_y .. ";" .. - w_sbut .. "," .. h_sbut .. - ";seat_" .. i .. ";" .. minetest.formspec_escape("Seat #" .. i) .. "]" - bpos_x = ((i - 1) % 8 == 0) and bpos_x + w_sbut + buts_space_w - bpos_y = (((i - 1) % 8 == 0) and buts_space_h+2) or bpos_y + h_sbut + buts_space_h - end - - formspec = (is_player_sit and (formspec .. "button[" .. fspec_size_w / 2 - w_sbut / 2 .. ", " .. fspec_size_h - 1.5 .. ";" .. w_sbut .. ", " .. h_sbut .. ";get_out;" .. minetest.formspec_escape("Get out") .. "]")) or formspec - --minetest.debug(formspec) - showed_seats_fspecs[playername] = self.object - minetest.show_formspec(playername, formspec_name, formspec) -end]] + minetest.close_formspec(playername, formname) + vehicles.showed_fspecs[playername] = nil +end vehicles.sit = function(self, player, seat_id) -- seat_id is an id of a seat table of 'self.seats' local sel_seat = self.seats[seat_id] @@ -403,24 +396,6 @@ vehicles.sit = function(self, player, seat_id) -- seat_id is an id of a seat dplayer_self.last_visual_size = player:get_properties().visual_size player:set_properties({visual_size = vector.new(0, 0, 0)}) player:set_look_horizontal(self.object:get_yaw()+180) - --local anim = player_api.get_animation(player) - --[[local pl_appear = minetest.registered_entities[self.name].player_sit_appearance - local pl_data = {} - if pl_appear then - local old_mesh = player:get_properties().mesh - local old_anim = table.pack(player:get_animation()) - if sel_seat.type == "driver" and pl_appear.driver then - pl_data.prev_model = old_mesh - pl_data.prev_anim = old_anim - dummy_player:set_properties({mesh=pl_appear.driver.mesh}) - dummy_player:set_animation(table.unpack(pl_appear.driver.animation)) - elseif sel_seat.type == "passenger" and pl_appear.passenger then - pl_data.prev_model = old_mesh - pl_data.prev_anim = old_anim - dummy_player:set_properties({mesh=pl_appear.passenger}) - dummy_player:set_animation(table.unpack(pl_appear.passenger.animation)) - end - end]] end vehicles.get_out = function(self, dplayer, seat_id) @@ -435,24 +410,22 @@ vehicles.get_out = function(self, dplayer, seat_id) dplayer.object:remove() self.seats[seat_id].is_busy = nil self.seats[seat_id].dplayer_obj = nil - --[[ if pl_data.prev_model then - player_api.set_model(player, pl_data.prev_model) - end - if pl_data.prev_anim then - player_api.set_animation(player, pl_data.prev_anim) - end - local seat = self.seats[pl_data.seat_id] - seat.is_busy = nil - plmeta:set_string("prev_data", "")]] + end -vehicles.on_formspec_event = function(player, formname, fields) - if formname ~= MOD_NAME .. ":vehicle_seats" then +vehicles.on_formspec_event = function(player, formname, fields) + local plname = player:get_player_name() + local self = vehicles.showed_fspecs[player:get_player_name()].self + if formname == MOD_NAME .. ":context_menu" then + if fields.quit then + vehicles.close_compartments_menu(self, plname) + elseif fields. + if formname ~= MOD_NAME .. ":vehicle_seats" or formname ~= MOD_NAME .. ":vehicle_trunk" then return end local plname = player:get_player_name() - local obj = vehicles.showed_seats_fspecs[plname] + local obj = vehicles.showed_vehicle_fspecs[plname] local self = obj:get_luaentity() if self then for i, sdata in ipairs(self.seats) do @@ -469,72 +442,27 @@ vehicles.on_formspec_event = function(player, formname, fields) end if fields.quit then - vehicles.close_seats_formspec(self, plname, formname) - return true + if formname == MOD_NAME .. ":vehicle_seats" then + vehicles.close_seats_formspec(self, plname, formname) + return true + elseif formname == MOD_NAME .. ":vehicle_trunk" then + local edef = minetest.registered_entities[self.name] + vehicles.close_trunk_formspec(self, plname, formname, edef.trunk.name, edef.trunk.listname) + return true + end end else -- supposed that vehicle is died while the player is viewing the formspec - vehicles.close_seats_formspec(self, plname, formname) + if formname == MOD_NAME .. ":vehicle_seats" then + vehicles.close_seats_formspec(self, plname, formname) + elseif formname == MOD_NAME .. ":vehicle_trunk" then + vehicles.close_trunk_formspec(self, plname, formname) + end end end --- Returns traction force along according direction or nil if no player as a driver or any driving keys are not pressed ---[[vehicles.on_move = function(self) - local drv_name = self.seats[vehicles.get_driver_i(self)].is_busy - if not drv_name then return end - local player = minetest.get_player_by_name(drv_name) - local ctrls = player:get_player_control() - local entity_def = minetest.registered_entities[self.name] - local v2d_vl = vehicles.v2d_length(self.object:get_velocity()) - if ctrls.up then - self.tracf_dir = entity_def.traction_force - else - self.tracf_dir = 0 - end - if ctrls.down then - self.tracf_dir = -entity_def.traction_force - elseif not ctrls.down and not ctrls.up then - self.tracf_dir = 0 - end - - if not ctrls.up or not ctrls.down then - self.tracf_dir = 0 - end - -end]] - --[[if ctrls.up and vehicles.v2d_length(self.acc_v2d) == 0 then - local new_acc = vector.add(acc, {x=self.acc_v2d.x, y=0, z=self.acc_v2d.z}) - self.object:set_acceleration(new_acc) - else - self.acc_v2d = {x=0, z=0} - end - - if ctrls.down and vehicles.v2d_length(self.acc_v2d) == 0 then - local new_acc = vector.add(acc, {x=-self.acc_v2d.x, y=0, z=-self.acc_v2d.z}) - self.object:set_acceleration(new_acc) - else - self.acc_v2d = {x=0, z=0} - end]] - --[[ local acc_len = vector.length(acc) - if acc_len <= ctrl_vals.up then - new_acc.x = acc.x - new_acc.z = acc.z - local v_and_a_codir = vehicles.are_horiz_codirectional(acc, vel) - if type(v_and_a_codir) == "number" then]] ---[[vehicles.force_brake = function(self) - local max_fcoef = vehicles.max_fric_coef(self) - local edef = minetest.registered_entities[self.name] - local fric_force = vehicles.friction_force(max_fcoef, edef.mass, math.abs(self.object:get_acceleration().y)) - local acc = self.object:get_acceleration() - local tracforce = edef.mass*vehicles.v2d_length({x=acc.x, z=acc.z}) - local new_acc_len = (tracforce-fric_force)/edef.mass - local new_acc_coords = vector - - local brake_vec = vehicles.get_acc_vect(vehicles.v2d_length({x=acc.x, z=acc.z})/max_fcoef, acc.y, self.object:get_yaw()+math.rad(180))]] - - -------‐--------------------------------------------------------------------- -- Helper functions @@ -573,62 +501,26 @@ vehicles.is_player_sit = function(player) local self = obj:get_attach():get_luaentity() for i, sdata in pairs(self.seats) do - if sdata.is_busy == player:get_player_name() then + if sdata.is_busy == player:get_player_name() then return i - end + end end return false end --- 'seat_id' is optional --- 'action' is supposed what to do with the player after closing (sit or go out off the vehicle) [optional] -vehicles.close_seats_formspec = function(self, playername, formname, action, seat_id) - local player = minetest.get_player_by_name(playername) - if action == "sit" then - minetest.debug("close_seats_formspec") - local is_busy = self.seats[seat_id].is_busy - if is_busy then return end - vehicles.sit(self, player, seat_id) - elseif action == "get_up" then - minetest.debug(dump(self.seats[seat_id].dplayer_obj)) - vehicles.get_out(self, self.seats[seat_id].dplayer_obj:get_luaentity(), seat_id) - local getout_coords = self.seats[seat_id].getout_coords - local yaw = self.object:get_yaw() - local pos = self.object:get_pos() - local player = minetest.get_player_by_name(playername) - local rel_getout_pos = vector.rotate(vector.add(self.seats[seat_id].pos, getout_coords), {x=0, y=self.object:get_yaw(), z=0}) - minetest.debug(dump(pos)) - minetest.debug(dump(rel_getout_pos)) - player:set_pos(vehicles.convert_pos_to_absolute(pos, rel_getout_pos)) - local rand_look = {-90, 90} - player:set_look_horizontal(yaw+rand_look[math.random(1, 2)]) - - end - - minetest.close_formspec(playername, formname) - vehicles.showed_seats_fspecs[playername] = nil -end - + -- Returns index in seats table where driver data is. vehicles.get_driver_i = function(self) if not self then return end for i, sdata in ipairs(self.seats) do - if sdata.type == "driver" then - return i - end + if sdata.type == "driver" then + return i + end end return end --- checks if given vectors are co-directional along the horizontal plane; returns true, if they are, otherwise angle between them ---[[vehicles.are_horiz_codirectional = function(acc, vel) - local vec1 = vector.new(acc.x, 0, acc.z) - local vec2 = vector.new(vel.x, 0, vel.z) - local angle = math.deg(vector.angle(vec1, vec2)) - - return (angle == 0) or (angle -end]] -- Get sign of 'n' value vehicles.get_sign = function(n) @@ -643,7 +535,6 @@ vehicles.calc_angle_vel = function(vel_l, radius) return vel_l end ---vehicles.rotate_acc_vect = function(old_acc, turn_ang) -- Calculates new (relative) coords of the acceleration vector vehicles.v2d_coords = function(a_len, yaw) local x = -a_len * math.sin(yaw) diff --git a/legacy_api.lua b/legacy_api.lua new file mode 100644 index 0000000..06417d0 --- /dev/null +++ b/legacy_api.lua @@ -0,0 +1,176 @@ +-- OLD OR UNUSED CODE FRAGMENTS FROM api.lua + +--[[ +on_rightclick + local pos = self.object:get_pos() + local range = minetest.registered_items[""].range + local ray_vec = vector.new(0, 0, range) + ray_vec = vector.rotate(ray_vec, {x=clicker:get_look_vertical(), y=clicker:get_look_horizontal(), z=0}) + local ray = minetest.raycast(pos, vehicles.convert_pos_to_absolute(pos, ray_vec), true) + + for pointed_thing in ray do + if pointed_thing.ref == self.object then + local compart_edge = minetest.registered_entities[self.name].compartments_edge + vehicles.show_seats_formspec(self, MOD_NAME .. ":vehicle_seats", clicker:get_player_name())]] + +--[[vehicles.show_cabin_formspec = function(self, formspec_name, playername) + local seats_n = #self.seats + local min_sbut_s = {x=1, y=0.5} -- min seat button size + local max_sbut_s = {x=4, y=2} + local buts_space_w = 0.3 -- space button size doesn`t depend on buttons` sizes + local buts_space_h = 0.2 + local w_sbut, h_sbut = 0.5, 0.5 -- distance between window edge and button doesn`t depend on buttons` sizes + + local sizedowns_n = math.ceil(math.sqrt(seats_n)) -- number of button sizedowns + local but_new_x_s = max_sbut_s.x-0.5*sizedowns_n + local but_new_y_s = max_sbut_s.y-0.5*sizedowns_n + + local wsize_chx = 0 + local wsize_chy = 0 + if but_new_x_s < min_sbut_s.x or but_new_y_s < min_sbut_s.y then + but_new_x_s = min_sbut_s.x + but_new_y_s = min_sbut_s.y + + wsize_chx = 1.5*sizedowns_n-(max_sbut_s.x-min_sbut_s.x) + wsize_chy = 1.5*sizedowns_n-(max_sbut_s.y-min_sbut_s.y) + end + + local wsize_x = sizedowns_n*but_new_x_s + 2*w_sbut + (sizedowns_n-1)*buts_space_w + wsize_chx + local wsize_y = sizedowns_n*but_new_y_s + 2*h_sbut + (sizedowns_n-1)*buts_space_h + wsize_chy + 2 -- '+ 2' is a space for label text + local is_player_sit = vehicles.is_player_sit(minetest.get_player_by_name(playername)) + wsize_y = is_player_sit and wsize_y + 2 or wsize_y + + local formspec = "formspec_version[3]size[" .. wsize_x .. ", " .. wsize_y .. "]label[" .. wsize_x/4 .. ",1;" .. minetest.formspec_escape("Select a seat to sit down or press to get up:") .. "]" + + local bpos_x = w_sbut + local bpos_y = 2 + + for i = 1, seats_n do + minetest.debug(i) + bpos_x = ((i <= sizedowns_n or i > (math.pow(sizedowns_n, 2)-sizedowns_n)) and bpos_x + w_sbut) or bpos_x + buts_space_w + bpos_y = ((i%sizedowns_n == 0) and bpos_y + h_sbut) or (((i-1)%sizedowns_n == 0) and 2 + h_sbut) or bpos_y + buts_space_h + + local i_busy = self.seats[i].is_busy + formspec = formspec .. ((i_busy and "style[seat_" .. i .. ";bgcolor=#FF0000]") or "") + formspec = formspec .. "button[" .. + bpos_x .. "," .. bpos_y .. ";" .. + but_new_x_s .. "," .. but_new_y_s .. + ";seat_" .. i .. ";" .. minetest.formspec_escape("#" .. i .. ((i_busy and "\n" .. playername) or "")) .. "]" + + bpos_x = (i ~= 1 and (i-1)%sizedowns_n == 0 and bpos_x + but_new_x_s) or bpos_x + bpos_y = bpos_y + but_new_y_s + + bpos_x = i == (math.pow(sizedowns_n, 2)-sizedowns_n+1) and bpos_x + w_sbut or (i-1)%sizedowns_n == 0 and bpos_x + buts_space_w + bpos_y = i%sizedowns_n == 0 and 2 or bpos_y + buts_space_h + end + + formspec = (is_player_sit and (formspec .. "button[" .. + wsize_x/2 - ((but_new_x_s+2)/2) .. "," .. bpos_y .. ";" .. + but_new_x_s+2 .. "," .. wsize_y - bpos_y - (buts_space_h + 0.5 + h_sbut) .. + ";get_up;" .. minetest.formspec_escape("Get up") .. "]")) or formspec + + + showed_seats_fspecs[playername] = self.object + minetest.show_formspec(playername, formspec_name, formspec) + + + +end]] +--[[vehicles.show_seats_formspec = function(self, formspec_name, playername) + local seats_n = #self.seats + --minetest.debug(seats_n) + local buts_space_w = 0.2 + local buts_space_h = 0.3 -- space between buttons along width and height + local int = math.modf(seats_n/8) + local columns_n = (seats_n % 8 == 0 and int) or int + 1 + local max_col_cells_n = (seats_n >= 8 and 8) or seats_n + local w_sbut, h_sbut = 2, 0.5 + local fspec_size_w = columns_n * w_sbut + (columns_n + 1) * buts_space_w + local fspec_size_h = max_col_cells_n * h_sbut + (max_col_cells_n+1) * buts_space_h + 2 -- + 2 is a space for label text + local is_player_sit = vehicles.is_player_sit(minetest.get_player_by_name(playername)) + fspec_size_h = (is_player_sit and fspec_size_h + 2) or fspec_size_h -- if player sits the vehicle, heighten the formspec window still at 2 units + local formspec = "size[" .. fspec_size_w .. ", " .. fspec_size_h .. "]label[2,1;" .. minetest.formspec_escape("Select a vacant seat inside the vehicle below:") .. "]" + + local bpos_x = buts_space_w + minetest.debug(bpos_x) + local bpos_y = buts_space_h+2 + for i = 1, seats_n do + formspec = formspec .. "button[" .. + tostring(bpos_x) .. "," .. bpos_y .. ";" .. + w_sbut .. "," .. h_sbut .. + ";seat_" .. i .. ";" .. minetest.formspec_escape("Seat #" .. i) .. "]" + bpos_x = ((i - 1) % 8 == 0) and bpos_x + w_sbut + buts_space_w + bpos_y = (((i - 1) % 8 == 0) and buts_space_h+2) or bpos_y + h_sbut + buts_space_h + end + + formspec = (is_player_sit and (formspec .. "button[" .. fspec_size_w / 2 - w_sbut / 2 .. ", " .. fspec_size_h - 1.5 .. ";" .. w_sbut .. ", " .. h_sbut .. ";get_out;" .. minetest.formspec_escape("Get out") .. "]")) or formspec + --minetest.debug(formspec) + showed_seats_fspecs[playername] = self.object + minetest.show_formspec(playername, formspec_name, formspec) +end]] + + +-- Returns traction force along according direction or nil if no player as a driver or any driving keys are not pressed +--[[vehicles.on_move = function(self) + local drv_name = self.seats[vehicles.get_driver_i(self)].is_busy + if not drv_name then return end + local player = minetest.get_player_by_name(drv_name) + local ctrls = player:get_player_control() + local entity_def = minetest.registered_entities[self.name] + local v2d_vl = vehicles.v2d_length(self.object:get_velocity()) + if ctrls.up then + self.tracf_dir = entity_def.traction_force + else + self.tracf_dir = 0 + end + if ctrls.down then + self.tracf_dir = -entity_def.traction_force + elseif not ctrls.down and not ctrls.up then + self.tracf_dir = 0 + end + + if not ctrls.up or not ctrls.down then + self.tracf_dir = 0 + end + +end]] + --[[if ctrls.up and vehicles.v2d_length(self.acc_v2d) == 0 then + local new_acc = vector.add(acc, {x=self.acc_v2d.x, y=0, z=self.acc_v2d.z}) + self.object:set_acceleration(new_acc) + else + self.acc_v2d = {x=0, z=0} + end + + if ctrls.down and vehicles.v2d_length(self.acc_v2d) == 0 then + local new_acc = vector.add(acc, {x=-self.acc_v2d.x, y=0, z=-self.acc_v2d.z}) + self.object:set_acceleration(new_acc) + else + self.acc_v2d = {x=0, z=0} + end]] + --[[ local acc_len = vector.length(acc) + if acc_len <= ctrl_vals.up then + new_acc.x = acc.x + new_acc.z = acc.z + local v_and_a_codir = vehicles.are_horiz_codirectional(acc, vel) + if type(v_and_a_codir) == "number" then]] + +--[[vehicles.force_brake = function(self) + local max_fcoef = vehicles.max_fric_coef(self) + local edef = minetest.registered_entities[self.name] + local fric_force = vehicles.friction_force(max_fcoef, edef.mass, math.abs(self.object:get_acceleration().y)) + local acc = self.object:get_acceleration() + local tracforce = edef.mass*vehicles.v2d_length({x=acc.x, z=acc.z}) + local new_acc_len = (tracforce-fric_force)/edef.mass + local new_acc_coords = vector + + local brake_vec = vehicles.get_acc_vect(vehicles.v2d_length({x=acc.x, z=acc.z})/max_fcoef, acc.y, self.object:get_yaw()+math.rad(180))]] + + +-- checks if given vectors are co-directional along the horizontal plane; returns true, if they are, otherwise angle between them +--[[vehicles.are_horiz_codirectional = function(acc, vel) + local vec1 = vector.new(acc.x, 0, acc.z) + local vec2 = vector.new(vel.x, 0, vel.z) + local angle = math.deg(vector.angle(vec1, vec2)) + + return (angle == 0) or (angle +end]] diff --git a/models/bmw_fw_open_trunk.b3d b/models/bmw_fw_open_trunk.b3d new file mode 100644 index 0000000..d119782 Binary files /dev/null and b/models/bmw_fw_open_trunk.b3d differ diff --git a/models/bmw_fw_open_trunk.blend b/models/bmw_fw_open_trunk.blend new file mode 100644 index 0000000..86255b9 Binary files /dev/null and b/models/bmw_fw_open_trunk.blend differ diff --git a/models/bmw_fw_open_trunk.blend1 b/models/bmw_fw_open_trunk.blend1 new file mode 100644 index 0000000..9bc46aa Binary files /dev/null and b/models/bmw_fw_open_trunk.blend1 differ