Files
o3de/Assets/Engine/Scripts/Utils/EntityUtils.lua
T
lumberyard-employee-dm 3dec5d3b71 LYN-2537 engine assets (#254)
* LYN-2537 Moved the Engine and Editor folder to be within the EngineAssets folder

* Fixed Documentation in bootstrap.cfg to correct the path to the user project specific registry file

* Adding a newline to the output of AssetCatalog 'Registering asset..., but type is not set' message

* Updating the AssetProcessorPlatformConfig.setreg Scan Folder to detect
the @ENGINEROOT@/EngineAssets/Engine path for engine runtime assets and
@ENGINEROOT@/EngineAssets/Editor path for engine tool assets

* Updating references to Icons and other assets to account for moving the
Engine and Editor folder under a single EngineAssets folder

* Moving the Engine Settings Registry folder from Engine/Registry -> Registry

* Removed the LY_PROJECT_CMAKE_PATH define as it is not portable to other locations. It is hard coded to the project location that was used for the CMake configuration. Furthermore it paths with backslashes within it are treated as escape characters and not a path separator

* Updated the LyTestTools asset_processor.py script to copy the exclude.filetag from the EngineAssets/Engine directory now

* Fixed Atom Shader Preprocessing when running using an External Project

* Updated the TSGenerateAction.cpp to fix the build error with using a renamed variable

* Updated the Install_Common.cmake ly_setup_others function to install the
EngineAssets directory and the each of the Gem's Assets directory while
maintaining the relative directory structure to the Engine Root
Also updated the install step to install the Registry folder at the
engine root

* Fixed the copying of the Registry folder to be in the install root, instead of under a second 'Registry' folder

* Moving the AssetProcessorPlatformConfig.setreg file over to the Registry folder

* Updated the LyTestTools and C++ code to point that the new location of
the AssetProcessorPlatformConfig.setreg file inside of the Registry
folder

* Renamed Test AssetProcessor*Config.ini files to have the .setreg extension

* Converted the AssetProcessor test setreg files from ini format to json
format using the SerializeContextTools convert-ini command

* Updated the AssetProcessor CMakeLists.txt to copy over the test setreg files to the build folder

* Updated the assetprocessor test file list to point at the renamed AsssetProcessor*Config setreg filenames

* Removed the Output Prefix code from the AssetProcessor. The complexity that it brought to the AP code is not needed, as users can replicate the behavior by just moving there assets underneath a another folder, underneath the scan folder

* Adding back support to read the AssetProcessorPlatformConfig.setreg file from the asset root. This is only needed for C++ UnitTests as they run in an environment where the accessing the Engine Settings Registry is not available

* Updating the Install_common.cmake logic to copy any "Assets" folder to
the install layout.
The Script has also been updated to copy over the "Assets" folder in the
Engine Root to the install layout instead of an "EngineAssets" folder

* Updating References to EngineAssets source asset folder in code to be the Assets source folder

* Moved the Engine Source Asset folder of 'EngineAssets' to a new folder name of 'Assets'. This is inline with the naming scheme we use for Gem asset folders

* Adding the EngineFinder.cmake to the AutomatedTesting project to allow it to work in a project centric manner

* Updating the LyTestTools copy_assets_to_project function to be able to copy assets with folders to the temporary project root
Fixed an issue in LyTestTools where the temporary log directory could have shutil.rmtree being called twice on it leading to an exception which fails an automated test

Updated the asset_procesor_gui_tests_2 AddScanFolder test to not use the
output prefix, but instead place the source asset root into a
subdirectory

* Correct the AssetProcessorPlatformConfig Scan Folders for the EngineAssets directory to point at the Assets directory

* Updated the asset procesor batch dependency test scan folder to point at the 'Assets' folder instead of 'EngineAssets'
2021-04-28 21:38:43 -05:00

962 lines
32 KiB
Lua

----------------------------------------------------------------------------------------------------
--
-- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
-- its licensors.
--
-- For complete copyright and license terms please see the LICENSE at the root of this
-- distribution (the "License"). All use of this software is governed by the License,
-- or, if provided, by the license below or the license accompanying this file. Do not
-- remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--
--
----------------------------------------------------------------------------------------------------
EntityCommon =
{
TempPhysParams = { mass=0,density=0 },
TempPhysicsFlags = { flags_mask=0, flags=0 };
TempSimulationParams = { max_time_step=0.02 };
}
----------------------------------------------------------
-- Creates a new table that is derived class of parent entity.
----------------------------------------------------------
function MakeDerivedEntity( _DerivedClass,_Parent )
local derivedProperties = _DerivedClass.Properties;
_DerivedClass.Properties = {};
mergef(_DerivedClass,_Parent,1);
-- Add derived class properties.
mergef(_DerivedClass.Properties,derivedProperties,1);
_DerivedClass.__super = BasicEntity;
return _DerivedClass;
end
----------------------------------------------------------
-- Creates a new table that is derived class of parent entity.
-- The Child's Properties will override the ones from the parent
----------------------------------------------------------
function MakeDerivedEntityOverride( _DerivedClass,_Parent )
--local derivedProperties = _Parent.Properties;
--_Parent.Properties = {};
mergef(_DerivedClass,_Parent,1);
-- Add derived class properties.
--mergef(_DerivedClass.Properties,derivedProperties,1);
_DerivedClass.__super = _Parent;
return _DerivedClass;
end
----------------------------------
function BroadcastEvent( sender,Event )
-- Check if Event Target for this input event exists.
sender:ProcessBroadcastEvent( Event );
if (sender.Events) then
--System.Log( "Events found" );
local eventTargets = sender.Events[Event];
if (eventTargets) then
--System.Log( "Events Targets found" );
for i, target in pairs(eventTargets) do
local TargetId = target[1];
local TargetEvent = target[2];
--System.Log( "Target: "..TargetId.."/"..TargetEvent );
--System.Log( "Target: "..TargetEvent );
if (TargetId == 0) then
-- If TargetId refer to global Mission table.
if Mission then
local func = Mission["Event_"..TargetEvent];
if (func ~= nil) then
func( sender )
else
System.Log( "Mission does not support event "..TargetEvent );
end
end
else
-- If TargetId refer to Entity.
local entity = System.GetEntity(TargetId);
if (entity ~= nil) then
local TargetName=entity:GetName();
--System.Log( "Entity Named "..TargetName.." Found." );
--System.Log( "Calling method: "..TargetName..":Event_"..TargetEvent );
local func = entity["Event_"..TargetEvent];
if (func ~= nil) then
func( entity,sender )
-- else
-- System.Log( "Entity "..TargetName.." does not support event "..TargetEvent );
end
-- else
-- System.Log( "Entity Named "..TargetName.." Not Found." );
end
end
end
end
end
end
function DumpEntities()
local ents=System.GetEntities();
System.Log("Entities dump");
for idx,e in pairs(ents) do
local pos=e:GetPos();
local ang=e:GetAngles();
System.Log("["..tostring(e.id).."]..name="..e:GetName().." clsid="..e.class..format(" pos=%.03f,%.03f,%.03f",pos.x,pos.y,pos.z)..format(" ang=%.03f,%.03f,%.03f",ang.x,ang.y,ang.z));
end
end
function MakeTargetableByAI( entity )
if (not entity.Properties) then entity.Properties = {} end
if (not entity.Properties.esFaction) then
entity.Properties.esFaction = "";
end
function entity:RegisterWithAI()
if (self.Properties.esFaction ~= "") then
CryAction.RegisterWithAI(self.id, AIOBJECT_TARGET);
AI.ChangeParameter(self.id, AIPARAM_FACTION, self.Properties.esFaction);
end
end
local _onReset = entity.OnReset;
function entity:OnReset(...)
if (_onReset) then
_onReset(self, ...);
end
self:RegisterWithAI();
end
local _onSpawn = entity.OnSpawn;
function entity:OnSpawn(...)
if (_onSpawn) then
_onSpawn(self, ...);
end
self:RegisterWithAI();
end
end
function MakeAICoverEntity(entity)
if (not entity.Properties) then entity.Properties = {} end
if (not entity.Properties.bProvideAICover) then entity.Properties.bProvideAICover = 1 end
local tbl = entity.Server and entity.Server or entity
local _onStartGame = tbl.OnStartGame
tbl.OnStartGame = function(self)
if (self.PropertiesInstance.bProvideAICover ~= 0) and (AI ~= nil) then
AI.AddCoverEntity(self.id)
end
if (_onStartGame) then
_onStartGame(self)
end
end
end
function MakeKillable( entity )
if (not entity.Properties) then entity.Properties = {} end
if (not entity.Properties.Health) then entity.Properties.Health = {} end
local Health = entity.Properties.Health;
Health.MaxHealth = 500;
Health.bInvulnerable = 0;
Health.bOnlyEnemyFire = 1;
function entity:IsDead()
return self.dead;
end
function entity:SetupHealthProperties()
self.dead = nil;
self.health = self.Properties.Health.MaxHealth;
self.invulnerable = self.Properties.Health.bInvulnerable ~= 0;
self.friendlyFire = self.Properties.Health.bOnlyEnemyFire == 0;
end
if (not entity.Server) then entity.Server = {} end
if (not entity.Client) then entity.Client = {} end
function entity:GetHealthRatio()
local healthR = 1;
local maxHealth = self.Properties.Health.MaxHealth;
if (maxHealth > 0) then
healthR = self.health / maxHealth;
end
return healthR;
end
function entity:IsInvulnerable()
return self.invulnerable
end
function entity:GetMaxHealth()
return self.Properties.Health.MaxHealth;
end
local _onHit = entity.Server.OnHit;
function entity.Server:OnHit(hit)
if ((not self.health) or (self.IsInvulnerable == nil)) then
Log("$4%s:%s Health not initialized!", self.class, self:GetName());
self:SetupHealthProperties();
end
local result = false;
if (_onHit) then
result = _onHit(self, hit);
end
if (not result) then
if (self:IsInvulnerable()) then
self:ActivateOutput("Health", self:GetHealthRatio() * 100);
self:Event_Hit();
return false;
end
if (not self.friendlyFire) then
if (System.GetEntity(hit.shooterId) ~= nil) then
local reaction = AI.GetReactionOf(self.id, hit.shooterId);
if (reaction == Friendly) then
self:ActivateOutput("Health", self:GetHealthRatio() * 100);
self:Event_Hit();
return false;
end
end
end
self.health = self.health - hit.damage;
end
self:ActivateOutput("Health", self:GetHealthRatio() * 100);
self:Event_Hit();
if (self.health <= 0) then
self.dead = true;
self:Event_Dead();
return true;
end
end
local _onReset = entity.OnReset;
function entity:OnReset(...)
if (_onReset) then
_onReset(self, ...);
end
self:SetupHealthProperties();
end
local _onSpawn = entity.OnSpawn;
function entity:OnSpawn(...)
if (_onSpawn) then
_onSpawn(self, ...);
end
self:SetupHealthProperties();
end
function entity:Event_ResetHealth()
self.dead = nil;
self.health = self.Properties.Health.MaxHealth;
end
function entity:SetInvulnerability(invulnerable)
self.invulnerable = invulnerable;
if(not self.overrode_saveload) then
local _onSave = self.OnSave;
function self:OnSave(table)
if(_onSave) then
_onSave(self, table);
end
if(self.invulnerable) then
table.invulnerable = self.invulnerable;
end
if(self.dead) then
table.dead = self.dead;
end
if(self.health) then
table.health = self.health;
end
end
local _onLoad = self.OnLoad;
function self:OnLoad(table)
if(_onLoad) then
_onLoad(self, table);
end
if(table.invulnerable) then
self.invulnerable = table.invulnerable;
else
self.invulnerable = false;
end
if(table.dead) then
self.dead = table.dead;
else
self.dead = false;
end
if(table.health) then
self.health = table.health;
else
self.health = self.Properties.Health.MaxHealth;
end
end
self.overrode_saveload = true;
end
end
function entity:Event_MakeVulnerable()
self:SetInvulnerability(false);
end
function entity:Event_MakeInvulnerable()
self:SetInvulnerability(true);
end
function entity:Event_Dead()
self:TriggerEvent(AIEVENT_DISABLE);
BroadcastEvent(self, "Dead");
end
function entity:Event_Hit()
BroadcastEvent(self, "Hit");
end
if not entity.FlowEvents then entity.FlowEvents = {} end
local fe = entity.FlowEvents
fe.Inputs = fe.Inputs or {}
fe.Outputs = fe.Outputs or {}
fe.Inputs["ResetHealth"] = { entity.Event_ResetHealth, "any" };
fe.Inputs["MakeVulnerable"] = { entity.Event_MakeVulnerable, "any" };
fe.Inputs["MakeInvulnerable"] = { entity.Event_MakeInvulnerable, "any" };
fe.Outputs["Dead"] = "bool";
fe.Outputs["Hit"] = "bool";
fe.Outputs["Health"] = "float";
end
function MakeRenderProxyOptions( entity )
if (not entity.Properties) then entity.Properties = {} end
if (not entity.Properties.RenderProxyOptions) then entity.Properties.RenderProxyOptions = {} end
entity.Properties.RenderProxyOptions.bAnimateOffScreenShadow = 0;
function entity:SetRenderProxyOptions()
self.bAnimateOffScreenShadow = self.Properties.RenderProxyOptions.bAnimateOffScreenShadow ~= 0;
if (self.bAnimateOffScreenShadow) then
self:CreateRenderProxy();
end
self:SetAnimateOffScreenShadow(self.bAnimateOffScreenShadow);
end
local _onReset = entity.OnReset;
function entity:OnReset(...)
if (_onReset) then
_onReset(self, ...);
end
self:SetRenderProxyOptions();
end
local _onSpawn = entity.OnSpawn;
function entity:OnSpawn(...)
if (_onSpawn) then
_onSpawn(self, ...);
end
self:SetRenderProxyOptions();
end
end
-- makes an OnUsed event for designers on an entity...
-- usage:
-- MyEntity = { ... whatever you usually put here ... }
-- MakeUsable(MyEntity)
-- function MyEntity:OnSpawn() ...
-- function MyEntity:OnReset()
-- self:ResetOnUsed()
-- ...
-- end
function MakeUsable( entity )
if not entity.Properties then entity.Properties = {} end
entity.Properties.UseMessage = "";
entity.Properties.bUsable = 0;
function entity:IsUsable()
if not self.__usable then
self.__origUsable = self.Properties.bUsable;
self.__origPickable = self.Properties.bPickable;
if (self.Properties.bUsable==1 or self.Properties.bPickable==1) then
self.__usable = 1;
else
self.__usable = 0;
end
end
return self.__usable;
end
function entity:ResetOnUsed()
self.__usable = nil;
end
function entity:GetUsableMessage()
return self.Properties.UseMessage;
end
function entity:OnUsed(user, idx)
BroadcastEvent(self, "Used");
if (self.Base_OnUsed) then
self:Base_OnUsed(user, idx);
end
end
function entity:Event_Used()
BroadcastEvent(self, "Used");
end
function entity:Event_EnableUsable()
self.__usable = 1;
BroadcastEvent(self, "EnableUsable");
end
function entity:Event_DisableUsable()
self.__usable = 0;
BroadcastEvent(self, "DisableUsable");
end
end
function MakePickable( entity )
if not entity.Properties then entity.Properties = {} end;
entity.Properties.bPickable = 0;
end
function AddHeavyObjectProperty(entity)
if (not entity.Properties) then
entity.Properties = {};
end;
entity.Properties.bHeavyObject = 0;
end;
function MakeThrownObjectTargetable( entity )
-- Add property
if not entity.Properties then
entity.Properties = {};
end
if not entity.Properties.AutoAimTarget then
entity.Properties.AutoAimTarget = {};
end
entity.Properties.AutoAimTarget.bMakeTargetableOnThrown = 0;
entity.Properties.AutoAimTarget.InnerRadiusVolumeFactor = 0.35;
entity.Properties.AutoAimTarget.OuterRadiusVolumeFactor = 0.6;
entity.Properties.AutoAimTarget.SnapRadiusVolumeFactor = 1.25;
entity.Properties.AutoAimTarget.AfterThrownTargetableTime = 3.0;
-- Add callback functions
function entity:OnThrown()
if ((self.Properties.AutoAimTarget.bMakeTargetableOnThrown ~= 0) and (self:CanBeMadeTargetable())) then
Game.RegisterWithAutoAimManager(self.id, self.Properties.AutoAimTarget.InnerRadiusVolumeFactor, self.Properties.AutoAimTarget.OuterRadiusVolumeFactor, self.Properties.AutoAimTarget.SnapRadiusVolumeFactor);
Script.SetTimer(self.Properties.AutoAimTarget.AfterThrownTargetableTime * 1000, function() self:AfterThrownTimer(); end)
self.isTargetable = 1;
end
end
function entity:AfterThrownTimer()
if (self.isTargetable) then
Game.UnregisterFromAutoAimManager(self.id);
self.isTargetable = nil;
end
end
local _CanBeMadeTargetable = entity.CanBeMadeTargetable;
function entity:CanBeMadeTargetable(...)
if (_CanBeMadeTargetable) then
return _CanBeMadeTargetable(self, ...);
end
return true;
end
-- Override shutdown/reset
local _OnShutDown = entity.OnShutDown;
function entity:OnShutDown(...)
if _OnShutDown then
_OnShutDown(self, ...);
end
if (self.isTargetable) then
Game.UnregisterFromAutoAimManager(self.id);
self.isTargetable = nil;
end
end
local _OnReset = entity.OnReset;
function entity:OnReset(...)
if _OnReset then
_OnReset(self, ...);
end
if (self.isTargetable) then
Game.UnregisterFromAutoAimManager(self.id);
self.isTargetable = nil;
end
end
end
function AddInteractLargeObjectProperty(entity)
if (not entity.Properties) then
entity.Properties = {};
end;
entity.Properties.bInteractLargeObject = 0;
end;
function MakeSpawnable( entity )
entity.spawnedEntity = nil
-- setup some basic properties
if not entity.Properties then entity.Properties = {} end
local p = entity.Properties;
p.bSpawner = false;
p.SpawnedEntityName = "";
local _OnDestroy = entity.OnDestroy;
function entity:OnDestroy(...)
-- System.Log("OnDestroy"..tostring(self.id));
if self.whoSpawnedMe then
-- inform that I'm dead
self.whoSpawnedMe:NotifyRemoval(self.id);
end
if _OnDestroy then
_OnDestroy(self, ...);
end
end
function entity:NotifyRemoval(spawnedEntityId)
-- System.Log("NotifyRemoval"..tostring(self.id).." spawned="..tostring(spawnedEntityId));
-- clear spawnedEntity on original
if (self.spawnedEntity and self.spawnedEntity == spawnedEntityId) then
--System.Log("...Cleared");
self.spawnedEntity = nil;
self.lastSpawnedEntity = nil;
end
end
-- override some functions to have our code called also
local _OnReset = entity.OnReset;
function entity:OnReset(...)
--System.Log("reset");
self.lastSpawnedEntity = nil;
if self.spawnedEntity then
System.RemoveEntity(self.spawnedEntity);
self.spawnedEntity = nil;
end
if self.whoSpawnedMe then
System.RemoveEntity( self.id );
return
end
_OnReset(self, ...);
end
local _OnEditorSetGameMode = entity.OnEditorSetGameMode;
function entity:OnEditorSetGameMode(...)
self.lastSpawnedEntity = nil;
if self.spawnedEntity then
self.spawnedEntity = nil;
end
if (_OnEditorSetGameMode) then
_OnEditorSetGameMode(self, ...);
end
end
-- allow flowgraph forwarding
function entity:GetFlowgraphForwardingEntity()
if (self.spawnedEntity) then
return self.spawnedEntity;
else
return self.lastSpawnedEntity;
end
end
-- OnSpawned event
function entity:Event_Spawned()
BroadcastEvent(self, "Spawned")
end
if not entity.FlowEvents then entity.FlowEvents = {} end
local fe = entity.FlowEvents
-- normalize events
fe.Inputs = fe.Inputs or {}
fe.Outputs = fe.Outputs or {}
-- collate events
local allEvents = {}
local name, data
for name, data in pairs(fe.Outputs) do
allEvents[name] = data
end
for name, data in pairs(fe.Inputs) do
allEvents[name] = data
end
-- event rebinding
for name, data in pairs(allEvents) do
local isInput = fe.Inputs[name]
local isOutput = fe.Outputs[name]
local isDeath = (name=="Dead")
local _event = data
if type(_event) == "table" then
_event = _event[1]
else
_event = nil
end
entity["Event_"..name] = function(self, sender, param)
-- auto broadcast received things for outputs
if isOutput and (sender and sender.id == self.spawnedEntity or sender==self) then
-- AI.LogEvent( ">>broadcasting output event "..name );
BroadcastEvent(self, name)
end
-- forward events where necessary
if isInput and (self.spawnedEntity and ((not sender) or (self.spawnedEntity ~= sender.id))) then
local ent = System.GetEntity(self.spawnedEntity)
if _event and ent and ent ~= sender then
_event(ent, sender, param)
end
elseif _event and not self.spawnedEntity then
-- and pass through where not
_event(self, sender, param)
end
-- handle death events
if isDeath and (sender and sender.id == self.spawnedEntity) then
self.spawnedEntity = nil
end
end
end
-- spawn event
function entity:Event_Spawn()
local entityIdDone = self:Event_Spawn_Internal();
-- the entity needs the output being activated, is not enough to just activate the output on the entity that spawnedMe,
-- because the flowgraph could be already forwarded to the newly spawned entity (if the entity does not have the flowgraph associated, the output event will be just ignored)
if (entityIdDone ~= self.id) then
self:ActivateOutput("Spawned", self.id);
end
end
function entity:Event_Spawn_Internal()
if self.whoSpawnedMe then
-- we were spawned (and not placed on a level)...
-- GetForwardingEntity will make sure that this event
-- is sent here first, but this event *MUST* be handled
-- by our spawner
return self.whoSpawnedMe:Event_Spawn_Internal()
else
if self.spawnedEntity then
return nil
end
local params = {
class = self.class;
position = self:GetPos(),
orientation = self:GetDirectionVector(1),
scale = self:GetScale(),
archetype = self:GetArchetype(),
properties = self.Properties,
propertiesInstance = self.PropertiesInstance,
}
if (self.InitialPosition) then
params.position = self.InitialPosition;
end
if self.Properties.SpawnedEntityName ~= "" then
params.name = self.Properties.SpawnedEntityName
else
params.name = self:GetName().."_s"
end
local ent = System.SpawnEntity(params, self.id)
if ent then
self.spawnedEntity = ent.id
self.lastSpawnedEntity = ent.id;
if not ent.Events then ent.Events = {} end
local evts = ent.Events
for name, data in pairs(self.FlowEvents.Outputs) do
if not evts[name] then evts[name] = {} end
table.insert(evts[name], {self.id, name})
end
ent.whoSpawnedMe = self;
ent:SetupTerritoryAndWave();
--self:Event_Spawned();
self:ActivateOutput("Spawned", ent.id);
return self.id;
end
end
end
-- spawn event keep
function entity:Event_SpawnKeep()
local params =
{
class = self.class;
position = self:GetPos(),
orientation = self:GetDirectionVector(1),
scale = self:GetScale(),
archetype = self:GetArchetype(),
properties = self.Properties,
propertiesInstance = self.PropertiesInstance,
}
local rndOffset = 1;
params.position.x = params.position.x + random(0,rndOffset*2)-rndOffset;
params.position.y = params.position.y + random(0,rndOffset*2)-rndOffset;
params.name = self:GetName()
local ent = System.SpawnEntity(params, self.id)
if ent then
self.spawnedEntity = ent.id
self.lastSpawnedEntity = ent.id;
if not ent.Events then ent.Events = {} end
local evts = ent.Events
for name, data in pairs(self.FlowEvents.Outputs) do
if not evts[name] then evts[name] = {} end
table.insert(evts[name], {self.id, name})
end
-- ent.whoSpawnedMe = self;
--self:Event_Spawned();
self:ActivateOutput("Spawned", ent.id);
end
end
-- hidhing/unhiding should be done inside disable/enable
-- function entity:Event_Hide()
-- self:Hide(1)
-- end
fe.Inputs["Spawn"] = {entity.Event_Spawn, "bool"}
-- fe.Inputs["Hide"] = {entity.Event_Hide, "bool"}
fe.Outputs["Spawned"] = "entity";
end
-----------------------------------------------------------------------------------------
-- Setup the collision filtering for the entity
-----------------------------------------------------------------------------------------
function SetupCollisionFiltering( entity )
-- Have we got an entity and a physics collision filtering table
if (entity == nil) then return end
if (entity.Properties == nil) then entity.Properties = {} end
if (entity.Properties.Physics == nil) then entity.Properties.Physics = {} end
-- Populate the table with the basic collision classes
-- These are enumurated in the global g_PhysicsCollisionClass
-- which is created by the game code
entity.Properties.Physics.CollisionFiltering = {};
local c = entity.Properties.Physics.CollisionFiltering;
c.collisionType = {}
c.collisionIgnore = {}
if (g_PhysicsCollisionClass == nil) then return end
for i, v in pairs(g_PhysicsCollisionClass) do
c.collisionType[i] = 0;
c.collisionIgnore[i] = 0;
end
end
function GetCollisionFiltering( entity )
local output = {}
output.collisionClass = 0;
output.collisionClassIgnore = 0;
if (entity.Properties.Physics==nil) then return output; end
if (entity.Properties.Physics.CollisionFiltering==nil) then return output; end
local c = entity.Properties.Physics.CollisionFiltering;
for i,v in pairs(c.collisionType) do
local gameSideFlag = g_PhysicsCollisionClass[i];
if (gameSideFlag ~= nil and v==1) then
output.collisionClass = output.collisionClass + gameSideFlag;
end
end
for i,v in pairs(c.collisionIgnore) do
local gameSideFlag = g_PhysicsCollisionClass[i];
if (gameSideFlag ~= nil and v==1) then
output.collisionClassIgnore = output.collisionClassIgnore + gameSideFlag;
end
end
return output;
end
function ApplyCollisionFiltering(entity, filtering)
if (filtering.collisionClass ~= 0 or filtering.collisionClassIgnore ~= 0) then
entity:SetPhysicParams(PHYSICPARAM_COLLISION_CLASS, filtering );
end
end
---------------------------------------------------------------------------------------------------
-- Physicalize rigid body.
---------------------------------------------------------------------------------------------------
--GlobalPhysicsSimParams = { max_logged_collisions = 1 };
EntityCommon.PhysicalizeRigid = function( entity,nSlot,Properties,bActive )
local Mass = Properties.Mass;
local Density = Properties.Density;
if bActive and bActive==0 then
Mass = 0.0; Density = 0.0;
end
local physType;
if (Properties.bArticulated == 1) then
physType = PE_ARTICULATED;
else
if (Properties.bRigidBody == 1) then
physType = PE_RIGID;
else
physType = PE_STATIC;
end
end
local TempPhysParams = EntityCommon.TempPhysParams;
TempPhysParams.density = Density;
TempPhysParams.mass = Mass;
TempPhysParams.flags = 0;
if (Properties.CGFPropsOverride) then
TempPhysParams.CGFprops = "";
for key,value in pairs(Properties.CGFPropsOverride) do
if (type(value)=="table") then
for key1,value1 in pairs(value) do
if (value1~="") then
TempPhysParams.CGFprops = TempPhysParams.CGFprops..key1.."="..value1.."\n";
end
end
else
if (value~="") then
TempPhysParams.CGFprops = TempPhysParams.CGFprops..key.."="..value.."\n";
end
end
end
end
entity:Physicalize( nSlot, physType, TempPhysParams );
--entity:SetPhysicParams(PHYSICPARAM_SIMULATION, GlobalPhysicsSimParams );
if (Mass>0 or Density>0) then
ApplyCollisionFiltering(entity, { collisionClass=gcc_rigid; } );
end
if(Properties.bInteractLargeObject==1) then
ApplyCollisionFiltering(entity, { collisionClass=gcc_large_kickable; } );
end
ApplyCollisionFiltering(entity, GetCollisionFiltering(entity));
if (Properties.Simulation) then
local SimulationSrc = Properties.Simulation;
local SimulationDst = EntityCommon.TempSimulationParams;
for key,value in next,SimulationDst,nil do SimulationDst[key] = nil; end
for key,value in next,SimulationSrc,nil do
if (key~="max_time_step" or value<0.0199 or value>0.0201) and (key~="sleep_speed" or value<0.0399 or value>0.0401) then SimulationDst[key] = value; end
end
entity:SetPhysicParams(PHYSICPARAM_SIMULATION, SimulationDst);
end
local Buoyancy = Properties.Buoyancy;
if (Buoyancy) then
entity:SetPhysicParams(PHYSICPARAM_BUOYANCY, Buoyancy);
end
local ForeignData = Properties.ForeignData;
if(ForeignData and ForeignData.bMovingPlatform == 1) then
entity:SetPhysicParams(PHYSICPARAM_FOREIGNDATA, { foreignFlags=FOREIGNFLAGS_MOVING_PLATFORM } );
end
-----------------------------------------------------------------------------
-- Set physical flags.
-----------------------------------------------------------------------------
local PhysFlags = EntityCommon.TempPhysicsFlags;
PhysFlags.flags = 0;
if (Properties.bPushableByPlayers == 1) then
PhysFlags.flags = pef_pushable_by_players;
end
if (Simulation and Simulation.bFixedDamping and Simulation.bFixedDamping==1) then
PhysFlags.flags = PhysFlags.flags+pef_fixed_damping;
end
if (Simulation and Simulation.bUseSimpleSolver and Simulation.bUseSimpleSolver==1) then
PhysFlags.flags = PhysFlags.flags+ref_use_simple_solver;
end
if (Properties.bCanBreakOthers==nil or Properties.bCanBreakOthers==0) then
PhysFlags.flags = PhysFlags.flags+pef_never_break;
end
if (Properties.MP and Properties.MP.bClientOnly) then
-- allow breaking on the client
PhysFlags.flags = PhysFlags.flags+pef_override_impulse_scale;
end
PhysFlags.flags_mask = pef_fixed_damping + ref_use_simple_solver + pef_pushable_by_players + pef_never_break + pef_override_impulse_scale;
entity:SetPhysicParams( PHYSICPARAM_FLAGS,PhysFlags );
-----------------------------------------------------------------------------
if (Properties.bResting == 0) then
entity:AwakePhysics(1);
else
entity:AwakePhysics(0);
end
end
-------------------------------------------------------------------------------
-- Compare entities by name (for table.sort)
-------------------------------------------------------------------------------
function CompareEntitiesByName( ent1, ent2 )
return ent1:GetName() < ent2:GetName()
end
function MakeCompareEntitiesByDistanceFromPoint( point )
function CompareEntitiesByDistanceFromPoint( ent1, ent2 )
distance1 = DistanceSqVectors( ent1:GetWorldPos(), point )
distance2 = DistanceSqVectors( ent2:GetWorldPos(), point )
return distance1 > distance2
end
return CompareEntitiesByDistanceFromPoint
end
-------------------------------------------------------------------------------
-- Called by Pool System when an Entity is bookmarked for pool usage
-- - Gives us its EntityId and PropertiesInstance tables for logic-driven utilities
-------------------------------------------------------------------------------
function OnEntityBookmarkCreated( entityId, propertiesInstance )
local waveName = nil;
if (propertiesInstance and propertiesInstance.AITerritoryAndWave) then
waveName = propertiesInstance.AITerritoryAndWave.aiwave_Wave;
end
if (waveName and waveName ~= "<None>") then
-- Notify territory and wave
AddBookmarkedToWave(entityId, waveName);
return false;
end
return true;
end