Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,33 @@
----------------------------------------------------------------------------------------------------
--
-- 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.
--
--
----------------------------------------------------------------------------------------------------
-- require with:
-- local gameplayMultiHandler = require('scripts.utils.components.gameplayutils')
-- use like:
-- self.gameplayHandlers = gameplayMultiHandler.ConnectMultiHandlers{
-- [GameplayNotificationId("jump")] = {
-- OnEventBegin = function(floatValue) self:JumpStart(floatValue) end,
-- OnEventUpdating = function(floatValue) self:Jumping(floatValue) end,
-- OnEventEnd = function(floatValue) self:JumpEnded(floatValue) end,
-- },
-- }
-- disconnect from like this:
-- self.gameplayHandlers:Disconnect()
local multiHandlers = require('scripts.utils.components.MultiHandlers')
return {
ConnectMultiHandlers = multiHandlers(GameplayNotificationBus, GameplayNotificationId),
}
@@ -0,0 +1,31 @@
----------------------------------------------------------------------------------------------------
--
-- 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.
--
--
----------------------------------------------------------------------------------------------------
-- require with:
-- local inputMultiHandler = require('scripts.utils.components.inpututils')
-- self.inputHandlers = inputMultiHandler.ConnectMultiHandlers{
-- [InputEventNotificationId("jump")] = {
-- OnPressed = function(floatValue) self:JumpPressed(floatValue) end,
-- OnHeld = function(floatValue) self:JumpHeld(floatValue) end,
-- OnReleased = function(floatValue) self:JumpReleased(floatValue) end,
-- },
-- }
-- disconnect from like this:
-- self.inputHandlers:Disconnect()
local inputMultiHandlers = require('scripts.utils.components.MultiHandlers')
return {
ConnectMultiHandlers = inputMultiHandlers(InputEventNotificationBus, InputEventNotificationId),
}
@@ -0,0 +1,74 @@
----------------------------------------------------------------------------------------------------
--
-- 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.
--
--
----------------------------------------------------------------------------------------------------
--This utility function allows you to easily create handlers when needing to handle multiple IDs.
--Common examples are inputs (where you have multiple key presses/actions you need to handle) and
--gameplay notifications (where there's several gameplay events your component needs to handle).
--See GameplayUtils.lua and InputUtils.lua as examples.
-- require with:
-- local multiHandlers = require('scripts.utils.components.multihandlers')
-- create multihandlers like:
-- return {
-- ConnectMultiHandlers = multiHandlers(GameplayNotificationBus, GameplayEventNotificationId),
-- }
local function ConnectMultiHandlers(bus, idType)
-- Generate listener constructor
return function(eventTable)
-- Our object emulating an ebus handler
local proxyHandler = {
handlers = { }
}
-- Connect to the bus for each id/handler passed
for busId, handlers in pairs(eventTable) do
Debug.Error(typeid(busId) == typeid(idType), "Wrong event id type, expected " .. tostring(typeid(busId)) .. " but got " .. tostring(typeid(idType)))
-- Setup proxy table
proxyHandler.handlers[busId] = {
originalHandlerTable = handlers; -- Reference to original handler table passed
handlerTable = { } -- Table containing actual handler functions
}
local currentProxy = proxyHandler.handlers[busId]
-- For each event handler, wrap and copy to proxy handlerTable
for eventName, eventHandler in pairs(handlers) do
if (type(eventHandler) == 'function') then
-- drop the original handler table for the callback
currentProxy.handlerTable[eventName] = function(origHandlerTable, ...)
eventHandler(...)
end
else
Debug.Warning(false, string.format("Invalid value passed to multihandler for key %s: %s. Function expected.", eventName, type(eventHandler)))
end
end
-- add a handler keyed on busid. Store the handler and callback by strong reference
currentProxy.busHandler = bus.Connect(currentProxy.handlerTable, busId)
end
-- Setup disconnect
function proxyHandler:Disconnect()
for busId, handlers in pairs(self.handlers) do
handlers.busHandler:Disconnect()
handlers.handlerTable = nil
handlers.origHandlerTable = nil
end
self.handlers = nil
end
return proxyHandler
end
end
return ConnectMultiHandlers
+334
View File
@@ -0,0 +1,334 @@
----------------------------------------------------------------------------------------------------
--
-- 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.
--
-- Original file Copyright Crytek GMBH or its affiliates, used under license.
--
--
-- Description: Containers for Lua
-- Docs:
--
----------------------------------------------------------------------------------------------------
local countTable = Set and Set.countTable or {};
-- Create factory and management table
Set = {};
Set.countTable = countTable;
-- All tables are registered in Set.countTable, therefore won't GCed!
-- Unless we make their entries weak references. See Lua docs.
-- The effect is that these references are ignored by the garbage collector.
setmetatable(Set.countTable, { __mode="kv" });
-- Create and return a new Set table
Set.New = function()
local nt = {}
Set.countTable[nt] = 0
return nt
end
Set.SerializeValues = function(tbl)
local result = {}
for k,v in pairs(tbl) do
local item = {}
table.insert( item, 1, k );
table.insert( item, 2, v );
table.insert( result, item );
end
return result
end
Set.DeserializeValues = function(tbl)
local result = Set.New()
for i,item in ipairs(tbl) do
Set.Add( result, item[1], item[2] );
end
return result;
end
Set.DeserializeEntities = function(tbl)
local result = Set.New()
for i,entityID in ipairs(tbl) do
Set.Add(result, entityID)
end
return result
end
Set.SerializeEntities = function(tbl)
local result = {};
for entityID,v in pairs(tbl) do
table.insert(result, entityID)
end
return result;
end
Set.DeserializeItems = function(tbl)
local result = Set.New();
for i,item in ipairs(tbl) do
if (item) then
Set.Add(result, item);
end
end
return result;
end
Set.SerializeItems = function(tbl)
local result = {};
local index = 1;
for item,v in pairs(tbl) do
result[index] = item;
index = index + 1;
end
return result;
end
-- Is this table registered as a Set?
-- When performance and best-effort execution are required, this function
-- can easily be reduced to a stub or even all calls to it removed with regexp
Set.Check = function(table)
-- Run-time checking
if (not Set.countTable[table]) then
-- Throwing an error here would be preferable
if (print) then
print(tostring(table).."is not registered as a Set");
else
System.Log(tostring(table).."is not registered as a Set");
end
System.ShowDebugger()
Set.throwAnError.throwAnError = true;
end
end
-- Add an entry into a Set iff it does not already exist
-- if v == nil, we set it to true - leaving it nil would break things
-- Returns true on success, false if entry already exists
Set.Add = function(table, k, v)
v = v or true;
Set.Check(table);
if (not table[k]) then
table[k] = v;
Set.countTable[table] = Set.countTable[table] + 1;
return true;
end
return false;
end
-- Removes an entry from a Set
-- if k == nil, does nothing, which is debatable semantics
-- Returns true on success, false if entry did not exist
Set.Remove = function(table, k)
Set.Check(table);
if (table[k]) then
table[k] = nil;
Set.countTable[table] = Set.countTable[table] - 1;
return true;
end
return false;
end
-- Get a value from a Set
-- Return the value or nil if none
Set.Get = function(table, k)
Set.Check(table);
return table[k]
end
-- Set the value associated with a key, even if it already exists
-- if v == nil, we set it to true - leaving it nil would break things
-- Returns true if it did already exist, false if not
Set.Set = function(table, k, v)
v = v or true;
Set.Check(table);
if (not table[k]) then
table[k] = v;
Set.countTable[table] = Set.countTable[table] + 1;
return false;
else
-- Replace, no need to change count
table[k] = v;
return true;
end
end
-- Get the number of entries in the Set
Set.Size = function(table)
Set.Check(table);
return Set.countTable[table];
end
-- Remove all entries
-- No return value
Set.RemoveAll = function(table)
Set.Check(table);
for k,v in pairs(table) do
table[k] = nil;
end
Set.countTable[table] = 0;
end
-- Add entries of one Set into another
-- Keys shared by both will not be overwritten
-- Returns true iff keys were disjoint, none shared
Set.Merge = function(dest, source)
local disjoint = true;
Set.Check(dest);
Set.Check(source);
for k,v in pairs(source) do
if (not Set.Add(dest, k, v)) then
disjoint = false;
end
end
return disjoint;
end
-- Iterating:
-- For now, treat a Set as any table.
-- This should change.
-- Utility/debugging function:
-- Sanity check a suspicious Set
-- Tries to find evidence of tampering
-- Returns true iff it passes
Set.SanityCheck = function(table)
-- Check this was created as a Set
Set.Check(table);
-- Find as many things as you can in the Set
local count = 0;
for i,v in pairs(table) do
count = count + 1;
end
local size = Set.Size(table);
if (count ~= size) then
System.Log("[Set] Sanity check failed - Set size is "..tostring(size)..", counted "..count);
return false;
end
-- Check that all entries have the same type
-- This is just good practice, rather than an error
local indexType = nil;
local valueType = nil;
for i,v in pairs(table) do
-- Check index
if (indexType) then
if (indexType ~= type(i)) then
System.Log("[Set] Sanity check failed - Found indices of both types "..indexType.." and "..type(i));
return false;
end
else
indexType = type(i);
end
-- Check value
if (valueType) then
if (valueType ~= type(v)) then
System.Log("[Set] Sanity check failed - Found values of both types "..valueType.." and "..type(v));
return false;
end
else
valueType = type(v);
end
end
return true;
end
-- Unit test
-- Return true for success or false on failure
Set.Test = function(fullTest)
local A = Set.New();
local B = Set.New();
if ( Set.Add(A, "key1", 1) == false ) then return false; end
if ( Set.Add(A, "key2") == false ) then return false; end
if ( Set.Add(A, "key3", 3) == false ) then return false; end
if ( Set.Add(A, "key1", 1) == true ) then return false; end
if ( Set.Remove(A, "key1") == false ) then return false; end
if ( Set.Get(A, "key2")== false ) then return false; end
if ( Set.Get(A, "key1") ~= nil ) then return false; end
if ( Set.Get(A, "key3") ~= 3 ) then return false; end
if ( Set.Size(A) ~= 2 ) then return false; end
Set.RemoveAll(A);
if ( Set.Size(A) ~= 0 ) then return false; end
if ( Set.Add(A, "key1", 1) == false ) then return false; end
if ( Set.Set(A, "key1", 9) == false ) then return false; end
if ( Set.Set(A, "key0", 9) == true ) then return false; end
if ( Set.Add(A, "keyF", 3) == false ) then return false; end
if ( Set.Size(A) ~= 3 ) then return false; end
if ( Set.Add(B, "key3", 3) == false ) then return false; end
if ( Set.Add(B, "key4", 4) == false ) then return false; end
if ( Set.Add(B, "key5", 5) == false ) then return false; end
if ( Set.Add(B, "key3", 3) == true ) then return false; end
if ( Set.Set(B, "key9") == true ) then return false; end
if ( Set.Get(B, "key9") == nil ) then return false; end
if (not Set.Merge(A,B)) then return false; end
if (Set.Size(A) ~= 7) then return false; end
if (Set.Merge(A,B)) then return false; end
if (Set.Size(A) ~= 7) then return false; end
if (fullTest) then
-- Check countTable and GC (slow)
-- Shouldn't assume there are no Sets when testing
collectgarbage();
local baseCount = 0;
for i,v in pairs(Set.countTable) do
baseCount = baseCount + 1;
end
B = nil;
collectgarbage();
local count = 0;
for i,v in pairs(Set.countTable) do
count = count + 1;
end
if (baseCount - count ~= 1) then return false; end
end
-- Move this line around for testing
do return true; end
return true;
end
-- Perform quick version of unit test
if (not Set.Test()) then
System.Log("Containers: ... Error - Failed Unit Test");
else
--System.Log("Containers: ... End loading!");
end
-- Useful testing lines in Editor:
-- #Script.ReloadScript("Scripts/Utils/Containers.lua");
-- #System.Log(tostring(Set.Test(true)))
+962
View File
@@ -0,0 +1,962 @@
----------------------------------------------------------------------------------------------------
--
-- 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
+420
View File
@@ -0,0 +1,420 @@
----------------------------------------------------------------------------------------------------
--
-- 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.
--
--
----------------------------------------------------------------------------------------------------
function ConstVec(v)
return v
end
----------------------------------------------------
-- global Vectors table and some vector functions
----------------------------------------------------
g_Vectors =
{
v000=ConstVec({x=0,y=0,z=0}),
v001=ConstVec({x=0,y=0,z=1}),
v010=ConstVec({x=0,y=1,z=0}),
v011=ConstVec({x=0,y=1,z=1}),
v100=ConstVec({x=1,y=0,z=0}),
v101=ConstVec({x=1,y=0,z=1}),
v110=ConstVec({x=1,y=1,z=0}),
v111=ConstVec({x=1,y=1,z=1}),
up = ConstVec({x=0,y=0,z=1}),
down = ConstVec({x=0,y=0,z=-1}),
temp={x=0,y=0,z=0},
tempColor={x=0,y=0,z=0},
temp_v1={x=0,y=0,z=0},
temp_v2={x=0,y=0,z=0},
temp_v3={x=0,y=0,z=0},
temp_v4={x=0,y=0,z=0},
temp_v5={x=0,y=0,z=0},
temp_v6={x=0,y=0,z=0},
vecMathTemp1={x=0,y=0,z=0},
vecMathTemp2={x=0,y=0,z=0},
}
-----------------------------------------------------------------------------
-- Math constants
-----------------------------------------------------------------------------
g_Rad2Deg = 180/math.pi;
g_Deg2Rad = math.pi/180;
g_Pi = math.pi;
g_2Pi = 2*math.pi;
g_Pi2 = 0.5*math.pi;
-----------------------------------------------------------------------------
-- Import commonly used math functions from math. table to global namespace.
-----------------------------------------------------------------------------
random = math.random;
math.randomseed(os.time()); -- seed and pop
random();
-----------------------------------------------------------------------------
function IsNullVector(a)
return (a.x==0 and a.y==0 and a.z ==0);
end
function IsNotNullVector(a)
return (a.x~=0 or a.y~=0 or a.z ~=0);
end
function LengthSqVector(a)
return (a.x * a.x + a.y * a.y + a.z * a.z);
end
function LengthVector(a)
return math.sqrt(LengthSqVector(a));
end
function DistanceSqVectors(a, b)
local x = a.x-b.x;
local y = a.y-b.y;
local z = a.z-b.z;
return x*x + y*y + z*z;
end
function DistanceSqVectors2d(a, b)
local x = a.x-b.x;
local y = a.y-b.y;
return x*x + y*y;
end
function DistanceVectors(a, b)
local x = a.x-b.x;
local y = a.y-b.y;
local z = a.z-b.z;
return math.sqrt(x*x + y*y + z*z);
end
-----------------------------------------------------------------------------
function dotproduct3d(a,b)
return (a.x * b.x) + (a.y * b.y) + (a.z * b.z);
end
-----------------------------------------------------------------------------
function dotproduct2d(a,b)
return (a.x * b.x) + (a.y * b.y);
end
function LogVec(name,v)
Log("%s = (%f %f %f)",name,v.x,v.y,v.z);
end
function ZeroVector(dest)
dest.x=0;
dest.y=0;
dest.z=0;
end
function CopyVector(dest,src)
dest.x=src.x;
dest.y=src.y;
dest.z=src.z;
end
----------------------------------
function SumVectors(a,b)
return {x=a.x+b.x,y=a.y+b.y,z=a.z+b.z};
end
function NegVector(a)
a.x = -a.x;
a.y = -a.y;
a.z = -a.z;
end
----------------------------------
function SubVectors(dest,a,b)
dest.x = a.x - b.x;
dest.y = a.y - b.y;
dest.z = a.z - b.z;
end
----------------------------------
function FastSumVectors(dest,a,b)
dest.x=a.x+b.x;
dest.y=a.y+b.y;
dest.z=a.z+b.z;
end
----------------------------------
function DifferenceVectors(a,b)
return {x=a.x-b.x,y=a.y-b.y,z=a.z-b.z};
end
----------------------------------
function FastDifferenceVectors(dest,a,b)
dest.x=a.x-b.x;
dest.y=a.y-b.y;
dest.z=a.z-b.z;
end
----------------------------------
function ProductVectors(a,b)
return {x=a.x*b.x,y=a.y*b.y,z=a.z*b.z};
end
----------------------------------
function FastProductVectors(dest,a,b)
dest.x=a.x*b.x;
dest.y=a.y*b.y;
dest.z=a.z*b.z;
end
----------------------------------
function ScaleVector(a,b)
return {x=a.x*b,y=a.y*b,z=a.z*b};
end
function ScaleVectorInPlace(a,b)
a.x=a.x*b;
a.y=a.y*b;
a.z=a.z*b;
end
----------------------------------
function NormalizeVector(a)
local len=math.sqrt(LengthSqVector(a));
local multiplier;
if(len>0)then
multiplier=1/len;
else
multiplier=0.0001;
end
a.x=a.x*multiplier;
a.y=a.y*multiplier;
a.z=a.z*multiplier;
return a
end
----------------------------------
function FastScaleVector(dest,a,b)
dest.x=a.x*b;
dest.y=a.y*b;
dest.z=a.z*b;
end
--linear interpolation
----------------------------------
function LerpColors(a,b,k)
g_Vectors.tempColor.x = a.x+(b.x-a.x)*k
g_Vectors.tempColor.y = a.y+(b.y-a.y)*k
g_Vectors.tempColor.z = a.z+(b.z-a.z)*k
return g_Vectors.tempColor;
end
----------------------------------
function Lerp(a,b,k)
return (a + (b - a)*k);
end
----------------------------------
function __max(a, b)
if (a > b) then
return a;
else
return b;
end
end
----------------------------------
function __min(a, b)
if (a < b) then
return a;
else
return b;
end
end
----------------------------------
function clamp(_n, _min, _max)
if (_n > _max) then _n = _max; end
if (_n < _min) then _n = _min; end
return _n;
end
----------------------------------
function Interpolate(actual,goal,speed)
local delta = goal - actual;
if (math.abs(delta)<0.001) then
return goal;
end
local res = actual + delta * __min(speed,1.0);
return res;
end
-----------------------------------
function sgn(a)
if (a == 0) then
return 0;
elseif (a > 0) then
return 1;
else
return -1;
end
end
-----------------------------------
function sgnnz(a)
return (a>=0) and 1 or -1;
end
-----------------------------------
function sqr(a)
return a*a;
end
-----------------
function randomF(a,b)
if (a>b) then
local c = b;
b = a;
a = c;
end
local delta = b-a;
return (a + math.random()*delta);
end
function VecRotate90_Z(v)
local x = v.x;
v.x = v.y;
v.y = -x;
end
function VecRotateMinus90_Z(v)
local x = v.x;
v.x = -v.y;
v.y = x;
end
function iff(c,a,b)
if c then return a else return b end
end
-----------------------------------------------------------------------------
-- calculate cross product
function crossproduct3d( dest, p, q )
dest.x = p.y*q.z-p.z*q.y;
dest.y = p.z*q.x-p.x*q.z;
dest.z = p.x*q.y-p.y*q.x;
end
-- rotate vector p around vector r by angle
-- the length of r needs to be 1
function RotateVectorAroundR( dest, p, r, angle )
-- p' = v1 + v2 +v3
-- v1 = pcosA
-- v2 = r**psinA;
-- v3 = r< r,p >( 1- cosA );
local cosValue = math.cos( angle );
local sinValue = math.sin( angle );
local v1 = {};
local v2 = {};
local v3 = {};
local vTmp = {};
-- v1
CopyVector( v1, p );
FastScaleVector( v1, v1, cosValue );
-- v2
CopyVector( vTmp, p );
FastScaleVector( vTmp, vTmp, sinValue );
crossproduct3d( v2, r, vTmp );
-- v3
CopyVector( v3, r );
FastScaleVector( v3, v3, dotproduct3d( r, p ) );
FastScaleVector( v3, v3, 1.0 - cosValue );
-- p'
CopyVector( dest, v1 );
FastSumVectors( dest, v1, v2 );
FastSumVectors( dest, dest, v3 );
end
-- project P to the surface whose normal vector is N.
function ProjectVector( dest, P, N )
-- projected vector is vector X (output)
-- X =P+(-P*N)N
local minusP ={};
FastScaleVector( minusP , P , -1.0 );
local t = dotproduct3d( minusP, N );
CopyVector( dest , N );
FastScaleVector( dest , dest , t );
FastSumVectors( dest , dest , P );
end
-- get a distance between line(pt+q) and point(a)
function DistanceLineAndPoint( a, p, q )
-- d=|| p * (a-q) || / ||p||
local length = LengthVector( p );
local outerProduct;
local vOuterProduct = {};
local vTmp = {};
local d;
SubVectors( vTmp, a, q );
crossproduct3d( vOuterProduct, p, vTmp );
outerProduct =LengthVector( vOuterProduct );
if ( length>0.01 ) then
d = outerProduct/length;
else
d = 0.0;
end
return d;
end
-- Get normalized direction vector from point 'a' to point 'b'
function GetDirection(a, b)
local dir = {};
SubVectors(dir, b, a);
NormalizeVector(dir);
return dir;
end
function GetAngleBetweenVectors2D(a, b)
return math.acos(dotproduct2d(a, b));
end
function GetAngleBetweenVectors(a, b)
return math.acos(clamp(dotproduct3d(a, b), -1, 1));
end