diff --git a/content/models/Advisor_ep2.dx80.vtx b/content/models/Advisor_ep2.dx80.vtx new file mode 100644 index 00000000..0c2581ea Binary files /dev/null and b/content/models/Advisor_ep2.dx80.vtx differ diff --git a/content/models/Advisor_ep2.dx90.vtx b/content/models/Advisor_ep2.dx90.vtx new file mode 100644 index 00000000..3f227197 Binary files /dev/null and b/content/models/Advisor_ep2.dx90.vtx differ diff --git a/content/models/Advisor_ep2.mdl b/content/models/Advisor_ep2.mdl new file mode 100644 index 00000000..82a97f8b Binary files /dev/null and b/content/models/Advisor_ep2.mdl differ diff --git a/content/models/Advisor_ep2.vvd b/content/models/Advisor_ep2.vvd new file mode 100644 index 00000000..a4a23035 Binary files /dev/null and b/content/models/Advisor_ep2.vvd differ diff --git a/content/models/Lamarr_ep2.ani b/content/models/Lamarr_ep2.ani new file mode 100644 index 00000000..a7be29a6 Binary files /dev/null and b/content/models/Lamarr_ep2.ani differ diff --git a/content/models/Lamarr_ep2.dx80.vtx b/content/models/Lamarr_ep2.dx80.vtx new file mode 100644 index 00000000..4eed1770 Binary files /dev/null and b/content/models/Lamarr_ep2.dx80.vtx differ diff --git a/content/models/Lamarr_ep2.dx90.vtx b/content/models/Lamarr_ep2.dx90.vtx new file mode 100644 index 00000000..f1390efe Binary files /dev/null and b/content/models/Lamarr_ep2.dx90.vtx differ diff --git a/content/models/Lamarr_ep2.mdl b/content/models/Lamarr_ep2.mdl new file mode 100644 index 00000000..6f7fea26 Binary files /dev/null and b/content/models/Lamarr_ep2.mdl differ diff --git a/content/models/Lamarr_ep2.phy b/content/models/Lamarr_ep2.phy new file mode 100644 index 00000000..cf6fff70 Binary files /dev/null and b/content/models/Lamarr_ep2.phy differ diff --git a/content/models/Lamarr_ep2.vvd b/content/models/Lamarr_ep2.vvd new file mode 100644 index 00000000..49b50c9d Binary files /dev/null and b/content/models/Lamarr_ep2.vvd differ diff --git a/content/models/advisor_ep2_animations.ani b/content/models/advisor_ep2_animations.ani new file mode 100644 index 00000000..216d5df8 Binary files /dev/null and b/content/models/advisor_ep2_animations.ani differ diff --git a/content/models/advisor_ep2_animations.mdl b/content/models/advisor_ep2_animations.mdl new file mode 100644 index 00000000..f202a314 Binary files /dev/null and b/content/models/advisor_ep2_animations.mdl differ diff --git a/content/models/combine_dropship_animatiep1.ani b/content/models/combine_dropship_animatiep1.ani new file mode 100644 index 00000000..d85246d9 Binary files /dev/null and b/content/models/combine_dropship_animatiep1.ani differ diff --git a/content/models/combine_dropship_animatiep1.mdl b/content/models/combine_dropship_animatiep1.mdl new file mode 100644 index 00000000..5c3a26f3 Binary files /dev/null and b/content/models/combine_dropship_animatiep1.mdl differ diff --git a/content/models/combine_dropship_animatiep2.ani b/content/models/combine_dropship_animatiep2.ani new file mode 100644 index 00000000..ededa0f8 Binary files /dev/null and b/content/models/combine_dropship_animatiep2.ani differ diff --git a/content/models/combine_dropship_animatiep2.mdl b/content/models/combine_dropship_animatiep2.mdl new file mode 100644 index 00000000..268a6728 Binary files /dev/null and b/content/models/combine_dropship_animatiep2.mdl differ diff --git a/content/models/combine_dropship_animatihl2.ani b/content/models/combine_dropship_animatihl2.ani new file mode 100644 index 00000000..48b195ed Binary files /dev/null and b/content/models/combine_dropship_animatihl2.ani differ diff --git a/content/models/combine_dropship_animatihl2.mdl b/content/models/combine_dropship_animatihl2.mdl new file mode 100644 index 00000000..2ca4d36f Binary files /dev/null and b/content/models/combine_dropship_animatihl2.mdl differ diff --git a/content/models/combine_dropship_animations.mdl b/content/models/combine_dropship_animations.mdl new file mode 100644 index 00000000..31485e6b Binary files /dev/null and b/content/models/combine_dropship_animations.mdl differ diff --git a/content/models/eli_anep1.ani b/content/models/eli_anep1.ani new file mode 100644 index 00000000..be020784 Binary files /dev/null and b/content/models/eli_anep1.ani differ diff --git a/content/models/eli_anep1.mdl b/content/models/eli_anep1.mdl new file mode 100644 index 00000000..45a38905 Binary files /dev/null and b/content/models/eli_anep1.mdl differ diff --git a/content/models/eli_anep2.ani b/content/models/eli_anep2.ani new file mode 100644 index 00000000..4df7aabe Binary files /dev/null and b/content/models/eli_anep2.ani differ diff --git a/content/models/eli_anep2.mdl b/content/models/eli_anep2.mdl new file mode 100644 index 00000000..a4a1f9b8 Binary files /dev/null and b/content/models/eli_anep2.mdl differ diff --git a/content/models/eli_anhl2.ani b/content/models/eli_anhl2.ani new file mode 100644 index 00000000..9466a1e6 Binary files /dev/null and b/content/models/eli_anhl2.ani differ diff --git a/content/models/eli_anhl2.mdl b/content/models/eli_anhl2.mdl new file mode 100644 index 00000000..10540cf7 Binary files /dev/null and b/content/models/eli_anhl2.mdl differ diff --git a/content/models/eli_anims.mdl b/content/models/eli_anims.mdl new file mode 100644 index 00000000..169a3b56 Binary files /dev/null and b/content/models/eli_anims.mdl differ diff --git a/content/models/eli_gestuep1.ani b/content/models/eli_gestuep1.ani new file mode 100644 index 00000000..b227a750 Binary files /dev/null and b/content/models/eli_gestuep1.ani differ diff --git a/content/models/eli_gestuep1.mdl b/content/models/eli_gestuep1.mdl new file mode 100644 index 00000000..a7f7e726 Binary files /dev/null and b/content/models/eli_gestuep1.mdl differ diff --git a/content/models/eli_gestuep2.ani b/content/models/eli_gestuep2.ani new file mode 100644 index 00000000..6ceac210 Binary files /dev/null and b/content/models/eli_gestuep2.ani differ diff --git a/content/models/eli_gestuep2.mdl b/content/models/eli_gestuep2.mdl new file mode 100644 index 00000000..ea49ac7b Binary files /dev/null and b/content/models/eli_gestuep2.mdl differ diff --git a/content/models/eli_gestuhl2.ani b/content/models/eli_gestuhl2.ani new file mode 100644 index 00000000..6556a79c Binary files /dev/null and b/content/models/eli_gestuhl2.ani differ diff --git a/content/models/eli_gestuhl2.mdl b/content/models/eli_gestuhl2.mdl new file mode 100644 index 00000000..38ff338c Binary files /dev/null and b/content/models/eli_gestuhl2.mdl differ diff --git a/content/models/eli_gestures.mdl b/content/models/eli_gestures.mdl new file mode 100644 index 00000000..51af561f Binary files /dev/null and b/content/models/eli_gestures.mdl differ diff --git a/content/models/eli_postuep1.ani b/content/models/eli_postuep1.ani new file mode 100644 index 00000000..afddd503 Binary files /dev/null and b/content/models/eli_postuep1.ani differ diff --git a/content/models/eli_postuep1.mdl b/content/models/eli_postuep1.mdl new file mode 100644 index 00000000..39cbd5ae Binary files /dev/null and b/content/models/eli_postuep1.mdl differ diff --git a/content/models/eli_postuep2.ani b/content/models/eli_postuep2.ani new file mode 100644 index 00000000..5aaf8a09 Binary files /dev/null and b/content/models/eli_postuep2.ani differ diff --git a/content/models/eli_postuep2.mdl b/content/models/eli_postuep2.mdl new file mode 100644 index 00000000..f820d539 Binary files /dev/null and b/content/models/eli_postuep2.mdl differ diff --git a/content/models/eli_postuhl2.ani b/content/models/eli_postuhl2.ani new file mode 100644 index 00000000..95dcf8b6 Binary files /dev/null and b/content/models/eli_postuhl2.ani differ diff --git a/content/models/eli_postuhl2.mdl b/content/models/eli_postuhl2.mdl new file mode 100644 index 00000000..08b81ffc Binary files /dev/null and b/content/models/eli_postuhl2.mdl differ diff --git a/content/models/eli_postures.mdl b/content/models/eli_postures.mdl new file mode 100644 index 00000000..166a0283 Binary files /dev/null and b/content/models/eli_postures.mdl differ diff --git a/content/models/kleiner_animatiep1.ani b/content/models/kleiner_animatiep1.ani new file mode 100644 index 00000000..5fdd9ecc Binary files /dev/null and b/content/models/kleiner_animatiep1.ani differ diff --git a/content/models/kleiner_animatiep1.mdl b/content/models/kleiner_animatiep1.mdl new file mode 100644 index 00000000..6d3a117d Binary files /dev/null and b/content/models/kleiner_animatiep1.mdl differ diff --git a/content/models/kleiner_animatiep2.ani b/content/models/kleiner_animatiep2.ani new file mode 100644 index 00000000..abc12008 Binary files /dev/null and b/content/models/kleiner_animatiep2.ani differ diff --git a/content/models/kleiner_animatiep2.mdl b/content/models/kleiner_animatiep2.mdl new file mode 100644 index 00000000..f5e50921 Binary files /dev/null and b/content/models/kleiner_animatiep2.mdl differ diff --git a/content/models/kleiner_animatihl2.ani b/content/models/kleiner_animatihl2.ani new file mode 100644 index 00000000..87f889a8 Binary files /dev/null and b/content/models/kleiner_animatihl2.ani differ diff --git a/content/models/kleiner_animatihl2.mdl b/content/models/kleiner_animatihl2.mdl new file mode 100644 index 00000000..5076b516 Binary files /dev/null and b/content/models/kleiner_animatihl2.mdl differ diff --git a/content/models/kleiner_animations.mdl b/content/models/kleiner_animations.mdl new file mode 100644 index 00000000..0eda6260 Binary files /dev/null and b/content/models/kleiner_animations.mdl differ diff --git a/content/models/kleiner_gestuep1.ani b/content/models/kleiner_gestuep1.ani new file mode 100644 index 00000000..566e98f8 Binary files /dev/null and b/content/models/kleiner_gestuep1.ani differ diff --git a/content/models/kleiner_gestuep1.mdl b/content/models/kleiner_gestuep1.mdl new file mode 100644 index 00000000..aa4e6c70 Binary files /dev/null and b/content/models/kleiner_gestuep1.mdl differ diff --git a/content/models/kleiner_gestuep2.ani b/content/models/kleiner_gestuep2.ani new file mode 100644 index 00000000..f5d7352e Binary files /dev/null and b/content/models/kleiner_gestuep2.ani differ diff --git a/content/models/kleiner_gestuep2.mdl b/content/models/kleiner_gestuep2.mdl new file mode 100644 index 00000000..69e80bf5 Binary files /dev/null and b/content/models/kleiner_gestuep2.mdl differ diff --git a/content/models/kleiner_gestuhl2.ani b/content/models/kleiner_gestuhl2.ani new file mode 100644 index 00000000..566e98f8 Binary files /dev/null and b/content/models/kleiner_gestuhl2.ani differ diff --git a/content/models/kleiner_gestuhl2.mdl b/content/models/kleiner_gestuhl2.mdl new file mode 100644 index 00000000..10f735f8 Binary files /dev/null and b/content/models/kleiner_gestuhl2.mdl differ diff --git a/content/models/kleiner_gestures.mdl b/content/models/kleiner_gestures.mdl new file mode 100644 index 00000000..df6c6897 Binary files /dev/null and b/content/models/kleiner_gestures.mdl differ diff --git a/content/models/kleiner_postuep1.ani b/content/models/kleiner_postuep1.ani new file mode 100644 index 00000000..a1196955 Binary files /dev/null and b/content/models/kleiner_postuep1.ani differ diff --git a/content/models/kleiner_postuep1.mdl b/content/models/kleiner_postuep1.mdl new file mode 100644 index 00000000..b284ce91 Binary files /dev/null and b/content/models/kleiner_postuep1.mdl differ diff --git a/content/models/kleiner_postuep2.ani b/content/models/kleiner_postuep2.ani new file mode 100644 index 00000000..c5574bd2 Binary files /dev/null and b/content/models/kleiner_postuep2.ani differ diff --git a/content/models/kleiner_postuep2.mdl b/content/models/kleiner_postuep2.mdl new file mode 100644 index 00000000..d08e9596 Binary files /dev/null and b/content/models/kleiner_postuep2.mdl differ diff --git a/content/models/kleiner_postuhl2.ani b/content/models/kleiner_postuhl2.ani new file mode 100644 index 00000000..305309f8 Binary files /dev/null and b/content/models/kleiner_postuhl2.ani differ diff --git a/content/models/kleiner_postuhl2.mdl b/content/models/kleiner_postuhl2.mdl new file mode 100644 index 00000000..1a706260 Binary files /dev/null and b/content/models/kleiner_postuhl2.mdl differ diff --git a/content/models/kleiner_postures.mdl b/content/models/kleiner_postures.mdl new file mode 100644 index 00000000..e64d92b1 Binary files /dev/null and b/content/models/kleiner_postures.mdl differ diff --git a/entities/entities/env_zoom.lua b/entities/entities/env_zoom.lua index c662a1f1..0e87839b 100644 --- a/entities/entities/env_zoom.lua +++ b/entities/entities/env_zoom.lua @@ -32,17 +32,16 @@ end function ENT:AcceptInput(fn, data, activator, caller) DbgPrint(self, fn, data, activator, caller) - BaseClass.AcceptInput(self, fn, data, activator, caller) + return BaseClass.AcceptInput(self, fn, data, activator, caller) end function ENT:KeyValue(key, val) - BaseClass.KeyValue(self, key, val) - if key:iequals("rate") then self.Rate = val elseif key:iequals("fov") then self.FOV = val end + return BaseClass.KeyValue(self, key, val) end function ENT:ZoomPlayer(ply) diff --git a/entities/entities/lambda_checkpoint.lua b/entities/entities/lambda_checkpoint.lua index b3fc1980..dcb692ef 100644 --- a/entities/entities/lambda_checkpoint.lua +++ b/entities/entities/lambda_checkpoint.lua @@ -136,14 +136,20 @@ function ENT:Think() end end +-- TODO: Remove all instances, use SetRenderPos function ENT:SetVisiblePos(pos) self:SetNWVector("VisiblePos", pos) end +ENT.SetRenderPos = ENT.SetVisiblePos + +-- TODO: Remove all instances, use GetRenderPos function ENT:GetVisiblePos() return self:GetNWVector("VisiblePos", self:GetPos()) end +ENT.GetRenderPos = ENT.GetVisiblePos + function ENT:SetDynamicCheckpoint(dynamic) self:SetNWBool("DynamicCheckpoint", dynamic) end diff --git a/entities/entities/lambda_entity.lua b/entities/entities/lambda_entity.lua index c355be0f..179323df 100644 --- a/entities/entities/lambda_entity.lua +++ b/entities/entities/lambda_entity.lua @@ -228,6 +228,14 @@ function ENT:SetupNWVar(key, dtType, data) end end +function ENT:OnRestore() + -- Adjust the Get/Set functions, functions can not be serialized. + for k, v in pairs(self.DTVarTable) do + v.Get = DTVAR_GET[v.Type] + v.Set = DTVAR_SET[v.Type] + end +end + function ENT:GetNWVar(key, fallback) if self.DTVarTable == nil then return fallback end local dtVar = self.DTVarTable[key] diff --git a/entities/entities/lambda_trigger.lua b/entities/entities/lambda_trigger.lua index bee66a08..3ad0996f 100644 --- a/entities/entities/lambda_trigger.lua +++ b/entities/entities/lambda_trigger.lua @@ -51,9 +51,14 @@ if SERVER then self:SetupOutput("OnStartTouchAll") self:SetupOutput("OnEndTouch") self:SetupOutput("OnEndTouchAll") + self:SetupOutput("OnTouching") + self:SetupOutput("OnNotTouching") self:SetInputFunction("Enable", self.Enable) self:SetInputFunction("Disable", self.Disable) self:SetInputFunction("Toggle", self.Toggle) + self:SetInputFunction("TouchTest", self.TouchTest) + self:SetInputFunction("StartTouch", self.InputStartTouch) + self:SetInputFunction("EndTouch", self.InputEndTouch) self:SetupNWVar("Disabled", "bool", { Default = false, @@ -121,6 +126,8 @@ if SERVER then self.DisabledTouchingObjects = {} self.TouchingObjects = {} self.LastTouch = CurTime() + self.PendingStartTouch = false + self:AddDebugOverlays(bit.bor(OVERLAY_PIVOT_BIT, OVERLAY_BBOX_BIT, OVERLAY_NAME_BIT)) end @@ -316,6 +323,27 @@ if SERVER then end end + function ENT:TouchTest() + DbgPrint(self, "ENT:TouchTest") + if table.Count(self.TouchingObjects) == 0 then + self:FireOutputs("OnNotTouching", nil, nil) + else + self:FireOutputs("OnTouching", nil, nil) + end + end + + function ENT:InputStartTouch(data, activator, caller) + DbgPrint(self, "ENT:InputStartTouch") + if not IsValid(caller) then return end + self:StartTouch(caller) + end + + function ENT:InputEndTouch(data, activator, caller) + DbgPrint(self, "ENT:InputEndTouch") + if not IsValid(caller) then return end + self:EndTouch(caller) + end + function ENT:GetTimeout() local timeout = self:GetNWVar("Timeout", 0) if timeout ~= 0 then return timeout end @@ -458,7 +486,8 @@ if SERVER then --DbgPrint(self, "OnTriggerEvents: " .. #self.OnTriggerEvents) local timeoutEvent = false - if self:GetNWVar("WaitForTeam") == true then + local isTeamWait = self:GetNWVar("WaitForTeam") + if isTeamWait == true then --DbgPrint("Waiting") if self.TeamInside == false then if self.NextTimeout == 0 then @@ -486,6 +515,15 @@ if SERVER then self.OnTrigger(self, ent) end + if isTeamWait and self.PendingStartTouch == true then + DbgPrint(self, "Firing pending StartTouch") + + -- We also have to fire StartTouch because if teamwait is set it will not fire it in StartTouch. + self:FireOutputs("OnStartTouch", nil, ent) + + self.PendingStartTouch = false + end + self:FireOutputs("OnTrigger", nil, ent) if waitTime < 0 then @@ -518,21 +556,15 @@ if SERVER then function ENT:TeleportAllToTrigger() local missingEnts = {} - for _, v in pairs(ents.GetAll()) do + for _, v in pairs(util.GetAllPlayers()) do if not self:IsEntityTouching(v) and self:PassesTriggerFilters(v) then - local isAlive = true - - if v:IsPlayer() then - isAlive = v:Alive() - end - - if isAlive then + if v:Alive() then table.insert(missingEnts, v) end end end - -- Teleport missing objects into the box + -- Teleport missing players into the box. local centerPos = Vector(0, 0, 0) local numEntries = 0 @@ -633,6 +665,9 @@ if SERVER then if waitForTeam == false or (waitForTeam == true and self.TeamInside == true) then DbgPrint(self, CurTime() .. ", OnStartTouch") self:FireOutputs("OnStartTouch", nil, ent) + else + DbgPrint(self, "Delaying StartTouch until condition is met") + self.PendingStartTouch = true end if table.Count(self.TouchingObjects) == 1 then diff --git a/entities/entities/lambda_vehicle_companion.lua b/entities/entities/lambda_vehicle_companion.lua new file mode 100644 index 00000000..6761e87f --- /dev/null +++ b/entities/entities/lambda_vehicle_companion.lua @@ -0,0 +1,128 @@ +local DbgPrint = GetLogging("Vehicle") + +ENT.Base = "lambda_entity" +ENT.Type = "point" +DEFINE_BASECLASS("lambda_entity") + +function ENT:PreInitialize() + BaseClass.PreInitialize(self) + DbgPrint(self, "PreInitialize") + self:SetInputFunction("OnPlayerVehicleEnter", self.InputOnPlayerVehicleEnter) + self:SetInputFunction("OnPlayerVehicleExit", self.InputOnPlayerVehicleExit) + self:SetupNWVar( + "CompanionName", + "string", + { + Default = "", + KeyValue = "CompanionName" + } + ) +end + +function ENT:Initialize() + BaseClass.Initialize(self) + DbgPrint(self, "Initialize") +end + +local function IsEntityInVehicle(ent) + if not IsValid(ent) then + return false + end + + if ent:IsPlayer() then + return ent:InVehicle() + end + + if ent:IsNPC() then + -- This is a bit akward. + local parent = ent:GetParent() + if IsValid(parent) and parent:IsVehicle() then + return true + end + end + + return false +end + +local function IsEntityBusy(ent) + if not IsValid(ent) then + return false + end + + if ent:IsNPC() then + local npcState = ent:GetNPCState() + if npcState == NPC_STATE_SCRIPT or npcState == NPC_STATE_DEAD or npcState == NPC_STATE_PLAYDEAD then + return true + end + end + + return false +end + +function ENT:InputOnPlayerVehicleEnter(data, activator, caller) + DbgPrint(self, "InputOnPlayerVehicleEnter", tostring(data), tostring(activator), tostring(caller)) + + local passenger = GAMEMODE:VehicleGetPassenger(caller) + if IsValid(passenger) then + -- Already holds a passenger. + DbgPrint("Vehicle already has a passenger") + return + end + + local companionName = self:GetNWVar("CompanionName") + local closest = nil + local closestDist = 999999 + for _, v in pairs(ents.FindByName(companionName)) do + local dist = v:GetPos():Distance(caller:GetPos()) + if dist < closestDist then + closest = v + closestDist = dist + end + end + + if not IsValid(closest) then + DbgPrint("Unable to find companion '" .. companionName .. "'") + return + end + + if IsEntityInVehicle(closest) then + DbgPrint("Companion is already in a vehicle") + return + end + + if IsEntityBusy(closest) then + DbgPrint("Companion is busy") + return + end + + local oldName = activator:GetName() + local newName = "lambda_vehicle_companion_" .. tostring(activator:EntIndex()) + caller:SetName(newName) + DbgPrint("Requesting '" .. companionName .. "' to enter vehicle") + closest:Input("EnterVehicle", activator, self, newName) + caller:SetName(oldName) +end + +function ENT:InputOnPlayerVehicleExit(data, activator, caller) + DbgPrint(self, "InputOnPlayerVehicleExit", tostring(data), tostring(activator), tostring(caller)) + if not IsValid(caller) or not IsValid(activator) then + return + end + + -- Check if the vehicle has a NPC companion. + local passenger = GAMEMODE:VehicleGetPassenger(caller) + if not IsValid(passenger) then + return + end + + DbgPrint("Requesting companion to leave vehicle.") + passenger:Input("ExitVehicle", activator, self) +end + +function ENT:UpdateTransmitState() + return TRANSMIT_NEVER +end + +function ENT:OnRemove() + DbgPrint(self, "OnRemove") +end \ No newline at end of file diff --git a/entities/entities/lambda_vehicle_tracker.lua b/entities/entities/lambda_vehicle_tracker.lua index ddd66970..066c9eea 100644 --- a/entities/entities/lambda_vehicle_tracker.lua +++ b/entities/entities/lambda_vehicle_tracker.lua @@ -20,7 +20,6 @@ function ENT:Initialize() end self:DrawShadow(false) - --self:AddEffects(EF_NODRAW) end function ENT:AttachToVehicle(vehicle) @@ -28,10 +27,9 @@ function ENT:AttachToVehicle(vehicle) self:SetPos(vehicle:GetPos()) self:SetAngles(vehicle:GetAngles()) self:SetParent(vehicle) - --self:AddEffects(EF_NODRAW) self:DrawShadow(false) self.Vehicle = vehicle - self.Player = vehicle.LambdaPlayer + self.Player = GAMEMODE:VehicleGetPlayerOwner(vehicle) self:SetNWEntity("LambdaVehicleOwner", self.Player) self:SetNWBool("LambdaVehicleTaken", IsValid(self.Player)) end @@ -50,8 +48,9 @@ if SERVER then return end - if self.Player ~= vehicle.LambdaPlayer then - self.Player = vehicle.LambdaPlayer + local vehicleOwner = GAMEMODE:VehicleGetPlayerOwner(vehicle) + if self.Player ~= vehicleOwner then + self.Player = vehicleOwner self:SetNWEntity("LambdaVehicleOwner", self.Player) self:SetNWBool("LambdaVehicleTaken", IsValid(self.Player)) DbgPrint("Owner changed") diff --git a/entities/entities/logic_auto.lua b/entities/entities/logic_auto.lua index 625ca5a8..ffb5b5f1 100644 --- a/entities/entities/logic_auto.lua +++ b/entities/entities/logic_auto.lua @@ -81,10 +81,12 @@ end function ENT:Enable() self:SetNWVar("Disabled", false) self:NextThink(CurTime()) + return true end function ENT:Disable() self:SetNWVar("Disabled", true) + return true end function ENT:UpdateTransmitState() diff --git a/entities/entities/npc_template_maker.lua b/entities/entities/npc_template_maker.lua index b9fe4876..4817f20e 100644 --- a/entities/entities/npc_template_maker.lua +++ b/entities/entities/npc_template_maker.lua @@ -523,8 +523,16 @@ function ENT:MakeNPCInLine() self:UpdateScaling() end -function ENT:MakeMultipleNPCS() - Error("MakeMultipleNPCS not implemented") +function ENT:MakeMultipleNPCS(data) + local numNpcs = tonumber(data) + local inRadius = self.DestinationGroup ~= nil and self.Radius > 0.1 + for i = 1, numNpcs do + if inRadius then + self:MakeNPCInRadius() + else + self:MakeNPC() + end + end end function ENT:ChangeDestinationGroup(data) diff --git a/entities/entities/player_speedmod.lua b/entities/entities/player_speedmod.lua index ed4582ba..5478dcf8 100644 --- a/entities/entities/player_speedmod.lua +++ b/entities/entities/player_speedmod.lua @@ -14,7 +14,9 @@ function ENT:AcceptInput(inputName, activator, called, data) local speed = tonumber(data) for _, v in pairs(util.GetAllPlayers()) do - v:Flashlight(false) + if v:FlashlightIsOn() then + v:Flashlight(false) + end v:SetLaggedMovementValue(speed) end diff --git a/entities/entities/trigger_changelevel.lua b/entities/entities/trigger_changelevel.lua index f9717b50..fb5f7115 100644 --- a/entities/entities/trigger_changelevel.lua +++ b/entities/entities/trigger_changelevel.lua @@ -12,6 +12,7 @@ if SERVER then BaseClass.PreInitialize(self) BaseClass.SetWaitTime(self, -1) -- Remove once triggered. self:SetInputFunction("ChangeLevel", self.InputChangeLevel) + self:SetupOutput("OnChangeLevel") self.TargetMap = "" self.Landmark = "" self.DisableTouch = false @@ -69,11 +70,16 @@ if SERVER then function ENT:DoChangeLevel() DbgPrint(self, "DoChangeLevel") + + self:FireOutputs("OnChangeLevel", nil, nil) + local restart = self:HasSpawnFlags(SF_CHANGELEVEL_RESTART) local touchingObjects = self:GetTouchingObjects() + local targetMap = string.lower(self.TargetMap) + local landmarkName = self.Landmark - if self.Landmark ~= nil and self.Landmark ~= "" then - for _, landmark in pairs(ents.FindByName(self.Landmark)) do + if landmarkName ~= nil and landmarkName ~= "" then + for _, landmark in pairs(ents.FindByName(landmarkName)) do if landmark:GetClass() == "trigger_transition" and landmark.GetTouching ~= nil then for _, v in pairs(landmark:GetTouching()) do touchingObjects[v:EntIndex()] = v @@ -82,6 +88,18 @@ if SERVER then end end - GAMEMODE:RequestChangeLevel(self, string.lower(self.TargetMap), self.Landmark, touchingObjects, restart) + -- Because OnChangeLevel might do some things we have to delay this a bit. + util.RunDelayed(function() + -- Remove invalid entities from touching objects, might have been killed by OnChangeLevel. + for k, v in pairs(touchingObjects) do + local ent = Entity(k) + if not IsValid(ent) or ent:IsEFlagSet(EFL_KILLME) then + DbgPrint("Removing killed entity #" .. tostring(k) .. " from touch list.") + touchingObjects[k] = nil + end + end + -- Request a change level. + GAMEMODE:RequestChangeLevel(targetMap, landmarkName, touchingObjects, restart) + end, CurTime() + 0.1) end end \ No newline at end of file diff --git a/gamemode/cl_init.lua b/gamemode/cl_init.lua index 718e207e..e33fe371 100644 --- a/gamemode/cl_init.lua +++ b/gamemode/cl_init.lua @@ -191,7 +191,7 @@ function GM:CalcViewModelView(wep, vm, oldPos, oldAng, vm_origin, vm_angles) local ply = wep:GetOwner() if IsValid(ply) and IsValid(ply:GetVehicle()) then local vehicle = ply:GetVehicle() - if vehicle:GetNWBool("IsPassengerSeat", false) == true then + if self:VehicleIsPassengerSeat(vehicle) == true then local ang = oldAng local eyeAng = ply:GetAimVector():Angle() ang:Set(eyeAng + ply:GetViewPunchAngles()) @@ -307,7 +307,7 @@ function GM:CalcView(ply, pos, ang, fov, nearZ, farZ) local vehicle = ply:GetVehicle() local wep = ply:GetActiveWeapon() if IsValid(vehicle) then - if vehicle:GetNWBool("IsPassengerSeat", false) == true then + if self:VehicleIsPassengerSeat(vehicle) == true then local eyeAng = ply:GetAimVector():Angle() ang:Set(eyeAng + ply:GetViewPunchAngles()) local _, localang = WorldToLocal(Vector(0, 0, 0), ang, Vector(0, 0, 0), vehicle:GetAngles()) diff --git a/gamemode/gametypes/base/cl_localisation.lua b/gamemode/gametypes/base/cl_localisation.lua index 98d964c2..80674eb1 100644 --- a/gamemode/gametypes/base/cl_localisation.lua +++ b/gamemode/gametypes/base/cl_localisation.lua @@ -75,5 +75,6 @@ LOCALISATION["english"] = { ["GM_DM_TIMELIMIT_DESC"] = "Set how long the round lasts", ["GM_DM_TEAMONLY"] = "Team based deathmatch", ["GM_DM_TEAMONLY_DESC"] = "Switch between DM or TDM", + ["LAMBDA_VEHICLE_TAKE_OVER"] = "Take over vehicle %+speed%+%+use%" } return LOCALISATION \ No newline at end of file diff --git a/gamemode/gametypes/hl2/mapscripts/d1_canals_06.lua b/gamemode/gametypes/hl2/mapscripts/d1_canals_06.lua index 1140d419..1f9f996d 100644 --- a/gamemode/gametypes/hl2/mapscripts/d1_canals_06.lua +++ b/gamemode/gametypes/hl2/mapscripts/d1_canals_06.lua @@ -19,7 +19,12 @@ MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { - ["global_newgame_entmaker"] = true + ["global_newgame_spawner_suit"] = true, + ["global_newgame_spawner_crowbar"] = true, + ["global_newgame_spawner_pistol"] = true, + ["global_newgame_spawner_smg"] = true, + ["global_newgame_spawner_smg"] = true, + ["global_newgame_ammo"] = true, } function MAPSCRIPT:PostInit() diff --git a/gamemode/gametypes/hl2/mapscripts/d1_canals_07.lua b/gamemode/gametypes/hl2/mapscripts/d1_canals_07.lua index 25a14e5a..98cb21ef 100644 --- a/gamemode/gametypes/hl2/mapscripts/d1_canals_07.lua +++ b/gamemode/gametypes/hl2/mapscripts/d1_canals_07.lua @@ -19,7 +19,10 @@ MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { - ["global_newgame_entmaker"] = true + ["global_newgame_spawner_suit"] = true, + ["global_newgame_spawner_crowbar"] = true, + ["global_newgame_spawner_pistol"] = true, + ["global_newgame_spawner_smg"] = true, } function MAPSCRIPT:PostInit() diff --git a/gamemode/gametypes/hl2/mapscripts/d1_canals_08.lua b/gamemode/gametypes/hl2/mapscripts/d1_canals_08.lua index 6529c5ed..160cb58b 100644 --- a/gamemode/gametypes/hl2/mapscripts/d1_canals_08.lua +++ b/gamemode/gametypes/hl2/mapscripts/d1_canals_08.lua @@ -19,7 +19,10 @@ MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { - ["global_newgame_entmaker"] = true, + ["global_newgame_spawner_suit"] = true, + ["global_newgame_spawner_crowbar"] = true, + ["global_newgame_spawner_pistol"] = true, + ["global_newgame_spawner_smg1"] = true, ["relay_locks_closegates"] = true -- Dont close the doors if we pass thru the gate } diff --git a/gamemode/gametypes/hl2/mapscripts/d1_canals_09.lua b/gamemode/gametypes/hl2/mapscripts/d1_canals_09.lua index 76f396ff..7dcd8cd3 100644 --- a/gamemode/gametypes/hl2/mapscripts/d1_canals_09.lua +++ b/gamemode/gametypes/hl2/mapscripts/d1_canals_09.lua @@ -19,7 +19,11 @@ MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { - ["global_newgame_entmaker"] = true + ["global_newgame_spawner_suit"] = true, + ["global_newgame_spawner_crowbar"] = true, + ["global_newgame_spawner_pistol"] = true, + ["global_newgame_spawner_smg"] = true, + ["global_newgame_spawner_357"] = true, } function MAPSCRIPT:PostInit() diff --git a/gamemode/gametypes/hl2/mapscripts/d1_canals_10.lua b/gamemode/gametypes/hl2/mapscripts/d1_canals_10.lua index 30a8a3d4..8a66f7a3 100644 --- a/gamemode/gametypes/hl2/mapscripts/d1_canals_10.lua +++ b/gamemode/gametypes/hl2/mapscripts/d1_canals_10.lua @@ -21,7 +21,11 @@ MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { - ["global_newgame_entmaker"] = true + ["global_newgame_spawner_suit"] = true, + ["global_newgame_spawner_crowbar"] = true, + ["global_newgame_spawner_pistol"] = true, + ["global_newgame_spawner_smg"] = true, + ["global_newgame_spawner_357"] = true, } function MAPSCRIPT:PostInit() diff --git a/gamemode/gametypes/hl2/mapscripts/d1_canals_11.lua b/gamemode/gametypes/hl2/mapscripts/d1_canals_11.lua index 99cdc494..e6eeeba0 100644 --- a/gamemode/gametypes/hl2/mapscripts/d1_canals_11.lua +++ b/gamemode/gametypes/hl2/mapscripts/d1_canals_11.lua @@ -23,7 +23,11 @@ MAPSCRIPT.InputFilters = { MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { - ["global_newgame_template"] = true, + ["global_newgame_spawner_suit"] = true, + ["global_newgame_spawner_crowbar"] = true, + ["global_newgame_spawner_pistol"] = true, + ["global_newgame_spawner_smg"] = true, + ["global_newgame_spawner_357"] = true, ["relay_guncave_gate_exit_close"] = true, ["filter_invulnerable"] = true } diff --git a/gamemode/gametypes/hl2/mapscripts/d1_canals_12.lua b/gamemode/gametypes/hl2/mapscripts/d1_canals_12.lua index a330bb68..687770db 100644 --- a/gamemode/gametypes/hl2/mapscripts/d1_canals_12.lua +++ b/gamemode/gametypes/hl2/mapscripts/d1_canals_12.lua @@ -20,8 +20,11 @@ MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { - ["global_newgame_entmaker"] = true, - ["spawnitems_template"] = true + ["global_newgame_spawner_suit"] = true, + ["global_newgame_spawner_crowbar"] = true, + ["global_newgame_spawner_pistol"] = true, + ["global_newgame_spawner_smg1"] = true, + ["global_newgame_spawner_357"] = true, } MAPSCRIPT.VehicleGuns = true diff --git a/gamemode/gametypes/hl2/mapscripts/d1_canals_13.lua b/gamemode/gametypes/hl2/mapscripts/d1_canals_13.lua index 099d2f87..a52ad1e8 100644 --- a/gamemode/gametypes/hl2/mapscripts/d1_canals_13.lua +++ b/gamemode/gametypes/hl2/mapscripts/d1_canals_13.lua @@ -21,7 +21,11 @@ MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { - ["global_newgame_entmaker"] = true, + ["global_newgame_spawner_suit"] = true, + ["global_newgame_spawner_crowbar"] = true, + ["global_newgame_spawner_pistol"] = true, + ["global_newgame_spawner_smg"] = true, + ["global_newgame_spawner_357"] = true, ["canals_trigger_elitrans"] = true -- Do not changelevel based on the output. } diff --git a/gamemode/gametypes/hl2/mapscripts/d1_eli_01.lua b/gamemode/gametypes/hl2/mapscripts/d1_eli_01.lua index f538e2c0..1cff7d3b 100644 --- a/gamemode/gametypes/hl2/mapscripts/d1_eli_01.lua +++ b/gamemode/gametypes/hl2/mapscripts/d1_eli_01.lua @@ -26,7 +26,11 @@ MAPSCRIPT.InputFilters = { MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { - ["global_newgame_template_base_items"] = true, + ["global_newgame_spawner_suit"] = true, + ["global_newgame_spawner_crowbar"] = true, + ["global_newgame_spawner_pistol"] = true, + ["global_newgame_spawner_smg1"] = true, + ["global_newgame_spawner_357"] = true, ["global_newgame_template_local_items"] = true, ["global_newgame_template_ammo"] = true, ["trigger_startScene"] = true, diff --git a/gamemode/gametypes/hl2/mapscripts/d2_coast_03.lua b/gamemode/gametypes/hl2/mapscripts/d2_coast_03.lua index 35b6305d..23a47f95 100644 --- a/gamemode/gametypes/hl2/mapscripts/d2_coast_03.lua +++ b/gamemode/gametypes/hl2/mapscripts/d2_coast_03.lua @@ -28,7 +28,6 @@ MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { ["player_spawn_items"] = true, - ["player_spawn_items_maker"] = true, ["invulnerable"] = true } diff --git a/gamemode/gametypes/hl2/mapscripts/d2_coast_04.lua b/gamemode/gametypes/hl2/mapscripts/d2_coast_04.lua index 3c89d816..4242bf3c 100644 --- a/gamemode/gametypes/hl2/mapscripts/d2_coast_04.lua +++ b/gamemode/gametypes/hl2/mapscripts/d2_coast_04.lua @@ -26,7 +26,11 @@ MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { ["global_newgame_template_base_items"] = true, - ["global_newgame_template_local_items"] = true, + ["global_newgame_spawner_shotgun"] = true, + ["global_newgame_spawner_smg1"] = true, + ["global_newgame_spawner_ar2"] = true, + ["global_newgame_spawner_rpg"] = true, + ["global_newgame_spawner_357"] = true, ["global_newgame_template_ammo"] = true, ["fall_trigger"] = true, -- We replaced it by a more friendly variant ["crane_soldier_kill"] = true -- Why? diff --git a/gamemode/gametypes/hl2/mapscripts/d2_coast_07.lua b/gamemode/gametypes/hl2/mapscripts/d2_coast_07.lua index 450ed3b8..0868c9f2 100644 --- a/gamemode/gametypes/hl2/mapscripts/d2_coast_07.lua +++ b/gamemode/gametypes/hl2/mapscripts/d2_coast_07.lua @@ -25,7 +25,7 @@ MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { - ["player_spawn_items_maker"] = true + ["player_spawn_items"] = true } MAPSCRIPT.VehicleGuns = true diff --git a/gamemode/gametypes/hl2/mapscripts/d2_coast_09.lua b/gamemode/gametypes/hl2/mapscripts/d2_coast_09.lua index 23469a37..bfa35396 100644 --- a/gamemode/gametypes/hl2/mapscripts/d2_coast_09.lua +++ b/gamemode/gametypes/hl2/mapscripts/d2_coast_09.lua @@ -28,7 +28,10 @@ MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { ["global_newgame_spawner_ammo"] = true, ["global_newgame_template_local_items"] = true, - ["global_newgame_template_base_items"] = true + ["global_newgame_spawner_suit"] = true, + ["global_newgame_spawner_crowbar"] = true, + ["global_newgame_spawner_pistol"] = true, + ["global_newgame_spawner_physcannon"] = true, } MAPSCRIPT.VehicleGuns = true diff --git a/gamemode/gametypes/hl2/mapscripts/d2_coast_10.lua b/gamemode/gametypes/hl2/mapscripts/d2_coast_10.lua index a739fdff..a3556237 100644 --- a/gamemode/gametypes/hl2/mapscripts/d2_coast_10.lua +++ b/gamemode/gametypes/hl2/mapscripts/d2_coast_10.lua @@ -23,7 +23,7 @@ MAPSCRIPT.DefaultLoadout = { } MAPSCRIPT.EntityFilterByName = { - ["player_spawn_items_maker"] = true + ["player_spawn_items"] = true } MAPSCRIPT.ImportantPlayerNPCNames = { diff --git a/gamemode/gametypes/hl2ep2.lua b/gamemode/gametypes/hl2ep2.lua index 2821dcf1..615b86ca 100644 --- a/gamemode/gametypes/hl2ep2.lua +++ b/gamemode/gametypes/hl2ep2.lua @@ -13,6 +13,7 @@ GAMETYPE.MapList = { "ep2_outland_02", "ep2_outland_03", "ep2_outland_04", + "ep2_outland_02", "ep2_outland_05", "ep2_outland_06", "ep2_outland_06a", @@ -59,6 +60,10 @@ GAMETYPE.CampaignNames = { } } GAMETYPE.Localisation = include("hl2ep2/cl_localisation.lua") +GAMETYPE.ModelRemapping = { + ["models/advisor.mdl"] = "models/advisor_ep2.mdl", + ["models/lamarr.mdl"] = "models/lamarr_ep2.mdl", +} function GAMETYPE:InitSettings() self.Base:InitSettings() diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_01.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_01.lua index b0969aad..16c11533 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_01.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_01.lua @@ -13,16 +13,125 @@ MAPSCRIPT.DefaultLoadout = { MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["clip_player"] = true, + ["clip_player_train"] = true, + ["mine_pit_clip_brush"] = true, } MAPSCRIPT.GlobalStates = { } MAPSCRIPT.Checkpoints = { + { + Pos = Vector(232, -775, 9), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(329, -770, 68), + Mins = Vector(-76, -82, -76), + Maxs = Vector(76, 82, 76) + } + }, + { + Pos = Vector(-2848, -784, 256), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-2848, -784, 256), + Mins = Vector(-224, -144, -16), + Maxs = Vector(224, 144, 16) + } + }, + { + Pos = Vector(-3777, 1764, 141), + Ang = Angle(0, 90, 0), + Trigger = { + Pos = Vector(-3777, 1764, 141), + Mins = Vector(-224, -144, -16), + Maxs = Vector(224, 144, 16) + } + }, } function MAPSCRIPT:PostInit() - print("-- Incomplete mapscript --") + -- Don't make the train fall. + ents.WaitForEntityByName("prop.phys.train.04", function(ent) + ent:SetName("lambda_prop.phys.train.04") + end) + + ents.WaitForEntityByName("prop.train.02", function(ent) + ent:SetName("lambda_prop.train.02") + end) + + -- Create the physcannon + local createPhyscannon = ents.Create("lambda_clientcommand") + createPhyscannon:Spawn() + createPhyscannon.Command = function(s, data, activator, caller) + local pos = Vector(1051, -316, -2) + local ang = Angle(0, 0, 0) + + local wep = ents.CreateSimple("weapon_physcannon", { + Pos = pos, + Ang = ang + }) + + local phys = wep:GetPhysicsObject() + + if IsValid(phys) then + phys:SetMass(1000) -- Somewhat prevents players trying to hide the gun or moving it too far as its rather important. + end + + table.insert(GAMEMODE:GetMapScript().DefaultLoadout.Weapons, "weapon_physcannon") + s:Remove() + + return true + end + + -- Remove the old clientcommand + ents.WaitForEntityByName("command_physcannon", function(ent) + createPhyscannon:SetName("command_physcannon") + ent:Remove() + end) + + -- Rename the door and add a new trigger to EnableMotion + ents.WaitForEntityByName("physbox_floor_door", function(ent) + ent:SetName("lambda_physbox_floor_door") + end) + + local openDoorTrigger = ents.Create("trigger_once") + openDoorTrigger:SetName("lambda_trigger_open_door") + openDoorTrigger:SetKeyValue("teamwait", "1") + openDoorTrigger:SetKeyValue("StartDisabled", "1") + openDoorTrigger:SetupTrigger( + Vector(-5613.831543, 4506.608398, -135.968750), + Angle(0, 0, 0), + Vector(-150, -100, 0), + Vector(150, 120, 128) + ) + openDoorTrigger:Fire("AddOutput", "OnTrigger lambda_physbox_floor_door,EnableMotion,,0,-1", "0.0") + + ents.WaitForEntityByName("gate_control_lever", function(ent) + ent:Fire("AddOutput", "OnPressed lambda_trigger_open_door,Enable,,0,-1", "0.0") + end) + + -- Wait for team before we can pull the crowbar and trigger the elevator + local multiple = ents.FindByPos(Vector(-6289, 3807, -288),"trigger_multiple") + for k, v in pairs(multiple) do + v:Remove() + end + + local elevatorTrigger = ents.Create("trigger_once") + elevatorTrigger:SetupTrigger(Vector(-6289, 3807, -288), Angle(0, 0, 0), Vector(-47, -25, -36), Vector(47, 25, 36)) + elevatorTrigger:SetKeyValue("teamwait", "1") + elevatorTrigger:Fire("AddOutput", "OnTrigger crowbar,EnablePhyscannonPickup,,0,-1") + elevatorTrigger:Fire("AddOutput", "OnTrigger crowbar_cover,Disable,,0,-1") + elevatorTrigger:Fire("AddOutput", "OnTrigger timer.vort.nag.01,Disable,,0,1") + elevatorTrigger:Fire("AddOutput", "OnTrigger lcs_intro_vort_carefull,Start,,0,1") + + ents.WaitForEntityByName("relay_crowbar_grabbed", function(ent) + ent.OnTrigger = function(_,activator) + local loadout = GAMEMODE:GetMapScript().DefaultLoadout + table.insert(loadout.Weapons, "weapon_crowbar") + end + end) end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_01a.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_01a.lua index 3af456bb..08ff4ffd 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_01a.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_01a.lua @@ -4,8 +4,13 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_crowbar", + }, + Ammo = { + }, Armor = 0, HEV = true } @@ -13,16 +18,97 @@ MAPSCRIPT.DefaultLoadout = { MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["spawnitems_template"] = true, } MAPSCRIPT.GlobalStates = { } MAPSCRIPT.Checkpoints = { + { + Pos = Vector(-10951, -7040, 1302), + Ang = Angle(0, 0, 0), + WeaponAdditions = { "weapon_pistol" }, + Trigger = { + Pos = Vector(-10947, -7060, 1358), + Mins = Vector(-66, -85, -60), + Maxs = Vector(66, 85, 60) + } + }, + { + Pos = Vector(-10538, -7397, 169), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-10539, -7437, 238), + Mins = Vector(-65, -18, -43), + Maxs = Vector(65, 18, 43) + } + }, + { + Pos = Vector(-8317, -8745, 46), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-8382, -8616, 102), + Mins = Vector(-100, -80, -25), + Maxs = Vector(100, 80, 25) + } + }, + { + Pos = Vector(-7103, -8731, 23), + Ang = Angle(0, 0, 0), + WeaponAdditions = { "weapon_frag", "weapon_357", "weapon_shotgun" }, + Trigger = { + Pos = Vector(-7136, -8618, 62), + Mins = Vector(-100, -80, -44), + Maxs = Vector(100, 80, 44) + } + }, + { + Pos = Vector(-5928, -6626, -86), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-6062, -6950, -220), + Mins = Vector(-70, -140, -130), + Maxs = Vector(70, 140, 130) + } + }, + { + Pos = Vector(-4573, -6412, -83), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-4612, -6364, -32), + Mins = Vector(-125, -205, -160), + Maxs = Vector(125, 205, 160) + } + }, } function MAPSCRIPT:PostInit() - print("-- Incomplete mapscript --") + -- Apply enough force to do more than one player. + ents.WaitForEntityByName("platform_2_thruster_1", function(ent) + ent:SetKeyValue("force", "1000") + end) + + -- This is one ugly hack, we want to keep the velocity of the platform in check due to the force applied. + -- Without enough force it won't budge and with too much force it sends players flying. + ents.WaitForEntityByName("platform_2", function(ent) + hook.Add("Think", "PlatformVelocityControl", function() + if not IsValid(ent) then + return + end + local physObj = ent:GetPhysicsObject() + if not IsValid(physObj) then + return + end + local vel = physObj:GetVelocity() + local len = vel:LengthSqr() + local maxSpeed = 150 + local maxLen = maxSpeed * maxSpeed + if len > maxLen then + physObj:SetVelocity(vel:GetNormalized() * maxSpeed) + end + end) + end) end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_02.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_02.lua index 3af456bb..a0323727 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_02.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_02.lua @@ -4,25 +4,112 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, - Armor = 0, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_crowbar", + "weapon_357", + "weapon_shotgun", + "weapon_pistol", + }, + Ammo = { + }, + Armor = 30, HEV = true } MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["spawnitems_template"] = true, + ["elevator_player_clip"] = true, + ["elevator_door_1_playerblocker"] = true, + ["gman_start_player_clip"] = true, +} + +MAPSCRIPT.ImportantPlayerNPCNames = { + ["sheckley"] = true, + ["griggs"] = true, + ["vort"] = true } MAPSCRIPT.GlobalStates = { } MAPSCRIPT.Checkpoints = { + { + Pos = Vector(-3189, -9255, -875), + Ang = Angle(0, 0, 0), + Condition = function() + return GAMEMODE:GetPreviousMap() ~= "ep2_outland_04" + end, + WeaponAdditions = { "weapon_smg1" }, + Trigger = { + Pos = Vector(-2031, -8637, -715), + Mins = Vector(-150, -150, 0), + Maxs = Vector(150, 150, 76) + } + }, } function MAPSCRIPT:PostInit() - print("-- Incomplete mapscript --") + if GAMEMODE:GetPreviousMap() == "ep2_outland_04" then + -- Came from 04 by the elevator. + ents.RemoveByClass("info_player_start") + + ents.WaitForEntityByName("elevator_model", function(ent) + -- Attach the new start to the elevator. + local newStart = ents.Create("info_player_start") + newStart:SetPos(ent:LocalToWorld(Vector(-26.5, -1.5, 3))) + newStart:SetAngles(Angle(0, 0, 0)) + newStart:AddSpawnFlags(1) -- Master + newStart:Spawn() + newStart:SetParent(ent) + + -- Make sure the vort exists, he wen't missing on play testing. + local vorts = ents.FindByName("Vort") + if #vorts == 0 then + local vort = ents.Create("npc_vortigaunt") + vort:SetPos(ent:LocalToWorld(Vector(22, 16.75, 1))) + vort:SetAngles(Angle(0, 0, 0)) + vort:SetModel("models/vortigaunt_blue.mdl") + vort:Spawn() + vort:SetName("Vort") + end + end) + + local doorCloseFirst = false + GAMEMODE:WaitForInput("elevator_door_1", "SetAnimation", function(_, _, _, _, param) + if param == "close" and doorCloseFirst == false then + -- Prevent the elevator from closing the first time. + doorCloseFirst = true + return true + end + end) + + -- Modify the extents of player_in_elevator_trigger and set teamwait + ents.WaitForEntityByName("player_in_elevator_trigger", function(ent) + ent:SetupTrigger( + ent:GetPos(), + Angle(0, 0, 0), + Vector(-20, -45, -36), + Vector(55, 45, 56) + ) + ent:SetKeyValue("teamwait", "1") + end) + else + -- The default spawn gets players stuck. + for _, v in pairs(ents.FindByClass("info_player_start")) do + if v:HasSpawnFlags(1) then -- Master + v:SetPos(Vector(-2293.175537, -8260.166016, -497.381989)) + end + end + + ents.WaitForEntityByName("trigger_turret2_vcd", function(ent) + ent:Fire("AddOutput", "OnTrigger lambda_loadout_change,RunLua,,0,-1", "0.0") + end) + end + end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_03.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_03.lua index 3af456bb..4aac988e 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_03.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_03.lua @@ -4,25 +4,105 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, - Armor = 0, + Weapons = { + "weapon_crowbar", + "weapon_frag", + "weapon_physcannon", + "weapon_smg1", + "weapon_357", + "weapon_pistol", + "weapon_shotgun", + }, + Ammo = { + ["Buckshot"] = 20, + ["Grenade"] = 3, + ["SMG1_Grenade"] = 1, + }, + Armor = 30, HEV = true } MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["spawnitems_template"] = true, + ["elevator_exit_gate_door_close_rl"] = true, -- (hopefully) delete relay that handles door closing after the elevator sequence + ["bucket_tunnel_clip"] = true } MAPSCRIPT.GlobalStates = { } MAPSCRIPT.Checkpoints = { + { + Pos = Vector(-3430, -3312, 1069), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-3444, -3480, -948), + Mins = Vector(-170, -95, -140), + Maxs = Vector(170, 95, 140) + } + }, + { + Pos = Vector(-964, -2519, -749), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-1002, -2485, -641), + Mins = Vector(-150, -105, -128), + Maxs = Vector(150, 105, 128) + } + }, + { + Pos = Vector(5305, -5140, -347), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(5208, -5152, -320), + Mins = Vector(-24, -47, -35), + Maxs = Vector(24, 47, 35) + } + }, + { + Pos = Vector(3042, -7176, -1533), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(3125, -7152, -1386), + Mins = Vector(-26, -127, -106), + Maxs = Vector(26, 127, 106) + } + }, + { + Pos = Vector(1483, -8533, -1788), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(1528, -8188, -1626), + Mins = Vector(-282, -135, -160), + Maxs = Vector(282, 135, 160) + } + }, + { + Pos = Vector(1168, -9901, -507), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(1193, -9932, -416), + Mins = Vector(-105, -75, -95), + Maxs = Vector(105, 75, 95) + } + }, + { + Pos = Vector(4132, -8943, -572), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(4012, -9116, -500), + Mins = Vector(-80, -133, -77), + Maxs = Vector(80, 133, 77) + } + } } function MAPSCRIPT:PostInit() print("-- Incomplete mapscript --") + + -- TODO: Team wait for that train to go downhill? end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_04.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_04.lua index 3af456bb..9e49ce63 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_04.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_04.lua @@ -4,15 +4,25 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, - Armor = 0, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_357", + "weapon_smg1", + "weapon_crowbar", + "weapon_pistol", + "weapon_shotgun", + }, + Ammo = { + }, + Armor = 30, HEV = true } MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["loaditems"] = true, } MAPSCRIPT.GlobalStates = { diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_05.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_05.lua index 3af456bb..21f90777 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_05.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_05.lua @@ -4,8 +4,24 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_crowbar", + "weapon_frag", + "weapon_357", + "weapon_shotgun", + "weapon_pistol", + "weapon_smg1", + }, + Ammo = { + ["SMG1"] = 45, + ["Buckshot"] = 6, + ["Pistol"] = 18, + ["Grenade"] = 3, + ["357"] = 6, + ["SMG1_Grenade"] = 1, + }, Armor = 0, HEV = true } @@ -13,16 +29,51 @@ MAPSCRIPT.DefaultLoadout = { MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["spawnitems"] = true, + ["player_on_trigger"] = true, -- elevator related trigger + ["player_off_buildingtop_trigger"] = true -- best to remove this one } MAPSCRIPT.GlobalStates = { } MAPSCRIPT.Checkpoints = { + { + Pos = Vector(-668, -440, 6), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-696, -1025, 424), + Mins = Vector(-182, -2024, -444), + Maxs = Vector(182, 2024, 444) + } + }, + { + Pos = Vector(-256, 3840, 84), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-192, 4032, -8), + Mins = Vector(-224, -265, -104), + Maxs = Vector(224, 265, 104) + } + }, } function MAPSCRIPT:PostInit() - print("-- Incomplete mapscript --") + -- Redo elevator logic to wait for everyone + local elevator + ents.WaitForEntityByName("basket_brushes", function(ent) + elevator = ent + end) + + local elevatorWait = ents.Create("trigger_once") + elevatorWait:SetupTrigger(Vector(660, 5760, 30), Angle(0, 0, 0), Vector(-52, -36, -20), Vector(52, 36, 20)) + elevatorWait:SetKeyValue("teamwait", 1) + elevatorWait:AddOutput("OnTrigger", "player_branch", "SetValue", "1", 0.0, "-1") + elevatorWait.OnTrigger = function(_, activator) + local elevCP = GAMEMODE:CreateCheckpoint(Vector(656, 5760, 35)) + elevCP:SetParent(elevator) + GAMEMODE:SetPlayerCheckpoint(elevCP, activator) + end end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_06.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_06.lua index 3af456bb..5d875337 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_06.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_06.lua @@ -4,25 +4,132 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, - Armor = 0, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_crowbar", + "weapon_pistol", + "weapon_shotgun", + "weapon_smg1", + "weapon_357", + "weapon_frag", + }, + Ammo = { + ["SMG1"] = 45, + ["Buckshot"] = 6, + ["Pistol"] = 18, + ["Grenade"] = 3, + ["357"] = 6, + ["SMG1_Grenade"] = 1, + }, + Armor = 15, HEV = true } MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["global_newgame_spawner_suit"] = true, + ["global_newgame_spawner_crowbar"] = true, + ["global_newgame_spawner_pistol"] = true, + ["global_newgame_spawner_physcannon"] = true, + ["global_newgame_template_ammo"] = true, + ["global_newgame_template_local_items"] = true, + ["trigger_goopit3_ladder_up"] = true, -- remove all fall related stuff + ["trigger_goopit3_ladder_down"] = true, + ["trigger_goopit3_hurt"] = true } MAPSCRIPT.GlobalStates = { } MAPSCRIPT.Checkpoints = { + { + Pos = Vector(737, 480, 101), -- 2 + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(600, 506, 156), + Mins = Vector(-4, -56, -60), + Maxs = Vector(4, 56, 60) + } + }, + { + Pos = Vector(731, 2040, 101), -- 4 + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(728, 1988, 128), + Mins = Vector(-40, -60, -40), + Maxs = Vector(40, 60, 40) + } + }, + { + Pos = Vector(2848, 1358, -280), -- 8 + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(2873, 1314, -258), + Mins = Vector(-42, -122, -16), + Maxs = Vector(42, 122, 16) + } + }, } function MAPSCRIPT:PostInit() - print("-- Incomplete mapscript --") + ents.WaitForEntityByName("trigger_alyxChoreoArrive06", function(ent) + ent.OnTrigger = function() + local loadout = GAMEMODE:GetMapScript().DefaultLoadout + table.insert(loadout.Weapons, "weapon_ar2") + end + end) + + -- triggers actually have names on this map, lets make use of that for checkpoints + local cp1 = GAMEMODE:CreateCheckpoint(Vector(331, 506, 785)) + ents.WaitForEntityByName("bldg1_trigger", function(ent) + ent.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(cp1, activator) + end + end) + + local cp3 = GAMEMODE:CreateCheckpoint(Vector(1874, 1079, -123)) + ents.WaitForEntityByName("alyx_sniperGuide00", function(ent) + ent.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(cp3, activator) + end + end) + + local cp5 = GAMEMODE:CreateCheckpoint(Vector(22, 3296, -114)) + ents.WaitForEntityByName("trigger_warehouse_noisesInTheDark", function(ent) + ent.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(cp5, activator) + end + end) + + local cp6 = GAMEMODE:CreateCheckpoint(Vector(1118, 2798, -252)) + ents.WaitForEntityByName("trigger_startspawn_goopit1", function(ent) + ent.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(cp6, activator) + end + end) + + local cp7 = GAMEMODE:CreateCheckpoint(Vector(1904, 2032, -282)) + ents.WaitForEntityByName("zombieTrigger_topofPipeHeadCrabs", function(ent) + ent.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(cp7, activator) + end + end) + + local cp9 = GAMEMODE:CreateCheckpoint(Vector(3961, 2340, 637)) + ents.WaitForEntityByName("autoSave_beforeJeep", function(ent) + ent.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(cp9, activator) + end + end) + + local cp10 = GAMEMODE:CreateCheckpoint(Vector(-400, 2344, 693)) + ents.WaitForEntityByName("trigger_alyxComeDownToJeep", function(ent) + ent.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(cp10, activator) + end + end) end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_06a.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_06a.lua index 3af456bb..43457281 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_06a.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_06a.lua @@ -4,15 +4,36 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, - Armor = 0, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_crowbar", + "weapon_pistol", + "weapon_shotgun", + "weapon_smg1", + "weapon_ar2", + "weapon_357", + "weapon_frag", + }, + Ammo = { + ["SMG1"] = 45, + ["Buckshot"] = 6, + ["Pistol"] = 18, + ["Grenade"] = 5, + ["AR2"] = 50, + ["357"] = 6, + ["SMG1_Grenade"] = 1, + }, + Armor = 90, HEV = true } MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["global_newgame_template_base_items"] = true, + ["global_newgame_template_local_items"] = true, + ["playerclip_powerroom"] = true, } MAPSCRIPT.GlobalStates = { @@ -23,6 +44,14 @@ MAPSCRIPT.Checkpoints = { function MAPSCRIPT:PostInit() print("-- Incomplete mapscript --") + + -- Power room checkpoint + local cp1_powerroom = GAMEMODE:CreateCheckpoint(Vector(-3330, -9731, -1519)) + ents.WaitForEntityByName("trigger_alyx_standby", function(ent) + ent.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(cp1_powerroom, activator) + end + end) end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_07.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_07.lua index 3af456bb..0a2c4fde 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_07.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_07.lua @@ -2,27 +2,145 @@ if SERVER then AddCSLuaFile() end +local function CreateSoundAlias(oldName, newName) + local soundData = sound.GetProperties(oldName) + soundData.name = newName + sound.Add(soundData) +end + +CreateSoundAlias("NPC_Advisor.Speak", "NPC_Advisor.Idle") +CreateSoundAlias("NPC_Advisor.ScreenVx02", "NPC_Advisor.Alert") + local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, - Armor = 0, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_crowbar", + "weapon_pistol", + "weapon_shotgun", + "weapon_smg1", + "weapon_ar2", + "weapon_crossbow", + "weapon_357", + "weapon_frag", + }, + Ammo = { + ["XBowBolt"] = 4, + ["AR2"] = 30, + ["Pistol"] = 20, + ["Grenade"] = 5, + ["SMG1"] = 45, + }, + Armor = 60, HEV = true } MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + -- FIXME: Contains alyx. + --["global_newgame_template_base_items"] = true, + ["global_newgame_spawner_suit"] = true, + ["global_newgame_spawner_crowbar"] = true, + ["global_newgame_spawner_pistol"] = true, + ["global_newgame_spawner_physcannon"] = true, + ["global_newgame_template_ammo"] = true, + ["global_newgame_template_local_items"] = true, + ["template_barn_vclip"] = true, -- Has a template that doesn't exist. } MAPSCRIPT.GlobalStates = { } MAPSCRIPT.Checkpoints = { + { + Pos = Vector(-10035.7, -9843.1, 32.7), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-10035.7, -9843.1, 32.7), + Mins = Vector(-80, -30, 0), + Maxs = Vector(80, 30, 70) + }, + Vehicle = { + Pos = Vector(-9846, -10604, 118.5), + Ang = Angle(0, -100, 0), + } + }, } function MAPSCRIPT:PostInit() - print("-- Incomplete mapscript --") + -- Create cvehicle_barn1 for all players. + local datacvehicle_barn1 = game.FindEntityInMapData("cvehicle_barn1") + if datacvehicle_barn1 ~= nil then + for i = 1, game.MaxPlayers() - 1 do + local dupe = ents.CreateFromData(datacvehicle_barn1) + dupe:Spawn() + dupe:Activate() + end + else + print("Unable to find cvehicle_barn1 in map data!") + end + + -- Don't draw players when they are in the vehicle. + for _, veh in pairs(ents.FindByName("cvehicle_barn1")) do + veh:Fire("AddOutput", "PlayerOn !activator,DisableDraw,,0.0,-1") + veh:Fire("AddOutput", "PlayerOff !activator,EnableDraw,,0.0,-1") + end + + GAMEMODE:WaitForInput("cvehicle_barn1", "EnterVehicle", function(ent, caller) + local vehicles = ents.FindByName("cvehicle_barn1") + for _, v in pairs(util.GetAllPlayers()) do + if v:Alive() == false then + continue + end + + v:SetNoDraw(true) + + local nextVehicle = vehicles[1] + table.remove(vehicles, 1) + + if IsValid(nextVehicle) then + v:EnterVehicle(nextVehicle) + end + end + + -- Suppress this input. + return true + end) + + -- Prevent the deletion of the vehicle. + GAMEMODE:WaitForInput("jeep", "Kill", function(ent, caller) + return true + end) + + -- Resize the trigger and make it wait for all players. + ents.WaitForEntityByName("trigger_alyx_start_advisor_scene", function(ent) + ent:SetupTrigger( + Vector(-9600, -9708, 125), + Angle(0, 0, 0), + Vector(-520, -260, 0), + Vector(350, 260, 180), + false + ) + ent:SetKeyValue("teamwait", "1") + end) +end + +function MAPSCRIPT:OnJalopyCreated(jalopy) + local companionController = ents.Create("lambda_vehicle_companion") + local name = "lambda_vc_" .. tostring(jalopy:EntIndex()) + companionController:SetName(name) + companionController:SetPos(jalopy:GetPos()) + companionController:SetKeyValue("CompanionName", "alyx") + companionController:SetParent(jalopy) + companionController:Spawn() + + jalopy:ClearAllOutputs("PlayerOff") + jalopy:ClearAllOutputs("PlayerOn") + + jalopy:Fire("AddOutput", "PlayerOn " .. name .. ",OnPlayerVehicleEnter,,0,-1") + jalopy:Fire("AddOutput", "PlayerOff " .. name .. ",OnPlayerVehicleExit,,0,-1") end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_08.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_08.lua index 3af456bb..c279fb5b 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_08.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_08.lua @@ -4,15 +4,37 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, - Armor = 0, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_357", + "weapon_crowbar", + "weapon_frag", + "weapon_pistol", + "weapon_ar2", + "weapon_crossbow", + "weapon_smg1", + "weapon_shotgun", + }, + Ammo = { + ["Pistol"] = 18, + ["XBowBolt"] = 4, + ["AR2"] = 30, + ["Buckshot"] = 30, + ["Grenade"] = 3, + ["357"] = 6, + ["SMG1"] = 90, + }, + Armor = 45, HEV = true } MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["spawnitems"] = true, + ["velsensor_car_superjump_01"] = true, + ["velsensor_car_superjump_00"] = true, } MAPSCRIPT.GlobalStates = { @@ -23,6 +45,30 @@ MAPSCRIPT.Checkpoints = { function MAPSCRIPT:PostInit() print("-- Incomplete mapscript --") + + -- Checkpoint before heli fight area + local containerCP = GAMEMODE:CreateCheckpoint(Vector(-998, 1096, 96)) + ents.WaitForEntityByName("trigger_enter_box", function(ent) + ent.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(containerCP, activator) + end + end) +end + +function MAPSCRIPT:OnJalopyCreated(jalopy) + local companionController = ents.Create("lambda_vehicle_companion") + local name = "lambda_vc_" .. tostring(jalopy:EntIndex()) + companionController:SetName(name) + companionController:SetPos(jalopy:GetPos()) + companionController:SetKeyValue("CompanionName", "alyx") + companionController:SetParent(jalopy) + companionController:Spawn() + + jalopy:ClearAllOutputs("PlayerOff") + jalopy:ClearAllOutputs("PlayerOn") + + jalopy:Fire("AddOutput", "PlayerOn " .. name .. ",OnPlayerVehicleEnter,,0,-1") + jalopy:Fire("AddOutput", "PlayerOff " .. name .. ",OnPlayerVehicleExit,,0,-1") end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_09.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_09.lua index 3af456bb..13380ab3 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_09.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_09.lua @@ -4,25 +4,128 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, - Armor = 0, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_frag", + "weapon_pistol", + "weapon_crowbar", + "weapon_crossbow", + "weapon_ar2", + "weapon_357", + "weapon_shotgun", + "weapon_smg1", + }, + Ammo = { + ["XBowBolt"] = 4, + ["AR2"] = 50, + ["lambda_health"] = 1, + ["Buckshot"] = 30, + ["Grenade"] = 3, + ["357"] = 6, + ["SMG1"] = 90, + }, + Armor = 45, HEV = true } -MAPSCRIPT.InputFilters = {} +MAPSCRIPT.InputFilters = { + ["citizen_warehouse_door_0"] = {"Lock", "Close"}, + ["door.garage.main"] = {"Close"} -- Maybe this keeps the garage open? +} + MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["spawnitems"] = true, + ["clip.door.garage.main"] = true -- Take care of garage vehicle clip } MAPSCRIPT.GlobalStates = { } MAPSCRIPT.Checkpoints = { + { + Pos = Vector(603, -9186, 77), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(510, -9196, 121), + Mins = Vector(-181, -74, -46), + Maxs = Vector(181, 74, 46) + } + }, + { + Pos = Vector(-251, -9586, 206), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-238, -9574, 246), + Mins = Vector(-80, -172, -54), + Maxs = Vector(80, 172, 54) + } + }, + { + Pos = Vector(-1483, -9593, 80), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-1351, -9716, 117), + Mins = Vector(-167, -244, -75), + Maxs = Vector(167, 244, 75) + } + }, + { + Pos = Vector(195, -8493, 80), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(82, -8531, 144), + Mins = Vector(-10, -70, -32), + Maxs = Vector(10, 70, 32) + } + }, + { + Pos = Vector(-876, -7222, 77), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-738, -7197, 132), + Mins = Vector(-257, -111, -55), + Maxs = Vector(257, 111, 55) + } + }, + { + Pos = Vector(2807, 1735, 92), + Ang = Angle(0, 0, 0), + WeaponAdditions = {"weapon_rpg"}, + Trigger = { + Pos = Vector(2589, 1274, 128), + Mins = Vector(-65, -536, -64), + Maxs = Vector(65, 536, 64) + } + } } function MAPSCRIPT:PostInit() print("-- Incomplete mapscript --") + + local cp_garage = GAMEMODE:CreateCheckpoint(Vector(163, -8971, 75)) + ents.WaitForEntityByName("trigger.alyx.lead.to.garage", function(ent) + ent.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(cp_garage, activator) + end + end) +end + +function MAPSCRIPT:OnJalopyCreated(jalopy) + local companionController = ents.Create("lambda_vehicle_companion") + local name = "lambda_vc_" .. tostring(jalopy:EntIndex()) + companionController:SetName(name) + companionController:SetPos(jalopy:GetPos()) + companionController:SetKeyValue("CompanionName", "alyx") + companionController:SetParent(jalopy) + companionController:Spawn() + + jalopy:ClearAllOutputs("PlayerOff") + jalopy:ClearAllOutputs("PlayerOn") + + jalopy:Fire("AddOutput", "PlayerOn " .. name .. ",OnPlayerVehicleEnter,,0,-1") + jalopy:Fire("AddOutput", "PlayerOff " .. name .. ",OnPlayerVehicleExit,,0,-1") end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_10.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_10.lua index 3af456bb..557e4c1f 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_10.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_10.lua @@ -4,15 +4,36 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, - Armor = 0, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_crowbar", + "weapon_ar2", + "weapon_crossbow", + "weapon_pistol", + "weapon_357", + "weapon_frag", + "weapon_shotgun", + "weapon_smg1", + }, + Ammo = { + ["lambda_health"] = 1, + ["357"] = 6, + ["AR2"] = 60, + ["Grenade"] = 3, + ["Buckshot"] = 18, + ["Pistol"] = 18, + ["XBowBolt"] = 4, + ["SMG1"] = 90, + }, + Armor = 45, HEV = true } MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["spawnitems"] = true, } MAPSCRIPT.GlobalStates = { @@ -25,4 +46,20 @@ function MAPSCRIPT:PostInit() print("-- Incomplete mapscript --") end +function MAPSCRIPT:OnJalopyCreated(jalopy) + local companionController = ents.Create("lambda_vehicle_companion") + local name = "lambda_vc_" .. tostring(jalopy:EntIndex()) + companionController:SetName(name) + companionController:SetPos(jalopy:GetPos()) + companionController:SetKeyValue("CompanionName", "alyx") + companionController:SetParent(jalopy) + companionController:Spawn() + + jalopy:ClearAllOutputs("PlayerOff") + jalopy:ClearAllOutputs("PlayerOn") + + jalopy:Fire("AddOutput", "PlayerOn " .. name .. ",OnPlayerVehicleEnter,,0,-1") + jalopy:Fire("AddOutput", "PlayerOff " .. name .. ",OnPlayerVehicleExit,,0,-1") +end + return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_10a.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_10a.lua new file mode 100644 index 00000000..4d51c352 --- /dev/null +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_10a.lua @@ -0,0 +1,68 @@ +if SERVER then + AddCSLuaFile() +end + +local MAPSCRIPT = {} +MAPSCRIPT.DefaultLoadout = { + Weapons = { + "weapon_lambda_medkit", + "weapon_pistol", + "weapon_crowbar", + "weapon_ar2", + "weapon_crossbow", + "weapon_357", + "weapon_frag", + "weapon_physcannon", + "weapon_rpg", + "weapon_shotgun", + "weapon_smg1", + }, + Ammo = { + ["AR2"] = 60, + ["Buckshot"] = 18, + ["Grenade"] = 3, + ["SMG1"] = 90, + ["SMG1_Grenade"] = 1, + ["AR2AltFire"] = 1, + ["RPG_Round"] = 3, + ["Pistol"] = 54, + ["357"] = 6, + ["XBowBolt"] = 4, + }, + Armor = 45, + HEV = true +} + +MAPSCRIPT.InputFilters = {} +MAPSCRIPT.EntityFilterByClass = {} +MAPSCRIPT.EntityFilterByName = { + ["spawnitems"] = true, +} + +MAPSCRIPT.GlobalStates = { +} + +MAPSCRIPT.Checkpoints = { +} + +function MAPSCRIPT:PostInit() + print("-- Incomplete mapscript --") +end + +function MAPSCRIPT:OnJalopyCreated(jalopy) + local companionController = ents.Create("lambda_vehicle_companion") + local name = "lambda_vc_" .. tostring(jalopy:EntIndex()) + companionController:SetName(name) + companionController:SetPos(jalopy:GetPos()) + companionController:SetKeyValue("CompanionName", "alyx") + companionController:SetParent(jalopy) + companionController:Spawn() + + jalopy:ClearAllOutputs("PlayerOff") + jalopy:ClearAllOutputs("PlayerOn") + + jalopy:Fire("AddOutput", "PlayerOn " .. name .. ",OnPlayerVehicleEnter,,0,-1") + jalopy:Fire("AddOutput", "PlayerOff " .. name .. ",OnPlayerVehicleExit,,0,-1") +end + +return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_11.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_11.lua index 3af456bb..f9243447 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_11.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_11.lua @@ -4,25 +4,103 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, - Armor = 0, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_crowbar", + "weapon_pistol", + "weapon_357", + "weapon_shotgun", + "weapon_smg1", + "weapon_ar2", + "weapon_crossbow", + }, + Ammo = { + ["Pistol"] = 20, + ["357"] = 6, + ["Buckshot"] = 18, + ["SMG1"] = 45, + ["SMG1_Grenade"] = 3, + ["AR2"] = 30, + ["Grenade"] = 3, + ["XBowBolt"] = 4, + }, + Armor = 60, HEV = true } -MAPSCRIPT.InputFilters = {} +MAPSCRIPT.InputFilters = { + ["door_silo_lab_4"] = {"Close"}, + ["brush_pclip_door_silo_lab_4"] = {"Enable"} +} + MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["global_newgame_template_base_items"] = true, + ["global_newgame_template_local_items"] = true, + ["global_newgame_template_ammo"] = true, + ["trigger_unlock_ele"] = true, + ["npcclip_alyx_hall_1"] = true } -MAPSCRIPT.GlobalStates = { -} +MAPSCRIPT.GlobalStates = {} MAPSCRIPT.Checkpoints = { + { -- Silo entrance + Pos = Vector(1490, -9934, -316), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(1514, -9936, -249), + Mins = Vector(-75, -75, -65), + Maxs = Vector(75, 75, 65) + } + }, } function MAPSCRIPT:PostInit() - print("-- Incomplete mapscript --") + -- Remove triggers that close doors + for k, v in pairs(ents.FindByName("trigger_dog_walk")) do + v:Remove() + end + for k, v in pairs(ents.FindByName("trigger_closegarage")) do + v:Remove() + end + + ents.WaitForEntityByName("trigger_gunschool", function(ent) + ent:SetKeyValue("StartDisabled", "0") + end) + + ents.WaitForEntityByName("trigger_start_entry_01", function(ent) + ent:SetKeyValue("StartDisabled", "0") + end) + + -- Elevator 1 + local elevcp = GAMEMODE:CreateCheckpoint(Vector(412, -9936, 60)) + ents.WaitForEntityByName("lift_1", function(ent) + elevcp:SetParent(ent) + end) + + ents.WaitForEntityByName("trigger_start_lift_1", function(ent) + ent:SetKeyValue("teamwait", "1") + ent.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(elevcp, activator) + end + end) + + -- Elevator no. 2 + local elevcp2 = GAMEMODE:CreateCheckpoint(Vector(1344, -9936, -561)) + ents.WaitForEntityByName("lift_2", function(ent) + elevcp2:SetParent(ent) + end) + + local elevTrigger = ents.Create("trigger_once") + elevTrigger:SetupTrigger(Vector(1324, -9936, -541), Angle(0, 0, 0), Vector(-45, -56, -73), Vector(45, 56, 73)) + elevTrigger:SetName("trigger_unlock_ele") + elevTrigger:SetKeyValue("teamwait", "1") + elevTrigger:Fire("AddOutput", "OnTrigger button_ele_2,Unlock,,0,-1") + elevTrigger.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(elevcp2, activator) + end end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_11a.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_11a.lua index 3af456bb..3ce9433e 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_11a.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_11a.lua @@ -4,25 +4,103 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, - Armor = 0, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_crowbar", + "weapon_pistol", + "weapon_shotgun", + "weapon_smg1", + "weapon_ar2", + "weapon_crossbow", + "weapon_357", + }, + Ammo = { + ["XBowBolt"] = 5, + ["AR2"] = 30, + ["SMG1_Grenade"] = 3, + ["Pistol"] = 18, + ["SMG1"] = 45, + ["357"] = 6, + ["Buckshot"] = 30, + }, + Armor = 50, HEV = true } MAPSCRIPT.InputFilters = {} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["global_newgame_template_local_items"] = true, + ["global_newgame_template_ammo"] = true, + ["global_newgame_template_base_items"] = true, + ["pclip_hall_attack_1"] = true -- Remove first door clip } - -MAPSCRIPT.GlobalStates = { -} - +MAPSCRIPT.GlobalStates = {} MAPSCRIPT.Checkpoints = { + { -- When out of duct + Pos = Vector(439, -9424, -1527), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(232, -9319, -1510), + Mins = Vector(-9, -35, -25), + Maxs = Vector(9, 35, 25) + } + }, + { -- hallways after piperoom + Pos = Vector(-1200, -9965, -1211), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-1212, -9804, -1144), + Mins = Vector(-50, -42, -64), + Maxs = Vector(50, 42, 64) + } + }, + { -- tunnel stairs after hallway + Pos = Vector(128, -11272, -1210), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(66, -11268, -1164), + Mins = Vector(-8, -60, -76), + Maxs = Vector(8, 60, 76) + } + }, + { -- out of water + Pos = Vector(1825, -11063, -1083), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(1826, -11262, -1015), + Mins = Vector(-204, -255, -55), + Maxs = Vector(204, 255, 55) + } + }, } function MAPSCRIPT:PostInit() print("-- Incomplete mapscript --") + + -- Find trigger and add CP before ambush + local atrigger + for k, v in pairs(ents.FindByPos(Vector(2360, -11256, -38.02), "trigger_once")) do + atrigger = v + end + + local aCP = GAMEMODE:CreateCheckpoint(Vector(2376, -11264, -91), Angle(0, 0, 0)) + atrigger.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(aCP, activator) + end + + -- Remove trigger for players at the end before transition + for k, v in pairs(ents.FindByName("trigger_transition_go")) do + if v:GetInternalVariable("filtername") == "" then + v:Remove() + end + end + + -- Since we removed the trigger we have to adjust the counter + ents.WaitForEntityByName("counter_labbydoor", function(ent) + ent:SetKeyValue("max", "1") + end) end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_11b.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_11b.lua index 3af456bb..4b0da75b 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_11b.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_11b.lua @@ -4,25 +4,65 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, - Armor = 0, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_crowbar", + "weapon_pistol", + "weapon_shotgun", + "weapon_smg1", + "weapon_ar2", + "weapon_crossbow", + "weapon_357", + }, + Ammo = { + ["XBowBolt"] = 5, + ["AR2"] = 30, + ["SMG1_Grenade"] = 3, + ["Pistol"] = 20, + ["SMG1"] = 45, + ["Buckshot"] = 6, + ["357"] = 6, + }, + Armor = 60, HEV = true } -MAPSCRIPT.InputFilters = {} +MAPSCRIPT.InputFilters = { + ["magnusson_courtyard_exitdoor"] = {"Close"}, + ["magnusson_courtyard_exitdoor_brush"] = {"Enable"}, +} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["global_newgame_template_local_items"] = true, + ["global_newgame_template_ammo"] = true, + ["global_newgame_template_base_items"] = true, + ["door_silo_lab_3_counter_playertrigger"] = true, -- Prevent closing the 2nd silo door + ["magnusson_ragdoll_door_playerout_trigger"] = true, -- Prevent closing the door after they go out } - -MAPSCRIPT.GlobalStates = { -} - -MAPSCRIPT.Checkpoints = { -} +MAPSCRIPT.GlobalStates = {} +MAPSCRIPT.Checkpoints = {} function MAPSCRIPT:PostInit() print("-- Incomplete mapscript --") + + -- Close the first door after everyone is in + -- Would leave it open but NPCs go in and out during scene + ents.WaitForEntityByName("trigger_player_closedoor_1", function(ent) + ent:SetKeyValue("teamwait", "1") + ent:SetKeyValue("showwait", "0") + end) + + -- Remove both triggers that close the first door + for k, v in pairs(ents.FindByName("trigger_close_tvdoor_transition_1")) do + v:Remove() + end + + -- Close the first door after everyone is out + local leaveTVroom = ents.Create("trigger_multiple") + leaveTVroom:SetupTrigger(Vector(432, -8611, -254), Angle(0, 0, 0), Vector(-224, -269, -62), Vector(224, 269, 62)) + leaveTVroom:Fire("AddOutput", "OnEndTouchAll door_silo_lab_1,Close,,0.0,-1") + end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_12.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_12.lua index 3af456bb..76c6de69 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_12.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_12.lua @@ -4,25 +4,74 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, - Armor = 0, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_rpg", + "weapon_pistol", + "weapon_crowbar", + "weapon_ar2", + "weapon_crossbow", + "weapon_357", + "weapon_shotgun", + "weapon_smg1", + }, + Ammo = { + ["lambda_health"] = 1, + ["XBowBolt"] = 9, + ["AR2"] = 60, + ["SMG1_Grenade"] = 1, + ["Buckshot"] = 18, + ["Pistol"] = 18, + ["RPG_Round"] = 3, + ["357"] = 12, + }, + Armor = 45, HEV = true } -MAPSCRIPT.InputFilters = {} +MAPSCRIPT.InputFilters = { + ["base_gate_door"] = {"Close"}, -- Dont close outside gate after battle + ["changelevel_to_12a_door"] = {"Close"} -- Keep the doors into the changelevel room open +} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["startitems"] = true, } MAPSCRIPT.GlobalStates = { } MAPSCRIPT.Checkpoints = { + { + Pos = Vector(-1219, -3919, -114), + Ang = Angle(0, 0, 0), + Trigger = { + Pos = Vector(-654, -5984, -240), + Mins = Vector(-285, -160, -80), + Maxs = Vector(285, 160, 80) + } + }, } function MAPSCRIPT:PostInit() print("-- Incomplete mapscript --") + + -- TODO: Set checkpoints based on when and which buildings/areas get destroyed by striders + -- for now, outside the base will do :) + + -- Simplify triggers near the changelevel + for k, v in pairs(ents.FindByPos(Vector(162, -8860, -256), "trigger_multiple")) do + print(k, v) + if v:GetInternalVariable("filtername") == "" then + print("removed") + v:Remove() + end + end + + ents.WaitForEntityByName("changelevel_to_12a_counter", function(ent) + ent:SetKeyValue("max", "1") + end) end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_12a.lua b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_12a.lua index 3af456bb..831a20d6 100644 --- a/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_12a.lua +++ b/gamemode/gametypes/hl2ep2/mapscripts/ep2_outland_12a.lua @@ -4,25 +4,113 @@ end local MAPSCRIPT = {} MAPSCRIPT.DefaultLoadout = { - Weapons = {"weapon_lambda_medkit", "weapon_physcannon"}, - Ammo = {}, - Armor = 0, + Weapons = { + "weapon_lambda_medkit", + "weapon_physcannon", + "weapon_pistol", + "weapon_rpg", + "weapon_357", + "weapon_crowbar", + "weapon_ar2", + "weapon_crossbow", + "weapon_frag", + "weapon_shotgun", + "weapon_smg1", + }, + Ammo = { + ["AR2"] = 60, + ["Buckshot"] = 18, + ["Grenade"] = 5, + ["SMG1"] = 90, + ["SMG1_Grenade"] = 1, + ["AR2AltFire"] = 2, + ["RPG_Round"] = 3, + ["Pistol"] = 54, + ["357"] = 12, + ["XBowBolt"] = 9, + }, + Armor = 30, HEV = true } -MAPSCRIPT.InputFilters = {} +MAPSCRIPT.InputFilters = { + ["door_silo_lab_3"] = {"Close"}, + ["door_launchbunker_exit_a"] = {"Close"} +} MAPSCRIPT.EntityFilterByClass = {} MAPSCRIPT.EntityFilterByName = { + ["startitems"] = true, + ["brush_launchbunker_exit_a"] = true, } - -MAPSCRIPT.GlobalStates = { -} - -MAPSCRIPT.Checkpoints = { -} +MAPSCRIPT.GlobalStates = {} +MAPSCRIPT.Checkpoints = {} function MAPSCRIPT:PostInit() print("-- Incomplete mapscript --") + + -- 1. elevator wait and parented checkpoint + local elev_cp = GAMEMODE:CreateCheckpoint(Vector(-376, -2180, -1559), Angle(0, 90, 0)) + ents.WaitForEntityByName("tracktrain_elevator", function(ent) + elev_cp:SetParent(ent) + end) + + ents.WaitForEntityByName("trigger_elevator_go_down", function(ent) + ent:SetKeyValue("teamwait", "1") + ent.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(elev_cp, activator) + end + end) + + -- 2. elevator wait and parented checkpoint + local elev2_cp = GAMEMODE:CreateCheckpoint(Vector(1821, -2506, -1165), Angle(0, 0, 0)) + ents.WaitForEntityByName("lift_1", function(ent) + elev2_cp:SetParent(ent) + end) + + ents.WaitForEntityByName("trigger_start_lift_1", function(ent) + ent:SetKeyValue("teamwait", "1") + ent.OnTrigger = function(_, activator) + GAMEMODE:SetPlayerCheckpoint(elev2_cp, activator) + end + end) + + -- TODO: Fix final cutscene .... lets try.... UNFINISHED + ents.WaitForEntityByName("cvehicle.hangar", function(ent) + for i = 1, game.MaxPlayers() do + local seat = ents.Create("prop_vehicle_choreo_generic") + seat:SetName("cvehicle.hangar_" .. tostring(i)) + seat:SetPos(ent:GetPos()) + seat:SetAngles(ent:GetAngles()) + seat:SetParent(ent) + seat:SetKeyValue("vehiclescript", "scripts/vehicles/choreo_vehicle_ep2_hangar.txt") + seat:SetKeyValue("VehicleLocked", "1") + seat:SetModel(ent:GetModel()) + seat:Spawn() + end + end) + + local autoParent = ents.Create("logic_auto") + autoParent:Fire("AddOutput", "OnMapSpawn cvehicle.hangar,SetParentAttachment,vehicle_driver_eyes,0.1,-1") + + local function GetNextVehicle() + local vehicles = ents.FindByName("cvehicle.hangar_*") + for _, v in pairs(vehicles) do + local driver = v:GetInternalVariable("m_hPlayer") + if IsValid(driver) == false then return v end + end + end + + GAMEMODE:WaitForInput("cvehicle.hangar", "EnterVehicle", function(ent) + for k, v in pairs(player.GetAll()) do + if v:Alive() == false then continue end + local vehicle = GetNextVehicle() + if IsValid(vehicle) then + v:EnterVehicle(vehicle) + end + end + + return true + end) end return MAPSCRIPT \ No newline at end of file diff --git a/gamemode/huds/hud_hint.lua b/gamemode/huds/hud_hint.lua index afe7f10e..81d261c7 100644 --- a/gamemode/huds/hud_hint.lua +++ b/gamemode/huds/hud_hint.lua @@ -92,7 +92,7 @@ function GM:HUDDrawHintHistory() v.y = v.y or y v.y = math.Approach(v.y, v.targetY, dt * 100) - v.elapsed = v.elapsed + (dt * 2 * v.timescale) + v.elapsed = v.elapsed + (dt * v.timescale) local delta = v.holdtime - v.elapsed delta = delta / v.holdtime local alpha = 255 diff --git a/gamemode/sh_hudhint.lua b/gamemode/sh_hudhint.lua index e4667681..d100cc6b 100644 --- a/gamemode/sh_hudhint.lua +++ b/gamemode/sh_hudhint.lua @@ -40,10 +40,8 @@ else -- CLIENT local function LocalizeWithInput(text, default) local msg = language.GetPhrase(text, default) - if msg == text then return msg end -- Process key bindings. local i = 1 - while i ~= nil do local pos = string.find(msg, "%", i, true) if pos == nil then break end diff --git a/gamemode/sh_lambda_player.lua b/gamemode/sh_lambda_player.lua index c8a3c118..a758a45b 100644 --- a/gamemode/sh_lambda_player.lua +++ b/gamemode/sh_lambda_player.lua @@ -468,7 +468,6 @@ if SERVER then ply:DisablePlayerCollide(true) -- Lets remove whatever the player left on vehicles behind before he got killed. if ply.Reviving ~= true then - self:RemovePlayerVehicles(ply) -- Should we really do this? ply.WeaponDuplication = {} ply:RemoveSuit() @@ -1098,6 +1097,12 @@ function GM:StartCommand(ply, cmd) -- offset the player for a short moment when pressing IN_DUCK again. We suppress this. if ply:Crouching() == true and ply:KeyDown(IN_DUCK) == false and cmd:KeyDown(IN_DUCK) == true then cmd:SetButtons(bit.band(cmd:GetButtons(), bit.bnot(IN_DUCK))) end ply.LastUserCmdButtons = cmd:GetButtons() + + -- Handle the vehicle take-over mechanic. + local vehicle = ply:GetVehicle() + if IsValid(vehicle) then + self:VehicleStartCommand(ply, vehicle, cmd) + end end function GM:SetupMove(ply, mv, cmd) @@ -1373,13 +1378,20 @@ function GM:CheckPlayerCollision(ply) if playersCollide == false then return end if ply:IsPlayerCollisionEnabled() == true then return end local hullMin, hullMax = ply:GetHull() + -- Extend the hull so we keep collisions off in tight spaces. + hullMin.x = hullMin.x * 2 + hullMin.y = hullMin.y * 2 + hullMax.x = hullMax.x * 2 + hullMax.y = hullMax.y * 2 local tr = util.TraceHull({ start = ply:GetPos(), endpos = ply:GetPos(), filter = ply, mins = hullMin, maxs = hullMax, - mask = MASK_SHOT_HULL + mask = MASK_SHOT_HULL, + ignoreworld = true, + collisiongroup = COLLISION_GROUP_PLAYER, }) if tr.Hit == false and tr.Fraction == 1 then @@ -1388,7 +1400,7 @@ function GM:CheckPlayerCollision(ply) DbgPrint(ply, "Reset player collision.") else DbgPrint(ply, "Colliding with " .. tostring(tr.Entity)) - ply.NextPlayerCollideTest = curTime + 2 + ply.NextPlayerCollideTest = curTime + 1 end end @@ -1607,4 +1619,40 @@ net.Receive("LambdaPlayerDamage", function(len) local hitgroup = net.ReadUInt(10) local hitpos = net.ReadVector() GAMEMODE:OnPlayerDamage(attacker, victim, hitgroup, hitpos) -end) \ No newline at end of file +end) + +if SERVER then + concommand.Add("lambda_show_loadout", function(ply, cmd, args) + -- Build the weapon table + local weps = {} + for _,v in pairs(ply:GetWeapons()) do + table.insert(weps, v:GetClass()) + end + + -- Build ammo table + local ammo = {} + for ammoId, count in pairs(ply:GetAmmo()) do + local ammoName = game.GetAmmoName(ammoId) + ammo[ammoName] = count + end + + local armor = ply:Armor() + local hev = ply:IsSuitEquipped() + + -- Print the DefaultLoadout table + print("DefaultLoadout = {") + print(" Weapons = {") + for _,v in pairs(weps) do + print(" \"" .. v .. "\",") + end + print(" },") + print(" Ammo = {") + for k,v in pairs(ammo) do + print(" [\"" .. k .. "\"] = " .. v .. ",") + end + print(" },") + print(" Armor = " .. armor .. ",") + print(" HEV = " .. tostring(hev)) + print("}") + end) +end \ No newline at end of file diff --git a/gamemode/sh_roundsystem.lua b/gamemode/sh_roundsystem.lua index 9abef4a6..a00a7cd7 100644 --- a/gamemode/sh_roundsystem.lua +++ b/gamemode/sh_roundsystem.lua @@ -162,12 +162,10 @@ if SERVER then DbgPrint("GM:CleanUpMap") -- Make sure nothing is going to create new things now self:SetRoundState(STATE_RESTARTING) - -- Remove vehicles - self:CleanUpVehicles() -- Check what we have to cleanup local filter = {} hook.Call("LambdaCleanupFilter", GAMEMODE, filter) - game.CleanUpMap(false, filter) + game.CleanUpMap(false, filter, function() end) end function GM:RoundStateBooting() @@ -315,7 +313,7 @@ if SERVER then mustComplete = true }, mapOptions, {}, function(vote, failed, timeout, winningOption) local picked = mapOptions[winningOption] - self:RequestChangeLevel(nil, picked, nil, {}) + self:RequestChangeLevel(picked, nil, {}) end) end) end @@ -387,6 +385,9 @@ function GM:PreCleanupMap() self.ChangingLevel = false if SERVER then + -- Make sure there are no pending outputs + RunConsoleCommand("ent_cancelpendingentfires") + -- Disable all overlays. for _, v in pairs(ents.FindByClass("env_screenoverlay")) do v:Input("StopOverlays") @@ -399,8 +400,8 @@ function GM:PreCleanupMap() v:KillSilent() end - -- Prevent recursions. for _, v in pairs(ents.GetAll()) do + -- Prevent recursions. v:EnableRespawn(false) end @@ -410,12 +411,12 @@ function GM:PreCleanupMap() for _, v in pairs(ents.FindByClass("logic_choreographed_scene")) do -- Cancel all scenes. DbgPrint("Cancel scene " .. tostring(v)) - v:Fire("Cancel") + v:Input("Cancel") end for _, v in pairs(ents.FindByClass("npc_*")) do DbgPrint("Cancel scripting " .. tostring(v)) - v:Fire("StopScripting") + v:Input("StopScripting") end end @@ -430,6 +431,7 @@ function GM:PreCleanupMap() self:ResetVehicleCheckpoint() self:ResetCheckpoints() self:ResetSceneCheck() + self:CleanUpVehicles() self:ClearLevelDesignerPlacedObjects() -- Reset all queued functions. util.ResetFunctionQueue() @@ -584,6 +586,7 @@ function GM:OnNewGame() -- Notify clients. self:NotifyRoundStateChanged(player.GetAll(), ROUND_INFO_NONE, {}) self:SetupRoundRelevantObjects() + self:ResetVehicleCheck() util.RunNextFrame(function() GAMEMODE:PostRoundSetup() @@ -680,11 +683,6 @@ function GM:StartRound(cleaned, force) DbgError("Unfinished booting") end - if cleaned == true then - -- Make sure map created vehicles are gone, we take over. - self:CleanUpVehicles() - end - --self.RoundState = STATE_RESTARTING self:SetRoundState(STATE_RESTARTING) end diff --git a/gamemode/sh_utils.lua b/gamemode/sh_utils.lua index 0ebbe1ed..ee923396 100644 --- a/gamemode/sh_utils.lua +++ b/gamemode/sh_utils.lua @@ -267,7 +267,19 @@ if SERVER then ["OnNewGame"] = true, ["OnGetValue"] = true, ["OnHitMax"] = true, - ["OnHitMin"] = true + ["OnHitMin"] = true, + ["PlayerOff"] = true, + ["PlayerOn"] = true, + ["OnCompanionEnteredVehicle"] = true, + ["OnCompanionExitedVehicle"] = true, + ["OnHostileEnteredVehicle"] = true, + ["OnHostileExitedVehicle"] = true, + ["PressedAttack"] = true, + ["PressedAttack2"] = true, + ["AttackAxis"] = true, + ["Attack2Axis"] = true, + ["OnPass"] = true, + ["OnChangeLevel"] = true, } function util.IsOutputValue(key) @@ -532,4 +544,30 @@ function util.GetAllPlayers() end return PLAYER_LIST_CACHE +end + +local function IsPlayerNearby(pos, ply, radius) + if ply:Alive() == false then + return false + end + if ply:GetPos():Distance(pos) <= radius then + return true + end + return false +end + +function util.IsPlayerNearby(entOrPos, radius) + local pos = entOrPos + + if IsEntity(entOrPos) then + pos = entOrPos:GetPos() + end + + for _, ply in pairs(util.GetAllPlayers()) do + if IsPlayerNearby(pos, ply, radius) then + return true + end + end + + return false end \ No newline at end of file diff --git a/gamemode/sh_vehicles.lua b/gamemode/sh_vehicles.lua index 4d13c5b1..3e8675c9 100644 --- a/gamemode/sh_vehicles.lua +++ b/gamemode/sh_vehicles.lua @@ -8,58 +8,100 @@ local CurTime = CurTime local VEHICLE_THINK = 1 VEHICLE_SPAWN_MINS = Vector(-85, -132, -40) VEHICLE_SPAWN_MAXS = Vector(85, 104, 110) -local VEHICLE_JEEP = 0 -local VEHICLE_AIRBOAT = 1 +local VEHICLE_JEEP = 1 +local VEHICLE_AIRBOAT = 2 +local VEHICLE_JALOPY = 3 +local VEHICLE_PASSENGER = 4 local NEXT_VEHICLE_SPAWN = CurTime() local VEHICLE_SPAWN_TIME = 2 +local VEHICLE_MIN_DESPAWN_DIST = 5000 ---local VEHICLE_JALOPY = 2 if SERVER then AddCSLuaFile() - function GM:InitializeMapVehicles() DbgPrint("InitializeMapVehicles") -- Because autoreload.. self.ActiveVehicles = {} self.MapVehicles = {} self.SpawnPlayerVehicles = true - local mapdata = game.GetMapData() - - for _, v in pairs(mapdata.Entities) do - if v["classname"] == "prop_vehicle_airboat" then - table.insert(self.MapVehicles, v) - elseif v["classname"] == "prop_vehicle_jeep" and v["model"] == "models/buggy.mdl" then - table.insert(self.MapVehicles, v) - elseif v["classname"] == "prop_vehicle_jalopy" then - table.insert(self.MapVehicles, v) + self.NextVehicleThink = 0 + + -- NOTE: We rely on the first vehicle spawn, this method is unreliable as in ep2_outland_09 + -- the vehicle is spawned later rather than at the start of the map. + if false then + local mapdata = game.GetMapData() + for _, v in pairs(mapdata.Entities) do + if v["classname"] == "prop_vehicle_airboat" then + table.insert(self.MapVehicles, v) + elseif v["classname"] == "prop_vehicle_jeep" and v["model"] == "models/buggy.mdl" then + table.insert(self.MapVehicles, v) + elseif v["classname"] == "prop_vehicle_jeep" and v["model"] == "models/vehicle.mdl" then + table.insert(self.MapVehicles, v) + end end end end function GM:CleanUpVehicles() DbgPrint("Cleaning up vehicles..") - - for k, _ in pairs(self.ActiveVehicles) do + for vehicle, _ in pairs(self.ActiveVehicles) do if IsValid(k) then - DbgPrint("Removing vehicle: " .. tostring(k)) - k:Remove() - local ply = k.LambdaPlayer - - if IsValid(ply) and ply.OwnedVehicle == k then - ply.OwnedVehicle = nil - end + DbgPrint("Removing vehicle: " .. tostring(vehicle)) + vehicle:ClearAllOutputs() + vehicle:Remove() else - DbgPrint("Vehicle " .. tostring(k) .. " invalid") + DbgPrint("Vehicle " .. tostring(vehicle) .. " invalid") end end + + DbgPrint("Clearing vehicle ownership..") + for _, v in pairs(util.GetAllPlayers()) do + self:PlayerSetVehicleOwned(v, nil) + end + + self.MapVehicles = {} + self:ResetVehicleCheck() + end + + function GM:ResetVehicleCheck() + self.NextVehicleThink = CurTime() + VEHICLE_THINK + end + + function GM:VehicleSetPlayerOwner(vehicle, ply) + DbgPrint("VehicleSetPlayerOwner", vehicle, ply) + vehicle:SetNWEntity("LambdaVehicleOwner", ply) + end + + function GM:PlayerSetVehicleOwned(ply, vehicle) + DbgPrint("PlayerSetVehicleOwned", ply, tostring(vehicle)) + ply:SetNWEntity("LambdaOwnedVehicle", vehicle) + end + + function GM:SetVehicleType(vehicle, vehicleType) + DbgPrint("SetVehicleType", vehicle, vehicleType) + vehicle:SetNWInt("LambdaVehicleType", vehicleType) + end + + function GM:VehicleSetPassengerSeat(vehicle, seat) + vehicle:SetNWEntity("PassengerSeat", seat) + end + + function GM:VehicleSetCanTakeOver(vehicle, canTakeOver) + vehicle:SetNWBool("LambdaVehicleTakeOver", canTakeOver) end function GM:HandleVehicleCreation(vehicle) - DbgPrint("HandleVehicleCreation") + DbgPrint("HandleVehicleCreation", tostring(vehicle)) local class = vehicle:GetClass() local vehicleType = nil if class ~= "prop_vehicle_airboat" and class ~= "prop_vehicle_jeep" then return end + -- Akward hack to know if an NPC passenger is inside. We handle this with our AcceptInput hook. + self.IsCreatingInternalOutputs = true + vehicle:Input("AddOutput", NULL, NULL, "OnCompanionEnteredVehicle !self,LambdaCompanionEnteredVehicle,,0,-1") + vehicle:Input("AddOutput", NULL, NULL, "OnCompanionExitedVehicle !self,LambdaCompanionExitedVehicle,,1,-1") + self.IsCreatingInternalOutputs = false + -- This has to be set this frame. if self.MapScript.VehicleGuns == true then DbgPrint("Enabling Gun") @@ -71,53 +113,132 @@ if SERVER then -- We only get a model next frame, delay it. util.RunNextFrame(function() if not IsValid(vehicle) then return end - local mdl = vehicle:GetModel() + local mdl = vehicle:GetModel() if class == "prop_vehicle_airboat" and mdl == "models/airboat.mdl" then vehicleType = VEHICLE_AIRBOAT elseif class == "prop_vehicle_jeep" and mdl == "models/buggy.mdl" then vehicleType = VEHICLE_JEEP + elseif class == "prop_vehicle_jeep" and mdl == "models/vehicle.mdl" then + vehicleType = VEHICLE_JALOPY else return end - self.ActiveVehicles[vehicle] = true - vehicle:SetCustomCollisionCheck(true) - - vehicle:CallOnRemove("LambdaVehicleCleanup", function(ent) - self.ActiveVehicles[ent] = nil - local ply = ent.LambdaPlayer - - if IsValid(ply) and ply.OwnedVehicle == ent then - ply.OwnedVehicle = nil + if vehicle:GetName() ~= "" and vehicle.VehicleIsCloned ~= true then + -- We have a map creation ID, we can use this to determine the vehicle type. + local mapdata = game.GetMapData() + for _, v in pairs(mapdata.Entities) do + if v["targetname"] == vehicle:GetName() then + DbgPrint("New vehicle on map: " .. tostring(v["classname"])) + table.insert(self.MapVehicles, v) + end end - end) + end + DbgPrint(vehicle, "Vehicle type: " .. tostring(vehicleType)) + self.ActiveVehicles[vehicle] = true + --vehicle:SetCustomCollisionCheck(true) + vehicle:CallOnRemove("LambdaVehicleCleanup", function(ent) self.ActiveVehicles[ent] = nil end) vehicle.AllowVehicleCheckpoint = true + self:SetVehicleType(vehicle, vehicleType) local tracker = ents.Create("lambda_vehicle_tracker") tracker:AttachToVehicle(vehicle) tracker:Spawn() - if vehicleType == VEHICLE_JEEP then self:HandleJeepCreation(vehicle) elseif vehicleType == VEHICLE_AIRBOAT then self:HandleAirboatCreation(vehicle) + elseif vehicleType == VEHICLE_JALOPY then + self:HandleJalopyCreation(vehicle) end end) end - function GM:HandleJeepCreation(jeep) - if IsValid(jeep:GetNWEntity("PassengerSeat")) then return end + local function CreatePassengerSeat(parent, localPos, localAng, mdl) local seat = ents.Create("prop_vehicle_prisoner_pod") - seat:SetPos(jeep:LocalToWorld(Vector(19.369112, -37.018456, 18.896046))) - seat:SetAngles(jeep:LocalToWorldAngles(Angle(-0.497, -3.368, 0.259))) - seat:SetModel("models/nova/jeep_seat.mdl") - seat:SetParent(jeep) + seat:SetPos(parent:LocalToWorld(localPos)) + seat:SetAngles(parent:LocalToWorldAngles(localAng)) + seat:SetModel(mdl) + seat:SetParent(parent) seat:Spawn() + -- TODO: Remove this, legacy. seat:SetNWBool("IsPassengerSeat", true) + GAMEMODE:SetVehicleType(seat, VEHICLE_PASSENGER) + return seat + end + + function GM:HandleJeepCreation(jeep) + DbgPrint("HandleJeepCreation", jeep) + + local seat = jeep:GetNWEntity("PassengerSeat") + if IsValid(seat) then + -- Already exists. + return + end + + seat = CreatePassengerSeat(jeep, Vector(19.369112, -37.018456, 18.896046), Angle(-0.497, -3.368, 0.259), "models/nova/jeep_seat.mdl") jeep:SetNWEntity("PassengerSeat", seat) end + function GM:HandleJalopyCreation(jalopy) + DbgPrint("HandleJalopyCreation", jalopy) + + local seat = jalopy:GetNWEntity("PassengerSeat") + if IsValid(seat) then + -- Already exists. + return + end + + seat = CreatePassengerSeat(jalopy, Vector(21.498613, -27.285204, 18.695107), Angle(-0.211, 0.621, -0.145), "models/nova/jalopy_seat.mdl") + -- Make it invisible, we just want the functionality. + seat:SetNoDraw(true) + jalopy:SetNWEntity("PassengerSeat", seat) + + local mapScript = self.MapScript + if mapScript ~= nil and mapScript.OnJalopyCreated ~= nil then + mapScript:OnJalopyCreated(jalopy) + end + end + + function GM:OnCompanionEnteredVehicle(jalopy, passenger) + DbgPrint("Companion entered vehicle") + + jalopy:SetNWEntity("LambdaPassenger", passenger) + passenger:SetNWEntity("LambdaVehicle", jalopy) + local seat = jalopy:GetNWEntity("PassengerSeat") + if IsValid(seat) then + -- Lock this seat as we have the passenger inside the vehicle. + seat:Fire("Lock") + else + DbgPrint("No passenger seat on jalopy: " .. tostring(jalopy)) + end + end + + function GM:OnCompanionExitedVehicle(jalopy, passenger) + DbgPrint("Companion exited vehicle") + + jalopy:SetNWEntity("LambdaPassenger", NULL) + passenger:SetNWEntity("LambdaVehicle", NULL) + local seat = jalopy:GetNWEntity("PassengerSeat") + if IsValid(seat) then + -- Lock this seat as we have the passenger inside the vehicle. + seat:Fire("Unlock") + end + end + + function GM:OnPlayerPassengerEnteredVehicle(ply, vehicle, seat) + DbgPrint("Player entered passenger seat", ply, vehicle, seat) + -- Prevent NPC companions from entering as passengers. + vehicle:Fire("LockEntrance") + end + + function GM:OnPlayerPassengerExitedVehicle(ply, vehicle, seat) + DbgPrint("Player exited passenger seat", ply, vehicle, seat) + -- Allow NPC companions to enter as passengers. + vehicle:Fire("UnlockEntrance") + end + function GM:HandleAirboatCreation(airboat) end @@ -126,74 +247,81 @@ if SERVER then -- Allow players to enter the passenger seat directly by swapping it. local passengerSeat = vehicle:GetNWEntity("PassengerSeat") if IsValid(passengerSeat) and passengerSeat:GetDriver() == NULL then return passengerSeat end - return vehicle end function GM:PlayerEnteredVehicle(ply, vehicle, role) - if ply:IsSprinting() == true then - ply:StopSprinting() + DbgPrint("PlayerEnteredVehicle", ply, vehicle, role) + if ply:IsSprinting() == true then ply:StopSprinting() end + local vehicleType = self:VehicleGetType(vehicle) + if vehicleType == nil then + -- Nothing to do here. + DbgPrint("Vehicle type is nil") + return end - if vehicle:GetClass() == "prop_vehicle_jeep" or vehicle:GetClass() == "prop_vehicle_airboat" or vehicle:GetClass() == "prop_vehicle_jalopy" then - if vehicle.LambdaPlayer ~= nil and vehicle.LambdaPlayer ~= ply then - DbgError("Bogus vehicle logic: Player entering vehicle that does not belong to him") - elseif vehicle.LambdaPlayer == nil then + if vehicleType ~= VEHICLE_PASSENGER then + -- Clear the previous vehicle owner. + local prevVehicle = self:PlayerGetVehicleOwned(ply) + if IsValid(prevVehicle) and prevVehicle ~= vehicle then + DbgPrint("Player owns another vehicle, clearing ownership", prevVehicle) + self:VehicleSetPlayerOwner(prevVehicle, NULL) + end + + local owner = self:VehicleGetPlayerOwner(vehicle) + if IsValid(owner) and owner ~= ply then + DbgError("Bogus vehicle logic: Player entering vehicle that does not belong to him", owner, ply, vehicle) + elseif not IsValid(owner) then -- Now belongs to the specific player. DbgPrint("Player " .. tostring(ply) .. " gets ownership of vehicle: " .. tostring(vehicle)) - vehicle.LambdaPlayer = ply - ply.OwnedVehicle = vehicle - ply:SetNWEntity("LambdaOwnedVehicle", vehicle) + self:VehicleSetPlayerOwner(vehicle, ply) + self:PlayerSetVehicleOwned(ply, vehicle) end end local ang = vehicle:GetForward():Angle() ang = vehicle:WorldToLocalAngles(ang) ply:SetEyeAngles(ang) - - if self.MapScript ~= nil and self.MapScript.OnEnteredVehicle ~= nil then - self.MapScript:OnEnteredVehicle(ply, vehicle, role) + if self:VehicleIsPassengerSeat(vehicle) then + local parent = vehicle:GetParent() + if IsValid(parent) and parent:GetNWEntity("PassengerSeat") == vehicle then self:OnPlayerPassengerEnteredVehicle(ply, parent, vehicle) end end + + if self.MapScript ~= nil and self.MapScript.OnEnteredVehicle ~= nil then self.MapScript:OnEnteredVehicle(ply, vehicle, role) end end function GM:CanExitVehicle(vehicle, ply) DbgPrint("CanPlayerExitVehicle", vehicle, ply) local locked = vehicle:GetInternalVariable("vehiclelocked") if locked ~= nil then return locked == false end - return true end function GM:PlayerLeaveVehicle(ply, vehicle) -- Reset, we disabled it for transition probably - DbgPrint("Player leave: " .. tostring(ply)) - + DbgPrint("PlayerLeaveVehicle", ply, vehicle) if vehicle.ResetVehicleEntryAnim == true then vehicle:SetVehicleEntryAnim(true) end + local vehicleType = self:VehicleGetType(vehicle) + if vehicleType == nil then + -- Nothing to do here. + DbgPrint("Vehicle type is nil", vehicle) + return + end + if ply:Alive() == false then - DbgPrint("Player who left is now dead, we shall remove this") - -- We give the driver a chance to pick up this vehicle. - local passengerSeat = vehicle:GetNWEntity("PassengerSeat") - - if IsValid(passengerSeat) then - local passenger = passengerSeat:GetDriver() - - if IsValid(passenger) and passenger:IsPlayer() then - DbgPrint("Giving passenger temporary ownership of vehicle") - vehicle.LambdaPlayer = nil - vehicle.LambdaAllowEnter = passenger - ply.OwnedVehicle = nil - ply:SetNWEntity("LambdaOwnedVehicle", nil) - end + if vehicleType ~= VEHICLE_PASSENGER then + self:VehicleSetPlayerOwner(vehicle, NULL) + self:VehicleDriverKilled(vehicle, ply) end else -- Make sure players won't collide if they exit strangely. ply:DisablePlayerCollide(true) end - if ply:Alive() and vehicle:GetNWBool("IsPassengerSeat", false) == true then + if ply:Alive() and vehicleType == VEHICLE_PASSENGER then local ang = vehicle:GetAngles() local pos = vehicle:GetPos() local exitpos = pos + (ang:Forward() * 50) @@ -201,119 +329,132 @@ if SERVER then local exitang = (pos - exitpos):Angle() ply:TeleportPlayer(exitpos, exitang) vehicle:GetParent().Passenger = nil + local parent = vehicle:GetParent() + if IsValid(parent) and self:VehicleGetPassengerSeat(parent) == vehicle then self:OnPlayerPassengerExitedVehicle(ply, parent, vehicle) end end end function GM:CanPlayerEnterVehicle(ply, vehicle, role) DbgPrint("CanPlayerEnterVehicle", ply, vehicle) - - if ply.OwnedVehicle ~= nil and IsValid(ply.OwnedVehicle) == false then - -- Just to make sure, its possible it might error somewhere and did not unassign it. - ply.OwnedVehicle = nil + -- Disallow by default. + ply:SetAllowWeaponsInVehicle(false) + local vehicleType = self:VehicleGetType(vehicle) + if vehicleType == nil then + -- Nothing to do here. + return true end - if vehicle:GetNWBool("IsPassengerSeat", false) == true then - vehicle:SetKeyValue("limitview", "0") - ply:SetAllowWeaponsInVehicle(true) - else - ply:SetAllowWeaponsInVehicle(false) - end - - if vehicle.SetVehicleEntryAnim ~= nil then - vehicle.ResetVehicleEntryAnim = true - vehicle:SetVehicleEntryAnim(false) - else - vehicle.ResetVehicleEntryAnim = false - end + DbgPrint(vehicle, "Vehicle Type: " .. tostring(vehicleType)) - if vehicle:GetClass() == "prop_vehicle_jeep" or vehicle:GetClass() == "prop_vehicle_airboat" or vehicle:GetClass() == "prop_vehicle_jalopy" then - if vehicle.LambdaPlayer == nil and ply.OwnedVehicle == nil then - -- Not yet owned. - return true - elseif vehicle.LambdaPlayer == nil and ply.OwnedVehicle ~= nil then - if vehicle.LambdaAllowEnter == ply then - -- Important to set this, RemovePlayerVehicles cleans up those where we are allowed to enter. - vehicle.LambdaAllowEnter = nil - -- Remove his old vehicle. - self:RemovePlayerVehicles(ply) - - return true - end - - -- TODO: Add notification of whats happening. + if vehicleType ~= VEHICLE_PASSENGER then + local vehicleOwner = self:VehicleGetPlayerOwner(vehicle) + -- Check if the vehicle is owned by someone else. + if IsValid(vehicleOwner) and vehicleOwner ~= ply then + DbgPrint("Player not allowed to enter vehicle, owned by: " .. tostring(vehicleOwner)) + -- If this is true it will start the engine and release the handbrake despite + -- the player not being able to enter the vehicle. + vehicle:SetVehicleEntryAnim(false) return false - elseif vehicle.LambdaPlayer ~= nil then - -- Check if we own the vehicle. - if vehicle.LambdaPlayer == ply then - DbgPrint("Player can enter") + end - return true - else - DbgPrint("Player not allowed to enter") + -- Check if the vehicle is already owned by us. + if vehicleOwner == ply then + DbgPrint("Player owns the vehicle") + return true + end + -- Check if the player already owns a vehicle other than this one. + local canTakeOver = self:VehicleCanTakeOver(vehicle, ply) + if not canTakeOver then + local playerVehicle = self:PlayerGetVehicleOwned(ply) + if IsValid(playerVehicle) and playerVehicle ~= vehicle then + DbgPrint("Player already owns a vehicle") + ply:EmitSound("HL2Player.UseDeny") + vehicle:SetVehicleEntryAnim(false) return false end + else + DbgPrint("Player " .. tostring(ply) .. " can take over vehicle: " .. tostring(vehicle)) + end + elseif vehicleType == VEHICLE_PASSENGER then + vehicle:SetKeyValue("limitview", "0") + ply:SetAllowWeaponsInVehicle(true) + if vehicle.SetVehicleEntryAnim ~= nil then + vehicle.ResetVehicleEntryAnim = true + vehicle:SetVehicleEntryAnim(false) + else + vehicle.ResetVehicleEntryAnim = false end end - return true end function GM:RemovePlayerVehicles(ply) DbgPrint("GM:RemovePlayerVehicles", ply) - for vehicle, _ in pairs(self.ActiveVehicles) do - if vehicle.LambdaPlayer == ply then + local vehicleOwner = self:VehicleGetPlayerOwner(vehicle) + local passenger = self:VehicleGetPassenger(vehicle) + if vehicleOwner == ply and not IsValid(passenger) then DbgPrint("Removing player vehicle: " .. tostring(vehicle)) - vehicle:Remove() - elseif vehicle.LambdaAllowEnter == ply and vehicle.LambdaPlayer == nil then - DbgPrint("Passenger took no ownership of the vehicle, no owner, removing.") + self:VehicleSetPlayerOwner(vehicle, NULL) vehicle:Remove() end end - ply.OwnedVehicle = nil - ply:SetNWEntity("LambdaOwnedVehicle", nil) + self:PlayerSetVehicleOwned(ply, nil) end function GM:SetSpawnPlayerVehicles(state) self.SpawnPlayerVehicles = state + if self.SpawnPlayerVehicles == nil then self.SpawnPlayerVehicles = true end + end - if self.SpawnPlayerVehicles == nil then - self.SpawnPlayerVehicles = true + function GM:GetMaxVehicleCount() + if self.MapVehicles == nil or #self.MapVehicles == 0 then + -- No vehicles on this map. + return 0 + end + -- TODO: Should we always have one extra in reserve? Its possible that a player + -- dies far away from the vehicle but has other players around that prevents a despawn. + local alive = 0 + for _, ply in pairs(util.GetAllPlayers()) do + if ply:Alive() then alive = alive + 1 end end + return alive end function GM:CanSpawnVehicle() if CurTime() < NEXT_VEHICLE_SPAWN then return false end - local alivePlayers = 0 - local playerCount = 0 - for _, v in pairs(util.GetAllPlayers()) do - if v:Alive() then - alivePlayers = alivePlayers + 1 - end + local maxVehicles = self:GetMaxVehicleCount() + if maxVehicles == 0 then + -- No vehicles on this map. + return false + end - playerCount = playerCount + 1 + if self.SpawnPlayerVehicles ~= true then + -- We are not allowed to spawn vehicles. + return false end - if alivePlayers == 0 then return false end - if self.SpawnPlayerVehicles ~= true then return false end - if table.Count(self.ActiveVehicles) < playerCount then return true end + if table.Count(self.ActiveVehicles) >= maxVehicles then + -- Exhausted. + return false + end - return false + return true end - function GM:SpawnVehicleAtSpot(vehicle) - local pos = util.StringToType(vehicle["origin"], "Vector") - - if self.VehicleCheckpoint ~= nil then - pos = self.VehicleCheckpoint.Pos - end + function GM:GetVehicleSpawnPos() + if self.VehicleCheckpoint ~= nil then return self.VehicleCheckpoint.Pos end + if #self.MapVehicles == 0 then return nil end + return util.StringToType(self.MapVehicles[1]["origin"], "Vector") + end + function GM:SpawnVehicleAtSpot(vehicle) + local pos = self:GetVehicleSpawnPos() -- Check if there is already one. local nearbyEnts = ents.FindInBox(pos + VEHICLE_SPAWN_MINS, pos + VEHICLE_SPAWN_MAXS) - for _, v in pairs(nearbyEnts) do if v:GetClass() == vehicle["classname"] then -- We normally dont want this, but its possible to spawn two of them at the same spot. @@ -323,99 +464,96 @@ if SERVER then -- The box is somewhat big, we should deal with players standing directly in the way. end + DbgPrint("Spawning vehicle at spot: " .. tostring(pos)) local newVehicle = ents.CreateFromData(vehicle) - + newVehicle.VehicleIsCloned = true if self.VehicleCheckpoint ~= nil then newVehicle:SetPos(self.VehicleCheckpoint.Pos) newVehicle:SetAngles(self.VehicleCheckpoint.Ang) end - if self.EnableVehicleGuns then - newVehicle:SetKeyValue("EnableGun", "1") - end - + if self.EnableVehicleGuns then newVehicle:SetKeyValue("EnableGun", "1") end newVehicle:Spawn() newVehicle:Activate() DbgPrint("Created new vehicle: " .. tostring(newVehicle)) NEXT_VEHICLE_SPAWN = CurTime() + VEHICLE_SPAWN_TIME end - function GM:VehiclesThink() - if self:IsRoundRunning() == false and self:RoundElapsedTime() >= 1 then return end - local curTime = CurTime() - self.NextVehicleThink = self.NextVehicleThink or (curTime + VEHICLE_THINK) - if curTime < self.NextVehicleThink then return end - self.NextVehicleThink = curTime + VEHICLE_THINK + function GM:CheckRemoveVehicle(vehicle, ignoreChecks) + local passenger = self:VehicleGetPassenger(vehicle) + if IsValid(passenger) then + -- Never remove vehicles that have a passenger. + return false + end - if self:CanSpawnVehicle() then - for _, v in pairs(self.MapVehicles) do - self:SpawnVehicleAtSpot(v) + local vehiclePos = vehicle:GetPos() + local vehicleOwner = self:VehicleGetPlayerOwner(vehicle) + if IsValid(vehicleOwner) then + -- Check how far away the vehicle owner is. + local ownerDist = vehicleOwner:GetPos():Distance(vehiclePos) + if ownerDist < VEHICLE_MIN_DESPAWN_DIST then + -- Owner is close, don't remove. + return false end end - local playerPosTable = {} - local centerPos = Vector(0, 0, 0) - local inVehicle = 0 - local alivePlayers = 0 - - for _, v in pairs(player.GetAll()) do - if v:Alive() == false then continue end - alivePlayers = alivePlayers + 1 + if vehicleOwner ~= NULL and not IsValid(vehicleOwner) then + -- Probably disconnected client. + self:VehicleSetPlayerOwner(vehicle, NULL) + end - if v:InVehicle() == true then - inVehicle = inVehicle + 1 + local spawnPos = self:GetVehicleSpawnPos() + if spawnPos ~= nil then + -- See if the checkpoint has moved. + local dist = vehiclePos:Distance(spawnPos) + if dist < 512 then + -- Vehicle is close to the spawn point. + return false end + end - local pos = v:GetPos() - table.insert(playerPosTable, pos) - centerPos = centerPos + pos + if ignoreChecks ~= true then + -- Check if there are players nearby. + if util.IsPlayerNearby(vehiclePos, VEHICLE_MIN_DESPAWN_DIST) then + -- Players are nearby, don't remove. + return + end end - if #playerPosTable > 0 then - centerPos = centerPos / #playerPosTable + DbgPrint("Removing vehicle " .. tostring(vehicle) .. " because it is too far away from the owner.") + vehicle:Remove() + + return true + end + + function GM:VehiclesThink() + if self:IsRoundRunning() == false and self:RoundElapsedTime() >= 1 then return end + if #self.MapVehicles == 0 then + -- No vehicles on this map. + return end - -- Make sure we clean up vehicles from disconnected players. - for vehicle, _ in pairs(self.ActiveVehicles or {}) do - if vehicle.LambdaPlayer ~= nil then - local ply = vehicle.LambdaPlayer + local plyCount = player.GetCount() + local vehicleCount = table.Count(self.ActiveVehicles) - if not IsValid(ply) then - DbgPrint("Removing player vehicle") - vehicle:Remove() - end + local curTime = CurTime() + if curTime < self.NextVehicleThink then return end + self.NextVehicleThink = curTime + VEHICLE_THINK + for vehicle, _ in pairs(self.ActiveVehicles) do + if not IsValid(vehicle) then + self.ActiveVehicles[vehicle] = nil else - -- If the player is too far away from an unowned vehicle remove it. - if #playerPosTable > 0 and inVehicle < alivePlayers then - local vehiclePos = vehicle:GetPos() - local centerDist = centerPos:Distance(vehiclePos) - local nearby = false - - if centerDist > 1024 then - for _, p in pairs(playerPosTable) do - local dist = p:Distance(vehiclePos) - - if dist < 4000 then - nearby = true - break - end - end - - -- If no player is nearby we can remove the old unowned vehicle. - if nearby == false then - vehicle:Remove() - continue - end - end + local additionalChecks = vehicleCount > plyCount + if self:CheckRemoveVehicle(vehicle, additionalChecks) then + vehicleCount = vehicleCount - 1 end end - -- Commented, util.IsInWorld is not reliable at all. - --[[ - if util.IsInWorld(vehicle:GetPos()) == false then - DbgPrint("Removing out of world vehicle") - vehicle:Remove() + end + + if self:CanSpawnVehicle() then + for _, v in pairs(self.MapVehicles) do + self:SpawnVehicleAtSpot(v) end - ]] end end else -- CLIENT @@ -424,17 +562,16 @@ else -- CLIENT if ply.VehicleSteeringView == true then local viewPos = view.origin local headBone = ply:LookupBone("ValveBiped.Bip01_Head1") - - if headBone ~= nil then - viewPos = ply:GetBonePosition(headBone) - end - + if headBone ~= nil then viewPos = ply:GetBonePosition(headBone) end view.origin = viewPos + (view.angles:Forward() * 3) end -- Don't roll the camera view.angles.z = 0 - if vehicle.GetThirdPersonMode == nil or ply:GetViewEntity() ~= ply then return end -- This should never happen. + if vehicle.GetThirdPersonMode == nil or ply:GetViewEntity() ~= ply then -- This should never happen. + return + end + if vehicle:GetThirdPersonMode() == false then return view end local mn, mx = vehicle:GetRenderBounds() local radius = (mn - mx):Length() @@ -442,13 +579,11 @@ else -- CLIENT -- Trace back from the original eye position, so we don't clip through walls/objects local TargetOrigin = view.origin + (view.angles:Forward() * -radius) local WallOffset = 4 - local tr = util.TraceHull({ start = view.origin, endpos = TargetOrigin, filter = function(e) local c = e:GetClass() -- Avoid contact with entities that can potentially be attached to the vehicle. Ideally, we should check if "e" is constrained to "Vehicle". - return not c:StartWith("prop_physics") and not c:StartWith("prop_dynamic") and not c:StartWith("prop_ragdoll") and not e:IsVehicle() and not c:StartWith("gmod_") end, mins = Vector(-WallOffset, -WallOffset, -WallOffset), @@ -457,14 +592,10 @@ else -- CLIENT view.origin = tr.HitPos view.drawviewer = true - -- -- If the trace hit something, put the camera there. -- - if tr.Hit and not tr.StartSolid then - view.origin = view.origin + tr.HitNormal * WallOffset - end - + if tr.Hit and not tr.StartSolid then view.origin = view.origin + tr.HitNormal * WallOffset end return view end end @@ -476,23 +607,83 @@ function GM:VehicleShouldCollide(veh1, veh2) end end +function GM:VehicleDriverKilled(vehicle, ply) + DbgPrint("Vehicle driver dead", ply, vehicle) + local vehicleType = self:VehicleGetType(vehicle) + if vehicleType == VEHICLE_PASSENGER then + -- Do nothing for passenger seats. + return + end + -- Check if we have a passenger, notify him that he can take over. + local passenger = self:VehicleGetPassenger(vehicle) + if IsValid(passenger) then + DbgPrint("Notifying passenger") + -- Send a hint that the passenger can take over. + self:AddHint("#LAMBDA_VEHICLE_TAKE_OVER", 7, passenger) + end + -- Allow vehicle to be taken over. + self:VehicleSetCanTakeOver(vehicle, true) +end + +function GM:VehiclePromotePassenger(ply, vehicle) + local parent = vehicle:GetParent() + if not IsValid(parent) then + DbgError("VehiclePromotePassenger: Invalid parent") + return + end + if IsValid(parent:GetDriver()) then + DbgError("VehiclePromotePassenger: Parent already has a driver") + return + end + local owner = self:VehicleGetPlayerOwner(vehicle) + if IsValid(owner) then + DbgError("VehiclePromotePassenger: Vehicle has an owner") + return + end + util.RunNextFrame(function() + -- Enter the driver side. + ply:EnterVehicle(parent) + end) +end + +function GM:VehicleStartCommand(ply, vehicle, cmd) + -- Handle the vehicle take-over mechanic. + local mainVehicle = vehicle:GetParent() + if not IsValid(mainVehicle) then + return + end + + if not (cmd:KeyDown(IN_USE) and cmd:KeyDown(IN_SPEED)) then + return + end + + if self:VehicleIsPassengerSeat(vehicle) then + if self:VehicleCanTakeOver(mainVehicle, ply) then + if SERVER then + self:VehiclePromotePassenger(ply, vehicle) + end + else + DbgPrint("Not allowed to take-over vehicle") + -- Remove the keys from the player. + cmd:RemoveKey(IN_USE) + cmd:RemoveKey(IN_SPEED) + end + end +end + function GM:VehicleMove(ply, vehicle, mv) + local modified = false -- We have to call it here because PlayerTick wont be called if we are inside a vehicle. self:UpdateSuit(ply, mv) self:PlayerWeaponTick(ply, mv) - -- -- On duck toggle third person view -- - if mv:KeyPressed(IN_DUCK) and vehicle.SetThirdPersonMode then - vehicle:SetThirdPersonMode(not vehicle:GetThirdPersonMode()) - end - + if mv:KeyPressed(IN_DUCK) and vehicle.SetThirdPersonMode then vehicle:SetThirdPersonMode(not vehicle:GetThirdPersonMode()) end -- -- Adjust the camera distance with the mouse wheel -- local iWheel = ply:GetCurrentCommand():GetMouseWheel() - if iWheel ~= 0 and vehicle.SetCameraDistance then -- The distance is a multiplier -- Actual camera distance = ( renderradius + renderradius * dist ) @@ -504,18 +695,79 @@ function GM:VehicleMove(ply, vehicle, mv) if ply:IsPositionLocked() ~= true then return end mv:SetButtons(0) local phys = vehicle:GetPhysicsObject() - if IsValid(phys) then local vel = phys:GetVelocity() local len = vel:Length() - if vel:Length() >= 1 then vel = vel - (vel * 0.015) phys:SetVelocity(vel) end - if len < 50 and (vehicle:GetClass() == "prop_vehicle_jeep" or vehicle:GetClass() == "prop_vehicle_jalopy") then - vehicle:Fire("HandBrakeOn") - end + if len < 50 and (vehicle:GetClass() == "prop_vehicle_jeep" or vehicle:GetClass() == "prop_vehicle_jalopy") then vehicle:Fire("HandBrakeOn") end + end + return modified +end + +function GM:VehicleGetPassenger(vehicle) + local npcPassenger = vehicle:GetNWEntity("LambdaPassenger") + if IsValid(npcPassenger) then + -- There is an NPC sitting in the vehicle. + return npcPassenger + end + + local seat = vehicle:GetNWEntity("PassengerSeat") + if IsValid(seat) then return seat:GetDriver() end + return nil +end + +function GM:VehicleGetPlayerOwner(vehicle) + if not IsValid(vehicle) then + DbgError("VehicleGetPlayerOwner: Invalid vehicle") + return nil + end + return vehicle:GetNWEntity("LambdaVehicleOwner", nil) +end + +function GM:PlayerGetVehicleOwned(ply) + if not IsValid(ply) then + DbgError("PlayerGetVehicleOwned: Invalid player") + return nil + end + return ply:GetNWEntity("LambdaOwnedVehicle", nil) +end + +function GM:VehicleGetType(vehicle) + if not IsValid(vehicle) then + DbgError("VehicleGetType: Invalid vehicle") + return nil + end + + if vehicle:GetNWBool("IsPassengerSeat", false) then return VEHICLE_PASSENGER end + local res = vehicle:GetNWInt("LambdaVehicleType", -1) + if res == -1 then + return nil + end + return res +end + +function GM:VehicleIsPassengerSeat(vehicle) + return self:VehicleGetType(vehicle) == VEHICLE_PASSENGER +end + +function GM:VehicleGetPassengerSeat(vehicle) + return vehicle:GetNWEntity("PassengerSeat") +end + +function GM:VehicleCanTakeOver(vehicle, ply) + local owner = self:VehicleGetPlayerOwner(vehicle) + if not IsValid(owner) and not IsValid(self:PlayerGetVehicleOwned(ply)) then + -- If there is no owner we can always take it. + return true + end + if owner == ply then + -- If we are the owner we can always take it. + return true end + -- If the driver died in a vehicle we can take it over. + return vehicle:GetNWBool("LambdaVehicleTakeOver", false) end \ No newline at end of file diff --git a/gamemode/shared.lua b/gamemode/shared.lua index 0b0b64a6..35248788 100644 --- a/gamemode/shared.lua +++ b/gamemode/shared.lua @@ -185,6 +185,10 @@ function GM:OnReloaded() self:SetGameType(lambda_gametype:GetString()) self:InitSettings() self:InitializeDifficulty() + + if SERVER then + self:ReloadCheckpoints() + end end function GM:Tick() @@ -507,7 +511,7 @@ function GM:OnEntityCreated(ent) end) -- Deal with vehicles at the same frame, sometimes it wouldn't show the gun. - if ent:IsVehicle() then + if ent:IsVehicle() and ent.CreatedByLevelTransition ~= true then self:HandleVehicleCreation(ent) end end @@ -553,6 +557,15 @@ end function GM:EntityKeyValue(ent, key, val) self:ConflictRecovery() + local gameType = self:GetGameType() + if gameType.ModelRemapping ~= nil and key:iequals("model") then + local newModel = gameType.ModelRemapping[val] + if newModel ~= nil then + DbgPrint("Remapping model: " .. val .. " to " .. newModel) + return newModel + end + end + if self.MapScript then -- Monitor scripts that we have filtered by class name. if key:iequals("classname") == true then diff --git a/gamemode/sv_changelevel.lua b/gamemode/sv_changelevel.lua index 6287a07c..adb643bd 100644 --- a/gamemode/sv_changelevel.lua +++ b/gamemode/sv_changelevel.lua @@ -26,9 +26,12 @@ function GM:InitializeCurrentLevel() end self.ChangingLevel = false - util.RemovePData("Lambda" .. lambda_instance_id:GetString(), "Changelevel") - util.RemovePData("Lambda" .. lambda_instance_id:GetString(), "Landmark") - util.RemovePData("Lambda" .. lambda_instance_id:GetString(), "PrevMap") + if g_debug_transitions:GetBool() == false then + util.RemovePData("Lambda" .. lambda_instance_id:GetString(), "Changelevel") + util.RemovePData("Lambda" .. lambda_instance_id:GetString(), "Landmark") + util.RemovePData("Lambda" .. lambda_instance_id:GetString(), "PrevMap") + end + DbgPrint("Used Changelevel: " .. tostring(self.IsChangeLevel)) self:InitializeTransitionData() end @@ -107,8 +110,8 @@ function GM:EnablePreviousMap() end end -function GM:PreChangelevel(activator, map, landmark, playersInTrigger, restart) - DbgPrint("GM:PreChangelevel", activator, map, landmark, playersInTrigger, restart) +function GM:PreChangelevel(map, landmark, playersInTrigger, restart) + DbgPrint("GM:PreChangelevel", map, landmark, playersInTrigger, restart) util.SetPData("Lambda" .. lambda_instance_id:GetString(), "PrevMap", self:GetCurrentMap()) util.SetPData("Lambda" .. lambda_instance_id:GetString(), "NextMap", map) util.SetPData("Lambda" .. lambda_instance_id:GetString(), "Landmark", landmark) @@ -120,20 +123,20 @@ function GM:PreChangelevel(activator, map, landmark, playersInTrigger, restart) end hook.Call("LambdaPreChangelevel", GAMEMODE, map, landmark, restart) - self:TransitionToLevel(activator, map, landmark, playersInTrigger, restart) + self:TransitionToLevel(map, landmark, playersInTrigger, restart) end -function GM:RequestChangeLevel(activator, map, landmark, playersInTrigger, restart) +function GM:RequestChangeLevel(map, landmark, playersInTrigger, restart) if self.ChangingLevel == true then return end if playersInTrigger == nil then playersInTrigger = {} end - DbgPrint("GM:ChangeLevel", activator, map, landmark, playersInTrigger, restart) + DbgPrint("GM:ChangeLevel", map, landmark, playersInTrigger, restart) self.ChangingLevel = true DbgPrint("Changing to level: " .. map) - self:PreChangelevel(activator, map, landmark, playersInTrigger, restart) + self:PreChangelevel(map, landmark, playersInTrigger, restart) if g_debug_transitions:GetBool() ~= true then local changeLevelDelay = self:GetSetting("changelevel_delay", 0) @@ -153,9 +156,9 @@ function GM:ChangeToNextLevel() if v.TargetMap == nextMap then local landmark = v.Landmark - return self:RequestChangeLevel(nil, nextMap, landmark, {}) + return self:RequestChangeLevel(nextMap, landmark, {}) end end - return self:RequestChangeLevel(nil, nextMap, nil, {}) + return self:RequestChangeLevel(nextMap, nil, {}) end \ No newline at end of file diff --git a/gamemode/sv_checkpoints.lua b/gamemode/sv_checkpoints.lua index f345b035..7c18345d 100644 --- a/gamemode/sv_checkpoints.lua +++ b/gamemode/sv_checkpoints.lua @@ -66,6 +66,7 @@ function GM:ResetCheckpoints() self.CurrentCheckpoint = nil self.CurrentCheckpointPos = nil self.NextCheckpointTest = nil + self.CheckpointInstances = {} end function GM:CreatePlayerCheckpoint(data) @@ -106,6 +107,22 @@ local function CreateCheckpointWithTrigger(data) error("Missing Ang in data") end + if data.Condition ~= nil and isfunction(data.Condition) then + if data.Condition() == false then + return + end + end + + local vehicleData = data.Vehicle + if vehicleData ~= nil then + if vehicleData.Pos == nil then + error("Missing Vehicle.Pos in data") + end + if vehicleData.Ang == nil then + error("Missing Vehicle.Ang in data") + end + end + local triggerData = data.Trigger if triggerData == nil then @@ -125,17 +142,78 @@ local function CreateCheckpointWithTrigger(data) end local checkpoint = GAMEMODE:CreateCheckpoint(data.Pos, data.Ang) + if data.RenderPos ~= nil then + checkpoint:SetRenderPos(data.RenderPos) + end + local checkpointTrigger = ents.Create("trigger_once") checkpointTrigger:SetupTrigger(triggerData.Pos, triggerData.Ang or Angle(0, 0, 0), triggerData.Mins, triggerData.Maxs) checkpointTrigger.OnTrigger = function(_, activator) GAMEMODE:SetPlayerCheckpoint(checkpoint, activator) + + if vehicleData ~= nil then + GAMEMODE:SetVehicleCheckpoint(vehicleData.Pos, vehicleData.Ang) + end + + local mapscript = GAMEMODE:GetMapScript() + if mapscript ~= nil and mapscript.DefaultLoadout ~= nil then + local loadoutWeapons = mapscript.DefaultLoadout.Weapons + -- Handle additions to the weapon loadout. + if data.WeaponAdditions ~= nil and #data.WeaponAdditions > 0 then + DbgPrint("Extending loadout...") + for _, v in pairs(data.WeaponAdditions) do + if table.HasValue(loadoutWeapons, v) == false then + table.insert(loadoutWeapons, v) + DbgPrint("Added weapon to loadout: " .. v) + end + end + end + -- Handle removal of weapons in the loadout. + if data.WeaponRemovals ~= nil and #data.WeaponRemovals > 0 then + DbgPrint("Removing weapons from loadout...") + for _, v in pairs(data.WeaponRemovals) do + table.RemoveByValue(loadoutWeapons, v) + DbgPrint("Removed weapon from loadout: " .. v) + end + end + end end + + local checkpointData = { + Checkpoint = checkpoint, + Trigger = checkpointTrigger + } + + return checkpointData end function GM:CreateCheckpointsFromData(data) for _, cp in pairs(data) do - CreateCheckpointWithTrigger(cp) + local instance = CreateCheckpointWithTrigger(cp) + if instance ~= nil then + table.insert(self.CheckpointInstances, instance) + end + end +end + +function GM:ReloadCheckpoints() + local mapScript = self:GetMapScript() + if mapScript == nil then return end + + -- Remove old ones. + self.CheckpointInstances = self.CheckpointInstances or {} + for _, v in pairs(self.CheckpointInstances) do + if IsValid(v.Trigger) then + v.Trigger:Remove() + end + if IsValid(v.Checkpoint) then + v.Checkpoint:Remove() + end + end + + if mapScript.Checkpoints ~= nil then + self:CreateCheckpointsFromData(mapScript.Checkpoints) end end diff --git a/gamemode/sv_inputoutput.lua b/gamemode/sv_inputoutput.lua index 03373505..ce56e03f 100644 --- a/gamemode/sv_inputoutput.lua +++ b/gamemode/sv_inputoutput.lua @@ -36,7 +36,6 @@ function GM:AcceptInput(ent, inputName, activator, caller, value) for _, v in pairs(filters) do if v == inputName then DbgPrint(ent, "Filtered input: " .. entName .. " -> " .. inputName) - return true end end @@ -49,7 +48,6 @@ function GM:AcceptInput(ent, inputName, activator, caller, value) local res = cb(ent, inputName, activator, caller, value) if res == true then DbgPrint(ent, "Filtered input: " .. entName .. " -> " .. inputName) - return true end end @@ -61,4 +59,30 @@ function GM:AcceptInput(ent, inputName, activator, caller, value) elseif inputName == "EnableDraw" then ent:SetNoDraw(false) end + + -- MORE HACKHACK: Handle the case where companions entered the jalopy. + if inputName == "LambdaCompanionEnteredVehicle" and activator:IsVehicle() then + self:OnCompanionEnteredVehicle(activator, caller) + return true + elseif inputName == "LambdaCompanionExitedVehicle" and activator:IsVehicle() then + self:OnCompanionExitedVehicle(activator, caller) + return true + end + + -- Deal with AddOutput. + if inputName == "AddOutput" and self.IsCreatingInternalOutputs ~= true then + -- Split outputname and output parameters. + local outputName, outputParams = string.match(value, "(%w+)%s+(.*)") + + -- AddOutput uses double point instead of comma, replace that. + outputParams = string.gsub(outputParams, ":", ",") + + DbgPrint("Handling AddOutput", ent, outputName, outputParams) + + ent.EntityOutputs = ent.EntityOutputs or {} + local outputs = ent.EntityOutputs[outputName] or {} + table.insert(outputs, outputParams) + ent.EntityOutputs[outputName] = outputs + end + end \ No newline at end of file diff --git a/gamemode/sv_transition.lua b/gamemode/sv_transition.lua index a85e626f..7bdc71a4 100644 --- a/gamemode/sv_transition.lua +++ b/gamemode/sv_transition.lua @@ -1,4 +1,5 @@ local DbgPrint = GetLogging("Transition") +local g_debug_transitions = GetConVar("g_debug_transitions") local util = util local ents = ents local player = player @@ -8,6 +9,7 @@ local ENT_TYPE_NPC = 0 local ENT_TYPE_VEHICLE = 1 local ENT_TYPE_DOOR = 2 local ENT_TYPE_GENERIC = 3 +local REF_PREFIX = "CoopRef" local SERIALIZE_VECTOR = function(ent, val) return ent:WorldToLocal(val) end local SERIALIZE_ANGLES = function(ent, val) return ent:WorldToLocalAngles(val) end local FIELD_SERIALIZE = { @@ -86,6 +88,17 @@ local function GetProfilingData() return ProfiledFunctions end +local function ToRefId(ent) + return REF_PREFIX .. "_" .. tostring(ent:EntIndex()) +end + +local function GetRefId(refId) + if not isstring(refId) then return nil end + local prefix = string.sub(refId, 1, #REF_PREFIX) + if prefix ~= REF_PREFIX then return nil end + return tonumber(string.sub(refId, #REF_PREFIX + 2)) +end + function GM:InitializeTransitionData() Profiled("GM:InitializeTransitionData", function() local transitionData = DEFAULT_TRANSITION_DATA @@ -104,11 +117,14 @@ function GM:InitializeTransitionData() DbgPrint(" World objects: " .. tostring(table.Count(self.TransitionData.Objects or {}))) DbgPrint(" Global states: " .. tostring(table.Count(self.TransitionData.GlobalStates or {}))) --PrintTable(self.TransitionData) - util.RemovePData("Lambda" .. lambda_instance_id:GetString(), "TransitionData") + + if g_debug_transitions:GetBool() == false then + util.RemovePData("Lambda" .. lambda_instance_id:GetString(), "TransitionData") + end end) end -function GM:TransitionToLevel(activator, map, landmark, playersInTrigger, restart) +function GM:TransitionToLevel(map, landmark, playersInTrigger, restart) Profiled("GM:TransitionToLevel", function() -- 1. Lets collect all entities with the landmark name we have to seperate them by landmark and trigger local transitionTriggers = {} @@ -132,7 +148,7 @@ function GM:TransitionToLevel(activator, map, landmark, playersInTrigger, restar if not IsValid(landmarkEnt) then DbgPrint("Unable to find landmark! - " .. tostring(landmark)) else - self:TransitionNearbyObjects(activator, landmarkEnt, transitionTriggers, objectTable, playerTable, playersInTrigger) + self:TransitionNearbyObjects(landmarkEnt, transitionTriggers, objectTable, playerTable, playersInTrigger) -- In case players didnt make it, we erase their position from the data. for k, v in pairs(playerTable) do local ply = Entity(v.RefId) @@ -365,7 +381,11 @@ function GM:SerializePlayerData(landmarkEnt, ply, playersInTrigger) end local SAVETABLE_WHITELIST = { - ["target"] = true + ["target"] = true, + ["m_bEntranceLocked"] = true, + ["m_bExitLocked"] = true, + ["m_bEngineLocked"] = true, + ["m_bRadarEnabled"] = true, } local SAVETABLE_BLACKLIST = { @@ -421,6 +441,7 @@ function GM:SerializeEntityData(landmarkEnt, ent, playersInTrigger) data.Expression = ent:GetExpression() data.Activity = ent:GetActivity() data.NPCState = ent:GetNPCState() + data.SaveTable["LambdaVehicle"] = ent:GetNWEntity("LambdaVehicle", nil) local activeWeapon = ent:GetActiveWeapon() if IsValid(activeWeapon) then data.ActiveWeapon = activeWeapon:GetClass() end elseif ent:IsVehicle() then @@ -432,7 +453,7 @@ function GM:SerializeEntityData(landmarkEnt, ent, playersInTrigger) end data.VehicleScript = ent:GetInternalVariable("VehicleScript") - if ent:GetNWBool("IsPassengerSeat", false) == true then data.IsPassengerSeat = true end + if self:VehicleIsPassengerSeat(ent) == true then data.IsPassengerSeat = true end elseif ent:IsDoor() then data.Type = ENT_TYPE_DOOR if ent:IsDoorOpen() or ent:IsDoorOpening() then @@ -467,7 +488,7 @@ function GM:SerializeEntityData(landmarkEnt, ent, playersInTrigger) end if IsEntity(v) and IsValid(v) then - data.SaveTable[k] = "CoopRef_" .. tostring(v:EntIndex()) + data.SaveTable[k] = ToRefId(v) else data.SaveTable[k] = v end @@ -624,7 +645,7 @@ function GM:GetTransitionList(landmarkEnt, transitionTriggers, objectTable, play end) end -function GM:TransitionNearbyObjects(activator, landmarkEnt, transitionTriggers, objectTable, playerTable, playersInTrigger) +function GM:TransitionNearbyObjects(landmarkEnt, transitionTriggers, objectTable, playerTable, playersInTrigger) return Profiled("GM:GetTransitionList", function() DbgPrint("GM:TransitionNearbyObjects") -- Create initial list. @@ -634,10 +655,10 @@ function GM:TransitionNearbyObjects(activator, landmarkEnt, transitionTriggers, local caps = v:ObjectCaps() if bit.band(caps, FCAP_NOTIFY_ON_TRANSITION) == 0 then continue end if table.HasValue(initialObjectList, v) == false then - v:Input("OutsideTransition", activator, activator, nil) + v:Input("OutsideTransition", nil, nil, nil) DbgPrint("Notifying " .. tostring(v) .. " outside of transitioning") else - v:Input("InsideTransition", activator, activator, nil) + v:Input("InsideTransition", nil, nil, nil) DbgPrint("Notifying " .. tostring(v) .. " inside of transitioning") end end @@ -837,6 +858,8 @@ function GM:CreateTransitionObjects() end end + local postSpawnQueue = {} + DbgPrint("Creating " .. tostring(objCount) .. " transition Objects...") local entityTransitionData = {} for _, data in pairs(objects) do @@ -856,6 +879,7 @@ function GM:CreateTransitionObjects() DbgPrint("Using global entity!") else ent = ents.Create(data.Class) + ent.CreatedByLevelTransition = true end if not IsValid(ent) then @@ -949,7 +973,7 @@ function GM:CreateTransitionObjects() --ent.TransitionData = data entityTransitionData[ent] = data self.CreatedTransitionObjects[data.RefId] = ent - DbgPrint("Created " .. tostring(ent)) + DbgPrint("Created " .. tostring(ent) .. ", RefId: " .. data.RefId) end -- Second iteration: We resolve dependencies. @@ -961,8 +985,10 @@ function GM:CreateTransitionObjects() local parent = self.CreatedTransitionObjects[data.Parent] if IsValid(parent) then ent:SetParent(parent) - -- FIX: Make sure we assign the seat to the vehicle. - if ent:GetNWBool("IsPassengerSeat", false) == true then parent:SetNWEntity("PassengerSeat", ent) end + if self:VehicleIsPassengerSeat(ent) == true then + -- FIX: Make sure we assign the seat to the vehicle. + parent:SetNWEntity("PassengerSeat", ent) + end end end @@ -972,15 +998,44 @@ function GM:CreateTransitionObjects() end for k, v in pairs(data.SaveTable) do - if isstring(v) and v:sub(1, 8) == "CoopRef_" then - local refId = tonumber(v:sub(9)) + local refId = GetRefId(v) + if refId ~= nil then local refEnt = self.CreatedTransitionObjects[refId] if IsValid(refEnt) and refEnt ~= ent and ent:IsNPC() == false then DbgPrint("Resolved reference for " .. tostring(ent) .. ": " .. k .. " -> " .. tostring(refEnt)) ent:SetSaveValue(k, refEnt) end else - if SAVETABLE_WHITELIST[k] == true then ent:SetSaveValue(k, v) end + if SAVETABLE_WHITELIST[k] == true then + DbgPrint("Setting save value for " .. tostring(ent) .. ": " .. k .. " -> " .. tostring(v)) + ent:SetSaveValue(k, v) + end + end + end + + -- Handle NPCs entering the vehicle/passenger seat. + if ent:IsNPC() then + local vehicleRef = data.SaveTable["LambdaVehicle"] + if vehicleRef ~= nil then + local refId = GetRefId(vehicleRef) + DbgPrint("Found vehicle reference for " .. tostring(ent) .. ": " .. vehicleRef) + local vehicle = self.CreatedTransitionObjects[refId] + if IsValid(vehicle) then + ent:SetNWEntity("LambdaVehicle", vehicle) + ent:SetParent(nil) + -- This has to wait after the vehicle is fully initialized/spawned, + -- otherwise it will cause crashes. + table.insert(postSpawnQueue, function() + local oldName = vehicle:GetName() + local newName = oldName .. tostring(vehicle:EntIndex()) + -- This is one major hack to ensure alyx will be in the correct "jeep" vehicle. + vehicle:SetName(newName) + -- Bypass queue as our name is only valid here so it can't end up in a queue. + ent:Input("EnterVehicleImmediately", NULL, NULL, newName) + -- Reset the name. + vehicle:SetName(oldName) + end) + end end end @@ -1008,6 +1063,11 @@ function GM:CreateTransitionObjects() end end end + + -- Those functions might fire some inputs which require the object to be fully initialized first. + for _, func in pairs(postSpawnQueue) do + func() + end end) end diff --git a/gamemode/sv_votefuncs.lua b/gamemode/sv_votefuncs.lua index 96d0f6d7..92d68042 100644 --- a/gamemode/sv_votefuncs.lua +++ b/gamemode/sv_votefuncs.lua @@ -35,7 +35,7 @@ function GM:StartMapVote(issuer, map) local function OnChangeLevelVoteResult(vote, success, timeout, option) -- Yes if success == true and option == 1 then - GAMEMODE:RequestChangeLevel(nil, map, nil, {}) + GAMEMODE:RequestChangeLevel(map, nil, {}) end end