You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Assets/Engine/Scripts/Entities/Triggers/AreaTrigger.lua

359 lines
8.6 KiB
Lua

----------------------------------------------------------------------------------------------------
--
-- Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
--
-- SPDX-License-Identifier: Apache-2.0 OR MIT
--
--
--
--
--
-- Description: Network-ready Area Trigger
--
----------------------------------------------------------------------------------------------------
AreaTrigger =
{
Properties =
{
bEnabled = 1,
bTriggerOnce = 0,
bOnlyPlayers = 1,
bOnlyLocalPlayer= 0,
esFactionFilter = "",
ScriptCommand = "",
PlaySequence = "",
bInVehicleOnly = 0,
MultiplayerOptions =
{
bNetworked = 0,
bPerPlayer = 0,
},
},
Client = {},
Server = {},
Editor =
{
Model = "Editor/Objects/T.cgf",
Icon = "AreaTrigger.bmp",
IsScalable = false;
IsRotatable = false;
},
}
Net.Expose
{
Class = AreaTrigger,
ClientMethods =
{
ClEnter = { RELIABLE_ORDERED, PRE_ATTACH, ENTITYID, INT8 },
ClLeave = { RELIABLE_ORDERED, PRE_ATTACH, ENTITYID, INT8 },
},
ServerMethods = {},
ServerProperties = {}
}
function AreaTrigger:OnPropertyChange()
self:OnReset();
end
function AreaTrigger:OnReset()
self.enabled = nil;
self.triggerOnce = tonumber(self.Properties.bTriggerOnce)~=0;
self.localOnly = self.Properties.MultiplayerOptions.bNetworked==0;
self.perPlayer = tonumber(self.Properties.MultiplayerOptions.bPerPlayer)~=0;
self.isServer=CryAction.IsServer();
self.isClient=CryAction.IsClient();
self.inside={};
self.insideCount=0;
if (not self.localOnly) then
self.triggeredPP={};
self.triggeredOncePP={};
else
self:SetFlags(ENTITY_FLAG_CLIENT_ONLY,0);
end
self.triggeredOnce=nil;
self.triggered=nil;
self:Enable(tonumber(self.Properties.bEnabled)~=0);
self:ActivateOutput("NrOfEntitiesInside", 0);
end
function AreaTrigger:Enable(enable)
self.enabled=enable;
self:RegisterForAreaEvents(enable and 1 or 0);
end
function AreaTrigger:OnSpawn()
self:OnReset();
end
function AreaTrigger:OnSave(props)
props.enabled = self.enabled;
props.triggered = self.triggered;
props.triggeredOnce = self.triggeredOnce;
end
function AreaTrigger:OnLoad(props)
self:OnReset();
self.enabled = props.enabled;
self.triggered = props.triggered;
self.triggeredOnce = props.triggeredOnce;
end
function AreaTrigger:CanTrigger(entityId)
local entity=System.GetEntity(entityId);
if (not entity) then return; end;
local Properties = self.Properties;
local isAPlayer = ActorSystem.IsPlayer(entity.id);
if (Properties.bOnlyPlayers ~= 0 and not(isAPlayer)) then
return false;
end
if (Properties.bOnlyLocalPlayer ~= 0 and entity ~= g_localActor) then
return false;
end
if (Properties.bInVehicleOnly ~= 0 and not entity.vehicleId) then
return false;
end
if (Properties.esFactionFilter ~= "") then
local faction = AI.GetFactionOf(entity.id) or "";
if (faction ~= Properties.esFactionFilter) then
return false
end
end
return true;
end
function AreaTrigger:Trigger(entityId, enter)
self:ActivateOutput("NrOfEntitiesInside", self.insideCount);
self:ActivateOutput("Sender", entityId or NULL_ENTITY);
self:ActivateOutput("Faction", AI.GetFactionOf(entityId or NULL_ENTITY) or "");
if (enter) then
if(self.Properties.ScriptCommand and self.Properties.ScriptCommand~="")then
local f = loadstring(self.Properties.ScriptCommand);
if (f~=nil) then
f();
end
end
if(self.Properties.PlaySequence~="")then
Movie.PlaySequence(self.Properties.PlaySequence);
end
end
end
function AreaTrigger:EnteredArea(entity, areaId)
if (not self:CanTrigger(entity.id, areaId)) then
return;
end
self.inside[entity.id]=true;
self.insideCount=self.insideCount+1;
self:Event_Enter(entity.id);
end
function AreaTrigger:LeftArea(entity, areaId)
if (not self:CanTrigger(entity.id, areaId)) then
return;
end
self.inside[entity.id]=nil;
self.insideCount=self.insideCount-1;
self:Event_Leave(entity.id);
end
function AreaTrigger.Server:OnEnterArea(entity, areaId)
if (self:CanTrigger(entity.id)) then
self:EnteredArea(entity, areaId);
end
end
function AreaTrigger.Server:OnLeaveArea(entity, areaId)
self:LeftArea(entity, areaId);
end
function AreaTrigger.Client:OnEnterArea(entity, areaId)
if (not self:CanTrigger(entity.id)) then return; end;
if (not self.localOnly or self.isServer) then return; end;
self:EnteredArea(entity, areaId);
end
function AreaTrigger.Client:OnLeaveArea(entity, areaId)
if (not self.localOnly or self.isServer) then return; end;
self:LeftArea(entity, areaId);
end
function AreaTrigger:Event_Enter(entityId)
if (not self.enabled) then return; end; -- TODO: might need a self.active here
if (self.triggerOnce) then
if (self.localOnly) then
if (self.triggeredOnce) then
return;
end
elseif (self.perPlayer and self.triggeredOncePP[entityId]) then -- TODO: will need to skip this for non-player entities
return;
elseif (not self.perPlayer and self.triggeredOnce) then
return;
end
end
self.triggered=true;
self.triggeredOnce=true;
if (not self.localOnly and entityId) then
self.triggeredPP[entityId]=true;
self.triggeredOncePP[entityId]=true;
end
self:Trigger(entityId, true);
--BroadcastEvent(self, "Enter");
self:ActivateOutput("Enter", entityId);
if (not self.localOnly and self.isServer) then
if (self.isClient) then
self.otherClients:ClEnter(g_localChannelId, entityId or NULL_ENTITY, self.insideCount);
else
self.allClients:ClEnter(entityId or NULL_ENTITY, self.insideCount);
end
end
end
function AreaTrigger:Event_Leave(entityId)
if (not self.enabled) then return; end;
if (self.localOnly and not self.triggered) then return; end;
if (self.perPlayer) then
if (not self.localOnly and entityId) then
if (not self.triggeredPP[entityId]) then return; end;
end
else
if (not self.triggered) then return; end;
end
--only disable triggered when all players are gone
if(self.insideCount == 0) then
self.triggered=nil;
end
if (not self.localOnly and entityId and self.insideCount == 0) then
self.triggeredPP[entityId]=nil;
end
self:Trigger(entityId, false);
--BroadcastEvent(self, "Leave");
self:ActivateOutput("Leave", entityId or NULL_ENTITY);
if (not self.localOnly and self.isServer) then
if (self.isClient) then
self.otherClients:ClLeave(g_localChannelId, entityId or NULL_ENTITY, self.insideCount);
else
self.allClients:ClLeave(entityId or NULL_ENTITY, self.insideCount);
end
end
end
function AreaTrigger.Client:ClEnter(entityId, insideCount)
self.insideCount = insideCount;
self.inside[entityId] = true;
self:Trigger(entityId, true);
--BroadcastEvent(self, "Enter");
self:ActivateOutput("Enter", entityId);
end
function AreaTrigger.Client:ClLeave(entityId, inside)
self.insideCount = inside;
self.inside[entityId] = nil;
self:Trigger(entityId, false);
self:ActivateOutput("Sender", entityId);
self:ActivateOutput("NrOfEntitiesInside", inside);
--BroadcastEvent(self, "Leave");
self:ActivateOutput("Leave", entityId);
end
function AreaTrigger:Event_Enable()
self:Enable(true);
local entityIdInside = next(self.inside);
if (entityIdInside) then
self:Event_Enter( entityIdInside );
end;
self:ActivateOutput("NrOfEntitiesInside", self.insideCount);
BroadcastEvent(self, "Enable");
end
function AreaTrigger:Event_Disable()
self:Enable(false);
BroadcastEvent(self, "Disable");
end
AreaTrigger.FlowEvents =
{
Inputs =
{
Disable = { AreaTrigger.Event_Disable, "bool" },
Enable = { AreaTrigger.Event_Enable, "bool" },
Enter = { AreaTrigger.Event_Enter, "bool" },
Leave = { AreaTrigger.Event_Leave, "bool" },
},
Outputs =
{
Disable = "bool",
Enable = "bool",
Enter = "entity",
Leave = "entity",
NrOfEntitiesInside = "int",
Sender = "entity",
Faction = "string",
},
}