forked from luanti-org/minetest_game
		
	Carts: Move entity definition to seperate file
This commit is contained in:
		
							
								
								
									
										384
									
								
								mods/carts/cart_entity.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										384
									
								
								mods/carts/cart_entity.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,384 @@ | ||||
| local cart_entity = { | ||||
| 	physical = false, -- otherwise going uphill breaks | ||||
| 	collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, | ||||
| 	visual = "mesh", | ||||
| 	mesh = "carts_cart.b3d", | ||||
| 	visual_size = {x=1, y=1}, | ||||
| 	textures = {"carts_cart.png"}, | ||||
|  | ||||
| 	driver = nil, | ||||
| 	punched = false, -- used to re-send velocity and position | ||||
| 	velocity = {x=0, y=0, z=0}, -- only used on punch | ||||
| 	old_dir = {x=1, y=0, z=0}, -- random value to start the cart on punch | ||||
| 	old_pos = nil, | ||||
| 	old_switch = 0, | ||||
| 	railtype = nil, | ||||
| 	attached_items = {} | ||||
| } | ||||
|  | ||||
| function cart_entity:on_rightclick(clicker) | ||||
| 	if not clicker or not clicker:is_player() then | ||||
| 		return | ||||
| 	end | ||||
| 	local player_name = clicker:get_player_name() | ||||
| 	if self.driver and player_name == self.driver then | ||||
| 		self.driver = nil | ||||
| 		carts:manage_attachment(clicker, nil) | ||||
| 	elseif not self.driver then | ||||
| 		self.driver = player_name | ||||
| 		carts:manage_attachment(clicker, self.object) | ||||
| 	end | ||||
| end | ||||
|  | ||||
| function cart_entity:on_activate(staticdata, dtime_s) | ||||
| 	self.object:set_armor_groups({immortal=1}) | ||||
| 	if string.sub(staticdata, 1, string.len("return")) ~= "return" then | ||||
| 		return | ||||
| 	end | ||||
| 	local data = minetest.deserialize(staticdata) | ||||
| 	if not data or type(data) ~= "table" then | ||||
| 		return | ||||
| 	end | ||||
| 	self.railtype = data.railtype | ||||
| 	if data.old_dir then | ||||
| 		self.old_dir = data.old_dir | ||||
| 	end | ||||
| 	if data.old_vel then | ||||
| 		self.old_vel = data.old_vel | ||||
| 	end | ||||
| end | ||||
|  | ||||
| function cart_entity:get_staticdata() | ||||
| 	return minetest.serialize({ | ||||
| 		railtype = self.railtype, | ||||
| 		old_dir = self.old_dir, | ||||
| 		old_vel = self.old_vel | ||||
| 	}) | ||||
| end | ||||
|  | ||||
| function cart_entity:on_punch(puncher, time_from_last_punch, tool_capabilities, direction) | ||||
| 	local pos = self.object:getpos() | ||||
| 	if not self.railtype then | ||||
| 		local node = minetest.get_node(pos).name | ||||
| 		self.railtype = minetest.get_item_group(node, "connect_to_raillike") | ||||
| 	end | ||||
|  | ||||
| 	if not puncher or not puncher:is_player() then | ||||
| 		local cart_dir = carts:get_rail_direction(pos, self.old_dir, nil, nil, self.railtype) | ||||
| 		if vector.equals(cart_dir, {x=0, y=0, z=0}) then | ||||
| 			return | ||||
| 		end | ||||
| 		self.velocity = vector.multiply(cart_dir, 3) | ||||
| 		self.punched = true | ||||
| 		return | ||||
| 	end | ||||
|  | ||||
| 	if puncher:get_player_control().sneak then | ||||
| 		if self.sound_handle then | ||||
| 			minetest.sound_stop(self.sound_handle) | ||||
| 		end | ||||
| 		-- Pick up cart: Drop all attachments | ||||
| 		if self.driver then | ||||
| 			if self.old_pos then | ||||
| 				self.object:setpos(self.old_pos) | ||||
| 			end | ||||
| 			local player = minetest.get_player_by_name(self.driver) | ||||
| 			carts:manage_attachment(player, nil) | ||||
| 		end | ||||
| 		for _,obj_ in ipairs(self.attached_items) do | ||||
| 			if obj_ then | ||||
| 				obj_:set_detach() | ||||
| 			end | ||||
| 		end | ||||
|  | ||||
| 		local leftover = puncher:get_inventory():add_item("main", "carts:cart") | ||||
| 		if not leftover:is_empty() then | ||||
| 			minetest.add_item(self.object:getpos(), leftover) | ||||
| 		end | ||||
| 		self.object:remove() | ||||
| 		return | ||||
| 	end | ||||
|  | ||||
| 	local vel = self.object:getvelocity() | ||||
| 	if puncher:get_player_name() == self.driver then | ||||
| 		if math.abs(vel.x + vel.z) > carts.punch_speed_max then | ||||
| 			return | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	local punch_dir = carts:velocity_to_dir(puncher:get_look_dir()) | ||||
| 	punch_dir.y = 0 | ||||
| 	local cart_dir = carts:get_rail_direction(pos, punch_dir, nil, nil, self.railtype) | ||||
| 	if vector.equals(cart_dir, {x=0, y=0, z=0}) then | ||||
| 		return | ||||
| 	end | ||||
|  | ||||
| 	local punch_interval = 1 | ||||
| 	if tool_capabilities and tool_capabilities.full_punch_interval then | ||||
| 		punch_interval = tool_capabilities.full_punch_interval | ||||
| 	end | ||||
| 	time_from_last_punch = math.min(time_from_last_punch or punch_interval, punch_interval) | ||||
| 	local f = 2 * (time_from_last_punch / punch_interval) | ||||
|  | ||||
| 	self.velocity = vector.multiply(cart_dir, f) | ||||
| 	self.old_dir = cart_dir | ||||
| 	self.punched = true | ||||
| end | ||||
|  | ||||
| local function rail_on_step_event(handler, obj, dtime) | ||||
| 	if handler then | ||||
| 		handler(obj, dtime) | ||||
| 	end | ||||
| end | ||||
|  | ||||
| -- sound refresh interval = 1.0sec | ||||
| local function rail_sound(self, dtime) | ||||
| 	if not self.sound_ttl then | ||||
| 		self.sound_ttl = 1.0 | ||||
| 		return | ||||
| 	elseif self.sound_ttl > 0 then | ||||
| 		self.sound_ttl = self.sound_ttl - dtime | ||||
| 		return | ||||
| 	end | ||||
| 	self.sound_ttl = 1.0 | ||||
| 	if self.sound_handle then | ||||
| 		local handle = self.sound_handle | ||||
| 		self.sound_handle = nil | ||||
| 		minetest.after(0.2, minetest.sound_stop, handle) | ||||
| 	end | ||||
| 	local vel = self.object:getvelocity() | ||||
| 	local speed = vector.length(vel) | ||||
| 	if speed > 0 then | ||||
| 		self.sound_handle = minetest.sound_play( | ||||
| 			"carts_cart_moving", { | ||||
| 			object = self.object, | ||||
| 			gain = (speed / carts.speed_max) / 2, | ||||
| 			loop = true, | ||||
| 		}) | ||||
| 	end | ||||
| end | ||||
|  | ||||
| local function rail_on_step(self, dtime) | ||||
| 	local pos = self.object:getpos() | ||||
| 	local node = minetest.get_node(pos) | ||||
| 	local railparams = carts.railparams[node.name] or {} | ||||
|  | ||||
| 	local vel = self.object:getvelocity() | ||||
| 	local update = {} | ||||
| 	if self.punched then | ||||
| 		vel = vector.add(vel, self.velocity) | ||||
| 		self.object:setvelocity(vel) | ||||
| 		self.old_dir.y = 0 | ||||
| 	elseif vector.equals(vel, {x=0, y=0, z=0}) then | ||||
| 		return | ||||
| 	end | ||||
|  | ||||
| 	-- stop cart if velocity vector flips | ||||
| 	if self.old_vel and (((self.old_vel.x * vel.x) < 0) or | ||||
| 			((self.old_vel.z * vel.z) < 0)) and | ||||
| 			(self.old_vel.y == 0) then | ||||
| 		self.old_dir = {x = 0, y = 0, z = 0} | ||||
| 		self.old_vel = {x = 0, y = 0, z = 0} | ||||
| 		self.velocity = {x = 0, y = 0, z = 0} | ||||
| 		self.old_pos = pos | ||||
| 		self.object:setvelocity(vector.new()) | ||||
| 		self.object:setacceleration(vector.new()) | ||||
| 		rail_on_step_event(railparams.on_step, self, dtime) | ||||
| 		return | ||||
| 	end | ||||
| 	self.old_vel = vector.new(vel) | ||||
|  | ||||
| 	if self.old_pos and not self.punched then | ||||
| 		local flo_pos = vector.round(pos) | ||||
| 		local flo_old = vector.round(self.old_pos) | ||||
| 		if vector.equals(flo_pos, flo_old) then | ||||
| 			-- Do not check one node multiple times | ||||
| 			rail_on_step_event(railparams.on_step, self, dtime) | ||||
| 			return | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	local ctrl, player | ||||
|  | ||||
| 	-- Get player controls | ||||
| 	if self.driver then | ||||
| 		player = minetest.get_player_by_name(self.driver) | ||||
| 		if player then | ||||
| 			ctrl = player:get_player_control() | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	if self.old_pos then | ||||
| 		-- Detection for "skipping" nodes | ||||
| 		local expected_pos = vector.add(self.old_pos, self.old_dir) | ||||
| 		local found_path = carts:pathfinder( | ||||
| 			pos, expected_pos, self.old_dir, ctrl, self.old_switch, self.railtype | ||||
| 		) | ||||
|  | ||||
| 		if not found_path then | ||||
| 			-- No rail found: reset back to the expected position | ||||
| 			pos = vector.new(self.old_pos) | ||||
| 			update.pos = true | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	local cart_dir = carts:velocity_to_dir(vel) | ||||
|  | ||||
| 	-- dir:         New moving direction of the cart | ||||
| 	-- switch_keys: Currently pressed L/R key, used to ignore the key on the next rail node | ||||
| 	local dir, switch_keys = carts:get_rail_direction( | ||||
| 		pos, cart_dir, ctrl, self.old_switch, self.railtype | ||||
| 	) | ||||
|  | ||||
| 	local new_acc = {x=0, y=0, z=0} | ||||
| 	if vector.equals(dir, {x=0, y=0, z=0}) then | ||||
| 		vel = {x=0, y=0, z=0} | ||||
| 		pos = vector.round(pos) | ||||
| 		update.pos = true | ||||
| 		update.vel = true | ||||
| 	else | ||||
| 		-- If the direction changed | ||||
| 		if dir.x ~= 0 and self.old_dir.z ~= 0 then | ||||
| 			vel.x = dir.x * math.abs(vel.z) | ||||
| 			vel.z = 0 | ||||
| 			pos.z = math.floor(pos.z + 0.5) | ||||
| 			update.pos = true | ||||
| 		end | ||||
| 		if dir.z ~= 0 and self.old_dir.x ~= 0 then | ||||
| 			vel.z = dir.z * math.abs(vel.x) | ||||
| 			vel.x = 0 | ||||
| 			pos.x = math.floor(pos.x + 0.5) | ||||
| 			update.pos = true | ||||
| 		end | ||||
| 		-- Up, down? | ||||
| 		if dir.y ~= self.old_dir.y then | ||||
| 			vel.y = dir.y * math.abs(vel.x + vel.z) | ||||
| 			pos = vector.round(pos) | ||||
| 			update.pos = true | ||||
| 		end | ||||
|  | ||||
| 		-- Slow down or speed up.. | ||||
| 		local acc = dir.y * -4.0 | ||||
|  | ||||
| 		-- no need to check for railparams == nil since we always make it exist. | ||||
| 		local speed_mod = railparams.acceleration | ||||
| 		if speed_mod and speed_mod ~= 0 then | ||||
| 			-- Try to make it similar to the original carts mod | ||||
| 			acc = acc + speed_mod | ||||
| 		else | ||||
| 			-- Handbrake | ||||
| 			if ctrl and ctrl.down then | ||||
| 				acc = acc - 1.6 | ||||
| 			else | ||||
| 				acc = acc - 0.4 | ||||
| 			end | ||||
| 		end | ||||
|  | ||||
| 		new_acc = vector.multiply(dir, acc) | ||||
| 	end | ||||
|  | ||||
| 	-- Limits | ||||
| 	local max_vel = carts.speed_max | ||||
| 	for _,v in ipairs({"x","y","z"}) do | ||||
| 		if math.abs(vel[v]) > max_vel then | ||||
| 			vel[v] = carts:get_sign(vel[v]) * max_vel | ||||
| 			new_acc[v] = 0 | ||||
| 			update.vel = true | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	self.object:setacceleration(new_acc) | ||||
| 	self.old_pos = vector.new(pos) | ||||
| 	if not vector.equals(dir, {x=0, y=0, z=0}) then | ||||
| 		self.old_dir = vector.new(dir) | ||||
| 	end | ||||
| 	self.old_switch = switch_keys | ||||
|  | ||||
| 	if self.punched then | ||||
| 		-- Collect dropped items | ||||
| 		for _,obj_ in ipairs(minetest.get_objects_inside_radius(pos, 1)) do | ||||
| 			if not obj_:is_player() and | ||||
| 					obj_:get_luaentity() and | ||||
| 					not obj_:get_luaentity().physical_state and | ||||
| 					obj_:get_luaentity().name == "__builtin:item" then | ||||
|  | ||||
| 				obj_:set_attach(self.object, "", {x=0, y=0, z=0}, {x=0, y=0, z=0}) | ||||
| 				self.attached_items[#self.attached_items + 1] = obj_ | ||||
| 			end | ||||
| 		end | ||||
| 		self.punched = false | ||||
| 		update.vel = true | ||||
| 	end | ||||
|  | ||||
| 	if not (update.vel or update.pos) then | ||||
| 		rail_on_step_event(railparams.on_step, self, dtime) | ||||
| 		return | ||||
| 	end | ||||
|  | ||||
| 	local yaw = 0 | ||||
| 	if self.old_dir.x < 0 then | ||||
| 		yaw = 0.5 | ||||
| 	elseif self.old_dir.x > 0 then | ||||
| 		yaw = 1.5 | ||||
| 	elseif self.old_dir.z < 0 then | ||||
| 		yaw = 1 | ||||
| 	end | ||||
| 	self.object:setyaw(yaw * math.pi) | ||||
|  | ||||
| 	local anim = {x=0, y=0} | ||||
| 	if dir.y == -1 then | ||||
| 		anim = {x=1, y=1} | ||||
| 	elseif dir.y == 1 then | ||||
| 		anim = {x=2, y=2} | ||||
| 	end | ||||
| 	self.object:set_animation(anim, 1, 0) | ||||
|  | ||||
| 	self.object:setvelocity(vel) | ||||
| 	if update.pos then | ||||
| 		self.object:setpos(pos) | ||||
| 	end | ||||
|  | ||||
| 	-- call event handler | ||||
| 	rail_on_step_event(railparams.on_step, self, dtime) | ||||
| end | ||||
|  | ||||
| function cart_entity:on_step(dtime) | ||||
| 	rail_on_step(self, dtime) | ||||
| 	rail_sound(self, dtime) | ||||
| end | ||||
|  | ||||
| minetest.register_entity("carts:cart", cart_entity) | ||||
|  | ||||
| minetest.register_craftitem("carts:cart", { | ||||
| 	description = "Cart (Sneak+Click to pick up)", | ||||
| 	inventory_image = minetest.inventorycube("carts_cart_top.png", "carts_cart_side.png", "carts_cart_side.png"), | ||||
| 	wield_image = "carts_cart_side.png", | ||||
| 	on_place = function(itemstack, placer, pointed_thing) | ||||
| 		if not pointed_thing.type == "node" then | ||||
| 			return | ||||
| 		end | ||||
| 		if carts:is_rail(pointed_thing.under) then | ||||
| 			minetest.add_entity(pointed_thing.under, "carts:cart") | ||||
| 		elseif carts:is_rail(pointed_thing.above) then | ||||
| 			minetest.add_entity(pointed_thing.above, "carts:cart") | ||||
| 		else | ||||
| 			return | ||||
| 		end | ||||
|  | ||||
| 		minetest.sound_play({name = "default_place_node_metal", gain = 0.5}, | ||||
| 			{pos = pointed_thing.above}) | ||||
|  | ||||
| 		if not minetest.setting_getbool("creative_mode") then | ||||
| 			itemstack:take_item() | ||||
| 		end | ||||
| 		return itemstack | ||||
| 	end, | ||||
| }) | ||||
|  | ||||
| minetest.register_craft({ | ||||
| 	output = "carts:cart", | ||||
| 	recipe = { | ||||
| 		{"default:steel_ingot", "", "default:steel_ingot"}, | ||||
| 		{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, | ||||
| 	}, | ||||
| }) | ||||
		Reference in New Issue
	
	Block a user