123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492 |
- //! The object which is in a trigger and its metadata
- class TriggerInsider
- {
- ref OLinkT insider; // DEPRECATED
-
- //! Object that data belongs to
- protected Object m_Object;
-
- //! Last time the object was seen in ms
- int timeStamp;
-
- //! Time the object was first seen in seconds
- float timeEntered;
-
- //! Last time the object was updated in seconds, is used for calculating deltaTime
- float lastUpdated;
-
- void TriggerInsider(Object obj)
- {
- insider = new OLinkT(obj);
- m_Object = obj;
- }
-
- void ~TriggerInsider()
- {
- }
- Object GetObject()
- {
- return m_Object;
- }
- };
- #ifdef DIAG_DEVELOPER
- typedef Param7<vector, vector, vector, vector, float, string, array<ref TriggerInsider>> DebugTriggerInfo;
- #endif
- //! Scripted Trigger
- class Trigger : TriggerEvents
- {
- //! DEPRECATED
- const int TIMEOUT = 1000;
- //! The objects and their metadata which are currently inside the Trigger
- ref array<ref TriggerInsider> m_insiders;
-
- #ifdef DIAG_DEVELOPER
- bool m_Local;//is this trigger spawning on client only ?
- string m_DebugAreaType;
- ref array<ref TriggerInsider> m_dbgInsiders;
- #endif
-
- //! ctor
- private void Trigger()
- {
- SetEventMask(EntityEvent.INIT /*| EntityEvent.TOUCH*/ | EntityEvent.FRAME | EntityEvent.ENTER | EntityEvent.LEAVE );
- SetFlags(EntityFlags.TRIGGER, false);
-
- m_insiders = new array<ref TriggerInsider>;
- }
-
- //! dtor
- private void ~Trigger()
- {
- #ifdef DIAG_DEVELOPER
- CleanupDebugShapes(dbgTargets);
- #endif
- }
- /** \name IEntity events
- Usage of IEntity events
- */
- //@{
- //! Set the default extents of the Trigger only once it is properly initialized
- override void EOnInit(IEntity other, int extra)
- {
- SetExtents("-2 -4 -2", "2 4 2");
- }
-
- //! When an Object touches the Trigger, we want to register it being inside the Trigger -> Replaced by EOnEnter
- /*override void EOnTouch(IEntity other, int extra)
- {
- Object obj;
- if (Class.CastTo(obj, other) && CanAddObjectAsInsider(obj))
- AddInsider(obj);
- }*/
-
- //! We want to update the insiders every frame (or do we?)
- override void EOnFrame(IEntity other, float timeSlice)
- {
- UpdateInsiders(TIMEOUT);
- }
-
- //! When an Object enters the trigger add it to Insiders
- override void EOnEnter(IEntity other, int extra)
- {
- Object obj;
- if (Class.CastTo(obj, other) && CanAddObjectAsInsider(obj))
- AddInsider(obj);
- }
-
- //! When an Object exits the trigger remove it from Insiders
- override void EOnLeave(IEntity other, int extra)
- {
- Object obj;
- if (Class.CastTo(obj, other))
- RemoveInsiderByObject(obj);
- }
- //@}
-
-
- /** \name Trigger configuration
- Basic configuration and data API
- */
- //@{
- //! Set the size of the Trigger, avoid using SetCollisionBox directly
- void SetExtents(vector mins, vector maxs)
- {
- SetCollisionBox(mins, maxs);
- }
-
- //! Get the radius of the CollisionBox, simply left for backwards compatibility
- float GetRadius(vector min, vector max)
- {
- return GetCollisionRadius();
- }
-
- //! Get the current TriggerInsider array, left for backwards compatibility, moved down from ManTrigger
- array<ref TriggerInsider> GetInsiders()
- {
- return m_insiders;
- }
-
- //! Gets the TriggerInsider for the Object if it exists
- TriggerInsider GetInsiderForObject(Object object)
- {
- TriggerInsider ins;
-
- for ( int n = 0; n < m_insiders.Count(); ++n )
- {
- ins = m_insiders[n];
- if (ins.GetObject() == object)
- return ins;
- }
-
- return null;
- }
-
- //! Gets the index in m_insiders for the Object
- int GetInsiderIndexForObject(Object object)
- {
- TriggerInsider ins;
-
- for ( int n = 0; n < m_insiders.Count(); ++n )
- {
- ins = m_insiders[n];
- if (ins.GetObject() == object)
- return n;
- }
-
- return -1;
- }
- //@}
-
-
- /** \name TriggerEvents, backwards compatibility calling
- Implementation for backwards compatibility with old Trigger system
- */
- //@{
- override protected void OnEnterBeginEvent(TriggerInsider insider)
- {
- // Call the old event for backwards compatibility
- OnEnter(insider.GetObject());
- }
-
- override protected void OnLeaveBeginEvent(TriggerInsider insider)
- {
- // Call the old event for backwards compatibility
- OnLeave(insider.GetObject());
- }
- //@}
-
- /** \name DEPRECATED Events
- left for backwards compatibility
- */
- //@{
- void OnEnter(Object obj) {}
-
- void OnLeave(Object obj) {}
- //@}
-
-
- /** \name TriggerInsider conditions
- Conditions applied to TriggerInsider for if it can be added or should be removed
- */
- //@{
- //! Condition whether an Object can be added as TriggerInsider (checked before calling AddInsider)
- protected bool CanAddObjectAsInsider(Object object)
- {
- return true;
- }
-
- //! Condition whether a TriggerInsider should still be updated or not (checked in update loop and before adding)
- protected bool ShouldRemoveInsider(TriggerInsider insider)
- {
- return false;
- }
-
- //! Condition whether a TriggerInsider should still be updated or not, skips OnLeaveEvent (checked in update loop and before adding)
- protected bool ShouldRemoveInsiderNoLeave(TriggerInsider insider)
- {
- return false;
- }
- //@}
-
-
- /** \name TriggerInsider processing
- Logic and processing of adding, removing and updating a TriggerInsider
- */
- //@{
- //! Used for easily overriding TriggerInsider creation without rewriting AddInsider
- protected TriggerInsider CreateInsider(Object obj)
- {
- return new TriggerInsider(obj);
- }
-
- //! Adding of new TriggerInsider
- protected void AddInsider(Object obj)
- {
- if ( !obj )
- return;
- // Already in?
- if ( GetInsiderForObject( obj ) )
- {
- Error(string.Format("[WARNING] :: [Trigger] :: [%1] :: Insider (%2) is already inside.", GetDebugName(this), GetDebugName(obj)));
- return;
- }
-
- // New Object entered! Fill the data.
- TriggerInsider insider = CreateInsider(obj);
- insider.timeStamp = g_Game.GetTime();
- insider.timeEntered = g_Game.GetTickTime();
- insider.lastUpdated = insider.timeEntered;
-
- // Don't add if it is going to be removed anyways..
- if ( ShouldRemoveInsider(insider) || ShouldRemoveInsiderNoLeave(insider) )
- return;
-
- // Keep track of the Object as long as it is inside the Trigger
- int index = m_insiders.Insert(insider);
-
- // Call the enter event to signal this Object entered
- Enter(insider);
- obj.OnEnterTrigger(this);
-
- #ifdef TRIGGER_DEBUG_NORMAL
- Debug.TriggerLog(string.Format("%1: inserted at index %2", GetDebugName(obj), index), "Trigger", "", "AddInsider", GetDebugName(this));
- #endif
- }
-
- //! Removing of TriggerInsider
- protected void RemoveInsider(TriggerInsider insider, int index = -1)
- {
- Leave(insider);
- insider.GetObject().OnLeaveTrigger(this);
-
- #ifdef TRIGGER_DEBUG_NORMAL
- Debug.TriggerLog(string.Format("%1: removing at index %2", GetDebugName(insider.GetObject()), index), "Trigger", "", "RemoveInsider", GetDebugName(this));
- #endif
-
- if (index >= 0)
- m_insiders.Remove(index);
- else
- m_insiders.RemoveItemUnOrdered(insider);
- }
-
- //! Removing of TriggerInsider through Object
- protected void RemoveInsiderByObject(Object object)
- {
- TriggerInsider ins;
- for ( int n = 0; n < m_insiders.Count(); ++n )
- {
- ins = m_insiders[n];
- if (ins.GetObject() == object)
- {
- RemoveInsider(ins, n);
- return;
- }
- }
-
- // As EOnLeave can call this, it is perfectly valid that this Object is not found on Script side
- // because of "ShouldRemoveInsider" and "ShouldRemoveInsiderNoLeave"
- }
-
- //! Update the current TriggerInsider inside the Trigger, timeout paramter is deprecated
- protected void UpdateInsiders(int timeout)
- {
- #ifdef DIAG_DEVELOPER
- DebugSendDmgTrigger();
- #endif
- // Don't do anything if there aren't any insiders
- if ( m_insiders.Count() == 0 )
- return;
-
- // Mark the beginning of the update loop
- StayStart(m_insiders.Count());
-
- // Iterate over the current insiders, backwards because we are deleting
- for ( int n = m_insiders.Count() - 1; n >= 0 ; --n)
- {
- TriggerInsider insider = m_insiders.Get(n);
- Object obj = insider.GetObject();
-
- // Check if the Object still exists or should be removed without calling OnLeaveEvent
- if ( !obj || ShouldRemoveInsiderNoLeave(insider) )
- {
- #ifdef TRIGGER_DEBUG_BASIC
- Debug.TriggerLog(string.Format("%1: removed with no Leave.", GetDebugName(obj)), "Trigger", "", "UpdateInsiders", GetDebugName(this));
- #endif
-
- m_insiders.Remove(n);
- continue;
- }
-
- // Check if Object left the Trigger or should be removed regardless
- if ( ShouldRemoveInsider(insider) )
- {
- RemoveInsider(insider, n);
- continue;
- }
-
- // Call the OnStayEvent, Object is still inside the Trigger and can be updated
- // Pass in the time since the Object was last updated (or entered)
- float currentTime = g_Game.GetTickTime();
- Stay(insider, currentTime - insider.lastUpdated);
- insider.lastUpdated = currentTime;
- }
-
- // Mark the end of the update loop
- StayFinish();
- }
- //@}
-
- /** \name DEBUGGING
- General internal debugging functionality
- */
- //@{
- override void OnRPC(PlayerIdentity sender, int rpc_type, ParamsReadContext ctx)
- {
- super.OnRPC(sender, rpc_type, ctx);
- #ifdef DIAG_DEVELOPER
- switch (rpc_type)
- {
- case ERPCs.DIAG_TRIGGER_DEBUG:
- DebugTriggerInfo data = new DebugTriggerInfo(vector.Zero, vector.Zero, vector.Zero, vector.Zero, 0, "", null);
-
- if (ctx.Read(data))
- DebugDmgTrigger(data.param1, data.param2, data.param3, data.param4, data.param5, data.param6, data.param7);
- break;
- }
- #endif
- }
-
- #ifdef DIAG_DEVELOPER
- void DebugSendDmgTrigger()
- {
- vector minmax[2];
- GetCollisionBox(minmax);
-
- DebugTriggerInfo data = new DebugTriggerInfo(vector.Zero, vector.Zero, vector.Zero, vector.Zero, 0, "", null);
- data.param1 = GetWorldPosition();
- data.param2 = GetOrientation();
- data.param3 = minmax[0];
- data.param4 = minmax[1];
- data.param5 = GetCollisionRadius();
- data.param6 = m_DebugAreaType;
- data.param7 = m_insiders;
-
- if (GetGame().IsMultiplayer() && GetGame().IsServer())
- PluginDiagMenuServer.SendDataToSubscribersServer(this, ESubscriberSystems.TRIGGERS, ERPCs.DIAG_TRIGGER_DEBUG, data, false);
- else if (!GetGame().IsMultiplayer() || m_Local)
- DebugDmgTrigger(data.param1, data.param2, data.param3, data.param4, data.param5, data.param6, data.param7);
- }
-
- protected ref array<Shape> dbgTargets = new array<Shape>();
-
- void DebugDmgTrigger( vector pos, vector orientation, vector min, vector max, float radius, string dmgType, array<ref TriggerInsider> insiders)
- {
- CleanupDebugShapes(dbgTargets);
- bool enableDebug = DiagMenu.GetBool(DiagMenuIDs.TRIGGER_DEBUG);
- if (enableDebug)
- {
- if (GetGame().IsMultiplayer() && GetGame().IsServer())
- {
- return;
- }
-
- vector w_pos, w_pos_sphr, w_pos_lend;
-
- w_pos = pos;
- // sphere pos tweaks
- w_pos_sphr = w_pos;
- // line pos tweaks
- w_pos_lend = w_pos;
-
- //Find way to change colour of box depending on ammoType in a more elegant fashion
- m_DebugAreaType = dmgType;
- Shape dbgShape;
-
- switch ( m_DebugAreaType )
- {
- case "FireDamage":
- dbgShape = DrawDebugShape(pos, min, max, radius, COLOR_RED_A);
- break;
-
- case "BarbedWireHit":
- dbgShape = DrawDebugShape(pos, min, max, radius, COLOR_BLUE_A);
- break;
-
- default:
- dbgShape = DrawDebugShape(pos, min, max, radius, COLOR_GREEN_A);
- break;
- }
-
- if (GetGame().IsMultiplayer() || GetGame().IsServer())
- m_dbgInsiders = insiders;
-
- if (m_dbgInsiders.Count() > 0)
- {
- //Change colour to make state clearer
- dbgShape.SetColor(COLOR_YELLOW_A);
-
- for (int i = 0; i < m_dbgInsiders.Count(); i++)
- {
- EntityAI insider_EAI = EntityAI.Cast(m_dbgInsiders[i].GetObject());
- if (insider_EAI)
- {
- vector insiderPos = insider_EAI.GetWorldPosition() + "0 0.1 0";
- dbgTargets.Insert(Debug.DrawArrow(w_pos, insiderPos));
- }
- }
- }
- }
- }
-
- protected Shape DrawDebugShape(vector pos, vector min, vector max, float radius, int color)
- {
- Shape dbgShape;
- switch (GetTriggerShape())
- {
- case TriggerShape.BOX:
- dbgShape = Debug.DrawBoxEx(min, max, color, ShapeFlags.TRANSP|ShapeFlags.NOZWRITE);
-
- vector mat[4];
- GetTransform(mat);
- dbgShape.CreateMatrix(mat);
- dbgShape.SetMatrix(mat);
- break;
- case TriggerShape.CYLINDER:
- dbgShape = Debug.DrawCylinder(pos, radius, max[1], color, ShapeFlags.TRANSP|ShapeFlags.NOZWRITE);
- break;
- case TriggerShape.SPHERE:
- dbgShape = Debug.DrawSphere(pos, radius, color, ShapeFlags.TRANSP|ShapeFlags.NOZWRITE);
- break;
- default:
- ErrorEx("TriggerShape not found", ErrorExSeverity.WARNING);
- break;
- }
- dbgTargets.Insert(dbgShape);
-
- return dbgShape;
- }
-
- protected void CleanupDebugShapes(array<Shape> shapes)
- {
- foreach (Shape shape : shapes)
- {
- Debug.RemoveShape(shape);
- }
- shapes.Clear();
- }
-
- #endif
- //@}
- };
|