mirror of
https://git.minetest.land/VoxeLibre/VoxeLibre.git
synced 2024-11-15 15:41:06 +01:00
Merge (latest playerplus)
This commit is contained in:
commit
b064688b14
2 changed files with 147 additions and 52 deletions
|
@ -66,7 +66,7 @@ Use the `/giveme` chat command to obtain them. See the in-game help for
|
||||||
an explanation.
|
an explanation.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
This game requires [Minetest](http://minetest.net) to run (version 5.4.0 or
|
This game requires [Minetest](http://minetest.net) to run (version 5.3.0 or
|
||||||
later). So you need to install Minetest first. Only stable versions of Minetest
|
later). So you need to install Minetest first. Only stable versions of Minetest
|
||||||
are officially supported.
|
are officially supported.
|
||||||
There is no support for running MineClone 2 in development versions of Minetest.
|
There is no support for running MineClone 2 in development versions of Minetest.
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
local S = minetest.get_translator("mcl_playerplus")
|
local S = minetest.get_translator("mcl_playerplus")
|
||||||
|
|
||||||
|
mcl_playerplus = {
|
||||||
|
elytra = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
local player_velocity_old = {x=0, y=0, z=0}
|
||||||
local get_connected_players = minetest.get_connected_players
|
local get_connected_players = minetest.get_connected_players
|
||||||
local dir_to_yaw = minetest.dir_to_yaw
|
local dir_to_yaw = minetest.dir_to_yaw
|
||||||
local get_item_group = minetest.get_item_group
|
local get_item_group = minetest.get_item_group
|
||||||
|
@ -19,21 +24,21 @@ local math = math
|
||||||
-- Internal player state
|
-- Internal player state
|
||||||
local mcl_playerplus_internal = {}
|
local mcl_playerplus_internal = {}
|
||||||
|
|
||||||
local def = {}
|
|
||||||
local time = 0
|
local time = 0
|
||||||
|
local look_pitch = 0
|
||||||
|
|
||||||
local player_collision = function(player)
|
local player_collision = function(player)
|
||||||
|
|
||||||
local pos = player:get_pos()
|
local pos = player:get_pos()
|
||||||
local vel = player:get_velocity()
|
--local vel = player:get_velocity()
|
||||||
local x = 0
|
local x = 0
|
||||||
local z = 0
|
local z = 0
|
||||||
local width = .75
|
local width = .75
|
||||||
|
|
||||||
for _,object in pairs(minetest.get_objects_inside_radius(pos, width)) do
|
for _,object in pairs(minetest.get_objects_inside_radius(pos, width)) do
|
||||||
|
|
||||||
if object:is_player()
|
if object and (object:is_player()
|
||||||
or (object:get_luaentity()._cmi_is_mob == true and object ~= player) then
|
or (object:get_luaentity()._cmi_is_mob == true and object ~= player)) then
|
||||||
|
|
||||||
local pos2 = object:get_pos()
|
local pos2 = object:get_pos()
|
||||||
local vec = {x = pos.x - pos2.x, z = pos.z - pos2.z}
|
local vec = {x = pos.x - pos2.x, z = pos.z - pos2.z}
|
||||||
|
@ -46,7 +51,7 @@ local player_collision = function(player)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return({x * 5,z * 5})
|
return({x,z})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- converts yaw to degrees
|
-- converts yaw to degrees
|
||||||
|
@ -54,18 +59,8 @@ local function degrees(rad)
|
||||||
return rad * 180.0 / math.pi
|
return rad * 180.0 / math.pi
|
||||||
end
|
end
|
||||||
|
|
||||||
local pi = math.pi
|
|
||||||
local atann = math.atan
|
|
||||||
local atan = function(x)
|
|
||||||
if not x or x ~= x then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return atann(x)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local dir_to_pitch = function(dir)
|
local dir_to_pitch = function(dir)
|
||||||
local dir2 = vector.normalize(dir)
|
--local dir2 = vector.normalize(dir)
|
||||||
local xz = math.abs(dir.x) + math.abs(dir.z)
|
local xz = math.abs(dir.x) + math.abs(dir.z)
|
||||||
return -math.atan2(-dir.y, xz)
|
return -math.atan2(-dir.y, xz)
|
||||||
end
|
end
|
||||||
|
@ -117,7 +112,38 @@ function limit_vel_yaw(player_vel_yaw, yaw)
|
||||||
return player_vel_yaw
|
return player_vel_yaw
|
||||||
end
|
end
|
||||||
|
|
||||||
local pitch, name, node_stand, node_stand_below, node_head, node_feet, pos
|
local node_stand, node_stand_below, node_head, node_feet
|
||||||
|
|
||||||
|
|
||||||
|
minetest.register_on_punchplayer(function(player, hitter, damage)
|
||||||
|
if hitter:is_player() then
|
||||||
|
if hitter:get_player_control().aux1 then
|
||||||
|
player:add_velocity(hitter:get_velocity())
|
||||||
|
end
|
||||||
|
if hitter:get_velocity().y < -6 then
|
||||||
|
player:set_hp(player:get_hp() - (damage * math.random(0.50 , 0.75)))
|
||||||
|
local pos = player:get_pos()
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 15,
|
||||||
|
time = 0.1,
|
||||||
|
minpos = {x=pos.x-0.5, y=pos.y-0.5, z=pos.z-0.5},
|
||||||
|
maxpos = {x=pos.x+0.5, y=pos.y+0.5, z=pos.z+0.5},
|
||||||
|
minvel = {x=-0.1, y=-0.1, z=-0.1},
|
||||||
|
maxvel = {x=0.1, y=0.1, z=0.1},
|
||||||
|
minacc = {x=0, y=0, z=0},
|
||||||
|
maxacc = {x=0, y=0, z=0},
|
||||||
|
minexptime = 1,
|
||||||
|
maxexptime = 2,
|
||||||
|
minsize = 1.5,
|
||||||
|
maxsize = 1.5,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_particles_crit.png^[colorize:#bc7a57:127",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
minetest.register_globalstep(function(dtime)
|
minetest.register_globalstep(function(dtime)
|
||||||
|
|
||||||
|
@ -125,13 +151,6 @@ minetest.register_globalstep(function(dtime)
|
||||||
|
|
||||||
for _,player in pairs(get_connected_players()) do
|
for _,player in pairs(get_connected_players()) do
|
||||||
|
|
||||||
c_x, c_y = unpack(player_collision(player))
|
|
||||||
|
|
||||||
if player:get_velocity().x + player:get_velocity().y < .5 and c_x + c_y > 0 then
|
|
||||||
--minetest.chat_send_player(player:get_player_name(), "pushed at " .. c_x + c_y .. " parsecs.")
|
|
||||||
player:add_velocity({x=c_x, y=0, z=c_y})
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
_ _ _
|
_ _ _
|
||||||
__ _ _ __ (_)_ __ ___ __ _| |_(_) ___ _ __ ___
|
__ _ _ __ (_)_ __ ___ __ _| |_(_) ___ _ __ ___
|
||||||
|
@ -141,14 +160,22 @@ minetest.register_globalstep(function(dtime)
|
||||||
|
|
||||||
]]--
|
]]--
|
||||||
|
|
||||||
local controls = player:get_player_control()
|
local control = player:get_player_control()
|
||||||
local name = player:get_player_name()
|
local name = player:get_player_name()
|
||||||
local meta = player:get_meta()
|
--local meta = player:get_meta()
|
||||||
local parent = player:get_attach()
|
local parent = player:get_attach()
|
||||||
local wielded = player:get_wielded_item()
|
local wielded = player:get_wielded_item()
|
||||||
local player_velocity = player:get_velocity() or player:get_player_velocity()
|
local player_velocity = player:get_velocity() or player:get_player_velocity()
|
||||||
|
|
||||||
-- controls head bone
|
local c_x, c_y = unpack(player_collision(player))
|
||||||
|
|
||||||
|
if player_velocity.x + player_velocity.y < .5 and c_x + c_y > 0 then
|
||||||
|
local add_velocity = player.add_player_velocity or player.add_velocity
|
||||||
|
add_velocity(player, {x = c_x, y = 0, z = c_y})
|
||||||
|
player_velocity = player:get_velocity() or player:get_player_velocity()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- control head bone
|
||||||
local pitch = - degrees(player:get_look_vertical())
|
local pitch = - degrees(player:get_look_vertical())
|
||||||
local yaw = degrees(player:get_look_horizontal())
|
local yaw = degrees(player:get_look_horizontal())
|
||||||
|
|
||||||
|
@ -159,12 +186,74 @@ minetest.register_globalstep(function(dtime)
|
||||||
player_vel_yaw = limit_vel_yaw(player_vel_yaw, yaw)
|
player_vel_yaw = limit_vel_yaw(player_vel_yaw, yaw)
|
||||||
player_vel_yaws[name] = player_vel_yaw
|
player_vel_yaws[name] = player_vel_yaw
|
||||||
|
|
||||||
|
local fly_pos = player:get_pos()
|
||||||
|
local fly_node = minetest.get_node({x = fly_pos.x, y = fly_pos.y - 0.5, z = fly_pos.z}).name
|
||||||
|
local elytra = mcl_playerplus.elytra[player]
|
||||||
|
|
||||||
|
elytra.active = player:get_inventory():get_stack("armor", 3):get_name() == "mcl_armor:elytra"
|
||||||
|
and not player:get_attach()
|
||||||
|
and (elytra.active or control.jump and player_velocity.y < -6)
|
||||||
|
and (fly_node == "air" or fly_node == "ignore")
|
||||||
|
|
||||||
|
if elytra.active then
|
||||||
|
if player_velocity.x < (player_velocity_old.x - 10) or player_velocity.x > (player_velocity_old.x + 10) then
|
||||||
|
player:set_hp(player:get_hp() - (math.abs(player_velocity_old.x) * 0.2))
|
||||||
|
end
|
||||||
|
if player_velocity.z < (player_velocity_old.z - 10) or player_velocity.z > (player_velocity_old.z + 10) then
|
||||||
|
player:set_hp(player:get_hp() - (math.abs(player_velocity_old.z) * 0.2))
|
||||||
|
end
|
||||||
|
mcl_player.player_set_animation(player, "fly")
|
||||||
|
if player_velocity.y < -1.5 then
|
||||||
|
player:add_velocity({x=0, y=0.17, z=0})
|
||||||
|
end
|
||||||
|
if math.abs(player_velocity.x) + math.abs(player_velocity.z) < 20 then
|
||||||
|
local dir = minetest.yaw_to_dir(player:get_look_horizontal())
|
||||||
|
if degrees(player:get_look_vertical()) * -.01 < .1 then
|
||||||
|
look_pitch = degrees(player:get_look_vertical()) * -.01
|
||||||
|
else
|
||||||
|
look_pitch = .1
|
||||||
|
end
|
||||||
|
player:add_velocity({x=dir.x, y=look_pitch, z=dir.z})
|
||||||
|
end
|
||||||
|
playerphysics.add_physics_factor(player, "gravity", "mcl_playerplus:elytra", 0.1)
|
||||||
|
|
||||||
|
if elytra.rocketing > 0 then
|
||||||
|
elytra.rocketing = elytra.rocketing - dtime
|
||||||
|
if vector.length(player_velocity) < 40 then
|
||||||
|
local add_velocity = player.add_velocity or player.add_player_velocity
|
||||||
|
add_velocity(player, vector.multiply(player:get_look_dir(), 4))
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 1,
|
||||||
|
time = 0.1,
|
||||||
|
minpos = fly_pos,
|
||||||
|
maxpos = fly_pos,
|
||||||
|
minvel = {x = 0, y = 0, z = 0},
|
||||||
|
maxvel = {x = 0, y = 0, z = 0},
|
||||||
|
minacc = {x = 0, y = 0, z = 0},
|
||||||
|
maxacc = {x = 0, y = 0, z = 0},
|
||||||
|
minexptime = 0.3,
|
||||||
|
maxexptime = 0.5,
|
||||||
|
minsize = 1,
|
||||||
|
maxsize = 2.5,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_particles_crit.png^[colorize:#bc7a57:127",
|
||||||
|
glow = 5,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
elytra.rocketing = 0
|
||||||
|
playerphysics.remove_physics_factor(player, "gravity", "mcl_playerplus:elytra")
|
||||||
|
end
|
||||||
|
|
||||||
|
player_velocity_old = player:get_velocity() or player:get_player_velocity()
|
||||||
-- controls right and left arms pitch when shooting a bow
|
-- controls right and left arms pitch when shooting a bow
|
||||||
if string.find(wielded:get_name(), "mcl_bows:bow") and controls.RMB and not controls.LMB and not controls.up and not controls.down and not controls.left and not controls.right then
|
if string.find(wielded:get_name(), "mcl_bows:bow") and control.RMB and not control.LMB and not control.up and not control.down and not control.left and not control.right then
|
||||||
player:set_bone_position("Arm_Right_Pitch_Control", vector.new(-3,5.785,0), vector.new(pitch+90,-30,pitch * -1 * .35))
|
player:set_bone_position("Arm_Right_Pitch_Control", vector.new(-3,5.785,0), vector.new(pitch+90,-30,pitch * -1 * .35))
|
||||||
player:set_bone_position("Arm_Left_Pitch_Control", vector.new(3.5,5.785,0), vector.new(pitch+90,43,pitch * .35))
|
player:set_bone_position("Arm_Left_Pitch_Control", vector.new(3.5,5.785,0), vector.new(pitch+90,43,pitch * .35))
|
||||||
-- when punching
|
-- when punching
|
||||||
elseif controls.LMB and not parent then
|
elseif control.LMB and not parent then
|
||||||
player:set_bone_position("Arm_Right_Pitch_Control", vector.new(-3,5.785,0), vector.new(pitch,0,0))
|
player:set_bone_position("Arm_Right_Pitch_Control", vector.new(-3,5.785,0), vector.new(pitch,0,0))
|
||||||
player:set_bone_position("Arm_Left_Pitch_Control", vector.new(3,5.785,0), vector.new(0,0,0))
|
player:set_bone_position("Arm_Left_Pitch_Control", vector.new(3,5.785,0), vector.new(0,0,0))
|
||||||
-- when holding an item.
|
-- when holding an item.
|
||||||
|
@ -177,30 +266,37 @@ minetest.register_globalstep(function(dtime)
|
||||||
player:set_bone_position("Arm_Right_Pitch_Control", vector.new(-3,5.785,0), vector.new(0,0,0))
|
player:set_bone_position("Arm_Right_Pitch_Control", vector.new(-3,5.785,0), vector.new(0,0,0))
|
||||||
end
|
end
|
||||||
|
|
||||||
if parent then
|
if elytra.active then
|
||||||
local parent_yaw = degrees(parent:get_yaw())
|
-- set head pitch and yaw when flying
|
||||||
player:set_properties({collisionbox = {-0.35,0,-0.35,0.35,1.8,0.35}, eye_height = 1.5, nametag_color = { r = 225, b = 225, a = 225, g = 225 }})
|
player:set_bone_position("Head_Control", vector.new(0,6.3,0), vector.new(pitch-degrees(dir_to_pitch(player_velocity)),player_vel_yaw - yaw,0))
|
||||||
player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(pitch, -limit_vel_yaw(yaw, parent_yaw) + parent_yaw, 0))
|
|
||||||
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(0,0,0))
|
|
||||||
elseif controls.sneak then
|
|
||||||
-- controls head pitch when sneaking
|
|
||||||
player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(pitch+36,0,0))
|
|
||||||
-- sets eye height, and nametag color accordingly
|
|
||||||
player:set_properties({collisionbox = {-0.35,0,-0.35,0.35,1.8,0.35}, eye_height = 1.35, nametag_color = { r = 225, b = 225, a = 0, g = 225 }})
|
|
||||||
-- sneaking body conrols
|
|
||||||
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(0,0,0))
|
|
||||||
elseif get_item_group(mcl_playerinfo[name].node_head, "water") ~= 0 and is_sprinting(name) == true then
|
|
||||||
-- set head pitch and yaw when swimming
|
|
||||||
player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(pitch+90-degrees(dir_to_pitch(player_velocity)),player_vel_yaw - yaw,0))
|
|
||||||
-- sets eye height, and nametag color accordingly
|
-- sets eye height, and nametag color accordingly
|
||||||
player:set_properties({collisionbox = {-0.35,0,-0.35,0.35,0.8,0.35}, eye_height = 0.5, nametag_color = { r = 225, b = 225, a = 225, g = 225 }})
|
player:set_properties({collisionbox = {-0.35,0,-0.35,0.35,0.8,0.35}, eye_height = 0.5, nametag_color = { r = 225, b = 225, a = 225, g = 225 }})
|
||||||
|
-- control body bone when flying
|
||||||
|
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(degrees(dir_to_pitch(player_velocity)) - 90,-player_vel_yaw + yaw + 180,0))
|
||||||
|
elseif parent then
|
||||||
|
local parent_yaw = degrees(parent:get_yaw())
|
||||||
|
player:set_properties({collisionbox = {-0.312,0,-0.312,0.312,1.8,0.312}, eye_height = 1.5, nametag_color = { r = 225, b = 225, a = 225, g = 225 }})
|
||||||
|
player:set_bone_position("Head_Control", vector.new(0,6.3,0), vector.new(pitch, -limit_vel_yaw(yaw, parent_yaw) + parent_yaw, 0))
|
||||||
|
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(0,0,0))
|
||||||
|
elseif control.sneak then
|
||||||
|
-- controls head pitch when sneaking
|
||||||
|
player:set_bone_position("Head_Control", vector.new(0,6.3,0), vector.new(pitch, player_vel_yaw - yaw, player_vel_yaw - yaw))
|
||||||
|
-- sets eye height, and nametag color accordingly
|
||||||
|
player:set_properties({collisionbox = {-0.312,0,-0.312,0.312,1.8,0.312}, eye_height = 1.35, nametag_color = { r = 225, b = 225, a = 0, g = 225 }})
|
||||||
|
-- sneaking body conrols
|
||||||
|
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(0, -player_vel_yaw + yaw, 0))
|
||||||
|
elseif get_item_group(mcl_playerinfo[name].node_head, "water") ~= 0 and is_sprinting(name) == true then
|
||||||
|
-- set head pitch and yaw when swimming
|
||||||
|
player:set_bone_position("Head_Control", vector.new(0,6.3,0), vector.new(pitch-degrees(dir_to_pitch(player_velocity)),player_vel_yaw - yaw,0))
|
||||||
|
-- sets eye height, and nametag color accordingly
|
||||||
|
player:set_properties({collisionbox = {-0.312,0,-0.312,0.312,0.8,0.312}, eye_height = 0.5, nametag_color = { r = 225, b = 225, a = 225, g = 225 }})
|
||||||
-- control body bone when swimming
|
-- control body bone when swimming
|
||||||
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(degrees(dir_to_pitch(player_velocity)) - 90,-player_vel_yaw + yaw + 180,0))
|
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(degrees(dir_to_pitch(player_velocity)) - 90,-player_vel_yaw + yaw + 180,0))
|
||||||
else
|
else
|
||||||
-- sets eye height, and nametag color accordingly
|
-- sets eye height, and nametag color accordingly
|
||||||
player:set_properties({collisionbox = {-0.35,0,-0.35,0.35,1.8,0.35}, eye_height = 1.5, nametag_color = { r = 225, b = 225, a = 225, g = 225 }})
|
player:set_properties({collisionbox = {-0.312,0,-0.312,0.312,1.8,0.312}, eye_height = 1.5, nametag_color = { r = 225, b = 225, a = 225, g = 225 }})
|
||||||
|
|
||||||
player:set_bone_position("Head", vector.new(0,6.3,0), vector.new(pitch, player_vel_yaw - yaw, 0))
|
player:set_bone_position("Head_Control", vector.new(0,6.3,0), vector.new(pitch, player_vel_yaw - yaw, 0))
|
||||||
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(0, -player_vel_yaw + yaw, 0))
|
player:set_bone_position("Body_Control", vector.new(0,6.3,0), vector.new(0, -player_vel_yaw + yaw, 0))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -211,9 +307,9 @@ minetest.register_globalstep(function(dtime)
|
||||||
mcl_playerplus_internal[name].jump_cooldown = mcl_playerplus_internal[name].jump_cooldown - dtime
|
mcl_playerplus_internal[name].jump_cooldown = mcl_playerplus_internal[name].jump_cooldown - dtime
|
||||||
end
|
end
|
||||||
|
|
||||||
if controls.jump and mcl_playerplus_internal[name].jump_cooldown <= 0 then
|
if control.jump and mcl_playerplus_internal[name].jump_cooldown <= 0 then
|
||||||
|
|
||||||
pos = player:get_pos()
|
--pos = player:get_pos()
|
||||||
|
|
||||||
node_stand = mcl_playerinfo[name].node_stand
|
node_stand = mcl_playerinfo[name].node_stand
|
||||||
node_stand_below = mcl_playerinfo[name].node_stand_below
|
node_stand_below = mcl_playerinfo[name].node_stand_below
|
||||||
|
@ -285,9 +381,6 @@ minetest.register_globalstep(function(dtime)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- set defaults
|
|
||||||
def.speed = 1
|
|
||||||
|
|
||||||
-- Standing on soul sand? If so, walk slower (unless player wears Soul Speed boots)
|
-- Standing on soul sand? If so, walk slower (unless player wears Soul Speed boots)
|
||||||
if node_stand == "mcl_nether:soul_sand" then
|
if node_stand == "mcl_nether:soul_sand" then
|
||||||
-- TODO: Tweak walk speed
|
-- TODO: Tweak walk speed
|
||||||
|
@ -442,6 +535,7 @@ minetest.register_on_joinplayer(function(player)
|
||||||
swimDistance = 0,
|
swimDistance = 0,
|
||||||
jump_cooldown = -1, -- Cooldown timer for jumping, we need this to prevent the jump exhaustion to increase rapidly
|
jump_cooldown = -1, -- Cooldown timer for jumping, we need this to prevent the jump exhaustion to increase rapidly
|
||||||
}
|
}
|
||||||
|
mcl_playerplus.elytra[player] = {active = false, rocketing = 0}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- clear when player leaves
|
-- clear when player leaves
|
||||||
|
@ -449,4 +543,5 @@ minetest.register_on_leaveplayer(function(player)
|
||||||
local name = player:get_player_name()
|
local name = player:get_player_name()
|
||||||
|
|
||||||
mcl_playerplus_internal[name] = nil
|
mcl_playerplus_internal[name] = nil
|
||||||
|
mcl_playerplus.elytra[player] = nil
|
||||||
end)
|
end)
|
||||||
|
|
Loading…
Reference in a new issue