From bc00adb6a4032de1ee1e58657d5fefcdb8033daf Mon Sep 17 00:00:00 2001 From: pingu7867 Date: Thu, 29 Feb 2024 22:49:40 -0500 Subject: [PATCH] camera rework and related stuff yet another attempt at making cameras more usable with some better code but also raw hackery -view camera when activating a pac_event related to it -manually view camera via partmenu option, some other part classes have similar activation/preview actions -better auto-switching between cameras in various cases PART:SetSmallIcon(str) overrides the tiny event indicator on part icons used when manually viewing a camera to display an eye showing that this camera is the one being viewed, might be used later PART:GetReasonsHidden() gives a whole list of reasons as a table of part-indexed strings instead of doing early returns as soon as one reason is found. there are often multiple reasons, a common one is parent hiding also added parent hiding reason to PART:GetReasonHidden() use interpolated_multibone's friendlyname in nicename to reflect that we'll refer to interpolated_multibone as interpolator, and added a better nicename showing the nodes event seen_by_player detects other players' eyeangle raycasts on an adjustable bounding box --- lua/pac3/core/client/base_part.lua | 85 +++++++++ lua/pac3/core/client/parts/camera.lua | 180 ++++++++++++++---- lua/pac3/core/client/parts/event.lua | 110 ++++++++--- .../client/parts/interpolated_multibone.lua | 21 ++ .../editor/client/panels/extra_properties.lua | 61 ++++++ lua/pac3/editor/client/panels/tree.lua | 2 +- lua/pac3/editor/client/parts.lua | 115 ++++++++--- lua/pac3/editor/client/view.lua | 106 +++++++---- lua/pac3/editor/client/wear.lua | 6 + 9 files changed, 563 insertions(+), 123 deletions(-) diff --git a/lua/pac3/core/client/base_part.lua b/lua/pac3/core/client/base_part.lua index 59b1225fa..31310ab0a 100644 --- a/lua/pac3/core/client/base_part.lua +++ b/lua/pac3/core/client/base_part.lua @@ -564,6 +564,35 @@ do -- scene graph end end + function PART:SetSmallIcon(str) + if str == "event" then str = "icon16/clock_red.png" end + if self.pace_tree_node then + if self.pace_tree_node.Icon then + if not self.pace_tree_node.Icon.event_icon then + local pnl = vgui.Create("DImage", self.pace_tree_node.Icon) + self.pace_tree_node.Icon.event_icon_alt = true + self.pace_tree_node.Icon.event_icon = pnl + pnl:SetSize(8*(1 + 0.5*(GetConVar("pac_editor_scale"):GetFloat()-1)), 8*(1 + 0.5*(GetConVar("pac_editor_scale"):GetFloat()-1))) + pnl:SetPos(8*(1 + 0.5*(GetConVar("pac_editor_scale"):GetFloat()-1)), 8*(1 + 0.5*(GetConVar("pac_editor_scale"):GetFloat()-1))) + end + self.pace_tree_node.Icon.event_icon_alt = true + self.pace_tree_node.Icon.event_icon:SetImage(str) + self.pace_tree_node.Icon.event_icon:SetVisible(true) + end + end + end + function PART:RemoveSmallIcon() + if self.pace_tree_node then + if self.pace_tree_node.Icon then + if self.pace_tree_node.Icon.event_icon then + self.pace_tree_node.Icon.event_icon_alt = false + self.pace_tree_node.Icon.event_icon:SetImage("icon16/clock_red.png") + self.pace_tree_node.Icon.event_icon:SetVisible(false) + end + end + end + end + end do -- hidden / events @@ -669,6 +698,17 @@ do -- hidden / events self.active_events[event_part] = event_part self.active_events_ref_count = self.active_events_ref_count + 1 self:CallRecursive("CalcShowHide", false) + if self.ClassName == "camera" and pac.LocalPlayer == self:GetPlayerOwner() then + if event_part.Event == "command" then + pac.camera_linked_command_events[string.Split(event_part.Arguments,"@@")[1]] = true + end + if pac.active_camera_manual == self then --we're force-viewing this camera on the editor, assume we want to swap + pace.ManuallySelectCamera(self, false) + else + pac.TryToAwakenDormantCameras(self) + end + self:SetSmallIcon("event") + end end else if self.active_events[event_part] then @@ -676,6 +716,12 @@ do -- hidden / events self.active_events_ref_count = self.active_events_ref_count - 1 self:CallRecursive("CalcShowHide", false) end + if self.ClassName == "camera" and pac.LocalPlayer == self:GetPlayerOwner() then + if pac.active_camera_manual then --we're force-viewing another camera on the editor, since we're showing a new camera, assume we want to swap + pace.ManuallySelectCamera(self, true) + end + self:SetSmallIcon("event") + end end end @@ -698,9 +744,48 @@ do -- hidden / events return "pac_hide_disturbing is set to 1" end + for i,part in ipairs(self:GetParentList()) do + if part:IsHidden() then + table_insert(found, tostring(part) .. " is parent hiding") + end + end + if found[1] then + return table.concat(found, "\n") + end + return "" end + function PART:GetReasonsHidden() + local found = {} + + for part in pairs(self.active_events) do + found[part] = "event hiding" + end + + if self.Hide then + found[self] = "self hiding" + end + + if self.hide_disturbing then + if self.Hide then + found[self] = "self hiding and disturbing" + else + found[self] = "disturbing" + end + end + + for i,part in ipairs(self:GetParentList()) do + if not found[part] then + if part:IsHidden() then + found[part] = "parent hidden" + end + end + end + + return found + end + function PART:CalcShowHide(from_rendering) local b = self:IsHidden() diff --git a/lua/pac3/core/client/parts/camera.lua b/lua/pac3/core/client/parts/camera.lua index dcd2c2bfa..382537168 100644 --- a/lua/pac3/core/client/parts/camera.lua +++ b/lua/pac3/core/client/parts/camera.lua @@ -19,24 +19,76 @@ for i, ply in ipairs(player.GetAll()) do ply.pac_cameras = nil end -function PART:OnShow() - local owner = self:GetPlayerOwner() - if not owner:IsValid() then return end - self.inactive = false +pac.client_camera_parts = {} - owner.pac_cameras = owner.pac_cameras or {} - owner.pac_cameras[self] = self +local function CheckCamerasAgain(ply) + local cams = ply.pac_cameras or {} + local fpos, fang, ffov, fnearz, ffarz - --the policy is that a shown camera takes priority over all others - for _, part in pairs(owner.pac_cameras) do + for _, part in pairs(cams) do + if (not part.inactive or part.priority) and not part:IsHidden() then + return true + end + end +end + +function pac.RebuildCameras(restricted_search) + local found_cams = false + pac.LocalPlayer.pac_cameras = {} + pac.client_camera_parts = {} + local parts_to_check + if restricted_search then parts_to_check = pac.client_camera_parts else parts_to_check = pac.GetLocalParts() end + if table.IsEmpty(pac.client_camera_parts) then + parts_to_check = pac.GetLocalParts() + end + for _,part in pairs(parts_to_check) do + if part:IsValid() then + part.inactive = nil + if part.ClassName == "camera" then + pac.nocams = false + found_cams = true + pac.client_camera_parts[part.UniqueID] = part + if not part.inactive or not part:IsHidden() or part.priority then + pac.LocalPlayer.pac_cameras[part] = part + end + end + + end + end + if not found_cams then + pac.nocams = true + end +end + +function PART:CameraTakePriority(then_view) + self:GetPlayerOwner().pac_cameras = self:GetPlayerOwner().pac_cameras or {} + for _, part in pairs(self:GetPlayerOwner().pac_cameras) do if part ~= self then part.priority = false + part.inactive = true + part:RemoveSmallIcon() end end self.priority = true + self.inactive = false timer.Simple(0.02, function() self.priority = true end) + if then_view then + timer.Simple(0.2, function() pace.CameraPartSwapView(true) end) + end +end + +function PART:OnShow() + local owner = self:GetPlayerOwner() + if not owner:IsValid() then return end + self.inactive = false + + owner.pac_cameras = owner.pac_cameras or {} + owner.pac_cameras[self] = self + + --the policy is that a shown camera takes priority over all others + self:CameraTakePriority() end function PART:OnHide() @@ -54,6 +106,44 @@ function PART:OnHide() self.inactive = true self.priority = false owner.pac_cameras[self] = nil + pac.TryToAwakenDormantCameras() +end + +function PART:OnRemove() + local owner = self:GetPlayerOwner() + + if LocalPlayer() == owner then + owner.pac_cameras = owner.pac_cameras or {} + pac.client_camera_parts[self.UniqueID] = nil + local other_visible_cameras = 0 + --this camera cedes priority to others that may be active + for _, part in pairs(owner.pac_cameras) do + if part.UniqueID ~= self.UniqueID and not part:IsHidden() then + part.priority = true + other_visible_cameras = other_visible_cameras + 1 + end + end + owner.pac_cameras[self] = nil + if not pace.hack_camera_part_donot_treat_wear_as_creating_part and not pace.is_still_loading_wearing then + timer.Simple(0.2, function() + pace.EnableView(true) + end) + timer.Simple(0.4, function() + pace.ResetView() + pace.CameraPartSwapView(true) + end) + end + if pac.active_camera == self then pac.active_camera = nil end + if pac.active_camera_manual == self then pac.active_camera_manual = nil end + pac.RebuildCameras() + end +end + +function PART:Initialize() + if pac.LocalPlayer == self:GetPlayerOwner() then + pac.nocams = false + pac.client_camera_parts[self.UniqueID] = self + end end --[[function PART:OnHide() @@ -88,47 +178,55 @@ local temp = {} local remaining_camera = false local remaining_camera_time_buffer = CurTime() -local function CheckCamerasAgain(ply) - local cams = ply.pac_cameras or {} - local fpos, fang, ffov, fnearz, ffarz - for _, part in pairs(cams) do - if (not part.inactive or part.priority) and not part:IsHidden() then - return true - end +function pac.TryToAwakenDormantCameras(calling_part) + if pace.Editor:IsValid() then return end + + if not isbool(calling_part) then + pac.RebuildCameras() end -end -local function RebuildCameras(ply) - ply.pac_cameras = {} - for _,part in pairs(pac.GetLocalParts()) do + for _,part in pairs(pac.client_camera_parts) do if part:IsValid() then - if part.ClassName == "camera" and (not part.inactive or not part:IsHidden() or part.priority) then - if part:GetPlayerOwner() == ply then - ply.pac_cameras[part] = part - end + if part.ClassName == "camera" and part ~= calling_part then + part:GetRootPart():CallRecursive("Think") end end end + + pace.EnableView(false) end -pac.AddHook("CalcView", "camera_part", function(ply, pos, ang, fov, nearz, farz) +pac.nocams = true +pac.nocam_counter = 0 +function pac.HandleCameraPart(ply, pos, ang, fov, nearz, farz) + local chosen_part local fpos, fang, ffov, fnearz, ffarz - local warning_state = not ply.pac_cameras + local ply = pac.LocalPlayer + if pac.nocams then return end + ply.pac_cameras = ply.pac_cameras or {} + + local warning_state = ply.pac_cameras == nil if not warning_state then warning_state = table.IsEmpty(ply.pac_cameras) end if ply:GetViewEntity() ~= ply then return end remaining_camera = false remaining_camera_time_buffer = remaining_camera_time_buffer or CurTime() + pace.delaymovement = RealTime() + 1 --we need to do that so that while testing cameras, you don't fly off when walking and end up far from your character if warning_state then - RebuildCameras(ply) + pac.RebuildCameras(true) + pac.nocam_counter = pac.nocam_counter + 1 + --go back to early returns to avoid looping through localparts when no cameras are active checked 500 times + if pac.nocam_counter > 500 then pac.nocams = true return end else + if not IsValid(pac.active_camera) then pac.active_camera = nil pac.RebuildCameras(true) end + pac.nocam_counter = 0 + local chosen_camera for _, part in pairs(ply.pac_cameras) do if part.ClassName ~= "camera" then ply.pac_cameras[part] = nil end - if part.ClassName == "camera" and part:IsValid() then if not part:IsHidden() then remaining_camera = true @@ -138,7 +236,7 @@ pac.AddHook("CalcView", "camera_part", function(ply, pos, ang, fov, nearz, farz) part:CalcShowHide() if not part.inactive then --calculate values ahead of the return, used as a fallback just in case - fpos, fang, ffov, fnearz, ffarz = part:CalcView(ply, pos, ang, fov, nearz, farz) + fpos, fang, ffov, fnearz, ffarz = part:CalcView(_,_,ply:EyeAngles()) temp.origin = fpos temp.angles = fang temp.fov = ffov @@ -147,16 +245,27 @@ pac.AddHook("CalcView", "camera_part", function(ply, pos, ang, fov, nearz, farz) temp.drawviewer = false if not part:IsHidden() and not part.inactive and part.priority then + pac.active_camera = part temp.drawviewer = not part.DrawViewModel - return temp + chosen_camera = part + break end end else ply.pac_cameras[part] = nil end end + + + if chosen_camera then + chosen_camera:SetSmallIcon("icon16/eye.png") + return temp + end end + if not pac.active_camera then + pac.RebuildCameras() + end if remaining_camera or CurTime() < remaining_camera_time_buffer then return temp end @@ -164,11 +273,16 @@ pac.AddHook("CalcView", "camera_part", function(ply, pos, ang, fov, nearz, farz) --final fallback, just give us any valid pac camera to preserve the view! priority will be handled elsewhere if CheckCamerasAgain(ply) then return temp - else - return end - - return --only time to return to first person is if all camera parts are hidden AFTER we pass the buffer time filter --until we make reversible first person a thing, letting some non-drawable parts think, this is the best solution I could come up with +end + +function pac.HasRemainingCameraPart() + pac.RebuildCameras() + return table.Count(pac.LocalPlayer.pac_cameras) ~= 0 +end + +pac.AddHook("CalcView", "camera_part", function(ply, pos, ang, fov, nearz, farz) + pac.HandleCameraPart(ply, pos, ang, fov, nearz, farz) end) diff --git a/lua/pac3/core/client/parts/event.lua b/lua/pac3/core/client/parts/event.lua index 5ea8a5dee..b9ec9d213 100644 --- a/lua/pac3/core/client/parts/event.lua +++ b/lua/pac3/core/client/parts/event.lua @@ -748,6 +748,47 @@ PART.OldEvents = { end, }, + seen_by_player = { + operator_type = "none", + tutorial = "looked_at_by_player activates when a player is looking at you, determined by whether a box around you touches the direct eyeangle line", + arguments = {{extra_radius = "number"}, {require_line_of_sight = "boolean"}}, + userdata = {{editor_panel = "seen_by_player"}}, + callback = function(self, ent, extra_radius, require_line_of_sight) + extra_radius = extra_radius or 0 + self.nextcheck = self.nextcheck or CurTime() + 0.1 + if CurTime() > self.nextcheck then + for _,v in ipairs(player.GetAll()) do + if v == ent then continue end + local eyetrace = v:GetEyeTrace() + + if util.IntersectRayWithOBB(eyetrace.StartPos, eyetrace.HitPos - eyetrace.StartPos, LocalPlayer():GetPos() + LocalPlayer():OBBCenter(), Angle(0,0,0), Vector(-extra_radius,-extra_radius,-extra_radius), Vector(extra_radius,extra_radius,extra_radius)) then + self.trace_success = true + self.trace_success_ply = v + self.nextcheck = CurTime() + 0.1 + goto CHECKOUT + end + if eyetrace.Entity == ent then + self.trace_success = true + self.trace_success_ply = v + self.nextcheck = CurTime() + 0.1 + goto CHECKOUT + end + end + self.trace_success = false + self.nextcheck = CurTime() + 0.1 + end + ::CHECKOUT:: + if require_line_of_sight then + return self.trace_success + and self.trace_success_ply:IsLineOfSightClear(ent) --check world LOS + and ((util.QuickTrace(self.trace_success_ply:EyePos(), ent:EyePos() - self.trace_success_ply:EyePos(), self.trace_success_ply).Entity == ent) + or (util.QuickTrace(self.trace_success_ply:EyePos(), ent:GetPos() + ent:OBBCenter() - self.trace_success_ply:EyePos(), self.trace_success_ply).Entity == ent)) + else + return self.trace_success + end + end, + }, + is_flashlight_on = { operator_type = "none", callback = function(self, ent) @@ -3337,6 +3378,11 @@ pac.AddHook("EntityFireBullets", "firebullets", function(ent, data) end end) +--for regaining focus on cameras from first person, hacky thing to not loop through localparts every time +--only if the received command name matches that of a camera's linked command event +--we won't be finding from substrings +pac.camera_linked_command_events = {} +local initially_check_camera_linked_command_events = true net.Receive("pac_event", function(umr) local ply = net.ReadEntity() @@ -3351,12 +3397,21 @@ net.Receive("pac_event", function(umr) if ply:IsValid() then ply.pac_command_events = ply.pac_command_events or {} ply.pac_command_events[str] = {name = str, time = pac.RealTime, on = on} + if pac.LocalPlayer == ply then + if pac.camera_linked_command_events[str] then --if this might be related to a camera + pac.TryToAwakenDormantCameras() + elseif initially_check_camera_linked_command_events then --if it's not known, check only once for initialize this might be related to a camera + pac.TryToAwakenDormantCameras(true) + initially_check_camera_linked_command_events = false + end + end end end) concommand.Add("pac_wipe_events", function(ply) ply.pac_command_events = nil ply.pac_command_event_sequencebases = nil + pac.camera_linked_command_events = {} end) concommand.Add("pac_print_events", function(ply) ply.pac_command_events = ply.pac_command_events or {} @@ -3624,43 +3679,44 @@ do local list = {} - if true then - local colors = {} - - for name,colstr in pairs(pace.command_colors) do - colors[colstr] = colors[colstr] or {} - colors[colstr][name] = available[name] - end + local colors = {} - for col,tbl in pairs(colors) do + for name,colstr in pairs(pace.command_colors) do + colors[colstr] = colors[colstr] or {} + colors[colstr][name] = available[name] + end - local sublist = {} - for k,v in pairs(tbl) do - table.insert(sublist,available[k]) - end - table.sort(sublist, function(a, b) return a.trigger < b.trigger end) + for col,tbl in pairs(colors) do - for i,v in pairs(sublist) do - table.insert(list,v) - end + local sublist = {} + for k,v in pairs(tbl) do + table.insert(sublist,available[k]) end - local uncolored_sublist = {} + table.sort(sublist, function(a, b) return a.trigger < b.trigger end) - for k,v in pairs(available) do - if uncolored_events[k] then - table.insert(uncolored_sublist,available[k]) - end + for i,v in pairs(sublist) do + table.insert(list,v) end + end - table.sort(uncolored_sublist, function(a, b) return a.trigger < b.trigger end) + local uncolored_sublist = {} - for k,v in ipairs(uncolored_sublist) do - table.insert(list, v) + for k,v in pairs(available) do + if uncolored_events[k] then + table.insert(uncolored_sublist,available[k]) end - else + end + + table.sort(uncolored_sublist, function(a, b) return a.trigger < b.trigger end) + + for k,v in ipairs(uncolored_sublist) do + table.insert(list, v) + end + + --[[legacy behavior for k,v in pairs(available) do if k == names[k].name then @@ -3670,7 +3726,7 @@ do end table.sort(list, function(a, b) return a.trigger > b.trigger end) - end + ]] return list end @@ -4167,4 +4223,4 @@ net.Receive("pac_update_healthbars", function() end end -end) +end) \ No newline at end of file diff --git a/lua/pac3/core/client/parts/interpolated_multibone.lua b/lua/pac3/core/client/parts/interpolated_multibone.lua index 38f24a742..168709755 100644 --- a/lua/pac3/core/client/parts/interpolated_multibone.lua +++ b/lua/pac3/core/client/parts/interpolated_multibone.lua @@ -44,6 +44,27 @@ function PART:OnRemove() SafeRemoveEntityDelayed(self.Owner,0.1) end +function PART:GetNiceName() + if self.Name ~= "" then return self.Name end + + if not self.valid_nodes then return self.FriendlyName end + local has_valid_node = false + for i,b in ipairs(self.valid_nodes) do + if b then has_valid_node = true end + end + if not has_valid_node then return self.FriendlyName end + + local str = "Interpolator: " + local firstnodecounted = false + for i=1,20,1 do + if IsValid(self["Node"..i]) then + str = str .. (firstnodecounted and "; " or "") .. "[" .. i .. "]" .. (self["Node"..i].Name ~= "" and self["Node"..i].Name or self["Node"..i].ClassName) + firstnodecounted = true + end + end + return str +end + function PART:Initialize() self.nodes = {} self.valid_nodes = {} diff --git a/lua/pac3/editor/client/panels/extra_properties.lua b/lua/pac3/editor/client/panels/extra_properties.lua index 13b16f021..65f04b10d 100644 --- a/lua/pac3/editor/client/panels/extra_properties.lua +++ b/lua/pac3/editor/client/panels/extra_properties.lua @@ -860,6 +860,67 @@ do -- event is_touching pace.RegisterPanel(PANEL) end +do -- event seen_by_player + local PANEL = {} + + PANEL.ClassName = "properties_seen_by_player" + PANEL.Base = "pace_properties_number" + + function PANEL:OnValueSet() + local function stop() + hook.Remove("PostDrawOpaqueRenderables", "pace_draw_is_touching2") + end + local last_part = pace.current_part + + hook.Add("PostDrawOpaqueRenderables", "pace_draw_is_touching2", function() + local part = pace.current_part + if part ~= last_part then stop() return end + if not part:IsValid() then stop() return end + if part.ClassName ~= "event" then stop() return end + if not (part:GetEvent() == "seen_by_player") then stop() return end + if not pace.IsActive() then stop() return end + + local extra_radius = part:GetProperty("extra_radius") or 0 + + local ent + if part.RootOwner then + ent = part:GetRootPart():GetOwner() + else + ent = part:GetOwner() + end + + if not IsValid(ent) then stop() return end + + local mins = Vector(-extra_radius,-extra_radius,-extra_radius) + local maxs = Vector(extra_radius,extra_radius,extra_radius) + + local b = false + local players_see = {} + for _,v in ipairs(player.GetAll()) do + if v == ent then continue end + local eyetrace = v:GetEyeTrace() + + local this_player_sees = false + if util.IntersectRayWithOBB(eyetrace.StartPos, eyetrace.HitPos - eyetrace.StartPos, LocalPlayer():GetPos() + LocalPlayer():OBBCenter(), Angle(0,0,0), Vector(-extra_radius,-extra_radius,-extra_radius), Vector(extra_radius,extra_radius,extra_radius)) then + b = true + this_player_sees = true + end + if eyetrace.Entity == ent then + b = true + this_player_sees = true + end + render.DrawLine(eyetrace.StartPos, eyetrace.HitPos, this_player_sees and Color(255, 0,0) or Color(255,255,255), true) + end + ::CHECKOUT:: + if self.udata then + render.DrawWireframeBox( ent:GetPos() + ent:OBBCenter(), Angle( 0, 0, 0 ), mins, maxs, b and Color(255,0,0) or Color(255,255,255), true ) + end + end) + end + + pace.RegisterPanel(PANEL) +end + do --projectile radius local PANEL = {} diff --git a/lua/pac3/editor/client/panels/tree.lua b/lua/pac3/editor/client/panels/tree.lua index 8f479046a..2fd6dff24 100644 --- a/lua/pac3/editor/client/panels/tree.lua +++ b/lua/pac3/editor/client/panels/tree.lua @@ -168,7 +168,7 @@ do node.Icon.event_icon:SetVisible(true) else - if node.Icon.event_icon then + if node.Icon.event_icon and not node.Icon.event_icon_alt then node.Icon.event_icon:SetVisible(false) end end diff --git a/lua/pac3/editor/client/parts.lua b/lua/pac3/editor/client/parts.lua index 0e27f7b29..adec193ec 100644 --- a/lua/pac3/editor/client/parts.lua +++ b/lua/pac3/editor/client/parts.lua @@ -334,22 +334,7 @@ function pace.OnCreatePart(class_name, name, mdl, no_parent) end if class_name == "camera" and GetConVar("pac_copilot_force_preview_cameras"):GetBool() then - RunConsoleCommand("pac_enable_editor_view", "0") - pace.EnableView(true) - pac.RemoveHook("CalcView", "editor") - pac.AddHook("CalcView", "camera_part", function(ply, pos, ang, fov, nearz, farz) - part:CalcShowHide() - pos, ang, fov, nearz, farz = part:CalcView(_,_,ply:EyeAngles()) - local temp = {} - temp.origin = pos - temp.angles = ang - temp.fov = fov - temp.znear = nearz - temp.zfar = farz - temp.drawviewer = not part.DrawViewModel - return temp - - end) + timer.Simple(0.2, function() pace.EnableView(false) end) end if GetConVar("pac_copilot_auto_focus_main_property_when_creating_part"):GetBool() then if star_properties[part.ClassName] then @@ -1243,17 +1228,7 @@ do -- menu if not obj:HasParent() and obj.ClassName == "group" then pace.RemovePartOnServer(obj:GetUniqueID(), false, true) end - if obj.ClassName == "camera" and GetConVar("pac_copilot_force_preview_cameras"):GetBool() then - local no_camera_part = true - for i,v in ipairs(pac.GetLocalParts()) do - if v.ClassName == "camera" then no_camera_part = false end - end - if no_camera_part then - RunConsoleCommand("pac_enable_editor_view", "1") - pac.RemoveHook("CalcView", "camera_part") - pac.AddHook("CalcView", "editor", pace.CalcView, DLib and -4 or ULib and -1 or nil) - end - end + end function pace.SwapBaseMovables(obj1, obj2, promote) @@ -1933,6 +1908,7 @@ do -- menu --new_operations_order --default_operations_order --if not obj then obj = pace.current_part end + if obj then pace.AddClassSpecificPartMenuComponents(menu, obj) end for _,option_name in ipairs(pace.operations_order) do pace.addPartMenuComponent(menu, obj, option_name) end @@ -2185,6 +2161,91 @@ function pace.GetPartSizeInformation(obj) } end +function pace.AddClassSpecificPartMenuComponents(menu, obj) + if obj.ClassName == "camera" then + if not obj:IsHidden() then + if obj ~= pac.active_camera then + menu:AddOption("View this camera", function() + pace.ManuallySelectCamera(obj, true) + end):SetIcon("icon16/star.png") + else + menu:AddOption("Unview this camera", function() + pace.EnableView(true) + pace.ResetView() + pac.active_camera_manual = nil + if obj.pace_tree_node then + if obj.pace_tree_node.Icon then + if obj.pace_tree_node.Icon.event_icon then + obj.pace_tree_node.Icon.event_icon_alt = false + obj.pace_tree_node.Icon.event_icon:SetImage("event") + obj.pace_tree_node.Icon.event_icon:SetVisible(false) + end + end + end + end):SetIcon("icon16/camera_delete.png") + end + else + menu:AddOption("View this camera", function() + local toggleable_command_events = {} + for part,reason in pairs(obj:GetReasonsHidden()) do + if reason == "event hiding" then + if part.Event == "command" then + local cmd, time, hide = part:GetParsedArgumentsForObject(part.Events.command) + if time == 0 then + toggleable_command_events[part] = cmd + end + end + end + end + for part,cmd in pairs(toggleable_command_events) do + RunConsoleCommand("pac_event", cmd, part.Invert and "1" or "0") + end + timer.Simple(0.1, function() + pace.ManuallySelectCamera(obj, true) + end) + end):SetIcon("icon16/star.png") + end + elseif obj.ClassName == "command" then + menu:AddOption("run command", function() obj:Execute() end):SetIcon("icon16/star.png") + elseif obj.ClassName == "sound" or obj.ClassName == "sound2" then + menu:AddOption("play sound", function() obj:PlaySound() end):SetIcon("icon16/star.png") + elseif obj.ClassName == "projectile" then + local pos, ang = obj:GetDrawPosition() + menu:AddOption("fire", function() obj:Shoot(pos, ang, obj.NumberProjectiles) end):SetIcon("icon16/star.png") + elseif obj.ClassName == "hitscan" then + menu:AddOption("fire", function() obj:Shoot() end):SetIcon("icon16/star.png") + elseif obj.ClassName == "damage_zone" then + menu:AddOption("run command", function() obj:OnShow() end):SetIcon("icon16/star.png") + elseif obj.ClassName == "particles" then + if obj.FireOnce then + menu:AddOption("(FireOnce only) spew", function() obj:OnShow() end):SetIcon("icon16/star.png") + end + elseif obj.ClassName == "proxy" then + if string.find(obj.Expression, "timeex") or string.find(obj.Expression, "ezfade") then + menu:AddOption("(timeex) reset clock", function() obj:OnHide() obj:OnShow() end):SetIcon("icon16/star.png") + end + elseif obj.ClassName == "shake" then + menu:AddOption("activate (editor camera should be off)", function() obj:OnHide() obj:OnShow() end):SetIcon("icon16/star.png") + elseif obj.ClassName == "event" then + if obj.Event == "command" then + local cmd, time, hide = obj:GetParsedArgumentsForObject(obj.Events.command) + if time == 0 then --toggling mode + pac.LocalPlayer.pac_command_events[cmd] = pac.LocalPlayer.pac_command_events[cmd] or {name = cmd, time = pac.RealTime, on = 0} + ----MORE PAC JANK?? SOMETIMES, THE 2 NOTATION DOESN'T CHANGE THE STATE YET + if pac.LocalPlayer.pac_command_events[cmd].on == 1 then + menu:AddOption("(command) toggle", function() RunConsoleCommand("pac_event", cmd, "0") end):SetIcon("icon16/star.png") + else + menu:AddOption("(command) toggle", function() RunConsoleCommand("pac_event", cmd, "1") end):SetIcon("icon16/star.png") + end + + else + menu:AddOption("(command) trigger", function() RunConsoleCommand("pac_event", cmd) end):SetIcon("icon16/star.png") + end + + end + end +end + function pace.addPartMenuComponent(menu, obj, option_name) if option_name == "save" and obj then diff --git a/lua/pac3/editor/client/view.lua b/lua/pac3/editor/client/view.lua index f1f638046..1d36dab55 100644 --- a/lua/pac3/editor/client/view.lua +++ b/lua/pac3/editor/client/view.lua @@ -139,17 +139,6 @@ function pace.GUIMouseReleased(mc) if pace.editing_viewmodel or pace.editing_hands then return end mcode = nil - if not GetConVar("pac_enable_editor_view"):GetBool() then pace.EnableView(true) - else - pac.RemoveHook("CalcView", "camera_part") - pac.AddHook("GUIMousePressed", "editor", pace.GUIMousePressed) - pac.AddHook("GUIMouseReleased", "editor", pace.GUIMouseReleased) - pac.AddHook("ShouldDrawLocalPlayer", "editor", pace.ShouldDrawLocalPlayer, DLib and -4 or ULib and -1 or nil) - pac.AddHook("CalcView", "editor", pace.CalcView, DLib and -4 or ULib and -1 or nil) - pac.AddHook("HUDPaint", "editor", pace.HUDPaint) - pac.AddHook("HUDShouldDraw", "editor", pace.HUDShouldDraw) - pac.AddHook("PostRenderVGUI", "editor", pace.PostRenderVGUI) - end end local function set_mouse_pos(x, y) @@ -166,6 +155,7 @@ local function MovementBindDown(name) end local function CalcDrag() + if not pace.properties or not pace.properties.search then return end if pace.BusyWithProperties:IsValid() or @@ -276,6 +266,14 @@ end local follow_entity = CreateClientConVar("pac_camera_follow_entity", "0", true) local enable_editor_view = CreateClientConVar("pac_enable_editor_view", "1", true) +cvars.AddChangeCallback("pac_enable_editor_view", function(name, old, new) + if new == "1" then + pace.EnableView(true) + else + pace.CameraPartSwapView() + end +end, "pace_update_editor_view") + local lastEntityPos function pace.CalcView(ply, pos, ang, fov) @@ -364,13 +362,18 @@ function pace.PostRenderVGUI() end function pace.EnableView(b) - if b then pac.AddHook("GUIMousePressed", "editor", pace.GUIMousePressed) pac.AddHook("GUIMouseReleased", "editor", pace.GUIMouseReleased) pac.AddHook("ShouldDrawLocalPlayer", "editor", pace.ShouldDrawLocalPlayer, DLib and -4 or ULib and -1 or nil) - pac.AddHook("CalcView", "editor", pace.CalcView, DLib and -4 or ULib and -1 or nil) - pac.RemoveHook("CalcView", "camera_part") + if enable_editor_view:GetBool() then + pac.AddHook("CalcView", "editor", pace.CalcView, DLib and -4 or ULib and -1 or nil) + pac.RemoveHook("CalcView", "camera_part") + pac.active_camera = nil + else + if pac.HasRemainingCameraPart() then pace.CameraPartSwapView() end + pac.RemoveHook("CalcView", "editor") + end pac.AddHook("HUDPaint", "editor", pace.HUDPaint) pac.AddHook("HUDShouldDraw", "editor", pace.HUDShouldDraw) pac.AddHook("PostRenderVGUI", "editor", pace.PostRenderVGUI) @@ -382,36 +385,69 @@ function pace.EnableView(b) pac.RemoveHook("GUIMouseReleased", "editor") pac.RemoveHook("ShouldDrawLocalPlayer", "editor") pac.RemoveHook("CalcView", "editor") - pac.RemoveHook("CalcView", "camera_part") + pac.AddHook("CalcView", "camera_part", pac.HandleCameraPart) pac.RemoveHook("HUDPaint", "editor") pac.RemoveHook("HUDShouldDraw", "editor") pac.RemoveHook("PostRenderVGUI", "editor") pace.SetTPose(false) end +end - if not enable_editor_view:GetBool() or not pace.Editor:IsValid() then - local ply = LocalPlayer() - pac.RemoveHook("CalcView", "editor") - pac.AddHook("CalcView", "camera_part", function(ply, pos, ang, fov, nearz, farz) - for _, part in pairs(pac.GetLocalParts()) do - if part:IsValid() and part.ClassName == "camera" then - part:CalcShowHide() - local temp = {} - if not part:IsHidden() then - pos, ang, fov, nearz, farz = part:CalcView(_,_,ply:EyeAngles()) - temp.origin = pos - temp.angles = ang - temp.fov = fov - temp.znear = nearz - temp.zfar = farz - temp.drawviewer = not part.DrawViewModel - return temp - end +function pace.ManuallySelectCamera(obj, doselect) + if obj and doselect then + obj:CameraTakePriority(true) + pace.CameraPartSwapView(true) + pac.active_camera_manual = obj + elseif not doselect then + for i,v in pairs(pac.GetLocalParts()) do + if v.ClassName == "camera" then + if not v:IsHidden() and v ~= obj then + v:CameraTakePriority(true) + pace.CameraPartSwapView(true) + pac.active_camera_manual = v + return end end - end) - --pac.RemoveHook("ShouldDrawLocalPlayer", "editor") + end + pac.active_camera_manual = nil + else + for i,v in pairs(pac.GetLocalParts()) do + if v.ClassName == "camera" then + if not v:IsHidden() then + v:CameraTakePriority(true) + pace.CameraPartSwapView(true) + pac.active_camera_manual = v + return + end + end + end + end +end + +function pace.CameraPartSwapView(force_pac_camera) + local pac_camera_parts_should_override = not enable_editor_view:GetBool() or not pace.Editor:IsValid() or pac.HasRemainingCameraPart() + + if pace.Editor:IsValid() and enable_editor_view:GetBool() and not force_pac_camera then pac_camera_parts_should_override = false end + + if pac.HandleCameraPart() == nil then --no cameras + if not pace.ShouldDrawLocalPlayer() then + pace.EnableView(false) + end + pac.RemoveHook("CalcView", "camera_part") + elseif pac_camera_parts_should_override then --cameras + pac.AddHook("CalcView", "camera_part", pac.HandleCameraPart) + pac.RemoveHook("CalcView", "editor") + else + pace.EnableView(enable_editor_view:GetBool()) + --[[if not GetConVar("pac_copilot_force_preview_cameras"):GetBool() then + + else + pace.EnableView(false) + end]] end + + + return pac.active_camera end local function CalcAnimationFix(ent) diff --git a/lua/pac3/editor/client/wear.lua b/lua/pac3/editor/client/wear.lua index 97f6fbbe8..c300b8f6b 100644 --- a/lua/pac3/editor/client/wear.lua +++ b/lua/pac3/editor/client/wear.lua @@ -89,6 +89,12 @@ do -- to server local data = {part = part:ToTable()} + --hack so that camera part doesn't force-gain focus if it's not manually created, because wearing removes and re-creates parts. + pace.hack_camera_part_donot_treat_wear_as_creating_part = true + timer.Simple(2, function() + pace.hack_camera_part_donot_treat_wear_as_creating_part = nil + end) + if extra then table.Merge(data, extra) end